diff options
Diffstat (limited to 'drivers')
2341 files changed, 143072 insertions, 44972 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 67ce51d62015..2eced9afba53 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -15,6 +15,9 @@ obj-$(CONFIG_PINCTRL) += pinctrl/ obj-$(CONFIG_GPIOLIB) += gpio/ obj-y += pwm/ obj-$(CONFIG_PCI) += pci/ +# PCI dwc controller drivers +obj-y += pci/dwc/ + obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ obj-y += video/ diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index f77956c3fd45..747c2ba98534 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -56,7 +56,7 @@ struct acpi_ipmi_device { struct ipmi_driver_data { struct list_head ipmi_devices; struct ipmi_smi_watcher bmc_events; - struct ipmi_user_hndl ipmi_hndlrs; + const struct ipmi_user_hndl ipmi_hndlrs; struct mutex ipmi_lock; /* diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 8ea836c046f8..5edfd9c49044 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -18,8 +18,10 @@ #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/platform_data/clk-lpss.h> +#include <linux/platform_data/x86/pmc_atom.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/pwm.h> #include <linux/delay.h> #include "internal.h" @@ -31,7 +33,6 @@ ACPI_MODULE_NAME("acpi_lpss"); #include <asm/cpu_device_id.h> #include <asm/intel-family.h> #include <asm/iosf_mbi.h> -#include <asm/pmc_atom.h> #define LPSS_ADDR(desc) ((unsigned long)&desc) @@ -154,6 +155,18 @@ static void byt_i2c_setup(struct lpss_private_data *pdata) writel(0, pdata->mmio_base + LPSS_I2C_ENABLE); } +/* BSW PWM used for backlight control by the i915 driver */ +static struct pwm_lookup bsw_pwm_lookup[] = { + PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0", + "pwm_backlight", 0, PWM_POLARITY_NORMAL, + "pwm-lpss-platform"), +}; + +static void bsw_pwm_setup(struct lpss_private_data *pdata) +{ + pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); +} + static const struct lpss_device_desc lpt_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, .prv_offset = 0x800, @@ -191,6 +204,7 @@ static const struct lpss_device_desc byt_pwm_dev_desc = { static const struct lpss_device_desc bsw_pwm_dev_desc = { .flags = LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, + .setup = bsw_pwm_setup, }; static const struct lpss_device_desc byt_uart_dev_desc = { diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index eb76a4c10dbf..754431031282 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -20,6 +20,7 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/kthread.h> +#include <uapi/linux/sched/types.h> #include <linux/freezer.h> #include <linux/cpu.h> #include <linux/tick.h> diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c index 251f9477a984..857dbc43a9b1 100644 --- a/drivers/acpi/acpica/dbconvert.c +++ b/drivers/acpi/acpica/dbconvert.c @@ -242,7 +242,7 @@ acpi_status acpi_db_convert_to_package(char *string, union acpi_object *object) * * RETURN: Status * - * DESCRIPTION: Convert a typed and tokenized string to an union acpi_object. Typing: + * DESCRIPTION: Convert a typed and tokenized string to a union acpi_object. Typing: * 1) String objects were surrounded by quotes. * 2) Buffer objects were surrounded by parentheses. * 3) Package objects were surrounded by brackets "[]". diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 3dbbecf22087..9d14b509529e 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -323,7 +323,7 @@ acpi_ns_check_reference(struct acpi_evaluate_info *info, /* * Check the reference object for the correct reference type (opcode). - * The only type of reference that can be converted to an union acpi_object is + * The only type of reference that can be converted to a union acpi_object is * a reference to a named object (reference class: NAME) */ if (return_object->reference.class == ACPI_REFCLASS_NAME) { diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index 8e365c0e766b..c944ff5c9c3d 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -495,9 +495,9 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info) /* * Two types of references are supported - those created by Index and * ref_of operators. A name reference (AML_NAMEPATH_OP) can be converted - * to an union acpi_object, so it is not dereferenced here. A ddb_handle + * to a union acpi_object, so it is not dereferenced here. A ddb_handle * (AML_LOAD_OP) cannot be dereferenced, nor can it be converted to - * an union acpi_object. + * a union acpi_object. */ switch (info->return_object->reference.class) { case ACPI_REFCLASS_INDEX: diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c index a05b5c0cf181..12771fcf0417 100644 --- a/drivers/acpi/apei/bert.c +++ b/drivers/acpi/apei/bert.c @@ -97,6 +97,7 @@ static int __init bert_check_table(struct acpi_table_bert *bert_tab) static int __init bert_init(void) { + struct apei_resources bert_resources; struct acpi_bert_region *boot_error_region; struct acpi_table_bert *bert_tab; unsigned int region_len; @@ -127,13 +128,14 @@ static int __init bert_init(void) } region_len = bert_tab->region_length; - if (!request_mem_region(bert_tab->address, region_len, "APEI BERT")) { - pr_err("Can't request iomem region <%016llx-%016llx>.\n", - (unsigned long long)bert_tab->address, - (unsigned long long)bert_tab->address + region_len - 1); - return -EIO; - } - + apei_resources_init(&bert_resources); + rc = apei_resources_add(&bert_resources, bert_tab->address, + region_len, true); + if (rc) + return rc; + rc = apei_resources_request(&bert_resources, "APEI BERT"); + if (rc) + goto out_fini; boot_error_region = ioremap_cache(bert_tab->address, region_len); if (boot_error_region) { bert_print_all(boot_error_region, region_len); @@ -142,7 +144,9 @@ static int __init bert_init(void) rc = -ENOMEM; } - release_mem_region(bert_tab->address, region_len); + apei_resources_release(&bert_resources); +out_fini: + apei_resources_fini(&bert_resources); return rc; } diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index e53bef6cf53c..b192b42a8351 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -44,6 +44,7 @@ #include <linux/pci.h> #include <linux/aer.h> #include <linux/nmi.h> +#include <linux/sched/clock.h> #include <acpi/ghes.h> #include <acpi/apei.h> diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 7361d00818e2..662036bdc65e 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1603,7 +1603,7 @@ static size_t sizeof_nfit_set_info(int num_mappings) + num_mappings * sizeof(struct nfit_set_info_map); } -static int cmp_map(const void *m0, const void *m1) +static int cmp_map_compat(const void *m0, const void *m1) { const struct nfit_set_info_map *map0 = m0; const struct nfit_set_info_map *map1 = m1; @@ -1612,6 +1612,14 @@ static int cmp_map(const void *m0, const void *m1) sizeof(u64)); } +static int cmp_map(const void *m0, const void *m1) +{ + const struct nfit_set_info_map *map0 = m0; + const struct nfit_set_info_map *map1 = m1; + + return map0->region_offset - map1->region_offset; +} + /* Retrieve the nth entry referencing this spa */ static struct acpi_nfit_memory_map *memdev_from_spa( struct acpi_nfit_desc *acpi_desc, u16 range_index, int n) @@ -1667,6 +1675,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), cmp_map, NULL); nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); + + /* support namespaces created with the wrong sort order */ + sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map), + cmp_map_compat, NULL); + nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0); + ndr_desc->nd_set = nd_set; devm_kfree(dev, info); diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c index a6a4ceaa6cc3..2944353253ed 100644 --- a/drivers/acpi/pci_mcfg.c +++ b/drivers/acpi/pci_mcfg.c @@ -195,11 +195,10 @@ int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres, goto skip_lookup; /* - * We expect exact match, unless MCFG entry end bus covers more than - * specified by caller. + * We expect the range in bus_res in the coverage of MCFG bus range. */ list_for_each_entry(e, &pci_mcfg_list, list) { - if (e->segment == seg && e->bus_start == bus_res->start && + if (e->segment == seg && e->bus_start <= bus_res->start && e->bus_end >= bus_res->end) { root->mcfg_addr = e->addr; } diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 8b11d6d385dc..cd4c4271dc4c 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -406,7 +406,7 @@ static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, } /* - * In IO-APIC mode, use overrided attribute. Two reasons: + * In IO-APIC mode, use overridden attribute. Two reasons: * 1. BIOS bug in DSDT * 2. BIOS uses IO-APIC mode Interrupt Source Override * diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c index 2b5d0fac81f0..01c94669a2b0 100644 --- a/drivers/acpi/spcr.c +++ b/drivers/acpi/spcr.c @@ -46,7 +46,7 @@ static bool qdf2400_erratum_44_present(struct acpi_table_header *h) * console is registered and if @earlycon is true, earlycon is set up. * * When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called - * from arch inintialization code as soon as the DT/ACPI decision is made. + * from arch initialization code as soon as the DT/ACPI decision is made. * */ int __init parse_spcr(bool earlycon) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 15b263a420e8..aae4d8d4be36 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -31,7 +31,8 @@ #include <linux/poll.h> #include <linux/debugfs.h> #include <linux/rbtree.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/mm.h> #include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/vmalloc.h> @@ -3342,7 +3343,7 @@ static void binder_vma_close(struct vm_area_struct *vma) binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); } -static int binder_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int binder_vm_fault(struct vm_fault *vmf) { return VM_FAULT_SIGBUS; } diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 0cc08f892fea..5db6ab261643 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -398,6 +398,9 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, int pmp, unsigned long deadline, int (*check_ready)(struct ata_link *link)); +int ahci_do_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline, bool *online); + unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); int ahci_stop_engine(struct ata_port *ap); void ahci_start_fis_rx(struct ata_port *ap); diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index 267a3d3e79f4..1a50cd3b4233 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -16,7 +16,8 @@ #include <linux/ahci_platform.h> #include "ahci.h" -#define DRV_NAME "ahci_da850" +#define DRV_NAME "ahci_da850" +#define HARDRESET_RETRIES 5 /* SATA PHY Control Register offset from AHCI base */ #define SATA_P0PHYCR_REG 0x178 @@ -28,17 +29,8 @@ #define SATA_PHY_TXSWING(x) ((x) << 19) #define SATA_PHY_ENPLL(x) ((x) << 31) -/* - * The multiplier needed for 1.5GHz PLL output. - * - * NOTE: This is currently hardcoded to be suitable for 100MHz crystal - * frequency (which is used by DA850 EVM board) and may need to be changed - * if you would like to use this driver on some other board. - */ -#define DA850_SATA_CLK_MULTIPLIER 7 - static void da850_sata_init(struct device *dev, void __iomem *pwrdn_reg, - void __iomem *ahci_base) + void __iomem *ahci_base, u32 mpy) { unsigned int val; @@ -47,18 +39,122 @@ static void da850_sata_init(struct device *dev, void __iomem *pwrdn_reg, val &= ~BIT(0); writel(val, pwrdn_reg); - val = SATA_PHY_MPY(DA850_SATA_CLK_MULTIPLIER + 1) | SATA_PHY_LOS(1) | - SATA_PHY_RXCDR(4) | SATA_PHY_RXEQ(1) | SATA_PHY_TXSWING(3) | - SATA_PHY_ENPLL(1); + val = SATA_PHY_MPY(mpy) | SATA_PHY_LOS(1) | SATA_PHY_RXCDR(4) | + SATA_PHY_RXEQ(1) | SATA_PHY_TXSWING(3) | SATA_PHY_ENPLL(1); writel(val, ahci_base + SATA_P0PHYCR_REG); } +static u32 ahci_da850_calculate_mpy(unsigned long refclk_rate) +{ + u32 pll_output = 1500000000, needed; + + /* + * We need to determine the value of the multiplier (MPY) bits. + * In order to include the 12.5 multiplier we need to first divide + * the refclk rate by ten. + * + * __div64_32() turned out to be unreliable, sometimes returning + * false results. + */ + WARN((refclk_rate % 10) != 0, "refclk must be divisible by 10"); + needed = pll_output / (refclk_rate / 10); + + /* + * What we have now is (multiplier * 10). + * + * Let's determine the actual register value we need to write. + */ + + switch (needed) { + case 50: + return 0x1; + case 60: + return 0x2; + case 80: + return 0x4; + case 100: + return 0x5; + case 120: + return 0x6; + case 125: + return 0x7; + case 150: + return 0x8; + case 200: + return 0x9; + case 250: + return 0xa; + default: + /* + * We should have divided evenly - if not, return an invalid + * value. + */ + return 0; + } +} + +static int ahci_da850_softreset(struct ata_link *link, + unsigned int *class, unsigned long deadline) +{ + int pmp, ret; + + pmp = sata_srst_pmp(link); + + /* + * There's an issue with the SATA controller on da850 SoCs: if we + * enable Port Multiplier support, but the drive is connected directly + * to the board, it can't be detected. As a workaround: if PMP is + * enabled, we first call ahci_do_softreset() and pass it the result of + * sata_srst_pmp(). If this call fails, we retry with pmp = 0. + */ + ret = ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); + if (pmp && ret == -EBUSY) + return ahci_do_softreset(link, class, 0, + deadline, ahci_check_ready); + + return ret; +} + +static int ahci_da850_hardreset(struct ata_link *link, + unsigned int *class, unsigned long deadline) +{ + int ret, retry = HARDRESET_RETRIES; + bool online; + + /* + * In order to correctly service the LCD controller of the da850 SoC, + * we increased the PLL0 frequency to 456MHz from the default 300MHz. + * + * This made the SATA controller unstable and the hardreset operation + * does not always succeed the first time. Before really giving up to + * bring up the link, retry the reset a couple times. + */ + do { + ret = ahci_do_hardreset(link, class, deadline, &online); + if (online) + return ret; + } while (retry--); + + return ret; +} + +static struct ata_port_operations ahci_da850_port_ops = { + .inherits = &ahci_platform_ops, + .softreset = ahci_da850_softreset, + /* + * No need to override .pmp_softreset - it's only used for actual + * PMP-enabled ports. + */ + .hardreset = ahci_da850_hardreset, + .pmp_hardreset = ahci_da850_hardreset, +}; + static const struct ata_port_info ahci_da850_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_ops, + .port_ops = &ahci_da850_port_ops, }; static struct scsi_host_template ahci_platform_sht = { @@ -69,14 +165,52 @@ static int ahci_da850_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; - struct resource *res; void __iomem *pwrdn_reg; + struct resource *res; + struct clk *clk; + u32 mpy; int rc; hpriv = ahci_platform_get_resources(pdev); if (IS_ERR(hpriv)) return PTR_ERR(hpriv); + /* + * Internally ahci_platform_get_resources() calls clk_get(dev, NULL) + * when trying to obtain the functional clock. This SATA controller + * uses two clocks for which we specify two connection ids. If we don't + * have the functional clock at this point - call clk_get() again with + * con_id = "fck". + */ + if (!hpriv->clks[0]) { + clk = clk_get(dev, "fck"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + hpriv->clks[0] = clk; + } + + /* + * The second clock used by ahci-da850 is the external REFCLK. If we + * didn't get it from ahci_platform_get_resources(), let's try to + * specify the con_id in clk_get(). + */ + if (!hpriv->clks[1]) { + clk = clk_get(dev, "refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "unable to obtain the reference clock"); + return -ENODEV; + } + + hpriv->clks[1] = clk; + } + + mpy = ahci_da850_calculate_mpy(clk_get_rate(hpriv->clks[1])); + if (mpy == 0) { + dev_err(dev, "invalid REFCLK multiplier value: 0x%x", mpy); + return -EINVAL; + } + rc = ahci_platform_enable_resources(hpriv); if (rc) return rc; @@ -89,7 +223,7 @@ static int ahci_da850_probe(struct platform_device *pdev) if (!pwrdn_reg) goto disable_resources; - da850_sata_init(dev, pwrdn_reg, hpriv->mmio); + da850_sata_init(dev, pwrdn_reg, hpriv->mmio, mpy); rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, &ahci_platform_sht); @@ -105,11 +239,18 @@ disable_resources: static SIMPLE_DEV_PM_OPS(ahci_da850_pm_ops, ahci_platform_suspend, ahci_platform_resume); +static const struct of_device_id ahci_da850_of_match[] = { + { .compatible = "ti,da850-ahci", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ahci_da850_of_match); + static struct platform_driver ahci_da850_driver = { .probe = ahci_da850_probe, .remove = ata_platform_remove_one, .driver = { .name = DRV_NAME, + .of_match_table = ahci_da850_of_match, .pm = &ahci_da850_pm_ops, }, }; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index ee7db3119b18..3159f9e66d8f 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1519,8 +1519,8 @@ static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, return rc; } -static int ahci_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +int ahci_do_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline, bool *online) { const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); struct ata_port *ap = link->ap; @@ -1528,7 +1528,6 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, struct ahci_host_priv *hpriv = ap->host->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; - bool online; int rc; DPRINTK("ENTER\n"); @@ -1540,17 +1539,26 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, tf.command = ATA_BUSY; ata_tf_to_fis(&tf, 0, 0, d2h_fis); - rc = sata_link_hardreset(link, timing, deadline, &online, + rc = sata_link_hardreset(link, timing, deadline, online, ahci_check_ready); hpriv->start_engine(ap); - if (online) + if (*online) *class = ahci_dev_classify(ap); DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); return rc; } +EXPORT_SYMBOL_GPL(ahci_do_hardreset); + +static int ahci_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + bool online; + + return ahci_do_hardreset(link, class, deadline, &online); +} static void ahci_postreset(struct ata_link *link, unsigned int *class) { diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 4e5bf36c5f46..ef68232b5222 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2034,7 +2034,7 @@ static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg) * This is to expedite speed down decisions right after device is * initially configured. * - * The followings are speed down rules. #1 and #2 deal with + * The following are speed down rules. #1 and #2 deal with * DUBIOUS errors. * * 1. If more than one DUBIOUS_ATA_BUS or DUBIOUS_TOUT_HSM errors diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 12d3a66600a3..1ac70744ae7b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -600,6 +600,7 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) u8 args[4], *argbuf = NULL, *sensebuf = NULL; int argsize = 0; enum dma_data_direction data_dir; + struct scsi_sense_hdr sshdr; int cmd_result; if (arg == NULL) @@ -648,7 +649,7 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) /* Good values for timeout and retries? Values below from scsi_ioctl_send_command() for default case... */ cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize, - sensebuf, (10*HZ), 5, 0, NULL); + sensebuf, &sshdr, (10*HZ), 5, 0, 0, NULL); if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */ u8 *desc = sensebuf + 8; @@ -657,9 +658,6 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) /* If we set cc then ATA pass-through will cause a * check condition even if no error. Filter that. */ if (cmd_result & SAM_STAT_CHECK_CONDITION) { - struct scsi_sense_hdr sshdr; - scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, - &sshdr); if (sshdr.sense_key == RECOVERED_ERROR && sshdr.asc == 0 && sshdr.ascq == 0x1d) cmd_result &= ~SAM_STAT_CHECK_CONDITION; @@ -707,6 +705,7 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) int rc = 0; u8 scsi_cmd[MAX_COMMAND_SIZE]; u8 args[7], *sensebuf = NULL; + struct scsi_sense_hdr sshdr; int cmd_result; if (arg == NULL) @@ -734,7 +733,7 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) /* Good values for timeout and retries? Values below from scsi_ioctl_send_command() for default case... */ cmd_result = scsi_execute(scsidev, scsi_cmd, DMA_NONE, NULL, 0, - sensebuf, (10*HZ), 5, 0, NULL); + sensebuf, &sshdr, (10*HZ), 5, 0, 0, NULL); if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */ u8 *desc = sensebuf + 8; @@ -743,9 +742,6 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) /* If we set cc then ATA pass-through will cause a * check condition even if no error. Filter that. */ if (cmd_result & SAM_STAT_CHECK_CONDITION) { - struct scsi_sense_hdr sshdr; - scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, - &sshdr); if (sshdr.sense_key == RECOVERED_ERROR && sshdr.asc == 0 && sshdr.ascq == 0x1d) cmd_result &= ~SAM_STAT_CHECK_CONDITION; diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index f1a9198dfe5a..4a610795b585 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -2394,12 +2394,7 @@ static int __init amb_module_init (void) { PRINTD (DBG_FLOW|DBG_INIT, "init_module"); - // sanity check - cast needed as printk does not support %Zu - if (sizeof(amb_mem) != 4*16 + 4*12) { - PRINTK (KERN_ERR, "Fix amb_mem (is %lu words).", - (unsigned long) sizeof(amb_mem)); - return -ENOMEM; - } + BUILD_BUG_ON(sizeof(amb_mem) != 4*16 + 4*12); show_version(); diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index 623359e407aa..b042ec458544 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -2326,11 +2326,7 @@ static int __init eni_init(void) { struct sk_buff *skb; /* dummy for sizeof */ - if (sizeof(skb->cb) < sizeof(struct eni_skb_prv)) { - printk(KERN_ERR "eni_detect: skb->cb is too small (%Zd < %Zd)\n", - sizeof(skb->cb),sizeof(struct eni_skb_prv)); - return -EIO; - } + BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct eni_skb_prv)); return pci_register_driver(&eni_driver); } diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c index 80c2ddcfa92c..22dcab952a24 100644 --- a/drivers/atm/firestream.c +++ b/drivers/atm/firestream.c @@ -895,7 +895,7 @@ static int fs_open(struct atm_vcc *atm_vcc) /* XXX handle qos parameters (rate limiting) ? */ vcc = kmalloc(sizeof(struct fs_vcc), GFP_KERNEL); - fs_dprintk (FS_DEBUG_ALLOC, "Alloc VCC: %p(%Zd)\n", vcc, sizeof(struct fs_vcc)); + fs_dprintk (FS_DEBUG_ALLOC, "Alloc VCC: %p(%zd)\n", vcc, sizeof(struct fs_vcc)); if (!vcc) { clear_bit(ATM_VF_ADDR, &atm_vcc->flags); return -ENOMEM; @@ -946,7 +946,7 @@ static int fs_open(struct atm_vcc *atm_vcc) if (DO_DIRECTION (txtp)) { tc = kmalloc (sizeof (struct fs_transmit_config), GFP_KERNEL); - fs_dprintk (FS_DEBUG_ALLOC, "Alloc tc: %p(%Zd)\n", + fs_dprintk (FS_DEBUG_ALLOC, "Alloc tc: %p(%zd)\n", tc, sizeof (struct fs_transmit_config)); if (!tc) { fs_dprintk (FS_DEBUG_OPEN, "fs: can't alloc transmit_config.\n"); @@ -1185,7 +1185,7 @@ static int fs_send (struct atm_vcc *atm_vcc, struct sk_buff *skb) vcc->last_skb = skb; td = kmalloc (sizeof (struct FS_BPENTRY), GFP_ATOMIC); - fs_dprintk (FS_DEBUG_ALLOC, "Alloc transd: %p(%Zd)\n", td, sizeof (struct FS_BPENTRY)); + fs_dprintk (FS_DEBUG_ALLOC, "Alloc transd: %p(%zd)\n", td, sizeof (struct FS_BPENTRY)); if (!td) { /* Oops out of mem */ return -ENOMEM; @@ -1492,7 +1492,7 @@ static void top_off_fp (struct fs_dev *dev, struct freepool *fp, fs_dprintk (FS_DEBUG_ALLOC, "Alloc rec-skb: %p(%d)\n", skb, fp->bufsize); if (!skb) break; ne = kmalloc (sizeof (struct FS_BPENTRY), gfp_flags); - fs_dprintk (FS_DEBUG_ALLOC, "Alloc rec-d: %p(%Zd)\n", ne, sizeof (struct FS_BPENTRY)); + fs_dprintk (FS_DEBUG_ALLOC, "Alloc rec-d: %p(%zd)\n", ne, sizeof (struct FS_BPENTRY)); if (!ne) { fs_dprintk (FS_DEBUG_ALLOC, "Free rec-skb: %p\n", skb); dev_kfree_skb_any (skb); @@ -1803,7 +1803,7 @@ static int fs_init(struct fs_dev *dev) } dev->atm_vccs = kcalloc (dev->nchannels, sizeof (struct atm_vcc *), GFP_KERNEL); - fs_dprintk (FS_DEBUG_ALLOC, "Alloc atmvccs: %p(%Zd)\n", + fs_dprintk (FS_DEBUG_ALLOC, "Alloc atmvccs: %p(%zd)\n", dev->atm_vccs, dev->nchannels * sizeof (struct atm_vcc *)); if (!dev->atm_vccs) { @@ -1911,7 +1911,7 @@ static int firestream_init_one(struct pci_dev *pci_dev, goto err_out; fs_dev = kzalloc (sizeof (struct fs_dev), GFP_KERNEL); - fs_dprintk (FS_DEBUG_ALLOC, "Alloc fs-dev: %p(%Zd)\n", + fs_dprintk (FS_DEBUG_ALLOC, "Alloc fs-dev: %p(%zd)\n", fs_dev, sizeof (struct fs_dev)); if (!fs_dev) goto err_out; diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c index 584aa881882b..0f18480b33b5 100644 --- a/drivers/atm/horizon.c +++ b/drivers/atm/horizon.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/mm.h> #include <linux/pci.h> #include <linux/errno.h> @@ -2884,12 +2885,7 @@ static struct pci_driver hrz_driver = { /********** module entry **********/ static int __init hrz_module_init (void) { - // sanity check - cast is needed since printk does not support %Zu - if (sizeof(struct MEMMAP) != 128*1024/4) { - PRINTK (KERN_ERR, "Fix struct MEMMAP (is %lu fakewords).", - (unsigned long) sizeof(struct MEMMAP)); - return -ENOMEM; - } + BUILD_BUG_ON(sizeof(struct MEMMAP) != 128*1024/4); show_version(); diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 8640bafeb471..a4fa6c82261e 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -21,7 +21,7 @@ supports a variety of varients of Interphase ATM PCI (i)Chip adapter card family (See www.iphase.com/products/ClassSheet.cfm?ClassID=ATM) in terms of PHY type, the size of control memory and the size of - packet memory. The followings are the change log and history: + packet memory. The following are the change log and history: Bugfix the Mona's UBR driver. Modify the basic memory allocation and dma logic. diff --git a/drivers/atm/iphase.h b/drivers/atm/iphase.h index 53ecac5a2161..2beacf2fc1ec 100644 --- a/drivers/atm/iphase.h +++ b/drivers/atm/iphase.h @@ -21,7 +21,7 @@ supports a variety of varients of Interphase ATM PCI (i)Chip adapter card family (See www.iphase.com/products/ClassSheet.cfm?ClassID=ATM) in terms of PHY type, the size of control memory and the size of - packet memory. The followings are the change log and history: + packet memory. The following are the change log and history: Bugfix the Mona's UBR driver. Modify the basic memory allocation and dma logic. diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index 445505d9ea07..1a9bc51284b0 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -1389,7 +1389,7 @@ static void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr) if (n < 0) n += lanai_buf_size(&lvcc->rx.buf); APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), - "vcc_rx_aal5: n out of range (%d/%Zu)\n", + "vcc_rx_aal5: n out of range (%d/%zu)\n", n, lanai_buf_size(&lvcc->rx.buf)); /* Recover the second-to-last word to get true pdu length */ if ((x = &end[-2]) < lvcc->rx.buf.start) @@ -1493,9 +1493,9 @@ static int lanai_get_sized_buffer(struct lanai_dev *lanai, return -ENOMEM; if (unlikely(lanai_buf_size(buf) < size)) printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes " - "for %s buffer, got only %Zu\n", lanai->number, size, + "for %s buffer, got only %zu\n", lanai->number, size, name, lanai_buf_size(buf)); - DPRINTK("Allocated %Zu byte %s buffer\n", lanai_buf_size(buf), name); + DPRINTK("Allocated %zu byte %s buffer\n", lanai_buf_size(buf), name); return 0; } @@ -1586,7 +1586,7 @@ static int service_buffer_allocate(struct lanai_dev *lanai) lanai->pci); if (unlikely(lanai->service.start == NULL)) return -ENOMEM; - DPRINTK("allocated service buffer at 0x%08lX, size %Zu(%d)\n", + DPRINTK("allocated service buffer at 0x%08lX, size %zu(%d)\n", (unsigned long) lanai->service.start, lanai_buf_size(&lanai->service), lanai_buf_size_cardorder(&lanai->service)); @@ -2467,8 +2467,8 @@ static int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) (lanai->status & STATUS_LED) ? 1 : 0, (lanai->status & STATUS_GPIN) ? 1 : 0); if (left-- == 0) - return sprintf(page, "global buffer sizes: service=%Zu, " - "aal0_rx=%Zu\n", lanai_buf_size(&lanai->service), + return sprintf(page, "global buffer sizes: service=%zu, " + "aal0_rx=%zu\n", lanai_buf_size(&lanai->service), lanai->naal0 ? lanai_buf_size(&lanai->aal0buf) : 0); if (left-- == 0) { get_statistics(lanai); @@ -2513,7 +2513,7 @@ static int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) left += sprintf(&page[left], ",\n rx_AAL=%d", lvcc->rx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0); if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) - left += sprintf(&page[left], ", rx_buf_size=%Zu, " + left += sprintf(&page[left], ", rx_buf_size=%zu, " "rx_bad_len=%u,\n rx_service_trash=%u, " "rx_service_stream=%u, rx_bad_crc=%u", lanai_buf_size(&lvcc->rx.buf), @@ -2524,7 +2524,7 @@ static int lanai_proc_read(struct atm_dev *atmdev, loff_t *pos, char *page) } if (lvcc->tx.atmvcc != NULL) left += sprintf(&page[left], ",\n tx_AAL=%d, " - "tx_buf_size=%Zu, tx_qos=%cBR, tx_backlogged=%c", + "tx_buf_size=%zu, tx_qos=%cBR, tx_backlogged=%c", lvcc->tx.atmvcc->qos.aal == ATM_AAL5 ? 5 : 0, lanai_buf_size(&lvcc->tx.buf), lvcc->tx.atmvcc == lanai->cbrvcc ? 'C' : 'U', diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index cb28579e8a94..d879f3bca107 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -1980,13 +1980,12 @@ static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe) card->lbfqc = ns_stat_lfbqc_get(stat); id = le32_to_cpu(rsqe->buffer_handle); - skb = idr_find(&card->idr, id); + skb = idr_remove(&card->idr, id); if (!skb) { RXPRINTK(KERN_ERR - "nicstar%d: idr_find() failed!\n", card->index); + "nicstar%d: skb not found!\n", card->index); return; } - idr_remove(&card->idr, id); dma_sync_single_for_cpu(&card->pcidev->dev, NS_PRV_DMA(skb), (NS_PRV_BUFTYPE(skb) == BUF_SM diff --git a/drivers/base/core.c b/drivers/base/core.c index 8c25e68e67d7..684bda4d14a1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -26,6 +26,7 @@ #include <linux/mutex.h> #include <linux/pm_runtime.h> #include <linux/netdevice.h> +#include <linux/sched/signal.h> #include <linux/sysfs.h> #include "base.h" @@ -638,6 +639,11 @@ int lock_device_hotplug_sysfs(void) return restart_syscall(); } +void assert_held_device_hotplug(void) +{ + lockdep_assert_held(&device_hotplug_lock); +} + #ifdef CONFIG_BLOCK static inline int device_is_not_partition(struct device *dev) { diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 44a74cf1372c..d2fb9c8ed205 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -309,7 +309,8 @@ static int handle_remove(const char *nodename, struct device *dev) if (d_really_is_positive(dentry)) { struct kstat stat; struct path p = {.mnt = parent.mnt, .dentry = dentry}; - err = vfs_getattr(&p, &stat); + err = vfs_getattr(&p, &stat, STATX_TYPE | STATX_MODE, + AT_STATX_SYNC_AS_STAT); if (!err && dev_mynode(dev, d_inode(dentry), &stat)) { struct iattr newattrs; /* diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index e167a1e1bccb..b55804cac4c4 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -181,6 +181,7 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, * @dev: Pointer to device for which the allocation is performed. * @count: Requested number of pages. * @align: Requested alignment of pages (in PAGE_SIZE order). + * @gfp_mask: GFP flags to use for this allocation. * * This function allocates memory buffer for specified device. It uses * device specific contiguous memory area if available or the default @@ -188,12 +189,12 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, * function. */ struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, - unsigned int align) + unsigned int align, gfp_t gfp_mask) { if (align > CONFIG_CMA_ALIGNMENT) align = CONFIG_CMA_ALIGNMENT; - return cma_alloc(dev_get_cma_area(dev), count, align); + return cma_alloc(dev_get_cma_area(dev), count, align, gfp_mask); } /** diff --git a/drivers/base/memory.c b/drivers/base/memory.c index fa26ffd25fa6..cc4f1d0cbffe 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -249,7 +249,7 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t return ret; } -int memory_block_change_state(struct memory_block *mem, +static int memory_block_change_state(struct memory_block *mem, unsigned long to_state, unsigned long from_state_req) { int ret = 0; diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 3a75fb1b4126..e697dec9d25b 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -274,6 +274,93 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) } /** + * genpd_power_off - Remove power from a given PM domain. + * @genpd: PM domain to power down. + * @one_dev_on: If invoked from genpd's ->runtime_suspend|resume() callback, the + * RPM status of the releated device is in an intermediate state, not yet turned + * into RPM_SUSPENDED. This means genpd_power_off() must allow one device to not + * be RPM_SUSPENDED, while it tries to power off the PM domain. + * + * If all of the @genpd's devices have been suspended and all of its subdomains + * have been powered down, remove power from @genpd. + */ +static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, + unsigned int depth) +{ + struct pm_domain_data *pdd; + struct gpd_link *link; + unsigned int not_suspended = 0; + + /* + * Do not try to power off the domain in the following situations: + * (1) The domain is already in the "power off" state. + * (2) System suspend is in progress. + */ + if (genpd->status == GPD_STATE_POWER_OFF + || genpd->prepared_count > 0) + return 0; + + if (atomic_read(&genpd->sd_count) > 0) + return -EBUSY; + + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + enum pm_qos_flags_status stat; + + stat = dev_pm_qos_flags(pdd->dev, + PM_QOS_FLAG_NO_POWER_OFF + | PM_QOS_FLAG_REMOTE_WAKEUP); + if (stat > PM_QOS_FLAGS_NONE) + return -EBUSY; + + /* + * Do not allow PM domain to be powered off, when an IRQ safe + * device is part of a non-IRQ safe domain. + */ + if (!pm_runtime_suspended(pdd->dev) || + irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd)) + not_suspended++; + } + + if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on)) + return -EBUSY; + + if (genpd->gov && genpd->gov->power_down_ok) { + if (!genpd->gov->power_down_ok(&genpd->domain)) + return -EAGAIN; + } + + if (genpd->power_off) { + int ret; + + if (atomic_read(&genpd->sd_count) > 0) + return -EBUSY; + + /* + * If sd_count > 0 at this point, one of the subdomains hasn't + * managed to call genpd_power_on() for the master yet after + * incrementing it. In that case genpd_power_on() will wait + * for us to drop the lock, so we can call .power_off() and let + * the genpd_power_on() restore power for us (this shouldn't + * happen very often). + */ + ret = _genpd_power_off(genpd, true); + if (ret) + return ret; + } + + genpd->status = GPD_STATE_POWER_OFF; + + list_for_each_entry(link, &genpd->slave_links, slave_node) { + genpd_sd_counter_dec(link->master); + genpd_lock_nested(link->master, depth + 1); + genpd_power_off(link->master, false, depth + 1); + genpd_unlock(link->master); + } + + return 0; +} + +/** * genpd_power_on - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. * @depth: nesting count for lockdep. @@ -321,7 +408,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) &genpd->slave_links, slave_node) { genpd_sd_counter_dec(link->master); - genpd_queue_power_off_work(link->master); + genpd_lock_nested(link->master, depth + 1); + genpd_power_off(link->master, false, depth + 1); + genpd_unlock(link->master); } return ret; @@ -368,87 +457,6 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, } /** - * genpd_power_off - Remove power from a given PM domain. - * @genpd: PM domain to power down. - * @is_async: PM domain is powered down from a scheduled work - * - * If all of the @genpd's devices have been suspended and all of its subdomains - * have been powered down, remove power from @genpd. - */ -static int genpd_power_off(struct generic_pm_domain *genpd, bool is_async) -{ - struct pm_domain_data *pdd; - struct gpd_link *link; - unsigned int not_suspended = 0; - - /* - * Do not try to power off the domain in the following situations: - * (1) The domain is already in the "power off" state. - * (2) System suspend is in progress. - */ - if (genpd->status == GPD_STATE_POWER_OFF - || genpd->prepared_count > 0) - return 0; - - if (atomic_read(&genpd->sd_count) > 0) - return -EBUSY; - - list_for_each_entry(pdd, &genpd->dev_list, list_node) { - enum pm_qos_flags_status stat; - - stat = dev_pm_qos_flags(pdd->dev, - PM_QOS_FLAG_NO_POWER_OFF - | PM_QOS_FLAG_REMOTE_WAKEUP); - if (stat > PM_QOS_FLAGS_NONE) - return -EBUSY; - - /* - * Do not allow PM domain to be powered off, when an IRQ safe - * device is part of a non-IRQ safe domain. - */ - if (!pm_runtime_suspended(pdd->dev) || - irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd)) - not_suspended++; - } - - if (not_suspended > 1 || (not_suspended == 1 && is_async)) - return -EBUSY; - - if (genpd->gov && genpd->gov->power_down_ok) { - if (!genpd->gov->power_down_ok(&genpd->domain)) - return -EAGAIN; - } - - if (genpd->power_off) { - int ret; - - if (atomic_read(&genpd->sd_count) > 0) - return -EBUSY; - - /* - * If sd_count > 0 at this point, one of the subdomains hasn't - * managed to call genpd_power_on() for the master yet after - * incrementing it. In that case genpd_power_on() will wait - * for us to drop the lock, so we can call .power_off() and let - * the genpd_power_on() restore power for us (this shouldn't - * happen very often). - */ - ret = _genpd_power_off(genpd, true); - if (ret) - return ret; - } - - genpd->status = GPD_STATE_POWER_OFF; - - list_for_each_entry(link, &genpd->slave_links, slave_node) { - genpd_sd_counter_dec(link->master); - genpd_queue_power_off_work(link->master); - } - - return 0; -} - -/** * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0. * @work: Work structure used for scheduling the execution of this function. */ @@ -459,7 +467,7 @@ static void genpd_power_off_work_fn(struct work_struct *work) genpd = container_of(work, struct generic_pm_domain, power_off_work); genpd_lock(genpd); - genpd_power_off(genpd, true); + genpd_power_off(genpd, false, 0); genpd_unlock(genpd); } @@ -578,7 +586,7 @@ static int genpd_runtime_suspend(struct device *dev) return 0; genpd_lock(genpd); - genpd_power_off(genpd, false); + genpd_power_off(genpd, true, 0); genpd_unlock(genpd); return 0; @@ -658,7 +666,7 @@ err_poweroff: if (!pm_runtime_is_irq_safe(dev) || (pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) { genpd_lock(genpd); - genpd_power_off(genpd, 0); + genpd_power_off(genpd, true, 0); genpd_unlock(genpd); } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 249e0304597f..9faee1c893e5 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -27,6 +27,7 @@ #include <linux/pm_wakeirq.h> #include <linux/interrupt.h> #include <linux/sched.h> +#include <linux/sched/debug.h> #include <linux/async.h> #include <linux/suspend.h> #include <trace/events/power.h> diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 91ec3232d630..dae61720b314 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -231,7 +231,8 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) * The caller needs to ensure that opp_table (and hence the regulator) * isn't freed, while we are executing this routine. */ - for (i = 0; reg = regulators[i], i < count; i++) { + for (i = 0; i < count; i++) { + reg = regulators[i]; ret = regulator_set_voltage_time(reg, uV[i].min, uV[i].max); if (ret > 0) latency_ns += ret * 1000; diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index d888d9869b6a..f850daeffba4 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -17,12 +17,9 @@ * * This QoS design is best effort based. Dependents register their QoS needs. * Watchers register to keep track of the current QoS needs of the system. - * Watchers can register different types of notification callbacks: - * . a per-device notification callback using the dev_pm_qos_*_notifier API. - * The notification chain data is stored in the per-device constraint - * data struct. - * . a system-wide notification callback using the dev_pm_qos_*_global_notifier - * API. The notification chain data is stored in a static variable. + * Watchers can register a per-device notification callback using the + * dev_pm_qos_*_notifier API. The notification chain data is stored in the + * per-device constraint data struct. * * Note about the per-device constraint data struct allocation: * . The per-device constraints data struct ptr is tored into the device @@ -49,8 +46,6 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); static DEFINE_MUTEX(dev_pm_qos_sysfs_mtx); -static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); - /** * __dev_pm_qos_flags - Check PM QoS flags for a given device. * @dev: Device to check the PM QoS flags for. @@ -108,8 +103,7 @@ s32 __dev_pm_qos_read_value(struct device *dev) { lockdep_assert_held(&dev->power.lock); - return IS_ERR_OR_NULL(dev->power.qos) ? - 0 : pm_qos_read_value(&dev->power.qos->resume_latency); + return dev_pm_qos_raw_read_value(dev); } /** @@ -135,8 +129,7 @@ s32 dev_pm_qos_read_value(struct device *dev) * @value: Value to assign to the QoS request. * * Internal function to update the constraints list using the PM QoS core - * code and if needed call the per-device and the global notification - * callbacks + * code and if needed call the per-device callbacks. */ static int apply_constraint(struct dev_pm_qos_request *req, enum pm_qos_req_action action, s32 value) @@ -148,12 +141,6 @@ static int apply_constraint(struct dev_pm_qos_request *req, case DEV_PM_QOS_RESUME_LATENCY: ret = pm_qos_update_target(&qos->resume_latency, &req->data.pnode, action, value); - if (ret) { - value = pm_qos_read_value(&qos->resume_latency); - blocking_notifier_call_chain(&dev_pm_notifiers, - (unsigned long)value, - req); - } break; case DEV_PM_QOS_LATENCY_TOLERANCE: ret = pm_qos_update_target(&qos->latency_tolerance, @@ -536,36 +523,6 @@ int dev_pm_qos_remove_notifier(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); /** - * dev_pm_qos_add_global_notifier - sets notification entry for changes to - * target value of the PM QoS constraints for any device - * - * @notifier: notifier block managed by caller. - * - * Will register the notifier into a notification chain that gets called - * upon changes to the target value for any device. - */ -int dev_pm_qos_add_global_notifier(struct notifier_block *notifier) -{ - return blocking_notifier_chain_register(&dev_pm_notifiers, notifier); -} -EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier); - -/** - * dev_pm_qos_remove_global_notifier - deletes notification for changes to - * target value of PM QoS constraints for any device - * - * @notifier: notifier block to be removed. - * - * Will remove the notifier from the notification chain that gets called - * upon changes to the target value for any device. - */ -int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier) -{ - return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier); -} -EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier); - -/** * dev_pm_qos_add_ancestor_request - Add PM QoS request for device's ancestor. * @dev: Device whose ancestor to add the request for. * @req: Pointer to the preallocated handle. diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index a14fac6a01d3..7bcf80fa9ada 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -7,7 +7,7 @@ * This file is released under the GPLv2. */ -#include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/export.h> #include <linux/pm_runtime.h> #include <linux/pm_wakeirq.h> diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index f546f8f107b0..136854970489 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -8,7 +8,7 @@ #include <linux/device.h> #include <linux/slab.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/capability.h> #include <linux/export.h> #include <linux/suspend.h> diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 27d613795653..8e1a4554951c 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -348,7 +348,7 @@ static void cciss_unmap_sg_chain_block(ctlr_info_t *h, CommandList_struct *c) pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE); } -static void cciss_map_sg_chain_block(ctlr_info_t *h, CommandList_struct *c, +static int cciss_map_sg_chain_block(ctlr_info_t *h, CommandList_struct *c, SGDescriptor_struct *chain_block, int len) { SGDescriptor_struct *chain_sg; @@ -359,8 +359,16 @@ static void cciss_map_sg_chain_block(ctlr_info_t *h, CommandList_struct *c, chain_sg->Len = len; temp64.val = pci_map_single(h->pdev, chain_block, len, PCI_DMA_TODEVICE); + if (dma_mapping_error(&h->pdev->dev, temp64.val)) { + dev_warn(&h->pdev->dev, + "%s: error mapping chain block for DMA\n", + __func__); + return -1; + } chain_sg->Addr.lower = temp64.val32.lower; chain_sg->Addr.upper = temp64.val32.upper; + + return 0; } #include "cciss_scsi.c" /* For SCSI tape support */ @@ -3369,15 +3377,31 @@ static void do_cciss_request(struct request_queue *q) temp64.val = (__u64) pci_map_page(h->pdev, sg_page(&tmp_sg[i]), tmp_sg[i].offset, tmp_sg[i].length, dir); + if (dma_mapping_error(&h->pdev->dev, temp64.val)) { + dev_warn(&h->pdev->dev, + "%s: error mapping page for DMA\n", __func__); + creq->errors = make_status_bytes(SAM_STAT_GOOD, + 0, DRIVER_OK, + DID_SOFT_ERROR); + cmd_free(h, c); + return; + } curr_sg[sg_index].Addr.lower = temp64.val32.lower; curr_sg[sg_index].Addr.upper = temp64.val32.upper; curr_sg[sg_index].Ext = 0; /* we are not chaining */ ++sg_index; } - if (chained) - cciss_map_sg_chain_block(h, c, h->cmd_sg_list[c->cmdindex], + if (chained) { + if (cciss_map_sg_chain_block(h, c, h->cmd_sg_list[c->cmdindex], (seg - (h->max_cmd_sgentries - 1)) * - sizeof(SGDescriptor_struct)); + sizeof(SGDescriptor_struct))) { + creq->errors = make_status_bytes(SAM_STAT_GOOD, + 0, DRIVER_OK, + DID_SOFT_ERROR); + cmd_free(h, c); + return; + } + } /* track how many SG entries we are using */ if (seg > h->maxSG) diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c index a18de9d727b0..01a1f7e24978 100644 --- a/drivers/block/cciss_scsi.c +++ b/drivers/block/cciss_scsi.c @@ -17,15 +17,15 @@ * 02111-1307, USA. * * Questions/Comments/Bugfixes to iss_storagedev@hp.com - * + * * Author: Stephen M. Cameron */ #ifdef CONFIG_CISS_SCSI_TAPE -/* Here we have code to present the driver as a scsi driver - as it is simultaneously presented as a block driver. The +/* Here we have code to present the driver as a scsi driver + as it is simultaneously presented as a block driver. The reason for doing this is to allow access to SCSI tape drives - through the array controller. Note in particular, neither + through the array controller. Note in particular, neither physical nor logical disks are presented through the scsi layer. */ #include <linux/timer.h> @@ -37,7 +37,7 @@ #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> -#include <scsi/scsi_host.h> +#include <scsi/scsi_host.h> #include "cciss_scsi.h" @@ -120,7 +120,7 @@ struct cciss_scsi_adapter_data_t { struct cciss_scsi_cmd_stack_t cmd_stack; SGDescriptor_struct **cmd_sg_list; int registered; - spinlock_t lock; // to protect ccissscsi[ctlr]; + spinlock_t lock; // to protect ccissscsi[ctlr]; }; #define CPQ_TAPE_LOCK(h, flags) spin_lock_irqsave( \ @@ -143,36 +143,36 @@ scsi_cmd_alloc(ctlr_info_t *h) u64bit temp64; sa = h->scsi_ctlr; - stk = &sa->cmd_stack; + stk = &sa->cmd_stack; - if (stk->top < 0) + if (stk->top < 0) return NULL; - c = stk->elem[stk->top]; + c = stk->elem[stk->top]; /* memset(c, 0, sizeof(*c)); */ memset(&c->cmd, 0, sizeof(c->cmd)); memset(&c->Err, 0, sizeof(c->Err)); /* set physical addr of cmd and addr of scsi parameters */ - c->cmd.busaddr = c->busaddr; + c->cmd.busaddr = c->busaddr; c->cmd.cmdindex = c->cmdindex; - /* (__u32) (stk->cmd_pool_handle + + /* (__u32) (stk->cmd_pool_handle + (sizeof(struct cciss_scsi_cmd_stack_elem_t)*stk->top)); */ temp64.val = (__u64) (c->busaddr + sizeof(CommandList_struct)); - /* (__u64) (stk->cmd_pool_handle + + /* (__u64) (stk->cmd_pool_handle + (sizeof(struct cciss_scsi_cmd_stack_elem_t)*stk->top) + sizeof(CommandList_struct)); */ stk->top--; c->cmd.ErrDesc.Addr.lower = temp64.val32.lower; c->cmd.ErrDesc.Addr.upper = temp64.val32.upper; c->cmd.ErrDesc.Len = sizeof(ErrorInfo_struct); - + c->cmd.ctlr = h->ctlr; c->cmd.err_info = &c->Err; return (CommandList_struct *) c; } -static void +static void scsi_cmd_free(ctlr_info_t *h, CommandList_struct *c) { /* assume only one process in here at a time, locking done by caller. */ @@ -183,7 +183,7 @@ scsi_cmd_free(ctlr_info_t *h, CommandList_struct *c) struct cciss_scsi_cmd_stack_t *stk; sa = h->scsi_ctlr; - stk = &sa->cmd_stack; + stk = &sa->cmd_stack; stk->top++; if (stk->top >= stk->nelems) { dev_err(&h->pdev->dev, @@ -228,7 +228,7 @@ scsi_cmd_stack_setup(ctlr_info_t *h, struct cciss_scsi_adapter_data_t *sa) } for (i = 0; i < stk->nelems; i++) { stk->elem[i] = &stk->pool[i]; - stk->elem[i]->busaddr = (__u32) (stk->cmd_pool_handle + + stk->elem[i]->busaddr = (__u32) (stk->cmd_pool_handle + (sizeof(struct cciss_scsi_cmd_stack_elem_t) * i)); stk->elem[i]->cmdindex = i; } @@ -244,7 +244,7 @@ scsi_cmd_stack_free(ctlr_info_t *h) size_t size; sa = h->scsi_ctlr; - stk = &sa->cmd_stack; + stk = &sa->cmd_stack; if (stk->top != stk->nelems-1) { dev_warn(&h->pdev->dev, "bug: %d scsi commands are still outstanding.\n", @@ -266,7 +266,7 @@ print_cmd(CommandList_struct *cp) printk("queue:%d\n", cp->Header.ReplyQueue); printk("sglist:%d\n", cp->Header.SGList); printk("sgtot:%d\n", cp->Header.SGTotal); - printk("Tag:0x%08x/0x%08x\n", cp->Header.Tag.upper, + printk("Tag:0x%08x/0x%08x\n", cp->Header.Tag.upper, cp->Header.Tag.lower); printk("LUN:0x%8phN\n", cp->Header.LUN.LunAddrBytes); printk("CDBLen:%d\n", cp->Request.CDBLen); @@ -275,8 +275,8 @@ print_cmd(CommandList_struct *cp) printk(" Dir:%d\n",cp->Request.Type.Direction); printk("Timeout:%d\n",cp->Request.Timeout); printk("CDB: %16ph\n", cp->Request.CDB); - printk("edesc.Addr: 0x%08x/0%08x, Len = %d\n", - cp->ErrDesc.Addr.upper, cp->ErrDesc.Addr.lower, + printk("edesc.Addr: 0x%08x/0%08x, Len = %d\n", + cp->ErrDesc.Addr.upper, cp->ErrDesc.Addr.lower, cp->ErrDesc.Len); printk("sgs..........Errorinfo:\n"); printk("scsistatus:%d\n", cp->err_info->ScsiStatus); @@ -289,7 +289,7 @@ print_cmd(CommandList_struct *cp) } #endif -static int +static int find_bus_target_lun(ctlr_info_t *h, int *bus, int *target, int *lun) { /* finds an unused bus, target, lun for a new device */ @@ -299,24 +299,24 @@ find_bus_target_lun(ctlr_info_t *h, int *bus, int *target, int *lun) memset(&target_taken[0], 0, CCISS_MAX_SCSI_DEVS_PER_HBA); - target_taken[SELF_SCSI_ID] = 1; + target_taken[SELF_SCSI_ID] = 1; for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) target_taken[ccissscsi[h->ctlr].dev[i].target] = 1; - + for (i = 0; i < CCISS_MAX_SCSI_DEVS_PER_HBA; i++) { if (!target_taken[i]) { *bus = 0; *target=i; *lun = 0; found=1; break; } } - return (!found); + return (!found); } struct scsi2map { char scsi3addr[8]; int bus, target, lun; }; -static int +static int cciss_scsi_add_entry(ctlr_info_t *h, int hostno, struct cciss_scsi_dev_t *device, struct scsi2map *added, int *nadded) @@ -381,8 +381,8 @@ cciss_scsi_add_entry(ctlr_info_t *h, int hostno, ccissscsi[h->ctlr].ndevices++; - /* initially, (before registering with scsi layer) we don't - know our hostno and we don't want to print anything first + /* initially, (before registering with scsi layer) we don't + know our hostno and we don't want to print anything first time anyway (the scsi layer's inquiries will show that info) */ if (hostno != -1) dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n", @@ -467,7 +467,7 @@ adjust_cciss_scsi_table(ctlr_info_t *h, int hostno, /* sd contains scsi3 addresses and devtypes, but bus target and lun are not filled in. This funciton takes what's in sd to be the current and adjusts - ccissscsi[] to be in line with what's in sd. */ + ccissscsi[] to be in line with what's in sd. */ int i,j, found, changes=0; struct cciss_scsi_dev_t *csd; @@ -492,7 +492,7 @@ adjust_cciss_scsi_table(ctlr_info_t *h, int hostno, if (hostno != -1) /* if it's not the first time... */ sh = h->scsi_ctlr->scsi_host; - /* find any devices in ccissscsi[] that are not in + /* find any devices in ccissscsi[] that are not in sd[] and remove them from ccissscsi[] */ i = 0; @@ -512,7 +512,7 @@ adjust_cciss_scsi_table(ctlr_info_t *h, int hostno, } } - if (found == 0) { /* device no longer present. */ + if (found == 0) { /* device no longer present. */ changes++; cciss_scsi_remove_entry(h, hostno, i, removed, &nremoved); @@ -641,14 +641,13 @@ lookup_scsi3addr(ctlr_info_t *h, int bus, int target, int lun, char *scsi3addr) return -1; } -static void +static void cciss_scsi_setup(ctlr_info_t *h) { struct cciss_scsi_adapter_data_t * shba; ccissscsi[h->ctlr].ndevices = 0; - shba = (struct cciss_scsi_adapter_data_t *) - kmalloc(sizeof(*shba), GFP_KERNEL); + shba = kmalloc(sizeof(*shba), GFP_KERNEL); if (shba == NULL) return; shba->scsi_host = NULL; @@ -693,20 +692,18 @@ static void complete_scsi_command(CommandList_struct *c, int timeout, /* copy the sense data whether we need to or not. */ - memcpy(cmd->sense_buffer, ei->SenseInfo, + memcpy(cmd->sense_buffer, ei->SenseInfo, ei->SenseLen > SCSI_SENSE_BUFFERSIZE ? - SCSI_SENSE_BUFFERSIZE : + SCSI_SENSE_BUFFERSIZE : ei->SenseLen); scsi_set_resid(cmd, ei->ResidualCnt); - if(ei->CommandStatus != 0) - { /* an error has occurred */ - switch(ei->CommandStatus) - { + if (ei->CommandStatus != 0) { /* an error has occurred */ + switch (ei->CommandStatus) { case CMD_TARGET_STATUS: /* Pass it up to the upper layers... */ if (!ei->ScsiStatus) { - + /* Ordinarily, this case should never happen, but there is a bug in some released firmware revisions that allows it to happen if, for example, a 4100 backplane loses power and the tape @@ -731,7 +728,7 @@ static void complete_scsi_command(CommandList_struct *c, int timeout, print_cmd(c); */ /* We get CMD_INVALID if you address a non-existent tape drive instead - of a selection timeout (no response). You will see this if you yank + of a selection timeout (no response). You will see this if you yank out a tape drive, then try to access it. This is kind of a shame because it means that any other CMD_INVALID (e.g. driver bug) will get interpreted as a missing target. */ @@ -780,7 +777,7 @@ static void complete_scsi_command(CommandList_struct *c, int timeout, cmd->result = DID_ERROR << 16; dev_warn(&h->pdev->dev, "%p returned unknown status %x\n", c, - ei->CommandStatus); + ei->CommandStatus); } } cmd->scsi_done(cmd); @@ -796,15 +793,15 @@ cciss_scsi_detect(ctlr_info_t *h) sh = scsi_host_alloc(&cciss_driver_template, sizeof(struct ctlr_info *)); if (sh == NULL) goto fail; - sh->io_port = 0; // good enough? FIXME, + sh->io_port = 0; // good enough? FIXME, sh->n_io_port = 0; // I don't think we use these two... - sh->this_id = SELF_SCSI_ID; + sh->this_id = SELF_SCSI_ID; sh->can_queue = cciss_tape_cmds; sh->sg_tablesize = h->maxsgentries; sh->max_cmd_len = MAX_COMMAND_SIZE; sh->max_sectors = h->cciss_max_sectors; - ((struct cciss_scsi_adapter_data_t *) + ((struct cciss_scsi_adapter_data_t *) h->scsi_ctlr)->scsi_host = sh; sh->hostdata[0] = (unsigned long) h; sh->irq = h->intr[SIMPLE_MODE_INT]; @@ -856,7 +853,7 @@ cciss_map_one(struct pci_dev *pdev, static int cciss_scsi_do_simple_cmd(ctlr_info_t *h, CommandList_struct *c, - unsigned char *scsi3addr, + unsigned char *scsi3addr, unsigned char *cdb, unsigned char cdblen, unsigned char *buf, int bufsize, @@ -871,7 +868,7 @@ cciss_scsi_do_simple_cmd(ctlr_info_t *h, c->Header.Tag.lower = c->busaddr; /* Use k. address of cmd as tag */ // Fill in the request block... - /* printk("Using scsi3addr 0x%02x%0x2%0x2%0x2%0x2%0x2%0x2%0x2\n", + /* printk("Using scsi3addr 0x%02x%0x2%0x2%0x2%0x2%0x2%0x2%0x2\n", scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3], scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]); */ @@ -885,7 +882,7 @@ cciss_scsi_do_simple_cmd(ctlr_info_t *h, /* Fill in the SG list and do dma mapping */ cciss_map_one(h->pdev, c, (unsigned char *) buf, - bufsize, DMA_FROM_DEVICE); + bufsize, DMA_FROM_DEVICE); c->waiting = &wait; enqueue_cmd_and_start_io(h, c); @@ -896,14 +893,13 @@ cciss_scsi_do_simple_cmd(ctlr_info_t *h, return(0); } -static void +static void cciss_scsi_interpret_error(ctlr_info_t *h, CommandList_struct *c) { ErrorInfo_struct *ei; ei = c->err_info; - switch(ei->CommandStatus) - { + switch (ei->CommandStatus) { case CMD_TARGET_STATUS: dev_warn(&h->pdev->dev, "cmd %p has completed with errors\n", c); @@ -1005,7 +1001,7 @@ cciss_scsi_do_inquiry(ctlr_info_t *h, unsigned char *scsi3addr, if (rc != 0) return rc; /* something went wrong */ - if (ei->CommandStatus != 0 && + if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { cciss_scsi_interpret_error(h, c); rc = -1; @@ -1013,7 +1009,7 @@ cciss_scsi_do_inquiry(ctlr_info_t *h, unsigned char *scsi3addr, spin_lock_irqsave(&h->lock, flags); scsi_cmd_free(h, c); spin_unlock_irqrestore(&h->lock, flags); - return rc; + return rc; } /* Get the device id from inquiry page 0x83 */ @@ -1042,7 +1038,7 @@ cciss_scsi_do_report_phys_luns(ctlr_info_t *h, int rc; CommandList_struct *c; unsigned char cdb[12]; - unsigned char scsi3addr[8]; + unsigned char scsi3addr[8]; ErrorInfo_struct *ei; unsigned long flags; @@ -1069,14 +1065,14 @@ cciss_scsi_do_report_phys_luns(ctlr_info_t *h, cdb[11] = 0; rc = cciss_scsi_do_simple_cmd(h, c, scsi3addr, - cdb, 12, - (unsigned char *) buf, + cdb, 12, + (unsigned char *) buf, bufsize, XFER_READ); if (rc != 0) return rc; /* something went wrong */ ei = c->err_info; - if (ei->CommandStatus != 0 && + if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { cciss_scsi_interpret_error(h, c); rc = -1; @@ -1084,36 +1080,36 @@ cciss_scsi_do_report_phys_luns(ctlr_info_t *h, spin_lock_irqsave(&h->lock, flags); scsi_cmd_free(h, c); spin_unlock_irqrestore(&h->lock, flags); - return rc; + return rc; } static void cciss_update_non_disk_devices(ctlr_info_t *h, int hostno) { /* the idea here is we could get notified from /proc - that some devices have changed, so we do a report - physical luns cmd, and adjust our list of devices + that some devices have changed, so we do a report + physical luns cmd, and adjust our list of devices accordingly. (We can't rely on the scsi-mid layer just - doing inquiries, because the "busses" that the scsi + doing inquiries, because the "busses" that the scsi mid-layer probes are totally fabricated by this driver, so new devices wouldn't show up. - the scsi3addr's of devices won't change so long as the - adapter is not reset. That means we can rescan and - tell which devices we already know about, vs. new + the scsi3addr's of devices won't change so long as the + adapter is not reset. That means we can rescan and + tell which devices we already know about, vs. new devices, vs. disappearing devices. Also, if you yank out a tape drive, then put in a disk - in it's place, (say, a configured volume from another - array controller for instance) _don't_ poke this driver - (so it thinks it's still a tape, but _do_ poke the scsi - mid layer, so it does an inquiry... the scsi mid layer + in it's place, (say, a configured volume from another + array controller for instance) _don't_ poke this driver + (so it thinks it's still a tape, but _do_ poke the scsi + mid layer, so it does an inquiry... the scsi mid layer will see the physical disk. This would be bad. Need to - think about how to prevent that. One idea would be to + think about how to prevent that. One idea would be to snoop all scsi responses and if an inquiry repsonse comes back that reports a disk, chuck it an return selection timeout instead and adjust our table... Not sure i like - that though. + that though. */ #define OBDR_TAPE_INQ_SIZE 49 @@ -1141,9 +1137,9 @@ cciss_update_non_disk_devices(ctlr_info_t *h, int hostno) ch = &ld_buff->LUNListLength[0]; num_luns = ((ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | ch[3]) / 8; if (num_luns > CISS_MAX_PHYS_LUN) { - printk(KERN_WARNING + printk(KERN_WARNING "cciss: Maximum physical LUNs (%d) exceeded. " - "%d LUNs ignored.\n", CISS_MAX_PHYS_LUN, + "%d LUNs ignored.\n", CISS_MAX_PHYS_LUN, num_luns - CISS_MAX_PHYS_LUN); num_luns = CISS_MAX_PHYS_LUN; } @@ -1154,7 +1150,7 @@ cciss_update_non_disk_devices(ctlr_info_t *h, int hostno) } - /* adjust our table of devices */ + /* adjust our table of devices */ for (i = 0; i < num_luns; i++) { /* for each physical lun, do an inquiry */ if (ld_buff->LUN[i][3] & 0xC0) continue; @@ -1182,8 +1178,7 @@ cciss_update_non_disk_devices(ctlr_info_t *h, int hostno) cciss_scsi_get_device_id(h, scsi3addr, this_device->device_id, sizeof(this_device->device_id)); - switch (this_device->devtype) - { + switch (this_device->devtype) { case 0x05: /* CD-ROM */ { /* We don't *really* support actual CD-ROM devices, @@ -1213,7 +1208,7 @@ cciss_update_non_disk_devices(ctlr_info_t *h, int hostno) currentsd[ncurrent] = *this_device; ncurrent++; break; - default: + default: break; } } @@ -1258,8 +1253,8 @@ cciss_scsi_write_info(struct Scsi_Host *sh, return -EINVAL; return cciss_scsi_user_command(h, sh->host_no, - buffer, length); -} + buffer, length); +} static int cciss_scsi_show_info(struct seq_file *m, struct Scsi_Host *sh) @@ -1297,8 +1292,8 @@ cciss_scsi_show_info(struct seq_file *m, struct Scsi_Host *sh) return 0; } -/* cciss_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci - dma mapping and fills in the scatter gather entries of the +/* cciss_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci + dma mapping and fills in the scatter gather entries of the cciss command, c. */ static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *c, @@ -1394,7 +1389,7 @@ cciss_scsi_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmn // Fill in the command list header - cmd->scsi_done = done; // save this for use by completion code + cmd->scsi_done = done; // save this for use by completion code /* save c in case we have to abort it */ cmd->host_scribble = (unsigned char *) c; @@ -1404,7 +1399,7 @@ cciss_scsi_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmn c->Header.ReplyQueue = 0; /* unused in simple mode */ memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8); c->Header.Tag.lower = c->busaddr; /* Use k. address of cmd as tag */ - + // Fill in the request block... c->Request.Timeout = 0; @@ -1414,8 +1409,7 @@ cciss_scsi_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmn memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len); c->Request.Type.Type = TYPE_CMD; c->Request.Type.Attribute = ATTR_SIMPLE; - switch(cmd->sc_data_direction) - { + switch (cmd->sc_data_direction) { case DMA_TO_DEVICE: c->Request.Type.Direction = XFER_WRITE; break; @@ -1432,15 +1426,15 @@ cciss_scsi_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmn c->Request.Type.Direction = XFER_RSVD; // This is technically wrong, and cciss controllers should - // reject it with CMD_INVALID, which is the most correct - // response, but non-fibre backends appear to let it + // reject it with CMD_INVALID, which is the most correct + // response, but non-fibre backends appear to let it // slide by, and give the same results as if this field // were set correctly. Either way is acceptable for // our purposes here. break; - default: + default: dev_warn(&h->pdev->dev, "unknown data direction: %d\n", cmd->sc_data_direction); BUG(); @@ -1464,9 +1458,9 @@ static void cciss_unregister_scsi(ctlr_info_t *h) spin_lock_irqsave(&h->lock, flags); sa = h->scsi_ctlr; - stk = &sa->cmd_stack; + stk = &sa->cmd_stack; - /* if we weren't ever actually registered, don't unregister */ + /* if we weren't ever actually registered, don't unregister */ if (sa->registered) { spin_unlock_irqrestore(&h->lock, flags); scsi_remove_host(sa->scsi_host); @@ -1474,7 +1468,7 @@ static void cciss_unregister_scsi(ctlr_info_t *h) spin_lock_irqsave(&h->lock, flags); } - /* set scsi_host to NULL so our detect routine will + /* set scsi_host to NULL so our detect routine will find us on register */ sa->scsi_host = NULL; spin_unlock_irqrestore(&h->lock, flags); @@ -1490,7 +1484,7 @@ static int cciss_engage_scsi(ctlr_info_t *h) spin_lock_irqsave(&h->lock, flags); sa = h->scsi_ctlr; - stk = &sa->cmd_stack; + stk = &sa->cmd_stack; if (sa->registered) { dev_info(&h->pdev->dev, "SCSI subsystem already engaged.\n"); @@ -1586,13 +1580,13 @@ retry_tur: return rc; } -/* Need at least one of these error handlers to keep ../scsi/hosts.c from - * complaining. Doing a host- or bus-reset can't do anything good here. +/* Need at least one of these error handlers to keep ../scsi/hosts.c from + * complaining. Doing a host- or bus-reset can't do anything good here. * Despite what it might say in scsi_error.c, there may well be commands * on the controller, as the cciss driver registers twice, once as a block * device for the logical drives, and once as a scsi device, for any tape * drives. So we know there are no commands out on the tape drives, but we - * don't know there are no commands on the controller, and it is likely + * don't know there are no commands on the controller, and it is likely * that there probably are, as the cciss block device is most commonly used * as a boot device (embedded controller on HP/Compaq systems.) */ diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 4cb8f21ff4ef..724d1c50fc52 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -30,7 +30,7 @@ #include <linux/compiler.h> #include <linux/types.h> #include <linux/list.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/bitops.h> #include <linux/slab.h> #include <linux/ratelimit.h> diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 615e5b5178a0..92c60cbd04ee 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -52,6 +52,7 @@ #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> #include <linux/vmalloc.h> +#include <linux/sched/signal.h> #include <linux/drbd_limits.h> #include "drbd_int.h" @@ -1846,7 +1847,7 @@ int drbd_send_out_of_sync(struct drbd_peer_device *peer_device, struct drbd_requ int drbd_send(struct drbd_connection *connection, struct socket *sock, void *buf, size_t size, unsigned msg_flags) { - struct kvec iov; + struct kvec iov = {.iov_base = buf, .iov_len = size}; struct msghdr msg; int rv, sent = 0; @@ -1855,15 +1856,14 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock, /* THINK if (signal_pending) return ... ? */ - iov.iov_base = buf; - iov.iov_len = size; - msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = msg_flags | MSG_NOSIGNAL; + iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iov, 1, size); + if (sock == connection->data.socket) { rcu_read_lock(); connection->ko_count = rcu_dereference(connection->net_conf)->ko_count; @@ -1871,7 +1871,7 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock, drbd_update_congested(connection); } do { - rv = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len); + rv = sock_sendmsg(sock, &msg); if (rv == -EAGAIN) { if (we_should_drop_the_connection(connection, sock)) break; @@ -1885,8 +1885,6 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock, if (rv < 0) break; sent += rv; - iov.iov_base += rv; - iov.iov_len -= rv; } while (sent < size); if (sock == connection->data.socket) @@ -2915,11 +2913,9 @@ out_idr_remove_vol: idr_remove(&connection->peer_devices, vnr); out_idr_remove_from_resource: for_each_connection(connection, resource) { - peer_device = idr_find(&connection->peer_devices, vnr); - if (peer_device) { - idr_remove(&connection->peer_devices, vnr); + peer_device = idr_remove(&connection->peer_devices, vnr); + if (peer_device) kref_put(&connection->kref, drbd_destroy_connection); - } } for_each_peer_device_safe(peer_device, tmp_peer_device, device) { list_del(&peer_device->peer_devices); diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index c7728dd77230..aa6bf9692eff 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -36,6 +36,8 @@ #include <linux/memcontrol.h> #include <linux/mm_inline.h> #include <linux/slab.h> +#include <uapi/linux/sched/types.h> +#include <linux/sched/signal.h> #include <linux/pkt_sched.h> #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index c6755c9a0aea..3bff33f21435 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -25,7 +25,7 @@ #include <linux/module.h> #include <linux/drbd.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/mm.h> #include <linux/memcontrol.h> diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 304377182c1a..0ecb6461ed81 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -186,7 +186,7 @@ static void __loop_update_dio(struct loop_device *lo, bool dio) * * TODO: the above condition may be loosed in the future, and * direct I/O may be switched runtime at that time because most - * of requests in sane appplications should be PAGE_SIZE algined + * of requests in sane applications should be PAGE_SIZE aligned */ if (dio) { if (queue_logical_block_size(lo->lo_queue) >= sb_bsize && @@ -501,9 +501,9 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, cmd->iocb.ki_flags = IOCB_DIRECT; if (rw == WRITE) - ret = file->f_op->write_iter(&cmd->iocb, &iter); + ret = call_write_iter(file, &cmd->iocb, &iter); else - ret = file->f_op->read_iter(&cmd->iocb, &iter); + ret = call_read_iter(file, &cmd->iocb, &iter); if (ret != -EIOCBQUEUED) cmd->iocb.ki_complete(&cmd->iocb, ret, 0); @@ -1142,13 +1142,6 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) (info->lo_flags & LO_FLAGS_AUTOCLEAR)) lo->lo_flags ^= LO_FLAGS_AUTOCLEAR; - if ((info->lo_flags & LO_FLAGS_PARTSCAN) && - !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { - lo->lo_flags |= LO_FLAGS_PARTSCAN; - lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; - loop_reread_partitions(lo, lo->lo_device); - } - lo->lo_encrypt_key_size = info->lo_encrypt_key_size; lo->lo_init[0] = info->lo_init[0]; lo->lo_init[1] = info->lo_init[1]; @@ -1163,6 +1156,14 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) exit: blk_mq_unfreeze_queue(lo->lo_queue); + + if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) && + !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { + lo->lo_flags |= LO_FLAGS_PARTSCAN; + lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; + loop_reread_partitions(lo, lo->lo_device); + } + return err; } @@ -1175,7 +1176,8 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info) if (lo->lo_state != Lo_bound) return -ENXIO; - error = vfs_getattr(&file->f_path, &stat); + error = vfs_getattr(&file->f_path, &stat, + STATX_INO, AT_STATX_SYNC_AS_STAT); if (error) return error; memset(info, 0, sizeof(*info)); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 0be84a3cb6d7..7e4287bc19e5 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -96,6 +96,10 @@ static int max_part; static struct workqueue_struct *recv_workqueue; static int part_shift; +static int nbd_dev_dbg_init(struct nbd_device *nbd); +static void nbd_dev_dbg_close(struct nbd_device *nbd); + + static inline struct device *nbd_to_dev(struct nbd_device *nbd) { return disk_to_dev(nbd->disk); @@ -120,7 +124,7 @@ static const char *nbdcmd_to_ascii(int cmd) static int nbd_size_clear(struct nbd_device *nbd, struct block_device *bdev) { - bdev->bd_inode->i_size = 0; + bd_set_size(bdev, 0); set_capacity(nbd->disk, 0); kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE); @@ -129,29 +133,20 @@ static int nbd_size_clear(struct nbd_device *nbd, struct block_device *bdev) static void nbd_size_update(struct nbd_device *nbd, struct block_device *bdev) { - if (!nbd_is_connected(nbd)) - return; - - bdev->bd_inode->i_size = nbd->bytesize; + blk_queue_logical_block_size(nbd->disk->queue, nbd->blksize); + blk_queue_physical_block_size(nbd->disk->queue, nbd->blksize); + bd_set_size(bdev, nbd->bytesize); set_capacity(nbd->disk, nbd->bytesize >> 9); kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE); } -static int nbd_size_set(struct nbd_device *nbd, struct block_device *bdev, +static void nbd_size_set(struct nbd_device *nbd, struct block_device *bdev, loff_t blocksize, loff_t nr_blocks) { - int ret; - - ret = set_blocksize(bdev, blocksize); - if (ret) - return ret; - nbd->blksize = blocksize; nbd->bytesize = blocksize * nr_blocks; - - nbd_size_update(nbd, bdev); - - return 0; + if (nbd_is_connected(nbd)) + nbd_size_update(nbd, bdev); } static void nbd_end_request(struct nbd_cmd *cmd) @@ -206,13 +201,12 @@ static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req, /* * Send or receive packet. */ -static int sock_xmit(struct nbd_device *nbd, int index, int send, void *buf, - int size, int msg_flags) +static int sock_xmit(struct nbd_device *nbd, int index, int send, + struct iov_iter *iter, int msg_flags) { struct socket *sock = nbd->socks[index]->sock; int result; struct msghdr msg; - struct kvec iov; unsigned long pflags = current->flags; if (unlikely(!sock)) { @@ -222,11 +216,11 @@ static int sock_xmit(struct nbd_device *nbd, int index, int send, void *buf, return -EINVAL; } + msg.msg_iter = *iter; + current->flags |= PF_MEMALLOC; do { sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC; - iov.iov_base = buf; - iov.iov_len = size; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = NULL; @@ -234,47 +228,37 @@ static int sock_xmit(struct nbd_device *nbd, int index, int send, void *buf, msg.msg_flags = msg_flags | MSG_NOSIGNAL; if (send) - result = kernel_sendmsg(sock, &msg, &iov, 1, size); + result = sock_sendmsg(sock, &msg); else - result = kernel_recvmsg(sock, &msg, &iov, 1, size, - msg.msg_flags); + result = sock_recvmsg(sock, &msg, msg.msg_flags); if (result <= 0) { if (result == 0) result = -EPIPE; /* short read */ break; } - size -= result; - buf += result; - } while (size > 0); + } while (msg_data_left(&msg)); tsk_restore_flags(current, pflags, PF_MEMALLOC); return result; } -static inline int sock_send_bvec(struct nbd_device *nbd, int index, - struct bio_vec *bvec, int flags) -{ - int result; - void *kaddr = kmap(bvec->bv_page); - result = sock_xmit(nbd, index, 1, kaddr + bvec->bv_offset, - bvec->bv_len, flags); - kunmap(bvec->bv_page); - return result; -} - /* always call with the tx_lock held */ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index) { struct request *req = blk_mq_rq_from_pdu(cmd); int result; - struct nbd_request request; + struct nbd_request request = {.magic = htonl(NBD_REQUEST_MAGIC)}; + struct kvec iov = {.iov_base = &request, .iov_len = sizeof(request)}; + struct iov_iter from; unsigned long size = blk_rq_bytes(req); struct bio *bio; u32 type; u32 tag = blk_mq_unique_tag(req); + iov_iter_kvec(&from, WRITE | ITER_KVEC, &iov, 1, sizeof(request)); + switch (req_op(req)) { case REQ_OP_DISCARD: type = NBD_CMD_TRIM; @@ -299,8 +283,6 @@ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index) return -EIO; } - memset(&request, 0, sizeof(request)); - request.magic = htonl(NBD_REQUEST_MAGIC); request.type = htonl(type); if (type != NBD_CMD_FLUSH) { request.from = cpu_to_be64((u64)blk_rq_pos(req) << 9); @@ -311,7 +293,7 @@ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index) dev_dbg(nbd_to_dev(nbd), "request %p: sending control (%s@%llu,%uB)\n", cmd, nbdcmd_to_ascii(type), (unsigned long long)blk_rq_pos(req) << 9, blk_rq_bytes(req)); - result = sock_xmit(nbd, index, 1, &request, sizeof(request), + result = sock_xmit(nbd, index, 1, &from, (type == NBD_CMD_WRITE) ? MSG_MORE : 0); if (result <= 0) { dev_err_ratelimited(disk_to_dev(nbd->disk), @@ -334,7 +316,9 @@ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index) dev_dbg(nbd_to_dev(nbd), "request %p: sending %d bytes data\n", cmd, bvec.bv_len); - result = sock_send_bvec(nbd, index, &bvec, flags); + iov_iter_bvec(&from, ITER_BVEC | WRITE, + &bvec, 1, bvec.bv_len); + result = sock_xmit(nbd, index, 1, &from, flags); if (result <= 0) { dev_err(disk_to_dev(nbd->disk), "Send data failed (result %d)\n", @@ -355,17 +339,6 @@ static int nbd_send_cmd(struct nbd_device *nbd, struct nbd_cmd *cmd, int index) return 0; } -static inline int sock_recv_bvec(struct nbd_device *nbd, int index, - struct bio_vec *bvec) -{ - int result; - void *kaddr = kmap(bvec->bv_page); - result = sock_xmit(nbd, index, 0, kaddr + bvec->bv_offset, - bvec->bv_len, MSG_WAITALL); - kunmap(bvec->bv_page); - return result; -} - /* NULL returned = something went wrong, inform userspace */ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index) { @@ -375,9 +348,12 @@ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index) struct request *req = NULL; u16 hwq; u32 tag; + struct kvec iov = {.iov_base = &reply, .iov_len = sizeof(reply)}; + struct iov_iter to; reply.magic = 0; - result = sock_xmit(nbd, index, 0, &reply, sizeof(reply), MSG_WAITALL); + iov_iter_kvec(&to, READ | ITER_KVEC, &iov, 1, sizeof(reply)); + result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL); if (result <= 0) { if (!test_bit(NBD_DISCONNECTED, &nbd->runtime_flags) && !test_bit(NBD_DISCONNECT_REQUESTED, &nbd->runtime_flags)) @@ -417,7 +393,9 @@ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index) struct bio_vec bvec; rq_for_each_segment(bvec, req, iter) { - result = sock_recv_bvec(nbd, index, &bvec); + iov_iter_bvec(&to, ITER_BVEC | READ, + &bvec, 1, bvec.bv_len); + result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL); if (result <= 0) { dev_err(disk_to_dev(nbd->disk), "Receive data failed (result %d)\n", result); @@ -571,10 +549,17 @@ static int nbd_queue_rq(struct blk_mq_hw_ctx *hctx, return BLK_MQ_RQ_QUEUE_OK; } -static int nbd_add_socket(struct nbd_device *nbd, struct socket *sock) +static int nbd_add_socket(struct nbd_device *nbd, struct block_device *bdev, + unsigned long arg) { + struct socket *sock; struct nbd_sock **socks; struct nbd_sock *nsock; + int err; + + sock = sockfd_lookup(arg, &err); + if (!sock) + return err; if (!nbd->task_setup) nbd->task_setup = current; @@ -598,26 +583,20 @@ static int nbd_add_socket(struct nbd_device *nbd, struct socket *sock) nsock->sock = sock; socks[nbd->num_connections++] = nsock; + if (max_part) + bdev->bd_invalidated = 1; return 0; } /* Reset all properties of an NBD device */ static void nbd_reset(struct nbd_device *nbd) { - int i; - - for (i = 0; i < nbd->num_connections; i++) - kfree(nbd->socks[i]); - kfree(nbd->socks); - nbd->socks = NULL; nbd->runtime_flags = 0; nbd->blksize = 1024; nbd->bytesize = 0; set_capacity(nbd->disk, 0); nbd->flags = 0; nbd->tag_set.timeout = 0; - nbd->num_connections = 0; - nbd->task_setup = NULL; queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue); } @@ -645,95 +624,162 @@ static void nbd_parse_flags(struct nbd_device *nbd, struct block_device *bdev) static void send_disconnects(struct nbd_device *nbd) { - struct nbd_request request = {}; + struct nbd_request request = { + .magic = htonl(NBD_REQUEST_MAGIC), + .type = htonl(NBD_CMD_DISC), + }; + struct kvec iov = {.iov_base = &request, .iov_len = sizeof(request)}; + struct iov_iter from; int i, ret; - request.magic = htonl(NBD_REQUEST_MAGIC); - request.type = htonl(NBD_CMD_DISC); - for (i = 0; i < nbd->num_connections; i++) { - ret = sock_xmit(nbd, i, 1, &request, sizeof(request), 0); + iov_iter_kvec(&from, WRITE | ITER_KVEC, &iov, 1, sizeof(request)); + ret = sock_xmit(nbd, i, 1, &from, 0); if (ret <= 0) dev_err(disk_to_dev(nbd->disk), "Send disconnect failed %d\n", ret); } } -static int nbd_dev_dbg_init(struct nbd_device *nbd); -static void nbd_dev_dbg_close(struct nbd_device *nbd); +static int nbd_disconnect(struct nbd_device *nbd, struct block_device *bdev) +{ + dev_info(disk_to_dev(nbd->disk), "NBD_DISCONNECT\n"); + if (!nbd->socks) + return -EINVAL; -/* Must be called with config_lock held */ -static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, - unsigned int cmd, unsigned long arg) + mutex_unlock(&nbd->config_lock); + fsync_bdev(bdev); + mutex_lock(&nbd->config_lock); + + /* Check again after getting mutex back. */ + if (!nbd->socks) + return -EINVAL; + + if (!test_and_set_bit(NBD_DISCONNECT_REQUESTED, + &nbd->runtime_flags)) + send_disconnects(nbd); + return 0; +} + +static int nbd_clear_sock(struct nbd_device *nbd, struct block_device *bdev) { - switch (cmd) { - case NBD_DISCONNECT: { - dev_info(disk_to_dev(nbd->disk), "NBD_DISCONNECT\n"); - if (!nbd->socks) - return -EINVAL; - - mutex_unlock(&nbd->config_lock); - fsync_bdev(bdev); - mutex_lock(&nbd->config_lock); - - /* Check again after getting mutex back. */ - if (!nbd->socks) - return -EINVAL; - - if (!test_and_set_bit(NBD_DISCONNECT_REQUESTED, - &nbd->runtime_flags)) - send_disconnects(nbd); - return 0; - } + sock_shutdown(nbd); + nbd_clear_que(nbd); + kill_bdev(bdev); + nbd_bdev_reset(bdev); + /* + * We want to give the run thread a chance to wait for everybody + * to clean up and then do it's own cleanup. + */ + if (!test_bit(NBD_RUNNING, &nbd->runtime_flags) && + nbd->num_connections) { + int i; - case NBD_CLEAR_SOCK: - sock_shutdown(nbd); - nbd_clear_que(nbd); - kill_bdev(bdev); - nbd_bdev_reset(bdev); - /* - * We want to give the run thread a chance to wait for everybody - * to clean up and then do it's own cleanup. - */ - if (!test_bit(NBD_RUNNING, &nbd->runtime_flags)) { - int i; - - for (i = 0; i < nbd->num_connections; i++) - kfree(nbd->socks[i]); - kfree(nbd->socks); - nbd->socks = NULL; - nbd->num_connections = 0; - nbd->task_setup = NULL; + for (i = 0; i < nbd->num_connections; i++) { + sockfd_put(nbd->socks[i]->sock); + kfree(nbd->socks[i]); } - return 0; + kfree(nbd->socks); + nbd->socks = NULL; + nbd->num_connections = 0; + } + nbd->task_setup = NULL; - case NBD_SET_SOCK: { - int err; - struct socket *sock = sockfd_lookup(arg, &err); + return 0; +} - if (!sock) - return err; +static int nbd_start_device(struct nbd_device *nbd, struct block_device *bdev) +{ + struct recv_thread_args *args; + int num_connections = nbd->num_connections; + int error = 0, i; - err = nbd_add_socket(nbd, sock); - if (!err && max_part) - bdev->bd_invalidated = 1; + if (nbd->task_recv) + return -EBUSY; + if (!nbd->socks) + return -EINVAL; + if (num_connections > 1 && + !(nbd->flags & NBD_FLAG_CAN_MULTI_CONN)) { + dev_err(disk_to_dev(nbd->disk), "server does not support multiple connections per device.\n"); + error = -EINVAL; + goto out_err; + } - return err; + set_bit(NBD_RUNNING, &nbd->runtime_flags); + blk_mq_update_nr_hw_queues(&nbd->tag_set, nbd->num_connections); + args = kcalloc(num_connections, sizeof(*args), GFP_KERNEL); + if (!args) { + error = -ENOMEM; + goto out_err; + } + nbd->task_recv = current; + mutex_unlock(&nbd->config_lock); + + nbd_parse_flags(nbd, bdev); + + error = device_create_file(disk_to_dev(nbd->disk), &pid_attr); + if (error) { + dev_err(disk_to_dev(nbd->disk), "device_create_file failed!\n"); + goto out_recv; } - case NBD_SET_BLKSIZE: { - loff_t bsize = div_s64(nbd->bytesize, arg); + nbd_size_update(nbd, bdev); - return nbd_size_set(nbd, bdev, arg, bsize); + nbd_dev_dbg_init(nbd); + for (i = 0; i < num_connections; i++) { + sk_set_memalloc(nbd->socks[i]->sock->sk); + atomic_inc(&nbd->recv_threads); + INIT_WORK(&args[i].work, recv_work); + args[i].nbd = nbd; + args[i].index = i; + queue_work(recv_workqueue, &args[i].work); } + wait_event_interruptible(nbd->recv_wq, + atomic_read(&nbd->recv_threads) == 0); + for (i = 0; i < num_connections; i++) + flush_work(&args[i].work); + nbd_dev_dbg_close(nbd); + nbd_size_clear(nbd, bdev); + device_remove_file(disk_to_dev(nbd->disk), &pid_attr); +out_recv: + mutex_lock(&nbd->config_lock); + nbd->task_recv = NULL; +out_err: + clear_bit(NBD_RUNNING, &nbd->runtime_flags); + nbd_clear_sock(nbd, bdev); - case NBD_SET_SIZE: - return nbd_size_set(nbd, bdev, nbd->blksize, - div_s64(arg, nbd->blksize)); + /* user requested, ignore socket errors */ + if (test_bit(NBD_DISCONNECT_REQUESTED, &nbd->runtime_flags)) + error = 0; + if (test_bit(NBD_TIMEDOUT, &nbd->runtime_flags)) + error = -ETIMEDOUT; - case NBD_SET_SIZE_BLOCKS: - return nbd_size_set(nbd, bdev, nbd->blksize, arg); + nbd_reset(nbd); + return error; +} +/* Must be called with config_lock held */ +static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case NBD_DISCONNECT: + return nbd_disconnect(nbd, bdev); + case NBD_CLEAR_SOCK: + return nbd_clear_sock(nbd, bdev); + case NBD_SET_SOCK: + return nbd_add_socket(nbd, bdev, arg); + case NBD_SET_BLKSIZE: + nbd_size_set(nbd, bdev, arg, + div_s64(nbd->bytesize, arg)); + return 0; + case NBD_SET_SIZE: + nbd_size_set(nbd, bdev, nbd->blksize, + div_s64(arg, nbd->blksize)); + return 0; + case NBD_SET_SIZE_BLOCKS: + nbd_size_set(nbd, bdev, nbd->blksize, arg); + return 0; case NBD_SET_TIMEOUT: nbd->tag_set.timeout = arg * HZ; return 0; @@ -741,85 +787,14 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, case NBD_SET_FLAGS: nbd->flags = arg; return 0; - - case NBD_DO_IT: { - struct recv_thread_args *args; - int num_connections = nbd->num_connections; - int error = 0, i; - - if (nbd->task_recv) - return -EBUSY; - if (!nbd->socks) - return -EINVAL; - if (num_connections > 1 && - !(nbd->flags & NBD_FLAG_CAN_MULTI_CONN)) { - dev_err(disk_to_dev(nbd->disk), "server does not support multiple connections per device.\n"); - error = -EINVAL; - goto out_err; - } - - set_bit(NBD_RUNNING, &nbd->runtime_flags); - blk_mq_update_nr_hw_queues(&nbd->tag_set, nbd->num_connections); - args = kcalloc(num_connections, sizeof(*args), GFP_KERNEL); - if (!args) { - error = -ENOMEM; - goto out_err; - } - nbd->task_recv = current; - mutex_unlock(&nbd->config_lock); - - nbd_parse_flags(nbd, bdev); - - error = device_create_file(disk_to_dev(nbd->disk), &pid_attr); - if (error) { - dev_err(disk_to_dev(nbd->disk), "device_create_file failed!\n"); - goto out_recv; - } - - nbd_size_update(nbd, bdev); - - nbd_dev_dbg_init(nbd); - for (i = 0; i < num_connections; i++) { - sk_set_memalloc(nbd->socks[i]->sock->sk); - atomic_inc(&nbd->recv_threads); - INIT_WORK(&args[i].work, recv_work); - args[i].nbd = nbd; - args[i].index = i; - queue_work(recv_workqueue, &args[i].work); - } - wait_event_interruptible(nbd->recv_wq, - atomic_read(&nbd->recv_threads) == 0); - for (i = 0; i < num_connections; i++) - flush_work(&args[i].work); - nbd_dev_dbg_close(nbd); - nbd_size_clear(nbd, bdev); - device_remove_file(disk_to_dev(nbd->disk), &pid_attr); -out_recv: - mutex_lock(&nbd->config_lock); - nbd->task_recv = NULL; -out_err: - sock_shutdown(nbd); - nbd_clear_que(nbd); - kill_bdev(bdev); - nbd_bdev_reset(bdev); - - /* user requested, ignore socket errors */ - if (test_bit(NBD_DISCONNECT_REQUESTED, &nbd->runtime_flags)) - error = 0; - if (test_bit(NBD_TIMEDOUT, &nbd->runtime_flags)) - error = -ETIMEDOUT; - - nbd_reset(nbd); - return error; - } - + case NBD_DO_IT: + return nbd_start_device(nbd, bdev); case NBD_CLEAR_QUE: /* * This is for compatibility only. The queue is always cleared * by NBD_DO_IT or NBD_CLEAR_SOCK. */ return 0; - case NBD_PRINT_DEBUG: /* * For compatibility only, we no longer keep a list of @@ -1134,8 +1109,10 @@ static int __init nbd_init(void) if (!recv_workqueue) return -ENOMEM; - if (register_blkdev(NBD_MAJOR, "nbd")) + if (register_blkdev(NBD_MAJOR, "nbd")) { + destroy_workqueue(recv_workqueue); return -EIO; + } nbd_dbg_init(); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 362cecc77130..4d6807723798 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -123,9 +123,11 @@ static int atomic_dec_return_safe(atomic_t *v) #define RBD_FEATURE_LAYERING (1<<0) #define RBD_FEATURE_STRIPINGV2 (1<<1) #define RBD_FEATURE_EXCLUSIVE_LOCK (1<<2) +#define RBD_FEATURE_DATA_POOL (1<<7) #define RBD_FEATURES_ALL (RBD_FEATURE_LAYERING | \ RBD_FEATURE_STRIPINGV2 | \ - RBD_FEATURE_EXCLUSIVE_LOCK) + RBD_FEATURE_EXCLUSIVE_LOCK | \ + RBD_FEATURE_DATA_POOL) /* Features supported by this (client software) implementation. */ @@ -144,10 +146,9 @@ struct rbd_image_header { /* These six fields never change for a given rbd image */ char *object_prefix; __u8 obj_order; - __u8 crypt_type; - __u8 comp_type; u64 stripe_unit; u64 stripe_count; + s64 data_pool_id; u64 features; /* Might be changeable someday? */ /* The remaining fields need to be updated occasionally */ @@ -230,7 +231,7 @@ enum obj_req_flags { }; struct rbd_obj_request { - const char *object_name; + u64 object_no; u64 offset; /* object start byte */ u64 length; /* bytes from offset */ unsigned long flags; @@ -438,7 +439,6 @@ static DEFINE_SPINLOCK(rbd_client_list_lock); static struct kmem_cache *rbd_img_request_cache; static struct kmem_cache *rbd_obj_request_cache; -static struct kmem_cache *rbd_segment_name_cache; static int rbd_major; static DEFINE_IDA(rbd_dev_id_ida); @@ -973,6 +973,30 @@ static bool rbd_dev_ondisk_valid(struct rbd_image_header_ondisk *ondisk) } /* + * returns the size of an object in the image + */ +static u32 rbd_obj_bytes(struct rbd_image_header *header) +{ + return 1U << header->obj_order; +} + +static void rbd_init_layout(struct rbd_device *rbd_dev) +{ + if (rbd_dev->header.stripe_unit == 0 || + rbd_dev->header.stripe_count == 0) { + rbd_dev->header.stripe_unit = rbd_obj_bytes(&rbd_dev->header); + rbd_dev->header.stripe_count = 1; + } + + rbd_dev->layout.stripe_unit = rbd_dev->header.stripe_unit; + rbd_dev->layout.stripe_count = rbd_dev->header.stripe_count; + rbd_dev->layout.object_size = rbd_obj_bytes(&rbd_dev->header); + rbd_dev->layout.pool_id = rbd_dev->header.data_pool_id == CEPH_NOPOOL ? + rbd_dev->spec->pool_id : rbd_dev->header.data_pool_id; + RCU_INIT_POINTER(rbd_dev->layout.pool_ns, NULL); +} + +/* * Fill an rbd image header with information from the given format 1 * on-disk header. */ @@ -992,15 +1016,11 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev, /* Allocate this now to avoid having to handle failure below */ if (first_time) { - size_t len; - - len = strnlen(ondisk->object_prefix, - sizeof (ondisk->object_prefix)); - object_prefix = kmalloc(len + 1, GFP_KERNEL); + object_prefix = kstrndup(ondisk->object_prefix, + sizeof(ondisk->object_prefix), + GFP_KERNEL); if (!object_prefix) return -ENOMEM; - memcpy(object_prefix, ondisk->object_prefix, len); - object_prefix[len] = '\0'; } /* Allocate the snapshot context and fill it in */ @@ -1051,12 +1071,7 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev, if (first_time) { header->object_prefix = object_prefix; header->obj_order = ondisk->options.order; - header->crypt_type = ondisk->options.crypt_type; - header->comp_type = ondisk->options.comp_type; - /* The rest aren't used for format 1 images */ - header->stripe_unit = 0; - header->stripe_count = 0; - header->features = 0; + rbd_init_layout(rbd_dev); } else { ceph_put_snap_context(header->snapc); kfree(header->snap_names); @@ -1232,42 +1247,9 @@ static void rbd_dev_mapping_clear(struct rbd_device *rbd_dev) rbd_dev->mapping.features = 0; } -static void rbd_segment_name_free(const char *name) -{ - /* The explicit cast here is needed to drop the const qualifier */ - - kmem_cache_free(rbd_segment_name_cache, (void *)name); -} - -static const char *rbd_segment_name(struct rbd_device *rbd_dev, u64 offset) -{ - char *name; - u64 segment; - int ret; - char *name_format; - - name = kmem_cache_alloc(rbd_segment_name_cache, GFP_NOIO); - if (!name) - return NULL; - segment = offset >> rbd_dev->header.obj_order; - name_format = "%s.%012llx"; - if (rbd_dev->image_format == 2) - name_format = "%s.%016llx"; - ret = snprintf(name, CEPH_MAX_OID_NAME_LEN + 1, name_format, - rbd_dev->header.object_prefix, segment); - if (ret < 0 || ret > CEPH_MAX_OID_NAME_LEN) { - pr_err("error formatting segment name for #%llu (%d)\n", - segment, ret); - rbd_segment_name_free(name); - name = NULL; - } - - return name; -} - static u64 rbd_segment_offset(struct rbd_device *rbd_dev, u64 offset) { - u64 segment_size = (u64) 1 << rbd_dev->header.obj_order; + u64 segment_size = rbd_obj_bytes(&rbd_dev->header); return offset & (segment_size - 1); } @@ -1275,7 +1257,7 @@ static u64 rbd_segment_offset(struct rbd_device *rbd_dev, u64 offset) static u64 rbd_segment_length(struct rbd_device *rbd_dev, u64 offset, u64 length) { - u64 segment_size = (u64) 1 << rbd_dev->header.obj_order; + u64 segment_size = rbd_obj_bytes(&rbd_dev->header); offset &= segment_size - 1; @@ -1287,14 +1269,6 @@ static u64 rbd_segment_length(struct rbd_device *rbd_dev, } /* - * returns the size of an object in the image - */ -static u64 rbd_obj_bytes(struct rbd_image_header *header) -{ - return 1 << header->obj_order; -} - -/* * bio helpers */ @@ -1623,7 +1597,9 @@ static void rbd_obj_request_submit(struct rbd_obj_request *obj_request) { struct ceph_osd_request *osd_req = obj_request->osd_req; - dout("%s %p osd_req %p\n", __func__, obj_request, osd_req); + dout("%s %p object_no %016llx %llu~%llu osd_req %p\n", __func__, + obj_request, obj_request->object_no, obj_request->offset, + obj_request->length, osd_req); if (obj_request_img_data_test(obj_request)) { WARN_ON(obj_request->callback != rbd_img_obj_callback); rbd_img_request_get(obj_request->img_request); @@ -1631,44 +1607,6 @@ static void rbd_obj_request_submit(struct rbd_obj_request *obj_request) ceph_osdc_start_request(osd_req->r_osdc, osd_req, false); } -static void rbd_obj_request_end(struct rbd_obj_request *obj_request) -{ - dout("%s %p\n", __func__, obj_request); - ceph_osdc_cancel_request(obj_request->osd_req); -} - -/* - * Wait for an object request to complete. If interrupted, cancel the - * underlying osd request. - * - * @timeout: in jiffies, 0 means "wait forever" - */ -static int __rbd_obj_request_wait(struct rbd_obj_request *obj_request, - unsigned long timeout) -{ - long ret; - - dout("%s %p\n", __func__, obj_request); - ret = wait_for_completion_interruptible_timeout( - &obj_request->completion, - ceph_timeout_jiffies(timeout)); - if (ret <= 0) { - if (ret == 0) - ret = -ETIMEDOUT; - rbd_obj_request_end(obj_request); - } else { - ret = 0; - } - - dout("%s %p ret %d\n", __func__, obj_request, (int)ret); - return ret; -} - -static int rbd_obj_request_wait(struct rbd_obj_request *obj_request) -{ - return __rbd_obj_request_wait(obj_request, 0); -} - static void rbd_img_request_complete(struct rbd_img_request *img_request) { @@ -1955,8 +1893,8 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req) rbd_osd_call_callback(obj_request); break; default: - rbd_warn(NULL, "%s: unsupported op %hu", - obj_request->object_name, (unsigned short) opcode); + rbd_warn(NULL, "unexpected OSD op: object_no %016llx opcode %d", + obj_request->object_no, opcode); break; } @@ -1980,6 +1918,40 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request) osd_req->r_data_offset = obj_request->offset; } +static struct ceph_osd_request * +__rbd_osd_req_create(struct rbd_device *rbd_dev, + struct ceph_snap_context *snapc, + int num_ops, unsigned int flags, + struct rbd_obj_request *obj_request) +{ + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct ceph_osd_request *req; + const char *name_format = rbd_dev->image_format == 1 ? + RBD_V1_DATA_FORMAT : RBD_V2_DATA_FORMAT; + + req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false, GFP_NOIO); + if (!req) + return NULL; + + req->r_flags = flags; + req->r_callback = rbd_osd_req_callback; + req->r_priv = obj_request; + + req->r_base_oloc.pool = rbd_dev->layout.pool_id; + if (ceph_oid_aprintf(&req->r_base_oid, GFP_NOIO, name_format, + rbd_dev->header.object_prefix, obj_request->object_no)) + goto err_req; + + if (ceph_osdc_alloc_messages(req, GFP_NOIO)) + goto err_req; + + return req; + +err_req: + ceph_osdc_put_request(req); + return NULL; +} + /* * Create an osd request. A read request has one osd op (read). * A write request has either one (watch) or two (hint+write) osd ops. @@ -1993,8 +1965,6 @@ static struct ceph_osd_request *rbd_osd_req_create( struct rbd_obj_request *obj_request) { struct ceph_snap_context *snapc = NULL; - struct ceph_osd_client *osdc; - struct ceph_osd_request *osd_req; if (obj_request_img_data_test(obj_request) && (op_type == OBJ_OP_DISCARD || op_type == OBJ_OP_WRITE)) { @@ -2009,35 +1979,9 @@ static struct ceph_osd_request *rbd_osd_req_create( rbd_assert(num_ops == 1 || ((op_type == OBJ_OP_WRITE) && num_ops == 2)); - /* Allocate and initialize the request, for the num_ops ops */ - - osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false, - GFP_NOIO); - if (!osd_req) - goto fail; - - if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD) - osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK; - else - osd_req->r_flags = CEPH_OSD_FLAG_READ; - - osd_req->r_callback = rbd_osd_req_callback; - osd_req->r_priv = obj_request; - - osd_req->r_base_oloc.pool = rbd_dev->layout.pool_id; - if (ceph_oid_aprintf(&osd_req->r_base_oid, GFP_NOIO, "%s", - obj_request->object_name)) - goto fail; - - if (ceph_osdc_alloc_messages(osd_req, GFP_NOIO)) - goto fail; - - return osd_req; - -fail: - ceph_osdc_put_request(osd_req); - return NULL; + return __rbd_osd_req_create(rbd_dev, snapc, num_ops, + (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD) ? + CEPH_OSD_FLAG_WRITE : CEPH_OSD_FLAG_READ, obj_request); } /* @@ -2050,10 +1994,6 @@ static struct ceph_osd_request * rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request) { struct rbd_img_request *img_request; - struct ceph_snap_context *snapc; - struct rbd_device *rbd_dev; - struct ceph_osd_client *osdc; - struct ceph_osd_request *osd_req; int num_osd_ops = 3; rbd_assert(obj_request_img_data_test(obj_request)); @@ -2065,77 +2005,34 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request) if (img_request_discard_test(img_request)) num_osd_ops = 2; - /* Allocate and initialize the request, for all the ops */ - - snapc = img_request->snapc; - rbd_dev = img_request->rbd_dev; - osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, num_osd_ops, - false, GFP_NOIO); - if (!osd_req) - goto fail; - - osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK; - osd_req->r_callback = rbd_osd_req_callback; - osd_req->r_priv = obj_request; - - osd_req->r_base_oloc.pool = rbd_dev->layout.pool_id; - if (ceph_oid_aprintf(&osd_req->r_base_oid, GFP_NOIO, "%s", - obj_request->object_name)) - goto fail; - - if (ceph_osdc_alloc_messages(osd_req, GFP_NOIO)) - goto fail; - - return osd_req; - -fail: - ceph_osdc_put_request(osd_req); - return NULL; + return __rbd_osd_req_create(img_request->rbd_dev, + img_request->snapc, num_osd_ops, + CEPH_OSD_FLAG_WRITE, obj_request); } - static void rbd_osd_req_destroy(struct ceph_osd_request *osd_req) { ceph_osdc_put_request(osd_req); } -/* object_name is assumed to be a non-null pointer and NUL-terminated */ - -static struct rbd_obj_request *rbd_obj_request_create(const char *object_name, - u64 offset, u64 length, - enum obj_request_type type) +static struct rbd_obj_request * +rbd_obj_request_create(enum obj_request_type type) { struct rbd_obj_request *obj_request; - size_t size; - char *name; rbd_assert(obj_request_type_valid(type)); - size = strlen(object_name) + 1; - name = kmalloc(size, GFP_NOIO); - if (!name) - return NULL; - obj_request = kmem_cache_zalloc(rbd_obj_request_cache, GFP_NOIO); - if (!obj_request) { - kfree(name); + if (!obj_request) return NULL; - } - obj_request->object_name = memcpy(name, object_name, size); - obj_request->offset = offset; - obj_request->length = length; - obj_request->flags = 0; obj_request->which = BAD_WHICH; obj_request->type = type; INIT_LIST_HEAD(&obj_request->links); init_completion(&obj_request->completion); kref_init(&obj_request->kref); - dout("%s: \"%s\" %llu/%llu %d -> obj %p\n", __func__, object_name, - offset, length, (int)type, obj_request); - + dout("%s %p\n", __func__, obj_request); return obj_request; } @@ -2170,8 +2067,6 @@ static void rbd_obj_request_destroy(struct kref *kref) break; } - kfree(obj_request->object_name); - obj_request->object_name = NULL; kmem_cache_free(rbd_obj_request_cache, obj_request); } @@ -2546,22 +2441,18 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, while (resid) { struct ceph_osd_request *osd_req; - const char *object_name; - u64 offset; - u64 length; + u64 object_no = img_offset >> rbd_dev->header.obj_order; + u64 offset = rbd_segment_offset(rbd_dev, img_offset); + u64 length = rbd_segment_length(rbd_dev, img_offset, resid); - object_name = rbd_segment_name(rbd_dev, img_offset); - if (!object_name) - goto out_unwind; - offset = rbd_segment_offset(rbd_dev, img_offset); - length = rbd_segment_length(rbd_dev, img_offset, resid); - obj_request = rbd_obj_request_create(object_name, - offset, length, type); - /* object request has its own copy of the object name */ - rbd_segment_name_free(object_name); + obj_request = rbd_obj_request_create(type); if (!obj_request) goto out_unwind; + obj_request->object_no = object_no; + obj_request->offset = offset; + obj_request->length = length; + /* * set obj_request->img_request before creating the * osd_request so that it gets the right snapc @@ -2771,7 +2662,7 @@ static int rbd_img_obj_parent_read_full(struct rbd_obj_request *obj_request) * child image to which the original request was to be sent. */ img_offset = obj_request->img_offset - obj_request->offset; - length = (u64)1 << rbd_dev->header.obj_order; + length = rbd_obj_bytes(&rbd_dev->header); /* * There is no defined parent data beyond the parent @@ -2900,11 +2791,12 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request) size_t size; int ret; - stat_request = rbd_obj_request_create(obj_request->object_name, 0, 0, - OBJ_REQUEST_PAGES); + stat_request = rbd_obj_request_create(OBJ_REQUEST_PAGES); if (!stat_request) return -ENOMEM; + stat_request->object_no = obj_request->object_no; + stat_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1, stat_request); if (!stat_request->osd_req) { @@ -3983,17 +3875,17 @@ out: * returned in the outbound buffer, or a negative error code. */ static int rbd_obj_method_sync(struct rbd_device *rbd_dev, - const char *object_name, - const char *class_name, + struct ceph_object_id *oid, + struct ceph_object_locator *oloc, const char *method_name, const void *outbound, size_t outbound_size, void *inbound, size_t inbound_size) { - struct rbd_obj_request *obj_request; - struct page **pages; - u32 page_count; + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct page *req_page = NULL; + struct page *reply_page; int ret; /* @@ -4003,61 +3895,35 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev, * method. Currently if this is present it will be a * snapshot id. */ - page_count = (u32)calc_pages_for(0, inbound_size); - pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); - if (IS_ERR(pages)) - return PTR_ERR(pages); - - ret = -ENOMEM; - obj_request = rbd_obj_request_create(object_name, 0, inbound_size, - OBJ_REQUEST_PAGES); - if (!obj_request) - goto out; + if (outbound) { + if (outbound_size > PAGE_SIZE) + return -E2BIG; - obj_request->pages = pages; - obj_request->page_count = page_count; - - obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1, - obj_request); - if (!obj_request->osd_req) - goto out; - - osd_req_op_cls_init(obj_request->osd_req, 0, CEPH_OSD_OP_CALL, - class_name, method_name); - if (outbound_size) { - struct ceph_pagelist *pagelist; - - pagelist = kmalloc(sizeof (*pagelist), GFP_NOFS); - if (!pagelist) - goto out; + req_page = alloc_page(GFP_KERNEL); + if (!req_page) + return -ENOMEM; - ceph_pagelist_init(pagelist); - ceph_pagelist_append(pagelist, outbound, outbound_size); - osd_req_op_cls_request_data_pagelist(obj_request->osd_req, 0, - pagelist); + memcpy(page_address(req_page), outbound, outbound_size); } - osd_req_op_cls_response_data_pages(obj_request->osd_req, 0, - obj_request->pages, inbound_size, - 0, false, false); - - rbd_obj_request_submit(obj_request); - ret = rbd_obj_request_wait(obj_request); - if (ret) - goto out; - ret = obj_request->result; - if (ret < 0) - goto out; + reply_page = alloc_page(GFP_KERNEL); + if (!reply_page) { + if (req_page) + __free_page(req_page); + return -ENOMEM; + } - rbd_assert(obj_request->xferred < (u64)INT_MAX); - ret = (int)obj_request->xferred; - ceph_copy_from_page_vector(pages, inbound, 0, obj_request->xferred); -out: - if (obj_request) - rbd_obj_request_put(obj_request); - else - ceph_release_page_vector(pages, page_count); + ret = ceph_osdc_call(osdc, oid, oloc, RBD_DRV_NAME, method_name, + CEPH_OSD_FLAG_READ, req_page, outbound_size, + reply_page, &inbound_size); + if (!ret) { + memcpy(inbound, page_address(reply_page), inbound_size); + ret = inbound_size; + } + if (req_page) + __free_page(req_page); + __free_page(reply_page); return ret; } @@ -4256,63 +4122,46 @@ static void rbd_free_disk(struct rbd_device *rbd_dev) } static int rbd_obj_read_sync(struct rbd_device *rbd_dev, - const char *object_name, - u64 offset, u64 length, void *buf) + struct ceph_object_id *oid, + struct ceph_object_locator *oloc, + void *buf, int buf_len) { - struct rbd_obj_request *obj_request; - struct page **pages = NULL; - u32 page_count; - size_t size; + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct ceph_osd_request *req; + struct page **pages; + int num_pages = calc_pages_for(0, buf_len); int ret; - page_count = (u32) calc_pages_for(offset, length); - pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); - if (IS_ERR(pages)) - return PTR_ERR(pages); - - ret = -ENOMEM; - obj_request = rbd_obj_request_create(object_name, offset, length, - OBJ_REQUEST_PAGES); - if (!obj_request) - goto out; - - obj_request->pages = pages; - obj_request->page_count = page_count; - - obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1, - obj_request); - if (!obj_request->osd_req) - goto out; + req = ceph_osdc_alloc_request(osdc, NULL, 1, false, GFP_KERNEL); + if (!req) + return -ENOMEM; - osd_req_op_extent_init(obj_request->osd_req, 0, CEPH_OSD_OP_READ, - offset, length, 0, 0); - osd_req_op_extent_osd_data_pages(obj_request->osd_req, 0, - obj_request->pages, - obj_request->length, - obj_request->offset & ~PAGE_MASK, - false, false); + ceph_oid_copy(&req->r_base_oid, oid); + ceph_oloc_copy(&req->r_base_oloc, oloc); + req->r_flags = CEPH_OSD_FLAG_READ; - rbd_obj_request_submit(obj_request); - ret = rbd_obj_request_wait(obj_request); + ret = ceph_osdc_alloc_messages(req, GFP_KERNEL); if (ret) - goto out; + goto out_req; - ret = obj_request->result; - if (ret < 0) - goto out; + pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); + if (IS_ERR(pages)) { + ret = PTR_ERR(pages); + goto out_req; + } - rbd_assert(obj_request->xferred <= (u64) SIZE_MAX); - size = (size_t) obj_request->xferred; - ceph_copy_from_page_vector(pages, buf, 0, size); - rbd_assert(size <= (size_t)INT_MAX); - ret = (int)size; -out: - if (obj_request) - rbd_obj_request_put(obj_request); - else - ceph_release_page_vector(pages, page_count); + osd_req_op_extent_init(req, 0, CEPH_OSD_OP_READ, 0, buf_len, 0, 0); + osd_req_op_extent_osd_data_pages(req, 0, pages, buf_len, 0, false, + true); + + ceph_osdc_start_request(osdc, req, false); + ret = ceph_osdc_wait_request(osdc, req); + if (ret >= 0) + ceph_copy_from_page_vector(pages, buf, 0, ret); +out_req: + ceph_osdc_put_request(req); return ret; } @@ -4348,8 +4197,8 @@ static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev) if (!ondisk) return -ENOMEM; - ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_oid.name, - 0, size, ondisk); + ret = rbd_obj_read_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, ondisk, size); if (ret < 0) goto out; if ((size_t)ret < size) { @@ -4781,7 +4630,7 @@ static const struct attribute_group *rbd_attr_groups[] = { static void rbd_dev_release(struct device *dev); -static struct device_type rbd_device_type = { +static const struct device_type rbd_device_type = { .name = "rbd", .groups = rbd_attr_groups, .release = rbd_dev_release, @@ -4876,8 +4725,9 @@ static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc, INIT_LIST_HEAD(&rbd_dev->node); init_rwsem(&rbd_dev->header_rwsem); + rbd_dev->header.data_pool_id = CEPH_NOPOOL; ceph_oid_init(&rbd_dev->header_oid); - ceph_oloc_init(&rbd_dev->header_oloc); + rbd_dev->header_oloc.pool = spec->pool_id; mutex_init(&rbd_dev->watch_mutex); rbd_dev->watch_state = RBD_WATCH_STATE_UNREGISTERED; @@ -4899,12 +4749,6 @@ static struct rbd_device *__rbd_dev_create(struct rbd_client *rbdc, rbd_dev->rbd_client = rbdc; rbd_dev->spec = spec; - rbd_dev->layout.stripe_unit = 1 << RBD_MAX_OBJ_ORDER; - rbd_dev->layout.stripe_count = 1; - rbd_dev->layout.object_size = 1 << RBD_MAX_OBJ_ORDER; - rbd_dev->layout.pool_id = spec->pool_id; - RCU_INIT_POINTER(rbd_dev->layout.pool_ns, NULL); - return rbd_dev; } @@ -4970,10 +4814,10 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, __le64 size; } __attribute__ ((packed)) size_buf = { 0 }; - ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name, - "rbd", "get_size", - &snapid, sizeof (snapid), - &size_buf, sizeof (size_buf)); + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_size", + &snapid, sizeof(snapid), + &size_buf, sizeof(size_buf)); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -5010,9 +4854,9 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) if (!reply_buf) return -ENOMEM; - ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name, - "rbd", "get_object_prefix", NULL, 0, - reply_buf, RBD_OBJ_PREFIX_LEN_MAX); + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_object_prefix", + NULL, 0, reply_buf, RBD_OBJ_PREFIX_LEN_MAX); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -5045,10 +4889,10 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, u64 unsup; int ret; - ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name, - "rbd", "get_features", - &snapid, sizeof (snapid), - &features_buf, sizeof (features_buf)); + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_features", + &snapid, sizeof(snapid), + &features_buf, sizeof(features_buf)); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -5107,10 +4951,9 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) } snapid = cpu_to_le64(rbd_dev->spec->snap_id); - ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name, - "rbd", "get_parent", - &snapid, sizeof (snapid), - reply_buf, size); + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_parent", + &snapid, sizeof(snapid), reply_buf, size); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out_err; @@ -5210,9 +5053,9 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev) u64 stripe_count; int ret; - ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name, - "rbd", "get_stripe_unit_count", NULL, 0, - (char *)&striping_info_buf, size); + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_stripe_unit_count", + NULL, 0, &striping_info_buf, size); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -5226,7 +5069,7 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev) * out, and only fail if the image has non-default values. */ ret = -EINVAL; - obj_size = (u64)1 << rbd_dev->header.obj_order; + obj_size = rbd_obj_bytes(&rbd_dev->header); p = &striping_info_buf; stripe_unit = ceph_decode_64(&p); if (stripe_unit != obj_size) { @@ -5247,8 +5090,27 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev) return 0; } +static int rbd_dev_v2_data_pool(struct rbd_device *rbd_dev) +{ + __le64 data_pool_id; + int ret; + + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_data_pool", + NULL, 0, &data_pool_id, sizeof(data_pool_id)); + if (ret < 0) + return ret; + if (ret < sizeof(data_pool_id)) + return -EBADMSG; + + rbd_dev->header.data_pool_id = le64_to_cpu(data_pool_id); + WARN_ON(rbd_dev->header.data_pool_id == CEPH_NOPOOL); + return 0; +} + static char *rbd_dev_image_name(struct rbd_device *rbd_dev) { + CEPH_DEFINE_OID_ONSTACK(oid); size_t image_id_size; char *image_id; void *p; @@ -5276,10 +5138,10 @@ static char *rbd_dev_image_name(struct rbd_device *rbd_dev) if (!reply_buf) goto out; - ret = rbd_obj_method_sync(rbd_dev, RBD_DIRECTORY, - "rbd", "dir_get_name", - image_id, image_id_size, - reply_buf, size); + ceph_oid_printf(&oid, "%s", RBD_DIRECTORY); + ret = rbd_obj_method_sync(rbd_dev, &oid, &rbd_dev->header_oloc, + "dir_get_name", image_id, image_id_size, + reply_buf, size); if (ret < 0) goto out; p = reply_buf; @@ -5458,9 +5320,9 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev) if (!reply_buf) return -ENOMEM; - ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name, - "rbd", "get_snapcontext", NULL, 0, - reply_buf, size); + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_snapcontext", + NULL, 0, reply_buf, size); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -5523,10 +5385,9 @@ static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, return ERR_PTR(-ENOMEM); snapid = cpu_to_le64(snap_id); - ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name, - "rbd", "get_snapshot_name", - &snapid, sizeof (snapid), - reply_buf, size); + ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, + &rbd_dev->header_oloc, "get_snapshot_name", + &snapid, sizeof(snapid), reply_buf, size); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) { snap_name = ERR_PTR(ret); @@ -5833,7 +5694,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) { int ret; size_t size; - char *object_name; + CEPH_DEFINE_OID_ONSTACK(oid); void *response; char *image_id; @@ -5853,12 +5714,12 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) * First, see if the format 2 image id file exists, and if * so, get the image's persistent id from it. */ - size = sizeof (RBD_ID_PREFIX) + strlen(rbd_dev->spec->image_name); - object_name = kmalloc(size, GFP_NOIO); - if (!object_name) - return -ENOMEM; - sprintf(object_name, "%s%s", RBD_ID_PREFIX, rbd_dev->spec->image_name); - dout("rbd id object name is %s\n", object_name); + ret = ceph_oid_aprintf(&oid, GFP_KERNEL, "%s%s", RBD_ID_PREFIX, + rbd_dev->spec->image_name); + if (ret) + return ret; + + dout("rbd id object name is %s\n", oid.name); /* Response will be an encoded string, which includes a length */ @@ -5871,9 +5732,9 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) /* If it doesn't exist we'll assume it's a format 1 image */ - ret = rbd_obj_method_sync(rbd_dev, object_name, - "rbd", "get_id", NULL, 0, - response, RBD_IMAGE_ID_LEN_MAX); + ret = rbd_obj_method_sync(rbd_dev, &oid, &rbd_dev->header_oloc, + "get_id", NULL, 0, + response, RBD_IMAGE_ID_LEN_MAX); dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret == -ENOENT) { image_id = kstrdup("", GFP_KERNEL); @@ -5896,8 +5757,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) } out: kfree(response); - kfree(object_name); - + ceph_oid_destroy(&oid); return ret; } @@ -5944,14 +5804,20 @@ static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev) if (ret < 0) goto out_err; } - /* No support for crypto and compression type format 2 images */ + if (rbd_dev->header.features & RBD_FEATURE_DATA_POOL) { + ret = rbd_dev_v2_data_pool(rbd_dev); + if (ret) + goto out_err; + } + + rbd_init_layout(rbd_dev); return 0; + out_err: rbd_dev->header.features = 0; kfree(rbd_dev->header.object_prefix); rbd_dev->header.object_prefix = NULL; - return ret; } @@ -6077,8 +5943,6 @@ static int rbd_dev_header_name(struct rbd_device *rbd_dev) /* Record the header object name for this rbd image. */ rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); - - rbd_dev->header_oloc.pool = rbd_dev->layout.pool_id; if (rbd_dev->image_format == 1) ret = ceph_oid_aprintf(&rbd_dev->header_oid, GFP_KERNEL, "%s%s", spec->image_name, RBD_SUFFIX); @@ -6471,27 +6335,16 @@ static int rbd_slab_init(void) if (!rbd_obj_request_cache) goto out_err; - rbd_assert(!rbd_segment_name_cache); - rbd_segment_name_cache = kmem_cache_create("rbd_segment_name", - CEPH_MAX_OID_NAME_LEN + 1, 1, 0, NULL); - if (rbd_segment_name_cache) - return 0; -out_err: - kmem_cache_destroy(rbd_obj_request_cache); - rbd_obj_request_cache = NULL; + return 0; +out_err: kmem_cache_destroy(rbd_img_request_cache); rbd_img_request_cache = NULL; - return -ENOMEM; } static void rbd_slab_exit(void) { - rbd_assert(rbd_segment_name_cache); - kmem_cache_destroy(rbd_segment_name_cache); - rbd_segment_name_cache = NULL; - rbd_assert(rbd_obj_request_cache); kmem_cache_destroy(rbd_obj_request_cache); rbd_obj_request_cache = NULL; diff --git a/drivers/block/rbd_types.h b/drivers/block/rbd_types.h index 94f367db27b0..62ff50d3e7a6 100644 --- a/drivers/block/rbd_types.h +++ b/drivers/block/rbd_types.h @@ -25,8 +25,8 @@ */ #define RBD_HEADER_PREFIX "rbd_header." -#define RBD_DATA_PREFIX "rbd_data." #define RBD_ID_PREFIX "rbd_id." +#define RBD_V2_DATA_FORMAT "%s.%016llx" #define RBD_LOCK_NAME "rbd_lock" #define RBD_LOCK_TAG "internal" @@ -42,13 +42,14 @@ enum rbd_notify_op { /* * For format version 1, rbd image 'foo' consists of objects * foo.rbd - image metadata - * rb.<idhi>.<idlo>.00000000 - * rb.<idhi>.<idlo>.00000001 + * rb.<idhi>.<idlo>.<extra>.000000000000 + * rb.<idhi>.<idlo>.<extra>.000000000001 * ... - data * There is no notion of a persistent image id in rbd format 1. */ #define RBD_SUFFIX ".rbd" +#define RBD_V1_DATA_FORMAT "%s.%012llx" #define RBD_DIRECTORY "rbd_directory" #define RBD_INFO "rbd_info" @@ -57,9 +58,6 @@ enum rbd_notify_op { #define RBD_MIN_OBJ_ORDER 16 #define RBD_MAX_OBJ_ORDER 30 -#define RBD_COMP_NONE 0 -#define RBD_CRYPT_NONE 0 - #define RBD_HEADER_TEXT "<<< Rados Block Device Image >>>\n" #define RBD_HEADER_SIGNATURE "RBD" #define RBD_HEADER_VERSION "001.005" diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index cab157331c4e..3f3a3ab3d50a 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -34,6 +34,7 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); #define VDC_TX_RING_SIZE 512 +#define VDC_DEFAULT_BLK_SIZE 512 #define WAITING_FOR_LINK_UP 0x01 #define WAITING_FOR_TX_SPACE 0x02 @@ -73,6 +74,7 @@ struct vdc_port { u32 vdisk_size; u8 vdisk_type; u8 vdisk_mtype; + u32 vdisk_phys_blksz; char disk_name[32]; }; @@ -88,6 +90,7 @@ static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio) /* Ordered from largest major to lowest */ static struct vio_version vdc_versions[] = { + { .major = 1, .minor = 2 }, { .major = 1, .minor = 1 }, { .major = 1, .minor = 0 }, }; @@ -271,6 +274,11 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg) if (pkt->max_xfer_size < port->max_xfer_size) port->max_xfer_size = pkt->max_xfer_size; port->vdisk_block_size = pkt->vdisk_block_size; + + port->vdisk_phys_blksz = VDC_DEFAULT_BLK_SIZE; + if (vdc_version_supported(port, 1, 2)) + port->vdisk_phys_blksz = pkt->phys_block_size; + return 0; } else { printk(KERN_ERR PFX "%s: Attribute NACK\n", vio->name); @@ -754,6 +762,12 @@ static int probe_disk(struct vdc_port *port) if (err) return err; + /* Using version 1.2 means vdisk_phys_blksz should be set unless the + * disk is reserved by another system. + */ + if (vdc_version_supported(port, 1, 2) && !port->vdisk_phys_blksz) + return -ENODEV; + if (vdc_version_supported(port, 1, 1)) { /* vdisk_size should be set during the handshake, if it wasn't * then the underlying disk is reserved by another system @@ -829,6 +843,8 @@ static int probe_disk(struct vdc_port *port) } } + blk_queue_physical_block_size(q, port->vdisk_phys_blksz); + pr_info(PFX "%s: %u sectors (%u MB) protocol %d.%d\n", g->disk_name, port->vdisk_size, (port->vdisk_size >> (20 - 9)), @@ -910,7 +926,7 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) if (err) goto err_out_free_port; - port->vdisk_block_size = 512; + port->vdisk_block_size = VDC_DEFAULT_BLK_SIZE; port->max_xfer_size = ((128 * 1024) / port->vdisk_block_size); port->ring_cookies = ((port->max_xfer_size * port->vdisk_block_size) / PAGE_SIZE) + 2; diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index aabd8e9d3035..61b3ffa4f458 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -20,7 +20,7 @@ #include <linux/stddef.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/timer.h> #include <linux/delay.h> #include <linux/fd.h> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 024b473524c0..1d4c9f8bc1e1 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -5,6 +5,7 @@ #include <linux/hdreg.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/interrupt.h> #include <linux/virtio.h> #include <linux/virtio_blk.h> #include <linux/scatterlist.h> @@ -12,6 +13,7 @@ #include <scsi/scsi_cmnd.h> #include <linux/idr.h> #include <linux/blk-mq.h> +#include <linux/blk-mq-virtio.h> #include <linux/numa.h> #define PART_BITS 4 @@ -426,6 +428,7 @@ static int init_vq(struct virtio_blk *vblk) struct virtqueue **vqs; unsigned short num_vqs; struct virtio_device *vdev = vblk->vdev; + struct irq_affinity desc = { 0, }; err = virtio_cread_feature(vdev, VIRTIO_BLK_F_MQ, struct virtio_blk_config, num_queues, @@ -452,7 +455,8 @@ static int init_vq(struct virtio_blk *vblk) } /* Discover virtqueues and write information to configuration. */ - err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names); + err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names, + &desc); if (err) goto out; @@ -586,10 +590,18 @@ static int virtblk_init_request(void *data, struct request *rq, return 0; } +static int virtblk_map_queues(struct blk_mq_tag_set *set) +{ + struct virtio_blk *vblk = set->driver_data; + + return blk_mq_virtio_map_queues(set, vblk->vdev, 0); +} + static struct blk_mq_ops virtio_mq_ops = { .queue_rq = virtio_queue_rq, .complete = virtblk_request_done, .init_request = virtblk_init_request, + .map_queues = virtblk_map_queues, }; static unsigned int virtblk_queue_depth; diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index c73fede582f7..e27d89a36c34 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -74,6 +74,17 @@ static void zram_clear_flag(struct zram_meta *meta, u32 index, meta->table[index].value &= ~BIT(flag); } +static inline void zram_set_element(struct zram_meta *meta, u32 index, + unsigned long element) +{ + meta->table[index].element = element; +} + +static inline void zram_clear_element(struct zram_meta *meta, u32 index) +{ + meta->table[index].element = 0; +} + static size_t zram_get_obj_size(struct zram_meta *meta, u32 index) { return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1); @@ -146,31 +157,46 @@ static inline void update_used_max(struct zram *zram, } while (old_max != cur_max); } -static bool page_zero_filled(void *ptr) +static inline void zram_fill_page(char *ptr, unsigned long len, + unsigned long value) +{ + int i; + unsigned long *page = (unsigned long *)ptr; + + WARN_ON_ONCE(!IS_ALIGNED(len, sizeof(unsigned long))); + + if (likely(value == 0)) { + memset(ptr, 0, len); + } else { + for (i = 0; i < len / sizeof(*page); i++) + page[i] = value; + } +} + +static bool page_same_filled(void *ptr, unsigned long *element) { unsigned int pos; unsigned long *page; page = (unsigned long *)ptr; - for (pos = 0; pos != PAGE_SIZE / sizeof(*page); pos++) { - if (page[pos]) + for (pos = 0; pos < PAGE_SIZE / sizeof(*page) - 1; pos++) { + if (page[pos] != page[pos + 1]) return false; } + *element = page[pos]; + return true; } -static void handle_zero_page(struct bio_vec *bvec) +static void handle_same_page(struct bio_vec *bvec, unsigned long element) { struct page *page = bvec->bv_page; void *user_mem; user_mem = kmap_atomic(page); - if (is_partial_io(bvec)) - memset(user_mem + bvec->bv_offset, 0, bvec->bv_len); - else - clear_page(user_mem); + zram_fill_page(user_mem + bvec->bv_offset, bvec->bv_len, element); kunmap_atomic(user_mem); flush_dcache_page(page); @@ -363,7 +389,7 @@ static ssize_t mm_stat_show(struct device *dev, mem_used << PAGE_SHIFT, zram->limit_pages << PAGE_SHIFT, max_used << PAGE_SHIFT, - (u64)atomic64_read(&zram->stats.zero_pages), + (u64)atomic64_read(&zram->stats.same_pages), pool_stats.pages_compacted); up_read(&zram->init_lock); @@ -391,18 +417,6 @@ static DEVICE_ATTR_RO(io_stat); static DEVICE_ATTR_RO(mm_stat); static DEVICE_ATTR_RO(debug_stat); -static inline bool zram_meta_get(struct zram *zram) -{ - if (atomic_inc_not_zero(&zram->refcount)) - return true; - return false; -} - -static inline void zram_meta_put(struct zram *zram) -{ - atomic_dec(&zram->refcount); -} - static void zram_meta_free(struct zram_meta *meta, u64 disksize) { size_t num_pages = disksize >> PAGE_SHIFT; @@ -411,8 +425,11 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize) /* Free all pages that are still in this zram device */ for (index = 0; index < num_pages; index++) { unsigned long handle = meta->table[index].handle; - - if (!handle) + /* + * No memory is allocated for same element filled pages. + * Simply clear same page flag. + */ + if (!handle || zram_test_flag(meta, index, ZRAM_SAME)) continue; zs_free(meta->mem_pool, handle); @@ -462,18 +479,20 @@ static void zram_free_page(struct zram *zram, size_t index) struct zram_meta *meta = zram->meta; unsigned long handle = meta->table[index].handle; - if (unlikely(!handle)) { - /* - * No memory is allocated for zero filled pages. - * Simply clear zero page flag. - */ - if (zram_test_flag(meta, index, ZRAM_ZERO)) { - zram_clear_flag(meta, index, ZRAM_ZERO); - atomic64_dec(&zram->stats.zero_pages); - } + /* + * No memory is allocated for same element filled pages. + * Simply clear same page flag. + */ + if (zram_test_flag(meta, index, ZRAM_SAME)) { + zram_clear_flag(meta, index, ZRAM_SAME); + zram_clear_element(meta, index); + atomic64_dec(&zram->stats.same_pages); return; } + if (!handle) + return; + zs_free(meta->mem_pool, handle); atomic64_sub(zram_get_obj_size(meta, index), @@ -496,9 +515,9 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) handle = meta->table[index].handle; size = zram_get_obj_size(meta, index); - if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) { + if (!handle || zram_test_flag(meta, index, ZRAM_SAME)) { bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); - clear_page(mem); + zram_fill_page(mem, PAGE_SIZE, meta->table[index].element); return 0; } @@ -534,9 +553,9 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); if (unlikely(!meta->table[index].handle) || - zram_test_flag(meta, index, ZRAM_ZERO)) { + zram_test_flag(meta, index, ZRAM_SAME)) { bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); - handle_zero_page(bvec); + handle_same_page(bvec, meta->table[index].element); return 0; } bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); @@ -584,6 +603,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, struct zram_meta *meta = zram->meta; struct zcomp_strm *zstrm = NULL; unsigned long alloced_pages; + unsigned long element; page = bvec->bv_page; if (is_partial_io(bvec)) { @@ -612,16 +632,17 @@ compress_again: uncmem = user_mem; } - if (page_zero_filled(uncmem)) { + if (page_same_filled(uncmem, &element)) { if (user_mem) kunmap_atomic(user_mem); /* Free memory associated with this sector now. */ bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value); zram_free_page(zram, index); - zram_set_flag(meta, index, ZRAM_ZERO); + zram_set_flag(meta, index, ZRAM_SAME); + zram_set_element(meta, index, element); bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value); - atomic64_inc(&zram->stats.zero_pages); + atomic64_inc(&zram->stats.same_pages); ret = 0; goto out; } @@ -859,22 +880,17 @@ static blk_qc_t zram_make_request(struct request_queue *queue, struct bio *bio) { struct zram *zram = queue->queuedata; - if (unlikely(!zram_meta_get(zram))) - goto error; - blk_queue_split(queue, &bio, queue->bio_split); if (!valid_io_request(zram, bio->bi_iter.bi_sector, bio->bi_iter.bi_size)) { atomic64_inc(&zram->stats.invalid_io); - goto put_zram; + goto error; } __zram_make_request(zram, bio); - zram_meta_put(zram); return BLK_QC_T_NONE; -put_zram: - zram_meta_put(zram); + error: bio_io_error(bio); return BLK_QC_T_NONE; @@ -904,13 +920,11 @@ static int zram_rw_page(struct block_device *bdev, sector_t sector, struct bio_vec bv; zram = bdev->bd_disk->private_data; - if (unlikely(!zram_meta_get(zram))) - goto out; if (!valid_io_request(zram, sector, PAGE_SIZE)) { atomic64_inc(&zram->stats.invalid_io); err = -EINVAL; - goto put_zram; + goto out; } index = sector >> SECTORS_PER_PAGE_SHIFT; @@ -921,8 +935,6 @@ static int zram_rw_page(struct block_device *bdev, sector_t sector, bv.bv_offset = 0; err = zram_bvec_rw(zram, &bv, index, offset, is_write); -put_zram: - zram_meta_put(zram); out: /* * If I/O fails, just return error(ie, non-zero) without @@ -955,17 +967,6 @@ static void zram_reset_device(struct zram *zram) meta = zram->meta; comp = zram->comp; disksize = zram->disksize; - /* - * Refcount will go down to 0 eventually and r/w handler - * cannot handle further I/O so it will bail out by - * check zram_meta_get. - */ - zram_meta_put(zram); - /* - * We want to free zram_meta in process context to avoid - * deadlock between reclaim path and any other locks. - */ - wait_event(zram->io_done, atomic_read(&zram->refcount) == 0); /* Reset stats */ memset(&zram->stats, 0, sizeof(zram->stats)); @@ -1013,8 +1014,6 @@ static ssize_t disksize_store(struct device *dev, goto out_destroy_comp; } - init_waitqueue_head(&zram->io_done); - atomic_set(&zram->refcount, 1); zram->meta = meta; zram->comp = comp; zram->disksize = disksize; diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 74fcf10da374..caeff51f1571 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -61,7 +61,7 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3; /* Flags for zram pages (table[page_no].value) */ enum zram_pageflags { /* Page consists entirely of zeros */ - ZRAM_ZERO = ZRAM_FLAG_SHIFT, + ZRAM_SAME = ZRAM_FLAG_SHIFT, ZRAM_ACCESS, /* page is now accessed */ __NR_ZRAM_PAGEFLAGS, @@ -71,7 +71,10 @@ enum zram_pageflags { /* Allocated for each disk page */ struct zram_table_entry { - unsigned long handle; + union { + unsigned long handle; + unsigned long element; + }; unsigned long value; }; @@ -83,7 +86,7 @@ struct zram_stats { atomic64_t failed_writes; /* can happen when memory is too low */ atomic64_t invalid_io; /* non-page-aligned I/O requests */ atomic64_t notify_free; /* no. of swap slot free notifications */ - atomic64_t zero_pages; /* no. of zero filled pages */ + atomic64_t same_pages; /* no. of same element filled pages */ atomic64_t pages_stored; /* no. of pages currently stored */ atomic_long_t max_used_pages; /* no. of maximum pages stored */ atomic64_t writestall; /* no. of write slow paths */ @@ -106,9 +109,6 @@ struct zram { unsigned long limit_pages; struct zram_stats stats; - atomic_t refcount; /* refcount for zram_meta */ - /* wait all IO under all of cpu are done */ - wait_queue_head_t io_done; /* * This is the limit on amount of *uncompressed* worth of data * we can store in a disk. diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index b9e8cfc93c7e..0a52da439abf 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -112,6 +112,7 @@ config QCOM_EBI2 bool "Qualcomm External Bus Interface 2 (EBI2)" depends on HAS_IOMEM depends on ARCH_QCOM || COMPILE_TEST + default ARCH_QCOM help Say y here to enable support for the Qualcomm External Bus Interface 2, which can be used to connect things like NAND Flash, diff --git a/drivers/bus/da8xx-mstpri.c b/drivers/bus/da8xx-mstpri.c index 063397f2c0db..9af9bcc68059 100644 --- a/drivers/bus/da8xx-mstpri.c +++ b/drivers/bus/da8xx-mstpri.c @@ -4,7 +4,7 @@ * Copyright (C) 2016 BayLibre SAS * * Author: - * Bartosz Golaszewski <bgolaszewski@baylibre.com.com> + * Bartosz Golaszewski <bgolaszewski@baylibre.com> * * 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 diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index 737187865269..53fe633df1e8 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -11,15 +11,14 @@ #include "agp.h" -static int alpha_core_agp_vm_fault(struct vm_area_struct *vma, - struct vm_fault *vmf) +static int alpha_core_agp_vm_fault(struct vm_fault *vmf) { alpha_agp_info *agp = agp_bridge->dev_private_data; dma_addr_t dma_addr; unsigned long pa; struct page *page; - dma_addr = vmf->address - vma->vm_start + agp->aperture.bus_base; + dma_addr = vmf->address - vmf->vma->vm_start + agp->aperture.bus_base; pa = agp->ops->translate(agp, dma_addr); if (pa == (unsigned long)-EINVAL) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 0f7d28a98b9a..9702c78f458d 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1420,8 +1420,10 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, } EXPORT_SYMBOL(intel_gmch_probe); -void intel_gtt_get(u64 *gtt_total, size_t *stolen_size, - phys_addr_t *mappable_base, u64 *mappable_end) +void intel_gtt_get(u64 *gtt_total, + u32 *stolen_size, + phys_addr_t *mappable_base, + u64 *mappable_end) { *gtt_total = intel_private.gtt_total_entries << PAGE_SHIFT; *stolen_size = intel_private.stolen_size; diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index e5c62dcf2c11..e770ad977472 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -23,7 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/mutex.h> diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 20b32bb8c2af..8bdc38d81adf 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -25,6 +25,7 @@ #include <linux/spinlock.h> #include <linux/sysctl.h> #include <linux/wait.h> +#include <linux/sched/signal.h> #include <linux/bcd.h> #include <linux/seq_file.h> #include <linux/bitops.h> diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index ceff2fc524b1..0cafe08919c9 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -172,8 +172,8 @@ config HW_RANDOM_OMAP default HW_RANDOM ---help--- This driver provides kernel-side support for the Random Number - Generator hardware found on OMAP16xx, OMAP2/3/4/5 and AM33xx/AM43xx - multimedia processors. + Generator hardware found on OMAP16xx, OMAP2/3/4/5, AM33xx/AM43xx + multimedia processors, and Marvell Armada 7k/8k SoCs. To compile this driver as a module, choose M here: the module will be called omap-rng. diff --git a/drivers/char/hw_random/cavium-rng-vf.c b/drivers/char/hw_random/cavium-rng-vf.c index 066ae0e78d63..dd1007aecb10 100644 --- a/drivers/char/hw_random/cavium-rng-vf.c +++ b/drivers/char/hw_random/cavium-rng-vf.c @@ -57,7 +57,11 @@ static int cavium_rng_probe_vf(struct pci_dev *pdev, return -ENOMEM; } - rng->ops.name = "cavium rng"; + rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "cavium-rng-%s", dev_name(&pdev->dev)); + if (!rng->ops.name) + return -ENOMEM; + rng->ops.read = cavium_rng_read; rng->ops.quality = 1000; diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 87fba424817e..503a41dfa193 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -1,55 +1,31 @@ /* - Added support for the AMD Geode LX RNG - (c) Copyright 2004-2005 Advanced Micro Devices, Inc. - - derived from - - Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) - (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com> - - derived from - - Hardware driver for the AMD 768 Random Number Generator (RNG) - (c) Copyright 2001 Red Hat Inc <alan@redhat.com> - - derived from - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> - Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> - - Added generic RNG API - Copyright 2006 Michael Buesch <m@bues.ch> - Copyright 2005 (c) MontaVista Software, Inc. - - Please read Documentation/hw_random.txt for details on use. - - ---------------------------------------------------------- - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - + * hw_random/core.c: HWRNG core API + * + * Copyright 2006 Michael Buesch <m@bues.ch> + * Copyright 2005 (c) MontaVista Software, Inc. + * + * Please read Documentation/hw_random.txt for details on use. + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. */ - +#include <linux/delay.h> #include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> #include <linux/hw_random.h> -#include <linux/module.h> #include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/sched.h> -#include <linux/miscdevice.h> #include <linux/kthread.h> -#include <linux/delay.h> -#include <linux/slab.h> +#include <linux/sched/signal.h> +#include <linux/miscdevice.h> +#include <linux/module.h> #include <linux/random.h> -#include <linux/err.h> +#include <linux/sched.h> +#include <linux/slab.h> #include <linux/uaccess.h> - #define RNG_MODULE_NAME "hw_random" -#define PFX RNG_MODULE_NAME ": " -#define RNG_MISCDEV_MINOR 183 /* official */ - static struct hwrng *current_rng; static struct task_struct *hwrng_fill; @@ -296,7 +272,6 @@ out_put: goto out; } - static const struct file_operations rng_chrdev_ops = { .owner = THIS_MODULE, .open = rng_dev_open, @@ -307,14 +282,13 @@ static const struct file_operations rng_chrdev_ops = { static const struct attribute_group *rng_dev_groups[]; static struct miscdevice rng_miscdev = { - .minor = RNG_MISCDEV_MINOR, + .minor = HWRNG_MINOR, .name = RNG_MODULE_NAME, .nodename = "hwrng", .fops = &rng_chrdev_ops, .groups = rng_dev_groups, }; - static ssize_t hwrng_attr_current_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -444,8 +418,7 @@ int hwrng_register(struct hwrng *rng) int err = -EINVAL; struct hwrng *old_rng, *tmp; - if (rng->name == NULL || - (rng->data_read == NULL && rng->read == NULL)) + if (!rng->name || (!rng->data_read && !rng->read)) goto out; mutex_lock(&rng_mutex); diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 3b06c1d6cfb2..31cbdbbaebfc 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -21,11 +21,11 @@ #define DRV_MODULE_NAME "n2rng" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "0.2" -#define DRV_MODULE_RELDATE "July 27, 2011" +#define DRV_MODULE_VERSION "0.3" +#define DRV_MODULE_RELDATE "Jan 7, 2017" static char version[] = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + DRV_MODULE_NAME " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); MODULE_DESCRIPTION("Niagara2 RNG driver"); @@ -302,26 +302,57 @@ static int n2rng_try_read_ctl(struct n2rng *np) return n2rng_hv_err_trans(hv_err); } -#define CONTROL_DEFAULT_BASE \ - ((2 << RNG_CTL_ASEL_SHIFT) | \ - (N2RNG_ACCUM_CYCLES_DEFAULT << RNG_CTL_WAIT_SHIFT) | \ - RNG_CTL_LFSR) - -#define CONTROL_DEFAULT_0 \ - (CONTROL_DEFAULT_BASE | \ - (1 << RNG_CTL_VCO_SHIFT) | \ - RNG_CTL_ES1) -#define CONTROL_DEFAULT_1 \ - (CONTROL_DEFAULT_BASE | \ - (2 << RNG_CTL_VCO_SHIFT) | \ - RNG_CTL_ES2) -#define CONTROL_DEFAULT_2 \ - (CONTROL_DEFAULT_BASE | \ - (3 << RNG_CTL_VCO_SHIFT) | \ - RNG_CTL_ES3) -#define CONTROL_DEFAULT_3 \ - (CONTROL_DEFAULT_BASE | \ - RNG_CTL_ES1 | RNG_CTL_ES2 | RNG_CTL_ES3) +static u64 n2rng_control_default(struct n2rng *np, int ctl) +{ + u64 val = 0; + + if (np->data->chip_version == 1) { + val = ((2 << RNG_v1_CTL_ASEL_SHIFT) | + (N2RNG_ACCUM_CYCLES_DEFAULT << RNG_v1_CTL_WAIT_SHIFT) | + RNG_CTL_LFSR); + + switch (ctl) { + case 0: + val |= (1 << RNG_v1_CTL_VCO_SHIFT) | RNG_CTL_ES1; + break; + case 1: + val |= (2 << RNG_v1_CTL_VCO_SHIFT) | RNG_CTL_ES2; + break; + case 2: + val |= (3 << RNG_v1_CTL_VCO_SHIFT) | RNG_CTL_ES3; + break; + case 3: + val |= RNG_CTL_ES1 | RNG_CTL_ES2 | RNG_CTL_ES3; + break; + default: + break; + } + + } else { + val = ((2 << RNG_v2_CTL_ASEL_SHIFT) | + (N2RNG_ACCUM_CYCLES_DEFAULT << RNG_v2_CTL_WAIT_SHIFT) | + RNG_CTL_LFSR); + + switch (ctl) { + case 0: + val |= (1 << RNG_v2_CTL_VCO_SHIFT) | RNG_CTL_ES1; + break; + case 1: + val |= (2 << RNG_v2_CTL_VCO_SHIFT) | RNG_CTL_ES2; + break; + case 2: + val |= (3 << RNG_v2_CTL_VCO_SHIFT) | RNG_CTL_ES3; + break; + case 3: + val |= RNG_CTL_ES1 | RNG_CTL_ES2 | RNG_CTL_ES3; + break; + default: + break; + } + } + + return val; +} static void n2rng_control_swstate_init(struct n2rng *np) { @@ -336,10 +367,10 @@ static void n2rng_control_swstate_init(struct n2rng *np) for (i = 0; i < np->num_units; i++) { struct n2rng_unit *up = &np->units[i]; - up->control[0] = CONTROL_DEFAULT_0; - up->control[1] = CONTROL_DEFAULT_1; - up->control[2] = CONTROL_DEFAULT_2; - up->control[3] = CONTROL_DEFAULT_3; + up->control[0] = n2rng_control_default(np, 0); + up->control[1] = n2rng_control_default(np, 1); + up->control[2] = n2rng_control_default(np, 2); + up->control[3] = n2rng_control_default(np, 3); } np->hv_state = HV_RNG_STATE_UNCONFIGURED; @@ -399,6 +430,7 @@ static int n2rng_data_read(struct hwrng *rng, u32 *data) } else { int err = n2rng_generic_read_data(ra); if (!err) { + np->flags |= N2RNG_FLAG_BUFFER_VALID; np->buffer = np->test_data >> 32; *data = np->test_data & 0xffffffff; len = 4; @@ -487,9 +519,21 @@ static void n2rng_dump_test_buffer(struct n2rng *np) static int n2rng_check_selftest_buffer(struct n2rng *np, unsigned long unit) { - u64 val = SELFTEST_VAL; + u64 val; int err, matches, limit; + switch (np->data->id) { + case N2_n2_rng: + case N2_vf_rng: + case N2_kt_rng: + case N2_m4_rng: /* yes, m4 uses the old value */ + val = RNG_v1_SELFTEST_VAL; + break; + default: + val = RNG_v2_SELFTEST_VAL; + break; + } + matches = 0; for (limit = 0; limit < SELFTEST_LOOPS_MAX; limit++) { matches += n2rng_test_buffer_find(np, val); @@ -512,14 +556,32 @@ static int n2rng_check_selftest_buffer(struct n2rng *np, unsigned long unit) static int n2rng_control_selftest(struct n2rng *np, unsigned long unit) { int err; + u64 base, base3; + + switch (np->data->id) { + case N2_n2_rng: + case N2_vf_rng: + case N2_kt_rng: + base = RNG_v1_CTL_ASEL_NOOUT << RNG_v1_CTL_ASEL_SHIFT; + base3 = base | RNG_CTL_LFSR | + ((RNG_v1_SELFTEST_TICKS - 2) << RNG_v1_CTL_WAIT_SHIFT); + break; + case N2_m4_rng: + base = RNG_v2_CTL_ASEL_NOOUT << RNG_v2_CTL_ASEL_SHIFT; + base3 = base | RNG_CTL_LFSR | + ((RNG_v1_SELFTEST_TICKS - 2) << RNG_v2_CTL_WAIT_SHIFT); + break; + default: + base = RNG_v2_CTL_ASEL_NOOUT << RNG_v2_CTL_ASEL_SHIFT; + base3 = base | RNG_CTL_LFSR | + (RNG_v2_SELFTEST_TICKS << RNG_v2_CTL_WAIT_SHIFT); + break; + } - np->test_control[0] = (0x2 << RNG_CTL_ASEL_SHIFT); - np->test_control[1] = (0x2 << RNG_CTL_ASEL_SHIFT); - np->test_control[2] = (0x2 << RNG_CTL_ASEL_SHIFT); - np->test_control[3] = ((0x2 << RNG_CTL_ASEL_SHIFT) | - RNG_CTL_LFSR | - ((SELFTEST_TICKS - 2) << RNG_CTL_WAIT_SHIFT)); - + np->test_control[0] = base; + np->test_control[1] = base; + np->test_control[2] = base; + np->test_control[3] = base3; err = n2rng_entropy_diag_read(np, unit, np->test_control, HV_RNG_STATE_HEALTHCHECK, @@ -557,11 +619,19 @@ static int n2rng_control_configure_units(struct n2rng *np) struct n2rng_unit *up = &np->units[unit]; unsigned long ctl_ra = __pa(&up->control[0]); int esrc; - u64 base; + u64 base, shift; - base = ((np->accum_cycles << RNG_CTL_WAIT_SHIFT) | - (2 << RNG_CTL_ASEL_SHIFT) | - RNG_CTL_LFSR); + if (np->data->chip_version == 1) { + base = ((np->accum_cycles << RNG_v1_CTL_WAIT_SHIFT) | + (RNG_v1_CTL_ASEL_NOOUT << RNG_v1_CTL_ASEL_SHIFT) | + RNG_CTL_LFSR); + shift = RNG_v1_CTL_VCO_SHIFT; + } else { + base = ((np->accum_cycles << RNG_v2_CTL_WAIT_SHIFT) | + (RNG_v2_CTL_ASEL_NOOUT << RNG_v2_CTL_ASEL_SHIFT) | + RNG_CTL_LFSR); + shift = RNG_v2_CTL_VCO_SHIFT; + } /* XXX This isn't the best. We should fetch a bunch * XXX of words using each entropy source combined XXX @@ -570,7 +640,7 @@ static int n2rng_control_configure_units(struct n2rng *np) */ for (esrc = 0; esrc < 3; esrc++) up->control[esrc] = base | - (esrc << RNG_CTL_VCO_SHIFT) | + (esrc << shift) | (RNG_CTL_ES1 << esrc); up->control[3] = base | @@ -589,6 +659,7 @@ static void n2rng_work(struct work_struct *work) { struct n2rng *np = container_of(work, struct n2rng, work.work); int err = 0; + static int retries = 4; if (!(np->flags & N2RNG_FLAG_CONTROL)) { err = n2rng_guest_check(np); @@ -606,7 +677,9 @@ static void n2rng_work(struct work_struct *work) dev_info(&np->op->dev, "RNG ready\n"); } - if (err && !(np->flags & N2RNG_FLAG_SHUTDOWN)) + if (--retries == 0) + dev_err(&np->op->dev, "Self-test retries failed, RNG not ready\n"); + else if (err && !(np->flags & N2RNG_FLAG_SHUTDOWN)) schedule_delayed_work(&np->work, HZ * 2); } @@ -622,24 +695,23 @@ static const struct of_device_id n2rng_match[]; static int n2rng_probe(struct platform_device *op) { const struct of_device_id *match; - int multi_capable; int err = -ENOMEM; struct n2rng *np; match = of_match_device(n2rng_match, &op->dev); if (!match) return -EINVAL; - multi_capable = (match->data != NULL); n2rng_driver_version(); np = devm_kzalloc(&op->dev, sizeof(*np), GFP_KERNEL); if (!np) goto out; np->op = op; + np->data = (struct n2rng_template *)match->data; INIT_DELAYED_WORK(&np->work, n2rng_work); - if (multi_capable) + if (np->data->multi_capable) np->flags |= N2RNG_FLAG_MULTI; err = -ENODEV; @@ -670,8 +742,9 @@ static int n2rng_probe(struct platform_device *op) dev_err(&op->dev, "VF RNG lacks rng-#units property\n"); goto out_hvapi_unregister; } - } else + } else { np->num_units = 1; + } dev_info(&op->dev, "Registered RNG HVAPI major %lu minor %lu\n", np->hvapi_major, np->hvapi_minor); @@ -692,7 +765,7 @@ static int n2rng_probe(struct platform_device *op) "multi-unit-capable" : "single-unit"), np->num_units); - np->hwrng.name = "n2rng"; + np->hwrng.name = DRV_MODULE_NAME; np->hwrng.data_read = n2rng_data_read; np->hwrng.priv = (unsigned long) np; @@ -728,30 +801,61 @@ static int n2rng_remove(struct platform_device *op) return 0; } +static struct n2rng_template n2_template = { + .id = N2_n2_rng, + .multi_capable = 0, + .chip_version = 1, +}; + +static struct n2rng_template vf_template = { + .id = N2_vf_rng, + .multi_capable = 1, + .chip_version = 1, +}; + +static struct n2rng_template kt_template = { + .id = N2_kt_rng, + .multi_capable = 1, + .chip_version = 1, +}; + +static struct n2rng_template m4_template = { + .id = N2_m4_rng, + .multi_capable = 1, + .chip_version = 2, +}; + +static struct n2rng_template m7_template = { + .id = N2_m7_rng, + .multi_capable = 1, + .chip_version = 2, +}; + static const struct of_device_id n2rng_match[] = { { .name = "random-number-generator", .compatible = "SUNW,n2-rng", + .data = &n2_template, }, { .name = "random-number-generator", .compatible = "SUNW,vf-rng", - .data = (void *) 1, + .data = &vf_template, }, { .name = "random-number-generator", .compatible = "SUNW,kt-rng", - .data = (void *) 1, + .data = &kt_template, }, { .name = "random-number-generator", .compatible = "ORCL,m4-rng", - .data = (void *) 1, + .data = &m4_template, }, { .name = "random-number-generator", .compatible = "ORCL,m7-rng", - .data = (void *) 1, + .data = &m7_template, }, {}, }; diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h index f244ac89087f..6bad6cc634e8 100644 --- a/drivers/char/hw_random/n2rng.h +++ b/drivers/char/hw_random/n2rng.h @@ -6,18 +6,34 @@ #ifndef _N2RNG_H #define _N2RNG_H -#define RNG_CTL_WAIT 0x0000000001fffe00ULL /* Minimum wait time */ -#define RNG_CTL_WAIT_SHIFT 9 -#define RNG_CTL_BYPASS 0x0000000000000100ULL /* VCO voltage source */ -#define RNG_CTL_VCO 0x00000000000000c0ULL /* VCO rate control */ -#define RNG_CTL_VCO_SHIFT 6 -#define RNG_CTL_ASEL 0x0000000000000030ULL /* Analog MUX select */ -#define RNG_CTL_ASEL_SHIFT 4 +/* ver1 devices - n2-rng, vf-rng, kt-rng */ +#define RNG_v1_CTL_WAIT 0x0000000001fffe00ULL /* Minimum wait time */ +#define RNG_v1_CTL_WAIT_SHIFT 9 +#define RNG_v1_CTL_BYPASS 0x0000000000000100ULL /* VCO voltage source */ +#define RNG_v1_CTL_VCO 0x00000000000000c0ULL /* VCO rate control */ +#define RNG_v1_CTL_VCO_SHIFT 6 +#define RNG_v1_CTL_ASEL 0x0000000000000030ULL /* Analog MUX select */ +#define RNG_v1_CTL_ASEL_SHIFT 4 +#define RNG_v1_CTL_ASEL_NOOUT 2 + +/* these are the same in v2 as in v1 */ #define RNG_CTL_LFSR 0x0000000000000008ULL /* Use LFSR or plain shift */ #define RNG_CTL_ES3 0x0000000000000004ULL /* Enable entropy source 3 */ #define RNG_CTL_ES2 0x0000000000000002ULL /* Enable entropy source 2 */ #define RNG_CTL_ES1 0x0000000000000001ULL /* Enable entropy source 1 */ +/* ver2 devices - m4-rng, m7-rng */ +#define RNG_v2_CTL_WAIT 0x0000000007fff800ULL /* Minimum wait time */ +#define RNG_v2_CTL_WAIT_SHIFT 12 +#define RNG_v2_CTL_BYPASS 0x0000000000000400ULL /* VCO voltage source */ +#define RNG_v2_CTL_VCO 0x0000000000000300ULL /* VCO rate control */ +#define RNG_v2_CTL_VCO_SHIFT 9 +#define RNG_v2_CTL_PERF 0x0000000000000180ULL /* Perf */ +#define RNG_v2_CTL_ASEL 0x0000000000000070ULL /* Analog MUX select */ +#define RNG_v2_CTL_ASEL_SHIFT 4 +#define RNG_v2_CTL_ASEL_NOOUT 7 + + #define HV_FAST_RNG_GET_DIAG_CTL 0x130 #define HV_FAST_RNG_CTL_READ 0x131 #define HV_FAST_RNG_CTL_WRITE 0x132 @@ -60,6 +76,20 @@ extern unsigned long sun4v_rng_data_read_diag_v2(unsigned long data_ra, extern unsigned long sun4v_rng_data_read(unsigned long data_ra, unsigned long *tick_delta); +enum n2rng_compat_id { + N2_n2_rng, + N2_vf_rng, + N2_kt_rng, + N2_m4_rng, + N2_m7_rng, +}; + +struct n2rng_template { + enum n2rng_compat_id id; + int multi_capable; + int chip_version; +}; + struct n2rng_unit { u64 control[HV_RNG_NUM_CONTROL]; }; @@ -74,6 +104,7 @@ struct n2rng { #define N2RNG_FLAG_SHUTDOWN 0x00000010 /* Driver unregistering */ #define N2RNG_FLAG_BUFFER_VALID 0x00000020 /* u32 buffer holds valid data */ + struct n2rng_template *data; int num_units; struct n2rng_unit *units; @@ -97,8 +128,10 @@ struct n2rng { u64 scratch_control[HV_RNG_NUM_CONTROL]; -#define SELFTEST_TICKS 38859 -#define SELFTEST_VAL ((u64)0xB8820C7BD387E32C) +#define RNG_v1_SELFTEST_TICKS 38859 +#define RNG_v1_SELFTEST_VAL ((u64)0xB8820C7BD387E32C) +#define RNG_v2_SELFTEST_TICKS 64 +#define RNG_v2_SELFTEST_VAL ((u64)0xffffffffffffffff) #define SELFTEST_POLY ((u64)0x231DCEE91262B8A3) #define SELFTEST_MATCH_GOAL 6 #define SELFTEST_LOOPS_MAX 40000 diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 7f816655cbbf..90f3edffb067 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -78,7 +78,8 @@ config IPMI_POWEROFF endif # IPMI_HANDLER config ASPEED_BT_IPMI_BMC - depends on ARCH_ASPEED + depends on ARCH_ASPEED || COMPILE_TEST + depends on REGMAP && REGMAP_MMIO && MFD_SYSCON tristate "BT IPMI bmc driver" help Provides a driver for the BT (Block Transfer) IPMI interface diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index fc9e8891eae3..d6f5d9eb102d 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -12,10 +12,13 @@ #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/mfd/syscon.h> #include <linux/miscdevice.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/poll.h> +#include <linux/regmap.h> #include <linux/sched.h> #include <linux/timer.h> @@ -60,7 +63,8 @@ struct bt_bmc { struct device dev; struct miscdevice miscdev; - void __iomem *base; + struct regmap *map; + int offset; int irq; wait_queue_head_t queue; struct timer_list poll_timer; @@ -69,14 +73,29 @@ struct bt_bmc { static atomic_t open_count = ATOMIC_INIT(0); +static const struct regmap_config bt_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + static u8 bt_inb(struct bt_bmc *bt_bmc, int reg) { - return ioread8(bt_bmc->base + reg); + uint32_t val = 0; + int rc; + + rc = regmap_read(bt_bmc->map, bt_bmc->offset + reg, &val); + WARN(rc != 0, "regmap_read() failed: %d\n", rc); + + return rc == 0 ? (u8) val : 0; } static void bt_outb(struct bt_bmc *bt_bmc, u8 data, int reg) { - iowrite8(data, bt_bmc->base + reg); + int rc; + + rc = regmap_write(bt_bmc->map, bt_bmc->offset + reg, data); + WARN(rc != 0, "regmap_write() failed: %d\n", rc); } static void clr_rd_ptr(struct bt_bmc *bt_bmc) @@ -367,14 +386,18 @@ static irqreturn_t bt_bmc_irq(int irq, void *arg) { struct bt_bmc *bt_bmc = arg; u32 reg; + int rc; + + rc = regmap_read(bt_bmc->map, bt_bmc->offset + BT_CR2, ®); + if (rc) + return IRQ_NONE; - reg = ioread32(bt_bmc->base + BT_CR2); reg &= BT_CR2_IRQ_H2B | BT_CR2_IRQ_HBUSY; if (!reg) return IRQ_NONE; /* ack pending IRQs */ - iowrite32(reg, bt_bmc->base + BT_CR2); + regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR2, reg); wake_up(&bt_bmc->queue); return IRQ_HANDLED; @@ -384,7 +407,6 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc, struct platform_device *pdev) { struct device *dev = &pdev->dev; - u32 reg; int rc; bt_bmc->irq = platform_get_irq(pdev, 0); @@ -405,18 +427,17 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc, * will be cleared (along with B2H) when we can write the next * message to the BT buffer */ - reg = ioread32(bt_bmc->base + BT_CR1); - reg |= BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY; - iowrite32(reg, bt_bmc->base + BT_CR1); + rc = regmap_update_bits(bt_bmc->map, bt_bmc->offset + BT_CR1, + (BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY), + (BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY)); - return 0; + return rc; } static int bt_bmc_probe(struct platform_device *pdev) { struct bt_bmc *bt_bmc; struct device *dev; - struct resource *res; int rc; if (!pdev || !pdev->dev.of_node) @@ -431,10 +452,27 @@ static int bt_bmc_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, bt_bmc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bt_bmc->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(bt_bmc->base)) - return PTR_ERR(bt_bmc->base); + bt_bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(bt_bmc->map)) { + struct resource *res; + void __iomem *base; + + /* + * Assume it's not the MFD-based devicetree description, in + * which case generate a regmap ourselves + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + bt_bmc->map = devm_regmap_init_mmio(dev, base, &bt_regmap_cfg); + bt_bmc->offset = 0; + } else { + rc = of_property_read_u32(dev->of_node, "reg", &bt_bmc->offset); + if (rc) + return rc; + } mutex_init(&bt_bmc->mutex); init_waitqueue_head(&bt_bmc->queue); @@ -461,12 +499,12 @@ static int bt_bmc_probe(struct platform_device *pdev) add_timer(&bt_bmc->poll_timer); } - iowrite32((BT_IO_BASE << BT_CR0_IO_BASE) | - (BT_IRQ << BT_CR0_IRQ) | - BT_CR0_EN_CLR_SLV_RDP | - BT_CR0_EN_CLR_SLV_WRP | - BT_CR0_ENABLE_IBT, - bt_bmc->base + BT_CR0); + regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR0, + (BT_IO_BASE << BT_CR0_IO_BASE) | + (BT_IRQ << BT_CR0_IRQ) | + BT_CR0_EN_CLR_SLV_RDP | + BT_CR0_EN_CLR_SLV_WRP | + BT_CR0_ENABLE_IBT); clr_b_busy(bt_bmc); diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index a21407de46ae..f45119c5337d 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -108,7 +108,7 @@ static int ipmi_fasync(int fd, struct file *file, int on) return (result); } -static struct ipmi_user_hndl ipmi_hndlrs = +static const struct ipmi_user_hndl ipmi_hndlrs = { .ipmi_recv_hndl = file_receive_handler, }; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 92e53acf2cd2..9f699951b75a 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -102,7 +102,7 @@ struct ipmi_user { struct kref refcount; /* The upper layer that handles receive messages. */ - struct ipmi_user_hndl *handler; + const struct ipmi_user_hndl *handler; void *handler_data; /* The interface this user is bound to. */ @@ -919,7 +919,7 @@ static int intf_err_seq(ipmi_smi_t intf, int ipmi_create_user(unsigned int if_num, - struct ipmi_user_hndl *handler, + const struct ipmi_user_hndl *handler, void *handler_data, ipmi_user_t *user) { diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index 6e658aa114f1..b338a4becbf8 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -196,7 +196,7 @@ static void ipmi_powernv_poll(void *send_info) ipmi_powernv_recv(smi); } -static struct ipmi_smi_handlers ipmi_powernv_smi_handlers = { +static const struct ipmi_smi_handlers ipmi_powernv_smi_handlers = { .owner = THIS_MODULE, .start_processing = ipmi_powernv_start_processing, .sender = ipmi_powernv_send, diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 4035495f3a86..5ca24d9b101b 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -53,6 +53,7 @@ #include <linux/ctype.h> #include <linux/delay.h> #include <linux/atomic.h> +#include <linux/sched/signal.h> #ifdef CONFIG_X86 /* @@ -985,7 +986,7 @@ static void ipmi_wdog_pretimeout_handler(void *handler_data) pretimeout_since_last_heartbeat = 1; } -static struct ipmi_user_hndl ipmi_hndlrs = { +static const struct ipmi_user_hndl ipmi_hndlrs = { .ipmi_recv_hndl = ipmi_wdog_msg_handler, .ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler }; diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 5b6742770656..565e4cf04a02 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -117,7 +117,7 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/major.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/fcntl.h> #include <linux/delay.h> diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index a697ca0cab1e..a9c2fa3c81e5 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -191,12 +191,12 @@ mspec_close(struct vm_area_struct *vma) * Creates a mspec page and maps it to user space. */ static int -mspec_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +mspec_fault(struct vm_fault *vmf) { unsigned long paddr, maddr; unsigned long pfn; pgoff_t index = vmf->pgoff; - struct vma_data *vdata = vma->vm_private_data; + struct vma_data *vdata = vmf->vma->vm_private_data; maddr = (volatile unsigned long) vdata->maddr[index]; if (maddr == 0) { @@ -227,7 +227,7 @@ mspec_fault(struct vm_area_struct *vma, struct vm_fault *vmf) * be because another thread has installed the pte first, so it * is no problem. */ - vm_insert_pfn(vma, vmf->address, pfn); + vm_insert_pfn(vmf->vma, vmf->address, pfn); return VM_FAULT_NOPAGE; } diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index e051fc8aa7d7..cd53771b9ae7 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -655,7 +655,7 @@ static void terminate_monitor(struct cm4000_dev *dev) * monitor the card every 50msec. as a side-effect, retrieve the * atr once a card is inserted. another side-effect of retrieving the * atr is that the card will be powered on, so there is no need to - * power on the card explictely from the application: the driver + * power on the card explicitly from the application: the driver * is already doing that for you. */ @@ -1037,7 +1037,7 @@ release_io: clear_bit(LOCK_IO, &dev->flags); wake_up_interruptible(&dev->ioq); - DEBUGP(2, dev, "<- cmm_read returns: rc = %Zi\n", + DEBUGP(2, dev, "<- cmm_read returns: rc = %zi\n", (rc < 0 ? rc : count)); return rc < 0 ? rc : count; } diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index d7123259143e..d4dbd8d8e524 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -331,7 +331,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, } if ((count < 5) || (count > READ_WRITE_BUFFER_SIZE)) { - DEBUGP(2, dev, "<- cm4040_write buffersize=%Zd < 5\n", count); + DEBUGP(2, dev, "<- cm4040_write buffersize=%zd < 5\n", count); return -EIO; } diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 87885d146dbb..2a558c706581 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -58,7 +58,7 @@ #include <linux/module.h> #include <linux/init.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/device.h> #include <linux/ioctl.h> #include <linux/parport.h> diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 35259961cc38..974d48927b07 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -74,7 +74,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/spinlock.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/sysctl.h> #include <linux/wait.h> #include <linux/bcd.h> diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c index ec07f0e99732..6aa32679fd58 100644 --- a/drivers/char/snsc.c +++ b/drivers/char/snsc.c @@ -16,7 +16,7 @@ */ #include <linux/interrupt.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/device.h> #include <linux/poll.h> #include <linux/init.h> diff --git a/drivers/char/snsc_event.c b/drivers/char/snsc_event.c index 59bcefd6ec7c..e452673dff66 100644 --- a/drivers/char/snsc_event.c +++ b/drivers/char/snsc_event.c @@ -16,7 +16,7 @@ */ #include <linux/interrupt.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <asm/byteorder.h> #include <asm/sn/sn_sal.h> diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 4fa7fcd8af36..f4f866ee54bc 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -603,7 +603,7 @@ static void sonypi_type3_srs(void) u16 v16; u8 v8; - /* This model type uses the same initialiazation of + /* This model type uses the same initialization of * the embedded controller as the type2 models. */ sonypi_type2_srs(); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 17857beb4892..e9b7e0b3cabe 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1136,6 +1136,8 @@ static int put_chars(u32 vtermno, const char *buf, int count) { struct port *port; struct scatterlist sg[1]; + void *data; + int ret; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); @@ -1144,8 +1146,14 @@ static int put_chars(u32 vtermno, const char *buf, int count) if (!port) return -EPIPE; - sg_init_one(sg, buf, count); - return __send_to_port(port, sg, 1, count, (void *)buf, false); + data = kmemdup(buf, count, GFP_ATOMIC); + if (!data) + return -ENOMEM; + + sg_init_one(sg, data, count); + ret = __send_to_port(port, sg, 1, count, data, false); + kfree(data); + return ret; } /* @@ -1939,7 +1947,7 @@ static int init_vqs(struct ports_device *portdev) /* Find the queues. */ err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, io_callbacks, - (const char **)io_names); + (const char **)io_names, NULL); if (err) goto free; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 56c1998ced3e..9356ab4b7d76 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -95,16 +95,17 @@ config COMMON_CLK_CDCE706 This driver supports TI CDCE706 programmable 3-PLL clock synthesizer. config COMMON_CLK_CDCE925 - tristate "Clock driver for TI CDCE925 devices" + tristate "Clock driver for TI CDCE913/925/937/949 devices" depends on I2C depends on OF select REGMAP_I2C help ---help--- - This driver supports the TI CDCE925 programmable clock synthesizer. - The chip contains two PLLs with spread-spectrum clocking support and - five output dividers. The driver only supports the following setup, - and uses a fixed setting for the output muxes. + This driver supports the TI CDCE913/925/937/949 programmable clock + synthesizer. Each chip has different number of PLLs and outputs. + For example, the CDCE925 contains two PLLs with spread-spectrum + clocking support and five output dividers. The driver only supports + the following setup, and uses a fixed setting for the output muxes. Y1 is derived from the input clock Y2 and Y3 derive from PLL1 Y4 and Y5 derive from PLL2 @@ -198,6 +199,16 @@ config COMMON_CLK_OXNAS ---help--- Support for the OXNAS SoC Family clocks. +config COMMON_CLK_VC5 + tristate "Clock driver for IDT VersaClock5 devices" + depends on I2C + depends on OF + select REGMAP_I2C + help + ---help--- + This driver supports the IDT VersaClock5 programmable clock + generator. + source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/mediatek/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 925081ec14c0..92c12b86c2e8 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o +obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o @@ -87,6 +88,8 @@ obj-y += ti/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ +ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_X86) += x86/ +endif obj-$(CONFIG_ARCH_ZX) += zte/ obj-$(CONFIG_ARCH_ZYNQ) += zynq/ diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c index 411310d29581..02d3bcd6216c 100644 --- a/drivers/clk/axs10x/i2s_pll_clock.c +++ b/drivers/clk/axs10x/i2s_pll_clock.c @@ -182,6 +182,7 @@ static int i2s_pll_clk_probe(struct platform_device *pdev) if (IS_ERR(pll_clk->base)) return PTR_ERR(pll_clk->base); + memset(&init, 0, sizeof(init)); clk_name = node->name; init.name = clk_name; init.ops = &i2s_pll_ops; diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 0d14409097e7..025853870619 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -39,6 +39,7 @@ #include <linux/clk.h> #include <linux/clk/bcm2835.h> #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -98,7 +99,8 @@ #define CM_SMIDIV 0x0b4 /* no definition for 0x0b8 and 0x0bc */ #define CM_TCNTCTL 0x0c0 -#define CM_TCNTDIV 0x0c4 +# define CM_TCNT_SRC1_SHIFT 12 +#define CM_TCNTCNT 0x0c4 #define CM_TECCTL 0x0c8 #define CM_TECDIV 0x0cc #define CM_TD0CTL 0x0d0 @@ -297,11 +299,32 @@ #define LOCK_TIMEOUT_NS 100000000 #define BCM2835_MAX_FB_RATE 1750000000u +/* + * Names of clocks used within the driver that need to be replaced + * with an external parent's name. This array is in the order that + * the clocks node in the DT references external clocks. + */ +static const char *const cprman_parent_names[] = { + "xosc", + "dsi0_byte", + "dsi0_ddr2", + "dsi0_ddr", + "dsi1_byte", + "dsi1_ddr2", + "dsi1_ddr", +}; + struct bcm2835_cprman { struct device *dev; void __iomem *regs; spinlock_t regs_lock; /* spinlock for all clocks */ - const char *osc_name; + + /* + * Real names of cprman clock parents looked up through + * of_clk_get_parent_name(), which will be used in the + * parent_names[] arrays for clock registration. + */ + const char *real_parent_names[ARRAY_SIZE(cprman_parent_names)]; /* Must be last */ struct clk_hw_onecell_data onecell; @@ -317,6 +340,61 @@ static inline u32 cprman_read(struct bcm2835_cprman *cprman, u32 reg) return readl(cprman->regs + reg); } +/* Does a cycle of measuring a clock through the TCNT clock, which may + * source from many other clocks in the system. + */ +static unsigned long bcm2835_measure_tcnt_mux(struct bcm2835_cprman *cprman, + u32 tcnt_mux) +{ + u32 osccount = 19200; /* 1ms */ + u32 count; + ktime_t timeout; + + spin_lock(&cprman->regs_lock); + + cprman_write(cprman, CM_TCNTCTL, CM_KILL); + + cprman_write(cprman, CM_TCNTCTL, + (tcnt_mux & CM_SRC_MASK) | + (tcnt_mux >> CM_SRC_BITS) << CM_TCNT_SRC1_SHIFT); + + cprman_write(cprman, CM_OSCCOUNT, osccount); + + /* do a kind delay at the start */ + mdelay(1); + + /* Finish off whatever is left of OSCCOUNT */ + timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS); + while (cprman_read(cprman, CM_OSCCOUNT)) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(cprman->dev, "timeout waiting for OSCCOUNT\n"); + count = 0; + goto out; + } + cpu_relax(); + } + + /* Wait for BUSY to clear. */ + timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS); + while (cprman_read(cprman, CM_TCNTCTL) & CM_BUSY) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(cprman->dev, "timeout waiting for !BUSY\n"); + count = 0; + goto out; + } + cpu_relax(); + } + + count = cprman_read(cprman, CM_TCNTCNT); + + cprman_write(cprman, CM_TCNTCTL, 0); + +out: + spin_unlock(&cprman->regs_lock); + + return count * 1000; +} + static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base, struct debugfs_reg32 *regs, size_t nregs, struct dentry *dentry) @@ -428,6 +506,7 @@ struct bcm2835_pll_divider_data { u32 load_mask; u32 hold_mask; u32 fixed_divider; + u32 flags; }; struct bcm2835_clock_data { @@ -451,6 +530,8 @@ struct bcm2835_clock_data { bool is_vpu_clock; bool is_mash_clock; + + u32 tcnt_mux; }; struct bcm2835_gate_data { @@ -906,6 +987,9 @@ static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock, const struct bcm2835_clock_data *data = clock->data; u64 temp; + if (data->int_bits == 0 && data->frac_bits == 0) + return parent_rate; + /* * The divisor is a 12.12 fixed point field, but only some of * the bits are populated in any given clock. @@ -929,7 +1013,12 @@ static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw, struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); struct bcm2835_cprman *cprman = clock->cprman; const struct bcm2835_clock_data *data = clock->data; - u32 div = cprman_read(cprman, data->div_reg); + u32 div; + + if (data->int_bits == 0 && data->frac_bits == 0) + return parent_rate; + + div = cprman_read(cprman, data->div_reg); return bcm2835_clock_rate_from_divisor(clock, parent_rate, div); } @@ -978,6 +1067,17 @@ static int bcm2835_clock_on(struct clk_hw *hw) CM_GATE); spin_unlock(&cprman->regs_lock); + /* Debug code to measure the clock once it's turned on to see + * if it's ticking at the rate we expect. + */ + if (data->tcnt_mux && false) { + dev_info(cprman->dev, + "clk %s: rate %ld, measure %ld\n", + data->name, + clk_hw_get_rate(hw), + bcm2835_measure_tcnt_mux(cprman, data->tcnt_mux)); + } + return 0; } @@ -1208,7 +1308,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman, memset(&init, 0, sizeof(init)); /* All of the PLLs derive from the external oscillator. */ - init.parent_names = &cprman->osc_name; + init.parent_names = &cprman->real_parent_names[0]; init.num_parents = 1; init.name = data->name; init.ops = &bcm2835_pll_clk_ops; @@ -1252,7 +1352,7 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman, init.num_parents = 1; init.name = divider_name; init.ops = &bcm2835_pll_divider_clk_ops; - init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED; + init.flags = data->flags | CLK_IGNORE_UNUSED; divider = devm_kzalloc(cprman->dev, sizeof(*divider), GFP_KERNEL); if (!divider) @@ -1294,18 +1394,22 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman, struct bcm2835_clock *clock; struct clk_init_data init; const char *parents[1 << CM_SRC_BITS]; - size_t i; + size_t i, j; int ret; /* - * Replace our "xosc" references with the oscillator's - * actual name. + * Replace our strings referencing parent clocks with the + * actual clock-output-name of the parent. */ for (i = 0; i < data->num_mux_parents; i++) { - if (strcmp(data->parents[i], "xosc") == 0) - parents[i] = cprman->osc_name; - else - parents[i] = data->parents[i]; + parents[i] = data->parents[i]; + + for (j = 0; j < ARRAY_SIZE(cprman_parent_names); j++) { + if (strcmp(parents[i], cprman_parent_names[j]) == 0) { + parents[i] = cprman->real_parent_names[j]; + break; + } + } } memset(&init, 0, sizeof(init)); @@ -1432,6 +1536,47 @@ static const char *const bcm2835_clock_vpu_parents[] = { __VA_ARGS__) /* + * DSI parent clocks. The DSI byte/DDR/DDR2 clocks come from the DSI + * analog PHY. The _inv variants are generated internally to cprman, + * but we don't use them so they aren't hooked up. + */ +static const char *const bcm2835_clock_dsi0_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "dsi0_ddr", + "dsi0_ddr_inv", + "dsi0_ddr2", + "dsi0_ddr2_inv", + "dsi0_byte", + "dsi0_byte_inv", +}; + +static const char *const bcm2835_clock_dsi1_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "dsi1_ddr", + "dsi1_ddr_inv", + "dsi1_ddr2", + "dsi1_ddr2_inv", + "dsi1_byte", + "dsi1_byte_inv", +}; + +#define REGISTER_DSI0_CLK(...) REGISTER_CLK( \ + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi0_parents), \ + .parents = bcm2835_clock_dsi0_parents, \ + __VA_ARGS__) + +#define REGISTER_DSI1_CLK(...) REGISTER_CLK( \ + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_dsi1_parents), \ + .parents = bcm2835_clock_dsi1_parents, \ + __VA_ARGS__) + +/* * the real definition of all the pll, pll_dividers and clocks * these make use of the above REGISTER_* macros */ @@ -1466,7 +1611,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLA_CORE, .load_mask = CM_PLLA_LOADCORE, .hold_mask = CM_PLLA_HOLDCORE, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLA_PER] = REGISTER_PLL_DIV( .name = "plla_per", .source_pll = "plla", @@ -1474,7 +1620,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLA_PER, .load_mask = CM_PLLA_LOADPER, .hold_mask = CM_PLLA_HOLDPER, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLA_DSI0] = REGISTER_PLL_DIV( .name = "plla_dsi0", .source_pll = "plla", @@ -1490,7 +1637,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLA_CCP2, .load_mask = CM_PLLA_LOADCCP2, .hold_mask = CM_PLLA_HOLDCCP2, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), /* PLLB is used for the ARM's clock. */ [BCM2835_PLLB] = REGISTER_PLL( @@ -1514,7 +1662,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLB_ARM, .load_mask = CM_PLLB_LOADARM, .hold_mask = CM_PLLB_HOLDARM, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), /* * PLLC is the core PLL, used to drive the core VPU clock. @@ -1543,7 +1692,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLC_CORE0, .load_mask = CM_PLLC_LOADCORE0, .hold_mask = CM_PLLC_HOLDCORE0, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLC_CORE1] = REGISTER_PLL_DIV( .name = "pllc_core1", .source_pll = "pllc", @@ -1551,7 +1701,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLC_CORE1, .load_mask = CM_PLLC_LOADCORE1, .hold_mask = CM_PLLC_HOLDCORE1, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLC_CORE2] = REGISTER_PLL_DIV( .name = "pllc_core2", .source_pll = "pllc", @@ -1559,7 +1710,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLC_CORE2, .load_mask = CM_PLLC_LOADCORE2, .hold_mask = CM_PLLC_HOLDCORE2, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLC_PER] = REGISTER_PLL_DIV( .name = "pllc_per", .source_pll = "pllc", @@ -1567,7 +1719,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLC_PER, .load_mask = CM_PLLC_LOADPER, .hold_mask = CM_PLLC_HOLDPER, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), /* * PLLD is the display PLL, used to drive DSI display panels. @@ -1596,7 +1749,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLD_CORE, .load_mask = CM_PLLD_LOADCORE, .hold_mask = CM_PLLD_HOLDCORE, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLD_PER] = REGISTER_PLL_DIV( .name = "plld_per", .source_pll = "plld", @@ -1604,7 +1758,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLD_PER, .load_mask = CM_PLLD_LOADPER, .hold_mask = CM_PLLD_HOLDPER, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLD_DSI0] = REGISTER_PLL_DIV( .name = "plld_dsi0", .source_pll = "plld", @@ -1649,7 +1804,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLH_RCAL, .load_mask = CM_PLLH_LOADRCAL, .hold_mask = 0, - .fixed_divider = 10), + .fixed_divider = 10, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLH_AUX] = REGISTER_PLL_DIV( .name = "pllh_aux", .source_pll = "pllh", @@ -1657,7 +1813,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLH_AUX, .load_mask = CM_PLLH_LOADAUX, .hold_mask = 0, - .fixed_divider = 1), + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT), [BCM2835_PLLH_PIX] = REGISTER_PLL_DIV( .name = "pllh_pix", .source_pll = "pllh", @@ -1665,7 +1822,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .a2w_reg = A2W_PLLH_PIX, .load_mask = CM_PLLH_LOADPIX, .hold_mask = 0, - .fixed_divider = 10), + .fixed_divider = 10, + .flags = CLK_SET_RATE_PARENT), /* the clocks */ @@ -1677,7 +1835,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_OTPCTL, .div_reg = CM_OTPDIV, .int_bits = 4, - .frac_bits = 0), + .frac_bits = 0, + .tcnt_mux = 6), /* * Used for a 1Mhz clock for the system clocksource, and also used * bythe watchdog timer and the camera pulse generator. @@ -1711,13 +1870,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_H264CTL, .div_reg = CM_H264DIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 1), [BCM2835_CLOCK_ISP] = REGISTER_VPU_CLK( .name = "isp", .ctl_reg = CM_ISPCTL, .div_reg = CM_ISPDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 2), /* * Secondary SDRAM clock. Used for low-voltage modes when the PLL @@ -1728,13 +1889,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_SDCCTL, .div_reg = CM_SDCDIV, .int_bits = 6, - .frac_bits = 0), + .frac_bits = 0, + .tcnt_mux = 3), [BCM2835_CLOCK_V3D] = REGISTER_VPU_CLK( .name = "v3d", .ctl_reg = CM_V3DCTL, .div_reg = CM_V3DDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 4), /* * VPU clock. This doesn't have an enable bit, since it drives * the bus for everything else, and is special so it doesn't need @@ -1748,7 +1911,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .int_bits = 12, .frac_bits = 8, .flags = CLK_IS_CRITICAL, - .is_vpu_clock = true), + .is_vpu_clock = true, + .tcnt_mux = 5), /* clocks with per parent mux */ [BCM2835_CLOCK_AVEO] = REGISTER_PER_CLK( @@ -1756,19 +1920,22 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_AVEOCTL, .div_reg = CM_AVEODIV, .int_bits = 4, - .frac_bits = 0), + .frac_bits = 0, + .tcnt_mux = 38), [BCM2835_CLOCK_CAM0] = REGISTER_PER_CLK( .name = "cam0", .ctl_reg = CM_CAM0CTL, .div_reg = CM_CAM0DIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 14), [BCM2835_CLOCK_CAM1] = REGISTER_PER_CLK( .name = "cam1", .ctl_reg = CM_CAM1CTL, .div_reg = CM_CAM1DIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 15), [BCM2835_CLOCK_DFT] = REGISTER_PER_CLK( .name = "dft", .ctl_reg = CM_DFTCTL, @@ -1780,7 +1947,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_DPICTL, .div_reg = CM_DPIDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 17), /* Arasan EMMC clock */ [BCM2835_CLOCK_EMMC] = REGISTER_PER_CLK( @@ -1788,7 +1956,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_EMMCCTL, .div_reg = CM_EMMCDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 39), /* General purpose (GPIO) clocks */ [BCM2835_CLOCK_GP0] = REGISTER_PER_CLK( @@ -1797,7 +1966,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .div_reg = CM_GP0DIV, .int_bits = 12, .frac_bits = 12, - .is_mash_clock = true), + .is_mash_clock = true, + .tcnt_mux = 20), [BCM2835_CLOCK_GP1] = REGISTER_PER_CLK( .name = "gp1", .ctl_reg = CM_GP1CTL, @@ -1805,7 +1975,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .int_bits = 12, .frac_bits = 12, .flags = CLK_IS_CRITICAL, - .is_mash_clock = true), + .is_mash_clock = true, + .tcnt_mux = 21), [BCM2835_CLOCK_GP2] = REGISTER_PER_CLK( .name = "gp2", .ctl_reg = CM_GP2CTL, @@ -1820,40 +1991,46 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_HSMCTL, .div_reg = CM_HSMDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 22), [BCM2835_CLOCK_PCM] = REGISTER_PER_CLK( .name = "pcm", .ctl_reg = CM_PCMCTL, .div_reg = CM_PCMDIV, .int_bits = 12, .frac_bits = 12, - .is_mash_clock = true), + .is_mash_clock = true, + .tcnt_mux = 23), [BCM2835_CLOCK_PWM] = REGISTER_PER_CLK( .name = "pwm", .ctl_reg = CM_PWMCTL, .div_reg = CM_PWMDIV, .int_bits = 12, .frac_bits = 12, - .is_mash_clock = true), + .is_mash_clock = true, + .tcnt_mux = 24), [BCM2835_CLOCK_SLIM] = REGISTER_PER_CLK( .name = "slim", .ctl_reg = CM_SLIMCTL, .div_reg = CM_SLIMDIV, .int_bits = 12, .frac_bits = 12, - .is_mash_clock = true), + .is_mash_clock = true, + .tcnt_mux = 25), [BCM2835_CLOCK_SMI] = REGISTER_PER_CLK( .name = "smi", .ctl_reg = CM_SMICTL, .div_reg = CM_SMIDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 27), [BCM2835_CLOCK_UART] = REGISTER_PER_CLK( .name = "uart", .ctl_reg = CM_UARTCTL, .div_reg = CM_UARTDIV, .int_bits = 10, - .frac_bits = 12), + .frac_bits = 12, + .tcnt_mux = 28), /* TV encoder clock. Only operating frequency is 108Mhz. */ [BCM2835_CLOCK_VEC] = REGISTER_PER_CLK( @@ -1866,7 +2043,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { * Allow rate change propagation only on PLLH_AUX which is * assigned index 7 in the parent array. */ - .set_rate_parent = BIT(7)), + .set_rate_parent = BIT(7), + .tcnt_mux = 29), /* dsi clocks */ [BCM2835_CLOCK_DSI0E] = REGISTER_PER_CLK( @@ -1874,13 +2052,29 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_DSI0ECTL, .div_reg = CM_DSI0EDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 18), [BCM2835_CLOCK_DSI1E] = REGISTER_PER_CLK( .name = "dsi1e", .ctl_reg = CM_DSI1ECTL, .div_reg = CM_DSI1EDIV, .int_bits = 4, - .frac_bits = 8), + .frac_bits = 8, + .tcnt_mux = 19), + [BCM2835_CLOCK_DSI0P] = REGISTER_DSI0_CLK( + .name = "dsi0p", + .ctl_reg = CM_DSI0PCTL, + .div_reg = CM_DSI0PDIV, + .int_bits = 0, + .frac_bits = 0, + .tcnt_mux = 12), + [BCM2835_CLOCK_DSI1P] = REGISTER_DSI1_CLK( + .name = "dsi1p", + .ctl_reg = CM_DSI1PCTL, + .div_reg = CM_DSI1PDIV, + .int_bits = 0, + .frac_bits = 0, + .tcnt_mux = 13), /* the gates */ @@ -1939,8 +2133,19 @@ static int bcm2835_clk_probe(struct platform_device *pdev) if (IS_ERR(cprman->regs)) return PTR_ERR(cprman->regs); - cprman->osc_name = of_clk_get_parent_name(dev->of_node, 0); - if (!cprman->osc_name) + memcpy(cprman->real_parent_names, cprman_parent_names, + sizeof(cprman_parent_names)); + of_clk_parent_fill(dev->of_node, cprman->real_parent_names, + ARRAY_SIZE(cprman_parent_names)); + + /* + * Make sure the external oscillator has been registered. + * + * The other (DSI) clocks are not present on older device + * trees, which we still need to support for backwards + * compatibility. + */ + if (!cprman->real_parent_names[0]) return -ENODEV; platform_set_drvdata(pdev, cprman); diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index f793b2d9238c..c933be01c7db 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -1,8 +1,8 @@ /* - * Driver for TI Dual PLL CDCE925 clock synthesizer + * Driver for TI Multi PLL CDCE913/925/937/949 clock synthesizer * - * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1 - * and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve + * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1, + * Y4/Y5 to PLL2, and so on. PLL frequency is set on a first-come-first-serve * basis. Clients can directly request any frequency that the chip can * deliver using the standard clk framework. In addition, the device can * be configured and activated via the devicetree. @@ -19,11 +19,32 @@ #include <linux/slab.h> #include <linux/gcd.h> -/* The chip has 2 PLLs which can be routed through dividers to 5 outputs. +/* Each chip has different number of PLLs and outputs, for example: + * The CECE925 has 2 PLLs which can be routed through dividers to 5 outputs. * Model this as 2 PLL clocks which are parents to the outputs. */ -#define NUMBER_OF_PLLS 2 -#define NUMBER_OF_OUTPUTS 5 + +enum { + CDCE913, + CDCE925, + CDCE937, + CDCE949, +}; + +struct clk_cdce925_chip_info { + int num_plls; + int num_outputs; +}; + +static const struct clk_cdce925_chip_info clk_cdce925_chip_info_tbl[] = { + [CDCE913] = { .num_plls = 1, .num_outputs = 3 }, + [CDCE925] = { .num_plls = 2, .num_outputs = 5 }, + [CDCE937] = { .num_plls = 3, .num_outputs = 7 }, + [CDCE949] = { .num_plls = 4, .num_outputs = 9 }, +}; + +#define MAX_NUMBER_OF_PLLS 4 +#define MAX_NUMBER_OF_OUTPUTS 9 #define CDCE925_REG_GLOBAL1 0x01 #define CDCE925_REG_Y1SPIPDIVH 0x02 @@ -43,7 +64,7 @@ struct clk_cdce925_output { struct clk_hw hw; struct clk_cdce925_chip *chip; u8 index; - u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */ + u16 pdiv; /* 1..127 for Y2-Y9; 1..1023 for Y1 */ }; #define to_clk_cdce925_output(_hw) \ container_of(_hw, struct clk_cdce925_output, hw) @@ -60,8 +81,9 @@ struct clk_cdce925_pll { struct clk_cdce925_chip { struct regmap *regmap; struct i2c_client *i2c_client; - struct clk_cdce925_pll pll[NUMBER_OF_PLLS]; - struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS]; + const struct clk_cdce925_chip_info *chip_info; + struct clk_cdce925_pll pll[MAX_NUMBER_OF_PLLS]; + struct clk_cdce925_output clk[MAX_NUMBER_OF_OUTPUTS]; }; /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ @@ -284,6 +306,18 @@ static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv) case 4: regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv); break; + case 5: + regmap_update_bits(data->chip->regmap, 0x36, 0x7F, pdiv); + break; + case 6: + regmap_update_bits(data->chip->regmap, 0x37, 0x7F, pdiv); + break; + case 7: + regmap_update_bits(data->chip->regmap, 0x46, 0x7F, pdiv); + break; + case 8: + regmap_update_bits(data->chip->regmap, 0x47, 0x7F, pdiv); + break; } } @@ -302,6 +336,14 @@ static void cdce925_clk_activate(struct clk_cdce925_output *data) case 4: regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03); break; + case 5: + case 6: + regmap_update_bits(data->chip->regmap, 0x34, 0x03, 0x03); + break; + case 7: + case 8: + regmap_update_bits(data->chip->regmap, 0x44, 0x03, 0x03); + break; } } @@ -474,15 +516,6 @@ static const struct clk_ops cdce925_clk_y1_ops = { .set_rate = cdce925_clk_y1_set_rate, }; - -static struct regmap_config cdce925_regmap_config = { - .name = "configuration0", - .reg_bits = 8, - .val_bits = 8, - .cache_type = REGCACHE_RBTREE, - .max_register = 0x2F, -}; - #define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00 #define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80 @@ -582,13 +615,19 @@ static int cdce925_probe(struct i2c_client *client, struct clk_cdce925_chip *data; struct device_node *node = client->dev.of_node; const char *parent_name; - const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,}; + const char *pll_clk_name[MAX_NUMBER_OF_PLLS] = {NULL,}; struct clk_init_data init; u32 value; int i; int err; struct device_node *np_output; char child_name[6]; + struct regmap_config config = { + .name = "configuration0", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + }; dev_dbg(&client->dev, "%s\n", __func__); data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); @@ -596,8 +635,11 @@ static int cdce925_probe(struct i2c_client *client, return -ENOMEM; data->i2c_client = client; + data->chip_info = &clk_cdce925_chip_info_tbl[id->driver_data]; + config.max_register = CDCE925_OFFSET_PLL + + data->chip_info->num_plls * 0x10 - 1; data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus, - &client->dev, &cdce925_regmap_config); + &client->dev, &config); if (IS_ERR(data->regmap)) { dev_err(&client->dev, "failed to allocate register map\n"); return PTR_ERR(data->regmap); @@ -626,7 +668,7 @@ static int cdce925_probe(struct i2c_client *client, init.num_parents = parent_name ? 1 : 0; /* Register PLL clocks */ - for (i = 0; i < NUMBER_OF_PLLS; ++i) { + for (i = 0; i < data->chip_info->num_plls; ++i) { pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d", client->dev.of_node->name, i); init.name = pll_clk_name[i]; @@ -684,7 +726,7 @@ static int cdce925_probe(struct i2c_client *client, init.ops = &cdce925_clk_ops; init.flags = CLK_SET_RATE_PARENT; init.num_parents = 1; - for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) { + for (i = 1; i < data->chip_info->num_outputs; ++i) { init.name = kasprintf(GFP_KERNEL, "%s.Y%d", client->dev.of_node->name, i+1); data->clk[i].chip = data; @@ -702,6 +744,16 @@ static int cdce925_probe(struct i2c_client *client, /* Mux Y4/5 to PLL2 */ init.parent_names = &pll_clk_name[1]; break; + case 5: + case 6: + /* Mux Y6/7 to PLL3 */ + init.parent_names = &pll_clk_name[2]; + break; + case 7: + case 8: + /* Mux Y8/9 to PLL4 */ + init.parent_names = &pll_clk_name[3]; + break; } err = devm_clk_hw_register(&client->dev, &data->clk[i].hw); kfree(init.name); /* clock framework made a copy of the name */ @@ -720,7 +772,7 @@ static int cdce925_probe(struct i2c_client *client, err = 0; error: - for (i = 0; i < NUMBER_OF_PLLS; ++i) + for (i = 0; i < data->chip_info->num_plls; ++i) /* clock framework made a copy of the name */ kfree(pll_clk_name[i]); @@ -728,13 +780,19 @@ error: } static const struct i2c_device_id cdce925_id[] = { - { "cdce925", 0 }, + { "cdce913", CDCE913 }, + { "cdce925", CDCE925 }, + { "cdce937", CDCE937 }, + { "cdce949", CDCE949 }, { } }; MODULE_DEVICE_TABLE(i2c, cdce925_id); static const struct of_device_id clk_cdce925_of_match[] = { + { .compatible = "ti,cdce913" }, { .compatible = "ti,cdce925" }, + { .compatible = "ti,cdce937" }, + { .compatible = "ti,cdce949" }, { }, }; MODULE_DEVICE_TABLE(of, clk_cdce925_of_match); @@ -750,5 +808,5 @@ static struct i2c_driver cdce925_driver = { module_i2c_driver(cdce925_driver); MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); -MODULE_DESCRIPTION("cdce925 driver"); +MODULE_DESCRIPTION("TI CDCE913/925/937/949 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 674785d968a3..e0e02a6e5900 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -40,8 +40,9 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) return 0; pclk = of_clk_get_from_provider(&clkspec); if (IS_ERR(pclk)) { - pr_warn("clk: couldn't get parent clock %d for %s\n", - index, node->full_name); + if (PTR_ERR(pclk) != -EPROBE_DEFER) + pr_warn("clk: couldn't get parent clock %d for %s\n", + index, node->full_name); return PTR_ERR(pclk); } @@ -55,8 +56,9 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) } clk = of_clk_get_from_provider(&clkspec); if (IS_ERR(clk)) { - pr_warn("clk: couldn't get assigned clock %d for %s\n", - index, node->full_name); + if (PTR_ERR(clk) != -EPROBE_DEFER) + pr_warn("clk: couldn't get assigned clock %d for %s\n", + index, node->full_name); rc = PTR_ERR(clk); goto err; } @@ -99,8 +101,9 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) clk = of_clk_get_from_provider(&clkspec); if (IS_ERR(clk)) { - pr_warn("clk: couldn't get clock %d for %s\n", - index, node->full_name); + if (PTR_ERR(clk) != -EPROBE_DEFER) + pr_warn("clk: couldn't get clock %d for %s\n", + index, node->full_name); return PTR_ERR(clk); } diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index 021f3daf34e1..3fca0526d940 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -59,6 +59,10 @@ struct cs2000_priv { struct i2c_client *client; struct clk *clk_in; struct clk *ref_clk; + + /* suspend/resume */ + unsigned long saved_rate; + unsigned long saved_parent_rate; }; static const struct of_device_id cs2000_of_match[] = { @@ -286,6 +290,9 @@ static int __cs2000_set_rate(struct cs2000_priv *priv, int ch, if (ret < 0) return ret; + priv->saved_rate = rate; + priv->saved_parent_rate = parent_rate; + return 0; } @@ -489,9 +496,24 @@ probe_err: return ret; } +static int cs2000_resume(struct device *dev) +{ + struct cs2000_priv *priv = dev_get_drvdata(dev); + int ch = 0; /* it uses ch0 only at this point */ + + return __cs2000_set_rate(priv, ch, + priv->saved_rate, + priv->saved_parent_rate); +} + +static const struct dev_pm_ops cs2000_pm_ops = { + .resume_early = cs2000_resume, +}; + static struct i2c_driver cs2000_driver = { .driver = { .name = "cs2000-cp", + .pm = &cs2000_pm_ops, .of_match_table = cs2000_of_match, }, .probe = cs2000_probe, diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c index 2a3e9d8e88b0..96d37175d0ad 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -290,13 +290,15 @@ static int scpi_clocks_probe(struct platform_device *pdev) of_node_put(child); return ret; } - } - /* Add the virtual cpufreq device */ - cpufreq_dev = platform_device_register_simple("scpi-cpufreq", - -1, NULL, 0); - if (IS_ERR(cpufreq_dev)) - pr_warn("unable to register cpufreq device"); + if (match->data != &scpi_dvfs_ops) + continue; + /* Add the virtual cpufreq device if it's DVFS clock provider */ + cpufreq_dev = platform_device_register_simple("scpi-cpufreq", + -1, NULL, 0); + if (IS_ERR(cpufreq_dev)) + pr_warn("unable to register cpufreq device"); + } return 0; } diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index fc585f370549..ab609a76706f 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -28,6 +28,14 @@ #include <linux/regmap.h> #include <linux/mfd/syscon.h> +/* + * Include list of clocks wich are not derived from system clock (SYSCLOCK) + * The index of these clocks is the secondary index of DT bindings + * + */ +#include <dt-bindings/clock/stm32fx-clock.h> + +#define STM32F4_RCC_CR 0x00 #define STM32F4_RCC_PLLCFGR 0x04 #define STM32F4_RCC_CFGR 0x08 #define STM32F4_RCC_AHB1ENR 0x30 @@ -37,6 +45,15 @@ #define STM32F4_RCC_APB2ENR 0x44 #define STM32F4_RCC_BDCR 0x70 #define STM32F4_RCC_CSR 0x74 +#define STM32F4_RCC_PLLI2SCFGR 0x84 +#define STM32F4_RCC_PLLSAICFGR 0x88 +#define STM32F4_RCC_DCKCFGR 0x8c +#define STM32F7_RCC_DCKCFGR2 0x90 + +#define NONE -1 +#define NO_IDX NONE +#define NO_MUX NONE +#define NO_GATE NONE struct stm32f4_gate_data { u8 offset; @@ -195,7 +212,7 @@ static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, - { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, + { STM32F4_RCC_APB2ENR, 11, "sdio", "sdmux" }, { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, @@ -208,7 +225,79 @@ static const struct stm32f4_gate_data stm32f469_gates[] __initconst = { { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, }; -enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK }; +static const struct stm32f4_gate_data stm32f746_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "dtcmram", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 16, "spdifrx", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 27, "cec", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdmmc", "sdmux" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 23, "sai2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, +}; /* * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx @@ -224,6 +313,10 @@ static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, 0x0000000000000003ull, 0x0c777f33f6fec9ffull }; +static const u64 stm32f746_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull, + 0x0000000000000003ull, + 0x04f77f033e01c9ffull }; + static const u64 *stm32f4_gate_map; static struct clk_hw **clks; @@ -233,6 +326,8 @@ static void __iomem *base; static struct regmap *pdrm; +static int stm32fx_end_primary_clk; + /* * "Multiplier" device for APBx clocks. * @@ -324,23 +419,342 @@ static struct clk *clk_register_apb_mul(struct device *dev, const char *name, return clk; } -/* - * Decode current PLL state and (statically) model the state we inherit from - * the bootloader. - */ -static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk) +enum { + PLL, + PLL_I2S, + PLL_SAI, +}; + +static const struct clk_div_table pll_divp_table[] = { + { 0, 2 }, { 1, 4 }, { 2, 6 }, { 3, 8 }, { 0 } +}; + +static const struct clk_div_table pll_divr_table[] = { + { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 } +}; + +struct stm32f4_pll { + spinlock_t *lock; + struct clk_gate gate; + u8 offset; + u8 bit_rdy_idx; + u8 status; + u8 n_start; +}; + +#define to_stm32f4_pll(_gate) container_of(_gate, struct stm32f4_pll, gate) + +struct stm32f4_pll_post_div_data { + int idx; + u8 pll_num; + const char *name; + const char *parent; + u8 flag; + u8 offset; + u8 shift; + u8 width; + u8 flag_div; + const struct clk_div_table *div_table; +}; + +struct stm32f4_vco_data { + const char *vco_name; + u8 offset; + u8 bit_idx; + u8 bit_rdy_idx; +}; + +static const struct stm32f4_vco_data vco_data[] = { + { "vco", STM32F4_RCC_PLLCFGR, 24, 25 }, + { "vco-i2s", STM32F4_RCC_PLLI2SCFGR, 26, 27 }, + { "vco-sai", STM32F4_RCC_PLLSAICFGR, 28, 29 }, +}; + + +static const struct clk_div_table post_divr_table[] = { + { 0, 2 }, { 1, 4 }, { 2, 8 }, { 3, 16 }, { 0 } +}; + +#define MAX_POST_DIV 3 +static const struct stm32f4_pll_post_div_data post_div_data[MAX_POST_DIV] = { + { CLK_I2SQ_PDIV, PLL_I2S, "plli2s-q-div", "plli2s-q", + CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 0, 5, 0, NULL}, + + { CLK_SAIQ_PDIV, PLL_SAI, "pllsai-q-div", "pllsai-q", + CLK_SET_RATE_PARENT, STM32F4_RCC_DCKCFGR, 8, 5, 0, NULL }, + + { NO_IDX, PLL_SAI, "pllsai-r-div", "pllsai-r", CLK_SET_RATE_PARENT, + STM32F4_RCC_DCKCFGR, 16, 2, 0, post_divr_table }, +}; + +struct stm32f4_div_data { + u8 shift; + u8 width; + u8 flag_div; + const struct clk_div_table *div_table; +}; + +#define MAX_PLL_DIV 3 +static const struct stm32f4_div_data div_data[MAX_PLL_DIV] = { + { 16, 2, 0, pll_divp_table }, + { 24, 4, CLK_DIVIDER_ONE_BASED, NULL }, + { 28, 3, 0, pll_divr_table }, +}; + +struct stm32f4_pll_data { + u8 pll_num; + u8 n_start; + const char *div_name[MAX_PLL_DIV]; +}; + +static const struct stm32f4_pll_data stm32f429_pll[MAX_PLL_DIV] = { + { PLL, 192, { "pll", "pll48", NULL } }, + { PLL_I2S, 192, { NULL, "plli2s-q", "plli2s-r" } }, + { PLL_SAI, 49, { NULL, "pllsai-q", "pllsai-r" } }, +}; + +static const struct stm32f4_pll_data stm32f469_pll[MAX_PLL_DIV] = { + { PLL, 50, { "pll", "pll-q", NULL } }, + { PLL_I2S, 50, { "plli2s-p", "plli2s-q", "plli2s-r" } }, + { PLL_SAI, 50, { "pllsai-p", "pllsai-q", "pllsai-r" } }, +}; + +static int stm32f4_pll_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static int stm32f4_pll_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + int ret = 0; + unsigned long reg; + + ret = clk_gate_ops.enable(hw); + + ret = readl_relaxed_poll_timeout_atomic(base + STM32F4_RCC_CR, reg, + reg & (1 << pll->bit_rdy_idx), 0, 10000); + + return ret; +} + +static void stm32f4_pll_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static unsigned long stm32f4_pll_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + unsigned long n; + + n = (readl(base + pll->offset) >> 6) & 0x1ff; + + return parent_rate * n; +} + +static long stm32f4_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + unsigned long n; + + n = rate / *prate; + + if (n < pll->n_start) + n = pll->n_start; + else if (n > 432) + n = 432; + + return *prate * n; +} + +static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32f4_pll *pll = to_stm32f4_pll(gate); + + unsigned long n; + unsigned long val; + int pll_state; + + pll_state = stm32f4_pll_is_enabled(hw); + + if (pll_state) + stm32f4_pll_disable(hw); + + n = rate / parent_rate; + + val = readl(base + pll->offset) & ~(0x1ff << 6); + + writel(val | ((n & 0x1ff) << 6), base + pll->offset); + + if (pll_state) + stm32f4_pll_enable(hw); + + return 0; +} + +static const struct clk_ops stm32f4_pll_gate_ops = { + .enable = stm32f4_pll_enable, + .disable = stm32f4_pll_disable, + .is_enabled = stm32f4_pll_is_enabled, + .recalc_rate = stm32f4_pll_recalc, + .round_rate = stm32f4_pll_round_rate, + .set_rate = stm32f4_pll_set_rate, +}; + +struct stm32f4_pll_div { + struct clk_divider div; + struct clk_hw *hw_pll; +}; + +#define to_pll_div_clk(_div) container_of(_div, struct stm32f4_pll_div, div) + +static unsigned long stm32f4_pll_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long stm32f4_pll_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clk_divider_ops.round_rate(hw, rate, prate); +} + +static int stm32f4_pll_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { - unsigned long pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); + int pll_state, ret; + + struct clk_divider *div = to_clk_divider(hw); + struct stm32f4_pll_div *pll_div = to_pll_div_clk(div); + + pll_state = stm32f4_pll_is_enabled(pll_div->hw_pll); + + if (pll_state) + stm32f4_pll_disable(pll_div->hw_pll); + + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); - unsigned long pllm = pllcfgr & 0x3f; - unsigned long plln = (pllcfgr >> 6) & 0x1ff; - unsigned long pllp = BIT(((pllcfgr >> 16) & 3) + 1); - const char *pllsrc = pllcfgr & BIT(22) ? hse_clk : hsi_clk; - unsigned long pllq = (pllcfgr >> 24) & 0xf; + if (pll_state) + stm32f4_pll_enable(pll_div->hw_pll); - clk_register_fixed_factor(NULL, "vco", pllsrc, 0, plln, pllm); - clk_register_fixed_factor(NULL, "pll", "vco", 0, 1, pllp); - clk_register_fixed_factor(NULL, "pll48", "vco", 0, 1, pllq); + return ret; +} + +static const struct clk_ops stm32f4_pll_div_ops = { + .recalc_rate = stm32f4_pll_div_recalc_rate, + .round_rate = stm32f4_pll_div_round_rate, + .set_rate = stm32f4_pll_div_set_rate, +}; + +static struct clk_hw *clk_register_pll_div(const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + struct clk_hw *pll_hw, spinlock_t *lock) +{ + struct stm32f4_pll_div *pll_div; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + /* allocate the divider */ + pll_div = kzalloc(sizeof(*pll_div), GFP_KERNEL); + if (!pll_div) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &stm32f4_pll_div_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_divider assignments */ + pll_div->div.reg = reg; + pll_div->div.shift = shift; + pll_div->div.width = width; + pll_div->div.flags = clk_divider_flags; + pll_div->div.lock = lock; + pll_div->div.table = table; + pll_div->div.hw.init = &init; + + pll_div->hw_pll = pll_hw; + + /* register the clock */ + hw = &pll_div->div.hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll_div); + hw = ERR_PTR(ret); + } + + return hw; +} + +static struct clk_hw *stm32f4_rcc_register_pll(const char *pllsrc, + const struct stm32f4_pll_data *data, spinlock_t *lock) +{ + struct stm32f4_pll *pll; + struct clk_init_data init = { NULL }; + void __iomem *reg; + struct clk_hw *pll_hw; + int ret; + int i; + const struct stm32f4_vco_data *vco; + + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + vco = &vco_data[data->pll_num]; + + init.name = vco->vco_name; + init.ops = &stm32f4_pll_gate_ops; + init.flags = CLK_SET_RATE_GATE; + init.parent_names = &pllsrc; + init.num_parents = 1; + + pll->gate.lock = lock; + pll->gate.reg = base + STM32F4_RCC_CR; + pll->gate.bit_idx = vco->bit_idx; + pll->gate.hw.init = &init; + + pll->offset = vco->offset; + pll->n_start = data->n_start; + pll->bit_rdy_idx = vco->bit_rdy_idx; + pll->status = (readl(base + STM32F4_RCC_CR) >> vco->bit_idx) & 0x1; + + reg = base + pll->offset; + + pll_hw = &pll->gate.hw; + ret = clk_hw_register(NULL, pll_hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + for (i = 0; i < MAX_PLL_DIV; i++) + if (data->div_name[i]) + clk_register_pll_div(data->div_name[i], + vco->vco_name, + 0, + reg, + div_data[i].shift, + div_data[i].width, + div_data[i].flag_div, + div_data[i].div_table, + pll_hw, + lock); + return pll_hw; } /* @@ -352,7 +766,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) u64 table[MAX_GATE_MAP]; if (primary == 1) { - if (WARN_ON(secondary >= END_PRIMARY_CLK)) + if (WARN_ON(secondary >= stm32fx_end_primary_clk)) return -EINVAL; return secondary; } @@ -369,7 +783,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) table[BIT_ULL_WORD(secondary)] &= GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0); - return END_PRIMARY_CLK - 1 + hweight64(table[0]) + + return stm32fx_end_primary_clk - 1 + hweight64(table[0]) + (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) + (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0); } @@ -611,22 +1025,291 @@ static const char *rtc_parents[4] = { "no-clock", "lse", "lsi", "hse-rtc" }; +static const char *lcd_parent[1] = { "pllsai-r-div" }; + +static const char *i2s_parents[2] = { "plli2s-r", NULL }; + +static const char *sai_parents[4] = { "pllsai-q-div", "plli2s-q-div", NULL, + "no-clock" }; + +static const char *pll48_parents[2] = { "pll-q", "pllsai-p" }; + +static const char *sdmux_parents[2] = { "pll48", "sys" }; + +static const char *hdmi_parents[2] = { "lse", "hsi_div488" }; + +static const char *spdif_parent[1] = { "plli2s-p" }; + +static const char *lptim_parent[4] = { "apb1_mul", "lsi", "hsi", "lse" }; + +static const char *uart_parents1[4] = { "apb2_div", "sys", "hsi", "lse" }; +static const char *uart_parents2[4] = { "apb1_div", "sys", "hsi", "lse" }; + +static const char *i2c_parents[4] = { "apb1_div", "sys", "hsi", "no-clock" }; + +struct stm32_aux_clk { + int idx; + const char *name; + const char * const *parent_names; + int num_parents; + int offset_mux; + u8 shift; + u8 mask; + int offset_gate; + u8 bit_idx; + unsigned long flags; +}; + struct stm32f4_clk_data { const struct stm32f4_gate_data *gates_data; const u64 *gates_map; int gates_num; + const struct stm32f4_pll_data *pll_data; + const struct stm32_aux_clk *aux_clk; + int aux_clk_num; + int end_primary; +}; + +static const struct stm32_aux_clk stm32f429_aux_clk[] = { + { + CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), + NO_MUX, 0, 0, + STM32F4_RCC_APB2ENR, 26, + CLK_SET_RATE_PARENT + }, + { + CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), + STM32F4_RCC_CFGR, 23, 1, + NO_GATE, 0, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 20, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 22, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, +}; + +static const struct stm32_aux_clk stm32f469_aux_clk[] = { + { + CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), + NO_MUX, 0, 0, + STM32F4_RCC_APB2ENR, 26, + CLK_SET_RATE_PARENT + }, + { + CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), + STM32F4_RCC_CFGR, 23, 1, + NO_GATE, 0, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI1, "sai1-a", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 20, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI2, "sai1-b", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 22, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents), + STM32F4_RCC_DCKCFGR, 27, 1, + NO_GATE, 0, + 0 + }, + { + NO_IDX, "sdmux", sdmux_parents, ARRAY_SIZE(sdmux_parents), + STM32F4_RCC_DCKCFGR, 28, 1, + NO_GATE, 0, + 0 + }, +}; + +static const struct stm32_aux_clk stm32f746_aux_clk[] = { + { + CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), + NO_MUX, 0, 0, + STM32F4_RCC_APB2ENR, 26, + CLK_SET_RATE_PARENT + }, + { + CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), + STM32F4_RCC_CFGR, 23, 1, + NO_GATE, 0, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI1, "sai1_clk", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 20, 3, + STM32F4_RCC_APB2ENR, 22, + CLK_SET_RATE_PARENT + }, + { + CLK_SAI2, "sai2_clk", sai_parents, ARRAY_SIZE(sai_parents), + STM32F4_RCC_DCKCFGR, 22, 3, + STM32F4_RCC_APB2ENR, 23, + CLK_SET_RATE_PARENT + }, + { + NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents), + STM32F7_RCC_DCKCFGR2, 27, 1, + NO_GATE, 0, + 0 + }, + { + NO_IDX, "sdmux", sdmux_parents, ARRAY_SIZE(sdmux_parents), + STM32F7_RCC_DCKCFGR2, 28, 1, + NO_GATE, 0, + 0 + }, + { + CLK_HDMI_CEC, "hdmi-cec", + hdmi_parents, ARRAY_SIZE(hdmi_parents), + STM32F7_RCC_DCKCFGR2, 26, 1, + NO_GATE, 0, + 0 + }, + { + CLK_SPDIF, "spdif-rx", + spdif_parent, ARRAY_SIZE(spdif_parent), + STM32F7_RCC_DCKCFGR2, 22, 3, + STM32F4_RCC_APB2ENR, 23, + CLK_SET_RATE_PARENT + }, + { + CLK_USART1, "usart1", + uart_parents1, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 0, 3, + STM32F4_RCC_APB2ENR, 4, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART2, "usart2", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 2, 3, + STM32F4_RCC_APB1ENR, 17, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART3, "usart3", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 4, 3, + STM32F4_RCC_APB1ENR, 18, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART4, "uart4", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 6, 3, + STM32F4_RCC_APB1ENR, 19, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART5, "uart5", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 8, 3, + STM32F4_RCC_APB1ENR, 20, + CLK_SET_RATE_PARENT, + }, + { + CLK_USART6, "usart6", + uart_parents1, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 10, 3, + STM32F4_RCC_APB2ENR, 5, + CLK_SET_RATE_PARENT, + }, + + { + CLK_UART7, "uart7", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 12, 3, + STM32F4_RCC_APB1ENR, 30, + CLK_SET_RATE_PARENT, + }, + { + CLK_UART8, "uart8", + uart_parents2, ARRAY_SIZE(uart_parents1), + STM32F7_RCC_DCKCFGR2, 14, 3, + STM32F4_RCC_APB1ENR, 31, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C1, "i2c1", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 16, 3, + STM32F4_RCC_APB1ENR, 21, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C2, "i2c2", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 18, 3, + STM32F4_RCC_APB1ENR, 22, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C3, "i2c3", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 20, 3, + STM32F4_RCC_APB1ENR, 23, + CLK_SET_RATE_PARENT, + }, + { + CLK_I2C4, "i2c4", + i2c_parents, ARRAY_SIZE(i2c_parents), + STM32F7_RCC_DCKCFGR2, 22, 3, + STM32F4_RCC_APB1ENR, 24, + CLK_SET_RATE_PARENT, + }, + + { + CLK_LPTIMER, "lptim1", + lptim_parent, ARRAY_SIZE(lptim_parent), + STM32F7_RCC_DCKCFGR2, 24, 3, + STM32F4_RCC_APB1ENR, 9, + CLK_SET_RATE_PARENT + }, }; static const struct stm32f4_clk_data stm32f429_clk_data = { + .end_primary = END_PRIMARY_CLK, .gates_data = stm32f429_gates, .gates_map = stm32f42xx_gate_map, .gates_num = ARRAY_SIZE(stm32f429_gates), + .pll_data = stm32f429_pll, + .aux_clk = stm32f429_aux_clk, + .aux_clk_num = ARRAY_SIZE(stm32f429_aux_clk), }; static const struct stm32f4_clk_data stm32f469_clk_data = { + .end_primary = END_PRIMARY_CLK, .gates_data = stm32f469_gates, .gates_map = stm32f46xx_gate_map, .gates_num = ARRAY_SIZE(stm32f469_gates), + .pll_data = stm32f469_pll, + .aux_clk = stm32f469_aux_clk, + .aux_clk_num = ARRAY_SIZE(stm32f469_aux_clk), +}; + +static const struct stm32f4_clk_data stm32f746_clk_data = { + .end_primary = END_PRIMARY_CLK_F7, + .gates_data = stm32f746_gates, + .gates_map = stm32f746_gate_map, + .gates_num = ARRAY_SIZE(stm32f746_gates), + .pll_data = stm32f469_pll, + .aux_clk = stm32f746_aux_clk, + .aux_clk_num = ARRAY_SIZE(stm32f746_aux_clk), }; static const struct of_device_id stm32f4_of_match[] = { @@ -638,15 +1321,84 @@ static const struct of_device_id stm32f4_of_match[] = { .compatible = "st,stm32f469-rcc", .data = &stm32f469_clk_data }, + { + .compatible = "st,stm32f746-rcc", + .data = &stm32f746_clk_data + }, {} }; +static struct clk_hw *stm32_register_aux_clk(const char *name, + const char * const *parent_names, int num_parents, + int offset_mux, u8 shift, u8 mask, + int offset_gate, u8 bit_idx, + unsigned long flags, spinlock_t *lock) +{ + struct clk_hw *hw; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL; + + if (offset_gate != NO_GATE) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + gate->reg = base + offset_gate; + gate->bit_idx = bit_idx; + gate->flags = 0; + gate->lock = lock; + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (offset_mux != NO_MUX) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + mux->reg = base + offset_mux; + mux->shift = shift; + mux->mask = mask; + mux->flags = 0; + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + } + + if (mux_hw == NULL && gate_hw == NULL) { + hw = ERR_PTR(-EINVAL); + goto fail; + } + + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, mux_ops, + NULL, NULL, + gate_hw, gate_ops, + flags); + +fail: + if (IS_ERR(hw)) { + kfree(gate); + kfree(mux); + } + + return hw; +} + static void __init stm32f4_rcc_init(struct device_node *np) { - const char *hse_clk; + const char *hse_clk, *i2s_in_clk; int n; const struct of_device_id *match; const struct stm32f4_clk_data *data; + unsigned long pllcfgr; + const char *pllsrc; + unsigned long pllm; base = of_iomap(np, 0); if (!base) { @@ -666,7 +1418,9 @@ static void __init stm32f4_rcc_init(struct device_node *np) data = match->data; - clks = kmalloc_array(data->gates_num + END_PRIMARY_CLK, + stm32fx_end_primary_clk = data->end_primary; + + clks = kmalloc_array(data->gates_num + stm32fx_end_primary_clk, sizeof(*clks), GFP_KERNEL); if (!clks) goto fail; @@ -675,12 +1429,54 @@ static void __init stm32f4_rcc_init(struct device_node *np) hse_clk = of_clk_get_parent_name(np, 0); - clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0, - 16000000, 160000); - stm32f4_rcc_register_pll(hse_clk, "hsi"); + i2s_in_clk = of_clk_get_parent_name(np, 1); + + i2s_parents[1] = i2s_in_clk; + sai_parents[2] = i2s_in_clk; + + clks[CLK_HSI] = clk_hw_register_fixed_rate_with_accuracy(NULL, "hsi", + NULL, 0, 16000000, 160000); + + pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); + pllsrc = pllcfgr & BIT(22) ? hse_clk : "hsi"; + pllm = pllcfgr & 0x3f; + + clk_hw_register_fixed_factor(NULL, "vco_in", pllsrc, + 0, 1, pllm); + + stm32f4_rcc_register_pll("vco_in", &data->pll_data[0], + &stm32f4_clk_lock); + + clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in", + &data->pll_data[1], &stm32f4_clk_lock); + + clks[PLL_VCO_SAI] = stm32f4_rcc_register_pll("vco_in", + &data->pll_data[2], &stm32f4_clk_lock); + + for (n = 0; n < MAX_POST_DIV; n++) { + const struct stm32f4_pll_post_div_data *post_div; + struct clk_hw *hw; + + post_div = &post_div_data[n]; + + hw = clk_register_pll_div(post_div->name, + post_div->parent, + post_div->flag, + base + post_div->offset, + post_div->shift, + post_div->width, + post_div->flag_div, + post_div->div_table, + clks[post_div->pll_num], + &stm32f4_clk_lock); + + if (post_div->idx != NO_IDX) + clks[post_div->idx] = hw; + } sys_parents[1] = hse_clk; - clk_register_mux_table( + + clks[CLK_SYSCLK] = clk_hw_register_mux_table( NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0, base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock); @@ -762,6 +1558,33 @@ static void __init stm32f4_rcc_init(struct device_node *np) goto fail; } + for (n = 0; n < data->aux_clk_num; n++) { + const struct stm32_aux_clk *aux_clk; + struct clk_hw *hw; + + aux_clk = &data->aux_clk[n]; + + hw = stm32_register_aux_clk(aux_clk->name, + aux_clk->parent_names, aux_clk->num_parents, + aux_clk->offset_mux, aux_clk->shift, + aux_clk->mask, aux_clk->offset_gate, + aux_clk->bit_idx, aux_clk->flags, + &stm32f4_clk_lock); + + if (IS_ERR(hw)) { + pr_warn("Unable to register %s clk\n", aux_clk->name); + continue; + } + + if (aux_clk->idx != NO_IDX) + clks[aux_clk->idx] = hw; + } + + if (of_device_is_compatible(np, "st,stm32f746-rcc")) + + clk_hw_register_fixed_factor(NULL, "hsi_div488", "hsi", 0, + 1, 488); + of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL); return; fail: @@ -770,3 +1593,4 @@ fail: } CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init); +CLK_OF_DECLARE_DRIVER(stm32f746_rcc, "st,stm32f746-rcc", stm32f4_rcc_init); diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c new file mode 100644 index 000000000000..56741f3cf0a3 --- /dev/null +++ b/drivers/clk/clk-versaclock5.c @@ -0,0 +1,791 @@ +/* + * Driver for IDT Versaclock 5 + * + * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Possible optimizations: + * - Use spread spectrum + * - Use integer divider in FOD if applicable + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/rational.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +/* VersaClock5 registers */ +#define VC5_OTP_CONTROL 0x00 + +/* Factory-reserved register block */ +#define VC5_RSVD_DEVICE_ID 0x01 +#define VC5_RSVD_ADC_GAIN_7_0 0x02 +#define VC5_RSVD_ADC_GAIN_15_8 0x03 +#define VC5_RSVD_ADC_OFFSET_7_0 0x04 +#define VC5_RSVD_ADC_OFFSET_15_8 0x05 +#define VC5_RSVD_TEMPY 0x06 +#define VC5_RSVD_OFFSET_TBIN 0x07 +#define VC5_RSVD_GAIN 0x08 +#define VC5_RSVD_TEST_NP 0x09 +#define VC5_RSVD_UNUSED 0x0a +#define VC5_RSVD_BANDGAP_TRIM_UP 0x0b +#define VC5_RSVD_BANDGAP_TRIM_DN 0x0c +#define VC5_RSVD_CLK_R_12_CLK_AMP_4 0x0d +#define VC5_RSVD_CLK_R_34_CLK_AMP_4 0x0e +#define VC5_RSVD_CLK_AMP_123 0x0f + +/* Configuration register block */ +#define VC5_PRIM_SRC_SHDN 0x10 +#define VC5_PRIM_SRC_SHDN_EN_XTAL BIT(7) +#define VC5_PRIM_SRC_SHDN_EN_CLKIN BIT(6) +#define VC5_PRIM_SRC_SHDN_SP BIT(1) +#define VC5_PRIM_SRC_SHDN_EN_GBL_SHDN BIT(0) + +#define VC5_VCO_BAND 0x11 +#define VC5_XTAL_X1_LOAD_CAP 0x12 +#define VC5_XTAL_X2_LOAD_CAP 0x13 +#define VC5_REF_DIVIDER 0x15 +#define VC5_REF_DIVIDER_SEL_PREDIV2 BIT(7) +#define VC5_REF_DIVIDER_REF_DIV(n) ((n) & 0x3f) + +#define VC5_VCO_CTRL_AND_PREDIV 0x16 +#define VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV BIT(7) + +#define VC5_FEEDBACK_INT_DIV 0x17 +#define VC5_FEEDBACK_INT_DIV_BITS 0x18 +#define VC5_FEEDBACK_FRAC_DIV(n) (0x19 + (n)) +#define VC5_RC_CONTROL0 0x1e +#define VC5_RC_CONTROL1 0x1f +/* Register 0x20 is factory reserved */ + +/* Output divider control for divider 1,2,3,4 */ +#define VC5_OUT_DIV_CONTROL(idx) (0x21 + ((idx) * 0x10)) +#define VC5_OUT_DIV_CONTROL_RESET BIT(7) +#define VC5_OUT_DIV_CONTROL_SELB_NORM BIT(3) +#define VC5_OUT_DIV_CONTROL_SEL_EXT BIT(2) +#define VC5_OUT_DIV_CONTROL_INT_MODE BIT(1) +#define VC5_OUT_DIV_CONTROL_EN_FOD BIT(0) + +#define VC5_OUT_DIV_FRAC(idx, n) (0x22 + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_FRAC4_OD_SCEE BIT(1) + +#define VC5_OUT_DIV_STEP_SPREAD(idx, n) (0x26 + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_SPREAD_MOD(idx, n) (0x29 + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_SKEW_INT(idx, n) (0x2b + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_INT(idx, n) (0x2d + ((idx) * 0x10) + (n)) +#define VC5_OUT_DIV_SKEW_FRAC(idx) (0x2f + ((idx) * 0x10)) +/* Registers 0x30, 0x40, 0x50 are factory reserved */ + +/* Clock control register for clock 1,2 */ +#define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) +#define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0) + +#define VC5_CLK_OE_SHDN 0x68 +#define VC5_CLK_OS_SHDN 0x69 + +#define VC5_GLOBAL_REGISTER 0x76 +#define VC5_GLOBAL_REGISTER_GLOBAL_RESET BIT(5) + +/* PLL/VCO runs between 2.5 GHz and 3.0 GHz */ +#define VC5_PLL_VCO_MIN 2500000000UL +#define VC5_PLL_VCO_MAX 3000000000UL + +/* VC5 Input mux settings */ +#define VC5_MUX_IN_XIN BIT(0) +#define VC5_MUX_IN_CLKIN BIT(1) + +/* Supported IDT VC5 models. */ +enum vc5_model { + IDT_VC5_5P49V5923, + IDT_VC5_5P49V5933, +}; + +struct vc5_driver_data; + +struct vc5_hw_data { + struct clk_hw hw; + struct vc5_driver_data *vc5; + u32 div_int; + u32 div_frc; + unsigned int num; +}; + +struct vc5_driver_data { + struct i2c_client *client; + struct regmap *regmap; + enum vc5_model model; + + struct clk *pin_xin; + struct clk *pin_clkin; + unsigned char clk_mux_ins; + struct clk_hw clk_mux; + struct vc5_hw_data clk_pll; + struct vc5_hw_data clk_fod[2]; + struct vc5_hw_data clk_out[3]; +}; + +static const char * const vc5_mux_names[] = { + "mux" +}; + +static const char * const vc5_pll_names[] = { + "pll" +}; + +static const char * const vc5_fod_names[] = { + "fod0", "fod1", "fod2", "fod3", +}; + +static const char * const vc5_clk_out_names[] = { + "out0_sel_i2cb", "out1", "out2", "out3", "out4", +}; + +/* + * VersaClock5 i2c regmap + */ +static bool vc5_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + /* Factory reserved regs, make them read-only */ + if (reg <= 0xf) + return false; + + /* Factory reserved regs, make them read-only */ + if (reg == 0x14 || reg == 0x1c || reg == 0x1d) + return false; + + return true; +} + +static const struct regmap_config vc5_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 0x76, + .writeable_reg = vc5_regmap_is_writeable, +}; + +/* + * VersaClock5 input multiplexer between XTAL and CLKIN divider + */ +static unsigned char vc5_mux_get_parent(struct clk_hw *hw) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_mux); + const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN; + unsigned int src; + + regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src); + src &= mask; + + if (src == VC5_PRIM_SRC_SHDN_EN_XTAL) + return 0; + + if (src == VC5_PRIM_SRC_SHDN_EN_CLKIN) + return 1; + + dev_warn(&vc5->client->dev, + "Invalid clock input configuration (%02x)\n", src); + return 0; +} + +static int vc5_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_mux); + const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN; + u8 src; + + if ((index > 1) || !vc5->clk_mux_ins) + return -EINVAL; + + if (vc5->clk_mux_ins == (VC5_MUX_IN_CLKIN | VC5_MUX_IN_XIN)) { + if (index == 0) + src = VC5_PRIM_SRC_SHDN_EN_XTAL; + if (index == 1) + src = VC5_PRIM_SRC_SHDN_EN_CLKIN; + } else { + if (index != 0) + return -EINVAL; + + if (vc5->clk_mux_ins == VC5_MUX_IN_XIN) + src = VC5_PRIM_SRC_SHDN_EN_XTAL; + if (vc5->clk_mux_ins == VC5_MUX_IN_CLKIN) + src = VC5_PRIM_SRC_SHDN_EN_CLKIN; + } + + return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, mask, src); +} + +static unsigned long vc5_mux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_mux); + unsigned int prediv, div; + + regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv); + + /* The bypass_prediv is set, PLL fed from Ref_in directly. */ + if (prediv & VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV) + return parent_rate; + + regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div); + + /* The Sel_prediv2 is set, PLL fed from prediv2 (Ref_in / 2) */ + if (div & VC5_REF_DIVIDER_SEL_PREDIV2) + return parent_rate / 2; + else + return parent_rate / VC5_REF_DIVIDER_REF_DIV(div); +} + +static long vc5_mux_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long idiv; + + /* PLL cannot operate with input clock above 50 MHz. */ + if (rate > 50000000) + return -EINVAL; + + /* CLKIN within range of PLL input, feed directly to PLL. */ + if (*parent_rate <= 50000000) + return *parent_rate; + + idiv = DIV_ROUND_UP(*parent_rate, rate); + if (idiv > 127) + return -EINVAL; + + return *parent_rate / idiv; +} + +static int vc5_mux_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_mux); + unsigned long idiv; + u8 div; + + /* CLKIN within range of PLL input, feed directly to PLL. */ + if (parent_rate <= 50000000) { + regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, + VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV, + VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV); + regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00); + return 0; + } + + idiv = DIV_ROUND_UP(parent_rate, rate); + + /* We have dedicated div-2 predivider. */ + if (idiv == 2) + div = VC5_REF_DIVIDER_SEL_PREDIV2; + else + div = VC5_REF_DIVIDER_REF_DIV(idiv); + + regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div); + regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, + VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV, 0); + + return 0; +} + +static const struct clk_ops vc5_mux_ops = { + .set_parent = vc5_mux_set_parent, + .get_parent = vc5_mux_get_parent, + .recalc_rate = vc5_mux_recalc_rate, + .round_rate = vc5_mux_round_rate, + .set_rate = vc5_mux_set_rate, +}; + +/* + * VersaClock5 PLL/VCO + */ +static unsigned long vc5_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + u32 div_int, div_frc; + u8 fb[5]; + + regmap_bulk_read(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5); + + div_int = (fb[0] << 4) | (fb[1] >> 4); + div_frc = (fb[2] << 16) | (fb[3] << 8) | fb[4]; + + /* The PLL divider has 12 integer bits and 24 fractional bits */ + return (parent_rate * div_int) + ((parent_rate * div_frc) >> 24); +} + +static long vc5_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + u32 div_int; + u64 div_frc; + + if (rate < VC5_PLL_VCO_MIN) + rate = VC5_PLL_VCO_MIN; + if (rate > VC5_PLL_VCO_MAX) + rate = VC5_PLL_VCO_MAX; + + /* Determine integer part, which is 12 bit wide */ + div_int = rate / *parent_rate; + if (div_int > 0xfff) + rate = *parent_rate * 0xfff; + + /* Determine best fractional part, which is 24 bit wide */ + div_frc = rate % *parent_rate; + div_frc *= BIT(24) - 1; + do_div(div_frc, *parent_rate); + + hwdata->div_int = div_int; + hwdata->div_frc = (u32)div_frc; + + return (*parent_rate * div_int) + ((*parent_rate * div_frc) >> 24); +} + +static int vc5_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + u8 fb[5]; + + fb[0] = hwdata->div_int >> 4; + fb[1] = hwdata->div_int << 4; + fb[2] = hwdata->div_frc >> 16; + fb[3] = hwdata->div_frc >> 8; + fb[4] = hwdata->div_frc; + + return regmap_bulk_write(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5); +} + +static const struct clk_ops vc5_pll_ops = { + .recalc_rate = vc5_pll_recalc_rate, + .round_rate = vc5_pll_round_rate, + .set_rate = vc5_pll_set_rate, +}; + +static unsigned long vc5_fod_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + /* VCO frequency is divided by two before entering FOD */ + u32 f_in = parent_rate / 2; + u32 div_int, div_frc; + u8 od_int[2]; + u8 od_frc[4]; + + regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_INT(hwdata->num, 0), + od_int, 2); + regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0), + od_frc, 4); + + div_int = (od_int[0] << 4) | (od_int[1] >> 4); + div_frc = (od_frc[0] << 22) | (od_frc[1] << 14) | + (od_frc[2] << 6) | (od_frc[3] >> 2); + + /* The PLL divider has 12 integer bits and 30 fractional bits */ + return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); +} + +static long vc5_fod_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + /* VCO frequency is divided by two before entering FOD */ + u32 f_in = *parent_rate / 2; + u32 div_int; + u64 div_frc; + + /* Determine integer part, which is 12 bit wide */ + div_int = f_in / rate; + /* + * WARNING: The clock chip does not output signal if the integer part + * of the divider is 0xfff and fractional part is non-zero. + * Clamp the divider at 0xffe to keep the code simple. + */ + if (div_int > 0xffe) { + div_int = 0xffe; + rate = f_in / div_int; + } + + /* Determine best fractional part, which is 30 bit wide */ + div_frc = f_in % rate; + div_frc <<= 24; + do_div(div_frc, rate); + + hwdata->div_int = div_int; + hwdata->div_frc = (u32)div_frc; + + return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); +} + +static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + u8 data[14] = { + hwdata->div_frc >> 22, hwdata->div_frc >> 14, + hwdata->div_frc >> 6, hwdata->div_frc << 2, + 0, 0, 0, 0, 0, + 0, 0, + hwdata->div_int >> 4, hwdata->div_int << 4, + 0 + }; + + regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0), + data, 14); + + /* + * Toggle magic bit in undocumented register for unknown reason. + * This is what the IDT timing commander tool does and the chip + * datasheet somewhat implies this is needed, but the register + * and the bit is not documented. + */ + regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER, + VC5_GLOBAL_REGISTER_GLOBAL_RESET, 0); + regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER, + VC5_GLOBAL_REGISTER_GLOBAL_RESET, + VC5_GLOBAL_REGISTER_GLOBAL_RESET); + return 0; +} + +static const struct clk_ops vc5_fod_ops = { + .recalc_rate = vc5_fod_recalc_rate, + .round_rate = vc5_fod_round_rate, + .set_rate = vc5_fod_set_rate, +}; + +static int vc5_clk_out_prepare(struct clk_hw *hw) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + + /* Enable the clock buffer */ + regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), + VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, + VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); + return 0; +} + +static void vc5_clk_out_unprepare(struct clk_hw *hw) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + + /* Enable the clock buffer */ + regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), + VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, 0); +} + +static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT | + VC5_OUT_DIV_CONTROL_EN_FOD; + const u8 fodclkmask = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_EN_FOD; + const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT; + unsigned int src; + + regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); + src &= mask; + + if ((src & fodclkmask) == VC5_OUT_DIV_CONTROL_EN_FOD) + return 0; + + if (src == extclk) + return 1; + + dev_warn(&vc5->client->dev, + "Invalid clock output configuration (%02x)\n", src); + return 0; +} + +static int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index) +{ + struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_driver_data *vc5 = hwdata->vc5; + const u8 mask = VC5_OUT_DIV_CONTROL_RESET | + VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT | + VC5_OUT_DIV_CONTROL_EN_FOD; + const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT; + u8 src = VC5_OUT_DIV_CONTROL_RESET; + + if (index == 0) + src |= VC5_OUT_DIV_CONTROL_EN_FOD; + else + src |= extclk; + + return regmap_update_bits(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), + mask, src); +} + +static const struct clk_ops vc5_clk_out_ops = { + .prepare = vc5_clk_out_prepare, + .unprepare = vc5_clk_out_unprepare, + .set_parent = vc5_clk_out_set_parent, + .get_parent = vc5_clk_out_get_parent, +}; + +static struct clk_hw *vc5_of_clk_get(struct of_phandle_args *clkspec, + void *data) +{ + struct vc5_driver_data *vc5 = data; + unsigned int idx = clkspec->args[0]; + + if (idx > 2) + return ERR_PTR(-EINVAL); + + return &vc5->clk_out[idx].hw; +} + +static int vc5_map_index_to_output(const enum vc5_model model, + const unsigned int n) +{ + switch (model) { + case IDT_VC5_5P49V5933: + return (n == 0) ? 0 : 3; + case IDT_VC5_5P49V5923: + default: + return n; + } +} + +static const struct of_device_id clk_vc5_of_match[]; + +static int vc5_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct of_device_id *of_id = + of_match_device(clk_vc5_of_match, &client->dev); + struct vc5_driver_data *vc5; + struct clk_init_data init; + const char *parent_names[2]; + unsigned int n, idx; + int ret; + + vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL); + if (vc5 == NULL) + return -ENOMEM; + + i2c_set_clientdata(client, vc5); + vc5->client = client; + vc5->model = (enum vc5_model)of_id->data; + + vc5->pin_xin = devm_clk_get(&client->dev, "xin"); + if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + vc5->pin_clkin = devm_clk_get(&client->dev, "clkin"); + if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config); + if (IS_ERR(vc5->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(vc5->regmap); + } + + /* Register clock input mux */ + memset(&init, 0, sizeof(init)); + + if (!IS_ERR(vc5->pin_xin)) { + vc5->clk_mux_ins |= VC5_MUX_IN_XIN; + parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin); + } else if (vc5->model == IDT_VC5_5P49V5933) { + /* IDT VC5 5P49V5933 has built-in oscilator. */ + vc5->pin_xin = clk_register_fixed_rate(&client->dev, + "internal-xtal", NULL, + 0, 25000000); + if (IS_ERR(vc5->pin_xin)) + return PTR_ERR(vc5->pin_xin); + vc5->clk_mux_ins |= VC5_MUX_IN_XIN; + parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin); + } + + if (!IS_ERR(vc5->pin_clkin)) { + vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN; + parent_names[init.num_parents++] = + __clk_get_name(vc5->pin_clkin); + } + + if (!init.num_parents) { + dev_err(&client->dev, "no input clock specified!\n"); + return -EINVAL; + } + + init.name = vc5_mux_names[0]; + init.ops = &vc5_mux_ops; + init.flags = 0; + init.parent_names = parent_names; + vc5->clk_mux.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", init.name); + goto err_clk; + } + + /* Register PLL */ + memset(&init, 0, sizeof(init)); + init.name = vc5_pll_names[0]; + init.ops = &vc5_pll_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = vc5_mux_names; + init.num_parents = 1; + vc5->clk_pll.num = 0; + vc5->clk_pll.vc5 = vc5; + vc5->clk_pll.hw.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", init.name); + goto err_clk; + } + + /* Register FODs */ + for (n = 0; n < 2; n++) { + idx = vc5_map_index_to_output(vc5->model, n); + memset(&init, 0, sizeof(init)); + init.name = vc5_fod_names[idx]; + init.ops = &vc5_fod_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = vc5_pll_names; + init.num_parents = 1; + vc5->clk_fod[n].num = idx; + vc5->clk_fod[n].vc5 = vc5; + vc5->clk_fod[n].hw.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + goto err_clk; + } + } + + /* Register MUX-connected OUT0_I2C_SELB output */ + memset(&init, 0, sizeof(init)); + init.name = vc5_clk_out_names[0]; + init.ops = &vc5_clk_out_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = vc5_mux_names; + init.num_parents = 1; + vc5->clk_out[0].num = idx; + vc5->clk_out[0].vc5 = vc5; + vc5->clk_out[0].hw.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + goto err_clk; + } + + /* Register FOD-connected OUTx outputs */ + for (n = 1; n < 3; n++) { + idx = vc5_map_index_to_output(vc5->model, n - 1); + parent_names[0] = vc5_fod_names[idx]; + if (n == 1) + parent_names[1] = vc5_mux_names[0]; + else + parent_names[1] = vc5_clk_out_names[n - 1]; + + memset(&init, 0, sizeof(init)); + init.name = vc5_clk_out_names[idx + 1]; + init.ops = &vc5_clk_out_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_names; + init.num_parents = 2; + vc5->clk_out[n].num = idx; + vc5->clk_out[n].vc5 = vc5; + vc5->clk_out[n].hw.init = &init; + ret = devm_clk_hw_register(&client->dev, + &vc5->clk_out[n].hw); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + goto err_clk; + } + } + + ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); + if (ret) { + dev_err(&client->dev, "unable to add clk provider\n"); + goto err_clk; + } + + return 0; + +err_clk: + if (vc5->model == IDT_VC5_5P49V5933) + clk_unregister_fixed_rate(vc5->pin_xin); + return ret; +} + +static int vc5_remove(struct i2c_client *client) +{ + struct vc5_driver_data *vc5 = i2c_get_clientdata(client); + + of_clk_del_provider(client->dev.of_node); + + if (vc5->model == IDT_VC5_5P49V5933) + clk_unregister_fixed_rate(vc5->pin_xin); + + return 0; +} + +static const struct i2c_device_id vc5_id[] = { + { "5p49v5923", .driver_data = IDT_VC5_5P49V5923 }, + { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vc5_id); + +static const struct of_device_id clk_vc5_of_match[] = { + { .compatible = "idt,5p49v5923", .data = (void *)IDT_VC5_5P49V5923 }, + { .compatible = "idt,5p49v5933", .data = (void *)IDT_VC5_5P49V5933 }, + { }, +}; +MODULE_DEVICE_TABLE(of, clk_vc5_of_match); + +static struct i2c_driver vc5_driver = { + .driver = { + .name = "vc5", + .of_match_table = clk_vc5_of_match, + }, + .probe = vc5_probe, + .remove = vc5_remove, + .id_table = vc5_id, +}; +module_i2c_driver(vc5_driver); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("IDT VersaClock 5 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 0621fbfb4beb..a47960aacfa5 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -97,7 +97,8 @@ static int wm831x_fll_prepare(struct clk_hw *hw) if (ret != 0) dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret); - usleep_range(2000, 2000); + /* wait 2-3 ms for new frequency taking effect */ + usleep_range(2000, 3000); return ret; } diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index cbed6602172b..7098bfd32b1b 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -14,6 +14,13 @@ config COMMON_CLK_HI3519 help Build the clock driver for hi3519. +config COMMON_CLK_HI3660 + bool "Hi3660 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the clock driver for hi3660. + config COMMON_CLK_HI3798CV200 tristate "Hi3798CV200 Clock Driver" depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 4eec5e511e4c..1e4c3ddbad84 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o obj-$(CONFIG_COMMON_CLK_HI3516CV300) += crg-hi3516cv300.o obj-$(CONFIG_COMMON_CLK_HI3519) += clk-hi3519.o +obj-$(CONFIG_COMMON_CLK_HI3660) += clk-hi3660.o obj-$(CONFIG_COMMON_CLK_HI3798CV200) += crg-hi3798cv200.o obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o obj-$(CONFIG_RESET_HISI) += reset.o diff --git a/drivers/clk/hisilicon/clk-hi3660.c b/drivers/clk/hisilicon/clk-hi3660.c new file mode 100644 index 000000000000..96a9697b06cf --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi3660.c @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <dt-bindings/clock/hi3660-clock.h> +#include <linux/clk-provider.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include "clk.h" + +static const struct hisi_fixed_rate_clock hi3660_fixed_rate_clks[] = { + { HI3660_CLKIN_SYS, "clkin_sys", NULL, 0, 19200000, }, + { HI3660_CLKIN_REF, "clkin_ref", NULL, 0, 32764, }, + { HI3660_CLK_FLL_SRC, "clk_fll_src", NULL, 0, 128000000, }, + { HI3660_CLK_PPLL0, "clk_ppll0", NULL, 0, 1600000000, }, + { HI3660_CLK_PPLL1, "clk_ppll1", NULL, 0, 1866000000, }, + { HI3660_CLK_PPLL2, "clk_ppll2", NULL, 0, 960000000, }, + { HI3660_CLK_PPLL3, "clk_ppll3", NULL, 0, 1290000000, }, + { HI3660_CLK_SCPLL, "clk_scpll", NULL, 0, 245760000, }, + { HI3660_PCLK, "pclk", NULL, 0, 20000000, }, + { HI3660_CLK_UART0_DBG, "clk_uart0_dbg", NULL, 0, 19200000, }, + { HI3660_CLK_UART6, "clk_uart6", NULL, 0, 19200000, }, + { HI3660_OSC32K, "osc32k", NULL, 0, 32764, }, + { HI3660_OSC19M, "osc19m", NULL, 0, 19200000, }, + { HI3660_CLK_480M, "clk_480m", NULL, 0, 480000000, }, + { HI3660_CLK_INV, "clk_inv", NULL, 0, 10000000, }, +}; + +/* crgctrl */ +static const struct hisi_fixed_factor_clock hi3660_crg_fixed_factor_clks[] = { + { HI3660_FACTOR_UART3, "clk_factor_uart3", "iomcu_peri0", 1, 8, 0, }, + { HI3660_CLK_FACTOR_MMC, "clk_factor_mmc", "clkin_sys", 1, 6, 0, }, + { HI3660_CLK_GATE_I2C0, "clk_gate_i2c0", "clk_i2c0_iomcu", 1, 4, 0, }, + { HI3660_CLK_GATE_I2C1, "clk_gate_i2c1", "clk_i2c1_iomcu", 1, 4, 0, }, + { HI3660_CLK_GATE_I2C2, "clk_gate_i2c2", "clk_i2c2_iomcu", 1, 4, 0, }, + { HI3660_CLK_GATE_I2C6, "clk_gate_i2c6", "clk_i2c6_iomcu", 1, 4, 0, }, + { HI3660_CLK_DIV_SYSBUS, "clk_div_sysbus", "clk_mux_sysbus", 1, 7, 0, }, + { HI3660_CLK_DIV_320M, "clk_div_320m", "clk_320m_pll_gt", 1, 5, 0, }, + { HI3660_CLK_DIV_A53, "clk_div_a53hpm", "clk_a53hpm_andgt", 1, 2, 0, }, + { HI3660_CLK_GATE_SPI0, "clk_gate_spi0", "clk_ppll0", 1, 8, 0, }, + { HI3660_CLK_GATE_SPI2, "clk_gate_spi2", "clk_ppll0", 1, 8, 0, }, + { HI3660_PCIEPHY_REF, "clk_pciephy_ref", "clk_div_pciephy", 1, 1, 0, }, + { HI3660_CLK_ABB_USB, "clk_abb_usb", "clk_gate_usb_tcxo_en", 1, 1, 0 }, +}; + +static const struct hisi_gate_clock hi3660_crgctrl_gate_sep_clks[] = { + { HI3660_HCLK_GATE_SDIO0, "hclk_gate_sdio0", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x0, 21, 0, }, + { HI3660_HCLK_GATE_SD, "hclk_gate_sd", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x0, 30, 0, }, + { HI3660_CLK_GATE_AOMM, "clk_gate_aomm", "clk_div_aomm", + CLK_SET_RATE_PARENT, 0x0, 31, 0, }, + { HI3660_PCLK_GPIO0, "pclk_gpio0", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 0, 0, }, + { HI3660_PCLK_GPIO1, "pclk_gpio1", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 1, 0, }, + { HI3660_PCLK_GPIO2, "pclk_gpio2", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 2, 0, }, + { HI3660_PCLK_GPIO3, "pclk_gpio3", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 3, 0, }, + { HI3660_PCLK_GPIO4, "pclk_gpio4", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 4, 0, }, + { HI3660_PCLK_GPIO5, "pclk_gpio5", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 5, 0, }, + { HI3660_PCLK_GPIO6, "pclk_gpio6", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 6, 0, }, + { HI3660_PCLK_GPIO7, "pclk_gpio7", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 7, 0, }, + { HI3660_PCLK_GPIO8, "pclk_gpio8", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 8, 0, }, + { HI3660_PCLK_GPIO9, "pclk_gpio9", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 9, 0, }, + { HI3660_PCLK_GPIO10, "pclk_gpio10", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 10, 0, }, + { HI3660_PCLK_GPIO11, "pclk_gpio11", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 11, 0, }, + { HI3660_PCLK_GPIO12, "pclk_gpio12", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 12, 0, }, + { HI3660_PCLK_GPIO13, "pclk_gpio13", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 13, 0, }, + { HI3660_PCLK_GPIO14, "pclk_gpio14", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 14, 0, }, + { HI3660_PCLK_GPIO15, "pclk_gpio15", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 15, 0, }, + { HI3660_PCLK_GPIO16, "pclk_gpio16", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 16, 0, }, + { HI3660_PCLK_GPIO17, "pclk_gpio17", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 17, 0, }, + { HI3660_PCLK_GPIO18, "pclk_gpio18", "clk_div_ioperi", + CLK_SET_RATE_PARENT, 0x10, 18, 0, }, + { HI3660_PCLK_GPIO19, "pclk_gpio19", "clk_div_ioperi", + CLK_SET_RATE_PARENT, 0x10, 19, 0, }, + { HI3660_PCLK_GPIO20, "pclk_gpio20", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 20, 0, }, + { HI3660_PCLK_GPIO21, "pclk_gpio21", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x10, 21, 0, }, + { HI3660_CLK_GATE_SPI3, "clk_gate_spi3", "clk_div_ioperi", + CLK_SET_RATE_PARENT, 0x10, 30, 0, }, + { HI3660_CLK_GATE_I2C7, "clk_gate_i2c7", "clk_mux_i2c", + CLK_SET_RATE_PARENT, 0x10, 31, 0, }, + { HI3660_CLK_GATE_I2C3, "clk_gate_i2c3", "clk_mux_i2c", + CLK_SET_RATE_PARENT, 0x20, 7, 0, }, + { HI3660_CLK_GATE_SPI1, "clk_gate_spi1", "clk_mux_spi", + CLK_SET_RATE_PARENT, 0x20, 9, 0, }, + { HI3660_CLK_GATE_UART1, "clk_gate_uart1", "clk_mux_uarth", + CLK_SET_RATE_PARENT, 0x20, 11, 0, }, + { HI3660_CLK_GATE_UART2, "clk_gate_uart2", "clk_mux_uart1", + CLK_SET_RATE_PARENT, 0x20, 12, 0, }, + { HI3660_CLK_GATE_UART4, "clk_gate_uart4", "clk_mux_uarth", + CLK_SET_RATE_PARENT, 0x20, 14, 0, }, + { HI3660_CLK_GATE_UART5, "clk_gate_uart5", "clk_mux_uart1", + CLK_SET_RATE_PARENT, 0x20, 15, 0, }, + { HI3660_CLK_GATE_I2C4, "clk_gate_i2c4", "clk_mux_i2c", + CLK_SET_RATE_PARENT, 0x20, 27, 0, }, + { HI3660_CLK_GATE_DMAC, "clk_gate_dmac", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x30, 1, 0, }, + { HI3660_PCLK_GATE_DSS, "pclk_gate_dss", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x30, 12, 0, }, + { HI3660_ACLK_GATE_DSS, "aclk_gate_dss", "clk_gate_vivobus", + CLK_SET_RATE_PARENT, 0x30, 13, 0, }, + { HI3660_CLK_GATE_LDI1, "clk_gate_ldi1", "clk_div_ldi1", + CLK_SET_RATE_PARENT, 0x30, 14, 0, }, + { HI3660_CLK_GATE_LDI0, "clk_gate_ldi0", "clk_div_ldi0", + CLK_SET_RATE_PARENT, 0x30, 15, 0, }, + { HI3660_CLK_GATE_VIVOBUS, "clk_gate_vivobus", "clk_div_vivobus", + CLK_SET_RATE_PARENT, 0x30, 16, 0, }, + { HI3660_CLK_GATE_EDC0, "clk_gate_edc0", "clk_div_edc0", + CLK_SET_RATE_PARENT, 0x30, 17, 0, }, + { HI3660_CLK_GATE_TXDPHY0_CFG, "clk_gate_txdphy0_cfg", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 28, 0, }, + { HI3660_CLK_GATE_TXDPHY0_REF, "clk_gate_txdphy0_ref", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 29, 0, }, + { HI3660_CLK_GATE_TXDPHY1_CFG, "clk_gate_txdphy1_cfg", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 30, 0, }, + { HI3660_CLK_GATE_TXDPHY1_REF, "clk_gate_txdphy1_ref", "clkin_sys", + CLK_SET_RATE_PARENT, 0x30, 31, 0, }, + { HI3660_ACLK_GATE_USB3OTG, "aclk_gate_usb3otg", "clk_div_mmc0bus", + CLK_SET_RATE_PARENT, 0x40, 1, 0, }, + { HI3660_CLK_GATE_SPI4, "clk_gate_spi4", "clk_mux_spi", + CLK_SET_RATE_PARENT, 0x40, 4, 0, }, + { HI3660_CLK_GATE_SD, "clk_gate_sd", "clk_mux_sd_sys", + CLK_SET_RATE_PARENT, 0x40, 17, 0, }, + { HI3660_CLK_GATE_SDIO0, "clk_gate_sdio0", "clk_mux_sdio_sys", + CLK_SET_RATE_PARENT, 0x40, 19, 0, }, + { HI3660_CLK_GATE_UFS_SUBSYS, "clk_gate_ufs_subsys", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x50, 21, 0, }, + { HI3660_PCLK_GATE_DSI0, "pclk_gate_dsi0", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x50, 28, 0, }, + { HI3660_PCLK_GATE_DSI1, "pclk_gate_dsi1", "clk_div_cfgbus", + CLK_SET_RATE_PARENT, 0x50, 29, 0, }, + { HI3660_ACLK_GATE_PCIE, "aclk_gate_pcie", "clk_div_mmc1bus", + CLK_SET_RATE_PARENT, 0x420, 5, 0, }, + { HI3660_PCLK_GATE_PCIE_SYS, "pclk_gate_pcie_sys", "clk_div_mmc1bus", + CLK_SET_RATE_PARENT, 0x420, 7, 0, }, + { HI3660_CLK_GATE_PCIEAUX, "clk_gate_pcieaux", "clkin_sys", + CLK_SET_RATE_PARENT, 0x420, 8, 0, }, + { HI3660_PCLK_GATE_PCIE_PHY, "pclk_gate_pcie_phy", "clk_div_mmc1bus", + CLK_SET_RATE_PARENT, 0x420, 9, 0, }, +}; + +static const struct hisi_gate_clock hi3660_crgctrl_gate_clks[] = { + { HI3660_CLK_ANDGT_LDI0, "clk_andgt_ldi0", "clk_mux_ldi0", + CLK_SET_RATE_PARENT, 0xf0, 6, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_LDI1, "clk_andgt_ldi1", "clk_mux_ldi1", + CLK_SET_RATE_PARENT, 0xf0, 7, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_EDC0, "clk_andgt_edc0", "clk_mux_edc0", + CLK_SET_RATE_PARENT, 0xf0, 8, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_GATE_UFSPHY_GT, "clk_gate_ufsphy_gt", "clk_div_ufsperi", + CLK_SET_RATE_PARENT, 0xf4, 1, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_MMC, "clk_andgt_mmc", "clk_mux_mmc_pll", + CLK_SET_RATE_PARENT, 0xf4, 2, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_SD, "clk_andgt_sd", "clk_mux_sd_pll", + CLK_SET_RATE_PARENT, 0xf4, 3, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_A53HPM_ANDGT, "clk_a53hpm_andgt", "clk_mux_a53hpm", + CLK_SET_RATE_PARENT, 0xf4, 7, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_SDIO, "clk_andgt_sdio", "clk_mux_sdio_pll", + CLK_SET_RATE_PARENT, 0xf4, 8, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_UART0, "clk_andgt_uart0", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 9, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_UART1, "clk_andgt_uart1", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 10, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_UARTH, "clk_andgt_uarth", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 11, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_ANDGT_SPI, "clk_andgt_spi", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xf4, 13, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_VIVOBUS_ANDGT, "clk_vivobus_andgt", "clk_mux_vivobus", + CLK_SET_RATE_PARENT, 0xf8, 1, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_AOMM_ANDGT, "clk_aomm_andgt", "clk_ppll2", + CLK_SET_RATE_PARENT, 0xf8, 3, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_320M_PLL_GT, "clk_320m_pll_gt", "clk_mux_320m", + CLK_SET_RATE_PARENT, 0xf8, 10, 0, }, + { HI3660_AUTODIV_EMMC0BUS, "autodiv_emmc0bus", "autodiv_sysbus", + CLK_SET_RATE_PARENT, 0x404, 1, CLK_GATE_HIWORD_MASK, }, + { HI3660_AUTODIV_SYSBUS, "autodiv_sysbus", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0x404, 5, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_GATE_UFSPHY_CFG, "clk_gate_ufsphy_cfg", + "clk_div_ufsphy_cfg", CLK_SET_RATE_PARENT, 0x420, 12, 0, }, + { HI3660_CLK_GATE_UFSIO_REF, "clk_gate_ufsio_ref", + "clk_gate_ufs_tcxo_en", CLK_SET_RATE_PARENT, 0x420, 14, 0, }, +}; + +static const char *const +clk_mux_sdio_sys_p[] = {"clk_factor_mmc", "clk_div_sdio",}; +static const char *const +clk_mux_sd_sys_p[] = {"clk_factor_mmc", "clk_div_sd",}; +static const char *const +clk_mux_pll_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll2",}; +static const char *const +clk_mux_pll0123_p[] = {"clk_ppll0", "clk_ppll1", "clk_ppll2", "clk_ppll3",}; +static const char *const +clk_mux_edc0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll1", "clk_inv", + "clk_ppll2", "clk_inv", "clk_inv", "clk_inv", + "clk_ppll3", "clk_inv", "clk_inv", "clk_inv", + "clk_inv", "clk_inv", "clk_inv", "clk_inv",}; +static const char *const +clk_mux_ldi0_p[] = {"clk_inv", "clk_ppll0", "clk_ppll2", "clk_inv", + "clk_ppll1", "clk_inv", "clk_inv", "clk_inv", + "clk_ppll3", "clk_inv", "clk_inv", "clk_inv", + "clk_inv", "clk_inv", "clk_inv", "clk_inv",}; +static const char *const +clk_mux_uart0_p[] = {"clkin_sys", "clk_div_uart0",}; +static const char *const +clk_mux_uart1_p[] = {"clkin_sys", "clk_div_uart1",}; +static const char *const +clk_mux_uarth_p[] = {"clkin_sys", "clk_div_uarth",}; +static const char *const +clk_mux_pll02p[] = {"clk_ppll0", "clk_ppll2",}; +static const char *const +clk_mux_ioperi_p[] = {"clk_div_320m", "clk_div_a53hpm",}; +static const char *const +clk_mux_spi_p[] = {"clkin_sys", "clk_div_spi",}; +static const char *const +clk_mux_i2c_p[] = {"clkin_sys", "clk_div_i2c",}; + +static const struct hisi_mux_clock hi3660_crgctrl_mux_clks[] = { + { HI3660_CLK_MUX_SYSBUS, "clk_mux_sysbus", clk_mux_sdio_sys_p, + ARRAY_SIZE(clk_mux_sdio_sys_p), CLK_SET_RATE_PARENT, 0xac, 0, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_UART0, "clk_mux_uart0", clk_mux_uart0_p, + ARRAY_SIZE(clk_mux_uart0_p), CLK_SET_RATE_PARENT, 0xac, 2, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_UART1, "clk_mux_uart1", clk_mux_uart1_p, + ARRAY_SIZE(clk_mux_uart1_p), CLK_SET_RATE_PARENT, 0xac, 3, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_UARTH, "clk_mux_uarth", clk_mux_uarth_p, + ARRAY_SIZE(clk_mux_uarth_p), CLK_SET_RATE_PARENT, 0xac, 4, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SPI, "clk_mux_spi", clk_mux_spi_p, + ARRAY_SIZE(clk_mux_spi_p), CLK_SET_RATE_PARENT, 0xac, 8, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_I2C, "clk_mux_i2c", clk_mux_i2c_p, + ARRAY_SIZE(clk_mux_i2c_p), CLK_SET_RATE_PARENT, 0xac, 13, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_MMC_PLL, "clk_mux_mmc_pll", clk_mux_pll02p, + ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xb4, 0, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_LDI1, "clk_mux_ldi1", clk_mux_ldi0_p, + ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 8, 4, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_LDI0, "clk_mux_ldi0", clk_mux_ldi0_p, + ARRAY_SIZE(clk_mux_ldi0_p), CLK_SET_RATE_PARENT, 0xb4, 12, 4, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SD_PLL, "clk_mux_sd_pll", clk_mux_pll_p, + ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xb8, 4, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SD_SYS, "clk_mux_sd_sys", clk_mux_sd_sys_p, + ARRAY_SIZE(clk_mux_sd_sys_p), CLK_SET_RATE_PARENT, 0xb8, 6, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_EDC0, "clk_mux_edc0", clk_mux_edc0_p, + ARRAY_SIZE(clk_mux_edc0_p), CLK_SET_RATE_PARENT, 0xbc, 6, 4, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SDIO_SYS, "clk_mux_sdio_sys", clk_mux_sdio_sys_p, + ARRAY_SIZE(clk_mux_sdio_sys_p), CLK_SET_RATE_PARENT, 0xc0, 6, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_SDIO_PLL, "clk_mux_sdio_pll", clk_mux_pll_p, + ARRAY_SIZE(clk_mux_pll_p), CLK_SET_RATE_PARENT, 0xc0, 4, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_VIVOBUS, "clk_mux_vivobus", clk_mux_pll0123_p, + ARRAY_SIZE(clk_mux_pll0123_p), CLK_SET_RATE_PARENT, 0xd0, 12, 2, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_A53HPM, "clk_mux_a53hpm", clk_mux_pll02p, + ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0xd4, 9, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_320M, "clk_mux_320m", clk_mux_pll02p, + ARRAY_SIZE(clk_mux_pll02p), CLK_SET_RATE_PARENT, 0x100, 0, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_MUX_IOPERI, "clk_mux_ioperi", clk_mux_ioperi_p, + ARRAY_SIZE(clk_mux_ioperi_p), CLK_SET_RATE_PARENT, 0x108, 10, 1, + CLK_MUX_HIWORD_MASK, }, +}; + +static const struct hisi_divider_clock hi3660_crgctrl_divider_clks[] = { + { HI3660_CLK_DIV_UART0, "clk_div_uart0", "clk_andgt_uart0", + CLK_SET_RATE_PARENT, 0xb0, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UART1, "clk_div_uart1", "clk_andgt_uart1", + CLK_SET_RATE_PARENT, 0xb0, 8, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UARTH, "clk_div_uarth", "clk_andgt_uarth", + CLK_SET_RATE_PARENT, 0xb0, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_MMC, "clk_div_mmc", "clk_andgt_mmc", + CLK_SET_RATE_PARENT, 0xb4, 3, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_SD, "clk_div_sd", "clk_andgt_sd", + CLK_SET_RATE_PARENT, 0xb8, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_EDC0, "clk_div_edc0", "clk_andgt_edc0", + CLK_SET_RATE_PARENT, 0xbc, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_LDI0, "clk_div_ldi0", "clk_andgt_ldi0", + CLK_SET_RATE_PARENT, 0xbc, 10, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_SDIO, "clk_div_sdio", "clk_andgt_sdio", + CLK_SET_RATE_PARENT, 0xc0, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_LDI1, "clk_div_ldi1", "clk_andgt_ldi1", + CLK_SET_RATE_PARENT, 0xc0, 8, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_SPI, "clk_div_spi", "clk_andgt_spi", + CLK_SET_RATE_PARENT, 0xc4, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_VIVOBUS, "clk_div_vivobus", "clk_vivobus_andgt", + CLK_SET_RATE_PARENT, 0xd0, 7, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_I2C, "clk_div_i2c", "clk_div_320m", + CLK_SET_RATE_PARENT, 0xe8, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UFSPHY, "clk_div_ufsphy_cfg", "clk_gate_ufsphy_gt", + CLK_SET_RATE_PARENT, 0xe8, 9, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_CFGBUS, "clk_div_cfgbus", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0xec, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_MMC0BUS, "clk_div_mmc0bus", "autodiv_emmc0bus", + CLK_SET_RATE_PARENT, 0xec, 2, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_MMC1BUS, "clk_div_mmc1bus", "clk_div_sysbus", + CLK_SET_RATE_PARENT, 0xec, 3, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_UFSPERI, "clk_div_ufsperi", "clk_gate_ufs_subsys", + CLK_SET_RATE_PARENT, 0xec, 14, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_AOMM, "clk_div_aomm", "clk_aomm_andgt", + CLK_SET_RATE_PARENT, 0x100, 7, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_IOPERI, "clk_div_ioperi", "clk_mux_ioperi", + CLK_SET_RATE_PARENT, 0x108, 11, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, +}; + +/* clk_pmuctrl */ +/* pmu register need shift 2 bits */ +static const struct hisi_gate_clock hi3660_pmu_gate_clks[] = { + { HI3660_GATE_ABB_192, "clk_gate_abb_192", "clkin_sys", + CLK_SET_RATE_PARENT, (0x10a << 2), 3, 0, }, +}; + +/* clk_pctrl */ +static const struct hisi_gate_clock hi3660_pctrl_gate_clks[] = { + { HI3660_GATE_UFS_TCXO_EN, "clk_gate_ufs_tcxo_en", + "clk_gate_abb_192", CLK_SET_RATE_PARENT, 0x10, 0, + CLK_GATE_HIWORD_MASK, }, + { HI3660_GATE_USB_TCXO_EN, "clk_gate_usb_tcxo_en", "clk_gate_abb_192", + CLK_SET_RATE_PARENT, 0x10, 1, CLK_GATE_HIWORD_MASK, }, +}; + +/* clk_sctrl */ +static const struct hisi_gate_clock hi3660_sctrl_gate_sep_clks[] = { + { HI3660_PCLK_AO_GPIO0, "pclk_ao_gpio0", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 11, 0, }, + { HI3660_PCLK_AO_GPIO1, "pclk_ao_gpio1", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 12, 0, }, + { HI3660_PCLK_AO_GPIO2, "pclk_ao_gpio2", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 13, 0, }, + { HI3660_PCLK_AO_GPIO3, "pclk_ao_gpio3", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 14, 0, }, + { HI3660_PCLK_AO_GPIO4, "pclk_ao_gpio4", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 21, 0, }, + { HI3660_PCLK_AO_GPIO5, "pclk_ao_gpio5", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 22, 0, }, + { HI3660_PCLK_AO_GPIO6, "pclk_ao_gpio6", "clk_div_aobus", + CLK_SET_RATE_PARENT, 0x160, 25, 0, }, + { HI3660_PCLK_GATE_MMBUF, "pclk_gate_mmbuf", "pclk_div_mmbuf", + CLK_SET_RATE_PARENT, 0x170, 23, 0, }, + { HI3660_CLK_GATE_DSS_AXI_MM, "clk_gate_dss_axi_mm", "aclk_mux_mmbuf", + CLK_SET_RATE_PARENT, 0x170, 24, 0, }, +}; + +static const struct hisi_gate_clock hi3660_sctrl_gate_clks[] = { + { HI3660_PCLK_MMBUF_ANDGT, "pclk_mmbuf_andgt", "clk_sw_mmbuf", + CLK_SET_RATE_PARENT, 0x258, 7, CLK_GATE_HIWORD_MASK, }, + { HI3660_CLK_MMBUF_PLL_ANDGT, "clk_mmbuf_pll_andgt", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x260, 11, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_FLL_MMBUF_ANDGT, "clk_fll_mmbuf_andgt", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x260, 12, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_SYS_MMBUF_ANDGT, "clk_sys_mmbuf_andgt", "clkin_sys", + CLK_SET_RATE_PARENT, 0x260, 13, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_GATE_PCIEPHY_GT, "clk_gate_pciephy_gt", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x268, 11, CLK_DIVIDER_HIWORD_MASK, 0, }, +}; + +static const char *const +aclk_mux_mmbuf_p[] = {"aclk_div_mmbuf", "clk_gate_aomm",}; +static const char *const +clk_sw_mmbuf_p[] = {"clk_sys_mmbuf_andgt", "clk_fll_mmbuf_andgt", + "aclk_mux_mmbuf", "aclk_mux_mmbuf"}; + +static const struct hisi_mux_clock hi3660_sctrl_mux_clks[] = { + { HI3660_ACLK_MUX_MMBUF, "aclk_mux_mmbuf", aclk_mux_mmbuf_p, + ARRAY_SIZE(aclk_mux_mmbuf_p), CLK_SET_RATE_PARENT, 0x250, 12, 1, + CLK_MUX_HIWORD_MASK, }, + { HI3660_CLK_SW_MMBUF, "clk_sw_mmbuf", clk_sw_mmbuf_p, + ARRAY_SIZE(clk_sw_mmbuf_p), CLK_SET_RATE_PARENT, 0x258, 8, 2, + CLK_MUX_HIWORD_MASK, }, +}; + +static const struct hisi_divider_clock hi3660_sctrl_divider_clks[] = { + { HI3660_CLK_DIV_AOBUS, "clk_div_aobus", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_PCLK_DIV_MMBUF, "pclk_div_mmbuf", "pclk_mmbuf_andgt", + CLK_SET_RATE_PARENT, 0x258, 10, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_ACLK_DIV_MMBUF, "aclk_div_mmbuf", "clk_mmbuf_pll_andgt", + CLK_SET_RATE_PARENT, 0x258, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + { HI3660_CLK_DIV_PCIEPHY, "clk_div_pciephy", "clk_gate_pciephy_gt", + CLK_SET_RATE_PARENT, 0x268, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, +}; + +/* clk_iomcu */ +static const struct hisi_gate_clock hi3660_iomcu_gate_sep_clks[] = { + { HI3660_CLK_I2C0_IOMCU, "clk_i2c0_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 3, 0, }, + { HI3660_CLK_I2C1_IOMCU, "clk_i2c1_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 4, 0, }, + { HI3660_CLK_I2C2_IOMCU, "clk_i2c2_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 5, 0, }, + { HI3660_CLK_I2C6_IOMCU, "clk_i2c6_iomcu", "clk_fll_src", + CLK_SET_RATE_PARENT, 0x10, 27, 0, }, + { HI3660_CLK_IOMCU_PERI0, "iomcu_peri0", "clk_ppll0", + CLK_SET_RATE_PARENT, 0x90, 0, 0, }, +}; + +static void hi3660_clk_iomcu_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_iomcu_gate_sep_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi3660_iomcu_gate_sep_clks, + ARRAY_SIZE(hi3660_iomcu_gate_sep_clks), + clk_data); +} + +static void hi3660_clk_pmuctrl_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_pmu_gate_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + + hisi_clk_register_gate(hi3660_pmu_gate_clks, + ARRAY_SIZE(hi3660_pmu_gate_clks), clk_data); +} + +static void hi3660_clk_pctrl_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_pctrl_gate_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + hisi_clk_register_gate(hi3660_pctrl_gate_clks, + ARRAY_SIZE(hi3660_pctrl_gate_clks), clk_data); +} + +static void hi3660_clk_sctrl_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_sctrl_gate_clks) + + ARRAY_SIZE(hi3660_sctrl_gate_sep_clks) + + ARRAY_SIZE(hi3660_sctrl_mux_clks) + + ARRAY_SIZE(hi3660_sctrl_divider_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + hisi_clk_register_gate(hi3660_sctrl_gate_clks, + ARRAY_SIZE(hi3660_sctrl_gate_clks), clk_data); + hisi_clk_register_gate_sep(hi3660_sctrl_gate_sep_clks, + ARRAY_SIZE(hi3660_sctrl_gate_sep_clks), + clk_data); + hisi_clk_register_mux(hi3660_sctrl_mux_clks, + ARRAY_SIZE(hi3660_sctrl_mux_clks), clk_data); + hisi_clk_register_divider(hi3660_sctrl_divider_clks, + ARRAY_SIZE(hi3660_sctrl_divider_clks), + clk_data); +} + +static void hi3660_clk_crgctrl_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + int nr = ARRAY_SIZE(hi3660_fixed_rate_clks) + + ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks) + + ARRAY_SIZE(hi3660_crgctrl_gate_clks) + + ARRAY_SIZE(hi3660_crgctrl_mux_clks) + + ARRAY_SIZE(hi3660_crg_fixed_factor_clks) + + ARRAY_SIZE(hi3660_crgctrl_divider_clks); + + clk_data = hisi_clk_init(np, nr); + if (!clk_data) + return; + + hisi_clk_register_fixed_rate(hi3660_fixed_rate_clks, + ARRAY_SIZE(hi3660_fixed_rate_clks), + clk_data); + hisi_clk_register_gate_sep(hi3660_crgctrl_gate_sep_clks, + ARRAY_SIZE(hi3660_crgctrl_gate_sep_clks), + clk_data); + hisi_clk_register_gate(hi3660_crgctrl_gate_clks, + ARRAY_SIZE(hi3660_crgctrl_gate_clks), + clk_data); + hisi_clk_register_mux(hi3660_crgctrl_mux_clks, + ARRAY_SIZE(hi3660_crgctrl_mux_clks), + clk_data); + hisi_clk_register_fixed_factor(hi3660_crg_fixed_factor_clks, + ARRAY_SIZE(hi3660_crg_fixed_factor_clks), + clk_data); + hisi_clk_register_divider(hi3660_crgctrl_divider_clks, + ARRAY_SIZE(hi3660_crgctrl_divider_clks), + clk_data); +} + +static const struct of_device_id hi3660_clk_match_table[] = { + { .compatible = "hisilicon,hi3660-crgctrl", + .data = hi3660_clk_crgctrl_init }, + { .compatible = "hisilicon,hi3660-pctrl", + .data = hi3660_clk_pctrl_init }, + { .compatible = "hisilicon,hi3660-pmuctrl", + .data = hi3660_clk_pmuctrl_init }, + { .compatible = "hisilicon,hi3660-sctrl", + .data = hi3660_clk_sctrl_init }, + { .compatible = "hisilicon,hi3660-iomcu", + .data = hi3660_clk_iomcu_init }, + { } +}; + +static int hi3660_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + void (*init_func)(struct device_node *np); + + init_func = of_device_get_match_data(dev); + if (!init_func) + return -ENODEV; + + init_func(np); + + return 0; +} + +static struct platform_driver hi3660_clk_driver = { + .probe = hi3660_clk_probe, + .driver = { + .name = "hi3660-clk", + .of_match_table = hi3660_clk_match_table, + }, +}; + +static int __init hi3660_clk_init(void) +{ + return platform_driver_register(&hi3660_clk_driver); +} +core_initcall(hi3660_clk_init); diff --git a/drivers/clk/hisilicon/clkgate-separated.c b/drivers/clk/hisilicon/clkgate-separated.c index a47812f56a17..7908bc3c9ec7 100644 --- a/drivers/clk/hisilicon/clkgate-separated.c +++ b/drivers/clk/hisilicon/clkgate-separated.c @@ -120,6 +120,7 @@ struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name, sclk->bit_idx = bit_idx; sclk->flags = clk_gate_flags; sclk->hw.init = &init; + sclk->lock = lock; clk = clk_register(dev, &sclk->hw); if (IS_ERR(clk)) diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 42ffc1c92bab..c07df719b8a3 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -592,15 +592,20 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) imx6q_mmdc_ch1_mask_handshake(base); - /* - * The LDB_DI0/1_SEL muxes are registered read-only due to a hardware - * bug. Set the muxes to the requested values before registering the - * ldb_di_sel clocks. - */ - init_ldb_clks(np, base); + if (clk_on_imx6qp()) { + clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + } else { + /* + * The LDB_DI0/1_SEL muxes are registered read-only due to a hardware + * bug. Set the muxes to the requested values before registering the + * ldb_di_sel clocks. + */ + init_ldb_clks(np, base); - clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); - clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); + clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); + clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); + } clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index e7c7353a86fc..ae1d31be906e 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -803,6 +803,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_DRAM_PHYM_ROOT_CLK] = imx_clk_gate4("dram_phym_root_clk", "dram_phym_cg", base + 0x4130, 0); clks[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_gate4("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0); clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0); + clks[IMX7D_OCOTP_CLK] = imx_clk_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0); clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4420, 0); clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0); clks[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0); diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index ed3a2df536ea..f1099167ba31 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -21,6 +21,9 @@ #define PLL_NUM_OFFSET 0x10 #define PLL_DENOM_OFFSET 0x20 +#define PLL_VF610_NUM_OFFSET 0x20 +#define PLL_VF610_DENOM_OFFSET 0x30 + #define BM_PLL_POWER (0x1 << 12) #define BM_PLL_LOCK (0x1 << 31) #define IMX7_ENET_PLL_POWER (0x1 << 5) @@ -300,6 +303,99 @@ static const struct clk_ops clk_pllv3_av_ops = { .set_rate = clk_pllv3_av_set_rate, }; +struct clk_pllv3_vf610_mf { + u32 mfi; /* integer part, can be 20 or 22 */ + u32 mfn; /* numerator, 30-bit value */ + u32 mfd; /* denominator, 30-bit value, must be less than mfn */ +}; + +static unsigned long clk_pllv3_vf610_mf_to_rate(unsigned long parent_rate, + struct clk_pllv3_vf610_mf mf) +{ + u64 temp64; + + temp64 = parent_rate; + temp64 *= mf.mfn; + do_div(temp64, mf.mfd); + + return (parent_rate * mf.mfi) + temp64; +} + +static struct clk_pllv3_vf610_mf clk_pllv3_vf610_rate_to_mf( + unsigned long parent_rate, unsigned long rate) +{ + struct clk_pllv3_vf610_mf mf; + u64 temp64; + + mf.mfi = (rate >= 22 * parent_rate) ? 22 : 20; + mf.mfd = 0x3fffffff; /* use max supported value for best accuracy */ + + if (rate <= parent_rate * mf.mfi) + mf.mfn = 0; + else if (rate >= parent_rate * (mf.mfi + 1)) + mf.mfn = mf.mfd - 1; + else { + /* rate = parent_rate * (mfi + mfn/mfd) */ + temp64 = rate - parent_rate * mf.mfi; + temp64 *= mf.mfd; + do_div(temp64, parent_rate); + mf.mfn = temp64; + } + + return mf; +} + +static unsigned long clk_pllv3_vf610_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + struct clk_pllv3_vf610_mf mf; + + mf.mfn = readl_relaxed(pll->base + PLL_VF610_NUM_OFFSET); + mf.mfd = readl_relaxed(pll->base + PLL_VF610_DENOM_OFFSET); + mf.mfi = (readl_relaxed(pll->base) & pll->div_mask) ? 22 : 20; + + return clk_pllv3_vf610_mf_to_rate(parent_rate, mf); +} + +static long clk_pllv3_vf610_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_pllv3_vf610_mf mf = clk_pllv3_vf610_rate_to_mf(*prate, rate); + + return clk_pllv3_vf610_mf_to_rate(*prate, mf); +} + +static int clk_pllv3_vf610_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + struct clk_pllv3_vf610_mf mf = + clk_pllv3_vf610_rate_to_mf(parent_rate, rate); + u32 val; + + val = readl_relaxed(pll->base); + if (mf.mfi == 20) + val &= ~pll->div_mask; /* clear bit for mfi=20 */ + else + val |= pll->div_mask; /* set bit for mfi=22 */ + writel_relaxed(val, pll->base); + + writel_relaxed(mf.mfn, pll->base + PLL_VF610_NUM_OFFSET); + writel_relaxed(mf.mfd, pll->base + PLL_VF610_DENOM_OFFSET); + + return clk_pllv3_wait_lock(pll); +} + +static const struct clk_ops clk_pllv3_vf610_ops = { + .prepare = clk_pllv3_prepare, + .unprepare = clk_pllv3_unprepare, + .is_prepared = clk_pllv3_is_prepared, + .recalc_rate = clk_pllv3_vf610_recalc_rate, + .round_rate = clk_pllv3_vf610_round_rate, + .set_rate = clk_pllv3_vf610_set_rate, +}; + static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -334,6 +430,9 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, case IMX_PLLV3_SYS: ops = &clk_pllv3_sys_ops; break; + case IMX_PLLV3_SYS_VF610: + ops = &clk_pllv3_vf610_ops; + break; case IMX_PLLV3_USB_VF610: pll->div_shift = 1; case IMX_PLLV3_USB: diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 0476353ab423..59b1863deb88 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -219,8 +219,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); - clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1); - clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1); + clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1); + clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1); clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x2); clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f); clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 4afad3b96a61..e1f5e425db73 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -34,6 +34,7 @@ enum imx_pllv3_type { IMX_PLLV3_AV, IMX_PLLV3_ENET, IMX_PLLV3_ENET_IMX7, + IMX_PLLV3_SYS_VF610, }; struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index 0bd631a41f6a..a01ef7806aed 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -8,52 +8,53 @@ config COMMON_CLK_MEDIATEK config COMMON_CLK_MT2701 bool "Clock driver for Mediatek MT2701" + depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST select COMMON_CLK_MEDIATEK - default ARCH_MEDIATEK + default ARCH_MEDIATEK && ARM ---help--- This driver supports Mediatek MT2701 basic clocks. config COMMON_CLK_MT2701_MMSYS bool "Clock driver for Mediatek MT2701 mmsys" - select COMMON_CLK_MT2701 + depends on COMMON_CLK_MT2701 ---help--- This driver supports Mediatek MT2701 mmsys clocks. config COMMON_CLK_MT2701_IMGSYS bool "Clock driver for Mediatek MT2701 imgsys" - select COMMON_CLK_MT2701 + depends on COMMON_CLK_MT2701 ---help--- This driver supports Mediatek MT2701 imgsys clocks. config COMMON_CLK_MT2701_VDECSYS bool "Clock driver for Mediatek MT2701 vdecsys" - select COMMON_CLK_MT2701 + depends on COMMON_CLK_MT2701 ---help--- This driver supports Mediatek MT2701 vdecsys clocks. config COMMON_CLK_MT2701_HIFSYS bool "Clock driver for Mediatek MT2701 hifsys" - select COMMON_CLK_MT2701 + depends on COMMON_CLK_MT2701 ---help--- This driver supports Mediatek MT2701 hifsys clocks. config COMMON_CLK_MT2701_ETHSYS bool "Clock driver for Mediatek MT2701 ethsys" - select COMMON_CLK_MT2701 + depends on COMMON_CLK_MT2701 ---help--- This driver supports Mediatek MT2701 ethsys clocks. config COMMON_CLK_MT2701_BDPSYS bool "Clock driver for Mediatek MT2701 bdpsys" - select COMMON_CLK_MT2701 + depends on COMMON_CLK_MT2701 ---help--- This driver supports Mediatek MT2701 bdpsys clocks. config COMMON_CLK_MT8135 bool "Clock driver for Mediatek MT8135" - depends on ARCH_MEDIATEK || COMPILE_TEST + depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST select COMMON_CLK_MEDIATEK - default ARCH_MEDIATEK + default ARCH_MEDIATEK && ARM ---help--- This driver supports Mediatek MT8135 clocks. diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 9d9af446bafc..1c1ec137a3cc 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -564,6 +564,46 @@ static struct clk_gate gxbb_clk81 = { }, }; +static struct clk_mux gxbb_sar_adc_clk_sel = { + .reg = (void *)HHI_SAR_CLK_CNTL, + .mask = 0x3, + .shift = 9, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sar_adc_clk_sel", + .ops = &clk_mux_ops, + /* NOTE: The datasheet doesn't list the parents for bit 10 */ + .parent_names = (const char *[]){ "xtal", "clk81", }, + .num_parents = 2, + }, +}; + +static struct clk_divider gxbb_sar_adc_clk_div = { + .reg = (void *)HHI_SAR_CLK_CNTL, + .shift = 0, + .width = 8, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sar_adc_clk_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "sar_adc_clk_sel" }, + .num_parents = 1, + }, +}; + +static struct clk_gate gxbb_sar_adc_clk = { + .reg = (void *)HHI_SAR_CLK_CNTL, + .bit_idx = 8, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sar_adc_clk", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "sar_adc_clk_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0); static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1); @@ -754,6 +794,9 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw, [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw, [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw, + [CLKID_SAR_ADC_CLK] = &gxbb_sar_adc_clk.hw, + [CLKID_SAR_ADC_SEL] = &gxbb_sar_adc_clk_sel.hw, + [CLKID_SAR_ADC_DIV] = &gxbb_sar_adc_clk_div.hw, }, .num = NR_CLKS, }; @@ -856,6 +899,7 @@ static struct clk_gate *gxbb_clk_gates[] = { &gxbb_emmc_a, &gxbb_emmc_b, &gxbb_emmc_c, + &gxbb_sar_adc_clk, }; static int gxbb_clkc_probe(struct platform_device *pdev) @@ -888,6 +932,10 @@ static int gxbb_clkc_probe(struct platform_device *pdev) gxbb_mpeg_clk_sel.reg = clk_base + (u64)gxbb_mpeg_clk_sel.reg; gxbb_mpeg_clk_div.reg = clk_base + (u64)gxbb_mpeg_clk_div.reg; + /* Populate the base address for the SAR ADC clks */ + gxbb_sar_adc_clk_sel.reg = clk_base + (u64)gxbb_sar_adc_clk_sel.reg; + gxbb_sar_adc_clk_div.reg = clk_base + (u64)gxbb_sar_adc_clk_div.reg; + /* Populate base address for gates */ for (i = 0; i < ARRAY_SIZE(gxbb_clk_gates); i++) gxbb_clk_gates[i]->reg = clk_base + diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index 0252939ba58f..8ee2022ce5d5 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -191,7 +191,7 @@ #define CLKID_PERIPHS 20 #define CLKID_SPICC 21 /* CLKID_I2C */ -#define CLKID_SAR_ADC 23 +/* #define CLKID_SAR_ADC */ #define CLKID_SMART_CARD 24 #define CLKID_RNG0 25 #define CLKID_UART0 26 @@ -204,7 +204,7 @@ #define CLKID_ASSIST_MISC 33 /* CLKID_SPI */ #define CLKID_I2S_SPDIF 35 -#define CLKID_ETH 36 +/* CLKID_ETH */ #define CLKID_DEMUX 37 #define CLKID_AIU_GLUE 38 #define CLKID_IEC958 39 @@ -231,13 +231,13 @@ #define CLKID_AHB_DATA_BUS 60 #define CLKID_AHB_CTRL_BUS 61 #define CLKID_HDMI_INTR_SYNC 62 -#define CLKID_HDMI_PCLK 63 +/* CLKID_HDMI_PCLK */ /* CLKID_USB1_DDR_BRIDGE */ /* CLKID_USB0_DDR_BRIDGE */ #define CLKID_MMC_PCLK 66 #define CLKID_DVIN 67 #define CLKID_UART2 68 -#define CLKID_SANA 69 +/* #define CLKID_SANA */ #define CLKID_VPU_INTR 70 #define CLKID_SEC_AHB_AHB3_BRIDGE 71 #define CLKID_CLK81_A53 72 @@ -245,7 +245,7 @@ #define CLKID_VCLK2_VENCI1 74 #define CLKID_VCLK2_VENCP0 75 #define CLKID_VCLK2_VENCP1 76 -#define CLKID_GCLK_VENCI_INT0 77 +/* CLKID_GCLK_VENCI_INT0 */ #define CLKID_GCLK_VENCI_INT 78 #define CLKID_DAC_CLK 79 #define CLKID_AOCLK_GATE 80 @@ -265,8 +265,11 @@ /* CLKID_SD_EMMC_A */ /* CLKID_SD_EMMC_B */ /* CLKID_SD_EMMC_C */ +/* CLKID_SAR_ADC_CLK */ +/* CLKID_SAR_ADC_SEL */ +#define CLKID_SAR_ADC_DIV 99 -#define NR_CLKS 97 +#define NR_CLKS 100 /* include the CLKIDs that have been made part of the stable DT binding */ #include <dt-bindings/clock/gxbb-clkc.h> diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 3f1be46cbb33..888494d4fb8a 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -607,7 +607,6 @@ static int meson8b_clkc_probe(struct platform_device *pdev) /* Populate the base address for the MPEG clks */ meson8b_mpeg_clk_sel.reg = clk_base + (u32)meson8b_mpeg_clk_sel.reg; meson8b_mpeg_clk_div.reg = clk_base + (u32)meson8b_mpeg_clk_div.reg; - meson8b_clk81.reg = clk_base + (u32)meson8b_clk81.reg; /* Populate base address for gates */ for (i = 0; i < ARRAY_SIZE(meson8b_clk_gates); i++) diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index d9ae97fb43c4..d71c7fd5da16 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-xtal.o obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-tbg.o obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-periph.o -obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o +obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o mv98dx3236.o obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o obj-$(CONFIG_DOVE_CLK) += dove.o dove-divider.o diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c index 8181b919f062..f17702107ac5 100644 --- a/drivers/clk/mvebu/ap806-system-controller.c +++ b/drivers/clk/mvebu/ap806-system-controller.c @@ -55,21 +55,39 @@ static int ap806_syscon_clk_probe(struct platform_device *pdev) freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK; switch (freq_mode) { - case 0x0 ... 0x5: + case 0x0: + case 0x1: cpuclk_freq = 2000; break; - case 0x6 ... 0xB: + case 0x6: + case 0x7: cpuclk_freq = 1800; break; - case 0xC ... 0x11: + case 0x4: + case 0xB: + case 0xD: cpuclk_freq = 1600; break; - case 0x12 ... 0x16: + case 0x1a: cpuclk_freq = 1400; break; - case 0x17 ... 0x19: + case 0x14: + case 0x17: cpuclk_freq = 1300; break; + case 0x19: + cpuclk_freq = 1200; + break; + case 0x13: + case 0x1d: + cpuclk_freq = 1000; + break; + case 0x1c: + cpuclk_freq = 800; + break; + case 0x1b: + cpuclk_freq = 600; + break; default: dev_err(&pdev->dev, "invalid SAR value\n"); return -EINVAL; diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index b3094315a3c0..0ec44ae9a2a2 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -52,6 +52,12 @@ static u32 __init axp_get_tclk_freq(void __iomem *sar) return 250000000; } +/* MV98DX3236 TCLK frequency is fixed to 200MHz */ +static u32 __init mv98dx3236_get_tclk_freq(void __iomem *sar) +{ + return 200000000; +} + static const u32 axp_cpu_freqs[] __initconst = { 1000000000, 1066000000, @@ -89,6 +95,12 @@ static u32 __init axp_get_cpu_freq(void __iomem *sar) return cpu_freq; } +/* MV98DX3236 CLK frequency is fixed to 800MHz */ +static u32 __init mv98dx3236_get_cpu_freq(void __iomem *sar) +{ + return 800000000; +} + static const int axp_nbclk_ratios[32][2] __initconst = { {0, 1}, {1, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 1}, {2, 3}, @@ -158,6 +170,11 @@ static const struct coreclk_soc_desc axp_coreclks = { .num_ratios = ARRAY_SIZE(axp_coreclk_ratios), }; +static const struct coreclk_soc_desc mv98dx3236_coreclks = { + .get_tclk_freq = mv98dx3236_get_tclk_freq, + .get_cpu_freq = mv98dx3236_get_cpu_freq, +}; + /* * Clock Gating Control */ @@ -195,6 +212,15 @@ static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = { { } }; +static const struct clk_gating_soc_desc mv98dx3236_gating_desc[] __initconst = { + { "ge1", NULL, 3, 0 }, + { "ge0", NULL, 4, 0 }, + { "pex00", NULL, 5, 0 }, + { "sdio", NULL, 17, 0 }, + { "xor0", NULL, 22, 0 }, + { } +}; + static void __init axp_clk_init(struct device_node *np) { struct device_node *cgnp = diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index d1e5863d3375..8491979f4096 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -71,6 +71,10 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] = { { .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */ }; +static const struct clk_corediv_desc mv98dx3236_corediv_desc[] = { + { .mask = 0x0f, .offset = 6, .fieldbit = 26 }, /* NAND clock */ +}; + #define to_corediv_clk(p) container_of(p, struct clk_corediv, hw) static int clk_corediv_is_enabled(struct clk_hw *hwclk) @@ -232,6 +236,18 @@ static const struct clk_corediv_soc_desc armada375_corediv_soc = { .ratio_offset = 0x4, }; +static const struct clk_corediv_soc_desc mv98dx3236_corediv_soc = { + .descs = mv98dx3236_corediv_desc, + .ndescs = ARRAY_SIZE(mv98dx3236_corediv_desc), + .ops = { + .recalc_rate = clk_corediv_recalc_rate, + .round_rate = clk_corediv_round_rate, + .set_rate = clk_corediv_set_rate, + }, + .ratio_reload = BIT(10), + .ratio_offset = 0x8, +}; + static void __init mvebu_corediv_clk_init(struct device_node *node, const struct clk_corediv_soc_desc *soc_desc) @@ -313,3 +329,10 @@ static void __init armada380_corediv_clk_init(struct device_node *node) } CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock", armada380_corediv_clk_init); + +static void __init mv98dx3236_corediv_clk_init(struct device_node *node) +{ + return mvebu_corediv_clk_init(node, &mv98dx3236_corediv_soc); +} +CLK_OF_DECLARE(mv98dx3236_corediv_clk, "marvell,mv98dx3236-corediv-clock", + mv98dx3236_corediv_clk_init); diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 5837eb8a212f..044892b6534d 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -245,3 +245,11 @@ cpuclk_out: CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock", of_cpu_clk_setup); + +static void __init of_mv98dx3236_cpu_clk_setup(struct device_node *node) +{ + of_clk_add_provider(node, of_clk_src_simple_get, NULL); +} + +CLK_OF_DECLARE(mv98dx3236_cpu_clock, "marvell,mv98dx3236-cpu-clock", + of_mv98dx3236_cpu_clk_setup); diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index 32e5b43c086f..6b11d7b3e0e0 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -64,8 +64,11 @@ enum { #define CP110_GATE_NAND 2 #define CP110_GATE_PPV2 3 #define CP110_GATE_SDIO 4 +#define CP110_GATE_MG 5 +#define CP110_GATE_MG_CORE 6 #define CP110_GATE_XOR1 7 #define CP110_GATE_XOR0 8 +#define CP110_GATE_GOP_DP 9 #define CP110_GATE_PCIE_X1_0 11 #define CP110_GATE_PCIE_X1_1 12 #define CP110_GATE_PCIE_X4 13 @@ -73,7 +76,7 @@ enum { #define CP110_GATE_SATA 15 #define CP110_GATE_SATA_USB 16 #define CP110_GATE_MAIN 17 -#define CP110_GATE_SDMMC 18 +#define CP110_GATE_SDMMC_GOP 18 #define CP110_GATE_SLOW_IO 21 #define CP110_GATE_USB3H0 22 #define CP110_GATE_USB3H1 23 @@ -296,6 +299,11 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) "gate-clock-output-names", CP110_GATE_MAIN, &parent); break; + case CP110_GATE_MG: + of_property_read_string_index(np, + "gate-clock-output-names", + CP110_GATE_MG_CORE, &parent); + break; case CP110_GATE_NAND: parent = nand_name; break; @@ -303,9 +311,10 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) parent = ppv2_name; break; case CP110_GATE_SDIO: + case CP110_GATE_GOP_DP: of_property_read_string_index(np, "gate-clock-output-names", - CP110_GATE_SDMMC, &parent); + CP110_GATE_SDMMC_GOP, &parent); break; case CP110_GATE_XOR1: case CP110_GATE_XOR0: diff --git a/drivers/clk/mvebu/mv98dx3236.c b/drivers/clk/mvebu/mv98dx3236.c new file mode 100644 index 000000000000..6e203af73cac --- /dev/null +++ b/drivers/clk/mvebu/mv98dx3236.c @@ -0,0 +1,180 @@ +/* + * Marvell MV98DX3236 SoC clocks + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + + +/* + * For 98DX4251 Sample At Reset the CPU, DDR and Main PLL clocks are all + * defined at the same time + * + * SAR1[20:18] : CPU frequency DDR frequency MPLL frequency + * 0 = 400 MHz 400 MHz 800 MHz + * 2 = 667 MHz 667 MHz 2000 MHz + * 3 = 800 MHz 800 MHz 1600 MHz + * others reserved. + * + * For 98DX3236 Sample At Reset the CPU, DDR and Main PLL clocks are all + * defined at the same time + * + * SAR1[20:18] : CPU frequency DDR frequency MPLL frequency + * 1 = 667 MHz 667 MHz 2000 MHz + * 2 = 400 MHz 400 MHz 400 MHz + * 3 = 800 MHz 800 MHz 800 MHz + * 5 = 800 MHz 400 MHz 800 MHz + * others reserved. + */ + +#define SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT 18 +#define SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT_MASK 0x7 + +static u32 __init mv98dx3236_get_tclk_freq(void __iomem *sar) +{ + /* Tclk = 200MHz, no SaR dependency */ + return 200000000; +} + +static const u32 mv98dx3236_cpu_frequencies[] __initconst = { + 0, + 667000000, + 400000000, + 800000000, + 0, + 800000000, + 0, 0, +}; + +static const u32 mv98dx4251_cpu_frequencies[] __initconst = { + 400000000, + 0, + 667000000, + 800000000, + 0, 0, 0, 0, +}; + +static u32 __init mv98dx3236_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq = 0; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT) & + SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT_MASK); + + if (of_machine_is_compatible("marvell,armadaxp-98dx4251")) + cpu_freq = mv98dx4251_cpu_frequencies[cpu_freq_select]; + else if (of_machine_is_compatible("marvell,armadaxp-98dx3236")) + cpu_freq = mv98dx3236_cpu_frequencies[cpu_freq_select]; + + if (!cpu_freq) + pr_err("CPU freq select unsupported %d\n", cpu_freq_select); + + return cpu_freq; +} + +enum { + MV98DX3236_CPU_TO_DDR, + MV98DX3236_CPU_TO_MPLL +}; + +static const struct coreclk_ratio mv98dx3236_core_ratios[] __initconst = { + { .id = MV98DX3236_CPU_TO_DDR, .name = "ddrclk" }, + { .id = MV98DX3236_CPU_TO_MPLL, .name = "mpll" }, +}; + +static const int __initconst mv98dx3236_cpu_mpll_ratios[8][2] = { + {0, 1}, {3, 1}, {1, 1}, {1, 1}, + {0, 1}, {1, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst mv98dx3236_cpu_ddr_ratios[8][2] = { + {0, 1}, {1, 1}, {1, 1}, {1, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, +}; + +static const int __initconst mv98dx4251_cpu_mpll_ratios[8][2] = { + {2, 1}, {0, 1}, {3, 1}, {2, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst mv98dx4251_cpu_ddr_ratios[8][2] = { + {1, 1}, {0, 1}, {1, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init mv98dx3236_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT) & + SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT_MASK); + + switch (id) { + case MV98DX3236_CPU_TO_DDR: + if (of_machine_is_compatible("marvell,armadaxp-98dx4251")) { + *mult = mv98dx4251_cpu_ddr_ratios[opt][0]; + *div = mv98dx4251_cpu_ddr_ratios[opt][1]; + } else if (of_machine_is_compatible("marvell,armadaxp-98dx3236")) { + *mult = mv98dx3236_cpu_ddr_ratios[opt][0]; + *div = mv98dx3236_cpu_ddr_ratios[opt][1]; + } + break; + case MV98DX3236_CPU_TO_MPLL: + if (of_machine_is_compatible("marvell,armadaxp-98dx4251")) { + *mult = mv98dx4251_cpu_mpll_ratios[opt][0]; + *div = mv98dx4251_cpu_mpll_ratios[opt][1]; + } else if (of_machine_is_compatible("marvell,armadaxp-98dx3236")) { + *mult = mv98dx3236_cpu_mpll_ratios[opt][0]; + *div = mv98dx3236_cpu_mpll_ratios[opt][1]; + } + break; + } +} + +static const struct coreclk_soc_desc mv98dx3236_core_clocks = { + .get_tclk_freq = mv98dx3236_get_tclk_freq, + .get_cpu_freq = mv98dx3236_get_cpu_freq, + .get_clk_ratio = mv98dx3236_get_clk_ratio, + .ratios = mv98dx3236_core_ratios, + .num_ratios = ARRAY_SIZE(mv98dx3236_core_ratios), +}; + + +/* + * Clock Gating Control + */ + +static const struct clk_gating_soc_desc mv98dx3236_gating_desc[] __initconst = { + { "ge1", NULL, 3, 0 }, + { "ge0", NULL, 4, 0 }, + { "pex00", NULL, 5, 0 }, + { "sdio", NULL, 17, 0 }, + { "usb0", NULL, 18, 0 }, + { "xor0", NULL, 22, 0 }, + { } +}; + +static void __init mv98dx3236_clk_init(struct device_node *np) +{ + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,mv98dx3236-gating-clock"); + + mvebu_coreclk_setup(np, &mv98dx3236_core_clocks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc); +} +CLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock", mv98dx3236_clk_init); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 07e2cc6ed781..3487c267833e 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -462,8 +462,79 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { .num_clks = ARRAY_SIZE(msm8916_clks), }; +/* msm8974 */ +DEFINE_CLK_SMD_RPM(msm8974, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8974, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8974, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8974, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, QCOM_SMD_RPM_BUS_CLK, 3); +DEFINE_CLK_SMD_RPM(msm8974, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8974, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8974, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(msm8974, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d0, cxo_d0_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_d1, cxo_d1_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a0, cxo_a0_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a1, cxo_a1_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, cxo_a2, cxo_a2_a, 6); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, diff_clk, diff_a_clk, 7); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk1, div_a_clk1, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8974, div_clk2, div_a_clk2, 12); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d0_pin, cxo_d0_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_d1_pin, cxo_d1_a_pin, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a0_pin, cxo_a0_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a1_pin, cxo_a1_a_pin, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8974, cxo_a2_pin, cxo_a2_a_pin, 6); + +static struct clk_smd_rpm *msm8974_clks[] = { + [RPM_SMD_PNOC_CLK] = &msm8974_pnoc_clk, + [RPM_SMD_PNOC_A_CLK] = &msm8974_pnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8974_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8974_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8974_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8974_cnoc_a_clk, + [RPM_SMD_MMSSNOC_AHB_CLK] = &msm8974_mmssnoc_ahb_clk, + [RPM_SMD_MMSSNOC_AHB_A_CLK] = &msm8974_mmssnoc_ahb_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8974_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8974_bimc_a_clk, + [RPM_SMD_OCMEMGX_CLK] = &msm8974_ocmemgx_clk, + [RPM_SMD_OCMEMGX_A_CLK] = &msm8974_ocmemgx_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8974_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8974_qdss_a_clk, + [RPM_SMD_CXO_D0] = &msm8974_cxo_d0, + [RPM_SMD_CXO_D0_A] = &msm8974_cxo_d0_a, + [RPM_SMD_CXO_D1] = &msm8974_cxo_d1, + [RPM_SMD_CXO_D1_A] = &msm8974_cxo_d1_a, + [RPM_SMD_CXO_A0] = &msm8974_cxo_a0, + [RPM_SMD_CXO_A0_A] = &msm8974_cxo_a0_a, + [RPM_SMD_CXO_A1] = &msm8974_cxo_a1, + [RPM_SMD_CXO_A1_A] = &msm8974_cxo_a1_a, + [RPM_SMD_CXO_A2] = &msm8974_cxo_a2, + [RPM_SMD_CXO_A2_A] = &msm8974_cxo_a2_a, + [RPM_SMD_DIFF_CLK] = &msm8974_diff_clk, + [RPM_SMD_DIFF_A_CLK] = &msm8974_diff_a_clk, + [RPM_SMD_DIV_CLK1] = &msm8974_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8974_div_a_clk1, + [RPM_SMD_DIV_CLK2] = &msm8974_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8974_div_a_clk2, + [RPM_SMD_CXO_D0_PIN] = &msm8974_cxo_d0_pin, + [RPM_SMD_CXO_D0_A_PIN] = &msm8974_cxo_d0_a_pin, + [RPM_SMD_CXO_D1_PIN] = &msm8974_cxo_d1_pin, + [RPM_SMD_CXO_D1_A_PIN] = &msm8974_cxo_d1_a_pin, + [RPM_SMD_CXO_A0_PIN] = &msm8974_cxo_a0_pin, + [RPM_SMD_CXO_A0_A_PIN] = &msm8974_cxo_a0_a_pin, + [RPM_SMD_CXO_A1_PIN] = &msm8974_cxo_a1_pin, + [RPM_SMD_CXO_A1_A_PIN] = &msm8974_cxo_a1_a_pin, + [RPM_SMD_CXO_A2_PIN] = &msm8974_cxo_a2_pin, + [RPM_SMD_CXO_A2_A_PIN] = &msm8974_cxo_a2_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8974 = { + .clks = msm8974_clks, + .num_clks = ARRAY_SIZE(msm8974_clks), +}; static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, + { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index cfab7b400381..03f9d316f969 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -145,7 +145,6 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path, clocks_node = of_find_node_by_path("/clocks"); if (clocks_node) node = of_find_node_by_name(clocks_node, path); - of_node_put(clocks_node); if (!node) { fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL); diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c index 33d09138f5e5..46cb256b4aa2 100644 --- a/drivers/clk/qcom/gcc-ipq4019.c +++ b/drivers/clk/qcom/gcc-ipq4019.c @@ -20,6 +20,9 @@ #include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/reset-controller.h> +#include <linux/math64.h> +#include <linux/delay.h> +#include <linux/clk.h> #include <dt-bindings/clock/qcom,gcc-ipq4019.h> @@ -28,6 +31,13 @@ #include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "clk-regmap-divider.h" + +#define to_clk_regmap_div(_hw) container_of(to_clk_regmap(_hw),\ + struct clk_regmap_div, clkr) + +#define to_clk_fepll(_hw) container_of(to_clk_regmap_div(_hw),\ + struct clk_fepll, cdiv) enum { P_XO, @@ -40,6 +50,41 @@ enum { P_DDRPLLAPSS, }; +/* + * struct clk_fepll_vco - vco feedback divider corresponds for FEPLL clocks + * @fdbkdiv_shift: lowest bit for FDBKDIV + * @fdbkdiv_width: number of bits in FDBKDIV + * @refclkdiv_shift: lowest bit for REFCLKDIV + * @refclkdiv_width: number of bits in REFCLKDIV + * @reg: PLL_DIV register address + */ +struct clk_fepll_vco { + u32 fdbkdiv_shift; + u32 fdbkdiv_width; + u32 refclkdiv_shift; + u32 refclkdiv_width; + u32 reg; +}; + +/* + * struct clk_fepll - clk divider corresponds to FEPLL clocks + * @fixed_div: fixed divider value if divider is fixed + * @parent_map: map from software's parent index to hardware's src_sel field + * @cdiv: divider values for PLL_DIV + * @pll_vco: vco feedback divider + * @div_table: mapping for actual divider value to register divider value + * in case of non fixed divider + * @freq_tbl: frequency table + */ +struct clk_fepll { + u32 fixed_div; + const u8 *parent_map; + struct clk_regmap_div cdiv; + const struct clk_fepll_vco *pll_vco; + const struct clk_div_table *div_table; + const struct freq_tbl *freq_tbl; +}; + static struct parent_map gcc_xo_200_500_map[] = { { P_XO, 0 }, { P_FEPLL200, 1 }, @@ -80,7 +125,7 @@ static struct parent_map gcc_xo_sdcc1_500_map[] = { static const char * const gcc_xo_sdcc1_500[] = { "xo", - "ddrpll", + "ddrpllsdcc", "fepll500", }; @@ -121,6 +166,12 @@ static struct parent_map gcc_xo_ddr_500_200_map[] = { { P_DDRPLLAPSS, 1 }, }; +/* + * Contains index for safe clock during APSS freq change. + * fepll500 is being used as safe clock so initialize it + * with its index in parents list gcc_xo_ddr_500_200. + */ +static const int gcc_ipq4019_cpu_safe_parent = 2; static const char * const gcc_xo_ddr_500_200[] = { "xo", "fepll200", @@ -505,7 +556,7 @@ static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk[] = { F(25000000, P_FEPLL500, 1, 1, 20), F(50000000, P_FEPLL500, 1, 1, 10), F(100000000, P_FEPLL500, 1, 1, 5), - F(193000000, P_DDRPLL, 1, 0, 0), + F(192000000, P_DDRPLL, 1, 0, 0), { } }; @@ -524,10 +575,20 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { }; static const struct freq_tbl ftbl_gcc_apps_clk[] = { - F(48000000, P_XO, 1, 0, 0), + F(48000000, P_XO, 1, 0, 0), F(200000000, P_FEPLL200, 1, 0, 0), + F(384000000, P_DDRPLLAPSS, 1, 0, 0), + F(413000000, P_DDRPLLAPSS, 1, 0, 0), + F(448000000, P_DDRPLLAPSS, 1, 0, 0), + F(488000000, P_DDRPLLAPSS, 1, 0, 0), F(500000000, P_FEPLL500, 1, 0, 0), - F(626000000, P_DDRPLLAPSS, 1, 0, 0), + F(512000000, P_DDRPLLAPSS, 1, 0, 0), + F(537000000, P_DDRPLLAPSS, 1, 0, 0), + F(565000000, P_DDRPLLAPSS, 1, 0, 0), + F(597000000, P_DDRPLLAPSS, 1, 0, 0), + F(632000000, P_DDRPLLAPSS, 1, 0, 0), + F(672000000, P_DDRPLLAPSS, 1, 0, 0), + F(716000000, P_DDRPLLAPSS, 1, 0, 0), { } }; @@ -541,6 +602,7 @@ static struct clk_rcg2 apps_clk_src = { .parent_names = gcc_xo_ddr_500_200, .num_parents = 4, .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -1154,6 +1216,364 @@ static struct clk_branch gcc_wcss5g_rtc_clk = { }, }; +/* Calculates the VCO rate for FEPLL. */ +static u64 clk_fepll_vco_calc_rate(struct clk_fepll *pll_div, + unsigned long parent_rate) +{ + const struct clk_fepll_vco *pll_vco = pll_div->pll_vco; + u32 fdbkdiv, refclkdiv, cdiv; + u64 vco; + + regmap_read(pll_div->cdiv.clkr.regmap, pll_vco->reg, &cdiv); + refclkdiv = (cdiv >> pll_vco->refclkdiv_shift) & + (BIT(pll_vco->refclkdiv_width) - 1); + fdbkdiv = (cdiv >> pll_vco->fdbkdiv_shift) & + (BIT(pll_vco->fdbkdiv_width) - 1); + + vco = parent_rate / refclkdiv; + vco *= 2; + vco *= fdbkdiv; + + return vco; +} + +static const struct clk_fepll_vco gcc_apss_ddrpll_vco = { + .fdbkdiv_shift = 16, + .fdbkdiv_width = 8, + .refclkdiv_shift = 24, + .refclkdiv_width = 5, + .reg = 0x2e020, +}; + +static const struct clk_fepll_vco gcc_fepll_vco = { + .fdbkdiv_shift = 16, + .fdbkdiv_width = 8, + .refclkdiv_shift = 24, + .refclkdiv_width = 5, + .reg = 0x2f020, +}; + +/* + * Round rate function for APSS CPU PLL Clock divider. + * It looks up the frequency table and returns the next higher frequency + * supported in hardware. + */ +static long clk_cpu_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *p_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + struct clk_hw *p_hw; + const struct freq_tbl *f; + + f = qcom_find_freq(pll->freq_tbl, rate); + if (!f) + return -EINVAL; + + p_hw = clk_hw_get_parent_by_index(hw, f->src); + *p_rate = clk_hw_get_rate(p_hw); + + return f->freq; +}; + +/* + * Clock set rate function for APSS CPU PLL Clock divider. + * It looks up the frequency table and updates the PLL divider to corresponding + * divider value. + */ +static int clk_cpu_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + const struct freq_tbl *f; + u32 mask; + int ret; + + f = qcom_find_freq(pll->freq_tbl, rate); + if (!f) + return -EINVAL; + + mask = (BIT(pll->cdiv.width) - 1) << pll->cdiv.shift; + ret = regmap_update_bits(pll->cdiv.clkr.regmap, + pll->cdiv.reg, mask, + f->pre_div << pll->cdiv.shift); + /* + * There is no status bit which can be checked for successful CPU + * divider update operation so using delay for the same. + */ + udelay(1); + + return 0; +}; + +/* + * Clock frequency calculation function for APSS CPU PLL Clock divider. + * This clock divider is nonlinear so this function calculates the actual + * divider and returns the output frequency by dividing VCO Frequency + * with this actual divider value. + */ +static unsigned long +clk_cpu_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + u32 cdiv, pre_div; + u64 rate; + + regmap_read(pll->cdiv.clkr.regmap, pll->cdiv.reg, &cdiv); + cdiv = (cdiv >> pll->cdiv.shift) & (BIT(pll->cdiv.width) - 1); + + /* + * Some dividers have value in 0.5 fraction so multiply both VCO + * frequency(parent_rate) and pre_div with 2 to make integer + * calculation. + */ + if (cdiv > 10) + pre_div = (cdiv + 1) * 2; + else + pre_div = cdiv + 12; + + rate = clk_fepll_vco_calc_rate(pll, parent_rate) * 2; + do_div(rate, pre_div); + + return rate; +}; + +static const struct clk_ops clk_regmap_cpu_div_ops = { + .round_rate = clk_cpu_div_round_rate, + .set_rate = clk_cpu_div_set_rate, + .recalc_rate = clk_cpu_div_recalc_rate, +}; + +static const struct freq_tbl ftbl_apss_ddr_pll[] = { + { 384000000, P_XO, 0xd, 0, 0 }, + { 413000000, P_XO, 0xc, 0, 0 }, + { 448000000, P_XO, 0xb, 0, 0 }, + { 488000000, P_XO, 0xa, 0, 0 }, + { 512000000, P_XO, 0x9, 0, 0 }, + { 537000000, P_XO, 0x8, 0, 0 }, + { 565000000, P_XO, 0x7, 0, 0 }, + { 597000000, P_XO, 0x6, 0, 0 }, + { 632000000, P_XO, 0x5, 0, 0 }, + { 672000000, P_XO, 0x4, 0, 0 }, + { 716000000, P_XO, 0x3, 0, 0 }, + { 768000000, P_XO, 0x2, 0, 0 }, + { 823000000, P_XO, 0x1, 0, 0 }, + { 896000000, P_XO, 0x0, 0, 0 }, + { } +}; + +static struct clk_fepll gcc_apss_cpu_plldiv_clk = { + .cdiv.reg = 0x2e020, + .cdiv.shift = 4, + .cdiv.width = 4, + .cdiv.clkr = { + .enable_reg = 0x2e000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "ddrpllapss", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_regmap_cpu_div_ops, + }, + }, + .freq_tbl = ftbl_apss_ddr_pll, + .pll_vco = &gcc_apss_ddrpll_vco, +}; + +/* Calculates the rate for PLL divider. + * If the divider value is not fixed then it gets the actual divider value + * from divider table. Then, it calculate the clock rate by dividing the + * parent rate with actual divider value. + */ +static unsigned long +clk_regmap_clk_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fepll *pll = to_clk_fepll(hw); + u32 cdiv, pre_div = 1; + u64 rate; + const struct clk_div_table *clkt; + + if (pll->fixed_div) { + pre_div = pll->fixed_div; + } else { + regmap_read(pll->cdiv.clkr.regmap, pll->cdiv.reg, &cdiv); + cdiv = (cdiv >> pll->cdiv.shift) & (BIT(pll->cdiv.width) - 1); + + for (clkt = pll->div_table; clkt->div; clkt++) { + if (clkt->val == cdiv) + pre_div = clkt->div; + } + } + + rate = clk_fepll_vco_calc_rate(pll, parent_rate); + do_div(rate, pre_div); + + return rate; +}; + +static const struct clk_ops clk_fepll_div_ops = { + .recalc_rate = clk_regmap_clk_div_recalc_rate, +}; + +static struct clk_fepll gcc_apss_sdcc_clk = { + .fixed_div = 28, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "ddrpllsdcc", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_apss_ddrpll_vco, +}; + +static struct clk_fepll gcc_fepll125_clk = { + .fixed_div = 32, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll125", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepll125dly_clk = { + .fixed_div = 32, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll125dly", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepll200_clk = { + .fixed_div = 20, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll200", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepll500_clk = { + .fixed_div = 8, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepll500", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .pll_vco = &gcc_fepll_vco, +}; + +static const struct clk_div_table fepllwcss_clk_div_table[] = { + { 0, 15 }, + { 1, 16 }, + { 2, 18 }, + { 3, 20 }, + { }, +}; + +static struct clk_fepll gcc_fepllwcss2g_clk = { + .cdiv.reg = 0x2f020, + .cdiv.shift = 8, + .cdiv.width = 2, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepllwcss2g", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table, + .pll_vco = &gcc_fepll_vco, +}; + +static struct clk_fepll gcc_fepllwcss5g_clk = { + .cdiv.reg = 0x2f020, + .cdiv.shift = 12, + .cdiv.width = 2, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data){ + .name = "fepllwcss5g", + .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_fepll_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table, + .pll_vco = &gcc_fepll_vco, +}; + +static const struct freq_tbl ftbl_gcc_pcnoc_ahb_clk[] = { + F(48000000, P_XO, 1, 0, 0), + F(100000000, P_FEPLL200, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcnoc_ahb_clk_src = { + .cmd_rcgr = 0x21024, + .hid_width = 5, + .parent_map = gcc_xo_200_500_map, + .freq_tbl = ftbl_gcc_pcnoc_ahb_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_pcnoc_ahb_clk_src", + .parent_names = gcc_xo_200_500, + .num_parents = 3, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch pcnoc_clk_src = { + .halt_reg = 0x21030, + .clkr = { + .enable_reg = 0x21030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "pcnoc_clk_src", + .parent_names = (const char *[]){ + "gcc_pcnoc_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT | + CLK_IS_CRITICAL, + }, + }, +}; + static struct clk_regmap *gcc_ipq4019_clocks[] = { [AUDIO_CLK_SRC] = &audio_clk_src.clkr, [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr, @@ -1214,6 +1634,16 @@ static struct clk_regmap *gcc_ipq4019_clocks[] = { [GCC_WCSS5G_CLK] = &gcc_wcss5g_clk.clkr, [GCC_WCSS5G_REF_CLK] = &gcc_wcss5g_ref_clk.clkr, [GCC_WCSS5G_RTC_CLK] = &gcc_wcss5g_rtc_clk.clkr, + [GCC_SDCC_PLLDIV_CLK] = &gcc_apss_sdcc_clk.cdiv.clkr, + [GCC_FEPLL125_CLK] = &gcc_fepll125_clk.cdiv.clkr, + [GCC_FEPLL125DLY_CLK] = &gcc_fepll125dly_clk.cdiv.clkr, + [GCC_FEPLL200_CLK] = &gcc_fepll200_clk.cdiv.clkr, + [GCC_FEPLL500_CLK] = &gcc_fepll500_clk.cdiv.clkr, + [GCC_FEPLL_WCSS2G_CLK] = &gcc_fepllwcss2g_clk.cdiv.clkr, + [GCC_FEPLL_WCSS5G_CLK] = &gcc_fepllwcss5g_clk.cdiv.clkr, + [GCC_APSS_CPU_PLLDIV_CLK] = &gcc_apss_cpu_plldiv_clk.cdiv.clkr, + [GCC_PCNOC_AHB_CLK_SRC] = &gcc_pcnoc_ahb_clk_src.clkr, + [GCC_PCNOC_AHB_CLK] = &pcnoc_clk_src.clkr, }; static const struct qcom_reset_map gcc_ipq4019_resets[] = { @@ -1294,7 +1724,7 @@ static const struct regmap_config gcc_ipq4019_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x2dfff, + .max_register = 0x2ffff, .fast_io = true, }; @@ -1312,23 +1742,44 @@ static const struct of_device_id gcc_ipq4019_match_table[] = { }; MODULE_DEVICE_TABLE(of, gcc_ipq4019_match_table); +static int +gcc_ipq4019_cpu_clk_notifier_fn(struct notifier_block *nb, + unsigned long action, void *data) +{ + int err = 0; + + if (action == PRE_RATE_CHANGE) + err = clk_rcg2_ops.set_parent(&apps_clk_src.clkr.hw, + gcc_ipq4019_cpu_safe_parent); + + return notifier_from_errno(err); +} + +static struct notifier_block gcc_ipq4019_cpu_clk_notifier = { + .notifier_call = gcc_ipq4019_cpu_clk_notifier_fn, +}; + static int gcc_ipq4019_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; + int err; - clk_register_fixed_rate(dev, "fepll125", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepll125dly", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepllwcss2g", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepllwcss5g", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepll200", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "fepll500", "xo", 0, 200000000); - clk_register_fixed_rate(dev, "ddrpllapss", "xo", 0, 666000000); + err = qcom_cc_probe(pdev, &gcc_ipq4019_desc); + if (err) + return err; - return qcom_cc_probe(pdev, &gcc_ipq4019_desc); + return clk_notifier_register(apps_clk_src.clkr.hw.clk, + &gcc_ipq4019_cpu_clk_notifier); +} + +static int gcc_ipq4019_remove(struct platform_device *pdev) +{ + return clk_notifier_unregister(apps_clk_src.clkr.hw.clk, + &gcc_ipq4019_cpu_clk_notifier); } static struct platform_driver gcc_ipq4019_driver = { .probe = gcc_ipq4019_probe, + .remove = gcc_ipq4019_remove, .driver = { .name = "qcom,gcc-ipq4019", .of_match_table = gcc_ipq4019_match_table, diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c index 581a17f67379..b99dd406e907 100644 --- a/drivers/clk/qcom/gcc-mdm9615.c +++ b/drivers/clk/qcom/gcc-mdm9615.c @@ -1563,6 +1563,34 @@ static struct clk_branch rpm_msg_ram_h_clk = { }, }; +static struct clk_branch ebi2_clk = { + .hwcg_reg = 0x2664, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 24, + .clkr = { + .enable_reg = 0x2664, + .enable_mask = BIT(6) | BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "ebi2_clk", + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch ebi2_aon_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x2664, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "ebi2_aon_clk", + .ops = &clk_branch_ops, + }, + }, +}; + static struct clk_hw *gcc_mdm9615_hws[] = { &cxo.hw, }; @@ -1637,6 +1665,8 @@ static struct clk_regmap *gcc_mdm9615_clks[] = { [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, + [EBI2_CLK] = &ebi2_clk.clkr, + [EBI2_AON_CLK] = &ebi2_aon_clk.clkr, }; static const struct qcom_reset_map gcc_mdm9615_resets[] = { diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c index 8afd8304a070..7983288d9141 100644 --- a/drivers/clk/qcom/gcc-msm8994.c +++ b/drivers/clk/qcom/gcc-msm8994.c @@ -1888,6 +1888,23 @@ static struct clk_branch gcc_sdcc1_apps_clk = { }, }; +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x04c8, + .clkr = { + .enable_reg = 0x04c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) + { + .name = "gcc_sdcc1_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_sdcc2_apps_clk = { .halt_reg = 0x0504, .clkr = { @@ -2231,6 +2248,7 @@ static struct clk_regmap *gcc_msm8994_clocks[] = { [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, [GCC_SDCC3_APPS_CLK] = &gcc_sdcc3_apps_clk.clkr, [GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, [GCC_SYS_NOC_UFS_AXI_CLK] = &gcc_sys_noc_ufs_axi_clk.clkr, [GCC_SYS_NOC_USB3_AXI_CLK] = &gcc_sys_noc_usb3_axi_clk.clkr, [GCC_TSIF_REF_CLK] = &gcc_tsif_ref_clk.clkr, diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 4b1fc1730d29..8abc200d4fd3 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -3448,6 +3448,7 @@ static const struct qcom_reset_map gcc_msm8996_resets[] = { [GCC_MSMPU_BCR] = { 0x8d000 }, [GCC_MSS_Q6_BCR] = { 0x8e000 }, [GCC_QREFS_VBG_CAL_BCR] = { 0x88020 }, + [GCC_MSS_RESTART] = { 0x8f008 }, }; static const struct regmap_config gcc_msm8996_regmap_config = { diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 288186cce0ae..a4f3580587b7 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -63,11 +63,26 @@ static int gdsc_hwctrl(struct gdsc *sc, bool en) return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val); } +static int gdsc_poll_status(struct gdsc *sc, unsigned int reg, bool en) +{ + ktime_t start; + + start = ktime_get(); + do { + if (gdsc_is_enabled(sc, reg) == en) + return 0; + } while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US); + + if (gdsc_is_enabled(sc, reg) == en) + return 0; + + return -ETIMEDOUT; +} + static int gdsc_toggle_logic(struct gdsc *sc, bool en) { int ret; u32 val = en ? 0 : SW_COLLAPSE_MASK; - ktime_t start; unsigned int status_reg = sc->gdscr; ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val); @@ -100,16 +115,7 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en) udelay(1); } - start = ktime_get(); - do { - if (gdsc_is_enabled(sc, status_reg) == en) - return 0; - } while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US); - - if (gdsc_is_enabled(sc, status_reg) == en) - return 0; - - return -ETIMEDOUT; + return gdsc_poll_status(sc, status_reg, en); } static inline int gdsc_deassert_reset(struct gdsc *sc) @@ -188,8 +194,20 @@ static int gdsc_enable(struct generic_pm_domain *domain) udelay(1); /* Turn on HW trigger mode if supported */ - if (sc->flags & HW_CTRL) - return gdsc_hwctrl(sc, true); + if (sc->flags & HW_CTRL) { + ret = gdsc_hwctrl(sc, true); + if (ret) + return ret; + /* + * Wait for the GDSC to go through a power down and + * up cycle. In case a firmware ends up polling status + * bits for the gdsc, it might read an 'on' status before + * the GDSC can finish the power cycle. + * We wait 1us before returning to ensure the firmware + * can't immediately poll the status bits. + */ + udelay(1); + } return 0; } @@ -204,9 +222,23 @@ static int gdsc_disable(struct generic_pm_domain *domain) /* Turn off HW trigger mode if supported */ if (sc->flags & HW_CTRL) { + unsigned int reg; + ret = gdsc_hwctrl(sc, false); if (ret < 0) return ret; + /* + * Wait for the GDSC to go through a power down and + * up cycle. In case we end up polling status + * bits for the gdsc before the power cycle is completed + * it might read an 'on' status wrongly. + */ + udelay(1); + + reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr; + ret = gdsc_poll_status(sc, reg, true); + if (ret) + return ret; } if (sc->pwrsts & PWRSTS_OFF) diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index b533f99550e1..4067216bf31f 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -91,6 +91,12 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) value |= bitmask; cpg_mstp_write(group, value, group->smstpcr); + if (!group->mstpsr) { + /* dummy read to ensure write has completed */ + cpg_mstp_read(group, group->smstpcr); + barrier_data(group->smstpcr); + } + spin_unlock_irqrestore(&group->lock, flags); if (!enable || !group->mstpsr) @@ -141,9 +147,9 @@ static const struct clk_ops cpg_mstp_clock_ops = { .is_enabled = cpg_mstp_clock_is_enabled, }; -static struct clk * __init -cpg_mstp_clock_register(const char *name, const char *parent_name, - unsigned int index, struct mstp_clock_group *group) +static struct clk * __init cpg_mstp_clock_register(const char *name, + const char *parent_name, unsigned int index, + struct mstp_clock_group *group) { struct clk_init_data init; struct mstp_clock *clock; @@ -158,6 +164,11 @@ cpg_mstp_clock_register(const char *name, const char *parent_name, init.name = name; init.ops = &cpg_mstp_clock_ops; init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + /* INTC-SYS is the module clock of the GIC, and must not be disabled */ + if (!strcmp(name, "intc-sys")) { + pr_debug("MSTP %s setting CLK_IS_CRITICAL\n", name); + init.flags |= CLK_IS_CRITICAL; + } init.parent_names = &parent_name; init.num_parents = 1; diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index 50698a7d9074..bfffdb00df97 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -221,6 +221,7 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { DEF_MOD("can-if0", 916, R8A7795_CLK_S3D4), DEF_MOD("i2c6", 918, R8A7795_CLK_S3D2), DEF_MOD("i2c5", 919, R8A7795_CLK_S3D2), + DEF_MOD("i2c-dvfs", 926, R8A7795_CLK_CP), DEF_MOD("i2c4", 927, R8A7795_CLK_S3D2), DEF_MOD("i2c3", 928, R8A7795_CLK_S3D2), DEF_MOD("i2c2", 929, R8A7795_CLK_S3D2), diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index 7d298c57a3e0..11e084a56b0d 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -103,7 +103,9 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { DEF_FIXED("cl", R8A7796_CLK_CL, CLK_PLL1_DIV2, 48, 1), DEF_FIXED("cp", R8A7796_CLK_CP, CLK_EXTAL, 2, 1), + DEF_DIV6P1("canfd", R8A7796_CLK_CANFD, CLK_PLL1_DIV4, 0x244), DEF_DIV6P1("csi0", R8A7796_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), + DEF_DIV6P1("mso", R8A7796_CLK_MSO, CLK_PLL1_DIV4, 0x014), DEF_DIV6_RO("osc", R8A7796_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8), DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32), @@ -117,6 +119,10 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { DEF_MOD("scif3", 204, R8A7796_CLK_S3D4), DEF_MOD("scif1", 206, R8A7796_CLK_S3D4), DEF_MOD("scif0", 207, R8A7796_CLK_S3D4), + DEF_MOD("msiof3", 208, R8A7796_CLK_MSO), + DEF_MOD("msiof2", 209, R8A7796_CLK_MSO), + DEF_MOD("msiof1", 210, R8A7796_CLK_MSO), + DEF_MOD("msiof0", 211, R8A7796_CLK_MSO), DEF_MOD("sys-dmac2", 217, R8A7796_CLK_S0D3), DEF_MOD("sys-dmac1", 218, R8A7796_CLK_S0D3), DEF_MOD("sys-dmac0", 219, R8A7796_CLK_S0D3), @@ -181,8 +187,12 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { DEF_MOD("gpio2", 910, R8A7796_CLK_S3D4), DEF_MOD("gpio1", 911, R8A7796_CLK_S3D4), DEF_MOD("gpio0", 912, R8A7796_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A7796_CLK_S3D2), + DEF_MOD("can-if1", 915, R8A7796_CLK_S3D4), + DEF_MOD("can-if0", 916, R8A7796_CLK_S3D4), DEF_MOD("i2c6", 918, R8A7796_CLK_S0D6), DEF_MOD("i2c5", 919, R8A7796_CLK_S0D6), + DEF_MOD("i2c-dvfs", 926, R8A7796_CLK_CP), DEF_MOD("i2c4", 927, R8A7796_CLK_S0D6), DEF_MOD("i2c3", 928, R8A7796_CLK_S0D6), DEF_MOD("i2c2", 929, R8A7796_CLK_S3D2), diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 8359ce75db7a..eadcbd43ff88 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -16,6 +16,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clk/renesas.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/init.h> #include <linux/mod_devicetable.h> @@ -25,6 +26,7 @@ #include <linux/platform_device.h> #include <linux/pm_clock.h> #include <linux/pm_domain.h> +#include <linux/reset-controller.h> #include <linux/slab.h> #include <dt-bindings/clock/renesas-cpg-mssr.h> @@ -43,7 +45,7 @@ * Module Standby and Software Reset register offets. * * If the registers exist, these are valid for SH-Mobile, R-Mobile, - * R-Car Gen 2, and R-Car Gen 3. + * R-Car Gen2, R-Car Gen3, and RZ/G1. * These are NOT valid for R-Car Gen1 and RZ/A1! */ @@ -96,18 +98,22 @@ static const u16 srcr[] = { /** * Clock Pulse Generator / Module Standby and Software Reset Private Data * + * @rcdev: Optional reset controller entity * @dev: CPG/MSSR device * @base: CPG/MSSR register block base address - * @mstp_lock: protects writes to SMSTPCR + * @rmw_lock: protects RMW register accesses * @clks: Array containing all Core and Module Clocks * @num_core_clks: Number of Core Clocks in clks[] * @num_mod_clks: Number of Module Clocks in clks[] * @last_dt_core_clk: ID of the last Core Clock exported to DT */ struct cpg_mssr_priv { +#ifdef CONFIG_RESET_CONTROLLER + struct reset_controller_dev rcdev; +#endif struct device *dev; void __iomem *base; - spinlock_t mstp_lock; + spinlock_t rmw_lock; struct clk **clks; unsigned int num_core_clks; @@ -144,7 +150,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) dev_dbg(dev, "MSTP %u%02u/%pC %s\n", reg, bit, hw->clk, enable ? "ON" : "OFF"); - spin_lock_irqsave(&priv->mstp_lock, flags); + spin_lock_irqsave(&priv->rmw_lock, flags); value = readl(priv->base + SMSTPCR(reg)); if (enable) @@ -153,7 +159,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) value |= bitmask; writel(value, priv->base + SMSTPCR(reg)); - spin_unlock_irqrestore(&priv->mstp_lock, flags); + spin_unlock_irqrestore(&priv->rmw_lock, flags); if (!enable) return 0; @@ -346,17 +352,10 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod, init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; for (i = 0; i < info->num_crit_mod_clks; i++) if (id == info->crit_mod_clks[i]) { -#ifdef CLK_ENABLE_HAND_OFF - dev_dbg(dev, "MSTP %s setting CLK_ENABLE_HAND_OFF\n", + dev_dbg(dev, "MSTP %s setting CLK_IS_CRITICAL\n", mod->name); - init.flags |= CLK_ENABLE_HAND_OFF; + init.flags |= CLK_IS_CRITICAL; break; -#else - dev_dbg(dev, "Ignoring MSTP %s to prevent disabling\n", - mod->name); - kfree(clock); - return; -#endif } parent_name = __clk_get_name(parent); @@ -501,6 +500,122 @@ static int __init cpg_mssr_add_clk_domain(struct device *dev, return 0; } +#ifdef CONFIG_RESET_CONTROLLER + +#define rcdev_to_priv(x) container_of(x, struct cpg_mssr_priv, rcdev) + +static int cpg_mssr_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev); + unsigned int reg = id / 32; + unsigned int bit = id % 32; + u32 bitmask = BIT(bit); + unsigned long flags; + u32 value; + + dev_dbg(priv->dev, "reset %u%02u\n", reg, bit); + + /* Reset module */ + spin_lock_irqsave(&priv->rmw_lock, flags); + value = readl(priv->base + SRCR(reg)); + value |= bitmask; + writel(value, priv->base + SRCR(reg)); + spin_unlock_irqrestore(&priv->rmw_lock, flags); + + /* Wait for at least one cycle of the RCLK clock (@ ca. 32 kHz) */ + udelay(35); + + /* Release module from reset state */ + writel(bitmask, priv->base + SRSTCLR(reg)); + + return 0; +} + +static int cpg_mssr_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev); + unsigned int reg = id / 32; + unsigned int bit = id % 32; + u32 bitmask = BIT(bit); + unsigned long flags; + u32 value; + + dev_dbg(priv->dev, "assert %u%02u\n", reg, bit); + + spin_lock_irqsave(&priv->rmw_lock, flags); + value = readl(priv->base + SRCR(reg)); + value |= bitmask; + writel(value, priv->base + SRCR(reg)); + spin_unlock_irqrestore(&priv->rmw_lock, flags); + return 0; +} + +static int cpg_mssr_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev); + unsigned int reg = id / 32; + unsigned int bit = id % 32; + u32 bitmask = BIT(bit); + + dev_dbg(priv->dev, "deassert %u%02u\n", reg, bit); + + writel(bitmask, priv->base + SRSTCLR(reg)); + return 0; +} + +static int cpg_mssr_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev); + unsigned int reg = id / 32; + unsigned int bit = id % 32; + u32 bitmask = BIT(bit); + + return !!(readl(priv->base + SRCR(reg)) & bitmask); +} + +static const struct reset_control_ops cpg_mssr_reset_ops = { + .reset = cpg_mssr_reset, + .assert = cpg_mssr_assert, + .deassert = cpg_mssr_deassert, + .status = cpg_mssr_status, +}; + +static int cpg_mssr_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct cpg_mssr_priv *priv = rcdev_to_priv(rcdev); + unsigned int unpacked = reset_spec->args[0]; + unsigned int idx = MOD_CLK_PACK(unpacked); + + if (unpacked % 100 > 31 || idx >= rcdev->nr_resets) { + dev_err(priv->dev, "Invalid reset index %u\n", unpacked); + return -EINVAL; + } + + return idx; +} + +static int cpg_mssr_reset_controller_register(struct cpg_mssr_priv *priv) +{ + priv->rcdev.ops = &cpg_mssr_reset_ops; + priv->rcdev.of_node = priv->dev->of_node; + priv->rcdev.of_reset_n_cells = 1; + priv->rcdev.of_xlate = cpg_mssr_reset_xlate; + priv->rcdev.nr_resets = priv->num_mod_clks; + return devm_reset_controller_register(priv->dev, &priv->rcdev); +} + +#else /* !CONFIG_RESET_CONTROLLER */ +static inline int cpg_mssr_reset_controller_register(struct cpg_mssr_priv *priv) +{ + return 0; +} +#endif /* !CONFIG_RESET_CONTROLLER */ + + static const struct of_device_id cpg_mssr_match[] = { #ifdef CONFIG_ARCH_R8A7743 { @@ -557,7 +672,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev) return -ENOMEM; priv->dev = dev; - spin_lock_init(&priv->mstp_lock); + spin_lock_init(&priv->rmw_lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(dev, res); @@ -598,6 +713,10 @@ static int __init cpg_mssr_probe(struct platform_device *pdev) if (error) return error; + error = cpg_mssr_reset_controller_register(priv); + if (error) + return error; + return 0; } diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 16e098c36f90..141971488f40 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -8,6 +8,7 @@ obj-y += clk-pll.o obj-y += clk-cpu.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o +obj-y += clk-muxgrf.o obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o @@ -16,5 +17,6 @@ obj-y += clk-rk3036.o obj-y += clk-rk3188.o obj-y += clk-rk3228.o obj-y += clk-rk3288.o +obj-y += clk-rk3328.o obj-y += clk-rk3368.o obj-y += clk-rk3399.o diff --git a/drivers/clk/rockchip/clk-muxgrf.c b/drivers/clk/rockchip/clk-muxgrf.c new file mode 100644 index 000000000000..4f291180a26b --- /dev/null +++ b/drivers/clk/rockchip/clk-muxgrf.c @@ -0,0 +1,102 @@ +/* + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include "clk.h" + +struct rockchip_muxgrf_clock { + struct clk_hw hw; + struct regmap *regmap; + u32 reg; + u32 shift; + u32 width; + int flags; +}; + +#define to_muxgrf_clock(_hw) container_of(_hw, struct rockchip_muxgrf_clock, hw) + +static u8 rockchip_muxgrf_get_parent(struct clk_hw *hw) +{ + struct rockchip_muxgrf_clock *mux = to_muxgrf_clock(hw); + unsigned int mask = GENMASK(mux->width - 1, 0); + unsigned int val; + + regmap_read(mux->regmap, mux->reg, &val); + + val >>= mux->shift; + val &= mask; + + return val; +} + +static int rockchip_muxgrf_set_parent(struct clk_hw *hw, u8 index) +{ + struct rockchip_muxgrf_clock *mux = to_muxgrf_clock(hw); + unsigned int mask = GENMASK(mux->width + mux->shift - 1, mux->shift); + unsigned int val; + + val = index; + val <<= mux->shift; + + if (mux->flags & CLK_MUX_HIWORD_MASK) + return regmap_write(mux->regmap, mux->reg, val | (mask << 16)); + else + return regmap_update_bits(mux->regmap, mux->reg, mask, val); +} + +static const struct clk_ops rockchip_muxgrf_clk_ops = { + .get_parent = rockchip_muxgrf_get_parent, + .set_parent = rockchip_muxgrf_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; + +struct clk *rockchip_clk_register_muxgrf(const char *name, + const char *const *parent_names, u8 num_parents, + int flags, struct regmap *regmap, int reg, + int shift, int width, int mux_flags) +{ + struct rockchip_muxgrf_clock *muxgrf_clock; + struct clk_init_data init; + struct clk *clk; + + if (IS_ERR(regmap)) { + pr_err("%s: regmap not available\n", __func__); + return ERR_PTR(-ENOTSUPP); + } + + muxgrf_clock = kmalloc(sizeof(*muxgrf_clock), GFP_KERNEL); + if (!muxgrf_clock) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = flags; + init.num_parents = num_parents; + init.parent_names = parent_names; + init.ops = &rockchip_muxgrf_clk_ops; + + muxgrf_clock->hw.init = &init; + muxgrf_clock->regmap = regmap; + muxgrf_clock->reg = reg; + muxgrf_clock->shift = shift; + muxgrf_clock->width = width; + muxgrf_clock->flags = mux_flags; + + clk = clk_register(NULL, &muxgrf_clock->hw); + if (IS_ERR(clk)) + kfree(muxgrf_clock); + + return clk; +} diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 6ed605776abd..eec51893a7e6 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -29,6 +29,7 @@ #define PLL_MODE_SLOW 0x0 #define PLL_MODE_NORM 0x1 #define PLL_MODE_DEEP 0x2 +#define PLL_RK3328_MODE_MASK 0x1 struct rockchip_clk_pll { struct clk_hw hw; @@ -848,7 +849,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, struct clk *pll_clk, *mux_clk; char pll_name[20]; - if (num_parents != 2) { + if ((pll_type != pll_rk3328 && num_parents != 2) || + (pll_type == pll_rk3328 && num_parents != 1)) { pr_err("%s: needs two parent clocks\n", __func__); return ERR_PTR(-EINVAL); } @@ -865,13 +867,17 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, pll_mux = &pll->pll_mux; pll_mux->reg = ctx->reg_base + mode_offset; pll_mux->shift = mode_shift; - pll_mux->mask = PLL_MODE_MASK; + if (pll_type == pll_rk3328) + pll_mux->mask = PLL_RK3328_MODE_MASK; + else + pll_mux->mask = PLL_MODE_MASK; pll_mux->flags = 0; pll_mux->lock = &ctx->lock; pll_mux->hw.init = &init; if (pll_type == pll_rk3036 || pll_type == pll_rk3066 || + pll_type == pll_rk3328 || pll_type == pll_rk3399) pll_mux->flags |= CLK_MUX_HIWORD_MASK; @@ -884,7 +890,10 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, init.flags = CLK_SET_RATE_PARENT; init.ops = pll->pll_mux_ops; init.parent_names = pll_parents; - init.num_parents = ARRAY_SIZE(pll_parents); + if (pll_type == pll_rk3328) + init.num_parents = 2; + else + init.num_parents = ARRAY_SIZE(pll_parents); mux_clk = clk_register(NULL, &pll_mux->hw); if (IS_ERR(mux_clk)) @@ -918,6 +927,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, switch (pll_type) { case pll_rk3036: + case pll_rk3328: if (!pll->rate_table || IS_ERR(ctx->grf)) init.ops = &rockchip_rk3036_pll_clk_norate_ops; else diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 062ef4960244..00ad0e5f8d66 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -507,8 +507,8 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 11, GFLAGS), GATE(PCLK_EFUSE, "pclk_efuse", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 2, GFLAGS), GATE(PCLK_TZPC, "pclk_tzpc", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 3, GFLAGS), - GATE(0, "pclk_ddrupctl", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 7, GFLAGS), - GATE(0, "pclk_ddrpubl", "pclk_cpu", 0, RK2928_CLKGATE_CON(9), 6, GFLAGS), + GATE(PCLK_DDRUPCTL, "pclk_ddrupctl", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 7, GFLAGS), + GATE(PCLK_PUBL, "pclk_ddrpubl", "pclk_cpu", 0, RK2928_CLKGATE_CON(9), 6, GFLAGS), GATE(0, "pclk_dbg", "pclk_cpu", 0, RK2928_CLKGATE_CON(9), 1, GFLAGS), GATE(PCLK_GRF, "pclk_grf", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 4, GFLAGS), GATE(PCLK_PMU, "pclk_pmu", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 5, GFLAGS), diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 39af05a589b3..68ba7d4105e7 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -198,6 +198,7 @@ PNAME(mux_hsadcout_p) = { "hsadc_src", "ext_hsadc" }; PNAME(mux_edp_24m_p) = { "ext_edp_24m", "xin24m" }; PNAME(mux_tspout_p) = { "cpll", "gpll", "npll", "xin27m" }; +PNAME(mux_aclk_vcodec_pre_p) = { "aclk_vepu", "aclk_vdpu" }; PNAME(mux_usbphy480m_p) = { "sclk_otgphy1_480m", "sclk_otgphy2_480m", "sclk_otgphy0_480m" }; PNAME(mux_hsicphy480m_p) = { "cpll", "gpll", "usbphy480m_src" }; @@ -398,14 +399,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(3), 11, GFLAGS), - /* - * We use aclk_vdpu by default GRF_SOC_CON0[7] setting in system, - * so we ignore the mux and make clocks nodes as following, - */ - GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vdpu", 0, + MUXGRF(0, "aclk_vcodec_pre", mux_aclk_vcodec_pre_p, 0, + RK3288_GRF_SOC_CON(0), 7, 1, MFLAGS), + GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vcodec_pre", 0, RK3288_CLKGATE_CON(9), 0, GFLAGS), - FACTOR_GATE(0, "hclk_vcodec_pre", "aclk_vdpu", 0, 1, 4, + FACTOR_GATE(0, "hclk_vcodec_pre", "aclk_vcodec_pre", 0, 1, 4, RK3288_CLKGATE_CON(3), 10, GFLAGS), GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0, @@ -469,7 +468,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE_NODIV(0, "vip_src", mux_pll_src_cpll_gpll_p, 0, RK3288_CLKSEL_CON(26), 8, 1, MFLAGS, RK3288_CLKGATE_CON(3), 7, GFLAGS), - COMPOSITE_NOGATE(0, "sclk_vip_out", mux_vip_out_p, 0, + COMPOSITE_NOGATE(SCLK_VIP_OUT, "sclk_vip_out", mux_vip_out_p, 0, RK3288_CLKSEL_CON(26), 15, 1, MFLAGS, 9, 5, DFLAGS), DIV(0, "pclk_pd_alive", "gpll", 0, @@ -690,7 +689,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { /* aclk_peri gates */ GATE(0, "aclk_peri_axi_matrix", "aclk_peri", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(6), 2, GFLAGS), GATE(ACLK_DMAC2, "aclk_dmac2", "aclk_peri", 0, RK3288_CLKGATE_CON(6), 3, GFLAGS), - GATE(0, "aclk_peri_niu", "aclk_peri", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(7), 11, GFLAGS), + GATE(0, "aclk_peri_niu", "aclk_peri", 0, RK3288_CLKGATE_CON(7), 11, GFLAGS), GATE(ACLK_MMU, "aclk_mmu", "aclk_peri", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(8), 12, GFLAGS), GATE(ACLK_GMAC, "aclk_gmac", "aclk_peri", 0, RK3288_CLKGATE_CON(8), 0, GFLAGS), GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 0, RK3288_CLKGATE_CON(8), 2, GFLAGS), @@ -753,12 +752,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(PCLK_GPIO5, "pclk_gpio5", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 5, GFLAGS), GATE(PCLK_GPIO6, "pclk_gpio6", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 6, GFLAGS), GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(14), 11, GFLAGS), - GATE(0, "pclk_alive_niu", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(14), 12, GFLAGS), + GATE(0, "pclk_alive_niu", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 12, GFLAGS), /* pclk_pd_pmu gates */ GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 0, GFLAGS), GATE(0, "pclk_intmem1", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 1, GFLAGS), - GATE(0, "pclk_pmu_niu", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 2, GFLAGS), + GATE(0, "pclk_pmu_niu", "pclk_pd_pmu", 0, RK3288_CLKGATE_CON(17), 2, GFLAGS), GATE(PCLK_SGRF, "pclk_sgrf", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 3, GFLAGS), GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pd_pmu", 0, RK3288_CLKGATE_CON(17), 4, GFLAGS), @@ -767,7 +766,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(HCLK_VOP0, "hclk_vop0", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 6, GFLAGS), GATE(HCLK_VOP1, "hclk_vop1", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 8, GFLAGS), GATE(HCLK_VIO_AHB_ARBI, "hclk_vio_ahb_arbi", "hclk_vio", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(15), 9, GFLAGS), - GATE(HCLK_VIO_NIU, "hclk_vio_niu", "hclk_vio", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(15), 10, GFLAGS), + GATE(HCLK_VIO_NIU, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS), GATE(HCLK_VIP, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS), GATE(HCLK_IEP, "hclk_iep", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 3, GFLAGS), GATE(HCLK_ISP, "hclk_isp", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 1, GFLAGS), @@ -783,17 +782,17 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { /* aclk_vio0 gates */ GATE(ACLK_VOP0, "aclk_vop0", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 5, GFLAGS), GATE(ACLK_IEP, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS), - GATE(ACLK_VIO0_NIU, "aclk_vio0_niu", "aclk_vio0", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(15), 11, GFLAGS), + GATE(ACLK_VIO0_NIU, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS), GATE(ACLK_VIP, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS), /* aclk_vio1 gates */ GATE(ACLK_VOP1, "aclk_vop1", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 7, GFLAGS), GATE(ACLK_ISP, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS), - GATE(ACLK_VIO1_NIU, "aclk_vio1_niu", "aclk_vio1", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(15), 12, GFLAGS), + GATE(ACLK_VIO1_NIU, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS), /* aclk_rga_pre gates */ GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 0, GFLAGS), - GATE(ACLK_RGA_NIU, "aclk_rga_niu", "aclk_rga_pre", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(15), 13, GFLAGS), + GATE(ACLK_RGA_NIU, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS), /* * Other ungrouped clocks. @@ -801,15 +800,22 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(0, "pclk_vip_in", "ext_vip", 0, RK3288_CLKGATE_CON(16), 0, GFLAGS), INVERTER(0, "pclk_vip", "pclk_vip_in", RK3288_CLKSEL_CON(29), 4, IFLAGS), - GATE(0, "pclk_isp_in", "ext_isp", 0, RK3288_CLKGATE_CON(16), 3, GFLAGS), + GATE(PCLK_ISP_IN, "pclk_isp_in", "ext_isp", 0, RK3288_CLKGATE_CON(16), 3, GFLAGS), INVERTER(0, "pclk_isp", "pclk_isp_in", RK3288_CLKSEL_CON(29), 3, IFLAGS), }; static const char *const rk3288_critical_clocks[] __initconst = { "aclk_cpu", "aclk_peri", + "aclk_peri_niu", + "aclk_vio0_niu", + "aclk_vio1_niu", + "aclk_rga_niu", "hclk_peri", + "hclk_vio_niu", + "pclk_alive_niu", "pclk_pd_pmu", + "pclk_pmu_niu", }; static void __iomem *rk3288_cru_base; diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c new file mode 100644 index 000000000000..1e384e143504 --- /dev/null +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -0,0 +1,895 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Elaine <zhangqing@rock-chips.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/syscore_ops.h> +#include <dt-bindings/clock/rk3328-cru.h> +#include "clk.h" + +#define RK3328_GRF_SOC_STATUS0 0x480 +#define RK3328_GRF_MAC_CON1 0x904 +#define RK3328_GRF_MAC_CON2 0x908 + +enum rk3328_plls { + apll, dpll, cpll, gpll, npll, +}; + +static struct rockchip_pll_rate_table rk3328_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0), + RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0), + RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0), + RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0), + RK3036_PLL_RATE(1488000000, 1, 62, 1, 1, 1, 0), + RK3036_PLL_RATE(1464000000, 1, 61, 1, 1, 1, 0), + RK3036_PLL_RATE(1440000000, 1, 60, 1, 1, 1, 0), + RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0), + RK3036_PLL_RATE(1392000000, 1, 58, 1, 1, 1, 0), + RK3036_PLL_RATE(1368000000, 1, 57, 1, 1, 1, 0), + RK3036_PLL_RATE(1344000000, 1, 56, 1, 1, 1, 0), + RK3036_PLL_RATE(1320000000, 1, 55, 1, 1, 1, 0), + RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0), + RK3036_PLL_RATE(1272000000, 1, 53, 1, 1, 1, 0), + RK3036_PLL_RATE(1248000000, 1, 52, 1, 1, 1, 0), + RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), + RK3036_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0), + RK3036_PLL_RATE(1104000000, 1, 46, 1, 1, 1, 0), + RK3036_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), + RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), + RK3036_PLL_RATE(984000000, 1, 82, 2, 1, 1, 0), + RK3036_PLL_RATE(960000000, 1, 80, 2, 1, 1, 0), + RK3036_PLL_RATE(936000000, 1, 78, 2, 1, 1, 0), + RK3036_PLL_RATE(912000000, 1, 76, 2, 1, 1, 0), + RK3036_PLL_RATE(900000000, 4, 300, 2, 1, 1, 0), + RK3036_PLL_RATE(888000000, 1, 74, 2, 1, 1, 0), + RK3036_PLL_RATE(864000000, 1, 72, 2, 1, 1, 0), + RK3036_PLL_RATE(840000000, 1, 70, 2, 1, 1, 0), + RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + RK3036_PLL_RATE(800000000, 6, 400, 2, 1, 1, 0), + RK3036_PLL_RATE(700000000, 6, 350, 2, 1, 1, 0), + RK3036_PLL_RATE(696000000, 1, 58, 2, 1, 1, 0), + RK3036_PLL_RATE(600000000, 1, 75, 3, 1, 1, 0), + RK3036_PLL_RATE(594000000, 2, 99, 2, 1, 1, 0), + RK3036_PLL_RATE(504000000, 1, 63, 3, 1, 1, 0), + RK3036_PLL_RATE(500000000, 6, 250, 2, 1, 1, 0), + RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0), + RK3036_PLL_RATE(312000000, 1, 52, 2, 2, 1, 0), + RK3036_PLL_RATE(216000000, 1, 72, 4, 2, 1, 0), + RK3036_PLL_RATE(96000000, 1, 64, 4, 4, 1, 0), + { /* sentinel */ }, +}; + +static struct rockchip_pll_rate_table rk3328_pll_frac_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1016064000, 3, 127, 1, 1, 0, 134217), + /* vco = 1016064000 */ + RK3036_PLL_RATE(983040000, 24, 983, 1, 1, 0, 671088), + /* vco = 983040000 */ + RK3036_PLL_RATE(491520000, 24, 983, 2, 1, 0, 671088), + /* vco = 983040000 */ + RK3036_PLL_RATE(61440000, 6, 215, 7, 2, 0, 671088), + /* vco = 860156000 */ + RK3036_PLL_RATE(56448000, 12, 451, 4, 4, 0, 9797894), + /* vco = 903168000 */ + RK3036_PLL_RATE(40960000, 12, 409, 4, 5, 0, 10066329), + /* vco = 819200000 */ + { /* sentinel */ }, +}; + +#define RK3328_DIV_ACLKM_MASK 0x7 +#define RK3328_DIV_ACLKM_SHIFT 4 +#define RK3328_DIV_PCLK_DBG_MASK 0xf +#define RK3328_DIV_PCLK_DBG_SHIFT 0 + +#define RK3328_CLKSEL1(_aclk_core, _pclk_dbg) \ +{ \ + .reg = RK3328_CLKSEL_CON(1), \ + .val = HIWORD_UPDATE(_aclk_core, RK3328_DIV_ACLKM_MASK, \ + RK3328_DIV_ACLKM_SHIFT) | \ + HIWORD_UPDATE(_pclk_dbg, RK3328_DIV_PCLK_DBG_MASK, \ + RK3328_DIV_PCLK_DBG_SHIFT), \ +} + +#define RK3328_CPUCLK_RATE(_prate, _aclk_core, _pclk_dbg) \ +{ \ + .prate = _prate, \ + .divs = { \ + RK3328_CLKSEL1(_aclk_core, _pclk_dbg), \ + }, \ +} + +static struct rockchip_cpuclk_rate_table rk3328_cpuclk_rates[] __initdata = { + RK3328_CPUCLK_RATE(1800000000, 1, 7), + RK3328_CPUCLK_RATE(1704000000, 1, 7), + RK3328_CPUCLK_RATE(1608000000, 1, 7), + RK3328_CPUCLK_RATE(1512000000, 1, 7), + RK3328_CPUCLK_RATE(1488000000, 1, 5), + RK3328_CPUCLK_RATE(1416000000, 1, 5), + RK3328_CPUCLK_RATE(1392000000, 1, 5), + RK3328_CPUCLK_RATE(1296000000, 1, 5), + RK3328_CPUCLK_RATE(1200000000, 1, 5), + RK3328_CPUCLK_RATE(1104000000, 1, 5), + RK3328_CPUCLK_RATE(1008000000, 1, 5), + RK3328_CPUCLK_RATE(912000000, 1, 5), + RK3328_CPUCLK_RATE(816000000, 1, 3), + RK3328_CPUCLK_RATE(696000000, 1, 3), + RK3328_CPUCLK_RATE(600000000, 1, 3), + RK3328_CPUCLK_RATE(408000000, 1, 1), + RK3328_CPUCLK_RATE(312000000, 1, 1), + RK3328_CPUCLK_RATE(216000000, 1, 1), + RK3328_CPUCLK_RATE(96000000, 1, 1), +}; + +static const struct rockchip_cpuclk_reg_data rk3328_cpuclk_data = { + .core_reg = RK3328_CLKSEL_CON(0), + .div_core_shift = 0, + .div_core_mask = 0x1f, + .mux_core_alt = 1, + .mux_core_main = 3, + .mux_core_shift = 6, + .mux_core_mask = 0x3, +}; + +PNAME(mux_pll_p) = { "xin24m" }; + +PNAME(mux_2plls_p) = { "cpll", "gpll" }; +PNAME(mux_gpll_cpll_p) = { "gpll", "cpll" }; +PNAME(mux_cpll_gpll_apll_p) = { "cpll", "gpll", "apll" }; +PNAME(mux_2plls_xin24m_p) = { "cpll", "gpll", "xin24m" }; +PNAME(mux_2plls_hdmiphy_p) = { "cpll", "gpll", + "dummy_hdmiphy" }; +PNAME(mux_4plls_p) = { "cpll", "gpll", + "dummy_hdmiphy", + "usb480m" }; +PNAME(mux_2plls_u480m_p) = { "cpll", "gpll", + "usb480m" }; +PNAME(mux_2plls_24m_u480m_p) = { "cpll", "gpll", + "xin24m", "usb480m" }; + +PNAME(mux_ddrphy_p) = { "dpll", "apll", "cpll" }; +PNAME(mux_armclk_p) = { "apll_core", + "gpll_core", + "dpll_core", + "npll_core"}; +PNAME(mux_hdmiphy_p) = { "hdmi_phy", "xin24m" }; +PNAME(mux_usb480m_p) = { "usb480m_phy", + "xin24m" }; + +PNAME(mux_i2s0_p) = { "clk_i2s0_div", + "clk_i2s0_frac", + "xin12m", + "xin12m" }; +PNAME(mux_i2s1_p) = { "clk_i2s1_div", + "clk_i2s1_frac", + "clkin_i2s1", + "xin12m" }; +PNAME(mux_i2s2_p) = { "clk_i2s2_div", + "clk_i2s2_frac", + "clkin_i2s2", + "xin12m" }; +PNAME(mux_i2s1out_p) = { "clk_i2s1", "xin12m"}; +PNAME(mux_i2s2out_p) = { "clk_i2s2", "xin12m" }; +PNAME(mux_spdif_p) = { "clk_spdif_div", + "clk_spdif_frac", + "xin12m", + "xin12m" }; +PNAME(mux_uart0_p) = { "clk_uart0_div", + "clk_uart0_frac", + "xin24m" }; +PNAME(mux_uart1_p) = { "clk_uart1_div", + "clk_uart1_frac", + "xin24m" }; +PNAME(mux_uart2_p) = { "clk_uart2_div", + "clk_uart2_frac", + "xin24m" }; + +PNAME(mux_sclk_cif_p) = { "clk_cif_src", + "xin24m" }; +PNAME(mux_dclk_lcdc_p) = { "hdmiphy", + "dclk_lcdc_src" }; +PNAME(mux_aclk_peri_pre_p) = { "cpll_peri", + "gpll_peri", + "hdmiphy_peri" }; +PNAME(mux_ref_usb3otg_src_p) = { "xin24m", + "clk_usb3otg_ref" }; +PNAME(mux_xin24m_32k_p) = { "xin24m", + "clk_rtc32k" }; +PNAME(mux_mac2io_src_p) = { "clk_mac2io_src", + "gmac_clkin" }; +PNAME(mux_mac2phy_src_p) = { "clk_mac2phy_src", + "phy_50m_out" }; + +static struct rockchip_pll_clock rk3328_pll_clks[] __initdata = { + [apll] = PLL(pll_rk3328, PLL_APLL, "apll", mux_pll_p, + 0, RK3328_PLL_CON(0), + RK3328_MODE_CON, 0, 4, 0, rk3328_pll_frac_rates), + [dpll] = PLL(pll_rk3328, PLL_DPLL, "dpll", mux_pll_p, + 0, RK3328_PLL_CON(8), + RK3328_MODE_CON, 4, 3, 0, NULL), + [cpll] = PLL(pll_rk3328, PLL_CPLL, "cpll", mux_pll_p, + 0, RK3328_PLL_CON(16), + RK3328_MODE_CON, 8, 2, 0, rk3328_pll_rates), + [gpll] = PLL(pll_rk3328, PLL_GPLL, "gpll", mux_pll_p, + 0, RK3328_PLL_CON(24), + RK3328_MODE_CON, 12, 1, 0, rk3328_pll_frac_rates), + [npll] = PLL(pll_rk3328, PLL_NPLL, "npll", mux_pll_p, + 0, RK3328_PLL_CON(40), + RK3328_MODE_CON, 1, 0, 0, rk3328_pll_rates), +}; + +#define MFLAGS CLK_MUX_HIWORD_MASK +#define DFLAGS CLK_DIVIDER_HIWORD_MASK +#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE) + +static struct rockchip_clk_branch rk3328_i2s0_fracmux __initdata = + MUX(0, "i2s0_pre", mux_i2s0_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(6), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3328_i2s1_fracmux __initdata = + MUX(0, "i2s1_pre", mux_i2s1_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(8), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3328_i2s2_fracmux __initdata = + MUX(0, "i2s2_pre", mux_i2s2_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(10), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3328_spdif_fracmux __initdata = + MUX(SCLK_SPDIF, "sclk_spdif", mux_spdif_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(12), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3328_uart0_fracmux __initdata = + MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(14), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3328_uart1_fracmux __initdata = + MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(16), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3328_uart2_fracmux __initdata = + MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(18), 8, 2, MFLAGS); + +static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { + /* + * Clock-Architecture Diagram 1 + */ + + DIV(0, "clk_24m", "xin24m", CLK_IGNORE_UNUSED, + RK3328_CLKSEL_CON(2), 8, 5, DFLAGS), + COMPOSITE(SCLK_RTC32K, "clk_rtc32k", mux_2plls_xin24m_p, 0, + RK3328_CLKSEL_CON(38), 14, 2, MFLAGS, 0, 14, DFLAGS, + RK3328_CLKGATE_CON(0), 11, GFLAGS), + + /* PD_MISC */ + MUX(HDMIPHY, "hdmiphy", mux_hdmiphy_p, CLK_SET_RATE_PARENT, + RK3328_MISC_CON, 13, 1, MFLAGS), + MUX(USB480M, "usb480m", mux_usb480m_p, CLK_SET_RATE_PARENT, + RK3328_MISC_CON, 15, 1, MFLAGS), + + /* + * Clock-Architecture Diagram 2 + */ + + /* PD_CORE */ + GATE(0, "apll_core", "apll", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(0), 0, GFLAGS), + GATE(0, "gpll_core", "gpll", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(0), 2, GFLAGS), + GATE(0, "dpll_core", "dpll", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(0), 1, GFLAGS), + GATE(0, "npll_core", "npll", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(0), 12, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_dbg", "armclk", CLK_IGNORE_UNUSED, + RK3328_CLKSEL_CON(1), 0, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3328_CLKGATE_CON(7), 0, GFLAGS), + COMPOSITE_NOMUX(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED, + RK3328_CLKSEL_CON(1), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, + RK3328_CLKGATE_CON(7), 1, GFLAGS), + GATE(0, "aclk_core_niu", "aclk_core", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(13), 0, GFLAGS), + GATE(0, "aclk_gic400", "aclk_core", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(13), 1, GFLAGS), + + GATE(0, "clk_jtag", "jtag_clkin", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(7), 2, GFLAGS), + + /* PD_GPU */ + COMPOSITE(0, "aclk_gpu_pre", mux_4plls_p, 0, + RK3328_CLKSEL_CON(44), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 6, GFLAGS), + GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(14), 0, GFLAGS), + GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(14), 1, GFLAGS), + + /* PD_DDR */ + COMPOSITE(0, "clk_ddr", mux_ddrphy_p, CLK_IGNORE_UNUSED, + RK3328_CLKSEL_CON(3), 8, 2, MFLAGS, 0, 3, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK3328_CLKGATE_CON(0), 4, GFLAGS), + GATE(0, "clk_ddrmsch", "clk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 6, GFLAGS), + GATE(0, "clk_ddrupctl", "clk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 5, GFLAGS), + GATE(0, "aclk_ddrupctl", "clk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 4, GFLAGS), + GATE(0, "clk_ddrmon", "xin24m", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(0), 6, GFLAGS), + + COMPOSITE(PCLK_DDR, "pclk_ddr", mux_2plls_hdmiphy_p, 0, + RK3328_CLKSEL_CON(4), 13, 2, MFLAGS, 8, 3, DFLAGS, + RK3328_CLKGATE_CON(7), 4, GFLAGS), + GATE(0, "pclk_ddrupctl", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 1, GFLAGS), + GATE(0, "pclk_ddr_msch", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 2, GFLAGS), + GATE(0, "pclk_ddr_mon", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 3, GFLAGS), + GATE(0, "pclk_ddrstdby", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 7, GFLAGS), + GATE(0, "pclk_ddr_grf", "pclk_ddr", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(18), 9, GFLAGS), + + /* + * Clock-Architecture Diagram 3 + */ + + /* PD_BUS */ + COMPOSITE(ACLK_BUS_PRE, "aclk_bus_pre", mux_2plls_hdmiphy_p, 0, + RK3328_CLKSEL_CON(0), 13, 2, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(8), 0, GFLAGS), + COMPOSITE_NOMUX(HCLK_BUS_PRE, "hclk_bus_pre", "aclk_bus_pre", 0, + RK3328_CLKSEL_CON(1), 8, 2, DFLAGS, + RK3328_CLKGATE_CON(8), 1, GFLAGS), + COMPOSITE_NOMUX(PCLK_BUS_PRE, "pclk_bus_pre", "aclk_bus_pre", 0, + RK3328_CLKSEL_CON(1), 12, 3, DFLAGS, + RK3328_CLKGATE_CON(8), 2, GFLAGS), + GATE(0, "pclk_bus", "pclk_bus_pre", 0, + RK3328_CLKGATE_CON(8), 3, GFLAGS), + GATE(0, "pclk_phy_pre", "pclk_bus_pre", 0, + RK3328_CLKGATE_CON(8), 4, GFLAGS), + + COMPOSITE(SCLK_TSP, "clk_tsp", mux_2plls_p, 0, + RK3328_CLKSEL_CON(21), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(2), 5, GFLAGS), + GATE(0, "clk_hsadc_tsp", "ext_gpio3a2", 0, + RK3328_CLKGATE_CON(17), 13, GFLAGS), + + /* PD_I2S */ + COMPOSITE(0, "clk_i2s0_div", mux_2plls_p, 0, + RK3328_CLKSEL_CON(6), 15, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(1), 1, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s0_frac", "clk_i2s0_div", CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(7), 0, + RK3328_CLKGATE_CON(1), 2, GFLAGS, + &rk3328_i2s0_fracmux), + GATE(SCLK_I2S0, "clk_i2s0", "i2s0_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(1), 3, GFLAGS), + + COMPOSITE(0, "clk_i2s1_div", mux_2plls_p, 0, + RK3328_CLKSEL_CON(8), 15, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(1), 4, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_div", CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(9), 0, + RK3328_CLKGATE_CON(1), 5, GFLAGS, + &rk3328_i2s1_fracmux), + GATE(SCLK_I2S1, "clk_i2s1", "i2s1_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(0), 6, GFLAGS), + COMPOSITE_NODIV(SCLK_I2S1_OUT, "i2s1_out", mux_i2s1out_p, 0, + RK3328_CLKSEL_CON(8), 12, 1, MFLAGS, + RK3328_CLKGATE_CON(1), 7, GFLAGS), + + COMPOSITE(0, "clk_i2s2_div", mux_2plls_p, 0, + RK3328_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(1), 8, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_div", CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(11), 0, + RK3328_CLKGATE_CON(1), 9, GFLAGS, + &rk3328_i2s2_fracmux), + GATE(SCLK_I2S2, "clk_i2s2", "i2s2_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(1), 10, GFLAGS), + COMPOSITE_NODIV(SCLK_I2S2_OUT, "i2s2_out", mux_i2s2out_p, 0, + RK3328_CLKSEL_CON(10), 12, 1, MFLAGS, + RK3328_CLKGATE_CON(1), 11, GFLAGS), + + COMPOSITE(0, "clk_spdif_div", mux_2plls_p, 0, + RK3328_CLKSEL_CON(12), 15, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(1), 12, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_spdif_frac", "clk_spdif_div", CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(13), 0, + RK3328_CLKGATE_CON(1), 13, GFLAGS, + &rk3328_spdif_fracmux), + + /* PD_UART */ + COMPOSITE(0, "clk_uart0_div", mux_2plls_u480m_p, 0, + RK3328_CLKSEL_CON(14), 12, 2, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(1), 14, GFLAGS), + COMPOSITE(0, "clk_uart1_div", mux_2plls_u480m_p, 0, + RK3328_CLKSEL_CON(16), 12, 2, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 0, GFLAGS), + COMPOSITE(0, "clk_uart2_div", mux_2plls_u480m_p, 0, + RK3328_CLKSEL_CON(18), 12, 2, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 2, GFLAGS), + COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_div", CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(15), 0, + RK3328_CLKGATE_CON(1), 15, GFLAGS, + &rk3328_uart0_fracmux), + COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_div", CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(17), 0, + RK3328_CLKGATE_CON(2), 1, GFLAGS, + &rk3328_uart1_fracmux), + COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_div", CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(19), 0, + RK3328_CLKGATE_CON(2), 3, GFLAGS, + &rk3328_uart2_fracmux), + + /* + * Clock-Architecture Diagram 4 + */ + + COMPOSITE(SCLK_I2C0, "clk_i2c0", mux_2plls_p, 0, + RK3328_CLKSEL_CON(34), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 9, GFLAGS), + COMPOSITE(SCLK_I2C1, "clk_i2c1", mux_2plls_p, 0, + RK3328_CLKSEL_CON(34), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 10, GFLAGS), + COMPOSITE(SCLK_I2C2, "clk_i2c2", mux_2plls_p, 0, + RK3328_CLKSEL_CON(35), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 11, GFLAGS), + COMPOSITE(SCLK_I2C3, "clk_i2c3", mux_2plls_p, 0, + RK3328_CLKSEL_CON(35), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 12, GFLAGS), + COMPOSITE(SCLK_CRYPTO, "clk_crypto", mux_2plls_p, 0, + RK3328_CLKSEL_CON(20), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 4, GFLAGS), + COMPOSITE_NOMUX(SCLK_TSADC, "clk_tsadc", "clk_24m", 0, + RK3328_CLKSEL_CON(22), 0, 10, DFLAGS, + RK3328_CLKGATE_CON(2), 6, GFLAGS), + COMPOSITE_NOMUX(SCLK_SARADC, "clk_saradc", "clk_24m", 0, + RK3328_CLKSEL_CON(23), 0, 10, DFLAGS, + RK3328_CLKGATE_CON(2), 14, GFLAGS), + COMPOSITE(SCLK_SPI, "clk_spi", mux_2plls_p, 0, + RK3328_CLKSEL_CON(24), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 7, GFLAGS), + COMPOSITE(SCLK_PWM, "clk_pwm", mux_2plls_p, 0, + RK3328_CLKSEL_CON(24), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3328_CLKGATE_CON(2), 8, GFLAGS), + COMPOSITE(SCLK_OTP, "clk_otp", mux_2plls_xin24m_p, 0, + RK3328_CLKSEL_CON(4), 6, 2, MFLAGS, 0, 6, DFLAGS, + RK3328_CLKGATE_CON(3), 8, GFLAGS), + COMPOSITE(SCLK_EFUSE, "clk_efuse", mux_2plls_xin24m_p, 0, + RK3328_CLKSEL_CON(5), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(2), 13, GFLAGS), + COMPOSITE(SCLK_PDM, "clk_pdm", mux_cpll_gpll_apll_p, CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(20), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(2), 15, GFLAGS), + + GATE(SCLK_TIMER0, "sclk_timer0", "xin24m", 0, + RK3328_CLKGATE_CON(8), 5, GFLAGS), + GATE(SCLK_TIMER1, "sclk_timer1", "xin24m", 0, + RK3328_CLKGATE_CON(8), 6, GFLAGS), + GATE(SCLK_TIMER2, "sclk_timer2", "xin24m", 0, + RK3328_CLKGATE_CON(8), 7, GFLAGS), + GATE(SCLK_TIMER3, "sclk_timer3", "xin24m", 0, + RK3328_CLKGATE_CON(8), 8, GFLAGS), + GATE(SCLK_TIMER4, "sclk_timer4", "xin24m", 0, + RK3328_CLKGATE_CON(8), 9, GFLAGS), + GATE(SCLK_TIMER5, "sclk_timer5", "xin24m", 0, + RK3328_CLKGATE_CON(8), 10, GFLAGS), + + COMPOSITE(SCLK_WIFI, "clk_wifi", mux_2plls_u480m_p, 0, + RK3328_CLKSEL_CON(52), 6, 2, MFLAGS, 0, 6, DFLAGS, + RK3328_CLKGATE_CON(0), 10, GFLAGS), + + /* + * Clock-Architecture Diagram 5 + */ + + /* PD_VIDEO */ + COMPOSITE(ACLK_RKVDEC_PRE, "aclk_rkvdec_pre", mux_4plls_p, 0, + RK3328_CLKSEL_CON(48), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 0, GFLAGS), + FACTOR_GATE(HCLK_RKVDEC_PRE, "hclk_rkvdec_pre", "aclk_rkvdec_pre", 0, 1, 4, + RK3328_CLKGATE_CON(11), 0, GFLAGS), + GATE(ACLK_RKVDEC, "aclk_rkvdec", "aclk_rkvdec_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(24), 0, GFLAGS), + GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(24), 1, GFLAGS), + GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(24), 2, GFLAGS), + GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(24), 3, GFLAGS), + + COMPOSITE(SCLK_VDEC_CABAC, "sclk_vdec_cabac", mux_4plls_p, 0, + RK3328_CLKSEL_CON(48), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 1, GFLAGS), + + COMPOSITE(SCLK_VDEC_CORE, "sclk_vdec_core", mux_4plls_p, 0, + RK3328_CLKSEL_CON(49), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 2, GFLAGS), + + COMPOSITE(ACLK_VPU_PRE, "aclk_vpu_pre", mux_4plls_p, 0, + RK3328_CLKSEL_CON(50), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 5, GFLAGS), + FACTOR_GATE(HCLK_VPU_PRE, "hclk_vpu_pre", "aclk_vpu_pre", 0, 1, 4, + RK3328_CLKGATE_CON(11), 8, GFLAGS), + GATE(ACLK_VPU, "aclk_vpu", "aclk_vpu_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(23), 0, GFLAGS), + GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(23), 1, GFLAGS), + GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(23), 2, GFLAGS), + GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(23), 3, GFLAGS), + + COMPOSITE(ACLK_RKVENC, "aclk_rkvenc", mux_4plls_p, 0, + RK3328_CLKSEL_CON(51), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 3, GFLAGS), + FACTOR_GATE(HCLK_RKVENC, "hclk_rkvenc", "aclk_rkvenc", 0, 1, 4, + RK3328_CLKGATE_CON(11), 4, GFLAGS), + GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(25), 0, GFLAGS), + GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(25), 1, GFLAGS), + GATE(ACLK_H265, "aclk_h265", "aclk_rkvenc", 0, + RK3328_CLKGATE_CON(25), 0, GFLAGS), + GATE(PCLK_H265, "pclk_h265", "hclk_rkvenc", 0, + RK3328_CLKGATE_CON(25), 1, GFLAGS), + GATE(ACLK_H264, "aclk_h264", "aclk_rkvenc", 0, + RK3328_CLKGATE_CON(25), 0, GFLAGS), + GATE(HCLK_H264, "hclk_h264", "hclk_rkvenc", 0, + RK3328_CLKGATE_CON(25), 1, GFLAGS), + GATE(ACLK_AXISRAM, "aclk_axisram", "aclk_rkvenc", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(25), 0, GFLAGS), + + COMPOSITE(SCLK_VENC_CORE, "sclk_venc_core", mux_4plls_p, 0, + RK3328_CLKSEL_CON(51), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 4, GFLAGS), + + COMPOSITE(SCLK_VENC_DSP, "sclk_venc_dsp", mux_4plls_p, 0, + RK3328_CLKSEL_CON(52), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(6), 7, GFLAGS), + + /* + * Clock-Architecture Diagram 6 + */ + + /* PD_VIO */ + COMPOSITE(ACLK_VIO_PRE, "aclk_vio_pre", mux_4plls_p, 0, + RK3328_CLKSEL_CON(37), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(5), 2, GFLAGS), + DIV(HCLK_VIO_PRE, "hclk_vio_pre", "aclk_vio_pre", 0, + RK3328_CLKSEL_CON(37), 8, 5, DFLAGS), + + COMPOSITE(ACLK_RGA_PRE, "aclk_rga_pre", mux_4plls_p, 0, + RK3328_CLKSEL_CON(36), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(5), 0, GFLAGS), + COMPOSITE(SCLK_RGA, "clk_rga", mux_4plls_p, 0, + RK3328_CLKSEL_CON(36), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(5), 1, GFLAGS), + COMPOSITE(ACLK_VOP_PRE, "aclk_vop_pre", mux_4plls_p, 0, + RK3328_CLKSEL_CON(39), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(5), 5, GFLAGS), + GATE(0, "clk_hdmi_sfc", "xin24m", 0, + RK3328_CLKGATE_CON(5), 4, GFLAGS), + + COMPOSITE_NODIV(0, "clk_cif_src", mux_2plls_p, 0, + RK3328_CLKSEL_CON(42), 7, 1, MFLAGS, + RK3328_CLKGATE_CON(5), 3, GFLAGS), + COMPOSITE_NOGATE(SCLK_CIF_OUT, "clk_cif_out", mux_sclk_cif_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(42), 5, 1, MFLAGS, 0, 5, DFLAGS), + + COMPOSITE(DCLK_LCDC_SRC, "dclk_lcdc_src", mux_gpll_cpll_p, 0, + RK3328_CLKSEL_CON(40), 0, 1, MFLAGS, 8, 8, DFLAGS, + RK3328_CLKGATE_CON(5), 6, GFLAGS), + DIV(DCLK_HDMIPHY, "dclk_hdmiphy", "dclk_lcdc_src", 0, + RK3328_CLKSEL_CON(40), 3, 3, DFLAGS), + MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, 0, + RK3328_CLKSEL_CON(40), 1, 1, MFLAGS), + + /* + * Clock-Architecture Diagram 7 + */ + + /* PD_PERI */ + GATE(0, "gpll_peri", "gpll", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(4), 0, GFLAGS), + GATE(0, "cpll_peri", "cpll", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(4), 1, GFLAGS), + GATE(0, "hdmiphy_peri", "hdmiphy", CLK_IGNORE_UNUSED, + RK3328_CLKGATE_CON(4), 2, GFLAGS), + COMPOSITE_NOGATE(ACLK_PERI_PRE, "aclk_peri_pre", mux_aclk_peri_pre_p, 0, + RK3328_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 5, DFLAGS), + COMPOSITE_NOMUX(PCLK_PERI, "pclk_peri", "aclk_peri_pre", CLK_IGNORE_UNUSED, + RK3328_CLKSEL_CON(29), 0, 2, DFLAGS, + RK3328_CLKGATE_CON(10), 2, GFLAGS), + COMPOSITE_NOMUX(HCLK_PERI, "hclk_peri", "aclk_peri_pre", CLK_IGNORE_UNUSED, + RK3328_CLKSEL_CON(29), 4, 3, DFLAGS, + RK3328_CLKGATE_CON(10), 1, GFLAGS), + GATE(ACLK_PERI, "aclk_peri", "aclk_peri_pre", CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, + RK3328_CLKGATE_CON(10), 0, GFLAGS), + + COMPOSITE(SCLK_SDMMC, "clk_sdmmc", mux_2plls_24m_u480m_p, 0, + RK3328_CLKSEL_CON(30), 8, 2, MFLAGS, 0, 8, DFLAGS, + RK3328_CLKGATE_CON(4), 3, GFLAGS), + + COMPOSITE(SCLK_SDIO, "clk_sdio", mux_2plls_24m_u480m_p, 0, + RK3328_CLKSEL_CON(31), 8, 2, MFLAGS, 0, 8, DFLAGS, + RK3328_CLKGATE_CON(4), 4, GFLAGS), + + COMPOSITE(SCLK_EMMC, "clk_emmc", mux_2plls_24m_u480m_p, 0, + RK3328_CLKSEL_CON(32), 8, 2, MFLAGS, 0, 8, DFLAGS, + RK3328_CLKGATE_CON(4), 5, GFLAGS), + + COMPOSITE(SCLK_SDMMC_EXT, "clk_sdmmc_ext", mux_2plls_24m_u480m_p, 0, + RK3328_CLKSEL_CON(43), 8, 2, MFLAGS, 0, 8, DFLAGS, + RK3328_CLKGATE_CON(4), 10, GFLAGS), + + COMPOSITE(SCLK_REF_USB3OTG_SRC, "clk_ref_usb3otg_src", mux_2plls_p, 0, + RK3328_CLKSEL_CON(45), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3328_CLKGATE_CON(4), 9, GFLAGS), + + MUX(SCLK_REF_USB3OTG, "clk_ref_usb3otg", mux_ref_usb3otg_src_p, CLK_SET_RATE_PARENT, + RK3328_CLKSEL_CON(45), 8, 1, MFLAGS), + + GATE(SCLK_USB3OTG_REF, "clk_usb3otg_ref", "xin24m", 0, + RK3328_CLKGATE_CON(4), 7, GFLAGS), + + COMPOSITE(SCLK_USB3OTG_SUSPEND, "clk_usb3otg_suspend", mux_xin24m_32k_p, 0, + RK3328_CLKSEL_CON(33), 15, 1, MFLAGS, 0, 10, DFLAGS, + RK3328_CLKGATE_CON(4), 8, GFLAGS), + + /* + * Clock-Architecture Diagram 8 + */ + + /* PD_GMAC */ + COMPOSITE(ACLK_GMAC, "aclk_gmac", mux_2plls_hdmiphy_p, 0, + RK3328_CLKSEL_CON(35), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(3), 2, GFLAGS), + COMPOSITE_NOMUX(PCLK_GMAC, "pclk_gmac", "aclk_gmac", 0, + RK3328_CLKSEL_CON(25), 8, 3, DFLAGS, + RK3328_CLKGATE_CON(9), 0, GFLAGS), + + COMPOSITE(SCLK_MAC2IO_SRC, "clk_mac2io_src", mux_2plls_p, 0, + RK3328_CLKSEL_CON(27), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(3), 1, GFLAGS), + GATE(SCLK_MAC2IO_REF, "clk_mac2io_ref", "clk_mac2io", 0, + RK3328_CLKGATE_CON(9), 7, GFLAGS), + GATE(SCLK_MAC2IO_RX, "clk_mac2io_rx", "clk_mac2io", 0, + RK3328_CLKGATE_CON(9), 4, GFLAGS), + GATE(SCLK_MAC2IO_TX, "clk_mac2io_tx", "clk_mac2io", 0, + RK3328_CLKGATE_CON(9), 5, GFLAGS), + GATE(SCLK_MAC2IO_REFOUT, "clk_mac2io_refout", "clk_mac2io", 0, + RK3328_CLKGATE_CON(9), 6, GFLAGS), + COMPOSITE(SCLK_MAC2IO_OUT, "clk_mac2io_out", mux_2plls_p, 0, + RK3328_CLKSEL_CON(27), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3328_CLKGATE_CON(3), 5, GFLAGS), + + COMPOSITE(SCLK_MAC2PHY_SRC, "clk_mac2phy_src", mux_2plls_p, 0, + RK3328_CLKSEL_CON(26), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK3328_CLKGATE_CON(3), 0, GFLAGS), + GATE(SCLK_MAC2PHY_REF, "clk_mac2phy_ref", "clk_mac2phy", 0, + RK3328_CLKGATE_CON(9), 3, GFLAGS), + GATE(SCLK_MAC2PHY_RXTX, "clk_mac2phy_rxtx", "clk_mac2phy", 0, + RK3328_CLKGATE_CON(9), 1, GFLAGS), + COMPOSITE_NOMUX(SCLK_MAC2PHY_OUT, "clk_mac2phy_out", "clk_mac2phy", 0, + RK3328_CLKSEL_CON(26), 8, 2, DFLAGS, + RK3328_CLKGATE_CON(9), 2, GFLAGS), + + FACTOR(0, "xin12m", "xin24m", 0, 1, 2), + + /* + * Clock-Architecture Diagram 9 + */ + + /* PD_VOP */ + GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(21), 10, GFLAGS), + GATE(0, "aclk_rga_niu", "aclk_rga_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 3, GFLAGS), + GATE(ACLK_VOP, "aclk_vop", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 2, GFLAGS), + GATE(0, "aclk_vop_niu", "aclk_vop_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 4, GFLAGS), + + GATE(ACLK_IEP, "aclk_iep", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 6, GFLAGS), + GATE(ACLK_CIF, "aclk_cif", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 8, GFLAGS), + GATE(ACLK_HDCP, "aclk_hdcp", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 15, GFLAGS), + GATE(0, "aclk_vio_niu", "aclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 2, GFLAGS), + + GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 3, GFLAGS), + GATE(0, "hclk_vop_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 5, GFLAGS), + GATE(HCLK_IEP, "hclk_iep", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 7, GFLAGS), + GATE(HCLK_CIF, "hclk_cif", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 9, GFLAGS), + GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 11, GFLAGS), + GATE(0, "hclk_ahb1tom", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 12, GFLAGS), + GATE(0, "pclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 13, GFLAGS), + GATE(0, "hclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 14, GFLAGS), + GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 0, GFLAGS), + GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS), + GATE(PCLK_HDMI, "pclk_hdmi", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 4, GFLAGS), + GATE(PCLK_HDCP, "pclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 5, GFLAGS), + + /* PD_PERI */ + GATE(0, "aclk_peri_noc", "aclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 11, GFLAGS), + GATE(ACLK_USB3OTG, "aclk_usb3otg", "aclk_peri", 0, RK3328_CLKGATE_CON(19), 4, GFLAGS), + + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 0, GFLAGS), + GATE(HCLK_SDIO, "hclk_sdio", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 1, GFLAGS), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 2, GFLAGS), + GATE(HCLK_SDMMC_EXT, "hclk_sdmmc_ext", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 15, GFLAGS), + GATE(HCLK_HOST0, "hclk_host0", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 6, GFLAGS), + GATE(HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 7, GFLAGS), + GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 8, GFLAGS), + GATE(HCLK_OTG_PMU, "hclk_otg_pmu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 9, GFLAGS), + GATE(0, "hclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 12, GFLAGS), + GATE(0, "pclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 13, GFLAGS), + + /* PD_GMAC */ + GATE(ACLK_MAC2PHY, "aclk_mac2phy", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 0, GFLAGS), + GATE(ACLK_MAC2IO, "aclk_mac2io", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 2, GFLAGS), + GATE(0, "aclk_gmac_niu", "aclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 4, GFLAGS), + GATE(PCLK_MAC2PHY, "pclk_mac2phy", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 1, GFLAGS), + GATE(PCLK_MAC2IO, "pclk_mac2io", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 3, GFLAGS), + GATE(0, "pclk_gmac_niu", "pclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 5, GFLAGS), + + /* PD_BUS */ + GATE(0, "aclk_bus_niu", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 12, GFLAGS), + GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 11, GFLAGS), + GATE(ACLK_TSP, "aclk_tsp", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 12, GFLAGS), + GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 0, GFLAGS), + GATE(ACLK_DMAC, "aclk_dmac_bus", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 1, GFLAGS), + + GATE(0, "hclk_rom", "hclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 2, GFLAGS), + GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 3, GFLAGS), + GATE(HCLK_I2S1_8CH, "hclk_i2s1_8ch", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 4, GFLAGS), + GATE(HCLK_I2S2_2CH, "hclk_i2s2_2ch", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 5, GFLAGS), + GATE(HCLK_SPDIF_8CH, "hclk_spdif_8ch", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 6, GFLAGS), + GATE(HCLK_TSP, "hclk_tsp", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 11, GFLAGS), + GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 7, GFLAGS), + GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 8, GFLAGS), + GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 13, GFLAGS), + GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(28), 0, GFLAGS), + + GATE(0, "pclk_bus_niu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 14, GFLAGS), + GATE(0, "pclk_efuse", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 9, GFLAGS), + GATE(0, "pclk_otp", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 4, GFLAGS), + GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 10, GFLAGS), + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 0, GFLAGS), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 1, GFLAGS), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 2, GFLAGS), + GATE(PCLK_TIMER, "pclk_timer0", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 3, GFLAGS), + GATE(0, "pclk_stimer", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 4, GFLAGS), + GATE(PCLK_SPI, "pclk_spi", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 5, GFLAGS), + GATE(PCLK_PWM, "pclk_rk_pwm", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 6, GFLAGS), + GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 7, GFLAGS), + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 8, GFLAGS), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 9, GFLAGS), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 10, GFLAGS), + GATE(PCLK_UART0, "pclk_uart0", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 11, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 12, GFLAGS), + GATE(PCLK_UART2, "pclk_uart2", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 13, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 14, GFLAGS), + GATE(PCLK_DCF, "pclk_dcf", "pclk_bus", 0, RK3328_CLKGATE_CON(16), 15, GFLAGS), + GATE(PCLK_GRF, "pclk_grf", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 0, GFLAGS), + GATE(0, "pclk_cru", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 4, GFLAGS), + GATE(0, "pclk_sgrf", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 6, GFLAGS), + GATE(0, "pclk_sim", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 10, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus", 0, RK3328_CLKGATE_CON(17), 15, GFLAGS), + GATE(0, "pclk_pmu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 3, GFLAGS), + + GATE(PCLK_USB3PHY_OTG, "pclk_usb3phy_otg", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(28), 1, GFLAGS), + GATE(PCLK_USB3PHY_PIPE, "pclk_usb3phy_pipe", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(28), 2, GFLAGS), + GATE(PCLK_USB3_GRF, "pclk_usb3_grf", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 2, GFLAGS), + GATE(PCLK_USB2_GRF, "pclk_usb2_grf", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 14, GFLAGS), + GATE(0, "pclk_ddrphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 13, GFLAGS), + GATE(0, "pclk_acodecphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 5, GFLAGS), + GATE(PCLK_HDMIPHY, "pclk_hdmiphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 7, GFLAGS), + GATE(0, "pclk_vdacphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 8, GFLAGS), + GATE(0, "pclk_phy_niu", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 15, GFLAGS), + + /* PD_MMC */ + MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", + RK3328_SDMMC_CON0, 1), + MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", + RK3328_SDMMC_CON1, 1), + + MMC(SCLK_SDIO_DRV, "sdio_drv", "sclk_sdio", + RK3328_SDIO_CON0, 1), + MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "sclk_sdio", + RK3328_SDIO_CON1, 1), + + MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc", + RK3328_EMMC_CON0, 1), + MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", + RK3328_EMMC_CON1, 1), + + MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "sclk_sdmmc_ext", + RK3328_SDMMC_EXT_CON0, 1), + MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "sclk_sdmmc_ext", + RK3328_SDMMC_EXT_CON1, 1), +}; + +static const char *const rk3328_critical_clocks[] __initconst = { + "aclk_bus", + "pclk_bus", + "hclk_bus", + "aclk_peri", + "hclk_peri", + "pclk_peri", + "pclk_dbg", + "aclk_core_niu", + "aclk_gic400", + "aclk_intmem", + "hclk_rom", + "pclk_grf", + "pclk_cru", + "pclk_sgrf", + "pclk_timer0", + "clk_timer0", + "pclk_ddr_msch", + "pclk_ddr_mon", + "pclk_ddr_grf", + "clk_ddrupctl", + "clk_ddrmsch", + "hclk_ahb1tom", + "clk_jtag", + "pclk_ddrphy", + "pclk_pmu", + "hclk_otg_pmu", + "aclk_rga_niu", + "pclk_vio_h2p", + "hclk_vio_h2p", +}; + +static void __init rk3328_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru region\n", __func__); + return; + } + + ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + if (IS_ERR(ctx)) { + pr_err("%s: rockchip clk init failed\n", __func__); + iounmap(reg_base); + return; + } + + rockchip_clk_register_plls(ctx, rk3328_pll_clks, + ARRAY_SIZE(rk3328_pll_clks), + RK3328_GRF_SOC_STATUS0); + rockchip_clk_register_branches(ctx, rk3328_clk_branches, + ARRAY_SIZE(rk3328_clk_branches)); + rockchip_clk_protect_critical(rk3328_critical_clocks, + ARRAY_SIZE(rk3328_critical_clocks)); + + rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk3328_cpuclk_data, rk3328_cpuclk_rates, + ARRAY_SIZE(rk3328_cpuclk_rates)); + + rockchip_register_softrst(np, 11, reg_base + RK3328_SOFTRST_CON(0), + ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(ctx, RK3328_GLB_SRST_FST, NULL); + + rockchip_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(rk3328_cru, "rockchip,rk3328-cru", rk3328_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index 3490887b0579..73121b144634 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -1132,7 +1132,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKGATE_CON(11), 8, GFLAGS), COMPOSITE(PCLK_EDP, "pclk_edp", mux_pll_src_cpll_gpll_p, 0, - RK3399_CLKSEL_CON(44), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKSEL_CON(44), 15, 1, MFLAGS, 8, 6, DFLAGS, RK3399_CLKGATE_CON(11), 11, GFLAGS), GATE(PCLK_EDP_NOC, "pclk_edp_noc", "pclk_edp", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(32), 12, GFLAGS), diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index b886be30f34f..fe1d393cf678 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -344,7 +344,6 @@ struct rockchip_clk_provider * __init rockchip_clk_init(struct device_node *np, ctx->clk_data.clks = clk_table; ctx->clk_data.clk_num = nr_clks; ctx->cru_node = np; - ctx->grf = ERR_PTR(-EPROBE_DEFER); spin_lock_init(&ctx->lock); ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, @@ -417,6 +416,13 @@ void __init rockchip_clk_register_branches( list->mux_shift, list->mux_width, list->mux_flags, &ctx->lock); break; + case branch_muxgrf: + clk = rockchip_clk_register_muxgrf(list->name, + list->parent_names, list->num_parents, + flags, ctx->grf, list->muxdiv_offset, + list->mux_shift, list->mux_width, + list->mux_flags); + break; case branch_divider: if (list->div_table) clk = clk_register_divider_table(NULL, diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index d67eecc4ade9..7c15473ea72b 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -91,6 +91,24 @@ struct clk; #define RK3288_EMMC_CON0 0x218 #define RK3288_EMMC_CON1 0x21c +#define RK3328_PLL_CON(x) RK2928_PLL_CON(x) +#define RK3328_CLKSEL_CON(x) ((x) * 0x4 + 0x100) +#define RK3328_CLKGATE_CON(x) ((x) * 0x4 + 0x200) +#define RK3328_GRFCLKSEL_CON(x) ((x) * 0x4 + 0x100) +#define RK3328_GLB_SRST_FST 0x9c +#define RK3328_GLB_SRST_SND 0x98 +#define RK3328_SOFTRST_CON(x) ((x) * 0x4 + 0x300) +#define RK3328_MODE_CON 0x80 +#define RK3328_MISC_CON 0x84 +#define RK3328_SDMMC_CON0 0x380 +#define RK3328_SDMMC_CON1 0x384 +#define RK3328_SDIO_CON0 0x388 +#define RK3328_SDIO_CON1 0x38c +#define RK3328_EMMC_CON0 0x390 +#define RK3328_EMMC_CON1 0x394 +#define RK3328_SDMMC_EXT_CON0 0x398 +#define RK3328_SDMMC_EXT_CON1 0x39C + #define RK3368_PLL_CON(x) RK2928_PLL_CON(x) #define RK3368_CLKSEL_CON(x) ((x) * 0x4 + 0x100) #define RK3368_CLKGATE_CON(x) ((x) * 0x4 + 0x200) @@ -130,6 +148,7 @@ struct clk; enum rockchip_pll_type { pll_rk3036, pll_rk3066, + pll_rk3328, pll_rk3399, }; @@ -317,11 +336,17 @@ struct clk *rockchip_clk_register_inverter(const char *name, void __iomem *reg, int shift, int flags, spinlock_t *lock); +struct clk *rockchip_clk_register_muxgrf(const char *name, + const char *const *parent_names, u8 num_parents, + int flags, struct regmap *grf, int reg, + int shift, int width, int mux_flags); + #define PNAME(x) static const char *const x[] __initconst enum rockchip_clk_branch_type { branch_composite, branch_mux, + branch_muxgrf, branch_divider, branch_fraction_divider, branch_gate, @@ -551,6 +576,21 @@ struct rockchip_clk_branch { .gate_offset = -1, \ } +#define MUXGRF(_id, cname, pnames, f, o, s, w, mf) \ + { \ + .id = _id, \ + .branch_type = branch_muxgrf, \ + .name = cname, \ + .parent_names = pnames, \ + .num_parents = ARRAY_SIZE(pnames), \ + .flags = f, \ + .muxdiv_offset = o, \ + .mux_shift = s, \ + .mux_width = w, \ + .mux_flags = mf, \ + .gate_offset = -1, \ + } + #define DIV(_id, cname, pname, f, o, s, w, df) \ { \ .id = _id, \ diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 57f4dc6dc447..7afc21dc374e 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -5,7 +5,6 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o -obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index 17e68a724945..cb7df358a27d 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -44,7 +44,7 @@ static unsigned long reg_save[][2] = { { ASS_CLK_GATE, 0 }, }; -static int exynos_audss_clk_suspend(void) +static int exynos_audss_clk_suspend(struct device *dev) { int i; @@ -54,18 +54,15 @@ static int exynos_audss_clk_suspend(void) return 0; } -static void exynos_audss_clk_resume(void) +static int exynos_audss_clk_resume(struct device *dev) { int i; for (i = 0; i < ARRAY_SIZE(reg_save); i++) writel(reg_save[i][1], reg_base + reg_save[i][0]); -} -static struct syscore_ops exynos_audss_clk_syscore_ops = { - .suspend = exynos_audss_clk_suspend, - .resume = exynos_audss_clk_resume, -}; + return 0; +} #endif /* CONFIG_PM_SLEEP */ struct exynos_audss_clk_drvdata { @@ -251,9 +248,6 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) goto unregister; } -#ifdef CONFIG_PM_SLEEP - register_syscore_ops(&exynos_audss_clk_syscore_ops); -#endif return 0; unregister: @@ -267,10 +261,6 @@ unregister: static int exynos_audss_clk_remove(struct platform_device *pdev) { -#ifdef CONFIG_PM_SLEEP - unregister_syscore_ops(&exynos_audss_clk_syscore_ops); -#endif - of_clk_del_provider(pdev->dev.of_node); exynos_audss_clk_teardown(); @@ -281,10 +271,16 @@ static int exynos_audss_clk_remove(struct platform_device *pdev) return 0; } +static const struct dev_pm_ops exynos_audss_clk_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend, + exynos_audss_clk_resume) +}; + static struct platform_driver exynos_audss_clk_driver = { .driver = { .name = "exynos-audss-clk", .of_match_table = exynos_audss_clk_of_match, + .pm = &exynos_audss_clk_pm_ops, }, .probe = exynos_audss_clk_probe, .remove = exynos_audss_clk_remove, diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index faab9b31baf5..e40b77583c47 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1298,6 +1298,8 @@ static const struct samsung_pll_rate_table exynos4210_vpll_rates[] __initconst = }; static const struct samsung_pll_rate_table exynos4x12_apll_rates[] __initconst = { + PLL_35XX_RATE(1704000000, 213, 3, 0), + PLL_35XX_RATE(1600000000, 200, 3, 0), PLL_35XX_RATE(1500000000, 250, 4, 0), PLL_35XX_RATE(1400000000, 175, 3, 0), PLL_35XX_RATE(1300000000, 325, 6, 0), @@ -1421,6 +1423,8 @@ static const struct exynos_cpuclk_cfg_data e4212_armclk_d[] __initconst = { (((cores) << 8) | ((hpm) << 4) | ((copy) << 0)) static const struct exynos_cpuclk_cfg_data e4412_armclk_d[] __initconst = { + { 1704000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(7, 0, 7), }, + { 1600000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(7, 0, 6), }, { 1500000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(7, 0, 6), }, { 1400000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4412_CPU_DIV1(6, 0, 6), }, { 1300000, E4210_CPU_DIV0(2, 1, 5, 0, 7, 3), E4412_CPU_DIV1(6, 0, 5), }, diff --git a/drivers/clk/samsung/clk-exynos4415.c b/drivers/clk/samsung/clk-exynos4415.c deleted file mode 100644 index 6c9063159717..000000000000 --- a/drivers/clk/samsung/clk-exynos4415.c +++ /dev/null @@ -1,1022 +0,0 @@ -/* - * Copyright (c) 2014 Samsung Electronics Co., Ltd. - * Author: Chanwoo Choi <cw00.choi@samsung.com> - * - * 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. - * - * Common Clock Framework support for Exynos4415 SoC. - */ - -#include <linux/clk-provider.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/platform_device.h> -#include <linux/syscore_ops.h> - -#include <dt-bindings/clock/exynos4415.h> - -#include "clk.h" -#include "clk-pll.h" - -#define SRC_LEFTBUS 0x4200 -#define DIV_LEFTBUS 0x4500 -#define GATE_IP_LEFTBUS 0x4800 -#define GATE_IP_IMAGE 0x4930 -#define SRC_RIGHTBUS 0x8200 -#define DIV_RIGHTBUS 0x8500 -#define GATE_IP_RIGHTBUS 0x8800 -#define GATE_IP_PERIR 0x8960 -#define EPLL_LOCK 0xc010 -#define G3D_PLL_LOCK 0xc020 -#define DISP_PLL_LOCK 0xc030 -#define ISP_PLL_LOCK 0xc040 -#define EPLL_CON0 0xc110 -#define EPLL_CON1 0xc114 -#define EPLL_CON2 0xc118 -#define G3D_PLL_CON0 0xc120 -#define G3D_PLL_CON1 0xc124 -#define G3D_PLL_CON2 0xc128 -#define ISP_PLL_CON0 0xc130 -#define ISP_PLL_CON1 0xc134 -#define ISP_PLL_CON2 0xc138 -#define DISP_PLL_CON0 0xc140 -#define DISP_PLL_CON1 0xc144 -#define DISP_PLL_CON2 0xc148 -#define SRC_TOP0 0xc210 -#define SRC_TOP1 0xc214 -#define SRC_CAM 0xc220 -#define SRC_TV 0xc224 -#define SRC_MFC 0xc228 -#define SRC_G3D 0xc22c -#define SRC_LCD 0xc234 -#define SRC_ISP 0xc238 -#define SRC_MAUDIO 0xc23c -#define SRC_FSYS 0xc240 -#define SRC_PERIL0 0xc250 -#define SRC_PERIL1 0xc254 -#define SRC_CAM1 0xc258 -#define SRC_TOP_ISP0 0xc25c -#define SRC_TOP_ISP1 0xc260 -#define SRC_MASK_TOP 0xc310 -#define SRC_MASK_CAM 0xc320 -#define SRC_MASK_TV 0xc324 -#define SRC_MASK_LCD 0xc334 -#define SRC_MASK_ISP 0xc338 -#define SRC_MASK_MAUDIO 0xc33c -#define SRC_MASK_FSYS 0xc340 -#define SRC_MASK_PERIL0 0xc350 -#define SRC_MASK_PERIL1 0xc354 -#define DIV_TOP 0xc510 -#define DIV_CAM 0xc520 -#define DIV_TV 0xc524 -#define DIV_MFC 0xc528 -#define DIV_G3D 0xc52c -#define DIV_LCD 0xc534 -#define DIV_ISP 0xc538 -#define DIV_MAUDIO 0xc53c -#define DIV_FSYS0 0xc540 -#define DIV_FSYS1 0xc544 -#define DIV_FSYS2 0xc548 -#define DIV_PERIL0 0xc550 -#define DIV_PERIL1 0xc554 -#define DIV_PERIL2 0xc558 -#define DIV_PERIL3 0xc55c -#define DIV_PERIL4 0xc560 -#define DIV_PERIL5 0xc564 -#define DIV_CAM1 0xc568 -#define DIV_TOP_ISP1 0xc56c -#define DIV_TOP_ISP0 0xc570 -#define CLKDIV2_RATIO 0xc580 -#define GATE_SCLK_CAM 0xc820 -#define GATE_SCLK_TV 0xc824 -#define GATE_SCLK_MFC 0xc828 -#define GATE_SCLK_G3D 0xc82c -#define GATE_SCLK_LCD 0xc834 -#define GATE_SCLK_MAUDIO 0xc83c -#define GATE_SCLK_FSYS 0xc840 -#define GATE_SCLK_PERIL 0xc850 -#define GATE_IP_CAM 0xc920 -#define GATE_IP_TV 0xc924 -#define GATE_IP_MFC 0xc928 -#define GATE_IP_G3D 0xc92c -#define GATE_IP_LCD 0xc934 -#define GATE_IP_FSYS 0xc940 -#define GATE_IP_PERIL 0xc950 -#define GATE_BLOCK 0xc970 -#define APLL_LOCK 0x14000 -#define APLL_CON0 0x14100 -#define SRC_CPU 0x14200 -#define DIV_CPU0 0x14500 -#define DIV_CPU1 0x14504 - -static const unsigned long exynos4415_cmu_clk_regs[] __initconst = { - SRC_LEFTBUS, - DIV_LEFTBUS, - GATE_IP_LEFTBUS, - GATE_IP_IMAGE, - SRC_RIGHTBUS, - DIV_RIGHTBUS, - GATE_IP_RIGHTBUS, - GATE_IP_PERIR, - EPLL_LOCK, - G3D_PLL_LOCK, - DISP_PLL_LOCK, - ISP_PLL_LOCK, - EPLL_CON0, - EPLL_CON1, - EPLL_CON2, - G3D_PLL_CON0, - G3D_PLL_CON1, - G3D_PLL_CON2, - ISP_PLL_CON0, - ISP_PLL_CON1, - ISP_PLL_CON2, - DISP_PLL_CON0, - DISP_PLL_CON1, - DISP_PLL_CON2, - SRC_TOP0, - SRC_TOP1, - SRC_CAM, - SRC_TV, - SRC_MFC, - SRC_G3D, - SRC_LCD, - SRC_ISP, - SRC_MAUDIO, - SRC_FSYS, - SRC_PERIL0, - SRC_PERIL1, - SRC_CAM1, - SRC_TOP_ISP0, - SRC_TOP_ISP1, - SRC_MASK_TOP, - SRC_MASK_CAM, - SRC_MASK_TV, - SRC_MASK_LCD, - SRC_MASK_ISP, - SRC_MASK_MAUDIO, - SRC_MASK_FSYS, - SRC_MASK_PERIL0, - SRC_MASK_PERIL1, - DIV_TOP, - DIV_CAM, - DIV_TV, - DIV_MFC, - DIV_G3D, - DIV_LCD, - DIV_ISP, - DIV_MAUDIO, - DIV_FSYS0, - DIV_FSYS1, - DIV_FSYS2, - DIV_PERIL0, - DIV_PERIL1, - DIV_PERIL2, - DIV_PERIL3, - DIV_PERIL4, - DIV_PERIL5, - DIV_CAM1, - DIV_TOP_ISP1, - DIV_TOP_ISP0, - CLKDIV2_RATIO, - GATE_SCLK_CAM, - GATE_SCLK_TV, - GATE_SCLK_MFC, - GATE_SCLK_G3D, - GATE_SCLK_LCD, - GATE_SCLK_MAUDIO, - GATE_SCLK_FSYS, - GATE_SCLK_PERIL, - GATE_IP_CAM, - GATE_IP_TV, - GATE_IP_MFC, - GATE_IP_G3D, - GATE_IP_LCD, - GATE_IP_FSYS, - GATE_IP_PERIL, - GATE_BLOCK, - APLL_LOCK, - APLL_CON0, - SRC_CPU, - DIV_CPU0, - DIV_CPU1, -}; - -/* list of all parent clock list */ -PNAME(mout_g3d_pllsrc_p) = { "fin_pll", }; - -PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; -PNAME(mout_g3d_pll_p) = { "fin_pll", "fout_g3d_pll", }; -PNAME(mout_isp_pll_p) = { "fin_pll", "fout_isp_pll", }; -PNAME(mout_disp_pll_p) = { "fin_pll", "fout_disp_pll", }; - -PNAME(mout_mpll_user_p) = { "fin_pll", "div_mpll_pre", }; -PNAME(mout_epll_p) = { "fin_pll", "fout_epll", }; -PNAME(mout_core_p) = { "mout_apll", "mout_mpll_user_c", }; -PNAME(mout_hpm_p) = { "mout_apll", "mout_mpll_user_c", }; - -PNAME(mout_ebi_p) = { "div_aclk_200", "div_aclk_160", }; -PNAME(mout_ebi_1_p) = { "mout_ebi", "mout_g3d_pll", }; - -PNAME(mout_gdl_p) = { "mout_mpll_user_l", }; -PNAME(mout_gdr_p) = { "mout_mpll_user_r", }; - -PNAME(mout_aclk_266_p) = { "mout_mpll_user_t", "mout_g3d_pll", }; - -PNAME(group_epll_g3dpll_p) = { "mout_epll", "mout_g3d_pll" }; -PNAME(group_sclk_p) = { "xxti", "xusbxti", - "none", "mout_isp_pll", - "none", "none", "div_mpll_pre", - "mout_epll", "mout_g3d_pll", }; -PNAME(group_spdif_p) = { "mout_audio0", "mout_audio1", - "mout_audio2", "spdif_extclk", }; -PNAME(group_sclk_audio2_p) = { "audiocdclk2", "none", - "none", "mout_isp_pll", - "mout_disp_pll", "xusbxti", - "div_mpll_pre", "mout_epll", - "mout_g3d_pll", }; -PNAME(group_sclk_audio1_p) = { "audiocdclk1", "none", - "none", "mout_isp_pll", - "mout_disp_pll", "xusbxti", - "div_mpll_pre", "mout_epll", - "mout_g3d_pll", }; -PNAME(group_sclk_audio0_p) = { "audiocdclk0", "none", - "none", "mout_isp_pll", - "mout_disp_pll", "xusbxti", - "div_mpll_pre", "mout_epll", - "mout_g3d_pll", }; -PNAME(group_fimc_lclk_p) = { "xxti", "xusbxti", - "none", "mout_isp_pll", - "none", "mout_disp_pll", - "mout_mpll_user_t", "mout_epll", - "mout_g3d_pll", }; -PNAME(group_sclk_fimd0_p) = { "xxti", "xusbxti", - "m_bitclkhsdiv4_4l", "mout_isp_pll", - "mout_disp_pll", "sclk_hdmiphy", - "div_mpll_pre", "mout_epll", - "mout_g3d_pll", }; -PNAME(mout_hdmi_p) = { "sclk_pixel", "sclk_hdmiphy" }; -PNAME(mout_mfc_p) = { "mout_mfc_0", "mout_mfc_1" }; -PNAME(mout_g3d_p) = { "mout_g3d_0", "mout_g3d_1" }; -PNAME(mout_jpeg_p) = { "mout_jpeg_0", "mout_jpeg_1" }; -PNAME(mout_jpeg1_p) = { "mout_epll", "mout_g3d_pll" }; -PNAME(group_aclk_isp0_300_p) = { "mout_isp_pll", "div_mpll_pre" }; -PNAME(group_aclk_isp0_400_user_p) = { "fin_pll", "div_aclk_400_mcuisp" }; -PNAME(group_aclk_isp0_300_user_p) = { "fin_pll", "mout_aclk_isp0_300" }; -PNAME(group_aclk_isp1_300_user_p) = { "fin_pll", "mout_aclk_isp1_300" }; -PNAME(group_mout_mpll_user_t_p) = { "mout_mpll_user_t" }; - -static const struct samsung_fixed_factor_clock exynos4415_fixed_factor_clks[] __initconst = { - /* HACK: fin_pll hardcoded to xusbxti until detection is implemented. */ - FFACTOR(CLK_FIN_PLL, "fin_pll", "xusbxti", 1, 1, 0), -}; - -static const struct samsung_fixed_rate_clock exynos4415_fixed_rate_clks[] __initconst = { - FRATE(CLK_SCLK_HDMIPHY, "sclk_hdmiphy", NULL, 0, 27000000), -}; - -static const struct samsung_mux_clock exynos4415_mux_clks[] __initconst = { - /* - * NOTE: Following table is sorted by register address in ascending - * order and then bitfield shift in descending order, as it is done - * in the User's Manual. When adding new entries, please make sure - * that the order is preserved, to avoid merge conflicts and make - * further work with defined data easier. - */ - - /* SRC_LEFTBUS */ - MUX(CLK_MOUT_MPLL_USER_L, "mout_mpll_user_l", mout_mpll_user_p, - SRC_LEFTBUS, 4, 1), - MUX(CLK_MOUT_GDL, "mout_gdl", mout_gdl_p, SRC_LEFTBUS, 0, 1), - - /* SRC_RIGHTBUS */ - MUX(CLK_MOUT_MPLL_USER_R, "mout_mpll_user_r", mout_mpll_user_p, - SRC_RIGHTBUS, 4, 1), - MUX(CLK_MOUT_GDR, "mout_gdr", mout_gdr_p, SRC_RIGHTBUS, 0, 1), - - /* SRC_TOP0 */ - MUX(CLK_MOUT_EBI, "mout_ebi", mout_ebi_p, SRC_TOP0, 28, 1), - MUX(CLK_MOUT_ACLK_200, "mout_aclk_200", group_mout_mpll_user_t_p, - SRC_TOP0, 24, 1), - MUX(CLK_MOUT_ACLK_160, "mout_aclk_160", group_mout_mpll_user_t_p, - SRC_TOP0, 20, 1), - MUX(CLK_MOUT_ACLK_100, "mout_aclk_100", group_mout_mpll_user_t_p, - SRC_TOP0, 16, 1), - MUX(CLK_MOUT_ACLK_266, "mout_aclk_266", mout_aclk_266_p, - SRC_TOP0, 12, 1), - MUX(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, - SRC_TOP0, 8, 1), - MUX(CLK_MOUT_EPLL, "mout_epll", mout_epll_p, SRC_TOP0, 4, 1), - MUX(CLK_MOUT_EBI_1, "mout_ebi_1", mout_ebi_1_p, SRC_TOP0, 0, 1), - - /* SRC_TOP1 */ - MUX(CLK_MOUT_ISP_PLL, "mout_isp_pll", mout_isp_pll_p, - SRC_TOP1, 28, 1), - MUX(CLK_MOUT_DISP_PLL, "mout_disp_pll", mout_disp_pll_p, - SRC_TOP1, 16, 1), - MUX(CLK_MOUT_MPLL_USER_T, "mout_mpll_user_t", mout_mpll_user_p, - SRC_TOP1, 12, 1), - MUX(CLK_MOUT_ACLK_400_MCUISP, "mout_aclk_400_mcuisp", - group_mout_mpll_user_t_p, SRC_TOP1, 8, 1), - MUX(CLK_MOUT_G3D_PLLSRC, "mout_g3d_pllsrc", mout_g3d_pllsrc_p, - SRC_TOP1, 0, 1), - - /* SRC_CAM */ - MUX(CLK_MOUT_CSIS1, "mout_csis1", group_fimc_lclk_p, SRC_CAM, 28, 4), - MUX(CLK_MOUT_CSIS0, "mout_csis0", group_fimc_lclk_p, SRC_CAM, 24, 4), - MUX(CLK_MOUT_CAM1, "mout_cam1", group_fimc_lclk_p, SRC_CAM, 20, 4), - MUX(CLK_MOUT_FIMC3_LCLK, "mout_fimc3_lclk", group_fimc_lclk_p, SRC_CAM, - 12, 4), - MUX(CLK_MOUT_FIMC2_LCLK, "mout_fimc2_lclk", group_fimc_lclk_p, SRC_CAM, - 8, 4), - MUX(CLK_MOUT_FIMC1_LCLK, "mout_fimc1_lclk", group_fimc_lclk_p, SRC_CAM, - 4, 4), - MUX(CLK_MOUT_FIMC0_LCLK, "mout_fimc0_lclk", group_fimc_lclk_p, SRC_CAM, - 0, 4), - - /* SRC_TV */ - MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), - - /* SRC_MFC */ - MUX(CLK_MOUT_MFC, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), - MUX(CLK_MOUT_MFC_1, "mout_mfc_1", group_epll_g3dpll_p, SRC_MFC, 4, 1), - MUX(CLK_MOUT_MFC_0, "mout_mfc_0", group_mout_mpll_user_t_p, SRC_MFC, 0, - 1), - - /* SRC_G3D */ - MUX(CLK_MOUT_G3D, "mout_g3d", mout_g3d_p, SRC_G3D, 8, 1), - MUX(CLK_MOUT_G3D_1, "mout_g3d_1", group_epll_g3dpll_p, SRC_G3D, 4, 1), - MUX(CLK_MOUT_G3D_0, "mout_g3d_0", group_mout_mpll_user_t_p, SRC_G3D, 0, - 1), - - /* SRC_LCD */ - MUX(CLK_MOUT_MIPI0, "mout_mipi0", group_fimc_lclk_p, SRC_LCD, 12, 4), - MUX(CLK_MOUT_FIMD0, "mout_fimd0", group_sclk_fimd0_p, SRC_LCD, 0, 4), - - /* SRC_ISP */ - MUX(CLK_MOUT_TSADC_ISP, "mout_tsadc_isp", group_fimc_lclk_p, SRC_ISP, - 16, 4), - MUX(CLK_MOUT_UART_ISP, "mout_uart_isp", group_fimc_lclk_p, SRC_ISP, - 12, 4), - MUX(CLK_MOUT_SPI1_ISP, "mout_spi1_isp", group_fimc_lclk_p, SRC_ISP, - 8, 4), - MUX(CLK_MOUT_SPI0_ISP, "mout_spi0_isp", group_fimc_lclk_p, SRC_ISP, - 4, 4), - MUX(CLK_MOUT_PWM_ISP, "mout_pwm_isp", group_fimc_lclk_p, SRC_ISP, - 0, 4), - - /* SRC_MAUDIO */ - MUX(CLK_MOUT_AUDIO0, "mout_audio0", group_sclk_audio0_p, SRC_MAUDIO, - 0, 4), - - /* SRC_FSYS */ - MUX(CLK_MOUT_TSADC, "mout_tsadc", group_sclk_p, SRC_FSYS, 28, 4), - MUX(CLK_MOUT_MMC2, "mout_mmc2", group_sclk_p, SRC_FSYS, 8, 4), - MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 4), - MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 4), - - /* SRC_PERIL0 */ - MUX(CLK_MOUT_UART3, "mout_uart3", group_sclk_p, SRC_PERIL0, 12, 4), - MUX(CLK_MOUT_UART2, "mout_uart2", group_sclk_p, SRC_PERIL0, 8, 4), - MUX(CLK_MOUT_UART1, "mout_uart1", group_sclk_p, SRC_PERIL0, 4, 4), - MUX(CLK_MOUT_UART0, "mout_uart0", group_sclk_p, SRC_PERIL0, 0, 4), - - /* SRC_PERIL1 */ - MUX(CLK_MOUT_SPI2, "mout_spi2", group_sclk_p, SRC_PERIL1, 24, 4), - MUX(CLK_MOUT_SPI1, "mout_spi1", group_sclk_p, SRC_PERIL1, 20, 4), - MUX(CLK_MOUT_SPI0, "mout_spi0", group_sclk_p, SRC_PERIL1, 16, 4), - MUX(CLK_MOUT_SPDIF, "mout_spdif", group_spdif_p, SRC_PERIL1, 8, 4), - MUX(CLK_MOUT_AUDIO2, "mout_audio2", group_sclk_audio2_p, SRC_PERIL1, - 4, 4), - MUX(CLK_MOUT_AUDIO1, "mout_audio1", group_sclk_audio1_p, SRC_PERIL1, - 0, 4), - - /* SRC_CPU */ - MUX(CLK_MOUT_MPLL_USER_C, "mout_mpll_user_c", mout_mpll_user_p, - SRC_CPU, 24, 1), - MUX(CLK_MOUT_HPM, "mout_hpm", mout_hpm_p, SRC_CPU, 20, 1), - MUX_F(CLK_MOUT_CORE, "mout_core", mout_core_p, SRC_CPU, 16, 1, 0, - CLK_MUX_READ_ONLY), - MUX_F(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, - CLK_SET_RATE_PARENT, 0), - - /* SRC_CAM1 */ - MUX(CLK_MOUT_PXLASYNC_CSIS1_FIMC, "mout_pxlasync_csis1", - group_fimc_lclk_p, SRC_CAM1, 20, 1), - MUX(CLK_MOUT_PXLASYNC_CSIS0_FIMC, "mout_pxlasync_csis0", - group_fimc_lclk_p, SRC_CAM1, 16, 1), - MUX(CLK_MOUT_JPEG, "mout_jpeg", mout_jpeg_p, SRC_CAM1, 8, 1), - MUX(CLK_MOUT_JPEG1, "mout_jpeg_1", mout_jpeg1_p, SRC_CAM1, 4, 1), - MUX(CLK_MOUT_JPEG0, "mout_jpeg_0", group_mout_mpll_user_t_p, SRC_CAM1, - 0, 1), - - /* SRC_TOP_ISP0 */ - MUX(CLK_MOUT_ACLK_ISP0_300, "mout_aclk_isp0_300", - group_aclk_isp0_300_p, SRC_TOP_ISP0, 8, 1), - MUX(CLK_MOUT_ACLK_ISP0_400, "mout_aclk_isp0_400_user", - group_aclk_isp0_400_user_p, SRC_TOP_ISP0, 4, 1), - MUX(CLK_MOUT_ACLK_ISP0_300_USER, "mout_aclk_isp0_300_user", - group_aclk_isp0_300_user_p, SRC_TOP_ISP0, 0, 1), - - /* SRC_TOP_ISP1 */ - MUX(CLK_MOUT_ACLK_ISP1_300, "mout_aclk_isp1_300", - group_aclk_isp0_300_p, SRC_TOP_ISP1, 4, 1), - MUX(CLK_MOUT_ACLK_ISP1_300_USER, "mout_aclk_isp1_300_user", - group_aclk_isp1_300_user_p, SRC_TOP_ISP1, 0, 1), -}; - -static const struct samsung_div_clock exynos4415_div_clks[] __initconst = { - /* - * NOTE: Following table is sorted by register address in ascending - * order and then bitfield shift in descending order, as it is done - * in the User's Manual. When adding new entries, please make sure - * that the order is preserved, to avoid merge conflicts and make - * further work with defined data easier. - */ - - /* DIV_LEFTBUS */ - DIV(CLK_DIV_GPL, "div_gpl", "div_gdl", DIV_LEFTBUS, 4, 3), - DIV(CLK_DIV_GDL, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 4), - - /* DIV_RIGHTBUS */ - DIV(CLK_DIV_GPR, "div_gpr", "div_gdr", DIV_RIGHTBUS, 4, 3), - DIV(CLK_DIV_GDR, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 4), - - /* DIV_TOP */ - DIV(CLK_DIV_ACLK_400_MCUISP, "div_aclk_400_mcuisp", - "mout_aclk_400_mcuisp", DIV_TOP, 24, 3), - DIV(CLK_DIV_EBI, "div_ebi", "mout_ebi_1", DIV_TOP, 16, 3), - DIV(CLK_DIV_ACLK_200, "div_aclk_200", "mout_aclk_200", DIV_TOP, 12, 3), - DIV(CLK_DIV_ACLK_160, "div_aclk_160", "mout_aclk_160", DIV_TOP, 8, 3), - DIV(CLK_DIV_ACLK_100, "div_aclk_100", "mout_aclk_100", DIV_TOP, 4, 4), - DIV(CLK_DIV_ACLK_266, "div_aclk_266", "mout_aclk_266", DIV_TOP, 0, 3), - - /* DIV_CAM */ - DIV(CLK_DIV_CSIS1, "div_csis1", "mout_csis1", DIV_CAM, 28, 4), - DIV(CLK_DIV_CSIS0, "div_csis0", "mout_csis0", DIV_CAM, 24, 4), - DIV(CLK_DIV_CAM1, "div_cam1", "mout_cam1", DIV_CAM, 20, 4), - DIV(CLK_DIV_FIMC3_LCLK, "div_fimc3_lclk", "mout_fimc3_lclk", DIV_CAM, - 12, 4), - DIV(CLK_DIV_FIMC2_LCLK, "div_fimc2_lclk", "mout_fimc2_lclk", DIV_CAM, - 8, 4), - DIV(CLK_DIV_FIMC1_LCLK, "div_fimc1_lclk", "mout_fimc1_lclk", DIV_CAM, - 4, 4), - DIV(CLK_DIV_FIMC0_LCLK, "div_fimc0_lclk", "mout_fimc0_lclk", DIV_CAM, - 0, 4), - - /* DIV_TV */ - DIV(CLK_DIV_TV_BLK, "div_tv_blk", "mout_g3d_pll", DIV_TV, 0, 4), - - /* DIV_MFC */ - DIV(CLK_DIV_MFC, "div_mfc", "mout_mfc", DIV_MFC, 0, 4), - - /* DIV_G3D */ - DIV(CLK_DIV_G3D, "div_g3d", "mout_g3d", DIV_G3D, 0, 4), - - /* DIV_LCD */ - DIV_F(CLK_DIV_MIPI0_PRE, "div_mipi0_pre", "div_mipi0", DIV_LCD, 20, 4, - CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_MIPI0, "div_mipi0", "mout_mipi0", DIV_LCD, 16, 4), - DIV(CLK_DIV_FIMD0, "div_fimd0", "mout_fimd0", DIV_LCD, 0, 4), - - /* DIV_ISP */ - DIV(CLK_DIV_UART_ISP, "div_uart_isp", "mout_uart_isp", DIV_ISP, 28, 4), - DIV_F(CLK_DIV_SPI1_ISP_PRE, "div_spi1_isp_pre", "div_spi1_isp", - DIV_ISP, 20, 8, CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_SPI1_ISP, "div_spi1_isp", "mout_spi1_isp", DIV_ISP, 16, 4), - DIV_F(CLK_DIV_SPI0_ISP_PRE, "div_spi0_isp_pre", "div_spi0_isp", - DIV_ISP, 8, 8, CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 4, 4), - DIV(CLK_DIV_PWM_ISP, "div_pwm_isp", "mout_pwm_isp", DIV_ISP, 0, 4), - - /* DIV_MAUDIO */ - DIV(CLK_DIV_PCM0, "div_pcm0", "div_audio0", DIV_MAUDIO, 4, 8), - DIV(CLK_DIV_AUDIO0, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4), - - /* DIV_FSYS0 */ - DIV_F(CLK_DIV_TSADC_PRE, "div_tsadc_pre", "div_tsadc", DIV_FSYS0, 8, 8, - CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_TSADC, "div_tsadc", "mout_tsadc", DIV_FSYS0, 0, 4), - - /* DIV_FSYS1 */ - DIV_F(CLK_DIV_MMC1_PRE, "div_mmc1_pre", "div_mmc1", DIV_FSYS1, 24, 8, - CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_MMC1, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4), - DIV_F(CLK_DIV_MMC0_PRE, "div_mmc0_pre", "div_mmc0", DIV_FSYS1, 8, 8, - CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_MMC0, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4), - - /* DIV_FSYS2 */ - DIV_F(CLK_DIV_MMC2_PRE, "div_mmc2_pre", "div_mmc2", DIV_FSYS2, 8, 8, - CLK_SET_RATE_PARENT, 0), - DIV_F(CLK_DIV_MMC2_PRE, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4, - CLK_SET_RATE_PARENT, 0), - - /* DIV_PERIL0 */ - DIV(CLK_DIV_UART3, "div_uart3", "mout_uart3", DIV_PERIL0, 12, 4), - DIV(CLK_DIV_UART2, "div_uart2", "mout_uart2", DIV_PERIL0, 8, 4), - DIV(CLK_DIV_UART1, "div_uart1", "mout_uart1", DIV_PERIL0, 4, 4), - DIV(CLK_DIV_UART0, "div_uart0", "mout_uart0", DIV_PERIL0, 0, 4), - - /* DIV_PERIL1 */ - DIV_F(CLK_DIV_SPI1_PRE, "div_spi1_pre", "div_spi1", DIV_PERIL1, 24, 8, - CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_SPI1, "div_spi1", "mout_spi1", DIV_PERIL1, 16, 4), - DIV_F(CLK_DIV_SPI0_PRE, "div_spi0_pre", "div_spi0", DIV_PERIL1, 8, 8, - CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_SPI0, "div_spi0", "mout_spi0", DIV_PERIL1, 0, 4), - - /* DIV_PERIL2 */ - DIV_F(CLK_DIV_SPI2_PRE, "div_spi2_pre", "div_spi2", DIV_PERIL2, 8, 8, - CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_SPI2, "div_spi2", "mout_spi2", DIV_PERIL2, 0, 4), - - /* DIV_PERIL4 */ - DIV(CLK_DIV_PCM2, "div_pcm2", "div_audio2", DIV_PERIL4, 20, 8), - DIV(CLK_DIV_AUDIO2, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4), - DIV(CLK_DIV_PCM1, "div_pcm1", "div_audio1", DIV_PERIL4, 20, 8), - DIV(CLK_DIV_AUDIO1, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4), - - /* DIV_PERIL5 */ - DIV(CLK_DIV_I2S1, "div_i2s1", "div_audio1", DIV_PERIL5, 0, 6), - - /* DIV_CAM1 */ - DIV(CLK_DIV_PXLASYNC_CSIS1_FIMC, "div_pxlasync_csis1_fimc", - "mout_pxlasync_csis1", DIV_CAM1, 24, 4), - DIV(CLK_DIV_PXLASYNC_CSIS0_FIMC, "div_pxlasync_csis0_fimc", - "mout_pxlasync_csis0", DIV_CAM1, 20, 4), - DIV(CLK_DIV_JPEG, "div_jpeg", "mout_jpeg", DIV_CAM1, 0, 4), - - /* DIV_CPU0 */ - DIV(CLK_DIV_CORE2, "div_core2", "div_core", DIV_CPU0, 28, 3), - DIV_F(CLK_DIV_APLL, "div_apll", "mout_apll", DIV_CPU0, 24, 3, - CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), - DIV(CLK_DIV_PCLK_DBG, "div_pclk_dbg", "div_core2", DIV_CPU0, 20, 3), - DIV(CLK_DIV_ATB, "div_atb", "div_core2", DIV_CPU0, 16, 3), - DIV(CLK_DIV_PERIPH, "div_periph", "div_core2", DIV_CPU0, 12, 3), - DIV(CLK_DIV_COREM1, "div_corem1", "div_core2", DIV_CPU0, 8, 3), - DIV(CLK_DIV_COREM0, "div_corem0", "div_core2", DIV_CPU0, 4, 3), - DIV_F(CLK_DIV_CORE, "div_core", "mout_core", DIV_CPU0, 0, 3, - CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), - - /* DIV_CPU1 */ - DIV(CLK_DIV_HPM, "div_hpm", "div_copy", DIV_CPU1, 4, 3), - DIV(CLK_DIV_COPY, "div_copy", "mout_hpm", DIV_CPU1, 0, 3), -}; - -static const struct samsung_gate_clock exynos4415_gate_clks[] __initconst = { - /* - * NOTE: Following table is sorted by register address in ascending - * order and then bitfield shift in descending order, as it is done - * in the User's Manual. When adding new entries, please make sure - * that the order is preserved, to avoid merge conflicts and make - * further work with defined data easier. - */ - - /* GATE_IP_LEFTBUS */ - GATE(CLK_ASYNC_G3D, "async_g3d", "div_aclk_100", GATE_IP_LEFTBUS, 6, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_ASYNC_MFCL, "async_mfcl", "div_aclk_100", GATE_IP_LEFTBUS, 4, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_ASYNC_TVX, "async_tvx", "div_aclk_100", GATE_IP_LEFTBUS, 3, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_PPMULEFT, "ppmuleft", "div_aclk_100", GATE_IP_LEFTBUS, 1, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_GPIO_LEFT, "gpio_left", "div_aclk_100", GATE_IP_LEFTBUS, 0, - CLK_IGNORE_UNUSED, 0), - - /* GATE_IP_IMAGE */ - GATE(CLK_PPMUIMAGE, "ppmuimage", "div_aclk_100", GATE_IP_IMAGE, - 9, 0, 0), - GATE(CLK_QEMDMA2, "qe_mdma2", "div_aclk_100", GATE_IP_IMAGE, - 8, 0, 0), - GATE(CLK_QEROTATOR, "qe_rotator", "div_aclk_100", GATE_IP_IMAGE, - 7, 0, 0), - GATE(CLK_SMMUMDMA2, "smmu_mdam2", "div_aclk_100", GATE_IP_IMAGE, - 5, 0, 0), - GATE(CLK_SMMUROTATOR, "smmu_rotator", "div_aclk_100", GATE_IP_IMAGE, - 4, 0, 0), - GATE(CLK_MDMA2, "mdma2", "div_aclk_100", GATE_IP_IMAGE, 2, 0, 0), - GATE(CLK_ROTATOR, "rotator", "div_aclk_100", GATE_IP_IMAGE, 1, 0, 0), - - /* GATE_IP_RIGHTBUS */ - GATE(CLK_ASYNC_ISPMX, "async_ispmx", "div_aclk_100", - GATE_IP_RIGHTBUS, 9, CLK_IGNORE_UNUSED, 0), - GATE(CLK_ASYNC_MAUDIOX, "async_maudiox", "div_aclk_100", - GATE_IP_RIGHTBUS, 7, CLK_IGNORE_UNUSED, 0), - GATE(CLK_ASYNC_MFCR, "async_mfcr", "div_aclk_100", - GATE_IP_RIGHTBUS, 6, CLK_IGNORE_UNUSED, 0), - GATE(CLK_ASYNC_FSYSD, "async_fsysd", "div_aclk_100", - GATE_IP_RIGHTBUS, 5, CLK_IGNORE_UNUSED, 0), - GATE(CLK_ASYNC_LCD0X, "async_lcd0x", "div_aclk_100", - GATE_IP_RIGHTBUS, 3, CLK_IGNORE_UNUSED, 0), - GATE(CLK_ASYNC_CAMX, "async_camx", "div_aclk_100", - GATE_IP_RIGHTBUS, 2, CLK_IGNORE_UNUSED, 0), - GATE(CLK_PPMURIGHT, "ppmuright", "div_aclk_100", - GATE_IP_RIGHTBUS, 1, CLK_IGNORE_UNUSED, 0), - GATE(CLK_GPIO_RIGHT, "gpio_right", "div_aclk_100", - GATE_IP_RIGHTBUS, 0, CLK_IGNORE_UNUSED, 0), - - /* GATE_IP_PERIR */ - GATE(CLK_ANTIRBK_APBIF, "antirbk_apbif", "div_aclk_100", - GATE_IP_PERIR, 24, CLK_IGNORE_UNUSED, 0), - GATE(CLK_EFUSE_WRITER_APBIF, "efuse_writer_apbif", "div_aclk_100", - GATE_IP_PERIR, 23, CLK_IGNORE_UNUSED, 0), - GATE(CLK_MONOCNT, "monocnt", "div_aclk_100", GATE_IP_PERIR, 22, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC6, "tzpc6", "div_aclk_100", GATE_IP_PERIR, 21, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_PROVISIONKEY1, "provisionkey1", "div_aclk_100", - GATE_IP_PERIR, 20, CLK_IGNORE_UNUSED, 0), - GATE(CLK_PROVISIONKEY0, "provisionkey0", "div_aclk_100", - GATE_IP_PERIR, 19, CLK_IGNORE_UNUSED, 0), - GATE(CLK_CMU_ISPPART, "cmu_isppart", "div_aclk_100", GATE_IP_PERIR, 18, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TMU_APBIF, "tmu_apbif", "div_aclk_100", - GATE_IP_PERIR, 17, 0, 0), - GATE(CLK_KEYIF, "keyif", "div_aclk_100", GATE_IP_PERIR, 16, 0, 0), - GATE(CLK_RTC, "rtc", "div_aclk_100", GATE_IP_PERIR, 15, 0, 0), - GATE(CLK_WDT, "wdt", "div_aclk_100", GATE_IP_PERIR, 14, 0, 0), - GATE(CLK_MCT, "mct", "div_aclk_100", GATE_IP_PERIR, 13, 0, 0), - GATE(CLK_SECKEY, "seckey", "div_aclk_100", GATE_IP_PERIR, 12, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_HDMI_CEC, "hdmi_cec", "div_aclk_100", GATE_IP_PERIR, 11, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC5, "tzpc5", "div_aclk_100", GATE_IP_PERIR, 10, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC4, "tzpc4", "div_aclk_100", GATE_IP_PERIR, 9, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC3, "tzpc3", "div_aclk_100", GATE_IP_PERIR, 8, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC2, "tzpc2", "div_aclk_100", GATE_IP_PERIR, 7, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC1, "tzpc1", "div_aclk_100", GATE_IP_PERIR, 6, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_TZPC0, "tzpc0", "div_aclk_100", GATE_IP_PERIR, 5, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_CMU_COREPART, "cmu_corepart", "div_aclk_100", GATE_IP_PERIR, 4, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_CMU_TOPPART, "cmu_toppart", "div_aclk_100", GATE_IP_PERIR, 3, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_PMU_APBIF, "pmu_apbif", "div_aclk_100", GATE_IP_PERIR, 2, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_SYSREG, "sysreg", "div_aclk_100", GATE_IP_PERIR, 1, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_CHIP_ID, "chip_id", "div_aclk_100", GATE_IP_PERIR, 0, - CLK_IGNORE_UNUSED, 0), - - /* GATE_SCLK_CAM - non-completed */ - GATE(CLK_SCLK_PXLAYSNC_CSIS1_FIMC, "sclk_pxlasync_csis1_fimc", - "div_pxlasync_csis1_fimc", GATE_SCLK_CAM, 11, - CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_PXLAYSNC_CSIS0_FIMC, "sclk_pxlasync_csis0_fimc", - "div_pxlasync_csis0_fimc", GATE_SCLK_CAM, - 10, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_JPEG, "sclk_jpeg", "div_jpeg", - GATE_SCLK_CAM, 8, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_CSIS1, "sclk_csis1", "div_csis1", - GATE_SCLK_CAM, 7, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_CSIS0, "sclk_csis0", "div_csis0", - GATE_SCLK_CAM, 6, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_CAM1, "sclk_cam1", "div_cam1", - GATE_SCLK_CAM, 5, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_FIMC3_LCLK, "sclk_fimc3_lclk", "div_fimc3_lclk", - GATE_SCLK_CAM, 3, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_FIMC2_LCLK, "sclk_fimc2_lclk", "div_fimc2_lclk", - GATE_SCLK_CAM, 2, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_FIMC1_LCLK, "sclk_fimc1_lclk", "div_fimc1_lclk", - GATE_SCLK_CAM, 1, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_FIMC0_LCLK, "sclk_fimc0_lclk", "div_fimc0_lclk", - GATE_SCLK_CAM, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_SCLK_TV */ - GATE(CLK_SCLK_PIXEL, "sclk_pixel", "div_tv_blk", - GATE_SCLK_TV, 3, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi", - GATE_SCLK_TV, 2, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_MIXER, "sclk_mixer", "div_tv_blk", - GATE_SCLK_TV, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_SCLK_MFC */ - GATE(CLK_SCLK_MFC, "sclk_mfc", "div_mfc", - GATE_SCLK_MFC, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_SCLK_G3D */ - GATE(CLK_SCLK_G3D, "sclk_g3d", "div_g3d", - GATE_SCLK_G3D, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_SCLK_LCD */ - GATE(CLK_SCLK_MIPIDPHY4L, "sclk_mipidphy4l", "div_mipi0", - GATE_SCLK_LCD, 4, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_MIPI0, "sclk_mipi0", "div_mipi0_pre", - GATE_SCLK_LCD, 3, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_MDNIE0, "sclk_mdnie0", "div_fimd0", - GATE_SCLK_LCD, 1, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_FIMD0, "sclk_fimd0", "div_fimd0", - GATE_SCLK_LCD, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_SCLK_MAUDIO */ - GATE(CLK_SCLK_PCM0, "sclk_pcm0", "div_pcm0", - GATE_SCLK_MAUDIO, 1, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_AUDIO0, "sclk_audio0", "div_audio0", - GATE_SCLK_MAUDIO, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_SCLK_FSYS */ - GATE(CLK_SCLK_TSADC, "sclk_tsadc", "div_tsadc_pre", - GATE_SCLK_FSYS, 9, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_EBI, "sclk_ebi", "div_ebi", - GATE_SCLK_FSYS, 6, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_MMC2, "sclk_mmc2", "div_mmc2_pre", - GATE_SCLK_FSYS, 2, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_MMC1, "sclk_mmc1", "div_mmc1_pre", - GATE_SCLK_FSYS, 1, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_MMC0, "sclk_mmc0", "div_mmc0_pre", - GATE_SCLK_FSYS, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_SCLK_PERIL */ - GATE(CLK_SCLK_I2S, "sclk_i2s1", "div_i2s1", - GATE_SCLK_PERIL, 18, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_PCM2, "sclk_pcm2", "div_pcm2", - GATE_SCLK_PERIL, 16, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_PCM1, "sclk_pcm1", "div_pcm1", - GATE_SCLK_PERIL, 15, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_AUDIO2, "sclk_audio2", "div_audio2", - GATE_SCLK_PERIL, 14, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_AUDIO1, "sclk_audio1", "div_audio1", - GATE_SCLK_PERIL, 13, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_SPDIF, "sclk_spdif", "mout_spdif", - GATE_SCLK_PERIL, 10, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_SPI2, "sclk_spi2", "div_spi2_pre", - GATE_SCLK_PERIL, 8, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_SPI1, "sclk_spi1", "div_spi1_pre", - GATE_SCLK_PERIL, 7, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_SPI0, "sclk_spi0", "div_spi0_pre", - GATE_SCLK_PERIL, 6, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_UART3, "sclk_uart3", "div_uart3", - GATE_SCLK_PERIL, 3, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_UART2, "sclk_uart2", "div_uart2", - GATE_SCLK_PERIL, 2, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_UART1, "sclk_uart1", "div_uart1", - GATE_SCLK_PERIL, 1, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_UART0, "sclk_uart0", "div_uart0", - GATE_SCLK_PERIL, 0, CLK_SET_RATE_PARENT, 0), - - /* GATE_IP_CAM */ - GATE(CLK_SMMUFIMC_LITE2, "smmufimc_lite2", "div_aclk_160", GATE_IP_CAM, - 22, CLK_IGNORE_UNUSED, 0), - GATE(CLK_FIMC_LITE2, "fimc_lite2", "div_aclk_160", GATE_IP_CAM, - 20, CLK_IGNORE_UNUSED, 0), - GATE(CLK_PIXELASYNCM1, "pixelasyncm1", "div_aclk_160", GATE_IP_CAM, - 18, CLK_IGNORE_UNUSED, 0), - GATE(CLK_PIXELASYNCM0, "pixelasyncm0", "div_aclk_160", GATE_IP_CAM, - 17, CLK_IGNORE_UNUSED, 0), - GATE(CLK_PPMUCAMIF, "ppmucamif", "div_aclk_160", GATE_IP_CAM, - 16, CLK_IGNORE_UNUSED, 0), - GATE(CLK_SMMUJPEG, "smmujpeg", "div_aclk_160", GATE_IP_CAM, 11, 0, 0), - GATE(CLK_SMMUFIMC3, "smmufimc3", "div_aclk_160", GATE_IP_CAM, 10, 0, 0), - GATE(CLK_SMMUFIMC2, "smmufimc2", "div_aclk_160", GATE_IP_CAM, 9, 0, 0), - GATE(CLK_SMMUFIMC1, "smmufimc1", "div_aclk_160", GATE_IP_CAM, 8, 0, 0), - GATE(CLK_SMMUFIMC0, "smmufimc0", "div_aclk_160", GATE_IP_CAM, 7, 0, 0), - GATE(CLK_JPEG, "jpeg", "div_aclk_160", GATE_IP_CAM, 6, 0, 0), - GATE(CLK_CSIS1, "csis1", "div_aclk_160", GATE_IP_CAM, 5, 0, 0), - GATE(CLK_CSIS0, "csis0", "div_aclk_160", GATE_IP_CAM, 4, 0, 0), - GATE(CLK_FIMC3, "fimc3", "div_aclk_160", GATE_IP_CAM, 3, 0, 0), - GATE(CLK_FIMC2, "fimc2", "div_aclk_160", GATE_IP_CAM, 2, 0, 0), - GATE(CLK_FIMC1, "fimc1", "div_aclk_160", GATE_IP_CAM, 1, 0, 0), - GATE(CLK_FIMC0, "fimc0", "div_aclk_160", GATE_IP_CAM, 0, 0, 0), - - /* GATE_IP_TV */ - GATE(CLK_PPMUTV, "ppmutv", "div_aclk_100", GATE_IP_TV, 5, 0, 0), - GATE(CLK_SMMUTV, "smmutv", "div_aclk_100", GATE_IP_TV, 4, 0, 0), - GATE(CLK_HDMI, "hdmi", "div_aclk_100", GATE_IP_TV, 3, 0, 0), - GATE(CLK_MIXER, "mixer", "div_aclk_100", GATE_IP_TV, 1, 0, 0), - GATE(CLK_VP, "vp", "div_aclk_100", GATE_IP_TV, 0, 0, 0), - - /* GATE_IP_MFC */ - GATE(CLK_PPMUMFC_R, "ppmumfc_r", "div_aclk_200", GATE_IP_MFC, 4, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_PPMUMFC_L, "ppmumfc_l", "div_aclk_200", GATE_IP_MFC, 3, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_SMMUMFC_R, "smmumfc_r", "div_aclk_200", GATE_IP_MFC, 2, 0, 0), - GATE(CLK_SMMUMFC_L, "smmumfc_l", "div_aclk_200", GATE_IP_MFC, 1, 0, 0), - GATE(CLK_MFC, "mfc", "div_aclk_200", GATE_IP_MFC, 0, 0, 0), - - /* GATE_IP_G3D */ - GATE(CLK_PPMUG3D, "ppmug3d", "div_aclk_200", GATE_IP_G3D, 1, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_G3D, "g3d", "div_aclk_200", GATE_IP_G3D, 0, 0, 0), - - /* GATE_IP_LCD */ - GATE(CLK_PPMULCD0, "ppmulcd0", "div_aclk_160", GATE_IP_LCD, 5, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_SMMUFIMD0, "smmufimd0", "div_aclk_160", GATE_IP_LCD, 4, 0, 0), - GATE(CLK_DSIM0, "dsim0", "div_aclk_160", GATE_IP_LCD, 3, 0, 0), - GATE(CLK_SMIES, "smies", "div_aclk_160", GATE_IP_LCD, 2, 0, 0), - GATE(CLK_MIE0, "mie0", "div_aclk_160", GATE_IP_LCD, 1, 0, 0), - GATE(CLK_FIMD0, "fimd0", "div_aclk_160", GATE_IP_LCD, 0, 0, 0), - - /* GATE_IP_FSYS */ - GATE(CLK_TSADC, "tsadc", "div_aclk_200", GATE_IP_FSYS, 20, 0, 0), - GATE(CLK_PPMUFILE, "ppmufile", "div_aclk_200", GATE_IP_FSYS, 17, - CLK_IGNORE_UNUSED, 0), - GATE(CLK_NFCON, "nfcon", "div_aclk_200", GATE_IP_FSYS, 16, 0, 0), - GATE(CLK_USBDEVICE, "usbdevice", "div_aclk_200", GATE_IP_FSYS, 13, - 0, 0), - GATE(CLK_USBHOST, "usbhost", "div_aclk_200", GATE_IP_FSYS, 12, 0, 0), - GATE(CLK_SROMC, "sromc", "div_aclk_200", GATE_IP_FSYS, 11, 0, 0), - GATE(CLK_SDMMC2, "sdmmc2", "div_aclk_200", GATE_IP_FSYS, 7, 0, 0), - GATE(CLK_SDMMC1, "sdmmc1", "div_aclk_200", GATE_IP_FSYS, 6, 0, 0), - GATE(CLK_SDMMC0, "sdmmc0", "div_aclk_200", GATE_IP_FSYS, 5, 0, 0), - GATE(CLK_PDMA1, "pdma1", "div_aclk_200", GATE_IP_FSYS, 1, 0, 0), - GATE(CLK_PDMA0, "pdma0", "div_aclk_200", GATE_IP_FSYS, 0, 0, 0), - - /* GATE_IP_PERIL */ - GATE(CLK_SPDIF, "spdif", "div_aclk_100", GATE_IP_PERIL, 26, 0, 0), - GATE(CLK_PWM, "pwm", "div_aclk_100", GATE_IP_PERIL, 24, 0, 0), - GATE(CLK_PCM2, "pcm2", "div_aclk_100", GATE_IP_PERIL, 23, 0, 0), - GATE(CLK_PCM1, "pcm1", "div_aclk_100", GATE_IP_PERIL, 22, 0, 0), - GATE(CLK_I2S1, "i2s1", "div_aclk_100", GATE_IP_PERIL, 20, 0, 0), - GATE(CLK_SPI2, "spi2", "div_aclk_100", GATE_IP_PERIL, 18, 0, 0), - GATE(CLK_SPI1, "spi1", "div_aclk_100", GATE_IP_PERIL, 17, 0, 0), - GATE(CLK_SPI0, "spi0", "div_aclk_100", GATE_IP_PERIL, 16, 0, 0), - GATE(CLK_I2CHDMI, "i2chdmi", "div_aclk_100", GATE_IP_PERIL, 14, 0, 0), - GATE(CLK_I2C7, "i2c7", "div_aclk_100", GATE_IP_PERIL, 13, 0, 0), - GATE(CLK_I2C6, "i2c6", "div_aclk_100", GATE_IP_PERIL, 12, 0, 0), - GATE(CLK_I2C5, "i2c5", "div_aclk_100", GATE_IP_PERIL, 11, 0, 0), - GATE(CLK_I2C4, "i2c4", "div_aclk_100", GATE_IP_PERIL, 10, 0, 0), - GATE(CLK_I2C3, "i2c3", "div_aclk_100", GATE_IP_PERIL, 9, 0, 0), - GATE(CLK_I2C2, "i2c2", "div_aclk_100", GATE_IP_PERIL, 8, 0, 0), - GATE(CLK_I2C1, "i2c1", "div_aclk_100", GATE_IP_PERIL, 7, 0, 0), - GATE(CLK_I2C0, "i2c0", "div_aclk_100", GATE_IP_PERIL, 6, 0, 0), - GATE(CLK_UART3, "uart3", "div_aclk_100", GATE_IP_PERIL, 3, 0, 0), - GATE(CLK_UART2, "uart2", "div_aclk_100", GATE_IP_PERIL, 2, 0, 0), - GATE(CLK_UART1, "uart1", "div_aclk_100", GATE_IP_PERIL, 1, 0, 0), - GATE(CLK_UART0, "uart0", "div_aclk_100", GATE_IP_PERIL, 0, 0, 0), -}; - -/* - * APLL & MPLL & BPLL & ISP_PLL & DISP_PLL & G3D_PLL - */ -static const struct samsung_pll_rate_table exynos4415_pll_rates[] __initconst = { - PLL_35XX_RATE(1600000000, 400, 3, 1), - PLL_35XX_RATE(1500000000, 250, 2, 1), - PLL_35XX_RATE(1400000000, 175, 3, 0), - PLL_35XX_RATE(1300000000, 325, 3, 1), - PLL_35XX_RATE(1200000000, 400, 4, 1), - PLL_35XX_RATE(1100000000, 275, 3, 1), - PLL_35XX_RATE(1066000000, 533, 6, 1), - PLL_35XX_RATE(1000000000, 250, 3, 1), - PLL_35XX_RATE(960000000, 320, 4, 1), - PLL_35XX_RATE(900000000, 300, 4, 1), - PLL_35XX_RATE(850000000, 425, 6, 1), - PLL_35XX_RATE(800000000, 200, 3, 1), - PLL_35XX_RATE(700000000, 175, 3, 1), - PLL_35XX_RATE(667000000, 667, 12, 1), - PLL_35XX_RATE(600000000, 400, 4, 2), - PLL_35XX_RATE(550000000, 275, 3, 2), - PLL_35XX_RATE(533000000, 533, 6, 2), - PLL_35XX_RATE(520000000, 260, 3, 2), - PLL_35XX_RATE(500000000, 250, 3, 2), - PLL_35XX_RATE(440000000, 220, 3, 2), - PLL_35XX_RATE(400000000, 200, 3, 2), - PLL_35XX_RATE(350000000, 175, 3, 2), - PLL_35XX_RATE(300000000, 300, 3, 3), - PLL_35XX_RATE(266000000, 266, 3, 3), - PLL_35XX_RATE(200000000, 200, 3, 3), - PLL_35XX_RATE(160000000, 160, 3, 3), - PLL_35XX_RATE(100000000, 200, 3, 4), - { /* sentinel */ } -}; - -/* EPLL */ -static const struct samsung_pll_rate_table exynos4415_epll_rates[] __initconst = { - PLL_36XX_RATE(800000000, 200, 3, 1, 0), - PLL_36XX_RATE(288000000, 96, 2, 2, 0), - PLL_36XX_RATE(192000000, 128, 2, 3, 0), - PLL_36XX_RATE(144000000, 96, 2, 3, 0), - PLL_36XX_RATE(96000000, 128, 2, 4, 0), - PLL_36XX_RATE(84000000, 112, 2, 4, 0), - PLL_36XX_RATE(80750011, 107, 2, 4, 43691), - PLL_36XX_RATE(73728004, 98, 2, 4, 19923), - PLL_36XX_RATE(67987602, 271, 3, 5, 62285), - PLL_36XX_RATE(65911004, 175, 2, 5, 49982), - PLL_36XX_RATE(50000000, 200, 3, 5, 0), - PLL_36XX_RATE(49152003, 131, 2, 5, 4719), - PLL_36XX_RATE(48000000, 128, 2, 5, 0), - PLL_36XX_RATE(45250000, 181, 3, 5, 0), - { /* sentinel */ } -}; - -static const struct samsung_pll_clock exynos4415_plls[] __initconst = { - PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", - APLL_LOCK, APLL_CON0, exynos4415_pll_rates), - PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll", - EPLL_LOCK, EPLL_CON0, exynos4415_epll_rates), - PLL(pll_35xx, CLK_FOUT_G3D_PLL, "fout_g3d_pll", "mout_g3d_pllsrc", - G3D_PLL_LOCK, G3D_PLL_CON0, exynos4415_pll_rates), - PLL(pll_35xx, CLK_FOUT_ISP_PLL, "fout_isp_pll", "fin_pll", - ISP_PLL_LOCK, ISP_PLL_CON0, exynos4415_pll_rates), - PLL(pll_35xx, CLK_FOUT_DISP_PLL, "fout_disp_pll", - "fin_pll", DISP_PLL_LOCK, DISP_PLL_CON0, exynos4415_pll_rates), -}; - -static const struct samsung_cmu_info cmu_info __initconst = { - .pll_clks = exynos4415_plls, - .nr_pll_clks = ARRAY_SIZE(exynos4415_plls), - .mux_clks = exynos4415_mux_clks, - .nr_mux_clks = ARRAY_SIZE(exynos4415_mux_clks), - .div_clks = exynos4415_div_clks, - .nr_div_clks = ARRAY_SIZE(exynos4415_div_clks), - .gate_clks = exynos4415_gate_clks, - .nr_gate_clks = ARRAY_SIZE(exynos4415_gate_clks), - .fixed_clks = exynos4415_fixed_rate_clks, - .nr_fixed_clks = ARRAY_SIZE(exynos4415_fixed_rate_clks), - .fixed_factor_clks = exynos4415_fixed_factor_clks, - .nr_fixed_factor_clks = ARRAY_SIZE(exynos4415_fixed_factor_clks), - .nr_clk_ids = CLK_NR_CLKS, - .clk_regs = exynos4415_cmu_clk_regs, - .nr_clk_regs = ARRAY_SIZE(exynos4415_cmu_clk_regs), -}; - -static void __init exynos4415_cmu_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &cmu_info); -} -CLK_OF_DECLARE(exynos4415_cmu, "samsung,exynos4415-cmu", exynos4415_cmu_init); - -/* - * CMU DMC - */ - -#define MPLL_LOCK 0x008 -#define MPLL_CON0 0x108 -#define MPLL_CON1 0x10c -#define MPLL_CON2 0x110 -#define BPLL_LOCK 0x118 -#define BPLL_CON0 0x218 -#define BPLL_CON1 0x21c -#define BPLL_CON2 0x220 -#define SRC_DMC 0x300 -#define DIV_DMC1 0x504 - -static const unsigned long exynos4415_cmu_dmc_clk_regs[] __initconst = { - MPLL_LOCK, - MPLL_CON0, - MPLL_CON1, - MPLL_CON2, - BPLL_LOCK, - BPLL_CON0, - BPLL_CON1, - BPLL_CON2, - SRC_DMC, - DIV_DMC1, -}; - -PNAME(mout_mpll_p) = { "fin_pll", "fout_mpll", }; -PNAME(mout_bpll_p) = { "fin_pll", "fout_bpll", }; -PNAME(mbpll_p) = { "mout_mpll", "mout_bpll", }; - -static const struct samsung_mux_clock exynos4415_dmc_mux_clks[] __initconst = { - MUX(CLK_DMC_MOUT_MPLL, "mout_mpll", mout_mpll_p, SRC_DMC, 12, 1), - MUX(CLK_DMC_MOUT_BPLL, "mout_bpll", mout_bpll_p, SRC_DMC, 10, 1), - MUX(CLK_DMC_MOUT_DPHY, "mout_dphy", mbpll_p, SRC_DMC, 8, 1), - MUX(CLK_DMC_MOUT_DMC_BUS, "mout_dmc_bus", mbpll_p, SRC_DMC, 4, 1), -}; - -static const struct samsung_div_clock exynos4415_dmc_div_clks[] __initconst = { - DIV(CLK_DMC_DIV_DMC, "div_dmc", "div_dmc_pre", DIV_DMC1, 27, 3), - DIV(CLK_DMC_DIV_DPHY, "div_dphy", "mout_dphy", DIV_DMC1, 23, 3), - DIV(CLK_DMC_DIV_DMC_PRE, "div_dmc_pre", "mout_dmc_bus", - DIV_DMC1, 19, 2), - DIV(CLK_DMC_DIV_DMCP, "div_dmcp", "div_dmcd", DIV_DMC1, 15, 3), - DIV(CLK_DMC_DIV_DMCD, "div_dmcd", "div_dmc", DIV_DMC1, 11, 3), - DIV(CLK_DMC_DIV_MPLL_PRE, "div_mpll_pre", "mout_mpll", DIV_DMC1, 8, 2), -}; - -static const struct samsung_pll_clock exynos4415_dmc_plls[] __initconst = { - PLL(pll_35xx, CLK_DMC_FOUT_MPLL, "fout_mpll", "fin_pll", - MPLL_LOCK, MPLL_CON0, exynos4415_pll_rates), - PLL(pll_35xx, CLK_DMC_FOUT_BPLL, "fout_bpll", "fin_pll", - BPLL_LOCK, BPLL_CON0, exynos4415_pll_rates), -}; - -static const struct samsung_cmu_info cmu_dmc_info __initconst = { - .pll_clks = exynos4415_dmc_plls, - .nr_pll_clks = ARRAY_SIZE(exynos4415_dmc_plls), - .mux_clks = exynos4415_dmc_mux_clks, - .nr_mux_clks = ARRAY_SIZE(exynos4415_dmc_mux_clks), - .div_clks = exynos4415_dmc_div_clks, - .nr_div_clks = ARRAY_SIZE(exynos4415_dmc_div_clks), - .nr_clk_ids = NR_CLKS_DMC, - .clk_regs = exynos4415_cmu_dmc_clk_regs, - .nr_clk_regs = ARRAY_SIZE(exynos4415_cmu_dmc_clk_regs), -}; - -static void __init exynos4415_cmu_dmc_init(struct device_node *np) -{ - samsung_cmu_register_one(np, &cmu_dmc_info); -} -CLK_OF_DECLARE(exynos4415_cmu_dmc, "samsung,exynos4415-cmu-dmc", - exynos4415_cmu_dmc_init); diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index f096bd7df40c..11343a597093 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -6,7 +6,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Common Clock Framework support for Exynos5443 SoC. + * Common Clock Framework support for Exynos5433 SoC. */ #include <linux/clk-provider.h> @@ -549,10 +549,10 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { 29, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_BUS0_400, "aclk_bus0_400", "div_aclk_bus0_400", ENABLE_ACLK_TOP, 26, - CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_BUS1_400, "aclk_bus1_400", "div_aclk_bus1_400", ENABLE_ACLK_TOP, 25, - CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_IMEM_200, "aclk_imem_200", "div_aclk_imem_266", ENABLE_ACLK_TOP, 24, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), @@ -616,7 +616,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { /* ENABLE_SCLK_TOP_MSCL */ GATE(CLK_SCLK_JPEG_MSCL, "sclk_jpeg_mscl", "div_sclk_jpeg", - ENABLE_SCLK_TOP_MSCL, 0, 0, 0), + ENABLE_SCLK_TOP_MSCL, 0, CLK_SET_RATE_PARENT, 0), /* ENABLE_SCLK_TOP_CAM1 */ GATE(CLK_SCLK_ISP_SENSOR2, "sclk_isp_sensor2", "div_sclk_isp_sensor2_b", @@ -698,7 +698,7 @@ static const struct samsung_gate_clock top_gate_clks[] __initconst = { * ATLAS_PLL & APOLLO_PLL & MEM0_PLL & MEM1_PLL & BUS_PLL & MFC_PLL * & MPHY_PLL & G3D_PLL & DISP_PLL & ISP_PLL */ -static const struct samsung_pll_rate_table exynos5443_pll_rates[] __initconst = { +static const struct samsung_pll_rate_table exynos5433_pll_rates[] __initconst = { PLL_35XX_RATE(2500000000U, 625, 6, 0), PLL_35XX_RATE(2400000000U, 500, 5, 0), PLL_35XX_RATE(2300000000U, 575, 6, 0), @@ -739,7 +739,9 @@ static const struct samsung_pll_rate_table exynos5443_pll_rates[] __initconst = PLL_35XX_RATE(350000000U, 350, 6, 2), PLL_35XX_RATE(333000000U, 222, 4, 2), PLL_35XX_RATE(300000000U, 500, 5, 3), + PLL_35XX_RATE(278000000U, 556, 6, 3), PLL_35XX_RATE(266000000U, 532, 6, 3), + PLL_35XX_RATE(250000000U, 500, 6, 3), PLL_35XX_RATE(200000000U, 400, 6, 3), PLL_35XX_RATE(166000000U, 332, 6, 3), PLL_35XX_RATE(160000000U, 320, 6, 3), @@ -749,7 +751,7 @@ static const struct samsung_pll_rate_table exynos5443_pll_rates[] __initconst = }; /* AUD_PLL */ -static const struct samsung_pll_rate_table exynos5443_aud_pll_rates[] __initconst = { +static const struct samsung_pll_rate_table exynos5433_aud_pll_rates[] __initconst = { PLL_36XX_RATE(400000000U, 200, 3, 2, 0), PLL_36XX_RATE(393216000U, 197, 3, 2, -25690), PLL_36XX_RATE(384000000U, 128, 2, 2, 0), @@ -764,9 +766,9 @@ static const struct samsung_pll_rate_table exynos5443_aud_pll_rates[] __initcons static const struct samsung_pll_clock top_pll_clks[] __initconst = { PLL(pll_35xx, CLK_FOUT_ISP_PLL, "fout_isp_pll", "oscclk", - ISP_PLL_LOCK, ISP_PLL_CON0, exynos5443_pll_rates), + ISP_PLL_LOCK, ISP_PLL_CON0, exynos5433_pll_rates), PLL(pll_36xx, CLK_FOUT_AUD_PLL, "fout_aud_pll", "oscclk", - AUD_PLL_LOCK, AUD_PLL_CON0, exynos5443_aud_pll_rates), + AUD_PLL_LOCK, AUD_PLL_CON0, exynos5433_aud_pll_rates), }; static const struct samsung_cmu_info top_cmu_info __initconst = { @@ -820,7 +822,7 @@ PNAME(mout_mphy_pll_p) = { "oscclk", "fout_mphy_pll", }; static const struct samsung_pll_clock cpif_pll_clks[] __initconst = { PLL(pll_35xx, CLK_FOUT_MPHY_PLL, "fout_mphy_pll", "oscclk", - MPHY_PLL_LOCK, MPHY_PLL_CON0, exynos5443_pll_rates), + MPHY_PLL_LOCK, MPHY_PLL_CON0, exynos5433_pll_rates), }; static const struct samsung_mux_clock cpif_mux_clks[] __initconst = { @@ -1011,13 +1013,13 @@ static const unsigned long mif_clk_regs[] __initconst = { static const struct samsung_pll_clock mif_pll_clks[] __initconst = { PLL(pll_35xx, CLK_FOUT_MEM0_PLL, "fout_mem0_pll", "oscclk", - MEM0_PLL_LOCK, MEM0_PLL_CON0, exynos5443_pll_rates), + MEM0_PLL_LOCK, MEM0_PLL_CON0, exynos5433_pll_rates), PLL(pll_35xx, CLK_FOUT_MEM1_PLL, "fout_mem1_pll", "oscclk", - MEM1_PLL_LOCK, MEM1_PLL_CON0, exynos5443_pll_rates), + MEM1_PLL_LOCK, MEM1_PLL_CON0, exynos5433_pll_rates), PLL(pll_35xx, CLK_FOUT_BUS_PLL, "fout_bus_pll", "oscclk", - BUS_PLL_LOCK, BUS_PLL_CON0, exynos5443_pll_rates), + BUS_PLL_LOCK, BUS_PLL_CON0, exynos5433_pll_rates), PLL(pll_35xx, CLK_FOUT_MFC_PLL, "fout_mfc_pll", "oscclk", - MFC_PLL_LOCK, MFC_PLL_CON0, exynos5443_pll_rates), + MFC_PLL_LOCK, MFC_PLL_CON0, exynos5433_pll_rates), }; /* list of all parent clock list */ @@ -1382,7 +1384,7 @@ static const struct samsung_gate_clock mif_gate_clks[] __initconst = { /* ENABLE_ACLK_MIF3 */ GATE(CLK_ACLK_BUS2_400, "aclk_bus2_400", "div_aclk_bus2_400", ENABLE_ACLK_MIF3, 4, - CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), GATE(CLK_ACLK_DISP_333, "aclk_disp_333", "div_aclk_disp_333", ENABLE_ACLK_MIF3, 1, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, 0), @@ -2539,7 +2541,7 @@ PNAME(mout_sclk_decon_tv_vclk_b_disp_p) = { "mout_sclk_decon_tv_vclk_a_disp", static const struct samsung_pll_clock disp_pll_clks[] __initconst = { PLL(pll_35xx, CLK_FOUT_DISP_PLL, "fout_disp_pll", "oscclk", - DISP_PLL_LOCK, DISP_PLL_CON0, exynos5443_pll_rates), + DISP_PLL_LOCK, DISP_PLL_CON0, exynos5433_pll_rates), }; static const struct samsung_fixed_factor_clock disp_fixed_factor_clks[] __initconst = { @@ -2559,8 +2561,10 @@ static const struct samsung_fixed_rate_clock disp_fixed_clks[] __initconst = { FRATE(0, "phyclk_mipidphy1_bitclkdiv8_phy", NULL, 0, 188000000), FRATE(0, "phyclk_mipidphy1_rxclkesc0_phy", NULL, 0, 100000000), /* PHY clocks from MIPI_DPHY0 */ - FRATE(0, "phyclk_mipidphy0_bitclkdiv8_phy", NULL, 0, 188000000), - FRATE(0, "phyclk_mipidphy0_rxclkesc0_phy", NULL, 0, 100000000), + FRATE(CLK_PHYCLK_MIPIDPHY0_BITCLKDIV8_PHY, "phyclk_mipidphy0_bitclkdiv8_phy", + NULL, 0, 188000000), + FRATE(CLK_PHYCLK_MIPIDPHY0_RXCLKESC0_PHY, "phyclk_mipidphy0_rxclkesc0_phy", + NULL, 0, 100000000), /* PHY clocks from HDMI_PHY */ FRATE(CLK_PHYCLK_HDMIPHY_TMDS_CLKO_PHY, "phyclk_hdmiphy_tmds_clko_phy", NULL, 0, 300000000), @@ -3224,7 +3228,7 @@ PNAME(mout_g3d_pll_p) = { "oscclk", "fout_g3d_pll", }; static const struct samsung_pll_clock g3d_pll_clks[] __initconst = { PLL(pll_35xx, CLK_FOUT_G3D_PLL, "fout_g3d_pll", "oscclk", - G3D_PLL_LOCK, G3D_PLL_CON0, exynos5443_pll_rates), + G3D_PLL_LOCK, G3D_PLL_CON0, exynos5433_pll_rates), }; static const struct samsung_mux_clock g3d_mux_clks[] __initconst = { @@ -3514,7 +3518,7 @@ PNAME(mout_apollo_p) = { "mout_apollo_pll", static const struct samsung_pll_clock apollo_pll_clks[] __initconst = { PLL(pll_35xx, CLK_FOUT_APOLLO_PLL, "fout_apollo_pll", "oscclk", - APOLLO_PLL_LOCK, APOLLO_PLL_CON0, exynos5443_pll_rates), + APOLLO_PLL_LOCK, APOLLO_PLL_CON0, exynos5433_pll_rates), }; static const struct samsung_mux_clock apollo_mux_clks[] __initconst = { @@ -3737,7 +3741,7 @@ PNAME(mout_atlas_p) = { "mout_atlas_pll", static const struct samsung_pll_clock atlas_pll_clks[] __initconst = { PLL(pll_35xx, CLK_FOUT_ATLAS_PLL, "fout_atlas_pll", "oscclk", - ATLAS_PLL_LOCK, ATLAS_PLL_CON0, exynos5443_pll_rates), + ATLAS_PLL_LOCK, ATLAS_PLL_CON0, exynos5433_pll_rates), }; static const struct samsung_mux_clock atlas_mux_clks[] __initconst = { diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 9617825daabb..52290894857a 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -136,11 +136,39 @@ static const struct clk_ops samsung_pll3000_clk_ops = { #define PLL35XX_MDIV_MASK (0x3FF) #define PLL35XX_PDIV_MASK (0x3F) #define PLL35XX_SDIV_MASK (0x7) -#define PLL35XX_LOCK_STAT_MASK (0x1) #define PLL35XX_MDIV_SHIFT (16) #define PLL35XX_PDIV_SHIFT (8) #define PLL35XX_SDIV_SHIFT (0) #define PLL35XX_LOCK_STAT_SHIFT (29) +#define PLL35XX_ENABLE_SHIFT (31) + +static int samsung_pll35xx_enable(struct clk_hw *hw) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 tmp; + + tmp = readl_relaxed(pll->con_reg); + tmp |= BIT(PLL35XX_ENABLE_SHIFT); + writel_relaxed(tmp, pll->con_reg); + + /* wait_lock_time */ + do { + cpu_relax(); + tmp = readl_relaxed(pll->con_reg); + } while (!(tmp & BIT(PLL35XX_LOCK_STAT_SHIFT))); + + return 0; +} + +static void samsung_pll35xx_disable(struct clk_hw *hw) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 tmp; + + tmp = readl_relaxed(pll->con_reg); + tmp &= ~BIT(PLL35XX_ENABLE_SHIFT); + writel_relaxed(tmp, pll->con_reg); +} static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -210,12 +238,13 @@ static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, (rate->sdiv << PLL35XX_SDIV_SHIFT); writel_relaxed(tmp, pll->con_reg); - /* wait_lock_time */ - do { - cpu_relax(); - tmp = readl_relaxed(pll->con_reg); - } while (!(tmp & (PLL35XX_LOCK_STAT_MASK - << PLL35XX_LOCK_STAT_SHIFT))); + /* wait_lock_time if enabled */ + if (tmp & BIT(PLL35XX_ENABLE_SHIFT)) { + do { + cpu_relax(); + tmp = readl_relaxed(pll->con_reg); + } while (!(tmp & BIT(PLL35XX_LOCK_STAT_SHIFT))); + } return 0; } @@ -223,6 +252,8 @@ static const struct clk_ops samsung_pll35xx_clk_ops = { .recalc_rate = samsung_pll35xx_recalc_rate, .round_rate = samsung_pll_round_rate, .set_rate = samsung_pll35xx_set_rate, + .enable = samsung_pll35xx_enable, + .disable = samsung_pll35xx_disable, }; static const struct clk_ops samsung_pll35xx_clk_min_ops = { diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c index d7a1e772d95a..e0650c33863b 100644 --- a/drivers/clk/samsung/clk-s3c2410.c +++ b/drivers/clk/samsung/clk-s3c2410.c @@ -76,7 +76,7 @@ static struct syscore_ops s3c2410_clk_syscore_ops = { .resume = s3c2410_clk_resume, }; -static void s3c2410_clk_sleep_init(void) +static void __init s3c2410_clk_sleep_init(void) { s3c2410_save = samsung_clk_alloc_reg_dump(s3c2410_clk_regs, ARRAY_SIZE(s3c2410_clk_regs)); @@ -90,7 +90,7 @@ static void s3c2410_clk_sleep_init(void) return; } #else -static void s3c2410_clk_sleep_init(void) {} +static void __init s3c2410_clk_sleep_init(void) {} #endif PNAME(fclk_p) = { "mpll", "div_slow" }; diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c index ec873ee15d37..b8340a49921b 100644 --- a/drivers/clk/samsung/clk-s3c2412.c +++ b/drivers/clk/samsung/clk-s3c2412.c @@ -69,7 +69,7 @@ static struct syscore_ops s3c2412_clk_syscore_ops = { .resume = s3c2412_clk_resume, }; -static void s3c2412_clk_sleep_init(void) +static void __init s3c2412_clk_sleep_init(void) { s3c2412_save = samsung_clk_alloc_reg_dump(s3c2412_clk_regs, ARRAY_SIZE(s3c2412_clk_regs)); @@ -83,7 +83,7 @@ static void s3c2412_clk_sleep_init(void) return; } #else -static void s3c2412_clk_sleep_init(void) {} +static void __init s3c2412_clk_sleep_init(void) {} #endif static struct clk_div_table divxti_d[] = { diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index 5e24a17e10e6..abb935c42916 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -89,7 +89,7 @@ static struct syscore_ops s3c2443_clk_syscore_ops = { .resume = s3c2443_clk_resume, }; -static void s3c2443_clk_sleep_init(void) +static void __init s3c2443_clk_sleep_init(void) { s3c2443_save = samsung_clk_alloc_reg_dump(s3c2443_clk_regs, ARRAY_SIZE(s3c2443_clk_regs)); @@ -103,7 +103,7 @@ static void s3c2443_clk_sleep_init(void) return; } #else -static void s3c2443_clk_sleep_init(void) {} +static void __init s3c2443_clk_sleep_init(void) {} #endif PNAME(epllref_p) = { "mpllref", "mpllref", "xti", "ext" }; diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c index a48bd5f17330..7306867a0ab8 100644 --- a/drivers/clk/samsung/clk-s3c64xx.c +++ b/drivers/clk/samsung/clk-s3c64xx.c @@ -121,7 +121,7 @@ static struct syscore_ops s3c64xx_clk_syscore_ops = { .resume = s3c64xx_clk_resume, }; -static void s3c64xx_clk_sleep_init(void) +static void __init s3c64xx_clk_sleep_init(void) { s3c64xx_save_common = samsung_clk_alloc_reg_dump(s3c64xx_clk_regs, ARRAY_SIZE(s3c64xx_clk_regs)); @@ -145,7 +145,7 @@ err_warn: __func__); } #else -static void s3c64xx_clk_sleep_init(void) {} +static void __init s3c64xx_clk_sleep_init(void) {} #endif /* List of parent clocks common for all S3C64xx SoCs. */ diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 8454c6e3dd65..695bbf9ef428 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -64,6 +64,17 @@ config SUN50I_A64_CCU select SUNXI_CCU_PHASE default ARM64 && ARCH_SUNXI +config SUN5I_CCU + bool "Support for the Allwinner sun5i family CCM" + select SUNXI_CCU_DIV + select SUNXI_CCU_MULT + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN5I + config SUN6I_A31_CCU bool "Support for the Allwinner A31/A31s CCU" select SUNXI_CCU_DIV @@ -109,4 +120,25 @@ config SUN8I_H3_CCU select SUNXI_CCU_PHASE default MACH_SUN8I +config SUN8I_V3S_CCU + bool "Support for the Allwinner V3s CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NKMP + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN8I + +config SUN9I_A80_CCU + bool "Support for the Allwinner A80 CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_GATE + select SUNXI_CCU_NKMP + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN9I + endif diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 24fbc6e5deb8..6feaac0c5600 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -19,7 +19,12 @@ obj-$(CONFIG_SUNXI_CCU_MP) += ccu_mp.o # SoC support obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o +obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o +obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o +obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o +obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o +obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.c b/drivers/clk/sunxi-ng/ccu-sun5i.c new file mode 100644 index 000000000000..06edaa523479 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun5i.c @@ -0,0 +1,1022 @@ +/* + * Copyright (c) 2016 Maxime Ripard. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun5i.h" + +static struct ccu_nkmp pll_core_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV(16, 2), + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-core", + "hosc", + &ccu_nkmp_ops, + 0), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN5I_PLL_AUDIO_REG 0x008 + +static struct ccu_nm pll_audio_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 7, 0), + + /* + * The datasheet is wrong here, this doesn't have any + * offset + */ + .m = _SUNXI_CCU_DIV_OFFSET(0, 5, 0), + .common = { + .reg = 0x008, + .hw.init = CLK_HW_INIT("pll-audio-base", + "hosc", + &ccu_nm_ops, + 0), + }, +}; + +static struct ccu_mult pll_video0_clk = { + .enable = BIT(31), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127), + .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14), + 270000000, 297000000), + .common = { + .reg = 0x010, + .features = (CCU_FEATURE_FRACTIONAL | + CCU_FEATURE_ALL_PREDIV), + .prediv = 8, + .hw.init = CLK_HW_INIT("pll-video0", + "hosc", + &ccu_mult_ops, + 0), + }, +}; + +static struct ccu_nkmp pll_ve_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV(16, 2), + .common = { + .reg = 0x018, + .hw.init = CLK_HW_INIT("pll-ve", + "hosc", + &ccu_nkmp_ops, + 0), + }, +}; + +static struct ccu_nk pll_ddr_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x020, + .hw.init = CLK_HW_INIT("pll-ddr-base", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static SUNXI_CCU_M(pll_ddr_clk, "pll-ddr", "pll-ddr-base", 0x020, 0, 2, + CLK_IS_CRITICAL); + +static struct ccu_div pll_ddr_other_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(16, 2, CLK_DIVIDER_POWER_OF_TWO), + + .common = { + .reg = 0x020, + .hw.init = CLK_HW_INIT("pll-ddr-other", "pll-ddr-base", + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_nk pll_periph_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .fixed_post_div = 2, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static struct ccu_mult pll_video1_clk = { + .enable = BIT(31), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127), + .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14), + 270000000, 297000000), + .common = { + .reg = 0x030, + .features = (CCU_FEATURE_FRACTIONAL | + CCU_FEATURE_ALL_PREDIV), + .prediv = 8, + .hw.init = CLK_HW_INIT("pll-video1", + "hosc", + &ccu_mult_ops, + 0), + }, +}; + +static SUNXI_CCU_GATE(hosc_clk, "hosc", "osc24M", 0x050, BIT(0), 0); + +#define SUN5I_AHB_REG 0x054 +static const char * const cpu_parents[] = { "osc32k", "hosc", + "pll-core" , "pll-periph" }; +static const struct ccu_mux_fixed_prediv cpu_predivs[] = { + { .index = 3, .div = 3, }, +}; +static struct ccu_mux cpu_clk = { + .mux = { + .shift = 16, + .width = 2, + .fixed_predivs = cpu_predivs, + .n_predivs = ARRAY_SIZE(cpu_predivs), + }, + .common = { + .reg = 0x054, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("cpu", + cpu_parents, + &ccu_mux_ops, + CLK_IS_CRITICAL), + } +}; + +static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x054, 0, 2, 0); + +static const char * const ahb_parents[] = { "axi" , "cpu", "pll-periph" }; +static const struct ccu_mux_fixed_prediv ahb_predivs[] = { + { .index = 2, .div = 2, }, +}; +static struct ccu_div ahb_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = { + .shift = 6, + .width = 2, + .fixed_predivs = ahb_predivs, + .n_predivs = ARRAY_SIZE(ahb_predivs), + }, + + .common = { + .reg = 0x054, + .hw.init = CLK_HW_INIT_PARENTS("ahb", + ahb_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb0_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb0_clk, "apb0", "ahb", + 0x054, 8, 2, apb0_div_table, 0); + +static const char * const apb1_parents[] = { "hosc", "pll-periph", "osc32k" }; +static SUNXI_CCU_MP_WITH_MUX(apb1_clk, "apb1", apb1_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static SUNXI_CCU_GATE(axi_dram_clk, "axi-dram", "axi", + 0x05c, BIT(0), 0); + +static SUNXI_CCU_GATE(ahb_otg_clk, "ahb-otg", "ahb", + 0x060, BIT(0), 0); +static SUNXI_CCU_GATE(ahb_ehci_clk, "ahb-ehci", "ahb", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(ahb_ohci_clk, "ahb-ohci", "ahb", + 0x060, BIT(2), 0); +static SUNXI_CCU_GATE(ahb_ss_clk, "ahb-ss", "ahb", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(ahb_dma_clk, "ahb-dma", "ahb", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(ahb_bist_clk, "ahb-bist", "ahb", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(ahb_mmc0_clk, "ahb-mmc0", "ahb", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(ahb_mmc1_clk, "ahb-mmc1", "ahb", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(ahb_mmc2_clk, "ahb-mmc2", "ahb", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(ahb_nand_clk, "ahb-nand", "ahb", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(ahb_sdram_clk, "ahb-sdram", "ahb", + 0x060, BIT(14), CLK_IS_CRITICAL); +static SUNXI_CCU_GATE(ahb_emac_clk, "ahb-emac", "ahb", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(ahb_ts_clk, "ahb-ts", "ahb", + 0x060, BIT(18), 0); +static SUNXI_CCU_GATE(ahb_spi0_clk, "ahb-spi0", "ahb", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(ahb_spi1_clk, "ahb-spi1", "ahb", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(ahb_spi2_clk, "ahb-spi2", "ahb", + 0x060, BIT(22), 0); +static SUNXI_CCU_GATE(ahb_gps_clk, "ahb-gps", "ahb", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(ahb_hstimer_clk, "ahb-hstimer", "ahb", + 0x060, BIT(28), 0); + +static SUNXI_CCU_GATE(ahb_ve_clk, "ahb-ve", "ahb", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(ahb_tve_clk, "ahb-tve", "ahb", + 0x064, BIT(2), 0); +static SUNXI_CCU_GATE(ahb_lcd_clk, "ahb-lcd", "ahb", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(ahb_csi_clk, "ahb-csi", "ahb", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(ahb_hdmi_clk, "ahb-hdmi", "ahb", + 0x064, BIT(11), 0); +static SUNXI_CCU_GATE(ahb_de_be_clk, "ahb-de-be", "ahb", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(ahb_de_fe_clk, "ahb-de-fe", "ahb", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(ahb_iep_clk, "ahb-iep", "ahb", + 0x064, BIT(19), 0); +static SUNXI_CCU_GATE(ahb_gpu_clk, "ahb-gpu", "ahb", + 0x064, BIT(20), 0); + +static SUNXI_CCU_GATE(apb0_codec_clk, "apb0-codec", "apb0", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(apb0_spdif_clk, "apb0-spdif", "apb0", + 0x068, BIT(1), 0); +static SUNXI_CCU_GATE(apb0_i2s_clk, "apb0-i2s", "apb0", + 0x068, BIT(3), 0); +static SUNXI_CCU_GATE(apb0_pio_clk, "apb0-pio", "apb0", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(apb0_ir_clk, "apb0-ir", "apb0", + 0x068, BIT(6), 0); +static SUNXI_CCU_GATE(apb0_keypad_clk, "apb0-keypad", "apb0", + 0x068, BIT(10), 0); + +static SUNXI_CCU_GATE(apb1_i2c0_clk, "apb1-i2c0", "apb1", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(apb1_i2c1_clk, "apb1-i2c1", "apb1", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(apb1_i2c2_clk, "apb1-i2c2", "apb1", + 0x06c, BIT(2), 0); +static SUNXI_CCU_GATE(apb1_uart0_clk, "apb1-uart0", "apb1", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(apb1_uart1_clk, "apb1-uart1", "apb1", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(apb1_uart2_clk, "apb1-uart2", "apb1", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(apb1_uart3_clk, "apb1-uart3", "apb1", + 0x06c, BIT(19), 0); + +static const char * const mod0_default_parents[] = { "hosc", "pll-periph", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents, 0x098, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ir_clk, "ir", mod0_default_parents, 0x0b0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s_clk, "i2s", i2s_parents, + 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const spdif_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", spdif_parents, + 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const keypad_parents[] = { "hosc", "losc"}; +static const u8 keypad_table[] = { 0, 2 }; +static struct ccu_mp keypad_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 2, keypad_table), + + .common = { + .reg = 0x0c4, + .hw.init = CLK_HW_INIT_PARENTS("keypad", + keypad_parents, + &ccu_mp_ops, + 0), + }, +}; + +static SUNXI_CCU_GATE(usb_ohci_clk, "usb-ohci", "pll-periph", + 0x0cc, BIT(6), 0); +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "pll-periph", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "pll-periph", + 0x0cc, BIT(9), 0); + +static const char * const gps_parents[] = { "hosc", "pll-periph", + "pll-video1", "pll-ve" }; +static SUNXI_CCU_M_WITH_MUX_GATE(gps_clk, "gps", gps_parents, + 0x0d0, 0, 3, 24, 2, BIT(31), 0); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "pll-ddr", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "pll-ddr", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "pll-ddr", + 0x100, BIT(3), 0); +static SUNXI_CCU_GATE(dram_tve_clk, "dram-tve", "pll-ddr", + 0x100, BIT(5), 0); +static SUNXI_CCU_GATE(dram_de_fe_clk, "dram-de-fe", "pll-ddr", + 0x100, BIT(25), 0); +static SUNXI_CCU_GATE(dram_de_be_clk, "dram-de-be", "pll-ddr", + 0x100, BIT(26), 0); +static SUNXI_CCU_GATE(dram_ace_clk, "dram-ace", "pll-ddr", + 0x100, BIT(29), 0); +static SUNXI_CCU_GATE(dram_iep_clk, "dram-iep", "pll-ddr", + 0x100, BIT(31), 0); + +static const char * const de_parents[] = { "pll-video0", "pll-video1", + "pll-ddr-other" }; +static SUNXI_CCU_M_WITH_MUX_GATE(de_be_clk, "de-be", de_parents, + 0x104, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(de_fe_clk, "de-fe", de_parents, + 0x10c, 0, 4, 24, 2, BIT(31), 0); + +static const char * const tcon_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x" }; +static SUNXI_CCU_MUX_WITH_GATE(tcon_ch0_clk, "tcon-ch0-sclk", tcon_parents, + 0x118, 24, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_GATE(tcon_ch1_sclk2_clk, "tcon-ch1-sclk2", + tcon_parents, + 0x12c, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(tcon_ch1_sclk1_clk, "tcon-ch1-sclk1", "tcon-ch1-sclk2", + 0x12c, 11, 1, BIT(15), CLK_SET_RATE_PARENT); + +static const char * const csi_parents[] = { "hosc", "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x" }; +static const u8 csi_table[] = { 0, 1, 2, 5, 6 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_clk, "csi", + csi_parents, csi_table, + 0x134, 0, 5, 24, 2, BIT(31), 0); + +static SUNXI_CCU_GATE(ve_clk, "ve", "pll-ve", + 0x13c, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(avs_clk, "avs", "hosc", + 0x144, BIT(31), 0); + +static const char * const hdmi_parents[] = { "pll-video0", "pll-video0-2x" }; +static const u8 hdmi_table[] = { 0, 2 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(hdmi_clk, "hdmi", + hdmi_parents, hdmi_table, + 0x150, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static const char * const gpu_parents[] = { "pll-video0", "pll-ve", + "pll-ddr-other", "pll-video1", + "pll-video1-2x" }; +static SUNXI_CCU_M_WITH_MUX_GATE(gpu_clk, "gpu", gpu_parents, + 0x154, 0, 4, 24, 3, BIT(31), 0); + +static const char * const mbus_parents[] = { "hosc", "pll-periph", "pll-ddr" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, + 0x15c, 0, 4, 16, 2, 24, 2, BIT(31), CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(iep_clk, "iep", "de-be", + 0x160, BIT(31), 0); + +static struct ccu_common *sun5i_a10s_ccu_clks[] = { + &hosc_clk.common, + &pll_core_clk.common, + &pll_audio_base_clk.common, + &pll_video0_clk.common, + &pll_ve_clk.common, + &pll_ddr_base_clk.common, + &pll_ddr_clk.common, + &pll_ddr_other_clk.common, + &pll_periph_clk.common, + &pll_video1_clk.common, + &cpu_clk.common, + &axi_clk.common, + &ahb_clk.common, + &apb0_clk.common, + &apb1_clk.common, + &axi_dram_clk.common, + &ahb_otg_clk.common, + &ahb_ehci_clk.common, + &ahb_ohci_clk.common, + &ahb_ss_clk.common, + &ahb_dma_clk.common, + &ahb_bist_clk.common, + &ahb_mmc0_clk.common, + &ahb_mmc1_clk.common, + &ahb_mmc2_clk.common, + &ahb_nand_clk.common, + &ahb_sdram_clk.common, + &ahb_emac_clk.common, + &ahb_ts_clk.common, + &ahb_spi0_clk.common, + &ahb_spi1_clk.common, + &ahb_spi2_clk.common, + &ahb_gps_clk.common, + &ahb_hstimer_clk.common, + &ahb_ve_clk.common, + &ahb_tve_clk.common, + &ahb_lcd_clk.common, + &ahb_csi_clk.common, + &ahb_hdmi_clk.common, + &ahb_de_be_clk.common, + &ahb_de_fe_clk.common, + &ahb_iep_clk.common, + &ahb_gpu_clk.common, + &apb0_codec_clk.common, + &apb0_spdif_clk.common, + &apb0_i2s_clk.common, + &apb0_pio_clk.common, + &apb0_ir_clk.common, + &apb0_keypad_clk.common, + &apb1_i2c0_clk.common, + &apb1_i2c1_clk.common, + &apb1_i2c2_clk.common, + &apb1_uart0_clk.common, + &apb1_uart1_clk.common, + &apb1_uart2_clk.common, + &apb1_uart3_clk.common, + &nand_clk.common, + &mmc0_clk.common, + &mmc1_clk.common, + &mmc2_clk.common, + &ts_clk.common, + &ss_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &ir_clk.common, + &i2s_clk.common, + &spdif_clk.common, + &keypad_clk.common, + &usb_ohci_clk.common, + &usb_phy0_clk.common, + &usb_phy1_clk.common, + &gps_clk.common, + &dram_ve_clk.common, + &dram_csi_clk.common, + &dram_ts_clk.common, + &dram_tve_clk.common, + &dram_de_fe_clk.common, + &dram_de_be_clk.common, + &dram_ace_clk.common, + &dram_iep_clk.common, + &de_be_clk.common, + &de_fe_clk.common, + &tcon_ch0_clk.common, + &tcon_ch1_sclk2_clk.common, + &tcon_ch1_sclk1_clk.common, + &csi_clk.common, + &ve_clk.common, + &codec_clk.common, + &avs_clk.common, + &hdmi_clk.common, + &gpu_clk.common, + &mbus_clk.common, + &iep_clk.common, +}; + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x", + "pll-video0", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x", + "pll-video1", 1, 2, CLK_SET_RATE_PARENT); + +static struct clk_hw_onecell_data sun5i_a10s_hw_clks = { + .hws = { + [CLK_HOSC] = &hosc_clk.common.hw, + [CLK_PLL_CORE] = &pll_core_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB] = &ahb_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_DRAM_AXI] = &axi_dram_clk.common.hw, + [CLK_AHB_OTG] = &ahb_otg_clk.common.hw, + [CLK_AHB_EHCI] = &ahb_ehci_clk.common.hw, + [CLK_AHB_OHCI] = &ahb_ohci_clk.common.hw, + [CLK_AHB_SS] = &ahb_ss_clk.common.hw, + [CLK_AHB_DMA] = &ahb_dma_clk.common.hw, + [CLK_AHB_BIST] = &ahb_bist_clk.common.hw, + [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw, + [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw, + [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw, + [CLK_AHB_NAND] = &ahb_nand_clk.common.hw, + [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw, + [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw, + [CLK_AHB_TS] = &ahb_ts_clk.common.hw, + [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw, + [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw, + [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw, + [CLK_AHB_GPS] = &ahb_gps_clk.common.hw, + [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw, + [CLK_AHB_VE] = &ahb_ve_clk.common.hw, + [CLK_AHB_TVE] = &ahb_tve_clk.common.hw, + [CLK_AHB_LCD] = &ahb_lcd_clk.common.hw, + [CLK_AHB_CSI] = &ahb_csi_clk.common.hw, + [CLK_AHB_HDMI] = &ahb_hdmi_clk.common.hw, + [CLK_AHB_DE_BE] = &ahb_de_be_clk.common.hw, + [CLK_AHB_DE_FE] = &ahb_de_fe_clk.common.hw, + [CLK_AHB_IEP] = &ahb_iep_clk.common.hw, + [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw, + [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw, + [CLK_APB0_I2S] = &apb0_i2s_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR] = &apb0_ir_clk.common.hw, + [CLK_APB0_KEYPAD] = &apb0_keypad_clk.common.hw, + [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw, + [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw, + [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw, + [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw, + [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw, + [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw, + [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_IR] = &ir_clk.common.hw, + [CLK_I2S] = &i2s_clk.common.hw, + [CLK_KEYPAD] = &keypad_clk.common.hw, + [CLK_USB_OHCI] = &usb_ohci_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_GPS] = &gps_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVE] = &dram_tve_clk.common.hw, + [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw, + [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw, + [CLK_DRAM_ACE] = &dram_ace_clk.common.hw, + [CLK_DRAM_IEP] = &dram_iep_clk.common.hw, + [CLK_DE_BE] = &de_be_clk.common.hw, + [CLK_DE_FE] = &de_fe_clk.common.hw, + [CLK_TCON_CH0] = &tcon_ch0_clk.common.hw, + [CLK_TCON_CH1_SCLK] = &tcon_ch1_sclk2_clk.common.hw, + [CLK_TCON_CH1] = &tcon_ch1_sclk1_clk.common.hw, + [CLK_CSI] = &csi_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_IEP] = &iep_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun5i_a10s_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + + [RST_GPS] = { 0x0d0, BIT(30) }, + + [RST_DE_BE] = { 0x104, BIT(30) }, + + [RST_DE_FE] = { 0x10c, BIT(30) }, + + [RST_TVE] = { 0x118, BIT(29) }, + [RST_LCD] = { 0x118, BIT(30) }, + + [RST_CSI] = { 0x134, BIT(30) }, + + [RST_VE] = { 0x13c, BIT(0) }, + + [RST_GPU] = { 0x154, BIT(30) }, + + [RST_IEP] = { 0x160, BIT(30) }, +}; + +static const struct sunxi_ccu_desc sun5i_a10s_ccu_desc = { + .ccu_clks = sun5i_a10s_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun5i_a10s_ccu_clks), + + .hw_clks = &sun5i_a10s_hw_clks, + + .resets = sun5i_a10s_ccu_resets, + .num_resets = ARRAY_SIZE(sun5i_a10s_ccu_resets), +}; + +/* + * The A13 is the A10s minus the TS, GPS, HDMI, I2S and the keypad + */ +static struct clk_hw_onecell_data sun5i_a13_hw_clks = { + .hws = { + [CLK_HOSC] = &hosc_clk.common.hw, + [CLK_PLL_CORE] = &pll_core_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB] = &ahb_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_DRAM_AXI] = &axi_dram_clk.common.hw, + [CLK_AHB_OTG] = &ahb_otg_clk.common.hw, + [CLK_AHB_EHCI] = &ahb_ehci_clk.common.hw, + [CLK_AHB_OHCI] = &ahb_ohci_clk.common.hw, + [CLK_AHB_SS] = &ahb_ss_clk.common.hw, + [CLK_AHB_DMA] = &ahb_dma_clk.common.hw, + [CLK_AHB_BIST] = &ahb_bist_clk.common.hw, + [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw, + [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw, + [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw, + [CLK_AHB_NAND] = &ahb_nand_clk.common.hw, + [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw, + [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw, + [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw, + [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw, + [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw, + [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw, + [CLK_AHB_VE] = &ahb_ve_clk.common.hw, + [CLK_AHB_TVE] = &ahb_tve_clk.common.hw, + [CLK_AHB_LCD] = &ahb_lcd_clk.common.hw, + [CLK_AHB_CSI] = &ahb_csi_clk.common.hw, + [CLK_AHB_DE_BE] = &ahb_de_be_clk.common.hw, + [CLK_AHB_DE_FE] = &ahb_de_fe_clk.common.hw, + [CLK_AHB_IEP] = &ahb_iep_clk.common.hw, + [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw, + [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR] = &apb0_ir_clk.common.hw, + [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw, + [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw, + [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw, + [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw, + [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw, + [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw, + [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_IR] = &ir_clk.common.hw, + [CLK_USB_OHCI] = &usb_ohci_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_DRAM_TVE] = &dram_tve_clk.common.hw, + [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw, + [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw, + [CLK_DRAM_ACE] = &dram_ace_clk.common.hw, + [CLK_DRAM_IEP] = &dram_iep_clk.common.hw, + [CLK_DE_BE] = &de_be_clk.common.hw, + [CLK_DE_FE] = &de_fe_clk.common.hw, + [CLK_TCON_CH0] = &tcon_ch0_clk.common.hw, + [CLK_TCON_CH1_SCLK] = &tcon_ch1_sclk2_clk.common.hw, + [CLK_TCON_CH1] = &tcon_ch1_sclk1_clk.common.hw, + [CLK_CSI] = &csi_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_IEP] = &iep_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static const struct sunxi_ccu_desc sun5i_a13_ccu_desc = { + .ccu_clks = sun5i_a10s_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun5i_a10s_ccu_clks), + + .hw_clks = &sun5i_a13_hw_clks, + + .resets = sun5i_a10s_ccu_resets, + .num_resets = ARRAY_SIZE(sun5i_a10s_ccu_resets), +}; + +/* + * The GR8 is the A10s CCU minus the HDMI and keypad, plus SPDIF + */ +static struct clk_hw_onecell_data sun5i_gr8_hw_clks = { + .hws = { + [CLK_HOSC] = &hosc_clk.common.hw, + [CLK_PLL_CORE] = &pll_core_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB] = &ahb_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_DRAM_AXI] = &axi_dram_clk.common.hw, + [CLK_AHB_OTG] = &ahb_otg_clk.common.hw, + [CLK_AHB_EHCI] = &ahb_ehci_clk.common.hw, + [CLK_AHB_OHCI] = &ahb_ohci_clk.common.hw, + [CLK_AHB_SS] = &ahb_ss_clk.common.hw, + [CLK_AHB_DMA] = &ahb_dma_clk.common.hw, + [CLK_AHB_BIST] = &ahb_bist_clk.common.hw, + [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw, + [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw, + [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw, + [CLK_AHB_NAND] = &ahb_nand_clk.common.hw, + [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw, + [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw, + [CLK_AHB_TS] = &ahb_ts_clk.common.hw, + [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw, + [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw, + [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw, + [CLK_AHB_GPS] = &ahb_gps_clk.common.hw, + [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw, + [CLK_AHB_VE] = &ahb_ve_clk.common.hw, + [CLK_AHB_TVE] = &ahb_tve_clk.common.hw, + [CLK_AHB_LCD] = &ahb_lcd_clk.common.hw, + [CLK_AHB_CSI] = &ahb_csi_clk.common.hw, + [CLK_AHB_DE_BE] = &ahb_de_be_clk.common.hw, + [CLK_AHB_DE_FE] = &ahb_de_fe_clk.common.hw, + [CLK_AHB_IEP] = &ahb_iep_clk.common.hw, + [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw, + [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw, + [CLK_APB0_SPDIF] = &apb0_spdif_clk.common.hw, + [CLK_APB0_I2S] = &apb0_i2s_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR] = &apb0_ir_clk.common.hw, + [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw, + [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw, + [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw, + [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw, + [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw, + [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw, + [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_IR] = &ir_clk.common.hw, + [CLK_I2S] = &i2s_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_USB_OHCI] = &usb_ohci_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_GPS] = &gps_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVE] = &dram_tve_clk.common.hw, + [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw, + [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw, + [CLK_DRAM_ACE] = &dram_ace_clk.common.hw, + [CLK_DRAM_IEP] = &dram_iep_clk.common.hw, + [CLK_DE_BE] = &de_be_clk.common.hw, + [CLK_DE_FE] = &de_fe_clk.common.hw, + [CLK_TCON_CH0] = &tcon_ch0_clk.common.hw, + [CLK_TCON_CH1_SCLK] = &tcon_ch1_sclk2_clk.common.hw, + [CLK_TCON_CH1] = &tcon_ch1_sclk1_clk.common.hw, + [CLK_CSI] = &csi_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_IEP] = &iep_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static const struct sunxi_ccu_desc sun5i_gr8_ccu_desc = { + .ccu_clks = sun5i_a10s_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun5i_a10s_ccu_clks), + + .hw_clks = &sun5i_gr8_hw_clks, + + .resets = sun5i_a10s_ccu_resets, + .num_resets = ARRAY_SIZE(sun5i_a10s_ccu_resets), +}; + +static void __init sun5i_ccu_init(struct device_node *node, + const struct sunxi_ccu_desc *desc) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN5I_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN5I_PLL_AUDIO_REG); + + /* + * Use the peripheral PLL as the AHB parent, instead of CPU / + * AXI which have rate changes due to cpufreq. + * + * This is especially a big deal for the HS timer whose parent + * clock is AHB. + */ + val = readl(reg + SUN5I_AHB_REG); + val &= ~GENMASK(7, 6); + writel(val | (2 << 6), reg + SUN5I_AHB_REG); + + sunxi_ccu_probe(node, reg, desc); +} + +static void __init sun5i_a10s_ccu_setup(struct device_node *node) +{ + sun5i_ccu_init(node, &sun5i_a10s_ccu_desc); +} +CLK_OF_DECLARE(sun5i_a10s_ccu, "allwinner,sun5i-a10s-ccu", + sun5i_a10s_ccu_setup); + +static void __init sun5i_a13_ccu_setup(struct device_node *node) +{ + sun5i_ccu_init(node, &sun5i_a13_ccu_desc); +} +CLK_OF_DECLARE(sun5i_a13_ccu, "allwinner,sun5i-a13-ccu", + sun5i_a13_ccu_setup); + +static void __init sun5i_gr8_ccu_setup(struct device_node *node) +{ + sun5i_ccu_init(node, &sun5i_gr8_ccu_desc); +} +CLK_OF_DECLARE(sun5i_gr8_ccu, "nextthing,gr8-ccu", + sun5i_gr8_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.h b/drivers/clk/sunxi-ng/ccu-sun5i.h new file mode 100644 index 000000000000..8144487eb7ca --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun5i.h @@ -0,0 +1,67 @@ +/* + * Copyright 2016 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN5I_H_ +#define _CCU_SUN5I_H_ + +#include <dt-bindings/clock/sun5i-ccu.h> +#include <dt-bindings/reset/sun5i-ccu.h> + +/* The HOSC is exported */ +#define CLK_PLL_CORE 2 +#define CLK_PLL_AUDIO_BASE 3 +#define CLK_PLL_AUDIO 4 +#define CLK_PLL_AUDIO_2X 5 +#define CLK_PLL_AUDIO_4X 6 +#define CLK_PLL_AUDIO_8X 7 +#define CLK_PLL_VIDEO0 8 +#define CLK_PLL_VIDEO0_2X 9 +#define CLK_PLL_VE 10 +#define CLK_PLL_DDR_BASE 11 +#define CLK_PLL_DDR 12 +#define CLK_PLL_DDR_OTHER 13 +#define CLK_PLL_PERIPH 14 +#define CLK_PLL_VIDEO1 15 +#define CLK_PLL_VIDEO1_2X 16 + +/* The CPU clock is exported */ + +#define CLK_AXI 18 +#define CLK_AHB 19 +#define CLK_APB0 20 +#define CLK_APB1 21 +#define CLK_DRAM_AXI 22 + +/* AHB gates are exported */ +/* APB0 gates are exported */ +/* APB1 gates are exported */ +/* Modules clocks are exported */ +/* USB clocks are exported */ +/* GPS clock is exported */ +/* DRAM gates are exported */ +/* More display modules clocks are exported */ + +#define CLK_TCON_CH1_SCLK 91 + +/* The rest of the module clocks are exported */ + +#define CLK_MBUS 99 + +/* And finally the IEP clock */ + +#define CLK_NUMBER (CLK_IEP + 1) + +#endif /* _CCU_SUN5I_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c index fc75a335a7ce..4c9a920ff4ab 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c @@ -468,8 +468,8 @@ static SUNXI_CCU_MUX_WITH_GATE(daudio0_clk, "daudio0", daudio_parents, static SUNXI_CCU_MUX_WITH_GATE(daudio1_clk, "daudio1", daudio_parents, 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT); -static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio", - 0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", daudio_parents, + 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", 0x0cc, BIT(8), 0); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c index 9bd1f78a0547..a7b3c08ed0e2 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c @@ -170,7 +170,7 @@ static SUNXI_CCU_N_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1", static const char * const cpux_parents[] = { "osc32k", "osc24M", "pll-cpux" , "pll-cpux" }; static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents, - 0x050, 16, 2, CLK_IS_CRITICAL); + 0x050, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT); static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); @@ -440,7 +440,7 @@ static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", - 0x140, BIT(31), 0); + 0x140, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(ac_dig_4x_clk, "ac-dig-4x", "pll-audio-4x", 0x140, BIT(30), 0); static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", @@ -468,7 +468,7 @@ static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(drc_clk, "drc", 0x180, 0, 4, 24, 3, BIT(31), 0); static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", - 0x1a0, 0, 3, BIT(31), 0); + 0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT); static const char * const ats_parents[] = { "osc24M", "pll-periph" }; static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", ats_parents, @@ -752,6 +752,13 @@ static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = { .num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets), }; +static struct ccu_mux_nb sun8i_a33_cpu_nb = { + .common = &cpux_clk.common, + .cm = &cpux_clk.mux, + .delay_us = 1, /* > 8 clock cycles at 24 MHz */ + .bypass_index = 1, /* index of 24 MHz oscillator */ +}; + static void __init sun8i_a33_ccu_setup(struct device_node *node) { void __iomem *reg; @@ -775,6 +782,9 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node) writel(val, reg + SUN8I_A33_PLL_MIPI_REG); sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc); + + ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, + &sun8i_a33_cpu_nb); } CLK_OF_DECLARE(sun8i_a33_ccu, "allwinner,sun8i-a33-ccu", sun8i_a33_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index 21c427d86f28..a26c8a19fe93 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -803,6 +803,13 @@ static const struct sunxi_ccu_desc sun8i_h3_ccu_desc = { .num_resets = ARRAY_SIZE(sun8i_h3_ccu_resets), }; +static struct ccu_mux_nb sun8i_h3_cpu_nb = { + .common = &cpux_clk.common, + .cm = &cpux_clk.mux, + .delay_us = 1, /* > 8 clock cycles at 24 MHz */ + .bypass_index = 1, /* index of 24 MHz oscillator */ +}; + static void __init sun8i_h3_ccu_setup(struct device_node *node) { void __iomem *reg; @@ -821,6 +828,9 @@ static void __init sun8i_h3_ccu_setup(struct device_node *node) writel(val | (3 << 16), reg + SUN8I_H3_PLL_AUDIO_REG); sunxi_ccu_probe(node, reg, &sun8i_h3_ccu_desc); + + ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, + &sun8i_h3_cpu_nb); } CLK_OF_DECLARE(sun8i_h3_ccu, "allwinner,sun8i-h3-ccu", sun8i_h3_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c new file mode 100644 index 000000000000..e58706b40ae9 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c @@ -0,0 +1,591 @@ +/* + * Copyright (c) 2016 Icenowy Zheng <icenowy@aosc.xyz> + * + * Based on ccu-sun8i-h3.c, which is: + * Copyright (c) 2016 Maxime Ripard. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun8i-v3s.h" + +static SUNXI_CCU_NKMP_WITH_GATE_LOCK(pll_cpu_clk, "pll-cpu", + "osc24M", 0x000, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + 16, 2, /* P */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN8I_V3S_PLL_AUDIO_REG 0x008 + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video", + "osc24M", 0x0010, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", + "osc24M", 0x0018, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr", + "osc24M", 0x020, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph0_clk, "pll-periph0", + "osc24M", 0x028, + 8, 5, /* N */ + 4, 2, /* K */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 2, /* post-div */ + 0); + +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_isp_clk, "pll-isp", + "osc24M", 0x002c, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 0); + +static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph1_clk, "pll-periph1", + "osc24M", 0x044, + 8, 5, /* N */ + 4, 2, /* K */ + BIT(31), /* gate */ + BIT(28), /* lock */ + 2, /* post-div */ + 0); + +static const char * const cpu_parents[] = { "osc32k", "osc24M", + "pll-cpu", "pll-cpu" }; +static SUNXI_CCU_MUX(cpu_clk, "cpu", cpu_parents, + 0x050, 16, 2, CLK_IS_CRITICAL); + +static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x050, 0, 2, 0); + +static const char * const ahb1_parents[] = { "osc32k", "osc24M", + "axi", "pll-periph0" }; +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 12, + .width = 2, + + .variable_prediv = { + .index = 3, + .shift = 6, + .width = 2, + }, + }, + + .common = { + .reg = 0x054, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb1_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", + 0x054, 8, 2, apb1_div_table, 0); + +static const char * const apb2_parents[] = { "osc32k", "osc24M", + "pll-periph0", "pll-periph0" }; +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static const char * const ahb2_parents[] = { "ahb1", "pll-periph0" }; +static const struct ccu_mux_fixed_prediv ahb2_fixed_predivs[] = { + { .index = 1, .div = 2 }, +}; +static struct ccu_mux ahb2_clk = { + .mux = { + .shift = 0, + .width = 1, + .fixed_predivs = ahb2_fixed_predivs, + .n_predivs = ARRAY_SIZE(ahb2_fixed_predivs), + }, + + .common = { + .reg = 0x05c, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb2", + ahb2_parents, + &ccu_mux_ops, + 0), + }, +}; + +static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "ahb1", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1", + 0x060, BIT(14), 0); +static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb2", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", + 0x060, BIT(19), 0); +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", + 0x060, BIT(24), 0); +static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb1", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb1", + 0x060, BIT(29), 0); + +static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(bus_tcon0_clk, "bus-tcon0", "ahb1", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb1", + 0x064, BIT(12), 0); + +static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1", + 0x068, BIT(5), 0); + +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", + 0x06c, BIT(18), 0); + +static SUNXI_CCU_GATE(bus_ephy_clk, "bus-ephy", "ahb1", + 0x070, BIT(0), 0); +static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "ahb1", + 0x070, BIT(7), 0); + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0", + "pll-periph1" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0", + 0x088, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0", + 0x088, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1", + 0x08c, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1", + 0x08c, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2", + 0x090, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2", + 0x090, 8, 3, 0); + +static const char * const ce_parents[] = { "osc24M", "pll-periph0", }; + +static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M", + 0x0cc, BIT(16), 0); + +static const char * const dram_parents[] = { "pll-ddr", "pll-periph0-2x" }; +static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents, + 0x0f4, 0, 4, 20, 2, CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "dram", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_ehci_clk, "dram-ehci", "dram", + 0x100, BIT(17), 0); +static SUNXI_CCU_GATE(dram_ohci_clk, "dram-ohci", "dram", + 0x100, BIT(18), 0); + +static const char * const de_parents[] = { "pll-video", "pll-periph0" }; +static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, + 0x104, 0, 4, 24, 2, BIT(31), 0); + +static const char * const tcon_parents[] = { "pll-video" }; +static SUNXI_CCU_M_WITH_MUX_GATE(tcon_clk, "tcon", tcon_parents, + 0x118, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", + 0x130, BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "osc24M", "pll-video", + "pll-periph0", "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi0_mclk_clk, "csi0-mclk", csi_mclk_parents, + 0x130, 0, 5, 8, 3, BIT(15), 0); + +static const char * const csi1_sclk_parents[] = { "pll-video", "pll-isp" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi1_sclk_clk, "csi-sclk", csi1_sclk_parents, + 0x134, 16, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(csi1_mclk_clk, "csi-mclk", csi_mclk_parents, + 0x134, 0, 5, 8, 3, BIT(15), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", + 0x13c, 16, 3, BIT(31), 0); + +static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", + 0x144, BIT(31), 0); + +static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x", + "pll-ddr" }; +static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, + 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL); + +static const char * const mipi_csi_parents[] = { "pll-video", "pll-periph0", + "pll-isp" }; +static SUNXI_CCU_M_WITH_MUX_GATE(mipi_csi_clk, "mipi-csi", mipi_csi_parents, + 0x16c, 0, 3, 24, 2, BIT(31), 0); + +static struct ccu_common *sun8i_v3s_ccu_clks[] = { + &pll_cpu_clk.common, + &pll_audio_base_clk.common, + &pll_video_clk.common, + &pll_ve_clk.common, + &pll_ddr_clk.common, + &pll_periph0_clk.common, + &pll_isp_clk.common, + &pll_periph1_clk.common, + &cpu_clk.common, + &axi_clk.common, + &ahb1_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &ahb2_clk.common, + &bus_ce_clk.common, + &bus_dma_clk.common, + &bus_mmc0_clk.common, + &bus_mmc1_clk.common, + &bus_mmc2_clk.common, + &bus_dram_clk.common, + &bus_emac_clk.common, + &bus_hstimer_clk.common, + &bus_spi0_clk.common, + &bus_otg_clk.common, + &bus_ehci0_clk.common, + &bus_ohci0_clk.common, + &bus_ve_clk.common, + &bus_tcon0_clk.common, + &bus_csi_clk.common, + &bus_de_clk.common, + &bus_codec_clk.common, + &bus_pio_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_ephy_clk.common, + &bus_dbg_clk.common, + &mmc0_clk.common, + &mmc0_sample_clk.common, + &mmc0_output_clk.common, + &mmc1_clk.common, + &mmc1_sample_clk.common, + &mmc1_output_clk.common, + &mmc2_clk.common, + &mmc2_sample_clk.common, + &mmc2_output_clk.common, + &ce_clk.common, + &spi0_clk.common, + &usb_phy0_clk.common, + &usb_ohci0_clk.common, + &dram_clk.common, + &dram_ve_clk.common, + &dram_csi_clk.common, + &dram_ohci_clk.common, + &dram_ehci_clk.common, + &de_clk.common, + &tcon_clk.common, + &csi_misc_clk.common, + &csi0_mclk_clk.common, + &csi1_sclk_clk.common, + &csi1_mclk_clk.common, + &ve_clk.common, + &ac_dig_clk.common, + &avs_clk.common, + &mbus_clk.common, + &mipi_csi_clk.common, +}; + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x", + "pll-periph0", 1, 2, 0); + +static struct clk_hw_onecell_data sun8i_v3s_hw_clks = { + .hws = { + [CLK_PLL_CPU] = &pll_cpu_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO] = &pll_video_clk.common.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw, + [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw, + [CLK_PLL_ISP] = &pll_isp_clk.common.hw, + [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_AHB2] = &ahb2_clk.common.hw, + [CLK_BUS_CE] = &bus_ce_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, + [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, + [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, + [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, + [CLK_BUS_EMAC] = &bus_emac_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw, + [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_TCON0] = &bus_tcon0_clk.common.hw, + [CLK_BUS_CSI] = &bus_csi_clk.common.hw, + [CLK_BUS_DE] = &bus_de_clk.common.hw, + [CLK_BUS_CODEC] = &bus_codec_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_clk.common.hw, + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_EPHY] = &bus_ephy_clk.common.hw, + [CLK_BUS_DBG] = &bus_dbg_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_CE] = &ce_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_DRAM] = &dram_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI] = &dram_csi_clk.common.hw, + [CLK_DRAM_EHCI] = &dram_ehci_clk.common.hw, + [CLK_DRAM_OHCI] = &dram_ohci_clk.common.hw, + [CLK_DE] = &de_clk.common.hw, + [CLK_TCON0] = &tcon_clk.common.hw, + [CLK_CSI_MISC] = &csi_misc_clk.common.hw, + [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw, + [CLK_CSI1_SCLK] = &csi1_sclk_clk.common.hw, + [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_AC_DIG] = &ac_dig_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_MIPI_CSI] = &mipi_csi_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun8i_v3s_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + + [RST_MBUS] = { 0x0fc, BIT(31) }, + + [RST_BUS_CE] = { 0x2c0, BIT(5) }, + [RST_BUS_DMA] = { 0x2c0, BIT(6) }, + [RST_BUS_MMC0] = { 0x2c0, BIT(8) }, + [RST_BUS_MMC1] = { 0x2c0, BIT(9) }, + [RST_BUS_MMC2] = { 0x2c0, BIT(10) }, + [RST_BUS_DRAM] = { 0x2c0, BIT(14) }, + [RST_BUS_EMAC] = { 0x2c0, BIT(17) }, + [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, + [RST_BUS_OTG] = { 0x2c0, BIT(23) }, + [RST_BUS_EHCI0] = { 0x2c0, BIT(26) }, + [RST_BUS_OHCI0] = { 0x2c0, BIT(29) }, + + [RST_BUS_VE] = { 0x2c4, BIT(0) }, + [RST_BUS_TCON0] = { 0x2c4, BIT(3) }, + [RST_BUS_CSI] = { 0x2c4, BIT(8) }, + [RST_BUS_DE] = { 0x2c4, BIT(12) }, + [RST_BUS_DBG] = { 0x2c4, BIT(31) }, + + [RST_BUS_EPHY] = { 0x2c8, BIT(2) }, + + [RST_BUS_CODEC] = { 0x2d0, BIT(0) }, + + [RST_BUS_I2C0] = { 0x2d8, BIT(0) }, + [RST_BUS_I2C1] = { 0x2d8, BIT(1) }, + [RST_BUS_UART0] = { 0x2d8, BIT(16) }, + [RST_BUS_UART1] = { 0x2d8, BIT(17) }, + [RST_BUS_UART2] = { 0x2d8, BIT(18) }, +}; + +static const struct sunxi_ccu_desc sun8i_v3s_ccu_desc = { + .ccu_clks = sun8i_v3s_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_ccu_clks), + + .hw_clks = &sun8i_v3s_hw_clks, + + .resets = sun8i_v3s_ccu_resets, + .num_resets = ARRAY_SIZE(sun8i_v3s_ccu_resets), +}; + +static void __init sun8i_v3s_ccu_setup(struct device_node *node) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN8I_V3S_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN8I_V3S_PLL_AUDIO_REG); + + sunxi_ccu_probe(node, reg, &sun8i_v3s_ccu_desc); +} +CLK_OF_DECLARE(sun8i_v3s_ccu, "allwinner,sun8i-v3s-ccu", + sun8i_v3s_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h new file mode 100644 index 000000000000..4a4d36fdad96 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Icenowy Zheng <icenowy@aosc.xyz> + * + * Based on ccu-sun8i-h3.h, which is: + * Copyright (c) 2016 Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN8I_H3_H_ +#define _CCU_SUN8I_H3_H_ + +#include <dt-bindings/clock/sun8i-v3s-ccu.h> +#include <dt-bindings/reset/sun8i-v3s-ccu.h> + +#define CLK_PLL_CPU 0 +#define CLK_PLL_AUDIO_BASE 1 +#define CLK_PLL_AUDIO 2 +#define CLK_PLL_AUDIO_2X 3 +#define CLK_PLL_AUDIO_4X 4 +#define CLK_PLL_AUDIO_8X 5 +#define CLK_PLL_VIDEO 6 +#define CLK_PLL_VE 7 +#define CLK_PLL_DDR 8 +#define CLK_PLL_PERIPH0 9 +#define CLK_PLL_PERIPH0_2X 10 +#define CLK_PLL_ISP 11 +#define CLK_PLL_PERIPH1 12 +/* Reserve one number for not implemented and not used PLL_DDR1 */ + +/* The CPU clock is exported */ + +#define CLK_AXI 15 +#define CLK_AHB1 16 +#define CLK_APB1 17 +#define CLK_APB2 18 +#define CLK_AHB2 19 + +/* All the bus gates are exported */ + +/* The first bunch of module clocks are exported */ + +#define CLK_DRAM 58 + +/* All the DRAM gates are exported */ + +/* Some more module clocks are exported */ + +#define CLK_MBUS 72 + +/* And the GPU module clock is exported */ + +#define CLK_NUMBER (CLK_MIPI_CSI + 1) + +#endif /* _CCU_SUN8I_H3_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c new file mode 100644 index 000000000000..6d116581c86d --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#include "ccu_common.h" +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_reset.h" + +#include "ccu-sun9i-a80-de.h" + +static SUNXI_CCU_GATE(fe0_clk, "fe0", "fe0-div", + 0x00, BIT(0), 0); +static SUNXI_CCU_GATE(fe1_clk, "fe1", "fe1-div", + 0x00, BIT(1), 0); +static SUNXI_CCU_GATE(fe2_clk, "fe2", "fe2-div", + 0x00, BIT(2), 0); +static SUNXI_CCU_GATE(iep_deu0_clk, "iep-deu0", "de", + 0x00, BIT(4), 0); +static SUNXI_CCU_GATE(iep_deu1_clk, "iep-deu1", "de", + 0x00, BIT(5), 0); +static SUNXI_CCU_GATE(be0_clk, "be0", "be0-div", + 0x00, BIT(8), 0); +static SUNXI_CCU_GATE(be1_clk, "be1", "be1-div", + 0x00, BIT(9), 0); +static SUNXI_CCU_GATE(be2_clk, "be2", "be2-div", + 0x00, BIT(10), 0); +static SUNXI_CCU_GATE(iep_drc0_clk, "iep-drc0", "de", + 0x00, BIT(12), 0); +static SUNXI_CCU_GATE(iep_drc1_clk, "iep-drc1", "de", + 0x00, BIT(13), 0); +static SUNXI_CCU_GATE(merge_clk, "merge", "de", + 0x00, BIT(20), 0); + +static SUNXI_CCU_GATE(dram_fe0_clk, "dram-fe0", "sdram", + 0x04, BIT(0), 0); +static SUNXI_CCU_GATE(dram_fe1_clk, "dram-fe1", "sdram", + 0x04, BIT(1), 0); +static SUNXI_CCU_GATE(dram_fe2_clk, "dram-fe2", "sdram", + 0x04, BIT(2), 0); +static SUNXI_CCU_GATE(dram_deu0_clk, "dram-deu0", "sdram", + 0x04, BIT(4), 0); +static SUNXI_CCU_GATE(dram_deu1_clk, "dram-deu1", "sdram", + 0x04, BIT(5), 0); +static SUNXI_CCU_GATE(dram_be0_clk, "dram-be0", "sdram", + 0x04, BIT(8), 0); +static SUNXI_CCU_GATE(dram_be1_clk, "dram-be1", "sdram", + 0x04, BIT(9), 0); +static SUNXI_CCU_GATE(dram_be2_clk, "dram-be2", "sdram", + 0x04, BIT(10), 0); +static SUNXI_CCU_GATE(dram_drc0_clk, "dram-drc0", "sdram", + 0x04, BIT(12), 0); +static SUNXI_CCU_GATE(dram_drc1_clk, "dram-drc1", "sdram", + 0x04, BIT(13), 0); + +static SUNXI_CCU_GATE(bus_fe0_clk, "bus-fe0", "bus-de", + 0x08, BIT(0), 0); +static SUNXI_CCU_GATE(bus_fe1_clk, "bus-fe1", "bus-de", + 0x08, BIT(1), 0); +static SUNXI_CCU_GATE(bus_fe2_clk, "bus-fe2", "bus-de", + 0x08, BIT(2), 0); +static SUNXI_CCU_GATE(bus_deu0_clk, "bus-deu0", "bus-de", + 0x08, BIT(4), 0); +static SUNXI_CCU_GATE(bus_deu1_clk, "bus-deu1", "bus-de", + 0x08, BIT(5), 0); +static SUNXI_CCU_GATE(bus_be0_clk, "bus-be0", "bus-de", + 0x08, BIT(8), 0); +static SUNXI_CCU_GATE(bus_be1_clk, "bus-be1", "bus-de", + 0x08, BIT(9), 0); +static SUNXI_CCU_GATE(bus_be2_clk, "bus-be2", "bus-de", + 0x08, BIT(10), 0); +static SUNXI_CCU_GATE(bus_drc0_clk, "bus-drc0", "bus-de", + 0x08, BIT(12), 0); +static SUNXI_CCU_GATE(bus_drc1_clk, "bus-drc1", "bus-de", + 0x08, BIT(13), 0); + +static SUNXI_CCU_M(fe0_div_clk, "fe0-div", "de", 0x20, 0, 4, 0); +static SUNXI_CCU_M(fe1_div_clk, "fe1-div", "de", 0x20, 4, 4, 0); +static SUNXI_CCU_M(fe2_div_clk, "fe2-div", "de", 0x20, 8, 4, 0); +static SUNXI_CCU_M(be0_div_clk, "be0-div", "de", 0x20, 16, 4, 0); +static SUNXI_CCU_M(be1_div_clk, "be1-div", "de", 0x20, 20, 4, 0); +static SUNXI_CCU_M(be2_div_clk, "be2-div", "de", 0x20, 24, 4, 0); + +static struct ccu_common *sun9i_a80_de_clks[] = { + &fe0_clk.common, + &fe1_clk.common, + &fe2_clk.common, + &iep_deu0_clk.common, + &iep_deu1_clk.common, + &be0_clk.common, + &be1_clk.common, + &be2_clk.common, + &iep_drc0_clk.common, + &iep_drc1_clk.common, + &merge_clk.common, + + &dram_fe0_clk.common, + &dram_fe1_clk.common, + &dram_fe2_clk.common, + &dram_deu0_clk.common, + &dram_deu1_clk.common, + &dram_be0_clk.common, + &dram_be1_clk.common, + &dram_be2_clk.common, + &dram_drc0_clk.common, + &dram_drc1_clk.common, + + &bus_fe0_clk.common, + &bus_fe1_clk.common, + &bus_fe2_clk.common, + &bus_deu0_clk.common, + &bus_deu1_clk.common, + &bus_be0_clk.common, + &bus_be1_clk.common, + &bus_be2_clk.common, + &bus_drc0_clk.common, + &bus_drc1_clk.common, + + &fe0_div_clk.common, + &fe1_div_clk.common, + &fe2_div_clk.common, + &be0_div_clk.common, + &be1_div_clk.common, + &be2_div_clk.common, +}; + +static struct clk_hw_onecell_data sun9i_a80_de_hw_clks = { + .hws = { + [CLK_FE0] = &fe0_clk.common.hw, + [CLK_FE1] = &fe1_clk.common.hw, + [CLK_FE2] = &fe2_clk.common.hw, + [CLK_IEP_DEU0] = &iep_deu0_clk.common.hw, + [CLK_IEP_DEU1] = &iep_deu1_clk.common.hw, + [CLK_BE0] = &be0_clk.common.hw, + [CLK_BE1] = &be1_clk.common.hw, + [CLK_BE2] = &be2_clk.common.hw, + [CLK_IEP_DRC0] = &iep_drc0_clk.common.hw, + [CLK_IEP_DRC1] = &iep_drc1_clk.common.hw, + [CLK_MERGE] = &merge_clk.common.hw, + + [CLK_DRAM_FE0] = &dram_fe0_clk.common.hw, + [CLK_DRAM_FE1] = &dram_fe1_clk.common.hw, + [CLK_DRAM_FE2] = &dram_fe2_clk.common.hw, + [CLK_DRAM_DEU0] = &dram_deu0_clk.common.hw, + [CLK_DRAM_DEU1] = &dram_deu1_clk.common.hw, + [CLK_DRAM_BE0] = &dram_be0_clk.common.hw, + [CLK_DRAM_BE1] = &dram_be1_clk.common.hw, + [CLK_DRAM_BE2] = &dram_be2_clk.common.hw, + [CLK_DRAM_DRC0] = &dram_drc0_clk.common.hw, + [CLK_DRAM_DRC1] = &dram_drc1_clk.common.hw, + + [CLK_BUS_FE0] = &bus_fe0_clk.common.hw, + [CLK_BUS_FE1] = &bus_fe1_clk.common.hw, + [CLK_BUS_FE2] = &bus_fe2_clk.common.hw, + [CLK_BUS_DEU0] = &bus_deu0_clk.common.hw, + [CLK_BUS_DEU1] = &bus_deu1_clk.common.hw, + [CLK_BUS_BE0] = &bus_be0_clk.common.hw, + [CLK_BUS_BE1] = &bus_be1_clk.common.hw, + [CLK_BUS_BE2] = &bus_be2_clk.common.hw, + [CLK_BUS_DRC0] = &bus_drc0_clk.common.hw, + [CLK_BUS_DRC1] = &bus_drc1_clk.common.hw, + + [CLK_FE0_DIV] = &fe0_div_clk.common.hw, + [CLK_FE1_DIV] = &fe1_div_clk.common.hw, + [CLK_FE2_DIV] = &fe2_div_clk.common.hw, + [CLK_BE0_DIV] = &be0_div_clk.common.hw, + [CLK_BE1_DIV] = &be1_div_clk.common.hw, + [CLK_BE2_DIV] = &be2_div_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun9i_a80_de_resets[] = { + [RST_FE0] = { 0x0c, BIT(0) }, + [RST_FE1] = { 0x0c, BIT(1) }, + [RST_FE2] = { 0x0c, BIT(2) }, + [RST_DEU0] = { 0x0c, BIT(4) }, + [RST_DEU1] = { 0x0c, BIT(5) }, + [RST_BE0] = { 0x0c, BIT(8) }, + [RST_BE1] = { 0x0c, BIT(9) }, + [RST_BE2] = { 0x0c, BIT(10) }, + [RST_DRC0] = { 0x0c, BIT(12) }, + [RST_DRC1] = { 0x0c, BIT(13) }, + [RST_MERGE] = { 0x0c, BIT(20) }, +}; + +static const struct sunxi_ccu_desc sun9i_a80_de_clk_desc = { + .ccu_clks = sun9i_a80_de_clks, + .num_ccu_clks = ARRAY_SIZE(sun9i_a80_de_clks), + + .hw_clks = &sun9i_a80_de_hw_clks, + + .resets = sun9i_a80_de_resets, + .num_resets = ARRAY_SIZE(sun9i_a80_de_resets), +}; + +static int sun9i_a80_de_clk_probe(struct platform_device *pdev) +{ + struct resource *res; + struct clk *bus_clk; + struct reset_control *rstc; + void __iomem *reg; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(bus_clk)) { + ret = PTR_ERR(bus_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret); + return ret; + } + + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Couldn't get reset control: %d\n", ret); + return ret; + } + + /* The bus clock needs to be enabled for us to access the registers */ + ret = clk_prepare_enable(bus_clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); + return ret; + } + + /* The reset control needs to be asserted for the controls to work */ + ret = reset_control_deassert(rstc); + if (ret) { + dev_err(&pdev->dev, + "Couldn't deassert reset control: %d\n", ret); + goto err_disable_clk; + } + + ret = sunxi_ccu_probe(pdev->dev.of_node, reg, + &sun9i_a80_de_clk_desc); + if (ret) + goto err_assert_reset; + + return 0; + +err_assert_reset: + reset_control_assert(rstc); +err_disable_clk: + clk_disable_unprepare(bus_clk); + return ret; +} + +static const struct of_device_id sun9i_a80_de_clk_ids[] = { + { .compatible = "allwinner,sun9i-a80-de-clks" }, + { } +}; + +static struct platform_driver sun9i_a80_de_clk_driver = { + .probe = sun9i_a80_de_clk_probe, + .driver = { + .name = "sun9i-a80-de-clks", + .of_match_table = sun9i_a80_de_clk_ids, + }, +}; +builtin_platform_driver(sun9i_a80_de_clk_driver); diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h new file mode 100644 index 000000000000..a4769041e40f --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.h @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai <wens@csie.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN9I_A80_DE_H_ +#define _CCU_SUN9I_A80_DE_H_ + +#include <dt-bindings/clock/sun9i-a80-de.h> +#include <dt-bindings/reset/sun9i-a80-de.h> + +/* Intermediary clock dividers are not exported */ +#define CLK_FE0_DIV 31 +#define CLK_FE1_DIV 32 +#define CLK_FE2_DIV 33 +#define CLK_BE0_DIV 34 +#define CLK_BE1_DIV 35 +#define CLK_BE2_DIV 36 + +#define CLK_NUMBER (CLK_BE2_DIV + 1) + +#endif /* _CCU_SUN9I_A80_DE_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c new file mode 100644 index 000000000000..1d76f24f7df3 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> + +#include "ccu_common.h" +#include "ccu_gate.h" +#include "ccu_reset.h" + +#include "ccu-sun9i-a80-usb.h" + +static SUNXI_CCU_GATE(bus_hci0_clk, "bus-hci0", "bus-usb", 0x0, BIT(1), 0); +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M", 0x0, BIT(2), 0); +static SUNXI_CCU_GATE(bus_hci1_clk, "bus-hci1", "bus-usb", 0x0, BIT(3), 0); +static SUNXI_CCU_GATE(bus_hci2_clk, "bus-hci2", "bus-usb", 0x0, BIT(5), 0); +static SUNXI_CCU_GATE(usb_ohci2_clk, "usb-ohci2", "osc24M", 0x0, BIT(6), 0); + +static SUNXI_CCU_GATE(usb0_phy_clk, "usb0-phy", "osc24M", 0x4, BIT(1), 0); +static SUNXI_CCU_GATE(usb1_hsic_clk, "usb1-hsic", "osc24M", 0x4, BIT(2), 0); +static SUNXI_CCU_GATE(usb1_phy_clk, "usb1-phy", "osc24M", 0x4, BIT(3), 0); +static SUNXI_CCU_GATE(usb2_hsic_clk, "usb2-hsic", "osc24M", 0x4, BIT(4), 0); +static SUNXI_CCU_GATE(usb2_phy_clk, "usb2-phy", "osc24M", 0x4, BIT(5), 0); +static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "osc24M", 0x4, BIT(10), 0); + +static struct ccu_common *sun9i_a80_usb_clks[] = { + &bus_hci0_clk.common, + &usb_ohci0_clk.common, + &bus_hci1_clk.common, + &bus_hci2_clk.common, + &usb_ohci2_clk.common, + + &usb0_phy_clk.common, + &usb1_hsic_clk.common, + &usb1_phy_clk.common, + &usb2_hsic_clk.common, + &usb2_phy_clk.common, + &usb_hsic_clk.common, +}; + +static struct clk_hw_onecell_data sun9i_a80_usb_hw_clks = { + .hws = { + [CLK_BUS_HCI0] = &bus_hci0_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_BUS_HCI1] = &bus_hci1_clk.common.hw, + [CLK_BUS_HCI2] = &bus_hci2_clk.common.hw, + [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw, + + [CLK_USB0_PHY] = &usb0_phy_clk.common.hw, + [CLK_USB1_HSIC] = &usb1_hsic_clk.common.hw, + [CLK_USB1_PHY] = &usb1_phy_clk.common.hw, + [CLK_USB2_HSIC] = &usb2_hsic_clk.common.hw, + [CLK_USB2_PHY] = &usb2_phy_clk.common.hw, + [CLK_USB_HSIC] = &usb_hsic_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun9i_a80_usb_resets[] = { + [RST_USB0_HCI] = { 0x0, BIT(17) }, + [RST_USB1_HCI] = { 0x0, BIT(18) }, + [RST_USB2_HCI] = { 0x0, BIT(19) }, + + [RST_USB0_PHY] = { 0x4, BIT(17) }, + [RST_USB1_HSIC] = { 0x4, BIT(18) }, + [RST_USB1_PHY] = { 0x4, BIT(19) }, + [RST_USB2_HSIC] = { 0x4, BIT(20) }, + [RST_USB2_PHY] = { 0x4, BIT(21) }, +}; + +static const struct sunxi_ccu_desc sun9i_a80_usb_clk_desc = { + .ccu_clks = sun9i_a80_usb_clks, + .num_ccu_clks = ARRAY_SIZE(sun9i_a80_usb_clks), + + .hw_clks = &sun9i_a80_usb_hw_clks, + + .resets = sun9i_a80_usb_resets, + .num_resets = ARRAY_SIZE(sun9i_a80_usb_resets), +}; + +static int sun9i_a80_usb_clk_probe(struct platform_device *pdev) +{ + struct resource *res; + struct clk *bus_clk; + void __iomem *reg; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(bus_clk)) { + ret = PTR_ERR(bus_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Couldn't get bus clk: %d\n", ret); + return ret; + } + + /* The bus clock needs to be enabled for us to access the registers */ + ret = clk_prepare_enable(bus_clk); + if (ret) { + dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret); + return ret; + } + + ret = sunxi_ccu_probe(pdev->dev.of_node, reg, + &sun9i_a80_usb_clk_desc); + if (ret) + goto err_disable_clk; + + return 0; + +err_disable_clk: + clk_disable_unprepare(bus_clk); + return ret; +} + +static const struct of_device_id sun9i_a80_usb_clk_ids[] = { + { .compatible = "allwinner,sun9i-a80-usb-clks" }, + { } +}; + +static struct platform_driver sun9i_a80_usb_clk_driver = { + .probe = sun9i_a80_usb_clk_probe, + .driver = { + .name = "sun9i-a80-usb-clks", + .of_match_table = sun9i_a80_usb_clk_ids, + }, +}; +builtin_platform_driver(sun9i_a80_usb_clk_driver); diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.h b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.h new file mode 100644 index 000000000000..a184280ba854 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.h @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai <wens@csie.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN9I_A80_USB_H_ +#define _CCU_SUN9I_A80_USB_H_ + +#include <dt-bindings/clock/sun9i-a80-usb.h> +#include <dt-bindings/reset/sun9i-a80-usb.h> + +#define CLK_NUMBER (CLK_USB_HSIC + 1) + +#endif /* _CCU_SUN9I_A80_USB_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c new file mode 100644 index 000000000000..e13e313ce4f5 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c @@ -0,0 +1,1223 @@ +/* + * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun9i-a80.h" + +#define CCU_SUN9I_LOCK_REG 0x09c + +static struct clk_div_table pll_cpux_p_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 4 }, + { /* Sentinel */ }, +}; + +/* + * The CPU PLLs are actually NP clocks, but P is /1 or /4, so here we + * use the NM clocks with a divider table for M. + */ +static struct ccu_nm pll_c0cpux_clk = { + .enable = BIT(31), + .lock = BIT(0), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV_TABLE(16, 1, pll_cpux_p_div_table), + .common = { + .reg = 0x000, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-c0cpux", "osc24M", + &ccu_nm_ops, CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nm pll_c1cpux_clk = { + .enable = BIT(31), + .lock = BIT(1), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV_TABLE(16, 1, pll_cpux_p_div_table), + .common = { + .reg = 0x004, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-c1cpux", "osc24M", + &ccu_nm_ops, CLK_SET_RATE_UNGATE), + }, +}; + +/* + * The Audio PLL has d1, d2 dividers in addition to the usual N, M + * factors. Since we only need 2 frequencies from this PLL: 22.5792 MHz + * and 24.576 MHz, ignore them for now. Enforce the default for them, + * which is d1 = 0, d2 = 1. + */ +#define SUN9I_A80_PLL_AUDIO_REG 0x008 + +static struct ccu_nm pll_audio_clk = { + .enable = BIT(31), + .lock = BIT(2), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV_OFFSET(0, 6, 0), + .common = { + .reg = 0x008, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-audio", "osc24M", + &ccu_nm_ops, CLK_SET_RATE_UNGATE), + }, +}; + +/* Some PLLs are input * N / div1 / div2. Model them as NKMP with no K */ +static struct ccu_nkmp pll_periph0_clk = { + .enable = BIT(31), + .lock = BIT(3), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x00c, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-periph0", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_ve_clk = { + .enable = BIT(31), + .lock = BIT(4), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x010, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-ve", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_ddr_clk = { + .enable = BIT(31), + .lock = BIT(5), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x014, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-ddr", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nm pll_video0_clk = { + .enable = BIT(31), + .lock = BIT(6), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .common = { + .reg = 0x018, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-video0", "osc24M", + &ccu_nm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_video1_clk = { + .enable = BIT(31), + .lock = BIT(7), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(0, 2), /* external divider p */ + .common = { + .reg = 0x01c, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-video1", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_gpu_clk = { + .enable = BIT(31), + .lock = BIT(8), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x020, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-gpu", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_de_clk = { + .enable = BIT(31), + .lock = BIT(9), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x024, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-de", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_isp_clk = { + .enable = BIT(31), + .lock = BIT(10), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x028, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-isp", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_nkmp pll_periph1_clk = { + .enable = BIT(31), + .lock = BIT(11), + .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), + .m = _SUNXI_CCU_DIV(16, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(18, 1), /* output divider */ + .common = { + .reg = 0x028, + .lock_reg = CCU_SUN9I_LOCK_REG, + .features = CCU_FEATURE_LOCK_REG, + .hw.init = CLK_HW_INIT("pll-periph1", "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const char * const c0cpux_parents[] = { "osc24M", "pll-c0cpux" }; +static SUNXI_CCU_MUX(c0cpux_clk, "c0cpux", c0cpux_parents, + 0x50, 0, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + +static const char * const c1cpux_parents[] = { "osc24M", "pll-c1cpux" }; +static SUNXI_CCU_MUX(c1cpux_clk, "c1cpux", c1cpux_parents, + 0x50, 8, 1, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + +static struct clk_div_table axi_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 4 }, + { .val = 5, .div = 4 }, + { .val = 6, .div = 4 }, + { .val = 7, .div = 4 }, + { /* Sentinel */ }, +}; + +static SUNXI_CCU_M(atb0_clk, "atb0", "c0cpux", 0x054, 8, 2, 0); + +static SUNXI_CCU_DIV_TABLE(axi0_clk, "axi0", "c0cpux", + 0x054, 0, 3, axi_div_table, 0); + +static SUNXI_CCU_M(atb1_clk, "atb1", "c1cpux", 0x058, 8, 2, 0); + +static SUNXI_CCU_DIV_TABLE(axi1_clk, "axi1", "c1cpux", + 0x058, 0, 3, axi_div_table, 0); + +static const char * const gtbus_parents[] = { "osc24M", "pll-periph0", + "pll-periph1", "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX(gtbus_clk, "gtbus", gtbus_parents, + 0x05c, 0, 2, 24, 2, CLK_IS_CRITICAL); + +static const char * const ahb_parents[] = { "gtbus", "pll-periph0", + "pll-periph1", "pll-periph1" }; +static struct ccu_div ahb0_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x060, + .hw.init = CLK_HW_INIT_PARENTS("ahb0", + ahb_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x064, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_div ahb2_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x068, + .hw.init = CLK_HW_INIT_PARENTS("ahb2", + ahb_parents, + &ccu_div_ops, + 0), + }, +}; + +static const char * const apb_parents[] = { "osc24M", "pll-periph0" }; + +static struct ccu_div apb0_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 1), + .common = { + .reg = 0x070, + .hw.init = CLK_HW_INIT_PARENTS("apb0", + apb_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_div apb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 1), + .common = { + .reg = 0x074, + .hw.init = CLK_HW_INIT_PARENTS("apb1", + apb_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_div cci400_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x078, + .hw.init = CLK_HW_INIT_PARENTS("cci400", + ahb_parents, + &ccu_div_ops, + CLK_IS_CRITICAL), + }, +}; + +static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", apb_parents, + 0x080, 0, 3, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(trace_clk, "trace", apb_parents, + 0x084, 0, 3, 24, 2, BIT(31), 0); + +static const char * const out_parents[] = { "osc24M", "osc32k", "osc24M" }; +static const struct ccu_mux_fixed_prediv out_prediv = { + .index = 0, .div = 750 +}; + +static struct ccu_mp out_a_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 4, + .fixed_predivs = &out_prediv, + .n_predivs = 1, + }, + .common = { + .reg = 0x180, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-a", + out_parents, + &ccu_mp_ops, + 0), + }, +}; + +static struct ccu_mp out_b_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 4, + .fixed_predivs = &out_prediv, + .n_predivs = 1, + }, + .common = { + .reg = 0x184, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-b", + out_parents, + &ccu_mp_ops, + 0), + }, +}; + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0" }; + +static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_0_clk, "nand0-0", mod0_default_parents, + 0x400, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_1_clk, "nand0-1", mod0_default_parents, + 0x404, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_0_clk, "nand1-0", mod0_default_parents, + 0x408, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_1_clk, "nand1-1", mod0_default_parents, + 0x40c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, + 0x410, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0-sample", "mmc0", + 0x410, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0-output", "mmc0", + 0x410, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, + 0x414, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1-sample", "mmc1", + 0x414, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1-output", "mmc1", + 0x414, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, + 0x418, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2-sample", "mmc2", + 0x418, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2-output", "mmc2", + 0x418, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents, + 0x41c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_PHASE(mmc3_sample_clk, "mmc3-sample", "mmc3", + 0x41c, 20, 3, 0); +static SUNXI_CCU_PHASE(mmc3_output_clk, "mmc3-output", "mmc3", + 0x41c, 8, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents, + 0x428, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const ss_parents[] = { "osc24M", "pll-periph", + "pll-periph1" }; +static const u8 ss_table[] = { 0, 1, 13 }; +static struct ccu_mp ss_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(0, 4), + .p = _SUNXI_CCU_DIV(16, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 4, ss_table), + .common = { + .reg = 0x42c, + .hw.init = CLK_HW_INIT_PARENTS("ss", + ss_parents, + &ccu_mp_ops, + 0), + }, +}; + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, + 0x430, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, + 0x434, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, + 0x438, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents, + 0x43c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_M_WITH_GATE(i2s0_clk, "i2s0", "pll-audio", + 0x440, 0, 4, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_GATE(i2s1_clk, "i2s1", "pll-audio", + 0x444, 0, 4, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio", + 0x44c, 0, 4, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const sdram_parents[] = { "pll-periph0", "pll-ddr" }; +static const u8 sdram_table[] = { 0, 3 }; + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(sdram_clk, "sdram", + sdram_parents, sdram_table, + 0x484, + 8, 4, /* M */ + 12, 4, /* mux */ + 0, /* no gate */ + CLK_IS_CRITICAL); + +static SUNXI_CCU_M_WITH_GATE(de_clk, "de", "pll-de", 0x490, + 0, 4, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(edp_clk, "edp", "osc24M", 0x494, BIT(31), 0); + +static const char * const mp_parents[] = { "pll-video1", "pll-gpu", "pll-de" }; +static const u8 mp_table[] = { 9, 10, 11 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mp_clk, "mp", mp_parents, mp_table, + 0x498, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const display_parents[] = { "pll-video0", "pll-video1" }; +static const u8 display_table[] = { 8, 9 }; + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd0_clk, "lcd0", + display_parents, display_table, + 0x49c, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_NO_REPARENT | + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd1_clk, "lcd1", + display_parents, display_table, + 0x4a0, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_NO_REPARENT | + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mipi_dsi0_clk, "mipi-dsi0", + display_parents, display_table, + 0x4a8, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static const char * const mipi_dsi1_parents[] = { "osc24M", "pll-video1" }; +static const u8 mipi_dsi1_table[] = { 0, 9 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(mipi_dsi1_clk, "mipi-dsi1", + mipi_dsi1_parents, mipi_dsi1_table, + 0x4ac, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(hdmi_clk, "hdmi", + display_parents, display_table, + 0x4b0, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_NO_REPARENT | + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(hdmi_slow_clk, "hdmi-slow", "osc24M", 0x4b4, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(mipi_csi_clk, "mipi-csi", "osc24M", 0x4bc, + 0, 4, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(csi_isp_clk, "csi-isp", "pll-isp", 0x4c0, + 0, 4, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(csi_misc_clk, "csi-misc", "osc24M", 0x4c0, BIT(16), 0); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi0_mclk_clk, "csi0-mclk", + mipi_dsi1_parents, mipi_dsi1_table, + 0x4c4, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi1_mclk_clk, "csi1-mclk", + mipi_dsi1_parents, mipi_dsi1_table, + 0x4c8, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static const char * const fd_parents[] = { "pll-periph0", "pll-isp" }; +static const u8 fd_table[] = { 1, 12 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(fd_clk, "fd", fd_parents, fd_table, + 0x4cc, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x4d0, + 16, 3, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x4d4, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(gpu_core_clk, "gpu-core", "pll-gpu", 0x4f0, + 0, 3, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_GATE(gpu_memory_clk, "gpu-memory", "pll-gpu", 0x4f4, + 0, 3, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const gpu_axi_parents[] = { "pll-periph0", "pll-gpu" }; +static const u8 gpu_axi_table[] = { 1, 10 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(gpu_axi_clk, "gpu-axi", + gpu_axi_parents, gpu_axi_table, + 0x4f8, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(sata_clk, "sata", "pll-periph0", 0x500, + 0, 4, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(ac97_clk, "ac97", "pll-audio", + 0x504, 0, 4, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_GATE(mipi_hsi_clk, "mipi-hsi", + mod0_default_parents, 0x508, + 0, 4, /* M */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const gpadc_parents[] = { "osc24M", "pll-audio", "osc32k" }; +static const u8 gpadc_table[] = { 0, 4, 7 }; +static struct ccu_mp gpadc_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(0, 4), + .p = _SUNXI_CCU_DIV(16, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 4, gpadc_table), + .common = { + .reg = 0x50c, + .hw.init = CLK_HW_INIT_PARENTS("gpadc", + gpadc_parents, + &ccu_mp_ops, + 0), + }, +}; + +static const char * const cir_tx_parents[] = { "osc24M", "osc32k" }; +static const u8 cir_tx_table[] = { 0, 7 }; +static struct ccu_mp cir_tx_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(0, 4), + .p = _SUNXI_CCU_DIV(16, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 4, cir_tx_table), + .common = { + .reg = 0x510, + .hw.init = CLK_HW_INIT_PARENTS("cir-tx", + cir_tx_parents, + &ccu_mp_ops, + 0), + }, +}; + +/* AHB0 bus gates */ +static SUNXI_CCU_GATE(bus_fd_clk, "bus-fd", "ahb0", + 0x580, BIT(0), 0); +static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb0", + 0x580, BIT(1), 0); +static SUNXI_CCU_GATE(bus_gpu_ctrl_clk, "bus-gpu-ctrl", "ahb0", + 0x580, BIT(3), 0); +static SUNXI_CCU_GATE(bus_ss_clk, "bus-ss", "ahb0", + 0x580, BIT(5), 0); +static SUNXI_CCU_GATE(bus_mmc_clk, "bus-mmc", "ahb0", + 0x580, BIT(8), 0); +static SUNXI_CCU_GATE(bus_nand0_clk, "bus-nand0", "ahb0", + 0x580, BIT(12), 0); +static SUNXI_CCU_GATE(bus_nand1_clk, "bus-nand1", "ahb0", + 0x580, BIT(13), 0); +static SUNXI_CCU_GATE(bus_sdram_clk, "bus-sdram", "ahb0", + 0x580, BIT(14), 0); +static SUNXI_CCU_GATE(bus_mipi_hsi_clk, "bus-mipi-hsi", "ahb0", + 0x580, BIT(15), 0); +static SUNXI_CCU_GATE(bus_sata_clk, "bus-sata", "ahb0", + 0x580, BIT(16), 0); +static SUNXI_CCU_GATE(bus_ts_clk, "bus-ts", "ahb0", + 0x580, BIT(18), 0); +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb0", + 0x580, BIT(20), 0); +static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb0", + 0x580, BIT(21), 0); +static SUNXI_CCU_GATE(bus_spi2_clk, "bus-spi2", "ahb0", + 0x580, BIT(22), 0); +static SUNXI_CCU_GATE(bus_spi3_clk, "bus-spi3", "ahb0", + 0x580, BIT(23), 0); + +/* AHB1 bus gates */ +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", + 0x584, BIT(0), 0); +static SUNXI_CCU_GATE(bus_usb_clk, "bus-usb", "ahb1", + 0x584, BIT(1), 0); +static SUNXI_CCU_GATE(bus_gmac_clk, "bus-gmac", "ahb1", + 0x584, BIT(17), 0); +static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1", + 0x584, BIT(21), 0); +static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1", + 0x584, BIT(22), 0); +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", + 0x584, BIT(23), 0); +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", + 0x584, BIT(24), 0); + +/* AHB2 bus gates */ +static SUNXI_CCU_GATE(bus_lcd0_clk, "bus-lcd0", "ahb2", + 0x588, BIT(0), 0); +static SUNXI_CCU_GATE(bus_lcd1_clk, "bus-lcd1", "ahb2", + 0x588, BIT(1), 0); +static SUNXI_CCU_GATE(bus_edp_clk, "bus-edp", "ahb2", + 0x588, BIT(2), 0); +static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb2", + 0x588, BIT(4), 0); +static SUNXI_CCU_GATE(bus_hdmi_clk, "bus-hdmi", "ahb2", + 0x588, BIT(5), 0); +static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb2", + 0x588, BIT(7), 0); +static SUNXI_CCU_GATE(bus_mp_clk, "bus-mp", "ahb2", + 0x588, BIT(8), 0); +static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb2", + 0x588, BIT(11), 0); + +/* APB0 bus gates */ +static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb0", + 0x590, BIT(1), 0); +static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb0", + 0x590, BIT(5), 0); +static SUNXI_CCU_GATE(bus_ac97_clk, "bus-ac97", "apb0", + 0x590, BIT(11), 0); +static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb0", + 0x590, BIT(12), 0); +static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb0", + 0x590, BIT(13), 0); +static SUNXI_CCU_GATE(bus_lradc_clk, "bus-lradc", "apb0", + 0x590, BIT(15), 0); +static SUNXI_CCU_GATE(bus_gpadc_clk, "bus-gpadc", "apb0", + 0x590, BIT(17), 0); +static SUNXI_CCU_GATE(bus_twd_clk, "bus-twd", "apb0", + 0x590, BIT(18), 0); +static SUNXI_CCU_GATE(bus_cir_tx_clk, "bus-cir-tx", "apb0", + 0x590, BIT(19), 0); + +/* APB1 bus gates */ +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb1", + 0x594, BIT(0), 0); +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb1", + 0x594, BIT(1), 0); +static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb1", + 0x594, BIT(2), 0); +static SUNXI_CCU_GATE(bus_i2c3_clk, "bus-i2c3", "apb1", + 0x594, BIT(3), 0); +static SUNXI_CCU_GATE(bus_i2c4_clk, "bus-i2c4", "apb1", + 0x594, BIT(4), 0); +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb1", + 0x594, BIT(16), 0); +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb1", + 0x594, BIT(17), 0); +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb1", + 0x594, BIT(18), 0); +static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb1", + 0x594, BIT(19), 0); +static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb1", + 0x594, BIT(20), 0); +static SUNXI_CCU_GATE(bus_uart5_clk, "bus-uart5", "apb1", + 0x594, BIT(21), 0); + +static struct ccu_common *sun9i_a80_ccu_clks[] = { + &pll_c0cpux_clk.common, + &pll_c1cpux_clk.common, + &pll_audio_clk.common, + &pll_periph0_clk.common, + &pll_ve_clk.common, + &pll_ddr_clk.common, + &pll_video0_clk.common, + &pll_video1_clk.common, + &pll_gpu_clk.common, + &pll_de_clk.common, + &pll_isp_clk.common, + &pll_periph1_clk.common, + &c0cpux_clk.common, + &c1cpux_clk.common, + &atb0_clk.common, + &axi0_clk.common, + &atb1_clk.common, + &axi1_clk.common, + >bus_clk.common, + &ahb0_clk.common, + &ahb1_clk.common, + &ahb2_clk.common, + &apb0_clk.common, + &apb1_clk.common, + &cci400_clk.common, + &ats_clk.common, + &trace_clk.common, + + &out_a_clk.common, + &out_b_clk.common, + + /* module clocks */ + &nand0_0_clk.common, + &nand0_1_clk.common, + &nand1_0_clk.common, + &nand1_1_clk.common, + &mmc0_clk.common, + &mmc0_sample_clk.common, + &mmc0_output_clk.common, + &mmc1_clk.common, + &mmc1_sample_clk.common, + &mmc1_output_clk.common, + &mmc2_clk.common, + &mmc2_sample_clk.common, + &mmc2_output_clk.common, + &mmc3_clk.common, + &mmc3_sample_clk.common, + &mmc3_output_clk.common, + &ts_clk.common, + &ss_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &spi3_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &spdif_clk.common, + &sdram_clk.common, + &de_clk.common, + &edp_clk.common, + &mp_clk.common, + &lcd0_clk.common, + &lcd1_clk.common, + &mipi_dsi0_clk.common, + &mipi_dsi1_clk.common, + &hdmi_clk.common, + &hdmi_slow_clk.common, + &mipi_csi_clk.common, + &csi_isp_clk.common, + &csi_misc_clk.common, + &csi0_mclk_clk.common, + &csi1_mclk_clk.common, + &fd_clk.common, + &ve_clk.common, + &avs_clk.common, + &gpu_core_clk.common, + &gpu_memory_clk.common, + &gpu_axi_clk.common, + &sata_clk.common, + &ac97_clk.common, + &mipi_hsi_clk.common, + &gpadc_clk.common, + &cir_tx_clk.common, + + /* AHB0 bus gates */ + &bus_fd_clk.common, + &bus_ve_clk.common, + &bus_gpu_ctrl_clk.common, + &bus_ss_clk.common, + &bus_mmc_clk.common, + &bus_nand0_clk.common, + &bus_nand1_clk.common, + &bus_sdram_clk.common, + &bus_mipi_hsi_clk.common, + &bus_sata_clk.common, + &bus_ts_clk.common, + &bus_spi0_clk.common, + &bus_spi1_clk.common, + &bus_spi2_clk.common, + &bus_spi3_clk.common, + + /* AHB1 bus gates */ + &bus_otg_clk.common, + &bus_usb_clk.common, + &bus_gmac_clk.common, + &bus_msgbox_clk.common, + &bus_spinlock_clk.common, + &bus_hstimer_clk.common, + &bus_dma_clk.common, + + /* AHB2 bus gates */ + &bus_lcd0_clk.common, + &bus_lcd1_clk.common, + &bus_edp_clk.common, + &bus_csi_clk.common, + &bus_hdmi_clk.common, + &bus_de_clk.common, + &bus_mp_clk.common, + &bus_mipi_dsi_clk.common, + + /* APB0 bus gates */ + &bus_spdif_clk.common, + &bus_pio_clk.common, + &bus_ac97_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_lradc_clk.common, + &bus_gpadc_clk.common, + &bus_twd_clk.common, + &bus_cir_tx_clk.common, + + /* APB1 bus gates */ + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_i2c3_clk.common, + &bus_i2c4_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &bus_uart5_clk.common, +}; + +static struct clk_hw_onecell_data sun9i_a80_hw_clks = { + .hws = { + [CLK_PLL_C0CPUX] = &pll_c0cpux_clk.common.hw, + [CLK_PLL_C1CPUX] = &pll_c1cpux_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.common.hw, + [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_DE] = &pll_de_clk.common.hw, + [CLK_PLL_ISP] = &pll_isp_clk.common.hw, + [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw, + [CLK_C0CPUX] = &c0cpux_clk.common.hw, + [CLK_C1CPUX] = &c1cpux_clk.common.hw, + [CLK_ATB0] = &atb0_clk.common.hw, + [CLK_AXI0] = &axi0_clk.common.hw, + [CLK_ATB1] = &atb1_clk.common.hw, + [CLK_AXI1] = &axi1_clk.common.hw, + [CLK_GTBUS] = >bus_clk.common.hw, + [CLK_AHB0] = &ahb0_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_AHB2] = &ahb2_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_CCI400] = &cci400_clk.common.hw, + [CLK_ATS] = &ats_clk.common.hw, + [CLK_TRACE] = &trace_clk.common.hw, + + [CLK_OUT_A] = &out_a_clk.common.hw, + [CLK_OUT_B] = &out_b_clk.common.hw, + + [CLK_NAND0_0] = &nand0_0_clk.common.hw, + [CLK_NAND0_1] = &nand0_1_clk.common.hw, + [CLK_NAND1_0] = &nand1_0_clk.common.hw, + [CLK_NAND1_1] = &nand1_1_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, + [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_MMC3_SAMPLE] = &mmc3_sample_clk.common.hw, + [CLK_MMC3_OUTPUT] = &mmc3_output_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_SDRAM] = &sdram_clk.common.hw, + [CLK_DE] = &de_clk.common.hw, + [CLK_EDP] = &edp_clk.common.hw, + [CLK_MP] = &mp_clk.common.hw, + [CLK_LCD0] = &lcd0_clk.common.hw, + [CLK_LCD1] = &lcd1_clk.common.hw, + [CLK_MIPI_DSI0] = &mipi_dsi0_clk.common.hw, + [CLK_MIPI_DSI1] = &mipi_dsi1_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_HDMI_SLOW] = &hdmi_slow_clk.common.hw, + [CLK_MIPI_CSI] = &mipi_csi_clk.common.hw, + [CLK_CSI_ISP] = &csi_isp_clk.common.hw, + [CLK_CSI_MISC] = &csi_misc_clk.common.hw, + [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw, + [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw, + [CLK_FD] = &fd_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_GPU_CORE] = &gpu_core_clk.common.hw, + [CLK_GPU_MEMORY] = &gpu_memory_clk.common.hw, + [CLK_GPU_AXI] = &gpu_axi_clk.common.hw, + [CLK_SATA] = &sata_clk.common.hw, + [CLK_AC97] = &ac97_clk.common.hw, + [CLK_MIPI_HSI] = &mipi_hsi_clk.common.hw, + [CLK_GPADC] = &gpadc_clk.common.hw, + [CLK_CIR_TX] = &cir_tx_clk.common.hw, + + [CLK_BUS_FD] = &bus_fd_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_GPU_CTRL] = &bus_gpu_ctrl_clk.common.hw, + [CLK_BUS_SS] = &bus_ss_clk.common.hw, + [CLK_BUS_MMC] = &bus_mmc_clk.common.hw, + [CLK_BUS_NAND0] = &bus_nand0_clk.common.hw, + [CLK_BUS_NAND1] = &bus_nand1_clk.common.hw, + [CLK_BUS_SDRAM] = &bus_sdram_clk.common.hw, + [CLK_BUS_MIPI_HSI] = &bus_mipi_hsi_clk.common.hw, + [CLK_BUS_SATA] = &bus_sata_clk.common.hw, + [CLK_BUS_TS] = &bus_ts_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, + [CLK_BUS_SPI2] = &bus_spi2_clk.common.hw, + [CLK_BUS_SPI3] = &bus_spi3_clk.common.hw, + + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_USB] = &bus_usb_clk.common.hw, + [CLK_BUS_GMAC] = &bus_gmac_clk.common.hw, + [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw, + [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + + [CLK_BUS_LCD0] = &bus_lcd0_clk.common.hw, + [CLK_BUS_LCD1] = &bus_lcd1_clk.common.hw, + [CLK_BUS_EDP] = &bus_edp_clk.common.hw, + [CLK_BUS_CSI] = &bus_csi_clk.common.hw, + [CLK_BUS_HDMI] = &bus_hdmi_clk.common.hw, + [CLK_BUS_DE] = &bus_de_clk.common.hw, + [CLK_BUS_MP] = &bus_mp_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + + [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_clk.common.hw, + [CLK_BUS_AC97] = &bus_ac97_clk.common.hw, + [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_LRADC] = &bus_lradc_clk.common.hw, + [CLK_BUS_GPADC] = &bus_gpadc_clk.common.hw, + [CLK_BUS_TWD] = &bus_twd_clk.common.hw, + [CLK_BUS_CIR_TX] = &bus_cir_tx_clk.common.hw, + + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, + [CLK_BUS_I2C3] = &bus_i2c3_clk.common.hw, + [CLK_BUS_I2C4] = &bus_i2c4_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, + [CLK_BUS_UART4] = &bus_uart4_clk.common.hw, + [CLK_BUS_UART5] = &bus_uart5_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun9i_a80_ccu_resets[] = { + /* AHB0 reset controls */ + [RST_BUS_FD] = { 0x5a0, BIT(0) }, + [RST_BUS_VE] = { 0x5a0, BIT(1) }, + [RST_BUS_GPU_CTRL] = { 0x5a0, BIT(3) }, + [RST_BUS_SS] = { 0x5a0, BIT(5) }, + [RST_BUS_MMC] = { 0x5a0, BIT(8) }, + [RST_BUS_NAND0] = { 0x5a0, BIT(12) }, + [RST_BUS_NAND1] = { 0x5a0, BIT(13) }, + [RST_BUS_SDRAM] = { 0x5a0, BIT(14) }, + [RST_BUS_SATA] = { 0x5a0, BIT(16) }, + [RST_BUS_TS] = { 0x5a0, BIT(18) }, + [RST_BUS_SPI0] = { 0x5a0, BIT(20) }, + [RST_BUS_SPI1] = { 0x5a0, BIT(21) }, + [RST_BUS_SPI2] = { 0x5a0, BIT(22) }, + [RST_BUS_SPI3] = { 0x5a0, BIT(23) }, + + /* AHB1 reset controls */ + [RST_BUS_OTG] = { 0x5a4, BIT(0) }, + [RST_BUS_OTG_PHY] = { 0x5a4, BIT(1) }, + [RST_BUS_MIPI_HSI] = { 0x5a4, BIT(9) }, + [RST_BUS_GMAC] = { 0x5a4, BIT(17) }, + [RST_BUS_MSGBOX] = { 0x5a4, BIT(21) }, + [RST_BUS_SPINLOCK] = { 0x5a4, BIT(22) }, + [RST_BUS_HSTIMER] = { 0x5a4, BIT(23) }, + [RST_BUS_DMA] = { 0x5a4, BIT(24) }, + + /* AHB2 reset controls */ + [RST_BUS_LCD0] = { 0x5a8, BIT(0) }, + [RST_BUS_LCD1] = { 0x5a8, BIT(1) }, + [RST_BUS_EDP] = { 0x5a8, BIT(2) }, + [RST_BUS_LVDS] = { 0x5a8, BIT(3) }, + [RST_BUS_CSI] = { 0x5a8, BIT(4) }, + [RST_BUS_HDMI0] = { 0x5a8, BIT(5) }, + [RST_BUS_HDMI1] = { 0x5a8, BIT(6) }, + [RST_BUS_DE] = { 0x5a8, BIT(7) }, + [RST_BUS_MP] = { 0x5a8, BIT(8) }, + [RST_BUS_GPU] = { 0x5a8, BIT(9) }, + [RST_BUS_MIPI_DSI] = { 0x5a8, BIT(11) }, + + /* APB0 reset controls */ + [RST_BUS_SPDIF] = { 0x5b0, BIT(1) }, + [RST_BUS_AC97] = { 0x5b0, BIT(11) }, + [RST_BUS_I2S0] = { 0x5b0, BIT(12) }, + [RST_BUS_I2S1] = { 0x5b0, BIT(13) }, + [RST_BUS_LRADC] = { 0x5b0, BIT(15) }, + [RST_BUS_GPADC] = { 0x5b0, BIT(17) }, + [RST_BUS_CIR_TX] = { 0x5b0, BIT(19) }, + + /* APB1 reset controls */ + [RST_BUS_I2C0] = { 0x5b4, BIT(0) }, + [RST_BUS_I2C1] = { 0x5b4, BIT(1) }, + [RST_BUS_I2C2] = { 0x5b4, BIT(2) }, + [RST_BUS_I2C3] = { 0x5b4, BIT(3) }, + [RST_BUS_I2C4] = { 0x5b4, BIT(4) }, + [RST_BUS_UART0] = { 0x5b4, BIT(16) }, + [RST_BUS_UART1] = { 0x5b4, BIT(17) }, + [RST_BUS_UART2] = { 0x5b4, BIT(18) }, + [RST_BUS_UART3] = { 0x5b4, BIT(19) }, + [RST_BUS_UART4] = { 0x5b4, BIT(20) }, + [RST_BUS_UART5] = { 0x5b4, BIT(21) }, +}; + +static const struct sunxi_ccu_desc sun9i_a80_ccu_desc = { + .ccu_clks = sun9i_a80_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun9i_a80_ccu_clks), + + .hw_clks = &sun9i_a80_hw_clks, + + .resets = sun9i_a80_ccu_resets, + .num_resets = ARRAY_SIZE(sun9i_a80_ccu_resets), +}; + +static int sun9i_a80_ccu_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *reg; + u32 val; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + /* Enforce d1 = 0, d2 = 0 for Audio PLL */ + val = readl(reg + SUN9I_A80_PLL_AUDIO_REG); + val &= (BIT(16) & BIT(18)); + writel(val, reg + SUN9I_A80_PLL_AUDIO_REG); + + return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun9i_a80_ccu_desc); +} + +static const struct of_device_id sun9i_a80_ccu_ids[] = { + { .compatible = "allwinner,sun9i-a80-ccu" }, + { } +}; + +static struct platform_driver sun9i_a80_ccu_driver = { + .probe = sun9i_a80_ccu_probe, + .driver = { + .name = "sun9i-a80-ccu", + .of_match_table = sun9i_a80_ccu_ids, + }, +}; +builtin_platform_driver(sun9i_a80_ccu_driver); diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.h b/drivers/clk/sunxi-ng/ccu-sun9i-a80.h new file mode 100644 index 000000000000..315662341c70 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.h @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai <wens@csie.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN9I_A80_H_ +#define _CCU_SUN9I_A80_H_ + +#include <dt-bindings/clock/sun9i-a80-ccu.h> +#include <dt-bindings/reset/sun9i-a80-ccu.h> + +#define CLK_PLL_C0CPUX 0 +#define CLK_PLL_C1CPUX 1 + +/* pll-audio and pll-periph0 are exported to the PRCM block */ + +#define CLK_PLL_VE 4 +#define CLK_PLL_DDR 5 +#define CLK_PLL_VIDEO0 6 +#define CLK_PLL_VIDEO1 7 +#define CLK_PLL_GPU 8 +#define CLK_PLL_DE 9 +#define CLK_PLL_ISP 10 +#define CLK_PLL_PERIPH1 11 + +/* The CPUX clocks are exported */ + +#define CLK_ATB0 14 +#define CLK_AXI0 15 +#define CLK_ATB1 16 +#define CLK_AXI1 17 +#define CLK_GTBUS 18 +#define CLK_AHB0 19 +#define CLK_AHB1 20 +#define CLK_AHB2 21 +#define CLK_APB0 22 +#define CLK_APB1 23 +#define CLK_CCI400 24 +#define CLK_ATS 25 +#define CLK_TRACE 26 + +/* module clocks and bus gates exported */ + +#define CLK_NUMBER (CLK_BUS_UART5 + 1) + +#endif /* _CCU_SUN9I_A80_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c index 51d4bac97ab3..8a47bafd7890 100644 --- a/drivers/clk/sunxi-ng/ccu_common.c +++ b/drivers/clk/sunxi-ng/ccu_common.c @@ -25,13 +25,18 @@ static DEFINE_SPINLOCK(ccu_lock); void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) { + void __iomem *addr; u32 reg; if (!lock) return; - WARN_ON(readl_relaxed_poll_timeout(common->base + common->reg, reg, - reg & lock, 100, 70000)); + if (common->features & CCU_FEATURE_LOCK_REG) + addr = common->base + common->lock_reg; + else + addr = common->base + common->reg; + + WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); } int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, @@ -70,6 +75,11 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, goto err_clk_unreg; reset = kzalloc(sizeof(*reset), GFP_KERNEL); + if (!reset) { + ret = -ENOMEM; + goto err_alloc_reset; + } + reset->rcdev.of_node = node; reset->rcdev.ops = &ccu_reset_ops; reset->rcdev.owner = THIS_MODULE; @@ -85,6 +95,16 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, return 0; err_of_clk_unreg: + kfree(reset); +err_alloc_reset: + of_clk_del_provider(node); err_clk_unreg: + while (--i >= 0) { + struct clk_hw *hw = desc->hw_clks->hws[i]; + + if (!hw) + continue; + clk_hw_unregister(hw); + } return ret; } diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h index b3d9abfbd721..73d81dc58fc5 100644 --- a/drivers/clk/sunxi-ng/ccu_common.h +++ b/drivers/clk/sunxi-ng/ccu_common.h @@ -21,6 +21,8 @@ #define CCU_FEATURE_VARIABLE_PREDIV BIT(1) #define CCU_FEATURE_FIXED_PREDIV BIT(2) #define CCU_FEATURE_FIXED_POSTDIV BIT(3) +#define CCU_FEATURE_ALL_PREDIV BIT(4) +#define CCU_FEATURE_LOCK_REG BIT(5) struct device_node; @@ -56,6 +58,8 @@ struct device_node; struct ccu_common { void __iomem *base; u16 reg; + u16 lock_reg; + u32 prediv; unsigned long features; spinlock_t *lock; diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index 8659b4cb6c20..4057e6021aa9 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -77,6 +77,18 @@ static int ccu_div_determine_rate(struct clk_hw *hw, { struct ccu_div *cd = hw_to_ccu_div(hw); + if (clk_hw_get_num_parents(hw) == 1) { + req->rate = divider_round_rate(hw, req->rate, + &req->best_parent_rate, + cd->div.table, + cd->div.width, + cd->div.flags); + + req->best_parent_hw = clk_hw_get_parent(hw); + + return 0; + } + return ccu_mux_helper_determine_rate(&cd->common, &cd->mux, req, ccu_div_round_rate, cd); } diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index 06540f7cf41c..08d074451204 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -41,6 +41,7 @@ struct ccu_div_internal { u8 width; u32 max; + u32 offset; u32 flags; @@ -58,20 +59,27 @@ struct ccu_div_internal { #define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \ _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0) -#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags) \ +#define _SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, _off, _max, _flags) \ { \ .shift = _shift, \ .width = _width, \ .flags = _flags, \ .max = _max, \ + .offset = _off, \ } +#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags) \ + _SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, 1, _max, _flags) + #define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \ _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, 0, _flags) #define _SUNXI_CCU_DIV_MAX(_shift, _width, _max) \ _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, 0) +#define _SUNXI_CCU_DIV_OFFSET(_shift, _width, _offset) \ + _SUNXI_CCU_DIV_OFFSET_MAX_FLAGS(_shift, _width, _offset, 0, 0) + #define _SUNXI_CCU_DIV(_shift, _width) \ _SUNXI_CCU_DIV_FLAGS(_shift, _width, 0) diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c index ebb1b31568a5..22c2ca7a2a22 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.c +++ b/drivers/clk/sunxi-ng/ccu_mp.c @@ -89,11 +89,14 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw, m = reg >> cmp->m.shift; m &= (1 << cmp->m.width) - 1; + m += cmp->m.offset; + if (!m) + m++; p = reg >> cmp->p.shift; p &= (1 << cmp->p.width) - 1; - return (parent_rate >> p) / (m + 1); + return (parent_rate >> p) / m; } static int ccu_mp_determine_rate(struct clk_hw *hw, @@ -124,9 +127,10 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate, reg = readl(cmp->common.base + cmp->common.reg); reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift); reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift); + reg |= (m - cmp->m.offset) << cmp->m.shift; + reg |= ilog2(p) << cmp->p.shift; - writel(reg | (ilog2(p) << cmp->p.shift) | ((m - 1) << cmp->m.shift), - cmp->common.base + cmp->common.reg); + writel(reg, cmp->common.base + cmp->common.reg); spin_unlock_irqrestore(cmp->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c index 678b6cb49f01..8724c01171b1 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.c +++ b/drivers/clk/sunxi-ng/ccu_mult.c @@ -40,8 +40,13 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux, struct ccu_mult *cm = data; struct _ccu_mult _cm; - _cm.min = 1; - _cm.max = 1 << cm->mult.width; + _cm.min = cm->mult.min; + + if (cm->mult.max) + _cm.max = cm->mult.max; + else + _cm.max = (1 << cm->mult.width) + cm->mult.offset - 1; + ccu_mult_find_best(parent_rate, rate, &_cm); return parent_rate * _cm.mult; @@ -75,6 +80,9 @@ static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw, unsigned long val; u32 reg; + if (ccu_frac_helper_is_enabled(&cm->common, &cm->frac)) + return ccu_frac_helper_read_rate(&cm->common, &cm->frac); + reg = readl(cm->common.base + cm->common.reg); val = reg >> cm->mult.shift; val &= (1 << cm->mult.width) - 1; @@ -82,7 +90,7 @@ static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw, ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, &parent_rate); - return parent_rate * (val + 1); + return parent_rate * (val + cm->mult.offset); } static int ccu_mult_determine_rate(struct clk_hw *hw, @@ -102,20 +110,30 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; + if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate)) + return ccu_frac_helper_set_rate(&cm->common, &cm->frac, rate); + else + ccu_frac_helper_disable(&cm->common, &cm->frac); + ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1, &parent_rate); _cm.min = cm->mult.min; - _cm.max = 1 << cm->mult.width; + + if (cm->mult.max) + _cm.max = cm->mult.max; + else + _cm.max = (1 << cm->mult.width) + cm->mult.offset - 1; + ccu_mult_find_best(parent_rate, rate, &_cm); spin_lock_irqsave(cm->common.lock, flags); reg = readl(cm->common.base + cm->common.reg); reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift); + reg |= ((_cm.mult - cm->mult.offset) << cm->mult.shift); - writel(reg | ((_cm.mult - 1) << cm->mult.shift), - cm->common.base + cm->common.reg); + writel(reg, cm->common.base + cm->common.reg); spin_unlock_irqrestore(cm->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h index c1a2134bdc71..524acddfcb2e 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.h +++ b/drivers/clk/sunxi-ng/ccu_mult.h @@ -2,27 +2,39 @@ #define _CCU_MULT_H_ #include "ccu_common.h" +#include "ccu_frac.h" #include "ccu_mux.h" struct ccu_mult_internal { + u8 offset; u8 shift; u8 width; u8 min; + u8 max; }; -#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min) \ - { \ - .shift = _shift, \ - .width = _width, \ - .min = _min, \ +#define _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, _offset, _min, _max) \ + { \ + .min = _min, \ + .max = _max, \ + .offset = _offset, \ + .shift = _shift, \ + .width = _width, \ } +#define _SUNXI_CCU_MULT_MIN(_shift, _width, _min) \ + _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, 1, _min, 0) + +#define _SUNXI_CCU_MULT_OFFSET(_shift, _width, _offset) \ + _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, _offset, 1, 0) + #define _SUNXI_CCU_MULT(_shift, _width) \ - _SUNXI_CCU_MULT_MIN(_shift, _width, 1) + _SUNXI_CCU_MULT_OFFSET_MIN_MAX(_shift, _width, 1, 1, 0) struct ccu_mult { u32 enable; + struct ccu_frac_internal frac; struct ccu_mult_internal mult; struct ccu_mux_internal mux; struct ccu_common common; diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index a43ad52a957d..c6bb1f523232 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -25,9 +25,15 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common, int i; if (!((common->features & CCU_FEATURE_FIXED_PREDIV) || - (common->features & CCU_FEATURE_VARIABLE_PREDIV))) + (common->features & CCU_FEATURE_VARIABLE_PREDIV) || + (common->features & CCU_FEATURE_ALL_PREDIV))) return; + if (common->features & CCU_FEATURE_ALL_PREDIV) { + *parent_rate = *parent_rate / common->prediv; + return; + } + reg = readl(common->base + common->reg); if (parent_index < 0) { parent_index = reg >> cm->shift; @@ -64,19 +70,46 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, struct clk_hw *best_parent, *hw = &common->hw; unsigned int i; + if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) { + unsigned long adj_parent_rate; + + best_parent = clk_hw_get_parent(hw); + best_parent_rate = clk_hw_get_rate(best_parent); + + adj_parent_rate = best_parent_rate; + ccu_mux_helper_adjust_parent_for_prediv(common, cm, -1, + &adj_parent_rate); + + best_rate = round(cm, adj_parent_rate, req->rate, data); + + goto out; + } + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - unsigned long tmp_rate, parent_rate; + unsigned long tmp_rate, parent_rate, adj_parent_rate; struct clk_hw *parent; parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; - parent_rate = clk_hw_get_rate(parent); + if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { + struct clk_rate_request parent_req = *req; + int ret = __clk_determine_rate(parent, &parent_req); + + if (ret) + continue; + + parent_rate = parent_req.rate; + } else { + parent_rate = clk_hw_get_rate(parent); + } + + adj_parent_rate = parent_rate; ccu_mux_helper_adjust_parent_for_prediv(common, cm, i, - &parent_rate); + &adj_parent_rate); - tmp_rate = round(cm, clk_hw_get_rate(parent), req->rate, data); + tmp_rate = round(cm, adj_parent_rate, req->rate, data); if (tmp_rate == req->rate) { best_parent = parent; best_parent_rate = parent_rate; diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c index eaf0fdf78d2b..b9e9b8a9d1b4 100644 --- a/drivers/clk/sunxi-ng/ccu_nk.c +++ b/drivers/clk/sunxi-ng/ccu_nk.c @@ -76,12 +76,17 @@ static unsigned long ccu_nk_recalc_rate(struct clk_hw *hw, n = reg >> nk->n.shift; n &= (1 << nk->n.width) - 1; + n += nk->n.offset; + if (!n) + n++; k = reg >> nk->k.shift; k &= (1 << nk->k.width) - 1; + k += nk->k.offset; + if (!k) + k++; - rate = parent_rate * (n + 1) * (k + 1); - + rate = parent_rate * n * k; if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV) rate /= nk->fixed_post_div; @@ -98,9 +103,9 @@ static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate, rate *= nk->fixed_post_div; _nk.min_n = nk->n.min; - _nk.max_n = 1 << nk->n.width; + _nk.max_n = nk->n.max ?: 1 << nk->n.width; _nk.min_k = nk->k.min; - _nk.max_k = 1 << nk->k.width; + _nk.max_k = nk->k.max ?: 1 << nk->k.width; ccu_nk_find_best(*parent_rate, rate, &_nk); rate = *parent_rate * _nk.n * _nk.k; @@ -123,9 +128,9 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate, rate = rate * nk->fixed_post_div; _nk.min_n = nk->n.min; - _nk.max_n = 1 << nk->n.width; + _nk.max_n = nk->n.max ?: 1 << nk->n.width; _nk.min_k = nk->k.min; - _nk.max_k = 1 << nk->k.width; + _nk.max_k = nk->k.max ?: 1 << nk->k.width; ccu_nk_find_best(parent_rate, rate, &_nk); @@ -135,8 +140,9 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate, reg &= ~GENMASK(nk->n.width + nk->n.shift - 1, nk->n.shift); reg &= ~GENMASK(nk->k.width + nk->k.shift - 1, nk->k.shift); - writel(reg | ((_nk.k - 1) << nk->k.shift) | ((_nk.n - 1) << nk->n.shift), - nk->common.base + nk->common.reg); + reg |= (_nk.k - nk->k.offset) << nk->k.shift; + reg |= (_nk.n - nk->n.offset) << nk->n.shift; + writel(reg, nk->common.base + nk->common.reg); spin_unlock_irqrestore(nk->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index 9b840a47a94d..71f81e95a061 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -82,14 +82,23 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, n = reg >> nkm->n.shift; n &= (1 << nkm->n.width) - 1; + n += nkm->n.offset; + if (!n) + n++; k = reg >> nkm->k.shift; k &= (1 << nkm->k.width) - 1; + k += nkm->k.offset; + if (!k) + k++; m = reg >> nkm->m.shift; m &= (1 << nkm->m.width) - 1; + m += nkm->m.offset; + if (!m) + m++; - return parent_rate * (n + 1) * (k + 1) / (m + 1); + return parent_rate * n * k / m; } static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, @@ -101,9 +110,9 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, struct _ccu_nkm _nkm; _nkm.min_n = nkm->n.min; - _nkm.max_n = 1 << nkm->n.width; + _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width; _nkm.min_k = nkm->k.min; - _nkm.max_k = 1 << nkm->k.width; + _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width; _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; @@ -130,9 +139,9 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, u32 reg; _nkm.min_n = nkm->n.min; - _nkm.max_n = 1 << nkm->n.width; + _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width; _nkm.min_k = nkm->k.min; - _nkm.max_k = 1 << nkm->k.width; + _nkm.max_k = nkm->k.max ?: 1 << nkm->k.width; _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; @@ -145,10 +154,9 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, reg &= ~GENMASK(nkm->k.width + nkm->k.shift - 1, nkm->k.shift); reg &= ~GENMASK(nkm->m.width + nkm->m.shift - 1, nkm->m.shift); - reg |= (_nkm.n - 1) << nkm->n.shift; - reg |= (_nkm.k - 1) << nkm->k.shift; - reg |= (_nkm.m - 1) << nkm->m.shift; - + reg |= (_nkm.n - nkm->n.offset) << nkm->n.shift; + reg |= (_nkm.k - nkm->k.offset) << nkm->k.shift; + reg |= (_nkm.m - nkm->m.offset) << nkm->m.shift; writel(reg, nkm->common.base + nkm->common.reg); spin_unlock_irqrestore(nkm->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c index 684c42da3ebb..a2b40a000157 100644 --- a/drivers/clk/sunxi-ng/ccu_nkmp.c +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c @@ -88,17 +88,26 @@ static unsigned long ccu_nkmp_recalc_rate(struct clk_hw *hw, n = reg >> nkmp->n.shift; n &= (1 << nkmp->n.width) - 1; + n += nkmp->n.offset; + if (!n) + n++; k = reg >> nkmp->k.shift; k &= (1 << nkmp->k.width) - 1; + k += nkmp->k.offset; + if (!k) + k++; m = reg >> nkmp->m.shift; m &= (1 << nkmp->m.width) - 1; + m += nkmp->m.offset; + if (!m) + m++; p = reg >> nkmp->p.shift; p &= (1 << nkmp->p.width) - 1; - return (parent_rate * (n + 1) * (k + 1) >> p) / (m + 1); + return parent_rate * n * k >> p / m; } static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, @@ -108,9 +117,9 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate, struct _ccu_nkmp _nkmp; _nkmp.min_n = nkmp->n.min; - _nkmp.max_n = 1 << nkmp->n.width; + _nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width; _nkmp.min_k = nkmp->k.min; - _nkmp.max_k = 1 << nkmp->k.width; + _nkmp.max_k = nkmp->k.max ?: 1 << nkmp->k.width; _nkmp.min_m = 1; _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; _nkmp.min_p = 1; @@ -130,9 +139,9 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, u32 reg; _nkmp.min_n = 1; - _nkmp.max_n = 1 << nkmp->n.width; + _nkmp.max_n = nkmp->n.max ?: 1 << nkmp->n.width; _nkmp.min_k = 1; - _nkmp.max_k = 1 << nkmp->k.width; + _nkmp.max_k = nkmp->k.max ?: 1 << nkmp->k.width; _nkmp.min_m = 1; _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width; _nkmp.min_p = 1; @@ -148,9 +157,9 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate, reg &= ~GENMASK(nkmp->m.width + nkmp->m.shift - 1, nkmp->m.shift); reg &= ~GENMASK(nkmp->p.width + nkmp->p.shift - 1, nkmp->p.shift); - reg |= (_nkmp.n - 1) << nkmp->n.shift; - reg |= (_nkmp.k - 1) << nkmp->k.shift; - reg |= (_nkmp.m - 1) << nkmp->m.shift; + reg |= (_nkmp.n - nkmp->n.offset) << nkmp->n.shift; + reg |= (_nkmp.k - nkmp->k.offset) << nkmp->k.shift; + reg |= (_nkmp.m - nkmp->m.offset) << nkmp->m.shift; reg |= ilog2(_nkmp.p) << nkmp->p.shift; writel(reg, nkmp->common.base + nkmp->common.reg); diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index c9f3b6c982f0..af71b1909cd9 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -80,11 +80,17 @@ static unsigned long ccu_nm_recalc_rate(struct clk_hw *hw, n = reg >> nm->n.shift; n &= (1 << nm->n.width) - 1; + n += nm->n.offset; + if (!n) + n++; m = reg >> nm->m.shift; m &= (1 << nm->m.width) - 1; + m += nm->m.offset; + if (!m) + m++; - return parent_rate * (n + 1) / (m + 1); + return parent_rate * n / m; } static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, @@ -94,7 +100,7 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, struct _ccu_nm _nm; _nm.min_n = nm->n.min; - _nm.max_n = 1 << nm->n.width; + _nm.max_n = nm->n.max ?: 1 << nm->n.width; _nm.min_m = 1; _nm.max_m = nm->m.max ?: 1 << nm->m.width; @@ -117,7 +123,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, ccu_frac_helper_disable(&nm->common, &nm->frac); _nm.min_n = 1; - _nm.max_n = 1 << nm->n.width; + _nm.max_n = nm->n.max ?: 1 << nm->n.width; _nm.min_m = 1; _nm.max_m = nm->m.max ?: 1 << nm->m.width; @@ -129,8 +135,9 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, reg &= ~GENMASK(nm->n.width + nm->n.shift - 1, nm->n.shift); reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift); - writel(reg | ((_nm.m - 1) << nm->m.shift) | ((_nm.n - 1) << nm->n.shift), - nm->common.base + nm->common.reg); + reg |= (_nm.n - nm->n.offset) << nm->n.shift; + reg |= (_nm.m - nm->m.offset) << nm->m.shift; + writel(reg, nm->common.base + nm->common.reg); spin_unlock_irqrestore(nm->common.lock, flags); diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig index 1ba30d1e14f2..7ddacae5d0b1 100644 --- a/drivers/clk/tegra/Kconfig +++ b/drivers/clk/tegra/Kconfig @@ -1,3 +1,7 @@ config TEGRA_CLK_EMC def_bool y depends on TEGRA124_EMC + +config CLK_TEGRA_BPMP + def_bool y + depends on TEGRA_BPMP diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 33fd0938d79e..4be8af28ee61 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124-dfll-fcpu.o obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o obj-y += cvb.o obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o +obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c new file mode 100644 index 000000000000..638ace64033b --- /dev/null +++ b/drivers/clk/tegra/clk-bpmp.c @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2016 NVIDIA Corporation + * + * 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/clk-provider.h> +#include <linux/device.h> +#include <linux/seq_buf.h> +#include <linux/slab.h> + +#include <soc/tegra/bpmp.h> +#include <soc/tegra/bpmp-abi.h> + +#define TEGRA_BPMP_DUMP_CLOCK_INFO 0 + +#define TEGRA_BPMP_CLK_HAS_MUX BIT(0) +#define TEGRA_BPMP_CLK_HAS_SET_RATE BIT(1) +#define TEGRA_BPMP_CLK_IS_ROOT BIT(2) + +struct tegra_bpmp_clk_info { + unsigned int id; + char name[MRQ_CLK_NAME_MAXLEN]; + unsigned int parents[MRQ_CLK_MAX_PARENTS]; + unsigned int num_parents; + unsigned long flags; +}; + +struct tegra_bpmp_clk { + struct clk_hw hw; + + struct tegra_bpmp *bpmp; + unsigned int id; + + unsigned int num_parents; + unsigned int *parents; +}; + +static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw) +{ + return container_of(hw, struct tegra_bpmp_clk, hw); +} + +struct tegra_bpmp_clk_message { + unsigned int cmd; + unsigned int id; + + struct { + const void *data; + size_t size; + } tx; + + struct { + void *data; + size_t size; + } rx; +}; + +static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp, + const struct tegra_bpmp_clk_message *clk) +{ + struct mrq_clk_request request; + struct tegra_bpmp_message msg; + void *req = &request; + + memset(&request, 0, sizeof(request)); + request.cmd_and_id = (clk->cmd << 24) | clk->id; + + /* + * The mrq_clk_request structure has an anonymous union at offset 4 + * that contains all possible sub-command structures. Copy the data + * to that union. Ideally we'd be able to refer to it by name, but + * doing so would require changing the ABI header and increase the + * maintenance burden. + */ + memcpy(req + 4, clk->tx.data, clk->tx.size); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_CLK; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = clk->rx.data; + msg.rx.size = clk->rx.size; + + return tegra_bpmp_transfer(bpmp, &msg); +} + +static int tegra_bpmp_clk_prepare(struct clk_hw *hw) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct tegra_bpmp_clk_message msg; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_ENABLE; + msg.id = clk->id; + + return tegra_bpmp_clk_transfer(clk->bpmp, &msg); +} + +static void tegra_bpmp_clk_unprepare(struct clk_hw *hw) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct tegra_bpmp_clk_message msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_DISABLE; + msg.id = clk->id; + + err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); + if (err < 0) + dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n", + clk_hw_get_name(hw), err); +} + +static int tegra_bpmp_clk_is_prepared(struct clk_hw *hw) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct cmd_clk_is_enabled_response response; + struct tegra_bpmp_clk_message msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_IS_ENABLED; + msg.id = clk->id; + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); + if (err < 0) + return err; + + return response.state; +} + +static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct cmd_clk_get_rate_response response; + struct cmd_clk_get_rate_request request; + struct tegra_bpmp_clk_message msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_GET_RATE; + msg.id = clk->id; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); + if (err < 0) + return err; + + return response.rate; +} + +static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct cmd_clk_round_rate_response response; + struct cmd_clk_round_rate_request request; + struct tegra_bpmp_clk_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.rate = rate; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_ROUND_RATE; + msg.id = clk->id; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); + if (err < 0) + return err; + + return response.rate; +} + +static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct cmd_clk_set_parent_response response; + struct cmd_clk_set_parent_request request; + struct tegra_bpmp_clk_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.parent_id = clk->parents[index]; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_SET_PARENT; + msg.id = clk->id; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); + if (err < 0) + return err; + + /* XXX check parent ID in response */ + + return 0; +} + +static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct cmd_clk_get_parent_response response; + struct tegra_bpmp_clk_message msg; + unsigned int i; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_GET_PARENT; + msg.id = clk->id; + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); + if (err < 0) { + dev_err(clk->bpmp->dev, "failed to get parent for %s: %d\n", + clk_hw_get_name(hw), err); + return U8_MAX; + } + + for (i = 0; i < clk->num_parents; i++) + if (clk->parents[i] == response.parent_id) + return i; + + return U8_MAX; +} + +static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw); + struct cmd_clk_set_rate_response response; + struct cmd_clk_set_rate_request request; + struct tegra_bpmp_clk_message msg; + + memset(&request, 0, sizeof(request)); + request.rate = rate; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_SET_RATE; + msg.id = clk->id; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + return tegra_bpmp_clk_transfer(clk->bpmp, &msg); +} + +static const struct clk_ops tegra_bpmp_clk_gate_ops = { + .prepare = tegra_bpmp_clk_prepare, + .unprepare = tegra_bpmp_clk_unprepare, + .is_prepared = tegra_bpmp_clk_is_prepared, + .recalc_rate = tegra_bpmp_clk_recalc_rate, +}; + +static const struct clk_ops tegra_bpmp_clk_mux_ops = { + .prepare = tegra_bpmp_clk_prepare, + .unprepare = tegra_bpmp_clk_unprepare, + .is_prepared = tegra_bpmp_clk_is_prepared, + .recalc_rate = tegra_bpmp_clk_recalc_rate, + .set_parent = tegra_bpmp_clk_set_parent, + .get_parent = tegra_bpmp_clk_get_parent, +}; + +static const struct clk_ops tegra_bpmp_clk_rate_ops = { + .prepare = tegra_bpmp_clk_prepare, + .unprepare = tegra_bpmp_clk_unprepare, + .is_prepared = tegra_bpmp_clk_is_prepared, + .recalc_rate = tegra_bpmp_clk_recalc_rate, + .round_rate = tegra_bpmp_clk_round_rate, + .set_rate = tegra_bpmp_clk_set_rate, +}; + +static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = { + .prepare = tegra_bpmp_clk_prepare, + .unprepare = tegra_bpmp_clk_unprepare, + .is_prepared = tegra_bpmp_clk_is_prepared, + .recalc_rate = tegra_bpmp_clk_recalc_rate, + .round_rate = tegra_bpmp_clk_round_rate, + .set_parent = tegra_bpmp_clk_set_parent, + .get_parent = tegra_bpmp_clk_get_parent, + .set_rate = tegra_bpmp_clk_set_rate, +}; + +static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp) +{ + struct cmd_clk_get_max_clk_id_response response; + struct tegra_bpmp_clk_message msg; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_GET_MAX_CLK_ID; + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_clk_transfer(bpmp, &msg); + if (err < 0) + return err; + + if (response.max_id > INT_MAX) + return -E2BIG; + + return response.max_id; +} + +static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id, + struct tegra_bpmp_clk_info *info) +{ + struct cmd_clk_get_all_info_response response; + struct tegra_bpmp_clk_message msg; + unsigned int i; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.cmd = CMD_CLK_GET_ALL_INFO; + msg.id = id; + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_clk_transfer(bpmp, &msg); + if (err < 0) + return err; + + strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN); + info->num_parents = response.num_parents; + + for (i = 0; i < info->num_parents; i++) + info->parents[i] = response.parents[i]; + + info->flags = response.flags; + + return 0; +} + +static void tegra_bpmp_clk_info_dump(struct tegra_bpmp *bpmp, + const char *level, + const struct tegra_bpmp_clk_info *info) +{ + const char *prefix = ""; + struct seq_buf buf; + unsigned int i; + char flags[64]; + + seq_buf_init(&buf, flags, sizeof(flags)); + + if (info->flags) + seq_buf_printf(&buf, "("); + + if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) { + seq_buf_printf(&buf, "%smux", prefix); + prefix = ", "; + } + + if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) { + seq_buf_printf(&buf, "%sfixed", prefix); + prefix = ", "; + } + + if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) { + seq_buf_printf(&buf, "%sroot", prefix); + prefix = ", "; + } + + if (info->flags) + seq_buf_printf(&buf, ")"); + + dev_printk(level, bpmp->dev, "%03u: %s\n", info->id, info->name); + dev_printk(level, bpmp->dev, " flags: %lx %s\n", info->flags, flags); + dev_printk(level, bpmp->dev, " parents: %u\n", info->num_parents); + + for (i = 0; i < info->num_parents; i++) + dev_printk(level, bpmp->dev, " %03u\n", info->parents[i]); +} + +static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp, + struct tegra_bpmp_clk_info **clocksp) +{ + struct tegra_bpmp_clk_info *clocks; + unsigned int max_id, id, count = 0; + unsigned int holes = 0; + int err; + + err = tegra_bpmp_clk_get_max_id(bpmp); + if (err < 0) + return err; + + max_id = err; + + dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id); + + clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL); + if (!clocks) + return -ENOMEM; + + for (id = 0; id <= max_id; id++) { + struct tegra_bpmp_clk_info *info = &clocks[count]; + + err = tegra_bpmp_clk_get_info(bpmp, id, info); + if (err < 0) { + dev_err(bpmp->dev, "failed to query clock %u: %d\n", + id, err); + continue; + } + + if (info->num_parents >= U8_MAX) { + dev_err(bpmp->dev, + "clock %u has too many parents (%u, max: %u)\n", + id, info->num_parents, U8_MAX); + continue; + } + + /* clock not exposed by BPMP */ + if (info->name[0] == '\0') { + holes++; + continue; + } + + info->id = id; + count++; + + if (TEGRA_BPMP_DUMP_CLOCK_INFO) + tegra_bpmp_clk_info_dump(bpmp, KERN_DEBUG, info); + } + + dev_dbg(bpmp->dev, "holes: %u\n", holes); + *clocksp = clocks; + + return count; +} + +static const struct tegra_bpmp_clk_info * +tegra_bpmp_clk_find(const struct tegra_bpmp_clk_info *clocks, + unsigned int num_clocks, unsigned int id) +{ + unsigned int i; + + for (i = 0; i < num_clocks; i++) + if (clocks[i].id == id) + return &clocks[i]; + + return NULL; +} + +static struct tegra_bpmp_clk * +tegra_bpmp_clk_register(struct tegra_bpmp *bpmp, + const struct tegra_bpmp_clk_info *info, + const struct tegra_bpmp_clk_info *clocks, + unsigned int num_clocks) +{ + struct tegra_bpmp_clk *clk; + struct clk_init_data init; + const char **parents; + unsigned int i; + int err; + + clk = devm_kzalloc(bpmp->dev, sizeof(*clk), GFP_KERNEL); + if (!clk) + return ERR_PTR(-ENOMEM); + + clk->id = info->id; + clk->bpmp = bpmp; + + clk->parents = devm_kcalloc(bpmp->dev, info->num_parents, + sizeof(*clk->parents), GFP_KERNEL); + if (!clk->parents) + return ERR_PTR(-ENOMEM); + + clk->num_parents = info->num_parents; + + /* hardware clock initialization */ + memset(&init, 0, sizeof(init)); + init.name = info->name; + clk->hw.init = &init; + + if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) { + if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) + init.ops = &tegra_bpmp_clk_mux_rate_ops; + else + init.ops = &tegra_bpmp_clk_mux_ops; + } else { + if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) + init.ops = &tegra_bpmp_clk_rate_ops; + else + init.ops = &tegra_bpmp_clk_gate_ops; + } + + init.num_parents = info->num_parents; + + parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL); + if (!parents) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < info->num_parents; i++) { + const struct tegra_bpmp_clk_info *parent; + + /* keep a private copy of the ID to parent index map */ + clk->parents[i] = info->parents[i]; + + parent = tegra_bpmp_clk_find(clocks, num_clocks, + info->parents[i]); + if (!parent) { + dev_err(bpmp->dev, "no parent %u found for %u\n", + info->parents[i], info->id); + continue; + } + + parents[i] = parent->name; + } + + init.parent_names = parents; + + err = devm_clk_hw_register(bpmp->dev, &clk->hw); + + kfree(parents); + + if (err < 0) + return ERR_PTR(err); + + return clk; +} + +static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp, + struct tegra_bpmp_clk_info *infos, + unsigned int count) +{ + struct tegra_bpmp_clk *clk; + unsigned int i; + + bpmp->num_clocks = count; + + bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(clk), GFP_KERNEL); + if (!bpmp->clocks) + return -ENOMEM; + + for (i = 0; i < count; i++) { + struct tegra_bpmp_clk_info *info = &infos[i]; + + clk = tegra_bpmp_clk_register(bpmp, info, infos, count); + if (IS_ERR(clk)) { + dev_err(bpmp->dev, + "failed to register clock %u (%s): %ld\n", + info->id, info->name, PTR_ERR(clk)); + continue; + } + + bpmp->clocks[i] = clk; + } + + return 0; +} + +static void tegra_bpmp_unregister_clocks(struct tegra_bpmp *bpmp) +{ + unsigned int i; + + for (i = 0; i < bpmp->num_clocks; i++) + clk_hw_unregister(&bpmp->clocks[i]->hw); +} + +static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec, + void *data) +{ + unsigned int id = clkspec->args[0], i; + struct tegra_bpmp *bpmp = data; + + for (i = 0; i < bpmp->num_clocks; i++) + if (bpmp->clocks[i]->id == id) + return &bpmp->clocks[i]->hw; + + return NULL; +} + +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp) +{ + struct tegra_bpmp_clk_info *clocks; + unsigned int count; + int err; + + err = tegra_bpmp_probe_clocks(bpmp, &clocks); + if (err < 0) + return err; + + count = err; + + dev_dbg(bpmp->dev, "%u clocks probed\n", count); + + err = tegra_bpmp_register_clocks(bpmp, clocks, count); + if (err < 0) + goto free; + + err = of_clk_add_hw_provider(bpmp->dev->of_node, + tegra_bpmp_clk_of_xlate, + bpmp); + if (err < 0) { + tegra_bpmp_unregister_clocks(bpmp); + goto free; + } + +free: + kfree(clocks); + return err; +} diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index b4e5de16e561..6bb87784a0d6 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -140,6 +140,35 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div) return true; } +static int _div_round_up(const struct clk_div_table *table, + unsigned long parent_rate, unsigned long rate) +{ + const struct clk_div_table *clkt; + int up = INT_MAX; + int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + + for (clkt = table; clkt->div; clkt++) { + if (clkt->div == div) + return clkt->div; + else if (clkt->div < div) + continue; + + if ((clkt->div - div) < (up - div)) + up = clkt->div; + } + + return up; +} + +static int _div_round(const struct clk_div_table *table, + unsigned long parent_rate, unsigned long rate) +{ + if (!table) + return DIV_ROUND_UP(parent_rate, rate); + + return _div_round_up(table, parent_rate, rate); +} + static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -155,7 +184,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { parent_rate = *best_parent_rate; - bestdiv = DIV_ROUND_UP(parent_rate, rate); + bestdiv = _div_round(divider->table, parent_rate, rate); bestdiv = bestdiv == 0 ? 1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; return bestdiv; diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index 0007218ce6a0..2cf386347f0c 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -90,11 +90,8 @@ static int uniphier_clk_probe(struct platform_device *pdev) dev_dbg(dev, "register %s (index=%d)\n", p->name, p->idx); hw = uniphier_clk_register(dev, regmap, p); - if (IS_ERR(hw)) { - dev_err(dev, "failed to register %s (error %ld)\n", - p->name, PTR_ERR(hw)); - return PTR_ERR(hw); - } + if (WARN(IS_ERR(hw), "failed to register %s", p->name)) + continue; if (p->idx >= 0) hw_data->hws[p->idx] = hw; diff --git a/drivers/clk/uniphier/clk-uniphier-cpugear.c b/drivers/clk/uniphier/clk-uniphier-cpugear.c index 9bff26e0cbb0..ec11f55594ad 100644 --- a/drivers/clk/uniphier/clk-uniphier-cpugear.c +++ b/drivers/clk/uniphier/clk-uniphier-cpugear.c @@ -14,7 +14,6 @@ */ #include <linux/clk-provider.h> -#include <linux/delay.h> #include <linux/device.h> #include <linux/regmap.h> diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index d049316c1c0f..c8027d909429 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -29,6 +29,15 @@ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 10), \ UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15) +#define UNIPHIER_SLD3_SYS_CLK_NAND(idx) \ + UNIPHIER_CLK_GATE("nand", (idx), NULL, 0x2104, 2) + +#define UNIPHIER_LD11_SYS_CLK_NAND(idx) \ + UNIPHIER_CLK_GATE("nand", (idx), NULL, 0x210c, 0) + +#define UNIPHIER_LD11_SYS_CLK_EMMC(idx) \ + UNIPHIER_CLK_GATE("emmc", (idx), NULL, 0x210c, 2) + #define UNIPHIER_SLD3_SYS_CLK_STDMAC(idx) \ UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x2104, 10) @@ -48,6 +57,7 @@ const struct uniphier_clk_data uniphier_sld3_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_SLD3_SYS_CLK_NAND(2), UNIPHIER_SLD3_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_SLD3_SYS_CLK_STDMAC(8), @@ -61,6 +71,7 @@ const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_SLD3_SYS_CLK_NAND(2), UNIPHIER_SLD3_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ @@ -74,6 +85,7 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32), + UNIPHIER_SLD3_SYS_CLK_NAND(2), UNIPHIER_SLD3_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */ @@ -89,6 +101,7 @@ const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 20), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_SLD3_SYS_CLK_NAND(2), UNIPHIER_SLD3_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ @@ -101,6 +114,7 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("dapll2", -1, "ref", 144, 125), /* 2949.12 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "dapll2", 1, 40), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), + UNIPHIER_SLD3_SYS_CLK_NAND(2), UNIPHIER_PRO5_SYS_CLK_SD, UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC */ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */ @@ -113,6 +127,7 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("spll", -1, "ref", 96, 1), /* 2400 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 27), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), + UNIPHIER_SLD3_SYS_CLK_NAND(2), UNIPHIER_PRO5_SYS_CLK_SD, UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, RLE */ /* GIO is always clock-enabled: no function for 0x2104 bit6 */ @@ -131,6 +146,9 @@ const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vspll", -1, "ref", 80, 1), /* 2000 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_LD11_SYS_CLK_NAND(2), + UNIPHIER_LD11_SYS_CLK_EMMC(4), + /* Index 5 reserved for eMMC PHY */ UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */ UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25), /* CPU gears */ @@ -156,6 +174,9 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vppll", -1, "ref", 504, 5), /* 2520 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_LD11_SYS_CLK_NAND(2), + UNIPHIER_LD11_SYS_CLK_EMMC(4), + /* Index 5 reserved for eMMC PHY */ UNIPHIER_LD20_SYS_CLK_SD, UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC */ /* GIO is always clock-enabled: no function for 0x210c bit5 */ diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c index a07c31e6f26d..2257d12ba988 100644 --- a/drivers/clk/ux500/abx500-clk.c +++ b/drivers/clk/ux500/abx500-clk.c @@ -10,20 +10,26 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500-sysctrl.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <dt-bindings/clock/ste-ab8500.h> #include "clk.h" +#define AB8500_NUM_CLKS 6 + +static struct clk *ab8500_clks[AB8500_NUM_CLKS]; +static struct clk_onecell_data ab8500_clk_data; + /* Clock definitions for ab8500 */ static int ab8500_reg_clks(struct device *dev) { int ret; struct clk *clk; - + struct device_node *np = dev->of_node; const char *intclk_parents[] = {"ab8500_sysclk", "ulpclk"}; u16 intclk_reg_sel[] = {0 , AB8500_SYSULPCLKCTRL1}; u8 intclk_reg_mask[] = {0 , AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK}; @@ -32,55 +38,52 @@ static int ab8500_reg_clks(struct device *dev) (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT) }; - dev_info(dev, "register clocks for ab850x\n"); - /* Enable SWAT */ ret = ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE); if (ret) return ret; - /* ab8500_sysclk */ - clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK, 0); - clk_register_clkdev(clk, "sysclk", "ab8500-usb.0"); - clk_register_clkdev(clk, "sysclk", "ab-iddet.0"); - clk_register_clkdev(clk, "sysclk", "snd-soc-mop500.0"); - clk_register_clkdev(clk, "sysclk", "shrm_bus"); - /* ab8500_sysclk2 */ clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk2", "ab8500_sysclk", AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ, 0, 0); - clk_register_clkdev(clk, "sysclk", "0-0070"); + ab8500_clks[AB8500_SYSCLK_BUF2] = clk; /* ab8500_sysclk3 */ clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk3", "ab8500_sysclk", AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ, 0, 0); - clk_register_clkdev(clk, "sysclk", "cg1960_core.0"); + ab8500_clks[AB8500_SYSCLK_BUF3] = clk; /* ab8500_sysclk4 */ clk = clk_reg_sysctrl_gate(dev , "ab8500_sysclk4", "ab8500_sysclk", AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ, 0, 0); + ab8500_clks[AB8500_SYSCLK_BUF4] = clk; /* ab_ulpclk */ clk = clk_reg_sysctrl_gate_fixed_rate(dev, "ulpclk", NULL, AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, 38400000, 9000, 0); - clk_register_clkdev(clk, "ulpclk", "snd-soc-mop500.0"); + ab8500_clks[AB8500_SYSCLK_ULP] = clk; /* ab8500_intclk */ clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2, intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0); - clk_register_clkdev(clk, "intclk", "snd-soc-mop500.0"); - clk_register_clkdev(clk, NULL, "ab8500-pwm.1"); + ab8500_clks[AB8500_SYSCLK_INT] = clk; /* ab8500_audioclk */ clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk", AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0); - clk_register_clkdev(clk, "audioclk", "ab8500-codec.0"); + ab8500_clks[AB8500_SYSCLK_AUDIO] = clk; + + ab8500_clk_data.clks = ab8500_clks; + ab8500_clk_data.clk_num = ARRAY_SIZE(ab8500_clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &ab8500_clk_data); + + dev_info(dev, "registered clocks for ab850x\n"); return 0; } @@ -116,9 +119,15 @@ static int abx500_clk_probe(struct platform_device *pdev) return ret; } +static const struct of_device_id abx500_clk_match[] = { + { .compatible = "stericsson,ab8500-clk", }, + {} +}; + static struct platform_driver abx500_clk_driver = { .driver = { .name = "abx500-clk", + .of_match_table = abx500_clk_match, }, .probe = abx500_clk_probe, }; @@ -127,7 +136,6 @@ static int __init abx500_clk_init(void) { return platform_driver_register(&abx500_clk_driver); } - arch_initcall(abx500_clk_init); MODULE_AUTHOR("Ulf Hansson <ulf.hansson@linaro.org"); diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index e960d686d9db..d5888591e1a9 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -206,6 +206,9 @@ static void u8500_clk_init(struct device_node *np) clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, 0); prcmu_clk[PRCMU_TIMCLK] = clk; + clk = clk_reg_prcmu_gate("ab8500_sysclk", NULL, PRCMU_SYSCLK, 0); + prcmu_clk[PRCMU_SYSCLK] = clk; + clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, PRCMU_SDMMCCLK, 100000000, CLK_SET_RATE_GATE); prcmu_clk[PRCMU_SDMMCCLK] = clk; diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile index 04781389d0fb..1367afb03858 100644 --- a/drivers/clk/x86/Makefile +++ b/drivers/clk/x86/Makefile @@ -1,2 +1,3 @@ clk-x86-lpss-objs := clk-lpt.o obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o +obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c new file mode 100644 index 000000000000..2b60577703ef --- /dev/null +++ b/drivers/clk/x86/clk-pmc-atom.c @@ -0,0 +1,371 @@ +/* + * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs + * + * Copyright (C) 2016, Intel Corporation + * Author: Irina Tirdea <irina.tirdea@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/platform_data/x86/clk-pmc-atom.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define PLT_CLK_NAME_BASE "pmc_plt_clk" + +#define PMC_CLK_CTL_OFFSET 0x60 +#define PMC_CLK_CTL_SIZE 4 +#define PMC_CLK_NUM 6 +#define PMC_CLK_CTL_GATED_ON_D3 0x0 +#define PMC_CLK_CTL_FORCE_ON 0x1 +#define PMC_CLK_CTL_FORCE_OFF 0x2 +#define PMC_CLK_CTL_RESERVED 0x3 +#define PMC_MASK_CLK_CTL GENMASK(1, 0) +#define PMC_MASK_CLK_FREQ BIT(2) +#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */ +#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */ + +struct clk_plt_fixed { + struct clk_hw *clk; + struct clk_lookup *lookup; +}; + +struct clk_plt { + struct clk_hw hw; + void __iomem *reg; + struct clk_lookup *lookup; + /* protect access to PMC registers */ + spinlock_t lock; +}; + +#define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw) + +struct clk_plt_data { + struct clk_plt_fixed **parents; + u8 nparents; + struct clk_plt *clks[PMC_CLK_NUM]; +}; + +/* Return an index in parent table */ +static inline int plt_reg_to_parent(int reg) +{ + switch (reg & PMC_MASK_CLK_FREQ) { + default: + case PMC_CLK_FREQ_XTAL: + return 0; + case PMC_CLK_FREQ_PLL: + return 1; + } +} + +/* Return clk index of parent */ +static inline int plt_parent_to_reg(int index) +{ + switch (index) { + default: + case 0: + return PMC_CLK_FREQ_XTAL; + case 1: + return PMC_CLK_FREQ_PLL; + } +} + +/* Abstract status in simpler enabled/disabled value */ +static inline int plt_reg_to_enabled(int reg) +{ + switch (reg & PMC_MASK_CLK_CTL) { + case PMC_CLK_CTL_GATED_ON_D3: + case PMC_CLK_CTL_FORCE_ON: + return 1; /* enabled */ + case PMC_CLK_CTL_FORCE_OFF: + case PMC_CLK_CTL_RESERVED: + default: + return 0; /* disabled */ + } +} + +static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val) +{ + u32 tmp; + unsigned long flags; + + spin_lock_irqsave(&clk->lock, flags); + + tmp = readl(clk->reg); + tmp = (tmp & ~mask) | (val & mask); + writel(tmp, clk->reg); + + spin_unlock_irqrestore(&clk->lock, flags); +} + +static int plt_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_plt *clk = to_clk_plt(hw); + + plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index)); + + return 0; +} + +static u8 plt_clk_get_parent(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + u32 value; + + value = readl(clk->reg); + + return plt_reg_to_parent(value); +} + +static int plt_clk_enable(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + + plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON); + + return 0; +} + +static void plt_clk_disable(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + + plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF); +} + +static int plt_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_plt *clk = to_clk_plt(hw); + u32 value; + + value = readl(clk->reg); + + return plt_reg_to_enabled(value); +} + +static const struct clk_ops plt_clk_ops = { + .enable = plt_clk_enable, + .disable = plt_clk_disable, + .is_enabled = plt_clk_is_enabled, + .get_parent = plt_clk_get_parent, + .set_parent = plt_clk_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; + +static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, + void __iomem *base, + const char **parent_names, + int num_parents) +{ + struct clk_plt *pclk; + struct clk_init_data init; + int ret; + + pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); + if (!pclk) + return ERR_PTR(-ENOMEM); + + init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id); + init.ops = &plt_clk_ops; + init.flags = 0; + init.parent_names = parent_names; + init.num_parents = num_parents; + + pclk->hw.init = &init; + pclk->reg = base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE; + spin_lock_init(&pclk->lock); + + ret = devm_clk_hw_register(&pdev->dev, &pclk->hw); + if (ret) { + pclk = ERR_PTR(ret); + goto err_free_init; + } + + pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL); + if (!pclk->lookup) { + pclk = ERR_PTR(-ENOMEM); + goto err_free_init; + } + +err_free_init: + kfree(init.name); + return pclk; +} + +static void plt_clk_unregister(struct clk_plt *pclk) +{ + clkdev_drop(pclk->lookup); +} + +static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev, + const char *name, + const char *parent_name, + unsigned long fixed_rate) +{ + struct clk_plt_fixed *pclk; + + pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); + if (!pclk) + return ERR_PTR(-ENOMEM); + + pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name, + 0, fixed_rate); + if (IS_ERR(pclk->clk)) + return ERR_CAST(pclk->clk); + + pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL); + if (!pclk->lookup) { + clk_hw_unregister_fixed_rate(pclk->clk); + return ERR_PTR(-ENOMEM); + } + + return pclk; +} + +static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk) +{ + clkdev_drop(pclk->lookup); + clk_hw_unregister_fixed_rate(pclk->clk); +} + +static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data, + unsigned int i) +{ + while (i--) + plt_clk_unregister_fixed_rate(data->parents[i]); +} + +static void plt_clk_free_parent_names_loop(const char **parent_names, + unsigned int i) +{ + while (i--) + kfree_const(parent_names[i]); + kfree(parent_names); +} + +static void plt_clk_unregister_loop(struct clk_plt_data *data, + unsigned int i) +{ + while (i--) + plt_clk_unregister(data->clks[i]); +} + +static const char **plt_clk_register_parents(struct platform_device *pdev, + struct clk_plt_data *data, + const struct pmc_clk *clks) +{ + const char **parent_names; + unsigned int i; + int err; + int nparents = 0; + + data->nparents = 0; + while (clks[nparents].name) + nparents++; + + data->parents = devm_kcalloc(&pdev->dev, nparents, + sizeof(*data->parents), GFP_KERNEL); + if (!data->parents) + return ERR_PTR(-ENOMEM); + + parent_names = kcalloc(nparents, sizeof(*parent_names), + GFP_KERNEL); + if (!parent_names) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < nparents; i++) { + data->parents[i] = + plt_clk_register_fixed_rate(pdev, clks[i].name, + clks[i].parent_name, + clks[i].freq); + if (IS_ERR(data->parents[i])) { + err = PTR_ERR(data->parents[i]); + goto err_unreg; + } + parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL); + } + + data->nparents = nparents; + return parent_names; + +err_unreg: + plt_clk_unregister_fixed_rate_loop(data, i); + plt_clk_free_parent_names_loop(parent_names, i); + return ERR_PTR(err); +} + +static void plt_clk_unregister_parents(struct clk_plt_data *data) +{ + plt_clk_unregister_fixed_rate_loop(data, data->nparents); +} + +static int plt_clk_probe(struct platform_device *pdev) +{ + const struct pmc_clk_data *pmc_data; + const char **parent_names; + struct clk_plt_data *data; + unsigned int i; + int err; + + pmc_data = dev_get_platdata(&pdev->dev); + if (!pmc_data || !pmc_data->clks) + return -EINVAL; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks); + if (IS_ERR(parent_names)) + return PTR_ERR(parent_names); + + for (i = 0; i < PMC_CLK_NUM; i++) { + data->clks[i] = plt_clk_register(pdev, i, pmc_data->base, + parent_names, data->nparents); + if (IS_ERR(data->clks[i])) { + err = PTR_ERR(data->clks[i]); + goto err_unreg_clk_plt; + } + } + + plt_clk_free_parent_names_loop(parent_names, data->nparents); + + platform_set_drvdata(pdev, data); + return 0; + +err_unreg_clk_plt: + plt_clk_unregister_loop(data, i); + plt_clk_unregister_parents(data); + plt_clk_free_parent_names_loop(parent_names, data->nparents); + return err; +} + +static int plt_clk_remove(struct platform_device *pdev) +{ + struct clk_plt_data *data; + + data = platform_get_drvdata(pdev); + + plt_clk_unregister_loop(data, PMC_CLK_NUM); + plt_clk_unregister_parents(data); + return 0; +} + +static struct platform_driver plt_clk_driver = { + .driver = { + .name = "clk-pmc-atom", + }, + .probe = plt_clk_probe, + .remove = plt_clk_remove, +}; +builtin_platform_driver(plt_clk_driver); diff --git a/drivers/clk/zte/clk-zx296718.c b/drivers/clk/zte/clk-zx296718.c index 707d62956e9b..2f7c668643fe 100644 --- a/drivers/clk/zte/clk-zx296718.c +++ b/drivers/clk/zte/clk-zx296718.c @@ -610,9 +610,12 @@ static int __init top_clocks_init(struct device_node *np) } } - if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &top_hw_onecell_data)) - panic("could not register clk provider\n"); - pr_info("top clk init over, nr:%d\n", TOP_NR_CLKS); + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + &top_hw_onecell_data); + if (ret) { + pr_err("failed to register top clk provider: %d\n", ret); + return ret; + } return 0; } @@ -776,9 +779,12 @@ static int __init lsp0_clocks_init(struct device_node *np) } } - if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp0_hw_onecell_data)) - panic("could not register clk provider\n"); - pr_info("lsp0-clk init over:%d\n", LSP0_NR_CLKS); + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + &lsp0_hw_onecell_data); + if (ret) { + pr_err("failed to register lsp0 clk provider: %d\n", ret); + return ret; + } return 0; } @@ -881,9 +887,142 @@ static int __init lsp1_clocks_init(struct device_node *np) } } - if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp1_hw_onecell_data)) - panic("could not register clk provider\n"); - pr_info("lsp1-clk init over, nr:%d\n", LSP1_NR_CLKS); + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + &lsp1_hw_onecell_data); + if (ret) { + pr_err("failed to register lsp1 clk provider: %d\n", ret); + return ret; + } + + return 0; +} + +PNAME(audio_wclk_common_p) = { + "audio_99m", + "audio_24m", +}; + +PNAME(audio_timer_p) = { + "audio_24m", + "audio_32k", +}; + +static struct zx_clk_mux audio_mux_clk[] = { + MUX(0, "i2s0_wclk_mux", audio_wclk_common_p, AUDIO_I2S0_CLK, 0, 1), + MUX(0, "i2s1_wclk_mux", audio_wclk_common_p, AUDIO_I2S1_CLK, 0, 1), + MUX(0, "i2s2_wclk_mux", audio_wclk_common_p, AUDIO_I2S2_CLK, 0, 1), + MUX(0, "i2s3_wclk_mux", audio_wclk_common_p, AUDIO_I2S3_CLK, 0, 1), + MUX(0, "i2c0_wclk_mux", audio_wclk_common_p, AUDIO_I2C0_CLK, 0, 1), + MUX(0, "spdif0_wclk_mux", audio_wclk_common_p, AUDIO_SPDIF0_CLK, 0, 1), + MUX(0, "spdif1_wclk_mux", audio_wclk_common_p, AUDIO_SPDIF1_CLK, 0, 1), + MUX(0, "timer_wclk_mux", audio_timer_p, AUDIO_TIMER_CLK, 0, 1), +}; + +static struct clk_zx_audio_divider audio_adiv_clk[] = { + AUDIO_DIV(0, "i2s0_wclk_div", "i2s0_wclk_mux", AUDIO_I2S0_DIV_CFG1), + AUDIO_DIV(0, "i2s1_wclk_div", "i2s1_wclk_mux", AUDIO_I2S1_DIV_CFG1), + AUDIO_DIV(0, "i2s2_wclk_div", "i2s2_wclk_mux", AUDIO_I2S2_DIV_CFG1), + AUDIO_DIV(0, "i2s3_wclk_div", "i2s3_wclk_mux", AUDIO_I2S3_DIV_CFG1), + AUDIO_DIV(0, "spdif0_wclk_div", "spdif0_wclk_mux", AUDIO_SPDIF0_DIV_CFG1), + AUDIO_DIV(0, "spdif1_wclk_div", "spdif1_wclk_mux", AUDIO_SPDIF1_DIV_CFG1), +}; + +static struct zx_clk_div audio_div_clk[] = { + DIV_T(0, "tdm_wclk_div", "audio_16m384", AUDIO_TDM_CLK, 8, 4, 0, common_div_table), +}; + +static struct zx_clk_gate audio_gate_clk[] = { + GATE(AUDIO_I2S0_WCLK, "i2s0_wclk", "i2s0_wclk_div", AUDIO_I2S0_CLK, 9, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_I2S1_WCLK, "i2s1_wclk", "i2s1_wclk_div", AUDIO_I2S1_CLK, 9, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_I2S2_WCLK, "i2s2_wclk", "i2s2_wclk_div", AUDIO_I2S2_CLK, 9, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_I2S3_WCLK, "i2s3_wclk", "i2s3_wclk_div", AUDIO_I2S3_CLK, 9, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_I2S0_PCLK, "i2s0_pclk", "clk49m5", AUDIO_I2S0_CLK, 8, 0, 0), + GATE(AUDIO_I2S1_PCLK, "i2s1_pclk", "clk49m5", AUDIO_I2S1_CLK, 8, 0, 0), + GATE(AUDIO_I2S2_PCLK, "i2s2_pclk", "clk49m5", AUDIO_I2S2_CLK, 8, 0, 0), + GATE(AUDIO_I2S3_PCLK, "i2s3_pclk", "clk49m5", AUDIO_I2S3_CLK, 8, 0, 0), + GATE(AUDIO_I2C0_WCLK, "i2c0_wclk", "i2c0_wclk_mux", AUDIO_I2C0_CLK, 9, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_SPDIF0_WCLK, "spdif0_wclk", "spdif0_wclk_div", AUDIO_SPDIF0_CLK, 9, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_SPDIF1_WCLK, "spdif1_wclk", "spdif1_wclk_div", AUDIO_SPDIF1_CLK, 9, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_TDM_WCLK, "tdm_wclk", "tdm_wclk_div", AUDIO_TDM_CLK, 17, CLK_SET_RATE_PARENT, 0), + GATE(AUDIO_TS_PCLK, "tempsensor_pclk", "clk49m5", AUDIO_TS_CLK, 1, 0, 0), +}; + +static struct clk_hw_onecell_data audio_hw_onecell_data = { + .num = AUDIO_NR_CLKS, + .hws = { + [AUDIO_NR_CLKS - 1] = NULL, + }, +}; + +static int __init audio_clocks_init(struct device_node *np) +{ + void __iomem *reg_base; + int i, ret; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: Unable to map audio clk base\n", __func__); + return -ENXIO; + } + + for (i = 0; i < ARRAY_SIZE(audio_mux_clk); i++) { + if (audio_mux_clk[i].id) + audio_hw_onecell_data.hws[audio_mux_clk[i].id] = + &audio_mux_clk[i].mux.hw; + + audio_mux_clk[i].mux.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &audio_mux_clk[i].mux.hw); + if (ret) { + pr_warn("audio clk %s init error!\n", + audio_mux_clk[i].mux.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(audio_adiv_clk); i++) { + if (audio_adiv_clk[i].id) + audio_hw_onecell_data.hws[audio_adiv_clk[i].id] = + &audio_adiv_clk[i].hw; + + audio_adiv_clk[i].reg_base += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &audio_adiv_clk[i].hw); + if (ret) { + pr_warn("audio clk %s init error!\n", + audio_adiv_clk[i].hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(audio_div_clk); i++) { + if (audio_div_clk[i].id) + audio_hw_onecell_data.hws[audio_div_clk[i].id] = + &audio_div_clk[i].div.hw; + + audio_div_clk[i].div.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &audio_div_clk[i].div.hw); + if (ret) { + pr_warn("audio clk %s init error!\n", + audio_div_clk[i].div.hw.init->name); + } + } + + for (i = 0; i < ARRAY_SIZE(audio_gate_clk); i++) { + if (audio_gate_clk[i].id) + audio_hw_onecell_data.hws[audio_gate_clk[i].id] = + &audio_gate_clk[i].gate.hw; + + audio_gate_clk[i].gate.reg += (uintptr_t)reg_base; + ret = clk_hw_register(NULL, &audio_gate_clk[i].gate.hw); + if (ret) { + pr_warn("audio clk %s init error!\n", + audio_gate_clk[i].gate.hw.init->name); + } + } + + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + &audio_hw_onecell_data); + if (ret) { + pr_err("failed to register audio clk provider: %d\n", ret); + return ret; + } return 0; } @@ -892,6 +1031,7 @@ static const struct of_device_id zx_clkc_match_table[] = { { .compatible = "zte,zx296718-topcrm", .data = &top_clocks_init }, { .compatible = "zte,zx296718-lsp0crm", .data = &lsp0_clocks_init }, { .compatible = "zte,zx296718-lsp1crm", .data = &lsp1_clocks_init }, + { .compatible = "zte,zx296718-audiocrm", .data = &audio_clocks_init }, { } }; diff --git a/drivers/clk/zte/clk.c b/drivers/clk/zte/clk.c index c4c1251bc1e7..878d879b23ff 100644 --- a/drivers/clk/zte/clk.c +++ b/drivers/clk/zte/clk.c @@ -9,6 +9,7 @@ #include <linux/clk-provider.h> #include <linux/err.h> +#include <linux/gcd.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/slab.h> @@ -310,3 +311,129 @@ struct clk *clk_register_zx_audio(const char *name, return clk; } + +#define CLK_AUDIO_DIV_FRAC BIT(0) +#define CLK_AUDIO_DIV_INT BIT(1) +#define CLK_AUDIO_DIV_UNCOMMON BIT(1) + +#define CLK_AUDIO_DIV_FRAC_NSHIFT 16 +#define CLK_AUDIO_DIV_INT_FRAC_RE BIT(16) +#define CLK_AUDIO_DIV_INT_FRAC_MAX (0xffff) +#define CLK_AUDIO_DIV_INT_FRAC_MIN (0x2) +#define CLK_AUDIO_DIV_INT_INT_SHIFT 24 +#define CLK_AUDIO_DIV_INT_INT_WIDTH 4 + +struct zx_clk_audio_div_table { + unsigned long rate; + unsigned int int_reg; + unsigned int frac_reg; +}; + +#define to_clk_zx_audio_div(_hw) container_of(_hw, struct clk_zx_audio_divider, hw) + +static unsigned long audio_calc_rate(struct clk_zx_audio_divider *audio_div, + u32 reg_frac, u32 reg_int, + unsigned long parent_rate) +{ + unsigned long rate, m, n; + + m = reg_frac & 0xffff; + n = (reg_frac >> 16) & 0xffff; + + m = (reg_int & 0xffff) * n + m; + rate = (parent_rate * n) / m; + + return rate; +} + +static void audio_calc_reg(struct clk_zx_audio_divider *audio_div, + struct zx_clk_audio_div_table *div_table, + unsigned long rate, unsigned long parent_rate) +{ + unsigned int reg_int, reg_frac; + unsigned long m, n, div; + + reg_int = parent_rate / rate; + + if (reg_int > CLK_AUDIO_DIV_INT_FRAC_MAX) + reg_int = CLK_AUDIO_DIV_INT_FRAC_MAX; + else if (reg_int < CLK_AUDIO_DIV_INT_FRAC_MIN) + reg_int = 0; + m = parent_rate - rate * reg_int; + n = rate; + + div = gcd(m, n); + m = m / div; + n = n / div; + + if ((m >> 16) || (n >> 16)) { + if (m > n) { + n = n * 0xffff / m; + m = 0xffff; + } else { + m = m * 0xffff / n; + n = 0xffff; + } + } + reg_frac = m | (n << 16); + + div_table->rate = parent_rate * n / (reg_int * n + m); + div_table->int_reg = reg_int; + div_table->frac_reg = reg_frac; +} + +static unsigned long zx_audio_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw); + u32 reg_frac, reg_int; + + reg_frac = readl_relaxed(zx_audio_div->reg_base); + reg_int = readl_relaxed(zx_audio_div->reg_base + 0x4); + + return audio_calc_rate(zx_audio_div, reg_frac, reg_int, parent_rate); +} + +static long zx_audio_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw); + struct zx_clk_audio_div_table divt; + + audio_calc_reg(zx_audio_div, &divt, rate, *prate); + + return audio_calc_rate(zx_audio_div, divt.frac_reg, divt.int_reg, *prate); +} + +static int zx_audio_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_zx_audio_divider *zx_audio_div = to_clk_zx_audio_div(hw); + struct zx_clk_audio_div_table divt; + unsigned int val; + + audio_calc_reg(zx_audio_div, &divt, rate, parent_rate); + if (divt.rate != rate) + pr_debug("the real rate is:%ld", divt.rate); + + writel_relaxed(divt.frac_reg, zx_audio_div->reg_base); + + val = readl_relaxed(zx_audio_div->reg_base + 0x4); + val &= ~0xffff; + val |= divt.int_reg | CLK_AUDIO_DIV_INT_FRAC_RE; + writel_relaxed(val, zx_audio_div->reg_base + 0x4); + + mdelay(1); + + val = readl_relaxed(zx_audio_div->reg_base + 0x4); + val &= ~CLK_AUDIO_DIV_INT_FRAC_RE; + writel_relaxed(val, zx_audio_div->reg_base + 0x4); + + return 0; +} + +const struct clk_ops zx_audio_div_ops = { + .recalc_rate = zx_audio_div_recalc_rate, + .round_rate = zx_audio_div_round_rate, + .set_rate = zx_audio_div_set_rate, +}; diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h index 0df3474b2cf3..84a55a3e2bd4 100644 --- a/drivers/clk/zte/clk.h +++ b/drivers/clk/zte/clk.h @@ -153,6 +153,25 @@ struct zx_clk_div { .id = _id, \ } +struct clk_zx_audio_divider { + struct clk_hw hw; + void __iomem *reg_base; + unsigned int rate_count; + spinlock_t *lock; + u16 id; +}; + +#define AUDIO_DIV(_id, _name, _parent, _reg) \ +{ \ + .reg_base = (void __iomem *) _reg, \ + .lock = &clk_lock, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &zx_audio_div_ops, \ + 0), \ + .id = _id, \ +} + struct clk *clk_register_zx_pll(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg_base, const struct zx_pll_config *lookup_table, int count, spinlock_t *lock); @@ -167,4 +186,6 @@ struct clk *clk_register_zx_audio(const char *name, unsigned long flags, void __iomem *reg_base); extern const struct clk_ops zx_pll_ops; +extern const struct clk_ops zx_audio_div_ops; + #endif diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 93aa1364376a..7a8a4117f123 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -24,6 +24,7 @@ #include <linux/of_address.h> #include <linux/io.h> #include <linux/slab.h> +#include <linux/sched/clock.h> #include <linux/sched_clock.h> #include <linux/acpi.h> diff --git a/drivers/clocksource/pxa_timer.c b/drivers/clocksource/pxa_timer.c index 9cae38eebec2..1c24de215c14 100644 --- a/drivers/clocksource/pxa_timer.c +++ b/drivers/clocksource/pxa_timer.c @@ -19,6 +19,7 @@ #include <linux/clockchips.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/sched/clock.h> #include <linux/sched_clock.h> #include <clocksource/pxa.h> diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c index 10318cc99c0e..e9f50d289362 100644 --- a/drivers/clocksource/timer-digicolor.c +++ b/drivers/clocksource/timer-digicolor.c @@ -31,6 +31,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqreturn.h> +#include <linux/sched/clock.h> #include <linux/sched_clock.h> #include <linux/of.h> #include <linux/of_address.h> diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 631bd2c86c5e..47e24b5384b3 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -18,7 +18,6 @@ #include <linux/export.h> #include <linux/kernel_stat.h> -#include <linux/sched.h> #include <linux/slab.h> #include "cpufreq_governor.h" diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index f5717ca070cc..0236ec2cd654 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -20,6 +20,7 @@ #include <linux/atomic.h> #include <linux/irq_work.h> #include <linux/cpufreq.h> +#include <linux/sched/cpufreq.h> #include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/mutex.h> diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 4a017e895296..3937acf7e026 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -16,6 +16,7 @@ #include <linux/percpu-defs.h> #include <linux/slab.h> #include <linux/tick.h> +#include <linux/sched/cpufreq.h> #include "cpufreq_ondemand.h" diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index b95a872800ec..d54a27c99121 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -55,7 +55,7 @@ static int davinci_target(struct cpufreq_policy *policy, unsigned int idx) return ret; } - ret = clk_set_rate(armclk, idx); + ret = clk_set_rate(armclk, new_freq * 1000); if (ret) return ret; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index eb0f7fb71685..b1fbaa30ae04 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -19,7 +19,7 @@ #include <linux/hrtimer.h> #include <linux/tick.h> #include <linux/slab.h> -#include <linux/sched.h> +#include <linux/sched/cpufreq.h> #include <linux/list.h> #include <linux/cpu.h> #include <linux/cpufreq.h> @@ -39,11 +39,6 @@ #define INTEL_CPUFREQ_TRANSITION_LATENCY 20000 -#define ATOM_RATIOS 0x66a -#define ATOM_VIDS 0x66b -#define ATOM_TURBO_RATIOS 0x66c -#define ATOM_TURBO_VIDS 0x66d - #ifdef CONFIG_ACPI #include <acpi/processor.h> #include <acpi/cppc_acpi.h> @@ -364,37 +359,25 @@ static bool driver_registered __read_mostly; static bool acpi_ppc; #endif -static struct perf_limits performance_limits = { - .no_turbo = 0, - .turbo_disabled = 0, - .max_perf_pct = 100, - .max_perf = int_ext_tofp(1), - .min_perf_pct = 100, - .min_perf = int_ext_tofp(1), - .max_policy_pct = 100, - .max_sysfs_pct = 100, - .min_policy_pct = 0, - .min_sysfs_pct = 0, -}; +static struct perf_limits performance_limits; +static struct perf_limits powersave_limits; +static struct perf_limits *limits; -static struct perf_limits powersave_limits = { - .no_turbo = 0, - .turbo_disabled = 0, - .max_perf_pct = 100, - .max_perf = int_ext_tofp(1), - .min_perf_pct = 0, - .min_perf = 0, - .max_policy_pct = 100, - .max_sysfs_pct = 100, - .min_policy_pct = 0, - .min_sysfs_pct = 0, -}; +static void intel_pstate_init_limits(struct perf_limits *limits) +{ + memset(limits, 0, sizeof(*limits)); + limits->max_perf_pct = 100; + limits->max_perf = int_ext_tofp(1); + limits->max_policy_pct = 100; + limits->max_sysfs_pct = 100; +} -#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE -static struct perf_limits *limits = &performance_limits; -#else -static struct perf_limits *limits = &powersave_limits; -#endif +static void intel_pstate_set_performance_limits(struct perf_limits *limits) +{ + intel_pstate_init_limits(limits); + limits->min_perf_pct = 100; + limits->min_perf = int_ext_tofp(1); +} static DEFINE_MUTEX(intel_pstate_driver_lock); static DEFINE_MUTEX(intel_pstate_limits_lock); @@ -1367,7 +1350,7 @@ static int atom_get_min_pstate(void) { u64 value; - rdmsrl(ATOM_RATIOS, value); + rdmsrl(MSR_ATOM_CORE_RATIOS, value); return (value >> 8) & 0x7F; } @@ -1375,7 +1358,7 @@ static int atom_get_max_pstate(void) { u64 value; - rdmsrl(ATOM_RATIOS, value); + rdmsrl(MSR_ATOM_CORE_RATIOS, value); return (value >> 16) & 0x7F; } @@ -1383,7 +1366,7 @@ static int atom_get_turbo_pstate(void) { u64 value; - rdmsrl(ATOM_TURBO_RATIOS, value); + rdmsrl(MSR_ATOM_CORE_TURBO_RATIOS, value); return value & 0x7F; } @@ -1445,7 +1428,7 @@ static void atom_get_vid(struct cpudata *cpudata) { u64 value; - rdmsrl(ATOM_VIDS, value); + rdmsrl(MSR_ATOM_CORE_VIDS, value); cpudata->vid.min = int_tofp((value >> 8) & 0x7f); cpudata->vid.max = int_tofp((value >> 16) & 0x7f); cpudata->vid.ratio = div_fp( @@ -1453,7 +1436,7 @@ static void atom_get_vid(struct cpudata *cpudata) int_tofp(cpudata->pstate.max_pstate - cpudata->pstate.min_pstate)); - rdmsrl(ATOM_TURBO_VIDS, value); + rdmsrl(MSR_ATOM_CORE_TURBO_VIDS, value); cpudata->vid.turbo = value & 0x7f; } @@ -2084,20 +2067,6 @@ static void intel_pstate_clear_update_util_hook(unsigned int cpu) synchronize_sched(); } -static void intel_pstate_set_performance_limits(struct perf_limits *limits) -{ - limits->no_turbo = 0; - limits->turbo_disabled = 0; - limits->max_perf_pct = 100; - limits->max_perf = int_ext_tofp(1); - limits->min_perf_pct = 100; - limits->min_perf = int_ext_tofp(1); - limits->max_policy_pct = 100; - limits->max_sysfs_pct = 100; - limits->min_policy_pct = 0; - limits->min_sysfs_pct = 0; -} - static void intel_pstate_update_perf_limits(struct cpufreq_policy *policy, struct perf_limits *limits) { @@ -2466,6 +2435,11 @@ static int intel_pstate_register_driver(void) { int ret; + intel_pstate_init_limits(&powersave_limits); + intel_pstate_set_performance_limits(&performance_limits); + limits = IS_ENABLED(CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE) ? + &performance_limits : &powersave_limits; + ret = cpufreq_register_driver(intel_pstate_driver); if (ret) { intel_pstate_driver_cleanup(); diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index a6fefac8afe4..bfec1bcd3835 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -23,10 +23,6 @@ #include <linux/slab.h> #include <linux/smp.h> -#if !defined(CONFIG_ARM) -#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */ -#endif - /** * struct cpu_data * @pclk: the parent clock of cpu diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index b73feeb666f9..35ddb6da93aa 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -234,7 +234,7 @@ static unsigned int us2e_freq_get(unsigned int cpu) cpumask_t cpus_allowed; unsigned long clock_tick, estar; - cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); + cpumask_copy(&cpus_allowed, ¤t->cpus_allowed); set_cpus_allowed_ptr(current, cpumask_of(cpu)); clock_tick = sparc64_get_clock_tick(cpu) / 1000; @@ -252,7 +252,7 @@ static int us2e_freq_target(struct cpufreq_policy *policy, unsigned int index) unsigned long clock_tick, divisor, old_divisor, estar; cpumask_t cpus_allowed; - cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); + cpumask_copy(&cpus_allowed, ¤t->cpus_allowed); set_cpus_allowed_ptr(current, cpumask_of(cpu)); new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000; diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c index 9bb42ba50efa..a8d86a449ca1 100644 --- a/drivers/cpufreq/sparc-us3-cpufreq.c +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -82,7 +82,7 @@ static unsigned int us3_freq_get(unsigned int cpu) unsigned long reg; unsigned int ret; - cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); + cpumask_copy(&cpus_allowed, ¤t->cpus_allowed); set_cpus_allowed_ptr(current, cpumask_of(cpu)); reg = read_safari_cfg(); @@ -99,7 +99,7 @@ static int us3_freq_target(struct cpufreq_policy *policy, unsigned int index) unsigned long new_bits, new_freq, reg; cpumask_t cpus_allowed; - cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); + cpumask_copy(&cpus_allowed, ¤t->cpus_allowed); set_cpus_allowed_ptr(current, cpumask_of(cpu)); new_freq = sparc64_get_clock_tick(cpu) / 1000; diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 62810ff3b00f..548b90be7685 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/mutex.h> #include <linux/sched.h> +#include <linux/sched/clock.h> #include <linux/notifier.h> #include <linux/pm_qos.h> #include <linux/cpu.h> diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index ab264d393233..e53fb861beb0 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -11,6 +11,7 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/sched.h> +#include <linux/sched/idle.h> #include <linux/cpuidle.h> #include <linux/cpumask.h> #include <linux/tick.h> diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 8d6d25c38c02..b2330fd69e34 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -18,6 +18,8 @@ #include <linux/hrtimer.h> #include <linux/tick.h> #include <linux/sched.h> +#include <linux/sched/loadavg.h> +#include <linux/sched/stat.h> #include <linux/math64.h> #include <linux/cpu.h> @@ -287,7 +289,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) unsigned int interactivity_req; unsigned int expected_interval; unsigned long nr_iowaiters, cpu_load; - int resume_latency = dev_pm_qos_read_value(device); + int resume_latency = dev_pm_qos_raw_read_value(device); if (data->needs_update) { menu_update(drv, dev); diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 79564785ae30..473d31288ad8 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -62,19 +62,32 @@ config CRYPTO_DEV_GEODE will be called geode-aes. config ZCRYPT - tristate "Support for PCI-attached cryptographic adapters" + tristate "Support for s390 cryptographic adapters" depends on S390 select HW_RANDOM help - Select this option if you want to use a PCI-attached cryptographic - adapter like: - + PCI Cryptographic Accelerator (PCICA) - + PCI Cryptographic Coprocessor (PCICC) + Select this option if you want to enable support for + s390 cryptographic adapters like: + PCI-X Cryptographic Coprocessor (PCIXCC) - + Crypto Express2 Coprocessor (CEX2C) - + Crypto Express2 Accelerator (CEX2A) - + Crypto Express3 Coprocessor (CEX3C) - + Crypto Express3 Accelerator (CEX3A) + + Crypto Express 2,3,4 or 5 Coprocessor (CEXxC) + + Crypto Express 2,3,4 or 5 Accelerator (CEXxA) + + Crypto Express 4 or 5 EP11 Coprocessor (CEXxP) + +config PKEY + tristate "Kernel API for protected key handling" + depends on S390 + depends on ZCRYPT + help + With this option enabled the pkey kernel module provides an API + for creation and handling of protected keys. Other parts of the + kernel or userspace applications may use these functions. + + Select this option if you want to enable the kernel and userspace + API for proteced key handling. + + Please note that creation of protected keys from secure keys + requires to have at least one CEX card in coprocessor mode + available at runtime. config CRYPTO_SHA1_S390 tristate "SHA1 digest algorithm" @@ -124,6 +137,7 @@ config CRYPTO_AES_S390 depends on S390 select CRYPTO_ALGAPI select CRYPTO_BLKCIPHER + select PKEY help This is the s390 hardware accelerated implementation of the AES cipher algorithms (FIPS-197). @@ -339,7 +353,7 @@ config CRYPTO_DEV_OMAP_DES config CRYPTO_DEV_PICOXCELL tristate "Support for picoXcell IPSEC and Layer2 crypto engines" - depends on ARCH_PICOXCELL && HAVE_CLK + depends on (ARCH_PICOXCELL || COMPILE_TEST) && HAVE_CLK select CRYPTO_AEAD select CRYPTO_AES select CRYPTO_AUTHENC @@ -415,10 +429,23 @@ config CRYPTO_DEV_BFIN_CRC Newer Blackfin processors have CRC hardware. Select this if you want to use the Blackfin CRC module. +config CRYPTO_DEV_ATMEL_AUTHENC + tristate "Support for Atmel IPSEC/SSL hw accelerator" + depends on HAS_DMA + depends on ARCH_AT91 || COMPILE_TEST + select CRYPTO_AUTHENC + select CRYPTO_DEV_ATMEL_AES + select CRYPTO_DEV_ATMEL_SHA + help + Some Atmel processors can combine the AES and SHA hw accelerators + to enhance support of IPSEC/SSL. + Select this if you want to use the Atmel modules for + authenc(hmac(shaX),Y(cbc)) algorithms. + config CRYPTO_DEV_ATMEL_AES tristate "Support for Atmel AES hw accelerator" depends on HAS_DMA - depends on AT_XDMAC || AT_HDMAC || COMPILE_TEST + depends on ARCH_AT91 || COMPILE_TEST select CRYPTO_AES select CRYPTO_AEAD select CRYPTO_BLKCIPHER @@ -432,7 +459,8 @@ config CRYPTO_DEV_ATMEL_AES config CRYPTO_DEV_ATMEL_TDES tristate "Support for Atmel DES/TDES hw accelerator" - depends on ARCH_AT91 + depends on HAS_DMA + depends on ARCH_AT91 || COMPILE_TEST select CRYPTO_DES select CRYPTO_BLKCIPHER help @@ -445,7 +473,8 @@ config CRYPTO_DEV_ATMEL_TDES config CRYPTO_DEV_ATMEL_SHA tristate "Support for Atmel SHA hw accelerator" - depends on ARCH_AT91 + depends on HAS_DMA + depends on ARCH_AT91 || COMPILE_TEST select CRYPTO_HASH help Some Atmel processors have SHA1/SHA224/SHA256/SHA384/SHA512 @@ -484,6 +513,7 @@ config CRYPTO_DEV_MXS_DCP will be called mxs-dcp. source "drivers/crypto/qat/Kconfig" +source "drivers/crypto/cavium/cpt/Kconfig" config CRYPTO_DEV_QCE tristate "Qualcomm crypto engine accelerator" @@ -553,8 +583,40 @@ config CRYPTO_DEV_ROCKCHIP This driver interfaces with the hardware crypto accelerator. Supporting cbc/ecb chainmode, and aes/des/des3_ede cipher mode. +config CRYPTO_DEV_MEDIATEK + tristate "MediaTek's EIP97 Cryptographic Engine driver" + depends on HAS_DMA + depends on (ARM && ARCH_MEDIATEK) || COMPILE_TEST + select CRYPTO_AES + select CRYPTO_AEAD + select CRYPTO_BLKCIPHER + select CRYPTO_CTR + select CRYPTO_SHA1 + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_HMAC + help + This driver allows you to utilize the hardware crypto accelerator + EIP97 which can be found on the MT7623 MT2701, MT8521p, etc .... + Select this if you want to use it for AES/SHA1/SHA2 algorithms. + source "drivers/crypto/chelsio/Kconfig" source "drivers/crypto/virtio/Kconfig" +config CRYPTO_DEV_BCM_SPU + tristate "Broadcom symmetric crypto/hash acceleration support" + depends on ARCH_BCM_IPROC + depends on BCM_PDC_MBOX + default m + select CRYPTO_DES + select CRYPTO_MD5 + select CRYPTO_SHA1 + select CRYPTO_SHA256 + select CRYPTO_SHA512 + help + This driver provides support for Broadcom crypto acceleration using the + Secure Processing Unit (SPU). The SPU driver registers ablkcipher, + ahash, and aead algorithms with the kernel cryptographic API. + endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index bc53cb833a06..739609471169 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -3,6 +3,8 @@ obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA) += atmel-sha.o obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o obj-$(CONFIG_CRYPTO_DEV_BFIN_CRC) += bfin_crc.o obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/ +obj-$(CONFIG_CRYPTO_DEV_CHELSIO) += chelsio/ +obj-$(CONFIG_CRYPTO_DEV_CPT) += cavium/cpt/ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o @@ -10,7 +12,9 @@ obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/ +obj-$(CONFIG_CRYPTO_DEV_MEDIATEK) += mediatek/ obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o +obj-$(CONFIG_CRYPTO_DEV_MXC_SCC) += mxc-scc.o obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o n2_crypto-y := n2_core.o n2_asm.o obj-$(CONFIG_CRYPTO_DEV_NX) += nx/ @@ -21,15 +25,14 @@ obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/ +obj-$(CONFIG_CRYPTO_DEV_QAT) += qat/ +obj-$(CONFIG_CRYPTO_DEV_QCE) += qce/ +obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rockchip/ obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o obj-$(CONFIG_CRYPTO_DEV_SAHARA) += sahara.o -obj-$(CONFIG_CRYPTO_DEV_MXC_SCC) += mxc-scc.o +obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sunxi-ss/ obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ -obj-$(CONFIG_CRYPTO_DEV_QAT) += qat/ -obj-$(CONFIG_CRYPTO_DEV_QCE) += qce/ -obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/ -obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sunxi-ss/ -obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rockchip/ -obj-$(CONFIG_CRYPTO_DEV_CHELSIO) += chelsio/ obj-$(CONFIG_CRYPTO_DEV_VIRTIO) += virtio/ +obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/ +obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) += bcm/ diff --git a/drivers/crypto/atmel-aes-regs.h b/drivers/crypto/atmel-aes-regs.h index 0ec04407b533..7694679802b3 100644 --- a/drivers/crypto/atmel-aes-regs.h +++ b/drivers/crypto/atmel-aes-regs.h @@ -68,6 +68,22 @@ #define AES_CTRR 0x98 #define AES_GCMHR(x) (0x9c + ((x) * 0x04)) +#define AES_EMR 0xb0 +#define AES_EMR_APEN BIT(0) /* Auto Padding Enable */ +#define AES_EMR_APM BIT(1) /* Auto Padding Mode */ +#define AES_EMR_APM_IPSEC 0x0 +#define AES_EMR_APM_SSL BIT(1) +#define AES_EMR_PLIPEN BIT(4) /* PLIP Enable */ +#define AES_EMR_PLIPD BIT(5) /* PLIP Decipher */ +#define AES_EMR_PADLEN_MASK (0xFu << 8) +#define AES_EMR_PADLEN_OFFSET 8 +#define AES_EMR_PADLEN(padlen) (((padlen) << AES_EMR_PADLEN_OFFSET) &\ + AES_EMR_PADLEN_MASK) +#define AES_EMR_NHEAD_MASK (0xFu << 16) +#define AES_EMR_NHEAD_OFFSET 16 +#define AES_EMR_NHEAD(nhead) (((nhead) << AES_EMR_NHEAD_OFFSET) &\ + AES_EMR_NHEAD_MASK) + #define AES_TWR(x) (0xc0 + ((x) * 0x04)) #define AES_ALPHAR(x) (0xd0 + ((x) * 0x04)) diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c index 0e3d0d655b96..29e20c37f3a6 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -41,6 +41,7 @@ #include <linux/platform_data/crypto-atmel.h> #include <dt-bindings/dma/at91.h> #include "atmel-aes-regs.h" +#include "atmel-authenc.h" #define ATMEL_AES_PRIORITY 300 @@ -78,6 +79,7 @@ #define AES_FLAGS_INIT BIT(2) #define AES_FLAGS_BUSY BIT(3) #define AES_FLAGS_DUMP_REG BIT(4) +#define AES_FLAGS_OWN_SHA BIT(5) #define AES_FLAGS_PERSISTENT (AES_FLAGS_INIT | AES_FLAGS_BUSY) @@ -92,6 +94,7 @@ struct atmel_aes_caps { bool has_ctr32; bool has_gcm; bool has_xts; + bool has_authenc; u32 max_burst_size; }; @@ -144,10 +147,31 @@ struct atmel_aes_xts_ctx { u32 key2[AES_KEYSIZE_256 / sizeof(u32)]; }; +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC +struct atmel_aes_authenc_ctx { + struct atmel_aes_base_ctx base; + struct atmel_sha_authenc_ctx *auth; +}; +#endif + struct atmel_aes_reqctx { unsigned long mode; }; +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC +struct atmel_aes_authenc_reqctx { + struct atmel_aes_reqctx base; + + struct scatterlist src[2]; + struct scatterlist dst[2]; + size_t textlen; + u32 digest[SHA512_DIGEST_SIZE / sizeof(u32)]; + + /* auth_req MUST be place last. */ + struct ahash_request auth_req; +}; +#endif + struct atmel_aes_dma { struct dma_chan *chan; struct scatterlist *sg; @@ -291,6 +315,9 @@ static const char *atmel_aes_reg_name(u32 offset, char *tmp, size_t sz) snprintf(tmp, sz, "GCMHR[%u]", (offset - AES_GCMHR(0)) >> 2); break; + case AES_EMR: + return "EMR"; + case AES_TWR(0): case AES_TWR(1): case AES_TWR(2): @@ -463,8 +490,16 @@ static inline bool atmel_aes_is_encrypt(const struct atmel_aes_dev *dd) return (dd->flags & AES_FLAGS_ENCRYPT); } +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC +static void atmel_aes_authenc_complete(struct atmel_aes_dev *dd, int err); +#endif + static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err) { +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC + atmel_aes_authenc_complete(dd, err); +#endif + clk_disable(dd->iclk); dd->flags &= ~AES_FLAGS_BUSY; @@ -879,6 +914,7 @@ static int atmel_aes_handle_queue(struct atmel_aes_dev *dd, struct crypto_async_request *areq, *backlog; struct atmel_aes_base_ctx *ctx; unsigned long flags; + bool start_async; int err, ret = 0; spin_lock_irqsave(&dd->lock, flags); @@ -904,10 +940,12 @@ static int atmel_aes_handle_queue(struct atmel_aes_dev *dd, dd->areq = areq; dd->ctx = ctx; - dd->is_async = (areq != new_areq); + start_async = (areq != new_areq); + dd->is_async = start_async; + /* WARNING: ctx->start() MAY change dd->is_async. */ err = ctx->start(dd); - return (dd->is_async) ? ret : err; + return (start_async) ? ret : err; } @@ -1928,6 +1966,384 @@ static struct crypto_alg aes_xts_alg = { } }; +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC +/* authenc aead functions */ + +static int atmel_aes_authenc_start(struct atmel_aes_dev *dd); +static int atmel_aes_authenc_init(struct atmel_aes_dev *dd, int err, + bool is_async); +static int atmel_aes_authenc_transfer(struct atmel_aes_dev *dd, int err, + bool is_async); +static int atmel_aes_authenc_digest(struct atmel_aes_dev *dd); +static int atmel_aes_authenc_final(struct atmel_aes_dev *dd, int err, + bool is_async); + +static void atmel_aes_authenc_complete(struct atmel_aes_dev *dd, int err) +{ + struct aead_request *req = aead_request_cast(dd->areq); + struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); + + if (err && (dd->flags & AES_FLAGS_OWN_SHA)) + atmel_sha_authenc_abort(&rctx->auth_req); + dd->flags &= ~AES_FLAGS_OWN_SHA; +} + +static int atmel_aes_authenc_start(struct atmel_aes_dev *dd) +{ + struct aead_request *req = aead_request_cast(dd->areq); + struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm); + int err; + + atmel_aes_set_mode(dd, &rctx->base); + + err = atmel_aes_hw_init(dd); + if (err) + return atmel_aes_complete(dd, err); + + return atmel_sha_authenc_schedule(&rctx->auth_req, ctx->auth, + atmel_aes_authenc_init, dd); +} + +static int atmel_aes_authenc_init(struct atmel_aes_dev *dd, int err, + bool is_async) +{ + struct aead_request *req = aead_request_cast(dd->areq); + struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); + + if (is_async) + dd->is_async = true; + if (err) + return atmel_aes_complete(dd, err); + + /* If here, we've got the ownership of the SHA device. */ + dd->flags |= AES_FLAGS_OWN_SHA; + + /* Configure the SHA device. */ + return atmel_sha_authenc_init(&rctx->auth_req, + req->src, req->assoclen, + rctx->textlen, + atmel_aes_authenc_transfer, dd); +} + +static int atmel_aes_authenc_transfer(struct atmel_aes_dev *dd, int err, + bool is_async) +{ + struct aead_request *req = aead_request_cast(dd->areq); + struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); + bool enc = atmel_aes_is_encrypt(dd); + struct scatterlist *src, *dst; + u32 iv[AES_BLOCK_SIZE / sizeof(u32)]; + u32 emr; + + if (is_async) + dd->is_async = true; + if (err) + return atmel_aes_complete(dd, err); + + /* Prepare src and dst scatter-lists to transfer cipher/plain texts. */ + src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen); + dst = src; + + if (req->src != req->dst) + dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen); + + /* Configure the AES device. */ + memcpy(iv, req->iv, sizeof(iv)); + + /* + * Here we always set the 2nd parameter of atmel_aes_write_ctrl() to + * 'true' even if the data transfer is actually performed by the CPU (so + * not by the DMA) because we must force the AES_MR_SMOD bitfield to the + * value AES_MR_SMOD_IDATAR0. Indeed, both AES_MR_SMOD and SHA_MR_SMOD + * must be set to *_MR_SMOD_IDATAR0. + */ + atmel_aes_write_ctrl(dd, true, iv); + emr = AES_EMR_PLIPEN; + if (!enc) + emr |= AES_EMR_PLIPD; + atmel_aes_write(dd, AES_EMR, emr); + + /* Transfer data. */ + return atmel_aes_dma_start(dd, src, dst, rctx->textlen, + atmel_aes_authenc_digest); +} + +static int atmel_aes_authenc_digest(struct atmel_aes_dev *dd) +{ + struct aead_request *req = aead_request_cast(dd->areq); + struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); + + /* atmel_sha_authenc_final() releases the SHA device. */ + dd->flags &= ~AES_FLAGS_OWN_SHA; + return atmel_sha_authenc_final(&rctx->auth_req, + rctx->digest, sizeof(rctx->digest), + atmel_aes_authenc_final, dd); +} + +static int atmel_aes_authenc_final(struct atmel_aes_dev *dd, int err, + bool is_async) +{ + struct aead_request *req = aead_request_cast(dd->areq); + struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + bool enc = atmel_aes_is_encrypt(dd); + u32 idigest[SHA512_DIGEST_SIZE / sizeof(u32)], *odigest = rctx->digest; + u32 offs, authsize; + + if (is_async) + dd->is_async = true; + if (err) + goto complete; + + offs = req->assoclen + rctx->textlen; + authsize = crypto_aead_authsize(tfm); + if (enc) { + scatterwalk_map_and_copy(odigest, req->dst, offs, authsize, 1); + } else { + scatterwalk_map_and_copy(idigest, req->src, offs, authsize, 0); + if (crypto_memneq(idigest, odigest, authsize)) + err = -EBADMSG; + } + +complete: + return atmel_aes_complete(dd, err); +} + +static int atmel_aes_authenc_setkey(struct crypto_aead *tfm, const u8 *key, + unsigned int keylen) +{ + struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm); + struct crypto_authenc_keys keys; + u32 flags; + int err; + + if (crypto_authenc_extractkeys(&keys, key, keylen) != 0) + goto badkey; + + if (keys.enckeylen > sizeof(ctx->base.key)) + goto badkey; + + /* Save auth key. */ + flags = crypto_aead_get_flags(tfm); + err = atmel_sha_authenc_setkey(ctx->auth, + keys.authkey, keys.authkeylen, + &flags); + crypto_aead_set_flags(tfm, flags & CRYPTO_TFM_RES_MASK); + if (err) { + memzero_explicit(&keys, sizeof(keys)); + return err; + } + + /* Save enc key. */ + ctx->base.keylen = keys.enckeylen; + memcpy(ctx->base.key, keys.enckey, keys.enckeylen); + + memzero_explicit(&keys, sizeof(keys)); + return 0; + +badkey: + crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + memzero_explicit(&key, sizeof(keys)); + return -EINVAL; +} + +static int atmel_aes_authenc_init_tfm(struct crypto_aead *tfm, + unsigned long auth_mode) +{ + struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm); + unsigned int auth_reqsize = atmel_sha_authenc_get_reqsize(); + + ctx->auth = atmel_sha_authenc_spawn(auth_mode); + if (IS_ERR(ctx->auth)) + return PTR_ERR(ctx->auth); + + crypto_aead_set_reqsize(tfm, (sizeof(struct atmel_aes_authenc_reqctx) + + auth_reqsize)); + ctx->base.start = atmel_aes_authenc_start; + + return 0; +} + +static int atmel_aes_authenc_hmac_sha1_init_tfm(struct crypto_aead *tfm) +{ + return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA1); +} + +static int atmel_aes_authenc_hmac_sha224_init_tfm(struct crypto_aead *tfm) +{ + return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA224); +} + +static int atmel_aes_authenc_hmac_sha256_init_tfm(struct crypto_aead *tfm) +{ + return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA256); +} + +static int atmel_aes_authenc_hmac_sha384_init_tfm(struct crypto_aead *tfm) +{ + return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA384); +} + +static int atmel_aes_authenc_hmac_sha512_init_tfm(struct crypto_aead *tfm) +{ + return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA512); +} + +static void atmel_aes_authenc_exit_tfm(struct crypto_aead *tfm) +{ + struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm); + + atmel_sha_authenc_free(ctx->auth); +} + +static int atmel_aes_authenc_crypt(struct aead_request *req, + unsigned long mode) +{ + struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct atmel_aes_base_ctx *ctx = crypto_aead_ctx(tfm); + u32 authsize = crypto_aead_authsize(tfm); + bool enc = (mode & AES_FLAGS_ENCRYPT); + struct atmel_aes_dev *dd; + + /* Compute text length. */ + if (!enc && req->cryptlen < authsize) + return -EINVAL; + rctx->textlen = req->cryptlen - (enc ? 0 : authsize); + + /* + * Currently, empty messages are not supported yet: + * the SHA auto-padding can be used only on non-empty messages. + * Hence a special case needs to be implemented for empty message. + */ + if (!rctx->textlen && !req->assoclen) + return -EINVAL; + + rctx->base.mode = mode; + ctx->block_size = AES_BLOCK_SIZE; + + dd = atmel_aes_find_dev(ctx); + if (!dd) + return -ENODEV; + + return atmel_aes_handle_queue(dd, &req->base); +} + +static int atmel_aes_authenc_cbc_aes_encrypt(struct aead_request *req) +{ + return atmel_aes_authenc_crypt(req, AES_FLAGS_CBC | AES_FLAGS_ENCRYPT); +} + +static int atmel_aes_authenc_cbc_aes_decrypt(struct aead_request *req) +{ + return atmel_aes_authenc_crypt(req, AES_FLAGS_CBC); +} + +static struct aead_alg aes_authenc_algs[] = { +{ + .setkey = atmel_aes_authenc_setkey, + .encrypt = atmel_aes_authenc_cbc_aes_encrypt, + .decrypt = atmel_aes_authenc_cbc_aes_decrypt, + .init = atmel_aes_authenc_hmac_sha1_init_tfm, + .exit = atmel_aes_authenc_exit_tfm, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = SHA1_DIGEST_SIZE, + + .base = { + .cra_name = "authenc(hmac(sha1),cbc(aes))", + .cra_driver_name = "atmel-authenc-hmac-sha1-cbc-aes", + .cra_priority = ATMEL_AES_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_aes_authenc_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, +{ + .setkey = atmel_aes_authenc_setkey, + .encrypt = atmel_aes_authenc_cbc_aes_encrypt, + .decrypt = atmel_aes_authenc_cbc_aes_decrypt, + .init = atmel_aes_authenc_hmac_sha224_init_tfm, + .exit = atmel_aes_authenc_exit_tfm, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = SHA224_DIGEST_SIZE, + + .base = { + .cra_name = "authenc(hmac(sha224),cbc(aes))", + .cra_driver_name = "atmel-authenc-hmac-sha224-cbc-aes", + .cra_priority = ATMEL_AES_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_aes_authenc_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, +{ + .setkey = atmel_aes_authenc_setkey, + .encrypt = atmel_aes_authenc_cbc_aes_encrypt, + .decrypt = atmel_aes_authenc_cbc_aes_decrypt, + .init = atmel_aes_authenc_hmac_sha256_init_tfm, + .exit = atmel_aes_authenc_exit_tfm, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = SHA256_DIGEST_SIZE, + + .base = { + .cra_name = "authenc(hmac(sha256),cbc(aes))", + .cra_driver_name = "atmel-authenc-hmac-sha256-cbc-aes", + .cra_priority = ATMEL_AES_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_aes_authenc_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, +{ + .setkey = atmel_aes_authenc_setkey, + .encrypt = atmel_aes_authenc_cbc_aes_encrypt, + .decrypt = atmel_aes_authenc_cbc_aes_decrypt, + .init = atmel_aes_authenc_hmac_sha384_init_tfm, + .exit = atmel_aes_authenc_exit_tfm, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = SHA384_DIGEST_SIZE, + + .base = { + .cra_name = "authenc(hmac(sha384),cbc(aes))", + .cra_driver_name = "atmel-authenc-hmac-sha384-cbc-aes", + .cra_priority = ATMEL_AES_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_aes_authenc_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, +{ + .setkey = atmel_aes_authenc_setkey, + .encrypt = atmel_aes_authenc_cbc_aes_encrypt, + .decrypt = atmel_aes_authenc_cbc_aes_decrypt, + .init = atmel_aes_authenc_hmac_sha512_init_tfm, + .exit = atmel_aes_authenc_exit_tfm, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = SHA512_DIGEST_SIZE, + + .base = { + .cra_name = "authenc(hmac(sha512),cbc(aes))", + .cra_driver_name = "atmel-authenc-hmac-sha512-cbc-aes", + .cra_priority = ATMEL_AES_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_aes_authenc_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}, +}; +#endif /* CONFIG_CRYPTO_DEV_ATMEL_AUTHENC */ /* Probe functions */ @@ -2037,6 +2453,12 @@ static void atmel_aes_unregister_algs(struct atmel_aes_dev *dd) { int i; +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC + if (dd->caps.has_authenc) + for (i = 0; i < ARRAY_SIZE(aes_authenc_algs); i++) + crypto_unregister_aead(&aes_authenc_algs[i]); +#endif + if (dd->caps.has_xts) crypto_unregister_alg(&aes_xts_alg); @@ -2078,8 +2500,25 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd) goto err_aes_xts_alg; } +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC + if (dd->caps.has_authenc) { + for (i = 0; i < ARRAY_SIZE(aes_authenc_algs); i++) { + err = crypto_register_aead(&aes_authenc_algs[i]); + if (err) + goto err_aes_authenc_alg; + } + } +#endif + return 0; +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC + /* i = ARRAY_SIZE(aes_authenc_algs); */ +err_aes_authenc_alg: + for (j = 0; j < i; j++) + crypto_unregister_aead(&aes_authenc_algs[j]); + crypto_unregister_alg(&aes_xts_alg); +#endif err_aes_xts_alg: crypto_unregister_aead(&aes_gcm_alg); err_aes_gcm_alg: @@ -2100,6 +2539,7 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) dd->caps.has_ctr32 = 0; dd->caps.has_gcm = 0; dd->caps.has_xts = 0; + dd->caps.has_authenc = 0; dd->caps.max_burst_size = 1; /* keep only major version number */ @@ -2110,6 +2550,7 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) dd->caps.has_ctr32 = 1; dd->caps.has_gcm = 1; dd->caps.has_xts = 1; + dd->caps.has_authenc = 1; dd->caps.max_burst_size = 4; break; case 0x200: @@ -2268,6 +2709,13 @@ static int atmel_aes_probe(struct platform_device *pdev) atmel_aes_get_cap(aes_dd); +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC + if (aes_dd->caps.has_authenc && !atmel_sha_authenc_is_ready()) { + err = -EPROBE_DEFER; + goto iclk_unprepare; + } +#endif + err = atmel_aes_buff_init(aes_dd); if (err) goto err_aes_buff; @@ -2304,7 +2752,8 @@ res_err: tasklet_kill(&aes_dd->done_task); tasklet_kill(&aes_dd->queue_task); aes_dd_err: - dev_err(dev, "initialization failed.\n"); + if (err != -EPROBE_DEFER) + dev_err(dev, "initialization failed.\n"); return err; } diff --git a/drivers/crypto/atmel-authenc.h b/drivers/crypto/atmel-authenc.h new file mode 100644 index 000000000000..2a60d1224143 --- /dev/null +++ b/drivers/crypto/atmel-authenc.h @@ -0,0 +1,64 @@ +/* + * API for Atmel Secure Protocol Layers Improved Performances (SPLIP) + * + * Copyright (C) 2016 Atmel Corporation + * + * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + * + * This driver is based on drivers/mtd/spi-nor/fsl-quadspi.c from Freescale. + */ + +#ifndef __ATMEL_AUTHENC_H__ +#define __ATMEL_AUTHENC_H__ + +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC + +#include <crypto/authenc.h> +#include <crypto/hash.h> +#include <crypto/sha.h> +#include "atmel-sha-regs.h" + +struct atmel_aes_dev; +typedef int (*atmel_aes_authenc_fn_t)(struct atmel_aes_dev *, int, bool); + +struct atmel_sha_authenc_ctx; + +bool atmel_sha_authenc_is_ready(void); +unsigned int atmel_sha_authenc_get_reqsize(void); + +struct atmel_sha_authenc_ctx *atmel_sha_authenc_spawn(unsigned long mode); +void atmel_sha_authenc_free(struct atmel_sha_authenc_ctx *auth); +int atmel_sha_authenc_setkey(struct atmel_sha_authenc_ctx *auth, + const u8 *key, unsigned int keylen, + u32 *flags); + +int atmel_sha_authenc_schedule(struct ahash_request *req, + struct atmel_sha_authenc_ctx *auth, + atmel_aes_authenc_fn_t cb, + struct atmel_aes_dev *dd); +int atmel_sha_authenc_init(struct ahash_request *req, + struct scatterlist *assoc, unsigned int assoclen, + unsigned int textlen, + atmel_aes_authenc_fn_t cb, + struct atmel_aes_dev *dd); +int atmel_sha_authenc_final(struct ahash_request *req, + u32 *digest, unsigned int digestlen, + atmel_aes_authenc_fn_t cb, + struct atmel_aes_dev *dd); +void atmel_sha_authenc_abort(struct ahash_request *req); + +#endif /* CONFIG_CRYPTO_DEV_ATMEL_AUTHENC */ + +#endif /* __ATMEL_AUTHENC_H__ */ diff --git a/drivers/crypto/atmel-sha-regs.h b/drivers/crypto/atmel-sha-regs.h index e08897109cab..1b0eba4a2706 100644 --- a/drivers/crypto/atmel-sha-regs.h +++ b/drivers/crypto/atmel-sha-regs.h @@ -16,16 +16,33 @@ #define SHA_MR_MODE_MANUAL 0x0 #define SHA_MR_MODE_AUTO 0x1 #define SHA_MR_MODE_PDC 0x2 +#define SHA_MR_MODE_IDATAR0 0x2 #define SHA_MR_PROCDLY (1 << 4) #define SHA_MR_UIHV (1 << 5) #define SHA_MR_UIEHV (1 << 6) +#define SHA_MR_ALGO_MASK GENMASK(10, 8) #define SHA_MR_ALGO_SHA1 (0 << 8) #define SHA_MR_ALGO_SHA256 (1 << 8) #define SHA_MR_ALGO_SHA384 (2 << 8) #define SHA_MR_ALGO_SHA512 (3 << 8) #define SHA_MR_ALGO_SHA224 (4 << 8) +#define SHA_MR_HMAC (1 << 11) #define SHA_MR_DUALBUFF (1 << 16) +#define SHA_FLAGS_ALGO_MASK SHA_MR_ALGO_MASK +#define SHA_FLAGS_SHA1 SHA_MR_ALGO_SHA1 +#define SHA_FLAGS_SHA256 SHA_MR_ALGO_SHA256 +#define SHA_FLAGS_SHA384 SHA_MR_ALGO_SHA384 +#define SHA_FLAGS_SHA512 SHA_MR_ALGO_SHA512 +#define SHA_FLAGS_SHA224 SHA_MR_ALGO_SHA224 +#define SHA_FLAGS_HMAC SHA_MR_HMAC +#define SHA_FLAGS_HMAC_SHA1 (SHA_FLAGS_HMAC | SHA_FLAGS_SHA1) +#define SHA_FLAGS_HMAC_SHA256 (SHA_FLAGS_HMAC | SHA_FLAGS_SHA256) +#define SHA_FLAGS_HMAC_SHA384 (SHA_FLAGS_HMAC | SHA_FLAGS_SHA384) +#define SHA_FLAGS_HMAC_SHA512 (SHA_FLAGS_HMAC | SHA_FLAGS_SHA512) +#define SHA_FLAGS_HMAC_SHA224 (SHA_FLAGS_HMAC | SHA_FLAGS_SHA224) +#define SHA_FLAGS_MODE_MASK (SHA_FLAGS_HMAC | SHA_FLAGS_ALGO_MASK) + #define SHA_IER 0x10 #define SHA_IDR 0x14 #define SHA_IMR 0x18 @@ -40,6 +57,9 @@ #define SHA_ISR_URAT_MR (0x2 << 12) #define SHA_ISR_URAT_WO (0x5 << 12) +#define SHA_MSR 0x20 +#define SHA_BCR 0x30 + #define SHA_HW_VERSION 0xFC #define SHA_TPR 0x108 diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 97e34799e077..a9482023d7d3 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -41,6 +41,7 @@ #include <crypto/internal/hash.h> #include <linux/platform_data/crypto-atmel.h> #include "atmel-sha-regs.h" +#include "atmel-authenc.h" /* SHA flags */ #define SHA_FLAGS_BUSY BIT(0) @@ -50,21 +51,22 @@ #define SHA_FLAGS_INIT BIT(4) #define SHA_FLAGS_CPU BIT(5) #define SHA_FLAGS_DMA_READY BIT(6) +#define SHA_FLAGS_DUMP_REG BIT(7) + +/* bits[11:8] are reserved. */ #define SHA_FLAGS_FINUP BIT(16) #define SHA_FLAGS_SG BIT(17) -#define SHA_FLAGS_ALGO_MASK GENMASK(22, 18) -#define SHA_FLAGS_SHA1 BIT(18) -#define SHA_FLAGS_SHA224 BIT(19) -#define SHA_FLAGS_SHA256 BIT(20) -#define SHA_FLAGS_SHA384 BIT(21) -#define SHA_FLAGS_SHA512 BIT(22) #define SHA_FLAGS_ERROR BIT(23) #define SHA_FLAGS_PAD BIT(24) #define SHA_FLAGS_RESTORE BIT(25) +#define SHA_FLAGS_IDATAR0 BIT(26) +#define SHA_FLAGS_WAIT_DATARDY BIT(27) +#define SHA_OP_INIT 0 #define SHA_OP_UPDATE 1 #define SHA_OP_FINAL 2 +#define SHA_OP_DIGEST 3 #define SHA_BUFFER_LEN (PAGE_SIZE / 16) @@ -76,6 +78,7 @@ struct atmel_sha_caps { bool has_sha224; bool has_sha_384_512; bool has_uihv; + bool has_hmac; }; struct atmel_sha_dev; @@ -101,12 +104,16 @@ struct atmel_sha_reqctx { unsigned int total; /* total request */ size_t block_size; + size_t hash_size; u8 buffer[SHA_BUFFER_LEN + SHA512_BLOCK_SIZE] __aligned(sizeof(u32)); }; +typedef int (*atmel_sha_fn_t)(struct atmel_sha_dev *); + struct atmel_sha_ctx { struct atmel_sha_dev *dd; + atmel_sha_fn_t start; unsigned long flags; }; @@ -116,6 +123,9 @@ struct atmel_sha_ctx { struct atmel_sha_dma { struct dma_chan *chan; struct dma_slave_config dma_conf; + struct scatterlist *sg; + int nents; + unsigned int last_sg_length; }; struct atmel_sha_dev { @@ -134,11 +144,17 @@ struct atmel_sha_dev { unsigned long flags; struct crypto_queue queue; struct ahash_request *req; + bool is_async; + bool force_complete; + atmel_sha_fn_t resume; + atmel_sha_fn_t cpu_transfer_complete; struct atmel_sha_dma dma_lch_in; struct atmel_sha_caps caps; + struct scatterlist tmp; + u32 hw_version; }; @@ -152,17 +168,140 @@ static struct atmel_sha_drv atmel_sha = { .lock = __SPIN_LOCK_UNLOCKED(atmel_sha.lock), }; +#ifdef VERBOSE_DEBUG +static const char *atmel_sha_reg_name(u32 offset, char *tmp, size_t sz, bool wr) +{ + switch (offset) { + case SHA_CR: + return "CR"; + + case SHA_MR: + return "MR"; + + case SHA_IER: + return "IER"; + + case SHA_IDR: + return "IDR"; + + case SHA_IMR: + return "IMR"; + + case SHA_ISR: + return "ISR"; + + case SHA_MSR: + return "MSR"; + + case SHA_BCR: + return "BCR"; + + case SHA_REG_DIN(0): + case SHA_REG_DIN(1): + case SHA_REG_DIN(2): + case SHA_REG_DIN(3): + case SHA_REG_DIN(4): + case SHA_REG_DIN(5): + case SHA_REG_DIN(6): + case SHA_REG_DIN(7): + case SHA_REG_DIN(8): + case SHA_REG_DIN(9): + case SHA_REG_DIN(10): + case SHA_REG_DIN(11): + case SHA_REG_DIN(12): + case SHA_REG_DIN(13): + case SHA_REG_DIN(14): + case SHA_REG_DIN(15): + snprintf(tmp, sz, "IDATAR[%u]", (offset - SHA_REG_DIN(0)) >> 2); + break; + + case SHA_REG_DIGEST(0): + case SHA_REG_DIGEST(1): + case SHA_REG_DIGEST(2): + case SHA_REG_DIGEST(3): + case SHA_REG_DIGEST(4): + case SHA_REG_DIGEST(5): + case SHA_REG_DIGEST(6): + case SHA_REG_DIGEST(7): + case SHA_REG_DIGEST(8): + case SHA_REG_DIGEST(9): + case SHA_REG_DIGEST(10): + case SHA_REG_DIGEST(11): + case SHA_REG_DIGEST(12): + case SHA_REG_DIGEST(13): + case SHA_REG_DIGEST(14): + case SHA_REG_DIGEST(15): + if (wr) + snprintf(tmp, sz, "IDATAR[%u]", + 16u + ((offset - SHA_REG_DIGEST(0)) >> 2)); + else + snprintf(tmp, sz, "ODATAR[%u]", + (offset - SHA_REG_DIGEST(0)) >> 2); + break; + + case SHA_HW_VERSION: + return "HWVER"; + + default: + snprintf(tmp, sz, "0x%02x", offset); + break; + } + + return tmp; +} + +#endif /* VERBOSE_DEBUG */ + static inline u32 atmel_sha_read(struct atmel_sha_dev *dd, u32 offset) { - return readl_relaxed(dd->io_base + offset); + u32 value = readl_relaxed(dd->io_base + offset); + +#ifdef VERBOSE_DEBUG + if (dd->flags & SHA_FLAGS_DUMP_REG) { + char tmp[16]; + + dev_vdbg(dd->dev, "read 0x%08x from %s\n", value, + atmel_sha_reg_name(offset, tmp, sizeof(tmp), false)); + } +#endif /* VERBOSE_DEBUG */ + + return value; } static inline void atmel_sha_write(struct atmel_sha_dev *dd, u32 offset, u32 value) { +#ifdef VERBOSE_DEBUG + if (dd->flags & SHA_FLAGS_DUMP_REG) { + char tmp[16]; + + dev_vdbg(dd->dev, "write 0x%08x into %s\n", value, + atmel_sha_reg_name(offset, tmp, sizeof(tmp), true)); + } +#endif /* VERBOSE_DEBUG */ + writel_relaxed(value, dd->io_base + offset); } +static inline int atmel_sha_complete(struct atmel_sha_dev *dd, int err) +{ + struct ahash_request *req = dd->req; + + dd->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL | SHA_FLAGS_CPU | + SHA_FLAGS_DMA_READY | SHA_FLAGS_OUTPUT_READY | + SHA_FLAGS_DUMP_REG); + + clk_disable(dd->iclk); + + if ((dd->is_async || dd->force_complete) && req->base.complete) + req->base.complete(&req->base, err); + + /* handle new request */ + tasklet_schedule(&dd->queue_task); + + return err; +} + static size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx) { size_t count; @@ -241,7 +380,9 @@ static void atmel_sha_fill_padding(struct atmel_sha_reqctx *ctx, int length) bits[1] = cpu_to_be64(size[0] << 3); bits[0] = cpu_to_be64(size[1] << 3 | size[0] >> 61); - if (ctx->flags & (SHA_FLAGS_SHA384 | SHA_FLAGS_SHA512)) { + switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { + case SHA_FLAGS_SHA384: + case SHA_FLAGS_SHA512: index = ctx->bufcnt & 0x7f; padlen = (index < 112) ? (112 - index) : ((128+112) - index); *(ctx->buffer + ctx->bufcnt) = 0x80; @@ -249,7 +390,9 @@ static void atmel_sha_fill_padding(struct atmel_sha_reqctx *ctx, int length) memcpy(ctx->buffer + ctx->bufcnt + padlen, bits, 16); ctx->bufcnt += padlen + 16; ctx->flags |= SHA_FLAGS_PAD; - } else { + break; + + default: index = ctx->bufcnt & 0x3f; padlen = (index < 56) ? (56 - index) : ((64+56) - index); *(ctx->buffer + ctx->bufcnt) = 0x80; @@ -257,14 +400,12 @@ static void atmel_sha_fill_padding(struct atmel_sha_reqctx *ctx, int length) memcpy(ctx->buffer + ctx->bufcnt + padlen, &bits[1], 8); ctx->bufcnt += padlen + 8; ctx->flags |= SHA_FLAGS_PAD; + break; } } -static int atmel_sha_init(struct ahash_request *req) +static struct atmel_sha_dev *atmel_sha_find_dev(struct atmel_sha_ctx *tctx) { - struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); - struct atmel_sha_ctx *tctx = crypto_ahash_ctx(tfm); - struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); struct atmel_sha_dev *dd = NULL; struct atmel_sha_dev *tmp; @@ -281,6 +422,16 @@ static int atmel_sha_init(struct ahash_request *req) spin_unlock_bh(&atmel_sha.lock); + return dd; +} + +static int atmel_sha_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_ctx *tctx = crypto_ahash_ctx(tfm); + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + struct atmel_sha_dev *dd = atmel_sha_find_dev(tctx); + ctx->dd = dd; ctx->flags = 0; @@ -397,6 +548,19 @@ static void atmel_sha_write_ctrl(struct atmel_sha_dev *dd, int dma) atmel_sha_write(dd, SHA_MR, valmr); } +static inline int atmel_sha_wait_for_data_ready(struct atmel_sha_dev *dd, + atmel_sha_fn_t resume) +{ + u32 isr = atmel_sha_read(dd, SHA_ISR); + + if (unlikely(isr & SHA_INT_DATARDY)) + return resume(dd); + + dd->resume = resume; + atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY); + return -EINPROGRESS; +} + static int atmel_sha_xmit_cpu(struct atmel_sha_dev *dd, const u8 *buf, size_t length, int final) { @@ -404,7 +568,7 @@ static int atmel_sha_xmit_cpu(struct atmel_sha_dev *dd, const u8 *buf, int count, len32; const u32 *buffer = (const u32 *)buf; - dev_dbg(dd->dev, "xmit_cpu: digcnt: 0x%llx 0x%llx, length: %d, final: %d\n", + dev_dbg(dd->dev, "xmit_cpu: digcnt: 0x%llx 0x%llx, length: %zd, final: %d\n", ctx->digcnt[1], ctx->digcnt[0], length, final); atmel_sha_write_ctrl(dd, 0); @@ -433,7 +597,7 @@ static int atmel_sha_xmit_pdc(struct atmel_sha_dev *dd, dma_addr_t dma_addr1, struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req); int len32; - dev_dbg(dd->dev, "xmit_pdc: digcnt: 0x%llx 0x%llx, length: %d, final: %d\n", + dev_dbg(dd->dev, "xmit_pdc: digcnt: 0x%llx 0x%llx, length: %zd, final: %d\n", ctx->digcnt[1], ctx->digcnt[0], length1, final); len32 = DIV_ROUND_UP(length1, sizeof(u32)); @@ -467,6 +631,8 @@ static void atmel_sha_dma_callback(void *data) { struct atmel_sha_dev *dd = data; + dd->is_async = true; + /* dma_lch_in - completed - wait DATRDY */ atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY); } @@ -478,7 +644,7 @@ static int atmel_sha_xmit_dma(struct atmel_sha_dev *dd, dma_addr_t dma_addr1, struct dma_async_tx_descriptor *in_desc; struct scatterlist sg[2]; - dev_dbg(dd->dev, "xmit_dma: digcnt: 0x%llx 0x%llx, length: %d, final: %d\n", + dev_dbg(dd->dev, "xmit_dma: digcnt: 0x%llx 0x%llx, length: %zd, final: %d\n", ctx->digcnt[1], ctx->digcnt[0], length1, final); dd->dma_lch_in.dma_conf.src_maxburst = 16; @@ -502,7 +668,7 @@ static int atmel_sha_xmit_dma(struct atmel_sha_dev *dd, dma_addr_t dma_addr1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } if (!in_desc) - return -EINVAL; + return atmel_sha_complete(dd, -EINVAL); in_desc->callback = atmel_sha_dma_callback; in_desc->callback_param = dd; @@ -557,9 +723,9 @@ static int atmel_sha_xmit_dma_map(struct atmel_sha_dev *dd, ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen + ctx->block_size, DMA_TO_DEVICE); if (dma_mapping_error(dd->dev, ctx->dma_addr)) { - dev_err(dd->dev, "dma %u bytes error\n", ctx->buflen + + dev_err(dd->dev, "dma %zu bytes error\n", ctx->buflen + ctx->block_size); - return -EINVAL; + return atmel_sha_complete(dd, -EINVAL); } ctx->flags &= ~SHA_FLAGS_SG; @@ -578,7 +744,7 @@ static int atmel_sha_update_dma_slow(struct atmel_sha_dev *dd) final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total; - dev_dbg(dd->dev, "slow: bufcnt: %u, digcnt: 0x%llx 0x%llx, final: %d\n", + dev_dbg(dd->dev, "slow: bufcnt: %zu, digcnt: 0x%llx 0x%llx, final: %d\n", ctx->bufcnt, ctx->digcnt[1], ctx->digcnt[0], final); if (final) @@ -606,7 +772,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd) if (ctx->bufcnt || ctx->offset) return atmel_sha_update_dma_slow(dd); - dev_dbg(dd->dev, "fast: digcnt: 0x%llx 0x%llx, bufcnt: %u, total: %u\n", + dev_dbg(dd->dev, "fast: digcnt: 0x%llx 0x%llx, bufcnt: %zd, total: %u\n", ctx->digcnt[1], ctx->digcnt[0], ctx->bufcnt, ctx->total); sg = ctx->sg; @@ -648,9 +814,9 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd) ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen + ctx->block_size, DMA_TO_DEVICE); if (dma_mapping_error(dd->dev, ctx->dma_addr)) { - dev_err(dd->dev, "dma %u bytes error\n", + dev_err(dd->dev, "dma %zu bytes error\n", ctx->buflen + ctx->block_size); - return -EINVAL; + return atmel_sha_complete(dd, -EINVAL); } if (length == 0) { @@ -664,7 +830,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd) if (!dma_map_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE)) { dev_err(dd->dev, "dma_map_sg error\n"); - return -EINVAL; + return atmel_sha_complete(dd, -EINVAL); } ctx->flags |= SHA_FLAGS_SG; @@ -678,7 +844,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd) if (!dma_map_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE)) { dev_err(dd->dev, "dma_map_sg error\n"); - return -EINVAL; + return atmel_sha_complete(dd, -EINVAL); } ctx->flags |= SHA_FLAGS_SG; @@ -796,16 +962,28 @@ static void atmel_sha_copy_ready_hash(struct ahash_request *req) if (!req->result) return; - if (ctx->flags & SHA_FLAGS_SHA1) + switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { + default: + case SHA_FLAGS_SHA1: memcpy(req->result, ctx->digest, SHA1_DIGEST_SIZE); - else if (ctx->flags & SHA_FLAGS_SHA224) + break; + + case SHA_FLAGS_SHA224: memcpy(req->result, ctx->digest, SHA224_DIGEST_SIZE); - else if (ctx->flags & SHA_FLAGS_SHA256) + break; + + case SHA_FLAGS_SHA256: memcpy(req->result, ctx->digest, SHA256_DIGEST_SIZE); - else if (ctx->flags & SHA_FLAGS_SHA384) + break; + + case SHA_FLAGS_SHA384: memcpy(req->result, ctx->digest, SHA384_DIGEST_SIZE); - else + break; + + case SHA_FLAGS_SHA512: memcpy(req->result, ctx->digest, SHA512_DIGEST_SIZE); + break; + } } static int atmel_sha_finish(struct ahash_request *req) @@ -816,7 +994,7 @@ static int atmel_sha_finish(struct ahash_request *req) if (ctx->digcnt[0] || ctx->digcnt[1]) atmel_sha_copy_ready_hash(req); - dev_dbg(dd->dev, "digcnt: 0x%llx 0x%llx, bufcnt: %d\n", ctx->digcnt[1], + dev_dbg(dd->dev, "digcnt: 0x%llx 0x%llx, bufcnt: %zd\n", ctx->digcnt[1], ctx->digcnt[0], ctx->bufcnt); return 0; @@ -836,16 +1014,7 @@ static void atmel_sha_finish_req(struct ahash_request *req, int err) } /* atomic operation is not needed here */ - dd->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL | SHA_FLAGS_CPU | - SHA_FLAGS_DMA_READY | SHA_FLAGS_OUTPUT_READY); - - clk_disable(dd->iclk); - - if (req->base.complete) - req->base.complete(&req->base, err); - - /* handle new request */ - tasklet_schedule(&dd->queue_task); + (void)atmel_sha_complete(dd, err); } static int atmel_sha_hw_init(struct atmel_sha_dev *dd) @@ -886,8 +1055,9 @@ static int atmel_sha_handle_queue(struct atmel_sha_dev *dd, struct ahash_request *req) { struct crypto_async_request *async_req, *backlog; - struct atmel_sha_reqctx *ctx; + struct atmel_sha_ctx *ctx; unsigned long flags; + bool start_async; int err = 0, ret = 0; spin_lock_irqsave(&dd->lock, flags); @@ -912,35 +1082,69 @@ static int atmel_sha_handle_queue(struct atmel_sha_dev *dd, if (backlog) backlog->complete(backlog, -EINPROGRESS); - req = ahash_request_cast(async_req); - dd->req = req; - ctx = ahash_request_ctx(req); + ctx = crypto_tfm_ctx(async_req->tfm); + + dd->req = ahash_request_cast(async_req); + start_async = (dd->req != req); + dd->is_async = start_async; + dd->force_complete = false; + + /* WARNING: ctx->start() MAY change dd->is_async. */ + err = ctx->start(dd); + return (start_async) ? ret : err; +} + +static int atmel_sha_done(struct atmel_sha_dev *dd); + +static int atmel_sha_start(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + int err; dev_dbg(dd->dev, "handling new req, op: %lu, nbytes: %d\n", ctx->op, req->nbytes); err = atmel_sha_hw_init(dd); - if (err) - goto err1; + return atmel_sha_complete(dd, err); + /* + * atmel_sha_update_req() and atmel_sha_final_req() can return either: + * -EINPROGRESS: the hardware is busy and the SHA driver will resume + * its job later in the done_task. + * This is the main path. + * + * 0: the SHA driver can continue its job then release the hardware + * later, if needed, with atmel_sha_finish_req(). + * This is the alternate path. + * + * < 0: an error has occurred so atmel_sha_complete(dd, err) has already + * been called, hence the hardware has been released. + * The SHA driver must stop its job without calling + * atmel_sha_finish_req(), otherwise atmel_sha_complete() would be + * called a second time. + * + * Please note that currently, atmel_sha_final_req() never returns 0. + */ + + dd->resume = atmel_sha_done; if (ctx->op == SHA_OP_UPDATE) { err = atmel_sha_update_req(dd); - if (err != -EINPROGRESS && (ctx->flags & SHA_FLAGS_FINUP)) + if (!err && (ctx->flags & SHA_FLAGS_FINUP)) /* no final() after finup() */ err = atmel_sha_final_req(dd); } else if (ctx->op == SHA_OP_FINAL) { err = atmel_sha_final_req(dd); } -err1: - if (err != -EINPROGRESS) + if (!err) /* done_task will not finish it, so do it here */ atmel_sha_finish_req(req, err); dev_dbg(dd->dev, "exit, err: %d\n", err); - return ret; + return err; } static int atmel_sha_enqueue(struct ahash_request *req, unsigned int op) @@ -1036,8 +1240,11 @@ static int atmel_sha_import(struct ahash_request *req, const void *in) static int atmel_sha_cra_init(struct crypto_tfm *tfm) { + struct atmel_sha_ctx *ctx = crypto_tfm_ctx(tfm); + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), sizeof(struct atmel_sha_reqctx)); + ctx->start = atmel_sha_start; return 0; } @@ -1176,9 +1383,8 @@ static void atmel_sha_queue_task(unsigned long data) atmel_sha_handle_queue(dd, NULL); } -static void atmel_sha_done_task(unsigned long data) +static int atmel_sha_done(struct atmel_sha_dev *dd) { - struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data; int err = 0; if (SHA_FLAGS_CPU & dd->flags) { @@ -1204,11 +1410,21 @@ static void atmel_sha_done_task(unsigned long data) goto finish; } } - return; + return err; finish: /* finish curent request */ atmel_sha_finish_req(dd->req, err); + + return err; +} + +static void atmel_sha_done_task(unsigned long data) +{ + struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data; + + dd->is_async = true; + (void)dd->resume(dd); } static irqreturn_t atmel_sha_irq(int irq, void *dev_id) @@ -1233,10 +1449,1104 @@ static irqreturn_t atmel_sha_irq(int irq, void *dev_id) return IRQ_NONE; } + +/* DMA transfer functions */ + +static bool atmel_sha_dma_check_aligned(struct atmel_sha_dev *dd, + struct scatterlist *sg, + size_t len) +{ + struct atmel_sha_dma *dma = &dd->dma_lch_in; + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + size_t bs = ctx->block_size; + int nents; + + for (nents = 0; sg; sg = sg_next(sg), ++nents) { + if (!IS_ALIGNED(sg->offset, sizeof(u32))) + return false; + + /* + * This is the last sg, the only one that is allowed to + * have an unaligned length. + */ + if (len <= sg->length) { + dma->nents = nents + 1; + dma->last_sg_length = sg->length; + sg->length = ALIGN(len, sizeof(u32)); + return true; + } + + /* All other sg lengths MUST be aligned to the block size. */ + if (!IS_ALIGNED(sg->length, bs)) + return false; + + len -= sg->length; + } + + return false; +} + +static void atmel_sha_dma_callback2(void *data) +{ + struct atmel_sha_dev *dd = data; + struct atmel_sha_dma *dma = &dd->dma_lch_in; + struct scatterlist *sg; + int nents; + + dmaengine_terminate_all(dma->chan); + dma_unmap_sg(dd->dev, dma->sg, dma->nents, DMA_TO_DEVICE); + + sg = dma->sg; + for (nents = 0; nents < dma->nents - 1; ++nents) + sg = sg_next(sg); + sg->length = dma->last_sg_length; + + dd->is_async = true; + (void)atmel_sha_wait_for_data_ready(dd, dd->resume); +} + +static int atmel_sha_dma_start(struct atmel_sha_dev *dd, + struct scatterlist *src, + size_t len, + atmel_sha_fn_t resume) +{ + struct atmel_sha_dma *dma = &dd->dma_lch_in; + struct dma_slave_config *config = &dma->dma_conf; + struct dma_chan *chan = dma->chan; + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + unsigned int sg_len; + int err; + + dd->resume = resume; + + /* + * dma->nents has already been initialized by + * atmel_sha_dma_check_aligned(). + */ + dma->sg = src; + sg_len = dma_map_sg(dd->dev, dma->sg, dma->nents, DMA_TO_DEVICE); + if (!sg_len) { + err = -ENOMEM; + goto exit; + } + + config->src_maxburst = 16; + config->dst_maxburst = 16; + err = dmaengine_slave_config(chan, config); + if (err) + goto unmap_sg; + + desc = dmaengine_prep_slave_sg(chan, dma->sg, sg_len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + err = -ENOMEM; + goto unmap_sg; + } + + desc->callback = atmel_sha_dma_callback2; + desc->callback_param = dd; + cookie = dmaengine_submit(desc); + err = dma_submit_error(cookie); + if (err) + goto unmap_sg; + + dma_async_issue_pending(chan); + + return -EINPROGRESS; + +unmap_sg: + dma_unmap_sg(dd->dev, dma->sg, dma->nents, DMA_TO_DEVICE); +exit: + return atmel_sha_complete(dd, err); +} + + +/* CPU transfer functions */ + +static int atmel_sha_cpu_transfer(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + const u32 *words = (const u32 *)ctx->buffer; + size_t i, num_words; + u32 isr, din, din_inc; + + din_inc = (ctx->flags & SHA_FLAGS_IDATAR0) ? 0 : 1; + for (;;) { + /* Write data into the Input Data Registers. */ + num_words = DIV_ROUND_UP(ctx->bufcnt, sizeof(u32)); + for (i = 0, din = 0; i < num_words; ++i, din += din_inc) + atmel_sha_write(dd, SHA_REG_DIN(din), words[i]); + + ctx->offset += ctx->bufcnt; + ctx->total -= ctx->bufcnt; + + if (!ctx->total) + break; + + /* + * Prepare next block: + * Fill ctx->buffer now with the next data to be written into + * IDATARx: it gives time for the SHA hardware to process + * the current data so the SHA_INT_DATARDY flag might be set + * in SHA_ISR when polling this register at the beginning of + * the next loop. + */ + ctx->bufcnt = min_t(size_t, ctx->block_size, ctx->total); + scatterwalk_map_and_copy(ctx->buffer, ctx->sg, + ctx->offset, ctx->bufcnt, 0); + + /* Wait for hardware to be ready again. */ + isr = atmel_sha_read(dd, SHA_ISR); + if (!(isr & SHA_INT_DATARDY)) { + /* Not ready yet. */ + dd->resume = atmel_sha_cpu_transfer; + atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY); + return -EINPROGRESS; + } + } + + if (unlikely(!(ctx->flags & SHA_FLAGS_WAIT_DATARDY))) + return dd->cpu_transfer_complete(dd); + + return atmel_sha_wait_for_data_ready(dd, dd->cpu_transfer_complete); +} + +static int atmel_sha_cpu_start(struct atmel_sha_dev *dd, + struct scatterlist *sg, + unsigned int len, + bool idatar0_only, + bool wait_data_ready, + atmel_sha_fn_t resume) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + + if (!len) + return resume(dd); + + ctx->flags &= ~(SHA_FLAGS_IDATAR0 | SHA_FLAGS_WAIT_DATARDY); + + if (idatar0_only) + ctx->flags |= SHA_FLAGS_IDATAR0; + + if (wait_data_ready) + ctx->flags |= SHA_FLAGS_WAIT_DATARDY; + + ctx->sg = sg; + ctx->total = len; + ctx->offset = 0; + + /* Prepare the first block to be written. */ + ctx->bufcnt = min_t(size_t, ctx->block_size, ctx->total); + scatterwalk_map_and_copy(ctx->buffer, ctx->sg, + ctx->offset, ctx->bufcnt, 0); + + dd->cpu_transfer_complete = resume; + return atmel_sha_cpu_transfer(dd); +} + +static int atmel_sha_cpu_hash(struct atmel_sha_dev *dd, + const void *data, unsigned int datalen, + bool auto_padding, + atmel_sha_fn_t resume) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + u32 msglen = (auto_padding) ? datalen : 0; + u32 mr = SHA_MR_MODE_AUTO; + + if (!(IS_ALIGNED(datalen, ctx->block_size) || auto_padding)) + return atmel_sha_complete(dd, -EINVAL); + + mr |= (ctx->flags & SHA_FLAGS_ALGO_MASK); + atmel_sha_write(dd, SHA_MR, mr); + atmel_sha_write(dd, SHA_MSR, msglen); + atmel_sha_write(dd, SHA_BCR, msglen); + atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); + + sg_init_one(&dd->tmp, data, datalen); + return atmel_sha_cpu_start(dd, &dd->tmp, datalen, false, true, resume); +} + + +/* hmac functions */ + +struct atmel_sha_hmac_key { + bool valid; + unsigned int keylen; + u8 buffer[SHA512_BLOCK_SIZE]; + u8 *keydup; +}; + +static inline void atmel_sha_hmac_key_init(struct atmel_sha_hmac_key *hkey) +{ + memset(hkey, 0, sizeof(*hkey)); +} + +static inline void atmel_sha_hmac_key_release(struct atmel_sha_hmac_key *hkey) +{ + kfree(hkey->keydup); + memset(hkey, 0, sizeof(*hkey)); +} + +static inline int atmel_sha_hmac_key_set(struct atmel_sha_hmac_key *hkey, + const u8 *key, + unsigned int keylen) +{ + atmel_sha_hmac_key_release(hkey); + + if (keylen > sizeof(hkey->buffer)) { + hkey->keydup = kmemdup(key, keylen, GFP_KERNEL); + if (!hkey->keydup) + return -ENOMEM; + + } else { + memcpy(hkey->buffer, key, keylen); + } + + hkey->valid = true; + hkey->keylen = keylen; + return 0; +} + +static inline bool atmel_sha_hmac_key_get(const struct atmel_sha_hmac_key *hkey, + const u8 **key, + unsigned int *keylen) +{ + if (!hkey->valid) + return false; + + *keylen = hkey->keylen; + *key = (hkey->keydup) ? hkey->keydup : hkey->buffer; + return true; +} + + +struct atmel_sha_hmac_ctx { + struct atmel_sha_ctx base; + + struct atmel_sha_hmac_key hkey; + u32 ipad[SHA512_BLOCK_SIZE / sizeof(u32)]; + u32 opad[SHA512_BLOCK_SIZE / sizeof(u32)]; + atmel_sha_fn_t resume; +}; + +static int atmel_sha_hmac_setup(struct atmel_sha_dev *dd, + atmel_sha_fn_t resume); +static int atmel_sha_hmac_prehash_key(struct atmel_sha_dev *dd, + const u8 *key, unsigned int keylen); +static int atmel_sha_hmac_prehash_key_done(struct atmel_sha_dev *dd); +static int atmel_sha_hmac_compute_ipad_hash(struct atmel_sha_dev *dd); +static int atmel_sha_hmac_compute_opad_hash(struct atmel_sha_dev *dd); +static int atmel_sha_hmac_setup_done(struct atmel_sha_dev *dd); + +static int atmel_sha_hmac_init_done(struct atmel_sha_dev *dd); +static int atmel_sha_hmac_final(struct atmel_sha_dev *dd); +static int atmel_sha_hmac_final_done(struct atmel_sha_dev *dd); +static int atmel_sha_hmac_digest2(struct atmel_sha_dev *dd); + +static int atmel_sha_hmac_setup(struct atmel_sha_dev *dd, + atmel_sha_fn_t resume) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + unsigned int keylen; + const u8 *key; + size_t bs; + + hmac->resume = resume; + switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { + case SHA_FLAGS_SHA1: + ctx->block_size = SHA1_BLOCK_SIZE; + ctx->hash_size = SHA1_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA224: + ctx->block_size = SHA224_BLOCK_SIZE; + ctx->hash_size = SHA256_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA256: + ctx->block_size = SHA256_BLOCK_SIZE; + ctx->hash_size = SHA256_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA384: + ctx->block_size = SHA384_BLOCK_SIZE; + ctx->hash_size = SHA512_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA512: + ctx->block_size = SHA512_BLOCK_SIZE; + ctx->hash_size = SHA512_DIGEST_SIZE; + break; + + default: + return atmel_sha_complete(dd, -EINVAL); + } + bs = ctx->block_size; + + if (likely(!atmel_sha_hmac_key_get(&hmac->hkey, &key, &keylen))) + return resume(dd); + + /* Compute K' from K. */ + if (unlikely(keylen > bs)) + return atmel_sha_hmac_prehash_key(dd, key, keylen); + + /* Prepare ipad. */ + memcpy((u8 *)hmac->ipad, key, keylen); + memset((u8 *)hmac->ipad + keylen, 0, bs - keylen); + return atmel_sha_hmac_compute_ipad_hash(dd); +} + +static int atmel_sha_hmac_prehash_key(struct atmel_sha_dev *dd, + const u8 *key, unsigned int keylen) +{ + return atmel_sha_cpu_hash(dd, key, keylen, true, + atmel_sha_hmac_prehash_key_done); +} + +static int atmel_sha_hmac_prehash_key_done(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + size_t ds = crypto_ahash_digestsize(tfm); + size_t bs = ctx->block_size; + size_t i, num_words = ds / sizeof(u32); + + /* Prepare ipad. */ + for (i = 0; i < num_words; ++i) + hmac->ipad[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); + memset((u8 *)hmac->ipad + ds, 0, bs - ds); + return atmel_sha_hmac_compute_ipad_hash(dd); +} + +static int atmel_sha_hmac_compute_ipad_hash(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + size_t bs = ctx->block_size; + size_t i, num_words = bs / sizeof(u32); + + memcpy(hmac->opad, hmac->ipad, bs); + for (i = 0; i < num_words; ++i) { + hmac->ipad[i] ^= 0x36363636; + hmac->opad[i] ^= 0x5c5c5c5c; + } + + return atmel_sha_cpu_hash(dd, hmac->ipad, bs, false, + atmel_sha_hmac_compute_opad_hash); +} + +static int atmel_sha_hmac_compute_opad_hash(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + size_t bs = ctx->block_size; + size_t hs = ctx->hash_size; + size_t i, num_words = hs / sizeof(u32); + + for (i = 0; i < num_words; ++i) + hmac->ipad[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); + return atmel_sha_cpu_hash(dd, hmac->opad, bs, false, + atmel_sha_hmac_setup_done); +} + +static int atmel_sha_hmac_setup_done(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + size_t hs = ctx->hash_size; + size_t i, num_words = hs / sizeof(u32); + + for (i = 0; i < num_words; ++i) + hmac->opad[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); + atmel_sha_hmac_key_release(&hmac->hkey); + return hmac->resume(dd); +} + +static int atmel_sha_hmac_start(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + int err; + + err = atmel_sha_hw_init(dd); + if (err) + return atmel_sha_complete(dd, err); + + switch (ctx->op) { + case SHA_OP_INIT: + err = atmel_sha_hmac_setup(dd, atmel_sha_hmac_init_done); + break; + + case SHA_OP_UPDATE: + dd->resume = atmel_sha_done; + err = atmel_sha_update_req(dd); + break; + + case SHA_OP_FINAL: + dd->resume = atmel_sha_hmac_final; + err = atmel_sha_final_req(dd); + break; + + case SHA_OP_DIGEST: + err = atmel_sha_hmac_setup(dd, atmel_sha_hmac_digest2); + break; + + default: + return atmel_sha_complete(dd, -EINVAL); + } + + return err; +} + +static int atmel_sha_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen) +{ + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + + if (atmel_sha_hmac_key_set(&hmac->hkey, key, keylen)) { + crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + return 0; +} + +static int atmel_sha_hmac_init(struct ahash_request *req) +{ + int err; + + err = atmel_sha_init(req); + if (err) + return err; + + return atmel_sha_enqueue(req, SHA_OP_INIT); +} + +static int atmel_sha_hmac_init_done(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + size_t bs = ctx->block_size; + size_t hs = ctx->hash_size; + + ctx->bufcnt = 0; + ctx->digcnt[0] = bs; + ctx->digcnt[1] = 0; + ctx->flags |= SHA_FLAGS_RESTORE; + memcpy(ctx->digest, hmac->ipad, hs); + return atmel_sha_complete(dd, 0); +} + +static int atmel_sha_hmac_final(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + u32 *digest = (u32 *)ctx->digest; + size_t ds = crypto_ahash_digestsize(tfm); + size_t bs = ctx->block_size; + size_t hs = ctx->hash_size; + size_t i, num_words; + u32 mr; + + /* Save d = SHA((K' + ipad) | msg). */ + num_words = ds / sizeof(u32); + for (i = 0; i < num_words; ++i) + digest[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); + + /* Restore context to finish computing SHA((K' + opad) | d). */ + atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV); + num_words = hs / sizeof(u32); + for (i = 0; i < num_words; ++i) + atmel_sha_write(dd, SHA_REG_DIN(i), hmac->opad[i]); + + mr = SHA_MR_MODE_AUTO | SHA_MR_UIHV; + mr |= (ctx->flags & SHA_FLAGS_ALGO_MASK); + atmel_sha_write(dd, SHA_MR, mr); + atmel_sha_write(dd, SHA_MSR, bs + ds); + atmel_sha_write(dd, SHA_BCR, ds); + atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); + + sg_init_one(&dd->tmp, digest, ds); + return atmel_sha_cpu_start(dd, &dd->tmp, ds, false, true, + atmel_sha_hmac_final_done); +} + +static int atmel_sha_hmac_final_done(struct atmel_sha_dev *dd) +{ + /* + * req->result might not be sizeof(u32) aligned, so copy the + * digest into ctx->digest[] before memcpy() the data into + * req->result. + */ + atmel_sha_copy_hash(dd->req); + atmel_sha_copy_ready_hash(dd->req); + return atmel_sha_complete(dd, 0); +} + +static int atmel_sha_hmac_digest(struct ahash_request *req) +{ + int err; + + err = atmel_sha_init(req); + if (err) + return err; + + return atmel_sha_enqueue(req, SHA_OP_DIGEST); +} + +static int atmel_sha_hmac_digest2(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_reqctx *ctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + size_t hs = ctx->hash_size; + size_t i, num_words = hs / sizeof(u32); + bool use_dma = false; + u32 mr; + + /* Special case for empty message. */ + if (!req->nbytes) + return atmel_sha_complete(dd, -EINVAL); // TODO: + + /* Check DMA threshold and alignment. */ + if (req->nbytes > ATMEL_SHA_DMA_THRESHOLD && + atmel_sha_dma_check_aligned(dd, req->src, req->nbytes)) + use_dma = true; + + /* Write both initial hash values to compute a HMAC. */ + atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV); + for (i = 0; i < num_words; ++i) + atmel_sha_write(dd, SHA_REG_DIN(i), hmac->ipad[i]); + + atmel_sha_write(dd, SHA_CR, SHA_CR_WUIEHV); + for (i = 0; i < num_words; ++i) + atmel_sha_write(dd, SHA_REG_DIN(i), hmac->opad[i]); + + /* Write the Mode, Message Size, Bytes Count then Control Registers. */ + mr = (SHA_MR_HMAC | SHA_MR_DUALBUFF); + mr |= ctx->flags & SHA_FLAGS_ALGO_MASK; + if (use_dma) + mr |= SHA_MR_MODE_IDATAR0; + else + mr |= SHA_MR_MODE_AUTO; + atmel_sha_write(dd, SHA_MR, mr); + + atmel_sha_write(dd, SHA_MSR, req->nbytes); + atmel_sha_write(dd, SHA_BCR, req->nbytes); + + atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); + + /* Process data. */ + if (use_dma) + return atmel_sha_dma_start(dd, req->src, req->nbytes, + atmel_sha_hmac_final_done); + + return atmel_sha_cpu_start(dd, req->src, req->nbytes, false, true, + atmel_sha_hmac_final_done); +} + +static int atmel_sha_hmac_cra_init(struct crypto_tfm *tfm) +{ + struct atmel_sha_hmac_ctx *hmac = crypto_tfm_ctx(tfm); + + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct atmel_sha_reqctx)); + hmac->base.start = atmel_sha_hmac_start; + atmel_sha_hmac_key_init(&hmac->hkey); + + return 0; +} + +static void atmel_sha_hmac_cra_exit(struct crypto_tfm *tfm) +{ + struct atmel_sha_hmac_ctx *hmac = crypto_tfm_ctx(tfm); + + atmel_sha_hmac_key_release(&hmac->hkey); +} + +static struct ahash_alg sha_hmac_algs[] = { +{ + .init = atmel_sha_hmac_init, + .update = atmel_sha_update, + .final = atmel_sha_final, + .digest = atmel_sha_hmac_digest, + .setkey = atmel_sha_hmac_setkey, + .export = atmel_sha_export, + .import = atmel_sha_import, + .halg = { + .digestsize = SHA1_DIGEST_SIZE, + .statesize = sizeof(struct atmel_sha_reqctx), + .base = { + .cra_name = "hmac(sha1)", + .cra_driver_name = "atmel-hmac-sha1", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_sha_hmac_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = atmel_sha_hmac_cra_init, + .cra_exit = atmel_sha_hmac_cra_exit, + } + } +}, +{ + .init = atmel_sha_hmac_init, + .update = atmel_sha_update, + .final = atmel_sha_final, + .digest = atmel_sha_hmac_digest, + .setkey = atmel_sha_hmac_setkey, + .export = atmel_sha_export, + .import = atmel_sha_import, + .halg = { + .digestsize = SHA224_DIGEST_SIZE, + .statesize = sizeof(struct atmel_sha_reqctx), + .base = { + .cra_name = "hmac(sha224)", + .cra_driver_name = "atmel-hmac-sha224", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_sha_hmac_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = atmel_sha_hmac_cra_init, + .cra_exit = atmel_sha_hmac_cra_exit, + } + } +}, +{ + .init = atmel_sha_hmac_init, + .update = atmel_sha_update, + .final = atmel_sha_final, + .digest = atmel_sha_hmac_digest, + .setkey = atmel_sha_hmac_setkey, + .export = atmel_sha_export, + .import = atmel_sha_import, + .halg = { + .digestsize = SHA256_DIGEST_SIZE, + .statesize = sizeof(struct atmel_sha_reqctx), + .base = { + .cra_name = "hmac(sha256)", + .cra_driver_name = "atmel-hmac-sha256", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_sha_hmac_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = atmel_sha_hmac_cra_init, + .cra_exit = atmel_sha_hmac_cra_exit, + } + } +}, +{ + .init = atmel_sha_hmac_init, + .update = atmel_sha_update, + .final = atmel_sha_final, + .digest = atmel_sha_hmac_digest, + .setkey = atmel_sha_hmac_setkey, + .export = atmel_sha_export, + .import = atmel_sha_import, + .halg = { + .digestsize = SHA384_DIGEST_SIZE, + .statesize = sizeof(struct atmel_sha_reqctx), + .base = { + .cra_name = "hmac(sha384)", + .cra_driver_name = "atmel-hmac-sha384", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_sha_hmac_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = atmel_sha_hmac_cra_init, + .cra_exit = atmel_sha_hmac_cra_exit, + } + } +}, +{ + .init = atmel_sha_hmac_init, + .update = atmel_sha_update, + .final = atmel_sha_final, + .digest = atmel_sha_hmac_digest, + .setkey = atmel_sha_hmac_setkey, + .export = atmel_sha_export, + .import = atmel_sha_import, + .halg = { + .digestsize = SHA512_DIGEST_SIZE, + .statesize = sizeof(struct atmel_sha_reqctx), + .base = { + .cra_name = "hmac(sha512)", + .cra_driver_name = "atmel-hmac-sha512", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct atmel_sha_hmac_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = atmel_sha_hmac_cra_init, + .cra_exit = atmel_sha_hmac_cra_exit, + } + } +}, +}; + +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC +/* authenc functions */ + +static int atmel_sha_authenc_init2(struct atmel_sha_dev *dd); +static int atmel_sha_authenc_init_done(struct atmel_sha_dev *dd); +static int atmel_sha_authenc_final_done(struct atmel_sha_dev *dd); + + +struct atmel_sha_authenc_ctx { + struct crypto_ahash *tfm; +}; + +struct atmel_sha_authenc_reqctx { + struct atmel_sha_reqctx base; + + atmel_aes_authenc_fn_t cb; + struct atmel_aes_dev *aes_dev; + + /* _init() parameters. */ + struct scatterlist *assoc; + u32 assoclen; + u32 textlen; + + /* _final() parameters. */ + u32 *digest; + unsigned int digestlen; +}; + +static void atmel_sha_authenc_complete(struct crypto_async_request *areq, + int err) +{ + struct ahash_request *req = areq->data; + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + + authctx->cb(authctx->aes_dev, err, authctx->base.dd->is_async); +} + +static int atmel_sha_authenc_start(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + int err; + + /* + * Force atmel_sha_complete() to call req->base.complete(), ie + * atmel_sha_authenc_complete(), which in turn calls authctx->cb(). + */ + dd->force_complete = true; + + err = atmel_sha_hw_init(dd); + return authctx->cb(authctx->aes_dev, err, dd->is_async); +} + +bool atmel_sha_authenc_is_ready(void) +{ + struct atmel_sha_ctx dummy; + + dummy.dd = NULL; + return (atmel_sha_find_dev(&dummy) != NULL); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_is_ready); + +unsigned int atmel_sha_authenc_get_reqsize(void) +{ + return sizeof(struct atmel_sha_authenc_reqctx); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_get_reqsize); + +struct atmel_sha_authenc_ctx *atmel_sha_authenc_spawn(unsigned long mode) +{ + struct atmel_sha_authenc_ctx *auth; + struct crypto_ahash *tfm; + struct atmel_sha_ctx *tctx; + const char *name; + int err = -EINVAL; + + switch (mode & SHA_FLAGS_MODE_MASK) { + case SHA_FLAGS_HMAC_SHA1: + name = "atmel-hmac-sha1"; + break; + + case SHA_FLAGS_HMAC_SHA224: + name = "atmel-hmac-sha224"; + break; + + case SHA_FLAGS_HMAC_SHA256: + name = "atmel-hmac-sha256"; + break; + + case SHA_FLAGS_HMAC_SHA384: + name = "atmel-hmac-sha384"; + break; + + case SHA_FLAGS_HMAC_SHA512: + name = "atmel-hmac-sha512"; + break; + + default: + goto error; + } + + tfm = crypto_alloc_ahash(name, + CRYPTO_ALG_TYPE_AHASH, + CRYPTO_ALG_TYPE_AHASH_MASK); + if (IS_ERR(tfm)) { + err = PTR_ERR(tfm); + goto error; + } + tctx = crypto_ahash_ctx(tfm); + tctx->start = atmel_sha_authenc_start; + tctx->flags = mode; + + auth = kzalloc(sizeof(*auth), GFP_KERNEL); + if (!auth) { + err = -ENOMEM; + goto err_free_ahash; + } + auth->tfm = tfm; + + return auth; + +err_free_ahash: + crypto_free_ahash(tfm); +error: + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_spawn); + +void atmel_sha_authenc_free(struct atmel_sha_authenc_ctx *auth) +{ + if (auth) + crypto_free_ahash(auth->tfm); + kfree(auth); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_free); + +int atmel_sha_authenc_setkey(struct atmel_sha_authenc_ctx *auth, + const u8 *key, unsigned int keylen, + u32 *flags) +{ + struct crypto_ahash *tfm = auth->tfm; + int err; + + crypto_ahash_clear_flags(tfm, CRYPTO_TFM_REQ_MASK); + crypto_ahash_set_flags(tfm, *flags & CRYPTO_TFM_REQ_MASK); + err = crypto_ahash_setkey(tfm, key, keylen); + *flags = crypto_ahash_get_flags(tfm); + + return err; +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_setkey); + +int atmel_sha_authenc_schedule(struct ahash_request *req, + struct atmel_sha_authenc_ctx *auth, + atmel_aes_authenc_fn_t cb, + struct atmel_aes_dev *aes_dev) +{ + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + struct atmel_sha_reqctx *ctx = &authctx->base; + struct crypto_ahash *tfm = auth->tfm; + struct atmel_sha_ctx *tctx = crypto_ahash_ctx(tfm); + struct atmel_sha_dev *dd; + + /* Reset request context (MUST be done first). */ + memset(authctx, 0, sizeof(*authctx)); + + /* Get SHA device. */ + dd = atmel_sha_find_dev(tctx); + if (!dd) + return cb(aes_dev, -ENODEV, false); + + /* Init request context. */ + ctx->dd = dd; + ctx->buflen = SHA_BUFFER_LEN; + authctx->cb = cb; + authctx->aes_dev = aes_dev; + ahash_request_set_tfm(req, tfm); + ahash_request_set_callback(req, 0, atmel_sha_authenc_complete, req); + + return atmel_sha_handle_queue(dd, req); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_schedule); + +int atmel_sha_authenc_init(struct ahash_request *req, + struct scatterlist *assoc, unsigned int assoclen, + unsigned int textlen, + atmel_aes_authenc_fn_t cb, + struct atmel_aes_dev *aes_dev) +{ + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + struct atmel_sha_reqctx *ctx = &authctx->base; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + struct atmel_sha_dev *dd = ctx->dd; + + if (unlikely(!IS_ALIGNED(assoclen, sizeof(u32)))) + return atmel_sha_complete(dd, -EINVAL); + + authctx->cb = cb; + authctx->aes_dev = aes_dev; + authctx->assoc = assoc; + authctx->assoclen = assoclen; + authctx->textlen = textlen; + + ctx->flags = hmac->base.flags; + return atmel_sha_hmac_setup(dd, atmel_sha_authenc_init2); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_init); + +static int atmel_sha_authenc_init2(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + struct atmel_sha_reqctx *ctx = &authctx->base; + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct atmel_sha_hmac_ctx *hmac = crypto_ahash_ctx(tfm); + size_t hs = ctx->hash_size; + size_t i, num_words = hs / sizeof(u32); + u32 mr, msg_size; + + atmel_sha_write(dd, SHA_CR, SHA_CR_WUIHV); + for (i = 0; i < num_words; ++i) + atmel_sha_write(dd, SHA_REG_DIN(i), hmac->ipad[i]); + + atmel_sha_write(dd, SHA_CR, SHA_CR_WUIEHV); + for (i = 0; i < num_words; ++i) + atmel_sha_write(dd, SHA_REG_DIN(i), hmac->opad[i]); + + mr = (SHA_MR_MODE_IDATAR0 | + SHA_MR_HMAC | + SHA_MR_DUALBUFF); + mr |= ctx->flags & SHA_FLAGS_ALGO_MASK; + atmel_sha_write(dd, SHA_MR, mr); + + msg_size = authctx->assoclen + authctx->textlen; + atmel_sha_write(dd, SHA_MSR, msg_size); + atmel_sha_write(dd, SHA_BCR, msg_size); + + atmel_sha_write(dd, SHA_CR, SHA_CR_FIRST); + + /* Process assoc data. */ + return atmel_sha_cpu_start(dd, authctx->assoc, authctx->assoclen, + true, false, + atmel_sha_authenc_init_done); +} + +static int atmel_sha_authenc_init_done(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + + return authctx->cb(authctx->aes_dev, 0, dd->is_async); +} + +int atmel_sha_authenc_final(struct ahash_request *req, + u32 *digest, unsigned int digestlen, + atmel_aes_authenc_fn_t cb, + struct atmel_aes_dev *aes_dev) +{ + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + struct atmel_sha_reqctx *ctx = &authctx->base; + struct atmel_sha_dev *dd = ctx->dd; + + switch (ctx->flags & SHA_FLAGS_ALGO_MASK) { + case SHA_FLAGS_SHA1: + authctx->digestlen = SHA1_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA224: + authctx->digestlen = SHA224_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA256: + authctx->digestlen = SHA256_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA384: + authctx->digestlen = SHA384_DIGEST_SIZE; + break; + + case SHA_FLAGS_SHA512: + authctx->digestlen = SHA512_DIGEST_SIZE; + break; + + default: + return atmel_sha_complete(dd, -EINVAL); + } + if (authctx->digestlen > digestlen) + authctx->digestlen = digestlen; + + authctx->cb = cb; + authctx->aes_dev = aes_dev; + authctx->digest = digest; + return atmel_sha_wait_for_data_ready(dd, + atmel_sha_authenc_final_done); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_final); + +static int atmel_sha_authenc_final_done(struct atmel_sha_dev *dd) +{ + struct ahash_request *req = dd->req; + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + size_t i, num_words = authctx->digestlen / sizeof(u32); + + for (i = 0; i < num_words; ++i) + authctx->digest[i] = atmel_sha_read(dd, SHA_REG_DIGEST(i)); + + return atmel_sha_complete(dd, 0); +} + +void atmel_sha_authenc_abort(struct ahash_request *req) +{ + struct atmel_sha_authenc_reqctx *authctx = ahash_request_ctx(req); + struct atmel_sha_reqctx *ctx = &authctx->base; + struct atmel_sha_dev *dd = ctx->dd; + + /* Prevent atmel_sha_complete() from calling req->base.complete(). */ + dd->is_async = false; + dd->force_complete = false; + (void)atmel_sha_complete(dd, 0); +} +EXPORT_SYMBOL_GPL(atmel_sha_authenc_abort); + +#endif /* CONFIG_CRYPTO_DEV_ATMEL_AUTHENC */ + + static void atmel_sha_unregister_algs(struct atmel_sha_dev *dd) { int i; + if (dd->caps.has_hmac) + for (i = 0; i < ARRAY_SIZE(sha_hmac_algs); i++) + crypto_unregister_ahash(&sha_hmac_algs[i]); + for (i = 0; i < ARRAY_SIZE(sha_1_256_algs); i++) crypto_unregister_ahash(&sha_1_256_algs[i]); @@ -1273,8 +2583,21 @@ static int atmel_sha_register_algs(struct atmel_sha_dev *dd) } } + if (dd->caps.has_hmac) { + for (i = 0; i < ARRAY_SIZE(sha_hmac_algs); i++) { + err = crypto_register_ahash(&sha_hmac_algs[i]); + if (err) + goto err_sha_hmac_algs; + } + } + return 0; + /*i = ARRAY_SIZE(sha_hmac_algs);*/ +err_sha_hmac_algs: + for (j = 0; j < i; j++) + crypto_unregister_ahash(&sha_hmac_algs[j]); + i = ARRAY_SIZE(sha_384_512_algs); err_sha_384_512_algs: for (j = 0; j < i; j++) crypto_unregister_ahash(&sha_384_512_algs[j]); @@ -1344,6 +2667,7 @@ static void atmel_sha_get_cap(struct atmel_sha_dev *dd) dd->caps.has_sha224 = 0; dd->caps.has_sha_384_512 = 0; dd->caps.has_uihv = 0; + dd->caps.has_hmac = 0; /* keep only major version number */ switch (dd->hw_version & 0xff0) { @@ -1353,6 +2677,7 @@ static void atmel_sha_get_cap(struct atmel_sha_dev *dd) dd->caps.has_sha224 = 1; dd->caps.has_sha_384_512 = 1; dd->caps.has_uihv = 1; + dd->caps.has_hmac = 1; break; case 0x420: dd->caps.has_dma = 1; diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c index bf467d7be35c..b25f1b3c981f 100644 --- a/drivers/crypto/atmel-tdes.c +++ b/drivers/crypto/atmel-tdes.c @@ -150,7 +150,7 @@ static struct atmel_tdes_drv atmel_tdes = { static int atmel_tdes_sg_copy(struct scatterlist **sg, size_t *offset, void *buf, size_t buflen, size_t total, int out) { - unsigned int count, off = 0; + size_t count, off = 0; while (buflen && total) { count = min((*sg)->length - *offset, total); @@ -336,7 +336,7 @@ static int atmel_tdes_crypt_pdc_stop(struct atmel_tdes_dev *dd) dd->buf_out, dd->buflen, dd->dma_size, 1); if (count != dd->dma_size) { err = -EINVAL; - pr_err("not all data converted: %u\n", count); + pr_err("not all data converted: %zu\n", count); } } @@ -361,7 +361,7 @@ static int atmel_tdes_buff_init(struct atmel_tdes_dev *dd) dd->dma_addr_in = dma_map_single(dd->dev, dd->buf_in, dd->buflen, DMA_TO_DEVICE); if (dma_mapping_error(dd->dev, dd->dma_addr_in)) { - dev_err(dd->dev, "dma %d bytes error\n", dd->buflen); + dev_err(dd->dev, "dma %zd bytes error\n", dd->buflen); err = -EINVAL; goto err_map_in; } @@ -369,7 +369,7 @@ static int atmel_tdes_buff_init(struct atmel_tdes_dev *dd) dd->dma_addr_out = dma_map_single(dd->dev, dd->buf_out, dd->buflen, DMA_FROM_DEVICE); if (dma_mapping_error(dd->dev, dd->dma_addr_out)) { - dev_err(dd->dev, "dma %d bytes error\n", dd->buflen); + dev_err(dd->dev, "dma %zd bytes error\n", dd->buflen); err = -EINVAL; goto err_map_out; } @@ -525,8 +525,8 @@ static int atmel_tdes_crypt_start(struct atmel_tdes_dev *dd) if (fast) { - count = min(dd->total, sg_dma_len(dd->in_sg)); - count = min(count, sg_dma_len(dd->out_sg)); + count = min_t(size_t, dd->total, sg_dma_len(dd->in_sg)); + count = min_t(size_t, count, sg_dma_len(dd->out_sg)); err = dma_map_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); if (!err) { @@ -661,7 +661,7 @@ static int atmel_tdes_crypt_dma_stop(struct atmel_tdes_dev *dd) dd->buf_out, dd->buflen, dd->dma_size, 1); if (count != dd->dma_size) { err = -EINVAL; - pr_err("not all data converted: %u\n", count); + pr_err("not all data converted: %zu\n", count); } } } diff --git a/drivers/crypto/bcm/Makefile b/drivers/crypto/bcm/Makefile new file mode 100644 index 000000000000..13cb80eb2665 --- /dev/null +++ b/drivers/crypto/bcm/Makefile @@ -0,0 +1,15 @@ +# File: drivers/crypto/bcm/Makefile +# +# Makefile for crypto acceleration files for Broadcom SPU driver +# +# Uncomment to enable debug tracing in the SPU driver. +# CFLAGS_util.o := -DDEBUG +# CFLAGS_cipher.o := -DDEBUG +# CFLAGS_spu.o := -DDEBUG +# CFLAGS_spu2.o := -DDEBUG + +obj-$(CONFIG_CRYPTO_DEV_BCM_SPU) := bcm_crypto_spu.o + +bcm_crypto_spu-objs := util.o spu.o spu2.o cipher.o + +ccflags-y += -I. -DBCMDRIVER diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c new file mode 100644 index 000000000000..cc0d5b98006e --- /dev/null +++ b/drivers/crypto/bcm/cipher.c @@ -0,0 +1,4963 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> +#include <linux/crypto.h> +#include <linux/kthread.h> +#include <linux/rtnetlink.h> +#include <linux/sched.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/io.h> +#include <linux/bitops.h> + +#include <crypto/algapi.h> +#include <crypto/aead.h> +#include <crypto/internal/aead.h> +#include <crypto/aes.h> +#include <crypto/des.h> +#include <crypto/sha.h> +#include <crypto/md5.h> +#include <crypto/authenc.h> +#include <crypto/skcipher.h> +#include <crypto/hash.h> +#include <crypto/aes.h> +#include <crypto/sha3.h> + +#include "util.h" +#include "cipher.h" +#include "spu.h" +#include "spum.h" +#include "spu2.h" + +/* ================= Device Structure ================== */ + +struct device_private iproc_priv; + +/* ==================== Parameters ===================== */ + +int flow_debug_logging; +module_param(flow_debug_logging, int, 0644); +MODULE_PARM_DESC(flow_debug_logging, "Enable Flow Debug Logging"); + +int packet_debug_logging; +module_param(packet_debug_logging, int, 0644); +MODULE_PARM_DESC(packet_debug_logging, "Enable Packet Debug Logging"); + +int debug_logging_sleep; +module_param(debug_logging_sleep, int, 0644); +MODULE_PARM_DESC(debug_logging_sleep, "Packet Debug Logging Sleep"); + +/* + * The value of these module parameters is used to set the priority for each + * algo type when this driver registers algos with the kernel crypto API. + * To use a priority other than the default, set the priority in the insmod or + * modprobe. Changing the module priority after init time has no effect. + * + * The default priorities are chosen to be lower (less preferred) than ARMv8 CE + * algos, but more preferred than generic software algos. + */ +static int cipher_pri = 150; +module_param(cipher_pri, int, 0644); +MODULE_PARM_DESC(cipher_pri, "Priority for cipher algos"); + +static int hash_pri = 100; +module_param(hash_pri, int, 0644); +MODULE_PARM_DESC(hash_pri, "Priority for hash algos"); + +static int aead_pri = 150; +module_param(aead_pri, int, 0644); +MODULE_PARM_DESC(aead_pri, "Priority for AEAD algos"); + +#define MAX_SPUS 16 + +/* A type 3 BCM header, expected to precede the SPU header for SPU-M. + * Bits 3 and 4 in the first byte encode the channel number (the dma ringset). + * 0x60 - ring 0 + * 0x68 - ring 1 + * 0x70 - ring 2 + * 0x78 - ring 3 + */ +char BCMHEADER[] = { 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28 }; +/* + * Some SPU hw does not use BCM header on SPU messages. So BCM_HDR_LEN + * is set dynamically after reading SPU type from device tree. + */ +#define BCM_HDR_LEN iproc_priv.bcm_hdr_len + +/* min and max time to sleep before retrying when mbox queue is full. usec */ +#define MBOX_SLEEP_MIN 800 +#define MBOX_SLEEP_MAX 1000 + +/** + * select_channel() - Select a SPU channel to handle a crypto request. Selects + * channel in round robin order. + * + * Return: channel index + */ +static u8 select_channel(void) +{ + u8 chan_idx = atomic_inc_return(&iproc_priv.next_chan); + + return chan_idx % iproc_priv.spu.num_spu; +} + +/** + * spu_ablkcipher_rx_sg_create() - Build up the scatterlist of buffers used to + * receive a SPU response message for an ablkcipher request. Includes buffers to + * catch SPU message headers and the response data. + * @mssg: mailbox message containing the receive sg + * @rctx: crypto request context + * @rx_frag_num: number of scatterlist elements required to hold the + * SPU response message + * @chunksize: Number of bytes of response data expected + * @stat_pad_len: Number of bytes required to pad the STAT field to + * a 4-byte boundary + * + * The scatterlist that gets allocated here is freed in spu_chunk_cleanup() + * when the request completes, whether the request is handled successfully or + * there is an error. + * + * Returns: + * 0 if successful + * < 0 if an error + */ +static int +spu_ablkcipher_rx_sg_create(struct brcm_message *mssg, + struct iproc_reqctx_s *rctx, + u8 rx_frag_num, + unsigned int chunksize, u32 stat_pad_len) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct scatterlist *sg; /* used to build sgs in mbox message */ + struct iproc_ctx_s *ctx = rctx->ctx; + u32 datalen; /* Number of bytes of response data expected */ + + mssg->spu.dst = kcalloc(rx_frag_num, sizeof(struct scatterlist), + rctx->gfp); + if (!mssg->spu.dst) + return -ENOMEM; + + sg = mssg->spu.dst; + sg_init_table(sg, rx_frag_num); + /* Space for SPU message header */ + sg_set_buf(sg++, rctx->msg_buf.spu_resp_hdr, ctx->spu_resp_hdr_len); + + /* If XTS tweak in payload, add buffer to receive encrypted tweak */ + if ((ctx->cipher.mode == CIPHER_MODE_XTS) && + spu->spu_xts_tweak_in_payload()) + sg_set_buf(sg++, rctx->msg_buf.c.supdt_tweak, + SPU_XTS_TWEAK_SIZE); + + /* Copy in each dst sg entry from request, up to chunksize */ + datalen = spu_msg_sg_add(&sg, &rctx->dst_sg, &rctx->dst_skip, + rctx->dst_nents, chunksize); + if (datalen < chunksize) { + pr_err("%s(): failed to copy dst sg to mbox msg. chunksize %u, datalen %u", + __func__, chunksize, datalen); + return -EFAULT; + } + + if (ctx->cipher.alg == CIPHER_ALG_RC4) + /* Add buffer to catch 260-byte SUPDT field for RC4 */ + sg_set_buf(sg++, rctx->msg_buf.c.supdt_tweak, SPU_SUPDT_LEN); + + if (stat_pad_len) + sg_set_buf(sg++, rctx->msg_buf.rx_stat_pad, stat_pad_len); + + memset(rctx->msg_buf.rx_stat, 0, SPU_RX_STATUS_LEN); + sg_set_buf(sg, rctx->msg_buf.rx_stat, spu->spu_rx_status_len()); + + return 0; +} + +/** + * spu_ablkcipher_tx_sg_create() - Build up the scatterlist of buffers used to + * send a SPU request message for an ablkcipher request. Includes SPU message + * headers and the request data. + * @mssg: mailbox message containing the transmit sg + * @rctx: crypto request context + * @tx_frag_num: number of scatterlist elements required to construct the + * SPU request message + * @chunksize: Number of bytes of request data + * @pad_len: Number of pad bytes + * + * The scatterlist that gets allocated here is freed in spu_chunk_cleanup() + * when the request completes, whether the request is handled successfully or + * there is an error. + * + * Returns: + * 0 if successful + * < 0 if an error + */ +static int +spu_ablkcipher_tx_sg_create(struct brcm_message *mssg, + struct iproc_reqctx_s *rctx, + u8 tx_frag_num, unsigned int chunksize, u32 pad_len) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct scatterlist *sg; /* used to build sgs in mbox message */ + struct iproc_ctx_s *ctx = rctx->ctx; + u32 datalen; /* Number of bytes of response data expected */ + u32 stat_len; + + mssg->spu.src = kcalloc(tx_frag_num, sizeof(struct scatterlist), + rctx->gfp); + if (unlikely(!mssg->spu.src)) + return -ENOMEM; + + sg = mssg->spu.src; + sg_init_table(sg, tx_frag_num); + + sg_set_buf(sg++, rctx->msg_buf.bcm_spu_req_hdr, + BCM_HDR_LEN + ctx->spu_req_hdr_len); + + /* if XTS tweak in payload, copy from IV (where crypto API puts it) */ + if ((ctx->cipher.mode == CIPHER_MODE_XTS) && + spu->spu_xts_tweak_in_payload()) + sg_set_buf(sg++, rctx->msg_buf.iv_ctr, SPU_XTS_TWEAK_SIZE); + + /* Copy in each src sg entry from request, up to chunksize */ + datalen = spu_msg_sg_add(&sg, &rctx->src_sg, &rctx->src_skip, + rctx->src_nents, chunksize); + if (unlikely(datalen < chunksize)) { + pr_err("%s(): failed to copy src sg to mbox msg", + __func__); + return -EFAULT; + } + + if (pad_len) + sg_set_buf(sg++, rctx->msg_buf.spu_req_pad, pad_len); + + stat_len = spu->spu_tx_status_len(); + if (stat_len) { + memset(rctx->msg_buf.tx_stat, 0, stat_len); + sg_set_buf(sg, rctx->msg_buf.tx_stat, stat_len); + } + return 0; +} + +/** + * handle_ablkcipher_req() - Submit as much of a block cipher request as fits in + * a single SPU request message, starting at the current position in the request + * data. + * @rctx: Crypto request context + * + * This may be called on the crypto API thread, or, when a request is so large + * it must be broken into multiple SPU messages, on the thread used to invoke + * the response callback. When requests are broken into multiple SPU + * messages, we assume subsequent messages depend on previous results, and + * thus always wait for previous results before submitting the next message. + * Because requests are submitted in lock step like this, there is no need + * to synchronize access to request data structures. + * + * Return: -EINPROGRESS: request has been accepted and result will be returned + * asynchronously + * Any other value indicates an error + */ +static int handle_ablkcipher_req(struct iproc_reqctx_s *rctx) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct crypto_async_request *areq = rctx->parent; + struct ablkcipher_request *req = + container_of(areq, struct ablkcipher_request, base); + struct iproc_ctx_s *ctx = rctx->ctx; + struct spu_cipher_parms cipher_parms; + int err = 0; + unsigned int chunksize = 0; /* Num bytes of request to submit */ + int remaining = 0; /* Bytes of request still to process */ + int chunk_start; /* Beginning of data for current SPU msg */ + + /* IV or ctr value to use in this SPU msg */ + u8 local_iv_ctr[MAX_IV_SIZE]; + u32 stat_pad_len; /* num bytes to align status field */ + u32 pad_len; /* total length of all padding */ + bool update_key = false; + struct brcm_message *mssg; /* mailbox message */ + int retry_cnt = 0; + + /* number of entries in src and dst sg in mailbox message. */ + u8 rx_frag_num = 2; /* response header and STATUS */ + u8 tx_frag_num = 1; /* request header */ + + flow_log("%s\n", __func__); + + cipher_parms.alg = ctx->cipher.alg; + cipher_parms.mode = ctx->cipher.mode; + cipher_parms.type = ctx->cipher_type; + cipher_parms.key_len = ctx->enckeylen; + cipher_parms.key_buf = ctx->enckey; + cipher_parms.iv_buf = local_iv_ctr; + cipher_parms.iv_len = rctx->iv_ctr_len; + + mssg = &rctx->mb_mssg; + chunk_start = rctx->src_sent; + remaining = rctx->total_todo - chunk_start; + + /* determine the chunk we are breaking off and update the indexes */ + if ((ctx->max_payload != SPU_MAX_PAYLOAD_INF) && + (remaining > ctx->max_payload)) + chunksize = ctx->max_payload; + else + chunksize = remaining; + + rctx->src_sent += chunksize; + rctx->total_sent = rctx->src_sent; + + /* Count number of sg entries to be included in this request */ + rctx->src_nents = spu_sg_count(rctx->src_sg, rctx->src_skip, chunksize); + rctx->dst_nents = spu_sg_count(rctx->dst_sg, rctx->dst_skip, chunksize); + + if ((ctx->cipher.mode == CIPHER_MODE_CBC) && + rctx->is_encrypt && chunk_start) + /* + * Encrypting non-first first chunk. Copy last block of + * previous result to IV for this chunk. + */ + sg_copy_part_to_buf(req->dst, rctx->msg_buf.iv_ctr, + rctx->iv_ctr_len, + chunk_start - rctx->iv_ctr_len); + + if (rctx->iv_ctr_len) { + /* get our local copy of the iv */ + __builtin_memcpy(local_iv_ctr, rctx->msg_buf.iv_ctr, + rctx->iv_ctr_len); + + /* generate the next IV if possible */ + if ((ctx->cipher.mode == CIPHER_MODE_CBC) && + !rctx->is_encrypt) { + /* + * CBC Decrypt: next IV is the last ciphertext block in + * this chunk + */ + sg_copy_part_to_buf(req->src, rctx->msg_buf.iv_ctr, + rctx->iv_ctr_len, + rctx->src_sent - rctx->iv_ctr_len); + } else if (ctx->cipher.mode == CIPHER_MODE_CTR) { + /* + * The SPU hardware increments the counter once for + * each AES block of 16 bytes. So update the counter + * for the next chunk, if there is one. Note that for + * this chunk, the counter has already been copied to + * local_iv_ctr. We can assume a block size of 16, + * because we only support CTR mode for AES, not for + * any other cipher alg. + */ + add_to_ctr(rctx->msg_buf.iv_ctr, chunksize >> 4); + } + } + + if (ctx->cipher.alg == CIPHER_ALG_RC4) { + rx_frag_num++; + if (chunk_start) { + /* + * for non-first RC4 chunks, use SUPDT from previous + * response as key for this chunk. + */ + cipher_parms.key_buf = rctx->msg_buf.c.supdt_tweak; + update_key = true; + cipher_parms.type = CIPHER_TYPE_UPDT; + } else if (!rctx->is_encrypt) { + /* + * First RC4 chunk. For decrypt, key in pre-built msg + * header may have been changed if encrypt required + * multiple chunks. So revert the key to the + * ctx->enckey value. + */ + update_key = true; + cipher_parms.type = CIPHER_TYPE_INIT; + } + } + + if (ctx->max_payload == SPU_MAX_PAYLOAD_INF) + flow_log("max_payload infinite\n"); + else + flow_log("max_payload %u\n", ctx->max_payload); + + flow_log("sent:%u start:%u remains:%u size:%u\n", + rctx->src_sent, chunk_start, remaining, chunksize); + + /* Copy SPU header template created at setkey time */ + memcpy(rctx->msg_buf.bcm_spu_req_hdr, ctx->bcm_spu_req_hdr, + sizeof(rctx->msg_buf.bcm_spu_req_hdr)); + + /* + * Pass SUPDT field as key. Key field in finish() call is only used + * when update_key has been set above for RC4. Will be ignored in + * all other cases. + */ + spu->spu_cipher_req_finish(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN, + ctx->spu_req_hdr_len, !(rctx->is_encrypt), + &cipher_parms, update_key, chunksize); + + atomic64_add(chunksize, &iproc_priv.bytes_out); + + stat_pad_len = spu->spu_wordalign_padlen(chunksize); + if (stat_pad_len) + rx_frag_num++; + pad_len = stat_pad_len; + if (pad_len) { + tx_frag_num++; + spu->spu_request_pad(rctx->msg_buf.spu_req_pad, 0, + 0, ctx->auth.alg, ctx->auth.mode, + rctx->total_sent, stat_pad_len); + } + + spu->spu_dump_msg_hdr(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN, + ctx->spu_req_hdr_len); + packet_log("payload:\n"); + dump_sg(rctx->src_sg, rctx->src_skip, chunksize); + packet_dump(" pad: ", rctx->msg_buf.spu_req_pad, pad_len); + + /* + * Build mailbox message containing SPU request msg and rx buffers + * to catch response message + */ + memset(mssg, 0, sizeof(*mssg)); + mssg->type = BRCM_MESSAGE_SPU; + mssg->ctx = rctx; /* Will be returned in response */ + + /* Create rx scatterlist to catch result */ + rx_frag_num += rctx->dst_nents; + + if ((ctx->cipher.mode == CIPHER_MODE_XTS) && + spu->spu_xts_tweak_in_payload()) + rx_frag_num++; /* extra sg to insert tweak */ + + err = spu_ablkcipher_rx_sg_create(mssg, rctx, rx_frag_num, chunksize, + stat_pad_len); + if (err) + return err; + + /* Create tx scatterlist containing SPU request message */ + tx_frag_num += rctx->src_nents; + if (spu->spu_tx_status_len()) + tx_frag_num++; + + if ((ctx->cipher.mode == CIPHER_MODE_XTS) && + spu->spu_xts_tweak_in_payload()) + tx_frag_num++; /* extra sg to insert tweak */ + + err = spu_ablkcipher_tx_sg_create(mssg, rctx, tx_frag_num, chunksize, + pad_len); + if (err) + return err; + + err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], mssg); + if (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) { + while ((err == -ENOBUFS) && (retry_cnt < SPU_MB_RETRY_MAX)) { + /* + * Mailbox queue is full. Since MAY_SLEEP is set, assume + * not in atomic context and we can wait and try again. + */ + retry_cnt++; + usleep_range(MBOX_SLEEP_MIN, MBOX_SLEEP_MAX); + err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], + mssg); + atomic_inc(&iproc_priv.mb_no_spc); + } + } + if (unlikely(err < 0)) { + atomic_inc(&iproc_priv.mb_send_fail); + return err; + } + + return -EINPROGRESS; +} + +/** + * handle_ablkcipher_resp() - Process a block cipher SPU response. Updates the + * total received count for the request and updates global stats. + * @rctx: Crypto request context + */ +static void handle_ablkcipher_resp(struct iproc_reqctx_s *rctx) +{ + struct spu_hw *spu = &iproc_priv.spu; +#ifdef DEBUG + struct crypto_async_request *areq = rctx->parent; + struct ablkcipher_request *req = ablkcipher_request_cast(areq); +#endif + struct iproc_ctx_s *ctx = rctx->ctx; + u32 payload_len; + + /* See how much data was returned */ + payload_len = spu->spu_payload_length(rctx->msg_buf.spu_resp_hdr); + + /* + * In XTS mode, the first SPU_XTS_TWEAK_SIZE bytes may be the + * encrypted tweak ("i") value; we don't count those. + */ + if ((ctx->cipher.mode == CIPHER_MODE_XTS) && + spu->spu_xts_tweak_in_payload() && + (payload_len >= SPU_XTS_TWEAK_SIZE)) + payload_len -= SPU_XTS_TWEAK_SIZE; + + atomic64_add(payload_len, &iproc_priv.bytes_in); + + flow_log("%s() offset: %u, bd_len: %u BD:\n", + __func__, rctx->total_received, payload_len); + + dump_sg(req->dst, rctx->total_received, payload_len); + if (ctx->cipher.alg == CIPHER_ALG_RC4) + packet_dump(" supdt ", rctx->msg_buf.c.supdt_tweak, + SPU_SUPDT_LEN); + + rctx->total_received += payload_len; + if (rctx->total_received == rctx->total_todo) { + atomic_inc(&iproc_priv.op_counts[SPU_OP_CIPHER]); + atomic_inc( + &iproc_priv.cipher_cnt[ctx->cipher.alg][ctx->cipher.mode]); + } +} + +/** + * spu_ahash_rx_sg_create() - Build up the scatterlist of buffers used to + * receive a SPU response message for an ahash request. + * @mssg: mailbox message containing the receive sg + * @rctx: crypto request context + * @rx_frag_num: number of scatterlist elements required to hold the + * SPU response message + * @digestsize: length of hash digest, in bytes + * @stat_pad_len: Number of bytes required to pad the STAT field to + * a 4-byte boundary + * + * The scatterlist that gets allocated here is freed in spu_chunk_cleanup() + * when the request completes, whether the request is handled successfully or + * there is an error. + * + * Return: + * 0 if successful + * < 0 if an error + */ +static int +spu_ahash_rx_sg_create(struct brcm_message *mssg, + struct iproc_reqctx_s *rctx, + u8 rx_frag_num, unsigned int digestsize, + u32 stat_pad_len) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct scatterlist *sg; /* used to build sgs in mbox message */ + struct iproc_ctx_s *ctx = rctx->ctx; + + mssg->spu.dst = kcalloc(rx_frag_num, sizeof(struct scatterlist), + rctx->gfp); + if (!mssg->spu.dst) + return -ENOMEM; + + sg = mssg->spu.dst; + sg_init_table(sg, rx_frag_num); + /* Space for SPU message header */ + sg_set_buf(sg++, rctx->msg_buf.spu_resp_hdr, ctx->spu_resp_hdr_len); + + /* Space for digest */ + sg_set_buf(sg++, rctx->msg_buf.digest, digestsize); + + if (stat_pad_len) + sg_set_buf(sg++, rctx->msg_buf.rx_stat_pad, stat_pad_len); + + memset(rctx->msg_buf.rx_stat, 0, SPU_RX_STATUS_LEN); + sg_set_buf(sg, rctx->msg_buf.rx_stat, spu->spu_rx_status_len()); + return 0; +} + +/** + * spu_ahash_tx_sg_create() - Build up the scatterlist of buffers used to send + * a SPU request message for an ahash request. Includes SPU message headers and + * the request data. + * @mssg: mailbox message containing the transmit sg + * @rctx: crypto request context + * @tx_frag_num: number of scatterlist elements required to construct the + * SPU request message + * @spu_hdr_len: length in bytes of SPU message header + * @hash_carry_len: Number of bytes of data carried over from previous req + * @new_data_len: Number of bytes of new request data + * @pad_len: Number of pad bytes + * + * The scatterlist that gets allocated here is freed in spu_chunk_cleanup() + * when the request completes, whether the request is handled successfully or + * there is an error. + * + * Return: + * 0 if successful + * < 0 if an error + */ +static int +spu_ahash_tx_sg_create(struct brcm_message *mssg, + struct iproc_reqctx_s *rctx, + u8 tx_frag_num, + u32 spu_hdr_len, + unsigned int hash_carry_len, + unsigned int new_data_len, u32 pad_len) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct scatterlist *sg; /* used to build sgs in mbox message */ + u32 datalen; /* Number of bytes of response data expected */ + u32 stat_len; + + mssg->spu.src = kcalloc(tx_frag_num, sizeof(struct scatterlist), + rctx->gfp); + if (!mssg->spu.src) + return -ENOMEM; + + sg = mssg->spu.src; + sg_init_table(sg, tx_frag_num); + + sg_set_buf(sg++, rctx->msg_buf.bcm_spu_req_hdr, + BCM_HDR_LEN + spu_hdr_len); + + if (hash_carry_len) + sg_set_buf(sg++, rctx->hash_carry, hash_carry_len); + + if (new_data_len) { + /* Copy in each src sg entry from request, up to chunksize */ + datalen = spu_msg_sg_add(&sg, &rctx->src_sg, &rctx->src_skip, + rctx->src_nents, new_data_len); + if (datalen < new_data_len) { + pr_err("%s(): failed to copy src sg to mbox msg", + __func__); + return -EFAULT; + } + } + + if (pad_len) + sg_set_buf(sg++, rctx->msg_buf.spu_req_pad, pad_len); + + stat_len = spu->spu_tx_status_len(); + if (stat_len) { + memset(rctx->msg_buf.tx_stat, 0, stat_len); + sg_set_buf(sg, rctx->msg_buf.tx_stat, stat_len); + } + + return 0; +} + +/** + * handle_ahash_req() - Process an asynchronous hash request from the crypto + * API. + * @rctx: Crypto request context + * + * Builds a SPU request message embedded in a mailbox message and submits the + * mailbox message on a selected mailbox channel. The SPU request message is + * constructed as a scatterlist, including entries from the crypto API's + * src scatterlist to avoid copying the data to be hashed. This function is + * called either on the thread from the crypto API, or, in the case that the + * crypto API request is too large to fit in a single SPU request message, + * on the thread that invokes the receive callback with a response message. + * Because some operations require the response from one chunk before the next + * chunk can be submitted, we always wait for the response for the previous + * chunk before submitting the next chunk. Because requests are submitted in + * lock step like this, there is no need to synchronize access to request data + * structures. + * + * Return: + * -EINPROGRESS: request has been submitted to SPU and response will be + * returned asynchronously + * -EAGAIN: non-final request included a small amount of data, which for + * efficiency we did not submit to the SPU, but instead stored + * to be submitted to the SPU with the next part of the request + * other: an error code + */ +static int handle_ahash_req(struct iproc_reqctx_s *rctx) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct crypto_async_request *areq = rctx->parent; + struct ahash_request *req = ahash_request_cast(areq); + struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); + struct crypto_tfm *tfm = crypto_ahash_tfm(ahash); + unsigned int blocksize = crypto_tfm_alg_blocksize(tfm); + struct iproc_ctx_s *ctx = rctx->ctx; + + /* number of bytes still to be hashed in this req */ + unsigned int nbytes_to_hash = 0; + int err = 0; + unsigned int chunksize = 0; /* length of hash carry + new data */ + /* + * length of new data, not from hash carry, to be submitted in + * this hw request + */ + unsigned int new_data_len; + + unsigned int chunk_start = 0; + u32 db_size; /* Length of data field, incl gcm and hash padding */ + int pad_len = 0; /* total pad len, including gcm, hash, stat padding */ + u32 data_pad_len = 0; /* length of GCM/CCM padding */ + u32 stat_pad_len = 0; /* length of padding to align STATUS word */ + struct brcm_message *mssg; /* mailbox message */ + struct spu_request_opts req_opts; + struct spu_cipher_parms cipher_parms; + struct spu_hash_parms hash_parms; + struct spu_aead_parms aead_parms; + unsigned int local_nbuf; + u32 spu_hdr_len; + unsigned int digestsize; + u16 rem = 0; + int retry_cnt = 0; + + /* + * number of entries in src and dst sg. Always includes SPU msg header. + * rx always includes a buffer to catch digest and STATUS. + */ + u8 rx_frag_num = 3; + u8 tx_frag_num = 1; + + flow_log("total_todo %u, total_sent %u\n", + rctx->total_todo, rctx->total_sent); + + memset(&req_opts, 0, sizeof(req_opts)); + memset(&cipher_parms, 0, sizeof(cipher_parms)); + memset(&hash_parms, 0, sizeof(hash_parms)); + memset(&aead_parms, 0, sizeof(aead_parms)); + + req_opts.bd_suppress = true; + hash_parms.alg = ctx->auth.alg; + hash_parms.mode = ctx->auth.mode; + hash_parms.type = HASH_TYPE_NONE; + hash_parms.key_buf = (u8 *)ctx->authkey; + hash_parms.key_len = ctx->authkeylen; + + /* + * For hash algorithms below assignment looks bit odd but + * it's needed for AES-XCBC and AES-CMAC hash algorithms + * to differentiate between 128, 192, 256 bit key values. + * Based on the key values, hash algorithm is selected. + * For example for 128 bit key, hash algorithm is AES-128. + */ + cipher_parms.type = ctx->cipher_type; + + mssg = &rctx->mb_mssg; + chunk_start = rctx->src_sent; + + /* + * Compute the amount remaining to hash. This may include data + * carried over from previous requests. + */ + nbytes_to_hash = rctx->total_todo - rctx->total_sent; + chunksize = nbytes_to_hash; + if ((ctx->max_payload != SPU_MAX_PAYLOAD_INF) && + (chunksize > ctx->max_payload)) + chunksize = ctx->max_payload; + + /* + * If this is not a final request and the request data is not a multiple + * of a full block, then simply park the extra data and prefix it to the + * data for the next request. + */ + if (!rctx->is_final) { + u8 *dest = rctx->hash_carry + rctx->hash_carry_len; + u16 new_len; /* len of data to add to hash carry */ + + rem = chunksize % blocksize; /* remainder */ + if (rem) { + /* chunksize not a multiple of blocksize */ + chunksize -= rem; + if (chunksize == 0) { + /* Don't have a full block to submit to hw */ + new_len = rem - rctx->hash_carry_len; + sg_copy_part_to_buf(req->src, dest, new_len, + rctx->src_sent); + rctx->hash_carry_len = rem; + flow_log("Exiting with hash carry len: %u\n", + rctx->hash_carry_len); + packet_dump(" buf: ", + rctx->hash_carry, + rctx->hash_carry_len); + return -EAGAIN; + } + } + } + + /* if we have hash carry, then prefix it to the data in this request */ + local_nbuf = rctx->hash_carry_len; + rctx->hash_carry_len = 0; + if (local_nbuf) + tx_frag_num++; + new_data_len = chunksize - local_nbuf; + + /* Count number of sg entries to be used in this request */ + rctx->src_nents = spu_sg_count(rctx->src_sg, rctx->src_skip, + new_data_len); + + /* AES hashing keeps key size in type field, so need to copy it here */ + if (hash_parms.alg == HASH_ALG_AES) + hash_parms.type = cipher_parms.type; + else + hash_parms.type = spu->spu_hash_type(rctx->total_sent); + + digestsize = spu->spu_digest_size(ctx->digestsize, ctx->auth.alg, + hash_parms.type); + hash_parms.digestsize = digestsize; + + /* update the indexes */ + rctx->total_sent += chunksize; + /* if you sent a prebuf then that wasn't from this req->src */ + rctx->src_sent += new_data_len; + + if ((rctx->total_sent == rctx->total_todo) && rctx->is_final) + hash_parms.pad_len = spu->spu_hash_pad_len(hash_parms.alg, + hash_parms.mode, + chunksize, + blocksize); + + /* + * If a non-first chunk, then include the digest returned from the + * previous chunk so that hw can add to it (except for AES types). + */ + if ((hash_parms.type == HASH_TYPE_UPDT) && + (hash_parms.alg != HASH_ALG_AES)) { + hash_parms.key_buf = rctx->incr_hash; + hash_parms.key_len = digestsize; + } + + atomic64_add(chunksize, &iproc_priv.bytes_out); + + flow_log("%s() final: %u nbuf: %u ", + __func__, rctx->is_final, local_nbuf); + + if (ctx->max_payload == SPU_MAX_PAYLOAD_INF) + flow_log("max_payload infinite\n"); + else + flow_log("max_payload %u\n", ctx->max_payload); + + flow_log("chunk_start: %u chunk_size: %u\n", chunk_start, chunksize); + + /* Prepend SPU header with type 3 BCM header */ + memcpy(rctx->msg_buf.bcm_spu_req_hdr, BCMHEADER, BCM_HDR_LEN); + + hash_parms.prebuf_len = local_nbuf; + spu_hdr_len = spu->spu_create_request(rctx->msg_buf.bcm_spu_req_hdr + + BCM_HDR_LEN, + &req_opts, &cipher_parms, + &hash_parms, &aead_parms, + new_data_len); + + if (spu_hdr_len == 0) { + pr_err("Failed to create SPU request header\n"); + return -EFAULT; + } + + /* + * Determine total length of padding required. Put all padding in one + * buffer. + */ + data_pad_len = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode, chunksize); + db_size = spu_real_db_size(0, 0, local_nbuf, new_data_len, + 0, 0, hash_parms.pad_len); + if (spu->spu_tx_status_len()) + stat_pad_len = spu->spu_wordalign_padlen(db_size); + if (stat_pad_len) + rx_frag_num++; + pad_len = hash_parms.pad_len + data_pad_len + stat_pad_len; + if (pad_len) { + tx_frag_num++; + spu->spu_request_pad(rctx->msg_buf.spu_req_pad, data_pad_len, + hash_parms.pad_len, ctx->auth.alg, + ctx->auth.mode, rctx->total_sent, + stat_pad_len); + } + + spu->spu_dump_msg_hdr(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN, + spu_hdr_len); + packet_dump(" prebuf: ", rctx->hash_carry, local_nbuf); + flow_log("Data:\n"); + dump_sg(rctx->src_sg, rctx->src_skip, new_data_len); + packet_dump(" pad: ", rctx->msg_buf.spu_req_pad, pad_len); + + /* + * Build mailbox message containing SPU request msg and rx buffers + * to catch response message + */ + memset(mssg, 0, sizeof(*mssg)); + mssg->type = BRCM_MESSAGE_SPU; + mssg->ctx = rctx; /* Will be returned in response */ + + /* Create rx scatterlist to catch result */ + err = spu_ahash_rx_sg_create(mssg, rctx, rx_frag_num, digestsize, + stat_pad_len); + if (err) + return err; + + /* Create tx scatterlist containing SPU request message */ + tx_frag_num += rctx->src_nents; + if (spu->spu_tx_status_len()) + tx_frag_num++; + err = spu_ahash_tx_sg_create(mssg, rctx, tx_frag_num, spu_hdr_len, + local_nbuf, new_data_len, pad_len); + if (err) + return err; + + err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], mssg); + if (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) { + while ((err == -ENOBUFS) && (retry_cnt < SPU_MB_RETRY_MAX)) { + /* + * Mailbox queue is full. Since MAY_SLEEP is set, assume + * not in atomic context and we can wait and try again. + */ + retry_cnt++; + usleep_range(MBOX_SLEEP_MIN, MBOX_SLEEP_MAX); + err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], + mssg); + atomic_inc(&iproc_priv.mb_no_spc); + } + } + if (err < 0) { + atomic_inc(&iproc_priv.mb_send_fail); + return err; + } + return -EINPROGRESS; +} + +/** + * spu_hmac_outer_hash() - Request synchonous software compute of the outer hash + * for an HMAC request. + * @req: The HMAC request from the crypto API + * @ctx: The session context + * + * Return: 0 if synchronous hash operation successful + * -EINVAL if the hash algo is unrecognized + * any other value indicates an error + */ +static int spu_hmac_outer_hash(struct ahash_request *req, + struct iproc_ctx_s *ctx) +{ + struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); + unsigned int blocksize = + crypto_tfm_alg_blocksize(crypto_ahash_tfm(ahash)); + int rc; + + switch (ctx->auth.alg) { + case HASH_ALG_MD5: + rc = do_shash("md5", req->result, ctx->opad, blocksize, + req->result, ctx->digestsize, NULL, 0); + break; + case HASH_ALG_SHA1: + rc = do_shash("sha1", req->result, ctx->opad, blocksize, + req->result, ctx->digestsize, NULL, 0); + break; + case HASH_ALG_SHA224: + rc = do_shash("sha224", req->result, ctx->opad, blocksize, + req->result, ctx->digestsize, NULL, 0); + break; + case HASH_ALG_SHA256: + rc = do_shash("sha256", req->result, ctx->opad, blocksize, + req->result, ctx->digestsize, NULL, 0); + break; + case HASH_ALG_SHA384: + rc = do_shash("sha384", req->result, ctx->opad, blocksize, + req->result, ctx->digestsize, NULL, 0); + break; + case HASH_ALG_SHA512: + rc = do_shash("sha512", req->result, ctx->opad, blocksize, + req->result, ctx->digestsize, NULL, 0); + break; + default: + pr_err("%s() Error : unknown hmac type\n", __func__); + rc = -EINVAL; + } + return rc; +} + +/** + * ahash_req_done() - Process a hash result from the SPU hardware. + * @rctx: Crypto request context + * + * Return: 0 if successful + * < 0 if an error + */ +static int ahash_req_done(struct iproc_reqctx_s *rctx) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct crypto_async_request *areq = rctx->parent; + struct ahash_request *req = ahash_request_cast(areq); + struct iproc_ctx_s *ctx = rctx->ctx; + int err; + + memcpy(req->result, rctx->msg_buf.digest, ctx->digestsize); + + if (spu->spu_type == SPU_TYPE_SPUM) { + /* byte swap the output from the UPDT function to network byte + * order + */ + if (ctx->auth.alg == HASH_ALG_MD5) { + __swab32s((u32 *)req->result); + __swab32s(((u32 *)req->result) + 1); + __swab32s(((u32 *)req->result) + 2); + __swab32s(((u32 *)req->result) + 3); + __swab32s(((u32 *)req->result) + 4); + } + } + + flow_dump(" digest ", req->result, ctx->digestsize); + + /* if this an HMAC then do the outer hash */ + if (rctx->is_sw_hmac) { + err = spu_hmac_outer_hash(req, ctx); + if (err < 0) + return err; + flow_dump(" hmac: ", req->result, ctx->digestsize); + } + + if (rctx->is_sw_hmac || ctx->auth.mode == HASH_MODE_HMAC) { + atomic_inc(&iproc_priv.op_counts[SPU_OP_HMAC]); + atomic_inc(&iproc_priv.hmac_cnt[ctx->auth.alg]); + } else { + atomic_inc(&iproc_priv.op_counts[SPU_OP_HASH]); + atomic_inc(&iproc_priv.hash_cnt[ctx->auth.alg]); + } + + return 0; +} + +/** + * handle_ahash_resp() - Process a SPU response message for a hash request. + * Checks if the entire crypto API request has been processed, and if so, + * invokes post processing on the result. + * @rctx: Crypto request context + */ +static void handle_ahash_resp(struct iproc_reqctx_s *rctx) +{ + struct iproc_ctx_s *ctx = rctx->ctx; +#ifdef DEBUG + struct crypto_async_request *areq = rctx->parent; + struct ahash_request *req = ahash_request_cast(areq); + struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); + unsigned int blocksize = + crypto_tfm_alg_blocksize(crypto_ahash_tfm(ahash)); +#endif + /* + * Save hash to use as input to next op if incremental. Might be copying + * too much, but that's easier than figuring out actual digest size here + */ + memcpy(rctx->incr_hash, rctx->msg_buf.digest, MAX_DIGEST_SIZE); + + flow_log("%s() blocksize:%u digestsize:%u\n", + __func__, blocksize, ctx->digestsize); + + atomic64_add(ctx->digestsize, &iproc_priv.bytes_in); + + if (rctx->is_final && (rctx->total_sent == rctx->total_todo)) + ahash_req_done(rctx); +} + +/** + * spu_aead_rx_sg_create() - Build up the scatterlist of buffers used to receive + * a SPU response message for an AEAD request. Includes buffers to catch SPU + * message headers and the response data. + * @mssg: mailbox message containing the receive sg + * @rctx: crypto request context + * @rx_frag_num: number of scatterlist elements required to hold the + * SPU response message + * @assoc_len: Length of associated data included in the crypto request + * @ret_iv_len: Length of IV returned in response + * @resp_len: Number of bytes of response data expected to be written to + * dst buffer from crypto API + * @digestsize: Length of hash digest, in bytes + * @stat_pad_len: Number of bytes required to pad the STAT field to + * a 4-byte boundary + * + * The scatterlist that gets allocated here is freed in spu_chunk_cleanup() + * when the request completes, whether the request is handled successfully or + * there is an error. + * + * Returns: + * 0 if successful + * < 0 if an error + */ +static int spu_aead_rx_sg_create(struct brcm_message *mssg, + struct aead_request *req, + struct iproc_reqctx_s *rctx, + u8 rx_frag_num, + unsigned int assoc_len, + u32 ret_iv_len, unsigned int resp_len, + unsigned int digestsize, u32 stat_pad_len) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct scatterlist *sg; /* used to build sgs in mbox message */ + struct iproc_ctx_s *ctx = rctx->ctx; + u32 datalen; /* Number of bytes of response data expected */ + u32 assoc_buf_len; + u8 data_padlen = 0; + + if (ctx->is_rfc4543) { + /* RFC4543: only pad after data, not after AAD */ + data_padlen = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode, + assoc_len + resp_len); + assoc_buf_len = assoc_len; + } else { + data_padlen = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode, + resp_len); + assoc_buf_len = spu->spu_assoc_resp_len(ctx->cipher.mode, + assoc_len, ret_iv_len, + rctx->is_encrypt); + } + + if (ctx->cipher.mode == CIPHER_MODE_CCM) + /* ICV (after data) must be in the next 32-bit word for CCM */ + data_padlen += spu->spu_wordalign_padlen(assoc_buf_len + + resp_len + + data_padlen); + + if (data_padlen) + /* have to catch gcm pad in separate buffer */ + rx_frag_num++; + + mssg->spu.dst = kcalloc(rx_frag_num, sizeof(struct scatterlist), + rctx->gfp); + if (!mssg->spu.dst) + return -ENOMEM; + + sg = mssg->spu.dst; + sg_init_table(sg, rx_frag_num); + + /* Space for SPU message header */ + sg_set_buf(sg++, rctx->msg_buf.spu_resp_hdr, ctx->spu_resp_hdr_len); + + if (assoc_buf_len) { + /* + * Don't write directly to req->dst, because SPU may pad the + * assoc data in the response + */ + memset(rctx->msg_buf.a.resp_aad, 0, assoc_buf_len); + sg_set_buf(sg++, rctx->msg_buf.a.resp_aad, assoc_buf_len); + } + + if (resp_len) { + /* + * Copy in each dst sg entry from request, up to chunksize. + * dst sg catches just the data. digest caught in separate buf. + */ + datalen = spu_msg_sg_add(&sg, &rctx->dst_sg, &rctx->dst_skip, + rctx->dst_nents, resp_len); + if (datalen < (resp_len)) { + pr_err("%s(): failed to copy dst sg to mbox msg. expected len %u, datalen %u", + __func__, resp_len, datalen); + return -EFAULT; + } + } + + /* If GCM/CCM data is padded, catch padding in separate buffer */ + if (data_padlen) { + memset(rctx->msg_buf.a.gcmpad, 0, data_padlen); + sg_set_buf(sg++, rctx->msg_buf.a.gcmpad, data_padlen); + } + + /* Always catch ICV in separate buffer */ + sg_set_buf(sg++, rctx->msg_buf.digest, digestsize); + + flow_log("stat_pad_len %u\n", stat_pad_len); + if (stat_pad_len) { + memset(rctx->msg_buf.rx_stat_pad, 0, stat_pad_len); + sg_set_buf(sg++, rctx->msg_buf.rx_stat_pad, stat_pad_len); + } + + memset(rctx->msg_buf.rx_stat, 0, SPU_RX_STATUS_LEN); + sg_set_buf(sg, rctx->msg_buf.rx_stat, spu->spu_rx_status_len()); + + return 0; +} + +/** + * spu_aead_tx_sg_create() - Build up the scatterlist of buffers used to send a + * SPU request message for an AEAD request. Includes SPU message headers and the + * request data. + * @mssg: mailbox message containing the transmit sg + * @rctx: crypto request context + * @tx_frag_num: number of scatterlist elements required to construct the + * SPU request message + * @spu_hdr_len: length of SPU message header in bytes + * @assoc: crypto API associated data scatterlist + * @assoc_len: length of associated data + * @assoc_nents: number of scatterlist entries containing assoc data + * @aead_iv_len: length of AEAD IV, if included + * @chunksize: Number of bytes of request data + * @aad_pad_len: Number of bytes of padding at end of AAD. For GCM/CCM. + * @pad_len: Number of pad bytes + * @incl_icv: If true, write separate ICV buffer after data and + * any padding + * + * The scatterlist that gets allocated here is freed in spu_chunk_cleanup() + * when the request completes, whether the request is handled successfully or + * there is an error. + * + * Return: + * 0 if successful + * < 0 if an error + */ +static int spu_aead_tx_sg_create(struct brcm_message *mssg, + struct iproc_reqctx_s *rctx, + u8 tx_frag_num, + u32 spu_hdr_len, + struct scatterlist *assoc, + unsigned int assoc_len, + int assoc_nents, + unsigned int aead_iv_len, + unsigned int chunksize, + u32 aad_pad_len, u32 pad_len, bool incl_icv) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct scatterlist *sg; /* used to build sgs in mbox message */ + struct scatterlist *assoc_sg = assoc; + struct iproc_ctx_s *ctx = rctx->ctx; + u32 datalen; /* Number of bytes of data to write */ + u32 written; /* Number of bytes of data written */ + u32 assoc_offset = 0; + u32 stat_len; + + mssg->spu.src = kcalloc(tx_frag_num, sizeof(struct scatterlist), + rctx->gfp); + if (!mssg->spu.src) + return -ENOMEM; + + sg = mssg->spu.src; + sg_init_table(sg, tx_frag_num); + + sg_set_buf(sg++, rctx->msg_buf.bcm_spu_req_hdr, + BCM_HDR_LEN + spu_hdr_len); + + if (assoc_len) { + /* Copy in each associated data sg entry from request */ + written = spu_msg_sg_add(&sg, &assoc_sg, &assoc_offset, + assoc_nents, assoc_len); + if (written < assoc_len) { + pr_err("%s(): failed to copy assoc sg to mbox msg", + __func__); + return -EFAULT; + } + } + + if (aead_iv_len) + sg_set_buf(sg++, rctx->msg_buf.iv_ctr, aead_iv_len); + + if (aad_pad_len) { + memset(rctx->msg_buf.a.req_aad_pad, 0, aad_pad_len); + sg_set_buf(sg++, rctx->msg_buf.a.req_aad_pad, aad_pad_len); + } + + datalen = chunksize; + if ((chunksize > ctx->digestsize) && incl_icv) + datalen -= ctx->digestsize; + if (datalen) { + /* For aead, a single msg should consume the entire src sg */ + written = spu_msg_sg_add(&sg, &rctx->src_sg, &rctx->src_skip, + rctx->src_nents, datalen); + if (written < datalen) { + pr_err("%s(): failed to copy src sg to mbox msg", + __func__); + return -EFAULT; + } + } + + if (pad_len) { + memset(rctx->msg_buf.spu_req_pad, 0, pad_len); + sg_set_buf(sg++, rctx->msg_buf.spu_req_pad, pad_len); + } + + if (incl_icv) + sg_set_buf(sg++, rctx->msg_buf.digest, ctx->digestsize); + + stat_len = spu->spu_tx_status_len(); + if (stat_len) { + memset(rctx->msg_buf.tx_stat, 0, stat_len); + sg_set_buf(sg, rctx->msg_buf.tx_stat, stat_len); + } + return 0; +} + +/** + * handle_aead_req() - Submit a SPU request message for the next chunk of the + * current AEAD request. + * @rctx: Crypto request context + * + * Unlike other operation types, we assume the length of the request fits in + * a single SPU request message. aead_enqueue() makes sure this is true. + * Comments for other op types regarding threads applies here as well. + * + * Unlike incremental hash ops, where the spu returns the entire hash for + * truncated algs like sha-224, the SPU returns just the truncated hash in + * response to aead requests. So digestsize is always ctx->digestsize here. + * + * Return: -EINPROGRESS: crypto request has been accepted and result will be + * returned asynchronously + * Any other value indicates an error + */ +static int handle_aead_req(struct iproc_reqctx_s *rctx) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct crypto_async_request *areq = rctx->parent; + struct aead_request *req = container_of(areq, + struct aead_request, base); + struct iproc_ctx_s *ctx = rctx->ctx; + int err; + unsigned int chunksize; + unsigned int resp_len; + u32 spu_hdr_len; + u32 db_size; + u32 stat_pad_len; + u32 pad_len; + struct brcm_message *mssg; /* mailbox message */ + struct spu_request_opts req_opts; + struct spu_cipher_parms cipher_parms; + struct spu_hash_parms hash_parms; + struct spu_aead_parms aead_parms; + int assoc_nents = 0; + bool incl_icv = false; + unsigned int digestsize = ctx->digestsize; + int retry_cnt = 0; + + /* number of entries in src and dst sg. Always includes SPU msg header. + */ + u8 rx_frag_num = 2; /* and STATUS */ + u8 tx_frag_num = 1; + + /* doing the whole thing at once */ + chunksize = rctx->total_todo; + + flow_log("%s: chunksize %u\n", __func__, chunksize); + + memset(&req_opts, 0, sizeof(req_opts)); + memset(&hash_parms, 0, sizeof(hash_parms)); + memset(&aead_parms, 0, sizeof(aead_parms)); + + req_opts.is_inbound = !(rctx->is_encrypt); + req_opts.auth_first = ctx->auth_first; + req_opts.is_aead = true; + req_opts.is_esp = ctx->is_esp; + + cipher_parms.alg = ctx->cipher.alg; + cipher_parms.mode = ctx->cipher.mode; + cipher_parms.type = ctx->cipher_type; + cipher_parms.key_buf = ctx->enckey; + cipher_parms.key_len = ctx->enckeylen; + cipher_parms.iv_buf = rctx->msg_buf.iv_ctr; + cipher_parms.iv_len = rctx->iv_ctr_len; + + hash_parms.alg = ctx->auth.alg; + hash_parms.mode = ctx->auth.mode; + hash_parms.type = HASH_TYPE_NONE; + hash_parms.key_buf = (u8 *)ctx->authkey; + hash_parms.key_len = ctx->authkeylen; + hash_parms.digestsize = digestsize; + + if ((ctx->auth.alg == HASH_ALG_SHA224) && + (ctx->authkeylen < SHA224_DIGEST_SIZE)) + hash_parms.key_len = SHA224_DIGEST_SIZE; + + aead_parms.assoc_size = req->assoclen; + if (ctx->is_esp && !ctx->is_rfc4543) { + /* + * 8-byte IV is included assoc data in request. SPU2 + * expects AAD to include just SPI and seqno. So + * subtract off the IV len. + */ + aead_parms.assoc_size -= GCM_ESP_IV_SIZE; + + if (rctx->is_encrypt) { + aead_parms.return_iv = true; + aead_parms.ret_iv_len = GCM_ESP_IV_SIZE; + aead_parms.ret_iv_off = GCM_ESP_SALT_SIZE; + } + } else { + aead_parms.ret_iv_len = 0; + } + + /* + * Count number of sg entries from the crypto API request that are to + * be included in this mailbox message. For dst sg, don't count space + * for digest. Digest gets caught in a separate buffer and copied back + * to dst sg when processing response. + */ + rctx->src_nents = spu_sg_count(rctx->src_sg, rctx->src_skip, chunksize); + rctx->dst_nents = spu_sg_count(rctx->dst_sg, rctx->dst_skip, chunksize); + if (aead_parms.assoc_size) + assoc_nents = spu_sg_count(rctx->assoc, 0, + aead_parms.assoc_size); + + mssg = &rctx->mb_mssg; + + rctx->total_sent = chunksize; + rctx->src_sent = chunksize; + if (spu->spu_assoc_resp_len(ctx->cipher.mode, + aead_parms.assoc_size, + aead_parms.ret_iv_len, + rctx->is_encrypt)) + rx_frag_num++; + + aead_parms.iv_len = spu->spu_aead_ivlen(ctx->cipher.mode, + rctx->iv_ctr_len); + + if (ctx->auth.alg == HASH_ALG_AES) + hash_parms.type = ctx->cipher_type; + + /* General case AAD padding (CCM and RFC4543 special cases below) */ + aead_parms.aad_pad_len = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode, + aead_parms.assoc_size); + + /* General case data padding (CCM decrypt special case below) */ + aead_parms.data_pad_len = spu->spu_gcm_ccm_pad_len(ctx->cipher.mode, + chunksize); + + if (ctx->cipher.mode == CIPHER_MODE_CCM) { + /* + * for CCM, AAD len + 2 (rather than AAD len) needs to be + * 128-bit aligned + */ + aead_parms.aad_pad_len = spu->spu_gcm_ccm_pad_len( + ctx->cipher.mode, + aead_parms.assoc_size + 2); + + /* + * And when decrypting CCM, need to pad without including + * size of ICV which is tacked on to end of chunk + */ + if (!rctx->is_encrypt) + aead_parms.data_pad_len = + spu->spu_gcm_ccm_pad_len(ctx->cipher.mode, + chunksize - digestsize); + + /* CCM also requires software to rewrite portions of IV: */ + spu->spu_ccm_update_iv(digestsize, &cipher_parms, req->assoclen, + chunksize, rctx->is_encrypt, + ctx->is_esp); + } + + if (ctx->is_rfc4543) { + /* + * RFC4543: data is included in AAD, so don't pad after AAD + * and pad data based on both AAD + data size + */ + aead_parms.aad_pad_len = 0; + if (!rctx->is_encrypt) + aead_parms.data_pad_len = spu->spu_gcm_ccm_pad_len( + ctx->cipher.mode, + aead_parms.assoc_size + chunksize - + digestsize); + else + aead_parms.data_pad_len = spu->spu_gcm_ccm_pad_len( + ctx->cipher.mode, + aead_parms.assoc_size + chunksize); + + req_opts.is_rfc4543 = true; + } + + if (spu_req_incl_icv(ctx->cipher.mode, rctx->is_encrypt)) { + incl_icv = true; + tx_frag_num++; + /* Copy ICV from end of src scatterlist to digest buf */ + sg_copy_part_to_buf(req->src, rctx->msg_buf.digest, digestsize, + req->assoclen + rctx->total_sent - + digestsize); + } + + atomic64_add(chunksize, &iproc_priv.bytes_out); + + flow_log("%s()-sent chunksize:%u\n", __func__, chunksize); + + /* Prepend SPU header with type 3 BCM header */ + memcpy(rctx->msg_buf.bcm_spu_req_hdr, BCMHEADER, BCM_HDR_LEN); + + spu_hdr_len = spu->spu_create_request(rctx->msg_buf.bcm_spu_req_hdr + + BCM_HDR_LEN, &req_opts, + &cipher_parms, &hash_parms, + &aead_parms, chunksize); + + /* Determine total length of padding. Put all padding in one buffer. */ + db_size = spu_real_db_size(aead_parms.assoc_size, aead_parms.iv_len, 0, + chunksize, aead_parms.aad_pad_len, + aead_parms.data_pad_len, 0); + + stat_pad_len = spu->spu_wordalign_padlen(db_size); + + if (stat_pad_len) + rx_frag_num++; + pad_len = aead_parms.data_pad_len + stat_pad_len; + if (pad_len) { + tx_frag_num++; + spu->spu_request_pad(rctx->msg_buf.spu_req_pad, + aead_parms.data_pad_len, 0, + ctx->auth.alg, ctx->auth.mode, + rctx->total_sent, stat_pad_len); + } + + spu->spu_dump_msg_hdr(rctx->msg_buf.bcm_spu_req_hdr + BCM_HDR_LEN, + spu_hdr_len); + dump_sg(rctx->assoc, 0, aead_parms.assoc_size); + packet_dump(" aead iv: ", rctx->msg_buf.iv_ctr, aead_parms.iv_len); + packet_log("BD:\n"); + dump_sg(rctx->src_sg, rctx->src_skip, chunksize); + packet_dump(" pad: ", rctx->msg_buf.spu_req_pad, pad_len); + + /* + * Build mailbox message containing SPU request msg and rx buffers + * to catch response message + */ + memset(mssg, 0, sizeof(*mssg)); + mssg->type = BRCM_MESSAGE_SPU; + mssg->ctx = rctx; /* Will be returned in response */ + + /* Create rx scatterlist to catch result */ + rx_frag_num += rctx->dst_nents; + resp_len = chunksize; + + /* + * Always catch ICV in separate buffer. Have to for GCM/CCM because of + * padding. Have to for SHA-224 and other truncated SHAs because SPU + * sends entire digest back. + */ + rx_frag_num++; + + if (((ctx->cipher.mode == CIPHER_MODE_GCM) || + (ctx->cipher.mode == CIPHER_MODE_CCM)) && !rctx->is_encrypt) { + /* + * Input is ciphertxt plus ICV, but ICV not incl + * in output. + */ + resp_len -= ctx->digestsize; + if (resp_len == 0) + /* no rx frags to catch output data */ + rx_frag_num -= rctx->dst_nents; + } + + err = spu_aead_rx_sg_create(mssg, req, rctx, rx_frag_num, + aead_parms.assoc_size, + aead_parms.ret_iv_len, resp_len, digestsize, + stat_pad_len); + if (err) + return err; + + /* Create tx scatterlist containing SPU request message */ + tx_frag_num += rctx->src_nents; + tx_frag_num += assoc_nents; + if (aead_parms.aad_pad_len) + tx_frag_num++; + if (aead_parms.iv_len) + tx_frag_num++; + if (spu->spu_tx_status_len()) + tx_frag_num++; + err = spu_aead_tx_sg_create(mssg, rctx, tx_frag_num, spu_hdr_len, + rctx->assoc, aead_parms.assoc_size, + assoc_nents, aead_parms.iv_len, chunksize, + aead_parms.aad_pad_len, pad_len, incl_icv); + if (err) + return err; + + err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], mssg); + if (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) { + while ((err == -ENOBUFS) && (retry_cnt < SPU_MB_RETRY_MAX)) { + /* + * Mailbox queue is full. Since MAY_SLEEP is set, assume + * not in atomic context and we can wait and try again. + */ + retry_cnt++; + usleep_range(MBOX_SLEEP_MIN, MBOX_SLEEP_MAX); + err = mbox_send_message(iproc_priv.mbox[rctx->chan_idx], + mssg); + atomic_inc(&iproc_priv.mb_no_spc); + } + } + if (err < 0) { + atomic_inc(&iproc_priv.mb_send_fail); + return err; + } + + return -EINPROGRESS; +} + +/** + * handle_aead_resp() - Process a SPU response message for an AEAD request. + * @rctx: Crypto request context + */ +static void handle_aead_resp(struct iproc_reqctx_s *rctx) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct crypto_async_request *areq = rctx->parent; + struct aead_request *req = container_of(areq, + struct aead_request, base); + struct iproc_ctx_s *ctx = rctx->ctx; + u32 payload_len; + unsigned int icv_offset; + u32 result_len; + + /* See how much data was returned */ + payload_len = spu->spu_payload_length(rctx->msg_buf.spu_resp_hdr); + flow_log("payload_len %u\n", payload_len); + + /* only count payload */ + atomic64_add(payload_len, &iproc_priv.bytes_in); + + if (req->assoclen) + packet_dump(" assoc_data ", rctx->msg_buf.a.resp_aad, + req->assoclen); + + /* + * Copy the ICV back to the destination + * buffer. In decrypt case, SPU gives us back the digest, but crypto + * API doesn't expect ICV in dst buffer. + */ + result_len = req->cryptlen; + if (rctx->is_encrypt) { + icv_offset = req->assoclen + rctx->total_sent; + packet_dump(" ICV: ", rctx->msg_buf.digest, ctx->digestsize); + flow_log("copying ICV to dst sg at offset %u\n", icv_offset); + sg_copy_part_from_buf(req->dst, rctx->msg_buf.digest, + ctx->digestsize, icv_offset); + result_len += ctx->digestsize; + } + + packet_log("response data: "); + dump_sg(req->dst, req->assoclen, result_len); + + atomic_inc(&iproc_priv.op_counts[SPU_OP_AEAD]); + if (ctx->cipher.alg == CIPHER_ALG_AES) { + if (ctx->cipher.mode == CIPHER_MODE_CCM) + atomic_inc(&iproc_priv.aead_cnt[AES_CCM]); + else if (ctx->cipher.mode == CIPHER_MODE_GCM) + atomic_inc(&iproc_priv.aead_cnt[AES_GCM]); + else + atomic_inc(&iproc_priv.aead_cnt[AUTHENC]); + } else { + atomic_inc(&iproc_priv.aead_cnt[AUTHENC]); + } +} + +/** + * spu_chunk_cleanup() - Do cleanup after processing one chunk of a request + * @rctx: request context + * + * Mailbox scatterlists are allocated for each chunk. So free them after + * processing each chunk. + */ +static void spu_chunk_cleanup(struct iproc_reqctx_s *rctx) +{ + /* mailbox message used to tx request */ + struct brcm_message *mssg = &rctx->mb_mssg; + + kfree(mssg->spu.src); + kfree(mssg->spu.dst); + memset(mssg, 0, sizeof(struct brcm_message)); +} + +/** + * finish_req() - Used to invoke the complete callback from the requester when + * a request has been handled asynchronously. + * @rctx: Request context + * @err: Indicates whether the request was successful or not + * + * Ensures that cleanup has been done for request + */ +static void finish_req(struct iproc_reqctx_s *rctx, int err) +{ + struct crypto_async_request *areq = rctx->parent; + + flow_log("%s() err:%d\n\n", __func__, err); + + /* No harm done if already called */ + spu_chunk_cleanup(rctx); + + if (areq) + areq->complete(areq, err); +} + +/** + * spu_rx_callback() - Callback from mailbox framework with a SPU response. + * @cl: mailbox client structure for SPU driver + * @msg: mailbox message containing SPU response + */ +static void spu_rx_callback(struct mbox_client *cl, void *msg) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct brcm_message *mssg = msg; + struct iproc_reqctx_s *rctx; + struct iproc_ctx_s *ctx; + struct crypto_async_request *areq; + int err = 0; + + rctx = mssg->ctx; + if (unlikely(!rctx)) { + /* This is fatal */ + pr_err("%s(): no request context", __func__); + err = -EFAULT; + goto cb_finish; + } + areq = rctx->parent; + ctx = rctx->ctx; + + /* process the SPU status */ + err = spu->spu_status_process(rctx->msg_buf.rx_stat); + if (err != 0) { + if (err == SPU_INVALID_ICV) + atomic_inc(&iproc_priv.bad_icv); + err = -EBADMSG; + goto cb_finish; + } + + /* Process the SPU response message */ + switch (rctx->ctx->alg->type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + handle_ablkcipher_resp(rctx); + break; + case CRYPTO_ALG_TYPE_AHASH: + handle_ahash_resp(rctx); + break; + case CRYPTO_ALG_TYPE_AEAD: + handle_aead_resp(rctx); + break; + default: + err = -EINVAL; + goto cb_finish; + } + + /* + * If this response does not complete the request, then send the next + * request chunk. + */ + if (rctx->total_sent < rctx->total_todo) { + /* Deallocate anything specific to previous chunk */ + spu_chunk_cleanup(rctx); + + switch (rctx->ctx->alg->type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + err = handle_ablkcipher_req(rctx); + break; + case CRYPTO_ALG_TYPE_AHASH: + err = handle_ahash_req(rctx); + if (err == -EAGAIN) + /* + * we saved data in hash carry, but tell crypto + * API we successfully completed request. + */ + err = 0; + break; + case CRYPTO_ALG_TYPE_AEAD: + err = handle_aead_req(rctx); + break; + default: + err = -EINVAL; + } + + if (err == -EINPROGRESS) + /* Successfully submitted request for next chunk */ + return; + } + +cb_finish: + finish_req(rctx, err); +} + +/* ==================== Kernel Cryptographic API ==================== */ + +/** + * ablkcipher_enqueue() - Handle ablkcipher encrypt or decrypt request. + * @req: Crypto API request + * @encrypt: true if encrypting; false if decrypting + * + * Return: -EINPROGRESS if request accepted and result will be returned + * asynchronously + * < 0 if an error + */ +static int ablkcipher_enqueue(struct ablkcipher_request *req, bool encrypt) +{ + struct iproc_reqctx_s *rctx = ablkcipher_request_ctx(req); + struct iproc_ctx_s *ctx = + crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); + int err; + + flow_log("%s() enc:%u\n", __func__, encrypt); + + rctx->gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; + rctx->parent = &req->base; + rctx->is_encrypt = encrypt; + rctx->bd_suppress = false; + rctx->total_todo = req->nbytes; + rctx->src_sent = 0; + rctx->total_sent = 0; + rctx->total_received = 0; + rctx->ctx = ctx; + + /* Initialize current position in src and dst scatterlists */ + rctx->src_sg = req->src; + rctx->src_nents = 0; + rctx->src_skip = 0; + rctx->dst_sg = req->dst; + rctx->dst_nents = 0; + rctx->dst_skip = 0; + + if (ctx->cipher.mode == CIPHER_MODE_CBC || + ctx->cipher.mode == CIPHER_MODE_CTR || + ctx->cipher.mode == CIPHER_MODE_OFB || + ctx->cipher.mode == CIPHER_MODE_XTS || + ctx->cipher.mode == CIPHER_MODE_GCM || + ctx->cipher.mode == CIPHER_MODE_CCM) { + rctx->iv_ctr_len = + crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(req)); + memcpy(rctx->msg_buf.iv_ctr, req->info, rctx->iv_ctr_len); + } else { + rctx->iv_ctr_len = 0; + } + + /* Choose a SPU to process this request */ + rctx->chan_idx = select_channel(); + err = handle_ablkcipher_req(rctx); + if (err != -EINPROGRESS) + /* synchronous result */ + spu_chunk_cleanup(rctx); + + return err; +} + +static int des_setkey(struct crypto_ablkcipher *cipher, const u8 *key, + unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher); + u32 tmp[DES_EXPKEY_WORDS]; + + if (keylen == DES_KEY_SIZE) { + if (des_ekey(tmp, key) == 0) { + if (crypto_ablkcipher_get_flags(cipher) & + CRYPTO_TFM_REQ_WEAK_KEY) { + u32 flags = CRYPTO_TFM_RES_WEAK_KEY; + + crypto_ablkcipher_set_flags(cipher, flags); + return -EINVAL; + } + } + + ctx->cipher_type = CIPHER_TYPE_DES; + } else { + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + return 0; +} + +static int threedes_setkey(struct crypto_ablkcipher *cipher, const u8 *key, + unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher); + + if (keylen == (DES_KEY_SIZE * 3)) { + const u32 *K = (const u32 *)key; + u32 flags = CRYPTO_TFM_RES_BAD_KEY_SCHED; + + if (!((K[0] ^ K[2]) | (K[1] ^ K[3])) || + !((K[2] ^ K[4]) | (K[3] ^ K[5]))) { + crypto_ablkcipher_set_flags(cipher, flags); + return -EINVAL; + } + + ctx->cipher_type = CIPHER_TYPE_3DES; + } else { + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + return 0; +} + +static int aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key, + unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher); + + if (ctx->cipher.mode == CIPHER_MODE_XTS) + /* XTS includes two keys of equal length */ + keylen = keylen / 2; + + switch (keylen) { + case AES_KEYSIZE_128: + ctx->cipher_type = CIPHER_TYPE_AES128; + break; + case AES_KEYSIZE_192: + ctx->cipher_type = CIPHER_TYPE_AES192; + break; + case AES_KEYSIZE_256: + ctx->cipher_type = CIPHER_TYPE_AES256; + break; + default: + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + WARN_ON((ctx->max_payload != SPU_MAX_PAYLOAD_INF) && + ((ctx->max_payload % AES_BLOCK_SIZE) != 0)); + return 0; +} + +static int rc4_setkey(struct crypto_ablkcipher *cipher, const u8 *key, + unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher); + int i; + + ctx->enckeylen = ARC4_MAX_KEY_SIZE + ARC4_STATE_SIZE; + + ctx->enckey[0] = 0x00; /* 0x00 */ + ctx->enckey[1] = 0x00; /* i */ + ctx->enckey[2] = 0x00; /* 0x00 */ + ctx->enckey[3] = 0x00; /* j */ + for (i = 0; i < ARC4_MAX_KEY_SIZE; i++) + ctx->enckey[i + ARC4_STATE_SIZE] = key[i % keylen]; + + ctx->cipher_type = CIPHER_TYPE_INIT; + + return 0; +} + +static int ablkcipher_setkey(struct crypto_ablkcipher *cipher, const u8 *key, + unsigned int keylen) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct iproc_ctx_s *ctx = crypto_ablkcipher_ctx(cipher); + struct spu_cipher_parms cipher_parms; + u32 alloc_len = 0; + int err; + + flow_log("ablkcipher_setkey() keylen: %d\n", keylen); + flow_dump(" key: ", key, keylen); + + switch (ctx->cipher.alg) { + case CIPHER_ALG_DES: + err = des_setkey(cipher, key, keylen); + break; + case CIPHER_ALG_3DES: + err = threedes_setkey(cipher, key, keylen); + break; + case CIPHER_ALG_AES: + err = aes_setkey(cipher, key, keylen); + break; + case CIPHER_ALG_RC4: + err = rc4_setkey(cipher, key, keylen); + break; + default: + pr_err("%s() Error: unknown cipher alg\n", __func__); + err = -EINVAL; + } + if (err) + return err; + + /* RC4 already populated ctx->enkey */ + if (ctx->cipher.alg != CIPHER_ALG_RC4) { + memcpy(ctx->enckey, key, keylen); + ctx->enckeylen = keylen; + } + /* SPU needs XTS keys in the reverse order the crypto API presents */ + if ((ctx->cipher.alg == CIPHER_ALG_AES) && + (ctx->cipher.mode == CIPHER_MODE_XTS)) { + unsigned int xts_keylen = keylen / 2; + + memcpy(ctx->enckey, key + xts_keylen, xts_keylen); + memcpy(ctx->enckey + xts_keylen, key, xts_keylen); + } + + if (spu->spu_type == SPU_TYPE_SPUM) + alloc_len = BCM_HDR_LEN + SPU_HEADER_ALLOC_LEN; + else if (spu->spu_type == SPU_TYPE_SPU2) + alloc_len = BCM_HDR_LEN + SPU2_HEADER_ALLOC_LEN; + memset(ctx->bcm_spu_req_hdr, 0, alloc_len); + cipher_parms.iv_buf = NULL; + cipher_parms.iv_len = crypto_ablkcipher_ivsize(cipher); + flow_log("%s: iv_len %u\n", __func__, cipher_parms.iv_len); + + cipher_parms.alg = ctx->cipher.alg; + cipher_parms.mode = ctx->cipher.mode; + cipher_parms.type = ctx->cipher_type; + cipher_parms.key_buf = ctx->enckey; + cipher_parms.key_len = ctx->enckeylen; + + /* Prepend SPU request message with BCM header */ + memcpy(ctx->bcm_spu_req_hdr, BCMHEADER, BCM_HDR_LEN); + ctx->spu_req_hdr_len = + spu->spu_cipher_req_init(ctx->bcm_spu_req_hdr + BCM_HDR_LEN, + &cipher_parms); + + ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen, + ctx->enckeylen, + false); + + atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_CIPHER]); + + return 0; +} + +static int ablkcipher_encrypt(struct ablkcipher_request *req) +{ + flow_log("ablkcipher_encrypt() nbytes:%u\n", req->nbytes); + + return ablkcipher_enqueue(req, true); +} + +static int ablkcipher_decrypt(struct ablkcipher_request *req) +{ + flow_log("ablkcipher_decrypt() nbytes:%u\n", req->nbytes); + return ablkcipher_enqueue(req, false); +} + +static int ahash_enqueue(struct ahash_request *req) +{ + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + int err = 0; + const char *alg_name; + + flow_log("ahash_enqueue() nbytes:%u\n", req->nbytes); + + rctx->gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; + rctx->parent = &req->base; + rctx->ctx = ctx; + rctx->bd_suppress = true; + memset(&rctx->mb_mssg, 0, sizeof(struct brcm_message)); + + /* Initialize position in src scatterlist */ + rctx->src_sg = req->src; + rctx->src_skip = 0; + rctx->src_nents = 0; + rctx->dst_sg = NULL; + rctx->dst_skip = 0; + rctx->dst_nents = 0; + + /* SPU2 hardware does not compute hash of zero length data */ + if ((rctx->is_final == 1) && (rctx->total_todo == 0) && + (iproc_priv.spu.spu_type == SPU_TYPE_SPU2)) { + alg_name = crypto_tfm_alg_name(crypto_ahash_tfm(tfm)); + flow_log("Doing %sfinal %s zero-len hash request in software\n", + rctx->is_final ? "" : "non-", alg_name); + err = do_shash((unsigned char *)alg_name, req->result, + NULL, 0, NULL, 0, ctx->authkey, + ctx->authkeylen); + if (err < 0) + flow_log("Hash request failed with error %d\n", err); + return err; + } + /* Choose a SPU to process this request */ + rctx->chan_idx = select_channel(); + + err = handle_ahash_req(rctx); + if (err != -EINPROGRESS) + /* synchronous result */ + spu_chunk_cleanup(rctx); + + if (err == -EAGAIN) + /* + * we saved data in hash carry, but tell crypto API + * we successfully completed request. + */ + err = 0; + + return err; +} + +static int __ahash_init(struct ahash_request *req) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + + flow_log("%s()\n", __func__); + + /* Initialize the context */ + rctx->hash_carry_len = 0; + rctx->is_final = 0; + + rctx->total_todo = 0; + rctx->src_sent = 0; + rctx->total_sent = 0; + rctx->total_received = 0; + + ctx->digestsize = crypto_ahash_digestsize(tfm); + /* If we add a hash whose digest is larger, catch it here. */ + WARN_ON(ctx->digestsize > MAX_DIGEST_SIZE); + + rctx->is_sw_hmac = false; + + ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen, 0, + true); + + return 0; +} + +/** + * spu_no_incr_hash() - Determine whether incremental hashing is supported. + * @ctx: Crypto session context + * + * SPU-2 does not support incremental hashing (we'll have to revisit and + * condition based on chip revision or device tree entry if future versions do + * support incremental hash) + * + * SPU-M also doesn't support incremental hashing of AES-XCBC + * + * Return: true if incremental hashing is not supported + * false otherwise + */ +bool spu_no_incr_hash(struct iproc_ctx_s *ctx) +{ + struct spu_hw *spu = &iproc_priv.spu; + + if (spu->spu_type == SPU_TYPE_SPU2) + return true; + + if ((ctx->auth.alg == HASH_ALG_AES) && + (ctx->auth.mode == HASH_MODE_XCBC)) + return true; + + /* Otherwise, incremental hashing is supported */ + return false; +} + +static int ahash_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + const char *alg_name; + struct crypto_shash *hash; + int ret; + gfp_t gfp; + + if (spu_no_incr_hash(ctx)) { + /* + * If we get an incremental hashing request and it's not + * supported by the hardware, we need to handle it in software + * by calling synchronous hash functions. + */ + alg_name = crypto_tfm_alg_name(crypto_ahash_tfm(tfm)); + hash = crypto_alloc_shash(alg_name, 0, 0); + if (IS_ERR(hash)) { + ret = PTR_ERR(hash); + goto err; + } + + gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; + ctx->shash = kmalloc(sizeof(*ctx->shash) + + crypto_shash_descsize(hash), gfp); + if (!ctx->shash) { + ret = -ENOMEM; + goto err_hash; + } + ctx->shash->tfm = hash; + ctx->shash->flags = 0; + + /* Set the key using data we already have from setkey */ + if (ctx->authkeylen > 0) { + ret = crypto_shash_setkey(hash, ctx->authkey, + ctx->authkeylen); + if (ret) + goto err_shash; + } + + /* Initialize hash w/ this key and other params */ + ret = crypto_shash_init(ctx->shash); + if (ret) + goto err_shash; + } else { + /* Otherwise call the internal function which uses SPU hw */ + ret = __ahash_init(req); + } + + return ret; + +err_shash: + kfree(ctx->shash); +err_hash: + crypto_free_shash(hash); +err: + return ret; +} + +static int __ahash_update(struct ahash_request *req) +{ + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + + flow_log("ahash_update() nbytes:%u\n", req->nbytes); + + if (!req->nbytes) + return 0; + rctx->total_todo += req->nbytes; + rctx->src_sent = 0; + + return ahash_enqueue(req); +} + +static int ahash_update(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + u8 *tmpbuf; + int ret; + int nents; + gfp_t gfp; + + if (spu_no_incr_hash(ctx)) { + /* + * If we get an incremental hashing request and it's not + * supported by the hardware, we need to handle it in software + * by calling synchronous hash functions. + */ + if (req->src) + nents = sg_nents(req->src); + else + return -EINVAL; + + /* Copy data from req scatterlist to tmp buffer */ + gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; + tmpbuf = kmalloc(req->nbytes, gfp); + if (!tmpbuf) + return -ENOMEM; + + if (sg_copy_to_buffer(req->src, nents, tmpbuf, req->nbytes) != + req->nbytes) { + kfree(tmpbuf); + return -EINVAL; + } + + /* Call synchronous update */ + ret = crypto_shash_update(ctx->shash, tmpbuf, req->nbytes); + kfree(tmpbuf); + } else { + /* Otherwise call the internal function which uses SPU hw */ + ret = __ahash_update(req); + } + + return ret; +} + +static int __ahash_final(struct ahash_request *req) +{ + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + + flow_log("ahash_final() nbytes:%u\n", req->nbytes); + + rctx->is_final = 1; + + return ahash_enqueue(req); +} + +static int ahash_final(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + int ret; + + if (spu_no_incr_hash(ctx)) { + /* + * If we get an incremental hashing request and it's not + * supported by the hardware, we need to handle it in software + * by calling synchronous hash functions. + */ + ret = crypto_shash_final(ctx->shash, req->result); + + /* Done with hash, can deallocate it now */ + crypto_free_shash(ctx->shash->tfm); + kfree(ctx->shash); + + } else { + /* Otherwise call the internal function which uses SPU hw */ + ret = __ahash_final(req); + } + + return ret; +} + +static int __ahash_finup(struct ahash_request *req) +{ + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + + flow_log("ahash_finup() nbytes:%u\n", req->nbytes); + + rctx->total_todo += req->nbytes; + rctx->src_sent = 0; + rctx->is_final = 1; + + return ahash_enqueue(req); +} + +static int ahash_finup(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + u8 *tmpbuf; + int ret; + int nents; + gfp_t gfp; + + if (spu_no_incr_hash(ctx)) { + /* + * If we get an incremental hashing request and it's not + * supported by the hardware, we need to handle it in software + * by calling synchronous hash functions. + */ + if (req->src) { + nents = sg_nents(req->src); + } else { + ret = -EINVAL; + goto ahash_finup_exit; + } + + /* Copy data from req scatterlist to tmp buffer */ + gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; + tmpbuf = kmalloc(req->nbytes, gfp); + if (!tmpbuf) { + ret = -ENOMEM; + goto ahash_finup_exit; + } + + if (sg_copy_to_buffer(req->src, nents, tmpbuf, req->nbytes) != + req->nbytes) { + ret = -EINVAL; + goto ahash_finup_free; + } + + /* Call synchronous update */ + ret = crypto_shash_finup(ctx->shash, tmpbuf, req->nbytes, + req->result); + } else { + /* Otherwise call the internal function which uses SPU hw */ + return __ahash_finup(req); + } +ahash_finup_free: + kfree(tmpbuf); + +ahash_finup_exit: + /* Done with hash, can deallocate it now */ + crypto_free_shash(ctx->shash->tfm); + kfree(ctx->shash); + return ret; +} + +static int ahash_digest(struct ahash_request *req) +{ + int err = 0; + + flow_log("ahash_digest() nbytes:%u\n", req->nbytes); + + /* whole thing at once */ + err = __ahash_init(req); + if (!err) + err = __ahash_finup(req); + + return err; +} + +static int ahash_setkey(struct crypto_ahash *ahash, const u8 *key, + unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_ahash_ctx(ahash); + + flow_log("%s() ahash:%p key:%p keylen:%u\n", + __func__, ahash, key, keylen); + flow_dump(" key: ", key, keylen); + + if (ctx->auth.alg == HASH_ALG_AES) { + switch (keylen) { + case AES_KEYSIZE_128: + ctx->cipher_type = CIPHER_TYPE_AES128; + break; + case AES_KEYSIZE_192: + ctx->cipher_type = CIPHER_TYPE_AES192; + break; + case AES_KEYSIZE_256: + ctx->cipher_type = CIPHER_TYPE_AES256; + break; + default: + pr_err("%s() Error: Invalid key length\n", __func__); + return -EINVAL; + } + } else { + pr_err("%s() Error: unknown hash alg\n", __func__); + return -EINVAL; + } + memcpy(ctx->authkey, key, keylen); + ctx->authkeylen = keylen; + + return 0; +} + +static int ahash_export(struct ahash_request *req, void *out) +{ + const struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + struct spu_hash_export_s *spu_exp = (struct spu_hash_export_s *)out; + + spu_exp->total_todo = rctx->total_todo; + spu_exp->total_sent = rctx->total_sent; + spu_exp->is_sw_hmac = rctx->is_sw_hmac; + memcpy(spu_exp->hash_carry, rctx->hash_carry, sizeof(rctx->hash_carry)); + spu_exp->hash_carry_len = rctx->hash_carry_len; + memcpy(spu_exp->incr_hash, rctx->incr_hash, sizeof(rctx->incr_hash)); + + return 0; +} + +static int ahash_import(struct ahash_request *req, const void *in) +{ + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + struct spu_hash_export_s *spu_exp = (struct spu_hash_export_s *)in; + + rctx->total_todo = spu_exp->total_todo; + rctx->total_sent = spu_exp->total_sent; + rctx->is_sw_hmac = spu_exp->is_sw_hmac; + memcpy(rctx->hash_carry, spu_exp->hash_carry, sizeof(rctx->hash_carry)); + rctx->hash_carry_len = spu_exp->hash_carry_len; + memcpy(rctx->incr_hash, spu_exp->incr_hash, sizeof(rctx->incr_hash)); + + return 0; +} + +static int ahash_hmac_setkey(struct crypto_ahash *ahash, const u8 *key, + unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_ahash_ctx(ahash); + unsigned int blocksize = + crypto_tfm_alg_blocksize(crypto_ahash_tfm(ahash)); + unsigned int digestsize = crypto_ahash_digestsize(ahash); + unsigned int index; + int rc; + + flow_log("%s() ahash:%p key:%p keylen:%u blksz:%u digestsz:%u\n", + __func__, ahash, key, keylen, blocksize, digestsize); + flow_dump(" key: ", key, keylen); + + if (keylen > blocksize) { + switch (ctx->auth.alg) { + case HASH_ALG_MD5: + rc = do_shash("md5", ctx->authkey, key, keylen, NULL, + 0, NULL, 0); + break; + case HASH_ALG_SHA1: + rc = do_shash("sha1", ctx->authkey, key, keylen, NULL, + 0, NULL, 0); + break; + case HASH_ALG_SHA224: + rc = do_shash("sha224", ctx->authkey, key, keylen, NULL, + 0, NULL, 0); + break; + case HASH_ALG_SHA256: + rc = do_shash("sha256", ctx->authkey, key, keylen, NULL, + 0, NULL, 0); + break; + case HASH_ALG_SHA384: + rc = do_shash("sha384", ctx->authkey, key, keylen, NULL, + 0, NULL, 0); + break; + case HASH_ALG_SHA512: + rc = do_shash("sha512", ctx->authkey, key, keylen, NULL, + 0, NULL, 0); + break; + case HASH_ALG_SHA3_224: + rc = do_shash("sha3-224", ctx->authkey, key, keylen, + NULL, 0, NULL, 0); + break; + case HASH_ALG_SHA3_256: + rc = do_shash("sha3-256", ctx->authkey, key, keylen, + NULL, 0, NULL, 0); + break; + case HASH_ALG_SHA3_384: + rc = do_shash("sha3-384", ctx->authkey, key, keylen, + NULL, 0, NULL, 0); + break; + case HASH_ALG_SHA3_512: + rc = do_shash("sha3-512", ctx->authkey, key, keylen, + NULL, 0, NULL, 0); + break; + default: + pr_err("%s() Error: unknown hash alg\n", __func__); + return -EINVAL; + } + if (rc < 0) { + pr_err("%s() Error %d computing shash for %s\n", + __func__, rc, hash_alg_name[ctx->auth.alg]); + return rc; + } + ctx->authkeylen = digestsize; + + flow_log(" keylen > digestsize... hashed\n"); + flow_dump(" newkey: ", ctx->authkey, ctx->authkeylen); + } else { + memcpy(ctx->authkey, key, keylen); + ctx->authkeylen = keylen; + } + + /* + * Full HMAC operation in SPUM is not verified, + * So keeping the generation of IPAD, OPAD and + * outer hashing in software. + */ + if (iproc_priv.spu.spu_type == SPU_TYPE_SPUM) { + memcpy(ctx->ipad, ctx->authkey, ctx->authkeylen); + memset(ctx->ipad + ctx->authkeylen, 0, + blocksize - ctx->authkeylen); + ctx->authkeylen = 0; + memcpy(ctx->opad, ctx->ipad, blocksize); + + for (index = 0; index < blocksize; index++) { + ctx->ipad[index] ^= 0x36; + ctx->opad[index] ^= 0x5c; + } + + flow_dump(" ipad: ", ctx->ipad, blocksize); + flow_dump(" opad: ", ctx->opad, blocksize); + } + ctx->digestsize = digestsize; + atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_HMAC]); + + return 0; +} + +static int ahash_hmac_init(struct ahash_request *req) +{ + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + unsigned int blocksize = + crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + + flow_log("ahash_hmac_init()\n"); + + /* init the context as a hash */ + ahash_init(req); + + if (!spu_no_incr_hash(ctx)) { + /* SPU-M can do incr hashing but needs sw for outer HMAC */ + rctx->is_sw_hmac = true; + ctx->auth.mode = HASH_MODE_HASH; + /* start with a prepended ipad */ + memcpy(rctx->hash_carry, ctx->ipad, blocksize); + rctx->hash_carry_len = blocksize; + rctx->total_todo += blocksize; + } + + return 0; +} + +static int ahash_hmac_update(struct ahash_request *req) +{ + flow_log("ahash_hmac_update() nbytes:%u\n", req->nbytes); + + if (!req->nbytes) + return 0; + + return ahash_update(req); +} + +static int ahash_hmac_final(struct ahash_request *req) +{ + flow_log("ahash_hmac_final() nbytes:%u\n", req->nbytes); + + return ahash_final(req); +} + +static int ahash_hmac_finup(struct ahash_request *req) +{ + flow_log("ahash_hmac_finupl() nbytes:%u\n", req->nbytes); + + return ahash_finup(req); +} + +static int ahash_hmac_digest(struct ahash_request *req) +{ + struct iproc_reqctx_s *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm); + unsigned int blocksize = + crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + + flow_log("ahash_hmac_digest() nbytes:%u\n", req->nbytes); + + /* Perform initialization and then call finup */ + __ahash_init(req); + + if (iproc_priv.spu.spu_type == SPU_TYPE_SPU2) { + /* + * SPU2 supports full HMAC implementation in the + * hardware, need not to generate IPAD, OPAD and + * outer hash in software. + * Only for hash key len > hash block size, SPU2 + * expects to perform hashing on the key, shorten + * it to digest size and feed it as hash key. + */ + rctx->is_sw_hmac = false; + ctx->auth.mode = HASH_MODE_HMAC; + } else { + rctx->is_sw_hmac = true; + ctx->auth.mode = HASH_MODE_HASH; + /* start with a prepended ipad */ + memcpy(rctx->hash_carry, ctx->ipad, blocksize); + rctx->hash_carry_len = blocksize; + rctx->total_todo += blocksize; + } + + return __ahash_finup(req); +} + +/* aead helpers */ + +static int aead_need_fallback(struct aead_request *req) +{ + struct iproc_reqctx_s *rctx = aead_request_ctx(req); + struct spu_hw *spu = &iproc_priv.spu; + struct crypto_aead *aead = crypto_aead_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_aead_ctx(aead); + u32 payload_len; + + /* + * SPU hardware cannot handle the AES-GCM/CCM case where plaintext + * and AAD are both 0 bytes long. So use fallback in this case. + */ + if (((ctx->cipher.mode == CIPHER_MODE_GCM) || + (ctx->cipher.mode == CIPHER_MODE_CCM)) && + (req->assoclen == 0)) { + if ((rctx->is_encrypt && (req->cryptlen == 0)) || + (!rctx->is_encrypt && (req->cryptlen == ctx->digestsize))) { + flow_log("AES GCM/CCM needs fallback for 0 len req\n"); + return 1; + } + } + + /* SPU-M hardware only supports CCM digest size of 8, 12, or 16 bytes */ + if ((ctx->cipher.mode == CIPHER_MODE_CCM) && + (spu->spu_type == SPU_TYPE_SPUM) && + (ctx->digestsize != 8) && (ctx->digestsize != 12) && + (ctx->digestsize != 16)) { + flow_log("%s() AES CCM needs fallbck for digest size %d\n", + __func__, ctx->digestsize); + return 1; + } + + /* + * SPU-M on NSP has an issue where AES-CCM hash is not correct + * when AAD size is 0 + */ + if ((ctx->cipher.mode == CIPHER_MODE_CCM) && + (spu->spu_subtype == SPU_SUBTYPE_SPUM_NSP) && + (req->assoclen == 0)) { + flow_log("%s() AES_CCM needs fallback for 0 len AAD on NSP\n", + __func__); + return 1; + } + + payload_len = req->cryptlen; + if (spu->spu_type == SPU_TYPE_SPUM) + payload_len += req->assoclen; + + flow_log("%s() payload len: %u\n", __func__, payload_len); + + if (ctx->max_payload == SPU_MAX_PAYLOAD_INF) + return 0; + else + return payload_len > ctx->max_payload; +} + +static void aead_complete(struct crypto_async_request *areq, int err) +{ + struct aead_request *req = + container_of(areq, struct aead_request, base); + struct iproc_reqctx_s *rctx = aead_request_ctx(req); + struct crypto_aead *aead = crypto_aead_reqtfm(req); + + flow_log("%s() err:%d\n", __func__, err); + + areq->tfm = crypto_aead_tfm(aead); + + areq->complete = rctx->old_complete; + areq->data = rctx->old_data; + + areq->complete(areq, err); +} + +static int aead_do_fallback(struct aead_request *req, bool is_encrypt) +{ + struct crypto_aead *aead = crypto_aead_reqtfm(req); + struct crypto_tfm *tfm = crypto_aead_tfm(aead); + struct iproc_reqctx_s *rctx = aead_request_ctx(req); + struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm); + int err; + u32 req_flags; + + flow_log("%s() enc:%u\n", __func__, is_encrypt); + + if (ctx->fallback_cipher) { + /* Store the cipher tfm and then use the fallback tfm */ + rctx->old_tfm = tfm; + aead_request_set_tfm(req, ctx->fallback_cipher); + /* + * Save the callback and chain ourselves in, so we can restore + * the tfm + */ + rctx->old_complete = req->base.complete; + rctx->old_data = req->base.data; + req_flags = aead_request_flags(req); + aead_request_set_callback(req, req_flags, aead_complete, req); + err = is_encrypt ? crypto_aead_encrypt(req) : + crypto_aead_decrypt(req); + + if (err == 0) { + /* + * fallback was synchronous (did not return + * -EINPROGRESS). So restore request state here. + */ + aead_request_set_callback(req, req_flags, + rctx->old_complete, req); + req->base.data = rctx->old_data; + aead_request_set_tfm(req, aead); + flow_log("%s() fallback completed successfully\n\n", + __func__); + } + } else { + err = -EINVAL; + } + + return err; +} + +static int aead_enqueue(struct aead_request *req, bool is_encrypt) +{ + struct iproc_reqctx_s *rctx = aead_request_ctx(req); + struct crypto_aead *aead = crypto_aead_reqtfm(req); + struct iproc_ctx_s *ctx = crypto_aead_ctx(aead); + int err; + + flow_log("%s() enc:%u\n", __func__, is_encrypt); + + if (req->assoclen > MAX_ASSOC_SIZE) { + pr_err + ("%s() Error: associated data too long. (%u > %u bytes)\n", + __func__, req->assoclen, MAX_ASSOC_SIZE); + return -EINVAL; + } + + rctx->gfp = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; + rctx->parent = &req->base; + rctx->is_encrypt = is_encrypt; + rctx->bd_suppress = false; + rctx->total_todo = req->cryptlen; + rctx->src_sent = 0; + rctx->total_sent = 0; + rctx->total_received = 0; + rctx->is_sw_hmac = false; + rctx->ctx = ctx; + memset(&rctx->mb_mssg, 0, sizeof(struct brcm_message)); + + /* assoc data is at start of src sg */ + rctx->assoc = req->src; + + /* + * Init current position in src scatterlist to be after assoc data. + * src_skip set to buffer offset where data begins. (Assoc data could + * end in the middle of a buffer.) + */ + if (spu_sg_at_offset(req->src, req->assoclen, &rctx->src_sg, + &rctx->src_skip) < 0) { + pr_err("%s() Error: Unable to find start of src data\n", + __func__); + return -EINVAL; + } + + rctx->src_nents = 0; + rctx->dst_nents = 0; + if (req->dst == req->src) { + rctx->dst_sg = rctx->src_sg; + rctx->dst_skip = rctx->src_skip; + } else { + /* + * Expect req->dst to have room for assoc data followed by + * output data and ICV, if encrypt. So initialize dst_sg + * to point beyond assoc len offset. + */ + if (spu_sg_at_offset(req->dst, req->assoclen, &rctx->dst_sg, + &rctx->dst_skip) < 0) { + pr_err("%s() Error: Unable to find start of dst data\n", + __func__); + return -EINVAL; + } + } + + if (ctx->cipher.mode == CIPHER_MODE_CBC || + ctx->cipher.mode == CIPHER_MODE_CTR || + ctx->cipher.mode == CIPHER_MODE_OFB || + ctx->cipher.mode == CIPHER_MODE_XTS || + ctx->cipher.mode == CIPHER_MODE_GCM) { + rctx->iv_ctr_len = + ctx->salt_len + + crypto_aead_ivsize(crypto_aead_reqtfm(req)); + } else if (ctx->cipher.mode == CIPHER_MODE_CCM) { + rctx->iv_ctr_len = CCM_AES_IV_SIZE; + } else { + rctx->iv_ctr_len = 0; + } + + rctx->hash_carry_len = 0; + + flow_log(" src sg: %p\n", req->src); + flow_log(" rctx->src_sg: %p, src_skip %u\n", + rctx->src_sg, rctx->src_skip); + flow_log(" assoc: %p, assoclen %u\n", rctx->assoc, req->assoclen); + flow_log(" dst sg: %p\n", req->dst); + flow_log(" rctx->dst_sg: %p, dst_skip %u\n", + rctx->dst_sg, rctx->dst_skip); + flow_log(" iv_ctr_len:%u\n", rctx->iv_ctr_len); + flow_dump(" iv: ", req->iv, rctx->iv_ctr_len); + flow_log(" authkeylen:%u\n", ctx->authkeylen); + flow_log(" is_esp: %s\n", ctx->is_esp ? "yes" : "no"); + + if (ctx->max_payload == SPU_MAX_PAYLOAD_INF) + flow_log(" max_payload infinite"); + else + flow_log(" max_payload: %u\n", ctx->max_payload); + + if (unlikely(aead_need_fallback(req))) + return aead_do_fallback(req, is_encrypt); + + /* + * Do memory allocations for request after fallback check, because if we + * do fallback, we won't call finish_req() to dealloc. + */ + if (rctx->iv_ctr_len) { + if (ctx->salt_len) + memcpy(rctx->msg_buf.iv_ctr + ctx->salt_offset, + ctx->salt, ctx->salt_len); + memcpy(rctx->msg_buf.iv_ctr + ctx->salt_offset + ctx->salt_len, + req->iv, + rctx->iv_ctr_len - ctx->salt_len - ctx->salt_offset); + } + + rctx->chan_idx = select_channel(); + err = handle_aead_req(rctx); + if (err != -EINPROGRESS) + /* synchronous result */ + spu_chunk_cleanup(rctx); + + return err; +} + +static int aead_authenc_setkey(struct crypto_aead *cipher, + const u8 *key, unsigned int keylen) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher); + struct crypto_tfm *tfm = crypto_aead_tfm(cipher); + struct rtattr *rta = (void *)key; + struct crypto_authenc_key_param *param; + const u8 *origkey = key; + const unsigned int origkeylen = keylen; + + int ret = 0; + + flow_log("%s() aead:%p key:%p keylen:%u\n", __func__, cipher, key, + keylen); + flow_dump(" key: ", key, keylen); + + if (!RTA_OK(rta, keylen)) + goto badkey; + if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM) + goto badkey; + if (RTA_PAYLOAD(rta) < sizeof(*param)) + goto badkey; + + param = RTA_DATA(rta); + ctx->enckeylen = be32_to_cpu(param->enckeylen); + + key += RTA_ALIGN(rta->rta_len); + keylen -= RTA_ALIGN(rta->rta_len); + + if (keylen < ctx->enckeylen) + goto badkey; + if (ctx->enckeylen > MAX_KEY_SIZE) + goto badkey; + + ctx->authkeylen = keylen - ctx->enckeylen; + + if (ctx->authkeylen > MAX_KEY_SIZE) + goto badkey; + + memcpy(ctx->enckey, key + ctx->authkeylen, ctx->enckeylen); + /* May end up padding auth key. So make sure it's zeroed. */ + memset(ctx->authkey, 0, sizeof(ctx->authkey)); + memcpy(ctx->authkey, key, ctx->authkeylen); + + switch (ctx->alg->cipher_info.alg) { + case CIPHER_ALG_DES: + if (ctx->enckeylen == DES_KEY_SIZE) { + u32 tmp[DES_EXPKEY_WORDS]; + u32 flags = CRYPTO_TFM_RES_WEAK_KEY; + + if (des_ekey(tmp, key) == 0) { + if (crypto_aead_get_flags(cipher) & + CRYPTO_TFM_REQ_WEAK_KEY) { + crypto_aead_set_flags(cipher, flags); + return -EINVAL; + } + } + + ctx->cipher_type = CIPHER_TYPE_DES; + } else { + goto badkey; + } + break; + case CIPHER_ALG_3DES: + if (ctx->enckeylen == (DES_KEY_SIZE * 3)) { + const u32 *K = (const u32 *)key; + u32 flags = CRYPTO_TFM_RES_BAD_KEY_SCHED; + + if (!((K[0] ^ K[2]) | (K[1] ^ K[3])) || + !((K[2] ^ K[4]) | (K[3] ^ K[5]))) { + crypto_aead_set_flags(cipher, flags); + return -EINVAL; + } + + ctx->cipher_type = CIPHER_TYPE_3DES; + } else { + crypto_aead_set_flags(cipher, + CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + break; + case CIPHER_ALG_AES: + switch (ctx->enckeylen) { + case AES_KEYSIZE_128: + ctx->cipher_type = CIPHER_TYPE_AES128; + break; + case AES_KEYSIZE_192: + ctx->cipher_type = CIPHER_TYPE_AES192; + break; + case AES_KEYSIZE_256: + ctx->cipher_type = CIPHER_TYPE_AES256; + break; + default: + goto badkey; + } + break; + case CIPHER_ALG_RC4: + ctx->cipher_type = CIPHER_TYPE_INIT; + break; + default: + pr_err("%s() Error: Unknown cipher alg\n", __func__); + return -EINVAL; + } + + flow_log(" enckeylen:%u authkeylen:%u\n", ctx->enckeylen, + ctx->authkeylen); + flow_dump(" enc: ", ctx->enckey, ctx->enckeylen); + flow_dump(" auth: ", ctx->authkey, ctx->authkeylen); + + /* setkey the fallback just in case we needto use it */ + if (ctx->fallback_cipher) { + flow_log(" running fallback setkey()\n"); + + ctx->fallback_cipher->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK; + ctx->fallback_cipher->base.crt_flags |= + tfm->crt_flags & CRYPTO_TFM_REQ_MASK; + ret = + crypto_aead_setkey(ctx->fallback_cipher, origkey, + origkeylen); + if (ret) { + flow_log(" fallback setkey() returned:%d\n", ret); + tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK; + tfm->crt_flags |= + (ctx->fallback_cipher->base.crt_flags & + CRYPTO_TFM_RES_MASK); + } + } + + ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen, + ctx->enckeylen, + false); + + atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_AEAD]); + + return ret; + +badkey: + ctx->enckeylen = 0; + ctx->authkeylen = 0; + ctx->digestsize = 0; + + crypto_aead_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; +} + +static int aead_gcm_ccm_setkey(struct crypto_aead *cipher, + const u8 *key, unsigned int keylen) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher); + struct crypto_tfm *tfm = crypto_aead_tfm(cipher); + + int ret = 0; + + flow_log("%s() keylen:%u\n", __func__, keylen); + flow_dump(" key: ", key, keylen); + + if (!ctx->is_esp) + ctx->digestsize = keylen; + + ctx->enckeylen = keylen; + ctx->authkeylen = 0; + memcpy(ctx->enckey, key, ctx->enckeylen); + + switch (ctx->enckeylen) { + case AES_KEYSIZE_128: + ctx->cipher_type = CIPHER_TYPE_AES128; + break; + case AES_KEYSIZE_192: + ctx->cipher_type = CIPHER_TYPE_AES192; + break; + case AES_KEYSIZE_256: + ctx->cipher_type = CIPHER_TYPE_AES256; + break; + default: + goto badkey; + } + + flow_log(" enckeylen:%u authkeylen:%u\n", ctx->enckeylen, + ctx->authkeylen); + flow_dump(" enc: ", ctx->enckey, ctx->enckeylen); + flow_dump(" auth: ", ctx->authkey, ctx->authkeylen); + + /* setkey the fallback just in case we need to use it */ + if (ctx->fallback_cipher) { + flow_log(" running fallback setkey()\n"); + + ctx->fallback_cipher->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK; + ctx->fallback_cipher->base.crt_flags |= + tfm->crt_flags & CRYPTO_TFM_REQ_MASK; + ret = crypto_aead_setkey(ctx->fallback_cipher, key, + keylen + ctx->salt_len); + if (ret) { + flow_log(" fallback setkey() returned:%d\n", ret); + tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK; + tfm->crt_flags |= + (ctx->fallback_cipher->base.crt_flags & + CRYPTO_TFM_RES_MASK); + } + } + + ctx->spu_resp_hdr_len = spu->spu_response_hdr_len(ctx->authkeylen, + ctx->enckeylen, + false); + + atomic_inc(&iproc_priv.setkey_cnt[SPU_OP_AEAD]); + + flow_log(" enckeylen:%u authkeylen:%u\n", ctx->enckeylen, + ctx->authkeylen); + + return ret; + +badkey: + ctx->enckeylen = 0; + ctx->authkeylen = 0; + ctx->digestsize = 0; + + crypto_aead_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; +} + +/** + * aead_gcm_esp_setkey() - setkey() operation for ESP variant of GCM AES. + * @cipher: AEAD structure + * @key: Key followed by 4 bytes of salt + * @keylen: Length of key plus salt, in bytes + * + * Extracts salt from key and stores it to be prepended to IV on each request. + * Digest is always 16 bytes + * + * Return: Value from generic gcm setkey. + */ +static int aead_gcm_esp_setkey(struct crypto_aead *cipher, + const u8 *key, unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher); + + flow_log("%s\n", __func__); + ctx->salt_len = GCM_ESP_SALT_SIZE; + ctx->salt_offset = GCM_ESP_SALT_OFFSET; + memcpy(ctx->salt, key + keylen - GCM_ESP_SALT_SIZE, GCM_ESP_SALT_SIZE); + keylen -= GCM_ESP_SALT_SIZE; + ctx->digestsize = GCM_ESP_DIGESTSIZE; + ctx->is_esp = true; + flow_dump("salt: ", ctx->salt, GCM_ESP_SALT_SIZE); + + return aead_gcm_ccm_setkey(cipher, key, keylen); +} + +/** + * rfc4543_gcm_esp_setkey() - setkey operation for RFC4543 variant of GCM/GMAC. + * cipher: AEAD structure + * key: Key followed by 4 bytes of salt + * keylen: Length of key plus salt, in bytes + * + * Extracts salt from key and stores it to be prepended to IV on each request. + * Digest is always 16 bytes + * + * Return: Value from generic gcm setkey. + */ +static int rfc4543_gcm_esp_setkey(struct crypto_aead *cipher, + const u8 *key, unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher); + + flow_log("%s\n", __func__); + ctx->salt_len = GCM_ESP_SALT_SIZE; + ctx->salt_offset = GCM_ESP_SALT_OFFSET; + memcpy(ctx->salt, key + keylen - GCM_ESP_SALT_SIZE, GCM_ESP_SALT_SIZE); + keylen -= GCM_ESP_SALT_SIZE; + ctx->digestsize = GCM_ESP_DIGESTSIZE; + ctx->is_esp = true; + ctx->is_rfc4543 = true; + flow_dump("salt: ", ctx->salt, GCM_ESP_SALT_SIZE); + + return aead_gcm_ccm_setkey(cipher, key, keylen); +} + +/** + * aead_ccm_esp_setkey() - setkey() operation for ESP variant of CCM AES. + * @cipher: AEAD structure + * @key: Key followed by 4 bytes of salt + * @keylen: Length of key plus salt, in bytes + * + * Extracts salt from key and stores it to be prepended to IV on each request. + * Digest is always 16 bytes + * + * Return: Value from generic ccm setkey. + */ +static int aead_ccm_esp_setkey(struct crypto_aead *cipher, + const u8 *key, unsigned int keylen) +{ + struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher); + + flow_log("%s\n", __func__); + ctx->salt_len = CCM_ESP_SALT_SIZE; + ctx->salt_offset = CCM_ESP_SALT_OFFSET; + memcpy(ctx->salt, key + keylen - CCM_ESP_SALT_SIZE, CCM_ESP_SALT_SIZE); + keylen -= CCM_ESP_SALT_SIZE; + ctx->is_esp = true; + flow_dump("salt: ", ctx->salt, CCM_ESP_SALT_SIZE); + + return aead_gcm_ccm_setkey(cipher, key, keylen); +} + +static int aead_setauthsize(struct crypto_aead *cipher, unsigned int authsize) +{ + struct iproc_ctx_s *ctx = crypto_aead_ctx(cipher); + int ret = 0; + + flow_log("%s() authkeylen:%u authsize:%u\n", + __func__, ctx->authkeylen, authsize); + + ctx->digestsize = authsize; + + /* setkey the fallback just in case we needto use it */ + if (ctx->fallback_cipher) { + flow_log(" running fallback setauth()\n"); + + ret = crypto_aead_setauthsize(ctx->fallback_cipher, authsize); + if (ret) + flow_log(" fallback setauth() returned:%d\n", ret); + } + + return ret; +} + +static int aead_encrypt(struct aead_request *req) +{ + flow_log("%s() cryptlen:%u %08x\n", __func__, req->cryptlen, + req->cryptlen); + dump_sg(req->src, 0, req->cryptlen + req->assoclen); + flow_log(" assoc_len:%u\n", req->assoclen); + + return aead_enqueue(req, true); +} + +static int aead_decrypt(struct aead_request *req) +{ + flow_log("%s() cryptlen:%u\n", __func__, req->cryptlen); + dump_sg(req->src, 0, req->cryptlen + req->assoclen); + flow_log(" assoc_len:%u\n", req->assoclen); + + return aead_enqueue(req, false); +} + +/* ==================== Supported Cipher Algorithms ==================== */ + +static struct iproc_alg_s driver_algs[] = { + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "gcm(aes)", + .cra_driver_name = "gcm-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK + }, + .setkey = aead_gcm_ccm_setkey, + .ivsize = GCM_AES_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_GCM, + }, + .auth_info = { + .alg = HASH_ALG_AES, + .mode = HASH_MODE_GCM, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "ccm(aes)", + .cra_driver_name = "ccm-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK + }, + .setkey = aead_gcm_ccm_setkey, + .ivsize = CCM_AES_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_CCM, + }, + .auth_info = { + .alg = HASH_ALG_AES, + .mode = HASH_MODE_CCM, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "rfc4106(gcm(aes))", + .cra_driver_name = "gcm-aes-esp-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK + }, + .setkey = aead_gcm_esp_setkey, + .ivsize = GCM_ESP_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_GCM, + }, + .auth_info = { + .alg = HASH_ALG_AES, + .mode = HASH_MODE_GCM, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "rfc4309(ccm(aes))", + .cra_driver_name = "ccm-aes-esp-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK + }, + .setkey = aead_ccm_esp_setkey, + .ivsize = CCM_AES_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_CCM, + }, + .auth_info = { + .alg = HASH_ALG_AES, + .mode = HASH_MODE_CCM, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "rfc4543(gcm(aes))", + .cra_driver_name = "gmac-aes-esp-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK + }, + .setkey = rfc4543_gcm_esp_setkey, + .ivsize = GCM_ESP_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_GCM, + }, + .auth_info = { + .alg = HASH_ALG_AES, + .mode = HASH_MODE_GCM, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(md5),cbc(aes))", + .cra_driver_name = "authenc-hmac-md5-cbc-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = MD5_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_MD5, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha1),cbc(aes))", + .cra_driver_name = "authenc-hmac-sha1-cbc-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = SHA1_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA1, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha256),cbc(aes))", + .cra_driver_name = "authenc-hmac-sha256-cbc-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = SHA256_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA256, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(md5),cbc(des))", + .cra_driver_name = "authenc-hmac-md5-cbc-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES_BLOCK_SIZE, + .maxauthsize = MD5_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_MD5, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha1),cbc(des))", + .cra_driver_name = "authenc-hmac-sha1-cbc-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES_BLOCK_SIZE, + .maxauthsize = SHA1_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA1, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha224),cbc(des))", + .cra_driver_name = "authenc-hmac-sha224-cbc-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES_BLOCK_SIZE, + .maxauthsize = SHA224_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA224, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha256),cbc(des))", + .cra_driver_name = "authenc-hmac-sha256-cbc-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES_BLOCK_SIZE, + .maxauthsize = SHA256_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA256, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha384),cbc(des))", + .cra_driver_name = "authenc-hmac-sha384-cbc-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES_BLOCK_SIZE, + .maxauthsize = SHA384_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA384, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha512),cbc(des))", + .cra_driver_name = "authenc-hmac-sha512-cbc-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES_BLOCK_SIZE, + .maxauthsize = SHA512_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA512, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(md5),cbc(des3_ede))", + .cra_driver_name = "authenc-hmac-md5-cbc-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES3_EDE_BLOCK_SIZE, + .maxauthsize = MD5_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_MD5, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha1),cbc(des3_ede))", + .cra_driver_name = "authenc-hmac-sha1-cbc-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES3_EDE_BLOCK_SIZE, + .maxauthsize = SHA1_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA1, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha224),cbc(des3_ede))", + .cra_driver_name = "authenc-hmac-sha224-cbc-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES3_EDE_BLOCK_SIZE, + .maxauthsize = SHA224_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA224, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha256),cbc(des3_ede))", + .cra_driver_name = "authenc-hmac-sha256-cbc-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES3_EDE_BLOCK_SIZE, + .maxauthsize = SHA256_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA256, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha384),cbc(des3_ede))", + .cra_driver_name = "authenc-hmac-sha384-cbc-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES3_EDE_BLOCK_SIZE, + .maxauthsize = SHA384_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA384, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + { + .type = CRYPTO_ALG_TYPE_AEAD, + .alg.aead = { + .base = { + .cra_name = "authenc(hmac(sha512),cbc(des3_ede))", + .cra_driver_name = "authenc-hmac-sha512-cbc-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_flags = CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC + }, + .setkey = aead_authenc_setkey, + .ivsize = DES3_EDE_BLOCK_SIZE, + .maxauthsize = SHA512_DIGEST_SIZE, + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_SHA512, + .mode = HASH_MODE_HMAC, + }, + .auth_first = 0, + }, + +/* ABLKCIPHER algorithms. */ + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ecb(arc4)", + .cra_driver_name = "ecb-arc4-iproc", + .cra_blocksize = ARC4_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = ARC4_MIN_KEY_SIZE, + .max_keysize = ARC4_MAX_KEY_SIZE, + .ivsize = 0, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_RC4, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ofb(des)", + .cra_driver_name = "ofb-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_OFB, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "cbc(des)", + .cra_driver_name = "cbc-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ecb(des)", + .cra_driver_name = "ecb-des-iproc", + .cra_blocksize = DES_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = 0, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_DES, + .mode = CIPHER_MODE_ECB, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ofb(des3_ede)", + .cra_driver_name = "ofb-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_OFB, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "cbc(des3_ede)", + .cra_driver_name = "cbc-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ecb(des3_ede)", + .cra_driver_name = "ecb-des3-iproc", + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = 0, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_3DES, + .mode = CIPHER_MODE_ECB, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ofb(aes)", + .cra_driver_name = "ofb-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_OFB, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_CBC, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ecb(aes)", + .cra_driver_name = "ecb-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = 0, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_ECB, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + { + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ablkcipher = { + /* .geniv = "chainiv", */ + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_CTR, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, +{ + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .alg.crypto = { + .cra_name = "xts(aes)", + .cra_driver_name = "xts-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ablkcipher = { + .min_keysize = 2 * AES_MIN_KEY_SIZE, + .max_keysize = 2 * AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_AES, + .mode = CIPHER_MODE_XTS, + }, + .auth_info = { + .alg = HASH_ALG_NONE, + .mode = HASH_MODE_NONE, + }, + }, + +/* AHASH algorithms. */ + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = MD5_DIGEST_SIZE, + .halg.base = { + .cra_name = "md5", + .cra_driver_name = "md5-iproc", + .cra_blocksize = MD5_BLOCK_WORDS * 4, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_MD5, + .mode = HASH_MODE_HASH, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = MD5_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(md5)", + .cra_driver_name = "hmac-md5-iproc", + .cra_blocksize = MD5_BLOCK_WORDS * 4, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_MD5, + .mode = HASH_MODE_HMAC, + }, + }, + {.type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA1_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha1", + .cra_driver_name = "sha1-iproc", + .cra_blocksize = SHA1_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA1, + .mode = HASH_MODE_HASH, + }, + }, + {.type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA1_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha1)", + .cra_driver_name = "hmac-sha1-iproc", + .cra_blocksize = SHA1_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA1, + .mode = HASH_MODE_HMAC, + }, + }, + {.type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha224", + .cra_driver_name = "sha224-iproc", + .cra_blocksize = SHA224_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA224, + .mode = HASH_MODE_HASH, + }, + }, + {.type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha224)", + .cra_driver_name = "hmac-sha224-iproc", + .cra_blocksize = SHA224_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA224, + .mode = HASH_MODE_HMAC, + }, + }, + {.type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-iproc", + .cra_blocksize = SHA256_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA256, + .mode = HASH_MODE_HASH, + }, + }, + {.type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha256)", + .cra_driver_name = "hmac-sha256-iproc", + .cra_blocksize = SHA256_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA256, + .mode = HASH_MODE_HMAC, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA384_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha384", + .cra_driver_name = "sha384-iproc", + .cra_blocksize = SHA384_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA384, + .mode = HASH_MODE_HASH, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA384_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha384)", + .cra_driver_name = "hmac-sha384-iproc", + .cra_blocksize = SHA384_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA384, + .mode = HASH_MODE_HMAC, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA512_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha512", + .cra_driver_name = "sha512-iproc", + .cra_blocksize = SHA512_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA512, + .mode = HASH_MODE_HASH, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA512_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha512)", + .cra_driver_name = "hmac-sha512-iproc", + .cra_blocksize = SHA512_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA512, + .mode = HASH_MODE_HMAC, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_224_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha3-224", + .cra_driver_name = "sha3-224-iproc", + .cra_blocksize = SHA3_224_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_224, + .mode = HASH_MODE_HASH, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_224_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha3-224)", + .cra_driver_name = "hmac-sha3-224-iproc", + .cra_blocksize = SHA3_224_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_224, + .mode = HASH_MODE_HMAC + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_256_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha3-256", + .cra_driver_name = "sha3-256-iproc", + .cra_blocksize = SHA3_256_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_256, + .mode = HASH_MODE_HASH, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_256_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha3-256)", + .cra_driver_name = "hmac-sha3-256-iproc", + .cra_blocksize = SHA3_256_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_256, + .mode = HASH_MODE_HMAC, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_384_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha3-384", + .cra_driver_name = "sha3-384-iproc", + .cra_blocksize = SHA3_224_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_384, + .mode = HASH_MODE_HASH, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_384_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha3-384)", + .cra_driver_name = "hmac-sha3-384-iproc", + .cra_blocksize = SHA3_384_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_384, + .mode = HASH_MODE_HMAC, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_512_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha3-512", + .cra_driver_name = "sha3-512-iproc", + .cra_blocksize = SHA3_512_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_512, + .mode = HASH_MODE_HASH, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = SHA3_512_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha3-512)", + .cra_driver_name = "hmac-sha3-512-iproc", + .cra_blocksize = SHA3_512_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_SHA3_512, + .mode = HASH_MODE_HMAC, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = AES_BLOCK_SIZE, + .halg.base = { + .cra_name = "xcbc(aes)", + .cra_driver_name = "xcbc-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_AES, + .mode = HASH_MODE_XCBC, + }, + }, + { + .type = CRYPTO_ALG_TYPE_AHASH, + .alg.hash = { + .halg.digestsize = AES_BLOCK_SIZE, + .halg.base = { + .cra_name = "cmac(aes)", + .cra_driver_name = "cmac-aes-iproc", + .cra_blocksize = AES_BLOCK_SIZE, + } + }, + .cipher_info = { + .alg = CIPHER_ALG_NONE, + .mode = CIPHER_MODE_NONE, + }, + .auth_info = { + .alg = HASH_ALG_AES, + .mode = HASH_MODE_CMAC, + }, + }, +}; + +static int generic_cra_init(struct crypto_tfm *tfm, + struct iproc_alg_s *cipher_alg) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm); + unsigned int blocksize = crypto_tfm_alg_blocksize(tfm); + + flow_log("%s()\n", __func__); + + ctx->alg = cipher_alg; + ctx->cipher = cipher_alg->cipher_info; + ctx->auth = cipher_alg->auth_info; + ctx->auth_first = cipher_alg->auth_first; + ctx->max_payload = spu->spu_ctx_max_payload(ctx->cipher.alg, + ctx->cipher.mode, + blocksize); + ctx->fallback_cipher = NULL; + + ctx->enckeylen = 0; + ctx->authkeylen = 0; + + atomic_inc(&iproc_priv.stream_count); + atomic_inc(&iproc_priv.session_count); + + return 0; +} + +static int ablkcipher_cra_init(struct crypto_tfm *tfm) +{ + struct crypto_alg *alg = tfm->__crt_alg; + struct iproc_alg_s *cipher_alg; + + flow_log("%s()\n", __func__); + + tfm->crt_ablkcipher.reqsize = sizeof(struct iproc_reqctx_s); + + cipher_alg = container_of(alg, struct iproc_alg_s, alg.crypto); + return generic_cra_init(tfm, cipher_alg); +} + +static int ahash_cra_init(struct crypto_tfm *tfm) +{ + int err; + struct crypto_alg *alg = tfm->__crt_alg; + struct iproc_alg_s *cipher_alg; + + cipher_alg = container_of(__crypto_ahash_alg(alg), struct iproc_alg_s, + alg.hash); + + err = generic_cra_init(tfm, cipher_alg); + flow_log("%s()\n", __func__); + + /* + * export state size has to be < 512 bytes. So don't include msg bufs + * in state size. + */ + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct iproc_reqctx_s)); + + return err; +} + +static int aead_cra_init(struct crypto_aead *aead) +{ + struct crypto_tfm *tfm = crypto_aead_tfm(aead); + struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm); + struct crypto_alg *alg = tfm->__crt_alg; + struct aead_alg *aalg = container_of(alg, struct aead_alg, base); + struct iproc_alg_s *cipher_alg = container_of(aalg, struct iproc_alg_s, + alg.aead); + + int err = generic_cra_init(tfm, cipher_alg); + + flow_log("%s()\n", __func__); + + crypto_aead_set_reqsize(aead, sizeof(struct iproc_reqctx_s)); + ctx->is_esp = false; + ctx->salt_len = 0; + ctx->salt_offset = 0; + + /* random first IV */ + get_random_bytes(ctx->iv, MAX_IV_SIZE); + flow_dump(" iv: ", ctx->iv, MAX_IV_SIZE); + + if (!err) { + if (alg->cra_flags & CRYPTO_ALG_NEED_FALLBACK) { + flow_log("%s() creating fallback cipher\n", __func__); + + ctx->fallback_cipher = + crypto_alloc_aead(alg->cra_name, 0, + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(ctx->fallback_cipher)) { + pr_err("%s() Error: failed to allocate fallback for %s\n", + __func__, alg->cra_name); + return PTR_ERR(ctx->fallback_cipher); + } + } + } + + return err; +} + +static void generic_cra_exit(struct crypto_tfm *tfm) +{ + atomic_dec(&iproc_priv.session_count); +} + +static void aead_cra_exit(struct crypto_aead *aead) +{ + struct crypto_tfm *tfm = crypto_aead_tfm(aead); + struct iproc_ctx_s *ctx = crypto_tfm_ctx(tfm); + + generic_cra_exit(tfm); + + if (ctx->fallback_cipher) { + crypto_free_aead(ctx->fallback_cipher); + ctx->fallback_cipher = NULL; + } +} + +/** + * spu_functions_register() - Specify hardware-specific SPU functions based on + * SPU type read from device tree. + * @dev: device structure + * @spu_type: SPU hardware generation + * @spu_subtype: SPU hardware version + */ +static void spu_functions_register(struct device *dev, + enum spu_spu_type spu_type, + enum spu_spu_subtype spu_subtype) +{ + struct spu_hw *spu = &iproc_priv.spu; + + if (spu_type == SPU_TYPE_SPUM) { + dev_dbg(dev, "Registering SPUM functions"); + spu->spu_dump_msg_hdr = spum_dump_msg_hdr; + spu->spu_payload_length = spum_payload_length; + spu->spu_response_hdr_len = spum_response_hdr_len; + spu->spu_hash_pad_len = spum_hash_pad_len; + spu->spu_gcm_ccm_pad_len = spum_gcm_ccm_pad_len; + spu->spu_assoc_resp_len = spum_assoc_resp_len; + spu->spu_aead_ivlen = spum_aead_ivlen; + spu->spu_hash_type = spum_hash_type; + spu->spu_digest_size = spum_digest_size; + spu->spu_create_request = spum_create_request; + spu->spu_cipher_req_init = spum_cipher_req_init; + spu->spu_cipher_req_finish = spum_cipher_req_finish; + spu->spu_request_pad = spum_request_pad; + spu->spu_tx_status_len = spum_tx_status_len; + spu->spu_rx_status_len = spum_rx_status_len; + spu->spu_status_process = spum_status_process; + spu->spu_xts_tweak_in_payload = spum_xts_tweak_in_payload; + spu->spu_ccm_update_iv = spum_ccm_update_iv; + spu->spu_wordalign_padlen = spum_wordalign_padlen; + if (spu_subtype == SPU_SUBTYPE_SPUM_NS2) + spu->spu_ctx_max_payload = spum_ns2_ctx_max_payload; + else + spu->spu_ctx_max_payload = spum_nsp_ctx_max_payload; + } else { + dev_dbg(dev, "Registering SPU2 functions"); + spu->spu_dump_msg_hdr = spu2_dump_msg_hdr; + spu->spu_ctx_max_payload = spu2_ctx_max_payload; + spu->spu_payload_length = spu2_payload_length; + spu->spu_response_hdr_len = spu2_response_hdr_len; + spu->spu_hash_pad_len = spu2_hash_pad_len; + spu->spu_gcm_ccm_pad_len = spu2_gcm_ccm_pad_len; + spu->spu_assoc_resp_len = spu2_assoc_resp_len; + spu->spu_aead_ivlen = spu2_aead_ivlen; + spu->spu_hash_type = spu2_hash_type; + spu->spu_digest_size = spu2_digest_size; + spu->spu_create_request = spu2_create_request; + spu->spu_cipher_req_init = spu2_cipher_req_init; + spu->spu_cipher_req_finish = spu2_cipher_req_finish; + spu->spu_request_pad = spu2_request_pad; + spu->spu_tx_status_len = spu2_tx_status_len; + spu->spu_rx_status_len = spu2_rx_status_len; + spu->spu_status_process = spu2_status_process; + spu->spu_xts_tweak_in_payload = spu2_xts_tweak_in_payload; + spu->spu_ccm_update_iv = spu2_ccm_update_iv; + spu->spu_wordalign_padlen = spu2_wordalign_padlen; + } +} + +/** + * spu_mb_init() - Initialize mailbox client. Request ownership of a mailbox + * channel for the SPU being probed. + * @dev: SPU driver device structure + * + * Return: 0 if successful + * < 0 otherwise + */ +static int spu_mb_init(struct device *dev) +{ + struct mbox_client *mcl = &iproc_priv.mcl[iproc_priv.spu.num_spu]; + int err; + + mcl->dev = dev; + mcl->tx_block = false; + mcl->tx_tout = 0; + mcl->knows_txdone = false; + mcl->rx_callback = spu_rx_callback; + mcl->tx_done = NULL; + + iproc_priv.mbox[iproc_priv.spu.num_spu] = + mbox_request_channel(mcl, 0); + if (IS_ERR(iproc_priv.mbox[iproc_priv.spu.num_spu])) { + err = (int)PTR_ERR(iproc_priv.mbox[iproc_priv.spu.num_spu]); + dev_err(dev, + "Mbox channel %d request failed with err %d", + iproc_priv.spu.num_spu, err); + iproc_priv.mbox[iproc_priv.spu.num_spu] = NULL; + return err; + } + + return 0; +} + +static void spu_mb_release(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < iproc_priv.spu.num_spu; i++) + mbox_free_channel(iproc_priv.mbox[i]); +} + +static void spu_counters_init(void) +{ + int i; + int j; + + atomic_set(&iproc_priv.session_count, 0); + atomic_set(&iproc_priv.stream_count, 0); + atomic_set(&iproc_priv.next_chan, (int)iproc_priv.spu.num_spu); + atomic64_set(&iproc_priv.bytes_in, 0); + atomic64_set(&iproc_priv.bytes_out, 0); + for (i = 0; i < SPU_OP_NUM; i++) { + atomic_set(&iproc_priv.op_counts[i], 0); + atomic_set(&iproc_priv.setkey_cnt[i], 0); + } + for (i = 0; i < CIPHER_ALG_LAST; i++) + for (j = 0; j < CIPHER_MODE_LAST; j++) + atomic_set(&iproc_priv.cipher_cnt[i][j], 0); + + for (i = 0; i < HASH_ALG_LAST; i++) { + atomic_set(&iproc_priv.hash_cnt[i], 0); + atomic_set(&iproc_priv.hmac_cnt[i], 0); + } + for (i = 0; i < AEAD_TYPE_LAST; i++) + atomic_set(&iproc_priv.aead_cnt[i], 0); + + atomic_set(&iproc_priv.mb_no_spc, 0); + atomic_set(&iproc_priv.mb_send_fail, 0); + atomic_set(&iproc_priv.bad_icv, 0); +} + +static int spu_register_ablkcipher(struct iproc_alg_s *driver_alg) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct crypto_alg *crypto = &driver_alg->alg.crypto; + int err; + + /* SPU2 does not support RC4 */ + if ((driver_alg->cipher_info.alg == CIPHER_ALG_RC4) && + (spu->spu_type == SPU_TYPE_SPU2)) + return 0; + + crypto->cra_module = THIS_MODULE; + crypto->cra_priority = cipher_pri; + crypto->cra_alignmask = 0; + crypto->cra_ctxsize = sizeof(struct iproc_ctx_s); + INIT_LIST_HEAD(&crypto->cra_list); + + crypto->cra_init = ablkcipher_cra_init; + crypto->cra_exit = generic_cra_exit; + crypto->cra_type = &crypto_ablkcipher_type; + crypto->cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY; + + crypto->cra_ablkcipher.setkey = ablkcipher_setkey; + crypto->cra_ablkcipher.encrypt = ablkcipher_encrypt; + crypto->cra_ablkcipher.decrypt = ablkcipher_decrypt; + + err = crypto_register_alg(crypto); + /* Mark alg as having been registered, if successful */ + if (err == 0) + driver_alg->registered = true; + pr_debug(" registered ablkcipher %s\n", crypto->cra_driver_name); + return err; +} + +static int spu_register_ahash(struct iproc_alg_s *driver_alg) +{ + struct spu_hw *spu = &iproc_priv.spu; + struct ahash_alg *hash = &driver_alg->alg.hash; + int err; + + /* AES-XCBC is the only AES hash type currently supported on SPU-M */ + if ((driver_alg->auth_info.alg == HASH_ALG_AES) && + (driver_alg->auth_info.mode != HASH_MODE_XCBC) && + (spu->spu_type == SPU_TYPE_SPUM)) + return 0; + + /* SHA3 algorithm variants are not registered for SPU-M or SPU2. */ + if ((driver_alg->auth_info.alg >= HASH_ALG_SHA3_224) && + (spu->spu_subtype != SPU_SUBTYPE_SPU2_V2)) + return 0; + + hash->halg.base.cra_module = THIS_MODULE; + hash->halg.base.cra_priority = hash_pri; + hash->halg.base.cra_alignmask = 0; + hash->halg.base.cra_ctxsize = sizeof(struct iproc_ctx_s); + hash->halg.base.cra_init = ahash_cra_init; + hash->halg.base.cra_exit = generic_cra_exit; + hash->halg.base.cra_type = &crypto_ahash_type; + hash->halg.base.cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC; + hash->halg.statesize = sizeof(struct spu_hash_export_s); + + if (driver_alg->auth_info.mode != HASH_MODE_HMAC) { + hash->setkey = ahash_setkey; + hash->init = ahash_init; + hash->update = ahash_update; + hash->final = ahash_final; + hash->finup = ahash_finup; + hash->digest = ahash_digest; + } else { + hash->setkey = ahash_hmac_setkey; + hash->init = ahash_hmac_init; + hash->update = ahash_hmac_update; + hash->final = ahash_hmac_final; + hash->finup = ahash_hmac_finup; + hash->digest = ahash_hmac_digest; + } + hash->export = ahash_export; + hash->import = ahash_import; + + err = crypto_register_ahash(hash); + /* Mark alg as having been registered, if successful */ + if (err == 0) + driver_alg->registered = true; + pr_debug(" registered ahash %s\n", + hash->halg.base.cra_driver_name); + return err; +} + +static int spu_register_aead(struct iproc_alg_s *driver_alg) +{ + struct aead_alg *aead = &driver_alg->alg.aead; + int err; + + aead->base.cra_module = THIS_MODULE; + aead->base.cra_priority = aead_pri; + aead->base.cra_alignmask = 0; + aead->base.cra_ctxsize = sizeof(struct iproc_ctx_s); + INIT_LIST_HEAD(&aead->base.cra_list); + + aead->base.cra_flags |= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC; + /* setkey set in alg initialization */ + aead->setauthsize = aead_setauthsize; + aead->encrypt = aead_encrypt; + aead->decrypt = aead_decrypt; + aead->init = aead_cra_init; + aead->exit = aead_cra_exit; + + err = crypto_register_aead(aead); + /* Mark alg as having been registered, if successful */ + if (err == 0) + driver_alg->registered = true; + pr_debug(" registered aead %s\n", aead->base.cra_driver_name); + return err; +} + +/* register crypto algorithms the device supports */ +static int spu_algs_register(struct device *dev) +{ + int i, j; + int err; + + for (i = 0; i < ARRAY_SIZE(driver_algs); i++) { + switch (driver_algs[i].type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + err = spu_register_ablkcipher(&driver_algs[i]); + break; + case CRYPTO_ALG_TYPE_AHASH: + err = spu_register_ahash(&driver_algs[i]); + break; + case CRYPTO_ALG_TYPE_AEAD: + err = spu_register_aead(&driver_algs[i]); + break; + default: + dev_err(dev, + "iproc-crypto: unknown alg type: %d", + driver_algs[i].type); + err = -EINVAL; + } + + if (err) { + dev_err(dev, "alg registration failed with error %d\n", + err); + goto err_algs; + } + } + + return 0; + +err_algs: + for (j = 0; j < i; j++) { + /* Skip any algorithm not registered */ + if (!driver_algs[j].registered) + continue; + switch (driver_algs[j].type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + crypto_unregister_alg(&driver_algs[j].alg.crypto); + driver_algs[j].registered = false; + break; + case CRYPTO_ALG_TYPE_AHASH: + crypto_unregister_ahash(&driver_algs[j].alg.hash); + driver_algs[j].registered = false; + break; + case CRYPTO_ALG_TYPE_AEAD: + crypto_unregister_aead(&driver_algs[j].alg.aead); + driver_algs[j].registered = false; + break; + } + } + return err; +} + +/* ==================== Kernel Platform API ==================== */ + +static struct spu_type_subtype spum_ns2_types = { + SPU_TYPE_SPUM, SPU_SUBTYPE_SPUM_NS2 +}; + +static struct spu_type_subtype spum_nsp_types = { + SPU_TYPE_SPUM, SPU_SUBTYPE_SPUM_NSP +}; + +static struct spu_type_subtype spu2_types = { + SPU_TYPE_SPU2, SPU_SUBTYPE_SPU2_V1 +}; + +static struct spu_type_subtype spu2_v2_types = { + SPU_TYPE_SPU2, SPU_SUBTYPE_SPU2_V2 +}; + +static const struct of_device_id bcm_spu_dt_ids[] = { + { + .compatible = "brcm,spum-crypto", + .data = &spum_ns2_types, + }, + { + .compatible = "brcm,spum-nsp-crypto", + .data = &spum_nsp_types, + }, + { + .compatible = "brcm,spu2-crypto", + .data = &spu2_types, + }, + { + .compatible = "brcm,spu2-v2-crypto", + .data = &spu2_v2_types, + }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, bcm_spu_dt_ids); + +static int spu_dt_read(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spu_hw *spu = &iproc_priv.spu; + struct resource *spu_ctrl_regs; + const struct of_device_id *match; + const struct spu_type_subtype *matched_spu_type; + void __iomem *spu_reg_vbase[MAX_SPUS]; + int err; + + match = of_match_device(of_match_ptr(bcm_spu_dt_ids), dev); + matched_spu_type = match->data; + + if (iproc_priv.spu.num_spu > 1) { + /* If this is 2nd or later SPU, make sure it's same type */ + if ((spu->spu_type != matched_spu_type->type) || + (spu->spu_subtype != matched_spu_type->subtype)) { + err = -EINVAL; + dev_err(&pdev->dev, "Multiple SPU types not allowed"); + return err; + } + } else { + /* Record type of first SPU */ + spu->spu_type = matched_spu_type->type; + spu->spu_subtype = matched_spu_type->subtype; + } + + /* Get and map SPU registers */ + spu_ctrl_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!spu_ctrl_regs) { + err = -EINVAL; + dev_err(&pdev->dev, "Invalid/missing registers for SPU\n"); + return err; + } + + spu_reg_vbase[iproc_priv.spu.num_spu] = + devm_ioremap_resource(dev, spu_ctrl_regs); + if (IS_ERR(spu_reg_vbase[iproc_priv.spu.num_spu])) { + err = PTR_ERR(spu_reg_vbase[iproc_priv.spu.num_spu]); + dev_err(&pdev->dev, "Failed to map registers: %d\n", + err); + spu_reg_vbase[iproc_priv.spu.num_spu] = NULL; + return err; + } + + dev_dbg(dev, "SPU %d detected.", iproc_priv.spu.num_spu); + + spu->reg_vbase[iproc_priv.spu.num_spu] = spu_reg_vbase; + + return 0; +} + +int bcm_spu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spu_hw *spu = &iproc_priv.spu; + int err = 0; + + iproc_priv.pdev[iproc_priv.spu.num_spu] = pdev; + platform_set_drvdata(iproc_priv.pdev[iproc_priv.spu.num_spu], + &iproc_priv); + + err = spu_dt_read(pdev); + if (err < 0) + goto failure; + + err = spu_mb_init(&pdev->dev); + if (err < 0) + goto failure; + + iproc_priv.spu.num_spu++; + + /* If already initialized, we've just added another SPU and are done */ + if (iproc_priv.inited) + return 0; + + if (spu->spu_type == SPU_TYPE_SPUM) + iproc_priv.bcm_hdr_len = 8; + else if (spu->spu_type == SPU_TYPE_SPU2) + iproc_priv.bcm_hdr_len = 0; + + spu_functions_register(&pdev->dev, spu->spu_type, spu->spu_subtype); + + spu_counters_init(); + + spu_setup_debugfs(); + + err = spu_algs_register(dev); + if (err < 0) + goto fail_reg; + + iproc_priv.inited = true; + + return 0; + +fail_reg: + spu_free_debugfs(); +failure: + spu_mb_release(pdev); + dev_err(dev, "%s failed with error %d.\n", __func__, err); + + return err; +} + +int bcm_spu_remove(struct platform_device *pdev) +{ + int i; + struct device *dev = &pdev->dev; + char *cdn; + + for (i = 0; i < ARRAY_SIZE(driver_algs); i++) { + /* + * Not all algorithms were registered, depending on whether + * hardware is SPU or SPU2. So here we make sure to skip + * those algorithms that were not previously registered. + */ + if (!driver_algs[i].registered) + continue; + + switch (driver_algs[i].type) { + case CRYPTO_ALG_TYPE_ABLKCIPHER: + crypto_unregister_alg(&driver_algs[i].alg.crypto); + dev_dbg(dev, " unregistered cipher %s\n", + driver_algs[i].alg.crypto.cra_driver_name); + driver_algs[i].registered = false; + break; + case CRYPTO_ALG_TYPE_AHASH: + crypto_unregister_ahash(&driver_algs[i].alg.hash); + cdn = driver_algs[i].alg.hash.halg.base.cra_driver_name; + dev_dbg(dev, " unregistered hash %s\n", cdn); + driver_algs[i].registered = false; + break; + case CRYPTO_ALG_TYPE_AEAD: + crypto_unregister_aead(&driver_algs[i].alg.aead); + dev_dbg(dev, " unregistered aead %s\n", + driver_algs[i].alg.aead.base.cra_driver_name); + driver_algs[i].registered = false; + break; + } + } + spu_free_debugfs(); + spu_mb_release(pdev); + return 0; +} + +/* ===== Kernel Module API ===== */ + +static struct platform_driver bcm_spu_pdriver = { + .driver = { + .name = "brcm-spu-crypto", + .of_match_table = of_match_ptr(bcm_spu_dt_ids), + }, + .probe = bcm_spu_probe, + .remove = bcm_spu_remove, +}; +module_platform_driver(bcm_spu_pdriver); + +MODULE_AUTHOR("Rob Rice <rob.rice@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom symmetric crypto offload driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/crypto/bcm/cipher.h b/drivers/crypto/bcm/cipher.h new file mode 100644 index 000000000000..51dca529ce8f --- /dev/null +++ b/drivers/crypto/bcm/cipher.h @@ -0,0 +1,483 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#ifndef _CIPHER_H +#define _CIPHER_H + +#include <linux/atomic.h> +#include <linux/mailbox/brcm-message.h> +#include <linux/mailbox_client.h> +#include <crypto/aes.h> +#include <crypto/internal/hash.h> +#include <crypto/aead.h> +#include <crypto/sha.h> +#include <crypto/sha3.h> + +#include "spu.h" +#include "spum.h" +#include "spu2.h" + +/* Driver supports up to MAX_SPUS SPU blocks */ +#define MAX_SPUS 16 + +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_BLOCK_SIZE 1 +#define ARC4_STATE_SIZE 4 + +#define CCM_AES_IV_SIZE 16 +#define GCM_AES_IV_SIZE 12 +#define GCM_ESP_IV_SIZE 8 +#define CCM_ESP_IV_SIZE 8 +#define RFC4543_ICV_SIZE 16 + +#define MAX_KEY_SIZE ARC4_MAX_KEY_SIZE +#define MAX_IV_SIZE AES_BLOCK_SIZE +#define MAX_DIGEST_SIZE SHA3_512_DIGEST_SIZE +#define MAX_ASSOC_SIZE 512 + +/* size of salt value for AES-GCM-ESP and AES-CCM-ESP */ +#define GCM_ESP_SALT_SIZE 4 +#define CCM_ESP_SALT_SIZE 3 +#define MAX_SALT_SIZE GCM_ESP_SALT_SIZE +#define GCM_ESP_SALT_OFFSET 0 +#define CCM_ESP_SALT_OFFSET 1 + +#define GCM_ESP_DIGESTSIZE 16 + +#define MAX_HASH_BLOCK_SIZE SHA512_BLOCK_SIZE + +/* + * Maximum number of bytes from a non-final hash request that can be deferred + * until more data is available. With new crypto API framework, this + * can be no more than one block of data. + */ +#define HASH_CARRY_MAX MAX_HASH_BLOCK_SIZE + +/* Force at least 4-byte alignment of all SPU message fields */ +#define SPU_MSG_ALIGN 4 + +/* Number of times to resend mailbox message if mb queue is full */ +#define SPU_MB_RETRY_MAX 1000 + +/* op_counts[] indexes */ +enum op_type { + SPU_OP_CIPHER, + SPU_OP_HASH, + SPU_OP_HMAC, + SPU_OP_AEAD, + SPU_OP_NUM +}; + +enum spu_spu_type { + SPU_TYPE_SPUM, + SPU_TYPE_SPU2, +}; + +/* + * SPUM_NS2 and SPUM_NSP are the SPU-M block on Northstar 2 and Northstar Plus, + * respectively. + */ +enum spu_spu_subtype { + SPU_SUBTYPE_SPUM_NS2, + SPU_SUBTYPE_SPUM_NSP, + SPU_SUBTYPE_SPU2_V1, + SPU_SUBTYPE_SPU2_V2 +}; + +struct spu_type_subtype { + enum spu_spu_type type; + enum spu_spu_subtype subtype; +}; + +struct cipher_op { + enum spu_cipher_alg alg; + enum spu_cipher_mode mode; +}; + +struct auth_op { + enum hash_alg alg; + enum hash_mode mode; +}; + +struct iproc_alg_s { + u32 type; + union { + struct crypto_alg crypto; + struct ahash_alg hash; + struct aead_alg aead; + } alg; + struct cipher_op cipher_info; + struct auth_op auth_info; + bool auth_first; + bool registered; +}; + +/* + * Buffers for a SPU request/reply message pair. All part of one structure to + * allow a single alloc per request. + */ +struct spu_msg_buf { + /* Request message fragments */ + + /* + * SPU request message header. For SPU-M, holds MH, EMH, SCTX, BDESC, + * and BD header. For SPU2, holds FMD, OMD. + */ + u8 bcm_spu_req_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)]; + + /* IV or counter. Size to include salt. Also used for XTS tweek. */ + u8 iv_ctr[ALIGN(2 * AES_BLOCK_SIZE, SPU_MSG_ALIGN)]; + + /* Hash digest. request and response. */ + u8 digest[ALIGN(MAX_DIGEST_SIZE, SPU_MSG_ALIGN)]; + + /* SPU request message padding */ + u8 spu_req_pad[ALIGN(SPU_PAD_LEN_MAX, SPU_MSG_ALIGN)]; + + /* SPU-M request message STATUS field */ + u8 tx_stat[ALIGN(SPU_TX_STATUS_LEN, SPU_MSG_ALIGN)]; + + /* Response message fragments */ + + /* SPU response message header */ + u8 spu_resp_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)]; + + /* SPU response message STATUS field padding */ + u8 rx_stat_pad[ALIGN(SPU_STAT_PAD_MAX, SPU_MSG_ALIGN)]; + + /* SPU response message STATUS field */ + u8 rx_stat[ALIGN(SPU_RX_STATUS_LEN, SPU_MSG_ALIGN)]; + + union { + /* Buffers only used for ablkcipher */ + struct { + /* + * Field used for either SUPDT when RC4 is used + * -OR- tweak value when XTS/AES is used + */ + u8 supdt_tweak[ALIGN(SPU_SUPDT_LEN, SPU_MSG_ALIGN)]; + } c; + + /* Buffers only used for aead */ + struct { + /* SPU response pad for GCM data */ + u8 gcmpad[ALIGN(AES_BLOCK_SIZE, SPU_MSG_ALIGN)]; + + /* SPU request msg padding for GCM AAD */ + u8 req_aad_pad[ALIGN(SPU_PAD_LEN_MAX, SPU_MSG_ALIGN)]; + + /* SPU response data to be discarded */ + u8 resp_aad[ALIGN(MAX_ASSOC_SIZE + MAX_IV_SIZE, + SPU_MSG_ALIGN)]; + } a; + }; +}; + +struct iproc_ctx_s { + u8 enckey[MAX_KEY_SIZE + ARC4_STATE_SIZE]; + unsigned int enckeylen; + + u8 authkey[MAX_KEY_SIZE + ARC4_STATE_SIZE]; + unsigned int authkeylen; + + u8 salt[MAX_SALT_SIZE]; + unsigned int salt_len; + unsigned int salt_offset; + u8 iv[MAX_IV_SIZE]; + + unsigned int digestsize; + + struct iproc_alg_s *alg; + bool is_esp; + + struct cipher_op cipher; + enum spu_cipher_type cipher_type; + + struct auth_op auth; + bool auth_first; + + /* + * The maximum length in bytes of the payload in a SPU message for this + * context. For SPU-M, the payload is the combination of AAD and data. + * For SPU2, the payload is just data. A value of SPU_MAX_PAYLOAD_INF + * indicates that there is no limit to the length of the SPU message + * payload. + */ + unsigned int max_payload; + + struct crypto_aead *fallback_cipher; + + /* auth_type is determined during processing of request */ + + u8 ipad[MAX_HASH_BLOCK_SIZE]; + u8 opad[MAX_HASH_BLOCK_SIZE]; + + /* + * Buffer to hold SPU message header template. Template is created at + * setkey time for ablkcipher requests, since most of the fields in the + * header are known at that time. At request time, just fill in a few + * missing pieces related to length of data in the request and IVs, etc. + */ + u8 bcm_spu_req_hdr[ALIGN(SPU2_HEADER_ALLOC_LEN, SPU_MSG_ALIGN)]; + + /* Length of SPU request header */ + u16 spu_req_hdr_len; + + /* Expected length of SPU response header */ + u16 spu_resp_hdr_len; + + /* + * shash descriptor - needed to perform incremental hashing in + * in software, when hw doesn't support it. + */ + struct shash_desc *shash; + + bool is_rfc4543; /* RFC 4543 style of GMAC */ +}; + +/* state from iproc_reqctx_s necessary for hash state export/import */ +struct spu_hash_export_s { + unsigned int total_todo; + unsigned int total_sent; + u8 hash_carry[HASH_CARRY_MAX]; + unsigned int hash_carry_len; + u8 incr_hash[MAX_DIGEST_SIZE]; + bool is_sw_hmac; +}; + +struct iproc_reqctx_s { + /* general context */ + struct crypto_async_request *parent; + + /* only valid after enqueue() */ + struct iproc_ctx_s *ctx; + + u8 chan_idx; /* Mailbox channel to be used to submit this request */ + + /* total todo, rx'd, and sent for this request */ + unsigned int total_todo; + unsigned int total_received; /* only valid for ablkcipher */ + unsigned int total_sent; + + /* + * num bytes sent to hw from the src sg in this request. This can differ + * from total_sent for incremental hashing. total_sent includes previous + * init() and update() data. src_sent does not. + */ + unsigned int src_sent; + + /* + * For AEAD requests, start of associated data. This will typically + * point to the beginning of the src scatterlist from the request, + * since assoc data is at the beginning of the src scatterlist rather + * than in its own sg. + */ + struct scatterlist *assoc; + + /* + * scatterlist entry and offset to start of data for next chunk. Crypto + * API src scatterlist for AEAD starts with AAD, if present. For first + * chunk, src_sg is sg entry at beginning of input data (after AAD). + * src_skip begins at the offset in that sg entry where data begins. + */ + struct scatterlist *src_sg; + int src_nents; /* Number of src entries with data */ + u32 src_skip; /* bytes of current sg entry already used */ + + /* + * Same for destination. For AEAD, if there is AAD, output data must + * be written at offset following AAD. + */ + struct scatterlist *dst_sg; + int dst_nents; /* Number of dst entries with data */ + u32 dst_skip; /* bytes of current sg entry already written */ + + /* Mailbox message used to send this request to PDC driver */ + struct brcm_message mb_mssg; + + bool bd_suppress; /* suppress BD field in SPU response? */ + + /* cipher context */ + bool is_encrypt; + + /* + * CBC mode: IV. CTR mode: counter. Else empty. Used as a DMA + * buffer for AEAD requests. So allocate as DMAable memory. If IV + * concatenated with salt, includes the salt. + */ + u8 *iv_ctr; + /* Length of IV or counter, in bytes */ + unsigned int iv_ctr_len; + + /* + * Hash requests can be of any size, whether initial, update, or final. + * A non-final request must be submitted to the SPU as an integral + * number of blocks. This may leave data at the end of the request + * that is not a full block. Since the request is non-final, it cannot + * be padded. So, we write the remainder to this hash_carry buffer and + * hold it until the next request arrives. The carry data is then + * submitted at the beginning of the data in the next SPU msg. + * hash_carry_len is the number of bytes currently in hash_carry. These + * fields are only used for ahash requests. + */ + u8 hash_carry[HASH_CARRY_MAX]; + unsigned int hash_carry_len; + unsigned int is_final; /* is this the final for the hash op? */ + + /* + * Digest from incremental hash is saved here to include in next hash + * operation. Cannot be stored in req->result for truncated hashes, + * since result may be sized for final digest. Cannot be saved in + * msg_buf because that gets deleted between incremental hash ops + * and is not saved as part of export(). + */ + u8 incr_hash[MAX_DIGEST_SIZE]; + + /* hmac context */ + bool is_sw_hmac; + + /* aead context */ + struct crypto_tfm *old_tfm; + crypto_completion_t old_complete; + void *old_data; + + gfp_t gfp; + + /* Buffers used to build SPU request and response messages */ + struct spu_msg_buf msg_buf; +}; + +/* + * Structure encapsulates a set of function pointers specific to the type of + * SPU hardware running. These functions handling creation and parsing of + * SPU request messages and SPU response messages. Includes hardware-specific + * values read from device tree. + */ +struct spu_hw { + void (*spu_dump_msg_hdr)(u8 *buf, unsigned int buf_len); + u32 (*spu_ctx_max_payload)(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + unsigned int blocksize); + u32 (*spu_payload_length)(u8 *spu_hdr); + u16 (*spu_response_hdr_len)(u16 auth_key_len, u16 enc_key_len, + bool is_hash); + u16 (*spu_hash_pad_len)(enum hash_alg hash_alg, + enum hash_mode hash_mode, u32 chunksize, + u16 hash_block_size); + u32 (*spu_gcm_ccm_pad_len)(enum spu_cipher_mode cipher_mode, + unsigned int data_size); + u32 (*spu_assoc_resp_len)(enum spu_cipher_mode cipher_mode, + unsigned int assoc_len, + unsigned int iv_len, bool is_encrypt); + u8 (*spu_aead_ivlen)(enum spu_cipher_mode cipher_mode, + u16 iv_len); + enum hash_type (*spu_hash_type)(u32 src_sent); + u32 (*spu_digest_size)(u32 digest_size, enum hash_alg alg, + enum hash_type); + u32 (*spu_create_request)(u8 *spu_hdr, + struct spu_request_opts *req_opts, + struct spu_cipher_parms *cipher_parms, + struct spu_hash_parms *hash_parms, + struct spu_aead_parms *aead_parms, + unsigned int data_size); + u16 (*spu_cipher_req_init)(u8 *spu_hdr, + struct spu_cipher_parms *cipher_parms); + void (*spu_cipher_req_finish)(u8 *spu_hdr, + u16 spu_req_hdr_len, + unsigned int is_inbound, + struct spu_cipher_parms *cipher_parms, + bool update_key, + unsigned int data_size); + void (*spu_request_pad)(u8 *pad_start, u32 gcm_padding, + u32 hash_pad_len, enum hash_alg auth_alg, + enum hash_mode auth_mode, + unsigned int total_sent, u32 status_padding); + u8 (*spu_xts_tweak_in_payload)(void); + u8 (*spu_tx_status_len)(void); + u8 (*spu_rx_status_len)(void); + int (*spu_status_process)(u8 *statp); + void (*spu_ccm_update_iv)(unsigned int digestsize, + struct spu_cipher_parms *cipher_parms, + unsigned int assoclen, unsigned int chunksize, + bool is_encrypt, bool is_esp); + u32 (*spu_wordalign_padlen)(u32 data_size); + + /* The base virtual address of the SPU hw registers */ + void __iomem *reg_vbase[MAX_SPUS]; + + /* Version of the SPU hardware */ + enum spu_spu_type spu_type; + + /* Sub-version of the SPU hardware */ + enum spu_spu_subtype spu_subtype; + + /* The number of SPUs on this platform */ + u32 num_spu; +}; + +struct device_private { + struct platform_device *pdev[MAX_SPUS]; + + struct spu_hw spu; + + atomic_t session_count; /* number of streams active */ + atomic_t stream_count; /* monotonic counter for streamID's */ + + /* Length of BCM header. Set to 0 when hw does not expect BCM HEADER. */ + u8 bcm_hdr_len; + + /* The index of the channel to use for the next crypto request */ + atomic_t next_chan; + + struct dentry *debugfs_dir; + struct dentry *debugfs_stats; + + /* Number of request bytes processed and result bytes returned */ + atomic64_t bytes_in; + atomic64_t bytes_out; + + /* Number of operations of each type */ + atomic_t op_counts[SPU_OP_NUM]; + + atomic_t cipher_cnt[CIPHER_ALG_LAST][CIPHER_MODE_LAST]; + atomic_t hash_cnt[HASH_ALG_LAST]; + atomic_t hmac_cnt[HASH_ALG_LAST]; + atomic_t aead_cnt[AEAD_TYPE_LAST]; + + /* Number of calls to setkey() for each operation type */ + atomic_t setkey_cnt[SPU_OP_NUM]; + + /* Number of times request was resubmitted because mb was full */ + atomic_t mb_no_spc; + + /* Number of mailbox send failures */ + atomic_t mb_send_fail; + + /* Number of ICV check failures for AEAD messages */ + atomic_t bad_icv; + + struct mbox_client mcl[MAX_SPUS]; + /* Array of mailbox channel pointers, one for each channel */ + struct mbox_chan *mbox[MAX_SPUS]; + + /* Driver initialized */ + bool inited; +}; + +extern struct device_private iproc_priv; + +#endif diff --git a/drivers/crypto/bcm/spu.c b/drivers/crypto/bcm/spu.c new file mode 100644 index 000000000000..dbb5c03dde49 --- /dev/null +++ b/drivers/crypto/bcm/spu.c @@ -0,0 +1,1251 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#include <linux/kernel.h> +#include <linux/string.h> + +#include "util.h" +#include "spu.h" +#include "spum.h" +#include "cipher.h" + +/* This array is based on the hash algo type supported in spu.h */ +char *tag_to_hash_idx[] = { "none", "md5", "sha1", "sha224", "sha256" }; + +char *hash_alg_name[] = { "None", "md5", "sha1", "sha224", "sha256", "aes", + "sha384", "sha512", "sha3_224", "sha3_256", "sha3_384", "sha3_512" }; + +char *aead_alg_name[] = { "ccm(aes)", "gcm(aes)", "authenc" }; + +/* Assumes SPU-M messages are in big endian */ +void spum_dump_msg_hdr(u8 *buf, unsigned int buf_len) +{ + u8 *ptr = buf; + struct SPUHEADER *spuh = (struct SPUHEADER *)buf; + unsigned int hash_key_len = 0; + unsigned int hash_state_len = 0; + unsigned int cipher_key_len = 0; + unsigned int iv_len; + u32 pflags; + u32 cflags; + u32 ecf; + u32 cipher_alg; + u32 cipher_mode; + u32 cipher_type; + u32 hash_alg; + u32 hash_mode; + u32 hash_type; + u32 sctx_size; /* SCTX length in words */ + u32 sctx_pl_len; /* SCTX payload length in bytes */ + + packet_log("\n"); + packet_log("SPU Message header %p len: %u\n", buf, buf_len); + + /* ========== Decode MH ========== */ + packet_log(" MH 0x%08x\n", be32_to_cpu(*((u32 *)ptr))); + if (spuh->mh.flags & MH_SCTX_PRES) + packet_log(" SCTX present\n"); + if (spuh->mh.flags & MH_BDESC_PRES) + packet_log(" BDESC present\n"); + if (spuh->mh.flags & MH_MFM_PRES) + packet_log(" MFM present\n"); + if (spuh->mh.flags & MH_BD_PRES) + packet_log(" BD present\n"); + if (spuh->mh.flags & MH_HASH_PRES) + packet_log(" HASH present\n"); + if (spuh->mh.flags & MH_SUPDT_PRES) + packet_log(" SUPDT present\n"); + packet_log(" Opcode 0x%02x\n", spuh->mh.op_code); + + ptr += sizeof(spuh->mh) + sizeof(spuh->emh); /* skip emh. unused */ + + /* ========== Decode SCTX ========== */ + if (spuh->mh.flags & MH_SCTX_PRES) { + pflags = be32_to_cpu(spuh->sa.proto_flags); + packet_log(" SCTX[0] 0x%08x\n", pflags); + sctx_size = pflags & SCTX_SIZE; + packet_log(" Size %u words\n", sctx_size); + + cflags = be32_to_cpu(spuh->sa.cipher_flags); + packet_log(" SCTX[1] 0x%08x\n", cflags); + packet_log(" Inbound:%lu (1:decrypt/vrfy 0:encrypt/auth)\n", + (cflags & CIPHER_INBOUND) >> CIPHER_INBOUND_SHIFT); + packet_log(" Order:%lu (1:AuthFirst 0:EncFirst)\n", + (cflags & CIPHER_ORDER) >> CIPHER_ORDER_SHIFT); + packet_log(" ICV_IS_512:%lx\n", + (cflags & ICV_IS_512) >> ICV_IS_512_SHIFT); + cipher_alg = (cflags & CIPHER_ALG) >> CIPHER_ALG_SHIFT; + cipher_mode = (cflags & CIPHER_MODE) >> CIPHER_MODE_SHIFT; + cipher_type = (cflags & CIPHER_TYPE) >> CIPHER_TYPE_SHIFT; + packet_log(" Crypto Alg:%u Mode:%u Type:%u\n", + cipher_alg, cipher_mode, cipher_type); + hash_alg = (cflags & HASH_ALG) >> HASH_ALG_SHIFT; + hash_mode = (cflags & HASH_MODE) >> HASH_MODE_SHIFT; + hash_type = (cflags & HASH_TYPE) >> HASH_TYPE_SHIFT; + packet_log(" Hash Alg:%x Mode:%x Type:%x\n", + hash_alg, hash_mode, hash_type); + packet_log(" UPDT_Offset:%u\n", cflags & UPDT_OFST); + + ecf = be32_to_cpu(spuh->sa.ecf); + packet_log(" SCTX[2] 0x%08x\n", ecf); + packet_log(" WriteICV:%lu CheckICV:%lu ICV_SIZE:%u ", + (ecf & INSERT_ICV) >> INSERT_ICV_SHIFT, + (ecf & CHECK_ICV) >> CHECK_ICV_SHIFT, + (ecf & ICV_SIZE) >> ICV_SIZE_SHIFT); + packet_log("BD_SUPPRESS:%lu\n", + (ecf & BD_SUPPRESS) >> BD_SUPPRESS_SHIFT); + packet_log(" SCTX_IV:%lu ExplicitIV:%lu GenIV:%lu ", + (ecf & SCTX_IV) >> SCTX_IV_SHIFT, + (ecf & EXPLICIT_IV) >> EXPLICIT_IV_SHIFT, + (ecf & GEN_IV) >> GEN_IV_SHIFT); + packet_log("IV_OV_OFST:%lu EXP_IV_SIZE:%u\n", + (ecf & IV_OFFSET) >> IV_OFFSET_SHIFT, + ecf & EXP_IV_SIZE); + + ptr += sizeof(struct SCTX); + + if (hash_alg && hash_mode) { + char *name = "NONE"; + + switch (hash_alg) { + case HASH_ALG_MD5: + hash_key_len = 16; + name = "MD5"; + break; + case HASH_ALG_SHA1: + hash_key_len = 20; + name = "SHA1"; + break; + case HASH_ALG_SHA224: + hash_key_len = 28; + name = "SHA224"; + break; + case HASH_ALG_SHA256: + hash_key_len = 32; + name = "SHA256"; + break; + case HASH_ALG_SHA384: + hash_key_len = 48; + name = "SHA384"; + break; + case HASH_ALG_SHA512: + hash_key_len = 64; + name = "SHA512"; + break; + case HASH_ALG_AES: + hash_key_len = 0; + name = "AES"; + break; + case HASH_ALG_NONE: + break; + } + + packet_log(" Auth Key Type:%s Length:%u Bytes\n", + name, hash_key_len); + packet_dump(" KEY: ", ptr, hash_key_len); + ptr += hash_key_len; + } else if ((hash_alg == HASH_ALG_AES) && + (hash_mode == HASH_MODE_XCBC)) { + char *name = "NONE"; + + switch (cipher_type) { + case CIPHER_TYPE_AES128: + hash_key_len = 16; + name = "AES128-XCBC"; + break; + case CIPHER_TYPE_AES192: + hash_key_len = 24; + name = "AES192-XCBC"; + break; + case CIPHER_TYPE_AES256: + hash_key_len = 32; + name = "AES256-XCBC"; + break; + } + packet_log(" Auth Key Type:%s Length:%u Bytes\n", + name, hash_key_len); + packet_dump(" KEY: ", ptr, hash_key_len); + ptr += hash_key_len; + } + + if (hash_alg && (hash_mode == HASH_MODE_NONE) && + (hash_type == HASH_TYPE_UPDT)) { + char *name = "NONE"; + + switch (hash_alg) { + case HASH_ALG_MD5: + hash_state_len = 16; + name = "MD5"; + break; + case HASH_ALG_SHA1: + hash_state_len = 20; + name = "SHA1"; + break; + case HASH_ALG_SHA224: + hash_state_len = 32; + name = "SHA224"; + break; + case HASH_ALG_SHA256: + hash_state_len = 32; + name = "SHA256"; + break; + case HASH_ALG_SHA384: + hash_state_len = 48; + name = "SHA384"; + break; + case HASH_ALG_SHA512: + hash_state_len = 64; + name = "SHA512"; + break; + case HASH_ALG_AES: + hash_state_len = 0; + name = "AES"; + break; + case HASH_ALG_NONE: + break; + } + + packet_log(" Auth State Type:%s Length:%u Bytes\n", + name, hash_state_len); + packet_dump(" State: ", ptr, hash_state_len); + ptr += hash_state_len; + } + + if (cipher_alg) { + char *name = "NONE"; + + switch (cipher_alg) { + case CIPHER_ALG_DES: + cipher_key_len = 8; + name = "DES"; + break; + case CIPHER_ALG_3DES: + cipher_key_len = 24; + name = "3DES"; + break; + case CIPHER_ALG_RC4: + cipher_key_len = 260; + name = "ARC4"; + break; + case CIPHER_ALG_AES: + switch (cipher_type) { + case CIPHER_TYPE_AES128: + cipher_key_len = 16; + name = "AES128"; + break; + case CIPHER_TYPE_AES192: + cipher_key_len = 24; + name = "AES192"; + break; + case CIPHER_TYPE_AES256: + cipher_key_len = 32; + name = "AES256"; + break; + } + break; + case CIPHER_ALG_NONE: + break; + } + + packet_log(" Cipher Key Type:%s Length:%u Bytes\n", + name, cipher_key_len); + + /* XTS has two keys */ + if (cipher_mode == CIPHER_MODE_XTS) { + packet_dump(" KEY2: ", ptr, cipher_key_len); + ptr += cipher_key_len; + packet_dump(" KEY1: ", ptr, cipher_key_len); + ptr += cipher_key_len; + + cipher_key_len *= 2; + } else { + packet_dump(" KEY: ", ptr, cipher_key_len); + ptr += cipher_key_len; + } + + if (ecf & SCTX_IV) { + sctx_pl_len = sctx_size * sizeof(u32) - + sizeof(struct SCTX); + iv_len = sctx_pl_len - + (hash_key_len + hash_state_len + + cipher_key_len); + packet_log(" IV Length:%u Bytes\n", iv_len); + packet_dump(" IV: ", ptr, iv_len); + ptr += iv_len; + } + } + } + + /* ========== Decode BDESC ========== */ + if (spuh->mh.flags & MH_BDESC_PRES) { +#ifdef DEBUG + struct BDESC_HEADER *bdesc = (struct BDESC_HEADER *)ptr; +#endif + packet_log(" BDESC[0] 0x%08x\n", be32_to_cpu(*((u32 *)ptr))); + packet_log(" OffsetMAC:%u LengthMAC:%u\n", + be16_to_cpu(bdesc->offset_mac), + be16_to_cpu(bdesc->length_mac)); + ptr += sizeof(u32); + + packet_log(" BDESC[1] 0x%08x\n", be32_to_cpu(*((u32 *)ptr))); + packet_log(" OffsetCrypto:%u LengthCrypto:%u\n", + be16_to_cpu(bdesc->offset_crypto), + be16_to_cpu(bdesc->length_crypto)); + ptr += sizeof(u32); + + packet_log(" BDESC[2] 0x%08x\n", be32_to_cpu(*((u32 *)ptr))); + packet_log(" OffsetICV:%u OffsetIV:%u\n", + be16_to_cpu(bdesc->offset_icv), + be16_to_cpu(bdesc->offset_iv)); + ptr += sizeof(u32); + } + + /* ========== Decode BD ========== */ + if (spuh->mh.flags & MH_BD_PRES) { +#ifdef DEBUG + struct BD_HEADER *bd = (struct BD_HEADER *)ptr; +#endif + packet_log(" BD[0] 0x%08x\n", be32_to_cpu(*((u32 *)ptr))); + packet_log(" Size:%ubytes PrevLength:%u\n", + be16_to_cpu(bd->size), be16_to_cpu(bd->prev_length)); + ptr += 4; + } + + /* Double check sanity */ + if (buf + buf_len != ptr) { + packet_log(" Packet parsed incorrectly. "); + packet_log("buf:%p buf_len:%u buf+buf_len:%p ptr:%p\n", + buf, buf_len, buf + buf_len, ptr); + } + + packet_log("\n"); +} + +/** + * spum_ns2_ctx_max_payload() - Determine the max length of the payload for a + * SPU message for a given cipher and hash alg context. + * @cipher_alg: The cipher algorithm + * @cipher_mode: The cipher mode + * @blocksize: The size of a block of data for this algo + * + * The max payload must be a multiple of the blocksize so that if a request is + * too large to fit in a single SPU message, the request can be broken into + * max_payload sized chunks. Each chunk must be a multiple of blocksize. + * + * Return: Max payload length in bytes + */ +u32 spum_ns2_ctx_max_payload(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + unsigned int blocksize) +{ + u32 max_payload = SPUM_NS2_MAX_PAYLOAD; + u32 excess; + + /* In XTS on SPU-M, we'll need to insert tweak before input data */ + if (cipher_mode == CIPHER_MODE_XTS) + max_payload -= SPU_XTS_TWEAK_SIZE; + + excess = max_payload % blocksize; + + return max_payload - excess; +} + +/** + * spum_nsp_ctx_max_payload() - Determine the max length of the payload for a + * SPU message for a given cipher and hash alg context. + * @cipher_alg: The cipher algorithm + * @cipher_mode: The cipher mode + * @blocksize: The size of a block of data for this algo + * + * The max payload must be a multiple of the blocksize so that if a request is + * too large to fit in a single SPU message, the request can be broken into + * max_payload sized chunks. Each chunk must be a multiple of blocksize. + * + * Return: Max payload length in bytes + */ +u32 spum_nsp_ctx_max_payload(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + unsigned int blocksize) +{ + u32 max_payload = SPUM_NSP_MAX_PAYLOAD; + u32 excess; + + /* In XTS on SPU-M, we'll need to insert tweak before input data */ + if (cipher_mode == CIPHER_MODE_XTS) + max_payload -= SPU_XTS_TWEAK_SIZE; + + excess = max_payload % blocksize; + + return max_payload - excess; +} + +/** spum_payload_length() - Given a SPU-M message header, extract the payload + * length. + * @spu_hdr: Start of SPU header + * + * Assumes just MH, EMH, BD (no SCTX, BDESC. Works for response frames. + * + * Return: payload length in bytes + */ +u32 spum_payload_length(u8 *spu_hdr) +{ + struct BD_HEADER *bd; + u32 pl_len; + + /* Find BD header. skip MH, EMH */ + bd = (struct BD_HEADER *)(spu_hdr + 8); + pl_len = be16_to_cpu(bd->size); + + return pl_len; +} + +/** + * spum_response_hdr_len() - Given the length of the hash key and encryption + * key, determine the expected length of a SPU response header. + * @auth_key_len: authentication key length (bytes) + * @enc_key_len: encryption key length (bytes) + * @is_hash: true if response message is for a hash operation + * + * Return: length of SPU response header (bytes) + */ +u16 spum_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash) +{ + if (is_hash) + return SPU_HASH_RESP_HDR_LEN; + else + return SPU_RESP_HDR_LEN; +} + +/** + * spum_hash_pad_len() - Calculate the length of hash padding required to extend + * data to a full block size. + * @hash_alg: hash algorithm + * @hash_mode: hash mode + * @chunksize: length of data, in bytes + * @hash_block_size: size of a block of data for hash algorithm + * + * Reserve space for 1 byte (0x80) start of pad and the total length as u64 + * + * Return: length of hash pad in bytes + */ +u16 spum_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode, + u32 chunksize, u16 hash_block_size) +{ + unsigned int length_len; + unsigned int used_space_last_block; + int hash_pad_len; + + /* AES-XCBC hash requires just padding to next block boundary */ + if ((hash_alg == HASH_ALG_AES) && (hash_mode == HASH_MODE_XCBC)) { + used_space_last_block = chunksize % hash_block_size; + hash_pad_len = hash_block_size - used_space_last_block; + if (hash_pad_len >= hash_block_size) + hash_pad_len -= hash_block_size; + return hash_pad_len; + } + + used_space_last_block = chunksize % hash_block_size + 1; + if ((hash_alg == HASH_ALG_SHA384) || (hash_alg == HASH_ALG_SHA512)) + length_len = 2 * sizeof(u64); + else + length_len = sizeof(u64); + + used_space_last_block += length_len; + hash_pad_len = hash_block_size - used_space_last_block; + if (hash_pad_len < 0) + hash_pad_len += hash_block_size; + + hash_pad_len += 1 + length_len; + return hash_pad_len; +} + +/** + * spum_gcm_ccm_pad_len() - Determine the required length of GCM or CCM padding. + * @cipher_mode: Algo type + * @data_size: Length of plaintext (bytes) + * + * @Return: Length of padding, in bytes + */ +u32 spum_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode, + unsigned int data_size) +{ + u32 pad_len = 0; + u32 m1 = SPU_GCM_CCM_ALIGN - 1; + + if ((cipher_mode == CIPHER_MODE_GCM) || + (cipher_mode == CIPHER_MODE_CCM)) + pad_len = ((data_size + m1) & ~m1) - data_size; + + return pad_len; +} + +/** + * spum_assoc_resp_len() - Determine the size of the receive buffer required to + * catch associated data. + * @cipher_mode: cipher mode + * @assoc_len: length of associated data (bytes) + * @iv_len: length of IV (bytes) + * @is_encrypt: true if encrypting. false if decrypting. + * + * Return: length of associated data in response message (bytes) + */ +u32 spum_assoc_resp_len(enum spu_cipher_mode cipher_mode, + unsigned int assoc_len, unsigned int iv_len, + bool is_encrypt) +{ + u32 buflen = 0; + u32 pad; + + if (assoc_len) + buflen = assoc_len; + + if (cipher_mode == CIPHER_MODE_GCM) { + /* AAD needs to be padded in responses too */ + pad = spum_gcm_ccm_pad_len(cipher_mode, buflen); + buflen += pad; + } + if (cipher_mode == CIPHER_MODE_CCM) { + /* + * AAD needs to be padded in responses too + * for CCM, len + 2 needs to be 128-bit aligned. + */ + pad = spum_gcm_ccm_pad_len(cipher_mode, buflen + 2); + buflen += pad; + } + + return buflen; +} + +/** + * spu_aead_ivlen() - Calculate the length of the AEAD IV to be included + * in a SPU request after the AAD and before the payload. + * @cipher_mode: cipher mode + * @iv_ctr_len: initialization vector length in bytes + * + * In Linux ~4.2 and later, the assoc_data sg includes the IV. So no need + * to include the IV as a separate field in the SPU request msg. + * + * Return: Length of AEAD IV in bytes + */ +u8 spum_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len) +{ + return 0; +} + +/** + * spum_hash_type() - Determine the type of hash operation. + * @src_sent: The number of bytes in the current request that have already + * been sent to the SPU to be hashed. + * + * We do not use HASH_TYPE_FULL for requests that fit in a single SPU message. + * Using FULL causes failures (such as when the string to be hashed is empty). + * For similar reasons, we never use HASH_TYPE_FIN. Instead, submit messages + * as INIT or UPDT and do the hash padding in sw. + */ +enum hash_type spum_hash_type(u32 src_sent) +{ + return src_sent ? HASH_TYPE_UPDT : HASH_TYPE_INIT; +} + +/** + * spum_digest_size() - Determine the size of a hash digest to expect the SPU to + * return. + * alg_digest_size: Number of bytes in the final digest for the given algo + * alg: The hash algorithm + * htype: Type of hash operation (init, update, full, etc) + * + * When doing incremental hashing for an algorithm with a truncated hash + * (e.g., SHA224), the SPU returns the full digest so that it can be fed back as + * a partial result for the next chunk. + */ +u32 spum_digest_size(u32 alg_digest_size, enum hash_alg alg, + enum hash_type htype) +{ + u32 digestsize = alg_digest_size; + + /* SPU returns complete digest when doing incremental hash and truncated + * hash algo. + */ + if ((htype == HASH_TYPE_INIT) || (htype == HASH_TYPE_UPDT)) { + if (alg == HASH_ALG_SHA224) + digestsize = SHA256_DIGEST_SIZE; + else if (alg == HASH_ALG_SHA384) + digestsize = SHA512_DIGEST_SIZE; + } + return digestsize; +} + +/** + * spum_create_request() - Build a SPU request message header, up to and + * including the BD header. Construct the message starting at spu_hdr. Caller + * should allocate this buffer in DMA-able memory at least SPU_HEADER_ALLOC_LEN + * bytes long. + * @spu_hdr: Start of buffer where SPU request header is to be written + * @req_opts: SPU request message options + * @cipher_parms: Parameters related to cipher algorithm + * @hash_parms: Parameters related to hash algorithm + * @aead_parms: Parameters related to AEAD operation + * @data_size: Length of data to be encrypted or authenticated. If AEAD, does + * not include length of AAD. + + * Return: the length of the SPU header in bytes. 0 if an error occurs. + */ +u32 spum_create_request(u8 *spu_hdr, + struct spu_request_opts *req_opts, + struct spu_cipher_parms *cipher_parms, + struct spu_hash_parms *hash_parms, + struct spu_aead_parms *aead_parms, + unsigned int data_size) +{ + struct SPUHEADER *spuh; + struct BDESC_HEADER *bdesc; + struct BD_HEADER *bd; + + u8 *ptr; + u32 protocol_bits = 0; + u32 cipher_bits = 0; + u32 ecf_bits = 0; + u8 sctx_words = 0; + unsigned int buf_len = 0; + + /* size of the cipher payload */ + unsigned int cipher_len = hash_parms->prebuf_len + data_size + + hash_parms->pad_len; + + /* offset of prebuf or data from end of BD header */ + unsigned int cipher_offset = aead_parms->assoc_size + + aead_parms->iv_len + aead_parms->aad_pad_len; + + /* total size of the DB data (without STAT word padding) */ + unsigned int real_db_size = spu_real_db_size(aead_parms->assoc_size, + aead_parms->iv_len, + hash_parms->prebuf_len, + data_size, + aead_parms->aad_pad_len, + aead_parms->data_pad_len, + hash_parms->pad_len); + + unsigned int auth_offset = 0; + unsigned int offset_iv = 0; + + /* size/offset of the auth payload */ + unsigned int auth_len; + + auth_len = real_db_size; + + if (req_opts->is_aead && req_opts->is_inbound) + cipher_len -= hash_parms->digestsize; + + if (req_opts->is_aead && req_opts->is_inbound) + auth_len -= hash_parms->digestsize; + + if ((hash_parms->alg == HASH_ALG_AES) && + (hash_parms->mode == HASH_MODE_XCBC)) { + auth_len -= hash_parms->pad_len; + cipher_len -= hash_parms->pad_len; + } + + flow_log("%s()\n", __func__); + flow_log(" in:%u authFirst:%u\n", + req_opts->is_inbound, req_opts->auth_first); + flow_log(" %s. cipher alg:%u mode:%u type %u\n", + spu_alg_name(cipher_parms->alg, cipher_parms->mode), + cipher_parms->alg, cipher_parms->mode, cipher_parms->type); + flow_log(" key: %d\n", cipher_parms->key_len); + flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len); + flow_log(" iv: %d\n", cipher_parms->iv_len); + flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len); + flow_log(" auth alg:%u mode:%u type %u\n", + hash_parms->alg, hash_parms->mode, hash_parms->type); + flow_log(" digestsize: %u\n", hash_parms->digestsize); + flow_log(" authkey: %d\n", hash_parms->key_len); + flow_dump(" authkey: ", hash_parms->key_buf, hash_parms->key_len); + flow_log(" assoc_size:%u\n", aead_parms->assoc_size); + flow_log(" prebuf_len:%u\n", hash_parms->prebuf_len); + flow_log(" data_size:%u\n", data_size); + flow_log(" hash_pad_len:%u\n", hash_parms->pad_len); + flow_log(" real_db_size:%u\n", real_db_size); + flow_log(" auth_offset:%u auth_len:%u cipher_offset:%u cipher_len:%u\n", + auth_offset, auth_len, cipher_offset, cipher_len); + flow_log(" aead_iv: %u\n", aead_parms->iv_len); + + /* starting out: zero the header (plus some) */ + ptr = spu_hdr; + memset(ptr, 0, sizeof(struct SPUHEADER)); + + /* format master header word */ + /* Do not set the next bit even though the datasheet says to */ + spuh = (struct SPUHEADER *)ptr; + ptr += sizeof(struct SPUHEADER); + buf_len += sizeof(struct SPUHEADER); + + spuh->mh.op_code = SPU_CRYPTO_OPERATION_GENERIC; + spuh->mh.flags |= (MH_SCTX_PRES | MH_BDESC_PRES | MH_BD_PRES); + + /* Format sctx word 0 (protocol_bits) */ + sctx_words = 3; /* size in words */ + + /* Format sctx word 1 (cipher_bits) */ + if (req_opts->is_inbound) + cipher_bits |= CIPHER_INBOUND; + if (req_opts->auth_first) + cipher_bits |= CIPHER_ORDER; + + /* Set the crypto parameters in the cipher.flags */ + cipher_bits |= cipher_parms->alg << CIPHER_ALG_SHIFT; + cipher_bits |= cipher_parms->mode << CIPHER_MODE_SHIFT; + cipher_bits |= cipher_parms->type << CIPHER_TYPE_SHIFT; + + /* Set the auth parameters in the cipher.flags */ + cipher_bits |= hash_parms->alg << HASH_ALG_SHIFT; + cipher_bits |= hash_parms->mode << HASH_MODE_SHIFT; + cipher_bits |= hash_parms->type << HASH_TYPE_SHIFT; + + /* + * Format sctx extensions if required, and update main fields if + * required) + */ + if (hash_parms->alg) { + /* Write the authentication key material if present */ + if (hash_parms->key_len) { + memcpy(ptr, hash_parms->key_buf, hash_parms->key_len); + ptr += hash_parms->key_len; + buf_len += hash_parms->key_len; + sctx_words += hash_parms->key_len / 4; + } + + if ((cipher_parms->mode == CIPHER_MODE_GCM) || + (cipher_parms->mode == CIPHER_MODE_CCM)) + /* unpadded length */ + offset_iv = aead_parms->assoc_size; + + /* if GCM/CCM we need to write ICV into the payload */ + if (!req_opts->is_inbound) { + if ((cipher_parms->mode == CIPHER_MODE_GCM) || + (cipher_parms->mode == CIPHER_MODE_CCM)) + ecf_bits |= 1 << INSERT_ICV_SHIFT; + } else { + ecf_bits |= CHECK_ICV; + } + + /* Inform the SPU of the ICV size (in words) */ + if (hash_parms->digestsize == 64) + cipher_bits |= ICV_IS_512; + else + ecf_bits |= + (hash_parms->digestsize / 4) << ICV_SIZE_SHIFT; + } + + if (req_opts->bd_suppress) + ecf_bits |= BD_SUPPRESS; + + /* copy the encryption keys in the SAD entry */ + if (cipher_parms->alg) { + if (cipher_parms->key_len) { + memcpy(ptr, cipher_parms->key_buf, + cipher_parms->key_len); + ptr += cipher_parms->key_len; + buf_len += cipher_parms->key_len; + sctx_words += cipher_parms->key_len / 4; + } + + /* + * if encrypting then set IV size, use SCTX IV unless no IV + * given here + */ + if (cipher_parms->iv_buf && cipher_parms->iv_len) { + /* Use SCTX IV */ + ecf_bits |= SCTX_IV; + + /* cipher iv provided so put it in here */ + memcpy(ptr, cipher_parms->iv_buf, cipher_parms->iv_len); + + ptr += cipher_parms->iv_len; + buf_len += cipher_parms->iv_len; + sctx_words += cipher_parms->iv_len / 4; + } + } + + /* + * RFC4543 (GMAC/ESP) requires data to be sent as part of AAD + * so we need to override the BDESC parameters. + */ + if (req_opts->is_rfc4543) { + if (req_opts->is_inbound) + data_size -= hash_parms->digestsize; + offset_iv = aead_parms->assoc_size + data_size; + cipher_len = 0; + cipher_offset = offset_iv; + auth_len = cipher_offset + aead_parms->data_pad_len; + } + + /* write in the total sctx length now that we know it */ + protocol_bits |= sctx_words; + + /* Endian adjust the SCTX */ + spuh->sa.proto_flags = cpu_to_be32(protocol_bits); + spuh->sa.cipher_flags = cpu_to_be32(cipher_bits); + spuh->sa.ecf = cpu_to_be32(ecf_bits); + + /* === create the BDESC section === */ + bdesc = (struct BDESC_HEADER *)ptr; + + bdesc->offset_mac = cpu_to_be16(auth_offset); + bdesc->length_mac = cpu_to_be16(auth_len); + bdesc->offset_crypto = cpu_to_be16(cipher_offset); + bdesc->length_crypto = cpu_to_be16(cipher_len); + + /* + * CCM in SPU-M requires that ICV not be in same 32-bit word as data or + * padding. So account for padding as necessary. + */ + if (cipher_parms->mode == CIPHER_MODE_CCM) + auth_len += spum_wordalign_padlen(auth_len); + + bdesc->offset_icv = cpu_to_be16(auth_len); + bdesc->offset_iv = cpu_to_be16(offset_iv); + + ptr += sizeof(struct BDESC_HEADER); + buf_len += sizeof(struct BDESC_HEADER); + + /* === no MFM section === */ + + /* === create the BD section === */ + + /* add the BD header */ + bd = (struct BD_HEADER *)ptr; + bd->size = cpu_to_be16(real_db_size); + bd->prev_length = 0; + + ptr += sizeof(struct BD_HEADER); + buf_len += sizeof(struct BD_HEADER); + + packet_dump(" SPU request header: ", spu_hdr, buf_len); + + return buf_len; +} + +/** + * spum_cipher_req_init() - Build a SPU request message header, up to and + * including the BD header. + * @spu_hdr: Start of SPU request header (MH) + * @cipher_parms: Parameters that describe the cipher request + * + * Construct the message starting at spu_hdr. Caller should allocate this buffer + * in DMA-able memory at least SPU_HEADER_ALLOC_LEN bytes long. + * + * Return: the length of the SPU header in bytes. 0 if an error occurs. + */ +u16 spum_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms) +{ + struct SPUHEADER *spuh; + u32 protocol_bits = 0; + u32 cipher_bits = 0; + u32 ecf_bits = 0; + u8 sctx_words = 0; + u8 *ptr = spu_hdr; + + flow_log("%s()\n", __func__); + flow_log(" cipher alg:%u mode:%u type %u\n", cipher_parms->alg, + cipher_parms->mode, cipher_parms->type); + flow_log(" cipher_iv_len: %u\n", cipher_parms->iv_len); + flow_log(" key: %d\n", cipher_parms->key_len); + flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len); + + /* starting out: zero the header (plus some) */ + memset(spu_hdr, 0, sizeof(struct SPUHEADER)); + ptr += sizeof(struct SPUHEADER); + + /* format master header word */ + /* Do not set the next bit even though the datasheet says to */ + spuh = (struct SPUHEADER *)spu_hdr; + + spuh->mh.op_code = SPU_CRYPTO_OPERATION_GENERIC; + spuh->mh.flags |= (MH_SCTX_PRES | MH_BDESC_PRES | MH_BD_PRES); + + /* Format sctx word 0 (protocol_bits) */ + sctx_words = 3; /* size in words */ + + /* copy the encryption keys in the SAD entry */ + if (cipher_parms->alg) { + if (cipher_parms->key_len) { + ptr += cipher_parms->key_len; + sctx_words += cipher_parms->key_len / 4; + } + + /* + * if encrypting then set IV size, use SCTX IV unless no IV + * given here + */ + if (cipher_parms->iv_len) { + /* Use SCTX IV */ + ecf_bits |= SCTX_IV; + ptr += cipher_parms->iv_len; + sctx_words += cipher_parms->iv_len / 4; + } + } + + /* Set the crypto parameters in the cipher.flags */ + cipher_bits |= cipher_parms->alg << CIPHER_ALG_SHIFT; + cipher_bits |= cipher_parms->mode << CIPHER_MODE_SHIFT; + cipher_bits |= cipher_parms->type << CIPHER_TYPE_SHIFT; + + /* copy the encryption keys in the SAD entry */ + if (cipher_parms->alg && cipher_parms->key_len) + memcpy(spuh + 1, cipher_parms->key_buf, cipher_parms->key_len); + + /* write in the total sctx length now that we know it */ + protocol_bits |= sctx_words; + + /* Endian adjust the SCTX */ + spuh->sa.proto_flags = cpu_to_be32(protocol_bits); + + /* Endian adjust the SCTX */ + spuh->sa.cipher_flags = cpu_to_be32(cipher_bits); + spuh->sa.ecf = cpu_to_be32(ecf_bits); + + packet_dump(" SPU request header: ", spu_hdr, + sizeof(struct SPUHEADER)); + + return sizeof(struct SPUHEADER) + cipher_parms->key_len + + cipher_parms->iv_len + sizeof(struct BDESC_HEADER) + + sizeof(struct BD_HEADER); +} + +/** + * spum_cipher_req_finish() - Finish building a SPU request message header for a + * block cipher request. Assumes much of the header was already filled in at + * setkey() time in spu_cipher_req_init(). + * @spu_hdr: Start of the request message header (MH field) + * @spu_req_hdr_len: Length in bytes of the SPU request header + * @isInbound: 0 encrypt, 1 decrypt + * @cipher_parms: Parameters describing cipher operation to be performed + * @update_key: If true, rewrite the cipher key in SCTX + * @data_size: Length of the data in the BD field + * + * Assumes much of the header was already filled in at setkey() time in + * spum_cipher_req_init(). + * spum_cipher_req_init() fills in the encryption key. For RC4, when submitting + * a request for a non-first chunk, we use the 260-byte SUPDT field from the + * previous response as the key. update_key is true for this case. Unused in all + * other cases. + */ +void spum_cipher_req_finish(u8 *spu_hdr, + u16 spu_req_hdr_len, + unsigned int is_inbound, + struct spu_cipher_parms *cipher_parms, + bool update_key, + unsigned int data_size) +{ + struct SPUHEADER *spuh; + struct BDESC_HEADER *bdesc; + struct BD_HEADER *bd; + u8 *bdesc_ptr = spu_hdr + spu_req_hdr_len - + (sizeof(struct BD_HEADER) + sizeof(struct BDESC_HEADER)); + + u32 cipher_bits; + + flow_log("%s()\n", __func__); + flow_log(" in: %u\n", is_inbound); + flow_log(" cipher alg: %u, cipher_type: %u\n", cipher_parms->alg, + cipher_parms->type); + if (update_key) { + flow_log(" cipher key len: %u\n", cipher_parms->key_len); + flow_dump(" key: ", cipher_parms->key_buf, + cipher_parms->key_len); + } + + /* + * In XTS mode, API puts "i" parameter (block tweak) in IV. For + * SPU-M, should be in start of the BD; tx_sg_create() copies it there. + * IV in SPU msg for SPU-M should be 0, since that's the "j" parameter + * (block ctr within larger data unit) - given we can send entire disk + * block (<= 4KB) in 1 SPU msg, don't need to use this parameter. + */ + if (cipher_parms->mode == CIPHER_MODE_XTS) + memset(cipher_parms->iv_buf, 0, cipher_parms->iv_len); + + flow_log(" iv len: %d\n", cipher_parms->iv_len); + flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len); + flow_log(" data_size: %u\n", data_size); + + /* format master header word */ + /* Do not set the next bit even though the datasheet says to */ + spuh = (struct SPUHEADER *)spu_hdr; + + /* cipher_bits was initialized at setkey time */ + cipher_bits = be32_to_cpu(spuh->sa.cipher_flags); + + /* Format sctx word 1 (cipher_bits) */ + if (is_inbound) + cipher_bits |= CIPHER_INBOUND; + else + cipher_bits &= ~CIPHER_INBOUND; + + /* update encryption key for RC4 on non-first chunk */ + if (update_key) { + spuh->sa.cipher_flags |= + cipher_parms->type << CIPHER_TYPE_SHIFT; + memcpy(spuh + 1, cipher_parms->key_buf, cipher_parms->key_len); + } + + if (cipher_parms->alg && cipher_parms->iv_buf && cipher_parms->iv_len) + /* cipher iv provided so put it in here */ + memcpy(bdesc_ptr - cipher_parms->iv_len, cipher_parms->iv_buf, + cipher_parms->iv_len); + + spuh->sa.cipher_flags = cpu_to_be32(cipher_bits); + + /* === create the BDESC section === */ + bdesc = (struct BDESC_HEADER *)bdesc_ptr; + bdesc->offset_mac = 0; + bdesc->length_mac = 0; + bdesc->offset_crypto = 0; + + /* XTS mode, data_size needs to include tweak parameter */ + if (cipher_parms->mode == CIPHER_MODE_XTS) + bdesc->length_crypto = cpu_to_be16(data_size + + SPU_XTS_TWEAK_SIZE); + else + bdesc->length_crypto = cpu_to_be16(data_size); + + bdesc->offset_icv = 0; + bdesc->offset_iv = 0; + + /* === no MFM section === */ + + /* === create the BD section === */ + /* add the BD header */ + bd = (struct BD_HEADER *)(bdesc_ptr + sizeof(struct BDESC_HEADER)); + bd->size = cpu_to_be16(data_size); + + /* XTS mode, data_size needs to include tweak parameter */ + if (cipher_parms->mode == CIPHER_MODE_XTS) + bd->size = cpu_to_be16(data_size + SPU_XTS_TWEAK_SIZE); + else + bd->size = cpu_to_be16(data_size); + + bd->prev_length = 0; + + packet_dump(" SPU request header: ", spu_hdr, spu_req_hdr_len); +} + +/** + * spum_request_pad() - Create pad bytes at the end of the data. + * @pad_start: Start of buffer where pad bytes are to be written + * @gcm_ccm_padding: length of GCM/CCM padding, in bytes + * @hash_pad_len: Number of bytes of padding extend data to full block + * @auth_alg: authentication algorithm + * @auth_mode: authentication mode + * @total_sent: length inserted at end of hash pad + * @status_padding: Number of bytes of padding to align STATUS word + * + * There may be three forms of pad: + * 1. GCM/CCM pad - for GCM/CCM mode ciphers, pad to 16-byte alignment + * 2. hash pad - pad to a block length, with 0x80 data terminator and + * size at the end + * 3. STAT pad - to ensure the STAT field is 4-byte aligned + */ +void spum_request_pad(u8 *pad_start, + u32 gcm_ccm_padding, + u32 hash_pad_len, + enum hash_alg auth_alg, + enum hash_mode auth_mode, + unsigned int total_sent, u32 status_padding) +{ + u8 *ptr = pad_start; + + /* fix data alignent for GCM/CCM */ + if (gcm_ccm_padding > 0) { + flow_log(" GCM: padding to 16 byte alignment: %u bytes\n", + gcm_ccm_padding); + memset(ptr, 0, gcm_ccm_padding); + ptr += gcm_ccm_padding; + } + + if (hash_pad_len > 0) { + /* clear the padding section */ + memset(ptr, 0, hash_pad_len); + + if ((auth_alg == HASH_ALG_AES) && + (auth_mode == HASH_MODE_XCBC)) { + /* AES/XCBC just requires padding to be 0s */ + ptr += hash_pad_len; + } else { + /* terminate the data */ + *ptr = 0x80; + ptr += (hash_pad_len - sizeof(u64)); + + /* add the size at the end as required per alg */ + if (auth_alg == HASH_ALG_MD5) + *(u64 *)ptr = cpu_to_le64((u64)total_sent * 8); + else /* SHA1, SHA2-224, SHA2-256 */ + *(u64 *)ptr = cpu_to_be64((u64)total_sent * 8); + ptr += sizeof(u64); + } + } + + /* pad to a 4byte alignment for STAT */ + if (status_padding > 0) { + flow_log(" STAT: padding to 4 byte alignment: %u bytes\n", + status_padding); + + memset(ptr, 0, status_padding); + ptr += status_padding; + } +} + +/** + * spum_xts_tweak_in_payload() - Indicate that SPUM DOES place the XTS tweak + * field in the packet payload (rather than using IV) + * + * Return: 1 + */ +u8 spum_xts_tweak_in_payload(void) +{ + return 1; +} + +/** + * spum_tx_status_len() - Return the length of the STATUS field in a SPU + * response message. + * + * Return: Length of STATUS field in bytes. + */ +u8 spum_tx_status_len(void) +{ + return SPU_TX_STATUS_LEN; +} + +/** + * spum_rx_status_len() - Return the length of the STATUS field in a SPU + * response message. + * + * Return: Length of STATUS field in bytes. + */ +u8 spum_rx_status_len(void) +{ + return SPU_RX_STATUS_LEN; +} + +/** + * spum_status_process() - Process the status from a SPU response message. + * @statp: start of STATUS word + * Return: + * 0 - if status is good and response should be processed + * !0 - status indicates an error and response is invalid + */ +int spum_status_process(u8 *statp) +{ + u32 status; + + status = __be32_to_cpu(*(__be32 *)statp); + flow_log("SPU response STATUS %#08x\n", status); + if (status & SPU_STATUS_ERROR_FLAG) { + pr_err("%s() Warning: Error result from SPU: %#08x\n", + __func__, status); + if (status & SPU_STATUS_INVALID_ICV) + return SPU_INVALID_ICV; + return -EBADMSG; + } + return 0; +} + +/** + * spum_ccm_update_iv() - Update the IV as per the requirements for CCM mode. + * + * @digestsize: Digest size of this request + * @cipher_parms: (pointer to) cipher parmaeters, includes IV buf & IV len + * @assoclen: Length of AAD data + * @chunksize: length of input data to be sent in this req + * @is_encrypt: true if this is an output/encrypt operation + * @is_esp: true if this is an ESP / RFC4309 operation + * + */ +void spum_ccm_update_iv(unsigned int digestsize, + struct spu_cipher_parms *cipher_parms, + unsigned int assoclen, + unsigned int chunksize, + bool is_encrypt, + bool is_esp) +{ + u8 L; /* L from CCM algorithm, length of plaintext data */ + u8 mprime; /* M' from CCM algo, (M - 2) / 2, where M=authsize */ + u8 adata; + + if (cipher_parms->iv_len != CCM_AES_IV_SIZE) { + pr_err("%s(): Invalid IV len %d for CCM mode, should be %d\n", + __func__, cipher_parms->iv_len, CCM_AES_IV_SIZE); + return; + } + + /* + * IV needs to be formatted as follows: + * + * | Byte 0 | Bytes 1 - N | Bytes (N+1) - 15 | + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits 7 - 0 | Bits 7 - 0 | + * | 0 |Ad?|(M - 2) / 2| L - 1 | Nonce | Plaintext Length | + * + * Ad? = 1 if AAD present, 0 if not present + * M = size of auth field, 8, 12, or 16 bytes (SPU-M) -or- + * 4, 6, 8, 10, 12, 14, 16 bytes (SPU2) + * L = Size of Plaintext Length field; Nonce size = 15 - L + * + * It appears that the crypto API already expects the L-1 portion + * to be set in the first byte of the IV, which implicitly determines + * the nonce size, and also fills in the nonce. But the other bits + * in byte 0 as well as the plaintext length need to be filled in. + * + * In rfc4309/esp mode, L is not already in the supplied IV and + * we need to fill it in, as well as move the IV data to be after + * the salt + */ + if (is_esp) { + L = CCM_ESP_L_VALUE; /* RFC4309 has fixed L */ + } else { + /* L' = plaintext length - 1 so Plaintext length is L' + 1 */ + L = ((cipher_parms->iv_buf[0] & CCM_B0_L_PRIME) >> + CCM_B0_L_PRIME_SHIFT) + 1; + } + + mprime = (digestsize - 2) >> 1; /* M' = (M - 2) / 2 */ + adata = (assoclen > 0); /* adata = 1 if any associated data */ + + cipher_parms->iv_buf[0] = (adata << CCM_B0_ADATA_SHIFT) | + (mprime << CCM_B0_M_PRIME_SHIFT) | + ((L - 1) << CCM_B0_L_PRIME_SHIFT); + + /* Nonce is already filled in by crypto API, and is 15 - L bytes */ + + /* Don't include digest in plaintext size when decrypting */ + if (!is_encrypt) + chunksize -= digestsize; + + /* Fill in length of plaintext, formatted to be L bytes long */ + format_value_ccm(chunksize, &cipher_parms->iv_buf[15 - L + 1], L); +} + +/** + * spum_wordalign_padlen() - Given the length of a data field, determine the + * padding required to align the data following this field on a 4-byte boundary. + * @data_size: length of data field in bytes + * + * Return: length of status field padding, in bytes + */ +u32 spum_wordalign_padlen(u32 data_size) +{ + return ((data_size + 3) & ~3) - data_size; +} diff --git a/drivers/crypto/bcm/spu.h b/drivers/crypto/bcm/spu.h new file mode 100644 index 000000000000..aa6fc38db263 --- /dev/null +++ b/drivers/crypto/bcm/spu.h @@ -0,0 +1,287 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* + * This file contains the definition of SPU messages. There are currently two + * SPU message formats: SPU-M and SPU2. The hardware uses different values to + * identify the same things in SPU-M vs SPU2. So this file defines values that + * are hardware independent. Software can use these values for any version of + * SPU hardware. These values are used in APIs in spu.c. Functions internal to + * spu.c and spu2.c convert these to hardware-specific values. + */ + +#ifndef _SPU_H +#define _SPU_H + +#include <linux/types.h> +#include <linux/scatterlist.h> +#include <crypto/sha.h> + +enum spu_cipher_alg { + CIPHER_ALG_NONE = 0x0, + CIPHER_ALG_RC4 = 0x1, + CIPHER_ALG_DES = 0x2, + CIPHER_ALG_3DES = 0x3, + CIPHER_ALG_AES = 0x4, + CIPHER_ALG_LAST = 0x5 +}; + +enum spu_cipher_mode { + CIPHER_MODE_NONE = 0x0, + CIPHER_MODE_ECB = 0x0, + CIPHER_MODE_CBC = 0x1, + CIPHER_MODE_OFB = 0x2, + CIPHER_MODE_CFB = 0x3, + CIPHER_MODE_CTR = 0x4, + CIPHER_MODE_CCM = 0x5, + CIPHER_MODE_GCM = 0x6, + CIPHER_MODE_XTS = 0x7, + CIPHER_MODE_LAST = 0x8 +}; + +enum spu_cipher_type { + CIPHER_TYPE_NONE = 0x0, + CIPHER_TYPE_DES = 0x0, + CIPHER_TYPE_3DES = 0x0, + CIPHER_TYPE_INIT = 0x0, /* used for ARC4 */ + CIPHER_TYPE_AES128 = 0x0, + CIPHER_TYPE_AES192 = 0x1, + CIPHER_TYPE_UPDT = 0x1, /* used for ARC4 */ + CIPHER_TYPE_AES256 = 0x2, +}; + +enum hash_alg { + HASH_ALG_NONE = 0x0, + HASH_ALG_MD5 = 0x1, + HASH_ALG_SHA1 = 0x2, + HASH_ALG_SHA224 = 0x3, + HASH_ALG_SHA256 = 0x4, + HASH_ALG_AES = 0x5, + HASH_ALG_SHA384 = 0x6, + HASH_ALG_SHA512 = 0x7, + /* Keep SHA3 algorithms at the end always */ + HASH_ALG_SHA3_224 = 0x8, + HASH_ALG_SHA3_256 = 0x9, + HASH_ALG_SHA3_384 = 0xa, + HASH_ALG_SHA3_512 = 0xb, + HASH_ALG_LAST +}; + +enum hash_mode { + HASH_MODE_NONE = 0x0, + HASH_MODE_HASH = 0x0, + HASH_MODE_XCBC = 0x0, + HASH_MODE_CMAC = 0x1, + HASH_MODE_CTXT = 0x1, + HASH_MODE_HMAC = 0x2, + HASH_MODE_RABIN = 0x4, + HASH_MODE_FHMAC = 0x6, + HASH_MODE_CCM = 0x5, + HASH_MODE_GCM = 0x6, +}; + +enum hash_type { + HASH_TYPE_NONE = 0x0, + HASH_TYPE_FULL = 0x0, + HASH_TYPE_INIT = 0x1, + HASH_TYPE_UPDT = 0x2, + HASH_TYPE_FIN = 0x3, + HASH_TYPE_AES128 = 0x0, + HASH_TYPE_AES192 = 0x1, + HASH_TYPE_AES256 = 0x2 +}; + +enum aead_type { + AES_CCM, + AES_GCM, + AUTHENC, + AEAD_TYPE_LAST +}; + +extern char *hash_alg_name[HASH_ALG_LAST]; +extern char *aead_alg_name[AEAD_TYPE_LAST]; + +struct spu_request_opts { + bool is_inbound; + bool auth_first; + bool is_aead; + bool is_esp; + bool bd_suppress; + bool is_rfc4543; +}; + +struct spu_cipher_parms { + enum spu_cipher_alg alg; + enum spu_cipher_mode mode; + enum spu_cipher_type type; + u8 *key_buf; + u16 key_len; + /* iv_buf and iv_len include salt, if applicable */ + u8 *iv_buf; + u16 iv_len; +}; + +struct spu_hash_parms { + enum hash_alg alg; + enum hash_mode mode; + enum hash_type type; + u8 digestsize; + u8 *key_buf; + u16 key_len; + u16 prebuf_len; + /* length of hash pad. signed, needs to handle roll-overs */ + int pad_len; +}; + +struct spu_aead_parms { + u32 assoc_size; + u16 iv_len; /* length of IV field between assoc data and data */ + u8 aad_pad_len; /* For AES GCM/CCM, length of padding after AAD */ + u8 data_pad_len;/* For AES GCM/CCM, length of padding after data */ + bool return_iv; /* True if SPU should return an IV */ + u32 ret_iv_len; /* Length in bytes of returned IV */ + u32 ret_iv_off; /* Offset into full IV if partial IV returned */ +}; + +/************** SPU sizes ***************/ + +#define SPU_RX_STATUS_LEN 4 + +/* Max length of padding for 4-byte alignment of STATUS field */ +#define SPU_STAT_PAD_MAX 4 + +/* Max length of pad fragment. 4 is for 4-byte alignment of STATUS field */ +#define SPU_PAD_LEN_MAX (SPU_GCM_CCM_ALIGN + MAX_HASH_BLOCK_SIZE + \ + SPU_STAT_PAD_MAX) + +/* GCM and CCM require 16-byte alignment */ +#define SPU_GCM_CCM_ALIGN 16 + +/* Length up SUPDT field in SPU response message for RC4 */ +#define SPU_SUPDT_LEN 260 + +/* SPU status error codes. These used as common error codes across all + * SPU variants. + */ +#define SPU_INVALID_ICV 1 + +/* Indicates no limit to the length of the payload in a SPU message */ +#define SPU_MAX_PAYLOAD_INF 0xFFFFFFFF + +/* Size of XTS tweak ("i" parameter), in bytes */ +#define SPU_XTS_TWEAK_SIZE 16 + +/* CCM B_0 field definitions, common for SPU-M and SPU2 */ +#define CCM_B0_ADATA 0x40 +#define CCM_B0_ADATA_SHIFT 6 +#define CCM_B0_M_PRIME 0x38 +#define CCM_B0_M_PRIME_SHIFT 3 +#define CCM_B0_L_PRIME 0x07 +#define CCM_B0_L_PRIME_SHIFT 0 +#define CCM_ESP_L_VALUE 4 + +/** + * spu_req_incl_icv() - Return true if SPU request message should include the + * ICV as a separate buffer. + * @cipher_mode: the cipher mode being requested + * @is_encrypt: true if encrypting. false if decrypting. + * + * Return: true if ICV to be included as separate buffer + */ +static __always_inline bool spu_req_incl_icv(enum spu_cipher_mode cipher_mode, + bool is_encrypt) +{ + if ((cipher_mode == CIPHER_MODE_GCM) && !is_encrypt) + return true; + if ((cipher_mode == CIPHER_MODE_CCM) && !is_encrypt) + return true; + + return false; +} + +static __always_inline u32 spu_real_db_size(u32 assoc_size, + u32 aead_iv_buf_len, + u32 prebuf_len, + u32 data_size, + u32 aad_pad_len, + u32 gcm_pad_len, + u32 hash_pad_len) +{ + return assoc_size + aead_iv_buf_len + prebuf_len + data_size + + aad_pad_len + gcm_pad_len + hash_pad_len; +} + +/************** SPU Functions Prototypes **************/ + +void spum_dump_msg_hdr(u8 *buf, unsigned int buf_len); + +u32 spum_ns2_ctx_max_payload(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + unsigned int blocksize); +u32 spum_nsp_ctx_max_payload(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + unsigned int blocksize); +u32 spum_payload_length(u8 *spu_hdr); +u16 spum_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash); +u16 spum_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode, + u32 chunksize, u16 hash_block_size); +u32 spum_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode, + unsigned int data_size); +u32 spum_assoc_resp_len(enum spu_cipher_mode cipher_mode, + unsigned int assoc_len, unsigned int iv_len, + bool is_encrypt); +u8 spum_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len); +bool spu_req_incl_icv(enum spu_cipher_mode cipher_mode, bool is_encrypt); +enum hash_type spum_hash_type(u32 src_sent); +u32 spum_digest_size(u32 alg_digest_size, enum hash_alg alg, + enum hash_type htype); + +u32 spum_create_request(u8 *spu_hdr, + struct spu_request_opts *req_opts, + struct spu_cipher_parms *cipher_parms, + struct spu_hash_parms *hash_parms, + struct spu_aead_parms *aead_parms, + unsigned int data_size); + +u16 spum_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms); + +void spum_cipher_req_finish(u8 *spu_hdr, + u16 spu_req_hdr_len, + unsigned int is_inbound, + struct spu_cipher_parms *cipher_parms, + bool update_key, + unsigned int data_size); + +void spum_request_pad(u8 *pad_start, + u32 gcm_padding, + u32 hash_pad_len, + enum hash_alg auth_alg, + enum hash_mode auth_mode, + unsigned int total_sent, u32 status_padding); + +u8 spum_xts_tweak_in_payload(void); +u8 spum_tx_status_len(void); +u8 spum_rx_status_len(void); +int spum_status_process(u8 *statp); + +void spum_ccm_update_iv(unsigned int digestsize, + struct spu_cipher_parms *cipher_parms, + unsigned int assoclen, + unsigned int chunksize, + bool is_encrypt, + bool is_esp); +u32 spum_wordalign_padlen(u32 data_size); +#endif diff --git a/drivers/crypto/bcm/spu2.c b/drivers/crypto/bcm/spu2.c new file mode 100644 index 000000000000..ef04c9748317 --- /dev/null +++ b/drivers/crypto/bcm/spu2.c @@ -0,0 +1,1401 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* + * This file works with the SPU2 version of the SPU. SPU2 has different message + * formats than the previous version of the SPU. All SPU message format + * differences should be hidden in the spux.c,h files. + */ + +#include <linux/kernel.h> +#include <linux/string.h> + +#include "util.h" +#include "spu.h" +#include "spu2.h" + +#define SPU2_TX_STATUS_LEN 0 /* SPU2 has no STATUS in input packet */ + +/* + * Controlled by pkt_stat_cnt field in CRYPTO_SS_SPU0_CORE_SPU2_CONTROL0 + * register. Defaults to 2. + */ +#define SPU2_RX_STATUS_LEN 2 + +enum spu2_proto_sel { + SPU2_PROTO_RESV = 0, + SPU2_MACSEC_SECTAG8_ECB = 1, + SPU2_MACSEC_SECTAG8_SCB = 2, + SPU2_MACSEC_SECTAG16 = 3, + SPU2_MACSEC_SECTAG16_8_XPN = 4, + SPU2_IPSEC = 5, + SPU2_IPSEC_ESN = 6, + SPU2_TLS_CIPHER = 7, + SPU2_TLS_AEAD = 8, + SPU2_DTLS_CIPHER = 9, + SPU2_DTLS_AEAD = 10 +}; + +char *spu2_cipher_type_names[] = { "None", "AES128", "AES192", "AES256", + "DES", "3DES" +}; + +char *spu2_cipher_mode_names[] = { "ECB", "CBC", "CTR", "CFB", "OFB", "XTS", + "CCM", "GCM" +}; + +char *spu2_hash_type_names[] = { "None", "AES128", "AES192", "AES256", + "Reserved", "Reserved", "MD5", "SHA1", "SHA224", "SHA256", "SHA384", + "SHA512", "SHA512/224", "SHA512/256", "SHA3-224", "SHA3-256", + "SHA3-384", "SHA3-512" +}; + +char *spu2_hash_mode_names[] = { "CMAC", "CBC-MAC", "XCBC-MAC", "HMAC", + "Rabin", "CCM", "GCM", "Reserved" +}; + +static char *spu2_ciph_type_name(enum spu2_cipher_type cipher_type) +{ + if (cipher_type >= SPU2_CIPHER_TYPE_LAST) + return "Reserved"; + return spu2_cipher_type_names[cipher_type]; +} + +static char *spu2_ciph_mode_name(enum spu2_cipher_mode cipher_mode) +{ + if (cipher_mode >= SPU2_CIPHER_MODE_LAST) + return "Reserved"; + return spu2_cipher_mode_names[cipher_mode]; +} + +static char *spu2_hash_type_name(enum spu2_hash_type hash_type) +{ + if (hash_type >= SPU2_HASH_TYPE_LAST) + return "Reserved"; + return spu2_hash_type_names[hash_type]; +} + +static char *spu2_hash_mode_name(enum spu2_hash_mode hash_mode) +{ + if (hash_mode >= SPU2_HASH_MODE_LAST) + return "Reserved"; + return spu2_hash_mode_names[hash_mode]; +} + +/* + * Convert from a software cipher mode value to the corresponding value + * for SPU2. + */ +static int spu2_cipher_mode_xlate(enum spu_cipher_mode cipher_mode, + enum spu2_cipher_mode *spu2_mode) +{ + switch (cipher_mode) { + case CIPHER_MODE_ECB: + *spu2_mode = SPU2_CIPHER_MODE_ECB; + break; + case CIPHER_MODE_CBC: + *spu2_mode = SPU2_CIPHER_MODE_CBC; + break; + case CIPHER_MODE_OFB: + *spu2_mode = SPU2_CIPHER_MODE_OFB; + break; + case CIPHER_MODE_CFB: + *spu2_mode = SPU2_CIPHER_MODE_CFB; + break; + case CIPHER_MODE_CTR: + *spu2_mode = SPU2_CIPHER_MODE_CTR; + break; + case CIPHER_MODE_CCM: + *spu2_mode = SPU2_CIPHER_MODE_CCM; + break; + case CIPHER_MODE_GCM: + *spu2_mode = SPU2_CIPHER_MODE_GCM; + break; + case CIPHER_MODE_XTS: + *spu2_mode = SPU2_CIPHER_MODE_XTS; + break; + default: + return -EINVAL; + } + return 0; +} + +/** + * spu2_cipher_xlate() - Convert a cipher {alg/mode/type} triple to a SPU2 + * cipher type and mode. + * @cipher_alg: [in] cipher algorithm value from software enumeration + * @cipher_mode: [in] cipher mode value from software enumeration + * @cipher_type: [in] cipher type value from software enumeration + * @spu2_type: [out] cipher type value used by spu2 hardware + * @spu2_mode: [out] cipher mode value used by spu2 hardware + * + * Return: 0 if successful + */ +static int spu2_cipher_xlate(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + enum spu_cipher_type cipher_type, + enum spu2_cipher_type *spu2_type, + enum spu2_cipher_mode *spu2_mode) +{ + int err; + + err = spu2_cipher_mode_xlate(cipher_mode, spu2_mode); + if (err) { + flow_log("Invalid cipher mode %d\n", cipher_mode); + return err; + } + + switch (cipher_alg) { + case CIPHER_ALG_NONE: + *spu2_type = SPU2_CIPHER_TYPE_NONE; + break; + case CIPHER_ALG_RC4: + /* SPU2 does not support RC4 */ + err = -EINVAL; + *spu2_type = SPU2_CIPHER_TYPE_NONE; + break; + case CIPHER_ALG_DES: + *spu2_type = SPU2_CIPHER_TYPE_DES; + break; + case CIPHER_ALG_3DES: + *spu2_type = SPU2_CIPHER_TYPE_3DES; + break; + case CIPHER_ALG_AES: + switch (cipher_type) { + case CIPHER_TYPE_AES128: + *spu2_type = SPU2_CIPHER_TYPE_AES128; + break; + case CIPHER_TYPE_AES192: + *spu2_type = SPU2_CIPHER_TYPE_AES192; + break; + case CIPHER_TYPE_AES256: + *spu2_type = SPU2_CIPHER_TYPE_AES256; + break; + default: + err = -EINVAL; + } + break; + case CIPHER_ALG_LAST: + default: + err = -EINVAL; + break; + } + + if (err) + flow_log("Invalid cipher alg %d or type %d\n", + cipher_alg, cipher_type); + return err; +} + +/* + * Convert from a software hash mode value to the corresponding value + * for SPU2. Note that HASH_MODE_NONE and HASH_MODE_XCBC have the same value. + */ +static int spu2_hash_mode_xlate(enum hash_mode hash_mode, + enum spu2_hash_mode *spu2_mode) +{ + switch (hash_mode) { + case HASH_MODE_XCBC: + *spu2_mode = SPU2_HASH_MODE_XCBC_MAC; + break; + case HASH_MODE_CMAC: + *spu2_mode = SPU2_HASH_MODE_CMAC; + break; + case HASH_MODE_HMAC: + *spu2_mode = SPU2_HASH_MODE_HMAC; + break; + case HASH_MODE_CCM: + *spu2_mode = SPU2_HASH_MODE_CCM; + break; + case HASH_MODE_GCM: + *spu2_mode = SPU2_HASH_MODE_GCM; + break; + default: + return -EINVAL; + } + return 0; +} + +/** + * spu2_hash_xlate() - Convert a hash {alg/mode/type} triple to a SPU2 hash type + * and mode. + * @hash_alg: [in] hash algorithm value from software enumeration + * @hash_mode: [in] hash mode value from software enumeration + * @hash_type: [in] hash type value from software enumeration + * @ciph_type: [in] cipher type value from software enumeration + * @spu2_type: [out] hash type value used by SPU2 hardware + * @spu2_mode: [out] hash mode value used by SPU2 hardware + * + * Return: 0 if successful + */ +static int +spu2_hash_xlate(enum hash_alg hash_alg, enum hash_mode hash_mode, + enum hash_type hash_type, enum spu_cipher_type ciph_type, + enum spu2_hash_type *spu2_type, enum spu2_hash_mode *spu2_mode) +{ + int err; + + err = spu2_hash_mode_xlate(hash_mode, spu2_mode); + if (err) { + flow_log("Invalid hash mode %d\n", hash_mode); + return err; + } + + switch (hash_alg) { + case HASH_ALG_NONE: + *spu2_type = SPU2_HASH_TYPE_NONE; + break; + case HASH_ALG_MD5: + *spu2_type = SPU2_HASH_TYPE_MD5; + break; + case HASH_ALG_SHA1: + *spu2_type = SPU2_HASH_TYPE_SHA1; + break; + case HASH_ALG_SHA224: + *spu2_type = SPU2_HASH_TYPE_SHA224; + break; + case HASH_ALG_SHA256: + *spu2_type = SPU2_HASH_TYPE_SHA256; + break; + case HASH_ALG_SHA384: + *spu2_type = SPU2_HASH_TYPE_SHA384; + break; + case HASH_ALG_SHA512: + *spu2_type = SPU2_HASH_TYPE_SHA512; + break; + case HASH_ALG_AES: + switch (ciph_type) { + case CIPHER_TYPE_AES128: + *spu2_type = SPU2_HASH_TYPE_AES128; + break; + case CIPHER_TYPE_AES192: + *spu2_type = SPU2_HASH_TYPE_AES192; + break; + case CIPHER_TYPE_AES256: + *spu2_type = SPU2_HASH_TYPE_AES256; + break; + default: + err = -EINVAL; + } + break; + case HASH_ALG_SHA3_224: + *spu2_type = SPU2_HASH_TYPE_SHA3_224; + break; + case HASH_ALG_SHA3_256: + *spu2_type = SPU2_HASH_TYPE_SHA3_256; + break; + case HASH_ALG_SHA3_384: + *spu2_type = SPU2_HASH_TYPE_SHA3_384; + break; + case HASH_ALG_SHA3_512: + *spu2_type = SPU2_HASH_TYPE_SHA3_512; + case HASH_ALG_LAST: + default: + err = -EINVAL; + break; + } + + if (err) + flow_log("Invalid hash alg %d or type %d\n", + hash_alg, hash_type); + return err; +} + +/* Dump FMD ctrl0. The ctrl0 input is in host byte order */ +static void spu2_dump_fmd_ctrl0(u64 ctrl0) +{ + enum spu2_cipher_type ciph_type; + enum spu2_cipher_mode ciph_mode; + enum spu2_hash_type hash_type; + enum spu2_hash_mode hash_mode; + char *ciph_name; + char *ciph_mode_name; + char *hash_name; + char *hash_mode_name; + u8 cfb; + u8 proto; + + packet_log(" FMD CTRL0 %#16llx\n", ctrl0); + if (ctrl0 & SPU2_CIPH_ENCRYPT_EN) + packet_log(" encrypt\n"); + else + packet_log(" decrypt\n"); + + ciph_type = (ctrl0 & SPU2_CIPH_TYPE) >> SPU2_CIPH_TYPE_SHIFT; + ciph_name = spu2_ciph_type_name(ciph_type); + packet_log(" Cipher type: %s\n", ciph_name); + + if (ciph_type != SPU2_CIPHER_TYPE_NONE) { + ciph_mode = (ctrl0 & SPU2_CIPH_MODE) >> SPU2_CIPH_MODE_SHIFT; + ciph_mode_name = spu2_ciph_mode_name(ciph_mode); + packet_log(" Cipher mode: %s\n", ciph_mode_name); + } + + cfb = (ctrl0 & SPU2_CFB_MASK) >> SPU2_CFB_MASK_SHIFT; + packet_log(" CFB %#x\n", cfb); + + proto = (ctrl0 & SPU2_PROTO_SEL) >> SPU2_PROTO_SEL_SHIFT; + packet_log(" protocol %#x\n", proto); + + if (ctrl0 & SPU2_HASH_FIRST) + packet_log(" hash first\n"); + else + packet_log(" cipher first\n"); + + if (ctrl0 & SPU2_CHK_TAG) + packet_log(" check tag\n"); + + hash_type = (ctrl0 & SPU2_HASH_TYPE) >> SPU2_HASH_TYPE_SHIFT; + hash_name = spu2_hash_type_name(hash_type); + packet_log(" Hash type: %s\n", hash_name); + + if (hash_type != SPU2_HASH_TYPE_NONE) { + hash_mode = (ctrl0 & SPU2_HASH_MODE) >> SPU2_HASH_MODE_SHIFT; + hash_mode_name = spu2_hash_mode_name(hash_mode); + packet_log(" Hash mode: %s\n", hash_mode_name); + } + + if (ctrl0 & SPU2_CIPH_PAD_EN) { + packet_log(" Cipher pad: %#2llx\n", + (ctrl0 & SPU2_CIPH_PAD) >> SPU2_CIPH_PAD_SHIFT); + } +} + +/* Dump FMD ctrl1. The ctrl1 input is in host byte order */ +static void spu2_dump_fmd_ctrl1(u64 ctrl1) +{ + u8 hash_key_len; + u8 ciph_key_len; + u8 ret_iv_len; + u8 iv_offset; + u8 iv_len; + u8 hash_tag_len; + u8 ret_md; + + packet_log(" FMD CTRL1 %#16llx\n", ctrl1); + if (ctrl1 & SPU2_TAG_LOC) + packet_log(" Tag after payload\n"); + + packet_log(" Msg includes "); + if (ctrl1 & SPU2_HAS_FR_DATA) + packet_log("FD "); + if (ctrl1 & SPU2_HAS_AAD1) + packet_log("AAD1 "); + if (ctrl1 & SPU2_HAS_NAAD) + packet_log("NAAD "); + if (ctrl1 & SPU2_HAS_AAD2) + packet_log("AAD2 "); + if (ctrl1 & SPU2_HAS_ESN) + packet_log("ESN "); + packet_log("\n"); + + hash_key_len = (ctrl1 & SPU2_HASH_KEY_LEN) >> SPU2_HASH_KEY_LEN_SHIFT; + packet_log(" Hash key len %u\n", hash_key_len); + + ciph_key_len = (ctrl1 & SPU2_CIPH_KEY_LEN) >> SPU2_CIPH_KEY_LEN_SHIFT; + packet_log(" Cipher key len %u\n", ciph_key_len); + + if (ctrl1 & SPU2_GENIV) + packet_log(" Generate IV\n"); + + if (ctrl1 & SPU2_HASH_IV) + packet_log(" IV included in hash\n"); + + if (ctrl1 & SPU2_RET_IV) + packet_log(" Return IV in output before payload\n"); + + ret_iv_len = (ctrl1 & SPU2_RET_IV_LEN) >> SPU2_RET_IV_LEN_SHIFT; + packet_log(" Length of returned IV %u bytes\n", + ret_iv_len ? ret_iv_len : 16); + + iv_offset = (ctrl1 & SPU2_IV_OFFSET) >> SPU2_IV_OFFSET_SHIFT; + packet_log(" IV offset %u\n", iv_offset); + + iv_len = (ctrl1 & SPU2_IV_LEN) >> SPU2_IV_LEN_SHIFT; + packet_log(" Input IV len %u bytes\n", iv_len); + + hash_tag_len = (ctrl1 & SPU2_HASH_TAG_LEN) >> SPU2_HASH_TAG_LEN_SHIFT; + packet_log(" Hash tag length %u bytes\n", hash_tag_len); + + packet_log(" Return "); + ret_md = (ctrl1 & SPU2_RETURN_MD) >> SPU2_RETURN_MD_SHIFT; + if (ret_md) + packet_log("FMD "); + if (ret_md == SPU2_RET_FMD_OMD) + packet_log("OMD "); + else if (ret_md == SPU2_RET_FMD_OMD_IV) + packet_log("OMD IV "); + if (ctrl1 & SPU2_RETURN_FD) + packet_log("FD "); + if (ctrl1 & SPU2_RETURN_AAD1) + packet_log("AAD1 "); + if (ctrl1 & SPU2_RETURN_NAAD) + packet_log("NAAD "); + if (ctrl1 & SPU2_RETURN_AAD2) + packet_log("AAD2 "); + if (ctrl1 & SPU2_RETURN_PAY) + packet_log("Payload"); + packet_log("\n"); +} + +/* Dump FMD ctrl2. The ctrl2 input is in host byte order */ +static void spu2_dump_fmd_ctrl2(u64 ctrl2) +{ + packet_log(" FMD CTRL2 %#16llx\n", ctrl2); + + packet_log(" AAD1 offset %llu length %llu bytes\n", + ctrl2 & SPU2_AAD1_OFFSET, + (ctrl2 & SPU2_AAD1_LEN) >> SPU2_AAD1_LEN_SHIFT); + packet_log(" AAD2 offset %llu\n", + (ctrl2 & SPU2_AAD2_OFFSET) >> SPU2_AAD2_OFFSET_SHIFT); + packet_log(" Payload offset %llu\n", + (ctrl2 & SPU2_PL_OFFSET) >> SPU2_PL_OFFSET_SHIFT); +} + +/* Dump FMD ctrl3. The ctrl3 input is in host byte order */ +static void spu2_dump_fmd_ctrl3(u64 ctrl3) +{ + packet_log(" FMD CTRL3 %#16llx\n", ctrl3); + + packet_log(" Payload length %llu bytes\n", ctrl3 & SPU2_PL_LEN); + packet_log(" TLS length %llu bytes\n", + (ctrl3 & SPU2_TLS_LEN) >> SPU2_TLS_LEN_SHIFT); +} + +static void spu2_dump_fmd(struct SPU2_FMD *fmd) +{ + spu2_dump_fmd_ctrl0(le64_to_cpu(fmd->ctrl0)); + spu2_dump_fmd_ctrl1(le64_to_cpu(fmd->ctrl1)); + spu2_dump_fmd_ctrl2(le64_to_cpu(fmd->ctrl2)); + spu2_dump_fmd_ctrl3(le64_to_cpu(fmd->ctrl3)); +} + +static void spu2_dump_omd(u8 *omd, u16 hash_key_len, u16 ciph_key_len, + u16 hash_iv_len, u16 ciph_iv_len) +{ + u8 *ptr = omd; + + packet_log(" OMD:\n"); + + if (hash_key_len) { + packet_log(" Hash Key Length %u bytes\n", hash_key_len); + packet_dump(" KEY: ", ptr, hash_key_len); + ptr += hash_key_len; + } + + if (ciph_key_len) { + packet_log(" Cipher Key Length %u bytes\n", ciph_key_len); + packet_dump(" KEY: ", ptr, ciph_key_len); + ptr += ciph_key_len; + } + + if (hash_iv_len) { + packet_log(" Hash IV Length %u bytes\n", hash_iv_len); + packet_dump(" hash IV: ", ptr, hash_iv_len); + ptr += ciph_key_len; + } + + if (ciph_iv_len) { + packet_log(" Cipher IV Length %u bytes\n", ciph_iv_len); + packet_dump(" cipher IV: ", ptr, ciph_iv_len); + } +} + +/* Dump a SPU2 header for debug */ +void spu2_dump_msg_hdr(u8 *buf, unsigned int buf_len) +{ + struct SPU2_FMD *fmd = (struct SPU2_FMD *)buf; + u8 *omd; + u64 ctrl1; + u16 hash_key_len; + u16 ciph_key_len; + u16 hash_iv_len; + u16 ciph_iv_len; + u16 omd_len; + + packet_log("\n"); + packet_log("SPU2 message header %p len: %u\n", buf, buf_len); + + spu2_dump_fmd(fmd); + omd = (u8 *)(fmd + 1); + + ctrl1 = le64_to_cpu(fmd->ctrl1); + hash_key_len = (ctrl1 & SPU2_HASH_KEY_LEN) >> SPU2_HASH_KEY_LEN_SHIFT; + ciph_key_len = (ctrl1 & SPU2_CIPH_KEY_LEN) >> SPU2_CIPH_KEY_LEN_SHIFT; + hash_iv_len = 0; + ciph_iv_len = (ctrl1 & SPU2_IV_LEN) >> SPU2_IV_LEN_SHIFT; + spu2_dump_omd(omd, hash_key_len, ciph_key_len, hash_iv_len, + ciph_iv_len); + + /* Double check sanity */ + omd_len = hash_key_len + ciph_key_len + hash_iv_len + ciph_iv_len; + if (FMD_SIZE + omd_len != buf_len) { + packet_log + (" Packet parsed incorrectly. buf_len %u, sum of MD %zu\n", + buf_len, FMD_SIZE + omd_len); + } + packet_log("\n"); +} + +/** + * spu2_fmd_init() - At setkey time, initialize the fixed meta data for + * subsequent ablkcipher requests for this context. + * @spu2_cipher_type: Cipher algorithm + * @spu2_mode: Cipher mode + * @cipher_key_len: Length of cipher key, in bytes + * @cipher_iv_len: Length of cipher initialization vector, in bytes + * + * Return: 0 (success) + */ +static int spu2_fmd_init(struct SPU2_FMD *fmd, + enum spu2_cipher_type spu2_type, + enum spu2_cipher_mode spu2_mode, + u32 cipher_key_len, u32 cipher_iv_len) +{ + u64 ctrl0; + u64 ctrl1; + u64 ctrl2; + u64 ctrl3; + u32 aad1_offset; + u32 aad2_offset; + u16 aad1_len = 0; + u64 payload_offset; + + ctrl0 = (spu2_type << SPU2_CIPH_TYPE_SHIFT) | + (spu2_mode << SPU2_CIPH_MODE_SHIFT); + + ctrl1 = (cipher_key_len << SPU2_CIPH_KEY_LEN_SHIFT) | + ((u64)cipher_iv_len << SPU2_IV_LEN_SHIFT) | + ((u64)SPU2_RET_FMD_ONLY << SPU2_RETURN_MD_SHIFT) | SPU2_RETURN_PAY; + + /* + * AAD1 offset is from start of FD. FD length is always 0 for this + * driver. So AAD1_offset is always 0. + */ + aad1_offset = 0; + aad2_offset = aad1_offset; + payload_offset = 0; + ctrl2 = aad1_offset | + (aad1_len << SPU2_AAD1_LEN_SHIFT) | + (aad2_offset << SPU2_AAD2_OFFSET_SHIFT) | + (payload_offset << SPU2_PL_OFFSET_SHIFT); + + ctrl3 = 0; + + fmd->ctrl0 = cpu_to_le64(ctrl0); + fmd->ctrl1 = cpu_to_le64(ctrl1); + fmd->ctrl2 = cpu_to_le64(ctrl2); + fmd->ctrl3 = cpu_to_le64(ctrl3); + + return 0; +} + +/** + * spu2_fmd_ctrl0_write() - Write ctrl0 field in fixed metadata (FMD) field of + * SPU request packet. + * @fmd: Start of FMD field to be written + * @is_inbound: true if decrypting. false if encrypting. + * @authFirst: true if alg authenticates before encrypting + * @protocol: protocol selector + * @cipher_type: cipher algorithm + * @cipher_mode: cipher mode + * @auth_type: authentication type + * @auth_mode: authentication mode + */ +static void spu2_fmd_ctrl0_write(struct SPU2_FMD *fmd, + bool is_inbound, bool auth_first, + enum spu2_proto_sel protocol, + enum spu2_cipher_type cipher_type, + enum spu2_cipher_mode cipher_mode, + enum spu2_hash_type auth_type, + enum spu2_hash_mode auth_mode) +{ + u64 ctrl0 = 0; + + if ((cipher_type != SPU2_CIPHER_TYPE_NONE) && !is_inbound) + ctrl0 |= SPU2_CIPH_ENCRYPT_EN; + + ctrl0 |= ((u64)cipher_type << SPU2_CIPH_TYPE_SHIFT) | + ((u64)cipher_mode << SPU2_CIPH_MODE_SHIFT); + + if (protocol) + ctrl0 |= (u64)protocol << SPU2_PROTO_SEL_SHIFT; + + if (auth_first) + ctrl0 |= SPU2_HASH_FIRST; + + if (is_inbound && (auth_type != SPU2_HASH_TYPE_NONE)) + ctrl0 |= SPU2_CHK_TAG; + + ctrl0 |= (((u64)auth_type << SPU2_HASH_TYPE_SHIFT) | + ((u64)auth_mode << SPU2_HASH_MODE_SHIFT)); + + fmd->ctrl0 = cpu_to_le64(ctrl0); +} + +/** + * spu2_fmd_ctrl1_write() - Write ctrl1 field in fixed metadata (FMD) field of + * SPU request packet. + * @fmd: Start of FMD field to be written + * @assoc_size: Length of additional associated data, in bytes + * @auth_key_len: Length of authentication key, in bytes + * @cipher_key_len: Length of cipher key, in bytes + * @gen_iv: If true, hw generates IV and returns in response + * @hash_iv: IV participates in hash. Used for IPSEC and TLS. + * @return_iv: Return IV in output packet before payload + * @ret_iv_len: Length of IV returned from SPU, in bytes + * @ret_iv_offset: Offset into full IV of start of returned IV + * @cipher_iv_len: Length of input cipher IV, in bytes + * @digest_size: Length of digest (aka, hash tag or ICV), in bytes + * @return_payload: Return payload in SPU response + * @return_md : return metadata in SPU response + * + * Packet can have AAD2 w/o AAD1. For algorithms currently supported, + * associated data goes in AAD2. + */ +static void spu2_fmd_ctrl1_write(struct SPU2_FMD *fmd, bool is_inbound, + u64 assoc_size, + u64 auth_key_len, u64 cipher_key_len, + bool gen_iv, bool hash_iv, bool return_iv, + u64 ret_iv_len, u64 ret_iv_offset, + u64 cipher_iv_len, u64 digest_size, + bool return_payload, bool return_md) +{ + u64 ctrl1 = 0; + + if (is_inbound && digest_size) + ctrl1 |= SPU2_TAG_LOC; + + if (assoc_size) { + ctrl1 |= SPU2_HAS_AAD2; + ctrl1 |= SPU2_RETURN_AAD2; /* need aad2 for gcm aes esp */ + } + + if (auth_key_len) + ctrl1 |= ((auth_key_len << SPU2_HASH_KEY_LEN_SHIFT) & + SPU2_HASH_KEY_LEN); + + if (cipher_key_len) + ctrl1 |= ((cipher_key_len << SPU2_CIPH_KEY_LEN_SHIFT) & + SPU2_CIPH_KEY_LEN); + + if (gen_iv) + ctrl1 |= SPU2_GENIV; + + if (hash_iv) + ctrl1 |= SPU2_HASH_IV; + + if (return_iv) { + ctrl1 |= SPU2_RET_IV; + ctrl1 |= ret_iv_len << SPU2_RET_IV_LEN_SHIFT; + ctrl1 |= ret_iv_offset << SPU2_IV_OFFSET_SHIFT; + } + + ctrl1 |= ((cipher_iv_len << SPU2_IV_LEN_SHIFT) & SPU2_IV_LEN); + + if (digest_size) + ctrl1 |= ((digest_size << SPU2_HASH_TAG_LEN_SHIFT) & + SPU2_HASH_TAG_LEN); + + /* Let's ask for the output pkt to include FMD, but don't need to + * get keys and IVs back in OMD. + */ + if (return_md) + ctrl1 |= ((u64)SPU2_RET_FMD_ONLY << SPU2_RETURN_MD_SHIFT); + else + ctrl1 |= ((u64)SPU2_RET_NO_MD << SPU2_RETURN_MD_SHIFT); + + /* Crypto API does not get assoc data back. So no need for AAD2. */ + + if (return_payload) + ctrl1 |= SPU2_RETURN_PAY; + + fmd->ctrl1 = cpu_to_le64(ctrl1); +} + +/** + * spu2_fmd_ctrl2_write() - Set the ctrl2 field in the fixed metadata field of + * SPU2 header. + * @fmd: Start of FMD field to be written + * @cipher_offset: Number of bytes from Start of Packet (end of FD field) where + * data to be encrypted or decrypted begins + * @auth_key_len: Length of authentication key, in bytes + * @auth_iv_len: Length of authentication initialization vector, in bytes + * @cipher_key_len: Length of cipher key, in bytes + * @cipher_iv_len: Length of cipher IV, in bytes + */ +static void spu2_fmd_ctrl2_write(struct SPU2_FMD *fmd, u64 cipher_offset, + u64 auth_key_len, u64 auth_iv_len, + u64 cipher_key_len, u64 cipher_iv_len) +{ + u64 ctrl2; + u64 aad1_offset; + u64 aad2_offset; + u16 aad1_len = 0; + u64 payload_offset; + + /* AAD1 offset is from start of FD. FD length always 0. */ + aad1_offset = 0; + + aad2_offset = aad1_offset; + payload_offset = cipher_offset; + ctrl2 = aad1_offset | + (aad1_len << SPU2_AAD1_LEN_SHIFT) | + (aad2_offset << SPU2_AAD2_OFFSET_SHIFT) | + (payload_offset << SPU2_PL_OFFSET_SHIFT); + + fmd->ctrl2 = cpu_to_le64(ctrl2); +} + +/** + * spu2_fmd_ctrl3_write() - Set the ctrl3 field in FMD + * @fmd: Fixed meta data. First field in SPU2 msg header. + * @payload_len: Length of payload, in bytes + */ +static void spu2_fmd_ctrl3_write(struct SPU2_FMD *fmd, u64 payload_len) +{ + u64 ctrl3; + + ctrl3 = payload_len & SPU2_PL_LEN; + + fmd->ctrl3 = cpu_to_le64(ctrl3); +} + +/** + * spu2_ctx_max_payload() - Determine the maximum length of the payload for a + * SPU message for a given cipher and hash alg context. + * @cipher_alg: The cipher algorithm + * @cipher_mode: The cipher mode + * @blocksize: The size of a block of data for this algo + * + * For SPU2, the hardware generally ignores the PayloadLen field in ctrl3 of + * FMD and just keeps computing until it receives a DMA descriptor with the EOF + * flag set. So we consider the max payload to be infinite. AES CCM is an + * exception. + * + * Return: Max payload length in bytes + */ +u32 spu2_ctx_max_payload(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + unsigned int blocksize) +{ + if ((cipher_alg == CIPHER_ALG_AES) && + (cipher_mode == CIPHER_MODE_CCM)) { + u32 excess = SPU2_MAX_PAYLOAD % blocksize; + + return SPU2_MAX_PAYLOAD - excess; + } else { + return SPU_MAX_PAYLOAD_INF; + } +} + +/** + * spu_payload_length() - Given a SPU2 message header, extract the payload + * length. + * @spu_hdr: Start of SPU message header (FMD) + * + * Return: payload length, in bytes + */ +u32 spu2_payload_length(u8 *spu_hdr) +{ + struct SPU2_FMD *fmd = (struct SPU2_FMD *)spu_hdr; + u32 pl_len; + u64 ctrl3; + + ctrl3 = le64_to_cpu(fmd->ctrl3); + pl_len = ctrl3 & SPU2_PL_LEN; + + return pl_len; +} + +/** + * spu_response_hdr_len() - Determine the expected length of a SPU response + * header. + * @auth_key_len: Length of authentication key, in bytes + * @enc_key_len: Length of encryption key, in bytes + * + * For SPU2, includes just FMD. OMD is never requested. + * + * Return: Length of FMD, in bytes + */ +u16 spu2_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash) +{ + return FMD_SIZE; +} + +/** + * spu_hash_pad_len() - Calculate the length of hash padding required to extend + * data to a full block size. + * @hash_alg: hash algorithm + * @hash_mode: hash mode + * @chunksize: length of data, in bytes + * @hash_block_size: size of a hash block, in bytes + * + * SPU2 hardware does all hash padding + * + * Return: length of hash pad in bytes + */ +u16 spu2_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode, + u32 chunksize, u16 hash_block_size) +{ + return 0; +} + +/** + * spu2_gcm_ccm_padlen() - Determine the length of GCM/CCM padding for either + * the AAD field or the data. + * + * Return: 0. Unlike SPU-M, SPU2 hardware does any GCM/CCM padding required. + */ +u32 spu2_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode, + unsigned int data_size) +{ + return 0; +} + +/** + * spu_assoc_resp_len() - Determine the size of the AAD2 buffer needed to catch + * associated data in a SPU2 output packet. + * @cipher_mode: cipher mode + * @assoc_len: length of additional associated data, in bytes + * @iv_len: length of initialization vector, in bytes + * @is_encrypt: true if encrypting. false if decrypt. + * + * Return: Length of buffer to catch associated data in response + */ +u32 spu2_assoc_resp_len(enum spu_cipher_mode cipher_mode, + unsigned int assoc_len, unsigned int iv_len, + bool is_encrypt) +{ + u32 resp_len = assoc_len; + + if (is_encrypt) + /* gcm aes esp has to write 8-byte IV in response */ + resp_len += iv_len; + return resp_len; +} + +/* + * spu_aead_ivlen() - Calculate the length of the AEAD IV to be included + * in a SPU request after the AAD and before the payload. + * @cipher_mode: cipher mode + * @iv_ctr_len: initialization vector length in bytes + * + * For SPU2, AEAD IV is included in OMD and does not need to be repeated + * prior to the payload. + * + * Return: Length of AEAD IV in bytes + */ +u8 spu2_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len) +{ + return 0; +} + +/** + * spu2_hash_type() - Determine the type of hash operation. + * @src_sent: The number of bytes in the current request that have already + * been sent to the SPU to be hashed. + * + * SPU2 always does a FULL hash operation + */ +enum hash_type spu2_hash_type(u32 src_sent) +{ + return HASH_TYPE_FULL; +} + +/** + * spu2_digest_size() - Determine the size of a hash digest to expect the SPU to + * return. + * alg_digest_size: Number of bytes in the final digest for the given algo + * alg: The hash algorithm + * htype: Type of hash operation (init, update, full, etc) + * + */ +u32 spu2_digest_size(u32 alg_digest_size, enum hash_alg alg, + enum hash_type htype) +{ + return alg_digest_size; +} + +/** + * spu_create_request() - Build a SPU2 request message header, includint FMD and + * OMD. + * @spu_hdr: Start of buffer where SPU request header is to be written + * @req_opts: SPU request message options + * @cipher_parms: Parameters related to cipher algorithm + * @hash_parms: Parameters related to hash algorithm + * @aead_parms: Parameters related to AEAD operation + * @data_size: Length of data to be encrypted or authenticated. If AEAD, does + * not include length of AAD. + * + * Construct the message starting at spu_hdr. Caller should allocate this buffer + * in DMA-able memory at least SPU_HEADER_ALLOC_LEN bytes long. + * + * Return: the length of the SPU header in bytes. 0 if an error occurs. + */ +u32 spu2_create_request(u8 *spu_hdr, + struct spu_request_opts *req_opts, + struct spu_cipher_parms *cipher_parms, + struct spu_hash_parms *hash_parms, + struct spu_aead_parms *aead_parms, + unsigned int data_size) +{ + struct SPU2_FMD *fmd; + u8 *ptr; + unsigned int buf_len; + int err; + enum spu2_cipher_type spu2_ciph_type = SPU2_CIPHER_TYPE_NONE; + enum spu2_cipher_mode spu2_ciph_mode; + enum spu2_hash_type spu2_auth_type = SPU2_HASH_TYPE_NONE; + enum spu2_hash_mode spu2_auth_mode; + bool return_md = true; + enum spu2_proto_sel proto = SPU2_PROTO_RESV; + + /* size of the payload */ + unsigned int payload_len = + hash_parms->prebuf_len + data_size + hash_parms->pad_len - + ((req_opts->is_aead && req_opts->is_inbound) ? + hash_parms->digestsize : 0); + + /* offset of prebuf or data from start of AAD2 */ + unsigned int cipher_offset = aead_parms->assoc_size + + aead_parms->aad_pad_len + aead_parms->iv_len; + +#ifdef DEBUG + /* total size of the data following OMD (without STAT word padding) */ + unsigned int real_db_size = spu_real_db_size(aead_parms->assoc_size, + aead_parms->iv_len, + hash_parms->prebuf_len, + data_size, + aead_parms->aad_pad_len, + aead_parms->data_pad_len, + hash_parms->pad_len); +#endif + unsigned int assoc_size = aead_parms->assoc_size; + + if (req_opts->is_aead && + (cipher_parms->alg == CIPHER_ALG_AES) && + (cipher_parms->mode == CIPHER_MODE_GCM)) + /* + * On SPU 2, aes gcm cipher first on encrypt, auth first on + * decrypt + */ + req_opts->auth_first = req_opts->is_inbound; + + /* and do opposite for ccm (auth 1st on encrypt) */ + if (req_opts->is_aead && + (cipher_parms->alg == CIPHER_ALG_AES) && + (cipher_parms->mode == CIPHER_MODE_CCM)) + req_opts->auth_first = !req_opts->is_inbound; + + flow_log("%s()\n", __func__); + flow_log(" in:%u authFirst:%u\n", + req_opts->is_inbound, req_opts->auth_first); + flow_log(" cipher alg:%u mode:%u type %u\n", cipher_parms->alg, + cipher_parms->mode, cipher_parms->type); + flow_log(" is_esp: %s\n", req_opts->is_esp ? "yes" : "no"); + flow_log(" key: %d\n", cipher_parms->key_len); + flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len); + flow_log(" iv: %d\n", cipher_parms->iv_len); + flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len); + flow_log(" auth alg:%u mode:%u type %u\n", + hash_parms->alg, hash_parms->mode, hash_parms->type); + flow_log(" digestsize: %u\n", hash_parms->digestsize); + flow_log(" authkey: %d\n", hash_parms->key_len); + flow_dump(" authkey: ", hash_parms->key_buf, hash_parms->key_len); + flow_log(" assoc_size:%u\n", assoc_size); + flow_log(" prebuf_len:%u\n", hash_parms->prebuf_len); + flow_log(" data_size:%u\n", data_size); + flow_log(" hash_pad_len:%u\n", hash_parms->pad_len); + flow_log(" real_db_size:%u\n", real_db_size); + flow_log(" cipher_offset:%u payload_len:%u\n", + cipher_offset, payload_len); + flow_log(" aead_iv: %u\n", aead_parms->iv_len); + + /* Convert to spu2 values for cipher alg, hash alg */ + err = spu2_cipher_xlate(cipher_parms->alg, cipher_parms->mode, + cipher_parms->type, + &spu2_ciph_type, &spu2_ciph_mode); + + /* If we are doing GCM hashing only - either via rfc4543 transform + * or because we happen to do GCM with AAD only and no payload - we + * need to configure hardware to use hash key rather than cipher key + * and put data into payload. This is because unlike SPU-M, running + * GCM cipher with 0 size payload is not permitted. + */ + if ((req_opts->is_rfc4543) || + ((spu2_ciph_mode == SPU2_CIPHER_MODE_GCM) && + (payload_len == 0))) { + /* Use hashing (only) and set up hash key */ + spu2_ciph_type = SPU2_CIPHER_TYPE_NONE; + hash_parms->key_len = cipher_parms->key_len; + memcpy(hash_parms->key_buf, cipher_parms->key_buf, + cipher_parms->key_len); + cipher_parms->key_len = 0; + + if (req_opts->is_rfc4543) + payload_len += assoc_size; + else + payload_len = assoc_size; + cipher_offset = 0; + assoc_size = 0; + } + + if (err) + return 0; + + flow_log("spu2 cipher type %s, cipher mode %s\n", + spu2_ciph_type_name(spu2_ciph_type), + spu2_ciph_mode_name(spu2_ciph_mode)); + + err = spu2_hash_xlate(hash_parms->alg, hash_parms->mode, + hash_parms->type, + cipher_parms->type, + &spu2_auth_type, &spu2_auth_mode); + if (err) + return 0; + + flow_log("spu2 hash type %s, hash mode %s\n", + spu2_hash_type_name(spu2_auth_type), + spu2_hash_mode_name(spu2_auth_mode)); + + fmd = (struct SPU2_FMD *)spu_hdr; + + spu2_fmd_ctrl0_write(fmd, req_opts->is_inbound, req_opts->auth_first, + proto, spu2_ciph_type, spu2_ciph_mode, + spu2_auth_type, spu2_auth_mode); + + spu2_fmd_ctrl1_write(fmd, req_opts->is_inbound, assoc_size, + hash_parms->key_len, cipher_parms->key_len, + false, false, + aead_parms->return_iv, aead_parms->ret_iv_len, + aead_parms->ret_iv_off, + cipher_parms->iv_len, hash_parms->digestsize, + !req_opts->bd_suppress, return_md); + + spu2_fmd_ctrl2_write(fmd, cipher_offset, hash_parms->key_len, 0, + cipher_parms->key_len, cipher_parms->iv_len); + + spu2_fmd_ctrl3_write(fmd, payload_len); + + ptr = (u8 *)(fmd + 1); + buf_len = sizeof(struct SPU2_FMD); + + /* Write OMD */ + if (hash_parms->key_len) { + memcpy(ptr, hash_parms->key_buf, hash_parms->key_len); + ptr += hash_parms->key_len; + buf_len += hash_parms->key_len; + } + if (cipher_parms->key_len) { + memcpy(ptr, cipher_parms->key_buf, cipher_parms->key_len); + ptr += cipher_parms->key_len; + buf_len += cipher_parms->key_len; + } + if (cipher_parms->iv_len) { + memcpy(ptr, cipher_parms->iv_buf, cipher_parms->iv_len); + ptr += cipher_parms->iv_len; + buf_len += cipher_parms->iv_len; + } + + packet_dump(" SPU request header: ", spu_hdr, buf_len); + + return buf_len; +} + +/** + * spu_cipher_req_init() - Build an ablkcipher SPU2 request message header, + * including FMD and OMD. + * @spu_hdr: Location of start of SPU request (FMD field) + * @cipher_parms: Parameters describing cipher request + * + * Called at setkey time to initialize a msg header that can be reused for all + * subsequent ablkcipher requests. Construct the message starting at spu_hdr. + * Caller should allocate this buffer in DMA-able memory at least + * SPU_HEADER_ALLOC_LEN bytes long. + * + * Return: the total length of the SPU header (FMD and OMD) in bytes. 0 if an + * error occurs. + */ +u16 spu2_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms) +{ + struct SPU2_FMD *fmd; + u8 *omd; + enum spu2_cipher_type spu2_type = SPU2_CIPHER_TYPE_NONE; + enum spu2_cipher_mode spu2_mode; + int err; + + flow_log("%s()\n", __func__); + flow_log(" cipher alg:%u mode:%u type %u\n", cipher_parms->alg, + cipher_parms->mode, cipher_parms->type); + flow_log(" cipher_iv_len: %u\n", cipher_parms->iv_len); + flow_log(" key: %d\n", cipher_parms->key_len); + flow_dump(" key: ", cipher_parms->key_buf, cipher_parms->key_len); + + /* Convert to spu2 values */ + err = spu2_cipher_xlate(cipher_parms->alg, cipher_parms->mode, + cipher_parms->type, &spu2_type, &spu2_mode); + if (err) + return 0; + + flow_log("spu2 cipher type %s, cipher mode %s\n", + spu2_ciph_type_name(spu2_type), + spu2_ciph_mode_name(spu2_mode)); + + /* Construct the FMD header */ + fmd = (struct SPU2_FMD *)spu_hdr; + err = spu2_fmd_init(fmd, spu2_type, spu2_mode, cipher_parms->key_len, + cipher_parms->iv_len); + if (err) + return 0; + + /* Write cipher key to OMD */ + omd = (u8 *)(fmd + 1); + if (cipher_parms->key_buf && cipher_parms->key_len) + memcpy(omd, cipher_parms->key_buf, cipher_parms->key_len); + + packet_dump(" SPU request header: ", spu_hdr, + FMD_SIZE + cipher_parms->key_len + cipher_parms->iv_len); + + return FMD_SIZE + cipher_parms->key_len + cipher_parms->iv_len; +} + +/** + * spu_cipher_req_finish() - Finish building a SPU request message header for a + * block cipher request. + * @spu_hdr: Start of the request message header (MH field) + * @spu_req_hdr_len: Length in bytes of the SPU request header + * @isInbound: 0 encrypt, 1 decrypt + * @cipher_parms: Parameters describing cipher operation to be performed + * @update_key: If true, rewrite the cipher key in SCTX + * @data_size: Length of the data in the BD field + * + * Assumes much of the header was already filled in at setkey() time in + * spu_cipher_req_init(). + * spu_cipher_req_init() fills in the encryption key. For RC4, when submitting a + * request for a non-first chunk, we use the 260-byte SUPDT field from the + * previous response as the key. update_key is true for this case. Unused in all + * other cases. + */ +void spu2_cipher_req_finish(u8 *spu_hdr, + u16 spu_req_hdr_len, + unsigned int is_inbound, + struct spu_cipher_parms *cipher_parms, + bool update_key, + unsigned int data_size) +{ + struct SPU2_FMD *fmd; + u8 *omd; /* start of optional metadata */ + u64 ctrl0; + u64 ctrl3; + + flow_log("%s()\n", __func__); + flow_log(" in: %u\n", is_inbound); + flow_log(" cipher alg: %u, cipher_type: %u\n", cipher_parms->alg, + cipher_parms->type); + if (update_key) { + flow_log(" cipher key len: %u\n", cipher_parms->key_len); + flow_dump(" key: ", cipher_parms->key_buf, + cipher_parms->key_len); + } + flow_log(" iv len: %d\n", cipher_parms->iv_len); + flow_dump(" iv: ", cipher_parms->iv_buf, cipher_parms->iv_len); + flow_log(" data_size: %u\n", data_size); + + fmd = (struct SPU2_FMD *)spu_hdr; + omd = (u8 *)(fmd + 1); + + /* + * FMD ctrl0 was initialized at setkey time. update it to indicate + * whether we are encrypting or decrypting. + */ + ctrl0 = le64_to_cpu(fmd->ctrl0); + if (is_inbound) + ctrl0 &= ~SPU2_CIPH_ENCRYPT_EN; /* decrypt */ + else + ctrl0 |= SPU2_CIPH_ENCRYPT_EN; /* encrypt */ + fmd->ctrl0 = cpu_to_le64(ctrl0); + + if (cipher_parms->alg && cipher_parms->iv_buf && cipher_parms->iv_len) { + /* cipher iv provided so put it in here */ + memcpy(omd + cipher_parms->key_len, cipher_parms->iv_buf, + cipher_parms->iv_len); + } + + ctrl3 = le64_to_cpu(fmd->ctrl3); + data_size &= SPU2_PL_LEN; + ctrl3 |= data_size; + fmd->ctrl3 = cpu_to_le64(ctrl3); + + packet_dump(" SPU request header: ", spu_hdr, spu_req_hdr_len); +} + +/** + * spu_request_pad() - Create pad bytes at the end of the data. + * @pad_start: Start of buffer where pad bytes are to be written + * @gcm_padding: Length of GCM padding, in bytes + * @hash_pad_len: Number of bytes of padding extend data to full block + * @auth_alg: Authentication algorithm + * @auth_mode: Authentication mode + * @total_sent: Length inserted at end of hash pad + * @status_padding: Number of bytes of padding to align STATUS word + * + * There may be three forms of pad: + * 1. GCM pad - for GCM mode ciphers, pad to 16-byte alignment + * 2. hash pad - pad to a block length, with 0x80 data terminator and + * size at the end + * 3. STAT pad - to ensure the STAT field is 4-byte aligned + */ +void spu2_request_pad(u8 *pad_start, u32 gcm_padding, u32 hash_pad_len, + enum hash_alg auth_alg, enum hash_mode auth_mode, + unsigned int total_sent, u32 status_padding) +{ + u8 *ptr = pad_start; + + /* fix data alignent for GCM */ + if (gcm_padding > 0) { + flow_log(" GCM: padding to 16 byte alignment: %u bytes\n", + gcm_padding); + memset(ptr, 0, gcm_padding); + ptr += gcm_padding; + } + + if (hash_pad_len > 0) { + /* clear the padding section */ + memset(ptr, 0, hash_pad_len); + + /* terminate the data */ + *ptr = 0x80; + ptr += (hash_pad_len - sizeof(u64)); + + /* add the size at the end as required per alg */ + if (auth_alg == HASH_ALG_MD5) + *(u64 *)ptr = cpu_to_le64((u64)total_sent * 8); + else /* SHA1, SHA2-224, SHA2-256 */ + *(u64 *)ptr = cpu_to_be64((u64)total_sent * 8); + ptr += sizeof(u64); + } + + /* pad to a 4byte alignment for STAT */ + if (status_padding > 0) { + flow_log(" STAT: padding to 4 byte alignment: %u bytes\n", + status_padding); + + memset(ptr, 0, status_padding); + ptr += status_padding; + } +} + +/** + * spu2_xts_tweak_in_payload() - Indicate that SPU2 does NOT place the XTS + * tweak field in the packet payload (it uses IV instead) + * + * Return: 0 + */ +u8 spu2_xts_tweak_in_payload(void) +{ + return 0; +} + +/** + * spu2_tx_status_len() - Return the length of the STATUS field in a SPU + * response message. + * + * Return: Length of STATUS field in bytes. + */ +u8 spu2_tx_status_len(void) +{ + return SPU2_TX_STATUS_LEN; +} + +/** + * spu2_rx_status_len() - Return the length of the STATUS field in a SPU + * response message. + * + * Return: Length of STATUS field in bytes. + */ +u8 spu2_rx_status_len(void) +{ + return SPU2_RX_STATUS_LEN; +} + +/** + * spu_status_process() - Process the status from a SPU response message. + * @statp: start of STATUS word + * + * Return: 0 - if status is good and response should be processed + * !0 - status indicates an error and response is invalid + */ +int spu2_status_process(u8 *statp) +{ + /* SPU2 status is 2 bytes by default - SPU_RX_STATUS_LEN */ + u16 status = le16_to_cpu(*(__le16 *)statp); + + if (status == 0) + return 0; + + flow_log("rx status is %#x\n", status); + if (status == SPU2_INVALID_ICV) + return SPU_INVALID_ICV; + + return -EBADMSG; +} + +/** + * spu2_ccm_update_iv() - Update the IV as per the requirements for CCM mode. + * + * @digestsize: Digest size of this request + * @cipher_parms: (pointer to) cipher parmaeters, includes IV buf & IV len + * @assoclen: Length of AAD data + * @chunksize: length of input data to be sent in this req + * @is_encrypt: true if this is an output/encrypt operation + * @is_esp: true if this is an ESP / RFC4309 operation + * + */ +void spu2_ccm_update_iv(unsigned int digestsize, + struct spu_cipher_parms *cipher_parms, + unsigned int assoclen, unsigned int chunksize, + bool is_encrypt, bool is_esp) +{ + int L; /* size of length field, in bytes */ + + /* + * In RFC4309 mode, L is fixed at 4 bytes; otherwise, IV from + * testmgr contains (L-1) in bottom 3 bits of first byte, + * per RFC 3610. + */ + if (is_esp) + L = CCM_ESP_L_VALUE; + else + L = ((cipher_parms->iv_buf[0] & CCM_B0_L_PRIME) >> + CCM_B0_L_PRIME_SHIFT) + 1; + + /* SPU2 doesn't want these length bytes nor the first byte... */ + cipher_parms->iv_len -= (1 + L); + memmove(cipher_parms->iv_buf, &cipher_parms->iv_buf[1], + cipher_parms->iv_len); +} + +/** + * spu2_wordalign_padlen() - SPU2 does not require padding. + * @data_size: length of data field in bytes + * + * Return: length of status field padding, in bytes (always 0 on SPU2) + */ +u32 spu2_wordalign_padlen(u32 data_size) +{ + return 0; +} diff --git a/drivers/crypto/bcm/spu2.h b/drivers/crypto/bcm/spu2.h new file mode 100644 index 000000000000..ab1f59934828 --- /dev/null +++ b/drivers/crypto/bcm/spu2.h @@ -0,0 +1,228 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* + * This file contains SPU message definitions specific to SPU2. + */ + +#ifndef _SPU2_H +#define _SPU2_H + +enum spu2_cipher_type { + SPU2_CIPHER_TYPE_NONE = 0x0, + SPU2_CIPHER_TYPE_AES128 = 0x1, + SPU2_CIPHER_TYPE_AES192 = 0x2, + SPU2_CIPHER_TYPE_AES256 = 0x3, + SPU2_CIPHER_TYPE_DES = 0x4, + SPU2_CIPHER_TYPE_3DES = 0x5, + SPU2_CIPHER_TYPE_LAST +}; + +enum spu2_cipher_mode { + SPU2_CIPHER_MODE_ECB = 0x0, + SPU2_CIPHER_MODE_CBC = 0x1, + SPU2_CIPHER_MODE_CTR = 0x2, + SPU2_CIPHER_MODE_CFB = 0x3, + SPU2_CIPHER_MODE_OFB = 0x4, + SPU2_CIPHER_MODE_XTS = 0x5, + SPU2_CIPHER_MODE_CCM = 0x6, + SPU2_CIPHER_MODE_GCM = 0x7, + SPU2_CIPHER_MODE_LAST +}; + +enum spu2_hash_type { + SPU2_HASH_TYPE_NONE = 0x0, + SPU2_HASH_TYPE_AES128 = 0x1, + SPU2_HASH_TYPE_AES192 = 0x2, + SPU2_HASH_TYPE_AES256 = 0x3, + SPU2_HASH_TYPE_MD5 = 0x6, + SPU2_HASH_TYPE_SHA1 = 0x7, + SPU2_HASH_TYPE_SHA224 = 0x8, + SPU2_HASH_TYPE_SHA256 = 0x9, + SPU2_HASH_TYPE_SHA384 = 0xa, + SPU2_HASH_TYPE_SHA512 = 0xb, + SPU2_HASH_TYPE_SHA512_224 = 0xc, + SPU2_HASH_TYPE_SHA512_256 = 0xd, + SPU2_HASH_TYPE_SHA3_224 = 0xe, + SPU2_HASH_TYPE_SHA3_256 = 0xf, + SPU2_HASH_TYPE_SHA3_384 = 0x10, + SPU2_HASH_TYPE_SHA3_512 = 0x11, + SPU2_HASH_TYPE_LAST +}; + +enum spu2_hash_mode { + SPU2_HASH_MODE_CMAC = 0x0, + SPU2_HASH_MODE_CBC_MAC = 0x1, + SPU2_HASH_MODE_XCBC_MAC = 0x2, + SPU2_HASH_MODE_HMAC = 0x3, + SPU2_HASH_MODE_RABIN = 0x4, + SPU2_HASH_MODE_CCM = 0x5, + SPU2_HASH_MODE_GCM = 0x6, + SPU2_HASH_MODE_RESERVED = 0x7, + SPU2_HASH_MODE_LAST +}; + +enum spu2_ret_md_opts { + SPU2_RET_NO_MD = 0, /* return no metadata */ + SPU2_RET_FMD_OMD = 1, /* return both FMD and OMD */ + SPU2_RET_FMD_ONLY = 2, /* return only FMD */ + SPU2_RET_FMD_OMD_IV = 3, /* return FMD and OMD with just IVs */ +}; + +/* Fixed Metadata format */ +struct SPU2_FMD { + u64 ctrl0; + u64 ctrl1; + u64 ctrl2; + u64 ctrl3; +}; + +#define FMD_SIZE sizeof(struct SPU2_FMD) + +/* Fixed part of request message header length in bytes. Just FMD. */ +#define SPU2_REQ_FIXED_LEN FMD_SIZE +#define SPU2_HEADER_ALLOC_LEN (SPU_REQ_FIXED_LEN + \ + 2 * MAX_KEY_SIZE + 2 * MAX_IV_SIZE) + +/* FMD ctrl0 field masks */ +#define SPU2_CIPH_ENCRYPT_EN 0x1 /* 0: decrypt, 1: encrypt */ +#define SPU2_CIPH_TYPE 0xF0 /* one of spu2_cipher_type */ +#define SPU2_CIPH_TYPE_SHIFT 4 +#define SPU2_CIPH_MODE 0xF00 /* one of spu2_cipher_mode */ +#define SPU2_CIPH_MODE_SHIFT 8 +#define SPU2_CFB_MASK 0x7000 /* cipher feedback mask */ +#define SPU2_CFB_MASK_SHIFT 12 +#define SPU2_PROTO_SEL 0xF00000 /* MACsec, IPsec, TLS... */ +#define SPU2_PROTO_SEL_SHIFT 20 +#define SPU2_HASH_FIRST 0x1000000 /* 1: hash input is input pkt + * data + */ +#define SPU2_CHK_TAG 0x2000000 /* 1: check digest provided */ +#define SPU2_HASH_TYPE 0x1F0000000 /* one of spu2_hash_type */ +#define SPU2_HASH_TYPE_SHIFT 28 +#define SPU2_HASH_MODE 0xF000000000 /* one of spu2_hash_mode */ +#define SPU2_HASH_MODE_SHIFT 36 +#define SPU2_CIPH_PAD_EN 0x100000000000 /* 1: Add pad to end of payload for + * enc + */ +#define SPU2_CIPH_PAD 0xFF000000000000 /* cipher pad value */ +#define SPU2_CIPH_PAD_SHIFT 48 + +/* FMD ctrl1 field masks */ +#define SPU2_TAG_LOC 0x1 /* 1: end of payload, 0: undef */ +#define SPU2_HAS_FR_DATA 0x2 /* 1: msg has frame data */ +#define SPU2_HAS_AAD1 0x4 /* 1: msg has AAD1 field */ +#define SPU2_HAS_NAAD 0x8 /* 1: msg has NAAD field */ +#define SPU2_HAS_AAD2 0x10 /* 1: msg has AAD2 field */ +#define SPU2_HAS_ESN 0x20 /* 1: msg has ESN field */ +#define SPU2_HASH_KEY_LEN 0xFF00 /* len of hash key in bytes. + * HMAC only. + */ +#define SPU2_HASH_KEY_LEN_SHIFT 8 +#define SPU2_CIPH_KEY_LEN 0xFF00000 /* len of cipher key in bytes */ +#define SPU2_CIPH_KEY_LEN_SHIFT 20 +#define SPU2_GENIV 0x10000000 /* 1: hw generates IV */ +#define SPU2_HASH_IV 0x20000000 /* 1: IV incl in hash */ +#define SPU2_RET_IV 0x40000000 /* 1: return IV in output msg + * b4 payload + */ +#define SPU2_RET_IV_LEN 0xF00000000 /* length in bytes of IV returned. + * 0 = 16 bytes + */ +#define SPU2_RET_IV_LEN_SHIFT 32 +#define SPU2_IV_OFFSET 0xF000000000 /* gen IV offset */ +#define SPU2_IV_OFFSET_SHIFT 36 +#define SPU2_IV_LEN 0x1F0000000000 /* length of input IV in bytes */ +#define SPU2_IV_LEN_SHIFT 40 +#define SPU2_HASH_TAG_LEN 0x7F000000000000 /* hash tag length in bytes */ +#define SPU2_HASH_TAG_LEN_SHIFT 48 +#define SPU2_RETURN_MD 0x300000000000000 /* return metadata */ +#define SPU2_RETURN_MD_SHIFT 56 +#define SPU2_RETURN_FD 0x400000000000000 +#define SPU2_RETURN_AAD1 0x800000000000000 +#define SPU2_RETURN_NAAD 0x1000000000000000 +#define SPU2_RETURN_AAD2 0x2000000000000000 +#define SPU2_RETURN_PAY 0x4000000000000000 /* return payload */ + +/* FMD ctrl2 field masks */ +#define SPU2_AAD1_OFFSET 0xFFF /* byte offset of AAD1 field */ +#define SPU2_AAD1_LEN 0xFF000 /* length of AAD1 in bytes */ +#define SPU2_AAD1_LEN_SHIFT 12 +#define SPU2_AAD2_OFFSET 0xFFF00000 /* byte offset of AAD2 field */ +#define SPU2_AAD2_OFFSET_SHIFT 20 +#define SPU2_PL_OFFSET 0xFFFFFFFF00000000 /* payload offset from AAD2 */ +#define SPU2_PL_OFFSET_SHIFT 32 + +/* FMD ctrl3 field masks */ +#define SPU2_PL_LEN 0xFFFFFFFF /* payload length in bytes */ +#define SPU2_TLS_LEN 0xFFFF00000000 /* TLS encrypt: cipher len + * TLS decrypt: compressed len + */ +#define SPU2_TLS_LEN_SHIFT 32 + +/* + * Max value that can be represented in the Payload Length field of the + * ctrl3 word of FMD. + */ +#define SPU2_MAX_PAYLOAD SPU2_PL_LEN + +/* Error values returned in STATUS field of response messages */ +#define SPU2_INVALID_ICV 1 + +void spu2_dump_msg_hdr(u8 *buf, unsigned int buf_len); +u32 spu2_ctx_max_payload(enum spu_cipher_alg cipher_alg, + enum spu_cipher_mode cipher_mode, + unsigned int blocksize); +u32 spu2_payload_length(u8 *spu_hdr); +u16 spu2_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash); +u16 spu2_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode, + u32 chunksize, u16 hash_block_size); +u32 spu2_gcm_ccm_pad_len(enum spu_cipher_mode cipher_mode, + unsigned int data_size); +u32 spu2_assoc_resp_len(enum spu_cipher_mode cipher_mode, + unsigned int assoc_len, unsigned int iv_len, + bool is_encrypt); +u8 spu2_aead_ivlen(enum spu_cipher_mode cipher_mode, + u16 iv_len); +enum hash_type spu2_hash_type(u32 src_sent); +u32 spu2_digest_size(u32 alg_digest_size, enum hash_alg alg, + enum hash_type htype); +u32 spu2_create_request(u8 *spu_hdr, + struct spu_request_opts *req_opts, + struct spu_cipher_parms *cipher_parms, + struct spu_hash_parms *hash_parms, + struct spu_aead_parms *aead_parms, + unsigned int data_size); +u16 spu2_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms); +void spu2_cipher_req_finish(u8 *spu_hdr, + u16 spu_req_hdr_len, + unsigned int is_inbound, + struct spu_cipher_parms *cipher_parms, + bool update_key, + unsigned int data_size); +void spu2_request_pad(u8 *pad_start, u32 gcm_padding, u32 hash_pad_len, + enum hash_alg auth_alg, enum hash_mode auth_mode, + unsigned int total_sent, u32 status_padding); +u8 spu2_xts_tweak_in_payload(void); +u8 spu2_tx_status_len(void); +u8 spu2_rx_status_len(void); +int spu2_status_process(u8 *statp); +void spu2_ccm_update_iv(unsigned int digestsize, + struct spu_cipher_parms *cipher_parms, + unsigned int assoclen, unsigned int chunksize, + bool is_encrypt, bool is_esp); +u32 spu2_wordalign_padlen(u32 data_size); +#endif diff --git a/drivers/crypto/bcm/spum.h b/drivers/crypto/bcm/spum.h new file mode 100644 index 000000000000..d0a5b5828638 --- /dev/null +++ b/drivers/crypto/bcm/spum.h @@ -0,0 +1,174 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +/* + * This file contains SPU message definitions specific to SPU-M. + */ + +#ifndef _SPUM_H_ +#define _SPUM_H_ + +#define SPU_CRYPTO_OPERATION_GENERIC 0x1 + +/* Length of STATUS field in tx and rx packets */ +#define SPU_TX_STATUS_LEN 4 + +/* SPU-M error codes */ +#define SPU_STATUS_MASK 0x0000FF00 +#define SPU_STATUS_SUCCESS 0x00000000 +#define SPU_STATUS_INVALID_ICV 0x00000100 + +#define SPU_STATUS_ERROR_FLAG 0x00020000 + +/* Request message. MH + EMH + BDESC + BD header */ +#define SPU_REQ_FIXED_LEN 24 + +/* + * Max length of a SPU message header. Used to allocate a buffer where + * the SPU message header is constructed. Can be used for either a SPU-M + * header or a SPU2 header. + * For SPU-M, sum of the following: + * MH - 4 bytes + * EMH - 4 + * SCTX - 3 + + * max auth key len - 64 + * max cipher key len - 264 (RC4) + * max IV len - 16 + * BDESC - 12 + * BD header - 4 + * Total: 371 + * + * For SPU2, FMD_SIZE (32) plus lengths of hash and cipher keys, + * hash and cipher IVs. If SPU2 does not support RC4, then + */ +#define SPU_HEADER_ALLOC_LEN (SPU_REQ_FIXED_LEN + MAX_KEY_SIZE + \ + MAX_KEY_SIZE + MAX_IV_SIZE) + +/* + * Response message header length. Normally MH, EMH, BD header, but when + * BD_SUPPRESS is used for hash requests, there is no BD header. + */ +#define SPU_RESP_HDR_LEN 12 +#define SPU_HASH_RESP_HDR_LEN 8 + +/* + * Max value that can be represented in the Payload Length field of the BD + * header. This is a 16-bit field. + */ +#define SPUM_NS2_MAX_PAYLOAD (BIT(16) - 1) + +/* + * NSP SPU is limited to ~9KB because of FA2 FIFO size limitations; + * Set MAX_PAYLOAD to 8k to allow for addition of header, digest, etc. + * and stay within limitation. + */ + +#define SPUM_NSP_MAX_PAYLOAD 8192 + +/* Buffer Descriptor Header [BDESC]. SPU in big-endian mode. */ +struct BDESC_HEADER { + u16 offset_mac; /* word 0 [31-16] */ + u16 length_mac; /* word 0 [15-0] */ + u16 offset_crypto; /* word 1 [31-16] */ + u16 length_crypto; /* word 1 [15-0] */ + u16 offset_icv; /* word 2 [31-16] */ + u16 offset_iv; /* word 2 [15-0] */ +}; + +/* Buffer Data Header [BD]. SPU in big-endian mode. */ +struct BD_HEADER { + u16 size; + u16 prev_length; +}; + +/* Command Context Header. SPU-M in big endian mode. */ +struct MHEADER { + u8 flags; /* [31:24] */ + u8 op_code; /* [23:16] */ + u16 reserved; /* [15:0] */ +}; + +/* MH header flags bits */ +#define MH_SUPDT_PRES BIT(0) +#define MH_HASH_PRES BIT(2) +#define MH_BD_PRES BIT(3) +#define MH_MFM_PRES BIT(4) +#define MH_BDESC_PRES BIT(5) +#define MH_SCTX_PRES BIT(7) + +/* SCTX word 0 bit offsets and fields masks */ +#define SCTX_SIZE 0x000000FF + +/* SCTX word 1 bit shifts and field masks */ +#define UPDT_OFST 0x000000FF /* offset of SCTX updateable fld */ +#define HASH_TYPE 0x00000300 /* hash alg operation type */ +#define HASH_TYPE_SHIFT 8 +#define HASH_MODE 0x00001C00 /* one of spu2_hash_mode */ +#define HASH_MODE_SHIFT 10 +#define HASH_ALG 0x0000E000 /* hash algorithm */ +#define HASH_ALG_SHIFT 13 +#define CIPHER_TYPE 0x00030000 /* encryption operation type */ +#define CIPHER_TYPE_SHIFT 16 +#define CIPHER_MODE 0x001C0000 /* encryption mode */ +#define CIPHER_MODE_SHIFT 18 +#define CIPHER_ALG 0x00E00000 /* encryption algo */ +#define CIPHER_ALG_SHIFT 21 +#define ICV_IS_512 BIT(27) +#define ICV_IS_512_SHIFT 27 +#define CIPHER_ORDER BIT(30) +#define CIPHER_ORDER_SHIFT 30 +#define CIPHER_INBOUND BIT(31) +#define CIPHER_INBOUND_SHIFT 31 + +/* SCTX word 2 bit shifts and field masks */ +#define EXP_IV_SIZE 0x7 +#define IV_OFFSET BIT(3) +#define IV_OFFSET_SHIFT 3 +#define GEN_IV BIT(5) +#define GEN_IV_SHIFT 5 +#define EXPLICIT_IV BIT(6) +#define EXPLICIT_IV_SHIFT 6 +#define SCTX_IV BIT(7) +#define SCTX_IV_SHIFT 7 +#define ICV_SIZE 0x0F00 +#define ICV_SIZE_SHIFT 8 +#define CHECK_ICV BIT(12) +#define CHECK_ICV_SHIFT 12 +#define INSERT_ICV BIT(13) +#define INSERT_ICV_SHIFT 13 +#define BD_SUPPRESS BIT(19) +#define BD_SUPPRESS_SHIFT 19 + +/* Generic Mode Security Context Structure [SCTX] */ +struct SCTX { +/* word 0: protocol flags */ + u32 proto_flags; + +/* word 1: cipher flags */ + u32 cipher_flags; + +/* word 2: Extended cipher flags */ + u32 ecf; + +}; + +struct SPUHEADER { + struct MHEADER mh; + u32 emh; + struct SCTX sa; +}; + +#endif /* _SPUM_H_ */ diff --git a/drivers/crypto/bcm/util.c b/drivers/crypto/bcm/util.c new file mode 100644 index 000000000000..0502f460dacd --- /dev/null +++ b/drivers/crypto/bcm/util.c @@ -0,0 +1,581 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#include <linux/debugfs.h> + +#include "cipher.h" +#include "util.h" + +/* offset of SPU_OFIFO_CTRL register */ +#define SPU_OFIFO_CTRL 0x40 +#define SPU_FIFO_WATERMARK 0x1FF + +/** + * spu_sg_at_offset() - Find the scatterlist entry at a given distance from the + * start of a scatterlist. + * @sg: [in] Start of a scatterlist + * @skip: [in] Distance from the start of the scatterlist, in bytes + * @sge: [out] Scatterlist entry at skip bytes from start + * @sge_offset: [out] Number of bytes from start of sge buffer to get to + * requested distance. + * + * Return: 0 if entry found at requested distance + * < 0 otherwise + */ +int spu_sg_at_offset(struct scatterlist *sg, unsigned int skip, + struct scatterlist **sge, unsigned int *sge_offset) +{ + /* byte index from start of sg to the end of the previous entry */ + unsigned int index = 0; + /* byte index from start of sg to the end of the current entry */ + unsigned int next_index; + + next_index = sg->length; + while (next_index <= skip) { + sg = sg_next(sg); + index = next_index; + if (!sg) + return -EINVAL; + next_index += sg->length; + } + + *sge_offset = skip - index; + *sge = sg; + return 0; +} + +/* Copy len bytes of sg data, starting at offset skip, to a dest buffer */ +void sg_copy_part_to_buf(struct scatterlist *src, u8 *dest, + unsigned int len, unsigned int skip) +{ + size_t copied; + unsigned int nents = sg_nents(src); + + copied = sg_pcopy_to_buffer(src, nents, dest, len, skip); + if (copied != len) { + flow_log("%s copied %u bytes of %u requested. ", + __func__, (u32)copied, len); + flow_log("sg with %u entries and skip %u\n", nents, skip); + } +} + +/* + * Copy data into a scatterlist starting at a specified offset in the + * scatterlist. Specifically, copy len bytes of data in the buffer src + * into the scatterlist dest, starting skip bytes into the scatterlist. + */ +void sg_copy_part_from_buf(struct scatterlist *dest, u8 *src, + unsigned int len, unsigned int skip) +{ + size_t copied; + unsigned int nents = sg_nents(dest); + + copied = sg_pcopy_from_buffer(dest, nents, src, len, skip); + if (copied != len) { + flow_log("%s copied %u bytes of %u requested. ", + __func__, (u32)copied, len); + flow_log("sg with %u entries and skip %u\n", nents, skip); + } +} + +/** + * spu_sg_count() - Determine number of elements in scatterlist to provide a + * specified number of bytes. + * @sg_list: scatterlist to examine + * @skip: index of starting point + * @nbytes: consider elements of scatterlist until reaching this number of + * bytes + * + * Return: the number of sg entries contributing to nbytes of data + */ +int spu_sg_count(struct scatterlist *sg_list, unsigned int skip, int nbytes) +{ + struct scatterlist *sg; + int sg_nents = 0; + unsigned int offset; + + if (!sg_list) + return 0; + + if (spu_sg_at_offset(sg_list, skip, &sg, &offset) < 0) + return 0; + + while (sg && (nbytes > 0)) { + sg_nents++; + nbytes -= (sg->length - offset); + offset = 0; + sg = sg_next(sg); + } + return sg_nents; +} + +/** + * spu_msg_sg_add() - Copy scatterlist entries from one sg to another, up to a + * given length. + * @to_sg: scatterlist to copy to + * @from_sg: scatterlist to copy from + * @from_skip: number of bytes to skip in from_sg. Non-zero when previous + * request included part of the buffer in entry in from_sg. + * Assumes from_skip < from_sg->length. + * @from_nents number of entries in from_sg + * @length number of bytes to copy. may reach this limit before exhausting + * from_sg. + * + * Copies the entries themselves, not the data in the entries. Assumes to_sg has + * enough entries. Does not limit the size of an individual buffer in to_sg. + * + * to_sg, from_sg, skip are all updated to end of copy + * + * Return: Number of bytes copied + */ +u32 spu_msg_sg_add(struct scatterlist **to_sg, + struct scatterlist **from_sg, u32 *from_skip, + u8 from_nents, u32 length) +{ + struct scatterlist *sg; /* an entry in from_sg */ + struct scatterlist *to = *to_sg; + struct scatterlist *from = *from_sg; + u32 skip = *from_skip; + u32 offset; + int i; + u32 entry_len = 0; + u32 frag_len = 0; /* length of entry added to to_sg */ + u32 copied = 0; /* number of bytes copied so far */ + + if (length == 0) + return 0; + + for_each_sg(from, sg, from_nents, i) { + /* number of bytes in this from entry not yet used */ + entry_len = sg->length - skip; + frag_len = min(entry_len, length - copied); + offset = sg->offset + skip; + if (frag_len) + sg_set_page(to++, sg_page(sg), frag_len, offset); + copied += frag_len; + if (copied == entry_len) { + /* used up all of from entry */ + skip = 0; /* start at beginning of next entry */ + } + if (copied == length) + break; + } + *to_sg = to; + *from_sg = sg; + if (frag_len < entry_len) + *from_skip = skip + frag_len; + else + *from_skip = 0; + + return copied; +} + +void add_to_ctr(u8 *ctr_pos, unsigned int increment) +{ + __be64 *high_be = (__be64 *)ctr_pos; + __be64 *low_be = high_be + 1; + u64 orig_low = __be64_to_cpu(*low_be); + u64 new_low = orig_low + (u64)increment; + + *low_be = __cpu_to_be64(new_low); + if (new_low < orig_low) + /* there was a carry from the low 8 bytes */ + *high_be = __cpu_to_be64(__be64_to_cpu(*high_be) + 1); +} + +struct sdesc { + struct shash_desc shash; + char ctx[]; +}; + +/* do a synchronous decrypt operation */ +int do_decrypt(char *alg_name, + void *key_ptr, unsigned int key_len, + void *iv_ptr, void *src_ptr, void *dst_ptr, + unsigned int block_len) +{ + struct scatterlist sg_in[1], sg_out[1]; + struct crypto_blkcipher *tfm = + crypto_alloc_blkcipher(alg_name, 0, CRYPTO_ALG_ASYNC); + struct blkcipher_desc desc = {.tfm = tfm, .flags = 0 }; + int ret = 0; + void *iv; + int ivsize; + + flow_log("%s() name:%s block_len:%u\n", __func__, alg_name, block_len); + + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + crypto_blkcipher_setkey((void *)tfm, key_ptr, key_len); + + sg_init_table(sg_in, 1); + sg_set_buf(sg_in, src_ptr, block_len); + + sg_init_table(sg_out, 1); + sg_set_buf(sg_out, dst_ptr, block_len); + + iv = crypto_blkcipher_crt(tfm)->iv; + ivsize = crypto_blkcipher_ivsize(tfm); + memcpy(iv, iv_ptr, ivsize); + + ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, block_len); + crypto_free_blkcipher(tfm); + + if (ret < 0) + pr_err("aes_decrypt failed %d\n", ret); + + return ret; +} + +/** + * do_shash() - Do a synchronous hash operation in software + * @name: The name of the hash algorithm + * @result: Buffer where digest is to be written + * @data1: First part of data to hash. May be NULL. + * @data1_len: Length of data1, in bytes + * @data2: Second part of data to hash. May be NULL. + * @data2_len: Length of data2, in bytes + * @key: Key (if keyed hash) + * @key_len: Length of key, in bytes (or 0 if non-keyed hash) + * + * Note that the crypto API will not select this driver's own transform because + * this driver only registers asynchronous algos. + * + * Return: 0 if hash successfully stored in result + * < 0 otherwise + */ +int do_shash(unsigned char *name, unsigned char *result, + const u8 *data1, unsigned int data1_len, + const u8 *data2, unsigned int data2_len, + const u8 *key, unsigned int key_len) +{ + int rc; + unsigned int size; + struct crypto_shash *hash; + struct sdesc *sdesc; + + hash = crypto_alloc_shash(name, 0, 0); + if (IS_ERR(hash)) { + rc = PTR_ERR(hash); + pr_err("%s: Crypto %s allocation error %d", __func__, name, rc); + return rc; + } + + size = sizeof(struct shash_desc) + crypto_shash_descsize(hash); + sdesc = kmalloc(size, GFP_KERNEL); + if (!sdesc) { + rc = -ENOMEM; + pr_err("%s: Memory allocation failure", __func__); + goto do_shash_err; + } + sdesc->shash.tfm = hash; + sdesc->shash.flags = 0x0; + + if (key_len > 0) { + rc = crypto_shash_setkey(hash, key, key_len); + if (rc) { + pr_err("%s: Could not setkey %s shash", __func__, name); + goto do_shash_err; + } + } + + rc = crypto_shash_init(&sdesc->shash); + if (rc) { + pr_err("%s: Could not init %s shash", __func__, name); + goto do_shash_err; + } + rc = crypto_shash_update(&sdesc->shash, data1, data1_len); + if (rc) { + pr_err("%s: Could not update1", __func__); + goto do_shash_err; + } + if (data2 && data2_len) { + rc = crypto_shash_update(&sdesc->shash, data2, data2_len); + if (rc) { + pr_err("%s: Could not update2", __func__); + goto do_shash_err; + } + } + rc = crypto_shash_final(&sdesc->shash, result); + if (rc) + pr_err("%s: Could not genereate %s hash", __func__, name); + +do_shash_err: + crypto_free_shash(hash); + kfree(sdesc); + + return rc; +} + +/* Dump len bytes of a scatterlist starting at skip bytes into the sg */ +void __dump_sg(struct scatterlist *sg, unsigned int skip, unsigned int len) +{ + u8 dbuf[16]; + unsigned int idx = skip; + unsigned int num_out = 0; /* number of bytes dumped so far */ + unsigned int count; + + if (packet_debug_logging) { + while (num_out < len) { + count = (len - num_out > 16) ? 16 : len - num_out; + sg_copy_part_to_buf(sg, dbuf, count, idx); + num_out += count; + print_hex_dump(KERN_ALERT, " sg: ", DUMP_PREFIX_NONE, + 4, 1, dbuf, count, false); + idx += 16; + } + } + if (debug_logging_sleep) + msleep(debug_logging_sleep); +} + +/* Returns the name for a given cipher alg/mode */ +char *spu_alg_name(enum spu_cipher_alg alg, enum spu_cipher_mode mode) +{ + switch (alg) { + case CIPHER_ALG_RC4: + return "rc4"; + case CIPHER_ALG_AES: + switch (mode) { + case CIPHER_MODE_CBC: + return "cbc(aes)"; + case CIPHER_MODE_ECB: + return "ecb(aes)"; + case CIPHER_MODE_OFB: + return "ofb(aes)"; + case CIPHER_MODE_CFB: + return "cfb(aes)"; + case CIPHER_MODE_CTR: + return "ctr(aes)"; + case CIPHER_MODE_XTS: + return "xts(aes)"; + case CIPHER_MODE_GCM: + return "gcm(aes)"; + default: + return "aes"; + } + break; + case CIPHER_ALG_DES: + switch (mode) { + case CIPHER_MODE_CBC: + return "cbc(des)"; + case CIPHER_MODE_ECB: + return "ecb(des)"; + case CIPHER_MODE_CTR: + return "ctr(des)"; + default: + return "des"; + } + break; + case CIPHER_ALG_3DES: + switch (mode) { + case CIPHER_MODE_CBC: + return "cbc(des3_ede)"; + case CIPHER_MODE_ECB: + return "ecb(des3_ede)"; + case CIPHER_MODE_CTR: + return "ctr(des3_ede)"; + default: + return "3des"; + } + break; + default: + return "other"; + } +} + +static ssize_t spu_debugfs_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct device_private *ipriv; + char *buf; + ssize_t ret, out_offset, out_count; + int i; + u32 fifo_len; + u32 spu_ofifo_ctrl; + u32 alg; + u32 mode; + u32 op_cnt; + + out_count = 2048; + + buf = kmalloc(out_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ipriv = filp->private_data; + out_offset = 0; + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Number of SPUs.........%u\n", + ipriv->spu.num_spu); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Current sessions.......%u\n", + atomic_read(&ipriv->session_count)); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Session count..........%u\n", + atomic_read(&ipriv->stream_count)); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Cipher setkey..........%u\n", + atomic_read(&ipriv->setkey_cnt[SPU_OP_CIPHER])); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Cipher Ops.............%u\n", + atomic_read(&ipriv->op_counts[SPU_OP_CIPHER])); + for (alg = 0; alg < CIPHER_ALG_LAST; alg++) { + for (mode = 0; mode < CIPHER_MODE_LAST; mode++) { + op_cnt = atomic_read(&ipriv->cipher_cnt[alg][mode]); + if (op_cnt) { + out_offset += snprintf(buf + out_offset, + out_count - out_offset, + " %-13s%11u\n", + spu_alg_name(alg, mode), op_cnt); + } + } + } + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Hash Ops...............%u\n", + atomic_read(&ipriv->op_counts[SPU_OP_HASH])); + for (alg = 0; alg < HASH_ALG_LAST; alg++) { + op_cnt = atomic_read(&ipriv->hash_cnt[alg]); + if (op_cnt) { + out_offset += snprintf(buf + out_offset, + out_count - out_offset, + " %-13s%11u\n", + hash_alg_name[alg], op_cnt); + } + } + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "HMAC setkey............%u\n", + atomic_read(&ipriv->setkey_cnt[SPU_OP_HMAC])); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "HMAC Ops...............%u\n", + atomic_read(&ipriv->op_counts[SPU_OP_HMAC])); + for (alg = 0; alg < HASH_ALG_LAST; alg++) { + op_cnt = atomic_read(&ipriv->hmac_cnt[alg]); + if (op_cnt) { + out_offset += snprintf(buf + out_offset, + out_count - out_offset, + " %-13s%11u\n", + hash_alg_name[alg], op_cnt); + } + } + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "AEAD setkey............%u\n", + atomic_read(&ipriv->setkey_cnt[SPU_OP_AEAD])); + + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "AEAD Ops...............%u\n", + atomic_read(&ipriv->op_counts[SPU_OP_AEAD])); + for (alg = 0; alg < AEAD_TYPE_LAST; alg++) { + op_cnt = atomic_read(&ipriv->aead_cnt[alg]); + if (op_cnt) { + out_offset += snprintf(buf + out_offset, + out_count - out_offset, + " %-13s%11u\n", + aead_alg_name[alg], op_cnt); + } + } + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Bytes of req data......%llu\n", + (u64)atomic64_read(&ipriv->bytes_out)); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Bytes of resp data.....%llu\n", + (u64)atomic64_read(&ipriv->bytes_in)); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Mailbox full...........%u\n", + atomic_read(&ipriv->mb_no_spc)); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Mailbox send failures..%u\n", + atomic_read(&ipriv->mb_send_fail)); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "Check ICV errors.......%u\n", + atomic_read(&ipriv->bad_icv)); + if (ipriv->spu.spu_type == SPU_TYPE_SPUM) + for (i = 0; i < ipriv->spu.num_spu; i++) { + spu_ofifo_ctrl = ioread32(ipriv->spu.reg_vbase[i] + + SPU_OFIFO_CTRL); + fifo_len = spu_ofifo_ctrl & SPU_FIFO_WATERMARK; + out_offset += snprintf(buf + out_offset, + out_count - out_offset, + "SPU %d output FIFO high water.....%u\n", + i, fifo_len); + } + + if (out_offset > out_count) + out_offset = out_count; + + ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset); + kfree(buf); + return ret; +} + +static const struct file_operations spu_debugfs_stats = { + .owner = THIS_MODULE, + .open = simple_open, + .read = spu_debugfs_read, +}; + +/* + * Create the debug FS directories. If the top-level directory has not yet + * been created, create it now. Create a stats file in this directory for + * a SPU. + */ +void spu_setup_debugfs(void) +{ + if (!debugfs_initialized()) + return; + + if (!iproc_priv.debugfs_dir) + iproc_priv.debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, + NULL); + + if (!iproc_priv.debugfs_stats) + /* Create file with permissions S_IRUSR */ + debugfs_create_file("stats", 0400, iproc_priv.debugfs_dir, + &iproc_priv, &spu_debugfs_stats); +} + +void spu_free_debugfs(void) +{ + debugfs_remove_recursive(iproc_priv.debugfs_dir); + iproc_priv.debugfs_dir = NULL; +} + +/** + * format_value_ccm() - Format a value into a buffer, using a specified number + * of bytes (i.e. maybe writing value X into a 4 byte + * buffer, or maybe into a 12 byte buffer), as per the + * SPU CCM spec. + * + * @val: value to write (up to max of unsigned int) + * @buf: (pointer to) buffer to write the value + * @len: number of bytes to use (0 to 255) + * + */ +void format_value_ccm(unsigned int val, u8 *buf, u8 len) +{ + int i; + + /* First clear full output buffer */ + memset(buf, 0, len); + + /* Then, starting from right side, fill in with data */ + for (i = 0; i < len; i++) { + buf[len - i - 1] = (val >> (8 * i)) & 0xff; + if (i >= 3) + break; /* Only handle up to 32 bits of 'val' */ + } +} diff --git a/drivers/crypto/bcm/util.h b/drivers/crypto/bcm/util.h new file mode 100644 index 000000000000..712e029795f8 --- /dev/null +++ b/drivers/crypto/bcm/util.h @@ -0,0 +1,116 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#ifndef _UTIL_H +#define _UTIL_H + +#include <linux/kernel.h> +#include <linux/delay.h> + +#include "spu.h" + +extern int flow_debug_logging; +extern int packet_debug_logging; +extern int debug_logging_sleep; + +#ifdef DEBUG +#define flow_log(...) \ + do { \ + if (flow_debug_logging) { \ + printk(__VA_ARGS__); \ + if (debug_logging_sleep) \ + msleep(debug_logging_sleep); \ + } \ + } while (0) +#define flow_dump(msg, var, var_len) \ + do { \ + if (flow_debug_logging) { \ + print_hex_dump(KERN_ALERT, msg, DUMP_PREFIX_NONE, \ + 16, 1, var, var_len, false); \ + if (debug_logging_sleep) \ + msleep(debug_logging_sleep); \ + } \ + } while (0) + +#define packet_log(...) \ + do { \ + if (packet_debug_logging) { \ + printk(__VA_ARGS__); \ + if (debug_logging_sleep) \ + msleep(debug_logging_sleep); \ + } \ + } while (0) +#define packet_dump(msg, var, var_len) \ + do { \ + if (packet_debug_logging) { \ + print_hex_dump(KERN_ALERT, msg, DUMP_PREFIX_NONE, \ + 16, 1, var, var_len, false); \ + if (debug_logging_sleep) \ + msleep(debug_logging_sleep); \ + } \ + } while (0) + +void __dump_sg(struct scatterlist *sg, unsigned int skip, unsigned int len); + +#define dump_sg(sg, skip, len) __dump_sg(sg, skip, len) + +#else /* !DEBUG_ON */ + +#define flow_log(...) do {} while (0) +#define flow_dump(msg, var, var_len) do {} while (0) +#define packet_log(...) do {} while (0) +#define packet_dump(msg, var, var_len) do {} while (0) + +#define dump_sg(sg, skip, len) do {} while (0) + +#endif /* DEBUG_ON */ + +int spu_sg_at_offset(struct scatterlist *sg, unsigned int skip, + struct scatterlist **sge, unsigned int *sge_offset); + +/* Copy sg data, from skip, length len, to dest */ +void sg_copy_part_to_buf(struct scatterlist *src, u8 *dest, + unsigned int len, unsigned int skip); +/* Copy src into scatterlist from offset, length len */ +void sg_copy_part_from_buf(struct scatterlist *dest, u8 *src, + unsigned int len, unsigned int skip); + +int spu_sg_count(struct scatterlist *sg_list, unsigned int skip, int nbytes); +u32 spu_msg_sg_add(struct scatterlist **to_sg, + struct scatterlist **from_sg, u32 *skip, + u8 from_nents, u32 tot_len); + +void add_to_ctr(u8 *ctr_pos, unsigned int increment); + +/* do a synchronous decrypt operation */ +int do_decrypt(char *alg_name, + void *key_ptr, unsigned int key_len, + void *iv_ptr, void *src_ptr, void *dst_ptr, + unsigned int block_len); + +/* produce a message digest from data of length n bytes */ +int do_shash(unsigned char *name, unsigned char *result, + const u8 *data1, unsigned int data1_len, + const u8 *data2, unsigned int data2_len, + const u8 *key, unsigned int key_len); + +char *spu_alg_name(enum spu_cipher_alg alg, enum spu_cipher_mode mode); + +void spu_setup_debugfs(void); +void spu_free_debugfs(void); +void format_value_ccm(unsigned int val, u8 *buf, u8 len); + +#endif diff --git a/drivers/crypto/bfin_crc.c b/drivers/crypto/bfin_crc.c index 10db7df366c8..a118b9bed669 100644 --- a/drivers/crypto/bfin_crc.c +++ b/drivers/crypto/bfin_crc.c @@ -203,7 +203,7 @@ static void bfin_crypto_crc_config_dma(struct bfin_crypto_crc *crc) crc->sg_cpu[i].x_count = 1; crc->sg_cpu[i].x_modify = CHKSUM_DIGEST_SIZE; dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, " - "cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n", + "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n", i, crc->sg_cpu[i].start_addr, crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count, crc->sg_cpu[i].x_modify); @@ -233,7 +233,7 @@ static void bfin_crypto_crc_config_dma(struct bfin_crypto_crc *crc) crc->sg_cpu[i].x_count = dma_count; crc->sg_cpu[i].x_modify = dma_mod; dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, " - "cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n", + "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n", i, crc->sg_cpu[i].start_addr, crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count, crc->sg_cpu[i].x_modify); @@ -257,7 +257,7 @@ static void bfin_crypto_crc_config_dma(struct bfin_crypto_crc *crc) crc->sg_cpu[i].x_count = 1; crc->sg_cpu[i].x_modify = CHKSUM_DIGEST_SIZE; dev_dbg(crc->dev, "%d: crc_dma: start_addr:0x%lx, " - "cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n", + "cfg:0x%x, x_count:0x%x, x_modify:0x%x\n", i, crc->sg_cpu[i].start_addr, crc->sg_cpu[i].cfg, crc->sg_cpu[i].x_count, crc->sg_cpu[i].x_modify); diff --git a/drivers/crypto/bfin_crc.h b/drivers/crypto/bfin_crc.h index 75cef4dc85a1..786ef746d109 100644 --- a/drivers/crypto/bfin_crc.h +++ b/drivers/crypto/bfin_crc.h @@ -55,7 +55,6 @@ struct crc_info { #include <linux/types.h> #include <linux/spinlock.h> -#include <linux/miscdevice.h> struct crc_register { u32 control; diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 662fe94cb2f8..9bc80eb06934 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -134,15 +134,15 @@ struct caam_aead_alg { * per-session context */ struct caam_ctx { - struct device *jrdev; u32 sh_desc_enc[DESC_MAX_USED_LEN]; u32 sh_desc_dec[DESC_MAX_USED_LEN]; u32 sh_desc_givenc[DESC_MAX_USED_LEN]; + u8 key[CAAM_MAX_KEY_SIZE]; dma_addr_t sh_desc_enc_dma; dma_addr_t sh_desc_dec_dma; dma_addr_t sh_desc_givenc_dma; - u8 key[CAAM_MAX_KEY_SIZE]; dma_addr_t key_dma; + struct device *jrdev; struct alginfo adata; struct alginfo cdata; unsigned int authsize; @@ -171,13 +171,8 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead) /* aead_encrypt shared descriptor */ desc = ctx->sh_desc_enc; cnstr_shdsc_aead_null_encap(desc, &ctx->adata, ctx->authsize); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* * Job Descriptor and Shared Descriptors @@ -194,13 +189,8 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead) /* aead_decrypt shared descriptor */ desc = ctx->sh_desc_dec; cnstr_shdsc_aead_null_decap(desc, &ctx->adata, ctx->authsize); - ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } @@ -278,13 +268,8 @@ static int aead_set_sh_desc(struct crypto_aead *aead) desc = ctx->sh_desc_enc; cnstr_shdsc_aead_encap(desc, &ctx->cdata, &ctx->adata, ctx->authsize, is_rfc3686, nonce, ctx1_iv_off); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); skip_enc: /* @@ -315,13 +300,8 @@ skip_enc: cnstr_shdsc_aead_decap(desc, &ctx->cdata, &ctx->adata, ivsize, ctx->authsize, alg->caam.geniv, is_rfc3686, nonce, ctx1_iv_off); - ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); if (!alg->caam.geniv) goto skip_givenc; @@ -354,13 +334,8 @@ skip_enc: cnstr_shdsc_aead_givencap(desc, &ctx->cdata, &ctx->adata, ivsize, ctx->authsize, is_rfc3686, nonce, ctx1_iv_off); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); skip_givenc: return 0; @@ -403,13 +378,8 @@ static int gcm_set_sh_desc(struct crypto_aead *aead) desc = ctx->sh_desc_enc; cnstr_shdsc_gcm_encap(desc, &ctx->cdata, ctx->authsize); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* * Job Descriptor and Shared Descriptors @@ -425,13 +395,8 @@ static int gcm_set_sh_desc(struct crypto_aead *aead) desc = ctx->sh_desc_dec; cnstr_shdsc_gcm_decap(desc, &ctx->cdata, ctx->authsize); - ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } @@ -472,13 +437,8 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead) desc = ctx->sh_desc_enc; cnstr_shdsc_rfc4106_encap(desc, &ctx->cdata, ctx->authsize); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* * Job Descriptor and Shared Descriptors @@ -494,13 +454,8 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead) desc = ctx->sh_desc_dec; cnstr_shdsc_rfc4106_decap(desc, &ctx->cdata, ctx->authsize); - ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } @@ -542,13 +497,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead) desc = ctx->sh_desc_enc; cnstr_shdsc_rfc4543_encap(desc, &ctx->cdata, ctx->authsize); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* * Job Descriptor and Shared Descriptors @@ -564,13 +514,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead) desc = ctx->sh_desc_dec; cnstr_shdsc_rfc4543_decap(desc, &ctx->cdata, ctx->authsize); - ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } @@ -614,28 +559,15 @@ static int aead_setkey(struct crypto_aead *aead, /* postpend encryption key to auth split key */ memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen); - - ctx->key_dma = dma_map_single(jrdev, ctx->key, ctx->adata.keylen_pad + - keys.enckeylen, DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->key_dma)) { - dev_err(jrdev, "unable to map key i/o memory\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->adata.keylen_pad + + keys.enckeylen, DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad + keys.enckeylen, 1); #endif - ctx->cdata.keylen = keys.enckeylen; - - ret = aead_set_sh_desc(aead); - if (ret) { - dma_unmap_single(jrdev, ctx->key_dma, ctx->adata.keylen_pad + - keys.enckeylen, DMA_TO_DEVICE); - } - - return ret; + return aead_set_sh_desc(aead); badkey: crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; @@ -646,7 +578,6 @@ static int gcm_setkey(struct crypto_aead *aead, { struct caam_ctx *ctx = crypto_aead_ctx(aead); struct device *jrdev = ctx->jrdev; - int ret = 0; #ifdef DEBUG print_hex_dump(KERN_ERR, "key in @"__stringify(__LINE__)": ", @@ -654,21 +585,10 @@ static int gcm_setkey(struct crypto_aead *aead, #endif memcpy(ctx->key, key, keylen); - ctx->key_dma = dma_map_single(jrdev, ctx->key, keylen, - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->key_dma)) { - dev_err(jrdev, "unable to map key i/o memory\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, DMA_TO_DEVICE); ctx->cdata.keylen = keylen; - ret = gcm_set_sh_desc(aead); - if (ret) { - dma_unmap_single(jrdev, ctx->key_dma, ctx->cdata.keylen, - DMA_TO_DEVICE); - } - - return ret; + return gcm_set_sh_desc(aead); } static int rfc4106_setkey(struct crypto_aead *aead, @@ -676,7 +596,6 @@ static int rfc4106_setkey(struct crypto_aead *aead, { struct caam_ctx *ctx = crypto_aead_ctx(aead); struct device *jrdev = ctx->jrdev; - int ret = 0; if (keylen < 4) return -EINVAL; @@ -693,21 +612,9 @@ static int rfc4106_setkey(struct crypto_aead *aead, * in the nonce. Update the AES key length. */ ctx->cdata.keylen = keylen - 4; - - ctx->key_dma = dma_map_single(jrdev, ctx->key, ctx->cdata.keylen, - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->key_dma)) { - dev_err(jrdev, "unable to map key i/o memory\n"); - return -ENOMEM; - } - - ret = rfc4106_set_sh_desc(aead); - if (ret) { - dma_unmap_single(jrdev, ctx->key_dma, ctx->cdata.keylen, - DMA_TO_DEVICE); - } - - return ret; + dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->cdata.keylen, + DMA_TO_DEVICE); + return rfc4106_set_sh_desc(aead); } static int rfc4543_setkey(struct crypto_aead *aead, @@ -715,7 +622,6 @@ static int rfc4543_setkey(struct crypto_aead *aead, { struct caam_ctx *ctx = crypto_aead_ctx(aead); struct device *jrdev = ctx->jrdev; - int ret = 0; if (keylen < 4) return -EINVAL; @@ -732,21 +638,9 @@ static int rfc4543_setkey(struct crypto_aead *aead, * in the nonce. Update the AES key length. */ ctx->cdata.keylen = keylen - 4; - - ctx->key_dma = dma_map_single(jrdev, ctx->key, ctx->cdata.keylen, - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->key_dma)) { - dev_err(jrdev, "unable to map key i/o memory\n"); - return -ENOMEM; - } - - ret = rfc4543_set_sh_desc(aead); - if (ret) { - dma_unmap_single(jrdev, ctx->key_dma, ctx->cdata.keylen, - DMA_TO_DEVICE); - } - - return ret; + dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->cdata.keylen, + DMA_TO_DEVICE); + return rfc4543_set_sh_desc(aead); } static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher, @@ -787,12 +681,7 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher, keylen -= CTR_RFC3686_NONCE_SIZE; } - ctx->key_dma = dma_map_single(jrdev, ctx->key, keylen, - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->key_dma)) { - dev_err(jrdev, "unable to map key i/o memory\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, DMA_TO_DEVICE); ctx->cdata.keylen = keylen; ctx->cdata.key_virt = ctx->key; ctx->cdata.key_inline = true; @@ -801,37 +690,22 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher, desc = ctx->sh_desc_enc; cnstr_shdsc_ablkcipher_encap(desc, &ctx->cdata, ivsize, is_rfc3686, ctx1_iv_off); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* ablkcipher_decrypt shared descriptor */ desc = ctx->sh_desc_dec; cnstr_shdsc_ablkcipher_decap(desc, &ctx->cdata, ivsize, is_rfc3686, ctx1_iv_off); - ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* ablkcipher_givencrypt shared descriptor */ desc = ctx->sh_desc_givenc; cnstr_shdsc_ablkcipher_givencap(desc, &ctx->cdata, ivsize, is_rfc3686, ctx1_iv_off); - ctx->sh_desc_givenc_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_givenc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_givenc_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } @@ -851,11 +725,7 @@ static int xts_ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher, } memcpy(ctx->key, key, keylen); - ctx->key_dma = dma_map_single(jrdev, ctx->key, keylen, DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->key_dma)) { - dev_err(jrdev, "unable to map key i/o memory\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, DMA_TO_DEVICE); ctx->cdata.keylen = keylen; ctx->cdata.key_virt = ctx->key; ctx->cdata.key_inline = true; @@ -863,32 +733,22 @@ static int xts_ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher, /* xts_ablkcipher_encrypt shared descriptor */ desc = ctx->sh_desc_enc; cnstr_shdsc_xts_ablkcipher_encap(desc, &ctx->cdata); - ctx->sh_desc_enc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* xts_ablkcipher_decrypt shared descriptor */ desc = ctx->sh_desc_dec; cnstr_shdsc_xts_ablkcipher_decap(desc, &ctx->cdata); - ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { - dma_unmap_single(jrdev, ctx->sh_desc_enc_dma, - desc_bytes(ctx->sh_desc_enc), DMA_TO_DEVICE); - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } /* * aead_edesc - s/w-extended aead descriptor - * @src_nents: number of segments in input scatterlist - * @dst_nents: number of segments in output scatterlist + * @src_nents: number of segments in input s/w scatterlist + * @dst_nents: number of segments in output s/w scatterlist * @sec4_sg_bytes: length of dma mapped sec4_sg space * @sec4_sg_dma: bus physical mapped address of h/w link table * @sec4_sg: pointer to h/w link table @@ -905,8 +765,8 @@ struct aead_edesc { /* * ablkcipher_edesc - s/w-extended ablkcipher descriptor - * @src_nents: number of segments in input scatterlist - * @dst_nents: number of segments in output scatterlist + * @src_nents: number of segments in input s/w scatterlist + * @dst_nents: number of segments in output s/w scatterlist * @iv_dma: dma address of iv for checking continuity and link table * @sec4_sg_bytes: length of dma mapped sec4_sg space * @sec4_sg_dma: bus physical mapped address of h/w link table @@ -930,10 +790,11 @@ static void caam_unmap(struct device *dev, struct scatterlist *src, int sec4_sg_bytes) { if (dst != src) { - dma_unmap_sg(dev, src, src_nents ? : 1, DMA_TO_DEVICE); - dma_unmap_sg(dev, dst, dst_nents ? : 1, DMA_FROM_DEVICE); + if (src_nents) + dma_unmap_sg(dev, src, src_nents, DMA_TO_DEVICE); + dma_unmap_sg(dev, dst, dst_nents, DMA_FROM_DEVICE); } else { - dma_unmap_sg(dev, src, src_nents ? : 1, DMA_BIDIRECTIONAL); + dma_unmap_sg(dev, src, src_nents, DMA_BIDIRECTIONAL); } if (iv_dma) @@ -1102,7 +963,7 @@ static void init_aead_job(struct aead_request *req, init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE); if (all_contig) { - src_dma = sg_dma_address(req->src); + src_dma = edesc->src_nents ? sg_dma_address(req->src) : 0; in_options = 0; } else { src_dma = edesc->sec4_sg_dma; @@ -1117,7 +978,7 @@ static void init_aead_job(struct aead_request *req, out_options = in_options; if (unlikely(req->src != req->dst)) { - if (!edesc->dst_nents) { + if (edesc->dst_nents == 1) { dst_dma = sg_dma_address(req->dst); } else { dst_dma = edesc->sec4_sg_dma + @@ -1227,10 +1088,11 @@ static void init_ablkcipher_job(u32 *sh_desc, dma_addr_t ptr, print_hex_dump(KERN_ERR, "presciv@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, req->info, ivsize, 1); - printk(KERN_ERR "asked=%d, nbytes%d\n", (int)edesc->src_nents ? 100 : req->nbytes, req->nbytes); + pr_err("asked=%d, nbytes%d\n", + (int)edesc->src_nents > 1 ? 100 : req->nbytes, req->nbytes); dbg_dump_sg(KERN_ERR, "src @"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, req->src, - edesc->src_nents ? 100 : req->nbytes, 1); + edesc->src_nents > 1 ? 100 : req->nbytes, 1); #endif len = desc_len(sh_desc); @@ -1247,7 +1109,7 @@ static void init_ablkcipher_job(u32 *sh_desc, dma_addr_t ptr, append_seq_in_ptr(desc, src_dma, req->nbytes + ivsize, in_options); if (likely(req->src == req->dst)) { - if (!edesc->src_nents && iv_contig) { + if (edesc->src_nents == 1 && iv_contig) { dst_dma = sg_dma_address(req->src); } else { dst_dma = edesc->sec4_sg_dma + @@ -1255,7 +1117,7 @@ static void init_ablkcipher_job(u32 *sh_desc, dma_addr_t ptr, out_options = LDST_SGF; } } else { - if (!edesc->dst_nents) { + if (edesc->dst_nents == 1) { dst_dma = sg_dma_address(req->dst); } else { dst_dma = edesc->sec4_sg_dma + @@ -1287,13 +1149,13 @@ static void init_ablkcipher_giv_job(u32 *sh_desc, dma_addr_t ptr, ivsize, 1); dbg_dump_sg(KERN_ERR, "src @" __stringify(__LINE__) ": ", DUMP_PREFIX_ADDRESS, 16, 4, req->src, - edesc->src_nents ? 100 : req->nbytes, 1); + edesc->src_nents > 1 ? 100 : req->nbytes, 1); #endif len = desc_len(sh_desc); init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE); - if (!edesc->src_nents) { + if (edesc->src_nents == 1) { src_dma = sg_dma_address(req->src); in_options = 0; } else { @@ -1326,83 +1188,98 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - int src_nents, dst_nents = 0; + int src_nents, mapped_src_nents, dst_nents = 0, mapped_dst_nents = 0; struct aead_edesc *edesc; - int sgc; - bool all_contig = true; - int sec4_sg_index, sec4_sg_len = 0, sec4_sg_bytes; + int sec4_sg_index, sec4_sg_len, sec4_sg_bytes; unsigned int authsize = ctx->authsize; if (unlikely(req->dst != req->src)) { - src_nents = sg_count(req->src, req->assoclen + req->cryptlen); - dst_nents = sg_count(req->dst, - req->assoclen + req->cryptlen + - (encrypt ? authsize : (-authsize))); - } else { - src_nents = sg_count(req->src, - req->assoclen + req->cryptlen + - (encrypt ? authsize : 0)); - } - - /* Check if data are contiguous. */ - all_contig = !src_nents; - if (!all_contig) - sec4_sg_len = src_nents; - - sec4_sg_len += dst_nents; - - sec4_sg_bytes = sec4_sg_len * sizeof(struct sec4_sg_entry); + src_nents = sg_nents_for_len(req->src, req->assoclen + + req->cryptlen); + if (unlikely(src_nents < 0)) { + dev_err(jrdev, "Insufficient bytes (%d) in src S/G\n", + req->assoclen + req->cryptlen); + return ERR_PTR(src_nents); + } - /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kzalloc(sizeof(*edesc) + desc_bytes + sec4_sg_bytes, - GFP_DMA | flags); - if (!edesc) { - dev_err(jrdev, "could not allocate extended descriptor\n"); - return ERR_PTR(-ENOMEM); + dst_nents = sg_nents_for_len(req->dst, req->assoclen + + req->cryptlen + + (encrypt ? authsize : + (-authsize))); + if (unlikely(dst_nents < 0)) { + dev_err(jrdev, "Insufficient bytes (%d) in dst S/G\n", + req->assoclen + req->cryptlen + + (encrypt ? authsize : (-authsize))); + return ERR_PTR(dst_nents); + } + } else { + src_nents = sg_nents_for_len(req->src, req->assoclen + + req->cryptlen + + (encrypt ? authsize : 0)); + if (unlikely(src_nents < 0)) { + dev_err(jrdev, "Insufficient bytes (%d) in src S/G\n", + req->assoclen + req->cryptlen + + (encrypt ? authsize : 0)); + return ERR_PTR(src_nents); + } } if (likely(req->src == req->dst)) { - sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1, - DMA_BIDIRECTIONAL); - if (unlikely(!sgc)) { + mapped_src_nents = dma_map_sg(jrdev, req->src, src_nents, + DMA_BIDIRECTIONAL); + if (unlikely(!mapped_src_nents)) { dev_err(jrdev, "unable to map source\n"); - kfree(edesc); return ERR_PTR(-ENOMEM); } } else { - sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1, - DMA_TO_DEVICE); - if (unlikely(!sgc)) { - dev_err(jrdev, "unable to map source\n"); - kfree(edesc); - return ERR_PTR(-ENOMEM); + /* Cover also the case of null (zero length) input data */ + if (src_nents) { + mapped_src_nents = dma_map_sg(jrdev, req->src, + src_nents, DMA_TO_DEVICE); + if (unlikely(!mapped_src_nents)) { + dev_err(jrdev, "unable to map source\n"); + return ERR_PTR(-ENOMEM); + } + } else { + mapped_src_nents = 0; } - sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1, - DMA_FROM_DEVICE); - if (unlikely(!sgc)) { + mapped_dst_nents = dma_map_sg(jrdev, req->dst, dst_nents, + DMA_FROM_DEVICE); + if (unlikely(!mapped_dst_nents)) { dev_err(jrdev, "unable to map destination\n"); - dma_unmap_sg(jrdev, req->src, src_nents ? : 1, - DMA_TO_DEVICE); - kfree(edesc); + dma_unmap_sg(jrdev, req->src, src_nents, DMA_TO_DEVICE); return ERR_PTR(-ENOMEM); } } + sec4_sg_len = mapped_src_nents > 1 ? mapped_src_nents : 0; + sec4_sg_len += mapped_dst_nents > 1 ? mapped_dst_nents : 0; + sec4_sg_bytes = sec4_sg_len * sizeof(struct sec4_sg_entry); + + /* allocate space for base edesc and hw desc commands, link tables */ + edesc = kzalloc(sizeof(*edesc) + desc_bytes + sec4_sg_bytes, + GFP_DMA | flags); + if (!edesc) { + caam_unmap(jrdev, req->src, req->dst, src_nents, dst_nents, 0, + 0, 0, 0); + return ERR_PTR(-ENOMEM); + } + edesc->src_nents = src_nents; edesc->dst_nents = dst_nents; edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) + desc_bytes; - *all_contig_ptr = all_contig; + *all_contig_ptr = !(mapped_src_nents > 1); sec4_sg_index = 0; - if (!all_contig) { - sg_to_sec4_sg_last(req->src, src_nents, - edesc->sec4_sg + sec4_sg_index, 0); - sec4_sg_index += src_nents; + if (mapped_src_nents > 1) { + sg_to_sec4_sg_last(req->src, mapped_src_nents, + edesc->sec4_sg + sec4_sg_index, 0); + sec4_sg_index += mapped_src_nents; } - if (dst_nents) { - sg_to_sec4_sg_last(req->dst, dst_nents, + if (mapped_dst_nents > 1) { + sg_to_sec4_sg_last(req->dst, mapped_dst_nents, edesc->sec4_sg + sec4_sg_index, 0); } @@ -1600,40 +1477,49 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - int src_nents, dst_nents = 0, sec4_sg_bytes; + int src_nents, mapped_src_nents, dst_nents = 0, mapped_dst_nents = 0; struct ablkcipher_edesc *edesc; dma_addr_t iv_dma = 0; - bool iv_contig = false; - int sgc; + bool in_contig; int ivsize = crypto_ablkcipher_ivsize(ablkcipher); - int sec4_sg_index; + int dst_sg_idx, sec4_sg_ents, sec4_sg_bytes; - src_nents = sg_count(req->src, req->nbytes); + src_nents = sg_nents_for_len(req->src, req->nbytes); + if (unlikely(src_nents < 0)) { + dev_err(jrdev, "Insufficient bytes (%d) in src S/G\n", + req->nbytes); + return ERR_PTR(src_nents); + } - if (req->dst != req->src) - dst_nents = sg_count(req->dst, req->nbytes); + if (req->dst != req->src) { + dst_nents = sg_nents_for_len(req->dst, req->nbytes); + if (unlikely(dst_nents < 0)) { + dev_err(jrdev, "Insufficient bytes (%d) in dst S/G\n", + req->nbytes); + return ERR_PTR(dst_nents); + } + } if (likely(req->src == req->dst)) { - sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1, - DMA_BIDIRECTIONAL); - if (unlikely(!sgc)) { + mapped_src_nents = dma_map_sg(jrdev, req->src, src_nents, + DMA_BIDIRECTIONAL); + if (unlikely(!mapped_src_nents)) { dev_err(jrdev, "unable to map source\n"); return ERR_PTR(-ENOMEM); } } else { - sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1, - DMA_TO_DEVICE); - if (unlikely(!sgc)) { + mapped_src_nents = dma_map_sg(jrdev, req->src, src_nents, + DMA_TO_DEVICE); + if (unlikely(!mapped_src_nents)) { dev_err(jrdev, "unable to map source\n"); return ERR_PTR(-ENOMEM); } - sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1, - DMA_FROM_DEVICE); - if (unlikely(!sgc)) { + mapped_dst_nents = dma_map_sg(jrdev, req->dst, dst_nents, + DMA_FROM_DEVICE); + if (unlikely(!mapped_dst_nents)) { dev_err(jrdev, "unable to map destination\n"); - dma_unmap_sg(jrdev, req->src, src_nents ? : 1, - DMA_TO_DEVICE); + dma_unmap_sg(jrdev, req->src, src_nents, DMA_TO_DEVICE); return ERR_PTR(-ENOMEM); } } @@ -1646,16 +1532,17 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request return ERR_PTR(-ENOMEM); } - /* - * Check if iv can be contiguous with source and destination. - * If so, include it. If not, create scatterlist. - */ - if (!src_nents && iv_dma + ivsize == sg_dma_address(req->src)) - iv_contig = true; - else - src_nents = src_nents ? : 1; - sec4_sg_bytes = ((iv_contig ? 0 : 1) + src_nents + dst_nents) * - sizeof(struct sec4_sg_entry); + if (mapped_src_nents == 1 && + iv_dma + ivsize == sg_dma_address(req->src)) { + in_contig = true; + sec4_sg_ents = 0; + } else { + in_contig = false; + sec4_sg_ents = 1 + mapped_src_nents; + } + dst_sg_idx = sec4_sg_ents; + sec4_sg_ents += mapped_dst_nents > 1 ? mapped_dst_nents : 0; + sec4_sg_bytes = sec4_sg_ents * sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ edesc = kzalloc(sizeof(*edesc) + desc_bytes + sec4_sg_bytes, @@ -1673,17 +1560,15 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request edesc->sec4_sg = (void *)edesc + sizeof(struct ablkcipher_edesc) + desc_bytes; - sec4_sg_index = 0; - if (!iv_contig) { + if (!in_contig) { dma_to_sec4_sg_one(edesc->sec4_sg, iv_dma, ivsize, 0); - sg_to_sec4_sg_last(req->src, src_nents, + sg_to_sec4_sg_last(req->src, mapped_src_nents, edesc->sec4_sg + 1, 0); - sec4_sg_index += 1 + src_nents; } - if (dst_nents) { - sg_to_sec4_sg_last(req->dst, dst_nents, - edesc->sec4_sg + sec4_sg_index, 0); + if (mapped_dst_nents > 1) { + sg_to_sec4_sg_last(req->dst, mapped_dst_nents, + edesc->sec4_sg + dst_sg_idx, 0); } edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, @@ -1704,7 +1589,7 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request sec4_sg_bytes, 1); #endif - *iv_contig_out = iv_contig; + *iv_contig_out = in_contig; return edesc; } @@ -1798,40 +1683,50 @@ static struct ablkcipher_edesc *ablkcipher_giv_edesc_alloc( gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - int src_nents, dst_nents = 0, sec4_sg_bytes; + int src_nents, mapped_src_nents, dst_nents, mapped_dst_nents; struct ablkcipher_edesc *edesc; dma_addr_t iv_dma = 0; - bool iv_contig = false; - int sgc; + bool out_contig; int ivsize = crypto_ablkcipher_ivsize(ablkcipher); - int sec4_sg_index; - - src_nents = sg_count(req->src, req->nbytes); + int dst_sg_idx, sec4_sg_ents, sec4_sg_bytes; - if (unlikely(req->dst != req->src)) - dst_nents = sg_count(req->dst, req->nbytes); + src_nents = sg_nents_for_len(req->src, req->nbytes); + if (unlikely(src_nents < 0)) { + dev_err(jrdev, "Insufficient bytes (%d) in src S/G\n", + req->nbytes); + return ERR_PTR(src_nents); + } if (likely(req->src == req->dst)) { - sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1, - DMA_BIDIRECTIONAL); - if (unlikely(!sgc)) { + mapped_src_nents = dma_map_sg(jrdev, req->src, src_nents, + DMA_BIDIRECTIONAL); + if (unlikely(!mapped_src_nents)) { dev_err(jrdev, "unable to map source\n"); return ERR_PTR(-ENOMEM); } + + dst_nents = src_nents; + mapped_dst_nents = src_nents; } else { - sgc = dma_map_sg(jrdev, req->src, src_nents ? : 1, - DMA_TO_DEVICE); - if (unlikely(!sgc)) { + mapped_src_nents = dma_map_sg(jrdev, req->src, src_nents, + DMA_TO_DEVICE); + if (unlikely(!mapped_src_nents)) { dev_err(jrdev, "unable to map source\n"); return ERR_PTR(-ENOMEM); } - sgc = dma_map_sg(jrdev, req->dst, dst_nents ? : 1, - DMA_FROM_DEVICE); - if (unlikely(!sgc)) { + dst_nents = sg_nents_for_len(req->dst, req->nbytes); + if (unlikely(dst_nents < 0)) { + dev_err(jrdev, "Insufficient bytes (%d) in dst S/G\n", + req->nbytes); + return ERR_PTR(dst_nents); + } + + mapped_dst_nents = dma_map_sg(jrdev, req->dst, dst_nents, + DMA_FROM_DEVICE); + if (unlikely(!mapped_dst_nents)) { dev_err(jrdev, "unable to map destination\n"); - dma_unmap_sg(jrdev, req->src, src_nents ? : 1, - DMA_TO_DEVICE); + dma_unmap_sg(jrdev, req->src, src_nents, DMA_TO_DEVICE); return ERR_PTR(-ENOMEM); } } @@ -1848,14 +1743,18 @@ static struct ablkcipher_edesc *ablkcipher_giv_edesc_alloc( return ERR_PTR(-ENOMEM); } - if (!dst_nents && iv_dma + ivsize == sg_dma_address(req->dst)) - iv_contig = true; - else - dst_nents = dst_nents ? : 1; - sec4_sg_bytes = ((iv_contig ? 0 : 1) + src_nents + dst_nents) * - sizeof(struct sec4_sg_entry); + sec4_sg_ents = mapped_src_nents > 1 ? mapped_src_nents : 0; + dst_sg_idx = sec4_sg_ents; + if (mapped_dst_nents == 1 && + iv_dma + ivsize == sg_dma_address(req->dst)) { + out_contig = true; + } else { + out_contig = false; + sec4_sg_ents += 1 + mapped_dst_nents; + } /* allocate space for base edesc and hw desc commands, link tables */ + sec4_sg_bytes = sec4_sg_ents * sizeof(struct sec4_sg_entry); edesc = kzalloc(sizeof(*edesc) + desc_bytes + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { @@ -1871,18 +1770,15 @@ static struct ablkcipher_edesc *ablkcipher_giv_edesc_alloc( edesc->sec4_sg = (void *)edesc + sizeof(struct ablkcipher_edesc) + desc_bytes; - sec4_sg_index = 0; - if (src_nents) { - sg_to_sec4_sg_last(req->src, src_nents, edesc->sec4_sg, 0); - sec4_sg_index += src_nents; - } + if (mapped_src_nents > 1) + sg_to_sec4_sg_last(req->src, mapped_src_nents, edesc->sec4_sg, + 0); - if (!iv_contig) { - dma_to_sec4_sg_one(edesc->sec4_sg + sec4_sg_index, + if (!out_contig) { + dma_to_sec4_sg_one(edesc->sec4_sg + dst_sg_idx, iv_dma, ivsize, 0); - sec4_sg_index += 1; - sg_to_sec4_sg_last(req->dst, dst_nents, - edesc->sec4_sg + sec4_sg_index, 0); + sg_to_sec4_sg_last(req->dst, mapped_dst_nents, + edesc->sec4_sg + dst_sg_idx + 1, 0); } edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, @@ -1903,7 +1799,7 @@ static struct ablkcipher_edesc *ablkcipher_giv_edesc_alloc( sec4_sg_bytes, 1); #endif - *iv_contig_out = iv_contig; + *iv_contig_out = out_contig; return edesc; } @@ -1914,7 +1810,7 @@ static int ablkcipher_givencrypt(struct skcipher_givcrypt_request *creq) struct crypto_ablkcipher *ablkcipher = crypto_ablkcipher_reqtfm(req); struct caam_ctx *ctx = crypto_ablkcipher_ctx(ablkcipher); struct device *jrdev = ctx->jrdev; - bool iv_contig; + bool iv_contig = false; u32 *desc; int ret = 0; @@ -3355,12 +3251,31 @@ struct caam_crypto_alg { static int caam_init_common(struct caam_ctx *ctx, struct caam_alg_entry *caam) { + dma_addr_t dma_addr; + ctx->jrdev = caam_jr_alloc(); if (IS_ERR(ctx->jrdev)) { pr_err("Job Ring Device allocation for transform failed\n"); return PTR_ERR(ctx->jrdev); } + dma_addr = dma_map_single_attrs(ctx->jrdev, ctx->sh_desc_enc, + offsetof(struct caam_ctx, + sh_desc_enc_dma), + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(ctx->jrdev, dma_addr)) { + dev_err(ctx->jrdev, "unable to map key, shared descriptors\n"); + caam_jr_free(ctx->jrdev); + return -ENOMEM; + } + + ctx->sh_desc_enc_dma = dma_addr; + ctx->sh_desc_dec_dma = dma_addr + offsetof(struct caam_ctx, + sh_desc_dec); + ctx->sh_desc_givenc_dma = dma_addr + offsetof(struct caam_ctx, + sh_desc_givenc); + ctx->key_dma = dma_addr + offsetof(struct caam_ctx, key); + /* copy descriptor header template value */ ctx->cdata.algtype = OP_TYPE_CLASS1_ALG | caam->class1_alg_type; ctx->adata.algtype = OP_TYPE_CLASS2_ALG | caam->class2_alg_type; @@ -3390,25 +3305,9 @@ static int caam_aead_init(struct crypto_aead *tfm) static void caam_exit_common(struct caam_ctx *ctx) { - if (ctx->sh_desc_enc_dma && - !dma_mapping_error(ctx->jrdev, ctx->sh_desc_enc_dma)) - dma_unmap_single(ctx->jrdev, ctx->sh_desc_enc_dma, - desc_bytes(ctx->sh_desc_enc), DMA_TO_DEVICE); - if (ctx->sh_desc_dec_dma && - !dma_mapping_error(ctx->jrdev, ctx->sh_desc_dec_dma)) - dma_unmap_single(ctx->jrdev, ctx->sh_desc_dec_dma, - desc_bytes(ctx->sh_desc_dec), DMA_TO_DEVICE); - if (ctx->sh_desc_givenc_dma && - !dma_mapping_error(ctx->jrdev, ctx->sh_desc_givenc_dma)) - dma_unmap_single(ctx->jrdev, ctx->sh_desc_givenc_dma, - desc_bytes(ctx->sh_desc_givenc), - DMA_TO_DEVICE); - if (ctx->key_dma && - !dma_mapping_error(ctx->jrdev, ctx->key_dma)) - dma_unmap_single(ctx->jrdev, ctx->key_dma, - ctx->cdata.keylen + ctx->adata.keylen_pad, - DMA_TO_DEVICE); - + dma_unmap_single_attrs(ctx->jrdev, ctx->sh_desc_enc_dma, + offsetof(struct caam_ctx, sh_desc_enc_dma), + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); caam_jr_free(ctx->jrdev); } diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index e58639ea53b1..da4f94eab3da 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -109,7 +109,6 @@ struct caam_hash_ctx { dma_addr_t sh_desc_digest_dma; struct device *jrdev; u8 key[CAAM_MAX_HASH_KEY_SIZE]; - dma_addr_t key_dma; int ctx_len; struct alginfo adata; }; @@ -138,6 +137,31 @@ struct caam_export_state { int (*finup)(struct ahash_request *req); }; +static inline void switch_buf(struct caam_hash_state *state) +{ + state->current_buf ^= 1; +} + +static inline u8 *current_buf(struct caam_hash_state *state) +{ + return state->current_buf ? state->buf_1 : state->buf_0; +} + +static inline u8 *alt_buf(struct caam_hash_state *state) +{ + return state->current_buf ? state->buf_0 : state->buf_1; +} + +static inline int *current_buflen(struct caam_hash_state *state) +{ + return state->current_buf ? &state->buflen_1 : &state->buflen_0; +} + +static inline int *alt_buflen(struct caam_hash_state *state) +{ + return state->current_buf ? &state->buflen_0 : &state->buflen_1; +} + /* Common job descriptor seq in/out ptr routines */ /* Map state->caam_ctx, and append seq_out_ptr command that points to it */ @@ -149,6 +173,7 @@ static inline int map_seq_out_ptr_ctx(u32 *desc, struct device *jrdev, ctx_len, DMA_FROM_DEVICE); if (dma_mapping_error(jrdev, state->ctx_dma)) { dev_err(jrdev, "unable to map ctx\n"); + state->ctx_dma = 0; return -ENOMEM; } @@ -169,36 +194,27 @@ static inline dma_addr_t map_seq_out_ptr_result(u32 *desc, struct device *jrdev, return dst_dma; } -/* Map current buffer in state and put it in link table */ -static inline dma_addr_t buf_map_to_sec4_sg(struct device *jrdev, - struct sec4_sg_entry *sec4_sg, - u8 *buf, int buflen) +/* Map current buffer in state (if length > 0) and put it in link table */ +static inline int buf_map_to_sec4_sg(struct device *jrdev, + struct sec4_sg_entry *sec4_sg, + struct caam_hash_state *state) { - dma_addr_t buf_dma; + int buflen = *current_buflen(state); - buf_dma = dma_map_single(jrdev, buf, buflen, DMA_TO_DEVICE); - dma_to_sec4_sg_one(sec4_sg, buf_dma, buflen, 0); + if (!buflen) + return 0; - return buf_dma; -} + state->buf_dma = dma_map_single(jrdev, current_buf(state), buflen, + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, state->buf_dma)) { + dev_err(jrdev, "unable to map buf\n"); + state->buf_dma = 0; + return -ENOMEM; + } -/* - * Only put buffer in link table if it contains data, which is possible, - * since a buffer has previously been used, and needs to be unmapped, - */ -static inline dma_addr_t -try_buf_map_to_sec4_sg(struct device *jrdev, struct sec4_sg_entry *sec4_sg, - u8 *buf, dma_addr_t buf_dma, int buflen, - int last_buflen) -{ - if (buf_dma && !dma_mapping_error(jrdev, buf_dma)) - dma_unmap_single(jrdev, buf_dma, last_buflen, DMA_TO_DEVICE); - if (buflen) - buf_dma = buf_map_to_sec4_sg(jrdev, sec4_sg, buf, buflen); - else - buf_dma = 0; - - return buf_dma; + dma_to_sec4_sg_one(sec4_sg, state->buf_dma, buflen, 0); + + return 0; } /* Map state->caam_ctx, and add it to link table */ @@ -209,6 +225,7 @@ static inline int ctx_map_to_sec4_sg(u32 *desc, struct device *jrdev, state->ctx_dma = dma_map_single(jrdev, state->caam_ctx, ctx_len, flag); if (dma_mapping_error(jrdev, state->ctx_dma)) { dev_err(jrdev, "unable to map ctx\n"); + state->ctx_dma = 0; return -ENOMEM; } @@ -277,12 +294,8 @@ static int ahash_set_sh_desc(struct crypto_ahash *ahash) /* ahash_update shared descriptor */ desc = ctx->sh_desc_update; ahash_gen_sh_desc(desc, OP_ALG_AS_UPDATE, ctx->ctx_len, ctx, true); - ctx->sh_desc_update_dma = dma_map_single(jrdev, desc, desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_update_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_update_dma, + desc_bytes(desc), DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "ahash update shdesc@"__stringify(__LINE__)": ", @@ -292,13 +305,8 @@ static int ahash_set_sh_desc(struct crypto_ahash *ahash) /* ahash_update_first shared descriptor */ desc = ctx->sh_desc_update_first; ahash_gen_sh_desc(desc, OP_ALG_AS_INIT, ctx->ctx_len, ctx, false); - ctx->sh_desc_update_first_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_update_first_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_update_first_dma, + desc_bytes(desc), DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "ahash update first shdesc@"__stringify(__LINE__)": ", @@ -308,12 +316,8 @@ static int ahash_set_sh_desc(struct crypto_ahash *ahash) /* ahash_final shared descriptor */ desc = ctx->sh_desc_fin; ahash_gen_sh_desc(desc, OP_ALG_AS_FINALIZE, digestsize, ctx, true); - ctx->sh_desc_fin_dma = dma_map_single(jrdev, desc, desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_fin_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_fin_dma, + desc_bytes(desc), DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "ahash final shdesc@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, @@ -323,13 +327,8 @@ static int ahash_set_sh_desc(struct crypto_ahash *ahash) /* ahash_digest shared descriptor */ desc = ctx->sh_desc_digest; ahash_gen_sh_desc(desc, OP_ALG_AS_INITFINAL, digestsize, ctx, false); - ctx->sh_desc_digest_dma = dma_map_single(jrdev, desc, - desc_bytes(desc), - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_digest_dma)) { - dev_err(jrdev, "unable to map shared descriptor\n"); - return -ENOMEM; - } + dma_sync_single_for_device(jrdev, ctx->sh_desc_digest_dma, + desc_bytes(desc), DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "ahash digest shdesc@"__stringify(__LINE__)": ", @@ -420,7 +419,6 @@ static int ahash_setkey(struct crypto_ahash *ahash, const u8 *key, unsigned int keylen) { struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); - struct device *jrdev = ctx->jrdev; int blocksize = crypto_tfm_alg_blocksize(&ahash->base); int digestsize = crypto_ahash_digestsize(ahash); int ret; @@ -448,28 +446,14 @@ static int ahash_setkey(struct crypto_ahash *ahash, if (ret) goto bad_free_key; - ctx->key_dma = dma_map_single(jrdev, ctx->key, ctx->adata.keylen_pad, - DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->key_dma)) { - dev_err(jrdev, "unable to map key i/o memory\n"); - ret = -ENOMEM; - goto error_free_key; - } #ifdef DEBUG print_hex_dump(KERN_ERR, "ctx.key@"__stringify(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->adata.keylen_pad, 1); #endif - ret = ahash_set_sh_desc(ahash); - if (ret) { - dma_unmap_single(jrdev, ctx->key_dma, ctx->adata.keylen_pad, - DMA_TO_DEVICE); - } - - error_free_key: kfree(hashed_key); - return ret; + return ahash_set_sh_desc(ahash); bad_free_key: kfree(hashed_key); crypto_ahash_set_flags(ahash, CRYPTO_TFM_RES_BAD_KEY_LEN); @@ -498,6 +482,8 @@ static inline void ahash_unmap(struct device *dev, struct ahash_edesc *edesc, struct ahash_request *req, int dst_len) { + struct caam_hash_state *state = ahash_request_ctx(req); + if (edesc->src_nents) dma_unmap_sg(dev, req->src, edesc->src_nents, DMA_TO_DEVICE); if (edesc->dst_dma) @@ -506,6 +492,12 @@ static inline void ahash_unmap(struct device *dev, if (edesc->sec4_sg_bytes) dma_unmap_single(dev, edesc->sec4_sg_dma, edesc->sec4_sg_bytes, DMA_TO_DEVICE); + + if (state->buf_dma) { + dma_unmap_single(dev, state->buf_dma, *current_buflen(state), + DMA_TO_DEVICE); + state->buf_dma = 0; + } } static inline void ahash_unmap_ctx(struct device *dev, @@ -516,8 +508,10 @@ static inline void ahash_unmap_ctx(struct device *dev, struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); struct caam_hash_state *state = ahash_request_ctx(req); - if (state->ctx_dma) + if (state->ctx_dma) { dma_unmap_single(dev, state->ctx_dma, ctx->ctx_len, flag); + state->ctx_dma = 0; + } ahash_unmap(dev, edesc, req, dst_len); } @@ -562,8 +556,8 @@ static void ahash_done_bi(struct device *jrdev, u32 *desc, u32 err, struct ahash_edesc *edesc; struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); -#ifdef DEBUG struct caam_hash_state *state = ahash_request_ctx(req); +#ifdef DEBUG int digestsize = crypto_ahash_digestsize(ahash); dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err); @@ -574,6 +568,7 @@ static void ahash_done_bi(struct device *jrdev, u32 *desc, u32 err, caam_jr_strstatus(jrdev, err); ahash_unmap_ctx(jrdev, edesc, req, ctx->ctx_len, DMA_BIDIRECTIONAL); + switch_buf(state); kfree(edesc); #ifdef DEBUG @@ -630,8 +625,8 @@ static void ahash_done_ctx_dst(struct device *jrdev, u32 *desc, u32 err, struct ahash_edesc *edesc; struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); -#ifdef DEBUG struct caam_hash_state *state = ahash_request_ctx(req); +#ifdef DEBUG int digestsize = crypto_ahash_digestsize(ahash); dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err); @@ -642,6 +637,7 @@ static void ahash_done_ctx_dst(struct device *jrdev, u32 *desc, u32 err, caam_jr_strstatus(jrdev, err); ahash_unmap_ctx(jrdev, edesc, req, ctx->ctx_len, DMA_FROM_DEVICE); + switch_buf(state); kfree(edesc); #ifdef DEBUG @@ -725,11 +721,10 @@ static int ahash_update_ctx(struct ahash_request *req) struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - u8 *buf = state->current_buf ? state->buf_1 : state->buf_0; - int *buflen = state->current_buf ? &state->buflen_1 : &state->buflen_0; - u8 *next_buf = state->current_buf ? state->buf_0 : state->buf_1; - int *next_buflen = state->current_buf ? &state->buflen_0 : - &state->buflen_1, last_buflen; + u8 *buf = current_buf(state); + int *buflen = current_buflen(state); + u8 *next_buf = alt_buf(state); + int *next_buflen = alt_buflen(state), last_buflen; int in_len = *buflen + req->nbytes, to_hash; u32 *desc; int src_nents, mapped_nents, sec4_sg_bytes, sec4_sg_src_index; @@ -783,10 +778,9 @@ static int ahash_update_ctx(struct ahash_request *req) if (ret) goto unmap_ctx; - state->buf_dma = try_buf_map_to_sec4_sg(jrdev, - edesc->sec4_sg + 1, - buf, state->buf_dma, - *buflen, last_buflen); + ret = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, state); + if (ret) + goto unmap_ctx; if (mapped_nents) { sg_to_sec4_sg_last(req->src, mapped_nents, @@ -801,8 +795,6 @@ static int ahash_update_ctx(struct ahash_request *req) cpu_to_caam32(SEC4_SG_LEN_FIN); } - state->current_buf = !state->current_buf; - desc = edesc->hw_desc; edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, @@ -859,10 +851,7 @@ static int ahash_final_ctx(struct ahash_request *req) struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - u8 *buf = state->current_buf ? state->buf_1 : state->buf_0; - int buflen = state->current_buf ? state->buflen_1 : state->buflen_0; - int last_buflen = state->current_buf ? state->buflen_0 : - state->buflen_1; + int buflen = *current_buflen(state); u32 *desc; int sec4_sg_bytes, sec4_sg_src_index; int digestsize = crypto_ahash_digestsize(ahash); @@ -889,9 +878,10 @@ static int ahash_final_ctx(struct ahash_request *req) if (ret) goto unmap_ctx; - state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, - buf, state->buf_dma, buflen, - last_buflen); + ret = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, state); + if (ret) + goto unmap_ctx; + (edesc->sec4_sg + sec4_sg_src_index - 1)->len |= cpu_to_caam32(SEC4_SG_LEN_FIN); @@ -938,10 +928,7 @@ static int ahash_finup_ctx(struct ahash_request *req) struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - u8 *buf = state->current_buf ? state->buf_1 : state->buf_0; - int buflen = state->current_buf ? state->buflen_1 : state->buflen_0; - int last_buflen = state->current_buf ? state->buflen_0 : - state->buflen_1; + int buflen = *current_buflen(state); u32 *desc; int sec4_sg_src_index; int src_nents, mapped_nents; @@ -986,9 +973,9 @@ static int ahash_finup_ctx(struct ahash_request *req) if (ret) goto unmap_ctx; - state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, - buf, state->buf_dma, buflen, - last_buflen); + ret = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, state); + if (ret) + goto unmap_ctx; ret = ahash_edesc_add_src(ctx, edesc, req, mapped_nents, sec4_sg_src_index, ctx->ctx_len + buflen, @@ -1024,6 +1011,7 @@ static int ahash_digest(struct ahash_request *req) { struct crypto_ahash *ahash = crypto_ahash_reqtfm(req); struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); + struct caam_hash_state *state = ahash_request_ctx(req); struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; @@ -1033,6 +1021,8 @@ static int ahash_digest(struct ahash_request *req) struct ahash_edesc *edesc; int ret; + state->buf_dma = 0; + src_nents = sg_nents_for_len(req->src, req->nbytes); if (src_nents < 0) { dev_err(jrdev, "Invalid number of src SG.\n"); @@ -1105,8 +1095,8 @@ static int ahash_final_no_ctx(struct ahash_request *req) struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - u8 *buf = state->current_buf ? state->buf_1 : state->buf_0; - int buflen = state->current_buf ? state->buflen_1 : state->buflen_0; + u8 *buf = current_buf(state); + int buflen = *current_buflen(state); u32 *desc; int digestsize = crypto_ahash_digestsize(ahash); struct ahash_edesc *edesc; @@ -1166,11 +1156,10 @@ static int ahash_update_no_ctx(struct ahash_request *req) struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - u8 *buf = state->current_buf ? state->buf_1 : state->buf_0; - int *buflen = state->current_buf ? &state->buflen_1 : &state->buflen_0; - u8 *next_buf = state->current_buf ? state->buf_0 : state->buf_1; - int *next_buflen = state->current_buf ? &state->buflen_0 : - &state->buflen_1; + u8 *buf = current_buf(state); + int *buflen = current_buflen(state); + u8 *next_buf = alt_buf(state); + int *next_buflen = alt_buflen(state); int in_len = *buflen + req->nbytes, to_hash; int sec4_sg_bytes, src_nents, mapped_nents; struct ahash_edesc *edesc; @@ -1219,8 +1208,10 @@ static int ahash_update_no_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->dst_dma = 0; - state->buf_dma = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, - buf, *buflen); + ret = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, state); + if (ret) + goto unmap_ctx; + sg_to_sec4_sg_last(req->src, mapped_nents, edesc->sec4_sg + 1, 0); @@ -1230,8 +1221,6 @@ static int ahash_update_no_ctx(struct ahash_request *req) *next_buflen, 0); } - state->current_buf = !state->current_buf; - desc = edesc->hw_desc; edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, @@ -1293,10 +1282,7 @@ static int ahash_finup_no_ctx(struct ahash_request *req) struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - u8 *buf = state->current_buf ? state->buf_1 : state->buf_0; - int buflen = state->current_buf ? state->buflen_1 : state->buflen_0; - int last_buflen = state->current_buf ? state->buflen_0 : - state->buflen_1; + int buflen = *current_buflen(state); u32 *desc; int sec4_sg_bytes, sec4_sg_src_index, src_nents, mapped_nents; int digestsize = crypto_ahash_digestsize(ahash); @@ -1338,9 +1324,9 @@ static int ahash_finup_no_ctx(struct ahash_request *req) edesc->src_nents = src_nents; edesc->sec4_sg_bytes = sec4_sg_bytes; - state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, buf, - state->buf_dma, buflen, - last_buflen); + ret = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, state); + if (ret) + goto unmap; ret = ahash_edesc_add_src(ctx, edesc, req, mapped_nents, 1, buflen, req->nbytes); @@ -1386,9 +1372,8 @@ static int ahash_update_first(struct ahash_request *req) struct device *jrdev = ctx->jrdev; gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC; - u8 *next_buf = state->current_buf ? state->buf_1 : state->buf_0; - int *next_buflen = state->current_buf ? - &state->buflen_1 : &state->buflen_0; + u8 *next_buf = alt_buf(state); + int *next_buflen = alt_buflen(state); int to_hash; u32 *desc; int src_nents, mapped_nents; @@ -1470,6 +1455,7 @@ static int ahash_update_first(struct ahash_request *req) state->final = ahash_final_no_ctx; scatterwalk_map_and_copy(next_buf, req->src, 0, req->nbytes, 0); + switch_buf(state); } #ifdef DEBUG print_hex_dump(KERN_ERR, "next buf@"__stringify(__LINE__)": ", @@ -1497,6 +1483,7 @@ static int ahash_init(struct ahash_request *req) state->finup = ahash_finup_first; state->final = ahash_final_no_ctx; + state->ctx_dma = 0; state->current_buf = 0; state->buf_dma = 0; state->buflen_0 = 0; @@ -1732,6 +1719,7 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm) HASH_MSG_LEN + SHA256_DIGEST_SIZE, HASH_MSG_LEN + 64, HASH_MSG_LEN + SHA512_DIGEST_SIZE }; + dma_addr_t dma_addr; /* * Get a Job ring from Job Ring driver to ensure in-order @@ -1742,6 +1730,26 @@ static int caam_hash_cra_init(struct crypto_tfm *tfm) pr_err("Job Ring Device allocation for transform failed\n"); return PTR_ERR(ctx->jrdev); } + + dma_addr = dma_map_single_attrs(ctx->jrdev, ctx->sh_desc_update, + offsetof(struct caam_hash_ctx, + sh_desc_update_dma), + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(ctx->jrdev, dma_addr)) { + dev_err(ctx->jrdev, "unable to map shared descriptors\n"); + caam_jr_free(ctx->jrdev); + return -ENOMEM; + } + + ctx->sh_desc_update_dma = dma_addr; + ctx->sh_desc_update_first_dma = dma_addr + + offsetof(struct caam_hash_ctx, + sh_desc_update_first); + ctx->sh_desc_fin_dma = dma_addr + offsetof(struct caam_hash_ctx, + sh_desc_fin); + ctx->sh_desc_digest_dma = dma_addr + offsetof(struct caam_hash_ctx, + sh_desc_digest); + /* copy descriptor header template value */ ctx->adata.algtype = OP_TYPE_CLASS2_ALG | caam_hash->alg_type; @@ -1758,26 +1766,10 @@ static void caam_hash_cra_exit(struct crypto_tfm *tfm) { struct caam_hash_ctx *ctx = crypto_tfm_ctx(tfm); - if (ctx->sh_desc_update_dma && - !dma_mapping_error(ctx->jrdev, ctx->sh_desc_update_dma)) - dma_unmap_single(ctx->jrdev, ctx->sh_desc_update_dma, - desc_bytes(ctx->sh_desc_update), - DMA_TO_DEVICE); - if (ctx->sh_desc_update_first_dma && - !dma_mapping_error(ctx->jrdev, ctx->sh_desc_update_first_dma)) - dma_unmap_single(ctx->jrdev, ctx->sh_desc_update_first_dma, - desc_bytes(ctx->sh_desc_update_first), - DMA_TO_DEVICE); - if (ctx->sh_desc_fin_dma && - !dma_mapping_error(ctx->jrdev, ctx->sh_desc_fin_dma)) - dma_unmap_single(ctx->jrdev, ctx->sh_desc_fin_dma, - desc_bytes(ctx->sh_desc_fin), DMA_TO_DEVICE); - if (ctx->sh_desc_digest_dma && - !dma_mapping_error(ctx->jrdev, ctx->sh_desc_digest_dma)) - dma_unmap_single(ctx->jrdev, ctx->sh_desc_digest_dma, - desc_bytes(ctx->sh_desc_digest), - DMA_TO_DEVICE); - + dma_unmap_single_attrs(ctx->jrdev, ctx->sh_desc_update_dma, + offsetof(struct caam_hash_ctx, + sh_desc_update_dma), + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); caam_jr_free(ctx->jrdev); } diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 755109841cfd..fef39f9f41ee 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -13,7 +13,6 @@ #include "intern.h" #include "jr.h" #include "desc_constr.h" -#include "error.h" #include "ctrl.h" bool caam_little_end; @@ -270,7 +269,7 @@ static int deinstantiate_rng(struct device *ctrldev, int state_handle_mask) /* * If the corresponding bit is set, then it means the state * handle was initialized by us, and thus it needs to be - * deintialized as well + * deinitialized as well */ if ((1 << sh_idx) & state_handle_mask) { /* @@ -309,10 +308,8 @@ static int caam_remove(struct platform_device *pdev) ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; /* Remove platform devices for JobRs */ - for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) { - if (ctrlpriv->jrpdev[ring]) - of_device_unregister(ctrlpriv->jrpdev[ring]); - } + for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) + of_device_unregister(ctrlpriv->jrpdev[ring]); /* De-initialize RNG state handles initialized by this driver. */ if (ctrlpriv->rng4_sh_init) @@ -424,7 +421,7 @@ DEFINE_SIMPLE_ATTRIBUTE(caam_fops_u64_ro, caam_debugfs_u64_get, NULL, "%llu\n"); /* Probe routine for CAAM top (controller) level */ static int caam_probe(struct platform_device *pdev) { - int ret, ring, rspec, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN; + int ret, ring, ridx, rspec, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN; u64 caam_id; struct device *dev; struct device_node *nprop, *np; @@ -587,13 +584,18 @@ static int caam_probe(struct platform_device *pdev) JRSTART_JR1_START | JRSTART_JR2_START | JRSTART_JR3_START); - if (sizeof(dma_addr_t) == sizeof(u64)) + if (sizeof(dma_addr_t) == sizeof(u64)) { if (of_device_is_compatible(nprop, "fsl,sec-v5.0")) - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); else - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); - else - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + } else { + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + } + if (ret) { + dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret); + goto iounmap_ctrl; + } /* * Detect and enable JobRs @@ -614,6 +616,7 @@ static int caam_probe(struct platform_device *pdev) } ring = 0; + ridx = 0; ctrlpriv->total_jobrs = 0; for_each_available_child_of_node(nprop, np) if (of_device_is_compatible(np, "fsl,sec-v4.0-job-ring") || @@ -621,17 +624,19 @@ static int caam_probe(struct platform_device *pdev) ctrlpriv->jrpdev[ring] = of_platform_device_create(np, NULL, dev); if (!ctrlpriv->jrpdev[ring]) { - pr_warn("JR%d Platform device creation error\n", - ring); + pr_warn("JR physical index %d: Platform device creation error\n", + ridx); + ridx++; continue; } ctrlpriv->jr[ring] = (struct caam_job_ring __iomem __force *) ((__force uint8_t *)ctrl + - (ring + JR_BLOCK_NUMBER) * + (ridx + JR_BLOCK_NUMBER) * BLOCK_OFFSET ); ctrlpriv->total_jobrs++; ring++; + ridx++; } /* Check to see if QI present. If so, enable */ diff --git a/drivers/crypto/caam/error.c b/drivers/crypto/caam/error.c index 79a0cc70717f..6f44ccb55c63 100644 --- a/drivers/crypto/caam/error.c +++ b/drivers/crypto/caam/error.c @@ -6,9 +6,7 @@ #include "compat.h" #include "regs.h" -#include "intern.h" #include "desc.h" -#include "jr.h" #include "error.h" static const struct { diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index c8604dfadbf5..27631000b9f8 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -498,13 +498,22 @@ static int caam_jr_probe(struct platform_device *pdev) jrpriv->rregs = (struct caam_job_ring __iomem __force *)ctrl; - if (sizeof(dma_addr_t) == sizeof(u64)) + if (sizeof(dma_addr_t) == sizeof(u64)) { if (of_device_is_compatible(nprop, "fsl,sec-v5.0-job-ring")) - dma_set_mask_and_coherent(jrdev, DMA_BIT_MASK(40)); + error = dma_set_mask_and_coherent(jrdev, + DMA_BIT_MASK(40)); else - dma_set_mask_and_coherent(jrdev, DMA_BIT_MASK(36)); - else - dma_set_mask_and_coherent(jrdev, DMA_BIT_MASK(32)); + error = dma_set_mask_and_coherent(jrdev, + DMA_BIT_MASK(36)); + } else { + error = dma_set_mask_and_coherent(jrdev, DMA_BIT_MASK(32)); + } + if (error) { + dev_err(jrdev, "dma_set_mask_and_coherent failed (%d)\n", + error); + iounmap(ctrl); + return error; + } /* Identify the interrupt */ jrpriv->irq = irq_of_parse_and_map(nprop, 0); diff --git a/drivers/crypto/caam/sg_sw_sec4.h b/drivers/crypto/caam/sg_sw_sec4.h index 6afa20c4a013..c6adad09c972 100644 --- a/drivers/crypto/caam/sg_sw_sec4.h +++ b/drivers/crypto/caam/sg_sw_sec4.h @@ -73,14 +73,3 @@ static inline struct sec4_sg_entry *sg_to_sec4_sg_len( } while (total); return sec4_sg_ptr - 1; } - -/* derive number of elements in scatterlist, but return 0 for 1 */ -static inline int sg_count(struct scatterlist *sg_list, int nbytes) -{ - int sg_nents = sg_nents_for_len(sg_list, nbytes); - - if (likely(sg_nents == 1)) - return 0; - - return sg_nents; -} diff --git a/drivers/crypto/cavium/cpt/Kconfig b/drivers/crypto/cavium/cpt/Kconfig new file mode 100644 index 000000000000..cbd51b1aa046 --- /dev/null +++ b/drivers/crypto/cavium/cpt/Kconfig @@ -0,0 +1,17 @@ +# +# Cavium crypto device configuration +# + +config CRYPTO_DEV_CPT + tristate + +config CAVIUM_CPT + tristate "Cavium Cryptographic Accelerator driver" + depends on ARCH_THUNDER || COMPILE_TEST + depends on PCI_MSI && 64BIT + select CRYPTO_DEV_CPT + help + Support for Cavium CPT block found in octeon-tx series of + processors. + + To compile this as a module, choose M here. diff --git a/drivers/crypto/cavium/cpt/Makefile b/drivers/crypto/cavium/cpt/Makefile new file mode 100644 index 000000000000..dbf055e14622 --- /dev/null +++ b/drivers/crypto/cavium/cpt/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_CAVIUM_CPT) += cptpf.o cptvf.o +cptpf-objs := cptpf_main.o cptpf_mbox.o +cptvf-objs := cptvf_main.o cptvf_reqmanager.o cptvf_mbox.o cptvf_algs.o diff --git a/drivers/crypto/cavium/cpt/cpt_common.h b/drivers/crypto/cavium/cpt/cpt_common.h new file mode 100644 index 000000000000..225078d03773 --- /dev/null +++ b/drivers/crypto/cavium/cpt/cpt_common.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef __CPT_COMMON_H +#define __CPT_COMMON_H + +#include <asm/byteorder.h> +#include <linux/delay.h> +#include <linux/pci.h> + +#include "cpt_hw_types.h" + +/* Device ID */ +#define CPT_81XX_PCI_PF_DEVICE_ID 0xa040 +#define CPT_81XX_PCI_VF_DEVICE_ID 0xa041 + +/* flags to indicate the features supported */ +#define CPT_FLAG_SRIOV_ENABLED BIT(1) +#define CPT_FLAG_VF_DRIVER BIT(2) +#define CPT_FLAG_DEVICE_READY BIT(3) + +#define cpt_sriov_enabled(cpt) ((cpt)->flags & CPT_FLAG_SRIOV_ENABLED) +#define cpt_vf_driver(cpt) ((cpt)->flags & CPT_FLAG_VF_DRIVER) +#define cpt_device_ready(cpt) ((cpt)->flags & CPT_FLAG_DEVICE_READY) + +#define CPT_MBOX_MSG_TYPE_ACK 1 +#define CPT_MBOX_MSG_TYPE_NACK 2 +#define CPT_MBOX_MSG_TIMEOUT 2000 +#define VF_STATE_DOWN 0 +#define VF_STATE_UP 1 + +/* + * CPT Registers map for 81xx + */ + +/* PF registers */ +#define CPTX_PF_CONSTANTS(a) (0x0ll + ((u64)(a) << 36)) +#define CPTX_PF_RESET(a) (0x100ll + ((u64)(a) << 36)) +#define CPTX_PF_DIAG(a) (0x120ll + ((u64)(a) << 36)) +#define CPTX_PF_BIST_STATUS(a) (0x160ll + ((u64)(a) << 36)) +#define CPTX_PF_ECC0_CTL(a) (0x200ll + ((u64)(a) << 36)) +#define CPTX_PF_ECC0_FLIP(a) (0x210ll + ((u64)(a) << 36)) +#define CPTX_PF_ECC0_INT(a) (0x220ll + ((u64)(a) << 36)) +#define CPTX_PF_ECC0_INT_W1S(a) (0x230ll + ((u64)(a) << 36)) +#define CPTX_PF_ECC0_ENA_W1S(a) (0x240ll + ((u64)(a) << 36)) +#define CPTX_PF_ECC0_ENA_W1C(a) (0x250ll + ((u64)(a) << 36)) +#define CPTX_PF_MBOX_INTX(a, b) \ + (0x400ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_MBOX_INT_W1SX(a, b) \ + (0x420ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_MBOX_ENA_W1CX(a, b) \ + (0x440ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_MBOX_ENA_W1SX(a, b) \ + (0x460ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_EXEC_INT(a) (0x500ll + 0x1000000000ll * ((a) & 0x1)) +#define CPTX_PF_EXEC_INT_W1S(a) (0x520ll + ((u64)(a) << 36)) +#define CPTX_PF_EXEC_ENA_W1C(a) (0x540ll + ((u64)(a) << 36)) +#define CPTX_PF_EXEC_ENA_W1S(a) (0x560ll + ((u64)(a) << 36)) +#define CPTX_PF_GX_EN(a, b) \ + (0x600ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_EXEC_INFO(a) (0x700ll + ((u64)(a) << 36)) +#define CPTX_PF_EXEC_BUSY(a) (0x800ll + ((u64)(a) << 36)) +#define CPTX_PF_EXEC_INFO0(a) (0x900ll + ((u64)(a) << 36)) +#define CPTX_PF_EXEC_INFO1(a) (0x910ll + ((u64)(a) << 36)) +#define CPTX_PF_INST_REQ_PC(a) (0x10000ll + ((u64)(a) << 36)) +#define CPTX_PF_INST_LATENCY_PC(a) \ + (0x10020ll + ((u64)(a) << 36)) +#define CPTX_PF_RD_REQ_PC(a) (0x10040ll + ((u64)(a) << 36)) +#define CPTX_PF_RD_LATENCY_PC(a) (0x10060ll + ((u64)(a) << 36)) +#define CPTX_PF_RD_UC_PC(a) (0x10080ll + ((u64)(a) << 36)) +#define CPTX_PF_ACTIVE_CYCLES_PC(a) (0x10100ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_CTL(a) (0x4000000ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_STATUS(a) (0x4000008ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_CLK(a) (0x4000010ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_DBG_CTL(a) (0x4000018ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_DBG_DATA(a) (0x4000020ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_BIST_STATUS(a) (0x4000028ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_REQ_TIMER(a) (0x4000030ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_MEM_CTL(a) (0x4000038ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_PERF_CTL(a) (0x4001000ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_DBG_CNTX(a, b) \ + (0x4001100ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_EXE_PERF_EVENT_CNT(a) (0x4001180ll + ((u64)(a) << 36)) +#define CPTX_PF_EXE_EPCI_INBX_CNT(a, b) \ + (0x4001200ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_EXE_EPCI_OUTBX_CNT(a, b) \ + (0x4001240ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_ENGX_UCODE_BASE(a, b) \ + (0x4002000ll + ((u64)(a) << 36) + ((b) << 3)) +#define CPTX_PF_QX_CTL(a, b) \ + (0x8000000ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_PF_QX_GMCTL(a, b) \ + (0x8000020ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_PF_QX_CTL2(a, b) \ + (0x8000100ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_PF_VFX_MBOXX(a, b, c) \ + (0x8001000ll + ((u64)(a) << 36) + ((b) << 20) + ((c) << 8)) + +/* VF registers */ +#define CPTX_VQX_CTL(a, b) (0x100ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_SADDR(a, b) (0x200ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DONE_WAIT(a, b) (0x400ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_INPROG(a, b) (0x410ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DONE(a, b) (0x420ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DONE_ACK(a, b) (0x440ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DONE_INT_W1S(a, b) (0x460ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DONE_INT_W1C(a, b) (0x468ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DONE_ENA_W1S(a, b) (0x470ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DONE_ENA_W1C(a, b) (0x478ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_MISC_INT(a, b) (0x500ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_MISC_INT_W1S(a, b) (0x508ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_MISC_ENA_W1S(a, b) (0x510ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_MISC_ENA_W1C(a, b) (0x518ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VQX_DOORBELL(a, b) (0x600ll + ((u64)(a) << 36) + ((b) << 20)) +#define CPTX_VFX_PF_MBOXX(a, b, c) \ + (0x1000ll + ((u64)(a) << 36) + ((b) << 20) + ((c) << 3)) + +enum vftype { + AE_TYPES = 1, + SE_TYPES = 2, + BAD_CPT_TYPES, +}; + +/* Max CPT devices supported */ +enum cpt_mbox_opcode { + CPT_MSG_VF_UP = 1, + CPT_MSG_VF_DOWN, + CPT_MSG_READY, + CPT_MSG_QLEN, + CPT_MSG_QBIND_GRP, + CPT_MSG_VQ_PRIORITY, +}; + +/* CPT mailbox structure */ +struct cpt_mbox { + u64 msg; /* Message type MBOX[0] */ + u64 data;/* Data MBOX[1] */ +}; + +/* Register read/write APIs */ +static inline void cpt_write_csr64(u8 __iomem *hw_addr, u64 offset, + u64 val) +{ + writeq(val, hw_addr + offset); +} + +static inline u64 cpt_read_csr64(u8 __iomem *hw_addr, u64 offset) +{ + return readq(hw_addr + offset); +} +#endif /* __CPT_COMMON_H */ diff --git a/drivers/crypto/cavium/cpt/cpt_hw_types.h b/drivers/crypto/cavium/cpt/cpt_hw_types.h new file mode 100644 index 000000000000..279669494196 --- /dev/null +++ b/drivers/crypto/cavium/cpt/cpt_hw_types.h @@ -0,0 +1,658 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef __CPT_HW_TYPES_H +#define __CPT_HW_TYPES_H + +#include "cpt_common.h" + +/** + * Enumeration cpt_comp_e + * + * CPT Completion Enumeration + * Enumerates the values of CPT_RES_S[COMPCODE]. + */ +enum cpt_comp_e { + CPT_COMP_E_NOTDONE = 0x00, + CPT_COMP_E_GOOD = 0x01, + CPT_COMP_E_FAULT = 0x02, + CPT_COMP_E_SWERR = 0x03, + CPT_COMP_E_LAST_ENTRY = 0xFF +}; + +/** + * Structure cpt_inst_s + * + * CPT Instruction Structure + * This structure specifies the instruction layout. Instructions are + * stored in memory as little-endian unless CPT()_PF_Q()_CTL[INST_BE] is set. + * cpt_inst_s_s + * Word 0 + * doneint:1 Done interrupt. + * 0 = No interrupts related to this instruction. + * 1 = When the instruction completes, CPT()_VQ()_DONE[DONE] will be + * incremented,and based on the rules described there an interrupt may + * occur. + * Word 1 + * res_addr [127: 64] Result IOVA. + * If nonzero, specifies where to write CPT_RES_S. + * If zero, no result structure will be written. + * Address must be 16-byte aligned. + * Bits <63:49> are ignored by hardware; software should use a + * sign-extended bit <48> for forward compatibility. + * Word 2 + * grp:10 [171:162] If [WQ_PTR] is nonzero, the SSO guest-group to use when + * CPT submits work SSO. + * For the SSO to not discard the add-work request, FPA_PF_MAP() must map + * [GRP] and CPT()_PF_Q()_GMCTL[GMID] as valid. + * tt:2 [161:160] If [WQ_PTR] is nonzero, the SSO tag type to use when CPT + * submits work to SSO + * tag:32 [159:128] If [WQ_PTR] is nonzero, the SSO tag to use when CPT + * submits work to SSO. + * Word 3 + * wq_ptr [255:192] If [WQ_PTR] is nonzero, it is a pointer to a + * work-queue entry that CPT submits work to SSO after all context, + * output data, and result write operations are visible to other + * CNXXXX units and the cores. Bits <2:0> must be zero. + * Bits <63:49> are ignored by hardware; software should + * use a sign-extended bit <48> for forward compatibility. + * Internal: + * Bits <63:49>, <2:0> are ignored by hardware, treated as always 0x0. + * Word 4 + * ei0; [319:256] Engine instruction word 0. Passed to the AE/SE. + * Word 5 + * ei1; [383:320] Engine instruction word 1. Passed to the AE/SE. + * Word 6 + * ei2; [447:384] Engine instruction word 1. Passed to the AE/SE. + * Word 7 + * ei3; [511:448] Engine instruction word 1. Passed to the AE/SE. + * + */ +union cpt_inst_s { + u64 u[8]; + struct cpt_inst_s_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_17_63:47; + u64 doneint:1; + u64 reserved_0_1:16; +#else /* Word 0 - Little Endian */ + u64 reserved_0_15:16; + u64 doneint:1; + u64 reserved_17_63:47; +#endif /* Word 0 - End */ + u64 res_addr; +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 2 - Big Endian */ + u64 reserved_172_19:20; + u64 grp:10; + u64 tt:2; + u64 tag:32; +#else /* Word 2 - Little Endian */ + u64 tag:32; + u64 tt:2; + u64 grp:10; + u64 reserved_172_191:20; +#endif /* Word 2 - End */ + u64 wq_ptr; + u64 ei0; + u64 ei1; + u64 ei2; + u64 ei3; + } s; +}; + +/** + * Structure cpt_res_s + * + * CPT Result Structure + * The CPT coprocessor writes the result structure after it completes a + * CPT_INST_S instruction. The result structure is exactly 16 bytes, and + * each instruction completion produces exactly one result structure. + * + * This structure is stored in memory as little-endian unless + * CPT()_PF_Q()_CTL[INST_BE] is set. + * cpt_res_s_s + * Word 0 + * doneint:1 [16:16] Done interrupt. This bit is copied from the + * corresponding instruction's CPT_INST_S[DONEINT]. + * compcode:8 [7:0] Indicates completion/error status of the CPT coprocessor + * for the associated instruction, as enumerated by CPT_COMP_E. + * Core software may write the memory location containing [COMPCODE] to + * 0x0 before ringing the doorbell, and then poll for completion by + * checking for a nonzero value. + * Once the core observes a nonzero [COMPCODE] value in this case,the CPT + * coprocessor will have also completed L2/DRAM write operations. + * Word 1 + * reserved + * + */ +union cpt_res_s { + u64 u[2]; + struct cpt_res_s_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_17_63:47; + u64 doneint:1; + u64 reserved_8_15:8; + u64 compcode:8; +#else /* Word 0 - Little Endian */ + u64 compcode:8; + u64 reserved_8_15:8; + u64 doneint:1; + u64 reserved_17_63:47; +#endif /* Word 0 - End */ + u64 reserved_64_127; + } s; +}; + +/** + * Register (NCB) cpt#_pf_bist_status + * + * CPT PF Control Bist Status Register + * This register has the BIST status of memories. Each bit is the BIST result + * of an individual memory (per bit, 0 = pass and 1 = fail). + * cptx_pf_bist_status_s + * Word0 + * bstatus [29:0](RO/H) BIST status. One bit per memory, enumerated by + * CPT_RAMS_E. + */ +union cptx_pf_bist_status { + u64 u; + struct cptx_pf_bist_status_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_30_63:34; + u64 bstatus:30; +#else /* Word 0 - Little Endian */ + u64 bstatus:30; + u64 reserved_30_63:34; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_pf_constants + * + * CPT PF Constants Register + * This register contains implementation-related parameters of CPT in CNXXXX. + * cptx_pf_constants_s + * Word 0 + * reserved_40_63:24 [63:40] Reserved. + * epcis:8 [39:32](RO) Number of EPCI busses. + * grps:8 [31:24](RO) Number of engine groups implemented. + * ae:8 [23:16](RO/H) Number of AEs. In CNXXXX, for CPT0 returns 0x0, + * for CPT1 returns 0x18, or less if there are fuse-disables. + * se:8 [15:8](RO/H) Number of SEs. In CNXXXX, for CPT0 returns 0x30, + * or less if there are fuse-disables, for CPT1 returns 0x0. + * vq:8 [7:0](RO) Number of VQs. + */ +union cptx_pf_constants { + u64 u; + struct cptx_pf_constants_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_40_63:24; + u64 epcis:8; + u64 grps:8; + u64 ae:8; + u64 se:8; + u64 vq:8; +#else /* Word 0 - Little Endian */ + u64 vq:8; + u64 se:8; + u64 ae:8; + u64 grps:8; + u64 epcis:8; + u64 reserved_40_63:24; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_pf_exe_bist_status + * + * CPT PF Engine Bist Status Register + * This register has the BIST status of each engine. Each bit is the + * BIST result of an individual engine (per bit, 0 = pass and 1 = fail). + * cptx_pf_exe_bist_status_s + * Word0 + * reserved_48_63:16 [63:48] reserved + * bstatus:48 [47:0](RO/H) BIST status. One bit per engine. + * + */ +union cptx_pf_exe_bist_status { + u64 u; + struct cptx_pf_exe_bist_status_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_48_63:16; + u64 bstatus:48; +#else /* Word 0 - Little Endian */ + u64 bstatus:48; + u64 reserved_48_63:16; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_pf_q#_ctl + * + * CPT Queue Control Register + * This register configures queues. This register should be changed only + * when quiescent (see CPT()_VQ()_INPROG[INFLIGHT]). + * cptx_pf_qx_ctl_s + * Word0 + * reserved_60_63:4 [63:60] reserved. + * aura:12; [59:48](R/W) Guest-aura for returning this queue's + * instruction-chunk buffers to FPA. Only used when [INST_FREE] is set. + * For the FPA to not discard the request, FPA_PF_MAP() must map + * [AURA] and CPT()_PF_Q()_GMCTL[GMID] as valid. + * reserved_45_47:3 [47:45] reserved. + * size:13 [44:32](R/W) Command-buffer size, in number of 64-bit words per + * command buffer segment. Must be 8*n + 1, where n is the number of + * instructions per buffer segment. + * reserved_11_31:21 [31:11] Reserved. + * cont_err:1 [10:10](R/W) Continue on error. + * 0 = When CPT()_VQ()_MISC_INT[NWRP], CPT()_VQ()_MISC_INT[IRDE] or + * CPT()_VQ()_MISC_INT[DOVF] are set by hardware or software via + * CPT()_VQ()_MISC_INT_W1S, then CPT()_VQ()_CTL[ENA] is cleared. Due to + * pipelining, additional instructions may have been processed between the + * instruction causing the error and the next instruction in the disabled + * queue (the instruction at CPT()_VQ()_SADDR). + * 1 = Ignore errors and continue processing instructions. + * For diagnostic use only. + * inst_free:1 [9:9](R/W) Instruction FPA free. When set, when CPT reaches the + * end of an instruction chunk, that chunk will be freed to the FPA. + * inst_be:1 [8:8](R/W) Instruction big-endian control. When set, instructions, + * instruction next chunk pointers, and result structures are stored in + * big-endian format in memory. + * iqb_ldwb:1 [7:7](R/W) Instruction load don't write back. + * 0 = The hardware issues NCB transient load (LDT) towards the cache, + * which if the line hits and is is dirty will cause the line to be + * written back before being replaced. + * 1 = The hardware issues NCB LDWB read-and-invalidate command towards + * the cache when fetching the last word of instructions; as a result the + * line will not be written back when replaced. This improves + * performance, but software must not read the instructions after they are + * posted to the hardware. Reads that do not consume the last word of a + * cache line always use LDI. + * reserved_4_6:3 [6:4] Reserved. + * grp:3; [3:1](R/W) Engine group. + * pri:1; [0:0](R/W) Queue priority. + * 1 = This queue has higher priority. Round-robin between higher + * priority queues. + * 0 = This queue has lower priority. Round-robin between lower + * priority queues. + */ +union cptx_pf_qx_ctl { + u64 u; + struct cptx_pf_qx_ctl_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_60_63:4; + u64 aura:12; + u64 reserved_45_47:3; + u64 size:13; + u64 reserved_11_31:21; + u64 cont_err:1; + u64 inst_free:1; + u64 inst_be:1; + u64 iqb_ldwb:1; + u64 reserved_4_6:3; + u64 grp:3; + u64 pri:1; +#else /* Word 0 - Little Endian */ + u64 pri:1; + u64 grp:3; + u64 reserved_4_6:3; + u64 iqb_ldwb:1; + u64 inst_be:1; + u64 inst_free:1; + u64 cont_err:1; + u64 reserved_11_31:21; + u64 size:13; + u64 reserved_45_47:3; + u64 aura:12; + u64 reserved_60_63:4; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_saddr + * + * CPT Queue Starting Buffer Address Registers + * These registers set the instruction buffer starting address. + * cptx_vqx_saddr_s + * Word0 + * reserved_49_63:15 [63:49] Reserved. + * ptr:43 [48:6](R/W/H) Instruction buffer IOVA <48:6> (64-byte aligned). + * When written, it is the initial buffer starting address; when read, + * it is the next read pointer to be requested from L2C. The PTR field + * is overwritten with the next pointer each time that the command buffer + * segment is exhausted. New commands will then be read from the newly + * specified command buffer pointer. + * reserved_0_5:6 [5:0] Reserved. + * + */ +union cptx_vqx_saddr { + u64 u; + struct cptx_vqx_saddr_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_49_63:15; + u64 ptr:43; + u64 reserved_0_5:6; +#else /* Word 0 - Little Endian */ + u64 reserved_0_5:6; + u64 ptr:43; + u64 reserved_49_63:15; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_misc_ena_w1s + * + * CPT Queue Misc Interrupt Enable Set Register + * This register sets interrupt enable bits. + * cptx_vqx_misc_ena_w1s_s + * Word0 + * reserved_5_63:59 [63:5] Reserved. + * swerr:1 [4:4](R/W1S/H) Reads or sets enable for + * CPT(0..1)_VQ(0..63)_MISC_INT[SWERR]. + * nwrp:1 [3:3](R/W1S/H) Reads or sets enable for + * CPT(0..1)_VQ(0..63)_MISC_INT[NWRP]. + * irde:1 [2:2](R/W1S/H) Reads or sets enable for + * CPT(0..1)_VQ(0..63)_MISC_INT[IRDE]. + * dovf:1 [1:1](R/W1S/H) Reads or sets enable for + * CPT(0..1)_VQ(0..63)_MISC_INT[DOVF]. + * mbox:1 [0:0](R/W1S/H) Reads or sets enable for + * CPT(0..1)_VQ(0..63)_MISC_INT[MBOX]. + * + */ +union cptx_vqx_misc_ena_w1s { + u64 u; + struct cptx_vqx_misc_ena_w1s_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_5_63:59; + u64 swerr:1; + u64 nwrp:1; + u64 irde:1; + u64 dovf:1; + u64 mbox:1; +#else /* Word 0 - Little Endian */ + u64 mbox:1; + u64 dovf:1; + u64 irde:1; + u64 nwrp:1; + u64 swerr:1; + u64 reserved_5_63:59; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_doorbell + * + * CPT Queue Doorbell Registers + * Doorbells for the CPT instruction queues. + * cptx_vqx_doorbell_s + * Word0 + * reserved_20_63:44 [63:20] Reserved. + * dbell_cnt:20 [19:0](R/W/H) Number of instruction queue 64-bit words to add + * to the CPT instruction doorbell count. Readback value is the the + * current number of pending doorbell requests. If counter overflows + * CPT()_VQ()_MISC_INT[DBELL_DOVF] is set. To reset the count back to + * zero, write one to clear CPT()_VQ()_MISC_INT_ENA_W1C[DBELL_DOVF], + * then write a value of 2^20 minus the read [DBELL_CNT], then write one + * to CPT()_VQ()_MISC_INT_W1C[DBELL_DOVF] and + * CPT()_VQ()_MISC_INT_ENA_W1S[DBELL_DOVF]. Must be a multiple of 8. + * All CPT instructions are 8 words and require a doorbell count of + * multiple of 8. + */ +union cptx_vqx_doorbell { + u64 u; + struct cptx_vqx_doorbell_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_20_63:44; + u64 dbell_cnt:20; +#else /* Word 0 - Little Endian */ + u64 dbell_cnt:20; + u64 reserved_20_63:44; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_inprog + * + * CPT Queue In Progress Count Registers + * These registers contain the per-queue instruction in flight registers. + * cptx_vqx_inprog_s + * Word0 + * reserved_8_63:56 [63:8] Reserved. + * inflight:8 [7:0](RO/H) Inflight count. Counts the number of instructions + * for the VF for which CPT is fetching, executing or responding to + * instructions. However this does not include any interrupts that are + * awaiting software handling (CPT()_VQ()_DONE[DONE] != 0x0). + * A queue may not be reconfigured until: + * 1. CPT()_VQ()_CTL[ENA] is cleared by software. + * 2. [INFLIGHT] is polled until equals to zero. + */ +union cptx_vqx_inprog { + u64 u; + struct cptx_vqx_inprog_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_8_63:56; + u64 inflight:8; +#else /* Word 0 - Little Endian */ + u64 inflight:8; + u64 reserved_8_63:56; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_misc_int + * + * CPT Queue Misc Interrupt Register + * These registers contain the per-queue miscellaneous interrupts. + * cptx_vqx_misc_int_s + * Word 0 + * reserved_5_63:59 [63:5] Reserved. + * swerr:1 [4:4](R/W1C/H) Software error from engines. + * nwrp:1 [3:3](R/W1C/H) NCB result write response error. + * irde:1 [2:2](R/W1C/H) Instruction NCB read response error. + * dovf:1 [1:1](R/W1C/H) Doorbell overflow. + * mbox:1 [0:0](R/W1C/H) PF to VF mailbox interrupt. Set when + * CPT()_VF()_PF_MBOX(0) is written. + * + */ +union cptx_vqx_misc_int { + u64 u; + struct cptx_vqx_misc_int_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_5_63:59; + u64 swerr:1; + u64 nwrp:1; + u64 irde:1; + u64 dovf:1; + u64 mbox:1; +#else /* Word 0 - Little Endian */ + u64 mbox:1; + u64 dovf:1; + u64 irde:1; + u64 nwrp:1; + u64 swerr:1; + u64 reserved_5_63:59; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_done_ack + * + * CPT Queue Done Count Ack Registers + * This register is written by software to acknowledge interrupts. + * cptx_vqx_done_ack_s + * Word0 + * reserved_20_63:44 [63:20] Reserved. + * done_ack:20 [19:0](R/W/H) Number of decrements to CPT()_VQ()_DONE[DONE]. + * Reads CPT()_VQ()_DONE[DONE]. Written by software to acknowledge + * interrupts. If CPT()_VQ()_DONE[DONE] is still nonzero the interrupt + * will be re-sent if the conditions described in CPT()_VQ()_DONE[DONE] + * are satisfied. + * + */ +union cptx_vqx_done_ack { + u64 u; + struct cptx_vqx_done_ack_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_20_63:44; + u64 done_ack:20; +#else /* Word 0 - Little Endian */ + u64 done_ack:20; + u64 reserved_20_63:44; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_done + * + * CPT Queue Done Count Registers + * These registers contain the per-queue instruction done count. + * cptx_vqx_done_s + * Word0 + * reserved_20_63:44 [63:20] Reserved. + * done:20 [19:0](R/W/H) Done count. When CPT_INST_S[DONEINT] set and that + * instruction completes, CPT()_VQ()_DONE[DONE] is incremented when the + * instruction finishes. Write to this field are for diagnostic use only; + * instead software writes CPT()_VQ()_DONE_ACK with the number of + * decrements for this field. + * Interrupts are sent as follows: + * * When CPT()_VQ()_DONE[DONE] = 0, then no results are pending, the + * interrupt coalescing timer is held to zero, and an interrupt is not + * sent. + * * When CPT()_VQ()_DONE[DONE] != 0, then the interrupt coalescing timer + * counts. If the counter is >= CPT()_VQ()_DONE_WAIT[TIME_WAIT]*1024, or + * CPT()_VQ()_DONE[DONE] >= CPT()_VQ()_DONE_WAIT[NUM_WAIT], i.e. enough + * time has passed or enough results have arrived, then the interrupt is + * sent. + * * When CPT()_VQ()_DONE_ACK is written (or CPT()_VQ()_DONE is written + * but this is not typical), the interrupt coalescing timer restarts. + * Note after decrementing this interrupt equation is recomputed, + * for example if CPT()_VQ()_DONE[DONE] >= CPT()_VQ()_DONE_WAIT[NUM_WAIT] + * and because the timer is zero, the interrupt will be resent immediately. + * (This covers the race case between software acknowledging an interrupt + * and a result returning.) + * * When CPT()_VQ()_DONE_ENA_W1S[DONE] = 0, interrupts are not sent, + * but the counting described above still occurs. + * Since CPT instructions complete out-of-order, if software is using + * completion interrupts the suggested scheme is to request a DONEINT on + * each request, and when an interrupt arrives perform a "greedy" scan for + * completions; even if a later command is acknowledged first this will + * not result in missing a completion. + * Software is responsible for making sure [DONE] does not overflow; + * for example by insuring there are not more than 2^20-1 instructions in + * flight that may request interrupts. + * + */ +union cptx_vqx_done { + u64 u; + struct cptx_vqx_done_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_20_63:44; + u64 done:20; +#else /* Word 0 - Little Endian */ + u64 done:20; + u64 reserved_20_63:44; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_done_wait + * + * CPT Queue Done Interrupt Coalescing Wait Registers + * Specifies the per queue interrupt coalescing settings. + * cptx_vqx_done_wait_s + * Word0 + * reserved_48_63:16 [63:48] Reserved. + * time_wait:16; [47:32](R/W) Time hold-off. When CPT()_VQ()_DONE[DONE] = 0 + * or CPT()_VQ()_DONE_ACK is written a timer is cleared. When the timer + * reaches [TIME_WAIT]*1024 then interrupt coalescing ends. + * see CPT()_VQ()_DONE[DONE]. If 0x0, time coalescing is disabled. + * reserved_20_31:12 [31:20] Reserved. + * num_wait:20 [19:0](R/W) Number of messages hold-off. + * When CPT()_VQ()_DONE[DONE] >= [NUM_WAIT] then interrupt coalescing ends + * see CPT()_VQ()_DONE[DONE]. If 0x0, same behavior as 0x1. + * + */ +union cptx_vqx_done_wait { + u64 u; + struct cptx_vqx_done_wait_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_48_63:16; + u64 time_wait:16; + u64 reserved_20_31:12; + u64 num_wait:20; +#else /* Word 0 - Little Endian */ + u64 num_wait:20; + u64 reserved_20_31:12; + u64 time_wait:16; + u64 reserved_48_63:16; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_done_ena_w1s + * + * CPT Queue Done Interrupt Enable Set Registers + * Write 1 to these registers will enable the DONEINT interrupt for the queue. + * cptx_vqx_done_ena_w1s_s + * Word0 + * reserved_1_63:63 [63:1] Reserved. + * done:1 [0:0](R/W1S/H) Write 1 will enable DONEINT for this queue. + * Write 0 has no effect. Read will return the enable bit. + */ +union cptx_vqx_done_ena_w1s { + u64 u; + struct cptx_vqx_done_ena_w1s_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_1_63:63; + u64 done:1; +#else /* Word 0 - Little Endian */ + u64 done:1; + u64 reserved_1_63:63; +#endif /* Word 0 - End */ + } s; +}; + +/** + * Register (NCB) cpt#_vq#_ctl + * + * CPT VF Queue Control Registers + * This register configures queues. This register should be changed (other than + * clearing [ENA]) only when quiescent (see CPT()_VQ()_INPROG[INFLIGHT]). + * cptx_vqx_ctl_s + * Word0 + * reserved_1_63:63 [63:1] Reserved. + * ena:1 [0:0](R/W/H) Enables the logical instruction queue. + * See also CPT()_PF_Q()_CTL[CONT_ERR] and CPT()_VQ()_INPROG[INFLIGHT]. + * 1 = Queue is enabled. + * 0 = Queue is disabled. + */ +union cptx_vqx_ctl { + u64 u; + struct cptx_vqx_ctl_s { +#if defined(__BIG_ENDIAN_BITFIELD) /* Word 0 - Big Endian */ + u64 reserved_1_63:63; + u64 ena:1; +#else /* Word 0 - Little Endian */ + u64 ena:1; + u64 reserved_1_63:63; +#endif /* Word 0 - End */ + } s; +}; +#endif /*__CPT_HW_TYPES_H*/ diff --git a/drivers/crypto/cavium/cpt/cptpf.h b/drivers/crypto/cavium/cpt/cptpf.h new file mode 100644 index 000000000000..c0556c5f63c9 --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptpf.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef __CPTPF_H +#define __CPTPF_H + +#include "cpt_common.h" + +#define CSR_DELAY 30 +#define CPT_MAX_CORE_GROUPS 8 +#define CPT_MAX_SE_CORES 10 +#define CPT_MAX_AE_CORES 6 +#define CPT_MAX_TOTAL_CORES (CPT_MAX_SE_CORES + CPT_MAX_AE_CORES) +#define CPT_MAX_VF_NUM 16 +#define CPT_PF_MSIX_VECTORS 3 +#define CPT_PF_INT_VEC_E_MBOXX(a) (0x02 + (a)) +#define CPT_UCODE_VERSION_SZ 32 +struct cpt_device; + +struct microcode { + u8 is_mc_valid; + u8 is_ae; + u8 group; + u8 num_cores; + u32 code_size; + u64 core_mask; + u8 version[CPT_UCODE_VERSION_SZ]; + /* Base info */ + dma_addr_t phys_base; + void *code; +}; + +struct cpt_vf_info { + u8 state; + u8 priority; + u8 id; + u32 qlen; +}; + +/** + * cpt device structure + */ +struct cpt_device { + u16 flags; /* Flags to hold device status bits */ + u8 num_vf_en; /* Number of VFs enabled (0...CPT_MAX_VF_NUM) */ + struct cpt_vf_info vfinfo[CPT_MAX_VF_NUM]; /* Per VF info */ + + void __iomem *reg_base; /* Register start address */ + struct pci_dev *pdev; /* pci device handle */ + + struct microcode mcode[CPT_MAX_CORE_GROUPS]; + u8 next_mc_idx; /* next microcode index */ + u8 next_group; + u8 max_se_cores; + u8 max_ae_cores; +}; + +void cpt_mbox_intr_handler(struct cpt_device *cpt, int mbx); +#endif /* __CPTPF_H */ diff --git a/drivers/crypto/cavium/cpt/cptpf_main.c b/drivers/crypto/cavium/cpt/cptpf_main.c new file mode 100644 index 000000000000..4119c40e7c4b --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptpf_main.c @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/printk.h> +#include <linux/version.h> + +#include "cptpf.h" + +#define DRV_NAME "thunder-cpt" +#define DRV_VERSION "1.0" + +static u32 num_vfs = 4; /* Default 4 VF enabled */ +module_param(num_vfs, uint, 0444); +MODULE_PARM_DESC(num_vfs, "Number of VFs to enable(1-16)"); + +/* + * Disable cores specified by coremask + */ +static void cpt_disable_cores(struct cpt_device *cpt, u64 coremask, + u8 type, u8 grp) +{ + u64 pf_exe_ctl; + u32 timeout = 100; + u64 grpmask = 0; + struct device *dev = &cpt->pdev->dev; + + if (type == AE_TYPES) + coremask = (coremask << cpt->max_se_cores); + + /* Disengage the cores from groups */ + grpmask = cpt_read_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp)); + cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), + (grpmask & ~coremask)); + udelay(CSR_DELAY); + grp = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXEC_BUSY(0)); + while (grp & coremask) { + dev_err(dev, "Cores still busy %llx", coremask); + grp = cpt_read_csr64(cpt->reg_base, + CPTX_PF_EXEC_BUSY(0)); + if (timeout--) + break; + + udelay(CSR_DELAY); + } + + /* Disable the cores */ + pf_exe_ctl = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0)); + cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), + (pf_exe_ctl & ~coremask)); + udelay(CSR_DELAY); +} + +/* + * Enable cores specified by coremask + */ +static void cpt_enable_cores(struct cpt_device *cpt, u64 coremask, + u8 type) +{ + u64 pf_exe_ctl; + + if (type == AE_TYPES) + coremask = (coremask << cpt->max_se_cores); + + pf_exe_ctl = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0)); + cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), + (pf_exe_ctl | coremask)); + udelay(CSR_DELAY); +} + +static void cpt_configure_group(struct cpt_device *cpt, u8 grp, + u64 coremask, u8 type) +{ + u64 pf_gx_en = 0; + + if (type == AE_TYPES) + coremask = (coremask << cpt->max_se_cores); + + pf_gx_en = cpt_read_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp)); + cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), + (pf_gx_en | coremask)); + udelay(CSR_DELAY); +} + +static void cpt_disable_mbox_interrupts(struct cpt_device *cpt) +{ + /* Clear mbox(0) interupts for all vfs */ + cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_ENA_W1CX(0, 0), ~0ull); +} + +static void cpt_disable_ecc_interrupts(struct cpt_device *cpt) +{ + /* Clear ecc(0) interupts for all vfs */ + cpt_write_csr64(cpt->reg_base, CPTX_PF_ECC0_ENA_W1C(0), ~0ull); +} + +static void cpt_disable_exec_interrupts(struct cpt_device *cpt) +{ + /* Clear exec interupts for all vfs */ + cpt_write_csr64(cpt->reg_base, CPTX_PF_EXEC_ENA_W1C(0), ~0ull); +} + +static void cpt_disable_all_interrupts(struct cpt_device *cpt) +{ + cpt_disable_mbox_interrupts(cpt); + cpt_disable_ecc_interrupts(cpt); + cpt_disable_exec_interrupts(cpt); +} + +static void cpt_enable_mbox_interrupts(struct cpt_device *cpt) +{ + /* Set mbox(0) interupts for all vfs */ + cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_ENA_W1SX(0, 0), ~0ull); +} + +static int cpt_load_microcode(struct cpt_device *cpt, struct microcode *mcode) +{ + int ret = 0, core = 0, shift = 0; + u32 total_cores = 0; + struct device *dev = &cpt->pdev->dev; + + if (!mcode || !mcode->code) { + dev_err(dev, "Either the mcode is null or data is NULL\n"); + return -EINVAL; + } + + if (mcode->code_size == 0) { + dev_err(dev, "microcode size is 0\n"); + return -EINVAL; + } + + /* Assumes 0-9 are SE cores for UCODE_BASE registers and + * AE core bases follow + */ + if (mcode->is_ae) { + core = CPT_MAX_SE_CORES; /* start couting from 10 */ + total_cores = CPT_MAX_TOTAL_CORES; /* upto 15 */ + } else { + core = 0; /* start couting from 0 */ + total_cores = CPT_MAX_SE_CORES; /* upto 9 */ + } + + /* Point to microcode for each core of the group */ + for (; core < total_cores ; core++, shift++) { + if (mcode->core_mask & (1 << shift)) { + cpt_write_csr64(cpt->reg_base, + CPTX_PF_ENGX_UCODE_BASE(0, core), + (u64)mcode->phys_base); + } + } + return ret; +} + +static int do_cpt_init(struct cpt_device *cpt, struct microcode *mcode) +{ + int ret = 0; + struct device *dev = &cpt->pdev->dev; + + /* Make device not ready */ + cpt->flags &= ~CPT_FLAG_DEVICE_READY; + /* Disable All PF interrupts */ + cpt_disable_all_interrupts(cpt); + /* Calculate mcode group and coremasks */ + if (mcode->is_ae) { + if (mcode->num_cores > cpt->max_ae_cores) { + dev_err(dev, "Requested for more cores than available AE cores\n"); + ret = -EINVAL; + goto cpt_init_fail; + } + + if (cpt->next_group >= CPT_MAX_CORE_GROUPS) { + dev_err(dev, "Can't load, all eight microcode groups in use"); + return -ENFILE; + } + + mcode->group = cpt->next_group; + /* Convert requested cores to mask */ + mcode->core_mask = GENMASK(mcode->num_cores, 0); + cpt_disable_cores(cpt, mcode->core_mask, AE_TYPES, + mcode->group); + /* Load microcode for AE engines */ + ret = cpt_load_microcode(cpt, mcode); + if (ret) { + dev_err(dev, "Microcode load Failed for %s\n", + mcode->version); + goto cpt_init_fail; + } + cpt->next_group++; + /* Configure group mask for the mcode */ + cpt_configure_group(cpt, mcode->group, mcode->core_mask, + AE_TYPES); + /* Enable AE cores for the group mask */ + cpt_enable_cores(cpt, mcode->core_mask, AE_TYPES); + } else { + if (mcode->num_cores > cpt->max_se_cores) { + dev_err(dev, "Requested for more cores than available SE cores\n"); + ret = -EINVAL; + goto cpt_init_fail; + } + if (cpt->next_group >= CPT_MAX_CORE_GROUPS) { + dev_err(dev, "Can't load, all eight microcode groups in use"); + return -ENFILE; + } + + mcode->group = cpt->next_group; + /* Covert requested cores to mask */ + mcode->core_mask = GENMASK(mcode->num_cores, 0); + cpt_disable_cores(cpt, mcode->core_mask, SE_TYPES, + mcode->group); + /* Load microcode for SE engines */ + ret = cpt_load_microcode(cpt, mcode); + if (ret) { + dev_err(dev, "Microcode load Failed for %s\n", + mcode->version); + goto cpt_init_fail; + } + cpt->next_group++; + /* Configure group mask for the mcode */ + cpt_configure_group(cpt, mcode->group, mcode->core_mask, + SE_TYPES); + /* Enable SE cores for the group mask */ + cpt_enable_cores(cpt, mcode->core_mask, SE_TYPES); + } + + /* Enabled PF mailbox interrupts */ + cpt_enable_mbox_interrupts(cpt); + cpt->flags |= CPT_FLAG_DEVICE_READY; + + return ret; + +cpt_init_fail: + /* Enabled PF mailbox interrupts */ + cpt_enable_mbox_interrupts(cpt); + + return ret; +} + +struct ucode_header { + u8 version[CPT_UCODE_VERSION_SZ]; + u32 code_length; + u32 data_length; + u64 sram_address; +}; + +static int cpt_ucode_load_fw(struct cpt_device *cpt, const u8 *fw, bool is_ae) +{ + const struct firmware *fw_entry; + struct device *dev = &cpt->pdev->dev; + struct ucode_header *ucode; + struct microcode *mcode; + int j, ret = 0; + + ret = request_firmware(&fw_entry, fw, dev); + if (ret) + return ret; + + ucode = (struct ucode_header *)fw_entry->data; + mcode = &cpt->mcode[cpt->next_mc_idx]; + memcpy(mcode->version, (u8 *)fw_entry->data, CPT_UCODE_VERSION_SZ); + mcode->code_size = ntohl(ucode->code_length) * 2; + if (!mcode->code_size) + return -EINVAL; + + mcode->is_ae = is_ae; + mcode->core_mask = 0ULL; + mcode->num_cores = is_ae ? 6 : 10; + + /* Allocate DMAable space */ + mcode->code = dma_zalloc_coherent(&cpt->pdev->dev, mcode->code_size, + &mcode->phys_base, GFP_KERNEL); + if (!mcode->code) { + dev_err(dev, "Unable to allocate space for microcode"); + return -ENOMEM; + } + + memcpy((void *)mcode->code, (void *)(fw_entry->data + sizeof(*ucode)), + mcode->code_size); + + /* Byte swap 64-bit */ + for (j = 0; j < (mcode->code_size / 8); j++) + ((u64 *)mcode->code)[j] = cpu_to_be64(((u64 *)mcode->code)[j]); + /* MC needs 16-bit swap */ + for (j = 0; j < (mcode->code_size / 2); j++) + ((u16 *)mcode->code)[j] = cpu_to_be16(((u16 *)mcode->code)[j]); + + dev_dbg(dev, "mcode->code_size = %u\n", mcode->code_size); + dev_dbg(dev, "mcode->is_ae = %u\n", mcode->is_ae); + dev_dbg(dev, "mcode->num_cores = %u\n", mcode->num_cores); + dev_dbg(dev, "mcode->code = %llx\n", (u64)mcode->code); + dev_dbg(dev, "mcode->phys_base = %llx\n", mcode->phys_base); + + ret = do_cpt_init(cpt, mcode); + if (ret) { + dev_err(dev, "do_cpt_init failed with ret: %d\n", ret); + return ret; + } + + dev_info(dev, "Microcode Loaded %s\n", mcode->version); + mcode->is_mc_valid = 1; + cpt->next_mc_idx++; + release_firmware(fw_entry); + + return ret; +} + +static int cpt_ucode_load(struct cpt_device *cpt) +{ + int ret = 0; + struct device *dev = &cpt->pdev->dev; + + ret = cpt_ucode_load_fw(cpt, "cpt8x-mc-ae.out", true); + if (ret) { + dev_err(dev, "ae:cpt_ucode_load failed with ret: %d\n", ret); + return ret; + } + ret = cpt_ucode_load_fw(cpt, "cpt8x-mc-se.out", false); + if (ret) { + dev_err(dev, "se:cpt_ucode_load failed with ret: %d\n", ret); + return ret; + } + + return ret; +} + +static irqreturn_t cpt_mbx0_intr_handler(int irq, void *cpt_irq) +{ + struct cpt_device *cpt = (struct cpt_device *)cpt_irq; + + cpt_mbox_intr_handler(cpt, 0); + + return IRQ_HANDLED; +} + +static void cpt_reset(struct cpt_device *cpt) +{ + cpt_write_csr64(cpt->reg_base, CPTX_PF_RESET(0), 1); +} + +static void cpt_find_max_enabled_cores(struct cpt_device *cpt) +{ + union cptx_pf_constants pf_cnsts = {0}; + + pf_cnsts.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_CONSTANTS(0)); + cpt->max_se_cores = pf_cnsts.s.se; + cpt->max_ae_cores = pf_cnsts.s.ae; +} + +static u32 cpt_check_bist_status(struct cpt_device *cpt) +{ + union cptx_pf_bist_status bist_sts = {0}; + + bist_sts.u = cpt_read_csr64(cpt->reg_base, + CPTX_PF_BIST_STATUS(0)); + + return bist_sts.u; +} + +static u64 cpt_check_exe_bist_status(struct cpt_device *cpt) +{ + union cptx_pf_exe_bist_status bist_sts = {0}; + + bist_sts.u = cpt_read_csr64(cpt->reg_base, + CPTX_PF_EXE_BIST_STATUS(0)); + + return bist_sts.u; +} + +static void cpt_disable_all_cores(struct cpt_device *cpt) +{ + u32 grp, timeout = 100; + struct device *dev = &cpt->pdev->dev; + + /* Disengage the cores from groups */ + for (grp = 0; grp < CPT_MAX_CORE_GROUPS; grp++) { + cpt_write_csr64(cpt->reg_base, CPTX_PF_GX_EN(0, grp), 0); + udelay(CSR_DELAY); + } + + grp = cpt_read_csr64(cpt->reg_base, CPTX_PF_EXEC_BUSY(0)); + while (grp) { + dev_err(dev, "Cores still busy"); + grp = cpt_read_csr64(cpt->reg_base, + CPTX_PF_EXEC_BUSY(0)); + if (timeout--) + break; + + udelay(CSR_DELAY); + } + /* Disable the cores */ + cpt_write_csr64(cpt->reg_base, CPTX_PF_EXE_CTL(0), 0); +} + +/** + * Ensure all cores are disengaged from all groups by + * calling cpt_disable_all_cores() before calling this + * function. + */ +static void cpt_unload_microcode(struct cpt_device *cpt) +{ + u32 grp = 0, core; + + /* Free microcode bases and reset group masks */ + for (grp = 0; grp < CPT_MAX_CORE_GROUPS; grp++) { + struct microcode *mcode = &cpt->mcode[grp]; + + if (cpt->mcode[grp].code) + dma_free_coherent(&cpt->pdev->dev, mcode->code_size, + mcode->code, mcode->phys_base); + mcode->code = NULL; + } + /* Clear UCODE_BASE registers for all engines */ + for (core = 0; core < CPT_MAX_TOTAL_CORES; core++) + cpt_write_csr64(cpt->reg_base, + CPTX_PF_ENGX_UCODE_BASE(0, core), 0ull); +} + +static int cpt_device_init(struct cpt_device *cpt) +{ + u64 bist; + struct device *dev = &cpt->pdev->dev; + + /* Reset the PF when probed first */ + cpt_reset(cpt); + mdelay(100); + + /*Check BIST status*/ + bist = (u64)cpt_check_bist_status(cpt); + if (bist) { + dev_err(dev, "RAM BIST failed with code 0x%llx", bist); + return -ENODEV; + } + + bist = cpt_check_exe_bist_status(cpt); + if (bist) { + dev_err(dev, "Engine BIST failed with code 0x%llx", bist); + return -ENODEV; + } + + /*Get CLK frequency*/ + /*Get max enabled cores */ + cpt_find_max_enabled_cores(cpt); + /*Disable all cores*/ + cpt_disable_all_cores(cpt); + /*Reset device parameters*/ + cpt->next_mc_idx = 0; + cpt->next_group = 0; + /* PF is ready */ + cpt->flags |= CPT_FLAG_DEVICE_READY; + + return 0; +} + +static int cpt_register_interrupts(struct cpt_device *cpt) +{ + int ret; + struct device *dev = &cpt->pdev->dev; + + /* Enable MSI-X */ + ret = pci_alloc_irq_vectors(cpt->pdev, CPT_PF_MSIX_VECTORS, + CPT_PF_MSIX_VECTORS, PCI_IRQ_MSIX); + if (ret < 0) { + dev_err(&cpt->pdev->dev, "Request for #%d msix vectors failed\n", + CPT_PF_MSIX_VECTORS); + return ret; + } + + /* Register mailbox interrupt handlers */ + ret = request_irq(pci_irq_vector(cpt->pdev, CPT_PF_INT_VEC_E_MBOXX(0)), + cpt_mbx0_intr_handler, 0, "CPT Mbox0", cpt); + if (ret) + goto fail; + + /* Enable mailbox interrupt */ + cpt_enable_mbox_interrupts(cpt); + return 0; + +fail: + dev_err(dev, "Request irq failed\n"); + pci_disable_msix(cpt->pdev); + return ret; +} + +static void cpt_unregister_interrupts(struct cpt_device *cpt) +{ + free_irq(pci_irq_vector(cpt->pdev, CPT_PF_INT_VEC_E_MBOXX(0)), cpt); + pci_disable_msix(cpt->pdev); +} + +static int cpt_sriov_init(struct cpt_device *cpt, int num_vfs) +{ + int pos = 0; + int err; + u16 total_vf_cnt; + struct pci_dev *pdev = cpt->pdev; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) { + dev_err(&pdev->dev, "SRIOV capability is not found in PCIe config space\n"); + return -ENODEV; + } + + cpt->num_vf_en = num_vfs; /* User requested VFs */ + pci_read_config_word(pdev, (pos + PCI_SRIOV_TOTAL_VF), &total_vf_cnt); + if (total_vf_cnt < cpt->num_vf_en) + cpt->num_vf_en = total_vf_cnt; + + if (!total_vf_cnt) + return 0; + + /*Enabled the available VFs */ + err = pci_enable_sriov(pdev, cpt->num_vf_en); + if (err) { + dev_err(&pdev->dev, "SRIOV enable failed, num VF is %d\n", + cpt->num_vf_en); + cpt->num_vf_en = 0; + return err; + } + + /* TODO: Optionally enable static VQ priorities feature */ + + dev_info(&pdev->dev, "SRIOV enabled, number of VF available %d\n", + cpt->num_vf_en); + + cpt->flags |= CPT_FLAG_SRIOV_ENABLED; + + return 0; +} + +static int cpt_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct cpt_device *cpt; + int err; + + if (num_vfs > 16 || num_vfs < 4) { + dev_warn(dev, "Invalid vf count %d, Resetting it to 4(default)\n", + num_vfs); + num_vfs = 4; + } + + cpt = devm_kzalloc(dev, sizeof(*cpt), GFP_KERNEL); + if (!cpt) + return -ENOMEM; + + pci_set_drvdata(pdev, cpt); + cpt->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + pci_set_drvdata(pdev, NULL); + return err; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + goto cpt_err_disable_device; + } + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to get usable DMA configuration\n"); + goto cpt_err_release_regions; + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); + goto cpt_err_release_regions; + } + + /* MAP PF's configuration registers */ + cpt->reg_base = pcim_iomap(pdev, 0, 0); + if (!cpt->reg_base) { + dev_err(dev, "Cannot map config register space, aborting\n"); + err = -ENOMEM; + goto cpt_err_release_regions; + } + + /* CPT device HW initialization */ + cpt_device_init(cpt); + + /* Register interrupts */ + err = cpt_register_interrupts(cpt); + if (err) + goto cpt_err_release_regions; + + err = cpt_ucode_load(cpt); + if (err) + goto cpt_err_unregister_interrupts; + + /* Configure SRIOV */ + err = cpt_sriov_init(cpt, num_vfs); + if (err) + goto cpt_err_unregister_interrupts; + + return 0; + +cpt_err_unregister_interrupts: + cpt_unregister_interrupts(cpt); +cpt_err_release_regions: + pci_release_regions(pdev); +cpt_err_disable_device: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return err; +} + +static void cpt_remove(struct pci_dev *pdev) +{ + struct cpt_device *cpt = pci_get_drvdata(pdev); + + /* Disengage SE and AE cores from all groups*/ + cpt_disable_all_cores(cpt); + /* Unload microcodes */ + cpt_unload_microcode(cpt); + cpt_unregister_interrupts(cpt); + pci_disable_sriov(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static void cpt_shutdown(struct pci_dev *pdev) +{ + struct cpt_device *cpt = pci_get_drvdata(pdev); + + if (!cpt) + return; + + dev_info(&pdev->dev, "Shutdown device %x:%x.\n", + (u32)pdev->vendor, (u32)pdev->device); + + cpt_unregister_interrupts(cpt); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +/* Supported devices */ +static const struct pci_device_id cpt_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, CPT_81XX_PCI_PF_DEVICE_ID) }, + { 0, } /* end of table */ +}; + +static struct pci_driver cpt_pci_driver = { + .name = DRV_NAME, + .id_table = cpt_id_table, + .probe = cpt_probe, + .remove = cpt_remove, + .shutdown = cpt_shutdown, +}; + +module_pci_driver(cpt_pci_driver); + +MODULE_AUTHOR("George Cherian <george.cherian@cavium.com>"); +MODULE_DESCRIPTION("Cavium Thunder CPT Physical Function Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, cpt_id_table); diff --git a/drivers/crypto/cavium/cpt/cptpf_mbox.c b/drivers/crypto/cavium/cpt/cptpf_mbox.c new file mode 100644 index 000000000000..20f2c6ee46a5 --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptpf_mbox.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ +#include <linux/module.h> +#include "cptpf.h" + +static void cpt_send_msg_to_vf(struct cpt_device *cpt, int vf, + struct cpt_mbox *mbx) +{ + /* Writing mbox(0) causes interrupt */ + cpt_write_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 1), + mbx->data); + cpt_write_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 0), mbx->msg); +} + +/* ACKs VF's mailbox message + * @vf: VF to which ACK to be sent + */ +static void cpt_mbox_send_ack(struct cpt_device *cpt, int vf, + struct cpt_mbox *mbx) +{ + mbx->data = 0ull; + mbx->msg = CPT_MBOX_MSG_TYPE_ACK; + cpt_send_msg_to_vf(cpt, vf, mbx); +} + +static void cpt_clear_mbox_intr(struct cpt_device *cpt, u32 vf) +{ + /* W1C for the VF */ + cpt_write_csr64(cpt->reg_base, CPTX_PF_MBOX_INTX(0, 0), (1 << vf)); +} + +/* + * Configure QLEN/Chunk sizes for VF + */ +static void cpt_cfg_qlen_for_vf(struct cpt_device *cpt, int vf, u32 size) +{ + union cptx_pf_qx_ctl pf_qx_ctl; + + pf_qx_ctl.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf)); + pf_qx_ctl.s.size = size; + pf_qx_ctl.s.cont_err = true; + cpt_write_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf), pf_qx_ctl.u); +} + +/* + * Configure VQ priority + */ +static void cpt_cfg_vq_priority(struct cpt_device *cpt, int vf, u32 pri) +{ + union cptx_pf_qx_ctl pf_qx_ctl; + + pf_qx_ctl.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf)); + pf_qx_ctl.s.pri = pri; + cpt_write_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, vf), pf_qx_ctl.u); +} + +static int cpt_bind_vq_to_grp(struct cpt_device *cpt, u8 q, u8 grp) +{ + struct microcode *mcode = cpt->mcode; + union cptx_pf_qx_ctl pf_qx_ctl; + struct device *dev = &cpt->pdev->dev; + + if (q >= CPT_MAX_VF_NUM) { + dev_err(dev, "Queues are more than cores in the group"); + return -EINVAL; + } + if (grp >= CPT_MAX_CORE_GROUPS) { + dev_err(dev, "Request group is more than possible groups"); + return -EINVAL; + } + if (grp >= cpt->next_mc_idx) { + dev_err(dev, "Request group is higher than available functional groups"); + return -EINVAL; + } + pf_qx_ctl.u = cpt_read_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, q)); + pf_qx_ctl.s.grp = mcode[grp].group; + cpt_write_csr64(cpt->reg_base, CPTX_PF_QX_CTL(0, q), pf_qx_ctl.u); + dev_dbg(dev, "VF %d TYPE %s", q, (mcode[grp].is_ae ? "AE" : "SE")); + + return mcode[grp].is_ae ? AE_TYPES : SE_TYPES; +} + +/* Interrupt handler to handle mailbox messages from VFs */ +static void cpt_handle_mbox_intr(struct cpt_device *cpt, int vf) +{ + struct cpt_vf_info *vfx = &cpt->vfinfo[vf]; + struct cpt_mbox mbx = {}; + int vftype; + struct device *dev = &cpt->pdev->dev; + /* + * MBOX[0] contains msg + * MBOX[1] contains data + */ + mbx.msg = cpt_read_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 0)); + mbx.data = cpt_read_csr64(cpt->reg_base, CPTX_PF_VFX_MBOXX(0, vf, 1)); + dev_dbg(dev, "%s: Mailbox msg 0x%llx from VF%d", __func__, mbx.msg, vf); + switch (mbx.msg) { + case CPT_MSG_VF_UP: + vfx->state = VF_STATE_UP; + try_module_get(THIS_MODULE); + cpt_mbox_send_ack(cpt, vf, &mbx); + break; + case CPT_MSG_READY: + mbx.msg = CPT_MSG_READY; + mbx.data = vf; + cpt_send_msg_to_vf(cpt, vf, &mbx); + break; + case CPT_MSG_VF_DOWN: + /* First msg in VF teardown sequence */ + vfx->state = VF_STATE_DOWN; + module_put(THIS_MODULE); + cpt_mbox_send_ack(cpt, vf, &mbx); + break; + case CPT_MSG_QLEN: + vfx->qlen = mbx.data; + cpt_cfg_qlen_for_vf(cpt, vf, vfx->qlen); + cpt_mbox_send_ack(cpt, vf, &mbx); + break; + case CPT_MSG_QBIND_GRP: + vftype = cpt_bind_vq_to_grp(cpt, vf, (u8)mbx.data); + if ((vftype != AE_TYPES) && (vftype != SE_TYPES)) + dev_err(dev, "Queue %d binding to group %llu failed", + vf, mbx.data); + else { + dev_dbg(dev, "Queue %d binding to group %llu successful", + vf, mbx.data); + mbx.msg = CPT_MSG_QBIND_GRP; + mbx.data = vftype; + cpt_send_msg_to_vf(cpt, vf, &mbx); + } + break; + case CPT_MSG_VQ_PRIORITY: + vfx->priority = mbx.data; + cpt_cfg_vq_priority(cpt, vf, vfx->priority); + cpt_mbox_send_ack(cpt, vf, &mbx); + break; + default: + dev_err(&cpt->pdev->dev, "Invalid msg from VF%d, msg 0x%llx\n", + vf, mbx.msg); + break; + } +} + +void cpt_mbox_intr_handler (struct cpt_device *cpt, int mbx) +{ + u64 intr; + u8 vf; + + intr = cpt_read_csr64(cpt->reg_base, CPTX_PF_MBOX_INTX(0, 0)); + dev_dbg(&cpt->pdev->dev, "PF interrupt Mbox%d 0x%llx\n", mbx, intr); + for (vf = 0; vf < CPT_MAX_VF_NUM; vf++) { + if (intr & (1ULL << vf)) { + dev_dbg(&cpt->pdev->dev, "Intr from VF %d\n", vf); + cpt_handle_mbox_intr(cpt, vf); + cpt_clear_mbox_intr(cpt, vf); + } + } +} diff --git a/drivers/crypto/cavium/cpt/cptvf.h b/drivers/crypto/cavium/cpt/cptvf.h new file mode 100644 index 000000000000..0a835a07d4f2 --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptvf.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef __CPTVF_H +#define __CPTVF_H + +#include <linux/list.h> +#include "cpt_common.h" + +/* Default command queue length */ +#define CPT_CMD_QLEN 2046 +#define CPT_CMD_QCHUNK_SIZE 1023 + +/* Default command timeout in seconds */ +#define CPT_COMMAND_TIMEOUT 4 +#define CPT_TIMER_THOLD 0xFFFF +#define CPT_NUM_QS_PER_VF 1 +#define CPT_INST_SIZE 64 +#define CPT_NEXT_CHUNK_PTR_SIZE 8 + +#define CPT_VF_MSIX_VECTORS 2 +#define CPT_VF_INTR_MBOX_MASK BIT(0) +#define CPT_VF_INTR_DOVF_MASK BIT(1) +#define CPT_VF_INTR_IRDE_MASK BIT(2) +#define CPT_VF_INTR_NWRP_MASK BIT(3) +#define CPT_VF_INTR_SERR_MASK BIT(4) +#define DMA_DIRECT_DIRECT 0 /* Input DIRECT, Output DIRECT */ +#define DMA_GATHER_SCATTER 1 +#define FROM_DPTR 1 + +/** + * Enumeration cpt_vf_int_vec_e + * + * CPT VF MSI-X Vector Enumeration + * Enumerates the MSI-X interrupt vectors. + */ +enum cpt_vf_int_vec_e { + CPT_VF_INT_VEC_E_MISC = 0x00, + CPT_VF_INT_VEC_E_DONE = 0x01 +}; + +struct command_chunk { + u8 *head; + dma_addr_t dma_addr; + u32 size; /* Chunk size, max CPT_INST_CHUNK_MAX_SIZE */ + struct hlist_node nextchunk; +}; + +struct command_queue { + spinlock_t lock; /* command queue lock */ + u32 idx; /* Command queue host write idx */ + u32 nchunks; /* Number of command chunks */ + struct command_chunk *qhead; /* Command queue head, instructions + * are inserted here + */ + struct hlist_head chead; +}; + +struct command_qinfo { + u32 cmd_size; + u32 qchunksize; /* Command queue chunk size */ + struct command_queue queue[CPT_NUM_QS_PER_VF]; +}; + +struct pending_entry { + u8 busy; /* Entry status (free/busy) */ + + volatile u64 *completion_addr; /* Completion address */ + void *post_arg; + void (*callback)(int, void *); /* Kernel ASYNC request callabck */ + void *callback_arg; /* Kernel ASYNC request callabck arg */ +}; + +struct pending_queue { + struct pending_entry *head; /* head of the queue */ + u32 front; /* Process work from here */ + u32 rear; /* Append new work here */ + atomic64_t pending_count; + spinlock_t lock; /* Queue lock */ +}; + +struct pending_qinfo { + u32 nr_queues; /* Number of queues supported */ + u32 qlen; /* Queue length */ + struct pending_queue queue[CPT_NUM_QS_PER_VF]; +}; + +#define for_each_pending_queue(qinfo, q, i) \ + for (i = 0, q = &qinfo->queue[i]; i < qinfo->nr_queues; i++, \ + q = &qinfo->queue[i]) + +struct cpt_vf { + u16 flags; /* Flags to hold device status bits */ + u8 vfid; /* Device Index 0...CPT_MAX_VF_NUM */ + u8 vftype; /* VF type of SE_TYPE(1) or AE_TYPE(1) */ + u8 vfgrp; /* VF group (0 - 8) */ + u8 node; /* Operating node: Bits (46:44) in BAR0 address */ + u8 priority; /* VF priority ring: 1-High proirity round + * robin ring;0-Low priority round robin ring; + */ + struct pci_dev *pdev; /* pci device handle */ + void __iomem *reg_base; /* Register start address */ + void *wqe_info; /* BH worker info */ + /* MSI-X */ + cpumask_var_t affinity_mask[CPT_VF_MSIX_VECTORS]; + /* Command and Pending queues */ + u32 qsize; + u32 nr_queues; + struct command_qinfo cqinfo; /* Command queue information */ + struct pending_qinfo pqinfo; /* Pending queue information */ + /* VF-PF mailbox communication */ + bool pf_acked; + bool pf_nacked; +}; + +int cptvf_send_vf_up(struct cpt_vf *cptvf); +int cptvf_send_vf_down(struct cpt_vf *cptvf); +int cptvf_send_vf_to_grp_msg(struct cpt_vf *cptvf); +int cptvf_send_vf_priority_msg(struct cpt_vf *cptvf); +int cptvf_send_vq_size_msg(struct cpt_vf *cptvf); +int cptvf_check_pf_ready(struct cpt_vf *cptvf); +void cptvf_handle_mbox_intr(struct cpt_vf *cptvf); +void cvm_crypto_exit(void); +int cvm_crypto_init(struct cpt_vf *cptvf); +void vq_post_process(struct cpt_vf *cptvf, u32 qno); +void cptvf_write_vq_doorbell(struct cpt_vf *cptvf, u32 val); +#endif /* __CPTVF_H */ diff --git a/drivers/crypto/cavium/cpt/cptvf_algs.c b/drivers/crypto/cavium/cpt/cptvf_algs.c new file mode 100644 index 000000000000..cc853f913d4b --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptvf_algs.c @@ -0,0 +1,444 @@ + +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include <crypto/aes.h> +#include <crypto/algapi.h> +#include <crypto/authenc.h> +#include <crypto/cryptd.h> +#include <crypto/crypto_wq.h> +#include <crypto/des.h> +#include <crypto/xts.h> +#include <linux/crypto.h> +#include <linux/err.h> +#include <linux/list.h> +#include <linux/scatterlist.h> + +#include "cptvf.h" +#include "cptvf_algs.h" + +struct cpt_device_handle { + void *cdev[MAX_DEVICES]; + u32 dev_count; +}; + +static struct cpt_device_handle dev_handle; + +static void cvm_callback(u32 status, void *arg) +{ + struct crypto_async_request *req = (struct crypto_async_request *)arg; + + req->complete(req, !status); +} + +static inline void update_input_iv(struct cpt_request_info *req_info, + u8 *iv, u32 enc_iv_len, + u32 *argcnt) +{ + /* Setting the iv information */ + req_info->in[*argcnt].vptr = (void *)iv; + req_info->in[*argcnt].size = enc_iv_len; + req_info->req.dlen += enc_iv_len; + + ++(*argcnt); +} + +static inline void update_output_iv(struct cpt_request_info *req_info, + u8 *iv, u32 enc_iv_len, + u32 *argcnt) +{ + /* Setting the iv information */ + req_info->out[*argcnt].vptr = (void *)iv; + req_info->out[*argcnt].size = enc_iv_len; + req_info->rlen += enc_iv_len; + + ++(*argcnt); +} + +static inline void update_input_data(struct cpt_request_info *req_info, + struct scatterlist *inp_sg, + u32 nbytes, u32 *argcnt) +{ + req_info->req.dlen += nbytes; + + while (nbytes) { + u32 len = min(nbytes, inp_sg->length); + u8 *ptr = sg_virt(inp_sg); + + req_info->in[*argcnt].vptr = (void *)ptr; + req_info->in[*argcnt].size = len; + nbytes -= len; + + ++(*argcnt); + ++inp_sg; + } +} + +static inline void update_output_data(struct cpt_request_info *req_info, + struct scatterlist *outp_sg, + u32 nbytes, u32 *argcnt) +{ + req_info->rlen += nbytes; + + while (nbytes) { + u32 len = min(nbytes, outp_sg->length); + u8 *ptr = sg_virt(outp_sg); + + req_info->out[*argcnt].vptr = (void *)ptr; + req_info->out[*argcnt].size = len; + nbytes -= len; + ++(*argcnt); + ++outp_sg; + } +} + +static inline u32 create_ctx_hdr(struct ablkcipher_request *req, u32 enc, + u32 cipher_type, u32 aes_key_type, + u32 *argcnt) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct cvm_enc_ctx *ctx = crypto_ablkcipher_ctx(tfm); + struct cvm_req_ctx *rctx = ablkcipher_request_ctx(req); + struct fc_context *fctx = &rctx->fctx; + u64 *offset_control = &rctx->control_word; + u32 enc_iv_len = crypto_ablkcipher_ivsize(tfm); + struct cpt_request_info *req_info = &rctx->cpt_req; + u64 *ctrl_flags = NULL; + + req_info->ctrl.s.grp = 0; + req_info->ctrl.s.dma_mode = DMA_GATHER_SCATTER; + req_info->ctrl.s.se_req = SE_CORE_REQ; + + req_info->req.opcode.s.major = MAJOR_OP_FC | + DMA_MODE_FLAG(DMA_GATHER_SCATTER); + if (enc) + req_info->req.opcode.s.minor = 2; + else + req_info->req.opcode.s.minor = 3; + + req_info->req.param1 = req->nbytes; /* Encryption Data length */ + req_info->req.param2 = 0; /*Auth data length */ + + fctx->enc.enc_ctrl.e.enc_cipher = cipher_type; + fctx->enc.enc_ctrl.e.aes_key = aes_key_type; + fctx->enc.enc_ctrl.e.iv_source = FROM_DPTR; + + if (cipher_type == AES_XTS) + memcpy(fctx->enc.encr_key, ctx->enc_key, ctx->key_len * 2); + else + memcpy(fctx->enc.encr_key, ctx->enc_key, ctx->key_len); + ctrl_flags = (u64 *)&fctx->enc.enc_ctrl.flags; + *ctrl_flags = cpu_to_be64(*ctrl_flags); + + *offset_control = cpu_to_be64(((u64)(enc_iv_len) << 16)); + /* Storing Packet Data Information in offset + * Control Word First 8 bytes + */ + req_info->in[*argcnt].vptr = (u8 *)offset_control; + req_info->in[*argcnt].size = CONTROL_WORD_LEN; + req_info->req.dlen += CONTROL_WORD_LEN; + ++(*argcnt); + + req_info->in[*argcnt].vptr = (u8 *)fctx; + req_info->in[*argcnt].size = sizeof(struct fc_context); + req_info->req.dlen += sizeof(struct fc_context); + + ++(*argcnt); + + return 0; +} + +static inline u32 create_input_list(struct ablkcipher_request *req, u32 enc, + u32 cipher_type, u32 aes_key_type, + u32 enc_iv_len) +{ + struct cvm_req_ctx *rctx = ablkcipher_request_ctx(req); + struct cpt_request_info *req_info = &rctx->cpt_req; + u32 argcnt = 0; + + create_ctx_hdr(req, enc, cipher_type, aes_key_type, &argcnt); + update_input_iv(req_info, req->info, enc_iv_len, &argcnt); + update_input_data(req_info, req->src, req->nbytes, &argcnt); + req_info->incnt = argcnt; + + return 0; +} + +static inline void store_cb_info(struct ablkcipher_request *req, + struct cpt_request_info *req_info) +{ + req_info->callback = (void *)cvm_callback; + req_info->callback_arg = (void *)&req->base; +} + +static inline void create_output_list(struct ablkcipher_request *req, + u32 cipher_type, + u32 enc_iv_len) +{ + struct cvm_req_ctx *rctx = ablkcipher_request_ctx(req); + struct cpt_request_info *req_info = &rctx->cpt_req; + u32 argcnt = 0; + + /* OUTPUT Buffer Processing + * AES encryption/decryption output would be + * received in the following format + * + * ------IV--------|------ENCRYPTED/DECRYPTED DATA-----| + * [ 16 Bytes/ [ Request Enc/Dec/ DATA Len AES CBC ] + */ + /* Reading IV information */ + update_output_iv(req_info, req->info, enc_iv_len, &argcnt); + update_output_data(req_info, req->dst, req->nbytes, &argcnt); + req_info->outcnt = argcnt; +} + +static inline int cvm_enc_dec(struct ablkcipher_request *req, u32 enc, + u32 cipher_type) +{ + struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); + struct cvm_enc_ctx *ctx = crypto_ablkcipher_ctx(tfm); + u32 key_type = AES_128_BIT; + struct cvm_req_ctx *rctx = ablkcipher_request_ctx(req); + u32 enc_iv_len = crypto_ablkcipher_ivsize(tfm); + struct fc_context *fctx = &rctx->fctx; + struct cpt_request_info *req_info = &rctx->cpt_req; + void *cdev = NULL; + int status; + + switch (ctx->key_len) { + case 16: + key_type = AES_128_BIT; + break; + case 24: + key_type = AES_192_BIT; + break; + case 32: + if (cipher_type == AES_XTS) + key_type = AES_128_BIT; + else + key_type = AES_256_BIT; + break; + case 64: + if (cipher_type == AES_XTS) + key_type = AES_256_BIT; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (cipher_type == DES3_CBC) + key_type = 0; + + memset(req_info, 0, sizeof(struct cpt_request_info)); + memset(fctx, 0, sizeof(struct fc_context)); + create_input_list(req, enc, cipher_type, key_type, enc_iv_len); + create_output_list(req, cipher_type, enc_iv_len); + store_cb_info(req, req_info); + cdev = dev_handle.cdev[smp_processor_id()]; + status = cptvf_do_request(cdev, req_info); + /* We perform an asynchronous send and once + * the request is completed the driver would + * intimate through registered call back functions + */ + + if (status) + return status; + else + return -EINPROGRESS; +} + +int cvm_des3_encrypt_cbc(struct ablkcipher_request *req) +{ + return cvm_enc_dec(req, true, DES3_CBC); +} + +int cvm_des3_decrypt_cbc(struct ablkcipher_request *req) +{ + return cvm_enc_dec(req, false, DES3_CBC); +} + +int cvm_aes_encrypt_xts(struct ablkcipher_request *req) +{ + return cvm_enc_dec(req, true, AES_XTS); +} + +int cvm_aes_decrypt_xts(struct ablkcipher_request *req) +{ + return cvm_enc_dec(req, false, AES_XTS); +} + +int cvm_aes_encrypt_cbc(struct ablkcipher_request *req) +{ + return cvm_enc_dec(req, true, AES_CBC); +} + +int cvm_aes_decrypt_cbc(struct ablkcipher_request *req) +{ + return cvm_enc_dec(req, false, AES_CBC); +} + +int cvm_xts_setkey(struct crypto_ablkcipher *cipher, const u8 *key, + u32 keylen) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher); + struct cvm_enc_ctx *ctx = crypto_tfm_ctx(tfm); + int err; + const u8 *key1 = key; + const u8 *key2 = key + (keylen / 2); + + err = xts_check_key(tfm, key, keylen); + if (err) + return err; + ctx->key_len = keylen; + memcpy(ctx->enc_key, key1, keylen / 2); + memcpy(ctx->enc_key + KEY2_OFFSET, key2, keylen / 2); + + return 0; +} + +int cvm_enc_dec_setkey(struct crypto_ablkcipher *cipher, const u8 *key, + u32 keylen) +{ + struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher); + struct cvm_enc_ctx *ctx = crypto_tfm_ctx(tfm); + + if ((keylen == 16) || (keylen == 24) || (keylen == 32)) { + ctx->key_len = keylen; + memcpy(ctx->enc_key, key, keylen); + return 0; + } + crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN); + + return -EINVAL; +} + +int cvm_enc_dec_init(struct crypto_tfm *tfm) +{ + struct cvm_enc_ctx *ctx = crypto_tfm_ctx(tfm); + + memset(ctx, 0, sizeof(*ctx)); + tfm->crt_ablkcipher.reqsize = sizeof(struct cvm_req_ctx) + + sizeof(struct ablkcipher_request); + /* Additional memory for ablkcipher_request is + * allocated since the cryptd daemon uses + * this memory for request_ctx information + */ + + return 0; +} + +struct crypto_alg algs[] = { { + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct cvm_enc_ctx), + .cra_alignmask = 7, + .cra_priority = 4001, + .cra_name = "xts(aes)", + .cra_driver_name = "cavium-xts-aes", + .cra_type = &crypto_ablkcipher_type, + .cra_u = { + .ablkcipher = { + .ivsize = AES_BLOCK_SIZE, + .min_keysize = 2 * AES_MIN_KEY_SIZE, + .max_keysize = 2 * AES_MAX_KEY_SIZE, + .setkey = cvm_xts_setkey, + .encrypt = cvm_aes_encrypt_xts, + .decrypt = cvm_aes_decrypt_xts, + }, + }, + .cra_init = cvm_enc_dec_init, + .cra_module = THIS_MODULE, +}, { + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct cvm_enc_ctx), + .cra_alignmask = 7, + .cra_priority = 4001, + .cra_name = "cbc(aes)", + .cra_driver_name = "cavium-cbc-aes", + .cra_type = &crypto_ablkcipher_type, + .cra_u = { + .ablkcipher = { + .ivsize = AES_BLOCK_SIZE, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = cvm_enc_dec_setkey, + .encrypt = cvm_aes_encrypt_cbc, + .decrypt = cvm_aes_decrypt_cbc, + }, + }, + .cra_init = cvm_enc_dec_init, + .cra_module = THIS_MODULE, +}, { + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct cvm_des3_ctx), + .cra_alignmask = 7, + .cra_priority = 4001, + .cra_name = "cbc(des3_ede)", + .cra_driver_name = "cavium-cbc-des3_ede", + .cra_type = &crypto_ablkcipher_type, + .cra_u = { + .ablkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + .setkey = cvm_enc_dec_setkey, + .encrypt = cvm_des3_encrypt_cbc, + .decrypt = cvm_des3_decrypt_cbc, + }, + }, + .cra_init = cvm_enc_dec_init, + .cra_module = THIS_MODULE, +} }; + +static inline int cav_register_algs(void) +{ + int err = 0; + + err = crypto_register_algs(algs, ARRAY_SIZE(algs)); + if (err) + return err; + + return 0; +} + +static inline void cav_unregister_algs(void) +{ + crypto_unregister_algs(algs, ARRAY_SIZE(algs)); +} + +int cvm_crypto_init(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + u32 dev_count; + + dev_count = dev_handle.dev_count; + dev_handle.cdev[dev_count] = cptvf; + dev_handle.dev_count++; + + if (dev_count == 3) { + if (cav_register_algs()) { + dev_err(&pdev->dev, "Error in registering crypto algorithms\n"); + return -EINVAL; + } + } + + return 0; +} + +void cvm_crypto_exit(void) +{ + u32 dev_count; + + dev_count = --dev_handle.dev_count; + if (!dev_count) + cav_unregister_algs(); +} diff --git a/drivers/crypto/cavium/cpt/cptvf_algs.h b/drivers/crypto/cavium/cpt/cptvf_algs.h new file mode 100644 index 000000000000..a12050d11b0c --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptvf_algs.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef _CPTVF_ALGS_H_ +#define _CPTVF_ALGS_H_ + +#include "request_manager.h" + +#define MAX_DEVICES 16 +#define MAJOR_OP_FC 0x33 +#define MAX_ENC_KEY_SIZE 32 +#define MAX_HASH_KEY_SIZE 64 +#define MAX_KEY_SIZE (MAX_ENC_KEY_SIZE + MAX_HASH_KEY_SIZE) +#define CONTROL_WORD_LEN 8 +#define KEY2_OFFSET 48 + +#define DMA_MODE_FLAG(dma_mode) \ + (((dma_mode) == DMA_GATHER_SCATTER) ? (1 << 7) : 0) + +enum req_type { + AE_CORE_REQ, + SE_CORE_REQ, +}; + +enum cipher_type { + DES3_CBC = 0x1, + DES3_ECB = 0x2, + AES_CBC = 0x3, + AES_ECB = 0x4, + AES_CFB = 0x5, + AES_CTR = 0x6, + AES_GCM = 0x7, + AES_XTS = 0x8 +}; + +enum aes_type { + AES_128_BIT = 0x1, + AES_192_BIT = 0x2, + AES_256_BIT = 0x3 +}; + +union encr_ctrl { + u64 flags; + struct { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 enc_cipher:4; + u64 reserved1:1; + u64 aes_key:2; + u64 iv_source:1; + u64 hash_type:4; + u64 reserved2:3; + u64 auth_input_type:1; + u64 mac_len:8; + u64 reserved3:8; + u64 encr_offset:16; + u64 iv_offset:8; + u64 auth_offset:8; +#else + u64 auth_offset:8; + u64 iv_offset:8; + u64 encr_offset:16; + u64 reserved3:8; + u64 mac_len:8; + u64 auth_input_type:1; + u64 reserved2:3; + u64 hash_type:4; + u64 iv_source:1; + u64 aes_key:2; + u64 reserved1:1; + u64 enc_cipher:4; +#endif + } e; +}; + +struct enc_context { + union encr_ctrl enc_ctrl; + u8 encr_key[32]; + u8 encr_iv[16]; +}; + +struct fchmac_context { + u8 ipad[64]; + u8 opad[64]; /* or OPAD */ +}; + +struct fc_context { + struct enc_context enc; + struct fchmac_context hmac; +}; + +struct cvm_enc_ctx { + u32 key_len; + u8 enc_key[MAX_KEY_SIZE]; +}; + +struct cvm_des3_ctx { + u32 key_len; + u8 des3_key[MAX_KEY_SIZE]; +}; + +struct cvm_req_ctx { + struct cpt_request_info cpt_req; + u64 control_word; + struct fc_context fctx; +}; + +int cptvf_do_request(void *cptvf, struct cpt_request_info *req); +#endif /*_CPTVF_ALGS_H_*/ diff --git a/drivers/crypto/cavium/cpt/cptvf_main.c b/drivers/crypto/cavium/cpt/cptvf_main.c new file mode 100644 index 000000000000..6ffc740c7431 --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptvf_main.c @@ -0,0 +1,866 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include <linux/interrupt.h> +#include <linux/module.h> + +#include "cptvf.h" + +#define DRV_NAME "thunder-cptvf" +#define DRV_VERSION "1.0" + +struct cptvf_wqe { + struct tasklet_struct twork; + void *cptvf; + u32 qno; +}; + +struct cptvf_wqe_info { + struct cptvf_wqe vq_wqe[CPT_NUM_QS_PER_VF]; +}; + +static void vq_work_handler(unsigned long data) +{ + struct cptvf_wqe_info *cwqe_info = (struct cptvf_wqe_info *)data; + struct cptvf_wqe *cwqe = &cwqe_info->vq_wqe[0]; + + vq_post_process(cwqe->cptvf, cwqe->qno); +} + +static int init_worker_threads(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + struct cptvf_wqe_info *cwqe_info; + int i; + + cwqe_info = kzalloc(sizeof(*cwqe_info), GFP_KERNEL); + if (!cwqe_info) + return -ENOMEM; + + if (cptvf->nr_queues) { + dev_info(&pdev->dev, "Creating VQ worker threads (%d)\n", + cptvf->nr_queues); + } + + for (i = 0; i < cptvf->nr_queues; i++) { + tasklet_init(&cwqe_info->vq_wqe[i].twork, vq_work_handler, + (u64)cwqe_info); + cwqe_info->vq_wqe[i].qno = i; + cwqe_info->vq_wqe[i].cptvf = cptvf; + } + + cptvf->wqe_info = cwqe_info; + + return 0; +} + +static void cleanup_worker_threads(struct cpt_vf *cptvf) +{ + struct cptvf_wqe_info *cwqe_info; + struct pci_dev *pdev = cptvf->pdev; + int i; + + cwqe_info = (struct cptvf_wqe_info *)cptvf->wqe_info; + if (!cwqe_info) + return; + + if (cptvf->nr_queues) { + dev_info(&pdev->dev, "Cleaning VQ worker threads (%u)\n", + cptvf->nr_queues); + } + + for (i = 0; i < cptvf->nr_queues; i++) + tasklet_kill(&cwqe_info->vq_wqe[i].twork); + + kzfree(cwqe_info); + cptvf->wqe_info = NULL; +} + +static void free_pending_queues(struct pending_qinfo *pqinfo) +{ + int i; + struct pending_queue *queue; + + for_each_pending_queue(pqinfo, queue, i) { + if (!queue->head) + continue; + + /* free single queue */ + kzfree((queue->head)); + + queue->front = 0; + queue->rear = 0; + + return; + } + + pqinfo->qlen = 0; + pqinfo->nr_queues = 0; +} + +static int alloc_pending_queues(struct pending_qinfo *pqinfo, u32 qlen, + u32 nr_queues) +{ + u32 i; + size_t size; + int ret; + struct pending_queue *queue = NULL; + + pqinfo->nr_queues = nr_queues; + pqinfo->qlen = qlen; + + size = (qlen * sizeof(struct pending_entry)); + + for_each_pending_queue(pqinfo, queue, i) { + queue->head = kzalloc((size), GFP_KERNEL); + if (!queue->head) { + ret = -ENOMEM; + goto pending_qfail; + } + + queue->front = 0; + queue->rear = 0; + atomic64_set((&queue->pending_count), (0)); + + /* init queue spin lock */ + spin_lock_init(&queue->lock); + } + + return 0; + +pending_qfail: + free_pending_queues(pqinfo); + + return ret; +} + +static int init_pending_queues(struct cpt_vf *cptvf, u32 qlen, u32 nr_queues) +{ + struct pci_dev *pdev = cptvf->pdev; + int ret; + + if (!nr_queues) + return 0; + + ret = alloc_pending_queues(&cptvf->pqinfo, qlen, nr_queues); + if (ret) { + dev_err(&pdev->dev, "failed to setup pending queues (%u)\n", + nr_queues); + return ret; + } + + return 0; +} + +static void cleanup_pending_queues(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + + if (!cptvf->nr_queues) + return; + + dev_info(&pdev->dev, "Cleaning VQ pending queue (%u)\n", + cptvf->nr_queues); + free_pending_queues(&cptvf->pqinfo); +} + +static void free_command_queues(struct cpt_vf *cptvf, + struct command_qinfo *cqinfo) +{ + int i; + struct command_queue *queue = NULL; + struct command_chunk *chunk = NULL; + struct pci_dev *pdev = cptvf->pdev; + struct hlist_node *node; + + /* clean up for each queue */ + for (i = 0; i < cptvf->nr_queues; i++) { + queue = &cqinfo->queue[i]; + if (hlist_empty(&cqinfo->queue[i].chead)) + continue; + + hlist_for_each_entry_safe(chunk, node, &cqinfo->queue[i].chead, + nextchunk) { + dma_free_coherent(&pdev->dev, chunk->size, + chunk->head, + chunk->dma_addr); + chunk->head = NULL; + chunk->dma_addr = 0; + hlist_del(&chunk->nextchunk); + kzfree(chunk); + } + + queue->nchunks = 0; + queue->idx = 0; + } + + /* common cleanup */ + cqinfo->cmd_size = 0; +} + +static int alloc_command_queues(struct cpt_vf *cptvf, + struct command_qinfo *cqinfo, size_t cmd_size, + u32 qlen) +{ + int i; + size_t q_size; + struct command_queue *queue = NULL; + struct pci_dev *pdev = cptvf->pdev; + + /* common init */ + cqinfo->cmd_size = cmd_size; + /* Qsize in dwords, needed for SADDR config, 1-next chunk pointer */ + cptvf->qsize = min(qlen, cqinfo->qchunksize) * + CPT_NEXT_CHUNK_PTR_SIZE + 1; + /* Qsize in bytes to create space for alignment */ + q_size = qlen * cqinfo->cmd_size; + + /* per queue initialization */ + for (i = 0; i < cptvf->nr_queues; i++) { + size_t c_size = 0; + size_t rem_q_size = q_size; + struct command_chunk *curr = NULL, *first = NULL, *last = NULL; + u32 qcsize_bytes = cqinfo->qchunksize * cqinfo->cmd_size; + + queue = &cqinfo->queue[i]; + INIT_HLIST_HEAD(&cqinfo->queue[i].chead); + do { + curr = kzalloc(sizeof(*curr), GFP_KERNEL); + if (!curr) + goto cmd_qfail; + + c_size = (rem_q_size > qcsize_bytes) ? qcsize_bytes : + rem_q_size; + curr->head = (u8 *)dma_zalloc_coherent(&pdev->dev, + c_size + CPT_NEXT_CHUNK_PTR_SIZE, + &curr->dma_addr, GFP_KERNEL); + if (!curr->head) { + dev_err(&pdev->dev, "Command Q (%d) chunk (%d) allocation failed\n", + i, queue->nchunks); + kfree(curr); + goto cmd_qfail; + } + + curr->size = c_size; + if (queue->nchunks == 0) { + hlist_add_head(&curr->nextchunk, + &cqinfo->queue[i].chead); + first = curr; + } else { + hlist_add_behind(&curr->nextchunk, + &last->nextchunk); + } + + queue->nchunks++; + rem_q_size -= c_size; + if (last) + *((u64 *)(&last->head[last->size])) = (u64)curr->dma_addr; + + last = curr; + } while (rem_q_size); + + /* Make the queue circular */ + /* Tie back last chunk entry to head */ + curr = first; + *((u64 *)(&last->head[last->size])) = (u64)curr->dma_addr; + queue->qhead = curr; + spin_lock_init(&queue->lock); + } + return 0; + +cmd_qfail: + free_command_queues(cptvf, cqinfo); + return -ENOMEM; +} + +static int init_command_queues(struct cpt_vf *cptvf, u32 qlen) +{ + struct pci_dev *pdev = cptvf->pdev; + int ret; + + /* setup AE command queues */ + ret = alloc_command_queues(cptvf, &cptvf->cqinfo, CPT_INST_SIZE, + qlen); + if (ret) { + dev_err(&pdev->dev, "failed to allocate AE command queues (%u)\n", + cptvf->nr_queues); + return ret; + } + + return ret; +} + +static void cleanup_command_queues(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + + if (!cptvf->nr_queues) + return; + + dev_info(&pdev->dev, "Cleaning VQ command queue (%u)\n", + cptvf->nr_queues); + free_command_queues(cptvf, &cptvf->cqinfo); +} + +static void cptvf_sw_cleanup(struct cpt_vf *cptvf) +{ + cleanup_worker_threads(cptvf); + cleanup_pending_queues(cptvf); + cleanup_command_queues(cptvf); +} + +static int cptvf_sw_init(struct cpt_vf *cptvf, u32 qlen, u32 nr_queues) +{ + struct pci_dev *pdev = cptvf->pdev; + int ret = 0; + u32 max_dev_queues = 0; + + max_dev_queues = CPT_NUM_QS_PER_VF; + /* possible cpus */ + nr_queues = min_t(u32, nr_queues, max_dev_queues); + cptvf->nr_queues = nr_queues; + + ret = init_command_queues(cptvf, qlen); + if (ret) { + dev_err(&pdev->dev, "Failed to setup command queues (%u)\n", + nr_queues); + return ret; + } + + ret = init_pending_queues(cptvf, qlen, nr_queues); + if (ret) { + dev_err(&pdev->dev, "Failed to setup pending queues (%u)\n", + nr_queues); + goto setup_pqfail; + } + + /* Create worker threads for BH processing */ + ret = init_worker_threads(cptvf); + if (ret) { + dev_err(&pdev->dev, "Failed to setup worker threads\n"); + goto init_work_fail; + } + + return 0; + +init_work_fail: + cleanup_worker_threads(cptvf); + cleanup_pending_queues(cptvf); + +setup_pqfail: + cleanup_command_queues(cptvf); + + return ret; +} + +static void cptvf_free_irq_affinity(struct cpt_vf *cptvf, int vec) +{ + irq_set_affinity_hint(pci_irq_vector(cptvf->pdev, vec), NULL); + free_cpumask_var(cptvf->affinity_mask[vec]); +} + +static void cptvf_write_vq_ctl(struct cpt_vf *cptvf, bool val) +{ + union cptx_vqx_ctl vqx_ctl; + + vqx_ctl.u = cpt_read_csr64(cptvf->reg_base, CPTX_VQX_CTL(0, 0)); + vqx_ctl.s.ena = val; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_CTL(0, 0), vqx_ctl.u); +} + +void cptvf_write_vq_doorbell(struct cpt_vf *cptvf, u32 val) +{ + union cptx_vqx_doorbell vqx_dbell; + + vqx_dbell.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_DOORBELL(0, 0)); + vqx_dbell.s.dbell_cnt = val * 8; /* Num of Instructions * 8 words */ + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_DOORBELL(0, 0), + vqx_dbell.u); +} + +static void cptvf_write_vq_inprog(struct cpt_vf *cptvf, u8 val) +{ + union cptx_vqx_inprog vqx_inprg; + + vqx_inprg.u = cpt_read_csr64(cptvf->reg_base, CPTX_VQX_INPROG(0, 0)); + vqx_inprg.s.inflight = val; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_INPROG(0, 0), vqx_inprg.u); +} + +static void cptvf_write_vq_done_numwait(struct cpt_vf *cptvf, u32 val) +{ + union cptx_vqx_done_wait vqx_dwait; + + vqx_dwait.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_DONE_WAIT(0, 0)); + vqx_dwait.s.num_wait = val; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_DONE_WAIT(0, 0), + vqx_dwait.u); +} + +static void cptvf_write_vq_done_timewait(struct cpt_vf *cptvf, u16 time) +{ + union cptx_vqx_done_wait vqx_dwait; + + vqx_dwait.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_DONE_WAIT(0, 0)); + vqx_dwait.s.time_wait = time; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_DONE_WAIT(0, 0), + vqx_dwait.u); +} + +static void cptvf_enable_swerr_interrupts(struct cpt_vf *cptvf) +{ + union cptx_vqx_misc_ena_w1s vqx_misc_ena; + + vqx_misc_ena.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_MISC_ENA_W1S(0, 0)); + /* Set mbox(0) interupts for the requested vf */ + vqx_misc_ena.s.swerr = 1; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_MISC_ENA_W1S(0, 0), + vqx_misc_ena.u); +} + +static void cptvf_enable_mbox_interrupts(struct cpt_vf *cptvf) +{ + union cptx_vqx_misc_ena_w1s vqx_misc_ena; + + vqx_misc_ena.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_MISC_ENA_W1S(0, 0)); + /* Set mbox(0) interupts for the requested vf */ + vqx_misc_ena.s.mbox = 1; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_MISC_ENA_W1S(0, 0), + vqx_misc_ena.u); +} + +static void cptvf_enable_done_interrupts(struct cpt_vf *cptvf) +{ + union cptx_vqx_done_ena_w1s vqx_done_ena; + + vqx_done_ena.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_DONE_ENA_W1S(0, 0)); + /* Set DONE interrupt for the requested vf */ + vqx_done_ena.s.done = 1; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_DONE_ENA_W1S(0, 0), + vqx_done_ena.u); +} + +static void cptvf_clear_dovf_intr(struct cpt_vf *cptvf) +{ + union cptx_vqx_misc_int vqx_misc_int; + + vqx_misc_int.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_MISC_INT(0, 0)); + /* W1C for the VF */ + vqx_misc_int.s.dovf = 1; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_MISC_INT(0, 0), + vqx_misc_int.u); +} + +static void cptvf_clear_irde_intr(struct cpt_vf *cptvf) +{ + union cptx_vqx_misc_int vqx_misc_int; + + vqx_misc_int.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_MISC_INT(0, 0)); + /* W1C for the VF */ + vqx_misc_int.s.irde = 1; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_MISC_INT(0, 0), + vqx_misc_int.u); +} + +static void cptvf_clear_nwrp_intr(struct cpt_vf *cptvf) +{ + union cptx_vqx_misc_int vqx_misc_int; + + vqx_misc_int.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_MISC_INT(0, 0)); + /* W1C for the VF */ + vqx_misc_int.s.nwrp = 1; + cpt_write_csr64(cptvf->reg_base, + CPTX_VQX_MISC_INT(0, 0), vqx_misc_int.u); +} + +static void cptvf_clear_mbox_intr(struct cpt_vf *cptvf) +{ + union cptx_vqx_misc_int vqx_misc_int; + + vqx_misc_int.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_MISC_INT(0, 0)); + /* W1C for the VF */ + vqx_misc_int.s.mbox = 1; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_MISC_INT(0, 0), + vqx_misc_int.u); +} + +static void cptvf_clear_swerr_intr(struct cpt_vf *cptvf) +{ + union cptx_vqx_misc_int vqx_misc_int; + + vqx_misc_int.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_MISC_INT(0, 0)); + /* W1C for the VF */ + vqx_misc_int.s.swerr = 1; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_MISC_INT(0, 0), + vqx_misc_int.u); +} + +static u64 cptvf_read_vf_misc_intr_status(struct cpt_vf *cptvf) +{ + return cpt_read_csr64(cptvf->reg_base, CPTX_VQX_MISC_INT(0, 0)); +} + +static irqreturn_t cptvf_misc_intr_handler(int irq, void *cptvf_irq) +{ + struct cpt_vf *cptvf = (struct cpt_vf *)cptvf_irq; + struct pci_dev *pdev = cptvf->pdev; + u64 intr; + + intr = cptvf_read_vf_misc_intr_status(cptvf); + /*Check for MISC interrupt types*/ + if (likely(intr & CPT_VF_INTR_MBOX_MASK)) { + dev_err(&pdev->dev, "Mailbox interrupt 0x%llx on CPT VF %d\n", + intr, cptvf->vfid); + cptvf_handle_mbox_intr(cptvf); + cptvf_clear_mbox_intr(cptvf); + } else if (unlikely(intr & CPT_VF_INTR_DOVF_MASK)) { + cptvf_clear_dovf_intr(cptvf); + /*Clear doorbell count*/ + cptvf_write_vq_doorbell(cptvf, 0); + dev_err(&pdev->dev, "Doorbell overflow error interrupt 0x%llx on CPT VF %d\n", + intr, cptvf->vfid); + } else if (unlikely(intr & CPT_VF_INTR_IRDE_MASK)) { + cptvf_clear_irde_intr(cptvf); + dev_err(&pdev->dev, "Instruction NCB read error interrupt 0x%llx on CPT VF %d\n", + intr, cptvf->vfid); + } else if (unlikely(intr & CPT_VF_INTR_NWRP_MASK)) { + cptvf_clear_nwrp_intr(cptvf); + dev_err(&pdev->dev, "NCB response write error interrupt 0x%llx on CPT VF %d\n", + intr, cptvf->vfid); + } else if (unlikely(intr & CPT_VF_INTR_SERR_MASK)) { + cptvf_clear_swerr_intr(cptvf); + dev_err(&pdev->dev, "Software error interrupt 0x%llx on CPT VF %d\n", + intr, cptvf->vfid); + } else { + dev_err(&pdev->dev, "Unhandled interrupt in CPT VF %d\n", + cptvf->vfid); + } + + return IRQ_HANDLED; +} + +static inline struct cptvf_wqe *get_cptvf_vq_wqe(struct cpt_vf *cptvf, + int qno) +{ + struct cptvf_wqe_info *nwqe_info; + + if (unlikely(qno >= cptvf->nr_queues)) + return NULL; + nwqe_info = (struct cptvf_wqe_info *)cptvf->wqe_info; + + return &nwqe_info->vq_wqe[qno]; +} + +static inline u32 cptvf_read_vq_done_count(struct cpt_vf *cptvf) +{ + union cptx_vqx_done vqx_done; + + vqx_done.u = cpt_read_csr64(cptvf->reg_base, CPTX_VQX_DONE(0, 0)); + return vqx_done.s.done; +} + +static inline void cptvf_write_vq_done_ack(struct cpt_vf *cptvf, + u32 ackcnt) +{ + union cptx_vqx_done_ack vqx_dack_cnt; + + vqx_dack_cnt.u = cpt_read_csr64(cptvf->reg_base, + CPTX_VQX_DONE_ACK(0, 0)); + vqx_dack_cnt.s.done_ack = ackcnt; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_DONE_ACK(0, 0), + vqx_dack_cnt.u); +} + +static irqreturn_t cptvf_done_intr_handler(int irq, void *cptvf_irq) +{ + struct cpt_vf *cptvf = (struct cpt_vf *)cptvf_irq; + struct pci_dev *pdev = cptvf->pdev; + /* Read the number of completions */ + u32 intr = cptvf_read_vq_done_count(cptvf); + + if (intr) { + struct cptvf_wqe *wqe; + + /* Acknowledge the number of + * scheduled completions for processing + */ + cptvf_write_vq_done_ack(cptvf, intr); + wqe = get_cptvf_vq_wqe(cptvf, 0); + if (unlikely(!wqe)) { + dev_err(&pdev->dev, "No work to schedule for VF (%d)", + cptvf->vfid); + return IRQ_NONE; + } + tasklet_hi_schedule(&wqe->twork); + } + + return IRQ_HANDLED; +} + +static void cptvf_set_irq_affinity(struct cpt_vf *cptvf, int vec) +{ + struct pci_dev *pdev = cptvf->pdev; + int cpu; + + if (!zalloc_cpumask_var(&cptvf->affinity_mask[vec], + GFP_KERNEL)) { + dev_err(&pdev->dev, "Allocation failed for affinity_mask for VF %d", + cptvf->vfid); + return; + } + + cpu = cptvf->vfid % num_online_cpus(); + cpumask_set_cpu(cpumask_local_spread(cpu, cptvf->node), + cptvf->affinity_mask[vec]); + irq_set_affinity_hint(pci_irq_vector(pdev, vec), + cptvf->affinity_mask[vec]); +} + +static void cptvf_write_vq_saddr(struct cpt_vf *cptvf, u64 val) +{ + union cptx_vqx_saddr vqx_saddr; + + vqx_saddr.u = val; + cpt_write_csr64(cptvf->reg_base, CPTX_VQX_SADDR(0, 0), vqx_saddr.u); +} + +void cptvf_device_init(struct cpt_vf *cptvf) +{ + u64 base_addr = 0; + + /* Disable the VQ */ + cptvf_write_vq_ctl(cptvf, 0); + /* Reset the doorbell */ + cptvf_write_vq_doorbell(cptvf, 0); + /* Clear inflight */ + cptvf_write_vq_inprog(cptvf, 0); + /* Write VQ SADDR */ + /* TODO: for now only one queue, so hard coded */ + base_addr = (u64)(cptvf->cqinfo.queue[0].qhead->dma_addr); + cptvf_write_vq_saddr(cptvf, base_addr); + /* Configure timerhold / coalescence */ + cptvf_write_vq_done_timewait(cptvf, CPT_TIMER_THOLD); + cptvf_write_vq_done_numwait(cptvf, 1); + /* Enable the VQ */ + cptvf_write_vq_ctl(cptvf, 1); + /* Flag the VF ready */ + cptvf->flags |= CPT_FLAG_DEVICE_READY; +} + +static int cptvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct cpt_vf *cptvf; + int err; + + cptvf = devm_kzalloc(dev, sizeof(*cptvf), GFP_KERNEL); + if (!cptvf) + return -ENOMEM; + + pci_set_drvdata(pdev, cptvf); + cptvf->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + pci_set_drvdata(pdev, NULL); + return err; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + goto cptvf_err_disable_device; + } + /* Mark as VF driver */ + cptvf->flags |= CPT_FLAG_VF_DRIVER; + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to get usable DMA configuration\n"); + goto cptvf_err_release_regions; + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); + goto cptvf_err_release_regions; + } + + /* MAP PF's configuration registers */ + cptvf->reg_base = pcim_iomap(pdev, 0, 0); + if (!cptvf->reg_base) { + dev_err(dev, "Cannot map config register space, aborting\n"); + err = -ENOMEM; + goto cptvf_err_release_regions; + } + + cptvf->node = dev_to_node(&pdev->dev); + err = pci_alloc_irq_vectors(pdev, CPT_VF_MSIX_VECTORS, + CPT_VF_MSIX_VECTORS, PCI_IRQ_MSIX); + if (err < 0) { + dev_err(dev, "Request for #%d msix vectors failed\n", + CPT_VF_MSIX_VECTORS); + goto cptvf_err_release_regions; + } + + err = request_irq(pci_irq_vector(pdev, CPT_VF_INT_VEC_E_MISC), + cptvf_misc_intr_handler, 0, "CPT VF misc intr", + cptvf); + if (err) { + dev_err(dev, "Request misc irq failed"); + goto cptvf_free_vectors; + } + + /* Enable mailbox interrupt */ + cptvf_enable_mbox_interrupts(cptvf); + cptvf_enable_swerr_interrupts(cptvf); + + /* Check ready with PF */ + /* Gets chip ID / device Id from PF if ready */ + err = cptvf_check_pf_ready(cptvf); + if (err) { + dev_err(dev, "PF not responding to READY msg"); + goto cptvf_free_misc_irq; + } + + /* CPT VF software resources initialization */ + cptvf->cqinfo.qchunksize = CPT_CMD_QCHUNK_SIZE; + err = cptvf_sw_init(cptvf, CPT_CMD_QLEN, CPT_NUM_QS_PER_VF); + if (err) { + dev_err(dev, "cptvf_sw_init() failed"); + goto cptvf_free_misc_irq; + } + /* Convey VQ LEN to PF */ + err = cptvf_send_vq_size_msg(cptvf); + if (err) { + dev_err(dev, "PF not responding to QLEN msg"); + goto cptvf_free_misc_irq; + } + + /* CPT VF device initialization */ + cptvf_device_init(cptvf); + /* Send msg to PF to assign currnet Q to required group */ + cptvf->vfgrp = 1; + err = cptvf_send_vf_to_grp_msg(cptvf); + if (err) { + dev_err(dev, "PF not responding to VF_GRP msg"); + goto cptvf_free_misc_irq; + } + + cptvf->priority = 1; + err = cptvf_send_vf_priority_msg(cptvf); + if (err) { + dev_err(dev, "PF not responding to VF_PRIO msg"); + goto cptvf_free_misc_irq; + } + + err = request_irq(pci_irq_vector(pdev, CPT_VF_INT_VEC_E_DONE), + cptvf_done_intr_handler, 0, "CPT VF done intr", + cptvf); + if (err) { + dev_err(dev, "Request done irq failed\n"); + goto cptvf_free_misc_irq; + } + + /* Enable mailbox interrupt */ + cptvf_enable_done_interrupts(cptvf); + + /* Set irq affinity masks */ + cptvf_set_irq_affinity(cptvf, CPT_VF_INT_VEC_E_MISC); + cptvf_set_irq_affinity(cptvf, CPT_VF_INT_VEC_E_DONE); + + err = cptvf_send_vf_up(cptvf); + if (err) { + dev_err(dev, "PF not responding to UP msg"); + goto cptvf_free_irq_affinity; + } + err = cvm_crypto_init(cptvf); + if (err) { + dev_err(dev, "Algorithm register failed\n"); + goto cptvf_free_irq_affinity; + } + return 0; + +cptvf_free_irq_affinity: + cptvf_free_irq_affinity(cptvf, CPT_VF_INT_VEC_E_DONE); + cptvf_free_irq_affinity(cptvf, CPT_VF_INT_VEC_E_MISC); +cptvf_free_misc_irq: + free_irq(pci_irq_vector(pdev, CPT_VF_INT_VEC_E_MISC), cptvf); +cptvf_free_vectors: + pci_free_irq_vectors(cptvf->pdev); +cptvf_err_release_regions: + pci_release_regions(pdev); +cptvf_err_disable_device: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + return err; +} + +static void cptvf_remove(struct pci_dev *pdev) +{ + struct cpt_vf *cptvf = pci_get_drvdata(pdev); + + if (!cptvf) { + dev_err(&pdev->dev, "Invalid CPT-VF device\n"); + return; + } + + /* Convey DOWN to PF */ + if (cptvf_send_vf_down(cptvf)) { + dev_err(&pdev->dev, "PF not responding to DOWN msg"); + } else { + cptvf_free_irq_affinity(cptvf, CPT_VF_INT_VEC_E_DONE); + cptvf_free_irq_affinity(cptvf, CPT_VF_INT_VEC_E_MISC); + free_irq(pci_irq_vector(pdev, CPT_VF_INT_VEC_E_DONE), cptvf); + free_irq(pci_irq_vector(pdev, CPT_VF_INT_VEC_E_MISC), cptvf); + pci_free_irq_vectors(cptvf->pdev); + cptvf_sw_cleanup(cptvf); + pci_set_drvdata(pdev, NULL); + pci_release_regions(pdev); + pci_disable_device(pdev); + cvm_crypto_exit(); + } +} + +static void cptvf_shutdown(struct pci_dev *pdev) +{ + cptvf_remove(pdev); +} + +/* Supported devices */ +static const struct pci_device_id cptvf_id_table[] = { + {PCI_VDEVICE(CAVIUM, CPT_81XX_PCI_VF_DEVICE_ID), 0}, + { 0, } /* end of table */ +}; + +static struct pci_driver cptvf_pci_driver = { + .name = DRV_NAME, + .id_table = cptvf_id_table, + .probe = cptvf_probe, + .remove = cptvf_remove, + .shutdown = cptvf_shutdown, +}; + +module_pci_driver(cptvf_pci_driver); + +MODULE_AUTHOR("George Cherian <george.cherian@cavium.com>"); +MODULE_DESCRIPTION("Cavium Thunder CPT Virtual Function Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, cptvf_id_table); diff --git a/drivers/crypto/cavium/cpt/cptvf_mbox.c b/drivers/crypto/cavium/cpt/cptvf_mbox.c new file mode 100644 index 000000000000..d5ec3b8a9e61 --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptvf_mbox.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include "cptvf.h" + +static void cptvf_send_msg_to_pf(struct cpt_vf *cptvf, struct cpt_mbox *mbx) +{ + /* Writing mbox(1) causes interrupt */ + cpt_write_csr64(cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 0), + mbx->msg); + cpt_write_csr64(cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 1), + mbx->data); +} + +/* ACKs PF's mailbox message + */ +void cptvf_mbox_send_ack(struct cpt_vf *cptvf, struct cpt_mbox *mbx) +{ + mbx->msg = CPT_MBOX_MSG_TYPE_ACK; + cptvf_send_msg_to_pf(cptvf, mbx); +} + +/* NACKs PF's mailbox message that VF is not able to + * complete the action + */ +void cptvf_mbox_send_nack(struct cpt_vf *cptvf, struct cpt_mbox *mbx) +{ + mbx->msg = CPT_MBOX_MSG_TYPE_NACK; + cptvf_send_msg_to_pf(cptvf, mbx); +} + +/* Interrupt handler to handle mailbox messages from VFs */ +void cptvf_handle_mbox_intr(struct cpt_vf *cptvf) +{ + struct cpt_mbox mbx = {}; + + /* + * MBOX[0] contains msg + * MBOX[1] contains data + */ + mbx.msg = cpt_read_csr64(cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 0)); + mbx.data = cpt_read_csr64(cptvf->reg_base, CPTX_VFX_PF_MBOXX(0, 0, 1)); + dev_dbg(&cptvf->pdev->dev, "%s: Mailbox msg 0x%llx from PF\n", + __func__, mbx.msg); + switch (mbx.msg) { + case CPT_MSG_READY: + { + cptvf->pf_acked = true; + cptvf->vfid = mbx.data; + dev_dbg(&cptvf->pdev->dev, "Received VFID %d\n", cptvf->vfid); + break; + } + case CPT_MSG_QBIND_GRP: + cptvf->pf_acked = true; + cptvf->vftype = mbx.data; + dev_dbg(&cptvf->pdev->dev, "VF %d type %s group %d\n", + cptvf->vfid, ((mbx.data == SE_TYPES) ? "SE" : "AE"), + cptvf->vfgrp); + break; + case CPT_MBOX_MSG_TYPE_ACK: + cptvf->pf_acked = true; + break; + case CPT_MBOX_MSG_TYPE_NACK: + cptvf->pf_nacked = true; + break; + default: + dev_err(&cptvf->pdev->dev, "Invalid msg from PF, msg 0x%llx\n", + mbx.msg); + break; + } +} + +static int cptvf_send_msg_to_pf_timeout(struct cpt_vf *cptvf, + struct cpt_mbox *mbx) +{ + int timeout = CPT_MBOX_MSG_TIMEOUT; + int sleep = 10; + + cptvf->pf_acked = false; + cptvf->pf_nacked = false; + cptvf_send_msg_to_pf(cptvf, mbx); + /* Wait for previous message to be acked, timeout 2sec */ + while (!cptvf->pf_acked) { + if (cptvf->pf_nacked) + return -EINVAL; + msleep(sleep); + if (cptvf->pf_acked) + break; + timeout -= sleep; + if (!timeout) { + dev_err(&cptvf->pdev->dev, "PF didn't ack to mbox msg %llx from VF%u\n", + (mbx->msg & 0xFF), cptvf->vfid); + return -EBUSY; + } + } + + return 0; +} + +/* + * Checks if VF is able to comminicate with PF + * and also gets the CPT number this VF is associated to. + */ +int cptvf_check_pf_ready(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + struct cpt_mbox mbx = {}; + + mbx.msg = CPT_MSG_READY; + if (cptvf_send_msg_to_pf_timeout(cptvf, &mbx)) { + dev_err(&pdev->dev, "PF didn't respond to READY msg\n"); + return -EBUSY; + } + + return 0; +} + +/* + * Communicate VQs size to PF to program CPT(0)_PF_Q(0-15)_CTL of the VF. + * Must be ACKed. + */ +int cptvf_send_vq_size_msg(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + struct cpt_mbox mbx = {}; + + mbx.msg = CPT_MSG_QLEN; + mbx.data = cptvf->qsize; + if (cptvf_send_msg_to_pf_timeout(cptvf, &mbx)) { + dev_err(&pdev->dev, "PF didn't respond to vq_size msg\n"); + return -EBUSY; + } + + return 0; +} + +/* + * Communicate VF group required to PF and get the VQ binded to that group + */ +int cptvf_send_vf_to_grp_msg(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + struct cpt_mbox mbx = {}; + + mbx.msg = CPT_MSG_QBIND_GRP; + /* Convey group of the VF */ + mbx.data = cptvf->vfgrp; + if (cptvf_send_msg_to_pf_timeout(cptvf, &mbx)) { + dev_err(&pdev->dev, "PF didn't respond to vf_type msg\n"); + return -EBUSY; + } + + return 0; +} + +/* + * Communicate VF group required to PF and get the VQ binded to that group + */ +int cptvf_send_vf_priority_msg(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + struct cpt_mbox mbx = {}; + + mbx.msg = CPT_MSG_VQ_PRIORITY; + /* Convey group of the VF */ + mbx.data = cptvf->priority; + if (cptvf_send_msg_to_pf_timeout(cptvf, &mbx)) { + dev_err(&pdev->dev, "PF didn't respond to vf_type msg\n"); + return -EBUSY; + } + return 0; +} + +/* + * Communicate to PF that VF is UP and running + */ +int cptvf_send_vf_up(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + struct cpt_mbox mbx = {}; + + mbx.msg = CPT_MSG_VF_UP; + if (cptvf_send_msg_to_pf_timeout(cptvf, &mbx)) { + dev_err(&pdev->dev, "PF didn't respond to UP msg\n"); + return -EBUSY; + } + + return 0; +} + +/* + * Communicate to PF that VF is DOWN and running + */ +int cptvf_send_vf_down(struct cpt_vf *cptvf) +{ + struct pci_dev *pdev = cptvf->pdev; + struct cpt_mbox mbx = {}; + + mbx.msg = CPT_MSG_VF_DOWN; + if (cptvf_send_msg_to_pf_timeout(cptvf, &mbx)) { + dev_err(&pdev->dev, "PF didn't respond to DOWN msg\n"); + return -EBUSY; + } + + return 0; +} diff --git a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c new file mode 100644 index 000000000000..169e66231bcf --- /dev/null +++ b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include "cptvf.h" +#include "request_manager.h" + +/** + * get_free_pending_entry - get free entry from pending queue + * @param pqinfo: pending_qinfo structure + * @param qno: queue number + */ +static struct pending_entry *get_free_pending_entry(struct pending_queue *q, + int qlen) +{ + struct pending_entry *ent = NULL; + + ent = &q->head[q->rear]; + if (unlikely(ent->busy)) { + ent = NULL; + goto no_free_entry; + } + + q->rear++; + if (unlikely(q->rear == qlen)) + q->rear = 0; + +no_free_entry: + return ent; +} + +static inline void pending_queue_inc_front(struct pending_qinfo *pqinfo, + int qno) +{ + struct pending_queue *queue = &pqinfo->queue[qno]; + + queue->front++; + if (unlikely(queue->front == pqinfo->qlen)) + queue->front = 0; +} + +static int setup_sgio_components(struct cpt_vf *cptvf, struct buf_ptr *list, + int buf_count, u8 *buffer) +{ + int ret = 0, i, j; + int components; + struct sglist_component *sg_ptr = NULL; + struct pci_dev *pdev = cptvf->pdev; + + if (unlikely(!list)) { + dev_err(&pdev->dev, "Input List pointer is NULL\n"); + return -EFAULT; + } + + for (i = 0; i < buf_count; i++) { + if (likely(list[i].vptr)) { + list[i].dma_addr = dma_map_single(&pdev->dev, + list[i].vptr, + list[i].size, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(&pdev->dev, + list[i].dma_addr))) { + dev_err(&pdev->dev, "DMA map kernel buffer failed for component: %d\n", + i); + ret = -EIO; + goto sg_cleanup; + } + } + } + + components = buf_count / 4; + sg_ptr = (struct sglist_component *)buffer; + for (i = 0; i < components; i++) { + sg_ptr->u.s.len0 = cpu_to_be16(list[i * 4 + 0].size); + sg_ptr->u.s.len1 = cpu_to_be16(list[i * 4 + 1].size); + sg_ptr->u.s.len2 = cpu_to_be16(list[i * 4 + 2].size); + sg_ptr->u.s.len3 = cpu_to_be16(list[i * 4 + 3].size); + sg_ptr->ptr0 = cpu_to_be64(list[i * 4 + 0].dma_addr); + sg_ptr->ptr1 = cpu_to_be64(list[i * 4 + 1].dma_addr); + sg_ptr->ptr2 = cpu_to_be64(list[i * 4 + 2].dma_addr); + sg_ptr->ptr3 = cpu_to_be64(list[i * 4 + 3].dma_addr); + sg_ptr++; + } + + components = buf_count % 4; + + switch (components) { + case 3: + sg_ptr->u.s.len2 = cpu_to_be16(list[i * 4 + 2].size); + sg_ptr->ptr2 = cpu_to_be64(list[i * 4 + 2].dma_addr); + /* Fall through */ + case 2: + sg_ptr->u.s.len1 = cpu_to_be16(list[i * 4 + 1].size); + sg_ptr->ptr1 = cpu_to_be64(list[i * 4 + 1].dma_addr); + /* Fall through */ + case 1: + sg_ptr->u.s.len0 = cpu_to_be16(list[i * 4 + 0].size); + sg_ptr->ptr0 = cpu_to_be64(list[i * 4 + 0].dma_addr); + break; + default: + break; + } + + return ret; + +sg_cleanup: + for (j = 0; j < i; j++) { + if (list[j].dma_addr) { + dma_unmap_single(&pdev->dev, list[i].dma_addr, + list[i].size, DMA_BIDIRECTIONAL); + } + + list[j].dma_addr = 0; + } + + return ret; +} + +static inline int setup_sgio_list(struct cpt_vf *cptvf, + struct cpt_info_buffer *info, + struct cpt_request_info *req) +{ + u16 g_sz_bytes = 0, s_sz_bytes = 0; + int ret = 0; + struct pci_dev *pdev = cptvf->pdev; + + if (req->incnt > MAX_SG_IN_CNT || req->outcnt > MAX_SG_OUT_CNT) { + dev_err(&pdev->dev, "Request SG components are higher than supported\n"); + ret = -EINVAL; + goto scatter_gather_clean; + } + + /* Setup gather (input) components */ + g_sz_bytes = ((req->incnt + 3) / 4) * sizeof(struct sglist_component); + info->gather_components = kzalloc(g_sz_bytes, GFP_KERNEL); + if (!info->gather_components) { + ret = -ENOMEM; + goto scatter_gather_clean; + } + + ret = setup_sgio_components(cptvf, req->in, + req->incnt, + info->gather_components); + if (ret) { + dev_err(&pdev->dev, "Failed to setup gather list\n"); + ret = -EFAULT; + goto scatter_gather_clean; + } + + /* Setup scatter (output) components */ + s_sz_bytes = ((req->outcnt + 3) / 4) * sizeof(struct sglist_component); + info->scatter_components = kzalloc(s_sz_bytes, GFP_KERNEL); + if (!info->scatter_components) { + ret = -ENOMEM; + goto scatter_gather_clean; + } + + ret = setup_sgio_components(cptvf, req->out, + req->outcnt, + info->scatter_components); + if (ret) { + dev_err(&pdev->dev, "Failed to setup gather list\n"); + ret = -EFAULT; + goto scatter_gather_clean; + } + + /* Create and initialize DPTR */ + info->dlen = g_sz_bytes + s_sz_bytes + SG_LIST_HDR_SIZE; + info->in_buffer = kzalloc(info->dlen, GFP_KERNEL); + if (!info->in_buffer) { + ret = -ENOMEM; + goto scatter_gather_clean; + } + + ((u16 *)info->in_buffer)[0] = req->outcnt; + ((u16 *)info->in_buffer)[1] = req->incnt; + ((u16 *)info->in_buffer)[2] = 0; + ((u16 *)info->in_buffer)[3] = 0; + *(u64 *)info->in_buffer = cpu_to_be64p((u64 *)info->in_buffer); + + memcpy(&info->in_buffer[8], info->gather_components, + g_sz_bytes); + memcpy(&info->in_buffer[8 + g_sz_bytes], + info->scatter_components, s_sz_bytes); + + info->dptr_baddr = dma_map_single(&pdev->dev, + (void *)info->in_buffer, + info->dlen, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(&pdev->dev, info->dptr_baddr)) { + dev_err(&pdev->dev, "Mapping DPTR Failed %d\n", info->dlen); + ret = -EIO; + goto scatter_gather_clean; + } + + /* Create and initialize RPTR */ + info->out_buffer = kzalloc(COMPLETION_CODE_SIZE, GFP_KERNEL); + if (!info->out_buffer) { + ret = -ENOMEM; + goto scatter_gather_clean; + } + + *((u64 *)info->out_buffer) = ~((u64)COMPLETION_CODE_INIT); + info->alternate_caddr = (u64 *)info->out_buffer; + info->rptr_baddr = dma_map_single(&pdev->dev, + (void *)info->out_buffer, + COMPLETION_CODE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(&pdev->dev, info->rptr_baddr)) { + dev_err(&pdev->dev, "Mapping RPTR Failed %d\n", + COMPLETION_CODE_SIZE); + ret = -EIO; + goto scatter_gather_clean; + } + + return 0; + +scatter_gather_clean: + return ret; +} + +int send_cpt_command(struct cpt_vf *cptvf, union cpt_inst_s *cmd, + u32 qno) +{ + struct pci_dev *pdev = cptvf->pdev; + struct command_qinfo *qinfo = NULL; + struct command_queue *queue; + struct command_chunk *chunk; + u8 *ent; + int ret = 0; + + if (unlikely(qno >= cptvf->nr_queues)) { + dev_err(&pdev->dev, "Invalid queue (qno: %d, nr_queues: %d)\n", + qno, cptvf->nr_queues); + return -EINVAL; + } + + qinfo = &cptvf->cqinfo; + queue = &qinfo->queue[qno]; + /* lock commad queue */ + spin_lock(&queue->lock); + ent = &queue->qhead->head[queue->idx * qinfo->cmd_size]; + memcpy(ent, (void *)cmd, qinfo->cmd_size); + + if (++queue->idx >= queue->qhead->size / 64) { + struct hlist_node *node; + + hlist_for_each(node, &queue->chead) { + chunk = hlist_entry(node, struct command_chunk, + nextchunk); + if (chunk == queue->qhead) { + continue; + } else { + queue->qhead = chunk; + break; + } + } + queue->idx = 0; + } + /* make sure all memory stores are done before ringing doorbell */ + smp_wmb(); + cptvf_write_vq_doorbell(cptvf, 1); + /* unlock command queue */ + spin_unlock(&queue->lock); + + return ret; +} + +void do_request_cleanup(struct cpt_vf *cptvf, + struct cpt_info_buffer *info) +{ + int i; + struct pci_dev *pdev = cptvf->pdev; + struct cpt_request_info *req; + + if (info->dptr_baddr) + dma_unmap_single(&pdev->dev, info->dptr_baddr, + info->dlen, DMA_BIDIRECTIONAL); + + if (info->rptr_baddr) + dma_unmap_single(&pdev->dev, info->rptr_baddr, + COMPLETION_CODE_SIZE, DMA_BIDIRECTIONAL); + + if (info->comp_baddr) + dma_unmap_single(&pdev->dev, info->comp_baddr, + sizeof(union cpt_res_s), DMA_BIDIRECTIONAL); + + if (info->req) { + req = info->req; + for (i = 0; i < req->outcnt; i++) { + if (req->out[i].dma_addr) + dma_unmap_single(&pdev->dev, + req->out[i].dma_addr, + req->out[i].size, + DMA_BIDIRECTIONAL); + } + + for (i = 0; i < req->incnt; i++) { + if (req->in[i].dma_addr) + dma_unmap_single(&pdev->dev, + req->in[i].dma_addr, + req->in[i].size, + DMA_BIDIRECTIONAL); + } + } + + if (info->scatter_components) + kzfree(info->scatter_components); + + if (info->gather_components) + kzfree(info->gather_components); + + if (info->out_buffer) + kzfree(info->out_buffer); + + if (info->in_buffer) + kzfree(info->in_buffer); + + if (info->completion_addr) + kzfree((void *)info->completion_addr); + + kzfree(info); +} + +void do_post_process(struct cpt_vf *cptvf, struct cpt_info_buffer *info) +{ + struct pci_dev *pdev = cptvf->pdev; + + if (!info) { + dev_err(&pdev->dev, "incorrect cpt_info_buffer for post processing\n"); + return; + } + + do_request_cleanup(cptvf, info); +} + +static inline void process_pending_queue(struct cpt_vf *cptvf, + struct pending_qinfo *pqinfo, + int qno) +{ + struct pci_dev *pdev = cptvf->pdev; + struct pending_queue *pqueue = &pqinfo->queue[qno]; + struct pending_entry *pentry = NULL; + struct cpt_info_buffer *info = NULL; + union cpt_res_s *status = NULL; + unsigned char ccode; + + while (1) { + spin_lock_bh(&pqueue->lock); + pentry = &pqueue->head[pqueue->front]; + if (unlikely(!pentry->busy)) { + spin_unlock_bh(&pqueue->lock); + break; + } + + info = (struct cpt_info_buffer *)pentry->post_arg; + if (unlikely(!info)) { + dev_err(&pdev->dev, "Pending Entry post arg NULL\n"); + pending_queue_inc_front(pqinfo, qno); + spin_unlock_bh(&pqueue->lock); + continue; + } + + status = (union cpt_res_s *)pentry->completion_addr; + ccode = status->s.compcode; + if ((status->s.compcode == CPT_COMP_E_FAULT) || + (status->s.compcode == CPT_COMP_E_SWERR)) { + dev_err(&pdev->dev, "Request failed with %s\n", + (status->s.compcode == CPT_COMP_E_FAULT) ? + "DMA Fault" : "Software error"); + pentry->completion_addr = NULL; + pentry->busy = false; + atomic64_dec((&pqueue->pending_count)); + pentry->post_arg = NULL; + pending_queue_inc_front(pqinfo, qno); + do_request_cleanup(cptvf, info); + spin_unlock_bh(&pqueue->lock); + break; + } else if (status->s.compcode == COMPLETION_CODE_INIT) { + /* check for timeout */ + if (time_after_eq(jiffies, + (info->time_in + + (CPT_COMMAND_TIMEOUT * HZ)))) { + dev_err(&pdev->dev, "Request timed out"); + pentry->completion_addr = NULL; + pentry->busy = false; + atomic64_dec((&pqueue->pending_count)); + pentry->post_arg = NULL; + pending_queue_inc_front(pqinfo, qno); + do_request_cleanup(cptvf, info); + spin_unlock_bh(&pqueue->lock); + break; + } else if ((*info->alternate_caddr == + (~COMPLETION_CODE_INIT)) && + (info->extra_time < TIME_IN_RESET_COUNT)) { + info->time_in = jiffies; + info->extra_time++; + spin_unlock_bh(&pqueue->lock); + break; + } + } + + pentry->completion_addr = NULL; + pentry->busy = false; + pentry->post_arg = NULL; + atomic64_dec((&pqueue->pending_count)); + pending_queue_inc_front(pqinfo, qno); + spin_unlock_bh(&pqueue->lock); + + do_post_process(info->cptvf, info); + /* + * Calling callback after we find + * that the request has been serviced + */ + pentry->callback(ccode, pentry->callback_arg); + } +} + +int process_request(struct cpt_vf *cptvf, struct cpt_request_info *req) +{ + int ret = 0, clear = 0, queue = 0; + struct cpt_info_buffer *info = NULL; + struct cptvf_request *cpt_req = NULL; + union ctrl_info *ctrl = NULL; + union cpt_res_s *result = NULL; + struct pending_entry *pentry = NULL; + struct pending_queue *pqueue = NULL; + struct pci_dev *pdev = cptvf->pdev; + u8 group = 0; + struct cpt_vq_command vq_cmd; + union cpt_inst_s cptinst; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (unlikely(!info)) { + dev_err(&pdev->dev, "Unable to allocate memory for info_buffer\n"); + return -ENOMEM; + } + + cpt_req = (struct cptvf_request *)&req->req; + ctrl = (union ctrl_info *)&req->ctrl; + + info->cptvf = cptvf; + group = ctrl->s.grp; + ret = setup_sgio_list(cptvf, info, req); + if (ret) { + dev_err(&pdev->dev, "Setting up SG list failed"); + goto request_cleanup; + } + + cpt_req->dlen = info->dlen; + /* + * Get buffer for union cpt_res_s response + * structure and its physical address + */ + info->completion_addr = kzalloc(sizeof(union cpt_res_s), GFP_KERNEL); + if (unlikely(!info->completion_addr)) { + dev_err(&pdev->dev, "Unable to allocate memory for completion_addr\n"); + return -ENOMEM; + } + + result = (union cpt_res_s *)info->completion_addr; + result->s.compcode = COMPLETION_CODE_INIT; + info->comp_baddr = dma_map_single(&pdev->dev, + (void *)info->completion_addr, + sizeof(union cpt_res_s), + DMA_BIDIRECTIONAL); + if (dma_mapping_error(&pdev->dev, info->comp_baddr)) { + dev_err(&pdev->dev, "mapping compptr Failed %lu\n", + sizeof(union cpt_res_s)); + ret = -EFAULT; + goto request_cleanup; + } + + /* Fill the VQ command */ + vq_cmd.cmd.u64 = 0; + vq_cmd.cmd.s.opcode = cpu_to_be16(cpt_req->opcode.flags); + vq_cmd.cmd.s.param1 = cpu_to_be16(cpt_req->param1); + vq_cmd.cmd.s.param2 = cpu_to_be16(cpt_req->param2); + vq_cmd.cmd.s.dlen = cpu_to_be16(cpt_req->dlen); + + /* 64-bit swap for microcode data reads, not needed for addresses*/ + vq_cmd.cmd.u64 = cpu_to_be64(vq_cmd.cmd.u64); + vq_cmd.dptr = info->dptr_baddr; + vq_cmd.rptr = info->rptr_baddr; + vq_cmd.cptr.u64 = 0; + vq_cmd.cptr.s.grp = group; + /* Get Pending Entry to submit command */ + /* Always queue 0, because 1 queue per VF */ + queue = 0; + pqueue = &cptvf->pqinfo.queue[queue]; + + if (atomic64_read(&pqueue->pending_count) > PENDING_THOLD) { + dev_err(&pdev->dev, "pending threshold reached\n"); + process_pending_queue(cptvf, &cptvf->pqinfo, queue); + } + +get_pending_entry: + spin_lock_bh(&pqueue->lock); + pentry = get_free_pending_entry(pqueue, cptvf->pqinfo.qlen); + if (unlikely(!pentry)) { + spin_unlock_bh(&pqueue->lock); + if (clear == 0) { + process_pending_queue(cptvf, &cptvf->pqinfo, queue); + clear = 1; + goto get_pending_entry; + } + dev_err(&pdev->dev, "Get free entry failed\n"); + dev_err(&pdev->dev, "queue: %d, rear: %d, front: %d\n", + queue, pqueue->rear, pqueue->front); + ret = -EFAULT; + goto request_cleanup; + } + + pentry->completion_addr = info->completion_addr; + pentry->post_arg = (void *)info; + pentry->callback = req->callback; + pentry->callback_arg = req->callback_arg; + info->pentry = pentry; + pentry->busy = true; + atomic64_inc(&pqueue->pending_count); + + /* Send CPT command */ + info->pentry = pentry; + info->time_in = jiffies; + info->req = req; + + /* Create the CPT_INST_S type command for HW intrepretation */ + cptinst.s.doneint = true; + cptinst.s.res_addr = (u64)info->comp_baddr; + cptinst.s.tag = 0; + cptinst.s.grp = 0; + cptinst.s.wq_ptr = 0; + cptinst.s.ei0 = vq_cmd.cmd.u64; + cptinst.s.ei1 = vq_cmd.dptr; + cptinst.s.ei2 = vq_cmd.rptr; + cptinst.s.ei3 = vq_cmd.cptr.u64; + + ret = send_cpt_command(cptvf, &cptinst, queue); + spin_unlock_bh(&pqueue->lock); + if (unlikely(ret)) { + dev_err(&pdev->dev, "Send command failed for AE\n"); + ret = -EFAULT; + goto request_cleanup; + } + + return 0; + +request_cleanup: + dev_dbg(&pdev->dev, "Failed to submit CPT command\n"); + do_request_cleanup(cptvf, info); + + return ret; +} + +void vq_post_process(struct cpt_vf *cptvf, u32 qno) +{ + struct pci_dev *pdev = cptvf->pdev; + + if (unlikely(qno > cptvf->nr_queues)) { + dev_err(&pdev->dev, "Request for post processing on invalid pending queue: %u\n", + qno); + return; + } + + process_pending_queue(cptvf, &cptvf->pqinfo, qno); +} + +int cptvf_do_request(void *vfdev, struct cpt_request_info *req) +{ + struct cpt_vf *cptvf = (struct cpt_vf *)vfdev; + struct pci_dev *pdev = cptvf->pdev; + + if (!cpt_device_ready(cptvf)) { + dev_err(&pdev->dev, "CPT Device is not ready"); + return -ENODEV; + } + + if ((cptvf->vftype == SE_TYPES) && (!req->ctrl.s.se_req)) { + dev_err(&pdev->dev, "CPTVF-%d of SE TYPE got AE request", + cptvf->vfid); + return -EINVAL; + } else if ((cptvf->vftype == AE_TYPES) && (req->ctrl.s.se_req)) { + dev_err(&pdev->dev, "CPTVF-%d of AE TYPE got SE request", + cptvf->vfid); + return -EINVAL; + } + + return process_request(cptvf, req); +} diff --git a/drivers/crypto/cavium/cpt/request_manager.h b/drivers/crypto/cavium/cpt/request_manager.h new file mode 100644 index 000000000000..80ee074c6e0c --- /dev/null +++ b/drivers/crypto/cavium/cpt/request_manager.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2016 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef __REQUEST_MANAGER_H +#define __REQUEST_MANAGER_H + +#include "cpt_common.h" + +#define TIME_IN_RESET_COUNT 5 +#define COMPLETION_CODE_SIZE 8 +#define COMPLETION_CODE_INIT 0 +#define PENDING_THOLD 100 +#define MAX_SG_IN_CNT 12 +#define MAX_SG_OUT_CNT 13 +#define SG_LIST_HDR_SIZE 8 +#define MAX_BUF_CNT 16 + +union ctrl_info { + u32 flags; + struct { +#if defined(__BIG_ENDIAN_BITFIELD) + u32 reserved0:26; + u32 grp:3; /* Group bits */ + u32 dma_mode:2; /* DMA mode */ + u32 se_req:1;/* To SE core */ +#else + u32 se_req:1; /* To SE core */ + u32 dma_mode:2; /* DMA mode */ + u32 grp:3; /* Group bits */ + u32 reserved0:26; +#endif + } s; +}; + +union opcode_info { + u16 flags; + struct { + u8 major; + u8 minor; + } s; +}; + +struct cptvf_request { + union opcode_info opcode; + u16 param1; + u16 param2; + u16 dlen; +}; + +struct buf_ptr { + u8 *vptr; + dma_addr_t dma_addr; + u16 size; +}; + +struct cpt_request_info { + u8 incnt; /* Number of input buffers */ + u8 outcnt; /* Number of output buffers */ + u16 rlen; /* Output length */ + union ctrl_info ctrl; /* User control information */ + struct cptvf_request req; /* Request Information (Core specific) */ + + struct buf_ptr in[MAX_BUF_CNT]; + struct buf_ptr out[MAX_BUF_CNT]; + + void (*callback)(int, void *); /* Kernel ASYNC request callabck */ + void *callback_arg; /* Kernel ASYNC request callabck arg */ +}; + +struct sglist_component { + union { + u64 len; + struct { + u16 len0; + u16 len1; + u16 len2; + u16 len3; + } s; + } u; + u64 ptr0; + u64 ptr1; + u64 ptr2; + u64 ptr3; +}; + +struct cpt_info_buffer { + struct cpt_vf *cptvf; + unsigned long time_in; + u8 extra_time; + + struct cpt_request_info *req; + dma_addr_t dptr_baddr; + u32 dlen; + dma_addr_t rptr_baddr; + dma_addr_t comp_baddr; + u8 *in_buffer; + u8 *out_buffer; + u8 *gather_components; + u8 *scatter_components; + + struct pending_entry *pentry; + volatile u64 *completion_addr; + volatile u64 *alternate_caddr; +}; + +/* + * CPT_INST_S software command definitions + * Words EI (0-3) + */ +union vq_cmd_word0 { + u64 u64; + struct { + u16 opcode; + u16 param1; + u16 param2; + u16 dlen; + } s; +}; + +union vq_cmd_word3 { + u64 u64; + struct { +#if defined(__BIG_ENDIAN_BITFIELD) + u64 grp:3; + u64 cptr:61; +#else + u64 cptr:61; + u64 grp:3; +#endif + } s; +}; + +struct cpt_vq_command { + union vq_cmd_word0 cmd; + u64 dptr; + u64 rptr; + union vq_cmd_word3 cptr; +}; + +void vq_post_process(struct cpt_vf *cptvf, u32 qno); +int process_request(struct cpt_vf *cptvf, struct cpt_request_info *req); +#endif /* __REQUEST_MANAGER_H */ diff --git a/drivers/crypto/ccp/ccp-dev-v5.c b/drivers/crypto/ccp/ccp-dev-v5.c index 612898b4aaad..41cc853f8569 100644 --- a/drivers/crypto/ccp/ccp-dev-v5.c +++ b/drivers/crypto/ccp/ccp-dev-v5.c @@ -250,17 +250,20 @@ static int ccp5_do_cmd(struct ccp5_desc *desc, ret = wait_event_interruptible(cmd_q->int_queue, cmd_q->int_rcvd); if (ret || cmd_q->cmd_error) { + /* Log the error and flush the queue by + * moving the head pointer + */ if (cmd_q->cmd_error) ccp_log_error(cmd_q->ccp, cmd_q->cmd_error); - /* A version 5 device doesn't use Job IDs... */ + iowrite32(tail, cmd_q->reg_head_lo); if (!ret) ret = -EIO; } cmd_q->int_rcvd = 0; } - return 0; + return ret; } static int ccp5_perform_aes(struct ccp_op *op) @@ -284,8 +287,7 @@ static int ccp5_perform_aes(struct ccp_op *op) CCP_AES_ENCRYPT(&function) = op->u.aes.action; CCP_AES_MODE(&function) = op->u.aes.mode; CCP_AES_TYPE(&function) = op->u.aes.type; - if (op->u.aes.mode == CCP_AES_MODE_CFB) - CCP_AES_SIZE(&function) = 0x7f; + CCP_AES_SIZE(&function) = op->u.aes.size; CCP5_CMD_FUNCTION(&desc) = function.raw; @@ -532,7 +534,7 @@ static int ccp_find_lsb_regions(struct ccp_cmd_queue *cmd_q, u64 status) status >>= LSB_REGION_WIDTH; } queues = bitmap_weight(cmd_q->lsbmask, MAX_LSB_CNT); - dev_info(cmd_q->ccp->dev, "Queue %d can access %d LSB regions\n", + dev_dbg(cmd_q->ccp->dev, "Queue %d can access %d LSB regions\n", cmd_q->id, queues); return queues ? 0 : -EINVAL; @@ -574,7 +576,7 @@ static int ccp_find_and_assign_lsb_to_q(struct ccp_device *ccp, */ cmd_q->lsb = bitno; bitmap_clear(lsb_pub, bitno, 1); - dev_info(ccp->dev, + dev_dbg(ccp->dev, "Queue %d gets LSB %d\n", i, bitno); break; @@ -732,7 +734,6 @@ static int ccp5_init(struct ccp_device *ccp) ret = -EIO; goto e_pool; } - dev_notice(dev, "%u command queues available\n", ccp->cmd_q_count); /* Turn off the queues and disable interrupts until ready */ for (i = 0; i < ccp->cmd_q_count; i++) { diff --git a/drivers/crypto/ccp/ccp-dev.h b/drivers/crypto/ccp/ccp-dev.h index 649e5610a5ce..2b5c01fade05 100644 --- a/drivers/crypto/ccp/ccp-dev.h +++ b/drivers/crypto/ccp/ccp-dev.h @@ -467,6 +467,7 @@ struct ccp_aes_op { enum ccp_aes_type type; enum ccp_aes_mode mode; enum ccp_aes_action action; + unsigned int size; }; struct ccp_xts_aes_op { diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c index 50fae4442801..f1396c3aedac 100644 --- a/drivers/crypto/ccp/ccp-ops.c +++ b/drivers/crypto/ccp/ccp-ops.c @@ -184,62 +184,46 @@ static void ccp_get_dm_area(struct ccp_dm_workarea *wa, unsigned int wa_offset, } static int ccp_reverse_set_dm_area(struct ccp_dm_workarea *wa, + unsigned int wa_offset, struct scatterlist *sg, - unsigned int len, unsigned int se_len, - bool sign_extend) + unsigned int sg_offset, + unsigned int len) { - unsigned int nbytes, sg_offset, dm_offset, sb_len, i; - u8 buffer[CCP_REVERSE_BUF_SIZE]; - - if (WARN_ON(se_len > sizeof(buffer))) - return -EINVAL; - - sg_offset = len; - dm_offset = 0; - nbytes = len; - while (nbytes) { - sb_len = min_t(unsigned int, nbytes, se_len); - sg_offset -= sb_len; - - scatterwalk_map_and_copy(buffer, sg, sg_offset, sb_len, 0); - for (i = 0; i < sb_len; i++) - wa->address[dm_offset + i] = buffer[sb_len - i - 1]; - - dm_offset += sb_len; - nbytes -= sb_len; - - if ((sb_len != se_len) && sign_extend) { - /* Must sign-extend to nearest sign-extend length */ - if (wa->address[dm_offset - 1] & 0x80) - memset(wa->address + dm_offset, 0xff, - se_len - sb_len); - } + u8 *p, *q; + + ccp_set_dm_area(wa, wa_offset, sg, sg_offset, len); + + p = wa->address + wa_offset; + q = p + len - 1; + while (p < q) { + *p = *p ^ *q; + *q = *p ^ *q; + *p = *p ^ *q; + p++; + q--; } - return 0; } static void ccp_reverse_get_dm_area(struct ccp_dm_workarea *wa, + unsigned int wa_offset, struct scatterlist *sg, + unsigned int sg_offset, unsigned int len) { - unsigned int nbytes, sg_offset, dm_offset, sb_len, i; - u8 buffer[CCP_REVERSE_BUF_SIZE]; - - sg_offset = 0; - dm_offset = len; - nbytes = len; - while (nbytes) { - sb_len = min_t(unsigned int, nbytes, sizeof(buffer)); - dm_offset -= sb_len; - - for (i = 0; i < sb_len; i++) - buffer[sb_len - i - 1] = wa->address[dm_offset + i]; - scatterwalk_map_and_copy(buffer, sg, sg_offset, sb_len, 1); - - sg_offset += sb_len; - nbytes -= sb_len; + u8 *p, *q; + + p = wa->address + wa_offset; + q = p + len - 1; + while (p < q) { + *p = *p ^ *q; + *q = *p ^ *q; + *p = *p ^ *q; + p++; + q--; } + + ccp_get_dm_area(wa, wa_offset, sg, sg_offset, len); } static void ccp_free_data(struct ccp_data *data, struct ccp_cmd_queue *cmd_q) @@ -692,6 +676,14 @@ static int ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) goto e_ctx; } } + switch (aes->mode) { + case CCP_AES_MODE_CFB: /* CFB128 only */ + case CCP_AES_MODE_CTR: + op.u.aes.size = AES_BLOCK_SIZE * BITS_PER_BYTE - 1; + break; + default: + op.u.aes.size = 0; + } /* Prepare the input and output data workareas. For in-place * operations we need to set the dma direction to BIDIRECTIONAL @@ -1261,8 +1253,7 @@ static int ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) if (ret) goto e_sb; - ret = ccp_reverse_set_dm_area(&exp, rsa->exp, rsa->exp_len, - CCP_SB_BYTES, false); + ret = ccp_reverse_set_dm_area(&exp, 0, rsa->exp, 0, rsa->exp_len); if (ret) goto e_exp; ret = ccp_copy_to_sb(cmd_q, &exp, op.jobid, op.sb_key, @@ -1280,16 +1271,12 @@ static int ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) if (ret) goto e_exp; - ret = ccp_reverse_set_dm_area(&src, rsa->mod, rsa->mod_len, - CCP_SB_BYTES, false); + ret = ccp_reverse_set_dm_area(&src, 0, rsa->mod, 0, rsa->mod_len); if (ret) goto e_src; - src.address += o_len; /* Adjust the address for the copy operation */ - ret = ccp_reverse_set_dm_area(&src, rsa->src, rsa->src_len, - CCP_SB_BYTES, false); + ret = ccp_reverse_set_dm_area(&src, o_len, rsa->src, 0, rsa->src_len); if (ret) goto e_src; - src.address -= o_len; /* Reset the address to original value */ /* Prepare the output area for the operation */ ret = ccp_init_data(&dst, cmd_q, rsa->dst, rsa->mod_len, @@ -1314,7 +1301,7 @@ static int ccp_run_rsa_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) goto e_dst; } - ccp_reverse_get_dm_area(&dst.dm_wa, rsa->dst, rsa->mod_len); + ccp_reverse_get_dm_area(&dst.dm_wa, 0, rsa->dst, 0, rsa->mod_len); e_dst: ccp_free_data(&dst, cmd_q); @@ -1566,25 +1553,22 @@ static int ccp_run_ecc_mm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) save = src.address; /* Copy the ECC modulus */ - ret = ccp_reverse_set_dm_area(&src, ecc->mod, ecc->mod_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->mod, 0, ecc->mod_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; /* Copy the first operand */ - ret = ccp_reverse_set_dm_area(&src, ecc->u.mm.operand_1, - ecc->u.mm.operand_1_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->u.mm.operand_1, 0, + ecc->u.mm.operand_1_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; if (ecc->function != CCP_ECC_FUNCTION_MINV_384BIT) { /* Copy the second operand */ - ret = ccp_reverse_set_dm_area(&src, ecc->u.mm.operand_2, - ecc->u.mm.operand_2_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->u.mm.operand_2, 0, + ecc->u.mm.operand_2_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; @@ -1623,7 +1607,8 @@ static int ccp_run_ecc_mm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) } /* Save the ECC result */ - ccp_reverse_get_dm_area(&dst, ecc->u.mm.result, CCP_ECC_MODULUS_BYTES); + ccp_reverse_get_dm_area(&dst, 0, ecc->u.mm.result, 0, + CCP_ECC_MODULUS_BYTES); e_dst: ccp_dm_free(&dst); @@ -1691,22 +1676,19 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) save = src.address; /* Copy the ECC modulus */ - ret = ccp_reverse_set_dm_area(&src, ecc->mod, ecc->mod_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->mod, 0, ecc->mod_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; /* Copy the first point X and Y coordinate */ - ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_1.x, - ecc->u.pm.point_1.x_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->u.pm.point_1.x, 0, + ecc->u.pm.point_1.x_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; - ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_1.y, - ecc->u.pm.point_1.y_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->u.pm.point_1.y, 0, + ecc->u.pm.point_1.y_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; @@ -1717,15 +1699,13 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) if (ecc->function == CCP_ECC_FUNCTION_PADD_384BIT) { /* Copy the second point X and Y coordinate */ - ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_2.x, - ecc->u.pm.point_2.x_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->u.pm.point_2.x, 0, + ecc->u.pm.point_2.x_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; - ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.point_2.y, - ecc->u.pm.point_2.y_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->u.pm.point_2.y, 0, + ecc->u.pm.point_2.y_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; @@ -1735,19 +1715,17 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) src.address += CCP_ECC_OPERAND_SIZE; } else { /* Copy the Domain "a" parameter */ - ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.domain_a, - ecc->u.pm.domain_a_len, - CCP_ECC_OPERAND_SIZE, false); + ret = ccp_reverse_set_dm_area(&src, 0, ecc->u.pm.domain_a, 0, + ecc->u.pm.domain_a_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; if (ecc->function == CCP_ECC_FUNCTION_PMUL_384BIT) { /* Copy the scalar value */ - ret = ccp_reverse_set_dm_area(&src, ecc->u.pm.scalar, - ecc->u.pm.scalar_len, - CCP_ECC_OPERAND_SIZE, - false); + ret = ccp_reverse_set_dm_area(&src, 0, + ecc->u.pm.scalar, 0, + ecc->u.pm.scalar_len); if (ret) goto e_src; src.address += CCP_ECC_OPERAND_SIZE; @@ -1792,10 +1770,10 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) save = dst.address; /* Save the ECC result X and Y coordinates */ - ccp_reverse_get_dm_area(&dst, ecc->u.pm.result.x, + ccp_reverse_get_dm_area(&dst, 0, ecc->u.pm.result.x, 0, CCP_ECC_MODULUS_BYTES); dst.address += CCP_ECC_OUTPUT_SIZE; - ccp_reverse_get_dm_area(&dst, ecc->u.pm.result.y, + ccp_reverse_get_dm_area(&dst, 0, ecc->u.pm.result.y, 0, CCP_ECC_MODULUS_BYTES); dst.address += CCP_ECC_OUTPUT_SIZE; diff --git a/drivers/crypto/chelsio/chcr_algo.c b/drivers/crypto/chelsio/chcr_algo.c index b4b78b37f8a6..41bc7f4f58cd 100644 --- a/drivers/crypto/chelsio/chcr_algo.c +++ b/drivers/crypto/chelsio/chcr_algo.c @@ -171,7 +171,7 @@ int chcr_handle_resp(struct crypto_async_request *req, unsigned char *input, } break; - case CRYPTO_ALG_TYPE_BLKCIPHER: + case CRYPTO_ALG_TYPE_ABLKCIPHER: ctx_req.req.ablk_req = (struct ablkcipher_request *)req; ctx_req.ctx.ablk_ctx = ablkcipher_request_ctx(ctx_req.req.ablk_req); @@ -542,10 +542,11 @@ static inline void create_wreq(struct chcr_context *ctx, (calc_tx_flits_ofld(skb) * 8), 16))); chcr_req->wreq.cookie = cpu_to_be64((uintptr_t)req); chcr_req->wreq.rx_chid_to_rx_q_id = - FILL_WR_RX_Q_ID(ctx->dev->tx_channel_id, qid, - is_iv ? iv_loc : IV_NOP); + FILL_WR_RX_Q_ID(ctx->dev->rx_channel_id, qid, + is_iv ? iv_loc : IV_NOP, ctx->tx_channel_id); - chcr_req->ulptx.cmd_dest = FILL_ULPTX_CMD_DEST(ctx->dev->tx_channel_id); + chcr_req->ulptx.cmd_dest = FILL_ULPTX_CMD_DEST(ctx->dev->tx_channel_id, + qid); chcr_req->ulptx.len = htonl((DIV_ROUND_UP((calc_tx_flits_ofld(skb) * 8), 16) - ((sizeof(chcr_req->wreq)) >> 4))); @@ -606,7 +607,7 @@ static struct sk_buff chcr_req = (struct chcr_wr *)__skb_put(skb, transhdr_len); memset(chcr_req, 0, transhdr_len); chcr_req->sec_cpl.op_ivinsrtofst = - FILL_SEC_CPL_OP_IVINSR(ctx->dev->tx_channel_id, 2, 1); + FILL_SEC_CPL_OP_IVINSR(ctx->dev->rx_channel_id, 2, 1); chcr_req->sec_cpl.pldlen = htonl(ivsize + req->nbytes); chcr_req->sec_cpl.aadstart_cipherstop_hi = @@ -782,6 +783,7 @@ static int chcr_device_init(struct chcr_context *ctx) spin_lock(&ctx->dev->lock_chcr_dev); ctx->tx_channel_id = rxq_idx; ctx->dev->tx_channel_id = !ctx->dev->tx_channel_id; + ctx->dev->rx_channel_id = 0; spin_unlock(&ctx->dev->lock_chcr_dev); } out: @@ -874,7 +876,7 @@ static struct sk_buff *create_hash_wr(struct ahash_request *req, memset(chcr_req, 0, transhdr_len); chcr_req->sec_cpl.op_ivinsrtofst = - FILL_SEC_CPL_OP_IVINSR(ctx->dev->tx_channel_id, 2, 0); + FILL_SEC_CPL_OP_IVINSR(ctx->dev->rx_channel_id, 2, 0); chcr_req->sec_cpl.pldlen = htonl(param->bfr_len + param->sg_len); chcr_req->sec_cpl.aadstart_cipherstop_hi = @@ -1425,7 +1427,7 @@ static struct sk_buff *create_authenc_wr(struct aead_request *req, * to the hardware spec */ chcr_req->sec_cpl.op_ivinsrtofst = - FILL_SEC_CPL_OP_IVINSR(ctx->dev->tx_channel_id, 2, + FILL_SEC_CPL_OP_IVINSR(ctx->dev->rx_channel_id, 2, (ivsize ? (assoclen + 1) : 0)); chcr_req->sec_cpl.pldlen = htonl(assoclen + ivsize + req->cryptlen); chcr_req->sec_cpl.aadstart_cipherstop_hi = FILL_SEC_CPL_CIPHERSTOP_HI( @@ -1601,7 +1603,7 @@ static void fill_sec_cpl_for_aead(struct cpl_tx_sec_pdu *sec_cpl, unsigned int ivsize = AES_BLOCK_SIZE; unsigned int cipher_mode = CHCR_SCMD_CIPHER_MODE_AES_CCM; unsigned int mac_mode = CHCR_SCMD_AUTH_MODE_CBCMAC; - unsigned int c_id = chcrctx->dev->tx_channel_id; + unsigned int c_id = chcrctx->dev->rx_channel_id; unsigned int ccm_xtra; unsigned char tag_offset = 0, auth_offset = 0; unsigned char hmac_ctrl = get_hmac(crypto_aead_authsize(tfm)); @@ -1877,7 +1879,7 @@ static struct sk_buff *create_gcm_wr(struct aead_request *req, tag_offset = (op_type == CHCR_ENCRYPT_OP) ? 0 : authsize; chcr_req->sec_cpl.op_ivinsrtofst = FILL_SEC_CPL_OP_IVINSR( - ctx->dev->tx_channel_id, 2, (ivsize ? + ctx->dev->rx_channel_id, 2, (ivsize ? (req->assoclen + 1) : 0)); chcr_req->sec_cpl.pldlen = htonl(req->assoclen + ivsize + crypt_len); chcr_req->sec_cpl.aadstart_cipherstop_hi = FILL_SEC_CPL_CIPHERSTOP_HI( @@ -2187,8 +2189,7 @@ static int chcr_gcm_setkey(struct crypto_aead *aead, const u8 *key, struct chcr_context *ctx = crypto_aead_ctx(aead); struct chcr_aead_ctx *aeadctx = AEAD_CTX(ctx); struct chcr_gcm_ctx *gctx = GCM_CTX(aeadctx); - struct blkcipher_desc h_desc; - struct scatterlist src[1]; + struct crypto_cipher *cipher; unsigned int ck_size; int ret = 0, key_ctx_size = 0; @@ -2221,27 +2222,26 @@ static int chcr_gcm_setkey(struct crypto_aead *aead, const u8 *key, CHCR_KEYCTX_MAC_KEY_SIZE_128, 0, 0, key_ctx_size >> 4); - /* Calculate the H = CIPH(K, 0 repeated 16 times) using sync aes - * blkcipher It will go on key context + /* Calculate the H = CIPH(K, 0 repeated 16 times). + * It will go in key context */ - h_desc.tfm = crypto_alloc_blkcipher("cbc(aes-generic)", 0, 0); - if (IS_ERR(h_desc.tfm)) { + cipher = crypto_alloc_cipher("aes-generic", 0, 0); + if (IS_ERR(cipher)) { aeadctx->enckey_len = 0; ret = -ENOMEM; goto out; } - h_desc.flags = 0; - ret = crypto_blkcipher_setkey(h_desc.tfm, key, keylen); + + ret = crypto_cipher_setkey(cipher, key, keylen); if (ret) { aeadctx->enckey_len = 0; goto out1; } memset(gctx->ghash_h, 0, AEAD_H_SIZE); - sg_init_one(&src[0], gctx->ghash_h, AEAD_H_SIZE); - ret = crypto_blkcipher_encrypt(&h_desc, &src[0], &src[0], AEAD_H_SIZE); + crypto_cipher_encrypt_one(cipher, gctx->ghash_h, gctx->ghash_h); out1: - crypto_free_blkcipher(h_desc.tfm); + crypto_free_cipher(cipher); out: return ret; } @@ -2456,13 +2456,14 @@ static int chcr_aead_op(struct aead_request *req, { struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct chcr_context *ctx = crypto_aead_ctx(tfm); - struct uld_ctx *u_ctx = ULD_CTX(ctx); + struct uld_ctx *u_ctx; struct sk_buff *skb; - if (ctx && !ctx->dev) { + if (!ctx->dev) { pr_err("chcr : %s : No crypto device.\n", __func__); return -ENXIO; } + u_ctx = ULD_CTX(ctx); if (cxgb4_is_crypto_q_full(u_ctx->lldi.ports[0], ctx->tx_channel_id)) { if (!(req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) @@ -2492,7 +2493,7 @@ static struct chcr_alg_template driver_algs[] = { .cra_name = "cbc(aes)", .cra_driver_name = "cbc-aes-chcr", .cra_priority = CHCR_CRA_PRIORITY, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct chcr_context) @@ -2519,7 +2520,7 @@ static struct chcr_alg_template driver_algs[] = { .cra_name = "xts(aes)", .cra_driver_name = "xts-aes-chcr", .cra_priority = CHCR_CRA_PRIORITY, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct chcr_context) + diff --git a/drivers/crypto/chelsio/chcr_algo.h b/drivers/crypto/chelsio/chcr_algo.h index 3c7c51f7bedf..ba38bae7ce80 100644 --- a/drivers/crypto/chelsio/chcr_algo.h +++ b/drivers/crypto/chelsio/chcr_algo.h @@ -185,20 +185,21 @@ FW_CRYPTO_LOOKASIDE_WR_CCTX_LOC_V(1) | \ FW_CRYPTO_LOOKASIDE_WR_CCTX_SIZE_V((ctx_len))) -#define FILL_WR_RX_Q_ID(cid, qid, wr_iv) \ +#define FILL_WR_RX_Q_ID(cid, qid, wr_iv, fid) \ htonl( \ FW_CRYPTO_LOOKASIDE_WR_RX_CHID_V((cid)) | \ FW_CRYPTO_LOOKASIDE_WR_RX_Q_ID_V((qid)) | \ FW_CRYPTO_LOOKASIDE_WR_LCB_V(0) | \ - FW_CRYPTO_LOOKASIDE_WR_IV_V((wr_iv))) + FW_CRYPTO_LOOKASIDE_WR_IV_V((wr_iv)) | \ + FW_CRYPTO_LOOKASIDE_WR_FQIDX_V(fid)) -#define FILL_ULPTX_CMD_DEST(cid) \ +#define FILL_ULPTX_CMD_DEST(cid, qid) \ htonl(ULPTX_CMD_V(ULP_TX_PKT) | \ ULP_TXPKT_DEST_V(0) | \ ULP_TXPKT_DATAMODIFY_V(0) | \ ULP_TXPKT_CHANNELID_V((cid)) | \ ULP_TXPKT_RO_V(1) | \ - ULP_TXPKT_FID_V(0)) + ULP_TXPKT_FID_V(qid)) #define KEYCTX_ALIGN_PAD(bs) ({unsigned int _bs = (bs);\ _bs == SHA1_DIGEST_SIZE ? 12 : 0; }) diff --git a/drivers/crypto/chelsio/chcr_core.c b/drivers/crypto/chelsio/chcr_core.c index 1c65f07e1cc9..c28e018e0773 100644 --- a/drivers/crypto/chelsio/chcr_core.c +++ b/drivers/crypto/chelsio/chcr_core.c @@ -61,7 +61,7 @@ int assign_chcr_device(struct chcr_dev **dev) */ mutex_lock(&dev_mutex); /* TODO ? */ list_for_each_entry(u_ctx, &uld_ctx_list, entry) - if (u_ctx && u_ctx->dev) { + if (u_ctx->dev) { *dev = u_ctx->dev; ret = 0; break; @@ -151,18 +151,17 @@ int chcr_uld_rx_handler(void *handle, const __be64 *rsp, { struct uld_ctx *u_ctx = (struct uld_ctx *)handle; struct chcr_dev *dev = u_ctx->dev; - const struct cpl_act_establish *rpl = (struct cpl_act_establish - *)rsp; + const struct cpl_fw6_pld *rpl = (struct cpl_fw6_pld *)rsp; - if (rpl->ot.opcode != CPL_FW6_PLD) { + if (rpl->opcode != CPL_FW6_PLD) { pr_err("Unsupported opcode\n"); return 0; } if (!pgl) - work_handlers[rpl->ot.opcode](dev, (unsigned char *)&rsp[1]); + work_handlers[rpl->opcode](dev, (unsigned char *)&rsp[1]); else - work_handlers[rpl->ot.opcode](dev, pgl->va); + work_handlers[rpl->opcode](dev, pgl->va); return 0; } diff --git a/drivers/crypto/chelsio/chcr_core.h b/drivers/crypto/chelsio/chcr_core.h index c7088a4e0a49..79da22b5cdc9 100644 --- a/drivers/crypto/chelsio/chcr_core.h +++ b/drivers/crypto/chelsio/chcr_core.h @@ -75,6 +75,7 @@ struct chcr_dev { spinlock_t lock_chcr_dev; struct uld_ctx *u_ctx; unsigned char tx_channel_id; + unsigned char rx_channel_id; }; struct uld_ctx { diff --git a/drivers/crypto/chelsio/chcr_crypto.h b/drivers/crypto/chelsio/chcr_crypto.h index 7ec0a8f12475..81cfd0ba132e 100644 --- a/drivers/crypto/chelsio/chcr_crypto.h +++ b/drivers/crypto/chelsio/chcr_crypto.h @@ -48,7 +48,7 @@ * giving the processed data */ -#define CHCR_CRA_PRIORITY 300 +#define CHCR_CRA_PRIORITY 3000 #define CHCR_AES_MAX_KEY_LEN (2 * (AES_MAX_KEY_SIZE)) /* consider xts */ #define CHCR_MAX_CRYPTO_IV_LEN 16 /* AES IV len */ diff --git a/drivers/crypto/img-hash.c b/drivers/crypto/img-hash.c index a2e77b87485b..9b07f3d88feb 100644 --- a/drivers/crypto/img-hash.c +++ b/drivers/crypto/img-hash.c @@ -226,7 +226,7 @@ static int img_hash_xmit_dma(struct img_hash_dev *hdev, struct scatterlist *sg) struct dma_async_tx_descriptor *desc; struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); - ctx->dma_ct = dma_map_sg(hdev->dev, sg, 1, DMA_MEM_TO_DEV); + ctx->dma_ct = dma_map_sg(hdev->dev, sg, 1, DMA_TO_DEVICE); if (ctx->dma_ct == 0) { dev_err(hdev->dev, "Invalid DMA sg\n"); hdev->err = -EINVAL; @@ -241,7 +241,7 @@ static int img_hash_xmit_dma(struct img_hash_dev *hdev, struct scatterlist *sg) if (!desc) { dev_err(hdev->dev, "Null DMA descriptor\n"); hdev->err = -EINVAL; - dma_unmap_sg(hdev->dev, sg, 1, DMA_MEM_TO_DEV); + dma_unmap_sg(hdev->dev, sg, 1, DMA_TO_DEVICE); return -EINVAL; } desc->callback = img_hash_dma_callback; diff --git a/drivers/crypto/mediatek/Makefile b/drivers/crypto/mediatek/Makefile new file mode 100644 index 000000000000..187be79c7f3e --- /dev/null +++ b/drivers/crypto/mediatek/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CRYPTO_DEV_MEDIATEK) += mtk-crypto.o +mtk-crypto-objs:= mtk-platform.o mtk-aes.o mtk-sha.o diff --git a/drivers/crypto/mediatek/mtk-aes.c b/drivers/crypto/mediatek/mtk-aes.c new file mode 100644 index 000000000000..3a47cdb8f0c8 --- /dev/null +++ b/drivers/crypto/mediatek/mtk-aes.c @@ -0,0 +1,1299 @@ +/* + * Cryptographic API. + * + * Driver for EIP97 AES acceleration. + * + * Copyright (c) 2016 Ryder Lee <ryder.lee@mediatek.com> + * + * 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. + * + * Some ideas are from atmel-aes.c drivers. + */ + +#include <crypto/aes.h> +#include "mtk-platform.h" + +#define AES_QUEUE_SIZE 512 +#define AES_BUF_ORDER 2 +#define AES_BUF_SIZE ((PAGE_SIZE << AES_BUF_ORDER) \ + & ~(AES_BLOCK_SIZE - 1)) + +/* AES command token size */ +#define AES_CT_SIZE_ECB 2 +#define AES_CT_SIZE_CBC 3 +#define AES_CT_SIZE_CTR 3 +#define AES_CT_SIZE_GCM_OUT 5 +#define AES_CT_SIZE_GCM_IN 6 +#define AES_CT_CTRL_HDR cpu_to_le32(0x00220000) + +/* AES-CBC/ECB/CTR command token */ +#define AES_CMD0 cpu_to_le32(0x05000000) +#define AES_CMD1 cpu_to_le32(0x2d060000) +#define AES_CMD2 cpu_to_le32(0xe4a63806) +/* AES-GCM command token */ +#define AES_GCM_CMD0 cpu_to_le32(0x0b000000) +#define AES_GCM_CMD1 cpu_to_le32(0xa0800000) +#define AES_GCM_CMD2 cpu_to_le32(0x25000010) +#define AES_GCM_CMD3 cpu_to_le32(0x0f020000) +#define AES_GCM_CMD4 cpu_to_le32(0x21e60000) +#define AES_GCM_CMD5 cpu_to_le32(0x40e60000) +#define AES_GCM_CMD6 cpu_to_le32(0xd0070000) + +/* AES transform information word 0 fields */ +#define AES_TFM_BASIC_OUT cpu_to_le32(0x4 << 0) +#define AES_TFM_BASIC_IN cpu_to_le32(0x5 << 0) +#define AES_TFM_GCM_OUT cpu_to_le32(0x6 << 0) +#define AES_TFM_GCM_IN cpu_to_le32(0xf << 0) +#define AES_TFM_SIZE(x) cpu_to_le32((x) << 8) +#define AES_TFM_128BITS cpu_to_le32(0xb << 16) +#define AES_TFM_192BITS cpu_to_le32(0xd << 16) +#define AES_TFM_256BITS cpu_to_le32(0xf << 16) +/* AES transform information word 1 fields */ +#define AES_TFM_ECB cpu_to_le32(0x0 << 0) +#define AES_TFM_CBC cpu_to_le32(0x1 << 0) +#define AES_TFM_CTR_INIT cpu_to_le32(0x2 << 0) /* init counter to 1 */ +#define AES_TFM_CTR_LOAD cpu_to_le32(0x6 << 0) /* load/reuse counter */ +#define AES_TFM_3IV cpu_to_le32(0x7 << 5) /* using IV 0-2 */ +#define AES_TFM_FULL_IV cpu_to_le32(0xf << 5) /* using IV 0-3 */ +#define AES_TFM_IV_CTR_MODE cpu_to_le32(0x1 << 10) +#define AES_TFM_ENC_HASH cpu_to_le32(0x1 << 17) +#define AES_TFM_GHASH_DIG cpu_to_le32(0x2 << 21) +#define AES_TFM_GHASH cpu_to_le32(0x4 << 23) + +/* AES flags */ +#define AES_FLAGS_ECB BIT(0) +#define AES_FLAGS_CBC BIT(1) +#define AES_FLAGS_CTR BIT(2) +#define AES_FLAGS_GCM BIT(3) +#define AES_FLAGS_ENCRYPT BIT(4) +#define AES_FLAGS_BUSY BIT(5) + +/** + * Command token(CT) is a set of hardware instructions that + * are used to control engine's processing flow of AES. + * + * Transform information(TFM) is used to define AES state and + * contains all keys and initial vectors. + * + * The engine requires CT and TFM to do: + * - Commands decoding and control of the engine's data path. + * - Coordinating hardware data fetch and store operations. + * - Result token construction and output. + * + * Memory map of GCM's TFM: + * /-----------\ + * | AES KEY | 128/196/256 bits + * |-----------| + * | HASH KEY | a string 128 zero bits encrypted using the block cipher + * |-----------| + * | IVs | 4 * 4 bytes + * \-----------/ + */ +struct mtk_aes_ct { + __le32 cmd[AES_CT_SIZE_GCM_IN]; +}; + +struct mtk_aes_tfm { + __le32 ctrl[2]; + __le32 state[SIZE_IN_WORDS(AES_KEYSIZE_256 + AES_BLOCK_SIZE * 2)]; +}; + +struct mtk_aes_reqctx { + u64 mode; +}; + +struct mtk_aes_base_ctx { + struct mtk_cryp *cryp; + u32 keylen; + mtk_aes_fn start; + + struct mtk_aes_ct ct; + dma_addr_t ct_dma; + struct mtk_aes_tfm tfm; + dma_addr_t tfm_dma; + + __le32 ct_hdr; + u32 ct_size; +}; + +struct mtk_aes_ctx { + struct mtk_aes_base_ctx base; +}; + +struct mtk_aes_ctr_ctx { + struct mtk_aes_base_ctx base; + + u32 iv[AES_BLOCK_SIZE / sizeof(u32)]; + size_t offset; + struct scatterlist src[2]; + struct scatterlist dst[2]; +}; + +struct mtk_aes_gcm_ctx { + struct mtk_aes_base_ctx base; + + u32 authsize; + size_t textlen; + + struct crypto_skcipher *ctr; +}; + +struct mtk_aes_gcm_setkey_result { + int err; + struct completion completion; +}; + +struct mtk_aes_drv { + struct list_head dev_list; + /* Device list lock */ + spinlock_t lock; +}; + +static struct mtk_aes_drv mtk_aes = { + .dev_list = LIST_HEAD_INIT(mtk_aes.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(mtk_aes.lock), +}; + +static inline u32 mtk_aes_read(struct mtk_cryp *cryp, u32 offset) +{ + return readl_relaxed(cryp->base + offset); +} + +static inline void mtk_aes_write(struct mtk_cryp *cryp, + u32 offset, u32 value) +{ + writel_relaxed(value, cryp->base + offset); +} + +static struct mtk_cryp *mtk_aes_find_dev(struct mtk_aes_base_ctx *ctx) +{ + struct mtk_cryp *cryp = NULL; + struct mtk_cryp *tmp; + + spin_lock_bh(&mtk_aes.lock); + if (!ctx->cryp) { + list_for_each_entry(tmp, &mtk_aes.dev_list, aes_list) { + cryp = tmp; + break; + } + ctx->cryp = cryp; + } else { + cryp = ctx->cryp; + } + spin_unlock_bh(&mtk_aes.lock); + + return cryp; +} + +static inline size_t mtk_aes_padlen(size_t len) +{ + len &= AES_BLOCK_SIZE - 1; + return len ? AES_BLOCK_SIZE - len : 0; +} + +static bool mtk_aes_check_aligned(struct scatterlist *sg, size_t len, + struct mtk_aes_dma *dma) +{ + int nents; + + if (!IS_ALIGNED(len, AES_BLOCK_SIZE)) + return false; + + for (nents = 0; sg; sg = sg_next(sg), ++nents) { + if (!IS_ALIGNED(sg->offset, sizeof(u32))) + return false; + + if (len <= sg->length) { + if (!IS_ALIGNED(len, AES_BLOCK_SIZE)) + return false; + + dma->nents = nents + 1; + dma->remainder = sg->length - len; + sg->length = len; + return true; + } + + if (!IS_ALIGNED(sg->length, AES_BLOCK_SIZE)) + return false; + + len -= sg->length; + } + + return false; +} + +static inline void mtk_aes_set_mode(struct mtk_aes_rec *aes, + const struct mtk_aes_reqctx *rctx) +{ + /* Clear all but persistent flags and set request flags. */ + aes->flags = (aes->flags & AES_FLAGS_BUSY) | rctx->mode; +} + +static inline void mtk_aes_restore_sg(const struct mtk_aes_dma *dma) +{ + struct scatterlist *sg = dma->sg; + int nents = dma->nents; + + if (!dma->remainder) + return; + + while (--nents > 0 && sg) + sg = sg_next(sg); + + if (!sg) + return; + + sg->length += dma->remainder; +} + +/* + * Write descriptors for processing. This will configure the engine, load + * the transform information and then start the packet processing. + */ +static int mtk_aes_xmit(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + struct mtk_ring *ring = cryp->ring[aes->id]; + struct mtk_desc *cmd = NULL, *res = NULL; + struct scatterlist *ssg = aes->src.sg, *dsg = aes->dst.sg; + u32 slen = aes->src.sg_len, dlen = aes->dst.sg_len; + int nents; + + /* Write command descriptors */ + for (nents = 0; nents < slen; ++nents, ssg = sg_next(ssg)) { + cmd = ring->cmd_base + ring->cmd_pos; + cmd->hdr = MTK_DESC_BUF_LEN(ssg->length); + cmd->buf = cpu_to_le32(sg_dma_address(ssg)); + + if (nents == 0) { + cmd->hdr |= MTK_DESC_FIRST | + MTK_DESC_CT_LEN(aes->ctx->ct_size); + cmd->ct = cpu_to_le32(aes->ctx->ct_dma); + cmd->ct_hdr = aes->ctx->ct_hdr; + cmd->tfm = cpu_to_le32(aes->ctx->tfm_dma); + } + + if (++ring->cmd_pos == MTK_DESC_NUM) + ring->cmd_pos = 0; + } + cmd->hdr |= MTK_DESC_LAST; + + /* Prepare result descriptors */ + for (nents = 0; nents < dlen; ++nents, dsg = sg_next(dsg)) { + res = ring->res_base + ring->res_pos; + res->hdr = MTK_DESC_BUF_LEN(dsg->length); + res->buf = cpu_to_le32(sg_dma_address(dsg)); + + if (nents == 0) + res->hdr |= MTK_DESC_FIRST; + + if (++ring->res_pos == MTK_DESC_NUM) + ring->res_pos = 0; + } + res->hdr |= MTK_DESC_LAST; + + /* Prepare enough space for authenticated tag */ + if (aes->flags & AES_FLAGS_GCM) + res->hdr += AES_BLOCK_SIZE; + + /* + * Make sure that all changes to the DMA ring are done before we + * start engine. + */ + wmb(); + /* Start DMA transfer */ + mtk_aes_write(cryp, RDR_PREP_COUNT(aes->id), MTK_DESC_CNT(dlen)); + mtk_aes_write(cryp, CDR_PREP_COUNT(aes->id), MTK_DESC_CNT(slen)); + + return -EINPROGRESS; +} + +static void mtk_aes_unmap(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + struct mtk_aes_base_ctx *ctx = aes->ctx; + + dma_unmap_single(cryp->dev, ctx->ct_dma, sizeof(ctx->ct), + DMA_TO_DEVICE); + dma_unmap_single(cryp->dev, ctx->tfm_dma, sizeof(ctx->tfm), + DMA_TO_DEVICE); + + if (aes->src.sg == aes->dst.sg) { + dma_unmap_sg(cryp->dev, aes->src.sg, aes->src.nents, + DMA_BIDIRECTIONAL); + + if (aes->src.sg != &aes->aligned_sg) + mtk_aes_restore_sg(&aes->src); + } else { + dma_unmap_sg(cryp->dev, aes->dst.sg, aes->dst.nents, + DMA_FROM_DEVICE); + + if (aes->dst.sg != &aes->aligned_sg) + mtk_aes_restore_sg(&aes->dst); + + dma_unmap_sg(cryp->dev, aes->src.sg, aes->src.nents, + DMA_TO_DEVICE); + + if (aes->src.sg != &aes->aligned_sg) + mtk_aes_restore_sg(&aes->src); + } + + if (aes->dst.sg == &aes->aligned_sg) + sg_copy_from_buffer(aes->real_dst, sg_nents(aes->real_dst), + aes->buf, aes->total); +} + +static int mtk_aes_map(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + struct mtk_aes_base_ctx *ctx = aes->ctx; + + ctx->ct_dma = dma_map_single(cryp->dev, &ctx->ct, sizeof(ctx->ct), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(cryp->dev, ctx->ct_dma))) + return -EINVAL; + + ctx->tfm_dma = dma_map_single(cryp->dev, &ctx->tfm, sizeof(ctx->tfm), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(cryp->dev, ctx->tfm_dma))) + goto tfm_map_err; + + if (aes->src.sg == aes->dst.sg) { + aes->src.sg_len = dma_map_sg(cryp->dev, aes->src.sg, + aes->src.nents, + DMA_BIDIRECTIONAL); + aes->dst.sg_len = aes->src.sg_len; + if (unlikely(!aes->src.sg_len)) + goto sg_map_err; + } else { + aes->src.sg_len = dma_map_sg(cryp->dev, aes->src.sg, + aes->src.nents, DMA_TO_DEVICE); + if (unlikely(!aes->src.sg_len)) + goto sg_map_err; + + aes->dst.sg_len = dma_map_sg(cryp->dev, aes->dst.sg, + aes->dst.nents, DMA_FROM_DEVICE); + if (unlikely(!aes->dst.sg_len)) { + dma_unmap_sg(cryp->dev, aes->src.sg, aes->src.nents, + DMA_TO_DEVICE); + goto sg_map_err; + } + } + + return mtk_aes_xmit(cryp, aes); + +sg_map_err: + dma_unmap_single(cryp->dev, ctx->tfm_dma, sizeof(ctx->tfm), + DMA_TO_DEVICE); +tfm_map_err: + dma_unmap_single(cryp->dev, ctx->ct_dma, sizeof(ctx->ct), + DMA_TO_DEVICE); + + return -EINVAL; +} + +/* Initialize transform information of CBC/ECB/CTR mode */ +static void mtk_aes_info_init(struct mtk_cryp *cryp, struct mtk_aes_rec *aes, + size_t len) +{ + struct ablkcipher_request *req = ablkcipher_request_cast(aes->areq); + struct mtk_aes_base_ctx *ctx = aes->ctx; + + ctx->ct_hdr = AES_CT_CTRL_HDR | cpu_to_le32(len); + ctx->ct.cmd[0] = AES_CMD0 | cpu_to_le32(len); + ctx->ct.cmd[1] = AES_CMD1; + + if (aes->flags & AES_FLAGS_ENCRYPT) + ctx->tfm.ctrl[0] = AES_TFM_BASIC_OUT; + else + ctx->tfm.ctrl[0] = AES_TFM_BASIC_IN; + + if (ctx->keylen == SIZE_IN_WORDS(AES_KEYSIZE_128)) + ctx->tfm.ctrl[0] |= AES_TFM_128BITS; + else if (ctx->keylen == SIZE_IN_WORDS(AES_KEYSIZE_256)) + ctx->tfm.ctrl[0] |= AES_TFM_256BITS; + else + ctx->tfm.ctrl[0] |= AES_TFM_192BITS; + + if (aes->flags & AES_FLAGS_CBC) { + const u32 *iv = (const u32 *)req->info; + u32 *iv_state = ctx->tfm.state + ctx->keylen; + int i; + + ctx->tfm.ctrl[0] |= AES_TFM_SIZE(ctx->keylen + + SIZE_IN_WORDS(AES_BLOCK_SIZE)); + ctx->tfm.ctrl[1] = AES_TFM_CBC | AES_TFM_FULL_IV; + + for (i = 0; i < SIZE_IN_WORDS(AES_BLOCK_SIZE); i++) + iv_state[i] = cpu_to_le32(iv[i]); + + ctx->ct.cmd[2] = AES_CMD2; + ctx->ct_size = AES_CT_SIZE_CBC; + } else if (aes->flags & AES_FLAGS_ECB) { + ctx->tfm.ctrl[0] |= AES_TFM_SIZE(ctx->keylen); + ctx->tfm.ctrl[1] = AES_TFM_ECB; + + ctx->ct_size = AES_CT_SIZE_ECB; + } else if (aes->flags & AES_FLAGS_CTR) { + ctx->tfm.ctrl[0] |= AES_TFM_SIZE(ctx->keylen + + SIZE_IN_WORDS(AES_BLOCK_SIZE)); + ctx->tfm.ctrl[1] = AES_TFM_CTR_LOAD | AES_TFM_FULL_IV; + + ctx->ct.cmd[2] = AES_CMD2; + ctx->ct_size = AES_CT_SIZE_CTR; + } +} + +static int mtk_aes_dma(struct mtk_cryp *cryp, struct mtk_aes_rec *aes, + struct scatterlist *src, struct scatterlist *dst, + size_t len) +{ + size_t padlen = 0; + bool src_aligned, dst_aligned; + + aes->total = len; + aes->src.sg = src; + aes->dst.sg = dst; + aes->real_dst = dst; + + src_aligned = mtk_aes_check_aligned(src, len, &aes->src); + if (src == dst) + dst_aligned = src_aligned; + else + dst_aligned = mtk_aes_check_aligned(dst, len, &aes->dst); + + if (!src_aligned || !dst_aligned) { + padlen = mtk_aes_padlen(len); + + if (len + padlen > AES_BUF_SIZE) + return -ENOMEM; + + if (!src_aligned) { + sg_copy_to_buffer(src, sg_nents(src), aes->buf, len); + aes->src.sg = &aes->aligned_sg; + aes->src.nents = 1; + aes->src.remainder = 0; + } + + if (!dst_aligned) { + aes->dst.sg = &aes->aligned_sg; + aes->dst.nents = 1; + aes->dst.remainder = 0; + } + + sg_init_table(&aes->aligned_sg, 1); + sg_set_buf(&aes->aligned_sg, aes->buf, len + padlen); + } + + mtk_aes_info_init(cryp, aes, len + padlen); + + return mtk_aes_map(cryp, aes); +} + +static int mtk_aes_handle_queue(struct mtk_cryp *cryp, u8 id, + struct crypto_async_request *new_areq) +{ + struct mtk_aes_rec *aes = cryp->aes[id]; + struct crypto_async_request *areq, *backlog; + struct mtk_aes_base_ctx *ctx; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&aes->lock, flags); + if (new_areq) + ret = crypto_enqueue_request(&aes->queue, new_areq); + if (aes->flags & AES_FLAGS_BUSY) { + spin_unlock_irqrestore(&aes->lock, flags); + return ret; + } + backlog = crypto_get_backlog(&aes->queue); + areq = crypto_dequeue_request(&aes->queue); + if (areq) + aes->flags |= AES_FLAGS_BUSY; + spin_unlock_irqrestore(&aes->lock, flags); + + if (!areq) + return ret; + + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + + ctx = crypto_tfm_ctx(areq->tfm); + + aes->areq = areq; + aes->ctx = ctx; + + return ctx->start(cryp, aes); +} + +static int mtk_aes_complete(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + aes->flags &= ~AES_FLAGS_BUSY; + aes->areq->complete(aes->areq, 0); + + /* Handle new request */ + return mtk_aes_handle_queue(cryp, aes->id, NULL); +} + +static int mtk_aes_start(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + struct ablkcipher_request *req = ablkcipher_request_cast(aes->areq); + struct mtk_aes_reqctx *rctx = ablkcipher_request_ctx(req); + + mtk_aes_set_mode(aes, rctx); + aes->resume = mtk_aes_complete; + + return mtk_aes_dma(cryp, aes, req->src, req->dst, req->nbytes); +} + +static inline struct mtk_aes_ctr_ctx * +mtk_aes_ctr_ctx_cast(struct mtk_aes_base_ctx *ctx) +{ + return container_of(ctx, struct mtk_aes_ctr_ctx, base); +} + +static int mtk_aes_ctr_transfer(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + struct mtk_aes_base_ctx *ctx = aes->ctx; + struct mtk_aes_ctr_ctx *cctx = mtk_aes_ctr_ctx_cast(ctx); + struct ablkcipher_request *req = ablkcipher_request_cast(aes->areq); + struct scatterlist *src, *dst; + int i; + u32 start, end, ctr, blocks, *iv_state; + size_t datalen; + bool fragmented = false; + + /* Check for transfer completion. */ + cctx->offset += aes->total; + if (cctx->offset >= req->nbytes) + return mtk_aes_complete(cryp, aes); + + /* Compute data length. */ + datalen = req->nbytes - cctx->offset; + blocks = DIV_ROUND_UP(datalen, AES_BLOCK_SIZE); + ctr = be32_to_cpu(cctx->iv[3]); + + /* Check 32bit counter overflow. */ + start = ctr; + end = start + blocks - 1; + if (end < start) { + ctr |= 0xffffffff; + datalen = AES_BLOCK_SIZE * -start; + fragmented = true; + } + + /* Jump to offset. */ + src = scatterwalk_ffwd(cctx->src, req->src, cctx->offset); + dst = ((req->src == req->dst) ? src : + scatterwalk_ffwd(cctx->dst, req->dst, cctx->offset)); + + /* Write IVs into transform state buffer. */ + iv_state = ctx->tfm.state + ctx->keylen; + for (i = 0; i < SIZE_IN_WORDS(AES_BLOCK_SIZE); i++) + iv_state[i] = cpu_to_le32(cctx->iv[i]); + + if (unlikely(fragmented)) { + /* + * Increment the counter manually to cope with the hardware + * counter overflow. + */ + cctx->iv[3] = cpu_to_be32(ctr); + crypto_inc((u8 *)cctx->iv, AES_BLOCK_SIZE); + } + aes->resume = mtk_aes_ctr_transfer; + + return mtk_aes_dma(cryp, aes, src, dst, datalen); +} + +static int mtk_aes_ctr_start(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + struct mtk_aes_ctr_ctx *cctx = mtk_aes_ctr_ctx_cast(aes->ctx); + struct ablkcipher_request *req = ablkcipher_request_cast(aes->areq); + struct mtk_aes_reqctx *rctx = ablkcipher_request_ctx(req); + + mtk_aes_set_mode(aes, rctx); + + memcpy(cctx->iv, req->info, AES_BLOCK_SIZE); + cctx->offset = 0; + aes->total = 0; + + return mtk_aes_ctr_transfer(cryp, aes); +} + +/* Check and set the AES key to transform state buffer */ +static int mtk_aes_setkey(struct crypto_ablkcipher *tfm, + const u8 *key, u32 keylen) +{ + struct mtk_aes_base_ctx *ctx = crypto_ablkcipher_ctx(tfm); + const u32 *aes_key = (const u32 *)key; + u32 *key_state = ctx->tfm.state; + int i; + + if (keylen != AES_KEYSIZE_128 && + keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_256) { + crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + ctx->keylen = SIZE_IN_WORDS(keylen); + + for (i = 0; i < ctx->keylen; i++) + key_state[i] = cpu_to_le32(aes_key[i]); + + return 0; +} + +static int mtk_aes_crypt(struct ablkcipher_request *req, u64 mode) +{ + struct mtk_aes_base_ctx *ctx; + struct mtk_aes_reqctx *rctx; + + ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); + rctx = ablkcipher_request_ctx(req); + rctx->mode = mode; + + return mtk_aes_handle_queue(ctx->cryp, !(mode & AES_FLAGS_ENCRYPT), + &req->base); +} + +static int mtk_aes_ecb_encrypt(struct ablkcipher_request *req) +{ + return mtk_aes_crypt(req, AES_FLAGS_ENCRYPT | AES_FLAGS_ECB); +} + +static int mtk_aes_ecb_decrypt(struct ablkcipher_request *req) +{ + return mtk_aes_crypt(req, AES_FLAGS_ECB); +} + +static int mtk_aes_cbc_encrypt(struct ablkcipher_request *req) +{ + return mtk_aes_crypt(req, AES_FLAGS_ENCRYPT | AES_FLAGS_CBC); +} + +static int mtk_aes_cbc_decrypt(struct ablkcipher_request *req) +{ + return mtk_aes_crypt(req, AES_FLAGS_CBC); +} + +static int mtk_aes_ctr_encrypt(struct ablkcipher_request *req) +{ + return mtk_aes_crypt(req, AES_FLAGS_ENCRYPT | AES_FLAGS_CTR); +} + +static int mtk_aes_ctr_decrypt(struct ablkcipher_request *req) +{ + return mtk_aes_crypt(req, AES_FLAGS_CTR); +} + +static int mtk_aes_cra_init(struct crypto_tfm *tfm) +{ + struct mtk_aes_ctx *ctx = crypto_tfm_ctx(tfm); + struct mtk_cryp *cryp = NULL; + + cryp = mtk_aes_find_dev(&ctx->base); + if (!cryp) { + pr_err("can't find crypto device\n"); + return -ENODEV; + } + + tfm->crt_ablkcipher.reqsize = sizeof(struct mtk_aes_reqctx); + ctx->base.start = mtk_aes_start; + return 0; +} + +static int mtk_aes_ctr_cra_init(struct crypto_tfm *tfm) +{ + struct mtk_aes_ctx *ctx = crypto_tfm_ctx(tfm); + struct mtk_cryp *cryp = NULL; + + cryp = mtk_aes_find_dev(&ctx->base); + if (!cryp) { + pr_err("can't find crypto device\n"); + return -ENODEV; + } + + tfm->crt_ablkcipher.reqsize = sizeof(struct mtk_aes_reqctx); + ctx->base.start = mtk_aes_ctr_start; + return 0; +} + +static struct crypto_alg aes_algs[] = { +{ + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-mtk", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC, + .cra_init = mtk_aes_cra_init, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_aes_ctx), + .cra_alignmask = 0xf, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = mtk_aes_setkey, + .encrypt = mtk_aes_cbc_encrypt, + .decrypt = mtk_aes_cbc_decrypt, + .ivsize = AES_BLOCK_SIZE, + } +}, +{ + .cra_name = "ecb(aes)", + .cra_driver_name = "ecb-aes-mtk", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC, + .cra_init = mtk_aes_cra_init, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_aes_ctx), + .cra_alignmask = 0xf, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = mtk_aes_setkey, + .encrypt = mtk_aes_ecb_encrypt, + .decrypt = mtk_aes_ecb_decrypt, + } +}, +{ + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-aes-mtk", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC, + .cra_init = mtk_aes_ctr_cra_init, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct mtk_aes_ctr_ctx), + .cra_alignmask = 0xf, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = mtk_aes_setkey, + .encrypt = mtk_aes_ctr_encrypt, + .decrypt = mtk_aes_ctr_decrypt, + } +}, +}; + +static inline struct mtk_aes_gcm_ctx * +mtk_aes_gcm_ctx_cast(struct mtk_aes_base_ctx *ctx) +{ + return container_of(ctx, struct mtk_aes_gcm_ctx, base); +} + +/* Initialize transform information of GCM mode */ +static void mtk_aes_gcm_info_init(struct mtk_cryp *cryp, + struct mtk_aes_rec *aes, + size_t len) +{ + struct aead_request *req = aead_request_cast(aes->areq); + struct mtk_aes_base_ctx *ctx = aes->ctx; + struct mtk_aes_gcm_ctx *gctx = mtk_aes_gcm_ctx_cast(ctx); + const u32 *iv = (const u32 *)req->iv; + u32 *iv_state = ctx->tfm.state + ctx->keylen + + SIZE_IN_WORDS(AES_BLOCK_SIZE); + u32 ivsize = crypto_aead_ivsize(crypto_aead_reqtfm(req)); + int i; + + ctx->ct_hdr = AES_CT_CTRL_HDR | len; + + ctx->ct.cmd[0] = AES_GCM_CMD0 | cpu_to_le32(req->assoclen); + ctx->ct.cmd[1] = AES_GCM_CMD1 | cpu_to_le32(req->assoclen); + ctx->ct.cmd[2] = AES_GCM_CMD2; + ctx->ct.cmd[3] = AES_GCM_CMD3 | cpu_to_le32(gctx->textlen); + + if (aes->flags & AES_FLAGS_ENCRYPT) { + ctx->ct.cmd[4] = AES_GCM_CMD4 | cpu_to_le32(gctx->authsize); + ctx->ct_size = AES_CT_SIZE_GCM_OUT; + ctx->tfm.ctrl[0] = AES_TFM_GCM_OUT; + } else { + ctx->ct.cmd[4] = AES_GCM_CMD5 | cpu_to_le32(gctx->authsize); + ctx->ct.cmd[5] = AES_GCM_CMD6 | cpu_to_le32(gctx->authsize); + ctx->ct_size = AES_CT_SIZE_GCM_IN; + ctx->tfm.ctrl[0] = AES_TFM_GCM_IN; + } + + if (ctx->keylen == SIZE_IN_WORDS(AES_KEYSIZE_128)) + ctx->tfm.ctrl[0] |= AES_TFM_128BITS; + else if (ctx->keylen == SIZE_IN_WORDS(AES_KEYSIZE_256)) + ctx->tfm.ctrl[0] |= AES_TFM_256BITS; + else + ctx->tfm.ctrl[0] |= AES_TFM_192BITS; + + ctx->tfm.ctrl[0] |= AES_TFM_GHASH_DIG | AES_TFM_GHASH | + AES_TFM_SIZE(ctx->keylen + SIZE_IN_WORDS( + AES_BLOCK_SIZE + ivsize)); + ctx->tfm.ctrl[1] = AES_TFM_CTR_INIT | AES_TFM_IV_CTR_MODE | + AES_TFM_3IV | AES_TFM_ENC_HASH; + + for (i = 0; i < SIZE_IN_WORDS(ivsize); i++) + iv_state[i] = cpu_to_le32(iv[i]); +} + +static int mtk_aes_gcm_dma(struct mtk_cryp *cryp, struct mtk_aes_rec *aes, + struct scatterlist *src, struct scatterlist *dst, + size_t len) +{ + bool src_aligned, dst_aligned; + + aes->src.sg = src; + aes->dst.sg = dst; + aes->real_dst = dst; + + src_aligned = mtk_aes_check_aligned(src, len, &aes->src); + if (src == dst) + dst_aligned = src_aligned; + else + dst_aligned = mtk_aes_check_aligned(dst, len, &aes->dst); + + if (!src_aligned || !dst_aligned) { + if (aes->total > AES_BUF_SIZE) + return -ENOMEM; + + if (!src_aligned) { + sg_copy_to_buffer(src, sg_nents(src), aes->buf, len); + aes->src.sg = &aes->aligned_sg; + aes->src.nents = 1; + aes->src.remainder = 0; + } + + if (!dst_aligned) { + aes->dst.sg = &aes->aligned_sg; + aes->dst.nents = 1; + aes->dst.remainder = 0; + } + + sg_init_table(&aes->aligned_sg, 1); + sg_set_buf(&aes->aligned_sg, aes->buf, aes->total); + } + + mtk_aes_gcm_info_init(cryp, aes, len); + + return mtk_aes_map(cryp, aes); +} + +/* Todo: GMAC */ +static int mtk_aes_gcm_start(struct mtk_cryp *cryp, struct mtk_aes_rec *aes) +{ + struct mtk_aes_gcm_ctx *gctx = mtk_aes_gcm_ctx_cast(aes->ctx); + struct aead_request *req = aead_request_cast(aes->areq); + struct mtk_aes_reqctx *rctx = aead_request_ctx(req); + u32 len = req->assoclen + req->cryptlen; + + mtk_aes_set_mode(aes, rctx); + + if (aes->flags & AES_FLAGS_ENCRYPT) { + u32 tag[4]; + /* Compute total process length. */ + aes->total = len + gctx->authsize; + /* Compute text length. */ + gctx->textlen = req->cryptlen; + /* Hardware will append authenticated tag to output buffer */ + scatterwalk_map_and_copy(tag, req->dst, len, gctx->authsize, 1); + } else { + aes->total = len; + gctx->textlen = req->cryptlen - gctx->authsize; + } + aes->resume = mtk_aes_complete; + + return mtk_aes_gcm_dma(cryp, aes, req->src, req->dst, len); +} + +static int mtk_aes_gcm_crypt(struct aead_request *req, u64 mode) +{ + struct mtk_aes_base_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); + struct mtk_aes_reqctx *rctx = aead_request_ctx(req); + + rctx->mode = AES_FLAGS_GCM | mode; + + return mtk_aes_handle_queue(ctx->cryp, !!(mode & AES_FLAGS_ENCRYPT), + &req->base); +} + +static void mtk_gcm_setkey_done(struct crypto_async_request *req, int err) +{ + struct mtk_aes_gcm_setkey_result *result = req->data; + + if (err == -EINPROGRESS) + return; + + result->err = err; + complete(&result->completion); +} + +/* + * Because of the hardware limitation, we need to pre-calculate key(H) + * for the GHASH operation. The result of the encryption operation + * need to be stored in the transform state buffer. + */ +static int mtk_aes_gcm_setkey(struct crypto_aead *aead, const u8 *key, + u32 keylen) +{ + struct mtk_aes_base_ctx *ctx = crypto_aead_ctx(aead); + struct mtk_aes_gcm_ctx *gctx = mtk_aes_gcm_ctx_cast(ctx); + struct crypto_skcipher *ctr = gctx->ctr; + struct { + u32 hash[4]; + u8 iv[8]; + + struct mtk_aes_gcm_setkey_result result; + + struct scatterlist sg[1]; + struct skcipher_request req; + } *data; + const u32 *aes_key; + u32 *key_state, *hash_state; + int err, i; + + if (keylen != AES_KEYSIZE_256 && + keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_128) { + crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + key_state = ctx->tfm.state; + aes_key = (u32 *)key; + ctx->keylen = SIZE_IN_WORDS(keylen); + + for (i = 0; i < ctx->keylen; i++) + ctx->tfm.state[i] = cpu_to_le32(aes_key[i]); + + /* Same as crypto_gcm_setkey() from crypto/gcm.c */ + crypto_skcipher_clear_flags(ctr, CRYPTO_TFM_REQ_MASK); + crypto_skcipher_set_flags(ctr, crypto_aead_get_flags(aead) & + CRYPTO_TFM_REQ_MASK); + err = crypto_skcipher_setkey(ctr, key, keylen); + crypto_aead_set_flags(aead, crypto_skcipher_get_flags(ctr) & + CRYPTO_TFM_RES_MASK); + if (err) + return err; + + data = kzalloc(sizeof(*data) + crypto_skcipher_reqsize(ctr), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + init_completion(&data->result.completion); + sg_init_one(data->sg, &data->hash, AES_BLOCK_SIZE); + skcipher_request_set_tfm(&data->req, ctr); + skcipher_request_set_callback(&data->req, CRYPTO_TFM_REQ_MAY_SLEEP | + CRYPTO_TFM_REQ_MAY_BACKLOG, + mtk_gcm_setkey_done, &data->result); + skcipher_request_set_crypt(&data->req, data->sg, data->sg, + AES_BLOCK_SIZE, data->iv); + + err = crypto_skcipher_encrypt(&data->req); + if (err == -EINPROGRESS || err == -EBUSY) { + err = wait_for_completion_interruptible( + &data->result.completion); + if (!err) + err = data->result.err; + } + if (err) + goto out; + + hash_state = key_state + ctx->keylen; + + for (i = 0; i < 4; i++) + hash_state[i] = cpu_to_be32(data->hash[i]); +out: + kzfree(data); + return err; +} + +static int mtk_aes_gcm_setauthsize(struct crypto_aead *aead, + u32 authsize) +{ + struct mtk_aes_base_ctx *ctx = crypto_aead_ctx(aead); + struct mtk_aes_gcm_ctx *gctx = mtk_aes_gcm_ctx_cast(ctx); + + /* Same as crypto_gcm_authsize() from crypto/gcm.c */ + switch (authsize) { + case 8: + case 12: + case 16: + break; + default: + return -EINVAL; + } + + gctx->authsize = authsize; + return 0; +} + +static int mtk_aes_gcm_encrypt(struct aead_request *req) +{ + return mtk_aes_gcm_crypt(req, AES_FLAGS_ENCRYPT); +} + +static int mtk_aes_gcm_decrypt(struct aead_request *req) +{ + return mtk_aes_gcm_crypt(req, 0); +} + +static int mtk_aes_gcm_init(struct crypto_aead *aead) +{ + struct mtk_aes_gcm_ctx *ctx = crypto_aead_ctx(aead); + struct mtk_cryp *cryp = NULL; + + cryp = mtk_aes_find_dev(&ctx->base); + if (!cryp) { + pr_err("can't find crypto device\n"); + return -ENODEV; + } + + ctx->ctr = crypto_alloc_skcipher("ctr(aes)", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(ctx->ctr)) { + pr_err("Error allocating ctr(aes)\n"); + return PTR_ERR(ctx->ctr); + } + + crypto_aead_set_reqsize(aead, sizeof(struct mtk_aes_reqctx)); + ctx->base.start = mtk_aes_gcm_start; + return 0; +} + +static void mtk_aes_gcm_exit(struct crypto_aead *aead) +{ + struct mtk_aes_gcm_ctx *ctx = crypto_aead_ctx(aead); + + crypto_free_skcipher(ctx->ctr); +} + +static struct aead_alg aes_gcm_alg = { + .setkey = mtk_aes_gcm_setkey, + .setauthsize = mtk_aes_gcm_setauthsize, + .encrypt = mtk_aes_gcm_encrypt, + .decrypt = mtk_aes_gcm_decrypt, + .init = mtk_aes_gcm_init, + .exit = mtk_aes_gcm_exit, + .ivsize = 12, + .maxauthsize = AES_BLOCK_SIZE, + + .base = { + .cra_name = "gcm(aes)", + .cra_driver_name = "gcm-aes-mtk", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct mtk_aes_gcm_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, + }, +}; + +static void mtk_aes_enc_task(unsigned long data) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)data; + struct mtk_aes_rec *aes = cryp->aes[0]; + + mtk_aes_unmap(cryp, aes); + aes->resume(cryp, aes); +} + +static void mtk_aes_dec_task(unsigned long data) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)data; + struct mtk_aes_rec *aes = cryp->aes[1]; + + mtk_aes_unmap(cryp, aes); + aes->resume(cryp, aes); +} + +static irqreturn_t mtk_aes_enc_irq(int irq, void *dev_id) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id; + struct mtk_aes_rec *aes = cryp->aes[0]; + u32 val = mtk_aes_read(cryp, RDR_STAT(RING0)); + + mtk_aes_write(cryp, RDR_STAT(RING0), val); + + if (likely(AES_FLAGS_BUSY & aes->flags)) { + mtk_aes_write(cryp, RDR_PROC_COUNT(RING0), MTK_CNT_RST); + mtk_aes_write(cryp, RDR_THRESH(RING0), + MTK_RDR_PROC_THRESH | MTK_RDR_PROC_MODE); + + tasklet_schedule(&aes->task); + } else { + dev_warn(cryp->dev, "AES interrupt when no active requests.\n"); + } + return IRQ_HANDLED; +} + +static irqreturn_t mtk_aes_dec_irq(int irq, void *dev_id) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id; + struct mtk_aes_rec *aes = cryp->aes[1]; + u32 val = mtk_aes_read(cryp, RDR_STAT(RING1)); + + mtk_aes_write(cryp, RDR_STAT(RING1), val); + + if (likely(AES_FLAGS_BUSY & aes->flags)) { + mtk_aes_write(cryp, RDR_PROC_COUNT(RING1), MTK_CNT_RST); + mtk_aes_write(cryp, RDR_THRESH(RING1), + MTK_RDR_PROC_THRESH | MTK_RDR_PROC_MODE); + + tasklet_schedule(&aes->task); + } else { + dev_warn(cryp->dev, "AES interrupt when no active requests.\n"); + } + return IRQ_HANDLED; +} + +/* + * The purpose of creating encryption and decryption records is + * to process outbound/inbound data in parallel, it can improve + * performance in most use cases, such as IPSec VPN, especially + * under heavy network traffic. + */ +static int mtk_aes_record_init(struct mtk_cryp *cryp) +{ + struct mtk_aes_rec **aes = cryp->aes; + int i, err = -ENOMEM; + + for (i = 0; i < MTK_REC_NUM; i++) { + aes[i] = kzalloc(sizeof(**aes), GFP_KERNEL); + if (!aes[i]) + goto err_cleanup; + + aes[i]->buf = (void *)__get_free_pages(GFP_KERNEL, + AES_BUF_ORDER); + if (!aes[i]->buf) + goto err_cleanup; + + aes[i]->id = i; + + spin_lock_init(&aes[i]->lock); + crypto_init_queue(&aes[i]->queue, AES_QUEUE_SIZE); + } + + tasklet_init(&aes[0]->task, mtk_aes_enc_task, (unsigned long)cryp); + tasklet_init(&aes[1]->task, mtk_aes_dec_task, (unsigned long)cryp); + + return 0; + +err_cleanup: + for (; i--; ) { + free_page((unsigned long)aes[i]->buf); + kfree(aes[i]); + } + + return err; +} + +static void mtk_aes_record_free(struct mtk_cryp *cryp) +{ + int i; + + for (i = 0; i < MTK_REC_NUM; i++) { + tasklet_kill(&cryp->aes[i]->task); + free_page((unsigned long)cryp->aes[i]->buf); + kfree(cryp->aes[i]); + } +} + +static void mtk_aes_unregister_algs(void) +{ + int i; + + crypto_unregister_aead(&aes_gcm_alg); + + for (i = 0; i < ARRAY_SIZE(aes_algs); i++) + crypto_unregister_alg(&aes_algs[i]); +} + +static int mtk_aes_register_algs(void) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(aes_algs); i++) { + err = crypto_register_alg(&aes_algs[i]); + if (err) + goto err_aes_algs; + } + + err = crypto_register_aead(&aes_gcm_alg); + if (err) + goto err_aes_algs; + + return 0; + +err_aes_algs: + for (; i--; ) + crypto_unregister_alg(&aes_algs[i]); + + return err; +} + +int mtk_cipher_alg_register(struct mtk_cryp *cryp) +{ + int ret; + + INIT_LIST_HEAD(&cryp->aes_list); + + /* Initialize two cipher records */ + ret = mtk_aes_record_init(cryp); + if (ret) + goto err_record; + + /* Ring0 is use by encryption record */ + ret = devm_request_irq(cryp->dev, cryp->irq[RING0], mtk_aes_enc_irq, + IRQF_TRIGGER_LOW, "mtk-aes", cryp); + if (ret) { + dev_err(cryp->dev, "unable to request AES encryption irq.\n"); + goto err_res; + } + + /* Ring1 is use by decryption record */ + ret = devm_request_irq(cryp->dev, cryp->irq[RING1], mtk_aes_dec_irq, + IRQF_TRIGGER_LOW, "mtk-aes", cryp); + if (ret) { + dev_err(cryp->dev, "unable to request AES decryption irq.\n"); + goto err_res; + } + + /* Enable ring0 and ring1 interrupt */ + mtk_aes_write(cryp, AIC_ENABLE_SET(RING0), MTK_IRQ_RDR0); + mtk_aes_write(cryp, AIC_ENABLE_SET(RING1), MTK_IRQ_RDR1); + + spin_lock(&mtk_aes.lock); + list_add_tail(&cryp->aes_list, &mtk_aes.dev_list); + spin_unlock(&mtk_aes.lock); + + ret = mtk_aes_register_algs(); + if (ret) + goto err_algs; + + return 0; + +err_algs: + spin_lock(&mtk_aes.lock); + list_del(&cryp->aes_list); + spin_unlock(&mtk_aes.lock); +err_res: + mtk_aes_record_free(cryp); +err_record: + + dev_err(cryp->dev, "mtk-aes initialization failed.\n"); + return ret; +} + +void mtk_cipher_alg_release(struct mtk_cryp *cryp) +{ + spin_lock(&mtk_aes.lock); + list_del(&cryp->aes_list); + spin_unlock(&mtk_aes.lock); + + mtk_aes_unregister_algs(); + mtk_aes_record_free(cryp); +} diff --git a/drivers/crypto/mediatek/mtk-platform.c b/drivers/crypto/mediatek/mtk-platform.c new file mode 100644 index 000000000000..a9c713d4c733 --- /dev/null +++ b/drivers/crypto/mediatek/mtk-platform.c @@ -0,0 +1,604 @@ +/* + * Driver for EIP97 cryptographic accelerator. + * + * Copyright (c) 2016 Ryder Lee <ryder.lee@mediatek.com> + * + * 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/clk.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include "mtk-platform.h" + +#define MTK_BURST_SIZE_MSK GENMASK(7, 4) +#define MTK_BURST_SIZE(x) ((x) << 4) +#define MTK_DESC_SIZE(x) ((x) << 0) +#define MTK_DESC_OFFSET(x) ((x) << 16) +#define MTK_DESC_FETCH_SIZE(x) ((x) << 0) +#define MTK_DESC_FETCH_THRESH(x) ((x) << 16) +#define MTK_DESC_OVL_IRQ_EN BIT(25) +#define MTK_DESC_ATP_PRESENT BIT(30) + +#define MTK_DFSE_IDLE GENMASK(3, 0) +#define MTK_DFSE_THR_CTRL_EN BIT(30) +#define MTK_DFSE_THR_CTRL_RESET BIT(31) +#define MTK_DFSE_RING_ID(x) (((x) >> 12) & GENMASK(3, 0)) +#define MTK_DFSE_MIN_DATA(x) ((x) << 0) +#define MTK_DFSE_MAX_DATA(x) ((x) << 8) +#define MTK_DFE_MIN_CTRL(x) ((x) << 16) +#define MTK_DFE_MAX_CTRL(x) ((x) << 24) + +#define MTK_IN_BUF_MIN_THRESH(x) ((x) << 8) +#define MTK_IN_BUF_MAX_THRESH(x) ((x) << 12) +#define MTK_OUT_BUF_MIN_THRESH(x) ((x) << 0) +#define MTK_OUT_BUF_MAX_THRESH(x) ((x) << 4) +#define MTK_IN_TBUF_SIZE(x) (((x) >> 4) & GENMASK(3, 0)) +#define MTK_IN_DBUF_SIZE(x) (((x) >> 8) & GENMASK(3, 0)) +#define MTK_OUT_DBUF_SIZE(x) (((x) >> 16) & GENMASK(3, 0)) +#define MTK_CMD_FIFO_SIZE(x) (((x) >> 8) & GENMASK(3, 0)) +#define MTK_RES_FIFO_SIZE(x) (((x) >> 12) & GENMASK(3, 0)) + +#define MTK_PE_TK_LOC_AVL BIT(2) +#define MTK_PE_PROC_HELD BIT(14) +#define MTK_PE_TK_TIMEOUT_EN BIT(22) +#define MTK_PE_INPUT_DMA_ERR BIT(0) +#define MTK_PE_OUTPUT_DMA_ERR BIT(1) +#define MTK_PE_PKT_PORC_ERR BIT(2) +#define MTK_PE_PKT_TIMEOUT BIT(3) +#define MTK_PE_FATAL_ERR BIT(14) +#define MTK_PE_INPUT_DMA_ERR_EN BIT(16) +#define MTK_PE_OUTPUT_DMA_ERR_EN BIT(17) +#define MTK_PE_PKT_PORC_ERR_EN BIT(18) +#define MTK_PE_PKT_TIMEOUT_EN BIT(19) +#define MTK_PE_FATAL_ERR_EN BIT(30) +#define MTK_PE_INT_OUT_EN BIT(31) + +#define MTK_HIA_SIGNATURE ((u16)0x35ca) +#define MTK_HIA_DATA_WIDTH(x) (((x) >> 25) & GENMASK(1, 0)) +#define MTK_HIA_DMA_LENGTH(x) (((x) >> 20) & GENMASK(4, 0)) +#define MTK_CDR_STAT_CLR GENMASK(4, 0) +#define MTK_RDR_STAT_CLR GENMASK(7, 0) + +#define MTK_AIC_INT_MSK GENMASK(5, 0) +#define MTK_AIC_VER_MSK (GENMASK(15, 0) | GENMASK(27, 20)) +#define MTK_AIC_VER11 0x011036c9 +#define MTK_AIC_VER12 0x012036c9 +#define MTK_AIC_G_CLR GENMASK(30, 20) + +/** + * EIP97 is an integrated security subsystem to accelerate cryptographic + * functions and protocols to offload the host processor. + * Some important hardware modules are briefly introduced below: + * + * Host Interface Adapter(HIA) - the main interface between the host + * system and the hardware subsystem. It is responsible for attaching + * processing engine to the specific host bus interface and provides a + * standardized software view for off loading tasks to the engine. + * + * Command Descriptor Ring Manager(CDR Manager) - keeps track of how many + * CD the host has prepared in the CDR. It monitors the fill level of its + * CD-FIFO and if there's sufficient space for the next block of descriptors, + * then it fires off a DMA request to fetch a block of CDs. + * + * Data fetch engine(DFE) - It is responsible for parsing the CD and + * setting up the required control and packet data DMA transfers from + * system memory to the processing engine. + * + * Result Descriptor Ring Manager(RDR Manager) - same as CDR Manager, + * but target is result descriptors, Moreover, it also handles the RD + * updates under control of the DSE. For each packet data segment + * processed, the DSE triggers the RDR Manager to write the updated RD. + * If triggered to update, the RDR Manager sets up a DMA operation to + * copy the RD from the DSE to the correct location in the RDR. + * + * Data Store Engine(DSE) - It is responsible for parsing the prepared RD + * and setting up the required control and packet data DMA transfers from + * the processing engine to system memory. + * + * Advanced Interrupt Controllers(AICs) - receive interrupt request signals + * from various sources and combine them into one interrupt output. + * The AICs are used by: + * - One for the HIA global and processing engine interrupts. + * - The others for the descriptor ring interrupts. + */ + +/* Cryptographic engine capabilities */ +struct mtk_sys_cap { + /* host interface adapter */ + u32 hia_ver; + u32 hia_opt; + /* packet engine */ + u32 pkt_eng_opt; + /* global hardware */ + u32 hw_opt; +}; + +static void mtk_desc_ring_link(struct mtk_cryp *cryp, u32 mask) +{ + /* Assign rings to DFE/DSE thread and enable it */ + writel(MTK_DFSE_THR_CTRL_EN | mask, cryp->base + DFE_THR_CTRL); + writel(MTK_DFSE_THR_CTRL_EN | mask, cryp->base + DSE_THR_CTRL); +} + +static void mtk_dfe_dse_buf_setup(struct mtk_cryp *cryp, + struct mtk_sys_cap *cap) +{ + u32 width = MTK_HIA_DATA_WIDTH(cap->hia_opt) + 2; + u32 len = MTK_HIA_DMA_LENGTH(cap->hia_opt) - 1; + u32 ipbuf = min((u32)MTK_IN_DBUF_SIZE(cap->hw_opt) + width, len); + u32 opbuf = min((u32)MTK_OUT_DBUF_SIZE(cap->hw_opt) + width, len); + u32 itbuf = min((u32)MTK_IN_TBUF_SIZE(cap->hw_opt) + width, len); + + writel(MTK_DFSE_MIN_DATA(ipbuf - 1) | + MTK_DFSE_MAX_DATA(ipbuf) | + MTK_DFE_MIN_CTRL(itbuf - 1) | + MTK_DFE_MAX_CTRL(itbuf), + cryp->base + DFE_CFG); + + writel(MTK_DFSE_MIN_DATA(opbuf - 1) | + MTK_DFSE_MAX_DATA(opbuf), + cryp->base + DSE_CFG); + + writel(MTK_IN_BUF_MIN_THRESH(ipbuf - 1) | + MTK_IN_BUF_MAX_THRESH(ipbuf), + cryp->base + PE_IN_DBUF_THRESH); + + writel(MTK_IN_BUF_MIN_THRESH(itbuf - 1) | + MTK_IN_BUF_MAX_THRESH(itbuf), + cryp->base + PE_IN_TBUF_THRESH); + + writel(MTK_OUT_BUF_MIN_THRESH(opbuf - 1) | + MTK_OUT_BUF_MAX_THRESH(opbuf), + cryp->base + PE_OUT_DBUF_THRESH); + + writel(0, cryp->base + PE_OUT_TBUF_THRESH); + writel(0, cryp->base + PE_OUT_BUF_CTRL); +} + +static int mtk_dfe_dse_state_check(struct mtk_cryp *cryp) +{ + int ret = -EINVAL; + u32 val; + + /* Check for completion of all DMA transfers */ + val = readl(cryp->base + DFE_THR_STAT); + if (MTK_DFSE_RING_ID(val) == MTK_DFSE_IDLE) { + val = readl(cryp->base + DSE_THR_STAT); + if (MTK_DFSE_RING_ID(val) == MTK_DFSE_IDLE) + ret = 0; + } + + if (!ret) { + /* Take DFE/DSE thread out of reset */ + writel(0, cryp->base + DFE_THR_CTRL); + writel(0, cryp->base + DSE_THR_CTRL); + } else { + return -EBUSY; + } + + return 0; +} + +static int mtk_dfe_dse_reset(struct mtk_cryp *cryp) +{ + int err; + + /* Reset DSE/DFE and correct system priorities for all rings. */ + writel(MTK_DFSE_THR_CTRL_RESET, cryp->base + DFE_THR_CTRL); + writel(0, cryp->base + DFE_PRIO_0); + writel(0, cryp->base + DFE_PRIO_1); + writel(0, cryp->base + DFE_PRIO_2); + writel(0, cryp->base + DFE_PRIO_3); + + writel(MTK_DFSE_THR_CTRL_RESET, cryp->base + DSE_THR_CTRL); + writel(0, cryp->base + DSE_PRIO_0); + writel(0, cryp->base + DSE_PRIO_1); + writel(0, cryp->base + DSE_PRIO_2); + writel(0, cryp->base + DSE_PRIO_3); + + err = mtk_dfe_dse_state_check(cryp); + if (err) + return err; + + return 0; +} + +static void mtk_cmd_desc_ring_setup(struct mtk_cryp *cryp, + int i, struct mtk_sys_cap *cap) +{ + /* Full descriptor that fits FIFO minus one */ + u32 count = + ((1 << MTK_CMD_FIFO_SIZE(cap->hia_opt)) / MTK_DESC_SZ) - 1; + + /* Temporarily disable external triggering */ + writel(0, cryp->base + CDR_CFG(i)); + + /* Clear CDR count */ + writel(MTK_CNT_RST, cryp->base + CDR_PREP_COUNT(i)); + writel(MTK_CNT_RST, cryp->base + CDR_PROC_COUNT(i)); + + writel(0, cryp->base + CDR_PREP_PNTR(i)); + writel(0, cryp->base + CDR_PROC_PNTR(i)); + writel(0, cryp->base + CDR_DMA_CFG(i)); + + /* Configure CDR host address space */ + writel(0, cryp->base + CDR_BASE_ADDR_HI(i)); + writel(cryp->ring[i]->cmd_dma, cryp->base + CDR_BASE_ADDR_LO(i)); + + writel(MTK_DESC_RING_SZ, cryp->base + CDR_RING_SIZE(i)); + + /* Clear and disable all CDR interrupts */ + writel(MTK_CDR_STAT_CLR, cryp->base + CDR_STAT(i)); + + /* + * Set command descriptor offset and enable additional + * token present in descriptor. + */ + writel(MTK_DESC_SIZE(MTK_DESC_SZ) | + MTK_DESC_OFFSET(MTK_DESC_OFF) | + MTK_DESC_ATP_PRESENT, + cryp->base + CDR_DESC_SIZE(i)); + + writel(MTK_DESC_FETCH_SIZE(count * MTK_DESC_OFF) | + MTK_DESC_FETCH_THRESH(count * MTK_DESC_SZ), + cryp->base + CDR_CFG(i)); +} + +static void mtk_res_desc_ring_setup(struct mtk_cryp *cryp, + int i, struct mtk_sys_cap *cap) +{ + u32 rndup = 2; + u32 count = ((1 << MTK_RES_FIFO_SIZE(cap->hia_opt)) / rndup) - 1; + + /* Temporarily disable external triggering */ + writel(0, cryp->base + RDR_CFG(i)); + + /* Clear RDR count */ + writel(MTK_CNT_RST, cryp->base + RDR_PREP_COUNT(i)); + writel(MTK_CNT_RST, cryp->base + RDR_PROC_COUNT(i)); + + writel(0, cryp->base + RDR_PREP_PNTR(i)); + writel(0, cryp->base + RDR_PROC_PNTR(i)); + writel(0, cryp->base + RDR_DMA_CFG(i)); + + /* Configure RDR host address space */ + writel(0, cryp->base + RDR_BASE_ADDR_HI(i)); + writel(cryp->ring[i]->res_dma, cryp->base + RDR_BASE_ADDR_LO(i)); + + writel(MTK_DESC_RING_SZ, cryp->base + RDR_RING_SIZE(i)); + writel(MTK_RDR_STAT_CLR, cryp->base + RDR_STAT(i)); + + /* + * RDR manager generates update interrupts on a per-completed-packet, + * and the rd_proc_thresh_irq interrupt is fired when proc_pkt_count + * for the RDR exceeds the number of packets. + */ + writel(MTK_RDR_PROC_THRESH | MTK_RDR_PROC_MODE, + cryp->base + RDR_THRESH(i)); + + /* + * Configure a threshold and time-out value for the processed + * result descriptors (or complete packets) that are written to + * the RDR. + */ + writel(MTK_DESC_SIZE(MTK_DESC_SZ) | MTK_DESC_OFFSET(MTK_DESC_OFF), + cryp->base + RDR_DESC_SIZE(i)); + + /* + * Configure HIA fetch size and fetch threshold that are used to + * fetch blocks of multiple descriptors. + */ + writel(MTK_DESC_FETCH_SIZE(count * MTK_DESC_OFF) | + MTK_DESC_FETCH_THRESH(count * rndup) | + MTK_DESC_OVL_IRQ_EN, + cryp->base + RDR_CFG(i)); +} + +static int mtk_packet_engine_setup(struct mtk_cryp *cryp) +{ + struct mtk_sys_cap cap; + int i, err; + u32 val; + + cap.hia_ver = readl(cryp->base + HIA_VERSION); + cap.hia_opt = readl(cryp->base + HIA_OPTIONS); + cap.hw_opt = readl(cryp->base + EIP97_OPTIONS); + + if (!(((u16)cap.hia_ver) == MTK_HIA_SIGNATURE)) + return -EINVAL; + + /* Configure endianness conversion method for master (DMA) interface */ + writel(0, cryp->base + EIP97_MST_CTRL); + + /* Set HIA burst size */ + val = readl(cryp->base + HIA_MST_CTRL); + val &= ~MTK_BURST_SIZE_MSK; + val |= MTK_BURST_SIZE(5); + writel(val, cryp->base + HIA_MST_CTRL); + + err = mtk_dfe_dse_reset(cryp); + if (err) { + dev_err(cryp->dev, "Failed to reset DFE and DSE.\n"); + return err; + } + + mtk_dfe_dse_buf_setup(cryp, &cap); + + /* Enable the 4 rings for the packet engines. */ + mtk_desc_ring_link(cryp, 0xf); + + for (i = 0; i < RING_MAX; i++) { + mtk_cmd_desc_ring_setup(cryp, i, &cap); + mtk_res_desc_ring_setup(cryp, i, &cap); + } + + writel(MTK_PE_TK_LOC_AVL | MTK_PE_PROC_HELD | MTK_PE_TK_TIMEOUT_EN, + cryp->base + PE_TOKEN_CTRL_STAT); + + /* Clear all pending interrupts */ + writel(MTK_AIC_G_CLR, cryp->base + AIC_G_ACK); + writel(MTK_PE_INPUT_DMA_ERR | MTK_PE_OUTPUT_DMA_ERR | + MTK_PE_PKT_PORC_ERR | MTK_PE_PKT_TIMEOUT | + MTK_PE_FATAL_ERR | MTK_PE_INPUT_DMA_ERR_EN | + MTK_PE_OUTPUT_DMA_ERR_EN | MTK_PE_PKT_PORC_ERR_EN | + MTK_PE_PKT_TIMEOUT_EN | MTK_PE_FATAL_ERR_EN | + MTK_PE_INT_OUT_EN, + cryp->base + PE_INTERRUPT_CTRL_STAT); + + return 0; +} + +static int mtk_aic_cap_check(struct mtk_cryp *cryp, int hw) +{ + u32 val; + + if (hw == RING_MAX) + val = readl(cryp->base + AIC_G_VERSION); + else + val = readl(cryp->base + AIC_VERSION(hw)); + + val &= MTK_AIC_VER_MSK; + if (val != MTK_AIC_VER11 && val != MTK_AIC_VER12) + return -ENXIO; + + if (hw == RING_MAX) + val = readl(cryp->base + AIC_G_OPTIONS); + else + val = readl(cryp->base + AIC_OPTIONS(hw)); + + val &= MTK_AIC_INT_MSK; + if (!val || val > 32) + return -ENXIO; + + return 0; +} + +static int mtk_aic_init(struct mtk_cryp *cryp, int hw) +{ + int err; + + err = mtk_aic_cap_check(cryp, hw); + if (err) + return err; + + /* Disable all interrupts and set initial configuration */ + if (hw == RING_MAX) { + writel(0, cryp->base + AIC_G_ENABLE_CTRL); + writel(0, cryp->base + AIC_G_POL_CTRL); + writel(0, cryp->base + AIC_G_TYPE_CTRL); + writel(0, cryp->base + AIC_G_ENABLE_SET); + } else { + writel(0, cryp->base + AIC_ENABLE_CTRL(hw)); + writel(0, cryp->base + AIC_POL_CTRL(hw)); + writel(0, cryp->base + AIC_TYPE_CTRL(hw)); + writel(0, cryp->base + AIC_ENABLE_SET(hw)); + } + + return 0; +} + +static int mtk_accelerator_init(struct mtk_cryp *cryp) +{ + int i, err; + + /* Initialize advanced interrupt controller(AIC) */ + for (i = 0; i < MTK_IRQ_NUM; i++) { + err = mtk_aic_init(cryp, i); + if (err) { + dev_err(cryp->dev, "Failed to initialize AIC.\n"); + return err; + } + } + + /* Initialize packet engine */ + err = mtk_packet_engine_setup(cryp); + if (err) { + dev_err(cryp->dev, "Failed to configure packet engine.\n"); + return err; + } + + return 0; +} + +static void mtk_desc_dma_free(struct mtk_cryp *cryp) +{ + int i; + + for (i = 0; i < RING_MAX; i++) { + dma_free_coherent(cryp->dev, MTK_DESC_RING_SZ, + cryp->ring[i]->res_base, + cryp->ring[i]->res_dma); + dma_free_coherent(cryp->dev, MTK_DESC_RING_SZ, + cryp->ring[i]->cmd_base, + cryp->ring[i]->cmd_dma); + kfree(cryp->ring[i]); + } +} + +static int mtk_desc_ring_alloc(struct mtk_cryp *cryp) +{ + struct mtk_ring **ring = cryp->ring; + int i, err = ENOMEM; + + for (i = 0; i < RING_MAX; i++) { + ring[i] = kzalloc(sizeof(**ring), GFP_KERNEL); + if (!ring[i]) + goto err_cleanup; + + ring[i]->cmd_base = dma_zalloc_coherent(cryp->dev, + MTK_DESC_RING_SZ, + &ring[i]->cmd_dma, + GFP_KERNEL); + if (!ring[i]->cmd_base) + goto err_cleanup; + + ring[i]->res_base = dma_zalloc_coherent(cryp->dev, + MTK_DESC_RING_SZ, + &ring[i]->res_dma, + GFP_KERNEL); + if (!ring[i]->res_base) + goto err_cleanup; + } + return 0; + +err_cleanup: + for (; i--; ) { + dma_free_coherent(cryp->dev, MTK_DESC_RING_SZ, + ring[i]->res_base, ring[i]->res_dma); + dma_free_coherent(cryp->dev, MTK_DESC_RING_SZ, + ring[i]->cmd_base, ring[i]->cmd_dma); + kfree(ring[i]); + } + return err; +} + +static int mtk_crypto_probe(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct mtk_cryp *cryp; + int i, err; + + cryp = devm_kzalloc(&pdev->dev, sizeof(*cryp), GFP_KERNEL); + if (!cryp) + return -ENOMEM; + + cryp->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(cryp->base)) + return PTR_ERR(cryp->base); + + for (i = 0; i < MTK_IRQ_NUM; i++) { + cryp->irq[i] = platform_get_irq(pdev, i); + if (cryp->irq[i] < 0) { + dev_err(cryp->dev, "no IRQ:%d resource info\n", i); + return -ENXIO; + } + } + + cryp->clk_ethif = devm_clk_get(&pdev->dev, "ethif"); + cryp->clk_cryp = devm_clk_get(&pdev->dev, "cryp"); + if (IS_ERR(cryp->clk_ethif) || IS_ERR(cryp->clk_cryp)) + return -EPROBE_DEFER; + + cryp->dev = &pdev->dev; + pm_runtime_enable(cryp->dev); + pm_runtime_get_sync(cryp->dev); + + err = clk_prepare_enable(cryp->clk_ethif); + if (err) + goto err_clk_ethif; + + err = clk_prepare_enable(cryp->clk_cryp); + if (err) + goto err_clk_cryp; + + /* Allocate four command/result descriptor rings */ + err = mtk_desc_ring_alloc(cryp); + if (err) { + dev_err(cryp->dev, "Unable to allocate descriptor rings.\n"); + goto err_resource; + } + + /* Initialize hardware modules */ + err = mtk_accelerator_init(cryp); + if (err) { + dev_err(cryp->dev, "Failed to initialize cryptographic engine.\n"); + goto err_engine; + } + + err = mtk_cipher_alg_register(cryp); + if (err) { + dev_err(cryp->dev, "Unable to register cipher algorithm.\n"); + goto err_cipher; + } + + err = mtk_hash_alg_register(cryp); + if (err) { + dev_err(cryp->dev, "Unable to register hash algorithm.\n"); + goto err_hash; + } + + platform_set_drvdata(pdev, cryp); + return 0; + +err_hash: + mtk_cipher_alg_release(cryp); +err_cipher: + mtk_dfe_dse_reset(cryp); +err_engine: + mtk_desc_dma_free(cryp); +err_resource: + clk_disable_unprepare(cryp->clk_cryp); +err_clk_cryp: + clk_disable_unprepare(cryp->clk_ethif); +err_clk_ethif: + pm_runtime_put_sync(cryp->dev); + pm_runtime_disable(cryp->dev); + + return err; +} + +static int mtk_crypto_remove(struct platform_device *pdev) +{ + struct mtk_cryp *cryp = platform_get_drvdata(pdev); + + mtk_hash_alg_release(cryp); + mtk_cipher_alg_release(cryp); + mtk_desc_dma_free(cryp); + + clk_disable_unprepare(cryp->clk_cryp); + clk_disable_unprepare(cryp->clk_ethif); + + pm_runtime_put_sync(cryp->dev); + pm_runtime_disable(cryp->dev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id of_crypto_id[] = { + { .compatible = "mediatek,eip97-crypto" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_crypto_id); + +static struct platform_driver mtk_crypto_driver = { + .probe = mtk_crypto_probe, + .remove = mtk_crypto_remove, + .driver = { + .name = "mtk-crypto", + .owner = THIS_MODULE, + .of_match_table = of_crypto_id, + }, +}; +module_platform_driver(mtk_crypto_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>"); +MODULE_DESCRIPTION("Cryptographic accelerator driver for EIP97"); diff --git a/drivers/crypto/mediatek/mtk-platform.h b/drivers/crypto/mediatek/mtk-platform.h new file mode 100644 index 000000000000..ed6d8717f7f4 --- /dev/null +++ b/drivers/crypto/mediatek/mtk-platform.h @@ -0,0 +1,231 @@ +/* + * Driver for EIP97 cryptographic accelerator. + * + * Copyright (c) 2016 Ryder Lee <ryder.lee@mediatek.com> + * + * 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 __MTK_PLATFORM_H_ +#define __MTK_PLATFORM_H_ + +#include <crypto/algapi.h> +#include <crypto/internal/aead.h> +#include <crypto/internal/hash.h> +#include <crypto/scatterwalk.h> +#include <crypto/skcipher.h> +#include <linux/crypto.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include "mtk-regs.h" + +#define MTK_RDR_PROC_THRESH BIT(0) +#define MTK_RDR_PROC_MODE BIT(23) +#define MTK_CNT_RST BIT(31) +#define MTK_IRQ_RDR0 BIT(1) +#define MTK_IRQ_RDR1 BIT(3) +#define MTK_IRQ_RDR2 BIT(5) +#define MTK_IRQ_RDR3 BIT(7) + +#define SIZE_IN_WORDS(x) ((x) >> 2) + +/** + * Ring 0/1 are used by AES encrypt and decrypt. + * Ring 2/3 are used by SHA. + */ +enum { + RING0 = 0, + RING1, + RING2, + RING3, + RING_MAX, +}; + +#define MTK_REC_NUM (RING_MAX / 2) +#define MTK_IRQ_NUM 5 + +/** + * struct mtk_desc - DMA descriptor + * @hdr: the descriptor control header + * @buf: DMA address of input buffer segment + * @ct: DMA address of command token that control operation flow + * @ct_hdr: the command token control header + * @tag: the user-defined field + * @tfm: DMA address of transform state + * @bound: align descriptors offset boundary + * + * Structure passed to the crypto engine to describe where source + * data needs to be fetched and how it needs to be processed. + */ +struct mtk_desc { + __le32 hdr; + __le32 buf; + __le32 ct; + __le32 ct_hdr; + __le32 tag; + __le32 tfm; + __le32 bound[2]; +}; + +#define MTK_DESC_NUM 512 +#define MTK_DESC_OFF SIZE_IN_WORDS(sizeof(struct mtk_desc)) +#define MTK_DESC_SZ (MTK_DESC_OFF - 2) +#define MTK_DESC_RING_SZ ((sizeof(struct mtk_desc) * MTK_DESC_NUM)) +#define MTK_DESC_CNT(x) ((MTK_DESC_OFF * (x)) << 2) +#define MTK_DESC_LAST cpu_to_le32(BIT(22)) +#define MTK_DESC_FIRST cpu_to_le32(BIT(23)) +#define MTK_DESC_BUF_LEN(x) cpu_to_le32(x) +#define MTK_DESC_CT_LEN(x) cpu_to_le32((x) << 24) + +/** + * struct mtk_ring - Descriptor ring + * @cmd_base: pointer to command descriptor ring base + * @cmd_dma: DMA address of command descriptor ring + * @cmd_pos: current position in the command descriptor ring + * @res_base: pointer to result descriptor ring base + * @res_dma: DMA address of result descriptor ring + * @res_pos: current position in the result descriptor ring + * + * A descriptor ring is a circular buffer that is used to manage + * one or more descriptors. There are two type of descriptor rings; + * the command descriptor ring and result descriptor ring. + */ +struct mtk_ring { + struct mtk_desc *cmd_base; + dma_addr_t cmd_dma; + u32 cmd_pos; + struct mtk_desc *res_base; + dma_addr_t res_dma; + u32 res_pos; +}; + +/** + * struct mtk_aes_dma - Structure that holds sg list info + * @sg: pointer to scatter-gather list + * @nents: number of entries in the sg list + * @remainder: remainder of sg list + * @sg_len: number of entries in the sg mapped list + */ +struct mtk_aes_dma { + struct scatterlist *sg; + int nents; + u32 remainder; + u32 sg_len; +}; + +struct mtk_aes_base_ctx; +struct mtk_aes_rec; +struct mtk_cryp; + +typedef int (*mtk_aes_fn)(struct mtk_cryp *cryp, struct mtk_aes_rec *aes); + +/** + * struct mtk_aes_rec - AES operation record + * @queue: crypto request queue + * @areq: pointer to async request + * @task: the tasklet is use in AES interrupt + * @ctx: pointer to current context + * @src: the structure that holds source sg list info + * @dst: the structure that holds destination sg list info + * @aligned_sg: the scatter list is use to alignment + * @real_dst: pointer to the destination sg list + * @resume: pointer to resume function + * @total: request buffer length + * @buf: pointer to page buffer + * @id: record identification + * @flags: it's describing AES operation state + * @lock: the async queue lock + * + * Structure used to record AES execution state. + */ +struct mtk_aes_rec { + struct crypto_queue queue; + struct crypto_async_request *areq; + struct tasklet_struct task; + struct mtk_aes_base_ctx *ctx; + struct mtk_aes_dma src; + struct mtk_aes_dma dst; + + struct scatterlist aligned_sg; + struct scatterlist *real_dst; + + mtk_aes_fn resume; + + size_t total; + void *buf; + + u8 id; + unsigned long flags; + /* queue lock */ + spinlock_t lock; +}; + +/** + * struct mtk_sha_rec - SHA operation record + * @queue: crypto request queue + * @req: pointer to ahash request + * @task: the tasklet is use in SHA interrupt + * @id: record identification + * @flags: it's describing SHA operation state + * @lock: the ablkcipher queue lock + * + * Structure used to record SHA execution state. + */ +struct mtk_sha_rec { + struct crypto_queue queue; + struct ahash_request *req; + struct tasklet_struct task; + + u8 id; + unsigned long flags; + /* queue lock */ + spinlock_t lock; +}; + +/** + * struct mtk_cryp - Cryptographic device + * @base: pointer to mapped register I/O base + * @dev: pointer to device + * @clk_ethif: pointer to ethif clock + * @clk_cryp: pointer to crypto clock + * @irq: global system and rings IRQ + * @ring: pointer to execution state of AES + * @aes: pointer to execution state of SHA + * @sha: each execution record map to a ring + * @aes_list: device list of AES + * @sha_list: device list of SHA + * @tmp: pointer to temporary buffer for internal use + * @tmp_dma: DMA address of temporary buffer + * @rec: it's used to select SHA record for tfm + * + * Structure storing cryptographic device information. + */ +struct mtk_cryp { + void __iomem *base; + struct device *dev; + struct clk *clk_ethif; + struct clk *clk_cryp; + int irq[MTK_IRQ_NUM]; + + struct mtk_ring *ring[RING_MAX]; + struct mtk_aes_rec *aes[MTK_REC_NUM]; + struct mtk_sha_rec *sha[MTK_REC_NUM]; + + struct list_head aes_list; + struct list_head sha_list; + + void *tmp; + dma_addr_t tmp_dma; + bool rec; +}; + +int mtk_cipher_alg_register(struct mtk_cryp *cryp); +void mtk_cipher_alg_release(struct mtk_cryp *cryp); +int mtk_hash_alg_register(struct mtk_cryp *cryp); +void mtk_hash_alg_release(struct mtk_cryp *cryp); + +#endif /* __MTK_PLATFORM_H_ */ diff --git a/drivers/crypto/mediatek/mtk-regs.h b/drivers/crypto/mediatek/mtk-regs.h new file mode 100644 index 000000000000..94f4eb85be3f --- /dev/null +++ b/drivers/crypto/mediatek/mtk-regs.h @@ -0,0 +1,194 @@ +/* + * Support for MediaTek cryptographic accelerator. + * + * Copyright (c) 2016 MediaTek Inc. + * Author: Ryder Lee <ryder.lee@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + */ + +#ifndef __MTK_REGS_H__ +#define __MTK_REGS_H__ + +/* HIA, Command Descriptor Ring Manager */ +#define CDR_BASE_ADDR_LO(x) (0x0 + ((x) << 12)) +#define CDR_BASE_ADDR_HI(x) (0x4 + ((x) << 12)) +#define CDR_DATA_BASE_ADDR_LO(x) (0x8 + ((x) << 12)) +#define CDR_DATA_BASE_ADDR_HI(x) (0xC + ((x) << 12)) +#define CDR_ACD_BASE_ADDR_LO(x) (0x10 + ((x) << 12)) +#define CDR_ACD_BASE_ADDR_HI(x) (0x14 + ((x) << 12)) +#define CDR_RING_SIZE(x) (0x18 + ((x) << 12)) +#define CDR_DESC_SIZE(x) (0x1C + ((x) << 12)) +#define CDR_CFG(x) (0x20 + ((x) << 12)) +#define CDR_DMA_CFG(x) (0x24 + ((x) << 12)) +#define CDR_THRESH(x) (0x28 + ((x) << 12)) +#define CDR_PREP_COUNT(x) (0x2C + ((x) << 12)) +#define CDR_PROC_COUNT(x) (0x30 + ((x) << 12)) +#define CDR_PREP_PNTR(x) (0x34 + ((x) << 12)) +#define CDR_PROC_PNTR(x) (0x38 + ((x) << 12)) +#define CDR_STAT(x) (0x3C + ((x) << 12)) + +/* HIA, Result Descriptor Ring Manager */ +#define RDR_BASE_ADDR_LO(x) (0x800 + ((x) << 12)) +#define RDR_BASE_ADDR_HI(x) (0x804 + ((x) << 12)) +#define RDR_DATA_BASE_ADDR_LO(x) (0x808 + ((x) << 12)) +#define RDR_DATA_BASE_ADDR_HI(x) (0x80C + ((x) << 12)) +#define RDR_ACD_BASE_ADDR_LO(x) (0x810 + ((x) << 12)) +#define RDR_ACD_BASE_ADDR_HI(x) (0x814 + ((x) << 12)) +#define RDR_RING_SIZE(x) (0x818 + ((x) << 12)) +#define RDR_DESC_SIZE(x) (0x81C + ((x) << 12)) +#define RDR_CFG(x) (0x820 + ((x) << 12)) +#define RDR_DMA_CFG(x) (0x824 + ((x) << 12)) +#define RDR_THRESH(x) (0x828 + ((x) << 12)) +#define RDR_PREP_COUNT(x) (0x82C + ((x) << 12)) +#define RDR_PROC_COUNT(x) (0x830 + ((x) << 12)) +#define RDR_PREP_PNTR(x) (0x834 + ((x) << 12)) +#define RDR_PROC_PNTR(x) (0x838 + ((x) << 12)) +#define RDR_STAT(x) (0x83C + ((x) << 12)) + +/* HIA, Ring AIC */ +#define AIC_POL_CTRL(x) (0xE000 - ((x) << 12)) +#define AIC_TYPE_CTRL(x) (0xE004 - ((x) << 12)) +#define AIC_ENABLE_CTRL(x) (0xE008 - ((x) << 12)) +#define AIC_RAW_STAL(x) (0xE00C - ((x) << 12)) +#define AIC_ENABLE_SET(x) (0xE00C - ((x) << 12)) +#define AIC_ENABLED_STAT(x) (0xE010 - ((x) << 12)) +#define AIC_ACK(x) (0xE010 - ((x) << 12)) +#define AIC_ENABLE_CLR(x) (0xE014 - ((x) << 12)) +#define AIC_OPTIONS(x) (0xE018 - ((x) << 12)) +#define AIC_VERSION(x) (0xE01C - ((x) << 12)) + +/* HIA, Global AIC */ +#define AIC_G_POL_CTRL 0xF800 +#define AIC_G_TYPE_CTRL 0xF804 +#define AIC_G_ENABLE_CTRL 0xF808 +#define AIC_G_RAW_STAT 0xF80C +#define AIC_G_ENABLE_SET 0xF80C +#define AIC_G_ENABLED_STAT 0xF810 +#define AIC_G_ACK 0xF810 +#define AIC_G_ENABLE_CLR 0xF814 +#define AIC_G_OPTIONS 0xF818 +#define AIC_G_VERSION 0xF81C + +/* HIA, Data Fetch Engine */ +#define DFE_CFG 0xF000 +#define DFE_PRIO_0 0xF010 +#define DFE_PRIO_1 0xF014 +#define DFE_PRIO_2 0xF018 +#define DFE_PRIO_3 0xF01C + +/* HIA, Data Fetch Engine access monitoring for CDR */ +#define DFE_RING_REGION_LO(x) (0xF080 + ((x) << 3)) +#define DFE_RING_REGION_HI(x) (0xF084 + ((x) << 3)) + +/* HIA, Data Fetch Engine thread control and status for thread */ +#define DFE_THR_CTRL 0xF200 +#define DFE_THR_STAT 0xF204 +#define DFE_THR_DESC_CTRL 0xF208 +#define DFE_THR_DESC_DPTR_LO 0xF210 +#define DFE_THR_DESC_DPTR_HI 0xF214 +#define DFE_THR_DESC_ACDPTR_LO 0xF218 +#define DFE_THR_DESC_ACDPTR_HI 0xF21C + +/* HIA, Data Store Engine */ +#define DSE_CFG 0xF400 +#define DSE_PRIO_0 0xF410 +#define DSE_PRIO_1 0xF414 +#define DSE_PRIO_2 0xF418 +#define DSE_PRIO_3 0xF41C + +/* HIA, Data Store Engine access monitoring for RDR */ +#define DSE_RING_REGION_LO(x) (0xF480 + ((x) << 3)) +#define DSE_RING_REGION_HI(x) (0xF484 + ((x) << 3)) + +/* HIA, Data Store Engine thread control and status for thread */ +#define DSE_THR_CTRL 0xF600 +#define DSE_THR_STAT 0xF604 +#define DSE_THR_DESC_CTRL 0xF608 +#define DSE_THR_DESC_DPTR_LO 0xF610 +#define DSE_THR_DESC_DPTR_HI 0xF614 +#define DSE_THR_DESC_S_DPTR_LO 0xF618 +#define DSE_THR_DESC_S_DPTR_HI 0xF61C +#define DSE_THR_ERROR_STAT 0xF620 + +/* HIA Global */ +#define HIA_MST_CTRL 0xFFF4 +#define HIA_OPTIONS 0xFFF8 +#define HIA_VERSION 0xFFFC + +/* Processing Engine Input Side, Processing Engine */ +#define PE_IN_DBUF_THRESH 0x10000 +#define PE_IN_TBUF_THRESH 0x10100 + +/* Packet Engine Configuration / Status Registers */ +#define PE_TOKEN_CTRL_STAT 0x11000 +#define PE_FUNCTION_EN 0x11004 +#define PE_CONTEXT_CTRL 0x11008 +#define PE_INTERRUPT_CTRL_STAT 0x11010 +#define PE_CONTEXT_STAT 0x1100C +#define PE_OUT_TRANS_CTRL_STAT 0x11018 +#define PE_OUT_BUF_CTRL 0x1101C + +/* Packet Engine PRNG Registers */ +#define PE_PRNG_STAT 0x11040 +#define PE_PRNG_CTRL 0x11044 +#define PE_PRNG_SEED_L 0x11048 +#define PE_PRNG_SEED_H 0x1104C +#define PE_PRNG_KEY_0_L 0x11050 +#define PE_PRNG_KEY_0_H 0x11054 +#define PE_PRNG_KEY_1_L 0x11058 +#define PE_PRNG_KEY_1_H 0x1105C +#define PE_PRNG_RES_0 0x11060 +#define PE_PRNG_RES_1 0x11064 +#define PE_PRNG_RES_2 0x11068 +#define PE_PRNG_RES_3 0x1106C +#define PE_PRNG_LFSR_L 0x11070 +#define PE_PRNG_LFSR_H 0x11074 + +/* Packet Engine AIC */ +#define PE_EIP96_AIC_POL_CTRL 0x113C0 +#define PE_EIP96_AIC_TYPE_CTRL 0x113C4 +#define PE_EIP96_AIC_ENABLE_CTRL 0x113C8 +#define PE_EIP96_AIC_RAW_STAT 0x113CC +#define PE_EIP96_AIC_ENABLE_SET 0x113CC +#define PE_EIP96_AIC_ENABLED_STAT 0x113D0 +#define PE_EIP96_AIC_ACK 0x113D0 +#define PE_EIP96_AIC_ENABLE_CLR 0x113D4 +#define PE_EIP96_AIC_OPTIONS 0x113D8 +#define PE_EIP96_AIC_VERSION 0x113DC + +/* Packet Engine Options & Version Registers */ +#define PE_EIP96_OPTIONS 0x113F8 +#define PE_EIP96_VERSION 0x113FC + +/* Processing Engine Output Side */ +#define PE_OUT_DBUF_THRESH 0x11C00 +#define PE_OUT_TBUF_THRESH 0x11D00 + +/* Processing Engine Local AIC */ +#define PE_AIC_POL_CTRL 0x11F00 +#define PE_AIC_TYPE_CTRL 0x11F04 +#define PE_AIC_ENABLE_CTRL 0x11F08 +#define PE_AIC_RAW_STAT 0x11F0C +#define PE_AIC_ENABLE_SET 0x11F0C +#define PE_AIC_ENABLED_STAT 0x11F10 +#define PE_AIC_ENABLE_CLR 0x11F14 +#define PE_AIC_OPTIONS 0x11F18 +#define PE_AIC_VERSION 0x11F1C + +/* Processing Engine General Configuration and Version */ +#define PE_IN_FLIGHT 0x11FF0 +#define PE_OPTIONS 0x11FF8 +#define PE_VERSION 0x11FFC + +/* EIP-97 - Global */ +#define EIP97_CLOCK_STATE 0x1FFE4 +#define EIP97_FORCE_CLOCK_ON 0x1FFE8 +#define EIP97_FORCE_CLOCK_OFF 0x1FFEC +#define EIP97_MST_CTRL 0x1FFF4 +#define EIP97_OPTIONS 0x1FFF8 +#define EIP97_VERSION 0x1FFFC +#endif /* __MTK_REGS_H__ */ diff --git a/drivers/crypto/mediatek/mtk-sha.c b/drivers/crypto/mediatek/mtk-sha.c new file mode 100644 index 000000000000..55e3805fba07 --- /dev/null +++ b/drivers/crypto/mediatek/mtk-sha.c @@ -0,0 +1,1435 @@ +/* + * Cryptographic API. + * + * Driver for EIP97 SHA1/SHA2(HMAC) acceleration. + * + * Copyright (c) 2016 Ryder Lee <ryder.lee@mediatek.com> + * + * 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. + * + * Some ideas are from atmel-sha.c and omap-sham.c drivers. + */ + +#include <crypto/sha.h> +#include "mtk-platform.h" + +#define SHA_ALIGN_MSK (sizeof(u32) - 1) +#define SHA_QUEUE_SIZE 512 +#define SHA_TMP_BUF_SIZE 512 +#define SHA_BUF_SIZE ((u32)PAGE_SIZE) + +#define SHA_OP_UPDATE 1 +#define SHA_OP_FINAL 2 + +#define SHA_DATA_LEN_MSK cpu_to_le32(GENMASK(16, 0)) + +/* SHA command token */ +#define SHA_CT_SIZE 5 +#define SHA_CT_CTRL_HDR cpu_to_le32(0x02220000) +#define SHA_CMD0 cpu_to_le32(0x03020000) +#define SHA_CMD1 cpu_to_le32(0x21060000) +#define SHA_CMD2 cpu_to_le32(0xe0e63802) + +/* SHA transform information */ +#define SHA_TFM_HASH cpu_to_le32(0x2 << 0) +#define SHA_TFM_INNER_DIG cpu_to_le32(0x1 << 21) +#define SHA_TFM_SIZE(x) cpu_to_le32((x) << 8) +#define SHA_TFM_START cpu_to_le32(0x1 << 4) +#define SHA_TFM_CONTINUE cpu_to_le32(0x1 << 5) +#define SHA_TFM_HASH_STORE cpu_to_le32(0x1 << 19) +#define SHA_TFM_SHA1 cpu_to_le32(0x2 << 23) +#define SHA_TFM_SHA256 cpu_to_le32(0x3 << 23) +#define SHA_TFM_SHA224 cpu_to_le32(0x4 << 23) +#define SHA_TFM_SHA512 cpu_to_le32(0x5 << 23) +#define SHA_TFM_SHA384 cpu_to_le32(0x6 << 23) +#define SHA_TFM_DIGEST(x) cpu_to_le32(((x) & GENMASK(3, 0)) << 24) + +/* SHA flags */ +#define SHA_FLAGS_BUSY BIT(0) +#define SHA_FLAGS_FINAL BIT(1) +#define SHA_FLAGS_FINUP BIT(2) +#define SHA_FLAGS_SG BIT(3) +#define SHA_FLAGS_ALGO_MSK GENMASK(8, 4) +#define SHA_FLAGS_SHA1 BIT(4) +#define SHA_FLAGS_SHA224 BIT(5) +#define SHA_FLAGS_SHA256 BIT(6) +#define SHA_FLAGS_SHA384 BIT(7) +#define SHA_FLAGS_SHA512 BIT(8) +#define SHA_FLAGS_HMAC BIT(9) +#define SHA_FLAGS_PAD BIT(10) + +/** + * mtk_sha_ct is a set of hardware instructions(command token) + * that are used to control engine's processing flow of SHA, + * and it contains the first two words of transform state. + */ +struct mtk_sha_ct { + __le32 ctrl[2]; + __le32 cmd[3]; +}; + +/** + * mtk_sha_tfm is used to define SHA transform state + * and store result digest that produced by engine. + */ +struct mtk_sha_tfm { + __le32 ctrl[2]; + __le32 digest[SIZE_IN_WORDS(SHA512_DIGEST_SIZE)]; +}; + +/** + * mtk_sha_info consists of command token and transform state + * of SHA, its role is similar to mtk_aes_info. + */ +struct mtk_sha_info { + struct mtk_sha_ct ct; + struct mtk_sha_tfm tfm; +}; + +struct mtk_sha_reqctx { + struct mtk_sha_info info; + unsigned long flags; + unsigned long op; + + u64 digcnt; + bool start; + size_t bufcnt; + dma_addr_t dma_addr; + + __le32 ct_hdr; + u32 ct_size; + dma_addr_t ct_dma; + dma_addr_t tfm_dma; + + /* Walk state */ + struct scatterlist *sg; + u32 offset; /* Offset in current sg */ + u32 total; /* Total request */ + size_t ds; + size_t bs; + + u8 *buffer; +}; + +struct mtk_sha_hmac_ctx { + struct crypto_shash *shash; + u8 ipad[SHA512_BLOCK_SIZE] __aligned(sizeof(u32)); + u8 opad[SHA512_BLOCK_SIZE] __aligned(sizeof(u32)); +}; + +struct mtk_sha_ctx { + struct mtk_cryp *cryp; + unsigned long flags; + u8 id; + u8 buf[SHA_BUF_SIZE] __aligned(sizeof(u32)); + + struct mtk_sha_hmac_ctx base[0]; +}; + +struct mtk_sha_drv { + struct list_head dev_list; + /* Device list lock */ + spinlock_t lock; +}; + +static struct mtk_sha_drv mtk_sha = { + .dev_list = LIST_HEAD_INIT(mtk_sha.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(mtk_sha.lock), +}; + +static int mtk_sha_handle_queue(struct mtk_cryp *cryp, u8 id, + struct ahash_request *req); + +static inline u32 mtk_sha_read(struct mtk_cryp *cryp, u32 offset) +{ + return readl_relaxed(cryp->base + offset); +} + +static inline void mtk_sha_write(struct mtk_cryp *cryp, + u32 offset, u32 value) +{ + writel_relaxed(value, cryp->base + offset); +} + +static struct mtk_cryp *mtk_sha_find_dev(struct mtk_sha_ctx *tctx) +{ + struct mtk_cryp *cryp = NULL; + struct mtk_cryp *tmp; + + spin_lock_bh(&mtk_sha.lock); + if (!tctx->cryp) { + list_for_each_entry(tmp, &mtk_sha.dev_list, sha_list) { + cryp = tmp; + break; + } + tctx->cryp = cryp; + } else { + cryp = tctx->cryp; + } + + /* + * Assign record id to tfm in round-robin fashion, and this + * will help tfm to bind to corresponding descriptor rings. + */ + tctx->id = cryp->rec; + cryp->rec = !cryp->rec; + + spin_unlock_bh(&mtk_sha.lock); + + return cryp; +} + +static int mtk_sha_append_sg(struct mtk_sha_reqctx *ctx) +{ + size_t count; + + while ((ctx->bufcnt < SHA_BUF_SIZE) && ctx->total) { + count = min(ctx->sg->length - ctx->offset, ctx->total); + count = min(count, SHA_BUF_SIZE - ctx->bufcnt); + + if (count <= 0) { + /* + * Check if count <= 0 because the buffer is full or + * because the sg length is 0. In the latest case, + * check if there is another sg in the list, a 0 length + * sg doesn't necessarily mean the end of the sg list. + */ + if ((ctx->sg->length == 0) && !sg_is_last(ctx->sg)) { + ctx->sg = sg_next(ctx->sg); + continue; + } else { + break; + } + } + + scatterwalk_map_and_copy(ctx->buffer + ctx->bufcnt, ctx->sg, + ctx->offset, count, 0); + + ctx->bufcnt += count; + ctx->offset += count; + ctx->total -= count; + + if (ctx->offset == ctx->sg->length) { + ctx->sg = sg_next(ctx->sg); + if (ctx->sg) + ctx->offset = 0; + else + ctx->total = 0; + } + } + + return 0; +} + +/* + * The purpose of this padding is to ensure that the padded message is a + * multiple of 512 bits (SHA1/SHA224/SHA256) or 1024 bits (SHA384/SHA512). + * The bit "1" is appended at the end of the message followed by + * "padlen-1" zero bits. Then a 64 bits block (SHA1/SHA224/SHA256) or + * 128 bits block (SHA384/SHA512) equals to the message length in bits + * is appended. + * + * For SHA1/SHA224/SHA256, padlen is calculated as followed: + * - if message length < 56 bytes then padlen = 56 - message length + * - else padlen = 64 + 56 - message length + * + * For SHA384/SHA512, padlen is calculated as followed: + * - if message length < 112 bytes then padlen = 112 - message length + * - else padlen = 128 + 112 - message length + */ +static void mtk_sha_fill_padding(struct mtk_sha_reqctx *ctx, u32 len) +{ + u32 index, padlen; + u64 bits[2]; + u64 size = ctx->digcnt; + + size += ctx->bufcnt; + size += len; + + bits[1] = cpu_to_be64(size << 3); + bits[0] = cpu_to_be64(size >> 61); + + if (ctx->flags & (SHA_FLAGS_SHA384 | SHA_FLAGS_SHA512)) { + index = ctx->bufcnt & 0x7f; + padlen = (index < 112) ? (112 - index) : ((128 + 112) - index); + *(ctx->buffer + ctx->bufcnt) = 0x80; + memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen - 1); + memcpy(ctx->buffer + ctx->bufcnt + padlen, bits, 16); + ctx->bufcnt += padlen + 16; + ctx->flags |= SHA_FLAGS_PAD; + } else { + index = ctx->bufcnt & 0x3f; + padlen = (index < 56) ? (56 - index) : ((64 + 56) - index); + *(ctx->buffer + ctx->bufcnt) = 0x80; + memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen - 1); + memcpy(ctx->buffer + ctx->bufcnt + padlen, &bits[1], 8); + ctx->bufcnt += padlen + 8; + ctx->flags |= SHA_FLAGS_PAD; + } +} + +/* Initialize basic transform information of SHA */ +static void mtk_sha_info_init(struct mtk_sha_reqctx *ctx) +{ + struct mtk_sha_ct *ct = &ctx->info.ct; + struct mtk_sha_tfm *tfm = &ctx->info.tfm; + + ctx->ct_hdr = SHA_CT_CTRL_HDR; + ctx->ct_size = SHA_CT_SIZE; + + tfm->ctrl[0] = SHA_TFM_HASH | SHA_TFM_INNER_DIG | + SHA_TFM_SIZE(SIZE_IN_WORDS(ctx->ds)); + + switch (ctx->flags & SHA_FLAGS_ALGO_MSK) { + case SHA_FLAGS_SHA1: + tfm->ctrl[0] |= SHA_TFM_SHA1; + break; + case SHA_FLAGS_SHA224: + tfm->ctrl[0] |= SHA_TFM_SHA224; + break; + case SHA_FLAGS_SHA256: + tfm->ctrl[0] |= SHA_TFM_SHA256; + break; + case SHA_FLAGS_SHA384: + tfm->ctrl[0] |= SHA_TFM_SHA384; + break; + case SHA_FLAGS_SHA512: + tfm->ctrl[0] |= SHA_TFM_SHA512; + break; + + default: + /* Should not happen... */ + return; + } + + tfm->ctrl[1] = SHA_TFM_HASH_STORE; + ct->ctrl[0] = tfm->ctrl[0] | SHA_TFM_CONTINUE | SHA_TFM_START; + ct->ctrl[1] = tfm->ctrl[1]; + + ct->cmd[0] = SHA_CMD0; + ct->cmd[1] = SHA_CMD1; + ct->cmd[2] = SHA_CMD2 | SHA_TFM_DIGEST(SIZE_IN_WORDS(ctx->ds)); +} + +/* + * Update input data length field of transform information and + * map it to DMA region. + */ +static int mtk_sha_info_update(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha, + size_t len) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req); + struct mtk_sha_info *info = &ctx->info; + struct mtk_sha_ct *ct = &info->ct; + + if (ctx->start) + ctx->start = false; + else + ct->ctrl[0] &= ~SHA_TFM_START; + + ctx->ct_hdr &= ~SHA_DATA_LEN_MSK; + ctx->ct_hdr |= cpu_to_le32(len); + ct->cmd[0] &= ~SHA_DATA_LEN_MSK; + ct->cmd[0] |= cpu_to_le32(len); + + ctx->digcnt += len; + + ctx->ct_dma = dma_map_single(cryp->dev, info, sizeof(*info), + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(cryp->dev, ctx->ct_dma))) { + dev_err(cryp->dev, "dma %zu bytes error\n", sizeof(*info)); + return -EINVAL; + } + ctx->tfm_dma = ctx->ct_dma + sizeof(*ct); + + return 0; +} + +/* + * Because of hardware limitation, we must pre-calculate the inner + * and outer digest that need to be processed firstly by engine, then + * apply the result digest to the input message. These complex hashing + * procedures limits HMAC performance, so we use fallback SW encoding. + */ +static int mtk_sha_finish_hmac(struct ahash_request *req) +{ + struct mtk_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm); + struct mtk_sha_hmac_ctx *bctx = tctx->base; + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + + SHASH_DESC_ON_STACK(shash, bctx->shash); + + shash->tfm = bctx->shash; + shash->flags = 0; /* not CRYPTO_TFM_REQ_MAY_SLEEP */ + + return crypto_shash_init(shash) ?: + crypto_shash_update(shash, bctx->opad, ctx->bs) ?: + crypto_shash_finup(shash, req->result, ctx->ds, req->result); +} + +/* Initialize request context */ +static int mtk_sha_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct mtk_sha_ctx *tctx = crypto_ahash_ctx(tfm); + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + + ctx->flags = 0; + ctx->ds = crypto_ahash_digestsize(tfm); + + switch (ctx->ds) { + case SHA1_DIGEST_SIZE: + ctx->flags |= SHA_FLAGS_SHA1; + ctx->bs = SHA1_BLOCK_SIZE; + break; + case SHA224_DIGEST_SIZE: + ctx->flags |= SHA_FLAGS_SHA224; + ctx->bs = SHA224_BLOCK_SIZE; + break; + case SHA256_DIGEST_SIZE: + ctx->flags |= SHA_FLAGS_SHA256; + ctx->bs = SHA256_BLOCK_SIZE; + break; + case SHA384_DIGEST_SIZE: + ctx->flags |= SHA_FLAGS_SHA384; + ctx->bs = SHA384_BLOCK_SIZE; + break; + case SHA512_DIGEST_SIZE: + ctx->flags |= SHA_FLAGS_SHA512; + ctx->bs = SHA512_BLOCK_SIZE; + break; + default: + return -EINVAL; + } + + ctx->bufcnt = 0; + ctx->digcnt = 0; + ctx->buffer = tctx->buf; + ctx->start = true; + + if (tctx->flags & SHA_FLAGS_HMAC) { + struct mtk_sha_hmac_ctx *bctx = tctx->base; + + memcpy(ctx->buffer, bctx->ipad, ctx->bs); + ctx->bufcnt = ctx->bs; + ctx->flags |= SHA_FLAGS_HMAC; + } + + return 0; +} + +static int mtk_sha_xmit(struct mtk_cryp *cryp, struct mtk_sha_rec *sha, + dma_addr_t addr, size_t len) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req); + struct mtk_ring *ring = cryp->ring[sha->id]; + struct mtk_desc *cmd = ring->cmd_base + ring->cmd_pos; + struct mtk_desc *res = ring->res_base + ring->res_pos; + int err; + + err = mtk_sha_info_update(cryp, sha, len); + if (err) + return err; + + /* Fill in the command/result descriptors */ + res->hdr = MTK_DESC_FIRST | MTK_DESC_LAST | MTK_DESC_BUF_LEN(len); + res->buf = cpu_to_le32(cryp->tmp_dma); + + cmd->hdr = MTK_DESC_FIRST | MTK_DESC_LAST | MTK_DESC_BUF_LEN(len) | + MTK_DESC_CT_LEN(ctx->ct_size); + + cmd->buf = cpu_to_le32(addr); + cmd->ct = cpu_to_le32(ctx->ct_dma); + cmd->ct_hdr = ctx->ct_hdr; + cmd->tfm = cpu_to_le32(ctx->tfm_dma); + + if (++ring->cmd_pos == MTK_DESC_NUM) + ring->cmd_pos = 0; + + ring->res_pos = ring->cmd_pos; + /* + * Make sure that all changes to the DMA ring are done before we + * start engine. + */ + wmb(); + /* Start DMA transfer */ + mtk_sha_write(cryp, RDR_PREP_COUNT(sha->id), MTK_DESC_CNT(1)); + mtk_sha_write(cryp, CDR_PREP_COUNT(sha->id), MTK_DESC_CNT(1)); + + return -EINPROGRESS; +} + +static int mtk_sha_xmit2(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha, + struct mtk_sha_reqctx *ctx, + size_t len1, size_t len2) +{ + struct mtk_ring *ring = cryp->ring[sha->id]; + struct mtk_desc *cmd = ring->cmd_base + ring->cmd_pos; + struct mtk_desc *res = ring->res_base + ring->res_pos; + int err; + + err = mtk_sha_info_update(cryp, sha, len1 + len2); + if (err) + return err; + + /* Fill in the command/result descriptors */ + res->hdr = MTK_DESC_BUF_LEN(len1) | MTK_DESC_FIRST; + res->buf = cpu_to_le32(cryp->tmp_dma); + + cmd->hdr = MTK_DESC_BUF_LEN(len1) | MTK_DESC_FIRST | + MTK_DESC_CT_LEN(ctx->ct_size); + cmd->buf = cpu_to_le32(sg_dma_address(ctx->sg)); + cmd->ct = cpu_to_le32(ctx->ct_dma); + cmd->ct_hdr = ctx->ct_hdr; + cmd->tfm = cpu_to_le32(ctx->tfm_dma); + + if (++ring->cmd_pos == MTK_DESC_NUM) + ring->cmd_pos = 0; + + ring->res_pos = ring->cmd_pos; + + cmd = ring->cmd_base + ring->cmd_pos; + res = ring->res_base + ring->res_pos; + + res->hdr = MTK_DESC_BUF_LEN(len2) | MTK_DESC_LAST; + res->buf = cpu_to_le32(cryp->tmp_dma); + + cmd->hdr = MTK_DESC_BUF_LEN(len2) | MTK_DESC_LAST; + cmd->buf = cpu_to_le32(ctx->dma_addr); + + if (++ring->cmd_pos == MTK_DESC_NUM) + ring->cmd_pos = 0; + + ring->res_pos = ring->cmd_pos; + + /* + * Make sure that all changes to the DMA ring are done before we + * start engine. + */ + wmb(); + /* Start DMA transfer */ + mtk_sha_write(cryp, RDR_PREP_COUNT(sha->id), MTK_DESC_CNT(2)); + mtk_sha_write(cryp, CDR_PREP_COUNT(sha->id), MTK_DESC_CNT(2)); + + return -EINPROGRESS; +} + +static int mtk_sha_dma_map(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha, + struct mtk_sha_reqctx *ctx, + size_t count) +{ + ctx->dma_addr = dma_map_single(cryp->dev, ctx->buffer, + SHA_BUF_SIZE, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(cryp->dev, ctx->dma_addr))) { + dev_err(cryp->dev, "dma map error\n"); + return -EINVAL; + } + + ctx->flags &= ~SHA_FLAGS_SG; + + return mtk_sha_xmit(cryp, sha, ctx->dma_addr, count); +} + +static int mtk_sha_update_slow(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req); + size_t count; + u32 final; + + mtk_sha_append_sg(ctx); + + final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total; + + dev_dbg(cryp->dev, "slow: bufcnt: %zu\n", ctx->bufcnt); + + if (final) { + sha->flags |= SHA_FLAGS_FINAL; + mtk_sha_fill_padding(ctx, 0); + } + + if (final || (ctx->bufcnt == SHA_BUF_SIZE && ctx->total)) { + count = ctx->bufcnt; + ctx->bufcnt = 0; + + return mtk_sha_dma_map(cryp, sha, ctx, count); + } + return 0; +} + +static int mtk_sha_update_start(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req); + u32 len, final, tail; + struct scatterlist *sg; + + if (!ctx->total) + return 0; + + if (ctx->bufcnt || ctx->offset) + return mtk_sha_update_slow(cryp, sha); + + sg = ctx->sg; + + if (!IS_ALIGNED(sg->offset, sizeof(u32))) + return mtk_sha_update_slow(cryp, sha); + + if (!sg_is_last(sg) && !IS_ALIGNED(sg->length, ctx->bs)) + /* size is not ctx->bs aligned */ + return mtk_sha_update_slow(cryp, sha); + + len = min(ctx->total, sg->length); + + if (sg_is_last(sg)) { + if (!(ctx->flags & SHA_FLAGS_FINUP)) { + /* not last sg must be ctx->bs aligned */ + tail = len & (ctx->bs - 1); + len -= tail; + } + } + + ctx->total -= len; + ctx->offset = len; /* offset where to start slow */ + + final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total; + + /* Add padding */ + if (final) { + size_t count; + + tail = len & (ctx->bs - 1); + len -= tail; + ctx->total += tail; + ctx->offset = len; /* offset where to start slow */ + + sg = ctx->sg; + mtk_sha_append_sg(ctx); + mtk_sha_fill_padding(ctx, len); + + ctx->dma_addr = dma_map_single(cryp->dev, ctx->buffer, + SHA_BUF_SIZE, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(cryp->dev, ctx->dma_addr))) { + dev_err(cryp->dev, "dma map bytes error\n"); + return -EINVAL; + } + + sha->flags |= SHA_FLAGS_FINAL; + count = ctx->bufcnt; + ctx->bufcnt = 0; + + if (len == 0) { + ctx->flags &= ~SHA_FLAGS_SG; + return mtk_sha_xmit(cryp, sha, ctx->dma_addr, count); + + } else { + ctx->sg = sg; + if (!dma_map_sg(cryp->dev, ctx->sg, 1, DMA_TO_DEVICE)) { + dev_err(cryp->dev, "dma_map_sg error\n"); + return -EINVAL; + } + + ctx->flags |= SHA_FLAGS_SG; + return mtk_sha_xmit2(cryp, sha, ctx, len, count); + } + } + + if (!dma_map_sg(cryp->dev, ctx->sg, 1, DMA_TO_DEVICE)) { + dev_err(cryp->dev, "dma_map_sg error\n"); + return -EINVAL; + } + + ctx->flags |= SHA_FLAGS_SG; + + return mtk_sha_xmit(cryp, sha, sg_dma_address(ctx->sg), len); +} + +static int mtk_sha_final_req(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req); + size_t count; + + mtk_sha_fill_padding(ctx, 0); + + sha->flags |= SHA_FLAGS_FINAL; + count = ctx->bufcnt; + ctx->bufcnt = 0; + + return mtk_sha_dma_map(cryp, sha, ctx, count); +} + +/* Copy ready hash (+ finalize hmac) */ +static int mtk_sha_finish(struct ahash_request *req) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + u32 *digest = ctx->info.tfm.digest; + u32 *result = (u32 *)req->result; + int i; + + /* Get the hash from the digest buffer */ + for (i = 0; i < SIZE_IN_WORDS(ctx->ds); i++) + result[i] = le32_to_cpu(digest[i]); + + if (ctx->flags & SHA_FLAGS_HMAC) + return mtk_sha_finish_hmac(req); + + return 0; +} + +static void mtk_sha_finish_req(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha, + int err) +{ + if (likely(!err && (SHA_FLAGS_FINAL & sha->flags))) + err = mtk_sha_finish(sha->req); + + sha->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL); + + sha->req->base.complete(&sha->req->base, err); + + /* Handle new request */ + mtk_sha_handle_queue(cryp, sha->id - RING2, NULL); +} + +static int mtk_sha_handle_queue(struct mtk_cryp *cryp, u8 id, + struct ahash_request *req) +{ + struct mtk_sha_rec *sha = cryp->sha[id]; + struct crypto_async_request *async_req, *backlog; + struct mtk_sha_reqctx *ctx; + unsigned long flags; + int err = 0, ret = 0; + + spin_lock_irqsave(&sha->lock, flags); + if (req) + ret = ahash_enqueue_request(&sha->queue, req); + + if (SHA_FLAGS_BUSY & sha->flags) { + spin_unlock_irqrestore(&sha->lock, flags); + return ret; + } + + backlog = crypto_get_backlog(&sha->queue); + async_req = crypto_dequeue_request(&sha->queue); + if (async_req) + sha->flags |= SHA_FLAGS_BUSY; + spin_unlock_irqrestore(&sha->lock, flags); + + if (!async_req) + return ret; + + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + + req = ahash_request_cast(async_req); + ctx = ahash_request_ctx(req); + + sha->req = req; + + mtk_sha_info_init(ctx); + + if (ctx->op == SHA_OP_UPDATE) { + err = mtk_sha_update_start(cryp, sha); + if (err != -EINPROGRESS && (ctx->flags & SHA_FLAGS_FINUP)) + /* No final() after finup() */ + err = mtk_sha_final_req(cryp, sha); + } else if (ctx->op == SHA_OP_FINAL) { + err = mtk_sha_final_req(cryp, sha); + } + + if (unlikely(err != -EINPROGRESS)) + /* Task will not finish it, so do it here */ + mtk_sha_finish_req(cryp, sha, err); + + return ret; +} + +static int mtk_sha_enqueue(struct ahash_request *req, u32 op) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + struct mtk_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm); + + ctx->op = op; + + return mtk_sha_handle_queue(tctx->cryp, tctx->id, req); +} + +static void mtk_sha_unmap(struct mtk_cryp *cryp, struct mtk_sha_rec *sha) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req); + + dma_unmap_single(cryp->dev, ctx->ct_dma, sizeof(ctx->info), + DMA_BIDIRECTIONAL); + + if (ctx->flags & SHA_FLAGS_SG) { + dma_unmap_sg(cryp->dev, ctx->sg, 1, DMA_TO_DEVICE); + if (ctx->sg->length == ctx->offset) { + ctx->sg = sg_next(ctx->sg); + if (ctx->sg) + ctx->offset = 0; + } + if (ctx->flags & SHA_FLAGS_PAD) { + dma_unmap_single(cryp->dev, ctx->dma_addr, + SHA_BUF_SIZE, DMA_TO_DEVICE); + } + } else + dma_unmap_single(cryp->dev, ctx->dma_addr, + SHA_BUF_SIZE, DMA_TO_DEVICE); +} + +static void mtk_sha_complete(struct mtk_cryp *cryp, + struct mtk_sha_rec *sha) +{ + int err = 0; + + err = mtk_sha_update_start(cryp, sha); + if (err != -EINPROGRESS) + mtk_sha_finish_req(cryp, sha, err); +} + +static int mtk_sha_update(struct ahash_request *req) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + + ctx->total = req->nbytes; + ctx->sg = req->src; + ctx->offset = 0; + + if ((ctx->bufcnt + ctx->total < SHA_BUF_SIZE) && + !(ctx->flags & SHA_FLAGS_FINUP)) + return mtk_sha_append_sg(ctx); + + return mtk_sha_enqueue(req, SHA_OP_UPDATE); +} + +static int mtk_sha_final(struct ahash_request *req) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + + ctx->flags |= SHA_FLAGS_FINUP; + + if (ctx->flags & SHA_FLAGS_PAD) + return mtk_sha_finish(req); + + return mtk_sha_enqueue(req, SHA_OP_FINAL); +} + +static int mtk_sha_finup(struct ahash_request *req) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + int err1, err2; + + ctx->flags |= SHA_FLAGS_FINUP; + + err1 = mtk_sha_update(req); + if (err1 == -EINPROGRESS || err1 == -EBUSY) + return err1; + /* + * final() has to be always called to cleanup resources + * even if update() failed + */ + err2 = mtk_sha_final(req); + + return err1 ?: err2; +} + +static int mtk_sha_digest(struct ahash_request *req) +{ + return mtk_sha_init(req) ?: mtk_sha_finup(req); +} + +static int mtk_sha_setkey(struct crypto_ahash *tfm, const u8 *key, + u32 keylen) +{ + struct mtk_sha_ctx *tctx = crypto_ahash_ctx(tfm); + struct mtk_sha_hmac_ctx *bctx = tctx->base; + size_t bs = crypto_shash_blocksize(bctx->shash); + size_t ds = crypto_shash_digestsize(bctx->shash); + int err, i; + + SHASH_DESC_ON_STACK(shash, bctx->shash); + + shash->tfm = bctx->shash; + shash->flags = crypto_shash_get_flags(bctx->shash) & + CRYPTO_TFM_REQ_MAY_SLEEP; + + if (keylen > bs) { + err = crypto_shash_digest(shash, key, keylen, bctx->ipad); + if (err) + return err; + keylen = ds; + } else { + memcpy(bctx->ipad, key, keylen); + } + + memset(bctx->ipad + keylen, 0, bs - keylen); + memcpy(bctx->opad, bctx->ipad, bs); + + for (i = 0; i < bs; i++) { + bctx->ipad[i] ^= 0x36; + bctx->opad[i] ^= 0x5c; + } + + return 0; +} + +static int mtk_sha_export(struct ahash_request *req, void *out) +{ + const struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + + memcpy(out, ctx, sizeof(*ctx)); + return 0; +} + +static int mtk_sha_import(struct ahash_request *req, const void *in) +{ + struct mtk_sha_reqctx *ctx = ahash_request_ctx(req); + + memcpy(ctx, in, sizeof(*ctx)); + return 0; +} + +static int mtk_sha_cra_init_alg(struct crypto_tfm *tfm, + const char *alg_base) +{ + struct mtk_sha_ctx *tctx = crypto_tfm_ctx(tfm); + struct mtk_cryp *cryp = NULL; + + cryp = mtk_sha_find_dev(tctx); + if (!cryp) + return -ENODEV; + + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct mtk_sha_reqctx)); + + if (alg_base) { + struct mtk_sha_hmac_ctx *bctx = tctx->base; + + tctx->flags |= SHA_FLAGS_HMAC; + bctx->shash = crypto_alloc_shash(alg_base, 0, + CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(bctx->shash)) { + pr_err("base driver %s could not be loaded.\n", + alg_base); + + return PTR_ERR(bctx->shash); + } + } + return 0; +} + +static int mtk_sha_cra_init(struct crypto_tfm *tfm) +{ + return mtk_sha_cra_init_alg(tfm, NULL); +} + +static int mtk_sha_cra_sha1_init(struct crypto_tfm *tfm) +{ + return mtk_sha_cra_init_alg(tfm, "sha1"); +} + +static int mtk_sha_cra_sha224_init(struct crypto_tfm *tfm) +{ + return mtk_sha_cra_init_alg(tfm, "sha224"); +} + +static int mtk_sha_cra_sha256_init(struct crypto_tfm *tfm) +{ + return mtk_sha_cra_init_alg(tfm, "sha256"); +} + +static int mtk_sha_cra_sha384_init(struct crypto_tfm *tfm) +{ + return mtk_sha_cra_init_alg(tfm, "sha384"); +} + +static int mtk_sha_cra_sha512_init(struct crypto_tfm *tfm) +{ + return mtk_sha_cra_init_alg(tfm, "sha512"); +} + +static void mtk_sha_cra_exit(struct crypto_tfm *tfm) +{ + struct mtk_sha_ctx *tctx = crypto_tfm_ctx(tfm); + + if (tctx->flags & SHA_FLAGS_HMAC) { + struct mtk_sha_hmac_ctx *bctx = tctx->base; + + crypto_free_shash(bctx->shash); + } +} + +static struct ahash_alg algs_sha1_sha224_sha256[] = { +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .halg.digestsize = SHA1_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "sha1", + .cra_driver_name = "mtk-sha1", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "sha224", + .cra_driver_name = "mtk-sha224", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "sha256", + .cra_driver_name = "mtk-sha256", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .setkey = mtk_sha_setkey, + .halg.digestsize = SHA1_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "hmac(sha1)", + .cra_driver_name = "mtk-hmac-sha1", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx) + + sizeof(struct mtk_sha_hmac_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_sha1_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .setkey = mtk_sha_setkey, + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "hmac(sha224)", + .cra_driver_name = "mtk-hmac-sha224", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx) + + sizeof(struct mtk_sha_hmac_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_sha224_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .setkey = mtk_sha_setkey, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "hmac(sha256)", + .cra_driver_name = "mtk-hmac-sha256", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx) + + sizeof(struct mtk_sha_hmac_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_sha256_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +}; + +static struct ahash_alg algs_sha384_sha512[] = { +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .halg.digestsize = SHA384_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "sha384", + .cra_driver_name = "mtk-sha384", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .halg.digestsize = SHA512_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "sha512", + .cra_driver_name = "mtk-sha512", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .setkey = mtk_sha_setkey, + .halg.digestsize = SHA384_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "hmac(sha384)", + .cra_driver_name = "mtk-hmac-sha384", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx) + + sizeof(struct mtk_sha_hmac_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_sha384_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +{ + .init = mtk_sha_init, + .update = mtk_sha_update, + .final = mtk_sha_final, + .finup = mtk_sha_finup, + .digest = mtk_sha_digest, + .export = mtk_sha_export, + .import = mtk_sha_import, + .setkey = mtk_sha_setkey, + .halg.digestsize = SHA512_DIGEST_SIZE, + .halg.statesize = sizeof(struct mtk_sha_reqctx), + .halg.base = { + .cra_name = "hmac(sha512)", + .cra_driver_name = "mtk-hmac-sha512", + .cra_priority = 400, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct mtk_sha_ctx) + + sizeof(struct mtk_sha_hmac_ctx), + .cra_alignmask = SHA_ALIGN_MSK, + .cra_module = THIS_MODULE, + .cra_init = mtk_sha_cra_sha512_init, + .cra_exit = mtk_sha_cra_exit, + } +}, +}; + +static void mtk_sha_task0(unsigned long data) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)data; + struct mtk_sha_rec *sha = cryp->sha[0]; + + mtk_sha_unmap(cryp, sha); + mtk_sha_complete(cryp, sha); +} + +static void mtk_sha_task1(unsigned long data) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)data; + struct mtk_sha_rec *sha = cryp->sha[1]; + + mtk_sha_unmap(cryp, sha); + mtk_sha_complete(cryp, sha); +} + +static irqreturn_t mtk_sha_ring2_irq(int irq, void *dev_id) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id; + struct mtk_sha_rec *sha = cryp->sha[0]; + u32 val = mtk_sha_read(cryp, RDR_STAT(RING2)); + + mtk_sha_write(cryp, RDR_STAT(RING2), val); + + if (likely((SHA_FLAGS_BUSY & sha->flags))) { + mtk_sha_write(cryp, RDR_PROC_COUNT(RING2), MTK_CNT_RST); + mtk_sha_write(cryp, RDR_THRESH(RING2), + MTK_RDR_PROC_THRESH | MTK_RDR_PROC_MODE); + + tasklet_schedule(&sha->task); + } else { + dev_warn(cryp->dev, "AES interrupt when no active requests.\n"); + } + return IRQ_HANDLED; +} + +static irqreturn_t mtk_sha_ring3_irq(int irq, void *dev_id) +{ + struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id; + struct mtk_sha_rec *sha = cryp->sha[1]; + u32 val = mtk_sha_read(cryp, RDR_STAT(RING3)); + + mtk_sha_write(cryp, RDR_STAT(RING3), val); + + if (likely((SHA_FLAGS_BUSY & sha->flags))) { + mtk_sha_write(cryp, RDR_PROC_COUNT(RING3), MTK_CNT_RST); + mtk_sha_write(cryp, RDR_THRESH(RING3), + MTK_RDR_PROC_THRESH | MTK_RDR_PROC_MODE); + + tasklet_schedule(&sha->task); + } else { + dev_warn(cryp->dev, "AES interrupt when no active requests.\n"); + } + return IRQ_HANDLED; +} + +/* + * The purpose of two SHA records is used to get extra performance. + * It is similar to mtk_aes_record_init(). + */ +static int mtk_sha_record_init(struct mtk_cryp *cryp) +{ + struct mtk_sha_rec **sha = cryp->sha; + int i, err = -ENOMEM; + + for (i = 0; i < MTK_REC_NUM; i++) { + sha[i] = kzalloc(sizeof(**sha), GFP_KERNEL); + if (!sha[i]) + goto err_cleanup; + + sha[i]->id = i + RING2; + + spin_lock_init(&sha[i]->lock); + crypto_init_queue(&sha[i]->queue, SHA_QUEUE_SIZE); + } + + tasklet_init(&sha[0]->task, mtk_sha_task0, (unsigned long)cryp); + tasklet_init(&sha[1]->task, mtk_sha_task1, (unsigned long)cryp); + + cryp->rec = 1; + + return 0; + +err_cleanup: + for (; i--; ) + kfree(sha[i]); + return err; +} + +static void mtk_sha_record_free(struct mtk_cryp *cryp) +{ + int i; + + for (i = 0; i < MTK_REC_NUM; i++) { + tasklet_kill(&cryp->sha[i]->task); + kfree(cryp->sha[i]); + } +} + +static void mtk_sha_unregister_algs(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(algs_sha1_sha224_sha256); i++) + crypto_unregister_ahash(&algs_sha1_sha224_sha256[i]); + + for (i = 0; i < ARRAY_SIZE(algs_sha384_sha512); i++) + crypto_unregister_ahash(&algs_sha384_sha512[i]); +} + +static int mtk_sha_register_algs(void) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(algs_sha1_sha224_sha256); i++) { + err = crypto_register_ahash(&algs_sha1_sha224_sha256[i]); + if (err) + goto err_sha_224_256_algs; + } + + for (i = 0; i < ARRAY_SIZE(algs_sha384_sha512); i++) { + err = crypto_register_ahash(&algs_sha384_sha512[i]); + if (err) + goto err_sha_384_512_algs; + } + + return 0; + +err_sha_384_512_algs: + for (; i--; ) + crypto_unregister_ahash(&algs_sha384_sha512[i]); + i = ARRAY_SIZE(algs_sha1_sha224_sha256); +err_sha_224_256_algs: + for (; i--; ) + crypto_unregister_ahash(&algs_sha1_sha224_sha256[i]); + + return err; +} + +int mtk_hash_alg_register(struct mtk_cryp *cryp) +{ + int err; + + INIT_LIST_HEAD(&cryp->sha_list); + + /* Initialize two hash records */ + err = mtk_sha_record_init(cryp); + if (err) + goto err_record; + + /* Ring2 is use by SHA record0 */ + err = devm_request_irq(cryp->dev, cryp->irq[RING2], + mtk_sha_ring2_irq, IRQF_TRIGGER_LOW, + "mtk-sha", cryp); + if (err) { + dev_err(cryp->dev, "unable to request sha irq0.\n"); + goto err_res; + } + + /* Ring3 is use by SHA record1 */ + err = devm_request_irq(cryp->dev, cryp->irq[RING3], + mtk_sha_ring3_irq, IRQF_TRIGGER_LOW, + "mtk-sha", cryp); + if (err) { + dev_err(cryp->dev, "unable to request sha irq1.\n"); + goto err_res; + } + + /* Enable ring2 and ring3 interrupt for hash */ + mtk_sha_write(cryp, AIC_ENABLE_SET(RING2), MTK_IRQ_RDR2); + mtk_sha_write(cryp, AIC_ENABLE_SET(RING3), MTK_IRQ_RDR3); + + cryp->tmp = dma_alloc_coherent(cryp->dev, SHA_TMP_BUF_SIZE, + &cryp->tmp_dma, GFP_KERNEL); + if (!cryp->tmp) { + dev_err(cryp->dev, "unable to allocate tmp buffer.\n"); + err = -EINVAL; + goto err_res; + } + + spin_lock(&mtk_sha.lock); + list_add_tail(&cryp->sha_list, &mtk_sha.dev_list); + spin_unlock(&mtk_sha.lock); + + err = mtk_sha_register_algs(); + if (err) + goto err_algs; + + return 0; + +err_algs: + spin_lock(&mtk_sha.lock); + list_del(&cryp->sha_list); + spin_unlock(&mtk_sha.lock); + dma_free_coherent(cryp->dev, SHA_TMP_BUF_SIZE, + cryp->tmp, cryp->tmp_dma); +err_res: + mtk_sha_record_free(cryp); +err_record: + + dev_err(cryp->dev, "mtk-sha initialization failed.\n"); + return err; +} + +void mtk_hash_alg_release(struct mtk_cryp *cryp) +{ + spin_lock(&mtk_sha.lock); + list_del(&cryp->sha_list); + spin_unlock(&mtk_sha.lock); + + mtk_sha_unregister_algs(); + dma_free_coherent(cryp->dev, SHA_TMP_BUF_SIZE, + cryp->tmp, cryp->tmp_dma); + mtk_sha_record_free(cryp); +} diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c index 47576098831f..b6f14844702e 100644 --- a/drivers/crypto/picoxcell_crypto.c +++ b/drivers/crypto/picoxcell_crypto.c @@ -1616,32 +1616,17 @@ static const struct of_device_id spacc_of_id_table[] = { MODULE_DEVICE_TABLE(of, spacc_of_id_table); #endif /* CONFIG_OF */ -static bool spacc_is_compatible(struct platform_device *pdev, - const char *spacc_type) -{ - const struct platform_device_id *platid = platform_get_device_id(pdev); - - if (platid && !strcmp(platid->name, spacc_type)) - return true; - -#ifdef CONFIG_OF - if (of_device_is_compatible(pdev->dev.of_node, spacc_type)) - return true; -#endif /* CONFIG_OF */ - - return false; -} - static int spacc_probe(struct platform_device *pdev) { int i, err, ret = -EINVAL; struct resource *mem, *irq; + struct device_node *np = pdev->dev.of_node; struct spacc_engine *engine = devm_kzalloc(&pdev->dev, sizeof(*engine), GFP_KERNEL); if (!engine) return -ENOMEM; - if (spacc_is_compatible(pdev, "picochip,spacc-ipsec")) { + if (of_device_is_compatible(np, "picochip,spacc-ipsec")) { engine->max_ctxs = SPACC_CRYPTO_IPSEC_MAX_CTXS; engine->cipher_pg_sz = SPACC_CRYPTO_IPSEC_CIPHER_PG_SZ; engine->hash_pg_sz = SPACC_CRYPTO_IPSEC_HASH_PG_SZ; @@ -1650,7 +1635,7 @@ static int spacc_probe(struct platform_device *pdev) engine->num_algs = ARRAY_SIZE(ipsec_engine_algs); engine->aeads = ipsec_engine_aeads; engine->num_aeads = ARRAY_SIZE(ipsec_engine_aeads); - } else if (spacc_is_compatible(pdev, "picochip,spacc-l2")) { + } else if (of_device_is_compatible(np, "picochip,spacc-l2")) { engine->max_ctxs = SPACC_CRYPTO_L2_MAX_CTXS; engine->cipher_pg_sz = SPACC_CRYPTO_L2_CIPHER_PG_SZ; engine->hash_pg_sz = SPACC_CRYPTO_L2_HASH_PG_SZ; @@ -1803,12 +1788,6 @@ static int spacc_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id spacc_id_table[] = { - { "picochip,spacc-ipsec", }, - { "picochip,spacc-l2", }, - { } -}; - static struct platform_driver spacc_driver = { .probe = spacc_probe, .remove = spacc_remove, @@ -1819,7 +1798,6 @@ static struct platform_driver spacc_driver = { #endif /* CONFIG_PM */ .of_match_table = of_match_ptr(spacc_of_id_table), }, - .id_table = spacc_id_table, }; module_platform_driver(spacc_driver); diff --git a/drivers/crypto/qat/qat_c3xxx/adf_drv.c b/drivers/crypto/qat/qat_c3xxx/adf_drv.c index 640c3fc870fd..f172171668ee 100644 --- a/drivers/crypto/qat/qat_c3xxx/adf_drv.c +++ b/drivers/crypto/qat/qat_c3xxx/adf_drv.c @@ -186,7 +186,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Create dev top level debugfs entry */ - snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d", + snprintf(name, sizeof(name), "%s%s_%02x:%02d.%d", ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); diff --git a/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c b/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c index 949d77b79fbe..24ec908eb26c 100644 --- a/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c +++ b/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c @@ -170,7 +170,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) accel_pci_dev->sku = hw_data->get_sku(hw_data); /* Create dev top level debugfs entry */ - snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d", + snprintf(name, sizeof(name), "%s%s_%02x:%02d.%d", ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); diff --git a/drivers/crypto/qat/qat_c62x/adf_drv.c b/drivers/crypto/qat/qat_c62x/adf_drv.c index 5b2d78a5b5aa..58a984c9c3ec 100644 --- a/drivers/crypto/qat/qat_c62x/adf_drv.c +++ b/drivers/crypto/qat/qat_c62x/adf_drv.c @@ -186,7 +186,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Create dev top level debugfs entry */ - snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d", + snprintf(name, sizeof(name), "%s%s_%02x:%02d.%d", ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); diff --git a/drivers/crypto/qat/qat_c62xvf/adf_drv.c b/drivers/crypto/qat/qat_c62xvf/adf_drv.c index 7540ce13b0d0..b9f3e0e4fde9 100644 --- a/drivers/crypto/qat/qat_c62xvf/adf_drv.c +++ b/drivers/crypto/qat/qat_c62xvf/adf_drv.c @@ -170,7 +170,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) accel_pci_dev->sku = hw_data->get_sku(hw_data); /* Create dev top level debugfs entry */ - snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d", + snprintf(name, sizeof(name), "%s%s_%02x:%02d.%d", ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); diff --git a/drivers/crypto/qat/qat_common/adf_cfg_common.h b/drivers/crypto/qat/qat_common/adf_cfg_common.h index 8c4f6573ce59..1211261de7c2 100644 --- a/drivers/crypto/qat/qat_common/adf_cfg_common.h +++ b/drivers/crypto/qat/qat_common/adf_cfg_common.h @@ -61,6 +61,7 @@ #define ADF_CFG_AFFINITY_WHATEVER 0xFF #define MAX_DEVICE_NAME_SIZE 32 #define ADF_MAX_DEVICES (32 * 32) +#define ADF_DEVS_ARRAY_SIZE BITS_TO_LONGS(ADF_MAX_DEVICES) enum adf_cfg_val_type { ADF_DEC, diff --git a/drivers/crypto/qat/qat_common/adf_common_drv.h b/drivers/crypto/qat/qat_common/adf_common_drv.h index 980e07475012..5c4c0a253129 100644 --- a/drivers/crypto/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/qat/qat_common/adf_common_drv.h @@ -87,8 +87,8 @@ enum adf_event { struct service_hndl { int (*event_hld)(struct adf_accel_dev *accel_dev, enum adf_event event); - unsigned long init_status; - unsigned long start_status; + unsigned long init_status[ADF_DEVS_ARRAY_SIZE]; + unsigned long start_status[ADF_DEVS_ARRAY_SIZE]; char *name; struct list_head list; }; diff --git a/drivers/crypto/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/qat/qat_common/adf_dev_mgr.c index b3ebb25f9ca7..8afac52677a6 100644 --- a/drivers/crypto/qat/qat_common/adf_dev_mgr.c +++ b/drivers/crypto/qat/qat_common/adf_dev_mgr.c @@ -152,7 +152,7 @@ void adf_devmgr_update_class_index(struct adf_hw_device_data *hw_data) ptr->hw_device->instance_id = i++; if (i == class->instances) - break; + break; } } EXPORT_SYMBOL_GPL(adf_devmgr_update_class_index); diff --git a/drivers/crypto/qat/qat_common/adf_init.c b/drivers/crypto/qat/qat_common/adf_init.c index 888c6675e7e5..26556c713049 100644 --- a/drivers/crypto/qat/qat_common/adf_init.c +++ b/drivers/crypto/qat/qat_common/adf_init.c @@ -64,8 +64,8 @@ static void adf_service_add(struct service_hndl *service) int adf_service_register(struct service_hndl *service) { - service->init_status = 0; - service->start_status = 0; + memset(service->init_status, 0, sizeof(service->init_status)); + memset(service->start_status, 0, sizeof(service->start_status)); adf_service_add(service); return 0; } @@ -79,9 +79,13 @@ static void adf_service_remove(struct service_hndl *service) int adf_service_unregister(struct service_hndl *service) { - if (service->init_status || service->start_status) { - pr_err("QAT: Could not remove active service\n"); - return -EFAULT; + int i; + + for (i = 0; i < ARRAY_SIZE(service->init_status); i++) { + if (service->init_status[i] || service->start_status[i]) { + pr_err("QAT: Could not remove active service\n"); + return -EFAULT; + } } adf_service_remove(service); return 0; @@ -163,7 +167,7 @@ int adf_dev_init(struct adf_accel_dev *accel_dev) service->name); return -EFAULT; } - set_bit(accel_dev->accel_id, &service->init_status); + set_bit(accel_dev->accel_id, service->init_status); } hw_data->enable_error_correction(accel_dev); @@ -210,7 +214,7 @@ int adf_dev_start(struct adf_accel_dev *accel_dev) service->name); return -EFAULT; } - set_bit(accel_dev->accel_id, &service->start_status); + set_bit(accel_dev->accel_id, service->start_status); } clear_bit(ADF_STATUS_STARTING, &accel_dev->status); @@ -259,14 +263,14 @@ void adf_dev_stop(struct adf_accel_dev *accel_dev) list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); - if (!test_bit(accel_dev->accel_id, &service->start_status)) + if (!test_bit(accel_dev->accel_id, service->start_status)) continue; ret = service->event_hld(accel_dev, ADF_EVENT_STOP); if (!ret) { - clear_bit(accel_dev->accel_id, &service->start_status); + clear_bit(accel_dev->accel_id, service->start_status); } else if (ret == -EAGAIN) { wait = true; - clear_bit(accel_dev->accel_id, &service->start_status); + clear_bit(accel_dev->accel_id, service->start_status); } } @@ -317,14 +321,14 @@ void adf_dev_shutdown(struct adf_accel_dev *accel_dev) list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); - if (!test_bit(accel_dev->accel_id, &service->init_status)) + if (!test_bit(accel_dev->accel_id, service->init_status)) continue; if (service->event_hld(accel_dev, ADF_EVENT_SHUTDOWN)) dev_err(&GET_DEV(accel_dev), "Failed to shutdown service %s\n", service->name); else - clear_bit(accel_dev->accel_id, &service->init_status); + clear_bit(accel_dev->accel_id, service->init_status); } hw_data->disable_iov(accel_dev); diff --git a/drivers/crypto/qat/qat_common/adf_sriov.c b/drivers/crypto/qat/qat_common/adf_sriov.c index 9320ae1d005b..b36d8653b1ba 100644 --- a/drivers/crypto/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/qat/qat_common/adf_sriov.c @@ -162,9 +162,9 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) /** * adf_disable_sriov() - Disable SRIOV for the device - * @pdev: Pointer to pci device. + * @accel_dev: Pointer to accel device. * - * Function disables SRIOV for the pci device. + * Function disables SRIOV for the accel device. * * Return: 0 on success, error code otherwise. */ diff --git a/drivers/crypto/qat/qat_common/adf_vf_isr.c b/drivers/crypto/qat/qat_common/adf_vf_isr.c index bf99e11a3403..4a73fc70f7a9 100644 --- a/drivers/crypto/qat/qat_common/adf_vf_isr.c +++ b/drivers/crypto/qat/qat_common/adf_vf_isr.c @@ -148,7 +148,7 @@ static void adf_pf2vf_bh_handler(void *data) INIT_WORK(&stop_data->work, adf_dev_stop_async); queue_work(adf_vf_stop_wq, &stop_data->work); /* To ack, clear the PF2VFINT bit */ - msg &= ~BIT(0); + msg &= ~ADF_PF2VF_INT; ADF_CSR_WR(pmisc_bar_addr, hw_data->get_pf2vf_offset(0), msg); return; } @@ -168,7 +168,7 @@ static void adf_pf2vf_bh_handler(void *data) } /* To ack, clear the PF2VFINT bit */ - msg &= ~BIT(0); + msg &= ~ADF_PF2VF_INT; ADF_CSR_WR(pmisc_bar_addr, hw_data->get_pf2vf_offset(0), msg); /* Re-enable PF2VF interrupts */ diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c index 4d2de2838451..2ce01f010c74 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c +++ b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c @@ -186,7 +186,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Create dev top level debugfs entry */ - snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d", + snprintf(name, sizeof(name), "%s%s_%02x:%02d.%d", ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c b/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c index 60df98632fa2..26ab17bfc6da 100644 --- a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c +++ b/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c @@ -170,7 +170,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) accel_pci_dev->sku = hw_data->get_sku(hw_data); /* Create dev top level debugfs entry */ - snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d", + snprintf(name, sizeof(name), "%s%s_%02x:%02d.%d", ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); diff --git a/drivers/crypto/virtio/Kconfig b/drivers/crypto/virtio/Kconfig index d80f73366ae2..5db07495ddc5 100644 --- a/drivers/crypto/virtio/Kconfig +++ b/drivers/crypto/virtio/Kconfig @@ -4,6 +4,7 @@ config CRYPTO_DEV_VIRTIO select CRYPTO_AEAD select CRYPTO_AUTHENC select CRYPTO_BLKCIPHER + select CRYPTO_ENGINE default m help This driver provides support for virtio crypto device. If you diff --git a/drivers/crypto/virtio/virtio_crypto_algs.c b/drivers/crypto/virtio/virtio_crypto_algs.c index c2374df9abae..49defda4e03d 100644 --- a/drivers/crypto/virtio/virtio_crypto_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_algs.c @@ -288,8 +288,7 @@ static int virtio_crypto_ablkcipher_setkey(struct crypto_ablkcipher *tfm, static int __virtio_crypto_ablkcipher_do_req(struct virtio_crypto_request *vc_req, struct ablkcipher_request *req, - struct data_queue *data_vq, - __u8 op) + struct data_queue *data_vq) { struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); unsigned int ivsize = crypto_ablkcipher_ivsize(tfm); @@ -329,7 +328,7 @@ __virtio_crypto_ablkcipher_do_req(struct virtio_crypto_request *vc_req, vc_req->req_data = req_data; vc_req->type = VIRTIO_CRYPTO_SYM_OP_CIPHER; /* Head of operation */ - if (op) { + if (vc_req->encrypt) { req_data->header.session_id = cpu_to_le64(ctx->enc_sess_info.session_id); req_data->header.opcode = @@ -424,19 +423,15 @@ static int virtio_crypto_ablkcipher_encrypt(struct ablkcipher_request *req) struct virtio_crypto_ablkcipher_ctx *ctx = crypto_ablkcipher_ctx(atfm); struct virtio_crypto_request *vc_req = ablkcipher_request_ctx(req); struct virtio_crypto *vcrypto = ctx->vcrypto; - int ret; /* Use the first data virtqueue as default */ struct data_queue *data_vq = &vcrypto->data_vq[0]; vc_req->ablkcipher_ctx = ctx; vc_req->ablkcipher_req = req; - ret = __virtio_crypto_ablkcipher_do_req(vc_req, req, data_vq, 1); - if (ret < 0) { - pr_err("virtio_crypto: Encryption failed!\n"); - return ret; - } + vc_req->encrypt = true; + vc_req->dataq = data_vq; - return -EINPROGRESS; + return crypto_transfer_cipher_request_to_engine(data_vq->engine, req); } static int virtio_crypto_ablkcipher_decrypt(struct ablkcipher_request *req) @@ -445,20 +440,16 @@ static int virtio_crypto_ablkcipher_decrypt(struct ablkcipher_request *req) struct virtio_crypto_ablkcipher_ctx *ctx = crypto_ablkcipher_ctx(atfm); struct virtio_crypto_request *vc_req = ablkcipher_request_ctx(req); struct virtio_crypto *vcrypto = ctx->vcrypto; - int ret; /* Use the first data virtqueue as default */ struct data_queue *data_vq = &vcrypto->data_vq[0]; vc_req->ablkcipher_ctx = ctx; vc_req->ablkcipher_req = req; - ret = __virtio_crypto_ablkcipher_do_req(vc_req, req, data_vq, 0); - if (ret < 0) { - pr_err("virtio_crypto: Decryption failed!\n"); - return ret; - } + vc_req->encrypt = false; + vc_req->dataq = data_vq; - return -EINPROGRESS; + return crypto_transfer_cipher_request_to_engine(data_vq->engine, req); } static int virtio_crypto_ablkcipher_init(struct crypto_tfm *tfm) @@ -484,10 +475,37 @@ static void virtio_crypto_ablkcipher_exit(struct crypto_tfm *tfm) ctx->vcrypto = NULL; } +int virtio_crypto_ablkcipher_crypt_req( + struct crypto_engine *engine, + struct ablkcipher_request *req) +{ + struct virtio_crypto_request *vc_req = ablkcipher_request_ctx(req); + struct data_queue *data_vq = vc_req->dataq; + int ret; + + ret = __virtio_crypto_ablkcipher_do_req(vc_req, req, data_vq); + if (ret < 0) + return ret; + + virtqueue_kick(data_vq->vq); + + return 0; +} + +void virtio_crypto_ablkcipher_finalize_req( + struct virtio_crypto_request *vc_req, + struct ablkcipher_request *req, + int err) +{ + crypto_finalize_cipher_request(vc_req->dataq->engine, req, err); + + virtcrypto_clear_request(vc_req); +} + static struct crypto_alg virtio_crypto_algs[] = { { .cra_name = "cbc(aes)", .cra_driver_name = "virtio_crypto_aes_cbc", - .cra_priority = 501, + .cra_priority = 150, .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct virtio_crypto_ablkcipher_ctx), diff --git a/drivers/crypto/virtio/virtio_crypto_common.h b/drivers/crypto/virtio/virtio_crypto_common.h index 3d6566b02876..da6d8c0ea407 100644 --- a/drivers/crypto/virtio/virtio_crypto_common.h +++ b/drivers/crypto/virtio/virtio_crypto_common.h @@ -25,6 +25,7 @@ #include <crypto/aead.h> #include <crypto/aes.h> #include <crypto/authenc.h> +#include <crypto/engine.h> /* Internal representation of a data virtqueue */ @@ -37,6 +38,8 @@ struct data_queue { /* Name of the tx queue: dataq.$index */ char name[32]; + + struct crypto_engine *engine; }; struct virtio_crypto { @@ -97,6 +100,9 @@ struct virtio_crypto_request { struct virtio_crypto_op_data_req *req_data; struct scatterlist **sgs; uint8_t *iv; + /* Encryption? */ + bool encrypt; + struct data_queue *dataq; }; int virtcrypto_devmgr_add_dev(struct virtio_crypto *vcrypto_dev); @@ -110,6 +116,16 @@ int virtcrypto_dev_started(struct virtio_crypto *vcrypto_dev); struct virtio_crypto *virtcrypto_get_dev_node(int node); int virtcrypto_dev_start(struct virtio_crypto *vcrypto); void virtcrypto_dev_stop(struct virtio_crypto *vcrypto); +int virtio_crypto_ablkcipher_crypt_req( + struct crypto_engine *engine, + struct ablkcipher_request *req); +void virtio_crypto_ablkcipher_finalize_req( + struct virtio_crypto_request *vc_req, + struct ablkcipher_request *req, + int err); + +void +virtcrypto_clear_request(struct virtio_crypto_request *vc_req); static inline int virtio_crypto_get_current_node(void) { diff --git a/drivers/crypto/virtio/virtio_crypto_core.c b/drivers/crypto/virtio/virtio_crypto_core.c index fe70ec823b27..21472e427f6f 100644 --- a/drivers/crypto/virtio/virtio_crypto_core.c +++ b/drivers/crypto/virtio/virtio_crypto_core.c @@ -25,7 +25,7 @@ #include "virtio_crypto_common.h" -static void +void virtcrypto_clear_request(struct virtio_crypto_request *vc_req) { if (vc_req) { @@ -66,12 +66,12 @@ static void virtcrypto_dataq_callback(struct virtqueue *vq) break; } ablk_req = vc_req->ablkcipher_req; - virtcrypto_clear_request(vc_req); spin_unlock_irqrestore( &vcrypto->data_vq[qid].lock, flags); /* Finish the encrypt or decrypt process */ - ablk_req->base.complete(&ablk_req->base, error); + virtio_crypto_ablkcipher_finalize_req(vc_req, + ablk_req, error); spin_lock_irqsave( &vcrypto->data_vq[qid].lock, flags); } @@ -87,6 +87,7 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi) int ret = -ENOMEM; int i, total_vqs; const char **names; + struct device *dev = &vi->vdev->dev; /* * We expect 1 data virtqueue, followed by @@ -119,7 +120,7 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi) } ret = vi->vdev->config->find_vqs(vi->vdev, total_vqs, vqs, callbacks, - names); + names, NULL); if (ret) goto err_find; @@ -128,6 +129,15 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi) for (i = 0; i < vi->max_data_queues; i++) { spin_lock_init(&vi->data_vq[i].lock); vi->data_vq[i].vq = vqs[i]; + /* Initialize crypto engine */ + vi->data_vq[i].engine = crypto_engine_alloc_init(dev, 1); + if (!vi->data_vq[i].engine) { + ret = -ENOMEM; + goto err_engine; + } + + vi->data_vq[i].engine->cipher_one_request = + virtio_crypto_ablkcipher_crypt_req; } kfree(names); @@ -136,6 +146,7 @@ static int virtcrypto_find_vqs(struct virtio_crypto *vi) return 0; +err_engine: err_find: kfree(names); err_names: @@ -269,6 +280,38 @@ static int virtcrypto_update_status(struct virtio_crypto *vcrypto) return 0; } +static int virtcrypto_start_crypto_engines(struct virtio_crypto *vcrypto) +{ + int32_t i; + int ret; + + for (i = 0; i < vcrypto->max_data_queues; i++) { + if (vcrypto->data_vq[i].engine) { + ret = crypto_engine_start(vcrypto->data_vq[i].engine); + if (ret) + goto err; + } + } + + return 0; + +err: + while (--i >= 0) + if (vcrypto->data_vq[i].engine) + crypto_engine_exit(vcrypto->data_vq[i].engine); + + return ret; +} + +static void virtcrypto_clear_crypto_engines(struct virtio_crypto *vcrypto) +{ + u32 i; + + for (i = 0; i < vcrypto->max_data_queues; i++) + if (vcrypto->data_vq[i].engine) + crypto_engine_exit(vcrypto->data_vq[i].engine); +} + static void virtcrypto_del_vqs(struct virtio_crypto *vcrypto) { struct virtio_device *vdev = vcrypto->vdev; @@ -355,14 +398,21 @@ static int virtcrypto_probe(struct virtio_device *vdev) dev_err(&vdev->dev, "Failed to initialize vqs.\n"); goto free_dev; } + + err = virtcrypto_start_crypto_engines(vcrypto); + if (err) + goto free_vqs; + virtio_device_ready(vdev); err = virtcrypto_update_status(vcrypto); if (err) - goto free_vqs; + goto free_engines; return 0; +free_engines: + virtcrypto_clear_crypto_engines(vcrypto); free_vqs: vcrypto->vdev->config->reset(vdev); virtcrypto_del_vqs(vcrypto); @@ -398,6 +448,7 @@ static void virtcrypto_remove(struct virtio_device *vdev) virtcrypto_dev_stop(vcrypto); vdev->config->reset(vdev); virtcrypto_free_unused_reqs(vcrypto); + virtcrypto_clear_crypto_engines(vcrypto); virtcrypto_del_vqs(vcrypto); virtcrypto_devmgr_rm_dev(vcrypto); kfree(vcrypto); @@ -420,6 +471,7 @@ static int virtcrypto_freeze(struct virtio_device *vdev) if (virtcrypto_dev_started(vcrypto)) virtcrypto_dev_stop(vcrypto); + virtcrypto_clear_crypto_engines(vcrypto); virtcrypto_del_vqs(vcrypto); return 0; } @@ -433,14 +485,26 @@ static int virtcrypto_restore(struct virtio_device *vdev) if (err) return err; + err = virtcrypto_start_crypto_engines(vcrypto); + if (err) + goto free_vqs; + virtio_device_ready(vdev); + err = virtcrypto_dev_start(vcrypto); if (err) { dev_err(&vdev->dev, "Failed to start virtio crypto device.\n"); - return -EFAULT; + goto free_engines; } return 0; + +free_engines: + virtcrypto_clear_crypto_engines(vcrypto); +free_vqs: + vcrypto->vdev->config->reset(vdev); + virtcrypto_del_vqs(vcrypto); + return err; } #endif diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c index 94ad5c0adbcb..72a26eb4e954 100644 --- a/drivers/crypto/vmx/aes_cbc.c +++ b/drivers/crypto/vmx/aes_cbc.c @@ -27,11 +27,12 @@ #include <asm/switch_to.h> #include <crypto/aes.h> #include <crypto/scatterwalk.h> +#include <crypto/skcipher.h> #include "aesp8-ppc.h" struct p8_aes_cbc_ctx { - struct crypto_blkcipher *fallback; + struct crypto_skcipher *fallback; struct aes_key enc_key; struct aes_key dec_key; }; @@ -39,7 +40,7 @@ struct p8_aes_cbc_ctx { static int p8_aes_cbc_init(struct crypto_tfm *tfm) { const char *alg; - struct crypto_blkcipher *fallback; + struct crypto_skcipher *fallback; struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm); if (!(alg = crypto_tfm_alg_name(tfm))) { @@ -47,8 +48,9 @@ static int p8_aes_cbc_init(struct crypto_tfm *tfm) return -ENOENT; } - fallback = - crypto_alloc_blkcipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK); + fallback = crypto_alloc_skcipher(alg, 0, + CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(fallback)) { printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n", @@ -56,11 +58,12 @@ static int p8_aes_cbc_init(struct crypto_tfm *tfm) return PTR_ERR(fallback); } printk(KERN_INFO "Using '%s' as fallback implementation.\n", - crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); + crypto_skcipher_driver_name(fallback)); + - crypto_blkcipher_set_flags( + crypto_skcipher_set_flags( fallback, - crypto_blkcipher_get_flags((struct crypto_blkcipher *)tfm)); + crypto_skcipher_get_flags((struct crypto_skcipher *)tfm)); ctx->fallback = fallback; return 0; @@ -71,7 +74,7 @@ static void p8_aes_cbc_exit(struct crypto_tfm *tfm) struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm); if (ctx->fallback) { - crypto_free_blkcipher(ctx->fallback); + crypto_free_skcipher(ctx->fallback); ctx->fallback = NULL; } } @@ -91,7 +94,7 @@ static int p8_aes_cbc_setkey(struct crypto_tfm *tfm, const u8 *key, pagefault_enable(); preempt_enable(); - ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen); + ret += crypto_skcipher_setkey(ctx->fallback, key, keylen); return ret; } @@ -103,15 +106,14 @@ static int p8_aes_cbc_encrypt(struct blkcipher_desc *desc, struct blkcipher_walk walk; struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm)); - struct blkcipher_desc fallback_desc = { - .tfm = ctx->fallback, - .info = desc->info, - .flags = desc->flags - }; if (in_interrupt()) { - ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, - nbytes); + SKCIPHER_REQUEST_ON_STACK(req, ctx->fallback); + skcipher_request_set_tfm(req, ctx->fallback); + skcipher_request_set_callback(req, desc->flags, NULL, NULL); + skcipher_request_set_crypt(req, src, dst, nbytes, desc->info); + ret = crypto_skcipher_encrypt(req); + skcipher_request_zero(req); } else { preempt_disable(); pagefault_disable(); @@ -144,15 +146,14 @@ static int p8_aes_cbc_decrypt(struct blkcipher_desc *desc, struct blkcipher_walk walk; struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm)); - struct blkcipher_desc fallback_desc = { - .tfm = ctx->fallback, - .info = desc->info, - .flags = desc->flags - }; if (in_interrupt()) { - ret = crypto_blkcipher_decrypt(&fallback_desc, dst, src, - nbytes); + SKCIPHER_REQUEST_ON_STACK(req, ctx->fallback); + skcipher_request_set_tfm(req, ctx->fallback); + skcipher_request_set_callback(req, desc->flags, NULL, NULL); + skcipher_request_set_crypt(req, src, dst, nbytes, desc->info); + ret = crypto_skcipher_decrypt(req); + skcipher_request_zero(req); } else { preempt_disable(); pagefault_disable(); diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c index 38ed10d761d0..7cf6d31c1123 100644 --- a/drivers/crypto/vmx/aes_ctr.c +++ b/drivers/crypto/vmx/aes_ctr.c @@ -80,11 +80,13 @@ static int p8_aes_ctr_setkey(struct crypto_tfm *tfm, const u8 *key, int ret; struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm); + preempt_disable(); pagefault_disable(); enable_kernel_vsx(); ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key); disable_kernel_vsx(); pagefault_enable(); + preempt_enable(); ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen); return ret; @@ -99,11 +101,13 @@ static void p8_aes_ctr_final(struct p8_aes_ctr_ctx *ctx, u8 *dst = walk->dst.virt.addr; unsigned int nbytes = walk->nbytes; + preempt_disable(); pagefault_disable(); enable_kernel_vsx(); aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key); disable_kernel_vsx(); pagefault_enable(); + preempt_enable(); crypto_xor(keystream, src, nbytes); memcpy(dst, keystream, nbytes); @@ -132,6 +136,7 @@ static int p8_aes_ctr_crypt(struct blkcipher_desc *desc, blkcipher_walk_init(&walk, dst, src, nbytes); ret = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) { + preempt_disable(); pagefault_disable(); enable_kernel_vsx(); aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr, @@ -143,6 +148,7 @@ static int p8_aes_ctr_crypt(struct blkcipher_desc *desc, walk.iv); disable_kernel_vsx(); pagefault_enable(); + preempt_enable(); /* We need to update IV mostly for last bytes/round */ inc = (nbytes & AES_BLOCK_MASK) / AES_BLOCK_SIZE; diff --git a/drivers/crypto/vmx/aes_xts.c b/drivers/crypto/vmx/aes_xts.c index 24353ec336c5..6adc9290557a 100644 --- a/drivers/crypto/vmx/aes_xts.c +++ b/drivers/crypto/vmx/aes_xts.c @@ -28,11 +28,12 @@ #include <crypto/aes.h> #include <crypto/scatterwalk.h> #include <crypto/xts.h> +#include <crypto/skcipher.h> #include "aesp8-ppc.h" struct p8_aes_xts_ctx { - struct crypto_blkcipher *fallback; + struct crypto_skcipher *fallback; struct aes_key enc_key; struct aes_key dec_key; struct aes_key tweak_key; @@ -41,7 +42,7 @@ struct p8_aes_xts_ctx { static int p8_aes_xts_init(struct crypto_tfm *tfm) { const char *alg; - struct crypto_blkcipher *fallback; + struct crypto_skcipher *fallback; struct p8_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm); if (!(alg = crypto_tfm_alg_name(tfm))) { @@ -49,8 +50,8 @@ static int p8_aes_xts_init(struct crypto_tfm *tfm) return -ENOENT; } - fallback = - crypto_alloc_blkcipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK); + fallback = crypto_alloc_skcipher(alg, 0, + CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(fallback)) { printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n", @@ -58,11 +59,11 @@ static int p8_aes_xts_init(struct crypto_tfm *tfm) return PTR_ERR(fallback); } printk(KERN_INFO "Using '%s' as fallback implementation.\n", - crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); + crypto_skcipher_driver_name(fallback)); - crypto_blkcipher_set_flags( + crypto_skcipher_set_flags( fallback, - crypto_blkcipher_get_flags((struct crypto_blkcipher *)tfm)); + crypto_skcipher_get_flags((struct crypto_skcipher *)tfm)); ctx->fallback = fallback; return 0; @@ -73,7 +74,7 @@ static void p8_aes_xts_exit(struct crypto_tfm *tfm) struct p8_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm); if (ctx->fallback) { - crypto_free_blkcipher(ctx->fallback); + crypto_free_skcipher(ctx->fallback); ctx->fallback = NULL; } } @@ -98,7 +99,7 @@ static int p8_aes_xts_setkey(struct crypto_tfm *tfm, const u8 *key, pagefault_enable(); preempt_enable(); - ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen); + ret += crypto_skcipher_setkey(ctx->fallback, key, keylen); return ret; } @@ -113,15 +114,14 @@ static int p8_aes_xts_crypt(struct blkcipher_desc *desc, struct blkcipher_walk walk; struct p8_aes_xts_ctx *ctx = crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm)); - struct blkcipher_desc fallback_desc = { - .tfm = ctx->fallback, - .info = desc->info, - .flags = desc->flags - }; if (in_interrupt()) { - ret = enc ? crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes) : - crypto_blkcipher_decrypt(&fallback_desc, dst, src, nbytes); + SKCIPHER_REQUEST_ON_STACK(req, ctx->fallback); + skcipher_request_set_tfm(req, ctx->fallback); + skcipher_request_set_callback(req, desc->flags, NULL, NULL); + skcipher_request_set_crypt(req, src, dst, nbytes, desc->info); + ret = enc? crypto_skcipher_encrypt(req) : crypto_skcipher_decrypt(req); + skcipher_request_zero(req); } else { preempt_disable(); pagefault_disable(); diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c index 18e9875f6277..8d9829ff2a78 100644 --- a/drivers/dax/dax.c +++ b/drivers/dax/dax.c @@ -13,6 +13,7 @@ #include <linux/pagemap.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/magic.h> #include <linux/mount.h> #include <linux/pfn_t.h> #include <linux/hash.h> @@ -419,8 +420,7 @@ static phys_addr_t pgoff_to_phys(struct dax_dev *dax_dev, pgoff_t pgoff, return -1; } -static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma, - struct vm_fault *vmf) +static int __dax_dev_pte_fault(struct dax_dev *dax_dev, struct vm_fault *vmf) { struct device *dev = &dax_dev->dev; struct dax_region *dax_region; @@ -428,7 +428,7 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma, phys_addr_t phys; pfn_t pfn; - if (check_vma(dax_dev, vma, __func__)) + if (check_vma(dax_dev, vmf->vma, __func__)) return VM_FAULT_SIGBUS; dax_region = dax_dev->region; @@ -446,7 +446,7 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma, pfn = phys_to_pfn_t(phys, dax_region->pfn_flags); - rc = vm_insert_mixed(vma, vmf->address, pfn); + rc = vm_insert_mixed(vmf->vma, vmf->address, pfn); if (rc == -ENOMEM) return VM_FAULT_OOM; @@ -456,22 +456,6 @@ static int __dax_dev_fault(struct dax_dev *dax_dev, struct vm_area_struct *vma, return VM_FAULT_NOPAGE; } -static int dax_dev_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - int rc; - struct file *filp = vma->vm_file; - struct dax_dev *dax_dev = filp->private_data; - - dev_dbg(&dax_dev->dev, "%s: %s: %s (%#lx - %#lx)\n", __func__, - current->comm, (vmf->flags & FAULT_FLAG_WRITE) - ? "write" : "read", vma->vm_start, vma->vm_end); - rcu_read_lock(); - rc = __dax_dev_fault(dax_dev, vma, vmf); - rcu_read_unlock(); - - return rc; -} - static int __dax_dev_pmd_fault(struct dax_dev *dax_dev, struct vm_fault *vmf) { unsigned long pmd_addr = vmf->address & PMD_MASK; @@ -510,7 +494,53 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev, struct vm_fault *vmf) vmf->flags & FAULT_FLAG_WRITE); } -static int dax_dev_pmd_fault(struct vm_fault *vmf) +#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD +static int __dax_dev_pud_fault(struct dax_dev *dax_dev, struct vm_fault *vmf) +{ + unsigned long pud_addr = vmf->address & PUD_MASK; + struct device *dev = &dax_dev->dev; + struct dax_region *dax_region; + phys_addr_t phys; + pgoff_t pgoff; + pfn_t pfn; + + if (check_vma(dax_dev, vmf->vma, __func__)) + return VM_FAULT_SIGBUS; + + dax_region = dax_dev->region; + if (dax_region->align > PUD_SIZE) { + dev_dbg(dev, "%s: alignment > fault size\n", __func__); + return VM_FAULT_SIGBUS; + } + + /* dax pud mappings require pfn_t_devmap() */ + if ((dax_region->pfn_flags & (PFN_DEV|PFN_MAP)) != (PFN_DEV|PFN_MAP)) { + dev_dbg(dev, "%s: alignment > fault size\n", __func__); + return VM_FAULT_SIGBUS; + } + + pgoff = linear_page_index(vmf->vma, pud_addr); + phys = pgoff_to_phys(dax_dev, pgoff, PUD_SIZE); + if (phys == -1) { + dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__, + pgoff); + return VM_FAULT_SIGBUS; + } + + pfn = phys_to_pfn_t(phys, dax_region->pfn_flags); + + return vmf_insert_pfn_pud(vmf->vma, vmf->address, vmf->pud, pfn, + vmf->flags & FAULT_FLAG_WRITE); +} +#else +static int __dax_dev_pud_fault(struct dax_dev *dax_dev, struct vm_fault *vmf) +{ + return VM_FAULT_FALLBACK; +} +#endif /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ + +static int dax_dev_huge_fault(struct vm_fault *vmf, + enum page_entry_size pe_size) { int rc; struct file *filp = vmf->vma->vm_file; @@ -522,15 +552,32 @@ static int dax_dev_pmd_fault(struct vm_fault *vmf) vmf->vma->vm_start, vmf->vma->vm_end); rcu_read_lock(); - rc = __dax_dev_pmd_fault(dax_dev, vmf); + switch (pe_size) { + case PE_SIZE_PTE: + rc = __dax_dev_pte_fault(dax_dev, vmf); + break; + case PE_SIZE_PMD: + rc = __dax_dev_pmd_fault(dax_dev, vmf); + break; + case PE_SIZE_PUD: + rc = __dax_dev_pud_fault(dax_dev, vmf); + break; + default: + return VM_FAULT_FALLBACK; + } rcu_read_unlock(); return rc; } +static int dax_dev_fault(struct vm_fault *vmf) +{ + return dax_dev_huge_fault(vmf, PE_SIZE_PTE); +} + static const struct vm_operations_struct dax_dev_vm_ops = { .fault = dax_dev_fault, - .pmd_fault = dax_dev_pmd_fault, + .huge_fault = dax_dev_huge_fault, }; static int dax_mmap(struct file *filp, struct vm_area_struct *vma) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 551a271353d2..dea04871b50d 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1228,7 +1228,7 @@ static int __init devfreq_init(void) subsys_initcall(devfreq_init); /* - * The followings are helper functions for devfreq user device drivers with + * The following are helper functions for devfreq user device drivers with * OPP framework. */ diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index e72e64484131..0007b792827b 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -124,6 +124,28 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence) return base + offset; } +/** + * DOC: fence polling + * + * To support cross-device and cross-driver synchronization of buffer access + * implicit fences (represented internally in the kernel with &struct fence) can + * be attached to a &dma_buf. The glue for that and a few related things are + * provided in the &reservation_object structure. + * + * Userspace can query the state of these implicitly tracked fences using poll() + * and related system calls: + * + * - Checking for POLLIN, i.e. read access, can be use to query the state of the + * most recent write or exclusive fence. + * + * - Checking for POLLOUT, i.e. write access, can be used to query the state of + * all attached fences, shared and exclusive ones. + * + * Note that this only signals the completion of the respective fences, i.e. the + * DMA transfers are complete. Cache flushing and any other necessary + * preparations before CPU access can begin still need to happen. + */ + static void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb) { struct dma_buf_poll_cb_t *dcb = (struct dma_buf_poll_cb_t *)cb; @@ -303,6 +325,9 @@ static const struct file_operations dma_buf_fops = { .llseek = dma_buf_llseek, .poll = dma_buf_poll, .unlocked_ioctl = dma_buf_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = dma_buf_ioctl, +#endif }; /* @@ -314,19 +339,52 @@ static inline int is_dma_buf_file(struct file *file) } /** + * DOC: dma buf device access + * + * For device DMA access to a shared DMA buffer the usual sequence of operations + * is fairly simple: + * + * 1. The exporter defines his exporter instance using + * DEFINE_DMA_BUF_EXPORT_INFO() and calls dma_buf_export() to wrap a private + * buffer object into a &dma_buf. It then exports that &dma_buf to userspace + * as a file descriptor by calling dma_buf_fd(). + * + * 2. Userspace passes this file-descriptors to all drivers it wants this buffer + * to share with: First the filedescriptor is converted to a &dma_buf using + * dma_buf_get(). The the buffer is attached to the device using + * dma_buf_attach(). + * + * Up to this stage the exporter is still free to migrate or reallocate the + * backing storage. + * + * 3. Once the buffer is attached to all devices userspace can inniate DMA + * access to the shared buffer. In the kernel this is done by calling + * dma_buf_map_attachment() and dma_buf_unmap_attachment(). + * + * 4. Once a driver is done with a shared buffer it needs to call + * dma_buf_detach() (after cleaning up any mappings) and then release the + * reference acquired with dma_buf_get by calling dma_buf_put(). + * + * For the detailed semantics exporters are expected to implement see + * &dma_buf_ops. + */ + +/** * dma_buf_export - Creates a new dma_buf, and associates an anon file * with this buffer, so it can be exported. * Also connect the allocator specific data and ops to the buffer. * Additionally, provide a name string for exporter; useful in debugging. * * @exp_info: [in] holds all the export related information provided - * by the exporter. see struct dma_buf_export_info + * by the exporter. see &struct dma_buf_export_info * for further details. * * Returns, on success, a newly created dma_buf object, which wraps the * supplied private data and operations for dma_buf_ops. On either missing * ops, or error in allocating struct dma_buf, will return negative error. * + * For most cases the easiest way to create @exp_info is through the + * %DEFINE_DMA_BUF_EXPORT_INFO macro. */ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) { @@ -458,7 +516,11 @@ EXPORT_SYMBOL_GPL(dma_buf_get); * dma_buf_put - decreases refcount of the buffer * @dmabuf: [in] buffer to reduce refcount of * - * Uses file's refcounting done implicitly by fput() + * Uses file's refcounting done implicitly by fput(). + * + * If, as a result of this call, the refcount becomes 0, the 'release' file + * operation related to this fd is called. It calls &dma_buf_ops.release vfunc + * in turn, and frees the memory allocated for dmabuf when exported. */ void dma_buf_put(struct dma_buf *dmabuf) { @@ -475,8 +537,17 @@ EXPORT_SYMBOL_GPL(dma_buf_put); * @dmabuf: [in] buffer to attach device to. * @dev: [in] device to be attached. * - * Returns struct dma_buf_attachment * for this attachment; returns ERR_PTR on - * error. + * Returns struct dma_buf_attachment pointer for this attachment. Attachments + * must be cleaned up by calling dma_buf_detach(). + * + * Returns: + * + * A pointer to newly created &dma_buf_attachment on success, or a negative + * error code wrapped into a pointer on failure. + * + * Note that this can fail if the backing storage of @dmabuf is in a place not + * accessible to @dev, and cannot be moved to a more suitable place. This is + * indicated with the error code -EBUSY. */ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, struct device *dev) @@ -519,6 +590,7 @@ EXPORT_SYMBOL_GPL(dma_buf_attach); * @dmabuf: [in] buffer to detach from. * @attach: [in] attachment to be detached; is free'd after this call. * + * Clean up a device attachment obtained by calling dma_buf_attach(). */ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { @@ -543,7 +615,12 @@ EXPORT_SYMBOL_GPL(dma_buf_detach); * @direction: [in] direction of DMA transfer * * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR - * on error. + * on error. May return -EINTR if it is interrupted by a signal. + * + * A mapping must be unmapped again using dma_buf_map_attachment(). Note that + * the underlying backing storage is pinned for as long as a mapping exists, + * therefore users/importers should not hold onto a mapping for undue amounts of + * time. */ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, enum dma_data_direction direction) @@ -571,6 +648,7 @@ EXPORT_SYMBOL_GPL(dma_buf_map_attachment); * @sg_table: [in] scatterlist info of the buffer to unmap * @direction: [in] direction of DMA transfer * + * This unmaps a DMA mapping for @attached obtained by dma_buf_map_attachment(). */ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, struct sg_table *sg_table, @@ -586,6 +664,122 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, } EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); +/** + * DOC: cpu access + * + * There are mutliple reasons for supporting CPU access to a dma buffer object: + * + * - Fallback operations in the kernel, for example when a device is connected + * over USB and the kernel needs to shuffle the data around first before + * sending it away. Cache coherency is handled by braketing any transactions + * with calls to dma_buf_begin_cpu_access() and dma_buf_end_cpu_access() + * access. + * + * To support dma_buf objects residing in highmem cpu access is page-based + * using an api similar to kmap. Accessing a dma_buf is done in aligned chunks + * of PAGE_SIZE size. Before accessing a chunk it needs to be mapped, which + * returns a pointer in kernel virtual address space. Afterwards the chunk + * needs to be unmapped again. There is no limit on how often a given chunk + * can be mapped and unmapped, i.e. the importer does not need to call + * begin_cpu_access again before mapping the same chunk again. + * + * Interfaces:: + * void \*dma_buf_kmap(struct dma_buf \*, unsigned long); + * void dma_buf_kunmap(struct dma_buf \*, unsigned long, void \*); + * + * There are also atomic variants of these interfaces. Like for kmap they + * facilitate non-blocking fast-paths. Neither the importer nor the exporter + * (in the callback) is allowed to block when using these. + * + * Interfaces:: + * void \*dma_buf_kmap_atomic(struct dma_buf \*, unsigned long); + * void dma_buf_kunmap_atomic(struct dma_buf \*, unsigned long, void \*); + * + * For importers all the restrictions of using kmap apply, like the limited + * supply of kmap_atomic slots. Hence an importer shall only hold onto at + * max 2 atomic dma_buf kmaps at the same time (in any given process context). + * + * dma_buf kmap calls outside of the range specified in begin_cpu_access are + * undefined. If the range is not PAGE_SIZE aligned, kmap needs to succeed on + * the partial chunks at the beginning and end but may return stale or bogus + * data outside of the range (in these partial chunks). + * + * Note that these calls need to always succeed. The exporter needs to + * complete any preparations that might fail in begin_cpu_access. + * + * For some cases the overhead of kmap can be too high, a vmap interface + * is introduced. This interface should be used very carefully, as vmalloc + * space is a limited resources on many architectures. + * + * Interfaces:: + * void \*dma_buf_vmap(struct dma_buf \*dmabuf) + * void dma_buf_vunmap(struct dma_buf \*dmabuf, void \*vaddr) + * + * The vmap call can fail if there is no vmap support in the exporter, or if + * it runs out of vmalloc space. Fallback to kmap should be implemented. Note + * that the dma-buf layer keeps a reference count for all vmap access and + * calls down into the exporter's vmap function only when no vmapping exists, + * and only unmaps it once. Protection against concurrent vmap/vunmap calls is + * provided by taking the dma_buf->lock mutex. + * + * - For full compatibility on the importer side with existing userspace + * interfaces, which might already support mmap'ing buffers. This is needed in + * many processing pipelines (e.g. feeding a software rendered image into a + * hardware pipeline, thumbnail creation, snapshots, ...). Also, Android's ION + * framework already supported this and for DMA buffer file descriptors to + * replace ION buffers mmap support was needed. + * + * There is no special interfaces, userspace simply calls mmap on the dma-buf + * fd. But like for CPU access there's a need to braket the actual access, + * which is handled by the ioctl (DMA_BUF_IOCTL_SYNC). Note that + * DMA_BUF_IOCTL_SYNC can fail with -EAGAIN or -EINTR, in which case it must + * be restarted. + * + * Some systems might need some sort of cache coherency management e.g. when + * CPU and GPU domains are being accessed through dma-buf at the same time. + * To circumvent this problem there are begin/end coherency markers, that + * forward directly to existing dma-buf device drivers vfunc hooks. Userspace + * can make use of those markers through the DMA_BUF_IOCTL_SYNC ioctl. The + * sequence would be used like following: + * + * - mmap dma-buf fd + * - for each drawing/upload cycle in CPU 1. SYNC_START ioctl, 2. read/write + * to mmap area 3. SYNC_END ioctl. This can be repeated as often as you + * want (with the new data being consumed by say the GPU or the scanout + * device) + * - munmap once you don't need the buffer any more + * + * For correctness and optimal performance, it is always required to use + * SYNC_START and SYNC_END before and after, respectively, when accessing the + * mapped address. Userspace cannot rely on coherent access, even when there + * are systems where it just works without calling these ioctls. + * + * - And as a CPU fallback in userspace processing pipelines. + * + * Similar to the motivation for kernel cpu access it is again important that + * the userspace code of a given importing subsystem can use the same + * interfaces with a imported dma-buf buffer object as with a native buffer + * object. This is especially important for drm where the userspace part of + * contemporary OpenGL, X, and other drivers is huge, and reworking them to + * use a different way to mmap a buffer rather invasive. + * + * The assumption in the current dma-buf interfaces is that redirecting the + * initial mmap is all that's needed. A survey of some of the existing + * subsystems shows that no driver seems to do any nefarious thing like + * syncing up with outstanding asynchronous processing on the device or + * allocating special resources at fault time. So hopefully this is good + * enough, since adding interfaces to intercept pagefaults and allow pte + * shootdowns would increase the complexity quite a bit. + * + * Interface:: + * int dma_buf_mmap(struct dma_buf \*, struct vm_area_struct \*, + * unsigned long); + * + * If the importing subsystem simply provides a special-purpose mmap call to + * set up a mapping in userspace, calling do_mmap with dma_buf->file will + * equally achieve that for a dma-buf object. + */ + static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction) { @@ -611,6 +805,10 @@ static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf, * @dmabuf: [in] buffer to prepare cpu access for. * @direction: [in] length of range for cpu access. * + * After the cpu access is complete the caller should call + * dma_buf_end_cpu_access(). Only when cpu access is braketed by both calls is + * it guaranteed to be coherent with other DMA access. + * * Can return negative error values, returns 0 on success. */ int dma_buf_begin_cpu_access(struct dma_buf *dmabuf, @@ -643,6 +841,8 @@ EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access); * @dmabuf: [in] buffer to complete cpu access for. * @direction: [in] length of range for cpu access. * + * This terminates CPU access started with dma_buf_begin_cpu_access(). + * * Can return negative error values, returns 0 on success. */ int dma_buf_end_cpu_access(struct dma_buf *dmabuf, diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 0212af7997d9..d195d617076d 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -22,12 +22,14 @@ #include <linux/export.h> #include <linux/atomic.h> #include <linux/dma-fence.h> +#include <linux/sched/signal.h> #define CREATE_TRACE_POINTS #include <trace/events/dma_fence.h> EXPORT_TRACEPOINT_SYMBOL(dma_fence_annotate_wait_on); EXPORT_TRACEPOINT_SYMBOL(dma_fence_emit); +EXPORT_TRACEPOINT_SYMBOL(dma_fence_enable_signal); /* * fence context counter: each execution context should have its own @@ -282,6 +284,31 @@ int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb, EXPORT_SYMBOL(dma_fence_add_callback); /** + * dma_fence_get_status - returns the status upon completion + * @fence: [in] the dma_fence to query + * + * This wraps dma_fence_get_status_locked() to return the error status + * condition on a signaled fence. See dma_fence_get_status_locked() for more + * details. + * + * Returns 0 if the fence has not yet been signaled, 1 if the fence has + * been signaled without an error condition, or a negative error code + * if the fence has been completed in err. + */ +int dma_fence_get_status(struct dma_fence *fence) +{ + unsigned long flags; + int status; + + spin_lock_irqsave(fence->lock, flags); + status = dma_fence_get_status_locked(fence); + spin_unlock_irqrestore(fence->lock, flags); + + return status; +} +EXPORT_SYMBOL(dma_fence_get_status); + +/** * dma_fence_remove_callback - remove a callback from the signaling list * @fence: [in] the fence to wait on * @cb: [in] the callback to remove @@ -541,6 +568,7 @@ dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, fence->context = context; fence->seqno = seqno; fence->flags = 0UL; + fence->error = 0; trace_dma_fence_init(fence); } diff --git a/drivers/dma-buf/sync_debug.c b/drivers/dma-buf/sync_debug.c index 48b20e34fb6d..c769dc653b34 100644 --- a/drivers/dma-buf/sync_debug.c +++ b/drivers/dma-buf/sync_debug.c @@ -62,30 +62,29 @@ void sync_file_debug_remove(struct sync_file *sync_file) static const char *sync_status_str(int status) { - if (status == 0) - return "signaled"; + if (status < 0) + return "error"; if (status > 0) - return "active"; + return "signaled"; - return "error"; + return "active"; } static void sync_print_fence(struct seq_file *s, struct dma_fence *fence, bool show) { - int status = 1; struct sync_timeline *parent = dma_fence_parent(fence); + int status; - if (dma_fence_is_signaled_locked(fence)) - status = fence->status; + status = dma_fence_get_status_locked(fence); seq_printf(s, " %s%sfence %s", show ? parent->name : "", show ? "_" : "", sync_status_str(status)); - if (status <= 0) { + if (status) { struct timespec64 ts64 = ktime_to_timespec64(fence->timestamp); @@ -136,7 +135,7 @@ static void sync_print_sync_file(struct seq_file *s, int i; seq_printf(s, "[%p] %s: %s\n", sync_file, sync_file->name, - sync_status_str(!dma_fence_is_signaled(sync_file->fence))); + sync_status_str(dma_fence_get_status(sync_file->fence))); if (dma_fence_is_array(sync_file->fence)) { struct dma_fence_array *array = to_dma_fence_array(sync_file->fence); diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 6d802f2d2881..2321035f6204 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -67,9 +67,10 @@ static void fence_check_cb_func(struct dma_fence *f, struct dma_fence_cb *cb) * sync_file_create() - creates a sync file * @fence: fence to add to the sync_fence * - * Creates a sync_file containg @fence. Once this is called, the sync_file - * takes ownership of @fence. The sync_file can be released with - * fput(sync_file->file). Returns the sync_file or NULL in case of error. + * Creates a sync_file containg @fence. This function acquires and additional + * reference of @fence for the newly-created &sync_file, if it succeeds. The + * sync_file can be released with fput(sync_file->file). Returns the + * sync_file or NULL in case of error. */ struct sync_file *sync_file_create(struct dma_fence *fence) { @@ -90,13 +91,6 @@ struct sync_file *sync_file_create(struct dma_fence *fence) } EXPORT_SYMBOL(sync_file_create); -/** - * sync_file_fdget() - get a sync_file from an fd - * @fd: fd referencing a fence - * - * Ensures @fd references a valid sync_file, increments the refcount of the - * backing file. Returns the sync_file or NULL in case of error. - */ static struct sync_file *sync_file_fdget(int fd) { struct file *file = fget(fd); @@ -379,10 +373,8 @@ static void sync_fill_fence_info(struct dma_fence *fence, sizeof(info->obj_name)); strlcpy(info->driver_name, fence->ops->get_driver_name(fence), sizeof(info->driver_name)); - if (dma_fence_is_signaled(fence)) - info->status = fence->status >= 0 ? 1 : fence->status; - else - info->status = 0; + + info->status = dma_fence_get_status(fence); info->timestamp_ns = ktime_to_ns(fence->timestamp); } @@ -468,4 +460,3 @@ static const struct file_operations sync_file_fops = { .unlocked_ioctl = sync_file_ioctl, .compat_ioctl = sync_file_ioctl, }; - diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index c9297605058c..54d581d407aa 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -16,6 +16,7 @@ #include <linux/freezer.h> #include <linux/init.h> #include <linux/kthread.h> +#include <linux/sched/task.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/random.h> diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index 3e882aa107e8..eaa355e7d9e4 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -537,7 +537,7 @@ static void rt8973a_init_dev_type(struct rt8973a_muic_info *info) regmap_update_bits(info->regmap, reg, mask, val); } - /* Check whether RT8973A is auto swithcing mode or not */ + /* Check whether RT8973A is auto switching mode or not */ ret = regmap_read(info->regmap, RT8973A_REG_CONTROL1, &data); if (ret) { dev_err(info->dev, diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index aee149bdf4c0..a301fcf46e88 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1307,8 +1307,7 @@ static void iso_resource_work(struct work_struct *work) */ if (r->todo == ISO_RES_REALLOC && !success && !client->in_shutdown && - idr_find(&client->resource_idr, r->resource.handle)) { - idr_remove(&client->resource_idr, r->resource.handle); + idr_remove(&client->resource_idr, r->resource.handle)) { client_put(client); free = true; } diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index f9e3aee6a211..7c2eed76011e 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -1068,7 +1068,7 @@ static void fw_device_init(struct work_struct *work) /* * Transition the device to running state. If it got pulled - * out from under us while we did the intialization work, we + * out from under us while we did the initialization work, we * have to shut down the device again here. Normally, though, * fw_node_event will be responsible for shutting it down when * necessary. We have to use the atomic cmpxchg here to avoid @@ -1231,7 +1231,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) break; /* - * Do minimal intialization of the device here, the + * Do minimal initialization of the device here, the * rest will happen in fw_device_init(). * * Attention: A lot of things, even fw_device_get(), diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 1867f0d1389b..6e4ed5a9c6fd 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -21,6 +21,7 @@ config ARM_PSCI_CHECKER config ARM_SCPI_PROTOCOL tristate "ARM System Control and Power Interface (SCPI) Message Protocol" + depends on ARM || ARM64 || COMPILE_TEST depends on MAILBOX help System Control and Power Interface (SCPI) Message Protocol is diff --git a/drivers/firmware/psci_checker.c b/drivers/firmware/psci_checker.c index 29d58feaf675..6523ce962865 100644 --- a/drivers/firmware/psci_checker.c +++ b/drivers/firmware/psci_checker.c @@ -20,6 +20,7 @@ #include <linux/cpu_pm.h> #include <linux/kernel.h> #include <linux/kthread.h> +#include <uapi/linux/sched/types.h> #include <linux/module.h> #include <linux/preempt.h> #include <linux/psci.h> diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index c6aeedbdcbb0..8ad226c60374 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -560,3 +560,21 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) return ret ? : le32_to_cpu(out); } + +int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) +{ + struct { + __le32 state; + __le32 id; + } req; + __le32 scm_ret = 0; + int ret; + + req.state = cpu_to_le32(state); + req.id = cpu_to_le32(id); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_REMOTE_STATE, + &req, sizeof(req), &scm_ret, sizeof(scm_ret)); + + return ret ? : le32_to_cpu(scm_ret); +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 1e2e5198db53..c9332590e8c6 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -365,3 +365,19 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) return ret ? : res.a1; } + +int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) +{ + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + int ret; + + desc.args[0] = state; + desc.args[1] = id; + desc.arginfo = QCOM_SCM_ARGS(2); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_REMOTE_STATE, + &desc, &res); + + return ret ? : res.a1; +} diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 893f953eaccf..d987bcc7489d 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -324,6 +324,12 @@ bool qcom_scm_is_available(void) } EXPORT_SYMBOL(qcom_scm_is_available); +int qcom_scm_set_remote_state(u32 state, u32 id) +{ + return __qcom_scm_set_remote_state(__scm->dev, state, id); +} +EXPORT_SYMBOL(qcom_scm_set_remote_state); + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; @@ -387,7 +393,7 @@ static int qcom_scm_probe(struct platform_device *pdev) static const struct of_device_id qcom_scm_dt_match[] = { { .compatible = "qcom,scm-apq8064", - .data = (void *) SCM_HAS_CORE_CLK, + /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */ }, { .compatible = "qcom,scm-msm8660", .data = (void *) SCM_HAS_CORE_CLK, diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 3584b00fe7e6..6a0f15469344 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -15,6 +15,8 @@ #define QCOM_SCM_SVC_BOOT 0x1 #define QCOM_SCM_BOOT_ADDR 0x1 #define QCOM_SCM_BOOT_ADDR_MC 0x11 +#define QCOM_SCM_SET_REMOTE_STATE 0xa +extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id); #define QCOM_SCM_FLAG_HLOS 0x01 #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 4ff02d310868..84e4c9a58a0c 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -19,6 +19,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/semaphore.h> +#include <linux/sched/clock.h> #include <soc/tegra/bpmp.h> #include <soc/tegra/bpmp-abi.h> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d5d36549ecc1..05043071fc98 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -185,6 +185,13 @@ config GPIO_ETRAXFS help Say yes here to support the GPIO controller on Axis ETRAX FS SoCs. +config GPIO_EXAR + tristate "Support for GPIO pins on XR17V352/354/358" + depends on SERIAL_8250_EXAR + help + Selecting this option will enable handling of GPIO pins present + on Exar XR17V352/354/358 chips. + config GPIO_GE_FPGA bool "GE FPGA based GPIO" depends on GE_FPGA @@ -197,6 +204,15 @@ config GPIO_GE_FPGA and write pin state) for GPIO implemented in a number of GE single board computers. +config GPIO_GEMINI + bool "Gemini GPIO" + depends on ARCH_GEMINI + depends on OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Support for common GPIOs found in Cortina systems Gemini platforms. + config GPIO_GENERIC_PLATFORM tristate "Generic memory-mapped GPIO controller support (MMIO platform device)" select GPIO_GENERIC @@ -282,6 +298,8 @@ config GPIO_MOCKUP tristate "GPIO Testing Driver" depends on GPIOLIB && SYSFS select GPIO_SYSFS + select GPIOLIB_IRQCHIP + select IRQ_WORK help This enables GPIO Testing driver, which provides a way to test GPIO subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS @@ -1141,6 +1159,15 @@ config GPIO_PCH ML7223/ML7831 is companion chip for Intel Atom E6xx series. ML7223/ML7831 is completely compatible for Intel EG20T PCH. +config GPIO_PCI_IDIO_16 + tristate "ACCES PCI-IDIO-16 GPIO support" + select GPIOLIB_IRQCHIP + help + Enables GPIO support for the ACCES PCI-IDIO-16. An interrupt is + generated when any of the inputs change state (low to high or high to + low). Input filter control is not supported by this driver, and the + input filters are deactivated by this driver. + config GPIO_RDC321X tristate "RDC R-321x GPIO support" select MFD_CORE @@ -1197,6 +1224,8 @@ config GPIO_MCP23S08 tristate "Microchip MCP23xxx I/O expander" depends on OF_GPIO select GPIOLIB_IRQCHIP + select REGMAP_I2C if I2C + select REGMAP if SPI_MASTER help SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017 I/O expanders. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a7676b82de6f..becb96c724fe 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -46,8 +46,10 @@ obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o +obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o +obj-$(CONFIG_GPIO_GEMINI) += gpio-gemini.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o @@ -90,6 +92,7 @@ obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o +obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index b760cbbb41d8..7031eea165c9 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -11,7 +11,7 @@ * * This file is based on kernel/irq/devres.c * - * Copyright (c) 2011 John Crispin <blogic@openwrt.org> + * Copyright (c) 2011 John Crispin <john@phrozen.org> */ #include <linux/module.h> @@ -21,6 +21,8 @@ #include <linux/device.h> #include <linux/gfp.h> +#include "gpiolib.h" + static void devm_gpiod_release(struct device *dev, void *res) { struct gpio_desc **desc = res; @@ -123,19 +125,26 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, EXPORT_SYMBOL(devm_gpiod_get_index); /** - * devm_get_gpiod_from_child - get a GPIO descriptor from a device's child node + * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a + * device's child node * @dev: GPIO consumer * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer * @child: firmware node (child of @dev) + * @flags: GPIO initialization flags * * GPIO descriptors returned from this function are automatically disposed on * driver detach. + * + * On successfull request the GPIO pin is configured in accordance with + * provided @flags. */ -struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, - const char *con_id, - struct fwnode_handle *child) +struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev, + const char *con_id, int index, + struct fwnode_handle *child, + enum gpiod_flags flags, + const char *label) { - static const char * const suffixes[] = { "gpios", "gpio" }; char prop_name[32]; /* 32 is max size of property name */ struct gpio_desc **dr; struct gpio_desc *desc; @@ -146,15 +155,16 @@ struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, if (!dr) return ERR_PTR(-ENOMEM); - for (i = 0; i < ARRAY_SIZE(suffixes); i++) { + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", - con_id, suffixes[i]); + con_id, gpio_suffixes[i]); else snprintf(prop_name, sizeof(prop_name), "%s", - suffixes[i]); + gpio_suffixes[i]); - desc = fwnode_get_named_gpiod(child, prop_name); + desc = fwnode_get_named_gpiod(child, prop_name, index, flags, + label); if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) break; } @@ -168,7 +178,7 @@ struct gpio_desc *devm_get_gpiod_from_child(struct device *dev, return desc; } -EXPORT_SYMBOL(devm_get_gpiod_from_child); +EXPORT_SYMBOL(devm_fwnode_get_index_gpiod_from_child); /** * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index fcf776971ca9..17bd2ab4ebe2 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -48,7 +48,6 @@ MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers"); * @control: Control registers state * @lock: synchronization lock to prevent I/O race conditions * @base: base port address of the GPIO device - * @irq: Interrupt line number * @irq_mask: I/O bits affected by interrupts */ struct dio48e_gpio { @@ -58,7 +57,6 @@ struct dio48e_gpio { unsigned char control[2]; spinlock_t lock; unsigned base; - unsigned irq; unsigned char irq_mask; }; @@ -204,6 +202,44 @@ static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value) spin_unlock_irqrestore(&dio48egpio->lock, flags); } +static void dio48e_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); + unsigned int i; + const unsigned int gpio_reg_size = 8; + unsigned int port; + unsigned int out_port; + unsigned int bitmask; + unsigned long flags; + + /* set bits are evaluated a gpio register size at a time */ + for (i = 0; i < chip->ngpio; i += gpio_reg_size) { + /* no more set bits in this mask word; skip to the next word */ + if (!mask[BIT_WORD(i)]) { + i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size; + continue; + } + + port = i / gpio_reg_size; + out_port = (port > 2) ? port + 1 : port; + bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)]; + + spin_lock_irqsave(&dio48egpio->lock, flags); + + /* update output state data and set device gpio register */ + dio48egpio->out_state[port] &= ~mask[BIT_WORD(i)]; + dio48egpio->out_state[port] |= bitmask; + outb(dio48egpio->out_state[port], dio48egpio->base + out_port); + + spin_unlock_irqrestore(&dio48egpio->lock, flags); + + /* prepare for next gpio register set */ + mask[BIT_WORD(i)] >>= gpio_reg_size; + bits[BIT_WORD(i)] >>= gpio_reg_size; + } +} + static void dio48e_irq_ack(struct irq_data *data) { } @@ -302,6 +338,26 @@ static irqreturn_t dio48e_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +#define DIO48E_NGPIO 48 +static const char *dio48e_names[DIO48E_NGPIO] = { + "PPI Group 0 Port A 0", "PPI Group 0 Port A 1", "PPI Group 0 Port A 2", + "PPI Group 0 Port A 3", "PPI Group 0 Port A 4", "PPI Group 0 Port A 5", + "PPI Group 0 Port A 6", "PPI Group 0 Port A 7", "PPI Group 0 Port B 0", + "PPI Group 0 Port B 1", "PPI Group 0 Port B 2", "PPI Group 0 Port B 3", + "PPI Group 0 Port B 4", "PPI Group 0 Port B 5", "PPI Group 0 Port B 6", + "PPI Group 0 Port B 7", "PPI Group 0 Port C 0", "PPI Group 0 Port C 1", + "PPI Group 0 Port C 2", "PPI Group 0 Port C 3", "PPI Group 0 Port C 4", + "PPI Group 0 Port C 5", "PPI Group 0 Port C 6", "PPI Group 0 Port C 7", + "PPI Group 1 Port A 0", "PPI Group 1 Port A 1", "PPI Group 1 Port A 2", + "PPI Group 1 Port A 3", "PPI Group 1 Port A 4", "PPI Group 1 Port A 5", + "PPI Group 1 Port A 6", "PPI Group 1 Port A 7", "PPI Group 1 Port B 0", + "PPI Group 1 Port B 1", "PPI Group 1 Port B 2", "PPI Group 1 Port B 3", + "PPI Group 1 Port B 4", "PPI Group 1 Port B 5", "PPI Group 1 Port B 6", + "PPI Group 1 Port B 7", "PPI Group 1 Port C 0", "PPI Group 1 Port C 1", + "PPI Group 1 Port C 2", "PPI Group 1 Port C 3", "PPI Group 1 Port C 4", + "PPI Group 1 Port C 5", "PPI Group 1 Port C 6", "PPI Group 1 Port C 7" +}; + static int dio48e_probe(struct device *dev, unsigned int id) { struct dio48e_gpio *dio48egpio; @@ -322,20 +378,19 @@ static int dio48e_probe(struct device *dev, unsigned int id) dio48egpio->chip.parent = dev; dio48egpio->chip.owner = THIS_MODULE; dio48egpio->chip.base = -1; - dio48egpio->chip.ngpio = 48; + dio48egpio->chip.ngpio = DIO48E_NGPIO; + dio48egpio->chip.names = dio48e_names; dio48egpio->chip.get_direction = dio48e_gpio_get_direction; dio48egpio->chip.direction_input = dio48e_gpio_direction_input; dio48egpio->chip.direction_output = dio48e_gpio_direction_output; dio48egpio->chip.get = dio48e_gpio_get; dio48egpio->chip.set = dio48e_gpio_set; + dio48egpio->chip.set_multiple = dio48e_gpio_set_multiple; dio48egpio->base = base[id]; - dio48egpio->irq = irq[id]; spin_lock_init(&dio48egpio->lock); - dev_set_drvdata(dev, dio48egpio); - - err = gpiochip_add_data(&dio48egpio->chip, dio48egpio); + err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); return err; @@ -360,30 +415,17 @@ static int dio48e_probe(struct device *dev, unsigned int id) handle_edge_irq, IRQ_TYPE_NONE); if (err) { dev_err(dev, "Could not add irqchip (%d)\n", err); - goto err_gpiochip_remove; + return err; } - err = request_irq(irq[id], dio48e_irq_handler, 0, name, dio48egpio); + err = devm_request_irq(dev, irq[id], dio48e_irq_handler, 0, name, + dio48egpio); if (err) { dev_err(dev, "IRQ handler registering failed (%d)\n", err); - goto err_gpiochip_remove; + return err; } return 0; - -err_gpiochip_remove: - gpiochip_remove(&dio48egpio->chip); - return err; -} - -static int dio48e_remove(struct device *dev, unsigned int id) -{ - struct dio48e_gpio *const dio48egpio = dev_get_drvdata(dev); - - free_irq(dio48egpio->irq, dio48egpio); - gpiochip_remove(&dio48egpio->chip); - - return 0; } static struct isa_driver dio48e_driver = { @@ -391,7 +433,6 @@ static struct isa_driver dio48e_driver = { .driver = { .name = "104-dio-48e" }, - .remove = dio48e_remove }; module_isa_driver(dio48e_driver, num_dio48e); diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 2d2763ea1a68..568375a7ebc2 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -47,7 +47,6 @@ MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers"); * @ack_lock: synchronization lock to prevent IRQ handler race conditions * @irq_mask: input bits affected by interrupts * @base: base port address of the GPIO device - * @irq: Interrupt line number * @cos_enb: Change-Of-State IRQ enable boundaries mask */ struct idi_48_gpio { @@ -56,7 +55,6 @@ struct idi_48_gpio { spinlock_t ack_lock; unsigned char irq_mask[6]; unsigned base; - unsigned irq; unsigned char cos_enb; }; @@ -219,6 +217,18 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +#define IDI48_NGPIO 48 +static const char *idi48_names[IDI48_NGPIO] = { + "Bit 0 A", "Bit 1 A", "Bit 2 A", "Bit 3 A", "Bit 4 A", "Bit 5 A", + "Bit 6 A", "Bit 7 A", "Bit 8 A", "Bit 9 A", "Bit 10 A", "Bit 11 A", + "Bit 12 A", "Bit 13 A", "Bit 14 A", "Bit 15 A", "Bit 16 A", "Bit 17 A", + "Bit 18 A", "Bit 19 A", "Bit 20 A", "Bit 21 A", "Bit 22 A", "Bit 23 A", + "Bit 0 B", "Bit 1 B", "Bit 2 B", "Bit 3 B", "Bit 4 B", "Bit 5 B", + "Bit 6 B", "Bit 7 B", "Bit 8 B", "Bit 9 B", "Bit 10 B", "Bit 11 B", + "Bit 12 B", "Bit 13 B", "Bit 14 B", "Bit 15 B", "Bit 16 B", "Bit 17 B", + "Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B" +}; + static int idi_48_probe(struct device *dev, unsigned int id) { struct idi_48_gpio *idi48gpio; @@ -239,19 +249,17 @@ static int idi_48_probe(struct device *dev, unsigned int id) idi48gpio->chip.parent = dev; idi48gpio->chip.owner = THIS_MODULE; idi48gpio->chip.base = -1; - idi48gpio->chip.ngpio = 48; + idi48gpio->chip.ngpio = IDI48_NGPIO; + idi48gpio->chip.names = idi48_names; idi48gpio->chip.get_direction = idi_48_gpio_get_direction; idi48gpio->chip.direction_input = idi_48_gpio_direction_input; idi48gpio->chip.get = idi_48_gpio_get; idi48gpio->base = base[id]; - idi48gpio->irq = irq[id]; spin_lock_init(&idi48gpio->lock); spin_lock_init(&idi48gpio->ack_lock); - dev_set_drvdata(dev, idi48gpio); - - err = gpiochip_add_data(&idi48gpio->chip, idi48gpio); + err = devm_gpiochip_add_data(dev, &idi48gpio->chip, idi48gpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); return err; @@ -265,31 +273,17 @@ static int idi_48_probe(struct device *dev, unsigned int id) handle_edge_irq, IRQ_TYPE_NONE); if (err) { dev_err(dev, "Could not add irqchip (%d)\n", err); - goto err_gpiochip_remove; + return err; } - err = request_irq(irq[id], idi_48_irq_handler, IRQF_SHARED, name, - idi48gpio); + err = devm_request_irq(dev, irq[id], idi_48_irq_handler, IRQF_SHARED, + name, idi48gpio); if (err) { dev_err(dev, "IRQ handler registering failed (%d)\n", err); - goto err_gpiochip_remove; + return err; } return 0; - -err_gpiochip_remove: - gpiochip_remove(&idi48gpio->chip); - return err; -} - -static int idi_48_remove(struct device *dev, unsigned int id) -{ - struct idi_48_gpio *const idi48gpio = dev_get_drvdata(dev); - - free_irq(idi48gpio->irq, idi48gpio); - gpiochip_remove(&idi48gpio->chip); - - return 0; } static struct isa_driver idi_48_driver = { @@ -297,7 +291,6 @@ static struct isa_driver idi_48_driver = { .driver = { .name = "104-idi-48" }, - .remove = idi_48_remove }; module_isa_driver(idi_48_driver, num_idi_48); diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 6787b8fcf0d8..7053cf736648 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -46,7 +46,6 @@ MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers"); * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts * @base: base port address of the GPIO device - * @irq: Interrupt line number * @out_state: output bits state */ struct idio_16_gpio { @@ -54,7 +53,6 @@ struct idio_16_gpio { spinlock_t lock; unsigned long irq_mask; unsigned base; - unsigned irq; unsigned out_state; }; @@ -116,6 +114,25 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value) spin_unlock_irqrestore(&idio16gpio->lock, flags); } +static void idio_16_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); + unsigned long flags; + + spin_lock_irqsave(&idio16gpio->lock, flags); + + idio16gpio->out_state &= ~*mask; + idio16gpio->out_state |= *mask & *bits; + + if (*mask & 0xFF) + outb(idio16gpio->out_state, idio16gpio->base); + if ((*mask >> 8) & 0xFF) + outb(idio16gpio->out_state >> 8, idio16gpio->base + 4); + + spin_unlock_irqrestore(&idio16gpio->lock, flags); +} + static void idio_16_irq_ack(struct irq_data *data) { } @@ -193,6 +210,14 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +#define IDIO_16_NGPIO 32 +static const char *idio_16_names[IDIO_16_NGPIO] = { + "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", + "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", + "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", + "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" +}; + static int idio_16_probe(struct device *dev, unsigned int id) { struct idio_16_gpio *idio16gpio; @@ -213,21 +238,20 @@ static int idio_16_probe(struct device *dev, unsigned int id) idio16gpio->chip.parent = dev; idio16gpio->chip.owner = THIS_MODULE; idio16gpio->chip.base = -1; - idio16gpio->chip.ngpio = 32; + idio16gpio->chip.ngpio = IDIO_16_NGPIO; + idio16gpio->chip.names = idio_16_names; idio16gpio->chip.get_direction = idio_16_gpio_get_direction; idio16gpio->chip.direction_input = idio_16_gpio_direction_input; idio16gpio->chip.direction_output = idio_16_gpio_direction_output; idio16gpio->chip.get = idio_16_gpio_get; idio16gpio->chip.set = idio_16_gpio_set; + idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; idio16gpio->base = base[id]; - idio16gpio->irq = irq[id]; idio16gpio->out_state = 0xFFFF; spin_lock_init(&idio16gpio->lock); - dev_set_drvdata(dev, idio16gpio); - - err = gpiochip_add_data(&idio16gpio->chip, idio16gpio); + err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); return err; @@ -241,30 +265,17 @@ static int idio_16_probe(struct device *dev, unsigned int id) handle_edge_irq, IRQ_TYPE_NONE); if (err) { dev_err(dev, "Could not add irqchip (%d)\n", err); - goto err_gpiochip_remove; + return err; } - err = request_irq(irq[id], idio_16_irq_handler, 0, name, idio16gpio); + err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name, + idio16gpio); if (err) { dev_err(dev, "IRQ handler registering failed (%d)\n", err); - goto err_gpiochip_remove; + return err; } return 0; - -err_gpiochip_remove: - gpiochip_remove(&idio16gpio->chip); - return err; -} - -static int idio_16_remove(struct device *dev, unsigned int id) -{ - struct idio_16_gpio *const idio16gpio = dev_get_drvdata(dev); - - free_irq(idio16gpio->irq, idio16gpio); - gpiochip_remove(&idio16gpio->chip); - - return 0; } static struct isa_driver idio_16_driver = { @@ -272,7 +283,6 @@ static struct isa_driver idio_16_driver = { .driver = { .name = "104-idio-16" }, - .remove = idio_16_remove }; module_isa_driver(idio_16_driver, num_idio_16); diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 9191056548fe..72f49d1e110d 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -43,25 +43,7 @@ typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq); #define MAX_LABEL_SIZE 20 static void __iomem *gpio_base; - -static struct davinci_gpio_regs __iomem *gpio2regs(unsigned gpio) -{ - void __iomem *ptr; - - if (gpio < 32 * 1) - ptr = gpio_base + 0x10; - else if (gpio < 32 * 2) - ptr = gpio_base + 0x38; - else if (gpio < 32 * 3) - ptr = gpio_base + 0x60; - else if (gpio < 32 * 4) - ptr = gpio_base + 0x88; - else if (gpio < 32 * 5) - ptr = gpio_base + 0xb0; - else - ptr = NULL; - return ptr; -} +static unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0}; static inline struct davinci_gpio_regs __iomem *irq2regs(struct irq_data *d) { @@ -81,11 +63,13 @@ static inline int __davinci_direction(struct gpio_chip *chip, unsigned offset, bool out, int value) { struct davinci_gpio_controller *d = gpiochip_get_data(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g; unsigned long flags; u32 temp; - u32 mask = 1 << offset; + int bank = offset / 32; + u32 mask = __gpio_mask(offset); + g = d->regs[bank]; spin_lock_irqsave(&d->lock, flags); temp = readl_relaxed(&g->dir); if (out) { @@ -121,9 +105,12 @@ davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value) static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset) { struct davinci_gpio_controller *d = gpiochip_get_data(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g; + int bank = offset / 32; - return !!((1 << offset) & readl_relaxed(&g->in_data)); + g = d->regs[bank]; + + return !!(__gpio_mask(offset) & readl_relaxed(&g->in_data)); } /* @@ -133,9 +120,13 @@ static void davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct davinci_gpio_controller *d = gpiochip_get_data(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g; + int bank = offset / 32; - writel_relaxed((1 << offset), value ? &g->set_data : &g->clr_data); + g = d->regs[bank]; + + writel_relaxed(__gpio_mask(offset), + value ? &g->set_data : &g->clr_data); } static struct davinci_gpio_platform_data * @@ -172,34 +163,13 @@ of_err: return NULL; } -#ifdef CONFIG_OF_GPIO -static int davinci_gpio_of_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, - u32 *flags) -{ - struct davinci_gpio_controller *chips = dev_get_drvdata(gc->parent); - struct davinci_gpio_platform_data *pdata = dev_get_platdata(gc->parent); - - if (gpiospec->args[0] > pdata->ngpio) - return -EINVAL; - - if (gc != &chips[gpiospec->args[0] / 32].chip) - return -EINVAL; - - if (flags) - *flags = gpiospec->args[1]; - - return gpiospec->args[0] % 32; -} -#endif - static int davinci_gpio_probe(struct platform_device *pdev) { - int i, base; + static int ctrl_num, bank_base; + int gpio, bank; unsigned ngpio, nbank; struct davinci_gpio_controller *chips; struct davinci_gpio_platform_data *pdata; - struct davinci_gpio_regs __iomem *regs; struct device *dev = &pdev->dev; struct resource *res; char label[MAX_LABEL_SIZE]; @@ -238,41 +208,31 @@ static int davinci_gpio_probe(struct platform_device *pdev) if (IS_ERR(gpio_base)) return PTR_ERR(gpio_base); - for (i = 0, base = 0; base < ngpio; i++, base += 32) { - snprintf(label, MAX_LABEL_SIZE, "davinci_gpio.%d", i); - chips[i].chip.label = devm_kstrdup(dev, label, GFP_KERNEL); - if (!chips[i].chip.label) + snprintf(label, MAX_LABEL_SIZE, "davinci_gpio.%d", ctrl_num++); + chips->chip.label = devm_kstrdup(dev, label, GFP_KERNEL); + if (!chips->chip.label) return -ENOMEM; - chips[i].chip.direction_input = davinci_direction_in; - chips[i].chip.get = davinci_gpio_get; - chips[i].chip.direction_output = davinci_direction_out; - chips[i].chip.set = davinci_gpio_set; + chips->chip.direction_input = davinci_direction_in; + chips->chip.get = davinci_gpio_get; + chips->chip.direction_output = davinci_direction_out; + chips->chip.set = davinci_gpio_set; - chips[i].chip.base = base; - chips[i].chip.ngpio = ngpio - base; - if (chips[i].chip.ngpio > 32) - chips[i].chip.ngpio = 32; + chips->chip.ngpio = ngpio; + chips->chip.base = bank_base; #ifdef CONFIG_OF_GPIO - chips[i].chip.of_gpio_n_cells = 2; - chips[i].chip.of_xlate = davinci_gpio_of_xlate; - chips[i].chip.parent = dev; - chips[i].chip.of_node = dev->of_node; + chips->chip.of_gpio_n_cells = 2; + chips->chip.parent = dev; + chips->chip.of_node = dev->of_node; #endif - spin_lock_init(&chips[i].lock); - - regs = gpio2regs(base); - if (!regs) - return -ENXIO; - chips[i].regs = regs; - chips[i].set_data = ®s->set_data; - chips[i].clr_data = ®s->clr_data; - chips[i].in_data = ®s->in_data; + spin_lock_init(&chips->lock); + bank_base += ngpio; - gpiochip_add_data(&chips[i].chip, &chips[i]); - } + for (gpio = 0, bank = 0; gpio < ngpio; gpio += 32, bank++) + chips->regs[bank] = gpio_base + offset_array[bank]; + gpiochip_add_data(&chips->chip, chips); platform_set_drvdata(pdev, chips); davinci_gpio_irq_setup(pdev); return 0; @@ -333,16 +293,19 @@ static struct irq_chip gpio_irqchip = { static void gpio_irq_handler(struct irq_desc *desc) { - unsigned int irq = irq_desc_get_irq(desc); struct davinci_gpio_regs __iomem *g; u32 mask = 0xffff; + int bank_num; struct davinci_gpio_controller *d; + struct davinci_gpio_irq_data *irqdata; - d = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc); - g = (struct davinci_gpio_regs __iomem *)d->regs; + irqdata = (struct davinci_gpio_irq_data *)irq_desc_get_handler_data(desc); + bank_num = irqdata->bank_num; + g = irqdata->regs; + d = irqdata->chip; /* we only care about one bank */ - if (irq & 1) + if ((bank_num % 2) == 1) mask <<= 16; /* temporarily mask (level sensitive) parent IRQ */ @@ -350,6 +313,7 @@ static void gpio_irq_handler(struct irq_desc *desc) while (1) { u32 status; int bit; + irq_hw_number_t hw_irq; /* ack any irqs */ status = readl_relaxed(&g->intstat) & mask; @@ -362,9 +326,13 @@ static void gpio_irq_handler(struct irq_desc *desc) while (status) { bit = __ffs(status); status &= ~BIT(bit); + /* Max number of gpios per controller is 144 so + * hw_irq will be in [0..143] + */ + hw_irq = (bank_num / 2) * 32 + bit; + generic_handle_irq( - irq_find_mapping(d->irq_domain, - d->chip.base + bit)); + irq_find_mapping(d->irq_domain, hw_irq)); } } chained_irq_exit(irq_desc_get_chip(desc), desc); @@ -376,7 +344,7 @@ static int gpio_to_irq_banked(struct gpio_chip *chip, unsigned offset) struct davinci_gpio_controller *d = gpiochip_get_data(chip); if (d->irq_domain) - return irq_create_mapping(d->irq_domain, d->chip.base + offset); + return irq_create_mapping(d->irq_domain, offset); else return -ENXIO; } @@ -390,7 +358,7 @@ static int gpio_to_irq_unbanked(struct gpio_chip *chip, unsigned offset) * can provide direct-mapped IRQs to AINTC (up to 32 GPIOs). */ if (offset < d->gpio_unbanked) - return d->gpio_irq + offset; + return d->base_irq + offset; else return -ENODEV; } @@ -403,7 +371,7 @@ static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger) d = (struct davinci_gpio_controller *)irq_data_get_irq_handler_data(data); g = (struct davinci_gpio_regs __iomem *)d->regs; - mask = __gpio_mask(data->irq - d->gpio_irq); + mask = __gpio_mask(data->irq - d->base_irq); if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; @@ -420,7 +388,9 @@ static int davinci_gpio_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { - struct davinci_gpio_regs __iomem *g = gpio2regs(hw); + struct davinci_gpio_controller *chips = + (struct davinci_gpio_controller *)d->host_data; + struct davinci_gpio_regs __iomem *g = chips->regs[hw / 32]; irq_set_chip_and_handler_name(irq, &gpio_irqchip, handle_simple_irq, "davinci_gpio"); @@ -478,6 +448,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) struct irq_domain *irq_domain = NULL; const struct of_device_id *match; struct irq_chip *irq_chip; + struct davinci_gpio_irq_data *irqdata; gpio_get_irq_chip_cb_t gpio_get_irq_chip; /* @@ -533,10 +504,8 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) * IRQs, while the others use banked IRQs, would need some setup * tweaks to recognize hardware which can do that. */ - for (gpio = 0, bank = 0; gpio < ngpio; bank++, gpio += 32) { - chips[bank].chip.to_irq = gpio_to_irq_banked; - chips[bank].irq_domain = irq_domain; - } + chips->chip.to_irq = gpio_to_irq_banked; + chips->irq_domain = irq_domain; /* * AINTC can handle direct/unbanked IRQs for GPIOs, with the GPIO @@ -545,9 +514,9 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) */ if (pdata->gpio_unbanked) { /* pass "bank 0" GPIO IRQs to AINTC */ - chips[0].chip.to_irq = gpio_to_irq_unbanked; - chips[0].gpio_irq = bank_irq; - chips[0].gpio_unbanked = pdata->gpio_unbanked; + chips->chip.to_irq = gpio_to_irq_unbanked; + chips->base_irq = bank_irq; + chips->gpio_unbanked = pdata->gpio_unbanked; binten = GENMASK(pdata->gpio_unbanked / 16, 0); /* AINTC handles mask/unmask; GPIO handles triggering */ @@ -557,14 +526,14 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) irq_chip->irq_set_type = gpio_irq_type_unbanked; /* default trigger: both edges */ - g = gpio2regs(0); + g = chips->regs[0]; writel_relaxed(~0, &g->set_falling); writel_relaxed(~0, &g->set_rising); /* set the direct IRQs up to use that irqchip */ for (gpio = 0; gpio < pdata->gpio_unbanked; gpio++, irq++) { irq_set_chip(irq, irq_chip); - irq_set_handler_data(irq, &chips[gpio / 32]); + irq_set_handler_data(irq, chips); irq_set_status_flags(irq, IRQ_TYPE_EDGE_BOTH); } @@ -576,8 +545,11 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) * then chain through our own handler. */ for (gpio = 0, bank = 0; gpio < ngpio; bank++, bank_irq++, gpio += 16) { - /* disabled by default, enabled only as needed */ - g = gpio2regs(gpio); + /* disabled by default, enabled only as needed + * There are register sets for 32 GPIOs. 2 banks of 16 + * GPIOs are covered by each set of registers hence divide by 2 + */ + g = chips->regs[bank / 2]; writel_relaxed(~0, &g->clr_falling); writel_relaxed(~0, &g->clr_rising); @@ -586,8 +558,19 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) * gpio irqs. Pass the irq bank's corresponding controller to * the chained irq handler. */ + irqdata = devm_kzalloc(&pdev->dev, + sizeof(struct + davinci_gpio_irq_data), + GFP_KERNEL); + if (!irqdata) + return -ENOMEM; + + irqdata->regs = g; + irqdata->bank_num = bank; + irqdata->chip = chips; + irq_set_chained_handler_and_data(bank_irq, gpio_irq_handler, - &chips[gpio / 32]); + irqdata); binten |= BIT(bank); } diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c new file mode 100644 index 000000000000..05c8946d6446 --- /dev/null +++ b/drivers/gpio/gpio-exar.c @@ -0,0 +1,200 @@ +/* + * GPIO driver for Exar XR17V35X chip + * + * Copyright (C) 2015 Sudip Mukherjee <sudip.mukherjee@codethink.co.uk> + * + * 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/bitops.h> +#include <linux/device.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +#define EXAR_OFFSET_MPIOLVL_LO 0x90 +#define EXAR_OFFSET_MPIOSEL_LO 0x93 +#define EXAR_OFFSET_MPIOLVL_HI 0x96 +#define EXAR_OFFSET_MPIOSEL_HI 0x99 + +#define DRIVER_NAME "gpio_exar" + +static DEFINE_IDA(ida_index); + +struct exar_gpio_chip { + struct gpio_chip gpio_chip; + struct mutex lock; + int index; + void __iomem *regs; + char name[20]; +}; + +static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, + unsigned int offset) +{ + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + int temp; + + mutex_lock(&exar_gpio->lock); + temp = readb(exar_gpio->regs + reg); + temp &= ~BIT(offset); + if (val) + temp |= BIT(offset); + writeb(temp, exar_gpio->regs + reg); + mutex_unlock(&exar_gpio->lock); +} + +static int exar_set_direction(struct gpio_chip *chip, int direction, + unsigned int offset) +{ + unsigned int bank = offset / 8; + unsigned int addr; + + addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + exar_update(chip, addr, direction, offset % 8); + return 0; +} + +static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + return exar_set_direction(chip, 0, offset); +} + +static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + return exar_set_direction(chip, 1, offset); +} + +static int exar_get(struct gpio_chip *chip, unsigned int reg) +{ + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + int value; + + mutex_lock(&exar_gpio->lock); + value = readb(exar_gpio->regs + reg); + mutex_unlock(&exar_gpio->lock); + + return !!value; +} + +static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + unsigned int bank = offset / 8; + unsigned int addr; + int val; + + addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + val = exar_get(chip, addr) >> (offset % 8); + + return !!val; +} + +static int exar_get_value(struct gpio_chip *chip, unsigned int offset) +{ + unsigned int bank = offset / 8; + unsigned int addr; + int val; + + addr = bank ? EXAR_OFFSET_MPIOLVL_LO : EXAR_OFFSET_MPIOLVL_HI; + val = exar_get(chip, addr) >> (offset % 8); + + return !!val; +} + +static void exar_set_value(struct gpio_chip *chip, unsigned int offset, + int value) +{ + unsigned int bank = offset / 8; + unsigned int addr; + + addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + exar_update(chip, addr, value, offset % 8); +} + +static int gpio_exar_probe(struct platform_device *pdev) +{ + struct pci_dev *pcidev = platform_get_drvdata(pdev); + struct exar_gpio_chip *exar_gpio; + void __iomem *p; + int index, ret; + + if (pcidev->vendor != PCI_VENDOR_ID_EXAR) + return -ENODEV; + + /* + * Map the pci device to get the register addresses. + * We will need to read and write those registers to control + * the GPIO pins. + * Using managed functions will save us from unmaping on exit. + * As the device is enabled using managed functions by the + * UART driver we can also use managed functions here. + */ + p = pcim_iomap(pcidev, 0, 0); + if (!p) + return -ENOMEM; + + exar_gpio = devm_kzalloc(&pcidev->dev, sizeof(*exar_gpio), GFP_KERNEL); + if (!exar_gpio) + return -ENOMEM; + + mutex_init(&exar_gpio->lock); + + index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL); + + sprintf(exar_gpio->name, "exar_gpio%d", index); + exar_gpio->gpio_chip.label = exar_gpio->name; + exar_gpio->gpio_chip.parent = &pcidev->dev; + exar_gpio->gpio_chip.direction_output = exar_direction_output; + exar_gpio->gpio_chip.direction_input = exar_direction_input; + exar_gpio->gpio_chip.get_direction = exar_get_direction; + exar_gpio->gpio_chip.get = exar_get_value; + exar_gpio->gpio_chip.set = exar_set_value; + exar_gpio->gpio_chip.base = -1; + exar_gpio->gpio_chip.ngpio = 16; + exar_gpio->regs = p; + exar_gpio->index = index; + + ret = devm_gpiochip_add_data(&pcidev->dev, + &exar_gpio->gpio_chip, exar_gpio); + if (ret) + goto err_destroy; + + platform_set_drvdata(pdev, exar_gpio); + + return 0; + +err_destroy: + ida_simple_remove(&ida_index, index); + mutex_destroy(&exar_gpio->lock); + return ret; +} + +static int gpio_exar_remove(struct platform_device *pdev) +{ + struct exar_gpio_chip *exar_gpio = platform_get_drvdata(pdev); + + ida_simple_remove(&ida_index, exar_gpio->index); + mutex_destroy(&exar_gpio->lock); + + return 0; +} + +static struct platform_driver gpio_exar_driver = { + .probe = gpio_exar_probe, + .remove = gpio_exar_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +module_platform_driver(gpio_exar_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_DESCRIPTION("Exar GPIO driver"); +MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-gemini.c b/drivers/gpio/gpio-gemini.c new file mode 100644 index 000000000000..962485163b7f --- /dev/null +++ b/drivers/gpio/gpio-gemini.c @@ -0,0 +1,236 @@ +/* + * Gemini gpiochip and interrupt routines + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * + * Based on arch/arm/mach-gemini/gpio.c: + * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> + * + * Based on plat-mxc/gpio.c: + * MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de> + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + */ +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of_gpio.h> +#include <linux/bitops.h> + +/* GPIO registers definition */ +#define GPIO_DATA_OUT 0x00 +#define GPIO_DATA_IN 0x04 +#define GPIO_DIR 0x08 +#define GPIO_DATA_SET 0x10 +#define GPIO_DATA_CLR 0x14 +#define GPIO_PULL_EN 0x18 +#define GPIO_PULL_TYPE 0x1C +#define GPIO_INT_EN 0x20 +#define GPIO_INT_STAT 0x24 +#define GPIO_INT_MASK 0x2C +#define GPIO_INT_CLR 0x30 +#define GPIO_INT_TYPE 0x34 +#define GPIO_INT_BOTH_EDGE 0x38 +#define GPIO_INT_LEVEL 0x3C +#define GPIO_DEBOUNCE_EN 0x40 +#define GPIO_DEBOUNCE_PRESCALE 0x44 + +/** + * struct gemini_gpio - Gemini GPIO state container + * @dev: containing device for this instance + * @gc: gpiochip for this instance + */ +struct gemini_gpio { + struct device *dev; + struct gpio_chip gc; + void __iomem *base; +}; + +static void gemini_gpio_ack_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + + writel(BIT(irqd_to_hwirq(d)), g->base + GPIO_INT_CLR); +} + +static void gemini_gpio_mask_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + u32 val; + + val = readl(g->base + GPIO_INT_EN); + val &= ~BIT(irqd_to_hwirq(d)); + writel(val, g->base + GPIO_INT_EN); +} + +static void gemini_gpio_unmask_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + u32 val; + + val = readl(g->base + GPIO_INT_EN); + val |= BIT(irqd_to_hwirq(d)); + writel(val, g->base + GPIO_INT_EN); +} + +static int gemini_gpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + u32 mask = BIT(irqd_to_hwirq(d)); + u32 reg_both, reg_level, reg_type; + + reg_type = readl(g->base + GPIO_INT_TYPE); + reg_level = readl(g->base + GPIO_INT_LEVEL); + reg_both = readl(g->base + GPIO_INT_BOTH_EDGE); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + irq_set_handler_locked(d, handle_edge_irq); + reg_type &= ~mask; + reg_both |= mask; + break; + case IRQ_TYPE_EDGE_RISING: + irq_set_handler_locked(d, handle_edge_irq); + reg_type &= ~mask; + reg_both &= ~mask; + reg_level &= ~mask; + break; + case IRQ_TYPE_EDGE_FALLING: + irq_set_handler_locked(d, handle_edge_irq); + reg_type &= ~mask; + reg_both &= ~mask; + reg_level |= mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + irq_set_handler_locked(d, handle_level_irq); + reg_type |= mask; + reg_level &= ~mask; + break; + case IRQ_TYPE_LEVEL_LOW: + irq_set_handler_locked(d, handle_level_irq); + reg_type |= mask; + reg_level |= mask; + break; + default: + irq_set_handler_locked(d, handle_bad_irq); + return -EINVAL; + } + + writel(reg_type, g->base + GPIO_INT_TYPE); + writel(reg_level, g->base + GPIO_INT_LEVEL); + writel(reg_both, g->base + GPIO_INT_BOTH_EDGE); + + gemini_gpio_ack_irq(d); + + return 0; +} + +static struct irq_chip gemini_gpio_irqchip = { + .name = "GPIO", + .irq_ack = gemini_gpio_ack_irq, + .irq_mask = gemini_gpio_mask_irq, + .irq_unmask = gemini_gpio_unmask_irq, + .irq_set_type = gemini_gpio_set_irq_type, +}; + +static void gemini_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct gemini_gpio *g = gpiochip_get_data(gc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + int offset; + unsigned long stat; + + chained_irq_enter(irqchip, desc); + + stat = readl(g->base + GPIO_INT_STAT); + if (stat) + for_each_set_bit(offset, &stat, gc->ngpio) + generic_handle_irq(irq_find_mapping(gc->irqdomain, + offset)); + + chained_irq_exit(irqchip, desc); +} + +static int gemini_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct gemini_gpio *g; + int irq; + int ret; + + g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); + if (!g) + return -ENOMEM; + + g->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + g->base = devm_ioremap_resource(dev, res); + if (IS_ERR(g->base)) + return PTR_ERR(g->base); + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + ret = bgpio_init(&g->gc, dev, 4, + g->base + GPIO_DATA_IN, + g->base + GPIO_DATA_SET, + g->base + GPIO_DATA_CLR, + g->base + GPIO_DIR, + NULL, + 0); + if (ret) { + dev_err(dev, "unable to init generic GPIO\n"); + return ret; + } + g->gc.label = "Gemini"; + g->gc.base = -1; + g->gc.parent = dev; + g->gc.owner = THIS_MODULE; + /* ngpio is set by bgpio_init() */ + + ret = devm_gpiochip_add_data(dev, &g->gc, g); + if (ret) + return ret; + + /* Disable, unmask and clear all interrupts */ + writel(0x0, g->base + GPIO_INT_EN); + writel(0x0, g->base + GPIO_INT_MASK); + writel(~0x0, g->base + GPIO_INT_CLR); + + ret = gpiochip_irqchip_add(&g->gc, &gemini_gpio_irqchip, + 0, handle_bad_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_info(dev, "could not add irqchip\n"); + return ret; + } + gpiochip_set_chained_irqchip(&g->gc, &gemini_gpio_irqchip, + irq, gemini_gpio_irq_handler); + + dev_info(dev, "Gemini GPIO @%p registered\n", g->base); + + return 0; +} + +static const struct of_device_id gemini_gpio_of_match[] = { + { + .compatible = "cortina,gemini-gpio", + }, + {}, +}; + +static struct platform_driver gemini_gpio_driver = { + .driver = { + .name = "gemini-gpio", + .of_match_table = of_match_ptr(gemini_gpio_of_match), + }, + .probe = gemini_gpio_probe, +}; +builtin_platform_driver(gemini_gpio_driver); diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index 1e7def9449ce..fa4baa2543db 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -192,6 +192,56 @@ static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset, spin_unlock_irqrestore(&gpiommgpio->lock, flags); } +static void gpiomm_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); + unsigned int i; + const unsigned int gpio_reg_size = 8; + unsigned int port; + unsigned int out_port; + unsigned int bitmask; + unsigned long flags; + + /* set bits are evaluated a gpio register size at a time */ + for (i = 0; i < chip->ngpio; i += gpio_reg_size) { + /* no more set bits in this mask word; skip to the next word */ + if (!mask[BIT_WORD(i)]) { + i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size; + continue; + } + + port = i / gpio_reg_size; + out_port = (port > 2) ? port + 1 : port; + bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)]; + + spin_lock_irqsave(&gpiommgpio->lock, flags); + + /* update output state data and set device gpio register */ + gpiommgpio->out_state[port] &= ~mask[BIT_WORD(i)]; + gpiommgpio->out_state[port] |= bitmask; + outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port); + + spin_unlock_irqrestore(&gpiommgpio->lock, flags); + + /* prepare for next gpio register set */ + mask[BIT_WORD(i)] >>= gpio_reg_size; + bits[BIT_WORD(i)] >>= gpio_reg_size; + } +} + +#define GPIOMM_NGPIO 48 +static const char *gpiomm_names[GPIOMM_NGPIO] = { + "Port 1A0", "Port 1A1", "Port 1A2", "Port 1A3", "Port 1A4", "Port 1A5", + "Port 1A6", "Port 1A7", "Port 1B0", "Port 1B1", "Port 1B2", "Port 1B3", + "Port 1B4", "Port 1B5", "Port 1B6", "Port 1B7", "Port 1C0", "Port 1C1", + "Port 1C2", "Port 1C3", "Port 1C4", "Port 1C5", "Port 1C6", "Port 1C7", + "Port 2A0", "Port 2A1", "Port 2A2", "Port 2A3", "Port 2A4", "Port 2A5", + "Port 2A6", "Port 2A7", "Port 2B0", "Port 2B1", "Port 2B2", "Port 2B3", + "Port 2B4", "Port 2B5", "Port 2B6", "Port 2B7", "Port 2C0", "Port 2C1", + "Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7", +}; + static int gpiomm_probe(struct device *dev, unsigned int id) { struct gpiomm_gpio *gpiommgpio; @@ -212,19 +262,19 @@ static int gpiomm_probe(struct device *dev, unsigned int id) gpiommgpio->chip.parent = dev; gpiommgpio->chip.owner = THIS_MODULE; gpiommgpio->chip.base = -1; - gpiommgpio->chip.ngpio = 48; + gpiommgpio->chip.ngpio = GPIOMM_NGPIO; + gpiommgpio->chip.names = gpiomm_names; gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction; gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input; gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output; gpiommgpio->chip.get = gpiomm_gpio_get; gpiommgpio->chip.set = gpiomm_gpio_set; + gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple; gpiommgpio->base = base[id]; spin_lock_init(&gpiommgpio->lock); - dev_set_drvdata(dev, gpiommgpio); - - err = gpiochip_add_data(&gpiommgpio->chip, gpiommgpio); + err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); return err; @@ -243,21 +293,11 @@ static int gpiomm_probe(struct device *dev, unsigned int id) return 0; } -static int gpiomm_remove(struct device *dev, unsigned int id) -{ - struct gpiomm_gpio *const gpiommgpio = dev_get_drvdata(dev); - - gpiochip_remove(&gpiommgpio->chip); - - return 0; -} - static struct isa_driver gpiomm_driver = { .probe = gpiomm_probe, .driver = { .name = "gpio-mm" }, - .remove = gpiomm_remove }; module_isa_driver(gpiomm_driver, num_gpiomm); diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c index a1e44c221f66..b76ecee82c3f 100644 --- a/drivers/gpio/gpio-intel-mid.c +++ b/drivers/gpio/gpio-intel-mid.c @@ -321,7 +321,7 @@ static void intel_mid_irq_init_hw(struct intel_mid_gpio *priv) } } -static int intel_gpio_runtime_idle(struct device *dev) +static int __maybe_unused intel_gpio_runtime_idle(struct device *dev) { int err = pm_schedule_suspend(dev, 500); return err ?: -EBUSY; diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index 504550665091..bdb692345428 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/of_irq.h> #include <linux/of_device.h> +#include <linux/regmap.h> /** * MCP types supported by driver @@ -58,16 +59,10 @@ struct mcp23s08; -struct mcp23s08_ops { - int (*read)(struct mcp23s08 *mcp, unsigned reg); - int (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val); - int (*read_regs)(struct mcp23s08 *mcp, unsigned reg, - u16 *vals, unsigned n); -}; - struct mcp23s08 { u8 addr; bool irq_active_high; + bool reg_shift; u16 cache[11]; u16 irq_rise; @@ -80,188 +75,126 @@ struct mcp23s08 { struct gpio_chip chip; - const struct mcp23s08_ops *ops; - void *data; /* ops specific data */ + struct regmap *regmap; + struct device *dev; }; -/* A given spi_device can represent up to eight mcp23sxx chips - * sharing the same chipselect but using different addresses - * (e.g. chips #0 and #3 might be populated, but not #1 or $2). - * Driver data holds all the per-chip data. - */ -struct mcp23s08_driver_data { - unsigned ngpio; - struct mcp23s08 *mcp[8]; - struct mcp23s08 chip[]; -}; +static const struct regmap_config mcp23x08_regmap = { + .reg_bits = 8, + .val_bits = 8, -/*----------------------------------------------------------------------*/ - -#if IS_ENABLED(CONFIG_I2C) - -static int mcp23008_read(struct mcp23s08 *mcp, unsigned reg) -{ - return i2c_smbus_read_byte_data(mcp->data, reg); -} - -static int mcp23008_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) -{ - return i2c_smbus_write_byte_data(mcp->data, reg, val); -} - -static int -mcp23008_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) -{ - while (n--) { - int ret = mcp23008_read(mcp, reg++); - if (ret < 0) - return ret; - *vals++ = ret; - } - - return 0; -} - -static int mcp23017_read(struct mcp23s08 *mcp, unsigned reg) -{ - return i2c_smbus_read_word_data(mcp->data, reg << 1); -} - -static int mcp23017_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) -{ - return i2c_smbus_write_word_data(mcp->data, reg << 1, val); -} - -static int -mcp23017_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) -{ - while (n--) { - int ret = mcp23017_read(mcp, reg++); - if (ret < 0) - return ret; - *vals++ = ret; - } - - return 0; -} - -static const struct mcp23s08_ops mcp23008_ops = { - .read = mcp23008_read, - .write = mcp23008_write, - .read_regs = mcp23008_read_regs, + .reg_stride = 1, + .max_register = MCP_OLAT, }; -static const struct mcp23s08_ops mcp23017_ops = { - .read = mcp23017_read, - .write = mcp23017_write, - .read_regs = mcp23017_read_regs, -}; +static const struct regmap_config mcp23x17_regmap = { + .reg_bits = 8, + .val_bits = 16, -#endif /* CONFIG_I2C */ + .reg_stride = 2, + .max_register = MCP_OLAT << 1, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; /*----------------------------------------------------------------------*/ #ifdef CONFIG_SPI_MASTER -static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) +static int mcp23sxx_spi_write(void *context, const void *data, size_t count) { - u8 tx[2], rx[1]; - int status; + struct mcp23s08 *mcp = context; + struct spi_device *spi = to_spi_device(mcp->dev); + struct spi_message m; + struct spi_transfer t[2] = { { .tx_buf = &mcp->addr, .len = 1, }, + { .tx_buf = data, .len = count, }, }; - tx[0] = mcp->addr | 0x01; - tx[1] = reg; - status = spi_write_then_read(mcp->data, tx, sizeof(tx), rx, sizeof(rx)); - return (status < 0) ? status : rx[0]; + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + return spi_sync(spi, &m); } -static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) +static int mcp23sxx_spi_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) { - u8 tx[3]; + struct mcp23s08 *mcp = context; + struct spi_device *spi = to_spi_device(mcp->dev); + struct spi_message m; + struct spi_transfer t[3] = { { .tx_buf = &mcp->addr, .len = 1, }, + { .tx_buf = reg, .len = reg_size, }, + { .tx_buf = val, .len = val_size, }, }; - tx[0] = mcp->addr; - tx[1] = reg; - tx[2] = val; - return spi_write_then_read(mcp->data, tx, sizeof(tx), NULL, 0); + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + spi_message_add_tail(&t[2], &m); + + return spi_sync(spi, &m); } -static int -mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) +static int mcp23sxx_spi_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) { - u8 tx[2], *tmp; - int status; + struct mcp23s08 *mcp = context; + struct spi_device *spi = to_spi_device(mcp->dev); + u8 tx[2]; - if ((n + reg) > sizeof(mcp->cache)) + if (reg_size != 1) return -EINVAL; + tx[0] = mcp->addr | 0x01; - tx[1] = reg; + tx[1] = *((u8 *) reg); - tmp = (u8 *)vals; - status = spi_write_then_read(mcp->data, tx, sizeof(tx), tmp, n); - if (status >= 0) { - while (n--) - vals[n] = tmp[n]; /* expand to 16bit */ - } - return status; + return spi_write_then_read(spi, tx, sizeof(tx), val, val_size); } -static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg) -{ - u8 tx[2], rx[2]; - int status; +static const struct regmap_bus mcp23sxx_spi_regmap = { + .write = mcp23sxx_spi_write, + .gather_write = mcp23sxx_spi_gather_write, + .read = mcp23sxx_spi_read, +}; - tx[0] = mcp->addr | 0x01; - tx[1] = reg << 1; - status = spi_write_then_read(mcp->data, tx, sizeof(tx), rx, sizeof(rx)); - return (status < 0) ? status : (rx[0] | (rx[1] << 8)); -} +#endif /* CONFIG_SPI_MASTER */ -static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val) +static int mcp_read(struct mcp23s08 *mcp, unsigned int reg, unsigned int *val) { - u8 tx[4]; - - tx[0] = mcp->addr; - tx[1] = reg << 1; - tx[2] = val; - tx[3] = val >> 8; - return spi_write_then_read(mcp->data, tx, sizeof(tx), NULL, 0); + return regmap_read(mcp->regmap, reg << mcp->reg_shift, val); } -static int -mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n) +static int mcp_write(struct mcp23s08 *mcp, unsigned int reg, unsigned int val) { - u8 tx[2]; - int status; + return regmap_write(mcp->regmap, reg << mcp->reg_shift, val); +} - if ((n + reg) > sizeof(mcp->cache)) - return -EINVAL; - tx[0] = mcp->addr | 0x01; - tx[1] = reg << 1; +static int mcp_update_cache(struct mcp23s08 *mcp) +{ + int ret, reg, i; - status = spi_write_then_read(mcp->data, tx, sizeof(tx), - (u8 *)vals, n * 2); - if (status >= 0) { - while (n--) - vals[n] = __le16_to_cpu((__le16)vals[n]); + for (i = 0; i < ARRAY_SIZE(mcp->cache); i++) { + ret = mcp_read(mcp, i, ®); + if (ret < 0) + return ret; + mcp->cache[i] = reg; } - return status; + return 0; } -static const struct mcp23s08_ops mcp23s08_ops = { - .read = mcp23s08_read, - .write = mcp23s08_write, - .read_regs = mcp23s08_read_regs, -}; +/*----------------------------------------------------------------------*/ -static const struct mcp23s08_ops mcp23s17_ops = { - .read = mcp23s17_read, - .write = mcp23s17_write, - .read_regs = mcp23s17_read_regs, +/* A given spi_device can represent up to eight mcp23sxx chips + * sharing the same chipselect but using different addresses + * (e.g. chips #0 and #3 might be populated, but not #1 or $2). + * Driver data holds all the per-chip data. + */ +struct mcp23s08_driver_data { + unsigned ngpio; + struct mcp23s08 *mcp[8]; + struct mcp23s08 chip[]; }; -#endif /* CONFIG_SPI_MASTER */ - -/*----------------------------------------------------------------------*/ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) { @@ -270,7 +203,7 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) mutex_lock(&mcp->lock); mcp->cache[MCP_IODIR] |= (1 << offset); - status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); + status = mcp_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); mutex_unlock(&mcp->lock); return status; } @@ -278,13 +211,13 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) { struct mcp23s08 *mcp = gpiochip_get_data(chip); - int status; + int status, ret; mutex_lock(&mcp->lock); /* REVISIT reading this clears any IRQ ... */ - status = mcp->ops->read(mcp, MCP_GPIO); - if (status < 0) + ret = mcp_read(mcp, MCP_GPIO, &status); + if (ret < 0) status = 0; else { mcp->cache[MCP_GPIO] = status; @@ -303,7 +236,7 @@ static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) else olat &= ~mask; mcp->cache[MCP_OLAT] = olat; - return mcp->ops->write(mcp, MCP_OLAT, olat); + return mcp_write(mcp, MCP_OLAT, olat); } static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) @@ -327,7 +260,7 @@ mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) status = __mcp23s08_set(mcp, mask, value); if (status == 0) { mcp->cache[MCP_IODIR] &= ~mask; - status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); + status = mcp_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); } mutex_unlock(&mcp->lock); return status; @@ -341,16 +274,14 @@ static irqreturn_t mcp23s08_irq(int irq, void *data) unsigned int child_irq; mutex_lock(&mcp->lock); - intf = mcp->ops->read(mcp, MCP_INTF); - if (intf < 0) { + if (mcp_read(mcp, MCP_INTF, &intf) < 0) { mutex_unlock(&mcp->lock); return IRQ_HANDLED; } mcp->cache[MCP_INTF] = intf; - intcap = mcp->ops->read(mcp, MCP_INTCAP); - if (intcap < 0) { + if (mcp_read(mcp, MCP_INTCAP, &intcap) < 0) { mutex_unlock(&mcp->lock); return IRQ_HANDLED; } @@ -435,9 +366,9 @@ static void mcp23s08_irq_bus_unlock(struct irq_data *data) struct mcp23s08 *mcp = gpiochip_get_data(gc); mutex_lock(&mcp->lock); - mcp->ops->write(mcp, MCP_GPINTEN, mcp->cache[MCP_GPINTEN]); - mcp->ops->write(mcp, MCP_DEFVAL, mcp->cache[MCP_DEFVAL]); - mcp->ops->write(mcp, MCP_INTCON, mcp->cache[MCP_INTCON]); + mcp_write(mcp, MCP_GPINTEN, mcp->cache[MCP_GPINTEN]); + mcp_write(mcp, MCP_DEFVAL, mcp->cache[MCP_DEFVAL]); + mcp_write(mcp, MCP_INTCON, mcp->cache[MCP_INTCON]); mutex_unlock(&mcp->lock); mutex_unlock(&mcp->irq_lock); } @@ -514,7 +445,7 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) bank = '0' + ((mcp->addr >> 1) & 0x7); mutex_lock(&mcp->lock); - t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); + t = mcp_update_cache(mcp); if (t < 0) { seq_printf(s, " I/O ERROR %d\n", t); goto done; @@ -549,12 +480,12 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, void *data, unsigned addr, unsigned type, struct mcp23s08_platform_data *pdata, int cs) { - int status; + int status, ret; bool mirror = false; mutex_init(&mcp->lock); - mcp->data = data; + mcp->dev = dev; mcp->addr = addr; mcp->irq_active_high = false; @@ -571,19 +502,25 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, switch (type) { #ifdef CONFIG_SPI_MASTER case MCP_TYPE_S08: - mcp->ops = &mcp23s08_ops; + mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, + &mcp23x08_regmap); + mcp->reg_shift = 0; mcp->chip.ngpio = 8; mcp->chip.label = "mcp23s08"; break; case MCP_TYPE_S17: - mcp->ops = &mcp23s17_ops; + mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, + &mcp23x17_regmap); + mcp->reg_shift = 1; mcp->chip.ngpio = 16; mcp->chip.label = "mcp23s17"; break; case MCP_TYPE_S18: - mcp->ops = &mcp23s17_ops; + mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, + &mcp23x17_regmap); + mcp->reg_shift = 1; mcp->chip.ngpio = 16; mcp->chip.label = "mcp23s18"; break; @@ -591,13 +528,15 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, #if IS_ENABLED(CONFIG_I2C) case MCP_TYPE_008: - mcp->ops = &mcp23008_ops; + mcp->regmap = devm_regmap_init_i2c(data, &mcp23x08_regmap); + mcp->reg_shift = 0; mcp->chip.ngpio = 8; mcp->chip.label = "mcp23008"; break; case MCP_TYPE_017: - mcp->ops = &mcp23017_ops; + mcp->regmap = devm_regmap_init_i2c(data, &mcp23x17_regmap); + mcp->reg_shift = 1; mcp->chip.ngpio = 16; mcp->chip.label = "mcp23017"; break; @@ -608,6 +547,9 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, return -EINVAL; } + if (IS_ERR(mcp->regmap)) + return PTR_ERR(mcp->regmap); + mcp->chip.base = pdata->base; mcp->chip.can_sleep = true; mcp->chip.parent = dev; @@ -617,8 +559,8 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, * and MCP_IOCON.HAEN = 1, so we work with all chips. */ - status = mcp->ops->read(mcp, MCP_IOCON); - if (status < 0) + ret = mcp_read(mcp, MCP_IOCON, &status); + if (ret < 0) goto fail; mcp->irq_controller = pdata->irq_controller; @@ -646,51 +588,49 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev, if (type == MCP_TYPE_S18) status |= IOCON_INTCC | (IOCON_INTCC << 8); - status = mcp->ops->write(mcp, MCP_IOCON, status); - if (status < 0) + ret = mcp_write(mcp, MCP_IOCON, status); + if (ret < 0) goto fail; } /* configure ~100K pullups */ - status = mcp->ops->write(mcp, MCP_GPPU, pdata->chip[cs].pullups); - if (status < 0) + ret = mcp_write(mcp, MCP_GPPU, pdata->chip[cs].pullups); + if (ret < 0) goto fail; - status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache)); - if (status < 0) + ret = mcp_update_cache(mcp); + if (ret < 0) goto fail; /* disable inverter on input */ if (mcp->cache[MCP_IPOL] != 0) { mcp->cache[MCP_IPOL] = 0; - status = mcp->ops->write(mcp, MCP_IPOL, 0); - if (status < 0) + ret = mcp_write(mcp, MCP_IPOL, 0); + if (ret < 0) goto fail; } /* disable irqs */ if (mcp->cache[MCP_GPINTEN] != 0) { mcp->cache[MCP_GPINTEN] = 0; - status = mcp->ops->write(mcp, MCP_GPINTEN, 0); - if (status < 0) + ret = mcp_write(mcp, MCP_GPINTEN, 0); + if (ret < 0) goto fail; } - status = gpiochip_add_data(&mcp->chip, mcp); - if (status < 0) + ret = gpiochip_add_data(&mcp->chip, mcp); + if (ret < 0) goto fail; if (mcp->irq && mcp->irq_controller) { - status = mcp23s08_irq_setup(mcp); - if (status) { + ret = mcp23s08_irq_setup(mcp); + if (ret) goto fail; - } } fail: - if (status < 0) - dev_dbg(dev, "can't setup chip %d, --> %d\n", - addr, status); - return status; + if (ret < 0) + dev_dbg(dev, "can't setup chip %d, --> %d\n", addr, ret); + return ret; } /*----------------------------------------------------------------------*/ diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index 54e5d8257d34..b1cf76dd84ba 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -3,7 +3,7 @@ * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * - * Copyright (C) 2012 John Crispin <blogic@openwrt.org> + * Copyright (C) 2012 John Crispin <john@phrozen.org> */ #include <linux/init.h> diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 1ef85b0c2b1f..06dac72cb69c 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -14,14 +14,23 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/gpio/driver.h> +#include <linux/gpio/consumer.h> #include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irq_work.h> +#include <linux/debugfs.h> +#include <linux/uaccess.h> -#define GPIO_NAME "gpio-mockup" -#define MAX_GC 10 +#include "gpiolib.h" -enum direction { - OUT, - IN +#define GPIO_MOCKUP_NAME "gpio-mockup" +#define GPIO_MOCKUP_MAX_GC 10 + +enum { + DIR_IN = 0, + DIR_OUT, }; /* @@ -29,150 +38,360 @@ enum direction { * @dir: Configures direction of gpio as "in" or "out", 0=in, 1=out * @value: Configures status of the gpio as 0(low) or 1(high) */ -struct gpio_pin_status { - enum direction dir; +struct gpio_mockup_line_status { + int dir; bool value; }; -struct mockup_gpio_controller { +struct gpio_mockup_irq_context { + struct irq_work work; + int irq; +}; + +struct gpio_mockup_chip { struct gpio_chip gc; - struct gpio_pin_status *stats; + struct gpio_mockup_line_status *lines; + struct gpio_mockup_irq_context irq_ctx; + struct dentry *dbg_dir; }; -static int gpio_mockup_ranges[MAX_GC << 1]; +struct gpio_mockup_dbgfs_private { + struct gpio_mockup_chip *chip; + struct gpio_desc *desc; + int offset; +}; + +static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1]; static int gpio_mockup_params_nr; module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); -const char pins_name_start = 'A'; +static bool gpio_mockup_named_lines; +module_param_named(gpio_mockup_named_lines, + gpio_mockup_named_lines, bool, 0400); + +static const char gpio_mockup_name_start = 'A'; +static struct dentry *gpio_mockup_dbg_dir; -static int mockup_gpio_get(struct gpio_chip *gc, unsigned int offset) +static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) { - struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); - return cntr->stats[offset].value; + return chip->lines[offset].value; } -static void mockup_gpio_set(struct gpio_chip *gc, unsigned int offset, +static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset, int value) { - struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); - cntr->stats[offset].value = !!value; + chip->lines[offset].value = !!value; } -static int mockup_gpio_dirout(struct gpio_chip *gc, unsigned int offset, +static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, int value) { - struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + gpio_mockup_set(gc, offset, value); + chip->lines[offset].dir = DIR_OUT; + + return 0; +} + +static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + chip->lines[offset].dir = DIR_IN; + + return 0; +} + +static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + return chip->lines[offset].dir; +} + +static int gpio_mockup_name_lines(struct device *dev, + struct gpio_mockup_chip *chip) +{ + struct gpio_chip *gc = &chip->gc; + char **names; + int i; + + names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL); + if (!names) + return -ENOMEM; + + for (i = 0; i < gc->ngpio; i++) { + names[i] = devm_kasprintf(dev, GFP_KERNEL, + "%s-%d", gc->label, i); + if (!names[i]) + return -ENOMEM; + } + + gc->names = (const char *const *)names; - mockup_gpio_set(gc, offset, value); - cntr->stats[offset].dir = OUT; return 0; } -static int mockup_gpio_dirin(struct gpio_chip *gc, unsigned int offset) +static int gpio_mockup_to_irq(struct gpio_chip *chip, unsigned int offset) { - struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + return chip->irq_base + offset; +} + +/* + * While we should generally support irqmask and irqunmask, this driver is + * for testing purposes only so we don't care. + */ +static void gpio_mockup_irqmask(struct irq_data *d) { } +static void gpio_mockup_irqunmask(struct irq_data *d) { } + +static struct irq_chip gpio_mockup_irqchip = { + .name = GPIO_MOCKUP_NAME, + .irq_mask = gpio_mockup_irqmask, + .irq_unmask = gpio_mockup_irqunmask, +}; + +static void gpio_mockup_handle_irq(struct irq_work *work) +{ + struct gpio_mockup_irq_context *irq_ctx; + + irq_ctx = container_of(work, struct gpio_mockup_irq_context, work); + handle_simple_irq(irq_to_desc(irq_ctx->irq)); +} + +static int gpio_mockup_irqchip_setup(struct device *dev, + struct gpio_mockup_chip *chip) +{ + struct gpio_chip *gc = &chip->gc; + int irq_base, i; + + irq_base = irq_alloc_descs(-1, 0, gc->ngpio, 0); + if (irq_base < 0) + return irq_base; + + gc->irq_base = irq_base; + gc->irqchip = &gpio_mockup_irqchip; + + for (i = 0; i < gc->ngpio; i++) { + irq_set_chip(irq_base + i, gc->irqchip); + irq_set_handler(irq_base + i, &handle_simple_irq); + irq_modify_status(irq_base + i, + IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); + } + + init_irq_work(&chip->irq_ctx.work, gpio_mockup_handle_irq); - cntr->stats[offset].dir = IN; return 0; } -static int mockup_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +static ssize_t gpio_mockup_event_write(struct file *file, + const char __user *usr_buf, + size_t size, loff_t *ppos) +{ + struct gpio_mockup_dbgfs_private *priv; + struct gpio_mockup_chip *chip; + struct seq_file *sfile; + struct gpio_desc *desc; + struct gpio_chip *gc; + int status, val; + char buf; + + sfile = file->private_data; + priv = sfile->private; + desc = priv->desc; + chip = priv->chip; + gc = &chip->gc; + + status = copy_from_user(&buf, usr_buf, 1); + if (status) + return status; + + if (buf == '0') + val = 0; + else if (buf == '1') + val = 1; + else + return -EINVAL; + + gpiod_set_value_cansleep(desc, val); + priv->chip->irq_ctx.irq = gc->irq_base + priv->offset; + irq_work_queue(&priv->chip->irq_ctx.work); + + return size; +} + +static int gpio_mockup_event_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, inode->i_private); +} + +static const struct file_operations gpio_mockup_event_ops = { + .owner = THIS_MODULE, + .open = gpio_mockup_event_open, + .write = gpio_mockup_event_write, + .llseek = no_llseek, +}; + +static void gpio_mockup_debugfs_setup(struct device *dev, + struct gpio_mockup_chip *chip) { - struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + struct gpio_mockup_dbgfs_private *priv; + struct dentry *evfile; + struct gpio_chip *gc; + char *name; + int i; + + gc = &chip->gc; - return cntr->stats[offset].dir; + chip->dbg_dir = debugfs_create_dir(gc->label, gpio_mockup_dbg_dir); + if (!chip->dbg_dir) + goto err; + + for (i = 0; i < gc->ngpio; i++) { + name = devm_kasprintf(dev, GFP_KERNEL, "%d", i); + if (!name) + goto err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + goto err; + + priv->chip = chip; + priv->offset = i; + priv->desc = &gc->gpiodev->descs[i]; + + evfile = debugfs_create_file(name, 0200, chip->dbg_dir, priv, + &gpio_mockup_event_ops); + if (!evfile) + goto err; + } + + return; + +err: + dev_err(dev, "error creating debugfs directory\n"); } -static int mockup_gpio_add(struct device *dev, - struct mockup_gpio_controller *cntr, +static int gpio_mockup_add(struct device *dev, + struct gpio_mockup_chip *chip, const char *name, int base, int ngpio) { + struct gpio_chip *gc = &chip->gc; int ret; - cntr->gc.base = base; - cntr->gc.ngpio = ngpio; - cntr->gc.label = name; - cntr->gc.owner = THIS_MODULE; - cntr->gc.parent = dev; - cntr->gc.get = mockup_gpio_get; - cntr->gc.set = mockup_gpio_set; - cntr->gc.direction_output = mockup_gpio_dirout; - cntr->gc.direction_input = mockup_gpio_dirin; - cntr->gc.get_direction = mockup_gpio_get_direction; - cntr->stats = devm_kzalloc(dev, sizeof(*cntr->stats) * cntr->gc.ngpio, + gc->base = base; + gc->ngpio = ngpio; + gc->label = name; + gc->owner = THIS_MODULE; + gc->parent = dev; + gc->get = gpio_mockup_get; + gc->set = gpio_mockup_set; + gc->direction_output = gpio_mockup_dirout; + gc->direction_input = gpio_mockup_dirin; + gc->get_direction = gpio_mockup_get_direction; + gc->to_irq = gpio_mockup_to_irq; + + chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio, GFP_KERNEL); - if (!cntr->stats) { - ret = -ENOMEM; - goto err; + if (!chip->lines) + return -ENOMEM; + + if (gpio_mockup_named_lines) { + ret = gpio_mockup_name_lines(dev, chip); + if (ret) + return ret; } - ret = devm_gpiochip_add_data(dev, &cntr->gc, cntr); + + ret = gpio_mockup_irqchip_setup(dev, chip); if (ret) - goto err; + return ret; + + ret = devm_gpiochip_add_data(dev, &chip->gc, chip); + if (ret) + return ret; + + if (gpio_mockup_dbg_dir) + gpio_mockup_debugfs_setup(dev, chip); - dev_info(dev, "gpio<%d..%d> add successful!", base, base + ngpio); return 0; -err: - dev_err(dev, "gpio<%d..%d> add failed!", base, base + ngpio); - return ret; } -static int mockup_gpio_probe(struct platform_device *pdev) +static int gpio_mockup_probe(struct platform_device *pdev) { + struct gpio_mockup_chip *chips; struct device *dev = &pdev->dev; - struct mockup_gpio_controller *cntr; - int ret; - int i; - int base; - int ngpio; - char chip_name[sizeof(GPIO_NAME) + 3]; + int ret, i, base, ngpio; + char *chip_name; if (gpio_mockup_params_nr < 2) return -EINVAL; - cntr = devm_kzalloc(dev, sizeof(*cntr) * (gpio_mockup_params_nr >> 1), - GFP_KERNEL); - if (!cntr) + chips = devm_kzalloc(dev, + sizeof(*chips) * (gpio_mockup_params_nr >> 1), + GFP_KERNEL); + if (!chips) return -ENOMEM; - platform_set_drvdata(pdev, cntr); + platform_set_drvdata(pdev, chips); for (i = 0; i < gpio_mockup_params_nr >> 1; i++) { base = gpio_mockup_ranges[i * 2]; + if (base == -1) ngpio = gpio_mockup_ranges[i * 2 + 1]; else ngpio = gpio_mockup_ranges[i * 2 + 1] - base; if (ngpio >= 0) { - sprintf(chip_name, "%s-%c", GPIO_NAME, - pins_name_start + i); - ret = mockup_gpio_add(dev, &cntr[i], + chip_name = devm_kasprintf(dev, GFP_KERNEL, + "%s-%c", GPIO_MOCKUP_NAME, + gpio_mockup_name_start + i); + if (!chip_name) + return -ENOMEM; + + ret = gpio_mockup_add(dev, &chips[i], chip_name, base, ngpio); } else { ret = -1; } + if (ret) { - if (base < 0) - dev_err(dev, "gpio<%d..%d> add failed\n", - base, ngpio); - else - dev_err(dev, "gpio<%d..%d> add failed\n", - base, base + ngpio); + dev_err(dev, "gpio<%d..%d> add failed\n", + base, base < 0 ? ngpio : base + ngpio); return ret; } + + dev_info(dev, "gpio<%d..%d> add successful!", + base, base + ngpio); } return 0; } -static struct platform_driver mockup_gpio_driver = { +static int gpio_mockup_remove(struct platform_device *pdev) +{ + struct gpio_mockup_chip *chips; + int i; + + chips = platform_get_drvdata(pdev); + + for (i = 0; i < gpio_mockup_params_nr >> 1; i++) + irq_free_descs(chips[i].gc.irq_base, chips[i].gc.ngpio); + + return 0; +} + +static struct platform_driver gpio_mockup_driver = { .driver = { - .name = GPIO_NAME, - }, - .probe = mockup_gpio_probe, + .name = GPIO_MOCKUP_NAME, + }, + .probe = gpio_mockup_probe, + .remove = gpio_mockup_remove, }; static struct platform_device *pdev; @@ -180,7 +399,12 @@ static int __init mock_device_init(void) { int err; - pdev = platform_device_alloc(GPIO_NAME, -1); + gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); + if (!gpio_mockup_dbg_dir) + pr_err("%s: error creating debugfs directory\n", + GPIO_MOCKUP_NAME); + + pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1); if (!pdev) return -ENOMEM; @@ -190,7 +414,7 @@ static int __init mock_device_init(void) return err; } - err = platform_driver_register(&mockup_gpio_driver); + err = platform_driver_register(&gpio_mockup_driver); if (err) { platform_device_unregister(pdev); return err; @@ -201,7 +425,8 @@ static int __init mock_device_init(void) static void __exit mock_device_exit(void) { - platform_driver_unregister(&mockup_gpio_driver); + debugfs_remove_recursive(gpio_mockup_dbg_dir); + platform_driver_unregister(&gpio_mockup_driver); platform_device_unregister(pdev); } diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 1ed6132b993c..a649556ac3ca 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -659,7 +659,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) match = of_match_device(mvebu_gpio_of_match, &pdev->dev); if (match) - soc_variant = (int) match->data; + soc_variant = (unsigned long) match->data; else soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index d5d72d84b719..d44232aadb6c 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/platform_data/pca953x.h> @@ -754,8 +755,16 @@ static int pca953x_probe(struct i2c_client *client, invert = pdata->invert; chip->names = pdata->names; } else { + struct gpio_desc *reset_gpio; + chip->gpio_start = -1; irq_base = 0; + + /* See if we need to de-assert a reset pin */ + reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); } chip->client = client; diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c new file mode 100644 index 000000000000..269ab628634b --- /dev/null +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -0,0 +1,349 @@ +/* + * GPIO driver for the ACCES PCI-IDIO-16 + * Copyright (C) 2017 William Breathitt Gray + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irqdesc.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +/** + * struct idio_16_gpio_reg - GPIO device registers structure + * @out0_7: Read: FET Drive Outputs 0-7 + * Write: FET Drive Outputs 0-7 + * @in0_7: Read: Isolated Inputs 0-7 + * Write: Clear Interrupt + * @irq_ctl: Read: Enable IRQ + * Write: Disable IRQ + * @filter_ctl: Read: Activate Input Filters 0-15 + * Write: Deactivate Input Filters 0-15 + * @out8_15: Read: FET Drive Outputs 8-15 + * Write: FET Drive Outputs 8-15 + * @in8_15: Read: Isolated Inputs 8-15 + * Write: Unused + * @irq_status: Read: Interrupt status + * Write: Unused + */ +struct idio_16_gpio_reg { + u8 out0_7; + u8 in0_7; + u8 irq_ctl; + u8 filter_ctl; + u8 out8_15; + u8 in8_15; + u8 irq_status; +}; + +/** + * struct idio_16_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @lock: synchronization lock to prevent I/O race conditions + * @reg: I/O address offset for the GPIO device registers + * @irq_mask: I/O bits affected by interrupts + */ +struct idio_16_gpio { + struct gpio_chip chip; + spinlock_t lock; + struct idio_16_gpio_reg __iomem *reg; + unsigned long irq_mask; +}; + +static int idio_16_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + if (offset > 15) + return 1; + + return 0; +} + +static int idio_16_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + return 0; +} + +static int idio_16_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + chip->set(chip, offset, value); + return 0; +} + +static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); + unsigned long mask = BIT(offset); + + if (offset < 8) + return !!(ioread8(&idio16gpio->reg->out0_7) & mask); + + if (offset < 16) + return !!(ioread8(&idio16gpio->reg->out8_15) & (mask >> 8)); + + if (offset < 24) + return !!(ioread8(&idio16gpio->reg->in0_7) & (mask >> 16)); + + return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24)); +} + +static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); + unsigned int mask = BIT(offset); + void __iomem *base; + unsigned long flags; + unsigned int out_state; + + if (offset > 15) + return; + + if (offset > 7) { + mask >>= 8; + base = &idio16gpio->reg->out8_15; + } else + base = &idio16gpio->reg->out0_7; + + spin_lock_irqsave(&idio16gpio->lock, flags); + + if (value) + out_state = ioread8(base) | mask; + else + out_state = ioread8(base) & ~mask; + + iowrite8(out_state, base); + + spin_unlock_irqrestore(&idio16gpio->lock, flags); +} + +static void idio_16_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); + unsigned long flags; + unsigned int out_state; + + spin_lock_irqsave(&idio16gpio->lock, flags); + + /* process output lines 0-7 */ + if (*mask & 0xFF) { + out_state = ioread8(&idio16gpio->reg->out0_7) & ~*mask; + out_state |= *mask & *bits; + iowrite8(out_state, &idio16gpio->reg->out0_7); + } + + /* shift to next output line word */ + *mask >>= 8; + + /* process output lines 8-15 */ + if (*mask & 0xFF) { + *bits >>= 8; + out_state = ioread8(&idio16gpio->reg->out8_15) & ~*mask; + out_state |= *mask & *bits; + iowrite8(out_state, &idio16gpio->reg->out8_15); + } + + spin_unlock_irqrestore(&idio16gpio->lock, flags); +} + +static void idio_16_irq_ack(struct irq_data *data) +{ +} + +static void idio_16_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); + const unsigned long mask = BIT(irqd_to_hwirq(data)); + unsigned long flags; + + idio16gpio->irq_mask &= ~mask; + + if (!idio16gpio->irq_mask) { + spin_lock_irqsave(&idio16gpio->lock, flags); + + iowrite8(0, &idio16gpio->reg->irq_ctl); + + spin_unlock_irqrestore(&idio16gpio->lock, flags); + } +} + +static void idio_16_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); + const unsigned long mask = BIT(irqd_to_hwirq(data)); + const unsigned long prev_irq_mask = idio16gpio->irq_mask; + unsigned long flags; + + idio16gpio->irq_mask |= mask; + + if (!prev_irq_mask) { + spin_lock_irqsave(&idio16gpio->lock, flags); + + ioread8(&idio16gpio->reg->irq_ctl); + + spin_unlock_irqrestore(&idio16gpio->lock, flags); + } +} + +static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + /* The only valid irq types are none and both-edges */ + if (flow_type != IRQ_TYPE_NONE && + (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) + return -EINVAL; + + return 0; +} + +static struct irq_chip idio_16_irqchip = { + .name = "pci-idio-16", + .irq_ack = idio_16_irq_ack, + .irq_mask = idio_16_irq_mask, + .irq_unmask = idio_16_irq_unmask, + .irq_set_type = idio_16_irq_set_type +}; + +static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) +{ + struct idio_16_gpio *const idio16gpio = dev_id; + unsigned int irq_status; + struct gpio_chip *const chip = &idio16gpio->chip; + int gpio; + + spin_lock(&idio16gpio->lock); + + irq_status = ioread8(&idio16gpio->reg->irq_status); + + spin_unlock(&idio16gpio->lock); + + /* Make sure our device generated IRQ */ + if (!(irq_status & 0x3) || !(irq_status & 0x4)) + return IRQ_NONE; + + for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) + generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio)); + + spin_lock(&idio16gpio->lock); + + /* Clear interrupt */ + iowrite8(0, &idio16gpio->reg->in0_7); + + spin_unlock(&idio16gpio->lock); + + return IRQ_HANDLED; +} + +#define IDIO_16_NGPIO 32 +static const char *idio_16_names[IDIO_16_NGPIO] = { + "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", + "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", + "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", + "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" +}; + +static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *const dev = &pdev->dev; + struct idio_16_gpio *idio16gpio; + int err; + const size_t pci_bar_index = 2; + const char *const name = pci_name(pdev); + + idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); + if (!idio16gpio) + return -ENOMEM; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device (%d)\n", err); + return err; + } + + err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name); + if (err) { + dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err); + return err; + } + + idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; + + /* Deactivate input filters */ + iowrite8(0, &idio16gpio->reg->filter_ctl); + + idio16gpio->chip.label = name; + idio16gpio->chip.parent = dev; + idio16gpio->chip.owner = THIS_MODULE; + idio16gpio->chip.base = -1; + idio16gpio->chip.ngpio = IDIO_16_NGPIO; + idio16gpio->chip.names = idio_16_names; + idio16gpio->chip.get_direction = idio_16_gpio_get_direction; + idio16gpio->chip.direction_input = idio_16_gpio_direction_input; + idio16gpio->chip.direction_output = idio_16_gpio_direction_output; + idio16gpio->chip.get = idio_16_gpio_get; + idio16gpio->chip.set = idio_16_gpio_set; + idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; + + spin_lock_init(&idio16gpio->lock); + + err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + /* Disable IRQ by default and clear any pending interrupt */ + iowrite8(0, &idio16gpio->reg->irq_ctl); + iowrite8(0, &idio16gpio->reg->in0_7); + + err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, + handle_edge_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(dev, "Could not add irqchip (%d)\n", err); + return err; + } + + err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED, + name, idio16gpio); + if (err) { + dev_err(dev, "IRQ handler registering failed (%d)\n", err); + return err; + } + + return 0; +} + +static const struct pci_device_id idio_16_pci_dev_id[] = { + { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 } +}; +MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id); + +static struct pci_driver idio_16_driver = { + .name = "pci-idio-16", + .id_table = idio_16_pci_dev_id, + .probe = idio_16_probe +}; + +module_pci_driver(idio_16_driver); + +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); +MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 2be48f5eba36..31ad288846af 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -242,11 +242,24 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) { - return pinctrl_request_gpio(chip->base + offset); + struct gpio_rcar_priv *p = gpiochip_get_data(chip); + int error; + + error = pm_runtime_get_sync(&p->pdev->dev); + if (error < 0) + return error; + + error = pinctrl_request_gpio(chip->base + offset); + if (error) + pm_runtime_put(&p->pdev->dev); + + return error; } static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) { + struct gpio_rcar_priv *p = gpiochip_get_data(chip); + pinctrl_free_gpio(chip->base + offset); /* @@ -254,6 +267,8 @@ static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) * drive the GPIO pin as an output. */ gpio_rcar_config_general_input_output_mode(chip, offset, false); + + pm_runtime_put(&p->pdev->dev); } static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) @@ -426,7 +441,6 @@ static int gpio_rcar_probe(struct platform_device *pdev) } pm_runtime_enable(dev); - pm_runtime_get_sync(dev); io = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -460,6 +474,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) irq_chip = &p->irq_chip; irq_chip->name = name; + irq_chip->parent_device = dev; irq_chip->irq_mask = gpio_rcar_irq_disable; irq_chip->irq_unmask = gpio_rcar_irq_enable; irq_chip->irq_set_type = gpio_rcar_irq_set_type; @@ -494,7 +509,6 @@ static int gpio_rcar_probe(struct platform_device *pdev) err1: gpiochip_remove(gpio_chip); err0: - pm_runtime_put(dev); pm_runtime_disable(dev); return ret; } @@ -505,7 +519,6 @@ static int gpio_rcar_remove(struct platform_device *pdev) gpiochip_remove(&p->gpio_chip); - pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; } diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 19e654f88b3a..c07385b71403 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -3,7 +3,7 @@ * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * - * Copyright (C) 2012 John Crispin <blogic@openwrt.org> + * Copyright (C) 2012 John Crispin <john@phrozen.org> * */ diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index eaa71d440ccf..901b5ccb032d 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -46,7 +46,6 @@ MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); * @irq_mask: I/O bits affected by interrupts * @flow_mask: IRQ flow type mask for the respective I/O bits * @base: base port address of the GPIO device - * @irq: Interrupt line number */ struct ws16c48_gpio { struct gpio_chip chip; @@ -56,7 +55,6 @@ struct ws16c48_gpio { unsigned long irq_mask; unsigned long flow_mask; unsigned base; - unsigned irq; }; static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -155,6 +153,46 @@ static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } +static void ws16c48_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); + unsigned int i; + const unsigned int gpio_reg_size = 8; + unsigned int port; + unsigned int iomask; + unsigned int bitmask; + unsigned long flags; + + /* set bits are evaluated a gpio register size at a time */ + for (i = 0; i < chip->ngpio; i += gpio_reg_size) { + /* no more set bits in this mask word; skip to the next word */ + if (!mask[BIT_WORD(i)]) { + i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size; + continue; + } + + port = i / gpio_reg_size; + + /* mask out GPIO configured for input */ + iomask = mask[BIT_WORD(i)] & ~ws16c48gpio->io_state[port]; + bitmask = iomask & bits[BIT_WORD(i)]; + + spin_lock_irqsave(&ws16c48gpio->lock, flags); + + /* update output state data and set device gpio register */ + ws16c48gpio->out_state[port] &= ~iomask; + ws16c48gpio->out_state[port] |= bitmask; + outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + + spin_unlock_irqrestore(&ws16c48gpio->lock, flags); + + /* prepare for next gpio register set */ + mask[BIT_WORD(i)] >>= gpio_reg_size; + bits[BIT_WORD(i)] >>= gpio_reg_size; + } +} + static void ws16c48_irq_ack(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); @@ -303,6 +341,22 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +#define WS16C48_NGPIO 48 +static const char *ws16c48_names[WS16C48_NGPIO] = { + "Port 0 Bit 0", "Port 0 Bit 1", "Port 0 Bit 2", "Port 0 Bit 3", + "Port 0 Bit 4", "Port 0 Bit 5", "Port 0 Bit 6", "Port 0 Bit 7", + "Port 1 Bit 0", "Port 1 Bit 1", "Port 1 Bit 2", "Port 1 Bit 3", + "Port 1 Bit 4", "Port 1 Bit 5", "Port 1 Bit 6", "Port 1 Bit 7", + "Port 2 Bit 0", "Port 2 Bit 1", "Port 2 Bit 2", "Port 2 Bit 3", + "Port 2 Bit 4", "Port 2 Bit 5", "Port 2 Bit 6", "Port 2 Bit 7", + "Port 3 Bit 0", "Port 3 Bit 1", "Port 3 Bit 2", "Port 3 Bit 3", + "Port 3 Bit 4", "Port 3 Bit 5", "Port 3 Bit 6", "Port 3 Bit 7", + "Port 4 Bit 0", "Port 4 Bit 1", "Port 4 Bit 2", "Port 4 Bit 3", + "Port 4 Bit 4", "Port 4 Bit 5", "Port 4 Bit 6", "Port 4 Bit 7", + "Port 5 Bit 0", "Port 5 Bit 1", "Port 5 Bit 2", "Port 5 Bit 3", + "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" +}; + static int ws16c48_probe(struct device *dev, unsigned int id) { struct ws16c48_gpio *ws16c48gpio; @@ -323,20 +377,19 @@ static int ws16c48_probe(struct device *dev, unsigned int id) ws16c48gpio->chip.parent = dev; ws16c48gpio->chip.owner = THIS_MODULE; ws16c48gpio->chip.base = -1; - ws16c48gpio->chip.ngpio = 48; + ws16c48gpio->chip.ngpio = WS16C48_NGPIO; + ws16c48gpio->chip.names = ws16c48_names; ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction; ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input; ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output; ws16c48gpio->chip.get = ws16c48_gpio_get; ws16c48gpio->chip.set = ws16c48_gpio_set; + ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple; ws16c48gpio->base = base[id]; - ws16c48gpio->irq = irq[id]; spin_lock_init(&ws16c48gpio->lock); - dev_set_drvdata(dev, ws16c48gpio); - - err = gpiochip_add_data(&ws16c48gpio->chip, ws16c48gpio); + err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); if (err) { dev_err(dev, "GPIO registering failed (%d)\n", err); return err; @@ -353,31 +406,17 @@ static int ws16c48_probe(struct device *dev, unsigned int id) handle_edge_irq, IRQ_TYPE_NONE); if (err) { dev_err(dev, "Could not add irqchip (%d)\n", err); - goto err_gpiochip_remove; + return err; } - err = request_irq(irq[id], ws16c48_irq_handler, IRQF_SHARED, name, - ws16c48gpio); + err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED, + name, ws16c48gpio); if (err) { dev_err(dev, "IRQ handler registering failed (%d)\n", err); - goto err_gpiochip_remove; + return err; } return 0; - -err_gpiochip_remove: - gpiochip_remove(&ws16c48gpio->chip); - return err; -} - -static int ws16c48_remove(struct device *dev, unsigned int id) -{ - struct ws16c48_gpio *const ws16c48gpio = dev_get_drvdata(dev); - - free_irq(ws16c48gpio->irq, ws16c48gpio); - gpiochip_remove(&ws16c48gpio->chip); - - return 0; } static struct isa_driver ws16c48_driver = { @@ -385,7 +424,6 @@ static struct isa_driver ws16c48_driver = { .driver = { .name = "ws16c48" }, - .remove = ws16c48_remove }; module_isa_driver(ws16c48_driver, num_ws16c48); diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index a3faefa44f68..9b37a3692b3f 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -416,9 +416,8 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; /* - * ActiveLow is only specified for GpioInt resource. If - * GpioIo is used then the only way to set the flag is - * to use _DSD "gpios" property. + * Polarity and triggering are only specified for GpioInt + * resource. * Note: we expect here: * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 92b185f19232..975b9f6cf408 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -160,6 +160,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API * @np: device node to get GPIO from * @chip: GPIO chip whose hog is parsed + * @idx: Index of the GPIO to parse * @name: GPIO line name * @lflags: gpio_lookup_flags - returned from of_find_gpio() or * of_parse_own_gpio() @@ -170,7 +171,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, */ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, struct gpio_chip *chip, - const char **name, + unsigned int idx, const char **name, enum gpio_lookup_flags *lflags, enum gpiod_flags *dflags) { @@ -178,6 +179,7 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, enum of_gpio_flags xlate_flags; struct of_phandle_args gpiospec; struct gpio_desc *desc; + unsigned int i; u32 tmp; int ret; @@ -196,9 +198,12 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, gpiospec.np = chip_np; gpiospec.args_count = tmp; - ret = of_property_read_u32_array(np, "gpios", gpiospec.args, tmp); - if (ret) - return ERR_PTR(ret); + for (i = 0; i < tmp; i++) { + ret = of_property_read_u32_index(np, "gpios", idx * tmp + i, + &gpiospec.args[i]); + if (ret) + return ERR_PTR(ret); + } desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags); if (IS_ERR(desc)) @@ -240,20 +245,24 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip) const char *name; enum gpio_lookup_flags lflags; enum gpiod_flags dflags; + unsigned int i; int ret; for_each_available_child_of_node(chip->of_node, np) { if (!of_property_read_bool(np, "gpio-hog")) continue; - desc = of_parse_own_gpio(np, chip, &name, &lflags, &dflags); - if (IS_ERR(desc)) - continue; + for (i = 0;; i++) { + desc = of_parse_own_gpio(np, chip, i, &name, &lflags, + &dflags); + if (IS_ERR(desc)) + break; - ret = gpiod_hog(desc, name, lflags, dflags); - if (ret < 0) { - of_node_put(np); - return ret; + ret = gpiod_hog(desc, name, lflags, dflags); + if (ret < 0) { + of_node_put(np); + return ret; + } } } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d0478f1853db..8b4d721d6d63 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,3 +1,4 @@ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -982,7 +983,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp) struct gpio_device, chrdev); /* Fail on open if the backing gpiochip is gone */ - if (!gdev || !gdev->chip) + if (!gdev->chip) return -ENODEV; get_device(&gdev->dev); filp->private_data = gdev; @@ -1001,8 +1002,6 @@ static int gpio_chrdev_release(struct inode *inode, struct file *filp) struct gpio_device *gdev = container_of(inode->i_cdev, struct gpio_device, chrdev); - if (!gdev) - return -ENODEV; put_device(&gdev->dev); return 0; } @@ -1423,8 +1422,7 @@ void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip) ret = devres_release(dev, devm_gpio_chip_release, devm_gpio_chip_match, chip); - if (!ret) - WARN_ON(ret); + WARN_ON(ret); } EXPORT_SYMBOL_GPL(devm_gpiochip_remove); @@ -2586,18 +2584,11 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip, if (chip->set_multiple) { chip->set_multiple(chip, mask, bits); } else { - int i; - for (i = 0; i < chip->ngpio; i++) { - if (mask[BIT_WORD(i)] == 0) { - /* no more set bits in this mask word; - * skip ahead to the next word */ - i = (BIT_WORD(i) + 1) * BITS_PER_LONG - 1; - continue; - } - /* set outputs if the corresponding mask bit is set */ - if (__test_and_clear_bit(i, mask)) - chip->set(chip, i, test_bit(i, bits)); - } + unsigned int i; + + /* set outputs if the corresponding mask bit is set */ + for_each_set_bit(i, mask, chip->ngpio) + chip->set(chip, i, test_bit(i, bits)); } } @@ -3325,6 +3316,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_index); * fwnode_get_named_gpiod - obtain a GPIO from firmware node * @fwnode: handle of the firmware node * @propname: name of the firmware property representing the GPIO + * @index: index of the GPIO to obtain in the consumer + * @dflags: GPIO initialization flags * * This function can be used for drivers that get their configuration * from firmware. @@ -3333,12 +3326,18 @@ EXPORT_SYMBOL_GPL(gpiod_get_index); * underlying firmware interface and then makes sure that the GPIO * descriptor is requested before it is returned to the caller. * + * On successfull request the GPIO pin is configured in accordance with + * provided @dflags. + * * In case of error an ERR_PTR() is returned. */ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, - const char *propname) + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) { struct gpio_desc *desc = ERR_PTR(-ENODEV); + unsigned long lflags = 0; bool active_low = false; bool single_ended = false; int ret; @@ -3349,8 +3348,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, if (is_of_node(fwnode)) { enum of_gpio_flags flags; - desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname, 0, - &flags); + desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname, + index, &flags); if (!IS_ERR(desc)) { active_low = flags & OF_GPIO_ACTIVE_LOW; single_ended = flags & OF_GPIO_SINGLE_ENDED; @@ -3358,7 +3357,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, } else if (is_acpi_node(fwnode)) { struct acpi_gpio_info info; - desc = acpi_node_get_gpiod(fwnode, propname, 0, &info); + desc = acpi_node_get_gpiod(fwnode, propname, index, &info); if (!IS_ERR(desc)) active_low = info.polarity == GPIO_ACTIVE_LOW; } @@ -3366,18 +3365,24 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, if (IS_ERR(desc)) return desc; - ret = gpiod_request(desc, NULL); + ret = gpiod_request(desc, label); if (ret) return ERR_PTR(ret); if (active_low) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); + lflags |= GPIO_ACTIVE_LOW; if (single_ended) { if (active_low) - set_bit(FLAG_OPEN_DRAIN, &desc->flags); + lflags |= GPIO_OPEN_DRAIN; else - set_bit(FLAG_OPEN_SOURCE, &desc->flags); + lflags |= GPIO_OPEN_SOURCE; + } + + ret = gpiod_configure_flags(desc, propname, lflags, dflags); + if (ret < 0) { + gpiod_put(desc); + return ERR_PTR(ret); } return desc; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d10eaf520860..2495b7ee1b42 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -76,7 +76,8 @@ struct gpio_device { /** * struct acpi_gpio_info - ACPI GPIO specific information * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo - * @active_low: in case of @gpioint, the pin is active low + * @polarity: interrupt polarity as provided by ACPI + * @triggering: triggering type as provided by ACPI */ struct acpi_gpio_info { bool gpioint; diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ebfe8404c25f..88e01e08e279 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -6,7 +6,7 @@ # menuconfig DRM tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" - depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU && HAS_DMA + depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA select HDMI select FB_CMDLINE select I2C @@ -48,6 +48,21 @@ config DRM_DEBUG_MM If in doubt, say "N". +config DRM_DEBUG_MM_SELFTEST + tristate "kselftests for DRM range manager (struct drm_mm)" + depends on DRM + depends on DEBUG_KERNEL + select PRIME_NUMBERS + select DRM_LIB_RANDOM + default n + help + This option provides a kernel module that can be used to test + the DRM range manager (drm_mm) and its API. This option is not + useful for distributions or general kernels, but only for kernel + developers working on DRM and associated drivers. + + If in doubt, say "N". + config DRM_KMS_HELPER tristate depends on DRM @@ -98,7 +113,7 @@ config DRM_LOAD_EDID_FIRMWARE config DRM_TTM tristate - depends on DRM + depends on DRM && MMU help GPU memory management subsystem for devices with multiple GPU memory types. Will be enabled automatically if a device driver @@ -121,13 +136,17 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions +config DRM_VM + bool + depends on DRM && MMU + source "drivers/gpu/drm/i2c/Kconfig" source "drivers/gpu/drm/arm/Kconfig" config DRM_RADEON tristate "ATI Radeon" - depends on DRM && PCI + depends on DRM && PCI && MMU select FW_LOADER select DRM_KMS_HELPER select DRM_TTM @@ -147,7 +166,7 @@ source "drivers/gpu/drm/radeon/Kconfig" config DRM_AMDGPU tristate "AMD GPU" - depends on DRM && PCI + depends on DRM && PCI && MMU select FW_LOADER select DRM_KMS_HELPER select DRM_TTM @@ -244,11 +263,14 @@ source "drivers/gpu/drm/mxsfb/Kconfig" source "drivers/gpu/drm/meson/Kconfig" +source "drivers/gpu/drm/tinydrm/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY bool "Enable legacy drivers (DANGEROUS)" - depends on DRM + depends on DRM && MMU + select DRM_VM help Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous APIs to user-space, which can be used to circumvent access @@ -321,3 +343,7 @@ config DRM_SAVAGE chipset. If M is selected the module will be called savage. endif # DRM_LEGACY + +config DRM_LIB_RANDOM + bool + default n diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index b9ae4280de9d..3ee95793d122 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -5,7 +5,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_context.o drm_dma.o \ drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ - drm_lock.o drm_memory.o drm_drv.o drm_vm.o \ + drm_lock.o drm_memory.o drm_drv.o \ drm_scatter.o drm_pci.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_fourcc.o drm_modes.o drm_edid.o \ @@ -18,6 +18,8 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o +drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o +drm-$(CONFIG_DRM_VM) += drm_vm.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o @@ -37,6 +39,7 @@ drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o +obj-$(CONFIG_DRM_DEBUG_MM_SELFTEST) += selftests/ CFLAGS_drm_trace_points.o := -I$(src) @@ -91,3 +94,4 @@ obj-$(CONFIG_DRM_ARCPGU)+= arc/ obj-y += hisilicon/ obj-$(CONFIG_DRM_ZTE) += zte/ obj-$(CONFIG_DRM_MXSFB) += mxsfb/ +obj-$(CONFIG_DRM_TINYDRM) += tinydrm/ diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index 41bd2bf28f4c..2814aad81752 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -24,7 +24,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \ atombios_encoders.o amdgpu_sa.o atombios_i2c.o \ amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \ amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o \ - amdgpu_gtt_mgr.o amdgpu_vram_mgr.o + amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o # add asic specific block amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o cik_ih.o kv_smc.o kv_dpm.o \ @@ -34,7 +34,7 @@ amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o cik_ih.o kv_smc.o kv_dpm.o \ amdgpu-$(CONFIG_DRM_AMDGPU_SI)+= si.o gmc_v6_0.o gfx_v6_0.o si_ih.o si_dma.o dce_v6_0.o si_dpm.o si_smc.o amdgpu-y += \ - vi.o + vi.o mxgpu_vi.o # add GMC block amdgpu-y += \ @@ -52,8 +52,7 @@ amdgpu-y += \ # add SMC block amdgpu-y += \ amdgpu_dpm.o \ - amdgpu_powerplay.o \ - cz_smc.o cz_dpm.o + amdgpu_powerplay.o # add DCE block amdgpu-y += \ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 63208e5c1588..c1b913541739 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -91,7 +91,6 @@ extern int amdgpu_vm_fault_stop; extern int amdgpu_vm_debug; extern int amdgpu_sched_jobs; extern int amdgpu_sched_hw_submission; -extern int amdgpu_powerplay; extern int amdgpu_no_evict; extern int amdgpu_direct_gma_size; extern unsigned amdgpu_pcie_gen_cap; @@ -184,12 +183,18 @@ enum amdgpu_thermal_irq { AMDGPU_THERMAL_IRQ_LAST }; +enum amdgpu_kiq_irq { + AMDGPU_CP_KIQ_IRQ_DRIVER0 = 0, + AMDGPU_CP_KIQ_IRQ_LAST +}; + int amdgpu_set_clockgating_state(struct amdgpu_device *adev, enum amd_ip_block_type block_type, enum amd_clockgating_state state); int amdgpu_set_powergating_state(struct amdgpu_device *adev, enum amd_ip_block_type block_type, enum amd_powergating_state state); +void amdgpu_get_clockgating_state(struct amdgpu_device *adev, u32 *flags); int amdgpu_wait_for_idle(struct amdgpu_device *adev, enum amd_ip_block_type block_type); bool amdgpu_is_idle(struct amdgpu_device *adev, @@ -352,7 +357,7 @@ struct amdgpu_bo_va_mapping { struct list_head list; struct interval_tree_node it; uint64_t offset; - uint32_t flags; + uint64_t flags; }; /* bo virtual addresses in a specific vm */ @@ -776,14 +781,20 @@ struct amdgpu_mec { u32 num_queue; }; +struct amdgpu_kiq { + u64 eop_gpu_addr; + struct amdgpu_bo *eop_obj; + struct amdgpu_ring ring; + struct amdgpu_irq_src irq; +}; + /* * GPU scratch registers structures, functions & helpers */ struct amdgpu_scratch { unsigned num_reg; uint32_t reg_base; - bool free[32]; - uint32_t reg[32]; + uint32_t free_mask; }; /* @@ -851,6 +862,7 @@ struct amdgpu_gfx { struct amdgpu_gca_config config; struct amdgpu_rlc rlc; struct amdgpu_mec mec; + struct amdgpu_kiq kiq; struct amdgpu_scratch scratch; const struct firmware *me_fw; /* ME firmware */ uint32_t me_fw_version; @@ -894,8 +906,8 @@ int amdgpu_ib_get(struct amdgpu_device *adev, struct amdgpu_vm *vm, void amdgpu_ib_free(struct amdgpu_device *adev, struct amdgpu_ib *ib, struct dma_fence *f); int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs, - struct amdgpu_ib *ib, struct dma_fence *last_vm_update, - struct amdgpu_job *job, struct dma_fence **f); + struct amdgpu_ib *ibs, struct amdgpu_job *job, + struct dma_fence **f); int amdgpu_ib_pool_init(struct amdgpu_device *adev); void amdgpu_ib_pool_fini(struct amdgpu_device *adev); int amdgpu_ib_ring_tests(struct amdgpu_device *adev); @@ -938,6 +950,7 @@ struct amdgpu_cs_parser { #define AMDGPU_PREAMBLE_IB_PRESENT (1 << 0) /* bit set means command submit involves a preamble IB */ #define AMDGPU_PREAMBLE_IB_PRESENT_FIRST (1 << 1) /* bit set means preamble IB is first presented in belonging context */ #define AMDGPU_HAVE_CTX_SWITCH (1 << 2) /* bit set means context switch occured */ +#define AMDGPU_VM_DOMAIN (1 << 3) /* bit set means in virtual memory context */ struct amdgpu_job { struct amd_sched_job base; @@ -1133,7 +1146,6 @@ int amdgpu_debugfs_fence_init(struct amdgpu_device *adev); #if defined(CONFIG_DEBUG_FS) int amdgpu_debugfs_init(struct drm_minor *minor); -void amdgpu_debugfs_cleanup(struct drm_minor *minor); #endif int amdgpu_debugfs_firmware_init(struct amdgpu_device *adev); @@ -1178,7 +1190,6 @@ struct amdgpu_asic_funcs { bool (*read_disabled_bios)(struct amdgpu_device *adev); bool (*read_bios_from_rom)(struct amdgpu_device *adev, u8 *bios, u32 length_bytes); - void (*detect_hw_virtualization) (struct amdgpu_device *adev); int (*read_register)(struct amdgpu_device *adev, u32 se_num, u32 sh_num, u32 reg_offset, u32 *value); void (*set_vga_state)(struct amdgpu_device *adev, bool state); @@ -1333,7 +1344,6 @@ struct amdgpu_device { /* BIOS */ uint8_t *bios; uint32_t bios_size; - bool is_atom_bios; struct amdgpu_bo *stollen_vga_memory; uint32_t bios_scratch[AMDGPU_BIOS_NUM_SCRATCH]; @@ -1463,7 +1473,7 @@ struct amdgpu_device { /* amdkfd interface */ struct kfd_dev *kfd; - struct amdgpu_virtualization virtualization; + struct amdgpu_virt virt; /* link all shadow bo */ struct list_head shadow_list; @@ -1472,6 +1482,9 @@ struct amdgpu_device { spinlock_t gtt_list_lock; struct list_head gtt_list; + /* record hw reset is performed */ + bool has_hw_reset; + }; static inline struct amdgpu_device *amdgpu_ttm_adev(struct ttm_bo_device *bdev) @@ -1576,6 +1589,37 @@ static inline void amdgpu_ring_write(struct amdgpu_ring *ring, uint32_t v) ring->count_dw--; } +static inline void amdgpu_ring_write_multiple(struct amdgpu_ring *ring, void *src, int count_dw) +{ + unsigned occupied, chunk1, chunk2; + void *dst; + + if (ring->count_dw < count_dw) { + DRM_ERROR("amdgpu: writing more dwords to the ring than expected!\n"); + } else { + occupied = ring->wptr & ring->ptr_mask; + dst = (void *)&ring->ring[occupied]; + chunk1 = ring->ptr_mask + 1 - occupied; + chunk1 = (chunk1 >= count_dw) ? count_dw: chunk1; + chunk2 = count_dw - chunk1; + chunk1 <<= 2; + chunk2 <<= 2; + + if (chunk1) + memcpy(dst, src, chunk1); + + if (chunk2) { + src += chunk1; + dst = (void *)ring->ring; + memcpy(dst, src, chunk2); + } + + ring->wptr += count_dw; + ring->wptr &= ring->ptr_mask; + ring->count_dw -= count_dw; + } +} + static inline struct amdgpu_sdma_instance * amdgpu_get_sdma_instance(struct amdgpu_ring *ring) { @@ -1605,7 +1649,6 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring) #define amdgpu_asic_get_gpu_clock_counter(adev) (adev)->asic_funcs->get_gpu_clock_counter((adev)) #define amdgpu_asic_read_disabled_bios(adev) (adev)->asic_funcs->read_disabled_bios((adev)) #define amdgpu_asic_read_bios_from_rom(adev, b, l) (adev)->asic_funcs->read_bios_from_rom((adev), (b), (l)) -#define amdgpu_asic_detect_hw_virtualization(adev) (adev)->asic_funcs->detect_hw_virtualization((adev)) #define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v))) #define amdgpu_gart_flush_gpu_tlb(adev, vmid) (adev)->gart.gart_funcs->flush_gpu_tlb((adev), (vmid)) #define amdgpu_gart_set_pte_pde(adev, pt, idx, addr, flags) (adev)->gart.gart_funcs->set_pte_pde((adev), (pt), (idx), (addr), (flags)) @@ -1627,6 +1670,8 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring) #define amdgpu_ring_emit_hdp_invalidate(r) (r)->funcs->emit_hdp_invalidate((r)) #define amdgpu_ring_emit_switch_buffer(r) (r)->funcs->emit_switch_buffer((r)) #define amdgpu_ring_emit_cntxcntl(r, d) (r)->funcs->emit_cntxcntl((r), (d)) +#define amdgpu_ring_emit_rreg(r, d) (r)->funcs->emit_rreg((r), (d)) +#define amdgpu_ring_emit_wreg(r, d, v) (r)->funcs->emit_wreg((r), (d), (v)) #define amdgpu_ring_pad_ib(r, ib) ((r)->funcs->pad_ib((r), (ib))) #define amdgpu_ring_init_cond_exec(r) (r)->funcs->init_cond_exec((r)) #define amdgpu_ring_patch_cond_exec(r,o) (r)->funcs->patch_cond_exec((r),(o)) @@ -1658,13 +1703,14 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring) int amdgpu_gpu_reset(struct amdgpu_device *adev); bool amdgpu_need_backup(struct amdgpu_device *adev); void amdgpu_pci_config_reset(struct amdgpu_device *adev); -bool amdgpu_card_posted(struct amdgpu_device *adev); +bool amdgpu_need_post(struct amdgpu_device *adev); void amdgpu_update_display_priority(struct amdgpu_device *adev); int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data); int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type, u32 ip_instance, u32 ring, struct amdgpu_ring **out_ring); +void amdgpu_cs_report_moved_bytes(struct amdgpu_device *adev, u64 num_bytes); void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *abo, u32 domain); bool amdgpu_ttm_bo_is_amdgpu_bo(struct ttm_buffer_object *bo); int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages); @@ -1711,7 +1757,7 @@ extern const struct drm_ioctl_desc amdgpu_ioctls_kms[]; extern const int amdgpu_max_kms_ioctl; int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags); -int amdgpu_driver_unload_kms(struct drm_device *dev); +void amdgpu_driver_unload_kms(struct drm_device *dev); void amdgpu_driver_lastclose_kms(struct drm_device *dev); int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv); void amdgpu_driver_postclose_kms(struct drm_device *dev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c index 5796539a0bcb..ef79551b4cb7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c @@ -672,12 +672,10 @@ int amdgpu_acpi_init(struct amdgpu_device *adev) if ((enc->devices & (ATOM_DEVICE_LCD_SUPPORT)) && enc->enc_priv) { - if (adev->is_atom_bios) { - struct amdgpu_encoder_atom_dig *dig = enc->enc_priv; - if (dig->bl_dev) { - atif->encoder_for_bl = enc; - break; - } + struct amdgpu_encoder_atom_dig *dig = enc->enc_priv; + if (dig->bl_dev) { + atif->encoder_for_bl = enc; + break; } } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index 8ec1967a850b..821f7cc2051f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -42,6 +42,51 @@ #define AMD_IS_VALID_VBIOS(p) ((p)[0] == 0x55 && (p)[1] == 0xAA) #define AMD_VBIOS_LENGTH(p) ((p)[2] << 9) +/* Check if current bios is an ATOM BIOS. + * Return true if it is ATOM BIOS. Otherwise, return false. + */ +static bool check_atom_bios(uint8_t *bios, size_t size) +{ + uint16_t tmp, bios_header_start; + + if (!bios || size < 0x49) { + DRM_INFO("vbios mem is null or mem size is wrong\n"); + return false; + } + + if (!AMD_IS_VALID_VBIOS(bios)) { + DRM_INFO("BIOS signature incorrect %x %x\n", bios[0], bios[1]); + return false; + } + + tmp = bios[0x18] | (bios[0x19] << 8); + if (bios[tmp + 0x14] != 0x0) { + DRM_INFO("Not an x86 BIOS ROM\n"); + return false; + } + + bios_header_start = bios[0x48] | (bios[0x49] << 8); + if (!bios_header_start) { + DRM_INFO("Can't locate bios header\n"); + return false; + } + + tmp = bios_header_start + 4; + if (size < tmp) { + DRM_INFO("BIOS header is broken\n"); + return false; + } + + if (!memcmp(bios + tmp, "ATOM", 4) || + !memcmp(bios + tmp, "MOTA", 4)) { + DRM_DEBUG("ATOMBIOS detected\n"); + return true; + } + + return false; +} + + /* If you boot an IGP board with a discrete card as the primary, * the IGP rom is not accessible via the rom bar as the IGP rom is * part of the system bios. On boot, the system bios puts a @@ -55,7 +100,7 @@ static bool igp_read_bios_from_vram(struct amdgpu_device *adev) resource_size_t size = 256 * 1024; /* ??? */ if (!(adev->flags & AMD_IS_APU)) - if (!amdgpu_card_posted(adev)) + if (amdgpu_need_post(adev)) return false; adev->bios = NULL; @@ -65,10 +110,6 @@ static bool igp_read_bios_from_vram(struct amdgpu_device *adev) return false; } - if (size == 0 || !AMD_IS_VALID_VBIOS(bios)) { - iounmap(bios); - return false; - } adev->bios = kmalloc(size, GFP_KERNEL); if (!adev->bios) { iounmap(bios); @@ -77,12 +118,18 @@ static bool igp_read_bios_from_vram(struct amdgpu_device *adev) adev->bios_size = size; memcpy_fromio(adev->bios, bios, size); iounmap(bios); + + if (!check_atom_bios(adev->bios, size)) { + kfree(adev->bios); + return false; + } + return true; } bool amdgpu_read_bios(struct amdgpu_device *adev) { - uint8_t __iomem *bios, val[2]; + uint8_t __iomem *bios; size_t size; adev->bios = NULL; @@ -92,13 +139,6 @@ bool amdgpu_read_bios(struct amdgpu_device *adev) return false; } - val[0] = readb(&bios[0]); - val[1] = readb(&bios[1]); - - if (size == 0 || !AMD_IS_VALID_VBIOS(val)) { - pci_unmap_rom(adev->pdev, bios); - return false; - } adev->bios = kzalloc(size, GFP_KERNEL); if (adev->bios == NULL) { pci_unmap_rom(adev->pdev, bios); @@ -107,6 +147,12 @@ bool amdgpu_read_bios(struct amdgpu_device *adev) adev->bios_size = size; memcpy_fromio(adev->bios, bios, size); pci_unmap_rom(adev->pdev, bios); + + if (!check_atom_bios(adev->bios, size)) { + kfree(adev->bios); + return false; + } + return true; } @@ -140,7 +186,14 @@ static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev) adev->bios_size = len; /* read complete BIOS */ - return amdgpu_asic_read_bios_from_rom(adev, adev->bios, len); + amdgpu_asic_read_bios_from_rom(adev, adev->bios, len); + + if (!check_atom_bios(adev->bios, len)) { + kfree(adev->bios); + return false; + } + + return true; } static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) @@ -155,13 +208,17 @@ static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) return false; } - if (size == 0 || !AMD_IS_VALID_VBIOS(bios)) { + adev->bios = kzalloc(size, GFP_KERNEL); + if (adev->bios == NULL) return false; - } - adev->bios = kmemdup(bios, size, GFP_KERNEL); - if (adev->bios == NULL) { + + memcpy_fromio(adev->bios, bios, size); + + if (!check_atom_bios(adev->bios, size)) { + kfree(adev->bios); return false; } + adev->bios_size = size; return true; @@ -273,7 +330,7 @@ static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) break; } - if (i == 0 || !AMD_IS_VALID_VBIOS(adev->bios)) { + if (!check_atom_bios(adev->bios, size)) { kfree(adev->bios); return false; } @@ -298,53 +355,59 @@ static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev) #ifdef CONFIG_ACPI static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) { - bool ret = false; struct acpi_table_header *hdr; acpi_size tbl_size; UEFI_ACPI_VFCT *vfct; - GOP_VBIOS_CONTENT *vbios; - VFCT_IMAGE_HEADER *vhdr; + unsigned offset; if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) return false; tbl_size = hdr->length; if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); - goto out_unmap; + return false; } vfct = (UEFI_ACPI_VFCT *)hdr; - if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { - DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); - goto out_unmap; - } + offset = vfct->VBIOSImageOffset; - vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); - vhdr = &vbios->VbiosHeader; - DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", - vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction, - vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength); - - if (vhdr->PCIBus != adev->pdev->bus->number || - vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) || - vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn) || - vhdr->VendorID != adev->pdev->vendor || - vhdr->DeviceID != adev->pdev->device) { - DRM_INFO("ACPI VFCT table is not for this card\n"); - goto out_unmap; - } + while (offset < tbl_size) { + GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset); + VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader; - if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { - DRM_ERROR("ACPI VFCT image truncated\n"); - goto out_unmap; - } + offset += sizeof(VFCT_IMAGE_HEADER); + if (offset > tbl_size) { + DRM_ERROR("ACPI VFCT image header truncated\n"); + return false; + } + + offset += vhdr->ImageLength; + if (offset > tbl_size) { + DRM_ERROR("ACPI VFCT image truncated\n"); + return false; + } - adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); - adev->bios_size = vhdr->ImageLength; - ret = !!adev->bios; + if (vhdr->ImageLength && + vhdr->PCIBus == adev->pdev->bus->number && + vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) && + vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) && + vhdr->VendorID == adev->pdev->vendor && + vhdr->DeviceID == adev->pdev->device) { + adev->bios = kmemdup(&vbios->VbiosContent, + vhdr->ImageLength, + GFP_KERNEL); + + if (!check_atom_bios(adev->bios, vhdr->ImageLength)) { + kfree(adev->bios); + return false; + } + adev->bios_size = vhdr->ImageLength; + return true; + } + } -out_unmap: - return ret; + DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); + return false; } #else static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) @@ -355,57 +418,27 @@ static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) bool amdgpu_get_bios(struct amdgpu_device *adev) { - bool r; - uint16_t tmp, bios_header_start; + if (amdgpu_atrm_get_bios(adev)) + return true; - r = amdgpu_atrm_get_bios(adev); - if (!r) - r = amdgpu_acpi_vfct_bios(adev); - if (!r) - r = igp_read_bios_from_vram(adev); - if (!r) - r = amdgpu_read_bios(adev); - if (!r) { - r = amdgpu_read_bios_from_rom(adev); - } - if (!r) { - r = amdgpu_read_disabled_bios(adev); - } - if (!r) { - r = amdgpu_read_platform_bios(adev); - } - if (!r || adev->bios == NULL) { - DRM_ERROR("Unable to locate a BIOS ROM\n"); - adev->bios = NULL; - return false; - } - if (!AMD_IS_VALID_VBIOS(adev->bios)) { - printk("BIOS signature incorrect %x %x\n", adev->bios[0], adev->bios[1]); - goto free_bios; - } + if (amdgpu_acpi_vfct_bios(adev)) + return true; - tmp = RBIOS16(0x18); - if (RBIOS8(tmp + 0x14) != 0x0) { - DRM_INFO("Not an x86 BIOS ROM, not using.\n"); - goto free_bios; - } + if (igp_read_bios_from_vram(adev)) + return true; - bios_header_start = RBIOS16(0x48); - if (!bios_header_start) { - goto free_bios; - } - tmp = bios_header_start + 4; - if (!memcmp(adev->bios + tmp, "ATOM", 4) || - !memcmp(adev->bios + tmp, "MOTA", 4)) { - adev->is_atom_bios = true; - } else { - adev->is_atom_bios = false; - } + if (amdgpu_read_bios(adev)) + return true; - DRM_DEBUG("%sBIOS detected\n", adev->is_atom_bios ? "ATOM" : "COM"); - return true; -free_bios: - kfree(adev->bios); - adev->bios = NULL; + if (amdgpu_read_bios_from_rom(adev)) + return true; + + if (amdgpu_read_disabled_bios(adev)) + return true; + + if (amdgpu_read_platform_bios(adev)) + return true; + + DRM_ERROR("Unable to locate a BIOS ROM\n"); return false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index c02db01f6583..0218cea6be4d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -70,10 +70,10 @@ static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id) struct amdgpu_bo_list *list; mutex_lock(&fpriv->bo_list_lock); - list = idr_find(&fpriv->bo_list_handles, id); + list = idr_remove(&fpriv->bo_list_handles, id); if (list) { + /* Another user may have a reference to this list still */ mutex_lock(&list->lock); - idr_remove(&fpriv->bo_list_handles, id); mutex_unlock(&list->lock); amdgpu_bo_list_free(list); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index 4c851fde1e82..d9e5aa4a79ef 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -713,6 +713,7 @@ static int amdgpu_cgs_rel_firmware(struct cgs_device *cgs_device, enum cgs_ucode CGS_FUNC_ADEV; if ((CGS_UCODE_ID_SMU == type) || (CGS_UCODE_ID_SMU_SK == type)) { release_firmware(adev->pm.fw); + adev->pm.fw = NULL; return 0; } /* cannot release other firmware because they are not created by cgs */ @@ -762,6 +763,23 @@ static uint16_t amdgpu_get_firmware_version(struct cgs_device *cgs_device, return fw_version; } +static int amdgpu_cgs_enter_safe_mode(struct cgs_device *cgs_device, + bool en) +{ + CGS_FUNC_ADEV; + + if (adev->gfx.rlc.funcs->enter_safe_mode == NULL || + adev->gfx.rlc.funcs->exit_safe_mode == NULL) + return 0; + + if (en) + adev->gfx.rlc.funcs->enter_safe_mode(adev); + else + adev->gfx.rlc.funcs->exit_safe_mode(adev); + + return 0; +} + static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device, enum cgs_ucode_id type, struct cgs_firmware_info *info) @@ -808,37 +826,65 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device, const uint8_t *src; const struct smc_firmware_header_v1_0 *hdr; + if (CGS_UCODE_ID_SMU_SK == type) + amdgpu_cgs_rel_firmware(cgs_device, CGS_UCODE_ID_SMU); + if (!adev->pm.fw) { switch (adev->asic_type) { case CHIP_TOPAZ: if (((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0x81)) || ((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0x83)) || - ((adev->pdev->device == 0x6907) && (adev->pdev->revision == 0x87))) + ((adev->pdev->device == 0x6907) && (adev->pdev->revision == 0x87))) { + info->is_kicker = true; strcpy(fw_name, "amdgpu/topaz_k_smc.bin"); - else + } else strcpy(fw_name, "amdgpu/topaz_smc.bin"); break; case CHIP_TONGA: if (((adev->pdev->device == 0x6939) && (adev->pdev->revision == 0xf1)) || - ((adev->pdev->device == 0x6938) && (adev->pdev->revision == 0xf1))) + ((adev->pdev->device == 0x6938) && (adev->pdev->revision == 0xf1))) { + info->is_kicker = true; strcpy(fw_name, "amdgpu/tonga_k_smc.bin"); - else + } else strcpy(fw_name, "amdgpu/tonga_smc.bin"); break; case CHIP_FIJI: strcpy(fw_name, "amdgpu/fiji_smc.bin"); break; case CHIP_POLARIS11: - if (type == CGS_UCODE_ID_SMU) - strcpy(fw_name, "amdgpu/polaris11_smc.bin"); - else if (type == CGS_UCODE_ID_SMU_SK) + if (type == CGS_UCODE_ID_SMU) { + if (((adev->pdev->device == 0x67ef) && + ((adev->pdev->revision == 0xe0) || + (adev->pdev->revision == 0xe2) || + (adev->pdev->revision == 0xe5))) || + ((adev->pdev->device == 0x67ff) && + ((adev->pdev->revision == 0xcf) || + (adev->pdev->revision == 0xef) || + (adev->pdev->revision == 0xff)))) { + info->is_kicker = true; + strcpy(fw_name, "amdgpu/polaris11_k_smc.bin"); + } else + strcpy(fw_name, "amdgpu/polaris11_smc.bin"); + } else if (type == CGS_UCODE_ID_SMU_SK) { strcpy(fw_name, "amdgpu/polaris11_smc_sk.bin"); + } break; case CHIP_POLARIS10: - if (type == CGS_UCODE_ID_SMU) - strcpy(fw_name, "amdgpu/polaris10_smc.bin"); - else if (type == CGS_UCODE_ID_SMU_SK) + if (type == CGS_UCODE_ID_SMU) { + if ((adev->pdev->device == 0x67df) && + ((adev->pdev->revision == 0xe0) || + (adev->pdev->revision == 0xe3) || + (adev->pdev->revision == 0xe4) || + (adev->pdev->revision == 0xe5) || + (adev->pdev->revision == 0xe7) || + (adev->pdev->revision == 0xef))) { + info->is_kicker = true; + strcpy(fw_name, "amdgpu/polaris10_k_smc.bin"); + } else + strcpy(fw_name, "amdgpu/polaris10_smc.bin"); + } else if (type == CGS_UCODE_ID_SMU_SK) { strcpy(fw_name, "amdgpu/polaris10_smc_sk.bin"); + } break; case CHIP_POLARIS12: strcpy(fw_name, "amdgpu/polaris12_smc.bin"); @@ -1200,51 +1246,52 @@ static int amdgpu_cgs_call_acpi_method(struct cgs_device *cgs_device, } static const struct cgs_ops amdgpu_cgs_ops = { - amdgpu_cgs_gpu_mem_info, - amdgpu_cgs_gmap_kmem, - amdgpu_cgs_gunmap_kmem, - amdgpu_cgs_alloc_gpu_mem, - amdgpu_cgs_free_gpu_mem, - amdgpu_cgs_gmap_gpu_mem, - amdgpu_cgs_gunmap_gpu_mem, - amdgpu_cgs_kmap_gpu_mem, - amdgpu_cgs_kunmap_gpu_mem, - amdgpu_cgs_read_register, - amdgpu_cgs_write_register, - amdgpu_cgs_read_ind_register, - amdgpu_cgs_write_ind_register, - amdgpu_cgs_read_pci_config_byte, - amdgpu_cgs_read_pci_config_word, - amdgpu_cgs_read_pci_config_dword, - amdgpu_cgs_write_pci_config_byte, - amdgpu_cgs_write_pci_config_word, - amdgpu_cgs_write_pci_config_dword, - amdgpu_cgs_get_pci_resource, - amdgpu_cgs_atom_get_data_table, - amdgpu_cgs_atom_get_cmd_table_revs, - amdgpu_cgs_atom_exec_cmd_table, - amdgpu_cgs_create_pm_request, - amdgpu_cgs_destroy_pm_request, - amdgpu_cgs_set_pm_request, - amdgpu_cgs_pm_request_clock, - amdgpu_cgs_pm_request_engine, - amdgpu_cgs_pm_query_clock_limits, - amdgpu_cgs_set_camera_voltages, - amdgpu_cgs_get_firmware_info, - amdgpu_cgs_rel_firmware, - amdgpu_cgs_set_powergating_state, - amdgpu_cgs_set_clockgating_state, - amdgpu_cgs_get_active_displays_info, - amdgpu_cgs_notify_dpm_enabled, - amdgpu_cgs_call_acpi_method, - amdgpu_cgs_query_system_info, - amdgpu_cgs_is_virtualization_enabled + .gpu_mem_info = amdgpu_cgs_gpu_mem_info, + .gmap_kmem = amdgpu_cgs_gmap_kmem, + .gunmap_kmem = amdgpu_cgs_gunmap_kmem, + .alloc_gpu_mem = amdgpu_cgs_alloc_gpu_mem, + .free_gpu_mem = amdgpu_cgs_free_gpu_mem, + .gmap_gpu_mem = amdgpu_cgs_gmap_gpu_mem, + .gunmap_gpu_mem = amdgpu_cgs_gunmap_gpu_mem, + .kmap_gpu_mem = amdgpu_cgs_kmap_gpu_mem, + .kunmap_gpu_mem = amdgpu_cgs_kunmap_gpu_mem, + .read_register = amdgpu_cgs_read_register, + .write_register = amdgpu_cgs_write_register, + .read_ind_register = amdgpu_cgs_read_ind_register, + .write_ind_register = amdgpu_cgs_write_ind_register, + .read_pci_config_byte = amdgpu_cgs_read_pci_config_byte, + .read_pci_config_word = amdgpu_cgs_read_pci_config_word, + .read_pci_config_dword = amdgpu_cgs_read_pci_config_dword, + .write_pci_config_byte = amdgpu_cgs_write_pci_config_byte, + .write_pci_config_word = amdgpu_cgs_write_pci_config_word, + .write_pci_config_dword = amdgpu_cgs_write_pci_config_dword, + .get_pci_resource = amdgpu_cgs_get_pci_resource, + .atom_get_data_table = amdgpu_cgs_atom_get_data_table, + .atom_get_cmd_table_revs = amdgpu_cgs_atom_get_cmd_table_revs, + .atom_exec_cmd_table = amdgpu_cgs_atom_exec_cmd_table, + .create_pm_request = amdgpu_cgs_create_pm_request, + .destroy_pm_request = amdgpu_cgs_destroy_pm_request, + .set_pm_request = amdgpu_cgs_set_pm_request, + .pm_request_clock = amdgpu_cgs_pm_request_clock, + .pm_request_engine = amdgpu_cgs_pm_request_engine, + .pm_query_clock_limits = amdgpu_cgs_pm_query_clock_limits, + .set_camera_voltages = amdgpu_cgs_set_camera_voltages, + .get_firmware_info = amdgpu_cgs_get_firmware_info, + .rel_firmware = amdgpu_cgs_rel_firmware, + .set_powergating_state = amdgpu_cgs_set_powergating_state, + .set_clockgating_state = amdgpu_cgs_set_clockgating_state, + .get_active_displays_info = amdgpu_cgs_get_active_displays_info, + .notify_dpm_enabled = amdgpu_cgs_notify_dpm_enabled, + .call_acpi_method = amdgpu_cgs_call_acpi_method, + .query_system_info = amdgpu_cgs_query_system_info, + .is_virtualization_enabled = amdgpu_cgs_is_virtualization_enabled, + .enter_safe_mode = amdgpu_cgs_enter_safe_mode, }; static const struct cgs_os_ops amdgpu_cgs_os_ops = { - amdgpu_cgs_add_irq_source, - amdgpu_cgs_irq_get, - amdgpu_cgs_irq_put + .add_irq_source = amdgpu_cgs_add_irq_source, + .irq_get = amdgpu_cgs_irq_get, + .irq_put = amdgpu_cgs_irq_put }; struct cgs_device *amdgpu_cgs_create_device(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 41e41f90265d..d2d0f60ff36d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -75,10 +75,10 @@ int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type, *out_ring = &adev->uvd.ring; break; case AMDGPU_HW_IP_VCE: - if (ring < 2){ + if (ring < adev->vce.num_rings){ *out_ring = &adev->vce.ring[ring]; } else { - DRM_ERROR("only two VCE rings are supported\n"); + DRM_ERROR("only %d VCE rings are supported\n", adev->vce.num_rings); return -EINVAL; } break; @@ -351,8 +351,7 @@ static u64 amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev) * submission. This can result in a debt that can stop buffer migrations * temporarily. */ -static void amdgpu_cs_report_moved_bytes(struct amdgpu_device *adev, - u64 num_bytes) +void amdgpu_cs_report_moved_bytes(struct amdgpu_device *adev, u64 num_bytes) { spin_lock(&adev->mm_stats.lock); adev->mm_stats.accum_us -= bytes_to_us(adev, num_bytes); @@ -778,6 +777,20 @@ static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p, if (r) return r; + if (amdgpu_sriov_vf(adev)) { + struct dma_fence *f; + bo_va = vm->csa_bo_va; + BUG_ON(!bo_va); + r = amdgpu_vm_bo_update(adev, bo_va, false); + if (r) + return r; + + f = bo_va->last_pt_update; + r = amdgpu_sync_fence(adev, &p->job->sync, f); + if (r) + return r; + } + if (p->bo_list) { for (i = 0; i < p->bo_list->num_entries; i++) { struct dma_fence *f; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index 400c66ba4c6b..cf0500671353 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -135,15 +135,11 @@ static int amdgpu_ctx_free(struct amdgpu_fpriv *fpriv, uint32_t id) struct amdgpu_ctx *ctx; mutex_lock(&mgr->lock); - ctx = idr_find(&mgr->ctx_handles, id); - if (ctx) { - idr_remove(&mgr->ctx_handles, id); + ctx = idr_remove(&mgr->ctx_handles, id); + if (ctx) kref_put(&ctx->refcount, amdgpu_ctx_do_release); - mutex_unlock(&mgr->lock); - return 0; - } mutex_unlock(&mgr->lock); - return -EINVAL; + return ctx ? 0 : -EINVAL; } static int amdgpu_ctx_query(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index fe3bb94fe58d..6abb238b25c9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -94,6 +94,11 @@ uint32_t amdgpu_mm_rreg(struct amdgpu_device *adev, uint32_t reg, { uint32_t ret; + if (amdgpu_sriov_runtime(adev)) { + BUG_ON(in_interrupt()); + return amdgpu_virt_kiq_rreg(adev, reg); + } + if ((reg * 4) < adev->rmmio_size && !always_indirect) ret = readl(((void __iomem *)adev->rmmio) + (reg * 4)); else { @@ -113,6 +118,11 @@ void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, { trace_amdgpu_mm_wreg(adev->pdev->device, reg, v); + if (amdgpu_sriov_runtime(adev)) { + BUG_ON(in_interrupt()); + return amdgpu_virt_kiq_wreg(adev, reg, v); + } + if ((reg * 4) < adev->rmmio_size && !always_indirect) writel(v, ((void __iomem *)adev->rmmio) + (reg * 4)); else { @@ -609,25 +619,29 @@ void amdgpu_gtt_location(struct amdgpu_device *adev, struct amdgpu_mc *mc) * GPU helpers function. */ /** - * amdgpu_card_posted - check if the hw has already been initialized + * amdgpu_need_post - check if the hw need post or not * * @adev: amdgpu_device pointer * - * Check if the asic has been initialized (all asics). - * Used at driver startup. - * Returns true if initialized or false if not. + * Check if the asic has been initialized (all asics) at driver startup + * or post is needed if hw reset is performed. + * Returns true if need or false if not. */ -bool amdgpu_card_posted(struct amdgpu_device *adev) +bool amdgpu_need_post(struct amdgpu_device *adev) { uint32_t reg; + if (adev->has_hw_reset) { + adev->has_hw_reset = false; + return true; + } /* then check MEM_SIZE, in case the crtcs are off */ reg = RREG32(mmCONFIG_MEMSIZE); if (reg) - return true; + return false; - return false; + return true; } @@ -655,7 +669,7 @@ static bool amdgpu_vpost_needed(struct amdgpu_device *adev) return true; } } - return !amdgpu_card_posted(adev); + return amdgpu_need_post(adev); } /** @@ -885,7 +899,7 @@ static int amdgpu_atombios_init(struct amdgpu_device *adev) atom_card_info->ioreg_read = cail_ioreg_read; atom_card_info->ioreg_write = cail_ioreg_write; } else { - DRM_ERROR("Unable to find PCI I/O BAR; using MMIO for ATOM IIO\n"); + DRM_INFO("PCI I/O BAR is not found. Using MMIO to access ATOM BIOS\n"); atom_card_info->ioreg_read = cail_reg_read; atom_card_info->ioreg_write = cail_reg_write; } @@ -1131,6 +1145,18 @@ int amdgpu_set_powergating_state(struct amdgpu_device *adev, return r; } +void amdgpu_get_clockgating_state(struct amdgpu_device *adev, u32 *flags) +{ + int i; + + for (i = 0; i < adev->num_ip_blocks; i++) { + if (!adev->ip_blocks[i].status.valid) + continue; + if (adev->ip_blocks[i].version->funcs->get_clockgating_state) + adev->ip_blocks[i].version->funcs->get_clockgating_state((void *)adev, flags); + } +} + int amdgpu_wait_for_idle(struct amdgpu_device *adev, enum amd_ip_block_type block_type) { @@ -1235,7 +1261,8 @@ static void amdgpu_device_enable_virtual_display(struct amdgpu_device *adev) pciaddstr_tmp = pciaddstr; while ((pciaddname_tmp = strsep(&pciaddstr_tmp, ";"))) { pciaddname = strsep(&pciaddname_tmp, ","); - if (!strcmp(pci_address_name, pciaddname)) { + if (!strcmp("all", pciaddname) + || !strcmp(pci_address_name, pciaddname)) { long num_crtc; int res = -1; @@ -1323,6 +1350,12 @@ static int amdgpu_early_init(struct amdgpu_device *adev) return -EINVAL; } + if (amdgpu_sriov_vf(adev)) { + r = amdgpu_virt_request_full_gpu(adev, true); + if (r) + return r; + } + for (i = 0; i < adev->num_ip_blocks; i++) { if ((amdgpu_ip_block_mask & (1 << i)) == 0) { DRM_ERROR("disabled ip block: %d\n", i); @@ -1383,6 +1416,15 @@ static int amdgpu_init(struct amdgpu_device *adev) return r; } adev->ip_blocks[i].status.hw = true; + + /* right after GMC hw init, we create CSA */ + if (amdgpu_sriov_vf(adev)) { + r = amdgpu_allocate_static_csa(adev); + if (r) { + DRM_ERROR("allocate CSA failed %d\n", r); + return r; + } + } } } @@ -1516,6 +1558,11 @@ static int amdgpu_fini(struct amdgpu_device *adev) adev->ip_blocks[i].status.late_initialized = false; } + if (amdgpu_sriov_vf(adev)) { + amdgpu_bo_free_kernel(&adev->virt.csa_obj, &adev->virt.csa_vmid0_addr, NULL); + amdgpu_virt_release_full_gpu(adev, false); + } + return 0; } @@ -1523,6 +1570,9 @@ int amdgpu_suspend(struct amdgpu_device *adev) { int i, r; + if (amdgpu_sriov_vf(adev)) + amdgpu_virt_request_full_gpu(adev, false); + /* ungate SMC block first */ r = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_SMC, AMD_CG_STATE_UNGATE); @@ -1551,6 +1601,9 @@ int amdgpu_suspend(struct amdgpu_device *adev) } } + if (amdgpu_sriov_vf(adev)) + amdgpu_virt_release_full_gpu(adev, false); + return 0; } @@ -1575,7 +1628,7 @@ static int amdgpu_resume(struct amdgpu_device *adev) static void amdgpu_device_detect_sriov_bios(struct amdgpu_device *adev) { if (amdgpu_atombios_has_gpu_virtualization_table(adev)) - adev->virtualization.virtual_caps |= AMDGPU_SRIOV_CAPS_SRIOV_VBIOS; + adev->virt.caps |= AMDGPU_SRIOV_CAPS_SRIOV_VBIOS; } /** @@ -1605,7 +1658,6 @@ int amdgpu_device_init(struct amdgpu_device *adev, adev->pdev = pdev; adev->flags = flags; adev->asic_type = flags & AMD_ASIC_MASK; - adev->is_atom_bios = false; adev->usec_timeout = AMDGPU_MAX_USEC_TIMEOUT; adev->mc.gtt_size = 512 * 1024 * 1024; adev->accel_working = false; @@ -1695,7 +1747,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, } } if (adev->rio_mem == NULL) - DRM_ERROR("Unable to find PCI I/O BAR\n"); + DRM_INFO("PCI I/O BAR is not found.\n"); /* early init functions */ r = amdgpu_early_init(adev); @@ -1720,12 +1772,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, r = -EINVAL; goto failed; } - /* Must be an ATOMBIOS */ - if (!adev->is_atom_bios) { - dev_err(adev->dev, "Expecting atombios for GPU\n"); - r = -EINVAL; - goto failed; - } + r = amdgpu_atombios_init(adev); if (r) { dev_err(adev->dev, "amdgpu_atombios_init failed\n"); @@ -1852,8 +1899,6 @@ failed: return r; } -static void amdgpu_debugfs_remove_files(struct amdgpu_device *adev); - /** * amdgpu_device_fini - tear down the driver * @@ -1893,7 +1938,6 @@ void amdgpu_device_fini(struct amdgpu_device *adev) if (adev->asic_type >= CHIP_BONAIRE) amdgpu_doorbell_fini(adev); amdgpu_debugfs_regs_cleanup(adev); - amdgpu_debugfs_remove_files(adev); } @@ -2031,7 +2075,7 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon) amdgpu_atombios_scratch_regs_restore(adev); /* post card */ - if (!amdgpu_card_posted(adev) || !resume) { + if (amdgpu_need_post(adev)) { r = amdgpu_atom_asic_init(adev->mode_info.atom_context); if (r) DRM_ERROR("amdgpu asic init failed\n"); @@ -2252,6 +2296,9 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev) int resched; bool need_full_reset; + if (amdgpu_sriov_vf(adev)) + return 0; + if (!amdgpu_check_soft_reset(adev)) { DRM_INFO("No hardware hang detected. Did some blocks stall?\n"); return 0; @@ -2507,19 +2554,6 @@ int amdgpu_debugfs_add_files(struct amdgpu_device *adev, return 0; } -static void amdgpu_debugfs_remove_files(struct amdgpu_device *adev) -{ -#if defined(CONFIG_DEBUG_FS) - unsigned i; - - for (i = 0; i < adev->debugfs_count; i++) { - drm_debugfs_remove_files(adev->debugfs[i].files, - adev->debugfs[i].num_files, - adev->ddev->primary); - } -#endif -} - #if defined(CONFIG_DEBUG_FS) static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf, @@ -2853,7 +2887,7 @@ static ssize_t amdgpu_debugfs_gca_config_read(struct file *f, char __user *buf, return -ENOMEM; /* version, increment each time something is added */ - config[no_regs++] = 2; + config[no_regs++] = 3; config[no_regs++] = adev->gfx.config.max_shader_engines; config[no_regs++] = adev->gfx.config.max_tile_pipes; config[no_regs++] = adev->gfx.config.max_cu_per_sh; @@ -2887,6 +2921,12 @@ static ssize_t amdgpu_debugfs_gca_config_read(struct file *f, char __user *buf, config[no_regs++] = adev->family; config[no_regs++] = adev->external_rev_id; + /* rev==3 */ + config[no_regs++] = adev->pdev->device; + config[no_regs++] = adev->pdev->revision; + config[no_regs++] = adev->pdev->subsystem_device; + config[no_regs++] = adev->pdev->subsystem_vendor; + while (size && (*pos < no_regs * 4)) { uint32_t value; @@ -3153,10 +3193,6 @@ int amdgpu_debugfs_init(struct drm_minor *minor) { return 0; } - -void amdgpu_debugfs_cleanup(struct drm_minor *minor) -{ -} #else static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index 581601ca6b89..39fc388f222a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -138,10 +138,52 @@ static void amdgpu_unpin_work_func(struct work_struct *__work) kfree(work); } -int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags, uint32_t target) + +static void amdgpu_flip_work_cleanup(struct amdgpu_flip_work *work) +{ + int i; + + amdgpu_bo_unref(&work->old_abo); + dma_fence_put(work->excl); + for (i = 0; i < work->shared_count; ++i) + dma_fence_put(work->shared[i]); + kfree(work->shared); + kfree(work); +} + +static void amdgpu_flip_cleanup_unreserve(struct amdgpu_flip_work *work, + struct amdgpu_bo *new_abo) +{ + amdgpu_bo_unreserve(new_abo); + amdgpu_flip_work_cleanup(work); +} + +static void amdgpu_flip_cleanup_unpin(struct amdgpu_flip_work *work, + struct amdgpu_bo *new_abo) +{ + if (unlikely(amdgpu_bo_unpin(new_abo) != 0)) + DRM_ERROR("failed to unpin new abo in error path\n"); + amdgpu_flip_cleanup_unreserve(work, new_abo); +} + +void amdgpu_crtc_cleanup_flip_ctx(struct amdgpu_flip_work *work, + struct amdgpu_bo *new_abo) +{ + if (unlikely(amdgpu_bo_reserve(new_abo, false) != 0)) { + DRM_ERROR("failed to reserve new abo in error path\n"); + amdgpu_flip_work_cleanup(work); + return; + } + amdgpu_flip_cleanup_unpin(work, new_abo); +} + +int amdgpu_crtc_prepare_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags, + uint32_t target, + struct amdgpu_flip_work **work_p, + struct amdgpu_bo **new_abo_p) { struct drm_device *dev = crtc->dev; struct amdgpu_device *adev = dev->dev_private; @@ -154,7 +196,7 @@ int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc, unsigned long flags; u64 tiling_flags; u64 base; - int i, r; + int r; work = kzalloc(sizeof *work, GFP_KERNEL); if (work == NULL) @@ -189,7 +231,6 @@ int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc, r = amdgpu_bo_pin(new_abo, AMDGPU_GEM_DOMAIN_VRAM, &base); if (unlikely(r != 0)) { - r = -EINVAL; DRM_ERROR("failed to pin new abo buffer before flip\n"); goto unreserve; } @@ -216,41 +257,79 @@ int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc, spin_unlock_irqrestore(&crtc->dev->event_lock, flags); r = -EBUSY; goto pflip_cleanup; + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + *work_p = work; + *new_abo_p = new_abo; + + return 0; + +pflip_cleanup: + amdgpu_crtc_cleanup_flip_ctx(work, new_abo); + return r; +unpin: + amdgpu_flip_cleanup_unpin(work, new_abo); + return r; + +unreserve: + amdgpu_flip_cleanup_unreserve(work, new_abo); + return r; + +cleanup: + amdgpu_flip_work_cleanup(work); + return r; + +} + +void amdgpu_crtc_submit_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct amdgpu_flip_work *work, + struct amdgpu_bo *new_abo) +{ + unsigned long flags; + struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + + spin_lock_irqsave(&crtc->dev->event_lock, flags); amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING; amdgpu_crtc->pflip_works = work; - - DRM_DEBUG_DRIVER("crtc:%d[%p], pflip_stat:AMDGPU_FLIP_PENDING, work: %p,\n", - amdgpu_crtc->crtc_id, amdgpu_crtc, work); /* update crtc fb */ crtc->primary->fb = fb; spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + DRM_DEBUG_DRIVER( + "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_PENDING, work: %p,\n", + amdgpu_crtc->crtc_id, amdgpu_crtc, work); + amdgpu_flip_work_func(&work->flip_work.work); - return 0; +} -pflip_cleanup: - if (unlikely(amdgpu_bo_reserve(new_abo, false) != 0)) { - DRM_ERROR("failed to reserve new abo in error path\n"); - goto cleanup; - } -unpin: - if (unlikely(amdgpu_bo_unpin(new_abo) != 0)) { - DRM_ERROR("failed to unpin new abo in error path\n"); - } -unreserve: - amdgpu_bo_unreserve(new_abo); +int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags, + uint32_t target) +{ + struct amdgpu_bo *new_abo; + struct amdgpu_flip_work *work; + int r; -cleanup: - amdgpu_bo_unref(&work->old_abo); - dma_fence_put(work->excl); - for (i = 0; i < work->shared_count; ++i) - dma_fence_put(work->shared[i]); - kfree(work->shared); - kfree(work); + r = amdgpu_crtc_prepare_flip(crtc, + fb, + event, + page_flip_flags, + target, + &work, + &new_abo); + if (r) + return r; - return r; + amdgpu_crtc_submit_flip(crtc, fb, work, new_abo); + + return 0; } int amdgpu_crtc_set_config(struct drm_mode_set *set) @@ -508,7 +587,7 @@ amdgpu_framebuffer_init(struct drm_device *dev, { int ret; rfb->obj = obj; - drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &rfb->base, mode_cmd); ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs); if (ret) { rfb->obj = NULL; @@ -582,12 +661,10 @@ int amdgpu_modeset_create_props(struct amdgpu_device *adev) { int sz; - if (adev->is_atom_bios) { - adev->mode_info.coherent_mode_property = - drm_property_create_range(adev->ddev, 0 , "coherent", 0, 1); - if (!adev->mode_info.coherent_mode_property) - return -ENOMEM; - } + adev->mode_info.coherent_mode_property = + drm_property_create_range(adev->ddev, 0 , "coherent", 0, 1); + if (!adev->mode_info.coherent_mode_property) + return -ENOMEM; adev->mode_info.load_detect_property = drm_property_create_range(adev->ddev, 0, "load detection", 0, 1); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h index 955d6f21e2b3..fa2b55681422 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h @@ -241,13 +241,6 @@ enum amdgpu_pcie_gen { AMDGPU_PCIE_GEN_INVALID = 0xffff }; -enum amdgpu_dpm_forced_level { - AMDGPU_DPM_FORCED_LEVEL_AUTO = 0, - AMDGPU_DPM_FORCED_LEVEL_LOW = 1, - AMDGPU_DPM_FORCED_LEVEL_HIGH = 2, - AMDGPU_DPM_FORCED_LEVEL_MANUAL = 3, -}; - struct amdgpu_dpm_funcs { int (*get_temperature)(struct amdgpu_device *adev); int (*pre_set_power_state)(struct amdgpu_device *adev); @@ -258,7 +251,7 @@ struct amdgpu_dpm_funcs { u32 (*get_mclk)(struct amdgpu_device *adev, bool low); void (*print_power_state)(struct amdgpu_device *adev, struct amdgpu_ps *ps); void (*debugfs_print_current_performance_level)(struct amdgpu_device *adev, struct seq_file *m); - int (*force_performance_level)(struct amdgpu_device *adev, enum amdgpu_dpm_forced_level level); + int (*force_performance_level)(struct amdgpu_device *adev, enum amd_dpm_forced_level level); bool (*vblank_too_short)(struct amdgpu_device *adev); void (*powergate_uvd)(struct amdgpu_device *adev, bool gate); void (*powergate_vce)(struct amdgpu_device *adev, bool gate); @@ -353,9 +346,6 @@ struct amdgpu_dpm_funcs { #define amdgpu_dpm_get_current_power_state(adev) \ (adev)->powerplay.pp_funcs->get_current_power_state((adev)->powerplay.pp_handle) -#define amdgpu_dpm_get_performance_level(adev) \ - (adev)->powerplay.pp_funcs->get_performance_level((adev)->powerplay.pp_handle) - #define amdgpu_dpm_get_pp_num_states(adev, data) \ (adev)->powerplay.pp_funcs->get_pp_num_states((adev)->powerplay.pp_handle, data) @@ -393,6 +383,11 @@ struct amdgpu_dpm_funcs { (adev)->powerplay.pp_funcs->get_vce_clock_state((adev)->powerplay.pp_handle, (i)) : \ (adev)->pm.funcs->get_vce_clock_state((adev), (i))) +#define amdgpu_dpm_get_performance_level(adev) \ + ((adev)->pp_enabled ? \ + (adev)->powerplay.pp_funcs->get_performance_level((adev)->powerplay.pp_handle) : \ + (adev)->pm.dpm.forced_level) + struct amdgpu_dpm { struct amdgpu_ps *ps; /* number of valid power states */ @@ -440,7 +435,7 @@ struct amdgpu_dpm { /* thermal handling */ struct amdgpu_dpm_thermal thermal; /* forced levels */ - enum amdgpu_dpm_forced_level forced_level; + enum amd_dpm_forced_level forced_level; }; struct amdgpu_pm { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 2534adaebe30..75fc376ba735 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -90,7 +90,6 @@ int amdgpu_vram_page_split = 1024; int amdgpu_exp_hw_support = 0; int amdgpu_sched_jobs = 32; int amdgpu_sched_hw_submission = 2; -int amdgpu_powerplay = -1; int amdgpu_no_evict = 0; int amdgpu_direct_gma_size = 0; unsigned amdgpu_pcie_gen_cap = 0; @@ -179,9 +178,6 @@ module_param_named(sched_jobs, amdgpu_sched_jobs, int, 0444); MODULE_PARM_DESC(sched_hw_submission, "the max number of HW submissions (default 2)"); module_param_named(sched_hw_submission, amdgpu_sched_hw_submission, int, 0444); -MODULE_PARM_DESC(powerplay, "Powerplay component (1 = enable, 0 = disable, -1 = auto (default))"); -module_param_named(powerplay, amdgpu_powerplay, int, 0444); - MODULE_PARM_DESC(ppfeaturemask, "all power features enabled (default))"); module_param_named(ppfeaturemask, amdgpu_pp_feature_mask, int, 0444); @@ -686,7 +682,6 @@ static struct drm_driver kms_driver = { DRIVER_USE_AGP | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER | DRIVER_MODESET, - .dev_priv_size = 0, .load = amdgpu_driver_load_kms, .open = amdgpu_driver_open_kms, .preclose = amdgpu_driver_preclose_kms, @@ -701,7 +696,6 @@ static struct drm_driver kms_driver = { .get_scanout_position = amdgpu_get_crtc_scanoutpos, #if defined(CONFIG_DEBUG_FS) .debugfs_init = amdgpu_debugfs_init, - .debugfs_cleanup = amdgpu_debugfs_cleanup, #endif .irq_preinstall = amdgpu_irq_preinstall, .irq_postinstall = amdgpu_irq_postinstall, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index 24629bec181a..36ce3cac81ba 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -245,7 +245,7 @@ static int amdgpufb_create(struct drm_fb_helper *helper, strcpy(info->fix.id, "amdgpudrmfb"); - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &amdgpufb_ops; @@ -272,7 +272,7 @@ static int amdgpufb_create(struct drm_fb_helper *helper, DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); DRM_INFO("vram apper at 0x%lX\n", (unsigned long)adev->mc.aper_base); DRM_INFO("size %lu\n", (unsigned long)amdgpu_bo_size(abo)); - DRM_INFO("fb depth is %d\n", fb->depth); + DRM_INFO("fb depth is %d\n", fb->format->depth); DRM_INFO(" pitch is %d\n", fb->pitches[0]); vga_switcheroo_client_fb_set(adev->ddev->pdev, info); @@ -374,7 +374,6 @@ int amdgpu_fbdev_init(struct amdgpu_device *adev) &amdgpu_fb_helper_funcs); ret = drm_fb_helper_init(adev->ddev, &rfbdev->helper, - adev->mode_info.num_crtc, AMDGPUFB_CONN_LIMIT); if (ret) { kfree(rfbdev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index cd62f6ffde2a..51d759463384 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -471,12 +471,15 @@ out: static int amdgpu_gem_va_check(void *param, struct amdgpu_bo *bo) { - unsigned domain = amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type); - /* if anything is swapped out don't swap it in here, just abort and wait for the next CS */ + if (!amdgpu_bo_gpu_accessible(bo)) + return -ERESTARTSYS; + + if (bo->shadow && !amdgpu_bo_gpu_accessible(bo->shadow)) + return -ERESTARTSYS; - return domain == AMDGPU_GEM_DOMAIN_CPU ? -ERESTARTSYS : 0; + return 0; } /** @@ -484,62 +487,44 @@ static int amdgpu_gem_va_check(void *param, struct amdgpu_bo *bo) * * @adev: amdgpu_device pointer * @bo_va: bo_va to update + * @list: validation list + * @operation: map or unmap * - * Update the bo_va directly after setting it's address. Errors are not + * Update the bo_va directly after setting its address. Errors are not * vital here, so they are not reported back to userspace. */ static void amdgpu_gem_va_update_vm(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, + struct list_head *list, uint32_t operation) { - struct ttm_validate_buffer tv, *entry; - struct amdgpu_bo_list_entry vm_pd; - struct ww_acquire_ctx ticket; - struct list_head list, duplicates; - unsigned domain; - int r; - - INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&duplicates); - - tv.bo = &bo_va->bo->tbo; - tv.shared = true; - list_add(&tv.head, &list); - - amdgpu_vm_get_pd_bo(bo_va->vm, &list, &vm_pd); - - /* Provide duplicates to avoid -EALREADY */ - r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates); - if (r) - goto error_print; - - list_for_each_entry(entry, &list, head) { - domain = amdgpu_mem_type_to_domain(entry->bo->mem.mem_type); - /* if anything is swapped out don't swap it in here, - just abort and wait for the next CS */ - if (domain == AMDGPU_GEM_DOMAIN_CPU) - goto error_unreserve; + struct ttm_validate_buffer *entry; + int r = -ERESTARTSYS; + + list_for_each_entry(entry, list, head) { + struct amdgpu_bo *bo = + container_of(entry->bo, struct amdgpu_bo, tbo); + if (amdgpu_gem_va_check(NULL, bo)) + goto error; } + r = amdgpu_vm_validate_pt_bos(adev, bo_va->vm, amdgpu_gem_va_check, NULL); if (r) - goto error_unreserve; + goto error; r = amdgpu_vm_update_page_directory(adev, bo_va->vm); if (r) - goto error_unreserve; + goto error; r = amdgpu_vm_clear_freed(adev, bo_va->vm); if (r) - goto error_unreserve; + goto error; if (operation == AMDGPU_VA_OP_MAP) r = amdgpu_vm_bo_update(adev, bo_va, false); -error_unreserve: - ttm_eu_backoff_reservation(&ticket, &list); - -error_print: +error: if (r && r != -ERESTARTSYS) DRM_ERROR("Couldn't update BO_VA (%d)\n", r); } @@ -556,7 +541,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct amdgpu_bo_list_entry vm_pd; struct ttm_validate_buffer tv; struct ww_acquire_ctx ticket; - struct list_head list, duplicates; + struct list_head list; uint32_t invalid_flags, va_flags = 0; int r = 0; @@ -594,14 +579,13 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, return -ENOENT; abo = gem_to_amdgpu_bo(gobj); INIT_LIST_HEAD(&list); - INIT_LIST_HEAD(&duplicates); tv.bo = &abo->tbo; - tv.shared = true; + tv.shared = false; list_add(&tv.head, &list); amdgpu_vm_get_pd_bo(&fpriv->vm, &list, &vm_pd); - r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates); + r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); if (r) { drm_gem_object_unreference_unlocked(gobj); return r; @@ -632,10 +616,10 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, default: break; } - ttm_eu_backoff_reservation(&ticket, &list); if (!r && !(args->flags & AMDGPU_VM_DELAY_UPDATE) && !amdgpu_vm_debug) - amdgpu_gem_va_update_vm(adev, bo_va, args->operation); + amdgpu_gem_va_update_vm(adev, bo_va, &list, args->operation); + ttm_eu_backoff_reservation(&ticket, &list); drm_gem_object_unreference_unlocked(gobj); return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 01a42b6a69a4..19943356cca7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -42,12 +42,12 @@ int amdgpu_gfx_scratch_get(struct amdgpu_device *adev, uint32_t *reg) { int i; - for (i = 0; i < adev->gfx.scratch.num_reg; i++) { - if (adev->gfx.scratch.free[i]) { - adev->gfx.scratch.free[i] = false; - *reg = adev->gfx.scratch.reg[i]; - return 0; - } + i = ffs(adev->gfx.scratch.free_mask); + if (i != 0 && i <= adev->gfx.scratch.num_reg) { + i--; + adev->gfx.scratch.free_mask &= ~(1u << i); + *reg = adev->gfx.scratch.reg_base + i; + return 0; } return -EINVAL; } @@ -62,14 +62,7 @@ int amdgpu_gfx_scratch_get(struct amdgpu_device *adev, uint32_t *reg) */ void amdgpu_gfx_scratch_free(struct amdgpu_device *adev, uint32_t reg) { - int i; - - for (i = 0; i < adev->gfx.scratch.num_reg; i++) { - if (adev->gfx.scratch.reg[i] == reg) { - adev->gfx.scratch.free[i] = true; - return; - } - } + adev->gfx.scratch.free_mask |= 1u << (reg - adev->gfx.scratch.reg_base); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index 00f46b0e076d..0335c2f331e9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -97,8 +97,7 @@ int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man, { struct amdgpu_gtt_mgr *mgr = man->priv; struct drm_mm_node *node = mem->mm_node; - enum drm_mm_search_flags sflags = DRM_MM_SEARCH_BEST; - enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; + enum drm_mm_insert_mode mode; unsigned long fpfn, lpfn; int r; @@ -115,15 +114,14 @@ int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man, else lpfn = man->size; - if (place && place->flags & TTM_PL_FLAG_TOPDOWN) { - sflags = DRM_MM_SEARCH_BELOW; - aflags = DRM_MM_CREATE_TOP; - } + mode = DRM_MM_INSERT_BEST; + if (place && place->flags & TTM_PL_FLAG_TOPDOWN) + mode = DRM_MM_INSERT_HIGH; spin_lock(&mgr->lock); - r = drm_mm_insert_node_in_range_generic(&mgr->mm, node, mem->num_pages, - mem->page_alignment, 0, - fpfn, lpfn, sflags, aflags); + r = drm_mm_insert_node_in_range(&mgr->mm, node, + mem->num_pages, mem->page_alignment, 0, + fpfn, lpfn, mode); spin_unlock(&mgr->lock); if (!r) { @@ -235,16 +233,17 @@ static void amdgpu_gtt_mgr_debug(struct ttm_mem_type_manager *man, const char *prefix) { struct amdgpu_gtt_mgr *mgr = man->priv; + struct drm_printer p = drm_debug_printer(prefix); spin_lock(&mgr->lock); - drm_mm_debug_table(&mgr->mm, prefix); + drm_mm_print(&mgr->mm, &p); spin_unlock(&mgr->lock); } const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func = { - amdgpu_gtt_mgr_init, - amdgpu_gtt_mgr_fini, - amdgpu_gtt_mgr_new, - amdgpu_gtt_mgr_del, - amdgpu_gtt_mgr_debug + .init = amdgpu_gtt_mgr_init, + .takedown = amdgpu_gtt_mgr_fini, + .get_node = amdgpu_gtt_mgr_new, + .put_node = amdgpu_gtt_mgr_del, + .debug = amdgpu_gtt_mgr_debug }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c index 91d367399956..f2739995c335 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c @@ -231,8 +231,7 @@ void amdgpu_i2c_init(struct amdgpu_device *adev) if (amdgpu_hw_i2c) DRM_INFO("hw_i2c forced on, you may experience display detection problems!\n"); - if (adev->is_atom_bios) - amdgpu_atombios_i2c_init(adev); + amdgpu_atombios_i2c_init(adev); } /* remove all the buses */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c index 216a9572d946..e02a70dd37b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c @@ -116,8 +116,8 @@ void amdgpu_ib_free(struct amdgpu_device *adev, struct amdgpu_ib *ib, * to SI there was just a DE IB. */ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs, - struct amdgpu_ib *ibs, struct dma_fence *last_vm_update, - struct amdgpu_job *job, struct dma_fence **f) + struct amdgpu_ib *ibs, struct amdgpu_job *job, + struct dma_fence **f) { struct amdgpu_device *adev = ring->adev; struct amdgpu_ib *ib = &ibs[0]; @@ -175,15 +175,15 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs, if (ring->funcs->emit_hdp_flush) amdgpu_ring_emit_hdp_flush(ring); - /* always set cond_exec_polling to CONTINUE */ - *ring->cond_exe_cpu_addr = 1; - skip_preamble = ring->current_ctx == fence_ctx; need_ctx_switch = ring->current_ctx != fence_ctx; if (job && ring->funcs->emit_cntxcntl) { if (need_ctx_switch) status |= AMDGPU_HAVE_CTX_SWITCH; status |= job->preamble_status; + + if (vm) + status |= AMDGPU_VM_DOMAIN; amdgpu_ring_emit_cntxcntl(ring, status); } @@ -193,7 +193,8 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs, /* drop preamble IBs if we don't have a context switch */ if ((ib->flags & AMDGPU_IB_FLAG_PREAMBLE) && skip_preamble && - !(status & AMDGPU_PREAMBLE_IB_PRESENT_FIRST)) + !(status & AMDGPU_PREAMBLE_IB_PRESENT_FIRST) && + !amdgpu_sriov_vf(adev)) /* for SRIOV preemption, Preamble CE ib must be inserted anyway */ continue; amdgpu_ring_emit_ib(ring, ib, job ? job->vm_id : 0, @@ -223,7 +224,7 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs, amdgpu_ring_patch_cond_exec(ring, patch_offset); ring->current_ctx = fence_ctx; - if (ring->funcs->emit_switch_buffer) + if (vm && ring->funcs->emit_switch_buffer) amdgpu_ring_emit_switch_buffer(ring); amdgpu_ring_commit(ring); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index fb902932f571..e63ece049b05 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -61,10 +61,8 @@ static void amdgpu_hotplug_work_func(struct work_struct *work) struct drm_connector *connector; mutex_lock(&mode_config->mutex); - if (mode_config->num_connector) { - list_for_each_entry(connector, &mode_config->connector_list, head) - amdgpu_connector_hotplug(connector); - } + list_for_each_entry(connector, &mode_config->connector_list, head) + amdgpu_connector_hotplug(connector); mutex_unlock(&mode_config->mutex); /* Just fire off a uevent and let userspace tell us what to do */ drm_helper_hpd_irq_event(dev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index a0de6286c453..86a12424c162 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -170,8 +170,7 @@ static struct dma_fence *amdgpu_job_run(struct amd_sched_job *sched_job) BUG_ON(amdgpu_sync_peek_fence(&job->sync, NULL)); trace_amdgpu_sched_run_job(job); - r = amdgpu_ib_schedule(job->ring, job->num_ibs, job->ibs, - job->sync.last_vm_update, job, &fence); + r = amdgpu_ib_schedule(job->ring, job->num_ibs, job->ibs, job, &fence); if (r) DRM_ERROR("Error scheduling IBs (%d)\n", r); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 9af87eaf8ee3..61d94c745672 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -50,16 +50,19 @@ static inline bool amdgpu_has_atpx(void) { return false; } * This is the main unload function for KMS (all asics). * Returns 0 on success. */ -int amdgpu_driver_unload_kms(struct drm_device *dev) +void amdgpu_driver_unload_kms(struct drm_device *dev) { struct amdgpu_device *adev = dev->dev_private; if (adev == NULL) - return 0; + return; if (adev->rmmio == NULL) goto done_free; + if (amdgpu_sriov_vf(adev)) + amdgpu_virt_request_full_gpu(adev, false); + if (amdgpu_device_is_px(dev)) { pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); @@ -74,7 +77,6 @@ int amdgpu_driver_unload_kms(struct drm_device *dev) done_free: kfree(adev); dev->dev_private = NULL; - return 0; } /** @@ -139,6 +141,9 @@ int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags) pm_runtime_put_autosuspend(dev->dev); } + if (amdgpu_sriov_vf(adev)) + amdgpu_virt_release_full_gpu(adev, true); + out: if (r) { /* balance pm_runtime_get_sync in amdgpu_driver_unload_kms */ @@ -570,6 +575,27 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file return -EINVAL; } } + case AMDGPU_INFO_NUM_HANDLES: { + struct drm_amdgpu_info_num_handles handle; + + switch (info->query_hw_ip.type) { + case AMDGPU_HW_IP_UVD: + /* Starting Polaris, we support unlimited UVD handles */ + if (adev->asic_type < CHIP_POLARIS10) { + handle.uvd_max_handles = adev->uvd.max_handles; + handle.uvd_used_handles = amdgpu_uvd_used_handles(adev); + + return copy_to_user(out, &handle, + min((size_t)size, sizeof(handle))) ? -EFAULT : 0; + } else { + return -ENODATA; + } + + break; + default: + return -EINVAL; + } + } default: DRM_DEBUG_KMS("Invalid request %d\n", info->query); return -EINVAL; @@ -629,6 +655,12 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) goto out_suspend; } + if (amdgpu_sriov_vf(adev)) { + r = amdgpu_map_static_csa(adev, &fpriv->vm); + if (r) + goto out_suspend; + } + mutex_init(&fpriv->bo_list_lock); idr_init(&fpriv->bo_list_handles); @@ -667,6 +699,14 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev, amdgpu_uvd_free_handles(adev, file_priv); amdgpu_vce_free_handles(adev, file_priv); + if (amdgpu_sriov_vf(adev)) { + /* TODO: how to handle reserve failure */ + BUG_ON(amdgpu_bo_reserve(adev->virt.csa_obj, false)); + amdgpu_vm_bo_rmv(adev, fpriv->vm.csa_bo_va); + fpriv->vm.csa_bo_va = NULL; + amdgpu_bo_unreserve(adev->virt.csa_obj); + } + amdgpu_vm_fini(adev, &fpriv->vm); idr_for_each_entry(&fpriv->bo_list_handles, list, handle) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 202b4176b74e..c12497bd3889 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -32,6 +32,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_dp_helper.h> #include <drm/drm_fixed.h> #include <drm/drm_crtc_helper.h> @@ -594,6 +595,21 @@ int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags, uint32_t target); +void amdgpu_crtc_cleanup_flip_ctx(struct amdgpu_flip_work *work, + struct amdgpu_bo *new_abo); +int amdgpu_crtc_prepare_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags, + uint32_t target, + struct amdgpu_flip_work **work, + struct amdgpu_bo **new_abo); + +void amdgpu_crtc_submit_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct amdgpu_flip_work *work, + struct amdgpu_bo *new_abo); + extern const struct drm_mode_config_funcs amdgpu_mode_funcs; #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index bf79b73e1538..be80a4a68d7b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -323,6 +323,7 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev, struct amdgpu_bo *bo; enum ttm_bo_type type; unsigned long page_align; + u64 initial_bytes_moved; size_t acc_size; int r; @@ -363,11 +364,33 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev, bo->flags = flags; +#ifdef CONFIG_X86_32 + /* XXX: Write-combined CPU mappings of GTT seem broken on 32-bit + * See https://bugs.freedesktop.org/show_bug.cgi?id=84627 + */ + bo->flags &= ~AMDGPU_GEM_CREATE_CPU_GTT_USWC; +#elif defined(CONFIG_X86) && !defined(CONFIG_X86_PAT) + /* Don't try to enable write-combining when it can't work, or things + * may be slow + * See https://bugs.freedesktop.org/show_bug.cgi?id=88758 + */ + +#ifndef CONFIG_COMPILE_TEST +#warning Please enable CONFIG_MTRR and CONFIG_X86_PAT for better performance \ + thanks to write-combining +#endif + + if (bo->flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) + DRM_INFO_ONCE("Please enable CONFIG_MTRR and CONFIG_X86_PAT for " + "better performance thanks to write-combining\n"); + bo->flags &= ~AMDGPU_GEM_CREATE_CPU_GTT_USWC; +#else /* For architectures that don't support WC memory, * mask out the WC flag from the BO */ if (!drm_arch_can_wc_memory()) bo->flags &= ~AMDGPU_GEM_CREATE_CPU_GTT_USWC; +#endif amdgpu_fill_placement_to_bo(bo, placement); /* Kernel allocation are uninterruptible */ @@ -379,12 +402,25 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev, locked = ww_mutex_trylock(&bo->tbo.ttm_resv.lock); WARN_ON(!locked); } + + initial_bytes_moved = atomic64_read(&adev->num_bytes_moved); r = ttm_bo_init(&adev->mman.bdev, &bo->tbo, size, type, &bo->placement, page_align, !kernel, NULL, acc_size, sg, resv ? resv : &bo->tbo.ttm_resv, &amdgpu_ttm_bo_destroy); - if (unlikely(r != 0)) + amdgpu_cs_report_moved_bytes(adev, + atomic64_read(&adev->num_bytes_moved) - initial_bytes_moved); + + if (unlikely(r != 0)) { + if (!resv) + ww_mutex_unlock(&bo->tbo.resv->lock); return r; + } + + bo->tbo.priority = ilog2(bo->tbo.num_pages); + if (kernel) + bo->tbo.priority *= 2; + bo->tbo.priority = min(bo->tbo.priority, (unsigned)(TTM_MAX_BO_PRIORITY - 1)); if (flags & AMDGPU_GEM_CREATE_VRAM_CLEARED && bo->tbo.mem.placement & TTM_PL_FLAG_VRAM) { @@ -408,7 +444,8 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev, return 0; fail_unreserve: - ww_mutex_unlock(&bo->tbo.resv->lock); + if (!resv) + ww_mutex_unlock(&bo->tbo.resv->lock); amdgpu_bo_unref(&bo); return r; } @@ -472,7 +509,16 @@ int amdgpu_bo_create(struct amdgpu_device *adev, return r; if (amdgpu_need_backup(adev) && (flags & AMDGPU_GEM_CREATE_SHADOW)) { + if (!resv) { + r = ww_mutex_lock(&(*bo_ptr)->tbo.resv->lock, NULL); + WARN_ON(r != 0); + } + r = amdgpu_bo_create_shadow(adev, size, byte_align, (*bo_ptr)); + + if (!resv) + ww_mutex_unlock(&(*bo_ptr)->tbo.resv->lock); + if (r) amdgpu_bo_unref(bo_ptr); } @@ -849,6 +895,7 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer, } void amdgpu_bo_move_notify(struct ttm_buffer_object *bo, + bool evict, struct ttm_mem_reg *new_mem) { struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev); @@ -861,6 +908,10 @@ void amdgpu_bo_move_notify(struct ttm_buffer_object *bo, abo = container_of(bo, struct amdgpu_bo, tbo); amdgpu_vm_bo_invalidate(adev, abo); + /* remember the eviction */ + if (evict) + atomic64_inc(&adev->num_evictions); + /* update statistics */ if (!new_mem) return; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index 5cbf59ec0f68..15a723adca76 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -114,6 +114,15 @@ static inline u64 amdgpu_bo_mmap_offset(struct amdgpu_bo *bo) return drm_vma_node_offset_addr(&bo->tbo.vma_node); } +/** + * amdgpu_bo_gpu_accessible - return whether the bo is currently in memory that + * is accessible to the GPU. + */ +static inline bool amdgpu_bo_gpu_accessible(struct amdgpu_bo *bo) +{ + return bo->tbo.mem.mem_type != TTM_PL_SYSTEM; +} + int amdgpu_bo_create(struct amdgpu_device *adev, unsigned long size, int byte_align, bool kernel, u32 domain, u64 flags, @@ -155,7 +164,8 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer, size_t buffer_size, uint32_t *metadata_size, uint64_t *flags); void amdgpu_bo_move_notify(struct ttm_buffer_object *bo, - struct ttm_mem_reg *new_mem); + bool evict, + struct ttm_mem_reg *new_mem); int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo); void amdgpu_bo_fence(struct amdgpu_bo *bo, struct dma_fence *fence, bool shared); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c index 723ae682bf25..346e80a7119b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c @@ -34,6 +34,28 @@ static int amdgpu_debugfs_pm_init(struct amdgpu_device *adev); +static const struct cg_flag_name clocks[] = { + {AMD_CG_SUPPORT_GFX_MGCG, "Graphics Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_GFX_MGLS, "Graphics Medium Grain memory Light Sleep"}, + {AMD_CG_SUPPORT_GFX_CGCG, "Graphics Coarse Grain Clock Gating"}, + {AMD_CG_SUPPORT_GFX_CGLS, "Graphics Coarse Grain memory Light Sleep"}, + {AMD_CG_SUPPORT_GFX_CGTS, "Graphics Coarse Grain Tree Shader Clock Gating"}, + {AMD_CG_SUPPORT_GFX_CGTS_LS, "Graphics Coarse Grain Tree Shader Light Sleep"}, + {AMD_CG_SUPPORT_GFX_CP_LS, "Graphics Command Processor Light Sleep"}, + {AMD_CG_SUPPORT_GFX_RLC_LS, "Graphics Run List Controller Light Sleep"}, + {AMD_CG_SUPPORT_MC_LS, "Memory Controller Light Sleep"}, + {AMD_CG_SUPPORT_MC_MGCG, "Memory Controller Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_SDMA_LS, "System Direct Memory Access Light Sleep"}, + {AMD_CG_SUPPORT_SDMA_MGCG, "System Direct Memory Access Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_BIF_LS, "Bus Interface Light Sleep"}, + {AMD_CG_SUPPORT_UVD_MGCG, "Unified Video Decoder Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_VCE_MGCG, "Video Compression Engine Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_HDP_LS, "Host Data Path Light Sleep"}, + {AMD_CG_SUPPORT_HDP_MGCG, "Host Data Path Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_ROM_MGCG, "Rom Medium Grain Clock Gating"}, + {0, NULL}, +}; + void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev) { if (adev->pp_enabled) @@ -112,28 +134,23 @@ static ssize_t amdgpu_get_dpm_forced_performance_level(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = ddev->dev_private; + enum amd_dpm_forced_level level; if ((adev->flags & AMD_IS_PX) && (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) return snprintf(buf, PAGE_SIZE, "off\n"); - if (adev->pp_enabled) { - enum amd_dpm_forced_level level; - - level = amdgpu_dpm_get_performance_level(adev); - return snprintf(buf, PAGE_SIZE, "%s\n", - (level == AMD_DPM_FORCED_LEVEL_AUTO) ? "auto" : - (level == AMD_DPM_FORCED_LEVEL_LOW) ? "low" : - (level == AMD_DPM_FORCED_LEVEL_HIGH) ? "high" : - (level == AMD_DPM_FORCED_LEVEL_MANUAL) ? "manual" : "unknown"); - } else { - enum amdgpu_dpm_forced_level level; - - level = adev->pm.dpm.forced_level; - return snprintf(buf, PAGE_SIZE, "%s\n", - (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) ? "auto" : - (level == AMDGPU_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); - } + level = amdgpu_dpm_get_performance_level(adev); + return snprintf(buf, PAGE_SIZE, "%s\n", + (level == AMD_DPM_FORCED_LEVEL_AUTO) ? "auto" : + (level == AMD_DPM_FORCED_LEVEL_LOW) ? "low" : + (level == AMD_DPM_FORCED_LEVEL_HIGH) ? "high" : + (level == AMD_DPM_FORCED_LEVEL_MANUAL) ? "manual" : + (level == AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD) ? "profile_standard" : + (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) ? "profile_min_sclk" : + (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) ? "profile_min_mclk" : + (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) ? "profile_peak" : + "unknown"); } static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev, @@ -143,7 +160,8 @@ static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = ddev->dev_private; - enum amdgpu_dpm_forced_level level; + enum amd_dpm_forced_level level; + enum amd_dpm_forced_level current_level; int ret = 0; /* Can't force performance level when the card is off */ @@ -151,19 +169,34 @@ static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev, (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) return -EINVAL; + current_level = amdgpu_dpm_get_performance_level(adev); + if (strncmp("low", buf, strlen("low")) == 0) { - level = AMDGPU_DPM_FORCED_LEVEL_LOW; + level = AMD_DPM_FORCED_LEVEL_LOW; } else if (strncmp("high", buf, strlen("high")) == 0) { - level = AMDGPU_DPM_FORCED_LEVEL_HIGH; + level = AMD_DPM_FORCED_LEVEL_HIGH; } else if (strncmp("auto", buf, strlen("auto")) == 0) { - level = AMDGPU_DPM_FORCED_LEVEL_AUTO; + level = AMD_DPM_FORCED_LEVEL_AUTO; } else if (strncmp("manual", buf, strlen("manual")) == 0) { - level = AMDGPU_DPM_FORCED_LEVEL_MANUAL; - } else { + level = AMD_DPM_FORCED_LEVEL_MANUAL; + } else if (strncmp("profile_exit", buf, strlen("profile_exit")) == 0) { + level = AMD_DPM_FORCED_LEVEL_PROFILE_EXIT; + } else if (strncmp("profile_standard", buf, strlen("profile_standard")) == 0) { + level = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD; + } else if (strncmp("profile_min_sclk", buf, strlen("profile_min_sclk")) == 0) { + level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK; + } else if (strncmp("profile_min_mclk", buf, strlen("profile_min_mclk")) == 0) { + level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK; + } else if (strncmp("profile_peak", buf, strlen("profile_peak")) == 0) { + level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; + } else { count = -EINVAL; goto fail; } + if (current_level == level) + return count; + if (adev->pp_enabled) amdgpu_dpm_force_performance_level(adev, level); else { @@ -180,6 +213,7 @@ static ssize_t amdgpu_set_dpm_forced_performance_level(struct device *dev, adev->pm.dpm.forced_level = level; mutex_unlock(&adev->pm.mutex); } + fail: return count; } @@ -1060,9 +1094,9 @@ static void amdgpu_dpm_change_power_state_locked(struct amdgpu_device *adev) if (adev->pm.funcs->force_performance_level) { if (adev->pm.dpm.thermal_active) { - enum amdgpu_dpm_forced_level level = adev->pm.dpm.forced_level; + enum amd_dpm_forced_level level = adev->pm.dpm.forced_level; /* force low perf level for thermal */ - amdgpu_dpm_force_performance_level(adev, AMDGPU_DPM_FORCED_LEVEL_LOW); + amdgpu_dpm_force_performance_level(adev, AMD_DPM_FORCED_LEVEL_LOW); /* save the user's level */ adev->pm.dpm.forced_level = level; } else { @@ -1108,12 +1142,22 @@ void amdgpu_dpm_enable_vce(struct amdgpu_device *adev, bool enable) /* XXX select vce level based on ring/task */ adev->pm.dpm.vce_level = AMD_VCE_LEVEL_AC_ALL; mutex_unlock(&adev->pm.mutex); + amdgpu_pm_compute_clocks(adev); + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_UNGATE); + amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_CG_STATE_UNGATE); } else { + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_GATE); + amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_CG_STATE_GATE); mutex_lock(&adev->pm.mutex); adev->pm.dpm.vce_active = false; mutex_unlock(&adev->pm.mutex); + amdgpu_pm_compute_clocks(adev); } - amdgpu_pm_compute_clocks(adev); + } } @@ -1252,7 +1296,8 @@ void amdgpu_pm_compute_clocks(struct amdgpu_device *adev) if (!adev->pm.dpm_enabled) return; - amdgpu_display_bandwidth_update(adev); + if (adev->mode_info.num_crtc) + amdgpu_display_bandwidth_update(adev); for (i = 0; i < AMDGPU_MAX_RINGS; i++) { struct amdgpu_ring *ring = adev->rings[i]; @@ -1351,12 +1396,27 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a return 0; } +static void amdgpu_parse_cg_state(struct seq_file *m, u32 flags) +{ + int i; + + for (i = 0; clocks[i].flag; i++) + seq_printf(m, "\t%s: %s\n", clocks[i].name, + (flags & clocks[i].flag) ? "On" : "Off"); +} + static int amdgpu_debugfs_pm_info(struct seq_file *m, void *data) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct amdgpu_device *adev = dev->dev_private; struct drm_device *ddev = adev->ddev; + u32 flags = 0; + + amdgpu_get_clockgating_state(adev, &flags); + seq_printf(m, "Clock Gating Flags Mask: 0x%x\n", flags); + amdgpu_parse_cg_state(m, flags); + seq_printf(m, "\n"); if (!adev->pm.dpm_enabled) { seq_printf(m, "dpm not enabled\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h index 5fd7734f15ca..c19c4d138751 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.h @@ -24,6 +24,12 @@ #ifndef __AMDGPU_PM_H__ #define __AMDGPU_PM_H__ +struct cg_flag_name +{ + u32 flag; + const char *name; +}; + int amdgpu_pm_sysfs_init(struct amdgpu_device *adev); void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev); void amdgpu_pm_print_power_states(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c index 95a568df8551..8856eccc37fa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c @@ -34,67 +34,34 @@ #include "cik_dpm.h" #include "vi_dpm.h" -static int amdgpu_powerplay_init(struct amdgpu_device *adev) +static int amdgpu_create_pp_handle(struct amdgpu_device *adev) { - int ret = 0; + struct amd_pp_init pp_init; struct amd_powerplay *amd_pp; + int ret; amd_pp = &(adev->powerplay); - - if (adev->pp_enabled) { - struct amd_pp_init *pp_init; - - pp_init = kzalloc(sizeof(struct amd_pp_init), GFP_KERNEL); - - if (pp_init == NULL) - return -ENOMEM; - - pp_init->chip_family = adev->family; - pp_init->chip_id = adev->asic_type; - pp_init->device = amdgpu_cgs_create_device(adev); - ret = amd_powerplay_init(pp_init, amd_pp); - kfree(pp_init); - } else { - amd_pp->pp_handle = (void *)adev; - - switch (adev->asic_type) { -#ifdef CONFIG_DRM_AMDGPU_SI - case CHIP_TAHITI: - case CHIP_PITCAIRN: - case CHIP_VERDE: - case CHIP_OLAND: - case CHIP_HAINAN: - amd_pp->ip_funcs = &si_dpm_ip_funcs; - break; -#endif -#ifdef CONFIG_DRM_AMDGPU_CIK - case CHIP_BONAIRE: - case CHIP_HAWAII: - amd_pp->ip_funcs = &ci_dpm_ip_funcs; - break; - case CHIP_KABINI: - case CHIP_MULLINS: - case CHIP_KAVERI: - amd_pp->ip_funcs = &kv_dpm_ip_funcs; - break; -#endif - case CHIP_CARRIZO: - case CHIP_STONEY: - amd_pp->ip_funcs = &cz_dpm_ip_funcs; - break; - default: - ret = -EINVAL; - break; - } - } - return ret; + pp_init.chip_family = adev->family; + pp_init.chip_id = adev->asic_type; + pp_init.pm_en = amdgpu_dpm != 0 ? true : false; + pp_init.feature_mask = amdgpu_pp_feature_mask; + pp_init.device = amdgpu_cgs_create_device(adev); + ret = amd_powerplay_create(&pp_init, &(amd_pp->pp_handle)); + if (ret) + return -EINVAL; + return 0; } static int amdgpu_pp_early_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; + struct amd_powerplay *amd_pp; int ret = 0; + amd_pp = &(adev->powerplay); + adev->pp_enabled = false; + amd_pp->pp_handle = (void *)adev; + switch (adev->asic_type) { case CHIP_POLARIS11: case CHIP_POLARIS10: @@ -102,30 +69,48 @@ static int amdgpu_pp_early_init(void *handle) case CHIP_TONGA: case CHIP_FIJI: case CHIP_TOPAZ: - adev->pp_enabled = true; - break; case CHIP_CARRIZO: case CHIP_STONEY: - adev->pp_enabled = (amdgpu_powerplay == 0) ? false : true; + adev->pp_enabled = true; + if (amdgpu_create_pp_handle(adev)) + return -EINVAL; + amd_pp->ip_funcs = &pp_ip_funcs; + amd_pp->pp_funcs = &pp_dpm_funcs; break; /* These chips don't have powerplay implemenations */ +#ifdef CONFIG_DRM_AMDGPU_SI + case CHIP_TAHITI: + case CHIP_PITCAIRN: + case CHIP_VERDE: + case CHIP_OLAND: + case CHIP_HAINAN: + amd_pp->ip_funcs = &si_dpm_ip_funcs; + break; +#endif +#ifdef CONFIG_DRM_AMDGPU_CIK case CHIP_BONAIRE: case CHIP_HAWAII: + amd_pp->ip_funcs = &ci_dpm_ip_funcs; + break; case CHIP_KABINI: case CHIP_MULLINS: case CHIP_KAVERI: + amd_pp->ip_funcs = &kv_dpm_ip_funcs; + break; +#endif default: - adev->pp_enabled = false; + ret = -EINVAL; break; } - ret = amdgpu_powerplay_init(adev); - if (ret) - return ret; - if (adev->powerplay.ip_funcs->early_init) ret = adev->powerplay.ip_funcs->early_init( adev->powerplay.pp_handle); + + if (ret == PP_DPM_DISABLED) { + adev->pm.dpm_enabled = false; + return 0; + } return ret; } @@ -185,6 +170,11 @@ static int amdgpu_pp_hw_init(void *handle) ret = adev->powerplay.ip_funcs->hw_init( adev->powerplay.pp_handle); + if (ret == PP_DPM_DISABLED) { + adev->pm.dpm_enabled = false; + return 0; + } + if ((amdgpu_dpm != 0) && !amdgpu_sriov_vf(adev)) adev->pm.dpm_enabled = true; @@ -210,14 +200,14 @@ static void amdgpu_pp_late_fini(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (adev->pp_enabled) { - amdgpu_pm_sysfs_fini(adev); - amd_powerplay_fini(adev->powerplay.pp_handle); - } - if (adev->powerplay.ip_funcs->late_fini) adev->powerplay.ip_funcs->late_fini( adev->powerplay.pp_handle); + + if (adev->pp_enabled && adev->pm.dpm_enabled) + amdgpu_pm_sysfs_fini(adev); + + amd_powerplay_destroy(adev->powerplay.pp_handle); } static int amdgpu_pp_suspend(void *handle) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index a47628395914..7c842b7f1004 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -207,6 +207,8 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring, } ring->cond_exe_gpu_addr = adev->wb.gpu_addr + (ring->cond_exe_offs * 4); ring->cond_exe_cpu_addr = &adev->wb.wb[ring->cond_exe_offs]; + /* always set cond_exec_polling to CONTINUE */ + *ring->cond_exe_cpu_addr = 1; r = amdgpu_fence_driver_start_ring(ring, irq_src, irq_type); if (r) { @@ -307,7 +309,7 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf, while (size) { if (*pos >= (ring->ring_size + 12)) return result; - + value = ring->ring[(*pos - 12)/4]; r = put_user(value, (uint32_t*)buf); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index 574f0b79c690..2345b39878c6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -135,6 +135,8 @@ struct amdgpu_ring_funcs { void (*end_use)(struct amdgpu_ring *ring); void (*emit_switch_buffer) (struct amdgpu_ring *ring); void (*emit_cntxcntl) (struct amdgpu_ring *ring, uint32_t flags); + void (*emit_rreg)(struct amdgpu_ring *ring, uint32_t reg); + void (*emit_wreg)(struct amdgpu_ring *ring, uint32_t reg, uint32_t val); }; struct amdgpu_ring { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h index bb964a8ff938..a18ae1e97860 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h @@ -24,7 +24,7 @@ TRACE_EVENT(amdgpu_mm_rreg, __entry->reg = reg; __entry->value = value; ), - TP_printk("0x%04lx, 0x%04lx, 0x%08lx", + TP_printk("0x%04lx, 0x%08lx, 0x%08lx", (unsigned long)__entry->did, (unsigned long)__entry->reg, (unsigned long)__entry->value) @@ -43,7 +43,7 @@ TRACE_EVENT(amdgpu_mm_wreg, __entry->reg = reg; __entry->value = value; ), - TP_printk("0x%04lx, 0x%04lx, 0x%08lx", + TP_printk("0x%04lx, 0x%08lx, 0x%08lx", (unsigned long)__entry->did, (unsigned long)__entry->reg, (unsigned long)__entry->value) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 8e35c1ff59e3..4c6094eefc51 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -466,10 +466,6 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, adev = amdgpu_ttm_adev(bo->bdev); - /* remember the eviction */ - if (evict) - atomic64_inc(&adev->num_evictions); - if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { amdgpu_move_null(bo, new_mem); return 0; @@ -533,6 +529,9 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ case TTM_PL_TT: break; case TTM_PL_VRAM: + if (mem->start == AMDGPU_BO_INVALID_OFFSET) + return -EINVAL; + mem->bus.offset = mem->start << PAGE_SHIFT; /* check if it's visible */ if ((mem->bus.offset + mem->bus.size) > adev->mc.visible_vram_size) @@ -552,6 +551,8 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ mem->bus.addr = ioremap_nocache(mem->bus.base + mem->bus.offset, mem->bus.size); + if (!mem->bus.addr) + return -ENOMEM; /* * Alpha: Use just the bus offset plus @@ -1052,56 +1053,6 @@ uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm, return flags; } -static void amdgpu_ttm_lru_removal(struct ttm_buffer_object *tbo) -{ - struct amdgpu_device *adev = amdgpu_ttm_adev(tbo->bdev); - unsigned i, j; - - for (i = 0; i < AMDGPU_TTM_LRU_SIZE; ++i) { - struct amdgpu_mman_lru *lru = &adev->mman.log2_size[i]; - - for (j = 0; j < TTM_NUM_MEM_TYPES; ++j) - if (&tbo->lru == lru->lru[j]) - lru->lru[j] = tbo->lru.prev; - - if (&tbo->swap == lru->swap_lru) - lru->swap_lru = tbo->swap.prev; - } -} - -static struct amdgpu_mman_lru *amdgpu_ttm_lru(struct ttm_buffer_object *tbo) -{ - struct amdgpu_device *adev = amdgpu_ttm_adev(tbo->bdev); - unsigned log2_size = min(ilog2(tbo->num_pages), - AMDGPU_TTM_LRU_SIZE - 1); - - return &adev->mman.log2_size[log2_size]; -} - -static struct list_head *amdgpu_ttm_lru_tail(struct ttm_buffer_object *tbo) -{ - struct amdgpu_mman_lru *lru = amdgpu_ttm_lru(tbo); - struct list_head *res = lru->lru[tbo->mem.mem_type]; - - lru->lru[tbo->mem.mem_type] = &tbo->lru; - while ((++lru)->lru[tbo->mem.mem_type] == res) - lru->lru[tbo->mem.mem_type] = &tbo->lru; - - return res; -} - -static struct list_head *amdgpu_ttm_swap_lru_tail(struct ttm_buffer_object *tbo) -{ - struct amdgpu_mman_lru *lru = amdgpu_ttm_lru(tbo); - struct list_head *res = lru->swap_lru; - - lru->swap_lru = &tbo->swap; - while ((++lru)->swap_lru == res) - lru->swap_lru = &tbo->swap; - - return res; -} - static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, const struct ttm_place *place) { @@ -1140,14 +1091,10 @@ static struct ttm_bo_driver amdgpu_bo_driver = { .fault_reserve_notify = &amdgpu_bo_fault_reserve_notify, .io_mem_reserve = &amdgpu_ttm_io_mem_reserve, .io_mem_free = &amdgpu_ttm_io_mem_free, - .lru_removal = &amdgpu_ttm_lru_removal, - .lru_tail = &amdgpu_ttm_lru_tail, - .swap_lru_tail = &amdgpu_ttm_swap_lru_tail, }; int amdgpu_ttm_init(struct amdgpu_device *adev) { - unsigned i, j; int r; r = amdgpu_ttm_global_init(adev); @@ -1165,19 +1112,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) DRM_ERROR("failed initializing buffer object driver(%d).\n", r); return r; } - - for (i = 0; i < AMDGPU_TTM_LRU_SIZE; ++i) { - struct amdgpu_mman_lru *lru = &adev->mman.log2_size[i]; - - for (j = 0; j < TTM_NUM_MEM_TYPES; ++j) - lru->lru[j] = &adev->mman.bdev.man[j].lru; - lru->swap_lru = &adev->mman.bdev.glob->swap_lru; - } - - for (j = 0; j < TTM_NUM_MEM_TYPES; ++j) - adev->mman.guard.lru[j] = NULL; - adev->mman.guard.swap_lru = NULL; - adev->mman.initialized = true; r = ttm_bo_init_mm(&adev->mman.bdev, TTM_PL_VRAM, adev->mc.real_vram_size >> PAGE_SHIFT); @@ -1365,7 +1299,7 @@ int amdgpu_copy_buffer(struct amdgpu_ring *ring, WARN_ON(job->ibs[0].length_dw > num_dw); if (direct_submit) { r = amdgpu_ib_schedule(ring, job->num_ibs, job->ibs, - NULL, NULL, fence); + NULL, fence); job->fence = dma_fence_get(*fence); if (r) DRM_ERROR("Error scheduling IBs (%d)\n", r); @@ -1482,18 +1416,18 @@ static int amdgpu_mm_dump_table(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct amdgpu_device *adev = dev->dev_private; struct drm_mm *mm = (struct drm_mm *)adev->mman.bdev.man[ttm_pl].priv; - int ret; struct ttm_bo_global *glob = adev->mman.bdev.glob; + struct drm_printer p = drm_seq_file_printer(m); spin_lock(&glob->lru_lock); - ret = drm_mm_dump_table(m, mm); + drm_mm_print(mm, &p); spin_unlock(&glob->lru_lock); if (ttm_pl == TTM_PL_VRAM) seq_printf(m, "man size:%llu pages, ram usage:%lluMB, vis usage:%lluMB\n", adev->mman.bdev.man[ttm_pl].size, (u64)atomic64_read(&adev->vram_usage) >> 20, (u64)atomic64_read(&adev->vram_vis_usage) >> 20); - return ret; + return 0; } static int ttm_pl_vram = TTM_PL_VRAM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index 98ee384f0fca..6bdede8ff12b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -34,13 +34,6 @@ #define AMDGPU_PL_FLAG_GWS (TTM_PL_FLAG_PRIV << 1) #define AMDGPU_PL_FLAG_OA (TTM_PL_FLAG_PRIV << 2) -#define AMDGPU_TTM_LRU_SIZE 20 - -struct amdgpu_mman_lru { - struct list_head *lru[TTM_NUM_MEM_TYPES]; - struct list_head *swap_lru; -}; - struct amdgpu_mman { struct ttm_bo_global_ref bo_global_ref; struct drm_global_reference mem_global_ref; @@ -58,11 +51,6 @@ struct amdgpu_mman { struct amdgpu_ring *buffer_funcs_ring; /* Scheduler entity for buffer moves */ struct amd_sched_entity entity; - - /* custom LRU management */ - struct amdgpu_mman_lru log2_size[AMDGPU_TTM_LRU_SIZE]; - /* guard for log2_size array, don't add anything in between */ - struct amdgpu_mman_lru guard; }; extern const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 1d564beb0fde..6d6ab7f11b4c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -976,7 +976,7 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, struct amdgpu_bo *bo, ib->length_dw = 16; if (direct) { - r = amdgpu_ib_schedule(ring, 1, ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, ib, NULL, &f); job->fence = dma_fence_get(f); if (r) goto err_free; @@ -1113,6 +1113,11 @@ static void amdgpu_uvd_idle_work_handler(struct work_struct *work) amdgpu_dpm_enable_uvd(adev, false); } else { amdgpu_asic_set_uvd_clocks(adev, 0, 0); + /* shutdown the UVD block */ + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_GATE); + amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_CG_STATE_GATE); } } else { schedule_delayed_work(&adev->uvd.idle_work, UVD_IDLE_TIMEOUT); @@ -1129,6 +1134,10 @@ void amdgpu_uvd_ring_begin_use(struct amdgpu_ring *ring) amdgpu_dpm_enable_uvd(adev, true); } else { amdgpu_asic_set_uvd_clocks(adev, 53300, 40000); + amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_CG_STATE_UNGATE); + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_UNGATE); } } } @@ -1178,3 +1187,28 @@ int amdgpu_uvd_ring_test_ib(struct amdgpu_ring *ring, long timeout) error: return r; } + +/** + * amdgpu_uvd_used_handles - returns used UVD handles + * + * @adev: amdgpu_device pointer + * + * Returns the number of UVD handles in use + */ +uint32_t amdgpu_uvd_used_handles(struct amdgpu_device *adev) +{ + unsigned i; + uint32_t used_handles = 0; + + for (i = 0; i < adev->uvd.max_handles; ++i) { + /* + * Handles can be freed in any order, and not + * necessarily linear. So we need to count + * all non-zero handles. + */ + if (atomic_read(&adev->uvd.handles[i])) + used_handles++; + } + + return used_handles; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h index 6249ba1bde2a..c10682baccae 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h @@ -38,5 +38,6 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx); void amdgpu_uvd_ring_begin_use(struct amdgpu_ring *ring); void amdgpu_uvd_ring_end_use(struct amdgpu_ring *ring); int amdgpu_uvd_ring_test_ib(struct amdgpu_ring *ring, long timeout); +uint32_t amdgpu_uvd_used_handles(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index 8fec802d3908..e2c06780ce49 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -321,6 +321,10 @@ static void amdgpu_vce_idle_work_handler(struct work_struct *work) amdgpu_dpm_enable_vce(adev, false); } else { amdgpu_asic_set_vce_clocks(adev, 0, 0); + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_GATE); + amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_CG_STATE_GATE); } } else { schedule_delayed_work(&adev->vce.idle_work, VCE_IDLE_TIMEOUT); @@ -346,6 +350,11 @@ void amdgpu_vce_ring_begin_use(struct amdgpu_ring *ring) amdgpu_dpm_enable_vce(adev, true); } else { amdgpu_asic_set_vce_clocks(adev, 53300, 40000); + amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_CG_STATE_UNGATE); + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_UNGATE); + } } mutex_unlock(&adev->vce.idle_mutex); @@ -455,7 +464,7 @@ int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, for (i = ib->length_dw; i < ib_size_dw; ++i) ib->ptr[i] = 0x0; - r = amdgpu_ib_schedule(ring, 1, ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, ib, NULL, &f); job->fence = dma_fence_get(f); if (r) goto err; @@ -518,7 +527,7 @@ int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, ib->ptr[i] = 0x0; if (direct) { - r = amdgpu_ib_schedule(ring, 1, ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, ib, NULL, &f); job->fence = dma_fence_get(f); if (r) goto err; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c new file mode 100644 index 000000000000..dcfb7df3caf4 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c @@ -0,0 +1,221 @@ +/* + * Copyright 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "amdgpu.h" + +int amdgpu_allocate_static_csa(struct amdgpu_device *adev) +{ + int r; + void *ptr; + + r = amdgpu_bo_create_kernel(adev, AMDGPU_CSA_SIZE, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_VRAM, &adev->virt.csa_obj, + &adev->virt.csa_vmid0_addr, &ptr); + if (r) + return r; + + memset(ptr, 0, AMDGPU_CSA_SIZE); + return 0; +} + +/* + * amdgpu_map_static_csa should be called during amdgpu_vm_init + * it maps virtual address "AMDGPU_VA_RESERVED_SIZE - AMDGPU_CSA_SIZE" + * to this VM, and each command submission of GFX should use this virtual + * address within META_DATA init package to support SRIOV gfx preemption. + */ + +int amdgpu_map_static_csa(struct amdgpu_device *adev, struct amdgpu_vm *vm) +{ + int r; + struct amdgpu_bo_va *bo_va; + struct ww_acquire_ctx ticket; + struct list_head list; + struct amdgpu_bo_list_entry pd; + struct ttm_validate_buffer csa_tv; + + INIT_LIST_HEAD(&list); + INIT_LIST_HEAD(&csa_tv.head); + csa_tv.bo = &adev->virt.csa_obj->tbo; + csa_tv.shared = true; + + list_add(&csa_tv.head, &list); + amdgpu_vm_get_pd_bo(vm, &list, &pd); + + r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); + if (r) { + DRM_ERROR("failed to reserve CSA,PD BOs: err=%d\n", r); + return r; + } + + bo_va = amdgpu_vm_bo_add(adev, vm, adev->virt.csa_obj); + if (!bo_va) { + ttm_eu_backoff_reservation(&ticket, &list); + DRM_ERROR("failed to create bo_va for static CSA\n"); + return -ENOMEM; + } + + r = amdgpu_vm_bo_map(adev, bo_va, AMDGPU_CSA_VADDR, 0,AMDGPU_CSA_SIZE, + AMDGPU_PTE_READABLE | AMDGPU_PTE_WRITEABLE | + AMDGPU_PTE_EXECUTABLE); + + if (r) { + DRM_ERROR("failed to do bo_map on static CSA, err=%d\n", r); + amdgpu_vm_bo_rmv(adev, bo_va); + ttm_eu_backoff_reservation(&ticket, &list); + return r; + } + + vm->csa_bo_va = bo_va; + ttm_eu_backoff_reservation(&ticket, &list); + return 0; +} + +void amdgpu_virt_init_setting(struct amdgpu_device *adev) +{ + /* enable virtual display */ + adev->mode_info.num_crtc = 1; + adev->enable_virtual_display = true; + + mutex_init(&adev->virt.lock); +} + +uint32_t amdgpu_virt_kiq_rreg(struct amdgpu_device *adev, uint32_t reg) +{ + signed long r; + uint32_t val; + struct dma_fence *f; + struct amdgpu_kiq *kiq = &adev->gfx.kiq; + struct amdgpu_ring *ring = &kiq->ring; + + BUG_ON(!ring->funcs->emit_rreg); + + mutex_lock(&adev->virt.lock); + amdgpu_ring_alloc(ring, 32); + amdgpu_ring_emit_hdp_flush(ring); + amdgpu_ring_emit_rreg(ring, reg); + amdgpu_ring_emit_hdp_invalidate(ring); + amdgpu_fence_emit(ring, &f); + amdgpu_ring_commit(ring); + mutex_unlock(&adev->virt.lock); + + r = dma_fence_wait(f, false); + if (r) + DRM_ERROR("wait for kiq fence error: %ld.\n", r); + dma_fence_put(f); + + val = adev->wb.wb[adev->virt.reg_val_offs]; + + return val; +} + +void amdgpu_virt_kiq_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v) +{ + signed long r; + struct dma_fence *f; + struct amdgpu_kiq *kiq = &adev->gfx.kiq; + struct amdgpu_ring *ring = &kiq->ring; + + BUG_ON(!ring->funcs->emit_wreg); + + mutex_lock(&adev->virt.lock); + amdgpu_ring_alloc(ring, 32); + amdgpu_ring_emit_hdp_flush(ring); + amdgpu_ring_emit_wreg(ring, reg, v); + amdgpu_ring_emit_hdp_invalidate(ring); + amdgpu_fence_emit(ring, &f); + amdgpu_ring_commit(ring); + mutex_unlock(&adev->virt.lock); + + r = dma_fence_wait(f, false); + if (r) + DRM_ERROR("wait for kiq fence error: %ld.\n", r); + dma_fence_put(f); +} + +/** + * amdgpu_virt_request_full_gpu() - request full gpu access + * @amdgpu: amdgpu device. + * @init: is driver init time. + * When start to init/fini driver, first need to request full gpu access. + * Return: Zero if request success, otherwise will return error. + */ +int amdgpu_virt_request_full_gpu(struct amdgpu_device *adev, bool init) +{ + struct amdgpu_virt *virt = &adev->virt; + int r; + + if (virt->ops && virt->ops->req_full_gpu) { + r = virt->ops->req_full_gpu(adev, init); + if (r) + return r; + + adev->virt.caps &= ~AMDGPU_SRIOV_CAPS_RUNTIME; + } + + return 0; +} + +/** + * amdgpu_virt_release_full_gpu() - release full gpu access + * @amdgpu: amdgpu device. + * @init: is driver init time. + * When finishing driver init/fini, need to release full gpu access. + * Return: Zero if release success, otherwise will returen error. + */ +int amdgpu_virt_release_full_gpu(struct amdgpu_device *adev, bool init) +{ + struct amdgpu_virt *virt = &adev->virt; + int r; + + if (virt->ops && virt->ops->rel_full_gpu) { + r = virt->ops->rel_full_gpu(adev, init); + if (r) + return r; + + adev->virt.caps |= AMDGPU_SRIOV_CAPS_RUNTIME; + } + return 0; +} + +/** + * amdgpu_virt_reset_gpu() - reset gpu + * @amdgpu: amdgpu device. + * Send reset command to GPU hypervisor to reset GPU that VM is using + * Return: Zero if reset success, otherwise will return error. + */ +int amdgpu_virt_reset_gpu(struct amdgpu_device *adev) +{ + struct amdgpu_virt *virt = &adev->virt; + int r; + + if (virt->ops && virt->ops->reset_gpu) { + r = virt->ops->reset_gpu(adev); + if (r) + return r; + + adev->virt.caps &= ~AMDGPU_SRIOV_CAPS_RUNTIME; + } + + return 0; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h index 2c37a374917f..675e12c42532 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h @@ -28,22 +28,48 @@ #define AMDGPU_SRIOV_CAPS_ENABLE_IOV (1 << 1) /* sr-iov is enabled on this GPU */ #define AMDGPU_SRIOV_CAPS_IS_VF (1 << 2) /* this GPU is a virtual function */ #define AMDGPU_PASSTHROUGH_MODE (1 << 3) /* thw whole GPU is pass through for VM */ +#define AMDGPU_SRIOV_CAPS_RUNTIME (1 << 4) /* is out of full access mode */ + +/** + * struct amdgpu_virt_ops - amdgpu device virt operations + */ +struct amdgpu_virt_ops { + int (*req_full_gpu)(struct amdgpu_device *adev, bool init); + int (*rel_full_gpu)(struct amdgpu_device *adev, bool init); + int (*reset_gpu)(struct amdgpu_device *adev); +}; + /* GPU virtualization */ -struct amdgpu_virtualization { - uint32_t virtual_caps; +struct amdgpu_virt { + uint32_t caps; + struct amdgpu_bo *csa_obj; + uint64_t csa_vmid0_addr; + bool chained_ib_support; + uint32_t reg_val_offs; + struct mutex lock; + struct amdgpu_irq_src ack_irq; + struct amdgpu_irq_src rcv_irq; + struct delayed_work flr_work; + const struct amdgpu_virt_ops *ops; }; +#define AMDGPU_CSA_SIZE (8 * 1024) +#define AMDGPU_CSA_VADDR (AMDGPU_VA_RESERVED_SIZE - AMDGPU_CSA_SIZE) + #define amdgpu_sriov_enabled(adev) \ -((adev)->virtualization.virtual_caps & AMDGPU_SRIOV_CAPS_ENABLE_IOV) +((adev)->virt.caps & AMDGPU_SRIOV_CAPS_ENABLE_IOV) #define amdgpu_sriov_vf(adev) \ -((adev)->virtualization.virtual_caps & AMDGPU_SRIOV_CAPS_IS_VF) +((adev)->virt.caps & AMDGPU_SRIOV_CAPS_IS_VF) #define amdgpu_sriov_bios(adev) \ -((adev)->virtualization.virtual_caps & AMDGPU_SRIOV_CAPS_SRIOV_VBIOS) +((adev)->virt.caps & AMDGPU_SRIOV_CAPS_SRIOV_VBIOS) + +#define amdgpu_sriov_runtime(adev) \ +((adev)->virt.caps & AMDGPU_SRIOV_CAPS_RUNTIME) #define amdgpu_passthrough(adev) \ -((adev)->virtualization.virtual_caps & AMDGPU_PASSTHROUGH_MODE) +((adev)->virt.caps & AMDGPU_PASSTHROUGH_MODE) static inline bool is_virtual_machine(void) { @@ -54,4 +80,14 @@ static inline bool is_virtual_machine(void) #endif } -#endif
\ No newline at end of file +struct amdgpu_vm; +int amdgpu_allocate_static_csa(struct amdgpu_device *adev); +int amdgpu_map_static_csa(struct amdgpu_device *adev, struct amdgpu_vm *vm); +void amdgpu_virt_init_setting(struct amdgpu_device *adev); +uint32_t amdgpu_virt_kiq_rreg(struct amdgpu_device *adev, uint32_t reg); +void amdgpu_virt_kiq_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v); +int amdgpu_virt_request_full_gpu(struct amdgpu_device *adev, bool init); +int amdgpu_virt_release_full_gpu(struct amdgpu_device *adev, bool init); +int amdgpu_virt_reset_gpu(struct amdgpu_device *adev); + +#endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 1dda9321bd5a..bd0d33125c18 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1293,7 +1293,7 @@ struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev, int amdgpu_vm_bo_map(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t saddr, uint64_t offset, - uint64_t size, uint32_t flags) + uint64_t size, uint64_t flags) { struct amdgpu_bo_va_mapping *mapping; struct amdgpu_vm *vm = bo_va->vm; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index adbc2f5e5c7f..18c72c0b478d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -111,6 +111,8 @@ struct amdgpu_vm { /* client id */ u64 client_id; + /* each VM will map on CSA */ + struct amdgpu_bo_va *csa_bo_va; }; struct amdgpu_vm_id { @@ -195,7 +197,7 @@ struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev, int amdgpu_vm_bo_map(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t addr, uint64_t offset, - uint64_t size, uint32_t flags); + uint64_t size, uint64_t flags); int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, uint64_t addr); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index d710226a0fff..9e577e3d3147 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -97,8 +97,7 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, struct amdgpu_vram_mgr *mgr = man->priv; struct drm_mm *mm = &mgr->mm; struct drm_mm_node *nodes; - enum drm_mm_search_flags sflags = DRM_MM_SEARCH_DEFAULT; - enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; + enum drm_mm_insert_mode mode; unsigned long lpfn, num_nodes, pages_per_node, pages_left; unsigned i; int r; @@ -121,10 +120,9 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, if (!nodes) return -ENOMEM; - if (place->flags & TTM_PL_FLAG_TOPDOWN) { - sflags = DRM_MM_SEARCH_BELOW; - aflags = DRM_MM_CREATE_TOP; - } + mode = DRM_MM_INSERT_BEST; + if (place->flags & TTM_PL_FLAG_TOPDOWN) + mode = DRM_MM_INSERT_HIGH; pages_left = mem->num_pages; @@ -135,13 +133,11 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, if (pages == pages_per_node) alignment = pages_per_node; - else - sflags |= DRM_MM_SEARCH_BEST; - r = drm_mm_insert_node_in_range_generic(mm, &nodes[i], pages, - alignment, 0, - place->fpfn, lpfn, - sflags, aflags); + r = drm_mm_insert_node_in_range(mm, &nodes[i], + pages, alignment, 0, + place->fpfn, lpfn, + mode); if (unlikely(r)) goto error; @@ -207,9 +203,10 @@ static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man, const char *prefix) { struct amdgpu_vram_mgr *mgr = man->priv; + struct drm_printer p = drm_debug_printer(prefix); spin_lock(&mgr->lock); - drm_mm_debug_table(&mgr->mm, prefix); + drm_mm_print(&mgr->mm, &p); spin_unlock(&mgr->lock); } diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c index c32eca26155c..2af26d2da127 100644 --- a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c +++ b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c @@ -181,9 +181,6 @@ void amdgpu_atombios_encoder_init_backlight(struct amdgpu_encoder *amdgpu_encode if (!amdgpu_encoder->enc_priv) return; - if (!adev->is_atom_bios) - return; - if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) return; @@ -236,9 +233,6 @@ amdgpu_atombios_encoder_fini_backlight(struct amdgpu_encoder *amdgpu_encoder) if (!amdgpu_encoder->enc_priv) return; - if (!adev->is_atom_bios) - return; - if (!(adev->mode_info.firmware_flags & ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU)) return; diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index bda9e3de191e..f97ecb49972e 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c @@ -889,7 +889,16 @@ static void ci_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate) pi->uvd_power_gated = gate; - ci_update_uvd_dpm(adev, gate); + if (gate) { + /* stop the UVD block */ + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_GATE); + ci_update_uvd_dpm(adev, gate); + } else { + amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_UNGATE); + ci_update_uvd_dpm(adev, gate); + } } static bool ci_dpm_vblank_too_short(struct amdgpu_device *adev) @@ -2201,7 +2210,6 @@ static void ci_clear_vc(struct amdgpu_device *adev) static int ci_upload_firmware(struct amdgpu_device *adev) { - struct ci_power_info *pi = ci_get_pi(adev); int i, ret; if (amdgpu_ci_is_smc_running(adev)) { @@ -2218,7 +2226,7 @@ static int ci_upload_firmware(struct amdgpu_device *adev) amdgpu_ci_stop_smc_clock(adev); amdgpu_ci_reset_smc(adev); - ret = amdgpu_ci_load_smc_ucode(adev, pi->sram_end); + ret = amdgpu_ci_load_smc_ucode(adev, SMC_RAM_END); return ret; @@ -4248,12 +4256,6 @@ static int ci_update_vce_dpm(struct amdgpu_device *adev, if (amdgpu_current_state->evclk != amdgpu_new_state->evclk) { if (amdgpu_new_state->evclk) { - /* turn the clocks on when encoding */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_UNGATE); - if (ret) - return ret; - pi->smc_state_table.VceBootLevel = ci_get_vce_boot_level(adev); tmp = RREG32_SMC(ixDPM_TABLE_475); tmp &= ~DPM_TABLE_475__VceBootLevel_MASK; @@ -4265,9 +4267,6 @@ static int ci_update_vce_dpm(struct amdgpu_device *adev, ret = ci_enable_vce_dpm(adev, false); if (ret) return ret; - /* turn the clocks off when not encoding */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_GATE); } } return ret; @@ -4336,13 +4335,13 @@ static u32 ci_get_lowest_enabled_level(struct amdgpu_device *adev, static int ci_dpm_force_performance_level(struct amdgpu_device *adev, - enum amdgpu_dpm_forced_level level) + enum amd_dpm_forced_level level) { struct ci_power_info *pi = ci_get_pi(adev); u32 tmp, levels, i; int ret; - if (level == AMDGPU_DPM_FORCED_LEVEL_HIGH) { + if (level == AMD_DPM_FORCED_LEVEL_HIGH) { if ((!pi->pcie_dpm_key_disabled) && pi->dpm_level_enable_mask.pcie_dpm_enable_mask) { levels = 0; @@ -4403,7 +4402,7 @@ static int ci_dpm_force_performance_level(struct amdgpu_device *adev, } } } - } else if (level == AMDGPU_DPM_FORCED_LEVEL_LOW) { + } else if (level == AMD_DPM_FORCED_LEVEL_LOW) { if ((!pi->sclk_dpm_key_disabled) && pi->dpm_level_enable_mask.sclk_dpm_enable_mask) { levels = ci_get_lowest_enabled_level(adev, @@ -4452,7 +4451,7 @@ static int ci_dpm_force_performance_level(struct amdgpu_device *adev, udelay(1); } } - } else if (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) { + } else if (level == AMD_DPM_FORCED_LEVEL_AUTO) { if (!pi->pcie_dpm_key_disabled) { PPSMC_Result smc_result; @@ -6262,20 +6261,20 @@ static int ci_dpm_sw_init(void *handle) /* default to balanced state */ adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; - adev->pm.dpm.forced_level = AMDGPU_DPM_FORCED_LEVEL_AUTO; + adev->pm.dpm.forced_level = AMD_DPM_FORCED_LEVEL_AUTO; adev->pm.default_sclk = adev->clock.default_sclk; adev->pm.default_mclk = adev->clock.default_mclk; adev->pm.current_sclk = adev->clock.default_sclk; adev->pm.current_mclk = adev->clock.default_mclk; adev->pm.int_thermal_type = THERMAL_TYPE_NONE; - if (amdgpu_dpm == 0) - return 0; - ret = ci_dpm_init_microcode(adev); if (ret) return ret; + if (amdgpu_dpm == 0) + return 0; + INIT_WORK(&adev->pm.dpm.thermal.work, amdgpu_dpm_thermal_work_handler); mutex_lock(&adev->pm.mutex); ret = ci_dpm_init(adev); @@ -6319,8 +6318,15 @@ static int ci_dpm_hw_init(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!amdgpu_dpm) + if (!amdgpu_dpm) { + ret = ci_upload_firmware(adev); + if (ret) { + DRM_ERROR("ci_upload_firmware failed\n"); + return ret; + } + ci_dpm_start_smc(adev); return 0; + } mutex_lock(&adev->pm.mutex); ci_dpm_setup_asic(adev); @@ -6342,6 +6348,8 @@ static int ci_dpm_hw_fini(void *handle) mutex_lock(&adev->pm.mutex); ci_dpm_disable(adev); mutex_unlock(&adev->pm.mutex); + } else { + ci_dpm_stop_smc(adev); } return 0; @@ -6571,8 +6579,9 @@ static int ci_dpm_force_clock_level(struct amdgpu_device *adev, { struct ci_power_info *pi = ci_get_pi(adev); - if (adev->pm.dpm.forced_level - != AMDGPU_DPM_FORCED_LEVEL_MANUAL) + if (adev->pm.dpm.forced_level & (AMD_DPM_FORCED_LEVEL_AUTO | + AMD_DPM_FORCED_LEVEL_LOW | + AMD_DPM_FORCED_LEVEL_HIGH)) return -EINVAL; switch (type) { @@ -6739,12 +6748,3 @@ static void ci_dpm_set_irq_funcs(struct amdgpu_device *adev) adev->pm.dpm.thermal.irq.num_types = AMDGPU_THERMAL_IRQ_LAST; adev->pm.dpm.thermal.irq.funcs = &ci_dpm_irq_funcs; } - -const struct amdgpu_ip_block_version ci_dpm_ip_block = -{ - .type = AMD_IP_BLOCK_TYPE_SMC, - .major = 7, - .minor = 0, - .rev = 0, - .funcs = &ci_dpm_ip_funcs, -}; diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c index 302df85893ab..c4d4b35e54ec 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik.c +++ b/drivers/gpu/drm/amd/amdgpu/cik.c @@ -1176,6 +1176,7 @@ static int cik_gpu_pci_config_reset(struct amdgpu_device *adev) if (RREG32(mmCONFIG_MEMSIZE) != 0xffffffff) { /* enable BM */ pci_set_master(adev->pdev); + adev->has_hw_reset = true; r = 0; break; } @@ -1627,14 +1628,13 @@ static uint32_t cik_get_rev_id(struct amdgpu_device *adev) static void cik_detect_hw_virtualization(struct amdgpu_device *adev) { if (is_virtual_machine()) /* passthrough mode */ - adev->virtualization.virtual_caps |= AMDGPU_PASSTHROUGH_MODE; + adev->virt.caps |= AMDGPU_PASSTHROUGH_MODE; } static const struct amdgpu_asic_funcs cik_asic_funcs = { .read_disabled_bios = &cik_read_disabled_bios, .read_bios_from_rom = &cik_read_bios_from_rom, - .detect_hw_virtualization = cik_detect_hw_virtualization, .read_register = &cik_read_register, .reset = &cik_asic_reset, .set_vga_state = &cik_vga_set_state, @@ -1723,8 +1723,8 @@ static int cik_common_early_init(void *handle) AMD_PG_SUPPORT_GFX_SMG | AMD_PG_SUPPORT_GFX_DMG |*/ AMD_PG_SUPPORT_UVD | - /*AMD_PG_SUPPORT_VCE | - AMD_PG_SUPPORT_CP | + AMD_PG_SUPPORT_VCE | + /* AMD_PG_SUPPORT_CP | AMD_PG_SUPPORT_GDS | AMD_PG_SUPPORT_RLC_SMU_HS | AMD_PG_SUPPORT_ACP | @@ -1890,6 +1890,8 @@ static const struct amdgpu_ip_block_version cik_common_ip_block = int cik_set_ip_blocks(struct amdgpu_device *adev) { + cik_detect_hw_virtualization(adev); + switch (adev->asic_type) { case CHIP_BONAIRE: amdgpu_ip_block_add(adev, &cik_common_ip_block); diff --git a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c index 4c34dbc7a254..810bba533975 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/cik_sdma.c @@ -651,7 +651,7 @@ static int cik_sdma_ring_test_ib(struct amdgpu_ring *ring, long timeout) ib.ptr[3] = 1; ib.ptr[4] = 0xDEADBEEF; ib.length_dw = 5; - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) goto err1; diff --git a/drivers/gpu/drm/amd/include/asic_reg/si/clearstate_si.h b/drivers/gpu/drm/amd/amdgpu/clearstate_si.h index 66e39cdb5cb0..66e39cdb5cb0 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/si/clearstate_si.h +++ b/drivers/gpu/drm/amd/amdgpu/clearstate_si.h diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c deleted file mode 100644 index ba2b66be9022..000000000000 --- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c +++ /dev/null @@ -1,2320 +0,0 @@ -/* - * Copyright 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <linux/firmware.h> -#include <linux/seq_file.h> -#include "drmP.h" -#include "amdgpu.h" -#include "amdgpu_pm.h" -#include "amdgpu_atombios.h" -#include "vid.h" -#include "vi_dpm.h" -#include "amdgpu_dpm.h" -#include "cz_dpm.h" -#include "cz_ppsmc.h" -#include "atom.h" - -#include "smu/smu_8_0_d.h" -#include "smu/smu_8_0_sh_mask.h" -#include "gca/gfx_8_0_d.h" -#include "gca/gfx_8_0_sh_mask.h" -#include "gmc/gmc_8_1_d.h" -#include "bif/bif_5_1_d.h" -#include "gfx_v8_0.h" - -static void cz_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate); -static void cz_dpm_powergate_vce(struct amdgpu_device *adev, bool gate); -static void cz_dpm_fini(struct amdgpu_device *adev); - -static struct cz_ps *cz_get_ps(struct amdgpu_ps *rps) -{ - struct cz_ps *ps = rps->ps_priv; - - return ps; -} - -static struct cz_power_info *cz_get_pi(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = adev->pm.dpm.priv; - - return pi; -} - -static uint16_t cz_convert_8bit_index_to_voltage(struct amdgpu_device *adev, - uint16_t voltage) -{ - uint16_t tmp = 6200 - voltage * 25; - - return tmp; -} - -static void cz_construct_max_power_limits_table(struct amdgpu_device *adev, - struct amdgpu_clock_and_voltage_limits *table) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_clock_voltage_dependency_table *dep_table = - &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk; - - if (dep_table->count > 0) { - table->sclk = dep_table->entries[dep_table->count - 1].clk; - table->vddc = cz_convert_8bit_index_to_voltage(adev, - dep_table->entries[dep_table->count - 1].v); - } - - table->mclk = pi->sys_info.nbp_memory_clock[0]; - -} - -union igp_info { - struct _ATOM_INTEGRATED_SYSTEM_INFO info; - struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7; - struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 info_8; - struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_9 info_9; -}; - -static int cz_parse_sys_info_table(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_mode_info *mode_info = &adev->mode_info; - int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); - union igp_info *igp_info; - u8 frev, crev; - u16 data_offset; - int i = 0; - - if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, - &frev, &crev, &data_offset)) { - igp_info = (union igp_info *)(mode_info->atom_context->bios + - data_offset); - - if (crev != 9) { - DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); - return -EINVAL; - } - pi->sys_info.bootup_sclk = - le32_to_cpu(igp_info->info_9.ulBootUpEngineClock); - pi->sys_info.bootup_uma_clk = - le32_to_cpu(igp_info->info_9.ulBootUpUMAClock); - pi->sys_info.dentist_vco_freq = - le32_to_cpu(igp_info->info_9.ulDentistVCOFreq); - pi->sys_info.bootup_nb_voltage_index = - le16_to_cpu(igp_info->info_9.usBootUpNBVoltage); - - if (igp_info->info_9.ucHtcTmpLmt == 0) - pi->sys_info.htc_tmp_lmt = 203; - else - pi->sys_info.htc_tmp_lmt = igp_info->info_9.ucHtcTmpLmt; - - if (igp_info->info_9.ucHtcHystLmt == 0) - pi->sys_info.htc_hyst_lmt = 5; - else - pi->sys_info.htc_hyst_lmt = igp_info->info_9.ucHtcHystLmt; - - if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) { - DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n"); - return -EINVAL; - } - - if (le32_to_cpu(igp_info->info_9.ulSystemConfig) & (1 << 3) && - pi->enable_nb_ps_policy) - pi->sys_info.nb_dpm_enable = true; - else - pi->sys_info.nb_dpm_enable = false; - - for (i = 0; i < CZ_NUM_NBPSTATES; i++) { - if (i < CZ_NUM_NBPMEMORY_CLOCK) - pi->sys_info.nbp_memory_clock[i] = - le32_to_cpu(igp_info->info_9.ulNbpStateMemclkFreq[i]); - pi->sys_info.nbp_n_clock[i] = - le32_to_cpu(igp_info->info_9.ulNbpStateNClkFreq[i]); - } - - for (i = 0; i < CZ_MAX_DISPLAY_CLOCK_LEVEL; i++) - pi->sys_info.display_clock[i] = - le32_to_cpu(igp_info->info_9.sDispClkVoltageMapping[i].ulMaximumSupportedCLK); - - for (i = 0; i < CZ_NUM_NBPSTATES; i++) - pi->sys_info.nbp_voltage_index[i] = - le32_to_cpu(igp_info->info_9.usNBPStateVoltage[i]); - - if (le32_to_cpu(igp_info->info_9.ulGPUCapInfo) & - SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS) - pi->caps_enable_dfs_bypass = true; - - pi->sys_info.uma_channel_number = - igp_info->info_9.ucUMAChannelNumber; - - cz_construct_max_power_limits_table(adev, - &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac); - } - - return 0; -} - -static void cz_patch_voltage_values(struct amdgpu_device *adev) -{ - int i; - struct amdgpu_uvd_clock_voltage_dependency_table *uvd_table = - &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; - struct amdgpu_vce_clock_voltage_dependency_table *vce_table = - &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; - struct amdgpu_clock_voltage_dependency_table *acp_table = - &adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table; - - if (uvd_table->count) { - for (i = 0; i < uvd_table->count; i++) - uvd_table->entries[i].v = - cz_convert_8bit_index_to_voltage(adev, - uvd_table->entries[i].v); - } - - if (vce_table->count) { - for (i = 0; i < vce_table->count; i++) - vce_table->entries[i].v = - cz_convert_8bit_index_to_voltage(adev, - vce_table->entries[i].v); - } - - if (acp_table->count) { - for (i = 0; i < acp_table->count; i++) - acp_table->entries[i].v = - cz_convert_8bit_index_to_voltage(adev, - acp_table->entries[i].v); - } - -} - -static void cz_construct_boot_state(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - pi->boot_pl.sclk = pi->sys_info.bootup_sclk; - pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index; - pi->boot_pl.ds_divider_index = 0; - pi->boot_pl.ss_divider_index = 0; - pi->boot_pl.allow_gnb_slow = 1; - pi->boot_pl.force_nbp_state = 0; - pi->boot_pl.display_wm = 0; - pi->boot_pl.vce_wm = 0; - -} - -static void cz_patch_boot_state(struct amdgpu_device *adev, - struct cz_ps *ps) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - ps->num_levels = 1; - ps->levels[0] = pi->boot_pl; -} - -union pplib_clock_info { - struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; - struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; - struct _ATOM_PPLIB_CZ_CLOCK_INFO carrizo; -}; - -static void cz_parse_pplib_clock_info(struct amdgpu_device *adev, - struct amdgpu_ps *rps, int index, - union pplib_clock_info *clock_info) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct cz_ps *ps = cz_get_ps(rps); - struct cz_pl *pl = &ps->levels[index]; - struct amdgpu_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk; - - pl->sclk = table->entries[clock_info->carrizo.index].clk; - pl->vddc_index = table->entries[clock_info->carrizo.index].v; - - ps->num_levels = index + 1; - - if (pi->caps_sclk_ds) { - pl->ds_divider_index = 5; - pl->ss_divider_index = 5; - } - -} - -static void cz_parse_pplib_non_clock_info(struct amdgpu_device *adev, - struct amdgpu_ps *rps, - struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info, - u8 table_rev) -{ - struct cz_ps *ps = cz_get_ps(rps); - - rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings); - rps->class = le16_to_cpu(non_clock_info->usClassification); - rps->class2 = le16_to_cpu(non_clock_info->usClassification2); - - if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) { - rps->vclk = le32_to_cpu(non_clock_info->ulVCLK); - rps->dclk = le32_to_cpu(non_clock_info->ulDCLK); - } else { - rps->vclk = 0; - rps->dclk = 0; - } - - if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) { - adev->pm.dpm.boot_ps = rps; - cz_patch_boot_state(adev, ps); - } - if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) - adev->pm.dpm.uvd_ps = rps; - -} - -union power_info { - struct _ATOM_PPLIB_POWERPLAYTABLE pplib; - struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; - struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; - struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4; - struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5; -}; - -union pplib_power_state { - struct _ATOM_PPLIB_STATE v1; - struct _ATOM_PPLIB_STATE_V2 v2; -}; - -static int cz_parse_power_table(struct amdgpu_device *adev) -{ - struct amdgpu_mode_info *mode_info = &adev->mode_info; - struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; - union pplib_power_state *power_state; - int i, j, k, non_clock_array_index, clock_array_index; - union pplib_clock_info *clock_info; - struct _StateArray *state_array; - struct _ClockInfoArray *clock_info_array; - struct _NonClockInfoArray *non_clock_info_array; - union power_info *power_info; - int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); - u16 data_offset; - u8 frev, crev; - u8 *power_state_offset; - struct cz_ps *ps; - - if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL, - &frev, &crev, &data_offset)) - return -EINVAL; - power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); - - state_array = (struct _StateArray *) - (mode_info->atom_context->bios + data_offset + - le16_to_cpu(power_info->pplib.usStateArrayOffset)); - clock_info_array = (struct _ClockInfoArray *) - (mode_info->atom_context->bios + data_offset + - le16_to_cpu(power_info->pplib.usClockInfoArrayOffset)); - non_clock_info_array = (struct _NonClockInfoArray *) - (mode_info->atom_context->bios + data_offset + - le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); - - adev->pm.dpm.ps = kzalloc(sizeof(struct amdgpu_ps) * - state_array->ucNumEntries, GFP_KERNEL); - - if (!adev->pm.dpm.ps) - return -ENOMEM; - - power_state_offset = (u8 *)state_array->states; - adev->pm.dpm.platform_caps = - le32_to_cpu(power_info->pplib.ulPlatformCaps); - adev->pm.dpm.backbias_response_time = - le16_to_cpu(power_info->pplib.usBackbiasTime); - adev->pm.dpm.voltage_response_time = - le16_to_cpu(power_info->pplib.usVoltageTime); - - for (i = 0; i < state_array->ucNumEntries; i++) { - power_state = (union pplib_power_state *)power_state_offset; - non_clock_array_index = power_state->v2.nonClockInfoIndex; - non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) - &non_clock_info_array->nonClockInfo[non_clock_array_index]; - - ps = kzalloc(sizeof(struct cz_ps), GFP_KERNEL); - if (ps == NULL) { - for (j = 0; j < i; j++) - kfree(adev->pm.dpm.ps[j].ps_priv); - kfree(adev->pm.dpm.ps); - return -ENOMEM; - } - - adev->pm.dpm.ps[i].ps_priv = ps; - k = 0; - for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) { - clock_array_index = power_state->v2.clockInfoIndex[j]; - if (clock_array_index >= clock_info_array->ucNumEntries) - continue; - if (k >= CZ_MAX_HARDWARE_POWERLEVELS) - break; - clock_info = (union pplib_clock_info *) - &clock_info_array->clockInfo[clock_array_index * - clock_info_array->ucEntrySize]; - cz_parse_pplib_clock_info(adev, &adev->pm.dpm.ps[i], - k, clock_info); - k++; - } - cz_parse_pplib_non_clock_info(adev, &adev->pm.dpm.ps[i], - non_clock_info, - non_clock_info_array->ucEntrySize); - power_state_offset += 2 + power_state->v2.ucNumDPMLevels; - } - adev->pm.dpm.num_ps = state_array->ucNumEntries; - - return 0; -} - -static int cz_process_firmware_header(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - u32 tmp; - int ret; - - ret = cz_read_smc_sram_dword(adev, SMU8_FIRMWARE_HEADER_LOCATION + - offsetof(struct SMU8_Firmware_Header, - DpmTable), - &tmp, pi->sram_end); - - if (ret == 0) - pi->dpm_table_start = tmp; - - return ret; -} - -static int cz_dpm_init(struct amdgpu_device *adev) -{ - struct cz_power_info *pi; - int ret, i; - - pi = kzalloc(sizeof(struct cz_power_info), GFP_KERNEL); - if (NULL == pi) - return -ENOMEM; - - adev->pm.dpm.priv = pi; - - ret = amdgpu_get_platform_caps(adev); - if (ret) - goto err; - - ret = amdgpu_parse_extended_power_table(adev); - if (ret) - goto err; - - pi->sram_end = SMC_RAM_END; - - /* set up DPM defaults */ - for (i = 0; i < CZ_MAX_HARDWARE_POWERLEVELS; i++) - pi->active_target[i] = CZ_AT_DFLT; - - pi->mgcg_cgtt_local0 = 0x0; - pi->mgcg_cgtt_local1 = 0x0; - pi->clock_slow_down_step = 25000; - pi->skip_clock_slow_down = 1; - pi->enable_nb_ps_policy = false; - pi->caps_power_containment = true; - pi->caps_cac = true; - pi->didt_enabled = false; - if (pi->didt_enabled) { - pi->caps_sq_ramping = true; - pi->caps_db_ramping = true; - pi->caps_td_ramping = true; - pi->caps_tcp_ramping = true; - } - if (amdgpu_pp_feature_mask & SCLK_DEEP_SLEEP_MASK) - pi->caps_sclk_ds = true; - else - pi->caps_sclk_ds = false; - - pi->voting_clients = 0x00c00033; - pi->auto_thermal_throttling_enabled = true; - pi->bapm_enabled = false; - pi->disable_nb_ps3_in_battery = false; - pi->voltage_drop_threshold = 0; - pi->caps_sclk_throttle_low_notification = false; - pi->gfx_pg_threshold = 500; - pi->caps_fps = true; - /* uvd */ - pi->caps_uvd_pg = (adev->pg_flags & AMD_PG_SUPPORT_UVD) ? true : false; - pi->caps_uvd_dpm = true; - /* vce */ - pi->caps_vce_pg = (adev->pg_flags & AMD_PG_SUPPORT_VCE) ? true : false; - pi->caps_vce_dpm = true; - /* acp */ - pi->caps_acp_pg = (adev->pg_flags & AMD_PG_SUPPORT_ACP) ? true : false; - pi->caps_acp_dpm = true; - - pi->caps_stable_power_state = false; - pi->nb_dpm_enabled_by_driver = true; - pi->nb_dpm_enabled = false; - pi->caps_voltage_island = false; - /* flags which indicate need to upload pptable */ - pi->need_pptable_upload = true; - - ret = cz_parse_sys_info_table(adev); - if (ret) - goto err; - - cz_patch_voltage_values(adev); - cz_construct_boot_state(adev); - - ret = cz_parse_power_table(adev); - if (ret) - goto err; - - ret = cz_process_firmware_header(adev); - if (ret) - goto err; - - pi->dpm_enabled = true; - pi->uvd_dynamic_pg = false; - - return 0; -err: - cz_dpm_fini(adev); - return ret; -} - -static void cz_dpm_fini(struct amdgpu_device *adev) -{ - int i; - - for (i = 0; i < adev->pm.dpm.num_ps; i++) - kfree(adev->pm.dpm.ps[i].ps_priv); - - kfree(adev->pm.dpm.ps); - kfree(adev->pm.dpm.priv); - amdgpu_free_extended_power_table(adev); -} - -#define ixSMUSVI_NB_CURRENTVID 0xD8230044 -#define CURRENT_NB_VID_MASK 0xff000000 -#define CURRENT_NB_VID__SHIFT 24 -#define ixSMUSVI_GFX_CURRENTVID 0xD8230048 -#define CURRENT_GFX_VID_MASK 0xff000000 -#define CURRENT_GFX_VID__SHIFT 24 - -static void -cz_dpm_debugfs_print_current_performance_level(struct amdgpu_device *adev, - struct seq_file *m) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk; - struct amdgpu_uvd_clock_voltage_dependency_table *uvd_table = - &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; - struct amdgpu_vce_clock_voltage_dependency_table *vce_table = - &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; - u32 sclk_index = REG_GET_FIELD(RREG32_SMC(ixTARGET_AND_CURRENT_PROFILE_INDEX), - TARGET_AND_CURRENT_PROFILE_INDEX, CURR_SCLK_INDEX); - u32 uvd_index = REG_GET_FIELD(RREG32_SMC(ixTARGET_AND_CURRENT_PROFILE_INDEX_2), - TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_UVD_INDEX); - u32 vce_index = REG_GET_FIELD(RREG32_SMC(ixTARGET_AND_CURRENT_PROFILE_INDEX_2), - TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_VCE_INDEX); - u32 sclk, vclk, dclk, ecclk, tmp; - u16 vddnb, vddgfx; - - if (sclk_index >= NUM_SCLK_LEVELS) { - seq_printf(m, "invalid sclk dpm profile %d\n", sclk_index); - } else { - sclk = table->entries[sclk_index].clk; - seq_printf(m, "%u sclk: %u\n", sclk_index, sclk); - } - - tmp = (RREG32_SMC(ixSMUSVI_NB_CURRENTVID) & - CURRENT_NB_VID_MASK) >> CURRENT_NB_VID__SHIFT; - vddnb = cz_convert_8bit_index_to_voltage(adev, (u16)tmp); - tmp = (RREG32_SMC(ixSMUSVI_GFX_CURRENTVID) & - CURRENT_GFX_VID_MASK) >> CURRENT_GFX_VID__SHIFT; - vddgfx = cz_convert_8bit_index_to_voltage(adev, (u16)tmp); - seq_printf(m, "vddnb: %u vddgfx: %u\n", vddnb, vddgfx); - - seq_printf(m, "uvd %sabled\n", pi->uvd_power_gated ? "dis" : "en"); - if (!pi->uvd_power_gated) { - if (uvd_index >= CZ_MAX_HARDWARE_POWERLEVELS) { - seq_printf(m, "invalid uvd dpm level %d\n", uvd_index); - } else { - vclk = uvd_table->entries[uvd_index].vclk; - dclk = uvd_table->entries[uvd_index].dclk; - seq_printf(m, "%u uvd vclk: %u dclk: %u\n", uvd_index, vclk, dclk); - } - } - - seq_printf(m, "vce %sabled\n", pi->vce_power_gated ? "dis" : "en"); - if (!pi->vce_power_gated) { - if (vce_index >= CZ_MAX_HARDWARE_POWERLEVELS) { - seq_printf(m, "invalid vce dpm level %d\n", vce_index); - } else { - ecclk = vce_table->entries[vce_index].ecclk; - seq_printf(m, "%u vce ecclk: %u\n", vce_index, ecclk); - } - } -} - -static void cz_dpm_print_power_state(struct amdgpu_device *adev, - struct amdgpu_ps *rps) -{ - int i; - struct cz_ps *ps = cz_get_ps(rps); - - amdgpu_dpm_print_class_info(rps->class, rps->class2); - amdgpu_dpm_print_cap_info(rps->caps); - - DRM_INFO("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); - for (i = 0; i < ps->num_levels; i++) { - struct cz_pl *pl = &ps->levels[i]; - - DRM_INFO("\t\tpower level %d sclk: %u vddc: %u\n", - i, pl->sclk, - cz_convert_8bit_index_to_voltage(adev, pl->vddc_index)); - } - - amdgpu_dpm_print_ps_status(adev, rps); -} - -static void cz_dpm_set_funcs(struct amdgpu_device *adev); - -static int cz_dpm_early_init(void *handle) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - cz_dpm_set_funcs(adev); - - return 0; -} - - -static int cz_dpm_late_init(void *handle) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - if (amdgpu_dpm) { - int ret; - /* init the sysfs and debugfs files late */ - ret = amdgpu_pm_sysfs_init(adev); - if (ret) - return ret; - - /* powerdown unused blocks for now */ - cz_dpm_powergate_uvd(adev, true); - cz_dpm_powergate_vce(adev, true); - } - - return 0; -} - -static int cz_dpm_sw_init(void *handle) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - int ret = 0; - /* fix me to add thermal support TODO */ - - /* default to balanced state */ - adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; - adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; - adev->pm.dpm.forced_level = AMDGPU_DPM_FORCED_LEVEL_AUTO; - adev->pm.default_sclk = adev->clock.default_sclk; - adev->pm.default_mclk = adev->clock.default_mclk; - adev->pm.current_sclk = adev->clock.default_sclk; - adev->pm.current_mclk = adev->clock.default_mclk; - adev->pm.int_thermal_type = THERMAL_TYPE_NONE; - - if (amdgpu_dpm == 0) - return 0; - - mutex_lock(&adev->pm.mutex); - ret = cz_dpm_init(adev); - if (ret) - goto dpm_init_failed; - - adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps; - if (amdgpu_dpm == 1) - amdgpu_pm_print_power_states(adev); - - mutex_unlock(&adev->pm.mutex); - DRM_INFO("amdgpu: dpm initialized\n"); - - return 0; - -dpm_init_failed: - cz_dpm_fini(adev); - mutex_unlock(&adev->pm.mutex); - DRM_ERROR("amdgpu: dpm initialization failed\n"); - - return ret; -} - -static int cz_dpm_sw_fini(void *handle) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - mutex_lock(&adev->pm.mutex); - amdgpu_pm_sysfs_fini(adev); - cz_dpm_fini(adev); - mutex_unlock(&adev->pm.mutex); - - return 0; -} - -static void cz_reset_ap_mask(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - pi->active_process_mask = 0; -} - -static int cz_dpm_download_pptable_from_smu(struct amdgpu_device *adev, - void **table) -{ - return cz_smu_download_pptable(adev, table); -} - -static int cz_dpm_upload_pptable_to_smu(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct SMU8_Fusion_ClkTable *clock_table; - struct atom_clock_dividers dividers; - void *table = NULL; - uint8_t i = 0; - int ret = 0; - - struct amdgpu_clock_voltage_dependency_table *vddc_table = - &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk; - struct amdgpu_clock_voltage_dependency_table *vddgfx_table = - &adev->pm.dpm.dyn_state.vddgfx_dependency_on_sclk; - struct amdgpu_uvd_clock_voltage_dependency_table *uvd_table = - &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; - struct amdgpu_vce_clock_voltage_dependency_table *vce_table = - &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; - struct amdgpu_clock_voltage_dependency_table *acp_table = - &adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table; - - if (!pi->need_pptable_upload) - return 0; - - ret = cz_dpm_download_pptable_from_smu(adev, &table); - if (ret) { - DRM_ERROR("amdgpu: Failed to get power play table from SMU!\n"); - return -EINVAL; - } - - clock_table = (struct SMU8_Fusion_ClkTable *)table; - /* patch clock table */ - if (vddc_table->count > CZ_MAX_HARDWARE_POWERLEVELS || - vddgfx_table->count > CZ_MAX_HARDWARE_POWERLEVELS || - uvd_table->count > CZ_MAX_HARDWARE_POWERLEVELS || - vce_table->count > CZ_MAX_HARDWARE_POWERLEVELS || - acp_table->count > CZ_MAX_HARDWARE_POWERLEVELS) { - DRM_ERROR("amdgpu: Invalid Clock Voltage Dependency Table!\n"); - return -EINVAL; - } - - for (i = 0; i < CZ_MAX_HARDWARE_POWERLEVELS; i++) { - - /* vddc sclk */ - clock_table->SclkBreakdownTable.ClkLevel[i].GnbVid = - (i < vddc_table->count) ? (uint8_t)vddc_table->entries[i].v : 0; - clock_table->SclkBreakdownTable.ClkLevel[i].Frequency = - (i < vddc_table->count) ? vddc_table->entries[i].clk : 0; - ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, - clock_table->SclkBreakdownTable.ClkLevel[i].Frequency, - false, ÷rs); - if (ret) - return ret; - clock_table->SclkBreakdownTable.ClkLevel[i].DfsDid = - (uint8_t)dividers.post_divider; - - /* vddgfx sclk */ - clock_table->SclkBreakdownTable.ClkLevel[i].GfxVid = - (i < vddgfx_table->count) ? (uint8_t)vddgfx_table->entries[i].v : 0; - - /* acp breakdown */ - clock_table->AclkBreakdownTable.ClkLevel[i].GfxVid = - (i < acp_table->count) ? (uint8_t)acp_table->entries[i].v : 0; - clock_table->AclkBreakdownTable.ClkLevel[i].Frequency = - (i < acp_table->count) ? acp_table->entries[i].clk : 0; - ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, - clock_table->SclkBreakdownTable.ClkLevel[i].Frequency, - false, ÷rs); - if (ret) - return ret; - clock_table->AclkBreakdownTable.ClkLevel[i].DfsDid = - (uint8_t)dividers.post_divider; - - /* uvd breakdown */ - clock_table->VclkBreakdownTable.ClkLevel[i].GfxVid = - (i < uvd_table->count) ? (uint8_t)uvd_table->entries[i].v : 0; - clock_table->VclkBreakdownTable.ClkLevel[i].Frequency = - (i < uvd_table->count) ? uvd_table->entries[i].vclk : 0; - ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, - clock_table->VclkBreakdownTable.ClkLevel[i].Frequency, - false, ÷rs); - if (ret) - return ret; - clock_table->VclkBreakdownTable.ClkLevel[i].DfsDid = - (uint8_t)dividers.post_divider; - - clock_table->DclkBreakdownTable.ClkLevel[i].GfxVid = - (i < uvd_table->count) ? (uint8_t)uvd_table->entries[i].v : 0; - clock_table->DclkBreakdownTable.ClkLevel[i].Frequency = - (i < uvd_table->count) ? uvd_table->entries[i].dclk : 0; - ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, - clock_table->DclkBreakdownTable.ClkLevel[i].Frequency, - false, ÷rs); - if (ret) - return ret; - clock_table->DclkBreakdownTable.ClkLevel[i].DfsDid = - (uint8_t)dividers.post_divider; - - /* vce breakdown */ - clock_table->EclkBreakdownTable.ClkLevel[i].GfxVid = - (i < vce_table->count) ? (uint8_t)vce_table->entries[i].v : 0; - clock_table->EclkBreakdownTable.ClkLevel[i].Frequency = - (i < vce_table->count) ? vce_table->entries[i].ecclk : 0; - ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, - clock_table->EclkBreakdownTable.ClkLevel[i].Frequency, - false, ÷rs); - if (ret) - return ret; - clock_table->EclkBreakdownTable.ClkLevel[i].DfsDid = - (uint8_t)dividers.post_divider; - } - - /* its time to upload to SMU */ - ret = cz_smu_upload_pptable(adev); - if (ret) { - DRM_ERROR("amdgpu: Failed to put power play table to SMU!\n"); - return ret; - } - - return 0; -} - -static void cz_init_sclk_limit(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk; - uint32_t clock = 0, level; - - if (!table || !table->count) { - DRM_ERROR("Invalid Voltage Dependency table.\n"); - return; - } - - pi->sclk_dpm.soft_min_clk = 0; - pi->sclk_dpm.hard_min_clk = 0; - cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxSclkLevel); - level = cz_get_argument(adev); - if (level < table->count) { - clock = table->entries[level].clk; - } else { - DRM_ERROR("Invalid SLCK Voltage Dependency table entry.\n"); - clock = table->entries[table->count - 1].clk; - } - - pi->sclk_dpm.soft_max_clk = clock; - pi->sclk_dpm.hard_max_clk = clock; - -} - -static void cz_init_uvd_limit(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_uvd_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; - uint32_t clock = 0, level; - - if (!table || !table->count) { - DRM_ERROR("Invalid Voltage Dependency table.\n"); - return; - } - - pi->uvd_dpm.soft_min_clk = 0; - pi->uvd_dpm.hard_min_clk = 0; - cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxUvdLevel); - level = cz_get_argument(adev); - if (level < table->count) { - clock = table->entries[level].vclk; - } else { - DRM_ERROR("Invalid UVD Voltage Dependency table entry.\n"); - clock = table->entries[table->count - 1].vclk; - } - - pi->uvd_dpm.soft_max_clk = clock; - pi->uvd_dpm.hard_max_clk = clock; - -} - -static void cz_init_vce_limit(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_vce_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; - uint32_t clock = 0, level; - - if (!table || !table->count) { - DRM_ERROR("Invalid Voltage Dependency table.\n"); - return; - } - - pi->vce_dpm.soft_min_clk = table->entries[0].ecclk; - pi->vce_dpm.hard_min_clk = table->entries[0].ecclk; - cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxEclkLevel); - level = cz_get_argument(adev); - if (level < table->count) { - clock = table->entries[level].ecclk; - } else { - /* future BIOS would fix this error */ - DRM_ERROR("Invalid VCE Voltage Dependency table entry.\n"); - clock = table->entries[table->count - 1].ecclk; - } - - pi->vce_dpm.soft_max_clk = clock; - pi->vce_dpm.hard_max_clk = clock; - -} - -static void cz_init_acp_limit(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table; - uint32_t clock = 0, level; - - if (!table || !table->count) { - DRM_ERROR("Invalid Voltage Dependency table.\n"); - return; - } - - pi->acp_dpm.soft_min_clk = 0; - pi->acp_dpm.hard_min_clk = 0; - cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxAclkLevel); - level = cz_get_argument(adev); - if (level < table->count) { - clock = table->entries[level].clk; - } else { - DRM_ERROR("Invalid ACP Voltage Dependency table entry.\n"); - clock = table->entries[table->count - 1].clk; - } - - pi->acp_dpm.soft_max_clk = clock; - pi->acp_dpm.hard_max_clk = clock; - -} - -static void cz_init_pg_state(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - pi->uvd_power_gated = false; - pi->vce_power_gated = false; - pi->acp_power_gated = false; - -} - -static void cz_init_sclk_threshold(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - pi->low_sclk_interrupt_threshold = 0; -} - -static void cz_dpm_setup_asic(struct amdgpu_device *adev) -{ - cz_reset_ap_mask(adev); - cz_dpm_upload_pptable_to_smu(adev); - cz_init_sclk_limit(adev); - cz_init_uvd_limit(adev); - cz_init_vce_limit(adev); - cz_init_acp_limit(adev); - cz_init_pg_state(adev); - cz_init_sclk_threshold(adev); - -} - -static bool cz_check_smu_feature(struct amdgpu_device *adev, - uint32_t feature) -{ - uint32_t smu_feature = 0; - int ret; - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_GetFeatureStatus, 0); - if (ret) { - DRM_ERROR("Failed to get SMU features from SMC.\n"); - return false; - } else { - smu_feature = cz_get_argument(adev); - if (feature & smu_feature) - return true; - } - - return false; -} - -static bool cz_check_for_dpm_enabled(struct amdgpu_device *adev) -{ - if (cz_check_smu_feature(adev, - SMU_EnabledFeatureScoreboard_SclkDpmOn)) - return true; - - return false; -} - -static void cz_program_voting_clients(struct amdgpu_device *adev) -{ - WREG32_SMC(ixCG_FREQ_TRAN_VOTING_0, PPCZ_VOTINGRIGHTSCLIENTS_DFLT0); -} - -static void cz_clear_voting_clients(struct amdgpu_device *adev) -{ - WREG32_SMC(ixCG_FREQ_TRAN_VOTING_0, 0); -} - -static int cz_start_dpm(struct amdgpu_device *adev) -{ - int ret = 0; - - if (amdgpu_dpm) { - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_EnableAllSmuFeatures, SCLK_DPM_MASK); - if (ret) { - DRM_ERROR("SMU feature: SCLK_DPM enable failed\n"); - return -EINVAL; - } - } - - return 0; -} - -static int cz_stop_dpm(struct amdgpu_device *adev) -{ - int ret = 0; - - if (amdgpu_dpm && adev->pm.dpm_enabled) { - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_DisableAllSmuFeatures, SCLK_DPM_MASK); - if (ret) { - DRM_ERROR("SMU feature: SCLK_DPM disable failed\n"); - return -EINVAL; - } - } - - return 0; -} - -static uint32_t cz_get_sclk_level(struct amdgpu_device *adev, - uint32_t clock, uint16_t msg) -{ - int i = 0; - struct amdgpu_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk; - - switch (msg) { - case PPSMC_MSG_SetSclkSoftMin: - case PPSMC_MSG_SetSclkHardMin: - for (i = 0; i < table->count; i++) - if (clock <= table->entries[i].clk) - break; - if (i == table->count) - i = table->count - 1; - break; - case PPSMC_MSG_SetSclkSoftMax: - case PPSMC_MSG_SetSclkHardMax: - for (i = table->count - 1; i >= 0; i--) - if (clock >= table->entries[i].clk) - break; - if (i < 0) - i = 0; - break; - default: - break; - } - - return i; -} - -static uint32_t cz_get_eclk_level(struct amdgpu_device *adev, - uint32_t clock, uint16_t msg) -{ - int i = 0; - struct amdgpu_vce_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; - - if (table->count == 0) - return 0; - - switch (msg) { - case PPSMC_MSG_SetEclkSoftMin: - case PPSMC_MSG_SetEclkHardMin: - for (i = 0; i < table->count-1; i++) - if (clock <= table->entries[i].ecclk) - break; - break; - case PPSMC_MSG_SetEclkSoftMax: - case PPSMC_MSG_SetEclkHardMax: - for (i = table->count - 1; i > 0; i--) - if (clock >= table->entries[i].ecclk) - break; - break; - default: - break; - } - - return i; -} - -static uint32_t cz_get_uvd_level(struct amdgpu_device *adev, - uint32_t clock, uint16_t msg) -{ - int i = 0; - struct amdgpu_uvd_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; - - switch (msg) { - case PPSMC_MSG_SetUvdSoftMin: - case PPSMC_MSG_SetUvdHardMin: - for (i = 0; i < table->count; i++) - if (clock <= table->entries[i].vclk) - break; - if (i == table->count) - i = table->count - 1; - break; - case PPSMC_MSG_SetUvdSoftMax: - case PPSMC_MSG_SetUvdHardMax: - for (i = table->count - 1; i >= 0; i--) - if (clock >= table->entries[i].vclk) - break; - if (i < 0) - i = 0; - break; - default: - break; - } - - return i; -} - -static int cz_program_bootup_state(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - uint32_t soft_min_clk = 0; - uint32_t soft_max_clk = 0; - int ret = 0; - - pi->sclk_dpm.soft_min_clk = pi->sys_info.bootup_sclk; - pi->sclk_dpm.soft_max_clk = pi->sys_info.bootup_sclk; - - soft_min_clk = cz_get_sclk_level(adev, - pi->sclk_dpm.soft_min_clk, - PPSMC_MSG_SetSclkSoftMin); - soft_max_clk = cz_get_sclk_level(adev, - pi->sclk_dpm.soft_max_clk, - PPSMC_MSG_SetSclkSoftMax); - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMin, soft_min_clk); - if (ret) - return -EINVAL; - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMax, soft_max_clk); - if (ret) - return -EINVAL; - - return 0; -} - -/* TODO */ -static int cz_disable_cgpg(struct amdgpu_device *adev) -{ - return 0; -} - -/* TODO */ -static int cz_enable_cgpg(struct amdgpu_device *adev) -{ - return 0; -} - -/* TODO */ -static int cz_program_pt_config_registers(struct amdgpu_device *adev) -{ - return 0; -} - -static void cz_do_enable_didt(struct amdgpu_device *adev, bool enable) -{ - struct cz_power_info *pi = cz_get_pi(adev); - uint32_t reg = 0; - - if (pi->caps_sq_ramping) { - reg = RREG32_DIDT(ixDIDT_SQ_CTRL0); - if (enable) - reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 1); - else - reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 0); - WREG32_DIDT(ixDIDT_SQ_CTRL0, reg); - } - if (pi->caps_db_ramping) { - reg = RREG32_DIDT(ixDIDT_DB_CTRL0); - if (enable) - reg = REG_SET_FIELD(reg, DIDT_DB_CTRL0, DIDT_CTRL_EN, 1); - else - reg = REG_SET_FIELD(reg, DIDT_DB_CTRL0, DIDT_CTRL_EN, 0); - WREG32_DIDT(ixDIDT_DB_CTRL0, reg); - } - if (pi->caps_td_ramping) { - reg = RREG32_DIDT(ixDIDT_TD_CTRL0); - if (enable) - reg = REG_SET_FIELD(reg, DIDT_TD_CTRL0, DIDT_CTRL_EN, 1); - else - reg = REG_SET_FIELD(reg, DIDT_TD_CTRL0, DIDT_CTRL_EN, 0); - WREG32_DIDT(ixDIDT_TD_CTRL0, reg); - } - if (pi->caps_tcp_ramping) { - reg = RREG32_DIDT(ixDIDT_TCP_CTRL0); - if (enable) - reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 1); - else - reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 0); - WREG32_DIDT(ixDIDT_TCP_CTRL0, reg); - } - -} - -static int cz_enable_didt(struct amdgpu_device *adev, bool enable) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret; - - if (pi->caps_sq_ramping || pi->caps_db_ramping || - pi->caps_td_ramping || pi->caps_tcp_ramping) { - if (adev->gfx.gfx_current_status != AMDGPU_GFX_SAFE_MODE) { - ret = cz_disable_cgpg(adev); - if (ret) { - DRM_ERROR("Pre Di/Dt disable cg/pg failed\n"); - return -EINVAL; - } - adev->gfx.gfx_current_status = AMDGPU_GFX_SAFE_MODE; - } - - ret = cz_program_pt_config_registers(adev); - if (ret) { - DRM_ERROR("Di/Dt config failed\n"); - return -EINVAL; - } - cz_do_enable_didt(adev, enable); - - if (adev->gfx.gfx_current_status == AMDGPU_GFX_SAFE_MODE) { - ret = cz_enable_cgpg(adev); - if (ret) { - DRM_ERROR("Post Di/Dt enable cg/pg failed\n"); - return -EINVAL; - } - adev->gfx.gfx_current_status = AMDGPU_GFX_NORMAL_MODE; - } - } - - return 0; -} - -/* TODO */ -static void cz_reset_acp_boot_level(struct amdgpu_device *adev) -{ -} - -static void cz_update_current_ps(struct amdgpu_device *adev, - struct amdgpu_ps *rps) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct cz_ps *ps = cz_get_ps(rps); - - pi->current_ps = *ps; - pi->current_rps = *rps; - pi->current_rps.ps_priv = &pi->current_ps; - adev->pm.dpm.current_ps = &pi->current_rps; - -} - -static void cz_update_requested_ps(struct amdgpu_device *adev, - struct amdgpu_ps *rps) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct cz_ps *ps = cz_get_ps(rps); - - pi->requested_ps = *ps; - pi->requested_rps = *rps; - pi->requested_rps.ps_priv = &pi->requested_ps; - adev->pm.dpm.requested_ps = &pi->requested_rps; - -} - -/* PP arbiter support needed TODO */ -static void cz_apply_state_adjust_rules(struct amdgpu_device *adev, - struct amdgpu_ps *new_rps, - struct amdgpu_ps *old_rps) -{ - struct cz_ps *ps = cz_get_ps(new_rps); - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_clock_and_voltage_limits *limits = - &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac; - /* 10kHz memory clock */ - uint32_t mclk = 0; - - ps->force_high = false; - ps->need_dfs_bypass = true; - pi->video_start = new_rps->dclk || new_rps->vclk || - new_rps->evclk || new_rps->ecclk; - - if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == - ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) - pi->battery_state = true; - else - pi->battery_state = false; - - if (pi->caps_stable_power_state) - mclk = limits->mclk; - - if (mclk > pi->sys_info.nbp_memory_clock[CZ_NUM_NBPMEMORY_CLOCK - 1]) - ps->force_high = true; - -} - -static int cz_dpm_enable(struct amdgpu_device *adev) -{ - const char *chip_name; - int ret = 0; - - /* renable will hang up SMU, so check first */ - if (cz_check_for_dpm_enabled(adev)) - return -EINVAL; - - cz_program_voting_clients(adev); - - switch (adev->asic_type) { - case CHIP_CARRIZO: - chip_name = "carrizo"; - break; - case CHIP_STONEY: - chip_name = "stoney"; - break; - default: - BUG(); - } - - - ret = cz_start_dpm(adev); - if (ret) { - DRM_ERROR("%s DPM enable failed\n", chip_name); - return -EINVAL; - } - - ret = cz_program_bootup_state(adev); - if (ret) { - DRM_ERROR("%s bootup state program failed\n", chip_name); - return -EINVAL; - } - - ret = cz_enable_didt(adev, true); - if (ret) { - DRM_ERROR("%s enable di/dt failed\n", chip_name); - return -EINVAL; - } - - cz_reset_acp_boot_level(adev); - cz_update_current_ps(adev, adev->pm.dpm.boot_ps); - - return 0; -} - -static int cz_dpm_hw_init(void *handle) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - int ret = 0; - - mutex_lock(&adev->pm.mutex); - - /* smu init only needs to be called at startup, not resume. - * It should be in sw_init, but requires the fw info gathered - * in sw_init from other IP modules. - */ - ret = cz_smu_init(adev); - if (ret) { - DRM_ERROR("amdgpu: smc initialization failed\n"); - mutex_unlock(&adev->pm.mutex); - return ret; - } - - /* do the actual fw loading */ - ret = cz_smu_start(adev); - if (ret) { - DRM_ERROR("amdgpu: smc start failed\n"); - mutex_unlock(&adev->pm.mutex); - return ret; - } - - if (!amdgpu_dpm) { - adev->pm.dpm_enabled = false; - mutex_unlock(&adev->pm.mutex); - return ret; - } - - /* cz dpm setup asic */ - cz_dpm_setup_asic(adev); - - /* cz dpm enable */ - ret = cz_dpm_enable(adev); - if (ret) - adev->pm.dpm_enabled = false; - else - adev->pm.dpm_enabled = true; - - mutex_unlock(&adev->pm.mutex); - - return 0; -} - -static int cz_dpm_disable(struct amdgpu_device *adev) -{ - int ret = 0; - - if (!cz_check_for_dpm_enabled(adev)) - return -EINVAL; - - ret = cz_enable_didt(adev, false); - if (ret) { - DRM_ERROR("disable di/dt failed\n"); - return -EINVAL; - } - - /* powerup blocks */ - cz_dpm_powergate_uvd(adev, false); - cz_dpm_powergate_vce(adev, false); - - cz_clear_voting_clients(adev); - cz_stop_dpm(adev); - cz_update_current_ps(adev, adev->pm.dpm.boot_ps); - - return 0; -} - -static int cz_dpm_hw_fini(void *handle) -{ - int ret = 0; - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - mutex_lock(&adev->pm.mutex); - - /* smu fini only needs to be called at teardown, not suspend. - * It should be in sw_fini, but we put it here for symmetry - * with smu init. - */ - cz_smu_fini(adev); - - if (adev->pm.dpm_enabled) { - ret = cz_dpm_disable(adev); - - adev->pm.dpm.current_ps = - adev->pm.dpm.requested_ps = - adev->pm.dpm.boot_ps; - } - - adev->pm.dpm_enabled = false; - - mutex_unlock(&adev->pm.mutex); - - return ret; -} - -static int cz_dpm_suspend(void *handle) -{ - int ret = 0; - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - if (adev->pm.dpm_enabled) { - mutex_lock(&adev->pm.mutex); - - ret = cz_dpm_disable(adev); - - adev->pm.dpm.current_ps = - adev->pm.dpm.requested_ps = - adev->pm.dpm.boot_ps; - - mutex_unlock(&adev->pm.mutex); - } - - return ret; -} - -static int cz_dpm_resume(void *handle) -{ - int ret = 0; - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - mutex_lock(&adev->pm.mutex); - - /* do the actual fw loading */ - ret = cz_smu_start(adev); - if (ret) { - DRM_ERROR("amdgpu: smc start failed\n"); - mutex_unlock(&adev->pm.mutex); - return ret; - } - - if (!amdgpu_dpm) { - adev->pm.dpm_enabled = false; - mutex_unlock(&adev->pm.mutex); - return ret; - } - - /* cz dpm setup asic */ - cz_dpm_setup_asic(adev); - - /* cz dpm enable */ - ret = cz_dpm_enable(adev); - if (ret) - adev->pm.dpm_enabled = false; - else - adev->pm.dpm_enabled = true; - - mutex_unlock(&adev->pm.mutex); - /* upon resume, re-compute the clocks */ - if (adev->pm.dpm_enabled) - amdgpu_pm_compute_clocks(adev); - - return 0; -} - -static int cz_dpm_set_clockgating_state(void *handle, - enum amd_clockgating_state state) -{ - return 0; -} - -static int cz_dpm_set_powergating_state(void *handle, - enum amd_powergating_state state) -{ - return 0; -} - -static int cz_dpm_get_temperature(struct amdgpu_device *adev) -{ - int actual_temp = 0; - uint32_t val = RREG32_SMC(ixTHM_TCON_CUR_TMP); - uint32_t temp = REG_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP); - - if (REG_GET_FIELD(val, THM_TCON_CUR_TMP, CUR_TEMP_RANGE_SEL)) - actual_temp = 1000 * ((temp / 8) - 49); - else - actual_temp = 1000 * (temp / 8); - - return actual_temp; -} - -static int cz_dpm_pre_set_power_state(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_ps requested_ps = *adev->pm.dpm.requested_ps; - struct amdgpu_ps *new_ps = &requested_ps; - - cz_update_requested_ps(adev, new_ps); - cz_apply_state_adjust_rules(adev, &pi->requested_rps, - &pi->current_rps); - - return 0; -} - -static int cz_dpm_update_sclk_limit(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_clock_and_voltage_limits *limits = - &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac; - uint32_t clock, stable_ps_clock = 0; - - clock = pi->sclk_dpm.soft_min_clk; - - if (pi->caps_stable_power_state) { - stable_ps_clock = limits->sclk * 75 / 100; - if (clock < stable_ps_clock) - clock = stable_ps_clock; - } - - if (clock != pi->sclk_dpm.soft_min_clk) { - pi->sclk_dpm.soft_min_clk = clock; - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMin, - cz_get_sclk_level(adev, clock, - PPSMC_MSG_SetSclkSoftMin)); - } - - if (pi->caps_stable_power_state && - pi->sclk_dpm.soft_max_clk != clock) { - pi->sclk_dpm.soft_max_clk = clock; - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMax, - cz_get_sclk_level(adev, clock, - PPSMC_MSG_SetSclkSoftMax)); - } else { - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMax, - cz_get_sclk_level(adev, - pi->sclk_dpm.soft_max_clk, - PPSMC_MSG_SetSclkSoftMax)); - } - - return 0; -} - -static int cz_dpm_set_deep_sleep_sclk_threshold(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - if (pi->caps_sclk_ds) { - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetMinDeepSleepSclk, - CZ_MIN_DEEP_SLEEP_SCLK); - } - - return 0; -} - -/* ?? without dal support, is this still needed in setpowerstate list*/ -static int cz_dpm_set_watermark_threshold(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetWatermarkFrequency, - pi->sclk_dpm.soft_max_clk); - - return 0; -} - -static int cz_dpm_enable_nbdpm(struct amdgpu_device *adev) -{ - int ret = 0; - struct cz_power_info *pi = cz_get_pi(adev); - - /* also depend on dal NBPStateDisableRequired */ - if (pi->nb_dpm_enabled_by_driver && !pi->nb_dpm_enabled) { - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_EnableAllSmuFeatures, - NB_DPM_MASK); - if (ret) { - DRM_ERROR("amdgpu: nb dpm enable failed\n"); - return ret; - } - pi->nb_dpm_enabled = true; - } - - return ret; -} - -static void cz_dpm_nbdpm_lm_pstate_enable(struct amdgpu_device *adev, - bool enable) -{ - if (enable) - cz_send_msg_to_smc(adev, PPSMC_MSG_EnableLowMemoryPstate); - else - cz_send_msg_to_smc(adev, PPSMC_MSG_DisableLowMemoryPstate); - -} - -static int cz_dpm_update_low_memory_pstate(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct cz_ps *ps = &pi->requested_ps; - - if (pi->sys_info.nb_dpm_enable) { - if (ps->force_high) - cz_dpm_nbdpm_lm_pstate_enable(adev, false); - else - cz_dpm_nbdpm_lm_pstate_enable(adev, true); - } - - return 0; -} - -/* with dpm enabled */ -static int cz_dpm_set_power_state(struct amdgpu_device *adev) -{ - cz_dpm_update_sclk_limit(adev); - cz_dpm_set_deep_sleep_sclk_threshold(adev); - cz_dpm_set_watermark_threshold(adev); - cz_dpm_enable_nbdpm(adev); - cz_dpm_update_low_memory_pstate(adev); - - return 0; -} - -static void cz_dpm_post_set_power_state(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_ps *ps = &pi->requested_rps; - - cz_update_current_ps(adev, ps); -} - -static int cz_dpm_force_highest(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (pi->sclk_dpm.soft_min_clk != pi->sclk_dpm.soft_max_clk) { - pi->sclk_dpm.soft_min_clk = - pi->sclk_dpm.soft_max_clk; - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMin, - cz_get_sclk_level(adev, - pi->sclk_dpm.soft_min_clk, - PPSMC_MSG_SetSclkSoftMin)); - if (ret) - return ret; - } - - return ret; -} - -static int cz_dpm_force_lowest(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (pi->sclk_dpm.soft_max_clk != pi->sclk_dpm.soft_min_clk) { - pi->sclk_dpm.soft_max_clk = pi->sclk_dpm.soft_min_clk; - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMax, - cz_get_sclk_level(adev, - pi->sclk_dpm.soft_max_clk, - PPSMC_MSG_SetSclkSoftMax)); - if (ret) - return ret; - } - - return ret; -} - -static uint32_t cz_dpm_get_max_sclk_level(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - if (!pi->max_sclk_level) { - cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxSclkLevel); - pi->max_sclk_level = cz_get_argument(adev) + 1; - } - - if (pi->max_sclk_level > CZ_MAX_HARDWARE_POWERLEVELS) { - DRM_ERROR("Invalid max sclk level!\n"); - return -EINVAL; - } - - return pi->max_sclk_level; -} - -static int cz_dpm_unforce_dpm_levels(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_clock_voltage_dependency_table *dep_table = - &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk; - uint32_t level = 0; - int ret = 0; - - pi->sclk_dpm.soft_min_clk = dep_table->entries[0].clk; - level = cz_dpm_get_max_sclk_level(adev) - 1; - if (level < dep_table->count) - pi->sclk_dpm.soft_max_clk = dep_table->entries[level].clk; - else - pi->sclk_dpm.soft_max_clk = - dep_table->entries[dep_table->count - 1].clk; - - /* get min/max sclk soft value - * notify SMU to execute */ - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMin, - cz_get_sclk_level(adev, - pi->sclk_dpm.soft_min_clk, - PPSMC_MSG_SetSclkSoftMin)); - if (ret) - return ret; - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetSclkSoftMax, - cz_get_sclk_level(adev, - pi->sclk_dpm.soft_max_clk, - PPSMC_MSG_SetSclkSoftMax)); - if (ret) - return ret; - - DRM_DEBUG("DPM unforce state min=%d, max=%d.\n", - pi->sclk_dpm.soft_min_clk, - pi->sclk_dpm.soft_max_clk); - - return 0; -} - -static int cz_dpm_uvd_force_highest(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (pi->uvd_dpm.soft_min_clk != pi->uvd_dpm.soft_max_clk) { - pi->uvd_dpm.soft_min_clk = - pi->uvd_dpm.soft_max_clk; - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetUvdSoftMin, - cz_get_uvd_level(adev, - pi->uvd_dpm.soft_min_clk, - PPSMC_MSG_SetUvdSoftMin)); - if (ret) - return ret; - } - - return ret; -} - -static int cz_dpm_uvd_force_lowest(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (pi->uvd_dpm.soft_max_clk != pi->uvd_dpm.soft_min_clk) { - pi->uvd_dpm.soft_max_clk = pi->uvd_dpm.soft_min_clk; - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetUvdSoftMax, - cz_get_uvd_level(adev, - pi->uvd_dpm.soft_max_clk, - PPSMC_MSG_SetUvdSoftMax)); - if (ret) - return ret; - } - - return ret; -} - -static uint32_t cz_dpm_get_max_uvd_level(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - if (!pi->max_uvd_level) { - cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxUvdLevel); - pi->max_uvd_level = cz_get_argument(adev) + 1; - } - - if (pi->max_uvd_level > CZ_MAX_HARDWARE_POWERLEVELS) { - DRM_ERROR("Invalid max uvd level!\n"); - return -EINVAL; - } - - return pi->max_uvd_level; -} - -static int cz_dpm_unforce_uvd_dpm_levels(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_uvd_clock_voltage_dependency_table *dep_table = - &adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table; - uint32_t level = 0; - int ret = 0; - - pi->uvd_dpm.soft_min_clk = dep_table->entries[0].vclk; - level = cz_dpm_get_max_uvd_level(adev) - 1; - if (level < dep_table->count) - pi->uvd_dpm.soft_max_clk = dep_table->entries[level].vclk; - else - pi->uvd_dpm.soft_max_clk = - dep_table->entries[dep_table->count - 1].vclk; - - /* get min/max sclk soft value - * notify SMU to execute */ - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetUvdSoftMin, - cz_get_uvd_level(adev, - pi->uvd_dpm.soft_min_clk, - PPSMC_MSG_SetUvdSoftMin)); - if (ret) - return ret; - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetUvdSoftMax, - cz_get_uvd_level(adev, - pi->uvd_dpm.soft_max_clk, - PPSMC_MSG_SetUvdSoftMax)); - if (ret) - return ret; - - DRM_DEBUG("DPM uvd unforce state min=%d, max=%d.\n", - pi->uvd_dpm.soft_min_clk, - pi->uvd_dpm.soft_max_clk); - - return 0; -} - -static int cz_dpm_vce_force_highest(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (pi->vce_dpm.soft_min_clk != pi->vce_dpm.soft_max_clk) { - pi->vce_dpm.soft_min_clk = - pi->vce_dpm.soft_max_clk; - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetEclkSoftMin, - cz_get_eclk_level(adev, - pi->vce_dpm.soft_min_clk, - PPSMC_MSG_SetEclkSoftMin)); - if (ret) - return ret; - } - - return ret; -} - -static int cz_dpm_vce_force_lowest(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (pi->vce_dpm.soft_max_clk != pi->vce_dpm.soft_min_clk) { - pi->vce_dpm.soft_max_clk = pi->vce_dpm.soft_min_clk; - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetEclkSoftMax, - cz_get_uvd_level(adev, - pi->vce_dpm.soft_max_clk, - PPSMC_MSG_SetEclkSoftMax)); - if (ret) - return ret; - } - - return ret; -} - -static uint32_t cz_dpm_get_max_vce_level(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - if (!pi->max_vce_level) { - cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxEclkLevel); - pi->max_vce_level = cz_get_argument(adev) + 1; - } - - if (pi->max_vce_level > CZ_MAX_HARDWARE_POWERLEVELS) { - DRM_ERROR("Invalid max vce level!\n"); - return -EINVAL; - } - - return pi->max_vce_level; -} - -static int cz_dpm_unforce_vce_dpm_levels(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_vce_clock_voltage_dependency_table *dep_table = - &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; - uint32_t level = 0; - int ret = 0; - - pi->vce_dpm.soft_min_clk = dep_table->entries[0].ecclk; - level = cz_dpm_get_max_vce_level(adev) - 1; - if (level < dep_table->count) - pi->vce_dpm.soft_max_clk = dep_table->entries[level].ecclk; - else - pi->vce_dpm.soft_max_clk = - dep_table->entries[dep_table->count - 1].ecclk; - - /* get min/max sclk soft value - * notify SMU to execute */ - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetEclkSoftMin, - cz_get_eclk_level(adev, - pi->vce_dpm.soft_min_clk, - PPSMC_MSG_SetEclkSoftMin)); - if (ret) - return ret; - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetEclkSoftMax, - cz_get_eclk_level(adev, - pi->vce_dpm.soft_max_clk, - PPSMC_MSG_SetEclkSoftMax)); - if (ret) - return ret; - - DRM_DEBUG("DPM vce unforce state min=%d, max=%d.\n", - pi->vce_dpm.soft_min_clk, - pi->vce_dpm.soft_max_clk); - - return 0; -} - -static int cz_dpm_force_dpm_level(struct amdgpu_device *adev, - enum amdgpu_dpm_forced_level level) -{ - int ret = 0; - - switch (level) { - case AMDGPU_DPM_FORCED_LEVEL_HIGH: - /* sclk */ - ret = cz_dpm_unforce_dpm_levels(adev); - if (ret) - return ret; - ret = cz_dpm_force_highest(adev); - if (ret) - return ret; - - /* uvd */ - ret = cz_dpm_unforce_uvd_dpm_levels(adev); - if (ret) - return ret; - ret = cz_dpm_uvd_force_highest(adev); - if (ret) - return ret; - - /* vce */ - ret = cz_dpm_unforce_vce_dpm_levels(adev); - if (ret) - return ret; - ret = cz_dpm_vce_force_highest(adev); - if (ret) - return ret; - break; - case AMDGPU_DPM_FORCED_LEVEL_LOW: - /* sclk */ - ret = cz_dpm_unforce_dpm_levels(adev); - if (ret) - return ret; - ret = cz_dpm_force_lowest(adev); - if (ret) - return ret; - - /* uvd */ - ret = cz_dpm_unforce_uvd_dpm_levels(adev); - if (ret) - return ret; - ret = cz_dpm_uvd_force_lowest(adev); - if (ret) - return ret; - - /* vce */ - ret = cz_dpm_unforce_vce_dpm_levels(adev); - if (ret) - return ret; - ret = cz_dpm_vce_force_lowest(adev); - if (ret) - return ret; - break; - case AMDGPU_DPM_FORCED_LEVEL_AUTO: - /* sclk */ - ret = cz_dpm_unforce_dpm_levels(adev); - if (ret) - return ret; - - /* uvd */ - ret = cz_dpm_unforce_uvd_dpm_levels(adev); - if (ret) - return ret; - - /* vce */ - ret = cz_dpm_unforce_vce_dpm_levels(adev); - if (ret) - return ret; - break; - default: - break; - } - - adev->pm.dpm.forced_level = level; - - return ret; -} - -/* fix me, display configuration change lists here - * mostly dal related*/ -static void cz_dpm_display_configuration_changed(struct amdgpu_device *adev) -{ -} - -static uint32_t cz_dpm_get_sclk(struct amdgpu_device *adev, bool low) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct cz_ps *requested_state = cz_get_ps(&pi->requested_rps); - - if (low) - return requested_state->levels[0].sclk; - else - return requested_state->levels[requested_state->num_levels - 1].sclk; - -} - -static uint32_t cz_dpm_get_mclk(struct amdgpu_device *adev, bool low) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - return pi->sys_info.bootup_uma_clk; -} - -static int cz_enable_uvd_dpm(struct amdgpu_device *adev, bool enable) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (enable && pi->caps_uvd_dpm ) { - pi->dpm_flags |= DPMFlags_UVD_Enabled; - DRM_DEBUG("UVD DPM Enabled.\n"); - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_EnableAllSmuFeatures, UVD_DPM_MASK); - } else { - pi->dpm_flags &= ~DPMFlags_UVD_Enabled; - DRM_DEBUG("UVD DPM Stopped\n"); - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_DisableAllSmuFeatures, UVD_DPM_MASK); - } - - return ret; -} - -static int cz_update_uvd_dpm(struct amdgpu_device *adev, bool gate) -{ - return cz_enable_uvd_dpm(adev, !gate); -} - - -static void cz_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret; - - if (pi->uvd_power_gated == gate) - return; - - pi->uvd_power_gated = gate; - - if (gate) { - if (pi->caps_uvd_pg) { - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_GATE); - if (ret) { - DRM_ERROR("UVD DPM Power Gating failed to set clockgating state\n"); - return; - } - - /* shutdown the UVD block */ - ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_PG_STATE_GATE); - - if (ret) { - DRM_ERROR("UVD DPM Power Gating failed to set powergating state\n"); - return; - } - } - cz_update_uvd_dpm(adev, gate); - if (pi->caps_uvd_pg) { - /* power off the UVD block */ - ret = cz_send_msg_to_smc(adev, PPSMC_MSG_UVDPowerOFF); - if (ret) { - DRM_ERROR("UVD DPM Power Gating failed to send SMU PowerOFF message\n"); - return; - } - } - } else { - if (pi->caps_uvd_pg) { - /* power on the UVD block */ - if (pi->uvd_dynamic_pg) - ret = cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 1); - else - ret = cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 0); - - if (ret) { - DRM_ERROR("UVD DPM Power Gating Failed to send SMU PowerON message\n"); - return; - } - - /* re-init the UVD block */ - ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_PG_STATE_UNGATE); - - if (ret) { - DRM_ERROR("UVD DPM Power Gating Failed to set powergating state\n"); - return; - } - - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_UNGATE); - if (ret) { - DRM_ERROR("UVD DPM Power Gating Failed to set clockgating state\n"); - return; - } - } - cz_update_uvd_dpm(adev, gate); - } -} - -static int cz_enable_vce_dpm(struct amdgpu_device *adev, bool enable) -{ - struct cz_power_info *pi = cz_get_pi(adev); - int ret = 0; - - if (enable && pi->caps_vce_dpm) { - pi->dpm_flags |= DPMFlags_VCE_Enabled; - DRM_DEBUG("VCE DPM Enabled.\n"); - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_EnableAllSmuFeatures, VCE_DPM_MASK); - - } else { - pi->dpm_flags &= ~DPMFlags_VCE_Enabled; - DRM_DEBUG("VCE DPM Stopped\n"); - - ret = cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_DisableAllSmuFeatures, VCE_DPM_MASK); - } - - return ret; -} - -static int cz_update_vce_dpm(struct amdgpu_device *adev) -{ - struct cz_power_info *pi = cz_get_pi(adev); - struct amdgpu_vce_clock_voltage_dependency_table *table = - &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table; - - /* Stable Pstate is enabled and we need to set the VCE DPM to highest level */ - if (pi->caps_stable_power_state) { - pi->vce_dpm.hard_min_clk = table->entries[table->count-1].ecclk; - } else { /* non-stable p-state cases. without vce.Arbiter.EcclkHardMin */ - /* leave it as set by user */ - /*pi->vce_dpm.hard_min_clk = table->entries[0].ecclk;*/ - } - - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetEclkHardMin, - cz_get_eclk_level(adev, - pi->vce_dpm.hard_min_clk, - PPSMC_MSG_SetEclkHardMin)); - return 0; -} - -static void cz_dpm_powergate_vce(struct amdgpu_device *adev, bool gate) -{ - struct cz_power_info *pi = cz_get_pi(adev); - - if (pi->caps_vce_pg) { - if (pi->vce_power_gated != gate) { - if (gate) { - /* disable clockgating so we can properly shut down the block */ - amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_UNGATE); - /* shutdown the VCE block */ - amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_PG_STATE_GATE); - - cz_enable_vce_dpm(adev, false); - cz_send_msg_to_smc(adev, PPSMC_MSG_VCEPowerOFF); - pi->vce_power_gated = true; - } else { - cz_send_msg_to_smc(adev, PPSMC_MSG_VCEPowerON); - pi->vce_power_gated = false; - - /* re-init the VCE block */ - amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_PG_STATE_UNGATE); - /* enable clockgating. hw will dynamically gate/ungate clocks on the fly */ - amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_GATE); - - cz_update_vce_dpm(adev); - cz_enable_vce_dpm(adev, true); - } - } else { - if (! pi->vce_power_gated) { - cz_update_vce_dpm(adev); - } - } - } else { /*pi->caps_vce_pg*/ - pi->vce_power_gated = gate; - cz_update_vce_dpm(adev); - cz_enable_vce_dpm(adev, !gate); - } -} - -static int cz_check_state_equal(struct amdgpu_device *adev, - struct amdgpu_ps *cps, - struct amdgpu_ps *rps, - bool *equal) -{ - if (equal == NULL) - return -EINVAL; - - *equal = false; - return 0; -} - -const struct amd_ip_funcs cz_dpm_ip_funcs = { - .name = "cz_dpm", - .early_init = cz_dpm_early_init, - .late_init = cz_dpm_late_init, - .sw_init = cz_dpm_sw_init, - .sw_fini = cz_dpm_sw_fini, - .hw_init = cz_dpm_hw_init, - .hw_fini = cz_dpm_hw_fini, - .suspend = cz_dpm_suspend, - .resume = cz_dpm_resume, - .is_idle = NULL, - .wait_for_idle = NULL, - .soft_reset = NULL, - .set_clockgating_state = cz_dpm_set_clockgating_state, - .set_powergating_state = cz_dpm_set_powergating_state, -}; - -static const struct amdgpu_dpm_funcs cz_dpm_funcs = { - .get_temperature = cz_dpm_get_temperature, - .pre_set_power_state = cz_dpm_pre_set_power_state, - .set_power_state = cz_dpm_set_power_state, - .post_set_power_state = cz_dpm_post_set_power_state, - .display_configuration_changed = cz_dpm_display_configuration_changed, - .get_sclk = cz_dpm_get_sclk, - .get_mclk = cz_dpm_get_mclk, - .print_power_state = cz_dpm_print_power_state, - .debugfs_print_current_performance_level = - cz_dpm_debugfs_print_current_performance_level, - .force_performance_level = cz_dpm_force_dpm_level, - .vblank_too_short = NULL, - .powergate_uvd = cz_dpm_powergate_uvd, - .powergate_vce = cz_dpm_powergate_vce, - .check_state_equal = cz_check_state_equal, -}; - -static void cz_dpm_set_funcs(struct amdgpu_device *adev) -{ - if (NULL == adev->pm.funcs) - adev->pm.funcs = &cz_dpm_funcs; -} - -const struct amdgpu_ip_block_version cz_dpm_ip_block = -{ - .type = AMD_IP_BLOCK_TYPE_SMC, - .major = 8, - .minor = 0, - .rev = 0, - .funcs = &cz_dpm_ip_funcs, -}; diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.h b/drivers/gpu/drm/amd/amdgpu/cz_dpm.h deleted file mode 100644 index 5df8c1faab51..000000000000 --- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __CZ_DPM_H__ -#define __CZ_DPM_H__ - -#include "smu8_fusion.h" - -#define CZ_AT_DFLT 30 -#define CZ_NUM_NBPSTATES 4 -#define CZ_NUM_NBPMEMORY_CLOCK 2 -#define CZ_MAX_HARDWARE_POWERLEVELS 8 -#define CZ_MAX_DISPLAY_CLOCK_LEVEL 8 -#define CZ_MAX_DISPLAYPHY_IDS 10 - -#define PPCZ_VOTINGRIGHTSCLIENTS_DFLT0 0x3FFFC102 - -#define SMC_RAM_END 0x40000 - -#define DPMFlags_SCLK_Enabled 0x00000001 -#define DPMFlags_UVD_Enabled 0x00000002 -#define DPMFlags_VCE_Enabled 0x00000004 -#define DPMFlags_ACP_Enabled 0x00000008 -#define DPMFlags_ForceHighestValid 0x40000000 -#define DPMFlags_Debug 0x80000000 - -/* Do not change the following, it is also defined in SMU8.h */ -#define SMU_EnabledFeatureScoreboard_AcpDpmOn 0x00000001 -#define SMU_EnabledFeatureScoreboard_SclkDpmOn 0x00200000 -#define SMU_EnabledFeatureScoreboard_UvdDpmOn 0x00800000 -#define SMU_EnabledFeatureScoreboard_VceDpmOn 0x01000000 - -/* temporary solution to SetMinDeepSleepSclk - * should indicate by display adaptor - * 10k Hz unit*/ -#define CZ_MIN_DEEP_SLEEP_SCLK 800 - -enum cz_pt_config_reg_type { - CZ_CONFIGREG_MMR = 0, - CZ_CONFIGREG_SMC_IND, - CZ_CONFIGREG_DIDT_IND, - CZ_CONFIGREG_CACHE, - CZ_CONFIGREG_MAX -}; - -struct cz_pt_config_reg { - uint32_t offset; - uint32_t mask; - uint32_t shift; - uint32_t value; - enum cz_pt_config_reg_type type; -}; - -struct cz_dpm_entry { - uint32_t soft_min_clk; - uint32_t hard_min_clk; - uint32_t soft_max_clk; - uint32_t hard_max_clk; -}; - -struct cz_pl { - uint32_t sclk; - uint8_t vddc_index; - uint8_t ds_divider_index; - uint8_t ss_divider_index; - uint8_t allow_gnb_slow; - uint8_t force_nbp_state; - uint8_t display_wm; - uint8_t vce_wm; -}; - -struct cz_ps { - struct cz_pl levels[CZ_MAX_HARDWARE_POWERLEVELS]; - uint32_t num_levels; - bool need_dfs_bypass; - uint8_t dpm0_pg_nb_ps_lo; - uint8_t dpm0_pg_nb_ps_hi; - uint8_t dpmx_nb_ps_lo; - uint8_t dpmx_nb_ps_hi; - bool force_high; -}; - -struct cz_displayphy_entry { - uint8_t phy_present; - uint8_t active_lane_mapping; - uint8_t display_conf_type; - uint8_t num_active_lanes; -}; - -struct cz_displayphy_info { - bool phy_access_initialized; - struct cz_displayphy_entry entries[CZ_MAX_DISPLAYPHY_IDS]; -}; - -struct cz_sys_info { - uint32_t bootup_uma_clk; - uint32_t bootup_sclk; - uint32_t dentist_vco_freq; - uint32_t nb_dpm_enable; - uint32_t nbp_memory_clock[CZ_NUM_NBPMEMORY_CLOCK]; - uint32_t nbp_n_clock[CZ_NUM_NBPSTATES]; - uint8_t nbp_voltage_index[CZ_NUM_NBPSTATES]; - uint32_t display_clock[CZ_MAX_DISPLAY_CLOCK_LEVEL]; - uint16_t bootup_nb_voltage_index; - uint8_t htc_tmp_lmt; - uint8_t htc_hyst_lmt; - uint32_t uma_channel_number; -}; - -struct cz_power_info { - uint32_t active_target[CZ_MAX_HARDWARE_POWERLEVELS]; - struct cz_sys_info sys_info; - struct cz_pl boot_pl; - bool disable_nb_ps3_in_battery; - bool battery_state; - uint32_t lowest_valid; - uint32_t highest_valid; - uint16_t high_voltage_threshold; - /* smc offsets */ - uint32_t sram_end; - uint32_t dpm_table_start; - uint32_t soft_regs_start; - /* dpm SMU tables */ - uint8_t uvd_level_count; - uint8_t vce_level_count; - uint8_t acp_level_count; - uint32_t fps_high_threshold; - uint32_t fps_low_threshold; - /* dpm table */ - uint32_t dpm_flags; - struct cz_dpm_entry sclk_dpm; - struct cz_dpm_entry uvd_dpm; - struct cz_dpm_entry vce_dpm; - struct cz_dpm_entry acp_dpm; - - uint8_t uvd_boot_level; - uint8_t uvd_interval; - uint8_t vce_boot_level; - uint8_t vce_interval; - uint8_t acp_boot_level; - uint8_t acp_interval; - - uint8_t graphics_boot_level; - uint8_t graphics_interval; - uint8_t graphics_therm_throttle_enable; - uint8_t graphics_voltage_change_enable; - uint8_t graphics_clk_slow_enable; - uint8_t graphics_clk_slow_divider; - - uint32_t low_sclk_interrupt_threshold; - bool uvd_power_gated; - bool vce_power_gated; - bool acp_power_gated; - - uint32_t active_process_mask; - - uint32_t mgcg_cgtt_local0; - uint32_t mgcg_cgtt_local1; - uint32_t clock_slow_down_step; - uint32_t skip_clock_slow_down; - bool enable_nb_ps_policy; - uint32_t voting_clients; - uint32_t voltage_drop_threshold; - uint32_t gfx_pg_threshold; - uint32_t max_sclk_level; - uint32_t max_uvd_level; - uint32_t max_vce_level; - /* flags */ - bool didt_enabled; - bool video_start; - bool cac_enabled; - bool bapm_enabled; - bool nb_dpm_enabled_by_driver; - bool nb_dpm_enabled; - bool auto_thermal_throttling_enabled; - bool dpm_enabled; - bool need_pptable_upload; - /* caps */ - bool caps_cac; - bool caps_power_containment; - bool caps_sq_ramping; - bool caps_db_ramping; - bool caps_td_ramping; - bool caps_tcp_ramping; - bool caps_sclk_throttle_low_notification; - bool caps_fps; - bool caps_uvd_dpm; - bool caps_uvd_pg; - bool caps_vce_dpm; - bool caps_vce_pg; - bool caps_acp_dpm; - bool caps_acp_pg; - bool caps_stable_power_state; - bool caps_enable_dfs_bypass; - bool caps_sclk_ds; - bool caps_voltage_island; - /* power state */ - struct amdgpu_ps current_rps; - struct cz_ps current_ps; - struct amdgpu_ps requested_rps; - struct cz_ps requested_ps; - - bool uvd_power_down; - bool vce_power_down; - bool acp_power_down; - - bool uvd_dynamic_pg; -}; - -/* cz_smc.c */ -uint32_t cz_get_argument(struct amdgpu_device *adev); -int cz_send_msg_to_smc(struct amdgpu_device *adev, uint16_t msg); -int cz_send_msg_to_smc_with_parameter(struct amdgpu_device *adev, - uint16_t msg, uint32_t parameter); -int cz_read_smc_sram_dword(struct amdgpu_device *adev, - uint32_t smc_address, uint32_t *value, uint32_t limit); -int cz_smu_upload_pptable(struct amdgpu_device *adev); -int cz_smu_download_pptable(struct amdgpu_device *adev, void **table); -#endif diff --git a/drivers/gpu/drm/amd/amdgpu/cz_smc.c b/drivers/gpu/drm/amd/amdgpu/cz_smc.c deleted file mode 100644 index aed7033c0973..000000000000 --- a/drivers/gpu/drm/amd/amdgpu/cz_smc.c +++ /dev/null @@ -1,995 +0,0 @@ -/* - * Copyright 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ -#include <linux/firmware.h> -#include "drmP.h" -#include "amdgpu.h" -#include "smu8.h" -#include "smu8_fusion.h" -#include "cz_ppsmc.h" -#include "cz_smumgr.h" -#include "smu_ucode_xfer_cz.h" -#include "amdgpu_ucode.h" -#include "cz_dpm.h" -#include "vi_dpm.h" - -#include "smu/smu_8_0_d.h" -#include "smu/smu_8_0_sh_mask.h" -#include "gca/gfx_8_0_d.h" -#include "gca/gfx_8_0_sh_mask.h" - -uint32_t cz_get_argument(struct amdgpu_device *adev) -{ - return RREG32(mmSMU_MP1_SRBM2P_ARG_0); -} - -static struct cz_smu_private_data *cz_smu_get_priv(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = - (struct cz_smu_private_data *)(adev->smu.priv); - - return priv; -} - -static int cz_send_msg_to_smc_async(struct amdgpu_device *adev, u16 msg) -{ - int i; - u32 content = 0, tmp; - - for (i = 0; i < adev->usec_timeout; i++) { - tmp = REG_GET_FIELD(RREG32(mmSMU_MP1_SRBM2P_RESP_0), - SMU_MP1_SRBM2P_RESP_0, CONTENT); - if (content != tmp) - break; - udelay(1); - } - - /* timeout means wrong logic*/ - if (i == adev->usec_timeout) - return -EINVAL; - - WREG32(mmSMU_MP1_SRBM2P_RESP_0, 0); - WREG32(mmSMU_MP1_SRBM2P_MSG_0, msg); - - return 0; -} - -int cz_send_msg_to_smc(struct amdgpu_device *adev, u16 msg) -{ - int i; - u32 content = 0, tmp = 0; - - if (cz_send_msg_to_smc_async(adev, msg)) - return -EINVAL; - - for (i = 0; i < adev->usec_timeout; i++) { - tmp = REG_GET_FIELD(RREG32(mmSMU_MP1_SRBM2P_RESP_0), - SMU_MP1_SRBM2P_RESP_0, CONTENT); - if (content != tmp) - break; - udelay(1); - } - - /* timeout means wrong logic*/ - if (i == adev->usec_timeout) - return -EINVAL; - - if (PPSMC_Result_OK != tmp) { - dev_err(adev->dev, "SMC Failed to send Message.\n"); - return -EINVAL; - } - - return 0; -} - -int cz_send_msg_to_smc_with_parameter(struct amdgpu_device *adev, - u16 msg, u32 parameter) -{ - WREG32(mmSMU_MP1_SRBM2P_ARG_0, parameter); - return cz_send_msg_to_smc(adev, msg); -} - -static int cz_set_smc_sram_address(struct amdgpu_device *adev, - u32 smc_address, u32 limit) -{ - if (smc_address & 3) - return -EINVAL; - if ((smc_address + 3) > limit) - return -EINVAL; - - WREG32(mmMP0PUB_IND_INDEX_0, SMN_MP1_SRAM_START_ADDR + smc_address); - - return 0; -} - -int cz_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address, - u32 *value, u32 limit) -{ - int ret; - - ret = cz_set_smc_sram_address(adev, smc_address, limit); - if (ret) - return ret; - - *value = RREG32(mmMP0PUB_IND_DATA_0); - - return 0; -} - -static int cz_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address, - u32 value, u32 limit) -{ - int ret; - - ret = cz_set_smc_sram_address(adev, smc_address, limit); - if (ret) - return ret; - - WREG32(mmMP0PUB_IND_DATA_0, value); - - return 0; -} - -static int cz_smu_request_load_fw(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - - uint32_t smc_addr = SMU8_FIRMWARE_HEADER_LOCATION + - offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus); - - cz_write_smc_sram_dword(adev, smc_addr, 0, smc_addr + 4); - - /*prepare toc buffers*/ - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_DriverDramAddrHi, - priv->toc_buffer.mc_addr_high); - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_DriverDramAddrLo, - priv->toc_buffer.mc_addr_low); - cz_send_msg_to_smc(adev, PPSMC_MSG_InitJobs); - - /*execute jobs*/ - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_ExecuteJob, - priv->toc_entry_aram); - - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_ExecuteJob, - priv->toc_entry_power_profiling_index); - - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_ExecuteJob, - priv->toc_entry_initialize_index); - - return 0; -} - -/* - *Check if the FW has been loaded, SMU will not return if loading - *has not finished. - */ -static int cz_smu_check_fw_load_finish(struct amdgpu_device *adev, - uint32_t fw_mask) -{ - int i; - uint32_t index = SMN_MP1_SRAM_START_ADDR + - SMU8_FIRMWARE_HEADER_LOCATION + - offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus); - - WREG32(mmMP0PUB_IND_INDEX, index); - - for (i = 0; i < adev->usec_timeout; i++) { - if (fw_mask == (RREG32(mmMP0PUB_IND_DATA) & fw_mask)) - break; - udelay(1); - } - - if (i >= adev->usec_timeout) { - dev_err(adev->dev, - "SMU check loaded firmware failed, expecting 0x%x, getting 0x%x", - fw_mask, RREG32(mmMP0PUB_IND_DATA)); - return -EINVAL; - } - - return 0; -} - -/* - * interfaces for different ip blocks to check firmware loading status - * 0 for success otherwise failed - */ -static int cz_smu_check_finished(struct amdgpu_device *adev, - enum AMDGPU_UCODE_ID id) -{ - switch (id) { - case AMDGPU_UCODE_ID_SDMA0: - if (adev->smu.fw_flags & AMDGPU_SDMA0_UCODE_LOADED) - return 0; - break; - case AMDGPU_UCODE_ID_SDMA1: - if (adev->smu.fw_flags & AMDGPU_SDMA1_UCODE_LOADED) - return 0; - break; - case AMDGPU_UCODE_ID_CP_CE: - if (adev->smu.fw_flags & AMDGPU_CPCE_UCODE_LOADED) - return 0; - break; - case AMDGPU_UCODE_ID_CP_PFP: - if (adev->smu.fw_flags & AMDGPU_CPPFP_UCODE_LOADED) - return 0; - case AMDGPU_UCODE_ID_CP_ME: - if (adev->smu.fw_flags & AMDGPU_CPME_UCODE_LOADED) - return 0; - break; - case AMDGPU_UCODE_ID_CP_MEC1: - if (adev->smu.fw_flags & AMDGPU_CPMEC1_UCODE_LOADED) - return 0; - break; - case AMDGPU_UCODE_ID_CP_MEC2: - if (adev->smu.fw_flags & AMDGPU_CPMEC2_UCODE_LOADED) - return 0; - break; - case AMDGPU_UCODE_ID_RLC_G: - if (adev->smu.fw_flags & AMDGPU_CPRLC_UCODE_LOADED) - return 0; - break; - case AMDGPU_UCODE_ID_MAXIMUM: - default: - break; - } - - return 1; -} - -static int cz_load_mec_firmware(struct amdgpu_device *adev) -{ - struct amdgpu_firmware_info *ucode = - &adev->firmware.ucode[AMDGPU_UCODE_ID_CP_MEC1]; - uint32_t reg_data; - uint32_t tmp; - - if (ucode->fw == NULL) - return -EINVAL; - - /* Disable MEC parsing/prefetching */ - tmp = RREG32(mmCP_MEC_CNTL); - tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME1_HALT, 1); - tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME2_HALT, 1); - WREG32(mmCP_MEC_CNTL, tmp); - - tmp = RREG32(mmCP_CPC_IC_BASE_CNTL); - tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, VMID, 0); - tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, ATC, 0); - tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, CACHE_POLICY, 0); - tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, MTYPE, 1); - WREG32(mmCP_CPC_IC_BASE_CNTL, tmp); - - reg_data = lower_32_bits(ucode->mc_addr) & - REG_FIELD_MASK(CP_CPC_IC_BASE_LO, IC_BASE_LO); - WREG32(mmCP_CPC_IC_BASE_LO, reg_data); - - reg_data = upper_32_bits(ucode->mc_addr) & - REG_FIELD_MASK(CP_CPC_IC_BASE_HI, IC_BASE_HI); - WREG32(mmCP_CPC_IC_BASE_HI, reg_data); - - return 0; -} - -int cz_smu_start(struct amdgpu_device *adev) -{ - int ret = 0; - - uint32_t fw_to_check = UCODE_ID_RLC_G_MASK | - UCODE_ID_SDMA0_MASK | - UCODE_ID_SDMA1_MASK | - UCODE_ID_CP_CE_MASK | - UCODE_ID_CP_ME_MASK | - UCODE_ID_CP_PFP_MASK | - UCODE_ID_CP_MEC_JT1_MASK | - UCODE_ID_CP_MEC_JT2_MASK; - - if (adev->asic_type == CHIP_STONEY) - fw_to_check &= ~(UCODE_ID_SDMA1_MASK | UCODE_ID_CP_MEC_JT2_MASK); - - cz_smu_request_load_fw(adev); - ret = cz_smu_check_fw_load_finish(adev, fw_to_check); - if (ret) - return ret; - - /* manually load MEC firmware for CZ */ - if (adev->asic_type == CHIP_CARRIZO || adev->asic_type == CHIP_STONEY) { - ret = cz_load_mec_firmware(adev); - if (ret) { - dev_err(adev->dev, "(%d) Mec Firmware load failed\n", ret); - return ret; - } - } - - /* setup fw load flag */ - adev->smu.fw_flags = AMDGPU_SDMA0_UCODE_LOADED | - AMDGPU_SDMA1_UCODE_LOADED | - AMDGPU_CPCE_UCODE_LOADED | - AMDGPU_CPPFP_UCODE_LOADED | - AMDGPU_CPME_UCODE_LOADED | - AMDGPU_CPMEC1_UCODE_LOADED | - AMDGPU_CPMEC2_UCODE_LOADED | - AMDGPU_CPRLC_UCODE_LOADED; - - if (adev->asic_type == CHIP_STONEY) - adev->smu.fw_flags &= ~(AMDGPU_SDMA1_UCODE_LOADED | AMDGPU_CPMEC2_UCODE_LOADED); - - return ret; -} - -static uint32_t cz_convert_fw_type(uint32_t fw_type) -{ - enum AMDGPU_UCODE_ID result = AMDGPU_UCODE_ID_MAXIMUM; - - switch (fw_type) { - case UCODE_ID_SDMA0: - result = AMDGPU_UCODE_ID_SDMA0; - break; - case UCODE_ID_SDMA1: - result = AMDGPU_UCODE_ID_SDMA1; - break; - case UCODE_ID_CP_CE: - result = AMDGPU_UCODE_ID_CP_CE; - break; - case UCODE_ID_CP_PFP: - result = AMDGPU_UCODE_ID_CP_PFP; - break; - case UCODE_ID_CP_ME: - result = AMDGPU_UCODE_ID_CP_ME; - break; - case UCODE_ID_CP_MEC_JT1: - case UCODE_ID_CP_MEC_JT2: - result = AMDGPU_UCODE_ID_CP_MEC1; - break; - case UCODE_ID_RLC_G: - result = AMDGPU_UCODE_ID_RLC_G; - break; - default: - DRM_ERROR("UCode type is out of range!"); - } - - return result; -} - -static uint8_t cz_smu_translate_firmware_enum_to_arg( - enum cz_scratch_entry firmware_enum) -{ - uint8_t ret = 0; - - switch (firmware_enum) { - case CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0: - ret = UCODE_ID_SDMA0; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1: - ret = UCODE_ID_SDMA1; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE: - ret = UCODE_ID_CP_CE; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP: - ret = UCODE_ID_CP_PFP; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME: - ret = UCODE_ID_CP_ME; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1: - ret = UCODE_ID_CP_MEC_JT1; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2: - ret = UCODE_ID_CP_MEC_JT2; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_GMCON_RENG: - ret = UCODE_ID_GMCON_RENG; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G: - ret = UCODE_ID_RLC_G; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH: - ret = UCODE_ID_RLC_SCRATCH; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM: - ret = UCODE_ID_RLC_SRM_ARAM; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM: - ret = UCODE_ID_RLC_SRM_DRAM; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_ERAM: - ret = UCODE_ID_DMCU_ERAM; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_IRAM: - ret = UCODE_ID_DMCU_IRAM; - break; - case CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING: - ret = TASK_ARG_INIT_MM_PWR_LOG; - break; - case CZ_SCRATCH_ENTRY_DATA_ID_SDMA_HALT: - case CZ_SCRATCH_ENTRY_DATA_ID_SYS_CLOCKGATING: - case CZ_SCRATCH_ENTRY_DATA_ID_SDMA_RING_REGS: - case CZ_SCRATCH_ENTRY_DATA_ID_NONGFX_REINIT: - case CZ_SCRATCH_ENTRY_DATA_ID_SDMA_START: - case CZ_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS: - ret = TASK_ARG_REG_MMIO; - break; - case CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE: - ret = TASK_ARG_INIT_CLK_TABLE; - break; - } - - return ret; -} - -static int cz_smu_populate_single_firmware_entry(struct amdgpu_device *adev, - enum cz_scratch_entry firmware_enum, - struct cz_buffer_entry *entry) -{ - uint64_t gpu_addr; - uint32_t data_size; - uint8_t ucode_id = cz_smu_translate_firmware_enum_to_arg(firmware_enum); - enum AMDGPU_UCODE_ID id = cz_convert_fw_type(ucode_id); - struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id]; - const struct gfx_firmware_header_v1_0 *header; - - if (ucode->fw == NULL) - return -EINVAL; - - gpu_addr = ucode->mc_addr; - header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data; - data_size = le32_to_cpu(header->header.ucode_size_bytes); - - if ((firmware_enum == CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1) || - (firmware_enum == CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2)) { - gpu_addr += le32_to_cpu(header->jt_offset) << 2; - data_size = le32_to_cpu(header->jt_size) << 2; - } - - entry->mc_addr_low = lower_32_bits(gpu_addr); - entry->mc_addr_high = upper_32_bits(gpu_addr); - entry->data_size = data_size; - entry->firmware_ID = firmware_enum; - - return 0; -} - -static int cz_smu_populate_single_scratch_entry(struct amdgpu_device *adev, - enum cz_scratch_entry scratch_type, - uint32_t size_in_byte, - struct cz_buffer_entry *entry) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - uint64_t mc_addr = (((uint64_t) priv->smu_buffer.mc_addr_high) << 32) | - priv->smu_buffer.mc_addr_low; - mc_addr += size_in_byte; - - priv->smu_buffer_used_bytes += size_in_byte; - entry->data_size = size_in_byte; - entry->kaddr = priv->smu_buffer.kaddr + priv->smu_buffer_used_bytes; - entry->mc_addr_low = lower_32_bits(mc_addr); - entry->mc_addr_high = upper_32_bits(mc_addr); - entry->firmware_ID = scratch_type; - - return 0; -} - -static int cz_smu_populate_single_ucode_load_task(struct amdgpu_device *adev, - enum cz_scratch_entry firmware_enum, - bool is_last) -{ - uint8_t i; - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr; - struct SMU_Task *task = &toc->tasks[priv->toc_entry_used_count++]; - - task->type = TASK_TYPE_UCODE_LOAD; - task->arg = cz_smu_translate_firmware_enum_to_arg(firmware_enum); - task->next = is_last ? END_OF_TASK_LIST : priv->toc_entry_used_count; - - for (i = 0; i < priv->driver_buffer_length; i++) - if (priv->driver_buffer[i].firmware_ID == firmware_enum) - break; - - if (i >= priv->driver_buffer_length) { - dev_err(adev->dev, "Invalid Firmware Type\n"); - return -EINVAL; - } - - task->addr.low = priv->driver_buffer[i].mc_addr_low; - task->addr.high = priv->driver_buffer[i].mc_addr_high; - task->size_bytes = priv->driver_buffer[i].data_size; - - return 0; -} - -static int cz_smu_populate_single_scratch_task(struct amdgpu_device *adev, - enum cz_scratch_entry firmware_enum, - uint8_t type, bool is_last) -{ - uint8_t i; - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr; - struct SMU_Task *task = &toc->tasks[priv->toc_entry_used_count++]; - - task->type = type; - task->arg = cz_smu_translate_firmware_enum_to_arg(firmware_enum); - task->next = is_last ? END_OF_TASK_LIST : priv->toc_entry_used_count; - - for (i = 0; i < priv->scratch_buffer_length; i++) - if (priv->scratch_buffer[i].firmware_ID == firmware_enum) - break; - - if (i >= priv->scratch_buffer_length) { - dev_err(adev->dev, "Invalid Firmware Type\n"); - return -EINVAL; - } - - task->addr.low = priv->scratch_buffer[i].mc_addr_low; - task->addr.high = priv->scratch_buffer[i].mc_addr_high; - task->size_bytes = priv->scratch_buffer[i].data_size; - - if (CZ_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS == firmware_enum) { - struct cz_ih_meta_data *pIHReg_restore = - (struct cz_ih_meta_data *)priv->scratch_buffer[i].kaddr; - pIHReg_restore->command = - METADATA_CMD_MODE0 | METADATA_PERFORM_ON_LOAD; - } - - return 0; -} - -static int cz_smu_construct_toc_for_rlc_aram_save(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - priv->toc_entry_aram = priv->toc_entry_used_count; - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM, - TASK_TYPE_UCODE_SAVE, true); - - return 0; -} - -static int cz_smu_construct_toc_for_vddgfx_enter(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr; - - toc->JobList[JOB_GFX_SAVE] = (uint8_t)priv->toc_entry_used_count; - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH, - TASK_TYPE_UCODE_SAVE, false); - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM, - TASK_TYPE_UCODE_SAVE, true); - - return 0; -} - -static int cz_smu_construct_toc_for_vddgfx_exit(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr; - - toc->JobList[JOB_GFX_RESTORE] = (uint8_t)priv->toc_entry_used_count; - - /* populate ucode */ - if (adev->firmware.smu_load) { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE, false); - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP, false); - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME, false); - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false); - if (adev->asic_type == CHIP_STONEY) { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false); - } else { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, false); - } - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G, false); - } - - /* populate scratch */ - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH, - TASK_TYPE_UCODE_LOAD, false); - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM, - TASK_TYPE_UCODE_LOAD, false); - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM, - TASK_TYPE_UCODE_LOAD, true); - - return 0; -} - -static int cz_smu_construct_toc_for_power_profiling(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - - priv->toc_entry_power_profiling_index = priv->toc_entry_used_count; - - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING, - TASK_TYPE_INITIALIZE, true); - return 0; -} - -static int cz_smu_construct_toc_for_bootup(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - - priv->toc_entry_initialize_index = priv->toc_entry_used_count; - - if (adev->firmware.smu_load) { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, false); - if (adev->asic_type == CHIP_STONEY) { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, false); - } else { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1, false); - } - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE, false); - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP, false); - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME, false); - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false); - if (adev->asic_type == CHIP_STONEY) { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false); - } else { - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, false); - } - cz_smu_populate_single_ucode_load_task(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G, true); - } - - return 0; -} - -static int cz_smu_construct_toc_for_clock_table(struct amdgpu_device *adev) -{ - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - - priv->toc_entry_clock_table = priv->toc_entry_used_count; - - cz_smu_populate_single_scratch_task(adev, - CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE, - TASK_TYPE_INITIALIZE, true); - - return 0; -} - -static int cz_smu_initialize_toc_empty_job_list(struct amdgpu_device *adev) -{ - int i; - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr; - - for (i = 0; i < NUM_JOBLIST_ENTRIES; i++) - toc->JobList[i] = (uint8_t)IGNORE_JOB; - - return 0; -} - -/* - * cz smu uninitialization - */ -int cz_smu_fini(struct amdgpu_device *adev) -{ - amdgpu_bo_unref(&adev->smu.toc_buf); - amdgpu_bo_unref(&adev->smu.smu_buf); - kfree(adev->smu.priv); - adev->smu.priv = NULL; - if (adev->firmware.smu_load) - amdgpu_ucode_fini_bo(adev); - - return 0; -} - -int cz_smu_download_pptable(struct amdgpu_device *adev, void **table) -{ - uint8_t i; - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - - for (i = 0; i < priv->scratch_buffer_length; i++) - if (priv->scratch_buffer[i].firmware_ID == - CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE) - break; - - if (i >= priv->scratch_buffer_length) { - dev_err(adev->dev, "Invalid Scratch Type\n"); - return -EINVAL; - } - - *table = (struct SMU8_Fusion_ClkTable *)priv->scratch_buffer[i].kaddr; - - /* prepare buffer for pptable */ - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetClkTableAddrHi, - priv->scratch_buffer[i].mc_addr_high); - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetClkTableAddrLo, - priv->scratch_buffer[i].mc_addr_low); - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_ExecuteJob, - priv->toc_entry_clock_table); - - /* actual downloading */ - cz_send_msg_to_smc(adev, PPSMC_MSG_ClkTableXferToDram); - - return 0; -} - -int cz_smu_upload_pptable(struct amdgpu_device *adev) -{ - uint8_t i; - struct cz_smu_private_data *priv = cz_smu_get_priv(adev); - - for (i = 0; i < priv->scratch_buffer_length; i++) - if (priv->scratch_buffer[i].firmware_ID == - CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE) - break; - - if (i >= priv->scratch_buffer_length) { - dev_err(adev->dev, "Invalid Scratch Type\n"); - return -EINVAL; - } - - /* prepare SMU */ - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetClkTableAddrHi, - priv->scratch_buffer[i].mc_addr_high); - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_SetClkTableAddrLo, - priv->scratch_buffer[i].mc_addr_low); - cz_send_msg_to_smc_with_parameter(adev, - PPSMC_MSG_ExecuteJob, - priv->toc_entry_clock_table); - - /* actual uploading */ - cz_send_msg_to_smc(adev, PPSMC_MSG_ClkTableXferToSmu); - - return 0; -} - -/* - * cz smumgr functions initialization - */ -static const struct amdgpu_smumgr_funcs cz_smumgr_funcs = { - .check_fw_load_finish = cz_smu_check_finished, - .request_smu_load_fw = NULL, - .request_smu_specific_fw = NULL, -}; - -/* - * cz smu initialization - */ -int cz_smu_init(struct amdgpu_device *adev) -{ - int ret = -EINVAL; - uint64_t mc_addr = 0; - struct amdgpu_bo **toc_buf = &adev->smu.toc_buf; - struct amdgpu_bo **smu_buf = &adev->smu.smu_buf; - void *toc_buf_ptr = NULL; - void *smu_buf_ptr = NULL; - - struct cz_smu_private_data *priv = - kzalloc(sizeof(struct cz_smu_private_data), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; - - /* allocate firmware buffers */ - if (adev->firmware.smu_load) - amdgpu_ucode_init_bo(adev); - - adev->smu.priv = priv; - adev->smu.fw_flags = 0; - priv->toc_buffer.data_size = 4096; - - priv->smu_buffer.data_size = - ALIGN(UCODE_ID_RLC_SCRATCH_SIZE_BYTE, 32) + - ALIGN(UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE, 32) + - ALIGN(UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE, 32) + - ALIGN(sizeof(struct SMU8_MultimediaPowerLogData), 32) + - ALIGN(sizeof(struct SMU8_Fusion_ClkTable), 32); - - /* prepare toc buffer and smu buffer: - * 1. create amdgpu_bo for toc buffer and smu buffer - * 2. pin mc address - * 3. map kernel virtual address - */ - ret = amdgpu_bo_create(adev, priv->toc_buffer.data_size, PAGE_SIZE, - true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, - toc_buf); - - if (ret) { - dev_err(adev->dev, "(%d) SMC TOC buffer allocation failed\n", ret); - return ret; - } - - ret = amdgpu_bo_create(adev, priv->smu_buffer.data_size, PAGE_SIZE, - true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, - smu_buf); - - if (ret) { - dev_err(adev->dev, "(%d) SMC Internal buffer allocation failed\n", ret); - return ret; - } - - /* toc buffer reserve/pin/map */ - ret = amdgpu_bo_reserve(adev->smu.toc_buf, false); - if (ret) { - amdgpu_bo_unref(&adev->smu.toc_buf); - dev_err(adev->dev, "(%d) SMC TOC buffer reserve failed\n", ret); - return ret; - } - - ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_GTT, &mc_addr); - if (ret) { - amdgpu_bo_unreserve(adev->smu.toc_buf); - amdgpu_bo_unref(&adev->smu.toc_buf); - dev_err(adev->dev, "(%d) SMC TOC buffer pin failed\n", ret); - return ret; - } - - ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr); - if (ret) - goto smu_init_failed; - - amdgpu_bo_unreserve(adev->smu.toc_buf); - - priv->toc_buffer.mc_addr_low = lower_32_bits(mc_addr); - priv->toc_buffer.mc_addr_high = upper_32_bits(mc_addr); - priv->toc_buffer.kaddr = toc_buf_ptr; - - /* smu buffer reserve/pin/map */ - ret = amdgpu_bo_reserve(adev->smu.smu_buf, false); - if (ret) { - amdgpu_bo_unref(&adev->smu.smu_buf); - dev_err(adev->dev, "(%d) SMC Internal buffer reserve failed\n", ret); - return ret; - } - - ret = amdgpu_bo_pin(adev->smu.smu_buf, AMDGPU_GEM_DOMAIN_GTT, &mc_addr); - if (ret) { - amdgpu_bo_unreserve(adev->smu.smu_buf); - amdgpu_bo_unref(&adev->smu.smu_buf); - dev_err(adev->dev, "(%d) SMC Internal buffer pin failed\n", ret); - return ret; - } - - ret = amdgpu_bo_kmap(*smu_buf, &smu_buf_ptr); - if (ret) - goto smu_init_failed; - - amdgpu_bo_unreserve(adev->smu.smu_buf); - - priv->smu_buffer.mc_addr_low = lower_32_bits(mc_addr); - priv->smu_buffer.mc_addr_high = upper_32_bits(mc_addr); - priv->smu_buffer.kaddr = smu_buf_ptr; - - if (adev->firmware.smu_load) { - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - - if (adev->asic_type == CHIP_STONEY) { - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - } else { - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - } - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - if (adev->asic_type == CHIP_STONEY) { - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - } else { - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - } - if (cz_smu_populate_single_firmware_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G, - &priv->driver_buffer[priv->driver_buffer_length++])) - goto smu_init_failed; - } - - if (cz_smu_populate_single_scratch_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH, - UCODE_ID_RLC_SCRATCH_SIZE_BYTE, - &priv->scratch_buffer[priv->scratch_buffer_length++])) - goto smu_init_failed; - if (cz_smu_populate_single_scratch_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM, - UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE, - &priv->scratch_buffer[priv->scratch_buffer_length++])) - goto smu_init_failed; - if (cz_smu_populate_single_scratch_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM, - UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE, - &priv->scratch_buffer[priv->scratch_buffer_length++])) - goto smu_init_failed; - if (cz_smu_populate_single_scratch_entry(adev, - CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING, - sizeof(struct SMU8_MultimediaPowerLogData), - &priv->scratch_buffer[priv->scratch_buffer_length++])) - goto smu_init_failed; - if (cz_smu_populate_single_scratch_entry(adev, - CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE, - sizeof(struct SMU8_Fusion_ClkTable), - &priv->scratch_buffer[priv->scratch_buffer_length++])) - goto smu_init_failed; - - cz_smu_initialize_toc_empty_job_list(adev); - cz_smu_construct_toc_for_rlc_aram_save(adev); - cz_smu_construct_toc_for_vddgfx_enter(adev); - cz_smu_construct_toc_for_vddgfx_exit(adev); - cz_smu_construct_toc_for_power_profiling(adev); - cz_smu_construct_toc_for_bootup(adev); - cz_smu_construct_toc_for_clock_table(adev); - /* init the smumgr functions */ - adev->smu.smumgr_funcs = &cz_smumgr_funcs; - - return 0; - -smu_init_failed: - amdgpu_bo_unref(toc_buf); - amdgpu_bo_unref(smu_buf); - - return ret; -} diff --git a/drivers/gpu/drm/amd/amdgpu/cz_smumgr.h b/drivers/gpu/drm/amd/amdgpu/cz_smumgr.h deleted file mode 100644 index 026342fcf0f3..000000000000 --- a/drivers/gpu/drm/amd/amdgpu/cz_smumgr.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ -#ifndef __CZ_SMC_H__ -#define __CZ_SMC_H__ - -#define MAX_NUM_FIRMWARE 8 -#define MAX_NUM_SCRATCH 11 -#define CZ_SCRATCH_SIZE_NONGFX_CLOCKGATING 1024 -#define CZ_SCRATCH_SIZE_NONGFX_GOLDENSETTING 2048 -#define CZ_SCRATCH_SIZE_SDMA_METADATA 1024 -#define CZ_SCRATCH_SIZE_IH ((2*256+1)*4) - -enum cz_scratch_entry { - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0 = 0, - CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, - CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, - CZ_SCRATCH_ENTRY_UCODE_ID_GMCON_RENG, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM, - CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM, - CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_ERAM, - CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_IRAM, - CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING, - CZ_SCRATCH_ENTRY_DATA_ID_SDMA_HALT, - CZ_SCRATCH_ENTRY_DATA_ID_SYS_CLOCKGATING, - CZ_SCRATCH_ENTRY_DATA_ID_SDMA_RING_REGS, - CZ_SCRATCH_ENTRY_DATA_ID_NONGFX_REINIT, - CZ_SCRATCH_ENTRY_DATA_ID_SDMA_START, - CZ_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS, - CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE -}; - -struct cz_buffer_entry { - uint32_t data_size; - uint32_t mc_addr_low; - uint32_t mc_addr_high; - void *kaddr; - enum cz_scratch_entry firmware_ID; -}; - -struct cz_register_index_data_pair { - uint32_t offset; - uint32_t value; -}; - -struct cz_ih_meta_data { - uint32_t command; - struct cz_register_index_data_pair register_index_value_pair[1]; -}; - -struct cz_smu_private_data { - uint8_t driver_buffer_length; - uint8_t scratch_buffer_length; - uint16_t toc_entry_used_count; - uint16_t toc_entry_initialize_index; - uint16_t toc_entry_power_profiling_index; - uint16_t toc_entry_aram; - uint16_t toc_entry_ih_register_restore_task_index; - uint16_t toc_entry_clock_table; - uint16_t ih_register_restore_task_size; - uint16_t smu_buffer_used_bytes; - - struct cz_buffer_entry toc_buffer; - struct cz_buffer_entry smu_buffer; - struct cz_buffer_entry driver_buffer[MAX_NUM_FIRMWARE]; - struct cz_buffer_entry scratch_buffer[MAX_NUM_SCRATCH]; -}; - -#endif diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index ccb5e02e7b20..d4452d8f76ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2072,7 +2072,7 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); - switch (target_fb->pixel_format) { + switch (target_fb->format->format) { case DRM_FORMAT_C8: fb_format = REG_SET_FIELD(0, GRPH_CONTROL, GRPH_DEPTH, 0); fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_FORMAT, 0); @@ -2145,7 +2145,7 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, break; default: DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format, &format_name)); + drm_get_format_name(target_fb->format->format, &format_name)); return -EINVAL; } @@ -2220,7 +2220,7 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, WREG32(mmGRPH_X_END + amdgpu_crtc->crtc_offset, target_fb->width); WREG32(mmGRPH_Y_END + amdgpu_crtc->crtc_offset, target_fb->height); - fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + fb_pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; WREG32(mmGRPH_PITCH + amdgpu_crtc->crtc_offset, fb_pitch_pixels); dce_v10_0_grph_enable(crtc, true); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index a7af5b33a5e3..5b24e89552ec 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2056,7 +2056,7 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); - switch (target_fb->pixel_format) { + switch (target_fb->format->format) { case DRM_FORMAT_C8: fb_format = REG_SET_FIELD(0, GRPH_CONTROL, GRPH_DEPTH, 0); fb_format = REG_SET_FIELD(fb_format, GRPH_CONTROL, GRPH_FORMAT, 0); @@ -2129,7 +2129,7 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, break; default: DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format, &format_name)); + drm_get_format_name(target_fb->format->format, &format_name)); return -EINVAL; } @@ -2204,7 +2204,7 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc, WREG32(mmGRPH_X_END + amdgpu_crtc->crtc_offset, target_fb->width); WREG32(mmGRPH_Y_END + amdgpu_crtc->crtc_offset, target_fb->height); - fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + fb_pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; WREG32(mmGRPH_PITCH + amdgpu_crtc->crtc_offset, fb_pitch_pixels); dce_v11_0_grph_enable(crtc, true); @@ -3737,9 +3737,15 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev, default: encoder->possible_crtcs = 0x3; break; + case 3: + encoder->possible_crtcs = 0x7; + break; case 4: encoder->possible_crtcs = 0xf; break; + case 5: + encoder->possible_crtcs = 0x1f; + break; case 6: encoder->possible_crtcs = 0x3f; break; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index 39df6a50637f..809aa94a0cc1 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -1501,7 +1501,7 @@ static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc, amdgpu_bo_get_tiling_flags(abo, &tiling_flags); amdgpu_bo_unreserve(abo); - switch (target_fb->pixel_format) { + switch (target_fb->format->format) { case DRM_FORMAT_C8: fb_format = (GRPH_DEPTH(GRPH_DEPTH_8BPP) | GRPH_FORMAT(GRPH_FORMAT_INDEXED)); @@ -1567,7 +1567,7 @@ static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc, break; default: DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format, &format_name)); + drm_get_format_name(target_fb->format->format, &format_name)); return -EINVAL; } @@ -1630,7 +1630,7 @@ static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc, WREG32(mmGRPH_X_END + amdgpu_crtc->crtc_offset, target_fb->width); WREG32(mmGRPH_Y_END + amdgpu_crtc->crtc_offset, target_fb->height); - fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + fb_pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; WREG32(mmGRPH_PITCH + amdgpu_crtc->crtc_offset, fb_pitch_pixels); dce_v6_0_grph_enable(crtc, true); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index 28102bb1704d..d2590d75aa11 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -1950,7 +1950,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); - switch (target_fb->pixel_format) { + switch (target_fb->format->format) { case DRM_FORMAT_C8: fb_format = ((GRPH_DEPTH_8BPP << GRPH_CONTROL__GRPH_DEPTH__SHIFT) | (GRPH_FORMAT_INDEXED << GRPH_CONTROL__GRPH_FORMAT__SHIFT)); @@ -2016,7 +2016,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, break; default: DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format, &format_name)); + drm_get_format_name(target_fb->format->format, &format_name)); return -EINVAL; } @@ -2079,7 +2079,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, WREG32(mmGRPH_X_END + amdgpu_crtc->crtc_offset, target_fb->width); WREG32(mmGRPH_Y_END + amdgpu_crtc->crtc_offset, target_fb->height); - fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + fb_pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; WREG32(mmGRPH_PITCH + amdgpu_crtc->crtc_offset, fb_pitch_pixels); dce_v8_0_grph_enable(crtc, true); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index b323f5ef64d2..2086e7e68de4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -25,7 +25,7 @@ #include "amdgpu_ih.h" #include "amdgpu_gfx.h" #include "amdgpu_ucode.h" -#include "si/clearstate_si.h" +#include "clearstate_si.h" #include "bif/bif_3_0_d.h" #include "bif/bif_3_0_sh_mask.h" #include "oss/oss_1_0_d.h" @@ -1325,21 +1325,19 @@ static u32 gfx_v6_0_create_bitmask(u32 bit_width) return (u32)(((u64)1 << bit_width) - 1); } -static u32 gfx_v6_0_get_rb_disabled(struct amdgpu_device *adev, - u32 max_rb_num_per_se, - u32 sh_per_se) +static u32 gfx_v6_0_get_rb_active_bitmap(struct amdgpu_device *adev) { u32 data, mask; - data = RREG32(mmCC_RB_BACKEND_DISABLE); - data &= CC_RB_BACKEND_DISABLE__BACKEND_DISABLE_MASK; - data |= RREG32(mmGC_USER_RB_BACKEND_DISABLE); + data = RREG32(mmCC_RB_BACKEND_DISABLE) | + RREG32(mmGC_USER_RB_BACKEND_DISABLE); - data >>= CC_RB_BACKEND_DISABLE__BACKEND_DISABLE__SHIFT; + data = REG_GET_FIELD(data, GC_USER_RB_BACKEND_DISABLE, BACKEND_DISABLE); - mask = gfx_v6_0_create_bitmask(max_rb_num_per_se / sh_per_se); + mask = gfx_v6_0_create_bitmask(adev->gfx.config.max_backends_per_se/ + adev->gfx.config.max_sh_per_se); - return data & mask; + return ~data & mask; } static void gfx_v6_0_raster_config(struct amdgpu_device *adev, u32 *rconf) @@ -1468,68 +1466,55 @@ static void gfx_v6_0_write_harvested_raster_configs(struct amdgpu_device *adev, gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); } -static void gfx_v6_0_setup_rb(struct amdgpu_device *adev, - u32 se_num, u32 sh_per_se, - u32 max_rb_num_per_se) +static void gfx_v6_0_setup_rb(struct amdgpu_device *adev) { int i, j; - u32 data, mask; - u32 disabled_rbs = 0; - u32 enabled_rbs = 0; + u32 data; + u32 raster_config = 0; + u32 active_rbs = 0; + u32 rb_bitmap_width_per_sh = adev->gfx.config.max_backends_per_se / + adev->gfx.config.max_sh_per_se; unsigned num_rb_pipes; mutex_lock(&adev->grbm_idx_mutex); - for (i = 0; i < se_num; i++) { - for (j = 0; j < sh_per_se; j++) { + for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { + for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { gfx_v6_0_select_se_sh(adev, i, j, 0xffffffff); - data = gfx_v6_0_get_rb_disabled(adev, max_rb_num_per_se, sh_per_se); - disabled_rbs |= data << ((i * sh_per_se + j) * 2); + data = gfx_v6_0_get_rb_active_bitmap(adev); + active_rbs |= data << ((i * adev->gfx.config.max_sh_per_se + j) * + rb_bitmap_width_per_sh); } } gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); - mutex_unlock(&adev->grbm_idx_mutex); - - mask = 1; - for (i = 0; i < max_rb_num_per_se * se_num; i++) { - if (!(disabled_rbs & mask)) - enabled_rbs |= mask; - mask <<= 1; - } - adev->gfx.config.backend_enable_mask = enabled_rbs; - adev->gfx.config.num_rbs = hweight32(enabled_rbs); + adev->gfx.config.backend_enable_mask = active_rbs; + adev->gfx.config.num_rbs = hweight32(active_rbs); num_rb_pipes = min_t(unsigned, adev->gfx.config.max_backends_per_se * adev->gfx.config.max_shader_engines, 16); - mutex_lock(&adev->grbm_idx_mutex); - for (i = 0; i < se_num; i++) { - gfx_v6_0_select_se_sh(adev, i, 0xffffffff, 0xffffffff); - data = 0; - for (j = 0; j < sh_per_se; j++) { - switch (enabled_rbs & 3) { - case 1: - data |= (RASTER_CONFIG_RB_MAP_0 << (i * sh_per_se + j) * 2); - break; - case 2: - data |= (RASTER_CONFIG_RB_MAP_3 << (i * sh_per_se + j) * 2); - break; - case 3: - default: - data |= (RASTER_CONFIG_RB_MAP_2 << (i * sh_per_se + j) * 2); - break; - } - enabled_rbs >>= 2; - } - gfx_v6_0_raster_config(adev, &data); + gfx_v6_0_raster_config(adev, &raster_config); - if (!adev->gfx.config.backend_enable_mask || - adev->gfx.config.num_rbs >= num_rb_pipes) - WREG32(mmPA_SC_RASTER_CONFIG, data); - else - gfx_v6_0_write_harvested_raster_configs(adev, data, - adev->gfx.config.backend_enable_mask, - num_rb_pipes); + if (!adev->gfx.config.backend_enable_mask || + adev->gfx.config.num_rbs >= num_rb_pipes) { + WREG32(mmPA_SC_RASTER_CONFIG, raster_config); + } else { + gfx_v6_0_write_harvested_raster_configs(adev, raster_config, + adev->gfx.config.backend_enable_mask, + num_rb_pipes); + } + + /* cache the values for userspace */ + for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { + for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { + gfx_v6_0_select_se_sh(adev, i, j, 0xffffffff); + adev->gfx.config.rb_config[i][j].rb_backend_disable = + RREG32(mmCC_RB_BACKEND_DISABLE); + adev->gfx.config.rb_config[i][j].user_rb_backend_disable = + RREG32(mmGC_USER_RB_BACKEND_DISABLE); + adev->gfx.config.rb_config[i][j].raster_config = + RREG32(mmPA_SC_RASTER_CONFIG); + } } gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); mutex_unlock(&adev->grbm_idx_mutex); @@ -1540,36 +1525,44 @@ static void gmc_v6_0_init_compute_vmid(struct amdgpu_device *adev) } */ -static u32 gfx_v6_0_get_cu_enabled(struct amdgpu_device *adev, u32 cu_per_sh) +static void gfx_v6_0_set_user_cu_inactive_bitmap(struct amdgpu_device *adev, + u32 bitmap) { - u32 data, mask; + u32 data; + + if (!bitmap) + return; - data = RREG32(mmCC_GC_SHADER_ARRAY_CONFIG); - data &= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; - data |= RREG32(mmGC_USER_SHADER_ARRAY_CONFIG); + data = bitmap << GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; + data &= GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; - data >>= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; + WREG32(mmGC_USER_SHADER_ARRAY_CONFIG, data); +} - mask = gfx_v6_0_create_bitmask(cu_per_sh); +static u32 gfx_v6_0_get_cu_enabled(struct amdgpu_device *adev) +{ + u32 data, mask; - return ~data & mask; + data = RREG32(mmCC_GC_SHADER_ARRAY_CONFIG) | + RREG32(mmGC_USER_SHADER_ARRAY_CONFIG); + + mask = gfx_v6_0_create_bitmask(adev->gfx.config.max_cu_per_sh); + return ~REG_GET_FIELD(data, CC_GC_SHADER_ARRAY_CONFIG, INACTIVE_CUS) & mask; } -static void gfx_v6_0_setup_spi(struct amdgpu_device *adev, - u32 se_num, u32 sh_per_se, - u32 cu_per_sh) +static void gfx_v6_0_setup_spi(struct amdgpu_device *adev) { int i, j, k; u32 data, mask; u32 active_cu = 0; mutex_lock(&adev->grbm_idx_mutex); - for (i = 0; i < se_num; i++) { - for (j = 0; j < sh_per_se; j++) { + for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { + for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { gfx_v6_0_select_se_sh(adev, i, j, 0xffffffff); data = RREG32(mmSPI_STATIC_THREAD_MGMT_3); - active_cu = gfx_v6_0_get_cu_enabled(adev, cu_per_sh); + active_cu = gfx_v6_0_get_cu_enabled(adev); mask = 1; for (k = 0; k < 16; k++) { @@ -1717,6 +1710,9 @@ static void gfx_v6_0_gpu_init(struct amdgpu_device *adev) gb_addr_config |= 2 << GB_ADDR_CONFIG__ROW_SIZE__SHIFT; break; } + gb_addr_config &= ~GB_ADDR_CONFIG__NUM_SHADER_ENGINES_MASK; + if (adev->gfx.config.max_shader_engines == 2) + gb_addr_config |= 1 << GB_ADDR_CONFIG__NUM_SHADER_ENGINES__SHIFT; adev->gfx.config.gb_addr_config = gb_addr_config; WREG32(mmGB_ADDR_CONFIG, gb_addr_config); @@ -1735,13 +1731,9 @@ static void gfx_v6_0_gpu_init(struct amdgpu_device *adev) #endif gfx_v6_0_tiling_mode_table_init(adev); - gfx_v6_0_setup_rb(adev, adev->gfx.config.max_shader_engines, - adev->gfx.config.max_sh_per_se, - adev->gfx.config.max_backends_per_se); + gfx_v6_0_setup_rb(adev); - gfx_v6_0_setup_spi(adev, adev->gfx.config.max_shader_engines, - adev->gfx.config.max_sh_per_se, - adev->gfx.config.max_cu_per_sh); + gfx_v6_0_setup_spi(adev); gfx_v6_0_get_cu_info(adev); @@ -1794,14 +1786,9 @@ static void gfx_v6_0_gpu_init(struct amdgpu_device *adev) static void gfx_v6_0_scratch_init(struct amdgpu_device *adev) { - int i; - adev->gfx.scratch.num_reg = 7; adev->gfx.scratch.reg_base = mmSCRATCH_REG0; - for (i = 0; i < adev->gfx.scratch.num_reg; i++) { - adev->gfx.scratch.free[i] = true; - adev->gfx.scratch.reg[i] = adev->gfx.scratch.reg_base + i; - } + adev->gfx.scratch.free_mask = (1u << adev->gfx.scratch.num_reg) - 1; } static int gfx_v6_0_ring_test_ring(struct amdgpu_ring *ring) @@ -1975,7 +1962,7 @@ static int gfx_v6_0_ring_test_ib(struct amdgpu_ring *ring, long timeout) ib.ptr[2] = 0xDEADBEEF; ib.length_dw = 3; - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) goto err2; @@ -2946,61 +2933,16 @@ static void gfx_v6_0_enable_gfx_cgpg(struct amdgpu_device *adev, } } -static u32 gfx_v6_0_get_cu_active_bitmap(struct amdgpu_device *adev, - u32 se, u32 sh) -{ - - u32 mask = 0, tmp, tmp1; - int i; - - mutex_lock(&adev->grbm_idx_mutex); - gfx_v6_0_select_se_sh(adev, se, sh, 0xffffffff); - tmp = RREG32(mmCC_GC_SHADER_ARRAY_CONFIG); - tmp1 = RREG32(mmGC_USER_SHADER_ARRAY_CONFIG); - gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); - mutex_unlock(&adev->grbm_idx_mutex); - - tmp &= 0xffff0000; - - tmp |= tmp1; - tmp >>= 16; - - for (i = 0; i < adev->gfx.config.max_cu_per_sh; i ++) { - mask <<= 1; - mask |= 1; - } - - return (~tmp) & mask; -} - static void gfx_v6_0_init_ao_cu_mask(struct amdgpu_device *adev) { - u32 i, j, k, active_cu_number = 0; - - u32 mask, counter, cu_bitmap; - u32 tmp = 0; - - for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { - for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { - mask = 1; - cu_bitmap = 0; - counter = 0; - for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { - if (gfx_v6_0_get_cu_active_bitmap(adev, i, j) & mask) { - if (counter < 2) - cu_bitmap |= mask; - counter++; - } - mask <<= 1; - } + u32 tmp; - active_cu_number += counter; - tmp |= (cu_bitmap << (i * 16 + j * 8)); - } - } + WREG32(mmRLC_PG_ALWAYS_ON_CU_MASK, adev->gfx.cu_info.ao_cu_mask); - WREG32(mmRLC_PG_AO_CU_MASK, tmp); - WREG32_FIELD(RLC_MAX_PG_CU, MAX_POWERED_UP_CU, active_cu_number); + tmp = RREG32(mmRLC_MAX_PG_CU); + tmp &= ~RLC_MAX_PG_CU__MAX_POWERED_UP_CU_MASK; + tmp |= (adev->gfx.cu_info.number << RLC_MAX_PG_CU__MAX_POWERED_UP_CU__SHIFT); + WREG32(mmRLC_MAX_PG_CU, tmp); } static void gfx_v6_0_enable_gfx_static_mgpg(struct amdgpu_device *adev, @@ -3775,18 +3717,26 @@ static void gfx_v6_0_get_cu_info(struct amdgpu_device *adev) int i, j, k, counter, active_cu_number = 0; u32 mask, bitmap, ao_bitmap, ao_cu_mask = 0; struct amdgpu_cu_info *cu_info = &adev->gfx.cu_info; + unsigned disable_masks[4 * 2]; memset(cu_info, 0, sizeof(*cu_info)); + amdgpu_gfx_parse_disable_cu(disable_masks, 4, 2); + + mutex_lock(&adev->grbm_idx_mutex); for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { mask = 1; ao_bitmap = 0; counter = 0; - bitmap = gfx_v6_0_get_cu_active_bitmap(adev, i, j); + gfx_v6_0_select_se_sh(adev, i, j, 0xffffffff); + if (i < 4 && j < 2) + gfx_v6_0_set_user_cu_inactive_bitmap( + adev, disable_masks[i * 2 + j]); + bitmap = gfx_v6_0_get_cu_enabled(adev); cu_info->bitmap[i][j] = bitmap; - for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { + for (k = 0; k < 16; k++) { if (bitmap & mask) { if (counter < 2) ao_bitmap |= mask; @@ -3799,6 +3749,9 @@ static void gfx_v6_0_get_cu_info(struct amdgpu_device *adev) } } + gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); + mutex_unlock(&adev->grbm_idx_mutex); + cu_info->number = active_cu_number; cu_info->ao_cu_mask = ao_cu_mask; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index c4e14015ec5b..1f9354541f29 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -1983,6 +1983,14 @@ static void gfx_v7_0_gpu_init(struct amdgpu_device *adev) WREG32(mmPA_CL_ENHANCE, PA_CL_ENHANCE__CLIP_VTX_REORDER_ENA_MASK | (3 << PA_CL_ENHANCE__NUM_CLIP_SEQ__SHIFT)); WREG32(mmPA_SC_ENHANCE, PA_SC_ENHANCE__ENABLE_PA_SC_OUT_OF_ORDER_MASK); + + tmp = RREG32(mmSPI_ARB_PRIORITY); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS0, 2); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS1, 2); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS2, 2); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS3, 2); + WREG32(mmSPI_ARB_PRIORITY, tmp); + mutex_unlock(&adev->grbm_idx_mutex); udelay(50); @@ -2003,14 +2011,9 @@ static void gfx_v7_0_gpu_init(struct amdgpu_device *adev) */ static void gfx_v7_0_scratch_init(struct amdgpu_device *adev) { - int i; - adev->gfx.scratch.num_reg = 7; adev->gfx.scratch.reg_base = mmSCRATCH_REG0; - for (i = 0; i < adev->gfx.scratch.num_reg; i++) { - adev->gfx.scratch.free[i] = true; - adev->gfx.scratch.reg[i] = adev->gfx.scratch.reg_base + i; - } + adev->gfx.scratch.free_mask = (1u << adev->gfx.scratch.num_reg) - 1; } /** @@ -2321,7 +2324,7 @@ static int gfx_v7_0_ring_test_ib(struct amdgpu_ring *ring, long timeout) ib.ptr[2] = 0xDEADBEEF; ib.length_dw = 3; - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) goto err2; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 373374164bd5..67afc901905c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -657,6 +657,8 @@ static void gfx_v8_0_set_gds_init(struct amdgpu_device *adev); static void gfx_v8_0_set_rlc_funcs(struct amdgpu_device *adev); static u32 gfx_v8_0_get_csb_size(struct amdgpu_device *adev); static void gfx_v8_0_get_cu_info(struct amdgpu_device *adev); +static void gfx_v8_0_ring_emit_ce_meta_init(struct amdgpu_ring *ring, uint64_t addr); +static void gfx_v8_0_ring_emit_de_meta_init(struct amdgpu_ring *ring, uint64_t addr); static void gfx_v8_0_init_golden_registers(struct amdgpu_device *adev) { @@ -749,14 +751,9 @@ static void gfx_v8_0_init_golden_registers(struct amdgpu_device *adev) static void gfx_v8_0_scratch_init(struct amdgpu_device *adev) { - int i; - adev->gfx.scratch.num_reg = 7; adev->gfx.scratch.reg_base = mmSCRATCH_REG0; - for (i = 0; i < adev->gfx.scratch.num_reg; i++) { - adev->gfx.scratch.free[i] = true; - adev->gfx.scratch.reg[i] = adev->gfx.scratch.reg_base + i; - } + adev->gfx.scratch.free_mask = (1u << adev->gfx.scratch.num_reg) - 1; } static int gfx_v8_0_ring_test_ring(struct amdgpu_ring *ring) @@ -829,7 +826,7 @@ static int gfx_v8_0_ring_test_ib(struct amdgpu_ring *ring, long timeout) ib.ptr[2] = 0xDEADBEEF; ib.length_dw = 3; - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) goto err2; @@ -941,6 +938,13 @@ static int gfx_v8_0_init_microcode(struct amdgpu_device *adev) goto out; cp_hdr = (const struct gfx_firmware_header_v1_0 *)adev->gfx.me_fw->data; adev->gfx.me_fw_version = le32_to_cpu(cp_hdr->header.ucode_version); + + /* chain ib ucode isn't formal released, just disable it by far + * TODO: when ucod ready we should use ucode version to judge if + * chain-ib support or not. + */ + adev->virt.chained_ib_support = false; + adev->gfx.me_feature_version = le32_to_cpu(cp_hdr->ucode_feature_version); snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_ce.bin", chip_name); @@ -1367,6 +1371,51 @@ static void gfx_v8_0_mec_fini(struct amdgpu_device *adev) } } +static int gfx_v8_0_kiq_init_ring(struct amdgpu_device *adev, + struct amdgpu_ring *ring, + struct amdgpu_irq_src *irq) +{ + int r = 0; + + if (amdgpu_sriov_vf(adev)) { + r = amdgpu_wb_get(adev, &adev->virt.reg_val_offs); + if (r) + return r; + } + + ring->adev = NULL; + ring->ring_obj = NULL; + ring->use_doorbell = true; + ring->doorbell_index = AMDGPU_DOORBELL_KIQ; + if (adev->gfx.mec2_fw) { + ring->me = 2; + ring->pipe = 0; + } else { + ring->me = 1; + ring->pipe = 1; + } + + irq->data = ring; + ring->queue = 0; + sprintf(ring->name, "kiq %d.%d.%d", ring->me, ring->pipe, ring->queue); + r = amdgpu_ring_init(adev, ring, 1024, + irq, AMDGPU_CP_KIQ_IRQ_DRIVER0); + if (r) + dev_warn(adev->dev, "(%d) failed to init kiq ring\n", r); + + return r; +} + +static void gfx_v8_0_kiq_free_ring(struct amdgpu_ring *ring, + struct amdgpu_irq_src *irq) +{ + if (amdgpu_sriov_vf(ring->adev)) + amdgpu_wb_free(ring->adev, ring->adev->virt.reg_val_offs); + + amdgpu_ring_fini(ring); + irq->data = NULL; +} + #define MEC_HPD_SIZE 2048 static int gfx_v8_0_mec_init(struct amdgpu_device *adev) @@ -1421,6 +1470,35 @@ static int gfx_v8_0_mec_init(struct amdgpu_device *adev) return 0; } +static void gfx_v8_0_kiq_fini(struct amdgpu_device *adev) +{ + struct amdgpu_kiq *kiq = &adev->gfx.kiq; + + amdgpu_bo_free_kernel(&kiq->eop_obj, &kiq->eop_gpu_addr, NULL); + kiq->eop_obj = NULL; +} + +static int gfx_v8_0_kiq_init(struct amdgpu_device *adev) +{ + int r; + u32 *hpd; + struct amdgpu_kiq *kiq = &adev->gfx.kiq; + + r = amdgpu_bo_create_kernel(adev, MEC_HPD_SIZE, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_GTT, &kiq->eop_obj, + &kiq->eop_gpu_addr, (void **)&hpd); + if (r) { + dev_warn(adev->dev, "failed to create KIQ bo (%d).\n", r); + return r; + } + + memset(hpd, 0, MEC_HPD_SIZE); + + amdgpu_bo_kunmap(kiq->eop_obj); + + return 0; +} + static const u32 vgpr_init_compute_shader[] = { 0x7e000209, 0x7e020208, @@ -1702,7 +1780,7 @@ static int gfx_v8_0_do_edc_gpr_workarounds(struct amdgpu_device *adev) ib.ptr[ib.length_dw++] = EVENT_TYPE(7) | EVENT_INDEX(4); /* shedule the ib on the ring */ - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) { DRM_ERROR("amdgpu: ib submit failed (%d).\n", r); goto fail; @@ -1997,8 +2075,14 @@ static int gfx_v8_0_sw_init(void *handle) { int i, r; struct amdgpu_ring *ring; + struct amdgpu_kiq *kiq; struct amdgpu_device *adev = (struct amdgpu_device *)handle; + /* KIQ event */ + r = amdgpu_irq_add_id(adev, 178, &adev->gfx.kiq.irq); + if (r) + return r; + /* EOP Event */ r = amdgpu_irq_add_id(adev, 181, &adev->gfx.eop_irq); if (r) @@ -2036,6 +2120,17 @@ static int gfx_v8_0_sw_init(void *handle) return r; } + r = gfx_v8_0_kiq_init(adev); + if (r) { + DRM_ERROR("Failed to init KIQ BOs!\n"); + return r; + } + + kiq = &adev->gfx.kiq; + r = gfx_v8_0_kiq_init_ring(adev, &kiq->ring, &kiq->irq); + if (r) + return r; + /* set up the gfx ring */ for (i = 0; i < adev->gfx.num_gfx_rings; i++) { ring = &adev->gfx.gfx_ring[i]; @@ -2119,7 +2214,9 @@ static int gfx_v8_0_sw_fini(void *handle) amdgpu_ring_fini(&adev->gfx.gfx_ring[i]); for (i = 0; i < adev->gfx.num_compute_rings; i++) amdgpu_ring_fini(&adev->gfx.compute_ring[i]); + gfx_v8_0_kiq_free_ring(&adev->gfx.kiq.ring, &adev->gfx.kiq.irq); + gfx_v8_0_kiq_fini(adev); gfx_v8_0_mec_fini(adev); gfx_v8_0_rlc_fini(adev); gfx_v8_0_free_microcode(adev); @@ -3801,6 +3898,14 @@ static void gfx_v8_0_gpu_init(struct amdgpu_device *adev) PA_SC_FIFO_SIZE__SC_HIZ_TILE_FIFO_SIZE__SHIFT) | (adev->gfx.config.sc_earlyz_tile_fifo_size << PA_SC_FIFO_SIZE__SC_EARLYZ_TILE_FIFO_SIZE__SHIFT)); + + tmp = RREG32(mmSPI_ARB_PRIORITY); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS0, 2); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS1, 2); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS2, 2); + tmp = REG_SET_FIELD(tmp, SPI_ARB_PRIORITY, PIPE_ORDER_TS3, 2); + WREG32(mmSPI_ARB_PRIORITY, tmp); + mutex_unlock(&adev->grbm_idx_mutex); } @@ -4024,17 +4129,6 @@ static void gfx_v8_0_init_pg(struct amdgpu_device *adev) WREG32(mmRLC_JUMP_TABLE_RESTORE, adev->gfx.rlc.cp_table_gpu_addr >> 8); gfx_v8_0_init_power_gating(adev); WREG32(mmRLC_PG_ALWAYS_ON_CU_MASK, adev->gfx.cu_info.ao_cu_mask); - if (adev->pg_flags & AMD_PG_SUPPORT_RLC_SMU_HS) { - cz_enable_sck_slow_down_on_power_up(adev, true); - cz_enable_sck_slow_down_on_power_down(adev, true); - } else { - cz_enable_sck_slow_down_on_power_up(adev, false); - cz_enable_sck_slow_down_on_power_down(adev, false); - } - if (adev->pg_flags & AMD_PG_SUPPORT_CP) - cz_enable_cp_power_gating(adev, true); - else - cz_enable_cp_power_gating(adev, false); } else if ((adev->asic_type == CHIP_POLARIS11) || (adev->asic_type == CHIP_POLARIS12)) { gfx_v8_0_init_csb(adev); @@ -4506,6 +4600,393 @@ static void gfx_v8_0_cp_compute_fini(struct amdgpu_device *adev) } } +/* KIQ functions */ +static void gfx_v8_0_kiq_setting(struct amdgpu_ring *ring) +{ + uint32_t tmp; + struct amdgpu_device *adev = ring->adev; + + /* tell RLC which is KIQ queue */ + tmp = RREG32(mmRLC_CP_SCHEDULERS); + tmp &= 0xffffff00; + tmp |= (ring->me << 5) | (ring->pipe << 3) | (ring->queue); + WREG32(mmRLC_CP_SCHEDULERS, tmp); + tmp |= 0x80; + WREG32(mmRLC_CP_SCHEDULERS, tmp); +} + +static void gfx_v8_0_kiq_enable(struct amdgpu_ring *ring) +{ + amdgpu_ring_alloc(ring, 8); + /* set resources */ + amdgpu_ring_write(ring, PACKET3(PACKET3_SET_RESOURCES, 6)); + amdgpu_ring_write(ring, 0); /* vmid_mask:0 queue_type:0 (KIQ) */ + amdgpu_ring_write(ring, 0x000000FF); /* queue mask lo */ + amdgpu_ring_write(ring, 0); /* queue mask hi */ + amdgpu_ring_write(ring, 0); /* gws mask lo */ + amdgpu_ring_write(ring, 0); /* gws mask hi */ + amdgpu_ring_write(ring, 0); /* oac mask */ + amdgpu_ring_write(ring, 0); /* gds heap base:0, gds heap size:0 */ + amdgpu_ring_commit(ring); + udelay(50); +} + +static void gfx_v8_0_map_queue_enable(struct amdgpu_ring *kiq_ring, + struct amdgpu_ring *ring) +{ + struct amdgpu_device *adev = kiq_ring->adev; + uint64_t mqd_addr, wptr_addr; + + mqd_addr = amdgpu_bo_gpu_offset(ring->mqd_obj); + wptr_addr = adev->wb.gpu_addr + (ring->wptr_offs * 4); + amdgpu_ring_alloc(kiq_ring, 8); + + amdgpu_ring_write(kiq_ring, PACKET3(PACKET3_MAP_QUEUES, 5)); + /* Q_sel:0, vmid:0, vidmem: 1, engine:0, num_Q:1*/ + amdgpu_ring_write(kiq_ring, 0x21010000); + amdgpu_ring_write(kiq_ring, (ring->doorbell_index << 2) | + (ring->queue << 26) | + (ring->pipe << 29) | + ((ring->me == 1 ? 0 : 1) << 31)); /* doorbell */ + amdgpu_ring_write(kiq_ring, lower_32_bits(mqd_addr)); + amdgpu_ring_write(kiq_ring, upper_32_bits(mqd_addr)); + amdgpu_ring_write(kiq_ring, lower_32_bits(wptr_addr)); + amdgpu_ring_write(kiq_ring, upper_32_bits(wptr_addr)); + amdgpu_ring_commit(kiq_ring); + udelay(50); +} + +static int gfx_v8_0_mqd_init(struct amdgpu_device *adev, + struct vi_mqd *mqd, + uint64_t mqd_gpu_addr, + uint64_t eop_gpu_addr, + struct amdgpu_ring *ring) +{ + uint64_t hqd_gpu_addr, wb_gpu_addr, eop_base_addr; + uint32_t tmp; + + mqd->header = 0xC0310800; + mqd->compute_pipelinestat_enable = 0x00000001; + mqd->compute_static_thread_mgmt_se0 = 0xffffffff; + mqd->compute_static_thread_mgmt_se1 = 0xffffffff; + mqd->compute_static_thread_mgmt_se2 = 0xffffffff; + mqd->compute_static_thread_mgmt_se3 = 0xffffffff; + mqd->compute_misc_reserved = 0x00000003; + + eop_base_addr = eop_gpu_addr >> 8; + mqd->cp_hqd_eop_base_addr_lo = eop_base_addr; + mqd->cp_hqd_eop_base_addr_hi = upper_32_bits(eop_base_addr); + + /* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */ + tmp = RREG32(mmCP_HQD_EOP_CONTROL); + tmp = REG_SET_FIELD(tmp, CP_HQD_EOP_CONTROL, EOP_SIZE, + (order_base_2(MEC_HPD_SIZE / 4) - 1)); + + mqd->cp_hqd_eop_control = tmp; + + /* enable doorbell? */ + tmp = RREG32(mmCP_HQD_PQ_DOORBELL_CONTROL); + + if (ring->use_doorbell) + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_DOORBELL_CONTROL, + DOORBELL_EN, 1); + else + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_DOORBELL_CONTROL, + DOORBELL_EN, 0); + + mqd->cp_hqd_pq_doorbell_control = tmp; + + /* disable the queue if it's active */ + mqd->cp_hqd_dequeue_request = 0; + mqd->cp_hqd_pq_rptr = 0; + mqd->cp_hqd_pq_wptr = 0; + + /* set the pointer to the MQD */ + mqd->cp_mqd_base_addr_lo = mqd_gpu_addr & 0xfffffffc; + mqd->cp_mqd_base_addr_hi = upper_32_bits(mqd_gpu_addr); + + /* set MQD vmid to 0 */ + tmp = RREG32(mmCP_MQD_CONTROL); + tmp = REG_SET_FIELD(tmp, CP_MQD_CONTROL, VMID, 0); + mqd->cp_mqd_control = tmp; + + /* set the pointer to the HQD, this is similar CP_RB0_BASE/_HI */ + hqd_gpu_addr = ring->gpu_addr >> 8; + mqd->cp_hqd_pq_base_lo = hqd_gpu_addr; + mqd->cp_hqd_pq_base_hi = upper_32_bits(hqd_gpu_addr); + + /* set up the HQD, this is similar to CP_RB0_CNTL */ + tmp = RREG32(mmCP_HQD_PQ_CONTROL); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, QUEUE_SIZE, + (order_base_2(ring->ring_size / 4) - 1)); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, RPTR_BLOCK_SIZE, + ((order_base_2(AMDGPU_GPU_PAGE_SIZE / 4) - 1) << 8)); +#ifdef __BIG_ENDIAN + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, ENDIAN_SWAP, 1); +#endif + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, UNORD_DISPATCH, 0); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, ROQ_PQ_IB_FLIP, 0); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, PRIV_STATE, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_CONTROL, KMD_QUEUE, 1); + mqd->cp_hqd_pq_control = tmp; + + /* set the wb address whether it's enabled or not */ + wb_gpu_addr = adev->wb.gpu_addr + (ring->rptr_offs * 4); + mqd->cp_hqd_pq_rptr_report_addr_lo = wb_gpu_addr & 0xfffffffc; + mqd->cp_hqd_pq_rptr_report_addr_hi = + upper_32_bits(wb_gpu_addr) & 0xffff; + + /* only used if CP_PQ_WPTR_POLL_CNTL.CP_PQ_WPTR_POLL_CNTL__EN_MASK=1 */ + wb_gpu_addr = adev->wb.gpu_addr + (ring->wptr_offs * 4); + mqd->cp_hqd_pq_wptr_poll_addr_lo = wb_gpu_addr & 0xfffffffc; + mqd->cp_hqd_pq_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr) & 0xffff; + + tmp = 0; + /* enable the doorbell if requested */ + if (ring->use_doorbell) { + tmp = RREG32(mmCP_HQD_PQ_DOORBELL_CONTROL); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_DOORBELL_CONTROL, + DOORBELL_OFFSET, ring->doorbell_index); + + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_DOORBELL_CONTROL, + DOORBELL_EN, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_DOORBELL_CONTROL, + DOORBELL_SOURCE, 0); + tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_DOORBELL_CONTROL, + DOORBELL_HIT, 0); + } + + mqd->cp_hqd_pq_doorbell_control = tmp; + + /* reset read and write pointers, similar to CP_RB0_WPTR/_RPTR */ + ring->wptr = 0; + mqd->cp_hqd_pq_wptr = ring->wptr; + mqd->cp_hqd_pq_rptr = RREG32(mmCP_HQD_PQ_RPTR); + + /* set the vmid for the queue */ + mqd->cp_hqd_vmid = 0; + + tmp = RREG32(mmCP_HQD_PERSISTENT_STATE); + tmp = REG_SET_FIELD(tmp, CP_HQD_PERSISTENT_STATE, PRELOAD_SIZE, 0x53); + mqd->cp_hqd_persistent_state = tmp; + + /* activate the queue */ + mqd->cp_hqd_active = 1; + + return 0; +} + +static int gfx_v8_0_kiq_init_register(struct amdgpu_device *adev, + struct vi_mqd *mqd, + struct amdgpu_ring *ring) +{ + uint32_t tmp; + int j; + + /* disable wptr polling */ + tmp = RREG32(mmCP_PQ_WPTR_POLL_CNTL); + tmp = REG_SET_FIELD(tmp, CP_PQ_WPTR_POLL_CNTL, EN, 0); + WREG32(mmCP_PQ_WPTR_POLL_CNTL, tmp); + + WREG32(mmCP_HQD_EOP_BASE_ADDR, mqd->cp_hqd_eop_base_addr_lo); + WREG32(mmCP_HQD_EOP_BASE_ADDR_HI, mqd->cp_hqd_eop_base_addr_hi); + + /* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */ + WREG32(mmCP_HQD_EOP_CONTROL, mqd->cp_hqd_eop_control); + + /* enable doorbell? */ + WREG32(mmCP_HQD_PQ_DOORBELL_CONTROL, mqd->cp_hqd_pq_doorbell_control); + + /* disable the queue if it's active */ + if (RREG32(mmCP_HQD_ACTIVE) & 1) { + WREG32(mmCP_HQD_DEQUEUE_REQUEST, 1); + for (j = 0; j < adev->usec_timeout; j++) { + if (!(RREG32(mmCP_HQD_ACTIVE) & 1)) + break; + udelay(1); + } + WREG32(mmCP_HQD_DEQUEUE_REQUEST, mqd->cp_hqd_dequeue_request); + WREG32(mmCP_HQD_PQ_RPTR, mqd->cp_hqd_pq_rptr); + WREG32(mmCP_HQD_PQ_WPTR, mqd->cp_hqd_pq_wptr); + } + + /* set the pointer to the MQD */ + WREG32(mmCP_MQD_BASE_ADDR, mqd->cp_mqd_base_addr_lo); + WREG32(mmCP_MQD_BASE_ADDR_HI, mqd->cp_mqd_base_addr_hi); + + /* set MQD vmid to 0 */ + WREG32(mmCP_MQD_CONTROL, mqd->cp_mqd_control); + + /* set the pointer to the HQD, this is similar CP_RB0_BASE/_HI */ + WREG32(mmCP_HQD_PQ_BASE, mqd->cp_hqd_pq_base_lo); + WREG32(mmCP_HQD_PQ_BASE_HI, mqd->cp_hqd_pq_base_hi); + + /* set up the HQD, this is similar to CP_RB0_CNTL */ + WREG32(mmCP_HQD_PQ_CONTROL, mqd->cp_hqd_pq_control); + + /* set the wb address whether it's enabled or not */ + WREG32(mmCP_HQD_PQ_RPTR_REPORT_ADDR, + mqd->cp_hqd_pq_rptr_report_addr_lo); + WREG32(mmCP_HQD_PQ_RPTR_REPORT_ADDR_HI, + mqd->cp_hqd_pq_rptr_report_addr_hi); + + /* only used if CP_PQ_WPTR_POLL_CNTL.CP_PQ_WPTR_POLL_CNTL__EN_MASK=1 */ + WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR, mqd->cp_hqd_pq_wptr_poll_addr_lo); + WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR_HI, mqd->cp_hqd_pq_wptr_poll_addr_hi); + + /* enable the doorbell if requested */ + if (ring->use_doorbell) { + if ((adev->asic_type == CHIP_CARRIZO) || + (adev->asic_type == CHIP_FIJI) || + (adev->asic_type == CHIP_STONEY)) { + WREG32(mmCP_MEC_DOORBELL_RANGE_LOWER, + AMDGPU_DOORBELL_KIQ << 2); + WREG32(mmCP_MEC_DOORBELL_RANGE_UPPER, + AMDGPU_DOORBELL_MEC_RING7 << 2); + } + } + WREG32(mmCP_HQD_PQ_DOORBELL_CONTROL, mqd->cp_hqd_pq_doorbell_control); + + /* reset read and write pointers, similar to CP_RB0_WPTR/_RPTR */ + WREG32(mmCP_HQD_PQ_WPTR, mqd->cp_hqd_pq_wptr); + + /* set the vmid for the queue */ + WREG32(mmCP_HQD_VMID, mqd->cp_hqd_vmid); + + WREG32(mmCP_HQD_PERSISTENT_STATE, mqd->cp_hqd_persistent_state); + + /* activate the queue */ + WREG32(mmCP_HQD_ACTIVE, mqd->cp_hqd_active); + + if (ring->use_doorbell) { + tmp = RREG32(mmCP_PQ_STATUS); + tmp = REG_SET_FIELD(tmp, CP_PQ_STATUS, DOORBELL_ENABLE, 1); + WREG32(mmCP_PQ_STATUS, tmp); + } + + return 0; +} + +static int gfx_v8_0_kiq_init_queue(struct amdgpu_ring *ring, + struct vi_mqd *mqd, + u64 mqd_gpu_addr) +{ + struct amdgpu_device *adev = ring->adev; + struct amdgpu_kiq *kiq = &adev->gfx.kiq; + uint64_t eop_gpu_addr; + bool is_kiq = false; + + if (ring->funcs->type == AMDGPU_RING_TYPE_KIQ) + is_kiq = true; + + if (is_kiq) { + eop_gpu_addr = kiq->eop_gpu_addr; + gfx_v8_0_kiq_setting(&kiq->ring); + } else + eop_gpu_addr = adev->gfx.mec.hpd_eop_gpu_addr + + ring->queue * MEC_HPD_SIZE; + + mutex_lock(&adev->srbm_mutex); + vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); + + gfx_v8_0_mqd_init(adev, mqd, mqd_gpu_addr, eop_gpu_addr, ring); + + if (is_kiq) + gfx_v8_0_kiq_init_register(adev, mqd, ring); + + vi_srbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + + if (is_kiq) + gfx_v8_0_kiq_enable(ring); + else + gfx_v8_0_map_queue_enable(&kiq->ring, ring); + + return 0; +} + +static void gfx_v8_0_kiq_free_queue(struct amdgpu_device *adev) +{ + struct amdgpu_ring *ring = NULL; + int i; + + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + amdgpu_bo_free_kernel(&ring->mqd_obj, NULL, NULL); + ring->mqd_obj = NULL; + } + + ring = &adev->gfx.kiq.ring; + amdgpu_bo_free_kernel(&ring->mqd_obj, NULL, NULL); + ring->mqd_obj = NULL; +} + +static int gfx_v8_0_kiq_setup_queue(struct amdgpu_device *adev, + struct amdgpu_ring *ring) +{ + struct vi_mqd *mqd; + u64 mqd_gpu_addr; + u32 *buf; + int r = 0; + + r = amdgpu_bo_create_kernel(adev, sizeof(struct vi_mqd), PAGE_SIZE, + AMDGPU_GEM_DOMAIN_GTT, &ring->mqd_obj, + &mqd_gpu_addr, (void **)&buf); + if (r) { + dev_warn(adev->dev, "failed to create ring mqd ob (%d)", r); + return r; + } + + /* init the mqd struct */ + memset(buf, 0, sizeof(struct vi_mqd)); + mqd = (struct vi_mqd *)buf; + + r = gfx_v8_0_kiq_init_queue(ring, mqd, mqd_gpu_addr); + if (r) + return r; + + amdgpu_bo_kunmap(ring->mqd_obj); + + return 0; +} + +static int gfx_v8_0_kiq_resume(struct amdgpu_device *adev) +{ + struct amdgpu_ring *ring = NULL; + int r, i; + + ring = &adev->gfx.kiq.ring; + r = gfx_v8_0_kiq_setup_queue(adev, ring); + if (r) + return r; + + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + r = gfx_v8_0_kiq_setup_queue(adev, ring); + if (r) + return r; + } + + gfx_v8_0_cp_compute_enable(adev, true); + + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + + ring->ready = true; + r = amdgpu_ring_test_ring(ring); + if (r) + ring->ready = false; + } + + ring = &adev->gfx.kiq.ring; + ring->ready = true; + r = amdgpu_ring_test_ring(ring); + if (r) + ring->ready = false; + + return 0; +} + static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev) { int r, i, j; @@ -4806,7 +5287,10 @@ static int gfx_v8_0_cp_resume(struct amdgpu_device *adev) if (r) return r; - r = gfx_v8_0_cp_compute_resume(adev); + if (amdgpu_sriov_vf(adev)) + r = gfx_v8_0_kiq_resume(adev); + else + r = gfx_v8_0_cp_compute_resume(adev); if (r) return r; @@ -4845,6 +5329,7 @@ static int gfx_v8_0_hw_fini(void *handle) amdgpu_irq_put(adev, &adev->gfx.priv_reg_irq, 0); amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0); if (amdgpu_sriov_vf(adev)) { + gfx_v8_0_kiq_free_queue(adev); pr_debug("For SRIOV client, shouldn't do anything.\n"); return 0; } @@ -5360,6 +5845,18 @@ static int gfx_v8_0_set_powergating_state(void *handle, case CHIP_CARRIZO: case CHIP_STONEY: + if (adev->pg_flags & AMD_PG_SUPPORT_RLC_SMU_HS) { + cz_enable_sck_slow_down_on_power_up(adev, true); + cz_enable_sck_slow_down_on_power_down(adev, true); + } else { + cz_enable_sck_slow_down_on_power_up(adev, false); + cz_enable_sck_slow_down_on_power_down(adev, false); + } + if (adev->pg_flags & AMD_PG_SUPPORT_CP) + cz_enable_cp_power_gating(adev, true); + else + cz_enable_cp_power_gating(adev, false); + cz_update_gfx_cg_power_gating(adev, enable); if ((adev->pg_flags & AMD_PG_SUPPORT_GFX_SMG) && enable) @@ -5396,6 +5893,45 @@ static int gfx_v8_0_set_powergating_state(void *handle, return 0; } +static void gfx_v8_0_get_clockgating_state(void *handle, u32 *flags) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int data; + + /* AMD_CG_SUPPORT_GFX_MGCG */ + data = RREG32(mmRLC_CGTT_MGCG_OVERRIDE); + if (!(data & RLC_CGTT_MGCG_OVERRIDE__CPF_MASK)) + *flags |= AMD_CG_SUPPORT_GFX_MGCG; + + /* AMD_CG_SUPPORT_GFX_CGLG */ + data = RREG32(mmRLC_CGCG_CGLS_CTRL); + if (data & RLC_CGCG_CGLS_CTRL__CGCG_EN_MASK) + *flags |= AMD_CG_SUPPORT_GFX_CGCG; + + /* AMD_CG_SUPPORT_GFX_CGLS */ + if (data & RLC_CGCG_CGLS_CTRL__CGLS_EN_MASK) + *flags |= AMD_CG_SUPPORT_GFX_CGLS; + + /* AMD_CG_SUPPORT_GFX_CGTS */ + data = RREG32(mmCGTS_SM_CTRL_REG); + if (!(data & CGTS_SM_CTRL_REG__OVERRIDE_MASK)) + *flags |= AMD_CG_SUPPORT_GFX_CGTS; + + /* AMD_CG_SUPPORT_GFX_CGTS_LS */ + if (!(data & CGTS_SM_CTRL_REG__LS_OVERRIDE_MASK)) + *flags |= AMD_CG_SUPPORT_GFX_CGTS_LS; + + /* AMD_CG_SUPPORT_GFX_RLC_LS */ + data = RREG32(mmRLC_MEM_SLP_CNTL); + if (data & RLC_MEM_SLP_CNTL__RLC_MEM_LS_EN_MASK) + *flags |= AMD_CG_SUPPORT_GFX_RLC_LS | AMD_CG_SUPPORT_GFX_MGLS; + + /* AMD_CG_SUPPORT_GFX_CP_LS */ + data = RREG32(mmCP_MEM_SLP_CNTL); + if (data & CP_MEM_SLP_CNTL__CP_MEM_LS_EN_MASK) + *flags |= AMD_CG_SUPPORT_GFX_CP_LS | AMD_CG_SUPPORT_GFX_MGLS; +} + static void gfx_v8_0_send_serdes_cmd(struct amdgpu_device *adev, uint32_t reg_addr, uint32_t cmd) { @@ -5444,68 +5980,6 @@ static void gfx_v8_0_send_serdes_cmd(struct amdgpu_device *adev, #define RLC_GPR_REG2__MESSAGE__SHIFT 0x00000001 #define RLC_GPR_REG2__MESSAGE_MASK 0x0000001e -static void cz_enter_rlc_safe_mode(struct amdgpu_device *adev) -{ - u32 data = 0; - unsigned i; - - data = RREG32(mmRLC_CNTL); - if ((data & RLC_CNTL__RLC_ENABLE_F32_MASK) == 0) - return; - - if ((adev->cg_flags & (AMD_CG_SUPPORT_GFX_CGCG | AMD_CG_SUPPORT_GFX_MGCG)) || - (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG | AMD_PG_SUPPORT_GFX_SMG | - AMD_PG_SUPPORT_GFX_DMG))) { - data |= RLC_GPR_REG2__REQ_MASK; - data &= ~RLC_GPR_REG2__MESSAGE_MASK; - data |= (MSG_ENTER_RLC_SAFE_MODE << RLC_GPR_REG2__MESSAGE__SHIFT); - WREG32(mmRLC_GPR_REG2, data); - - for (i = 0; i < adev->usec_timeout; i++) { - if ((RREG32(mmRLC_GPM_STAT) & - (RLC_GPM_STAT__GFX_CLOCK_STATUS_MASK | - RLC_GPM_STAT__GFX_POWER_STATUS_MASK)) == - (RLC_GPM_STAT__GFX_CLOCK_STATUS_MASK | - RLC_GPM_STAT__GFX_POWER_STATUS_MASK)) - break; - udelay(1); - } - - for (i = 0; i < adev->usec_timeout; i++) { - if (!REG_GET_FIELD(RREG32(mmRLC_GPR_REG2), RLC_GPR_REG2, REQ)) - break; - udelay(1); - } - adev->gfx.rlc.in_safe_mode = true; - } -} - -static void cz_exit_rlc_safe_mode(struct amdgpu_device *adev) -{ - u32 data; - unsigned i; - - data = RREG32(mmRLC_CNTL); - if ((data & RLC_CNTL__RLC_ENABLE_F32_MASK) == 0) - return; - - if ((adev->cg_flags & (AMD_CG_SUPPORT_GFX_CGCG | AMD_CG_SUPPORT_GFX_MGCG)) || - (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG | AMD_PG_SUPPORT_GFX_SMG | - AMD_PG_SUPPORT_GFX_DMG))) { - data |= RLC_GPR_REG2__REQ_MASK; - data &= ~RLC_GPR_REG2__MESSAGE_MASK; - data |= (MSG_EXIT_RLC_SAFE_MODE << RLC_GPR_REG2__MESSAGE__SHIFT); - WREG32(mmRLC_GPR_REG2, data); - adev->gfx.rlc.in_safe_mode = false; - } - - for (i = 0; i < adev->usec_timeout; i++) { - if (!REG_GET_FIELD(RREG32(mmRLC_GPR_REG2), RLC_GPR_REG2, REQ)) - break; - udelay(1); - } -} - static void iceland_enter_rlc_safe_mode(struct amdgpu_device *adev) { u32 data; @@ -5565,31 +6039,11 @@ static void iceland_exit_rlc_safe_mode(struct amdgpu_device *adev) } } -static void gfx_v8_0_nop_enter_rlc_safe_mode(struct amdgpu_device *adev) -{ - adev->gfx.rlc.in_safe_mode = true; -} - -static void gfx_v8_0_nop_exit_rlc_safe_mode(struct amdgpu_device *adev) -{ - adev->gfx.rlc.in_safe_mode = false; -} - -static const struct amdgpu_rlc_funcs cz_rlc_funcs = { - .enter_safe_mode = cz_enter_rlc_safe_mode, - .exit_safe_mode = cz_exit_rlc_safe_mode -}; - static const struct amdgpu_rlc_funcs iceland_rlc_funcs = { .enter_safe_mode = iceland_enter_rlc_safe_mode, .exit_safe_mode = iceland_exit_rlc_safe_mode }; -static const struct amdgpu_rlc_funcs gfx_v8_0_nop_rlc_funcs = { - .enter_safe_mode = gfx_v8_0_nop_enter_rlc_safe_mode, - .exit_safe_mode = gfx_v8_0_nop_exit_rlc_safe_mode -}; - static void gfx_v8_0_update_medium_grain_clock_gating(struct amdgpu_device *adev, bool enable) { @@ -6011,7 +6465,8 @@ static void gfx_v8_0_ring_emit_hdp_flush(struct amdgpu_ring *ring) { u32 ref_and_mask, reg_mem_engine; - if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) { + if ((ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) || + (ring->funcs->type == AMDGPU_RING_TYPE_KIQ)) { switch (ring->me) { case 1: ref_and_mask = GPU_HDP_FLUSH_DONE__CP2_MASK << ring->pipe; @@ -6224,6 +6679,31 @@ static void gfx_v8_0_ring_emit_fence_compute(struct amdgpu_ring *ring, amdgpu_ring_write(ring, upper_32_bits(seq)); } +static void gfx_v8_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, + u64 seq, unsigned int flags) +{ + /* we only allocate 32bit for each seq wb address */ + BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); + + /* write fence seq to the "addr" */ + amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(5) | WR_CONFIRM)); + amdgpu_ring_write(ring, lower_32_bits(addr)); + amdgpu_ring_write(ring, upper_32_bits(addr)); + amdgpu_ring_write(ring, lower_32_bits(seq)); + + if (flags & AMDGPU_FENCE_FLAG_INT) { + /* set register to trigger INT */ + amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0) | WR_CONFIRM)); + amdgpu_ring_write(ring, mmCPC_INT_STATUS); + amdgpu_ring_write(ring, 0); + amdgpu_ring_write(ring, 0x20000000); /* src_id is 178 */ + } +} + static void gfx_v8_ring_emit_sb(struct amdgpu_ring *ring) { amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); @@ -6234,6 +6714,10 @@ static void gfx_v8_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags) { uint32_t dw2 = 0; + if (amdgpu_sriov_vf(ring->adev)) + gfx_v8_0_ring_emit_ce_meta_init(ring, + (flags & AMDGPU_VM_DOMAIN) ? AMDGPU_CSA_VADDR : ring->adev->virt.csa_vmid0_addr); + dw2 |= 0x80000000; /* set load_enable otherwise this package is just NOPs */ if (flags & AMDGPU_HAVE_CTX_SWITCH) { gfx_v8_0_ring_emit_vgt_flush(ring); @@ -6258,6 +6742,36 @@ static void gfx_v8_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags) amdgpu_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1)); amdgpu_ring_write(ring, dw2); amdgpu_ring_write(ring, 0); + + if (amdgpu_sriov_vf(ring->adev)) + gfx_v8_0_ring_emit_de_meta_init(ring, + (flags & AMDGPU_VM_DOMAIN) ? AMDGPU_CSA_VADDR : ring->adev->virt.csa_vmid0_addr); +} + +static void gfx_v8_0_ring_emit_rreg(struct amdgpu_ring *ring, uint32_t reg) +{ + struct amdgpu_device *adev = ring->adev; + + amdgpu_ring_write(ring, PACKET3(PACKET3_COPY_DATA, 4)); + amdgpu_ring_write(ring, 0 | /* src: register*/ + (5 << 8) | /* dst: memory */ + (1 << 20)); /* write confirm */ + amdgpu_ring_write(ring, reg); + amdgpu_ring_write(ring, 0); + amdgpu_ring_write(ring, lower_32_bits(adev->wb.gpu_addr + + adev->virt.reg_val_offs * 4)); + amdgpu_ring_write(ring, upper_32_bits(adev->wb.gpu_addr + + adev->virt.reg_val_offs * 4)); +} + +static void gfx_v8_0_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, + uint32_t val) +{ + amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + amdgpu_ring_write(ring, (1 << 16)); /* no inc addr */ + amdgpu_ring_write(ring, reg); + amdgpu_ring_write(ring, 0); + amdgpu_ring_write(ring, val); } static void gfx_v8_0_set_gfx_eop_interrupt_state(struct amdgpu_device *adev, @@ -6405,6 +6919,72 @@ static int gfx_v8_0_priv_inst_irq(struct amdgpu_device *adev, return 0; } +static int gfx_v8_0_kiq_set_interrupt_state(struct amdgpu_device *adev, + struct amdgpu_irq_src *src, + unsigned int type, + enum amdgpu_interrupt_state state) +{ + uint32_t tmp, target; + struct amdgpu_ring *ring = (struct amdgpu_ring *)src->data; + + BUG_ON(!ring || (ring->funcs->type != AMDGPU_RING_TYPE_KIQ)); + + if (ring->me == 1) + target = mmCP_ME1_PIPE0_INT_CNTL; + else + target = mmCP_ME2_PIPE0_INT_CNTL; + target += ring->pipe; + + switch (type) { + case AMDGPU_CP_KIQ_IRQ_DRIVER0: + if (state == AMDGPU_IRQ_STATE_DISABLE) { + tmp = RREG32(mmCPC_INT_CNTL); + tmp = REG_SET_FIELD(tmp, CPC_INT_CNTL, + GENERIC2_INT_ENABLE, 0); + WREG32(mmCPC_INT_CNTL, tmp); + + tmp = RREG32(target); + tmp = REG_SET_FIELD(tmp, CP_ME2_PIPE0_INT_CNTL, + GENERIC2_INT_ENABLE, 0); + WREG32(target, tmp); + } else { + tmp = RREG32(mmCPC_INT_CNTL); + tmp = REG_SET_FIELD(tmp, CPC_INT_CNTL, + GENERIC2_INT_ENABLE, 1); + WREG32(mmCPC_INT_CNTL, tmp); + + tmp = RREG32(target); + tmp = REG_SET_FIELD(tmp, CP_ME2_PIPE0_INT_CNTL, + GENERIC2_INT_ENABLE, 1); + WREG32(target, tmp); + } + break; + default: + BUG(); /* kiq only support GENERIC2_INT now */ + break; + } + return 0; +} + +static int gfx_v8_0_kiq_irq(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + struct amdgpu_iv_entry *entry) +{ + u8 me_id, pipe_id, queue_id; + struct amdgpu_ring *ring = (struct amdgpu_ring *)source->data; + + BUG_ON(!ring || (ring->funcs->type != AMDGPU_RING_TYPE_KIQ)); + + me_id = (entry->ring_id & 0x0c) >> 2; + pipe_id = (entry->ring_id & 0x03) >> 0; + queue_id = (entry->ring_id & 0x70) >> 4; + DRM_DEBUG("IH: CPC GENERIC2_INT, me:%d, pipe:%d, queue:%d\n", + me_id, pipe_id, queue_id); + + amdgpu_fence_process(ring); + return 0; +} + static const struct amd_ip_funcs gfx_v8_0_ip_funcs = { .name = "gfx_v8_0", .early_init = gfx_v8_0_early_init, @@ -6423,6 +7003,7 @@ static const struct amd_ip_funcs gfx_v8_0_ip_funcs = { .post_soft_reset = gfx_v8_0_post_soft_reset, .set_clockgating_state = gfx_v8_0_set_clockgating_state, .set_powergating_state = gfx_v8_0_set_powergating_state, + .get_clockgating_state = gfx_v8_0_get_clockgating_state, }; static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = { @@ -6440,7 +7021,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = { 7 + /* gfx_v8_0_ring_emit_pipeline_sync */ 128 + 19 + /* gfx_v8_0_ring_emit_vm_flush */ 2 + /* gfx_v8_ring_emit_sb */ - 3 + 4, /* gfx_v8_ring_emit_cntxcntl including vgt flush */ + 3 + 4 + 29, /* gfx_v8_ring_emit_cntxcntl including vgt flush/meta-data */ .emit_ib_size = 4, /* gfx_v8_0_ring_emit_ib_gfx */ .emit_ib = gfx_v8_0_ring_emit_ib_gfx, .emit_fence = gfx_v8_0_ring_emit_fence_gfx, @@ -6485,10 +7066,39 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_compute = { .pad_ib = amdgpu_ring_generic_pad_ib, }; +static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_kiq = { + .type = AMDGPU_RING_TYPE_KIQ, + .align_mask = 0xff, + .nop = PACKET3(PACKET3_NOP, 0x3FFF), + .get_rptr = gfx_v8_0_ring_get_rptr, + .get_wptr = gfx_v8_0_ring_get_wptr_compute, + .set_wptr = gfx_v8_0_ring_set_wptr_compute, + .emit_frame_size = + 20 + /* gfx_v8_0_ring_emit_gds_switch */ + 7 + /* gfx_v8_0_ring_emit_hdp_flush */ + 5 + /* gfx_v8_0_ring_emit_hdp_invalidate */ + 7 + /* gfx_v8_0_ring_emit_pipeline_sync */ + 17 + /* gfx_v8_0_ring_emit_vm_flush */ + 7 + 7 + 7, /* gfx_v8_0_ring_emit_fence_kiq x3 for user fence, vm fence */ + .emit_ib_size = 4, /* gfx_v8_0_ring_emit_ib_compute */ + .emit_ib = gfx_v8_0_ring_emit_ib_compute, + .emit_fence = gfx_v8_0_ring_emit_fence_kiq, + .emit_hdp_flush = gfx_v8_0_ring_emit_hdp_flush, + .emit_hdp_invalidate = gfx_v8_0_ring_emit_hdp_invalidate, + .test_ring = gfx_v8_0_ring_test_ring, + .test_ib = gfx_v8_0_ring_test_ib, + .insert_nop = amdgpu_ring_insert_nop, + .pad_ib = amdgpu_ring_generic_pad_ib, + .emit_rreg = gfx_v8_0_ring_emit_rreg, + .emit_wreg = gfx_v8_0_ring_emit_wreg, +}; + static void gfx_v8_0_set_ring_funcs(struct amdgpu_device *adev) { int i; + adev->gfx.kiq.ring.funcs = &gfx_v8_0_ring_funcs_kiq; + for (i = 0; i < adev->gfx.num_gfx_rings; i++) adev->gfx.gfx_ring[i].funcs = &gfx_v8_0_ring_funcs_gfx; @@ -6511,6 +7121,11 @@ static const struct amdgpu_irq_src_funcs gfx_v8_0_priv_inst_irq_funcs = { .process = gfx_v8_0_priv_inst_irq, }; +static const struct amdgpu_irq_src_funcs gfx_v8_0_kiq_irq_funcs = { + .set = gfx_v8_0_kiq_set_interrupt_state, + .process = gfx_v8_0_kiq_irq, +}; + static void gfx_v8_0_set_irq_funcs(struct amdgpu_device *adev) { adev->gfx.eop_irq.num_types = AMDGPU_CP_IRQ_LAST; @@ -6521,22 +7136,14 @@ static void gfx_v8_0_set_irq_funcs(struct amdgpu_device *adev) adev->gfx.priv_inst_irq.num_types = 1; adev->gfx.priv_inst_irq.funcs = &gfx_v8_0_priv_inst_irq_funcs; + + adev->gfx.kiq.irq.num_types = AMDGPU_CP_KIQ_IRQ_LAST; + adev->gfx.kiq.irq.funcs = &gfx_v8_0_kiq_irq_funcs; } static void gfx_v8_0_set_rlc_funcs(struct amdgpu_device *adev) { - switch (adev->asic_type) { - case CHIP_TOPAZ: - adev->gfx.rlc.funcs = &iceland_rlc_funcs; - break; - case CHIP_STONEY: - case CHIP_CARRIZO: - adev->gfx.rlc.funcs = &cz_rlc_funcs; - break; - default: - adev->gfx.rlc.funcs = &gfx_v8_0_nop_rlc_funcs; - break; - } + adev->gfx.rlc.funcs = &iceland_rlc_funcs; } static void gfx_v8_0_set_gds_init(struct amdgpu_device *adev) @@ -6653,3 +7260,62 @@ const struct amdgpu_ip_block_version gfx_v8_1_ip_block = .rev = 0, .funcs = &gfx_v8_0_ip_funcs, }; + +static void gfx_v8_0_ring_emit_ce_meta_init(struct amdgpu_ring *ring, uint64_t csa_addr) +{ + uint64_t ce_payload_addr; + int cnt_ce; + static union { + struct amdgpu_ce_ib_state regular; + struct amdgpu_ce_ib_state_chained_ib chained; + } ce_payload = {}; + + if (ring->adev->virt.chained_ib_support) { + ce_payload_addr = csa_addr + offsetof(struct amdgpu_gfx_meta_data_chained_ib, ce_payload); + cnt_ce = (sizeof(ce_payload.chained) >> 2) + 4 - 2; + } else { + ce_payload_addr = csa_addr + offsetof(struct amdgpu_gfx_meta_data, ce_payload); + cnt_ce = (sizeof(ce_payload.regular) >> 2) + 4 - 2; + } + + amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, cnt_ce)); + amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(2) | + WRITE_DATA_DST_SEL(8) | + WR_CONFIRM) | + WRITE_DATA_CACHE_POLICY(0)); + amdgpu_ring_write(ring, lower_32_bits(ce_payload_addr)); + amdgpu_ring_write(ring, upper_32_bits(ce_payload_addr)); + amdgpu_ring_write_multiple(ring, (void *)&ce_payload, cnt_ce - 2); +} + +static void gfx_v8_0_ring_emit_de_meta_init(struct amdgpu_ring *ring, uint64_t csa_addr) +{ + uint64_t de_payload_addr, gds_addr; + int cnt_de; + static union { + struct amdgpu_de_ib_state regular; + struct amdgpu_de_ib_state_chained_ib chained; + } de_payload = {}; + + gds_addr = csa_addr + 4096; + if (ring->adev->virt.chained_ib_support) { + de_payload.chained.gds_backup_addrlo = lower_32_bits(gds_addr); + de_payload.chained.gds_backup_addrhi = upper_32_bits(gds_addr); + de_payload_addr = csa_addr + offsetof(struct amdgpu_gfx_meta_data_chained_ib, de_payload); + cnt_de = (sizeof(de_payload.chained) >> 2) + 4 - 2; + } else { + de_payload.regular.gds_backup_addrlo = lower_32_bits(gds_addr); + de_payload.regular.gds_backup_addrhi = upper_32_bits(gds_addr); + de_payload_addr = csa_addr + offsetof(struct amdgpu_gfx_meta_data, de_payload); + cnt_de = (sizeof(de_payload.regular) >> 2) + 4 - 2; + } + + amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, cnt_de)); + amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) | + WRITE_DATA_DST_SEL(8) | + WR_CONFIRM) | + WRITE_DATA_CACHE_POLICY(0)); + amdgpu_ring_write(ring, lower_32_bits(de_payload_addr)); + amdgpu_ring_write(ring, upper_32_bits(de_payload_addr)); + amdgpu_ring_write_multiple(ring, (void *)&de_payload, cnt_de - 2); +} diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index 273b16fb9459..8d05e0c4e3d7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -375,9 +375,16 @@ static int gmc_v7_0_mc_init(struct amdgpu_device *adev) /* size in MB on si */ adev->mc.mc_vram_size = RREG32(mmCONFIG_MEMSIZE) * 1024ULL * 1024ULL; adev->mc.real_vram_size = RREG32(mmCONFIG_MEMSIZE) * 1024ULL * 1024ULL; - adev->mc.visible_vram_size = adev->mc.aper_size; + +#ifdef CONFIG_X86_64 + if (adev->flags & AMD_IS_APU) { + adev->mc.aper_base = ((u64)RREG32(mmMC_VM_FB_OFFSET)) << 22; + adev->mc.aper_size = adev->mc.real_vram_size; + } +#endif /* In case the PCI BAR is larger than the actual amount of vram */ + adev->mc.visible_vram_size = adev->mc.aper_size; if (adev->mc.visible_vram_size > adev->mc.real_vram_size) adev->mc.visible_vram_size = adev->mc.real_vram_size; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index 476bc9f1954b..7669b3259f35 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -467,9 +467,16 @@ static int gmc_v8_0_mc_init(struct amdgpu_device *adev) /* size in MB on si */ adev->mc.mc_vram_size = RREG32(mmCONFIG_MEMSIZE) * 1024ULL * 1024ULL; adev->mc.real_vram_size = RREG32(mmCONFIG_MEMSIZE) * 1024ULL * 1024ULL; - adev->mc.visible_vram_size = adev->mc.aper_size; + +#ifdef CONFIG_X86_64 + if (adev->flags & AMD_IS_APU) { + adev->mc.aper_base = ((u64)RREG32(mmMC_VM_FB_OFFSET)) << 22; + adev->mc.aper_size = adev->mc.real_vram_size; + } +#endif /* In case the PCI BAR is larger than the actual amount of vram */ + adev->mc.visible_vram_size = adev->mc.aper_size; if (adev->mc.visible_vram_size > adev->mc.real_vram_size) adev->mc.visible_vram_size = adev->mc.real_vram_size; @@ -1439,6 +1446,21 @@ static int gmc_v8_0_set_powergating_state(void *handle, return 0; } +static void gmc_v8_0_get_clockgating_state(void *handle, u32 *flags) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int data; + + /* AMD_CG_SUPPORT_MC_MGCG */ + data = RREG32(mmMC_HUB_MISC_HUB_CG); + if (data & MC_HUB_MISC_HUB_CG__ENABLE_MASK) + *flags |= AMD_CG_SUPPORT_MC_MGCG; + + /* AMD_CG_SUPPORT_MC_LS */ + if (data & MC_HUB_MISC_HUB_CG__MEM_LS_ENABLE_MASK) + *flags |= AMD_CG_SUPPORT_MC_LS; +} + static const struct amd_ip_funcs gmc_v8_0_ip_funcs = { .name = "gmc_v8_0", .early_init = gmc_v8_0_early_init, @@ -1457,6 +1479,7 @@ static const struct amd_ip_funcs gmc_v8_0_ip_funcs = { .post_soft_reset = gmc_v8_0_post_soft_reset, .set_clockgating_state = gmc_v8_0_set_clockgating_state, .set_powergating_state = gmc_v8_0_set_powergating_state, + .get_clockgating_state = gmc_v8_0_get_clockgating_state, }; static const struct amdgpu_gart_funcs gmc_v8_0_gart_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c index 5a1bc358bcb1..f5a343cb0010 100644 --- a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c @@ -1230,6 +1230,7 @@ static void kv_update_current_ps(struct amdgpu_device *adev, pi->current_rps = *rps; pi->current_ps = *new_ps; pi->current_rps.ps_priv = &pi->current_ps; + adev->pm.dpm.current_ps = &pi->current_rps; } static void kv_update_requested_ps(struct amdgpu_device *adev, @@ -1241,6 +1242,7 @@ static void kv_update_requested_ps(struct amdgpu_device *adev, pi->requested_rps = *rps; pi->requested_ps = *new_ps; pi->requested_rps.ps_priv = &pi->requested_ps; + adev->pm.dpm.requested_ps = &pi->requested_rps; } static void kv_dpm_enable_bapm(struct amdgpu_device *adev, bool enable) @@ -1548,11 +1550,6 @@ static int kv_update_vce_dpm(struct amdgpu_device *adev, if (amdgpu_new_state->evclk > 0 && amdgpu_current_state->evclk == 0) { kv_dpm_powergate_vce(adev, false); - /* turn the clocks on when encoding */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_UNGATE); - if (ret) - return ret; if (pi->caps_stable_p_state) pi->vce_boot_level = table->count - 1; else @@ -1571,15 +1568,9 @@ static int kv_update_vce_dpm(struct amdgpu_device *adev, amdgpu_kv_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_VCEDPM_SetEnabledMask, (1 << pi->vce_boot_level)); - kv_enable_vce_dpm(adev, true); } else if (amdgpu_new_state->evclk == 0 && amdgpu_current_state->evclk > 0) { kv_enable_vce_dpm(adev, false); - /* turn the clocks off when not encoding */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_GATE); - if (ret) - return ret; kv_dpm_powergate_vce(adev, true); } @@ -1686,70 +1677,44 @@ static void kv_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate) struct kv_power_info *pi = kv_get_pi(adev); int ret; - if (pi->uvd_power_gated == gate) - return; - pi->uvd_power_gated = gate; if (gate) { - if (pi->caps_uvd_pg) { - /* disable clockgating so we can properly shut down the block */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_UNGATE); - /* shutdown the UVD block */ - ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_PG_STATE_GATE); - /* XXX: check for errors */ - } + /* stop the UVD block */ + ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_GATE); kv_update_uvd_dpm(adev, gate); if (pi->caps_uvd_pg) /* power off the UVD block */ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_UVDPowerOFF); } else { - if (pi->caps_uvd_pg) { + if (pi->caps_uvd_pg) /* power on the UVD block */ amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_UVDPowerON); /* re-init the UVD block */ - ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_PG_STATE_UNGATE); - /* enable clockgating. hw will dynamically gate/ungate clocks on the fly */ - ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_GATE); - /* XXX: check for errors */ - } kv_update_uvd_dpm(adev, gate); + + ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD, + AMD_PG_STATE_UNGATE); } } static void kv_dpm_powergate_vce(struct amdgpu_device *adev, bool gate) { struct kv_power_info *pi = kv_get_pi(adev); - int ret; if (pi->vce_power_gated == gate) return; pi->vce_power_gated = gate; - if (gate) { - if (pi->caps_vce_pg) { - /* shutdown the VCE block */ - ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_PG_STATE_GATE); - /* XXX: check for errors */ - /* power off the VCE block */ - amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_VCEPowerOFF); - } - } else { - if (pi->caps_vce_pg) { - /* power on the VCE block */ - amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_VCEPowerON); - /* re-init the VCE block */ - ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE, - AMD_PG_STATE_UNGATE); - /* XXX: check for errors */ - } - } + if (!pi->caps_vce_pg) + return; + + if (gate) + amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_VCEPowerOFF); + else + amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_VCEPowerON); } static void kv_dpm_powergate_samu(struct amdgpu_device *adev, bool gate) @@ -1904,19 +1869,19 @@ static int kv_enable_nb_dpm(struct amdgpu_device *adev, } static int kv_dpm_force_performance_level(struct amdgpu_device *adev, - enum amdgpu_dpm_forced_level level) + enum amd_dpm_forced_level level) { int ret; - if (level == AMDGPU_DPM_FORCED_LEVEL_HIGH) { + if (level == AMD_DPM_FORCED_LEVEL_HIGH) { ret = kv_force_dpm_highest(adev); if (ret) return ret; - } else if (level == AMDGPU_DPM_FORCED_LEVEL_LOW) { + } else if (level == AMD_DPM_FORCED_LEVEL_LOW) { ret = kv_force_dpm_lowest(adev); if (ret) return ret; - } else if (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) { + } else if (level == AMD_DPM_FORCED_LEVEL_AUTO) { ret = kv_unforce_levels(adev); if (ret) return ret; @@ -3007,8 +2972,6 @@ static int kv_dpm_late_init(void *handle) kv_dpm_powergate_acp(adev, true); kv_dpm_powergate_samu(adev, true); - kv_dpm_powergate_vce(adev, true); - kv_dpm_powergate_uvd(adev, true); return 0; } @@ -3029,7 +2992,7 @@ static int kv_dpm_sw_init(void *handle) /* default to balanced state */ adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; - adev->pm.dpm.forced_level = AMDGPU_DPM_FORCED_LEVEL_AUTO; + adev->pm.dpm.forced_level = AMD_DPM_FORCED_LEVEL_AUTO; adev->pm.default_sclk = adev->clock.default_sclk; adev->pm.default_mclk = adev->clock.default_mclk; adev->pm.current_sclk = adev->clock.default_sclk; @@ -3078,6 +3041,9 @@ static int kv_dpm_hw_init(void *handle) int ret; struct amdgpu_device *adev = (struct amdgpu_device *)handle; + if (!amdgpu_dpm) + return 0; + mutex_lock(&adev->pm.mutex); kv_dpm_setup_asic(adev); ret = kv_dpm_enable(adev); @@ -3245,15 +3211,52 @@ static int kv_dpm_set_powergating_state(void *handle, return 0; } +static inline bool kv_are_power_levels_equal(const struct kv_pl *kv_cpl1, + const struct kv_pl *kv_cpl2) +{ + return ((kv_cpl1->sclk == kv_cpl2->sclk) && + (kv_cpl1->vddc_index == kv_cpl2->vddc_index) && + (kv_cpl1->ds_divider_index == kv_cpl2->ds_divider_index) && + (kv_cpl1->force_nbp_state == kv_cpl2->force_nbp_state)); +} + static int kv_check_state_equal(struct amdgpu_device *adev, struct amdgpu_ps *cps, struct amdgpu_ps *rps, bool *equal) { - if (equal == NULL) + struct kv_ps *kv_cps; + struct kv_ps *kv_rps; + int i; + + if (adev == NULL || cps == NULL || rps == NULL || equal == NULL) return -EINVAL; - *equal = false; + kv_cps = kv_get_ps(cps); + kv_rps = kv_get_ps(rps); + + if (kv_cps == NULL) { + *equal = false; + return 0; + } + + if (kv_cps->num_levels != kv_rps->num_levels) { + *equal = false; + return 0; + } + + for (i = 0; i < kv_cps->num_levels; i++) { + if (!kv_are_power_levels_equal(&(kv_cps->levels[i]), + &(kv_rps->levels[i]))) { + *equal = false; + return 0; + } + } + + /* If all performance levels are the same try to use the UVD clocks to break the tie.*/ + *equal = ((cps->vclk == rps->vclk) && (cps->dclk == rps->dclk)); + *equal &= ((cps->evclk == rps->evclk) && (cps->ecclk == rps->ecclk)); + return 0; } @@ -3307,12 +3310,3 @@ static void kv_dpm_set_irq_funcs(struct amdgpu_device *adev) adev->pm.dpm.thermal.irq.num_types = AMDGPU_THERMAL_IRQ_LAST; adev->pm.dpm.thermal.irq.funcs = &kv_dpm_irq_funcs; } - -const struct amdgpu_ip_block_version kv_dpm_ip_block = -{ - .type = AMD_IP_BLOCK_TYPE_SMC, - .major = 7, - .minor = 0, - .rev = 0, - .funcs = &kv_dpm_ip_funcs, -}; diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_vi.c b/drivers/gpu/drm/amd/amdgpu/mxgpu_vi.c new file mode 100644 index 000000000000..d2622b6f49fa --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_vi.c @@ -0,0 +1,592 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Xiangliang.Yu@amd.com + */ + +#include "amdgpu.h" +#include "vi.h" +#include "bif/bif_5_0_d.h" +#include "bif/bif_5_0_sh_mask.h" +#include "vid.h" +#include "gca/gfx_8_0_d.h" +#include "gca/gfx_8_0_sh_mask.h" +#include "gmc_v8_0.h" +#include "gfx_v8_0.h" +#include "sdma_v3_0.h" +#include "tonga_ih.h" +#include "gmc/gmc_8_2_d.h" +#include "gmc/gmc_8_2_sh_mask.h" +#include "oss/oss_3_0_d.h" +#include "oss/oss_3_0_sh_mask.h" +#include "gca/gfx_8_0_sh_mask.h" +#include "dce/dce_10_0_d.h" +#include "dce/dce_10_0_sh_mask.h" +#include "smu/smu_7_1_3_d.h" +#include "mxgpu_vi.h" + +/* VI golden setting */ +static const u32 xgpu_fiji_mgcg_cgcg_init[] = { + mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xffffffff, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CPC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CPF_CLK_CTRL, 0xffffffff, 0x40000100, + mmCGTT_DRM_CLK_CTRL0, 0xffffffff, 0x00600100, + mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_WD_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL4, 0xffffffff, 0x00000100, + mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100, + mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100, + mmTA_CGTT_CTRL, 0xffffffff, 0x00000100, + mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTD_CGTT_CTRL, 0xffffffff, 0x00000100, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96e00200, + mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100, + mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003c, + mmPCIE_INDEX, 0xffffffff, 0x0140001c, + mmPCIE_DATA, 0x000f0000, 0x00000000, + mmSMC_IND_INDEX_4, 0xffffffff, 0xC060000C, + mmSMC_IND_DATA_4, 0xc0000fff, 0x00000100, + mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100, + mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000, + mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104, + mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100, + mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104, + mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001, + mmSDMA0_CLK_CTRL, 0xff000ff0, 0x00000100, + mmSDMA1_CLK_CTRL, 0xff000ff0, 0x00000100, +}; + +static const u32 xgpu_fiji_golden_settings_a10[] = { + mmCB_HW_CONTROL_3, 0x000001ff, 0x00000040, + mmDB_DEBUG2, 0xf00fffff, 0x00000400, + mmDCI_CLK_CNTL, 0x00000080, 0x00000000, + mmFBC_DEBUG_COMP, 0x000000f0, 0x00000070, + mmFBC_MISC, 0x1f311fff, 0x12300000, + mmHDMI_CONTROL, 0x31000111, 0x00000011, + mmPA_SC_ENHANCE, 0xffffffff, 0x20000001, + mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmSDMA0_CHICKEN_BITS, 0xfc910007, 0x00810007, + mmSDMA0_GFX_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA0_RLC0_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA0_RLC1_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA1_CHICKEN_BITS, 0xfc910007, 0x00810007, + mmSDMA1_GFX_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA1_RLC0_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA1_RLC1_IB_CNTL, 0x800f0111, 0x00000100, + mmSQ_RANDOM_WAVE_PRI, 0x001fffff, 0x000006fd, + mmTA_CNTL_AUX, 0x000f000f, 0x000b0000, + mmTCC_EXE_DISABLE, 0x00000002, 0x00000002, + mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000ff, + mmVGT_RESET_DEBUG, 0x00000004, 0x00000004, + mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff, +}; + +static const u32 xgpu_fiji_golden_common_all[] = { + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmPA_SC_RASTER_CONFIG, 0xffffffff, 0x3a00161a, + mmPA_SC_RASTER_CONFIG_1, 0xffffffff, 0x0000002e, + mmGB_ADDR_CONFIG, 0xffffffff, 0x22011003, + mmSPI_RESOURCE_RESERVE_CU_0, 0xffffffff, 0x00000800, + mmSPI_RESOURCE_RESERVE_CU_1, 0xffffffff, 0x00000800, + mmSPI_RESOURCE_RESERVE_EN_CU_0, 0xffffffff, 0x00007FBF, + mmSPI_RESOURCE_RESERVE_EN_CU_1, 0xffffffff, 0x00007FAF, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmSPI_CONFIG_CNTL_1, 0x0000000f, 0x00000009, +}; + +static const u32 xgpu_tonga_mgcg_cgcg_init[] = { + mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xffffffff, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CPC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CPF_CLK_CTRL, 0xffffffff, 0x40000100, + mmCGTT_DRM_CLK_CTRL0, 0xffffffff, 0x00600100, + mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_WD_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL4, 0xffffffff, 0x00000100, + mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100, + mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100, + mmTA_CGTT_CTRL, 0xffffffff, 0x00000100, + mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTD_CGTT_CTRL, 0xffffffff, 0x00000100, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCGTS_CU0_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU0_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU0_TA_SQC_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU0_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU0_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_CU1_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU1_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU1_TA_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU1_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU1_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_CU2_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU2_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU2_TA_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU2_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU2_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_CU3_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU3_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU3_TA_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU3_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU3_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_CU4_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU4_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU4_TA_SQC_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU4_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU4_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_CU5_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU5_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU5_TA_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU5_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU5_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_CU6_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU6_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU6_TA_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU6_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU6_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_CU7_SP0_CTRL_REG, 0xffffffff, 0x00010000, + mmCGTS_CU7_LDS_SQ_CTRL_REG, 0xffffffff, 0x00030002, + mmCGTS_CU7_TA_CTRL_REG, 0xffffffff, 0x00040007, + mmCGTS_CU7_SP1_CTRL_REG, 0xffffffff, 0x00060005, + mmCGTS_CU7_TD_TCP_CTRL_REG, 0xffffffff, 0x00090008, + mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96e00200, + mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100, + mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003c, + mmPCIE_INDEX, 0xffffffff, 0x0140001c, + mmPCIE_DATA, 0x000f0000, 0x00000000, + mmSMC_IND_INDEX_4, 0xffffffff, 0xC060000C, + mmSMC_IND_DATA_4, 0xc0000fff, 0x00000100, + mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100, + mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000, + mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104, + mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100, + mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104, + mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001, + mmSDMA0_CLK_CTRL, 0xff000ff0, 0x00000100, + mmSDMA1_CLK_CTRL, 0xff000ff0, 0x00000100, +}; + +static const u32 xgpu_tonga_golden_settings_a11[] = { + mmCB_HW_CONTROL, 0xfffdf3cf, 0x00007208, + mmCB_HW_CONTROL_3, 0x00000040, 0x00000040, + mmDB_DEBUG2, 0xf00fffff, 0x00000400, + mmDCI_CLK_CNTL, 0x00000080, 0x00000000, + mmFBC_DEBUG_COMP, 0x000000f0, 0x00000070, + mmFBC_MISC, 0x1f311fff, 0x12300000, + mmGB_GPU_ID, 0x0000000f, 0x00000000, + mmHDMI_CONTROL, 0x31000111, 0x00000011, + mmMC_ARB_WTM_GRPWT_RD, 0x00000003, 0x00000000, + mmMC_HUB_RDREQ_DMIF_LIMIT, 0x0000007f, 0x00000028, + mmMC_HUB_WDP_UMC, 0x00007fb6, 0x00000991, + mmPA_SC_ENHANCE, 0xffffffff, 0x20000001, + mmPA_SC_FIFO_DEPTH_CNTL, 0x000003ff, 0x000000fc, + mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmRLC_CGCG_CGLS_CTRL, 0x00000003, 0x0000003c, + mmSDMA0_CHICKEN_BITS, 0xfc910007, 0x00810007, + mmSDMA0_CLK_CTRL, 0xff000fff, 0x00000000, + mmSDMA0_GFX_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA0_RLC0_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA0_RLC1_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA1_CHICKEN_BITS, 0xfc910007, 0x00810007, + mmSDMA1_CLK_CTRL, 0xff000fff, 0x00000000, + mmSDMA1_GFX_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA1_RLC0_IB_CNTL, 0x800f0111, 0x00000100, + mmSDMA1_RLC1_IB_CNTL, 0x800f0111, 0x00000100, + mmSQ_RANDOM_WAVE_PRI, 0x001fffff, 0x000006fd, + mmTA_CNTL_AUX, 0x000f000f, 0x000b0000, + mmTCC_CTRL, 0x00100000, 0xf31fff7f, + mmTCC_EXE_DISABLE, 0x00000002, 0x00000002, + mmTCP_ADDR_CONFIG, 0x000003ff, 0x000002fb, + mmTCP_CHAN_STEER_HI, 0xffffffff, 0x0000543b, + mmTCP_CHAN_STEER_LO, 0xffffffff, 0xa9210876, + mmVGT_RESET_DEBUG, 0x00000004, 0x00000004, + mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff, +}; + +static const u32 xgpu_tonga_golden_common_all[] = { + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmPA_SC_RASTER_CONFIG, 0xffffffff, 0x16000012, + mmPA_SC_RASTER_CONFIG_1, 0xffffffff, 0x0000002A, + mmGB_ADDR_CONFIG, 0xffffffff, 0x22011002, + mmSPI_RESOURCE_RESERVE_CU_0, 0xffffffff, 0x00000800, + mmSPI_RESOURCE_RESERVE_CU_1, 0xffffffff, 0x00000800, + mmSPI_RESOURCE_RESERVE_EN_CU_0, 0xffffffff, 0x00007FBF, +}; + +void xgpu_vi_init_golden_registers(struct amdgpu_device *adev) +{ + switch (adev->asic_type) { + case CHIP_FIJI: + amdgpu_program_register_sequence(adev, + xgpu_fiji_mgcg_cgcg_init, + (const u32)ARRAY_SIZE( + xgpu_fiji_mgcg_cgcg_init)); + amdgpu_program_register_sequence(adev, + xgpu_fiji_golden_settings_a10, + (const u32)ARRAY_SIZE( + xgpu_fiji_golden_settings_a10)); + amdgpu_program_register_sequence(adev, + xgpu_fiji_golden_common_all, + (const u32)ARRAY_SIZE( + xgpu_fiji_golden_common_all)); + break; + case CHIP_TONGA: + amdgpu_program_register_sequence(adev, + xgpu_tonga_mgcg_cgcg_init, + (const u32)ARRAY_SIZE( + xgpu_tonga_mgcg_cgcg_init)); + amdgpu_program_register_sequence(adev, + xgpu_tonga_golden_settings_a11, + (const u32)ARRAY_SIZE( + xgpu_tonga_golden_settings_a11)); + amdgpu_program_register_sequence(adev, + xgpu_tonga_golden_common_all, + (const u32)ARRAY_SIZE( + xgpu_tonga_golden_common_all)); + break; + default: + BUG_ON("Doesn't support chip type.\n"); + break; + } +} + +/* + * Mailbox communication between GPU hypervisor and VFs + */ +static void xgpu_vi_mailbox_send_ack(struct amdgpu_device *adev) +{ + u32 reg; + + reg = RREG32(mmMAILBOX_CONTROL); + reg = REG_SET_FIELD(reg, MAILBOX_CONTROL, RCV_MSG_ACK, 1); + WREG32(mmMAILBOX_CONTROL, reg); +} + +static void xgpu_vi_mailbox_set_valid(struct amdgpu_device *adev, bool val) +{ + u32 reg; + + reg = RREG32(mmMAILBOX_CONTROL); + reg = REG_SET_FIELD(reg, MAILBOX_CONTROL, + TRN_MSG_VALID, val ? 1 : 0); + WREG32(mmMAILBOX_CONTROL, reg); +} + +static void xgpu_vi_mailbox_trans_msg(struct amdgpu_device *adev, + enum idh_event event) +{ + u32 reg; + + reg = RREG32(mmMAILBOX_MSGBUF_TRN_DW0); + reg = REG_SET_FIELD(reg, MAILBOX_MSGBUF_TRN_DW0, + MSGBUF_DATA, event); + WREG32(mmMAILBOX_MSGBUF_TRN_DW0, reg); + + xgpu_vi_mailbox_set_valid(adev, true); +} + +static int xgpu_vi_mailbox_rcv_msg(struct amdgpu_device *adev, + enum idh_event event) +{ + u32 reg; + + reg = RREG32(mmMAILBOX_MSGBUF_RCV_DW0); + if (reg != event) + return -ENOENT; + + /* send ack to PF */ + xgpu_vi_mailbox_send_ack(adev); + + return 0; +} + +static int xgpu_vi_poll_ack(struct amdgpu_device *adev) +{ + int r = 0, timeout = VI_MAILBOX_TIMEDOUT; + u32 mask = REG_FIELD_MASK(MAILBOX_CONTROL, TRN_MSG_ACK); + u32 reg; + + reg = RREG32(mmMAILBOX_CONTROL); + while (!(reg & mask)) { + if (timeout <= 0) { + pr_err("Doesn't get ack from pf.\n"); + r = -ETIME; + break; + } + msleep(1); + timeout -= 1; + + reg = RREG32(mmMAILBOX_CONTROL); + } + + return r; +} + +static int xgpu_vi_poll_msg(struct amdgpu_device *adev, enum idh_event event) +{ + int r = 0, timeout = VI_MAILBOX_TIMEDOUT; + + r = xgpu_vi_mailbox_rcv_msg(adev, event); + while (r) { + if (timeout <= 0) { + pr_err("Doesn't get ack from pf.\n"); + r = -ETIME; + break; + } + msleep(1); + timeout -= 1; + + r = xgpu_vi_mailbox_rcv_msg(adev, event); + } + + return r; +} + +static int xgpu_vi_send_access_requests(struct amdgpu_device *adev, + enum idh_request request) +{ + int r; + + xgpu_vi_mailbox_trans_msg(adev, request); + + /* start to poll ack */ + r = xgpu_vi_poll_ack(adev); + if (r) + return r; + + xgpu_vi_mailbox_set_valid(adev, false); + + /* start to check msg if request is idh_req_gpu_init_access */ + if (request == IDH_REQ_GPU_INIT_ACCESS) { + r = xgpu_vi_poll_msg(adev, IDH_READY_TO_ACCESS_GPU); + if (r) + return r; + } + + return 0; +} + +static int xgpu_vi_request_reset(struct amdgpu_device *adev) +{ + return xgpu_vi_send_access_requests(adev, IDH_REQ_GPU_RESET_ACCESS); +} + +static int xgpu_vi_request_full_gpu_access(struct amdgpu_device *adev, + bool init) +{ + enum idh_event event; + + event = init ? IDH_REQ_GPU_INIT_ACCESS : IDH_REQ_GPU_FINI_ACCESS; + return xgpu_vi_send_access_requests(adev, event); +} + +static int xgpu_vi_release_full_gpu_access(struct amdgpu_device *adev, + bool init) +{ + enum idh_event event; + int r = 0; + + event = init ? IDH_REL_GPU_INIT_ACCESS : IDH_REL_GPU_FINI_ACCESS; + r = xgpu_vi_send_access_requests(adev, event); + + return r; +} + +/* add support mailbox interrupts */ +static int xgpu_vi_mailbox_ack_irq(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + struct amdgpu_iv_entry *entry) +{ + DRM_DEBUG("get ack intr and do nothing.\n"); + return 0; +} + +static int xgpu_vi_set_mailbox_ack_irq(struct amdgpu_device *adev, + struct amdgpu_irq_src *src, + unsigned type, + enum amdgpu_interrupt_state state) +{ + u32 tmp = RREG32(mmMAILBOX_INT_CNTL); + + tmp = REG_SET_FIELD(tmp, MAILBOX_INT_CNTL, ACK_INT_EN, + (state == AMDGPU_IRQ_STATE_ENABLE) ? 1 : 0); + WREG32(mmMAILBOX_INT_CNTL, tmp); + + return 0; +} + +static void xgpu_vi_mailbox_flr_work(struct work_struct *work) +{ + struct amdgpu_virt *virt = container_of(work, + struct amdgpu_virt, flr_work.work); + struct amdgpu_device *adev = container_of(virt, + struct amdgpu_device, virt); + int r = 0; + + r = xgpu_vi_poll_msg(adev, IDH_FLR_NOTIFICATION_CMPL); + if (r) + DRM_ERROR("failed to get flr cmpl msg from hypervior.\n"); + + /* TODO: need to restore gfx states */ +} + +static int xgpu_vi_set_mailbox_rcv_irq(struct amdgpu_device *adev, + struct amdgpu_irq_src *src, + unsigned type, + enum amdgpu_interrupt_state state) +{ + u32 tmp = RREG32(mmMAILBOX_INT_CNTL); + + tmp = REG_SET_FIELD(tmp, MAILBOX_INT_CNTL, VALID_INT_EN, + (state == AMDGPU_IRQ_STATE_ENABLE) ? 1 : 0); + WREG32(mmMAILBOX_INT_CNTL, tmp); + + return 0; +} + +static int xgpu_vi_mailbox_rcv_irq(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + struct amdgpu_iv_entry *entry) +{ + int r; + + adev->virt.caps &= ~AMDGPU_SRIOV_CAPS_RUNTIME; + r = xgpu_vi_mailbox_rcv_msg(adev, IDH_FLR_NOTIFICATION); + /* do nothing for other msg */ + if (r) + return 0; + + /* TODO: need to save gfx states */ + schedule_delayed_work(&adev->virt.flr_work, + msecs_to_jiffies(VI_MAILBOX_RESET_TIME)); + + return 0; +} + +static const struct amdgpu_irq_src_funcs xgpu_vi_mailbox_ack_irq_funcs = { + .set = xgpu_vi_set_mailbox_ack_irq, + .process = xgpu_vi_mailbox_ack_irq, +}; + +static const struct amdgpu_irq_src_funcs xgpu_vi_mailbox_rcv_irq_funcs = { + .set = xgpu_vi_set_mailbox_rcv_irq, + .process = xgpu_vi_mailbox_rcv_irq, +}; + +void xgpu_vi_mailbox_set_irq_funcs(struct amdgpu_device *adev) +{ + adev->virt.ack_irq.num_types = 1; + adev->virt.ack_irq.funcs = &xgpu_vi_mailbox_ack_irq_funcs; + adev->virt.rcv_irq.num_types = 1; + adev->virt.rcv_irq.funcs = &xgpu_vi_mailbox_rcv_irq_funcs; +} + +int xgpu_vi_mailbox_add_irq_id(struct amdgpu_device *adev) +{ + int r; + + r = amdgpu_irq_add_id(adev, 135, &adev->virt.rcv_irq); + if (r) + return r; + + r = amdgpu_irq_add_id(adev, 138, &adev->virt.ack_irq); + if (r) { + amdgpu_irq_put(adev, &adev->virt.rcv_irq, 0); + return r; + } + + return 0; +} + +int xgpu_vi_mailbox_get_irq(struct amdgpu_device *adev) +{ + int r; + + r = amdgpu_irq_get(adev, &adev->virt.rcv_irq, 0); + if (r) + return r; + r = amdgpu_irq_get(adev, &adev->virt.ack_irq, 0); + if (r) { + amdgpu_irq_put(adev, &adev->virt.rcv_irq, 0); + return r; + } + + INIT_DELAYED_WORK(&adev->virt.flr_work, xgpu_vi_mailbox_flr_work); + + return 0; +} + +void xgpu_vi_mailbox_put_irq(struct amdgpu_device *adev) +{ + cancel_delayed_work_sync(&adev->virt.flr_work); + amdgpu_irq_put(adev, &adev->virt.ack_irq, 0); + amdgpu_irq_put(adev, &adev->virt.rcv_irq, 0); +} + +const struct amdgpu_virt_ops xgpu_vi_virt_ops = { + .req_full_gpu = xgpu_vi_request_full_gpu_access, + .rel_full_gpu = xgpu_vi_release_full_gpu_access, + .reset_gpu = xgpu_vi_request_reset, +}; diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_vi.h b/drivers/gpu/drm/amd/amdgpu/mxgpu_vi.h new file mode 100644 index 000000000000..fd6216efd2b0 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_vi.h @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __MXGPU_VI_H__ +#define __MXGPU_VI_H__ + +#define VI_MAILBOX_TIMEDOUT 150 +#define VI_MAILBOX_RESET_TIME 12 + +/* VI mailbox messages request */ +enum idh_request { + IDH_REQ_GPU_INIT_ACCESS = 1, + IDH_REL_GPU_INIT_ACCESS, + IDH_REQ_GPU_FINI_ACCESS, + IDH_REL_GPU_FINI_ACCESS, + IDH_REQ_GPU_RESET_ACCESS +}; + +/* VI mailbox messages data */ +enum idh_event { + IDH_CLR_MSG_BUF = 0, + IDH_READY_TO_ACCESS_GPU, + IDH_FLR_NOTIFICATION, + IDH_FLR_NOTIFICATION_CMPL, + IDH_EVENT_MAX +}; + +extern const struct amdgpu_virt_ops xgpu_vi_virt_ops; + +void xgpu_vi_init_golden_registers(struct amdgpu_device *adev); +void xgpu_vi_mailbox_set_irq_funcs(struct amdgpu_device *adev); +int xgpu_vi_mailbox_add_irq_id(struct amdgpu_device *adev); +int xgpu_vi_mailbox_get_irq(struct amdgpu_device *adev); +void xgpu_vi_mailbox_put_irq(struct amdgpu_device *adev); + +#endif diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c index fbe74a33899c..896be64b7013 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c @@ -701,7 +701,7 @@ static int sdma_v2_4_ring_test_ib(struct amdgpu_ring *ring, long timeout) ib.ptr[7] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP); ib.length_dw = 8; - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) goto err1; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 034ace79ed49..31375bdde6f1 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -910,7 +910,7 @@ static int sdma_v3_0_ring_test_ib(struct amdgpu_ring *ring, long timeout) ib.ptr[7] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP); ib.length_dw = 8; - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) goto err1; @@ -1533,6 +1533,22 @@ static int sdma_v3_0_set_powergating_state(void *handle, return 0; } +static void sdma_v3_0_get_clockgating_state(void *handle, u32 *flags) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int data; + + /* AMD_CG_SUPPORT_SDMA_MGCG */ + data = RREG32(mmSDMA0_CLK_CTRL + sdma_offsets[0]); + if (!(data & SDMA0_CLK_CTRL__SOFT_OVERRIDE0_MASK)) + *flags |= AMD_CG_SUPPORT_SDMA_MGCG; + + /* AMD_CG_SUPPORT_SDMA_LS */ + data = RREG32(mmSDMA0_POWER_CNTL + sdma_offsets[0]); + if (data & SDMA0_POWER_CNTL__MEM_POWER_OVERRIDE_MASK) + *flags |= AMD_CG_SUPPORT_SDMA_LS; +} + static const struct amd_ip_funcs sdma_v3_0_ip_funcs = { .name = "sdma_v3_0", .early_init = sdma_v3_0_early_init, @@ -1551,6 +1567,7 @@ static const struct amd_ip_funcs sdma_v3_0_ip_funcs = { .soft_reset = sdma_v3_0_soft_reset, .set_clockgating_state = sdma_v3_0_set_clockgating_state, .set_powergating_state = sdma_v3_0_set_powergating_state, + .get_clockgating_state = sdma_v3_0_get_clockgating_state, }; static const struct amdgpu_ring_funcs sdma_v3_0_ring_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c index c46b0159007d..b71e3faa40db 100644 --- a/drivers/gpu/drm/amd/amdgpu/si.c +++ b/drivers/gpu/drm/amd/amdgpu/si.c @@ -32,7 +32,7 @@ #include "amdgpu_vce.h" #include "atom.h" #include "amdgpu_powerplay.h" -#include "si/sid.h" +#include "sid.h" #include "si_ih.h" #include "gfx_v6_0.h" #include "gmc_v6_0.h" @@ -40,337 +40,343 @@ #include "dce_v6_0.h" #include "si.h" #include "dce_virtual.h" +#include "gca/gfx_6_0_d.h" +#include "oss/oss_1_0_d.h" +#include "gmc/gmc_6_0_d.h" +#include "dce/dce_6_0_d.h" +#include "uvd/uvd_4_0_d.h" static const u32 tahiti_golden_registers[] = { - 0x17bc, 0x00000030, 0x00000011, - 0x2684, 0x00010000, 0x00018208, - 0x260c, 0xffffffff, 0x00000000, - 0x260d, 0xf00fffff, 0x00000400, - 0x260e, 0x0002021c, 0x00020200, - 0x031e, 0x00000080, 0x00000000, + mmAZALIA_SCLK_CONTROL, 0x00000030, 0x00000011, + mmCB_HW_CONTROL, 0x00010000, 0x00018208, + mmDB_DEBUG, 0xffffffff, 0x00000000, + mmDB_DEBUG2, 0xf00fffff, 0x00000400, + mmDB_DEBUG3, 0x0002021c, 0x00020200, + mmDCI_CLK_CNTL, 0x00000080, 0x00000000, 0x340c, 0x000000c0, 0x00800040, 0x360c, 0x000000c0, 0x00800040, - 0x16ec, 0x000000f0, 0x00000070, - 0x16f0, 0x00200000, 0x50100000, - 0x1c0c, 0x31000311, 0x00000011, - 0x09df, 0x00000003, 0x000007ff, - 0x0903, 0x000007ff, 0x00000000, - 0x2285, 0xf000001f, 0x00000007, - 0x22c9, 0xffffffff, 0x00ffffff, - 0x22c4, 0x0000ff0f, 0x00000000, - 0xa293, 0x07ffffff, 0x4e000000, - 0xa0d4, 0x3f3f3fff, 0x2a00126a, + mmFBC_DEBUG_COMP, 0x000000f0, 0x00000070, + mmFBC_MISC, 0x00200000, 0x50100000, + mmDIG0_HDMI_CONTROL, 0x31000311, 0x00000011, + mmMC_ARB_WTM_CNTL_RD, 0x00000003, 0x000007ff, + mmMC_XPB_P2P_BAR_CFG, 0x000007ff, 0x00000000, + mmPA_CL_ENHANCE, 0xf000001f, 0x00000007, + mmPA_SC_FORCE_EOV_MAX_CNTS, 0xffffffff, 0x00ffffff, + mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmPA_SC_MODE_CNTL_1, 0x07ffffff, 0x4e000000, + mmPA_SC_RASTER_CONFIG, 0x3f3f3fff, 0x2a00126a, 0x000c, 0xffffffff, 0x0040, 0x000d, 0x00000040, 0x00004040, - 0x2440, 0x07ffffff, 0x03000000, - 0x23a2, 0x01ff1f3f, 0x00000000, - 0x23a1, 0x01ff1f3f, 0x00000000, - 0x2418, 0x0000007f, 0x00000020, - 0x2542, 0x00010000, 0x00010000, - 0x2b05, 0x00000200, 0x000002fb, - 0x2b04, 0xffffffff, 0x0000543b, - 0x2b03, 0xffffffff, 0xa9210876, - 0x2234, 0xffffffff, 0x000fff40, - 0x2235, 0x0000001f, 0x00000010, - 0x0504, 0x20000000, 0x20fffed8, - 0x0570, 0x000c0fc0, 0x000c0400, - 0x052c, 0x0fffffff, 0xffffffff, - 0x052d, 0x0fffffff, 0x0fffffff, - 0x052e, 0x0fffffff, 0x0fffffff, - 0x052f, 0x0fffffff, 0x0fffffff + mmSPI_CONFIG_CNTL, 0x07ffffff, 0x03000000, + mmSQ_DED_CNT, 0x01ff1f3f, 0x00000000, + mmSQ_SEC_CNT, 0x01ff1f3f, 0x00000000, + mmSX_DEBUG_1, 0x0000007f, 0x00000020, + mmTA_CNTL_AUX, 0x00010000, 0x00010000, + mmTCP_ADDR_CONFIG, 0x00000200, 0x000002fb, + mmTCP_CHAN_STEER_HI, 0xffffffff, 0x0000543b, + mmTCP_CHAN_STEER_LO, 0xffffffff, 0xa9210876, + mmVGT_FIFO_DEPTHS, 0xffffffff, 0x000fff40, + mmVGT_GS_VERTEX_REUSE, 0x0000001f, 0x00000010, + mmVM_CONTEXT0_CNTL, 0x20000000, 0x20fffed8, + mmVM_L2_CG, 0x000c0fc0, 0x000c0400, + mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0xffffffff, + mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff, }; static const u32 tahiti_golden_registers2[] = { - 0x0319, 0x00000001, 0x00000001 + mmMCIF_MEM_CONTROL, 0x00000001, 0x00000001, }; static const u32 tahiti_golden_rlc_registers[] = { - 0x263e, 0xffffffff, 0x12011003, - 0x3109, 0xffffffff, 0x00601005, + mmGB_ADDR_CONFIG, 0xffffffff, 0x12011003, + mmRLC_LB_PARAMS, 0xffffffff, 0x00601005, 0x311f, 0xffffffff, 0x10104040, 0x3122, 0xffffffff, 0x0100000a, - 0x30c5, 0xffffffff, 0x00000800, - 0x30c3, 0xffffffff, 0x800000f4, - 0x3d2a, 0x00000008, 0x00000000 + mmRLC_LB_CNTR_MAX, 0xffffffff, 0x00000800, + mmRLC_LB_CNTL, 0xffffffff, 0x800000f4, + mmUVD_CGC_GATE, 0x00000008, 0x00000000, }; static const u32 pitcairn_golden_registers[] = { - 0x17bc, 0x00000030, 0x00000011, - 0x2684, 0x00010000, 0x00018208, - 0x260c, 0xffffffff, 0x00000000, - 0x260d, 0xf00fffff, 0x00000400, - 0x260e, 0x0002021c, 0x00020200, - 0x031e, 0x00000080, 0x00000000, + mmAZALIA_SCLK_CONTROL, 0x00000030, 0x00000011, + mmCB_HW_CONTROL, 0x00010000, 0x00018208, + mmDB_DEBUG, 0xffffffff, 0x00000000, + mmDB_DEBUG2, 0xf00fffff, 0x00000400, + mmDB_DEBUG3, 0x0002021c, 0x00020200, + mmDCI_CLK_CNTL, 0x00000080, 0x00000000, 0x340c, 0x000300c0, 0x00800040, 0x360c, 0x000300c0, 0x00800040, - 0x16ec, 0x000000f0, 0x00000070, - 0x16f0, 0x00200000, 0x50100000, - 0x1c0c, 0x31000311, 0x00000011, - 0x0ab9, 0x00073ffe, 0x000022a2, - 0x0903, 0x000007ff, 0x00000000, - 0x2285, 0xf000001f, 0x00000007, - 0x22c9, 0xffffffff, 0x00ffffff, - 0x22c4, 0x0000ff0f, 0x00000000, - 0xa293, 0x07ffffff, 0x4e000000, - 0xa0d4, 0x3f3f3fff, 0x2a00126a, + mmFBC_DEBUG_COMP, 0x000000f0, 0x00000070, + mmFBC_MISC, 0x00200000, 0x50100000, + mmDIG0_HDMI_CONTROL, 0x31000311, 0x00000011, + mmMC_SEQ_PMG_PG_HWCNTL, 0x00073ffe, 0x000022a2, + mmMC_XPB_P2P_BAR_CFG, 0x000007ff, 0x00000000, + mmPA_CL_ENHANCE, 0xf000001f, 0x00000007, + mmPA_SC_FORCE_EOV_MAX_CNTS, 0xffffffff, 0x00ffffff, + mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmPA_SC_MODE_CNTL_1, 0x07ffffff, 0x4e000000, + mmPA_SC_RASTER_CONFIG, 0x3f3f3fff, 0x2a00126a, 0x000c, 0xffffffff, 0x0040, 0x000d, 0x00000040, 0x00004040, - 0x2440, 0x07ffffff, 0x03000000, - 0x2418, 0x0000007f, 0x00000020, - 0x2542, 0x00010000, 0x00010000, - 0x2b05, 0x000003ff, 0x000000f7, - 0x2b04, 0xffffffff, 0x00000000, - 0x2b03, 0xffffffff, 0x32761054, - 0x2235, 0x0000001f, 0x00000010, - 0x0570, 0x000c0fc0, 0x000c0400, - 0x052c, 0x0fffffff, 0xffffffff, - 0x052d, 0x0fffffff, 0x0fffffff, - 0x052e, 0x0fffffff, 0x0fffffff, - 0x052f, 0x0fffffff, 0x0fffffff + mmSPI_CONFIG_CNTL, 0x07ffffff, 0x03000000, + mmSX_DEBUG_1, 0x0000007f, 0x00000020, + mmTA_CNTL_AUX, 0x00010000, 0x00010000, + mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f7, + mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000, + mmTCP_CHAN_STEER_LO, 0xffffffff, 0x32761054, + mmVGT_GS_VERTEX_REUSE, 0x0000001f, 0x00000010, + mmVM_L2_CG, 0x000c0fc0, 0x000c0400, + mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0xffffffff, + mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff, }; static const u32 pitcairn_golden_rlc_registers[] = { - 0x263e, 0xffffffff, 0x12011003, - 0x3109, 0xffffffff, 0x00601004, + mmGB_ADDR_CONFIG, 0xffffffff, 0x12011003, + mmRLC_LB_PARAMS, 0xffffffff, 0x00601004, 0x311f, 0xffffffff, 0x10102020, 0x3122, 0xffffffff, 0x01000020, - 0x30c5, 0xffffffff, 0x00000800, - 0x30c3, 0xffffffff, 0x800000a4 + mmRLC_LB_CNTR_MAX, 0xffffffff, 0x00000800, + mmRLC_LB_CNTL, 0xffffffff, 0x800000a4, }; static const u32 verde_pg_init[] = { - 0x0d4f, 0xffffffff, 0x40000, - 0x0d4e, 0xffffffff, 0x200010ff, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x7007, - 0x0d4e, 0xffffffff, 0x300010ff, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x400000, - 0x0d4e, 0xffffffff, 0x100010ff, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x120200, - 0x0d4e, 0xffffffff, 0x500010ff, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x1e1e16, - 0x0d4e, 0xffffffff, 0x600010ff, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x171f1e, - 0x0d4e, 0xffffffff, 0x700010ff, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4f, 0xffffffff, 0x0, - 0x0d4e, 0xffffffff, 0x9ff, - 0x0d40, 0xffffffff, 0x0, - 0x0d41, 0xffffffff, 0x10000800, - 0x0d41, 0xffffffff, 0xf, - 0x0d41, 0xffffffff, 0xf, - 0x0d40, 0xffffffff, 0x4, - 0x0d41, 0xffffffff, 0x1000051e, - 0x0d41, 0xffffffff, 0xffff, - 0x0d41, 0xffffffff, 0xffff, - 0x0d40, 0xffffffff, 0x8, - 0x0d41, 0xffffffff, 0x80500, - 0x0d40, 0xffffffff, 0x12, - 0x0d41, 0xffffffff, 0x9050c, - 0x0d40, 0xffffffff, 0x1d, - 0x0d41, 0xffffffff, 0xb052c, - 0x0d40, 0xffffffff, 0x2a, - 0x0d41, 0xffffffff, 0x1053e, - 0x0d40, 0xffffffff, 0x2d, - 0x0d41, 0xffffffff, 0x10546, - 0x0d40, 0xffffffff, 0x30, - 0x0d41, 0xffffffff, 0xa054e, - 0x0d40, 0xffffffff, 0x3c, - 0x0d41, 0xffffffff, 0x1055f, - 0x0d40, 0xffffffff, 0x3f, - 0x0d41, 0xffffffff, 0x10567, - 0x0d40, 0xffffffff, 0x42, - 0x0d41, 0xffffffff, 0x1056f, - 0x0d40, 0xffffffff, 0x45, - 0x0d41, 0xffffffff, 0x10572, - 0x0d40, 0xffffffff, 0x48, - 0x0d41, 0xffffffff, 0x20575, - 0x0d40, 0xffffffff, 0x4c, - 0x0d41, 0xffffffff, 0x190801, - 0x0d40, 0xffffffff, 0x67, - 0x0d41, 0xffffffff, 0x1082a, - 0x0d40, 0xffffffff, 0x6a, - 0x0d41, 0xffffffff, 0x1b082d, - 0x0d40, 0xffffffff, 0x87, - 0x0d41, 0xffffffff, 0x310851, - 0x0d40, 0xffffffff, 0xba, - 0x0d41, 0xffffffff, 0x891, - 0x0d40, 0xffffffff, 0xbc, - 0x0d41, 0xffffffff, 0x893, - 0x0d40, 0xffffffff, 0xbe, - 0x0d41, 0xffffffff, 0x20895, - 0x0d40, 0xffffffff, 0xc2, - 0x0d41, 0xffffffff, 0x20899, - 0x0d40, 0xffffffff, 0xc6, - 0x0d41, 0xffffffff, 0x2089d, - 0x0d40, 0xffffffff, 0xca, - 0x0d41, 0xffffffff, 0x8a1, - 0x0d40, 0xffffffff, 0xcc, - 0x0d41, 0xffffffff, 0x8a3, - 0x0d40, 0xffffffff, 0xce, - 0x0d41, 0xffffffff, 0x308a5, - 0x0d40, 0xffffffff, 0xd3, - 0x0d41, 0xffffffff, 0x6d08cd, - 0x0d40, 0xffffffff, 0x142, - 0x0d41, 0xffffffff, 0x2000095a, - 0x0d41, 0xffffffff, 0x1, - 0x0d40, 0xffffffff, 0x144, - 0x0d41, 0xffffffff, 0x301f095b, - 0x0d40, 0xffffffff, 0x165, - 0x0d41, 0xffffffff, 0xc094d, - 0x0d40, 0xffffffff, 0x173, - 0x0d41, 0xffffffff, 0xf096d, - 0x0d40, 0xffffffff, 0x184, - 0x0d41, 0xffffffff, 0x15097f, - 0x0d40, 0xffffffff, 0x19b, - 0x0d41, 0xffffffff, 0xc0998, - 0x0d40, 0xffffffff, 0x1a9, - 0x0d41, 0xffffffff, 0x409a7, - 0x0d40, 0xffffffff, 0x1af, - 0x0d41, 0xffffffff, 0xcdc, - 0x0d40, 0xffffffff, 0x1b1, - 0x0d41, 0xffffffff, 0x800, - 0x0d42, 0xffffffff, 0x6c9b2000, - 0x0d44, 0xfc00, 0x2000, - 0x0d51, 0xffffffff, 0xfc0, - 0x0a35, 0x00000100, 0x100 + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x40000, + mmGMCON_PGFSM_CONFIG, 0xffffffff, 0x200010ff, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x7007, + mmGMCON_PGFSM_CONFIG, 0xffffffff, 0x300010ff, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x400000, + mmGMCON_PGFSM_CONFIG, 0xffffffff, 0x100010ff, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x120200, + mmGMCON_PGFSM_CONFIG, 0xffffffff, 0x500010ff, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x1e1e16, + mmGMCON_PGFSM_CONFIG, 0xffffffff, 0x600010ff, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x171f1e, + mmGMCON_PGFSM_CONFIG, 0xffffffff, 0x700010ff, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_WRITE, 0xffffffff, 0x0, + mmGMCON_PGFSM_CONFIG, 0xffffffff, 0x9ff, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x0, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x10000800, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xf, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xf, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x4, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x1000051e, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xffff, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xffff, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x8, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x80500, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x12, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x9050c, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x1d, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xb052c, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x2a, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x1053e, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x2d, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x10546, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x30, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xa054e, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x3c, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x1055f, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x3f, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x10567, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x42, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x1056f, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x45, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x10572, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x48, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x20575, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x4c, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x190801, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x67, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x1082a, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x6a, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x1b082d, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x87, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x310851, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xba, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x891, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xbc, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x893, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xbe, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x20895, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xc2, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x20899, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xc6, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x2089d, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xca, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x8a1, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xcc, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x8a3, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xce, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x308a5, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0xd3, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x6d08cd, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x142, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x2000095a, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x1, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x144, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x301f095b, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x165, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xc094d, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x173, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xf096d, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x184, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x15097f, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x19b, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xc0998, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x1a9, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x409a7, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x1af, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0xcdc, + mmGMCON_RENG_RAM_INDEX, 0xffffffff, 0x1b1, + mmGMCON_RENG_RAM_DATA, 0xffffffff, 0x800, + mmGMCON_RENG_EXECUTE, 0xffffffff, 0x6c9b2000, + mmGMCON_MISC2, 0xfc00, 0x2000, + mmGMCON_MISC3, 0xffffffff, 0xfc0, + mmMC_PMG_AUTO_CFG, 0x00000100, 0x100, }; static const u32 verde_golden_rlc_registers[] = { - 0x263e, 0xffffffff, 0x02010002, - 0x3109, 0xffffffff, 0x033f1005, + mmGB_ADDR_CONFIG, 0xffffffff, 0x02010002, + mmRLC_LB_PARAMS, 0xffffffff, 0x033f1005, 0x311f, 0xffffffff, 0x10808020, 0x3122, 0xffffffff, 0x00800008, - 0x30c5, 0xffffffff, 0x00001000, - 0x30c3, 0xffffffff, 0x80010014 + mmRLC_LB_CNTR_MAX, 0xffffffff, 0x00001000, + mmRLC_LB_CNTL, 0xffffffff, 0x80010014, }; static const u32 verde_golden_registers[] = { - 0x17bc, 0x00000030, 0x00000011, - 0x2684, 0x00010000, 0x00018208, - 0x260c, 0xffffffff, 0x00000000, - 0x260d, 0xf00fffff, 0x00000400, - 0x260e, 0x0002021c, 0x00020200, - 0x031e, 0x00000080, 0x00000000, + mmAZALIA_SCLK_CONTROL, 0x00000030, 0x00000011, + mmCB_HW_CONTROL, 0x00010000, 0x00018208, + mmDB_DEBUG, 0xffffffff, 0x00000000, + mmDB_DEBUG2, 0xf00fffff, 0x00000400, + mmDB_DEBUG3, 0x0002021c, 0x00020200, + mmDCI_CLK_CNTL, 0x00000080, 0x00000000, 0x340c, 0x000300c0, 0x00800040, 0x360c, 0x000300c0, 0x00800040, - 0x16ec, 0x000000f0, 0x00000070, - 0x16f0, 0x00200000, 0x50100000, - 0x1c0c, 0x31000311, 0x00000011, - 0x0ab9, 0x00073ffe, 0x000022a2, - 0x0903, 0x000007ff, 0x00000000, - 0x2285, 0xf000001f, 0x00000007, - 0x22c9, 0xffffffff, 0x00ffffff, - 0x22c4, 0x0000ff0f, 0x00000000, - 0xa293, 0x07ffffff, 0x4e000000, - 0xa0d4, 0x3f3f3fff, 0x0000124a, + mmFBC_DEBUG_COMP, 0x000000f0, 0x00000070, + mmFBC_MISC, 0x00200000, 0x50100000, + mmDIG0_HDMI_CONTROL, 0x31000311, 0x00000011, + mmMC_SEQ_PMG_PG_HWCNTL, 0x00073ffe, 0x000022a2, + mmMC_XPB_P2P_BAR_CFG, 0x000007ff, 0x00000000, + mmPA_CL_ENHANCE, 0xf000001f, 0x00000007, + mmPA_SC_FORCE_EOV_MAX_CNTS, 0xffffffff, 0x00ffffff, + mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmPA_SC_MODE_CNTL_1, 0x07ffffff, 0x4e000000, + mmPA_SC_RASTER_CONFIG, 0x3f3f3fff, 0x0000124a, 0x000c, 0xffffffff, 0x0040, 0x000d, 0x00000040, 0x00004040, - 0x2440, 0x07ffffff, 0x03000000, - 0x23a2, 0x01ff1f3f, 0x00000000, - 0x23a1, 0x01ff1f3f, 0x00000000, - 0x2418, 0x0000007f, 0x00000020, - 0x2542, 0x00010000, 0x00010000, - 0x2b05, 0x000003ff, 0x00000003, - 0x2b04, 0xffffffff, 0x00000000, - 0x2b03, 0xffffffff, 0x00001032, - 0x2235, 0x0000001f, 0x00000010, - 0x0570, 0x000c0fc0, 0x000c0400, - 0x052c, 0x0fffffff, 0xffffffff, - 0x052d, 0x0fffffff, 0x0fffffff, - 0x052e, 0x0fffffff, 0x0fffffff, - 0x052f, 0x0fffffff, 0x0fffffff + mmSPI_CONFIG_CNTL, 0x07ffffff, 0x03000000, + mmSQ_DED_CNT, 0x01ff1f3f, 0x00000000, + mmSQ_SEC_CNT, 0x01ff1f3f, 0x00000000, + mmSX_DEBUG_1, 0x0000007f, 0x00000020, + mmTA_CNTL_AUX, 0x00010000, 0x00010000, + mmTCP_ADDR_CONFIG, 0x000003ff, 0x00000003, + mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000, + mmTCP_CHAN_STEER_LO, 0xffffffff, 0x00001032, + mmVGT_GS_VERTEX_REUSE, 0x0000001f, 0x00000010, + mmVM_L2_CG, 0x000c0fc0, 0x000c0400, + mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0xffffffff, + mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff, }; static const u32 oland_golden_registers[] = { - 0x17bc, 0x00000030, 0x00000011, - 0x2684, 0x00010000, 0x00018208, - 0x260c, 0xffffffff, 0x00000000, - 0x260d, 0xf00fffff, 0x00000400, - 0x260e, 0x0002021c, 0x00020200, - 0x031e, 0x00000080, 0x00000000, + mmAZALIA_SCLK_CONTROL, 0x00000030, 0x00000011, + mmCB_HW_CONTROL, 0x00010000, 0x00018208, + mmDB_DEBUG, 0xffffffff, 0x00000000, + mmDB_DEBUG2, 0xf00fffff, 0x00000400, + mmDB_DEBUG3, 0x0002021c, 0x00020200, + mmDCI_CLK_CNTL, 0x00000080, 0x00000000, 0x340c, 0x000300c0, 0x00800040, 0x360c, 0x000300c0, 0x00800040, - 0x16ec, 0x000000f0, 0x00000070, - 0x16f0, 0x00200000, 0x50100000, - 0x1c0c, 0x31000311, 0x00000011, - 0x0ab9, 0x00073ffe, 0x000022a2, - 0x0903, 0x000007ff, 0x00000000, - 0x2285, 0xf000001f, 0x00000007, - 0x22c9, 0xffffffff, 0x00ffffff, - 0x22c4, 0x0000ff0f, 0x00000000, - 0xa293, 0x07ffffff, 0x4e000000, - 0xa0d4, 0x3f3f3fff, 0x00000082, + mmFBC_DEBUG_COMP, 0x000000f0, 0x00000070, + mmFBC_MISC, 0x00200000, 0x50100000, + mmDIG0_HDMI_CONTROL, 0x31000311, 0x00000011, + mmMC_SEQ_PMG_PG_HWCNTL, 0x00073ffe, 0x000022a2, + mmMC_XPB_P2P_BAR_CFG, 0x000007ff, 0x00000000, + mmPA_CL_ENHANCE, 0xf000001f, 0x00000007, + mmPA_SC_FORCE_EOV_MAX_CNTS, 0xffffffff, 0x00ffffff, + mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmPA_SC_MODE_CNTL_1, 0x07ffffff, 0x4e000000, + mmPA_SC_RASTER_CONFIG, 0x3f3f3fff, 0x00000082, 0x000c, 0xffffffff, 0x0040, 0x000d, 0x00000040, 0x00004040, - 0x2440, 0x07ffffff, 0x03000000, - 0x2418, 0x0000007f, 0x00000020, - 0x2542, 0x00010000, 0x00010000, - 0x2b05, 0x000003ff, 0x000000f3, - 0x2b04, 0xffffffff, 0x00000000, - 0x2b03, 0xffffffff, 0x00003210, - 0x2235, 0x0000001f, 0x00000010, - 0x0570, 0x000c0fc0, 0x000c0400, - 0x052c, 0x0fffffff, 0xffffffff, - 0x052d, 0x0fffffff, 0x0fffffff, - 0x052e, 0x0fffffff, 0x0fffffff, - 0x052f, 0x0fffffff, 0x0fffffff + mmSPI_CONFIG_CNTL, 0x07ffffff, 0x03000000, + mmSX_DEBUG_1, 0x0000007f, 0x00000020, + mmTA_CNTL_AUX, 0x00010000, 0x00010000, + mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f3, + mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000, + mmTCP_CHAN_STEER_LO, 0xffffffff, 0x00003210, + mmVGT_GS_VERTEX_REUSE, 0x0000001f, 0x00000010, + mmVM_L2_CG, 0x000c0fc0, 0x000c0400, + mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0xffffffff, + mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff, + }; static const u32 oland_golden_rlc_registers[] = { - 0x263e, 0xffffffff, 0x02010002, - 0x3109, 0xffffffff, 0x00601005, + mmGB_ADDR_CONFIG, 0xffffffff, 0x02010002, + mmRLC_LB_PARAMS, 0xffffffff, 0x00601005, 0x311f, 0xffffffff, 0x10104040, 0x3122, 0xffffffff, 0x0100000a, - 0x30c5, 0xffffffff, 0x00000800, - 0x30c3, 0xffffffff, 0x800000f4 + mmRLC_LB_CNTR_MAX, 0xffffffff, 0x00000800, + mmRLC_LB_CNTL, 0xffffffff, 0x800000f4, }; static const u32 hainan_golden_registers[] = { 0x17bc, 0x00000030, 0x00000011, - 0x2684, 0x00010000, 0x00018208, - 0x260c, 0xffffffff, 0x00000000, - 0x260d, 0xf00fffff, 0x00000400, - 0x260e, 0x0002021c, 0x00020200, + mmCB_HW_CONTROL, 0x00010000, 0x00018208, + mmDB_DEBUG, 0xffffffff, 0x00000000, + mmDB_DEBUG2, 0xf00fffff, 0x00000400, + mmDB_DEBUG3, 0x0002021c, 0x00020200, 0x031e, 0x00000080, 0x00000000, 0x3430, 0xff000fff, 0x00000100, 0x340c, 0x000300c0, 0x00800040, @@ -379,63 +385,63 @@ static const u32 hainan_golden_registers[] = 0x16ec, 0x000000f0, 0x00000070, 0x16f0, 0x00200000, 0x50100000, 0x1c0c, 0x31000311, 0x00000011, - 0x0ab9, 0x00073ffe, 0x000022a2, - 0x0903, 0x000007ff, 0x00000000, - 0x2285, 0xf000001f, 0x00000007, - 0x22c9, 0xffffffff, 0x00ffffff, - 0x22c4, 0x0000ff0f, 0x00000000, - 0xa293, 0x07ffffff, 0x4e000000, - 0xa0d4, 0x3f3f3fff, 0x00000000, + mmMC_SEQ_PMG_PG_HWCNTL, 0x00073ffe, 0x000022a2, + mmMC_XPB_P2P_BAR_CFG, 0x000007ff, 0x00000000, + mmPA_CL_ENHANCE, 0xf000001f, 0x00000007, + mmPA_SC_FORCE_EOV_MAX_CNTS, 0xffffffff, 0x00ffffff, + mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmPA_SC_MODE_CNTL_1, 0x07ffffff, 0x4e000000, + mmPA_SC_RASTER_CONFIG, 0x3f3f3fff, 0x00000000, 0x000c, 0xffffffff, 0x0040, 0x000d, 0x00000040, 0x00004040, - 0x2440, 0x03e00000, 0x03600000, - 0x2418, 0x0000007f, 0x00000020, - 0x2542, 0x00010000, 0x00010000, - 0x2b05, 0x000003ff, 0x000000f1, - 0x2b04, 0xffffffff, 0x00000000, - 0x2b03, 0xffffffff, 0x00003210, - 0x2235, 0x0000001f, 0x00000010, - 0x0570, 0x000c0fc0, 0x000c0400, - 0x052c, 0x0fffffff, 0xffffffff, - 0x052d, 0x0fffffff, 0x0fffffff, - 0x052e, 0x0fffffff, 0x0fffffff, - 0x052f, 0x0fffffff, 0x0fffffff + mmSPI_CONFIG_CNTL, 0x03e00000, 0x03600000, + mmSX_DEBUG_1, 0x0000007f, 0x00000020, + mmTA_CNTL_AUX, 0x00010000, 0x00010000, + mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f1, + mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000, + mmTCP_CHAN_STEER_LO, 0xffffffff, 0x00003210, + mmVGT_GS_VERTEX_REUSE, 0x0000001f, 0x00000010, + mmVM_L2_CG, 0x000c0fc0, 0x000c0400, + mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0xffffffff, + mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff, + mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff, }; static const u32 hainan_golden_registers2[] = { - 0x263e, 0xffffffff, 0x2011003 + mmGB_ADDR_CONFIG, 0xffffffff, 0x2011003, }; static const u32 tahiti_mgcg_cgcg_init[] = { - 0x3100, 0xffffffff, 0xfffffffc, - 0x200b, 0xffffffff, 0xe0000000, - 0x2698, 0xffffffff, 0x00000100, - 0x24a9, 0xffffffff, 0x00000100, - 0x3059, 0xffffffff, 0x00000100, - 0x25dd, 0xffffffff, 0x00000100, - 0x2261, 0xffffffff, 0x06000100, - 0x2286, 0xffffffff, 0x00000100, - 0x24a8, 0xffffffff, 0x00000100, - 0x30e0, 0xffffffff, 0x00000100, - 0x22ca, 0xffffffff, 0x00000100, - 0x2451, 0xffffffff, 0x00000100, - 0x2362, 0xffffffff, 0x00000100, - 0x2363, 0xffffffff, 0x00000100, - 0x240c, 0xffffffff, 0x00000100, - 0x240d, 0xffffffff, 0x00000100, - 0x240e, 0xffffffff, 0x00000100, - 0x240f, 0xffffffff, 0x00000100, - 0x2b60, 0xffffffff, 0x00000100, - 0x2b15, 0xffffffff, 0x00000100, - 0x225f, 0xffffffff, 0x06000100, - 0x261a, 0xffffffff, 0x00000100, - 0x2544, 0xffffffff, 0x00000100, - 0x2bc1, 0xffffffff, 0x00000100, - 0x2b81, 0xffffffff, 0x00000100, - 0x2527, 0xffffffff, 0x00000100, - 0x200b, 0xffffffff, 0xe0000000, + mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xfffffffc, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100, + mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100, + mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100, + mmTA_CGTT_CTRL, 0xffffffff, 0x00000100, + mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTD_CGTT_CTRL, 0xffffffff, 0x00000100, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, 0x2458, 0xffffffff, 0x00010000, 0x2459, 0xffffffff, 0x00030002, 0x245a, 0xffffffff, 0x00040007, @@ -516,55 +522,55 @@ static const u32 tahiti_mgcg_cgcg_init[] = 0x24a5, 0xffffffff, 0x00000015, 0x24a6, 0xffffffff, 0x00140013, 0x24a7, 0xffffffff, 0x00170016, - 0x2454, 0xffffffff, 0x96940200, - 0x21c2, 0xffffffff, 0x00900100, - 0x311e, 0xffffffff, 0x00000080, - 0x3101, 0xffffffff, 0x0020003f, + mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96940200, + mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100, + mmRLC_GCPM_GENERAL_3, 0xffffffff, 0x00000080, + mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003f, 0x000c, 0xffffffff, 0x0000001c, 0x000d, 0x000f0000, 0x000f0000, 0x0583, 0xffffffff, 0x00000100, - 0x0409, 0xffffffff, 0x00000100, - 0x040b, 0x00000101, 0x00000000, - 0x082a, 0xffffffff, 0x00000104, - 0x0993, 0x000c0000, 0x000c0000, - 0x0992, 0x000c0000, 0x000c0000, - 0x1579, 0xff000fff, 0x00000100, + mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100, + mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000, + mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104, + mmMC_CITF_MISC_WR_CG, 0x000c0000, 0x000c0000, + mmMC_CITF_MISC_RD_CG, 0x000c0000, 0x000c0000, + mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100, 0x157a, 0x00000001, 0x00000001, - 0x0bd4, 0x00000001, 0x00000001, - 0x0c33, 0xc0000fff, 0x00000104, - 0x3079, 0x00000001, 0x00000001, + mmHDP_MEM_POWER_LS, 0x00000001, 0x00000001, + mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104, + mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001, 0x3430, 0xfffffff0, 0x00000100, - 0x3630, 0xfffffff0, 0x00000100 + 0x3630, 0xfffffff0, 0x00000100, }; static const u32 pitcairn_mgcg_cgcg_init[] = { - 0x3100, 0xffffffff, 0xfffffffc, - 0x200b, 0xffffffff, 0xe0000000, - 0x2698, 0xffffffff, 0x00000100, - 0x24a9, 0xffffffff, 0x00000100, - 0x3059, 0xffffffff, 0x00000100, - 0x25dd, 0xffffffff, 0x00000100, - 0x2261, 0xffffffff, 0x06000100, - 0x2286, 0xffffffff, 0x00000100, - 0x24a8, 0xffffffff, 0x00000100, - 0x30e0, 0xffffffff, 0x00000100, - 0x22ca, 0xffffffff, 0x00000100, - 0x2451, 0xffffffff, 0x00000100, - 0x2362, 0xffffffff, 0x00000100, - 0x2363, 0xffffffff, 0x00000100, - 0x240c, 0xffffffff, 0x00000100, - 0x240d, 0xffffffff, 0x00000100, - 0x240e, 0xffffffff, 0x00000100, - 0x240f, 0xffffffff, 0x00000100, - 0x2b60, 0xffffffff, 0x00000100, - 0x2b15, 0xffffffff, 0x00000100, - 0x225f, 0xffffffff, 0x06000100, - 0x261a, 0xffffffff, 0x00000100, - 0x2544, 0xffffffff, 0x00000100, - 0x2bc1, 0xffffffff, 0x00000100, - 0x2b81, 0xffffffff, 0x00000100, - 0x2527, 0xffffffff, 0x00000100, - 0x200b, 0xffffffff, 0xe0000000, + mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xfffffffc, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100, + mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100, + mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100, + mmTA_CGTT_CTRL, 0xffffffff, 0x00000100, + mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTD_CGTT_CTRL, 0xffffffff, 0x00000100, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, 0x2458, 0xffffffff, 0x00010000, 0x2459, 0xffffffff, 0x00030002, 0x245a, 0xffffffff, 0x00040007, @@ -615,53 +621,54 @@ static const u32 pitcairn_mgcg_cgcg_init[] = 0x2496, 0xffffffff, 0x00100013, 0x2497, 0xffffffff, 0x00120011, 0x2498, 0xffffffff, 0x00150014, - 0x2454, 0xffffffff, 0x96940200, - 0x21c2, 0xffffffff, 0x00900100, - 0x311e, 0xffffffff, 0x00000080, - 0x3101, 0xffffffff, 0x0020003f, + mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96940200, + mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100, + mmRLC_GCPM_GENERAL_3, 0xffffffff, 0x00000080, + mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003f, 0x000c, 0xffffffff, 0x0000001c, 0x000d, 0x000f0000, 0x000f0000, 0x0583, 0xffffffff, 0x00000100, - 0x0409, 0xffffffff, 0x00000100, - 0x040b, 0x00000101, 0x00000000, - 0x082a, 0xffffffff, 0x00000104, - 0x1579, 0xff000fff, 0x00000100, + mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100, + mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000, + mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104, + mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100, 0x157a, 0x00000001, 0x00000001, - 0x0bd4, 0x00000001, 0x00000001, - 0x0c33, 0xc0000fff, 0x00000104, - 0x3079, 0x00000001, 0x00000001, + mmHDP_MEM_POWER_LS, 0x00000001, 0x00000001, + mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104, + mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001, 0x3430, 0xfffffff0, 0x00000100, - 0x3630, 0xfffffff0, 0x00000100 + 0x3630, 0xfffffff0, 0x00000100, }; + static const u32 verde_mgcg_cgcg_init[] = { - 0x3100, 0xffffffff, 0xfffffffc, - 0x200b, 0xffffffff, 0xe0000000, - 0x2698, 0xffffffff, 0x00000100, - 0x24a9, 0xffffffff, 0x00000100, - 0x3059, 0xffffffff, 0x00000100, - 0x25dd, 0xffffffff, 0x00000100, - 0x2261, 0xffffffff, 0x06000100, - 0x2286, 0xffffffff, 0x00000100, - 0x24a8, 0xffffffff, 0x00000100, - 0x30e0, 0xffffffff, 0x00000100, - 0x22ca, 0xffffffff, 0x00000100, - 0x2451, 0xffffffff, 0x00000100, - 0x2362, 0xffffffff, 0x00000100, - 0x2363, 0xffffffff, 0x00000100, - 0x240c, 0xffffffff, 0x00000100, - 0x240d, 0xffffffff, 0x00000100, - 0x240e, 0xffffffff, 0x00000100, - 0x240f, 0xffffffff, 0x00000100, - 0x2b60, 0xffffffff, 0x00000100, - 0x2b15, 0xffffffff, 0x00000100, - 0x225f, 0xffffffff, 0x06000100, - 0x261a, 0xffffffff, 0x00000100, - 0x2544, 0xffffffff, 0x00000100, - 0x2bc1, 0xffffffff, 0x00000100, - 0x2b81, 0xffffffff, 0x00000100, - 0x2527, 0xffffffff, 0x00000100, - 0x200b, 0xffffffff, 0xe0000000, + mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xfffffffc, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100, + mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100, + mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100, + mmTA_CGTT_CTRL, 0xffffffff, 0x00000100, + mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTD_CGTT_CTRL, 0xffffffff, 0x00000100, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, 0x2458, 0xffffffff, 0x00010000, 0x2459, 0xffffffff, 0x00030002, 0x245a, 0xffffffff, 0x00040007, @@ -712,55 +719,56 @@ static const u32 verde_mgcg_cgcg_init[] = 0x2496, 0xffffffff, 0x00100013, 0x2497, 0xffffffff, 0x00120011, 0x2498, 0xffffffff, 0x00150014, - 0x2454, 0xffffffff, 0x96940200, - 0x21c2, 0xffffffff, 0x00900100, - 0x311e, 0xffffffff, 0x00000080, - 0x3101, 0xffffffff, 0x0020003f, + mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96940200, + mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100, + mmRLC_GCPM_GENERAL_3, 0xffffffff, 0x00000080, + mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003f, 0x000c, 0xffffffff, 0x0000001c, 0x000d, 0x000f0000, 0x000f0000, 0x0583, 0xffffffff, 0x00000100, - 0x0409, 0xffffffff, 0x00000100, - 0x040b, 0x00000101, 0x00000000, - 0x082a, 0xffffffff, 0x00000104, - 0x0993, 0x000c0000, 0x000c0000, - 0x0992, 0x000c0000, 0x000c0000, - 0x1579, 0xff000fff, 0x00000100, + mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100, + mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000, + mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104, + mmMC_CITF_MISC_WR_CG, 0x000c0000, 0x000c0000, + mmMC_CITF_MISC_RD_CG, 0x000c0000, 0x000c0000, + mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100, 0x157a, 0x00000001, 0x00000001, - 0x0bd4, 0x00000001, 0x00000001, - 0x0c33, 0xc0000fff, 0x00000104, - 0x3079, 0x00000001, 0x00000001, + mmHDP_MEM_POWER_LS, 0x00000001, 0x00000001, + mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104, + mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001, 0x3430, 0xfffffff0, 0x00000100, - 0x3630, 0xfffffff0, 0x00000100 + 0x3630, 0xfffffff0, 0x00000100, }; + static const u32 oland_mgcg_cgcg_init[] = { - 0x3100, 0xffffffff, 0xfffffffc, - 0x200b, 0xffffffff, 0xe0000000, - 0x2698, 0xffffffff, 0x00000100, - 0x24a9, 0xffffffff, 0x00000100, - 0x3059, 0xffffffff, 0x00000100, - 0x25dd, 0xffffffff, 0x00000100, - 0x2261, 0xffffffff, 0x06000100, - 0x2286, 0xffffffff, 0x00000100, - 0x24a8, 0xffffffff, 0x00000100, - 0x30e0, 0xffffffff, 0x00000100, - 0x22ca, 0xffffffff, 0x00000100, - 0x2451, 0xffffffff, 0x00000100, - 0x2362, 0xffffffff, 0x00000100, - 0x2363, 0xffffffff, 0x00000100, - 0x240c, 0xffffffff, 0x00000100, - 0x240d, 0xffffffff, 0x00000100, - 0x240e, 0xffffffff, 0x00000100, - 0x240f, 0xffffffff, 0x00000100, - 0x2b60, 0xffffffff, 0x00000100, - 0x2b15, 0xffffffff, 0x00000100, - 0x225f, 0xffffffff, 0x06000100, - 0x261a, 0xffffffff, 0x00000100, - 0x2544, 0xffffffff, 0x00000100, - 0x2bc1, 0xffffffff, 0x00000100, - 0x2b81, 0xffffffff, 0x00000100, - 0x2527, 0xffffffff, 0x00000100, - 0x200b, 0xffffffff, 0xe0000000, + mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xfffffffc, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100, + mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100, + mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100, + mmTA_CGTT_CTRL, 0xffffffff, 0x00000100, + mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTD_CGTT_CTRL, 0xffffffff, 0x00000100, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, 0x2458, 0xffffffff, 0x00010000, 0x2459, 0xffffffff, 0x00030002, 0x245a, 0xffffffff, 0x00040007, @@ -791,55 +799,56 @@ static const u32 oland_mgcg_cgcg_init[] = 0x2473, 0xffffffff, 0x0000000b, 0x2474, 0xffffffff, 0x000a0009, 0x2475, 0xffffffff, 0x000d000c, - 0x2454, 0xffffffff, 0x96940200, - 0x21c2, 0xffffffff, 0x00900100, - 0x311e, 0xffffffff, 0x00000080, - 0x3101, 0xffffffff, 0x0020003f, + mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96940200, + mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100, + mmRLC_GCPM_GENERAL_3, 0xffffffff, 0x00000080, + mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003f, 0x000c, 0xffffffff, 0x0000001c, 0x000d, 0x000f0000, 0x000f0000, 0x0583, 0xffffffff, 0x00000100, - 0x0409, 0xffffffff, 0x00000100, - 0x040b, 0x00000101, 0x00000000, - 0x082a, 0xffffffff, 0x00000104, - 0x0993, 0x000c0000, 0x000c0000, - 0x0992, 0x000c0000, 0x000c0000, - 0x1579, 0xff000fff, 0x00000100, + mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100, + mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000, + mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104, + mmMC_CITF_MISC_WR_CG, 0x000c0000, 0x000c0000, + mmMC_CITF_MISC_RD_CG, 0x000c0000, 0x000c0000, + mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100, 0x157a, 0x00000001, 0x00000001, - 0x0bd4, 0x00000001, 0x00000001, - 0x0c33, 0xc0000fff, 0x00000104, - 0x3079, 0x00000001, 0x00000001, + mmHDP_MEM_POWER_LS, 0x00000001, 0x00000001, + mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104, + mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001, 0x3430, 0xfffffff0, 0x00000100, - 0x3630, 0xfffffff0, 0x00000100 + 0x3630, 0xfffffff0, 0x00000100, }; + static const u32 hainan_mgcg_cgcg_init[] = { - 0x3100, 0xffffffff, 0xfffffffc, - 0x200b, 0xffffffff, 0xe0000000, - 0x2698, 0xffffffff, 0x00000100, - 0x24a9, 0xffffffff, 0x00000100, - 0x3059, 0xffffffff, 0x00000100, - 0x25dd, 0xffffffff, 0x00000100, - 0x2261, 0xffffffff, 0x06000100, - 0x2286, 0xffffffff, 0x00000100, - 0x24a8, 0xffffffff, 0x00000100, - 0x30e0, 0xffffffff, 0x00000100, - 0x22ca, 0xffffffff, 0x00000100, - 0x2451, 0xffffffff, 0x00000100, - 0x2362, 0xffffffff, 0x00000100, - 0x2363, 0xffffffff, 0x00000100, - 0x240c, 0xffffffff, 0x00000100, - 0x240d, 0xffffffff, 0x00000100, - 0x240e, 0xffffffff, 0x00000100, - 0x240f, 0xffffffff, 0x00000100, - 0x2b60, 0xffffffff, 0x00000100, - 0x2b15, 0xffffffff, 0x00000100, - 0x225f, 0xffffffff, 0x06000100, - 0x261a, 0xffffffff, 0x00000100, - 0x2544, 0xffffffff, 0x00000100, - 0x2bc1, 0xffffffff, 0x00000100, - 0x2b81, 0xffffffff, 0x00000100, - 0x2527, 0xffffffff, 0x00000100, - 0x200b, 0xffffffff, 0xe0000000, + mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xfffffffc, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, + mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100, + mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100, + mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100, + mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100, + mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100, + mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100, + mmTA_CGTT_CTRL, 0xffffffff, 0x00000100, + mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100, + mmTD_CGTT_CTRL, 0xffffffff, 0x00000100, + mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000, 0x2458, 0xffffffff, 0x00010000, 0x2459, 0xffffffff, 0x00030002, 0x245a, 0xffffffff, 0x00040007, @@ -870,22 +879,22 @@ static const u32 hainan_mgcg_cgcg_init[] = 0x2473, 0xffffffff, 0x0000000b, 0x2474, 0xffffffff, 0x000a0009, 0x2475, 0xffffffff, 0x000d000c, - 0x2454, 0xffffffff, 0x96940200, - 0x21c2, 0xffffffff, 0x00900100, - 0x311e, 0xffffffff, 0x00000080, - 0x3101, 0xffffffff, 0x0020003f, + mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96940200, + mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100, + mmRLC_GCPM_GENERAL_3, 0xffffffff, 0x00000080, + mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003f, 0x000c, 0xffffffff, 0x0000001c, 0x000d, 0x000f0000, 0x000f0000, 0x0583, 0xffffffff, 0x00000100, 0x0409, 0xffffffff, 0x00000100, - 0x082a, 0xffffffff, 0x00000104, - 0x0993, 0x000c0000, 0x000c0000, - 0x0992, 0x000c0000, 0x000c0000, - 0x0bd4, 0x00000001, 0x00000001, - 0x0c33, 0xc0000fff, 0x00000104, - 0x3079, 0x00000001, 0x00000001, + mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104, + mmMC_CITF_MISC_WR_CG, 0x000c0000, 0x000c0000, + mmMC_CITF_MISC_RD_CG, 0x000c0000, 0x000c0000, + mmHDP_MEM_POWER_LS, 0x00000001, 0x00000001, + mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104, + mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001, 0x3430, 0xfffffff0, 0x00000100, - 0x3630, 0xfffffff0, 0x00000100 + 0x3630, 0xfffffff0, 0x00000100, }; static u32 si_pcie_rreg(struct amdgpu_device *adev, u32 reg) @@ -1001,24 +1010,81 @@ static struct amdgpu_allowed_register_entry si_allowed_read_registers[] = { {PA_SC_RASTER_CONFIG, false, true}, }; -static uint32_t si_read_indexed_register(struct amdgpu_device *adev, - u32 se_num, u32 sh_num, - u32 reg_offset) +static uint32_t si_get_register_value(struct amdgpu_device *adev, + bool indexed, u32 se_num, + u32 sh_num, u32 reg_offset) { - uint32_t val; + if (indexed) { + uint32_t val; + unsigned se_idx = (se_num == 0xffffffff) ? 0 : se_num; + unsigned sh_idx = (sh_num == 0xffffffff) ? 0 : sh_num; + + switch (reg_offset) { + case mmCC_RB_BACKEND_DISABLE: + return adev->gfx.config.rb_config[se_idx][sh_idx].rb_backend_disable; + case mmGC_USER_RB_BACKEND_DISABLE: + return adev->gfx.config.rb_config[se_idx][sh_idx].user_rb_backend_disable; + case mmPA_SC_RASTER_CONFIG: + return adev->gfx.config.rb_config[se_idx][sh_idx].raster_config; + } - mutex_lock(&adev->grbm_idx_mutex); - if (se_num != 0xffffffff || sh_num != 0xffffffff) - amdgpu_gfx_select_se_sh(adev, se_num, sh_num, 0xffffffff); + mutex_lock(&adev->grbm_idx_mutex); + if (se_num != 0xffffffff || sh_num != 0xffffffff) + amdgpu_gfx_select_se_sh(adev, se_num, sh_num, 0xffffffff); - val = RREG32(reg_offset); + val = RREG32(reg_offset); - if (se_num != 0xffffffff || sh_num != 0xffffffff) - amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); - mutex_unlock(&adev->grbm_idx_mutex); - return val; + if (se_num != 0xffffffff || sh_num != 0xffffffff) + amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); + mutex_unlock(&adev->grbm_idx_mutex); + return val; + } else { + unsigned idx; + + switch (reg_offset) { + case mmGB_ADDR_CONFIG: + return adev->gfx.config.gb_addr_config; + case mmMC_ARB_RAMCFG: + return adev->gfx.config.mc_arb_ramcfg; + case mmGB_TILE_MODE0: + case mmGB_TILE_MODE1: + case mmGB_TILE_MODE2: + case mmGB_TILE_MODE3: + case mmGB_TILE_MODE4: + case mmGB_TILE_MODE5: + case mmGB_TILE_MODE6: + case mmGB_TILE_MODE7: + case mmGB_TILE_MODE8: + case mmGB_TILE_MODE9: + case mmGB_TILE_MODE10: + case mmGB_TILE_MODE11: + case mmGB_TILE_MODE12: + case mmGB_TILE_MODE13: + case mmGB_TILE_MODE14: + case mmGB_TILE_MODE15: + case mmGB_TILE_MODE16: + case mmGB_TILE_MODE17: + case mmGB_TILE_MODE18: + case mmGB_TILE_MODE19: + case mmGB_TILE_MODE20: + case mmGB_TILE_MODE21: + case mmGB_TILE_MODE22: + case mmGB_TILE_MODE23: + case mmGB_TILE_MODE24: + case mmGB_TILE_MODE25: + case mmGB_TILE_MODE26: + case mmGB_TILE_MODE27: + case mmGB_TILE_MODE28: + case mmGB_TILE_MODE29: + case mmGB_TILE_MODE30: + case mmGB_TILE_MODE31: + idx = (reg_offset - mmGB_TILE_MODE0); + return adev->gfx.config.tile_mode_array[idx]; + default: + return RREG32(reg_offset); + } + } } - static int si_read_register(struct amdgpu_device *adev, u32 se_num, u32 sh_num, u32 reg_offset, u32 *value) { @@ -1030,10 +1096,9 @@ static int si_read_register(struct amdgpu_device *adev, u32 se_num, continue; if (!si_allowed_read_registers[i].untouched) - *value = si_allowed_read_registers[i].grbm_indexed ? - si_read_indexed_register(adev, se_num, - sh_num, reg_offset) : - RREG32(reg_offset); + *value = si_get_register_value(adev, + si_allowed_read_registers[i].grbm_indexed, + se_num, sh_num, reg_offset); return 0; } return -EINVAL; @@ -1129,13 +1194,12 @@ static int si_set_uvd_clocks(struct amdgpu_device *adev, u32 vclk, u32 dclk) static void si_detect_hw_virtualization(struct amdgpu_device *adev) { if (is_virtual_machine()) /* passthrough mode */ - adev->virtualization.virtual_caps |= AMDGPU_PASSTHROUGH_MODE; + adev->virt.caps |= AMDGPU_PASSTHROUGH_MODE; } static const struct amdgpu_asic_funcs si_asic_funcs = { .read_disabled_bios = &si_read_disabled_bios, - .detect_hw_virtualization = si_detect_hw_virtualization, .read_register = &si_read_register, .reset = &si_asic_reset, .set_vga_state = &si_vga_set_state, @@ -1852,6 +1916,8 @@ static const struct amdgpu_ip_block_version si_common_ip_block = int si_set_ip_blocks(struct amdgpu_device *adev) { + si_detect_hw_virtualization(adev); + switch (adev->asic_type) { case CHIP_VERDE: case CHIP_TAHITI: diff --git a/drivers/gpu/drm/amd/amdgpu/si_dma.c b/drivers/gpu/drm/amd/amdgpu/si_dma.c index 3dd552ae0b59..3372a071bb85 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_dma.c +++ b/drivers/gpu/drm/amd/amdgpu/si_dma.c @@ -24,7 +24,7 @@ #include <drm/drmP.h> #include "amdgpu.h" #include "amdgpu_trace.h" -#include "si/sid.h" +#include "sid.h" const u32 sdma_offsets[SDMA_MAX_INSTANCE] = { @@ -301,7 +301,7 @@ static int si_dma_ring_test_ib(struct amdgpu_ring *ring, long timeout) ib.ptr[2] = upper_32_bits(gpu_addr) & 0xff; ib.ptr[3] = 0xDEADBEEF; ib.length_dw = 4; - r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f); + r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f); if (r) goto err1; diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c index 6e150db8f380..f55e45b52fbc 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c @@ -26,7 +26,7 @@ #include "amdgpu_pm.h" #include "amdgpu_dpm.h" #include "amdgpu_atombios.h" -#include "si/sid.h" +#include "sid.h" #include "r600_dpm.h" #include "si_dpm.h" #include "atom.h" @@ -3009,29 +3009,6 @@ static int si_init_smc_spll_table(struct amdgpu_device *adev) return ret; } -struct si_dpm_quirk { - u32 chip_vendor; - u32 chip_device; - u32 subsys_vendor; - u32 subsys_device; - u32 max_sclk; - u32 max_mclk; -}; - -/* cards with dpm stability problems */ -static struct si_dpm_quirk si_dpm_quirk_list[] = { - /* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */ - { PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6810, 0x174b, 0xe271, 85000, 90000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x1462, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x1043, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x148c, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6810, 0x1682, 0x9275, 0, 120000 }, - { 0, 0, 0, 0 }, -}; - static u16 si_get_lower_of_leakage_and_vce_voltage(struct amdgpu_device *adev, u16 vce_voltage) { @@ -3477,18 +3454,8 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev, u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; u32 max_sclk = 0, max_mclk = 0; int i; - struct si_dpm_quirk *p = si_dpm_quirk_list; - /* limit all SI kickers */ - if (adev->asic_type == CHIP_PITCAIRN) { - if ((adev->pdev->revision == 0x81) || - (adev->pdev->device == 0x6810) || - (adev->pdev->device == 0x6811) || - (adev->pdev->device == 0x6816) || - (adev->pdev->device == 0x6817) || - (adev->pdev->device == 0x6806)) - max_mclk = 120000; - } else if (adev->asic_type == CHIP_HAINAN) { + if (adev->asic_type == CHIP_HAINAN) { if ((adev->pdev->revision == 0x81) || (adev->pdev->revision == 0x83) || (adev->pdev->revision == 0xC3) || @@ -3498,18 +3465,6 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev, max_sclk = 75000; } } - /* Apply dpm quirks */ - while (p && p->chip_device != 0) { - if (adev->pdev->vendor == p->chip_vendor && - adev->pdev->device == p->chip_device && - adev->pdev->subsystem_vendor == p->subsys_vendor && - adev->pdev->subsystem_device == p->subsys_device) { - max_sclk = p->max_sclk; - max_mclk = p->max_mclk; - break; - } - ++p; - } if (rps->vce_active) { rps->evclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].evclk; @@ -3906,25 +3861,25 @@ static int si_restrict_performance_levels_before_switch(struct amdgpu_device *ad } static int si_dpm_force_performance_level(struct amdgpu_device *adev, - enum amdgpu_dpm_forced_level level) + enum amd_dpm_forced_level level) { struct amdgpu_ps *rps = adev->pm.dpm.current_ps; struct si_ps *ps = si_get_ps(rps); u32 levels = ps->performance_level_count; - if (level == AMDGPU_DPM_FORCED_LEVEL_HIGH) { + if (level == AMD_DPM_FORCED_LEVEL_HIGH) { if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK) return -EINVAL; if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK) return -EINVAL; - } else if (level == AMDGPU_DPM_FORCED_LEVEL_LOW) { + } else if (level == AMD_DPM_FORCED_LEVEL_LOW) { if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) return -EINVAL; if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, 1) != PPSMC_Result_OK) return -EINVAL; - } else if (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) { + } else if (level == AMD_DPM_FORCED_LEVEL_AUTO) { if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) return -EINVAL; @@ -7746,7 +7701,7 @@ static int si_dpm_sw_init(void *handle) /* default to balanced state */ adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; - adev->pm.dpm.forced_level = AMDGPU_DPM_FORCED_LEVEL_AUTO; + adev->pm.dpm.forced_level = AMD_DPM_FORCED_LEVEL_AUTO; adev->pm.default_sclk = adev->clock.default_sclk; adev->pm.default_mclk = adev->clock.default_mclk; adev->pm.current_sclk = adev->clock.default_sclk; @@ -8072,11 +8027,3 @@ static void si_dpm_set_irq_funcs(struct amdgpu_device *adev) adev->pm.dpm.thermal.irq.funcs = &si_dpm_irq_funcs; } -const struct amdgpu_ip_block_version si_dpm_ip_block = -{ - .type = AMD_IP_BLOCK_TYPE_SMC, - .major = 6, - .minor = 0, - .rev = 0, - .funcs = &si_dpm_ip_funcs, -}; diff --git a/drivers/gpu/drm/amd/amdgpu/si_enums.h b/drivers/gpu/drm/amd/amdgpu/si_enums.h index fde2086246fa..dc9e0e6b4558 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_enums.h +++ b/drivers/gpu/drm/amd/amdgpu/si_enums.h @@ -143,8 +143,8 @@ #define RLC_CLEAR_STATE_DESCRIPTOR_OFFSET 0x3D #define TAHITI_GB_ADDR_CONFIG_GOLDEN 0x12011003 -#define VERDE_GB_ADDR_CONFIG_GOLDEN 0x12010002 -#define HAINAN_GB_ADDR_CONFIG_GOLDEN 0x02010001 +#define VERDE_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define HAINAN_GB_ADDR_CONFIG_GOLDEN 0x02011003 #define PACKET3(op, n) ((RADEON_PACKET_TYPE3 << 30) | \ (((op) & 0xFF) << 8) | \ diff --git a/drivers/gpu/drm/amd/amdgpu/si_ih.c b/drivers/gpu/drm/amd/amdgpu/si_ih.c index db0f36846661..81f90800ba73 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/si_ih.c @@ -23,7 +23,7 @@ #include "drmP.h" #include "amdgpu.h" #include "amdgpu_ih.h" -#include "si/sid.h" +#include "sid.h" #include "si_ih.h" static void si_ih_set_interrupt_funcs(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/si_smc.c b/drivers/gpu/drm/amd/amdgpu/si_smc.c index 668ba99d6c05..0726bc3b6f90 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_smc.c +++ b/drivers/gpu/drm/amd/amdgpu/si_smc.c @@ -25,7 +25,7 @@ #include <linux/firmware.h> #include "drmP.h" #include "amdgpu.h" -#include "si/sid.h" +#include "sid.h" #include "ppsmc.h" #include "amdgpu_ucode.h" #include "sislands_smc.h" diff --git a/drivers/gpu/drm/amd/include/asic_reg/si/sid.h b/drivers/gpu/drm/amd/amdgpu/sid.h index c57eff159374..c57eff159374 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/si/sid.h +++ b/drivers/gpu/drm/amd/amdgpu/sid.h diff --git a/drivers/gpu/drm/amd/amdgpu/smu_ucode_xfer_vi.h b/drivers/gpu/drm/amd/amdgpu/smu_ucode_xfer_vi.h deleted file mode 100644 index 880152c0f775..000000000000 --- a/drivers/gpu/drm/amd/amdgpu/smu_ucode_xfer_vi.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef SMU_UCODE_XFER_VI_H -#define SMU_UCODE_XFER_VI_H - -#define SMU_DRAMData_TOC_VERSION 1 -#define MAX_IH_REGISTER_COUNT 65535 -#define SMU_DIGEST_SIZE_BYTES 20 -#define SMU_FB_SIZE_BYTES 1048576 -#define SMU_MAX_ENTRIES 12 - -#define UCODE_ID_SMU 0 -#define UCODE_ID_SDMA0 1 -#define UCODE_ID_SDMA1 2 -#define UCODE_ID_CP_CE 3 -#define UCODE_ID_CP_PFP 4 -#define UCODE_ID_CP_ME 5 -#define UCODE_ID_CP_MEC 6 -#define UCODE_ID_CP_MEC_JT1 7 -#define UCODE_ID_CP_MEC_JT2 8 -#define UCODE_ID_GMCON_RENG 9 -#define UCODE_ID_RLC_G 10 -#define UCODE_ID_IH_REG_RESTORE 11 -#define UCODE_ID_VBIOS 12 -#define UCODE_ID_MISC_METADATA 13 -#define UCODE_ID_SMU_SK 14 -#define UCODE_ID_RLC_SCRATCH 32 -#define UCODE_ID_RLC_SRM_ARAM 33 -#define UCODE_ID_RLC_SRM_DRAM 34 -#define UCODE_ID_MEC_STORAGE 35 -#define UCODE_ID_VBIOS_PARAMETERS 36 -#define UCODE_META_DATA 0xFF - -#define UCODE_ID_SMU_MASK 0x00000001 -#define UCODE_ID_SDMA0_MASK 0x00000002 -#define UCODE_ID_SDMA1_MASK 0x00000004 -#define UCODE_ID_CP_CE_MASK 0x00000008 -#define UCODE_ID_CP_PFP_MASK 0x00000010 -#define UCODE_ID_CP_ME_MASK 0x00000020 -#define UCODE_ID_CP_MEC_MASK 0x00000040 -#define UCODE_ID_CP_MEC_JT1_MASK 0x00000080 -#define UCODE_ID_CP_MEC_JT2_MASK 0x00000100 -#define UCODE_ID_GMCON_RENG_MASK 0x00000200 -#define UCODE_ID_RLC_G_MASK 0x00000400 -#define UCODE_ID_IH_REG_RESTORE_MASK 0x00000800 -#define UCODE_ID_VBIOS_MASK 0x00001000 - -#define UCODE_FLAG_UNHALT_MASK 0x1 - -struct SMU_Entry { -#ifndef __BIG_ENDIAN - uint16_t id; - uint16_t version; - uint32_t image_addr_high; - uint32_t image_addr_low; - uint32_t meta_data_addr_high; - uint32_t meta_data_addr_low; - uint32_t data_size_byte; - uint16_t flags; - uint16_t num_register_entries; -#else - uint16_t version; - uint16_t id; - uint32_t image_addr_high; - uint32_t image_addr_low; - uint32_t meta_data_addr_high; - uint32_t meta_data_addr_low; - uint32_t data_size_byte; - uint16_t num_register_entries; - uint16_t flags; -#endif -}; - -struct SMU_DRAMData_TOC { - uint32_t structure_version; - uint32_t num_entries; - struct SMU_Entry entry[SMU_MAX_ENTRIES]; -}; - -#endif diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c index 7fb9137dd89b..b34cefc7ebd5 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c @@ -159,9 +159,6 @@ static int uvd_v4_2_hw_init(void *handle) uvd_v4_2_enable_mgcg(adev, true); amdgpu_asic_set_uvd_clocks(adev, 10000, 10000); - r = uvd_v4_2_start(adev); - if (r) - goto done; ring->ready = true; r = amdgpu_ring_test_ring(ring); @@ -198,7 +195,6 @@ static int uvd_v4_2_hw_init(void *handle) amdgpu_ring_commit(ring); done: - if (!r) DRM_INFO("UVD initialized successfully.\n"); @@ -217,7 +213,9 @@ static int uvd_v4_2_hw_fini(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; struct amdgpu_ring *ring = &adev->uvd.ring; - uvd_v4_2_stop(adev); + if (RREG32(mmUVD_STATUS) != 0) + uvd_v4_2_stop(adev); + ring->ready = false; return 0; @@ -267,37 +265,26 @@ static int uvd_v4_2_start(struct amdgpu_device *adev) struct amdgpu_ring *ring = &adev->uvd.ring; uint32_t rb_bufsz; int i, j, r; + u32 tmp; /* disable byte swapping */ u32 lmi_swap_cntl = 0; u32 mp_swap_cntl = 0; - WREG32(mmUVD_CGC_GATE, 0); - uvd_v4_2_set_dcm(adev, true); - - uvd_v4_2_mc_resume(adev); + /* set uvd busy */ + WREG32_P(mmUVD_STATUS, 1<<2, ~(1<<2)); - /* disable interupt */ - WREG32_P(mmUVD_MASTINT_EN, 0, ~(1 << 1)); - - /* Stall UMC and register bus before resetting VCPU */ - WREG32_P(mmUVD_LMI_CTRL2, 1 << 8, ~(1 << 8)); - mdelay(1); - - /* put LMI, VCPU, RBC etc... into reset */ - WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__LMI_SOFT_RESET_MASK | - UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK | UVD_SOFT_RESET__LBSI_SOFT_RESET_MASK | - UVD_SOFT_RESET__RBC_SOFT_RESET_MASK | UVD_SOFT_RESET__CSM_SOFT_RESET_MASK | - UVD_SOFT_RESET__CXW_SOFT_RESET_MASK | UVD_SOFT_RESET__TAP_SOFT_RESET_MASK | - UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK); - mdelay(5); + uvd_v4_2_set_dcm(adev, true); + WREG32(mmUVD_CGC_GATE, 0); /* take UVD block out of reset */ WREG32_P(mmSRBM_SOFT_RESET, 0, ~SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK); mdelay(5); - /* initialize UVD memory controller */ - WREG32(mmUVD_LMI_CTRL, 0x40 | (1 << 8) | (1 << 13) | - (1 << 21) | (1 << 9) | (1 << 20)); + /* enable VCPU clock */ + WREG32(mmUVD_VCPU_CNTL, 1 << 9); + + /* disable interupt */ + WREG32_P(mmUVD_MASTINT_EN, 0, ~(1 << 1)); #ifdef __BIG_ENDIAN /* swap (8 in 32) RB and IB */ @@ -306,6 +293,11 @@ static int uvd_v4_2_start(struct amdgpu_device *adev) #endif WREG32(mmUVD_LMI_SWAP_CNTL, lmi_swap_cntl); WREG32(mmUVD_MP_SWAP_CNTL, mp_swap_cntl); + /* initialize UVD memory controller */ + WREG32(mmUVD_LMI_CTRL, 0x203108); + + tmp = RREG32(mmUVD_MPC_CNTL); + WREG32(mmUVD_MPC_CNTL, tmp | 0x10); WREG32(mmUVD_MPC_SET_MUXA0, 0x40c2040); WREG32(mmUVD_MPC_SET_MUXA1, 0x0); @@ -314,18 +306,20 @@ static int uvd_v4_2_start(struct amdgpu_device *adev) WREG32(mmUVD_MPC_SET_ALU, 0); WREG32(mmUVD_MPC_SET_MUX, 0x88); - /* take all subblocks out of reset, except VCPU */ - WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK); - mdelay(5); + uvd_v4_2_mc_resume(adev); - /* enable VCPU clock */ - WREG32(mmUVD_VCPU_CNTL, 1 << 9); + tmp = RREG32_UVD_CTX(ixUVD_LMI_CACHE_CTRL); + WREG32_UVD_CTX(ixUVD_LMI_CACHE_CTRL, tmp & (~0x10)); /* enable UMC */ WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8)); - /* boot up the VCPU */ - WREG32(mmUVD_SOFT_RESET, 0); + WREG32_P(mmUVD_SOFT_RESET, 0, ~UVD_SOFT_RESET__LMI_SOFT_RESET_MASK); + + WREG32_P(mmUVD_SOFT_RESET, 0, ~UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK); + + WREG32_P(mmUVD_SOFT_RESET, 0, ~UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK); + mdelay(10); for (i = 0; i < 10; ++i) { @@ -357,6 +351,8 @@ static int uvd_v4_2_start(struct amdgpu_device *adev) /* enable interupt */ WREG32_P(mmUVD_MASTINT_EN, 3<<1, ~(3 << 1)); + WREG32_P(mmUVD_STATUS, 0, ~(1<<2)); + /* force RBC into idle state */ WREG32(mmUVD_RBC_RB_CNTL, 0x11010101); @@ -393,22 +389,57 @@ static int uvd_v4_2_start(struct amdgpu_device *adev) */ static void uvd_v4_2_stop(struct amdgpu_device *adev) { - /* force RBC into idle state */ + uint32_t i, j; + uint32_t status; + WREG32(mmUVD_RBC_RB_CNTL, 0x11010101); + for (i = 0; i < 10; ++i) { + for (j = 0; j < 100; ++j) { + status = RREG32(mmUVD_STATUS); + if (status & 2) + break; + mdelay(1); + } + if (status & 2) + break; + } + + for (i = 0; i < 10; ++i) { + for (j = 0; j < 100; ++j) { + status = RREG32(mmUVD_LMI_STATUS); + if (status & 0xf) + break; + mdelay(1); + } + if (status & 0xf) + break; + } + /* Stall UMC and register bus before resetting VCPU */ WREG32_P(mmUVD_LMI_CTRL2, 1 << 8, ~(1 << 8)); - mdelay(1); - /* put VCPU into reset */ - WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK); - mdelay(5); + for (i = 0; i < 10; ++i) { + for (j = 0; j < 100; ++j) { + status = RREG32(mmUVD_LMI_STATUS); + if (status & 0x240) + break; + mdelay(1); + } + if (status & 0x240) + break; + } - /* disable VCPU clock */ - WREG32(mmUVD_VCPU_CNTL, 0x0); + WREG32_P(0x3D49, 0, ~(1 << 2)); - /* Unstall UMC and register bus */ - WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8)); + WREG32_P(mmUVD_VCPU_CNTL, 0, ~(1 << 9)); + + /* put LMI, VCPU, RBC etc... into reset */ + WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__LMI_SOFT_RESET_MASK | + UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK | + UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK); + + WREG32(mmUVD_STATUS, 0); uvd_v4_2_set_dcm(adev, false); } @@ -694,8 +725,26 @@ static int uvd_v4_2_set_powergating_state(void *handle, if (state == AMD_PG_STATE_GATE) { uvd_v4_2_stop(adev); + if (adev->pg_flags & AMD_PG_SUPPORT_UVD && amdgpu_dpm == 0) { + if (!(RREG32_SMC(ixCURRENT_PG_STATUS) & + CURRENT_PG_STATUS__UVD_PG_STATUS_MASK)) { + WREG32(mmUVD_PGFSM_CONFIG, (UVD_PGFSM_CONFIG__UVD_PGFSM_FSM_ADDR_MASK | + UVD_PGFSM_CONFIG__UVD_PGFSM_POWER_DOWN_MASK | + UVD_PGFSM_CONFIG__UVD_PGFSM_P1_SELECT_MASK)); + mdelay(20); + } + } return 0; } else { + if (adev->pg_flags & AMD_PG_SUPPORT_UVD && amdgpu_dpm == 0) { + if (RREG32_SMC(ixCURRENT_PG_STATUS) & + CURRENT_PG_STATUS__UVD_PG_STATUS_MASK) { + WREG32(mmUVD_PGFSM_CONFIG, (UVD_PGFSM_CONFIG__UVD_PGFSM_FSM_ADDR_MASK | + UVD_PGFSM_CONFIG__UVD_PGFSM_POWER_UP_MASK | + UVD_PGFSM_CONFIG__UVD_PGFSM_P1_SELECT_MASK)); + mdelay(30); + } + } return uvd_v4_2_start(adev); } } diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c index 6de6becce745..ad8c02e423d4 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c @@ -152,9 +152,9 @@ static int uvd_v5_0_hw_init(void *handle) uint32_t tmp; int r; - r = uvd_v5_0_start(adev); - if (r) - goto done; + amdgpu_asic_set_uvd_clocks(adev, 10000, 10000); + uvd_v5_0_set_clockgating_state(adev, AMD_CG_STATE_UNGATE); + uvd_v5_0_enable_mgcg(adev, true); ring->ready = true; r = amdgpu_ring_test_ring(ring); @@ -189,11 +189,13 @@ static int uvd_v5_0_hw_init(void *handle) amdgpu_ring_write(ring, 3); amdgpu_ring_commit(ring); + done: if (!r) DRM_INFO("UVD initialized successfully.\n"); return r; + } /** @@ -208,7 +210,9 @@ static int uvd_v5_0_hw_fini(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; struct amdgpu_ring *ring = &adev->uvd.ring; - uvd_v5_0_stop(adev); + if (RREG32(mmUVD_STATUS) != 0) + uvd_v5_0_stop(adev); + ring->ready = false; return 0; @@ -310,10 +314,6 @@ static int uvd_v5_0_start(struct amdgpu_device *adev) uvd_v5_0_mc_resume(adev); - amdgpu_asic_set_uvd_clocks(adev, 10000, 10000); - uvd_v5_0_set_clockgating_state(adev, AMD_CG_STATE_UNGATE); - uvd_v5_0_enable_mgcg(adev, true); - /* disable interupt */ WREG32_P(mmUVD_MASTINT_EN, 0, ~(1 << 1)); @@ -456,6 +456,8 @@ static void uvd_v5_0_stop(struct amdgpu_device *adev) /* Unstall UMC and register bus */ WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8)); + + WREG32(mmUVD_STATUS, 0); } /** @@ -792,9 +794,6 @@ static int uvd_v5_0_set_clockgating_state(void *handle, struct amdgpu_device *adev = (struct amdgpu_device *)handle; bool enable = (state == AMD_CG_STATE_GATE) ? true : false; - if (!(adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) - return 0; - if (enable) { /* wait for STATUS to clear */ if (uvd_v5_0_wait_for_idle(handle)) @@ -822,16 +821,40 @@ static int uvd_v5_0_set_powergating_state(void *handle, * the smc and the hw blocks */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - if (!(adev->pg_flags & AMD_PG_SUPPORT_UVD)) - return 0; + int ret = 0; if (state == AMD_PG_STATE_GATE) { uvd_v5_0_stop(adev); - return 0; } else { - return uvd_v5_0_start(adev); + ret = uvd_v5_0_start(adev); + if (ret) + goto out; + } + +out: + return ret; +} + +static void uvd_v5_0_get_clockgating_state(void *handle, u32 *flags) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int data; + + mutex_lock(&adev->pm.mutex); + + if (RREG32_SMC(ixCURRENT_PG_STATUS) & + CURRENT_PG_STATUS__UVD_PG_STATUS_MASK) { + DRM_INFO("Cannot get clockgating state when UVD is powergated.\n"); + goto out; } + + /* AMD_CG_SUPPORT_UVD_MGCG */ + data = RREG32(mmUVD_CGC_CTRL); + if (data & UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK) + *flags |= AMD_CG_SUPPORT_UVD_MGCG; + +out: + mutex_unlock(&adev->pm.mutex); } static const struct amd_ip_funcs uvd_v5_0_ip_funcs = { @@ -849,6 +872,7 @@ static const struct amd_ip_funcs uvd_v5_0_ip_funcs = { .soft_reset = uvd_v5_0_soft_reset, .set_clockgating_state = uvd_v5_0_set_clockgating_state, .set_powergating_state = uvd_v5_0_set_powergating_state, + .get_clockgating_state = uvd_v5_0_get_clockgating_state, }; static const struct amdgpu_ring_funcs uvd_v5_0_ring_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index ba0bbf7138dc..18a6de4e1512 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -155,9 +155,9 @@ static int uvd_v6_0_hw_init(void *handle) uint32_t tmp; int r; - r = uvd_v6_0_start(adev); - if (r) - goto done; + amdgpu_asic_set_uvd_clocks(adev, 10000, 10000); + uvd_v6_0_set_clockgating_state(adev, AMD_CG_STATE_UNGATE); + uvd_v6_0_enable_mgcg(adev, true); ring->ready = true; r = amdgpu_ring_test_ring(ring); @@ -212,7 +212,9 @@ static int uvd_v6_0_hw_fini(void *handle) struct amdgpu_device *adev = (struct amdgpu_device *)handle; struct amdgpu_ring *ring = &adev->uvd.ring; - uvd_v6_0_stop(adev); + if (RREG32(mmUVD_STATUS) != 0) + uvd_v6_0_stop(adev); + ring->ready = false; return 0; @@ -397,9 +399,6 @@ static int uvd_v6_0_start(struct amdgpu_device *adev) lmi_swap_cntl = 0; mp_swap_cntl = 0; - amdgpu_asic_set_uvd_clocks(adev, 10000, 10000); - uvd_v6_0_set_clockgating_state(adev, AMD_CG_STATE_UNGATE); - uvd_v6_0_enable_mgcg(adev, true); uvd_v6_0_mc_resume(adev); /* disable interupt */ @@ -554,6 +553,8 @@ static void uvd_v6_0_stop(struct amdgpu_device *adev) /* Unstall UMC and register bus */ WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8)); + + WREG32(mmUVD_STATUS, 0); } /** @@ -1018,9 +1019,6 @@ static int uvd_v6_0_set_clockgating_state(void *handle, struct amdgpu_device *adev = (struct amdgpu_device *)handle; bool enable = (state == AMD_CG_STATE_GATE) ? true : false; - if (!(adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) - return 0; - if (enable) { /* wait for STATUS to clear */ if (uvd_v6_0_wait_for_idle(handle)) @@ -1047,18 +1045,42 @@ static int uvd_v6_0_set_powergating_state(void *handle, * the smc and the hw blocks */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - if (!(adev->pg_flags & AMD_PG_SUPPORT_UVD)) - return 0; + int ret = 0; WREG32(mmUVD_POWER_STATUS, UVD_POWER_STATUS__UVD_PG_EN_MASK); if (state == AMD_PG_STATE_GATE) { uvd_v6_0_stop(adev); - return 0; } else { - return uvd_v6_0_start(adev); + ret = uvd_v6_0_start(adev); + if (ret) + goto out; + } + +out: + return ret; +} + +static void uvd_v6_0_get_clockgating_state(void *handle, u32 *flags) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int data; + + mutex_lock(&adev->pm.mutex); + + if (RREG32_SMC(ixCURRENT_PG_STATUS) & + CURRENT_PG_STATUS__UVD_PG_STATUS_MASK) { + DRM_INFO("Cannot get clockgating state when UVD is powergated.\n"); + goto out; } + + /* AMD_CG_SUPPORT_UVD_MGCG */ + data = RREG32(mmUVD_CGC_CTRL); + if (data & UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK) + *flags |= AMD_CG_SUPPORT_UVD_MGCG; + +out: + mutex_unlock(&adev->pm.mutex); } static const struct amd_ip_funcs uvd_v6_0_ip_funcs = { @@ -1079,6 +1101,7 @@ static const struct amd_ip_funcs uvd_v6_0_ip_funcs = { .post_soft_reset = uvd_v6_0_post_soft_reset, .set_clockgating_state = uvd_v6_0_set_clockgating_state, .set_powergating_state = uvd_v6_0_set_powergating_state, + .get_clockgating_state = uvd_v6_0_get_clockgating_state, }; static const struct amdgpu_ring_funcs uvd_v6_0_ring_phys_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c index 38ed903dd6f8..9ea99348e493 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c @@ -42,10 +42,9 @@ #define VCE_V2_0_DATA_SIZE (23552 * AMDGPU_MAX_VCE_HANDLES) #define VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK 0x02 -static void vce_v2_0_mc_resume(struct amdgpu_device *adev); static void vce_v2_0_set_ring_funcs(struct amdgpu_device *adev); static void vce_v2_0_set_irq_funcs(struct amdgpu_device *adev); -static int vce_v2_0_wait_for_idle(void *handle); + /** * vce_v2_0_ring_get_rptr - get read pointer * @@ -140,6 +139,86 @@ static int vce_v2_0_firmware_loaded(struct amdgpu_device *adev) return -ETIMEDOUT; } +static void vce_v2_0_disable_cg(struct amdgpu_device *adev) +{ + WREG32(mmVCE_CGTT_CLK_OVERRIDE, 7); +} + +static void vce_v2_0_init_cg(struct amdgpu_device *adev) +{ + u32 tmp; + + tmp = RREG32(mmVCE_CLOCK_GATING_A); + tmp &= ~0xfff; + tmp |= ((0 << 0) | (4 << 4)); + tmp |= 0x40000; + WREG32(mmVCE_CLOCK_GATING_A, tmp); + + tmp = RREG32(mmVCE_UENC_CLOCK_GATING); + tmp &= ~0xfff; + tmp |= ((0 << 0) | (4 << 4)); + WREG32(mmVCE_UENC_CLOCK_GATING, tmp); + + tmp = RREG32(mmVCE_CLOCK_GATING_B); + tmp |= 0x10; + tmp &= ~0x100000; + WREG32(mmVCE_CLOCK_GATING_B, tmp); +} + +static void vce_v2_0_mc_resume(struct amdgpu_device *adev) +{ + uint64_t addr = adev->vce.gpu_addr; + uint32_t size; + + WREG32_P(mmVCE_CLOCK_GATING_A, 0, ~(1 << 16)); + WREG32_P(mmVCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000); + WREG32_P(mmVCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F); + WREG32(mmVCE_CLOCK_GATING_B, 0xf7); + + WREG32(mmVCE_LMI_CTRL, 0x00398000); + WREG32_P(mmVCE_LMI_CACHE_CTRL, 0x0, ~0x1); + WREG32(mmVCE_LMI_SWAP_CNTL, 0); + WREG32(mmVCE_LMI_SWAP_CNTL1, 0); + WREG32(mmVCE_LMI_VM_CTRL, 0); + + addr += AMDGPU_VCE_FIRMWARE_OFFSET; + size = VCE_V2_0_FW_SIZE; + WREG32(mmVCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff); + WREG32(mmVCE_VCPU_CACHE_SIZE0, size); + + addr += size; + size = VCE_V2_0_STACK_SIZE; + WREG32(mmVCE_VCPU_CACHE_OFFSET1, addr & 0x7fffffff); + WREG32(mmVCE_VCPU_CACHE_SIZE1, size); + + addr += size; + size = VCE_V2_0_DATA_SIZE; + WREG32(mmVCE_VCPU_CACHE_OFFSET2, addr & 0x7fffffff); + WREG32(mmVCE_VCPU_CACHE_SIZE2, size); + + WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100); + WREG32_FIELD(VCE_SYS_INT_EN, VCE_SYS_INT_TRAP_INTERRUPT_EN, 1); +} + +static bool vce_v2_0_is_idle(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + return !(RREG32(mmSRBM_STATUS2) & SRBM_STATUS2__VCE_BUSY_MASK); +} + +static int vce_v2_0_wait_for_idle(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + unsigned i; + + for (i = 0; i < adev->usec_timeout; i++) { + if (vce_v2_0_is_idle(handle)) + return 0; + } + return -ETIMEDOUT; +} + /** * vce_v2_0_start - start VCE block * @@ -152,11 +231,14 @@ static int vce_v2_0_start(struct amdgpu_device *adev) struct amdgpu_ring *ring; int r; - vce_v2_0_mc_resume(adev); - /* set BUSY flag */ WREG32_P(mmVCE_STATUS, 1, ~1); + vce_v2_0_init_cg(adev); + vce_v2_0_disable_cg(adev); + + vce_v2_0_mc_resume(adev); + ring = &adev->vce.ring[0]; WREG32(mmVCE_RB_RPTR, ring->wptr); WREG32(mmVCE_RB_WPTR, ring->wptr); @@ -189,6 +271,145 @@ static int vce_v2_0_start(struct amdgpu_device *adev) return 0; } +static int vce_v2_0_stop(struct amdgpu_device *adev) +{ + int i, j; + int status; + + if (vce_v2_0_lmi_clean(adev)) { + DRM_INFO("vce is not idle \n"); + return 0; + } +/* + for (i = 0; i < 10; ++i) { + for (j = 0; j < 100; ++j) { + status = RREG32(mmVCE_FW_REG_STATUS); + if (!(status & 1)) + break; + mdelay(1); + } + break; + } +*/ + if (vce_v2_0_wait_for_idle(adev)) { + DRM_INFO("VCE is busy, Can't set clock gateing"); + return 0; + } + + /* Stall UMC and register bus before resetting VCPU */ + WREG32_P(mmVCE_LMI_CTRL2, 1 << 8, ~(1 << 8)); + + for (i = 0; i < 10; ++i) { + for (j = 0; j < 100; ++j) { + status = RREG32(mmVCE_LMI_STATUS); + if (status & 0x240) + break; + mdelay(1); + } + break; + } + + WREG32_P(mmVCE_VCPU_CNTL, 0, ~0x80001); + + /* put LMI, VCPU, RBC etc... into reset */ + WREG32_P(mmVCE_SOFT_RESET, 1, ~0x1); + + WREG32(mmVCE_STATUS, 0); + + return 0; +} + +static void vce_v2_0_set_sw_cg(struct amdgpu_device *adev, bool gated) +{ + u32 tmp; + + if (gated) { + tmp = RREG32(mmVCE_CLOCK_GATING_B); + tmp |= 0xe70000; + WREG32(mmVCE_CLOCK_GATING_B, tmp); + + tmp = RREG32(mmVCE_UENC_CLOCK_GATING); + tmp |= 0xff000000; + WREG32(mmVCE_UENC_CLOCK_GATING, tmp); + + tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING); + tmp &= ~0x3fc; + WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp); + + WREG32(mmVCE_CGTT_CLK_OVERRIDE, 0); + } else { + tmp = RREG32(mmVCE_CLOCK_GATING_B); + tmp |= 0xe7; + tmp &= ~0xe70000; + WREG32(mmVCE_CLOCK_GATING_B, tmp); + + tmp = RREG32(mmVCE_UENC_CLOCK_GATING); + tmp |= 0x1fe000; + tmp &= ~0xff000000; + WREG32(mmVCE_UENC_CLOCK_GATING, tmp); + + tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING); + tmp |= 0x3fc; + WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp); + } +} + +static void vce_v2_0_set_dyn_cg(struct amdgpu_device *adev, bool gated) +{ + u32 orig, tmp; + +/* LMI_MC/LMI_UMC always set in dynamic, + * set {CGC_*_GATE_MODE, CGC_*_SW_GATE} = {0, 0} + */ + tmp = RREG32(mmVCE_CLOCK_GATING_B); + tmp &= ~0x00060006; + +/* Exception for ECPU, IH, SEM, SYS blocks needs to be turned on/off by SW */ + if (gated) { + tmp |= 0xe10000; + WREG32(mmVCE_CLOCK_GATING_B, tmp); + } else { + tmp |= 0xe1; + tmp &= ~0xe10000; + WREG32(mmVCE_CLOCK_GATING_B, tmp); + } + + orig = tmp = RREG32(mmVCE_UENC_CLOCK_GATING); + tmp &= ~0x1fe000; + tmp &= ~0xff000000; + if (tmp != orig) + WREG32(mmVCE_UENC_CLOCK_GATING, tmp); + + orig = tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING); + tmp &= ~0x3fc; + if (tmp != orig) + WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp); + + /* set VCE_UENC_REG_CLOCK_GATING always in dynamic mode */ + WREG32(mmVCE_UENC_REG_CLOCK_GATING, 0x00); + + if(gated) + WREG32(mmVCE_CGTT_CLK_OVERRIDE, 0); +} + +static void vce_v2_0_enable_mgcg(struct amdgpu_device *adev, bool enable, + bool sw_cg) +{ + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)) { + if (sw_cg) + vce_v2_0_set_sw_cg(adev, true); + else + vce_v2_0_set_dyn_cg(adev, true); + } else { + vce_v2_0_disable_cg(adev); + + if (sw_cg) + vce_v2_0_set_sw_cg(adev, false); + else + vce_v2_0_set_dyn_cg(adev, false); + } +} + static int vce_v2_0_early_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; @@ -254,11 +475,8 @@ static int vce_v2_0_hw_init(void *handle) int r, i; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - r = vce_v2_0_start(adev); - /* this error mean vcpu not in running state, so just skip ring test, not stop driver initialize */ - if (r) - return 0; - + amdgpu_asic_set_vce_clocks(adev, 10000, 10000); + vce_v2_0_enable_mgcg(adev, true, false); for (i = 0; i < adev->vce.num_rings; i++) adev->vce.ring[i].ready = false; @@ -312,190 +530,6 @@ static int vce_v2_0_resume(void *handle) return r; } -static void vce_v2_0_set_sw_cg(struct amdgpu_device *adev, bool gated) -{ - u32 tmp; - - if (gated) { - tmp = RREG32(mmVCE_CLOCK_GATING_B); - tmp |= 0xe70000; - WREG32(mmVCE_CLOCK_GATING_B, tmp); - - tmp = RREG32(mmVCE_UENC_CLOCK_GATING); - tmp |= 0xff000000; - WREG32(mmVCE_UENC_CLOCK_GATING, tmp); - - tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING); - tmp &= ~0x3fc; - WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp); - - WREG32(mmVCE_CGTT_CLK_OVERRIDE, 0); - } else { - tmp = RREG32(mmVCE_CLOCK_GATING_B); - tmp |= 0xe7; - tmp &= ~0xe70000; - WREG32(mmVCE_CLOCK_GATING_B, tmp); - - tmp = RREG32(mmVCE_UENC_CLOCK_GATING); - tmp |= 0x1fe000; - tmp &= ~0xff000000; - WREG32(mmVCE_UENC_CLOCK_GATING, tmp); - - tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING); - tmp |= 0x3fc; - WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp); - } -} - -static void vce_v2_0_set_dyn_cg(struct amdgpu_device *adev, bool gated) -{ - if (vce_v2_0_wait_for_idle(adev)) { - DRM_INFO("VCE is busy, Can't set clock gateing"); - return; - } - - WREG32_P(mmVCE_LMI_CTRL2, 0x100, ~0x100); - - if (vce_v2_0_lmi_clean(adev)) { - DRM_INFO("LMI is busy, Can't set clock gateing"); - return; - } - - WREG32_P(mmVCE_VCPU_CNTL, 0, ~VCE_VCPU_CNTL__CLK_EN_MASK); - WREG32_P(mmVCE_SOFT_RESET, - VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK, - ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK); - WREG32(mmVCE_STATUS, 0); - - if (gated) - WREG32(mmVCE_CGTT_CLK_OVERRIDE, 0); - /* LMI_MC/LMI_UMC always set in dynamic, set {CGC_*_GATE_MODE, CGC_*_SW_GATE} = {0, 0} */ - if (gated) { - /* Force CLOCK OFF , set {CGC_*_GATE_MODE, CGC_*_SW_GATE} = {*, 1} */ - WREG32(mmVCE_CLOCK_GATING_B, 0xe90010); - } else { - /* Force CLOCK ON, set {CGC_*_GATE_MODE, CGC_*_SW_GATE} = {1, 0} */ - WREG32(mmVCE_CLOCK_GATING_B, 0x800f1); - } - - /* Set VCE_UENC_CLOCK_GATING always in dynamic mode {*_FORCE_ON, *_FORCE_OFF} = {0, 0}*/; - WREG32(mmVCE_UENC_CLOCK_GATING, 0x40); - - /* set VCE_UENC_REG_CLOCK_GATING always in dynamic mode */ - WREG32(mmVCE_UENC_REG_CLOCK_GATING, 0x00); - - WREG32_P(mmVCE_LMI_CTRL2, 0, ~0x100); - if(!gated) { - WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK, ~VCE_VCPU_CNTL__CLK_EN_MASK); - mdelay(100); - WREG32_P(mmVCE_SOFT_RESET, 0, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK); - - vce_v2_0_firmware_loaded(adev); - WREG32_P(mmVCE_STATUS, 0, ~VCE_STATUS__JOB_BUSY_MASK); - } -} - -static void vce_v2_0_disable_cg(struct amdgpu_device *adev) -{ - WREG32(mmVCE_CGTT_CLK_OVERRIDE, 7); -} - -static void vce_v2_0_enable_mgcg(struct amdgpu_device *adev, bool enable) -{ - bool sw_cg = false; - - if (enable && (adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)) { - if (sw_cg) - vce_v2_0_set_sw_cg(adev, true); - else - vce_v2_0_set_dyn_cg(adev, true); - } else { - vce_v2_0_disable_cg(adev); - - if (sw_cg) - vce_v2_0_set_sw_cg(adev, false); - else - vce_v2_0_set_dyn_cg(adev, false); - } -} - -static void vce_v2_0_init_cg(struct amdgpu_device *adev) -{ - u32 tmp; - - tmp = RREG32(mmVCE_CLOCK_GATING_A); - tmp &= ~0xfff; - tmp |= ((0 << 0) | (4 << 4)); - tmp |= 0x40000; - WREG32(mmVCE_CLOCK_GATING_A, tmp); - - tmp = RREG32(mmVCE_UENC_CLOCK_GATING); - tmp &= ~0xfff; - tmp |= ((0 << 0) | (4 << 4)); - WREG32(mmVCE_UENC_CLOCK_GATING, tmp); - - tmp = RREG32(mmVCE_CLOCK_GATING_B); - tmp |= 0x10; - tmp &= ~0x100000; - WREG32(mmVCE_CLOCK_GATING_B, tmp); -} - -static void vce_v2_0_mc_resume(struct amdgpu_device *adev) -{ - uint64_t addr = adev->vce.gpu_addr; - uint32_t size; - - WREG32_P(mmVCE_CLOCK_GATING_A, 0, ~(1 << 16)); - WREG32_P(mmVCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000); - WREG32_P(mmVCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F); - WREG32(mmVCE_CLOCK_GATING_B, 0xf7); - - WREG32(mmVCE_LMI_CTRL, 0x00398000); - WREG32_P(mmVCE_LMI_CACHE_CTRL, 0x0, ~0x1); - WREG32(mmVCE_LMI_SWAP_CNTL, 0); - WREG32(mmVCE_LMI_SWAP_CNTL1, 0); - WREG32(mmVCE_LMI_VM_CTRL, 0); - - addr += AMDGPU_VCE_FIRMWARE_OFFSET; - size = VCE_V2_0_FW_SIZE; - WREG32(mmVCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff); - WREG32(mmVCE_VCPU_CACHE_SIZE0, size); - - addr += size; - size = VCE_V2_0_STACK_SIZE; - WREG32(mmVCE_VCPU_CACHE_OFFSET1, addr & 0x7fffffff); - WREG32(mmVCE_VCPU_CACHE_SIZE1, size); - - addr += size; - size = VCE_V2_0_DATA_SIZE; - WREG32(mmVCE_VCPU_CACHE_OFFSET2, addr & 0x7fffffff); - WREG32(mmVCE_VCPU_CACHE_SIZE2, size); - - WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100); - WREG32_FIELD(VCE_SYS_INT_EN, VCE_SYS_INT_TRAP_INTERRUPT_EN, 1); - - vce_v2_0_init_cg(adev); -} - -static bool vce_v2_0_is_idle(void *handle) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - - return !(RREG32(mmSRBM_STATUS2) & SRBM_STATUS2__VCE_BUSY_MASK); -} - -static int vce_v2_0_wait_for_idle(void *handle) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - unsigned i; - - for (i = 0; i < adev->usec_timeout; i++) { - if (vce_v2_0_is_idle(handle)) - return 0; - } - return -ETIMEDOUT; -} - static int vce_v2_0_soft_reset(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; @@ -539,33 +573,20 @@ static int vce_v2_0_process_interrupt(struct amdgpu_device *adev, return 0; } -static void vce_v2_0_set_bypass_mode(struct amdgpu_device *adev, bool enable) -{ - u32 tmp = RREG32_SMC(ixGCK_DFS_BYPASS_CNTL); - - if (enable) - tmp |= GCK_DFS_BYPASS_CNTL__BYPASSECLK_MASK; - else - tmp &= ~GCK_DFS_BYPASS_CNTL__BYPASSECLK_MASK; - - WREG32_SMC(ixGCK_DFS_BYPASS_CNTL, tmp); -} - - static int vce_v2_0_set_clockgating_state(void *handle, enum amd_clockgating_state state) { bool gate = false; - struct amdgpu_device *adev = (struct amdgpu_device *)handle; - bool enable = (state == AMD_CG_STATE_GATE) ? true : false; - + bool sw_cg = false; - vce_v2_0_set_bypass_mode(adev, enable); + struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (state == AMD_CG_STATE_GATE) + if (state == AMD_CG_STATE_GATE) { gate = true; + sw_cg = true; + } - vce_v2_0_enable_mgcg(adev, gate); + vce_v2_0_enable_mgcg(adev, gate, sw_cg); return 0; } @@ -582,12 +603,8 @@ static int vce_v2_0_set_powergating_state(void *handle, */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!(adev->pg_flags & AMD_PG_SUPPORT_VCE)) - return 0; - if (state == AMD_PG_STATE_GATE) - /* XXX do we need a vce_v2_0_stop()? */ - return 0; + return vce_v2_0_stop(adev); else return vce_v2_0_start(adev); } diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index 37ca685e5a9a..93ec8815bb13 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -432,9 +432,9 @@ static int vce_v3_0_hw_init(void *handle) int r, i; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - r = vce_v3_0_start(adev); - if (r) - return r; + vce_v3_0_override_vce_clock_gating(adev, true); + if (!(adev->flags & AMD_IS_APU)) + amdgpu_asic_set_vce_clocks(adev, 10000, 10000); for (i = 0; i < adev->vce.num_rings; i++) adev->vce.ring[i].ready = false; @@ -510,6 +510,8 @@ static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx) WREG32(mmVCE_LMI_SWAP_CNTL, 0); WREG32(mmVCE_LMI_SWAP_CNTL1, 0); WREG32(mmVCE_LMI_VM_CTRL, 0); + WREG32_OR(mmVCE_VCPU_CNTL, 0x00100000); + if (adev->asic_type >= CHIP_STONEY) { WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR0, (adev->vce.gpu_addr >> 8)); WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR1, (adev->vce.gpu_addr >> 8)); @@ -708,18 +710,6 @@ static int vce_v3_0_process_interrupt(struct amdgpu_device *adev, return 0; } -static void vce_v3_0_set_bypass_mode(struct amdgpu_device *adev, bool enable) -{ - u32 tmp = RREG32_SMC(ixGCK_DFS_BYPASS_CNTL); - - if (enable) - tmp |= GCK_DFS_BYPASS_CNTL__BYPASSECLK_MASK; - else - tmp &= ~GCK_DFS_BYPASS_CNTL__BYPASSECLK_MASK; - - WREG32_SMC(ixGCK_DFS_BYPASS_CNTL, tmp); -} - static int vce_v3_0_set_clockgating_state(void *handle, enum amd_clockgating_state state) { @@ -727,11 +717,6 @@ static int vce_v3_0_set_clockgating_state(void *handle, bool enable = (state == AMD_CG_STATE_GATE) ? true : false; int i; - if ((adev->asic_type == CHIP_POLARIS10) || - (adev->asic_type == CHIP_TONGA) || - (adev->asic_type == CHIP_FIJI)) - vce_v3_0_set_bypass_mode(adev, enable); - if (!(adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)) return 0; @@ -777,15 +762,44 @@ static int vce_v3_0_set_powergating_state(void *handle, * the smc and the hw blocks */ struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int ret = 0; - if (!(adev->pg_flags & AMD_PG_SUPPORT_VCE)) - return 0; + if (state == AMD_PG_STATE_GATE) { + ret = vce_v3_0_stop(adev); + if (ret) + goto out; + } else { + ret = vce_v3_0_start(adev); + if (ret) + goto out; + } - if (state == AMD_PG_STATE_GATE) - /* XXX do we need a vce_v3_0_stop()? */ - return 0; - else - return vce_v3_0_start(adev); +out: + return ret; +} + +static void vce_v3_0_get_clockgating_state(void *handle, u32 *flags) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int data; + + mutex_lock(&adev->pm.mutex); + + if (RREG32_SMC(ixCURRENT_PG_STATUS) & + CURRENT_PG_STATUS__VCE_PG_STATUS_MASK) { + DRM_INFO("Cannot get clockgating state when VCE is powergated.\n"); + goto out; + } + + WREG32_FIELD(GRBM_GFX_INDEX, VCE_INSTANCE, 0); + + /* AMD_CG_SUPPORT_VCE_MGCG */ + data = RREG32(mmVCE_CLOCK_GATING_A); + if (data & (0x04 << 4)) + *flags |= AMD_CG_SUPPORT_VCE_MGCG; + +out: + mutex_unlock(&adev->pm.mutex); } static void vce_v3_0_ring_emit_ib(struct amdgpu_ring *ring, @@ -839,6 +853,7 @@ static const struct amd_ip_funcs vce_v3_0_ip_funcs = { .post_soft_reset = vce_v3_0_post_soft_reset, .set_clockgating_state = vce_v3_0_set_clockgating_state, .set_powergating_state = vce_v3_0_set_powergating_state, + .get_clockgating_state = vce_v3_0_get_clockgating_state, }; static const struct amdgpu_ring_funcs vce_v3_0_ring_phys_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index c2ac54f11341..50bdb24ef8d6 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -20,9 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ -#include <linux/firmware.h> #include <linux/slab.h> -#include <linux/module.h> #include "drmP.h" #include "amdgpu.h" #include "amdgpu_atombios.h" @@ -78,17 +76,7 @@ #include "amdgpu_acp.h" #endif #include "dce_virtual.h" - -MODULE_FIRMWARE("amdgpu/topaz_smc.bin"); -MODULE_FIRMWARE("amdgpu/topaz_k_smc.bin"); -MODULE_FIRMWARE("amdgpu/tonga_smc.bin"); -MODULE_FIRMWARE("amdgpu/tonga_k_smc.bin"); -MODULE_FIRMWARE("amdgpu/fiji_smc.bin"); -MODULE_FIRMWARE("amdgpu/polaris10_smc.bin"); -MODULE_FIRMWARE("amdgpu/polaris10_smc_sk.bin"); -MODULE_FIRMWARE("amdgpu/polaris11_smc.bin"); -MODULE_FIRMWARE("amdgpu/polaris11_smc_sk.bin"); -MODULE_FIRMWARE("amdgpu/polaris12_smc.bin"); +#include "mxgpu_vi.h" /* * Indirect registers accessor @@ -285,6 +273,12 @@ static void vi_init_golden_registers(struct amdgpu_device *adev) /* Some of the registers might be dependent on GRBM_GFX_INDEX */ mutex_lock(&adev->grbm_idx_mutex); + if (amdgpu_sriov_vf(adev)) { + xgpu_vi_init_golden_registers(adev); + mutex_unlock(&adev->grbm_idx_mutex); + return; + } + switch (adev->asic_type) { case CHIP_TOPAZ: amdgpu_program_register_sequence(adev, @@ -458,14 +452,14 @@ static void vi_detect_hw_virtualization(struct amdgpu_device *adev) /* bit0: 0 means pf and 1 means vf */ /* bit31: 0 means disable IOV and 1 means enable */ if (reg & 1) - adev->virtualization.virtual_caps |= AMDGPU_SRIOV_CAPS_IS_VF; + adev->virt.caps |= AMDGPU_SRIOV_CAPS_IS_VF; if (reg & 0x80000000) - adev->virtualization.virtual_caps |= AMDGPU_SRIOV_CAPS_ENABLE_IOV; + adev->virt.caps |= AMDGPU_SRIOV_CAPS_ENABLE_IOV; if (reg == 0) { if (is_virtual_machine()) /* passthrough mode exclus sr-iov mode */ - adev->virtualization.virtual_caps |= AMDGPU_PASSTHROUGH_MODE; + adev->virt.caps |= AMDGPU_PASSTHROUGH_MODE; } } @@ -727,6 +721,7 @@ static int vi_gpu_pci_config_reset(struct amdgpu_device *adev) if (RREG32(mmCONFIG_MEMSIZE) != 0xffffffff) { /* enable BM */ pci_set_master(adev->pdev); + adev->has_hw_reset = true; return 0; } udelay(1); @@ -801,7 +796,37 @@ static int vi_set_uvd_clocks(struct amdgpu_device *adev, u32 vclk, u32 dclk) static int vi_set_vce_clocks(struct amdgpu_device *adev, u32 evclk, u32 ecclk) { - /* todo */ + int r, i; + struct atom_clock_dividers dividers; + u32 tmp; + + r = amdgpu_atombios_get_clock_dividers(adev, + COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, + ecclk, false, ÷rs); + if (r) + return r; + + for (i = 0; i < 100; i++) { + if (RREG32_SMC(ixCG_ECLK_STATUS) & CG_ECLK_STATUS__ECLK_STATUS_MASK) + break; + mdelay(10); + } + if (i == 100) + return -ETIMEDOUT; + + tmp = RREG32_SMC(ixCG_ECLK_CNTL); + tmp &= ~(CG_ECLK_CNTL__ECLK_DIR_CNTL_EN_MASK | + CG_ECLK_CNTL__ECLK_DIVIDER_MASK); + tmp |= dividers.post_divider; + WREG32_SMC(ixCG_ECLK_CNTL, tmp); + + for (i = 0; i < 100; i++) { + if (RREG32_SMC(ixCG_ECLK_STATUS) & CG_ECLK_STATUS__ECLK_STATUS_MASK) + break; + mdelay(10); + } + if (i == 100) + return -ETIMEDOUT; return 0; } @@ -869,7 +894,6 @@ static const struct amdgpu_asic_funcs vi_asic_funcs = { .read_disabled_bios = &vi_read_disabled_bios, .read_bios_from_rom = &vi_read_bios_from_rom, - .detect_hw_virtualization = vi_detect_hw_virtualization, .read_register = &vi_read_register, .reset = &vi_asic_reset, .set_vga_state = &vi_vga_set_state, @@ -905,6 +929,11 @@ static int vi_common_early_init(void *handle) (amdgpu_ip_block_mask & (1 << AMD_IP_BLOCK_TYPE_SMC))) smc_enabled = true; + if (amdgpu_sriov_vf(adev)) { + amdgpu_virt_init_setting(adev); + xgpu_vi_mailbox_set_irq_funcs(adev); + } + adev->rev_id = vi_get_rev_id(adev); adev->external_rev_id = 0xFF; switch (adev->asic_type) { @@ -1061,10 +1090,6 @@ static int vi_common_early_init(void *handle) return -EINVAL; } - /* in early init stage, vbios code won't work */ - if (adev->asic_funcs->detect_hw_virtualization) - amdgpu_asic_detect_hw_virtualization(adev); - if (amdgpu_smc_load_fw && smc_enabled) adev->firmware.smu_load = true; @@ -1073,8 +1098,23 @@ static int vi_common_early_init(void *handle) return 0; } +static int vi_common_late_init(void *handle) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + if (amdgpu_sriov_vf(adev)) + xgpu_vi_mailbox_get_irq(adev); + + return 0; +} + static int vi_common_sw_init(void *handle) { + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + if (amdgpu_sriov_vf(adev)) + xgpu_vi_mailbox_add_irq_id(adev); + return 0; } @@ -1106,6 +1146,9 @@ static int vi_common_hw_fini(void *handle) /* enable the doorbell aperture */ vi_enable_doorbell_aperture(adev, false); + if (amdgpu_sriov_vf(adev)) + xgpu_vi_mailbox_put_irq(adev); + return 0; } @@ -1190,6 +1233,23 @@ static void vi_update_hdp_light_sleep(struct amdgpu_device *adev, WREG32(mmHDP_MEM_POWER_LS, data); } +static void vi_update_drm_light_sleep(struct amdgpu_device *adev, + bool enable) +{ + uint32_t temp, data; + + temp = data = RREG32(0x157a); + + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_DRM_LS)) + data |= 1; + else + data &= ~1; + + if (temp != data) + WREG32(0x157a, data); +} + + static void vi_update_rom_medium_grain_clock_gating(struct amdgpu_device *adev, bool enable) { @@ -1350,6 +1410,8 @@ static int vi_common_set_clockgating_state(void *handle, state == AMD_CG_STATE_GATE ? true : false); vi_update_hdp_light_sleep(adev, state == AMD_CG_STATE_GATE ? true : false); + vi_update_drm_light_sleep(adev, + state == AMD_CG_STATE_GATE ? true : false); break; case CHIP_TONGA: case CHIP_POLARIS10: @@ -1368,10 +1430,36 @@ static int vi_common_set_powergating_state(void *handle, return 0; } +static void vi_common_get_clockgating_state(void *handle, u32 *flags) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int data; + + /* AMD_CG_SUPPORT_BIF_LS */ + data = RREG32_PCIE(ixPCIE_CNTL2); + if (data & PCIE_CNTL2__SLV_MEM_LS_EN_MASK) + *flags |= AMD_CG_SUPPORT_BIF_LS; + + /* AMD_CG_SUPPORT_HDP_LS */ + data = RREG32(mmHDP_MEM_POWER_LS); + if (data & HDP_MEM_POWER_LS__LS_ENABLE_MASK) + *flags |= AMD_CG_SUPPORT_HDP_LS; + + /* AMD_CG_SUPPORT_HDP_MGCG */ + data = RREG32(mmHDP_HOST_PATH_CNTL); + if (!(data & HDP_HOST_PATH_CNTL__CLOCK_GATING_DIS_MASK)) + *flags |= AMD_CG_SUPPORT_HDP_MGCG; + + /* AMD_CG_SUPPORT_ROM_MGCG */ + data = RREG32_SMC(ixCGTT_ROM_CLK_CTRL0); + if (!(data & CGTT_ROM_CLK_CTRL0__SOFT_OVERRIDE0_MASK)) + *flags |= AMD_CG_SUPPORT_ROM_MGCG; +} + static const struct amd_ip_funcs vi_common_ip_funcs = { .name = "vi_common", .early_init = vi_common_early_init, - .late_init = NULL, + .late_init = vi_common_late_init, .sw_init = vi_common_sw_init, .sw_fini = vi_common_sw_fini, .hw_init = vi_common_hw_init, @@ -1383,6 +1471,7 @@ static const struct amd_ip_funcs vi_common_ip_funcs = { .soft_reset = vi_common_soft_reset, .set_clockgating_state = vi_common_set_clockgating_state, .set_powergating_state = vi_common_set_powergating_state, + .get_clockgating_state = vi_common_get_clockgating_state, }; static const struct amdgpu_ip_block_version vi_common_ip_block = @@ -1396,6 +1485,12 @@ static const struct amdgpu_ip_block_version vi_common_ip_block = int vi_set_ip_blocks(struct amdgpu_device *adev) { + /* in early init stage, vbios code won't work */ + vi_detect_hw_virtualization(adev); + + if (amdgpu_sriov_vf(adev)) + adev->virt.ops = &xgpu_vi_virt_ops; + switch (adev->asic_type) { case CHIP_TOPAZ: /* topaz has no DCE, UVD, VCE */ @@ -1413,28 +1508,32 @@ int vi_set_ip_blocks(struct amdgpu_device *adev) amdgpu_ip_block_add(adev, &gmc_v8_5_ip_block); amdgpu_ip_block_add(adev, &tonga_ih_ip_block); amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); - if (adev->enable_virtual_display) + if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); else amdgpu_ip_block_add(adev, &dce_v10_1_ip_block); amdgpu_ip_block_add(adev, &gfx_v8_0_ip_block); amdgpu_ip_block_add(adev, &sdma_v3_0_ip_block); - amdgpu_ip_block_add(adev, &uvd_v6_0_ip_block); - amdgpu_ip_block_add(adev, &vce_v3_0_ip_block); + if (!amdgpu_sriov_vf(adev)) { + amdgpu_ip_block_add(adev, &uvd_v6_0_ip_block); + amdgpu_ip_block_add(adev, &vce_v3_0_ip_block); + } break; case CHIP_TONGA: amdgpu_ip_block_add(adev, &vi_common_ip_block); amdgpu_ip_block_add(adev, &gmc_v8_0_ip_block); amdgpu_ip_block_add(adev, &tonga_ih_ip_block); amdgpu_ip_block_add(adev, &amdgpu_pp_ip_block); - if (adev->enable_virtual_display) + if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_ip_block_add(adev, &dce_virtual_ip_block); else amdgpu_ip_block_add(adev, &dce_v10_0_ip_block); amdgpu_ip_block_add(adev, &gfx_v8_0_ip_block); amdgpu_ip_block_add(adev, &sdma_v3_0_ip_block); - amdgpu_ip_block_add(adev, &uvd_v5_0_ip_block); - amdgpu_ip_block_add(adev, &vce_v3_0_ip_block); + if (!amdgpu_sriov_vf(adev)) { + amdgpu_ip_block_add(adev, &uvd_v5_0_ip_block); + amdgpu_ip_block_add(adev, &vce_v3_0_ip_block); + } break; case CHIP_POLARIS11: case CHIP_POLARIS10: diff --git a/drivers/gpu/drm/amd/amdgpu/vi.h b/drivers/gpu/drm/amd/amdgpu/vi.h index 575d7aed5d32..719587b8b0cb 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.h +++ b/drivers/gpu/drm/amd/amdgpu/vi.h @@ -28,4 +28,116 @@ void vi_srbm_select(struct amdgpu_device *adev, u32 me, u32 pipe, u32 queue, u32 vmid); int vi_set_ip_blocks(struct amdgpu_device *adev); +struct amdgpu_ce_ib_state +{ + uint32_t ce_ib_completion_status; + uint32_t ce_constegnine_count; + uint32_t ce_ibOffset_ib1; + uint32_t ce_ibOffset_ib2; +}; /* Total of 4 DWORD */ + +struct amdgpu_de_ib_state +{ + uint32_t ib_completion_status; + uint32_t de_constEngine_count; + uint32_t ib_offset_ib1; + uint32_t ib_offset_ib2; + uint32_t preamble_begin_ib1; + uint32_t preamble_begin_ib2; + uint32_t preamble_end_ib1; + uint32_t preamble_end_ib2; + uint32_t draw_indirect_baseLo; + uint32_t draw_indirect_baseHi; + uint32_t disp_indirect_baseLo; + uint32_t disp_indirect_baseHi; + uint32_t gds_backup_addrlo; + uint32_t gds_backup_addrhi; + uint32_t index_base_addrlo; + uint32_t index_base_addrhi; + uint32_t sample_cntl; +}; /* Total of 17 DWORD */ + +struct amdgpu_ce_ib_state_chained_ib +{ + /* section of non chained ib part */ + uint32_t ce_ib_completion_status; + uint32_t ce_constegnine_count; + uint32_t ce_ibOffset_ib1; + uint32_t ce_ibOffset_ib2; + + /* section of chained ib */ + uint32_t ce_chainib_addrlo_ib1; + uint32_t ce_chainib_addrlo_ib2; + uint32_t ce_chainib_addrhi_ib1; + uint32_t ce_chainib_addrhi_ib2; + uint32_t ce_chainib_size_ib1; + uint32_t ce_chainib_size_ib2; +}; /* total 10 DWORD */ + +struct amdgpu_de_ib_state_chained_ib +{ + /* section of non chained ib part */ + uint32_t ib_completion_status; + uint32_t de_constEngine_count; + uint32_t ib_offset_ib1; + uint32_t ib_offset_ib2; + + /* section of chained ib */ + uint32_t chain_ib_addrlo_ib1; + uint32_t chain_ib_addrlo_ib2; + uint32_t chain_ib_addrhi_ib1; + uint32_t chain_ib_addrhi_ib2; + uint32_t chain_ib_size_ib1; + uint32_t chain_ib_size_ib2; + + /* section of non chained ib part */ + uint32_t preamble_begin_ib1; + uint32_t preamble_begin_ib2; + uint32_t preamble_end_ib1; + uint32_t preamble_end_ib2; + + /* section of chained ib */ + uint32_t chain_ib_pream_addrlo_ib1; + uint32_t chain_ib_pream_addrlo_ib2; + uint32_t chain_ib_pream_addrhi_ib1; + uint32_t chain_ib_pream_addrhi_ib2; + + /* section of non chained ib part */ + uint32_t draw_indirect_baseLo; + uint32_t draw_indirect_baseHi; + uint32_t disp_indirect_baseLo; + uint32_t disp_indirect_baseHi; + uint32_t gds_backup_addrlo; + uint32_t gds_backup_addrhi; + uint32_t index_base_addrlo; + uint32_t index_base_addrhi; + uint32_t sample_cntl; +}; /* Total of 27 DWORD */ + +struct amdgpu_gfx_meta_data +{ + /* 4 DWORD, address must be 4KB aligned */ + struct amdgpu_ce_ib_state ce_payload; + uint32_t reserved1[60]; + /* 17 DWORD, address must be 64B aligned */ + struct amdgpu_de_ib_state de_payload; + /* PFP IB base address which get pre-empted */ + uint32_t DeIbBaseAddrLo; + uint32_t DeIbBaseAddrHi; + uint32_t reserved2[941]; +}; /* Total of 4K Bytes */ + +struct amdgpu_gfx_meta_data_chained_ib +{ + /* 10 DWORD, address must be 4KB aligned */ + struct amdgpu_ce_ib_state_chained_ib ce_payload; + uint32_t reserved1[54]; + /* 27 DWORD, address must be 64B aligned */ + struct amdgpu_de_ib_state_chained_ib de_payload; + /* PFP IB base address which get pre-empted */ + uint32_t DeIbBaseAddrLo; + uint32_t DeIbBaseAddrHi; + uint32_t reserved2[931]; +}; /* Total of 4K Bytes */ + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/vi_dpm.h b/drivers/gpu/drm/amd/amdgpu/vi_dpm.h index fc120ba18aad..c43e03fddfba 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi_dpm.h +++ b/drivers/gpu/drm/amd/amdgpu/vi_dpm.h @@ -29,8 +29,4 @@ int cz_smu_init(struct amdgpu_device *adev); int cz_smu_start(struct amdgpu_device *adev); int cz_smu_fini(struct amdgpu_device *adev); -extern const struct amd_ip_funcs tonga_dpm_ip_funcs; -extern const struct amd_ip_funcs fiji_dpm_ip_funcs; -extern const struct amd_ip_funcs iceland_dpm_ip_funcs; - #endif diff --git a/drivers/gpu/drm/amd/amdgpu/vid.h b/drivers/gpu/drm/amd/amdgpu/vid.h index 11746f22d0c5..7a3863a45f0a 100644 --- a/drivers/gpu/drm/amd/amdgpu/vid.h +++ b/drivers/gpu/drm/amd/amdgpu/vid.h @@ -360,6 +360,8 @@ #define PACKET3_WAIT_ON_CE_COUNTER 0x86 #define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88 #define PACKET3_SWITCH_BUFFER 0x8B +#define PACKET3_SET_RESOURCES 0xA0 +#define PACKET3_MAP_QUEUES 0xA2 #define VCE_CMD_NO_OP 0x00000000 #define VCE_CMD_END 0x00000001 diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index ee3e04e10dae..6316aad43a73 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -486,7 +486,7 @@ static int kfd_ioctl_dbg_register(struct file *filep, return status; } -static int kfd_ioctl_dbg_unrgesiter(struct file *filep, +static int kfd_ioctl_dbg_unregister(struct file *filep, struct kfd_process *p, void *data) { struct kfd_ioctl_dbg_unregister_args *args = data; @@ -498,7 +498,7 @@ static int kfd_ioctl_dbg_unrgesiter(struct file *filep, return -EINVAL; if (dev->device_info->asic_family == CHIP_CARRIZO) { - pr_debug("kfd_ioctl_dbg_unrgesiter not supported on CZ\n"); + pr_debug("kfd_ioctl_dbg_unregister not supported on CZ\n"); return -EINVAL; } @@ -892,7 +892,7 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { kfd_ioctl_dbg_register, 0), AMDKFD_IOCTL_DEF(AMDKFD_IOC_DBG_UNREGISTER, - kfd_ioctl_dbg_unrgesiter, 0), + kfd_ioctl_dbg_unregister, 0), AMDKFD_IOCTL_DEF(AMDKFD_IOC_DBG_ADDRESS_WATCH, kfd_ioctl_dbg_address_watch, 0), diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index a6a4b2b1c0d9..d1ce83d73a87 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -23,7 +23,7 @@ #include <linux/mm_types.h> #include <linux/slab.h> #include <linux/types.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/uaccess.h> #include <linux/mm.h> #include <linux/mman.h> @@ -739,8 +739,10 @@ int kfd_wait_on_events(struct kfd_process *p, struct kfd_event_data event_data; if (copy_from_user(&event_data, &events[i], - sizeof(struct kfd_event_data))) + sizeof(struct kfd_event_data))) { + ret = -EFAULT; goto fail; + } ret = init_event_waiter(p, &event_waiters[i], event_data.event_id, i); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c index d83de985e88c..6acc4313363e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c @@ -23,6 +23,8 @@ #include <linux/printk.h> #include <linux/slab.h> +#include <linux/mm_types.h> + #include "kfd_priv.h" #include "kfd_mqd_manager.h" #include "cik_regs.h" diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c index fa32c32fa1c2..a9b9882a9a77 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c @@ -23,6 +23,8 @@ #include <linux/printk.h> #include <linux/slab.h> +#include <linux/mm_types.h> + #include "kfd_priv.h" #include "kfd_mqd_manager.h" #include "vi_structs.h" diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index ef7c8de7060e..84d1ffd1eef9 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -23,6 +23,7 @@ #include <linux/mutex.h> #include <linux/log2.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/slab.h> #include <linux/amd-iommu.h> #include <linux/notifier.h> @@ -262,7 +263,7 @@ static void kfd_process_notifier_release(struct mmu_notifier *mn, * and because the mmu_notifier_unregister function also drop * mm_count we need to take an extra count here. */ - atomic_inc(&p->mm->mm_count); + mmgrab(p->mm); mmu_notifier_unregister_no_release(&p->mmu_notifier, p->mm); mmu_notifier_call_srcu(&p->rcu, &kfd_process_destroy_delayed); } diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 85f358764bbc..43f45adeccd1 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -80,6 +80,18 @@ enum amd_clockgating_state { AMD_CG_STATE_UNGATE, }; +enum amd_dpm_forced_level { + AMD_DPM_FORCED_LEVEL_AUTO = 0x1, + AMD_DPM_FORCED_LEVEL_MANUAL = 0x2, + AMD_DPM_FORCED_LEVEL_LOW = 0x4, + AMD_DPM_FORCED_LEVEL_HIGH = 0x8, + AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD = 0x10, + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK = 0x20, + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK = 0x40, + AMD_DPM_FORCED_LEVEL_PROFILE_PEAK = 0x80, + AMD_DPM_FORCED_LEVEL_PROFILE_EXIT = 0x100, +}; + enum amd_powergating_state { AMD_PG_STATE_GATE = 0, AMD_PG_STATE_UNGATE, @@ -206,6 +218,8 @@ struct amd_ip_funcs { /* enable/disable pg for the IP block */ int (*set_powergating_state)(void *handle, enum amd_powergating_state state); + /* get current clockgating status */ + void (*get_clockgating_state)(void *handle, u32 *flags); }; #endif /* __AMD_SHARED_H__ */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_d.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_d.h index 95570dbd18bb..813957a17a2d 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_d.h @@ -4552,6 +4552,14 @@ #define mmDP4_DP_DPHY_PRBS_CNTL 0x4eb5 #define mmDP5_DP_DPHY_PRBS_CNTL 0x4fb5 #define mmDP6_DP_DPHY_PRBS_CNTL 0x54b5 +#define mmDP_DPHY_SCRAM_CNTL 0x4ab6 +#define mmDP0_DP_DPHY_SCRAM_CNTL 0x4ab6 +#define mmDP1_DP_DPHY_SCRAM_CNTL 0x4bb6 +#define mmDP2_DP_DPHY_SCRAM_CNTL 0x4cb6 +#define mmDP3_DP_DPHY_SCRAM_CNTL 0x4db6 +#define mmDP4_DP_DPHY_SCRAM_CNTL 0x4eb6 +#define mmDP5_DP_DPHY_SCRAM_CNTL 0x4fb6 +#define mmDP6_DP_DPHY_SCRAM_CNTL 0x54b6 #define mmDP_DPHY_CRC_EN 0x4ab7 #define mmDP0_DP_DPHY_CRC_EN 0x4ab7 #define mmDP1_DP_DPHY_CRC_EN 0x4bb7 diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_sh_mask.h index 8a75eb9d732b..c755f43aaaf8 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_10_0_sh_mask.h @@ -8690,6 +8690,10 @@ #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEL__SHIFT 0x4 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED_MASK 0x7fffff00 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED__SHIFT 0x8 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE_MASK 0x10 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE__SHIFT 0x4 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT_MASK 0x3ff00 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT__SHIFT 0x8 #define DP_DPHY_CRC_EN__DPHY_CRC_EN_MASK 0x1 #define DP_DPHY_CRC_EN__DPHY_CRC_EN__SHIFT 0x0 #define DP_DPHY_CRC_EN__DPHY_CRC_CONT_EN_MASK 0x10 diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_d.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_d.h index c39234ecedd0..6df651a94b0a 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_d.h @@ -4544,6 +4544,15 @@ #define mmDP6_DP_DPHY_PRBS_CNTL 0x54b5 #define mmDP7_DP_DPHY_PRBS_CNTL 0x56b5 #define mmDP8_DP_DPHY_PRBS_CNTL 0x57b5 +#define mmDP_DPHY_SCRAM_CNTL 0x4ab6 +#define mmDP0_DP_DPHY_SCRAM_CNTL 0x4ab6 +#define mmDP1_DP_DPHY_SCRAM_CNTL 0x4bb6 +#define mmDP2_DP_DPHY_SCRAM_CNTL 0x4cb6 +#define mmDP3_DP_DPHY_SCRAM_CNTL 0x4db6 +#define mmDP4_DP_DPHY_SCRAM_CNTL 0x4eb6 +#define mmDP5_DP_DPHY_SCRAM_CNTL 0x4fb6 +#define mmDP6_DP_DPHY_SCRAM_CNTL 0x54b6 +#define mmDP8_DP_DPHY_SCRAM_CNTL 0x56b6 #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4adc #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4adc #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4bdc diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_sh_mask.h index a438c2b6e280..14a3bacfcfd1 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_0_sh_mask.h @@ -6004,6 +6004,8 @@ #define DIG_DISPCLK_SWITCH_STATUS__DIG_DISPCLK_SWITCH_ALLOWED_INT_MASK__SHIFT 0xc #define HDMI_CONTROL__HDMI_KEEPOUT_MODE_MASK 0x1 #define HDMI_CONTROL__HDMI_KEEPOUT_MODE__SHIFT 0x0 +#define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN_MASK 0x2 +#define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN__SHIFT 0x1 #define HDMI_CONTROL__HDMI_CLOCK_CHANNEL_RATE_MASK 0x4 #define HDMI_CONTROL__HDMI_CLOCK_CHANNEL_RATE__SHIFT 0x2 #define HDMI_CONTROL__HDMI_NO_EXTRA_NULL_PACKET_FILLED_MASK 0x8 @@ -8364,6 +8366,10 @@ #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEL__SHIFT 0x4 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED_MASK 0x7fffff00 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED__SHIFT 0x8 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE_MASK 0x10 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE__SHIFT 0x4 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT_MASK 0x3ff00 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT__SHIFT 0x8 #define DP_DPHY_BS_SR_SWAP_CNTL__DPHY_LOAD_BS_COUNT_MASK 0x3ff #define DP_DPHY_BS_SR_SWAP_CNTL__DPHY_LOAD_BS_COUNT__SHIFT 0x0 #define DP_DPHY_BS_SR_SWAP_CNTL__DPHY_BS_SR_SWAP_DONE_MASK 0x8000 diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_d.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_d.h index 09a7df17570d..367b191d49fb 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_d.h @@ -5776,6 +5776,15 @@ #define mmDP6_DP_DPHY_PRBS_CNTL 0x54b5 #define mmDP7_DP_DPHY_PRBS_CNTL 0x56b5 #define mmDP8_DP_DPHY_PRBS_CNTL 0x57b5 +#define mmDP_DPHY_SCRAM_CNTL 0x4ab6 +#define mmDP0_DP_DPHY_SCRAM_CNTL 0x4ab6 +#define mmDP1_DP_DPHY_SCRAM_CNTL 0x4bb6 +#define mmDP2_DP_DPHY_SCRAM_CNTL 0x4cb6 +#define mmDP3_DP_DPHY_SCRAM_CNTL 0x4db6 +#define mmDP4_DP_DPHY_SCRAM_CNTL 0x4eb6 +#define mmDP5_DP_DPHY_SCRAM_CNTL 0x4fb6 +#define mmDP6_DP_DPHY_SCRAM_CNTL 0x54b6 +#define mmDP8_DP_DPHY_SCRAM_CNTL 0x56b6 #define mmDP_DPHY_BS_SR_SWAP_CNTL 0x4adc #define mmDP0_DP_DPHY_BS_SR_SWAP_CNTL 0x4adc #define mmDP1_DP_DPHY_BS_SR_SWAP_CNTL 0x4bdc diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_sh_mask.h index 1ddc4183a1c9..106094ed0661 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_sh_mask.h @@ -7088,6 +7088,8 @@ #define DIG_DISPCLK_SWITCH_STATUS__DIG_DISPCLK_SWITCH_ALLOWED_INT_MASK__SHIFT 0xc #define HDMI_CONTROL__HDMI_KEEPOUT_MODE_MASK 0x1 #define HDMI_CONTROL__HDMI_KEEPOUT_MODE__SHIFT 0x0 +#define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN_MASK 0x2 +#define HDMI_CONTROL__HDMI_DATA_SCRAMBLE_EN__SHIFT 0x1 #define HDMI_CONTROL__HDMI_CLOCK_CHANNEL_RATE_MASK 0x4 #define HDMI_CONTROL__HDMI_CLOCK_CHANNEL_RATE__SHIFT 0x2 #define HDMI_CONTROL__HDMI_NO_EXTRA_NULL_PACKET_FILLED_MASK 0x8 @@ -9626,6 +9628,10 @@ #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEL__SHIFT 0x4 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED_MASK 0x7fffff00 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED__SHIFT 0x8 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE_MASK 0x10 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE__SHIFT 0x4 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT_MASK 0x3ff00 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT__SHIFT 0x8 #define DP_DPHY_BS_SR_SWAP_CNTL__DPHY_LOAD_BS_COUNT_MASK 0x3ff #define DP_DPHY_BS_SR_SWAP_CNTL__DPHY_LOAD_BS_COUNT__SHIFT 0x0 #define DP_DPHY_BS_SR_SWAP_CNTL__DPHY_BS_SR_SWAP_DONE_MASK 0x8000 diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_d.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_d.h index d3ccf5a86de0..93d84a475134 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_d.h @@ -3920,6 +3920,14 @@ #define mmDP4_DP_DPHY_PRBS_CNTL 0x48d4 #define mmDP5_DP_DPHY_PRBS_CNTL 0x4bd4 #define mmDP6_DP_DPHY_PRBS_CNTL 0x4ed4 +#define mmDP_DPHY_SCRAM_CNTL 0x1cd5 +#define mmDP0_DP_DPHY_SCRAM_CNTL 0x1cd5 +#define mmDP1_DP_DPHY_SCRAM_CNTL 0x1fd5 +#define mmDP2_DP_DPHY_SCRAM_CNTL 0x42d5 +#define mmDP3_DP_DPHY_SCRAM_CNTL 0x45d5 +#define mmDP4_DP_DPHY_SCRAM_CNTL 0x48d5 +#define mmDP5_DP_DPHY_SCRAM_CNTL 0x4bd5 +#define mmDP6_DP_DPHY_SCRAM_CNTL 0x4ed5 #define mmDP_DPHY_CRC_EN 0x1cd6 #define mmDP0_DP_DPHY_CRC_EN 0x1cd6 #define mmDP1_DP_DPHY_CRC_EN 0x1fd6 diff --git a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_sh_mask.h index c331c9fe7b81..9b6825b74cc1 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/dce/dce_8_0_sh_mask.h @@ -9214,6 +9214,10 @@ #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEL__SHIFT 0x4 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED_MASK 0x7fffff00 #define DP_DPHY_PRBS_CNTL__DPHY_PRBS_SEED__SHIFT 0x8 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE_MASK 0x10 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_ADVANCE__SHIFT 0x4 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT_MASK 0x3ff00 +#define DP_DPHY_SCRAM_CNTL__DPHY_SCRAMBLER_BS_COUNT__SHIFT 0x8 #define DP_DPHY_CRC_EN__DPHY_CRC_EN_MASK 0x1 #define DP_DPHY_CRC_EN__DPHY_CRC_EN__SHIFT 0x0 #define DP_DPHY_CRC_EN__DPHY_CRC_CONT_EN_MASK 0x10 diff --git a/drivers/gpu/drm/amd/include/asic_reg/si/si_reg.h b/drivers/gpu/drm/amd/include/asic_reg/si/si_reg.h deleted file mode 100644 index 895c8e2353e3..000000000000 --- a/drivers/gpu/drm/amd/include/asic_reg/si/si_reg.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2010 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: Alex Deucher - */ -#ifndef __SI_REG_H__ -#define __SI_REG_H__ - -/* SI */ -#define SI_DC_GPIO_HPD_MASK 0x196c -#define SI_DC_GPIO_HPD_A 0x196d -#define SI_DC_GPIO_HPD_EN 0x196e -#define SI_DC_GPIO_HPD_Y 0x196f - -#define SI_GRPH_CONTROL 0x1a01 -# define SI_GRPH_DEPTH(x) (((x) & 0x3) << 0) -# define SI_GRPH_DEPTH_8BPP 0 -# define SI_GRPH_DEPTH_16BPP 1 -# define SI_GRPH_DEPTH_32BPP 2 -# define SI_GRPH_NUM_BANKS(x) (((x) & 0x3) << 2) -# define SI_ADDR_SURF_2_BANK 0 -# define SI_ADDR_SURF_4_BANK 1 -# define SI_ADDR_SURF_8_BANK 2 -# define SI_ADDR_SURF_16_BANK 3 -# define SI_GRPH_Z(x) (((x) & 0x3) << 4) -# define SI_GRPH_BANK_WIDTH(x) (((x) & 0x3) << 6) -# define SI_ADDR_SURF_BANK_WIDTH_1 0 -# define SI_ADDR_SURF_BANK_WIDTH_2 1 -# define SI_ADDR_SURF_BANK_WIDTH_4 2 -# define SI_ADDR_SURF_BANK_WIDTH_8 3 -# define SI_GRPH_FORMAT(x) (((x) & 0x7) << 8) -/* 8 BPP */ -# define SI_GRPH_FORMAT_INDEXED 0 -/* 16 BPP */ -# define SI_GRPH_FORMAT_ARGB1555 0 -# define SI_GRPH_FORMAT_ARGB565 1 -# define SI_GRPH_FORMAT_ARGB4444 2 -# define SI_GRPH_FORMAT_AI88 3 -# define SI_GRPH_FORMAT_MONO16 4 -# define SI_GRPH_FORMAT_BGRA5551 5 -/* 32 BPP */ -# define SI_GRPH_FORMAT_ARGB8888 0 -# define SI_GRPH_FORMAT_ARGB2101010 1 -# define SI_GRPH_FORMAT_32BPP_DIG 2 -# define SI_GRPH_FORMAT_8B_ARGB2101010 3 -# define SI_GRPH_FORMAT_BGRA1010102 4 -# define SI_GRPH_FORMAT_8B_BGRA1010102 5 -# define SI_GRPH_FORMAT_RGB111110 6 -# define SI_GRPH_FORMAT_BGR101111 7 -# define SI_GRPH_BANK_HEIGHT(x) (((x) & 0x3) << 11) -# define SI_ADDR_SURF_BANK_HEIGHT_1 0 -# define SI_ADDR_SURF_BANK_HEIGHT_2 1 -# define SI_ADDR_SURF_BANK_HEIGHT_4 2 -# define SI_ADDR_SURF_BANK_HEIGHT_8 3 -# define SI_GRPH_TILE_SPLIT(x) (((x) & 0x7) << 13) -# define SI_ADDR_SURF_TILE_SPLIT_64B 0 -# define SI_ADDR_SURF_TILE_SPLIT_128B 1 -# define SI_ADDR_SURF_TILE_SPLIT_256B 2 -# define SI_ADDR_SURF_TILE_SPLIT_512B 3 -# define SI_ADDR_SURF_TILE_SPLIT_1KB 4 -# define SI_ADDR_SURF_TILE_SPLIT_2KB 5 -# define SI_ADDR_SURF_TILE_SPLIT_4KB 6 -# define SI_GRPH_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 18) -# define SI_ADDR_SURF_MACRO_TILE_ASPECT_1 0 -# define SI_ADDR_SURF_MACRO_TILE_ASPECT_2 1 -# define SI_ADDR_SURF_MACRO_TILE_ASPECT_4 2 -# define SI_ADDR_SURF_MACRO_TILE_ASPECT_8 3 -# define SI_GRPH_ARRAY_MODE(x) (((x) & 0x7) << 20) -# define SI_GRPH_ARRAY_LINEAR_GENERAL 0 -# define SI_GRPH_ARRAY_LINEAR_ALIGNED 1 -# define SI_GRPH_ARRAY_1D_TILED_THIN1 2 -# define SI_GRPH_ARRAY_2D_TILED_THIN1 4 -# define SI_GRPH_PIPE_CONFIG(x) (((x) & 0x1f) << 24) -# define SI_ADDR_SURF_P2 0 -# define SI_ADDR_SURF_P4_8x16 4 -# define SI_ADDR_SURF_P4_16x16 5 -# define SI_ADDR_SURF_P4_16x32 6 -# define SI_ADDR_SURF_P4_32x32 7 -# define SI_ADDR_SURF_P8_16x16_8x16 8 -# define SI_ADDR_SURF_P8_16x32_8x16 9 -# define SI_ADDR_SURF_P8_32x32_8x16 10 -# define SI_ADDR_SURF_P8_16x32_16x16 11 -# define SI_ADDR_SURF_P8_32x32_16x16 12 -# define SI_ADDR_SURF_P8_32x32_16x32 13 -# define SI_ADDR_SURF_P8_32x64_32x32 14 - -#endif diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_d.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_d.h index f9fd2ea4625b..dbc2e723f659 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_d.h @@ -1310,5 +1310,6 @@ #define ixROM_SW_DATA_62 0xc060012c #define ixROM_SW_DATA_63 0xc0600130 #define ixROM_SW_DATA_64 0xc0600134 +#define ixCURRENT_PG_STATUS 0xc020029c #endif /* SMU_7_0_1_D_H */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_sh_mask.h index 25882a4dea5d..34c6ff52710e 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_0_1_sh_mask.h @@ -5452,5 +5452,7 @@ #define ROM_SW_DATA_63__ROM_SW_DATA__SHIFT 0x0 #define ROM_SW_DATA_64__ROM_SW_DATA_MASK 0xffffffff #define ROM_SW_DATA_64__ROM_SW_DATA__SHIFT 0x0 +#define CURRENT_PG_STATUS__VCE_PG_STATUS_MASK 0x00000002 +#define CURRENT_PG_STATUS__UVD_PG_STATUS_MASK 0x00000004 #endif /* SMU_7_0_1_SH_MASK_H */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_d.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_d.h index a9ef1562f43b..66597c64f525 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_d.h @@ -1121,5 +1121,6 @@ #define ixROM_SW_DATA_62 0xc060011c #define ixROM_SW_DATA_63 0xc0600120 #define ixROM_SW_DATA_64 0xc0600124 +#define ixCURRENT_PG_STATUS 0xc020029c #endif /* SMU_7_1_1_D_H */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_sh_mask.h index 2c997f7b5d13..fb06f2e2f6e6 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_1_sh_mask.h @@ -4860,5 +4860,7 @@ #define ROM_SW_DATA_63__ROM_SW_DATA__SHIFT 0x0 #define ROM_SW_DATA_64__ROM_SW_DATA_MASK 0xffffffff #define ROM_SW_DATA_64__ROM_SW_DATA__SHIFT 0x0 +#define CURRENT_PG_STATUS__VCE_PG_STATUS_MASK 0x00000002 +#define CURRENT_PG_STATUS__UVD_PG_STATUS_MASK 0x00000004 #endif /* SMU_7_1_1_SH_MASK_H */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_d.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_d.h index 22dd4c2b7290..4446d43d2a8f 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_d.h @@ -1271,5 +1271,6 @@ #define ixROM_SW_DATA_62 0xc060011c #define ixROM_SW_DATA_63 0xc0600120 #define ixROM_SW_DATA_64 0xc0600124 +#define ixCURRENT_PG_STATUS 0xc020029c #endif /* SMU_7_1_2_D_H */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_sh_mask.h index 518fd02e9d35..627906674fe8 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_2_sh_mask.h @@ -5830,5 +5830,7 @@ #define ROM_SW_DATA_63__ROM_SW_DATA__SHIFT 0x0 #define ROM_SW_DATA_64__ROM_SW_DATA_MASK 0xffffffff #define ROM_SW_DATA_64__ROM_SW_DATA__SHIFT 0x0 +#define CURRENT_PG_STATUS__VCE_PG_STATUS_MASK 0x00000002 +#define CURRENT_PG_STATUS__UVD_PG_STATUS_MASK 0x00000004 #endif /* SMU_7_1_2_SH_MASK_H */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_d.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_d.h index eca2b851f25f..0333d880bc9e 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_d.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_d.h @@ -1244,5 +1244,5 @@ #define ixGC_CAC_ACC_CU14 0xc8 #define ixGC_CAC_ACC_CU15 0xc9 #define ixGC_CAC_OVRD_CU 0xe7 - +#define ixCURRENT_PG_STATUS 0xc020029c #endif /* SMU_7_1_3_D_H */ diff --git a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_sh_mask.h index 1ede9e274714..654c1093d362 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_sh_mask.h +++ b/drivers/gpu/drm/amd/include/asic_reg/smu/smu_7_1_3_sh_mask.h @@ -6076,5 +6076,8 @@ #define GC_CAC_OVRD_CU__OVRRD_SELECT__SHIFT 0x0 #define GC_CAC_OVRD_CU__OVRRD_VALUE_MASK 0xffff0000 #define GC_CAC_OVRD_CU__OVRRD_VALUE__SHIFT 0x10 +#define CURRENT_PG_STATUS__VCE_PG_STATUS_MASK 0x00000002 +#define CURRENT_PG_STATUS__UVD_PG_STATUS_MASK 0x00000004 + #endif /* SMU_7_1_3_SH_MASK_H */ diff --git a/drivers/gpu/drm/amd/include/atombios.h b/drivers/gpu/drm/amd/include/atombios.h index 4a4d3797a6d3..181a2c3c6362 100644 --- a/drivers/gpu/drm/amd/include/atombios.h +++ b/drivers/gpu/drm/amd/include/atombios.h @@ -188,7 +188,7 @@ #define HW_ASSISTED_I2C_STATUS_FAILURE 2 #define HW_ASSISTED_I2C_STATUS_SUCCESS 1 -#pragma pack(1) // BIOS data must use byte aligment +#pragma pack(1) // BIOS data must use byte alignment // Define offset to location of ROM header. #define OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER 0x00000048L @@ -4361,7 +4361,7 @@ typedef struct _ATOM_GPIO_PIN_ASSIGNMENT // GPIO use to control PCIE_VDDC in certain SLT board #define PCIE_VDDC_CONTROL_GPIO_PINID 56 -//from SMU7.x, if ucGPIO_ID=PP_AC_DC_SWITCH_GPIO_PINID in GPIO_LUTTable, AC/DC swithing feature is enable +//from SMU7.x, if ucGPIO_ID=PP_AC_DC_SWITCH_GPIO_PINID in GPIO_LUTTable, AC/DC switching feature is enable #define PP_AC_DC_SWITCH_GPIO_PINID 60 //from SMU7.x, if ucGPIO_ID=VDDC_REGULATOR_VRHOT_GPIO_PINID in GPIO_LUTable, VRHot feature is enable #define VDDC_VRHOT_GPIO_PINID 61 @@ -9180,7 +9180,7 @@ typedef struct _ATOM_POWERPLAY_INFO_V3 /*********************************************************************************/ -#pragma pack() // BIOS data must use byte aligment +#pragma pack() // BIOS data must use byte alignment #pragma pack(1) @@ -9211,7 +9211,7 @@ typedef struct _ATOM_SERVICE_INFO -#pragma pack() // BIOS data must use byte aligment +#pragma pack() // BIOS data must use byte alignment // // AMD ACPI Table diff --git a/drivers/gpu/drm/amd/include/cgs_common.h b/drivers/gpu/drm/amd/include/cgs_common.h index e4a1697ec1d3..17b9d41f3e87 100644 --- a/drivers/gpu/drm/amd/include/cgs_common.h +++ b/drivers/gpu/drm/amd/include/cgs_common.h @@ -171,6 +171,7 @@ struct cgs_firmware_info { uint32_t ucode_start_address; void *kptr; + bool is_kicker; }; struct cgs_mode_info { @@ -622,6 +623,8 @@ typedef int (*cgs_query_system_info)(struct cgs_device *cgs_device, typedef int (*cgs_is_virtualization_enabled_t)(void *cgs_device); +typedef int (*cgs_enter_safe_mode)(struct cgs_device *cgs_device, bool en); + struct cgs_ops { /* memory management calls (similar to KFD interface) */ cgs_gpu_mem_info_t gpu_mem_info; @@ -674,6 +677,7 @@ struct cgs_ops { /* get system info */ cgs_query_system_info query_system_info; cgs_is_virtualization_enabled_t is_virtualization_enabled; + cgs_enter_safe_mode enter_safe_mode; }; struct cgs_os_ops; /* To be define in OS-specific CGS header */ @@ -779,4 +783,8 @@ struct cgs_device #define cgs_is_virtualization_enabled(cgs_device) \ CGS_CALL(is_virtualization_enabled, cgs_device) + +#define cgs_enter_safe_mode(cgs_device, en) \ + CGS_CALL(enter_safe_mode, cgs_device, en) + #endif /* _CGS_COMMON_H */ diff --git a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c index c81cf1412728..429f18b99323 100644 --- a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c @@ -20,6 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/types.h> #include <linux/kernel.h> #include <linux/gfp.h> @@ -29,153 +30,154 @@ #include "pp_instance.h" #include "power_state.h" #include "eventmanager.h" -#include "pp_debug.h" -#define PP_CHECK(handle) \ - do { \ - if ((handle) == NULL || (handle)->pp_valid != PP_VALID) \ - return -EINVAL; \ - } while (0) +static inline int pp_check(struct pp_instance *handle) +{ + if (handle == NULL || handle->pp_valid != PP_VALID) + return -EINVAL; -#define PP_CHECK_HW(hwmgr) \ - do { \ - if ((hwmgr) == NULL || (hwmgr)->hwmgr_func == NULL) \ - return 0; \ - } while (0) + if (handle->smu_mgr == NULL || handle->smu_mgr->smumgr_funcs == NULL) + return -EINVAL; + + if (handle->pm_en == 0) + return PP_DPM_DISABLED; + + if (handle->hwmgr == NULL || handle->hwmgr->hwmgr_func == NULL + || handle->eventmgr == NULL) + return PP_DPM_DISABLED; + + return 0; +} static int pp_early_init(void *handle) { + int ret; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + + ret = smum_early_init(pp_handle); + if (ret) + return ret; + + if ((pp_handle->pm_en == 0) + || cgs_is_virtualization_enabled(pp_handle->device)) + return PP_DPM_DISABLED; + + ret = hwmgr_early_init(pp_handle); + if (ret) { + pp_handle->pm_en = 0; + return PP_DPM_DISABLED; + } + + ret = eventmgr_early_init(pp_handle); + if (ret) { + kfree(pp_handle->hwmgr); + pp_handle->hwmgr = NULL; + pp_handle->pm_en = 0; + return PP_DPM_DISABLED; + } + return 0; } static int pp_sw_init(void *handle) { - struct pp_instance *pp_handle; - struct pp_hwmgr *hwmgr; + struct pp_smumgr *smumgr; int ret = 0; + struct pp_instance *pp_handle = (struct pp_instance *)handle; - if (handle == NULL) - return -EINVAL; - - pp_handle = (struct pp_instance *)handle; - hwmgr = pp_handle->hwmgr; - - PP_CHECK_HW(hwmgr); - - if (hwmgr->pptable_func == NULL || - hwmgr->pptable_func->pptable_init == NULL || - hwmgr->hwmgr_func->backend_init == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - ret = hwmgr->pptable_func->pptable_init(hwmgr); - if (ret) - goto err; + if (ret == 0 || ret == PP_DPM_DISABLED) { + smumgr = pp_handle->smu_mgr; - ret = hwmgr->hwmgr_func->backend_init(hwmgr); - if (ret) - goto err1; + if (smumgr->smumgr_funcs->smu_init == NULL) + return -EINVAL; - pr_info("amdgpu: powerplay initialized\n"); + ret = smumgr->smumgr_funcs->smu_init(smumgr); - return 0; -err1: - if (hwmgr->pptable_func->pptable_fini) - hwmgr->pptable_func->pptable_fini(hwmgr); -err: - pr_err("amdgpu: powerplay initialization failed\n"); + pr_info("amdgpu: powerplay sw initialized\n"); + } return ret; } static int pp_sw_fini(void *handle) { - struct pp_instance *pp_handle; - struct pp_hwmgr *hwmgr; + struct pp_smumgr *smumgr; int ret = 0; + struct pp_instance *pp_handle = (struct pp_instance *)handle; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); + if (ret == 0 || ret == PP_DPM_DISABLED) { + smumgr = pp_handle->smu_mgr; - pp_handle = (struct pp_instance *)handle; - hwmgr = pp_handle->hwmgr; - - PP_CHECK_HW(hwmgr); - - if (hwmgr->hwmgr_func->backend_fini != NULL) - ret = hwmgr->hwmgr_func->backend_fini(hwmgr); - - if (hwmgr->pptable_func->pptable_fini) - hwmgr->pptable_func->pptable_fini(hwmgr); + if (smumgr->smumgr_funcs->smu_fini == NULL) + return -EINVAL; + ret = smumgr->smumgr_funcs->smu_fini(smumgr); + } return ret; } static int pp_hw_init(void *handle) { - struct pp_instance *pp_handle; struct pp_smumgr *smumgr; struct pp_eventmgr *eventmgr; - struct pp_hwmgr *hwmgr; int ret = 0; + struct pp_instance *pp_handle = (struct pp_instance *)handle; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - pp_handle = (struct pp_instance *)handle; - smumgr = pp_handle->smu_mgr; - hwmgr = pp_handle->hwmgr; + if (ret == 0 || ret == PP_DPM_DISABLED) { + smumgr = pp_handle->smu_mgr; - if (smumgr == NULL || smumgr->smumgr_funcs == NULL || - smumgr->smumgr_funcs->smu_init == NULL || - smumgr->smumgr_funcs->start_smu == NULL) - return -EINVAL; - - ret = smumgr->smumgr_funcs->smu_init(smumgr); - if (ret) { - printk(KERN_ERR "[ powerplay ] smc initialization failed\n"); - return ret; - } + if (smumgr->smumgr_funcs->start_smu == NULL) + return -EINVAL; - ret = smumgr->smumgr_funcs->start_smu(smumgr); - if (ret) { - printk(KERN_ERR "[ powerplay ] smc start failed\n"); - smumgr->smumgr_funcs->smu_fini(smumgr); - return ret; + if(smumgr->smumgr_funcs->start_smu(smumgr)) { + pr_err("smc start failed\n"); + smumgr->smumgr_funcs->smu_fini(smumgr); + return -EINVAL;; + } + if (ret == PP_DPM_DISABLED) + return PP_DPM_DISABLED; } - PP_CHECK_HW(hwmgr); - - hw_init_power_state_table(hwmgr); + ret = hwmgr_hw_init(pp_handle); + if (ret) + goto err; eventmgr = pp_handle->eventmgr; - if (eventmgr == NULL || eventmgr->pp_eventmgr_init == NULL) - return -EINVAL; + if (eventmgr->pp_eventmgr_init == NULL || + eventmgr->pp_eventmgr_init(eventmgr)) + goto err; - ret = eventmgr->pp_eventmgr_init(eventmgr); return 0; +err: + pp_handle->pm_en = 0; + kfree(pp_handle->eventmgr); + kfree(pp_handle->hwmgr); + pp_handle->hwmgr = NULL; + pp_handle->eventmgr = NULL; + return PP_DPM_DISABLED; } static int pp_hw_fini(void *handle) { - struct pp_instance *pp_handle; - struct pp_smumgr *smumgr; struct pp_eventmgr *eventmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; - - pp_handle = (struct pp_instance *)handle; - eventmgr = pp_handle->eventmgr; + ret = pp_check(pp_handle); - if (eventmgr != NULL && eventmgr->pp_eventmgr_fini != NULL) - eventmgr->pp_eventmgr_fini(eventmgr); + if (ret == 0) { + eventmgr = pp_handle->eventmgr; - smumgr = pp_handle->smu_mgr; - - if (smumgr != NULL && smumgr->smumgr_funcs != NULL && - smumgr->smumgr_funcs->smu_fini != NULL) - smumgr->smumgr_funcs->smu_fini(smumgr); + if (eventmgr->pp_eventmgr_fini != NULL) + eventmgr->pp_eventmgr_fini(eventmgr); + hwmgr_hw_fini(pp_handle); + } return 0; } @@ -198,16 +200,18 @@ static int pp_sw_reset(void *handle) int amd_set_clockgating_by_smu(void *handle, uint32_t msg_id) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->update_clock_gatings == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -218,16 +222,18 @@ static int pp_set_powergating_state(void *handle, enum amd_powergating_state state) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->enable_per_cu_power_gating == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -238,49 +244,53 @@ static int pp_set_powergating_state(void *handle, static int pp_suspend(void *handle) { - struct pp_instance *pp_handle; struct pp_eventmgr *eventmgr; struct pem_event_data event_data = { {0} }; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); + + if (ret != 0) + return ret; - pp_handle = (struct pp_instance *)handle; eventmgr = pp_handle->eventmgr; + pem_handle_event(eventmgr, AMD_PP_EVENT_SUSPEND, &event_data); - if (eventmgr != NULL) - pem_handle_event(eventmgr, AMD_PP_EVENT_SUSPEND, &event_data); return 0; } static int pp_resume(void *handle) { - struct pp_instance *pp_handle; struct pp_eventmgr *eventmgr; struct pem_event_data event_data = { {0} }; struct pp_smumgr *smumgr; - int ret; + int ret, ret1; + struct pp_instance *pp_handle = (struct pp_instance *)handle; - if (handle == NULL) - return -EINVAL; + ret1 = pp_check(pp_handle); + + if (ret1 != 0 && ret1 != PP_DPM_DISABLED) + return ret1; - pp_handle = (struct pp_instance *)handle; smumgr = pp_handle->smu_mgr; - if (smumgr == NULL || smumgr->smumgr_funcs == NULL || - smumgr->smumgr_funcs->start_smu == NULL) + if (smumgr->smumgr_funcs->start_smu == NULL) return -EINVAL; ret = smumgr->smumgr_funcs->start_smu(smumgr); if (ret) { - printk(KERN_ERR "[ powerplay ] smc start failed\n"); + pr_err("smc start failed\n"); smumgr->smumgr_funcs->smu_fini(smumgr); return ret; } + if (ret1 == PP_DPM_DISABLED) + return ret1; + eventmgr = pp_handle->eventmgr; - if (eventmgr != NULL) - pem_handle_event(eventmgr, AMD_PP_EVENT_RESUME, &event_data); + + pem_handle_event(eventmgr, AMD_PP_EVENT_RESUME, &event_data); return 0; } @@ -315,20 +325,19 @@ static int pp_dpm_fw_loading_complete(void *handle) static int pp_dpm_force_performance_level(void *handle, enum amd_dpm_forced_level level) { - struct pp_instance *pp_handle; struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - pp_handle = (struct pp_instance *)handle; + if (ret != 0) + return ret; hwmgr = pp_handle->hwmgr; - PP_CHECK_HW(hwmgr); - if (hwmgr->hwmgr_func->force_dpm_level == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -341,30 +350,34 @@ static enum amd_dpm_forced_level pp_dpm_get_performance_level( void *handle) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; - return (((struct pp_instance *)handle)->hwmgr->dpm_level); + return hwmgr->dpm_level; } static int pp_dpm_get_sclk(void *handle, bool low) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_sclk == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -374,16 +387,18 @@ static int pp_dpm_get_sclk(void *handle, bool low) static int pp_dpm_get_mclk(void *handle, bool low) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_mclk == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -393,16 +408,18 @@ static int pp_dpm_get_mclk(void *handle, bool low) static int pp_dpm_powergate_vce(void *handle, bool gate) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->powergate_vce == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -412,16 +429,18 @@ static int pp_dpm_powergate_vce(void *handle, bool gate) static int pp_dpm_powergate_uvd(void *handle, bool gate) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->powergate_uvd == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -446,16 +465,13 @@ static int pp_dpm_dispatch_tasks(void *handle, enum amd_pp_event event_id, void *input, void *output) { int ret = 0; - struct pp_instance *pp_handle; struct pem_event_data data = { {0} }; + struct pp_instance *pp_handle = (struct pp_instance *)handle; - pp_handle = (struct pp_instance *)handle; + ret = pp_check(pp_handle); - if (pp_handle == NULL) - return -EINVAL; - - if (pp_handle->eventmgr == NULL) - return 0; + if (ret != 0) + return ret; switch (event_id) { case AMD_PP_EVENT_DISPLAY_CONFIG_CHANGE: @@ -489,13 +505,17 @@ static enum amd_pm_state_type pp_dpm_get_current_power_state(void *handle) { struct pp_hwmgr *hwmgr; struct pp_power_state *state; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - if (hwmgr == NULL || hwmgr->current_ps == NULL) + hwmgr = pp_handle->hwmgr; + + if (hwmgr->current_ps == NULL) return -EINVAL; state = hwmgr->current_ps; @@ -518,16 +538,18 @@ static enum amd_pm_state_type pp_dpm_get_current_power_state(void *handle) static int pp_dpm_set_fan_control_mode(void *handle, uint32_t mode) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->set_fan_control_mode == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -537,16 +559,18 @@ static int pp_dpm_set_fan_control_mode(void *handle, uint32_t mode) static int pp_dpm_get_fan_control_mode(void *handle) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_fan_control_mode == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -556,16 +580,18 @@ static int pp_dpm_get_fan_control_mode(void *handle) static int pp_dpm_set_fan_speed_percent(void *handle, uint32_t percent) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->set_fan_speed_percent == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -575,16 +601,18 @@ static int pp_dpm_set_fan_speed_percent(void *handle, uint32_t percent) static int pp_dpm_get_fan_speed_percent(void *handle, uint32_t *speed) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_fan_speed_percent == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -594,13 +622,15 @@ static int pp_dpm_get_fan_speed_percent(void *handle, uint32_t *speed) static int pp_dpm_get_fan_speed_rpm(void *handle, uint32_t *rpm) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_fan_speed_rpm == NULL) return -EINVAL; @@ -611,16 +641,18 @@ static int pp_dpm_get_fan_speed_rpm(void *handle, uint32_t *rpm) static int pp_dpm_get_temperature(void *handle) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_temperature == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -632,13 +664,17 @@ static int pp_dpm_get_pp_num_states(void *handle, { struct pp_hwmgr *hwmgr; int i; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; + + hwmgr = pp_handle->hwmgr; - if (hwmgr == NULL || hwmgr->ps == NULL) + if (hwmgr->ps == NULL) return -EINVAL; data->nums = hwmgr->num_ps; @@ -670,13 +706,15 @@ static int pp_dpm_get_pp_num_states(void *handle, static int pp_dpm_get_pp_table(void *handle, char **table) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (!hwmgr->soft_pp_table) return -EINVAL; @@ -689,13 +727,15 @@ static int pp_dpm_get_pp_table(void *handle, char **table) static int pp_dpm_set_pp_table(void *handle, const char *buf, size_t size) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (!hwmgr->hardcode_pp_table) { hwmgr->hardcode_pp_table = kmemdup(hwmgr->soft_pp_table, @@ -717,16 +757,18 @@ static int pp_dpm_force_clock_level(void *handle, enum pp_clock_type type, uint32_t mask) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->force_clock_level == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -737,16 +779,18 @@ static int pp_dpm_print_clock_levels(void *handle, enum pp_clock_type type, char *buf) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->print_clock_levels == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } return hwmgr->hwmgr_func->print_clock_levels(hwmgr, type, buf); @@ -755,16 +799,18 @@ static int pp_dpm_print_clock_levels(void *handle, static int pp_dpm_get_sclk_od(void *handle) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_sclk_od == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -774,16 +820,18 @@ static int pp_dpm_get_sclk_od(void *handle) static int pp_dpm_set_sclk_od(void *handle, uint32_t value) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->set_sclk_od == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -793,16 +841,18 @@ static int pp_dpm_set_sclk_od(void *handle, uint32_t value) static int pp_dpm_get_mclk_od(void *handle) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->get_mclk_od == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -812,16 +862,18 @@ static int pp_dpm_get_mclk_od(void *handle) static int pp_dpm_set_mclk_od(void *handle, uint32_t value) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->set_mclk_od == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -831,16 +883,18 @@ static int pp_dpm_set_mclk_od(void *handle, uint32_t value) static int pp_dpm_read_sensor(void *handle, int idx, int32_t *value) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (!handle) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; if (hwmgr->hwmgr_func->read_sensor == NULL) { - printk(KERN_INFO "%s was not implemented.\n", __func__); + pr_info("%s was not implemented.\n", __func__); return 0; } @@ -851,13 +905,18 @@ static struct amd_vce_state* pp_dpm_get_vce_clock_state(void *handle, unsigned idx) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - if (handle) { - hwmgr = ((struct pp_instance *)handle)->hwmgr; + ret = pp_check(pp_handle); - if (hwmgr && idx < hwmgr->num_vce_state_tables) - return &hwmgr->vce_states[idx]; - } + if (ret != 0) + return NULL; + + hwmgr = pp_handle->hwmgr; + + if (hwmgr && idx < hwmgr->num_vce_state_tables) + return &hwmgr->vce_states[idx]; return NULL; } @@ -892,89 +951,44 @@ const struct amd_powerplay_funcs pp_dpm_funcs = { .get_vce_clock_state = pp_dpm_get_vce_clock_state, }; -static int amd_pp_instance_init(struct amd_pp_init *pp_init, - struct amd_powerplay *amd_pp) +int amd_powerplay_create(struct amd_pp_init *pp_init, + void **handle) { - int ret; - struct pp_instance *handle; - - handle = kzalloc(sizeof(struct pp_instance), GFP_KERNEL); - if (handle == NULL) - return -ENOMEM; - - handle->pp_valid = PP_VALID; - - ret = smum_init(pp_init, handle); - if (ret) - goto fail_smum; - - - amd_pp->pp_handle = handle; + struct pp_instance *instance; - if ((amdgpu_dpm == 0) - || cgs_is_virtualization_enabled(pp_init->device)) - return 0; + if (pp_init == NULL || handle == NULL) + return -EINVAL; - ret = hwmgr_init(pp_init, handle); - if (ret) - goto fail_hwmgr; + instance = kzalloc(sizeof(struct pp_instance), GFP_KERNEL); + if (instance == NULL) + return -ENOMEM; - ret = eventmgr_init(handle); - if (ret) - goto fail_eventmgr; + instance->pp_valid = PP_VALID; + instance->chip_family = pp_init->chip_family; + instance->chip_id = pp_init->chip_id; + instance->pm_en = pp_init->pm_en; + instance->feature_mask = pp_init->feature_mask; + instance->device = pp_init->device; + *handle = instance; return 0; - -fail_eventmgr: - hwmgr_fini(handle->hwmgr); -fail_hwmgr: - smum_fini(handle->smu_mgr); -fail_smum: - kfree(handle); - return ret; } -static int amd_pp_instance_fini(void *handle) +int amd_powerplay_destroy(void *handle) { struct pp_instance *instance = (struct pp_instance *)handle; - if (instance == NULL) - return -EINVAL; - - if ((amdgpu_dpm != 0) - && !cgs_is_virtualization_enabled(instance->smu_mgr->device)) { - eventmgr_fini(instance->eventmgr); - hwmgr_fini(instance->hwmgr); + if (instance->pm_en) { + kfree(instance->eventmgr); + kfree(instance->hwmgr); + instance->hwmgr = NULL; + instance->eventmgr = NULL; } - smum_fini(instance->smu_mgr); - kfree(handle); - return 0; -} - -int amd_powerplay_init(struct amd_pp_init *pp_init, - struct amd_powerplay *amd_pp) -{ - int ret; - - if (pp_init == NULL || amd_pp == NULL) - return -EINVAL; - - ret = amd_pp_instance_init(pp_init, amd_pp); - - if (ret) - return ret; - - amd_pp->ip_funcs = &pp_ip_funcs; - amd_pp->pp_funcs = &pp_dpm_funcs; - - return 0; -} - -int amd_powerplay_fini(void *handle) -{ - amd_pp_instance_fini(handle); - + kfree(instance->smu_mgr); + instance->smu_mgr = NULL; + kfree(instance); + instance = NULL; return 0; } @@ -985,33 +999,25 @@ int amd_powerplay_reset(void *handle) struct pem_event_data event_data = { {0} }; int ret; - if (instance == NULL) - return -EINVAL; - - eventmgr = instance->eventmgr; - if (!eventmgr || !eventmgr->pp_eventmgr_fini) - return -EINVAL; - - eventmgr->pp_eventmgr_fini(eventmgr); + if (cgs_is_virtualization_enabled(instance->smu_mgr->device)) + return PP_DPM_DISABLED; - ret = pp_sw_fini(handle); - if (ret) + ret = pp_check(instance); + if (ret != 0) return ret; - kfree(instance->hwmgr->ps); - - ret = pp_sw_init(handle); + ret = pp_hw_fini(handle); if (ret) return ret; - if ((amdgpu_dpm == 0) - || cgs_is_virtualization_enabled(instance->smu_mgr->device)) - return 0; + ret = hwmgr_hw_init(instance); + if (ret) + return PP_DPM_DISABLED; - hw_init_power_state_table(instance->hwmgr); + eventmgr = instance->eventmgr; - if (eventmgr == NULL || eventmgr->pp_eventmgr_init == NULL) - return -EINVAL; + if (eventmgr->pp_eventmgr_init == NULL) + return PP_DPM_DISABLED; ret = eventmgr->pp_eventmgr_init(eventmgr); if (ret) @@ -1026,12 +1032,15 @@ int amd_powerplay_display_configuration_change(void *handle, const struct amd_pp_display_configuration *display_config) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - PP_CHECK((struct pp_instance *)handle); + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; phm_store_dal_configuration_data(hwmgr, display_config); @@ -1042,15 +1051,18 @@ int amd_powerplay_get_display_power_level(void *handle, struct amd_pp_simple_clock_info *output) { struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - PP_CHECK((struct pp_instance *)handle); + ret = pp_check(pp_handle); - if (output == NULL) - return -EINVAL; + if (ret != 0) + return ret; - hwmgr = ((struct pp_instance *)handle)->hwmgr; + hwmgr = pp_handle->hwmgr; - PP_CHECK_HW(hwmgr); + if (output == NULL) + return -EINVAL; return phm_get_dal_power_level(hwmgr, output); } @@ -1058,18 +1070,18 @@ int amd_powerplay_get_display_power_level(void *handle, int amd_powerplay_get_current_clocks(void *handle, struct amd_pp_clock_info *clocks) { - struct pp_hwmgr *hwmgr; struct amd_pp_simple_clock_info simple_clocks; struct pp_clock_info hw_clocks; + struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - PP_CHECK((struct pp_instance *)handle); - - if (clocks == NULL) - return -EINVAL; + ret = pp_check(pp_handle); - hwmgr = ((struct pp_instance *)handle)->hwmgr; + if (ret != 0) + return ret; - PP_CHECK_HW(hwmgr); + hwmgr = pp_handle->hwmgr; phm_get_dal_power_level(hwmgr, &simple_clocks); @@ -1105,18 +1117,20 @@ int amd_powerplay_get_current_clocks(void *handle, int amd_powerplay_get_clock_by_type(void *handle, enum amd_pp_clock_type type, struct amd_pp_clocks *clocks) { int result = -1; + struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - struct pp_hwmgr *hwmgr; + ret = pp_check(pp_handle); - PP_CHECK((struct pp_instance *)handle); + if (ret != 0) + return ret; + + hwmgr = pp_handle->hwmgr; if (clocks == NULL) return -EINVAL; - hwmgr = ((struct pp_instance *)handle)->hwmgr; - - PP_CHECK_HW(hwmgr); - result = phm_get_clock_by_type(hwmgr, type, clocks); return result; @@ -1125,21 +1139,24 @@ int amd_powerplay_get_clock_by_type(void *handle, enum amd_pp_clock_type type, s int amd_powerplay_get_display_mode_validation_clocks(void *handle, struct amd_pp_simple_clock_info *clocks) { - int result = -1; struct pp_hwmgr *hwmgr; + struct pp_instance *pp_handle = (struct pp_instance *)handle; + int ret = 0; - PP_CHECK((struct pp_instance *)handle); + ret = pp_check(pp_handle); - if (clocks == NULL) - return -EINVAL; + if (ret != 0) + return ret; + + hwmgr = pp_handle->hwmgr; - hwmgr = ((struct pp_instance *)handle)->hwmgr; - PP_CHECK_HW(hwmgr); + if (clocks == NULL) + return -EINVAL; if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DynamicPatchPowerState)) - result = phm_get_max_high_clocks(hwmgr, clocks); + ret = phm_get_max_high_clocks(hwmgr, clocks); - return result; + return ret; } diff --git a/drivers/gpu/drm/amd/powerplay/eventmgr/eventinit.c b/drivers/gpu/drm/amd/powerplay/eventmgr/eventinit.c index d5ec8ccbe97d..a3cd230d636d 100644 --- a/drivers/gpu/drm/amd/powerplay/eventmgr/eventinit.c +++ b/drivers/gpu/drm/amd/powerplay/eventmgr/eventinit.c @@ -151,7 +151,7 @@ static int thermal_interrupt_callback(void *private_data, unsigned src_id, const uint32_t *iv_entry) { /* TO DO hanle PEM_Event_ThermalNotification (struct pp_eventmgr *)private_data*/ - printk("current thermal is out of range \n"); + pr_info("current thermal is out of range \n"); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/eventmgr/eventmgr.c b/drivers/gpu/drm/amd/powerplay/eventmgr/eventmgr.c index fb88e4e5d625..781e53dcf128 100644 --- a/drivers/gpu/drm/amd/powerplay/eventmgr/eventmgr.c +++ b/drivers/gpu/drm/amd/powerplay/eventmgr/eventmgr.c @@ -60,9 +60,8 @@ static void pem_fini(struct pp_eventmgr *eventmgr) pem_handle_event(eventmgr, AMD_PP_EVENT_UNINITIALIZE, &event_data); } -int eventmgr_init(struct pp_instance *handle) +int eventmgr_early_init(struct pp_instance *handle) { - int result = 0; struct pp_eventmgr *eventmgr; if (handle == NULL) @@ -79,12 +78,6 @@ int eventmgr_init(struct pp_instance *handle) eventmgr->pp_eventmgr_init = pem_init; eventmgr->pp_eventmgr_fini = pem_fini; - return result; -} - -int eventmgr_fini(struct pp_eventmgr *eventmgr) -{ - kfree(eventmgr); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c b/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c index ec36c0e28388..e04216ec7ee1 100644 --- a/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c +++ b/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c @@ -38,10 +38,13 @@ int pem_task_update_allowed_performance_levels(struct pp_eventmgr *eventmgr, struct pem_event_data *event_data) { + if (eventmgr == NULL || eventmgr->hwmgr == NULL) + return -EINVAL; + if (pem_is_hw_access_blocked(eventmgr)) return 0; - phm_force_dpm_levels(eventmgr->hwmgr, AMD_DPM_FORCED_LEVEL_AUTO); + phm_force_dpm_levels(eventmgr->hwmgr, eventmgr->hwmgr->dpm_level); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c index 6bb79c94cb9f..b33935fcf428 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_clockpowergating.c @@ -161,28 +161,25 @@ int cz_dpm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate) { struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); - if (cz_hwmgr->uvd_power_gated == bgate) - return 0; - cz_hwmgr->uvd_power_gated = bgate; if (bgate) { - cgs_set_clockgating_state(hwmgr->device, - AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_GATE); cgs_set_powergating_state(hwmgr->device, AMD_IP_BLOCK_TYPE_UVD, AMD_PG_STATE_GATE); + cgs_set_clockgating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_UVD, + AMD_CG_STATE_GATE); cz_dpm_update_uvd_dpm(hwmgr, true); cz_dpm_powerdown_uvd(hwmgr); } else { cz_dpm_powerup_uvd(hwmgr); - cgs_set_powergating_state(hwmgr->device, - AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_UNGATE); cgs_set_clockgating_state(hwmgr->device, AMD_IP_BLOCK_TYPE_UVD, AMD_PG_STATE_UNGATE); + cgs_set_powergating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_UVD, + AMD_CG_STATE_UNGATE); cz_dpm_update_uvd_dpm(hwmgr, false); } @@ -193,57 +190,50 @@ int cz_dpm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate) { struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend); - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, - PHM_PlatformCaps_VCEPowerGating)) { - if (cz_hwmgr->vce_power_gated != bgate) { - if (bgate) { - cgs_set_clockgating_state( - hwmgr->device, - AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_GATE); - cgs_set_powergating_state( - hwmgr->device, - AMD_IP_BLOCK_TYPE_VCE, - AMD_PG_STATE_GATE); - cz_enable_disable_vce_dpm(hwmgr, false); - cz_dpm_powerdown_vce(hwmgr); - cz_hwmgr->vce_power_gated = true; - } else { - cz_dpm_powerup_vce(hwmgr); - cz_hwmgr->vce_power_gated = false; - cgs_set_powergating_state( - hwmgr->device, - AMD_IP_BLOCK_TYPE_VCE, - AMD_CG_STATE_UNGATE); - cgs_set_clockgating_state( - hwmgr->device, - AMD_IP_BLOCK_TYPE_VCE, - AMD_PG_STATE_UNGATE); - cz_dpm_update_vce_dpm(hwmgr); - cz_enable_disable_vce_dpm(hwmgr, true); - return 0; - } - } + if (bgate) { + cgs_set_powergating_state( + hwmgr->device, + AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_GATE); + cgs_set_clockgating_state( + hwmgr->device, + AMD_IP_BLOCK_TYPE_VCE, + AMD_CG_STATE_GATE); + cz_enable_disable_vce_dpm(hwmgr, false); + cz_dpm_powerdown_vce(hwmgr); + cz_hwmgr->vce_power_gated = true; } else { - cz_hwmgr->vce_power_gated = bgate; + cz_dpm_powerup_vce(hwmgr); + cz_hwmgr->vce_power_gated = false; + cgs_set_clockgating_state( + hwmgr->device, + AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_UNGATE); + cgs_set_powergating_state( + hwmgr->device, + AMD_IP_BLOCK_TYPE_VCE, + AMD_CG_STATE_UNGATE); cz_dpm_update_vce_dpm(hwmgr); - cz_enable_disable_vce_dpm(hwmgr, !bgate); + cz_enable_disable_vce_dpm(hwmgr, true); return 0; } - if (!cz_hwmgr->vce_power_gated) - cz_dpm_update_vce_dpm(hwmgr); - return 0; } static const struct phm_master_table_item cz_enable_clock_power_gatings_list[] = { /*we don't need an exit table here, because there is only D3 cold on Kv*/ - { phm_cf_want_uvd_power_gating, cz_tf_uvd_power_gating_initialize }, - { phm_cf_want_vce_power_gating, cz_tf_vce_power_gating_initialize }, + { + .isFunctionNeededInRuntimeTable = phm_cf_want_uvd_power_gating, + .tableFunction = cz_tf_uvd_power_gating_initialize + }, + { + .isFunctionNeededInRuntimeTable = phm_cf_want_vce_power_gating, + .tableFunction = cz_tf_vce_power_gating_initialize + }, /* to do { NULL, cz_tf_xdma_power_gating_enable }, */ - { NULL, NULL } + { } }; const struct phm_master_table_header cz_phm_enable_clock_power_gatings_master = { diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c index 0fb4e8c8f5e1..a4cde3d778b8 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c @@ -20,13 +20,13 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> #include "atom-types.h" #include "atombios.h" #include "processpptables.h" -#include "pp_debug.h" #include "cgs_common.h" #include "smu/smu_8_0_d.h" #include "smu8_fusion.h" @@ -38,7 +38,6 @@ #include "cz_hwmgr.h" #include "power_state.h" #include "cz_clockpowergating.h" -#include "pp_debug.h" #define ixSMUSVI_NB_CURRENTVID 0xD8230044 #define CURRENT_NB_VID_MASK 0xff000000 @@ -288,7 +287,7 @@ static int cz_init_dynamic_state_adjustment_rule_settings( kzalloc(table_size, GFP_KERNEL); if (NULL == table_clk_vlt) { - printk(KERN_ERR "[ powerplay ] Can not allocate memory!\n"); + pr_err("Can not allocate memory!\n"); return -ENOMEM; } @@ -329,12 +328,12 @@ static int cz_get_system_info_data(struct pp_hwmgr *hwmgr) &size, &frev, &crev); if (crev != 9) { - printk(KERN_ERR "[ powerplay ] Unsupported IGP table: %d %d\n", frev, crev); + pr_err("Unsupported IGP table: %d %d\n", frev, crev); return -EINVAL; } if (info == NULL) { - printk(KERN_ERR "[ powerplay ] Could not retrieve the Integrated System Info Table!\n"); + pr_err("Could not retrieve the Integrated System Info Table!\n"); return -EINVAL; } @@ -361,7 +360,7 @@ static int cz_get_system_info_data(struct pp_hwmgr *hwmgr) if (cz_hwmgr->sys_info.htc_tmp_lmt <= cz_hwmgr->sys_info.htc_hyst_lmt) { - printk(KERN_ERR "[ powerplay ] The htcTmpLmt should be larger than htcHystLmt.\n"); + pr_err("The htcTmpLmt should be larger than htcHystLmt.\n"); return -EINVAL; } @@ -723,7 +722,7 @@ static int cz_tf_update_sclk_limit(struct pp_hwmgr *hwmgr, clock = hwmgr->display_config.min_core_set_clock; if (clock == 0) - printk(KERN_INFO "[ powerplay ] min_core_set_clock not set\n"); + pr_info("min_core_set_clock not set\n"); if (cz_hwmgr->sclk_dpm.hard_min_clk != clock) { cz_hwmgr->sclk_dpm.hard_min_clk = clock; @@ -888,13 +887,13 @@ static int cz_tf_update_low_mem_pstate(struct pp_hwmgr *hwmgr, } static const struct phm_master_table_item cz_set_power_state_list[] = { - {NULL, cz_tf_update_sclk_limit}, - {NULL, cz_tf_set_deep_sleep_sclk_threshold}, - {NULL, cz_tf_set_watermark_threshold}, - {NULL, cz_tf_set_enabled_levels}, - {NULL, cz_tf_enable_nb_dpm}, - {NULL, cz_tf_update_low_mem_pstate}, - {NULL, NULL} + { .tableFunction = cz_tf_update_sclk_limit }, + { .tableFunction = cz_tf_set_deep_sleep_sclk_threshold }, + { .tableFunction = cz_tf_set_watermark_threshold }, + { .tableFunction = cz_tf_set_enabled_levels }, + { .tableFunction = cz_tf_enable_nb_dpm }, + { .tableFunction = cz_tf_update_low_mem_pstate }, + { } }; static const struct phm_master_table_header cz_set_power_state_master = { @@ -904,15 +903,15 @@ static const struct phm_master_table_header cz_set_power_state_master = { }; static const struct phm_master_table_item cz_setup_asic_list[] = { - {NULL, cz_tf_reset_active_process_mask}, - {NULL, cz_tf_upload_pptable_to_smu}, - {NULL, cz_tf_init_sclk_limit}, - {NULL, cz_tf_init_uvd_limit}, - {NULL, cz_tf_init_vce_limit}, - {NULL, cz_tf_init_acp_limit}, - {NULL, cz_tf_init_power_gate_state}, - {NULL, cz_tf_init_sclk_threshold}, - {NULL, NULL} + { .tableFunction = cz_tf_reset_active_process_mask }, + { .tableFunction = cz_tf_upload_pptable_to_smu }, + { .tableFunction = cz_tf_init_sclk_limit }, + { .tableFunction = cz_tf_init_uvd_limit }, + { .tableFunction = cz_tf_init_vce_limit }, + { .tableFunction = cz_tf_init_acp_limit }, + { .tableFunction = cz_tf_init_power_gate_state }, + { .tableFunction = cz_tf_init_sclk_threshold }, + { } }; static const struct phm_master_table_header cz_setup_asic_master = { @@ -957,10 +956,10 @@ static int cz_tf_reset_cc6_data(struct pp_hwmgr *hwmgr, } static const struct phm_master_table_item cz_power_down_asic_list[] = { - {NULL, cz_tf_power_up_display_clock_sys_pll}, - {NULL, cz_tf_clear_nb_dpm_flag}, - {NULL, cz_tf_reset_cc6_data}, - {NULL, NULL} + { .tableFunction = cz_tf_power_up_display_clock_sys_pll }, + { .tableFunction = cz_tf_clear_nb_dpm_flag }, + { .tableFunction = cz_tf_reset_cc6_data }, + { } }; static const struct phm_master_table_header cz_power_down_asic_master = { @@ -1068,8 +1067,8 @@ static int cz_tf_check_for_dpm_enabled(struct pp_hwmgr *hwmgr, } static const struct phm_master_table_item cz_disable_dpm_list[] = { - { NULL, cz_tf_check_for_dpm_enabled}, - {NULL, NULL}, + { .tableFunction = cz_tf_check_for_dpm_enabled }, + { }, }; @@ -1080,13 +1079,13 @@ static const struct phm_master_table_header cz_disable_dpm_master = { }; static const struct phm_master_table_item cz_enable_dpm_list[] = { - { NULL, cz_tf_check_for_dpm_disabled }, - { NULL, cz_tf_program_voting_clients }, - { NULL, cz_tf_start_dpm}, - { NULL, cz_tf_program_bootup_state}, - { NULL, cz_tf_enable_didt }, - { NULL, cz_tf_reset_acp_boot_level }, - {NULL, NULL}, + { .tableFunction = cz_tf_check_for_dpm_disabled }, + { .tableFunction = cz_tf_program_voting_clients }, + { .tableFunction = cz_tf_start_dpm }, + { .tableFunction = cz_tf_program_bootup_state }, + { .tableFunction = cz_tf_enable_didt }, + { .tableFunction = cz_tf_reset_acp_boot_level }, + { }, }; static const struct phm_master_table_header cz_enable_dpm_master = { @@ -1162,13 +1161,13 @@ static int cz_hwmgr_backend_init(struct pp_hwmgr *hwmgr) result = cz_initialize_dpm_defaults(hwmgr); if (result != 0) { - printk(KERN_ERR "[ powerplay ] cz_initialize_dpm_defaults failed\n"); + pr_err("cz_initialize_dpm_defaults failed\n"); return result; } result = cz_get_system_info_data(hwmgr); if (result != 0) { - printk(KERN_ERR "[ powerplay ] cz_get_system_info_data failed\n"); + pr_err("cz_get_system_info_data failed\n"); return result; } @@ -1177,40 +1176,40 @@ static int cz_hwmgr_backend_init(struct pp_hwmgr *hwmgr) result = phm_construct_table(hwmgr, &cz_setup_asic_master, &(hwmgr->setup_asic)); if (result != 0) { - printk(KERN_ERR "[ powerplay ] Fail to construct setup ASIC\n"); + pr_err("Fail to construct setup ASIC\n"); return result; } result = phm_construct_table(hwmgr, &cz_power_down_asic_master, &(hwmgr->power_down_asic)); if (result != 0) { - printk(KERN_ERR "[ powerplay ] Fail to construct power down ASIC\n"); + pr_err("Fail to construct power down ASIC\n"); return result; } result = phm_construct_table(hwmgr, &cz_disable_dpm_master, &(hwmgr->disable_dynamic_state_management)); if (result != 0) { - printk(KERN_ERR "[ powerplay ] Fail to disable_dynamic_state\n"); + pr_err("Fail to disable_dynamic_state\n"); return result; } result = phm_construct_table(hwmgr, &cz_enable_dpm_master, &(hwmgr->enable_dynamic_state_management)); if (result != 0) { - printk(KERN_ERR "[ powerplay ] Fail to enable_dynamic_state\n"); + pr_err("Fail to enable_dynamic_state\n"); return result; } result = phm_construct_table(hwmgr, &cz_set_power_state_master, &(hwmgr->set_power_state)); if (result != 0) { - printk(KERN_ERR "[ powerplay ] Fail to construct set_power_state\n"); + pr_err("Fail to construct set_power_state\n"); return result; } hwmgr->platform_descriptor.hardwareActivityPerformanceLevels = CZ_MAX_HARDWARE_POWERLEVELS; result = phm_construct_table(hwmgr, &cz_phm_enable_clock_power_gatings_master, &(hwmgr->enable_clock_power_gatings)); if (result != 0) { - printk(KERN_ERR "[ powerplay ] Fail to construct enable_clock_power_gatings\n"); + pr_err("Fail to construct enable_clock_power_gatings\n"); return result; } return result; @@ -1218,9 +1217,15 @@ static int cz_hwmgr_backend_init(struct pp_hwmgr *hwmgr) static int cz_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) { - if (hwmgr != NULL && hwmgr->backend != NULL) { + if (hwmgr != NULL) { + phm_destroy_table(hwmgr, &(hwmgr->enable_clock_power_gatings)); + phm_destroy_table(hwmgr, &(hwmgr->set_power_state)); + phm_destroy_table(hwmgr, &(hwmgr->enable_dynamic_state_management)); + phm_destroy_table(hwmgr, &(hwmgr->disable_dynamic_state_management)); + phm_destroy_table(hwmgr, &(hwmgr->power_down_asic)); + phm_destroy_table(hwmgr, &(hwmgr->setup_asic)); kfree(hwmgr->backend); - kfree(hwmgr); + hwmgr->backend = NULL; } return 0; } @@ -1939,7 +1944,7 @@ static const struct pp_hwmgr_func cz_hwmgr_funcs = { .read_sensor = cz_read_sensor, }; -int cz_hwmgr_init(struct pp_hwmgr *hwmgr) +int cz_init_function_pointers(struct pp_hwmgr *hwmgr) { hwmgr->hwmgr_func = &cz_hwmgr_funcs; hwmgr->pptable_func = &pptable_funcs; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h index c477f1cf3f23..508b422d6159 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.h @@ -316,7 +316,6 @@ struct cz_hwmgr { struct pp_hwmgr; -int cz_hwmgr_init(struct pp_hwmgr *hwmgr); int cz_dpm_powerdown_uvd(struct pp_hwmgr *hwmgr); int cz_dpm_powerup_uvd(struct pp_hwmgr *hwmgr); int cz_dpm_powerdown_vce(struct pp_hwmgr *hwmgr); diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c index 71822ae73a12..bc7d8bd7e7cb 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c @@ -35,7 +35,7 @@ static int phm_run_table(struct pp_hwmgr *hwmgr, phm_table_function *function; if (rt_table->function_list == NULL) { - pr_debug("[ powerplay ] this function not implement!\n"); + pr_debug("this function not implement!\n"); return 0; } @@ -63,14 +63,14 @@ int phm_dispatch_table(struct pp_hwmgr *hwmgr, void *temp_storage; if (hwmgr == NULL || rt_table == NULL) { - printk(KERN_ERR "[ powerplay ] Invalid Parameter!\n"); + pr_err("Invalid Parameter!\n"); return -EINVAL; } if (0 != rt_table->storage_size) { temp_storage = kzalloc(rt_table->storage_size, GFP_KERNEL); if (temp_storage == NULL) { - printk(KERN_ERR "[ powerplay ] Could not allocate table temporary storage\n"); + pr_err("Could not allocate table temporary storage\n"); return -ENOMEM; } } else { @@ -95,7 +95,7 @@ int phm_construct_table(struct pp_hwmgr *hwmgr, phm_table_function *rtf; if (hwmgr == NULL || master_table == NULL || rt_table == NULL) { - printk(KERN_ERR "[ powerplay ] Invalid Parameter!\n"); + pr_err("Invalid Parameter!\n"); return -EINVAL; } @@ -116,7 +116,7 @@ int phm_construct_table(struct pp_hwmgr *hwmgr, for (table_item = master_table->master_list; NULL != table_item->tableFunction; table_item++) { if ((rtf - run_time_list) > function_count) { - printk(KERN_ERR "[ powerplay ] Check function results have changed\n"); + pr_err("Check function results have changed\n"); kfree(run_time_list); return -EINVAL; } @@ -128,7 +128,7 @@ int phm_construct_table(struct pp_hwmgr *hwmgr, } if ((rtf - run_time_list) > function_count) { - printk(KERN_ERR "[ powerplay ] Check function results have changed\n"); + pr_err("Check function results have changed\n"); kfree(run_time_list); return -EINVAL; } @@ -144,7 +144,7 @@ int phm_destroy_table(struct pp_hwmgr *hwmgr, struct phm_runtime_table_header *rt_table) { if (hwmgr == NULL || rt_table == NULL) { - printk(KERN_ERR "[ powerplay ] Invalid Parameter\n"); + pr_err("Invalid Parameter\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c index c355a0f51663..0eb8e886bf35 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c @@ -20,11 +20,11 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/errno.h> #include "hwmgr.h" #include "hardwaremanager.h" #include "power_state.h" -#include "pp_debug.h" #define PHM_FUNC_CHECK(hw) \ do { \ diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c index b03606405a53..2ea9c0e78689 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c @@ -20,6 +20,8 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ + +#include "pp_debug.h" #include "linux/delay.h" #include <linux/types.h> #include <linux/kernel.h> @@ -29,13 +31,12 @@ #include "power_state.h" #include "hwmgr.h" #include "pppcielanes.h" -#include "pp_debug.h" #include "ppatomctrl.h" #include "ppsmc.h" #include "pp_acpi.h" #include "amd_acpi.h" -extern int cz_hwmgr_init(struct pp_hwmgr *hwmgr); +extern int cz_init_function_pointers(struct pp_hwmgr *hwmgr); static int polaris_set_asic_special_caps(struct pp_hwmgr *hwmgr); static void hwmgr_init_default_caps(struct pp_hwmgr *hwmgr); @@ -49,11 +50,11 @@ uint8_t convert_to_vid(uint16_t vddc) return (uint8_t) ((6200 - (vddc * VOLTAGE_SCALE)) / 25); } -int hwmgr_init(struct amd_pp_init *pp_init, struct pp_instance *handle) +int hwmgr_early_init(struct pp_instance *handle) { struct pp_hwmgr *hwmgr; - if ((handle == NULL) || (pp_init == NULL)) + if (handle == NULL) return -EINVAL; hwmgr = kzalloc(sizeof(struct pp_hwmgr), GFP_KERNEL); @@ -62,19 +63,20 @@ int hwmgr_init(struct amd_pp_init *pp_init, struct pp_instance *handle) handle->hwmgr = hwmgr; hwmgr->smumgr = handle->smu_mgr; - hwmgr->device = pp_init->device; - hwmgr->chip_family = pp_init->chip_family; - hwmgr->chip_id = pp_init->chip_id; + hwmgr->device = handle->device; + hwmgr->chip_family = handle->chip_family; + hwmgr->chip_id = handle->chip_id; + hwmgr->feature_mask = handle->feature_mask; hwmgr->usec_timeout = AMD_MAX_USEC_TIMEOUT; hwmgr->power_source = PP_PowerSource_AC; hwmgr->pp_table_version = PP_TABLE_V1; - + hwmgr->dpm_level = AMD_DPM_FORCED_LEVEL_AUTO; hwmgr_init_default_caps(hwmgr); hwmgr_set_user_specify_caps(hwmgr); switch (hwmgr->chip_family) { case AMDGPU_FAMILY_CZ: - cz_hwmgr_init(hwmgr); + cz_init_function_pointers(hwmgr); break; case AMDGPU_FAMILY_VI: switch (hwmgr->chip_id) { @@ -102,7 +104,7 @@ int hwmgr_init(struct amd_pp_init *pp_init, struct pp_instance *handle) default: return -EINVAL; } - smu7_hwmgr_init(hwmgr); + smu7_init_function_pointers(hwmgr); break; default: return -EINVAL; @@ -111,28 +113,7 @@ int hwmgr_init(struct amd_pp_init *pp_init, struct pp_instance *handle) return 0; } -int hwmgr_fini(struct pp_hwmgr *hwmgr) -{ - if (hwmgr == NULL || hwmgr->ps == NULL) - return -EINVAL; - - /* do hwmgr finish*/ - kfree(hwmgr->hardcode_pp_table); - - kfree(hwmgr->backend); - - kfree(hwmgr->start_thermal_controller.function_list); - - kfree(hwmgr->set_temperature_range.function_list); - - kfree(hwmgr->ps); - kfree(hwmgr->current_ps); - kfree(hwmgr->request_ps); - kfree(hwmgr); - return 0; -} - -int hw_init_power_state_table(struct pp_hwmgr *hwmgr) +static int hw_init_power_state_table(struct pp_hwmgr *hwmgr) { int result; unsigned int i; @@ -156,12 +137,20 @@ int hw_init_power_state_table(struct pp_hwmgr *hwmgr) return -ENOMEM; hwmgr->request_ps = kzalloc(size, GFP_KERNEL); - if (hwmgr->request_ps == NULL) + if (hwmgr->request_ps == NULL) { + kfree(hwmgr->ps); + hwmgr->ps = NULL; return -ENOMEM; + } hwmgr->current_ps = kzalloc(size, GFP_KERNEL); - if (hwmgr->current_ps == NULL) + if (hwmgr->current_ps == NULL) { + kfree(hwmgr->request_ps); + kfree(hwmgr->ps); + hwmgr->request_ps = NULL; + hwmgr->ps = NULL; return -ENOMEM; + } state = hwmgr->ps; @@ -181,10 +170,77 @@ int hw_init_power_state_table(struct pp_hwmgr *hwmgr) state = (struct pp_power_state *)((unsigned long)state + size); } + return 0; +} +static int hw_fini_power_state_table(struct pp_hwmgr *hwmgr) +{ + if (hwmgr == NULL) + return -EINVAL; + + kfree(hwmgr->current_ps); + kfree(hwmgr->request_ps); + kfree(hwmgr->ps); + hwmgr->request_ps = NULL; + hwmgr->ps = NULL; + hwmgr->current_ps = NULL; return 0; } +int hwmgr_hw_init(struct pp_instance *handle) +{ + struct pp_hwmgr *hwmgr; + int ret = 0; + + if (handle == NULL) + return -EINVAL; + + hwmgr = handle->hwmgr; + + if (hwmgr->pptable_func == NULL || + hwmgr->pptable_func->pptable_init == NULL || + hwmgr->hwmgr_func->backend_init == NULL) + return -EINVAL; + + ret = hwmgr->pptable_func->pptable_init(hwmgr); + if (ret) + goto err; + + ret = hwmgr->hwmgr_func->backend_init(hwmgr); + if (ret) + goto err1; + + ret = hw_init_power_state_table(hwmgr); + if (ret) + goto err2; + return 0; +err2: + if (hwmgr->hwmgr_func->backend_fini) + hwmgr->hwmgr_func->backend_fini(hwmgr); +err1: + if (hwmgr->pptable_func->pptable_fini) + hwmgr->pptable_func->pptable_fini(hwmgr); +err: + pr_err("amdgpu: powerplay initialization failed\n"); + return ret; +} + +int hwmgr_hw_fini(struct pp_instance *handle) +{ + struct pp_hwmgr *hwmgr; + + if (handle == NULL) + return -EINVAL; + + hwmgr = handle->hwmgr; + + if (hwmgr->hwmgr_func->backend_fini) + hwmgr->hwmgr_func->backend_fini(hwmgr); + if (hwmgr->pptable_func->pptable_fini) + hwmgr->pptable_func->pptable_fini(hwmgr); + return hw_fini_power_state_table(hwmgr); +} + /** * Returns once the part of the register indicated by the mask has @@ -197,7 +253,7 @@ int phm_wait_on_register(struct pp_hwmgr *hwmgr, uint32_t index, uint32_t cur_value; if (hwmgr == NULL || hwmgr->device == NULL) { - printk(KERN_ERR "[ powerplay ] Invalid Hardware Manager!"); + pr_err("Invalid Hardware Manager!"); return -EINVAL; } @@ -227,7 +283,7 @@ void phm_wait_on_indirect_register(struct pp_hwmgr *hwmgr, uint32_t mask) { if (hwmgr == NULL || hwmgr->device == NULL) { - printk(KERN_ERR "[ powerplay ] Invalid Hardware Manager!"); + pr_err("Invalid Hardware Manager!"); return; } @@ -288,7 +344,7 @@ int phm_trim_voltage_table(struct pp_atomctrl_voltage_table *vol_table) memcpy(vol_table, table, sizeof(struct pp_atomctrl_voltage_table)); kfree(table); - + table = NULL; return 0; } @@ -549,7 +605,7 @@ int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr table_clk_vlt = kzalloc(table_size, GFP_KERNEL); if (NULL == table_clk_vlt) { - printk(KERN_ERR "[ powerplay ] Can not allocate space for vddc_dep_on_dal_pwrl! \n"); + pr_err("Can not allocate space for vddc_dep_on_dal_pwrl! \n"); return -ENOMEM; } else { table_clk_vlt->count = 4; @@ -569,21 +625,6 @@ int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr return 0; } -int phm_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) -{ - if (NULL != hwmgr->dyn_state.vddc_dep_on_dal_pwrl) { - kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl); - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; - } - - if (NULL != hwmgr->backend) { - kfree(hwmgr->backend); - hwmgr->backend = NULL; - } - - return 0; -} - uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask) { uint32_t level = 0; @@ -625,7 +666,7 @@ void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr) return; } } - printk(KERN_ERR "DAL requested level can not" + pr_err("DAL requested level can not" " found a available voltage in VDDC DPM Table \n"); } @@ -683,14 +724,14 @@ void hwmgr_init_default_caps(struct pp_hwmgr *hwmgr) int hwmgr_set_user_specify_caps(struct pp_hwmgr *hwmgr) { - if (amdgpu_pp_feature_mask & PP_SCLK_DEEP_SLEEP_MASK) + if (hwmgr->feature_mask & PP_SCLK_DEEP_SLEEP_MASK) phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep); else phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep); - if (amdgpu_pp_feature_mask & PP_POWER_CONTAINMENT_MASK) { + if (hwmgr->feature_mask & PP_POWER_CONTAINMENT_MASK) { phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PowerContainment); phm_cap_set(hwmgr->platform_descriptor.platformCaps, @@ -701,7 +742,6 @@ int hwmgr_set_user_specify_caps(struct pp_hwmgr *hwmgr) phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_CAC); } - hwmgr->feature_mask = amdgpu_pp_feature_mask; return 0; } @@ -727,17 +767,10 @@ int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type, int polaris_set_asic_special_caps(struct pp_hwmgr *hwmgr) { - /* power tune caps Assume disabled */ + phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping); phm_cap_set(hwmgr->platform_descriptor.platformCaps, - PHM_PlatformCaps_DBRamping); - phm_cap_set(hwmgr->platform_descriptor.platformCaps, - PHM_PlatformCaps_TDRamping); - phm_cap_set(hwmgr->platform_descriptor.platformCaps, - PHM_PlatformCaps_TCPRamping); - - phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_RegulatorHot); phm_cap_set(hwmgr->platform_descriptor.platformCaps, @@ -746,9 +779,19 @@ int polaris_set_asic_special_caps(struct pp_hwmgr *hwmgr) phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TablelessHardwareInterface); - if ((hwmgr->chip_id == CHIP_POLARIS11) || (hwmgr->chip_id == CHIP_POLARIS12)) + + if (hwmgr->chip_id != CHIP_POLARIS10) phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SPLLShutdownSupport); + + if (hwmgr->chip_id != CHIP_POLARIS11) { + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_DBRamping); + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_TDRamping); + phm_cap_set(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_TCPRamping); + } return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c index 0894527d932f..953e0c9ad7cd 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c @@ -20,13 +20,13 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/module.h> #include <linux/slab.h> #include "ppatomctrl.h" #include "atombios.h" #include "cgs_common.h" -#include "pp_debug.h" #include "ppevvmath.h" #define MEM_ID_MASK 0xff000000 @@ -145,10 +145,10 @@ int atomctrl_initialize_mc_reg_table( GetIndexIntoMasterTable(DATA, VRAM_Info), &size, &frev, &crev); if (module_index >= vram_info->ucNumOfVRAMModule) { - printk(KERN_ERR "[ powerplay ] Invalid VramInfo table."); + pr_err("Invalid VramInfo table."); result = -1; } else if (vram_info->sHeader.ucTableFormatRevision < 2) { - printk(KERN_ERR "[ powerplay ] Invalid VramInfo table."); + pr_err("Invalid VramInfo table."); result = -1; } @@ -688,7 +688,7 @@ int atomctrl_calculate_voltage_evv_on_sclk( fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM7), 1000); break; default: - printk(KERN_ERR "DPM Level not supported\n"); + pr_err("DPM Level not supported\n"); fPowerDPMx = Convert_ULONG_ToFraction(1); fDerateTDP = GetScaledFraction(le32_to_cpu(getASICProfilingInfo->ulTdpDerateDPM0), 1000); } @@ -1396,3 +1396,25 @@ int atomctrl_get_avfs_information(struct pp_hwmgr *hwmgr, return 0; } + +int atomctrl_get_svi2_info(struct pp_hwmgr *hwmgr, uint8_t voltage_type, + uint8_t *svd_gpio_id, uint8_t *svc_gpio_id, + uint16_t *load_line) +{ + ATOM_VOLTAGE_OBJECT_INFO_V3_1 *voltage_info = + (ATOM_VOLTAGE_OBJECT_INFO_V3_1 *)get_voltage_info_table(hwmgr->device); + + const ATOM_VOLTAGE_OBJECT_V3 *voltage_object; + + PP_ASSERT_WITH_CODE((NULL != voltage_info), + "Could not find Voltage Table in BIOS.", return -EINVAL); + + voltage_object = atomctrl_lookup_voltage_type_v3 + (voltage_info, voltage_type, VOLTAGE_OBJ_SVID2); + + *svd_gpio_id = voltage_object->asSVID2Obj.ucSVDGpioId; + *svc_gpio_id = voltage_object->asSVID2Obj.ucSVCGpioId; + *load_line = voltage_object->asSVID2Obj.usLoadLine_PSI; + + return 0; +} diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h index fc898afce002..e9fe2e84006b 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h @@ -311,5 +311,8 @@ extern int atomctrl_get_smc_sclk_range_table(struct pp_hwmgr *hwmgr, struct pp_a extern int atomctrl_get_avfs_information(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl__avfs_parameters *param); +extern int atomctrl_get_svi2_info(struct pp_hwmgr *hwmgr, uint8_t voltage_type, + uint8_t *svd_gpio_id, uint8_t *svc_gpio_id, + uint16_t *load_line); #endif diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c index c45bd2560468..84f01fd33aff 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c @@ -20,13 +20,13 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/module.h> #include <linux/slab.h> #include "process_pptables_v1_0.h" #include "ppatomctrl.h" #include "atombios.h" -#include "pp_debug.h" #include "hwmgr.h" #include "cgs_common.h" #include "pptable_v1_0.h" @@ -535,7 +535,7 @@ static int get_pcie_table( if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count) pcie_count = (uint32_t)atom_pcie_table->ucNumEntries; else - printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \ + pr_err("Number of Pcie Entries exceed the number of SCLK Dpm Levels! \ Disregarding the excess entries... \n"); pcie_table->count = pcie_count; @@ -577,7 +577,7 @@ static int get_pcie_table( if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count) pcie_count = (uint32_t)atom_pcie_table->ucNumEntries; else - printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \ + pr_err("Number of Pcie Entries exceed the number of SCLK Dpm Levels! \ Disregarding the excess entries... \n"); pcie_table->count = pcie_count; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c index a4e9cf429e62..ed6c934927fb 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c @@ -20,6 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -27,7 +28,6 @@ #include "processpptables.h" #include <atom-types.h> #include <atombios.h> -#include "pp_debug.h" #include "pptable.h" #include "power_state.h" #include "hwmgr.h" diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.c index a1fc4fcac1e0..8cf71f3c6d0e 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.c @@ -147,22 +147,22 @@ int smu7_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate) data->uvd_power_gated = bgate; if (bgate) { - cgs_set_clockgating_state(hwmgr->device, - AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_GATE); cgs_set_powergating_state(hwmgr->device, AMD_IP_BLOCK_TYPE_UVD, AMD_PG_STATE_GATE); + cgs_set_clockgating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_UVD, + AMD_CG_STATE_GATE); smu7_update_uvd_dpm(hwmgr, true); smu7_powerdown_uvd(hwmgr); } else { smu7_powerup_uvd(hwmgr); - cgs_set_powergating_state(hwmgr->device, - AMD_IP_BLOCK_TYPE_UVD, - AMD_CG_STATE_UNGATE); cgs_set_clockgating_state(hwmgr->device, AMD_IP_BLOCK_TYPE_UVD, AMD_CG_STATE_UNGATE); + cgs_set_powergating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_UVD, + AMD_CG_STATE_UNGATE); smu7_update_uvd_dpm(hwmgr, false); } @@ -173,12 +173,12 @@ int smu7_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate) { struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); - if (data->vce_power_gated == bgate) - return 0; - data->vce_power_gated = bgate; if (bgate) { + cgs_set_powergating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_UNGATE); cgs_set_clockgating_state(hwmgr->device, AMD_IP_BLOCK_TYPE_VCE, AMD_CG_STATE_GATE); @@ -186,10 +186,13 @@ int smu7_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate) smu7_powerdown_vce(hwmgr); } else { smu7_powerup_vce(hwmgr); - smu7_update_vce_dpm(hwmgr, false); cgs_set_clockgating_state(hwmgr->device, AMD_IP_BLOCK_TYPE_VCE, AMD_CG_STATE_UNGATE); + cgs_set_powergating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_VCE, + AMD_PG_STATE_UNGATE); + smu7_update_vce_dpm(hwmgr, false); } return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.h index d52a28c343e3..c96ed9ed7eaf 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.h @@ -22,7 +22,7 @@ */ #ifndef _SMU7_CLOCK_POWER_GATING_H_ -#define _SMU7_CLOCK__POWER_GATING_H_ +#define _SMU7_CLOCK_POWER_GATING_H_ #include "smu7_hwmgr.h" #include "pp_asicblocks.h" diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c index a74f60a575ae..f75ee33ec5bb 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c @@ -20,13 +20,13 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/module.h> #include <linux/slab.h> #include <linux/fb.h> #include <asm/div64.h> #include "linux/delay.h" #include "pp_acpi.h" -#include "pp_debug.h" #include "ppatomctrl.h" #include "atombios.h" #include "pptable_v1_0.h" @@ -40,6 +40,8 @@ #include "hwmgr.h" #include "smu7_hwmgr.h" +#include "smu7_smumgr.h" +#include "smu_ucode_xfer_vi.h" #include "smu7_powertune.h" #include "smu7_dyn_defaults.h" #include "smu7_thermal.h" @@ -88,6 +90,8 @@ enum DPM_EVENT_SRC { }; static const unsigned long PhwVIslands_Magic = (unsigned long)(PHM_VIslands_Magic); +static int smu7_force_clock_level(struct pp_hwmgr *hwmgr, + enum pp_clock_type type, uint32_t mask); static struct smu7_power_state *cast_phw_smu7_power_state( struct pp_hw_power_state *hw_ps) @@ -994,7 +998,7 @@ static int smu7_start_dpm(struct pp_hwmgr *hwmgr) SWRST_COMMAND_1, RESETLC, 0x0); if (smu7_enable_sclk_mclk_dpm(hwmgr)) { - printk(KERN_ERR "Failed to enable Sclk DPM and Mclk DPM!"); + pr_err("Failed to enable Sclk DPM and Mclk DPM!"); return -EINVAL; } @@ -1079,7 +1083,7 @@ static void smu7_set_dpm_event_sources(struct pp_hwmgr *hwmgr, uint32_t sources) switch (sources) { default: - printk(KERN_ERR "Unknown throttling event sources."); + pr_err("Unknown throttling event sources."); /* fall through */ case 0: protection = false; @@ -1292,6 +1296,10 @@ int smu7_disable_dpm_tasks(struct pp_hwmgr *hwmgr) PP_ASSERT_WITH_CODE((tmp_result == 0), "Failed to disable SMC CAC!", result = tmp_result); + tmp_result = smu7_disable_didt_config(hwmgr); + PP_ASSERT_WITH_CODE((tmp_result == 0), + "Failed to disable DIDT!", result = tmp_result); + PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_SPLL_SPREAD_SPECTRUM, SSEN, 0); PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, @@ -1375,6 +1383,15 @@ static void smu7_init_dpm_defaults(struct pp_hwmgr *hwmgr) data->force_pcie_gen = PP_PCIEGenInvalid; data->ulv_supported = hwmgr->feature_mask & PP_ULV_MASK ? true : false; + if (hwmgr->chip_id == CHIP_POLARIS12 || hwmgr->smumgr->is_kicker) { + uint8_t tmp1, tmp2; + uint16_t tmp3 = 0; + atomctrl_get_svi2_info(hwmgr, VOLTAGE_TYPE_VDDC, &tmp1, &tmp2, + &tmp3); + tmp3 = (tmp3 >> 5) & 0x3; + data->vddc_phase_shed_control = ((tmp3 << 1) | (tmp3 >> 1)) & 0x3; + } + data->fast_watermark_threshold = 100; if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr, VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2)) @@ -1499,7 +1516,7 @@ static int smu7_get_evv_voltages(struct pp_hwmgr *hwmgr) data->vddcgfx_leakage.count++; } } else { - printk("Error retrieving EVV voltage value!\n"); + pr_info("Error retrieving EVV voltage value!\n"); } } } else { @@ -1527,7 +1544,7 @@ static int smu7_get_evv_voltages(struct pp_hwmgr *hwmgr) if (vddc >= 2000 || vddc == 0) return -EINVAL; } else { - printk(KERN_WARNING "failed to retrieving EVV voltage!\n"); + pr_warning("failed to retrieving EVV voltage!\n"); continue; } @@ -1567,7 +1584,7 @@ static void smu7_patch_ppt_v1_with_vdd_leakage(struct pp_hwmgr *hwmgr, } if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0) - printk(KERN_ERR "Voltage value looks like a Leakage ID but it's not patched \n"); + pr_err("Voltage value looks like a Leakage ID but it's not patched \n"); } /** @@ -2032,7 +2049,7 @@ static void smu7_patch_ppt_v0_with_vdd_leakage(struct pp_hwmgr *hwmgr, } if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0) - printk(KERN_ERR "Voltage value looks like a Leakage ID but it's not patched \n"); + pr_err("Voltage value looks like a Leakage ID but it's not patched \n"); } @@ -2267,6 +2284,21 @@ static int smu7_set_private_data_based_on_pptable_v0(struct pp_hwmgr *hwmgr) return 0; } +static int smu7_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) +{ + if (NULL != hwmgr->dyn_state.vddc_dep_on_dal_pwrl) { + kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl); + hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; + } + pp_smu7_thermal_fini(hwmgr); + if (NULL != hwmgr->backend) { + kfree(hwmgr->backend); + hwmgr->backend = NULL; + } + + return 0; +} + static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) { struct smu7_hwmgr *data; @@ -2277,6 +2309,7 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) return -ENOMEM; hwmgr->backend = data; + pp_smu7_thermal_initialize(hwmgr); smu7_patch_voltage_workaround(hwmgr); smu7_init_dpm_defaults(hwmgr); @@ -2285,7 +2318,7 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) result = smu7_get_evv_voltages(hwmgr); if (result) { - printk("Get EVV Voltage Failed. Abort Driver loading!\n"); + pr_info("Get EVV Voltage Failed. Abort Driver loading!\n"); return -EINVAL; } @@ -2334,7 +2367,7 @@ static int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr) smu7_thermal_parameter_init(hwmgr); } else { /* Ignore return value in here, we are cleaning up a mess. */ - phm_hwmgr_backend_fini(hwmgr); + smu7_hwmgr_backend_fini(hwmgr); } return 0; @@ -2466,36 +2499,156 @@ static int smu7_force_dpm_lowest(struct pp_hwmgr *hwmgr) } return 0; +} + +static int smu7_get_profiling_clk(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level, + uint32_t *sclk_mask, uint32_t *mclk_mask, uint32_t *pcie_mask) +{ + uint32_t percentage; + struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); + struct smu7_dpm_table *golden_dpm_table = &data->golden_dpm_table; + int32_t tmp_mclk; + int32_t tmp_sclk; + int32_t count; + + if (golden_dpm_table->mclk_table.count < 1) + return -EINVAL; + + percentage = 100 * golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count - 1].value / + golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count - 1].value; + + if (golden_dpm_table->mclk_table.count == 1) { + percentage = 70; + tmp_mclk = golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count - 1].value; + *mclk_mask = golden_dpm_table->mclk_table.count - 1; + } else { + tmp_mclk = golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count - 2].value; + *mclk_mask = golden_dpm_table->mclk_table.count - 2; + } + + tmp_sclk = tmp_mclk * percentage / 100; + + if (hwmgr->pp_table_version == PP_TABLE_V0) { + for (count = hwmgr->dyn_state.vddc_dependency_on_sclk->count-1; + count >= 0; count--) { + if (tmp_sclk >= hwmgr->dyn_state.vddc_dependency_on_sclk->entries[count].clk) { + tmp_sclk = hwmgr->dyn_state.vddc_dependency_on_sclk->entries[count].clk; + *sclk_mask = count; + break; + } + } + if (count < 0 || level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) + *sclk_mask = 0; + + if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) + *sclk_mask = hwmgr->dyn_state.vddc_dependency_on_sclk->count-1; + } else if (hwmgr->pp_table_version == PP_TABLE_V1) { + struct phm_ppt_v1_information *table_info = + (struct phm_ppt_v1_information *)(hwmgr->pptable); + + for (count = table_info->vdd_dep_on_sclk->count-1; count >= 0; count--) { + if (tmp_sclk >= table_info->vdd_dep_on_sclk->entries[count].clk) { + tmp_sclk = table_info->vdd_dep_on_sclk->entries[count].clk; + *sclk_mask = count; + break; + } + } + if (count < 0 || level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) + *sclk_mask = 0; + if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) + *sclk_mask = table_info->vdd_dep_on_sclk->count - 1; + } + + if (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) + *mclk_mask = 0; + else if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) + *mclk_mask = golden_dpm_table->mclk_table.count - 1; + + *pcie_mask = data->dpm_table.pcie_speed_table.count - 1; + return 0; } + static int smu7_force_dpm_level(struct pp_hwmgr *hwmgr, enum amd_dpm_forced_level level) { int ret = 0; + uint32_t sclk_mask = 0; + uint32_t mclk_mask = 0; + uint32_t pcie_mask = 0; + uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; + + if (level == hwmgr->dpm_level) + return ret; + + if (!(hwmgr->dpm_level & profile_mode_mask)) { + /* enter profile mode, save current level, disable gfx cg*/ + if (level & profile_mode_mask) { + hwmgr->saved_dpm_level = hwmgr->dpm_level; + cgs_set_clockgating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_GFX, + AMD_CG_STATE_UNGATE); + } + } else { + /* exit profile mode, restore level, enable gfx cg*/ + if (!(level & profile_mode_mask)) { + if (level == AMD_DPM_FORCED_LEVEL_PROFILE_EXIT) + level = hwmgr->saved_dpm_level; + cgs_set_clockgating_state(hwmgr->device, + AMD_IP_BLOCK_TYPE_GFX, + AMD_CG_STATE_GATE); + } + } switch (level) { case AMD_DPM_FORCED_LEVEL_HIGH: ret = smu7_force_dpm_highest(hwmgr); if (ret) return ret; + hwmgr->dpm_level = level; break; case AMD_DPM_FORCED_LEVEL_LOW: ret = smu7_force_dpm_lowest(hwmgr); if (ret) return ret; + hwmgr->dpm_level = level; break; case AMD_DPM_FORCED_LEVEL_AUTO: ret = smu7_unforce_dpm_levels(hwmgr); if (ret) return ret; + hwmgr->dpm_level = level; + break; + case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD: + case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK: + case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK: + case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK: + ret = smu7_get_profiling_clk(hwmgr, level, &sclk_mask, &mclk_mask, &pcie_mask); + if (ret) + return ret; + hwmgr->dpm_level = level; + smu7_force_clock_level(hwmgr, PP_SCLK, 1<<sclk_mask); + smu7_force_clock_level(hwmgr, PP_MCLK, 1<<mclk_mask); + smu7_force_clock_level(hwmgr, PP_PCIE, 1<<pcie_mask); + break; + case AMD_DPM_FORCED_LEVEL_MANUAL: + hwmgr->dpm_level = level; + break; + case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT: default: break; } - hwmgr->dpm_level = level; + if (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK && hwmgr->saved_dpm_level != AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) + smu7_fan_ctrl_set_fan_speed_percent(hwmgr, 100); + else if (level != AMD_DPM_FORCED_LEVEL_PROFILE_PEAK && hwmgr->saved_dpm_level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) + smu7_fan_ctrl_reset_fan_speed_to_default(hwmgr); - return ret; + return 0; } static int smu7_get_power_state_size(struct pp_hwmgr *hwmgr) @@ -2898,11 +3051,11 @@ static int smu7_get_pp_table_entry_v1(struct pp_hwmgr *hwmgr, if (dep_mclk_table != NULL && dep_mclk_table->count == 1) { if (dep_mclk_table->entries[0].clk != data->vbios_boot_state.mclk_bootup_value) - printk(KERN_ERR "Single MCLK entry VDDCI/MCLK dependency table " + pr_err("Single MCLK entry VDDCI/MCLK dependency table " "does not match VBIOS boot MCLK level"); if (dep_mclk_table->entries[0].vddci != data->vbios_boot_state.vddci_bootup_value) - printk(KERN_ERR "Single VDDCI entry VDDCI/MCLK dependency table " + pr_err("Single VDDCI entry VDDCI/MCLK dependency table " "does not match VBIOS boot VDDCI level"); } @@ -3046,11 +3199,11 @@ static int smu7_get_pp_table_entry_v0(struct pp_hwmgr *hwmgr, if (dep_mclk_table != NULL && dep_mclk_table->count == 1) { if (dep_mclk_table->entries[0].clk != data->vbios_boot_state.mclk_bootup_value) - printk(KERN_ERR "Single MCLK entry VDDCI/MCLK dependency table " + pr_err("Single MCLK entry VDDCI/MCLK dependency table " "does not match VBIOS boot MCLK level"); if (dep_mclk_table->entries[0].v != data->vbios_boot_state.vddci_bootup_value) - printk(KERN_ERR "Single VDDCI entry VDDCI/MCLK dependency table " + pr_err("Single VDDCI entry VDDCI/MCLK dependency table " "does not match VBIOS boot VDDCI level"); } @@ -3590,9 +3743,9 @@ static int smu7_notify_link_speed_change_after_state_change( if (acpi_pcie_perf_request(hwmgr->device, request, false)) { if (PP_PCIEGen2 == target_link_speed) - printk("PSPP request to switch to Gen2 from Gen3 Failed!"); + pr_info("PSPP request to switch to Gen2 from Gen3 Failed!"); else - printk("PSPP request to switch to Gen1 from Gen2 Failed!"); + pr_info("PSPP request to switch to Gen1 from Gen2 Failed!"); } } @@ -4029,7 +4182,9 @@ static int smu7_force_clock_level(struct pp_hwmgr *hwmgr, { struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); - if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) + if (hwmgr->dpm_level & (AMD_DPM_FORCED_LEVEL_AUTO | + AMD_DPM_FORCED_LEVEL_LOW | + AMD_DPM_FORCED_LEVEL_HIGH)) return -EINVAL; switch (type) { @@ -4252,16 +4407,14 @@ static int smu7_get_sclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks) if (table_info == NULL || table_info->vdd_dep_on_sclk == NULL) return -EINVAL; dep_sclk_table = table_info->vdd_dep_on_sclk; - for (i = 0; i < dep_sclk_table->count; i++) { + for (i = 0; i < dep_sclk_table->count; i++) clocks->clock[i] = dep_sclk_table->entries[i].clk; - clocks->count++; - } + clocks->count = dep_sclk_table->count; } else if (hwmgr->pp_table_version == PP_TABLE_V0) { sclk_table = hwmgr->dyn_state.vddc_dependency_on_sclk; - for (i = 0; i < sclk_table->count; i++) { + for (i = 0; i < sclk_table->count; i++) clocks->clock[i] = sclk_table->entries[i].clk; - clocks->count++; - } + clocks->count = sclk_table->count; } return 0; @@ -4295,14 +4448,13 @@ static int smu7_get_mclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks) clocks->clock[i] = dep_mclk_table->entries[i].clk; clocks->latency[i] = smu7_get_mem_latency(hwmgr, dep_mclk_table->entries[i].clk); - clocks->count++; } + clocks->count = dep_mclk_table->count; } else if (hwmgr->pp_table_version == PP_TABLE_V0) { mclk_table = hwmgr->dyn_state.vddc_dependency_on_mclk; - for (i = 0; i < mclk_table->count; i++) { + for (i = 0; i < mclk_table->count; i++) clocks->clock[i] = mclk_table->entries[i].clk; - clocks->count++; - } + clocks->count = mclk_table->count; } return 0; } @@ -4324,9 +4476,35 @@ static int smu7_get_clock_by_type(struct pp_hwmgr *hwmgr, enum amd_pp_clock_type return 0; } +static int smu7_request_firmware(struct pp_hwmgr *hwmgr) +{ + int ret; + struct cgs_firmware_info info = {0}; + + ret = cgs_get_firmware_info(hwmgr->device, + smu7_convert_fw_type_to_cgs(UCODE_ID_SMU), + &info); + if (ret || !info.kptr) + return -EINVAL; + + return 0; +} + +static int smu7_release_firmware(struct pp_hwmgr *hwmgr) +{ + int ret; + + ret = cgs_rel_firmware(hwmgr->device, + smu7_convert_fw_type_to_cgs(UCODE_ID_SMU)); + if (ret) + return -EINVAL; + + return 0; +} + static const struct pp_hwmgr_func smu7_hwmgr_funcs = { .backend_init = &smu7_hwmgr_backend_init, - .backend_fini = &phm_hwmgr_backend_fini, + .backend_fini = &smu7_hwmgr_backend_fini, .asic_setup = &smu7_setup_asic_task, .dynamic_state_management_enable = &smu7_enable_dpm_tasks, .apply_state_adjust_rules = smu7_apply_state_adjust_rules, @@ -4371,6 +4549,8 @@ static const struct pp_hwmgr_func smu7_hwmgr_funcs = { .get_clock_by_type = smu7_get_clock_by_type, .read_sensor = smu7_read_sensor, .dynamic_state_management_disable = smu7_disable_dpm_tasks, + .request_firmware = smu7_request_firmware, + .release_firmware = smu7_release_firmware, }; uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock, @@ -4390,7 +4570,7 @@ uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock, return i; } -int smu7_hwmgr_init(struct pp_hwmgr *hwmgr) +int smu7_init_function_pointers(struct pp_hwmgr *hwmgr) { int ret = 0; @@ -4400,7 +4580,6 @@ int smu7_hwmgr_init(struct pp_hwmgr *hwmgr) else if (hwmgr->pp_table_version == PP_TABLE_V1) hwmgr->pptable_func = &pptable_v1_0_funcs; - pp_smu7_thermal_initialize(hwmgr); return ret; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h index 27e7f76ad8a6..f221e17b67e7 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h @@ -268,7 +268,7 @@ struct smu7_hwmgr { uint32_t fast_watermark_threshold; /* ---- Phase Shedding ---- */ - bool vddc_phase_shed_control; + uint8_t vddc_phase_shed_control; /* ---- DI/DT ---- */ struct smu7_display_timing display_timing; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c index 6cd1287a7a8f..1dc31aa72781 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c @@ -20,17 +20,19 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include "hwmgr.h" #include "smumgr.h" #include "smu7_hwmgr.h" #include "smu7_powertune.h" -#include "pp_debug.h" #include "smu7_common.h" #define VOLTAGE_SCALE 4 static uint32_t DIDTBlock_Info = SQ_IR_MASK | TCP_IR_MASK | TD_PCC_MASK; +static uint32_t Polaris11_DIDTBlock_Info = SQ_PCC_MASK | TCP_IR_MASK | TD_PCC_MASK; + static const struct gpu_pt_config_reg GCCACConfig_Polaris10[] = { /* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * Offset Mask Shift Value Type @@ -261,9 +263,9 @@ static const struct gpu_pt_config_reg DIDTConfig_Polaris11[] = { { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_0_MASK, DIDT_SQ_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_1_MASK, DIDT_SQ_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_2_MASK, DIDT_SQ_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, @@ -271,12 +273,12 @@ static const struct gpu_pt_config_reg DIDTConfig_Polaris11[] = { { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__UNUSED_0_MASK, DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3853, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3153, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK, DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, @@ -284,8 +286,8 @@ static const struct gpu_pt_config_reg DIDTConfig_Polaris11[] = { { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, - { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__UNUSED_0_MASK, DIDT_SQ_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT0_MASK, DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT, 0x000a, GPU_CONFIGREG_DIDT_IND }, @@ -373,55 +375,305 @@ static const struct gpu_pt_config_reg DIDTConfig_Polaris11[] = { { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { 0xFFFFFFFF } }; +static const struct gpu_pt_config_reg DIDTConfig_Polaris12[] = { +/* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + * Offset Mask Shift Value Type + * --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT, 0x0073, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT, 0x00ab, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT, 0x005a, GPU_CONFIGREG_DIDT_IND }, -static int smu7_enable_didt(struct pp_hwmgr *hwmgr, const bool enable) + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT, 0x0067, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT, 0x0084, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT, 0x0027, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT, 0x00aa, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_0_MASK, DIDT_TD_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_1_MASK, DIDT_TD_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_2_MASK, DIDT_TD_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__UNUSED_0_MASK, DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__UNUSED_0_MASK, DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__USE_REF_CLOCK_MASK, DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__UNUSED_0_MASK, DIDT_TD_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT, 0x0004, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT, 0x0037, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT, 0x0054, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__UNUSED_0_MASK, DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_0_MASK, DIDT_TCP_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x0032, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_1_MASK, DIDT_TCP_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_2_MASK, DIDT_TCP_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__UNUSED_0_MASK, DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK, DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK, DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { 0xFFFFFFFF } +}; + +static const struct gpu_pt_config_reg DIDTConfig_Polaris11_Kicker[] = { +/* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + * Offset Mask Shift Value Type + * --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + /* DIDT_SQ */ + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT, 0x004c, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT, 0x00d0, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT, 0x0069, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT0_3, DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK, DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT, 0x0048, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT, 0x005f, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT, 0x007a, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT, 0x001f, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT4_7, DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK, DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT, 0x002d, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT, 0x0088, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_WEIGHT8_11, DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK, DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MIN_POWER_MASK, DIDT_SQ_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL1, DIDT_SQ_CTRL1__MAX_POWER_MASK, DIDT_SQ_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__UNUSED_0_MASK, DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL_OCP, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK, DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_0_MASK, DIDT_SQ_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_1_MASK, DIDT_SQ_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL2, DIDT_SQ_CTRL2__UNUSED_2_MASK, DIDT_SQ_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_STALL_CTRL, DIDT_SQ_STALL_CTRL__UNUSED_0_MASK, DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_TUNING_CTRL, DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK, DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK, DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__PHASE_OFFSET_MASK, DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK, DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_SQ_CTRL0, DIDT_SQ_CTRL0__UNUSED_0_MASK, DIDT_SQ_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + /* DIDT_TD */ + { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT0_MASK, DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT, 0x000a, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT1_MASK, DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT2_MASK, DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT, 0x0017, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_WEIGHT0_3, DIDT_TD_WEIGHT0_3__WEIGHT3_MASK, DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT, 0x002f, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT4_MASK, DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT, 0x0046, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT5_MASK, DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT, 0x005d, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT6_MASK, DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_WEIGHT4_7, DIDT_TD_WEIGHT4_7__WEIGHT7_MASK, DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MIN_POWER_MASK, DIDT_TD_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL1, DIDT_TD_CTRL1__MAX_POWER_MASK, DIDT_TD_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__UNUSED_0_MASK, DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL_OCP, DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3fff, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_0_MASK, DIDT_TD_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x000f, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_1_MASK, DIDT_TD_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL2, DIDT_TD_CTRL2__UNUSED_2_MASK, DIDT_TD_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_STALL_CTRL, DIDT_TD_STALL_CTRL__UNUSED_0_MASK, DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x0dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_TUNING_CTRL, DIDT_TD_TUNING_CTRL__UNUSED_0_MASK, DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__USE_REF_CLOCK_MASK, DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__PHASE_OFFSET_MASK, DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0008, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TD_CTRL0, DIDT_TD_CTRL0__UNUSED_0_MASK, DIDT_TD_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + /* DIDT_TCP */ + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT, 0x0004, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT, 0x0037, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT0_3, DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK, DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT, 0x00ff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT, 0x0054, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_WEIGHT4_7, DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK, DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MIN_POWER_MASK, DIDT_TCP_CTRL1__MIN_POWER__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL1, DIDT_TCP_CTRL1__MAX_POWER_MASK, DIDT_TCP_CTRL1__MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__UNUSED_0_MASK, DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL_OCP, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK, DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT, 0xffff, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK, DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_0_MASK, DIDT_TCP_CTRL2__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK, DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT, 0x0032, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_1_MASK, DIDT_TCP_CTRL2__UNUSED_1__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK, DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL2, DIDT_TCP_CTRL2__UNUSED_2_MASK, DIDT_TCP_CTRL2__UNUSED_2__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK, DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK, DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT,0x01aa, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_STALL_CTRL, DIDT_TCP_STALL_CTRL__UNUSED_0_MASK, DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK, DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK, DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT, 0x3dde, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_TUNING_CTRL, DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK, DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT, 0x0001, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK, DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__PHASE_OFFSET_MASK, DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK, DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK, DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK, DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT, 0x0010, GPU_CONFIGREG_DIDT_IND }, + { ixDIDT_TCP_CTRL0, DIDT_TCP_CTRL0__UNUSED_0_MASK, DIDT_TCP_CTRL0__UNUSED_0__SHIFT, 0x0000, GPU_CONFIGREG_DIDT_IND }, + + { 0xFFFFFFFF } /* End of list */ +}; + +static int smu7_enable_didt(struct pp_hwmgr *hwmgr, const bool enable) +{ uint32_t en = enable ? 1 : 0; + uint32_t block_en = 0; int32_t result = 0; + uint32_t didt_block; uint32_t data; - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping)) { - data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0); - data &= ~DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK; - data |= ((en << DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK); - cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0, data); - DIDTBlock_Info &= ~SQ_Enable_MASK; - DIDTBlock_Info |= en << SQ_Enable_SHIFT; - } + if (hwmgr->chip_id == CHIP_POLARIS11) + didt_block = Polaris11_DIDTBlock_Info; + else + didt_block = DIDTBlock_Info; - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping)) { - data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0); - data &= ~DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK; - data |= ((en << DIDT_DB_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK); - cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0, data); - DIDTBlock_Info &= ~DB_Enable_MASK; - DIDTBlock_Info |= en << DB_Enable_SHIFT; - } + block_en = phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping) ? en : 0; - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping)) { - data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0); - data &= ~DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK; - data |= ((en << DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK); - cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0, data); - DIDTBlock_Info &= ~TD_Enable_MASK; - DIDTBlock_Info |= en << TD_Enable_SHIFT; - } + data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0); + data &= ~DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK; + data |= ((block_en << DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK); + cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0, data); + didt_block &= ~SQ_Enable_MASK; + didt_block |= block_en << SQ_Enable_SHIFT; + + block_en = phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping) ? en : 0; + + data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0); + data &= ~DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK; + data |= ((block_en << DIDT_DB_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK); + cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0, data); + didt_block &= ~DB_Enable_MASK; + didt_block |= block_en << DB_Enable_SHIFT; + + block_en = phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping) ? en : 0; + data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0); + data &= ~DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK; + data |= ((block_en << DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK); + cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0, data); + didt_block &= ~TD_Enable_MASK; + didt_block |= block_en << TD_Enable_SHIFT; + + block_en = phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping) ? en : 0; + + data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0); + data &= ~DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK; + data |= ((block_en << DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK); + cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0, data); + didt_block &= ~TCP_Enable_MASK; + didt_block |= block_en << TCP_Enable_SHIFT; - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) { - data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0); - data &= ~DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK; - data |= ((en << DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK); - cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0, data); - DIDTBlock_Info &= ~TCP_Enable_MASK; - DIDTBlock_Info |= en << TCP_Enable_SHIFT; - } if (enable) - result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_Didt_Block_Function, DIDTBlock_Info); + result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_Didt_Block_Function, didt_block); return result; } @@ -498,7 +750,6 @@ int smu7_enable_didt_config(struct pp_hwmgr *hwmgr) sys_info.info_id = CGS_SYSTEM_INFO_GFX_SE_INFO; result = cgs_query_system_info(hwmgr->device, &sys_info); - if (result == 0) num_se = sys_info.value; @@ -507,7 +758,7 @@ int smu7_enable_didt_config(struct pp_hwmgr *hwmgr) phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping) || phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) { - /* TO DO Pre DIDT disable clock gating */ + cgs_enter_safe_mode(hwmgr->device, true); value = 0; value2 = cgs_read_register(hwmgr->device, mmGRBM_GFX_INDEX); for (count = 0; count < num_se; count++) { @@ -521,10 +772,18 @@ int smu7_enable_didt_config(struct pp_hwmgr *hwmgr) PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result); result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris10); PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result); - } else if ((hwmgr->chip_id == CHIP_POLARIS11) || (hwmgr->chip_id == CHIP_POLARIS12)) { + } else if (hwmgr->chip_id == CHIP_POLARIS11) { + result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_Polaris11); + PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result); + if (hwmgr->smumgr->is_kicker) + result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris11_Kicker); + else + result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris11); + PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result); + } else if (hwmgr->chip_id == CHIP_POLARIS12) { result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_Polaris11); PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result); - result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris11); + result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris12); PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result); } } @@ -533,7 +792,13 @@ int smu7_enable_didt_config(struct pp_hwmgr *hwmgr) result = smu7_enable_didt(hwmgr, true); PP_ASSERT_WITH_CODE((result == 0), "EnableDiDt failed.", return result); - /* TO DO Post DIDT enable clock gating */ + if (hwmgr->chip_id == CHIP_POLARIS11) { + result = smum_send_msg_to_smc(hwmgr->smumgr, + (uint16_t)(PPSMC_MSG_EnableDpmDidt)); + PP_ASSERT_WITH_CODE((0 == result), + "Failed to enable DPM DIDT.", return result); + } + cgs_enter_safe_mode(hwmgr->device, false); } return 0; @@ -547,11 +812,20 @@ int smu7_disable_didt_config(struct pp_hwmgr *hwmgr) phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping) || phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping) || phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) { - /* TO DO Pre DIDT disable clock gating */ + + cgs_enter_safe_mode(hwmgr->device, true); result = smu7_enable_didt(hwmgr, false); - PP_ASSERT_WITH_CODE((result == 0), "Post DIDT enable clock gating failed.", return result); - /* TO DO Post DIDT enable clock gating */ + PP_ASSERT_WITH_CODE((result == 0), + "Post DIDT enable clock gating failed.", + return result); + if (hwmgr->chip_id == CHIP_POLARIS11) { + result = smum_send_msg_to_smc(hwmgr->smumgr, + (uint16_t)(PPSMC_MSG_DisableDpmDidt)); + PP_ASSERT_WITH_CODE((0 == result), + "Failed to disable DPM DIDT.", return result); + } + cgs_enter_safe_mode(hwmgr->device, false); } return 0; @@ -651,7 +925,7 @@ int smu7_enable_power_containment(struct pp_hwmgr *hwmgr) POWERCONTAINMENT_FEATURE_PkgPwrLimit; if (smu7_set_power_limit(hwmgr, default_limit)) - printk(KERN_ERR "Failed to set Default Power Limit in SMC!"); + pr_err("Failed to set Default Power Limit in SMC!"); } } } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c index 29d0319b22e6..436ca5ce8248 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c @@ -506,18 +506,18 @@ static int tf_smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr, static const struct phm_master_table_item phm_thermal_start_thermal_controller_master_list[] = { - {NULL, tf_smu7_thermal_initialize}, - {NULL, tf_smu7_thermal_set_temperature_range}, - {NULL, tf_smu7_thermal_enable_alert}, - {NULL, smum_thermal_avfs_enable}, + { .tableFunction = tf_smu7_thermal_initialize }, + { .tableFunction = tf_smu7_thermal_set_temperature_range }, + { .tableFunction = tf_smu7_thermal_enable_alert }, + { .tableFunction = smum_thermal_avfs_enable }, /* We should restrict performance levels to low before we halt the SMC. * On the other hand we are still in boot state when we do this * so it would be pointless. * If this assumption changes we have to revisit this table. */ - {NULL, smum_thermal_setup_fan_table}, - {NULL, tf_smu7_thermal_start_smc_fan_control}, - {NULL, NULL} + { .tableFunction = smum_thermal_setup_fan_table }, + { .tableFunction = tf_smu7_thermal_start_smc_fan_control }, + { } }; static const struct phm_master_table_header @@ -529,10 +529,10 @@ phm_thermal_start_thermal_controller_master = { static const struct phm_master_table_item phm_thermal_set_temperature_range_master_list[] = { - {NULL, tf_smu7_thermal_disable_alert}, - {NULL, tf_smu7_thermal_set_temperature_range}, - {NULL, tf_smu7_thermal_enable_alert}, - {NULL, NULL} + { .tableFunction = tf_smu7_thermal_disable_alert }, + { .tableFunction = tf_smu7_thermal_set_temperature_range }, + { .tableFunction = tf_smu7_thermal_enable_alert }, + { } }; static const struct phm_master_table_header @@ -575,3 +575,9 @@ int pp_smu7_thermal_initialize(struct pp_hwmgr *hwmgr) return result; } +void pp_smu7_thermal_fini(struct pp_hwmgr *hwmgr) +{ + phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range)); + phm_destroy_table(hwmgr, &(hwmgr->start_thermal_controller)); + return; +}
\ No newline at end of file diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.h index 6face973be43..2ed774db42c7 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.h @@ -47,6 +47,7 @@ extern int smu7_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode); extern int smu7_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t speed); extern int smu7_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr); extern int pp_smu7_thermal_initialize(struct pp_hwmgr *hwmgr); +extern void pp_smu7_thermal_fini(struct pp_hwmgr *hwmgr); extern int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr); extern int smu7_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed); extern int smu7_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed); diff --git a/drivers/gpu/drm/amd/powerplay/inc/amd_powerplay.h b/drivers/gpu/drm/amd/powerplay/inc/amd_powerplay.h index 3a883e6c601a..6dd5f0e9ef87 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/amd_powerplay.h +++ b/drivers/gpu/drm/amd/powerplay/inc/amd_powerplay.h @@ -29,7 +29,10 @@ #include "amd_shared.h" #include "cgs_common.h" -extern int amdgpu_dpm; +extern const struct amd_ip_funcs pp_ip_funcs; +extern const struct amd_powerplay_funcs pp_dpm_funcs; + +#define PP_DPM_DISABLED 0xCCCC enum amd_pp_sensors { AMDGPU_PP_SENSOR_GFX_SCLK = 0, @@ -135,17 +138,12 @@ enum amd_pp_event { AMD_PP_EVENT_MAX }; -enum amd_dpm_forced_level { - AMD_DPM_FORCED_LEVEL_AUTO = 0, - AMD_DPM_FORCED_LEVEL_LOW = 1, - AMD_DPM_FORCED_LEVEL_HIGH = 2, - AMD_DPM_FORCED_LEVEL_MANUAL = 3, -}; - struct amd_pp_init { struct cgs_device *device; uint32_t chip_family; uint32_t chip_id; + bool pm_en; + uint32_t feature_mask; }; enum amd_pp_display_config_type{ @@ -371,10 +369,10 @@ struct amd_powerplay { const struct amd_powerplay_funcs *pp_funcs; }; -int amd_powerplay_init(struct amd_pp_init *pp_init, - struct amd_powerplay *amd_pp); +int amd_powerplay_create(struct amd_pp_init *pp_init, + void **handle); -int amd_powerplay_fini(void *handle); +int amd_powerplay_destroy(void *handle); int amd_powerplay_reset(void *handle); diff --git a/drivers/gpu/drm/amd/powerplay/inc/eventmgr.h b/drivers/gpu/drm/amd/powerplay/inc/eventmgr.h index d63ef83b2628..7bd8a7e57080 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/eventmgr.h +++ b/drivers/gpu/drm/amd/powerplay/inc/eventmgr.h @@ -119,7 +119,6 @@ struct pp_eventmgr { void (*pp_eventmgr_fini)(struct pp_eventmgr *eventmgr); }; -int eventmgr_init(struct pp_instance *handle); -int eventmgr_fini(struct pp_eventmgr *eventmgr); +int eventmgr_early_init(struct pp_instance *handle); #endif /* _EVENTMGR_H_ */ diff --git a/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h b/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h index 26129972f686..80ed65985af8 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h +++ b/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h @@ -89,7 +89,7 @@ enum phm_platform_caps { PHM_PlatformCaps_EnableSideportControl, /* indicates Sideport can be controlled */ PHM_PlatformCaps_VideoPlaybackEEUNotification, /* indicates EEU notification of video start/stop is required */ PHM_PlatformCaps_TurnOffPll_ASPML1, /* PCIE Turn Off PLL in ASPM L1 */ - PHM_PlatformCaps_EnableHTLinkControl, /* indicates HT Link can be controlled by ACPI or CLMC overrided/automated mode. */ + PHM_PlatformCaps_EnableHTLinkControl, /* indicates HT Link can be controlled by ACPI or CLMC overridden/automated mode. */ PHM_PlatformCaps_PerformanceStateOnly, /* indicates only performance power state to be used on current system. */ PHM_PlatformCaps_ExclusiveModeAlwaysHigh, /* In Exclusive (3D) mode always stay in High state. */ PHM_PlatformCaps_DisableMGClockGating, /* to disable Medium Grain Clock Gating or not */ diff --git a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h index 6cdb7cbf515e..7275a29293eb 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h @@ -38,8 +38,6 @@ struct pp_hwmgr; struct phm_fan_speed_info; struct pp_atomctrl_voltage_table; -extern unsigned amdgpu_pp_feature_mask; - #define VOLTAGE_SCALE 4 uint8_t convert_to_vid(uint16_t vddc); @@ -358,6 +356,8 @@ struct pp_hwmgr_func { int (*get_mclk_od)(struct pp_hwmgr *hwmgr); int (*set_mclk_od)(struct pp_hwmgr *hwmgr, uint32_t value); int (*read_sensor)(struct pp_hwmgr *hwmgr, int idx, int32_t *value); + int (*request_firmware)(struct pp_hwmgr *hwmgr); + int (*release_firmware)(struct pp_hwmgr *hwmgr); }; struct pp_table_func { @@ -612,6 +612,7 @@ struct pp_hwmgr { uint32_t num_vce_state_tables; enum amd_dpm_forced_level dpm_level; + enum amd_dpm_forced_level saved_dpm_level; bool block_hw_access; struct phm_gfx_arbiter gfx_arbiter; struct phm_acp_arbiter acp_arbiter; @@ -651,19 +652,12 @@ struct pp_hwmgr { uint32_t feature_mask; }; - -extern int hwmgr_init(struct amd_pp_init *pp_init, - struct pp_instance *handle); - -extern int hwmgr_fini(struct pp_hwmgr *hwmgr); - -extern int hw_init_power_state_table(struct pp_hwmgr *hwmgr); - +extern int hwmgr_early_init(struct pp_instance *handle); +extern int hwmgr_hw_init(struct pp_instance *handle); +extern int hwmgr_hw_fini(struct pp_instance *handle); extern int phm_wait_on_register(struct pp_hwmgr *hwmgr, uint32_t index, uint32_t value, uint32_t mask); - - extern void phm_wait_on_indirect_register(struct pp_hwmgr *hwmgr, uint32_t indirect_port, uint32_t index, @@ -692,11 +686,10 @@ extern int phm_find_boot_level(void *table, uint32_t value, uint32_t *boot_level extern int phm_get_sclk_for_voltage_evv(struct pp_hwmgr *hwmgr, phm_ppt_v1_voltage_lookup_table *lookup_table, uint16_t virtual_voltage_id, int32_t *sclk); extern int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr); -extern int phm_hwmgr_backend_fini(struct pp_hwmgr *hwmgr); extern uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask); extern void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr); -extern int smu7_hwmgr_init(struct pp_hwmgr *hwmgr); +extern int smu7_init_function_pointers(struct pp_hwmgr *hwmgr); extern int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type, uint32_t sclk, uint16_t id, uint16_t *voltage); diff --git a/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h b/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h index bfdbec10cdd5..072880130cfb 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h +++ b/drivers/gpu/drm/amd/powerplay/inc/pp_debug.h @@ -24,6 +24,12 @@ #ifndef PP_DEBUG_H #define PP_DEBUG_H +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) "amdgpu: [powerplay] " fmt + #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -31,7 +37,7 @@ #define PP_ASSERT_WITH_CODE(cond, msg, code) \ do { \ if (!(cond)) { \ - printk("%s\n", msg); \ + pr_warning("%s\n", msg); \ code; \ } \ } while (0) @@ -39,7 +45,7 @@ #define PP_DBG_LOG(fmt, ...) \ do { \ - if(0)printk(KERN_INFO "[ pp_dbg ] " fmt, ##__VA_ARGS__); \ + pr_debug(fmt, ##__VA_ARGS__); \ } while (0) diff --git a/drivers/gpu/drm/amd/powerplay/inc/pp_instance.h b/drivers/gpu/drm/amd/powerplay/inc/pp_instance.h index 4d8ed1f33de4..ab8494fb5c6b 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/pp_instance.h +++ b/drivers/gpu/drm/amd/powerplay/inc/pp_instance.h @@ -31,6 +31,11 @@ struct pp_instance { uint32_t pp_valid; + uint32_t chip_family; + uint32_t chip_id; + bool pm_en; + uint32_t feature_mask; + void *device; struct pp_smumgr *smu_mgr; struct pp_hwmgr *hwmgr; struct pp_eventmgr *eventmgr; diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h b/drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h index bce00096d80d..fbc504c70b8b 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h +++ b/drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h @@ -394,6 +394,9 @@ typedef uint16_t PPSMC_Result; #define PPSMC_MSG_SetVBITimeout ((uint16_t) 0x306) +#define PPSMC_MSG_EnableDpmDidt ((uint16_t) 0x309) +#define PPSMC_MSG_DisableDpmDidt ((uint16_t) 0x30A) + #define PPSMC_MSG_SecureSRBMWrite ((uint16_t) 0x600) #define PPSMC_MSG_SecureSRBMRead ((uint16_t) 0x601) #define PPSMC_MSG_SetAddress ((uint16_t) 0x800) diff --git a/drivers/gpu/drm/amd/powerplay/inc/smumgr.h b/drivers/gpu/drm/amd/powerplay/inc/smumgr.h index 2139072065cc..7c318a95e0c2 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/smumgr.h +++ b/drivers/gpu/drm/amd/powerplay/inc/smumgr.h @@ -33,6 +33,12 @@ struct pp_hwmgr; #define smu_lower_32_bits(n) ((uint32_t)(n)) #define smu_upper_32_bits(n) ((uint32_t)(((n)>>16)>>16)) +extern const struct pp_smumgr_func cz_smu_funcs; +extern const struct pp_smumgr_func iceland_smu_funcs; +extern const struct pp_smumgr_func tonga_smu_funcs; +extern const struct pp_smumgr_func fiji_smu_funcs; +extern const struct pp_smumgr_func polaris10_smu_funcs; + enum AVFS_BTC_STATUS { AVFS_BTC_BOOT = 0, AVFS_BTC_BOOT_STARTEDSMU, @@ -131,13 +137,10 @@ struct pp_smumgr { uint32_t usec_timeout; bool reload_fw; const struct pp_smumgr_func *smumgr_funcs; + bool is_kicker; }; - -extern int smum_init(struct amd_pp_init *pp_init, - struct pp_instance *handle); - -extern int smum_fini(struct pp_smumgr *smumgr); +extern int smum_early_init(struct pp_instance *handle); extern int smum_get_argument(struct pp_smumgr *smumgr); @@ -172,13 +175,6 @@ extern int smu_allocate_memory(void *device, uint32_t size, void **kptr, void *handle); extern int smu_free_memory(void *device, void *handle); - -extern int cz_smum_init(struct pp_smumgr *smumgr); -extern int iceland_smum_init(struct pp_smumgr *smumgr); -extern int tonga_smum_init(struct pp_smumgr *smumgr); -extern int fiji_smum_init(struct pp_smumgr *smumgr); -extern int polaris10_smum_init(struct pp_smumgr *smumgr); - extern int smum_update_sclk_threshold(struct pp_hwmgr *hwmgr); extern int smum_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type); diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.c index 5a44485526d2..1f6744a443d4 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.c @@ -70,7 +70,7 @@ static int cz_send_msg_to_smc_async(struct pp_smumgr *smumgr, result = SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMU_MP1_SRBM2P_RESP_0, CONTENT, 0); if (result != 0) { - printk(KERN_ERR "[ powerplay ] cz_send_msg_to_smc_async failed\n"); + pr_err("cz_send_msg_to_smc_async failed\n"); return result; } @@ -100,12 +100,12 @@ static int cz_set_smc_sram_address(struct pp_smumgr *smumgr, return -EINVAL; if (0 != (3 & smc_address)) { - printk(KERN_ERR "[ powerplay ] SMC address must be 4 byte aligned\n"); + pr_err("SMC address must be 4 byte aligned\n"); return -EINVAL; } if (limit <= (smc_address + 3)) { - printk(KERN_ERR "[ powerplay ] SMC address beyond the SMC RAM area\n"); + pr_err("SMC address beyond the SMC RAM area\n"); return -EINVAL; } @@ -141,42 +141,6 @@ static int cz_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr, return cz_send_msg_to_smc(smumgr, msg); } -static int cz_request_smu_load_fw(struct pp_smumgr *smumgr) -{ - struct cz_smumgr *cz_smu = (struct cz_smumgr *)(smumgr->backend); - uint32_t smc_address; - - if (!smumgr->reload_fw) { - printk(KERN_INFO "[ powerplay ] skip reloading...\n"); - return 0; - } - - smc_address = SMU8_FIRMWARE_HEADER_LOCATION + - offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus); - - cz_write_smc_sram_dword(smumgr, smc_address, 0, smc_address+4); - - cz_send_msg_to_smc_with_parameter(smumgr, - PPSMC_MSG_DriverDramAddrHi, - cz_smu->toc_buffer.mc_addr_high); - - cz_send_msg_to_smc_with_parameter(smumgr, - PPSMC_MSG_DriverDramAddrLo, - cz_smu->toc_buffer.mc_addr_low); - - cz_send_msg_to_smc(smumgr, PPSMC_MSG_InitJobs); - - cz_send_msg_to_smc_with_parameter(smumgr, - PPSMC_MSG_ExecuteJob, - cz_smu->toc_entry_aram); - cz_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_ExecuteJob, - cz_smu->toc_entry_power_profiling_index); - - return cz_send_msg_to_smc_with_parameter(smumgr, - PPSMC_MSG_ExecuteJob, - cz_smu->toc_entry_initialize_index); -} - static int cz_check_fw_load_finish(struct pp_smumgr *smumgr, uint32_t firmware) { @@ -198,7 +162,7 @@ static int cz_check_fw_load_finish(struct pp_smumgr *smumgr, } if (i >= smumgr->usec_timeout) { - printk(KERN_ERR "[ powerplay ] SMU check loaded firmware failed.\n"); + pr_err("SMU check loaded firmware failed.\n"); return -EINVAL; } @@ -250,34 +214,6 @@ static int cz_load_mec_firmware(struct pp_smumgr *smumgr) return 0; } -static int cz_start_smu(struct pp_smumgr *smumgr) -{ - int ret = 0; - uint32_t fw_to_check = UCODE_ID_RLC_G_MASK | - UCODE_ID_SDMA0_MASK | - UCODE_ID_SDMA1_MASK | - UCODE_ID_CP_CE_MASK | - UCODE_ID_CP_ME_MASK | - UCODE_ID_CP_PFP_MASK | - UCODE_ID_CP_MEC_JT1_MASK | - UCODE_ID_CP_MEC_JT2_MASK; - - if (smumgr->chip_id == CHIP_STONEY) - fw_to_check &= ~(UCODE_ID_SDMA1_MASK | UCODE_ID_CP_MEC_JT2_MASK); - - ret = cz_request_smu_load_fw(smumgr); - if (ret) - printk(KERN_ERR "[ powerplay] SMU firmware load failed\n"); - - cz_check_fw_load_finish(smumgr, fw_to_check); - - ret = cz_load_mec_firmware(smumgr); - if (ret) - printk(KERN_ERR "[ powerplay ] Mec Firmware load failed\n"); - - return ret; -} - static uint8_t cz_translate_firmware_enum_to_arg(struct pp_smumgr *smumgr, enum cz_scratch_entry firmware_enum) { @@ -406,7 +342,7 @@ static int cz_smu_populate_single_scratch_task( break; if (i >= cz_smu->scratch_buffer_length) { - printk(KERN_ERR "[ powerplay ] Invalid Firmware Type\n"); + pr_err("Invalid Firmware Type\n"); return -EINVAL; } @@ -443,7 +379,7 @@ static int cz_smu_populate_single_ucode_load_task( break; if (i >= cz_smu->driver_buffer_length) { - printk(KERN_ERR "[ powerplay ] Invalid Firmware Type\n"); + pr_err("Invalid Firmware Type\n"); return -EINVAL; } @@ -729,11 +665,87 @@ static int cz_upload_pptable_settings(struct pp_smumgr *smumgr) return 0; } +static int cz_request_smu_load_fw(struct pp_smumgr *smumgr) +{ + struct cz_smumgr *cz_smu = (struct cz_smumgr *)(smumgr->backend); + uint32_t smc_address; + + if (!smumgr->reload_fw) { + pr_info("skip reloading...\n"); + return 0; + } + + cz_smu_populate_firmware_entries(smumgr); + + cz_smu_construct_toc(smumgr); + + smc_address = SMU8_FIRMWARE_HEADER_LOCATION + + offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus); + + cz_write_smc_sram_dword(smumgr, smc_address, 0, smc_address+4); + + cz_send_msg_to_smc_with_parameter(smumgr, + PPSMC_MSG_DriverDramAddrHi, + cz_smu->toc_buffer.mc_addr_high); + + cz_send_msg_to_smc_with_parameter(smumgr, + PPSMC_MSG_DriverDramAddrLo, + cz_smu->toc_buffer.mc_addr_low); + + cz_send_msg_to_smc(smumgr, PPSMC_MSG_InitJobs); + + cz_send_msg_to_smc_with_parameter(smumgr, + PPSMC_MSG_ExecuteJob, + cz_smu->toc_entry_aram); + cz_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_ExecuteJob, + cz_smu->toc_entry_power_profiling_index); + + return cz_send_msg_to_smc_with_parameter(smumgr, + PPSMC_MSG_ExecuteJob, + cz_smu->toc_entry_initialize_index); +} + +static int cz_start_smu(struct pp_smumgr *smumgr) +{ + int ret = 0; + uint32_t fw_to_check = 0; + + fw_to_check = UCODE_ID_RLC_G_MASK | + UCODE_ID_SDMA0_MASK | + UCODE_ID_SDMA1_MASK | + UCODE_ID_CP_CE_MASK | + UCODE_ID_CP_ME_MASK | + UCODE_ID_CP_PFP_MASK | + UCODE_ID_CP_MEC_JT1_MASK | + UCODE_ID_CP_MEC_JT2_MASK; + + if (smumgr->chip_id == CHIP_STONEY) + fw_to_check &= ~(UCODE_ID_SDMA1_MASK | UCODE_ID_CP_MEC_JT2_MASK); + + ret = cz_request_smu_load_fw(smumgr); + if (ret) + pr_err("SMU firmware load failed\n"); + + cz_check_fw_load_finish(smumgr, fw_to_check); + + ret = cz_load_mec_firmware(smumgr); + if (ret) + pr_err("Mec Firmware load failed\n"); + + return ret; +} + static int cz_smu_init(struct pp_smumgr *smumgr) { - struct cz_smumgr *cz_smu = (struct cz_smumgr *)smumgr->backend; uint64_t mc_addr = 0; int ret = 0; + struct cz_smumgr *cz_smu; + + cz_smu = kzalloc(sizeof(struct cz_smumgr), GFP_KERNEL); + if (cz_smu == NULL) + return -ENOMEM; + + smumgr->backend = cz_smu; cz_smu->toc_buffer.data_size = 4096; cz_smu->smu_buffer.data_size = @@ -769,12 +781,11 @@ static int cz_smu_init(struct pp_smumgr *smumgr) cz_smu->smu_buffer.mc_addr_high = smu_upper_32_bits(mc_addr); cz_smu->smu_buffer.mc_addr_low = smu_lower_32_bits(mc_addr); - cz_smu_populate_firmware_entries(smumgr); if (0 != cz_smu_populate_single_scratch_entry(smumgr, CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH, UCODE_ID_RLC_SCRATCH_SIZE_BYTE, &cz_smu->scratch_buffer[cz_smu->scratch_buffer_length++])) { - printk(KERN_ERR "[ powerplay ] Error when Populate Firmware Entry.\n"); + pr_err("Error when Populate Firmware Entry.\n"); return -1; } @@ -782,14 +793,14 @@ static int cz_smu_init(struct pp_smumgr *smumgr) CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM, UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE, &cz_smu->scratch_buffer[cz_smu->scratch_buffer_length++])) { - printk(KERN_ERR "[ powerplay ] Error when Populate Firmware Entry.\n"); + pr_err("Error when Populate Firmware Entry.\n"); return -1; } if (0 != cz_smu_populate_single_scratch_entry(smumgr, CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM, UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE, &cz_smu->scratch_buffer[cz_smu->scratch_buffer_length++])) { - printk(KERN_ERR "[ powerplay ] Error when Populate Firmware Entry.\n"); + pr_err("Error when Populate Firmware Entry.\n"); return -1; } @@ -797,7 +808,7 @@ static int cz_smu_init(struct pp_smumgr *smumgr) CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING, sizeof(struct SMU8_MultimediaPowerLogData), &cz_smu->scratch_buffer[cz_smu->scratch_buffer_length++])) { - printk(KERN_ERR "[ powerplay ] Error when Populate Firmware Entry.\n"); + pr_err("Error when Populate Firmware Entry.\n"); return -1; } @@ -805,10 +816,9 @@ static int cz_smu_init(struct pp_smumgr *smumgr) CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE, sizeof(struct SMU8_Fusion_ClkTable), &cz_smu->scratch_buffer[cz_smu->scratch_buffer_length++])) { - printk(KERN_ERR "[ powerplay ] Error when Populate Firmware Entry.\n"); + pr_err("Error when Populate Firmware Entry.\n"); return -1; } - cz_smu_construct_toc(smumgr); return 0; } @@ -827,13 +837,12 @@ static int cz_smu_fini(struct pp_smumgr *smumgr) cgs_free_gpu_mem(smumgr->device, cz_smu->smu_buffer.handle); kfree(cz_smu); - kfree(smumgr); } return 0; } -static const struct pp_smumgr_func cz_smu_funcs = { +const struct pp_smumgr_func cz_smu_funcs = { .smu_init = cz_smu_init, .smu_fini = cz_smu_fini, .start_smu = cz_start_smu, @@ -847,15 +856,3 @@ static const struct pp_smumgr_func cz_smu_funcs = { .upload_pptable_settings = cz_upload_pptable_settings, }; -int cz_smum_init(struct pp_smumgr *smumgr) -{ - struct cz_smumgr *cz_smu; - - cz_smu = kzalloc(sizeof(struct cz_smumgr), GFP_KERNEL); - if (cz_smu == NULL) - return -ENOMEM; - - smumgr->backend = cz_smu; - smumgr->smumgr_funcs = &cz_smu_funcs; - return 0; -} diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.h b/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.h index 883818039248..7c3a290c8957 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.h +++ b/drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.h @@ -95,8 +95,4 @@ struct cz_smumgr { struct cz_buffer_entry scratch_buffer[MAX_NUM_SCRATCH]; }; -struct pp_smumgr; - -extern int cz_smum_init(struct pp_smumgr *smumgr); - #endif diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c index 6aeb1d20cc3b..0f7a77b7312e 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c @@ -21,13 +21,13 @@ * */ +#include "pp_debug.h" #include "fiji_smc.h" #include "smu7_dyn_defaults.h" #include "smu7_hwmgr.h" #include "hardwaremanager.h" #include "ppatomctrl.h" -#include "pp_debug.h" #include "cgs_common.h" #include "atombios.h" #include "fiji_smumgr.h" @@ -2131,7 +2131,7 @@ uint32_t fiji_get_offsetof(uint32_t type, uint32_t member) return offsetof(SMU73_Discrete_DpmTable, LowSclkInterruptThreshold); } } - printk(KERN_WARNING "can't get the offset of type %x member %x\n", type, member); + pr_warning("can't get the offset of type %x member %x\n", type, member); return 0; } @@ -2156,7 +2156,7 @@ uint32_t fiji_get_mac_definition(uint32_t value) return SMU73_MAX_LEVELS_MVDD; } - printk(KERN_WARNING "can't get the mac of %x\n", value); + pr_warning("can't get the mac of %x\n", value); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c index 26eff56b4a99..54b347366b5d 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c @@ -21,6 +21,7 @@ * */ +#include "pp_debug.h" #include "smumgr.h" #include "smu73.h" #include "smu_ucode_xfer_vi.h" @@ -36,7 +37,6 @@ #include "gca/gfx_8_0_d.h" #include "bif/bif_5_0_d.h" #include "bif/bif_5_0_sh_mask.h" -#include "pp_debug.h" #include "fiji_pwrvirus.h" #include "fiji_smc.h" @@ -179,7 +179,7 @@ static int fiji_setup_pwr_virus(struct pp_smumgr *smumgr) result = 0; break; default: - printk(KERN_ERR "Table Exit with Invalid Command!"); + pr_err("Table Exit with Invalid Command!"); priv->avfs.AvfsBtcStatus = AVFS_BTC_VIRUS_FAIL; result = -1; break; @@ -202,13 +202,13 @@ static int fiji_start_avfs_btc(struct pp_smumgr *smumgr) priv->avfs.AvfsBtcStatus = AVFS_BTC_COMPLETED_UNSAVED; result = 0; } else { - printk(KERN_ERR "[AVFS][fiji_start_avfs_btc] Attempt" + pr_err("[AVFS][fiji_start_avfs_btc] Attempt" " to Enable AVFS Failed!"); smum_send_msg_to_smc(smumgr, PPSMC_MSG_DisableAvfs); result = -1; } } else { - printk(KERN_ERR "[AVFS][fiji_start_avfs_btc] " + pr_err("[AVFS][fiji_start_avfs_btc] " "PerformBTC SMU msg failed"); result = -1; } @@ -384,7 +384,7 @@ static int fiji_avfs_event_mgr(struct pp_smumgr *smumgr, bool smu_started) case AVFS_BTC_NOTSUPPORTED: /* Do nothing */ break; default: - printk(KERN_ERR "[AVFS] Something is broken. See log!"); + pr_err("[AVFS] Something is broken. See log!"); break; } return 0; @@ -464,13 +464,20 @@ static bool fiji_is_hw_avfs_present(struct pp_smumgr *smumgr) */ static int fiji_smu_init(struct pp_smumgr *smumgr) { - struct fiji_smumgr *priv = (struct fiji_smumgr *)(smumgr->backend); int i; + struct fiji_smumgr *fiji_priv = NULL; + + fiji_priv = kzalloc(sizeof(struct fiji_smumgr), GFP_KERNEL); + + if (fiji_priv == NULL) + return -ENOMEM; + + smumgr->backend = fiji_priv; if (smu7_init(smumgr)) return -EINVAL; - priv->avfs.AvfsBtcStatus = AVFS_BTC_BOOT; + fiji_priv->avfs.AvfsBtcStatus = AVFS_BTC_BOOT; if (fiji_is_hw_avfs_present(smumgr)) /* AVFS Parameter * 0 - BTC DC disabled, BTC AC disabled @@ -479,18 +486,18 @@ static int fiji_smu_init(struct pp_smumgr *smumgr) * 3 - BTC DC enabled, BTC AC enabled * Default is 0 - BTC DC disabled, BTC AC disabled */ - priv->avfs.AvfsBtcParam = 0; + fiji_priv->avfs.AvfsBtcParam = 0; else - priv->avfs.AvfsBtcStatus = AVFS_BTC_NOTSUPPORTED; + fiji_priv->avfs.AvfsBtcStatus = AVFS_BTC_NOTSUPPORTED; for (i = 0; i < SMU73_MAX_LEVELS_GRAPHICS; i++) - priv->activity_target[i] = 30; + fiji_priv->activity_target[i] = 30; return 0; } -static const struct pp_smumgr_func fiji_smu_funcs = { +const struct pp_smumgr_func fiji_smu_funcs = { .smu_init = &fiji_smu_init, .smu_fini = &smu7_smu_fini, .start_smu = &fiji_start_smu, @@ -513,18 +520,3 @@ static const struct pp_smumgr_func fiji_smu_funcs = { .initialize_mc_reg_table = fiji_initialize_mc_reg_table, .is_dpm_running = fiji_is_dpm_running, }; - -int fiji_smum_init(struct pp_smumgr *smumgr) -{ - struct fiji_smumgr *fiji_smu = NULL; - - fiji_smu = kzalloc(sizeof(struct fiji_smumgr), GFP_KERNEL); - - if (fiji_smu == NULL) - return -ENOMEM; - - smumgr->backend = fiji_smu; - smumgr->smumgr_funcs = &fiji_smu_funcs; - - return 0; -} diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c index a24971a33bfd..ad82161df831 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c @@ -21,13 +21,13 @@ * */ +#include "pp_debug.h" #include "iceland_smc.h" #include "smu7_dyn_defaults.h" #include "smu7_hwmgr.h" #include "hardwaremanager.h" #include "ppatomctrl.h" -#include "pp_debug.h" #include "cgs_common.h" #include "atombios.h" #include "pppcielanes.h" @@ -1545,7 +1545,7 @@ static int iceland_populate_smc_boot_level(struct pp_hwmgr *hwmgr, if (0 != result) { smu_data->smc_state_table.GraphicsBootLevel = 0; - printk(KERN_ERR "[ powerplay ] VBIOS did not find boot engine clock value \ + pr_err("VBIOS did not find boot engine clock value \ in dependency table. Using Graphics DPM level 0!"); result = 0; } @@ -1556,7 +1556,7 @@ static int iceland_populate_smc_boot_level(struct pp_hwmgr *hwmgr, if (0 != result) { smu_data->smc_state_table.MemoryBootLevel = 0; - printk(KERN_ERR "[ powerplay ] VBIOS did not find boot engine clock value \ + pr_err("VBIOS did not find boot engine clock value \ in dependency table. Using Memory DPM level 0!"); result = 0; } @@ -2146,7 +2146,7 @@ uint32_t iceland_get_offsetof(uint32_t type, uint32_t member) return offsetof(SMU71_Discrete_DpmTable, LowSclkInterruptThreshold); } } - printk(KERN_WARNING "can't get the offset of type %x member %x\n", type, member); + pr_warning("can't get the offset of type %x member %x\n", type, member); return 0; } @@ -2169,7 +2169,7 @@ uint32_t iceland_get_mac_definition(uint32_t value) return SMU71_MAX_LEVELS_MVDD; } - printk(KERN_WARNING "can't get the mac of %x\n", value); + pr_warning("can't get the mac of %x\n", value); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c index eeafefc4acba..0bf2def3b659 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c @@ -22,6 +22,7 @@ * Author: Huang Rui <ray.huang@amd.com> * */ +#include "pp_debug.h" #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -29,7 +30,6 @@ #include "smumgr.h" #include "iceland_smumgr.h" -#include "pp_debug.h" #include "smu_ucode_xfer_vi.h" #include "ppsmc.h" #include "smu/smu_7_1_1_d.h" @@ -176,7 +176,7 @@ static int iceland_start_smu(struct pp_smumgr *smumgr) return result; if (!smu7_is_smc_ram_running(smumgr)) { - printk("smu not running, upload firmware again \n"); + pr_info("smu not running, upload firmware again \n"); result = iceland_smu_upload_firmware_image(smumgr); if (result) return result; @@ -201,17 +201,25 @@ static int iceland_start_smu(struct pp_smumgr *smumgr) static int iceland_smu_init(struct pp_smumgr *smumgr) { int i; - struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(smumgr->backend); + struct iceland_smumgr *iceland_priv = NULL; + + iceland_priv = kzalloc(sizeof(struct iceland_smumgr), GFP_KERNEL); + + if (iceland_priv == NULL) + return -ENOMEM; + + smumgr->backend = iceland_priv; + if (smu7_init(smumgr)) return -EINVAL; for (i = 0; i < SMU71_MAX_LEVELS_GRAPHICS; i++) - smu_data->activity_target[i] = 30; + iceland_priv->activity_target[i] = 30; return 0; } -static const struct pp_smumgr_func iceland_smu_funcs = { +const struct pp_smumgr_func iceland_smu_funcs = { .smu_init = &iceland_smu_init, .smu_fini = &smu7_smu_fini, .start_smu = &iceland_start_smu, @@ -234,17 +242,3 @@ static const struct pp_smumgr_func iceland_smu_funcs = { .is_dpm_running = iceland_is_dpm_running, }; -int iceland_smum_init(struct pp_smumgr *smumgr) -{ - struct iceland_smumgr *iceland_smu = NULL; - - iceland_smu = kzalloc(sizeof(struct iceland_smumgr), GFP_KERNEL); - - if (iceland_smu == NULL) - return -ENOMEM; - - smumgr->backend = iceland_smu; - smumgr->smumgr_funcs = &iceland_smu_funcs; - - return 0; -} diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c index 5190e821200c..80e2329a1b9e 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c @@ -21,13 +21,13 @@ * */ +#include "pp_debug.h" #include "polaris10_smc.h" #include "smu7_dyn_defaults.h" #include "smu7_hwmgr.h" #include "hardwaremanager.h" #include "ppatomctrl.h" -#include "pp_debug.h" #include "cgs_common.h" #include "atombios.h" #include "polaris10_smumgr.h" @@ -494,6 +494,7 @@ static int polaris10_populate_ulv_level(struct pp_hwmgr *hwmgr, struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend); struct phm_ppt_v1_information *table_info = (struct phm_ppt_v1_information *)(hwmgr->pptable); + struct pp_smumgr *smumgr = hwmgr->smumgr; state->CcPwrDynRm = 0; state->CcPwrDynRm1 = 0; @@ -502,7 +503,10 @@ static int polaris10_populate_ulv_level(struct pp_hwmgr *hwmgr, state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset * VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1); - state->VddcPhase = (data->vddc_phase_shed_control) ? 0 : 1; + if (smumgr->chip_id == CHIP_POLARIS12 || smumgr->is_kicker) + state->VddcPhase = data->vddc_phase_shed_control ^ 0x3; + else + state->VddcPhase = (data->vddc_phase_shed_control) ? 0 : 1; CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm); CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1); @@ -2180,7 +2184,7 @@ uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member) return offsetof(SMU74_Discrete_DpmTable, LowSclkInterruptThreshold); } } - printk(KERN_WARNING "can't get the offset of type %x member %x\n", type, member); + pr_warning("can't get the offset of type %x member %x\n", type, member); return 0; } @@ -2207,7 +2211,7 @@ uint32_t polaris10_get_mac_definition(uint32_t value) return SMU7_UVD_MCLK_HANDSHAKE_DISABLE; } - printk(KERN_WARNING "can't get the mac of %x\n", value); + pr_warning("can't get the mac of %x\n", value); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c index f38a68747df0..ce20ae2e520e 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c @@ -21,6 +21,7 @@ * */ +#include "pp_debug.h" #include "smumgr.h" #include "smu74.h" #include "smu_ucode_xfer_vi.h" @@ -36,7 +37,6 @@ #include "bif/bif_5_0_sh_mask.h" #include "polaris10_pwrvirus.h" #include "ppatomctrl.h" -#include "pp_debug.h" #include "cgs_common.h" #include "polaris10_smc.h" #include "smu7_ppsmc.h" @@ -84,7 +84,7 @@ static int polaris10_setup_pwr_virus(struct pp_smumgr *smumgr) break; default: - printk("Table Exit with Invalid Command!"); + pr_info("Table Exit with Invalid Command!"); smu_data->avfs.avfs_btc_status = AVFS_BTC_VIRUS_FAIL; result = -1; break; @@ -102,7 +102,7 @@ static int polaris10_perform_btc(struct pp_smumgr *smumgr) if (0 != smu_data->avfs.avfs_btc_param) { if (0 != smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_PerformBtc, smu_data->avfs.avfs_btc_param)) { - printk("[AVFS][SmuPolaris10_PerformBtc] PerformBTC SMU msg failed"); + pr_info("[AVFS][SmuPolaris10_PerformBtc] PerformBTC SMU msg failed"); result = -1; } } @@ -189,7 +189,7 @@ polaris10_avfs_event_mgr(struct pp_smumgr *smumgr, bool SMU_VFT_INTACT) return -1); if (smu_data->avfs.avfs_btc_param > 1) { - printk("[AVFS][Polaris10_AVFSEventMgr] AC BTC has not been successfully verified on Fiji. There may be in this setting."); + pr_info("[AVFS][Polaris10_AVFSEventMgr] AC BTC has not been successfully verified on Fiji. There may be in this setting."); smu_data->avfs.avfs_btc_status = AVFS_BTC_VIRUS_FAIL; PP_ASSERT_WITH_CODE(-1 == polaris10_setup_pwr_virus(smumgr), "[AVFS][Polaris10_AVFSEventMgr] Could not setup Pwr Virus for AVFS ", @@ -208,7 +208,7 @@ polaris10_avfs_event_mgr(struct pp_smumgr *smumgr, bool SMU_VFT_INTACT) break; default: - printk("[AVFS] Something is broken. See log!"); + pr_info("[AVFS] Something is broken. See log!"); break; } @@ -328,6 +328,7 @@ static int polaris10_start_smu(struct pp_smumgr *smumgr) /* If failed, try with different security Key. */ if (result != 0) { smu_data->smu7_data.security_hard_key ^= 1; + cgs_rel_firmware(smumgr->device, CGS_UCODE_ID_SMU); result = polaris10_start_smu_in_protection_mode(smumgr); } } @@ -363,9 +364,15 @@ static bool polaris10_is_hw_avfs_present(struct pp_smumgr *smumgr) static int polaris10_smu_init(struct pp_smumgr *smumgr) { - struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend); + struct polaris10_smumgr *smu_data; int i; + smu_data = kzalloc(sizeof(struct polaris10_smumgr), GFP_KERNEL); + if (smu_data == NULL) + return -ENOMEM; + + smumgr->backend = smu_data; + if (smu7_init(smumgr)) return -EINVAL; @@ -380,7 +387,7 @@ static int polaris10_smu_init(struct pp_smumgr *smumgr) return 0; } -static const struct pp_smumgr_func polaris10_smu_funcs = { +const struct pp_smumgr_func polaris10_smu_funcs = { .smu_init = polaris10_smu_init, .smu_fini = smu7_smu_fini, .start_smu = polaris10_start_smu, @@ -403,18 +410,3 @@ static const struct pp_smumgr_func polaris10_smu_funcs = { .get_mac_definition = polaris10_get_mac_definition, .is_dpm_running = polaris10_is_dpm_running, }; - -int polaris10_smum_init(struct pp_smumgr *smumgr) -{ - struct polaris10_smumgr *polaris10_smu = NULL; - - polaris10_smu = kzalloc(sizeof(struct polaris10_smumgr), GFP_KERNEL); - - if (polaris10_smu == NULL) - return -EINVAL; - - smumgr->backend = polaris10_smu; - smumgr->smumgr_funcs = &polaris10_smu_funcs; - - return 0; -} diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c index f49b5487b951..35ac27681415 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c @@ -22,12 +22,12 @@ */ +#include "pp_debug.h" #include "smumgr.h" #include "smu_ucode_xfer_vi.h" #include "smu/smu_7_1_3_d.h" #include "smu/smu_7_1_3_sh_mask.h" #include "ppatomctrl.h" -#include "pp_debug.h" #include "cgs_common.h" #include "smu7_ppsmc.h" #include "smu7_smumgr.h" @@ -175,7 +175,7 @@ int smu7_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg) ret = SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP); if (ret != 1) - printk("\n failed to send pre message %x ret is %d \n", msg, ret); + pr_info("\n failed to send pre message %x ret is %d \n", msg, ret); cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg); @@ -184,7 +184,7 @@ int smu7_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg) ret = SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP); if (ret != 1) - printk("\n failed to send message %x ret is %d \n", msg, ret); + pr_info("\n failed to send message %x ret is %d \n", msg, ret); return 0; } @@ -225,7 +225,7 @@ int smu7_send_msg_to_smc_offset(struct pp_smumgr *smumgr) SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0); if (1 != SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP)) - printk("Failed to send Message.\n"); + pr_info("Failed to send Message.\n"); return 0; } @@ -347,7 +347,7 @@ static uint32_t smu7_get_mask_for_firmware_type(uint32_t fw_type) result = UCODE_ID_RLC_G_MASK; break; default: - printk("UCode type is out of range! \n"); + pr_info("UCode type is out of range! \n"); result = 0; } @@ -396,7 +396,7 @@ int smu7_request_smu_load_fw(struct pp_smumgr *smumgr) struct SMU_DRAMData_TOC *toc; if (!smumgr->reload_fw) { - printk(KERN_INFO "[ powerplay ] skip reloading...\n"); + pr_info("skip reloading...\n"); return 0; } @@ -474,7 +474,7 @@ int smu7_request_smu_load_fw(struct pp_smumgr *smumgr) smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_DRV_DRAM_ADDR_LO, smu_data->header_buffer.mc_addr_low); if (smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_LoadUcodes, fw_to_load)) - printk(KERN_ERR "Fail to Request SMU Load uCode"); + pr_err("Fail to Request SMU Load uCode"); return result; } @@ -533,6 +533,8 @@ int smu7_upload_smu_firmware_image(struct pp_smumgr *smumgr) cgs_get_firmware_info(smumgr->device, smu7_convert_fw_type_to_cgs(UCODE_ID_SMU_SK), &info); + smumgr->is_kicker = info.is_kicker; + result = smu7_upload_smc_firmware_data(smumgr, info.image_size, (uint32_t *)info.kptr, SMU7_SMC_SIZE); return result; diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smumgr.c index 6e618aa20719..c0956a4207a9 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/smumgr.c @@ -22,6 +22,7 @@ */ #include <linux/types.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/slab.h> #include <drm/amdgpu_drm.h> #include "pp_instance.h" @@ -29,44 +30,57 @@ #include "cgs_common.h" #include "linux/delay.h" - -int smum_init(struct amd_pp_init *pp_init, struct pp_instance *handle) +MODULE_FIRMWARE("amdgpu/topaz_smc.bin"); +MODULE_FIRMWARE("amdgpu/topaz_k_smc.bin"); +MODULE_FIRMWARE("amdgpu/tonga_smc.bin"); +MODULE_FIRMWARE("amdgpu/tonga_k_smc.bin"); +MODULE_FIRMWARE("amdgpu/fiji_smc.bin"); +MODULE_FIRMWARE("amdgpu/polaris10_smc.bin"); +MODULE_FIRMWARE("amdgpu/polaris10_smc_sk.bin"); +MODULE_FIRMWARE("amdgpu/polaris10_k_smc.bin"); +MODULE_FIRMWARE("amdgpu/polaris11_smc.bin"); +MODULE_FIRMWARE("amdgpu/polaris11_smc_sk.bin"); +MODULE_FIRMWARE("amdgpu/polaris11_k_smc.bin"); +MODULE_FIRMWARE("amdgpu/polaris12_smc.bin"); + + +int smum_early_init(struct pp_instance *handle) { struct pp_smumgr *smumgr; - if ((handle == NULL) || (pp_init == NULL)) + if (handle == NULL) return -EINVAL; smumgr = kzalloc(sizeof(struct pp_smumgr), GFP_KERNEL); if (smumgr == NULL) return -ENOMEM; - smumgr->device = pp_init->device; - smumgr->chip_family = pp_init->chip_family; - smumgr->chip_id = pp_init->chip_id; + smumgr->device = handle->device; + smumgr->chip_family = handle->chip_family; + smumgr->chip_id = handle->chip_id; smumgr->usec_timeout = AMD_MAX_USEC_TIMEOUT; smumgr->reload_fw = 1; handle->smu_mgr = smumgr; switch (smumgr->chip_family) { case AMDGPU_FAMILY_CZ: - cz_smum_init(smumgr); + smumgr->smumgr_funcs = &cz_smu_funcs; break; case AMDGPU_FAMILY_VI: switch (smumgr->chip_id) { case CHIP_TOPAZ: - iceland_smum_init(smumgr); + smumgr->smumgr_funcs = &iceland_smu_funcs; break; case CHIP_TONGA: - tonga_smum_init(smumgr); + smumgr->smumgr_funcs = &tonga_smu_funcs; break; case CHIP_FIJI: - fiji_smum_init(smumgr); + smumgr->smumgr_funcs = &fiji_smu_funcs; break; case CHIP_POLARIS11: case CHIP_POLARIS10: case CHIP_POLARIS12: - polaris10_smum_init(smumgr); + smumgr->smumgr_funcs = &polaris10_smu_funcs; break; default: return -EINVAL; @@ -80,13 +94,6 @@ int smum_init(struct amd_pp_init *pp_init, struct pp_instance *handle) return 0; } -int smum_fini(struct pp_smumgr *smumgr) -{ - kfree(smumgr->device); - kfree(smumgr); - return 0; -} - int smum_thermal_avfs_enable(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result) { diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c index 2e1493ce1bb5..331b0aba4a13 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c @@ -21,13 +21,13 @@ * */ +#include "pp_debug.h" #include "tonga_smc.h" #include "smu7_dyn_defaults.h" #include "smu7_hwmgr.h" #include "hardwaremanager.h" #include "ppatomctrl.h" -#include "pp_debug.h" #include "cgs_common.h" #include "atombios.h" #include "tonga_smumgr.h" @@ -656,7 +656,7 @@ int tonga_populate_all_graphic_levels(struct pp_hwmgr *hwmgr) } } else { if (0 == data->dpm_level_enable_mask.pcie_dpm_enable_mask) - printk(KERN_ERR "[ powerplay ] Pcie Dpm Enablemask is 0 !"); + pr_err("Pcie Dpm Enablemask is 0 !"); while (data->dpm_level_enable_mask.pcie_dpm_enable_mask && ((data->dpm_level_enable_mask.pcie_dpm_enable_mask & @@ -1503,7 +1503,7 @@ static int tonga_populate_smc_boot_level(struct pp_hwmgr *hwmgr, if (result != 0) { smu_data->smc_state_table.GraphicsBootLevel = 0; - printk(KERN_ERR "[powerplay] VBIOS did not find boot engine " + pr_err("[powerplay] VBIOS did not find boot engine " "clock value in dependency table. " "Using Graphics DPM level 0 !"); result = 0; @@ -1515,7 +1515,7 @@ static int tonga_populate_smc_boot_level(struct pp_hwmgr *hwmgr, if (result != 0) { smu_data->smc_state_table.MemoryBootLevel = 0; - printk(KERN_ERR "[powerplay] VBIOS did not find boot " + pr_err("[powerplay] VBIOS did not find boot " "engine clock value in dependency table." "Using Memory DPM level 0 !"); result = 0; @@ -1739,7 +1739,7 @@ static int tonga_populate_vr_config(struct pp_hwmgr *hwmgr, config = VR_SVI2_PLANE_2; table->VRConfig |= config; } else { - printk(KERN_ERR "[ powerplay ] VDDC and VDDGFX should " + pr_err("VDDC and VDDGFX should " "be both on SVI2 control in splitted mode !\n"); } } else { @@ -1752,7 +1752,7 @@ static int tonga_populate_vr_config(struct pp_hwmgr *hwmgr, config = VR_SVI2_PLANE_1; table->VRConfig |= config; } else { - printk(KERN_ERR "[ powerplay ] VDDC should be on " + pr_err("VDDC should be on " "SVI2 control in merged mode !\n"); } } @@ -2657,7 +2657,7 @@ uint32_t tonga_get_offsetof(uint32_t type, uint32_t member) return offsetof(SMU72_Discrete_DpmTable, LowSclkInterruptThreshold); } } - printk(KERN_WARNING "can't get the offset of type %x member %x\n", type, member); + pr_warning("can't get the offset of type %x member %x\n", type, member); return 0; } @@ -2681,7 +2681,7 @@ uint32_t tonga_get_mac_definition(uint32_t value) case SMU_MAX_LEVELS_MVDD: return SMU72_MAX_LEVELS_MVDD; } - printk(KERN_WARNING "can't get the mac value %x\n", value); + pr_warning("can't get the mac value %x\n", value); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c index eff9a232e72e..a7d55366f2d2 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c @@ -20,6 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. * */ +#include "pp_debug.h" #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -27,7 +28,6 @@ #include "smumgr.h" #include "tonga_smumgr.h" -#include "pp_debug.h" #include "smu_ucode_xfer_vi.h" #include "tonga_ppsmc.h" #include "smu/smu_7_1_2_d.h" @@ -84,7 +84,7 @@ static int tonga_start_in_protection_mode(struct pp_smumgr *smumgr) /* Check pass/failed indicator */ if (1 != SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC, SMU_STATUS, SMU_PASS)) { - printk(KERN_ERR "[ powerplay ] SMU Firmware start failed\n"); + pr_err("SMU Firmware start failed\n"); return -EINVAL; } @@ -169,20 +169,25 @@ static int tonga_start_smu(struct pp_smumgr *smumgr) */ static int tonga_smu_init(struct pp_smumgr *smumgr) { - struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(smumgr->backend); + struct tonga_smumgr *tonga_priv = NULL; + int i; + + tonga_priv = kzalloc(sizeof(struct tonga_smumgr), GFP_KERNEL); + if (tonga_priv == NULL) + return -ENOMEM; - int i; + smumgr->backend = tonga_priv; if (smu7_init(smumgr)) return -EINVAL; for (i = 0; i < SMU72_MAX_LEVELS_GRAPHICS; i++) - smu_data->activity_target[i] = 30; + tonga_priv->activity_target[i] = 30; return 0; } -static const struct pp_smumgr_func tonga_smu_funcs = { +const struct pp_smumgr_func tonga_smu_funcs = { .smu_init = &tonga_smu_init, .smu_fini = &smu7_smu_fini, .start_smu = &tonga_start_smu, @@ -205,18 +210,3 @@ static const struct pp_smumgr_func tonga_smu_funcs = { .initialize_mc_reg_table = tonga_initialize_mc_reg_table, .is_dpm_running = tonga_is_dpm_running, }; - -int tonga_smum_init(struct pp_smumgr *smumgr) -{ - struct tonga_smumgr *tonga_smu = NULL; - - tonga_smu = kzalloc(sizeof(struct tonga_smumgr), GFP_KERNEL); - - if (tonga_smu == NULL) - return -ENOMEM; - - smumgr->backend = tonga_smu; - smumgr->smumgr_funcs = &tonga_smu_funcs; - - return 0; -} diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c index 1bf83ed113b3..16f96563cd2b 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c @@ -24,6 +24,7 @@ #include <linux/kthread.h> #include <linux/wait.h> #include <linux/sched.h> +#include <uapi/linux/sched/types.h> #include <drm/drmP.h> #include "gpu_scheduler.h" diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index 7130b044b004..ad9a95916f1f 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -35,7 +35,8 @@ static struct simplefb_format supported_formats[] = { static void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc) { struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); - uint32_t pixel_format = crtc->primary->state->fb->pixel_format; + const struct drm_framebuffer *fb = crtc->primary->state->fb; + uint32_t pixel_format = fb->format->format; struct simplefb_format *format = NULL; int i; diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index 0b6eaa49a1db..8d8344ed655e 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -135,8 +135,7 @@ static int arcpgu_load(struct drm_device *drm) drm_kms_helper_poll_init(drm); arcpgu->fbdev = drm_fbdev_cma_init(drm, 16, - drm->mode_config.num_crtc, - drm->mode_config.num_connector); + drm->mode_config.num_connector); if (IS_ERR(arcpgu->fbdev)) { ret = PTR_ERR(arcpgu->fbdev); arcpgu->fbdev = NULL; diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c index b69c66b4897e..0ce7f398bcff 100644 --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c @@ -47,10 +47,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np) return ret; /* Link drm_bridge to encoder */ - bridge->encoder = encoder; - encoder->bridge = bridge; - - ret = drm_bridge_attach(drm, bridge); + ret = drm_bridge_attach(encoder, bridge, NULL); if (ret) drm_encoder_cleanup(encoder); diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index 7d4e5aa77195..20ebfb4fbdfa 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -60,11 +60,12 @@ static int hdlcd_set_pxl_fmt(struct drm_crtc *crtc) { unsigned int btpp; struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc); + const struct drm_framebuffer *fb = crtc->primary->state->fb; uint32_t pixel_format; struct simplefb_format *format = NULL; int i; - pixel_format = crtc->primary->state->fb->pixel_format; + pixel_format = fb->format->format; for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { if (supported_formats[i].fourcc == pixel_format) @@ -220,27 +221,28 @@ static int hdlcd_plane_atomic_check(struct drm_plane *plane, static void hdlcd_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *state) { + struct drm_framebuffer *fb = plane->state->fb; struct hdlcd_drm_private *hdlcd; struct drm_gem_cma_object *gem; u32 src_w, src_h, dest_w, dest_h; dma_addr_t scanout_start; - if (!plane->state->fb) + if (!fb) return; src_w = plane->state->src_w >> 16; src_h = plane->state->src_h >> 16; dest_w = plane->state->crtc_w; dest_h = plane->state->crtc_h; - gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0); - scanout_start = gem->paddr + plane->state->fb->offsets[0] + - plane->state->crtc_y * plane->state->fb->pitches[0] + + gem = drm_fb_cma_get_gem_obj(fb, 0); + scanout_start = gem->paddr + fb->offsets[0] + + plane->state->crtc_y * fb->pitches[0] + plane->state->crtc_x * - drm_format_plane_cpp(plane->state->fb->pixel_format, 0); + fb->format->cpp[0]; hdlcd = plane->dev->dev_private; - hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_LENGTH, plane->state->fb->pitches[0]); - hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_PITCH, plane->state->fb->pitches[0]); + hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_LENGTH, fb->pitches[0]); + hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_PITCH, fb->pitches[0]); hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_COUNT, dest_h - 1); hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start); } diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index e5f4f4a6546d..4ce4f970920b 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -255,12 +255,6 @@ static int hdlcd_debugfs_init(struct drm_minor *minor) return drm_debugfs_create_files(hdlcd_debugfs_list, ARRAY_SIZE(hdlcd_debugfs_list), minor->debugfs_root, minor); } - -static void hdlcd_debugfs_cleanup(struct drm_minor *minor) -{ - drm_debugfs_remove_files(hdlcd_debugfs_list, - ARRAY_SIZE(hdlcd_debugfs_list), minor); -} #endif static const struct file_operations fops = { @@ -303,7 +297,6 @@ static struct drm_driver hdlcd_driver = { .gem_prime_mmap = drm_gem_cma_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = hdlcd_debugfs_init, - .debugfs_cleanup = hdlcd_debugfs_cleanup, #endif .fops = &fops, .name = "hdlcd", @@ -356,7 +349,7 @@ static int hdlcd_drm_bind(struct device *dev) drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); - hdlcd->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + hdlcd->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(hdlcd->fbdev)) { diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 32f746e31379..8b0672d4aee9 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -22,7 +22,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_of.h> @@ -256,6 +255,60 @@ static const struct of_device_id malidp_drm_of_match[] = { }; MODULE_DEVICE_TABLE(of, malidp_drm_of_match); +static bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev, + const struct of_device_id *dev_id) +{ + u32 core_id; + const char *compatstr_dp500 = "arm,mali-dp500"; + bool is_dp500; + bool dt_is_dp500; + + /* + * The DP500 CORE_ID register is in a different location, so check it + * first. If the product id field matches, then this is DP500, otherwise + * check the DP550/650 CORE_ID register. + */ + core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID); + /* Offset 0x18 will never read 0x500 on products other than DP500. */ + is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500); + dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500, + sizeof(dev_id->compatible)) != NULL; + if (is_dp500 != dt_is_dp500) { + DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n", + dev_id->compatible, is_dp500 ? "is" : "is not"); + return false; + } else if (!dt_is_dp500) { + u16 product_id; + char buf[32]; + + core_id = malidp_hw_read(hwdev, + MALIDP550_DC_BASE + MALIDP_DE_CORE_ID); + product_id = MALIDP_PRODUCT_ID(core_id); + snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id); + if (!strnstr(dev_id->compatible, buf, + sizeof(dev_id->compatible))) { + DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n", + dev_id->compatible, product_id); + return false; + } + } + return true; +} + +static bool malidp_has_sufficient_address_space(const struct resource *res, + const struct of_device_id *dev_id) +{ + resource_size_t res_size = resource_size(res); + const char *compatstr_dp500 = "arm,mali-dp500"; + + if (!strnstr(dev_id->compatible, compatstr_dp500, + sizeof(dev_id->compatible))) + return res_size >= MALIDP550_ADDR_SPACE_SIZE; + else if (res_size < MALIDP500_ADDR_SPACE_SIZE) + return false; + return true; +} + #define MAX_OUTPUT_CHANNELS 3 static int malidp_bind(struct device *dev) @@ -266,6 +319,7 @@ static int malidp_bind(struct device *dev) struct malidp_drm *malidp; struct malidp_hw_device *hwdev; struct platform_device *pdev = to_platform_device(dev); + struct of_device_id const *dev_id; /* number of lines for the R, G and B output */ u8 output_width[MAX_OUTPUT_CHANNELS]; int ret = 0, i; @@ -286,7 +340,6 @@ static int malidp_bind(struct device *dev) memcpy(hwdev, of_device_get_match_data(dev), sizeof(*hwdev)); malidp->dev = hwdev; - INIT_LIST_HEAD(&malidp->event_list); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hwdev->regs = devm_ioremap_resource(dev, res); @@ -329,6 +382,23 @@ static int malidp_bind(struct device *dev) clk_prepare_enable(hwdev->aclk); clk_prepare_enable(hwdev->mclk); + dev_id = of_match_device(malidp_drm_of_match, dev); + if (!dev_id) { + ret = -EINVAL; + goto query_hw_fail; + } + + if (!malidp_has_sufficient_address_space(res, dev_id)) { + DRM_ERROR("Insufficient address space in device-tree.\n"); + ret = -EINVAL; + goto query_hw_fail; + } + + if (!malidp_is_compatible_hw_id(hwdev, dev_id)) { + ret = -EINVAL; + goto query_hw_fail; + } + ret = hwdev->query_hw(hwdev); if (ret) { DRM_ERROR("Invalid HW configuration\n"); @@ -387,7 +457,7 @@ static int malidp_bind(struct device *dev) drm_mode_config_reset(drm); - malidp->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + malidp->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(malidp->fbdev)) { diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h index 9fc8a2e405e4..dbc617c6e4ef 100644 --- a/drivers/gpu/drm/arm/malidp_drv.h +++ b/drivers/gpu/drm/arm/malidp_drv.h @@ -15,12 +15,12 @@ #include <linux/mutex.h> #include <linux/wait.h> +#include <drm/drmP.h> #include "malidp_hw.h" struct malidp_drm { struct malidp_hw_device *dev; struct drm_fbdev_cma *fbdev; - struct list_head event_list; struct drm_crtc crtc; wait_queue_head_t wq; atomic_t config_valid; diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c index 4bdf531f7844..488aedf5b58d 100644 --- a/drivers/gpu/drm/arm/malidp_hw.c +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -21,7 +21,7 @@ #include "malidp_drv.h" #include "malidp_hw.h" -static const struct malidp_input_format malidp500_de_formats[] = { +static const struct malidp_format_id malidp500_de_formats[] = { /* fourcc, layers supporting the format, internal id */ { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 }, { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 1 }, @@ -69,21 +69,21 @@ static const struct malidp_input_format malidp500_de_formats[] = { { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \ { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) } -static const struct malidp_input_format malidp550_de_formats[] = { +static const struct malidp_format_id malidp550_de_formats[] = { MALIDP_COMMON_FORMATS, }; static const struct malidp_layer malidp500_layers[] = { - { DE_VIDEO1, MALIDP500_DE_LV_BASE, MALIDP500_DE_LV_PTR_BASE }, - { DE_GRAPHICS1, MALIDP500_DE_LG1_BASE, MALIDP500_DE_LG1_PTR_BASE }, - { DE_GRAPHICS2, MALIDP500_DE_LG2_BASE, MALIDP500_DE_LG2_PTR_BASE }, + { DE_VIDEO1, MALIDP500_DE_LV_BASE, MALIDP500_DE_LV_PTR_BASE, MALIDP_DE_LV_STRIDE0 }, + { DE_GRAPHICS1, MALIDP500_DE_LG1_BASE, MALIDP500_DE_LG1_PTR_BASE, MALIDP_DE_LG_STRIDE }, + { DE_GRAPHICS2, MALIDP500_DE_LG2_BASE, MALIDP500_DE_LG2_PTR_BASE, MALIDP_DE_LG_STRIDE }, }; static const struct malidp_layer malidp550_layers[] = { - { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE }, - { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE }, - { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE }, - { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE }, + { DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE, MALIDP_DE_LV_STRIDE0 }, + { DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE, MALIDP_DE_LG_STRIDE }, + { DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE, MALIDP_DE_LV_STRIDE0 }, + { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE, 0 }, }; #define MALIDP_DE_DEFAULT_PREFETCH_START 5 @@ -436,8 +436,8 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = { .irq_mask = MALIDP500_DE_IRQ_CONF_VALID, .vsync_irq = MALIDP500_DE_IRQ_CONF_VALID, }, - .input_formats = malidp500_de_formats, - .n_input_formats = ARRAY_SIZE(malidp500_de_formats), + .pixel_formats = malidp500_de_formats, + .n_pixel_formats = ARRAY_SIZE(malidp500_de_formats), .bus_align_bytes = 8, }, .query_hw = malidp500_query_hw, @@ -447,6 +447,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = { .set_config_valid = malidp500_set_config_valid, .modeset = malidp500_modeset, .rotmem_required = malidp500_rotmem_required, + .features = MALIDP_DEVICE_LV_HAS_3_STRIDES, }, [MALIDP_550] = { .map = { @@ -469,8 +470,8 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = { .irq_mask = MALIDP550_DC_IRQ_CONF_VALID, .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID, }, - .input_formats = malidp550_de_formats, - .n_input_formats = ARRAY_SIZE(malidp550_de_formats), + .pixel_formats = malidp550_de_formats, + .n_pixel_formats = ARRAY_SIZE(malidp550_de_formats), .bus_align_bytes = 8, }, .query_hw = malidp550_query_hw, @@ -480,6 +481,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = { .set_config_valid = malidp550_set_config_valid, .modeset = malidp550_modeset, .rotmem_required = malidp550_rotmem_required, + .features = 0, }, [MALIDP_650] = { .map = { @@ -503,8 +505,8 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = { .irq_mask = MALIDP550_DC_IRQ_CONF_VALID, .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID, }, - .input_formats = malidp550_de_formats, - .n_input_formats = ARRAY_SIZE(malidp550_de_formats), + .pixel_formats = malidp550_de_formats, + .n_pixel_formats = ARRAY_SIZE(malidp550_de_formats), .bus_align_bytes = 16, }, .query_hw = malidp650_query_hw, @@ -514,6 +516,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = { .set_config_valid = malidp550_set_config_valid, .modeset = malidp550_modeset, .rotmem_required = malidp550_rotmem_required, + .features = 0, }, }; @@ -522,10 +525,10 @@ u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map, { unsigned int i; - for (i = 0; i < map->n_input_formats; i++) { - if (((map->input_formats[i].layer & layer_id) == layer_id) && - (map->input_formats[i].format == format)) - return map->input_formats[i].id; + for (i = 0; i < map->n_pixel_formats; i++) { + if (((map->pixel_formats[i].layer & layer_id) == layer_id) && + (map->pixel_formats[i].format == format)) + return map->pixel_formats[i].id; } return MALIDP_INVALID_FORMAT_ID; diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h index 087e1202db3d..00974b59407d 100644 --- a/drivers/gpu/drm/arm/malidp_hw.h +++ b/drivers/gpu/drm/arm/malidp_hw.h @@ -35,7 +35,7 @@ enum { DE_SMART = BIT(4), }; -struct malidp_input_format { +struct malidp_format_id { u32 format; /* DRM fourcc */ u8 layer; /* bitmask of layers supporting it */ u8 id; /* used internally */ @@ -58,6 +58,7 @@ struct malidp_layer { u16 id; /* layer ID */ u16 base; /* address offset for the register bank */ u16 ptr; /* address offset for the pointer register */ + u16 stride_offset; /* Offset to the first stride register. */ }; /* regmap features */ @@ -85,14 +86,18 @@ struct malidp_hw_regmap { const struct malidp_irq_map se_irq_map; const struct malidp_irq_map dc_irq_map; - /* list of supported input formats for each layer */ - const struct malidp_input_format *input_formats; - const u8 n_input_formats; + /* list of supported pixel formats for each layer */ + const struct malidp_format_id *pixel_formats; + const u8 n_pixel_formats; /* pitch alignment requirement in bytes */ const u8 bus_align_bytes; }; +/* device features */ +/* Unlike DP550/650, DP500 has 3 stride registers in its video layer. */ +#define MALIDP_DEVICE_LV_HAS_3_STRIDES BIT(0) + struct malidp_hw_device { const struct malidp_hw_regmap map; void __iomem *regs; diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 63eec8f37cfc..414aada10fe5 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -11,6 +11,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> @@ -36,7 +37,6 @@ #define LAYER_V_VAL(x) (((x) & 0x1fff) << 16) #define MALIDP_LAYER_COMP_SIZE 0x010 #define MALIDP_LAYER_OFFSET 0x014 -#define MALIDP_LAYER_STRIDE 0x018 /* * This 4-entry look-up-table is used to determine the full 8-bit alpha value @@ -67,13 +67,14 @@ drm_plane_state *malidp_duplicate_plane_state(struct drm_plane *plane) return NULL; state = kmalloc(sizeof(*state), GFP_KERNEL); - if (state) { - m_state = to_malidp_plane_state(plane->state); - __drm_atomic_helper_plane_duplicate_state(plane, &state->base); - state->rotmem_size = m_state->rotmem_size; - state->format = m_state->format; - state->n_planes = m_state->n_planes; - } + if (!state) + return NULL; + + m_state = to_malidp_plane_state(plane->state); + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + state->rotmem_size = m_state->rotmem_size; + state->format = m_state->format; + state->n_planes = m_state->n_planes; return &state->base; } @@ -102,8 +103,10 @@ static int malidp_de_plane_check(struct drm_plane *plane, { struct malidp_plane *mp = to_malidp_plane(plane); struct malidp_plane_state *ms = to_malidp_plane_state(state); + struct drm_crtc_state *crtc_state; struct drm_framebuffer *fb; - int i; + struct drm_rect clip = { 0 }; + int i, ret; u32 src_w, src_h; if (!state->crtc || !state->fb) @@ -112,11 +115,11 @@ static int malidp_de_plane_check(struct drm_plane *plane, fb = state->fb; ms->format = malidp_hw_get_format_id(&mp->hwdev->map, mp->layer->id, - fb->pixel_format); + fb->format->format); if (ms->format == MALIDP_INVALID_FORMAT_ID) return -EINVAL; - ms->n_planes = drm_format_num_planes(fb->pixel_format); + ms->n_planes = fb->format->num_planes; for (i = 0; i < ms->n_planes; i++) { if (!malidp_hw_pitch_valid(mp->hwdev, fb->pitches[i])) { DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n", @@ -131,23 +134,42 @@ static int malidp_de_plane_check(struct drm_plane *plane, if ((state->crtc_w > mp->hwdev->max_line_size) || (state->crtc_h > mp->hwdev->max_line_size) || (state->crtc_w < mp->hwdev->min_line_size) || - (state->crtc_h < mp->hwdev->min_line_size) || - (state->crtc_w != src_w) || (state->crtc_h != src_h)) + (state->crtc_h < mp->hwdev->min_line_size)) + return -EINVAL; + + /* + * DP550/650 video layers can accept 3 plane formats only if + * fb->pitches[1] == fb->pitches[2] since they don't have a + * third plane stride register. + */ + if (ms->n_planes == 3 && + !(mp->hwdev->features & MALIDP_DEVICE_LV_HAS_3_STRIDES) && + (state->fb->pitches[1] != state->fb->pitches[2])) return -EINVAL; /* packed RGB888 / BGR888 can't be rotated or flipped */ if (state->rotation != DRM_ROTATE_0 && - (state->fb->pixel_format == DRM_FORMAT_RGB888 || - state->fb->pixel_format == DRM_FORMAT_BGR888)) + (fb->format->format == DRM_FORMAT_RGB888 || + fb->format->format == DRM_FORMAT_BGR888)) return -EINVAL; + crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + ret = drm_plane_helper_check_state(state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); + if (ret) + return ret; + ms->rotmem_size = 0; if (state->rotation & MALIDP_ROTATED_MASK) { int val; val = mp->hwdev->rotmem_required(mp->hwdev, state->crtc_h, state->crtc_w, - state->fb->pixel_format); + fb->format->format); if (val < 0) return val; @@ -157,6 +179,25 @@ static int malidp_de_plane_check(struct drm_plane *plane, return 0; } +static void malidp_de_set_plane_pitches(struct malidp_plane *mp, + int num_planes, unsigned int pitches[3]) +{ + int i; + int num_strides = num_planes; + + if (!mp->layer->stride_offset) + return; + + if (num_planes == 3) + num_strides = (mp->hwdev->features & + MALIDP_DEVICE_LV_HAS_3_STRIDES) ? 3 : 2; + + for (i = 0; i < num_strides; ++i) + malidp_hw_write(mp->hwdev, pitches[i], + mp->layer->base + + mp->layer->stride_offset + i * 4); +} + static void malidp_de_plane_update(struct drm_plane *plane, struct drm_plane_state *old_state) { @@ -174,13 +215,8 @@ static void malidp_de_plane_update(struct drm_plane *plane, /* convert src values from Q16 fixed point to integer */ src_w = plane->state->src_w >> 16; src_h = plane->state->src_h >> 16; - if (plane->state->rotation & MALIDP_ROTATED_MASK) { - dest_w = plane->state->crtc_h; - dest_h = plane->state->crtc_w; - } else { - dest_w = plane->state->crtc_w; - dest_h = plane->state->crtc_h; - } + dest_w = plane->state->crtc_w; + dest_h = plane->state->crtc_h; malidp_hw_write(mp->hwdev, ms->format, mp->layer->base); @@ -189,11 +225,12 @@ static void malidp_de_plane_update(struct drm_plane *plane, ptr = mp->layer->ptr + (i << 4); obj = drm_fb_cma_get_gem_obj(plane->state->fb, i); + obj->paddr += plane->state->fb->offsets[i]; malidp_hw_write(mp->hwdev, lower_32_bits(obj->paddr), ptr); malidp_hw_write(mp->hwdev, upper_32_bits(obj->paddr), ptr + 4); - malidp_hw_write(mp->hwdev, plane->state->fb->pitches[i], - mp->layer->base + MALIDP_LAYER_STRIDE); } + malidp_de_set_plane_pitches(mp, ms->n_planes, + plane->state->fb->pitches); malidp_hw_write(mp->hwdev, LAYER_H_VAL(src_w) | LAYER_V_VAL(src_h), mp->layer->base + MALIDP_LAYER_SIZE); @@ -211,11 +248,12 @@ static void malidp_de_plane_update(struct drm_plane *plane, /* setup the rotation and axis flip bits */ if (plane->state->rotation & DRM_ROTATE_MASK) - val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET; + val |= ilog2(plane->state->rotation & DRM_ROTATE_MASK) << + LAYER_ROT_OFFSET; if (plane->state->rotation & DRM_REFLECT_X) - val |= LAYER_V_FLIP; - if (plane->state->rotation & DRM_REFLECT_Y) val |= LAYER_H_FLIP; + if (plane->state->rotation & DRM_REFLECT_Y) + val |= LAYER_V_FLIP; /* * always enable pixel alpha blending until we have a way to change @@ -258,7 +296,7 @@ int malidp_de_planes_init(struct drm_device *drm) u32 *formats; int ret, i, j, n; - formats = kcalloc(map->n_input_formats, sizeof(*formats), GFP_KERNEL); + formats = kcalloc(map->n_pixel_formats, sizeof(*formats), GFP_KERNEL); if (!formats) { ret = -ENOMEM; goto cleanup; @@ -274,9 +312,9 @@ int malidp_de_planes_init(struct drm_device *drm) } /* build the list of DRM supported formats based on the map */ - for (n = 0, j = 0; j < map->n_input_formats; j++) { - if ((map->input_formats[j].layer & id) == id) - formats[n++] = map->input_formats[j].format; + for (n = 0, j = 0; j < map->n_pixel_formats; j++) { + if ((map->pixel_formats[j].layer & id) == id) + formats[n++] = map->pixel_formats[j].format; } plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY : diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h index 73fecb38f955..aff6d4a84e99 100644 --- a/drivers/gpu/drm/arm/malidp_regs.h +++ b/drivers/gpu/drm/arm/malidp_regs.h @@ -81,6 +81,10 @@ #define MALIDP_DE_SYNC_WIDTH 0x8 #define MALIDP_DE_HV_ACTIVE 0xc +/* Stride register offsets relative to Lx_BASE */ +#define MALIDP_DE_LG_STRIDE 0x18 +#define MALIDP_DE_LV_STRIDE0 0x18 + /* macros to set values into registers */ #define MALIDP_DE_H_FRONTPORCH(x) (((x) & 0xfff) << 0) #define MALIDP_DE_H_BACKPORCH(x) (((x) & 0x3ff) << 16) @@ -92,7 +96,10 @@ #define MALIDP_DE_H_ACTIVE(x) (((x) & 0x1fff) << 0) #define MALIDP_DE_V_ACTIVE(x) (((x) & 0x1fff) << 16) +#define MALIDP_PRODUCT_ID(__core_id) ((u32)(__core_id) >> 16) + /* register offsets and bits specific to DP500 */ +#define MALIDP500_ADDR_SPACE_SIZE 0x01000 #define MALIDP500_DC_BASE 0x00000 #define MALIDP500_DC_CONTROL 0x0000c #define MALIDP500_DC_CONFIG_REQ (1 << 17) @@ -125,6 +132,7 @@ #define MALIDP500_CONFIG_ID 0x00fd4 /* register offsets and bits specific to DP550/DP650 */ +#define MALIDP550_ADDR_SPACE_SIZE 0x10000 #define MALIDP550_DE_CONTROL 0x00010 #define MALIDP550_DE_LINE_COUNTER 0x00014 #define MALIDP550_DE_AXI_CONTROL 0x00018 diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig index 15f3ecfb16f1..eafaeeb7b5b1 100644 --- a/drivers/gpu/drm/armada/Kconfig +++ b/drivers/gpu/drm/armada/Kconfig @@ -1,6 +1,6 @@ config DRM_ARMADA tristate "DRM support for Marvell Armada SoCs" - depends on DRM && HAVE_CLK && ARM + depends on DRM && HAVE_CLK && ARM && MMU select DRM_KMS_HELPER help Support the "LCD" controllers found on the Marvell Armada 510 diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile index a18f156c8b66..64c0b4546fb2 100644 --- a/drivers/gpu/drm/armada/Makefile +++ b/drivers/gpu/drm/armada/Makefile @@ -4,3 +4,5 @@ armada-y += armada_510.o armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o obj-$(CONFIG_DRM_ARMADA) := armada.o + +CFLAGS_armada_trace.o := -I$(src) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 95cb3966b2ca..e62ee4498ce4 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -169,8 +169,7 @@ void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb, int x, int y) { u32 addr = drm_fb_obj(fb)->dev_addr; - u32 pixel_format = fb->pixel_format; - int num_planes = drm_format_num_planes(pixel_format); + int num_planes = fb->format->num_planes; int i; if (num_planes > 3) @@ -178,7 +177,7 @@ void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb, for (i = 0; i < num_planes; i++) addrs[i] = addr + fb->offsets[i] + y * fb->pitches[i] + - x * drm_format_plane_cpp(pixel_format, i); + x * fb->format->cpp[i]; for (; i < 3; i++) addrs[i] = 0; } @@ -191,7 +190,7 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb, unsigned i = 0; DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n", - pitch, x, y, fb->bits_per_pixel); + pitch, x, y, fb->format->cpp[0] * 8); armada_drm_plane_calc_addrs(addrs, fb, x, y); @@ -1036,7 +1035,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, int ret; /* We don't support changing the pixel format */ - if (fb->pixel_format != crtc->primary->fb->pixel_format) + if (fb->format != crtc->primary->fb->format) return -EINVAL; work = kmalloc(sizeof(*work), GFP_KERNEL); diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c index 90222e60d2d6..a8020cf9da2e 100644 --- a/drivers/gpu/drm/armada/armada_debugfs.c +++ b/drivers/gpu/drm/armada/armada_debugfs.c @@ -19,13 +19,13 @@ static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data) struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; struct armada_private *priv = dev->dev_private; - int ret; + struct drm_printer p = drm_seq_file_printer(m); mutex_lock(&priv->linear_lock); - ret = drm_mm_dump_table(m, &priv->linear); + drm_mm_print(&priv->linear, &p); mutex_unlock(&priv->linear_lock); - return ret; + return 0; } static int armada_debugfs_reg_show(struct seq_file *m, void *data) diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 07086b427c22..63f42d001f33 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -203,12 +203,6 @@ static int armada_drm_bind(struct device *dev) armada_drm_debugfs_init(priv->drm.primary); #endif - DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", - armada_drm_driver.name, armada_drm_driver.major, - armada_drm_driver.minor, armada_drm_driver.patchlevel, - armada_drm_driver.date, dev_name(dev), - priv->drm.primary->index); - return 0; err_poll: diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c index f03c212b754d..2a7eb6817c36 100644 --- a/drivers/gpu/drm/armada/armada_fb.c +++ b/drivers/gpu/drm/armada/armada_fb.c @@ -81,7 +81,7 @@ struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, dfb->mod = config; dfb->obj = obj; - drm_helper_mode_fill_fb_struct(&dfb->fb, mode); + drm_helper_mode_fill_fb_struct(dev, &dfb->fb, mode); ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs); if (ret) { diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index c5dc06a55883..0233e1dc33e1 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -89,11 +89,12 @@ static int armada_fb_create(struct drm_fb_helper *fbh, info->screen_base = ptr; fbh->fb = &dfb->fb; - drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth); + drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], + dfb->fb.format->depth); drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height); DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n", - dfb->fb.width, dfb->fb.height, dfb->fb.bits_per_pixel, + dfb->fb.width, dfb->fb.height, dfb->fb.format->cpp[0] * 8, (unsigned long long)obj->phys_addr); return 0; @@ -136,7 +137,7 @@ int armada_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); - ret = drm_fb_helper_init(dev, fbh, 1, 1); + ret = drm_fb_helper_init(dev, fbh, 1); if (ret) { DRM_ERROR("failed to initialize drm fb helper\n"); goto err_fb_helper; diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index a293c8be232c..1597458d884e 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -14,14 +14,15 @@ #include <drm/armada_drm.h> #include "armada_ioctlP.h" -static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int armada_gem_vm_fault(struct vm_fault *vmf) { - struct armada_gem_object *obj = drm_to_armada_gem(vma->vm_private_data); + struct drm_gem_object *gobj = vmf->vma->vm_private_data; + struct armada_gem_object *obj = drm_to_armada_gem(gobj); unsigned long pfn = obj->phys_addr >> PAGE_SHIFT; int ret; - pfn += (vmf->address - vma->vm_start) >> PAGE_SHIFT; - ret = vm_insert_pfn(vma, vmf->address, pfn); + pfn += (vmf->address - vmf->vma->vm_start) >> PAGE_SHIFT; + ret = vm_insert_pfn(vmf->vma, vmf->address, pfn); switch (ret) { case 0: @@ -148,8 +149,8 @@ armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj) return -ENOSPC; mutex_lock(&priv->linear_lock); - ret = drm_mm_insert_node(&priv->linear, node, size, align, - DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node_generic(&priv->linear, node, + size, align, 0, 0); mutex_unlock(&priv->linear_lock); if (ret) { kfree(node); diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index 6743615232f5..34cb73d0db77 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -186,9 +186,9 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, armada_drm_plane_calc_addrs(addrs, fb, src_x, src_y); - pixel_format = fb->pixel_format; + pixel_format = fb->format->format; hsub = drm_format_horz_chroma_subsampling(pixel_format); - num_planes = drm_format_num_planes(pixel_format); + num_planes = fb->format->num_planes; /* * Annoyingly, shifting a YUYV-format image by one pixel diff --git a/drivers/gpu/drm/ast/Kconfig b/drivers/gpu/drm/ast/Kconfig index 15f6ce7acb2a..9647e1f07088 100644 --- a/drivers/gpu/drm/ast/Kconfig +++ b/drivers/gpu/drm/ast/Kconfig @@ -1,6 +1,6 @@ config DRM_AST tristate "AST server chips" - depends on DRM && PCI + depends on DRM && PCI && MMU select DRM_TTM select DRM_KMS_HELPER select DRM_TTM diff --git a/drivers/gpu/drm/ast/ast_dram_tables.h b/drivers/gpu/drm/ast/ast_dram_tables.h index cc04539c0ff3..1d9c4e75d303 100644 --- a/drivers/gpu/drm/ast/ast_dram_tables.h +++ b/drivers/gpu/drm/ast/ast_dram_tables.h @@ -141,4 +141,66 @@ static const struct ast_dramstruct ast2100_dram_table_data[] = { { 0xffff, 0xffffffff }, }; +/* + * AST2500 DRAM settings modules + */ +#define REGTBL_NUM 17 +#define REGIDX_010 0 +#define REGIDX_014 1 +#define REGIDX_018 2 +#define REGIDX_020 3 +#define REGIDX_024 4 +#define REGIDX_02C 5 +#define REGIDX_030 6 +#define REGIDX_214 7 +#define REGIDX_2E0 8 +#define REGIDX_2E4 9 +#define REGIDX_2E8 10 +#define REGIDX_2EC 11 +#define REGIDX_2F0 12 +#define REGIDX_2F4 13 +#define REGIDX_2F8 14 +#define REGIDX_RFC 15 +#define REGIDX_PLL 16 + +static const u32 ast2500_ddr3_1600_timing_table[REGTBL_NUM] = { + 0x64604D38, /* 0x010 */ + 0x29690599, /* 0x014 */ + 0x00000300, /* 0x018 */ + 0x00000000, /* 0x020 */ + 0x00000000, /* 0x024 */ + 0x02181E70, /* 0x02C */ + 0x00000040, /* 0x030 */ + 0x00000024, /* 0x214 */ + 0x02001300, /* 0x2E0 */ + 0x0E0000A0, /* 0x2E4 */ + 0x000E001B, /* 0x2E8 */ + 0x35B8C105, /* 0x2EC */ + 0x08090408, /* 0x2F0 */ + 0x9B000800, /* 0x2F4 */ + 0x0E400A00, /* 0x2F8 */ + 0x9971452F, /* tRFC */ + 0x000071C1 /* PLL */ +}; + +static const u32 ast2500_ddr4_1600_timing_table[REGTBL_NUM] = { + 0x63604E37, /* 0x010 */ + 0xE97AFA99, /* 0x014 */ + 0x00019000, /* 0x018 */ + 0x08000000, /* 0x020 */ + 0x00000400, /* 0x024 */ + 0x00000410, /* 0x02C */ + 0x00000101, /* 0x030 */ + 0x00000024, /* 0x214 */ + 0x03002900, /* 0x2E0 */ + 0x0E0000A0, /* 0x2E4 */ + 0x000E001C, /* 0x2E8 */ + 0x35B8C106, /* 0x2EC */ + 0x08080607, /* 0x2F0 */ + 0x9B000900, /* 0x2F4 */ + 0x0E400A00, /* 0x2F8 */ + 0x99714545, /* tRFC */ + 0x000071C1 /* PLL */ +}; + #endif diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 7abda94fc2cf..8880f0b62e9c 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -28,6 +28,7 @@ #ifndef __AST_DRV_H__ #define __AST_DRV_H__ +#include <drm/drm_encoder.h> #include <drm/drm_fb_helper.h> #include <drm/ttm/ttm_bo_api.h> @@ -64,6 +65,7 @@ enum ast_chip { AST2150, AST2300, AST2400, + AST2500, AST1180, }; @@ -80,6 +82,7 @@ enum ast_tx_chip { #define AST_DRAM_1Gx32 3 #define AST_DRAM_2Gx16 6 #define AST_DRAM_4Gx16 7 +#define AST_DRAM_8Gx16 8 struct ast_fbdev; @@ -113,7 +116,11 @@ struct ast_private { struct ttm_bo_kmap_obj cache_kmap; int next_cursor; bool support_wide_screen; - bool DisableP2A; + enum { + ast_use_p2a, + ast_use_dt, + ast_use_defaults + } config_mode; enum ast_tx_chip tx_chip_type; u8 dp501_maxclk; @@ -122,7 +129,7 @@ struct ast_private { }; int ast_driver_load(struct drm_device *dev, unsigned long flags); -int ast_driver_unload(struct drm_device *dev); +void ast_driver_unload(struct drm_device *dev); struct ast_gem_object; @@ -300,8 +307,8 @@ struct ast_vbios_dclk_info { }; struct ast_vbios_mode_info { - struct ast_vbios_stdtable *std_table; - struct ast_vbios_enhtable *enh_table; + const struct ast_vbios_stdtable *std_table; + const struct ast_vbios_enhtable *enh_table; }; extern int ast_mode_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index d6f5ec64c667..5d0ffab411a8 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -49,7 +49,7 @@ static void ast_dirty_update(struct ast_fbdev *afbdev, struct drm_gem_object *obj; struct ast_bo *bo; int src_offset, dst_offset; - int bpp = (afbdev->afb.base.bits_per_pixel + 7)/8; + int bpp = afbdev->afb.base.format->cpp[0]; int ret = -EBUSY; bool unmap = false; bool store_for_later = false; @@ -237,7 +237,7 @@ static int astfb_create(struct drm_fb_helper *helper, info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0); info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0); - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &afbdev->helper, sizes->fb_width, sizes->fb_height); info->screen_base = sysram; @@ -315,8 +315,7 @@ int ast_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs); - ret = drm_fb_helper_init(dev, &afbdev->helper, - 1, 1); + ret = drm_fb_helper_init(dev, &afbdev->helper, 1); if (ret) goto free; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 533e762d036d..262c2c0e43b4 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -32,8 +32,6 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> -#include "ast_dram_tables.h" - void ast_set_index_reg_mask(struct ast_private *ast, uint32_t base, uint8_t index, uint8_t mask, uint8_t val) @@ -62,30 +60,99 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast, return ret; } +static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) +{ + struct device_node *np = dev->pdev->dev.of_node; + struct ast_private *ast = dev->dev_private; + uint32_t data, jregd0, jregd1; + + /* Defaults */ + ast->config_mode = ast_use_defaults; + *scu_rev = 0xffffffff; + + /* Check if we have device-tree properties */ + if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", + scu_rev)) { + /* We do, disable P2A access */ + ast->config_mode = ast_use_dt; + DRM_INFO("Using device-tree for configuration\n"); + return; + } + + /* Not all families have a P2A bridge */ + if (dev->pdev->device != PCI_CHIP_AST2000) + return; + + /* + * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge + * is disabled. We force using P2A if VGA only mode bit + * is set D[7] + */ + jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { + /* Double check it's actually working */ + data = ast_read32(ast, 0xf004); + if (data != 0xFFFFFFFF) { + /* P2A works, grab silicon revision */ + ast->config_mode = ast_use_p2a; + + DRM_INFO("Using P2A bridge for configuration\n"); + + /* Read SCU7c (silicon revision register) */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + *scu_rev = ast_read32(ast, 0x1207c); + return; + } + } + + /* We have a P2A bridge but it's disabled */ + DRM_INFO("P2A bridge disabled, using default configuration\n"); +} static int ast_detect_chip(struct drm_device *dev, bool *need_post) { struct ast_private *ast = dev->dev_private; - uint32_t data, jreg; + uint32_t jreg, scu_rev; + + /* + * If VGA isn't enabled, we need to enable now or subsequent + * access to the scratch registers will fail. We also inform + * our caller that it needs to POST the chip + * (Assumption: VGA not enabled -> need to POST) + */ + if (!ast_is_vga_enabled(dev)) { + ast_enable_vga(dev); + DRM_INFO("VGA not enabled on entry, requesting chip POST\n"); + *need_post = true; + } else + *need_post = false; + + + /* Enable extended register access */ + ast_enable_mmio(dev); ast_open_key(ast); + /* Find out whether P2A works or whether to use device-tree */ + ast_detect_config_mode(dev, &scu_rev); + + /* Identify chipset */ if (dev->pdev->device == PCI_CHIP_AST1180) { ast->chip = AST1100; DRM_INFO("AST 1180 detected\n"); } else { - if (dev->pdev->revision >= 0x30) { + if (dev->pdev->revision >= 0x40) { + ast->chip = AST2500; + DRM_INFO("AST 2500 detected\n"); + } else if (dev->pdev->revision >= 0x30) { ast->chip = AST2400; DRM_INFO("AST 2400 detected\n"); } else if (dev->pdev->revision >= 0x20) { ast->chip = AST2300; DRM_INFO("AST 2300 detected\n"); } else if (dev->pdev->revision >= 0x10) { - uint32_t data; - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - - data = ast_read32(ast, 0x1207c); - switch (data & 0x0300) { + switch (scu_rev & 0x0300) { case 0x0200: ast->chip = AST1100; DRM_INFO("AST 1100 detected\n"); @@ -110,26 +177,6 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) } } - /* - * If VGA isn't enabled, we need to enable now or subsequent - * access to the scratch registers will fail. We also inform - * our caller that it needs to POST the chip - * (Assumption: VGA not enabled -> need to POST) - */ - if (!ast_is_vga_enabled(dev)) { - ast_enable_vga(dev); - ast_enable_mmio(dev); - DRM_INFO("VGA not enabled on entry, requesting chip POST\n"); - *need_post = true; - } else - *need_post = false; - - /* Check P2A Access */ - ast->DisableP2A = true; - data = ast_read32(ast, 0xf004); - if (data != 0xFFFFFFFF) - ast->DisableP2A = false; - /* Check if we support wide screen */ switch (ast->chip) { case AST1180: @@ -146,17 +193,15 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) ast->support_wide_screen = true; else { ast->support_wide_screen = false; - if (ast->DisableP2A == false) { - /* Read SCU7c (silicon revision register) */ - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - data = ast_read32(ast, 0x1207c); - data &= 0x300; - if (ast->chip == AST2300 && data == 0x0) /* ast1300 */ - ast->support_wide_screen = true; - if (ast->chip == AST2400 && data == 0x100) /* ast1400 */ - ast->support_wide_screen = true; - } + if (ast->chip == AST2300 && + (scu_rev & 0x300) == 0x0) /* ast1300 */ + ast->support_wide_screen = true; + if (ast->chip == AST2400 && + (scu_rev & 0x300) == 0x100) /* ast1400 */ + ast->support_wide_screen = true; + if (ast->chip == AST2500 && + scu_rev == 0x100) /* ast2510 */ + ast->support_wide_screen = true; } break; } @@ -220,85 +265,121 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) static int ast_get_dram_info(struct drm_device *dev) { + struct device_node *np = dev->pdev->dev.of_node; struct ast_private *ast = dev->dev_private; - uint32_t data, data2; - uint32_t denum, num, div, ref_pll; + uint32_t mcr_cfg, mcr_scu_mpll, mcr_scu_strap; + uint32_t denum, num, div, ref_pll, dsel; - if (ast->DisableP2A) - { - ast->dram_bus_width = 16; - ast->dram_type = AST_DRAM_1Gx16; - ast->mclk = 396; - } - else - { + switch (ast->config_mode) { + case ast_use_dt: + /* + * If some properties are missing, use reasonable + * defaults for AST2400 + */ + if (of_property_read_u32(np, "aspeed,mcr-configuration", + &mcr_cfg)) + mcr_cfg = 0x00000577; + if (of_property_read_u32(np, "aspeed,mcr-scu-mpll", + &mcr_scu_mpll)) + mcr_scu_mpll = 0x000050C0; + if (of_property_read_u32(np, "aspeed,mcr-scu-strap", + &mcr_scu_strap)) + mcr_scu_strap = 0; + break; + case ast_use_p2a: ast_write32(ast, 0xf004, 0x1e6e0000); ast_write32(ast, 0xf000, 0x1); - data = ast_read32(ast, 0x10004); - - if (data & 0x40) - ast->dram_bus_width = 16; + mcr_cfg = ast_read32(ast, 0x10004); + mcr_scu_mpll = ast_read32(ast, 0x10120); + mcr_scu_strap = ast_read32(ast, 0x10170); + break; + case ast_use_defaults: + default: + ast->dram_bus_width = 16; + ast->dram_type = AST_DRAM_1Gx16; + if (ast->chip == AST2500) + ast->mclk = 800; else - ast->dram_bus_width = 32; - - if (ast->chip == AST2300 || ast->chip == AST2400) { - switch (data & 0x03) { - case 0: - ast->dram_type = AST_DRAM_512Mx16; - break; - default: - case 1: - ast->dram_type = AST_DRAM_1Gx16; - break; - case 2: - ast->dram_type = AST_DRAM_2Gx16; - break; - case 3: - ast->dram_type = AST_DRAM_4Gx16; - break; - } - } else { - switch (data & 0x0c) { - case 0: - case 4: - ast->dram_type = AST_DRAM_512Mx16; - break; - case 8: - if (data & 0x40) - ast->dram_type = AST_DRAM_1Gx16; - else - ast->dram_type = AST_DRAM_512Mx32; - break; - case 0xc: - ast->dram_type = AST_DRAM_1Gx32; - break; - } - } + ast->mclk = 396; + return 0; + } - data = ast_read32(ast, 0x10120); - data2 = ast_read32(ast, 0x10170); - if (data2 & 0x2000) - ref_pll = 14318; - else - ref_pll = 12000; + if (mcr_cfg & 0x40) + ast->dram_bus_width = 16; + else + ast->dram_bus_width = 32; - denum = data & 0x1f; - num = (data & 0x3fe0) >> 5; - data = (data & 0xc000) >> 14; - switch (data) { - case 3: - div = 0x4; + if (ast->chip == AST2500) { + switch (mcr_cfg & 0x03) { + case 0: + ast->dram_type = AST_DRAM_1Gx16; break; - case 2: + default: case 1: - div = 0x2; + ast->dram_type = AST_DRAM_2Gx16; + break; + case 2: + ast->dram_type = AST_DRAM_4Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_8Gx16; + break; + } + } else if (ast->chip == AST2300 || ast->chip == AST2400) { + switch (mcr_cfg & 0x03) { + case 0: + ast->dram_type = AST_DRAM_512Mx16; break; default: - div = 0x1; + case 1: + ast->dram_type = AST_DRAM_1Gx16; + break; + case 2: + ast->dram_type = AST_DRAM_2Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_4Gx16; + break; + } + } else { + switch (mcr_cfg & 0x0c) { + case 0: + case 4: + ast->dram_type = AST_DRAM_512Mx16; + break; + case 8: + if (mcr_cfg & 0x40) + ast->dram_type = AST_DRAM_1Gx16; + else + ast->dram_type = AST_DRAM_512Mx32; + break; + case 0xc: + ast->dram_type = AST_DRAM_1Gx32; break; } - ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); } + + if (mcr_scu_strap & 0x2000) + ref_pll = 14318; + else + ref_pll = 12000; + + denum = mcr_scu_mpll & 0x1f; + num = (mcr_scu_mpll & 0x3fe0) >> 5; + dsel = (mcr_scu_mpll & 0xc000) >> 14; + switch (dsel) { + case 3: + div = 0x4; + break; + case 2: + case 1: + div = 0x2; + break; + default: + div = 0x1; + break; + } + ast->mclk = ref_pll * (num + 2) / ((denum + 2) * (div * 1000)); return 0; } @@ -323,7 +404,7 @@ int ast_framebuffer_init(struct drm_device *dev, { int ret; - drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &ast_fb->base, mode_cmd); ast_fb->obj = obj; ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); if (ret) { @@ -437,17 +518,19 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ast_detect_chip(dev, &need_post); + if (need_post) + ast_post_gpu(dev); + if (ast->chip != AST1180) { ret = ast_get_dram_info(dev); if (ret) goto out_free; ast->vram_size = ast_get_vram_info(dev); - DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size); + DRM_INFO("dram MCLK=%u Mhz type=%d bus_width=%d size=%08x\n", + ast->mclk, ast->dram_type, + ast->dram_bus_width, ast->vram_size); } - if (need_post) - ast_post_gpu(dev); - ret = ast_mm_init(ast); if (ret) goto out_free; @@ -465,6 +548,7 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ast->chip == AST2200 || ast->chip == AST2300 || ast->chip == AST2400 || + ast->chip == AST2500 || ast->chip == AST1180) { dev->mode_config.max_width = 1920; dev->mode_config.max_height = 2048; @@ -488,7 +572,7 @@ out_free: return ret; } -int ast_driver_unload(struct drm_device *dev) +void ast_driver_unload(struct drm_device *dev) { struct ast_private *ast = dev->dev_private; @@ -501,7 +585,6 @@ int ast_driver_unload(struct drm_device *dev) pci_iounmap(dev->pdev, ast->ioregs); pci_iounmap(dev->pdev, ast->regs); kfree(ast); - return 0; } int ast_gem_create(struct drm_device *dev, diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index e26c98f51eb4..47b78e52691c 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -79,12 +79,13 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo struct ast_vbios_mode_info *vbios_mode) { struct ast_private *ast = crtc->dev->dev_private; + const struct drm_framebuffer *fb = crtc->primary->fb; u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate; + const struct ast_vbios_enhtable *best = NULL; u32 hborder, vborder; bool check_sync; - struct ast_vbios_enhtable *best = NULL; - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: vbios_mode->std_table = &vbios_stdtable[VGAModeIndex]; color_index = VGAModeIndex - 1; @@ -146,7 +147,7 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo refresh_rate = drm_mode_vrefresh(mode); check_sync = vbios_mode->enh_table->flags & WideScreenMode; do { - struct ast_vbios_enhtable *loop = vbios_mode->enh_table; + const struct ast_vbios_enhtable *loop = vbios_mode->enh_table; while (loop->refresh_rate != 0xff) { if ((check_sync) && @@ -207,7 +208,8 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0x00); if (vbios_mode->enh_table->flags & NewModeInfo) { ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, + fb->format->cpp[0] * 8); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8); @@ -225,7 +227,7 @@ static void ast_set_std_reg(struct drm_crtc *crtc, struct drm_display_mode *mode struct ast_vbios_mode_info *vbios_mode) { struct ast_private *ast = crtc->dev->dev_private; - struct ast_vbios_stdtable *stdtable; + const struct ast_vbios_stdtable *stdtable; u32 i; u8 jreg; @@ -271,7 +273,11 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod { struct ast_private *ast = crtc->dev->dev_private; u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0; - u16 temp; + u16 temp, precache = 0; + + if ((ast->chip == AST2500) && + (vbios_mode->enh_table->flags & AST2500PreCatchCRT)) + precache = 40; ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00); @@ -297,12 +303,12 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod jregAD |= 0x01; /* HBE D[5] */ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x03, 0xE0, (temp & 0x1f)); - temp = (mode->crtc_hsync_start >> 3) - 1; + temp = ((mode->crtc_hsync_start-precache) >> 3) - 1; if (temp & 0x100) jregAC |= 0x40; /* HRS D[5] */ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x04, 0x00, temp); - temp = ((mode->crtc_hsync_end >> 3) - 1) & 0x3f; + temp = (((mode->crtc_hsync_end-precache) >> 3) - 1) & 0x3f; if (temp & 0x20) jregAD |= 0x04; /* HRE D[5] */ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05)); @@ -363,16 +369,22 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x09, 0xdf, jreg09); ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAE, 0x00, (jregAE | 0x80)); + if (precache) + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0x3f, 0x80); + else + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0x3f, 0x00); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x80); } static void ast_set_offset_reg(struct drm_crtc *crtc) { struct ast_private *ast = crtc->dev->dev_private; + const struct drm_framebuffer *fb = crtc->primary->fb; u16 offset; - offset = crtc->primary->fb->pitches[0] >> 3; + offset = fb->pitches[0] >> 3; ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x13, (offset & 0xff)); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xb0, (offset >> 8) & 0x3f); } @@ -381,23 +393,28 @@ static void ast_set_dclk_reg(struct drm_device *dev, struct drm_display_mode *mo struct ast_vbios_mode_info *vbios_mode) { struct ast_private *ast = dev->dev_private; - struct ast_vbios_dclk_info *clk_info; + const struct ast_vbios_dclk_info *clk_info; - clk_info = &dclk_table[vbios_mode->enh_table->dclk_index]; + if (ast->chip == AST2500) + clk_info = &dclk_table_ast2500[vbios_mode->enh_table->dclk_index]; + else + clk_info = &dclk_table[vbios_mode->enh_table->dclk_index]; ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc0, 0x00, clk_info->param1); ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc1, 0x00, clk_info->param2); ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xbb, 0x0f, - (clk_info->param3 & 0x80) | ((clk_info->param3 & 0x3) << 4)); + (clk_info->param3 & 0xc0) | + ((clk_info->param3 & 0x3) << 4)); } static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, struct ast_vbios_mode_info *vbios_mode) { struct ast_private *ast = crtc->dev->dev_private; + const struct drm_framebuffer *fb = crtc->primary->fb; u8 jregA0 = 0, jregA3 = 0, jregA8 = 0; - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: jregA0 = 0x70; jregA3 = 0x01; @@ -421,7 +438,8 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8); /* Set Threshold */ - if (ast->chip == AST2300 || ast->chip == AST2400) { + if (ast->chip == AST2300 || ast->chip == AST2400 || + ast->chip == AST2500) { ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60); } else if (ast->chip == AST2100 || @@ -452,7 +470,9 @@ static void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mo static bool ast_set_dac_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, struct ast_vbios_mode_info *vbios_mode) { - switch (crtc->primary->fb->bits_per_pixel) { + const struct drm_framebuffer *fb = crtc->primary->fb; + + switch (fb->format->cpp[0] * 8) { case 8: break; default: @@ -794,7 +814,9 @@ static int ast_mode_valid(struct drm_connector *connector, if ((mode->hdisplay == 1600) && (mode->vdisplay == 900)) return MODE_OK; - if ((ast->chip == AST2100) || (ast->chip == AST2200) || (ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST1180)) { + if ((ast->chip == AST2100) || (ast->chip == AST2200) || + (ast->chip == AST2300) || (ast->chip == AST2400) || + (ast->chip == AST2500) || (ast->chip == AST1180)) { if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080)) return MODE_OK; diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 5331ee1df086..f7d421359d56 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -31,7 +31,8 @@ #include "ast_dram_tables.h" -static void ast_init_dram_2300(struct drm_device *dev); +static void ast_post_chip_2300(struct drm_device *dev); +static void ast_post_chip_2500(struct drm_device *dev); void ast_enable_vga(struct drm_device *dev) { @@ -58,13 +59,9 @@ bool ast_is_vga_enabled(struct drm_device *dev) /* TODO 1180 */ } else { ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); - if (ch) { - ast_open_key(ast); - ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff); - return ch & 0x04; - } + return !!(ch & 0x01); } - return 0; + return false; } static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; @@ -79,10 +76,11 @@ ast_set_def_ext_reg(struct drm_device *dev) const u8 *ext_reg_info; /* reset scratch */ - for (i = 0x81; i <= 0x8f; i++) + for (i = 0x81; i <= 0x9f; i++) ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00); - if (ast->chip == AST2300 || ast->chip == AST2400) { + if (ast->chip == AST2300 || ast->chip == AST2400 || + ast->chip == AST2500) { if (dev->pdev->revision >= 0x20) ext_reg_info = extreginfo_ast2300; else @@ -106,7 +104,8 @@ ast_set_def_ext_reg(struct drm_device *dev) /* Enable RAMDAC for A1 */ reg = 0x04; - if (ast->chip == AST2300 || ast->chip == AST2400) + if (ast->chip == AST2300 || ast->chip == AST2400 || + ast->chip == AST2500) reg |= 0x20; ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg); } @@ -375,21 +374,20 @@ void ast_post_gpu(struct drm_device *dev) pci_write_config_dword(ast->dev->pdev, 0x04, reg); ast_enable_vga(dev); - ast_enable_mmio(dev); ast_open_key(ast); + ast_enable_mmio(dev); ast_set_def_ext_reg(dev); - if (ast->DisableP2A == false) - { - if (ast->chip == AST2300 || ast->chip == AST2400) - ast_init_dram_2300(dev); + if (ast->config_mode == ast_use_p2a) { + if (ast->chip == AST2500) + ast_post_chip_2500(dev); + else if (ast->chip == AST2300 || ast->chip == AST2400) + ast_post_chip_2300(dev); else ast_init_dram_reg(dev); ast_init_3rdtx(dev); - } - else - { + } else { if (ast->tx_chip_type != AST_TX_NONE) ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); /* Enable DVO */ } @@ -448,85 +446,70 @@ static const u32 pattern[8] = { 0x7C61D253 }; -static int mmc_test_burst(struct ast_private *ast, u32 datagen) +static bool mmc_test(struct ast_private *ast, u32 datagen, u8 test_ctl) { u32 data, timeout; ast_moutdwm(ast, 0x1e6e0070, 0x00000000); - ast_moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, (datagen << 3) | test_ctl); timeout = 0; do { data = ast_mindwm(ast, 0x1e6e0070) & 0x3000; - if (data & 0x2000) { - return 0; - } + if (data & 0x2000) + return false; if (++timeout > TIMEOUT) { ast_moutdwm(ast, 0x1e6e0070, 0x00000000); - return 0; + return false; } } while (!data); - ast_moutdwm(ast, 0x1e6e0070, 0x00000000); - return 1; + ast_moutdwm(ast, 0x1e6e0070, 0x0); + return true; } -static int mmc_test_burst2(struct ast_private *ast, u32 datagen) +static u32 mmc_test2(struct ast_private *ast, u32 datagen, u8 test_ctl) { u32 data, timeout; ast_moutdwm(ast, 0x1e6e0070, 0x00000000); - ast_moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, (datagen << 3) | test_ctl); timeout = 0; do { data = ast_mindwm(ast, 0x1e6e0070) & 0x1000; if (++timeout > TIMEOUT) { ast_moutdwm(ast, 0x1e6e0070, 0x0); - return -1; + return 0xffffffff; } } while (!data); data = ast_mindwm(ast, 0x1e6e0078); data = (data | (data >> 16)) & 0xffff; - ast_moutdwm(ast, 0x1e6e0070, 0x0); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return data; } -static int mmc_test_single(struct ast_private *ast, u32 datagen) + +static bool mmc_test_burst(struct ast_private *ast, u32 datagen) { - u32 data, timeout; + return mmc_test(ast, datagen, 0xc1); +} - ast_moutdwm(ast, 0x1e6e0070, 0x00000000); - ast_moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3)); - timeout = 0; - do { - data = ast_mindwm(ast, 0x1e6e0070) & 0x3000; - if (data & 0x2000) - return 0; - if (++timeout > TIMEOUT) { - ast_moutdwm(ast, 0x1e6e0070, 0x0); - return 0; - } - } while (!data); - ast_moutdwm(ast, 0x1e6e0070, 0x0); - return 1; +static u32 mmc_test_burst2(struct ast_private *ast, u32 datagen) +{ + return mmc_test2(ast, datagen, 0x41); } -static int mmc_test_single2(struct ast_private *ast, u32 datagen) +static bool mmc_test_single(struct ast_private *ast, u32 datagen) { - u32 data, timeout; + return mmc_test(ast, datagen, 0xc5); +} - ast_moutdwm(ast, 0x1e6e0070, 0x00000000); - ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); - timeout = 0; - do { - data = ast_mindwm(ast, 0x1e6e0070) & 0x1000; - if (++timeout > TIMEOUT) { - ast_moutdwm(ast, 0x1e6e0070, 0x0); - return -1; - } - } while (!data); - data = ast_mindwm(ast, 0x1e6e0078); - data = (data | (data >> 16)) & 0xffff; - ast_moutdwm(ast, 0x1e6e0070, 0x0); - return data; +static u32 mmc_test_single2(struct ast_private *ast, u32 datagen) +{ + return mmc_test2(ast, datagen, 0x05); +} + +static bool mmc_test_single_2500(struct ast_private *ast, u32 datagen) +{ + return mmc_test(ast, datagen, 0x85); } static int cbr_test(struct ast_private *ast) @@ -604,16 +587,16 @@ static u32 cbr_scan2(struct ast_private *ast) return data2; } -static u32 cbr_test3(struct ast_private *ast) +static bool cbr_test3(struct ast_private *ast) { if (!mmc_test_burst(ast, 0)) - return 0; + return false; if (!mmc_test_single(ast, 0)) - return 0; - return 1; + return false; + return true; } -static u32 cbr_scan3(struct ast_private *ast) +static bool cbr_scan3(struct ast_private *ast) { u32 patcnt, loop; @@ -624,9 +607,9 @@ static u32 cbr_scan3(struct ast_private *ast) break; } if (loop == 2) - return 0; + return false; } - return 1; + return true; } static bool finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param) @@ -1612,7 +1595,7 @@ ddr2_init_start: } -static void ast_init_dram_2300(struct drm_device *dev) +static void ast_post_chip_2300(struct drm_device *dev) { struct ast_private *ast = dev->dev_private; struct ast2300_dram_param param; @@ -1638,12 +1621,44 @@ static void ast_init_dram_2300(struct drm_device *dev) temp |= 0x73; ast_write32(ast, 0x12008, temp); + param.dram_freq = 396; param.dram_type = AST_DDR3; + temp = ast_mindwm(ast, 0x1e6e2070); if (temp & 0x01000000) param.dram_type = AST_DDR2; - param.dram_chipid = ast->dram_type; - param.dram_freq = ast->mclk; - param.vram_size = ast->vram_size; + switch (temp & 0x18000000) { + case 0: + param.dram_chipid = AST_DRAM_512Mx16; + break; + default: + case 0x08000000: + param.dram_chipid = AST_DRAM_1Gx16; + break; + case 0x10000000: + param.dram_chipid = AST_DRAM_2Gx16; + break; + case 0x18000000: + param.dram_chipid = AST_DRAM_4Gx16; + break; + } + switch (temp & 0x0c) { + default: + case 0x00: + param.vram_size = AST_VIDMEM_SIZE_8M; + break; + + case 0x04: + param.vram_size = AST_VIDMEM_SIZE_16M; + break; + + case 0x08: + param.vram_size = AST_VIDMEM_SIZE_32M; + break; + + case 0x0c: + param.vram_size = AST_VIDMEM_SIZE_64M; + break; + } if (param.dram_type == AST_DDR3) { get_ddr3_info(ast, ¶m); @@ -1663,3 +1678,404 @@ static void ast_init_dram_2300(struct drm_device *dev) } while ((reg & 0x40) == 0); } +static bool cbr_test_2500(struct ast_private *ast) +{ + ast_moutdwm(ast, 0x1E6E0074, 0x0000FFFF); + ast_moutdwm(ast, 0x1E6E007C, 0xFF00FF00); + if (!mmc_test_burst(ast, 0)) + return false; + if (!mmc_test_single_2500(ast, 0)) + return false; + return true; +} + +static bool ddr_test_2500(struct ast_private *ast) +{ + ast_moutdwm(ast, 0x1E6E0074, 0x0000FFFF); + ast_moutdwm(ast, 0x1E6E007C, 0xFF00FF00); + if (!mmc_test_burst(ast, 0)) + return false; + if (!mmc_test_burst(ast, 1)) + return false; + if (!mmc_test_burst(ast, 2)) + return false; + if (!mmc_test_burst(ast, 3)) + return false; + if (!mmc_test_single_2500(ast, 0)) + return false; + return true; +} + +static void ddr_init_common_2500(struct ast_private *ast) +{ + ast_moutdwm(ast, 0x1E6E0034, 0x00020080); + ast_moutdwm(ast, 0x1E6E0008, 0x2003000F); + ast_moutdwm(ast, 0x1E6E0038, 0x00000FFF); + ast_moutdwm(ast, 0x1E6E0040, 0x88448844); + ast_moutdwm(ast, 0x1E6E0044, 0x24422288); + ast_moutdwm(ast, 0x1E6E0048, 0x22222222); + ast_moutdwm(ast, 0x1E6E004C, 0x22222222); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0208, 0x00000000); + ast_moutdwm(ast, 0x1E6E0218, 0x00000000); + ast_moutdwm(ast, 0x1E6E0220, 0x00000000); + ast_moutdwm(ast, 0x1E6E0228, 0x00000000); + ast_moutdwm(ast, 0x1E6E0230, 0x00000000); + ast_moutdwm(ast, 0x1E6E02A8, 0x00000000); + ast_moutdwm(ast, 0x1E6E02B0, 0x00000000); + ast_moutdwm(ast, 0x1E6E0240, 0x86000000); + ast_moutdwm(ast, 0x1E6E0244, 0x00008600); + ast_moutdwm(ast, 0x1E6E0248, 0x80000000); + ast_moutdwm(ast, 0x1E6E024C, 0x80808080); +} + +static void ddr_phy_init_2500(struct ast_private *ast) +{ + u32 data, pass, timecnt; + + pass = 0; + ast_moutdwm(ast, 0x1E6E0060, 0x00000005); + while (!pass) { + for (timecnt = 0; timecnt < TIMEOUT; timecnt++) { + data = ast_mindwm(ast, 0x1E6E0060) & 0x1; + if (!data) + break; + } + if (timecnt != TIMEOUT) { + data = ast_mindwm(ast, 0x1E6E0300) & 0x000A0000; + if (!data) + pass = 1; + } + if (!pass) { + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + udelay(10); /* delay 10 us */ + ast_moutdwm(ast, 0x1E6E0060, 0x00000005); + } + } + + ast_moutdwm(ast, 0x1E6E0060, 0x00000006); +} + +/* + * Check DRAM Size + * 1Gb : 0x80000000 ~ 0x87FFFFFF + * 2Gb : 0x80000000 ~ 0x8FFFFFFF + * 4Gb : 0x80000000 ~ 0x9FFFFFFF + * 8Gb : 0x80000000 ~ 0xBFFFFFFF + */ +static void check_dram_size_2500(struct ast_private *ast, u32 tRFC) +{ + u32 reg_04, reg_14; + + reg_04 = ast_mindwm(ast, 0x1E6E0004) & 0xfffffffc; + reg_14 = ast_mindwm(ast, 0x1E6E0014) & 0xffffff00; + + ast_moutdwm(ast, 0xA0100000, 0x41424344); + ast_moutdwm(ast, 0x90100000, 0x35363738); + ast_moutdwm(ast, 0x88100000, 0x292A2B2C); + ast_moutdwm(ast, 0x80100000, 0x1D1E1F10); + + /* Check 8Gbit */ + if (ast_mindwm(ast, 0xA0100000) == 0x41424344) { + reg_04 |= 0x03; + reg_14 |= (tRFC >> 24) & 0xFF; + /* Check 4Gbit */ + } else if (ast_mindwm(ast, 0x90100000) == 0x35363738) { + reg_04 |= 0x02; + reg_14 |= (tRFC >> 16) & 0xFF; + /* Check 2Gbit */ + } else if (ast_mindwm(ast, 0x88100000) == 0x292A2B2C) { + reg_04 |= 0x01; + reg_14 |= (tRFC >> 8) & 0xFF; + } else { + reg_14 |= tRFC & 0xFF; + } + ast_moutdwm(ast, 0x1E6E0004, reg_04); + ast_moutdwm(ast, 0x1E6E0014, reg_14); +} + +static void enable_cache_2500(struct ast_private *ast) +{ + u32 reg_04, data; + + reg_04 = ast_mindwm(ast, 0x1E6E0004); + ast_moutdwm(ast, 0x1E6E0004, reg_04 | 0x1000); + + do + data = ast_mindwm(ast, 0x1E6E0004); + while (!(data & 0x80000)); + ast_moutdwm(ast, 0x1E6E0004, reg_04 | 0x400); +} + +static void set_mpll_2500(struct ast_private *ast) +{ + u32 addr, data, param; + + /* Reset MMC */ + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); + ast_moutdwm(ast, 0x1E6E0034, 0x00020080); + for (addr = 0x1e6e0004; addr < 0x1e6e0090;) { + ast_moutdwm(ast, addr, 0x0); + addr += 4; + } + ast_moutdwm(ast, 0x1E6E0034, 0x00020000); + + ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + data = ast_mindwm(ast, 0x1E6E2070) & 0x00800000; + if (data) { + /* CLKIN = 25MHz */ + param = 0x930023E0; + ast_moutdwm(ast, 0x1E6E2160, 0x00011320); + } else { + /* CLKIN = 24MHz */ + param = 0x93002400; + } + ast_moutdwm(ast, 0x1E6E2020, param); + udelay(100); +} + +static void reset_mmc_2500(struct ast_private *ast) +{ + ast_moutdwm(ast, 0x1E78505C, 0x00000004); + ast_moutdwm(ast, 0x1E785044, 0x00000001); + ast_moutdwm(ast, 0x1E785048, 0x00004755); + ast_moutdwm(ast, 0x1E78504C, 0x00000013); + mdelay(100); + ast_moutdwm(ast, 0x1E785054, 0x00000077); + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); +} + +static void ddr3_init_2500(struct ast_private *ast, const u32 *ddr_table) +{ + + ast_moutdwm(ast, 0x1E6E0004, 0x00000303); + ast_moutdwm(ast, 0x1E6E0010, ddr_table[REGIDX_010]); + ast_moutdwm(ast, 0x1E6E0014, ddr_table[REGIDX_014]); + ast_moutdwm(ast, 0x1E6E0018, ddr_table[REGIDX_018]); + ast_moutdwm(ast, 0x1E6E0020, ddr_table[REGIDX_020]); /* MODEREG4/6 */ + ast_moutdwm(ast, 0x1E6E0024, ddr_table[REGIDX_024]); /* MODEREG5 */ + ast_moutdwm(ast, 0x1E6E002C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */ + ast_moutdwm(ast, 0x1E6E0030, ddr_table[REGIDX_030]); /* MODEREG1/3 */ + + /* DDR PHY Setting */ + ast_moutdwm(ast, 0x1E6E0200, 0x02492AAE); + ast_moutdwm(ast, 0x1E6E0204, 0x00001001); + ast_moutdwm(ast, 0x1E6E020C, 0x55E00B0B); + ast_moutdwm(ast, 0x1E6E0210, 0x20000000); + ast_moutdwm(ast, 0x1E6E0214, ddr_table[REGIDX_214]); + ast_moutdwm(ast, 0x1E6E02E0, ddr_table[REGIDX_2E0]); + ast_moutdwm(ast, 0x1E6E02E4, ddr_table[REGIDX_2E4]); + ast_moutdwm(ast, 0x1E6E02E8, ddr_table[REGIDX_2E8]); + ast_moutdwm(ast, 0x1E6E02EC, ddr_table[REGIDX_2EC]); + ast_moutdwm(ast, 0x1E6E02F0, ddr_table[REGIDX_2F0]); + ast_moutdwm(ast, 0x1E6E02F4, ddr_table[REGIDX_2F4]); + ast_moutdwm(ast, 0x1E6E02F8, ddr_table[REGIDX_2F8]); + ast_moutdwm(ast, 0x1E6E0290, 0x00100008); + ast_moutdwm(ast, 0x1E6E02C0, 0x00000006); + + /* Controller Setting */ + ast_moutdwm(ast, 0x1E6E0034, 0x00020091); + + /* Wait DDR PHY init done */ + ddr_phy_init_2500(ast); + + ast_moutdwm(ast, 0x1E6E0120, ddr_table[REGIDX_PLL]); + ast_moutdwm(ast, 0x1E6E000C, 0x42AA5C81); + ast_moutdwm(ast, 0x1E6E0034, 0x0001AF93); + + check_dram_size_2500(ast, ddr_table[REGIDX_RFC]); + enable_cache_2500(ast); + ast_moutdwm(ast, 0x1E6E001C, 0x00000008); + ast_moutdwm(ast, 0x1E6E0038, 0xFFFFFF00); +} + +static void ddr4_init_2500(struct ast_private *ast, const u32 *ddr_table) +{ + u32 data, data2, pass, retrycnt; + u32 ddr_vref, phy_vref; + u32 min_ddr_vref = 0, min_phy_vref = 0; + u32 max_ddr_vref = 0, max_phy_vref = 0; + + ast_moutdwm(ast, 0x1E6E0004, 0x00000313); + ast_moutdwm(ast, 0x1E6E0010, ddr_table[REGIDX_010]); + ast_moutdwm(ast, 0x1E6E0014, ddr_table[REGIDX_014]); + ast_moutdwm(ast, 0x1E6E0018, ddr_table[REGIDX_018]); + ast_moutdwm(ast, 0x1E6E0020, ddr_table[REGIDX_020]); /* MODEREG4/6 */ + ast_moutdwm(ast, 0x1E6E0024, ddr_table[REGIDX_024]); /* MODEREG5 */ + ast_moutdwm(ast, 0x1E6E002C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */ + ast_moutdwm(ast, 0x1E6E0030, ddr_table[REGIDX_030]); /* MODEREG1/3 */ + + /* DDR PHY Setting */ + ast_moutdwm(ast, 0x1E6E0200, 0x42492AAE); + ast_moutdwm(ast, 0x1E6E0204, 0x09002000); + ast_moutdwm(ast, 0x1E6E020C, 0x55E00B0B); + ast_moutdwm(ast, 0x1E6E0210, 0x20000000); + ast_moutdwm(ast, 0x1E6E0214, ddr_table[REGIDX_214]); + ast_moutdwm(ast, 0x1E6E02E0, ddr_table[REGIDX_2E0]); + ast_moutdwm(ast, 0x1E6E02E4, ddr_table[REGIDX_2E4]); + ast_moutdwm(ast, 0x1E6E02E8, ddr_table[REGIDX_2E8]); + ast_moutdwm(ast, 0x1E6E02EC, ddr_table[REGIDX_2EC]); + ast_moutdwm(ast, 0x1E6E02F0, ddr_table[REGIDX_2F0]); + ast_moutdwm(ast, 0x1E6E02F4, ddr_table[REGIDX_2F4]); + ast_moutdwm(ast, 0x1E6E02F8, ddr_table[REGIDX_2F8]); + ast_moutdwm(ast, 0x1E6E0290, 0x00100008); + ast_moutdwm(ast, 0x1E6E02C4, 0x3C183C3C); + ast_moutdwm(ast, 0x1E6E02C8, 0x00631E0E); + + /* Controller Setting */ + ast_moutdwm(ast, 0x1E6E0034, 0x0001A991); + + /* Train PHY Vref first */ + pass = 0; + + for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) { + max_phy_vref = 0x0; + pass = 0; + ast_moutdwm(ast, 0x1E6E02C0, 0x00001C06); + for (phy_vref = 0x40; phy_vref < 0x80; phy_vref++) { + ast_moutdwm(ast, 0x1E6E000C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + ast_moutdwm(ast, 0x1E6E02CC, phy_vref | (phy_vref << 8)); + /* Fire DFI Init */ + ddr_phy_init_2500(ast); + ast_moutdwm(ast, 0x1E6E000C, 0x00005C01); + if (cbr_test_2500(ast)) { + pass++; + data = ast_mindwm(ast, 0x1E6E03D0); + data2 = data >> 8; + data = data & 0xff; + if (data > data2) + data = data2; + if (max_phy_vref < data) { + max_phy_vref = data; + min_phy_vref = phy_vref; + } + } else if (pass > 0) + break; + } + } + ast_moutdwm(ast, 0x1E6E02CC, min_phy_vref | (min_phy_vref << 8)); + + /* Train DDR Vref next */ + pass = 0; + + for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) { + min_ddr_vref = 0xFF; + max_ddr_vref = 0x0; + pass = 0; + for (ddr_vref = 0x00; ddr_vref < 0x40; ddr_vref++) { + ast_moutdwm(ast, 0x1E6E000C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + ast_moutdwm(ast, 0x1E6E02C0, 0x00000006 | (ddr_vref << 8)); + /* Fire DFI Init */ + ddr_phy_init_2500(ast); + ast_moutdwm(ast, 0x1E6E000C, 0x00005C01); + if (cbr_test_2500(ast)) { + pass++; + if (min_ddr_vref > ddr_vref) + min_ddr_vref = ddr_vref; + if (max_ddr_vref < ddr_vref) + max_ddr_vref = ddr_vref; + } else if (pass != 0) + break; + } + } + + ast_moutdwm(ast, 0x1E6E000C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + ddr_vref = (min_ddr_vref + max_ddr_vref + 1) >> 1; + ast_moutdwm(ast, 0x1E6E02C0, 0x00000006 | (ddr_vref << 8)); + + /* Wait DDR PHY init done */ + ddr_phy_init_2500(ast); + + ast_moutdwm(ast, 0x1E6E0120, ddr_table[REGIDX_PLL]); + ast_moutdwm(ast, 0x1E6E000C, 0x42AA5C81); + ast_moutdwm(ast, 0x1E6E0034, 0x0001AF93); + + check_dram_size_2500(ast, ddr_table[REGIDX_RFC]); + enable_cache_2500(ast); + ast_moutdwm(ast, 0x1E6E001C, 0x00000008); + ast_moutdwm(ast, 0x1E6E0038, 0xFFFFFF00); +} + +static bool ast_dram_init_2500(struct ast_private *ast) +{ + u32 data; + u32 max_tries = 5; + + do { + if (max_tries-- == 0) + return false; + set_mpll_2500(ast); + reset_mmc_2500(ast); + ddr_init_common_2500(ast); + + data = ast_mindwm(ast, 0x1E6E2070); + if (data & 0x01000000) + ddr4_init_2500(ast, ast2500_ddr4_1600_timing_table); + else + ddr3_init_2500(ast, ast2500_ddr3_1600_timing_table); + } while (!ddr_test_2500(ast)); + + ast_moutdwm(ast, 0x1E6E2040, ast_mindwm(ast, 0x1E6E2040) | 0x41); + + /* Patch code */ + data = ast_mindwm(ast, 0x1E6E200C) & 0xF9FFFFFF; + ast_moutdwm(ast, 0x1E6E200C, data | 0x10000000); + + return true; +} + +void ast_post_chip_2500(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + u32 temp; + u8 reg; + + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if ((reg & 0x80) == 0) {/* vga only */ + /* Clear bus lock condition */ + ast_moutdwm(ast, 0x1e600000, 0xAEED1A03); + ast_moutdwm(ast, 0x1e600084, 0x00010000); + ast_moutdwm(ast, 0x1e600088, 0x00000000); + ast_moutdwm(ast, 0x1e6e2000, 0x1688A8A8); + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x12000, 0x1688a8a8); + while (ast_read32(ast, 0x12000) != 0x1) + ; + + ast_write32(ast, 0x10000, 0xfc600309); + while (ast_read32(ast, 0x10000) != 0x1) + ; + + /* Slow down CPU/AHB CLK in VGA only mode */ + temp = ast_read32(ast, 0x12008); + temp |= 0x73; + ast_write32(ast, 0x12008, temp); + + /* Reset USB port to patch USB unknown device issue */ + ast_moutdwm(ast, 0x1e6e2090, 0x20000000); + temp = ast_mindwm(ast, 0x1e6e2094); + temp |= 0x00004000; + ast_moutdwm(ast, 0x1e6e2094, temp); + temp = ast_mindwm(ast, 0x1e6e2070); + if (temp & 0x00800000) { + ast_moutdwm(ast, 0x1e6e207c, 0x00800000); + mdelay(100); + ast_moutdwm(ast, 0x1e6e2070, 0x00800000); + } + + if (!ast_dram_init_2500(ast)) + DRM_ERROR("DRAM init failed !\n"); + + temp = ast_mindwm(ast, 0x1e6e2040); + ast_moutdwm(ast, 0x1e6e2040, temp | 0x40); + } + + /* wait ready */ + do { + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + } while ((reg & 0x40) == 0); +} diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h index 3608d5aa7451..5f4c2e833a65 100644 --- a/drivers/gpu/drm/ast/ast_tables.h +++ b/drivers/gpu/drm/ast/ast_tables.h @@ -47,6 +47,7 @@ #define SyncPN (PVSync | NHSync) #define SyncNP (NVSync | PHSync) #define SyncNN (NVSync | NHSync) +#define AST2500PreCatchCRT 0x00004000 /* DCLK Index */ #define VCLK25_175 0x00 @@ -78,37 +79,67 @@ #define VCLK97_75 0x19 #define VCLK118_25 0x1A -static struct ast_vbios_dclk_info dclk_table[] = { - {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ - {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ - {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ - {0x76, 0x63, 0x01}, /* 03: VCLK36 */ - {0xEE, 0x67, 0x01}, /* 04: VCLK40 */ - {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ - {0xC6, 0x64, 0x01}, /* 06: VCLK50 */ - {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ - {0x80, 0x64, 0x00}, /* 08: VCLK65 */ - {0x7B, 0x63, 0x00}, /* 09: VCLK75 */ - {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */ - {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */ - {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */ - {0x85, 0x24, 0x00}, /* 0D: VCLK135 */ - {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */ - {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */ - {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ - {0xa7, 0x78, 0x80}, /* 11: VCLK83.5 */ - {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ - {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ - {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ - {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ - {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ - {0x77, 0x58, 0x80}, /* 17: VCLK119 */ - {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ - {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ - {0x3b, 0x2c, 0x81}, /* 1A: VCLK118_25 */ +static const struct ast_vbios_dclk_info dclk_table[] = { + {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ + {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ + {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ + {0x76, 0x63, 0x01}, /* 03: VCLK36 */ + {0xEE, 0x67, 0x01}, /* 04: VCLK40 */ + {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ + {0xC6, 0x64, 0x01}, /* 06: VCLK50 */ + {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ + {0x80, 0x64, 0x00}, /* 08: VCLK65 */ + {0x7B, 0x63, 0x00}, /* 09: VCLK75 */ + {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */ + {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */ + {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */ + {0x85, 0x24, 0x00}, /* 0D: VCLK135 */ + {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */ + {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */ + {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ + {0xa7, 0x78, 0x80}, /* 11: VCLK83.5 */ + {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ + {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ + {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ + {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ + {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ + {0x77, 0x58, 0x80}, /* 17: VCLK119 */ + {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ + {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ + {0x3b, 0x2c, 0x81}, /* 1A: VCLK118_25 */ }; -static struct ast_vbios_stdtable vbios_stdtable[] = { +static const struct ast_vbios_dclk_info dclk_table_ast2500[] = { + {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ + {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ + {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ + {0x76, 0x63, 0x01}, /* 03: VCLK36 */ + {0xEE, 0x67, 0x01}, /* 04: VCLK40 */ + {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ + {0xC6, 0x64, 0x01}, /* 06: VCLK50 */ + {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ + {0x80, 0x64, 0x00}, /* 08: VCLK65 */ + {0x7B, 0x63, 0x00}, /* 09: VCLK75 */ + {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */ + {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */ + {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */ + {0x85, 0x24, 0x00}, /* 0D: VCLK135 */ + {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */ + {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */ + {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ + {0xa7, 0x78, 0x80}, /* 11: VCLK83.5 */ + {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ + {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ + {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ + {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ + {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ + {0x58, 0x01, 0x42}, /* 17: VCLK119 */ + {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ + {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ + {0x44, 0x20, 0x43}, /* 1A: VCLK118_25 */ +}; + +static const struct ast_vbios_stdtable vbios_stdtable[] = { /* MD_2_3_400 */ { 0x67, @@ -181,21 +212,21 @@ static struct ast_vbios_stdtable vbios_stdtable[] = { }, }; -static struct ast_vbios_enhtable res_640x480[] = { +static const struct ast_vbios_enhtable res_640x480[] = { { 800, 640, 8, 96, 525, 480, 2, 2, VCLK25_175, /* 60Hz */ (SyncNN | HBorder | VBorder | Charx8Dot), 60, 1, 0x2E }, { 832, 640, 16, 40, 520, 480, 1, 3, VCLK31_5, /* 72Hz */ (SyncNN | HBorder | VBorder | Charx8Dot), 72, 2, 0x2E }, { 840, 640, 16, 64, 500, 480, 1, 3, VCLK31_5, /* 75Hz */ (SyncNN | Charx8Dot) , 75, 3, 0x2E }, - { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85Hz */ + { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85Hz */ (SyncNN | Charx8Dot) , 85, 4, 0x2E }, - { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* end */ + { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* end */ (SyncNN | Charx8Dot) , 0xFF, 4, 0x2E }, }; -static struct ast_vbios_enhtable res_800x600[] = { - {1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56Hz */ +static const struct ast_vbios_enhtable res_800x600[] = { + {1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56Hz */ (SyncPP | Charx8Dot), 56, 1, 0x30 }, {1056, 800, 40, 128, 628, 600, 1, 4, VCLK40, /* 60Hz */ (SyncPP | Charx8Dot), 60, 2, 0x30 }, @@ -210,7 +241,7 @@ static struct ast_vbios_enhtable res_800x600[] = { }; -static struct ast_vbios_enhtable res_1024x768[] = { +static const struct ast_vbios_enhtable res_1024x768[] = { {1344, 1024, 24, 136, 806, 768, 3, 6, VCLK65, /* 60Hz */ (SyncNN | Charx8Dot), 60, 1, 0x31 }, {1328, 1024, 24, 136, 806, 768, 3, 6, VCLK75, /* 70Hz */ @@ -223,7 +254,7 @@ static struct ast_vbios_enhtable res_1024x768[] = { (SyncPP | Charx8Dot), 0xFF, 4, 0x31 }, }; -static struct ast_vbios_enhtable res_1280x1024[] = { +static const struct ast_vbios_enhtable res_1280x1024[] = { {1688, 1280, 48, 112, 1066, 1024, 1, 3, VCLK108, /* 60Hz */ (SyncPP | Charx8Dot), 60, 1, 0x32 }, {1688, 1280, 16, 144, 1066, 1024, 1, 3, VCLK135, /* 75Hz */ @@ -234,7 +265,7 @@ static struct ast_vbios_enhtable res_1280x1024[] = { (SyncPP | Charx8Dot), 0xFF, 3, 0x32 }, }; -static struct ast_vbios_enhtable res_1600x1200[] = { +static const struct ast_vbios_enhtable res_1600x1200[] = { {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* 60Hz */ (SyncPP | Charx8Dot), 60, 1, 0x33 }, {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* end */ @@ -242,34 +273,39 @@ static struct ast_vbios_enhtable res_1600x1200[] = { }; /* 16:9 */ -static struct ast_vbios_enhtable res_1360x768[] = { - {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* 60Hz */ +static const struct ast_vbios_enhtable res_1360x768[] = { + {1792, 1360, 64, 112, 795, 768, 3, 6, VCLK85_5, /* 60Hz */ (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 }, - {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* end */ - (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x39 }, + {1792, 1360, 64, 112, 795, 768, 3, 6, VCLK85_5, /* end */ + (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 0xFF, 1, 0x39 }, }; -static struct ast_vbios_enhtable res_1600x900[] = { - {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60Hz CVT RB */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x3A }, - {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ +static const struct ast_vbios_enhtable res_1600x900[] = { + {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60Hz CVT RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x3A }, + {2112, 1600, 88, 168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x3A }, - {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ + {2112, 1600, 88, 168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x3A }, }; -static struct ast_vbios_enhtable res_1920x1080[] = { +static const struct ast_vbios_enhtable res_1920x1080[] = { {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x38 }, + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x38 }, {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x38 }, + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 0xFF, 1, 0x38 }, }; /* 16:10 */ -static struct ast_vbios_enhtable res_1280x800[] = { - {1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60Hz RB */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 }, +static const struct ast_vbios_enhtable res_1280x800[] = { + {1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x35 }, {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x35 }, {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ @@ -277,29 +313,33 @@ static struct ast_vbios_enhtable res_1280x800[] = { }; -static struct ast_vbios_enhtable res_1440x900[] = { +static const struct ast_vbios_enhtable res_1440x900[] = { {1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60Hz RB */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 }, + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x36 }, {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x36 }, {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x36 }, }; -static struct ast_vbios_enhtable res_1680x1050[] = { - {1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 }, +static const struct ast_vbios_enhtable res_1680x1050[] = { + {1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x37 }, {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x37 }, {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x37 }, }; -static struct ast_vbios_enhtable res_1920x1200[] = { - {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB*/ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x34 }, - {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x34 }, +static const struct ast_vbios_enhtable res_1920x1200[] = { + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB*/ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x34 }, + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 0xFF, 1, 0x34 }, }; #endif diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c index 2a1368fac1d1..50c910efa13d 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -236,8 +236,6 @@ struct ttm_bo_driver ast_bo_driver = { .verify_access = ast_bo_verify_access, .io_mem_reserve = &ast_ttm_io_mem_reserve, .io_mem_free = &ast_ttm_io_mem_free, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int ast_mm_init(struct ast_private *ast) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index cbd0070265c9..427bdff425c2 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -431,15 +431,8 @@ static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) { struct atmel_hlcdc_dc *dc = dev->dev_private; - if (dc->fbdev) { + if (dc->fbdev) drm_fbdev_cma_hotplug_event(dc->fbdev); - } else { - dc->fbdev = drm_fbdev_cma_init(dev, 24, - dev->mode_config.num_crtc, - dev->mode_config.num_connector); - if (IS_ERR(dc->fbdev)) - dc->fbdev = NULL; - } } struct atmel_hlcdc_dc_commit { @@ -653,10 +646,12 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) platform_set_drvdata(pdev, dev); - drm_kms_helper_poll_init(dev); + dc->fbdev = drm_fbdev_cma_init(dev, 24, + dev->mode_config.num_connector); + if (IS_ERR(dc->fbdev)) + dc->fbdev = NULL; - /* force connectors detection */ - drm_helper_hpd_irq_event(dev); + drm_kms_helper_poll_init(dev); return 0; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c index 377e43cea9dd..63dfdbf34f80 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c @@ -446,7 +446,7 @@ void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer, return; if (fb) - nplanes = drm_format_num_planes(fb->pixel_format); + nplanes = fb->format->num_planes; if (nplanes > layer->max_planes) return; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 6119b5085501..e7799b6ee829 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -230,9 +230,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, of_node_put(np); if (bridge) { - output->encoder.bridge = bridge; - bridge->encoder = &output->encoder; - ret = drm_bridge_attach(dev, bridge); + ret = drm_bridge_attach(&output->encoder, bridge, NULL); if (!ret) return 0; } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 246ed1e33d8a..bd2791c4b002 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -356,7 +356,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | ATMEL_HLCDC_LAYER_ITER; - if (atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)) + if (atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format)) cfg |= ATMEL_HLCDC_LAYER_LAEN; else cfg |= ATMEL_HLCDC_LAYER_GAEN | @@ -386,13 +386,13 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, u32 cfg; int ret; - ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->pixel_format, + ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format, &cfg); if (ret) return; - if ((state->base.fb->pixel_format == DRM_FORMAT_YUV422 || - state->base.fb->pixel_format == DRM_FORMAT_NV61) && + if ((state->base.fb->format->format == DRM_FORMAT_YUV422 || + state->base.fb->format->format == DRM_FORMAT_NV61) && drm_rotation_90_or_270(state->base.rotation)) cfg |= ATMEL_HLCDC_YUV422ROT; @@ -405,7 +405,7 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, * Rotation optimization is not working on RGB888 (rotation is still * working but without any optimization). */ - if (state->base.fb->pixel_format == DRM_FORMAT_RGB888) + if (state->base.fb->format->format == DRM_FORMAT_RGB888) cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS; else cfg = 0; @@ -514,7 +514,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state) ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s); if (!ovl_s->fb || - atmel_hlcdc_format_embeds_alpha(ovl_s->fb->pixel_format) || + atmel_hlcdc_format_embeds_alpha(ovl_s->fb->format->format) || ovl_state->alpha != 255) continue; @@ -621,7 +621,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, state->src_w >>= 16; state->src_h >>= 16; - state->nplanes = drm_format_num_planes(fb->pixel_format); + state->nplanes = fb->format->num_planes; if (state->nplanes > ATMEL_HLCDC_MAX_PLANES) return -EINVAL; @@ -664,15 +664,15 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h, state->crtc_h); - hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); - vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); + hsub = drm_format_horz_chroma_subsampling(fb->format->format); + vsub = drm_format_vert_chroma_subsampling(fb->format->format); for (i = 0; i < state->nplanes; i++) { unsigned int offset = 0; int xdiv = i ? hsub : 1; int ydiv = i ? vsub : 1; - state->bpp[i] = drm_format_plane_cpp(fb->pixel_format, i); + state->bpp[i] = fb->format->cpp[i]; if (!state->bpp[i]) return -EINVAL; @@ -741,7 +741,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) && (!layout->memsize || - atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format))) + atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format))) return -EINVAL; if (state->crtc_x < 0 || state->crtc_y < 0) diff --git a/drivers/gpu/drm/bochs/Kconfig b/drivers/gpu/drm/bochs/Kconfig index f739763f47ce..bd2718015cdb 100644 --- a/drivers/gpu/drm/bochs/Kconfig +++ b/drivers/gpu/drm/bochs/Kconfig @@ -1,6 +1,6 @@ config DRM_BOCHS tristate "DRM Support for bochs dispi vga interface (qemu stdvga)" - depends on DRM && PCI + depends on DRM && PCI && MMU select DRM_KMS_HELPER select DRM_TTM help diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h index 32dfe418cc98..f626bab7f5e3 100644 --- a/drivers/gpu/drm/bochs/bochs.h +++ b/drivers/gpu/drm/bochs/bochs.h @@ -4,6 +4,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_helper.h> #include <drm/drm_gem.h> diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index 15a293e65b31..aa342515ddf4 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -12,6 +12,10 @@ #include "bochs.h" +static int bochs_modeset = -1; +module_param_named(modeset, bochs_modeset, int, 0444); +MODULE_PARM_DESC(modeset, "enable/disable kernel modesetting"); + static bool enable_fbdev = true; module_param_named(fbdev, enable_fbdev, bool, 0444); MODULE_PARM_DESC(fbdev, "register fbdev device"); @@ -19,7 +23,7 @@ MODULE_PARM_DESC(fbdev, "register fbdev device"); /* ---------------------------------------------------------------------- */ /* drm interface */ -static int bochs_unload(struct drm_device *dev) +static void bochs_unload(struct drm_device *dev) { struct bochs_device *bochs = dev->dev_private; @@ -29,7 +33,6 @@ static int bochs_unload(struct drm_device *dev) bochs_hw_fini(dev); kfree(bochs); dev->dev_private = NULL; - return 0; } static int bochs_load(struct drm_device *dev, unsigned long flags) @@ -215,6 +218,12 @@ static struct pci_driver bochs_pci_driver = { static int __init bochs_init(void) { + if (vgacon_text_force() && bochs_modeset == -1) + return -EINVAL; + + if (bochs_modeset == 0) + return -EINVAL; + return drm_pci_init(&bochs_driver, &bochs_pci_driver); } diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index da790a1c302a..932a769637ef 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -123,7 +123,7 @@ static int bochsfb_create(struct drm_fb_helper *helper, info->flags = FBINFO_DEFAULT; info->fbops = &bochsfb_ops; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &bochs->fb.helper, sizes->fb_width, sizes->fb_height); @@ -169,8 +169,7 @@ int bochs_fbdev_init(struct bochs_device *bochs) drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper, &bochs_fb_helper_funcs); - ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, - 1, 1); + ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, 1); if (ret) return ret; diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c index 099a3c688c26..857755ac2d70 100644 --- a/drivers/gpu/drm/bochs/bochs_mm.c +++ b/drivers/gpu/drm/bochs/bochs_mm.c @@ -205,8 +205,6 @@ struct ttm_bo_driver bochs_bo_driver = { .verify_access = bochs_bo_verify_access, .io_mem_reserve = &bochs_ttm_io_mem_reserve, .io_mem_free = &bochs_ttm_io_mem_free, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int bochs_mm_init(struct bochs_device *bochs) @@ -484,7 +482,7 @@ int bochs_framebuffer_init(struct drm_device *dev, { int ret; - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &gfb->base, mode_cmd); gfb->obj = obj; ret = drm_framebuffer_init(dev, &gfb->base, &bochs_fb_funcs); if (ret) { diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 992d76ce02bb..fe18a5d2d84b 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -12,6 +12,7 @@ #include <linux/hdmi.h> #include <linux/i2c.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_mipi_dsi.h> @@ -317,6 +318,8 @@ struct adv7511 { bool edid_read; wait_queue_head_t wq; + struct work_struct hpd_work; + struct drm_bridge bridge; struct drm_connector connector; @@ -329,6 +332,9 @@ struct adv7511 { struct gpio_desc *gpio_pd; + struct regulator_bulk_data *supplies; + unsigned int num_supplies; + /* ADV7533 DSI RX related params */ struct device_node *host_node; struct mipi_dsi_device *dsi; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 8dba729f6ef9..f75ab6278113 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -325,7 +325,7 @@ static void adv7511_set_link_config(struct adv7511 *adv7511, adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; } -static void adv7511_power_on(struct adv7511 *adv7511) +static void __adv7511_power_on(struct adv7511 *adv7511) { adv7511->current_edid_segment = -1; @@ -338,7 +338,7 @@ static void adv7511_power_on(struct adv7511 *adv7511) * Still, let's be safe and stick to the documentation. */ regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0), - ADV7511_INT0_EDID_READY); + ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD); regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1), ADV7511_INT1_DDC_ERROR); } @@ -354,6 +354,11 @@ static void adv7511_power_on(struct adv7511 *adv7511) regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, ADV7511_REG_POWER2_HPD_SRC_MASK, ADV7511_REG_POWER2_HPD_SRC_NONE); +} + +static void adv7511_power_on(struct adv7511 *adv7511) +{ + __adv7511_power_on(adv7511); /* * Most of the registers are reset during power down or when HPD is low. @@ -362,21 +367,23 @@ static void adv7511_power_on(struct adv7511 *adv7511) if (adv7511->type == ADV7533) adv7533_dsi_power_on(adv7511); - adv7511->powered = true; } -static void adv7511_power_off(struct adv7511 *adv7511) +static void __adv7511_power_off(struct adv7511 *adv7511) { /* TODO: setup additional power down modes */ regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN); regcache_mark_dirty(adv7511->regmap); +} +static void adv7511_power_off(struct adv7511 *adv7511) +{ + __adv7511_power_off(adv7511); if (adv7511->type == ADV7533) adv7533_dsi_power_off(adv7511); - adv7511->powered = false; } @@ -402,6 +409,27 @@ static bool adv7511_hpd(struct adv7511 *adv7511) return false; } +static void adv7511_hpd_work(struct work_struct *work) +{ + struct adv7511 *adv7511 = container_of(work, struct adv7511, hpd_work); + enum drm_connector_status status; + unsigned int val; + int ret; + + ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val); + if (ret < 0) + status = connector_status_disconnected; + else if (val & ADV7511_STATUS_HPD) + status = connector_status_connected; + else + status = connector_status_disconnected; + + if (adv7511->connector.status != status) { + adv7511->connector.status = status; + drm_kms_helper_hotplug_event(adv7511->connector.dev); + } +} + static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) { unsigned int irq0, irq1; @@ -419,7 +447,7 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) - drm_helper_hpd_irq_event(adv7511->connector.dev); + schedule_work(&adv7511->hpd_work); if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { adv7511->edid_read = true; @@ -546,23 +574,20 @@ static int adv7511_get_modes(struct adv7511 *adv7511, /* Reading the EDID only works if the device is powered */ if (!adv7511->powered) { - regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, - ADV7511_POWER_POWER_DOWN, 0); - if (adv7511->i2c_main->irq) { - regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0), - ADV7511_INT0_EDID_READY); - regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1), - ADV7511_INT1_DDC_ERROR); - } - adv7511->current_edid_segment = -1; + unsigned int edid_i2c_addr = + (adv7511->i2c_main->addr << 1) + 4; + + __adv7511_power_on(adv7511); + + /* Reset the EDID_I2C_ADDR register as it might be cleared */ + regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, + edid_i2c_addr); } edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511); if (!adv7511->powered) - regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, - ADV7511_POWER_POWER_DOWN, - ADV7511_POWER_POWER_DOWN); + __adv7511_power_off(adv7511); kfree(adv7511->edid); adv7511->edid = edid; @@ -825,6 +850,10 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge) if (adv->type == ADV7533) ret = adv7533_attach_dsi(adv); + if (adv->i2c_main->irq) + regmap_write(adv->regmap, ADV7511_REG_INT_ENABLE(0), + ADV7511_INT0_HPD); + return ret; } @@ -839,6 +868,58 @@ static struct drm_bridge_funcs adv7511_bridge_funcs = { * Probe & remove */ +static const char * const adv7511_supply_names[] = { + "avdd", + "dvdd", + "pvdd", + "bgvdd", + "dvdd-3v", +}; + +static const char * const adv7533_supply_names[] = { + "avdd", + "dvdd", + "pvdd", + "a2vdd", + "v3p3", + "v1p2", +}; + +static int adv7511_init_regulators(struct adv7511 *adv) +{ + struct device *dev = &adv->i2c_main->dev; + const char * const *supply_names; + unsigned int i; + int ret; + + if (adv->type == ADV7511) { + supply_names = adv7511_supply_names; + adv->num_supplies = ARRAY_SIZE(adv7511_supply_names); + } else { + supply_names = adv7533_supply_names; + adv->num_supplies = ARRAY_SIZE(adv7533_supply_names); + } + + adv->supplies = devm_kcalloc(dev, adv->num_supplies, + sizeof(*adv->supplies), GFP_KERNEL); + if (!adv->supplies) + return -ENOMEM; + + for (i = 0; i < adv->num_supplies; i++) + adv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, adv->num_supplies, adv->supplies); + if (ret) + return ret; + + return regulator_bulk_enable(adv->num_supplies, adv->supplies); +} + +static void adv7511_uninit_regulators(struct adv7511 *adv) +{ + regulator_bulk_disable(adv->num_supplies, adv->supplies); +} + static int adv7511_parse_dt(struct device_node *np, struct adv7511_link_config *config) { @@ -939,6 +1020,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (!adv7511) return -ENOMEM; + adv7511->i2c_main = i2c; adv7511->powered = false; adv7511->status = connector_status_disconnected; @@ -956,13 +1038,21 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (ret) return ret; + ret = adv7511_init_regulators(adv7511); + if (ret) { + dev_err(dev, "failed to init regulators\n"); + return ret; + } + /* * The power down GPIO is optional. If present, toggle it from active to * inactive to wake up the encoder. */ adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH); - if (IS_ERR(adv7511->gpio_pd)) - return PTR_ERR(adv7511->gpio_pd); + if (IS_ERR(adv7511->gpio_pd)) { + ret = PTR_ERR(adv7511->gpio_pd); + goto uninit_regulators; + } if (adv7511->gpio_pd) { mdelay(5); @@ -970,12 +1060,14 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) } adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config); - if (IS_ERR(adv7511->regmap)) - return PTR_ERR(adv7511->regmap); + if (IS_ERR(adv7511->regmap)) { + ret = PTR_ERR(adv7511->regmap); + goto uninit_regulators; + } ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val); if (ret) - return ret; + goto uninit_regulators; dev_dbg(dev, "Rev. %d\n", val); if (adv7511->type == ADV7511) @@ -985,7 +1077,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) else ret = adv7533_patch_registers(adv7511); if (ret) - return ret; + goto uninit_regulators; regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr); regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR, @@ -995,10 +1087,11 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) adv7511_packet_disable(adv7511, 0xffff); - adv7511->i2c_main = i2c; adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1); - if (!adv7511->i2c_edid) - return -ENOMEM; + if (!adv7511->i2c_edid) { + ret = -ENOMEM; + goto uninit_regulators; + } if (adv7511->type == ADV7533) { ret = adv7533_init_cec(adv7511); @@ -1006,6 +1099,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) goto err_i2c_unregister_edid; } + INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work); + if (i2c->irq) { init_waitqueue_head(&adv7511->wq); @@ -1045,6 +1140,8 @@ err_unregister_cec: adv7533_uninit_cec(adv7511); err_i2c_unregister_edid: i2c_unregister_device(adv7511->i2c_edid); +uninit_regulators: + adv7511_uninit_regulators(adv7511); return ret; } @@ -1058,6 +1155,8 @@ static int adv7511_remove(struct i2c_client *i2c) adv7533_uninit_cec(adv7511); } + adv7511_uninit_regulators(adv7511); + drm_bridge_remove(&adv7511->bridge); adv7511_audio_exit(adv7511); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 18eefdcbf1ba..e7cd1056ff2d 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -133,6 +133,7 @@ int analogix_dp_disable_psr(struct device *dev) { struct analogix_dp_device *dp = dev_get_drvdata(dev); struct edp_vsc_psr psr_vsc; + int ret; if (!dp->psr_support) return 0; @@ -147,6 +148,10 @@ int analogix_dp_disable_psr(struct device *dev) psr_vsc.DB0 = 0; psr_vsc.DB1 = 0; + ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D0); + if (ret != 1) + dev_err(dp->dev, "Failed to set DP Power0 %d\n", ret); + analogix_dp_send_psr_spd(dp, &psr_vsc); return 0; } @@ -1227,12 +1232,10 @@ static int analogix_dp_create_bridge(struct drm_device *drm_dev, dp->bridge = bridge; - dp->encoder->bridge = bridge; bridge->driver_private = dp; - bridge->encoder = dp->encoder; bridge->funcs = &analogix_dp_bridge_funcs; - ret = drm_bridge_attach(drm_dev, bridge); + ret = drm_bridge_attach(dp->encoder, bridge, NULL); if (ret) { DRM_ERROR("failed to attach drm bridge\n"); return -EINVAL; diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index e5706981c934..86e9f9c7b59c 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -237,6 +237,7 @@ static int dumb_vga_remove(struct platform_device *pdev) static const struct of_device_id dumb_vga_match[] = { { .compatible = "dumb-vga-dac" }, + { .compatible = "ti,ths8135" }, {}, }; MODULE_DEVICE_TABLE(of, dumb_vga_match); diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index 235ce7d1583d..9a9ec27d9e28 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -113,13 +113,20 @@ struct dw_hdmi_i2c { bool is_regaddr; }; +struct dw_hdmi_phy_data { + enum dw_hdmi_phy_type type; + const char *name; + bool has_svsret; +}; + struct dw_hdmi { struct drm_connector connector; - struct drm_encoder *encoder; - struct drm_bridge *bridge; + struct drm_bridge bridge; - struct platform_device *audio; enum dw_hdmi_devtype dev_type; + unsigned int version; + + struct platform_device *audio; struct device *dev; struct clk *isfr_clk; struct clk *iahb_clk; @@ -133,7 +140,9 @@ struct dw_hdmi { u8 edid[HDMI_EDID_LEN]; bool cable_plugin; + const struct dw_hdmi_phy_data *phy; bool phy_enabled; + struct drm_display_mode previous_mode; struct i2c_adapter *ddc; @@ -868,7 +877,7 @@ static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec) return true; } -static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, +static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, unsigned char addr) { hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); @@ -882,13 +891,6 @@ static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, hdmi_phy_wait_i2c_done(hdmi, 1000); } -static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data, - unsigned char addr) -{ - __hdmi_phy_i2c_write(hdmi, data, addr); - return 0; -} - static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable) { hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0, @@ -903,11 +905,11 @@ static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_ENTMDS_MASK); } -static void dw_hdmi_phy_enable_spare(struct dw_hdmi *hdmi, u8 enable) +static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable) { hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0, - HDMI_PHY_CONF0_SPARECTRL_OFFSET, - HDMI_PHY_CONF0_SPARECTRL_MASK); + HDMI_PHY_CONF0_SVSRET_OFFSET, + HDMI_PHY_CONF0_SVSRET_MASK); } static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable) @@ -938,34 +940,14 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_SELDIPIF_MASK); } -static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, - unsigned char res, int cscon) +static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon) { - unsigned res_idx; u8 val, msec; const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; - if (prep) - return -EINVAL; - - switch (res) { - case 0: /* color resolution 0 is 8 bit colour depth */ - case 8: - res_idx = DW_HDMI_RES_8; - break; - case 10: - res_idx = DW_HDMI_RES_10; - break; - case 12: - res_idx = DW_HDMI_RES_12; - break; - default: - return -EINVAL; - } - /* PLL/MPLL Cfg - always match on final entry */ for (; mpll_config->mpixelclock != ~0UL; mpll_config++) if (hdmi->hdmi_data.video_mode.mpixelclock <= @@ -1004,9 +986,13 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, /* gen2 pddq */ dw_hdmi_phy_gen2_pddq(hdmi, 1); - /* PHY reset */ - hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); - hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); + /* Leave low power consumption mode by asserting SVSRET. */ + if (hdmi->phy->has_svsret) + dw_hdmi_phy_enable_svsret(hdmi, 1); + + /* PHY reset. The reset signal is active high on Gen2 PHYs. */ + hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ); + hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ); hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); @@ -1015,21 +1001,26 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, HDMI_PHY_I2CM_SLAVE_ADDR); hdmi_phy_test_clear(hdmi, 0); - hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06); - hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15); - - /* CURRCTRL */ - hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10); + hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce, + HDMI_3D_TX_PHY_CPCE_CTRL); + hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, + HDMI_3D_TX_PHY_GMPCTRL); + hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], + HDMI_3D_TX_PHY_CURRCTRL); - hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ - hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); + hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL); + hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK, + HDMI_3D_TX_PHY_MSM_CTRL); - hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19); /* TXTERM */ - hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */ - hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */ + hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM); + hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, + HDMI_3D_TX_PHY_CKSYMTXCTRL); + hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, + HDMI_3D_TX_PHY_VLEVCTRL); - /* REMOVE CLK TERM */ - hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ + /* Override and disable clock termination. */ + hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE, + HDMI_3D_TX_PHY_CKCALCTRL); dw_hdmi_phy_enable_powerdown(hdmi, false); @@ -1041,10 +1032,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, dw_hdmi_phy_gen2_txpwron(hdmi, 1); dw_hdmi_phy_gen2_pddq(hdmi, 0); - if (hdmi->dev_type == RK3288_HDMI) - dw_hdmi_phy_enable_spare(hdmi, 1); - - /*Wait for PHY PLL lock */ + /* Wait for PHY PLL lock */ msec = 5; do { val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; @@ -1079,7 +1067,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) dw_hdmi_phy_enable_powerdown(hdmi, true); /* Enable CSC */ - ret = hdmi_phy_configure(hdmi, 0, 8, cscon); + ret = hdmi_phy_configure(hdmi, cscon); if (ret) return ret; } @@ -1351,19 +1339,38 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) /* Workaround to clear the overflow condition */ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) { - int count; + unsigned int count; + unsigned int i; u8 val; - /* TMDS software reset */ - hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); + /* + * Under some circumstances the Frame Composer arithmetic unit can miss + * an FC register write due to being busy processing the previous one. + * The issue can be worked around by issuing a TMDS software reset and + * then write one of the FC registers several times. + * + * The number of iterations matters and depends on the HDMI TX revision + * (and possibly on the platform). So far only i.MX6Q (v1.30a) and + * i.MX6DL (v1.31a) have been identified as needing the workaround, with + * 4 and 1 iterations respectively. + */ - val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); - if (hdmi->dev_type == IMX6DL_HDMI) { - hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); + switch (hdmi->version) { + case 0x130a: + count = 4; + break; + case 0x131a: + count = 1; + break; + default: return; } - for (count = 0; count < 4; count++) + /* TMDS software reset */ + hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); + + val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); + for (i = 0; i < count; i++) hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); } @@ -1586,42 +1593,6 @@ static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi) hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); } -static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, - struct drm_display_mode *orig_mode, - struct drm_display_mode *mode) -{ - struct dw_hdmi *hdmi = bridge->driver_private; - - mutex_lock(&hdmi->mutex); - - /* Store the display mode for plugin/DKMS poweron events */ - memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); - - mutex_unlock(&hdmi->mutex); -} - -static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) -{ - struct dw_hdmi *hdmi = bridge->driver_private; - - mutex_lock(&hdmi->mutex); - hdmi->disabled = true; - dw_hdmi_update_power(hdmi); - dw_hdmi_update_phy_mask(hdmi); - mutex_unlock(&hdmi->mutex); -} - -static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) -{ - struct dw_hdmi *hdmi = bridge->driver_private; - - mutex_lock(&hdmi->mutex); - hdmi->disabled = false; - dw_hdmi_update_power(hdmi); - dw_hdmi_update_phy_mask(hdmi); - mutex_unlock(&hdmi->mutex); -} - static enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector, bool force) { @@ -1714,7 +1685,63 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = .best_encoder = drm_atomic_helper_best_encoder, }; +static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + struct drm_encoder *encoder = bridge->encoder; + struct drm_connector *connector = &hdmi->connector; + + connector->interlace_allowed = 1; + connector->polled = DRM_CONNECTOR_POLL_HPD; + + drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); + + drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; +} + +static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *orig_mode, + struct drm_display_mode *mode) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + mutex_lock(&hdmi->mutex); + + /* Store the display mode for plugin/DKMS poweron events */ + memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode)); + + mutex_unlock(&hdmi->mutex); +} + +static void dw_hdmi_bridge_disable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + mutex_lock(&hdmi->mutex); + hdmi->disabled = true; + dw_hdmi_update_power(hdmi); + dw_hdmi_update_phy_mask(hdmi); + mutex_unlock(&hdmi->mutex); +} + +static void dw_hdmi_bridge_enable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + mutex_lock(&hdmi->mutex); + hdmi->disabled = false; + dw_hdmi_update_power(hdmi); + dw_hdmi_update_phy_mask(hdmi); + mutex_unlock(&hdmi->mutex); +} + static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { + .attach = dw_hdmi_bridge_attach, .enable = dw_hdmi_bridge_enable, .disable = dw_hdmi_bridge_disable, .mode_set = dw_hdmi_bridge_mode_set, @@ -1816,7 +1843,8 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { dev_dbg(hdmi->dev, "EVENT=%s\n", phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout"); - drm_helper_hpd_irq_event(hdmi->bridge->dev); + if (hdmi->bridge.dev) + drm_helper_hpd_irq_event(hdmi->bridge.dev); } hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); @@ -1826,68 +1854,80 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi) -{ - struct drm_encoder *encoder = hdmi->encoder; - struct drm_bridge *bridge; - int ret; - - bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) { - DRM_ERROR("Failed to allocate drm bridge\n"); - return -ENOMEM; - } - - hdmi->bridge = bridge; - bridge->driver_private = hdmi; - bridge->funcs = &dw_hdmi_bridge_funcs; - ret = drm_bridge_attach(drm, bridge); - if (ret) { - DRM_ERROR("Failed to initialize bridge with drm\n"); - return -EINVAL; +static const struct dw_hdmi_phy_data dw_hdmi_phys[] = { + { + .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY, + .name = "DWC HDMI TX PHY", + }, { + .type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC, + .name = "DWC MHL PHY + HEAC PHY", + .has_svsret = true, + }, { + .type = DW_HDMI_PHY_DWC_MHL_PHY, + .name = "DWC MHL PHY", + .has_svsret = true, + }, { + .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC, + .name = "DWC HDMI 3D TX PHY + HEAC PHY", + }, { + .type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY, + .name = "DWC HDMI 3D TX PHY", + }, { + .type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY, + .name = "DWC HDMI 2.0 TX PHY", + .has_svsret = true, } +}; - encoder->bridge = bridge; - hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; +static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) +{ + unsigned int i; + u8 phy_type; - drm_connector_helper_add(&hdmi->connector, - &dw_hdmi_connector_helper_funcs); + phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); - drm_connector_init(drm, &hdmi->connector, - &dw_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); + for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) { + if (dw_hdmi_phys[i].type == phy_type) { + hdmi->phy = &dw_hdmi_phys[i]; + return 0; + } + } - drm_mode_connector_attach_encoder(&hdmi->connector, encoder); + if (phy_type == DW_HDMI_PHY_VENDOR_PHY) + dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n"); + else + dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", + phy_type); - return 0; + return -ENODEV; } -int dw_hdmi_bind(struct device *dev, struct device *master, - void *data, struct drm_encoder *encoder, - struct resource *iores, int irq, - const struct dw_hdmi_plat_data *plat_data) +static struct dw_hdmi * +__dw_hdmi_probe(struct platform_device *pdev, + const struct dw_hdmi_plat_data *plat_data) { - struct drm_device *drm = data; + struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct platform_device_info pdevinfo; struct device_node *ddc_node; struct dw_hdmi *hdmi; + struct resource *iores; + int irq; int ret; u32 val = 1; + u8 prod_id0; + u8 prod_id1; u8 config0; - u8 config1; + u8 config3; hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) - return -ENOMEM; - - hdmi->connector.interlace_allowed = 1; + return ERR_PTR(-ENOMEM); hdmi->plat_data = plat_data; hdmi->dev = dev; hdmi->dev_type = plat_data->dev_type; hdmi->sample_rate = 48000; - hdmi->encoder = encoder; hdmi->disabled = true; hdmi->rxsense = true; hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); @@ -1909,7 +1949,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master, break; default: dev_err(dev, "reg-io-width must be 1 or 4\n"); - return -EINVAL; + return ERR_PTR(-EINVAL); } ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0); @@ -1918,13 +1958,14 @@ int dw_hdmi_bind(struct device *dev, struct device *master, of_node_put(ddc_node); if (!hdmi->ddc) { dev_dbg(hdmi->dev, "failed to read ddc node\n"); - return -EPROBE_DEFER; + return ERR_PTR(-EPROBE_DEFER); } } else { dev_dbg(hdmi->dev, "no ddc property found\n"); } + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); hdmi->regs = devm_ioremap_resource(dev, iores); if (IS_ERR(hdmi->regs)) { ret = PTR_ERR(hdmi->regs); @@ -1958,15 +1999,36 @@ int dw_hdmi_bind(struct device *dev, struct device *master, } /* Product and revision IDs */ - dev_info(dev, - "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", - hdmi_readb(hdmi, HDMI_DESIGN_ID), - hdmi_readb(hdmi, HDMI_REVISION_ID), - hdmi_readb(hdmi, HDMI_PRODUCT_ID0), - hdmi_readb(hdmi, HDMI_PRODUCT_ID1)); + hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8) + | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0); + prod_id0 = hdmi_readb(hdmi, HDMI_PRODUCT_ID0); + prod_id1 = hdmi_readb(hdmi, HDMI_PRODUCT_ID1); + + if (prod_id0 != HDMI_PRODUCT_ID0_HDMI_TX || + (prod_id1 & ~HDMI_PRODUCT_ID1_HDCP) != HDMI_PRODUCT_ID1_HDMI_TX) { + dev_err(dev, "Unsupported HDMI controller (%04x:%02x:%02x)\n", + hdmi->version, prod_id0, prod_id1); + ret = -ENODEV; + goto err_iahb; + } + + ret = dw_hdmi_detect_phy(hdmi); + if (ret < 0) + goto err_iahb; + + dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n", + hdmi->version >> 12, hdmi->version & 0xfff, + prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", + hdmi->phy->name); initialize_hdmi_ih_mutes(hdmi); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err_iahb; + } + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, dw_hdmi_irq, IRQF_SHARED, dev_name(dev), hdmi); @@ -1996,11 +2058,13 @@ int dw_hdmi_bind(struct device *dev, struct device *master, hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, HDMI_IH_PHY_STAT0); - ret = dw_hdmi_fb_registered(hdmi); - if (ret) - goto err_iahb; + hdmi->bridge.driver_private = hdmi; + hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; +#ifdef CONFIG_OF + hdmi->bridge.of_node = pdev->dev.of_node; +#endif - ret = dw_hdmi_register(drm, hdmi); + ret = dw_hdmi_fb_registered(hdmi); if (ret) goto err_iahb; @@ -2013,9 +2077,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master, pdevinfo.id = PLATFORM_DEVID_AUTO; config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID); - config1 = hdmi_readb(hdmi, HDMI_CONFIG1_ID); + config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); - if (config1 & HDMI_CONFIG1_AHB) { + if (config3 & HDMI_CONFIG3_AHBAUDDMA) { struct dw_hdmi_audio_data audio; audio.phys = iores->start; @@ -2047,9 +2111,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master, if (hdmi->i2c) dw_hdmi_i2c_init(hdmi); - dev_set_drvdata(dev, hdmi); + platform_set_drvdata(pdev, hdmi); - return 0; + return hdmi; err_iahb: if (hdmi->i2c) { @@ -2063,14 +2127,11 @@ err_isfr: err_res: i2c_put_adapter(hdmi->ddc); - return ret; + return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(dw_hdmi_bind); -void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) +static void __dw_hdmi_remove(struct dw_hdmi *hdmi) { - struct dw_hdmi *hdmi = dev_get_drvdata(dev); - if (hdmi->audio && !IS_ERR(hdmi->audio)) platform_device_unregister(hdmi->audio); @@ -2085,6 +2146,70 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data) else i2c_put_adapter(hdmi->ddc); } + +/* ----------------------------------------------------------------------------- + * Probe/remove API, used from platforms based on the DRM bridge API. + */ +int dw_hdmi_probe(struct platform_device *pdev, + const struct dw_hdmi_plat_data *plat_data) +{ + struct dw_hdmi *hdmi; + int ret; + + hdmi = __dw_hdmi_probe(pdev, plat_data); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); + + ret = drm_bridge_add(&hdmi->bridge); + if (ret < 0) { + __dw_hdmi_remove(hdmi); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_probe); + +void dw_hdmi_remove(struct platform_device *pdev) +{ + struct dw_hdmi *hdmi = platform_get_drvdata(pdev); + + drm_bridge_remove(&hdmi->bridge); + + __dw_hdmi_remove(hdmi); +} +EXPORT_SYMBOL_GPL(dw_hdmi_remove); + +/* ----------------------------------------------------------------------------- + * Bind/unbind API, used from platforms based on the component framework. + */ +int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, + const struct dw_hdmi_plat_data *plat_data) +{ + struct dw_hdmi *hdmi; + int ret; + + hdmi = __dw_hdmi_probe(pdev, plat_data); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); + + ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL); + if (ret) { + dw_hdmi_remove(pdev); + DRM_ERROR("Failed to initialize bridge with drm\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dw_hdmi_bind); + +void dw_hdmi_unbind(struct device *dev) +{ + struct dw_hdmi *hdmi = dev_get_drvdata(dev); + + __dw_hdmi_remove(hdmi); +} EXPORT_SYMBOL_GPL(dw_hdmi_unbind); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index 55135bbd0c16..325b0b8ae639 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -545,12 +545,24 @@ #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 enum { +/* PRODUCT_ID0 field values */ + HDMI_PRODUCT_ID0_HDMI_TX = 0xa0, + +/* PRODUCT_ID1 field values */ + HDMI_PRODUCT_ID1_HDCP = 0xc0, + HDMI_PRODUCT_ID1_HDMI_RX = 0x02, + HDMI_PRODUCT_ID1_HDMI_TX = 0x01, + /* CONFIG0_ID field values */ HDMI_CONFIG0_I2S = 0x10, /* CONFIG1_ID field values */ HDMI_CONFIG1_AHB = 0x01, +/* CONFIG3_ID field values */ + HDMI_CONFIG3_AHBAUDDMA = 0x02, + HDMI_CONFIG3_GPAUD = 0x01, + /* IH_FC_INT2 field values */ HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, @@ -847,8 +859,8 @@ enum { HDMI_PHY_CONF0_PDZ_OFFSET = 7, HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, - HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20, - HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5, + HDMI_PHY_CONF0_SVSRET_MASK = 0x20, + HDMI_PHY_CONF0_SVSRET_OFFSET = 5, HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, @@ -977,8 +989,7 @@ enum { HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0, /* MC_PHYRSTZ field values */ - HDMI_MC_PHYRSTZ_ASSERT = 0x0, - HDMI_MC_PHYRSTZ_DEASSERT = 0x1, + HDMI_MC_PHYRSTZ_PHYRSTZ = 0x01, /* MC_HEACPHY_RST field values */ HDMI_MC_HEACPHY_RST_ASSERT = 0x1, @@ -1073,4 +1084,70 @@ enum { HDMI_I2CM_CTLINT_ARB_MASK = 0x4, }; +/* + * HDMI 3D TX PHY registers + */ +#define HDMI_3D_TX_PHY_PWRCTRL 0x00 +#define HDMI_3D_TX_PHY_SERDIVCTRL 0x01 +#define HDMI_3D_TX_PHY_SERCKCTRL 0x02 +#define HDMI_3D_TX_PHY_SERCKKILLCTRL 0x03 +#define HDMI_3D_TX_PHY_TXRESCTRL 0x04 +#define HDMI_3D_TX_PHY_CKCALCTRL 0x05 +#define HDMI_3D_TX_PHY_CPCE_CTRL 0x06 +#define HDMI_3D_TX_PHY_TXCLKMEASCTRL 0x07 +#define HDMI_3D_TX_PHY_TXMEASCTRL 0x08 +#define HDMI_3D_TX_PHY_CKSYMTXCTRL 0x09 +#define HDMI_3D_TX_PHY_CMPSEQCTRL 0x0a +#define HDMI_3D_TX_PHY_CMPPWRCTRL 0x0b +#define HDMI_3D_TX_PHY_CMPMODECTRL 0x0c +#define HDMI_3D_TX_PHY_MEASCTRL 0x0d +#define HDMI_3D_TX_PHY_VLEVCTRL 0x0e +#define HDMI_3D_TX_PHY_D2ACTRL 0x0f +#define HDMI_3D_TX_PHY_CURRCTRL 0x10 +#define HDMI_3D_TX_PHY_DRVANACTRL 0x11 +#define HDMI_3D_TX_PHY_PLLMEASCTRL 0x12 +#define HDMI_3D_TX_PHY_PLLPHBYCTRL 0x13 +#define HDMI_3D_TX_PHY_GRP_CTRL 0x14 +#define HDMI_3D_TX_PHY_GMPCTRL 0x15 +#define HDMI_3D_TX_PHY_MPLLMEASCTRL 0x16 +#define HDMI_3D_TX_PHY_MSM_CTRL 0x17 +#define HDMI_3D_TX_PHY_SCRPB_STATUS 0x18 +#define HDMI_3D_TX_PHY_TXTERM 0x19 +#define HDMI_3D_TX_PHY_PTRPT_ENBL 0x1a +#define HDMI_3D_TX_PHY_PATTERNGEN 0x1b +#define HDMI_3D_TX_PHY_SDCAP_MODE 0x1c +#define HDMI_3D_TX_PHY_SCOPEMODE 0x1d +#define HDMI_3D_TX_PHY_DIGTXMODE 0x1e +#define HDMI_3D_TX_PHY_STR_STATUS 0x1f +#define HDMI_3D_TX_PHY_SCOPECNT0 0x20 +#define HDMI_3D_TX_PHY_SCOPECNT1 0x21 +#define HDMI_3D_TX_PHY_SCOPECNT2 0x22 +#define HDMI_3D_TX_PHY_SCOPECNTCLK 0x23 +#define HDMI_3D_TX_PHY_SCOPESAMPLE 0x24 +#define HDMI_3D_TX_PHY_SCOPECNTMSB01 0x25 +#define HDMI_3D_TX_PHY_SCOPECNTMSB2CK 0x26 + +/* HDMI_3D_TX_PHY_CKCALCTRL values */ +#define HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE BIT(15) + +/* HDMI_3D_TX_PHY_MSM_CTRL values */ +#define HDMI_3D_TX_PHY_MSM_CTRL_MPLL_PH_SEL_CK BIT(13) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_CLK_REF_MPLL (0 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_OFF (1 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_PCLK (2 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK (3 << 1) +#define HDMI_3D_TX_PHY_MSM_CTRL_SCOPE_CK_SEL BIT(0) + +/* HDMI_3D_TX_PHY_PTRPT_ENBL values */ +#define HDMI_3D_TX_PHY_PTRPT_ENBL_OVERRIDE BIT(15) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT2 BIT(8) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT1 BIT(7) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT0 BIT(6) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_CK_REF_ENB BIT(5) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_RCAL_ENB BIT(4) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_CLK_ALIGN_ENB BIT(3) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_READY BIT(2) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_CKO_WORD_ENB BIT(1) +#define HDMI_3D_TX_PHY_PTRPT_ENBL_REFCLK_ENB BIT(0) + #endif /* __DW_HDMI_H__ */ diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index b2c267df7ee7..cdd0a9d44ba1 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -9,6 +9,8 @@ * published by the Free Software Foundation. */ +#include <asm/unaligned.h> + #include <drm/bridge/mhl.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> @@ -28,7 +30,10 @@ #include "sil-sii8620.h" -#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) +#define SII8620_BURST_BUF_LEN 288 +#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) +#define MHL1_MAX_LCLK 225000 +#define MHL3_MAX_LCLK 600000 enum sii8620_mode { CM_DISCONNECTED, @@ -59,6 +64,9 @@ struct sii8620 { struct regulator_bulk_data supplies[2]; struct mutex lock; /* context lock, protects fields below */ int error; + int pixel_clock; + unsigned int use_packed_pixel:1; + int video_code; enum sii8620_mode mode; enum sii8620_sink_type sink_type; u8 cbus_status; @@ -66,11 +74,20 @@ struct sii8620 { u8 xstat[MHL_XDS_SIZE]; u8 devcap[MHL_DCAP_SIZE]; u8 xdevcap[MHL_XDC_SIZE]; - u8 avif[19]; + u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; struct edid *edid; unsigned int gen2_write_burst:1; enum sii8620_mt_state mt_state; struct list_head mt_queue; + struct { + int r_size; + int r_count; + int rx_ack; + int rx_count; + u8 rx_buf[32]; + int tx_count; + u8 tx_buf[32]; + } burst; }; struct sii8620_mt_msg; @@ -78,12 +95,15 @@ struct sii8620_mt_msg; typedef void (*sii8620_mt_msg_cb)(struct sii8620 *ctx, struct sii8620_mt_msg *msg); +typedef void (*sii8620_cb)(struct sii8620 *ctx, int ret); + struct sii8620_mt_msg { struct list_head node; u8 reg[4]; u8 ret; sii8620_mt_msg_cb send; sii8620_mt_msg_cb recv; + sii8620_cb continuation; }; static const u8 sii8620_i2c_page[] = { @@ -101,6 +121,7 @@ static void sii8620_fetch_edid(struct sii8620 *ctx); static void sii8620_set_upstream_edid(struct sii8620 *ctx); static void sii8620_enable_hpd(struct sii8620 *ctx); static void sii8620_mhl_disconnected(struct sii8620 *ctx); +static void sii8620_disconnect(struct sii8620 *ctx); static int sii8620_clear_error(struct sii8620 *ctx) { @@ -227,6 +248,11 @@ static void sii8620_setbits(struct sii8620 *ctx, u16 addr, u8 mask, u8 val) sii8620_write(ctx, addr, val); } +static inline bool sii8620_is_mhl3(struct sii8620 *ctx) +{ + return ctx->mode >= CM_MHL3; +} + static void sii8620_mt_cleanup(struct sii8620 *ctx) { struct sii8620_mt_msg *msg, *n; @@ -251,9 +277,11 @@ static void sii8620_mt_work(struct sii8620 *ctx) ctx->mt_state = MT_STATE_READY; msg = list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); + list_del(&msg->node); if (msg->recv) msg->recv(ctx, msg); - list_del(&msg->node); + if (msg->continuation) + msg->continuation(ctx, msg->ret); kfree(msg); } @@ -266,9 +294,59 @@ static void sii8620_mt_work(struct sii8620 *ctx) msg->send(ctx, msg); } +static void sii8620_enable_gen2_write_burst(struct sii8620 *ctx) +{ + u8 ctrl = BIT_MDT_RCV_CTRL_MDT_RCV_EN; + + if (ctx->gen2_write_burst) + return; + + if (ctx->mode >= CM_MHL1) + ctrl |= BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN; + + sii8620_write_seq(ctx, + REG_MDT_RCV_TIMEOUT, 100, + REG_MDT_RCV_CTRL, ctrl + ); + ctx->gen2_write_burst = 1; +} + +static void sii8620_disable_gen2_write_burst(struct sii8620 *ctx) +{ + if (!ctx->gen2_write_burst) + return; + + sii8620_write_seq_static(ctx, + REG_MDT_XMIT_CTRL, 0, + REG_MDT_RCV_CTRL, 0 + ); + ctx->gen2_write_burst = 0; +} + +static void sii8620_start_gen2_write_burst(struct sii8620 *ctx) +{ + sii8620_write_seq_static(ctx, + REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT + | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR + | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD + | BIT_MDT_XMIT_SM_ERROR, + REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY + | BIT_MDT_IDLE_AFTER_HAWB_DISABLE + | BIT_MDT_RFIFO_DATA_RDY + ); + sii8620_enable_gen2_write_burst(ctx); +} + static void sii8620_mt_msc_cmd_send(struct sii8620 *ctx, struct sii8620_mt_msg *msg) { + if (msg->reg[0] == MHL_SET_INT && + msg->reg[1] == MHL_INT_REG(RCHANGE) && + msg->reg[2] == MHL_INT_RC_FEAT_REQ) + sii8620_enable_gen2_write_burst(ctx); + else + sii8620_disable_gen2_write_burst(ctx); + switch (msg->reg[0]) { case MHL_WRITE_STAT: case MHL_SET_INT: @@ -281,6 +359,12 @@ static void sii8620_mt_msc_cmd_send(struct sii8620 *ctx, sii8620_write(ctx, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_MSG); break; + case MHL_READ_DEVCAP_REG: + case MHL_READ_XDEVCAP_REG: + sii8620_write(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg[1]); + sii8620_write(ctx, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_READ_DEVCAP); + break; default: dev_err(ctx->dev, "%s: command %#x not supported\n", __func__, msg->reg[0]); @@ -299,6 +383,21 @@ static struct sii8620_mt_msg *sii8620_mt_msg_new(struct sii8620 *ctx) return msg; } +static void sii8620_mt_set_cont(struct sii8620 *ctx, sii8620_cb cont) +{ + struct sii8620_mt_msg *msg; + + if (ctx->error) + return; + + if (list_empty(&ctx->mt_queue)) { + ctx->error = -EINVAL; + return; + } + msg = list_last_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); + msg->continuation = cont; +} + static void sii8620_mt_msc_cmd(struct sii8620 *ctx, u8 cmd, u8 arg1, u8 arg2) { struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); @@ -358,7 +457,7 @@ static void sii8620_update_array(u8 *dst, u8 *src, int count) } } -static void sii8620_mr_devcap(struct sii8620 *ctx) +static void sii8620_sink_detected(struct sii8620 *ctx, int ret) { static const char * const sink_str[] = { [SINK_NONE] = "NONE", @@ -366,23 +465,10 @@ static void sii8620_mr_devcap(struct sii8620 *ctx) [SINK_DVI] = "DVI" }; - u8 dcap[MHL_DCAP_SIZE]; char sink_name[20]; struct device *dev = ctx->dev; - sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE); - if (ctx->error < 0) - return; - - dev_info(dev, "dcap: %*ph\n", MHL_DCAP_SIZE, dcap); - dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n", - dcap[MHL_DCAP_MHL_VERSION] / 16, - dcap[MHL_DCAP_MHL_VERSION] % 16, dcap[MHL_DCAP_ADOPTER_ID_H], - dcap[MHL_DCAP_ADOPTER_ID_L], dcap[MHL_DCAP_DEVICE_ID_H], - dcap[MHL_DCAP_DEVICE_ID_L]); - sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); - - if (!(dcap[MHL_DCAP_CAT] & MHL_DCAP_CAT_SINK)) + if (ret < 0) return; sii8620_fetch_edid(ctx); @@ -401,18 +487,76 @@ static void sii8620_mr_devcap(struct sii8620 *ctx) dev_info(dev, "detected sink(type: %s): %s\n", sink_str[ctx->sink_type], sink_name); +} + +static void sii8620_hsic_init(struct sii8620 *ctx) +{ + if (!sii8620_is_mhl3(ctx)) + return; + + sii8620_write(ctx, REG_FCGC, + BIT_FCGC_HSIC_HOSTMODE | BIT_FCGC_HSIC_ENABLE); + sii8620_setbits(ctx, REG_HRXCTRL3, + BIT_HRXCTRL3_HRX_STAY_RESET | BIT_HRXCTRL3_STATUS_EN, ~0); + sii8620_setbits(ctx, REG_TTXNUMB, MSK_TTXNUMB_TTX_NUMBPS, 4); + sii8620_setbits(ctx, REG_TRXCTRL, BIT_TRXCTRL_TRX_FROM_SE_COC, ~0); + sii8620_setbits(ctx, REG_HTXCTRL, BIT_HTXCTRL_HTX_DRVCONN1, 0); + sii8620_setbits(ctx, REG_KEEPER, MSK_KEEPER_MODE, VAL_KEEPER_MODE_HOST); + sii8620_write_seq_static(ctx, + REG_TDMLLCTL, 0, + REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST | + BIT_UTSRST_KEEPER_SRST | BIT_UTSRST_FC_SRST, + REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST, + REG_HRXINTL, 0xff, + REG_HRXINTH, 0xff, + REG_TTXINTL, 0xff, + REG_TTXINTH, 0xff, + REG_TRXINTL, 0xff, + REG_TRXINTH, 0xff, + REG_HTXINTL, 0xff, + REG_HTXINTH, 0xff, + REG_FCINTR0, 0xff, + REG_FCINTR1, 0xff, + REG_FCINTR2, 0xff, + REG_FCINTR3, 0xff, + REG_FCINTR4, 0xff, + REG_FCINTR5, 0xff, + REG_FCINTR6, 0xff, + REG_FCINTR7, 0xff + ); +} + +static void sii8620_edid_read(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + sii8620_set_upstream_edid(ctx); + sii8620_hsic_init(ctx); sii8620_enable_hpd(ctx); } +static void sii8620_mr_devcap(struct sii8620 *ctx) +{ + u8 dcap[MHL_DCAP_SIZE]; + struct device *dev = ctx->dev; + + sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE); + if (ctx->error < 0) + return; + + dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n", + dcap[MHL_DCAP_MHL_VERSION] / 16, + dcap[MHL_DCAP_MHL_VERSION] % 16, + dcap[MHL_DCAP_ADOPTER_ID_H], dcap[MHL_DCAP_ADOPTER_ID_L], + dcap[MHL_DCAP_DEVICE_ID_H], dcap[MHL_DCAP_DEVICE_ID_L]); + sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); +} + static void sii8620_mr_xdevcap(struct sii8620 *ctx) { sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, ctx->xdevcap, MHL_XDC_SIZE); - - sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE), - MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); - sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP); } static void sii8620_mt_read_devcap_recv(struct sii8620 *ctx, @@ -450,6 +594,197 @@ static void sii8620_mt_read_devcap(struct sii8620 *ctx, bool xdevcap) msg->recv = sii8620_mt_read_devcap_recv; } +static void sii8620_mt_read_devcap_reg_recv(struct sii8620 *ctx, + struct sii8620_mt_msg *msg) +{ + u8 reg = msg->reg[0] & 0x7f; + + if (msg->reg[0] & 0x80) + ctx->xdevcap[reg] = msg->ret; + else + ctx->devcap[reg] = msg->ret; +} + +static void sii8620_mt_read_devcap_reg(struct sii8620 *ctx, u8 reg) +{ + struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); + + if (!msg) + return; + + msg->reg[0] = (reg & 0x80) ? MHL_READ_XDEVCAP_REG : MHL_READ_DEVCAP_REG; + msg->reg[1] = reg; + msg->send = sii8620_mt_msc_cmd_send; + msg->recv = sii8620_mt_read_devcap_reg_recv; +} + +static inline void sii8620_mt_read_xdevcap_reg(struct sii8620 *ctx, u8 reg) +{ + sii8620_mt_read_devcap_reg(ctx, reg | 0x80); +} + +static void *sii8620_burst_get_tx_buf(struct sii8620 *ctx, int len) +{ + u8 *buf = &ctx->burst.tx_buf[ctx->burst.tx_count]; + int size = len + 2; + + if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) { + dev_err(ctx->dev, "TX-BLK buffer exhausted\n"); + ctx->error = -EINVAL; + return NULL; + } + + ctx->burst.tx_count += size; + buf[1] = len; + + return buf + 2; +} + +static u8 *sii8620_burst_get_rx_buf(struct sii8620 *ctx, int len) +{ + u8 *buf = &ctx->burst.rx_buf[ctx->burst.rx_count]; + int size = len + 1; + + if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) { + dev_err(ctx->dev, "RX-BLK buffer exhausted\n"); + ctx->error = -EINVAL; + return NULL; + } + + ctx->burst.rx_count += size; + buf[0] = len; + + return buf + 1; +} + +static void sii8620_burst_send(struct sii8620 *ctx) +{ + int tx_left = ctx->burst.tx_count; + u8 *d = ctx->burst.tx_buf; + + while (tx_left > 0) { + int len = d[1] + 2; + + if (ctx->burst.r_count + len > ctx->burst.r_size) + break; + d[0] = min(ctx->burst.rx_ack, 255); + ctx->burst.rx_ack -= d[0]; + sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, d, len); + ctx->burst.r_count += len; + tx_left -= len; + d += len; + } + + ctx->burst.tx_count = tx_left; + + while (ctx->burst.rx_ack > 0) { + u8 b[2] = { min(ctx->burst.rx_ack, 255), 0 }; + + if (ctx->burst.r_count + 2 > ctx->burst.r_size) + break; + ctx->burst.rx_ack -= b[0]; + sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, b, 2); + ctx->burst.r_count += 2; + } +} + +static void sii8620_burst_receive(struct sii8620 *ctx) +{ + u8 buf[3], *d; + int count; + + sii8620_read_buf(ctx, REG_EMSCRFIFOBCNTL, buf, 2); + count = get_unaligned_le16(buf); + while (count > 0) { + int len = min(count, 3); + + sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, buf, len); + count -= len; + ctx->burst.rx_ack += len - 1; + ctx->burst.r_count -= buf[1]; + if (ctx->burst.r_count < 0) + ctx->burst.r_count = 0; + + if (len < 3 || !buf[2]) + continue; + + len = buf[2]; + d = sii8620_burst_get_rx_buf(ctx, len); + if (!d) + continue; + sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, d, len); + count -= len; + ctx->burst.rx_ack += len; + } +} + +static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) +{ + struct mhl_burst_blk_rcv_buffer_info *d = + sii8620_burst_get_tx_buf(ctx, sizeof(*d)); + if (!d) + return; + + d->id = cpu_to_be16(MHL_BURST_ID_BLK_RCV_BUFFER_INFO); + d->size = cpu_to_le16(size); +} + +static u8 sii8620_checksum(void *ptr, int size) +{ + u8 *d = ptr, sum = 0; + + while (size--) + sum += *d++; + + return sum; +} + +static void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, + enum mhl_burst_id id) +{ + h->id = cpu_to_be16(id); + h->total_entries = 1; + h->sequence_index = 1; +} + +static void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) +{ + struct mhl_burst_bits_per_pixel_fmt *d; + const int size = sizeof(*d) + sizeof(d->desc[0]); + + d = sii8620_burst_get_tx_buf(ctx, size); + if (!d) + return; + + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); + d->num_entries = 1; + d->desc[0].stream_id = 0; + d->desc[0].pixel_format = fmt; + d->hdr.checksum -= sii8620_checksum(d, size); +} + +static void sii8620_burst_rx_all(struct sii8620 *ctx) +{ + u8 *d = ctx->burst.rx_buf; + int count = ctx->burst.rx_count; + + while (count-- > 0) { + int len = *d++; + int id = get_unaligned_be16(&d[0]); + + switch (id) { + case MHL_BURST_ID_BLK_RCV_BUFFER_INFO: + ctx->burst.r_size = get_unaligned_le16(&d[2]); + break; + default: + break; + } + count -= len; + d += len; + } + ctx->burst.rx_count = 0; +} + static void sii8620_fetch_edid(struct sii8620 *ctx) { u8 lm_ddc, ddc_cmd, int3, cbus; @@ -537,12 +872,12 @@ static void sii8620_fetch_edid(struct sii8620 *ctx) edid = new_edid; } } - - if (fetched + FETCH_SIZE == edid_len) - sii8620_write(ctx, REG_INTR3, int3); } - sii8620_write(ctx, REG_LM_DDC, lm_ddc); + sii8620_write_seq(ctx, + REG_INTR3_MASK, BIT_DDC_CMD_DONE, + REG_LM_DDC, lm_ddc + ); end: kfree(ctx->edid); @@ -641,11 +976,10 @@ static void sii8620_hw_reset(struct sii8620 *ctx) static void sii8620_cbus_reset(struct sii8620 *ctx) { - sii8620_write_seq_static(ctx, - REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST - | BIT_PWD_SRST_CBUS_RST_SW_EN, - REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN - ); + sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST + | BIT_PWD_SRST_CBUS_RST_SW_EN); + usleep_range(10000, 20000); + sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN); } static void sii8620_set_auto_zone(struct sii8620 *ctx) @@ -683,48 +1017,208 @@ static void sii8620_stop_video(struct sii8620 *ctx) | BIT_TPI_SC_TPI_AV_MUTE; break; case SINK_HDMI: + default: val = BIT_TPI_SC_REG_TMDS_OE_POWER_DOWN | BIT_TPI_SC_TPI_AV_MUTE | BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI; break; - default: - return; } sii8620_write(ctx, REG_TPI_SC, val); } +static void sii8620_set_format(struct sii8620 *ctx) +{ + u8 out_fmt; + + if (sii8620_is_mhl3(ctx)) { + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, + ctx->use_packed_pixel ? ~0 : 0); + } else { + if (ctx->use_packed_pixel) + sii8620_write_seq_static(ctx, + REG_VID_MODE, BIT_VID_MODE_M1080P, + REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, + REG_MHLTX_CTL6, 0x60 + ); + else + sii8620_write_seq_static(ctx, + REG_VID_MODE, 0, + REG_MHL_TOP_CTL, 1, + REG_MHLTX_CTL6, 0xa0 + ); + } + + if (ctx->use_packed_pixel) + out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | + BIT_TPI_OUTPUT_CSCMODE709; + else + out_fmt = VAL_TPI_FORMAT(RGB, FULL); + + sii8620_write_seq(ctx, + REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), + REG_TPI_OUTPUT, out_fmt, + ); +} + +static int mhl3_infoframe_init(struct mhl3_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->version = 3; + frame->hev_format = -1; + return 0; +} + +static ssize_t mhl3_infoframe_pack(struct mhl3_infoframe *frame, + void *buffer, size_t size) +{ + const int frm_len = HDMI_INFOFRAME_HEADER_SIZE + MHL3_INFOFRAME_SIZE; + u8 *ptr = buffer; + + if (size < frm_len) + return -ENOSPC; + + memset(buffer, 0, size); + ptr[0] = HDMI_INFOFRAME_TYPE_VENDOR; + ptr[1] = frame->version; + ptr[2] = MHL3_INFOFRAME_SIZE; + ptr[4] = MHL3_IEEE_OUI & 0xff; + ptr[5] = (MHL3_IEEE_OUI >> 8) & 0xff; + ptr[6] = (MHL3_IEEE_OUI >> 16) & 0xff; + ptr[7] = frame->video_format & 0x3; + ptr[7] |= (frame->format_type & 0x7) << 2; + ptr[7] |= frame->sep_audio ? BIT(5) : 0; + if (frame->hev_format >= 0) { + ptr[9] = 1; + ptr[10] = (frame->hev_format >> 8) & 0xff; + ptr[11] = frame->hev_format & 0xff; + } + if (frame->av_delay) { + bool sign = frame->av_delay < 0; + int delay = sign ? -frame->av_delay : frame->av_delay; + + ptr[12] = (delay >> 16) & 0xf; + if (sign) + ptr[12] |= BIT(4); + ptr[13] = (delay >> 8) & 0xff; + ptr[14] = delay & 0xff; + } + ptr[3] -= sii8620_checksum(buffer, frm_len); + return frm_len; +} + +static void sii8620_set_infoframes(struct sii8620 *ctx) +{ + struct mhl3_infoframe mhl_frm; + union hdmi_infoframe frm; + u8 buf[31]; + int ret; + + if (!sii8620_is_mhl3(ctx) || !ctx->use_packed_pixel) { + sii8620_write(ctx, REG_TPI_SC, + BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif + 3, + ARRAY_SIZE(ctx->avif) - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_DROP_GEN_PKT); + return; + } + + ret = hdmi_avi_infoframe_init(&frm.avi); + frm.avi.colorspace = HDMI_COLORSPACE_YUV422; + frm.avi.active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; + frm.avi.picture_aspect = HDMI_PICTURE_ASPECT_16_9; + frm.avi.colorimetry = HDMI_COLORIMETRY_ITU_709; + frm.avi.video_code = ctx->video_code; + if (!ret) + ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf)); + if (ret > 0) + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_AVI_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | + BIT_PKT_FILTER_1_DROP_GEN_PKT | + BIT_PKT_FILTER_1_DROP_VSIF_PKT); + + sii8620_write(ctx, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_EN + | BIT_TPI_INFO_FSEL_RPT | VAL_TPI_INFO_FSEL_VSI); + ret = mhl3_infoframe_init(&mhl_frm); + if (!ret) + ret = mhl3_infoframe_pack(&mhl_frm, buf, ARRAY_SIZE(buf)); + sii8620_write_buf(ctx, REG_TPI_INFO_B0, buf, ret); +} + static void sii8620_start_hdmi(struct sii8620 *ctx) { sii8620_write_seq_static(ctx, REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL | BIT_RX_HDMI_CTRL2_USE_AV_MUTE, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE - | BIT_VID_OVRRD_M1080P_OVRRD, - REG_VID_MODE, 0, - REG_MHL_TOP_CTL, 0x1, - REG_MHLTX_CTL6, 0xa0, - REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), - REG_TPI_OUTPUT, VAL_TPI_FORMAT(RGB, FULL), - ); - - sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), - MHL_DST_LM_CLK_MODE_NORMAL | - MHL_DST_LM_PATH_ENABLED); + | BIT_VID_OVRRD_M1080P_OVRRD); + sii8620_set_format(ctx); - sii8620_set_auto_zone(ctx); + if (!sii8620_is_mhl3(ctx)) { + sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), + MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); + sii8620_set_auto_zone(ctx); + } else { + static const struct { + int max_clk; + u8 zone; + u8 link_rate; + u8 rrp_decode; + } clk_spec[] = { + { 150000, VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS, + MHL_XDS_LINK_RATE_1_5_GBPS, 0x38 }, + { 300000, VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS, + MHL_XDS_LINK_RATE_3_0_GBPS, 0x40 }, + { 600000, VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS, + MHL_XDS_LINK_RATE_6_0_GBPS, 0x40 }, + }; + u8 p0_ctrl = BIT_M3_P0CTRL_MHL3_P0_PORT_EN; + int clk = ctx->pixel_clock * (ctx->use_packed_pixel ? 2 : 3); + int i; + + for (i = 0; i < ARRAY_SIZE(clk_spec); ++i) + if (clk < clk_spec[i].max_clk) + break; - sii8620_write(ctx, REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + if (100 * clk >= 98 * clk_spec[i].max_clk) + p0_ctrl |= BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN; - sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif, - ARRAY_SIZE(ctx->avif)); + sii8620_burst_tx_bits_per_pixel_fmt(ctx, ctx->use_packed_pixel); + sii8620_burst_send(ctx); + sii8620_write_seq(ctx, + REG_MHL_DP_CTL0, 0xf0, + REG_MHL3_TX_ZONE_CTL, clk_spec[i].zone); + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN + | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, p0_ctrl); + sii8620_setbits(ctx, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, + clk_spec[i].rrp_decode); + sii8620_write_seq_static(ctx, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + | BIT_M3_CTRL_H2M_SWRST, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + ); + sii8620_mt_write_stat(ctx, MHL_XDS_REG(AVLINK_MODE_CONTROL), + clk_spec[i].link_rate); + } - sii8620_write(ctx, REG_PKT_FILTER_0, 0xa1, 0x2); + sii8620_set_infoframes(ctx); } static void sii8620_start_video(struct sii8620 *ctx) { - if (ctx->mode < CM_MHL3) + if (!sii8620_is_mhl3(ctx)) sii8620_stop_video(ctx); switch (ctx->sink_type) { @@ -757,44 +1251,6 @@ static void sii8620_enable_hpd(struct sii8620 *ctx) ); } -static void sii8620_enable_gen2_write_burst(struct sii8620 *ctx) -{ - if (ctx->gen2_write_burst) - return; - - sii8620_write_seq_static(ctx, - REG_MDT_RCV_TIMEOUT, 100, - REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN - ); - ctx->gen2_write_burst = 1; -} - -static void sii8620_disable_gen2_write_burst(struct sii8620 *ctx) -{ - if (!ctx->gen2_write_burst) - return; - - sii8620_write_seq_static(ctx, - REG_MDT_XMIT_CTRL, 0, - REG_MDT_RCV_CTRL, 0 - ); - ctx->gen2_write_burst = 0; -} - -static void sii8620_start_gen2_write_burst(struct sii8620 *ctx) -{ - sii8620_write_seq_static(ctx, - REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT - | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR - | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD - | BIT_MDT_XMIT_SM_ERROR, - REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY - | BIT_MDT_IDLE_AFTER_HAWB_DISABLE - | BIT_MDT_RFIFO_DATA_RDY - ); - sii8620_enable_gen2_write_burst(ctx); -} - static void sii8620_mhl_discover(struct sii8620 *ctx) { sii8620_write_seq_static(ctx, @@ -838,7 +1294,7 @@ static void sii8620_mhl_discover(struct sii8620 *ctx) static void sii8620_peer_specific_init(struct sii8620 *ctx) { - if (ctx->mode == CM_MHL3) + if (sii8620_is_mhl3(ctx)) sii8620_write_seq_static(ctx, REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD, REG_EMSCINTRMASK1, @@ -948,21 +1404,51 @@ static void sii8620_mhl_init(struct sii8620 *ctx) ); sii8620_disable_gen2_write_burst(ctx); - /* currently MHL3 is not supported, so we force version to 0 */ - sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), 0); + sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), SII8620_MHL_VERSION); sii8620_mt_write_stat(ctx, MHL_DST_REG(CONNECTED_RDY), MHL_DST_CONN_DCAP_RDY | MHL_DST_CONN_XDEVCAPP_SUPP | MHL_DST_CONN_POW_STAT); sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), MHL_INT_RC_DCAP_CHG); } +static void sii8620_emsc_enable(struct sii8620 *ctx) +{ + u8 reg; + + sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_EMSC_EN + | BIT_GENCTL_CLR_EMSC_RFIFO + | BIT_GENCTL_CLR_EMSC_XFIFO, ~0); + sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_CLR_EMSC_RFIFO + | BIT_GENCTL_CLR_EMSC_XFIFO, 0); + sii8620_setbits(ctx, REG_COMMECNT, BIT_COMMECNT_I2C_TO_EMSC_EN, ~0); + reg = sii8620_readb(ctx, REG_EMSCINTR); + sii8620_write(ctx, REG_EMSCINTR, reg); + sii8620_write(ctx, REG_EMSCINTRMASK, BIT_EMSCINTR_SPI_DVLD); +} + +static int sii8620_wait_for_fsm_state(struct sii8620 *ctx, u8 state) +{ + int i; + + for (i = 0; i < 10; ++i) { + u8 s = sii8620_readb(ctx, REG_COC_STAT_0); + + if ((s & MSK_COC_STAT_0_FSM_STATE) == state) + return 0; + if (!(s & BIT_COC_STAT_0_PLL_LOCKED)) + return -EBUSY; + usleep_range(4000, 6000); + } + return -ETIMEDOUT; +} + static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) { + int ret; + if (ctx->mode == mode) return; - ctx->mode = mode; - switch (mode) { case CM_MHL1: sii8620_write_seq_static(ctx, @@ -972,15 +1458,46 @@ static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) | BIT_DPD_OSC_EN, REG_COC_INTR_MASK, 0 ); + ctx->mode = mode; break; case CM_MHL3: + sii8620_write(ctx, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); + ctx->mode = mode; + return; + case CM_ECBUS_S: + sii8620_emsc_enable(ctx); sii8620_write_seq_static(ctx, - REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE, - REG_COC_CTL0, 0x40, - REG_MHL_COC_CTL1, 0x07 + REG_TTXSPINUMS, 4, + REG_TRXSPINUMS, 4, + REG_TTXHSICNUMS, 0x14, + REG_TRXHSICNUMS, 0x14, + REG_TTXTOTNUMS, 0x18, + REG_TRXTOTNUMS, 0x18, + REG_PWD_SRST, BIT_PWD_SRST_COC_DOC_RST + | BIT_PWD_SRST_CBUS_RST_SW_EN, + REG_MHL_COC_CTL1, 0xbd, + REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN, + REG_COC_CTLB, 0x01, + REG_COC_CTL0, 0x5c, + REG_COC_CTL14, 0x03, + REG_COC_CTL15, 0x80, + REG_MHL_DP_CTL6, BIT_MHL_DP_CTL6_DP_TAP1_SGN + | BIT_MHL_DP_CTL6_DP_TAP1_EN + | BIT_MHL_DP_CTL6_DT_PREDRV_FEEDCAP_EN, + REG_MHL_DP_CTL8, 0x03 ); - break; + ret = sii8620_wait_for_fsm_state(ctx, 0x03); + sii8620_write_seq_static(ctx, + REG_COC_CTL14, 0x00, + REG_COC_CTL15, 0x80 + ); + if (!ret) + sii8620_write(ctx, REG_CBUS3_CNVT, 0x85); + else + sii8620_disconnect(ctx); + return; case CM_DISCONNECTED: + ctx->mode = mode; break; default: dev_err(ctx->dev, "%s mode %d not supported\n", __func__, mode); @@ -1007,10 +1524,12 @@ static void sii8620_disconnect(struct sii8620 *ctx) { sii8620_disable_gen2_write_burst(ctx); sii8620_stop_video(ctx); - msleep(50); + msleep(100); sii8620_cbus_reset(ctx); sii8620_set_mode(ctx, CM_DISCONNECTED); sii8620_write_seq_static(ctx, + REG_TX_ZONE_CTL1, 0, + REG_MHL_PLL_CTL0, 0x07, REG_COC_CTL0, 0x40, REG_CBUS3_CNVT, 0x84, REG_COC_CTL14, 0x00, @@ -1123,24 +1642,45 @@ static void sii8620_irq_disc(struct sii8620 *ctx) sii8620_write(ctx, REG_CBUS_DISC_INTR0, stat); } +static void sii8620_read_burst(struct sii8620 *ctx) +{ + u8 buf[17]; + + sii8620_read_buf(ctx, REG_MDT_RCV_READ_PORT, buf, ARRAY_SIZE(buf)); + sii8620_write(ctx, REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN | + BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN | + BIT_MDT_RCV_CTRL_MDT_RFIFO_CLR_CUR); + sii8620_readb(ctx, REG_MDT_RFIFO_STAT); +} + static void sii8620_irq_g2wb(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_MDT_INT_0); if (stat & BIT_MDT_IDLE_AFTER_HAWB_DISABLE) - dev_dbg(ctx->dev, "HAWB idle\n"); + if (sii8620_is_mhl3(ctx)) + sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), + MHL_INT_RC_FEAT_COMPLETE); + + if (stat & BIT_MDT_RFIFO_DATA_RDY) + sii8620_read_burst(ctx); + + if (stat & BIT_MDT_XFIFO_EMPTY) + sii8620_write(ctx, REG_MDT_XMIT_CTRL, 0); sii8620_write(ctx, REG_MDT_INT_0, stat); } -static void sii8620_status_changed_dcap(struct sii8620 *ctx) +static void sii8620_status_dcap_ready(struct sii8620 *ctx) { - if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) { - sii8620_set_mode(ctx, CM_MHL1); - sii8620_peer_specific_init(ctx); - sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE - | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR); - } + enum sii8620_mode mode; + + mode = ctx->stat[MHL_DST_VERSION] >= 0x30 ? CM_MHL3 : CM_MHL1; + if (mode > ctx->mode) + sii8620_set_mode(ctx, mode); + sii8620_peer_specific_init(ctx); + sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE + | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR); } static void sii8620_status_changed_path(struct sii8620 *ctx) @@ -1149,7 +1689,9 @@ static void sii8620_status_changed_path(struct sii8620 *ctx) sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); - sii8620_mt_read_devcap(ctx, false); + if (!sii8620_is_mhl3(ctx)) + sii8620_mt_read_devcap(ctx, false); + sii8620_mt_set_cont(ctx, sii8620_sink_detected); } else { sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL); @@ -1166,19 +1708,75 @@ static void sii8620_msc_mr_write_stat(struct sii8620 *ctx) sii8620_update_array(ctx->stat, st, MHL_DST_SIZE); sii8620_update_array(ctx->xstat, xst, MHL_XDS_SIZE); - if (st[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) - sii8620_status_changed_dcap(ctx); + if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) + sii8620_status_dcap_ready(ctx); if (st[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) sii8620_status_changed_path(ctx); } +static void sii8620_ecbus_up(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_set_mode(ctx, CM_ECBUS_S); +} + +static void sii8620_got_ecbus_speed(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE), + MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); + sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP); + sii8620_mt_set_cont(ctx, sii8620_ecbus_up); +} + +static void sii8620_mhl_burst_emsc_support_set(struct mhl_burst_emsc_support *d, + enum mhl_burst_id id) +{ + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_EMSC_SUPPORT); + d->num_entries = 1; + d->burst_id[0] = cpu_to_be16(id); +} + +static void sii8620_send_features(struct sii8620 *ctx) +{ + u8 buf[16]; + + sii8620_write(ctx, REG_MDT_XMIT_CTRL, BIT_MDT_XMIT_CTRL_EN + | BIT_MDT_XMIT_CTRL_FIXED_BURST_LEN); + sii8620_mhl_burst_emsc_support_set((void *)buf, + MHL_BURST_ID_HID_PAYLOAD); + sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf)); +} + static void sii8620_msc_mr_set_int(struct sii8620 *ctx) { u8 ints[MHL_INT_SIZE]; sii8620_read_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); sii8620_write_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); + + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_DCAP_CHG) { + switch (ctx->mode) { + case CM_MHL3: + sii8620_mt_read_xdevcap_reg(ctx, MHL_XDC_ECBUS_SPEEDS); + sii8620_mt_set_cont(ctx, sii8620_got_ecbus_speed); + break; + case CM_ECBUS_S: + sii8620_mt_read_devcap(ctx, true); + break; + default: + break; + } + } + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ) + sii8620_send_features(ctx); + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE) + sii8620_edid_read(ctx, 0); } static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx) @@ -1261,6 +1859,19 @@ static void sii8620_irq_coc(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_COC_INTR); + if (stat & BIT_COC_CALIBRATION_DONE) { + u8 cstat = sii8620_readb(ctx, REG_COC_STAT_0); + + cstat &= BIT_COC_STAT_0_PLL_LOCKED | MSK_COC_STAT_0_FSM_STATE; + if (cstat == (BIT_COC_STAT_0_PLL_LOCKED | 0x02)) { + sii8620_write_seq_static(ctx, + REG_COC_CTLB, 0, + REG_TRXINTMH, BIT_TDM_INTR_SYNC_DATA + | BIT_TDM_INTR_SYNC_WAIT + ); + } + } + sii8620_write(ctx, REG_COC_INTR, stat); } @@ -1289,17 +1900,6 @@ static void sii8620_scdt_high(struct sii8620 *ctx) ); } -static void sii8620_scdt_low(struct sii8620 *ctx) -{ - sii8620_write(ctx, REG_TMDS_CSTAT_P3, - BIT_TMDS_CSTAT_P3_SCDT_CLR_AVI_DIS | - BIT_TMDS_CSTAT_P3_CLR_AVI); - - sii8620_stop_video(ctx); - - sii8620_write(ctx, REG_INTR8_MASK, 0); -} - static void sii8620_irq_scdt(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_INTR5); @@ -1309,8 +1909,6 @@ static void sii8620_irq_scdt(struct sii8620 *ctx) if (cstat & BIT_TMDS_CSTAT_P3_SCDT) sii8620_scdt_high(ctx); - else - sii8620_scdt_low(ctx); } sii8620_write(ctx, REG_INTR5, stat); @@ -1351,6 +1949,65 @@ static void sii8620_irq_infr(struct sii8620 *ctx) sii8620_start_video(ctx); } +static void sii8620_got_xdevcap(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_mt_read_devcap(ctx, false); +} + +static void sii8620_irq_tdm(struct sii8620 *ctx) +{ + u8 stat = sii8620_readb(ctx, REG_TRXINTH); + u8 tdm = sii8620_readb(ctx, REG_TRXSTA2); + + if ((tdm & MSK_TDM_SYNCHRONIZED) == VAL_TDM_SYNCHRONIZED) { + ctx->mode = CM_ECBUS_S; + ctx->burst.rx_ack = 0; + ctx->burst.r_size = SII8620_BURST_BUF_LEN; + sii8620_burst_tx_rbuf_info(ctx, SII8620_BURST_BUF_LEN); + sii8620_mt_read_devcap(ctx, true); + sii8620_mt_set_cont(ctx, sii8620_got_xdevcap); + } else { + sii8620_write_seq_static(ctx, + REG_MHL_PLL_CTL2, 0, + REG_MHL_PLL_CTL2, BIT_MHL_PLL_CTL2_CLKDETECT_EN + ); + } + + sii8620_write(ctx, REG_TRXINTH, stat); +} + +static void sii8620_irq_block(struct sii8620 *ctx) +{ + u8 stat = sii8620_readb(ctx, REG_EMSCINTR); + + if (stat & BIT_EMSCINTR_SPI_DVLD) { + u8 bstat = sii8620_readb(ctx, REG_SPIBURSTSTAT); + + if (bstat & BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE) + sii8620_burst_receive(ctx); + } + + sii8620_write(ctx, REG_EMSCINTR, stat); +} + +static void sii8620_irq_ddc(struct sii8620 *ctx) +{ + u8 stat = sii8620_readb(ctx, REG_INTR3); + + if (stat & BIT_DDC_CMD_DONE) { + sii8620_write(ctx, REG_INTR3_MASK, 0); + if (sii8620_is_mhl3(ctx)) + sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), + MHL_INT_RC_FEAT_REQ); + else + sii8620_edid_read(ctx, 0); + } + sii8620_write(ctx, REG_INTR3, stat); +} + /* endian agnostic, non-volatile version of test_bit */ static bool sii8620_test_bit(unsigned int nr, const u8 *addr) { @@ -1366,9 +2023,12 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data) { BIT_FAST_INTR_STAT_DISC, sii8620_irq_disc }, { BIT_FAST_INTR_STAT_G2WB, sii8620_irq_g2wb }, { BIT_FAST_INTR_STAT_COC, sii8620_irq_coc }, + { BIT_FAST_INTR_STAT_TDM, sii8620_irq_tdm }, { BIT_FAST_INTR_STAT_MSC, sii8620_irq_msc }, { BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr }, + { BIT_FAST_INTR_STAT_BLOCK, sii8620_irq_block }, { BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid }, + { BIT_FAST_INTR_STAT_DDC, sii8620_irq_ddc }, { BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt }, { BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr }, }; @@ -1383,7 +2043,9 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data) if (sii8620_test_bit(irq_vec[i].bit, stats)) irq_vec[i].handler(ctx); + sii8620_burst_rx_all(ctx); sii8620_mt_work(ctx); + sii8620_burst_send(ctx); ret = sii8620_clear_error(ctx); if (ret) { @@ -1450,22 +2112,41 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode) { struct sii8620 *ctx = bridge_to_sii8620(bridge); - bool ret = false; - int max_clock = 74250; + int max_lclk; + bool ret = true; mutex_lock(&ctx->lock); - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - goto out; - - if (ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) - max_clock = 300000; - - ret = mode->clock <= max_clock; - -out: + max_lclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : MHL1_MAX_LCLK; + if (max_lclk > 3 * adjusted_mode->clock) { + ctx->use_packed_pixel = 0; + goto end; + } + if ((ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) && + max_lclk > 2 * adjusted_mode->clock) { + ctx->use_packed_pixel = 1; + goto end; + } + ret = false; +end: + if (ret) { + u8 vic = drm_match_cea_mode(adjusted_mode); + + if (!vic) { + union hdmi_infoframe frm; + u8 mhl_vic[] = { 0, 95, 94, 93, 98 }; + + drm_hdmi_vendor_infoframe_from_display_mode( + &frm.vendor.hdmi, adjusted_mode); + vic = frm.vendor.hdmi.vic; + if (vic >= ARRAY_SIZE(mhl_vic)) + vic = 0; + vic = mhl_vic[vic]; + } + ctx->video_code = vic; + ctx->pixel_clock = adjusted_mode->clock; + } mutex_unlock(&ctx->lock); - return ret; } diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h index 6ff616a4f6ce..51ab540cf092 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.h +++ b/drivers/gpu/drm/bridge/sil-sii8620.h @@ -353,7 +353,7 @@ #define REG_TTXNUMB 0x0116 #define MSK_TTXNUMB_TTX_AFFCTRL_3_0 0xf0 #define BIT_TTXNUMB_TTX_COM1_AT_SYNC_WAIT BIT(3) -#define MSK_TTXNUMB_TTX_NUMBPS_2_0 0x07 +#define MSK_TTXNUMB_TTX_NUMBPS 0x07 /* TDM TX NUMSPISYM, default value: 0x04 */ #define REG_TTXSPINUMS 0x0117 @@ -403,12 +403,16 @@ /* TDM RX Status 2nd, default value: 0x00 */ #define REG_TRXSTA2 0x015c +#define MSK_TDM_SYNCHRONIZED 0xc0 +#define VAL_TDM_SYNCHRONIZED 0x80 /* TDM RX INT Low, default value: 0x00 */ #define REG_TRXINTL 0x0163 /* TDM RX INT High, default value: 0x00 */ #define REG_TRXINTH 0x0164 +#define BIT_TDM_INTR_SYNC_DATA BIT(0) +#define BIT_TDM_INTR_SYNC_WAIT BIT(1) /* TDM RX INTMASK High, default value: 0x00 */ #define REG_TRXINTMH 0x0166 @@ -429,12 +433,14 @@ /* HSIC Keeper, default value: 0x00 */ #define REG_KEEPER 0x0181 -#define MSK_KEEPER_KEEPER_MODE_1_0 0x03 +#define MSK_KEEPER_MODE 0x03 +#define VAL_KEEPER_MODE_HOST 0 +#define VAL_KEEPER_MODE_DEVICE 2 /* HSIC Flow Control General, default value: 0x02 */ #define REG_FCGC 0x0183 -#define BIT_FCGC_HSIC_FC_HOSTMODE BIT(1) -#define BIT_FCGC_HSIC_FC_ENABLE BIT(0) +#define BIT_FCGC_HSIC_HOSTMODE BIT(1) +#define BIT_FCGC_HSIC_ENABLE BIT(0) /* HSIC Flow Control CTR13, default value: 0xfc */ #define REG_FCCTR13 0x0191 @@ -841,6 +847,8 @@ #define MSK_MHL_DP_CTL7_DT_DRV_VBIAS_CASCTL 0xf0 #define MSK_MHL_DP_CTL7_DT_DRV_IREF_CTL 0x0f +#define REG_MHL_DP_CTL8 0x0352 + /* Tx Zone Ctl1, default value: 0x00 */ #define REG_TX_ZONE_CTL1 0x0361 #define VAL_TX_ZONE_CTL1_TX_ZONE_CTRL_MODE 0x08 @@ -1078,16 +1086,26 @@ /* TPI Info Frame Select, default value: 0x00 */ #define REG_TPI_INFO_FSEL 0x06bf -#define BIT_TPI_INFO_FSEL_TPI_INFO_EN BIT(7) -#define BIT_TPI_INFO_FSEL_TPI_INFO_RPT BIT(6) -#define BIT_TPI_INFO_FSEL_TPI_INFO_READ_FLAG BIT(5) -#define MSK_TPI_INFO_FSEL_TPI_INFO_SEL 0x07 +#define BIT_TPI_INFO_FSEL_EN BIT(7) +#define BIT_TPI_INFO_FSEL_RPT BIT(6) +#define BIT_TPI_INFO_FSEL_READ_FLAG BIT(5) +#define MSK_TPI_INFO_FSEL_PKT 0x07 +#define VAL_TPI_INFO_FSEL_AVI 0x00 +#define VAL_TPI_INFO_FSEL_SPD 0x01 +#define VAL_TPI_INFO_FSEL_AUD 0x02 +#define VAL_TPI_INFO_FSEL_MPG 0x03 +#define VAL_TPI_INFO_FSEL_GEN 0x04 +#define VAL_TPI_INFO_FSEL_GEN2 0x05 +#define VAL_TPI_INFO_FSEL_VSI 0x06 /* TPI Info Byte #0, default value: 0x00 */ #define REG_TPI_INFO_B0 0x06c0 /* CoC Status, default value: 0x00 */ #define REG_COC_STAT_0 0x0700 +#define BIT_COC_STAT_0_PLL_LOCKED BIT(7) +#define MSK_COC_STAT_0_FSM_STATE 0x0f + #define REG_COC_STAT_1 0x0701 #define REG_COC_STAT_2 0x0702 #define REG_COC_STAT_3 0x0703 @@ -1282,14 +1300,14 @@ /* MDT Transmit Control, default value: 0x70 */ #define REG_MDT_XMIT_CTRL 0x0588 -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_EN BIT(7) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_CMD_MERGE_EN BIT(6) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_FIXED_BURST_LEN BIT(5) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_FIXED_AID BIT(4) -#define BIT_MDT_XMIT_CTRL_MDT_XMIT_SINGLE_RUN_EN BIT(3) -#define BIT_MDT_XMIT_CTRL_MDT_CLR_ABORT_WAIT BIT(2) -#define BIT_MDT_XMIT_CTRL_MDT_XFIFO_CLR_ALL BIT(1) -#define BIT_MDT_XMIT_CTRL_MDT_XFIFO_CLR_CUR BIT(0) +#define BIT_MDT_XMIT_CTRL_EN BIT(7) +#define BIT_MDT_XMIT_CTRL_CMD_MERGE_EN BIT(6) +#define BIT_MDT_XMIT_CTRL_FIXED_BURST_LEN BIT(5) +#define BIT_MDT_XMIT_CTRL_FIXED_AID BIT(4) +#define BIT_MDT_XMIT_CTRL_SINGLE_RUN_EN BIT(3) +#define BIT_MDT_XMIT_CTRL_CLR_ABORT_WAIT BIT(2) +#define BIT_MDT_XMIT_CTRL_XFIFO_CLR_ALL BIT(1) +#define BIT_MDT_XMIT_CTRL_XFIFO_CLR_CUR BIT(0) /* MDT Receive WRITE Port, default value: 0x00 */ #define REG_MDT_XMIT_WRITE_PORT 0x0589 diff --git a/drivers/gpu/drm/cirrus/Kconfig b/drivers/gpu/drm/cirrus/Kconfig index 7f4cc6e172ab..fc78c90ee931 100644 --- a/drivers/gpu/drm/cirrus/Kconfig +++ b/drivers/gpu/drm/cirrus/Kconfig @@ -1,6 +1,6 @@ config DRM_CIRRUS_QEMU tristate "Cirrus driver for QEMU emulated device" - depends on DRM && PCI + depends on DRM && PCI && MMU select DRM_KMS_HELPER select DRM_TTM help diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 2188d6b61b3e..8690352d96f7 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -13,6 +13,7 @@ #include <video/vga.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_helper.h> #include <drm/ttm/ttm_bo_api.h> @@ -230,7 +231,7 @@ irqreturn_t cirrus_driver_irq_handler(int irq, void *arg); /* cirrus_kms.c */ int cirrus_driver_load(struct drm_device *dev, unsigned long flags); -int cirrus_driver_unload(struct drm_device *dev); +void cirrus_driver_unload(struct drm_device *dev); extern struct drm_ioctl_desc cirrus_ioctls[]; extern int cirrus_max_ioctl; diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 3a6309d7d8e4..4cc679278182 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -22,7 +22,7 @@ static void cirrus_dirty_update(struct cirrus_fbdev *afbdev, struct drm_gem_object *obj; struct cirrus_bo *bo; int src_offset, dst_offset; - int bpp = (afbdev->gfb.base.bits_per_pixel + 7)/8; + int bpp = afbdev->gfb.base.format->cpp[0]; int ret = -EBUSY; bool unmap = false; bool store_for_later = false; @@ -218,7 +218,7 @@ static int cirrusfb_create(struct drm_fb_helper *helper, info->flags = FBINFO_DEFAULT; info->fbops = &cirrusfb_ops; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &gfbdev->helper, sizes->fb_width, sizes->fb_height); @@ -238,7 +238,7 @@ static int cirrusfb_create(struct drm_fb_helper *helper, DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start); DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len); - DRM_INFO("fb depth is %d\n", fb->depth); + DRM_INFO("fb depth is %d\n", fb->format->depth); DRM_INFO(" pitch is %d\n", fb->pitches[0]); return 0; @@ -289,7 +289,7 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) &cirrus_fb_helper_funcs); ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, - cdev->num_crtc, CIRRUSFB_CONN_LIMIT); + CIRRUSFB_CONN_LIMIT); if (ret) return ret; diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 2c3c0d4072ce..e7fc95f63dca 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -34,7 +34,7 @@ int cirrus_framebuffer_init(struct drm_device *dev, { int ret; - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &gfb->base, mode_cmd); gfb->obj = obj; ret = drm_framebuffer_init(dev, &gfb->base, &cirrus_fb_funcs); if (ret) { @@ -208,18 +208,17 @@ out: return r; } -int cirrus_driver_unload(struct drm_device *dev) +void cirrus_driver_unload(struct drm_device *dev) { struct cirrus_device *cdev = dev->dev_private; if (cdev == NULL) - return 0; + return; cirrus_modeset_fini(cdev); cirrus_mm_fini(cdev); cirrus_device_fini(cdev); kfree(cdev); dev->dev_private = NULL; - return 0; } int cirrus_gem_create(struct drm_device *dev, diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 9a4a27c1afd2..ed43ab10ac99 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -185,6 +185,7 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct cirrus_device *cdev = dev->dev_private; + const struct drm_framebuffer *fb = crtc->primary->fb; int hsyncstart, hsyncend, htotal, hdispend; int vtotal, vdispend; int tmp; @@ -257,7 +258,7 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc, sr07 = RREG8(SEQ_DATA); sr07 &= 0xe0; hdr = 0; - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: sr07 |= 0x11; break; @@ -280,13 +281,13 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc, WREG_SEQ(0x7, sr07); /* Program the pitch */ - tmp = crtc->primary->fb->pitches[0] / 8; + tmp = fb->pitches[0] / 8; WREG_CRT(VGA_CRTC_OFFSET, tmp); /* Enable extended blanking and pitch bits, and enable full memory */ tmp = 0x22; - tmp |= (crtc->primary->fb->pitches[0] >> 7) & 0x10; - tmp |= (crtc->primary->fb->pitches[0] >> 6) & 0x40; + tmp |= (fb->pitches[0] >> 7) & 0x10; + tmp |= (fb->pitches[0] >> 6) & 0x40; WREG_CRT(0x1b, tmp); /* Enable high-colour modes */ diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index d6da848f7c6f..f53aa8f4a143 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -236,8 +236,6 @@ struct ttm_bo_driver cirrus_bo_driver = { .verify_access = cirrus_bo_verify_access, .io_mem_reserve = &cirrus_ttm_io_mem_reserve, .io_mem_free = &cirrus_ttm_io_mem_free, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int cirrus_mm_init(struct cirrus_device *cirrus) diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c index d621c8a4cf00..c89953449e96 100644 --- a/drivers/gpu/drm/drm_agpsupport.c +++ b/drivers/gpu/drm/drm_agpsupport.c @@ -421,6 +421,8 @@ struct drm_agp_head *drm_agp_init(struct drm_device *dev) head->base = head->agp_info.aper_base; return head; } +/* Only exported for i810.ko */ +EXPORT_SYMBOL(drm_agp_init); /** * drm_legacy_agp_clear - Clear AGP resource list diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index fdfb1ec17e66..a5673107db26 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -35,19 +35,14 @@ #include "drm_crtc_internal.h" -static void crtc_commit_free(struct kref *kref) +void __drm_crtc_commit_free(struct kref *kref) { struct drm_crtc_commit *commit = container_of(kref, struct drm_crtc_commit, ref); kfree(commit); } - -void drm_crtc_commit_put(struct drm_crtc_commit *commit) -{ - kref_put(&commit->ref, crtc_commit_free); -} -EXPORT_SYMBOL(drm_crtc_commit_put); +EXPORT_SYMBOL(__drm_crtc_commit_free); /** * drm_atomic_state_default_release - @@ -200,8 +195,8 @@ EXPORT_SYMBOL(drm_atomic_state_default_clear); * all locks. So someone else could sneak in and change the current modeset * configuration. Which means that all the state assembled in @state is no * longer an atomic update to the current state, but to some arbitrary earlier - * state. Which could break assumptions the driver's ->atomic_check likely - * relies on. + * state. Which could break assumptions the driver's + * &drm_mode_config_funcs.atomic_check likely relies on. * * Hence we must clear all cached state and completely start over, using this * function. @@ -312,9 +307,8 @@ static s32 __user *get_out_fence_for_crtc(struct drm_atomic_state *state, * @state: the CRTC whose incoming state to update * @mode: kernel-internal mode to use for the CRTC, or NULL to disable * - * Set a mode (originating from the kernel) on the desired CRTC state. Does - * not change any other state properties, including enable, active, or - * mode_changed. + * Set a mode (originating from the kernel) on the desired CRTC state and update + * the enable property. * * RETURNS: * Zero on success, error code on failure. Cannot return -EDEADLK. @@ -461,11 +455,10 @@ drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc, * @property: the property to set * @val: the new property value * - * Use this instead of calling crtc->atomic_set_property directly. - * This function handles generic/core properties and calls out to - * driver's ->atomic_set_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. + * This function handles generic/core properties and calls out to driver's + * &drm_crtc_funcs.atomic_set_property for driver properties. To ensure + * consistent behavior you must call this function rather than the driver hook + * directly. * * RETURNS: * Zero on success, error code on failure @@ -537,10 +530,10 @@ EXPORT_SYMBOL(drm_atomic_crtc_set_property); * @property: the property to set * @val: return location for the property value * - * This function handles generic/core properties and calls out to - * driver's ->atomic_get_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. + * This function handles generic/core properties and calls out to driver's + * &drm_crtc_funcs.atomic_get_property for driver properties. To ensure + * consistent behavior you must call this function rather than the driver hook + * directly. * * RETURNS: * Zero on success, error code on failure @@ -721,11 +714,10 @@ EXPORT_SYMBOL(drm_atomic_get_plane_state); * @property: the property to set * @val: the new property value * - * Use this instead of calling plane->atomic_set_property directly. - * This function handles generic/core properties and calls out to - * driver's ->atomic_set_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. + * This function handles generic/core properties and calls out to driver's + * &drm_plane_funcs.atomic_set_property for driver properties. To ensure + * consistent behavior you must call this function rather than the driver hook + * directly. * * RETURNS: * Zero on success, error code on failure @@ -796,10 +788,10 @@ EXPORT_SYMBOL(drm_atomic_plane_set_property); * @property: the property to set * @val: return location for the property value * - * This function handles generic/core properties and calls out to - * driver's ->atomic_get_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. + * This function handles generic/core properties and calls out to driver's + * &drm_plane_funcs.atomic_get_property for driver properties. To ensure + * consistent behavior you must call this function rather than the driver hook + * directly. * * RETURNS: * Zero on success, error code on failure @@ -902,11 +894,11 @@ static int drm_atomic_plane_check(struct drm_plane *plane, } /* Check whether this plane supports the fb pixel format. */ - ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format); + ret = drm_plane_check_pixel_format(plane, state->fb->format->format); if (ret) { struct drm_format_name_buf format_name; DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", - drm_get_format_name(state->fb->pixel_format, + drm_get_format_name(state->fb->format->format, &format_name)); return ret; } @@ -960,11 +952,11 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, drm_printf(p, "\tfb=%u\n", state->fb ? state->fb->base.id : 0); if (state->fb) { struct drm_framebuffer *fb = state->fb; - int i, n = drm_format_num_planes(fb->pixel_format); + int i, n = fb->format->num_planes; struct drm_format_name_buf format_name; drm_printf(p, "\t\tformat=%s\n", - drm_get_format_name(fb->pixel_format, &format_name)); + drm_get_format_name(fb->format->format, &format_name)); drm_printf(p, "\t\t\tmodifier=0x%llx\n", fb->modifier); drm_printf(p, "\t\tsize=%dx%d\n", fb->width, fb->height); drm_printf(p, "\t\tlayers:\n"); @@ -1062,11 +1054,10 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); * @property: the property to set * @val: the new property value * - * Use this instead of calling connector->atomic_set_property directly. - * This function handles generic/core properties and calls out to - * driver's ->atomic_set_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. + * This function handles generic/core properties and calls out to driver's + * &drm_connector_funcs.atomic_set_property for driver properties. To ensure + * consistent behavior you must call this function rather than the driver hook + * directly. * * RETURNS: * Zero on success, error code on failure @@ -1141,10 +1132,10 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, * @property: the property to set * @val: return location for the property value * - * This function handles generic/core properties and calls out to - * driver's ->atomic_get_property() for driver properties. To ensure - * consistent behavior you must call this function rather than the - * driver hook directly. + * This function handles generic/core properties and calls out to driver's + * &drm_connector_funcs.atomic_get_property for driver properties. To ensure + * consistent behavior you must call this function rather than the driver hook + * directly. * * RETURNS: * Zero on success, error code on failure @@ -1317,12 +1308,11 @@ EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); * implicit or explicit fencing. * * This function will not set the fence to the state if it was set - * via explicit fencing interfaces on the atomic ioctl. It will - * all drope the reference to the fence as we not storing it - * anywhere. - * - * Otherwise, if plane_state->fence is not set this function we - * just set it with the received implict fence. + * via explicit fencing interfaces on the atomic ioctl. In that case it will + * drop the reference to the fence as we are not storing it anywhere. + * Otherwise, if &drm_plane_state.fence is not set this function we just set it + * with the received implicit fence. In both cases this function consumes a + * reference for @fence. */ void drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state, @@ -1417,6 +1407,7 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state, struct drm_mode_config *config = &state->dev->mode_config; struct drm_connector *connector; struct drm_connector_state *conn_state; + struct drm_connector_list_iter conn_iter; int ret; ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx); @@ -1430,14 +1421,18 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state, * Changed connectors are already in @state, so only need to look at the * current configuration. */ - drm_for_each_connector(connector, state->dev) { + drm_connector_list_iter_get(state->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->state->crtc != crtc) continue; conn_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(conn_state)) + if (IS_ERR(conn_state)) { + drm_connector_list_iter_put(&conn_iter); return PTR_ERR(conn_state); + } } + drm_connector_list_iter_put(&conn_iter); return 0; } @@ -1594,10 +1589,8 @@ EXPORT_SYMBOL(drm_atomic_check_only); * more locks but encountered a deadlock. The caller must then do the usual w/w * backoff dance and restart. All other errors are fatal. * - * Also note that on successful execution ownership of @state is transferred - * from the caller of this function to the function itself. The caller must not - * free or in any other way access @state. If the function fails then the caller - * must clean up @state itself. + * This function will take its own reference on @state. + * Callers should always release their reference with drm_atomic_state_put(). * * Returns: * 0 on success, negative error code on failure. @@ -1618,17 +1611,15 @@ int drm_atomic_commit(struct drm_atomic_state *state) EXPORT_SYMBOL(drm_atomic_commit); /** - * drm_atomic_nonblocking_commit - atomic&nonblocking configuration commit + * drm_atomic_nonblocking_commit - atomic nonblocking commit * @state: atomic configuration to check * * Note that this function can return -EDEADLK if the driver needed to acquire * more locks but encountered a deadlock. The caller must then do the usual w/w * backoff dance and restart. All other errors are fatal. * - * Also note that on successful execution ownership of @state is transferred - * from the caller of this function to the function itself. The caller must not - * free or in any other way access @state. If the function fails then the caller - * must clean up @state itself. + * This function will take its own reference on @state. + * Callers should always release their reference with drm_atomic_state_put(). * * Returns: * 0 on success, negative error code on failure. @@ -1692,6 +1683,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p) struct drm_plane *plane; struct drm_crtc *crtc; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) return; @@ -1702,8 +1694,10 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p) list_for_each_entry(crtc, &config->crtc_list, head) drm_atomic_crtc_print_state(p, crtc->state); - list_for_each_entry(connector, &config->connector_list, head) + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) drm_atomic_connector_print_state(p, connector->state); + drm_connector_list_iter_put(&conn_iter); } EXPORT_SYMBOL(drm_state_dump); @@ -1732,13 +1726,6 @@ int drm_atomic_debugfs_init(struct drm_minor *minor) ARRAY_SIZE(drm_atomic_debugfs_list), minor->debugfs_root, minor); } - -int drm_atomic_debugfs_cleanup(struct drm_minor *minor) -{ - return drm_debugfs_remove_files(drm_atomic_debugfs_list, - ARRAY_SIZE(drm_atomic_debugfs_list), - minor); -} #endif /* @@ -1830,10 +1817,10 @@ static int atomic_set_prop(struct drm_atomic_state *state, * @plane_mask: plane mask for planes that were updated. * @ret: return value, can be -EDEADLK for a retry. * - * Before doing an update plane->old_fb is set to plane->fb, - * but before dropping the locks old_fb needs to be set to NULL - * and plane->fb updated. This is a common operation for each - * atomic update, so this call is split off as a helper. + * Before doing an update &drm_plane.old_fb is set to &drm_plane.fb, but before + * dropping the locks old_fb needs to be set to NULL and plane->fb updated. This + * is a common operation for each atomic update, so this call is split off as a + * helper. */ void drm_atomic_clean_old_fb(struct drm_device *dev, unsigned plane_mask, @@ -1874,7 +1861,7 @@ EXPORT_SYMBOL(drm_atomic_clean_old_fb); * As a contrast, with implicit fencing the kernel keeps track of any * ongoing rendering, and automatically ensures that the atomic update waits * for any pending rendering to complete. For shared buffers represented with - * a struct &dma_buf this is tracked in &reservation_object structures. + * a &struct dma_buf this is tracked in &struct reservation_object. * Implicit syncing is how Linux traditionally worked (e.g. DRI2/3 on X.org), * whereas explicit fencing is what Android wants. * @@ -1890,7 +1877,7 @@ EXPORT_SYMBOL(drm_atomic_clean_old_fb); * it will only check if the Sync File is a valid one. * * On the driver side the fence is stored on the @fence parameter of - * struct &drm_plane_state. Drivers which also support implicit fencing + * &struct drm_plane_state. Drivers which also support implicit fencing * should set the implicit fence using drm_atomic_set_fence_for_plane(), * to make sure there's consistent behaviour between drivers in precedence * of implicit vs. explicit fencing. @@ -1909,7 +1896,7 @@ EXPORT_SYMBOL(drm_atomic_clean_old_fb); * DRM_MODE_ATOMIC_TEST_ONLY flag the out fence will also be set to -1. * * Note that out-fences don't have a special interface to drivers and are - * internally represented by a struct &drm_pending_vblank_event in struct + * internally represented by a &struct drm_pending_vblank_event in struct * &drm_crtc_state, which is also used by the nonblocking atomic commit * helpers and for the DRM event handling for existing userspace. */ @@ -2198,10 +2185,6 @@ retry: goto out; if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) { - /* - * Unlike commit, check_only does not clean up state. - * Below we call drm_atomic_state_put for it. - */ ret = drm_atomic_check_only(state); } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { ret = drm_atomic_nonblocking_commit(state); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 4594477dee00..01d936b7be43 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -56,9 +56,9 @@ * implement these functions themselves but must use the provided helpers. * * The atomic helper uses the same function table structures as all other - * modesetting helpers. See the documentation for struct &drm_crtc_helper_funcs, - * struct &drm_encoder_helper_funcs and struct &drm_connector_helper_funcs. It - * also shares the struct &drm_plane_helper_funcs function table with the plane + * modesetting helpers. See the documentation for &struct drm_crtc_helper_funcs, + * struct &drm_encoder_helper_funcs and &struct drm_connector_helper_funcs. It + * also shares the &struct drm_plane_helper_funcs function table with the plane * helpers. */ static void @@ -94,9 +94,10 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, { struct drm_connector_state *conn_state; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; struct drm_encoder *encoder; unsigned encoder_mask = 0; - int i, ret; + int i, ret = 0; /* * First loop, find all newly assigned encoders from the connectors @@ -144,7 +145,8 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, * and the crtc is disabled if no encoder is left. This preserves * compatibility with the legacy set_config behavior. */ - drm_for_each_connector(connector, state->dev) { + drm_connector_list_iter_get(state->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { struct drm_crtc_state *crtc_state; if (drm_atomic_get_existing_connector_state(state, connector)) @@ -160,12 +162,15 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, connector->state->crtc->base.id, connector->state->crtc->name, connector->base.id, connector->name); - return -EINVAL; + ret = -EINVAL; + goto out; } conn_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(conn_state)) - return PTR_ERR(conn_state); + if (IS_ERR(conn_state)) { + ret = PTR_ERR(conn_state); + goto out; + } DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], disabling [CONNECTOR:%d:%s]\n", encoder->base.id, encoder->name, @@ -176,19 +181,21 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state, ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); if (ret) - return ret; + goto out; if (!crtc_state->connector_mask) { ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL); if (ret < 0) - return ret; + goto out; crtc_state->active = false; } } +out: + drm_connector_list_iter_put(&conn_iter); - return 0; + return ret; } static void @@ -362,7 +369,7 @@ mode_fixup(struct drm_atomic_state *state) struct drm_connector *connector; struct drm_connector_state *conn_state; int i; - bool ret; + int ret; for_each_crtc_in_state(state, crtc, crtc_state, i) { if (!crtc_state->mode_changed && @@ -451,22 +458,25 @@ mode_fixup(struct drm_atomic_state *state) * Check the state object to see if the requested state is physically possible. * This does all the crtc and connector related computations for an atomic * update and adds any additional connectors needed for full modesets and calls - * down into ->mode_fixup functions of the driver backend. - * - * crtc_state->mode_changed is set when the input mode is changed. - * crtc_state->connectors_changed is set when a connector is added or - * removed from the crtc. - * crtc_state->active_changed is set when crtc_state->active changes, - * which is used for dpms. + * down into &drm_crtc_helper_funcs.mode_fixup and + * &drm_encoder_helper_funcs.mode_fixup or + * &drm_encoder_helper_funcs.atomic_check functions of the driver backend. + * + * &drm_crtc_state.mode_changed is set when the input mode is changed. + * &drm_crtc_state.connectors_changed is set when a connector is added or + * removed from the crtc. &drm_crtc_state.active_changed is set when + * &drm_crtc_state.active changes, which is used for DPMS. * See also: drm_atomic_crtc_needs_modeset() * * IMPORTANT: * - * Drivers which set ->mode_changed (e.g. in their ->atomic_check hooks if a - * plane update can't be done without a full modeset) _must_ call this function - * afterwards after that change. It is permitted to call this function multiple - * times for the same update, e.g. when the ->atomic_check functions depend upon - * the adjusted dotclock for fifo space allocation and watermark computation. + * Drivers which set &drm_crtc_state.mode_changed (e.g. in their + * &drm_plane_helper_funcs.atomic_check hooks if a plane update can't be done + * without a full modeset) _must_ call this function afterwards after that + * change. It is permitted to call this function multiple times for the same + * update, e.g. when the &drm_crtc_helper_funcs.atomic_check functions depend + * upon the adjusted dotclock for fifo space allocation and watermark + * computation. * * RETURNS: * Zero for success or -errno @@ -577,9 +587,10 @@ EXPORT_SYMBOL(drm_atomic_helper_check_modeset); * * Check the state object to see if the requested state is physically possible. * This does all the plane update related checks using by calling into the - * ->atomic_check hooks provided by the driver. + * &drm_crtc_helper_funcs.atomic_check and &drm_plane_helper_funcs.atomic_check + * hooks provided by the driver. * - * It also sets crtc_state->planes_changed to indicate that a crtc has + * It also sets &drm_crtc_state.planes_changed to indicate that a crtc has * updated planes. * * RETURNS: @@ -641,14 +652,15 @@ EXPORT_SYMBOL(drm_atomic_helper_check_planes); * Check the state object to see if the requested state is physically possible. * Only crtcs and planes have check callbacks, so for any additional (global) * checking that a driver needs it can simply wrap that around this function. - * Drivers without such needs can directly use this as their ->atomic_check() - * callback. + * Drivers without such needs can directly use this as their + * &drm_mode_config_funcs.atomic_check callback. * * This just wraps the two parts of the state checking for planes and modeset * state in the default order: First it calls drm_atomic_helper_check_modeset() * and then drm_atomic_helper_check_planes(). The assumption is that the - * ->atomic_check functions depend upon an updated adjusted_mode.clock to - * e.g. properly compute watermarks. + * @drm_plane_helper_funcs.atomic_check and @drm_crtc_helper_funcs.atomic_check + * functions depend upon an updated adjusted_mode.clock to e.g. properly compute + * watermarks. * * RETURNS: * Zero for success or -errno @@ -1058,41 +1070,6 @@ int drm_atomic_helper_wait_for_fences(struct drm_device *dev, EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences); /** - * drm_atomic_helper_framebuffer_changed - check if framebuffer has changed - * @dev: DRM device - * @old_state: atomic state object with old state structures - * @crtc: DRM crtc - * - * Checks whether the framebuffer used for this CRTC changes as a result of - * the atomic update. This is useful for drivers which cannot use - * drm_atomic_helper_wait_for_vblanks() and need to reimplement its - * functionality. - * - * Returns: - * true if the framebuffer changed. - */ -bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev, - struct drm_atomic_state *old_state, - struct drm_crtc *crtc) -{ - struct drm_plane *plane; - struct drm_plane_state *old_plane_state; - int i; - - for_each_plane_in_state(old_state, plane, old_plane_state, i) { - if (plane->state->crtc != crtc && - old_plane_state->crtc != crtc) - continue; - - if (plane->state->fb != old_plane_state->fb) - return true; - } - - return false; -} -EXPORT_SYMBOL(drm_atomic_helper_framebuffer_changed); - -/** * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs * @dev: DRM device * @old_state: atomic state object with old state structures @@ -1110,39 +1087,35 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; int i, ret; + unsigned crtc_mask = 0; - for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - /* No one cares about the old state, so abuse it for tracking - * and store whether we hold a vblank reference (and should do a - * vblank wait) in the ->enable boolean. */ - old_crtc_state->enable = false; - - if (!crtc->state->enable) - continue; + /* + * Legacy cursor ioctls are completely unsynced, and userspace + * relies on that (by doing tons of cursor updates). + */ + if (old_state->legacy_cursor_update) + return; - /* Legacy cursor ioctls are completely unsynced, and userspace - * relies on that (by doing tons of cursor updates). */ - if (old_state->legacy_cursor_update) - continue; + for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + struct drm_crtc_state *new_crtc_state = crtc->state; - if (!drm_atomic_helper_framebuffer_changed(dev, - old_state, crtc)) + if (!new_crtc_state->active || !new_crtc_state->planes_changed) continue; ret = drm_crtc_vblank_get(crtc); if (ret != 0) continue; - old_crtc_state->enable = true; - old_crtc_state->last_vblank_count = drm_crtc_vblank_count(crtc); + crtc_mask |= drm_crtc_mask(crtc); + old_state->crtcs[i].last_vblank_count = drm_crtc_vblank_count(crtc); } for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { - if (!old_crtc_state->enable) + if (!(crtc_mask & drm_crtc_mask(crtc))) continue; ret = wait_event_timeout(dev->vblank[i].queue, - old_crtc_state->last_vblank_count != + old_state->crtcs[i].last_vblank_count != drm_crtc_vblank_count(crtc), msecs_to_jiffies(50)); @@ -1157,8 +1130,8 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); * drm_atomic_helper_commit_tail - commit atomic update to hardware * @old_state: atomic state object with old state structures * - * This is the default implemenation for the ->atomic_commit_tail() hook of the - * &drm_mode_config_helper_funcs vtable. + * This is the default implementation for the + * &drm_mode_config_helper_funcs.atomic_commit_tail hook. * * Note that the default ordering of how the various stages are called is to * match the legacy modeset helper library closest. One peculiarity of that is @@ -1235,8 +1208,8 @@ static void commit_work(struct work_struct *work) * drm_atomic_helper_setup_commit() and related functions. * * Committing the actual hardware state is done through the - * ->atomic_commit_tail() callback of the &drm_mode_config_helper_funcs vtable, - * or it's default implementation drm_atomic_helper_commit_tail(). + * &drm_mode_config_helper_funcs.atomic_commit_tail callback, or it's default + * implementation drm_atomic_helper_commit_tail(). * * RETURNS: * Zero for success or -errno. @@ -1389,6 +1362,15 @@ static int stall_checks(struct drm_crtc *crtc, bool nonblock) return ret < 0 ? ret : 0; } +static void release_crtc_commit(struct completion *completion) +{ + struct drm_crtc_commit *commit = container_of(completion, + typeof(*commit), + flip_done); + + drm_crtc_commit_put(commit); +} + /** * drm_atomic_helper_setup_commit - setup possibly nonblocking commit * @state: new modeset state to be committed @@ -1396,14 +1378,15 @@ static int stall_checks(struct drm_crtc *crtc, bool nonblock) * * This function prepares @state to be used by the atomic helper's support for * nonblocking commits. Drivers using the nonblocking commit infrastructure - * should always call this function from their ->atomic_commit hook. + * should always call this function from their + * &drm_mode_config_funcs.atomic_commit hook. * * To be able to use this support drivers need to use a few more helper * functions. drm_atomic_helper_wait_for_dependencies() must be called before * actually committing the hardware state, and for nonblocking commits this call * must be placed in the async worker. See also drm_atomic_helper_swap_state() * and it's stall parameter, for when a driver's commit hooks look at the - * ->state pointers of struct &drm_crtc, &drm_plane or &drm_connector directly. + * &drm_crtc.state, &drm_plane.state or &drm_connector.state pointer directly. * * Completion of the hardware commit step must be signalled using * drm_atomic_helper_commit_hw_done(). After this step the driver is not allowed @@ -1481,6 +1464,8 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, } crtc_state->event->base.completion = &commit->flip_done; + crtc_state->event->base.completion_release = release_crtc_commit; + drm_crtc_commit_get(commit); } return 0; @@ -1510,8 +1495,7 @@ static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc) * This function waits for all preceeding commits that touch the same CRTC as * @old_state to both be committed to the hardware (as signalled by * drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled - * by calling drm_crtc_vblank_send_event on the event member of - * &drm_crtc_state). + * by calling drm_crtc_vblank_send_event() on the &drm_crtc_state.event). * * This is part of the atomic helper support for nonblocking commits, see * drm_atomic_helper_setup_commit() for an overview. @@ -1648,8 +1632,9 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); * @state: atomic state object with new state structures * * This function prepares plane state, specifically framebuffers, for the new - * configuration. If any failure is encountered this function will call - * ->cleanup_fb on any already successfully prepared framebuffer. + * configuration, by calling &drm_plane_helper_funcs.prepare_fb. If any failure + * is encountered this function will call &drm_plane_helper_funcs.cleanup_fb on + * any already successfully prepared framebuffer. * * Returns: * 0 on success, negative error code on failure. @@ -1729,10 +1714,10 @@ static bool plane_crtc_active(const struct drm_plane_state *state) * * Drivers may set the NO_DISABLE_AFTER_MODESET flag in @flags if the relevant * display controllers require to disable a CRTC's planes when the CRTC is - * disabled. This function would skip the ->atomic_disable call for a plane if - * the CRTC of the old plane state needs a modesetting operation. Of course, - * the drivers need to disable the planes in their CRTC disable callbacks - * since no one else would do that. + * disabled. This function would skip the &drm_plane_helper_funcs.atomic_disable + * call for a plane if the CRTC of the old plane state needs a modesetting + * operation. Of course, the drivers need to disable the planes in their CRTC + * disable callbacks since no one else would do that. * * The drm_atomic_helper_commit() default implementation doesn't set the * ACTIVE_ONLY flag to most closely match the behaviour of the legacy helpers. @@ -1895,7 +1880,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); * planes. * * It is a bug to call this function without having implemented the - * ->atomic_disable() plane hook. + * &drm_plane_helper_funcs.atomic_disable plane hook. */ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state, @@ -1982,8 +1967,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); * contains the old state. Also do any other cleanup required with that state. * * @stall must be set when nonblocking commits for this driver directly access - * the ->state pointer of &drm_plane, &drm_crtc or &drm_connector. With the - * current atomic helpers this is almost always the case, since the helpers + * the &drm_plane.state, &drm_crtc.state or &drm_connector.state pointer. With + * the current atomic helpers this is almost always the case, since the helpers * don't pass the right state structures to the callbacks. */ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, @@ -2384,7 +2369,7 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set, if (ret != 0) return ret; - drm_crtc_get_hv_timing(set->mode, &hdisplay, &vdisplay); + drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay); drm_atomic_set_fb_for_plane(primary_state, set->fb); primary_state->crtc_x = 0; @@ -2435,6 +2420,7 @@ int drm_atomic_helper_disable_all(struct drm_device *dev, { struct drm_atomic_state *state; struct drm_connector *conn; + struct drm_connector_list_iter conn_iter; int err; state = drm_atomic_state_alloc(dev); @@ -2443,7 +2429,8 @@ int drm_atomic_helper_disable_all(struct drm_device *dev, state->acquire_ctx = ctx; - drm_for_each_connector(conn, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(conn, &conn_iter) { struct drm_crtc *crtc = conn->state->crtc; struct drm_crtc_state *crtc_state; @@ -2461,6 +2448,7 @@ int drm_atomic_helper_disable_all(struct drm_device *dev, err = drm_atomic_commit(state); free: + drm_connector_list_iter_put(&conn_iter); drm_atomic_state_put(state); return err; } @@ -2726,6 +2714,44 @@ backoff: } EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); +static int page_flip_common( + struct drm_atomic_state *state, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct drm_plane *plane = crtc->primary; + struct drm_plane_state *plane_state; + struct drm_crtc_state *crtc_state; + int ret = 0; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + crtc_state->event = event; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + if (ret != 0) + return ret; + drm_atomic_set_fb_for_plane(plane_state, fb); + + /* Make sure we don't accidentally do a full modeset. */ + state->allow_modeset = false; + if (!crtc_state->active) { + DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n", + crtc->base.id); + return -EINVAL; + } + + return ret; +} + /** * drm_atomic_helper_page_flip - execute a legacy page flip * @crtc: DRM crtc @@ -2733,7 +2759,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); * @event: optional DRM event to signal upon completion * @flags: flip flags for non-vblank sync'ed updates * - * Provides a default page flip implementation using the atomic driver interface. + * Provides a default &drm_crtc_funcs.page_flip implementation + * using the atomic driver interface. * * Note that for now so called async page flips (i.e. updates which are not * synchronized to vblank) are not supported, since the atomic interfaces have @@ -2741,6 +2768,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); * * Returns: * Returns 0 on success, negative errno numbers on failure. + * + * See also: + * drm_atomic_helper_page_flip_target() */ int drm_atomic_helper_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, @@ -2749,8 +2779,6 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc, { struct drm_plane *plane = crtc->primary; struct drm_atomic_state *state; - struct drm_plane_state *plane_state; - struct drm_crtc_state *crtc_state; int ret = 0; if (flags & DRM_MODE_PAGE_FLIP_ASYNC) @@ -2761,35 +2789,86 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc, return -ENOMEM; state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + retry: - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); + ret = page_flip_common(state, crtc, fb, event); + if (ret != 0) goto fail; - } - crtc_state->event = event; - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto fail; - } + ret = drm_atomic_nonblocking_commit(state); - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_put(state); + return ret; + +backoff: + drm_atomic_state_clear(state); + drm_atomic_legacy_backoff(state); + + /* + * Someone might have exchanged the framebuffer while we dropped locks + * in the backoff code. We need to fix up the fb refcount tracking the + * core does for us. + */ + plane->old_fb = plane->fb; + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_page_flip); + +/** + * drm_atomic_helper_page_flip_target - do page flip on target vblank period. + * @crtc: DRM crtc + * @fb: DRM framebuffer + * @event: optional DRM event to signal upon completion + * @flags: flip flags for non-vblank sync'ed updates + * @target: specifying the target vblank period when the flip to take effect + * + * Provides a default &drm_crtc_funcs.page_flip_target implementation. + * Similar to drm_atomic_helper_page_flip() with extra parameter to specify + * target vblank period to flip. + * + * Returns: + * Returns 0 on success, negative errno numbers on failure. + */ +int drm_atomic_helper_page_flip_target( + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags, + uint32_t target) +{ + struct drm_plane *plane = crtc->primary; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + int ret = 0; + + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) + return -EINVAL; + + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + +retry: + ret = page_flip_common(state, crtc, fb, event); if (ret != 0) goto fail; - drm_atomic_set_fb_for_plane(plane_state, fb); - /* Make sure we don't accidentally do a full modeset. */ - state->allow_modeset = false; - if (!crtc_state->active) { - DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n", - crtc->base.id); + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + if (WARN_ON(!crtc_state)) { ret = -EINVAL; goto fail; } + crtc_state->target_vblank = target; ret = drm_atomic_nonblocking_commit(state); + fail: if (ret == -EDEADLK) goto backoff; @@ -2810,7 +2889,7 @@ backoff: goto retry; } -EXPORT_SYMBOL(drm_atomic_helper_page_flip); +EXPORT_SYMBOL(drm_atomic_helper_page_flip_target); /** * drm_atomic_helper_connector_dpms() - connector dpms helper implementation @@ -2819,8 +2898,8 @@ EXPORT_SYMBOL(drm_atomic_helper_page_flip); * * This is the main helper function provided by the atomic helper framework for * implementing the legacy DPMS connector interface. It computes the new desired - * ->active state for the corresponding CRTC (if the connector is enabled) and - * updates it. + * &drm_crtc_state.active state for the corresponding CRTC (if the connector is + * enabled) and updates it. * * Returns: * Returns 0 on success, negative errno numbers on failure. @@ -2833,6 +2912,7 @@ int drm_atomic_helper_connector_dpms(struct drm_connector *connector, struct drm_crtc_state *crtc_state; struct drm_crtc *crtc; struct drm_connector *tmp_connector; + struct drm_connector_list_iter conn_iter; int ret; bool active = false; int old_mode = connector->dpms; @@ -2860,7 +2940,8 @@ retry: WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); - drm_for_each_connector(tmp_connector, connector->dev) { + drm_connector_list_iter_get(connector->dev, &conn_iter); + drm_for_each_connector_iter(tmp_connector, &conn_iter) { if (tmp_connector->state->crtc != crtc) continue; @@ -2869,6 +2950,7 @@ retry: break; } } + drm_connector_list_iter_put(&conn_iter); crtc_state->active = active; ret = drm_atomic_commit(state); @@ -2889,11 +2971,11 @@ backoff: EXPORT_SYMBOL(drm_atomic_helper_connector_dpms); /** - * drm_atomic_helper_best_encoder - Helper for &drm_connector_helper_funcs - * ->best_encoder callback + * drm_atomic_helper_best_encoder - Helper for + * &drm_connector_helper_funcs.best_encoder callback * @connector: Connector control structure * - * This is a &drm_connector_helper_funcs ->best_encoder callback helper for + * This is a &drm_connector_helper_funcs.best_encoder callback helper for * connectors that support exactly 1 encoder, statically determined at driver * init time. */ @@ -2927,7 +3009,7 @@ EXPORT_SYMBOL(drm_atomic_helper_best_encoder); */ /** - * drm_atomic_helper_crtc_reset - default ->reset hook for CRTCs + * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs * @crtc: drm CRTC * * Resets the atomic state for @crtc by freeing the state pointer (which might @@ -3034,7 +3116,7 @@ void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); /** - * drm_atomic_helper_plane_reset - default ->reset hook for planes + * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes * @plane: drm plane * * Resets the atomic state for @plane by freeing the state pointer (which might @@ -3138,8 +3220,9 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); * @conn_state: connector state to assign * * Initializes the newly allocated @conn_state and assigns it to - * #connector ->state, usually required when initializing the drivers - * or when called from the ->reset hook. + * the &drm_conector->state pointer of @connector, usually required when + * initializing the drivers or when called from the &drm_connector_funcs.reset + * hook. * * This is useful for drivers that subclass the connector state. */ @@ -3155,7 +3238,7 @@ __drm_atomic_helper_connector_reset(struct drm_connector *connector, EXPORT_SYMBOL(__drm_atomic_helper_connector_reset); /** - * drm_atomic_helper_connector_reset - default ->reset hook for connectors + * drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors * @connector: drm connector * * Resets the atomic state for @connector by freeing the state pointer (which @@ -3246,6 +3329,7 @@ drm_atomic_helper_duplicate_state(struct drm_device *dev, { struct drm_atomic_state *state; struct drm_connector *conn; + struct drm_connector_list_iter conn_iter; struct drm_plane *plane; struct drm_crtc *crtc; int err = 0; @@ -3276,15 +3360,18 @@ drm_atomic_helper_duplicate_state(struct drm_device *dev, } } - drm_for_each_connector(conn, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(conn, &conn_iter) { struct drm_connector_state *conn_state; conn_state = drm_atomic_get_connector_state(state, conn); if (IS_ERR(conn_state)) { err = PTR_ERR(conn_state); + drm_connector_list_iter_put(&conn_iter); goto free; } } + drm_connector_list_iter_put(&conn_iter); /* clear the acquire context so that it isn't accidentally reused */ state->acquire_ctx = NULL; @@ -3310,11 +3397,6 @@ EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); void __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state) { - /* - * This is currently a placeholder so that drivers that subclass the - * state will automatically do the right thing if code is ever added - * to this function. - */ if (state->crtc) drm_connector_unreference(state->connector); } diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 6b143514a566..7ff697389d74 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -35,13 +35,13 @@ /** * DOC: master and authentication * - * struct &drm_master is used to track groups of clients with open - * primary/legacy device nodes. For every struct &drm_file which has had at + * &struct drm_master is used to track groups of clients with open + * primary/legacy device nodes. For every &struct drm_file which has had at * least once successfully became the device master (either through the * SET_MASTER IOCTL, or implicitly through opening the primary device node when * no one else is the current master that time) there exists one &drm_master. - * This is noted in the is_master member of &drm_file. All other clients have - * just a pointer to the &drm_master they are associated with. + * This is noted in &drm_file.is_master. All other clients have just a pointer + * to the &drm_master they are associated with. * * In addition only one &drm_master can be the current master for a &drm_device. * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or @@ -294,7 +294,7 @@ EXPORT_SYMBOL(drm_is_current_master); /** * drm_master_get - reference a master pointer - * @master: struct &drm_master + * @master: &struct drm_master * * Increments the reference count of @master and returns a pointer to @master. */ @@ -322,7 +322,7 @@ static void drm_master_destroy(struct kref *kref) /** * drm_master_put - unreference and clear a master pointer - * @master: pointer to a pointer of struct &drm_master + * @master: pointer to a pointer of &struct drm_master * * This decrements the &drm_master behind @master and sets it to NULL. */ diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index 1f2412c7ccfd..665aafc6ad68 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -40,9 +40,8 @@ * sub-pixel accuracy, which is scaled up to a pixel-aligned destination * rectangle in the visible area of a &drm_crtc. The visible area of a CRTC is * defined by the horizontal and vertical visible pixels (stored in @hdisplay - * and @vdisplay) of the requested mode (stored in @mode in the - * &drm_crtc_state). These two rectangles are both stored in the - * &drm_plane_state. + * and @vdisplay) of the requested mode (stored in &drm_crtc_state.mode). These + * two rectangles are both stored in the &drm_plane_state. * * For the atomic ioctl the following standard (atomic) properties on the plane object * encode the basic plane composition model: @@ -215,7 +214,7 @@ EXPORT_SYMBOL(drm_rotation_simplify); * for it in drm core. Drivers can then attach this property to planes to enable * support for configurable planes arrangement during blending operation. * Once mutable zpos property has been enabled, the DRM core will automatically - * calculate drm_plane_state->normalized_zpos values. Usually min should be set + * calculate &drm_plane_state.normalized_zpos values. Usually min should be set * to 0 and max to maximal number of planes for given crtc - 1. * * If zpos of some planes cannot be changed (like fixed background or @@ -367,8 +366,8 @@ done: * For every CRTC this function checks new states of all planes assigned to * it and calculates normalized zpos value for these planes. Planes are compared * first by their zpos values, then by plane id (if zpos is equal). The plane - * with lowest zpos value is at the bottom. The plane_state->normalized_zpos is - * then filled with unique values from 0 to number of active planes in crtc + * with lowest zpos value is at the bottom. The &drm_plane_state.normalized_zpos + * is then filled with unique values from 0 to number of active planes in crtc * minus one. * * RETURNS diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 0ee052b7c21a..86a7637ba344 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -26,11 +26,14 @@ #include <linux/mutex.h> #include <drm/drm_bridge.h> +#include <drm/drm_encoder.h> + +#include "drm_crtc_internal.h" /** * DOC: overview * - * struct &drm_bridge represents a device that hangs on to an encoder. These are + * &struct drm_bridge represents a device that hangs on to an encoder. These are * handy when a regular &drm_encoder entity isn't enough to represent the entire * encoder chain. * @@ -52,7 +55,7 @@ * just provide additional hooks to get the desired output at the end of the * encoder chain. * - * Bridges can also be chained up using the next pointer in struct &drm_bridge. + * Bridges can also be chained up using the &drm_bridge.next pointer. * * Both legacy CRTC helpers and the new atomic modeset helpers support bridges. */ @@ -92,47 +95,58 @@ void drm_bridge_remove(struct drm_bridge *bridge) EXPORT_SYMBOL(drm_bridge_remove); /** - * drm_bridge_attach - associate given bridge to our DRM device + * drm_bridge_attach - attach the bridge to an encoder's chain * - * @dev: DRM device - * @bridge: bridge control structure + * @encoder: DRM encoder + * @bridge: bridge to attach + * @previous: previous bridge in the chain (optional) * - * Called by a kms driver to link one of our encoder/bridge to the given - * bridge. + * Called by a kms driver to link the bridge to an encoder's chain. The previous + * argument specifies the previous bridge in the chain. If NULL, the bridge is + * linked directly at the encoder's output. Otherwise it is linked at the + * previous bridge's output. * - * Note that setting up links between the bridge and our encoder/bridge - * objects needs to be handled by the kms driver itself. + * If non-NULL the previous bridge must be already attached by a call to this + * function. * * RETURNS: * Zero on success, error code on failure */ -int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) +int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, + struct drm_bridge *previous) { - if (!dev || !bridge) + int ret; + + if (!encoder || !bridge) + return -EINVAL; + + if (previous && (!previous->dev || previous->encoder != encoder)) return -EINVAL; if (bridge->dev) return -EBUSY; - bridge->dev = dev; + bridge->dev = encoder->dev; + bridge->encoder = encoder; + + if (bridge->funcs->attach) { + ret = bridge->funcs->attach(bridge); + if (ret < 0) { + bridge->dev = NULL; + bridge->encoder = NULL; + return ret; + } + } - if (bridge->funcs->attach) - return bridge->funcs->attach(bridge); + if (previous) + previous->next = bridge; + else + encoder->bridge = bridge; return 0; } EXPORT_SYMBOL(drm_bridge_attach); -/** - * drm_bridge_detach - deassociate given bridge from its DRM device - * - * @bridge: bridge control structure - * - * Called by a kms driver to unlink the given bridge from its DRM device. - * - * Note that tearing down links between the bridge and our encoder/bridge - * objects needs to be handled by the kms driver itself. - */ void drm_bridge_detach(struct drm_bridge *bridge) { if (WARN_ON(!bridge)) @@ -146,7 +160,6 @@ void drm_bridge_detach(struct drm_bridge *bridge) bridge->dev = NULL; } -EXPORT_SYMBOL(drm_bridge_detach); /** * DOC: bridge callbacks @@ -166,7 +179,7 @@ EXPORT_SYMBOL(drm_bridge_detach); * @mode: desired mode to be set for the bridge * @adjusted_mode: updated mode that works for this bridge * - * Calls ->mode_fixup() &drm_bridge_funcs op for all the bridges in the + * Calls &drm_bridge_funcs.mode_fixup for all the bridges in the * encoder chain, starting from the first bridge to the last. * * Note: the bridge passed should be the one closest to the encoder @@ -193,11 +206,10 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_bridge_mode_fixup); /** - * drm_bridge_disable - calls ->disable() &drm_bridge_funcs op for all - * bridges in the encoder chain. + * drm_bridge_disable - disables all bridges in the encoder chain * @bridge: bridge control structure * - * Calls ->disable() &drm_bridge_funcs op for all the bridges in the encoder + * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder * chain, starting from the last bridge to the first. These are called before * calling the encoder's prepare op. * @@ -216,11 +228,10 @@ void drm_bridge_disable(struct drm_bridge *bridge) EXPORT_SYMBOL(drm_bridge_disable); /** - * drm_bridge_post_disable - calls ->post_disable() &drm_bridge_funcs op for - * all bridges in the encoder chain. + * drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain * @bridge: bridge control structure * - * Calls ->post_disable() &drm_bridge_funcs op for all the bridges in the + * Calls &drm_bridge_funcs.post_disable op for all the bridges in the * encoder chain, starting from the first bridge to the last. These are called * after completing the encoder's prepare op. * @@ -245,7 +256,7 @@ EXPORT_SYMBOL(drm_bridge_post_disable); * @mode: desired mode to be set for the bridge * @adjusted_mode: updated mode that works for this bridge * - * Calls ->mode_set() &drm_bridge_funcs op for all the bridges in the + * Calls &drm_bridge_funcs.mode_set op for all the bridges in the * encoder chain, starting from the first bridge to the last. * * Note: the bridge passed should be the one closest to the encoder @@ -265,11 +276,11 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_bridge_mode_set); /** - * drm_bridge_pre_enable - calls ->pre_enable() &drm_bridge_funcs op for all - * bridges in the encoder chain. + * drm_bridge_pre_enable - prepares for enabling all + * bridges in the encoder chain * @bridge: bridge control structure * - * Calls ->pre_enable() &drm_bridge_funcs op for all the bridges in the encoder + * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder * chain, starting from the last bridge to the first. These are called * before calling the encoder's commit op. * @@ -288,11 +299,10 @@ void drm_bridge_pre_enable(struct drm_bridge *bridge) EXPORT_SYMBOL(drm_bridge_pre_enable); /** - * drm_bridge_enable - calls ->enable() &drm_bridge_funcs op for all bridges - * in the encoder chain. + * drm_bridge_enable - enables all bridges in the encoder chain * @bridge: bridge control structure * - * Calls ->enable() &drm_bridge_funcs op for all the bridges in the encoder + * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder * chain, starting from the first bridge to the last. These are called * after completing the encoder's commit op. * diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index a7916e5f8864..c3b9aaccdf42 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -29,7 +29,9 @@ */ #include <linux/export.h> -#include <drm/drmP.h> +#include <linux/highmem.h> + +#include <drm/drm_cache.h> #if defined(CONFIG_X86) #include <asm/smp.h> @@ -67,6 +69,14 @@ static void drm_cache_flush_clflush(struct page *pages[], } #endif +/** + * drm_clflush_pages - Flush dcache lines of a set of pages. + * @pages: List of pages to be flushed. + * @num_pages: Number of pages in the array. + * + * Flush every data cache line entry that points to an address belonging + * to a page in the array. + */ void drm_clflush_pages(struct page *pages[], unsigned long num_pages) { @@ -101,6 +111,13 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages) } EXPORT_SYMBOL(drm_clflush_pages); +/** + * drm_clflush_sg - Flush dcache lines pointing to a scather-gather. + * @st: struct sg_table. + * + * Flush every data cache line entry that points to an address in the + * sg. + */ void drm_clflush_sg(struct sg_table *st) { @@ -125,6 +142,14 @@ drm_clflush_sg(struct sg_table *st) } EXPORT_SYMBOL(drm_clflush_sg); +/** + * drm_clflush_virt_range - Flush dcache lines of a region + * @addr: Initial kernel memory address. + * @length: Region size. + * + * Flush every data cache line entry that points to an address in the + * region requested. + */ void drm_clflush_virt_range(void *addr, unsigned long length) { diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index 6543ebde501a..cc23b9a505c0 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -36,7 +36,7 @@ * "DEGAMMA_LUT”: * Blob property to set the degamma lookup table (LUT) mapping pixel data * from the framebuffer before it is given to the transformation matrix. - * The data is interpreted as an array of struct &drm_color_lut elements. + * The data is interpreted as an array of &struct drm_color_lut elements. * Hardware might choose not to use the full precision of the LUT elements * nor use all the elements of the LUT (for example the hardware might * choose to interpolate between LUT[0] and LUT[4]). @@ -65,7 +65,7 @@ * “GAMMA_LUT”: * Blob property to set the gamma lookup table (LUT) mapping pixel data * after the transformation matrix to data sent to the connector. The - * data is interpreted as an array of struct &drm_color_lut elements. + * data is interpreted as an array of &struct drm_color_lut elements. * Hardware might choose not to use the full precision of the LUT elements * nor use all the elements of the LUT (for example the hardware might * choose to interpolate between LUT[0] and LUT[4]). @@ -88,6 +88,30 @@ */ /** + * drm_color_lut_extract - clamp and round LUT entries + * @user_input: input value + * @bit_precision: number of bits the hw LUT supports + * + * Extract a degamma/gamma LUT value provided by user (in the form of + * &drm_color_lut entries) and round it to the precision supported by the + * hardware. + */ +uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision) +{ + uint32_t val = user_input; + uint32_t max = 0xffff >> (16 - bit_precision); + + /* Round only if we're not using full precision. */ + if (bit_precision < 16) { + val += 1UL << (16 - bit_precision - 1); + val >>= 16 - bit_precision; + } + + return clamp_val(val, 0, max); +} +EXPORT_SYMBOL(drm_color_lut_extract); + +/** * drm_crtc_enable_color_mgmt - enable color management properties * @crtc: DRM CRTC * @degamma_lut_size: the size of the degamma lut (before CSC) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 7a7019ac9388..45464c8b797d 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -23,6 +23,7 @@ #include <drm/drmP.h> #include <drm/drm_connector.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -37,18 +38,17 @@ * Hence they are reference-counted using drm_connector_reference() and * drm_connector_unreference(). * - * KMS driver must create, initialize, register and attach at a struct - * &drm_connector for each such sink. The instance is created as other KMS - * objects and initialized by setting the following fields. - * - * The connector is then registered with a call to drm_connector_init() with a - * pointer to the connector functions and a connector type, and exposed through - * sysfs with a call to drm_connector_register(). + * KMS driver must create, initialize, register and attach at a &struct + * drm_connector for each such sink. The instance is created as other KMS + * objects and initialized by setting the following fields. The connector is + * initialized with a call to drm_connector_init() with a pointer to the + * &struct drm_connector_funcs and a connector type, and then exposed to + * userspace with a call to drm_connector_register(). * * Connectors must be attached to an encoder to be used. For devices that map * connectors to encoders 1:1, the connector should be attached at * initialization time with a call to drm_mode_connector_attach_encoder(). The - * driver must also set the struct &drm_connector encoder field to point to the + * driver must also set the &drm_connector.encoder field to point to the * attached encoder. * * For connectors which are not fixed (like built-in panels) the driver needs to @@ -189,13 +189,11 @@ int drm_connector_init(struct drm_device *dev, struct ida *connector_ida = &drm_connector_enum_list[connector_type].ida; - drm_modeset_lock_all(dev); - ret = drm_mode_object_get_reg(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR, false, drm_connector_free); if (ret) - goto out_unlock; + return ret; connector->base.properties = &connector->properties; connector->dev = dev; @@ -233,8 +231,10 @@ int drm_connector_init(struct drm_device *dev, /* We should add connectors at the end to avoid upsetting the connector * index too much. */ + spin_lock_irq(&config->connector_list_lock); list_add_tail(&connector->head, &config->connector_list); config->num_connector++; + spin_unlock_irq(&config->connector_list_lock); if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) drm_object_attach_property(&connector->base, @@ -259,9 +259,6 @@ out_put: if (ret) drm_mode_object_unregister(dev, &connector->base); -out_unlock: - drm_modeset_unlock_all(dev); - return ret; } EXPORT_SYMBOL(drm_connector_init); @@ -352,8 +349,10 @@ void drm_connector_cleanup(struct drm_connector *connector) drm_mode_object_unregister(dev, &connector->base); kfree(connector->name); connector->name = NULL; + spin_lock_irq(&dev->mode_config.connector_list_lock); list_del(&connector->head); dev->mode_config.num_connector--; + spin_unlock_irq(&dev->mode_config.connector_list_lock); WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); if (connector->state && connector->funcs->atomic_destroy_state) @@ -444,30 +443,30 @@ EXPORT_SYMBOL(drm_connector_unregister); void drm_connector_unregister_all(struct drm_device *dev) { struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; - /* FIXME: taking the mode config mutex ends up in a clash with sysfs */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) drm_connector_unregister(connector); + drm_connector_list_iter_put(&conn_iter); } int drm_connector_register_all(struct drm_device *dev) { struct drm_connector *connector; - int ret; + struct drm_connector_list_iter conn_iter; + int ret = 0; - /* FIXME: taking the mode config mutex ends up in a clash with - * fbcon/backlight registration */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { ret = drm_connector_register(connector); if (ret) - goto err; + break; } + drm_connector_list_iter_put(&conn_iter); - return 0; - -err: - mutex_unlock(&dev->mode_config.mutex); - drm_connector_unregister_all(dev); + if (ret) + drm_connector_unregister_all(dev); return ret; } @@ -489,6 +488,87 @@ const char *drm_get_connector_status_name(enum drm_connector_status status) } EXPORT_SYMBOL(drm_get_connector_status_name); +#ifdef CONFIG_LOCKDEP +static struct lockdep_map connector_list_iter_dep_map = { + .name = "drm_connector_list_iter" +}; +#endif + +/** + * drm_connector_list_iter_get - initialize a connector_list iterator + * @dev: DRM device + * @iter: connector_list iterator + * + * Sets @iter up to walk the &drm_mode_config.connector_list of @dev. @iter + * must always be cleaned up again by calling drm_connector_list_iter_put(). + * Iteration itself happens using drm_connector_list_iter_next() or + * drm_for_each_connector_iter(). + */ +void drm_connector_list_iter_get(struct drm_device *dev, + struct drm_connector_list_iter *iter) +{ + iter->dev = dev; + iter->conn = NULL; + lock_acquire_shared_recursive(&connector_list_iter_dep_map, 0, 1, NULL, _RET_IP_); +} +EXPORT_SYMBOL(drm_connector_list_iter_get); + +/** + * drm_connector_list_iter_next - return next connector + * @iter: connectr_list iterator + * + * Returns the next connector for @iter, or NULL when the list walk has + * completed. + */ +struct drm_connector * +drm_connector_list_iter_next(struct drm_connector_list_iter *iter) +{ + struct drm_connector *old_conn = iter->conn; + struct drm_mode_config *config = &iter->dev->mode_config; + struct list_head *lhead; + unsigned long flags; + + spin_lock_irqsave(&config->connector_list_lock, flags); + lhead = old_conn ? &old_conn->head : &config->connector_list; + + do { + if (lhead->next == &config->connector_list) { + iter->conn = NULL; + break; + } + + lhead = lhead->next; + iter->conn = list_entry(lhead, struct drm_connector, head); + + /* loop until it's not a zombie connector */ + } while (!kref_get_unless_zero(&iter->conn->base.refcount)); + spin_unlock_irqrestore(&config->connector_list_lock, flags); + + if (old_conn) + drm_connector_unreference(old_conn); + + return iter->conn; +} +EXPORT_SYMBOL(drm_connector_list_iter_next); + +/** + * drm_connector_list_iter_put - tear down a connector_list iterator + * @iter: connector_list iterator + * + * Tears down @iter and releases any resources (like &drm_connector references) + * acquired while walking the list. This must always be called, both when the + * iteration completes fully or when it was aborted without walking the entire + * list. + */ +void drm_connector_list_iter_put(struct drm_connector_list_iter *iter) +{ + iter->dev = NULL; + if (iter->conn) + drm_connector_unreference(iter->conn); + lock_release(&connector_list_iter_dep_map, 0, _RET_IP_); +} +EXPORT_SYMBOL(drm_connector_list_iter_put); + static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { { SubPixelUnknown, "Unknown" }, { SubPixelHorizontalRGB, "Horizontal RGB" }, @@ -618,8 +698,8 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, * drivers this is only provided for backwards compatibility with existing * drivers, it remaps to controlling the "ACTIVE" property on the CRTC the * connector is linked to. Drivers should never set this property directly, - * it is handled by the DRM core by calling the ->dpms() callback in - * &drm_connector_funcs. Atomic drivers should implement this hook using + * it is handled by the DRM core by calling the &drm_connector_funcs.dpms + * callback. Atomic drivers should implement this hook using * drm_atomic_helper_connector_dpms(). This is the only property standard * connector property that userspace can change. * PATH: @@ -1085,43 +1165,65 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); - mutex_lock(&dev->mode_config.mutex); - connector = drm_connector_lookup(dev, out_resp->connector_id); - if (!connector) { - ret = -ENOENT; - goto out_unlock; - } + if (!connector) + return -ENOENT; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + encoder = drm_connector_get_encoder(connector); + if (encoder) + out_resp->encoder_id = encoder->base.id; + else + out_resp->encoder_id = 0; + + ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic, + (uint32_t __user *)(unsigned long)(out_resp->props_ptr), + (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), + &out_resp->count_props); + drm_modeset_unlock(&dev->mode_config.connection_mutex); + if (ret) + goto out_unref; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) if (connector->encoder_ids[i] != 0) encoders_count++; + if ((out_resp->count_encoders >= encoders_count) && encoders_count) { + copied = 0; + encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] != 0) { + if (put_user(connector->encoder_ids[i], + encoder_ptr + copied)) { + ret = -EFAULT; + goto out_unref; + } + copied++; + } + } + } + out_resp->count_encoders = encoders_count; + + out_resp->connector_id = connector->base.id; + out_resp->connector_type = connector->connector_type; + out_resp->connector_type_id = connector->connector_type_id; + + mutex_lock(&dev->mode_config.mutex); if (out_resp->count_modes == 0) { connector->funcs->fill_modes(connector, dev->mode_config.max_width, dev->mode_config.max_height); } - /* delayed so we get modes regardless of pre-fill_modes state */ - list_for_each_entry(mode, &connector->modes, head) - if (drm_mode_expose_to_userspace(mode, file_priv)) - mode_count++; - - out_resp->connector_id = connector->base.id; - out_resp->connector_type = connector->connector_type; - out_resp->connector_type_id = connector->connector_type_id; out_resp->mm_width = connector->display_info.width_mm; out_resp->mm_height = connector->display_info.height_mm; out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - encoder = drm_connector_get_encoder(connector); - if (encoder) - out_resp->encoder_id = encoder->base.id; - else - out_resp->encoder_id = 0; + /* delayed so we get modes regardless of pre-fill_modes state */ + list_for_each_entry(mode, &connector->modes, head) + if (drm_mode_expose_to_userspace(mode, file_priv)) + mode_count++; /* * This ioctl is called twice, once to determine how much space is @@ -1144,36 +1246,10 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, } } out_resp->count_modes = mode_count; - - ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic, - (uint32_t __user *)(unsigned long)(out_resp->props_ptr), - (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), - &out_resp->count_props); - if (ret) - goto out; - - if ((out_resp->count_encoders >= encoders_count) && encoders_count) { - copied = 0; - encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] != 0) { - if (put_user(connector->encoder_ids[i], - encoder_ptr + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - } - out_resp->count_encoders = encoders_count; - out: - drm_modeset_unlock(&dev->mode_config.connection_mutex); - - drm_connector_unreference(connector); -out_unlock: mutex_unlock(&dev->mode_config.mutex); +out_unref: + drm_connector_unreference(connector); return ret; } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index e75f62cd8a65..6915f897bd8e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -47,6 +47,50 @@ #include "drm_internal.h" /** + * DOC: overview + * + * A CRTC represents the overall display pipeline. It receives pixel data from + * &drm_plane and blends them together. The &drm_display_mode is also attached + * to the CRTC, specifying display timings. On the output side the data is fed + * to one or more &drm_encoder, which are then each connected to one + * &drm_connector. + * + * To create a CRTC, a KMS drivers allocates and zeroes an instances of + * &struct drm_crtc (possibly as part of a larger structure) and registers it + * with a call to drm_crtc_init_with_planes(). + * + * The CRTC is also the entry point for legacy modeset operations, see + * &drm_crtc_funcs.set_config, legacy plane operations, see + * &drm_crtc_funcs.page_flip and &drm_crtc_funcs.cursor_set2, and other legacy + * operations like &drm_crtc_funcs.gamma_set. For atomic drivers all these + * features are controlled through &drm_property and + * &drm_mode_config_funcs.atomic_check and &drm_mode_config_funcs.atomic_check. + */ + +/** + * drm_crtc_from_index - find the registered CRTC at an index + * @dev: DRM device + * @idx: index of registered CRTC to find for + * + * Given a CRTC index, return the registered CRTC from DRM device's + * list of CRTCs with matching index. This is the inverse of drm_crtc_index(). + * It's useful in the vblank callbacks (like &drm_driver.enable_vblank or + * &drm_driver.disable_vblank), since that still deals with indices instead + * of pointers to &struct drm_crtc." + */ +struct drm_crtc *drm_crtc_from_index(struct drm_device *dev, int idx) +{ + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, dev) + if (idx == crtc->index) + return crtc; + + return NULL; +} +EXPORT_SYMBOL(drm_crtc_from_index); + +/** * drm_crtc_force_disable - Forcibly turn off a CRTC * @crtc: CRTC to turn off * @@ -357,7 +401,10 @@ int drm_mode_getcrtc(struct drm_device *dev, drm_modeset_lock_crtc(crtc, crtc->primary); crtc_resp->gamma_size = crtc->gamma_size; - if (crtc->primary->fb) + + if (crtc->primary->state && crtc->primary->state->fb) + crtc_resp->fb_id = crtc->primary->state->fb->base.id; + else if (!crtc->primary->state && crtc->primary->fb) crtc_resp->fb_id = crtc->primary->fb->base.id; else crtc_resp->fb_id = 0; @@ -389,11 +436,12 @@ int drm_mode_getcrtc(struct drm_device *dev, } /** - * drm_mode_set_config_internal - helper to call ->set_config + * drm_mode_set_config_internal - helper to call &drm_mode_config_funcs.set_config * @set: modeset config to set * - * This is a little helper to wrap internal calls to the ->set_config driver - * interface. The only thing it adds is correct refcounting dance. + * This is a little helper to wrap internal calls to the + * &drm_mode_config_funcs.set_config driver interface. The only thing it adds is + * correct refcounting dance. * * Returns: * Zero on success, negative errno on failure. @@ -434,27 +482,6 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) EXPORT_SYMBOL(drm_mode_set_config_internal); /** - * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode - * @mode: mode to query - * @hdisplay: hdisplay value to fill in - * @vdisplay: vdisplay value to fill in - * - * The vdisplay value will be doubled if the specified mode is a stereo mode of - * the appropriate layout. - */ -void drm_crtc_get_hv_timing(const struct drm_display_mode *mode, - int *hdisplay, int *vdisplay) -{ - struct drm_display_mode adjusted; - - drm_mode_copy(&adjusted, mode); - drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); - *hdisplay = adjusted.crtc_hdisplay; - *vdisplay = adjusted.crtc_vdisplay; -} -EXPORT_SYMBOL(drm_crtc_get_hv_timing); - -/** * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the * CRTC viewport * @crtc: CRTC that framebuffer will be displayed on @@ -471,7 +498,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, { int hdisplay, vdisplay; - drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay); + drm_mode_get_hv_timing(mode, &hdisplay, &vdisplay); if (crtc->state && drm_rotation_90_or_270(crtc->primary->state->rotation)) @@ -572,11 +599,11 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, */ if (!crtc->primary->format_default) { ret = drm_plane_check_pixel_format(crtc->primary, - fb->pixel_format); + fb->format->format); if (ret) { struct drm_format_name_buf format_name; DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format, + drm_get_format_name(fb->format->format, &format_name)); goto out; } diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 5d2cb138eba6..44ba0e990d6c 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -36,6 +36,7 @@ #include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> #include <drm/drm_fourcc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> @@ -52,9 +53,9 @@ * configuration on resume with drm_helper_resume_force_mode(). * * Note that this helper library doesn't track the current power state of CRTCs - * and encoders. It can call callbacks like ->dpms() even though the hardware is - * already in the desired state. This deficiency has been fixed in the atomic - * helpers. + * and encoders. It can call callbacks like &drm_encoder_helper_funcs.dpms even + * though the hardware is already in the desired state. This deficiency has been + * fixed in the atomic helpers. * * The driver callbacks are mostly compatible with the atomic modeset helpers, * except for the handling of the primary plane: Atomic helpers require that the @@ -70,7 +71,7 @@ * * These legacy modeset helpers use the same function table structures as * all other modesetting helpers. See the documentation for struct - * &drm_crtc_helper_funcs, struct &drm_encoder_helper_funcs and struct + * &drm_crtc_helper_funcs, &struct drm_encoder_helper_funcs and struct * &drm_connector_helper_funcs. */ @@ -88,6 +89,7 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder) { struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; struct drm_device *dev = encoder->dev; /* @@ -99,9 +101,15 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder) WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); } - drm_for_each_connector(connector, dev) - if (connector->encoder == encoder) + + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->encoder == encoder) { + drm_connector_list_iter_put(&conn_iter); return true; + } + } + drm_connector_list_iter_put(&conn_iter); return false; } EXPORT_SYMBOL(drm_helper_encoder_in_use); @@ -436,10 +444,13 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) /* Decouple all encoders and their attached connectors from this crtc */ drm_for_each_encoder(encoder, dev) { + struct drm_connector_list_iter conn_iter; + if (encoder->crtc != crtc) continue; - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->encoder != encoder) continue; @@ -456,6 +467,7 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) /* we keep a reference while the encoder is bound */ drm_connector_unreference(connector); } + drm_connector_list_iter_put(&conn_iter); } __drm_helper_disable_unused_functions(dev); @@ -465,12 +477,12 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) * drm_crtc_helper_set_config - set a new config from userspace * @set: mode set configuration * - * The drm_crtc_helper_set_config() helper function implements the set_config - * callback of struct &drm_crtc_funcs for drivers using the legacy CRTC helpers. + * The drm_crtc_helper_set_config() helper function implements the of + * &drm_crtc_funcs.set_config callback for drivers using the legacy CRTC + * helpers. * * It first tries to locate the best encoder for each connector by calling the - * connector ->best_encoder() (struct &drm_connector_helper_funcs) helper - * operation. + * connector @drm_connector_helper_funcs.best_encoder helper operation. * * After locating the appropriate encoders, the helper function will call the * mode_fixup encoder and CRTC helper operations to adjust the requested mode, @@ -481,15 +493,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) * * If the adjusted mode is identical to the current mode but changes to the * frame buffer need to be applied, the drm_crtc_helper_set_config() function - * will call the CRTC ->mode_set_base() (struct &drm_crtc_helper_funcs) helper - * operation. + * will call the CRTC &drm_crtc_helper_funcs.mode_set_base helper operation. * * If the adjusted mode differs from the current mode, or if the * ->mode_set_base() helper operation is not provided, the helper function * performs a full mode set sequence by calling the ->prepare(), ->mode_set() * and ->commit() CRTC and encoder helper operations, in that order. * Alternatively it can also use the dpms and disable helper operations. For - * details see struct &drm_crtc_helper_funcs and struct + * details see &struct drm_crtc_helper_funcs and struct * &drm_encoder_helper_funcs. * * This function is deprecated. New drivers must implement atomic modeset @@ -507,6 +518,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) bool mode_changed = false; /* if true do a full mode set */ bool fb_changed = false; /* if true and !mode_changed just do a flip */ struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; int count = 0, ro, fail = 0; const struct drm_crtc_helper_funcs *crtc_funcs; struct drm_mode_set save_set; @@ -571,9 +583,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) } count = 0; - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) save_connector_encoders[count++] = connector->encoder; - } + drm_connector_list_iter_put(&conn_iter); save_set.crtc = set->crtc; save_set.mode = &set->crtc->mode; @@ -588,8 +601,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) if (set->crtc->primary->fb == NULL) { DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); mode_changed = true; - } else if (set->fb->pixel_format != - set->crtc->primary->fb->pixel_format) { + } else if (set->fb->format != set->crtc->primary->fb->format) { mode_changed = true; } else fb_changed = true; @@ -616,7 +628,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) /* a) traverse passed in connector list and get encoders for them */ count = 0; - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; new_encoder = connector->encoder; @@ -649,6 +662,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) connector->encoder = new_encoder; } } + drm_connector_list_iter_put(&conn_iter); if (fail) { ret = -EINVAL; @@ -656,7 +670,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) } count = 0; - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (!connector->encoder) continue; @@ -674,6 +689,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) if (new_crtc && !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { ret = -EINVAL; + drm_connector_list_iter_put(&conn_iter); goto fail; } if (new_crtc != connector->encoder->crtc) { @@ -690,6 +706,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) connector->base.id, connector->name); } } + drm_connector_list_iter_put(&conn_iter); /* mode_set_base is not a required function */ if (fb_changed && !crtc_funcs->mode_set_base) @@ -744,9 +761,10 @@ fail: } count = 0; - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) connector->encoder = save_connector_encoders[count++]; - } + drm_connector_list_iter_put(&conn_iter); /* after fail drop reference on all unbound connectors in set, let * bound connectors keep their reference @@ -773,12 +791,16 @@ static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) { int dpms = DRM_MODE_DPMS_OFF; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; struct drm_device *dev = encoder->dev; - drm_for_each_connector(connector, dev) + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) if (connector->encoder == encoder) if (connector->dpms < dpms) dpms = connector->dpms; + drm_connector_list_iter_put(&conn_iter); + return dpms; } @@ -810,12 +832,16 @@ static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) { int dpms = DRM_MODE_DPMS_OFF; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; struct drm_device *dev = crtc->dev; - drm_for_each_connector(connector, dev) + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) if (connector->encoder && connector->encoder->crtc == crtc) if (connector->dpms < dpms) dpms = connector->dpms; + drm_connector_list_iter_put(&conn_iter); + return dpms; } @@ -824,14 +850,15 @@ static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) * @connector: affected connector * @mode: DPMS mode * - * The drm_helper_connector_dpms() helper function implements the ->dpms() - * callback of struct &drm_connector_funcs for drivers using the legacy CRTC helpers. + * The drm_helper_connector_dpms() helper function implements the + * &drm_connector_funcs.dpms callback for drivers using the legacy CRTC + * helpers. * * This is the main helper function provided by the CRTC helper framework for * implementing the DPMS connector attribute. It computes the new desired DPMS - * state for all encoders and CRTCs in the output mesh and calls the ->dpms() - * callbacks provided by the driver in struct &drm_crtc_helper_funcs and struct - * &drm_encoder_helper_funcs appropriately. + * state for all encoders and CRTCs in the output mesh and calls the + * &drm_crtc_helper_funcs.dpms and &drm_encoder_helper_funcs.dpms callbacks + * provided by the driver. * * This function is deprecated. New drivers must implement atomic modeset * support, for which this function is unsuitable. Instead drivers should use diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index cdf6860c9d22..955c5690bf64 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -174,6 +174,11 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); /* drm_atomic.c */ +#ifdef CONFIG_DEBUG_FS +struct drm_minor; +int drm_atomic_debugfs_init(struct drm_minor *minor); +#endif + int drm_atomic_get_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val); int drm_mode_atomic_ioctl(struct drm_device *dev, @@ -186,6 +191,9 @@ void drm_plane_unregister_all(struct drm_device *dev); int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format); +/* drm_bridge.c */ +void drm_bridge_detach(struct drm_bridge *bridge); + /* IOCTL */ int drm_mode_getplane_res(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -199,3 +207,6 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); + +/* drm_edid.c */ +void drm_mode_fixup_1366x768(struct drm_display_mode *mode); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 2e3e46a53805..2290a74a6e46 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -38,6 +38,7 @@ #include <drm/drm_edid.h> #include <drm/drm_atomic.h> #include "drm_internal.h" +#include "drm_crtc_internal.h" #if defined(CONFIG_DEBUG_FS) @@ -80,7 +81,8 @@ static const struct file_operations drm_debugfs_fops = { * \return Zero on success, non-zero on failure * * Create a given set of debugfs files represented by an array of - * gdm_debugfs_lists in the given root directory. + * &drm_info_list in the given root directory. These files will be removed + * automatically on drm_debugfs_cleanup(). */ int drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root, struct drm_minor *minor) @@ -217,6 +219,19 @@ int drm_debugfs_remove_files(const struct drm_info_list *files, int count, } EXPORT_SYMBOL(drm_debugfs_remove_files); +static void drm_debugfs_remove_all_files(struct drm_minor *minor) +{ + struct drm_info_node *node, *tmp; + + mutex_lock(&minor->debugfs_lock); + list_for_each_entry_safe(node, tmp, &minor->debugfs_list, list) { + debugfs_remove(node->dent); + list_del(&node->list); + kfree(node); + } + mutex_unlock(&minor->debugfs_lock); +} + /** * Cleanup the debugfs filesystem resources. * @@ -228,7 +243,6 @@ EXPORT_SYMBOL(drm_debugfs_remove_files); int drm_debugfs_cleanup(struct drm_minor *minor) { struct drm_device *dev = minor->dev; - int ret; if (!minor->debugfs_root) return 0; @@ -236,17 +250,9 @@ int drm_debugfs_cleanup(struct drm_minor *minor) if (dev->driver->debugfs_cleanup) dev->driver->debugfs_cleanup(minor); - if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { - ret = drm_atomic_debugfs_cleanup(minor); - if (ret) { - DRM_ERROR("DRM: Failed to remove atomic debugfs entries\n"); - return ret; - } - } - - drm_debugfs_remove_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, minor); + drm_debugfs_remove_all_files(minor); - debugfs_remove(minor->debugfs_root); + debugfs_remove_recursive(minor->debugfs_root); minor->debugfs_root = NULL; return 0; diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index 00e771fb7df2..96891c4a6e23 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c @@ -125,6 +125,12 @@ static const struct file_operations drm_crtc_crc_control_fops = { .write = crc_control_write }; +static int crtc_crc_data_count(struct drm_crtc_crc *crc) +{ + assert_spin_locked(&crc->lock); + return CIRC_CNT(crc->head, crc->tail, DRM_CRC_ENTRIES_NR); +} + static int crtc_crc_open(struct inode *inode, struct file *filep) { struct drm_crtc *crtc = inode->i_private; @@ -160,8 +166,19 @@ static int crtc_crc_open(struct inode *inode, struct file *filep) crc->entries = entries; crc->values_cnt = values_cnt; crc->opened = true; + + /* + * Only return once we got a first frame, so userspace doesn't have to + * guess when this particular piece of HW will be ready to start + * generating CRCs. + */ + ret = wait_event_interruptible_lock_irq(crc->wq, + crtc_crc_data_count(crc), + crc->lock); spin_unlock_irq(&crc->lock); + WARN_ON(ret); + return 0; err_disable: @@ -189,12 +206,6 @@ static int crtc_crc_release(struct inode *inode, struct file *filep) return 0; } -static int crtc_crc_data_count(struct drm_crtc_crc *crc) -{ - assert_spin_locked(&crc->lock); - return CIRC_CNT(crc->head, crc->tail, DRM_CRC_ENTRIES_NR); -} - /* * 1 frame field of 10 chars plus a number of CRC fields of 10 chars each, space * separated, with a newline at the end and null-terminated. @@ -325,16 +336,19 @@ int drm_crtc_add_crc_entry(struct drm_crtc *crtc, bool has_frame, struct drm_crtc_crc_entry *entry; int head, tail; - assert_spin_locked(&crc->lock); + spin_lock(&crc->lock); /* Caller may not have noticed yet that userspace has stopped reading */ - if (!crc->opened) + if (!crc->opened) { + spin_unlock(&crc->lock); return -EINVAL; + } head = crc->head; tail = crc->tail; if (CIRC_SPACE(head, tail, DRM_CRC_ENTRIES_NR) < 1) { + spin_unlock(&crc->lock); DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n"); return -ENOBUFS; } @@ -347,6 +361,10 @@ int drm_crtc_add_crc_entry(struct drm_crtc *crtc, bool has_frame, head = (head + 1) & (DRM_CRC_ENTRIES_NR - 1); crc->head = head; + spin_unlock(&crc->lock); + + wake_up_interruptible(&crc->wq); + return 0; } EXPORT_SYMBOL_GPL(drm_crtc_add_crc_entry); diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 3e6fe82c6d64..68908c1d5ca1 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -725,7 +725,7 @@ MODULE_PARM_DESC(dp_aux_i2c_speed_khz, /* * Transfer a single I2C-over-AUX message and handle various error conditions, * retrying the transaction as appropriate. It is assumed that the - * aux->transfer function does not modify anything in the msg other than the + * &drm_dp_aux.transfer function does not modify anything in the msg other than the * reply field. * * Returns bytes transferred on success, or a negative error code on failure. diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index f59771da52ee..f2cc375907d0 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1086,7 +1086,7 @@ static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb, } static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, - struct device *dev, + struct drm_device *dev, struct drm_dp_link_addr_reply_port *port_msg) { struct drm_dp_mst_port *port; @@ -1104,7 +1104,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, port->port_num = port_msg->port_number; port->mgr = mstb->mgr; port->aux.name = "DPMST"; - port->aux.dev = dev; + port->aux.dev = dev->dev; created = true; } else { old_pdt = port->pdt; @@ -2949,7 +2949,7 @@ static void drm_dp_destroy_connector_work(struct work_struct *work) * Return 0 for success, or negative error code on failure */ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, - struct device *dev, struct drm_dp_aux *aux, + struct drm_device *dev, struct drm_dp_aux *aux, int max_dpcd_transaction_bytes, int max_payloads, int conn_base_id) { diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 6594b4088f11..b5c6bb46a425 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -221,7 +221,7 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root); if (ret) { DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); - return ret; + goto err_debugfs; } ret = device_add(minor->kdev); @@ -298,7 +298,7 @@ void drm_minor_release(struct drm_minor *minor) /** * DOC: driver instance overview * - * A device instance for a drm driver is represented by struct &drm_device. This + * A device instance for a drm driver is represented by &struct drm_device. This * is allocated with drm_dev_alloc(), usually from bus-specific ->probe() * callbacks implemented by the driver. The driver then needs to initialize all * the various subsystems for the drm device like memory management, vblank @@ -309,7 +309,7 @@ void drm_minor_release(struct drm_minor *minor) * userspace the device instance can be published using drm_dev_register(). * * There is also deprecated support for initalizing device instances using - * bus-specific helpers and the ->load() callback. But due to + * bus-specific helpers and the &drm_driver.load callback. But due to * backwards-compatibility needs the device instance have to be published too * early, which requires unpretty global locking to make safe and is therefore * only support for existing drivers not yet converted to the new scheme. @@ -323,9 +323,8 @@ void drm_minor_release(struct drm_minor *minor) * historical baggage. Hence use the reference counting provided by * drm_dev_ref() and drm_dev_unref() only carefully. * - * Also note that embedding of &drm_device is currently not (yet) supported (but - * it would be easy to add). Drivers can store driver-private data in the - * dev_priv field of &drm_device. + * It is recommended that drivers embed &struct drm_device into their own device + * structure, which is supported through drm_dev_init(). */ /** @@ -462,7 +461,14 @@ static void drm_fs_inode_free(struct inode *inode) * Note that for purely virtual devices @parent can be NULL. * * Drivers that do not want to allocate their own device struct - * embedding struct &drm_device can call drm_dev_alloc() instead. + * embedding &struct drm_device can call drm_dev_alloc() instead. For drivers + * that do embed &struct drm_device it must be placed first in the overall + * structure, and the overall structure must be allocated using kmalloc(): The + * drm core's release function unconditionally calls kfree() on the @dev pointer + * when the final reference is released. To override this behaviour, and so + * allow embedding of the drm_device inside the driver's device struct at an + * arbitrary offset, you must supply a &drm_driver.release callback and control + * the finalization explicitly. * * RETURNS: * 0 on success, or error code on failure. @@ -550,6 +556,41 @@ err_free: EXPORT_SYMBOL(drm_dev_init); /** + * drm_dev_fini - Finalize a dead DRM device + * @dev: DRM device + * + * Finalize a dead DRM device. This is the converse to drm_dev_init() and + * frees up all data allocated by it. All driver private data should be + * finalized first. Note that this function does not free the @dev, that is + * left to the caller. + * + * The ref-count of @dev must be zero, and drm_dev_fini() should only be called + * from a &drm_driver.release callback. + */ +void drm_dev_fini(struct drm_device *dev) +{ + drm_vblank_cleanup(dev); + + if (drm_core_check_feature(dev, DRIVER_GEM)) + drm_gem_destroy(dev); + + drm_legacy_ctxbitmap_cleanup(dev); + drm_ht_remove(&dev->map_hash); + drm_fs_inode_free(dev->anon_inode); + + drm_minor_free(dev, DRM_MINOR_PRIMARY); + drm_minor_free(dev, DRM_MINOR_RENDER); + drm_minor_free(dev, DRM_MINOR_CONTROL); + + mutex_destroy(&dev->master_mutex); + mutex_destroy(&dev->ctxlist_mutex); + mutex_destroy(&dev->filelist_mutex); + mutex_destroy(&dev->struct_mutex); + kfree(dev->unique); +} +EXPORT_SYMBOL(drm_dev_fini); + +/** * drm_dev_alloc - Allocate new DRM device * @driver: DRM driver to allocate device for * @parent: Parent device object @@ -565,7 +606,7 @@ EXPORT_SYMBOL(drm_dev_init); * * Note that for purely virtual devices @parent can be NULL. * - * Drivers that wish to subclass or embed struct &drm_device into their + * Drivers that wish to subclass or embed &struct drm_device into their * own struct should look at using drm_dev_init() instead. * * RETURNS: @@ -595,23 +636,12 @@ static void drm_dev_release(struct kref *ref) { struct drm_device *dev = container_of(ref, struct drm_device, ref); - if (drm_core_check_feature(dev, DRIVER_GEM)) - drm_gem_destroy(dev); - - drm_legacy_ctxbitmap_cleanup(dev); - drm_ht_remove(&dev->map_hash); - drm_fs_inode_free(dev->anon_inode); - - drm_minor_free(dev, DRM_MINOR_PRIMARY); - drm_minor_free(dev, DRM_MINOR_RENDER); - drm_minor_free(dev, DRM_MINOR_CONTROL); - - mutex_destroy(&dev->master_mutex); - mutex_destroy(&dev->ctxlist_mutex); - mutex_destroy(&dev->filelist_mutex); - mutex_destroy(&dev->struct_mutex); - kfree(dev->unique); - kfree(dev); + if (dev->driver->release) { + dev->driver->release(dev); + } else { + drm_dev_fini(dev); + kfree(dev); + } } /** @@ -715,9 +745,9 @@ static void remove_compat_control_link(struct drm_device *dev) * Never call this twice on any device! * * NOTE: To ensure backward compatibility with existing drivers method this - * function calls the ->load() method after registering the device nodes, - * creating race conditions. Usage of the ->load() methods is therefore - * deprecated, drivers must perform all initialization before calling + * function calls the &drm_driver.load method after registering the device + * nodes, creating race conditions. Usage of the &drm_driver.load methods is + * therefore deprecated, drivers must perform all initialization before calling * drm_dev_register(). * * RETURNS: @@ -725,6 +755,7 @@ static void remove_compat_control_link(struct drm_device *dev) */ int drm_dev_register(struct drm_device *dev, unsigned long flags) { + struct drm_driver *driver = dev->driver; int ret; mutex_lock(&drm_global_mutex); @@ -757,6 +788,13 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) drm_modeset_register_all(dev); ret = 0; + + DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", + driver->name, driver->major, driver->minor, + driver->patchlevel, driver->date, + dev->dev ? dev_name(dev->dev) : "virtual device", + dev->primary->index); + goto out_unlock; err_minors: @@ -798,8 +836,6 @@ void drm_dev_unregister(struct drm_device *dev) if (dev->agp) drm_pci_agp_destroy(dev); - drm_vblank_cleanup(dev); - list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) drm_legacy_rmmap(dev, r_list->map); @@ -925,7 +961,7 @@ static int __init drm_core_init(void) if (ret < 0) goto error; - DRM_INFO("Initialized\n"); + DRM_DEBUG("Initialized\n"); return 0; error: diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index 8ac5a1c1d811..10307cc16d75 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -42,8 +42,8 @@ * create dumb buffers suitable for scanout, which can then be used to create * KMS frame buffers. * - * To support dumb objects drivers must implement the dumb_create, - * dumb_destroy and dumb_map_offset operations from struct &drm_driver. See + * To support dumb objects drivers must implement the &drm_driver.dumb_create, + * &drm_driver.dumb_destroy and &drm_driver.dumb_map_offset operations. See * there for further details. * * Note that dumb objects may not be used for gpu acceleration, as has been diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 336be31ff3de..c8baab9bee0d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -35,8 +35,11 @@ #include <linux/vga_switcheroo.h> #include <drm/drmP.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_displayid.h> +#include "drm_crtc_internal.h" + #define version_greater(edid, maj, min) \ (((edid)->version > (maj)) || \ ((edid)->version == (maj) && (edid)->revision > (min))) @@ -90,7 +93,7 @@ struct detailed_mode_closure { #define LEVEL_GTF2 2 #define LEVEL_CVT 3 -static struct edid_quirk { +static const struct edid_quirk { char vendor[4]; int product_id; u32 quirks; @@ -1477,7 +1480,7 @@ EXPORT_SYMBOL(drm_edid_duplicate); * * Returns true if @vendor is in @edid, false otherwise */ -static bool edid_vendor(struct edid *edid, char *vendor) +static bool edid_vendor(struct edid *edid, const char *vendor) { char edid_vendor[3]; @@ -1497,7 +1500,7 @@ static bool edid_vendor(struct edid *edid, char *vendor) */ static u32 edid_get_quirks(struct edid *edid) { - struct edid_quirk *quirk; + const struct edid_quirk *quirk; int i; for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { @@ -2152,7 +2155,7 @@ drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, /* fix up 1366x768 mode from 1368x768; * GFT/CVT can't express 1366 width which isn't dividable by 8 */ -static void fixup_mode_1366x768(struct drm_display_mode *mode) +void drm_mode_fixup_1366x768(struct drm_display_mode *mode) { if (mode->hdisplay == 1368 && mode->vdisplay == 768) { mode->hdisplay = 1366; @@ -2176,7 +2179,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, if (!newmode) return modes; - fixup_mode_1366x768(newmode); + drm_mode_fixup_1366x768(newmode); if (!mode_in_range(newmode, edid, timing) || !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); @@ -2205,7 +2208,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, if (!newmode) return modes; - fixup_mode_1366x768(newmode); + drm_mode_fixup_1366x768(newmode); if (!mode_in_range(newmode, edid, timing) || !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); @@ -3767,6 +3770,25 @@ bool drm_rgb_quant_range_selectable(struct edid *edid) } EXPORT_SYMBOL(drm_rgb_quant_range_selectable); +/** + * drm_default_rgb_quant_range - default RGB quantization range + * @mode: display mode + * + * Determine the default RGB quantization range for the mode, + * as specified in CEA-861. + * + * Return: The default RGB quantization range for the mode + */ +enum hdmi_quantization_range +drm_default_rgb_quant_range(const struct drm_display_mode *mode) +{ + /* All CEA modes other than VIC 1 use limited quantization range. */ + return drm_match_cea_mode(mode) > 1 ? + HDMI_QUANTIZATION_RANGE_LIMITED : + HDMI_QUANTIZATION_RANGE_FULL; +} +EXPORT_SYMBOL(drm_default_rgb_quant_range); + static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, const u8 *hdmi) { @@ -4272,6 +4294,52 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, } EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); +/** + * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe + * quantization range information + * @frame: HDMI AVI infoframe + * @mode: DRM display mode + * @rgb_quant_range: RGB quantization range (Q) + * @rgb_quant_range_selectable: Sink support selectable RGB quantization range (QS) + */ +void +drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame, + const struct drm_display_mode *mode, + enum hdmi_quantization_range rgb_quant_range, + bool rgb_quant_range_selectable) +{ + /* + * CEA-861: + * "A Source shall not send a non-zero Q value that does not correspond + * to the default RGB Quantization Range for the transmitted Picture + * unless the Sink indicates support for the Q bit in a Video + * Capabilities Data Block." + * + * HDMI 2.0 recommends sending non-zero Q when it does match the + * default RGB quantization range for the mode, even when QS=0. + */ + if (rgb_quant_range_selectable || + rgb_quant_range == drm_default_rgb_quant_range(mode)) + frame->quantization_range = rgb_quant_range; + else + frame->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; + + /* + * CEA-861-F: + * "When transmitting any RGB colorimetry, the Source should set the + * YQ-field to match the RGB Quantization Range being transmitted + * (e.g., when Limited Range RGB, set YQ=0 or when Full Range RGB, + * set YQ=1) and the Sink shall ignore the YQ-field." + */ + if (rgb_quant_range == HDMI_QUANTIZATION_RANGE_LIMITED) + frame->ycc_quantization_range = + HDMI_YCC_QUANTIZATION_RANGE_LIMITED; + else + frame->ycc_quantization_range = + HDMI_YCC_QUANTIZATION_RANGE_FULL; +} +EXPORT_SYMBOL(drm_hdmi_avi_infoframe_quant_range); + static enum hdmi_3d_structure s3d_structure_from_display_mode(const struct drm_display_mode *mode) { diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 992879f15f23..129450713bb7 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -30,8 +30,8 @@ * DOC: overview * * Encoders represent the connecting element between the CRTC (as the overall - * pixel pipeline, represented by struct &drm_crtc) and the connectors (as the - * generic sink entity, represented by struct &drm_connector). An encoder takes + * pixel pipeline, represented by &struct drm_crtc) and the connectors (as the + * generic sink entity, represented by &struct drm_connector). An encoder takes * pixel data from a CRTC and converts it to a format suitable for any attached * connector. Encoders are objects exposed to userspace, originally to allow * userspace to infer cloning and connector/CRTC restrictions. Unfortunately @@ -98,7 +98,7 @@ void drm_encoder_unregister_all(struct drm_device *dev) * * Initialises a preallocated encoder. Encoder should be subclassed as part of * driver encoder objects. At driver unload time drm_encoder_cleanup() should be - * called from the driver's destroy hook in &drm_encoder_funcs. + * called from the driver's &drm_encoder_funcs.destroy hook. * * Returns: * Zero on success, error code on failure. @@ -159,6 +159,17 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) * the indices on the drm_encoder after us in the encoder_list. */ + if (encoder->bridge) { + struct drm_bridge *bridge = encoder->bridge; + struct drm_bridge *next; + + while (bridge) { + next = bridge->next; + drm_bridge_detach(bridge); + bridge = next; + } + } + drm_mode_object_unregister(dev, &encoder->base); kfree(encoder->name); list_del(&encoder->head); @@ -173,10 +184,12 @@ static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) struct drm_connector *connector; struct drm_device *dev = encoder->dev; bool uses_atomic = false; + struct drm_connector_list_iter conn_iter; /* For atomic drivers only state objects are synchronously updated and * protected by modeset locks, so check those first. */ - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (!connector->state) continue; @@ -185,8 +198,10 @@ static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) if (connector->state->best_encoder != encoder) continue; + drm_connector_list_iter_put(&conn_iter); return connector->state->crtc; } + drm_connector_list_iter_put(&conn_iter); /* Don't return stale data (e.g. pending async disable). */ if (uses_atomic) diff --git a/drivers/gpu/drm/drm_encoder_slave.c b/drivers/gpu/drm/drm_encoder_slave.c index 4484785cd9ac..cf804389f5ec 100644 --- a/drivers/gpu/drm/drm_encoder_slave.c +++ b/drivers/gpu/drm/drm_encoder_slave.c @@ -43,7 +43,7 @@ * &drm_encoder_slave. The @slave_funcs field will be initialized with * the hooks provided by the slave driver. * - * If @info->platform_data is non-NULL it will be used as the initial + * If @info.platform_data is non-NULL it will be used as the initial * slave config. * * Returns 0 on success or a negative errno on failure, in particular, diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 81b3558302b5..596fabf18c3e 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -39,6 +39,7 @@ struct drm_fb_cma { struct drm_fbdev_cma { struct drm_fb_helper fb_helper; struct drm_fb_cma *fb; + const struct drm_framebuffer_funcs *fb_funcs; }; /** @@ -47,50 +48,40 @@ struct drm_fbdev_cma { * Provides helper functions for creating a cma (contiguous memory allocator) * backed framebuffer. * - * drm_fb_cma_create() is used in the &drm_mode_config_funcs ->fb_create + * drm_fb_cma_create() is used in the &drm_mode_config_funcs.fb_create * callback function to create a cma backed framebuffer. * * An fbdev framebuffer backed by cma is also available by calling * drm_fbdev_cma_init(). drm_fbdev_cma_fini() tears it down. - * If the &drm_framebuffer_funcs ->dirty callback is set, fb_deferred_io - * will be set up automatically. dirty() is called by - * drm_fb_helper_deferred_io() in process context (struct delayed_work). + * If the &drm_framebuffer_funcs.dirty callback is set, fb_deferred_io will be + * set up automatically. &drm_framebuffer_funcs.dirty is called by + * drm_fb_helper_deferred_io() in process context (&struct delayed_work). * * Example fbdev deferred io code:: * - * static int driver_fbdev_fb_dirty(struct drm_framebuffer *fb, - * struct drm_file *file_priv, - * unsigned flags, unsigned color, - * struct drm_clip_rect *clips, - * unsigned num_clips) + * static int driver_fb_dirty(struct drm_framebuffer *fb, + * struct drm_file *file_priv, + * unsigned flags, unsigned color, + * struct drm_clip_rect *clips, + * unsigned num_clips) * { * struct drm_gem_cma_object *cma = drm_fb_cma_get_gem_obj(fb, 0); * ... push changes ... * return 0; * } * - * static struct drm_framebuffer_funcs driver_fbdev_fb_funcs = { + * static struct drm_framebuffer_funcs driver_fb_funcs = { * .destroy = drm_fb_cma_destroy, * .create_handle = drm_fb_cma_create_handle, - * .dirty = driver_fbdev_fb_dirty, + * .dirty = driver_fb_dirty, * }; * - * static int driver_fbdev_create(struct drm_fb_helper *helper, - * struct drm_fb_helper_surface_size *sizes) - * { - * return drm_fbdev_cma_create_with_funcs(helper, sizes, - * &driver_fbdev_fb_funcs); - * } - * - * static const struct drm_fb_helper_funcs driver_fb_helper_funcs = { - * .fb_probe = driver_fbdev_create, - * }; + * Initialize:: * - * Initialize: * fbdev = drm_fbdev_cma_init_with_funcs(dev, 16, * dev->mode_config.num_crtc, * dev->mode_config.num_connector, - * &driver_fb_helper_funcs); + * &driver_fb_funcs); * */ @@ -147,7 +138,7 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, if (!fb_cma) return ERR_PTR(-ENOMEM); - drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &fb_cma->fb, mode_cmd); for (i = 0; i < num_planes; i++) fb_cma->obj[i] = obj[i]; @@ -164,16 +155,16 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, /** * drm_fb_cma_create_with_funcs() - helper function for the - * &drm_mode_config_funcs ->fb_create - * callback function + * &drm_mode_config_funcs.fb_create + * callback * @dev: DRM device * @file_priv: drm file for the ioctl call * @mode_cmd: metadata from the userspace fb creation request * @funcs: vtable to be used for the new framebuffer object * * This can be used to set &drm_framebuffer_funcs for drivers that need the - * dirty() callback. Use drm_fb_cma_create() if you don't need to change - * &drm_framebuffer_funcs. + * &drm_framebuffer_funcs.dirty callback. Use drm_fb_cma_create() if you don't + * need to change &drm_framebuffer_funcs. */ struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd, @@ -230,14 +221,14 @@ err_gem_object_unreference: EXPORT_SYMBOL_GPL(drm_fb_cma_create_with_funcs); /** - * drm_fb_cma_create() - &drm_mode_config_funcs ->fb_create callback function + * drm_fb_cma_create() - &drm_mode_config_funcs.fb_create callback function * @dev: DRM device * @file_priv: drm file for the ioctl call * @mode_cmd: metadata from the userspace fb creation request * * If your hardware has special alignment or pitch requirements these should be * checked before calling this function. Use drm_fb_cma_create_with_funcs() if - * you need to set &drm_framebuffer_funcs ->dirty. + * you need to set &drm_framebuffer_funcs.dirty. */ struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) @@ -273,7 +264,7 @@ EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); * @plane: Which plane * @state: Plane state attach fence to * - * This should be put into prepare_fb hook of struct &drm_plane_helper_funcs . + * This should be set as the &struct drm_plane_helper_funcs.prepare_fb hook. * * This function checks if the plane FB has an dma-buf attached, extracts * the exclusive fence and attaches it to plane state for the atomic helper @@ -304,15 +295,12 @@ EXPORT_SYMBOL_GPL(drm_fb_cma_prepare_fb); static void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) { struct drm_fb_cma *fb_cma = to_fb_cma(fb); - const struct drm_format_info *info; int i; seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, - (char *)&fb->pixel_format); - - info = drm_format_info(fb->pixel_format); + (char *)&fb->format->format); - for (i = 0; i < info->num_planes; i++) { + for (i = 0; i < fb->format->num_planes; i++) { seq_printf(m, " %d: offset=%d pitch=%d, obj: ", i, fb->offsets[i], fb->pitches[i]); drm_gem_cma_describe(fb_cma->obj[i], m); @@ -411,13 +399,9 @@ static void drm_fbdev_cma_defio_fini(struct fb_info *fbi) kfree(fbi->fbops); } -/* - * For use in a (struct drm_fb_helper_funcs *)->fb_probe callback function that - * needs custom struct drm_framebuffer_funcs, like dirty() for deferred_io use. - */ -int drm_fbdev_cma_create_with_funcs(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes, - const struct drm_framebuffer_funcs *funcs) +static int +drm_fbdev_cma_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) { struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper); struct drm_mode_fb_cmd2 mode_cmd = { 0 }; @@ -453,7 +437,8 @@ int drm_fbdev_cma_create_with_funcs(struct drm_fb_helper *helper, goto err_gem_free_object; } - fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1, funcs); + fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1, + fbdev_cma->fb_funcs); if (IS_ERR(fbdev_cma->fb)) { dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); ret = PTR_ERR(fbdev_cma->fb); @@ -467,7 +452,7 @@ int drm_fbdev_cma_create_with_funcs(struct drm_fb_helper *helper, fbi->flags = FBINFO_FLAG_DEFAULT; fbi->fbops = &drm_fbdev_cma_ops; - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); offset = fbi->var.xoffset * bytes_per_pixel; @@ -479,7 +464,7 @@ int drm_fbdev_cma_create_with_funcs(struct drm_fb_helper *helper, fbi->screen_size = size; fbi->fix.smem_len = size; - if (funcs->dirty) { + if (fbdev_cma->fb_funcs->dirty) { ret = drm_fbdev_cma_defio_init(fbi, obj); if (ret) goto err_cma_destroy; @@ -488,21 +473,13 @@ int drm_fbdev_cma_create_with_funcs(struct drm_fb_helper *helper, return 0; err_cma_destroy: - drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); - drm_fb_cma_destroy(&fbdev_cma->fb->fb); + drm_framebuffer_remove(&fbdev_cma->fb->fb); err_fb_info_destroy: drm_fb_helper_release_fbi(helper); err_gem_free_object: drm_gem_object_unreference_unlocked(&obj->base); return ret; } -EXPORT_SYMBOL(drm_fbdev_cma_create_with_funcs); - -static int drm_fbdev_cma_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - return drm_fbdev_cma_create_with_funcs(helper, sizes, &drm_fb_cma_funcs); -} static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { .fb_probe = drm_fbdev_cma_create, @@ -512,15 +489,14 @@ static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { * drm_fbdev_cma_init_with_funcs() - Allocate and initializes a drm_fbdev_cma struct * @dev: DRM device * @preferred_bpp: Preferred bits per pixel for the device - * @num_crtc: Number of CRTCs * @max_conn_count: Maximum number of connectors - * @funcs: fb helper functions, in particular fb_probe() + * @funcs: fb helper functions, in particular a custom dirty() callback * * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. */ struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count, const struct drm_fb_helper_funcs *funcs) + unsigned int preferred_bpp, unsigned int max_conn_count, + const struct drm_framebuffer_funcs *funcs) { struct drm_fbdev_cma *fbdev_cma; struct drm_fb_helper *helper; @@ -531,12 +507,13 @@ struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev, dev_err(dev->dev, "Failed to allocate drm fbdev.\n"); return ERR_PTR(-ENOMEM); } + fbdev_cma->fb_funcs = funcs; helper = &fbdev_cma->fb_helper; - drm_fb_helper_prepare(dev, helper, funcs); + drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs); - ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); + ret = drm_fb_helper_init(dev, helper, max_conn_count); if (ret < 0) { dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); goto err_free; @@ -576,11 +553,11 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_init_with_funcs); * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. */ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int num_crtc, - unsigned int max_conn_count) + unsigned int preferred_bpp, unsigned int max_conn_count) { - return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp, num_crtc, - max_conn_count, &drm_fb_cma_helper_funcs); + return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp, + max_conn_count, + &drm_fb_cma_funcs); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); @@ -595,10 +572,8 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) drm_fbdev_cma_defio_fini(fbdev_cma->fb_helper.fbdev); drm_fb_helper_release_fbi(&fbdev_cma->fb_helper); - if (fbdev_cma->fb) { - drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); - drm_fb_cma_destroy(&fbdev_cma->fb->fb); - } + if (fbdev_cma->fb) + drm_framebuffer_remove(&fbdev_cma->fb->fb); drm_fb_helper_fini(&fbdev_cma->fb_helper); kfree(fbdev_cma); @@ -609,7 +584,7 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode * @fbdev_cma: The drm_fbdev_cma struct, may be NULL * - * This function is usually called from the DRM drivers lastclose callback. + * This function is usually called from the &drm_driver.lastclose callback. */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { @@ -622,7 +597,7 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events * @fbdev_cma: The drm_fbdev_cma struct, may be NULL * - * This function is usually called from the DRM drivers output_poll_changed + * This function is usually called from the &drm_mode_config.output_poll_changed * callback. */ void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) @@ -646,3 +621,21 @@ void drm_fbdev_cma_set_suspend(struct drm_fbdev_cma *fbdev_cma, int state) drm_fb_helper_set_suspend(&fbdev_cma->fb_helper, state); } EXPORT_SYMBOL(drm_fbdev_cma_set_suspend); + +/** + * drm_fbdev_cma_set_suspend_unlocked - wrapper around + * drm_fb_helper_set_suspend_unlocked + * @fbdev_cma: The drm_fbdev_cma struct, may be NULL + * @state: desired state, zero to resume, non-zero to suspend + * + * Calls drm_fb_helper_set_suspend, which is a wrapper around + * fb_set_suspend implemented by fbdev core. + */ +void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma, + int state) +{ + if (fbdev_cma) + drm_fb_helper_set_suspend_unlocked(&fbdev_cma->fb_helper, + state); +} +EXPORT_SYMBOL(drm_fbdev_cma_set_suspend_unlocked); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e934b541feea..f6d4d9700734 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -66,11 +66,11 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * Teardown is done with drm_fb_helper_fini(). * * At runtime drivers should restore the fbdev console by calling - * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback. - * They should also notify the fb helper code from updates to the output - * configuration by calling drm_fb_helper_hotplug_event(). For easier + * drm_fb_helper_restore_fbdev_mode_unlocked() from their &drm_driver.lastclose + * callback. They should also notify the fb helper code from updates to the + * output configuration by calling drm_fb_helper_hotplug_event(). For easier * integration with the output polling code in drm_crtc_helper.c the modeset - * code provides a ->output_poll_changed callback. + * code provides a &drm_mode_config_funcs.output_poll_changed callback. * * All other functions exported by the fb helper library can be used to * implement the fbdev driver interface by the driver. @@ -79,7 +79,7 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare() * helper must be called first to initialize the minimum required to make * hotplug detection work. Drivers also need to make sure to properly set up - * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init() + * the &drm_mode_config.funcs member. After calling drm_kms_helper_poll_init() * it is safe to enable interrupts and start processing hotplug events. At the * same time, drivers should initialize all modeset objects such as CRTCs, * encoders and connectors. To finish up the fbdev helper initialization, the @@ -88,9 +88,9 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * should call drm_fb_helper_single_add_all_connectors() followed by * drm_fb_helper_initial_config(). * - * If &drm_framebuffer_funcs ->dirty is set, the + * If &drm_framebuffer_funcs.dirty is set, the * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will - * accumulate changes and schedule &drm_fb_helper ->dirty_work to run right + * accumulate changes and schedule &drm_fb_helper.dirty_work to run right * away. This worker then calls the dirty() function ensuring that it will * always run in process context since the fb_*() function could be running in * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io @@ -120,20 +120,22 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; struct drm_connector *connector; - int i, ret; + struct drm_connector_list_iter conn_iter; + int i, ret = 0; if (!drm_fbdev_emulation) return 0; mutex_lock(&dev->mode_config.mutex); - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { ret = drm_fb_helper_add_one_connector(fb_helper, connector); if (ret) goto fail; } - mutex_unlock(&dev->mode_config.mutex); - return 0; + goto out; + fail: drm_fb_helper_for_each_connector(fb_helper, i) { struct drm_fb_helper_connector *fb_helper_connector = @@ -145,6 +147,8 @@ fail: fb_helper->connector_info[i] = NULL; } fb_helper->connector_count = 0; +out: + drm_connector_list_iter_put(&conn_iter); mutex_unlock(&dev->mode_config.mutex); return ret; @@ -243,7 +247,7 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) } /** - * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter + * drm_fb_helper_debug_enter - implementation for &fb_ops.fb_debug_enter * @info: fbdev registered by the helper */ int drm_fb_helper_debug_enter(struct fb_info *info) @@ -292,7 +296,7 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) } /** - * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave + * drm_fb_helper_debug_leave - implementation for &fb_ops.fb_debug_leave * @info: fbdev registered by the helper */ int drm_fb_helper_debug_leave(struct fb_info *info) @@ -401,7 +405,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) drm_warn_on_modeset_not_all_locked(dev); - if (dev->mode_config.funcs->atomic_commit) + if (drm_drv_uses_atomic_modeset(dev)) return restore_fbdev_mode_atomic(fb_helper); drm_for_each_plane(plane, dev) { @@ -441,7 +445,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration * @fb_helper: fbcon to restore * - * This should be called from driver's drm ->lastclose callback + * This should be called from driver's drm &drm_driver.lastclose callback * when implementing an fbcon on top of kms using this helper. This ensures that * the user isn't greeted with a black screen when e.g. X dies. * @@ -581,7 +585,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) } /** - * drm_fb_helper_blank - implementation for ->fb_blank + * drm_fb_helper_blank - implementation for &fb_ops.fb_blank * @blank: desired blanking state * @info: fbdev registered by the helper */ @@ -708,7 +712,6 @@ EXPORT_SYMBOL(drm_fb_helper_prepare); * drm_fb_helper_init - initialize a drm_fb_helper structure * @dev: drm device * @fb_helper: driver-allocated fbdev helper structure to initialize - * @crtc_count: maximum number of crtcs to support in this fbdev emulation * @max_conn_count: max connector count * * This allocates the structures for the fbdev helper with the given limits. @@ -723,9 +726,10 @@ EXPORT_SYMBOL(drm_fb_helper_prepare); */ int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, - int crtc_count, int max_conn_count) + int max_conn_count) { struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; int i; if (!drm_fbdev_emulation) @@ -734,11 +738,11 @@ int drm_fb_helper_init(struct drm_device *dev, if (!max_conn_count) return -EINVAL; - fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); + fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); if (!fb_helper->crtc_info) return -ENOMEM; - fb_helper->crtc_count = crtc_count; + fb_helper->crtc_count = config->num_crtc; fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); if (!fb_helper->connector_info) { kfree(fb_helper->crtc_info); @@ -747,7 +751,7 @@ int drm_fb_helper_init(struct drm_device *dev, fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; fb_helper->connector_count = 0; - for (i = 0; i < crtc_count; i++) { + for (i = 0; i < fb_helper->crtc_count; i++) { fb_helper->crtc_info[i].mode_set.connectors = kcalloc(max_conn_count, sizeof(struct drm_connector *), @@ -856,6 +860,9 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) if (!drm_fbdev_emulation) return; + cancel_work_sync(&fb_helper->resume_work); + cancel_work_sync(&fb_helper->dirty_work); + mutex_lock(&kernel_fb_helper_lock); if (!list_empty(&fb_helper->kernel_fb_list)) { list_del(&fb_helper->kernel_fb_list); @@ -908,7 +915,7 @@ static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y, * @info: fb_info struct pointer * @pagelist: list of dirty mmap framebuffer pages * - * This function is used as the &fb_deferred_io ->deferred_io + * This function is used as the &fb_deferred_io.deferred_io * callback function for flushing the fbdev mmap writes. */ void drm_fb_helper_deferred_io(struct fb_info *info, @@ -1099,7 +1106,7 @@ EXPORT_SYMBOL(drm_fb_helper_set_suspend); * due to all the printk activity. * * This function can be called multiple times with the same state since - * &fb_info->state is checked to see if fbdev is running or not before locking. + * &fb_info.state is checked to see if fbdev is running or not before locking. * * Use drm_fb_helper_set_suspend() if you need to take the lock yourself. */ @@ -1169,7 +1176,7 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, !fb_helper->funcs->gamma_get)) return -EINVAL; - WARN_ON(fb->bits_per_pixel != 8); + WARN_ON(fb->format->cpp[0] != 1); fb_helper->funcs->gamma_set(crtc, red, green, blue, regno); @@ -1177,7 +1184,7 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, } /** - * drm_fb_helper_setcmap - implementation for ->fb_setcmap + * drm_fb_helper_setcmap - implementation for &fb_ops.fb_setcmap * @cmap: cmap to set * @info: fbdev registered by the helper */ @@ -1234,7 +1241,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) EXPORT_SYMBOL(drm_fb_helper_setcmap); /** - * drm_fb_helper_check_var - implementation for ->fb_check_var + * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var * @var: screeninfo to check * @info: fbdev registered by the helper */ @@ -1252,14 +1259,14 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, * Changes struct fb_var_screeninfo are currently not pushed back * to KMS, hence fail if different settings are requested. */ - if (var->bits_per_pixel != fb->bits_per_pixel || + if (var->bits_per_pixel != fb->format->cpp[0] * 8 || var->xres != fb->width || var->yres != fb->height || var->xres_virtual != fb->width || var->yres_virtual != fb->height) { DRM_DEBUG("fb userspace requested width/height/bpp different than current fb " "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel, var->xres_virtual, var->yres_virtual, - fb->width, fb->height, fb->bits_per_pixel); + fb->width, fb->height, fb->format->cpp[0] * 8); return -EINVAL; } @@ -1334,7 +1341,7 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, EXPORT_SYMBOL(drm_fb_helper_check_var); /** - * drm_fb_helper_set_par - implementation for ->fb_set_par + * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par * @info: fbdev registered by the helper * * This will let fbcon do the mode init and is called at initialization time by @@ -1418,7 +1425,7 @@ backoff: } /** - * drm_fb_helper_pan_display - implementation for ->fb_pan_display + * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display * @var: updated screen information * @info: fbdev registered by the helper */ @@ -1440,7 +1447,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, return -EBUSY; } - if (dev->mode_config.funcs->atomic_commit) { + if (drm_drv_uses_atomic_modeset(dev)) { ret = pan_display_atomic(var, info); goto unlock; } @@ -1603,7 +1610,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, * additional constraints need to set up their own limits. * * Drivers should call this (or their equivalent setup code) from their - * ->fb_probe callback. + * &drm_fb_helper_funcs.fb_probe callback. */ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) @@ -1632,11 +1639,11 @@ EXPORT_SYMBOL(drm_fb_helper_fill_fix); * @fb_height: desired fb height * * Sets up the variable fbdev metainformation from the given fb helper instance - * and the drm framebuffer allocated in fb_helper->fb. + * and the drm framebuffer allocated in &drm_fb_helper.fb. * * Drivers should call this (or their equivalent setup code) from their - * ->fb_probe callback after having allocated the fbdev backing - * storage framebuffer. + * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev + * backing storage framebuffer. */ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height) @@ -1645,7 +1652,7 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe info->pseudo_palette = fb_helper->pseudo_palette; info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; - info->var.bits_per_pixel = fb->bits_per_pixel; + info->var.bits_per_pixel = fb->format->cpp[0] * 8; info->var.accel_flags = FB_ACCELF_TEXT; info->var.xoffset = 0; info->var.yoffset = 0; @@ -1653,7 +1660,7 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe info->var.height = -1; info->var.width = -1; - switch (fb->depth) { + switch (fb->format->depth) { case 8: info->var.red.offset = 0; info->var.green.offset = 0; @@ -1748,8 +1755,7 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) return fb_connector->connector->cmdline_mode.specified; } -struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, - int width, int height) +struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn) { struct drm_cmdline_mode *cmdline_mode; struct drm_display_mode *mode; @@ -1867,7 +1873,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper, if (!enabled[i]) continue; fb_helper_conn = fb_helper->connector_info[i]; - modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); + modes[i] = drm_pick_cmdline_mode(fb_helper_conn); if (!modes[i]) { can_clone = false; break; @@ -1989,7 +1995,7 @@ retry: fb_helper_conn->connector->base.id); /* got for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); + modes[i] = drm_pick_cmdline_mode(fb_helper_conn); if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); @@ -2056,7 +2062,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, * NULL we fallback to the default drm_atomic_helper_best_encoder() * helper. */ - if (fb_helper->dev->mode_config.funcs->atomic_commit && + if (drm_drv_uses_atomic_modeset(fb_helper->dev) && !connector_funcs->best_encoder) encoder = drm_atomic_helper_best_encoder(connector); else @@ -2204,9 +2210,9 @@ out: * Note that this also registers the fbdev and so allows userspace to call into * the driver through the fbdev interfaces. * - * This function will call down into the ->fb_probe callback to let - * the driver allocate and initialize the fbdev info structure and the drm - * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and + * This function will call down into the &drm_fb_helper_funcs.fb_probe callback + * to let the driver allocate and initialize the fbdev info structure and the + * drm framebuffer used to back the fbdev. drm_fb_helper_fill_var() and * drm_fb_helper_fill_fix() are provided as helpers to setup simple default * values for the fbdev info structure. * diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 5d96de40b63f..afdf5b147f39 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -580,7 +580,7 @@ EXPORT_SYMBOL(drm_poll); * kmalloc and @p must be the first member element. * * This is the locked version of drm_event_reserve_init() for callers which - * already hold dev->event_lock. + * already hold &drm_device.event_lock. * * RETURNS: * @@ -621,8 +621,8 @@ EXPORT_SYMBOL(drm_event_reserve_init_locked); * If callers embedded @p into a larger structure it must be allocated with * kmalloc and @p must be the first member element. * - * Callers which already hold dev->event_lock should use - * drm_event_reserve_init() instead. + * Callers which already hold &drm_device.event_lock should use + * drm_event_reserve_init_locked() instead. * * RETURNS: * @@ -677,7 +677,7 @@ EXPORT_SYMBOL(drm_event_cancel_free); * * This function sends the event @e, initialized with drm_event_reserve_init(), * to its associated userspace DRM file. Callers must already hold - * dev->event_lock, see drm_send_event() for the unlocked version. + * &drm_device.event_lock, see drm_send_event() for the unlocked version. * * Note that the core will take care of unlinking and disarming events when the * corresponding DRM file is closed. Drivers need not worry about whether the @@ -689,8 +689,8 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) assert_spin_locked(&dev->event_lock); if (e->completion) { - /* ->completion might disappear as soon as it signalled. */ complete_all(e->completion); + e->completion_release(e->completion); e->completion = NULL; } @@ -717,8 +717,9 @@ EXPORT_SYMBOL(drm_send_event_locked); * @e: DRM event to deliver * * This function sends the event @e, initialized with drm_event_reserve_init(), - * to its associated userspace DRM file. This function acquires dev->event_lock, - * see drm_send_event_locked() for callers which already hold this lock. + * to its associated userspace DRM file. This function acquires + * &drm_device.event_lock, see drm_send_event_locked() for callers which already + * hold this lock. * * Note that the core will take care of unlinking and disarming events when the * corresponding DRM file is closed. Drivers need not worry about whether the diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index cbf0c893f426..28a0108a1ab8 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -39,13 +39,13 @@ * Frame buffers rely on the underlying memory manager for allocating backing * storage. When creating a frame buffer applications pass a memory handle * (or a list of memory handles for multi-planar formats) through the - * struct &drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace + * &struct drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace * buffer management interface this would be a GEM handle. Drivers are however * free to use their own backing storage object handles, e.g. vmwgfx directly * exposes special TTM handles to userspace and so expects TTM handles in the * create ioctl and not GEM handles. * - * Framebuffers are tracked with struct &drm_framebuffer. They are published + * Framebuffers are tracked with &struct drm_framebuffer. They are published * using drm_framebuffer_init() - after calling that function userspace can use * and access the framebuffer object. The helper function * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required @@ -55,11 +55,11 @@ * drivers can grab additional references with drm_framebuffer_reference() and * drop them again with drm_framebuffer_unreference(). For driver-private * framebuffers for which the last reference is never dropped (e.g. for the - * fbdev framebuffer when the struct struct &drm_framebuffer is embedded into + * fbdev framebuffer when the struct &struct drm_framebuffer is embedded into * the fbdev helper struct) drivers can manually clean up a framebuffer at * module unload time with drm_framebuffer_unregister_private(). But doing this - * is not recommended, and it's better to have a normal free-standing struct - * &drm_framebuffer. + * is not recommended, and it's better to have a normal free-standing &struct + * drm_framebuffer. */ int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y, @@ -432,8 +432,8 @@ int drm_mode_getfb(struct drm_device *dev, r->height = fb->height; r->width = fb->width; - r->depth = fb->depth; - r->bpp = fb->bits_per_pixel; + r->depth = fb->format->depth; + r->bpp = fb->format->cpp[0] * 8; r->pitch = fb->pitches[0]; if (fb->funcs->create_handle) { if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) || @@ -470,7 +470,7 @@ int drm_mode_getfb(struct drm_device *dev, * usb display-link, mipi manual update panels or edp panel self refresh modes. * * Modesetting drivers which always update the frontbuffer do not need to - * implement the corresponding ->dirty framebuffer callback. + * implement the corresponding &drm_framebuffer_funcs.dirty callback. * * Called by the user via ioctl. * @@ -631,8 +631,11 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, { int ret; + if (WARN_ON_ONCE(fb->dev != dev || !fb->format)) + return -EINVAL; + INIT_LIST_HEAD(&fb->filp_head); - fb->dev = dev; + fb->funcs = funcs; ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB, @@ -706,10 +709,10 @@ EXPORT_SYMBOL(drm_framebuffer_unregister_private); * @fb: framebuffer to remove * * Cleanup framebuffer. This function is intended to be used from the drivers - * ->destroy callback. It can also be used to clean up driver private - * framebuffers embedded into a larger structure. + * &drm_framebuffer_funcs.destroy callback. It can also be used to clean up + * driver private framebuffers embedded into a larger structure. * - * Note that this function does not remove the fb from active usuage - if it is + * Note that this function does not remove the fb from active usage - if it is * still used anywhere, hilarity can ensue since userspace could call getfb on * the id and get back -EINVAL. Obviously no concern at driver unload time. * @@ -790,3 +793,47 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) drm_framebuffer_unreference(fb); } EXPORT_SYMBOL(drm_framebuffer_remove); + +/** + * drm_framebuffer_plane_width - width of the plane given the first plane + * @width: width of the first plane + * @fb: the framebuffer + * @plane: plane index + * + * Returns: + * The width of @plane, given that the width of the first plane is @width. + */ +int drm_framebuffer_plane_width(int width, + const struct drm_framebuffer *fb, int plane) +{ + if (plane >= fb->format->num_planes) + return 0; + + if (plane == 0) + return width; + + return width / fb->format->hsub; +} +EXPORT_SYMBOL(drm_framebuffer_plane_width); + +/** + * drm_framebuffer_plane_height - height of the plane given the first plane + * @height: height of the first plane + * @fb: the framebuffer + * @plane: plane index + * + * Returns: + * The height of @plane, given that the height of the first plane is @height. + */ +int drm_framebuffer_plane_height(int height, + const struct drm_framebuffer *fb, int plane) +{ + if (plane >= fb->format->num_planes) + return 0; + + if (plane == 0) + return height; + + return height / fb->format->vsub; +} +EXPORT_SYMBOL(drm_framebuffer_plane_height); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 465bacd0a630..bc93de308673 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -316,8 +316,8 @@ EXPORT_SYMBOL(drm_gem_handle_delete); * @dev: corresponding drm_device * @handle: the dumb handle to remove * - * This implements the ->dumb_destroy kms driver callback for drivers which use - * gem to manage their backing storage. + * This implements the &drm_driver.dumb_destroy kms driver callback for drivers + * which use gem to manage their backing storage. */ int drm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, @@ -333,9 +333,9 @@ EXPORT_SYMBOL(drm_gem_dumb_destroy); * @obj: object to register * @handlep: pointer to return the created handle to the caller * - * This expects the dev->object_name_lock to be held already and will drop it - * before returning. Used to avoid races in establishing new handles when - * importing an object from either an flink name or a dma-buf. + * This expects the &drm_device.object_name_lock to be held already and will + * drop it before returning. Used to avoid races in establishing new handles + * when importing an object from either an flink name or a dma-buf. * * Handles must be release again through drm_gem_handle_delete(). This is done * when userspace closes @file_priv for all attached handles, or through the @@ -447,8 +447,8 @@ EXPORT_SYMBOL(drm_gem_free_mmap_offset); * structures. * * This routine allocates and attaches a fake offset for @obj, in cases where - * the virtual size differs from the physical size (ie. obj->size). Otherwise - * just use drm_gem_create_mmap_offset(). + * the virtual size differs from the physical size (ie. &drm_gem_object.size). + * Otherwise just use drm_gem_create_mmap_offset(). * * This function is idempotent and handles an already allocated mmap offset * transparently. Drivers do not need to check for this case. @@ -787,7 +787,7 @@ EXPORT_SYMBOL(drm_gem_object_release); * @kref: kref of the object to free * * Called after the last reference to the object has been lost. - * Must be called holding &drm_device->struct_mutex. + * Must be called holding &drm_device.struct_mutex. * * Frees the object */ @@ -813,7 +813,7 @@ EXPORT_SYMBOL(drm_gem_object_free); * @obj: GEM buffer object * * This releases a reference to @obj. Callers must not hold the - * dev->struct_mutex lock when calling this function. + * &drm_device.struct_mutex lock when calling this function. * * See also __drm_gem_object_unreference(). */ @@ -840,9 +840,9 @@ EXPORT_SYMBOL(drm_gem_object_unreference_unlocked); * drm_gem_object_unreference - release a GEM BO reference * @obj: GEM buffer object * - * This releases a reference to @obj. Callers must hold the dev->struct_mutex - * lock when calling this function, even when the driver doesn't use - * dev->struct_mutex for anything. + * This releases a reference to @obj. Callers must hold the + * &drm_device.struct_mutex lock when calling this function, even when the + * driver doesn't use &drm_device.struct_mutex for anything. * * For drivers not encumbered with legacy locking use * drm_gem_object_unreference_unlocked() instead. diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 33cd51632721..f7ba32bfe39b 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -176,8 +176,8 @@ drm_gem_cma_create_with_handle(struct drm_file *file_priv, * * This function frees the backing memory of the CMA GEM object, cleans up the * GEM object state and frees the memory used to store the object itself. - * Drivers using the CMA helpers should set this as their DRM driver's - * ->gem_free_object() callback. + * Drivers using the CMA helpers should set this as their + * &drm_driver.gem_free_object callback. */ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) { @@ -207,7 +207,7 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_free_object); * This aligns the pitch and size arguments to the minimum required. This is * an internal helper that can be wrapped by a driver to account for hardware * with more specific alignment requirements. It should not be used directly - * as the ->dumb_create() callback in a DRM driver. + * as their &drm_driver.dumb_create callback. * * Returns: * 0 on success or a negative error code on failure. @@ -240,7 +240,7 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create_internal); * This function computes the pitch of the dumb buffer and rounds it up to an * integer number of bytes per pixel. Drivers for hardware that doesn't have * any additional restrictions on the pitch can directly use this function as - * their ->dumb_create() callback. + * their &drm_driver.dumb_create callback. * * For hardware with additional restrictions, drivers can adjust the fields * set up by userspace and pass the IOCTL data along to the @@ -274,7 +274,7 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create); * * This function look up an object by its handle and returns the fake mmap * offset associated with it. Drivers using the CMA helpers should set this - * as their DRM driver's ->dumb_map_offset() callback. + * as their &drm_driver.dumb_map_offset callback. * * Returns: * 0 on success or a negative error code on failure. @@ -358,6 +358,77 @@ int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma) } EXPORT_SYMBOL_GPL(drm_gem_cma_mmap); +#ifndef CONFIG_MMU +/** + * drm_gem_cma_get_unmapped_area - propose address for mapping in noMMU cases + * @filp: file object + * @addr: memory address + * @len: buffer size + * @pgoff: page offset + * @flags: memory flags + * + * This function is used in noMMU platforms to propose address mapping + * for a given buffer. + * It's intended to be used as a direct handler for the struct + * &file_operations.get_unmapped_area operation. + * + * Returns: + * mapping address on success or a negative error code on failure. + */ +unsigned long drm_gem_cma_get_unmapped_area(struct file *filp, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *obj = NULL; + struct drm_file *priv = filp->private_data; + struct drm_device *dev = priv->minor->dev; + struct drm_vma_offset_node *node; + + if (drm_device_is_unplugged(dev)) + return -ENODEV; + + drm_vma_offset_lock_lookup(dev->vma_offset_manager); + node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager, + pgoff, + len >> PAGE_SHIFT); + if (likely(node)) { + obj = container_of(node, struct drm_gem_object, vma_node); + /* + * When the object is being freed, after it hits 0-refcnt it + * proceeds to tear down the object. In the process it will + * attempt to remove the VMA offset and so acquire this + * mgr->vm_lock. Therefore if we find an object with a 0-refcnt + * that matches our range, we know it is in the process of being + * destroyed and will be freed as soon as we release the lock - + * so we have to check for the 0-refcnted object and treat it as + * invalid. + */ + if (!kref_get_unless_zero(&obj->refcount)) + obj = NULL; + } + + drm_vma_offset_unlock_lookup(dev->vma_offset_manager); + + if (!obj) + return -EINVAL; + + if (!drm_vma_node_is_allowed(node, priv)) { + drm_gem_object_unreference_unlocked(obj); + return -EACCES; + } + + cma_obj = to_drm_gem_cma_obj(obj); + + drm_gem_object_unreference_unlocked(obj); + + return cma_obj->vaddr ? (unsigned long)cma_obj->vaddr : -EINVAL; +} +EXPORT_SYMBOL_GPL(drm_gem_cma_get_unmapped_area); +#endif + #ifdef CONFIG_DEBUG_FS /** * drm_gem_cma_describe - describe a CMA GEM object for debugfs @@ -391,7 +462,7 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_describe); * * This function exports a scatter/gather table suitable for PRIME usage by * calling the standard DMA mapping API. Drivers using the CMA helpers should - * set this as their DRM driver's ->gem_prime_get_sg_table() callback. + * set this as their &drm_driver.gem_prime_get_sg_table callback. * * Returns: * A pointer to the scatter/gather table of pinned pages or NULL on failure. @@ -429,8 +500,8 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table); * This function imports a scatter/gather table exported via DMA-BUF by * another driver. Imported buffers must be physically contiguous in memory * (i.e. the scatter/gather table must contain a single entry). Drivers that - * use the CMA helpers should set this as their DRM driver's - * ->gem_prime_import_sg_table() callback. + * use the CMA helpers should set this as their + * &drm_driver.gem_prime_import_sg_table callback. * * Returns: * A pointer to a newly created GEM object or an ERR_PTR-encoded negative @@ -467,7 +538,7 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_prime_import_sg_table); * * This function maps a buffer imported via DRM PRIME into a userspace * process's address space. Drivers that use the CMA helpers should set this - * as their DRM driver's ->gem_prime_mmap() callback. + * as their &drm_driver.gem_prime_mmap callback. * * Returns: * 0 on success or a negative error code on failure. @@ -496,7 +567,7 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_prime_mmap); * virtual address space. Since the CMA buffers are already mapped into the * kernel virtual address space this simply returns the cached virtual * address. Drivers using the CMA helpers should set this as their DRM - * driver's ->gem_prime_vmap() callback. + * driver's &drm_driver.gem_prime_vmap callback. * * Returns: * The kernel virtual address of the CMA GEM object's backing store. @@ -518,7 +589,7 @@ EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vmap); * This function removes a buffer exported via DRM PRIME from the kernel's * virtual address space. This is a no-op because CMA buffers cannot be * unmapped from kernel space. Drivers using the CMA helpers should set this - * as their DRM driver's ->gem_prime_vunmap() callback. + * as their &drm_driver.gem_prime_vunmap callback. */ void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { diff --git a/drivers/gpu/drm/drm_global.c b/drivers/gpu/drm/drm_global.c index b404287abb97..b2dc21e33ae0 100644 --- a/drivers/gpu/drm/drm_global.c +++ b/drivers/gpu/drm/drm_global.c @@ -63,6 +63,18 @@ void drm_global_release(void) } } +/** + * drm_global_item_ref - Initialize and acquire reference to memory + * object + * @ref: Object for initialization + * + * This initializes a memory object, allocating memory and calling the + * .init() hook. Further calls will increase the reference count for + * that item. + * + * Returns: + * Zero on success, non-zero otherwise. + */ int drm_global_item_ref(struct drm_global_reference *ref) { int ret = 0; @@ -97,6 +109,17 @@ error_unlock: } EXPORT_SYMBOL(drm_global_item_ref); +/** + * drm_global_item_unref - Drop reference to memory + * object + * @ref: Object being removed + * + * Drop a reference to the memory object and eventually call the + * release() hook. The allocated object should be dropped in the + * release() hook or before calling this function + * + */ + void drm_global_item_unref(struct drm_global_reference *ref) { struct drm_global_item *item = &glob[ref->global_type]; diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index db80ec860e33..f37388cb2fde 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -31,6 +31,7 @@ void drm_lastclose(struct drm_device *dev); /* drm_pci.c */ int drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv); +void drm_pci_agp_destroy(struct drm_device *dev); /* drm_prime.c */ int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, @@ -58,10 +59,10 @@ extern unsigned int drm_timestamp_monotonic; /* IOCTLS */ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); -int drm_control(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int drm_modeset_ctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); +int drm_legacy_irq_control(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_legacy_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); /* drm_auth.c */ int drm_getmagic(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index fed22c2b98b6..a7c61c23685a 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -95,9 +95,6 @@ * broken. */ -static int drm_version(struct drm_device *dev, void *data, - struct drm_file *file_priv); - /* * Get the bus id. * @@ -115,11 +112,15 @@ static int drm_getunique(struct drm_device *dev, void *data, struct drm_unique *u = data; struct drm_master *master = file_priv->master; + mutex_lock(&master->dev->master_mutex); if (u->unique_len >= master->unique_len) { - if (copy_to_user(u->unique, master->unique, master->unique_len)) + if (copy_to_user(u->unique, master->unique, master->unique_len)) { + mutex_unlock(&master->dev->master_mutex); return -EFAULT; + } } u->unique_len = master->unique_len; + mutex_unlock(&master->dev->master_mutex); return 0; } @@ -340,6 +341,7 @@ static int drm_setversion(struct drm_device *dev, void *data, struct drm_file *f struct drm_set_version *sv = data; int if_version, retcode = 0; + mutex_lock(&dev->master_mutex); if (sv->drm_di_major != -1) { if (sv->drm_di_major != DRM_IF_MAJOR || sv->drm_di_minor < 0 || sv->drm_di_minor > DRM_IF_MINOR) { @@ -374,6 +376,7 @@ done: sv->drm_di_minor = DRM_IF_MINOR; sv->drm_dd_major = dev->driver->major; sv->drm_dd_minor = dev->driver->minor; + mutex_unlock(&dev->master_mutex); return retcode; } @@ -475,15 +478,17 @@ static int drm_version(struct drm_device *dev, void *data, return err; } -/* +/** * drm_ioctl_permit - Check ioctl permissions against caller * * @flags: ioctl permission flags. * @file_priv: Pointer to struct drm_file identifying the caller. * * Checks whether the caller is allowed to run an ioctl with the - * indicated permissions. If so, returns zero. Otherwise returns an - * error code suitable for ioctl return. + * indicated permissions. + * + * Returns: + * Zero if allowed, -EACCES otherwise. */ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) { @@ -528,15 +533,15 @@ EXPORT_SYMBOL(drm_ioctl_permit); static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW|DRM_CONTROL_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), + DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_legacy_getmap_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0), - DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), + DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_UNLOCKED | DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -575,7 +580,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_legacy_freebufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_legacy_dma_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_legacy_irq_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), #if IS_ENABLED(CONFIG_AGP) DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -593,7 +598,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0), + DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_legacy_modeset_ctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -729,9 +734,8 @@ long drm_ioctl(struct file *filp, if (ksize > in_size) memset(kdata + in_size, 0, ksize - in_size); - /* Enforce sane locking for modern driver ioctls. Core ioctls are - * too messy still. */ - if ((!drm_core_check_feature(dev, DRIVER_LEGACY) && is_driver_ioctl) || + /* Enforce sane locking for modern driver ioctls. */ + if (!drm_core_check_feature(dev, DRIVER_LEGACY) || (ioctl->flags & DRM_UNLOCKED)) retcode = func(dev, kdata, file_priv); else { diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 273625a85036..e06cf11ebb4a 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -95,7 +95,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, * * Only to be called from drm_crtc_vblank_on(). * - * Note: caller must hold dev->vbl_lock since this reads & writes + * Note: caller must hold &drm_device.vbl_lock since this reads & writes * device vblank fields. */ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe) @@ -142,7 +142,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe * Only necessary when going from off->on, to account for frames we * didn't get an interrupt for. * - * Note: caller must hold dev->vbl_lock since this reads & writes + * Note: caller must hold &drm_device.vbl_lock since this reads & writes * device vblank fields. */ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, @@ -415,29 +415,6 @@ err: } EXPORT_SYMBOL(drm_vblank_init); -static void drm_irq_vgaarb_nokms(void *cookie, bool state) -{ - struct drm_device *dev = cookie; - - if (dev->driver->vgaarb_irq) { - dev->driver->vgaarb_irq(dev, state); - return; - } - - if (!dev->irq_enabled) - return; - - if (state) { - if (dev->driver->irq_uninstall) - dev->driver->irq_uninstall(dev); - } else { - if (dev->driver->irq_preinstall) - dev->driver->irq_preinstall(dev); - if (dev->driver->irq_postinstall) - dev->driver->irq_postinstall(dev); - } -} - /** * drm_irq_install - install IRQ handler * @dev: DRM device @@ -449,7 +426,7 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) * * This is the simplified helper interface provided for drivers with no special * needs. Drivers which need to install interrupt handlers for multiple - * interrupts must instead set drm_device->irq_enabled to signal the DRM core + * interrupts must instead set &drm_device.irq_enabled to signal the DRM core * that vblank interrupts are available. * * Returns: @@ -492,9 +469,6 @@ int drm_irq_install(struct drm_device *dev, int irq) return ret; } - if (drm_core_check_feature(dev, DRIVER_LEGACY)) - vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL); - /* After installing handler */ if (dev->driver->irq_postinstall) ret = dev->driver->irq_postinstall(dev); @@ -519,7 +493,7 @@ EXPORT_SYMBOL(drm_irq_install); * Calls the driver's irq_uninstall() function and unregisters the IRQ handler. * This should only be called by drivers which used drm_irq_install() to set up * their interrupt handler. Other drivers must only reset - * drm_device->irq_enabled to false. + * &drm_device.irq_enabled to false. * * Note that for kernel modesetting drivers it is a bug if this function fails. * The sanity checks are only to catch buggy user modesetting drivers which call @@ -579,19 +553,8 @@ int drm_irq_uninstall(struct drm_device *dev) } EXPORT_SYMBOL(drm_irq_uninstall); -/* - * IRQ control ioctl. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_control structure. - * \return zero on success or a negative number on failure. - * - * Calls irq_install() or irq_uninstall() according to \p arg. - */ -int drm_control(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_irq_control(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_control *ctl = data; int ret = 0, irq; @@ -993,12 +956,11 @@ static void send_vblank_event(struct drm_device *dev, * period. This helper function implements exactly the required vblank arming * behaviour. * - * NOTE: Drivers using this to send out the event in struct &drm_crtc_state - * as part of an atomic commit must ensure that the next vblank happens at - * exactly the same time as the atomic commit is committed to the hardware. This - * function itself does **not** protect again the next vblank interrupt racing - * with either this function call or the atomic commit operation. A possible - * sequence could be: + * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an + * atomic commit must ensure that the next vblank happens at exactly the same + * time as the atomic commit is committed to the hardware. This function itself + * does **not** protect again the next vblank interrupt racing with either this + * function call or the atomic commit operation. A possible sequence could be: * * 1. Driver commits new hardware state into vblank-synchronized registers. * 2. A vblank happens, committing the hardware state. Also the corresponding @@ -1442,19 +1404,8 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev, } } -/* - * drm_modeset_ctl - handle vblank event counter changes across mode switch - * @DRM_IOCTL_ARGS: standard ioctl arguments - * - * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET - * ioctls around modesetting so that any lost vblank events are accounted for. - * - * Generally the counter will reset across mode sets. If interrupts are - * enabled around this call, we don't have to do anything since the counter - * will have already been incremented. - */ -int drm_modeset_ctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_modeset_ctl *modeset = data; unsigned int pipe; diff --git a/drivers/gpu/drm/drm_legacy.h b/drivers/gpu/drm/drm_legacy.h index c6f422e879dd..e4bb5ad747c8 100644 --- a/drivers/gpu/drm/drm_legacy.h +++ b/drivers/gpu/drm/drm_legacy.h @@ -74,7 +74,14 @@ int drm_legacy_freebufs(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_mapbufs(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_dma_ioctl(struct drm_device *d, void *v, struct drm_file *f); +#ifdef CONFIG_DRM_VM void drm_legacy_vma_flush(struct drm_device *d); +#else +static inline void drm_legacy_vma_flush(struct drm_device *d) +{ + /* do nothing */ +} +#endif /* * AGP Support diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index 32d43f86a8f2..96bb6badb818 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -34,6 +34,8 @@ */ #include <linux/export.h> +#include <linux/sched/signal.h> + #include <drm/drmP.h> #include "drm_legacy.h" #include "drm_internal.h" diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index ca1e344f318d..8bfb0b327267 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -1,6 +1,7 @@ /************************************************************************** * * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * Copyright 2016 Intel Corporation * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -31,9 +32,9 @@ * class implementation for more advanced memory managers. * * Note that the algorithm used is quite simple and there might be substantial - * performance gains if a smarter free list is implemented. Currently it is just an - * unordered stack of free regions. This could easily be improved if an RB-tree - * is used instead. At least if we expect heavy fragmentation. + * performance gains if a smarter free list is implemented. Currently it is + * just an unordered stack of free regions. This could easily be improved if + * an RB-tree is used instead. At least if we expect heavy fragmentation. * * Aligned allocations can also see improvement. * @@ -58,8 +59,8 @@ * * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node. * Drivers are free to embed either of them into their own suitable - * datastructures. drm_mm itself will not do any allocations of its own, so if - * drivers choose not to embed nodes they need to still allocate them + * datastructures. drm_mm itself will not do any memory allocations of its own, + * so if drivers choose not to embed nodes they need to still allocate them * themselves. * * The range allocator also supports reservation of preallocated blocks. This is @@ -67,7 +68,7 @@ * where an object needs to be created which exactly matches the firmware's * scanout target. As long as the range is still free it can be inserted anytime * after the allocator is initialized, which helps with avoiding looped - * depencies in the driver load sequence. + * dependencies in the driver load sequence. * * drm_mm maintains a stack of most recently freed holes, which of all * simplistic datastructures seems to be a fairly decent approach to clustering @@ -77,33 +78,25 @@ * steep cliff not a real concern. Removing a node again is O(1). * * drm_mm supports a few features: Alignment and range restrictions can be - * supplied. Further more every &drm_mm_node has a color value (which is just an - * opaqua unsigned long) which in conjunction with a driver callback can be used + * supplied. Furthermore every &drm_mm_node has a color value (which is just an + * opaque unsigned long) which in conjunction with a driver callback can be used * to implement sophisticated placement restrictions. The i915 DRM driver uses * this to implement guard pages between incompatible caching domains in the * graphics TT. * - * Two behaviors are supported for searching and allocating: bottom-up and top-down. - * The default is bottom-up. Top-down allocation can be used if the memory area - * has different restrictions, or just to reduce fragmentation. + * Two behaviors are supported for searching and allocating: bottom-up and + * top-down. The default is bottom-up. Top-down allocation can be used if the + * memory area has different restrictions, or just to reduce fragmentation. * * Finally iteration helpers to walk all nodes and all holes are provided as are * some basic allocator dumpers for debugging. + * + * Note that this range allocator is not thread-safe, drivers need to protect + * modifications with their on locking. The idea behind this is that for a full + * memory manager additional data needs to be protected anyway, hence internal + * locking would be fully redundant. */ -static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, - u64 size, - unsigned alignment, - unsigned long color, - enum drm_mm_search_flags flags); -static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, - u64 size, - unsigned alignment, - unsigned long color, - u64 start, - u64 end, - enum drm_mm_search_flags flags); - #ifdef CONFIG_DRM_DEBUG_MM #include <linux/stackdepot.h> @@ -138,7 +131,7 @@ static void show_leaks(struct drm_mm *mm) if (!buf) return; - list_for_each_entry(node, &mm->head_node.node_list, node_list) { + list_for_each_entry(node, drm_mm_nodes(mm), node_list) { struct stack_trace trace = { .entries = entries, .max_entries = STACKDEPTH @@ -174,9 +167,9 @@ INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, START, LAST, static inline, drm_mm_interval_tree) struct drm_mm_node * -__drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last) +__drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last) { - return drm_mm_interval_tree_iter_first(&mm->interval_tree, + return drm_mm_interval_tree_iter_first((struct rb_root *)&mm->interval_tree, start, last); } EXPORT_SYMBOL(__drm_mm_interval_first); @@ -225,66 +218,151 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, &drm_mm_interval_tree_augment); } -static void drm_mm_insert_helper(struct drm_mm_node *hole_node, - struct drm_mm_node *node, - u64 size, unsigned alignment, - unsigned long color, - enum drm_mm_allocator_flags flags) +#define RB_INSERT(root, member, expr) do { \ + struct rb_node **link = &root.rb_node, *rb = NULL; \ + u64 x = expr(node); \ + while (*link) { \ + rb = *link; \ + if (x < expr(rb_entry(rb, struct drm_mm_node, member))) \ + link = &rb->rb_left; \ + else \ + link = &rb->rb_right; \ + } \ + rb_link_node(&node->member, rb, link); \ + rb_insert_color(&node->member, &root); \ +} while (0) + +#define HOLE_SIZE(NODE) ((NODE)->hole_size) +#define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE)) + +static void add_hole(struct drm_mm_node *node) { - struct drm_mm *mm = hole_node->mm; - u64 hole_start = drm_mm_hole_node_start(hole_node); - u64 hole_end = drm_mm_hole_node_end(hole_node); - u64 adj_start = hole_start; - u64 adj_end = hole_end; + struct drm_mm *mm = node->mm; - BUG_ON(node->allocated); + node->hole_size = + __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node); + DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); - if (mm->color_adjust) - mm->color_adjust(hole_node, color, &adj_start, &adj_end); + RB_INSERT(mm->holes_size, rb_hole_size, HOLE_SIZE); + RB_INSERT(mm->holes_addr, rb_hole_addr, HOLE_ADDR); + + list_add(&node->hole_stack, &mm->hole_stack); +} - if (flags & DRM_MM_CREATE_TOP) - adj_start = adj_end - size; +static void rm_hole(struct drm_mm_node *node) +{ + DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); - if (alignment) { - u64 tmp = adj_start; - unsigned rem; + list_del(&node->hole_stack); + rb_erase(&node->rb_hole_size, &node->mm->holes_size); + rb_erase(&node->rb_hole_addr, &node->mm->holes_addr); + node->hole_size = 0; - rem = do_div(tmp, alignment); - if (rem) { - if (flags & DRM_MM_CREATE_TOP) - adj_start -= rem; - else - adj_start += alignment - rem; + DRM_MM_BUG_ON(drm_mm_hole_follows(node)); +} + +static inline struct drm_mm_node *rb_hole_size_to_node(struct rb_node *rb) +{ + return rb_entry_safe(rb, struct drm_mm_node, rb_hole_size); +} + +static inline struct drm_mm_node *rb_hole_addr_to_node(struct rb_node *rb) +{ + return rb_entry_safe(rb, struct drm_mm_node, rb_hole_addr); +} + +static inline u64 rb_hole_size(struct rb_node *rb) +{ + return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size; +} + +static struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size) +{ + struct rb_node *best = NULL; + struct rb_node **link = &mm->holes_size.rb_node; + + while (*link) { + struct rb_node *rb = *link; + + if (size <= rb_hole_size(rb)) { + link = &rb->rb_left; + best = rb; + } else { + link = &rb->rb_right; } } - BUG_ON(adj_start < hole_start); - BUG_ON(adj_end > hole_end); + return rb_hole_size_to_node(best); +} + +static struct drm_mm_node *find_hole(struct drm_mm *mm, u64 addr) +{ + struct drm_mm_node *node = NULL; + struct rb_node **link = &mm->holes_addr.rb_node; - if (adj_start == hole_start) { - hole_node->hole_follows = 0; - list_del(&hole_node->hole_stack); + while (*link) { + u64 hole_start; + + node = rb_hole_addr_to_node(*link); + hole_start = __drm_mm_hole_node_start(node); + + if (addr < hole_start) + link = &node->rb_hole_addr.rb_left; + else if (addr > hole_start + node->hole_size) + link = &node->rb_hole_addr.rb_right; + else + break; } - node->start = adj_start; - node->size = size; - node->mm = mm; - node->color = color; - node->allocated = 1; + return node; +} + +static struct drm_mm_node * +first_hole(struct drm_mm *mm, + u64 start, u64 end, u64 size, + enum drm_mm_insert_mode mode) +{ + if (RB_EMPTY_ROOT(&mm->holes_size)) + return NULL; - list_add(&node->node_list, &hole_node->node_list); + switch (mode) { + default: + case DRM_MM_INSERT_BEST: + return best_hole(mm, size); - drm_mm_interval_tree_add_node(hole_node, node); + case DRM_MM_INSERT_LOW: + return find_hole(mm, start); - BUG_ON(node->start + node->size > adj_end); + case DRM_MM_INSERT_HIGH: + return find_hole(mm, end); - node->hole_follows = 0; - if (__drm_mm_hole_node_start(node) < hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; + case DRM_MM_INSERT_EVICT: + return list_first_entry_or_null(&mm->hole_stack, + struct drm_mm_node, + hole_stack); } +} - save_stack(node); +static struct drm_mm_node * +next_hole(struct drm_mm *mm, + struct drm_mm_node *node, + enum drm_mm_insert_mode mode) +{ + switch (mode) { + default: + case DRM_MM_INSERT_BEST: + return rb_hole_size_to_node(rb_next(&node->rb_hole_size)); + + case DRM_MM_INSERT_LOW: + return rb_hole_addr_to_node(rb_next(&node->rb_hole_addr)); + + case DRM_MM_INSERT_HIGH: + return rb_hole_addr_to_node(rb_prev(&node->rb_hole_addr)); + + case DRM_MM_INSERT_EVICT: + node = list_next_entry(node, hole_stack); + return &node->hole_stack == &mm->hole_stack ? NULL : node; + } } /** @@ -292,11 +370,11 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, * @mm: drm_mm allocator to insert @node into * @node: drm_mm_node to insert * - * This functions inserts an already set-up drm_mm_node into the allocator, - * meaning that start, size and color must be set by the caller. This is useful - * to initialize the allocator with preallocated objects which must be set-up - * before the range allocator can be set-up, e.g. when taking over a firmware - * framebuffer. + * This functions inserts an already set-up &drm_mm_node into the allocator, + * meaning that start, size and color must be set by the caller. All other + * fields must be cleared to 0. This is useful to initialize the allocator with + * preallocated objects which must be set-up before the range allocator can be + * set-up, e.g. when taking over a firmware framebuffer. * * Returns: * 0 on success, -ENOSPC if there's no hole where @node is. @@ -308,28 +386,17 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) u64 hole_start, hole_end; u64 adj_start, adj_end; - if (WARN_ON(node->size == 0)) - return -EINVAL; - end = node->start + node->size; + if (unlikely(end <= node->start)) + return -ENOSPC; /* Find the relevant hole to add our node to */ - hole = drm_mm_interval_tree_iter_first(&mm->interval_tree, - node->start, ~(u64)0); - if (hole) { - if (hole->start < end) - return -ENOSPC; - } else { - hole = list_entry(&mm->head_node.node_list, - typeof(*hole), node_list); - } - - hole = list_last_entry(&hole->node_list, typeof(*hole), node_list); - if (!hole->hole_follows) + hole = find_hole(mm, node->start); + if (!hole) return -ENOSPC; adj_start = hole_start = __drm_mm_hole_node_start(hole); - adj_end = hole_end = __drm_mm_hole_node_end(hole); + adj_end = hole_end = hole_start + hole->hole_size; if (mm->color_adjust) mm->color_adjust(hole, node->color, &adj_start, &adj_end); @@ -338,174 +405,130 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) return -ENOSPC; node->mm = mm; - node->allocated = 1; list_add(&node->node_list, &hole->node_list); - drm_mm_interval_tree_add_node(hole, node); + node->allocated = true; + node->hole_size = 0; - if (node->start == hole_start) { - hole->hole_follows = 0; - list_del(&hole->hole_stack); - } - - node->hole_follows = 0; - if (end != hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + rm_hole(hole); + if (node->start > hole_start) + add_hole(hole); + if (end < hole_end) + add_hole(node); save_stack(node); - return 0; } EXPORT_SYMBOL(drm_mm_reserve_node); /** - * drm_mm_insert_node_generic - search for space and insert @node + * drm_mm_insert_node_in_range - ranged search for space and insert @node * @mm: drm_mm to allocate from * @node: preallocate node to insert * @size: size of the allocation * @alignment: alignment of the allocation * @color: opaque tag value to use for this node - * @sflags: flags to fine-tune the allocation search - * @aflags: flags to fine-tune the allocation behavior + * @range_start: start of the allowed range for this node + * @range_end: end of the allowed range for this node + * @mode: fine-tune the allocation search and placement * - * The preallocated node must be cleared to 0. + * The preallocated @node must be cleared to 0. * * Returns: * 0 on success, -ENOSPC if there's no suitable hole. */ -int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, - u64 size, unsigned alignment, - unsigned long color, - enum drm_mm_search_flags sflags, - enum drm_mm_allocator_flags aflags) +int drm_mm_insert_node_in_range(struct drm_mm * const mm, + struct drm_mm_node * const node, + u64 size, u64 alignment, + unsigned long color, + u64 range_start, u64 range_end, + enum drm_mm_insert_mode mode) { - struct drm_mm_node *hole_node; + struct drm_mm_node *hole; + u64 remainder_mask; - if (WARN_ON(size == 0)) - return -EINVAL; + DRM_MM_BUG_ON(range_start >= range_end); - hole_node = drm_mm_search_free_generic(mm, size, alignment, - color, sflags); - if (!hole_node) + if (unlikely(size == 0 || range_end - range_start < size)) return -ENOSPC; - drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags); - return 0; -} -EXPORT_SYMBOL(drm_mm_insert_node_generic); - -static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, - struct drm_mm_node *node, - u64 size, unsigned alignment, - unsigned long color, - u64 start, u64 end, - enum drm_mm_allocator_flags flags) -{ - struct drm_mm *mm = hole_node->mm; - u64 hole_start = drm_mm_hole_node_start(hole_node); - u64 hole_end = drm_mm_hole_node_end(hole_node); - u64 adj_start = hole_start; - u64 adj_end = hole_end; + if (alignment <= 1) + alignment = 0; - BUG_ON(!hole_node->hole_follows || node->allocated); + remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; + for (hole = first_hole(mm, range_start, range_end, size, mode); hole; + hole = next_hole(mm, hole, mode)) { + u64 hole_start = __drm_mm_hole_node_start(hole); + u64 hole_end = hole_start + hole->hole_size; + u64 adj_start, adj_end; + u64 col_start, col_end; - if (adj_start < start) - adj_start = start; - if (adj_end > end) - adj_end = end; + if (mode == DRM_MM_INSERT_LOW && hole_start >= range_end) + break; - if (mm->color_adjust) - mm->color_adjust(hole_node, color, &adj_start, &adj_end); + if (mode == DRM_MM_INSERT_HIGH && hole_end <= range_start) + break; - if (flags & DRM_MM_CREATE_TOP) - adj_start = adj_end - size; + col_start = hole_start; + col_end = hole_end; + if (mm->color_adjust) + mm->color_adjust(hole, color, &col_start, &col_end); - if (alignment) { - u64 tmp = adj_start; - unsigned rem; + adj_start = max(col_start, range_start); + adj_end = min(col_end, range_end); - rem = do_div(tmp, alignment); - if (rem) { - if (flags & DRM_MM_CREATE_TOP) - adj_start -= rem; - else - adj_start += alignment - rem; - } - } + if (adj_end <= adj_start || adj_end - adj_start < size) + continue; - if (adj_start == hole_start) { - hole_node->hole_follows = 0; - list_del(&hole_node->hole_stack); - } + if (mode == DRM_MM_INSERT_HIGH) + adj_start = adj_end - size; - node->start = adj_start; - node->size = size; - node->mm = mm; - node->color = color; - node->allocated = 1; + if (alignment) { + u64 rem; - list_add(&node->node_list, &hole_node->node_list); + if (likely(remainder_mask)) + rem = adj_start & remainder_mask; + else + div64_u64_rem(adj_start, alignment, &rem); + if (rem) { + adj_start -= rem; + if (mode != DRM_MM_INSERT_HIGH) + adj_start += alignment; - drm_mm_interval_tree_add_node(hole_node, node); + if (adj_start < max(col_start, range_start) || + min(col_end, range_end) - adj_start < size) + continue; - BUG_ON(node->start < start); - BUG_ON(node->start < adj_start); - BUG_ON(node->start + node->size > adj_end); - BUG_ON(node->start + node->size > end); + if (adj_end <= adj_start || + adj_end - adj_start < size) + continue; + } + } - node->hole_follows = 0; - if (__drm_mm_hole_node_start(node) < hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + node->mm = mm; + node->size = size; + node->start = adj_start; + node->color = color; + node->hole_size = 0; - save_stack(node); -} + list_add(&node->node_list, &hole->node_list); + drm_mm_interval_tree_add_node(hole, node); + node->allocated = true; -/** - * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node - * @mm: drm_mm to allocate from - * @node: preallocate node to insert - * @size: size of the allocation - * @alignment: alignment of the allocation - * @color: opaque tag value to use for this node - * @start: start of the allowed range for this node - * @end: end of the allowed range for this node - * @sflags: flags to fine-tune the allocation search - * @aflags: flags to fine-tune the allocation behavior - * - * The preallocated node must be cleared to 0. - * - * Returns: - * 0 on success, -ENOSPC if there's no suitable hole. - */ -int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, - u64 size, unsigned alignment, - unsigned long color, - u64 start, u64 end, - enum drm_mm_search_flags sflags, - enum drm_mm_allocator_flags aflags) -{ - struct drm_mm_node *hole_node; + rm_hole(hole); + if (adj_start > hole_start) + add_hole(hole); + if (adj_start + size < hole_end) + add_hole(node); - if (WARN_ON(size == 0)) - return -EINVAL; - - hole_node = drm_mm_search_free_in_range_generic(mm, - size, alignment, color, - start, end, sflags); - if (!hole_node) - return -ENOSPC; + save_stack(node); + return 0; + } - drm_mm_insert_helper_range(hole_node, node, - size, alignment, color, - start, end, aflags); - return 0; + return -ENOSPC; } -EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); +EXPORT_SYMBOL(drm_mm_insert_node_in_range); /** * drm_mm_remove_node - Remove a memory node from the allocator. @@ -513,150 +536,30 @@ EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); * * This just removes a node from its drm_mm allocator. The node does not need to * be cleared again before it can be re-inserted into this or any other drm_mm - * allocator. It is a bug to call this function on a un-allocated node. + * allocator. It is a bug to call this function on a unallocated node. */ void drm_mm_remove_node(struct drm_mm_node *node) { struct drm_mm *mm = node->mm; struct drm_mm_node *prev_node; - if (WARN_ON(!node->allocated)) - return; - - BUG_ON(node->scanned_block || node->scanned_prev_free - || node->scanned_next_free); - - prev_node = - list_entry(node->node_list.prev, struct drm_mm_node, node_list); + DRM_MM_BUG_ON(!node->allocated); + DRM_MM_BUG_ON(node->scanned_block); - if (node->hole_follows) { - BUG_ON(__drm_mm_hole_node_start(node) == - __drm_mm_hole_node_end(node)); - list_del(&node->hole_stack); - } else - BUG_ON(__drm_mm_hole_node_start(node) != - __drm_mm_hole_node_end(node)); + prev_node = list_prev_entry(node, node_list); - - if (!prev_node->hole_follows) { - prev_node->hole_follows = 1; - list_add(&prev_node->hole_stack, &mm->hole_stack); - } else - list_move(&prev_node->hole_stack, &mm->hole_stack); + if (drm_mm_hole_follows(node)) + rm_hole(node); drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); - node->allocated = 0; -} -EXPORT_SYMBOL(drm_mm_remove_node); - -static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment) -{ - if (end - start < size) - return 0; - - if (alignment) { - u64 tmp = start; - unsigned rem; - - rem = do_div(tmp, alignment); - if (rem) - start += alignment - rem; - } - - return end >= start + size; -} + node->allocated = false; -static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, - u64 size, - unsigned alignment, - unsigned long color, - enum drm_mm_search_flags flags) -{ - struct drm_mm_node *entry; - struct drm_mm_node *best; - u64 adj_start; - u64 adj_end; - u64 best_size; - - BUG_ON(mm->scanned_blocks); - - best = NULL; - best_size = ~0UL; - - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; - - if (mm->color_adjust) { - mm->color_adjust(entry, color, &adj_start, &adj_end); - if (adj_end <= adj_start) - continue; - } - - if (!check_free_hole(adj_start, adj_end, size, alignment)) - continue; - - if (!(flags & DRM_MM_SEARCH_BEST)) - return entry; - - if (hole_size < best_size) { - best = entry; - best_size = hole_size; - } - } - - return best; -} - -static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, - u64 size, - unsigned alignment, - unsigned long color, - u64 start, - u64 end, - enum drm_mm_search_flags flags) -{ - struct drm_mm_node *entry; - struct drm_mm_node *best; - u64 adj_start; - u64 adj_end; - u64 best_size; - - BUG_ON(mm->scanned_blocks); - - best = NULL; - best_size = ~0UL; - - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; - - if (adj_start < start) - adj_start = start; - if (adj_end > end) - adj_end = end; - - if (mm->color_adjust) { - mm->color_adjust(entry, color, &adj_start, &adj_end); - if (adj_end <= adj_start) - continue; - } - - if (!check_free_hole(adj_start, adj_end, size, alignment)) - continue; - - if (!(flags & DRM_MM_SEARCH_BEST)) - return entry; - - if (hole_size < best_size) { - best = entry; - best_size = hole_size; - } - } - - return best; + if (drm_mm_hole_follows(prev_node)) + rm_hole(prev_node); + add_hole(prev_node); } +EXPORT_SYMBOL(drm_mm_remove_node); /** * drm_mm_replace_node - move an allocation from @old to @new @@ -669,119 +572,114 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ */ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { + DRM_MM_BUG_ON(!old->allocated); + + *new = *old; + list_replace(&old->node_list, &new->node_list); - list_replace(&old->hole_stack, &new->hole_stack); rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); - new->hole_follows = old->hole_follows; - new->mm = old->mm; - new->start = old->start; - new->size = old->size; - new->color = old->color; - new->__subtree_last = old->__subtree_last; - - old->allocated = 0; - new->allocated = 1; + + if (drm_mm_hole_follows(old)) { + list_replace(&old->hole_stack, &new->hole_stack); + rb_replace_node(&old->rb_hole_size, + &new->rb_hole_size, + &old->mm->holes_size); + rb_replace_node(&old->rb_hole_addr, + &new->rb_hole_addr, + &old->mm->holes_addr); + } + + old->allocated = false; + new->allocated = true; } EXPORT_SYMBOL(drm_mm_replace_node); /** - * DOC: lru scan roaster + * DOC: lru scan roster * * Very often GPUs need to have continuous allocations for a given object. When * evicting objects to make space for a new one it is therefore not most * efficient when we simply start to select all objects from the tail of an LRU * until there's a suitable hole: Especially for big objects or nodes that * otherwise have special allocation constraints there's a good chance we evict - * lots of (smaller) objects unecessarily. + * lots of (smaller) objects unnecessarily. * * The DRM range allocator supports this use-case through the scanning * interfaces. First a scan operation needs to be initialized with - * drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds - * objects to the roaster (probably by walking an LRU list, but this can be - * freely implemented) until a suitable hole is found or there's no further - * evitable object. - * - * The the driver must walk through all objects again in exactly the reverse + * drm_mm_scan_init() or drm_mm_scan_init_with_range(). The driver adds + * objects to the roster, probably by walking an LRU list, but this can be + * freely implemented. Eviction candiates are added using + * drm_mm_scan_add_block() until a suitable hole is found or there are no + * further evictable objects. Eviction roster metadata is tracked in &struct + * drm_mm_scan. + * + * The driver must walk through all objects again in exactly the reverse * order to restore the allocator state. Note that while the allocator is used * in the scan mode no other operation is allowed. * - * Finally the driver evicts all objects selected in the scan. Adding and - * removing an object is O(1), and since freeing a node is also O(1) the overall - * complexity is O(scanned_objects). So like the free stack which needs to be - * walked before a scan operation even begins this is linear in the number of - * objects. It doesn't seem to hurt badly. - */ - -/** - * drm_mm_init_scan - initialize lru scanning - * @mm: drm_mm to scan - * @size: size of the allocation - * @alignment: alignment of the allocation - * @color: opaque tag value to use for the allocation - * - * This simply sets up the scanning routines with the parameters for the desired - * hole. Note that there's no need to specify allocation flags, since they only - * change the place a node is allocated from within a suitable hole. - * - * Warning: - * As long as the scan list is non-empty, no other operations than - * adding/removing nodes to/from the scan list are allowed. + * Finally the driver evicts all objects selected (drm_mm_scan_remove_block() + * reported true) in the scan, and any overlapping nodes after color adjustment + * (drm_mm_scan_color_evict()). Adding and removing an object is O(1), and + * since freeing a node is also O(1) the overall complexity is + * O(scanned_objects). So like the free stack which needs to be walked before a + * scan operation even begins this is linear in the number of objects. It + * doesn't seem to hurt too badly. */ -void drm_mm_init_scan(struct drm_mm *mm, - u64 size, - unsigned alignment, - unsigned long color) -{ - mm->scan_color = color; - mm->scan_alignment = alignment; - mm->scan_size = size; - mm->scanned_blocks = 0; - mm->scan_hit_start = 0; - mm->scan_hit_end = 0; - mm->scan_check_range = 0; - mm->prev_scanned_node = NULL; -} -EXPORT_SYMBOL(drm_mm_init_scan); /** - * drm_mm_init_scan - initialize range-restricted lru scanning + * drm_mm_scan_init_with_range - initialize range-restricted lru scanning + * @scan: scan state * @mm: drm_mm to scan * @size: size of the allocation * @alignment: alignment of the allocation * @color: opaque tag value to use for the allocation * @start: start of the allowed range for the allocation * @end: end of the allowed range for the allocation + * @mode: fine-tune the allocation search and placement * * This simply sets up the scanning routines with the parameters for the desired - * hole. Note that there's no need to specify allocation flags, since they only - * change the place a node is allocated from within a suitable hole. + * hole. * * Warning: * As long as the scan list is non-empty, no other operations than * adding/removing nodes to/from the scan list are allowed. */ -void drm_mm_init_scan_with_range(struct drm_mm *mm, +void drm_mm_scan_init_with_range(struct drm_mm_scan *scan, + struct drm_mm *mm, u64 size, - unsigned alignment, + u64 alignment, unsigned long color, u64 start, - u64 end) + u64 end, + enum drm_mm_insert_mode mode) { - mm->scan_color = color; - mm->scan_alignment = alignment; - mm->scan_size = size; - mm->scanned_blocks = 0; - mm->scan_hit_start = 0; - mm->scan_hit_end = 0; - mm->scan_start = start; - mm->scan_end = end; - mm->scan_check_range = 1; - mm->prev_scanned_node = NULL; + DRM_MM_BUG_ON(start >= end); + DRM_MM_BUG_ON(!size || size > end - start); + DRM_MM_BUG_ON(mm->scan_active); + + scan->mm = mm; + + if (alignment <= 1) + alignment = 0; + + scan->color = color; + scan->alignment = alignment; + scan->remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; + scan->size = size; + scan->mode = mode; + + DRM_MM_BUG_ON(end <= start); + scan->range_start = start; + scan->range_end = end; + + scan->hit_start = U64_MAX; + scan->hit_end = 0; } -EXPORT_SYMBOL(drm_mm_init_scan_with_range); +EXPORT_SYMBOL(drm_mm_scan_init_with_range); /** * drm_mm_scan_add_block - add a node to the scan list + * @scan: the active drm_mm scanner * @node: drm_mm_node to add * * Add a node to the scan list that might be freed to make space for the desired @@ -790,105 +688,165 @@ EXPORT_SYMBOL(drm_mm_init_scan_with_range); * Returns: * True if a hole has been found, false otherwise. */ -bool drm_mm_scan_add_block(struct drm_mm_node *node) +bool drm_mm_scan_add_block(struct drm_mm_scan *scan, + struct drm_mm_node *node) { - struct drm_mm *mm = node->mm; - struct drm_mm_node *prev_node; + struct drm_mm *mm = scan->mm; + struct drm_mm_node *hole; u64 hole_start, hole_end; + u64 col_start, col_end; u64 adj_start, adj_end; - mm->scanned_blocks++; + DRM_MM_BUG_ON(node->mm != mm); + DRM_MM_BUG_ON(!node->allocated); + DRM_MM_BUG_ON(node->scanned_block); + node->scanned_block = true; + mm->scan_active++; + + /* Remove this block from the node_list so that we enlarge the hole + * (distance between the end of our previous node and the start of + * or next), without poisoning the link so that we can restore it + * later in drm_mm_scan_remove_block(). + */ + hole = list_prev_entry(node, node_list); + DRM_MM_BUG_ON(list_next_entry(hole, node_list) != node); + __list_del_entry(&node->node_list); + + hole_start = __drm_mm_hole_node_start(hole); + hole_end = __drm_mm_hole_node_end(hole); + + col_start = hole_start; + col_end = hole_end; + if (mm->color_adjust) + mm->color_adjust(hole, scan->color, &col_start, &col_end); - BUG_ON(node->scanned_block); - node->scanned_block = 1; + adj_start = max(col_start, scan->range_start); + adj_end = min(col_end, scan->range_end); + if (adj_end <= adj_start || adj_end - adj_start < scan->size) + return false; - prev_node = list_entry(node->node_list.prev, struct drm_mm_node, - node_list); + if (scan->mode == DRM_MM_INSERT_HIGH) + adj_start = adj_end - scan->size; - node->scanned_preceeds_hole = prev_node->hole_follows; - prev_node->hole_follows = 1; - list_del(&node->node_list); - node->node_list.prev = &prev_node->node_list; - node->node_list.next = &mm->prev_scanned_node->node_list; - mm->prev_scanned_node = node; - - adj_start = hole_start = drm_mm_hole_node_start(prev_node); - adj_end = hole_end = drm_mm_hole_node_end(prev_node); - - if (mm->scan_check_range) { - if (adj_start < mm->scan_start) - adj_start = mm->scan_start; - if (adj_end > mm->scan_end) - adj_end = mm->scan_end; - } + if (scan->alignment) { + u64 rem; - if (mm->color_adjust) - mm->color_adjust(prev_node, mm->scan_color, - &adj_start, &adj_end); - - if (check_free_hole(adj_start, adj_end, - mm->scan_size, mm->scan_alignment)) { - mm->scan_hit_start = hole_start; - mm->scan_hit_end = hole_end; - return true; + if (likely(scan->remainder_mask)) + rem = adj_start & scan->remainder_mask; + else + div64_u64_rem(adj_start, scan->alignment, &rem); + if (rem) { + adj_start -= rem; + if (scan->mode != DRM_MM_INSERT_HIGH) + adj_start += scan->alignment; + if (adj_start < max(col_start, scan->range_start) || + min(col_end, scan->range_end) - adj_start < scan->size) + return false; + + if (adj_end <= adj_start || + adj_end - adj_start < scan->size) + return false; + } } - return false; + scan->hit_start = adj_start; + scan->hit_end = adj_start + scan->size; + + DRM_MM_BUG_ON(scan->hit_start >= scan->hit_end); + DRM_MM_BUG_ON(scan->hit_start < hole_start); + DRM_MM_BUG_ON(scan->hit_end > hole_end); + + return true; } EXPORT_SYMBOL(drm_mm_scan_add_block); /** * drm_mm_scan_remove_block - remove a node from the scan list + * @scan: the active drm_mm scanner * @node: drm_mm_node to remove * - * Nodes _must_ be removed in the exact same order from the scan list as they - * have been added, otherwise the internal state of the memory manager will be - * corrupted. + * Nodes **must** be removed in exactly the reverse order from the scan list as + * they have been added (e.g. using list_add() as they are added and then + * list_for_each() over that eviction list to remove), otherwise the internal + * state of the memory manager will be corrupted. * * When the scan list is empty, the selected memory nodes can be freed. An - * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then - * return the just freed block (because its at the top of the free_stack list). + * immediately following drm_mm_insert_node_in_range_generic() or one of the + * simpler versions of that function with !DRM_MM_SEARCH_BEST will then return + * the just freed block (because its at the top of the free_stack list). * * Returns: * True if this block should be evicted, false otherwise. Will always * return false when no hole has been found. */ -bool drm_mm_scan_remove_block(struct drm_mm_node *node) +bool drm_mm_scan_remove_block(struct drm_mm_scan *scan, + struct drm_mm_node *node) { - struct drm_mm *mm = node->mm; struct drm_mm_node *prev_node; - mm->scanned_blocks--; - - BUG_ON(!node->scanned_block); - node->scanned_block = 0; - - prev_node = list_entry(node->node_list.prev, struct drm_mm_node, - node_list); - - prev_node->hole_follows = node->scanned_preceeds_hole; + DRM_MM_BUG_ON(node->mm != scan->mm); + DRM_MM_BUG_ON(!node->scanned_block); + node->scanned_block = false; + + DRM_MM_BUG_ON(!node->mm->scan_active); + node->mm->scan_active--; + + /* During drm_mm_scan_add_block() we decoupled this node leaving + * its pointers intact. Now that the caller is walking back along + * the eviction list we can restore this block into its rightful + * place on the full node_list. To confirm that the caller is walking + * backwards correctly we check that prev_node->next == node->next, + * i.e. both believe the same node should be on the other side of the + * hole. + */ + prev_node = list_prev_entry(node, node_list); + DRM_MM_BUG_ON(list_next_entry(prev_node, node_list) != + list_next_entry(node, node_list)); list_add(&node->node_list, &prev_node->node_list); - return (drm_mm_hole_node_end(node) > mm->scan_hit_start && - node->start < mm->scan_hit_end); + return (node->start + node->size > scan->hit_start && + node->start < scan->hit_end); } EXPORT_SYMBOL(drm_mm_scan_remove_block); /** - * drm_mm_clean - checks whether an allocator is clean - * @mm: drm_mm allocator to check + * drm_mm_scan_color_evict - evict overlapping nodes on either side of hole + * @scan: drm_mm scan with target hole + * + * After completing an eviction scan and removing the selected nodes, we may + * need to remove a few more nodes from either side of the target hole if + * mm.color_adjust is being used. * * Returns: - * True if the allocator is completely free, false if there's still a node - * allocated in it. + * A node to evict, or NULL if there are no overlapping nodes. */ -bool drm_mm_clean(struct drm_mm * mm) +struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan) { - struct list_head *head = &mm->head_node.node_list; + struct drm_mm *mm = scan->mm; + struct drm_mm_node *hole; + u64 hole_start, hole_end; + + DRM_MM_BUG_ON(list_empty(&mm->hole_stack)); + + if (!mm->color_adjust) + return NULL; + + hole = list_first_entry(&mm->hole_stack, typeof(*hole), hole_stack); + hole_start = __drm_mm_hole_node_start(hole); + hole_end = hole_start + hole->hole_size; - return (head->next->next == head); + DRM_MM_BUG_ON(hole_start > scan->hit_start); + DRM_MM_BUG_ON(hole_end < scan->hit_end); + + mm->color_adjust(hole, scan->color, &hole_start, &hole_end); + if (hole_start > scan->hit_start) + return hole; + if (hole_end < scan->hit_end) + return list_next_entry(hole, node_list); + + return NULL; } -EXPORT_SYMBOL(drm_mm_clean); +EXPORT_SYMBOL(drm_mm_scan_color_evict); /** * drm_mm_init - initialize a drm-mm allocator @@ -898,26 +856,26 @@ EXPORT_SYMBOL(drm_mm_clean); * * Note that @mm must be cleared to 0 before calling this function. */ -void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) +void drm_mm_init(struct drm_mm *mm, u64 start, u64 size) { + DRM_MM_BUG_ON(start + size <= start); + + mm->color_adjust = NULL; + INIT_LIST_HEAD(&mm->hole_stack); - mm->scanned_blocks = 0; + mm->interval_tree = RB_ROOT; + mm->holes_size = RB_ROOT; + mm->holes_addr = RB_ROOT; /* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); - mm->head_node.allocated = 0; - mm->head_node.hole_follows = 1; - mm->head_node.scanned_block = 0; - mm->head_node.scanned_prev_free = 0; - mm->head_node.scanned_next_free = 0; + mm->head_node.allocated = false; mm->head_node.mm = mm; mm->head_node.start = start + size; - mm->head_node.size = start - mm->head_node.start; - list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); + mm->head_node.size = -size; + add_hole(&mm->head_node); - mm->interval_tree = RB_ROOT; - - mm->color_adjust = NULL; + mm->scan_active = 0; } EXPORT_SYMBOL(drm_mm_init); @@ -930,95 +888,46 @@ EXPORT_SYMBOL(drm_mm_init); */ void drm_mm_takedown(struct drm_mm *mm) { - if (WARN(!list_empty(&mm->head_node.node_list), + if (WARN(!drm_mm_clean(mm), "Memory manager not clean during takedown.\n")) show_leaks(mm); - } EXPORT_SYMBOL(drm_mm_takedown); -static u64 drm_mm_debug_hole(struct drm_mm_node *entry, - const char *prefix) +static u64 drm_mm_dump_hole(struct drm_printer *p, const struct drm_mm_node *entry) { - u64 hole_start, hole_end, hole_size; - - if (entry->hole_follows) { - hole_start = drm_mm_hole_node_start(entry); - hole_end = drm_mm_hole_node_end(entry); - hole_size = hole_end - hole_start; - pr_debug("%s %#llx-%#llx: %llu: free\n", prefix, hole_start, - hole_end, hole_size); - return hole_size; - } - - return 0; -} - -/** - * drm_mm_debug_table - dump allocator state to dmesg - * @mm: drm_mm allocator to dump - * @prefix: prefix to use for dumping to dmesg - */ -void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) -{ - struct drm_mm_node *entry; - u64 total_used = 0, total_free = 0, total = 0; + u64 start, size; - total_free += drm_mm_debug_hole(&mm->head_node, prefix); - - drm_mm_for_each_node(entry, mm) { - pr_debug("%s %#llx-%#llx: %llu: used\n", prefix, entry->start, - entry->start + entry->size, entry->size); - total_used += entry->size; - total_free += drm_mm_debug_hole(entry, prefix); + size = entry->hole_size; + if (size) { + start = drm_mm_hole_node_start(entry); + drm_printf(p, "%#018llx-%#018llx: %llu: free\n", + start, start + size, size); } - total = total_free + total_used; - pr_debug("%s total: %llu, used %llu free %llu\n", prefix, total, - total_used, total_free); + return size; } -EXPORT_SYMBOL(drm_mm_debug_table); - -#if defined(CONFIG_DEBUG_FS) -static u64 drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry) -{ - u64 hole_start, hole_end, hole_size; - - if (entry->hole_follows) { - hole_start = drm_mm_hole_node_start(entry); - hole_end = drm_mm_hole_node_end(entry); - hole_size = hole_end - hole_start; - seq_printf(m, "%#018llx-%#018llx: %llu: free\n", hole_start, - hole_end, hole_size); - return hole_size; - } - - return 0; -} - /** - * drm_mm_dump_table - dump allocator state to a seq_file - * @m: seq_file to dump to - * @mm: drm_mm allocator to dump + * drm_mm_print - print allocator state + * @mm: drm_mm allocator to print + * @p: DRM printer to use */ -int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) +void drm_mm_print(const struct drm_mm *mm, struct drm_printer *p) { - struct drm_mm_node *entry; + const struct drm_mm_node *entry; u64 total_used = 0, total_free = 0, total = 0; - total_free += drm_mm_dump_hole(m, &mm->head_node); + total_free += drm_mm_dump_hole(p, &mm->head_node); drm_mm_for_each_node(entry, mm) { - seq_printf(m, "%#018llx-%#018llx: %llu: used\n", entry->start, + drm_printf(p, "%#018llx-%#018llx: %llu: used\n", entry->start, entry->start + entry->size, entry->size); total_used += entry->size; - total_free += drm_mm_dump_hole(m, entry); + total_free += drm_mm_dump_hole(p, entry); } total = total_free + total_used; - seq_printf(m, "total: %llu, used %llu free %llu\n", total, + drm_printf(p, "total: %llu, used %llu free %llu\n", total, total_used, total_free); - return 0; } -EXPORT_SYMBOL(drm_mm_dump_table); -#endif +EXPORT_SYMBOL(drm_mm_print); diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 2735a5847ffa..884cc4d26fb5 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -20,6 +20,7 @@ * OF THIS SOFTWARE. */ +#include <drm/drm_encoder.h> #include <drm/drm_mode_config.h> #include <drm/drmP.h> @@ -84,113 +85,74 @@ int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_card_res *card_res = data; - struct list_head *lh; struct drm_framebuffer *fb; struct drm_connector *connector; struct drm_crtc *crtc; struct drm_encoder *encoder; - int ret = 0; - int connector_count = 0; - int crtc_count = 0; - int fb_count = 0; - int encoder_count = 0; - int copied = 0; + int count, ret = 0; uint32_t __user *fb_id; uint32_t __user *crtc_id; uint32_t __user *connector_id; uint32_t __user *encoder_id; + struct drm_connector_list_iter conn_iter; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; mutex_lock(&file_priv->fbs_lock); - /* - * For the non-control nodes we need to limit the list of resources - * by IDs in the group list for this node - */ - list_for_each(lh, &file_priv->fbs) - fb_count++; - - /* handle this in 4 parts */ - /* FBs */ - if (card_res->count_fbs >= fb_count) { - copied = 0; - fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; - list_for_each_entry(fb, &file_priv->fbs, filp_head) { - if (put_user(fb->base.id, fb_id + copied)) { - mutex_unlock(&file_priv->fbs_lock); - return -EFAULT; - } - copied++; + count = 0; + fb_id = u64_to_user_ptr(card_res->fb_id_ptr); + list_for_each_entry(fb, &file_priv->fbs, filp_head) { + if (count < card_res->count_fbs && + put_user(fb->base.id, fb_id + count)) { + mutex_unlock(&file_priv->fbs_lock); + return -EFAULT; } + count++; } - card_res->count_fbs = fb_count; + card_res->count_fbs = count; mutex_unlock(&file_priv->fbs_lock); - /* mode_config.mutex protects the connector list against e.g. DP MST - * connector hot-adding. CRTC/Plane lists are invariant. */ - mutex_lock(&dev->mode_config.mutex); - drm_for_each_crtc(crtc, dev) - crtc_count++; - - drm_for_each_connector(connector, dev) - connector_count++; - - drm_for_each_encoder(encoder, dev) - encoder_count++; - card_res->max_height = dev->mode_config.max_height; card_res->min_height = dev->mode_config.min_height; card_res->max_width = dev->mode_config.max_width; card_res->min_width = dev->mode_config.min_width; - /* CRTCs */ - if (card_res->count_crtcs >= crtc_count) { - copied = 0; - crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; - drm_for_each_crtc(crtc, dev) { - if (put_user(crtc->base.id, crtc_id + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } + count = 0; + crtc_id = u64_to_user_ptr(card_res->crtc_id_ptr); + drm_for_each_crtc(crtc, dev) { + if (count < card_res->count_crtcs && + put_user(crtc->base.id, crtc_id + count)) + return -EFAULT; + count++; } - card_res->count_crtcs = crtc_count; - - /* Encoders */ - if (card_res->count_encoders >= encoder_count) { - copied = 0; - encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; - drm_for_each_encoder(encoder, dev) { - if (put_user(encoder->base.id, encoder_id + - copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } + card_res->count_crtcs = count; + + count = 0; + encoder_id = u64_to_user_ptr(card_res->encoder_id_ptr); + drm_for_each_encoder(encoder, dev) { + if (count < card_res->count_encoders && + put_user(encoder->base.id, encoder_id + count)) + return -EFAULT; + count++; } - card_res->count_encoders = encoder_count; - - /* Connectors */ - if (card_res->count_connectors >= connector_count) { - copied = 0; - connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; - drm_for_each_connector(connector, dev) { - if (put_user(connector->base.id, - connector_id + copied)) { - ret = -EFAULT; - goto out; - } - copied++; + card_res->count_encoders = count; + + drm_connector_list_iter_get(dev, &conn_iter); + count = 0; + connector_id = u64_to_user_ptr(card_res->connector_id_ptr); + drm_for_each_connector_iter(connector, &conn_iter) { + if (count < card_res->count_connectors && + put_user(connector->base.id, connector_id + count)) { + drm_connector_list_iter_put(&conn_iter); + return -EFAULT; } + count++; } - card_res->count_connectors = connector_count; + card_res->count_connectors = count; + drm_connector_list_iter_put(&conn_iter); -out: - mutex_unlock(&dev->mode_config.mutex); return ret; } @@ -208,6 +170,7 @@ void drm_mode_config_reset(struct drm_device *dev) struct drm_plane *plane; struct drm_encoder *encoder; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; drm_for_each_plane(plane, dev) if (plane->funcs->reset) @@ -221,11 +184,11 @@ void drm_mode_config_reset(struct drm_device *dev) if (encoder->funcs->reset) encoder->funcs->reset(encoder); - mutex_lock(&dev->mode_config.mutex); - drm_for_each_connector(connector, dev) + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) if (connector->funcs->reset) connector->funcs->reset(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_connector_list_iter_put(&conn_iter); } EXPORT_SYMBOL(drm_mode_config_reset); @@ -406,10 +369,9 @@ void drm_mode_config_init(struct drm_device *dev) idr_init(&dev->mode_config.crtc_idr); idr_init(&dev->mode_config.tile_idr); ida_init(&dev->mode_config.connector_ida); + spin_lock_init(&dev->mode_config.connector_list_lock); - drm_modeset_lock_all(dev); drm_mode_create_standard_properties(dev); - drm_modeset_unlock_all(dev); /* Just to be sure */ dev->mode_config.num_fb = 0; @@ -436,7 +398,8 @@ EXPORT_SYMBOL(drm_mode_config_init); */ void drm_mode_config_cleanup(struct drm_device *dev) { - struct drm_connector *connector, *ot; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; struct drm_crtc *crtc, *ct; struct drm_encoder *encoder, *enct; struct drm_framebuffer *fb, *fbt; @@ -449,9 +412,20 @@ void drm_mode_config_cleanup(struct drm_device *dev) encoder->funcs->destroy(encoder); } - list_for_each_entry_safe(connector, ot, - &dev->mode_config.connector_list, head) { - connector->funcs->destroy(connector); + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + /* drm_connector_list_iter holds an full reference to the + * current connector itself, which means it is inherently safe + * against unreferencing the current connector - but not against + * deleting it right away. */ + drm_connector_unreference(connector); + } + drm_connector_list_iter_put(&conn_iter); + if (WARN_ON(!list_empty(&dev->mode_config.connector_list))) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) + DRM_ERROR("connector %s leaked!\n", connector->name); + drm_connector_list_iter_put(&conn_iter); } list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index c6885a4911c0..220a6c1f4ab9 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -23,6 +23,7 @@ #include <linux/export.h> #include <drm/drmP.h> #include <drm/drm_mode_object.h> +#include <drm/drm_atomic.h> #include "drm_crtc_internal.h" @@ -273,7 +274,7 @@ int drm_object_property_get_value(struct drm_mode_object *obj, * their value in obj->properties->values[].. mostly to avoid * having to deal w/ EDID and similar props in atomic paths: */ - if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && + if (drm_drv_uses_atomic_modeset(property->dev) && !(property->flags & DRM_MODE_PROP_IMMUTABLE)) return drm_atomic_get_property(obj, property, val); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index e6b19bc9021a..fd22c1c891bf 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -797,6 +797,26 @@ int drm_mode_vrefresh(const struct drm_display_mode *mode) EXPORT_SYMBOL(drm_mode_vrefresh); /** + * drm_mode_get_hv_timing - Fetches hdisplay/vdisplay for given mode + * @mode: mode to query + * @hdisplay: hdisplay value to fill in + * @vdisplay: vdisplay value to fill in + * + * The vdisplay value will be doubled if the specified mode is a stereo mode of + * the appropriate layout. + */ +void drm_mode_get_hv_timing(const struct drm_display_mode *mode, + int *hdisplay, int *vdisplay) +{ + struct drm_display_mode adjusted = *mode; + + drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); + *hdisplay = adjusted.crtc_hdisplay; + *vdisplay = adjusted.crtc_vdisplay; +} +EXPORT_SYMBOL(drm_mode_get_hv_timing); + +/** * drm_mode_set_crtcinfo - set CRTC modesetting timing parameters * @p: mode * @adjust_flags: a combination of adjustment flags @@ -1461,12 +1481,8 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev, mode->type |= DRM_MODE_TYPE_USERDEF; /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */ - if (cmd->xres == 1366 && mode->hdisplay == 1368) { - mode->hdisplay = 1366; - mode->hsync_start--; - mode->hsync_end--; - drm_mode_set_name(mode); - } + if (cmd->xres == 1366) + drm_mode_fixup_1366x768(mode); drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); return mode; } diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c index cc232ac6c950..cc44a9a4b004 100644 --- a/drivers/gpu/drm/drm_modeset_helper.c +++ b/drivers/gpu/drm/drm_modeset_helper.c @@ -48,6 +48,7 @@ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) INIT_LIST_HEAD(&panel_list); + spin_lock_irq(&dev->mode_config.connector_list_lock); list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || @@ -57,38 +58,27 @@ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) } list_splice(&panel_list, &dev->mode_config.connector_list); + spin_unlock_irq(&dev->mode_config.connector_list_lock); } EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); /** * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata + * @dev: DRM device * @fb: drm_framebuffer object to fill out * @mode_cmd: metadata from the userspace fb creation request * * This helper can be used in a drivers fb_create callback to pre-fill the fb's * metadata fields. */ -void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, +void drm_helper_mode_fill_fb_struct(struct drm_device *dev, + struct drm_framebuffer *fb, const struct drm_mode_fb_cmd2 *mode_cmd) { - const struct drm_format_info *info; int i; - info = drm_format_info(mode_cmd->pixel_format); - if (!info || !info->depth) { - struct drm_format_name_buf format_name; - - DRM_DEBUG_KMS("non-RGB pixel format %s\n", - drm_get_format_name(mode_cmd->pixel_format, - &format_name)); - - fb->depth = 0; - fb->bits_per_pixel = 0; - } else { - fb->depth = info->depth; - fb->bits_per_pixel = info->cpp[0] * 8; - } - + fb->dev = dev; + fb->format = drm_format_info(mode_cmd->pixel_format); fb->width = mode_cmd->width; fb->height = mode_cmd->height; for (i = 0; i < 4; i++) { @@ -96,7 +86,6 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, fb->offsets[i] = mode_cmd->offsets[i]; } fb->modifier = mode_cmd->modifier[0]; - fb->pixel_format = mode_cmd->pixel_format; fb->flags = mode_cmd->flags; } EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 3551ae31f143..bf60f2645e55 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -33,7 +33,7 @@ * to use &ww_mutex and acquire-contexts to avoid deadlocks. But because * the locking is more distributed around the driver code, we want a bit * of extra utility/tracking out of our acquire-ctx. This is provided - * by drm_modeset_lock / drm_modeset_acquire_ctx. + * by &struct drm_modeset_lock and &struct drm_modeset_acquire_ctx. * * For basic principles of &ww_mutex, see: Documentation/locking/ww-mutex-design.txt * @@ -53,7 +53,7 @@ * drm_modeset_acquire_fini(&ctx); * * On top of of these per-object locks using &ww_mutex there's also an overall - * dev->mode_config.lock, for protecting everything else. Mostly this means + * &drm_mode_config.mutex, for protecting everything else. Mostly this means * probe state of connectors, and preventing hotplug add/removal of connectors. * * Finally there's a bunch of dedicated locks to protect drm core internal @@ -71,7 +71,7 @@ static DEFINE_WW_CLASS(crtc_ww_class); * drm_modeset_unlock_all() function. * * This function is deprecated. It allocates a lock acquisition context and - * stores it in the DRM device's ->mode_config. This facilitate conversion of + * stores it in &drm_device.mode_config. This facilitate conversion of * existing code because it removes the need to manually deal with the * acquisition context, but it is also brittle because the context is global * and care must be taken not to nest calls. New code should use the @@ -124,7 +124,7 @@ EXPORT_SYMBOL(drm_modeset_lock_all); * drm_modeset_lock_all() function. * * This function is deprecated. It uses the lock acquisition context stored - * in the DRM device's ->mode_config. This facilitates conversion of existing + * in &drm_device.mode_config. This facilitates conversion of existing * code because it removes the need to manually deal with the acquisition * context, but it is also brittle because the context is global and care must * be taken not to nest calls. New code should pass the acquisition context @@ -468,7 +468,7 @@ EXPORT_SYMBOL(drm_modeset_unlock); * This function takes all modeset locks, suitable where a more fine-grained * scheme isn't (yet) implemented. * - * Unlike drm_modeset_lock_all(), it doesn't take the dev->mode_config.mutex + * Unlike drm_modeset_lock_all(), it doesn't take the &drm_mode_config.mutex * since that lock isn't required for modeset state changes. Callers which * need to grab that lock too need to do so outside of the acquire context * @ctx. diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 47848ed8ca48..b5f2f0fece99 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -4,6 +4,7 @@ #include <linux/of_graph.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> #include <drm/drm_of.h> static void drm_release_of(struct device *dev, void *data) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 3dfe3c886502..308d442a531b 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -137,7 +137,7 @@ EXPORT_SYMBOL(drm_panel_detach); * Return: A pointer to the panel registered for the specified device tree * node or NULL if no panel matching the device tree node can be found. */ -struct drm_panel *of_drm_find_panel(struct device_node *np) +struct drm_panel *of_drm_find_panel(const struct device_node *np) { struct drm_panel *panel; diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 3ceea9cb9d3e..a3b356e70b35 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -191,7 +191,7 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, static void drm_pci_agp_init(struct drm_device *dev) { if (drm_core_check_feature(dev, DRIVER_USE_AGP)) { - if (drm_pci_device_is_agp(dev)) + if (pci_find_capability(dev->pdev, PCI_CAP_ID_AGP)) dev->agp = drm_agp_init(dev); if (dev->agp) { dev->agp->agp_mtrr = arch_phys_wc_add( @@ -223,7 +223,7 @@ void drm_pci_agp_destroy(struct drm_device *dev) * Try and register, if we fail to register, backout previous work. * * NOTE: This function is deprecated, please use drm_dev_alloc() and - * drm_dev_register() instead and remove your ->load() callback. + * drm_dev_register() instead and remove your &drm_driver.load callback. * * Return: 0 on success or a negative error code on failure. */ @@ -257,10 +257,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, if (ret) goto err_agp; - DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", - driver->name, driver->major, driver->minor, driver->patchlevel, - driver->date, pci_name(pdev), dev->primary->index); - /* No locking needed since shadow-attach is single-threaded since it may * only be called from the per-driver module init hook. */ if (drm_core_check_feature(dev, DRIVER_LEGACY)) diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 62b98f386fd1..c464fc4a874d 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -37,12 +37,12 @@ * rotation or Z-position. All these properties are stored in &drm_plane_state. * * To create a plane, a KMS drivers allocates and zeroes an instances of - * struct &drm_plane (possibly as part of a larger structure) and registers it + * &struct drm_plane (possibly as part of a larger structure) and registers it * with a call to drm_universal_plane_init(). * * Cursor and overlay planes are optional. All drivers should provide one * primary plane per CRTC to avoid surprising userspace too much. See enum - * &drm_plane_type for a more in-depth discussion of these special uapi-relevant + * drm_plane_type for a more in-depth discussion of these special uapi-relevant * plane types. Special planes are associated with their CRTC by calling * drm_crtc_init_with_planes(). * @@ -254,7 +254,7 @@ EXPORT_SYMBOL(drm_plane_cleanup); * @idx: index of registered plane to find for * * Given a plane index, return the registered plane from DRM device's - * list of planes with matching index. + * list of planes with matching index. This is the inverse of drm_plane_index(). */ struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx) @@ -392,12 +392,16 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return -ENOENT; drm_modeset_lock(&plane->mutex, NULL); - if (plane->crtc) + if (plane->state && plane->state->crtc) + plane_resp->crtc_id = plane->state->crtc->base.id; + else if (!plane->state && plane->crtc) plane_resp->crtc_id = plane->crtc->base.id; else plane_resp->crtc_id = 0; - if (plane->fb) + if (plane->state && plane->state->fb) + plane_resp->fb_id = plane->state->fb->base.id; + else if (!plane->state && plane->fb) plane_resp->fb_id = plane->fb->base.id; else plane_resp->fb_id = 0; @@ -478,11 +482,11 @@ static int __setplane_internal(struct drm_plane *plane, } /* Check whether this plane supports the fb pixel format. */ - ret = drm_plane_check_pixel_format(plane, fb->pixel_format); + ret = drm_plane_check_pixel_format(plane, fb->format->format); if (ret) { struct drm_format_name_buf format_name; DRM_DEBUG_KMS("Invalid pixel format %s\n", - drm_get_format_name(fb->pixel_format, + drm_get_format_name(fb->format->format, &format_name)); goto out; } @@ -854,7 +858,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (ret) goto out; - if (crtc->primary->fb->pixel_format != fb->pixel_format) { + if (crtc->primary->fb->format != fb->format) { DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); ret = -EINVAL; goto out; diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 7a7dddf604d7..148688fb920a 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -29,6 +29,7 @@ #include <drm/drm_rect.h> #include <drm/drm_atomic.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> #include <drm/drm_atomic_helper.h> #define SUBPIXEL_MASK 0xffff @@ -38,9 +39,9 @@ * * This helper library has two parts. The first part has support to implement * primary plane support on top of the normal CRTC configuration interface. - * Since the legacy ->set_config interface ties the primary plane together with - * the CRTC state this does not allow userspace to disable the primary plane - * itself. To avoid too much duplicated code use + * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary + * plane together with the CRTC state this does not allow userspace to disable + * the primary plane itself. To avoid too much duplicated code use * drm_plane_helper_check_update() which can be used to enforce the same * restrictions as primary planes had thus. The default primary plane only * expose XRBG8888 and ARGB8888 as valid pixel formats for the attached @@ -59,7 +60,7 @@ * Again drivers are strongly urged to switch to the new interfaces. * * The plane helpers share the function table structures with other helpers, - * specifically also the atomic helpers. See struct &drm_plane_helper_funcs for + * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for * the details. */ @@ -74,6 +75,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; int count = 0; /* @@ -83,7 +85,8 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, */ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->encoder && connector->encoder->crtc == crtc) { if (connector_list != NULL && count < num_connectors) *(connector_list++) = connector; @@ -91,6 +94,7 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc, count++; } } + drm_connector_list_iter_put(&conn_iter); return count; } @@ -380,7 +384,8 @@ EXPORT_SYMBOL(drm_primary_helper_update); * is called in response to a userspace SetPlane operation on the plane with a * NULL framebuffer parameter. It unconditionally fails the disable call with * -EINVAL the only way to disable the primary plane without driver support is - * to disable the entier CRTC. Which does not match the plane ->disable hook. + * to disable the entire CRTC. Which does not match the plane + * &drm_plane_funcs.disable_plane hook. * * Note that some hardware may be able to disable the primary plane without * disabling the whole CRTC. Drivers for such hardware should provide their diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 026269851ce9..56d2f93ed6b9 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -57,10 +57,6 @@ static int drm_get_platform_dev(struct platform_device *platdev, if (ret) goto err_free; - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", - driver->name, driver->major, driver->minor, driver->patchlevel, - driver->date, dev->primary->index); - return 0; err_free: @@ -78,7 +74,7 @@ err_free: * .load() function. * * NOTE: This function is deprecated, please use drm_dev_alloc() and - * drm_dev_register() instead and remove your ->load() callback. + * drm_dev_register() instead and remove your &drm_driver.load callback. * * Return: 0 on success or a negative error code on failure. */ diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 8d77b2462594..25aa4558f1b5 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -40,8 +40,11 @@ * On the export the dma_buf holds a reference to the exporting GEM * object. It takes this reference in handle_to_fd_ioctl, when it * first calls .prime_export and stores the exporting GEM object in - * the dma_buf priv. This reference is released when the dma_buf - * object goes away in the driver .release function. + * the dma_buf priv. This reference needs to be released when the + * final reference to the &dma_buf itself is dropped and its + * &dma_buf_ops.release function is called. For GEM-based drivers, + * the dma_buf should be exported using drm_gem_dmabuf_export() and + * then released by drm_gem_dmabuf_release(). * * On the import the importing GEM object holds a reference to the * dma_buf (which in turn holds a ref to the exporting GEM object). @@ -51,6 +54,16 @@ * when the imported object is destroyed, we remove the attachment * and drop the reference to the dma_buf. * + * When all the references to the &dma_buf are dropped, i.e. when + * userspace has closed both handles to the imported GEM object (through the + * FD_TO_HANDLE IOCTL) and closed the file descriptor of the exported + * (through the HANDLE_TO_FD IOCTL) dma_buf, and all kernel-internal references + * are also gone, then the dma_buf gets destroyed. This can also happen as a + * part of the clean up procedure in the drm_release() function if userspace + * fails to properly clean up. Note that both the kernel and userspace (by + * keeeping the PRIME file descriptors open) can hold references onto a + * &dma_buf. + * * Thus the chain of references always flows in one direction * (avoiding loops): importing_gem -> dmabuf -> exporting_gem * @@ -291,7 +304,7 @@ static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, * This wraps dma_buf_export() for use by generic GEM drivers that are using * drm_gem_dmabuf_release(). In addition to calling dma_buf_export(), we take * a reference to the &drm_device and the exported &drm_gem_object (stored in - * exp_info->priv) which is released by drm_gem_dmabuf_release(). + * &dma_buf_export_info.priv) which is released by drm_gem_dmabuf_release(). * * Returns the new dmabuf. */ diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index ad3caaa1f48b..02a107d50706 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -40,6 +40,12 @@ void __drm_printfn_info(struct drm_printer *p, struct va_format *vaf) } EXPORT_SYMBOL(__drm_printfn_info); +void __drm_printfn_debug(struct drm_printer *p, struct va_format *vaf) +{ + pr_debug("%s %pV", p->prefix, vaf); +} +EXPORT_SYMBOL(__drm_printfn_debug); + /** * drm_printf - print to a &drm_printer stream * @p: the &drm_printer diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index cf8f0128c161..dc4419ada12c 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -43,7 +43,7 @@ * DOC: output probing helper overview * * This library provides some helper code for output probing. It provides an - * implementation of the core connector->fill_modes interface with + * implementation of the core &drm_connector_funcs.fill_modes interface with * drm_helper_probe_single_connector_modes. * * It also provides support for polling connectors with a work item and for @@ -55,7 +55,7 @@ * handling code to avoid probing unrelated outputs. * * The probe helpers share the function table structures with other display - * helper libraries. See struct &drm_connector_helper_funcs for the details. + * helper libraries. See &struct drm_connector_helper_funcs for the details. */ static bool drm_kms_helper_poll = true; @@ -115,32 +115,38 @@ static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector) #define DRM_OUTPUT_POLL_PERIOD (10*HZ) /** - * drm_kms_helper_poll_enable_locked - re-enable output polling. + * drm_kms_helper_poll_enable - re-enable output polling. * @dev: drm_device * - * This function re-enables the output polling work without - * locking the mode_config mutex. + * This function re-enables the output polling work, after it has been + * temporarily disabled using drm_kms_helper_poll_disable(), for example over + * suspend/resume. + * + * Drivers can call this helper from their device resume implementation. It is + * an error to call this when the output polling support has not yet been set + * up. * - * This is like drm_kms_helper_poll_enable() however it is to be - * called from a context where the mode_config mutex is locked - * already. + * Note that calls to enable and disable polling must be strictly ordered, which + * is automatically the case when they're only call from suspend/resume + * callbacks. */ -void drm_kms_helper_poll_enable_locked(struct drm_device *dev) +void drm_kms_helper_poll_enable(struct drm_device *dev) { bool poll = false; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; unsigned long delay = DRM_OUTPUT_POLL_PERIOD; - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); - if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll) return; - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)) poll = true; } + drm_connector_list_iter_put(&conn_iter); if (dev->mode_config.delayed_event) { /* @@ -160,7 +166,7 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev) if (poll) schedule_delayed_work(&dev->mode_config.output_poll_work, delay); } -EXPORT_SYMBOL(drm_kms_helper_poll_enable_locked); +EXPORT_SYMBOL(drm_kms_helper_poll_enable); static enum drm_connector_status drm_connector_detect(struct drm_connector *connector, bool force) @@ -181,9 +187,9 @@ drm_connector_detect(struct drm_connector *connector, bool force) * be added to the connector's probed_modes list, then culled (based on validity * and the @maxX, @maxY parameters) and put into the normal modes list. * - * Intended to be used as a generic implementation of the ->fill_modes() - * @connector vfunc for drivers that use the CRTC helpers for output mode - * filtering and detection. + * Intended to be used as a generic implementation of the + * &drm_connector_funcs.fill_modes() vfunc for drivers that use the CRTC helpers + * for output mode filtering and detection. * * The basic procedure is as follows * @@ -195,7 +201,7 @@ drm_connector_detect(struct drm_connector *connector, bool force) * * - debugfs 'override_edid' (used for testing only) * - firmware EDID (drm_load_edid_firmware()) - * - connector helper ->get_modes() vfunc + * - &drm_connector_helper_funcs.get_modes vfunc * - if the connector status is connector_status_connected, standard * VESA DMT modes up to 1024x768 are automatically added * (drm_add_modes_noedid()) @@ -214,10 +220,10 @@ drm_connector_detect(struct drm_connector *connector, bool force) * - drm_mode_validate_basic() performs basic sanity checks * - drm_mode_validate_size() filters out modes larger than @maxX and @maxY * (if specified) - * - drm_mode_validate_flag() checks the modes againt basic connector - * capabilites (interlace_allowed,doublescan_allowed,stereo_allowed) - * - the optional connector ->mode_valid() helper can perform driver and/or - * hardware specific checks + * - drm_mode_validate_flag() checks the modes against basic connector + * capabilities (interlace_allowed,doublescan_allowed,stereo_allowed) + * - the optional &drm_connector_helper_funcs.mode_valid helper can perform + * driver and/or hardware specific checks * * 5. Any mode whose status is not OK is pruned from the connector's modes list, * accompanied by a debug message indicating the reason for the mode's @@ -287,7 +293,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, /* Re-enable polling in case the global poll config changed. */ if (drm_kms_helper_poll != dev->mode_config.poll_running) - drm_kms_helper_poll_enable_locked(dev); + drm_kms_helper_poll_enable(dev); dev->mode_config.poll_running = drm_kms_helper_poll; @@ -392,6 +398,7 @@ static void output_poll_execute(struct work_struct *work) struct delayed_work *delayed_work = to_delayed_work(work); struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; enum drm_connector_status old_status; bool repoll = false, changed; @@ -407,8 +414,8 @@ static void output_poll_execute(struct work_struct *work) goto out; } - drm_for_each_connector(connector, dev) { - + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { /* Ignore forced connectors. */ if (connector->force) continue; @@ -461,6 +468,7 @@ static void output_poll_execute(struct work_struct *work) changed = true; } } + drm_connector_list_iter_put(&conn_iter); mutex_unlock(&dev->mode_config.mutex); @@ -479,8 +487,12 @@ out: * This function disables the output polling work. * * Drivers can call this helper from their device suspend implementation. It is - * not an error to call this even when output polling isn't enabled or arlready - * disabled. + * not an error to call this even when output polling isn't enabled or already + * disabled. Polling is re-enabled by calling drm_kms_helper_poll_enable(). + * + * Note that calls to enable and disable polling must be strictly ordered, which + * is automatically the case when they're only call from suspend/resume + * callbacks. */ void drm_kms_helper_poll_disable(struct drm_device *dev) { @@ -491,24 +503,6 @@ void drm_kms_helper_poll_disable(struct drm_device *dev) EXPORT_SYMBOL(drm_kms_helper_poll_disable); /** - * drm_kms_helper_poll_enable - re-enable output polling. - * @dev: drm_device - * - * This function re-enables the output polling work. - * - * Drivers can call this helper from their device resume implementation. It is - * an error to call this when the output polling support has not yet been set - * up. - */ -void drm_kms_helper_poll_enable(struct drm_device *dev) -{ - mutex_lock(&dev->mode_config.mutex); - drm_kms_helper_poll_enable_locked(dev); - mutex_unlock(&dev->mode_config.mutex); -} -EXPORT_SYMBOL(drm_kms_helper_poll_enable); - -/** * drm_kms_helper_poll_init - initialize and enable output polling * @dev: drm_device * @@ -572,6 +566,7 @@ EXPORT_SYMBOL(drm_kms_helper_poll_fini); bool drm_helper_hpd_irq_event(struct drm_device *dev) { struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; enum drm_connector_status old_status; bool changed = false; @@ -579,8 +574,8 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) return false; mutex_lock(&dev->mode_config.mutex); - drm_for_each_connector(connector, dev) { - + drm_connector_list_iter_get(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { /* Only handle HPD capable connectors. */ if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) continue; @@ -596,7 +591,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) if (old_status != connector->status) changed = true; } - + drm_connector_list_iter_put(&conn_iter); mutex_unlock(&dev->mode_config.mutex); if (changed) diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c index 24be69d29964..7fc070f3e49e 100644 --- a/drivers/gpu/drm/drm_property.c +++ b/drivers/gpu/drm/drm_property.c @@ -34,7 +34,7 @@ * even the only way to transport metadata about the desired new modeset * configuration from userspace to the kernel. Properties have a well-defined * value range, which is enforced by the drm core. See the documentation of the - * flags member of struct &drm_property for an overview of the different + * flags member of &struct drm_property for an overview of the different * property types and ranges. * * Properties don't store the current value directly, but need to be @@ -42,8 +42,8 @@ * drm_object_attach_property(). * * Property values are only 64bit. To support bigger piles of data (like gamma - * tables, color correction matrizes or large structures) a property can instead - * point at a &drm_property_blob with that additional data + * tables, color correction matrices or large structures) a property can instead + * point at a &drm_property_blob with that additional data. * * Properties are defined by their symbolic name, userspace must keep a * per-object mapping from those names to the property ID used in the atomic diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index e6057d8cdcd5..bc5575960ebc 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -371,10 +371,10 @@ EXPORT_SYMBOL(drm_rect_rotate); * to the vertical axis of the original untransformed * coordinate space, so that you never have to flip * them when doing a rotatation and its inverse. - * That is, if you do: + * That is, if you do :: * - * drm_rotate(&r, width, height, rotation); - * drm_rotate_inv(&r, width, height, rotation); + * drm_rotate(&r, width, height, rotation); + * drm_rotate_inv(&r, width, height, rotation); * * you will always get back the original rectangle. */ diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 7bae08c2bf0a..35c5d99296b9 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -23,7 +23,7 @@ * * drm_simple_display_pipe_init() initializes a simple display pipeline * which has only one full-screen scanout buffer feeding one output. The - * pipeline is represented by struct &drm_simple_display_pipe and binds + * pipeline is represented by &struct drm_simple_display_pipe and binds * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed * entity. Some flexibility for code reuse is provided through a separately * allocated &drm_connector object and supporting optional &drm_bridge @@ -182,30 +182,11 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, struct drm_bridge *bridge) { - bridge->encoder = &pipe->encoder; - pipe->encoder.bridge = bridge; - return drm_bridge_attach(pipe->encoder.dev, bridge); + return drm_bridge_attach(&pipe->encoder, bridge, NULL); } EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); /** - * drm_simple_display_pipe_detach_bridge - Detach the bridge from the display pipe - * @pipe: simple display pipe object - * - * Detaches the drm bridge previously attached with - * drm_simple_display_pipe_attach_bridge() - */ -void drm_simple_display_pipe_detach_bridge(struct drm_simple_display_pipe *pipe) -{ - if (WARN_ON(!pipe->encoder.bridge)) - return; - - drm_bridge_detach(pipe->encoder.bridge); - pipe->encoder.bridge = NULL; -} -EXPORT_SYMBOL(drm_simple_display_pipe_detach_bridge); - -/** * drm_simple_display_pipe_init - Initialize a simple display pipeline * @dev: DRM device * @pipe: simple display pipe object to initialize diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 9a37196c1bf1..513288b5c2f6 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -255,7 +255,7 @@ static const struct attribute_group *connector_dev_groups[] = { * @connector: connector to add * * Create a connector device in sysfs, along with its associated connector - * properties (so far, connection status, dpms, mode list & edid) and + * properties (so far, connection status, dpms, mode list and edid) and * generate a hotplug event so userspace knows there's a new connector * available. */ diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index bd311c77c254..1170b3209a12 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -96,8 +96,9 @@ static pgprot_t drm_dma_prot(uint32_t map_type, struct vm_area_struct *vma) * map, get the page, increment the use count and return it. */ #if IS_ENABLED(CONFIG_AGP) -static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int drm_vm_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_file *priv = vma->vm_file->private_data; struct drm_device *dev = priv->minor->dev; struct drm_local_map *map = NULL; @@ -168,7 +169,7 @@ vm_fault_error: return VM_FAULT_SIGBUS; /* Disallow mremap */ } #else -static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int drm_vm_fault(struct vm_fault *vmf) { return VM_FAULT_SIGBUS; } @@ -184,8 +185,9 @@ static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) * Get the mapping, find the real physical page to map, get the page, and * return it. */ -static int drm_do_vm_shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int drm_vm_shm_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_local_map *map = vma->vm_private_data; unsigned long offset; unsigned long i; @@ -280,14 +282,14 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) /** * \c fault method for DMA virtual memory. * - * \param vma virtual memory area. * \param address access address. * \return pointer to the page structure. * * Determine the page number from the page offset and get it from drm_device_dma::pagelist. */ -static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int drm_vm_dma_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_file *priv = vma->vm_file->private_data; struct drm_device *dev = priv->minor->dev; struct drm_device_dma *dma = dev->dma; @@ -315,14 +317,14 @@ static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /** * \c fault method for scatter-gather virtual memory. * - * \param vma virtual memory area. * \param address access address. * \return pointer to the page structure. * * Determine the map offset from the page offset and get it from drm_sg_mem::pagelist. */ -static int drm_do_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int drm_vm_sg_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_local_map *map = vma->vm_private_data; struct drm_file *priv = vma->vm_file->private_data; struct drm_device *dev = priv->minor->dev; @@ -347,26 +349,6 @@ static int drm_do_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static int drm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - return drm_do_vm_fault(vma, vmf); -} - -static int drm_vm_shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - return drm_do_vm_shm_fault(vma, vmf); -} - -static int drm_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - return drm_do_vm_dma_fault(vma, vmf); -} - -static int drm_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - return drm_do_vm_sg_fault(vma, vmf); -} - /** AGP virtual memory operations */ static const struct vm_operations_struct drm_vm_ops = { .fault = drm_vm_fault, diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index 20cc33d1bfc1..d9100b565198 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -212,8 +212,7 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, goto out_unlock; } - ret = drm_mm_insert_node(&mgr->vm_addr_space_mm, &node->vm_node, - pages, 0, DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node(&mgr->vm_addr_space_mm, &node->vm_node, pages); if (ret) goto out_unlock; diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig index 2cde7a5442fb..cc1731c5289c 100644 --- a/drivers/gpu/drm/etnaviv/Kconfig +++ b/drivers/gpu/drm/etnaviv/Kconfig @@ -2,7 +2,8 @@ config DRM_ETNAVIV tristate "ETNAVIV (DRM support for Vivante GPU IP cores)" depends on DRM - depends on ARCH_MXC || ARCH_DOVE + depends on ARCH_MXC || ARCH_DOVE || (ARM && COMPILE_TEST) + depends on MMU select SHMEM select TMPFS select IOMMU_API diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 1086e9876f91..4f76c992043f 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -1,6 +1,7 @@ etnaviv-y := \ etnaviv_buffer.o \ etnaviv_cmd_parser.o \ + etnaviv_cmdbuf.o \ etnaviv_drv.o \ etnaviv_dump.o \ etnaviv_gem_prime.o \ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c index d9230132dfbc..ed9588f36bc9 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c @@ -15,6 +15,7 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "etnaviv_cmdbuf.h" #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" @@ -125,7 +126,7 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, u32 *ptr = buf->vaddr + off; dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n", - ptr, etnaviv_iommu_get_cmdbuf_va(gpu, buf) + off, size - len * 4 - off); + ptr, etnaviv_cmdbuf_get_va(buf) + off, size - len * 4 - off); print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, ptr, len * 4, 0); @@ -158,7 +159,7 @@ static u32 etnaviv_buffer_reserve(struct etnaviv_gpu *gpu, if (buffer->user_size + cmd_dwords * sizeof(u64) > buffer->size) buffer->user_size = 0; - return etnaviv_iommu_get_cmdbuf_va(gpu, buffer) + buffer->user_size; + return etnaviv_cmdbuf_get_va(buffer) + buffer->user_size; } u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) @@ -169,7 +170,7 @@ u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) buffer->user_size = 0; CMD_WAIT(buffer); - CMD_LINK(buffer, 2, etnaviv_iommu_get_cmdbuf_va(gpu, buffer) + + CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer) + buffer->user_size - 4); return buffer->user_size / 8; @@ -261,7 +262,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, if (drm_debug & DRM_UT_DRIVER) etnaviv_buffer_dump(gpu, buffer, 0, 0x50); - link_target = etnaviv_iommu_get_cmdbuf_va(gpu, cmdbuf); + link_target = etnaviv_cmdbuf_get_va(cmdbuf); link_dwords = cmdbuf->size / 8; /* @@ -355,12 +356,13 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | VIVS_GL_EVENT_FROM_PE); CMD_WAIT(buffer); - CMD_LINK(buffer, 2, etnaviv_iommu_get_cmdbuf_va(gpu, buffer) + + CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer) + buffer->user_size - 4); if (drm_debug & DRM_UT_DRIVER) pr_info("stream link to 0x%08x @ 0x%08x %p\n", - return_target, etnaviv_iommu_get_cmdbuf_va(gpu, cmdbuf), cmdbuf->vaddr); + return_target, etnaviv_cmdbuf_get_va(cmdbuf), + cmdbuf->vaddr); if (drm_debug & DRM_UT_DRIVER) { print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c b/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c index 2a2e5e366ab7..6e3bbcf24160 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c @@ -56,6 +56,8 @@ static const struct { ST(0x0644, 1), ST(0x064c, 1), ST(0x0680, 8), + ST(0x086c, 1), + ST(0x1028, 1), ST(0x1410, 1), ST(0x1430, 1), ST(0x1458, 1), @@ -73,8 +75,12 @@ static const struct { ST(0x16c0, 8), ST(0x16e0, 8), ST(0x1740, 8), + ST(0x17c0, 8), + ST(0x17e0, 8), ST(0x2400, 14 * 16), ST(0x10800, 32 * 16), + ST(0x14600, 16), + ST(0x14800, 8 * 8), #undef ST }; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c new file mode 100644 index 000000000000..633e0f07cbac --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2017 Etnaviv Project + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <drm/drm_mm.h> + +#include "etnaviv_cmdbuf.h" +#include "etnaviv_gpu.h" +#include "etnaviv_mmu.h" + +#define SUBALLOC_SIZE SZ_256K +#define SUBALLOC_GRANULE SZ_4K +#define SUBALLOC_GRANULES (SUBALLOC_SIZE / SUBALLOC_GRANULE) + +struct etnaviv_cmdbuf_suballoc { + /* suballocated dma buffer properties */ + struct etnaviv_gpu *gpu; + void *vaddr; + dma_addr_t paddr; + + /* GPU mapping */ + u32 iova; + struct drm_mm_node vram_node; /* only used on MMUv2 */ + + /* allocation management */ + struct mutex lock; + DECLARE_BITMAP(granule_map, SUBALLOC_GRANULES); + int free_space; + wait_queue_head_t free_event; +}; + +struct etnaviv_cmdbuf_suballoc * +etnaviv_cmdbuf_suballoc_new(struct etnaviv_gpu * gpu) +{ + struct etnaviv_cmdbuf_suballoc *suballoc; + int ret; + + suballoc = kzalloc(sizeof(*suballoc), GFP_KERNEL); + if (!suballoc) + return ERR_PTR(-ENOMEM); + + suballoc->gpu = gpu; + mutex_init(&suballoc->lock); + init_waitqueue_head(&suballoc->free_event); + + suballoc->vaddr = dma_alloc_wc(gpu->dev, SUBALLOC_SIZE, + &suballoc->paddr, GFP_KERNEL); + if (!suballoc->vaddr) + goto free_suballoc; + + ret = etnaviv_iommu_get_suballoc_va(gpu, suballoc->paddr, + &suballoc->vram_node, SUBALLOC_SIZE, + &suballoc->iova); + if (ret) + goto free_dma; + + return suballoc; + +free_dma: + dma_free_wc(gpu->dev, SUBALLOC_SIZE, suballoc->vaddr, suballoc->paddr); +free_suballoc: + kfree(suballoc); + + return NULL; +} + +void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) +{ + etnaviv_iommu_put_suballoc_va(suballoc->gpu, &suballoc->vram_node, + SUBALLOC_SIZE, suballoc->iova); + dma_free_wc(suballoc->gpu->dev, SUBALLOC_SIZE, suballoc->vaddr, + suballoc->paddr); + kfree(suballoc); +} + +struct etnaviv_cmdbuf * +etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size, + size_t nr_bos) +{ + struct etnaviv_cmdbuf *cmdbuf; + size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]), + sizeof(*cmdbuf)); + int granule_offs, order, ret; + + cmdbuf = kzalloc(sz, GFP_KERNEL); + if (!cmdbuf) + return NULL; + + cmdbuf->suballoc = suballoc; + cmdbuf->size = size; + + order = order_base_2(ALIGN(size, SUBALLOC_GRANULE) / SUBALLOC_GRANULE); +retry: + mutex_lock(&suballoc->lock); + granule_offs = bitmap_find_free_region(suballoc->granule_map, + SUBALLOC_GRANULES, order); + if (granule_offs < 0) { + suballoc->free_space = 0; + mutex_unlock(&suballoc->lock); + ret = wait_event_interruptible_timeout(suballoc->free_event, + suballoc->free_space, + msecs_to_jiffies(10 * 1000)); + if (!ret) { + dev_err(suballoc->gpu->dev, + "Timeout waiting for cmdbuf space\n"); + return NULL; + } + goto retry; + } + mutex_unlock(&suballoc->lock); + cmdbuf->suballoc_offset = granule_offs * SUBALLOC_GRANULE; + cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset; + + return cmdbuf; +} + +void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) +{ + struct etnaviv_cmdbuf_suballoc *suballoc = cmdbuf->suballoc; + int order = order_base_2(ALIGN(cmdbuf->size, SUBALLOC_GRANULE) / + SUBALLOC_GRANULE); + + mutex_lock(&suballoc->lock); + bitmap_release_region(suballoc->granule_map, + cmdbuf->suballoc_offset / SUBALLOC_GRANULE, + order); + suballoc->free_space = 1; + mutex_unlock(&suballoc->lock); + wake_up_all(&suballoc->free_event); + kfree(cmdbuf); +} + +u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf) +{ + return buf->suballoc->iova + buf->suballoc_offset; +} + +dma_addr_t etnaviv_cmdbuf_get_pa(struct etnaviv_cmdbuf *buf) +{ + return buf->suballoc->paddr + buf->suballoc_offset; +} diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h new file mode 100644 index 000000000000..80d78076c679 --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 Etnaviv Project + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ETNAVIV_CMDBUF_H__ +#define __ETNAVIV_CMDBUF_H__ + +#include <linux/types.h> + +struct etnaviv_gpu; +struct etnaviv_cmdbuf_suballoc; + +struct etnaviv_cmdbuf { + /* suballocator this cmdbuf is allocated from */ + struct etnaviv_cmdbuf_suballoc *suballoc; + /* user context key, must be unique between all active users */ + struct etnaviv_file_private *ctx; + /* cmdbuf properties */ + int suballoc_offset; + void *vaddr; + u32 size; + u32 user_size; + /* fence after which this buffer is to be disposed */ + struct dma_fence *fence; + /* target exec state */ + u32 exec_state; + /* per GPU in-flight list */ + struct list_head node; + /* BOs attached to this command buffer */ + unsigned int nr_bos; + struct etnaviv_vram_mapping *bo_map[0]; +}; + +struct etnaviv_cmdbuf_suballoc * +etnaviv_cmdbuf_suballoc_new(struct etnaviv_gpu * gpu); +void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc); + +struct etnaviv_cmdbuf * +etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size, + size_t nr_bos); +void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf); + +u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf); +dma_addr_t etnaviv_cmdbuf_get_pa(struct etnaviv_cmdbuf *buf); + +#endif /* __ETNAVIV_CMDBUF_H__ */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 00368b14d08d..587e45043542 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -18,11 +18,11 @@ #include <linux/of_platform.h> #include <drm/drm_of.h> +#include "etnaviv_cmdbuf.h" #include "etnaviv_drv.h" #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" -#include "etnaviv_gem.h" #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING static bool reglog; @@ -147,21 +147,23 @@ static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m) static int etnaviv_mm_show(struct drm_device *dev, struct seq_file *m) { - int ret; + struct drm_printer p = drm_seq_file_printer(m); read_lock(&dev->vma_offset_manager->vm_lock); - ret = drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm); + drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p); read_unlock(&dev->vma_offset_manager->vm_lock); - return ret; + return 0; } static int etnaviv_mmu_show(struct etnaviv_gpu *gpu, struct seq_file *m) { + struct drm_printer p = drm_seq_file_printer(m); + seq_printf(m, "Active Objects (%s):\n", dev_name(gpu->dev)); mutex_lock(&gpu->mmu->lock); - drm_mm_dump_table(m, &gpu->mmu->mm); + drm_mm_print(&gpu->mmu->mm, &p); mutex_unlock(&gpu->mmu->lock); return 0; @@ -175,7 +177,8 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct seq_file *m) u32 i; seq_printf(m, "virt %p - phys 0x%llx - free 0x%08x\n", - buf->vaddr, (u64)buf->paddr, size - buf->user_size); + buf->vaddr, (u64)etnaviv_cmdbuf_get_pa(buf), + size - buf->user_size); for (i = 0; i < size / 4; i++) { if (i && !(i % 4)) @@ -256,12 +259,6 @@ static int etnaviv_debugfs_init(struct drm_minor *minor) return ret; } - -static void etnaviv_debugfs_cleanup(struct drm_minor *minor) -{ - drm_debugfs_remove_files(etnaviv_debugfs_list, - ARRAY_SIZE(etnaviv_debugfs_list), minor); -} #endif /* @@ -507,7 +504,6 @@ static struct drm_driver etnaviv_drm_driver = { .gem_prime_mmap = etnaviv_gem_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = etnaviv_debugfs_init, - .debugfs_cleanup = etnaviv_debugfs_cleanup, #endif .ioctls = etnaviv_ioctls, .num_ioctls = DRM_ETNAVIV_NUM_IOCTLS, @@ -592,7 +588,7 @@ static void etnaviv_unbind(struct device *dev) drm->dev_private = NULL; kfree(priv); - drm_put_dev(drm); + drm_dev_unref(drm); } static const struct component_master_ops etnaviv_master_ops = { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index c255eda40526..e41f38667c1c 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -73,7 +73,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_file *file); int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma); -int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +int etnaviv_gem_fault(struct vm_fault *vmf); int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset); struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj); void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c index af65491a78e2..d019b5e311cc 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_dump.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c @@ -15,6 +15,7 @@ */ #include <linux/devcoredump.h> +#include "etnaviv_cmdbuf.h" #include "etnaviv_dump.h" #include "etnaviv_gem.h" #include "etnaviv_gpu.h" @@ -177,12 +178,11 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu) etnaviv_core_dump_mmu(&iter, gpu, mmu_size); etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer->vaddr, gpu->buffer->size, - etnaviv_iommu_get_cmdbuf_va(gpu, gpu->buffer)); + etnaviv_cmdbuf_get_va(gpu->buffer)); list_for_each_entry(cmd, &gpu->active_cmd_list, node) etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, cmd->vaddr, - cmd->size, - etnaviv_iommu_get_cmdbuf_va(gpu, cmd)); + cmd->size, etnaviv_cmdbuf_get_va(cmd)); /* Reserve space for the bomap */ if (n_bomap_pages) { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index aa6e35ddc87f..fd56f92f3469 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -16,6 +16,8 @@ #include <linux/spinlock.h> #include <linux/shmem_fs.h> +#include <linux/sched/mm.h> +#include <linux/sched/task.h> #include "etnaviv_drv.h" #include "etnaviv_gem.h" @@ -175,8 +177,9 @@ int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma) return obj->ops->mmap(obj, vma); } -int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int etnaviv_gem_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj = vma->vm_private_data; struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); struct page **pages, *page; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index afdd55ddf821..726090d7a6ac 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -15,6 +15,7 @@ */ #include <linux/reservation.h> +#include "etnaviv_cmdbuf.h" #include "etnaviv_drv.h" #include "etnaviv_gpu.h" #include "etnaviv_gem.h" @@ -332,8 +333,9 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, bos = drm_malloc_ab(args->nr_bos, sizeof(*bos)); relocs = drm_malloc_ab(args->nr_relocs, sizeof(*relocs)); stream = drm_malloc_ab(1, args->stream_size); - cmdbuf = etnaviv_gpu_cmdbuf_new(gpu, ALIGN(args->stream_size, 8) + 8, - args->nr_bos); + cmdbuf = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, + ALIGN(args->stream_size, 8) + 8, + args->nr_bos); if (!bos || !relocs || !stream || !cmdbuf) { ret = -ENOMEM; goto err_submit_cmds; @@ -422,7 +424,7 @@ err_submit_objects: err_submit_cmds: /* if we still own the cmdbuf */ if (cmdbuf) - etnaviv_gpu_cmdbuf_free(cmdbuf); + etnaviv_cmdbuf_free(cmdbuf); if (stream) drm_free_large(stream); if (bos) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 0a67124bb2a4..130d7d517a19 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -18,6 +18,8 @@ #include <linux/dma-fence.h> #include <linux/moduleparam.h> #include <linux/of_device.h> + +#include "etnaviv_cmdbuf.h" #include "etnaviv_dump.h" #include "etnaviv_gpu.h" #include "etnaviv_gem.h" @@ -546,6 +548,37 @@ void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch) VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch)); } +static void etnaviv_gpu_setup_pulse_eater(struct etnaviv_gpu *gpu) +{ + /* + * Base value for VIVS_PM_PULSE_EATER register on models where it + * cannot be read, extracted from vivante kernel driver. + */ + u32 pulse_eater = 0x01590880; + + if (etnaviv_is_model_rev(gpu, GC4000, 0x5208) || + etnaviv_is_model_rev(gpu, GC4000, 0x5222)) { + pulse_eater |= BIT(23); + + } + + if (etnaviv_is_model_rev(gpu, GC1000, 0x5039) || + etnaviv_is_model_rev(gpu, GC1000, 0x5040)) { + pulse_eater &= ~BIT(16); + pulse_eater |= BIT(17); + } + + if ((gpu->identity.revision > 0x5420) && + (gpu->identity.features & chipFeatures_PIPE_3D)) + { + /* Performance fix: disable internal DFS */ + pulse_eater = gpu_read(gpu, VIVS_PM_PULSE_EATER); + pulse_eater |= BIT(18); + } + + gpu_write(gpu, VIVS_PM_PULSE_EATER, pulse_eater); +} + static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) { u16 prefetch; @@ -586,6 +619,9 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config); } + /* setup the pulse eater */ + etnaviv_gpu_setup_pulse_eater(gpu); + /* setup the MMU */ etnaviv_iommu_restore(gpu); @@ -593,7 +629,7 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) prefetch = etnaviv_buffer_init(gpu); gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U); - etnaviv_gpu_start_fe(gpu, etnaviv_iommu_get_cmdbuf_va(gpu, gpu->buffer), + etnaviv_gpu_start_fe(gpu, etnaviv_cmdbuf_get_va(gpu->buffer), prefetch); } @@ -658,8 +694,15 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) goto fail; } + gpu->cmdbuf_suballoc = etnaviv_cmdbuf_suballoc_new(gpu); + if (IS_ERR(gpu->cmdbuf_suballoc)) { + dev_err(gpu->dev, "Failed to create cmdbuf suballocator\n"); + ret = PTR_ERR(gpu->cmdbuf_suballoc); + goto fail; + } + /* Create buffer: */ - gpu->buffer = etnaviv_gpu_cmdbuf_new(gpu, PAGE_SIZE, 0); + gpu->buffer = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, PAGE_SIZE, 0); if (!gpu->buffer) { ret = -ENOMEM; dev_err(gpu->dev, "could not create command buffer\n"); @@ -667,7 +710,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) } if (gpu->mmu->version == ETNAVIV_IOMMU_V1 && - gpu->buffer->paddr - gpu->memory_base > 0x80000000) { + etnaviv_cmdbuf_get_va(gpu->buffer) > 0x80000000) { ret = -EINVAL; dev_err(gpu->dev, "command buffer outside valid memory window\n"); @@ -694,7 +737,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) return 0; free_buffer: - etnaviv_gpu_cmdbuf_free(gpu->buffer); + etnaviv_cmdbuf_free(gpu->buffer); gpu->buffer = NULL; destroy_iommu: etnaviv_iommu_destroy(gpu->mmu); @@ -1117,41 +1160,6 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event) * Cmdstream submission/retirement: */ -struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size, - size_t nr_bos) -{ - struct etnaviv_cmdbuf *cmdbuf; - size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]), - sizeof(*cmdbuf)); - - cmdbuf = kzalloc(sz, GFP_KERNEL); - if (!cmdbuf) - return NULL; - - if (gpu->mmu->version == ETNAVIV_IOMMU_V2) - size = ALIGN(size, SZ_4K); - - cmdbuf->vaddr = dma_alloc_wc(gpu->dev, size, &cmdbuf->paddr, - GFP_KERNEL); - if (!cmdbuf->vaddr) { - kfree(cmdbuf); - return NULL; - } - - cmdbuf->gpu = gpu; - cmdbuf->size = size; - - return cmdbuf; -} - -void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) -{ - etnaviv_iommu_put_cmdbuf_va(cmdbuf->gpu, cmdbuf); - dma_free_wc(cmdbuf->gpu->dev, cmdbuf->size, cmdbuf->vaddr, - cmdbuf->paddr); - kfree(cmdbuf); -} - static void retire_worker(struct work_struct *work) { struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, @@ -1177,7 +1185,7 @@ static void retire_worker(struct work_struct *work) etnaviv_gem_mapping_unreference(mapping); } - etnaviv_gpu_cmdbuf_free(cmdbuf); + etnaviv_cmdbuf_free(cmdbuf); /* * We need to balance the runtime PM count caused by * each submission. Upon submission, we increment @@ -1593,10 +1601,15 @@ static void etnaviv_gpu_unbind(struct device *dev, struct device *master, #endif if (gpu->buffer) { - etnaviv_gpu_cmdbuf_free(gpu->buffer); + etnaviv_cmdbuf_free(gpu->buffer); gpu->buffer = NULL; } + if (gpu->cmdbuf_suballoc) { + etnaviv_cmdbuf_suballoc_destroy(gpu->cmdbuf_suballoc); + gpu->cmdbuf_suballoc = NULL; + } + if (gpu->mmu) { etnaviv_iommu_destroy(gpu->mmu); gpu->mmu = NULL; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h index 8c6b824e9d0a..1c0606ea7d5e 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h @@ -92,6 +92,7 @@ struct etnaviv_event { struct dma_fence *fence; }; +struct etnaviv_cmdbuf_suballoc; struct etnaviv_cmdbuf; struct etnaviv_gpu { @@ -135,6 +136,7 @@ struct etnaviv_gpu { int irq; struct etnaviv_iommu *mmu; + struct etnaviv_cmdbuf_suballoc *cmdbuf_suballoc; /* Power Control: */ struct clk *clk_bus; @@ -150,29 +152,6 @@ struct etnaviv_gpu { struct work_struct recover_work; }; -struct etnaviv_cmdbuf { - /* device this cmdbuf is allocated for */ - struct etnaviv_gpu *gpu; - /* user context key, must be unique between all active users */ - struct etnaviv_file_private *ctx; - /* cmdbuf properties */ - void *vaddr; - dma_addr_t paddr; - u32 size; - u32 user_size; - /* vram node used if the cmdbuf is mapped through the MMUv2 */ - struct drm_mm_node vram_node; - /* fence after which this buffer is to be disposed */ - struct dma_fence *fence; - /* target exec state */ - u32 exec_state; - /* per GPU in-flight list */ - struct list_head node; - /* BOs attached to this command buffer */ - unsigned int nr_bos; - struct etnaviv_vram_mapping *bo_map[0]; -}; - static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data) { etnaviv_writel(data, gpu->mmio + reg); @@ -211,9 +190,6 @@ int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu, struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout); int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf); -struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, - u32 size, size_t nr_bos); -void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf); int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu); void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu); int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c index 81f1583a7946..7a7c97f599d7 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c @@ -184,7 +184,7 @@ static void etnaviv_iommuv1_dump(struct iommu_domain *domain, void *buf) memcpy(buf, etnaviv_domain->pgtable.pgtable, PT_SIZE); } -static struct etnaviv_iommu_ops etnaviv_iommu_ops = { +static const struct etnaviv_iommu_ops etnaviv_iommu_ops = { .ops = { .domain_free = etnaviv_domain_free, .map = etnaviv_iommuv1_map, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c index 7e9c4d210a84..cbe447ac5974 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c @@ -21,6 +21,7 @@ #include <linux/dma-mapping.h> #include <linux/bitops.h> +#include "etnaviv_cmdbuf.h" #include "etnaviv_gpu.h" #include "etnaviv_mmu.h" #include "etnaviv_iommu.h" @@ -229,7 +230,7 @@ static void etnaviv_iommuv2_dump(struct iommu_domain *domain, void *buf) memcpy(buf, etnaviv_domain->stlb_cpu[i], SZ_4K); } -static struct etnaviv_iommu_ops etnaviv_iommu_ops = { +static const struct etnaviv_iommu_ops etnaviv_iommu_ops = { .ops = { .domain_free = etnaviv_iommuv2_domain_free, .map = etnaviv_iommuv2_map, @@ -254,7 +255,8 @@ void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu) prefetch = etnaviv_buffer_config_mmuv2(gpu, (u32)etnaviv_domain->mtlb_dma, (u32)etnaviv_domain->bad_page_dma); - etnaviv_gpu_start_fe(gpu, gpu->buffer->paddr, prefetch); + etnaviv_gpu_start_fe(gpu, (u32)etnaviv_cmdbuf_get_pa(gpu->buffer), + prefetch); etnaviv_gpu_wait_idle(gpu, 100); gpu_write(gpu, VIVS_MMUv2_CONTROL, VIVS_MMUv2_CONTROL_ENABLE); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index fe0e85b41310..f103e787de94 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -15,6 +15,7 @@ */ #include "common.xml.h" +#include "etnaviv_cmdbuf.h" #include "etnaviv_drv.h" #include "etnaviv_gem.h" #include "etnaviv_gpu.h" @@ -107,24 +108,21 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, struct drm_mm_node *node, size_t size) { struct etnaviv_vram_mapping *free = NULL; + enum drm_mm_insert_mode mode = DRM_MM_INSERT_LOW; int ret; lockdep_assert_held(&mmu->lock); while (1) { struct etnaviv_vram_mapping *m, *n; + struct drm_mm_scan scan; struct list_head list; bool found; - /* - * XXX: The DRM_MM_SEARCH_BELOW is really a hack to trick - * drm_mm into giving out a low IOVA after address space - * rollover. This needs a proper fix. - */ ret = drm_mm_insert_node_in_range(&mmu->mm, node, - size, 0, mmu->last_iova, ~0UL, - mmu->last_iova ? DRM_MM_SEARCH_DEFAULT : DRM_MM_SEARCH_BELOW); - + size, 0, 0, + mmu->last_iova, U64_MAX, + mode); if (ret != -ENOSPC) break; @@ -139,7 +137,7 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, } /* Try to retire some entries */ - drm_mm_init_scan(&mmu->mm, size, 0, 0); + drm_mm_scan_init(&scan, &mmu->mm, size, 0, 0, mode); found = 0; INIT_LIST_HEAD(&list); @@ -156,7 +154,7 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, continue; list_add(&free->scan_node, &list); - if (drm_mm_scan_add_block(&free->vram_node)) { + if (drm_mm_scan_add_block(&scan, &free->vram_node)) { found = true; break; } @@ -165,7 +163,7 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, if (!found) { /* Nothing found, clean up and fail */ list_for_each_entry_safe(m, n, &list, scan_node) - BUG_ON(drm_mm_scan_remove_block(&m->vram_node)); + BUG_ON(drm_mm_scan_remove_block(&scan, &m->vram_node)); break; } @@ -176,7 +174,7 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, * can leave the block pinned. */ list_for_each_entry_safe(m, n, &list, scan_node) - if (!drm_mm_scan_remove_block(&m->vram_node)) + if (!drm_mm_scan_remove_block(&scan, &m->vram_node)) list_del_init(&m->scan_node); /* @@ -191,13 +189,12 @@ static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu, list_del_init(&m->scan_node); } + mode = DRM_MM_INSERT_EVICT; + /* * We removed enough mappings so that the new allocation will - * succeed. Ensure that the MMU will be flushed before the - * associated commit requesting this mapping, and retry the - * allocation one more time. + * succeed, retry the allocation one more time. */ - mmu->need_flush = true; } return ret; @@ -249,6 +246,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, } list_add_tail(&mapping->mmu_node, &mmu->mappings); + mmu->need_flush = true; mutex_unlock(&mmu->lock); return ret; @@ -266,6 +264,7 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu, etnaviv_iommu_remove_mapping(mmu, mapping); list_del(&mapping->mmu_node); + mmu->need_flush = true; mutex_unlock(&mmu->lock); } @@ -321,55 +320,50 @@ void etnaviv_iommu_restore(struct etnaviv_gpu *gpu) etnaviv_iommuv2_restore(gpu); } -u32 etnaviv_iommu_get_cmdbuf_va(struct etnaviv_gpu *gpu, - struct etnaviv_cmdbuf *buf) +int etnaviv_iommu_get_suballoc_va(struct etnaviv_gpu *gpu, dma_addr_t paddr, + struct drm_mm_node *vram_node, size_t size, + u32 *iova) { struct etnaviv_iommu *mmu = gpu->mmu; if (mmu->version == ETNAVIV_IOMMU_V1) { - return buf->paddr - gpu->memory_base; + *iova = paddr - gpu->memory_base; + return 0; } else { int ret; - if (buf->vram_node.allocated) - return (u32)buf->vram_node.start; - mutex_lock(&mmu->lock); - ret = etnaviv_iommu_find_iova(mmu, &buf->vram_node, - buf->size + SZ_64K); + ret = etnaviv_iommu_find_iova(mmu, vram_node, size); if (ret < 0) { mutex_unlock(&mmu->lock); - return 0; + return ret; } - ret = iommu_map(mmu->domain, buf->vram_node.start, buf->paddr, - buf->size, IOMMU_READ); + ret = iommu_map(mmu->domain, vram_node->start, paddr, size, + IOMMU_READ); if (ret < 0) { - drm_mm_remove_node(&buf->vram_node); + drm_mm_remove_node(vram_node); mutex_unlock(&mmu->lock); - return 0; + return ret; } - /* - * At least on GC3000 the FE MMU doesn't properly flush old TLB - * entries. Make sure to space the command buffers out in a way - * that the FE MMU prefetch won't load invalid entries. - */ - mmu->last_iova = buf->vram_node.start + buf->size + SZ_64K; + mmu->last_iova = vram_node->start + size; gpu->mmu->need_flush = true; mutex_unlock(&mmu->lock); - return (u32)buf->vram_node.start; + *iova = (u32)vram_node->start; + return 0; } } -void etnaviv_iommu_put_cmdbuf_va(struct etnaviv_gpu *gpu, - struct etnaviv_cmdbuf *buf) +void etnaviv_iommu_put_suballoc_va(struct etnaviv_gpu *gpu, + struct drm_mm_node *vram_node, size_t size, + u32 iova) { struct etnaviv_iommu *mmu = gpu->mmu; - if (mmu->version == ETNAVIV_IOMMU_V2 && buf->vram_node.allocated) { + if (mmu->version == ETNAVIV_IOMMU_V2) { mutex_lock(&mmu->lock); - iommu_unmap(mmu->domain, buf->vram_node.start, buf->size); - drm_mm_remove_node(&buf->vram_node); + iommu_unmap(mmu->domain,iova, size); + drm_mm_remove_node(vram_node); mutex_unlock(&mmu->lock); } } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h index e787e49c9693..54be289e5981 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h @@ -62,10 +62,12 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu, struct etnaviv_vram_mapping *mapping); void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu); -u32 etnaviv_iommu_get_cmdbuf_va(struct etnaviv_gpu *gpu, - struct etnaviv_cmdbuf *buf); -void etnaviv_iommu_put_cmdbuf_va(struct etnaviv_gpu *gpu, - struct etnaviv_cmdbuf *buf); +int etnaviv_iommu_get_suballoc_va(struct etnaviv_gpu *gpu, dma_addr_t paddr, + struct drm_mm_node *vram_node, size_t size, + u32 *iova); +void etnaviv_iommu_put_suballoc_va(struct etnaviv_gpu *gpu, + struct drm_mm_node *vram_node, size_t size, + u32 iova); size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu); void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf); diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index d706ca4e2f02..1d185347c64c 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -19,7 +19,6 @@ comment "CRTCs" config DRM_EXYNOS_FIMD bool "FIMD" depends on !FB_S3C - select FB_MODE_HELPERS select MFD_SYSCON help Choose this option if you want to use Exynos FIMD for DRM. @@ -32,7 +31,6 @@ config DRM_EXYNOS5433_DECON config DRM_EXYNOS7_DECON bool "DECON on Exynos7" depends on !FB_S3C - select FB_MODE_HELPERS help Choose this option if you want to use Exynos DECON for DRM. diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 75eeb831ed6a..0fd6f7a18364 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -13,9 +13,11 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/component.h> +#include <linux/mfd/syscon.h> #include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <video/exynos5433_decon.h> @@ -25,6 +27,9 @@ #include "exynos_drm_plane.h" #include "exynos_drm_iommu.h" +#define DSD_CFG_MUX 0x1004 +#define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13) + #define WINDOWS_NR 3 #define MIN_FB_WIDTH_FOR_16WORD_BURST 128 @@ -57,6 +62,7 @@ struct decon_context { struct exynos_drm_plane planes[WINDOWS_NR]; struct exynos_drm_plane_config configs[WINDOWS_NR]; void __iomem *addr; + struct regmap *sysreg; struct clk *clks[ARRAY_SIZE(decon_clks_name)]; int pipe; unsigned long flags; @@ -118,18 +124,29 @@ static void decon_disable_vblank(struct exynos_drm_crtc *crtc) static void decon_setup_trigger(struct decon_context *ctx) { - u32 val = !(ctx->out_type & I80_HW_TRG) - ? TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | - TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN - : TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | - TRIGCON_HWTRIGMASK | TRIGCON_HWTRIGEN; - writel(val, ctx->addr + DECON_TRIGCON); + if (!(ctx->out_type & (IFTYPE_I80 | I80_HW_TRG))) + return; + + if (!(ctx->out_type & I80_HW_TRG)) { + writel(TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN + | TRIGCON_TE_AUTO_MASK | TRIGCON_SWTRIGEN, + ctx->addr + DECON_TRIGCON); + return; + } + + writel(TRIGCON_TRIGEN_PER_F | TRIGCON_TRIGEN_F | TRIGCON_HWTRIGMASK + | TRIGCON_HWTRIGEN, ctx->addr + DECON_TRIGCON); + + if (regmap_update_bits(ctx->sysreg, DSD_CFG_MUX, + DSD_CFG_MUX_TE_UNMASK_GLOBAL, ~0)) + DRM_ERROR("Cannot update sysreg.\n"); } static void decon_commit(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; struct drm_display_mode *m = &crtc->base.mode; + bool interlaced = false; u32 val; if (test_bit(BIT_SUSPENDED, &ctx->flags)) @@ -140,13 +157,16 @@ static void decon_commit(struct exynos_drm_crtc *crtc) m->crtc_hsync_end = m->crtc_htotal - 92; m->crtc_vsync_start = m->crtc_vdisplay + 1; m->crtc_vsync_end = m->crtc_vsync_start + 1; + if (m->flags & DRM_MODE_FLAG_INTERLACE) + interlaced = true; } - if (ctx->out_type & (IFTYPE_I80 | I80_HW_TRG)) - decon_setup_trigger(ctx); + decon_setup_trigger(ctx); /* lcd on and use command if */ val = VIDOUT_LCD_ON; + if (interlaced) + val |= VIDOUT_INTERLACE_EN_F; if (ctx->out_type & IFTYPE_I80) { val |= VIDOUT_COMMAND_IF; } else { @@ -155,15 +175,21 @@ static void decon_commit(struct exynos_drm_crtc *crtc) writel(val, ctx->addr + DECON_VIDOUTCON0); - val = VIDTCON2_LINEVAL(m->vdisplay - 1) | - VIDTCON2_HOZVAL(m->hdisplay - 1); + if (interlaced) + val = VIDTCON2_LINEVAL(m->vdisplay / 2 - 1) | + VIDTCON2_HOZVAL(m->hdisplay - 1); + else + val = VIDTCON2_LINEVAL(m->vdisplay - 1) | + VIDTCON2_HOZVAL(m->hdisplay - 1); writel(val, ctx->addr + DECON_VIDTCON2); if (!(ctx->out_type & IFTYPE_I80)) { - val = VIDTCON00_VBPD_F( - m->crtc_vtotal - m->crtc_vsync_end - 1) | - VIDTCON00_VFPD_F( - m->crtc_vsync_start - m->crtc_vdisplay - 1); + int vbp = m->crtc_vtotal - m->crtc_vsync_end; + int vfp = m->crtc_vsync_start - m->crtc_vdisplay; + + if (interlaced) + vbp = vbp / 2 - 1; + val = VIDTCON00_VBPD_F(vbp - 1) | VIDTCON00_VFPD_F(vfp - 1); writel(val, ctx->addr + DECON_VIDTCON00); val = VIDTCON01_VSPW_F( @@ -195,7 +221,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, val = readl(ctx->addr + DECON_WINCONx(win)); val &= ~WINCONx_BPPMODE_MASK; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_XRGB1555: val |= WINCONx_BPPMODE_16BPP_I1555; val |= WINCONx_HAWSWP_F; @@ -221,7 +247,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, return; } - DRM_DEBUG_KMS("bpp = %u\n", fb->bits_per_pixel); + DRM_DEBUG_KMS("bpp = %u\n", fb->format->cpp[0] * 8); /* * In case of exynos, setting dma-burst to 16Word causes permanent @@ -270,7 +296,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, struct decon_context *ctx = crtc->ctx; struct drm_framebuffer *fb = state->base.fb; unsigned int win = plane->index; - unsigned int bpp = fb->bits_per_pixel >> 3; + unsigned int bpp = fb->format->cpp[0]; unsigned int pitch = fb->pitches[0]; dma_addr_t dma_addr = exynos_drm_fb_dma_addr(fb, 0); u32 val; @@ -278,12 +304,22 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, if (test_bit(BIT_SUSPENDED, &ctx->flags)) return; - val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y); - writel(val, ctx->addr + DECON_VIDOSDxA(win)); + if (crtc->base.mode.flags & DRM_MODE_FLAG_INTERLACE) { + val = COORDINATE_X(state->crtc.x) | + COORDINATE_Y(state->crtc.y / 2); + writel(val, ctx->addr + DECON_VIDOSDxA(win)); + + val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | + COORDINATE_Y((state->crtc.y + state->crtc.h) / 2 - 1); + writel(val, ctx->addr + DECON_VIDOSDxB(win)); + } else { + val = COORDINATE_X(state->crtc.x) | COORDINATE_Y(state->crtc.y); + writel(val, ctx->addr + DECON_VIDOSDxA(win)); - val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | - COORDINATE_Y(state->crtc.y + state->crtc.h - 1); - writel(val, ctx->addr + DECON_VIDOSDxB(win)); + val = COORDINATE_X(state->crtc.x + state->crtc.w - 1) | + COORDINATE_Y(state->crtc.y + state->crtc.h - 1); + writel(val, ctx->addr + DECON_VIDOSDxB(win)); + } val = VIDOSD_Wx_ALPHA_R_F(0x0) | VIDOSD_Wx_ALPHA_G_F(0x0) | VIDOSD_Wx_ALPHA_B_F(0x0); @@ -355,8 +391,6 @@ static void decon_swreset(struct decon_context *ctx) udelay(10); } - WARN(tries == 0, "failed to disable DECON\n"); - writel(VIDCON0_SWRESET, ctx->addr + DECON_VIDCON0); for (tries = 2000; tries; --tries) { if (~readl(ctx->addr + DECON_VIDCON0) & VIDCON0_SWRESET) @@ -467,7 +501,7 @@ err: clk_disable_unprepare(ctx->clks[i]); } -static struct exynos_drm_crtc_ops decon_crtc_ops = { +static const struct exynos_drm_crtc_ops decon_crtc_ops = { .enable = decon_enable, .disable = decon_disable, .enable_vblank = decon_enable_vblank, @@ -557,6 +591,13 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) if (val) { writel(val, ctx->addr + DECON_VIDINTCON1); + if (ctx->out_type & IFTYPE_HDMI) { + val = readl(ctx->addr + DECON_VIDOUTCON0); + val &= VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F; + if (val == + (VIDOUT_INTERLACE_EN_F | VIDOUT_INTERLACE_FIELD_F)) + return IRQ_HANDLED; + } drm_crtc_handle_vblank(&ctx->crtc->base); } @@ -637,6 +678,15 @@ static int exynos5433_decon_probe(struct platform_device *pdev) ctx->out_type |= IFTYPE_I80; } + if (ctx->out_type | I80_HW_TRG) { + ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,disp-sysreg"); + if (IS_ERR(ctx->sysreg)) { + dev_err(dev, "failed to get system register\n"); + return PTR_ERR(ctx->sysreg); + } + } + for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) { struct clk *clk; diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index f4d5a2133777..f9ab19e205e2 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -281,7 +281,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, val = readl(ctx->regs + WINCON(win)); val &= ~WINCONx_BPPMODE_MASK; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_RGB565: val |= WINCONx_BPPMODE_16BPP_565; val |= WINCONx_BURSTLEN_16WORD; @@ -330,7 +330,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, break; } - DRM_DEBUG_KMS("bpp = %d\n", fb->bits_per_pixel); + DRM_DEBUG_KMS("bpp = %d\n", fb->format->cpp[0] * 8); /* * In case of exynos, setting dma-burst to 16Word causes permanent @@ -340,7 +340,7 @@ static void decon_win_set_pixfmt(struct decon_context *ctx, unsigned int win, * movement causes unstable DMA which results into iommu crash/tear. */ - padding = (fb->pitches[0] / (fb->bits_per_pixel >> 3)) - fb->width; + padding = (fb->pitches[0] / fb->format->cpp[0]) - fb->width; if (fb->width + padding < MIN_FB_WIDTH_FOR_16WORD_BURST) { val &= ~WINCONx_BURSTLEN_MASK; val |= WINCONx_BURSTLEN_8WORD; @@ -407,7 +407,7 @@ static void decon_update_plane(struct exynos_drm_crtc *crtc, unsigned int last_x; unsigned int last_y; unsigned int win = plane->index; - unsigned int bpp = fb->bits_per_pixel >> 3; + unsigned int bpp = fb->format->cpp[0]; unsigned int pitch = fb->pitches[0]; if (ctx->suspended) diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 528229faffe4..1ef0be338b85 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -99,7 +99,6 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data, struct drm_connector *connector) { struct exynos_dp_device *dp = to_dp(plat_data); - struct drm_encoder *encoder = &dp->encoder; int ret; drm_connector_register(connector); @@ -107,9 +106,7 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data, /* Pre-empt DP connector creation if there's a bridge */ if (dp->ptn_bridge) { - bridge->next = dp->ptn_bridge; - dp->ptn_bridge->encoder = encoder; - ret = drm_bridge_attach(encoder->dev, dp->ptn_bridge); + ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge); if (ret) { DRM_ERROR("Failed to attach bridge to drm\n"); bridge->next = NULL; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 2530bf57716a..5367b6664fe3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -39,6 +39,14 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc) if (exynos_crtc->ops->disable) exynos_crtc->ops->disable(exynos_crtc); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } } static void @@ -109,9 +117,6 @@ static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct exynos_drm_private *private = crtc->dev->dev_private; - - private->crtc[exynos_crtc->pipe] = NULL; drm_crtc_cleanup(crtc); kfree(exynos_crtc); @@ -134,7 +139,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, void *ctx) { struct exynos_drm_crtc *exynos_crtc; - struct exynos_drm_private *private = drm_dev->dev_private; struct drm_crtc *crtc; int ret; @@ -149,8 +153,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, crtc = &exynos_crtc->base; - private->crtc[pipe] = crtc; - ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, &exynos_crtc_funcs, NULL); if (ret < 0) @@ -209,23 +211,3 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) if (exynos_crtc->ops->te_handler) exynos_crtc->ops->te_handler(exynos_crtc); } - -void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc, - struct drm_file *file) -{ - struct drm_pending_vblank_event *e; - unsigned long flags; - - spin_lock_irqsave(&crtc->dev->event_lock, flags); - - e = crtc->state->event; - if (e && e->base.file_priv == file) - crtc->state->event = NULL; - else - e = NULL; - - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - - if (e) - drm_event_cancel_free(crtc->dev, &e->base); -} diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index cfdcf3e4eb1b..6a581a8af465 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -40,8 +40,4 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, */ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc); -/* This function cancels a page flip request. */ -void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc, - struct drm_file *file); - #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 739180ac3da5..035d02ecffcd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -38,56 +38,6 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 -struct exynos_atomic_commit { - struct work_struct work; - struct drm_device *dev; - struct drm_atomic_state *state; - u32 crtcs; -}; - -static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) -{ - struct drm_device *dev = commit->dev; - struct exynos_drm_private *priv = dev->dev_private; - struct drm_atomic_state *state = commit->state; - - drm_atomic_helper_commit_modeset_disables(dev, state); - - drm_atomic_helper_commit_modeset_enables(dev, state); - - /* - * Exynos can't update planes with CRTCs and encoders disabled, - * its updates routines, specially for FIMD, requires the clocks - * to be enabled. So it is necessary to handle the modeset operations - * *before* the commit_planes() step, this way it will always - * have the relevant clocks enabled to perform the update. - */ - - drm_atomic_helper_commit_planes(dev, state, 0); - - drm_atomic_helper_wait_for_vblanks(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_put(state); - - spin_lock(&priv->lock); - priv->pending &= ~commit->crtcs; - spin_unlock(&priv->lock); - - wake_up_all(&priv->wait); - - kfree(commit); -} - -static void exynos_drm_atomic_work(struct work_struct *work) -{ - struct exynos_atomic_commit *commit = container_of(work, - struct exynos_atomic_commit, work); - - exynos_atomic_commit_complete(commit); -} - static struct device *exynos_drm_get_dma_device(void); static int exynos_drm_load(struct drm_device *dev, unsigned long flags) @@ -186,7 +136,7 @@ err_free_private: return ret; } -static int exynos_drm_unload(struct drm_device *dev) +static void exynos_drm_unload(struct drm_device *dev) { exynos_drm_device_subdrv_remove(dev); @@ -200,67 +150,6 @@ static int exynos_drm_unload(struct drm_device *dev) kfree(dev->dev_private); dev->dev_private = NULL; - - return 0; -} - -static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs) -{ - bool pending; - - spin_lock(&priv->lock); - pending = priv->pending & crtcs; - spin_unlock(&priv->lock); - - return pending; -} - -int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, - bool nonblock) -{ - struct exynos_drm_private *priv = dev->dev_private; - struct exynos_atomic_commit *commit; - struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - int i, ret; - - commit = kzalloc(sizeof(*commit), GFP_KERNEL); - if (!commit) - return -ENOMEM; - - ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) { - kfree(commit); - return ret; - } - - /* This is the point of no return */ - - INIT_WORK(&commit->work, exynos_drm_atomic_work); - commit->dev = dev; - commit->state = state; - - /* Wait until all affected CRTCs have completed previous commits and - * mark them as pending. - */ - for_each_crtc_in_state(state, crtc, crtc_state, i) - commit->crtcs |= drm_crtc_mask(crtc); - - wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs)); - - spin_lock(&priv->lock); - priv->pending |= commit->crtcs; - spin_unlock(&priv->lock); - - drm_atomic_helper_swap_state(state, true); - - drm_atomic_state_get(state); - if (nonblock) - schedule_work(&commit->work); - else - exynos_atomic_commit_complete(commit); - - return 0; } int exynos_atomic_check(struct drm_device *dev, @@ -309,12 +198,7 @@ err_file_priv_free: static void exynos_drm_preclose(struct drm_device *dev, struct drm_file *file) { - struct drm_crtc *crtc; - exynos_drm_subdrv_close(dev, file); - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - exynos_drm_crtc_cancel_page_flip(crtc, file); } static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 80c4d5b81689..cf6e08cb35a7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -211,12 +211,6 @@ struct drm_exynos_file_private { struct exynos_drm_private { struct drm_fb_helper *fb_helper; - /* - * created crtc object would be contained at this array and - * this array is used to be aware of which crtc did it request vblank. - */ - struct drm_crtc *crtc[MAX_CRTC]; - struct device *dma_dev; void *mapping; @@ -231,9 +225,9 @@ struct exynos_drm_private { static inline struct exynos_drm_crtc * exynos_drm_crtc_from_pipe(struct drm_device *dev, int pipe) { - struct exynos_drm_private *private = dev->dev_private; + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); - return to_exynos_crtc(private->crtc[pipe]); + return to_exynos_crtc(crtc); } static inline struct device *to_dma_dev(struct drm_device *dev) diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index e07cb1fe4860..812e2ec0761d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1718,10 +1718,8 @@ static int exynos_dsi_bind(struct device *dev, struct device *master, } bridge = of_drm_find_bridge(dsi->bridge_node); - if (bridge) { - encoder->bridge = bridge; - drm_bridge_attach(drm_dev, bridge); - } + if (bridge) + drm_bridge_attach(encoder, bridge, NULL); return mipi_dsi_host_register(&dsi->dsi_host); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 23cce0a3f5fc..c77a5aced81a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -126,7 +126,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev, + mode_cmd->offsets[i]; } - drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &exynos_fb->fb, mode_cmd); ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); if (ret < 0) { @@ -187,11 +187,40 @@ dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index) return exynos_fb->dma_addr[index]; } +static void exynos_drm_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + /* + * Exynos can't update planes with CRTCs and encoders disabled, + * its updates routines, specially for FIMD, requires the clocks + * to be enabled. So it is necessary to handle the modeset operations + * *before* the commit_planes() step, this way it will always + * have the relevant clocks enabled to perform the update. + */ + drm_atomic_helper_commit_planes(dev, state, + DRM_PLANE_COMMIT_ACTIVE_ONLY); + + drm_atomic_helper_commit_hw_done(state); + + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); +} + +static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = { + .atomic_commit_tail = exynos_drm_atomic_commit_tail, +}; + static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { .fb_create = exynos_user_fb_create, .output_poll_changed = exynos_drm_output_poll_changed, .atomic_check = exynos_atomic_check, - .atomic_commit = exynos_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, }; void exynos_drm_mode_config_init(struct drm_device *dev) @@ -208,4 +237,5 @@ void exynos_drm_mode_config_init(struct drm_device *dev) dev->mode_config.max_height = 4096; dev->mode_config.funcs = &exynos_drm_mode_config_funcs; + dev->mode_config.helper_private = &exynos_drm_mode_config_helpers; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 9f35deb56170..bcdb2720b68e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -76,7 +76,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, { struct fb_info *fbi; struct drm_framebuffer *fb = helper->fb; - unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); + unsigned int size = fb->width * fb->height * fb->format->cpp[0]; unsigned int nr_pages; unsigned long offset; @@ -90,7 +90,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, fbi->flags = FBINFO_FLAG_DEFAULT; fbi->fbops = &exynos_drm_fb_ops; - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); nr_pages = exynos_gem->size >> PAGE_SHIFT; @@ -103,7 +103,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, return -EIO; } - offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); + offset = fbi->var.xoffset * fb->format->cpp[0]; offset += fbi->var.yoffset * fb->pitches[0]; fbi->screen_base = exynos_gem->kvaddr + offset; @@ -208,7 +208,6 @@ int exynos_drm_fbdev_init(struct drm_device *dev) struct exynos_drm_fbdev *fbdev; struct exynos_drm_private *private = dev->dev_private; struct drm_fb_helper *helper; - unsigned int num_crtc; int ret; if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) @@ -225,9 +224,7 @@ int exynos_drm_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs); - num_crtc = dev->mode_config.num_crtc; - - ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); + ret = drm_fb_helper_init(dev, helper, MAX_CONNECTOR); if (ret < 0) { DRM_ERROR("failed to initialize drm fb helper.\n"); goto err_init; @@ -270,10 +267,8 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, /* release drm framebuffer and real buffer */ if (fb_helper->fb && fb_helper->fb->funcs) { fb = fb_helper->fb; - if (fb) { - drm_framebuffer_unregister_private(fb); + if (fb) drm_framebuffer_remove(fb); - } } drm_fb_helper_unregister_fbi(fb_helper); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index e2e405170d35..a9fa444c6053 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -125,10 +125,8 @@ static struct fimd_driver_data exynos3_fimd_driver_data = { .timing_base = 0x20000, .lcdblk_offset = 0x210, .lcdblk_bypass_shift = 1, - .trg_type = I80_HW_TRG, .has_shadowcon = 1, .has_vidoutcon = 1, - .has_trigger_per_te = 1, }; static struct fimd_driver_data exynos4_fimd_driver_data = { @@ -738,7 +736,7 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, unsigned long val, size, offset; unsigned int last_x, last_y, buf_offsize, line_size; unsigned int win = plane->index; - unsigned int bpp = fb->bits_per_pixel >> 3; + unsigned int bpp = fb->format->cpp[0]; unsigned int pitch = fb->pitches[0]; if (ctx->suspended) @@ -804,7 +802,7 @@ static void fimd_update_plane(struct exynos_drm_crtc *crtc, DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); } - fimd_win_set_pixfmt(ctx, win, fb->pixel_format, state->src.w); + fimd_win_set_pixfmt(ctx, win, fb->format->format, state->src.w); /* hardware window 0 doesn't support color key. */ if (win != 0) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index fbd13fabdf2d..2b8bf2dd6387 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -1193,6 +1193,17 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, if (!node) return -ENOMEM; + /* + * To avoid an integer overflow for the later size computations, we + * enforce a maximum number of submitted commands here. This limit is + * sufficient for all conceivable usage cases of the G2D. + */ + if (req->cmd_nr > G2D_CMDLIST_DATA_NUM || + req->cmd_buf_nr > G2D_CMDLIST_DATA_NUM) { + dev_err(dev, "number of submitted G2D commands exceeds limit\n"); + return -EINVAL; + } + node->event = NULL; if (req->event_type != G2D_EVENT_NOT) { @@ -1250,7 +1261,11 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF; } - /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ + /* + * Check the size of cmdlist. The 2 that is added last comes from + * the implicit G2D_BITBLT_START that is appended once we have + * checked all the submitted commands. + */ size = cmdlist->last + req->cmd_nr * 2 + req->cmd_buf_nr * 2 + 2; if (size > G2D_CMDLIST_DATA_NUM) { dev_err(dev, "cmdlist size is too big\n"); @@ -1668,7 +1683,7 @@ struct platform_driver g2d_driver = { .probe = g2d_probe, .remove = g2d_remove, .driver = { - .name = "s5p-g2d", + .name = "exynos-drm-g2d", .owner = THIS_MODULE, .pm = &g2d_pm_ops, .of_match_table = exynos_g2d_match, diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 57b81460fec8..4c28f7ffcc4d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -447,8 +447,9 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, return ret; } -int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int exynos_drm_gem_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj = vma->vm_private_data; struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); unsigned long pfn; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index df7c543d6558..85457255fcd1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -116,7 +116,7 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, uint64_t *offset); /* page fault handler and mmap fault address(virtual) to physical memory. */ -int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +int exynos_drm_gem_fault(struct vm_fault *vmf); /* set vm_flags and we can change the vm attribute to other one at here. */ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index a0def0be6d65..2ef43d403eaa 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -19,6 +19,7 @@ #include <linux/of_graph.h> #include <linux/clk.h> #include <linux/component.h> +#include <linux/pm_runtime.h> #include <drm/drmP.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> @@ -269,35 +270,9 @@ static int parse_dt(struct exynos_mic *mic) } nodes[j++] = remote_node; - switch (i) { - case ENDPOINT_DECON_NODE: - /* decon node */ - if (of_get_child_by_name(remote_node, - "i80-if-timings")) - mic->i80_mode = 1; - - break; - case ENDPOINT_DSI_NODE: - /* panel node */ - remote_node = get_remote_node(remote_node, 1); - if (!remote_node) { - ret = -EPIPE; - goto exit; - } - nodes[j++] = remote_node; - - ret = of_get_videomode(remote_node, - &mic->vm, 0); - if (ret) { - DRM_ERROR("mic: failed to get videomode"); - goto exit; - } - - break; - default: - DRM_ERROR("mic: Unknown endpoint from MIC"); - break; - } + if (i == ENDPOINT_DECON_NODE && + of_get_child_by_name(remote_node, "i80-if-timings")) + mic->i80_mode = 1; } exit: @@ -312,7 +287,6 @@ static void mic_disable(struct drm_bridge *bridge) { } static void mic_post_disable(struct drm_bridge *bridge) { struct exynos_mic *mic = bridge->driver_private; - int i; mutex_lock(&mic_mutex); if (!mic->enabled) @@ -320,39 +294,43 @@ static void mic_post_disable(struct drm_bridge *bridge) mic_set_path(mic, 0); - for (i = NUM_CLKS - 1; i > -1; i--) - clk_disable_unprepare(mic->clks[i]); - + pm_runtime_put(mic->dev); mic->enabled = 0; already_disabled: mutex_unlock(&mic_mutex); } +static void mic_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct exynos_mic *mic = bridge->driver_private; + + mutex_lock(&mic_mutex); + drm_display_mode_to_videomode(mode, &mic->vm); + mutex_unlock(&mic_mutex); +} + static void mic_pre_enable(struct drm_bridge *bridge) { struct exynos_mic *mic = bridge->driver_private; - int ret, i; + int ret; mutex_lock(&mic_mutex); if (mic->enabled) - goto already_enabled; + goto unlock; - for (i = 0; i < NUM_CLKS; i++) { - ret = clk_prepare_enable(mic->clks[i]); - if (ret < 0) { - DRM_ERROR("Failed to enable clock (%s)\n", - clk_names[i]); - goto turn_off_clks; - } - } + ret = pm_runtime_get_sync(mic->dev); + if (ret < 0) + goto unlock; mic_set_path(mic, 1); ret = mic_sw_reset(mic); if (ret) { DRM_ERROR("Failed to reset\n"); - goto turn_off_clks; + goto turn_off; } if (!mic->i80_mode) @@ -365,10 +343,9 @@ static void mic_pre_enable(struct drm_bridge *bridge) return; -turn_off_clks: - while (--i > -1) - clk_disable_unprepare(mic->clks[i]); -already_enabled: +turn_off: + pm_runtime_put(mic->dev); +unlock: mutex_unlock(&mic_mutex); } @@ -377,6 +354,7 @@ static void mic_enable(struct drm_bridge *bridge) { } static const struct drm_bridge_funcs mic_bridge_funcs = { .disable = mic_disable, .post_disable = mic_post_disable, + .mode_set = mic_mode_set, .pre_enable = mic_pre_enable, .enable = mic_enable, }; @@ -401,14 +379,12 @@ static void exynos_mic_unbind(struct device *dev, struct device *master, void *data) { struct exynos_mic *mic = dev_get_drvdata(dev); - int i; mutex_lock(&mic_mutex); if (!mic->enabled) goto already_disabled; - for (i = NUM_CLKS - 1; i > -1; i--) - clk_disable_unprepare(mic->clks[i]); + pm_runtime_put(mic->dev); already_disabled: mutex_unlock(&mic_mutex); @@ -421,6 +397,41 @@ static const struct component_ops exynos_mic_component_ops = { .unbind = exynos_mic_unbind, }; +#ifdef CONFIG_PM +static int exynos_mic_suspend(struct device *dev) +{ + struct exynos_mic *mic = dev_get_drvdata(dev); + int i; + + for (i = NUM_CLKS - 1; i > -1; i--) + clk_disable_unprepare(mic->clks[i]); + + return 0; +} + +static int exynos_mic_resume(struct device *dev) +{ + struct exynos_mic *mic = dev_get_drvdata(dev); + int ret, i; + + for (i = 0; i < NUM_CLKS; i++) { + ret = clk_prepare_enable(mic->clks[i]); + if (ret < 0) { + DRM_ERROR("Failed to enable clock (%s)\n", + clk_names[i]); + while (--i > -1) + clk_disable_unprepare(mic->clks[i]); + return ret; + } + } + return 0; +} +#endif + +static const struct dev_pm_ops exynos_mic_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_mic_suspend, exynos_mic_resume, NULL) +}; + static int exynos_mic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -473,9 +484,18 @@ static int exynos_mic_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mic); + pm_runtime_enable(dev); + + ret = component_add(dev, &exynos_mic_component_ops); + if (ret) + goto err_pm; + DRM_DEBUG_KMS("MIC has been probed\n"); - return component_add(dev, &exynos_mic_component_ops); + return 0; + +err_pm: + pm_runtime_disable(dev); err: return ret; } @@ -483,6 +503,7 @@ err: static int exynos_mic_remove(struct platform_device *pdev) { component_del(&pdev->dev, &exynos_mic_component_ops); + pm_runtime_disable(&pdev->dev); return 0; } @@ -497,6 +518,7 @@ struct platform_driver mic_driver = { .remove = exynos_mic_remove, .driver = { .name = "exynos-mic", + .pm = &exynos_mic_pm_ops, .owner = THIS_MODULE, .of_match_table = exynos_mic_of_match, }, diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 5ed8b1effe71..88ccc0469316 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -35,6 +35,7 @@ #include <linux/io.h> #include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_graph.h> #include <linux/hdmi.h> #include <linux/component.h> #include <linux/mfd/syscon.h> @@ -133,6 +134,7 @@ struct hdmi_context { struct regulator_bulk_data regul_bulk[ARRAY_SIZE(supply)]; struct regulator *reg_hdmi_en; struct exynos_drm_clk phy_clk; + struct drm_bridge *bridge; }; static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e) @@ -509,9 +511,9 @@ static const struct hdmiphy_config hdmiphy_5433_configs[] = { { .pixel_clock = 27000000, .conf = { - 0x01, 0x51, 0x22, 0x51, 0x08, 0xfc, 0x88, 0x46, - 0x72, 0x50, 0x24, 0x0c, 0x24, 0x0f, 0x7c, 0xa5, - 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30, + 0x01, 0x51, 0x2d, 0x75, 0x01, 0x00, 0x88, 0x02, + 0x72, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac, + 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30, 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40, }, }, @@ -519,9 +521,9 @@ static const struct hdmiphy_config hdmiphy_5433_configs[] = { .pixel_clock = 27027000, .conf = { 0x01, 0x51, 0x2d, 0x72, 0x64, 0x09, 0x88, 0xc3, - 0x71, 0x50, 0x24, 0x14, 0x24, 0x0f, 0x7c, 0xa5, - 0xd4, 0x2b, 0x87, 0x00, 0x00, 0x04, 0x00, 0x30, - 0x28, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40, + 0x71, 0x50, 0x44, 0x8c, 0x27, 0x00, 0x7c, 0xac, + 0xd6, 0x2b, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30, + 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40, }, }, { @@ -587,6 +589,15 @@ static const struct hdmiphy_config hdmiphy_5433_configs[] = { 0x08, 0x10, 0x01, 0x01, 0x48, 0x4a, 0x00, 0x40, }, }, + { + .pixel_clock = 297000000, + .conf = { + 0x01, 0x51, 0x3E, 0x05, 0x40, 0xF0, 0x88, 0xC2, + 0x52, 0x53, 0x44, 0x8C, 0x27, 0x00, 0x7C, 0xAC, + 0xD6, 0x2B, 0x67, 0x00, 0x00, 0x04, 0x00, 0x30, + 0x08, 0x10, 0x01, 0x01, 0x48, 0x40, 0x00, 0x40, + }, + }, }; static const char * const hdmi_clk_gates4[] = { @@ -788,7 +799,8 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata) sizeof(buf)); if (ret > 0) { hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_EVERY_VSYNC); - hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, ret); + hdmi_reg_write_buf(hdata, HDMI_VSI_HEADER0, buf, 3); + hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3); } ret = hdmi_audio_infoframe_init(&frm.audio); @@ -912,7 +924,15 @@ static int hdmi_create_connector(struct drm_encoder *encoder) drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); - return 0; + if (hdata->bridge) { + encoder->bridge = hdata->bridge; + hdata->bridge->encoder = encoder; + ret = drm_bridge_attach(encoder, hdata->bridge, NULL); + if (ret) + DRM_ERROR("Failed to attach bridge\n"); + } + + return ret; } static bool hdmi_mode_fixup(struct drm_encoder *encoder, @@ -1581,6 +1601,31 @@ static void hdmiphy_clk_enable(struct exynos_drm_clk *clk, bool enable) hdmiphy_disable(hdata); } +static int hdmi_bridge_init(struct hdmi_context *hdata) +{ + struct device *dev = hdata->dev; + struct device_node *ep, *np; + + ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); + if (!ep) + return 0; + + np = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (!np) { + DRM_ERROR("failed to get remote port parent"); + return -EINVAL; + } + + hdata->bridge = of_drm_find_bridge(np); + of_node_put(np); + + if (!hdata->bridge) + return -EPROBE_DEFER; + + return 0; +} + static int hdmi_resources_init(struct hdmi_context *hdata) { struct device *dev = hdata->dev; @@ -1620,17 +1665,18 @@ static int hdmi_resources_init(struct hdmi_context *hdata) hdata->reg_hdmi_en = devm_regulator_get_optional(dev, "hdmi-en"); - if (PTR_ERR(hdata->reg_hdmi_en) == -ENODEV) - return 0; + if (PTR_ERR(hdata->reg_hdmi_en) != -ENODEV) { + if (IS_ERR(hdata->reg_hdmi_en)) + return PTR_ERR(hdata->reg_hdmi_en); - if (IS_ERR(hdata->reg_hdmi_en)) - return PTR_ERR(hdata->reg_hdmi_en); - - ret = regulator_enable(hdata->reg_hdmi_en); - if (ret) - DRM_ERROR("failed to enable hdmi-en regulator\n"); + ret = regulator_enable(hdata->reg_hdmi_en); + if (ret) { + DRM_ERROR("failed to enable hdmi-en regulator\n"); + return ret; + } + } - return ret; + return hdmi_bridge_init(hdata); } static struct of_device_id hdmi_match_types[] = { diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index edb20a34c66c..72143ac10525 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -485,7 +485,7 @@ static void vp_video_buffer(struct mixer_context *ctx, bool crcb_mode = false; u32 val; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_NV12: crcb_mode = false; break; @@ -494,7 +494,7 @@ static void vp_video_buffer(struct mixer_context *ctx, break; default: DRM_ERROR("pixel format for vp is wrong [%d].\n", - fb->pixel_format); + fb->format->format); return; } @@ -597,7 +597,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int fmt; u32 val; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_XRGB4444: case DRM_FORMAT_ARGB4444: fmt = MXR_FORMAT_ARGB4444; @@ -631,7 +631,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, /* converting dma address base and source offset */ dma_addr = exynos_drm_fb_dma_addr(fb, 0) - + (state->src.x * fb->bits_per_pixel >> 3) + + (state->src.x * fb->format->cpp[0]) + (state->src.y * fb->pitches[0]); src_x_offset = 0; src_y_offset = 0; @@ -649,7 +649,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, /* setup geometry */ mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), - fb->pitches[0] / (fb->bits_per_pixel >> 3)); + fb->pitches[0] / fb->format->cpp[0]); /* setup display size */ if (ctx->mxr_ver == MXR_VER_128_0_0_184 && @@ -681,7 +681,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, mixer_cfg_scan(ctx, mode->vdisplay); mixer_cfg_rgb_fmt(ctx, mode->vdisplay); mixer_cfg_layer(ctx, win, priority, true); - mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->pixel_format)); + mixer_cfg_gfx_blend(ctx, win, is_alpha_format(fb->format->format)); /* layer update mandatory for mixer 16.0.33.0 */ if (ctx->mxr_ver == MXR_VER_16_0_33_0 || @@ -701,7 +701,7 @@ static void vp_win_reset(struct mixer_context *ctx) unsigned int tries = 100; vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING); - while (tries--) { + while (--tries) { /* waiting until VP_SRESET_PROCESSING is 0 */ if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING) break; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 537ca159ffe5..04173235f448 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -94,7 +94,7 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags) "Invalid legacyfb_depth. Defaulting to 24bpp\n"); legacyfb_depth = 24; } - fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1, 1); + fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1); if (IS_ERR(fsl_dev->fbdev)) { ret = PTR_ERR(fsl_dev->fbdev); fsl_dev->fbdev = NULL; @@ -116,7 +116,7 @@ done: return ret; } -static int fsl_dcu_unload(struct drm_device *dev) +static void fsl_dcu_unload(struct drm_device *dev) { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; @@ -131,8 +131,6 @@ static int fsl_dcu_unload(struct drm_device *dev) drm_irq_uninstall(dev); dev->dev_private = NULL; - - return 0; } static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg) @@ -415,10 +413,6 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) if (ret < 0) goto unref; - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, - driver->major, driver->minor, driver->patchlevel, - driver->date, drm->primary->index); - return 0; unref: @@ -434,7 +428,8 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev) { struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev); - drm_put_dev(fsl_dev->drm); + drm_dev_unregister(fsl_dev->drm); + drm_dev_unref(fsl_dev->drm); clk_disable_unprepare(fsl_dev->clk); clk_unregister(fsl_dev->pix_clk); diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h index e9e9aeecf2eb..da9bfd432ca6 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h @@ -12,6 +12,8 @@ #ifndef __FSL_DCU_DRM_DRV_H__ #define __FSL_DCU_DRM_DRV_H__ +#include <drm/drm_encoder.h> + #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_output.h" #include "fsl_dcu_drm_plane.h" diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c index a99f48847420..0a20723aa6e1 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c @@ -44,7 +44,7 @@ static int fsl_dcu_drm_plane_atomic_check(struct drm_plane *plane, if (!state->fb || !state->crtc) return 0; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB888: case DRM_FORMAT_XRGB8888: @@ -96,7 +96,7 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, gem = drm_fb_cma_get_gem_obj(fb, 0); - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_RGB565: bpp = FSL_DCU_RGB565; break; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index 05a8ee106879..c3651456c963 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -160,10 +160,7 @@ static int fsl_dcu_attach_endpoint(struct fsl_dcu_drm_device *fsl_dev, if (!bridge) return -ENODEV; - fsl_dev->encoder.bridge = bridge; - bridge->encoder = &fsl_dev->encoder; - - return drm_bridge_attach(fsl_dev->drm, bridge); + return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL); } int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c index 3194e544ee27..b3d70a63c5a3 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c @@ -72,10 +72,8 @@ struct fsl_tcon *fsl_tcon_init(struct device *dev) return NULL; tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); - if (!tcon) { - ret = -ENOMEM; + if (!tcon) goto err_node_put; - } ret = fsl_tcon_init_regmap(dev, tcon, np); if (ret) { @@ -89,9 +87,13 @@ struct fsl_tcon *fsl_tcon_init(struct device *dev) goto err_node_put; } - of_node_put(np); - clk_prepare_enable(tcon->ipg_clk); + ret = clk_prepare_enable(tcon->ipg_clk); + if (ret) { + dev_err(dev, "Couldn't enable the TCON clock\n"); + goto err_node_put; + } + of_node_put(np); dev_info(dev, "Using TCON in bypass mode\n"); return tcon; diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig index 8906d67494fc..df11582f1efc 100644 --- a/drivers/gpu/drm/gma500/Kconfig +++ b/drivers/gpu/drm/gma500/Kconfig @@ -1,6 +1,6 @@ config DRM_GMA500 tristate "Intel GMA5/600 KMS Framebuffer" - depends on DRM && PCI && X86 + depends on DRM && PCI && X86 && MMU select DRM_KMS_HELPER select DRM_TTM # GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915 diff --git a/drivers/gpu/drm/gma500/accel_2d.c b/drivers/gpu/drm/gma500/accel_2d.c index 0d2bb1682508..c51d9259c7a7 100644 --- a/drivers/gpu/drm/gma500/accel_2d.c +++ b/drivers/gpu/drm/gma500/accel_2d.c @@ -254,7 +254,7 @@ static void psbfb_copyarea_accel(struct fb_info *info, offset = psbfb->gtt->offset; stride = fb->pitches[0]; - switch (fb->depth) { + switch (fb->format->depth) { case 8: src_format = PSB_2D_SRC_332RGB; dst_format = PSB_2D_DST_332RGB; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 8b44fa542562..ffe6b4ffa1a8 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -77,7 +77,7 @@ static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green, (transp << info->var.transp.offset); if (regno < 16) { - switch (fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 16: ((uint32_t *) info->pseudo_palette)[regno] = v; break; @@ -111,8 +111,9 @@ static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } -static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int psbfb_vm_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct psb_framebuffer *psbfb = vma->vm_private_data; struct drm_device *dev = psbfb->base.dev; struct drm_psb_private *dev_priv = dev->dev_private; @@ -244,7 +245,7 @@ static int psb_framebuffer_init(struct drm_device *dev, if (mode_cmd->pitches[0] & 63) return -EINVAL; - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &fb->base, mode_cmd); fb->gtt = gt; ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); if (ret) { @@ -407,7 +408,7 @@ static int psbfb_create(struct psb_fbdev *fbdev, fbdev->psb_fb_helper.fb = fb; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); strcpy(info->fix.id, "psbdrmfb"); info->flags = FBINFO_DEFAULT; @@ -564,7 +565,7 @@ int psb_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs); ret = drm_fb_helper_init(dev, &fbdev->psb_fb_helper, - dev_priv->ops->crtcs, INTELFB_CONN_LIMIT); + INTELFB_CONN_LIMIT); if (ret) goto free; diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 527c62917660..7da061aab729 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -164,8 +164,9 @@ int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, * vma->vm_private_data points to the GEM object that is backing this * mapping. */ -int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int psb_gem_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj; struct gtt_range *r; int ret; diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 1a1cf7a3b5ef..d1c5642b1c1e 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -59,7 +59,8 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = dev->dev_private; struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb); + struct drm_framebuffer *fb = crtc->primary->fb; + struct psb_framebuffer *psbfb = to_psb_fb(fb); int pipe = gma_crtc->pipe; const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; @@ -70,7 +71,7 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; /* no fb bound */ - if (!crtc->primary->fb) { + if (!fb) { dev_err(dev->dev, "No FB bound\n"); goto gma_pipe_cleaner; } @@ -81,19 +82,19 @@ int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, if (ret < 0) goto gma_pipe_set_base_exit; start = psbfb->gtt->offset; - offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8); + offset = y * fb->pitches[0] + x * fb->format->cpp[0]; - REG_WRITE(map->stride, crtc->primary->fb->pitches[0]); + REG_WRITE(map->stride, fb->pitches[0]); dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: dspcntr |= DISPPLANE_8BPP; break; case 16: - if (crtc->primary->fb->depth == 15) + if (fb->format->depth == 15) dspcntr |= DISPPLANE_15_16BPP; else dspcntr |= DISPPLANE_16BPP; diff --git a/drivers/gpu/drm/gma500/mdfld_intel_display.c b/drivers/gpu/drm/gma500/mdfld_intel_display.c index 92e3f93ee682..63c6e08600ae 100644 --- a/drivers/gpu/drm/gma500/mdfld_intel_display.c +++ b/drivers/gpu/drm/gma500/mdfld_intel_display.c @@ -148,7 +148,7 @@ static int check_fb(struct drm_framebuffer *fb) if (!fb) return 0; - switch (fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: case 16: case 24: @@ -165,8 +165,9 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, { struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb = crtc->primary->fb; struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb); + struct psb_framebuffer *psbfb = to_psb_fb(fb); int pipe = gma_crtc->pipe; const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; @@ -178,12 +179,12 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, dev_dbg(dev->dev, "pipe = 0x%x.\n", pipe); /* no fb bound */ - if (!crtc->primary->fb) { + if (!fb) { dev_dbg(dev->dev, "No FB bound\n"); return 0; } - ret = check_fb(crtc->primary->fb); + ret = check_fb(fb); if (ret) return ret; @@ -196,18 +197,18 @@ static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; start = psbfb->gtt->offset; - offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8); + offset = y * fb->pitches[0] + x * fb->format->cpp[0]; - REG_WRITE(map->stride, crtc->primary->fb->pitches[0]); + REG_WRITE(map->stride, fb->pitches[0]); dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: dspcntr |= DISPPLANE_8BPP; break; case 16: - if (crtc->primary->fb->depth == 15) + if (fb->format->depth == 15) dspcntr |= DISPPLANE_15_16BPP; else dspcntr |= DISPPLANE_16BPP; diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c index da9fd34b9550..0fff269d3fe6 100644 --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -599,7 +599,8 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = dev->dev_private; struct gma_crtc *gma_crtc = to_gma_crtc(crtc); - struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb); + struct drm_framebuffer *fb = crtc->primary->fb; + struct psb_framebuffer *psbfb = to_psb_fb(fb); int pipe = gma_crtc->pipe; const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; @@ -608,7 +609,7 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc, int ret = 0; /* no fb bound */ - if (!crtc->primary->fb) { + if (!fb) { dev_dbg(dev->dev, "No FB bound\n"); return 0; } @@ -617,19 +618,19 @@ static int oaktrail_pipe_set_base(struct drm_crtc *crtc, return 0; start = psbfb->gtt->offset; - offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8); + offset = y * fb->pitches[0] + x * fb->format->cpp[0]; - REG_WRITE(map->stride, crtc->primary->fb->pitches[0]); + REG_WRITE(map->stride, fb->pitches[0]); dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: dspcntr |= DISPPLANE_8BPP; break; case 16: - if (crtc->primary->fb->depth == 15) + if (fb->format->depth == 15) dspcntr |= DISPPLANE_15_16BPP; else dspcntr |= DISPPLANE_16BPP; diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index ff37ea585664..5ee93ff55608 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -159,7 +159,7 @@ static int psb_do_init(struct drm_device *dev) return 0; } -static int psb_driver_unload(struct drm_device *dev) +static void psb_driver_unload(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; @@ -220,7 +220,6 @@ static int psb_driver_unload(struct drm_device *dev) dev->dev_private = NULL; } gma_power_uninit(dev); - return 0; } static int psb_driver_load(struct drm_device *dev, unsigned long flags) @@ -407,11 +406,6 @@ out_err: return ret; } -static int psb_driver_device_is_agp(struct drm_device *dev) -{ - return 0; -} - static inline void get_brightness(struct backlight_device *bd) { #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE @@ -488,7 +482,6 @@ static struct drm_driver driver = { .set_busid = drm_pci_set_busid, .num_ioctls = ARRAY_SIZE(psb_ioctls), - .device_is_agp = psb_driver_device_is_agp, .irq_preinstall = psb_irq_preinstall, .irq_postinstall = psb_irq_postinstall, .irq_uninstall = psb_irq_uninstall, diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 05d7aaf47eea..83e22fd4cfc0 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -752,7 +752,7 @@ extern int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); extern int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset); -extern int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +extern int psb_gem_fault(struct vm_fault *vmf); /* psb_device.c */ extern const struct psb_ops psb_chip_ops; diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 2a3b7c684db2..6a10215fc42d 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -23,6 +23,7 @@ #include <linux/i2c-algo-bit.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> #include <linux/gpio.h> #include "gma_display.h" diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig index 380622a0da35..c7129dc3bdfc 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig +++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig @@ -1,6 +1,6 @@ config DRM_HISI_HIBMC tristate "DRM Support for Hisilicon Hibmc" - depends on DRM && PCI + depends on DRM && PCI && MMU select DRM_KMS_HELPER select DRM_TTM diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 2a1386e33126..c655883d3613 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -122,11 +122,11 @@ static void hibmc_plane_atomic_update(struct drm_plane *plane, writel(gpu_addr, priv->mmio + HIBMC_CRT_FB_ADDRESS); - reg = state->fb->width * (state->fb->bits_per_pixel / 8); + reg = state->fb->width * (state->fb->format->cpp[0]); /* now line_pad is 16 */ reg = PADDING(16, reg); - line_l = state->fb->width * state->fb->bits_per_pixel / 8; + line_l = state->fb->width * state->fb->format->cpp[0]; line_l = PADDING(16, line_l); writel(HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_WIDTH, reg) | HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_OFFS, line_l), @@ -136,7 +136,7 @@ static void hibmc_plane_atomic_update(struct drm_plane *plane, reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL); reg &= ~HIBMC_CRT_DISP_CTL_FORMAT_MASK; reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT, - state->fb->bits_per_pixel / 16); + state->fb->format->cpp[0] * 8 / 16); writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL); } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c index 9b0696735ba1..d7a4d9095b33 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_fbdev.c @@ -121,7 +121,7 @@ static int hibmc_drm_fb_create(struct drm_fb_helper *helper, hi_fbdev->fb = hibmc_framebuffer_init(priv->dev, &mode_cmd, gobj); if (IS_ERR(hi_fbdev->fb)) { - ret = PTR_ERR(info); + ret = PTR_ERR(hi_fbdev->fb); DRM_ERROR("failed to initialize framebuffer: %d\n", ret); goto out_release_fbi; } @@ -135,7 +135,7 @@ static int hibmc_drm_fb_create(struct drm_fb_helper *helper, info->fbops = &hibmc_drm_fb_ops; drm_fb_helper_fill_fix(info, hi_fbdev->fb->fb.pitches[0], - hi_fbdev->fb->fb.depth); + hi_fbdev->fb->fb.format->depth); drm_fb_helper_fill_var(info, &priv->fbdev->helper, sizes->fb_width, sizes->fb_height); @@ -200,8 +200,7 @@ int hibmc_fbdev_init(struct hibmc_drm_private *priv) &hibmc_fbdev_helper_funcs); /* Now just one crtc and one channel */ - ret = drm_fb_helper_init(priv->dev, - &hifbdev->helper, 1, 1); + ret = drm_fb_helper_init(priv->dev, &hifbdev->helper, 1); if (ret) { DRM_ERROR("failed to initialize fb helper: %d\n", ret); return ret; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c index e76abf61edae..20732b62d4c9 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c @@ -243,8 +243,6 @@ struct ttm_bo_driver hibmc_bo_driver = { .verify_access = hibmc_bo_verify_access, .io_mem_reserve = &hibmc_ttm_io_mem_reserve, .io_mem_free = NULL, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int hibmc_mm_init(struct hibmc_drm_private *hibmc) @@ -512,7 +510,7 @@ hibmc_framebuffer_init(struct drm_device *dev, return ERR_PTR(-ENOMEM); } - drm_helper_mode_fill_fb_struct(&hibmc_fb->fb, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &hibmc_fb->fb, mode_cmd); hibmc_fb->obj = obj; ret = drm_framebuffer_init(dev, &hibmc_fb->fb, &hibmc_fb_funcs); if (ret) { diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index 998452ad0fcb..1737e98bc10a 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -709,10 +709,7 @@ static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) int ret; /* associate the bridge to dsi encoder */ - encoder->bridge = bridge; - bridge->encoder = encoder; - - ret = drm_bridge_attach(dev, bridge); + ret = drm_bridge_attach(encoder, bridge, NULL); if (ret) { DRM_ERROR("failed to attach external bridge\n"); return ret; diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index afc2b5d2d5f0..9a0678a33e0d 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -304,8 +304,8 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc) static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) { - struct kirin_drm_private *priv = dev->dev_private; - struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + struct ade_crtc *acrtc = to_ade_crtc(crtc); struct ade_hw_ctx *ctx = acrtc->ctx; void __iomem *base = ctx->base; @@ -320,8 +320,8 @@ static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe) static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe) { - struct kirin_drm_private *priv = dev->dev_private; - struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + struct ade_crtc *acrtc = to_ade_crtc(crtc); struct ade_hw_ctx *ctx = acrtc->ctx; void __iomem *base = ctx->base; @@ -575,7 +575,6 @@ static const struct drm_crtc_funcs ade_crtc_funcs = { static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *plane) { - struct kirin_drm_private *priv = dev->dev_private; struct device_node *port; int ret; @@ -599,7 +598,6 @@ static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, } drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs); - priv->crtc[drm_crtc_index(crtc)] = crtc; return 0; } @@ -617,7 +615,7 @@ static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb, ch + 1, y, in_h, stride, (u32)obj->paddr); DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n", addr, fb->width, fb->height, fmt, - drm_get_format_name(fb->pixel_format, &format_name)); + drm_get_format_name(fb->format->format, &format_name)); /* get reg offset */ reg_ctrl = RD_CH_CTRL(ch); @@ -773,7 +771,7 @@ static void ade_update_channel(struct ade_plane *aplane, { struct ade_hw_ctx *ctx = aplane->ctx; void __iomem *base = ctx->base; - u32 fmt = ade_get_format(fb->pixel_format); + u32 fmt = ade_get_format(fb->format->format); u32 ch = aplane->ch; u32 in_w; u32 in_h; @@ -835,7 +833,7 @@ static int ade_plane_atomic_check(struct drm_plane *plane, if (!crtc || !fb) return 0; - fmt = ade_get_format(fb->pixel_format); + fmt = ade_get_format(fb->format->format); if (fmt == ADE_FORMAT_UNSUPPORT) return -EINVAL; @@ -973,9 +971,9 @@ static int ade_dts_parse(struct platform_device *pdev, struct ade_hw_ctx *ctx) return 0; } -static int ade_drm_init(struct drm_device *dev) +static int ade_drm_init(struct platform_device *pdev) { - struct platform_device *pdev = dev->platformdev; + struct drm_device *dev = platform_get_drvdata(pdev); struct ade_data *ade; struct ade_hw_ctx *ctx; struct ade_crtc *acrtc; @@ -1034,13 +1032,8 @@ static int ade_drm_init(struct drm_device *dev) return 0; } -static void ade_drm_cleanup(struct drm_device *dev) +static void ade_drm_cleanup(struct platform_device *pdev) { - struct platform_device *pdev = dev->platformdev; - struct ade_data *ade = platform_get_drvdata(pdev); - struct drm_crtc *crtc = &ade->acrtc.base; - - drm_crtc_cleanup(crtc); } const struct kirin_dc_ops ade_dc_ops = { diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index ebd5f4fe4c23..7ec93aec7e88 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -42,7 +42,7 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev) #endif drm_kms_helper_poll_fini(dev); drm_vblank_cleanup(dev); - dc_ops->cleanup(dev); + dc_ops->cleanup(to_platform_device(dev->dev)); drm_mode_config_cleanup(dev); devm_kfree(dev->dev, priv); dev->dev_private = NULL; @@ -59,8 +59,7 @@ static void kirin_fbdev_output_poll_changed(struct drm_device *dev) drm_fbdev_cma_hotplug_event(priv->fbdev); } else { priv->fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_crtc, - dev->mode_config.num_connector); + dev->mode_config.num_connector); if (IS_ERR(priv->fbdev)) priv->fbdev = NULL; } @@ -104,7 +103,7 @@ static int kirin_drm_kms_init(struct drm_device *dev) kirin_drm_mode_config_init(dev); /* display controller init */ - ret = dc_ops->init(dev); + ret = dc_ops->init(to_platform_device(dev->dev)); if (ret) goto err_mode_config_cleanup; @@ -138,7 +137,7 @@ static int kirin_drm_kms_init(struct drm_device *dev) err_unbind_all: component_unbind_all(dev->dev, dev); err_dc_cleanup: - dc_ops->cleanup(dev); + dc_ops->cleanup(to_platform_device(dev->dev)); err_mode_config_cleanup: drm_mode_config_cleanup(dev); devm_kfree(dev->dev, priv); @@ -209,8 +208,6 @@ static int kirin_drm_bind(struct device *dev) if (IS_ERR(drm_dev)) return PTR_ERR(drm_dev); - drm_dev->platformdev = to_platform_device(dev); - ret = kirin_drm_kms_init(drm_dev); if (ret) goto err_drm_dev_unref; @@ -219,10 +216,6 @@ static int kirin_drm_bind(struct device *dev) if (ret) goto err_kms_cleanup; - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", - driver->name, driver->major, driver->minor, driver->patchlevel, - driver->date, drm_dev->primary->index); - return 0; err_kms_cleanup: diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h index 1a07caf8e7f4..7f60c64915d9 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h @@ -15,12 +15,11 @@ /* display controller init/cleanup ops */ struct kirin_dc_ops { - int (*init)(struct drm_device *dev); - void (*cleanup)(struct drm_device *dev); + int (*init)(struct platform_device *pdev); + void (*cleanup)(struct platform_device *pdev); }; struct kirin_drm_private { - struct drm_crtc *crtc[MAX_CRTC]; #ifdef CONFIG_DRM_FBDEV_EMULATION struct drm_fbdev_cma *fbdev; #endif diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index ab4e6cbe1f8b..576a417690d4 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -1190,6 +1190,14 @@ static int i810_flip_bufs(struct drm_device *dev, void *data, int i810_driver_load(struct drm_device *dev, unsigned long flags) { + dev->agp = drm_agp_init(dev); + if (dev->agp) { + dev->agp->agp_mtrr = arch_phys_wc_add( + dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * + 1024 * 1024); + } + /* Our userspace depends upon the agp mapping support. */ if (!dev->agp) return -EINVAL; @@ -1249,19 +1257,3 @@ const struct drm_ioctl_desc i810_ioctls[] = { }; int i810_max_ioctl = ARRAY_SIZE(i810_ioctls); - -/** - * Determine if the device really is AGP or not. - * - * All Intel graphics chipsets are treated as AGP, even if they are really - * PCI-e. - * - * \param dev The device to be tested. - * - * \returns - * A value of 1 is always retured to indictate every i810 is AGP. - */ -int i810_driver_device_is_agp(struct drm_device *dev) -{ - return 1; -} diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index 02504a7cfaf2..37fd0906f807 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -60,7 +60,6 @@ static struct drm_driver driver = { .lastclose = i810_driver_lastclose, .preclose = i810_driver_preclose, .set_busid = drm_pci_set_busid, - .device_is_agp = i810_driver_device_is_agp, .dma_quiescent = i810_driver_dma_quiescent, .ioctls = i810_ioctls, .fops = &i810_driver_fops, diff --git a/drivers/gpu/drm/i810/i810_drv.h b/drivers/gpu/drm/i810/i810_drv.h index 93ec5dc4e7d3..c73d2f2da57b 100644 --- a/drivers/gpu/drm/i810/i810_drv.h +++ b/drivers/gpu/drm/i810/i810_drv.h @@ -124,7 +124,6 @@ extern int i810_driver_load(struct drm_device *, unsigned long flags); extern void i810_driver_lastclose(struct drm_device *dev); extern void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv); -extern int i810_driver_device_is_agp(struct drm_device *dev); extern long i810_ioctl(struct file *file, unsigned int cmd, unsigned long arg); extern const struct drm_ioctl_desc i810_ioctls[]; diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug index 51ba630a134b..597648c7a645 100644 --- a/drivers/gpu/drm/i915/Kconfig.debug +++ b/drivers/gpu/drm/i915/Kconfig.debug @@ -19,9 +19,12 @@ config DRM_I915_DEBUG bool "Enable additional driver debugging" depends on DRM_I915 select PREEMPT_COUNT + select I2C_CHARDEV + select DRM_DP_AUX_CHARDEV select X86_MSR # used by igt/pm_rpm select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks) select DRM_DEBUG_MM if DRM=y + select DRM_I915_SW_FENCE_DEBUG_OBJECTS default n help Choose this option to turn on extra driver debugging that may affect @@ -43,3 +46,15 @@ config DRM_I915_DEBUG_GEM If in doubt, say "N". +config DRM_I915_SW_FENCE_DEBUG_OBJECTS + bool "Enable additional driver debugging for fence objects" + depends on DRM_I915 + select DEBUG_OBJECTS + default n + help + Choose this option to turn on extra driver debugging that may affect + performance but will catch some internal issues. + + Recommended for driver developers only. + + If in doubt, say "N". diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 3dea46af9fe6..c62ab45683c0 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -24,7 +24,7 @@ i915-y := i915_drv.o \ intel_runtime_pm.o i915-$(CONFIG_COMPAT) += i915_ioc32.o -i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o +i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o intel_pipe_crc.o # GEM code i915-y += i915_cmd_parser.o \ @@ -55,7 +55,10 @@ i915-y += i915_cmd_parser.o \ intel_uncore.o # general-purpose microcontroller (GuC) support -i915-y += intel_guc_loader.o \ +i915-y += intel_uc.o \ + intel_guc_log.o \ + intel_guc_loader.o \ + intel_huc.o \ i915_guc_submission.o # autogenerated null render state @@ -117,11 +120,18 @@ i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o # virtual gpu code i915-y += i915_vgpu.o +# perf code +i915-y += i915_perf.o \ + i915_oa_hsw.o + ifeq ($(CONFIG_DRM_I915_GVT),y) i915-y += intel_gvt.o include $(src)/gvt/Makefile endif +# LPE Audio for VLV and CHT +i915-y += intel_lpe_audio.o + obj-$(CONFIG_DRM_I915) += i915.o CFLAGS_i915_trace_points.o := -I$(src) diff --git a/drivers/gpu/drm/i915/gvt/aperture_gm.c b/drivers/gpu/drm/i915/gvt/aperture_gm.c index f7bce8603958..3b6caaca9751 100644 --- a/drivers/gpu/drm/i915/gvt/aperture_gm.c +++ b/drivers/gpu/drm/i915/gvt/aperture_gm.c @@ -41,44 +41,35 @@ static int alloc_gm(struct intel_vgpu *vgpu, bool high_gm) { struct intel_gvt *gvt = vgpu->gvt; struct drm_i915_private *dev_priv = gvt->dev_priv; - u32 alloc_flag, search_flag; + unsigned int flags; u64 start, end, size; struct drm_mm_node *node; - int retried = 0; int ret; if (high_gm) { - search_flag = DRM_MM_SEARCH_BELOW; - alloc_flag = DRM_MM_CREATE_TOP; node = &vgpu->gm.high_gm_node; size = vgpu_hidden_sz(vgpu); - start = gvt_hidden_gmadr_base(gvt); - end = gvt_hidden_gmadr_end(gvt); + start = ALIGN(gvt_hidden_gmadr_base(gvt), I915_GTT_PAGE_SIZE); + end = ALIGN(gvt_hidden_gmadr_end(gvt), I915_GTT_PAGE_SIZE); + flags = PIN_HIGH; } else { - search_flag = DRM_MM_SEARCH_DEFAULT; - alloc_flag = DRM_MM_CREATE_DEFAULT; node = &vgpu->gm.low_gm_node; size = vgpu_aperture_sz(vgpu); - start = gvt_aperture_gmadr_base(gvt); - end = gvt_aperture_gmadr_end(gvt); + start = ALIGN(gvt_aperture_gmadr_base(gvt), I915_GTT_PAGE_SIZE); + end = ALIGN(gvt_aperture_gmadr_end(gvt), I915_GTT_PAGE_SIZE); + flags = PIN_MAPPABLE; } mutex_lock(&dev_priv->drm.struct_mutex); -search_again: - ret = drm_mm_insert_node_in_range_generic(&dev_priv->ggtt.base.mm, - node, size, 4096, 0, - start, end, search_flag, - alloc_flag); - if (ret) { - ret = i915_gem_evict_something(&dev_priv->ggtt.base, - size, 4096, 0, start, end, 0); - if (ret == 0 && ++retried < 3) - goto search_again; - - gvt_err("fail to alloc %s gm space from host, retried %d\n", - high_gm ? "high" : "low", retried); - } + ret = i915_gem_gtt_insert(&dev_priv->ggtt.base, node, + size, I915_GTT_PAGE_SIZE, + I915_COLOR_UNEVICTABLE, + start, end, flags); mutex_unlock(&dev_priv->drm.struct_mutex); + if (ret) + gvt_err("fail to alloc %s gm space from host\n", + high_gm ? "high" : "low"); + return ret; } @@ -264,7 +255,7 @@ static int alloc_resource(struct intel_vgpu *vgpu, if (request > avail) goto no_enough_resource; - vgpu_aperture_sz(vgpu) = request; + vgpu_aperture_sz(vgpu) = ALIGN(request, I915_GTT_PAGE_SIZE); item = "high GM space"; max = gvt_hidden_sz(gvt) - HOST_HIGH_GM_SIZE; @@ -275,7 +266,7 @@ static int alloc_resource(struct intel_vgpu *vgpu, if (request > avail) goto no_enough_resource; - vgpu_hidden_sz(vgpu) = request; + vgpu_hidden_sz(vgpu) = ALIGN(request, I915_GTT_PAGE_SIZE); item = "fence"; max = gvt_fence_sz(gvt) - HOST_FENCE; diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index e4563984cb1e..b9c8e2407682 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -1134,6 +1134,8 @@ static int skl_decode_mi_display_flip(struct parser_exec_state *s, u32 dword2 = cmd_val(s, 2); u32 plane = (dword0 & GENMASK(12, 8)) >> 8; + info->plane = PRIMARY_PLANE; + switch (plane) { case MI_DISPLAY_FLIP_SKL_PLANE_1_A: info->pipe = PIPE_A; @@ -1147,12 +1149,28 @@ static int skl_decode_mi_display_flip(struct parser_exec_state *s, info->pipe = PIPE_C; info->event = PRIMARY_C_FLIP_DONE; break; + + case MI_DISPLAY_FLIP_SKL_PLANE_2_A: + info->pipe = PIPE_A; + info->event = SPRITE_A_FLIP_DONE; + info->plane = SPRITE_PLANE; + break; + case MI_DISPLAY_FLIP_SKL_PLANE_2_B: + info->pipe = PIPE_B; + info->event = SPRITE_B_FLIP_DONE; + info->plane = SPRITE_PLANE; + break; + case MI_DISPLAY_FLIP_SKL_PLANE_2_C: + info->pipe = PIPE_C; + info->event = SPRITE_C_FLIP_DONE; + info->plane = SPRITE_PLANE; + break; + default: gvt_err("unknown plane code %d\n", plane); return -EINVAL; } - info->pipe = PRIMARY_PLANE; info->stride_val = (dword1 & GENMASK(15, 6)) >> 6; info->tile_val = (dword1 & GENMASK(2, 0)); info->surf_val = (dword2 & GENMASK(31, 12)) >> 12; @@ -1598,7 +1616,7 @@ static int perform_bb_shadow(struct parser_exec_state *s) return -ENOMEM; entry_obj->obj = - i915_gem_object_create(&(s->vgpu->gvt->dev_priv->drm), + i915_gem_object_create(s->vgpu->gvt->dev_priv, roundup(bb_size, PAGE_SIZE)); if (IS_ERR(entry_obj->obj)) { ret = PTR_ERR(entry_obj->obj); @@ -2661,14 +2679,13 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload) static int shadow_indirect_ctx(struct intel_shadow_wa_ctx *wa_ctx) { - struct drm_device *dev = &wa_ctx->workload->vgpu->gvt->dev_priv->drm; int ctx_size = wa_ctx->indirect_ctx.size; unsigned long guest_gma = wa_ctx->indirect_ctx.guest_gma; struct drm_i915_gem_object *obj; int ret = 0; void *map; - obj = i915_gem_object_create(dev, + obj = i915_gem_object_create(wa_ctx->workload->vgpu->gvt->dev_priv, roundup(ctx_size + CACHELINE_BYTES, PAGE_SIZE)); if (IS_ERR(obj)) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index c0c884aeb30e..6d8fde880c39 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -83,7 +83,7 @@ static int pipe_is_enabled(struct intel_vgpu *vgpu, int pipe) return 0; } -/* EDID with 1024x768 as its resolution */ +/* EDID with 1920x1200 as its resolution */ static unsigned char virtual_dp_monitor_edid[] = { /*Header*/ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, @@ -97,11 +97,16 @@ static unsigned char virtual_dp_monitor_edid[] = { 0xfc, 0x81, 0xa4, 0x55, 0x4d, 0x9d, 0x25, 0x12, 0x50, 0x54, /* Established Timings: maximum resolution is 1024x768 */ 0x21, 0x08, 0x00, - /* Standard Timings. All invalid */ - 0x00, 0xc0, 0x00, 0xc0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, - /* 18 Byte Data Blocks 1: invalid */ - 0x00, 0x00, 0x80, 0xa0, 0x70, 0xb0, + /* + * Standard Timings. + * below new resolutions can be supported: + * 1920x1080, 1280x720, 1280x960, 1280x1024, + * 1440x900, 1600x1200, 1680x1050 + */ + 0xd1, 0xc0, 0x81, 0xc0, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, + 0xa9, 0x40, 0xb3, 0x00, 0x01, 0x01, + /* 18 Byte Data Blocks 1: max resolution is 1920x1200 */ + 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x06, 0x44, 0x21, 0x00, 0x00, 0x1a, /* 18 Byte Data Blocks 2: invalid */ 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x3c, 0x18, 0x50, 0x11, 0x00, 0x0a, @@ -115,7 +120,7 @@ static unsigned char virtual_dp_monitor_edid[] = { /* Extension Block Count */ 0x00, /* Checksum */ - 0xef, + 0x45, }; #define DPCD_HEADER_SIZE 0xb @@ -328,3 +333,15 @@ int intel_vgpu_init_display(struct intel_vgpu *vgpu) else return setup_virtual_dp_monitor(vgpu, PORT_B, GVT_DP_B); } + +/** + * intel_vgpu_reset_display- reset vGPU virtual display emulation + * @vgpu: a vGPU + * + * This function is used to reset vGPU virtual display emulation stuffs + * + */ +void intel_vgpu_reset_display(struct intel_vgpu *vgpu) +{ + emulate_monitor_status_change(vgpu); +} diff --git a/drivers/gpu/drm/i915/gvt/display.h b/drivers/gpu/drm/i915/gvt/display.h index 7a60cb848268..8b234ea961f6 100644 --- a/drivers/gpu/drm/i915/gvt/display.h +++ b/drivers/gpu/drm/i915/gvt/display.h @@ -158,6 +158,7 @@ void intel_gvt_emulate_vblank(struct intel_gvt *gvt); void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt); int intel_vgpu_init_display(struct intel_vgpu *vgpu); +void intel_vgpu_reset_display(struct intel_vgpu *vgpu); void intel_vgpu_clean_display(struct intel_vgpu *vgpu); #endif diff --git a/drivers/gpu/drm/i915/gvt/execlist.c b/drivers/gpu/drm/i915/gvt/execlist.c index 34083731669d..46eb9fd3c03f 100644 --- a/drivers/gpu/drm/i915/gvt/execlist.c +++ b/drivers/gpu/drm/i915/gvt/execlist.c @@ -487,7 +487,7 @@ static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload) static void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx) { - if (wa_ctx->indirect_ctx.size == 0) + if (!wa_ctx->indirect_ctx.obj) return; i915_gem_object_unpin_map(wa_ctx->indirect_ctx.obj); diff --git a/drivers/gpu/drm/i915/gvt/firmware.c b/drivers/gpu/drm/i915/gvt/firmware.c index 2fae2a2ca96f..1cb29b2d7dc6 100644 --- a/drivers/gpu/drm/i915/gvt/firmware.c +++ b/drivers/gpu/drm/i915/gvt/firmware.c @@ -48,31 +48,6 @@ struct gvt_firmware_header { unsigned char data[1]; }; -#define RD(offset) (readl(mmio + offset.reg)) -#define WR(v, offset) (writel(v, mmio + offset.reg)) - -static void bdw_forcewake_get(void __iomem *mmio) -{ - WR(_MASKED_BIT_DISABLE(0xffff), FORCEWAKE_MT); - - RD(ECOBUS); - - if (wait_for((RD(FORCEWAKE_ACK_HSW) & FORCEWAKE_KERNEL) == 0, 50)) - gvt_err("fail to wait forcewake idle\n"); - - WR(_MASKED_BIT_ENABLE(FORCEWAKE_KERNEL), FORCEWAKE_MT); - - if (wait_for((RD(FORCEWAKE_ACK_HSW) & FORCEWAKE_KERNEL), 50)) - gvt_err("fail to wait forcewake ack\n"); - - if (wait_for((RD(GEN6_GT_THREAD_STATUS_REG) & - GEN6_GT_THREAD_STATUS_CORE_MASK) == 0, 50)) - gvt_err("fail to wait c0 wake up\n"); -} - -#undef RD -#undef WR - #define dev_to_drm_minor(d) dev_get_drvdata((d)) static ssize_t @@ -91,9 +66,9 @@ static struct bin_attribute firmware_attr = { .mmap = NULL, }; -static int expose_firmware_sysfs(struct intel_gvt *gvt, - void __iomem *mmio) +static int expose_firmware_sysfs(struct intel_gvt *gvt) { + struct drm_i915_private *dev_priv = gvt->dev_priv; struct intel_gvt_device_info *info = &gvt->device_info; struct pci_dev *pdev = gvt->dev_priv->drm.pdev; struct intel_gvt_mmio_info *e; @@ -132,7 +107,7 @@ static int expose_firmware_sysfs(struct intel_gvt *gvt, for (j = 0; j < e->length; j += 4) *(u32 *)(p + e->offset + j) = - readl(mmio + e->offset + j); + I915_READ_NOTRACE(_MMIO(e->offset + j)); } memcpy(gvt->firmware.mmio, p, info->mmio_size); @@ -235,7 +210,6 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt) struct gvt_firmware_header *h; const struct firmware *fw; char *path; - void __iomem *mmio; void *mem; int ret; @@ -260,17 +234,6 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt) firmware->mmio = mem; - mmio = pci_iomap(pdev, info->mmio_bar, info->mmio_size); - if (!mmio) { - kfree(path); - kfree(firmware->cfg_space); - kfree(firmware->mmio); - return -EINVAL; - } - - if (IS_BROADWELL(gvt->dev_priv) || IS_SKYLAKE(gvt->dev_priv)) - bdw_forcewake_get(mmio); - sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%04x.golden_hw_state", GVT_FIRMWARE_PATH, pdev->vendor, pdev->device, pdev->revision); @@ -300,13 +263,11 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt) release_firmware(fw); firmware->firmware_loaded = true; - pci_iounmap(pdev, mmio); return 0; out_free_fw: release_firmware(fw); expose_firmware: - expose_firmware_sysfs(gvt, mmio); - pci_iounmap(pdev, mmio); + expose_firmware_sysfs(gvt); return 0; } diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 47dec4acf7ff..28c92346db0e 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -606,21 +606,33 @@ struct intel_vgpu_guest_page *intel_vgpu_find_guest_page( static inline int init_shadow_page(struct intel_vgpu *vgpu, struct intel_vgpu_shadow_page *p, int type) { + struct device *kdev = &vgpu->gvt->dev_priv->drm.pdev->dev; + dma_addr_t daddr; + + daddr = dma_map_page(kdev, p->page, 0, 4096, PCI_DMA_BIDIRECTIONAL); + if (dma_mapping_error(kdev, daddr)) { + gvt_err("fail to map dma addr\n"); + return -EINVAL; + } + p->vaddr = page_address(p->page); p->type = type; INIT_HLIST_NODE(&p->node); - p->mfn = intel_gvt_hypervisor_virt_to_mfn(p->vaddr); - if (p->mfn == INTEL_GVT_INVALID_ADDR) - return -EFAULT; - + p->mfn = daddr >> GTT_PAGE_SHIFT; hash_add(vgpu->gtt.shadow_page_hash_table, &p->node, p->mfn); return 0; } -static inline void clean_shadow_page(struct intel_vgpu_shadow_page *p) +static inline void clean_shadow_page(struct intel_vgpu *vgpu, + struct intel_vgpu_shadow_page *p) { + struct device *kdev = &vgpu->gvt->dev_priv->drm.pdev->dev; + + dma_unmap_page(kdev, p->mfn << GTT_PAGE_SHIFT, 4096, + PCI_DMA_BIDIRECTIONAL); + if (!hlist_unhashed(&p->node)) hash_del(&p->node); } @@ -670,7 +682,7 @@ static void ppgtt_free_shadow_page(struct intel_vgpu_ppgtt_spt *spt) { trace_spt_free(spt->vgpu->id, spt, spt->shadow_page.type); - clean_shadow_page(&spt->shadow_page); + clean_shadow_page(spt->vgpu, &spt->shadow_page); intel_vgpu_clean_guest_page(spt->vgpu, &spt->guest_page); list_del_init(&spt->post_shadow_list); @@ -1875,8 +1887,9 @@ static int alloc_scratch_pages(struct intel_vgpu *vgpu, int page_entry_num = GTT_PAGE_SIZE >> vgpu->gvt->device_info.gtt_entry_size_shift; void *scratch_pt; - unsigned long mfn; int i; + struct device *dev = &vgpu->gvt->dev_priv->drm.pdev->dev; + dma_addr_t daddr; if (WARN_ON(type < GTT_TYPE_PPGTT_PTE_PT || type >= GTT_TYPE_MAX)) return -EINVAL; @@ -1887,16 +1900,18 @@ static int alloc_scratch_pages(struct intel_vgpu *vgpu, return -ENOMEM; } - mfn = intel_gvt_hypervisor_virt_to_mfn(scratch_pt); - if (mfn == INTEL_GVT_INVALID_ADDR) { - gvt_err("fail to translate vaddr:0x%lx\n", (unsigned long)scratch_pt); - free_page((unsigned long)scratch_pt); - return -EFAULT; + daddr = dma_map_page(dev, virt_to_page(scratch_pt), 0, + 4096, PCI_DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, daddr)) { + gvt_err("fail to dmamap scratch_pt\n"); + __free_page(virt_to_page(scratch_pt)); + return -ENOMEM; } - gtt->scratch_pt[type].page_mfn = mfn; + gtt->scratch_pt[type].page_mfn = + (unsigned long)(daddr >> GTT_PAGE_SHIFT); gtt->scratch_pt[type].page = virt_to_page(scratch_pt); gvt_dbg_mm("vgpu%d create scratch_pt: type %d mfn=0x%lx\n", - vgpu->id, type, mfn); + vgpu->id, type, gtt->scratch_pt[type].page_mfn); /* Build the tree by full filled the scratch pt with the entries which * point to the next level scratch pt or scratch page. The @@ -1930,9 +1945,14 @@ static int alloc_scratch_pages(struct intel_vgpu *vgpu, static int release_scratch_page_tree(struct intel_vgpu *vgpu) { int i; + struct device *dev = &vgpu->gvt->dev_priv->drm.pdev->dev; + dma_addr_t daddr; for (i = GTT_TYPE_PPGTT_PTE_PT; i < GTT_TYPE_MAX; i++) { if (vgpu->gtt.scratch_pt[i].page != NULL) { + daddr = (dma_addr_t)(vgpu->gtt.scratch_pt[i].page_mfn << + GTT_PAGE_SHIFT); + dma_unmap_page(dev, daddr, 4096, PCI_DMA_BIDIRECTIONAL); __free_page(vgpu->gtt.scratch_pt[i].page); vgpu->gtt.scratch_pt[i].page = NULL; vgpu->gtt.scratch_pt[i].page_mfn = 0; @@ -2192,6 +2212,8 @@ int intel_gvt_init_gtt(struct intel_gvt *gvt) { int ret; void *page; + struct device *dev = &gvt->dev_priv->drm.pdev->dev; + dma_addr_t daddr; gvt_dbg_core("init gtt\n"); @@ -2209,14 +2231,16 @@ int intel_gvt_init_gtt(struct intel_gvt *gvt) gvt_err("fail to allocate scratch ggtt page\n"); return -ENOMEM; } - gvt->gtt.scratch_ggtt_page = virt_to_page(page); - gvt->gtt.scratch_ggtt_mfn = intel_gvt_hypervisor_virt_to_mfn(page); - if (gvt->gtt.scratch_ggtt_mfn == INTEL_GVT_INVALID_ADDR) { - gvt_err("fail to translate scratch ggtt page\n"); - __free_page(gvt->gtt.scratch_ggtt_page); - return -EFAULT; + daddr = dma_map_page(dev, virt_to_page(page), 0, + 4096, PCI_DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, daddr)) { + gvt_err("fail to dmamap scratch ggtt page\n"); + __free_page(virt_to_page(page)); + return -ENOMEM; } + gvt->gtt.scratch_ggtt_page = virt_to_page(page); + gvt->gtt.scratch_ggtt_mfn = (unsigned long)(daddr >> GTT_PAGE_SHIFT); if (enable_out_of_sync) { ret = setup_spt_oos(gvt); @@ -2239,6 +2263,12 @@ int intel_gvt_init_gtt(struct intel_gvt *gvt) */ void intel_gvt_clean_gtt(struct intel_gvt *gvt) { + struct device *dev = &gvt->dev_priv->drm.pdev->dev; + dma_addr_t daddr = (dma_addr_t)(gvt->gtt.scratch_ggtt_mfn << + GTT_PAGE_SHIFT); + + dma_unmap_page(dev, daddr, 4096, PCI_DMA_BIDIRECTIONAL); + __free_page(gvt->gtt.scratch_ggtt_page); if (enable_out_of_sync) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index e6bf5c533fbe..3b9d59e457ba 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -68,8 +68,6 @@ static const struct intel_gvt_ops intel_gvt_ops = { */ int intel_gvt_init_host(void) { - int ret; - if (intel_gvt_host.initialized) return 0; @@ -96,11 +94,6 @@ int intel_gvt_init_host(void) if (!intel_gvt_host.mpt) return -EINVAL; - /* Try to detect if we're running in host instead of VM. */ - ret = intel_gvt_hypervisor_detect_host(); - if (ret) - return -ENODEV; - gvt_dbg_core("Running with hypervisor %s in host mode\n", supported_hypervisors[intel_gvt_host.hypervisor_type]); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index ab2ea157da4c..1d450627ff65 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -2167,7 +2167,7 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_DFH(0x1217c, D_ALL, F_CMD_ACCESS, NULL, NULL); MMIO_F(0x2290, 8, 0, 0, 0, D_HSW_PLUS, NULL, NULL); - MMIO_D(OACONTROL, D_HSW); + MMIO_D(GEN7_OACONTROL, D_HSW); MMIO_D(0x2b00, D_BDW_PLUS); MMIO_D(0x2360, D_BDW_PLUS); MMIO_F(0x5200, 32, 0, 0, 0, D_ALL, NULL, NULL); diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index 30e543f5a703..df7f33abd393 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -38,7 +38,6 @@ * both Xen and KVM by providing dedicated hypervisor-related MPT modules. */ struct intel_gvt_mpt { - int (*detect_host)(void); int (*host_init)(struct device *dev, void *gvt, const void *ops); void (*host_exit)(struct device *dev, void *gvt); int (*attach_vgpu)(void *vgpu, unsigned long *handle); diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c index f7be02ac4be1..92bb247e3478 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.c +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -176,26 +176,15 @@ int intel_vgpu_reg_imr_handler(struct intel_vgpu *vgpu, { struct intel_gvt *gvt = vgpu->gvt; struct intel_gvt_irq_ops *ops = gvt->irq.ops; - u32 changed, masked, unmasked; u32 imr = *(u32 *)p_data; - gvt_dbg_irq("write IMR %x with val %x\n", - reg, imr); - - gvt_dbg_irq("old vIMR %x\n", vgpu_vreg(vgpu, reg)); - - /* figure out newly masked/unmasked bits */ - changed = vgpu_vreg(vgpu, reg) ^ imr; - masked = (vgpu_vreg(vgpu, reg) & changed) ^ changed; - unmasked = masked ^ changed; - - gvt_dbg_irq("changed %x, masked %x, unmasked %x\n", - changed, masked, unmasked); + gvt_dbg_irq("write IMR %x, new %08x, old %08x, changed %08x\n", + reg, imr, vgpu_vreg(vgpu, reg), vgpu_vreg(vgpu, reg) ^ imr); vgpu_vreg(vgpu, reg) = imr; ops->check_pending_irq(vgpu); - gvt_dbg_irq("IRQ: new vIMR %x\n", vgpu_vreg(vgpu, reg)); + return 0; } @@ -217,14 +206,11 @@ int intel_vgpu_reg_master_irq_handler(struct intel_vgpu *vgpu, { struct intel_gvt *gvt = vgpu->gvt; struct intel_gvt_irq_ops *ops = gvt->irq.ops; - u32 changed, enabled, disabled; u32 ier = *(u32 *)p_data; u32 virtual_ier = vgpu_vreg(vgpu, reg); - gvt_dbg_irq("write master irq reg %x with val %x\n", - reg, ier); - - gvt_dbg_irq("old vreg %x\n", vgpu_vreg(vgpu, reg)); + gvt_dbg_irq("write MASTER_IRQ %x, new %08x, old %08x, changed %08x\n", + reg, ier, virtual_ier, virtual_ier ^ ier); /* * GEN8_MASTER_IRQ is a special irq register, @@ -236,16 +222,8 @@ int intel_vgpu_reg_master_irq_handler(struct intel_vgpu *vgpu, vgpu_vreg(vgpu, reg) &= ~GEN8_MASTER_IRQ_CONTROL; vgpu_vreg(vgpu, reg) |= ier; - /* figure out newly enabled/disable bits */ - changed = virtual_ier ^ ier; - enabled = (virtual_ier & changed) ^ changed; - disabled = enabled ^ changed; - - gvt_dbg_irq("changed %x, enabled %x, disabled %x\n", - changed, enabled, disabled); - ops->check_pending_irq(vgpu); - gvt_dbg_irq("new vreg %x\n", vgpu_vreg(vgpu, reg)); + return 0; } @@ -268,21 +246,11 @@ int intel_vgpu_reg_ier_handler(struct intel_vgpu *vgpu, struct intel_gvt *gvt = vgpu->gvt; struct intel_gvt_irq_ops *ops = gvt->irq.ops; struct intel_gvt_irq_info *info; - u32 changed, enabled, disabled; u32 ier = *(u32 *)p_data; - gvt_dbg_irq("write IER %x with val %x\n", - reg, ier); - - gvt_dbg_irq("old vIER %x\n", vgpu_vreg(vgpu, reg)); + gvt_dbg_irq("write IER %x, new %08x, old %08x, changed %08x\n", + reg, ier, vgpu_vreg(vgpu, reg), vgpu_vreg(vgpu, reg) ^ ier); - /* figure out newly enabled/disable bits */ - changed = vgpu_vreg(vgpu, reg) ^ ier; - enabled = (vgpu_vreg(vgpu, reg) & changed) ^ changed; - disabled = enabled ^ changed; - - gvt_dbg_irq("changed %x, enabled %x, disabled %x\n", - changed, enabled, disabled); vgpu_vreg(vgpu, reg) = ier; info = regbase_to_irq_info(gvt, ier_to_regbase(reg)); @@ -293,7 +261,7 @@ int intel_vgpu_reg_ier_handler(struct intel_vgpu *vgpu, update_upstream_irq(vgpu, info); ops->check_pending_irq(vgpu); - gvt_dbg_irq("new vIER %x\n", vgpu_vreg(vgpu, reg)); + return 0; } @@ -317,7 +285,8 @@ int intel_vgpu_reg_iir_handler(struct intel_vgpu *vgpu, unsigned int reg, iir_to_regbase(reg)); u32 iir = *(u32 *)p_data; - gvt_dbg_irq("write IIR %x with val %x\n", reg, iir); + gvt_dbg_irq("write IIR %x, new %08x, old %08x, changed %08x\n", + reg, iir, vgpu_vreg(vgpu, reg), vgpu_vreg(vgpu, reg) ^ iir); if (WARN_ON(!info)) return -EINVAL; @@ -619,6 +588,10 @@ static void gen8_init_irq( SET_BIT_INFO(irq, 3, PRIMARY_A_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_A); SET_BIT_INFO(irq, 3, PRIMARY_B_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_B); SET_BIT_INFO(irq, 3, PRIMARY_C_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_C); + + SET_BIT_INFO(irq, 4, SPRITE_A_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_A); + SET_BIT_INFO(irq, 4, SPRITE_B_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_B); + SET_BIT_INFO(irq, 4, SPRITE_C_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_C); } /* GEN8 interrupt PCU events */ diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c index 3f656e3a6e5a..0f7f5d97f582 100644 --- a/drivers/gpu/drm/i915/gvt/kvmgt.c +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c @@ -77,7 +77,7 @@ struct kvmgt_guest_info { struct gvt_dma { struct rb_node node; gfn_t gfn; - kvm_pfn_t pfn; + unsigned long iova; }; static inline bool handle_valid(unsigned long handle) @@ -89,6 +89,35 @@ static int kvmgt_guest_init(struct mdev_device *mdev); static void intel_vgpu_release_work(struct work_struct *work); static bool kvmgt_guest_exit(struct kvmgt_guest_info *info); +static int gvt_dma_map_iova(struct intel_vgpu *vgpu, kvm_pfn_t pfn, + unsigned long *iova) +{ + struct page *page; + struct device *dev = &vgpu->gvt->dev_priv->drm.pdev->dev; + dma_addr_t daddr; + + page = pfn_to_page(pfn); + if (is_error_page(page)) + return -EFAULT; + + daddr = dma_map_page(dev, page, 0, PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, daddr)) + return -ENOMEM; + + *iova = (unsigned long)(daddr >> PAGE_SHIFT); + return 0; +} + +static void gvt_dma_unmap_iova(struct intel_vgpu *vgpu, unsigned long iova) +{ + struct device *dev = &vgpu->gvt->dev_priv->drm.pdev->dev; + dma_addr_t daddr; + + daddr = (dma_addr_t)(iova << PAGE_SHIFT); + dma_unmap_page(dev, daddr, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); +} + static struct gvt_dma *__gvt_cache_find(struct intel_vgpu *vgpu, gfn_t gfn) { struct rb_node *node = vgpu->vdev.cache.rb_node; @@ -111,21 +140,22 @@ out: return ret; } -static kvm_pfn_t gvt_cache_find(struct intel_vgpu *vgpu, gfn_t gfn) +static unsigned long gvt_cache_find(struct intel_vgpu *vgpu, gfn_t gfn) { struct gvt_dma *entry; - kvm_pfn_t pfn; + unsigned long iova; mutex_lock(&vgpu->vdev.cache_lock); entry = __gvt_cache_find(vgpu, gfn); - pfn = (entry == NULL) ? 0 : entry->pfn; + iova = (entry == NULL) ? INTEL_GVT_INVALID_ADDR : entry->iova; mutex_unlock(&vgpu->vdev.cache_lock); - return pfn; + return iova; } -static void gvt_cache_add(struct intel_vgpu *vgpu, gfn_t gfn, kvm_pfn_t pfn) +static void gvt_cache_add(struct intel_vgpu *vgpu, gfn_t gfn, + unsigned long iova) { struct gvt_dma *new, *itr; struct rb_node **link = &vgpu->vdev.cache.rb_node, *parent = NULL; @@ -135,7 +165,7 @@ static void gvt_cache_add(struct intel_vgpu *vgpu, gfn_t gfn, kvm_pfn_t pfn) return; new->gfn = gfn; - new->pfn = pfn; + new->iova = iova; mutex_lock(&vgpu->vdev.cache_lock); while (*link) { @@ -182,6 +212,7 @@ static void gvt_cache_remove(struct intel_vgpu *vgpu, gfn_t gfn) } g1 = gfn; + gvt_dma_unmap_iova(vgpu, this->iova); rc = vfio_unpin_pages(dev, &g1, 1); WARN_ON(rc != 1); __gvt_cache_remove_entry(vgpu, this); @@ -204,6 +235,7 @@ static void gvt_cache_destroy(struct intel_vgpu *vgpu) mutex_lock(&vgpu->vdev.cache_lock); while ((node = rb_first(&vgpu->vdev.cache))) { dma = rb_entry(node, struct gvt_dma, node); + gvt_dma_unmap_iova(vgpu, dma->iova); gfn = dma->gfn; vfio_unpin_pages(dev, &gfn, 1); @@ -965,11 +997,6 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd, sparse->areas[0].offset = PAGE_ALIGN(vgpu_aperture_offset(vgpu)); sparse->areas[0].size = vgpu_aperture_sz(vgpu); - if (!caps.buf) { - kfree(caps.buf); - caps.buf = NULL; - caps.size = 0; - } break; case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: @@ -1248,43 +1275,6 @@ static void kvmgt_page_track_flush_slot(struct kvm *kvm, spin_unlock(&kvm->mmu_lock); } -static bool kvmgt_check_guest(void) -{ - unsigned int eax, ebx, ecx, edx; - char s[12]; - unsigned int *i; - - eax = KVM_CPUID_SIGNATURE; - ebx = ecx = edx = 0; - - asm volatile ("cpuid" - : "+a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) - : - : "cc", "memory"); - i = (unsigned int *)s; - i[0] = ebx; - i[1] = ecx; - i[2] = edx; - - return !strncmp(s, "KVMKVMKVM", strlen("KVMKVMKVM")); -} - -/** - * NOTE: - * It's actually impossible to check if we are running in KVM host, - * since the "KVM host" is simply native. So we only dectect guest here. - */ -static int kvmgt_detect_host(void) -{ -#ifdef CONFIG_INTEL_IOMMU - if (intel_iommu_gfx_mapped) { - gvt_err("Hardware IOMMU compatibility not yet supported, try to boot with intel_iommu=igfx_off\n"); - return -ENODEV; - } -#endif - return kvmgt_check_guest() ? -ENODEV : 0; -} - static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu, struct kvm *kvm) { struct intel_vgpu *itr; @@ -1390,7 +1380,7 @@ static int kvmgt_inject_msi(unsigned long handle, u32 addr, u16 data) static unsigned long kvmgt_gfn_to_pfn(unsigned long handle, unsigned long gfn) { - unsigned long pfn; + unsigned long iova, pfn; struct kvmgt_guest_info *info; struct device *dev; int rc; @@ -1399,9 +1389,9 @@ static unsigned long kvmgt_gfn_to_pfn(unsigned long handle, unsigned long gfn) return INTEL_GVT_INVALID_ADDR; info = (struct kvmgt_guest_info *)handle; - pfn = gvt_cache_find(info->vgpu, gfn); - if (pfn != 0) - return pfn; + iova = gvt_cache_find(info->vgpu, gfn); + if (iova != INTEL_GVT_INVALID_ADDR) + return iova; pfn = INTEL_GVT_INVALID_ADDR; dev = mdev_dev(info->vgpu->vdev.mdev); @@ -1410,9 +1400,16 @@ static unsigned long kvmgt_gfn_to_pfn(unsigned long handle, unsigned long gfn) gvt_err("vfio_pin_pages failed for gfn 0x%lx: %d\n", gfn, rc); return INTEL_GVT_INVALID_ADDR; } + /* transfer to host iova for GFX to use DMA */ + rc = gvt_dma_map_iova(info->vgpu, pfn, &iova); + if (rc) { + gvt_err("gvt_dma_map_iova failed for gfn: 0x%lx\n", gfn); + vfio_unpin_pages(dev, &gfn, 1); + return INTEL_GVT_INVALID_ADDR; + } - gvt_cache_add(info->vgpu, gfn, pfn); - return pfn; + gvt_cache_add(info->vgpu, gfn, iova); + return iova; } static int kvmgt_rw_gpa(unsigned long handle, unsigned long gpa, @@ -1459,7 +1456,6 @@ static unsigned long kvmgt_virt_to_pfn(void *addr) } struct intel_gvt_mpt kvmgt_mpt = { - .detect_host = kvmgt_detect_host, .host_init = kvmgt_host_init, .host_exit = kvmgt_host_exit, .attach_vgpu = kvmgt_attach_vgpu, diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index 1af5830c0a56..419353624c5a 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -44,18 +44,6 @@ */ /** - * intel_gvt_hypervisor_detect_host - check if GVT-g is running within - * hypervisor host/privilged domain - * - * Returns: - * Zero on success, -ENODEV if current kernel is running inside a VM - */ -static inline int intel_gvt_hypervisor_detect_host(void) -{ - return intel_gvt_host.mpt->detect_host(); -} - -/** * intel_gvt_hypervisor_host_init - init GVT-g host side * * Returns: diff --git a/drivers/gpu/drm/i915/gvt/render.c b/drivers/gpu/drm/i915/gvt/render.c index 44136b1f3aab..2b3a642284b6 100644 --- a/drivers/gpu/drm/i915/gvt/render.c +++ b/drivers/gpu/drm/i915/gvt/render.c @@ -236,12 +236,18 @@ static void restore_mocs(struct intel_vgpu *vgpu, int ring_id) } } +#define CTX_CONTEXT_CONTROL_VAL 0x03 + void intel_gvt_load_render_mmio(struct intel_vgpu *vgpu, int ring_id) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; struct render_mmio *mmio; u32 v; int i, array_size; + u32 *reg_state = vgpu->shadow_ctx->engine[ring_id].lrc_reg_state; + u32 ctx_ctrl = reg_state[CTX_CONTEXT_CONTROL_VAL]; + u32 inhibit_mask = + _MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT); if (IS_SKYLAKE(vgpu->gvt->dev_priv)) { mmio = gen9_render_mmio_list; @@ -257,6 +263,17 @@ void intel_gvt_load_render_mmio(struct intel_vgpu *vgpu, int ring_id) continue; mmio->value = I915_READ(mmio->reg); + + /* + * if it is an inhibit context, load in_context mmio + * into HW by mmio write. If it is not, skip this mmio + * write. + */ + if (mmio->in_context && + ((ctx_ctrl & inhibit_mask) != inhibit_mask) && + i915.enable_execlists) + continue; + if (mmio->mask) v = vgpu_vreg(vgpu, mmio->reg) | (mmio->mask << 16); else diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c b/drivers/gpu/drm/i915/gvt/sched_policy.c index 678b0be85376..06c9584ac5f0 100644 --- a/drivers/gpu/drm/i915/gvt/sched_policy.c +++ b/drivers/gpu/drm/i915/gvt/sched_policy.c @@ -125,7 +125,6 @@ static void tbs_sched_func(struct work_struct *work) vgpu_data = scheduler->current_vgpu->sched_data; head = &vgpu_data->list; } else { - gvt_dbg_sched("no current vgpu search from q head\n"); head = &sched_data->runq_head; } diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index e91885dffeff..d6b6d0efdd1a 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -169,7 +169,8 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) gvt_dbg_sched("ring id %d prepare to dispatch workload %p\n", ring_id, workload); - shadow_ctx->desc_template = workload->ctx_desc.addressing_mode << + shadow_ctx->desc_template &= ~(0x3 << GEN8_CTX_ADDRESSING_MODE_SHIFT); + shadow_ctx->desc_template |= workload->ctx_desc.addressing_mode << GEN8_CTX_ADDRESSING_MODE_SHIFT; mutex_lock(&dev_priv->drm.struct_mutex); @@ -456,7 +457,7 @@ static int workload_thread(void *priv) } complete: - gvt_dbg_sched("will complete workload %p\n, status: %d\n", + gvt_dbg_sched("will complete workload %p, status: %d\n", workload, workload->status); if (workload->req) @@ -549,18 +550,10 @@ err: void intel_vgpu_clean_gvt_context(struct intel_vgpu *vgpu) { - struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; - atomic_notifier_chain_unregister(&vgpu->shadow_ctx->status_notifier, &vgpu->shadow_ctx_notifier_block); - mutex_lock(&dev_priv->drm.struct_mutex); - - /* a little hacky to mark as ctx closed */ - vgpu->shadow_ctx->closed = true; - i915_gem_context_put(vgpu->shadow_ctx); - - mutex_unlock(&dev_priv->drm.struct_mutex); + i915_gem_context_put_unlocked(vgpu->shadow_ctx); } int intel_vgpu_init_gvt_context(struct intel_vgpu *vgpu) diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 7295bc8e12fb..95a97aa0051e 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -74,7 +74,7 @@ void populate_pvinfo_page(struct intel_vgpu *vgpu) int intel_gvt_init_vgpu_types(struct intel_gvt *gvt) { unsigned int num_types; - unsigned int i, low_avail; + unsigned int i, low_avail, high_avail; unsigned int min_low; /* vGPU type name is defined as GVTg_Vx_y which contains @@ -89,9 +89,9 @@ int intel_gvt_init_vgpu_types(struct intel_gvt *gvt) * to indicate how many vGPU instance can be created for this * type. * - * Currently use static size here as we init type earlier.. */ - low_avail = MB_TO_BYTES(256) - HOST_LOW_GM_SIZE; + low_avail = gvt_aperture_sz(gvt) - HOST_LOW_GM_SIZE; + high_avail = gvt_hidden_sz(gvt) - HOST_HIGH_GM_SIZE; num_types = 4; gvt->types = kzalloc(num_types * sizeof(struct intel_vgpu_type), @@ -106,7 +106,8 @@ int intel_gvt_init_vgpu_types(struct intel_gvt *gvt) gvt->types[i].low_gm_size = min_low; gvt->types[i].high_gm_size = max((min_low<<3), MB_TO_BYTES(384U)); gvt->types[i].fence = 4; - gvt->types[i].max_instance = low_avail / min_low; + gvt->types[i].max_instance = min(low_avail / min_low, + high_avail / gvt->types[i].high_gm_size); gvt->types[i].avail_instance = gvt->types[i].max_instance; if (IS_GEN8(gvt->dev_priv)) @@ -142,9 +143,9 @@ static void intel_gvt_update_vgpu_types(struct intel_gvt *gvt) /* Need to depend on maxium hw resource size but keep on * static config for now. */ - low_gm_avail = MB_TO_BYTES(256) - HOST_LOW_GM_SIZE - + low_gm_avail = gvt_aperture_sz(gvt) - HOST_LOW_GM_SIZE - gvt->gm.vgpu_allocated_low_gm_size; - high_gm_avail = MB_TO_BYTES(256) * 8UL - HOST_HIGH_GM_SIZE - + high_gm_avail = gvt_hidden_sz(gvt) - HOST_HIGH_GM_SIZE - gvt->gm.vgpu_allocated_high_gm_size; fence_avail = gvt_fence_sz(gvt) - HOST_FENCE - gvt->fence.vgpu_allocated_fence_num; @@ -384,6 +385,7 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, intel_vgpu_reset_resource(vgpu); intel_vgpu_reset_mmio(vgpu); populate_pvinfo_page(vgpu); + intel_vgpu_reset_display(vgpu); if (dmlr) intel_vgpu_reset_cfg_space(vgpu); diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index f5039f4f988f..21b1cd917d81 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -86,6 +86,102 @@ * general bitmasking mechanism. */ +/* + * A command that requires special handling by the command parser. + */ +struct drm_i915_cmd_descriptor { + /* + * Flags describing how the command parser processes the command. + * + * CMD_DESC_FIXED: The command has a fixed length if this is set, + * a length mask if not set + * CMD_DESC_SKIP: The command is allowed but does not follow the + * standard length encoding for the opcode range in + * which it falls + * CMD_DESC_REJECT: The command is never allowed + * CMD_DESC_REGISTER: The command should be checked against the + * register whitelist for the appropriate ring + * CMD_DESC_MASTER: The command is allowed if the submitting process + * is the DRM master + */ + u32 flags; +#define CMD_DESC_FIXED (1<<0) +#define CMD_DESC_SKIP (1<<1) +#define CMD_DESC_REJECT (1<<2) +#define CMD_DESC_REGISTER (1<<3) +#define CMD_DESC_BITMASK (1<<4) +#define CMD_DESC_MASTER (1<<5) + + /* + * The command's unique identification bits and the bitmask to get them. + * This isn't strictly the opcode field as defined in the spec and may + * also include type, subtype, and/or subop fields. + */ + struct { + u32 value; + u32 mask; + } cmd; + + /* + * The command's length. The command is either fixed length (i.e. does + * not include a length field) or has a length field mask. The flag + * CMD_DESC_FIXED indicates a fixed length. Otherwise, the command has + * a length mask. All command entries in a command table must include + * length information. + */ + union { + u32 fixed; + u32 mask; + } length; + + /* + * Describes where to find a register address in the command to check + * against the ring's register whitelist. Only valid if flags has the + * CMD_DESC_REGISTER bit set. + * + * A non-zero step value implies that the command may access multiple + * registers in sequence (e.g. LRI), in that case step gives the + * distance in dwords between individual offset fields. + */ + struct { + u32 offset; + u32 mask; + u32 step; + } reg; + +#define MAX_CMD_DESC_BITMASKS 3 + /* + * Describes command checks where a particular dword is masked and + * compared against an expected value. If the command does not match + * the expected value, the parser rejects it. Only valid if flags has + * the CMD_DESC_BITMASK bit set. Only entries where mask is non-zero + * are valid. + * + * If the check specifies a non-zero condition_mask then the parser + * only performs the check when the bits specified by condition_mask + * are non-zero. + */ + struct { + u32 offset; + u32 mask; + u32 expected; + u32 condition_offset; + u32 condition_mask; + } bits[MAX_CMD_DESC_BITMASKS]; +}; + +/* + * A table of commands requiring special handling by the command parser. + * + * Each engine has an array of tables. Each table consists of an array of + * command descriptors, which must be sorted with command opcodes in + * ascending order. + */ +struct drm_i915_cmd_table { + const struct drm_i915_cmd_descriptor *table; + int count; +}; + #define STD_MI_OPCODE_SHIFT (32 - 9) #define STD_3D_OPCODE_SHIFT (32 - 16) #define STD_2D_OPCODE_SHIFT (32 - 10) @@ -450,7 +546,6 @@ static const struct drm_i915_reg_descriptor gen7_render_regs[] = { REG64(PS_INVOCATION_COUNT), REG64(PS_DEPTH_COUNT), REG64_IDX(RING_TIMESTAMP, RENDER_RING_BASE), - REG32(OACONTROL), /* Only allowed for LRI and SRM. See below. */ REG64(MI_PREDICATE_SRC0), REG64(MI_PREDICATE_SRC1), REG32(GEN7_3DPRIM_END_OFFSET), @@ -559,7 +654,7 @@ static const struct drm_i915_reg_table hsw_blt_reg_tables[] = { static u32 gen7_render_get_cmd_length_mask(u32 cmd_header) { - u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT; + u32 client = cmd_header >> INSTR_CLIENT_SHIFT; u32 subclient = (cmd_header & INSTR_SUBCLIENT_MASK) >> INSTR_SUBCLIENT_SHIFT; @@ -578,7 +673,7 @@ static u32 gen7_render_get_cmd_length_mask(u32 cmd_header) static u32 gen7_bsd_get_cmd_length_mask(u32 cmd_header) { - u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT; + u32 client = cmd_header >> INSTR_CLIENT_SHIFT; u32 subclient = (cmd_header & INSTR_SUBCLIENT_MASK) >> INSTR_SUBCLIENT_SHIFT; u32 op = (cmd_header & INSTR_26_TO_24_MASK) >> INSTR_26_TO_24_SHIFT; @@ -601,7 +696,7 @@ static u32 gen7_bsd_get_cmd_length_mask(u32 cmd_header) static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header) { - u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT; + u32 client = cmd_header >> INSTR_CLIENT_SHIFT; if (client == INSTR_MI_CLIENT) return 0x3F; @@ -984,7 +1079,7 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj, src = ERR_PTR(-ENODEV); if (src_needs_clflush && - i915_memcpy_from_wc((void *)(uintptr_t)batch_start_offset, NULL, 0)) { + i915_can_memcpy_from_wc(NULL, batch_start_offset, 0)) { src = i915_gem_object_pin_map(src_obj, I915_MAP_WC); if (!IS_ERR(src)) { i915_memcpy_from_wc(dst, @@ -1036,32 +1131,10 @@ unpin_src: return dst; } -/** - * intel_engine_needs_cmd_parser() - should a given engine use software - * command parsing? - * @engine: the engine in question - * - * Only certain platforms require software batch buffer command parsing, and - * only when enabled via module parameter. - * - * Return: true if the engine requires software command parsing - */ -bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine) -{ - if (!engine->needs_cmd_parser) - return false; - - if (!USES_PPGTT(engine->i915)) - return false; - - return (i915.enable_cmd_parser == 1); -} - static bool check_cmd(const struct intel_engine_cs *engine, const struct drm_i915_cmd_descriptor *desc, const u32 *cmd, u32 length, - const bool is_master, - bool *oacontrol_set) + const bool is_master) { if (desc->flags & CMD_DESC_SKIP) return true; @@ -1099,31 +1172,6 @@ static bool check_cmd(const struct intel_engine_cs *engine, } /* - * OACONTROL requires some special handling for - * writes. We want to make sure that any batch which - * enables OA also disables it before the end of the - * batch. The goal is to prevent one process from - * snooping on the perf data from another process. To do - * that, we need to check the value that will be written - * to the register. Hence, limit OACONTROL writes to - * only MI_LOAD_REGISTER_IMM commands. - */ - if (reg_addr == i915_mmio_reg_offset(OACONTROL)) { - if (desc->cmd.value == MI_LOAD_REGISTER_MEM) { - DRM_DEBUG_DRIVER("CMD: Rejected LRM to OACONTROL\n"); - return false; - } - - if (desc->cmd.value == MI_LOAD_REGISTER_REG) { - DRM_DEBUG_DRIVER("CMD: Rejected LRR to OACONTROL\n"); - return false; - } - - if (desc->cmd.value == MI_LOAD_REGISTER_IMM(1)) - *oacontrol_set = (cmd[offset + 1] != 0); - } - - /* * Check the value written to the register against the * allowed mask/value pair given in the whitelist entry. */ @@ -1214,7 +1262,6 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine, u32 *cmd, *batch_end; struct drm_i915_cmd_descriptor default_desc = noop_desc; const struct drm_i915_cmd_descriptor *desc = &default_desc; - bool oacontrol_set = false; /* OACONTROL tracking. See check_cmd() */ bool needs_clflush_after = false; int ret = 0; @@ -1270,20 +1317,14 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine, break; } - if (!check_cmd(engine, desc, cmd, length, is_master, - &oacontrol_set)) { - ret = -EINVAL; + if (!check_cmd(engine, desc, cmd, length, is_master)) { + ret = -EACCES; break; } cmd += length; } - if (oacontrol_set) { - DRM_DEBUG_DRIVER("CMD: batch set OACONTROL but did not clear it\n"); - ret = -EINVAL; - } - if (cmd >= batch_end) { DRM_DEBUG_DRIVER("CMD: Got to the end of the buffer w/o a BBE cmd!\n"); ret = -EINVAL; @@ -1313,7 +1354,7 @@ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv) /* If the command parser is not enabled, report 0 - unsupported */ for_each_engine(engine, dev_priv, id) { - if (intel_engine_needs_cmd_parser(engine)) { + if (engine->needs_cmd_parser) { active = true; break; } @@ -1333,6 +1374,11 @@ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv) * 5. GPGPU dispatch compute indirect registers. * 6. TIMESTAMP register and Haswell CS GPR registers * 7. Allow MI_LOAD_REGISTER_REG between whitelisted registers. + * 8. Don't report cmd_check() failures as EINVAL errors to userspace; + * rely on the HW to NOOP disallowed commands as it would without + * the parser enabled. + * 9. Don't whitelist or handle oacontrol specially, as ownership + * for oacontrol state is moving to i915-perf. */ - return 7; + return 9; } diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 791bfc760075..fa69d72fdcb9 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -26,19 +26,9 @@ * */ -#include <linux/seq_file.h> -#include <linux/circ_buf.h> -#include <linux/ctype.h> #include <linux/debugfs.h> -#include <linux/slab.h> -#include <linux/export.h> #include <linux/list_sort.h> -#include <asm/msr-index.h> -#include <drm/drmP.h> #include "intel_drv.h" -#include "intel_ringbuffer.h" -#include <drm/i915_drm.h> -#include "i915_drv.h" static inline struct drm_i915_private *node_to_i915(struct drm_info_node *node) { @@ -77,6 +67,7 @@ static int i915_capabilities(struct seq_file *m, void *data) const struct intel_device_info *info = INTEL_INFO(dev_priv); seq_printf(m, "gen: %d\n", INTEL_GEN(dev_priv)); + seq_printf(m, "platform: %s\n", intel_platform_name(info->platform)); seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev_priv)); #define PRINT_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x)) DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG); @@ -168,8 +159,35 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) seq_printf(m, " (%sgtt offset: %08llx, size: %08llx", i915_vma_is_ggtt(vma) ? "g" : "pp", vma->node.start, vma->node.size); - if (i915_vma_is_ggtt(vma)) - seq_printf(m, ", type: %u", vma->ggtt_view.type); + if (i915_vma_is_ggtt(vma)) { + switch (vma->ggtt_view.type) { + case I915_GGTT_VIEW_NORMAL: + seq_puts(m, ", normal"); + break; + + case I915_GGTT_VIEW_PARTIAL: + seq_printf(m, ", partial [%08llx+%x]", + vma->ggtt_view.partial.offset << PAGE_SHIFT, + vma->ggtt_view.partial.size << PAGE_SHIFT); + break; + + case I915_GGTT_VIEW_ROTATED: + seq_printf(m, ", rotated [(%ux%u, stride=%u, offset=%u), (%ux%u, stride=%u, offset=%u)]", + vma->ggtt_view.rotated.plane[0].width, + vma->ggtt_view.rotated.plane[0].height, + vma->ggtt_view.rotated.plane[0].stride, + vma->ggtt_view.rotated.plane[0].offset, + vma->ggtt_view.rotated.plane[1].width, + vma->ggtt_view.rotated.plane[1].height, + vma->ggtt_view.rotated.plane[1].stride, + vma->ggtt_view.rotated.plane[1].offset); + break; + + default: + MISSING_CASE(vma->ggtt_view.type); + break; + } + } if (vma->fence) seq_printf(m, " , fence: %d%s", vma->fence->id, @@ -549,10 +567,10 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) if (work->flip_queued_req) { struct intel_engine_cs *engine = work->flip_queued_req->engine; - seq_printf(m, "Flip queued on %s at seqno %x, next seqno %x [current breadcrumb %x], completed? %d\n", + seq_printf(m, "Flip queued on %s at seqno %x, last submitted seqno %x [current breadcrumb %x], completed? %d\n", engine->name, work->flip_queued_req->global_seqno, - atomic_read(&dev_priv->gt.global_timeline.next_seqno), + intel_engine_last_submit(engine), intel_engine_get_seqno(engine), i915_gem_request_completed(work->flip_queued_req)); } else @@ -686,7 +704,7 @@ static void i915_ring_seqno_info(struct seq_file *m, spin_lock_irq(&b->lock); for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) { - struct intel_wait *w = container_of(rb, typeof(*w), node); + struct intel_wait *w = rb_entry(rb, typeof(*w), node); seq_printf(m, "Waiting (%s): %s [%d] on %x\n", engine->name, w->tsk->comm, w->tsk->pid, w->seqno); @@ -946,7 +964,7 @@ i915_error_state_write(struct file *filp, struct i915_error_state_file_priv *error_priv = filp->private_data; DRM_DEBUG_DRIVER("Resetting error state\n"); - i915_destroy_error_state(error_priv->dev); + i915_destroy_error_state(error_priv->i915); return cnt; } @@ -960,7 +978,7 @@ static int i915_error_state_open(struct inode *inode, struct file *file) if (!error_priv) return -ENOMEM; - error_priv->dev = &dev_priv->drm; + error_priv->i915 = dev_priv; i915_error_state_get(&dev_priv->drm, error_priv); @@ -988,8 +1006,8 @@ static ssize_t i915_error_state_read(struct file *file, char __user *userbuf, ssize_t ret_count = 0; int ret; - ret = i915_error_state_buf_init(&error_str, - to_i915(error_priv->dev), count, *pos); + ret = i915_error_state_buf_init(&error_str, error_priv->i915, + count, *pos); if (ret) return ret; @@ -1026,7 +1044,7 @@ i915_next_seqno_get(void *data, u64 *val) { struct drm_i915_private *dev_priv = data; - *val = 1 + atomic_read(&dev_priv->gt.global_timeline.next_seqno); + *val = 1 + atomic_read(&dev_priv->gt.global_timeline.seqno); return 0; } @@ -1108,7 +1126,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) int max_freq; rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS); - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { rp_state_cap = I915_READ(BXT_RP_STATE_CAP); gt_perf_status = I915_READ(BXT_GT_PERF_STATUS); } else { @@ -1204,7 +1222,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "Down threshold: %d%%\n", dev_priv->rps.down_threshold); - max_freq = (IS_BROXTON(dev_priv) ? rp_state_cap >> 0 : + max_freq = (IS_GEN9_LP(dev_priv) ? rp_state_cap >> 0 : rp_state_cap >> 16) & 0xff; max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1); @@ -1217,7 +1235,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) seq_printf(m, "Nominal (RP1) frequency: %dMHz\n", intel_gpu_freq(dev_priv, max_freq)); - max_freq = (IS_BROXTON(dev_priv) ? rp_state_cap >> 16 : + max_freq = (IS_GEN9_LP(dev_priv) ? rp_state_cap >> 16 : rp_state_cap >> 0) & 0xff; max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GEN9_FREQ_SCALER : 1); @@ -1330,13 +1348,15 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused) seq_printf(m, "\tseqno = %x [current %x, last %x]\n", engine->hangcheck.seqno, seqno[id], intel_engine_last_submit(engine)); - seq_printf(m, "\twaiters? %s, fake irq active? %s\n", + seq_printf(m, "\twaiters? %s, fake irq active? %s, stalled? %s\n", yesno(intel_engine_has_waiter(engine)), yesno(test_bit(engine->id, - &dev_priv->gpu_error.missed_irq_rings))); + &dev_priv->gpu_error.missed_irq_rings)), + yesno(engine->hangcheck.stalled)); + spin_lock_irq(&b->lock); for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) { - struct intel_wait *w = container_of(rb, typeof(*w), node); + struct intel_wait *w = rb_entry(rb, typeof(*w), node); seq_printf(m, "\t%s [%d] waiting for %x\n", w->tsk->comm, w->tsk->pid, w->seqno); @@ -1346,8 +1366,11 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused) seq_printf(m, "\tACTHD = 0x%08llx [current 0x%08llx]\n", (long long)engine->hangcheck.acthd, (long long)acthd[id]); - seq_printf(m, "\tscore = %d\n", engine->hangcheck.score); - seq_printf(m, "\taction = %d\n", engine->hangcheck.action); + seq_printf(m, "\taction = %s(%d) %d ms ago\n", + hangcheck_action_to_str(engine->hangcheck.action), + engine->hangcheck.action, + jiffies_to_msecs(jiffies - + engine->hangcheck.action_timestamp)); if (engine->id == RCS) { seq_puts(m, "\tinstdone read =\n"); @@ -1728,7 +1751,7 @@ static int i915_sr_status(struct seq_file *m, void *unused) if (HAS_PCH_SPLIT(dev_priv)) sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN; - else if (IS_CRESTLINE(dev_priv) || IS_G4X(dev_priv) || + else if (IS_I965GM(dev_priv) || IS_G4X(dev_priv) || IS_I945G(dev_priv) || IS_I945GM(dev_priv)) sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; else if (IS_I915GM(dev_priv)) @@ -1873,8 +1896,8 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ", fbdev_fb->base.width, fbdev_fb->base.height, - fbdev_fb->base.depth, - fbdev_fb->base.bits_per_pixel, + fbdev_fb->base.format->depth, + fbdev_fb->base.format->cpp[0] * 8, fbdev_fb->base.modifier, drm_framebuffer_read_refcount(&fbdev_fb->base)); describe_obj(m, fbdev_fb->obj); @@ -1891,8 +1914,8 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) seq_printf(m, "user size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ", fb->base.width, fb->base.height, - fb->base.depth, - fb->base.bits_per_pixel, + fb->base.format->depth, + fb->base.format->cpp[0] * 8, fb->base.modifier, drm_framebuffer_read_refcount(&fb->base)); describe_obj(m, fb->obj); @@ -2329,10 +2352,40 @@ static int i915_llc(struct seq_file *m, void *data) return 0; } +static int i915_huc_load_status_info(struct seq_file *m, void *data) +{ + struct drm_i915_private *dev_priv = node_to_i915(m->private); + struct intel_uc_fw *huc_fw = &dev_priv->huc.fw; + + if (!HAS_HUC_UCODE(dev_priv)) + return 0; + + seq_puts(m, "HuC firmware status:\n"); + seq_printf(m, "\tpath: %s\n", huc_fw->path); + seq_printf(m, "\tfetch: %s\n", + intel_uc_fw_status_repr(huc_fw->fetch_status)); + seq_printf(m, "\tload: %s\n", + intel_uc_fw_status_repr(huc_fw->load_status)); + seq_printf(m, "\tversion wanted: %d.%d\n", + huc_fw->major_ver_wanted, huc_fw->minor_ver_wanted); + seq_printf(m, "\tversion found: %d.%d\n", + huc_fw->major_ver_found, huc_fw->minor_ver_found); + seq_printf(m, "\theader: offset is %d; size = %d\n", + huc_fw->header_offset, huc_fw->header_size); + seq_printf(m, "\tuCode: offset is %d; size = %d\n", + huc_fw->ucode_offset, huc_fw->ucode_size); + seq_printf(m, "\tRSA: offset is %d; size = %d\n", + huc_fw->rsa_offset, huc_fw->rsa_size); + + seq_printf(m, "\nHuC status 0x%08x:\n", I915_READ(HUC_STATUS2)); + + return 0; +} + static int i915_guc_load_status_info(struct seq_file *m, void *data) { struct drm_i915_private *dev_priv = node_to_i915(m->private); - struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; u32 tmp, i; if (!HAS_GUC_UCODE(dev_priv)) @@ -2340,15 +2393,15 @@ static int i915_guc_load_status_info(struct seq_file *m, void *data) seq_printf(m, "GuC firmware status:\n"); seq_printf(m, "\tpath: %s\n", - guc_fw->guc_fw_path); + guc_fw->path); seq_printf(m, "\tfetch: %s\n", - intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status)); + intel_uc_fw_status_repr(guc_fw->fetch_status)); seq_printf(m, "\tload: %s\n", - intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + intel_uc_fw_status_repr(guc_fw->load_status)); seq_printf(m, "\tversion wanted: %d.%d\n", - guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted); + guc_fw->major_ver_wanted, guc_fw->minor_ver_wanted); seq_printf(m, "\tversion found: %d.%d\n", - guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found); + guc_fw->major_ver_found, guc_fw->minor_ver_found); seq_printf(m, "\theader: offset is %d; size = %d\n", guc_fw->header_offset, guc_fw->header_size); seq_printf(m, "\tuCode: offset is %d; size = %d\n", @@ -2409,7 +2462,7 @@ static void i915_guc_client_info(struct seq_file *m, seq_printf(m, "\tPriority %d, GuC ctx index: %u, PD offset 0x%x\n", client->priority, client->ctx_index, client->proc_desc_offset); seq_printf(m, "\tDoorbell id %d, offset: 0x%x, cookie 0x%x\n", - client->doorbell_id, client->doorbell_offset, client->cookie); + client->doorbell_id, client->doorbell_offset, client->doorbell_cookie); seq_printf(m, "\tWQ size %d, offset: 0x%x, tail %d\n", client->wq_size, client->wq_offset, client->wq_tail); @@ -2429,47 +2482,41 @@ static void i915_guc_client_info(struct seq_file *m, static int i915_guc_info(struct seq_file *m, void *data) { struct drm_i915_private *dev_priv = node_to_i915(m->private); - struct drm_device *dev = &dev_priv->drm; - struct intel_guc guc; - struct i915_guc_client client = {}; + const struct intel_guc *guc = &dev_priv->guc; struct intel_engine_cs *engine; enum intel_engine_id id; - u64 total = 0; + u64 total; - if (!HAS_GUC_SCHED(dev_priv)) + if (!guc->execbuf_client) { + seq_printf(m, "GuC submission %s\n", + HAS_GUC_SCHED(dev_priv) ? + "disabled" : + "not supported"); return 0; - - if (mutex_lock_interruptible(&dev->struct_mutex)) - return 0; - - /* Take a local copy of the GuC data, so we can dump it at leisure */ - guc = dev_priv->guc; - if (guc.execbuf_client) - client = *guc.execbuf_client; - - mutex_unlock(&dev->struct_mutex); + } seq_printf(m, "Doorbell map:\n"); - seq_printf(m, "\t%*pb\n", GUC_MAX_DOORBELLS, guc.doorbell_bitmap); - seq_printf(m, "Doorbell next cacheline: 0x%x\n\n", guc.db_cacheline); + seq_printf(m, "\t%*pb\n", GUC_MAX_DOORBELLS, guc->doorbell_bitmap); + seq_printf(m, "Doorbell next cacheline: 0x%x\n\n", guc->db_cacheline); - seq_printf(m, "GuC total action count: %llu\n", guc.action_count); - seq_printf(m, "GuC action failure count: %u\n", guc.action_fail); - seq_printf(m, "GuC last action command: 0x%x\n", guc.action_cmd); - seq_printf(m, "GuC last action status: 0x%x\n", guc.action_status); - seq_printf(m, "GuC last action error code: %d\n", guc.action_err); + seq_printf(m, "GuC total action count: %llu\n", guc->action_count); + seq_printf(m, "GuC action failure count: %u\n", guc->action_fail); + seq_printf(m, "GuC last action command: 0x%x\n", guc->action_cmd); + seq_printf(m, "GuC last action status: 0x%x\n", guc->action_status); + seq_printf(m, "GuC last action error code: %d\n", guc->action_err); + total = 0; seq_printf(m, "\nGuC submissions:\n"); for_each_engine(engine, dev_priv, id) { - u64 submissions = guc.submissions[id]; + u64 submissions = guc->submissions[id]; total += submissions; seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x\n", - engine->name, submissions, guc.last_seqno[id]); + engine->name, submissions, guc->last_seqno[id]); } seq_printf(m, "\t%s: %llu\n", "Total", total); - seq_printf(m, "\nGuC execbuf client @ %p:\n", guc.execbuf_client); - i915_guc_client_info(m, dev_priv, &client); + seq_printf(m, "\nGuC execbuf client @ %p:\n", guc->execbuf_client); + i915_guc_client_info(m, dev_priv, guc->execbuf_client); i915_guc_log_info(m, dev_priv); @@ -2542,6 +2589,29 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_guc_log_control_fops, i915_guc_log_control_get, i915_guc_log_control_set, "%lld\n"); +static const char *psr2_live_status(u32 val) +{ + static const char * const live_status[] = { + "IDLE", + "CAPTURE", + "CAPTURE_FS", + "SLEEP", + "BUFON_FW", + "ML_UP", + "SU_STANDBY", + "FAST_SLEEP", + "DEEP_SLEEP", + "BUF_ON", + "TG_ON" + }; + + val = (val & EDP_PSR2_STATUS_STATE_MASK) >> EDP_PSR2_STATUS_STATE_SHIFT; + if (val < ARRAY_SIZE(live_status)) + return live_status[val]; + + return "unknown"; +} + static int i915_edp_psr_status(struct seq_file *m, void *data) { struct drm_i915_private *dev_priv = node_to_i915(m->private); @@ -2567,9 +2637,12 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) seq_printf(m, "Re-enable work scheduled: %s\n", yesno(work_busy(&dev_priv->psr.work.work))); - if (HAS_DDI(dev_priv)) - enabled = I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE; - else { + if (HAS_DDI(dev_priv)) { + if (dev_priv->psr.psr2_support) + enabled = I915_READ(EDP_PSR2_CTL) & EDP_PSR2_ENABLE; + else + enabled = I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE; + } else { for_each_pipe(dev_priv, pipe) { enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); @@ -2613,6 +2686,12 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) seq_printf(m, "Performance_Counter: %u\n", psrperf); } + if (dev_priv->psr.psr2_support) { + u32 psr2 = I915_READ(EDP_PSR2_STATUS_CTL); + + seq_printf(m, "EDP_PSR2_STATUS_CTL: %x [%s]\n", + psr2, psr2_live_status(psr2)); + } mutex_unlock(&dev_priv->psr.lock); intel_runtime_pm_put(dev_priv); @@ -2872,6 +2951,20 @@ static void intel_dp_info(struct seq_file *m, &intel_dp->aux); } +static void intel_dp_mst_info(struct seq_file *m, + struct intel_connector *intel_connector) +{ + struct intel_encoder *intel_encoder = intel_connector->encoder; + struct intel_dp_mst_encoder *intel_mst = + enc_to_mst(&intel_encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_dp *intel_dp = &intel_dig_port->dp; + bool has_audio = drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, + intel_connector->port); + + seq_printf(m, "\taudio support: %s\n", yesno(has_audio)); +} + static void intel_hdmi_info(struct seq_file *m, struct intel_connector *intel_connector) { @@ -2914,7 +3007,10 @@ static void intel_connector_info(struct seq_file *m, switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_eDP: - intel_dp_info(m, intel_connector); + if (intel_encoder->type == INTEL_OUTPUT_DP_MST) + intel_dp_mst_info(m, intel_connector); + else + intel_dp_info(m, intel_connector); break; case DRM_MODE_CONNECTOR_LVDS: if (intel_encoder->type == INTEL_OUTPUT_LVDS) @@ -2938,7 +3034,7 @@ static bool cursor_active(struct drm_i915_private *dev_priv, int pipe) { u32 state; - if (IS_845G(dev_priv) || IS_I865G(dev_priv)) + if (IS_I845G(dev_priv) || IS_I865G(dev_priv)) state = I915_READ(CURCNTR(PIPE_A)) & CURSOR_ENABLE; else state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE; @@ -3021,7 +3117,8 @@ static void intel_plane_info(struct seq_file *m, struct intel_crtc *intel_crtc) state = plane->state; if (state->fb) { - drm_get_format_name(state->fb->pixel_format, &format_name); + drm_get_format_name(state->fb->format->format, + &format_name); } else { sprintf(format_name.str, "N/A"); } @@ -3059,7 +3156,7 @@ static void intel_scaler_info(struct seq_file *m, struct intel_crtc *intel_crtc) pipe_config->scaler_state.scaler_users, pipe_config->scaler_state.scaler_id); - for (i = 0; i < SKL_NUM_SCALERS; i++) { + for (i = 0; i < num_scalers; i++) { struct intel_scaler *sc = &pipe_config->scaler_state.scalers[i]; @@ -3141,11 +3238,11 @@ static int i915_engine_info(struct seq_file *m, void *unused) u64 addr; seq_printf(m, "%s\n", engine->name); - seq_printf(m, "\tcurrent seqno %x, last %x, hangcheck %x [score %d]\n", + seq_printf(m, "\tcurrent seqno %x, last %x, hangcheck %x [%d ms]\n", intel_engine_get_seqno(engine), intel_engine_last_submit(engine), engine->hangcheck.seqno, - engine->hangcheck.score); + jiffies_to_msecs(jiffies - engine->hangcheck.action_timestamp)); rcu_read_lock(); @@ -3251,7 +3348,7 @@ static int i915_engine_info(struct seq_file *m, void *unused) spin_lock_irq(&b->lock); for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) { - struct intel_wait *w = container_of(rb, typeof(*w), node); + struct intel_wait *w = rb_entry(rb, typeof(*w), node); seq_printf(m, "\t%s [%d] waiting for %x\n", w->tsk->comm, w->tsk->pid, w->seqno); @@ -3341,14 +3438,14 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused) seq_printf(m, "DPLL%i: %s, id: %i\n", i, pll->name, pll->id); seq_printf(m, " crtc_mask: 0x%08x, active: 0x%x, on: %s\n", - pll->config.crtc_mask, pll->active_mask, yesno(pll->on)); + pll->state.crtc_mask, pll->active_mask, yesno(pll->on)); seq_printf(m, " tracked hardware state:\n"); - seq_printf(m, " dpll: 0x%08x\n", pll->config.hw_state.dpll); + seq_printf(m, " dpll: 0x%08x\n", pll->state.hw_state.dpll); seq_printf(m, " dpll_md: 0x%08x\n", - pll->config.hw_state.dpll_md); - seq_printf(m, " fp0: 0x%08x\n", pll->config.hw_state.fp0); - seq_printf(m, " fp1: 0x%08x\n", pll->config.hw_state.fp1); - seq_printf(m, " wrpll: 0x%08x\n", pll->config.hw_state.wrpll); + pll->state.hw_state.dpll_md); + seq_printf(m, " fp0: 0x%08x\n", pll->state.hw_state.fp0); + seq_printf(m, " fp1: 0x%08x\n", pll->state.hw_state.fp1); + seq_printf(m, " wrpll: 0x%08x\n", pll->state.hw_state.wrpll); } drm_modeset_unlock_all(dev); @@ -3526,12 +3623,6 @@ static int i915_drrs_status(struct seq_file *m, void *unused) return 0; } -struct pipe_crc_info { - const char *name; - struct drm_i915_private *dev_priv; - enum pipe pipe; -}; - static int i915_dp_mst_info(struct seq_file *m, void *unused) { struct drm_i915_private *dev_priv = node_to_i915(m->private); @@ -3561,844 +3652,6 @@ static int i915_dp_mst_info(struct seq_file *m, void *unused) return 0; } -static int i915_pipe_crc_open(struct inode *inode, struct file *filep) -{ - struct pipe_crc_info *info = inode->i_private; - struct drm_i915_private *dev_priv = info->dev_priv; - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; - - if (info->pipe >= INTEL_INFO(dev_priv)->num_pipes) - return -ENODEV; - - spin_lock_irq(&pipe_crc->lock); - - if (pipe_crc->opened) { - spin_unlock_irq(&pipe_crc->lock); - return -EBUSY; /* already open */ - } - - pipe_crc->opened = true; - filep->private_data = inode->i_private; - - spin_unlock_irq(&pipe_crc->lock); - - return 0; -} - -static int i915_pipe_crc_release(struct inode *inode, struct file *filep) -{ - struct pipe_crc_info *info = inode->i_private; - struct drm_i915_private *dev_priv = info->dev_priv; - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; - - spin_lock_irq(&pipe_crc->lock); - pipe_crc->opened = false; - spin_unlock_irq(&pipe_crc->lock); - - return 0; -} - -/* (6 fields, 8 chars each, space separated (5) + '\n') */ -#define PIPE_CRC_LINE_LEN (6 * 8 + 5 + 1) -/* account for \'0' */ -#define PIPE_CRC_BUFFER_LEN (PIPE_CRC_LINE_LEN + 1) - -static int pipe_crc_data_count(struct intel_pipe_crc *pipe_crc) -{ - assert_spin_locked(&pipe_crc->lock); - return CIRC_CNT(pipe_crc->head, pipe_crc->tail, - INTEL_PIPE_CRC_ENTRIES_NR); -} - -static ssize_t -i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count, - loff_t *pos) -{ - struct pipe_crc_info *info = filep->private_data; - struct drm_i915_private *dev_priv = info->dev_priv; - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; - char buf[PIPE_CRC_BUFFER_LEN]; - int n_entries; - ssize_t bytes_read; - - /* - * Don't allow user space to provide buffers not big enough to hold - * a line of data. - */ - if (count < PIPE_CRC_LINE_LEN) - return -EINVAL; - - if (pipe_crc->source == INTEL_PIPE_CRC_SOURCE_NONE) - return 0; - - /* nothing to read */ - spin_lock_irq(&pipe_crc->lock); - while (pipe_crc_data_count(pipe_crc) == 0) { - int ret; - - if (filep->f_flags & O_NONBLOCK) { - spin_unlock_irq(&pipe_crc->lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_lock_irq(pipe_crc->wq, - pipe_crc_data_count(pipe_crc), pipe_crc->lock); - if (ret) { - spin_unlock_irq(&pipe_crc->lock); - return ret; - } - } - - /* We now have one or more entries to read */ - n_entries = count / PIPE_CRC_LINE_LEN; - - bytes_read = 0; - while (n_entries > 0) { - struct intel_pipe_crc_entry *entry = - &pipe_crc->entries[pipe_crc->tail]; - - if (CIRC_CNT(pipe_crc->head, pipe_crc->tail, - INTEL_PIPE_CRC_ENTRIES_NR) < 1) - break; - - BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR); - pipe_crc->tail = (pipe_crc->tail + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); - - bytes_read += snprintf(buf, PIPE_CRC_BUFFER_LEN, - "%8u %8x %8x %8x %8x %8x\n", - entry->frame, entry->crc[0], - entry->crc[1], entry->crc[2], - entry->crc[3], entry->crc[4]); - - spin_unlock_irq(&pipe_crc->lock); - - if (copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN)) - return -EFAULT; - - user_buf += PIPE_CRC_LINE_LEN; - n_entries--; - - spin_lock_irq(&pipe_crc->lock); - } - - spin_unlock_irq(&pipe_crc->lock); - - return bytes_read; -} - -static const struct file_operations i915_pipe_crc_fops = { - .owner = THIS_MODULE, - .open = i915_pipe_crc_open, - .read = i915_pipe_crc_read, - .release = i915_pipe_crc_release, -}; - -static struct pipe_crc_info i915_pipe_crc_data[I915_MAX_PIPES] = { - { - .name = "i915_pipe_A_crc", - .pipe = PIPE_A, - }, - { - .name = "i915_pipe_B_crc", - .pipe = PIPE_B, - }, - { - .name = "i915_pipe_C_crc", - .pipe = PIPE_C, - }, -}; - -static int i915_pipe_crc_create(struct dentry *root, struct drm_minor *minor, - enum pipe pipe) -{ - struct drm_i915_private *dev_priv = to_i915(minor->dev); - struct dentry *ent; - struct pipe_crc_info *info = &i915_pipe_crc_data[pipe]; - - info->dev_priv = dev_priv; - ent = debugfs_create_file(info->name, S_IRUGO, root, info, - &i915_pipe_crc_fops); - if (!ent) - return -ENOMEM; - - return drm_add_fake_info_node(minor, ent, info); -} - -static const char * const pipe_crc_sources[] = { - "none", - "plane1", - "plane2", - "pf", - "pipe", - "TV", - "DP-B", - "DP-C", - "DP-D", - "auto", -}; - -static const char *pipe_crc_source_name(enum intel_pipe_crc_source source) -{ - BUILD_BUG_ON(ARRAY_SIZE(pipe_crc_sources) != INTEL_PIPE_CRC_SOURCE_MAX); - return pipe_crc_sources[source]; -} - -static int display_crc_ctl_show(struct seq_file *m, void *data) -{ - struct drm_i915_private *dev_priv = m->private; - int i; - - for (i = 0; i < I915_MAX_PIPES; i++) - seq_printf(m, "%c %s\n", pipe_name(i), - pipe_crc_source_name(dev_priv->pipe_crc[i].source)); - - return 0; -} - -static int display_crc_ctl_open(struct inode *inode, struct file *file) -{ - return single_open(file, display_crc_ctl_show, inode->i_private); -} - -static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, - uint32_t *val) -{ - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) - *source = INTEL_PIPE_CRC_SOURCE_PIPE; - - switch (*source) { - case INTEL_PIPE_CRC_SOURCE_PIPE: - *val = PIPE_CRC_ENABLE | PIPE_CRC_INCLUDE_BORDER_I8XX; - break; - case INTEL_PIPE_CRC_SOURCE_NONE: - *val = 0; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int i9xx_pipe_crc_auto_source(struct drm_i915_private *dev_priv, - enum pipe pipe, - enum intel_pipe_crc_source *source) -{ - struct drm_device *dev = &dev_priv->drm; - struct intel_encoder *encoder; - struct intel_crtc *crtc; - struct intel_digital_port *dig_port; - int ret = 0; - - *source = INTEL_PIPE_CRC_SOURCE_PIPE; - - drm_modeset_lock_all(dev); - for_each_intel_encoder(dev, encoder) { - if (!encoder->base.crtc) - continue; - - crtc = to_intel_crtc(encoder->base.crtc); - - if (crtc->pipe != pipe) - continue; - - switch (encoder->type) { - case INTEL_OUTPUT_TVOUT: - *source = INTEL_PIPE_CRC_SOURCE_TV; - break; - case INTEL_OUTPUT_DP: - case INTEL_OUTPUT_EDP: - dig_port = enc_to_dig_port(&encoder->base); - switch (dig_port->port) { - case PORT_B: - *source = INTEL_PIPE_CRC_SOURCE_DP_B; - break; - case PORT_C: - *source = INTEL_PIPE_CRC_SOURCE_DP_C; - break; - case PORT_D: - *source = INTEL_PIPE_CRC_SOURCE_DP_D; - break; - default: - WARN(1, "nonexisting DP port %c\n", - port_name(dig_port->port)); - break; - } - break; - default: - break; - } - } - drm_modeset_unlock_all(dev); - - return ret; -} - -static int vlv_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, - enum pipe pipe, - enum intel_pipe_crc_source *source, - uint32_t *val) -{ - bool need_stable_symbols = false; - - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) { - int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source); - if (ret) - return ret; - } - - switch (*source) { - case INTEL_PIPE_CRC_SOURCE_PIPE: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_VLV; - break; - case INTEL_PIPE_CRC_SOURCE_DP_B: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_VLV; - need_stable_symbols = true; - break; - case INTEL_PIPE_CRC_SOURCE_DP_C: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_VLV; - need_stable_symbols = true; - break; - case INTEL_PIPE_CRC_SOURCE_DP_D: - if (!IS_CHERRYVIEW(dev_priv)) - return -EINVAL; - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_VLV; - need_stable_symbols = true; - break; - case INTEL_PIPE_CRC_SOURCE_NONE: - *val = 0; - break; - default: - return -EINVAL; - } - - /* - * When the pipe CRC tap point is after the transcoders we need - * to tweak symbol-level features to produce a deterministic series of - * symbols for a given frame. We need to reset those features only once - * a frame (instead of every nth symbol): - * - DC-balance: used to ensure a better clock recovery from the data - * link (SDVO) - * - DisplayPort scrambling: used for EMI reduction - */ - if (need_stable_symbols) { - uint32_t tmp = I915_READ(PORT_DFT2_G4X); - - tmp |= DC_BALANCE_RESET_VLV; - switch (pipe) { - case PIPE_A: - tmp |= PIPE_A_SCRAMBLE_RESET; - break; - case PIPE_B: - tmp |= PIPE_B_SCRAMBLE_RESET; - break; - case PIPE_C: - tmp |= PIPE_C_SCRAMBLE_RESET; - break; - default: - return -EINVAL; - } - I915_WRITE(PORT_DFT2_G4X, tmp); - } - - return 0; -} - -static int i9xx_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, - enum pipe pipe, - enum intel_pipe_crc_source *source, - uint32_t *val) -{ - bool need_stable_symbols = false; - - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) { - int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source); - if (ret) - return ret; - } - - switch (*source) { - case INTEL_PIPE_CRC_SOURCE_PIPE: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_I9XX; - break; - case INTEL_PIPE_CRC_SOURCE_TV: - if (!SUPPORTS_TV(dev_priv)) - return -EINVAL; - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_TV_PRE; - break; - case INTEL_PIPE_CRC_SOURCE_DP_B: - if (!IS_G4X(dev_priv)) - return -EINVAL; - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_G4X; - need_stable_symbols = true; - break; - case INTEL_PIPE_CRC_SOURCE_DP_C: - if (!IS_G4X(dev_priv)) - return -EINVAL; - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_G4X; - need_stable_symbols = true; - break; - case INTEL_PIPE_CRC_SOURCE_DP_D: - if (!IS_G4X(dev_priv)) - return -EINVAL; - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_G4X; - need_stable_symbols = true; - break; - case INTEL_PIPE_CRC_SOURCE_NONE: - *val = 0; - break; - default: - return -EINVAL; - } - - /* - * When the pipe CRC tap point is after the transcoders we need - * to tweak symbol-level features to produce a deterministic series of - * symbols for a given frame. We need to reset those features only once - * a frame (instead of every nth symbol): - * - DC-balance: used to ensure a better clock recovery from the data - * link (SDVO) - * - DisplayPort scrambling: used for EMI reduction - */ - if (need_stable_symbols) { - uint32_t tmp = I915_READ(PORT_DFT2_G4X); - - WARN_ON(!IS_G4X(dev_priv)); - - I915_WRITE(PORT_DFT_I9XX, - I915_READ(PORT_DFT_I9XX) | DC_BALANCE_RESET); - - if (pipe == PIPE_A) - tmp |= PIPE_A_SCRAMBLE_RESET; - else - tmp |= PIPE_B_SCRAMBLE_RESET; - - I915_WRITE(PORT_DFT2_G4X, tmp); - } - - return 0; -} - -static void vlv_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - uint32_t tmp = I915_READ(PORT_DFT2_G4X); - - switch (pipe) { - case PIPE_A: - tmp &= ~PIPE_A_SCRAMBLE_RESET; - break; - case PIPE_B: - tmp &= ~PIPE_B_SCRAMBLE_RESET; - break; - case PIPE_C: - tmp &= ~PIPE_C_SCRAMBLE_RESET; - break; - default: - return; - } - if (!(tmp & PIPE_SCRAMBLE_RESET_MASK)) - tmp &= ~DC_BALANCE_RESET_VLV; - I915_WRITE(PORT_DFT2_G4X, tmp); - -} - -static void g4x_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv, - enum pipe pipe) -{ - uint32_t tmp = I915_READ(PORT_DFT2_G4X); - - if (pipe == PIPE_A) - tmp &= ~PIPE_A_SCRAMBLE_RESET; - else - tmp &= ~PIPE_B_SCRAMBLE_RESET; - I915_WRITE(PORT_DFT2_G4X, tmp); - - if (!(tmp & PIPE_SCRAMBLE_RESET_MASK)) { - I915_WRITE(PORT_DFT_I9XX, - I915_READ(PORT_DFT_I9XX) & ~DC_BALANCE_RESET); - } -} - -static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, - uint32_t *val) -{ - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) - *source = INTEL_PIPE_CRC_SOURCE_PIPE; - - switch (*source) { - case INTEL_PIPE_CRC_SOURCE_PLANE1: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_ILK; - break; - case INTEL_PIPE_CRC_SOURCE_PLANE2: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_ILK; - break; - case INTEL_PIPE_CRC_SOURCE_PIPE: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_ILK; - break; - case INTEL_PIPE_CRC_SOURCE_NONE: - *val = 0; - break; - default: - return -EINVAL; - } - - return 0; -} - -static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv, - bool enable) -{ - struct drm_device *dev = &dev_priv->drm; - struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_A); - struct intel_crtc_state *pipe_config; - struct drm_atomic_state *state; - int ret = 0; - - drm_modeset_lock_all(dev); - state = drm_atomic_state_alloc(dev); - if (!state) { - ret = -ENOMEM; - goto out; - } - - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(&crtc->base); - pipe_config = intel_atomic_get_crtc_state(state, crtc); - if (IS_ERR(pipe_config)) { - ret = PTR_ERR(pipe_config); - goto out; - } - - pipe_config->pch_pfit.force_thru = enable; - if (pipe_config->cpu_transcoder == TRANSCODER_EDP && - pipe_config->pch_pfit.enabled != enable) - pipe_config->base.connectors_changed = true; - - ret = drm_atomic_commit(state); -out: - WARN(ret, "Toggling workaround to %i returns %i\n", enable, ret); - drm_modeset_unlock_all(dev); - drm_atomic_state_put(state); -} - -static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, - enum pipe pipe, - enum intel_pipe_crc_source *source, - uint32_t *val) -{ - if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) - *source = INTEL_PIPE_CRC_SOURCE_PF; - - switch (*source) { - case INTEL_PIPE_CRC_SOURCE_PLANE1: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_IVB; - break; - case INTEL_PIPE_CRC_SOURCE_PLANE2: - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB; - break; - case INTEL_PIPE_CRC_SOURCE_PF: - if (IS_HASWELL(dev_priv) && pipe == PIPE_A) - hsw_trans_edp_pipe_A_crc_wa(dev_priv, true); - - *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB; - break; - case INTEL_PIPE_CRC_SOURCE_NONE: - *val = 0; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int pipe_crc_set_source(struct drm_i915_private *dev_priv, - enum pipe pipe, - enum intel_pipe_crc_source source) -{ - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; - struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); - enum intel_display_power_domain power_domain; - u32 val = 0; /* shut up gcc */ - int ret; - - if (pipe_crc->source == source) - return 0; - - /* forbid changing the source without going back to 'none' */ - if (pipe_crc->source && source) - return -EINVAL; - - power_domain = POWER_DOMAIN_PIPE(pipe); - if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) { - DRM_DEBUG_KMS("Trying to capture CRC while pipe is off\n"); - return -EIO; - } - - if (IS_GEN2(dev_priv)) - ret = i8xx_pipe_crc_ctl_reg(&source, &val); - else if (INTEL_GEN(dev_priv) < 5) - ret = i9xx_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val); - else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - ret = vlv_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val); - else if (IS_GEN5(dev_priv) || IS_GEN6(dev_priv)) - ret = ilk_pipe_crc_ctl_reg(&source, &val); - else - ret = ivb_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val); - - if (ret != 0) - goto out; - - /* none -> real source transition */ - if (source) { - struct intel_pipe_crc_entry *entries; - - DRM_DEBUG_DRIVER("collecting CRCs for pipe %c, %s\n", - pipe_name(pipe), pipe_crc_source_name(source)); - - entries = kcalloc(INTEL_PIPE_CRC_ENTRIES_NR, - sizeof(pipe_crc->entries[0]), - GFP_KERNEL); - if (!entries) { - ret = -ENOMEM; - goto out; - } - - /* - * When IPS gets enabled, the pipe CRC changes. Since IPS gets - * enabled and disabled dynamically based on package C states, - * user space can't make reliable use of the CRCs, so let's just - * completely disable it. - */ - hsw_disable_ips(crtc); - - spin_lock_irq(&pipe_crc->lock); - kfree(pipe_crc->entries); - pipe_crc->entries = entries; - pipe_crc->head = 0; - pipe_crc->tail = 0; - spin_unlock_irq(&pipe_crc->lock); - } - - pipe_crc->source = source; - - I915_WRITE(PIPE_CRC_CTL(pipe), val); - POSTING_READ(PIPE_CRC_CTL(pipe)); - - /* real source -> none transition */ - if (source == INTEL_PIPE_CRC_SOURCE_NONE) { - struct intel_pipe_crc_entry *entries; - struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, - pipe); - - DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n", - pipe_name(pipe)); - - drm_modeset_lock(&crtc->base.mutex, NULL); - if (crtc->base.state->active) - intel_wait_for_vblank(dev_priv, pipe); - drm_modeset_unlock(&crtc->base.mutex); - - spin_lock_irq(&pipe_crc->lock); - entries = pipe_crc->entries; - pipe_crc->entries = NULL; - pipe_crc->head = 0; - pipe_crc->tail = 0; - spin_unlock_irq(&pipe_crc->lock); - - kfree(entries); - - if (IS_G4X(dev_priv)) - g4x_undo_pipe_scramble_reset(dev_priv, pipe); - else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - vlv_undo_pipe_scramble_reset(dev_priv, pipe); - else if (IS_HASWELL(dev_priv) && pipe == PIPE_A) - hsw_trans_edp_pipe_A_crc_wa(dev_priv, false); - - hsw_enable_ips(crtc); - } - - ret = 0; - -out: - intel_display_power_put(dev_priv, power_domain); - - return ret; -} - -/* - * Parse pipe CRC command strings: - * command: wsp* object wsp+ name wsp+ source wsp* - * object: 'pipe' - * name: (A | B | C) - * source: (none | plane1 | plane2 | pf) - * wsp: (#0x20 | #0x9 | #0xA)+ - * - * eg.: - * "pipe A plane1" -> Start CRC computations on plane1 of pipe A - * "pipe A none" -> Stop CRC - */ -static int display_crc_ctl_tokenize(char *buf, char *words[], int max_words) -{ - int n_words = 0; - - while (*buf) { - char *end; - - /* skip leading white space */ - buf = skip_spaces(buf); - if (!*buf) - break; /* end of buffer */ - - /* find end of word */ - for (end = buf; *end && !isspace(*end); end++) - ; - - if (n_words == max_words) { - DRM_DEBUG_DRIVER("too many words, allowed <= %d\n", - max_words); - return -EINVAL; /* ran out of words[] before bytes */ - } - - if (*end) - *end++ = '\0'; - words[n_words++] = buf; - buf = end; - } - - return n_words; -} - -enum intel_pipe_crc_object { - PIPE_CRC_OBJECT_PIPE, -}; - -static const char * const pipe_crc_objects[] = { - "pipe", -}; - -static int -display_crc_ctl_parse_object(const char *buf, enum intel_pipe_crc_object *o) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pipe_crc_objects); i++) - if (!strcmp(buf, pipe_crc_objects[i])) { - *o = i; - return 0; - } - - return -EINVAL; -} - -static int display_crc_ctl_parse_pipe(const char *buf, enum pipe *pipe) -{ - const char name = buf[0]; - - if (name < 'A' || name >= pipe_name(I915_MAX_PIPES)) - return -EINVAL; - - *pipe = name - 'A'; - - return 0; -} - -static int -display_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *s) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pipe_crc_sources); i++) - if (!strcmp(buf, pipe_crc_sources[i])) { - *s = i; - return 0; - } - - return -EINVAL; -} - -static int display_crc_ctl_parse(struct drm_i915_private *dev_priv, - char *buf, size_t len) -{ -#define N_WORDS 3 - int n_words; - char *words[N_WORDS]; - enum pipe pipe; - enum intel_pipe_crc_object object; - enum intel_pipe_crc_source source; - - n_words = display_crc_ctl_tokenize(buf, words, N_WORDS); - if (n_words != N_WORDS) { - DRM_DEBUG_DRIVER("tokenize failed, a command is %d words\n", - N_WORDS); - return -EINVAL; - } - - if (display_crc_ctl_parse_object(words[0], &object) < 0) { - DRM_DEBUG_DRIVER("unknown object %s\n", words[0]); - return -EINVAL; - } - - if (display_crc_ctl_parse_pipe(words[1], &pipe) < 0) { - DRM_DEBUG_DRIVER("unknown pipe %s\n", words[1]); - return -EINVAL; - } - - if (display_crc_ctl_parse_source(words[2], &source) < 0) { - DRM_DEBUG_DRIVER("unknown source %s\n", words[2]); - return -EINVAL; - } - - return pipe_crc_set_source(dev_priv, pipe, source); -} - -static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf, - size_t len, loff_t *offp) -{ - struct seq_file *m = file->private_data; - struct drm_i915_private *dev_priv = m->private; - char *tmpbuf; - int ret; - - if (len == 0) - return 0; - - if (len > PAGE_SIZE - 1) { - DRM_DEBUG_DRIVER("expected <%lu bytes into pipe crc control\n", - PAGE_SIZE); - return -E2BIG; - } - - tmpbuf = kmalloc(len + 1, GFP_KERNEL); - if (!tmpbuf) - return -ENOMEM; - - if (copy_from_user(tmpbuf, ubuf, len)) { - ret = -EFAULT; - goto out; - } - tmpbuf[len] = '\0'; - - ret = display_crc_ctl_parse(dev_priv, tmpbuf, len); - -out: - kfree(tmpbuf); - if (ret < 0) - return ret; - - *offp += len; - return len; -} - -static const struct file_operations i915_display_crc_ctl_fops = { - .owner = THIS_MODULE, - .open = display_crc_ctl_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = display_crc_ctl_write -}; - static ssize_t i915_displayport_test_active_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) @@ -4446,9 +3699,9 @@ static ssize_t i915_displayport_test_active_write(struct file *file, * testing code, only accept an actual value of 1 here */ if (val == 1) - intel_dp->compliance_test_active = 1; + intel_dp->compliance.test_active = 1; else - intel_dp->compliance_test_active = 0; + intel_dp->compliance.test_active = 0; } } out: @@ -4475,7 +3728,7 @@ static int i915_displayport_test_active_show(struct seq_file *m, void *data) if (connector->status == connector_status_connected && connector->encoder != NULL) { intel_dp = enc_to_intel_dp(connector->encoder); - if (intel_dp->compliance_test_active) + if (intel_dp->compliance.test_active) seq_puts(m, "1"); else seq_puts(m, "0"); @@ -4519,7 +3772,7 @@ static int i915_displayport_test_data_show(struct seq_file *m, void *data) if (connector->status == connector_status_connected && connector->encoder != NULL) { intel_dp = enc_to_intel_dp(connector->encoder); - seq_printf(m, "%lx", intel_dp->compliance_test_data); + seq_printf(m, "%lx", intel_dp->compliance.test_data.edid); } else seq_puts(m, "0"); } @@ -4558,7 +3811,7 @@ static int i915_displayport_test_type_show(struct seq_file *m, void *data) if (connector->status == connector_status_connected && connector->encoder != NULL) { intel_dp = enc_to_intel_dp(connector->encoder); - seq_printf(m, "%02lx", intel_dp->compliance_test_type); + seq_printf(m, "%02lx", intel_dp->compliance.test_type); } else seq_puts(m, "0"); } @@ -4957,7 +4210,7 @@ unlock: if (val & DROP_FREED) { synchronize_rcu(); - flush_work(&dev_priv->mm.free_work); + i915_gem_drain_freed_objects(dev_priv); } return ret; @@ -5164,7 +4417,7 @@ static void gen9_sseu_device_status(struct drm_i915_private *dev_priv, u32 s_reg[s_max], eu_reg[2*s_max], eu_mask[2]; /* BXT has a single slice and at most 3 subslices. */ - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { s_max = 1; ss_max = 3; } @@ -5198,7 +4451,7 @@ static void gen9_sseu_device_status(struct drm_i915_private *dev_priv, for (ss = 0; ss < ss_max; ss++) { unsigned int eu_cnt; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { if (!(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss)))) /* skip disabled subslice */ continue; @@ -5386,6 +4639,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_guc_info", i915_guc_info, 0}, {"i915_guc_load_status", i915_guc_load_status_info, 0}, {"i915_guc_log_dump", i915_guc_log_dump, 0}, + {"i915_huc_load_status", i915_huc_load_status_info, 0}, {"i915_frequency_info", i915_frequency_info, 0}, {"i915_hangcheck_info", i915_hangcheck_info, 0}, {"i915_drpc_info", i915_drpc_info, 0}, @@ -5449,19 +4703,6 @@ static const struct i915_debugfs_files { {"i915_guc_log_control", &i915_guc_log_control_fops} }; -void intel_display_crc_init(struct drm_i915_private *dev_priv) -{ - enum pipe pipe; - - for_each_pipe(dev_priv, pipe) { - struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; - - pipe_crc->opened = false; - spin_lock_init(&pipe_crc->lock); - init_waitqueue_head(&pipe_crc->wq); - } -} - int i915_debugfs_register(struct drm_i915_private *dev_priv) { struct drm_minor *minor = dev_priv->drm.primary; @@ -5471,11 +4712,9 @@ int i915_debugfs_register(struct drm_i915_private *dev_priv) if (ret) return ret; - for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) { - ret = i915_pipe_crc_create(minor->debugfs_root, minor, i); - if (ret) - return ret; - } + ret = intel_pipe_crc_create(minor); + if (ret) + return ret; for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { ret = i915_debugfs_create(minor->debugfs_root, minor, @@ -5501,12 +4740,7 @@ void i915_debugfs_unregister(struct drm_i915_private *dev_priv) drm_debugfs_remove_files((struct drm_info_list *)&i915_forcewake_fops, 1, minor); - for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) { - struct drm_info_list *info_list = - (struct drm_info_list *)&i915_pipe_crc_data[i]; - - drm_debugfs_remove_files(info_list, 1, minor); - } + intel_pipe_crc_cleanup(minor); for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) { struct drm_info_list *info_list = diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 728ca3ea74d2..e703556eba99 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -49,6 +49,7 @@ #include "i915_trace.h" #include "i915_vgpu.h" #include "intel_drv.h" +#include "intel_uc.h" static struct drm_driver driver; @@ -142,9 +143,8 @@ static enum intel_pch intel_virt_detect_pch(struct drm_i915_private *dev_priv) return ret; } -static void intel_detect_pch(struct drm_device *dev) +static void intel_detect_pch(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct pci_dev *pch = NULL; /* In all current cases, num_pipes is equivalent to the PCH_NOP setting @@ -317,6 +317,12 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_MIN_EU_IN_POOL: value = INTEL_INFO(dev_priv)->sseu.min_eu_in_pool; break; + case I915_PARAM_HUC_STATUS: + /* The register is already force-woken. We dont need + * any rpm here + */ + value = I915_READ(HUC_STATUS2) & HUC_FW_VERIFIED; + break; case I915_PARAM_MMAP_GTT_VERSION: /* Though we've started our numbering from 1, and so class all * earlier versions as 0, in effect their value is undefined as @@ -362,10 +368,8 @@ static int i915_getparam(struct drm_device *dev, void *data, return 0; } -static int i915_get_bridge_dev(struct drm_device *dev) +static int i915_get_bridge_dev(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); if (!dev_priv->bridge_dev) { DRM_ERROR("bridge device not found\n"); @@ -376,9 +380,8 @@ static int i915_get_bridge_dev(struct drm_device *dev) /* Allocate space for the MCH regs if needed, return nonzero on error */ static int -intel_alloc_mchbar_resource(struct drm_device *dev) +intel_alloc_mchbar_resource(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); int reg = INTEL_GEN(dev_priv) >= 4 ? MCHBAR_I965 : MCHBAR_I915; u32 temp_lo, temp_hi = 0; u64 mchbar_addr; @@ -422,9 +425,8 @@ intel_alloc_mchbar_resource(struct drm_device *dev) /* Setup MCHBAR if possible, return true if we should disable it again */ static void -intel_setup_mchbar(struct drm_device *dev) +intel_setup_mchbar(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); int mchbar_reg = INTEL_GEN(dev_priv) >= 4 ? MCHBAR_I965 : MCHBAR_I915; u32 temp; bool enabled; @@ -446,7 +448,7 @@ intel_setup_mchbar(struct drm_device *dev) if (enabled) return; - if (intel_alloc_mchbar_resource(dev)) + if (intel_alloc_mchbar_resource(dev_priv)) return; dev_priv->mchbar_need_disable = true; @@ -462,9 +464,8 @@ intel_setup_mchbar(struct drm_device *dev) } static void -intel_teardown_mchbar(struct drm_device *dev) +intel_teardown_mchbar(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); int mchbar_reg = INTEL_GEN(dev_priv) >= 4 ? MCHBAR_I965 : MCHBAR_I915; if (dev_priv->mchbar_need_disable) { @@ -494,9 +495,9 @@ intel_teardown_mchbar(struct drm_device *dev) /* true = enable decode, false = disable decoder */ static unsigned int i915_vga_set_decode(void *cookie, bool state) { - struct drm_device *dev = cookie; + struct drm_i915_private *dev_priv = cookie; - intel_modeset_vga_set_state(to_i915(dev), state); + intel_modeset_vga_set_state(dev_priv, state); if (state) return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; @@ -504,6 +505,9 @@ static unsigned int i915_vga_set_decode(void *cookie, bool state) return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; } +static int i915_resume_switcheroo(struct drm_device *dev); +static int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state); + static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) { struct drm_device *dev = pci_get_drvdata(pdev); @@ -545,12 +549,11 @@ static const struct vga_switcheroo_client_ops i915_switcheroo_ops = { static void i915_gem_fini(struct drm_i915_private *dev_priv) { mutex_lock(&dev_priv->drm.struct_mutex); - i915_gem_cleanup_engines(&dev_priv->drm); - i915_gem_context_fini(&dev_priv->drm); + i915_gem_cleanup_engines(dev_priv); + i915_gem_context_fini(dev_priv); mutex_unlock(&dev_priv->drm.struct_mutex); - rcu_barrier(); - flush_work(&dev_priv->mm.free_work); + i915_gem_drain_freed_objects(dev_priv); WARN_ON(!list_empty(&dev_priv->context_list)); } @@ -575,7 +578,7 @@ static int i915_load_modeset_init(struct drm_device *dev) * then we do not take part in VGA arbitration and the * vga_client_register() fails with -ENODEV. */ - ret = vga_client_register(pdev, dev, NULL, i915_vga_set_decode); + ret = vga_client_register(pdev, dev_priv, NULL, i915_vga_set_decode); if (ret && ret != -ENODEV) goto out; @@ -596,7 +599,7 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_csr; - intel_setup_gmbus(dev); + intel_setup_gmbus(dev_priv); /* Important: The output setup functions called by modeset_init need * working irqs for e.g. gmbus and dp aux transfers. */ @@ -604,9 +607,10 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_irq; - intel_guc_init(dev); + intel_huc_init(dev_priv); + intel_guc_init(dev_priv); - ret = i915_gem_init(dev); + ret = i915_gem_init(dev_priv); if (ret) goto cleanup_irq; @@ -627,13 +631,14 @@ static int i915_load_modeset_init(struct drm_device *dev) return 0; cleanup_gem: - if (i915_gem_suspend(dev)) + if (i915_gem_suspend(dev_priv)) DRM_ERROR("failed to idle hardware; continuing to unload!\n"); i915_gem_fini(dev_priv); cleanup_irq: - intel_guc_fini(dev); + intel_guc_fini(dev_priv); + intel_huc_fini(dev_priv); drm_irq_uninstall(dev); - intel_teardown_gmbus(dev); + intel_teardown_gmbus(dev_priv); cleanup_csr: intel_csr_ucode_fini(dev_priv); intel_power_domains_fini(dev_priv); @@ -644,7 +649,6 @@ out: return ret; } -#if IS_ENABLED(CONFIG_FB) static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) { struct apertures_struct *ap; @@ -669,12 +673,6 @@ static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) return ret; } -#else -static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) -{ - return 0; -} -#endif #if !defined(CONFIG_VGA_CONSOLE) static int i915_kick_out_vgacon(struct drm_i915_private *dev_priv) @@ -812,26 +810,25 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, spin_lock_init(&dev_priv->uncore.lock); spin_lock_init(&dev_priv->mm.object_stat_lock); spin_lock_init(&dev_priv->mmio_flip_lock); + spin_lock_init(&dev_priv->wm.dsparb_lock); mutex_init(&dev_priv->sb_lock); mutex_init(&dev_priv->modeset_restore_lock); mutex_init(&dev_priv->av_mutex); mutex_init(&dev_priv->wm.wm_mutex); mutex_init(&dev_priv->pps_mutex); + intel_uc_init_early(dev_priv); + i915_memcpy_init_early(dev_priv); ret = i915_workqueues_init(dev_priv); if (ret < 0) return ret; - ret = intel_gvt_init(dev_priv); - if (ret < 0) - goto err_workqueues; - /* This must be called before any calls to HAS_PCH_* */ - intel_detect_pch(&dev_priv->drm); + intel_detect_pch(dev_priv); - intel_pm_setup(&dev_priv->drm); + intel_pm_setup(dev_priv); intel_init_dpio(dev_priv); intel_power_domains_init(dev_priv); intel_irq_init(dev_priv); @@ -839,9 +836,9 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, intel_init_display_hooks(dev_priv); intel_init_clock_gating_hooks(dev_priv); intel_init_audio_hooks(dev_priv); - ret = i915_gem_load_init(&dev_priv->drm); + ret = i915_gem_load_init(dev_priv); if (ret < 0) - goto err_gvt; + goto err_workqueues; intel_display_crc_init(dev_priv); @@ -849,10 +846,10 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, intel_detect_preproduction_hw(dev_priv); + i915_perf_init(dev_priv); + return 0; -err_gvt: - intel_gvt_cleanup(dev_priv); err_workqueues: i915_workqueues_cleanup(dev_priv); return ret; @@ -864,13 +861,13 @@ err_workqueues: */ static void i915_driver_cleanup_early(struct drm_i915_private *dev_priv) { - i915_gem_load_cleanup(&dev_priv->drm); + i915_perf_fini(dev_priv); + i915_gem_load_cleanup(dev_priv); i915_workqueues_cleanup(dev_priv); } -static int i915_mmio_setup(struct drm_device *dev) +static int i915_mmio_setup(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct pci_dev *pdev = dev_priv->drm.pdev; int mmio_bar; int mmio_size; @@ -896,17 +893,16 @@ static int i915_mmio_setup(struct drm_device *dev) } /* Try to make sure MCHBAR is enabled before poking at it */ - intel_setup_mchbar(dev); + intel_setup_mchbar(dev_priv); return 0; } -static void i915_mmio_cleanup(struct drm_device *dev) +static void i915_mmio_cleanup(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct pci_dev *pdev = dev_priv->drm.pdev; - intel_teardown_mchbar(dev); + intel_teardown_mchbar(dev_priv); pci_iounmap(pdev, dev_priv->regs); } @@ -921,16 +917,15 @@ static void i915_mmio_cleanup(struct drm_device *dev) */ static int i915_driver_init_mmio(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; int ret; if (i915_inject_load_failure()) return -ENODEV; - if (i915_get_bridge_dev(dev)) + if (i915_get_bridge_dev(dev_priv)) return -EIO; - ret = i915_mmio_setup(dev); + ret = i915_mmio_setup(dev_priv); if (ret < 0) goto put_bridge; @@ -950,10 +945,8 @@ put_bridge: */ static void i915_driver_cleanup_mmio(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; - intel_uncore_fini(dev_priv); - i915_mmio_cleanup(dev); + i915_mmio_cleanup(dev_priv); pci_dev_put(dev_priv->bridge_dev); } @@ -1044,7 +1037,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) * behaviour if any general state is accessed within a page above 4GB, * which also needs to be handled carefully. */ - if (IS_BROADWATER(dev_priv) || IS_CRESTLINE(dev_priv)) { + if (IS_I965G(dev_priv) || IS_I965GM(dev_priv)) { ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { @@ -1079,6 +1072,10 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) DRM_DEBUG_DRIVER("can't enable MSI"); } + ret = intel_gvt_init(dev_priv); + if (ret) + goto out_ggtt; + return 0; out_ggtt: @@ -1125,8 +1122,11 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) /* Reveal our presence to userspace */ if (drm_dev_register(dev, 0) == 0) { i915_debugfs_register(dev_priv); - i915_guc_register(dev_priv); + i915_guc_log_register(dev_priv); i915_setup_sysfs(dev_priv); + + /* Depends on sysfs having been initialized */ + i915_perf_register(dev_priv); } else DRM_ERROR("Failed to register driver for userspace access!\n"); @@ -1139,7 +1139,7 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) if (IS_GEN5(dev_priv)) intel_gpu_ips_init(dev_priv); - i915_audio_component_init(dev_priv); + intel_audio_init(dev_priv); /* * Some ports require correctly set-up hpd registers for detection to @@ -1157,14 +1157,16 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) */ static void i915_driver_unregister(struct drm_i915_private *dev_priv) { - i915_audio_component_cleanup(dev_priv); + intel_audio_deinit(dev_priv); intel_gpu_ips_teardown(); acpi_video_unregister(); intel_opregion_unregister(dev_priv); + i915_perf_unregister(dev_priv); + i915_teardown_sysfs(dev_priv); - i915_guc_unregister(dev_priv); + i915_guc_log_unregister(dev_priv); i915_debugfs_unregister(dev_priv); drm_dev_unregister(&dev_priv->drm); @@ -1195,8 +1197,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) if (dev_priv) ret = drm_dev_init(&dev_priv->drm, &driver, &pdev->dev); if (ret) { - dev_printk(KERN_ERR, &pdev->dev, - "[" DRM_NAME ":%s] allocation failed\n", __func__); + DRM_DEV_ERROR(&pdev->dev, "allocation failed\n"); kfree(dev_priv); return ret; } @@ -1244,6 +1245,8 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) intel_runtime_pm_enable(dev_priv); + dev_priv->ipc_enabled = false; + /* Everything is in place, we can now relax! */ DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", driver.name, driver.major, driver.minor, driver.patchlevel, @@ -1281,11 +1284,13 @@ void i915_driver_unload(struct drm_device *dev) intel_fbdev_fini(dev); - if (i915_gem_suspend(dev)) + if (i915_gem_suspend(dev_priv)) DRM_ERROR("failed to idle hardware; continuing to unload!\n"); intel_display_power_get(dev_priv, POWER_DOMAIN_INIT); + intel_gvt_cleanup(dev_priv); + i915_driver_unregister(dev_priv); drm_vblank_cleanup(dev); @@ -1313,12 +1318,13 @@ void i915_driver_unload(struct drm_device *dev) /* Free error state after interrupts are fully disabled. */ cancel_delayed_work_sync(&dev_priv->gpu_error.hangcheck_work); - i915_destroy_error_state(dev); + i915_destroy_error_state(dev_priv); /* Flush any outstanding unpin_work. */ drain_workqueue(dev_priv->wq); - intel_guc_fini(dev); + intel_guc_fini(dev_priv); + intel_huc_fini(dev_priv); i915_gem_fini(dev_priv); intel_fbc_cleanup_cfb(dev_priv); @@ -1423,14 +1429,14 @@ static int i915_drm_suspend(struct drm_device *dev) pci_save_state(pdev); - error = i915_gem_suspend(dev); + error = i915_gem_suspend(dev_priv); if (error) { dev_err(&pdev->dev, "GEM idle failed, resume might fail\n"); goto out; } - intel_guc_suspend(dev); + intel_guc_suspend(dev_priv); intel_display_suspend(dev); @@ -1445,7 +1451,7 @@ static int i915_drm_suspend(struct drm_device *dev) i915_gem_suspend_gtt_mappings(dev_priv); - i915_save_state(dev); + i915_save_state(dev_priv); opregion_target_state = suspend_to_idle(dev_priv) ? PCI_D1 : PCI_D3cold; intel_opregion_notify_adapter(dev_priv, opregion_target_state); @@ -1476,7 +1482,7 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation) intel_display_set_init_power(dev_priv, false); - fw_csr = !IS_BROXTON(dev_priv) && + fw_csr = !IS_GEN9_LP(dev_priv) && suspend_to_idle(dev_priv) && dev_priv->csr.dmc_payload; /* * In case of firmware assisted context save/restore don't manually @@ -1489,7 +1495,7 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation) intel_power_domains_suspend(dev_priv); ret = 0; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) bxt_enable_dc9(dev_priv); else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) hsw_enable_pc8(dev_priv); @@ -1528,7 +1534,7 @@ out: return ret; } -int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state) +static int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state) { int error; @@ -1566,33 +1572,36 @@ static int i915_drm_resume(struct drm_device *dev) intel_csr_ucode_resume(dev_priv); - i915_gem_resume(dev); + i915_gem_resume(dev_priv); - i915_restore_state(dev); + i915_restore_state(dev_priv); intel_pps_unlock_regs_wa(dev_priv); intel_opregion_setup(dev_priv); - intel_init_pch_refclk(dev); - drm_mode_config_reset(dev); + intel_init_pch_refclk(dev_priv); /* * Interrupts have to be enabled before any batches are run. If not the * GPU will hang. i915_gem_init_hw() will initiate batches to * update/restore the context. * + * drm_mode_config_reset() needs AUX interrupts. + * * Modeset enabling in intel_modeset_init_hw() also needs working * interrupts. */ intel_runtime_pm_enable_interrupts(dev_priv); + drm_mode_config_reset(dev); + mutex_lock(&dev->struct_mutex); - if (i915_gem_init_hw(dev)) { + if (i915_gem_init_hw(dev_priv)) { DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n"); i915_gem_set_wedged(dev_priv); } mutex_unlock(&dev->struct_mutex); - intel_guc_resume(dev); + intel_guc_resume(dev_priv); intel_modeset_init_hw(dev); @@ -1694,7 +1703,7 @@ static int i915_drm_resume_early(struct drm_device *dev) intel_uncore_early_sanitize(dev_priv, true); - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { if (!dev_priv->suspended_to_idle) gen9_sanitize_dc_state(dev_priv); bxt_disable_dc9(dev_priv); @@ -1704,7 +1713,7 @@ static int i915_drm_resume_early(struct drm_device *dev) intel_uncore_sanitize(dev_priv); - if (IS_BROXTON(dev_priv) || + if (IS_GEN9_LP(dev_priv) || !(dev_priv->suspended_to_idle && dev_priv->csr.dmc_payload)) intel_power_domains_init_hw(dev_priv, true); @@ -1716,7 +1725,7 @@ out: return ret; } -int i915_resume_switcheroo(struct drm_device *dev) +static int i915_resume_switcheroo(struct drm_device *dev) { int ret; @@ -1730,25 +1739,9 @@ int i915_resume_switcheroo(struct drm_device *dev) return i915_drm_resume(dev); } -static void disable_engines_irq(struct drm_i915_private *dev_priv) -{ - struct intel_engine_cs *engine; - enum intel_engine_id id; - - /* Ensure irq handler finishes, and not run again. */ - disable_irq(dev_priv->drm.irq); - for_each_engine(engine, dev_priv, id) - tasklet_kill(&engine->irq_tasklet); -} - -static void enable_engines_irq(struct drm_i915_private *dev_priv) -{ - enable_irq(dev_priv->drm.irq); -} - /** * i915_reset - reset chip after a hang - * @dev: drm device to reset + * @dev_priv: device private to reset * * Reset the chip. Useful if a hang is detected. Marks the device as wedged * on failure. @@ -1765,11 +1758,10 @@ static void enable_engines_irq(struct drm_i915_private *dev_priv) */ void i915_reset(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; struct i915_gpu_error *error = &dev_priv->gpu_error; int ret; - lockdep_assert_held(&dev->struct_mutex); + lockdep_assert_held(&dev_priv->drm.struct_mutex); if (!test_and_clear_bit(I915_RESET_IN_PROGRESS, &error->flags)) return; @@ -1779,11 +1771,15 @@ void i915_reset(struct drm_i915_private *dev_priv) error->reset_count++; pr_notice("drm/i915: Resetting chip after gpu hang\n"); + disable_irq(dev_priv->drm.irq); + ret = i915_gem_reset_prepare(dev_priv); + if (ret) { + DRM_ERROR("GPU recovery failed\n"); + intel_gpu_reset(dev_priv, ALL_ENGINES); + goto error; + } - disable_engines_irq(dev_priv); ret = intel_gpu_reset(dev_priv, ALL_ENGINES); - enable_engines_irq(dev_priv); - if (ret) { if (ret != -ENODEV) DRM_ERROR("Failed to reset chip: %i\n", ret); @@ -1792,7 +1788,7 @@ void i915_reset(struct drm_i915_private *dev_priv) goto error; } - i915_gem_reset(dev_priv); + i915_gem_reset_finish(dev_priv); intel_overlay_reset(dev_priv); /* Ok, now get things going again... */ @@ -1809,13 +1805,16 @@ void i915_reset(struct drm_i915_private *dev_priv) * was running at the time of the reset (i.e. we weren't VT * switched away). */ - ret = i915_gem_init_hw(dev); + ret = i915_gem_init_hw(dev_priv); if (ret) { DRM_ERROR("Failed hw init on reset %d\n", ret); goto error; } + i915_queue_hangcheck(dev_priv); + wakeup: + enable_irq(dev_priv->drm.irq); wake_up_bit(&error->flags, I915_RESET_IN_PROGRESS); return; @@ -2321,12 +2320,12 @@ static int intel_runtime_suspend(struct device *kdev) */ i915_gem_runtime_suspend(dev_priv); - intel_guc_suspend(dev); + intel_guc_suspend(dev_priv); intel_runtime_pm_disable_interrupts(dev_priv); ret = 0; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { bxt_display_core_uninit(dev_priv); bxt_enable_dc9(dev_priv); } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { @@ -2406,12 +2405,12 @@ static int intel_runtime_resume(struct device *kdev) if (intel_uncore_unclaimed_mmio(dev_priv)) DRM_DEBUG_DRIVER("Unclaimed access during suspend, bios?\n"); - intel_guc_resume(dev); + intel_guc_resume(dev_priv); if (IS_GEN6(dev_priv)) - intel_init_pch_refclk(dev); + intel_init_pch_refclk(dev_priv); - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { bxt_disable_dc9(dev_priv); bxt_display_core_init(dev_priv, true); if (dev_priv->csr.dmc_payload && @@ -2550,8 +2549,8 @@ static const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, DRM_RENDER_ALLOW), - DRM_IOCTL_DEF_DRV(I915_GEM_SET_TILING, i915_gem_set_tiling, DRM_RENDER_ALLOW), - DRM_IOCTL_DEF_DRV(I915_GEM_GET_TILING, i915_gem_get_tiling, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_SET_TILING, i915_gem_set_tiling_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_GET_TILING, i915_gem_get_tiling_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0), DRM_IOCTL_DEF_DRV(I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_RENDER_ALLOW), @@ -2567,6 +2566,7 @@ static const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_GETPARAM, i915_gem_context_getparam_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_SETPARAM, i915_gem_context_setparam_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_PERF_OPEN, i915_perf_open_ioctl, DRM_RENDER_ALLOW), }; static struct drm_driver driver = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8493e19b563a..0a4b42d31391 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -49,17 +49,20 @@ #include <drm/drm_legacy.h> /* for struct drm_dma_handle */ #include <drm/drm_gem.h> #include <drm/drm_auth.h> +#include <drm/drm_cache.h> #include "i915_params.h" #include "i915_reg.h" +#include "i915_utils.h" #include "intel_bios.h" #include "intel_dpll_mgr.h" -#include "intel_guc.h" +#include "intel_uc.h" #include "intel_lrc.h" #include "intel_ringbuffer.h" #include "i915_gem.h" +#include "i915_gem_context.h" #include "i915_gem_fence_reg.h" #include "i915_gem_object.h" #include "i915_gem_gtt.h" @@ -76,8 +79,8 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20161121" -#define DRIVER_TIMESTAMP 1479717903 +#define DRIVER_DATE "20170123" +#define DRIVER_TIMESTAMP 1485156432 #undef WARN_ON /* Many gcc seem to no see through this and fall over :( */ @@ -119,6 +122,90 @@ bool __i915_inject_load_failure(const char *func, int line); #define i915_inject_load_failure() \ __i915_inject_load_failure(__func__, __LINE__) +typedef struct { + uint32_t val; +} uint_fixed_16_16_t; + +#define FP_16_16_MAX ({ \ + uint_fixed_16_16_t fp; \ + fp.val = UINT_MAX; \ + fp; \ +}) + +static inline uint_fixed_16_16_t u32_to_fixed_16_16(uint32_t val) +{ + uint_fixed_16_16_t fp; + + WARN_ON(val >> 16); + + fp.val = val << 16; + return fp; +} + +static inline uint32_t fixed_16_16_to_u32_round_up(uint_fixed_16_16_t fp) +{ + return DIV_ROUND_UP(fp.val, 1 << 16); +} + +static inline uint32_t fixed_16_16_to_u32(uint_fixed_16_16_t fp) +{ + return fp.val >> 16; +} + +static inline uint_fixed_16_16_t min_fixed_16_16(uint_fixed_16_16_t min1, + uint_fixed_16_16_t min2) +{ + uint_fixed_16_16_t min; + + min.val = min(min1.val, min2.val); + return min; +} + +static inline uint_fixed_16_16_t max_fixed_16_16(uint_fixed_16_16_t max1, + uint_fixed_16_16_t max2) +{ + uint_fixed_16_16_t max; + + max.val = max(max1.val, max2.val); + return max; +} + +static inline uint_fixed_16_16_t fixed_16_16_div_round_up(uint32_t val, + uint32_t d) +{ + uint_fixed_16_16_t fp, res; + + fp = u32_to_fixed_16_16(val); + res.val = DIV_ROUND_UP(fp.val, d); + return res; +} + +static inline uint_fixed_16_16_t fixed_16_16_div_round_up_u64(uint32_t val, + uint32_t d) +{ + uint_fixed_16_16_t res; + uint64_t interm_val; + + interm_val = (uint64_t)val << 16; + interm_val = DIV_ROUND_UP_ULL(interm_val, d); + WARN_ON(interm_val >> 32); + res.val = (uint32_t) interm_val; + + return res; +} + +static inline uint_fixed_16_16_t mul_u32_fixed_16_16(uint32_t val, + uint_fixed_16_16_t mul) +{ + uint64_t intermediate_val; + uint_fixed_16_16_t fp; + + intermediate_val = (uint64_t) val * mul.val; + WARN_ON(intermediate_val >> 32); + fp.val = (uint32_t) intermediate_val; + return fp; +} + static inline const char *yesno(bool v) { return v ? "yes" : "no"; @@ -180,21 +267,39 @@ static inline bool transcoder_is_dsi(enum transcoder transcoder) } /* + * Global legacy plane identifier. Valid only for primary/sprite + * planes on pre-g4x, and only for primary planes on g4x+. + */ +enum plane { + PLANE_A, + PLANE_B, + PLANE_C, +}; +#define plane_name(p) ((p) + 'A') + +#define sprite_name(p, s) ((p) * INTEL_INFO(dev_priv)->num_sprites[(p)] + (s) + 'A') + +/* + * Per-pipe plane identifier. * I915_MAX_PLANES in the enum below is the maximum (across all platforms) * number of planes per CRTC. Not all platforms really have this many planes, * which means some arrays of size I915_MAX_PLANES may have unused entries * between the topmost sprite plane and the cursor plane. + * + * This is expected to be passed to various register macros + * (eg. PLANE_CTL(), PS_PLANE_SEL(), etc.) so adjust with care. */ -enum plane { - PLANE_A = 0, - PLANE_B, - PLANE_C, +enum plane_id { + PLANE_PRIMARY, + PLANE_SPRITE0, + PLANE_SPRITE1, PLANE_CURSOR, I915_MAX_PLANES, }; -#define plane_name(p) ((p) + 'A') -#define sprite_name(p, s) ((p) * INTEL_INFO(dev_priv)->num_sprites[(p)] + (s) + 'A') +#define for_each_plane_id_on_crtc(__crtc, __p) \ + for ((__p) = PLANE_PRIMARY; (__p) < I915_MAX_PLANES; (__p)++) \ + for_each_if ((__crtc)->plane_ids_mask & BIT(__p)) enum port { PORT_NONE = -1, @@ -216,7 +321,8 @@ enum dpio_channel { enum dpio_phy { DPIO_PHY0, - DPIO_PHY1 + DPIO_PHY1, + DPIO_PHY2, }; enum intel_display_power_domain { @@ -416,6 +522,15 @@ struct drm_i915_file_private { } rps; unsigned int bsd_engine; + +/* Client can have a maximum of 3 contexts banned before + * it is denied of creating new contexts. As one context + * ban needs 4 consecutive hangs, and more if there is + * progress in between, this is a last resort stop gap measure + * to limit the badly behaving clients access to gpu. + */ +#define I915_MAX_CLIENT_CONTEXT_BANS 3 + int context_bans; }; /* Used by dp and fdi links */ @@ -659,32 +774,20 @@ struct intel_csr { }; #define DEV_INFO_FOR_EACH_FLAG(func) \ - /* Keep is_* in chronological order */ \ func(is_mobile); \ - func(is_i85x); \ - func(is_i915g); \ - func(is_i945gm); \ - func(is_g33); \ - func(is_g4x); \ - func(is_pineview); \ - func(is_broadwater); \ - func(is_crestline); \ - func(is_ivybridge); \ - func(is_valleyview); \ - func(is_cherryview); \ - func(is_haswell); \ - func(is_broadwell); \ - func(is_skylake); \ - func(is_broxton); \ - func(is_kabylake); \ + func(is_lp); \ func(is_alpha_support); \ /* Keep has_* in alphabetical order */ \ func(has_64bit_reloc); \ + func(has_aliasing_ppgtt); \ func(has_csr); \ func(has_ddi); \ + func(has_decoupled_mmio); \ func(has_dp_mst); \ func(has_fbc); \ func(has_fpga_dbg); \ + func(has_full_ppgtt); \ + func(has_full_48bit_ppgtt); \ func(has_gmbus_irq); \ func(has_gmch_display); \ func(has_guc); \ @@ -705,8 +808,7 @@ struct intel_csr { func(cursor_needs_physical); \ func(hws_needs_physical); \ func(overlay_needs_physical); \ - func(supports_tv); \ - func(has_decoupled_mmio) + func(supports_tv); struct sseu_dev_info { u8 slice_mask; @@ -726,13 +828,45 @@ static inline unsigned int sseu_subslice_total(const struct sseu_dev_info *sseu) return hweight8(sseu->slice_mask) * hweight8(sseu->subslice_mask); } +/* Keep in gen based order, and chronological order within a gen */ +enum intel_platform { + INTEL_PLATFORM_UNINITIALIZED = 0, + INTEL_I830, + INTEL_I845G, + INTEL_I85X, + INTEL_I865G, + INTEL_I915G, + INTEL_I915GM, + INTEL_I945G, + INTEL_I945GM, + INTEL_G33, + INTEL_PINEVIEW, + INTEL_I965G, + INTEL_I965GM, + INTEL_G45, + INTEL_GM45, + INTEL_IRONLAKE, + INTEL_SANDYBRIDGE, + INTEL_IVYBRIDGE, + INTEL_VALLEYVIEW, + INTEL_HASWELL, + INTEL_BROADWELL, + INTEL_CHERRYVIEW, + INTEL_SKYLAKE, + INTEL_BROXTON, + INTEL_KABYLAKE, + INTEL_GEMINILAKE, +}; + struct intel_device_info { u32 display_mmio_offset; u16 device_id; u8 num_pipes; u8 num_sprites[I915_MAX_PIPES]; + u8 num_scalers[I915_MAX_PIPES]; u8 gen; u16 gen_mask; + enum intel_platform platform; u8 ring_mask; /* Rings supported by the HW */ u8 num_rings; #define DEFINE_FLAG(name) u8 name:1 @@ -800,7 +934,8 @@ struct drm_i915_error_state { /* Software tracked state */ bool waiting; int num_waiters; - int hangcheck_score; + unsigned long hangcheck_timestamp; + bool hangcheck_stalled; enum intel_engine_hangcheck_action hangcheck_action; struct i915_address_space *vm; int num_requests; @@ -849,6 +984,7 @@ struct drm_i915_error_state { long jiffies; pid_t pid; u32 context; + int ban_score; u32 seqno; u32 head; u32 tail; @@ -870,6 +1006,7 @@ struct drm_i915_error_state { pid_t pid; char comm[TASK_COMM_LEN]; + int context_bans; } engine[I915_NUM_ENGINES]; struct drm_i915_error_buffer { @@ -901,86 +1038,7 @@ enum i915_cache_level { I915_CACHE_WT, /* hsw:gt3e WriteThrough for scanouts */ }; -struct i915_ctx_hang_stats { - /* This context had batch pending when hang was declared */ - unsigned batch_pending; - - /* This context had batch active when hang was declared */ - unsigned batch_active; - - /* Time when this context was last blamed for a GPU reset */ - unsigned long guilty_ts; - - /* If the contexts causes a second GPU hang within this time, - * it is permanently banned from submitting any more work. - */ - unsigned long ban_period_seconds; - - /* This context is banned to submit more work */ - bool banned; -}; - -/* This must match up with the value previously used for execbuf2.rsvd1. */ -#define DEFAULT_CONTEXT_HANDLE 0 - -/** - * struct i915_gem_context - as the name implies, represents a context. - * @ref: reference count. - * @user_handle: userspace tracking identity for this context. - * @remap_slice: l3 row remapping information. - * @flags: context specific flags: - * CONTEXT_NO_ZEROMAP: do not allow mapping things to page 0. - * @file_priv: filp associated with this context (NULL for global default - * context). - * @hang_stats: information about the role of this context in possible GPU - * hangs. - * @ppgtt: virtual memory space used by this context. - * @legacy_hw_ctx: render context backing object and whether it is correctly - * initialized (legacy ring submission mechanism only). - * @link: link in the global list of contexts. - * - * Contexts are memory images used by the hardware to store copies of their - * internal state. - */ -struct i915_gem_context { - struct kref ref; - struct drm_i915_private *i915; - struct drm_i915_file_private *file_priv; - struct i915_hw_ppgtt *ppgtt; - struct pid *pid; - const char *name; - - struct i915_ctx_hang_stats hang_stats; - - unsigned long flags; -#define CONTEXT_NO_ZEROMAP BIT(0) -#define CONTEXT_NO_ERROR_CAPTURE BIT(1) - - /* Unique identifier for this context, used by the hw for tracking */ - unsigned int hw_id; - u32 user_handle; - int priority; /* greater priorities are serviced first */ - - u32 ggtt_alignment; - - struct intel_context { - struct i915_vma *state; - struct intel_ring *ring; - uint32_t *lrc_reg_state; - u64 lrc_desc; - int pin_count; - bool initialised; - } engine[I915_NUM_ENGINES]; - u32 ring_size; - u32 desc_template; - struct atomic_notifier_head status_notifier; - bool execlists_force_single_submission; - - struct list_head link; - - u8 remap_slice; - bool closed:1; -}; +#define I915_COLOR_UNEVICTABLE (-1) /* a non-vma sharing the address space */ enum fb_op_origin { ORIGIN_GTT, @@ -1027,7 +1085,7 @@ struct intel_fbc { } plane; struct { - uint32_t pixel_format; + const struct drm_format_info *format; unsigned int stride; } fb; } state_cache; @@ -1042,7 +1100,7 @@ struct intel_fbc { } crtc; struct { - uint32_t pixel_format; + const struct drm_format_info *format; unsigned int stride; } fb; @@ -1058,7 +1116,7 @@ struct intel_fbc { const char *no_fbc_reason; }; -/** +/* * HIGH_RR is the highest eDP panel refresh rate read from EDID * LOW_RR is the lowest eDP panel refresh rate found from EDID * parsing for same resolution. @@ -1096,6 +1154,9 @@ struct i915_psr { bool psr2_support; bool aux_frame_sync; bool link_standby; + bool y_cord_support; + bool colorimetry_support; + bool alpm; }; enum intel_pch { @@ -1395,7 +1456,7 @@ struct i915_gem_mm { struct work_struct free_work; /** Usable portion of the GTT for GEM */ - unsigned long stolen_base; /* limited to low memory (32-bit) */ + phys_addr_t stolen_base; /* limited to low memory (32-bit) */ /** PPGTT used for aliasing the PPGTT with the GTT */ struct i915_hw_ppgtt *aliasing_ppgtt; @@ -1438,19 +1499,20 @@ struct drm_i915_error_state_buf { }; struct i915_error_state_file_priv { - struct drm_device *dev; + struct drm_i915_private *i915; struct drm_i915_error_state *error; }; #define I915_RESET_TIMEOUT (10 * HZ) /* 10s */ #define I915_FENCE_TIMEOUT (10 * HZ) /* 10s */ +#define I915_ENGINE_DEAD_TIMEOUT (4 * HZ) /* Seqno, head and subunits dead */ +#define I915_SEQNO_DEAD_TIMEOUT (12 * HZ) /* Seqno dead with active head */ + struct i915_gpu_error { /* For hangcheck timer */ #define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ #define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) - /* Hang gpu twice in this window and your context gets banned */ -#define DRM_I915_CTX_BAN_PERIOD DIV_ROUND_UP(8*DRM_I915_HANGCHECK_PERIOD, 1000) struct delayed_work hangcheck_work; @@ -1532,6 +1594,7 @@ struct ddi_vbt_port_info { uint8_t supports_dvi:1; uint8_t supports_hdmi:1; uint8_t supports_dp:1; + uint8_t supports_edp:1; uint8_t alternate_aux_channel; uint8_t alternate_ddc_pin; @@ -1591,6 +1654,7 @@ struct intel_vbt_data { bool present; bool active_low_pwm; u8 min_brightness; /* min_brightness/255 of max */ + u8 controller; /* brightness controller number */ enum intel_backlight_type type; } backlight; @@ -1637,24 +1701,22 @@ struct ilk_wm_values { }; struct vlv_pipe_wm { - uint16_t primary; - uint16_t sprite[2]; - uint8_t cursor; + uint16_t plane[I915_MAX_PLANES]; }; struct vlv_sr_wm { uint16_t plane; - uint8_t cursor; + uint16_t cursor; +}; + +struct vlv_wm_ddl_values { + uint8_t plane[I915_MAX_PLANES]; }; struct vlv_wm_values { struct vlv_pipe_wm pipe[3]; struct vlv_sr_wm sr; - struct { - uint8_t cursor; - uint8_t sprite[2]; - uint8_t primary; - } ddl[3]; + struct vlv_wm_ddl_values ddl[3]; uint8_t level; bool cxsr; }; @@ -1750,6 +1812,7 @@ struct intel_pipe_crc { enum intel_pipe_crc_source source; int head, tail; wait_queue_head_t wq; + int skipped; }; struct i915_frontbuffer_tracking { @@ -1795,6 +1858,201 @@ struct intel_wm_config { bool sprites_scaled; }; +struct i915_oa_format { + u32 format; + int size; +}; + +struct i915_oa_reg { + i915_reg_t addr; + u32 value; +}; + +struct i915_perf_stream; + +/** + * struct i915_perf_stream_ops - the OPs to support a specific stream type + */ +struct i915_perf_stream_ops { + /** + * @enable: Enables the collection of HW samples, either in response to + * `I915_PERF_IOCTL_ENABLE` or implicitly called when stream is opened + * without `I915_PERF_FLAG_DISABLED`. + */ + void (*enable)(struct i915_perf_stream *stream); + + /** + * @disable: Disables the collection of HW samples, either in response + * to `I915_PERF_IOCTL_DISABLE` or implicitly called before destroying + * the stream. + */ + void (*disable)(struct i915_perf_stream *stream); + + /** + * @poll_wait: Call poll_wait, passing a wait queue that will be woken + * once there is something ready to read() for the stream + */ + void (*poll_wait)(struct i915_perf_stream *stream, + struct file *file, + poll_table *wait); + + /** + * @wait_unlocked: For handling a blocking read, wait until there is + * something to ready to read() for the stream. E.g. wait on the same + * wait queue that would be passed to poll_wait(). + */ + int (*wait_unlocked)(struct i915_perf_stream *stream); + + /** + * @read: Copy buffered metrics as records to userspace + * **buf**: the userspace, destination buffer + * **count**: the number of bytes to copy, requested by userspace + * **offset**: zero at the start of the read, updated as the read + * proceeds, it represents how many bytes have been copied so far and + * the buffer offset for copying the next record. + * + * Copy as many buffered i915 perf samples and records for this stream + * to userspace as will fit in the given buffer. + * + * Only write complete records; returning -%ENOSPC if there isn't room + * for a complete record. + * + * Return any error condition that results in a short read such as + * -%ENOSPC or -%EFAULT, even though these may be squashed before + * returning to userspace. + */ + int (*read)(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset); + + /** + * @destroy: Cleanup any stream specific resources. + * + * The stream will always be disabled before this is called. + */ + void (*destroy)(struct i915_perf_stream *stream); +}; + +/** + * struct i915_perf_stream - state for a single open stream FD + */ +struct i915_perf_stream { + /** + * @dev_priv: i915 drm device + */ + struct drm_i915_private *dev_priv; + + /** + * @link: Links the stream into ``&drm_i915_private->streams`` + */ + struct list_head link; + + /** + * @sample_flags: Flags representing the `DRM_I915_PERF_PROP_SAMPLE_*` + * properties given when opening a stream, representing the contents + * of a single sample as read() by userspace. + */ + u32 sample_flags; + + /** + * @sample_size: Considering the configured contents of a sample + * combined with the required header size, this is the total size + * of a single sample record. + */ + int sample_size; + + /** + * @ctx: %NULL if measuring system-wide across all contexts or a + * specific context that is being monitored. + */ + struct i915_gem_context *ctx; + + /** + * @enabled: Whether the stream is currently enabled, considering + * whether the stream was opened in a disabled state and based + * on `I915_PERF_IOCTL_ENABLE` and `I915_PERF_IOCTL_DISABLE` calls. + */ + bool enabled; + + /** + * @ops: The callbacks providing the implementation of this specific + * type of configured stream. + */ + const struct i915_perf_stream_ops *ops; +}; + +/** + * struct i915_oa_ops - Gen specific implementation of an OA unit stream + */ +struct i915_oa_ops { + /** + * @init_oa_buffer: Resets the head and tail pointers of the + * circular buffer for periodic OA reports. + * + * Called when first opening a stream for OA metrics, but also may be + * called in response to an OA buffer overflow or other error + * condition. + * + * Note it may be necessary to clear the full OA buffer here as part of + * maintaining the invariable that new reports must be written to + * zeroed memory for us to be able to reliable detect if an expected + * report has not yet landed in memory. (At least on Haswell the OA + * buffer tail pointer is not synchronized with reports being visible + * to the CPU) + */ + void (*init_oa_buffer)(struct drm_i915_private *dev_priv); + + /** + * @enable_metric_set: Applies any MUX configuration to set up the + * Boolean and Custom (B/C) counters that are part of the counter + * reports being sampled. May apply system constraints such as + * disabling EU clock gating as required. + */ + int (*enable_metric_set)(struct drm_i915_private *dev_priv); + + /** + * @disable_metric_set: Remove system constraints associated with using + * the OA unit. + */ + void (*disable_metric_set)(struct drm_i915_private *dev_priv); + + /** + * @oa_enable: Enable periodic sampling + */ + void (*oa_enable)(struct drm_i915_private *dev_priv); + + /** + * @oa_disable: Disable periodic sampling + */ + void (*oa_disable)(struct drm_i915_private *dev_priv); + + /** + * @read: Copy data from the circular OA buffer into a given userspace + * buffer. + */ + int (*read)(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset); + + /** + * @oa_buffer_is_empty: Check if OA buffer empty (false positives OK) + * + * This is either called via fops or the poll check hrtimer (atomic + * ctx) without any locks taken. + * + * It's safe to read OA config state here unlocked, assuming that this + * is only called while the stream is enabled, while the global OA + * configuration can't be modified. + * + * Efficiency is more important than avoiding some false positives + * here, which will be handled gracefully - likely resulting in an + * %EAGAIN error for userspace. + */ + bool (*oa_buffer_is_empty)(struct drm_i915_private *dev_priv); +}; + struct drm_i915_private { struct drm_device drm; @@ -1815,6 +2073,7 @@ struct drm_i915_private { struct intel_gvt *gvt; + struct intel_huc huc; struct intel_guc guc; struct intel_csr csr; @@ -1898,7 +2157,14 @@ struct drm_i915_private { unsigned int fsb_freq, mem_freq, is_ddr3; unsigned int skl_preferred_vco_freq; - unsigned int cdclk_freq, max_cdclk_freq, atomic_cdclk_freq; + unsigned int cdclk_freq, max_cdclk_freq; + + /* + * For reading holding any crtc lock is sufficient, + * for writing must hold all of them. + */ + unsigned int atomic_cdclk_freq; + unsigned int max_dotclk_freq; unsigned int rawclk_freq; unsigned int hpll_freq; @@ -2051,6 +2317,9 @@ struct drm_i915_private { } sagv_status; struct { + /* protects DSPARB registers on pre-g4x/vlv/chv */ + spinlock_t dsparb_lock; + /* * Raw watermark latency values: * in 0.1us units for WM0, @@ -2095,6 +2364,54 @@ struct drm_i915_private { struct i915_runtime_pm pm; + struct { + bool initialized; + + struct kobject *metrics_kobj; + struct ctl_table_header *sysctl_header; + + struct mutex lock; + struct list_head streams; + + spinlock_t hook_lock; + + struct { + struct i915_perf_stream *exclusive_stream; + + u32 specific_ctx_id; + + struct hrtimer poll_check_timer; + wait_queue_head_t poll_wq; + bool pollin; + + bool periodic; + int period_exponent; + int timestamp_frequency; + + int tail_margin; + + int metrics_set; + + const struct i915_oa_reg *mux_regs; + int mux_regs_len; + const struct i915_oa_reg *b_counter_regs; + int b_counter_regs_len; + + struct { + struct i915_vma *vma; + u8 *vaddr; + int format; + int format_size; + } oa_buffer; + + u32 gen7_latched_oastatus1; + + struct i915_oa_ops ops; + const struct i915_oa_format *oa_formats; + int n_builtin_sets; + } oa; + } perf; + /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */ struct { void (*resume)(struct drm_i915_private *); @@ -2137,9 +2454,17 @@ struct drm_i915_private { /* perform PHY state sanity checks? */ bool chv_phy_assert[2]; + bool ipc_enabled; + /* Used to save the pipe-to-encoder mapping for audio */ struct intel_encoder *av_enc_map[I915_MAX_PIPES]; + /* necessary resource sharing with HDMI LPE audio driver. */ + struct { + struct platform_device *platdev; + int irq; + } lpe_audio; + /* * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch * will be rejected. Instead look for a better place. @@ -2285,102 +2610,6 @@ static inline struct scatterlist *__sg_next(struct scatterlist *sg) (((__iter).curr += PAGE_SIZE) < (__iter).max) || \ ((__iter) = __sgt_iter(__sg_next((__iter).sgp), false), 0)) -/* - * A command that requires special handling by the command parser. - */ -struct drm_i915_cmd_descriptor { - /* - * Flags describing how the command parser processes the command. - * - * CMD_DESC_FIXED: The command has a fixed length if this is set, - * a length mask if not set - * CMD_DESC_SKIP: The command is allowed but does not follow the - * standard length encoding for the opcode range in - * which it falls - * CMD_DESC_REJECT: The command is never allowed - * CMD_DESC_REGISTER: The command should be checked against the - * register whitelist for the appropriate ring - * CMD_DESC_MASTER: The command is allowed if the submitting process - * is the DRM master - */ - u32 flags; -#define CMD_DESC_FIXED (1<<0) -#define CMD_DESC_SKIP (1<<1) -#define CMD_DESC_REJECT (1<<2) -#define CMD_DESC_REGISTER (1<<3) -#define CMD_DESC_BITMASK (1<<4) -#define CMD_DESC_MASTER (1<<5) - - /* - * The command's unique identification bits and the bitmask to get them. - * This isn't strictly the opcode field as defined in the spec and may - * also include type, subtype, and/or subop fields. - */ - struct { - u32 value; - u32 mask; - } cmd; - - /* - * The command's length. The command is either fixed length (i.e. does - * not include a length field) or has a length field mask. The flag - * CMD_DESC_FIXED indicates a fixed length. Otherwise, the command has - * a length mask. All command entries in a command table must include - * length information. - */ - union { - u32 fixed; - u32 mask; - } length; - - /* - * Describes where to find a register address in the command to check - * against the ring's register whitelist. Only valid if flags has the - * CMD_DESC_REGISTER bit set. - * - * A non-zero step value implies that the command may access multiple - * registers in sequence (e.g. LRI), in that case step gives the - * distance in dwords between individual offset fields. - */ - struct { - u32 offset; - u32 mask; - u32 step; - } reg; - -#define MAX_CMD_DESC_BITMASKS 3 - /* - * Describes command checks where a particular dword is masked and - * compared against an expected value. If the command does not match - * the expected value, the parser rejects it. Only valid if flags has - * the CMD_DESC_BITMASK bit set. Only entries where mask is non-zero - * are valid. - * - * If the check specifies a non-zero condition_mask then the parser - * only performs the check when the bits specified by condition_mask - * are non-zero. - */ - struct { - u32 offset; - u32 mask; - u32 expected; - u32 condition_offset; - u32 condition_mask; - } bits[MAX_CMD_DESC_BITMASKS]; -}; - -/* - * A table of commands requiring special handling by the command parser. - * - * Each engine has an array of tables. Each table consists of an array of - * command descriptors, which must be sorted with command opcodes in - * ascending order. - */ -struct drm_i915_cmd_table { - const struct drm_i915_cmd_descriptor *table; - int count; -}; - static inline const struct intel_device_info * intel_info(const struct drm_i915_private *dev_priv) { @@ -2422,34 +2651,36 @@ intel_info(const struct drm_i915_private *dev_priv) #define IS_REVID(p, since, until) \ (INTEL_REVID(p) >= (since) && INTEL_REVID(p) <= (until)) -#define IS_I830(dev_priv) (INTEL_DEVID(dev_priv) == 0x3577) -#define IS_845G(dev_priv) (INTEL_DEVID(dev_priv) == 0x2562) -#define IS_I85X(dev_priv) ((dev_priv)->info.is_i85x) -#define IS_I865G(dev_priv) (INTEL_DEVID(dev_priv) == 0x2572) -#define IS_I915G(dev_priv) ((dev_priv)->info.is_i915g) -#define IS_I915GM(dev_priv) (INTEL_DEVID(dev_priv) == 0x2592) -#define IS_I945G(dev_priv) (INTEL_DEVID(dev_priv) == 0x2772) -#define IS_I945GM(dev_priv) ((dev_priv)->info.is_i945gm) -#define IS_BROADWATER(dev_priv) ((dev_priv)->info.is_broadwater) -#define IS_CRESTLINE(dev_priv) ((dev_priv)->info.is_crestline) -#define IS_GM45(dev_priv) (INTEL_DEVID(dev_priv) == 0x2A42) -#define IS_G4X(dev_priv) ((dev_priv)->info.is_g4x) +#define IS_I830(dev_priv) ((dev_priv)->info.platform == INTEL_I830) +#define IS_I845G(dev_priv) ((dev_priv)->info.platform == INTEL_I845G) +#define IS_I85X(dev_priv) ((dev_priv)->info.platform == INTEL_I85X) +#define IS_I865G(dev_priv) ((dev_priv)->info.platform == INTEL_I865G) +#define IS_I915G(dev_priv) ((dev_priv)->info.platform == INTEL_I915G) +#define IS_I915GM(dev_priv) ((dev_priv)->info.platform == INTEL_I915GM) +#define IS_I945G(dev_priv) ((dev_priv)->info.platform == INTEL_I945G) +#define IS_I945GM(dev_priv) ((dev_priv)->info.platform == INTEL_I945GM) +#define IS_I965G(dev_priv) ((dev_priv)->info.platform == INTEL_I965G) +#define IS_I965GM(dev_priv) ((dev_priv)->info.platform == INTEL_I965GM) +#define IS_G45(dev_priv) ((dev_priv)->info.platform == INTEL_G45) +#define IS_GM45(dev_priv) ((dev_priv)->info.platform == INTEL_GM45) +#define IS_G4X(dev_priv) (IS_G45(dev_priv) || IS_GM45(dev_priv)) #define IS_PINEVIEW_G(dev_priv) (INTEL_DEVID(dev_priv) == 0xa001) #define IS_PINEVIEW_M(dev_priv) (INTEL_DEVID(dev_priv) == 0xa011) -#define IS_PINEVIEW(dev_priv) ((dev_priv)->info.is_pineview) -#define IS_G33(dev_priv) ((dev_priv)->info.is_g33) +#define IS_PINEVIEW(dev_priv) ((dev_priv)->info.platform == INTEL_PINEVIEW) +#define IS_G33(dev_priv) ((dev_priv)->info.platform == INTEL_G33) #define IS_IRONLAKE_M(dev_priv) (INTEL_DEVID(dev_priv) == 0x0046) -#define IS_IVYBRIDGE(dev_priv) ((dev_priv)->info.is_ivybridge) +#define IS_IVYBRIDGE(dev_priv) ((dev_priv)->info.platform == INTEL_IVYBRIDGE) #define IS_IVB_GT1(dev_priv) (INTEL_DEVID(dev_priv) == 0x0156 || \ INTEL_DEVID(dev_priv) == 0x0152 || \ INTEL_DEVID(dev_priv) == 0x015a) -#define IS_VALLEYVIEW(dev_priv) ((dev_priv)->info.is_valleyview) -#define IS_CHERRYVIEW(dev_priv) ((dev_priv)->info.is_cherryview) -#define IS_HASWELL(dev_priv) ((dev_priv)->info.is_haswell) -#define IS_BROADWELL(dev_priv) ((dev_priv)->info.is_broadwell) -#define IS_SKYLAKE(dev_priv) ((dev_priv)->info.is_skylake) -#define IS_BROXTON(dev_priv) ((dev_priv)->info.is_broxton) -#define IS_KABYLAKE(dev_priv) ((dev_priv)->info.is_kabylake) +#define IS_VALLEYVIEW(dev_priv) ((dev_priv)->info.platform == INTEL_VALLEYVIEW) +#define IS_CHERRYVIEW(dev_priv) ((dev_priv)->info.platform == INTEL_CHERRYVIEW) +#define IS_HASWELL(dev_priv) ((dev_priv)->info.platform == INTEL_HASWELL) +#define IS_BROADWELL(dev_priv) ((dev_priv)->info.platform == INTEL_BROADWELL) +#define IS_SKYLAKE(dev_priv) ((dev_priv)->info.platform == INTEL_SKYLAKE) +#define IS_BROXTON(dev_priv) ((dev_priv)->info.platform == INTEL_BROXTON) +#define IS_KABYLAKE(dev_priv) ((dev_priv)->info.platform == INTEL_KABYLAKE) +#define IS_GEMINILAKE(dev_priv) ((dev_priv)->info.platform == INTEL_GEMINILAKE) #define IS_MOBILE(dev_priv) ((dev_priv)->info.is_mobile) #define IS_HSW_EARLY_SDV(dev_priv) (IS_HASWELL(dev_priv) && \ (INTEL_DEVID(dev_priv) & 0xFF00) == 0x0C00) @@ -2506,6 +2737,7 @@ intel_info(const struct drm_i915_private *dev_priv) #define BXT_REVID_A0 0x0 #define BXT_REVID_A1 0x1 #define BXT_REVID_B0 0x3 +#define BXT_REVID_B_LAST 0x8 #define BXT_REVID_C0 0x9 #define IS_BXT_REVID(dev_priv, since, until) \ @@ -2535,6 +2767,9 @@ intel_info(const struct drm_i915_private *dev_priv) #define IS_GEN8(dev_priv) (!!((dev_priv)->info.gen_mask & BIT(7))) #define IS_GEN9(dev_priv) (!!((dev_priv)->info.gen_mask & BIT(8))) +#define IS_GEN9_LP(dev_priv) (IS_GEN9(dev_priv) && INTEL_INFO(dev_priv)->is_lp) +#define IS_LP(dev_priv) (INTEL_INFO(dev_priv)->is_lp) + #define ENGINE_MASK(id) BIT(id) #define RENDER_RING ENGINE_MASK(RCS) #define BSD_RING ENGINE_MASK(VCS) @@ -2571,7 +2806,7 @@ intel_info(const struct drm_i915_private *dev_priv) ((dev_priv)->info.overlay_needs_physical) /* Early gen2 have a totally busted CS tlb and require pinned batches. */ -#define HAS_BROKEN_CS_TLB(dev_priv) (IS_I830(dev_priv) || IS_845G(dev_priv)) +#define HAS_BROKEN_CS_TLB(dev_priv) (IS_I830(dev_priv) || IS_I845G(dev_priv)) /* WaRsDisableCoarsePowerGating:skl,bxt */ #define NEEDS_WaRsDisableCoarsePowerGating(dev_priv) \ @@ -2624,6 +2859,7 @@ intel_info(const struct drm_i915_private *dev_priv) #define HAS_GUC(dev_priv) ((dev_priv)->info.has_guc) #define HAS_GUC_UCODE(dev_priv) (HAS_GUC(dev_priv)) #define HAS_GUC_SCHED(dev_priv) (HAS_GUC(dev_priv)) +#define HAS_HUC_UCODE(dev_priv) (HAS_GUC(dev_priv)) #define HAS_RESOURCE_STREAMER(dev_priv) ((dev_priv)->info.has_resource_streamer) @@ -2680,9 +2916,6 @@ static inline bool intel_scanout_needs_vtd_wa(struct drm_i915_private *dev_priv) return false; } -extern int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state); -extern int i915_resume_switcheroo(struct drm_device *dev); - int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, int enable_ppgtt); @@ -2885,10 +3118,10 @@ int i915_gem_throttle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int i915_gem_set_tiling(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int i915_gem_get_tiling(struct drm_device *dev, void *data, - struct drm_file *file_priv); +int i915_gem_set_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int i915_gem_get_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); void i915_gem_init_userptr(struct drm_i915_private *dev_priv); int i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file); @@ -2896,23 +3129,37 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int i915_gem_load_init(struct drm_device *dev); -void i915_gem_load_cleanup(struct drm_device *dev); +int i915_gem_load_init(struct drm_i915_private *dev_priv); +void i915_gem_load_cleanup(struct drm_i915_private *dev_priv); void i915_gem_load_init_fences(struct drm_i915_private *dev_priv); int i915_gem_freeze(struct drm_i915_private *dev_priv); int i915_gem_freeze_late(struct drm_i915_private *dev_priv); -void *i915_gem_object_alloc(struct drm_device *dev); +void *i915_gem_object_alloc(struct drm_i915_private *dev_priv); void i915_gem_object_free(struct drm_i915_gem_object *obj); void i915_gem_object_init(struct drm_i915_gem_object *obj, const struct drm_i915_gem_object_ops *ops); -struct drm_i915_gem_object *i915_gem_object_create(struct drm_device *dev, - u64 size); -struct drm_i915_gem_object *i915_gem_object_create_from_data( - struct drm_device *dev, const void *data, size_t size); +struct drm_i915_gem_object * +i915_gem_object_create(struct drm_i915_private *dev_priv, u64 size); +struct drm_i915_gem_object * +i915_gem_object_create_from_data(struct drm_i915_private *dev_priv, + const void *data, size_t size); void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file); void i915_gem_free_object(struct drm_gem_object *obj); +static inline void i915_gem_drain_freed_objects(struct drm_i915_private *i915) +{ + /* A single pass should suffice to release all the freed objects (along + * most call paths) , but be a little more paranoid in that freeing + * the objects does take a little amount of time, during which the rcu + * callbacks could have added new objects into the freed list, and + * armed the work again. + */ + do { + rcu_barrier(); + } while (flush_work(&i915->mm.free_work)); +} + struct i915_vma * __must_check i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, const struct i915_ggtt_view *view, @@ -2982,7 +3229,6 @@ __i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj) GEM_BUG_ON(!obj->mm.pages); atomic_dec(&obj->mm.pages_pin_count); - GEM_BUG_ON(atomic_read(&obj->mm.pages_pin_count) < obj->bind_count); } static inline void @@ -3007,8 +3253,8 @@ enum i915_map_type { /** * i915_gem_object_pin_map - return a contiguous mapping of the entire object - * @obj - the object to map into kernel address space - * @type - the type of mapping, used to select pgprot_t + * @obj: the object to map into kernel address space + * @type: the type of mapping, used to select pgprot_t * * Calls i915_gem_object_pin_pages() to prevent reaping of the object's * pages and then returns a contiguous mapping of the backing storage into @@ -3026,7 +3272,7 @@ void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj, /** * i915_gem_object_unpin_map - releases an earlier mapping - * @obj - the object to unmap + * @obj: the object to unmap * * After pinning the object and mapping its pages, once you are finished * with your access, call i915_gem_object_unpin_map() to release the pin @@ -3094,18 +3340,19 @@ static inline u32 i915_reset_count(struct i915_gpu_error *error) return READ_ONCE(error->reset_count); } -void i915_gem_reset(struct drm_i915_private *dev_priv); +int i915_gem_reset_prepare(struct drm_i915_private *dev_priv); +void i915_gem_reset_finish(struct drm_i915_private *dev_priv); void i915_gem_set_wedged(struct drm_i915_private *dev_priv); void i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force); -int __must_check i915_gem_init(struct drm_device *dev); -int __must_check i915_gem_init_hw(struct drm_device *dev); +int __must_check i915_gem_init(struct drm_i915_private *dev_priv); +int __must_check i915_gem_init_hw(struct drm_i915_private *dev_priv); void i915_gem_init_swizzling(struct drm_i915_private *dev_priv); -void i915_gem_cleanup_engines(struct drm_device *dev); +void i915_gem_cleanup_engines(struct drm_i915_private *dev_priv); int __must_check i915_gem_wait_for_idle(struct drm_i915_private *dev_priv, unsigned int flags); -int __must_check i915_gem_suspend(struct drm_device *dev); -void i915_gem_resume(struct drm_device *dev); -int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +int __must_check i915_gem_suspend(struct drm_i915_private *dev_priv); +void i915_gem_resume(struct drm_i915_private *dev_priv); +int i915_gem_fault(struct vm_fault *vmf); int i915_gem_object_wait(struct drm_i915_gem_object *obj, unsigned int flags, long timeout, @@ -3130,11 +3377,6 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int i915_gem_open(struct drm_device *dev, struct drm_file *file); void i915_gem_release(struct drm_device *dev, struct drm_file *file); -u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv, u64 size, - int tiling_mode); -u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size, - int tiling_mode, bool fenced); - int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); @@ -3144,33 +3386,17 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, struct dma_buf *i915_gem_prime_export(struct drm_device *dev, struct drm_gem_object *gem_obj, int flags); -struct i915_vma * -i915_gem_obj_to_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *view); - -struct i915_vma * -i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *view); - static inline struct i915_hw_ppgtt * i915_vm_to_ppgtt(struct i915_address_space *vm) { return container_of(vm, struct i915_hw_ppgtt, base); } -static inline struct i915_vma * -i915_gem_object_to_ggtt(struct drm_i915_gem_object *obj, - const struct i915_ggtt_view *view) -{ - return i915_gem_obj_to_vma(obj, &to_i915(obj->base.dev)->ggtt.base, view); -} - /* i915_gem_fence_reg.c */ int __must_check i915_vma_get_fence(struct i915_vma *vma); int __must_check i915_vma_put_fence(struct i915_vma *vma); +void i915_gem_revoke_fences(struct drm_i915_private *dev_priv); void i915_gem_restore_fences(struct drm_i915_private *dev_priv); void i915_gem_detect_bit_6_swizzle(struct drm_i915_private *dev_priv); @@ -3179,23 +3405,6 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj, void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj, struct sg_table *pages); -/* i915_gem_context.c */ -int __must_check i915_gem_context_init(struct drm_device *dev); -void i915_gem_context_lost(struct drm_i915_private *dev_priv); -void i915_gem_context_fini(struct drm_device *dev); -int i915_gem_context_open(struct drm_device *dev, struct drm_file *file); -void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); -int i915_switch_context(struct drm_i915_gem_request *req); -int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv); -struct i915_vma * -i915_gem_context_pin_legacy(struct i915_gem_context *ctx, - unsigned int flags); -void i915_gem_context_free(struct kref *ctx_ref); -struct drm_i915_gem_object * -i915_gem_alloc_context_obj(struct drm_device *dev, size_t size); -struct i915_gem_context * -i915_gem_context_create_gvt(struct drm_device *dev); - static inline struct i915_gem_context * i915_gem_context_lookup(struct drm_i915_file_private *file_priv, u32 id) { @@ -3223,6 +3432,14 @@ static inline void i915_gem_context_put(struct i915_gem_context *ctx) kref_put(&ctx->ref, i915_gem_context_free); } +static inline void i915_gem_context_put_unlocked(struct i915_gem_context *ctx) +{ + struct mutex *lock = &ctx->i915->drm.struct_mutex; + + if (kref_put_mutex(&ctx->ref, i915_gem_context_free, lock)) + mutex_unlock(lock); +} + static inline struct intel_timeline * i915_gem_context_lookup_timeline(struct i915_gem_context *ctx, struct intel_engine_cs *engine) @@ -3233,21 +3450,8 @@ i915_gem_context_lookup_timeline(struct i915_gem_context *ctx, return &vm->timeline.engine[engine->id]; } -static inline bool i915_gem_context_is_default(const struct i915_gem_context *c) -{ - return c->user_handle == DEFAULT_CONTEXT_HANDLE; -} - -int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file); -int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, - struct drm_file *file); -int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data, - struct drm_file *file); +int i915_perf_open_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct i915_address_space *vm, @@ -3255,7 +3459,9 @@ int __must_check i915_gem_evict_something(struct i915_address_space *vm, unsigned cache_level, u64 start, u64 end, unsigned flags); -int __must_check i915_gem_evict_for_vma(struct i915_vma *target); +int __must_check i915_gem_evict_for_node(struct i915_address_space *vm, + struct drm_mm_node *node, + unsigned int flags); int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle); /* belongs in i915_gem_gtt.h */ @@ -3279,9 +3485,9 @@ void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv, int i915_gem_init_stolen(struct drm_i915_private *dev_priv); void i915_gem_cleanup_stolen(struct drm_device *dev); struct drm_i915_gem_object * -i915_gem_object_create_stolen(struct drm_device *dev, u32 size); +i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, u32 size); struct drm_i915_gem_object * -i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, +i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv, u32 stolen_offset, u32 gtt_offset, u32 size); @@ -3289,7 +3495,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, /* i915_gem_internal.c */ struct drm_i915_gem_object * i915_gem_object_create_internal(struct drm_i915_private *dev_priv, - unsigned int size); + phys_addr_t size); /* i915_gem_shrinker.c */ unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv, @@ -3314,6 +3520,11 @@ static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_objec i915_gem_object_is_tiled(obj); } +u32 i915_gem_fence_size(struct drm_i915_private *dev_priv, u32 size, + unsigned int tiling, unsigned int stride); +u32 i915_gem_fence_alignment(struct drm_i915_private *dev_priv, u32 size, + unsigned int tiling, unsigned int stride); + /* i915_debugfs.c */ #ifdef CONFIG_DEBUG_FS int i915_debugfs_register(struct drm_i915_private *dev_priv); @@ -3349,7 +3560,7 @@ void i915_capture_error_state(struct drm_i915_private *dev_priv, void i915_error_state_get(struct drm_device *dev, struct i915_error_state_file_priv *error_priv); void i915_error_state_put(struct i915_error_state_file_priv *error_priv); -void i915_destroy_error_state(struct drm_device *dev); +void i915_destroy_error_state(struct drm_i915_private *dev_priv); #else @@ -3359,7 +3570,7 @@ static inline void i915_capture_error_state(struct drm_i915_private *dev_priv, { } -static inline void i915_destroy_error_state(struct drm_device *dev) +static inline void i915_destroy_error_state(struct drm_i915_private *dev_priv) { } @@ -3371,7 +3582,6 @@ const char *i915_cache_level_str(struct drm_i915_private *i915, int type); int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv); void intel_engine_init_cmd_parser(struct intel_engine_cs *engine); void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine); -bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine); int intel_engine_cmd_parser(struct intel_engine_cs *engine, struct drm_i915_gem_object *batch_obj, struct drm_i915_gem_object *shadow_batch_obj, @@ -3379,17 +3589,31 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine, u32 batch_len, bool is_master); +/* i915_perf.c */ +extern void i915_perf_init(struct drm_i915_private *dev_priv); +extern void i915_perf_fini(struct drm_i915_private *dev_priv); +extern void i915_perf_register(struct drm_i915_private *dev_priv); +extern void i915_perf_unregister(struct drm_i915_private *dev_priv); + /* i915_suspend.c */ -extern int i915_save_state(struct drm_device *dev); -extern int i915_restore_state(struct drm_device *dev); +extern int i915_save_state(struct drm_i915_private *dev_priv); +extern int i915_restore_state(struct drm_i915_private *dev_priv); /* i915_sysfs.c */ void i915_setup_sysfs(struct drm_i915_private *dev_priv); void i915_teardown_sysfs(struct drm_i915_private *dev_priv); +/* intel_lpe_audio.c */ +int intel_lpe_audio_init(struct drm_i915_private *dev_priv); +void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv); +void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv); +void intel_lpe_audio_notify(struct drm_i915_private *dev_priv, + void *eld, int port, int pipe, int tmds_clk_speed, + bool dp_output, int link_rate); + /* intel_i2c.c */ -extern int intel_setup_gmbus(struct drm_device *dev); -extern void intel_teardown_gmbus(struct drm_device *dev); +extern int intel_setup_gmbus(struct drm_i915_private *dev_priv); +extern void intel_teardown_gmbus(struct drm_i915_private *dev_priv); extern bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv, unsigned int pin); @@ -3401,7 +3625,7 @@ static inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter) { return container_of(adapter, struct intel_gmbus, adapter)->force_bit; } -extern void intel_i2c_reset(struct drm_device *dev); +extern void intel_i2c_reset(struct drm_i915_private *dev_priv); /* intel_bios.c */ int intel_bios_init(struct drm_i915_private *dev_priv); @@ -3468,6 +3692,7 @@ mkwrite_device_info(struct drm_i915_private *dev_priv) return (struct intel_device_info *)&dev_priv->info; } +const char *intel_platform_name(enum intel_platform platform); void intel_device_info_runtime_init(struct drm_i915_private *dev_priv); void intel_device_info_dump(struct drm_i915_private *dev_priv); @@ -3484,9 +3709,9 @@ extern void intel_display_resume(struct drm_device *dev); extern void i915_redisable_vga(struct drm_i915_private *dev_priv); extern void i915_redisable_vga_power_on(struct drm_i915_private *dev_priv); extern bool ironlake_set_drps(struct drm_i915_private *dev_priv, u8 val); -extern void intel_init_pch_refclk(struct drm_device *dev); +extern void intel_init_pch_refclk(struct drm_i915_private *dev_priv); extern void intel_set_rps(struct drm_i915_private *dev_priv, u8 val); -extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, +extern bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable); int i915_reg_read_ioctl(struct drm_device *dev, void *data, @@ -3531,7 +3756,7 @@ u32 vlv_flisdsi_read(struct drm_i915_private *dev_priv, u32 reg); void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val); /* intel_dpio_phy.c */ -void bxt_port_to_phy_channel(enum port port, +void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port, enum dpio_phy *phy, enum dpio_channel *ch); void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv, enum port port, u32 margin, u32 scale, @@ -3798,29 +4023,25 @@ __i915_request_irq_complete(struct drm_i915_gem_request *req) void i915_memcpy_init_early(struct drm_i915_private *dev_priv); bool i915_memcpy_from_wc(void *dst, const void *src, unsigned long len); +/* The movntdqa instructions used for memcpy-from-wc require 16-byte alignment, + * as well as SSE4.1 support. i915_memcpy_from_wc() will report if it cannot + * perform the operation. To check beforehand, pass in the parameters to + * to i915_can_memcpy_from_wc() - since we only care about the low 4 bits, + * you only need to pass in the minor offsets, page-aligned pointers are + * always valid. + * + * For just checking for SSE4.1, in the foreknowledge that the future use + * will be correctly aligned, just use i915_has_memcpy_from_wc(). + */ +#define i915_can_memcpy_from_wc(dst, src, len) \ + i915_memcpy_from_wc((void *)((unsigned long)(dst) | (unsigned long)(src) | (len)), NULL, 0) + +#define i915_has_memcpy_from_wc() \ + i915_memcpy_from_wc(NULL, NULL, 0) + /* i915_mm.c */ int remap_io_mapping(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, struct io_mapping *iomap); -#define ptr_mask_bits(ptr) ({ \ - unsigned long __v = (unsigned long)(ptr); \ - (typeof(ptr))(__v & PAGE_MASK); \ -}) - -#define ptr_unpack_bits(ptr, bits) ({ \ - unsigned long __v = (unsigned long)(ptr); \ - (bits) = __v & ~PAGE_MASK; \ - (typeof(ptr))(__v & PAGE_MASK); \ -}) - -#define ptr_pack_bits(ptr, bits) \ - ((typeof(ptr))((unsigned long)(ptr) | (bits))) - -#define fetch_and_zero(ptr) ({ \ - typeof(*ptr) __T = *(ptr); \ - *(ptr) = (typeof(*ptr))0; \ - __T; \ -}) - #endif diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 24b5b046754b..6908123162d1 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -38,6 +38,7 @@ #include <linux/reservation.h> #include <linux/shmem_fs.h> #include <linux/slab.h> +#include <linux/stop_machine.h> #include <linux/swap.h> #include <linux/pci.h> #include <linux/dma-buf.h> @@ -68,11 +69,10 @@ insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size) { memset(node, 0, sizeof(*node)); - return drm_mm_insert_node_in_range_generic(&ggtt->base.mm, node, - size, 0, -1, - 0, ggtt->mappable_end, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + return drm_mm_insert_node_in_range(&ggtt->base.mm, node, + size, 0, I915_COLOR_UNEVICTABLE, + 0, ggtt->mappable_end, + DRM_MM_INSERT_LOW); } static void @@ -440,7 +440,7 @@ i915_gem_object_wait_reservation(struct reservation_object *resv, timeout = i915_gem_object_wait_fence(shared[i], flags, timeout, rps); - if (timeout <= 0) + if (timeout < 0) break; dma_fence_put(shared[i]); @@ -453,7 +453,7 @@ i915_gem_object_wait_reservation(struct reservation_object *resv, excl = reservation_object_get_excl_rcu(resv); } - if (excl && timeout > 0) + if (excl && timeout >= 0) timeout = i915_gem_object_wait_fence(excl, flags, timeout, rps); dma_fence_put(excl); @@ -612,9 +612,8 @@ i915_gem_phys_pwrite(struct drm_i915_gem_object *obj, return 0; } -void *i915_gem_object_alloc(struct drm_device *dev) +void *i915_gem_object_alloc(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); return kmem_cache_zalloc(dev_priv->objects, GFP_KERNEL); } @@ -626,7 +625,7 @@ void i915_gem_object_free(struct drm_i915_gem_object *obj) static int i915_gem_create(struct drm_file *file, - struct drm_device *dev, + struct drm_i915_private *dev_priv, uint64_t size, uint32_t *handle_p) { @@ -639,7 +638,7 @@ i915_gem_create(struct drm_file *file, return -EINVAL; /* Allocate the new object */ - obj = i915_gem_object_create(dev, size); + obj = i915_gem_object_create(dev_priv, size); if (IS_ERR(obj)) return PTR_ERR(obj); @@ -661,7 +660,7 @@ i915_gem_dumb_create(struct drm_file *file, /* have to work out size/pitch and return them */ args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 64); args->size = args->pitch * args->height; - return i915_gem_create(file, dev, + return i915_gem_create(file, to_i915(dev), args->size, &args->handle); } @@ -675,11 +674,12 @@ int i915_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_create *args = data; - i915_gem_flush_free_objects(to_i915(dev)); + i915_gem_flush_free_objects(dev_priv); - return i915_gem_create(file, dev, + return i915_gem_create(file, dev_priv, args->size, &args->handle); } @@ -1114,8 +1114,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, return -ENOENT; /* Bounds check source. */ - if (args->offset > obj->base.size || - args->size > obj->base.size - args->offset) { + if (range_overflows_t(u64, args->offset, args->size, obj->base.size)) { ret = -EINVAL; goto out; } @@ -1428,8 +1427,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, return -ENOENT; /* Bounds check destination. */ - if (args->offset > obj->base.size || - args->size > obj->base.size - args->offset) { + if (range_overflows_t(u64, args->offset, args->size, obj->base.size)) { ret = -EINVAL; goto err; } @@ -1491,7 +1489,7 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj) list_for_each_entry(vma, &obj->vma_list, obj_link) { if (!i915_vma_is_ggtt(vma)) - continue; + break; if (i915_vma_is_active(vma)) continue; @@ -1696,12 +1694,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, static unsigned int tile_row_pages(struct drm_i915_gem_object *obj) { - u64 size; - - size = i915_gem_object_get_stride(obj); - size *= i915_gem_object_get_tiling(obj) == I915_TILING_Y ? 32 : 8; - - return size >> PAGE_SHIFT; + return i915_gem_object_get_tile_row_size(obj) >> PAGE_SHIFT; } /** @@ -1754,9 +1747,31 @@ int i915_gem_mmap_gtt_version(void) return 1; } +static inline struct i915_ggtt_view +compute_partial_view(struct drm_i915_gem_object *obj, + pgoff_t page_offset, + unsigned int chunk) +{ + struct i915_ggtt_view view; + + if (i915_gem_object_is_tiled(obj)) + chunk = roundup(chunk, tile_row_pages(obj)); + + view.type = I915_GGTT_VIEW_PARTIAL; + view.partial.offset = rounddown(page_offset, chunk); + view.partial.size = + min_t(unsigned int, chunk, + (obj->base.size >> PAGE_SHIFT) - view.partial.offset); + + /* If the partial covers the entire object, just create a normal VMA. */ + if (chunk >= obj->base.size >> PAGE_SHIFT) + view.type = I915_GGTT_VIEW_NORMAL; + + return view; +} + /** * i915_gem_fault - fault a page into the GTT - * @area: CPU VMA in question * @vmf: fault info * * The fault handler is set up by drm_gem_mmap() when a object is GTT mapped @@ -1773,9 +1788,10 @@ int i915_gem_mmap_gtt_version(void) * The current feature set supported by i915_gem_fault() and thus GTT mmaps * is exposed via I915_PARAM_MMAP_GTT_VERSION (see i915_gem_mmap_gtt_version). */ -int i915_gem_fault(struct vm_area_struct *area, struct vm_fault *vmf) +int i915_gem_fault(struct vm_fault *vmf) { #define MIN_CHUNK_PAGES ((1 << 20) >> PAGE_SHIFT) /* 1 MiB */ + struct vm_area_struct *area = vmf->vma; struct drm_i915_gem_object *obj = to_intel_bo(area->vm_private_data); struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -1830,26 +1846,9 @@ int i915_gem_fault(struct vm_area_struct *area, struct vm_fault *vmf) /* Now pin it into the GTT as needed */ vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, flags); if (IS_ERR(vma)) { - struct i915_ggtt_view view; - unsigned int chunk_size; - /* Use a partial view if it is bigger than available space */ - chunk_size = MIN_CHUNK_PAGES; - if (i915_gem_object_is_tiled(obj)) - chunk_size = roundup(chunk_size, tile_row_pages(obj)); - - memset(&view, 0, sizeof(view)); - view.type = I915_GGTT_VIEW_PARTIAL; - view.params.partial.offset = rounddown(page_offset, chunk_size); - view.params.partial.size = - min_t(unsigned int, chunk_size, - vma_pages(area) - view.params.partial.offset); - - /* If the partial covers the entire object, just create a - * normal VMA. - */ - if (chunk_size >= obj->base.size >> PAGE_SHIFT) - view.type = I915_GGTT_VIEW_NORMAL; + struct i915_ggtt_view view = + compute_partial_view(obj, page_offset, MIN_CHUNK_PAGES); /* Userspace is now writing through an untracked VMA, abandon * all hope that the hardware is able to track future writes. @@ -1878,7 +1877,7 @@ int i915_gem_fault(struct vm_area_struct *area, struct vm_fault *vmf) /* Finally, remap it using the new GTT offset */ ret = remap_io_mapping(area, - area->vm_start + (vma->ggtt_view.params.partial.offset << PAGE_SHIFT), + area->vm_start + (vma->ggtt_view.partial.offset << PAGE_SHIFT), (ggtt->mappable_base + vma->node.start) >> PAGE_SHIFT, min_t(u64, vma->size, area->vm_end - area->vm_start), &ggtt->mappable); @@ -2029,91 +2028,27 @@ void i915_gem_runtime_suspend(struct drm_i915_private *dev_priv) } } -/** - * i915_gem_get_ggtt_size - return required global GTT size for an object - * @dev_priv: i915 device - * @size: object size - * @tiling_mode: tiling mode - * - * Return the required global GTT size for an object, taking into account - * potential fence register mapping. - */ -u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv, - u64 size, int tiling_mode) -{ - u64 ggtt_size; - - GEM_BUG_ON(size == 0); - - if (INTEL_GEN(dev_priv) >= 4 || - tiling_mode == I915_TILING_NONE) - return size; - - /* Previous chips need a power-of-two fence region when tiling */ - if (IS_GEN3(dev_priv)) - ggtt_size = 1024*1024; - else - ggtt_size = 512*1024; - - while (ggtt_size < size) - ggtt_size <<= 1; - - return ggtt_size; -} - -/** - * i915_gem_get_ggtt_alignment - return required global GTT alignment - * @dev_priv: i915 device - * @size: object size - * @tiling_mode: tiling mode - * @fenced: is fenced alignment required or not - * - * Return the required global GTT alignment for an object, taking into account - * potential fence register mapping. - */ -u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size, - int tiling_mode, bool fenced) -{ - GEM_BUG_ON(size == 0); - - /* - * Minimum alignment is 4k (GTT page size), but might be greater - * if a fence register is needed for the object. - */ - if (INTEL_GEN(dev_priv) >= 4 || (!fenced && IS_G33(dev_priv)) || - tiling_mode == I915_TILING_NONE) - return 4096; - - /* - * Previous chips need to be aligned to the size of the smallest - * fence register that can contain the object. - */ - return i915_gem_get_ggtt_size(dev_priv, size, tiling_mode); -} - static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = to_i915(obj->base.dev); int err; err = drm_gem_create_mmap_offset(&obj->base); - if (!err) + if (likely(!err)) return 0; - /* We can idle the GPU locklessly to flush stale objects, but in order - * to claim that space for ourselves, we need to take the big - * struct_mutex to free the requests+objects and allocate our slot. - */ - err = i915_gem_wait_for_idle(dev_priv, I915_WAIT_INTERRUPTIBLE); - if (err) - return err; + /* Attempt to reap some mmap space from dead objects */ + do { + err = i915_gem_wait_for_idle(dev_priv, I915_WAIT_INTERRUPTIBLE); + if (err) + break; - err = i915_mutex_lock_interruptible(&dev_priv->drm); - if (!err) { - i915_gem_retire_requests(dev_priv); + i915_gem_drain_freed_objects(dev_priv); err = drm_gem_create_mmap_offset(&obj->base); - mutex_unlock(&dev_priv->drm.struct_mutex); - } + if (!err) + break; + + } while (flush_delayed_work(&dev_priv->gt.retire_work)); return err; } @@ -2306,6 +2241,7 @@ static void i915_sg_trim(struct sg_table *orig_st) /* called before being DMA mapped, no need to copy sg->dma_* */ new_sg = sg_next(new_sg); } + GEM_BUG_ON(new_sg); /* Should walk exactly nents and hit the end */ sg_free_table(orig_st); @@ -2627,35 +2563,34 @@ err_unlock: goto out_unlock; } -static bool i915_context_is_banned(const struct i915_gem_context *ctx) +static bool ban_context(const struct i915_gem_context *ctx) { - unsigned long elapsed; + return (i915_gem_context_is_bannable(ctx) && + ctx->ban_score >= CONTEXT_SCORE_BAN_THRESHOLD); +} - if (ctx->hang_stats.banned) - return true; +static void i915_gem_context_mark_guilty(struct i915_gem_context *ctx) +{ + ctx->guilty_count++; + ctx->ban_score += CONTEXT_SCORE_GUILTY; + if (ban_context(ctx)) + i915_gem_context_set_banned(ctx); - elapsed = get_seconds() - ctx->hang_stats.guilty_ts; - if (ctx->hang_stats.ban_period_seconds && - elapsed <= ctx->hang_stats.ban_period_seconds) { - DRM_DEBUG("context hanging too fast, banning!\n"); - return true; - } + DRM_DEBUG_DRIVER("context %s marked guilty (score %d) banned? %s\n", + ctx->name, ctx->ban_score, + yesno(i915_gem_context_is_banned(ctx))); + + if (!i915_gem_context_is_banned(ctx) || IS_ERR_OR_NULL(ctx->file_priv)) + return; - return false; + ctx->file_priv->context_bans++; + DRM_DEBUG_DRIVER("client %s has had %d context banned\n", + ctx->name, ctx->file_priv->context_bans); } -static void i915_set_reset_status(struct i915_gem_context *ctx, - const bool guilty) +static void i915_gem_context_mark_innocent(struct i915_gem_context *ctx) { - struct i915_ctx_hang_stats *hs = &ctx->hang_stats; - - if (guilty) { - hs->banned = i915_context_is_banned(ctx); - hs->batch_active++; - hs->guilty_ts = get_seconds(); - } else { - hs->batch_pending++; - } + ctx->active_count++; } struct drm_i915_gem_request * @@ -2675,13 +2610,52 @@ i915_gem_find_active_request(struct intel_engine_cs *engine) if (__i915_gem_request_completed(request)) continue; + GEM_BUG_ON(request->engine != engine); return request; } return NULL; } -static void reset_request(struct drm_i915_gem_request *request) +static bool engine_stalled(struct intel_engine_cs *engine) +{ + if (!engine->hangcheck.stalled) + return false; + + /* Check for possible seqno movement after hang declaration */ + if (engine->hangcheck.seqno != intel_engine_get_seqno(engine)) { + DRM_DEBUG_DRIVER("%s pardoned\n", engine->name); + return false; + } + + return true; +} + +int i915_gem_reset_prepare(struct drm_i915_private *dev_priv) +{ + struct intel_engine_cs *engine; + enum intel_engine_id id; + int err = 0; + + /* Ensure irq handler finishes, and not run again. */ + for_each_engine(engine, dev_priv, id) { + struct drm_i915_gem_request *request; + + tasklet_kill(&engine->irq_tasklet); + + if (engine_stalled(engine)) { + request = i915_gem_find_active_request(engine); + if (request && request->fence.error == -EIO) + err = -EIO; /* Previous reset failed! */ + } + } + + i915_gem_revoke_fences(dev_priv); + + return err; +} + +static void skip_request(struct drm_i915_gem_request *request) { void *vaddr = request->ring->vaddr; u32 head; @@ -2696,66 +2670,93 @@ static void reset_request(struct drm_i915_gem_request *request) head = 0; } memset(vaddr + head, 0, request->postfix - head); + + dma_fence_set_error(&request->fence, -EIO); } -static void i915_gem_reset_engine(struct intel_engine_cs *engine) +static void engine_skip_context(struct drm_i915_gem_request *request) { - struct drm_i915_gem_request *request; - struct i915_gem_context *incomplete_ctx; + struct intel_engine_cs *engine = request->engine; + struct i915_gem_context *hung_ctx = request->ctx; struct intel_timeline *timeline; unsigned long flags; - bool ring_hung; - if (engine->irq_seqno_barrier) - engine->irq_seqno_barrier(engine); + timeline = i915_gem_context_lookup_timeline(hung_ctx, engine); - request = i915_gem_find_active_request(engine); - if (!request) - return; + spin_lock_irqsave(&engine->timeline->lock, flags); + spin_lock(&timeline->lock); - ring_hung = engine->hangcheck.score >= HANGCHECK_SCORE_RING_HUNG; - if (engine->hangcheck.seqno != intel_engine_get_seqno(engine)) - ring_hung = false; + list_for_each_entry_continue(request, &engine->timeline->requests, link) + if (request->ctx == hung_ctx) + skip_request(request); - i915_set_reset_status(request->ctx, ring_hung); - if (!ring_hung) - return; + list_for_each_entry(request, &timeline->requests, link) + skip_request(request); - DRM_DEBUG_DRIVER("resetting %s to restart from tail of request 0x%x\n", - engine->name, request->global_seqno); + spin_unlock(&timeline->lock); + spin_unlock_irqrestore(&engine->timeline->lock, flags); +} - /* Setup the CS to resume from the breadcrumb of the hung request */ - engine->reset_hw(engine, request); +/* Returns true if the request was guilty of hang */ +static bool i915_gem_reset_request(struct drm_i915_gem_request *request) +{ + /* Read once and return the resolution */ + const bool guilty = engine_stalled(request->engine); - /* Users of the default context do not rely on logical state - * preserved between batches. They have to emit full state on - * every batch and so it is safe to execute queued requests following - * the hang. + /* The guilty request will get skipped on a hung engine. + * + * Users of client default contexts do not rely on logical + * state preserved between batches so it is safe to execute + * queued requests following the hang. Non default contexts + * rely on preserved state, so skipping a batch loses the + * evolution of the state and it needs to be considered corrupted. + * Executing more queued batches on top of corrupted state is + * risky. But we take the risk by trying to advance through + * the queued requests in order to make the client behaviour + * more predictable around resets, by not throwing away random + * amount of batches it has prepared for execution. Sophisticated + * clients can use gem_reset_stats_ioctl and dma fence status + * (exported via sync_file info ioctl on explicit fences) to observe + * when it loses the context state and should rebuild accordingly. * - * Other contexts preserve state, now corrupt. We want to skip all - * queued requests that reference the corrupt context. + * The context ban, and ultimately the client ban, mechanism are safety + * valves if client submission ends up resulting in nothing more than + * subsequent hangs. */ - incomplete_ctx = request->ctx; - if (i915_gem_context_is_default(incomplete_ctx)) - return; - timeline = i915_gem_context_lookup_timeline(incomplete_ctx, engine); + if (guilty) { + i915_gem_context_mark_guilty(request->ctx); + skip_request(request); + } else { + i915_gem_context_mark_innocent(request->ctx); + dma_fence_set_error(&request->fence, -EAGAIN); + } - spin_lock_irqsave(&engine->timeline->lock, flags); - spin_lock(&timeline->lock); + return guilty; +} - list_for_each_entry_continue(request, &engine->timeline->requests, link) - if (request->ctx == incomplete_ctx) - reset_request(request); +static void i915_gem_reset_engine(struct intel_engine_cs *engine) +{ + struct drm_i915_gem_request *request; - list_for_each_entry(request, &timeline->requests, link) - reset_request(request); + if (engine->irq_seqno_barrier) + engine->irq_seqno_barrier(engine); - spin_unlock(&timeline->lock); - spin_unlock_irqrestore(&engine->timeline->lock, flags); + request = i915_gem_find_active_request(engine); + if (request && i915_gem_reset_request(request)) { + DRM_DEBUG_DRIVER("resetting %s to restart from tail of request 0x%x\n", + engine->name, request->global_seqno); + + /* If this context is now banned, skip all pending requests. */ + if (i915_gem_context_is_banned(request->ctx)) + engine_skip_context(request); + } + + /* Setup the CS to resume from the breadcrumb of the hung request */ + engine->reset_hw(engine, request); } -void i915_gem_reset(struct drm_i915_private *dev_priv) +void i915_gem_reset_finish(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; enum intel_engine_id id; @@ -2779,14 +2780,30 @@ void i915_gem_reset(struct drm_i915_private *dev_priv) static void nop_submit_request(struct drm_i915_gem_request *request) { + dma_fence_set_error(&request->fence, -EIO); i915_gem_request_submit(request); intel_engine_init_global_seqno(request->engine, request->global_seqno); } -static void i915_gem_cleanup_engine(struct intel_engine_cs *engine) +static void engine_set_wedged(struct intel_engine_cs *engine) { + struct drm_i915_gem_request *request; + unsigned long flags; + + /* We need to be sure that no thread is running the old callback as + * we install the nop handler (otherwise we would submit a request + * to hardware that will never complete). In order to prevent this + * race, we wait until the machine is idle before making the swap + * (using stop_machine()). + */ engine->submit_request = nop_submit_request; + /* Mark all executing requests as skipped */ + spin_lock_irqsave(&engine->timeline->lock, flags); + list_for_each_entry(request, &engine->timeline->requests, link) + dma_fence_set_error(&request->fence, -EIO); + spin_unlock_irqrestore(&engine->timeline->lock, flags); + /* Mark all pending requests as complete so that any concurrent * (lockless) lookup doesn't try and wait upon the request as we * reset it. @@ -2815,20 +2832,29 @@ static void i915_gem_cleanup_engine(struct intel_engine_cs *engine) } } -void i915_gem_set_wedged(struct drm_i915_private *dev_priv) +static int __i915_gem_set_wedged_BKL(void *data) { + struct drm_i915_private *i915 = data; struct intel_engine_cs *engine; enum intel_engine_id id; + for_each_engine(engine, i915, id) + engine_set_wedged(engine); + + return 0; +} + +void i915_gem_set_wedged(struct drm_i915_private *dev_priv) +{ lockdep_assert_held(&dev_priv->drm.struct_mutex); set_bit(I915_WEDGED, &dev_priv->gpu_error.flags); - i915_gem_context_lost(dev_priv); - for_each_engine(engine, dev_priv, id) - i915_gem_cleanup_engine(engine); - mod_delayed_work(dev_priv->wq, &dev_priv->gt.idle_work, 0); + stop_machine(__i915_gem_set_wedged_BKL, dev_priv, NULL); + i915_gem_context_lost(dev_priv); i915_gem_retire_requests(dev_priv); + + mod_delayed_work(dev_priv->wq, &dev_priv->gt.idle_work, 0); } static void @@ -3373,7 +3399,7 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_caching *args = data; struct drm_i915_gem_object *obj; enum i915_cache_level level; - int ret; + int ret = 0; switch (args->caching) { case I915_CACHING_NONE: @@ -3398,20 +3424,29 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data, return -EINVAL; } - ret = i915_mutex_lock_interruptible(dev); + obj = i915_gem_object_lookup(file, args->handle); + if (!obj) + return -ENOENT; + + if (obj->cache_level == level) + goto out; + + ret = i915_gem_object_wait(obj, + I915_WAIT_INTERRUPTIBLE, + MAX_SCHEDULE_TIMEOUT, + to_rps_client(file)); if (ret) - return ret; + goto out; - obj = i915_gem_object_lookup(file, args->handle); - if (!obj) { - ret = -ENOENT; - goto unlock; - } + ret = i915_mutex_lock_interruptible(dev); + if (ret) + goto out; ret = i915_gem_object_set_cache_level(obj, level); - i915_gem_object_put(obj); -unlock: mutex_unlock(&dev->struct_mutex); + +out: + i915_gem_object_put(obj); return ret; } @@ -3461,7 +3496,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, * try to preserve the existing ABI). */ vma = ERR_PTR(-ENOSPC); - if (view->type == I915_GGTT_VIEW_NORMAL) + if (!view || view->type == I915_GGTT_VIEW_NORMAL) vma = i915_gem_object_ggtt_pin(obj, view, 0, alignment, PIN_MAPPABLE | PIN_NONBLOCK); if (IS_ERR(vma)) { @@ -3514,17 +3549,16 @@ err_unpin_display: void i915_gem_object_unpin_from_display_plane(struct i915_vma *vma) { - lockdep_assert_held(&vma->vm->dev->struct_mutex); + lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); if (WARN_ON(vma->obj->pin_display == 0)) return; if (--vma->obj->pin_display == 0) - vma->display_alignment = 0; + vma->display_alignment = I915_GTT_MIN_ALIGNMENT; /* Bump the LRU to try and avoid premature eviction whilst flipping */ - if (!i915_vma_is_active(vma)) - list_move_tail(&vma->vm_link, &vma->vm->inactive_list); + i915_gem_object_bump_inactive_ggtt(vma->obj); i915_vma_unpin(vma); } @@ -3655,8 +3689,8 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, lockdep_assert_held(&obj->base.dev->struct_mutex); - vma = i915_gem_obj_lookup_or_create_vma(obj, vm, view); - if (IS_ERR(vma)) + vma = i915_vma_instance(obj, vm, view); + if (unlikely(IS_ERR(vma))) return vma; if (i915_vma_misplaced(vma, size, alignment, flags)) { @@ -3665,10 +3699,6 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, return ERR_PTR(-ENOSPC); if (flags & PIN_MAPPABLE) { - u32 fence_size; - - fence_size = i915_gem_get_ggtt_size(dev_priv, vma->size, - i915_gem_object_get_tiling(obj)); /* If the required space is larger than the available * aperture, we will not able to find a slot for the * object and unbinding the object now will be in @@ -3676,7 +3706,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, * the object in and out of the Global GTT and * waste a lot of cycles under the mutex. */ - if (fence_size > dev_priv->ggtt.mappable_end) + if (vma->fence_size > dev_priv->ggtt.mappable_end) return ERR_PTR(-E2BIG); /* If NONBLOCK is set the caller is optimistically @@ -3695,7 +3725,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj, * we could try to minimise harm to others. */ if (flags & PIN_NONBLOCK && - fence_size > dev_priv->ggtt.mappable_end / 2) + vma->fence_size > dev_priv->ggtt.mappable_end / 2) return ERR_PTR(-ENOSPC); } @@ -3948,14 +3978,9 @@ static const struct drm_i915_gem_object_ops i915_gem_object_ops = { .put_pages = i915_gem_object_put_pages_gtt, }; -/* Note we don't consider signbits :| */ -#define overflows_type(x, T) \ - (sizeof(x) > sizeof(T) && (x) >> (sizeof(T) * BITS_PER_BYTE)) - struct drm_i915_gem_object * -i915_gem_object_create(struct drm_device *dev, u64 size) +i915_gem_object_create(struct drm_i915_private *dev_priv, u64 size) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_object *obj; struct address_space *mapping; gfp_t mask; @@ -3972,16 +3997,16 @@ i915_gem_object_create(struct drm_device *dev, u64 size) if (overflows_type(size, obj->base.size)) return ERR_PTR(-E2BIG); - obj = i915_gem_object_alloc(dev); + obj = i915_gem_object_alloc(dev_priv); if (obj == NULL) return ERR_PTR(-ENOMEM); - ret = drm_gem_object_init(dev, &obj->base, size); + ret = drm_gem_object_init(&dev_priv->drm, &obj->base, size); if (ret) goto fail; mask = GFP_HIGHUSER | __GFP_RECLAIMABLE; - if (IS_CRESTLINE(dev_priv) || IS_BROADWATER(dev_priv)) { + if (IS_I965GM(dev_priv) || IS_I965G(dev_priv)) { /* 965gm cannot relocate objects above 4GiB. */ mask &= ~__GFP_HIGHMEM; mask |= __GFP_DMA32; @@ -4174,12 +4199,13 @@ static void assert_kernel_context_is_current(struct drm_i915_private *dev_priv) enum intel_engine_id id; for_each_engine(engine, dev_priv, id) - GEM_BUG_ON(engine->last_context != dev_priv->kernel_context); + GEM_BUG_ON(engine->last_retired_context && + !i915_gem_context_is_kernel(engine->last_retired_context)); } -int i915_gem_suspend(struct drm_device *dev) +int i915_gem_suspend(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_device *dev = &dev_priv->drm; int ret; intel_suspend_gt_powersave(dev_priv); @@ -4213,8 +4239,14 @@ int i915_gem_suspend(struct drm_device *dev) cancel_delayed_work_sync(&dev_priv->gpu_error.hangcheck_work); cancel_delayed_work_sync(&dev_priv->gt.retire_work); - flush_delayed_work(&dev_priv->gt.idle_work); - flush_work(&dev_priv->mm.free_work); + + /* As the idle_work is rearming if it detects a race, play safe and + * repeat the flush until it is definitely idle. + */ + while (flush_delayed_work(&dev_priv->gt.idle_work)) + ; + + i915_gem_drain_freed_objects(dev_priv); /* Assert that we sucessfully flushed all the work and * reset the GPU back to its idle, low power state. @@ -4253,9 +4285,9 @@ err: return ret; } -void i915_gem_resume(struct drm_device *dev) +void i915_gem_resume(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_device *dev = &dev_priv->drm; WARN_ON(dev_priv->gt.awake); @@ -4320,9 +4352,8 @@ static void init_unused_rings(struct drm_i915_private *dev_priv) } int -i915_gem_init_hw(struct drm_device *dev) +i915_gem_init_hw(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_engine_cs *engine; enum intel_engine_id id; int ret; @@ -4376,10 +4407,10 @@ i915_gem_init_hw(struct drm_device *dev) goto out; } - intel_mocs_init_l3cc_table(dev); + intel_mocs_init_l3cc_table(dev_priv); /* We can't enable contexts until all firmware is loaded */ - ret = intel_guc_setup(dev); + ret = intel_guc_setup(dev_priv); if (ret) goto out; @@ -4409,12 +4440,11 @@ bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value) return true; } -int i915_gem_init(struct drm_device *dev) +int i915_gem_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); int ret; - mutex_lock(&dev->struct_mutex); + mutex_lock(&dev_priv->drm.struct_mutex); if (!i915.enable_execlists) { dev_priv->gt.resume = intel_legacy_submission_resume; @@ -4438,15 +4468,15 @@ int i915_gem_init(struct drm_device *dev) if (ret) goto out_unlock; - ret = i915_gem_context_init(dev); + ret = i915_gem_context_init(dev_priv); if (ret) goto out_unlock; - ret = intel_engines_init(dev); + ret = intel_engines_init(dev_priv); if (ret) goto out_unlock; - ret = i915_gem_init_hw(dev); + ret = i915_gem_init_hw(dev_priv); if (ret == -EIO) { /* Allow engine initialisation to fail by marking the GPU as * wedged. But we only want to do this where the GPU is angry, @@ -4459,15 +4489,14 @@ int i915_gem_init(struct drm_device *dev) out_unlock: intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev_priv->drm.struct_mutex); return ret; } void -i915_gem_cleanup_engines(struct drm_device *dev) +i915_gem_cleanup_engines(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_engine_cs *engine; enum intel_engine_id id; @@ -4483,8 +4512,9 @@ i915_gem_load_init_fences(struct drm_i915_private *dev_priv) if (INTEL_INFO(dev_priv)->gen >= 7 && !IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) dev_priv->num_fence_regs = 32; - else if (INTEL_INFO(dev_priv)->gen >= 4 || IS_I945G(dev_priv) || - IS_I945GM(dev_priv) || IS_G33(dev_priv)) + else if (INTEL_INFO(dev_priv)->gen >= 4 || + IS_I945G(dev_priv) || IS_I945GM(dev_priv) || + IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) dev_priv->num_fence_regs = 16; else dev_priv->num_fence_regs = 8; @@ -4507,9 +4537,8 @@ i915_gem_load_init_fences(struct drm_i915_private *dev_priv) } int -i915_gem_load_init(struct drm_device *dev) +i915_gem_load_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); int err = -ENOMEM; dev_priv->objects = KMEM_CACHE(drm_i915_gem_object, SLAB_HWCACHE_ALIGN); @@ -4578,10 +4607,8 @@ err_out: return err; } -void i915_gem_load_cleanup(struct drm_device *dev) +void i915_gem_load_cleanup(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - WARN_ON(!llist_empty(&dev_priv->mm.free_list)); mutex_lock(&dev_priv->drm.struct_mutex); @@ -4732,7 +4759,7 @@ void i915_gem_track_fb(struct drm_i915_gem_object *old, /* Allocate a new GEM object and fill it with the supplied data */ struct drm_i915_gem_object * -i915_gem_object_create_from_data(struct drm_device *dev, +i915_gem_object_create_from_data(struct drm_i915_private *dev_priv, const void *data, size_t size) { struct drm_i915_gem_object *obj; @@ -4740,7 +4767,7 @@ i915_gem_object_create_from_data(struct drm_device *dev, size_t bytes; int ret; - obj = i915_gem_object_create(dev, round_up(size, PAGE_SIZE)); + obj = i915_gem_object_create(dev_priv, round_up(size, PAGE_SIZE)); if (IS_ERR(obj)) return obj; diff --git a/drivers/gpu/drm/i915/i915_gem.h b/drivers/gpu/drm/i915/i915_gem.h index 51ec793f2e20..a585d47c420a 100644 --- a/drivers/gpu/drm/i915/i915_gem.h +++ b/drivers/gpu/drm/i915/i915_gem.h @@ -27,8 +27,10 @@ #ifdef CONFIG_DRM_I915_DEBUG_GEM #define GEM_BUG_ON(expr) BUG_ON(expr) +#define GEM_WARN_ON(expr) WARN_ON(expr) #else -#define GEM_BUG_ON(expr) do { } while (0) +#define GEM_BUG_ON(expr) BUILD_BUG_ON_INVALID(expr) +#define GEM_WARN_ON(expr) (BUILD_BUG_ON_INVALID(expr), 0) #endif #define I915_NUM_ENGINES 5 diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 1f94b8d6d83d..17f90c618208 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -97,7 +97,7 @@ * part. It should be safe to decrease this, but it's more future proof as is. */ #define GEN6_CONTEXT_ALIGN (64<<10) -#define GEN7_CONTEXT_ALIGN 4096 +#define GEN7_CONTEXT_ALIGN I915_GTT_MIN_ALIGNMENT static size_t get_context_alignment(struct drm_i915_private *dev_priv) { @@ -141,7 +141,7 @@ void i915_gem_context_free(struct kref *ctx_ref) lockdep_assert_held(&ctx->i915->drm.struct_mutex); trace_i915_context_free(ctx); - GEM_BUG_ON(!ctx->closed); + GEM_BUG_ON(!i915_gem_context_is_closed(ctx)); i915_ppgtt_put(ctx->ppgtt); @@ -166,15 +166,15 @@ void i915_gem_context_free(struct kref *ctx_ref) kfree(ctx); } -struct drm_i915_gem_object * -i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) +static struct drm_i915_gem_object * +alloc_context_obj(struct drm_i915_private *dev_priv, u64 size) { struct drm_i915_gem_object *obj; int ret; - lockdep_assert_held(&dev->struct_mutex); + lockdep_assert_held(&dev_priv->drm.struct_mutex); - obj = i915_gem_object_create(dev, size); + obj = i915_gem_object_create(dev_priv, size); if (IS_ERR(obj)) return obj; @@ -193,7 +193,7 @@ i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) * This is only applicable for Ivy Bridge devices since * later platforms don't have L3 control bits in the PTE. */ - if (IS_IVYBRIDGE(to_i915(dev))) { + if (IS_IVYBRIDGE(dev_priv)) { ret = i915_gem_object_set_cache_level(obj, I915_CACHE_L3_LLC); /* Failure shouldn't ever happen this early */ if (WARN_ON(ret)) { @@ -205,31 +205,9 @@ i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) return obj; } -static void i915_ppgtt_close(struct i915_address_space *vm) -{ - struct list_head *phases[] = { - &vm->active_list, - &vm->inactive_list, - &vm->unbound_list, - NULL, - }, **phase; - - GEM_BUG_ON(vm->closed); - vm->closed = true; - - for (phase = phases; *phase; phase++) { - struct i915_vma *vma, *vn; - - list_for_each_entry_safe(vma, vn, *phase, vm_link) - if (!i915_vma_is_closed(vma)) - i915_vma_close(vma); - } -} - static void context_close(struct i915_gem_context *ctx) { - GEM_BUG_ON(ctx->closed); - ctx->closed = true; + i915_gem_context_set_closed(ctx); if (ctx->ppgtt) i915_ppgtt_close(&ctx->ppgtt->base); ctx->file_priv = ERR_PTR(-EBADF); @@ -259,10 +237,9 @@ static int assign_hw_id(struct drm_i915_private *dev_priv, unsigned *out) } static struct i915_gem_context * -__create_hw_context(struct drm_device *dev, +__create_hw_context(struct drm_i915_private *dev_priv, struct drm_i915_file_private *file_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_gem_context *ctx; int ret; @@ -286,14 +263,13 @@ __create_hw_context(struct drm_device *dev, struct drm_i915_gem_object *obj; struct i915_vma *vma; - obj = i915_gem_alloc_context_obj(dev, - dev_priv->hw_context_size); + obj = alloc_context_obj(dev_priv, dev_priv->hw_context_size); if (IS_ERR(obj)) { ret = PTR_ERR(obj); goto err_out; } - vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + vma = i915_vma_instance(obj, &dev_priv->ggtt.base, NULL); if (IS_ERR(vma)) { i915_gem_object_put(obj); ret = PTR_ERR(vma); @@ -331,12 +307,21 @@ __create_hw_context(struct drm_device *dev, * is no remap info, it will be a NOP. */ ctx->remap_slice = ALL_L3_SLICES(dev_priv); - ctx->hang_stats.ban_period_seconds = DRM_I915_CTX_BAN_PERIOD; + i915_gem_context_set_bannable(ctx); ctx->ring_size = 4 * PAGE_SIZE; ctx->desc_template = GEN8_CTX_ADDRESSING_MODE(dev_priv) << GEN8_CTX_ADDRESSING_MODE_SHIFT; ATOMIC_INIT_NOTIFIER_HEAD(&ctx->status_notifier); + /* GuC requires the ring to be placed above GUC_WOPCM_TOP. If GuC is not + * present or not in use we still need a small bias as ring wraparound + * at offset 0 sometimes hangs. No idea why. + */ + if (HAS_GUC(dev_priv) && i915.enable_guc_loading) + ctx->ggtt_offset_bias = GUC_WOPCM_TOP; + else + ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE; + return ctx; err_pid: @@ -353,21 +338,21 @@ err_out: * well as an idle case. */ static struct i915_gem_context * -i915_gem_create_context(struct drm_device *dev, +i915_gem_create_context(struct drm_i915_private *dev_priv, struct drm_i915_file_private *file_priv) { struct i915_gem_context *ctx; - lockdep_assert_held(&dev->struct_mutex); + lockdep_assert_held(&dev_priv->drm.struct_mutex); - ctx = __create_hw_context(dev, file_priv); + ctx = __create_hw_context(dev_priv, file_priv); if (IS_ERR(ctx)) return ctx; - if (USES_FULL_PPGTT(dev)) { + if (USES_FULL_PPGTT(dev_priv)) { struct i915_hw_ppgtt *ppgtt; - ppgtt = i915_ppgtt_create(to_i915(dev), file_priv, ctx->name); + ppgtt = i915_ppgtt_create(dev_priv, file_priv, ctx->name); if (IS_ERR(ppgtt)) { DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n", PTR_ERR(ppgtt)); @@ -407,35 +392,24 @@ i915_gem_context_create_gvt(struct drm_device *dev) if (ret) return ERR_PTR(ret); - ctx = i915_gem_create_context(dev, NULL); + ctx = __create_hw_context(to_i915(dev), NULL); if (IS_ERR(ctx)) goto out; - ctx->execlists_force_single_submission = true; + ctx->file_priv = ERR_PTR(-EBADF); + i915_gem_context_set_closed(ctx); /* not user accessible */ + i915_gem_context_clear_bannable(ctx); + i915_gem_context_set_force_single_submission(ctx); ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */ + + GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); out: mutex_unlock(&dev->struct_mutex); return ctx; } -static void i915_gem_context_unpin(struct i915_gem_context *ctx, - struct intel_engine_cs *engine) -{ - if (i915.enable_execlists) { - intel_lr_context_unpin(ctx, engine); - } else { - struct intel_context *ce = &ctx->engine[engine->id]; - - if (ce->state) - i915_vma_unpin(ce->state); - - i915_gem_context_put(ctx); - } -} - -int i915_gem_context_init(struct drm_device *dev) +int i915_gem_context_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_gem_context *ctx; /* Init should only be called once per module load. Eventually the @@ -461,7 +435,8 @@ int i915_gem_context_init(struct drm_device *dev) dev_priv->hw_context_size = 0; } else if (HAS_HW_CONTEXTS(dev_priv)) { dev_priv->hw_context_size = - round_up(get_context_size(dev_priv), 4096); + round_up(get_context_size(dev_priv), + I915_GTT_PAGE_SIZE); if (dev_priv->hw_context_size > (1<<20)) { DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size %d\n", dev_priv->hw_context_size); @@ -469,16 +444,19 @@ int i915_gem_context_init(struct drm_device *dev) } } - ctx = i915_gem_create_context(dev, NULL); + ctx = i915_gem_create_context(dev_priv, NULL); if (IS_ERR(ctx)) { DRM_ERROR("Failed to create default global context (error %ld)\n", PTR_ERR(ctx)); return PTR_ERR(ctx); } + i915_gem_context_clear_bannable(ctx); ctx->priority = I915_PRIORITY_MIN; /* lowest priority; idle task */ dev_priv->kernel_context = ctx; + GEM_BUG_ON(!i915_gem_context_is_kernel(ctx)); + DRM_DEBUG_DRIVER("%s context support initialized\n", i915.enable_execlists ? "LR" : dev_priv->hw_context_size ? "HW" : "fake"); @@ -493,10 +471,13 @@ void i915_gem_context_lost(struct drm_i915_private *dev_priv) lockdep_assert_held(&dev_priv->drm.struct_mutex); for_each_engine(engine, dev_priv, id) { - if (engine->last_context) { - i915_gem_context_unpin(engine->last_context, engine); - engine->last_context = NULL; - } + engine->legacy_active_context = NULL; + + if (!engine->last_retired_context) + continue; + + engine->context_unpin(engine, engine->last_retired_context); + engine->last_retired_context = NULL; } /* Force the GPU state to be restored on enabling */ @@ -522,12 +503,13 @@ void i915_gem_context_lost(struct drm_i915_private *dev_priv) } } -void i915_gem_context_fini(struct drm_device *dev) +void i915_gem_context_fini(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_gem_context *dctx = dev_priv->kernel_context; - lockdep_assert_held(&dev->struct_mutex); + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + GEM_BUG_ON(!i915_gem_context_is_kernel(dctx)); context_close(dctx); dev_priv->kernel_context = NULL; @@ -551,9 +533,11 @@ int i915_gem_context_open(struct drm_device *dev, struct drm_file *file) idr_init(&file_priv->context_idr); mutex_lock(&dev->struct_mutex); - ctx = i915_gem_create_context(dev, file_priv); + ctx = i915_gem_create_context(to_i915(dev), file_priv); mutex_unlock(&dev->struct_mutex); + GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); + if (IS_ERR(ctx)) { idr_destroy(&file_priv->context_idr); return PTR_ERR(ctx); @@ -719,7 +703,7 @@ static inline bool skip_rcs_switch(struct i915_hw_ppgtt *ppgtt, if (ppgtt && (intel_engine_flag(engine) & ppgtt->pd_dirty_rings)) return false; - return to == engine->last_context; + return to == engine->legacy_active_context; } static bool @@ -731,11 +715,11 @@ needs_pd_load_pre(struct i915_hw_ppgtt *ppgtt, return false; /* Always load the ppgtt on first use */ - if (!engine->last_context) + if (!engine->legacy_active_context) return true; /* Same context without new entries, skip */ - if (engine->last_context == to && + if (engine->legacy_active_context == to && !(intel_engine_flag(engine) & ppgtt->pd_dirty_rings)) return false; @@ -765,57 +749,20 @@ needs_pd_load_post(struct i915_hw_ppgtt *ppgtt, return false; } -struct i915_vma * -i915_gem_context_pin_legacy(struct i915_gem_context *ctx, - unsigned int flags) -{ - struct i915_vma *vma = ctx->engine[RCS].state; - int ret; - - /* Clear this page out of any CPU caches for coherent swap-in/out. - * We only want to do this on the first bind so that we do not stall - * on an active context (which by nature is already on the GPU). - */ - if (!(vma->flags & I915_VMA_GLOBAL_BIND)) { - ret = i915_gem_object_set_to_gtt_domain(vma->obj, false); - if (ret) - return ERR_PTR(ret); - } - - ret = i915_vma_pin(vma, 0, ctx->ggtt_alignment, PIN_GLOBAL | flags); - if (ret) - return ERR_PTR(ret); - - return vma; -} - static int do_rcs_switch(struct drm_i915_gem_request *req) { struct i915_gem_context *to = req->ctx; struct intel_engine_cs *engine = req->engine; struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt; - struct i915_vma *vma; - struct i915_gem_context *from; + struct i915_gem_context *from = engine->legacy_active_context; u32 hw_flags; int ret, i; + GEM_BUG_ON(engine->id != RCS); + if (skip_rcs_switch(ppgtt, engine, to)) return 0; - /* Trying to pin first makes error handling easier. */ - vma = i915_gem_context_pin_legacy(to, 0); - if (IS_ERR(vma)) - return PTR_ERR(vma); - - /* - * Pin can switch back to the default context if we end up calling into - * evict_everything - as a last ditch gtt defrag effort that also - * switches to the default context. Hence we need to reload from here. - * - * XXX: Doing so is painfully broken! - */ - from = engine->last_context; - if (needs_pd_load_pre(ppgtt, engine, to)) { /* Older GENs and non render rings still want the load first, * "PP_DCLV followed by PP_DIR_BASE register through Load @@ -824,7 +771,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) trace_switch_mm(engine, to); ret = ppgtt->switch_mm(ppgtt, req); if (ret) - goto err; + return ret; } if (!to->engine[RCS].initialised || i915_gem_context_is_default(to)) @@ -841,29 +788,10 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) if (to != from || (hw_flags & MI_FORCE_RESTORE)) { ret = mi_set_context(req, hw_flags); if (ret) - goto err; - } + return ret; - /* The backing object for the context is done after switching to the - * *next* context. Therefore we cannot retire the previous context until - * the next context has already started running. In fact, the below code - * is a bit suboptimal because the retiring can occur simply after the - * MI_SET_CONTEXT instead of when the next seqno has completed. - */ - if (from != NULL) { - /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the - * whole damn pipeline, we don't need to explicitly mark the - * object dirty. The only exception is that the context must be - * correct in case the object gets swapped out. Ideally we'd be - * able to defer doing this until we know the object would be - * swapped, but there is no way to do that yet. - */ - i915_vma_move_to_active(from->engine[RCS].state, req, 0); - /* state is kept alive until the next request */ - i915_vma_unpin(from->engine[RCS].state); - i915_gem_context_put(from); + engine->legacy_active_context = to; } - engine->last_context = i915_gem_context_get(to); /* GEN8 does *not* require an explicit reload if the PDPs have been * setup, and we do not wish to move them. @@ -904,10 +832,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) } return 0; - -err: - i915_vma_unpin(vma); - return ret; } /** @@ -947,18 +871,32 @@ int i915_switch_context(struct drm_i915_gem_request *req) ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine); } - if (to != engine->last_context) { - if (engine->last_context) - i915_gem_context_put(engine->last_context); - engine->last_context = i915_gem_context_get(to); - } - return 0; } return do_rcs_switch(req); } +static bool engine_has_kernel_context(struct intel_engine_cs *engine) +{ + struct i915_gem_timeline *timeline; + + list_for_each_entry(timeline, &engine->i915->gt.timelines, link) { + struct intel_timeline *tl; + + if (timeline == &engine->i915->gt.global_timeline) + continue; + + tl = &timeline->engine[engine->id]; + if (i915_gem_active_peek(&tl->last_request, + &engine->i915->drm.struct_mutex)) + return false; + } + + return (!engine->last_retired_context || + i915_gem_context_is_kernel(engine->last_retired_context)); +} + int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv) { struct intel_engine_cs *engine; @@ -967,10 +905,15 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv) lockdep_assert_held(&dev_priv->drm.struct_mutex); + i915_gem_retire_requests(dev_priv); + for_each_engine(engine, dev_priv, id) { struct drm_i915_gem_request *req; int ret; + if (engine_has_kernel_context(engine)) + continue; + req = i915_gem_request_alloc(engine, dev_priv->kernel_context); if (IS_ERR(req)) return PTR_ERR(req); @@ -1003,6 +946,11 @@ static bool contexts_enabled(struct drm_device *dev) return i915.enable_execlists || to_i915(dev)->hw_context_size; } +static bool client_is_banned(struct drm_i915_file_private *file_priv) +{ + return file_priv->context_bans > I915_MAX_CLIENT_CONTEXT_BANS; +} + int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { @@ -1017,17 +965,27 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, if (args->pad != 0) return -EINVAL; + if (client_is_banned(file_priv)) { + DRM_DEBUG("client %s[%d] banned from creating ctx\n", + current->comm, + pid_nr(get_task_pid(current, PIDTYPE_PID))); + + return -EIO; + } + ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; - ctx = i915_gem_create_context(dev, file_priv); + ctx = i915_gem_create_context(to_i915(dev), file_priv); mutex_unlock(&dev->struct_mutex); if (IS_ERR(ctx)) return PTR_ERR(ctx); + GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); + args->ctx_id = ctx->user_handle; - DRM_DEBUG_DRIVER("HW context %d created\n", args->ctx_id); + DRM_DEBUG("HW context %d created\n", args->ctx_id); return 0; } @@ -1060,7 +1018,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, context_close(ctx); mutex_unlock(&dev->struct_mutex); - DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); + DRM_DEBUG("HW context %d destroyed\n", args->ctx_id); return 0; } @@ -1085,7 +1043,7 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, args->size = 0; switch (args->param) { case I915_CONTEXT_PARAM_BAN_PERIOD: - args->value = ctx->hang_stats.ban_period_seconds; + ret = -EINVAL; break; case I915_CONTEXT_PARAM_NO_ZEROMAP: args->value = ctx->flags & CONTEXT_NO_ZEROMAP; @@ -1099,7 +1057,10 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, args->value = to_i915(dev)->ggtt.base.total; break; case I915_CONTEXT_PARAM_NO_ERROR_CAPTURE: - args->value = !!(ctx->flags & CONTEXT_NO_ERROR_CAPTURE); + args->value = i915_gem_context_no_error_capture(ctx); + break; + case I915_CONTEXT_PARAM_BANNABLE: + args->value = i915_gem_context_is_bannable(ctx); break; default: ret = -EINVAL; @@ -1130,13 +1091,7 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, switch (args->param) { case I915_CONTEXT_PARAM_BAN_PERIOD: - if (args->size) - ret = -EINVAL; - else if (args->value < ctx->hang_stats.ban_period_seconds && - !capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ctx->hang_stats.ban_period_seconds = args->value; + ret = -EINVAL; break; case I915_CONTEXT_PARAM_NO_ZEROMAP: if (args->size) { @@ -1147,14 +1102,22 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, } break; case I915_CONTEXT_PARAM_NO_ERROR_CAPTURE: - if (args->size) { + if (args->size) ret = -EINVAL; - } else { - if (args->value) - ctx->flags |= CONTEXT_NO_ERROR_CAPTURE; - else - ctx->flags &= ~CONTEXT_NO_ERROR_CAPTURE; - } + else if (args->value) + i915_gem_context_set_no_error_capture(ctx); + else + i915_gem_context_clear_no_error_capture(ctx); + break; + case I915_CONTEXT_PARAM_BANNABLE: + if (args->size) + ret = -EINVAL; + else if (!capable(CAP_SYS_ADMIN) && !args->value) + ret = -EPERM; + else if (args->value) + i915_gem_context_set_bannable(ctx); + else + i915_gem_context_clear_bannable(ctx); break; default: ret = -EINVAL; @@ -1170,7 +1133,6 @@ int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, { struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_reset_stats *args = data; - struct i915_ctx_hang_stats *hs; struct i915_gem_context *ctx; int ret; @@ -1189,15 +1151,14 @@ int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, mutex_unlock(&dev->struct_mutex); return PTR_ERR(ctx); } - hs = &ctx->hang_stats; if (capable(CAP_SYS_ADMIN)) args->reset_count = i915_reset_count(&dev_priv->gpu_error); else args->reset_count = 0; - args->batch_active = hs->batch_active; - args->batch_pending = hs->batch_pending; + args->batch_active = ctx->guilty_count; + args->batch_pending = ctx->active_count; mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_context.h b/drivers/gpu/drm/i915/i915_gem_context.h new file mode 100644 index 000000000000..0ac750b90f3d --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_context.h @@ -0,0 +1,277 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __I915_GEM_CONTEXT_H__ +#define __I915_GEM_CONTEXT_H__ + +#include <linux/bitops.h> +#include <linux/list.h> + +struct pid; + +struct drm_device; +struct drm_file; + +struct drm_i915_private; +struct drm_i915_file_private; +struct i915_hw_ppgtt; +struct i915_vma; +struct intel_ring; + +#define DEFAULT_CONTEXT_HANDLE 0 + +/** + * struct i915_gem_context - client state + * + * The struct i915_gem_context represents the combined view of the driver and + * logical hardware state for a particular client. + */ +struct i915_gem_context { + /** i915: i915 device backpointer */ + struct drm_i915_private *i915; + + /** file_priv: owning file descriptor */ + struct drm_i915_file_private *file_priv; + + /** + * @ppgtt: unique address space (GTT) + * + * In full-ppgtt mode, each context has its own address space ensuring + * complete seperation of one client from all others. + * + * In other modes, this is a NULL pointer with the expectation that + * the caller uses the shared global GTT. + */ + struct i915_hw_ppgtt *ppgtt; + + /** + * @pid: process id of creator + * + * Note that who created the context may not be the principle user, + * as the context may be shared across a local socket. However, + * that should only affect the default context, all contexts created + * explicitly by the client are expected to be isolated. + */ + struct pid *pid; + + /** + * @name: arbitrary name + * + * A name is constructed for the context from the creator's process + * name, pid and user handle in order to uniquely identify the + * context in messages. + */ + const char *name; + + /** link: place with &drm_i915_private.context_list */ + struct list_head link; + + /** + * @ref: reference count + * + * A reference to a context is held by both the client who created it + * and on each request submitted to the hardware using the request + * (to ensure the hardware has access to the state until it has + * finished all pending writes). See i915_gem_context_get() and + * i915_gem_context_put() for access. + */ + struct kref ref; + + /** + * @flags: small set of booleans + */ + unsigned long flags; +#define CONTEXT_NO_ZEROMAP BIT(0) +#define CONTEXT_NO_ERROR_CAPTURE 1 +#define CONTEXT_CLOSED 2 +#define CONTEXT_BANNABLE 3 +#define CONTEXT_BANNED 4 +#define CONTEXT_FORCE_SINGLE_SUBMISSION 5 + + /** + * @hw_id: - unique identifier for the context + * + * The hardware needs to uniquely identify the context for a few + * functions like fault reporting, PASID, scheduling. The + * &drm_i915_private.context_hw_ida is used to assign a unqiue + * id for the lifetime of the context. + */ + unsigned int hw_id; + + /** + * @user_handle: userspace identifier + * + * A unique per-file identifier is generated from + * &drm_i915_file_private.contexts. + */ + u32 user_handle; + + /** + * @priority: execution and service priority + * + * All clients are equal, but some are more equal than others! + * + * Requests from a context with a greater (more positive) value of + * @priority will be executed before those with a lower @priority + * value, forming a simple QoS. + * + * The &drm_i915_private.kernel_context is assigned the lowest priority. + */ + int priority; + + /** ggtt_alignment: alignment restriction for context objects */ + u32 ggtt_alignment; + /** ggtt_offset_bias: placement restriction for context objects */ + u32 ggtt_offset_bias; + + /** engine: per-engine logical HW state */ + struct intel_context { + struct i915_vma *state; + struct intel_ring *ring; + u32 *lrc_reg_state; + u64 lrc_desc; + int pin_count; + bool initialised; + } engine[I915_NUM_ENGINES]; + + /** ring_size: size for allocating the per-engine ring buffer */ + u32 ring_size; + /** desc_template: invariant fields for the HW context descriptor */ + u32 desc_template; + + /** status_notifier: list of callbacks for context-switch changes */ + struct atomic_notifier_head status_notifier; + + /** guilty_count: How many times this context has caused a GPU hang. */ + unsigned int guilty_count; + /** + * @active_count: How many times this context was active during a GPU + * hang, but did not cause it. + */ + unsigned int active_count; + +#define CONTEXT_SCORE_GUILTY 10 +#define CONTEXT_SCORE_BAN_THRESHOLD 40 + /** ban_score: Accumulated score of all hangs caused by this context. */ + int ban_score; + + /** remap_slice: Bitmask of cache lines that need remapping */ + u8 remap_slice; +}; + +static inline bool i915_gem_context_is_closed(const struct i915_gem_context *ctx) +{ + return test_bit(CONTEXT_CLOSED, &ctx->flags); +} + +static inline void i915_gem_context_set_closed(struct i915_gem_context *ctx) +{ + GEM_BUG_ON(i915_gem_context_is_closed(ctx)); + __set_bit(CONTEXT_CLOSED, &ctx->flags); +} + +static inline bool i915_gem_context_no_error_capture(const struct i915_gem_context *ctx) +{ + return test_bit(CONTEXT_NO_ERROR_CAPTURE, &ctx->flags); +} + +static inline void i915_gem_context_set_no_error_capture(struct i915_gem_context *ctx) +{ + __set_bit(CONTEXT_NO_ERROR_CAPTURE, &ctx->flags); +} + +static inline void i915_gem_context_clear_no_error_capture(struct i915_gem_context *ctx) +{ + __clear_bit(CONTEXT_NO_ERROR_CAPTURE, &ctx->flags); +} + +static inline bool i915_gem_context_is_bannable(const struct i915_gem_context *ctx) +{ + return test_bit(CONTEXT_BANNABLE, &ctx->flags); +} + +static inline void i915_gem_context_set_bannable(struct i915_gem_context *ctx) +{ + __set_bit(CONTEXT_BANNABLE, &ctx->flags); +} + +static inline void i915_gem_context_clear_bannable(struct i915_gem_context *ctx) +{ + __clear_bit(CONTEXT_BANNABLE, &ctx->flags); +} + +static inline bool i915_gem_context_is_banned(const struct i915_gem_context *ctx) +{ + return test_bit(CONTEXT_BANNED, &ctx->flags); +} + +static inline void i915_gem_context_set_banned(struct i915_gem_context *ctx) +{ + __set_bit(CONTEXT_BANNED, &ctx->flags); +} + +static inline bool i915_gem_context_force_single_submission(const struct i915_gem_context *ctx) +{ + return test_bit(CONTEXT_FORCE_SINGLE_SUBMISSION, &ctx->flags); +} + +static inline void i915_gem_context_set_force_single_submission(struct i915_gem_context *ctx) +{ + __set_bit(CONTEXT_FORCE_SINGLE_SUBMISSION, &ctx->flags); +} + +static inline bool i915_gem_context_is_default(const struct i915_gem_context *c) +{ + return c->user_handle == DEFAULT_CONTEXT_HANDLE; +} + +static inline bool i915_gem_context_is_kernel(struct i915_gem_context *ctx) +{ + return !ctx->file_priv; +} + +/* i915_gem_context.c */ +int __must_check i915_gem_context_init(struct drm_i915_private *dev_priv); +void i915_gem_context_lost(struct drm_i915_private *dev_priv); +void i915_gem_context_fini(struct drm_i915_private *dev_priv); +int i915_gem_context_open(struct drm_device *dev, struct drm_file *file); +void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); +int i915_switch_context(struct drm_i915_gem_request *req); +int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv); +void i915_gem_context_free(struct kref *ctx_ref); +struct i915_gem_context * +i915_gem_context_create_gvt(struct drm_device *dev); + +int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); + +#endif /* !__I915_GEM_CONTEXT_H__ */ diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 5e38299b5df6..29bb8011dbc4 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -141,7 +141,7 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct * if (!obj->base.filp) return -ENODEV; - ret = obj->base.filp->f_op->mmap(obj->base.filp, vma); + ret = call_mmap(obj->base.filp, vma); if (ret) return ret; @@ -278,7 +278,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, get_dma_buf(dma_buf); - obj = i915_gem_object_alloc(dev); + obj = i915_gem_object_alloc(to_i915(dev)); if (obj == NULL) { ret = -ENOMEM; goto fail_detach; diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index d534a316a16e..c181b1bb3d2c 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -51,7 +51,10 @@ static bool ggtt_is_idle(struct drm_i915_private *dev_priv) } static bool -mark_free(struct i915_vma *vma, unsigned int flags, struct list_head *unwind) +mark_free(struct drm_mm_scan *scan, + struct i915_vma *vma, + unsigned int flags, + struct list_head *unwind) { if (i915_vma_is_pinned(vma)) return false; @@ -63,7 +66,7 @@ mark_free(struct i915_vma *vma, unsigned int flags, struct list_head *unwind) return false; list_add(&vma->exec_list, unwind); - return drm_mm_scan_add_block(&vma->node); + return drm_mm_scan_add_block(scan, &vma->node); } /** @@ -96,7 +99,8 @@ i915_gem_evict_something(struct i915_address_space *vm, u64 start, u64 end, unsigned flags) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; + struct drm_mm_scan scan; struct list_head eviction_list; struct list_head *phases[] = { &vm->inactive_list, @@ -104,9 +108,11 @@ i915_gem_evict_something(struct i915_address_space *vm, NULL, }, **phase; struct i915_vma *vma, *next; + struct drm_mm_node *node; + enum drm_mm_insert_mode mode; int ret; - lockdep_assert_held(&vm->dev->struct_mutex); + lockdep_assert_held(&vm->i915->drm.struct_mutex); trace_i915_gem_evict(vm, min_size, alignment, flags); /* @@ -122,14 +128,23 @@ i915_gem_evict_something(struct i915_address_space *vm, * On each list, the oldest objects lie at the HEAD with the freshest * object on the TAIL. */ - if (start != 0 || end != vm->total) { - drm_mm_init_scan_with_range(&vm->mm, min_size, - alignment, cache_level, - start, end); - } else - drm_mm_init_scan(&vm->mm, min_size, alignment, cache_level); - - if (flags & PIN_NONBLOCK) + mode = DRM_MM_INSERT_BEST; + if (flags & PIN_HIGH) + mode = DRM_MM_INSERT_HIGH; + if (flags & PIN_MAPPABLE) + mode = DRM_MM_INSERT_LOW; + drm_mm_scan_init_with_range(&scan, &vm->mm, + min_size, alignment, cache_level, + start, end, mode); + + /* Retire before we search the active list. Although we have + * reasonable accuracy in our retirement lists, we may have + * a stray pin (preventing eviction) that can only be resolved by + * retiring. + */ + if (!(flags & PIN_NONBLOCK)) + i915_gem_retire_requests(dev_priv); + else phases[1] = NULL; search_again: @@ -137,13 +152,13 @@ search_again: phase = phases; do { list_for_each_entry(vma, *phase, vm_link) - if (mark_free(vma, flags, &eviction_list)) + if (mark_free(&scan, vma, flags, &eviction_list)) goto found; } while (*++phase); /* Nothing found, clean up and bail out! */ list_for_each_entry_safe(vma, next, &eviction_list, exec_list) { - ret = drm_mm_scan_remove_block(&vma->node); + ret = drm_mm_scan_remove_block(&scan, &vma->node); BUG_ON(ret); INIT_LIST_HEAD(&vma->exec_list); @@ -162,7 +177,7 @@ search_again: * back to userspace to give our workqueues time to * acquire our locks and unpin the old scanouts. */ - return intel_has_pending_fb_unpin(vm->dev) ? -EAGAIN : -ENOSPC; + return intel_has_pending_fb_unpin(dev_priv) ? -EAGAIN : -ENOSPC; } /* Not everything in the GGTT is tracked via vma (otherwise we @@ -192,7 +207,7 @@ found: * of any of our objects, thus corrupting the list). */ list_for_each_entry_safe(vma, next, &eviction_list, exec_list) { - if (drm_mm_scan_remove_block(&vma->node)) + if (drm_mm_scan_remove_block(&scan, &vma->node)) __i915_vma_pin(vma); else list_del_init(&vma->exec_list); @@ -210,48 +225,119 @@ found: if (ret == 0) ret = i915_vma_unbind(vma); } + + while (ret == 0 && (node = drm_mm_scan_color_evict(&scan))) { + vma = container_of(node, struct i915_vma, node); + ret = i915_vma_unbind(vma); + } + return ret; } -int -i915_gem_evict_for_vma(struct i915_vma *target) +/** + * i915_gem_evict_for_vma - Evict vmas to make room for binding a new one + * @vm: address space to evict from + * @target: range (and color) to evict for + * @flags: additional flags to control the eviction algorithm + * + * This function will try to evict vmas that overlap the target node. + * + * To clarify: This is for freeing up virtual address space, not for freeing + * memory in e.g. the shrinker. + */ +int i915_gem_evict_for_node(struct i915_address_space *vm, + struct drm_mm_node *target, + unsigned int flags) { - struct drm_mm_node *node, *next; + LIST_HEAD(eviction_list); + struct drm_mm_node *node; + u64 start = target->start; + u64 end = start + target->size; + struct i915_vma *vma, *next; + bool check_color; + int ret = 0; - lockdep_assert_held(&target->vm->dev->struct_mutex); + lockdep_assert_held(&vm->i915->drm.struct_mutex); + trace_i915_gem_evict_node(vm, target, flags); - list_for_each_entry_safe(node, next, - &target->vm->mm.head_node.node_list, - node_list) { - struct i915_vma *vma; - int ret; + /* Retire before we search the active list. Although we have + * reasonable accuracy in our retirement lists, we may have + * a stray pin (preventing eviction) that can only be resolved by + * retiring. + */ + if (!(flags & PIN_NONBLOCK)) + i915_gem_retire_requests(vm->i915); + + check_color = vm->mm.color_adjust; + if (check_color) { + /* Expand search to cover neighbouring guard pages (or lack!) */ + if (start > vm->start) + start -= I915_GTT_PAGE_SIZE; + if (end < vm->start + vm->total) + end += I915_GTT_PAGE_SIZE; + } - if (node->start + node->size <= target->node.start) - continue; - if (node->start >= target->node.start + target->node.size) + drm_mm_for_each_node_in_range(node, &vm->mm, start, end) { + /* If we find any non-objects (!vma), we cannot evict them */ + if (node->color == I915_COLOR_UNEVICTABLE) { + ret = -ENOSPC; break; + } vma = container_of(node, typeof(*vma), node); - if (i915_vma_is_pinned(vma)) { - if (!vma->exec_entry || i915_vma_pin_count(vma) > 1) - /* Object is pinned for some other use */ - return -EBUSY; + /* If we are using coloring to insert guard pages between + * different cache domains within the address space, we have + * to check whether the objects on either side of our range + * abutt and conflict. If they are in conflict, then we evict + * those as well to make room for our guard pages. + */ + if (check_color) { + if (vma->node.start + vma->node.size == node->start) { + if (vma->node.color == node->color) + continue; + } + if (vma->node.start == node->start + node->size) { + if (vma->node.color == node->color) + continue; + } + } - /* We need to evict a buffer in the same batch */ - if (vma->exec_entry->flags & EXEC_OBJECT_PINNED) - /* Overlapping fixed objects in the same batch */ - return -EINVAL; + if (flags & PIN_NONBLOCK && + (i915_vma_is_pinned(vma) || i915_vma_is_active(vma))) { + ret = -ENOSPC; + break; + } - return -ENOSPC; + /* Overlap of objects in the same batch? */ + if (i915_vma_is_pinned(vma) || !list_empty(&vma->exec_list)) { + ret = -ENOSPC; + if (vma->exec_entry && + vma->exec_entry->flags & EXEC_OBJECT_PINNED) + ret = -EINVAL; + break; } - ret = i915_vma_unbind(vma); - if (ret) - return ret; + /* Never show fear in the face of dragons! + * + * We cannot directly remove this node from within this + * iterator and as with i915_gem_evict_something() we employ + * the vma pin_count in order to prevent the action of + * unbinding one vma from freeing (by dropping its active + * reference) another in our eviction list. + */ + __i915_vma_pin(vma); + list_add(&vma->exec_list, &eviction_list); } - return 0; + list_for_each_entry_safe(vma, next, &eviction_list, exec_list) { + list_del_init(&vma->exec_list); + __i915_vma_unpin(vma); + if (ret == 0) + ret = i915_vma_unbind(vma); + } + + return ret; } /** @@ -273,11 +359,11 @@ int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle) struct i915_vma *vma, *next; int ret; - lockdep_assert_held(&vm->dev->struct_mutex); + lockdep_assert_held(&vm->i915->drm.struct_mutex); trace_i915_gem_evict_vm(vm); if (do_idle) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; if (i915_is_ggtt(vm)) { ret = i915_gem_switch_to_kernel_context(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index b8b877c91b0a..d02cfaefe1c8 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -184,7 +184,7 @@ eb_lookup_vmas(struct eb_vmas *eb, * from the (obj, vm) we don't run the risk of creating * duplicated vmas for the same vm. */ - vma = i915_gem_obj_lookup_or_create_vma(obj, vm, NULL); + vma = i915_vma_instance(obj, vm, NULL); if (unlikely(IS_ERR(vma))) { DRM_DEBUG("Failed to lookup VMA\n"); ret = PTR_ERR(vma); @@ -274,6 +274,7 @@ static void eb_destroy(struct eb_vmas *eb) exec_list); list_del_init(&vma->exec_list); i915_gem_execbuffer_unreserve_vma(vma); + vma->exec_entry = NULL; i915_vma_put(vma); } kfree(eb); @@ -435,12 +436,11 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj, PIN_MAPPABLE | PIN_NONBLOCK); if (IS_ERR(vma)) { memset(&cache->node, 0, sizeof(cache->node)); - ret = drm_mm_insert_node_in_range_generic + ret = drm_mm_insert_node_in_range (&ggtt->base.mm, &cache->node, - 4096, 0, 0, + PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE, 0, ggtt->mappable_end, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + DRM_MM_INSERT_LOW); if (ret) /* no inactive aperture space, use cpu reloc */ return NULL; } else { @@ -850,8 +850,7 @@ eb_vma_misplaced(struct i915_vma *vma) WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP && !i915_vma_is_ggtt(vma)); - if (entry->alignment && - vma->node.start & (entry->alignment - 1)) + if (entry->alignment && !IS_ALIGNED(vma->node.start, entry->alignment)) return true; if (vma->node.size < entry->pad_to_size) @@ -1232,14 +1231,12 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file, struct intel_engine_cs *engine, const u32 ctx_id) { struct i915_gem_context *ctx; - struct i915_ctx_hang_stats *hs; ctx = i915_gem_context_lookup(file->driver_priv, ctx_id); if (IS_ERR(ctx)) return ctx; - hs = &ctx->hang_stats; - if (hs->banned) { + if (i915_gem_context_is_banned(ctx)) { DRM_DEBUG("Context %u tried to submit while banned\n", ctx_id); return ERR_PTR(-EIO); } @@ -1260,6 +1257,7 @@ void i915_vma_move_to_active(struct i915_vma *vma, struct drm_i915_gem_object *obj = vma->obj; const unsigned int idx = req->engine->id; + lockdep_assert_held(&req->i915->drm.struct_mutex); GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); /* Add a reference if we're newly entering the active list. @@ -1715,7 +1713,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } params->args_batch_start_offset = args->batch_start_offset; - if (intel_engine_needs_cmd_parser(engine) && args->batch_len) { + if (engine->needs_cmd_parser && args->batch_len) { struct i915_vma *vma; vma = i915_gem_execbuffer_parse(engine, &shadow_exec_entry, diff --git a/drivers/gpu/drm/i915/i915_gem_fence_reg.c b/drivers/gpu/drm/i915/i915_gem_fence_reg.c index 0efa3571afc3..fadbe8f4c745 100644 --- a/drivers/gpu/drm/i915/i915_gem_fence_reg.c +++ b/drivers/gpu/drm/i915/i915_gem_fence_reg.c @@ -77,16 +77,17 @@ static void i965_write_fence_reg(struct drm_i915_fence_reg *fence, val = 0; if (vma) { - unsigned int tiling = i915_gem_object_get_tiling(vma->obj); - bool is_y_tiled = tiling == I915_TILING_Y; unsigned int stride = i915_gem_object_get_stride(vma->obj); - u32 row_size = stride * (is_y_tiled ? 32 : 8); - u32 size = rounddown((u32)vma->node.size, row_size); - val = ((vma->node.start + size - 4096) & 0xfffff000) << 32; - val |= vma->node.start & 0xfffff000; + GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma)); + GEM_BUG_ON(!IS_ALIGNED(vma->node.start, I965_FENCE_PAGE)); + GEM_BUG_ON(!IS_ALIGNED(vma->fence_size, I965_FENCE_PAGE)); + GEM_BUG_ON(!IS_ALIGNED(stride, 128)); + + val = (vma->node.start + vma->fence_size - I965_FENCE_PAGE) << 32; + val |= vma->node.start; val |= (u64)((stride / 128) - 1) << fence_pitch_shift; - if (is_y_tiled) + if (i915_gem_object_get_tiling(vma->obj) == I915_TILING_Y) val |= BIT(I965_FENCE_TILING_Y_SHIFT); val |= I965_FENCE_REG_VALID; } @@ -122,31 +123,24 @@ static void i915_write_fence_reg(struct drm_i915_fence_reg *fence, unsigned int tiling = i915_gem_object_get_tiling(vma->obj); bool is_y_tiled = tiling == I915_TILING_Y; unsigned int stride = i915_gem_object_get_stride(vma->obj); - int pitch_val; - int tile_width; - WARN((vma->node.start & ~I915_FENCE_START_MASK) || - !is_power_of_2(vma->node.size) || - (vma->node.start & (vma->node.size - 1)), - "object 0x%08llx [fenceable? %d] not 1M or pot-size (0x%08llx) aligned\n", - vma->node.start, - i915_vma_is_map_and_fenceable(vma), - vma->node.size); + GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma)); + GEM_BUG_ON(vma->node.start & ~I915_FENCE_START_MASK); + GEM_BUG_ON(!is_power_of_2(vma->fence_size)); + GEM_BUG_ON(!IS_ALIGNED(vma->node.start, vma->fence_size)); if (is_y_tiled && HAS_128_BYTE_Y_TILING(fence->i915)) - tile_width = 128; + stride /= 128; else - tile_width = 512; - - /* Note: pitch better be a power of two tile widths */ - pitch_val = stride / tile_width; - pitch_val = ffs(pitch_val) - 1; + stride /= 512; + GEM_BUG_ON(!is_power_of_2(stride)); val = vma->node.start; if (is_y_tiled) val |= BIT(I830_FENCE_TILING_Y_SHIFT); - val |= I915_FENCE_SIZE_BITS(vma->node.size); - val |= pitch_val << I830_FENCE_PITCH_SHIFT; + val |= I915_FENCE_SIZE_BITS(vma->fence_size); + val |= ilog2(stride) << I830_FENCE_PITCH_SHIFT; + val |= I830_FENCE_REG_VALID; } @@ -166,25 +160,19 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *fence, val = 0; if (vma) { - unsigned int tiling = i915_gem_object_get_tiling(vma->obj); - bool is_y_tiled = tiling == I915_TILING_Y; unsigned int stride = i915_gem_object_get_stride(vma->obj); - u32 pitch_val; - - WARN((vma->node.start & ~I830_FENCE_START_MASK) || - !is_power_of_2(vma->node.size) || - (vma->node.start & (vma->node.size - 1)), - "object 0x%08llx not 512K or pot-size 0x%08llx aligned\n", - vma->node.start, vma->node.size); - pitch_val = stride / 128; - pitch_val = ffs(pitch_val) - 1; + GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma)); + GEM_BUG_ON(vma->node.start & ~I830_FENCE_START_MASK); + GEM_BUG_ON(!is_power_of_2(vma->fence_size)); + GEM_BUG_ON(!is_power_of_2(stride / 128)); + GEM_BUG_ON(!IS_ALIGNED(vma->node.start, vma->fence_size)); val = vma->node.start; - if (is_y_tiled) + if (i915_gem_object_get_tiling(vma->obj) == I915_TILING_Y) val |= BIT(I830_FENCE_TILING_Y_SHIFT); - val |= I830_FENCE_SIZE_BITS(vma->node.size); - val |= pitch_val << I830_FENCE_PITCH_SHIFT; + val |= I830_FENCE_SIZE_BITS(vma->fence_size); + val |= ilog2(stride / 128) << I830_FENCE_PITCH_SHIFT; val |= I830_FENCE_REG_VALID; } @@ -290,7 +278,7 @@ i915_vma_put_fence(struct i915_vma *vma) { struct drm_i915_fence_reg *fence = vma->fence; - assert_rpm_wakelock_held(to_i915(vma->vm->dev)); + assert_rpm_wakelock_held(vma->vm->i915); if (!fence) return 0; @@ -313,7 +301,7 @@ static struct drm_i915_fence_reg *fence_find(struct drm_i915_private *dev_priv) } /* Wait for completion of pending flips which consume fences */ - if (intel_has_pending_fb_unpin(&dev_priv->drm)) + if (intel_has_pending_fb_unpin(dev_priv)) return ERR_PTR(-EAGAIN); return ERR_PTR(-EDEADLK); @@ -346,7 +334,7 @@ i915_vma_get_fence(struct i915_vma *vma) /* Note that we revoke fences on runtime suspend. Therefore the user * must keep the device awake whilst using the fence. */ - assert_rpm_wakelock_held(to_i915(vma->vm->dev)); + assert_rpm_wakelock_held(vma->vm->i915); /* Just update our place in the LRU if our fence is getting reused. */ if (vma->fence) { @@ -357,7 +345,7 @@ i915_vma_get_fence(struct i915_vma *vma) return 0; } } else if (set) { - fence = fence_find(to_i915(vma->vm->dev)); + fence = fence_find(vma->vm->i915); if (IS_ERR(fence)) return PTR_ERR(fence); } else @@ -367,6 +355,30 @@ i915_vma_get_fence(struct i915_vma *vma) } /** + * i915_gem_revoke_fences - revoke fence state + * @dev_priv: i915 device private + * + * Removes all GTT mmappings via the fence registers. This forces any user + * of the fence to reacquire that fence before continuing with their access. + * One use is during GPU reset where the fence register is lost and we need to + * revoke concurrent userspace access via GTT mmaps until the hardware has been + * reset and the fence registers have been restored. + */ +void i915_gem_revoke_fences(struct drm_i915_private *dev_priv) +{ + int i; + + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + for (i = 0; i < dev_priv->num_fence_regs; i++) { + struct drm_i915_fence_reg *fence = &dev_priv->fence_regs[i]; + + if (fence->vma) + i915_gem_release_mmap(fence->vma->obj); + } +} + +/** * i915_gem_restore_fences - restore fence state * @dev_priv: i915 device private * @@ -512,8 +524,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_i915_private *dev_priv) */ swizzle_x = I915_BIT_6_SWIZZLE_NONE; swizzle_y = I915_BIT_6_SWIZZLE_NONE; - } else if (IS_MOBILE(dev_priv) || (IS_GEN3(dev_priv) && - !IS_G33(dev_priv))) { + } else if (IS_MOBILE(dev_priv) || + IS_I915G(dev_priv) || IS_I945G(dev_priv)) { uint32_t dcc; /* On 9xx chipsets, channel interleave by the CPU is diff --git a/drivers/gpu/drm/i915/i915_gem_fence_reg.h b/drivers/gpu/drm/i915/i915_gem_fence_reg.h index 22c4a2d01adf..99a31ded4dfd 100644 --- a/drivers/gpu/drm/i915/i915_gem_fence_reg.h +++ b/drivers/gpu/drm/i915/i915_gem_fence_reg.h @@ -30,6 +30,8 @@ struct drm_i915_private; struct i915_vma; +#define I965_FENCE_PAGE 4096UL + struct drm_i915_fence_reg { struct list_head link; struct drm_i915_private *i915; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index b4bde1452f2a..2801a4d56324 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -23,10 +23,14 @@ * */ +#include <linux/log2.h> +#include <linux/random.h> #include <linux/seq_file.h> #include <linux/stop_machine.h> + #include <drm/drmP.h> #include <drm/i915_drm.h> + #include "i915_drv.h" #include "i915_vgpu.h" #include "i915_trace.h" @@ -99,12 +103,29 @@ static int i915_get_ggtt_vma_pages(struct i915_vma *vma); -const struct i915_ggtt_view i915_ggtt_view_normal = { - .type = I915_GGTT_VIEW_NORMAL, -}; -const struct i915_ggtt_view i915_ggtt_view_rotated = { - .type = I915_GGTT_VIEW_ROTATED, -}; +static void gen6_ggtt_invalidate(struct drm_i915_private *dev_priv) +{ + /* Note that as an uncached mmio write, this should flush the + * WCB of the writes into the GGTT before it triggers the invalidate. + */ + I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); +} + +static void guc_ggtt_invalidate(struct drm_i915_private *dev_priv) +{ + gen6_ggtt_invalidate(dev_priv); + I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); +} + +static void gmch_ggtt_invalidate(struct drm_i915_private *dev_priv) +{ + intel_gtt_chipset_flush(); +} + +static inline void i915_ggtt_invalidate(struct drm_i915_private *i915) +{ + i915->ggtt.invalidate(i915); +} int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, int enable_ppgtt) @@ -113,10 +134,9 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv, bool has_full_ppgtt; bool has_full_48bit_ppgtt; - has_aliasing_ppgtt = INTEL_GEN(dev_priv) >= 6; - has_full_ppgtt = INTEL_GEN(dev_priv) >= 7; - has_full_48bit_ppgtt = - IS_BROADWELL(dev_priv) || INTEL_GEN(dev_priv) >= 9; + has_aliasing_ppgtt = dev_priv->info.has_aliasing_ppgtt; + has_full_ppgtt = dev_priv->info.has_full_ppgtt; + has_full_48bit_ppgtt = dev_priv->info.has_full_48bit_ppgtt; if (intel_vgpu_active(dev_priv)) { /* emulation is too hard */ @@ -330,7 +350,7 @@ static int __setup_page_dma(struct drm_i915_private *dev_priv, return -ENOMEM; p->daddr = dma_map_page(kdev, - p->page, 0, 4096, PCI_DMA_BIDIRECTIONAL); + p->page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (dma_mapping_error(kdev, p->daddr)) { __free_page(p->page); @@ -354,7 +374,7 @@ static void cleanup_page_dma(struct drm_i915_private *dev_priv, if (WARN_ON(!p->page)) return; - dma_unmap_page(&pdev->dev, p->daddr, 4096, PCI_DMA_BIDIRECTIONAL); + dma_unmap_page(&pdev->dev, p->daddr, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); __free_page(p->page); memset(p, 0, sizeof(*p)); } @@ -372,7 +392,7 @@ static void kunmap_page_dma(struct drm_i915_private *dev_priv, void *vaddr) /* There are only few exceptions for gen >=6. chv and bxt. * And we are not sure about the latter so play safe for now. */ - if (IS_CHERRYVIEW(dev_priv) || IS_BROXTON(dev_priv)) + if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv)) drm_clflush_virt_range(vaddr, PAGE_SIZE); kunmap_atomic(vaddr); @@ -380,7 +400,7 @@ static void kunmap_page_dma(struct drm_i915_private *dev_priv, void *vaddr) #define kmap_px(px) kmap_page_dma(px_base(px)) #define kunmap_px(ppgtt, vaddr) \ - kunmap_page_dma(to_i915((ppgtt)->base.dev), (vaddr)) + kunmap_page_dma((ppgtt)->base.i915, (vaddr)) #define setup_px(dev_priv, px) setup_page_dma((dev_priv), px_base(px)) #define cleanup_px(dev_priv, px) cleanup_page_dma((dev_priv), px_base(px)) @@ -470,7 +490,7 @@ static void gen8_initialize_pt(struct i915_address_space *vm, scratch_pte = gen8_pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC); - fill_px(to_i915(vm->dev), pt, scratch_pte); + fill_px(vm->i915, pt, scratch_pte); } static void gen6_initialize_pt(struct i915_address_space *vm, @@ -483,7 +503,7 @@ static void gen6_initialize_pt(struct i915_address_space *vm, scratch_pte = vm->pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC, 0); - fill32_px(to_i915(vm->dev), pt, scratch_pte); + fill32_px(vm->i915, pt, scratch_pte); } static struct i915_page_directory *alloc_pd(struct drm_i915_private *dev_priv) @@ -531,7 +551,7 @@ static void gen8_initialize_pd(struct i915_address_space *vm, scratch_pde = gen8_pde_encode(px_dma(vm->scratch_pt), I915_CACHE_LLC); - fill_px(to_i915(vm->dev), pd, scratch_pde); + fill_px(vm->i915, pd, scratch_pde); } static int __pdp_init(struct drm_i915_private *dev_priv, @@ -612,7 +632,7 @@ static void gen8_initialize_pdp(struct i915_address_space *vm, scratch_pdpe = gen8_pdpe_encode(px_dma(vm->scratch_pd), I915_CACHE_LLC); - fill_px(to_i915(vm->dev), pdp, scratch_pdpe); + fill_px(vm->i915, pdp, scratch_pdpe); } static void gen8_initialize_pml4(struct i915_address_space *vm, @@ -623,14 +643,14 @@ static void gen8_initialize_pml4(struct i915_address_space *vm, scratch_pml4e = gen8_pml4e_encode(px_dma(vm->scratch_pdp), I915_CACHE_LLC); - fill_px(to_i915(vm->dev), pml4, scratch_pml4e); + fill_px(vm->i915, pml4, scratch_pml4e); } static void -gen8_setup_page_directory(struct i915_hw_ppgtt *ppgtt, - struct i915_page_directory_pointer *pdp, - struct i915_page_directory *pd, - int index) +gen8_setup_pdpe(struct i915_hw_ppgtt *ppgtt, + struct i915_page_directory_pointer *pdp, + struct i915_page_directory *pd, + int index) { gen8_ppgtt_pdpe_t *page_directorypo; @@ -643,10 +663,10 @@ gen8_setup_page_directory(struct i915_hw_ppgtt *ppgtt, } static void -gen8_setup_page_directory_pointer(struct i915_hw_ppgtt *ppgtt, - struct i915_pml4 *pml4, - struct i915_page_directory_pointer *pdp, - int index) +gen8_setup_pml4e(struct i915_hw_ppgtt *ppgtt, + struct i915_pml4 *pml4, + struct i915_page_directory_pointer *pdp, + int index) { gen8_ppgtt_pml4e_t *pagemap = kmap_px(pml4); @@ -710,7 +730,7 @@ static int gen8_48b_mm_switch(struct i915_hw_ppgtt *ppgtt, */ static void mark_tlbs_dirty(struct i915_hw_ppgtt *ppgtt) { - ppgtt->pd_dirty_rings = INTEL_INFO(to_i915(ppgtt->base.dev))->ring_mask; + ppgtt->pd_dirty_rings = INTEL_INFO(ppgtt->base.i915)->ring_mask; } /* Removes entries from a single page table, releasing it if it's empty. @@ -735,10 +755,9 @@ static bool gen8_ppgtt_clear_pt(struct i915_address_space *vm, GEM_BUG_ON(pte_end > GEN8_PTES); bitmap_clear(pt->used_ptes, pte, num_entries); - - if (bitmap_empty(pt->used_ptes, GEN8_PTES)) { - free_pt(to_i915(vm->dev), pt); - return true; + if (USES_FULL_PPGTT(vm->i915)) { + if (bitmap_empty(pt->used_ptes, GEN8_PTES)) + return true; } pt_vaddr = kmap_px(pt); @@ -775,13 +794,12 @@ static bool gen8_ppgtt_clear_pd(struct i915_address_space *vm, pde_vaddr = kmap_px(pd); pde_vaddr[pde] = scratch_pde; kunmap_px(ppgtt, pde_vaddr); + free_pt(vm->i915, pt); } } - if (bitmap_empty(pd->used_pdes, I915_PDES)) { - free_pd(to_i915(vm->dev), pd); + if (bitmap_empty(pd->used_pdes, I915_PDES)) return true; - } return false; } @@ -795,12 +813,8 @@ static bool gen8_ppgtt_clear_pdp(struct i915_address_space *vm, uint64_t length) { struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); - struct drm_i915_private *dev_priv = to_i915(vm->dev); struct i915_page_directory *pd; uint64_t pdpe; - gen8_ppgtt_pdpe_t *pdpe_vaddr; - gen8_ppgtt_pdpe_t scratch_pdpe = - gen8_pdpe_encode(px_dma(vm->scratch_pd), I915_CACHE_LLC); gen8_for_each_pdpe(pd, pdp, start, length, pdpe) { if (WARN_ON(!pdp->page_directory[pdpe])) @@ -808,21 +822,15 @@ static bool gen8_ppgtt_clear_pdp(struct i915_address_space *vm, if (gen8_ppgtt_clear_pd(vm, pd, start, length)) { __clear_bit(pdpe, pdp->used_pdpes); - if (USES_FULL_48BIT_PPGTT(dev_priv)) { - pdpe_vaddr = kmap_px(pdp); - pdpe_vaddr[pdpe] = scratch_pdpe; - kunmap_px(ppgtt, pdpe_vaddr); - } + gen8_setup_pdpe(ppgtt, pdp, vm->scratch_pd, pdpe); + free_pd(vm->i915, pd); } } mark_tlbs_dirty(ppgtt); - if (USES_FULL_48BIT_PPGTT(dev_priv) && - bitmap_empty(pdp->used_pdpes, I915_PDPES_PER_PDP(dev_priv))) { - free_pdp(dev_priv, pdp); + if (bitmap_empty(pdp->used_pdpes, I915_PDPES_PER_PDP(dev_priv))) return true; - } return false; } @@ -839,11 +847,8 @@ static void gen8_ppgtt_clear_pml4(struct i915_address_space *vm, struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); struct i915_page_directory_pointer *pdp; uint64_t pml4e; - gen8_ppgtt_pml4e_t *pml4e_vaddr; - gen8_ppgtt_pml4e_t scratch_pml4e = - gen8_pml4e_encode(px_dma(vm->scratch_pdp), I915_CACHE_LLC); - GEM_BUG_ON(!USES_FULL_48BIT_PPGTT(to_i915(vm->dev))); + GEM_BUG_ON(!USES_FULL_48BIT_PPGTT(vm->i915)); gen8_for_each_pml4e(pdp, pml4, start, length, pml4e) { if (WARN_ON(!pml4->pdps[pml4e])) @@ -851,9 +856,8 @@ static void gen8_ppgtt_clear_pml4(struct i915_address_space *vm, if (gen8_ppgtt_clear_pdp(vm, pdp, start, length)) { __clear_bit(pml4e, pml4->used_pml4es); - pml4e_vaddr = kmap_px(pml4); - pml4e_vaddr[pml4e] = scratch_pml4e; - kunmap_px(ppgtt, pml4e_vaddr); + gen8_setup_pml4e(ppgtt, pml4, vm->scratch_pdp, pml4e); + free_pdp(vm->i915, pdp); } } } @@ -863,7 +867,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); - if (USES_FULL_48BIT_PPGTT(to_i915(vm->dev))) + if (USES_FULL_48BIT_PPGTT(vm->i915)) gen8_ppgtt_clear_pml4(vm, &ppgtt->pml4, start, length); else gen8_ppgtt_clear_pdp(vm, &ppgtt->pdp, start, length); @@ -898,7 +902,7 @@ gen8_ppgtt_insert_pte_entries(struct i915_address_space *vm, kunmap_px(ppgtt, pt_vaddr); pt_vaddr = NULL; if (++pde == I915_PDES) { - if (++pdpe == I915_PDPES_PER_PDP(to_i915(vm->dev))) + if (++pdpe == I915_PDPES_PER_PDP(vm->i915)) break; pde = 0; } @@ -921,7 +925,7 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm, __sg_page_iter_start(&sg_iter, pages->sgl, sg_nents(pages->sgl), 0); - if (!USES_FULL_48BIT_PPGTT(to_i915(vm->dev))) { + if (!USES_FULL_48BIT_PPGTT(vm->i915)) { gen8_ppgtt_insert_pte_entries(vm, &ppgtt->pdp, &sg_iter, start, cache_level); } else { @@ -955,7 +959,7 @@ static void gen8_free_page_tables(struct drm_i915_private *dev_priv, static int gen8_init_scratch(struct i915_address_space *vm) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; int ret; ret = setup_scratch_page(dev_priv, &vm->scratch_page, I915_GFP_DMA); @@ -1002,7 +1006,7 @@ free_scratch_page: static int gen8_ppgtt_notify_vgt(struct i915_hw_ppgtt *ppgtt, bool create) { enum vgt_g2v_type msg; - struct drm_i915_private *dev_priv = to_i915(ppgtt->base.dev); + struct drm_i915_private *dev_priv = ppgtt->base.i915; int i; if (USES_FULL_48BIT_PPGTT(dev_priv)) { @@ -1032,7 +1036,7 @@ static int gen8_ppgtt_notify_vgt(struct i915_hw_ppgtt *ppgtt, bool create) static void gen8_free_scratch(struct i915_address_space *vm) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; if (USES_FULL_48BIT_PPGTT(dev_priv)) free_pdp(dev_priv, vm->scratch_pdp); @@ -1059,7 +1063,7 @@ static void gen8_ppgtt_cleanup_3lvl(struct drm_i915_private *dev_priv, static void gen8_ppgtt_cleanup_4lvl(struct i915_hw_ppgtt *ppgtt) { - struct drm_i915_private *dev_priv = to_i915(ppgtt->base.dev); + struct drm_i915_private *dev_priv = ppgtt->base.i915; int i; for_each_set_bit(i, ppgtt->pml4.used_pml4es, GEN8_PML4ES_PER_PML4) { @@ -1074,7 +1078,7 @@ static void gen8_ppgtt_cleanup_4lvl(struct i915_hw_ppgtt *ppgtt) static void gen8_ppgtt_cleanup(struct i915_address_space *vm) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); if (intel_vgpu_active(dev_priv)) @@ -1112,7 +1116,7 @@ static int gen8_ppgtt_alloc_pagetabs(struct i915_address_space *vm, uint64_t length, unsigned long *new_pts) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; struct i915_page_table *pt; uint32_t pde; @@ -1173,7 +1177,7 @@ gen8_ppgtt_alloc_page_directories(struct i915_address_space *vm, uint64_t length, unsigned long *new_pds) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; struct i915_page_directory *pd; uint32_t pdpe; uint32_t pdpes = I915_PDPES_PER_PDP(dev_priv); @@ -1226,7 +1230,7 @@ gen8_ppgtt_alloc_page_dirpointers(struct i915_address_space *vm, uint64_t length, unsigned long *new_pdps) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; struct i915_page_directory_pointer *pdp; uint32_t pml4e; @@ -1301,7 +1305,7 @@ static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); unsigned long *new_page_dirs, *new_page_tables; - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; struct i915_page_directory *pd; const uint64_t orig_start = start; const uint64_t orig_length = length; @@ -1309,15 +1313,6 @@ static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm, uint32_t pdpes = I915_PDPES_PER_PDP(dev_priv); int ret; - /* Wrap is never okay since we can only represent 48b, and we don't - * actually use the other side of the canonical address space. - */ - if (WARN_ON(start + length < start)) - return -ENODEV; - - if (WARN_ON(start + length > vm->total)) - return -ENODEV; - ret = alloc_gen8_temp_bitmaps(&new_page_dirs, &new_page_tables, pdpes); if (ret) return ret; @@ -1381,7 +1376,7 @@ static int gen8_alloc_va_range_3lvl(struct i915_address_space *vm, kunmap_px(ppgtt, page_directory); __set_bit(pdpe, pdp->used_pdpes); - gen8_setup_page_directory(ppgtt, pdp, pd, pdpe); + gen8_setup_pdpe(ppgtt, pdp, pd, pdpe); } free_gen8_temp_bitmaps(new_page_dirs, new_page_tables); @@ -1440,7 +1435,7 @@ static int gen8_alloc_va_range_4lvl(struct i915_address_space *vm, if (ret) goto err_out; - gen8_setup_page_directory_pointer(ppgtt, pml4, pdp, pml4e); + gen8_setup_pml4e(ppgtt, pml4, pdp, pml4e); } bitmap_or(pml4->used_pml4es, new_pdps, pml4->used_pml4es, @@ -1450,7 +1445,7 @@ static int gen8_alloc_va_range_4lvl(struct i915_address_space *vm, err_out: for_each_set_bit(pml4e, new_pdps, GEN8_PML4ES_PER_PML4) - gen8_ppgtt_cleanup_3lvl(to_i915(vm->dev), pml4->pdps[pml4e]); + gen8_ppgtt_cleanup_3lvl(vm->i915, pml4->pdps[pml4e]); return ret; } @@ -1460,7 +1455,7 @@ static int gen8_alloc_va_range(struct i915_address_space *vm, { struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); - if (USES_FULL_48BIT_PPGTT(to_i915(vm->dev))) + if (USES_FULL_48BIT_PPGTT(vm->i915)) return gen8_alloc_va_range_4lvl(vm, &ppgtt->pml4, start, length); else return gen8_alloc_va_range_3lvl(vm, &ppgtt->pdp, start, length); @@ -1531,7 +1526,7 @@ static void gen8_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m) gen8_pte_t scratch_pte = gen8_pte_encode(vm->scratch_page.daddr, I915_CACHE_LLC); - if (!USES_FULL_48BIT_PPGTT(to_i915(vm->dev))) { + if (!USES_FULL_48BIT_PPGTT(vm->i915)) { gen8_dump_pdp(&ppgtt->pdp, start, length, scratch_pte, m); } else { uint64_t pml4e; @@ -1584,7 +1579,7 @@ static int gen8_preallocate_top_level_pdps(struct i915_hw_ppgtt *ppgtt) */ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt) { - struct drm_i915_private *dev_priv = to_i915(ppgtt->base.dev); + struct drm_i915_private *dev_priv = ppgtt->base.i915; int ret; ret = gen8_init_scratch(&ppgtt->base); @@ -1927,7 +1922,7 @@ static int gen6_alloc_va_range(struct i915_address_space *vm, uint64_t start_in, uint64_t length_in) { DECLARE_BITMAP(new_page_tables, I915_PDES); - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; struct i915_ggtt *ggtt = &dev_priv->ggtt; struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); struct i915_page_table *pt; @@ -1935,9 +1930,6 @@ static int gen6_alloc_va_range(struct i915_address_space *vm, uint32_t pde; int ret; - if (WARN_ON(start_in + length_in > ppgtt->base.total)) - return -ENODEV; - start = start_save = start_in; length = length_save = length_in; @@ -2014,7 +2006,7 @@ unwind_out: static int gen6_init_scratch(struct i915_address_space *vm) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; int ret; ret = setup_scratch_page(dev_priv, &vm->scratch_page, I915_GFP_DMA); @@ -2034,7 +2026,7 @@ static int gen6_init_scratch(struct i915_address_space *vm) static void gen6_free_scratch(struct i915_address_space *vm) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; free_pt(dev_priv, vm->scratch_pt); cleanup_scratch_page(dev_priv, &vm->scratch_page); @@ -2044,7 +2036,7 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm) { struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm); struct i915_page_directory *pd = &ppgtt->pd; - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct drm_i915_private *dev_priv = vm->i915; struct i915_page_table *pt; uint32_t pde; @@ -2060,9 +2052,8 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm) static int gen6_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt) { struct i915_address_space *vm = &ppgtt->base; - struct drm_i915_private *dev_priv = to_i915(ppgtt->base.dev); + struct drm_i915_private *dev_priv = ppgtt->base.i915; struct i915_ggtt *ggtt = &dev_priv->ggtt; - bool retried = false; int ret; /* PPGTT PDEs reside in the GGTT and consists of 512 entries. The @@ -2075,29 +2066,14 @@ static int gen6_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt) if (ret) return ret; -alloc: - ret = drm_mm_insert_node_in_range_generic(&ggtt->base.mm, - &ppgtt->node, GEN6_PD_SIZE, - GEN6_PD_ALIGN, 0, - 0, ggtt->base.total, - DRM_MM_TOPDOWN); - if (ret == -ENOSPC && !retried) { - ret = i915_gem_evict_something(&ggtt->base, - GEN6_PD_SIZE, GEN6_PD_ALIGN, - I915_CACHE_NONE, - 0, ggtt->base.total, - 0); - if (ret) - goto err_out; - - retried = true; - goto alloc; - } - + ret = i915_gem_gtt_insert(&ggtt->base, &ppgtt->node, + GEN6_PD_SIZE, GEN6_PD_ALIGN, + I915_COLOR_UNEVICTABLE, + 0, ggtt->base.total, + PIN_HIGH); if (ret) goto err_out; - if (ppgtt->node.start < ggtt->mappable_end) DRM_DEBUG("Forced to use aperture for PDEs\n"); @@ -2125,7 +2101,7 @@ static void gen6_scratch_va_range(struct i915_hw_ppgtt *ppgtt, static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) { - struct drm_i915_private *dev_priv = to_i915(ppgtt->base.dev); + struct drm_i915_private *dev_priv = ppgtt->base.i915; struct i915_ggtt *ggtt = &dev_priv->ggtt; int ret; @@ -2176,7 +2152,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) static int __hw_ppgtt_init(struct i915_hw_ppgtt *ppgtt, struct drm_i915_private *dev_priv) { - ppgtt->base.dev = &dev_priv->drm; + ppgtt->base.i915 = dev_priv; if (INTEL_INFO(dev_priv)->gen < 8) return gen6_ppgtt_init(ppgtt); @@ -2285,6 +2261,27 @@ i915_ppgtt_create(struct drm_i915_private *dev_priv, return ppgtt; } +void i915_ppgtt_close(struct i915_address_space *vm) +{ + struct list_head *phases[] = { + &vm->active_list, + &vm->inactive_list, + &vm->unbound_list, + NULL, + }, **phase; + + GEM_BUG_ON(vm->closed); + vm->closed = true; + + for (phase = phases; *phase; phase++) { + struct i915_vma *vma, *vn; + + list_for_each_entry_safe(vma, vn, *phase, vm_link) + if (!i915_vma_is_closed(vma)) + i915_vma_close(vma); + } +} + void i915_ppgtt_release(struct kref *kref) { struct i915_hw_ppgtt *ppgtt = @@ -2349,16 +2346,6 @@ void i915_check_and_clear_faults(struct drm_i915_private *dev_priv) POSTING_READ(RING_FAULT_REG(dev_priv->engine[RCS])); } -static void i915_ggtt_flush(struct drm_i915_private *dev_priv) -{ - if (INTEL_INFO(dev_priv)->gen < 6) { - intel_gtt_chipset_flush(); - } else { - I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); - POSTING_READ(GFX_FLSH_CNTL_GEN6); - } -} - void i915_gem_suspend_gtt_mappings(struct drm_i915_private *dev_priv) { struct i915_ggtt *ggtt = &dev_priv->ggtt; @@ -2373,16 +2360,30 @@ void i915_gem_suspend_gtt_mappings(struct drm_i915_private *dev_priv) ggtt->base.clear_range(&ggtt->base, ggtt->base.start, ggtt->base.total); - i915_ggtt_flush(dev_priv); + i915_ggtt_invalidate(dev_priv); } int i915_gem_gtt_prepare_pages(struct drm_i915_gem_object *obj, struct sg_table *pages) { - if (dma_map_sg(&obj->base.dev->pdev->dev, - pages->sgl, pages->nents, - PCI_DMA_BIDIRECTIONAL)) - return 0; + do { + if (dma_map_sg(&obj->base.dev->pdev->dev, + pages->sgl, pages->nents, + PCI_DMA_BIDIRECTIONAL)) + return 0; + + /* If the DMA remap fails, one cause can be that we have + * too many objects pinned in a small remapping table, + * such as swiotlb. Incrementally purge all other objects and + * try again - if there are no more pages to remove from + * the DMA remapper, i915_gem_shrink will return 0. + */ + GEM_BUG_ON(obj->mm.pages == pages); + } while (i915_gem_shrink(to_i915(obj->base.dev), + obj->base.size >> PAGE_SHIFT, + I915_SHRINK_BOUND | + I915_SHRINK_UNBOUND | + I915_SHRINK_ACTIVE)); return -ENOSPC; } @@ -2398,15 +2399,13 @@ static void gen8_ggtt_insert_page(struct i915_address_space *vm, enum i915_cache_level level, u32 unused) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm); gen8_pte_t __iomem *pte = - (gen8_pte_t __iomem *)dev_priv->ggtt.gsm + - (offset >> PAGE_SHIFT); + (gen8_pte_t __iomem *)ggtt->gsm + (offset >> PAGE_SHIFT); gen8_set_pte(pte, gen8_pte_encode(addr, level)); - I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); - POSTING_READ(GFX_FLSH_CNTL_GEN6); + ggtt->invalidate(vm->i915); } static void gen8_ggtt_insert_entries(struct i915_address_space *vm, @@ -2414,7 +2413,6 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm, uint64_t start, enum i915_cache_level level, u32 unused) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm); struct sgt_iter sgt_iter; gen8_pte_t __iomem *gtt_entries; @@ -2443,8 +2441,7 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm, * want to flush the TLBs only after we're certain all the PTE updates * have finished. */ - I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); - POSTING_READ(GFX_FLSH_CNTL_GEN6); + ggtt->invalidate(vm->i915); } struct insert_entries { @@ -2479,15 +2476,13 @@ static void gen6_ggtt_insert_page(struct i915_address_space *vm, enum i915_cache_level level, u32 flags) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); + struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm); gen6_pte_t __iomem *pte = - (gen6_pte_t __iomem *)dev_priv->ggtt.gsm + - (offset >> PAGE_SHIFT); + (gen6_pte_t __iomem *)ggtt->gsm + (offset >> PAGE_SHIFT); iowrite32(vm->pte_encode(addr, level, flags), pte); - I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); - POSTING_READ(GFX_FLSH_CNTL_GEN6); + ggtt->invalidate(vm->i915); } /* @@ -2501,7 +2496,6 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm, uint64_t start, enum i915_cache_level level, u32 flags) { - struct drm_i915_private *dev_priv = to_i915(vm->dev); struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm); struct sgt_iter sgt_iter; gen6_pte_t __iomem *gtt_entries; @@ -2529,8 +2523,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm, * want to flush the TLBs only after we're certain all the PTE updates * have finished. */ - I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); - POSTING_READ(GFX_FLSH_CNTL_GEN6); + ggtt->invalidate(vm->i915); } static void nop_clear_range(struct i915_address_space *vm, @@ -2621,7 +2614,7 @@ static int ggtt_bind_vma(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags) { - struct drm_i915_private *i915 = to_i915(vma->vm->dev); + struct drm_i915_private *i915 = vma->vm->i915; struct drm_i915_gem_object *obj = vma->obj; u32 pte_flags = 0; int ret; @@ -2653,7 +2646,7 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags) { - struct drm_i915_private *i915 = to_i915(vma->vm->dev); + struct drm_i915_private *i915 = vma->vm->i915; u32 pte_flags; int ret; @@ -2687,7 +2680,7 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma, static void ggtt_unbind_vma(struct i915_vma *vma) { - struct drm_i915_private *i915 = to_i915(vma->vm->dev); + struct drm_i915_private *i915 = vma->vm->i915; struct i915_hw_ppgtt *appgtt = i915->mm.aliasing_ppgtt; const u64 size = min(vma->size, vma->node.size); @@ -2721,19 +2714,17 @@ void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj, dma_unmap_sg(kdev, pages->sgl, pages->nents, PCI_DMA_BIDIRECTIONAL); } -static void i915_gtt_color_adjust(struct drm_mm_node *node, +static void i915_gtt_color_adjust(const struct drm_mm_node *node, unsigned long color, u64 *start, u64 *end) { if (node->color != color) - *start += 4096; + *start += I915_GTT_PAGE_SIZE; - node = list_first_entry_or_null(&node->node_list, - struct drm_mm_node, - node_list); - if (node && node->allocated && node->color != color) - *end -= 4096; + node = list_next_entry(node, node_list); + if (node->allocated && node->color != color) + *end -= I915_GTT_PAGE_SIZE; } int i915_gem_init_ggtt(struct drm_i915_private *dev_priv) @@ -2758,11 +2749,10 @@ int i915_gem_init_ggtt(struct drm_i915_private *dev_priv) return ret; /* Reserve a mappable slot for our lockless error capture */ - ret = drm_mm_insert_node_in_range_generic(&ggtt->base.mm, - &ggtt->error_capture, - 4096, 0, -1, - 0, ggtt->mappable_end, - 0, 0); + ret = drm_mm_insert_node_in_range(&ggtt->base.mm, &ggtt->error_capture, + PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE, + 0, ggtt->mappable_end, + DRM_MM_INSERT_LOW); if (ret) return ret; @@ -2929,8 +2919,8 @@ static size_t gen9_get_stolen_size(u16 gen9_gmch_ctl) static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size) { - struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev); - struct pci_dev *pdev = ggtt->base.dev->pdev; + struct drm_i915_private *dev_priv = ggtt->base.i915; + struct pci_dev *pdev = dev_priv->drm.pdev; phys_addr_t phys_addr; int ret; @@ -2944,7 +2934,7 @@ static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size) * resort to an uncached mapping. The WC issue is easily caught by the * readback check when writing GTT PTE entries. */ - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) ggtt->gsm = ioremap_nocache(phys_addr, size); else ggtt->gsm = ioremap_wc(phys_addr, size); @@ -3042,12 +3032,12 @@ static void gen6_gmch_remove(struct i915_address_space *vm) struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm); iounmap(ggtt->gsm); - cleanup_scratch_page(to_i915(vm->dev), &vm->scratch_page); + cleanup_scratch_page(vm->i915, &vm->scratch_page); } static int gen8_gmch_probe(struct i915_ggtt *ggtt) { - struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev); + struct drm_i915_private *dev_priv = ggtt->base.i915; struct pci_dev *pdev = dev_priv->drm.pdev; unsigned int size; u16 snb_gmch_ctl; @@ -3074,7 +3064,7 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt) ggtt->base.total = (size / sizeof(gen8_pte_t)) << PAGE_SHIFT; - if (IS_CHERRYVIEW(dev_priv) || IS_BROXTON(dev_priv)) + if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv)) chv_setup_private_ppat(dev_priv); else bdw_setup_private_ppat(dev_priv); @@ -3091,12 +3081,14 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt) if (IS_CHERRYVIEW(dev_priv)) ggtt->base.insert_entries = gen8_ggtt_insert_entries__BKL; + ggtt->invalidate = gen6_ggtt_invalidate; + return ggtt_probe_common(ggtt, size); } static int gen6_gmch_probe(struct i915_ggtt *ggtt) { - struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev); + struct drm_i915_private *dev_priv = ggtt->base.i915; struct pci_dev *pdev = dev_priv->drm.pdev; unsigned int size; u16 snb_gmch_ctl; @@ -3128,6 +3120,8 @@ static int gen6_gmch_probe(struct i915_ggtt *ggtt) ggtt->base.unbind_vma = ggtt_unbind_vma; ggtt->base.cleanup = gen6_gmch_remove; + ggtt->invalidate = gen6_ggtt_invalidate; + if (HAS_EDRAM(dev_priv)) ggtt->base.pte_encode = iris_pte_encode; else if (IS_HASWELL(dev_priv)) @@ -3149,7 +3143,7 @@ static void i915_gmch_remove(struct i915_address_space *vm) static int i915_gmch_probe(struct i915_ggtt *ggtt) { - struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev); + struct drm_i915_private *dev_priv = ggtt->base.i915; int ret; ret = intel_gmch_probe(dev_priv->bridge_dev, dev_priv->drm.pdev, NULL); @@ -3158,8 +3152,10 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt) return -EIO; } - intel_gtt_get(&ggtt->base.total, &ggtt->stolen_size, - &ggtt->mappable_base, &ggtt->mappable_end); + intel_gtt_get(&ggtt->base.total, + &ggtt->stolen_size, + &ggtt->mappable_base, + &ggtt->mappable_end); ggtt->do_idle_maps = needs_idle_maps(dev_priv); ggtt->base.insert_page = i915_ggtt_insert_page; @@ -3169,6 +3165,8 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt) ggtt->base.unbind_vma = ggtt_unbind_vma; ggtt->base.cleanup = i915_gmch_remove; + ggtt->invalidate = gmch_ggtt_invalidate; + if (unlikely(ggtt->do_idle_maps)) DRM_INFO("applying Ironlake quirks for intel_iommu\n"); @@ -3184,7 +3182,7 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv) struct i915_ggtt *ggtt = &dev_priv->ggtt; int ret; - ggtt->base.dev = &dev_priv->drm; + ggtt->base.i915 = dev_priv; if (INTEL_GEN(dev_priv) <= 5) ret = i915_gmch_probe(ggtt); @@ -3195,6 +3193,16 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv) if (ret) return ret; + /* Trim the GGTT to fit the GuC mappable upper range (when enabled). + * This is easier than doing range restriction on the fly, as we + * currently don't have any bits spare to pass in this upper + * restriction! + */ + if (HAS_GUC(dev_priv) && i915.enable_guc_loading) { + ggtt->base.total = min_t(u64, ggtt->base.total, GUC_GGTT_TOP); + ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total); + } + if ((ggtt->base.total - 1) >> 32) { DRM_ERROR("We never expected a Global GTT with more than 32bits" " of address space! Found %lldM!\n", @@ -3214,7 +3222,7 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv) DRM_INFO("Memory usable by graphics device = %lluM\n", ggtt->base.total >> 20); DRM_DEBUG_DRIVER("GMADR size = %lldM\n", ggtt->mappable_end >> 20); - DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n", ggtt->stolen_size >> 20); + DRM_DEBUG_DRIVER("GTT stolen size = %uM\n", ggtt->stolen_size >> 20); #ifdef CONFIG_INTEL_IOMMU if (intel_iommu_gfx_mapped) DRM_INFO("VT-d active for gfx access\n"); @@ -3277,6 +3285,16 @@ int i915_ggtt_enable_hw(struct drm_i915_private *dev_priv) return 0; } +void i915_ggtt_enable_guc(struct drm_i915_private *i915) +{ + i915->ggtt.invalidate = guc_ggtt_invalidate; +} + +void i915_ggtt_disable_guc(struct drm_i915_private *i915) +{ + i915->ggtt.invalidate = gen6_ggtt_invalidate; +} + void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv) { struct i915_ggtt *ggtt = &dev_priv->ggtt; @@ -3314,7 +3332,7 @@ void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv) ggtt->base.closed = false; if (INTEL_GEN(dev_priv) >= 8) { - if (IS_CHERRYVIEW(dev_priv) || IS_BROXTON(dev_priv)) + if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv)) chv_setup_private_ppat(dev_priv); else bdw_setup_private_ppat(dev_priv); @@ -3340,52 +3358,7 @@ void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv) } } - i915_ggtt_flush(dev_priv); -} - -struct i915_vma * -i915_gem_obj_to_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *view) -{ - struct rb_node *rb; - - rb = obj->vma_tree.rb_node; - while (rb) { - struct i915_vma *vma = rb_entry(rb, struct i915_vma, obj_node); - long cmp; - - cmp = i915_vma_compare(vma, vm, view); - if (cmp == 0) - return vma; - - if (cmp < 0) - rb = rb->rb_right; - else - rb = rb->rb_left; - } - - return NULL; -} - -struct i915_vma * -i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *view) -{ - struct i915_vma *vma; - - lockdep_assert_held(&obj->base.dev->struct_mutex); - GEM_BUG_ON(view && !i915_is_ggtt(vm)); - - vma = i915_gem_obj_to_vma(obj, vm, view); - if (!vma) { - vma = i915_vma_create(obj, vm, view); - GEM_BUG_ON(vma != i915_gem_obj_to_vma(obj, vm, view)); - } - - GEM_BUG_ON(i915_vma_is_closed(vma)); - return vma; + i915_ggtt_invalidate(dev_priv); } static struct scatterlist * @@ -3485,7 +3458,7 @@ intel_partial_pages(const struct i915_ggtt_view *view, { struct sg_table *st; struct scatterlist *sg, *iter; - unsigned int count = view->params.partial.size; + unsigned int count = view->partial.size; unsigned int offset; int ret = -ENOMEM; @@ -3497,9 +3470,7 @@ intel_partial_pages(const struct i915_ggtt_view *view, if (ret) goto err_sg_alloc; - iter = i915_gem_object_get_sg(obj, - view->params.partial.offset, - &offset); + iter = i915_gem_object_get_sg(obj, view->partial.offset, &offset); GEM_BUG_ON(!iter); sg = st->sgl; @@ -3551,7 +3522,8 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma) vma->pages = vma->obj->mm.pages; else if (vma->ggtt_view.type == I915_GGTT_VIEW_ROTATED) vma->pages = - intel_rotate_fb_obj_pages(&vma->ggtt_view.params.rotated, vma->obj); + intel_rotate_fb_obj_pages(&vma->ggtt_view.rotated, + vma->obj); else if (vma->ggtt_view.type == I915_GGTT_VIEW_PARTIAL) vma->pages = intel_partial_pages(&vma->ggtt_view, vma->obj); else @@ -3572,3 +3544,202 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma) return ret; } +/** + * i915_gem_gtt_reserve - reserve a node in an address_space (GTT) + * @vm: the &struct i915_address_space + * @node: the &struct drm_mm_node (typically i915_vma.mode) + * @size: how much space to allocate inside the GTT, + * must be #I915_GTT_PAGE_SIZE aligned + * @offset: where to insert inside the GTT, + * must be #I915_GTT_MIN_ALIGNMENT aligned, and the node + * (@offset + @size) must fit within the address space + * @color: color to apply to node, if this node is not from a VMA, + * color must be #I915_COLOR_UNEVICTABLE + * @flags: control search and eviction behaviour + * + * i915_gem_gtt_reserve() tries to insert the @node at the exact @offset inside + * the address space (using @size and @color). If the @node does not fit, it + * tries to evict any overlapping nodes from the GTT, including any + * neighbouring nodes if the colors do not match (to ensure guard pages between + * differing domains). See i915_gem_evict_for_node() for the gory details + * on the eviction algorithm. #PIN_NONBLOCK may used to prevent waiting on + * evicting active overlapping objects, and any overlapping node that is pinned + * or marked as unevictable will also result in failure. + * + * Returns: 0 on success, -ENOSPC if no suitable hole is found, -EINTR if + * asked to wait for eviction and interrupted. + */ +int i915_gem_gtt_reserve(struct i915_address_space *vm, + struct drm_mm_node *node, + u64 size, u64 offset, unsigned long color, + unsigned int flags) +{ + int err; + + GEM_BUG_ON(!size); + GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE)); + GEM_BUG_ON(!IS_ALIGNED(offset, I915_GTT_MIN_ALIGNMENT)); + GEM_BUG_ON(range_overflows(offset, size, vm->total)); + GEM_BUG_ON(vm == &vm->i915->mm.aliasing_ppgtt->base); + GEM_BUG_ON(drm_mm_node_allocated(node)); + + node->size = size; + node->start = offset; + node->color = color; + + err = drm_mm_reserve_node(&vm->mm, node); + if (err != -ENOSPC) + return err; + + err = i915_gem_evict_for_node(vm, node, flags); + if (err == 0) + err = drm_mm_reserve_node(&vm->mm, node); + + return err; +} + +static u64 random_offset(u64 start, u64 end, u64 len, u64 align) +{ + u64 range, addr; + + GEM_BUG_ON(range_overflows(start, len, end)); + GEM_BUG_ON(round_up(start, align) > round_down(end - len, align)); + + range = round_down(end - len, align) - round_up(start, align); + if (range) { + if (sizeof(unsigned long) == sizeof(u64)) { + addr = get_random_long(); + } else { + addr = get_random_int(); + if (range > U32_MAX) { + addr <<= 32; + addr |= get_random_int(); + } + } + div64_u64_rem(addr, range, &addr); + start += addr; + } + + return round_up(start, align); +} + +/** + * i915_gem_gtt_insert - insert a node into an address_space (GTT) + * @vm: the &struct i915_address_space + * @node: the &struct drm_mm_node (typically i915_vma.node) + * @size: how much space to allocate inside the GTT, + * must be #I915_GTT_PAGE_SIZE aligned + * @alignment: required alignment of starting offset, may be 0 but + * if specified, this must be a power-of-two and at least + * #I915_GTT_MIN_ALIGNMENT + * @color: color to apply to node + * @start: start of any range restriction inside GTT (0 for all), + * must be #I915_GTT_PAGE_SIZE aligned + * @end: end of any range restriction inside GTT (U64_MAX for all), + * must be #I915_GTT_PAGE_SIZE aligned if not U64_MAX + * @flags: control search and eviction behaviour + * + * i915_gem_gtt_insert() first searches for an available hole into which + * is can insert the node. The hole address is aligned to @alignment and + * its @size must then fit entirely within the [@start, @end] bounds. The + * nodes on either side of the hole must match @color, or else a guard page + * will be inserted between the two nodes (or the node evicted). If no + * suitable hole is found, first a victim is randomly selected and tested + * for eviction, otherwise then the LRU list of objects within the GTT + * is scanned to find the first set of replacement nodes to create the hole. + * Those old overlapping nodes are evicted from the GTT (and so must be + * rebound before any future use). Any node that is currently pinned cannot + * be evicted (see i915_vma_pin()). Similar if the node's VMA is currently + * active and #PIN_NONBLOCK is specified, that node is also skipped when + * searching for an eviction candidate. See i915_gem_evict_something() for + * the gory details on the eviction algorithm. + * + * Returns: 0 on success, -ENOSPC if no suitable hole is found, -EINTR if + * asked to wait for eviction and interrupted. + */ +int i915_gem_gtt_insert(struct i915_address_space *vm, + struct drm_mm_node *node, + u64 size, u64 alignment, unsigned long color, + u64 start, u64 end, unsigned int flags) +{ + enum drm_mm_insert_mode mode; + u64 offset; + int err; + + lockdep_assert_held(&vm->i915->drm.struct_mutex); + GEM_BUG_ON(!size); + GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE)); + GEM_BUG_ON(alignment && !is_power_of_2(alignment)); + GEM_BUG_ON(alignment && !IS_ALIGNED(alignment, I915_GTT_MIN_ALIGNMENT)); + GEM_BUG_ON(start >= end); + GEM_BUG_ON(start > 0 && !IS_ALIGNED(start, I915_GTT_PAGE_SIZE)); + GEM_BUG_ON(end < U64_MAX && !IS_ALIGNED(end, I915_GTT_PAGE_SIZE)); + GEM_BUG_ON(vm == &vm->i915->mm.aliasing_ppgtt->base); + GEM_BUG_ON(drm_mm_node_allocated(node)); + + if (unlikely(range_overflows(start, size, end))) + return -ENOSPC; + + if (unlikely(round_up(start, alignment) > round_down(end - size, alignment))) + return -ENOSPC; + + mode = DRM_MM_INSERT_BEST; + if (flags & PIN_HIGH) + mode = DRM_MM_INSERT_HIGH; + if (flags & PIN_MAPPABLE) + mode = DRM_MM_INSERT_LOW; + + /* We only allocate in PAGE_SIZE/GTT_PAGE_SIZE (4096) chunks, + * so we know that we always have a minimum alignment of 4096. + * The drm_mm range manager is optimised to return results + * with zero alignment, so where possible use the optimal + * path. + */ + BUILD_BUG_ON(I915_GTT_MIN_ALIGNMENT > I915_GTT_PAGE_SIZE); + if (alignment <= I915_GTT_MIN_ALIGNMENT) + alignment = 0; + + err = drm_mm_insert_node_in_range(&vm->mm, node, + size, alignment, color, + start, end, mode); + if (err != -ENOSPC) + return err; + + /* No free space, pick a slot at random. + * + * There is a pathological case here using a GTT shared between + * mmap and GPU (i.e. ggtt/aliasing_ppgtt but not full-ppgtt): + * + * |<-- 256 MiB aperture -->||<-- 1792 MiB unmappable -->| + * (64k objects) (448k objects) + * + * Now imagine that the eviction LRU is ordered top-down (just because + * pathology meets real life), and that we need to evict an object to + * make room inside the aperture. The eviction scan then has to walk + * the 448k list before it finds one within range. And now imagine that + * it has to search for a new hole between every byte inside the memcpy, + * for several simultaneous clients. + * + * On a full-ppgtt system, if we have run out of available space, there + * will be lots and lots of objects in the eviction list! Again, + * searching that LRU list may be slow if we are also applying any + * range restrictions (e.g. restriction to low 4GiB) and so, for + * simplicity and similarilty between different GTT, try the single + * random replacement first. + */ + offset = random_offset(start, end, + size, alignment ?: I915_GTT_MIN_ALIGNMENT); + err = i915_gem_gtt_reserve(vm, node, size, offset, color, flags); + if (err != -ENOSPC) + return err; + + /* Randomly selected placement is pinned, do a search */ + err = i915_gem_evict_something(vm, size, alignment, color, + start, end, flags); + if (err) + return err; + + return drm_mm_insert_node_in_range(&vm->mm, node, + size, alignment, color, + start, end, DRM_MM_INSERT_EVICT); +} diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 4f35be4c26c7..3c5ef5358cef 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -40,6 +40,9 @@ #include "i915_gem_timeline.h" #include "i915_gem_request.h" +#define I915_GTT_PAGE_SIZE 4096UL +#define I915_GTT_MIN_ALIGNMENT I915_GTT_PAGE_SIZE + #define I915_FENCE_REG_NONE -1 #define I915_MAX_NUM_FENCES 32 /* 32 fences + sign bit for FENCE_REG_NONE */ @@ -142,34 +145,57 @@ typedef uint64_t gen8_ppgtt_pml4e_t; struct sg_table; -enum i915_ggtt_view_type { - I915_GGTT_VIEW_NORMAL = 0, - I915_GGTT_VIEW_ROTATED, - I915_GGTT_VIEW_PARTIAL, -}; - struct intel_rotation_info { - struct { + struct intel_rotation_plane_info { /* tiles */ unsigned int width, height, stride, offset; } plane[2]; +} __packed; + +static inline void assert_intel_rotation_info_is_packed(void) +{ + BUILD_BUG_ON(sizeof(struct intel_rotation_info) != 8*sizeof(unsigned int)); +} + +struct intel_partial_info { + u64 offset; + unsigned int size; +} __packed; + +static inline void assert_intel_partial_info_is_packed(void) +{ + BUILD_BUG_ON(sizeof(struct intel_partial_info) != sizeof(u64) + sizeof(unsigned int)); +} + +enum i915_ggtt_view_type { + I915_GGTT_VIEW_NORMAL = 0, + I915_GGTT_VIEW_ROTATED = sizeof(struct intel_rotation_info), + I915_GGTT_VIEW_PARTIAL = sizeof(struct intel_partial_info), }; +static inline void assert_i915_ggtt_view_type_is_unique(void) +{ + /* As we encode the size of each branch inside the union into its type, + * we have to be careful that each branch has a unique size. + */ + switch ((enum i915_ggtt_view_type)0) { + case I915_GGTT_VIEW_NORMAL: + case I915_GGTT_VIEW_PARTIAL: + case I915_GGTT_VIEW_ROTATED: + /* gcc complains if these are identical cases */ + break; + } +} + struct i915_ggtt_view { enum i915_ggtt_view_type type; - union { - struct { - u64 offset; - unsigned int size; - } partial; + /* Members need to contain no holes/padding */ + struct intel_partial_info partial; struct intel_rotation_info rotated; - } params; + }; }; -extern const struct i915_ggtt_view i915_ggtt_view_normal; -extern const struct i915_ggtt_view i915_ggtt_view_rotated; - enum i915_cache_level; struct i915_vma; @@ -220,7 +246,7 @@ struct i915_pml4 { struct i915_address_space { struct drm_mm mm; struct i915_gem_timeline timeline; - struct drm_device *dev; + struct drm_i915_private *i915; /* Every address space belongs to a struct file - except for the global * GTT that is owned by the driver (and so @file is set to NULL). In * principle, no information should leak from one context to another @@ -315,15 +341,25 @@ struct i915_ggtt { struct i915_address_space base; struct io_mapping mappable; /* Mapping to our CPU mappable region */ - size_t stolen_size; /* Total size of stolen memory */ - size_t stolen_usable_size; /* Total size minus BIOS reserved */ - size_t stolen_reserved_base; - size_t stolen_reserved_size; - u64 mappable_end; /* End offset that we can CPU map */ phys_addr_t mappable_base; /* PA of our GMADR */ + u64 mappable_end; /* End offset that we can CPU map */ + + /* Stolen memory is segmented in hardware with different portions + * offlimits to certain functions. + * + * The drm_mm is initialised to the total accessible range, as found + * from the PCI config. On Broadwell+, this is further restricted to + * avoid the first page! The upper end of stolen memory is reserved for + * hardware functions and similarly removed from the accessible range. + */ + u32 stolen_size; /* Total size of stolen memory */ + u32 stolen_usable_size; /* Total size minus reserved ranges */ + u32 stolen_reserved_base; + u32 stolen_reserved_size; /** "Graphics Stolen Memory" holds the global PTEs */ void __iomem *gsm; + void (*invalidate)(struct drm_i915_private *dev_priv); bool do_idle_maps; @@ -492,6 +528,8 @@ i915_vm_to_ggtt(struct i915_address_space *vm) int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv); int i915_ggtt_init_hw(struct drm_i915_private *dev_priv); int i915_ggtt_enable_hw(struct drm_i915_private *dev_priv); +void i915_ggtt_enable_guc(struct drm_i915_private *i915); +void i915_ggtt_disable_guc(struct drm_i915_private *i915); int i915_gem_init_ggtt(struct drm_i915_private *dev_priv); void i915_ggtt_cleanup_hw(struct drm_i915_private *dev_priv); @@ -500,6 +538,7 @@ void i915_ppgtt_release(struct kref *kref); struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_i915_private *dev_priv, struct drm_i915_file_private *fpriv, const char *name); +void i915_ppgtt_close(struct i915_address_space *vm); static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) { if (ppgtt) @@ -520,6 +559,16 @@ int __must_check i915_gem_gtt_prepare_pages(struct drm_i915_gem_object *obj, void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj, struct sg_table *pages); +int i915_gem_gtt_reserve(struct i915_address_space *vm, + struct drm_mm_node *node, + u64 size, u64 offset, unsigned long color, + unsigned int flags); + +int i915_gem_gtt_insert(struct i915_address_space *vm, + struct drm_mm_node *node, + u64 size, u64 alignment, unsigned long color, + u64 start, u64 end, unsigned int flags); + /* Flags used by pin/bind&friends. */ #define PIN_NONBLOCK BIT(0) #define PIN_MAPPABLE BIT(1) @@ -534,6 +583,6 @@ void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj, #define PIN_HIGH BIT(9) #define PIN_OFFSET_BIAS BIT(10) #define PIN_OFFSET_FIXED BIT(11) -#define PIN_OFFSET_MASK (~4095) +#define PIN_OFFSET_MASK (-I915_GTT_PAGE_SIZE) #endif diff --git a/drivers/gpu/drm/i915/i915_gem_internal.c b/drivers/gpu/drm/i915/i915_gem_internal.c index d09c74973cb3..933019e1b206 100644 --- a/drivers/gpu/drm/i915/i915_gem_internal.c +++ b/drivers/gpu/drm/i915/i915_gem_internal.c @@ -46,24 +46,12 @@ static struct sg_table * i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj) { struct drm_i915_private *i915 = to_i915(obj->base.dev); - unsigned int npages = obj->base.size / PAGE_SIZE; struct sg_table *st; struct scatterlist *sg; + unsigned int npages; int max_order; gfp_t gfp; - st = kmalloc(sizeof(*st), GFP_KERNEL); - if (!st) - return ERR_PTR(-ENOMEM); - - if (sg_alloc_table(st, npages, GFP_KERNEL)) { - kfree(st); - return ERR_PTR(-ENOMEM); - } - - sg = st->sgl; - st->nents = 0; - max_order = MAX_ORDER; #ifdef CONFIG_SWIOTLB if (swiotlb_nr_tbl()) { @@ -79,12 +67,26 @@ i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj) #endif gfp = GFP_KERNEL | __GFP_HIGHMEM | __GFP_RECLAIMABLE; - if (IS_CRESTLINE(i915) || IS_BROADWATER(i915)) { + if (IS_I965GM(i915) || IS_I965G(i915)) { /* 965gm cannot relocate objects above 4GiB. */ gfp &= ~__GFP_HIGHMEM; gfp |= __GFP_DMA32; } +create_st: + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return ERR_PTR(-ENOMEM); + + npages = obj->base.size / PAGE_SIZE; + if (sg_alloc_table(st, npages, GFP_KERNEL)) { + kfree(st); + return ERR_PTR(-ENOMEM); + } + + sg = st->sgl; + st->nents = 0; + do { int order = min(fls(npages) - 1, max_order); struct page *page; @@ -112,8 +114,15 @@ i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj) sg = __sg_next(sg); } while (1); - if (i915_gem_gtt_prepare_pages(obj, st)) + if (i915_gem_gtt_prepare_pages(obj, st)) { + /* Failed to dma-map try again with single page sg segments */ + if (get_order(st->sgl->length)) { + internal_free_pages(st); + max_order = 0; + goto create_st; + } goto err; + } /* Mark the pages as dontneed whilst they are still pinned. As soon * as they are unpinned they are allowed to be reaped by the shrinker, @@ -159,11 +168,17 @@ static const struct drm_i915_gem_object_ops i915_gem_object_internal_ops = { */ struct drm_i915_gem_object * i915_gem_object_create_internal(struct drm_i915_private *i915, - unsigned int size) + phys_addr_t size) { struct drm_i915_gem_object *obj; - obj = i915_gem_object_alloc(&i915->drm); + GEM_BUG_ON(!size); + GEM_BUG_ON(!IS_ALIGNED(size, PAGE_SIZE)); + + if (overflows_type(size, obj->base.size)) + return ERR_PTR(-E2BIG); + + obj = i915_gem_object_alloc(i915); if (!obj) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/i915/i915_gem_object.h b/drivers/gpu/drm/i915/i915_gem_object.h index ecfefb9d42e4..bf90b07163d1 100644 --- a/drivers/gpu/drm/i915/i915_gem_object.h +++ b/drivers/gpu/drm/i915/i915_gem_object.h @@ -317,6 +317,29 @@ i915_gem_object_get_stride(struct drm_i915_gem_object *obj) return obj->tiling_and_stride & STRIDE_MASK; } +static inline unsigned int +i915_gem_tile_height(unsigned int tiling) +{ + GEM_BUG_ON(!tiling); + return tiling == I915_TILING_Y ? 32 : 8; +} + +static inline unsigned int +i915_gem_object_get_tile_height(struct drm_i915_gem_object *obj) +{ + return i915_gem_tile_height(i915_gem_object_get_tiling(obj)); +} + +static inline unsigned int +i915_gem_object_get_tile_row_size(struct drm_i915_gem_object *obj) +{ + return (i915_gem_object_get_stride(obj) * + i915_gem_object_get_tile_height(obj)); +} + +int i915_gem_object_set_tiling(struct drm_i915_gem_object *obj, + unsigned int tiling, unsigned int stride); + static inline struct intel_engine_cs * i915_gem_object_last_write_engine(struct drm_i915_gem_object *obj) { diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index 5af19b0bf713..7032c542a9b1 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -60,7 +60,7 @@ render_state_get_rodata(const struct intel_engine_cs *engine) * this is sufficient as the null state generator makes the final batch * with two passes to build command and state separately. At this point * the size of both are known and it compacts them by relocating the state - * right after the commands taking care of aligment so we should sufficient + * right after the commands taking care of alignment so we should sufficient * space below them for adding new commands. */ #define OUT_BATCH(batch, i, val) \ @@ -187,20 +187,20 @@ int i915_gem_render_state_init(struct intel_engine_cs *engine) if (!rodata) return 0; - if (rodata->batch_items * 4 > 4096) + if (rodata->batch_items * 4 > PAGE_SIZE) return -EINVAL; so = kmalloc(sizeof(*so), GFP_KERNEL); if (!so) return -ENOMEM; - obj = i915_gem_object_create_internal(engine->i915, 4096); + obj = i915_gem_object_create_internal(engine->i915, PAGE_SIZE); if (IS_ERR(obj)) { ret = PTR_ERR(obj); goto err_free; } - so->vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL); + so->vma = i915_vma_instance(obj, &engine->i915->ggtt.base, NULL); if (IS_ERR(so->vma)) { ret = PTR_ERR(so->vma); goto err_obj; diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c index b8f403faadbb..e7c3c0318ff6 100644 --- a/drivers/gpu/drm/i915/i915_gem_request.c +++ b/drivers/gpu/drm/i915/i915_gem_request.c @@ -24,6 +24,9 @@ #include <linux/prefetch.h> #include <linux/dma-fence-array.h> +#include <linux/sched.h> +#include <linux/sched/clock.h> +#include <linux/sched/signal.h> #include "i915_drv.h" @@ -62,6 +65,15 @@ static void i915_fence_release(struct dma_fence *fence) { struct drm_i915_gem_request *req = to_request(fence); + /* The request is put onto a RCU freelist (i.e. the address + * is immediately reused), mark the fences as being freed now. + * Otherwise the debugobjects for the fences are only marked as + * freed when the slab cache itself is freed, and so we would get + * caught trying to reuse dead objects. + */ + i915_sw_fence_fini(&req->submit); + i915_sw_fence_fini(&req->execute); + kmem_cache_free(req->i915->requests, req); } @@ -197,6 +209,7 @@ void i915_gem_retire_noop(struct i915_gem_active *active, static void i915_gem_request_retire(struct drm_i915_gem_request *request) { + struct intel_engine_cs *engine = request->engine; struct i915_gem_active *active, *next; lockdep_assert_held(&request->i915->drm.struct_mutex); @@ -207,9 +220,9 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request) trace_i915_gem_request_retire(request); - spin_lock_irq(&request->engine->timeline->lock); + spin_lock_irq(&engine->timeline->lock); list_del_init(&request->link); - spin_unlock_irq(&request->engine->timeline->lock); + spin_unlock_irq(&engine->timeline->lock); /* We know the GPU must have read the request to have * sent us the seqno + interrupt, so use the position @@ -257,13 +270,20 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request) i915_gem_request_remove_from_client(request); - if (request->previous_context) { - if (i915.enable_execlists) - intel_lr_context_unpin(request->previous_context, - request->engine); - } + /* Retirement decays the ban score as it is a sign of ctx progress */ + if (request->ctx->ban_score > 0) + request->ctx->ban_score--; - i915_gem_context_put(request->ctx); + /* The backing object for the context is done after switching to the + * *next* context. Therefore we cannot retire the previous context until + * the next context has already started running. However, since we + * cannot take the required locks at i915_gem_request_submit() we + * defer the unpinning of the active context to now, retirement of + * the subsequent request. + */ + if (engine->last_retired_context) + engine->context_unpin(engine, engine->last_retired_context); + engine->last_retired_context = request->ctx; dma_fence_signal(&request->fence); @@ -277,6 +297,8 @@ void i915_gem_request_retire_upto(struct drm_i915_gem_request *req) struct drm_i915_gem_request *tmp; lockdep_assert_held(&req->i915->drm.struct_mutex); + GEM_BUG_ON(!i915_gem_request_completed(req)); + if (list_empty(&req->link)) return; @@ -288,26 +310,6 @@ void i915_gem_request_retire_upto(struct drm_i915_gem_request *req) } while (tmp != req); } -static int i915_gem_check_wedge(struct drm_i915_private *dev_priv) -{ - struct i915_gpu_error *error = &dev_priv->gpu_error; - - if (i915_terminally_wedged(error)) - return -EIO; - - if (i915_reset_in_progress(error)) { - /* Non-interruptible callers can't handle -EAGAIN, hence return - * -EIO unconditionally for these. - */ - if (!dev_priv->mm.interruptible) - return -EIO; - - return -EAGAIN; - } - - return 0; -} - static int i915_gem_init_global_seqno(struct drm_i915_private *i915, u32 seqno) { struct i915_gem_timeline *timeline = &i915->gt.global_timeline; @@ -326,11 +328,11 @@ static int i915_gem_init_global_seqno(struct drm_i915_private *i915, u32 seqno) GEM_BUG_ON(i915->gt.active_requests > 1); /* If the seqno wraps around, we need to clear the breadcrumb rbtree */ - if (!i915_seqno_passed(seqno, atomic_read(&timeline->next_seqno))) { + if (!i915_seqno_passed(seqno, atomic_read(&timeline->seqno))) { while (intel_breadcrumbs_busy(i915)) cond_resched(); /* spin until threads are complete */ } - atomic_set(&timeline->next_seqno, seqno); + atomic_set(&timeline->seqno, seqno); /* Finally reset hw state */ for_each_engine(engine, i915, id) @@ -365,11 +367,11 @@ int i915_gem_set_global_seqno(struct drm_device *dev, u32 seqno) static int reserve_global_seqno(struct drm_i915_private *i915) { u32 active_requests = ++i915->gt.active_requests; - u32 next_seqno = atomic_read(&i915->gt.global_timeline.next_seqno); + u32 seqno = atomic_read(&i915->gt.global_timeline.seqno); int ret; /* Reservation is fine until we need to wrap around */ - if (likely(next_seqno + active_requests > next_seqno)) + if (likely(seqno + active_requests > seqno)) return 0; ret = i915_gem_init_global_seqno(i915, 0); @@ -383,13 +385,13 @@ static int reserve_global_seqno(struct drm_i915_private *i915) static u32 __timeline_get_seqno(struct i915_gem_timeline *tl) { - /* next_seqno only incremented under a mutex */ - return ++tl->next_seqno.counter; + /* seqno only incremented under a mutex */ + return ++tl->seqno.counter; } static u32 timeline_get_seqno(struct i915_gem_timeline *tl) { - return atomic_inc_return(&tl->next_seqno); + return atomic_inc_return(&tl->seqno); } void __i915_gem_request_submit(struct drm_i915_gem_request *request) @@ -502,16 +504,22 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, lockdep_assert_held(&dev_priv->drm.struct_mutex); /* ABI: Before userspace accesses the GPU (e.g. execbuffer), report - * EIO if the GPU is already wedged, or EAGAIN to drop the struct_mutex - * and restart. + * EIO if the GPU is already wedged. + */ + if (i915_terminally_wedged(&dev_priv->gpu_error)) + return ERR_PTR(-EIO); + + /* Pinning the contexts may generate requests in order to acquire + * GGTT space, so do this first before we reserve a seqno for + * ourselves. */ - ret = i915_gem_check_wedge(dev_priv); + ret = engine->context_pin(engine, ctx); if (ret) return ERR_PTR(ret); ret = reserve_global_seqno(dev_priv); if (ret) - return ERR_PTR(ret); + goto err_unpin; /* Move the oldest request to the slab-cache (if not in use!) */ req = list_first_entry_or_null(&engine->timeline->requests, @@ -578,11 +586,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, INIT_LIST_HEAD(&req->active_list); req->i915 = dev_priv; req->engine = engine; - req->ctx = i915_gem_context_get(ctx); + req->ctx = ctx; /* No zalloc, must clear what we need by hand */ req->global_seqno = 0; - req->previous_context = NULL; req->file_priv = NULL; req->batch = NULL; @@ -596,10 +603,7 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST; GEM_BUG_ON(req->reserved_space < engine->emit_breadcrumb_sz); - if (i915.enable_execlists) - ret = intel_logical_ring_alloc_request_extras(req); - else - ret = intel_ring_alloc_request_extras(req); + ret = engine->request_alloc(req); if (ret) goto err_ctx; @@ -613,10 +617,16 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, return req; err_ctx: - i915_gem_context_put(ctx); + /* Make sure we didn't add ourselves to external state before freeing */ + GEM_BUG_ON(!list_empty(&req->active_list)); + GEM_BUG_ON(!list_empty(&req->priotree.signalers_list)); + GEM_BUG_ON(!list_empty(&req->priotree.waiters_list)); + kmem_cache_free(dev_priv->requests, req); err_unreserve: dev_priv->gt.active_requests--; +err_unpin: + engine->context_unpin(engine, ctx); return ERR_PTR(ret); } @@ -822,6 +832,13 @@ void __i915_add_request(struct drm_i915_gem_request *request, bool flush_caches) lockdep_assert_held(&request->i915->drm.struct_mutex); trace_i915_gem_request_add(request); + /* Make sure that no request gazumped us - if it was allocated after + * our i915_gem_request_alloc() and called __i915_add_request() before + * us, the timeline will hold its seqno which is later than ours. + */ + GEM_BUG_ON(i915_seqno_passed(timeline->last_submitted_seqno, + request->fence.seqno)); + /* * To ensure that this call will not fail, space for its emissions * should already have been reserved in the ring buffer. Let the ring @@ -1011,8 +1028,13 @@ __i915_request_wait_for_execute(struct drm_i915_gem_request *request, break; } + if (!timeout) { + timeout = -ETIME; + break; + } + timeout = io_schedule_timeout(timeout); - } while (timeout); + } while (1); finish_wait(&request->execute.wait, &wait); if (flags & I915_WAIT_LOCKED) diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h index d229f47d1028..ea511f06efaf 100644 --- a/drivers/gpu/drm/i915/i915_gem_request.h +++ b/drivers/gpu/drm/i915/i915_gem_request.h @@ -170,17 +170,6 @@ struct drm_i915_gem_request { /** Preallocate space in the ring for the emitting the request */ u32 reserved_space; - /** - * Context related to the previous request. - * As the contexts are accessed by the hardware until the switch is - * completed to a new context, the hardware may still be writing - * to the context object after the breadcrumb is visible. We must - * not unpin/unbind/prune that object whilst still active and so - * we keep the previous context pinned until the following (this) - * request is retired. - */ - struct i915_gem_context *previous_context; - /** Batch buffer related to this request if any (used for * error state dump only). */ diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index abc78bbfc1dc..9673bcc3b6ad 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -54,16 +54,10 @@ int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *dev_priv, if (!drm_mm_initialized(&dev_priv->mm.stolen)) return -ENODEV; - /* See the comment at the drm_mm_init() call for more about this check. - * WaSkipStolenMemoryFirstPage:bdw+ (incomplete) - */ - if (start < 4096 && INTEL_GEN(dev_priv) >= 8) - start = 4096; - mutex_lock(&dev_priv->mm.stolen_lock); - ret = drm_mm_insert_node_in_range(&dev_priv->mm.stolen, node, size, - alignment, start, end, - DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node_in_range(&dev_priv->mm.stolen, node, + size, alignment, 0, + start, end, DRM_MM_INSERT_BEST); mutex_unlock(&dev_priv->mm.stolen_lock); return ret; @@ -73,11 +67,8 @@ int i915_gem_stolen_insert_node(struct drm_i915_private *dev_priv, struct drm_mm_node *node, u64 size, unsigned alignment) { - struct i915_ggtt *ggtt = &dev_priv->ggtt; - return i915_gem_stolen_insert_node_in_range(dev_priv, node, size, - alignment, 0, - ggtt->stolen_usable_size); + alignment, 0, U64_MAX); } void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv, @@ -152,7 +143,7 @@ static unsigned long i915_stolen_to_physical(struct drm_i915_private *dev_priv) tom = tmp * MB(32); base = tom - tseg_size - ggtt->stolen_size; - } else if (IS_845G(dev_priv)) { + } else if (IS_I845G(dev_priv)) { u32 tseg_size = 0; u32 tom; u8 tmp; @@ -202,8 +193,8 @@ static unsigned long i915_stolen_to_physical(struct drm_i915_private *dev_priv) return 0; /* make sure we don't clobber the GTT if it's within stolen memory */ - if (INTEL_GEN(dev_priv) <= 4 && !IS_G33(dev_priv) && - !IS_G4X(dev_priv)) { + if (INTEL_GEN(dev_priv) <= 4 && + !IS_G33(dev_priv) && !IS_PINEVIEW(dev_priv) && !IS_G4X(dev_priv)) { struct { u32 start, end; } stolen[2] = { @@ -290,14 +281,13 @@ void i915_gem_cleanup_stolen(struct drm_device *dev) } static void g4x_get_stolen_reserved(struct drm_i915_private *dev_priv, - unsigned long *base, unsigned long *size) + phys_addr_t *base, u32 *size) { struct i915_ggtt *ggtt = &dev_priv->ggtt; uint32_t reg_val = I915_READ(IS_GM45(dev_priv) ? CTG_STOLEN_RESERVED : ELK_STOLEN_RESERVED); - unsigned long stolen_top = dev_priv->mm.stolen_base + - ggtt->stolen_size; + phys_addr_t stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size; *base = (reg_val & G4X_STOLEN_RESERVED_ADDR2_MASK) << 16; @@ -314,7 +304,7 @@ static void g4x_get_stolen_reserved(struct drm_i915_private *dev_priv, } static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv, - unsigned long *base, unsigned long *size) + phys_addr_t *base, u32 *size) { uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); @@ -340,7 +330,7 @@ static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv, } static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv, - unsigned long *base, unsigned long *size) + phys_addr_t *base, u32 *size) { uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); @@ -359,8 +349,8 @@ static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv, } } -static void gen8_get_stolen_reserved(struct drm_i915_private *dev_priv, - unsigned long *base, unsigned long *size) +static void chv_get_stolen_reserved(struct drm_i915_private *dev_priv, + phys_addr_t *base, u32 *size) { uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); @@ -386,11 +376,11 @@ static void gen8_get_stolen_reserved(struct drm_i915_private *dev_priv, } static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv, - unsigned long *base, unsigned long *size) + phys_addr_t *base, u32 *size) { struct i915_ggtt *ggtt = &dev_priv->ggtt; uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); - unsigned long stolen_top; + phys_addr_t stolen_top; stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size; @@ -409,11 +399,17 @@ static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv, int i915_gem_init_stolen(struct drm_i915_private *dev_priv) { struct i915_ggtt *ggtt = &dev_priv->ggtt; - unsigned long reserved_total, reserved_base = 0, reserved_size; - unsigned long stolen_top; + phys_addr_t reserved_base, stolen_top; + u32 reserved_total, reserved_size; + u32 stolen_usable_start; mutex_init(&dev_priv->mm.stolen_lock); + if (intel_vgpu_active(dev_priv)) { + DRM_INFO("iGVT-g active, disabling use of stolen memory\n"); + return 0; + } + #ifdef CONFIG_INTEL_IOMMU if (intel_iommu_gfx_mapped && INTEL_GEN(dev_priv) < 8) { DRM_INFO("DMAR active, disabling use of stolen memory\n"); @@ -429,6 +425,8 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv) return 0; stolen_top = dev_priv->mm.stolen_base + ggtt->stolen_size; + reserved_base = 0; + reserved_size = 0; switch (INTEL_INFO(dev_priv)->gen) { case 2: @@ -436,8 +434,8 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv) break; case 4: if (IS_G4X(dev_priv)) - g4x_get_stolen_reserved(dev_priv, &reserved_base, - &reserved_size); + g4x_get_stolen_reserved(dev_priv, + &reserved_base, &reserved_size); break; case 5: /* Assume the gen6 maximum for the older platforms. */ @@ -445,21 +443,20 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv) reserved_base = stolen_top - reserved_size; break; case 6: - gen6_get_stolen_reserved(dev_priv, &reserved_base, - &reserved_size); + gen6_get_stolen_reserved(dev_priv, + &reserved_base, &reserved_size); break; case 7: - gen7_get_stolen_reserved(dev_priv, &reserved_base, - &reserved_size); + gen7_get_stolen_reserved(dev_priv, + &reserved_base, &reserved_size); break; default: - if (IS_BROADWELL(dev_priv) || - IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) - bdw_get_stolen_reserved(dev_priv, &reserved_base, - &reserved_size); + if (IS_LP(dev_priv)) + chv_get_stolen_reserved(dev_priv, + &reserved_base, &reserved_size); else - gen8_get_stolen_reserved(dev_priv, &reserved_base, - &reserved_size); + bdw_get_stolen_reserved(dev_priv, + &reserved_base, &reserved_size); break; } @@ -472,9 +469,10 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv) if (reserved_base < dev_priv->mm.stolen_base || reserved_base + reserved_size > stolen_top) { - DRM_DEBUG_KMS("Stolen reserved area [0x%08lx - 0x%08lx] outside stolen memory [0x%08lx - 0x%08lx]\n", - reserved_base, reserved_base + reserved_size, - dev_priv->mm.stolen_base, stolen_top); + phys_addr_t reserved_top = reserved_base + reserved_size; + DRM_DEBUG_KMS("Stolen reserved area [%pa - %pa] outside stolen memory [%pa - %pa]\n", + &reserved_base, &reserved_top, + &dev_priv->mm.stolen_base, &stolen_top); return 0; } @@ -485,24 +483,21 @@ int i915_gem_init_stolen(struct drm_i915_private *dev_priv) * memory, so just consider the start. */ reserved_total = stolen_top - reserved_base; - DRM_DEBUG_KMS("Memory reserved for graphics device: %zuK, usable: %luK\n", + DRM_DEBUG_KMS("Memory reserved for graphics device: %uK, usable: %uK\n", ggtt->stolen_size >> 10, (ggtt->stolen_size - reserved_total) >> 10); - ggtt->stolen_usable_size = ggtt->stolen_size - reserved_total; + stolen_usable_start = 0; + /* WaSkipStolenMemoryFirstPage:bdw+ */ + if (INTEL_GEN(dev_priv) >= 8) + stolen_usable_start = 4096; - /* - * Basic memrange allocator for stolen space. - * - * TODO: Notice that some platforms require us to not use the first page - * of the stolen memory but their BIOSes may still put the framebuffer - * on the first page. So we don't reserve this page for now because of - * that. Our current solution is to just prevent new nodes from being - * inserted on the first page - see the check we have at - * i915_gem_stolen_insert_node_in_range(). We may want to fix the fbcon - * problem later. - */ - drm_mm_init(&dev_priv->mm.stolen, 0, ggtt->stolen_usable_size); + ggtt->stolen_usable_size = + ggtt->stolen_size - reserved_total - stolen_usable_start; + + /* Basic memrange allocator for stolen space. */ + drm_mm_init(&dev_priv->mm.stolen, stolen_usable_start, + ggtt->stolen_usable_size); return 0; } @@ -515,7 +510,7 @@ i915_pages_create_for_stolen(struct drm_device *dev, struct sg_table *st; struct scatterlist *sg; - GEM_BUG_ON(offset > dev_priv->ggtt.stolen_size - size); + GEM_BUG_ON(range_overflows(offset, size, dev_priv->ggtt.stolen_size)); /* We hide that we have no struct page backing our stolen object * by wrapping the contiguous physical allocation with a fake @@ -578,22 +573,21 @@ static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = { }; static struct drm_i915_gem_object * -_i915_gem_object_create_stolen(struct drm_device *dev, +_i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, struct drm_mm_node *stolen) { struct drm_i915_gem_object *obj; - obj = i915_gem_object_alloc(dev); + obj = i915_gem_object_alloc(dev_priv); if (obj == NULL) return NULL; - drm_gem_private_object_init(dev, &obj->base, stolen->size); + drm_gem_private_object_init(&dev_priv->drm, &obj->base, stolen->size); i915_gem_object_init(obj, &i915_gem_object_stolen_ops); obj->stolen = stolen; obj->base.read_domains = I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT; - obj->cache_level = HAS_LLC(to_i915(dev)) ? - I915_CACHE_LLC : I915_CACHE_NONE; + obj->cache_level = HAS_LLC(dev_priv) ? I915_CACHE_LLC : I915_CACHE_NONE; if (i915_gem_object_pin_pages(obj)) goto cleanup; @@ -606,9 +600,8 @@ cleanup: } struct drm_i915_gem_object * -i915_gem_object_create_stolen(struct drm_device *dev, u32 size) +i915_gem_object_create_stolen(struct drm_i915_private *dev_priv, u32 size) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_object *obj; struct drm_mm_node *stolen; int ret; @@ -629,7 +622,7 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size) return NULL; } - obj = _i915_gem_object_create_stolen(dev, stolen); + obj = _i915_gem_object_create_stolen(dev_priv, stolen); if (obj) return obj; @@ -639,12 +632,11 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size) } struct drm_i915_gem_object * -i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, +i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv, u32 stolen_offset, u32 gtt_offset, u32 size) { - struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; struct drm_i915_gem_object *obj; struct drm_mm_node *stolen; @@ -654,14 +646,15 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, if (!drm_mm_initialized(&dev_priv->mm.stolen)) return NULL; - lockdep_assert_held(&dev->struct_mutex); + lockdep_assert_held(&dev_priv->drm.struct_mutex); DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n", stolen_offset, gtt_offset, size); /* KISS and expect everything to be page-aligned */ - if (WARN_ON(size == 0) || WARN_ON(size & 4095) || - WARN_ON(stolen_offset & 4095)) + if (WARN_ON(size == 0) || + WARN_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE)) || + WARN_ON(!IS_ALIGNED(stolen_offset, I915_GTT_MIN_ALIGNMENT))) return NULL; stolen = kzalloc(sizeof(*stolen), GFP_KERNEL); @@ -679,7 +672,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, return NULL; } - obj = _i915_gem_object_create_stolen(dev, stolen); + obj = _i915_gem_object_create_stolen(dev_priv, stolen); if (obj == NULL) { DRM_DEBUG_KMS("failed to allocate stolen object\n"); i915_gem_stolen_remove_node(dev_priv, stolen); @@ -695,7 +688,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, if (ret) goto err; - vma = i915_gem_obj_lookup_or_create_vma(obj, &ggtt->base, NULL); + vma = i915_vma_instance(obj, &ggtt->base, NULL); if (IS_ERR(vma)) { ret = PTR_ERR(vma); goto err_pages; @@ -706,15 +699,16 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, * setting up the GTT space. The actual reservation will occur * later. */ - vma->node.start = gtt_offset; - vma->node.size = size; - - ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node); + ret = i915_gem_gtt_reserve(&ggtt->base, &vma->node, + size, gtt_offset, obj->cache_level, + 0); if (ret) { DRM_DEBUG_KMS("failed to allocate stolen GTT space\n"); goto err_pages; } + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); + vma->pages = obj->mm.pages; vma->flags |= I915_VMA_GLOBAL_BIND; __i915_vma_set_map_and_fenceable(vma); diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index c85e7b06bdba..974ac08df473 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -34,8 +34,8 @@ /** * DOC: buffer object tiling * - * i915_gem_set_tiling() and i915_gem_get_tiling() is the userspace interface to - * declare fence register requirements. + * i915_gem_set_tiling_ioctl() and i915_gem_get_tiling_ioctl() is the userspace + * interface to declare fence register requirements. * * In principle GEM doesn't care at all about the internal data layout of an * object, and hence it also doesn't care about tiling or swizzling. There's two @@ -58,86 +58,147 @@ * invovlement. */ +/** + * i915_gem_fence_size - required global GTT size for a fence + * @i915: i915 device + * @size: object size + * @tiling: tiling mode + * @stride: tiling stride + * + * Return the required global GTT size for a fence (view of a tiled object), + * taking into account potential fence register mapping. + */ +u32 i915_gem_fence_size(struct drm_i915_private *i915, + u32 size, unsigned int tiling, unsigned int stride) +{ + u32 ggtt_size; + + GEM_BUG_ON(!size); + + if (tiling == I915_TILING_NONE) + return size; + + GEM_BUG_ON(!stride); + + if (INTEL_GEN(i915) >= 4) { + stride *= i915_gem_tile_height(tiling); + GEM_BUG_ON(!IS_ALIGNED(stride, I965_FENCE_PAGE)); + return roundup(size, stride); + } + + /* Previous chips need a power-of-two fence region when tiling */ + if (IS_GEN3(i915)) + ggtt_size = 1024*1024; + else + ggtt_size = 512*1024; + + while (ggtt_size < size) + ggtt_size <<= 1; + + return ggtt_size; +} + +/** + * i915_gem_fence_alignment - required global GTT alignment for a fence + * @i915: i915 device + * @size: object size + * @tiling: tiling mode + * @stride: tiling stride + * + * Return the required global GTT alignment for a fence (a view of a tiled + * object), taking into account potential fence register mapping. + */ +u32 i915_gem_fence_alignment(struct drm_i915_private *i915, u32 size, + unsigned int tiling, unsigned int stride) +{ + GEM_BUG_ON(!size); + + /* + * Minimum alignment is 4k (GTT page size), but might be greater + * if a fence register is needed for the object. + */ + if (tiling == I915_TILING_NONE) + return I915_GTT_MIN_ALIGNMENT; + + if (INTEL_GEN(i915) >= 4) + return I965_FENCE_PAGE; + + /* + * Previous chips need to be aligned to the size of the smallest + * fence register that can contain the object. + */ + return i915_gem_fence_size(i915, size, tiling, stride); +} + /* Check pitch constriants for all chips & tiling formats */ static bool -i915_tiling_ok(struct drm_i915_private *dev_priv, - int stride, int size, int tiling_mode) +i915_tiling_ok(struct drm_i915_gem_object *obj, + unsigned int tiling, unsigned int stride) { - int tile_width; + struct drm_i915_private *i915 = to_i915(obj->base.dev); + unsigned int tile_width; /* Linear is always fine */ - if (tiling_mode == I915_TILING_NONE) + if (tiling == I915_TILING_NONE) return true; - if (tiling_mode > I915_TILING_LAST) + if (tiling > I915_TILING_LAST) return false; - if (IS_GEN2(dev_priv) || - (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev_priv))) - tile_width = 128; - else - tile_width = 512; - /* check maximum stride & object size */ /* i965+ stores the end address of the gtt mapping in the fence * reg, so dont bother to check the size */ - if (INTEL_GEN(dev_priv) >= 7) { + if (INTEL_GEN(i915) >= 7) { if (stride / 128 > GEN7_FENCE_MAX_PITCH_VAL) return false; - } else if (INTEL_GEN(dev_priv) >= 4) { + } else if (INTEL_GEN(i915) >= 4) { if (stride / 128 > I965_FENCE_MAX_PITCH_VAL) return false; } else { if (stride > 8192) return false; - if (IS_GEN3(dev_priv)) { - if (size > I830_FENCE_MAX_SIZE_VAL << 20) + if (IS_GEN3(i915)) { + if (obj->base.size > I830_FENCE_MAX_SIZE_VAL << 20) return false; } else { - if (size > I830_FENCE_MAX_SIZE_VAL << 19) + if (obj->base.size > I830_FENCE_MAX_SIZE_VAL << 19) return false; } } - if (stride < tile_width) + if (IS_GEN2(i915) || + (tiling == I915_TILING_Y && HAS_128_BYTE_Y_TILING(i915))) + tile_width = 128; + else + tile_width = 512; + + if (!stride || !IS_ALIGNED(stride, tile_width)) return false; /* 965+ just needs multiples of tile width */ - if (INTEL_GEN(dev_priv) >= 4) { - if (stride & (tile_width - 1)) - return false; + if (INTEL_GEN(i915) >= 4) return true; - } /* Pre-965 needs power of two tile widths */ - if (stride & (stride - 1)) - return false; - - return true; + return is_power_of_2(stride); } -static bool i915_vma_fence_prepare(struct i915_vma *vma, int tiling_mode) +static bool i915_vma_fence_prepare(struct i915_vma *vma, + int tiling_mode, unsigned int stride) { - struct drm_i915_private *dev_priv = to_i915(vma->vm->dev); - u32 size; + struct drm_i915_private *i915 = vma->vm->i915; + u32 size, alignment; if (!i915_vma_is_map_and_fenceable(vma)) return true; - if (INTEL_GEN(dev_priv) == 3) { - if (vma->node.start & ~I915_FENCE_START_MASK) - return false; - } else { - if (vma->node.start & ~I830_FENCE_START_MASK) - return false; - } - - size = i915_gem_get_ggtt_size(dev_priv, vma->size, tiling_mode); + size = i915_gem_fence_size(i915, vma->size, tiling_mode, stride); if (vma->node.size < size) return false; - if (vma->node.start & (size - 1)) + alignment = i915_gem_fence_alignment(i915, vma->size, tiling_mode, stride); + if (!IS_ALIGNED(vma->node.start, alignment)) return false; return true; @@ -145,20 +206,20 @@ static bool i915_vma_fence_prepare(struct i915_vma *vma, int tiling_mode) /* Make the current GTT allocation valid for the change in tiling. */ static int -i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode) +i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, + int tiling_mode, unsigned int stride) { - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); struct i915_vma *vma; int ret; if (tiling_mode == I915_TILING_NONE) return 0; - if (INTEL_GEN(dev_priv) >= 4) - return 0; - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (i915_vma_fence_prepare(vma, tiling_mode)) + if (!i915_vma_is_ggtt(vma)) + break; + + if (i915_vma_fence_prepare(vma, tiling_mode, stride)) continue; ret = i915_vma_unbind(vma); @@ -169,8 +230,100 @@ i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode) return 0; } +int +i915_gem_object_set_tiling(struct drm_i915_gem_object *obj, + unsigned int tiling, unsigned int stride) +{ + struct drm_i915_private *i915 = to_i915(obj->base.dev); + struct i915_vma *vma; + int err; + + /* Make sure we don't cross-contaminate obj->tiling_and_stride */ + BUILD_BUG_ON(I915_TILING_LAST & STRIDE_MASK); + + GEM_BUG_ON(!i915_tiling_ok(obj, tiling, stride)); + GEM_BUG_ON(!stride ^ (tiling == I915_TILING_NONE)); + lockdep_assert_held(&i915->drm.struct_mutex); + + if ((tiling | stride) == obj->tiling_and_stride) + return 0; + + if (obj->framebuffer_references) + return -EBUSY; + + /* We need to rebind the object if its current allocation + * no longer meets the alignment restrictions for its new + * tiling mode. Otherwise we can just leave it alone, but + * need to ensure that any fence register is updated before + * the next fenced (either through the GTT or by the BLT unit + * on older GPUs) access. + * + * After updating the tiling parameters, we then flag whether + * we need to update an associated fence register. Note this + * has to also include the unfenced register the GPU uses + * whilst executing a fenced command for an untiled object. + */ + + err = i915_gem_object_fence_prepare(obj, tiling, stride); + if (err) + return err; + + /* If the memory has unknown (i.e. varying) swizzling, we pin the + * pages to prevent them being swapped out and causing corruption + * due to the change in swizzling. + */ + mutex_lock(&obj->mm.lock); + if (obj->mm.pages && + obj->mm.madv == I915_MADV_WILLNEED && + i915->quirks & QUIRK_PIN_SWIZZLED_PAGES) { + if (tiling == I915_TILING_NONE) { + GEM_BUG_ON(!obj->mm.quirked); + __i915_gem_object_unpin_pages(obj); + obj->mm.quirked = false; + } + if (!i915_gem_object_is_tiled(obj)) { + GEM_BUG_ON(!obj->mm.quirked); + __i915_gem_object_pin_pages(obj); + obj->mm.quirked = true; + } + } + mutex_unlock(&obj->mm.lock); + + list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (!i915_vma_is_ggtt(vma)) + break; + + vma->fence_size = + i915_gem_fence_size(i915, vma->size, tiling, stride); + vma->fence_alignment = + i915_gem_fence_alignment(i915, + vma->size, tiling, stride); + + if (vma->fence) + vma->fence->dirty = true; + } + + obj->tiling_and_stride = tiling | stride; + + /* Force the fence to be reacquired for GTT access */ + i915_gem_release_mmap(obj); + + /* Try to preallocate memory required to save swizzling on put-pages */ + if (i915_gem_object_needs_bit17_swizzle(obj)) { + if (!obj->bit_17) { + obj->bit_17 = kcalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT), + sizeof(long), GFP_KERNEL); + } + } else { + kfree(obj->bit_17); + obj->bit_17 = NULL; + } + + return 0; +} + /** - * i915_gem_set_tiling - IOCTL handler to set tiling mode + * i915_gem_set_tiling_ioctl - IOCTL handler to set tiling mode * @dev: DRM device * @data: data pointer for the ioctl * @file: DRM file for the ioctl call @@ -184,30 +337,19 @@ i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode) * Zero on success, negative errno on failure. */ int -i915_gem_set_tiling(struct drm_device *dev, void *data, - struct drm_file *file) +i915_gem_set_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) { struct drm_i915_gem_set_tiling *args = data; - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_object *obj; - int err = 0; - - /* Make sure we don't cross-contaminate obj->tiling_and_stride */ - BUILD_BUG_ON(I915_TILING_LAST & STRIDE_MASK); + int err; obj = i915_gem_object_lookup(file, args->handle); if (!obj) return -ENOENT; - if (!i915_tiling_ok(dev_priv, - args->stride, obj->base.size, args->tiling_mode)) { - i915_gem_object_put(obj); - return -EINVAL; - } - - mutex_lock(&dev->struct_mutex); - if (obj->pin_display || obj->framebuffer_references) { - err = -EBUSY; + if (!i915_tiling_ok(obj, args->tiling_mode, args->stride)) { + err = -EINVAL; goto err; } @@ -216,9 +358,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, args->stride = 0; } else { if (args->tiling_mode == I915_TILING_X) - args->swizzle_mode = dev_priv->mm.bit_6_swizzle_x; + args->swizzle_mode = to_i915(dev)->mm.bit_6_swizzle_x; else - args->swizzle_mode = dev_priv->mm.bit_6_swizzle_y; + args->swizzle_mode = to_i915(dev)->mm.bit_6_swizzle_y; /* Hide bit 17 swizzling from the user. This prevents old Mesa * from aborting the application on sw fallbacks to bit 17, @@ -240,79 +382,24 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } } - if (args->tiling_mode != i915_gem_object_get_tiling(obj) || - args->stride != i915_gem_object_get_stride(obj)) { - /* We need to rebind the object if its current allocation - * no longer meets the alignment restrictions for its new - * tiling mode. Otherwise we can just leave it alone, but - * need to ensure that any fence register is updated before - * the next fenced (either through the GTT or by the BLT unit - * on older GPUs) access. - * - * After updating the tiling parameters, we then flag whether - * we need to update an associated fence register. Note this - * has to also include the unfenced register the GPU uses - * whilst executing a fenced command for an untiled object. - */ + err = mutex_lock_interruptible(&dev->struct_mutex); + if (err) + goto err; - err = i915_gem_object_fence_prepare(obj, args->tiling_mode); - if (!err) { - struct i915_vma *vma; - - mutex_lock(&obj->mm.lock); - if (obj->mm.pages && - obj->mm.madv == I915_MADV_WILLNEED && - dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES) { - if (args->tiling_mode == I915_TILING_NONE) { - GEM_BUG_ON(!obj->mm.quirked); - __i915_gem_object_unpin_pages(obj); - obj->mm.quirked = false; - } - if (!i915_gem_object_is_tiled(obj)) { - GEM_BUG_ON(!obj->mm.quirked); - __i915_gem_object_pin_pages(obj); - obj->mm.quirked = true; - } - } - mutex_unlock(&obj->mm.lock); - - list_for_each_entry(vma, &obj->vma_list, obj_link) { - if (!vma->fence) - continue; - - vma->fence->dirty = true; - } - obj->tiling_and_stride = - args->stride | args->tiling_mode; - - /* Force the fence to be reacquired for GTT access */ - i915_gem_release_mmap(obj); - } - } - /* we have to maintain this existing ABI... */ + err = i915_gem_object_set_tiling(obj, args->tiling_mode, args->stride); + mutex_unlock(&dev->struct_mutex); + + /* We have to maintain this existing ABI... */ args->stride = i915_gem_object_get_stride(obj); args->tiling_mode = i915_gem_object_get_tiling(obj); - /* Try to preallocate memory required to save swizzling on put-pages */ - if (i915_gem_object_needs_bit17_swizzle(obj)) { - if (obj->bit_17 == NULL) { - obj->bit_17 = kcalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT), - sizeof(long), GFP_KERNEL); - } - } else { - kfree(obj->bit_17); - obj->bit_17 = NULL; - } - err: i915_gem_object_put(obj); - mutex_unlock(&dev->struct_mutex); - return err; } /** - * i915_gem_get_tiling - IOCTL handler to get tiling mode + * i915_gem_get_tiling_ioctl - IOCTL handler to get tiling mode * @dev: DRM device * @data: data pointer for the ioctl * @file: DRM file for the ioctl call @@ -325,8 +412,8 @@ err: * Zero on success, negative errno on failure. */ int -i915_gem_get_tiling(struct drm_device *dev, void *data, - struct drm_file *file) +i915_gem_get_tiling_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) { struct drm_i915_gem_get_tiling *args = data; struct drm_i915_private *dev_priv = to_i915(dev); diff --git a/drivers/gpu/drm/i915/i915_gem_timeline.c b/drivers/gpu/drm/i915/i915_gem_timeline.c index bf8a471b61e6..b596ca7ee058 100644 --- a/drivers/gpu/drm/i915/i915_gem_timeline.c +++ b/drivers/gpu/drm/i915/i915_gem_timeline.c @@ -81,10 +81,18 @@ int i915_gem_timeline_init__global(struct drm_i915_private *i915) &class, "&global_timeline->lock"); } -void i915_gem_timeline_fini(struct i915_gem_timeline *tl) +void i915_gem_timeline_fini(struct i915_gem_timeline *timeline) { - lockdep_assert_held(&tl->i915->drm.struct_mutex); + int i; - list_del(&tl->link); - kfree(tl->name); + lockdep_assert_held(&timeline->i915->drm.struct_mutex); + + for (i = 0; i < ARRAY_SIZE(timeline->engine); i++) { + struct intel_timeline *tl = &timeline->engine[i]; + + GEM_BUG_ON(!list_empty(&tl->requests)); + } + + list_del(&timeline->link); + kfree(timeline->name); } diff --git a/drivers/gpu/drm/i915/i915_gem_timeline.h b/drivers/gpu/drm/i915/i915_gem_timeline.h index 98d99a62b4ae..f2e51f42cc2f 100644 --- a/drivers/gpu/drm/i915/i915_gem_timeline.h +++ b/drivers/gpu/drm/i915/i915_gem_timeline.h @@ -56,7 +56,7 @@ struct intel_timeline { struct i915_gem_timeline { struct list_head link; - atomic_t next_seqno; + atomic_t seqno; struct drm_i915_private *i915; const char *name; diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index d068af2ec3a3..22b46398831e 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -31,6 +31,7 @@ #include <linux/mmu_notifier.h> #include <linux/mempolicy.h> #include <linux/swap.h> +#include <linux/sched/mm.h> struct i915_mm_struct { struct mm_struct *mm; @@ -334,7 +335,7 @@ i915_gem_userptr_init__mm_struct(struct drm_i915_gem_object *obj) mm->i915 = to_i915(obj->base.dev); mm->mm = current->mm; - atomic_inc(¤t->mm->mm_count); + mmgrab(current->mm); mm->mn = NULL; @@ -507,7 +508,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work) flags |= FOLL_WRITE; ret = -EFAULT; - if (atomic_inc_not_zero(&mm->mm_users)) { + if (mmget_not_zero(mm)) { down_read(&mm->mmap_sem); while (pinned < npages) { ret = get_user_pages_remote @@ -784,7 +785,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file return -ENODEV; } - obj = i915_gem_object_alloc(dev); + obj = i915_gem_object_alloc(dev_priv); if (obj == NULL) return -ENOMEM; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index ae84aa4b1467..9cd22cda17af 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -121,6 +121,7 @@ static void __i915_error_advance(struct drm_i915_error_state_buf *e, e->pos += len; } +__printf(2, 0) static void i915_error_vprintf(struct drm_i915_error_state_buf *e, const char *f, va_list args) { @@ -176,9 +177,14 @@ static void i915_error_puts(struct drm_i915_error_state_buf *e, #ifdef CONFIG_DRM_I915_COMPRESS_ERROR -static bool compress_init(struct z_stream_s *zstream) +struct compress { + struct z_stream_s zstream; + void *tmp; +}; + +static bool compress_init(struct compress *c) { - memset(zstream, 0, sizeof(*zstream)); + struct z_stream_s *zstream = memset(&c->zstream, 0, sizeof(c->zstream)); zstream->workspace = kmalloc(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL), @@ -191,14 +197,22 @@ static bool compress_init(struct z_stream_s *zstream) return false; } + c->tmp = NULL; + if (i915_has_memcpy_from_wc()) + c->tmp = (void *)__get_free_page(GFP_ATOMIC | __GFP_NOWARN); + return true; } -static int compress_page(struct z_stream_s *zstream, +static int compress_page(struct compress *c, void *src, struct drm_i915_error_object *dst) { + struct z_stream_s *zstream = &c->zstream; + zstream->next_in = src; + if (c->tmp && i915_memcpy_from_wc(c->tmp, src, PAGE_SIZE)) + zstream->next_in = c->tmp; zstream->avail_in = PAGE_SIZE; do { @@ -226,9 +240,11 @@ static int compress_page(struct z_stream_s *zstream, return 0; } -static void compress_fini(struct z_stream_s *zstream, +static void compress_fini(struct compress *c, struct drm_i915_error_object *dst) { + struct z_stream_s *zstream = &c->zstream; + if (dst) { zlib_deflate(zstream, Z_FINISH); dst->unused = zstream->avail_out; @@ -236,6 +252,9 @@ static void compress_fini(struct z_stream_s *zstream, zlib_deflateEnd(zstream); kfree(zstream->workspace); + + if (c->tmp) + free_page((unsigned long)c->tmp); } static void err_compression_marker(struct drm_i915_error_state_buf *m) @@ -245,28 +264,34 @@ static void err_compression_marker(struct drm_i915_error_state_buf *m) #else -static bool compress_init(struct z_stream_s *zstream) +struct compress { +}; + +static bool compress_init(struct compress *c) { return true; } -static int compress_page(struct z_stream_s *zstream, +static int compress_page(struct compress *c, void *src, struct drm_i915_error_object *dst) { unsigned long page; + void *ptr; page = __get_free_page(GFP_ATOMIC | __GFP_NOWARN); if (!page) return -ENOMEM; - dst->pages[dst->page_count++] = - memcpy((void *)page, src, PAGE_SIZE); + ptr = (void *)page; + if (!i915_memcpy_from_wc(ptr, src, PAGE_SIZE)) + memcpy(ptr, src, PAGE_SIZE); + dst->pages[dst->page_count++] = ptr; return 0; } -static void compress_fini(struct z_stream_s *zstream, +static void compress_fini(struct compress *c, struct drm_i915_error_object *dst) { } @@ -316,24 +341,6 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, } } -static const char *hangcheck_action_to_str(enum intel_engine_hangcheck_action a) -{ - switch (a) { - case HANGCHECK_IDLE: - return "idle"; - case HANGCHECK_WAIT: - return "wait"; - case HANGCHECK_ACTIVE: - return "active"; - case HANGCHECK_KICK: - return "kick"; - case HANGCHECK_HUNG: - return "hung"; - } - - return "unknown"; -} - static void error_print_instdone(struct drm_i915_error_state_buf *m, struct drm_i915_error_engine *ee) { @@ -370,8 +377,8 @@ static void error_print_request(struct drm_i915_error_state_buf *m, if (!erq->seqno) return; - err_printf(m, "%s pid %d, seqno %8x:%08x, emitted %dms ago, head %08x, tail %08x\n", - prefix, erq->pid, + err_printf(m, "%s pid %d, ban score %d, seqno %8x:%08x, emitted %dms ago, head %08x, tail %08x\n", + prefix, erq->pid, erq->ban_score, erq->context, erq->seqno, jiffies_to_msecs(jiffies - erq->jiffies), erq->head, erq->tail); @@ -441,9 +448,13 @@ static void error_print_engine(struct drm_i915_error_state_buf *m, err_printf(m, " waiting: %s\n", yesno(ee->waiting)); err_printf(m, " ring->head: 0x%08x\n", ee->cpu_ring_head); err_printf(m, " ring->tail: 0x%08x\n", ee->cpu_ring_tail); - err_printf(m, " hangcheck: %s [%d]\n", - hangcheck_action_to_str(ee->hangcheck_action), - ee->hangcheck_score); + err_printf(m, " hangcheck stall: %s\n", yesno(ee->hangcheck_stalled)); + err_printf(m, " hangcheck action: %s\n", + hangcheck_action_to_str(ee->hangcheck_action)); + err_printf(m, " hangcheck action timestamp: %lu, %u ms ago\n", + ee->hangcheck_timestamp, + jiffies_to_msecs(jiffies - ee->hangcheck_timestamp)); + error_print_request(m, " ELSP[0]: ", &ee->execlist[0]); error_print_request(m, " ELSP[1]: ", &ee->execlist[1]); } @@ -528,11 +539,10 @@ static void err_print_capabilities(struct drm_i915_error_state_buf *m, int i915_error_state_to_str(struct drm_i915_error_state_buf *m, const struct i915_error_state_file_priv *error_priv) { - struct drm_i915_private *dev_priv = to_i915(error_priv->dev); + struct drm_i915_private *dev_priv = error_priv->i915; struct pci_dev *pdev = dev_priv->drm.pdev; struct drm_i915_error_state *error = error_priv->error; struct drm_i915_error_object *obj; - int max_hangcheck_score; int i, j; if (!error) { @@ -549,22 +559,20 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, err_printf(m, "Uptime: %ld s %ld us\n", error->uptime.tv_sec, error->uptime.tv_usec); err_print_capabilities(m, &error->device_info); - max_hangcheck_score = 0; - for (i = 0; i < ARRAY_SIZE(error->engine); i++) { - if (error->engine[i].hangcheck_score > max_hangcheck_score) - max_hangcheck_score = error->engine[i].hangcheck_score; - } + for (i = 0; i < ARRAY_SIZE(error->engine); i++) { - if (error->engine[i].hangcheck_score == max_hangcheck_score && + if (error->engine[i].hangcheck_stalled && error->engine[i].pid != -1) { - err_printf(m, "Active process (on ring %s): %s [%d]\n", + err_printf(m, "Active process (on ring %s): %s [%d], context bans %d\n", engine_str(i), error->engine[i].comm, - error->engine[i].pid); + error->engine[i].pid, + error->engine[i].context_bans); } } err_printf(m, "Reset count: %u\n", error->reset_count); err_printf(m, "Suspend count: %u\n", error->suspend_count); + err_printf(m, "Platform: %s\n", intel_platform_name(error->device_info.platform)); err_printf(m, "PCI ID: 0x%04x\n", pdev->device); err_printf(m, "PCI Revision: 0x%02x\n", pdev->revision); err_printf(m, "PCI Subsystem: %04x:%04x\n", @@ -651,9 +659,10 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, if (obj) { err_puts(m, dev_priv->engine[i]->name); if (ee->pid != -1) - err_printf(m, " (submitted by %s [%d])", + err_printf(m, " (submitted by %s [%d], bans %d)", ee->comm, - ee->pid); + ee->pid, + ee->context_bans); err_printf(m, " --- gtt_offset = 0x%08x %08x\n", upper_32_bits(obj->gtt_offset), lower_32_bits(obj->gtt_offset)); @@ -801,7 +810,7 @@ i915_error_object_create(struct drm_i915_private *i915, struct i915_ggtt *ggtt = &i915->ggtt; const u64 slot = ggtt->error_capture.start; struct drm_i915_error_object *dst; - struct z_stream_s zstream; + struct compress compress; unsigned long num_pages; struct sgt_iter iter; dma_addr_t dma; @@ -821,7 +830,7 @@ i915_error_object_create(struct drm_i915_private *i915, dst->page_count = 0; dst->unused = 0; - if (!compress_init(&zstream)) { + if (!compress_init(&compress)) { kfree(dst); return NULL; } @@ -834,7 +843,7 @@ i915_error_object_create(struct drm_i915_private *i915, I915_CACHE_NONE, 0); s = io_mapping_map_atomic_wc(&ggtt->mappable, slot); - ret = compress_page(&zstream, (void __force *)s, dst); + ret = compress_page(&compress, (void __force *)s, dst); io_mapping_unmap_atomic(s); if (ret) @@ -849,7 +858,7 @@ unwind: dst = NULL; out: - compress_fini(&zstream, dst); + compress_fini(&compress, dst); ggtt->base.clear_range(&ggtt->base, slot, PAGE_SIZE); return dst; } @@ -941,7 +950,7 @@ static uint32_t i915_error_generate_code(struct drm_i915_private *dev_priv, * strictly a client bug. Use instdone to differentiate those some. */ for (i = 0; i < I915_NUM_ENGINES; i++) { - if (error->engine[i].hangcheck_action == HANGCHECK_HUNG) { + if (error->engine[i].hangcheck_stalled) { if (engine_id) *engine_id = i; @@ -1159,8 +1168,9 @@ static void error_record_engine_registers(struct drm_i915_error_state *error, ee->hws = I915_READ(mmio); } - ee->hangcheck_score = engine->hangcheck.score; + ee->hangcheck_timestamp = engine->hangcheck.action_timestamp; ee->hangcheck_action = engine->hangcheck.action; + ee->hangcheck_stalled = engine->hangcheck.stalled; if (USES_PPGTT(dev_priv)) { int i; @@ -1188,6 +1198,7 @@ static void record_request(struct drm_i915_gem_request *request, struct drm_i915_error_request *erq) { erq->context = request->ctx->hw_id; + erq->ban_score = request->ctx->ban_score; erq->seqno = request->global_seqno; erq->jiffies = request->emitted_jiffies; erq->head = request->head; @@ -1321,7 +1332,7 @@ static void i915_gem_record_rings(struct drm_i915_private *dev_priv, } error->simulated |= - request->ctx->flags & CONTEXT_NO_ERROR_CAPTURE; + i915_gem_context_no_error_capture(request->ctx); ee->rq_head = request->head; ee->rq_post = request->postfix; @@ -1659,9 +1670,8 @@ void i915_error_state_put(struct i915_error_state_file_priv *error_priv) kref_put(&error_priv->error->ref, i915_error_state_free); } -void i915_destroy_error_state(struct drm_device *dev) +void i915_destroy_error_state(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_error_state *error; spin_lock_irq(&dev_priv->gpu_error.lock); diff --git a/drivers/gpu/drm/i915/i915_guc_reg.h b/drivers/gpu/drm/i915/i915_guc_reg.h index a47e1e4aec03..35cf9918d09a 100644 --- a/drivers/gpu/drm/i915/i915_guc_reg.h +++ b/drivers/gpu/drm/i915/i915_guc_reg.h @@ -61,18 +61,27 @@ #define DMA_ADDRESS_SPACE_GTT (8 << 16) #define DMA_COPY_SIZE _MMIO(0xc310) #define DMA_CTRL _MMIO(0xc314) +#define HUC_UKERNEL (1<<9) #define UOS_MOVE (1<<4) #define START_DMA (1<<0) #define DMA_GUC_WOPCM_OFFSET _MMIO(0xc340) +#define HUC_LOADING_AGENT_VCR (0<<1) +#define HUC_LOADING_AGENT_GUC (1<<1) #define GUC_WOPCM_OFFSET_VALUE 0x80000 /* 512KB */ #define GUC_MAX_IDLE_COUNT _MMIO(0xC3E4) +#define HUC_STATUS2 _MMIO(0xD3B0) +#define HUC_FW_VERIFIED (1<<7) + /* Defines WOPCM space available to GuC firmware */ #define GUC_WOPCM_SIZE _MMIO(0xc050) /* GuC addresses below GUC_WOPCM_TOP don't map through the GTT */ #define GUC_WOPCM_TOP (0x80 << 12) /* 512KB */ #define BXT_GUC_WOPCM_RC6_RESERVED (0x10 << 12) /* 64KB */ +/* GuC addresses above GUC_GGTT_TOP also don't map through the GTT */ +#define GUC_GGTT_TOP 0xFEE00000 + #define GEN8_GT_PM_CONFIG _MMIO(0x138140) #define GEN9LP_GT_PM_CONFIG _MMIO(0x138140) #define GEN9_GT_PM_CONFIG _MMIO(0x13816c) @@ -100,8 +109,8 @@ GUC_ENABLE_READ_CACHE_FOR_WOPCM_DATA | \ GUC_ENABLE_MIA_CLOCK_GATING) -#define HOST2GUC_INTERRUPT _MMIO(0xc4c8) -#define HOST2GUC_TRIGGER (1<<0) +#define GUC_SEND_INTERRUPT _MMIO(0xc4c8) +#define GUC_SEND_TRIGGER (1<<0) #define GEN8_DRBREGL(x) _MMIO(0x1000 + (x) * 8) #define GEN8_DRB_VALID (1<<0) diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index 4462112725ef..8ced9e26f075 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -21,12 +21,9 @@ * IN THE SOFTWARE. * */ -#include <linux/firmware.h> #include <linux/circ_buf.h> -#include <linux/debugfs.h> -#include <linux/relay.h> #include "i915_drv.h" -#include "intel_guc.h" +#include "intel_uc.h" /** * DOC: GuC-based command submission @@ -49,7 +46,7 @@ * Firmware writes a success/fail code back to the action register after * processes the request. The kernel driver polls waiting for this update and * then proceeds. - * See host2guc_action() + * See intel_guc_send() * * Doorbells: * Doorbells are interrupts to uKernel. A doorbell is a single cache line (QW) @@ -66,141 +63,29 @@ */ /* - * Read GuC command/status register (SOFT_SCRATCH_0) - * Return true if it contains a response rather than a command - */ -static inline bool host2guc_action_response(struct drm_i915_private *dev_priv, - u32 *status) -{ - u32 val = I915_READ(SOFT_SCRATCH(0)); - *status = val; - return GUC2HOST_IS_RESPONSE(val); -} - -static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - u32 status; - int i; - int ret; - - if (WARN_ON(len < 1 || len > 15)) - return -EINVAL; - - mutex_lock(&guc->action_lock); - intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); - - dev_priv->guc.action_count += 1; - dev_priv->guc.action_cmd = data[0]; - - for (i = 0; i < len; i++) - I915_WRITE(SOFT_SCRATCH(i), data[i]); - - POSTING_READ(SOFT_SCRATCH(i - 1)); - - I915_WRITE(HOST2GUC_INTERRUPT, HOST2GUC_TRIGGER); - - /* - * Fast commands should complete in less than 10us, so sample quickly - * up to that length of time, then switch to a slower sleep-wait loop. - * No HOST2GUC command should ever take longer than 10ms. - */ - ret = wait_for_us(host2guc_action_response(dev_priv, &status), 10); - if (ret) - ret = wait_for(host2guc_action_response(dev_priv, &status), 10); - if (status != GUC2HOST_STATUS_SUCCESS) { - /* - * Either the GuC explicitly returned an error (which - * we convert to -EIO here) or no response at all was - * received within the timeout limit (-ETIMEDOUT) - */ - if (ret != -ETIMEDOUT) - ret = -EIO; - - DRM_WARN("Action 0x%X failed; ret=%d status=0x%08X response=0x%08X\n", - data[0], ret, status, I915_READ(SOFT_SCRATCH(15))); - - dev_priv->guc.action_fail += 1; - dev_priv->guc.action_err = ret; - } - dev_priv->guc.action_status = status; - - intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); - mutex_unlock(&guc->action_lock); - - return ret; -} - -/* * Tell the GuC to allocate or deallocate a specific doorbell */ -static int host2guc_allocate_doorbell(struct intel_guc *guc, - struct i915_guc_client *client) -{ - u32 data[2]; - - data[0] = HOST2GUC_ACTION_ALLOCATE_DOORBELL; - data[1] = client->ctx_index; - - return host2guc_action(guc, data, 2); -} - -static int host2guc_release_doorbell(struct intel_guc *guc, - struct i915_guc_client *client) -{ - u32 data[2]; - - data[0] = HOST2GUC_ACTION_DEALLOCATE_DOORBELL; - data[1] = client->ctx_index; - - return host2guc_action(guc, data, 2); -} - -static int host2guc_sample_forcewake(struct intel_guc *guc, - struct i915_guc_client *client) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - u32 data[2]; - - data[0] = HOST2GUC_ACTION_SAMPLE_FORCEWAKE; - /* WaRsDisableCoarsePowerGating:skl,bxt */ - if (!intel_enable_rc6() || NEEDS_WaRsDisableCoarsePowerGating(dev_priv)) - data[1] = 0; - else - /* bit 0 and 1 are for Render and Media domain separately */ - data[1] = GUC_FORCEWAKE_RENDER | GUC_FORCEWAKE_MEDIA; - - return host2guc_action(guc, data, ARRAY_SIZE(data)); -} - -static int host2guc_logbuffer_flush_complete(struct intel_guc *guc) -{ - u32 data[1]; - - data[0] = HOST2GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE; - - return host2guc_action(guc, data, 1); -} - -static int host2guc_force_logbuffer_flush(struct intel_guc *guc) +static int guc_allocate_doorbell(struct intel_guc *guc, + struct i915_guc_client *client) { - u32 data[2]; + u32 action[] = { + INTEL_GUC_ACTION_ALLOCATE_DOORBELL, + client->ctx_index + }; - data[0] = HOST2GUC_ACTION_FORCE_LOG_BUFFER_FLUSH; - data[1] = 0; - - return host2guc_action(guc, data, 2); + return intel_guc_send(guc, action, ARRAY_SIZE(action)); } -static int host2guc_logging_control(struct intel_guc *guc, u32 control_val) +static int guc_release_doorbell(struct intel_guc *guc, + struct i915_guc_client *client) { - u32 data[2]; - - data[0] = HOST2GUC_ACTION_UK_LOG_ENABLE_LOGGING; - data[1] = control_val; + u32 action[] = { + INTEL_GUC_ACTION_DEALLOCATE_DOORBELL, + client->ctx_index + }; - return host2guc_action(guc, data, 2); + return intel_guc_send(guc, action, ARRAY_SIZE(action)); } /* @@ -226,7 +111,7 @@ static int guc_update_doorbell_id(struct intel_guc *guc, test_bit(client->doorbell_id, doorbell_bitmap)) { /* Deactivate the old doorbell */ doorbell->db_status = GUC_DOORBELL_DISABLED; - (void)host2guc_release_doorbell(guc, client); + (void)guc_release_doorbell(guc, client); __clear_bit(client->doorbell_id, doorbell_bitmap); } @@ -247,16 +132,9 @@ static int guc_update_doorbell_id(struct intel_guc *guc, /* Activate the new doorbell */ __set_bit(new_id, doorbell_bitmap); - doorbell->cookie = 0; doorbell->db_status = GUC_DOORBELL_ENABLED; - return host2guc_allocate_doorbell(guc, client); -} - -static int guc_init_doorbell(struct intel_guc *guc, - struct i915_guc_client *client, - uint16_t db_id) -{ - return guc_update_doorbell_id(guc, client, db_id); + doorbell->cookie = client->doorbell_cookie; + return guc_allocate_doorbell(guc, client); } static void guc_disable_doorbell(struct intel_guc *guc, @@ -298,7 +176,7 @@ select_doorbell_register(struct intel_guc *guc, uint32_t priority) * Select, assign and relase doorbell cachelines * * These functions track which doorbell cachelines are in use. - * The data they manipulate is protected by the host2guc lock. + * The data they manipulate is protected by the intel_guc_send lock. */ static uint32_t select_doorbell_cacheline(struct intel_guc *guc) @@ -390,11 +268,11 @@ static void guc_ctx_desc_init(struct intel_guc *guc, /* The state page is after PPHWSP */ lrc->ring_lcra = - i915_ggtt_offset(ce->state) + LRC_STATE_PN * PAGE_SIZE; + guc_ggtt_offset(ce->state) + LRC_STATE_PN * PAGE_SIZE; lrc->context_id = (client->ctx_index << GUC_ELC_CTXID_OFFSET) | (guc_engine_id << GUC_ELC_ENGINE_OFFSET); - lrc->ring_begin = i915_ggtt_offset(ce->ring->vma); + lrc->ring_begin = guc_ggtt_offset(ce->ring->vma); lrc->ring_end = lrc->ring_begin + ce->ring->size - 1; lrc->ring_next_free_location = lrc->ring_begin; lrc->ring_current_tail_pointer_value = 0; @@ -410,7 +288,7 @@ static void guc_ctx_desc_init(struct intel_guc *guc, * The doorbell, process descriptor, and workqueue are all parts * of the client object, which the GuC will reference via the GGTT */ - gfx_addr = i915_ggtt_offset(client->vma); + gfx_addr = guc_ggtt_offset(client->vma); desc.db_trigger_phy = sg_dma_address(client->vma->pages->sgl) + client->doorbell_offset; desc.db_trigger_cpu = @@ -464,22 +342,23 @@ static void guc_ctx_desc_fini(struct intel_guc *guc, int i915_guc_wq_reserve(struct drm_i915_gem_request *request) { const size_t wqi_size = sizeof(struct guc_wq_item); - struct i915_guc_client *gc = request->i915->guc.execbuf_client; - struct guc_process_desc *desc = gc->vaddr + gc->proc_desc_offset; + struct i915_guc_client *client = request->i915->guc.execbuf_client; + struct guc_process_desc *desc = client->vaddr + + client->proc_desc_offset; u32 freespace; int ret; - spin_lock(&gc->wq_lock); - freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size); - freespace -= gc->wq_rsvd; + spin_lock(&client->wq_lock); + freespace = CIRC_SPACE(client->wq_tail, desc->head, client->wq_size); + freespace -= client->wq_rsvd; if (likely(freespace >= wqi_size)) { - gc->wq_rsvd += wqi_size; + client->wq_rsvd += wqi_size; ret = 0; } else { - gc->no_wq_space++; + client->no_wq_space++; ret = -EAGAIN; } - spin_unlock(&gc->wq_lock); + spin_unlock(&client->wq_lock); return ret; } @@ -487,17 +366,17 @@ int i915_guc_wq_reserve(struct drm_i915_gem_request *request) void i915_guc_wq_unreserve(struct drm_i915_gem_request *request) { const size_t wqi_size = sizeof(struct guc_wq_item); - struct i915_guc_client *gc = request->i915->guc.execbuf_client; + struct i915_guc_client *client = request->i915->guc.execbuf_client; - GEM_BUG_ON(READ_ONCE(gc->wq_rsvd) < wqi_size); + GEM_BUG_ON(READ_ONCE(client->wq_rsvd) < wqi_size); - spin_lock(&gc->wq_lock); - gc->wq_rsvd -= wqi_size; - spin_unlock(&gc->wq_lock); + spin_lock(&client->wq_lock); + client->wq_rsvd -= wqi_size; + spin_unlock(&client->wq_lock); } /* Construct a Work Item and append it to the GuC's Work Queue */ -static void guc_wq_item_append(struct i915_guc_client *gc, +static void guc_wq_item_append(struct i915_guc_client *client, struct drm_i915_gem_request *rq) { /* wqi_len is in DWords, and does not include the one-word header */ @@ -508,10 +387,10 @@ static void guc_wq_item_append(struct i915_guc_client *gc, struct guc_wq_item *wqi; u32 freespace, tail, wq_off; - desc = gc->vaddr + gc->proc_desc_offset; + desc = client->vaddr + client->proc_desc_offset; /* Free space is guaranteed, see i915_guc_wq_reserve() above */ - freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size); + freespace = CIRC_SPACE(client->wq_tail, desc->head, client->wq_size); GEM_BUG_ON(freespace < wqi_size); /* The GuC firmware wants the tail index in QWords, not bytes */ @@ -528,17 +407,17 @@ static void guc_wq_item_append(struct i915_guc_client *gc, * workqueue buffer dw by dw. */ BUILD_BUG_ON(wqi_size != 16); - GEM_BUG_ON(gc->wq_rsvd < wqi_size); + GEM_BUG_ON(client->wq_rsvd < wqi_size); /* postincrement WQ tail for next time */ - wq_off = gc->wq_tail; + wq_off = client->wq_tail; GEM_BUG_ON(wq_off & (wqi_size - 1)); - gc->wq_tail += wqi_size; - gc->wq_tail &= gc->wq_size - 1; - gc->wq_rsvd -= wqi_size; + client->wq_tail += wqi_size; + client->wq_tail &= client->wq_size - 1; + client->wq_rsvd -= wqi_size; /* WQ starts from the page after doorbell / process_desc */ - wqi = gc->vaddr + wq_off + GUC_DB_SIZE; + wqi = client->vaddr + wq_off + GUC_DB_SIZE; /* Now fill in the 4-word work queue item */ wqi->header = WQ_TYPE_INORDER | @@ -553,30 +432,30 @@ static void guc_wq_item_append(struct i915_guc_client *gc, wqi->fence_id = rq->global_seqno; } -static int guc_ring_doorbell(struct i915_guc_client *gc) +static int guc_ring_doorbell(struct i915_guc_client *client) { struct guc_process_desc *desc; union guc_doorbell_qw db_cmp, db_exc, db_ret; union guc_doorbell_qw *db; int attempt = 2, ret = -EAGAIN; - desc = gc->vaddr + gc->proc_desc_offset; + desc = client->vaddr + client->proc_desc_offset; /* Update the tail so it is visible to GuC */ - desc->tail = gc->wq_tail; + desc->tail = client->wq_tail; /* current cookie */ db_cmp.db_status = GUC_DOORBELL_ENABLED; - db_cmp.cookie = gc->cookie; + db_cmp.cookie = client->doorbell_cookie; /* cookie to be updated */ db_exc.db_status = GUC_DOORBELL_ENABLED; - db_exc.cookie = gc->cookie + 1; + db_exc.cookie = client->doorbell_cookie + 1; if (db_exc.cookie == 0) db_exc.cookie = 1; /* pointer of current doorbell cacheline */ - db = gc->vaddr + gc->doorbell_offset; + db = client->vaddr + client->doorbell_offset; while (attempt--) { /* lets ring the doorbell */ @@ -586,7 +465,7 @@ static int guc_ring_doorbell(struct i915_guc_client *gc) /* if the exchange was successfully executed */ if (db_ret.value_qw == db_cmp.value_qw) { /* db was successfully rung */ - gc->cookie = db_exc.cookie; + client->doorbell_cookie = db_exc.cookie; ret = 0; break; } @@ -609,12 +488,9 @@ static int guc_ring_doorbell(struct i915_guc_client *gc) } /** - * i915_guc_submit() - Submit commands through GuC + * __i915_guc_submit() - Submit commands through GuC * @rq: request associated with the commands * - * Return: 0 on success, otherwise an errno. - * (Note: nonzero really shouldn't happen!) - * * The caller must have already called i915_guc_wq_reserve() above with * a result of 0 (success), guaranteeing that there is space in the work * queue for the new request, so enqueuing the item cannot fail. @@ -626,7 +502,7 @@ static int guc_ring_doorbell(struct i915_guc_client *gc) * The only error here arises if the doorbell hardware isn't functioning * as expected, which really shouln't happen. */ -static void i915_guc_submit(struct drm_i915_gem_request *rq) +static void __i915_guc_submit(struct drm_i915_gem_request *rq) { struct drm_i915_private *dev_priv = rq->i915; struct intel_engine_cs *engine = rq->engine; @@ -635,17 +511,6 @@ static void i915_guc_submit(struct drm_i915_gem_request *rq) struct i915_guc_client *client = guc->execbuf_client; int b_ret; - /* We keep the previous context alive until we retire the following - * request. This ensures that any the context object is still pinned - * for any residual writes the HW makes into it on the context switch - * into the next object following the breadcrumb. Otherwise, we may - * retire the context too early. - */ - rq->previous_context = engine->last_context; - engine->last_context = rq->ctx; - - i915_gem_request_submit(rq); - spin_lock(&client->wq_lock); guc_wq_item_append(client, rq); @@ -665,6 +530,12 @@ static void i915_guc_submit(struct drm_i915_gem_request *rq) spin_unlock(&client->wq_lock); } +static void i915_guc_submit(struct drm_i915_gem_request *rq) +{ + i915_gem_request_submit(rq); + __i915_guc_submit(rq); +} + /* * Everything below here is concerned with setup & teardown, and is * therefore not part of the somewhat time-critical batch-submission @@ -672,7 +543,7 @@ static void i915_guc_submit(struct drm_i915_gem_request *rq) */ /** - * guc_allocate_vma() - Allocate a GGTT VMA for GuC usage + * intel_guc_allocate_vma() - Allocate a GGTT VMA for GuC usage * @guc: the guc * @size: size of area to allocate (both virtual space and memory) * @@ -684,18 +555,18 @@ static void i915_guc_submit(struct drm_i915_gem_request *rq) * * Return: A i915_vma if successful, otherwise an ERR_PTR. */ -static struct i915_vma *guc_allocate_vma(struct intel_guc *guc, u32 size) +struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size) { struct drm_i915_private *dev_priv = guc_to_i915(guc); struct drm_i915_gem_object *obj; struct i915_vma *vma; int ret; - obj = i915_gem_object_create(&dev_priv->drm, size); + obj = i915_gem_object_create(dev_priv, size); if (IS_ERR(obj)) return ERR_CAST(obj); - vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + vma = i915_vma_instance(obj, &dev_priv->ggtt.base, NULL); if (IS_ERR(vma)) goto err; @@ -706,9 +577,6 @@ static struct i915_vma *guc_allocate_vma(struct intel_guc *guc, u32 size) goto err; } - /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */ - I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); - return vma; err: @@ -779,8 +647,7 @@ static void guc_init_doorbell_hw(struct intel_guc *guc) uint16_t db_id; int i, err; - /* Save client's original doorbell selection */ - db_id = client->doorbell_id; + guc_disable_doorbell(guc, client); for (i = 0; i < GUC_MAX_DOORBELLS; ++i) { /* Skip if doorbell is OK */ @@ -793,7 +660,9 @@ static void guc_init_doorbell_hw(struct intel_guc *guc) i, err); } - /* Restore to original value */ + db_id = select_doorbell_register(guc, client->priority); + WARN_ON(db_id == GUC_INVALID_DOORBELL_ID); + err = guc_update_doorbell_id(guc, client, db_id); if (err) DRM_WARN("Failed to restore doorbell to %d, err %d\n", @@ -847,7 +716,7 @@ guc_client_alloc(struct drm_i915_private *dev_priv, } /* The first page is doorbell/proc_desc. Two followed pages are wq. */ - vma = guc_allocate_vma(guc, GUC_DB_SIZE + GUC_WQ_SIZE); + vma = intel_guc_allocate_vma(guc, GUC_DB_SIZE + GUC_WQ_SIZE); if (IS_ERR(vma)) goto err; @@ -883,8 +752,13 @@ guc_client_alloc(struct drm_i915_private *dev_priv, guc_proc_desc_init(guc, client); guc_ctx_desc_init(guc, client); - if (guc_init_doorbell(guc, client, db_id)) - goto err; + + /* For runtime client allocation we need to enable the doorbell. Not + * required yet for the static execbuf_client as this special kernel + * client is enabled from i915_guc_submission_enable(). + * + * guc_update_doorbell_id(guc, client, db_id); + */ DRM_DEBUG_DRIVER("new priority %u client %p for engine(s) 0x%x: ctx_index %u\n", priority, client, client->engines, client->ctx_index); @@ -898,488 +772,7 @@ err: return NULL; } -/* - * Sub buffer switch callback. Called whenever relay has to switch to a new - * sub buffer, relay stays on the same sub buffer if 0 is returned. - */ -static int subbuf_start_callback(struct rchan_buf *buf, - void *subbuf, - void *prev_subbuf, - size_t prev_padding) -{ - /* Use no-overwrite mode by default, where relay will stop accepting - * new data if there are no empty sub buffers left. - * There is no strict synchronization enforced by relay between Consumer - * and Producer. In overwrite mode, there is a possibility of getting - * inconsistent/garbled data, the producer could be writing on to the - * same sub buffer from which Consumer is reading. This can't be avoided - * unless Consumer is fast enough and can always run in tandem with - * Producer. - */ - if (relay_buf_full(buf)) - return 0; - - return 1; -} - -/* - * file_create() callback. Creates relay file in debugfs. - */ -static struct dentry *create_buf_file_callback(const char *filename, - struct dentry *parent, - umode_t mode, - struct rchan_buf *buf, - int *is_global) -{ - struct dentry *buf_file; - - /* This to enable the use of a single buffer for the relay channel and - * correspondingly have a single file exposed to User, through which - * it can collect the logs in order without any post-processing. - * Need to set 'is_global' even if parent is NULL for early logging. - */ - *is_global = 1; - - if (!parent) - return NULL; - - /* Not using the channel filename passed as an argument, since for each - * channel relay appends the corresponding CPU number to the filename - * passed in relay_open(). This should be fine as relay just needs a - * dentry of the file associated with the channel buffer and that file's - * name need not be same as the filename passed as an argument. - */ - buf_file = debugfs_create_file("guc_log", mode, - parent, buf, &relay_file_operations); - return buf_file; -} - -/* - * file_remove() default callback. Removes relay file in debugfs. - */ -static int remove_buf_file_callback(struct dentry *dentry) -{ - debugfs_remove(dentry); - return 0; -} - -/* relay channel callbacks */ -static struct rchan_callbacks relay_callbacks = { - .subbuf_start = subbuf_start_callback, - .create_buf_file = create_buf_file_callback, - .remove_buf_file = remove_buf_file_callback, -}; - -static void guc_log_remove_relay_file(struct intel_guc *guc) -{ - relay_close(guc->log.relay_chan); -} - -static int guc_log_create_relay_channel(struct intel_guc *guc) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - struct rchan *guc_log_relay_chan; - size_t n_subbufs, subbuf_size; - - /* Keep the size of sub buffers same as shared log buffer */ - subbuf_size = guc->log.vma->obj->base.size; - - /* Store up to 8 snapshots, which is large enough to buffer sufficient - * boot time logs and provides enough leeway to User, in terms of - * latency, for consuming the logs from relay. Also doesn't take - * up too much memory. - */ - n_subbufs = 8; - - guc_log_relay_chan = relay_open(NULL, NULL, subbuf_size, - n_subbufs, &relay_callbacks, dev_priv); - if (!guc_log_relay_chan) { - DRM_ERROR("Couldn't create relay chan for GuC logging\n"); - return -ENOMEM; - } - - GEM_BUG_ON(guc_log_relay_chan->subbuf_size < subbuf_size); - guc->log.relay_chan = guc_log_relay_chan; - return 0; -} - -static int guc_log_create_relay_file(struct intel_guc *guc) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - struct dentry *log_dir; - int ret; - - /* For now create the log file in /sys/kernel/debug/dri/0 dir */ - log_dir = dev_priv->drm.primary->debugfs_root; - - /* If /sys/kernel/debug/dri/0 location do not exist, then debugfs is - * not mounted and so can't create the relay file. - * The relay API seems to fit well with debugfs only, for availing relay - * there are 3 requirements which can be met for debugfs file only in a - * straightforward/clean manner :- - * i) Need the associated dentry pointer of the file, while opening the - * relay channel. - * ii) Should be able to use 'relay_file_operations' fops for the file. - * iii) Set the 'i_private' field of file's inode to the pointer of - * relay channel buffer. - */ - if (!log_dir) { - DRM_ERROR("Debugfs dir not available yet for GuC log file\n"); - return -ENODEV; - } - - ret = relay_late_setup_files(guc->log.relay_chan, "guc_log", log_dir); - if (ret) { - DRM_ERROR("Couldn't associate relay chan with file %d\n", ret); - return ret; - } - - return 0; -} - -static void guc_move_to_next_buf(struct intel_guc *guc) -{ - /* Make sure the updates made in the sub buffer are visible when - * Consumer sees the following update to offset inside the sub buffer. - */ - smp_wmb(); - - /* All data has been written, so now move the offset of sub buffer. */ - relay_reserve(guc->log.relay_chan, guc->log.vma->obj->base.size); - - /* Switch to the next sub buffer */ - relay_flush(guc->log.relay_chan); -} - -static void *guc_get_write_buffer(struct intel_guc *guc) -{ - if (!guc->log.relay_chan) - return NULL; - - /* Just get the base address of a new sub buffer and copy data into it - * ourselves. NULL will be returned in no-overwrite mode, if all sub - * buffers are full. Could have used the relay_write() to indirectly - * copy the data, but that would have been bit convoluted, as we need to - * write to only certain locations inside a sub buffer which cannot be - * done without using relay_reserve() along with relay_write(). So its - * better to use relay_reserve() alone. - */ - return relay_reserve(guc->log.relay_chan, 0); -} - -static bool -guc_check_log_buf_overflow(struct intel_guc *guc, - enum guc_log_buffer_type type, unsigned int full_cnt) -{ - unsigned int prev_full_cnt = guc->log.prev_overflow_count[type]; - bool overflow = false; - - if (full_cnt != prev_full_cnt) { - overflow = true; - - guc->log.prev_overflow_count[type] = full_cnt; - guc->log.total_overflow_count[type] += full_cnt - prev_full_cnt; - - if (full_cnt < prev_full_cnt) { - /* buffer_full_cnt is a 4 bit counter */ - guc->log.total_overflow_count[type] += 16; - } - DRM_ERROR_RATELIMITED("GuC log buffer overflow\n"); - } - - return overflow; -} - -static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type) -{ - switch (type) { - case GUC_ISR_LOG_BUFFER: - return (GUC_LOG_ISR_PAGES + 1) * PAGE_SIZE; - case GUC_DPC_LOG_BUFFER: - return (GUC_LOG_DPC_PAGES + 1) * PAGE_SIZE; - case GUC_CRASH_DUMP_LOG_BUFFER: - return (GUC_LOG_CRASH_PAGES + 1) * PAGE_SIZE; - default: - MISSING_CASE(type); - } - - return 0; -} - -static void guc_read_update_log_buffer(struct intel_guc *guc) -{ - unsigned int buffer_size, read_offset, write_offset, bytes_to_copy, full_cnt; - struct guc_log_buffer_state *log_buf_state, *log_buf_snapshot_state; - struct guc_log_buffer_state log_buf_state_local; - enum guc_log_buffer_type type; - void *src_data, *dst_data; - bool new_overflow; - - if (WARN_ON(!guc->log.buf_addr)) - return; - - /* Get the pointer to shared GuC log buffer */ - log_buf_state = src_data = guc->log.buf_addr; - - /* Get the pointer to local buffer to store the logs */ - log_buf_snapshot_state = dst_data = guc_get_write_buffer(guc); - /* Actual logs are present from the 2nd page */ - src_data += PAGE_SIZE; - dst_data += PAGE_SIZE; - - for (type = GUC_ISR_LOG_BUFFER; type < GUC_MAX_LOG_BUFFER; type++) { - /* Make a copy of the state structure, inside GuC log buffer - * (which is uncached mapped), on the stack to avoid reading - * from it multiple times. - */ - memcpy(&log_buf_state_local, log_buf_state, - sizeof(struct guc_log_buffer_state)); - buffer_size = guc_get_log_buffer_size(type); - read_offset = log_buf_state_local.read_ptr; - write_offset = log_buf_state_local.sampled_write_ptr; - full_cnt = log_buf_state_local.buffer_full_cnt; - - /* Bookkeeping stuff */ - guc->log.flush_count[type] += log_buf_state_local.flush_to_file; - new_overflow = guc_check_log_buf_overflow(guc, type, full_cnt); - - /* Update the state of shared log buffer */ - log_buf_state->read_ptr = write_offset; - log_buf_state->flush_to_file = 0; - log_buf_state++; - - if (unlikely(!log_buf_snapshot_state)) - continue; - - /* First copy the state structure in snapshot buffer */ - memcpy(log_buf_snapshot_state, &log_buf_state_local, - sizeof(struct guc_log_buffer_state)); - - /* The write pointer could have been updated by GuC firmware, - * after sending the flush interrupt to Host, for consistency - * set write pointer value to same value of sampled_write_ptr - * in the snapshot buffer. - */ - log_buf_snapshot_state->write_ptr = write_offset; - log_buf_snapshot_state++; - - /* Now copy the actual logs. */ - if (unlikely(new_overflow)) { - /* copy the whole buffer in case of overflow */ - read_offset = 0; - write_offset = buffer_size; - } else if (unlikely((read_offset > buffer_size) || - (write_offset > buffer_size))) { - DRM_ERROR("invalid log buffer state\n"); - /* copy whole buffer as offsets are unreliable */ - read_offset = 0; - write_offset = buffer_size; - } - - /* Just copy the newly written data */ - if (read_offset > write_offset) { - i915_memcpy_from_wc(dst_data, src_data, write_offset); - bytes_to_copy = buffer_size - read_offset; - } else { - bytes_to_copy = write_offset - read_offset; - } - i915_memcpy_from_wc(dst_data + read_offset, - src_data + read_offset, bytes_to_copy); - - src_data += buffer_size; - dst_data += buffer_size; - } - - if (log_buf_snapshot_state) - guc_move_to_next_buf(guc); - else { - /* Used rate limited to avoid deluge of messages, logs might be - * getting consumed by User at a slow rate. - */ - DRM_ERROR_RATELIMITED("no sub-buffer to capture logs\n"); - guc->log.capture_miss_count++; - } -} - -static void guc_capture_logs_work(struct work_struct *work) -{ - struct drm_i915_private *dev_priv = - container_of(work, struct drm_i915_private, guc.log.flush_work); - - i915_guc_capture_logs(dev_priv); -} - -static void guc_log_cleanup(struct intel_guc *guc) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - - lockdep_assert_held(&dev_priv->drm.struct_mutex); - - /* First disable the flush interrupt */ - gen9_disable_guc_interrupts(dev_priv); - - if (guc->log.flush_wq) - destroy_workqueue(guc->log.flush_wq); - - guc->log.flush_wq = NULL; - - if (guc->log.relay_chan) - guc_log_remove_relay_file(guc); - - guc->log.relay_chan = NULL; - - if (guc->log.buf_addr) - i915_gem_object_unpin_map(guc->log.vma->obj); - - guc->log.buf_addr = NULL; -} - -static int guc_log_create_extras(struct intel_guc *guc) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - void *vaddr; - int ret; - - lockdep_assert_held(&dev_priv->drm.struct_mutex); - - /* Nothing to do */ - if (i915.guc_log_level < 0) - return 0; - - if (!guc->log.buf_addr) { - /* Create a WC (Uncached for read) vmalloc mapping of log - * buffer pages, so that we can directly get the data - * (up-to-date) from memory. - */ - vaddr = i915_gem_object_pin_map(guc->log.vma->obj, I915_MAP_WC); - if (IS_ERR(vaddr)) { - ret = PTR_ERR(vaddr); - DRM_ERROR("Couldn't map log buffer pages %d\n", ret); - return ret; - } - - guc->log.buf_addr = vaddr; - } - - if (!guc->log.relay_chan) { - /* Create a relay channel, so that we have buffers for storing - * the GuC firmware logs, the channel will be linked with a file - * later on when debugfs is registered. - */ - ret = guc_log_create_relay_channel(guc); - if (ret) - return ret; - } - - if (!guc->log.flush_wq) { - INIT_WORK(&guc->log.flush_work, guc_capture_logs_work); - - /* - * GuC log buffer flush work item has to do register access to - * send the ack to GuC and this work item, if not synced before - * suspend, can potentially get executed after the GFX device is - * suspended. - * By marking the WQ as freezable, we don't have to bother about - * flushing of this work item from the suspend hooks, the pending - * work item if any will be either executed before the suspend - * or scheduled later on resume. This way the handling of work - * item can be kept same between system suspend & rpm suspend. - */ - guc->log.flush_wq = alloc_ordered_workqueue("i915-guc_log", - WQ_HIGHPRI | WQ_FREEZABLE); - if (guc->log.flush_wq == NULL) { - DRM_ERROR("Couldn't allocate the wq for GuC logging\n"); - return -ENOMEM; - } - } - - return 0; -} - -static void guc_log_create(struct intel_guc *guc) -{ - struct i915_vma *vma; - unsigned long offset; - uint32_t size, flags; - - if (i915.guc_log_level > GUC_LOG_VERBOSITY_MAX) - i915.guc_log_level = GUC_LOG_VERBOSITY_MAX; - - /* The first page is to save log buffer state. Allocate one - * extra page for others in case for overlap */ - size = (1 + GUC_LOG_DPC_PAGES + 1 + - GUC_LOG_ISR_PAGES + 1 + - GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT; - - vma = guc->log.vma; - if (!vma) { - /* We require SSE 4.1 for fast reads from the GuC log buffer and - * it should be present on the chipsets supporting GuC based - * submisssions. - */ - if (WARN_ON(!i915_memcpy_from_wc(NULL, NULL, 0))) { - /* logging will not be enabled */ - i915.guc_log_level = -1; - return; - } - - vma = guc_allocate_vma(guc, size); - if (IS_ERR(vma)) { - /* logging will be off */ - i915.guc_log_level = -1; - return; - } - - guc->log.vma = vma; - - if (guc_log_create_extras(guc)) { - guc_log_cleanup(guc); - i915_vma_unpin_and_release(&guc->log.vma); - i915.guc_log_level = -1; - return; - } - } - - /* each allocated unit is a page */ - flags = GUC_LOG_VALID | GUC_LOG_NOTIFY_ON_HALF_FULL | - (GUC_LOG_DPC_PAGES << GUC_LOG_DPC_SHIFT) | - (GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) | - (GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT); - - offset = i915_ggtt_offset(vma) >> PAGE_SHIFT; /* in pages */ - guc->log.flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags; -} - -static int guc_log_late_setup(struct intel_guc *guc) -{ - struct drm_i915_private *dev_priv = guc_to_i915(guc); - int ret; - - lockdep_assert_held(&dev_priv->drm.struct_mutex); - - if (i915.guc_log_level < 0) - return -EINVAL; - - /* If log_level was set as -1 at boot time, then setup needed to - * handle log buffer flush interrupts would not have been done yet, - * so do that now. - */ - ret = guc_log_create_extras(guc); - if (ret) - goto err; - - ret = guc_log_create_relay_file(guc); - if (ret) - goto err; - - return 0; -err: - guc_log_cleanup(guc); - /* logging will remain off */ - i915.guc_log_level = -1; - return ret; -} static void guc_policies_init(struct guc_policies *policies) { @@ -1422,7 +815,7 @@ static void guc_addon_create(struct intel_guc *guc) vma = guc->ads_vma; if (!vma) { - vma = guc_allocate_vma(guc, PAGE_ALIGN(size)); + vma = intel_guc_allocate_vma(guc, PAGE_ALIGN(size)); if (IS_ERR(vma)) return; @@ -1450,7 +843,7 @@ static void guc_addon_create(struct intel_guc *guc) guc_policies_init(policies); ads->scheduler_policies = - i915_ggtt_offset(vma) + sizeof(struct guc_ads); + guc_ggtt_offset(vma) + sizeof(struct guc_ads); /* MMIO reg state */ reg_state = (void *)policies + sizeof(struct guc_policies); @@ -1484,6 +877,9 @@ int i915_guc_submission_init(struct drm_i915_private *dev_priv) struct intel_guc *guc = &dev_priv->guc; struct i915_vma *vma; + if (!HAS_GUC_SCHED(dev_priv)) + return 0; + /* Wipe bitmap & delete client in case of reinitialisation */ bitmap_clear(guc->doorbell_bitmap, 0, GUC_MAX_DOORBELLS); i915_guc_submission_disable(dev_priv); @@ -1494,52 +890,68 @@ int i915_guc_submission_init(struct drm_i915_private *dev_priv) if (guc->ctx_pool_vma) return 0; /* already allocated */ - vma = guc_allocate_vma(guc, gemsize); + vma = intel_guc_allocate_vma(guc, gemsize); if (IS_ERR(vma)) return PTR_ERR(vma); guc->ctx_pool_vma = vma; ida_init(&guc->ctx_ids); - mutex_init(&guc->action_lock); - guc_log_create(guc); + intel_guc_log_create(guc); guc_addon_create(guc); + guc->execbuf_client = guc_client_alloc(dev_priv, + INTEL_INFO(dev_priv)->ring_mask, + GUC_CTX_PRIORITY_KMD_NORMAL, + dev_priv->kernel_context); + if (!guc->execbuf_client) { + DRM_ERROR("Failed to create GuC client for execbuf!\n"); + goto err; + } + return 0; + +err: + i915_guc_submission_fini(dev_priv); + return -ENOMEM; +} + +static void guc_reset_wq(struct i915_guc_client *client) +{ + struct guc_process_desc *desc = client->vaddr + + client->proc_desc_offset; + + desc->head = 0; + desc->tail = 0; + + client->wq_tail = 0; } int i915_guc_submission_enable(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; - struct drm_i915_gem_request *request; - struct i915_guc_client *client; + struct i915_guc_client *client = guc->execbuf_client; struct intel_engine_cs *engine; enum intel_engine_id id; - /* client for execbuf submission */ - client = guc_client_alloc(dev_priv, - INTEL_INFO(dev_priv)->ring_mask, - GUC_CTX_PRIORITY_KMD_NORMAL, - dev_priv->kernel_context); - if (!client) { - DRM_ERROR("Failed to create normal GuC client!\n"); - return -ENOMEM; - } + if (!client) + return -ENODEV; + + intel_guc_sample_forcewake(guc); - guc->execbuf_client = client; - host2guc_sample_forcewake(guc, client); + guc_reset_wq(client); guc_init_doorbell_hw(guc); /* Take over from manual control of ELSP (execlists) */ for_each_engine(engine, dev_priv, id) { + struct drm_i915_gem_request *rq; + engine->submit_request = i915_guc_submit; engine->schedule = NULL; /* Replay the current set of previously submitted requests */ - list_for_each_entry(request, - &engine->timeline->requests, link) { + list_for_each_entry(rq, &engine->timeline->requests, link) { client->wq_rsvd += sizeof(struct guc_wq_item); - if (i915_sw_fence_done(&request->submit)) - i915_guc_submit(request); + __i915_guc_submit(rq); } } @@ -1555,14 +967,18 @@ void i915_guc_submission_disable(struct drm_i915_private *dev_priv) /* Revert back to manual ELSP submission */ intel_execlists_enable_submission(dev_priv); - - guc_client_free(dev_priv, guc->execbuf_client); - guc->execbuf_client = NULL; } void i915_guc_submission_fini(struct drm_i915_private *dev_priv) { struct intel_guc *guc = &dev_priv->guc; + struct i915_guc_client *client; + + client = fetch_and_zero(&guc->execbuf_client); + if (!client) + return; + + guc_client_free(dev_priv, client); i915_vma_unpin_and_release(&guc->ads_vma); i915_vma_unpin_and_release(&guc->log.vma); @@ -1574,44 +990,42 @@ void i915_guc_submission_fini(struct drm_i915_private *dev_priv) /** * intel_guc_suspend() - notify GuC entering suspend state - * @dev: drm device + * @dev_priv: i915 device private */ -int intel_guc_suspend(struct drm_device *dev) +int intel_guc_suspend(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_guc *guc = &dev_priv->guc; struct i915_gem_context *ctx; u32 data[3]; - if (guc->guc_fw.guc_fw_load_status != GUC_FIRMWARE_SUCCESS) + if (guc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS) return 0; gen9_disable_guc_interrupts(dev_priv); ctx = dev_priv->kernel_context; - data[0] = HOST2GUC_ACTION_ENTER_S_STATE; + data[0] = INTEL_GUC_ACTION_ENTER_S_STATE; /* any value greater than GUC_POWER_D0 */ data[1] = GUC_POWER_D1; /* first page is shared data with GuC */ - data[2] = i915_ggtt_offset(ctx->engine[RCS].state); + data[2] = guc_ggtt_offset(ctx->engine[RCS].state); - return host2guc_action(guc, data, ARRAY_SIZE(data)); + return intel_guc_send(guc, data, ARRAY_SIZE(data)); } /** * intel_guc_resume() - notify GuC resuming from suspend state - * @dev: drm device + * @dev_priv: i915 device private */ -int intel_guc_resume(struct drm_device *dev) +int intel_guc_resume(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_guc *guc = &dev_priv->guc; struct i915_gem_context *ctx; u32 data[3]; - if (guc->guc_fw.guc_fw_load_status != GUC_FIRMWARE_SUCCESS) + if (guc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS) return 0; if (i915.guc_log_level >= 0) @@ -1619,111 +1033,12 @@ int intel_guc_resume(struct drm_device *dev) ctx = dev_priv->kernel_context; - data[0] = HOST2GUC_ACTION_EXIT_S_STATE; + data[0] = INTEL_GUC_ACTION_EXIT_S_STATE; data[1] = GUC_POWER_D0; /* first page is shared data with GuC */ - data[2] = i915_ggtt_offset(ctx->engine[RCS].state); + data[2] = guc_ggtt_offset(ctx->engine[RCS].state); - return host2guc_action(guc, data, ARRAY_SIZE(data)); + return intel_guc_send(guc, data, ARRAY_SIZE(data)); } -void i915_guc_capture_logs(struct drm_i915_private *dev_priv) -{ - guc_read_update_log_buffer(&dev_priv->guc); - /* Generally device is expected to be active only at this - * time, so get/put should be really quick. - */ - intel_runtime_pm_get(dev_priv); - host2guc_logbuffer_flush_complete(&dev_priv->guc); - intel_runtime_pm_put(dev_priv); -} - -void i915_guc_flush_logs(struct drm_i915_private *dev_priv) -{ - if (!i915.enable_guc_submission || (i915.guc_log_level < 0)) - return; - - /* First disable the interrupts, will be renabled afterwards */ - gen9_disable_guc_interrupts(dev_priv); - - /* Before initiating the forceful flush, wait for any pending/ongoing - * flush to complete otherwise forceful flush may not actually happen. - */ - flush_work(&dev_priv->guc.log.flush_work); - - /* Ask GuC to update the log buffer state */ - host2guc_force_logbuffer_flush(&dev_priv->guc); - - /* GuC would have updated log buffer by now, so capture it */ - i915_guc_capture_logs(dev_priv); -} - -void i915_guc_unregister(struct drm_i915_private *dev_priv) -{ - if (!i915.enable_guc_submission) - return; - - mutex_lock(&dev_priv->drm.struct_mutex); - guc_log_cleanup(&dev_priv->guc); - mutex_unlock(&dev_priv->drm.struct_mutex); -} - -void i915_guc_register(struct drm_i915_private *dev_priv) -{ - if (!i915.enable_guc_submission) - return; - - mutex_lock(&dev_priv->drm.struct_mutex); - guc_log_late_setup(&dev_priv->guc); - mutex_unlock(&dev_priv->drm.struct_mutex); -} - -int i915_guc_log_control(struct drm_i915_private *dev_priv, u64 control_val) -{ - union guc_log_control log_param; - int ret; - - log_param.value = control_val; - - if (log_param.verbosity < GUC_LOG_VERBOSITY_MIN || - log_param.verbosity > GUC_LOG_VERBOSITY_MAX) - return -EINVAL; - - /* This combination doesn't make sense & won't have any effect */ - if (!log_param.logging_enabled && (i915.guc_log_level < 0)) - return 0; - - ret = host2guc_logging_control(&dev_priv->guc, log_param.value); - if (ret < 0) { - DRM_DEBUG_DRIVER("host2guc action failed %d\n", ret); - return ret; - } - - i915.guc_log_level = log_param.verbosity; - - /* If log_level was set as -1 at boot time, then the relay channel file - * wouldn't have been created by now and interrupts also would not have - * been enabled. - */ - if (!dev_priv->guc.log.relay_chan) { - ret = guc_log_late_setup(&dev_priv->guc); - if (!ret) - gen9_enable_guc_interrupts(dev_priv); - } else if (!log_param.logging_enabled) { - /* Once logging is disabled, GuC won't generate logs & send an - * interrupt. But there could be some data in the log buffer - * which is yet to be captured. So request GuC to update the log - * buffer state and then collect the left over logs. - */ - i915_guc_flush_logs(dev_priv); - - /* As logging is disabled, update log level to reflect that */ - i915.guc_log_level = -1; - } else { - /* In case interrupts were disabled, enable them now */ - gen9_enable_guc_interrupts(dev_priv); - } - - return ret; -} diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 07ca71cabb2b..e6ffef2f707a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1170,6 +1170,9 @@ static void gen6_pm_rps_work(struct work_struct *work) adj *= 2; else /* CHV needs even encode values */ adj = IS_CHERRYVIEW(dev_priv) ? 2 : 1; + + if (new_delay >= dev_priv->rps.max_freq_softlimit) + adj = 0; /* * For better performance, jump directly * to RPe if we're below it. @@ -1191,6 +1194,9 @@ static void gen6_pm_rps_work(struct work_struct *work) adj *= 2; else /* CHV needs even encode values */ adj = IS_CHERRYVIEW(dev_priv) ? -2 : -1; + + if (new_delay <= dev_priv->rps.min_freq_softlimit) + adj = 0; } else { /* unknown event */ adj = 0; } @@ -1553,41 +1559,68 @@ static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv, { struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; struct intel_pipe_crc_entry *entry; + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + struct drm_driver *driver = dev_priv->drm.driver; + uint32_t crcs[5]; int head, tail; spin_lock(&pipe_crc->lock); + if (pipe_crc->source) { + if (!pipe_crc->entries) { + spin_unlock(&pipe_crc->lock); + DRM_DEBUG_KMS("spurious interrupt\n"); + return; + } - if (!pipe_crc->entries) { - spin_unlock(&pipe_crc->lock); - DRM_DEBUG_KMS("spurious interrupt\n"); - return; - } - - head = pipe_crc->head; - tail = pipe_crc->tail; + head = pipe_crc->head; + tail = pipe_crc->tail; - if (CIRC_SPACE(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) { - spin_unlock(&pipe_crc->lock); - DRM_ERROR("CRC buffer overflowing\n"); - return; - } + if (CIRC_SPACE(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) { + spin_unlock(&pipe_crc->lock); + DRM_ERROR("CRC buffer overflowing\n"); + return; + } - entry = &pipe_crc->entries[head]; + entry = &pipe_crc->entries[head]; - entry->frame = dev_priv->drm.driver->get_vblank_counter(&dev_priv->drm, - pipe); - entry->crc[0] = crc0; - entry->crc[1] = crc1; - entry->crc[2] = crc2; - entry->crc[3] = crc3; - entry->crc[4] = crc4; + entry->frame = driver->get_vblank_counter(&dev_priv->drm, pipe); + entry->crc[0] = crc0; + entry->crc[1] = crc1; + entry->crc[2] = crc2; + entry->crc[3] = crc3; + entry->crc[4] = crc4; - head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); - pipe_crc->head = head; + head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); + pipe_crc->head = head; - spin_unlock(&pipe_crc->lock); + spin_unlock(&pipe_crc->lock); - wake_up_interruptible(&pipe_crc->wq); + wake_up_interruptible(&pipe_crc->wq); + } else { + /* + * For some not yet identified reason, the first CRC is + * bonkers. So let's just wait for the next vblank and read + * out the buggy result. + * + * On CHV sometimes the second CRC is bonkers as well, so + * don't trust that one either. + */ + if (pipe_crc->skipped == 0 || + (IS_CHERRYVIEW(dev_priv) && pipe_crc->skipped == 1)) { + pipe_crc->skipped++; + spin_unlock(&pipe_crc->lock); + return; + } + spin_unlock(&pipe_crc->lock); + crcs[0] = crc0; + crcs[1] = crc1; + crcs[2] = crc2; + crcs[3] = crc3; + crcs[4] = crc4; + drm_crtc_add_crc_entry(&crtc->base, true, + drm_accurate_vblank_count(&crtc->base), + crcs); + } } #else static inline void @@ -1683,8 +1716,8 @@ static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 gt_iir) u32 msg, flush; msg = I915_READ(SOFT_SCRATCH(15)); - flush = msg & (GUC2HOST_MSG_CRASH_DUMP_POSTED | - GUC2HOST_MSG_FLUSH_LOG_BUFFER); + flush = msg & (INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED | + INTEL_GUC_RECV_MSG_FLUSH_LOG_BUFFER); if (flush) { /* Clear the message bits that are handled */ I915_WRITE(SOFT_SCRATCH(15), msg & ~flush); @@ -1893,6 +1926,10 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) * signalled in iir */ valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); + if (iir & (I915_LPE_PIPE_A_INTERRUPT | + I915_LPE_PIPE_B_INTERRUPT)) + intel_lpe_audio_irq_handler(dev_priv); + /* * VLV_IIR is single buffered, and reflects the level * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. @@ -1973,6 +2010,11 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) * signalled in iir */ valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); + if (iir & (I915_LPE_PIPE_A_INTERRUPT | + I915_LPE_PIPE_B_INTERRUPT | + I915_LPE_PIPE_C_INTERRUPT)) + intel_lpe_audio_irq_handler(dev_priv); + /* * VLV_IIR is single buffered, and reflects the level * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. @@ -2435,7 +2477,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) found = true; } - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { tmp_mask = iir & BXT_DE_PORT_HOTPLUG_MASK; if (tmp_mask) { bxt_hpd_irq_handler(dev_priv, tmp_mask, @@ -2451,7 +2493,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) } } - if (IS_BROXTON(dev_priv) && (iir & BXT_DE_PORT_GMBUS)) { + if (IS_GEN9_LP(dev_priv) && (iir & BXT_DE_PORT_GMBUS)) { gmbus_irq_handler(dev_priv); found = true; } @@ -2703,12 +2745,13 @@ static void i915_clear_error_registers(struct drm_i915_private *dev_priv) * i915_handle_error - handle a gpu error * @dev_priv: i915 device private * @engine_mask: mask representing engines that are hung + * @fmt: Error message format string + * * Do some basic checking of register state at error time and * dump it to the syslog. Also call i915_capture_error_state() to make * sure we get a record and make it available in debugfs. Fire a uevent * so userspace knows something bad happened (should trigger collection * of a ring dump etc.). - * @fmt: Error message format string */ void i915_handle_error(struct drm_i915_private *dev_priv, u32 engine_mask, @@ -2914,6 +2957,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) u32 pipestat_mask; u32 enable_mask; enum pipe pipe; + u32 val; pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV | PIPE_CRC_DONE_INTERRUPT_STATUS; @@ -2930,6 +2974,12 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) WARN_ON(dev_priv->irq_mask != ~0); + val = (I915_LPE_PIPE_A_INTERRUPT | + I915_LPE_PIPE_B_INTERRUPT | + I915_LPE_PIPE_C_INTERRUPT); + + enable_mask |= val; + dev_priv->irq_mask = ~enable_mask; GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask); @@ -3089,19 +3139,16 @@ static void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv) I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } -static void spt_hpd_irq_setup(struct drm_i915_private *dev_priv) +static void spt_hpd_detection_setup(struct drm_i915_private *dev_priv) { - u32 hotplug_irqs, hotplug, enabled_irqs; - - hotplug_irqs = SDE_HOTPLUG_MASK_SPT; - enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_spt); - - ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); + u32 hotplug; /* Enable digital hotplug on the PCH */ hotplug = I915_READ(PCH_PORT_HOTPLUG); - hotplug |= PORTD_HOTPLUG_ENABLE | PORTC_HOTPLUG_ENABLE | - PORTB_HOTPLUG_ENABLE | PORTA_HOTPLUG_ENABLE; + hotplug |= PORTA_HOTPLUG_ENABLE | + PORTB_HOTPLUG_ENABLE | + PORTC_HOTPLUG_ENABLE | + PORTD_HOTPLUG_ENABLE; I915_WRITE(PCH_PORT_HOTPLUG, hotplug); hotplug = I915_READ(PCH_PORT_HOTPLUG2); @@ -3109,6 +3156,18 @@ static void spt_hpd_irq_setup(struct drm_i915_private *dev_priv) I915_WRITE(PCH_PORT_HOTPLUG2, hotplug); } +static void spt_hpd_irq_setup(struct drm_i915_private *dev_priv) +{ + u32 hotplug_irqs, enabled_irqs; + + hotplug_irqs = SDE_HOTPLUG_MASK_SPT; + enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_spt); + + ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); + + spt_hpd_detection_setup(dev_priv); +} + static void ilk_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_irqs, hotplug, enabled_irqs; @@ -3143,18 +3202,15 @@ static void ilk_hpd_irq_setup(struct drm_i915_private *dev_priv) ibx_hpd_irq_setup(dev_priv); } -static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv) +static void __bxt_hpd_detection_setup(struct drm_i915_private *dev_priv, + u32 enabled_irqs) { - u32 hotplug_irqs, hotplug, enabled_irqs; - - enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_bxt); - hotplug_irqs = BXT_DE_PORT_HOTPLUG_MASK; - - bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); + u32 hotplug; hotplug = I915_READ(PCH_PORT_HOTPLUG); - hotplug |= PORTC_HOTPLUG_ENABLE | PORTB_HOTPLUG_ENABLE | - PORTA_HOTPLUG_ENABLE; + hotplug |= PORTA_HOTPLUG_ENABLE | + PORTB_HOTPLUG_ENABLE | + PORTC_HOTPLUG_ENABLE; DRM_DEBUG_KMS("Invert bit setting: hp_ctl:%x hp_port:%x\n", hotplug, enabled_irqs); @@ -3164,7 +3220,6 @@ static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv) * For BXT invert bit has to be set based on AOB design * for HPD detection logic, update it based on VBT fields. */ - if ((enabled_irqs & BXT_DE_PORT_HP_DDIA) && intel_bios_is_port_hpd_inverted(dev_priv, PORT_A)) hotplug |= BXT_DDIA_HPD_INVERT; @@ -3178,6 +3233,23 @@ static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv) I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } +static void bxt_hpd_detection_setup(struct drm_i915_private *dev_priv) +{ + __bxt_hpd_detection_setup(dev_priv, BXT_DE_PORT_HOTPLUG_MASK); +} + +static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv) +{ + u32 hotplug_irqs, enabled_irqs; + + enabled_irqs = intel_hpd_enabled_irqs(dev_priv, hpd_bxt); + hotplug_irqs = BXT_DE_PORT_HOTPLUG_MASK; + + bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); + + __bxt_hpd_detection_setup(dev_priv, enabled_irqs); +} + static void ibx_irq_postinstall(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -3193,6 +3265,12 @@ static void ibx_irq_postinstall(struct drm_device *dev) gen5_assert_iir_is_zero(dev_priv, SDEIIR); I915_WRITE(SDEIMR, ~mask); + + if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) || + HAS_PCH_LPT(dev_priv)) + ; /* TODO: Enable HPD detection on older PCH platforms too */ + else + spt_hpd_detection_setup(dev_priv); } static void gen5_gt_irq_postinstall(struct drm_device *dev) @@ -3375,7 +3453,7 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) GEN9_DE_PIPE_IRQ_FAULT_ERRORS; de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | GEN9_AUX_CHANNEL_D; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) de_port_masked |= BXT_DE_PORT_GMBUS; } else { de_pipe_masked |= GEN8_PIPE_PRIMARY_FLIP_DONE | @@ -3386,7 +3464,7 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) GEN8_PIPE_FIFO_UNDERRUN; de_port_enables = de_port_masked; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) de_port_enables |= BXT_DE_PORT_HOTPLUG_MASK; else if (IS_BROADWELL(dev_priv)) de_port_enables |= GEN8_PORT_DP_A_HOTPLUG; @@ -3404,6 +3482,9 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) GEN5_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables); GEN5_IRQ_INIT(GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked); + + if (IS_GEN9_LP(dev_priv)) + bxt_hpd_detection_setup(dev_priv); } static int gen8_irq_postinstall(struct drm_device *dev) @@ -4211,7 +4292,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev->driver->irq_uninstall = gen8_irq_uninstall; dev->driver->enable_vblank = gen8_enable_vblank; dev->driver->disable_vblank = gen8_disable_vblank; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup; else if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv)) dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup; diff --git a/drivers/gpu/drm/i915/i915_oa_hsw.c b/drivers/gpu/drm/i915/i915_oa_hsw.c new file mode 100644 index 000000000000..4ddf756add31 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_oa_hsw.c @@ -0,0 +1,752 @@ +/* + * Autogenerated file, DO NOT EDIT manually! + * + * Copyright (c) 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include <linux/sysfs.h> + +#include "i915_drv.h" +#include "i915_oa_hsw.h" + +enum metric_set_id { + METRIC_SET_ID_RENDER_BASIC = 1, + METRIC_SET_ID_COMPUTE_BASIC, + METRIC_SET_ID_COMPUTE_EXTENDED, + METRIC_SET_ID_MEMORY_READS, + METRIC_SET_ID_MEMORY_WRITES, + METRIC_SET_ID_SAMPLER_BALANCE, +}; + +int i915_oa_n_builtin_metric_sets_hsw = 6; + +static const struct i915_oa_reg b_counter_config_render_basic[] = { + { _MMIO(0x2724), 0x00800000 }, + { _MMIO(0x2720), 0x00000000 }, + { _MMIO(0x2714), 0x00800000 }, + { _MMIO(0x2710), 0x00000000 }, +}; + +static const struct i915_oa_reg mux_config_render_basic[] = { + { _MMIO(0x253a4), 0x01600000 }, + { _MMIO(0x25440), 0x00100000 }, + { _MMIO(0x25128), 0x00000000 }, + { _MMIO(0x2691c), 0x00000800 }, + { _MMIO(0x26aa0), 0x01500000 }, + { _MMIO(0x26b9c), 0x00006000 }, + { _MMIO(0x2791c), 0x00000800 }, + { _MMIO(0x27aa0), 0x01500000 }, + { _MMIO(0x27b9c), 0x00006000 }, + { _MMIO(0x2641c), 0x00000400 }, + { _MMIO(0x25380), 0x00000010 }, + { _MMIO(0x2538c), 0x00000000 }, + { _MMIO(0x25384), 0x0800aaaa }, + { _MMIO(0x25400), 0x00000004 }, + { _MMIO(0x2540c), 0x06029000 }, + { _MMIO(0x25410), 0x00000002 }, + { _MMIO(0x25404), 0x5c30ffff }, + { _MMIO(0x25100), 0x00000016 }, + { _MMIO(0x25110), 0x00000400 }, + { _MMIO(0x25104), 0x00000000 }, + { _MMIO(0x26804), 0x00001211 }, + { _MMIO(0x26884), 0x00000100 }, + { _MMIO(0x26900), 0x00000002 }, + { _MMIO(0x26908), 0x00700000 }, + { _MMIO(0x26904), 0x00000000 }, + { _MMIO(0x26984), 0x00001022 }, + { _MMIO(0x26a04), 0x00000011 }, + { _MMIO(0x26a80), 0x00000006 }, + { _MMIO(0x26a88), 0x00000c02 }, + { _MMIO(0x26a84), 0x00000000 }, + { _MMIO(0x26b04), 0x00001000 }, + { _MMIO(0x26b80), 0x00000002 }, + { _MMIO(0x26b8c), 0x00000007 }, + { _MMIO(0x26b84), 0x00000000 }, + { _MMIO(0x27804), 0x00004844 }, + { _MMIO(0x27884), 0x00000400 }, + { _MMIO(0x27900), 0x00000002 }, + { _MMIO(0x27908), 0x0e000000 }, + { _MMIO(0x27904), 0x00000000 }, + { _MMIO(0x27984), 0x00004088 }, + { _MMIO(0x27a04), 0x00000044 }, + { _MMIO(0x27a80), 0x00000006 }, + { _MMIO(0x27a88), 0x00018040 }, + { _MMIO(0x27a84), 0x00000000 }, + { _MMIO(0x27b04), 0x00004000 }, + { _MMIO(0x27b80), 0x00000002 }, + { _MMIO(0x27b8c), 0x000000e0 }, + { _MMIO(0x27b84), 0x00000000 }, + { _MMIO(0x26104), 0x00002222 }, + { _MMIO(0x26184), 0x0c006666 }, + { _MMIO(0x26284), 0x04000000 }, + { _MMIO(0x26304), 0x04000000 }, + { _MMIO(0x26400), 0x00000002 }, + { _MMIO(0x26410), 0x000000a0 }, + { _MMIO(0x26404), 0x00000000 }, + { _MMIO(0x25420), 0x04108020 }, + { _MMIO(0x25424), 0x1284a420 }, + { _MMIO(0x2541c), 0x00000000 }, + { _MMIO(0x25428), 0x00042049 }, +}; + +static const struct i915_oa_reg * +get_render_basic_mux_config(struct drm_i915_private *dev_priv, + int *len) +{ + *len = ARRAY_SIZE(mux_config_render_basic); + return mux_config_render_basic; +} + +static const struct i915_oa_reg b_counter_config_compute_basic[] = { + { _MMIO(0x2710), 0x00000000 }, + { _MMIO(0x2714), 0x00800000 }, + { _MMIO(0x2718), 0xaaaaaaaa }, + { _MMIO(0x271c), 0xaaaaaaaa }, + { _MMIO(0x2720), 0x00000000 }, + { _MMIO(0x2724), 0x00800000 }, + { _MMIO(0x2728), 0xaaaaaaaa }, + { _MMIO(0x272c), 0xaaaaaaaa }, + { _MMIO(0x2740), 0x00000000 }, + { _MMIO(0x2744), 0x00000000 }, + { _MMIO(0x2748), 0x00000000 }, + { _MMIO(0x274c), 0x00000000 }, + { _MMIO(0x2750), 0x00000000 }, + { _MMIO(0x2754), 0x00000000 }, + { _MMIO(0x2758), 0x00000000 }, + { _MMIO(0x275c), 0x00000000 }, + { _MMIO(0x236c), 0x00000000 }, +}; + +static const struct i915_oa_reg mux_config_compute_basic[] = { + { _MMIO(0x253a4), 0x00000000 }, + { _MMIO(0x2681c), 0x01f00800 }, + { _MMIO(0x26820), 0x00001000 }, + { _MMIO(0x2781c), 0x01f00800 }, + { _MMIO(0x26520), 0x00000007 }, + { _MMIO(0x265a0), 0x00000007 }, + { _MMIO(0x25380), 0x00000010 }, + { _MMIO(0x2538c), 0x00300000 }, + { _MMIO(0x25384), 0xaa8aaaaa }, + { _MMIO(0x25404), 0xffffffff }, + { _MMIO(0x26800), 0x00004202 }, + { _MMIO(0x26808), 0x00605817 }, + { _MMIO(0x2680c), 0x10001005 }, + { _MMIO(0x26804), 0x00000000 }, + { _MMIO(0x27800), 0x00000102 }, + { _MMIO(0x27808), 0x0c0701e0 }, + { _MMIO(0x2780c), 0x000200a0 }, + { _MMIO(0x27804), 0x00000000 }, + { _MMIO(0x26484), 0x44000000 }, + { _MMIO(0x26704), 0x44000000 }, + { _MMIO(0x26500), 0x00000006 }, + { _MMIO(0x26510), 0x00000001 }, + { _MMIO(0x26504), 0x88000000 }, + { _MMIO(0x26580), 0x00000006 }, + { _MMIO(0x26590), 0x00000020 }, + { _MMIO(0x26584), 0x00000000 }, + { _MMIO(0x26104), 0x55822222 }, + { _MMIO(0x26184), 0xaa866666 }, + { _MMIO(0x25420), 0x08320c83 }, + { _MMIO(0x25424), 0x06820c83 }, + { _MMIO(0x2541c), 0x00000000 }, + { _MMIO(0x25428), 0x00000c03 }, +}; + +static const struct i915_oa_reg * +get_compute_basic_mux_config(struct drm_i915_private *dev_priv, + int *len) +{ + *len = ARRAY_SIZE(mux_config_compute_basic); + return mux_config_compute_basic; +} + +static const struct i915_oa_reg b_counter_config_compute_extended[] = { + { _MMIO(0x2724), 0xf0800000 }, + { _MMIO(0x2720), 0x00000000 }, + { _MMIO(0x2714), 0xf0800000 }, + { _MMIO(0x2710), 0x00000000 }, + { _MMIO(0x2770), 0x0007fe2a }, + { _MMIO(0x2774), 0x0000ff00 }, + { _MMIO(0x2778), 0x0007fe6a }, + { _MMIO(0x277c), 0x0000ff00 }, + { _MMIO(0x2780), 0x0007fe92 }, + { _MMIO(0x2784), 0x0000ff00 }, + { _MMIO(0x2788), 0x0007fea2 }, + { _MMIO(0x278c), 0x0000ff00 }, + { _MMIO(0x2790), 0x0007fe32 }, + { _MMIO(0x2794), 0x0000ff00 }, + { _MMIO(0x2798), 0x0007fe9a }, + { _MMIO(0x279c), 0x0000ff00 }, + { _MMIO(0x27a0), 0x0007ff23 }, + { _MMIO(0x27a4), 0x0000ff00 }, + { _MMIO(0x27a8), 0x0007fff3 }, + { _MMIO(0x27ac), 0x0000fffe }, +}; + +static const struct i915_oa_reg mux_config_compute_extended[] = { + { _MMIO(0x2681c), 0x3eb00800 }, + { _MMIO(0x26820), 0x00900000 }, + { _MMIO(0x25384), 0x02aaaaaa }, + { _MMIO(0x25404), 0x03ffffff }, + { _MMIO(0x26800), 0x00142284 }, + { _MMIO(0x26808), 0x0e629062 }, + { _MMIO(0x2680c), 0x3f6f55cb }, + { _MMIO(0x26810), 0x00000014 }, + { _MMIO(0x26804), 0x00000000 }, + { _MMIO(0x26104), 0x02aaaaaa }, + { _MMIO(0x26184), 0x02aaaaaa }, + { _MMIO(0x25420), 0x00000000 }, + { _MMIO(0x25424), 0x00000000 }, + { _MMIO(0x2541c), 0x00000000 }, + { _MMIO(0x25428), 0x00000000 }, +}; + +static const struct i915_oa_reg * +get_compute_extended_mux_config(struct drm_i915_private *dev_priv, + int *len) +{ + *len = ARRAY_SIZE(mux_config_compute_extended); + return mux_config_compute_extended; +} + +static const struct i915_oa_reg b_counter_config_memory_reads[] = { + { _MMIO(0x2724), 0xf0800000 }, + { _MMIO(0x2720), 0x00000000 }, + { _MMIO(0x2714), 0xf0800000 }, + { _MMIO(0x2710), 0x00000000 }, + { _MMIO(0x274c), 0x76543298 }, + { _MMIO(0x2748), 0x98989898 }, + { _MMIO(0x2744), 0x000000e4 }, + { _MMIO(0x2740), 0x00000000 }, + { _MMIO(0x275c), 0x98a98a98 }, + { _MMIO(0x2758), 0x88888888 }, + { _MMIO(0x2754), 0x000c5500 }, + { _MMIO(0x2750), 0x00000000 }, + { _MMIO(0x2770), 0x0007f81a }, + { _MMIO(0x2774), 0x0000fc00 }, + { _MMIO(0x2778), 0x0007f82a }, + { _MMIO(0x277c), 0x0000fc00 }, + { _MMIO(0x2780), 0x0007f872 }, + { _MMIO(0x2784), 0x0000fc00 }, + { _MMIO(0x2788), 0x0007f8ba }, + { _MMIO(0x278c), 0x0000fc00 }, + { _MMIO(0x2790), 0x0007f87a }, + { _MMIO(0x2794), 0x0000fc00 }, + { _MMIO(0x2798), 0x0007f8ea }, + { _MMIO(0x279c), 0x0000fc00 }, + { _MMIO(0x27a0), 0x0007f8e2 }, + { _MMIO(0x27a4), 0x0000fc00 }, + { _MMIO(0x27a8), 0x0007f8f2 }, + { _MMIO(0x27ac), 0x0000fc00 }, +}; + +static const struct i915_oa_reg mux_config_memory_reads[] = { + { _MMIO(0x253a4), 0x34300000 }, + { _MMIO(0x25440), 0x2d800000 }, + { _MMIO(0x25444), 0x00000008 }, + { _MMIO(0x25128), 0x0e600000 }, + { _MMIO(0x25380), 0x00000450 }, + { _MMIO(0x25390), 0x00052c43 }, + { _MMIO(0x25384), 0x00000000 }, + { _MMIO(0x25400), 0x00006144 }, + { _MMIO(0x25408), 0x0a418820 }, + { _MMIO(0x2540c), 0x000820e6 }, + { _MMIO(0x25404), 0xff500000 }, + { _MMIO(0x25100), 0x000005d6 }, + { _MMIO(0x2510c), 0x0ef00000 }, + { _MMIO(0x25104), 0x00000000 }, + { _MMIO(0x25420), 0x02108421 }, + { _MMIO(0x25424), 0x00008421 }, + { _MMIO(0x2541c), 0x00000000 }, + { _MMIO(0x25428), 0x00000000 }, +}; + +static const struct i915_oa_reg * +get_memory_reads_mux_config(struct drm_i915_private *dev_priv, + int *len) +{ + *len = ARRAY_SIZE(mux_config_memory_reads); + return mux_config_memory_reads; +} + +static const struct i915_oa_reg b_counter_config_memory_writes[] = { + { _MMIO(0x2724), 0xf0800000 }, + { _MMIO(0x2720), 0x00000000 }, + { _MMIO(0x2714), 0xf0800000 }, + { _MMIO(0x2710), 0x00000000 }, + { _MMIO(0x274c), 0x76543298 }, + { _MMIO(0x2748), 0x98989898 }, + { _MMIO(0x2744), 0x000000e4 }, + { _MMIO(0x2740), 0x00000000 }, + { _MMIO(0x275c), 0xbabababa }, + { _MMIO(0x2758), 0x88888888 }, + { _MMIO(0x2754), 0x000c5500 }, + { _MMIO(0x2750), 0x00000000 }, + { _MMIO(0x2770), 0x0007f81a }, + { _MMIO(0x2774), 0x0000fc00 }, + { _MMIO(0x2778), 0x0007f82a }, + { _MMIO(0x277c), 0x0000fc00 }, + { _MMIO(0x2780), 0x0007f822 }, + { _MMIO(0x2784), 0x0000fc00 }, + { _MMIO(0x2788), 0x0007f8ba }, + { _MMIO(0x278c), 0x0000fc00 }, + { _MMIO(0x2790), 0x0007f87a }, + { _MMIO(0x2794), 0x0000fc00 }, + { _MMIO(0x2798), 0x0007f8ea }, + { _MMIO(0x279c), 0x0000fc00 }, + { _MMIO(0x27a0), 0x0007f8e2 }, + { _MMIO(0x27a4), 0x0000fc00 }, + { _MMIO(0x27a8), 0x0007f8f2 }, + { _MMIO(0x27ac), 0x0000fc00 }, +}; + +static const struct i915_oa_reg mux_config_memory_writes[] = { + { _MMIO(0x253a4), 0x34300000 }, + { _MMIO(0x25440), 0x01500000 }, + { _MMIO(0x25444), 0x00000120 }, + { _MMIO(0x25128), 0x0c200000 }, + { _MMIO(0x25380), 0x00000450 }, + { _MMIO(0x25390), 0x00052c43 }, + { _MMIO(0x25384), 0x00000000 }, + { _MMIO(0x25400), 0x00007184 }, + { _MMIO(0x25408), 0x0a418820 }, + { _MMIO(0x2540c), 0x000820e6 }, + { _MMIO(0x25404), 0xff500000 }, + { _MMIO(0x25100), 0x000005d6 }, + { _MMIO(0x2510c), 0x1e700000 }, + { _MMIO(0x25104), 0x00000000 }, + { _MMIO(0x25420), 0x02108421 }, + { _MMIO(0x25424), 0x00008421 }, + { _MMIO(0x2541c), 0x00000000 }, + { _MMIO(0x25428), 0x00000000 }, +}; + +static const struct i915_oa_reg * +get_memory_writes_mux_config(struct drm_i915_private *dev_priv, + int *len) +{ + *len = ARRAY_SIZE(mux_config_memory_writes); + return mux_config_memory_writes; +} + +static const struct i915_oa_reg b_counter_config_sampler_balance[] = { + { _MMIO(0x2740), 0x00000000 }, + { _MMIO(0x2744), 0x00800000 }, + { _MMIO(0x2710), 0x00000000 }, + { _MMIO(0x2714), 0x00800000 }, + { _MMIO(0x2720), 0x00000000 }, + { _MMIO(0x2724), 0x00800000 }, +}; + +static const struct i915_oa_reg mux_config_sampler_balance[] = { + { _MMIO(0x2eb9c), 0x01906400 }, + { _MMIO(0x2fb9c), 0x01906400 }, + { _MMIO(0x253a4), 0x00000000 }, + { _MMIO(0x26b9c), 0x01906400 }, + { _MMIO(0x27b9c), 0x01906400 }, + { _MMIO(0x27104), 0x00a00000 }, + { _MMIO(0x27184), 0x00a50000 }, + { _MMIO(0x2e804), 0x00500000 }, + { _MMIO(0x2e984), 0x00500000 }, + { _MMIO(0x2eb04), 0x00500000 }, + { _MMIO(0x2eb80), 0x00000084 }, + { _MMIO(0x2eb8c), 0x14200000 }, + { _MMIO(0x2eb84), 0x00000000 }, + { _MMIO(0x2f804), 0x00050000 }, + { _MMIO(0x2f984), 0x00050000 }, + { _MMIO(0x2fb04), 0x00050000 }, + { _MMIO(0x2fb80), 0x00000084 }, + { _MMIO(0x2fb8c), 0x00050800 }, + { _MMIO(0x2fb84), 0x00000000 }, + { _MMIO(0x25380), 0x00000010 }, + { _MMIO(0x2538c), 0x000000c0 }, + { _MMIO(0x25384), 0xaa550000 }, + { _MMIO(0x25404), 0xffffc000 }, + { _MMIO(0x26804), 0x50000000 }, + { _MMIO(0x26984), 0x50000000 }, + { _MMIO(0x26b04), 0x50000000 }, + { _MMIO(0x26b80), 0x00000084 }, + { _MMIO(0x26b90), 0x00050800 }, + { _MMIO(0x26b84), 0x00000000 }, + { _MMIO(0x27804), 0x05000000 }, + { _MMIO(0x27984), 0x05000000 }, + { _MMIO(0x27b04), 0x05000000 }, + { _MMIO(0x27b80), 0x00000084 }, + { _MMIO(0x27b90), 0x00000142 }, + { _MMIO(0x27b84), 0x00000000 }, + { _MMIO(0x26104), 0xa0000000 }, + { _MMIO(0x26184), 0xa5000000 }, + { _MMIO(0x25424), 0x00008620 }, + { _MMIO(0x2541c), 0x00000000 }, + { _MMIO(0x25428), 0x0004a54a }, +}; + +static const struct i915_oa_reg * +get_sampler_balance_mux_config(struct drm_i915_private *dev_priv, + int *len) +{ + *len = ARRAY_SIZE(mux_config_sampler_balance); + return mux_config_sampler_balance; +} + +int i915_oa_select_metric_set_hsw(struct drm_i915_private *dev_priv) +{ + dev_priv->perf.oa.mux_regs = NULL; + dev_priv->perf.oa.mux_regs_len = 0; + dev_priv->perf.oa.b_counter_regs = NULL; + dev_priv->perf.oa.b_counter_regs_len = 0; + + switch (dev_priv->perf.oa.metrics_set) { + case METRIC_SET_ID_RENDER_BASIC: + dev_priv->perf.oa.mux_regs = + get_render_basic_mux_config(dev_priv, + &dev_priv->perf.oa.mux_regs_len); + if (!dev_priv->perf.oa.mux_regs) { + DRM_DEBUG_DRIVER("No suitable MUX config for \"RENDER_BASIC\" metric set"); + + /* EINVAL because *_register_sysfs already checked this + * and so it wouldn't have been advertised so userspace and + * so shouldn't have been requested + */ + return -EINVAL; + } + + dev_priv->perf.oa.b_counter_regs = + b_counter_config_render_basic; + dev_priv->perf.oa.b_counter_regs_len = + ARRAY_SIZE(b_counter_config_render_basic); + + return 0; + case METRIC_SET_ID_COMPUTE_BASIC: + dev_priv->perf.oa.mux_regs = + get_compute_basic_mux_config(dev_priv, + &dev_priv->perf.oa.mux_regs_len); + if (!dev_priv->perf.oa.mux_regs) { + DRM_DEBUG_DRIVER("No suitable MUX config for \"COMPUTE_BASIC\" metric set"); + + /* EINVAL because *_register_sysfs already checked this + * and so it wouldn't have been advertised so userspace and + * so shouldn't have been requested + */ + return -EINVAL; + } + + dev_priv->perf.oa.b_counter_regs = + b_counter_config_compute_basic; + dev_priv->perf.oa.b_counter_regs_len = + ARRAY_SIZE(b_counter_config_compute_basic); + + return 0; + case METRIC_SET_ID_COMPUTE_EXTENDED: + dev_priv->perf.oa.mux_regs = + get_compute_extended_mux_config(dev_priv, + &dev_priv->perf.oa.mux_regs_len); + if (!dev_priv->perf.oa.mux_regs) { + DRM_DEBUG_DRIVER("No suitable MUX config for \"COMPUTE_EXTENDED\" metric set"); + + /* EINVAL because *_register_sysfs already checked this + * and so it wouldn't have been advertised so userspace and + * so shouldn't have been requested + */ + return -EINVAL; + } + + dev_priv->perf.oa.b_counter_regs = + b_counter_config_compute_extended; + dev_priv->perf.oa.b_counter_regs_len = + ARRAY_SIZE(b_counter_config_compute_extended); + + return 0; + case METRIC_SET_ID_MEMORY_READS: + dev_priv->perf.oa.mux_regs = + get_memory_reads_mux_config(dev_priv, + &dev_priv->perf.oa.mux_regs_len); + if (!dev_priv->perf.oa.mux_regs) { + DRM_DEBUG_DRIVER("No suitable MUX config for \"MEMORY_READS\" metric set"); + + /* EINVAL because *_register_sysfs already checked this + * and so it wouldn't have been advertised so userspace and + * so shouldn't have been requested + */ + return -EINVAL; + } + + dev_priv->perf.oa.b_counter_regs = + b_counter_config_memory_reads; + dev_priv->perf.oa.b_counter_regs_len = + ARRAY_SIZE(b_counter_config_memory_reads); + + return 0; + case METRIC_SET_ID_MEMORY_WRITES: + dev_priv->perf.oa.mux_regs = + get_memory_writes_mux_config(dev_priv, + &dev_priv->perf.oa.mux_regs_len); + if (!dev_priv->perf.oa.mux_regs) { + DRM_DEBUG_DRIVER("No suitable MUX config for \"MEMORY_WRITES\" metric set"); + + /* EINVAL because *_register_sysfs already checked this + * and so it wouldn't have been advertised so userspace and + * so shouldn't have been requested + */ + return -EINVAL; + } + + dev_priv->perf.oa.b_counter_regs = + b_counter_config_memory_writes; + dev_priv->perf.oa.b_counter_regs_len = + ARRAY_SIZE(b_counter_config_memory_writes); + + return 0; + case METRIC_SET_ID_SAMPLER_BALANCE: + dev_priv->perf.oa.mux_regs = + get_sampler_balance_mux_config(dev_priv, + &dev_priv->perf.oa.mux_regs_len); + if (!dev_priv->perf.oa.mux_regs) { + DRM_DEBUG_DRIVER("No suitable MUX config for \"SAMPLER_BALANCE\" metric set"); + + /* EINVAL because *_register_sysfs already checked this + * and so it wouldn't have been advertised so userspace and + * so shouldn't have been requested + */ + return -EINVAL; + } + + dev_priv->perf.oa.b_counter_regs = + b_counter_config_sampler_balance; + dev_priv->perf.oa.b_counter_regs_len = + ARRAY_SIZE(b_counter_config_sampler_balance); + + return 0; + default: + return -ENODEV; + } +} + +static ssize_t +show_render_basic_id(struct device *kdev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", METRIC_SET_ID_RENDER_BASIC); +} + +static struct device_attribute dev_attr_render_basic_id = { + .attr = { .name = "id", .mode = 0444 }, + .show = show_render_basic_id, + .store = NULL, +}; + +static struct attribute *attrs_render_basic[] = { + &dev_attr_render_basic_id.attr, + NULL, +}; + +static struct attribute_group group_render_basic = { + .name = "403d8832-1a27-4aa6-a64e-f5389ce7b212", + .attrs = attrs_render_basic, +}; + +static ssize_t +show_compute_basic_id(struct device *kdev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", METRIC_SET_ID_COMPUTE_BASIC); +} + +static struct device_attribute dev_attr_compute_basic_id = { + .attr = { .name = "id", .mode = 0444 }, + .show = show_compute_basic_id, + .store = NULL, +}; + +static struct attribute *attrs_compute_basic[] = { + &dev_attr_compute_basic_id.attr, + NULL, +}; + +static struct attribute_group group_compute_basic = { + .name = "39ad14bc-2380-45c4-91eb-fbcb3aa7ae7b", + .attrs = attrs_compute_basic, +}; + +static ssize_t +show_compute_extended_id(struct device *kdev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", METRIC_SET_ID_COMPUTE_EXTENDED); +} + +static struct device_attribute dev_attr_compute_extended_id = { + .attr = { .name = "id", .mode = 0444 }, + .show = show_compute_extended_id, + .store = NULL, +}; + +static struct attribute *attrs_compute_extended[] = { + &dev_attr_compute_extended_id.attr, + NULL, +}; + +static struct attribute_group group_compute_extended = { + .name = "3865be28-6982-49fe-9494-e4d1b4795413", + .attrs = attrs_compute_extended, +}; + +static ssize_t +show_memory_reads_id(struct device *kdev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", METRIC_SET_ID_MEMORY_READS); +} + +static struct device_attribute dev_attr_memory_reads_id = { + .attr = { .name = "id", .mode = 0444 }, + .show = show_memory_reads_id, + .store = NULL, +}; + +static struct attribute *attrs_memory_reads[] = { + &dev_attr_memory_reads_id.attr, + NULL, +}; + +static struct attribute_group group_memory_reads = { + .name = "bb5ed49b-2497-4095-94f6-26ba294db88a", + .attrs = attrs_memory_reads, +}; + +static ssize_t +show_memory_writes_id(struct device *kdev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", METRIC_SET_ID_MEMORY_WRITES); +} + +static struct device_attribute dev_attr_memory_writes_id = { + .attr = { .name = "id", .mode = 0444 }, + .show = show_memory_writes_id, + .store = NULL, +}; + +static struct attribute *attrs_memory_writes[] = { + &dev_attr_memory_writes_id.attr, + NULL, +}; + +static struct attribute_group group_memory_writes = { + .name = "3358d639-9b5f-45ab-976d-9b08cbfc6240", + .attrs = attrs_memory_writes, +}; + +static ssize_t +show_sampler_balance_id(struct device *kdev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", METRIC_SET_ID_SAMPLER_BALANCE); +} + +static struct device_attribute dev_attr_sampler_balance_id = { + .attr = { .name = "id", .mode = 0444 }, + .show = show_sampler_balance_id, + .store = NULL, +}; + +static struct attribute *attrs_sampler_balance[] = { + &dev_attr_sampler_balance_id.attr, + NULL, +}; + +static struct attribute_group group_sampler_balance = { + .name = "bc274488-b4b6-40c7-90da-b77d7ad16189", + .attrs = attrs_sampler_balance, +}; + +int +i915_perf_register_sysfs_hsw(struct drm_i915_private *dev_priv) +{ + int mux_len; + int ret = 0; + + if (get_render_basic_mux_config(dev_priv, &mux_len)) { + ret = sysfs_create_group(dev_priv->perf.metrics_kobj, &group_render_basic); + if (ret) + goto error_render_basic; + } + if (get_compute_basic_mux_config(dev_priv, &mux_len)) { + ret = sysfs_create_group(dev_priv->perf.metrics_kobj, &group_compute_basic); + if (ret) + goto error_compute_basic; + } + if (get_compute_extended_mux_config(dev_priv, &mux_len)) { + ret = sysfs_create_group(dev_priv->perf.metrics_kobj, &group_compute_extended); + if (ret) + goto error_compute_extended; + } + if (get_memory_reads_mux_config(dev_priv, &mux_len)) { + ret = sysfs_create_group(dev_priv->perf.metrics_kobj, &group_memory_reads); + if (ret) + goto error_memory_reads; + } + if (get_memory_writes_mux_config(dev_priv, &mux_len)) { + ret = sysfs_create_group(dev_priv->perf.metrics_kobj, &group_memory_writes); + if (ret) + goto error_memory_writes; + } + if (get_sampler_balance_mux_config(dev_priv, &mux_len)) { + ret = sysfs_create_group(dev_priv->perf.metrics_kobj, &group_sampler_balance); + if (ret) + goto error_sampler_balance; + } + + return 0; + +error_sampler_balance: + if (get_sampler_balance_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_memory_writes); +error_memory_writes: + if (get_sampler_balance_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_memory_reads); +error_memory_reads: + if (get_sampler_balance_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_compute_extended); +error_compute_extended: + if (get_sampler_balance_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_compute_basic); +error_compute_basic: + if (get_sampler_balance_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_render_basic); +error_render_basic: + return ret; +} + +void +i915_perf_unregister_sysfs_hsw(struct drm_i915_private *dev_priv) +{ + int mux_len; + + if (get_render_basic_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_render_basic); + if (get_compute_basic_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_compute_basic); + if (get_compute_extended_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_compute_extended); + if (get_memory_reads_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_memory_reads); + if (get_memory_writes_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_memory_writes); + if (get_sampler_balance_mux_config(dev_priv, &mux_len)) + sysfs_remove_group(dev_priv->perf.metrics_kobj, &group_sampler_balance); +} diff --git a/drivers/gpu/drm/i915/i915_oa_hsw.h b/drivers/gpu/drm/i915/i915_oa_hsw.h new file mode 100644 index 000000000000..429a229b5158 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_oa_hsw.h @@ -0,0 +1,38 @@ +/* + * Autogenerated file, DO NOT EDIT manually! + * + * Copyright (c) 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __I915_OA_HSW_H__ +#define __I915_OA_HSW_H__ + +extern int i915_oa_n_builtin_metric_sets_hsw; + +extern int i915_oa_select_metric_set_hsw(struct drm_i915_private *dev_priv); + +extern int i915_perf_register_sysfs_hsw(struct drm_i915_private *dev_priv); + +extern void i915_perf_unregister_sysfs_hsw(struct drm_i915_private *dev_priv); + +#endif diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index d46ffe7086bc..0e280fbd52f1 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -50,7 +50,7 @@ struct i915_params i915 __read_mostly = { .error_capture = true, .invert_brightness = 0, .disable_display = 0, - .enable_cmd_parser = 1, + .enable_cmd_parser = true, .use_mmio_flip = 0, .mmio_debug = 0, .verbose_state_checks = 1, @@ -188,9 +188,9 @@ MODULE_PARM_DESC(invert_brightness, module_param_named(disable_display, i915.disable_display, bool, 0400); MODULE_PARM_DESC(disable_display, "Disable display (default: false)"); -module_param_named_unsafe(enable_cmd_parser, i915.enable_cmd_parser, int, 0600); +module_param_named_unsafe(enable_cmd_parser, i915.enable_cmd_parser, bool, 0400); MODULE_PARM_DESC(enable_cmd_parser, - "Enable command parsing (1=enabled [default], 0=disabled)"); + "Enable command parsing (true=enabled [default], false=disabled)"); module_param_named_unsafe(use_mmio_flip, i915.use_mmio_flip, int, 0600); MODULE_PARM_DESC(use_mmio_flip, diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 817ad959941e..8e433de04679 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -44,7 +44,6 @@ struct i915_params { int disable_power_well; int enable_ips; int invert_brightness; - int enable_cmd_parser; int enable_guc_loading; int enable_guc_submission; int guc_log_level; @@ -53,6 +52,7 @@ struct i915_params { int edp_vswing; unsigned int inject_load_failure; /* leave bools at the end to not create holes */ + bool enable_cmd_parser; bool enable_hangcheck; bool fastboot; bool prefault_disable; diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index fce8e198bc76..ecb487b5356f 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -54,6 +54,7 @@ #define CHV_COLORS \ .color = { .degamma_lut_size = 65, .gamma_lut_size = 257 } +/* Keep in gen based order, and chronological order within a gen */ #define GEN2_FEATURES \ .gen = 2, .num_pipes = 1, \ .has_overlay = 1, .overlay_needs_physical = 1, \ @@ -65,17 +66,19 @@ static const struct intel_device_info intel_i830_info = { GEN2_FEATURES, + .platform = INTEL_I830, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2, /* legal, last one wins */ }; -static const struct intel_device_info intel_845g_info = { +static const struct intel_device_info intel_i845g_info = { GEN2_FEATURES, + .platform = INTEL_I845G, }; static const struct intel_device_info intel_i85x_info = { GEN2_FEATURES, - .is_i85x = 1, .is_mobile = 1, + .platform = INTEL_I85X, .is_mobile = 1, .num_pipes = 2, /* legal, last one wins */ .cursor_needs_physical = 1, .has_fbc = 1, @@ -83,6 +86,7 @@ static const struct intel_device_info intel_i85x_info = { static const struct intel_device_info intel_i865g_info = { GEN2_FEATURES, + .platform = INTEL_I865G, }; #define GEN3_FEATURES \ @@ -94,12 +98,14 @@ static const struct intel_device_info intel_i865g_info = { static const struct intel_device_info intel_i915g_info = { GEN3_FEATURES, - .is_i915g = 1, .cursor_needs_physical = 1, + .platform = INTEL_I915G, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .hws_needs_physical = 1, }; + static const struct intel_device_info intel_i915gm_info = { GEN3_FEATURES, + .platform = INTEL_I915GM, .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, @@ -107,15 +113,18 @@ static const struct intel_device_info intel_i915gm_info = { .has_fbc = 1, .hws_needs_physical = 1, }; + static const struct intel_device_info intel_i945g_info = { GEN3_FEATURES, + .platform = INTEL_I945G, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .hws_needs_physical = 1, }; + static const struct intel_device_info intel_i945gm_info = { GEN3_FEATURES, - .is_i945gm = 1, .is_mobile = 1, + .platform = INTEL_I945GM, .is_mobile = 1, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, @@ -123,6 +132,20 @@ static const struct intel_device_info intel_i945gm_info = { .hws_needs_physical = 1, }; +static const struct intel_device_info intel_g33_info = { + GEN3_FEATURES, + .platform = INTEL_G33, + .has_hotplug = 1, + .has_overlay = 1, +}; + +static const struct intel_device_info intel_pineview_info = { + GEN3_FEATURES, + .platform = INTEL_PINEVIEW, .is_mobile = 1, + .has_hotplug = 1, + .has_overlay = 1, +}; + #define GEN4_FEATURES \ .gen = 4, .num_pipes = 2, \ .has_hotplug = 1, \ @@ -133,50 +156,36 @@ static const struct intel_device_info intel_i945gm_info = { static const struct intel_device_info intel_i965g_info = { GEN4_FEATURES, - .is_broadwater = 1, + .platform = INTEL_I965G, .has_overlay = 1, .hws_needs_physical = 1, }; static const struct intel_device_info intel_i965gm_info = { GEN4_FEATURES, - .is_crestline = 1, + .platform = INTEL_I965GM, .is_mobile = 1, .has_fbc = 1, .has_overlay = 1, .supports_tv = 1, .hws_needs_physical = 1, }; -static const struct intel_device_info intel_g33_info = { - GEN3_FEATURES, - .is_g33 = 1, - .has_hotplug = 1, - .has_overlay = 1, -}; - static const struct intel_device_info intel_g45_info = { GEN4_FEATURES, - .is_g4x = 1, + .platform = INTEL_G45, .has_pipe_cxsr = 1, .ring_mask = RENDER_RING | BSD_RING, }; static const struct intel_device_info intel_gm45_info = { GEN4_FEATURES, - .is_g4x = 1, + .platform = INTEL_GM45, .is_mobile = 1, .has_fbc = 1, .has_pipe_cxsr = 1, .supports_tv = 1, .ring_mask = RENDER_RING | BSD_RING, }; -static const struct intel_device_info intel_pineview_info = { - GEN3_FEATURES, - .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, - .has_hotplug = 1, - .has_overlay = 1, -}; - #define GEN5_FEATURES \ .gen = 5, .num_pipes = 2, \ .has_hotplug = 1, \ @@ -187,10 +196,12 @@ static const struct intel_device_info intel_pineview_info = { static const struct intel_device_info intel_ironlake_d_info = { GEN5_FEATURES, + .platform = INTEL_IRONLAKE, }; static const struct intel_device_info intel_ironlake_m_info = { GEN5_FEATURES, + .platform = INTEL_IRONLAKE, .is_mobile = 1, }; @@ -204,15 +215,18 @@ static const struct intel_device_info intel_ironlake_m_info = { .has_rc6p = 1, \ .has_gmbus_irq = 1, \ .has_hw_contexts = 1, \ + .has_aliasing_ppgtt = 1, \ GEN_DEFAULT_PIPEOFFSETS, \ CURSOR_OFFSETS static const struct intel_device_info intel_sandybridge_d_info = { GEN6_FEATURES, + .platform = INTEL_SANDYBRIDGE, }; static const struct intel_device_info intel_sandybridge_m_info = { GEN6_FEATURES, + .platform = INTEL_SANDYBRIDGE, .is_mobile = 1, }; @@ -226,46 +240,49 @@ static const struct intel_device_info intel_sandybridge_m_info = { .has_rc6p = 1, \ .has_gmbus_irq = 1, \ .has_hw_contexts = 1, \ + .has_aliasing_ppgtt = 1, \ + .has_full_ppgtt = 1, \ GEN_DEFAULT_PIPEOFFSETS, \ IVB_CURSOR_OFFSETS static const struct intel_device_info intel_ivybridge_d_info = { GEN7_FEATURES, - .is_ivybridge = 1, + .platform = INTEL_IVYBRIDGE, .has_l3_dpf = 1, }; static const struct intel_device_info intel_ivybridge_m_info = { GEN7_FEATURES, - .is_ivybridge = 1, + .platform = INTEL_IVYBRIDGE, .is_mobile = 1, .has_l3_dpf = 1, }; static const struct intel_device_info intel_ivybridge_q_info = { GEN7_FEATURES, - .is_ivybridge = 1, + .platform = INTEL_IVYBRIDGE, .num_pipes = 0, /* legal, last one wins */ .has_l3_dpf = 1, }; -#define VLV_FEATURES \ - .gen = 7, .num_pipes = 2, \ - .has_psr = 1, \ - .has_runtime_pm = 1, \ - .has_rc6 = 1, \ - .has_gmbus_irq = 1, \ - .has_hw_contexts = 1, \ - .has_gmch_display = 1, \ - .has_hotplug = 1, \ - .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \ - .display_mmio_offset = VLV_DISPLAY_BASE, \ - GEN_DEFAULT_PIPEOFFSETS, \ - CURSOR_OFFSETS - static const struct intel_device_info intel_valleyview_info = { - VLV_FEATURES, - .is_valleyview = 1, + .platform = INTEL_VALLEYVIEW, + .gen = 7, + .is_lp = 1, + .num_pipes = 2, + .has_psr = 1, + .has_runtime_pm = 1, + .has_rc6 = 1, + .has_gmbus_irq = 1, + .has_hw_contexts = 1, + .has_gmch_display = 1, + .has_hotplug = 1, + .has_aliasing_ppgtt = 1, + .has_full_ppgtt = 1, + .ring_mask = RENDER_RING | BSD_RING | BLT_RING, + .display_mmio_offset = VLV_DISPLAY_BASE, + GEN_DEFAULT_PIPEOFFSETS, + CURSOR_OFFSETS }; #define HSW_FEATURES \ @@ -281,7 +298,7 @@ static const struct intel_device_info intel_valleyview_info = { static const struct intel_device_info intel_haswell_info = { HSW_FEATURES, - .is_haswell = 1, + .platform = INTEL_HASWELL, .has_l3_dpf = 1, }; @@ -289,26 +306,28 @@ static const struct intel_device_info intel_haswell_info = { HSW_FEATURES, \ BDW_COLORS, \ .has_logical_ring_contexts = 1, \ + .has_full_48bit_ppgtt = 1, \ .has_64bit_reloc = 1 static const struct intel_device_info intel_broadwell_info = { BDW_FEATURES, .gen = 8, - .is_broadwell = 1, + .platform = INTEL_BROADWELL, }; static const struct intel_device_info intel_broadwell_gt3_info = { BDW_FEATURES, .gen = 8, - .is_broadwell = 1, + .platform = INTEL_BROADWELL, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; static const struct intel_device_info intel_cherryview_info = { .gen = 8, .num_pipes = 3, .has_hotplug = 1, + .is_lp = 1, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, - .is_cherryview = 1, + .platform = INTEL_CHERRYVIEW, .has_64bit_reloc = 1, .has_psr = 1, .has_runtime_pm = 1, @@ -318,6 +337,8 @@ static const struct intel_device_info intel_cherryview_info = { .has_hw_contexts = 1, .has_logical_ring_contexts = 1, .has_gmch_display = 1, + .has_aliasing_ppgtt = 1, + .has_full_ppgtt = 1, .display_mmio_offset = VLV_DISPLAY_BASE, GEN_CHV_PIPEOFFSETS, CURSOR_OFFSETS, @@ -326,7 +347,7 @@ static const struct intel_device_info intel_cherryview_info = { static const struct intel_device_info intel_skylake_info = { BDW_FEATURES, - .is_skylake = 1, + .platform = INTEL_SKYLAKE, .gen = 9, .has_csr = 1, .has_guc = 1, @@ -335,7 +356,7 @@ static const struct intel_device_info intel_skylake_info = { static const struct intel_device_info intel_skylake_gt3_info = { BDW_FEATURES, - .is_skylake = 1, + .platform = INTEL_SKYLAKE, .gen = 9, .has_csr = 1, .has_guc = 1, @@ -343,36 +364,50 @@ static const struct intel_device_info intel_skylake_gt3_info = { .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; +#define GEN9_LP_FEATURES \ + .gen = 9, \ + .is_lp = 1, \ + .has_hotplug = 1, \ + .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, \ + .num_pipes = 3, \ + .has_64bit_reloc = 1, \ + .has_ddi = 1, \ + .has_fpga_dbg = 1, \ + .has_fbc = 1, \ + .has_runtime_pm = 1, \ + .has_pooled_eu = 0, \ + .has_csr = 1, \ + .has_resource_streamer = 1, \ + .has_rc6 = 1, \ + .has_dp_mst = 1, \ + .has_gmbus_irq = 1, \ + .has_hw_contexts = 1, \ + .has_logical_ring_contexts = 1, \ + .has_guc = 1, \ + .has_decoupled_mmio = 1, \ + .has_aliasing_ppgtt = 1, \ + .has_full_ppgtt = 1, \ + .has_full_48bit_ppgtt = 1, \ + GEN_DEFAULT_PIPEOFFSETS, \ + IVB_CURSOR_OFFSETS, \ + BDW_COLORS + static const struct intel_device_info intel_broxton_info = { - .is_broxton = 1, - .gen = 9, - .has_hotplug = 1, - .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, - .num_pipes = 3, - .has_64bit_reloc = 1, - .has_ddi = 1, - .has_fpga_dbg = 1, - .has_fbc = 1, - .has_runtime_pm = 1, - .has_pooled_eu = 0, - .has_csr = 1, - .has_resource_streamer = 1, - .has_rc6 = 1, - .has_dp_mst = 1, - .has_gmbus_irq = 1, - .has_hw_contexts = 1, - .has_logical_ring_contexts = 1, - .has_guc = 1, - .has_decoupled_mmio = 1, + GEN9_LP_FEATURES, + .platform = INTEL_BROXTON, .ddb_size = 512, - GEN_DEFAULT_PIPEOFFSETS, - IVB_CURSOR_OFFSETS, - BDW_COLORS, +}; + +static const struct intel_device_info intel_geminilake_info = { + GEN9_LP_FEATURES, + .platform = INTEL_GEMINILAKE, + .is_alpha_support = 1, + .ddb_size = 1024, }; static const struct intel_device_info intel_kabylake_info = { BDW_FEATURES, - .is_kabylake = 1, + .platform = INTEL_KABYLAKE, .gen = 9, .has_csr = 1, .has_guc = 1, @@ -381,7 +416,7 @@ static const struct intel_device_info intel_kabylake_info = { static const struct intel_device_info intel_kabylake_gt3_info = { BDW_FEATURES, - .is_kabylake = 1, + .platform = INTEL_KABYLAKE, .gen = 9, .has_csr = 1, .has_guc = 1, @@ -397,7 +432,7 @@ static const struct intel_device_info intel_kabylake_gt3_info = { */ static const struct pci_device_id pciidlist[] = { INTEL_I830_IDS(&intel_i830_info), - INTEL_I845G_IDS(&intel_845g_info), + INTEL_I845G_IDS(&intel_i845g_info), INTEL_I85X_IDS(&intel_i85x_info), INTEL_I865G_IDS(&intel_i865g_info), INTEL_I915G_IDS(&intel_i915g_info), @@ -421,12 +456,14 @@ static const struct pci_device_id pciidlist[] = { INTEL_VLV_IDS(&intel_valleyview_info), INTEL_BDW_GT12_IDS(&intel_broadwell_info), INTEL_BDW_GT3_IDS(&intel_broadwell_gt3_info), + INTEL_BDW_RSVD_IDS(&intel_broadwell_info), INTEL_CHV_IDS(&intel_cherryview_info), INTEL_SKL_GT1_IDS(&intel_skylake_info), INTEL_SKL_GT2_IDS(&intel_skylake_info), INTEL_SKL_GT3_IDS(&intel_skylake_gt3_info), INTEL_SKL_GT4_IDS(&intel_skylake_gt3_info), INTEL_BXT_IDS(&intel_broxton_info), + INTEL_GLK_IDS(&intel_geminilake_info), INTEL_KBL_GT1_IDS(&intel_kabylake_info), INTEL_KBL_GT2_IDS(&intel_kabylake_info), INTEL_KBL_GT3_IDS(&intel_kabylake_gt3_info), diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c new file mode 100644 index 000000000000..a1b7eec58be2 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -0,0 +1,2096 @@ +/* + * Copyright © 2015-2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Robert Bragg <robert@sixbynine.org> + */ + + +/** + * DOC: i915 Perf Overview + * + * Gen graphics supports a large number of performance counters that can help + * driver and application developers understand and optimize their use of the + * GPU. + * + * This i915 perf interface enables userspace to configure and open a file + * descriptor representing a stream of GPU metrics which can then be read() as + * a stream of sample records. + * + * The interface is particularly suited to exposing buffered metrics that are + * captured by DMA from the GPU, unsynchronized with and unrelated to the CPU. + * + * Streams representing a single context are accessible to applications with a + * corresponding drm file descriptor, such that OpenGL can use the interface + * without special privileges. Access to system-wide metrics requires root + * privileges by default, unless changed via the dev.i915.perf_event_paranoid + * sysctl option. + * + */ + +/** + * DOC: i915 Perf History and Comparison with Core Perf + * + * The interface was initially inspired by the core Perf infrastructure but + * some notable differences are: + * + * i915 perf file descriptors represent a "stream" instead of an "event"; where + * a perf event primarily corresponds to a single 64bit value, while a stream + * might sample sets of tightly-coupled counters, depending on the + * configuration. For example the Gen OA unit isn't designed to support + * orthogonal configurations of individual counters; it's configured for a set + * of related counters. Samples for an i915 perf stream capturing OA metrics + * will include a set of counter values packed in a compact HW specific format. + * The OA unit supports a number of different packing formats which can be + * selected by the user opening the stream. Perf has support for grouping + * events, but each event in the group is configured, validated and + * authenticated individually with separate system calls. + * + * i915 perf stream configurations are provided as an array of u64 (key,value) + * pairs, instead of a fixed struct with multiple miscellaneous config members, + * interleaved with event-type specific members. + * + * i915 perf doesn't support exposing metrics via an mmap'd circular buffer. + * The supported metrics are being written to memory by the GPU unsynchronized + * with the CPU, using HW specific packing formats for counter sets. Sometimes + * the constraints on HW configuration require reports to be filtered before it + * would be acceptable to expose them to unprivileged applications - to hide + * the metrics of other processes/contexts. For these use cases a read() based + * interface is a good fit, and provides an opportunity to filter data as it + * gets copied from the GPU mapped buffers to userspace buffers. + * + * + * Issues hit with first prototype based on Core Perf + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The first prototype of this driver was based on the core perf + * infrastructure, and while we did make that mostly work, with some changes to + * perf, we found we were breaking or working around too many assumptions baked + * into perf's currently cpu centric design. + * + * In the end we didn't see a clear benefit to making perf's implementation and + * interface more complex by changing design assumptions while we knew we still + * wouldn't be able to use any existing perf based userspace tools. + * + * Also considering the Gen specific nature of the Observability hardware and + * how userspace will sometimes need to combine i915 perf OA metrics with + * side-band OA data captured via MI_REPORT_PERF_COUNT commands; we're + * expecting the interface to be used by a platform specific userspace such as + * OpenGL or tools. This is to say; we aren't inherently missing out on having + * a standard vendor/architecture agnostic interface by not using perf. + * + * + * For posterity, in case we might re-visit trying to adapt core perf to be + * better suited to exposing i915 metrics these were the main pain points we + * hit: + * + * - The perf based OA PMU driver broke some significant design assumptions: + * + * Existing perf pmus are used for profiling work on a cpu and we were + * introducing the idea of _IS_DEVICE pmus with different security + * implications, the need to fake cpu-related data (such as user/kernel + * registers) to fit with perf's current design, and adding _DEVICE records + * as a way to forward device-specific status records. + * + * The OA unit writes reports of counters into a circular buffer, without + * involvement from the CPU, making our PMU driver the first of a kind. + * + * Given the way we were periodically forward data from the GPU-mapped, OA + * buffer to perf's buffer, those bursts of sample writes looked to perf like + * we were sampling too fast and so we had to subvert its throttling checks. + * + * Perf supports groups of counters and allows those to be read via + * transactions internally but transactions currently seem designed to be + * explicitly initiated from the cpu (say in response to a userspace read()) + * and while we could pull a report out of the OA buffer we can't + * trigger a report from the cpu on demand. + * + * Related to being report based; the OA counters are configured in HW as a + * set while perf generally expects counter configurations to be orthogonal. + * Although counters can be associated with a group leader as they are + * opened, there's no clear precedent for being able to provide group-wide + * configuration attributes (for example we want to let userspace choose the + * OA unit report format used to capture all counters in a set, or specify a + * GPU context to filter metrics on). We avoided using perf's grouping + * feature and forwarded OA reports to userspace via perf's 'raw' sample + * field. This suited our userspace well considering how coupled the counters + * are when dealing with normalizing. It would be inconvenient to split + * counters up into separate events, only to require userspace to recombine + * them. For Mesa it's also convenient to be forwarded raw, periodic reports + * for combining with the side-band raw reports it captures using + * MI_REPORT_PERF_COUNT commands. + * + * - As a side note on perf's grouping feature; there was also some concern + * that using PERF_FORMAT_GROUP as a way to pack together counter values + * would quite drastically inflate our sample sizes, which would likely + * lower the effective sampling resolutions we could use when the available + * memory bandwidth is limited. + * + * With the OA unit's report formats, counters are packed together as 32 + * or 40bit values, with the largest report size being 256 bytes. + * + * PERF_FORMAT_GROUP values are 64bit, but there doesn't appear to be a + * documented ordering to the values, implying PERF_FORMAT_ID must also be + * used to add a 64bit ID before each value; giving 16 bytes per counter. + * + * Related to counter orthogonality; we can't time share the OA unit, while + * event scheduling is a central design idea within perf for allowing + * userspace to open + enable more events than can be configured in HW at any + * one time. The OA unit is not designed to allow re-configuration while in + * use. We can't reconfigure the OA unit without losing internal OA unit + * state which we can't access explicitly to save and restore. Reconfiguring + * the OA unit is also relatively slow, involving ~100 register writes. From + * userspace Mesa also depends on a stable OA configuration when emitting + * MI_REPORT_PERF_COUNT commands and importantly the OA unit can't be + * disabled while there are outstanding MI_RPC commands lest we hang the + * command streamer. + * + * The contents of sample records aren't extensible by device drivers (i.e. + * the sample_type bits). As an example; Sourab Gupta had been looking to + * attach GPU timestamps to our OA samples. We were shoehorning OA reports + * into sample records by using the 'raw' field, but it's tricky to pack more + * than one thing into this field because events/core.c currently only lets a + * pmu give a single raw data pointer plus len which will be copied into the + * ring buffer. To include more than the OA report we'd have to copy the + * report into an intermediate larger buffer. I'd been considering allowing a + * vector of data+len values to be specified for copying the raw data, but + * it felt like a kludge to being using the raw field for this purpose. + * + * - It felt like our perf based PMU was making some technical compromises + * just for the sake of using perf: + * + * perf_event_open() requires events to either relate to a pid or a specific + * cpu core, while our device pmu related to neither. Events opened with a + * pid will be automatically enabled/disabled according to the scheduling of + * that process - so not appropriate for us. When an event is related to a + * cpu id, perf ensures pmu methods will be invoked via an inter process + * interrupt on that core. To avoid invasive changes our userspace opened OA + * perf events for a specific cpu. This was workable but it meant the + * majority of the OA driver ran in atomic context, including all OA report + * forwarding, which wasn't really necessary in our case and seems to make + * our locking requirements somewhat complex as we handled the interaction + * with the rest of the i915 driver. + */ + +#include <linux/anon_inodes.h> +#include <linux/sizes.h> + +#include "i915_drv.h" +#include "i915_oa_hsw.h" + +/* HW requires this to be a power of two, between 128k and 16M, though driver + * is currently generally designed assuming the largest 16M size is used such + * that the overflow cases are unlikely in normal operation. + */ +#define OA_BUFFER_SIZE SZ_16M + +#define OA_TAKEN(tail, head) ((tail - head) & (OA_BUFFER_SIZE - 1)) + +/* There's a HW race condition between OA unit tail pointer register updates and + * writes to memory whereby the tail pointer can sometimes get ahead of what's + * been written out to the OA buffer so far. + * + * Although this can be observed explicitly by checking for a zeroed report-id + * field in tail reports, it seems preferable to account for this earlier e.g. + * as part of the _oa_buffer_is_empty checks to minimize -EAGAIN polling cycles + * in this situation. + * + * To give time for the most recent reports to land before they may be copied to + * userspace, the driver operates as if the tail pointer effectively lags behind + * the HW tail pointer by 'tail_margin' bytes. The margin in bytes is calculated + * based on this constant in nanoseconds, the current OA sampling exponent + * and current report size. + * + * There is also a fallback check while reading to simply skip over reports with + * a zeroed report-id. + */ +#define OA_TAIL_MARGIN_NSEC 100000ULL + +/* frequency for checking whether the OA unit has written new reports to the + * circular OA buffer... + */ +#define POLL_FREQUENCY 200 +#define POLL_PERIOD (NSEC_PER_SEC / POLL_FREQUENCY) + +/* for sysctl proc_dointvec_minmax of dev.i915.perf_stream_paranoid */ +static int zero; +static int one = 1; +static u32 i915_perf_stream_paranoid = true; + +/* The maximum exponent the hardware accepts is 63 (essentially it selects one + * of the 64bit timestamp bits to trigger reports from) but there's currently + * no known use case for sampling as infrequently as once per 47 thousand years. + * + * Since the timestamps included in OA reports are only 32bits it seems + * reasonable to limit the OA exponent where it's still possible to account for + * overflow in OA report timestamps. + */ +#define OA_EXPONENT_MAX 31 + +#define INVALID_CTX_ID 0xffffffff + + +/* For sysctl proc_dointvec_minmax of i915_oa_max_sample_rate + * + * 160ns is the smallest sampling period we can theoretically program the OA + * unit with on Haswell, corresponding to 6.25MHz. + */ +static int oa_sample_rate_hard_limit = 6250000; + +/* Theoretically we can program the OA unit to sample every 160ns but don't + * allow that by default unless root... + * + * The default threshold of 100000Hz is based on perf's similar + * kernel.perf_event_max_sample_rate sysctl parameter. + */ +static u32 i915_oa_max_sample_rate = 100000; + +/* XXX: beware if future OA HW adds new report formats that the current + * code assumes all reports have a power-of-two size and ~(size - 1) can + * be used as a mask to align the OA tail pointer. + */ +static struct i915_oa_format hsw_oa_formats[I915_OA_FORMAT_MAX] = { + [I915_OA_FORMAT_A13] = { 0, 64 }, + [I915_OA_FORMAT_A29] = { 1, 128 }, + [I915_OA_FORMAT_A13_B8_C8] = { 2, 128 }, + /* A29_B8_C8 Disallowed as 192 bytes doesn't factor into buffer size */ + [I915_OA_FORMAT_B4_C8] = { 4, 64 }, + [I915_OA_FORMAT_A45_B8_C8] = { 5, 256 }, + [I915_OA_FORMAT_B4_C8_A16] = { 6, 128 }, + [I915_OA_FORMAT_C4_B8] = { 7, 64 }, +}; + +#define SAMPLE_OA_REPORT (1<<0) + +/** + * struct perf_open_properties - for validated properties given to open a stream + * @sample_flags: `DRM_I915_PERF_PROP_SAMPLE_*` properties are tracked as flags + * @single_context: Whether a single or all gpu contexts should be monitored + * @ctx_handle: A gem ctx handle for use with @single_context + * @metrics_set: An ID for an OA unit metric set advertised via sysfs + * @oa_format: An OA unit HW report format + * @oa_periodic: Whether to enable periodic OA unit sampling + * @oa_period_exponent: The OA unit sampling period is derived from this + * + * As read_properties_unlocked() enumerates and validates the properties given + * to open a stream of metrics the configuration is built up in the structure + * which starts out zero initialized. + */ +struct perf_open_properties { + u32 sample_flags; + + u64 single_context:1; + u64 ctx_handle; + + /* OA sampling state */ + int metrics_set; + int oa_format; + bool oa_periodic; + int oa_period_exponent; +}; + +/* NB: This is either called via fops or the poll check hrtimer (atomic ctx) + * + * It's safe to read OA config state here unlocked, assuming that this is only + * called while the stream is enabled, while the global OA configuration can't + * be modified. + * + * Note: we don't lock around the head/tail reads even though there's the slim + * possibility of read() fop errors forcing a re-init of the OA buffer + * pointers. A race here could result in a false positive !empty status which + * is acceptable. + */ +static bool gen7_oa_buffer_is_empty_fop_unlocked(struct drm_i915_private *dev_priv) +{ + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + u32 oastatus2 = I915_READ(GEN7_OASTATUS2); + u32 oastatus1 = I915_READ(GEN7_OASTATUS1); + u32 head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; + u32 tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; + + return OA_TAKEN(tail, head) < + dev_priv->perf.oa.tail_margin + report_size; +} + +/** + * append_oa_status - Appends a status record to a userspace read() buffer. + * @stream: An i915-perf stream opened for OA metrics + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @offset: (inout): the current position for writing into @buf + * @type: The kind of status to report to userspace + * + * Writes a status record (such as `DRM_I915_PERF_RECORD_OA_REPORT_LOST`) + * into the userspace read() buffer. + * + * The @buf @offset will only be updated on success. + * + * Returns: 0 on success, negative error code on failure. + */ +static int append_oa_status(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset, + enum drm_i915_perf_record_type type) +{ + struct drm_i915_perf_record_header header = { type, 0, sizeof(header) }; + + if ((count - *offset) < header.size) + return -ENOSPC; + + if (copy_to_user(buf + *offset, &header, sizeof(header))) + return -EFAULT; + + (*offset) += header.size; + + return 0; +} + +/** + * append_oa_sample - Copies single OA report into userspace read() buffer. + * @stream: An i915-perf stream opened for OA metrics + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @offset: (inout): the current position for writing into @buf + * @report: A single OA report to (optionally) include as part of the sample + * + * The contents of a sample are configured through `DRM_I915_PERF_PROP_SAMPLE_*` + * properties when opening a stream, tracked as `stream->sample_flags`. This + * function copies the requested components of a single sample to the given + * read() @buf. + * + * The @buf @offset will only be updated on success. + * + * Returns: 0 on success, negative error code on failure. + */ +static int append_oa_sample(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset, + const u8 *report) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + struct drm_i915_perf_record_header header; + u32 sample_flags = stream->sample_flags; + + header.type = DRM_I915_PERF_RECORD_SAMPLE; + header.pad = 0; + header.size = stream->sample_size; + + if ((count - *offset) < header.size) + return -ENOSPC; + + buf += *offset; + if (copy_to_user(buf, &header, sizeof(header))) + return -EFAULT; + buf += sizeof(header); + + if (sample_flags & SAMPLE_OA_REPORT) { + if (copy_to_user(buf, report, report_size)) + return -EFAULT; + } + + (*offset) += header.size; + + return 0; +} + +/** + * Copies all buffered OA reports into userspace read() buffer. + * @stream: An i915-perf stream opened for OA metrics + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @offset: (inout): the current position for writing into @buf + * @head_ptr: (inout): the current oa buffer cpu read position + * @tail: the current oa buffer gpu write position + * + * Notably any error condition resulting in a short read (-%ENOSPC or + * -%EFAULT) will be returned even though one or more records may + * have been successfully copied. In this case it's up to the caller + * to decide if the error should be squashed before returning to + * userspace. + * + * Note: reports are consumed from the head, and appended to the + * tail, so the head chases the tail?... If you think that's mad + * and back-to-front you're not alone, but this follows the + * Gen PRM naming convention. + * + * Returns: 0 on success, negative error code on failure. + */ +static int gen7_append_oa_reports(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset, + u32 *head_ptr, + u32 tail) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + u8 *oa_buf_base = dev_priv->perf.oa.oa_buffer.vaddr; + int tail_margin = dev_priv->perf.oa.tail_margin; + u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma); + u32 mask = (OA_BUFFER_SIZE - 1); + u32 head; + u32 taken; + int ret = 0; + + if (WARN_ON(!stream->enabled)) + return -EIO; + + head = *head_ptr - gtt_offset; + tail -= gtt_offset; + + /* The OA unit is expected to wrap the tail pointer according to the OA + * buffer size and since we should never write a misaligned head + * pointer we don't expect to read one back either... + */ + if (tail > OA_BUFFER_SIZE || head > OA_BUFFER_SIZE || + head % report_size) { + DRM_ERROR("Inconsistent OA buffer pointer (head = %u, tail = %u): force restart\n", + head, tail); + dev_priv->perf.oa.ops.oa_disable(dev_priv); + dev_priv->perf.oa.ops.oa_enable(dev_priv); + *head_ptr = I915_READ(GEN7_OASTATUS2) & + GEN7_OASTATUS2_HEAD_MASK; + return -EIO; + } + + + /* The tail pointer increases in 64 byte increments, not in report_size + * steps... + */ + tail &= ~(report_size - 1); + + /* Move the tail pointer back by the current tail_margin to account for + * the possibility that the latest reports may not have really landed + * in memory yet... + */ + + if (OA_TAKEN(tail, head) < report_size + tail_margin) + return -EAGAIN; + + tail -= tail_margin; + tail &= mask; + + for (/* none */; + (taken = OA_TAKEN(tail, head)); + head = (head + report_size) & mask) { + u8 *report = oa_buf_base + head; + u32 *report32 = (void *)report; + + /* All the report sizes factor neatly into the buffer + * size so we never expect to see a report split + * between the beginning and end of the buffer. + * + * Given the initial alignment check a misalignment + * here would imply a driver bug that would result + * in an overrun. + */ + if (WARN_ON((OA_BUFFER_SIZE - head) < report_size)) { + DRM_ERROR("Spurious OA head ptr: non-integral report offset\n"); + break; + } + + /* The report-ID field for periodic samples includes + * some undocumented flags related to what triggered + * the report and is never expected to be zero so we + * can check that the report isn't invalid before + * copying it to userspace... + */ + if (report32[0] == 0) { + DRM_NOTE("Skipping spurious, invalid OA report\n"); + continue; + } + + ret = append_oa_sample(stream, buf, count, offset, report); + if (ret) + break; + + /* The above report-id field sanity check is based on + * the assumption that the OA buffer is initially + * zeroed and we reset the field after copying so the + * check is still meaningful once old reports start + * being overwritten. + */ + report32[0] = 0; + } + + *head_ptr = gtt_offset + head; + + return ret; +} + +/** + * gen7_oa_read - copy status records then buffered OA reports + * @stream: An i915-perf stream opened for OA metrics + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @offset: (inout): the current position for writing into @buf + * + * Checks Gen 7 specific OA unit status registers and if necessary appends + * corresponding status records for userspace (such as for a buffer full + * condition) and then initiate appending any buffered OA reports. + * + * Updates @offset according to the number of bytes successfully copied into + * the userspace buffer. + * + * Returns: zero on success or a negative error code + */ +static int gen7_oa_read(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int report_size = dev_priv->perf.oa.oa_buffer.format_size; + u32 oastatus2; + u32 oastatus1; + u32 head; + u32 tail; + int ret; + + if (WARN_ON(!dev_priv->perf.oa.oa_buffer.vaddr)) + return -EIO; + + oastatus2 = I915_READ(GEN7_OASTATUS2); + oastatus1 = I915_READ(GEN7_OASTATUS1); + + head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; + tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; + + /* XXX: On Haswell we don't have a safe way to clear oastatus1 + * bits while the OA unit is enabled (while the tail pointer + * may be updated asynchronously) so we ignore status bits + * that have already been reported to userspace. + */ + oastatus1 &= ~dev_priv->perf.oa.gen7_latched_oastatus1; + + /* We treat OABUFFER_OVERFLOW as a significant error: + * + * - The status can be interpreted to mean that the buffer is + * currently full (with a higher precedence than OA_TAKEN() + * which will start to report a near-empty buffer after an + * overflow) but it's awkward that we can't clear the status + * on Haswell, so without a reset we won't be able to catch + * the state again. + * + * - Since it also implies the HW has started overwriting old + * reports it may also affect our sanity checks for invalid + * reports when copying to userspace that assume new reports + * are being written to cleared memory. + * + * - In the future we may want to introduce a flight recorder + * mode where the driver will automatically maintain a safe + * guard band between head/tail, avoiding this overflow + * condition, but we avoid the added driver complexity for + * now. + */ + if (unlikely(oastatus1 & GEN7_OASTATUS1_OABUFFER_OVERFLOW)) { + ret = append_oa_status(stream, buf, count, offset, + DRM_I915_PERF_RECORD_OA_BUFFER_LOST); + if (ret) + return ret; + + DRM_DEBUG("OA buffer overflow: force restart\n"); + + dev_priv->perf.oa.ops.oa_disable(dev_priv); + dev_priv->perf.oa.ops.oa_enable(dev_priv); + + oastatus2 = I915_READ(GEN7_OASTATUS2); + oastatus1 = I915_READ(GEN7_OASTATUS1); + + head = oastatus2 & GEN7_OASTATUS2_HEAD_MASK; + tail = oastatus1 & GEN7_OASTATUS1_TAIL_MASK; + } + + if (unlikely(oastatus1 & GEN7_OASTATUS1_REPORT_LOST)) { + ret = append_oa_status(stream, buf, count, offset, + DRM_I915_PERF_RECORD_OA_REPORT_LOST); + if (ret) + return ret; + dev_priv->perf.oa.gen7_latched_oastatus1 |= + GEN7_OASTATUS1_REPORT_LOST; + } + + ret = gen7_append_oa_reports(stream, buf, count, offset, + &head, tail); + + /* All the report sizes are a power of two and the + * head should always be incremented by some multiple + * of the report size. + * + * A warning here, but notably if we later read back a + * misaligned pointer we will treat that as a bug since + * it could lead to a buffer overrun. + */ + WARN_ONCE(head & (report_size - 1), + "i915: Writing misaligned OA head pointer"); + + /* Note: we update the head pointer here even if an error + * was returned since the error may represent a short read + * where some some reports were successfully copied. + */ + I915_WRITE(GEN7_OASTATUS2, + ((head & GEN7_OASTATUS2_HEAD_MASK) | + OA_MEM_SELECT_GGTT)); + + return ret; +} + +/** + * i915_oa_wait_unlocked - handles blocking IO until OA data available + * @stream: An i915-perf stream opened for OA metrics + * + * Called when userspace tries to read() from a blocking stream FD opened + * for OA metrics. It waits until the hrtimer callback finds a non-empty + * OA buffer and wakes us. + * + * Note: it's acceptable to have this return with some false positives + * since any subsequent read handling will return -EAGAIN if there isn't + * really data ready for userspace yet. + * + * Returns: zero on success or a negative error code + */ +static int i915_oa_wait_unlocked(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + /* We would wait indefinitely if periodic sampling is not enabled */ + if (!dev_priv->perf.oa.periodic) + return -EIO; + + /* Note: the oa_buffer_is_empty() condition is ok to run unlocked as it + * just performs mmio reads of the OA buffer head + tail pointers and + * it's assumed we're handling some operation that implies the stream + * can't be destroyed until completion (such as a read()) that ensures + * the device + OA buffer can't disappear + */ + return wait_event_interruptible(dev_priv->perf.oa.poll_wq, + !dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv)); +} + +/** + * i915_oa_poll_wait - call poll_wait() for an OA stream poll() + * @stream: An i915-perf stream opened for OA metrics + * @file: An i915 perf stream file + * @wait: poll() state table + * + * For handling userspace polling on an i915 perf stream opened for OA metrics, + * this starts a poll_wait with the wait queue that our hrtimer callback wakes + * when it sees data ready to read in the circular OA buffer. + */ +static void i915_oa_poll_wait(struct i915_perf_stream *stream, + struct file *file, + poll_table *wait) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + poll_wait(file, &dev_priv->perf.oa.poll_wq, wait); +} + +/** + * i915_oa_read - just calls through to &i915_oa_ops->read + * @stream: An i915-perf stream opened for OA metrics + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @offset: (inout): the current position for writing into @buf + * + * Updates @offset according to the number of bytes successfully copied into + * the userspace buffer. + * + * Returns: zero on success or a negative error code + */ +static int i915_oa_read(struct i915_perf_stream *stream, + char __user *buf, + size_t count, + size_t *offset) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + return dev_priv->perf.oa.ops.read(stream, buf, count, offset); +} + +/** + * oa_get_render_ctx_id - determine and hold ctx hw id + * @stream: An i915-perf stream opened for OA metrics + * + * Determine the render context hw id, and ensure it remains fixed for the + * lifetime of the stream. This ensures that we don't have to worry about + * updating the context ID in OACONTROL on the fly. + * + * Returns: zero on success or a negative error code + */ +static int oa_get_render_ctx_id(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + struct intel_engine_cs *engine = dev_priv->engine[RCS]; + int ret; + + ret = i915_mutex_lock_interruptible(&dev_priv->drm); + if (ret) + return ret; + + /* As the ID is the gtt offset of the context's vma we pin + * the vma to ensure the ID remains fixed. + * + * NB: implied RCS engine... + */ + ret = engine->context_pin(engine, stream->ctx); + if (ret) + goto unlock; + + /* Explicitly track the ID (instead of calling i915_ggtt_offset() + * on the fly) considering the difference with gen8+ and + * execlists + */ + dev_priv->perf.oa.specific_ctx_id = + i915_ggtt_offset(stream->ctx->engine[engine->id].state); + +unlock: + mutex_unlock(&dev_priv->drm.struct_mutex); + + return ret; +} + +/** + * oa_put_render_ctx_id - counterpart to oa_get_render_ctx_id releases hold + * @stream: An i915-perf stream opened for OA metrics + * + * In case anything needed doing to ensure the context HW ID would remain valid + * for the lifetime of the stream, then that can be undone here. + */ +static void oa_put_render_ctx_id(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + struct intel_engine_cs *engine = dev_priv->engine[RCS]; + + mutex_lock(&dev_priv->drm.struct_mutex); + + dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID; + engine->context_unpin(engine, stream->ctx); + + mutex_unlock(&dev_priv->drm.struct_mutex); +} + +static void +free_oa_buffer(struct drm_i915_private *i915) +{ + mutex_lock(&i915->drm.struct_mutex); + + i915_gem_object_unpin_map(i915->perf.oa.oa_buffer.vma->obj); + i915_vma_unpin(i915->perf.oa.oa_buffer.vma); + i915_gem_object_put(i915->perf.oa.oa_buffer.vma->obj); + + i915->perf.oa.oa_buffer.vma = NULL; + i915->perf.oa.oa_buffer.vaddr = NULL; + + mutex_unlock(&i915->drm.struct_mutex); +} + +static void i915_oa_stream_destroy(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + BUG_ON(stream != dev_priv->perf.oa.exclusive_stream); + + dev_priv->perf.oa.ops.disable_metric_set(dev_priv); + + free_oa_buffer(dev_priv); + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + intel_runtime_pm_put(dev_priv); + + if (stream->ctx) + oa_put_render_ctx_id(stream); + + dev_priv->perf.oa.exclusive_stream = NULL; +} + +static void gen7_init_oa_buffer(struct drm_i915_private *dev_priv) +{ + u32 gtt_offset = i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma); + + /* Pre-DevBDW: OABUFFER must be set with counters off, + * before OASTATUS1, but after OASTATUS2 + */ + I915_WRITE(GEN7_OASTATUS2, gtt_offset | OA_MEM_SELECT_GGTT); /* head */ + I915_WRITE(GEN7_OABUFFER, gtt_offset); + I915_WRITE(GEN7_OASTATUS1, gtt_offset | OABUFFER_SIZE_16M); /* tail */ + + /* On Haswell we have to track which OASTATUS1 flags we've + * already seen since they can't be cleared while periodic + * sampling is enabled. + */ + dev_priv->perf.oa.gen7_latched_oastatus1 = 0; + + /* NB: although the OA buffer will initially be allocated + * zeroed via shmfs (and so this memset is redundant when + * first allocating), we may re-init the OA buffer, either + * when re-enabling a stream or in error/reset paths. + * + * The reason we clear the buffer for each re-init is for the + * sanity check in gen7_append_oa_reports() that looks at the + * report-id field to make sure it's non-zero which relies on + * the assumption that new reports are being written to zeroed + * memory... + */ + memset(dev_priv->perf.oa.oa_buffer.vaddr, 0, OA_BUFFER_SIZE); + + /* Maybe make ->pollin per-stream state if we support multiple + * concurrent streams in the future. + */ + dev_priv->perf.oa.pollin = false; +} + +static int alloc_oa_buffer(struct drm_i915_private *dev_priv) +{ + struct drm_i915_gem_object *bo; + struct i915_vma *vma; + int ret; + + if (WARN_ON(dev_priv->perf.oa.oa_buffer.vma)) + return -ENODEV; + + ret = i915_mutex_lock_interruptible(&dev_priv->drm); + if (ret) + return ret; + + BUILD_BUG_ON_NOT_POWER_OF_2(OA_BUFFER_SIZE); + BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M); + + bo = i915_gem_object_create(dev_priv, OA_BUFFER_SIZE); + if (IS_ERR(bo)) { + DRM_ERROR("Failed to allocate OA buffer\n"); + ret = PTR_ERR(bo); + goto unlock; + } + + ret = i915_gem_object_set_cache_level(bo, I915_CACHE_LLC); + if (ret) + goto err_unref; + + /* PreHSW required 512K alignment, HSW requires 16M */ + vma = i915_gem_object_ggtt_pin(bo, NULL, 0, SZ_16M, 0); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto err_unref; + } + dev_priv->perf.oa.oa_buffer.vma = vma; + + dev_priv->perf.oa.oa_buffer.vaddr = + i915_gem_object_pin_map(bo, I915_MAP_WB); + if (IS_ERR(dev_priv->perf.oa.oa_buffer.vaddr)) { + ret = PTR_ERR(dev_priv->perf.oa.oa_buffer.vaddr); + goto err_unpin; + } + + dev_priv->perf.oa.ops.init_oa_buffer(dev_priv); + + DRM_DEBUG_DRIVER("OA Buffer initialized, gtt offset = 0x%x, vaddr = %p\n", + i915_ggtt_offset(dev_priv->perf.oa.oa_buffer.vma), + dev_priv->perf.oa.oa_buffer.vaddr); + + goto unlock; + +err_unpin: + __i915_vma_unpin(vma); + +err_unref: + i915_gem_object_put(bo); + + dev_priv->perf.oa.oa_buffer.vaddr = NULL; + dev_priv->perf.oa.oa_buffer.vma = NULL; + +unlock: + mutex_unlock(&dev_priv->drm.struct_mutex); + return ret; +} + +static void config_oa_regs(struct drm_i915_private *dev_priv, + const struct i915_oa_reg *regs, + int n_regs) +{ + int i; + + for (i = 0; i < n_regs; i++) { + const struct i915_oa_reg *reg = regs + i; + + I915_WRITE(reg->addr, reg->value); + } +} + +static int hsw_enable_metric_set(struct drm_i915_private *dev_priv) +{ + int ret = i915_oa_select_metric_set_hsw(dev_priv); + + if (ret) + return ret; + + I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) | + GT_NOA_ENABLE)); + + /* PRM: + * + * OA unit is using “crclk” for its functionality. When trunk + * level clock gating takes place, OA clock would be gated, + * unable to count the events from non-render clock domain. + * Render clock gating must be disabled when OA is enabled to + * count the events from non-render domain. Unit level clock + * gating for RCS should also be disabled. + */ + I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) & + ~GEN7_DOP_CLOCK_GATE_ENABLE)); + I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) | + GEN6_CSUNIT_CLOCK_GATE_DISABLE)); + + config_oa_regs(dev_priv, dev_priv->perf.oa.mux_regs, + dev_priv->perf.oa.mux_regs_len); + + /* It apparently takes a fairly long time for a new MUX + * configuration to be be applied after these register writes. + * This delay duration was derived empirically based on the + * render_basic config but hopefully it covers the maximum + * configuration latency. + * + * As a fallback, the checks in _append_oa_reports() to skip + * invalid OA reports do also seem to work to discard reports + * generated before this config has completed - albeit not + * silently. + * + * Unfortunately this is essentially a magic number, since we + * don't currently know of a reliable mechanism for predicting + * how long the MUX config will take to apply and besides + * seeing invalid reports we don't know of a reliable way to + * explicitly check that the MUX config has landed. + * + * It's even possible we've miss characterized the underlying + * problem - it just seems like the simplest explanation why + * a delay at this location would mitigate any invalid reports. + */ + usleep_range(15000, 20000); + + config_oa_regs(dev_priv, dev_priv->perf.oa.b_counter_regs, + dev_priv->perf.oa.b_counter_regs_len); + + return 0; +} + +static void hsw_disable_metric_set(struct drm_i915_private *dev_priv) +{ + I915_WRITE(GEN6_UCGCTL1, (I915_READ(GEN6_UCGCTL1) & + ~GEN6_CSUNIT_CLOCK_GATE_DISABLE)); + I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) | + GEN7_DOP_CLOCK_GATE_ENABLE)); + + I915_WRITE(GDT_CHICKEN_BITS, (I915_READ(GDT_CHICKEN_BITS) & + ~GT_NOA_ENABLE)); +} + +static void gen7_update_oacontrol_locked(struct drm_i915_private *dev_priv) +{ + assert_spin_locked(&dev_priv->perf.hook_lock); + + if (dev_priv->perf.oa.exclusive_stream->enabled) { + struct i915_gem_context *ctx = + dev_priv->perf.oa.exclusive_stream->ctx; + u32 ctx_id = dev_priv->perf.oa.specific_ctx_id; + + bool periodic = dev_priv->perf.oa.periodic; + u32 period_exponent = dev_priv->perf.oa.period_exponent; + u32 report_format = dev_priv->perf.oa.oa_buffer.format; + + I915_WRITE(GEN7_OACONTROL, + (ctx_id & GEN7_OACONTROL_CTX_MASK) | + (period_exponent << + GEN7_OACONTROL_TIMER_PERIOD_SHIFT) | + (periodic ? GEN7_OACONTROL_TIMER_ENABLE : 0) | + (report_format << GEN7_OACONTROL_FORMAT_SHIFT) | + (ctx ? GEN7_OACONTROL_PER_CTX_ENABLE : 0) | + GEN7_OACONTROL_ENABLE); + } else + I915_WRITE(GEN7_OACONTROL, 0); +} + +static void gen7_oa_enable(struct drm_i915_private *dev_priv) +{ + unsigned long flags; + + /* Reset buf pointers so we don't forward reports from before now. + * + * Think carefully if considering trying to avoid this, since it + * also ensures status flags and the buffer itself are cleared + * in error paths, and we have checks for invalid reports based + * on the assumption that certain fields are written to zeroed + * memory which this helps maintains. + */ + gen7_init_oa_buffer(dev_priv); + + spin_lock_irqsave(&dev_priv->perf.hook_lock, flags); + gen7_update_oacontrol_locked(dev_priv); + spin_unlock_irqrestore(&dev_priv->perf.hook_lock, flags); +} + +/** + * i915_oa_stream_enable - handle `I915_PERF_IOCTL_ENABLE` for OA stream + * @stream: An i915 perf stream opened for OA metrics + * + * [Re]enables hardware periodic sampling according to the period configured + * when opening the stream. This also starts a hrtimer that will periodically + * check for data in the circular OA buffer for notifying userspace (e.g. + * during a read() or poll()). + */ +static void i915_oa_stream_enable(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + dev_priv->perf.oa.ops.oa_enable(dev_priv); + + if (dev_priv->perf.oa.periodic) + hrtimer_start(&dev_priv->perf.oa.poll_check_timer, + ns_to_ktime(POLL_PERIOD), + HRTIMER_MODE_REL_PINNED); +} + +static void gen7_oa_disable(struct drm_i915_private *dev_priv) +{ + I915_WRITE(GEN7_OACONTROL, 0); +} + +/** + * i915_oa_stream_disable - handle `I915_PERF_IOCTL_DISABLE` for OA stream + * @stream: An i915 perf stream opened for OA metrics + * + * Stops the OA unit from periodically writing counter reports into the + * circular OA buffer. This also stops the hrtimer that periodically checks for + * data in the circular OA buffer, for notifying userspace. + */ +static void i915_oa_stream_disable(struct i915_perf_stream *stream) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + + dev_priv->perf.oa.ops.oa_disable(dev_priv); + + if (dev_priv->perf.oa.periodic) + hrtimer_cancel(&dev_priv->perf.oa.poll_check_timer); +} + +static u64 oa_exponent_to_ns(struct drm_i915_private *dev_priv, int exponent) +{ + return div_u64(1000000000ULL * (2ULL << exponent), + dev_priv->perf.oa.timestamp_frequency); +} + +static const struct i915_perf_stream_ops i915_oa_stream_ops = { + .destroy = i915_oa_stream_destroy, + .enable = i915_oa_stream_enable, + .disable = i915_oa_stream_disable, + .wait_unlocked = i915_oa_wait_unlocked, + .poll_wait = i915_oa_poll_wait, + .read = i915_oa_read, +}; + +/** + * i915_oa_stream_init - validate combined props for OA stream and init + * @stream: An i915 perf stream + * @param: The open parameters passed to `DRM_I915_PERF_OPEN` + * @props: The property state that configures stream (individually validated) + * + * While read_properties_unlocked() validates properties in isolation it + * doesn't ensure that the combination necessarily makes sense. + * + * At this point it has been determined that userspace wants a stream of + * OA metrics, but still we need to further validate the combined + * properties are OK. + * + * If the configuration makes sense then we can allocate memory for + * a circular OA buffer and apply the requested metric set configuration. + * + * Returns: zero on success or a negative error code. + */ +static int i915_oa_stream_init(struct i915_perf_stream *stream, + struct drm_i915_perf_open_param *param, + struct perf_open_properties *props) +{ + struct drm_i915_private *dev_priv = stream->dev_priv; + int format_size; + int ret; + + /* If the sysfs metrics/ directory wasn't registered for some + * reason then don't let userspace try their luck with config + * IDs + */ + if (!dev_priv->perf.metrics_kobj) { + DRM_DEBUG("OA metrics weren't advertised via sysfs\n"); + return -EINVAL; + } + + if (!(props->sample_flags & SAMPLE_OA_REPORT)) { + DRM_DEBUG("Only OA report sampling supported\n"); + return -EINVAL; + } + + if (!dev_priv->perf.oa.ops.init_oa_buffer) { + DRM_DEBUG("OA unit not supported\n"); + return -ENODEV; + } + + /* To avoid the complexity of having to accurately filter + * counter reports and marshal to the appropriate client + * we currently only allow exclusive access + */ + if (dev_priv->perf.oa.exclusive_stream) { + DRM_DEBUG("OA unit already in use\n"); + return -EBUSY; + } + + if (!props->metrics_set) { + DRM_DEBUG("OA metric set not specified\n"); + return -EINVAL; + } + + if (!props->oa_format) { + DRM_DEBUG("OA report format not specified\n"); + return -EINVAL; + } + + stream->sample_size = sizeof(struct drm_i915_perf_record_header); + + format_size = dev_priv->perf.oa.oa_formats[props->oa_format].size; + + stream->sample_flags |= SAMPLE_OA_REPORT; + stream->sample_size += format_size; + + dev_priv->perf.oa.oa_buffer.format_size = format_size; + if (WARN_ON(dev_priv->perf.oa.oa_buffer.format_size == 0)) + return -EINVAL; + + dev_priv->perf.oa.oa_buffer.format = + dev_priv->perf.oa.oa_formats[props->oa_format].format; + + dev_priv->perf.oa.metrics_set = props->metrics_set; + + dev_priv->perf.oa.periodic = props->oa_periodic; + if (dev_priv->perf.oa.periodic) { + u32 tail; + + dev_priv->perf.oa.period_exponent = props->oa_period_exponent; + + /* See comment for OA_TAIL_MARGIN_NSEC for details + * about this tail_margin... + */ + tail = div64_u64(OA_TAIL_MARGIN_NSEC, + oa_exponent_to_ns(dev_priv, + props->oa_period_exponent)); + dev_priv->perf.oa.tail_margin = (tail + 1) * format_size; + } + + if (stream->ctx) { + ret = oa_get_render_ctx_id(stream); + if (ret) + return ret; + } + + ret = alloc_oa_buffer(dev_priv); + if (ret) + goto err_oa_buf_alloc; + + /* PRM - observability performance counters: + * + * OACONTROL, performance counter enable, note: + * + * "When this bit is set, in order to have coherent counts, + * RC6 power state and trunk clock gating must be disabled. + * This can be achieved by programming MMIO registers as + * 0xA094=0 and 0xA090[31]=1" + * + * In our case we are expecting that taking pm + FORCEWAKE + * references will effectively disable RC6. + */ + intel_runtime_pm_get(dev_priv); + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + + ret = dev_priv->perf.oa.ops.enable_metric_set(dev_priv); + if (ret) + goto err_enable; + + stream->ops = &i915_oa_stream_ops; + + dev_priv->perf.oa.exclusive_stream = stream; + + return 0; + +err_enable: + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + intel_runtime_pm_put(dev_priv); + free_oa_buffer(dev_priv); + +err_oa_buf_alloc: + if (stream->ctx) + oa_put_render_ctx_id(stream); + + return ret; +} + +/** + * i915_perf_read_locked - &i915_perf_stream_ops->read with error normalisation + * @stream: An i915 perf stream + * @file: An i915 perf stream file + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @ppos: (inout) file seek position (unused) + * + * Besides wrapping &i915_perf_stream_ops->read this provides a common place to + * ensure that if we've successfully copied any data then reporting that takes + * precedence over any internal error status, so the data isn't lost. + * + * For example ret will be -ENOSPC whenever there is more buffered data than + * can be copied to userspace, but that's only interesting if we weren't able + * to copy some data because it implies the userspace buffer is too small to + * receive a single record (and we never split records). + * + * Another case with ret == -EFAULT is more of a grey area since it would seem + * like bad form for userspace to ask us to overrun its buffer, but the user + * knows best: + * + * http://yarchive.net/comp/linux/partial_reads_writes.html + * + * Returns: The number of bytes copied or a negative error code on failure. + */ +static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream, + struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + /* Note we keep the offset (aka bytes read) separate from any + * error status so that the final check for whether we return + * the bytes read with a higher precedence than any error (see + * comment below) doesn't need to be handled/duplicated in + * stream->ops->read() implementations. + */ + size_t offset = 0; + int ret = stream->ops->read(stream, buf, count, &offset); + + return offset ?: (ret ?: -EAGAIN); +} + +/** + * i915_perf_read - handles read() FOP for i915 perf stream FDs + * @file: An i915 perf stream file + * @buf: destination buffer given by userspace + * @count: the number of bytes userspace wants to read + * @ppos: (inout) file seek position (unused) + * + * The entry point for handling a read() on a stream file descriptor from + * userspace. Most of the work is left to the i915_perf_read_locked() and + * &i915_perf_stream_ops->read but to save having stream implementations (of + * which we might have multiple later) we handle blocking read here. + * + * We can also consistently treat trying to read from a disabled stream + * as an IO error so implementations can assume the stream is enabled + * while reading. + * + * Returns: The number of bytes copied or a negative error code on failure. + */ +static ssize_t i915_perf_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + struct i915_perf_stream *stream = file->private_data; + struct drm_i915_private *dev_priv = stream->dev_priv; + ssize_t ret; + + /* To ensure it's handled consistently we simply treat all reads of a + * disabled stream as an error. In particular it might otherwise lead + * to a deadlock for blocking file descriptors... + */ + if (!stream->enabled) + return -EIO; + + if (!(file->f_flags & O_NONBLOCK)) { + /* There's the small chance of false positives from + * stream->ops->wait_unlocked. + * + * E.g. with single context filtering since we only wait until + * oabuffer has >= 1 report we don't immediately know whether + * any reports really belong to the current context + */ + do { + ret = stream->ops->wait_unlocked(stream); + if (ret) + return ret; + + mutex_lock(&dev_priv->perf.lock); + ret = i915_perf_read_locked(stream, file, + buf, count, ppos); + mutex_unlock(&dev_priv->perf.lock); + } while (ret == -EAGAIN); + } else { + mutex_lock(&dev_priv->perf.lock); + ret = i915_perf_read_locked(stream, file, buf, count, ppos); + mutex_unlock(&dev_priv->perf.lock); + } + + if (ret >= 0) { + /* Maybe make ->pollin per-stream state if we support multiple + * concurrent streams in the future. + */ + dev_priv->perf.oa.pollin = false; + } + + return ret; +} + +static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer) +{ + struct drm_i915_private *dev_priv = + container_of(hrtimer, typeof(*dev_priv), + perf.oa.poll_check_timer); + + if (!dev_priv->perf.oa.ops.oa_buffer_is_empty(dev_priv)) { + dev_priv->perf.oa.pollin = true; + wake_up(&dev_priv->perf.oa.poll_wq); + } + + hrtimer_forward_now(hrtimer, ns_to_ktime(POLL_PERIOD)); + + return HRTIMER_RESTART; +} + +/** + * i915_perf_poll_locked - poll_wait() with a suitable wait queue for stream + * @dev_priv: i915 device instance + * @stream: An i915 perf stream + * @file: An i915 perf stream file + * @wait: poll() state table + * + * For handling userspace polling on an i915 perf stream, this calls through to + * &i915_perf_stream_ops->poll_wait to call poll_wait() with a wait queue that + * will be woken for new stream data. + * + * Note: The &drm_i915_private->perf.lock mutex has been taken to serialize + * with any non-file-operation driver hooks. + * + * Returns: any poll events that are ready without sleeping + */ +static unsigned int i915_perf_poll_locked(struct drm_i915_private *dev_priv, + struct i915_perf_stream *stream, + struct file *file, + poll_table *wait) +{ + unsigned int events = 0; + + stream->ops->poll_wait(stream, file, wait); + + /* Note: we don't explicitly check whether there's something to read + * here since this path may be very hot depending on what else + * userspace is polling, or on the timeout in use. We rely solely on + * the hrtimer/oa_poll_check_timer_cb to notify us when there are + * samples to read. + */ + if (dev_priv->perf.oa.pollin) + events |= POLLIN; + + return events; +} + +/** + * i915_perf_poll - call poll_wait() with a suitable wait queue for stream + * @file: An i915 perf stream file + * @wait: poll() state table + * + * For handling userspace polling on an i915 perf stream, this ensures + * poll_wait() gets called with a wait queue that will be woken for new stream + * data. + * + * Note: Implementation deferred to i915_perf_poll_locked() + * + * Returns: any poll events that are ready without sleeping + */ +static unsigned int i915_perf_poll(struct file *file, poll_table *wait) +{ + struct i915_perf_stream *stream = file->private_data; + struct drm_i915_private *dev_priv = stream->dev_priv; + int ret; + + mutex_lock(&dev_priv->perf.lock); + ret = i915_perf_poll_locked(dev_priv, stream, file, wait); + mutex_unlock(&dev_priv->perf.lock); + + return ret; +} + +/** + * i915_perf_enable_locked - handle `I915_PERF_IOCTL_ENABLE` ioctl + * @stream: A disabled i915 perf stream + * + * [Re]enables the associated capture of data for this stream. + * + * If a stream was previously enabled then there's currently no intention + * to provide userspace any guarantee about the preservation of previously + * buffered data. + */ +static void i915_perf_enable_locked(struct i915_perf_stream *stream) +{ + if (stream->enabled) + return; + + /* Allow stream->ops->enable() to refer to this */ + stream->enabled = true; + + if (stream->ops->enable) + stream->ops->enable(stream); +} + +/** + * i915_perf_disable_locked - handle `I915_PERF_IOCTL_DISABLE` ioctl + * @stream: An enabled i915 perf stream + * + * Disables the associated capture of data for this stream. + * + * The intention is that disabling an re-enabling a stream will ideally be + * cheaper than destroying and re-opening a stream with the same configuration, + * though there are no formal guarantees about what state or buffered data + * must be retained between disabling and re-enabling a stream. + * + * Note: while a stream is disabled it's considered an error for userspace + * to attempt to read from the stream (-EIO). + */ +static void i915_perf_disable_locked(struct i915_perf_stream *stream) +{ + if (!stream->enabled) + return; + + /* Allow stream->ops->disable() to refer to this */ + stream->enabled = false; + + if (stream->ops->disable) + stream->ops->disable(stream); +} + +/** + * i915_perf_ioctl - support ioctl() usage with i915 perf stream FDs + * @stream: An i915 perf stream + * @cmd: the ioctl request + * @arg: the ioctl data + * + * Note: The &drm_i915_private->perf.lock mutex has been taken to serialize + * with any non-file-operation driver hooks. + * + * Returns: zero on success or a negative error code. Returns -EINVAL for + * an unknown ioctl request. + */ +static long i915_perf_ioctl_locked(struct i915_perf_stream *stream, + unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case I915_PERF_IOCTL_ENABLE: + i915_perf_enable_locked(stream); + return 0; + case I915_PERF_IOCTL_DISABLE: + i915_perf_disable_locked(stream); + return 0; + } + + return -EINVAL; +} + +/** + * i915_perf_ioctl - support ioctl() usage with i915 perf stream FDs + * @file: An i915 perf stream file + * @cmd: the ioctl request + * @arg: the ioctl data + * + * Implementation deferred to i915_perf_ioctl_locked(). + * + * Returns: zero on success or a negative error code. Returns -EINVAL for + * an unknown ioctl request. + */ +static long i915_perf_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct i915_perf_stream *stream = file->private_data; + struct drm_i915_private *dev_priv = stream->dev_priv; + long ret; + + mutex_lock(&dev_priv->perf.lock); + ret = i915_perf_ioctl_locked(stream, cmd, arg); + mutex_unlock(&dev_priv->perf.lock); + + return ret; +} + +/** + * i915_perf_destroy_locked - destroy an i915 perf stream + * @stream: An i915 perf stream + * + * Frees all resources associated with the given i915 perf @stream, disabling + * any associated data capture in the process. + * + * Note: The &drm_i915_private->perf.lock mutex has been taken to serialize + * with any non-file-operation driver hooks. + */ +static void i915_perf_destroy_locked(struct i915_perf_stream *stream) +{ + if (stream->enabled) + i915_perf_disable_locked(stream); + + if (stream->ops->destroy) + stream->ops->destroy(stream); + + list_del(&stream->link); + + if (stream->ctx) + i915_gem_context_put_unlocked(stream->ctx); + + kfree(stream); +} + +/** + * i915_perf_release - handles userspace close() of a stream file + * @inode: anonymous inode associated with file + * @file: An i915 perf stream file + * + * Cleans up any resources associated with an open i915 perf stream file. + * + * NB: close() can't really fail from the userspace point of view. + * + * Returns: zero on success or a negative error code. + */ +static int i915_perf_release(struct inode *inode, struct file *file) +{ + struct i915_perf_stream *stream = file->private_data; + struct drm_i915_private *dev_priv = stream->dev_priv; + + mutex_lock(&dev_priv->perf.lock); + i915_perf_destroy_locked(stream); + mutex_unlock(&dev_priv->perf.lock); + + return 0; +} + + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .release = i915_perf_release, + .poll = i915_perf_poll, + .read = i915_perf_read, + .unlocked_ioctl = i915_perf_ioctl, +}; + + +static struct i915_gem_context * +lookup_context(struct drm_i915_private *dev_priv, + struct drm_i915_file_private *file_priv, + u32 ctx_user_handle) +{ + struct i915_gem_context *ctx; + int ret; + + ret = i915_mutex_lock_interruptible(&dev_priv->drm); + if (ret) + return ERR_PTR(ret); + + ctx = i915_gem_context_lookup(file_priv, ctx_user_handle); + if (!IS_ERR(ctx)) + i915_gem_context_get(ctx); + + mutex_unlock(&dev_priv->drm.struct_mutex); + + return ctx; +} + +/** + * i915_perf_open_ioctl_locked - DRM ioctl() for userspace to open a stream FD + * @dev_priv: i915 device instance + * @param: The open parameters passed to 'DRM_I915_PERF_OPEN` + * @props: individually validated u64 property value pairs + * @file: drm file + * + * See i915_perf_ioctl_open() for interface details. + * + * Implements further stream config validation and stream initialization on + * behalf of i915_perf_open_ioctl() with the &drm_i915_private->perf.lock mutex + * taken to serialize with any non-file-operation driver hooks. + * + * Note: at this point the @props have only been validated in isolation and + * it's still necessary to validate that the combination of properties makes + * sense. + * + * In the case where userspace is interested in OA unit metrics then further + * config validation and stream initialization details will be handled by + * i915_oa_stream_init(). The code here should only validate config state that + * will be relevant to all stream types / backends. + * + * Returns: zero on success or a negative error code. + */ +static int +i915_perf_open_ioctl_locked(struct drm_i915_private *dev_priv, + struct drm_i915_perf_open_param *param, + struct perf_open_properties *props, + struct drm_file *file) +{ + struct i915_gem_context *specific_ctx = NULL; + struct i915_perf_stream *stream = NULL; + unsigned long f_flags = 0; + int stream_fd; + int ret; + + if (props->single_context) { + u32 ctx_handle = props->ctx_handle; + struct drm_i915_file_private *file_priv = file->driver_priv; + + specific_ctx = lookup_context(dev_priv, file_priv, ctx_handle); + if (IS_ERR(specific_ctx)) { + ret = PTR_ERR(specific_ctx); + if (ret != -EINTR) + DRM_DEBUG("Failed to look up context with ID %u for opening perf stream\n", + ctx_handle); + goto err; + } + } + + /* Similar to perf's kernel.perf_paranoid_cpu sysctl option + * we check a dev.i915.perf_stream_paranoid sysctl option + * to determine if it's ok to access system wide OA counters + * without CAP_SYS_ADMIN privileges. + */ + if (!specific_ctx && + i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) { + DRM_DEBUG("Insufficient privileges to open system-wide i915 perf stream\n"); + ret = -EACCES; + goto err_ctx; + } + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) { + ret = -ENOMEM; + goto err_ctx; + } + + stream->dev_priv = dev_priv; + stream->ctx = specific_ctx; + + ret = i915_oa_stream_init(stream, param, props); + if (ret) + goto err_alloc; + + /* we avoid simply assigning stream->sample_flags = props->sample_flags + * to have _stream_init check the combination of sample flags more + * thoroughly, but still this is the expected result at this point. + */ + if (WARN_ON(stream->sample_flags != props->sample_flags)) { + ret = -ENODEV; + goto err_alloc; + } + + list_add(&stream->link, &dev_priv->perf.streams); + + if (param->flags & I915_PERF_FLAG_FD_CLOEXEC) + f_flags |= O_CLOEXEC; + if (param->flags & I915_PERF_FLAG_FD_NONBLOCK) + f_flags |= O_NONBLOCK; + + stream_fd = anon_inode_getfd("[i915_perf]", &fops, stream, f_flags); + if (stream_fd < 0) { + ret = stream_fd; + goto err_open; + } + + if (!(param->flags & I915_PERF_FLAG_DISABLED)) + i915_perf_enable_locked(stream); + + return stream_fd; + +err_open: + list_del(&stream->link); + if (stream->ops->destroy) + stream->ops->destroy(stream); +err_alloc: + kfree(stream); +err_ctx: + if (specific_ctx) + i915_gem_context_put_unlocked(specific_ctx); +err: + return ret; +} + +/** + * read_properties_unlocked - validate + copy userspace stream open properties + * @dev_priv: i915 device instance + * @uprops: The array of u64 key value pairs given by userspace + * @n_props: The number of key value pairs expected in @uprops + * @props: The stream configuration built up while validating properties + * + * Note this function only validates properties in isolation it doesn't + * validate that the combination of properties makes sense or that all + * properties necessary for a particular kind of stream have been set. + * + * Note that there currently aren't any ordering requirements for properties so + * we shouldn't validate or assume anything about ordering here. This doesn't + * rule out defining new properties with ordering requirements in the future. + */ +static int read_properties_unlocked(struct drm_i915_private *dev_priv, + u64 __user *uprops, + u32 n_props, + struct perf_open_properties *props) +{ + u64 __user *uprop = uprops; + int i; + + memset(props, 0, sizeof(struct perf_open_properties)); + + if (!n_props) { + DRM_DEBUG("No i915 perf properties given\n"); + return -EINVAL; + } + + /* Considering that ID = 0 is reserved and assuming that we don't + * (currently) expect any configurations to ever specify duplicate + * values for a particular property ID then the last _PROP_MAX value is + * one greater than the maximum number of properties we expect to get + * from userspace. + */ + if (n_props >= DRM_I915_PERF_PROP_MAX) { + DRM_DEBUG("More i915 perf properties specified than exist\n"); + return -EINVAL; + } + + for (i = 0; i < n_props; i++) { + u64 oa_period, oa_freq_hz; + u64 id, value; + int ret; + + ret = get_user(id, uprop); + if (ret) + return ret; + + ret = get_user(value, uprop + 1); + if (ret) + return ret; + + switch ((enum drm_i915_perf_property_id)id) { + case DRM_I915_PERF_PROP_CTX_HANDLE: + props->single_context = 1; + props->ctx_handle = value; + break; + case DRM_I915_PERF_PROP_SAMPLE_OA: + props->sample_flags |= SAMPLE_OA_REPORT; + break; + case DRM_I915_PERF_PROP_OA_METRICS_SET: + if (value == 0 || + value > dev_priv->perf.oa.n_builtin_sets) { + DRM_DEBUG("Unknown OA metric set ID\n"); + return -EINVAL; + } + props->metrics_set = value; + break; + case DRM_I915_PERF_PROP_OA_FORMAT: + if (value == 0 || value >= I915_OA_FORMAT_MAX) { + DRM_DEBUG("Invalid OA report format\n"); + return -EINVAL; + } + if (!dev_priv->perf.oa.oa_formats[value].size) { + DRM_DEBUG("Invalid OA report format\n"); + return -EINVAL; + } + props->oa_format = value; + break; + case DRM_I915_PERF_PROP_OA_EXPONENT: + if (value > OA_EXPONENT_MAX) { + DRM_DEBUG("OA timer exponent too high (> %u)\n", + OA_EXPONENT_MAX); + return -EINVAL; + } + + /* Theoretically we can program the OA unit to sample + * every 160ns but don't allow that by default unless + * root. + * + * On Haswell the period is derived from the exponent + * as: + * + * period = 80ns * 2^(exponent + 1) + */ + BUILD_BUG_ON(sizeof(oa_period) != 8); + oa_period = 80ull * (2ull << value); + + /* This check is primarily to ensure that oa_period <= + * UINT32_MAX (before passing to do_div which only + * accepts a u32 denominator), but we can also skip + * checking anything < 1Hz which implicitly can't be + * limited via an integer oa_max_sample_rate. + */ + if (oa_period <= NSEC_PER_SEC) { + u64 tmp = NSEC_PER_SEC; + do_div(tmp, oa_period); + oa_freq_hz = tmp; + } else + oa_freq_hz = 0; + + if (oa_freq_hz > i915_oa_max_sample_rate && + !capable(CAP_SYS_ADMIN)) { + DRM_DEBUG("OA exponent would exceed the max sampling frequency (sysctl dev.i915.oa_max_sample_rate) %uHz without root privileges\n", + i915_oa_max_sample_rate); + return -EACCES; + } + + props->oa_periodic = true; + props->oa_period_exponent = value; + break; + default: + MISSING_CASE(id); + DRM_DEBUG("Unknown i915 perf property ID\n"); + return -EINVAL; + } + + uprop += 2; + } + + return 0; +} + +/** + * i915_perf_open_ioctl - DRM ioctl() for userspace to open a stream FD + * @dev: drm device + * @data: ioctl data copied from userspace (unvalidated) + * @file: drm file + * + * Validates the stream open parameters given by userspace including flags + * and an array of u64 key, value pair properties. + * + * Very little is assumed up front about the nature of the stream being + * opened (for instance we don't assume it's for periodic OA unit metrics). An + * i915-perf stream is expected to be a suitable interface for other forms of + * buffered data written by the GPU besides periodic OA metrics. + * + * Note we copy the properties from userspace outside of the i915 perf + * mutex to avoid an awkward lockdep with mmap_sem. + * + * Most of the implementation details are handled by + * i915_perf_open_ioctl_locked() after taking the &drm_i915_private->perf.lock + * mutex for serializing with any non-file-operation driver hooks. + * + * Return: A newly opened i915 Perf stream file descriptor or negative + * error code on failure. + */ +int i915_perf_open_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_perf_open_param *param = data; + struct perf_open_properties props; + u32 known_open_flags; + int ret; + + if (!dev_priv->perf.initialized) { + DRM_DEBUG("i915 perf interface not available for this system\n"); + return -ENOTSUPP; + } + + known_open_flags = I915_PERF_FLAG_FD_CLOEXEC | + I915_PERF_FLAG_FD_NONBLOCK | + I915_PERF_FLAG_DISABLED; + if (param->flags & ~known_open_flags) { + DRM_DEBUG("Unknown drm_i915_perf_open_param flag\n"); + return -EINVAL; + } + + ret = read_properties_unlocked(dev_priv, + u64_to_user_ptr(param->properties_ptr), + param->num_properties, + &props); + if (ret) + return ret; + + mutex_lock(&dev_priv->perf.lock); + ret = i915_perf_open_ioctl_locked(dev_priv, param, &props, file); + mutex_unlock(&dev_priv->perf.lock); + + return ret; +} + +/** + * i915_perf_register - exposes i915-perf to userspace + * @dev_priv: i915 device instance + * + * In particular OA metric sets are advertised under a sysfs metrics/ + * directory allowing userspace to enumerate valid IDs that can be + * used to open an i915-perf stream. + */ +void i915_perf_register(struct drm_i915_private *dev_priv) +{ + if (!IS_HASWELL(dev_priv)) + return; + + if (!dev_priv->perf.initialized) + return; + + /* To be sure we're synchronized with an attempted + * i915_perf_open_ioctl(); considering that we register after + * being exposed to userspace. + */ + mutex_lock(&dev_priv->perf.lock); + + dev_priv->perf.metrics_kobj = + kobject_create_and_add("metrics", + &dev_priv->drm.primary->kdev->kobj); + if (!dev_priv->perf.metrics_kobj) + goto exit; + + if (i915_perf_register_sysfs_hsw(dev_priv)) { + kobject_put(dev_priv->perf.metrics_kobj); + dev_priv->perf.metrics_kobj = NULL; + } + +exit: + mutex_unlock(&dev_priv->perf.lock); +} + +/** + * i915_perf_unregister - hide i915-perf from userspace + * @dev_priv: i915 device instance + * + * i915-perf state cleanup is split up into an 'unregister' and + * 'deinit' phase where the interface is first hidden from + * userspace by i915_perf_unregister() before cleaning up + * remaining state in i915_perf_fini(). + */ +void i915_perf_unregister(struct drm_i915_private *dev_priv) +{ + if (!IS_HASWELL(dev_priv)) + return; + + if (!dev_priv->perf.metrics_kobj) + return; + + i915_perf_unregister_sysfs_hsw(dev_priv); + + kobject_put(dev_priv->perf.metrics_kobj); + dev_priv->perf.metrics_kobj = NULL; +} + +static struct ctl_table oa_table[] = { + { + .procname = "perf_stream_paranoid", + .data = &i915_perf_stream_paranoid, + .maxlen = sizeof(i915_perf_stream_paranoid), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + { + .procname = "oa_max_sample_rate", + .data = &i915_oa_max_sample_rate, + .maxlen = sizeof(i915_oa_max_sample_rate), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &oa_sample_rate_hard_limit, + }, + {} +}; + +static struct ctl_table i915_root[] = { + { + .procname = "i915", + .maxlen = 0, + .mode = 0555, + .child = oa_table, + }, + {} +}; + +static struct ctl_table dev_root[] = { + { + .procname = "dev", + .maxlen = 0, + .mode = 0555, + .child = i915_root, + }, + {} +}; + +/** + * i915_perf_init - initialize i915-perf state on module load + * @dev_priv: i915 device instance + * + * Initializes i915-perf state without exposing anything to userspace. + * + * Note: i915-perf initialization is split into an 'init' and 'register' + * phase with the i915_perf_register() exposing state to userspace. + */ +void i915_perf_init(struct drm_i915_private *dev_priv) +{ + if (!IS_HASWELL(dev_priv)) + return; + + hrtimer_init(&dev_priv->perf.oa.poll_check_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + dev_priv->perf.oa.poll_check_timer.function = oa_poll_check_timer_cb; + init_waitqueue_head(&dev_priv->perf.oa.poll_wq); + + INIT_LIST_HEAD(&dev_priv->perf.streams); + mutex_init(&dev_priv->perf.lock); + spin_lock_init(&dev_priv->perf.hook_lock); + + dev_priv->perf.oa.ops.init_oa_buffer = gen7_init_oa_buffer; + dev_priv->perf.oa.ops.enable_metric_set = hsw_enable_metric_set; + dev_priv->perf.oa.ops.disable_metric_set = hsw_disable_metric_set; + dev_priv->perf.oa.ops.oa_enable = gen7_oa_enable; + dev_priv->perf.oa.ops.oa_disable = gen7_oa_disable; + dev_priv->perf.oa.ops.read = gen7_oa_read; + dev_priv->perf.oa.ops.oa_buffer_is_empty = + gen7_oa_buffer_is_empty_fop_unlocked; + + dev_priv->perf.oa.timestamp_frequency = 12500000; + + dev_priv->perf.oa.oa_formats = hsw_oa_formats; + + dev_priv->perf.oa.n_builtin_sets = + i915_oa_n_builtin_metric_sets_hsw; + + dev_priv->perf.sysctl_header = register_sysctl_table(dev_root); + + dev_priv->perf.initialized = true; +} + +/** + * i915_perf_fini - Counter part to i915_perf_init() + * @dev_priv: i915 device instance + */ +void i915_perf_fini(struct drm_i915_private *dev_priv) +{ + if (!dev_priv->perf.initialized) + return; + + unregister_sysctl_table(dev_priv->perf.sysctl_header); + + memset(&dev_priv->perf.oa.ops, 0, sizeof(dev_priv->perf.oa.ops)); + dev_priv->perf.initialized = false; +} diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index c70c07a7b586..1c8f5b9a7fcd 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -62,6 +62,9 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define _PORT3(port, a, b, c) ((port) == PORT_A ? (a) : \ (port) == PORT_B ? (b) : (c)) #define _MMIO_PORT3(pipe, a, b, c) _MMIO(_PORT3(pipe, a, b, c)) +#define _PHY3(phy, a, b, c) ((phy) == DPIO_PHY0 ? (a) : \ + (phy) == DPIO_PHY1 ? (b) : (c)) +#define _MMIO_PHY3(phy, a, b, c) _MMIO(_PHY3(phy, a, b, c)) #define _MASKED_FIELD(mask, value) ({ \ if (__builtin_constant_p(mask)) \ @@ -107,6 +110,10 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define GRDOM_RESET_STATUS (1 << 1) #define GRDOM_RESET_ENABLE (1 << 0) +/* BSpec only has register offset, PCI device and bit found empirically */ +#define I830_CLOCK_GATE 0xc8 /* device 0 */ +#define I830_L2_CACHE_CLOCK_GATE_DISABLE (1 << 2) + #define GCDGMBUS 0xcc #define GCFGC2 0xda @@ -294,7 +301,6 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) * Instruction field definitions used by the command parser */ #define INSTR_CLIENT_SHIFT 29 -#define INSTR_CLIENT_MASK 0xE0000000 #define INSTR_MI_CLIENT 0x0 #define INSTR_BC_CLIENT 0x2 #define INSTR_RC_CLIENT 0x3 @@ -615,7 +621,344 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define HSW_CS_GPR(n) _MMIO(0x2600 + (n) * 8) #define HSW_CS_GPR_UDW(n) _MMIO(0x2600 + (n) * 8 + 4) -#define OACONTROL _MMIO(0x2360) +#define GEN7_OACONTROL _MMIO(0x2360) +#define GEN7_OACONTROL_CTX_MASK 0xFFFFF000 +#define GEN7_OACONTROL_TIMER_PERIOD_MASK 0x3F +#define GEN7_OACONTROL_TIMER_PERIOD_SHIFT 6 +#define GEN7_OACONTROL_TIMER_ENABLE (1<<5) +#define GEN7_OACONTROL_FORMAT_A13 (0<<2) +#define GEN7_OACONTROL_FORMAT_A29 (1<<2) +#define GEN7_OACONTROL_FORMAT_A13_B8_C8 (2<<2) +#define GEN7_OACONTROL_FORMAT_A29_B8_C8 (3<<2) +#define GEN7_OACONTROL_FORMAT_B4_C8 (4<<2) +#define GEN7_OACONTROL_FORMAT_A45_B8_C8 (5<<2) +#define GEN7_OACONTROL_FORMAT_B4_C8_A16 (6<<2) +#define GEN7_OACONTROL_FORMAT_C4_B8 (7<<2) +#define GEN7_OACONTROL_FORMAT_SHIFT 2 +#define GEN7_OACONTROL_PER_CTX_ENABLE (1<<1) +#define GEN7_OACONTROL_ENABLE (1<<0) + +#define GEN8_OACTXID _MMIO(0x2364) + +#define GEN8_OACONTROL _MMIO(0x2B00) +#define GEN8_OA_REPORT_FORMAT_A12 (0<<2) +#define GEN8_OA_REPORT_FORMAT_A12_B8_C8 (2<<2) +#define GEN8_OA_REPORT_FORMAT_A36_B8_C8 (5<<2) +#define GEN8_OA_REPORT_FORMAT_C4_B8 (7<<2) +#define GEN8_OA_REPORT_FORMAT_SHIFT 2 +#define GEN8_OA_SPECIFIC_CONTEXT_ENABLE (1<<1) +#define GEN8_OA_COUNTER_ENABLE (1<<0) + +#define GEN8_OACTXCONTROL _MMIO(0x2360) +#define GEN8_OA_TIMER_PERIOD_MASK 0x3F +#define GEN8_OA_TIMER_PERIOD_SHIFT 2 +#define GEN8_OA_TIMER_ENABLE (1<<1) +#define GEN8_OA_COUNTER_RESUME (1<<0) + +#define GEN7_OABUFFER _MMIO(0x23B0) /* R/W */ +#define GEN7_OABUFFER_OVERRUN_DISABLE (1<<3) +#define GEN7_OABUFFER_EDGE_TRIGGER (1<<2) +#define GEN7_OABUFFER_STOP_RESUME_ENABLE (1<<1) +#define GEN7_OABUFFER_RESUME (1<<0) + +#define GEN8_OABUFFER _MMIO(0x2b14) + +#define GEN7_OASTATUS1 _MMIO(0x2364) +#define GEN7_OASTATUS1_TAIL_MASK 0xffffffc0 +#define GEN7_OASTATUS1_COUNTER_OVERFLOW (1<<2) +#define GEN7_OASTATUS1_OABUFFER_OVERFLOW (1<<1) +#define GEN7_OASTATUS1_REPORT_LOST (1<<0) + +#define GEN7_OASTATUS2 _MMIO(0x2368) +#define GEN7_OASTATUS2_HEAD_MASK 0xffffffc0 + +#define GEN8_OASTATUS _MMIO(0x2b08) +#define GEN8_OASTATUS_OVERRUN_STATUS (1<<3) +#define GEN8_OASTATUS_COUNTER_OVERFLOW (1<<2) +#define GEN8_OASTATUS_OABUFFER_OVERFLOW (1<<1) +#define GEN8_OASTATUS_REPORT_LOST (1<<0) + +#define GEN8_OAHEADPTR _MMIO(0x2B0C) +#define GEN8_OATAILPTR _MMIO(0x2B10) + +#define OABUFFER_SIZE_128K (0<<3) +#define OABUFFER_SIZE_256K (1<<3) +#define OABUFFER_SIZE_512K (2<<3) +#define OABUFFER_SIZE_1M (3<<3) +#define OABUFFER_SIZE_2M (4<<3) +#define OABUFFER_SIZE_4M (5<<3) +#define OABUFFER_SIZE_8M (6<<3) +#define OABUFFER_SIZE_16M (7<<3) + +#define OA_MEM_SELECT_GGTT (1<<0) + +#define EU_PERF_CNTL0 _MMIO(0xe458) + +#define GDT_CHICKEN_BITS _MMIO(0x9840) +#define GT_NOA_ENABLE 0x00000080 + +/* + * OA Boolean state + */ + +#define OAREPORTTRIG1 _MMIO(0x2740) +#define OAREPORTTRIG1_THRESHOLD_MASK 0xffff +#define OAREPORTTRIG1_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */ + +#define OAREPORTTRIG2 _MMIO(0x2744) +#define OAREPORTTRIG2_INVERT_A_0 (1<<0) +#define OAREPORTTRIG2_INVERT_A_1 (1<<1) +#define OAREPORTTRIG2_INVERT_A_2 (1<<2) +#define OAREPORTTRIG2_INVERT_A_3 (1<<3) +#define OAREPORTTRIG2_INVERT_A_4 (1<<4) +#define OAREPORTTRIG2_INVERT_A_5 (1<<5) +#define OAREPORTTRIG2_INVERT_A_6 (1<<6) +#define OAREPORTTRIG2_INVERT_A_7 (1<<7) +#define OAREPORTTRIG2_INVERT_A_8 (1<<8) +#define OAREPORTTRIG2_INVERT_A_9 (1<<9) +#define OAREPORTTRIG2_INVERT_A_10 (1<<10) +#define OAREPORTTRIG2_INVERT_A_11 (1<<11) +#define OAREPORTTRIG2_INVERT_A_12 (1<<12) +#define OAREPORTTRIG2_INVERT_A_13 (1<<13) +#define OAREPORTTRIG2_INVERT_A_14 (1<<14) +#define OAREPORTTRIG2_INVERT_A_15 (1<<15) +#define OAREPORTTRIG2_INVERT_B_0 (1<<16) +#define OAREPORTTRIG2_INVERT_B_1 (1<<17) +#define OAREPORTTRIG2_INVERT_B_2 (1<<18) +#define OAREPORTTRIG2_INVERT_B_3 (1<<19) +#define OAREPORTTRIG2_INVERT_C_0 (1<<20) +#define OAREPORTTRIG2_INVERT_C_1 (1<<21) +#define OAREPORTTRIG2_INVERT_D_0 (1<<22) +#define OAREPORTTRIG2_THRESHOLD_ENABLE (1<<23) +#define OAREPORTTRIG2_REPORT_TRIGGER_ENABLE (1<<31) + +#define OAREPORTTRIG3 _MMIO(0x2748) +#define OAREPORTTRIG3_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG3_NOA_SELECT_8_SHIFT 0 +#define OAREPORTTRIG3_NOA_SELECT_9_SHIFT 4 +#define OAREPORTTRIG3_NOA_SELECT_10_SHIFT 8 +#define OAREPORTTRIG3_NOA_SELECT_11_SHIFT 12 +#define OAREPORTTRIG3_NOA_SELECT_12_SHIFT 16 +#define OAREPORTTRIG3_NOA_SELECT_13_SHIFT 20 +#define OAREPORTTRIG3_NOA_SELECT_14_SHIFT 24 +#define OAREPORTTRIG3_NOA_SELECT_15_SHIFT 28 + +#define OAREPORTTRIG4 _MMIO(0x274c) +#define OAREPORTTRIG4_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG4_NOA_SELECT_0_SHIFT 0 +#define OAREPORTTRIG4_NOA_SELECT_1_SHIFT 4 +#define OAREPORTTRIG4_NOA_SELECT_2_SHIFT 8 +#define OAREPORTTRIG4_NOA_SELECT_3_SHIFT 12 +#define OAREPORTTRIG4_NOA_SELECT_4_SHIFT 16 +#define OAREPORTTRIG4_NOA_SELECT_5_SHIFT 20 +#define OAREPORTTRIG4_NOA_SELECT_6_SHIFT 24 +#define OAREPORTTRIG4_NOA_SELECT_7_SHIFT 28 + +#define OAREPORTTRIG5 _MMIO(0x2750) +#define OAREPORTTRIG5_THRESHOLD_MASK 0xffff +#define OAREPORTTRIG5_EDGE_LEVEL_TRIGER_SELECT_MASK 0xffff0000 /* 0=level */ + +#define OAREPORTTRIG6 _MMIO(0x2754) +#define OAREPORTTRIG6_INVERT_A_0 (1<<0) +#define OAREPORTTRIG6_INVERT_A_1 (1<<1) +#define OAREPORTTRIG6_INVERT_A_2 (1<<2) +#define OAREPORTTRIG6_INVERT_A_3 (1<<3) +#define OAREPORTTRIG6_INVERT_A_4 (1<<4) +#define OAREPORTTRIG6_INVERT_A_5 (1<<5) +#define OAREPORTTRIG6_INVERT_A_6 (1<<6) +#define OAREPORTTRIG6_INVERT_A_7 (1<<7) +#define OAREPORTTRIG6_INVERT_A_8 (1<<8) +#define OAREPORTTRIG6_INVERT_A_9 (1<<9) +#define OAREPORTTRIG6_INVERT_A_10 (1<<10) +#define OAREPORTTRIG6_INVERT_A_11 (1<<11) +#define OAREPORTTRIG6_INVERT_A_12 (1<<12) +#define OAREPORTTRIG6_INVERT_A_13 (1<<13) +#define OAREPORTTRIG6_INVERT_A_14 (1<<14) +#define OAREPORTTRIG6_INVERT_A_15 (1<<15) +#define OAREPORTTRIG6_INVERT_B_0 (1<<16) +#define OAREPORTTRIG6_INVERT_B_1 (1<<17) +#define OAREPORTTRIG6_INVERT_B_2 (1<<18) +#define OAREPORTTRIG6_INVERT_B_3 (1<<19) +#define OAREPORTTRIG6_INVERT_C_0 (1<<20) +#define OAREPORTTRIG6_INVERT_C_1 (1<<21) +#define OAREPORTTRIG6_INVERT_D_0 (1<<22) +#define OAREPORTTRIG6_THRESHOLD_ENABLE (1<<23) +#define OAREPORTTRIG6_REPORT_TRIGGER_ENABLE (1<<31) + +#define OAREPORTTRIG7 _MMIO(0x2758) +#define OAREPORTTRIG7_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG7_NOA_SELECT_8_SHIFT 0 +#define OAREPORTTRIG7_NOA_SELECT_9_SHIFT 4 +#define OAREPORTTRIG7_NOA_SELECT_10_SHIFT 8 +#define OAREPORTTRIG7_NOA_SELECT_11_SHIFT 12 +#define OAREPORTTRIG7_NOA_SELECT_12_SHIFT 16 +#define OAREPORTTRIG7_NOA_SELECT_13_SHIFT 20 +#define OAREPORTTRIG7_NOA_SELECT_14_SHIFT 24 +#define OAREPORTTRIG7_NOA_SELECT_15_SHIFT 28 + +#define OAREPORTTRIG8 _MMIO(0x275c) +#define OAREPORTTRIG8_NOA_SELECT_MASK 0xf +#define OAREPORTTRIG8_NOA_SELECT_0_SHIFT 0 +#define OAREPORTTRIG8_NOA_SELECT_1_SHIFT 4 +#define OAREPORTTRIG8_NOA_SELECT_2_SHIFT 8 +#define OAREPORTTRIG8_NOA_SELECT_3_SHIFT 12 +#define OAREPORTTRIG8_NOA_SELECT_4_SHIFT 16 +#define OAREPORTTRIG8_NOA_SELECT_5_SHIFT 20 +#define OAREPORTTRIG8_NOA_SELECT_6_SHIFT 24 +#define OAREPORTTRIG8_NOA_SELECT_7_SHIFT 28 + +#define OASTARTTRIG1 _MMIO(0x2710) +#define OASTARTTRIG1_THRESHOLD_COUNT_MASK_MBZ 0xffff0000 +#define OASTARTTRIG1_THRESHOLD_MASK 0xffff + +#define OASTARTTRIG2 _MMIO(0x2714) +#define OASTARTTRIG2_INVERT_A_0 (1<<0) +#define OASTARTTRIG2_INVERT_A_1 (1<<1) +#define OASTARTTRIG2_INVERT_A_2 (1<<2) +#define OASTARTTRIG2_INVERT_A_3 (1<<3) +#define OASTARTTRIG2_INVERT_A_4 (1<<4) +#define OASTARTTRIG2_INVERT_A_5 (1<<5) +#define OASTARTTRIG2_INVERT_A_6 (1<<6) +#define OASTARTTRIG2_INVERT_A_7 (1<<7) +#define OASTARTTRIG2_INVERT_A_8 (1<<8) +#define OASTARTTRIG2_INVERT_A_9 (1<<9) +#define OASTARTTRIG2_INVERT_A_10 (1<<10) +#define OASTARTTRIG2_INVERT_A_11 (1<<11) +#define OASTARTTRIG2_INVERT_A_12 (1<<12) +#define OASTARTTRIG2_INVERT_A_13 (1<<13) +#define OASTARTTRIG2_INVERT_A_14 (1<<14) +#define OASTARTTRIG2_INVERT_A_15 (1<<15) +#define OASTARTTRIG2_INVERT_B_0 (1<<16) +#define OASTARTTRIG2_INVERT_B_1 (1<<17) +#define OASTARTTRIG2_INVERT_B_2 (1<<18) +#define OASTARTTRIG2_INVERT_B_3 (1<<19) +#define OASTARTTRIG2_INVERT_C_0 (1<<20) +#define OASTARTTRIG2_INVERT_C_1 (1<<21) +#define OASTARTTRIG2_INVERT_D_0 (1<<22) +#define OASTARTTRIG2_THRESHOLD_ENABLE (1<<23) +#define OASTARTTRIG2_START_TRIG_FLAG_MBZ (1<<24) +#define OASTARTTRIG2_EVENT_SELECT_0 (1<<28) +#define OASTARTTRIG2_EVENT_SELECT_1 (1<<29) +#define OASTARTTRIG2_EVENT_SELECT_2 (1<<30) +#define OASTARTTRIG2_EVENT_SELECT_3 (1<<31) + +#define OASTARTTRIG3 _MMIO(0x2718) +#define OASTARTTRIG3_NOA_SELECT_MASK 0xf +#define OASTARTTRIG3_NOA_SELECT_8_SHIFT 0 +#define OASTARTTRIG3_NOA_SELECT_9_SHIFT 4 +#define OASTARTTRIG3_NOA_SELECT_10_SHIFT 8 +#define OASTARTTRIG3_NOA_SELECT_11_SHIFT 12 +#define OASTARTTRIG3_NOA_SELECT_12_SHIFT 16 +#define OASTARTTRIG3_NOA_SELECT_13_SHIFT 20 +#define OASTARTTRIG3_NOA_SELECT_14_SHIFT 24 +#define OASTARTTRIG3_NOA_SELECT_15_SHIFT 28 + +#define OASTARTTRIG4 _MMIO(0x271c) +#define OASTARTTRIG4_NOA_SELECT_MASK 0xf +#define OASTARTTRIG4_NOA_SELECT_0_SHIFT 0 +#define OASTARTTRIG4_NOA_SELECT_1_SHIFT 4 +#define OASTARTTRIG4_NOA_SELECT_2_SHIFT 8 +#define OASTARTTRIG4_NOA_SELECT_3_SHIFT 12 +#define OASTARTTRIG4_NOA_SELECT_4_SHIFT 16 +#define OASTARTTRIG4_NOA_SELECT_5_SHIFT 20 +#define OASTARTTRIG4_NOA_SELECT_6_SHIFT 24 +#define OASTARTTRIG4_NOA_SELECT_7_SHIFT 28 + +#define OASTARTTRIG5 _MMIO(0x2720) +#define OASTARTTRIG5_THRESHOLD_COUNT_MASK_MBZ 0xffff0000 +#define OASTARTTRIG5_THRESHOLD_MASK 0xffff + +#define OASTARTTRIG6 _MMIO(0x2724) +#define OASTARTTRIG6_INVERT_A_0 (1<<0) +#define OASTARTTRIG6_INVERT_A_1 (1<<1) +#define OASTARTTRIG6_INVERT_A_2 (1<<2) +#define OASTARTTRIG6_INVERT_A_3 (1<<3) +#define OASTARTTRIG6_INVERT_A_4 (1<<4) +#define OASTARTTRIG6_INVERT_A_5 (1<<5) +#define OASTARTTRIG6_INVERT_A_6 (1<<6) +#define OASTARTTRIG6_INVERT_A_7 (1<<7) +#define OASTARTTRIG6_INVERT_A_8 (1<<8) +#define OASTARTTRIG6_INVERT_A_9 (1<<9) +#define OASTARTTRIG6_INVERT_A_10 (1<<10) +#define OASTARTTRIG6_INVERT_A_11 (1<<11) +#define OASTARTTRIG6_INVERT_A_12 (1<<12) +#define OASTARTTRIG6_INVERT_A_13 (1<<13) +#define OASTARTTRIG6_INVERT_A_14 (1<<14) +#define OASTARTTRIG6_INVERT_A_15 (1<<15) +#define OASTARTTRIG6_INVERT_B_0 (1<<16) +#define OASTARTTRIG6_INVERT_B_1 (1<<17) +#define OASTARTTRIG6_INVERT_B_2 (1<<18) +#define OASTARTTRIG6_INVERT_B_3 (1<<19) +#define OASTARTTRIG6_INVERT_C_0 (1<<20) +#define OASTARTTRIG6_INVERT_C_1 (1<<21) +#define OASTARTTRIG6_INVERT_D_0 (1<<22) +#define OASTARTTRIG6_THRESHOLD_ENABLE (1<<23) +#define OASTARTTRIG6_START_TRIG_FLAG_MBZ (1<<24) +#define OASTARTTRIG6_EVENT_SELECT_4 (1<<28) +#define OASTARTTRIG6_EVENT_SELECT_5 (1<<29) +#define OASTARTTRIG6_EVENT_SELECT_6 (1<<30) +#define OASTARTTRIG6_EVENT_SELECT_7 (1<<31) + +#define OASTARTTRIG7 _MMIO(0x2728) +#define OASTARTTRIG7_NOA_SELECT_MASK 0xf +#define OASTARTTRIG7_NOA_SELECT_8_SHIFT 0 +#define OASTARTTRIG7_NOA_SELECT_9_SHIFT 4 +#define OASTARTTRIG7_NOA_SELECT_10_SHIFT 8 +#define OASTARTTRIG7_NOA_SELECT_11_SHIFT 12 +#define OASTARTTRIG7_NOA_SELECT_12_SHIFT 16 +#define OASTARTTRIG7_NOA_SELECT_13_SHIFT 20 +#define OASTARTTRIG7_NOA_SELECT_14_SHIFT 24 +#define OASTARTTRIG7_NOA_SELECT_15_SHIFT 28 + +#define OASTARTTRIG8 _MMIO(0x272c) +#define OASTARTTRIG8_NOA_SELECT_MASK 0xf +#define OASTARTTRIG8_NOA_SELECT_0_SHIFT 0 +#define OASTARTTRIG8_NOA_SELECT_1_SHIFT 4 +#define OASTARTTRIG8_NOA_SELECT_2_SHIFT 8 +#define OASTARTTRIG8_NOA_SELECT_3_SHIFT 12 +#define OASTARTTRIG8_NOA_SELECT_4_SHIFT 16 +#define OASTARTTRIG8_NOA_SELECT_5_SHIFT 20 +#define OASTARTTRIG8_NOA_SELECT_6_SHIFT 24 +#define OASTARTTRIG8_NOA_SELECT_7_SHIFT 28 + +/* CECX_0 */ +#define OACEC_COMPARE_LESS_OR_EQUAL 6 +#define OACEC_COMPARE_NOT_EQUAL 5 +#define OACEC_COMPARE_LESS_THAN 4 +#define OACEC_COMPARE_GREATER_OR_EQUAL 3 +#define OACEC_COMPARE_EQUAL 2 +#define OACEC_COMPARE_GREATER_THAN 1 +#define OACEC_COMPARE_ANY_EQUAL 0 + +#define OACEC_COMPARE_VALUE_MASK 0xffff +#define OACEC_COMPARE_VALUE_SHIFT 3 + +#define OACEC_SELECT_NOA (0<<19) +#define OACEC_SELECT_PREV (1<<19) +#define OACEC_SELECT_BOOLEAN (2<<19) + +/* CECX_1 */ +#define OACEC_MASK_MASK 0xffff +#define OACEC_CONSIDERATIONS_MASK 0xffff +#define OACEC_CONSIDERATIONS_SHIFT 16 + +#define OACEC0_0 _MMIO(0x2770) +#define OACEC0_1 _MMIO(0x2774) +#define OACEC1_0 _MMIO(0x2778) +#define OACEC1_1 _MMIO(0x277c) +#define OACEC2_0 _MMIO(0x2780) +#define OACEC2_1 _MMIO(0x2784) +#define OACEC3_0 _MMIO(0x2788) +#define OACEC3_1 _MMIO(0x278c) +#define OACEC4_0 _MMIO(0x2790) +#define OACEC4_1 _MMIO(0x2794) +#define OACEC5_0 _MMIO(0x2798) +#define OACEC5_1 _MMIO(0x279c) +#define OACEC6_0 _MMIO(0x27a0) +#define OACEC6_1 _MMIO(0x27a4) +#define OACEC7_0 _MMIO(0x27a8) +#define OACEC7_1 _MMIO(0x27ac) + #define _GEN7_PIPEA_DE_LOAD_SL 0x70068 #define _GEN7_PIPEB_DE_LOAD_SL 0x71068 @@ -708,9 +1051,15 @@ enum skl_disp_power_wells { /* These numbers are fixed and must match the position of the pw bits */ SKL_DISP_PW_MISC_IO, SKL_DISP_PW_DDI_A_E, + GLK_DISP_PW_DDI_A = SKL_DISP_PW_DDI_A_E, SKL_DISP_PW_DDI_B, SKL_DISP_PW_DDI_C, SKL_DISP_PW_DDI_D, + + GLK_DISP_PW_AUX_A = 8, + GLK_DISP_PW_AUX_B, + GLK_DISP_PW_AUX_C, + SKL_DISP_PW_1 = 14, SKL_DISP_PW_2, @@ -720,6 +1069,7 @@ enum skl_disp_power_wells { BXT_DPIO_CMN_A, BXT_DPIO_CMN_BC, + GLK_DPIO_CMN_C, }; #define SKL_POWER_WELL_STATE(pw) (1 << ((pw) * 2)) @@ -1188,8 +1538,10 @@ enum skl_disp_power_wells { /* BXT PHY registers */ #define _BXT_PHY0_BASE 0x6C000 #define _BXT_PHY1_BASE 0x162000 -#define BXT_PHY_BASE(phy) _PIPE((phy), _BXT_PHY0_BASE, \ - _BXT_PHY1_BASE) +#define _BXT_PHY2_BASE 0x163000 +#define BXT_PHY_BASE(phy) _PHY3((phy), _BXT_PHY0_BASE, \ + _BXT_PHY1_BASE, \ + _BXT_PHY2_BASE) #define _BXT_PHY(phy, reg) \ _MMIO(BXT_PHY_BASE(phy) - _BXT_PHY0_BASE + (reg)) @@ -1201,7 +1553,6 @@ enum skl_disp_power_wells { _MMIO(_BXT_PHY_CH(phy, ch, reg_ch0, reg_ch1)) #define BXT_P_CR_GT_DISP_PWRON _MMIO(0x138090) -#define GT_DISPLAY_POWER_ON(phy) (1 << (phy)) #define _BXT_PHY_CTL_DDI_A 0x64C00 #define _BXT_PHY_CTL_DDI_B 0x64C10 @@ -1214,9 +1565,11 @@ enum skl_disp_power_wells { #define _PHY_CTL_FAMILY_EDP 0x64C80 #define _PHY_CTL_FAMILY_DDI 0x64C90 +#define _PHY_CTL_FAMILY_DDI_C 0x64CA0 #define COMMON_RESET_DIS (1 << 31) -#define BXT_PHY_CTL_FAMILY(phy) _MMIO_PIPE((phy), _PHY_CTL_FAMILY_DDI, \ - _PHY_CTL_FAMILY_EDP) +#define BXT_PHY_CTL_FAMILY(phy) _MMIO_PHY3((phy), _PHY_CTL_FAMILY_DDI, \ + _PHY_CTL_FAMILY_EDP, \ + _PHY_CTL_FAMILY_DDI_C) /* BXT PHY PLL registers */ #define _PORT_PLL_A 0x46074 @@ -1225,6 +1578,8 @@ enum skl_disp_power_wells { #define PORT_PLL_ENABLE (1 << 31) #define PORT_PLL_LOCK (1 << 30) #define PORT_PLL_REF_SEL (1 << 27) +#define PORT_PLL_POWER_ENABLE (1 << 26) +#define PORT_PLL_POWER_STATE (1 << 25) #define BXT_PORT_PLL_ENABLE(port) _MMIO_PORT(port, _PORT_PLL_A, _PORT_PLL_B) #define _PORT_PLL_EBB_0_A 0x162034 @@ -1435,6 +1790,21 @@ enum skl_disp_power_wells { #define DEEMPH_SHIFT 24 #define DE_EMPHASIS (0xFF << DEEMPH_SHIFT) +#define _PORT_TX_DW5_LN0_A 0x162514 +#define _PORT_TX_DW5_LN0_B 0x6C514 +#define _PORT_TX_DW5_LN0_C 0x6C914 +#define _PORT_TX_DW5_GRP_A 0x162D14 +#define _PORT_TX_DW5_GRP_B 0x6CD14 +#define _PORT_TX_DW5_GRP_C 0x6CF14 +#define BXT_PORT_TX_DW5_LN0(phy, ch) _MMIO_BXT_PHY_CH(phy, ch, \ + _PORT_TX_DW5_LN0_B, \ + _PORT_TX_DW5_LN0_C) +#define BXT_PORT_TX_DW5_GRP(phy, ch) _MMIO_BXT_PHY_CH(phy, ch, \ + _PORT_TX_DW5_GRP_B, \ + _PORT_TX_DW5_GRP_C) +#define DCC_DELAY_RANGE_1 (1 << 9) +#define DCC_DELAY_RANGE_2 (1 << 8) + #define _PORT_TX_DW14_LN0_A 0x162538 #define _PORT_TX_DW14_LN0_B 0x6C538 #define _PORT_TX_DW14_LN0_C 0x6C938 @@ -2058,6 +2428,22 @@ enum skl_disp_power_wells { #define I915_ASLE_INTERRUPT (1<<0) #define I915_BSD_USER_INTERRUPT (1<<25) +#define I915_HDMI_LPE_AUDIO_BASE (VLV_DISPLAY_BASE + 0x65000) +#define I915_HDMI_LPE_AUDIO_SIZE 0x1000 + +/* DisplayPort Audio w/ LPE */ +#define VLV_AUD_CHICKEN_BIT_REG _MMIO(VLV_DISPLAY_BASE + 0x62F38) +#define VLV_CHICKEN_BIT_DBG_ENABLE (1 << 0) + +#define _VLV_AUD_PORT_EN_B_DBG (VLV_DISPLAY_BASE + 0x62F20) +#define _VLV_AUD_PORT_EN_C_DBG (VLV_DISPLAY_BASE + 0x62F30) +#define _VLV_AUD_PORT_EN_D_DBG (VLV_DISPLAY_BASE + 0x62F34) +#define VLV_AUD_PORT_EN_DBG(port) _MMIO_PORT3((port) - PORT_B, \ + _VLV_AUD_PORT_EN_B_DBG, \ + _VLV_AUD_PORT_EN_C_DBG, \ + _VLV_AUD_PORT_EN_D_DBG) +#define VLV_AMP_MUTE (1 << 1) + #define GEN6_BSD_RNCID _MMIO(0x12198) #define GEN7_FF_THREAD_MODE _MMIO(0x20a0) @@ -2920,7 +3306,7 @@ enum skl_disp_power_wells { #define INTERVAL_1_33_US(us) (((us) * 3) >> 2) #define INTERVAL_0_833_US(us) (((us) * 6) / 5) #define GT_INTERVAL_FROM_US(dev_priv, us) (IS_GEN9(dev_priv) ? \ - (IS_BROXTON(dev_priv) ? \ + (IS_GEN9_LP(dev_priv) ? \ INTERVAL_0_833_US(us) : \ INTERVAL_1_33_US(us)) : \ INTERVAL_1_28_US(us)) @@ -2929,7 +3315,7 @@ enum skl_disp_power_wells { #define INTERVAL_1_33_TO_US(interval) (((interval) << 2) / 3) #define INTERVAL_0_833_TO_US(interval) (((interval) * 5) / 6) #define GT_PM_INTERVAL_TO_US(dev_priv, interval) (IS_GEN9(dev_priv) ? \ - (IS_BROXTON(dev_priv) ? \ + (IS_GEN9_LP(dev_priv) ? \ INTERVAL_0_833_TO_US(interval) : \ INTERVAL_1_33_TO_US(interval)) : \ INTERVAL_1_28_TO_US(interval)) @@ -2937,8 +3323,10 @@ enum skl_disp_power_wells { /* * Logical Context regs */ -#define CCID _MMIO(0x2180) -#define CCID_EN (1<<0) +#define CCID _MMIO(0x2180) +#define CCID_EN BIT(0) +#define CCID_EXTENDED_STATE_RESTORE BIT(2) +#define CCID_EXTENDED_STATE_SAVE BIT(3) /* * Notes on SNB/IVB/VLV context size: * - Power context is saved elsewhere (LLC or stolen) @@ -3227,9 +3615,12 @@ enum { #define EDP_PSR_PERF_CNT_MASK 0xffffff #define EDP_PSR_DEBUG_CTL _MMIO(dev_priv->psr_mmio_base + 0x60) -#define EDP_PSR_DEBUG_MASK_LPSP (1<<27) -#define EDP_PSR_DEBUG_MASK_MEMUP (1<<26) -#define EDP_PSR_DEBUG_MASK_HPD (1<<25) +#define EDP_PSR_DEBUG_MASK_MAX_SLEEP (1<<28) +#define EDP_PSR_DEBUG_MASK_LPSP (1<<27) +#define EDP_PSR_DEBUG_MASK_MEMUP (1<<26) +#define EDP_PSR_DEBUG_MASK_HPD (1<<25) +#define EDP_PSR_DEBUG_MASK_DISP_REG_WRITE (1<<16) +#define EDP_PSR_DEBUG_EXIT_ON_PIXEL_UNDERRUN (1<<15) #define EDP_PSR2_CTL _MMIO(0x6f900) #define EDP_PSR2_ENABLE (1<<31) @@ -3244,6 +3635,11 @@ enum { #define EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4 #define EDP_PSR2_FRAME_BEFORE_SU_MASK (0xf<<4) #define EDP_PSR2_IDLE_MASK 0xf +#define EDP_FRAMES_BEFORE_SU_ENTRY (1<<4) + +#define EDP_PSR2_STATUS_CTL _MMIO(0x6f940) +#define EDP_PSR2_STATUS_STATE_MASK (0xf<<28) +#define EDP_PSR2_STATUS_STATE_SHIFT 28 /* VGA port control */ #define ADPA _MMIO(0x61100) @@ -5374,18 +5770,21 @@ enum { #define _SPBCONSTALPHA (VLV_DISPLAY_BASE + 0x722a8) #define _SPBGAMC (VLV_DISPLAY_BASE + 0x722f4) -#define SPCNTR(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPACNTR, _SPBCNTR) -#define SPLINOFF(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPALINOFF, _SPBLINOFF) -#define SPSTRIDE(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPASTRIDE, _SPBSTRIDE) -#define SPPOS(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPAPOS, _SPBPOS) -#define SPSIZE(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPASIZE, _SPBSIZE) -#define SPKEYMINVAL(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPAKEYMINVAL, _SPBKEYMINVAL) -#define SPKEYMSK(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPAKEYMSK, _SPBKEYMSK) -#define SPSURF(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPASURF, _SPBSURF) -#define SPKEYMAXVAL(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPAKEYMAXVAL, _SPBKEYMAXVAL) -#define SPTILEOFF(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPATILEOFF, _SPBTILEOFF) -#define SPCONSTALPHA(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPACONSTALPHA, _SPBCONSTALPHA) -#define SPGAMC(pipe, plane) _MMIO_PIPE((pipe) * 2 + (plane), _SPAGAMC, _SPBGAMC) +#define _MMIO_VLV_SPR(pipe, plane_id, reg_a, reg_b) \ + _MMIO_PIPE((pipe) * 2 + (plane_id) - PLANE_SPRITE0, (reg_a), (reg_b)) + +#define SPCNTR(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPACNTR, _SPBCNTR) +#define SPLINOFF(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPALINOFF, _SPBLINOFF) +#define SPSTRIDE(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPASTRIDE, _SPBSTRIDE) +#define SPPOS(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPAPOS, _SPBPOS) +#define SPSIZE(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPASIZE, _SPBSIZE) +#define SPKEYMINVAL(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPAKEYMINVAL, _SPBKEYMINVAL) +#define SPKEYMSK(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPAKEYMSK, _SPBKEYMSK) +#define SPSURF(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPASURF, _SPBSURF) +#define SPKEYMAXVAL(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPAKEYMAXVAL, _SPBKEYMAXVAL) +#define SPTILEOFF(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPATILEOFF, _SPBTILEOFF) +#define SPCONSTALPHA(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPACONSTALPHA, _SPBCONSTALPHA) +#define SPGAMC(pipe, plane_id) _MMIO_VLV_SPR((pipe), (plane_id), _SPAGAMC, _SPBGAMC) /* * CHV pipe B sprite CSC @@ -5394,29 +5793,32 @@ enum { * |yg| = |c3 c4 c5| x |yg + yg_ioff| + |yg_ooff| * |cb| |c6 c7 c8| |cb + cr_ioff| |cb_ooff| */ -#define SPCSCYGOFF(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d900 + (sprite) * 0x1000) -#define SPCSCCBOFF(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d904 + (sprite) * 0x1000) -#define SPCSCCROFF(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d908 + (sprite) * 0x1000) +#define _MMIO_CHV_SPCSC(plane_id, reg) \ + _MMIO(VLV_DISPLAY_BASE + ((plane_id) - PLANE_SPRITE0) * 0x1000 + (reg)) + +#define SPCSCYGOFF(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d900) +#define SPCSCCBOFF(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d904) +#define SPCSCCROFF(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d908) #define SPCSC_OOFF(x) (((x) & 0x7ff) << 16) /* s11 */ #define SPCSC_IOFF(x) (((x) & 0x7ff) << 0) /* s11 */ -#define SPCSCC01(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d90c + (sprite) * 0x1000) -#define SPCSCC23(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d910 + (sprite) * 0x1000) -#define SPCSCC45(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d914 + (sprite) * 0x1000) -#define SPCSCC67(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d918 + (sprite) * 0x1000) -#define SPCSCC8(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d91c + (sprite) * 0x1000) +#define SPCSCC01(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d90c) +#define SPCSCC23(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d910) +#define SPCSCC45(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d914) +#define SPCSCC67(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d918) +#define SPCSCC8(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d91c) #define SPCSC_C1(x) (((x) & 0x7fff) << 16) /* s3.12 */ #define SPCSC_C0(x) (((x) & 0x7fff) << 0) /* s3.12 */ -#define SPCSCYGICLAMP(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d920 + (sprite) * 0x1000) -#define SPCSCCBICLAMP(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d924 + (sprite) * 0x1000) -#define SPCSCCRICLAMP(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d928 + (sprite) * 0x1000) +#define SPCSCYGICLAMP(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d920) +#define SPCSCCBICLAMP(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d924) +#define SPCSCCRICLAMP(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d928) #define SPCSC_IMAX(x) (((x) & 0x7ff) << 16) /* s11 */ #define SPCSC_IMIN(x) (((x) & 0x7ff) << 0) /* s11 */ -#define SPCSCYGOCLAMP(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d92c + (sprite) * 0x1000) -#define SPCSCCBOCLAMP(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d930 + (sprite) * 0x1000) -#define SPCSCCROCLAMP(sprite) _MMIO(VLV_DISPLAY_BASE + 0x6d934 + (sprite) * 0x1000) +#define SPCSCYGOCLAMP(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d92c) +#define SPCSCCBOCLAMP(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d930) +#define SPCSCCROCLAMP(plane_id) _MMIO_CHV_SPCSC(plane_id, 0x6d934) #define SPCSC_OMAX(x) ((x) << 16) /* u10 */ #define SPCSC_OMIN(x) ((x) << 0) /* u10 */ @@ -6070,6 +6472,12 @@ enum { #define BDW_DPRS_MASK_VBLANK_SRD (1 << 0) #define CHICKEN_PIPESL_1(pipe) _MMIO_PIPE(pipe, _CHICKEN_PIPESL_1_A, _CHICKEN_PIPESL_1_B) +#define CHICKEN_TRANS_A 0x420c0 +#define CHICKEN_TRANS_B 0x420c4 +#define CHICKEN_TRANS(trans) _MMIO_TRANS(trans, CHICKEN_TRANS_A, CHICKEN_TRANS_B) +#define PSR2_VSC_ENABLE_PROG_HEADER (1<<12) +#define PSR2_ADD_VERTICAL_LINE_COUNT (1<<15) + #define DISP_ARB_CTL _MMIO(0x45000) #define DISP_FBC_MEMORY_WAKE (1<<31) #define DISP_TILE_SURFACE_SWIZZLING (1<<13) @@ -6914,6 +7322,7 @@ enum { # define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11) #define GEN6_UCGCTL3 _MMIO(0x9408) +# define GEN6_OACSUNIT_CLOCK_GATE_DISABLE (1 << 20) #define GEN7_UCGCTL4 _MMIO(0x940c) #define GEN7_L3BANK2X_CLOCK_GATE_DISABLE (1<<25) @@ -8299,6 +8708,21 @@ enum { #define BXT_PIPE_SELECT_SHIFT 7 #define BXT_PIPE_SELECT_MASK (7 << 7) #define BXT_PIPE_SELECT(pipe) ((pipe) << 7) +#define GLK_PHY_STATUS_PORT_READY (1 << 31) /* RO */ +#define GLK_ULPS_NOT_ACTIVE (1 << 30) /* RO */ +#define GLK_MIPIIO_RESET_RELEASED (1 << 28) +#define GLK_CLOCK_LANE_STOP_STATE (1 << 27) /* RO */ +#define GLK_DATA_LANE_STOP_STATE (1 << 26) /* RO */ +#define GLK_LP_WAKE (1 << 22) +#define GLK_LP11_LOW_PWR_MODE (1 << 21) +#define GLK_LP00_LOW_PWR_MODE (1 << 20) +#define GLK_FIREWALL_ENABLE (1 << 16) +#define BXT_PIXEL_OVERLAP_CNT_MASK (0xf << 10) +#define BXT_PIXEL_OVERLAP_CNT_SHIFT 10 +#define BXT_DSC_ENABLE (1 << 3) +#define BXT_RGB_FLIP (1 << 2) +#define GLK_MIPIIO_PORT_POWERED (1 << 1) /* RO */ +#define GLK_MIPIIO_ENABLE (1 << 0) #define _MIPIA_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb108) #define _MIPIC_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb908) diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index b0e1e7ca75da..5c86925a0294 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -56,13 +56,12 @@ static void i915_restore_display(struct drm_i915_private *dev_priv) i915_redisable_vga(dev_priv); } -int i915_save_state(struct drm_device *dev) +int i915_save_state(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct pci_dev *pdev = dev_priv->drm.pdev; int i; - mutex_lock(&dev->struct_mutex); + mutex_lock(&dev_priv->drm.struct_mutex); i915_save_display(dev_priv); @@ -97,18 +96,17 @@ int i915_save_state(struct drm_device *dev) dev_priv->regfile.saveSWF3[i] = I915_READ(SWF3(i)); } - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev_priv->drm.struct_mutex); return 0; } -int i915_restore_state(struct drm_device *dev) +int i915_restore_state(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct pci_dev *pdev = dev_priv->drm.pdev; int i; - mutex_lock(&dev->struct_mutex); + mutex_lock(&dev_priv->drm.struct_mutex); i915_gem_restore_fences(dev_priv); @@ -145,9 +143,9 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE(SWF3(i), dev_priv->regfile.saveSWF3[i]); } - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev_priv->drm.struct_mutex); - intel_i2c_reset(dev); + intel_i2c_reset(dev_priv); return 0; } diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c index 147420ccf49c..40f4e5efaf83 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -17,6 +17,93 @@ static DEFINE_SPINLOCK(i915_sw_fence_lock); +enum { + DEBUG_FENCE_IDLE = 0, + DEBUG_FENCE_NOTIFY, +}; + +#ifdef CONFIG_DRM_I915_SW_FENCE_DEBUG_OBJECTS + +static void *i915_sw_fence_debug_hint(void *addr) +{ + return (void *)(((struct i915_sw_fence *)addr)->flags & I915_SW_FENCE_MASK); +} + +static struct debug_obj_descr i915_sw_fence_debug_descr = { + .name = "i915_sw_fence", + .debug_hint = i915_sw_fence_debug_hint, +}; + +static inline void debug_fence_init(struct i915_sw_fence *fence) +{ + debug_object_init(fence, &i915_sw_fence_debug_descr); +} + +static inline void debug_fence_activate(struct i915_sw_fence *fence) +{ + debug_object_activate(fence, &i915_sw_fence_debug_descr); +} + +static inline void debug_fence_set_state(struct i915_sw_fence *fence, + int old, int new) +{ + debug_object_active_state(fence, &i915_sw_fence_debug_descr, old, new); +} + +static inline void debug_fence_deactivate(struct i915_sw_fence *fence) +{ + debug_object_deactivate(fence, &i915_sw_fence_debug_descr); +} + +static inline void debug_fence_destroy(struct i915_sw_fence *fence) +{ + debug_object_destroy(fence, &i915_sw_fence_debug_descr); +} + +static inline void debug_fence_free(struct i915_sw_fence *fence) +{ + debug_object_free(fence, &i915_sw_fence_debug_descr); + smp_wmb(); /* flush the change in state before reallocation */ +} + +static inline void debug_fence_assert(struct i915_sw_fence *fence) +{ + debug_object_assert_init(fence, &i915_sw_fence_debug_descr); +} + +#else + +static inline void debug_fence_init(struct i915_sw_fence *fence) +{ +} + +static inline void debug_fence_activate(struct i915_sw_fence *fence) +{ +} + +static inline void debug_fence_set_state(struct i915_sw_fence *fence, + int old, int new) +{ +} + +static inline void debug_fence_deactivate(struct i915_sw_fence *fence) +{ +} + +static inline void debug_fence_destroy(struct i915_sw_fence *fence) +{ +} + +static inline void debug_fence_free(struct i915_sw_fence *fence) +{ +} + +static inline void debug_fence_assert(struct i915_sw_fence *fence) +{ +} + +#endif + static int __i915_sw_fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state) { @@ -26,25 +113,37 @@ static int __i915_sw_fence_notify(struct i915_sw_fence *fence, return fn(fence, state); } -static void i915_sw_fence_free(struct kref *kref) +#ifdef CONFIG_DRM_I915_SW_FENCE_DEBUG_OBJECTS +void i915_sw_fence_fini(struct i915_sw_fence *fence) +{ + debug_fence_free(fence); +} +#endif + +static void i915_sw_fence_release(struct kref *kref) { struct i915_sw_fence *fence = container_of(kref, typeof(*fence), kref); WARN_ON(atomic_read(&fence->pending) > 0); + debug_fence_destroy(fence); - if (fence->flags & I915_SW_FENCE_MASK) + if (fence->flags & I915_SW_FENCE_MASK) { __i915_sw_fence_notify(fence, FENCE_FREE); - else + } else { + i915_sw_fence_fini(fence); kfree(fence); + } } static void i915_sw_fence_put(struct i915_sw_fence *fence) { - kref_put(&fence->kref, i915_sw_fence_free); + debug_fence_assert(fence); + kref_put(&fence->kref, i915_sw_fence_release); } static struct i915_sw_fence *i915_sw_fence_get(struct i915_sw_fence *fence) { + debug_fence_assert(fence); kref_get(&fence->kref); return fence; } @@ -56,6 +155,7 @@ static void __i915_sw_fence_wake_up_all(struct i915_sw_fence *fence, wait_queue_t *pos, *next; unsigned long flags; + debug_fence_deactivate(fence); atomic_set_release(&fence->pending, -1); /* 0 -> -1 [done] */ /* @@ -88,23 +188,33 @@ static void __i915_sw_fence_wake_up_all(struct i915_sw_fence *fence, } while (1); } spin_unlock_irqrestore(&x->lock, flags); + + debug_fence_assert(fence); } static void __i915_sw_fence_complete(struct i915_sw_fence *fence, struct list_head *continuation) { + debug_fence_assert(fence); + if (!atomic_dec_and_test(&fence->pending)) return; + debug_fence_set_state(fence, DEBUG_FENCE_IDLE, DEBUG_FENCE_NOTIFY); + if (fence->flags & I915_SW_FENCE_MASK && __i915_sw_fence_notify(fence, FENCE_COMPLETE) != NOTIFY_DONE) return; + debug_fence_set_state(fence, DEBUG_FENCE_NOTIFY, DEBUG_FENCE_IDLE); + __i915_sw_fence_wake_up_all(fence, continuation); } static void i915_sw_fence_complete(struct i915_sw_fence *fence) { + debug_fence_assert(fence); + if (WARN_ON(i915_sw_fence_done(fence))) return; @@ -113,6 +223,7 @@ static void i915_sw_fence_complete(struct i915_sw_fence *fence) static void i915_sw_fence_await(struct i915_sw_fence *fence) { + debug_fence_assert(fence); WARN_ON(atomic_inc_return(&fence->pending) <= 1); } @@ -123,18 +234,26 @@ void __i915_sw_fence_init(struct i915_sw_fence *fence, { BUG_ON((unsigned long)fn & ~I915_SW_FENCE_MASK); + debug_fence_init(fence); + __init_waitqueue_head(&fence->wait, name, key); kref_init(&fence->kref); atomic_set(&fence->pending, 1); fence->flags = (unsigned long)fn; } -void i915_sw_fence_commit(struct i915_sw_fence *fence) +static void __i915_sw_fence_commit(struct i915_sw_fence *fence) { i915_sw_fence_complete(fence); i915_sw_fence_put(fence); } +void i915_sw_fence_commit(struct i915_sw_fence *fence) +{ + debug_fence_activate(fence); + __i915_sw_fence_commit(fence); +} + static int i915_sw_fence_wake(wait_queue_t *wq, unsigned mode, int flags, void *key) { list_del(&wq->task_list); @@ -206,9 +325,13 @@ static int __i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence, unsigned long flags; int pending; + debug_fence_assert(fence); + if (i915_sw_fence_done(signaler)) return 0; + debug_fence_assert(signaler); + /* The dependency graph must be acyclic. */ if (unlikely(i915_sw_fence_check_if_after(fence, signaler))) return -EINVAL; @@ -279,7 +402,7 @@ static void timer_i915_sw_fence_wake(unsigned long data) dma_fence_put(cb->dma); cb->dma = NULL; - i915_sw_fence_commit(cb->fence); + __i915_sw_fence_commit(cb->fence); cb->timer.function = NULL; } @@ -290,7 +413,7 @@ static void dma_i915_sw_fence_wake(struct dma_fence *dma, del_timer_sync(&cb->timer); if (cb->timer.function) - i915_sw_fence_commit(cb->fence); + __i915_sw_fence_commit(cb->fence); dma_fence_put(cb->dma); kfree(cb); @@ -304,6 +427,8 @@ int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence, struct i915_sw_dma_fence_cb *cb; int ret; + debug_fence_assert(fence); + if (dma_fence_is_signaled(dma)) return 0; @@ -349,6 +474,8 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, struct dma_fence *excl; int ret = 0, pending; + debug_fence_assert(fence); + if (write) { struct dma_fence **shared; unsigned int count, i; diff --git a/drivers/gpu/drm/i915/i915_sw_fence.h b/drivers/gpu/drm/i915/i915_sw_fence.h index 0f3185ef7f4e..d31cefbbcc04 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.h +++ b/drivers/gpu/drm/i915/i915_sw_fence.h @@ -56,6 +56,12 @@ do { \ __i915_sw_fence_init((fence), (fn), NULL, NULL) #endif +#ifdef CONFIG_DRM_I915_SW_FENCE_DEBUG_OBJECTS +void i915_sw_fence_fini(struct i915_sw_fence *fence); +#else +static inline void i915_sw_fence_fini(struct i915_sw_fence *fence) {} +#endif + void i915_sw_fence_commit(struct i915_sw_fence *fence); int i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence, diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 3df8d3dd31cd..376ac957cd1c 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -58,7 +58,7 @@ static u32 calc_residency(struct drm_i915_private *dev_priv, if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH) units <<= 8; - } else if (IS_BROXTON(dev_priv)) { + } else if (IS_GEN9_LP(dev_priv)) { units = 1; div = 1200; /* 833.33ns */ } @@ -535,7 +535,7 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj, if (ret) return ret; - error_priv.dev = dev; + error_priv.i915 = dev_priv; i915_error_state_get(dev, &error_priv); ret = i915_error_state_to_str(&error_str, &error_priv); @@ -560,7 +560,7 @@ static ssize_t error_state_write(struct file *file, struct kobject *kobj, struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev); DRM_DEBUG_DRIVER("Resetting error state\n"); - i915_destroy_error_state(&dev_priv->drm); + i915_destroy_error_state(dev_priv); return count; } diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index c5d210ebaa9a..4461df5a94fe 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -406,7 +406,7 @@ TRACE_EVENT(i915_gem_evict, ), TP_fast_assign( - __entry->dev = vm->dev->primary->index; + __entry->dev = vm->i915->drm.primary->index; __entry->vm = vm; __entry->size = size; __entry->align = align; @@ -443,13 +443,41 @@ TRACE_EVENT(i915_gem_evict_vm, ), TP_fast_assign( - __entry->dev = vm->dev->primary->index; + __entry->dev = vm->i915->drm.primary->index; __entry->vm = vm; ), TP_printk("dev=%d, vm=%p", __entry->dev, __entry->vm) ); +TRACE_EVENT(i915_gem_evict_node, + TP_PROTO(struct i915_address_space *vm, struct drm_mm_node *node, unsigned int flags), + TP_ARGS(vm, node, flags), + + TP_STRUCT__entry( + __field(u32, dev) + __field(struct i915_address_space *, vm) + __field(u64, start) + __field(u64, size) + __field(unsigned long, color) + __field(unsigned int, flags) + ), + + TP_fast_assign( + __entry->dev = vm->i915->drm.primary->index; + __entry->vm = vm; + __entry->start = node->start; + __entry->size = node->size; + __entry->color = node->color; + __entry->flags = flags; + ), + + TP_printk("dev=%d, vm=%p, start=%llx size=%llx, color=%lx, flags=%x", + __entry->dev, __entry->vm, + __entry->start, __entry->size, + __entry->color, __entry->flags) +); + TRACE_EVENT(i915_gem_ring_sync_to, TP_PROTO(struct drm_i915_gem_request *to, struct drm_i915_gem_request *from), @@ -711,7 +739,7 @@ DECLARE_EVENT_CLASS(i915_ppgtt, TP_fast_assign( __entry->vm = vm; - __entry->dev = vm->dev->primary->index; + __entry->dev = vm->i915->drm.primary->index; ), TP_printk("dev=%u, vm=%p", __entry->dev, __entry->vm) diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h new file mode 100644 index 000000000000..34020873e1f6 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_utils.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __I915_UTILS_H +#define __I915_UTILS_H + +#define range_overflows(start, size, max) ({ \ + typeof(start) start__ = (start); \ + typeof(size) size__ = (size); \ + typeof(max) max__ = (max); \ + (void)(&start__ == &size__); \ + (void)(&start__ == &max__); \ + start__ > max__ || size__ > max__ - start__; \ +}) + +#define range_overflows_t(type, start, size, max) \ + range_overflows((type)(start), (type)(size), (type)(max)) + +/* Note we don't consider signbits :| */ +#define overflows_type(x, T) \ + (sizeof(x) > sizeof(T) && (x) >> (sizeof(T) * BITS_PER_BYTE)) + +#define ptr_mask_bits(ptr) ({ \ + unsigned long __v = (unsigned long)(ptr); \ + (typeof(ptr))(__v & PAGE_MASK); \ +}) + +#define ptr_unpack_bits(ptr, bits) ({ \ + unsigned long __v = (unsigned long)(ptr); \ + (bits) = __v & ~PAGE_MASK; \ + (typeof(ptr))(__v & PAGE_MASK); \ +}) + +#define ptr_pack_bits(ptr, bits) \ + ((typeof(ptr))((unsigned long)(ptr) | (bits))) + +#define fetch_and_zero(ptr) ({ \ + typeof(*ptr) __T = *(ptr); \ + *(ptr) = (typeof(*ptr))0; \ + __T; \ +}) + +#endif /* !__I915_UTILS_H */ diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index dae340cfc6c7..d0abfd08a01c 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -116,22 +116,20 @@ void intel_vgt_deballoon(struct drm_i915_private *dev_priv) memset(&bl_info, 0, sizeof(bl_info)); } -static int vgt_balloon_space(struct drm_mm *mm, +static int vgt_balloon_space(struct i915_ggtt *ggtt, struct drm_mm_node *node, unsigned long start, unsigned long end) { unsigned long size = end - start; - if (start == end) + if (start >= end) return -EINVAL; DRM_INFO("balloon space: range [ 0x%lx - 0x%lx ] %lu KiB.\n", start, end, size / 1024); - - node->start = start; - node->size = size; - - return drm_mm_reserve_node(mm, node); + return i915_gem_gtt_reserve(&ggtt->base, node, + size, start, I915_COLOR_UNEVICTABLE, + 0); } /** @@ -214,10 +212,8 @@ int intel_vgt_balloon(struct drm_i915_private *dev_priv) /* Unmappable graphic memory ballooning */ if (unmappable_base > ggtt->mappable_end) { - ret = vgt_balloon_space(&ggtt->base.mm, - &bl_info.space[2], - ggtt->mappable_end, - unmappable_base); + ret = vgt_balloon_space(ggtt, &bl_info.space[2], + ggtt->mappable_end, unmappable_base); if (ret) goto err; @@ -228,18 +224,15 @@ int intel_vgt_balloon(struct drm_i915_private *dev_priv) * because it is reserved to the guard page. */ if (unmappable_end < ggtt_end - PAGE_SIZE) { - ret = vgt_balloon_space(&ggtt->base.mm, - &bl_info.space[3], - unmappable_end, - ggtt_end - PAGE_SIZE); + ret = vgt_balloon_space(ggtt, &bl_info.space[3], + unmappable_end, ggtt_end - PAGE_SIZE); if (ret) goto err; } /* Mappable graphic memory ballooning */ if (mappable_base > ggtt->base.start) { - ret = vgt_balloon_space(&ggtt->base.mm, - &bl_info.space[0], + ret = vgt_balloon_space(ggtt, &bl_info.space[0], ggtt->base.start, mappable_base); if (ret) @@ -247,10 +240,8 @@ int intel_vgt_balloon(struct drm_i915_private *dev_priv) } if (mappable_end < ggtt->mappable_end) { - ret = vgt_balloon_space(&ggtt->base.mm, - &bl_info.space[1], - mappable_end, - ggtt->mappable_end); + ret = vgt_balloon_space(ggtt, &bl_info.space[1], + mappable_end, ggtt->mappable_end); if (ret) goto err; diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c index e924a9516079..155906e84812 100644 --- a/drivers/gpu/drm/i915/i915_vma.c +++ b/drivers/gpu/drm/i915/i915_vma.c @@ -45,6 +45,7 @@ i915_vma_retire(struct i915_gem_active *active, if (i915_vma_is_active(vma)) return; + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); list_move_tail(&vma->vm_link, &vma->vm->inactive_list); if (unlikely(i915_vma_is_closed(vma) && !i915_vma_is_pinned(vma))) WARN_ON(i915_vma_unbind(vma)); @@ -69,17 +70,15 @@ i915_vma_retire(struct i915_gem_active *active, } static struct i915_vma * -__i915_vma_create(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *view) +vma_create(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) { struct i915_vma *vma; struct rb_node *rb, **p; int i; - GEM_BUG_ON(vm->closed); - - vma = kmem_cache_zalloc(to_i915(obj->base.dev)->vmas, GFP_KERNEL); + vma = kmem_cache_zalloc(vm->i915->vmas, GFP_KERNEL); if (vma == NULL) return ERR_PTR(-ENOMEM); @@ -87,24 +86,50 @@ __i915_vma_create(struct drm_i915_gem_object *obj, for (i = 0; i < ARRAY_SIZE(vma->last_read); i++) init_request_active(&vma->last_read[i], i915_vma_retire); init_request_active(&vma->last_fence, NULL); - list_add(&vma->vm_link, &vm->unbound_list); vma->vm = vm; vma->obj = obj; vma->size = obj->base.size; + vma->display_alignment = I915_GTT_MIN_ALIGNMENT; - if (view) { + if (view && view->type != I915_GGTT_VIEW_NORMAL) { vma->ggtt_view = *view; if (view->type == I915_GGTT_VIEW_PARTIAL) { - vma->size = view->params.partial.size; + GEM_BUG_ON(range_overflows_t(u64, + view->partial.offset, + view->partial.size, + obj->base.size >> PAGE_SHIFT)); + vma->size = view->partial.size; vma->size <<= PAGE_SHIFT; + GEM_BUG_ON(vma->size >= obj->base.size); } else if (view->type == I915_GGTT_VIEW_ROTATED) { - vma->size = - intel_rotation_info_size(&view->params.rotated); + vma->size = intel_rotation_info_size(&view->rotated); vma->size <<= PAGE_SHIFT; } } + if (unlikely(vma->size > vm->total)) + goto err_vma; + + GEM_BUG_ON(!IS_ALIGNED(vma->size, I915_GTT_PAGE_SIZE)); + if (i915_is_ggtt(vm)) { + if (unlikely(overflows_type(vma->size, u32))) + goto err_vma; + + vma->fence_size = i915_gem_fence_size(vm->i915, vma->size, + i915_gem_object_get_tiling(obj), + i915_gem_object_get_stride(obj)); + if (unlikely(vma->fence_size < vma->size || /* overflow */ + vma->fence_size > vm->total)) + goto err_vma; + + GEM_BUG_ON(!IS_ALIGNED(vma->fence_size, I915_GTT_MIN_ALIGNMENT)); + + vma->fence_alignment = i915_gem_fence_alignment(vm->i915, vma->size, + i915_gem_object_get_tiling(obj), + i915_gem_object_get_stride(obj)); + GEM_BUG_ON(!is_power_of_2(vma->fence_alignment)); + vma->flags |= I915_VMA_GGTT; list_add(&vma->obj_link, &obj->vma_list); } else { @@ -126,20 +151,74 @@ __i915_vma_create(struct drm_i915_gem_object *obj, } rb_link_node(&vma->obj_node, rb, p); rb_insert_color(&vma->obj_node, &obj->vma_tree); + list_add(&vma->vm_link, &vm->unbound_list); return vma; + +err_vma: + kmem_cache_free(vm->i915->vmas, vma); + return ERR_PTR(-E2BIG); } +static struct i915_vma * +vma_lookup(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) +{ + struct rb_node *rb; + + rb = obj->vma_tree.rb_node; + while (rb) { + struct i915_vma *vma = rb_entry(rb, struct i915_vma, obj_node); + long cmp; + + cmp = i915_vma_compare(vma, vm, view); + if (cmp == 0) + return vma; + + if (cmp < 0) + rb = rb->rb_right; + else + rb = rb->rb_left; + } + + return NULL; +} + +/** + * i915_vma_instance - return the singleton instance of the VMA + * @obj: parent &struct drm_i915_gem_object to be mapped + * @vm: address space in which the mapping is located + * @view: additional mapping requirements + * + * i915_vma_instance() looks up an existing VMA of the @obj in the @vm with + * the same @view characteristics. If a match is not found, one is created. + * Once created, the VMA is kept until either the object is freed, or the + * address space is closed. + * + * Must be called with struct_mutex held. + * + * Returns the vma, or an error pointer. + */ struct i915_vma * -i915_vma_create(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *view) +i915_vma_instance(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view) { + struct i915_vma *vma; + lockdep_assert_held(&obj->base.dev->struct_mutex); GEM_BUG_ON(view && !i915_is_ggtt(vm)); - GEM_BUG_ON(i915_gem_obj_to_vma(obj, vm, view)); + GEM_BUG_ON(vm->closed); + + vma = vma_lookup(obj, vm, view); + if (!vma) + vma = vma_create(obj, vm, view); - return __i915_vma_create(obj, vm, view); + GEM_BUG_ON(!IS_ERR(vma) && i915_vma_is_closed(vma)); + GEM_BUG_ON(!IS_ERR(vma) && i915_vma_compare(vma, vm, view)); + GEM_BUG_ON(!IS_ERR(vma) && vma_lookup(obj, vm, view) != vma); + return vma; } /** @@ -176,6 +255,11 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level, if (bind_flags == 0) return 0; + if (GEM_WARN_ON(range_overflows(vma->node.start, + vma->node.size, + vma->vm->total))) + return -ENODEV; + if (vma_flags == 0 && vma->vm->allocate_va_range) { trace_i915_va_alloc(vma); ret = vma->vm->allocate_va_range(vma->vm, @@ -199,9 +283,9 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma) void __iomem *ptr; /* Access through the GTT requires the device to be awake. */ - assert_rpm_wakelock_held(to_i915(vma->vm->dev)); + assert_rpm_wakelock_held(vma->vm->i915); - lockdep_assert_held(&vma->vm->dev->struct_mutex); + lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); if (WARN_ON(!i915_vma_is_map_and_fenceable(vma))) return IO_ERR_PTR(-ENODEV); @@ -249,7 +333,8 @@ i915_vma_misplaced(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) if (vma->node.size < size) return true; - if (alignment && vma->node.start & (alignment - 1)) + GEM_BUG_ON(alignment && !is_power_of_2(alignment)); + if (alignment && !IS_ALIGNED(vma->node.start, alignment)) return true; if (flags & PIN_MAPPABLE && !i915_vma_is_map_and_fenceable(vma)) @@ -268,40 +353,37 @@ i915_vma_misplaced(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) void __i915_vma_set_map_and_fenceable(struct i915_vma *vma) { - struct drm_i915_gem_object *obj = vma->obj; - struct drm_i915_private *dev_priv = to_i915(obj->base.dev); bool mappable, fenceable; - u32 fence_size, fence_alignment; - - fence_size = i915_gem_get_ggtt_size(dev_priv, - vma->size, - i915_gem_object_get_tiling(obj)); - fence_alignment = i915_gem_get_ggtt_alignment(dev_priv, - vma->size, - i915_gem_object_get_tiling(obj), - true); - - fenceable = (vma->node.size == fence_size && - (vma->node.start & (fence_alignment - 1)) == 0); - mappable = (vma->node.start + fence_size <= - dev_priv->ggtt.mappable_end); + GEM_BUG_ON(!i915_vma_is_ggtt(vma)); + GEM_BUG_ON(!vma->fence_size); /* * Explicitly disable for rotated VMA since the display does not * need the fence and the VMA is not accessible to other users. */ - if (mappable && fenceable && - vma->ggtt_view.type != I915_GGTT_VIEW_ROTATED) + if (vma->ggtt_view.type == I915_GGTT_VIEW_ROTATED) + return; + + fenceable = (vma->node.size >= vma->fence_size && + IS_ALIGNED(vma->node.start, vma->fence_alignment)); + + mappable = vma->node.start + vma->fence_size <= i915_vm_to_ggtt(vma->vm)->mappable_end; + + if (mappable && fenceable) vma->flags |= I915_VMA_CAN_FENCE; else vma->flags &= ~I915_VMA_CAN_FENCE; } -bool i915_gem_valid_gtt_space(struct i915_vma *vma, - unsigned long cache_level) +static bool color_differs(struct drm_mm_node *node, unsigned long color) +{ + return node->allocated && node->color != color; +} + +bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long cache_level) { - struct drm_mm_node *gtt_space = &vma->node; + struct drm_mm_node *node = &vma->node; struct drm_mm_node *other; /* @@ -314,18 +396,16 @@ bool i915_gem_valid_gtt_space(struct i915_vma *vma, if (vma->vm->mm.color_adjust == NULL) return true; - if (!drm_mm_node_allocated(gtt_space)) - return true; - - if (list_empty(>t_space->node_list)) - return true; + /* Only valid to be called on an already inserted vma */ + GEM_BUG_ON(!drm_mm_node_allocated(node)); + GEM_BUG_ON(list_empty(&node->node_list)); - other = list_entry(gtt_space->node_list.prev, struct drm_mm_node, node_list); - if (other->allocated && !other->hole_follows && other->color != cache_level) + other = list_prev_entry(node, node_list); + if (color_differs(other, cache_level) && !drm_mm_hole_follows(other)) return false; - other = list_entry(gtt_space->node_list.next, struct drm_mm_node, node_list); - if (other->allocated && !gtt_space->hole_follows && other->color != cache_level) + other = list_next_entry(node, node_list); + if (color_differs(other, cache_level) && !drm_mm_hole_follows(node)) return false; return true; @@ -348,7 +428,7 @@ bool i915_gem_valid_gtt_space(struct i915_vma *vma, static int i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) { - struct drm_i915_private *dev_priv = to_i915(vma->vm->dev); + struct drm_i915_private *dev_priv = vma->vm->i915; struct drm_i915_gem_object *obj = vma->obj; u64 start, end; int ret; @@ -357,22 +437,26 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) GEM_BUG_ON(drm_mm_node_allocated(&vma->node)); size = max(size, vma->size); - if (flags & PIN_MAPPABLE) - size = i915_gem_get_ggtt_size(dev_priv, size, - i915_gem_object_get_tiling(obj)); + alignment = max(alignment, vma->display_alignment); + if (flags & PIN_MAPPABLE) { + size = max_t(typeof(size), size, vma->fence_size); + alignment = max_t(typeof(alignment), + alignment, vma->fence_alignment); + } - alignment = max(max(alignment, vma->display_alignment), - i915_gem_get_ggtt_alignment(dev_priv, size, - i915_gem_object_get_tiling(obj), - flags & PIN_MAPPABLE)); + GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE)); + GEM_BUG_ON(!IS_ALIGNED(alignment, I915_GTT_MIN_ALIGNMENT)); + GEM_BUG_ON(!is_power_of_2(alignment)); start = flags & PIN_OFFSET_BIAS ? flags & PIN_OFFSET_MASK : 0; + GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE)); end = vma->vm->total; if (flags & PIN_MAPPABLE) end = min_t(u64, end, dev_priv->ggtt.mappable_end); if (flags & PIN_ZONE_4G) - end = min_t(u64, end, (1ULL << 32) - PAGE_SIZE); + end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE); + GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE)); /* If binding the object/GGTT view requires more space than the entire * aperture has, reject it early before evicting everything in a vain @@ -392,64 +476,28 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) if (flags & PIN_OFFSET_FIXED) { u64 offset = flags & PIN_OFFSET_MASK; - if (offset & (alignment - 1) || offset > end - size) { + if (!IS_ALIGNED(offset, alignment) || + range_overflows(offset, size, end)) { ret = -EINVAL; goto err_unpin; } - vma->node.start = offset; - vma->node.size = size; - vma->node.color = obj->cache_level; - ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node); - if (ret) { - ret = i915_gem_evict_for_vma(vma); - if (ret == 0) - ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node); - if (ret) - goto err_unpin; - } + ret = i915_gem_gtt_reserve(vma->vm, &vma->node, + size, offset, obj->cache_level, + flags); + if (ret) + goto err_unpin; } else { - u32 search_flag, alloc_flag; - - if (flags & PIN_HIGH) { - search_flag = DRM_MM_SEARCH_BELOW; - alloc_flag = DRM_MM_CREATE_TOP; - } else { - search_flag = DRM_MM_SEARCH_DEFAULT; - alloc_flag = DRM_MM_CREATE_DEFAULT; - } - - /* We only allocate in PAGE_SIZE/GTT_PAGE_SIZE (4096) chunks, - * so we know that we always have a minimum alignment of 4096. - * The drm_mm range manager is optimised to return results - * with zero alignment, so where possible use the optimal - * path. - */ - if (alignment <= 4096) - alignment = 0; - -search_free: - ret = drm_mm_insert_node_in_range_generic(&vma->vm->mm, - &vma->node, - size, alignment, - obj->cache_level, - start, end, - search_flag, - alloc_flag); - if (ret) { - ret = i915_gem_evict_something(vma->vm, size, alignment, - obj->cache_level, - start, end, - flags); - if (ret == 0) - goto search_free; - + ret = i915_gem_gtt_insert(vma->vm, &vma->node, + size, alignment, obj->cache_level, + start, end, flags); + if (ret) goto err_unpin; - } GEM_BUG_ON(vma->node.start < start); GEM_BUG_ON(vma->node.start + vma->node.size > end); } + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, obj->cache_level)); list_move_tail(&obj->global_link, &dev_priv->mm.bound_list); @@ -470,7 +518,7 @@ int __i915_vma_do_pin(struct i915_vma *vma, unsigned int bound = vma->flags; int ret; - lockdep_assert_held(&vma->vm->dev->struct_mutex); + lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0); GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma)); @@ -492,6 +540,7 @@ int __i915_vma_do_pin(struct i915_vma *vma, if ((bound ^ vma->flags) & I915_VMA_GLOBAL_BIND) __i915_vma_set_map_and_fenceable(vma); + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags)); return 0; @@ -568,7 +617,7 @@ int i915_vma_unbind(struct i915_vma *vma) for_each_active(active, idx) { ret = i915_gem_active_retire(&vma->last_read[idx], - &vma->vm->dev->struct_mutex); + &vma->vm->i915->drm.struct_mutex); if (ret) break; } @@ -629,6 +678,7 @@ int i915_vma_unbind(struct i915_vma *vma) * reaped by the shrinker. */ i915_gem_object_unpin_pages(obj); + GEM_BUG_ON(atomic_read(&obj->mm.pages_pin_count) < obj->bind_count); destroy: if (unlikely(i915_vma_is_closed(vma))) diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h index 85446f0b0b3f..e39d922cfb6f 100644 --- a/drivers/gpu/drm/i915/i915_vma.h +++ b/drivers/gpu/drm/i915/i915_vma.h @@ -55,6 +55,9 @@ struct i915_vma { u64 size; u64 display_alignment; + u32 fence_size; + u32 fence_alignment; + unsigned int flags; /** * How many users have pinned this object in GTT space. The following @@ -109,9 +112,9 @@ struct i915_vma { }; struct i915_vma * -i915_vma_create(struct drm_i915_gem_object *obj, - struct i915_address_space *vm, - const struct i915_ggtt_view *view); +i915_vma_instance(struct drm_i915_gem_object *obj, + struct i915_address_space *vm, + const struct i915_ggtt_view *view); void i915_vma_unpin_and_release(struct i915_vma **p_vma); @@ -178,25 +181,48 @@ static inline void i915_vma_put(struct i915_vma *vma) i915_gem_object_put(vma->obj); } +static __always_inline ptrdiff_t ptrdiff(const void *a, const void *b) +{ + return a - b; +} + static inline long i915_vma_compare(struct i915_vma *vma, struct i915_address_space *vm, const struct i915_ggtt_view *view) { + ptrdiff_t cmp; + GEM_BUG_ON(view && !i915_is_ggtt(vm)); - if (vma->vm != vm) - return vma->vm - vm; + cmp = ptrdiff(vma->vm, vm); + if (cmp) + return cmp; + BUILD_BUG_ON(I915_GGTT_VIEW_NORMAL != 0); + cmp = vma->ggtt_view.type; if (!view) - return vma->ggtt_view.type; + return cmp; - if (vma->ggtt_view.type != view->type) - return vma->ggtt_view.type - view->type; + cmp -= view->type; + if (cmp) + return cmp; - return memcmp(&vma->ggtt_view.params, - &view->params, - sizeof(view->params)); + /* ggtt_view.type also encodes its size so that we both distinguish + * different views using it as a "type" and also use a compact (no + * accessing of uninitialised padding bytes) memcmp without storing + * an extra parameter or adding more code. + * + * To ensure that the memcmp is valid for all branches of the union, + * even though the code looks like it is just comparing one branch, + * we assert above that all branches have the same address, and that + * each branch has a unique type/size. + */ + BUILD_BUG_ON(I915_GGTT_VIEW_NORMAL >= I915_GGTT_VIEW_PARTIAL); + BUILD_BUG_ON(I915_GGTT_VIEW_PARTIAL >= I915_GGTT_VIEW_ROTATED); + BUILD_BUG_ON(offsetof(typeof(*view), rotated) != + offsetof(typeof(*view), partial)); + return memcmp(&vma->ggtt_view.partial, &view->partial, view->type); } int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level, @@ -221,8 +247,11 @@ i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags) /* Pin early to prevent the shrinker/eviction logic from destroying * our vma as we insert and bind. */ - if (likely(((++vma->flags ^ flags) & I915_VMA_BIND_MASK) == 0)) + if (likely(((++vma->flags ^ flags) & I915_VMA_BIND_MASK) == 0)) { + GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); + GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags)); return 0; + } return __i915_vma_do_pin(vma, size, alignment, flags); } @@ -282,7 +311,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma); */ static inline void i915_vma_unpin_iomap(struct i915_vma *vma) { - lockdep_assert_held(&vma->vm->dev->struct_mutex); + lockdep_assert_held(&vma->obj->base.dev->struct_mutex); GEM_BUG_ON(vma->iomap == NULL); i915_vma_unpin(vma); } @@ -311,7 +340,7 @@ static inline struct page *i915_vma_first_page(struct i915_vma *vma) static inline bool i915_vma_pin_fence(struct i915_vma *vma) { - lockdep_assert_held(&vma->vm->dev->struct_mutex); + lockdep_assert_held(&vma->obj->base.dev->struct_mutex); if (vma->fence) { vma->fence->pin_count++; return true; @@ -330,7 +359,7 @@ i915_vma_pin_fence(struct i915_vma *vma) static inline void i915_vma_unpin_fence(struct i915_vma *vma) { - lockdep_assert_held(&vma->vm->dev->struct_mutex); + lockdep_assert_held(&vma->obj->base.dev->struct_mutex); if (vma->fence) { GEM_BUG_ON(vma->fence->pin_count <= 0); vma->fence->pin_count--; diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index c5a166752eda..aa9160e7f1d8 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -265,37 +265,6 @@ int intel_atomic_setup_scalers(struct drm_device *dev, return 0; } -static void -intel_atomic_duplicate_dpll_state(struct drm_i915_private *dev_priv, - struct intel_shared_dpll_config *shared_dpll) -{ - enum intel_dpll_id i; - - /* Copy shared dpll state */ - for (i = 0; i < dev_priv->num_shared_dpll; i++) { - struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; - - shared_dpll[i] = pll->config; - } -} - -struct intel_shared_dpll_config * -intel_atomic_get_shared_dpll_state(struct drm_atomic_state *s) -{ - struct intel_atomic_state *state = to_intel_atomic_state(s); - - WARN_ON(!drm_modeset_is_locked(&s->dev->mode_config.connection_mutex)); - - if (!state->dpll_set) { - state->dpll_set = true; - - intel_atomic_duplicate_dpll_state(to_i915(s->dev), - state->shared_dpll); - } - - return state->shared_dpll; -} - struct drm_atomic_state * intel_atomic_state_alloc(struct drm_device *dev) { diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c index 8d3e515f27ba..41fd94e62d3c 100644 --- a/drivers/gpu/drm/i915/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c @@ -123,36 +123,24 @@ intel_plane_destroy_state(struct drm_plane *plane, drm_atomic_helper_plane_destroy_state(plane, state); } -static int intel_plane_atomic_check(struct drm_plane *plane, - struct drm_plane_state *state) +int intel_plane_atomic_check_with_state(struct intel_crtc_state *crtc_state, + struct intel_plane_state *intel_state) { + struct drm_plane *plane = intel_state->base.plane; struct drm_i915_private *dev_priv = to_i915(plane->dev); - struct drm_crtc *crtc = state->crtc; - struct intel_crtc *intel_crtc; - struct intel_crtc_state *crtc_state; + struct drm_plane_state *state = &intel_state->base; struct intel_plane *intel_plane = to_intel_plane(plane); - struct intel_plane_state *intel_state = to_intel_plane_state(state); - struct drm_crtc_state *drm_crtc_state; int ret; - crtc = crtc ? crtc : plane->state->crtc; - intel_crtc = to_intel_crtc(crtc); - /* * Both crtc and plane->crtc could be NULL if we're updating a * property while the plane is disabled. We don't actually have * anything driver-specific we need to test in that case, so * just return success. */ - if (!crtc) + if (!intel_state->base.crtc && !plane->state->crtc) return 0; - drm_crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); - if (WARN_ON(!drm_crtc_state)) - return -EINVAL; - - crtc_state = to_intel_crtc_state(drm_crtc_state); - /* Clip all planes to CRTC size, or 0x0 if CRTC is disabled */ intel_state->clip.x1 = 0; intel_state->clip.y1 = 0; @@ -175,11 +163,11 @@ static int intel_plane_atomic_check(struct drm_plane *plane, * RGB 16-bit 5:6:5, and Indexed 8-bit. * TBD: Add RGB64 case once its added in supported format list. */ - switch (state->fb->pixel_format) { + switch (state->fb->format->format) { case DRM_FORMAT_C8: case DRM_FORMAT_RGB565: DRM_DEBUG_KMS("Unsupported pixel format %s for 90/270!\n", - drm_get_format_name(state->fb->pixel_format, + drm_get_format_name(state->fb->format->format, &format_name)); return -EINVAL; @@ -204,6 +192,31 @@ static int intel_plane_atomic_check(struct drm_plane *plane, return intel_plane_atomic_calc_changes(&crtc_state->base, state); } +static int intel_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *drm_crtc_state; + + crtc = crtc ? crtc : plane->state->crtc; + + /* + * Both crtc and plane->crtc could be NULL if we're updating a + * property while the plane is disabled. We don't actually have + * anything driver-specific we need to test in that case, so + * just return success. + */ + if (!crtc) + return 0; + + drm_crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!drm_crtc_state)) + return -EINVAL; + + return intel_plane_atomic_check_with_state(to_intel_crtc_state(drm_crtc_state), + to_intel_plane_state(state)); +} + static void intel_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 49f10538d4aa..d76f3033e890 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -24,6 +24,7 @@ #include <linux/kernel.h> #include <linux/component.h> #include <drm/i915_component.h> +#include <drm/intel_lpe_audio.h> #include "intel_drv.h" #include <drm/drmP.h> @@ -623,13 +624,28 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder, dev_priv->av_enc_map[pipe] = intel_encoder; mutex_unlock(&dev_priv->av_mutex); - /* audio drivers expect pipe = -1 to indicate Non-MST cases */ - if (intel_encoder->type != INTEL_OUTPUT_DP_MST) - pipe = -1; - - if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) { + /* audio drivers expect pipe = -1 to indicate Non-MST cases */ + if (intel_encoder->type != INTEL_OUTPUT_DP_MST) + pipe = -1; acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port, (int) pipe); + } + + switch (intel_encoder->type) { + case INTEL_OUTPUT_HDMI: + intel_lpe_audio_notify(dev_priv, connector->eld, port, pipe, + crtc_state->port_clock, + false, 0); + break; + case INTEL_OUTPUT_DP: + intel_lpe_audio_notify(dev_priv, connector->eld, port, pipe, + adjusted_mode->crtc_clock, + true, crtc_state->port_clock); + break; + default: + break; + } } /** @@ -656,13 +672,15 @@ void intel_audio_codec_disable(struct intel_encoder *intel_encoder) dev_priv->av_enc_map[pipe] = NULL; mutex_unlock(&dev_priv->av_mutex); - /* audio drivers expect pipe = -1 to indicate Non-MST cases */ - if (intel_encoder->type != INTEL_OUTPUT_DP_MST) - pipe = -1; - - if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) { + /* audio drivers expect pipe = -1 to indicate Non-MST cases */ + if (intel_encoder->type != INTEL_OUTPUT_DP_MST) + pipe = -1; acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port, (int) pipe); + } + + intel_lpe_audio_notify(dev_priv, NULL, port, pipe, 0, false, 0); } /** @@ -737,25 +755,49 @@ static int i915_audio_component_get_cdclk_freq(struct device *kdev) return dev_priv->cdclk_freq; } +/* + * get the intel_encoder according to the parameter port and pipe + * intel_encoder is saved by the index of pipe + * MST & (pipe >= 0): return the av_enc_map[pipe], + * when port is matched + * MST & (pipe < 0): this is invalid + * Non-MST & (pipe >= 0): only pipe = 0 (the first device entry) + * will get the right intel_encoder with port matched + * Non-MST & (pipe < 0): get the right intel_encoder with port matched + */ static struct intel_encoder *get_saved_enc(struct drm_i915_private *dev_priv, int port, int pipe) { + struct intel_encoder *encoder; if (WARN_ON(pipe >= I915_MAX_PIPES)) return NULL; /* MST */ - if (pipe >= 0) - return dev_priv->av_enc_map[pipe]; + if (pipe >= 0) { + encoder = dev_priv->av_enc_map[pipe]; + /* + * when bootup, audio driver may not know it is + * MST or not. So it will poll all the port & pipe + * combinations + */ + if (encoder != NULL && encoder->port == port && + encoder->type == INTEL_OUTPUT_DP_MST) + return encoder; + } /* Non-MST */ - for_each_pipe(dev_priv, pipe) { - struct intel_encoder *encoder; + if (pipe > 0) + return NULL; + for_each_pipe(dev_priv, pipe) { encoder = dev_priv->av_enc_map[pipe]; if (encoder == NULL) continue; + if (encoder->type == INTEL_OUTPUT_DP_MST) + continue; + if (port == encoder->port) return encoder; } @@ -781,9 +823,7 @@ static int i915_audio_component_sync_audio_rate(struct device *kdev, int port, /* 1. get the pipe */ intel_encoder = get_saved_enc(dev_priv, port, pipe); - if (!intel_encoder || !intel_encoder->base.crtc || - (intel_encoder->type != INTEL_OUTPUT_HDMI && - intel_encoder->type != INTEL_OUTPUT_DP)) { + if (!intel_encoder || !intel_encoder->base.crtc) { DRM_DEBUG_KMS("Not valid for port %c\n", port_name(port)); err = -ENODEV; goto unlock; @@ -906,6 +946,9 @@ void i915_audio_component_init(struct drm_i915_private *dev_priv) { int ret; + if (INTEL_INFO(dev_priv)->num_pipes == 0) + return; + ret = component_add(dev_priv->drm.dev, &i915_audio_component_bind_ops); if (ret < 0) { DRM_ERROR("failed to add audio component (%d)\n", ret); @@ -931,3 +974,28 @@ void i915_audio_component_cleanup(struct drm_i915_private *dev_priv) component_del(dev_priv->drm.dev, &i915_audio_component_bind_ops); dev_priv->audio_component_registered = false; } + +/** + * intel_audio_init() - Initialize the audio driver either using + * component framework or using lpe audio bridge + * @dev_priv: the i915 drm device private data + * + */ +void intel_audio_init(struct drm_i915_private *dev_priv) +{ + if (intel_lpe_audio_init(dev_priv) < 0) + i915_audio_component_init(dev_priv); +} + +/** + * intel_audio_deinit() - deinitialize the audio driver + * @dev_priv: the i915 drm device private data + * + */ +void intel_audio_deinit(struct drm_i915_private *dev_priv) +{ + if ((dev_priv)->lpe_audio.platdev != NULL) + intel_lpe_audio_teardown(dev_priv); + else + i915_audio_component_cleanup(dev_priv); +} diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 7ffab1abc518..e144f033f4b5 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -114,16 +114,18 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + - dvo_timing->hsync_pulse_width; + ((dvo_timing->hsync_pulse_width_hi << 8) | + dvo_timing->hsync_pulse_width_lo); panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | dvo_timing->vactive_lo; panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + - dvo_timing->vsync_off; + ((dvo_timing->vsync_off_hi << 4) | dvo_timing->vsync_off_lo); panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + - dvo_timing->vsync_pulse_width; + ((dvo_timing->vsync_pulse_width_hi << 4) | + dvo_timing->vsync_pulse_width_lo); panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); panel_fixed_mode->clock = dvo_timing->clock * 10; @@ -330,17 +332,19 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv, method = &backlight_data->backlight_control[panel_type]; dev_priv->vbt.backlight.type = method->type; + dev_priv->vbt.backlight.controller = method->controller; } dev_priv->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz; dev_priv->vbt.backlight.active_low_pwm = entry->active_low_pwm; dev_priv->vbt.backlight.min_brightness = entry->min_brightness; DRM_DEBUG_KMS("VBT backlight PWM modulation frequency %u Hz, " - "active %s, min brightness %u, level %u\n", + "active %s, min brightness %u, level %u, controller %u\n", dev_priv->vbt.backlight.pwm_freq_hz, dev_priv->vbt.backlight.active_low_pwm ? "low" : "high", dev_priv->vbt.backlight.min_brightness, - backlight_data->level[panel_type]); + backlight_data->level[panel_type], + dev_priv->vbt.backlight.controller); } /* Try to find sdvo panel data */ @@ -1159,6 +1163,7 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port, info->supports_dvi = is_dvi; info->supports_hdmi = is_hdmi; info->supports_dp = is_dp; + info->supports_edp = is_edp; DRM_DEBUG_KMS("Port %c VBT info: DP:%d HDMI:%d DVI:%d EDP:%d CRT:%d\n", port_name(port), is_dp, is_hdmi, is_dvi, is_edp, is_crt); @@ -1411,13 +1416,16 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size) return false; } - if (vbt->bdb_offset + sizeof(struct bdb_header) > size) { + if (range_overflows_t(size_t, + vbt->bdb_offset, + sizeof(struct bdb_header), + size)) { DRM_DEBUG_DRIVER("BDB header incomplete\n"); return false; } bdb = get_bdb_header(vbt); - if (vbt->bdb_offset + bdb->bdb_size > size) { + if (range_overflows_t(size_t, vbt->bdb_offset, bdb->bdb_size, size)) { DRM_DEBUG_DRIVER("BDB incomplete\n"); return false; } @@ -1662,6 +1670,9 @@ bool intel_bios_is_port_edp(struct drm_i915_private *dev_priv, enum port port) }; int i; + if (HAS_DDI(dev_priv)) + return dev_priv->vbt.ddi_port_info[port].supports_edp; + if (!dev_priv->vbt.child_dev_num) return false; @@ -1779,7 +1790,7 @@ intel_bios_is_port_hpd_inverted(struct drm_i915_private *dev_priv, { int i; - if (WARN_ON_ONCE(!IS_BROXTON(dev_priv))) + if (WARN_ON_ONCE(!IS_GEN9_LP(dev_priv))) return false; for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c index c9c46a538edb..7044e9a6abf7 100644 --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c @@ -23,6 +23,7 @@ */ #include <linux/kthread.h> +#include <uapi/linux/sched/types.h> #include "i915_drv.h" @@ -154,7 +155,7 @@ static void __intel_breadcrumbs_disable_irq(struct intel_breadcrumbs *b) static inline struct intel_wait *to_wait(struct rb_node *node) { - return container_of(node, struct intel_wait, node); + return rb_entry(node, struct intel_wait, node); } static inline void __intel_breadcrumbs_finish(struct intel_breadcrumbs *b, @@ -427,7 +428,7 @@ static bool signal_complete(struct drm_i915_gem_request *request) static struct drm_i915_gem_request *to_signaler(struct rb_node *rb) { - return container_of(rb, struct drm_i915_gem_request, signaling.node); + return rb_entry(rb, struct drm_i915_gem_request, signaling.node); } static void signaler_set_rtpriority(void) @@ -623,6 +624,12 @@ void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine) { struct intel_breadcrumbs *b = &engine->breadcrumbs; + /* The engines should be idle and all requests accounted for! */ + WARN_ON(READ_ONCE(b->first_wait)); + WARN_ON(!RB_EMPTY_ROOT(&b->waiters)); + WARN_ON(READ_ONCE(b->first_signal)); + WARN_ON(!RB_EMPTY_ROOT(&b->signals)); + if (!IS_ERR_OR_NULL(b->signaler)) kthread_stop(b->signaler); diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 588470eb8d39..2bf5aca6e37c 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -837,12 +837,11 @@ static const struct drm_encoder_funcs intel_crt_enc_funcs = { .destroy = intel_encoder_destroy, }; -void intel_crt_init(struct drm_device *dev) +void intel_crt_init(struct drm_i915_private *dev_priv) { struct drm_connector *connector; struct intel_crt *crt; struct intel_connector *intel_connector; - struct drm_i915_private *dev_priv = to_i915(dev); i915_reg_t adpa_reg; u32 adpa; @@ -882,10 +881,10 @@ void intel_crt_init(struct drm_device *dev) connector = &intel_connector->base; crt->connector = intel_connector; - drm_connector_init(dev, &intel_connector->base, + drm_connector_init(&dev_priv->drm, &intel_connector->base, &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); - drm_encoder_init(dev, &crt->base.base, &intel_crt_enc_funcs, + drm_encoder_init(&dev_priv->drm, &crt->base.base, &intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC, "CRT"); intel_connector_attach_encoder(intel_connector, &crt->base); diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index d7a04bca8c28..0085bc745f6a 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -34,6 +34,10 @@ * low-power state and comes back to normal. */ +#define I915_CSR_GLK "i915/glk_dmc_ver1_01.bin" +MODULE_FIRMWARE(I915_CSR_GLK); +#define GLK_CSR_VERSION_REQUIRED CSR_VERSION(1, 1) + #define I915_CSR_KBL "i915/kbl_dmc_ver1_01.bin" MODULE_FIRMWARE(I915_CSR_KBL); #define KBL_CSR_VERSION_REQUIRED CSR_VERSION(1, 1) @@ -286,7 +290,9 @@ static uint32_t *parse_csr_fw(struct drm_i915_private *dev_priv, csr->version = css_header->version; - if (IS_KABYLAKE(dev_priv)) { + if (IS_GEMINILAKE(dev_priv)) { + required_version = GLK_CSR_VERSION_REQUIRED; + } else if (IS_KABYLAKE(dev_priv)) { required_version = KBL_CSR_VERSION_REQUIRED; } else if (IS_SKYLAKE(dev_priv)) { required_version = SKL_CSR_VERSION_REQUIRED; @@ -389,7 +395,7 @@ static void csr_load_work_fn(struct work_struct *work) { struct drm_i915_private *dev_priv; struct intel_csr *csr; - const struct firmware *fw; + const struct firmware *fw = NULL; int ret; dev_priv = container_of(work, typeof(*dev_priv), csr.work); @@ -405,7 +411,7 @@ static void csr_load_work_fn(struct work_struct *work) intel_display_power_put(dev_priv, POWER_DOMAIN_INIT); - DRM_INFO("Finished loading %s (v%u.%u)\n", + DRM_INFO("Finished loading DMC firmware %s (v%u.%u)\n", dev_priv->csr.fw_path, CSR_VERSION_MAJOR(csr->version), CSR_VERSION_MINOR(csr->version)); @@ -435,7 +441,9 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv) if (!HAS_CSR(dev_priv)) return; - if (IS_KABYLAKE(dev_priv)) + if (IS_GEMINILAKE(dev_priv)) + csr->fw_path = I915_CSR_GLK; + else if (IS_KABYLAKE(dev_priv)) csr->fw_path = I915_CSR_KBL; else if (IS_SKYLAKE(dev_priv)) csr->fw_path = I915_CSR_SKL; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 10ec9d4b7d45..66b367d0771a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -442,7 +442,7 @@ static int intel_ddi_hdmi_level(struct drm_i915_private *dev_priv, enum port por hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) return hdmi_level; if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { @@ -484,7 +484,7 @@ void intel_prepare_dp_ddi_buffers(struct intel_encoder *encoder) const struct ddi_buf_trans *ddi_translations_edp; const struct ddi_buf_trans *ddi_translations; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) return; if (IS_KABYLAKE(dev_priv)) { @@ -567,7 +567,7 @@ static void intel_prepare_hdmi_ddi_buffers(struct intel_encoder *encoder) enum port port = intel_ddi_get_encoder_port(encoder); const struct ddi_buf_trans *ddi_translations_hdmi; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) return; hdmi_level = intel_ddi_hdmi_level(dev_priv, port); @@ -1057,7 +1057,7 @@ static int bxt_calc_pll_link(struct drm_i915_private *dev_priv, return 0; pll = &dev_priv->shared_dplls[dpll]; - state = &pll->config.hw_state; + state = &pll->state.hw_state; clock.m1 = 2; clock.m2 = (state->pll0 & PORT_PLL_M2_MASK) << 22; @@ -1091,7 +1091,7 @@ void intel_ddi_clock_get(struct intel_encoder *encoder, hsw_ddi_clock_get(encoder, pipe_config); else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) skl_ddi_clock_get(encoder, pipe_config); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_ddi_clock_get(encoder, pipe_config); } @@ -1153,7 +1153,7 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc, if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) return skl_ddi_pll_select(intel_crtc, crtc_state, intel_encoder); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) return bxt_ddi_pll_select(intel_crtc, crtc_state, intel_encoder); else @@ -1429,7 +1429,7 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port)); out: - if (ret && IS_BROXTON(dev_priv)) { + if (ret && IS_GEN9_LP(dev_priv)) { tmp = I915_READ(BXT_PHY_CTL(port)); if ((tmp & (BXT_PHY_LANE_POWERDOWN_ACK | BXT_PHY_LANE_ENABLED)) != BXT_PHY_LANE_ENABLED) @@ -1643,7 +1643,7 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp) if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) skl_ddi_set_iboost(encoder, level); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_ddi_vswing_sequence(dev_priv, level, port, encoder->type); return DDI_BUF_TRANS_SELECT(level); @@ -1701,7 +1701,8 @@ static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder, static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder, bool has_hdmi_sink, - struct drm_display_mode *adjusted_mode, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state, struct intel_shared_dpll *pll) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); @@ -1715,13 +1716,13 @@ static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder, intel_prepare_hdmi_ddi_buffers(encoder); if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) skl_ddi_set_iboost(encoder, level); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_ddi_vswing_sequence(dev_priv, level, port, INTEL_OUTPUT_HDMI); intel_hdmi->set_infoframes(drm_encoder, has_hdmi_sink, - adjusted_mode); + crtc_state, conn_state); } static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder, @@ -1742,8 +1743,8 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder, } if (type == INTEL_OUTPUT_HDMI) { intel_ddi_pre_enable_hdmi(intel_encoder, - crtc->config->has_hdmi_sink, - &crtc->config->base.adjusted_mode, + pipe_config->has_hdmi_sink, + pipe_config, conn_state, crtc->config->shared_dpll); } } @@ -1949,6 +1950,19 @@ void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp) udelay(600); } +bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv, + struct intel_crtc *intel_crtc) +{ + u32 temp; + + if (intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_AUDIO)) { + temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + if (temp & AUDIO_OUTPUT_ENABLE(intel_crtc->pipe)) + return true; + } + return false; +} + void intel_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { @@ -2014,11 +2028,8 @@ void intel_ddi_get_config(struct intel_encoder *encoder, break; } - if (intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_AUDIO)) { - temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); - if (temp & AUDIO_OUTPUT_ENABLE(intel_crtc->pipe)) - pipe_config->has_audio = true; - } + pipe_config->has_audio = + intel_ddi_is_audio_enabled(dev_priv, intel_crtc); if (encoder->type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp.bpp && pipe_config->pipe_bpp > dev_priv->vbt.edp.bpp) { @@ -2042,7 +2053,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder, intel_ddi_clock_get(encoder, pipe_config); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) pipe_config->lane_lat_optim_mask = bxt_ddi_phy_get_lane_lat_optim_mask(encoder); } @@ -2066,7 +2077,7 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder, else ret = intel_dp_compute_config(encoder, pipe_config, conn_state); - if (IS_BROXTON(dev_priv) && ret) + if (IS_GEN9_LP(dev_priv) && ret) pipe_config->lane_lat_optim_mask = bxt_ddi_phy_calc_lane_lat_optim_mask(encoder, pipe_config->lane_count); @@ -2123,10 +2134,10 @@ intel_ddi_get_link_dpll(struct intel_dp *intel_dp, int clock) struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_shared_dpll *pll = NULL; - struct intel_shared_dpll_config tmp_pll_config; + struct intel_shared_dpll_state tmp_pll_state; enum intel_dpll_id dpll_id; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { dpll_id = (enum intel_dpll_id)dig_port->port; /* * Select the required PLL. This works for platforms where @@ -2139,11 +2150,11 @@ intel_ddi_get_link_dpll(struct intel_dp *intel_dp, int clock) pll->active_mask); return NULL; } - tmp_pll_config = pll->config; + tmp_pll_state = pll->state; if (!bxt_ddi_dp_set_dpll_hw_state(clock, - &pll->config.hw_state)) { + &pll->state.hw_state)) { DRM_ERROR("Could not setup DPLL\n"); - pll->config = tmp_pll_config; + pll->state = tmp_pll_state; return NULL; } } else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { @@ -2154,9 +2165,8 @@ intel_ddi_get_link_dpll(struct intel_dp *intel_dp, int clock) return pll; } -void intel_ddi_init(struct drm_device *dev, enum port port) +void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_digital_port *intel_dig_port; struct intel_encoder *intel_encoder; struct drm_encoder *encoder; @@ -2218,12 +2228,12 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder = &intel_dig_port->base; encoder = &intel_encoder->base; - drm_encoder_init(dev, encoder, &intel_ddi_funcs, + drm_encoder_init(&dev_priv->drm, encoder, &intel_ddi_funcs, DRM_MODE_ENCODER_TMDS, "DDI %c", port_name(port)); intel_encoder->compute_config = intel_ddi_compute_config; intel_encoder->enable = intel_enable_ddi; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) intel_encoder->pre_pll_enable = bxt_ddi_pre_pll_enable; intel_encoder->pre_enable = intel_ddi_pre_enable; intel_encoder->disable = intel_disable_ddi; @@ -2244,7 +2254,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port) * configuration so that we use the proper lane count for our * calculations. */ - if (IS_BROXTON(dev_priv) && port == PORT_A) { + if (IS_GEN9_LP(dev_priv) && port == PORT_A) { if (!(intel_dig_port->saved_port_bits & DDI_A_4_LANES)) { DRM_DEBUG_KMS("BXT BIOS forgot to set DDI_A_4_LANES for port A; fixing\n"); intel_dig_port->saved_port_bits |= DDI_A_4_LANES; diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c index 185e3bbc9ec9..fcf81815daff 100644 --- a/drivers/gpu/drm/i915/intel_device_info.c +++ b/drivers/gpu/drm/i915/intel_device_info.c @@ -24,11 +24,51 @@ #include "i915_drv.h" +#define PLATFORM_NAME(x) [INTEL_##x] = #x +static const char * const platform_names[] = { + PLATFORM_NAME(I830), + PLATFORM_NAME(I845G), + PLATFORM_NAME(I85X), + PLATFORM_NAME(I865G), + PLATFORM_NAME(I915G), + PLATFORM_NAME(I915GM), + PLATFORM_NAME(I945G), + PLATFORM_NAME(I945GM), + PLATFORM_NAME(G33), + PLATFORM_NAME(PINEVIEW), + PLATFORM_NAME(I965G), + PLATFORM_NAME(I965GM), + PLATFORM_NAME(G45), + PLATFORM_NAME(GM45), + PLATFORM_NAME(IRONLAKE), + PLATFORM_NAME(SANDYBRIDGE), + PLATFORM_NAME(IVYBRIDGE), + PLATFORM_NAME(VALLEYVIEW), + PLATFORM_NAME(HASWELL), + PLATFORM_NAME(BROADWELL), + PLATFORM_NAME(CHERRYVIEW), + PLATFORM_NAME(SKYLAKE), + PLATFORM_NAME(BROXTON), + PLATFORM_NAME(KABYLAKE), + PLATFORM_NAME(GEMINILAKE), +}; +#undef PLATFORM_NAME + +const char *intel_platform_name(enum intel_platform platform) +{ + if (WARN_ON_ONCE(platform >= ARRAY_SIZE(platform_names) || + platform_names[platform] == NULL)) + return "<unknown>"; + + return platform_names[platform]; +} + void intel_device_info_dump(struct drm_i915_private *dev_priv) { const struct intel_device_info *info = &dev_priv->info; - DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x rev=0x%02x", + DRM_DEBUG_DRIVER("i915 device info: platform=%s gen=%i pciid=0x%04x rev=0x%02x", + intel_platform_name(info->platform), info->gen, dev_priv->drm.pdev->device, dev_priv->drm.pdev->revision); @@ -152,7 +192,7 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv) (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) && hweight8(sseu->slice_mask) > 1; sseu->has_subslice_pg = - IS_BROXTON(dev_priv) && sseu_subslice_total(sseu) > 1; + IS_GEN9_LP(dev_priv) && sseu_subslice_total(sseu) > 1; sseu->has_eu_pg = sseu->eu_per_subslice > 2; if (IS_BROXTON(dev_priv)) { @@ -270,6 +310,12 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv) struct intel_device_info *info = mkwrite_device_info(dev_priv); enum pipe pipe; + if (INTEL_GEN(dev_priv) >= 9) { + info->num_scalers[PIPE_A] = 2; + info->num_scalers[PIPE_B] = 2; + info->num_scalers[PIPE_C] = 1; + } + /* * Skylake and Broxton currently don't expose the topmost plane as its * use is exclusive with the legacy cursor and we only want to expose @@ -278,7 +324,10 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv) * we don't expose the topmost plane at all to prevent ABI breakage * down the line. */ - if (IS_BROXTON(dev_priv)) { + if (IS_GEMINILAKE(dev_priv)) + for_each_pipe(dev_priv, pipe) + info->num_sprites[pipe] = 3; + else if (IS_BROXTON(dev_priv)) { info->num_sprites[PIPE_A] = 2; info->num_sprites[PIPE_B] = 2; info->num_sprites[PIPE_C] = 1; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 891c86aef99d..01341670738f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -115,15 +115,15 @@ static void chv_prepare_pll(struct intel_crtc *crtc, const struct intel_crtc_state *pipe_config); static void intel_begin_crtc_commit(struct drm_crtc *, struct drm_crtc_state *); static void intel_finish_crtc_commit(struct drm_crtc *, struct drm_crtc_state *); -static void skl_init_scalers(struct drm_i915_private *dev_priv, - struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state); +static void intel_crtc_init_scalers(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state); static void skylake_pfit_enable(struct intel_crtc *crtc); static void ironlake_pfit_disable(struct intel_crtc *crtc, bool force); static void ironlake_pfit_enable(struct intel_crtc *crtc); static void intel_modeset_setup_hw_state(struct drm_device *dev); static void intel_pre_disable_primary_noatomic(struct drm_crtc *crtc); static int ilk_max_pixel_rate(struct drm_atomic_state *state); +static int glk_calc_cdclk(int max_pixclk); static int bxt_calc_cdclk(int max_pixclk); struct intel_limit { @@ -614,12 +614,12 @@ static bool intel_PLL_is_valid(struct drm_i915_private *dev_priv, INTELPllInvalid("m1 out of range\n"); if (!IS_PINEVIEW(dev_priv) && !IS_VALLEYVIEW(dev_priv) && - !IS_CHERRYVIEW(dev_priv) && !IS_BROXTON(dev_priv)) + !IS_CHERRYVIEW(dev_priv) && !IS_GEN9_LP(dev_priv)) if (clock->m1 <= clock->m2) INTELPllInvalid("m1 <= m2\n"); if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv) && - !IS_BROXTON(dev_priv)) { + !IS_GEN9_LP(dev_priv)) { if (clock->p < limit->p.min || limit->p.max < clock->p) INTELPllInvalid("p out of range\n"); if (clock->m < limit->m.min || limit->m.max < clock->m) @@ -1232,7 +1232,7 @@ static void assert_cursor(struct drm_i915_private *dev_priv, { bool cur_state; - if (IS_845G(dev_priv) || IS_I865G(dev_priv)) + if (IS_I845G(dev_priv) || IS_I865G(dev_priv)) cur_state = I915_READ(CURCNTR(PIPE_A)) & CURSOR_ENABLE; else cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE; @@ -1327,7 +1327,7 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv, } } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { for_each_sprite(dev_priv, pipe, sprite) { - u32 val = I915_READ(SPCNTR(pipe, sprite)); + u32 val = I915_READ(SPCNTR(pipe, PLANE_SPRITE0 + sprite)); I915_STATE_WARN(val & SP_ENABLE, "sprite %c assertion failure, should be off on pipe %c but is still active\n", sprite_name(pipe, sprite), pipe_name(pipe)); @@ -2137,11 +2137,10 @@ intel_fill_fb_ggtt_view(struct i915_ggtt_view *view, const struct drm_framebuffer *fb, unsigned int rotation) { + view->type = I915_GGTT_VIEW_NORMAL; if (drm_rotation_90_or_270(rotation)) { - *view = i915_ggtt_view_rotated; - view->params.rotated = to_intel_framebuffer(fb)->rot_info; - } else { - *view = i915_ggtt_view_normal; + view->type = I915_GGTT_VIEW_ROTATED; + view->rotated = to_intel_framebuffer(fb)->rot_info; } } @@ -2149,7 +2148,7 @@ static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_pr { if (INTEL_INFO(dev_priv)->gen >= 9) return 256 * 1024; - else if (IS_BROADWATER(dev_priv) || IS_CRESTLINE(dev_priv) || + else if (IS_I965G(dev_priv) || IS_I965GM(dev_priv) || IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) return 128 * 1024; else if (INTEL_INFO(dev_priv)->gen >= 4) @@ -2243,10 +2242,7 @@ err: void intel_unpin_fb_vma(struct i915_vma *vma) { - lockdep_assert_held(&vma->vm->dev->struct_mutex); - - if (WARN_ON_ONCE(!vma)) - return; + lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); i915_vma_unpin_fence(vma); i915_gem_object_unpin_from_display_plane(vma); @@ -2273,7 +2269,7 @@ u32 intel_fb_xy_to_linear(int x, int y, int plane) { const struct drm_framebuffer *fb = state->base.fb; - unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + unsigned int cpp = fb->format->cpp[plane]; unsigned int pitch = fb->pitches[plane]; return y * pitch + x * cpp; @@ -2342,7 +2338,7 @@ static u32 intel_adjust_tile_offset(int *x, int *y, { const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev); const struct drm_framebuffer *fb = state->base.fb; - unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + unsigned int cpp = fb->format->cpp[plane]; unsigned int rotation = state->base.rotation; unsigned int pitch = intel_fb_pitch(fb, plane, rotation); @@ -2398,7 +2394,7 @@ static u32 _intel_compute_tile_offset(const struct drm_i915_private *dev_priv, u32 alignment) { uint64_t fb_modifier = fb->modifier; - unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + unsigned int cpp = fb->format->cpp[plane]; u32 offset, offset_aligned; if (alignment) @@ -2453,7 +2449,7 @@ u32 intel_compute_tile_offset(int *x, int *y, u32 alignment; /* AUX_DIST needs only 4K alignment */ - if (fb->pixel_format == DRM_FORMAT_NV12 && plane == 1) + if (fb->format->format == DRM_FORMAT_NV12 && plane == 1) alignment = 4096; else alignment = intel_surf_alignment(dev_priv, fb->modifier); @@ -2466,7 +2462,7 @@ u32 intel_compute_tile_offset(int *x, int *y, static void intel_fb_offset_to_xy(int *x, int *y, const struct drm_framebuffer *fb, int plane) { - unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + unsigned int cpp = fb->format->cpp[plane]; unsigned int pitch = fb->pitches[plane]; u32 linear_offset = fb->offsets[plane]; @@ -2494,8 +2490,7 @@ intel_fill_fb_info(struct drm_i915_private *dev_priv, struct intel_rotation_info *rot_info = &intel_fb->rot_info; u32 gtt_offset_rotated = 0; unsigned int max_size = 0; - uint32_t format = fb->pixel_format; - int i, num_planes = drm_format_num_planes(format); + int i, num_planes = fb->format->num_planes; unsigned int tile_size = intel_tile_size(dev_priv); for (i = 0; i < num_planes; i++) { @@ -2504,9 +2499,9 @@ intel_fill_fb_info(struct drm_i915_private *dev_priv, u32 offset; int x, y; - cpp = drm_format_plane_cpp(format, i); - width = drm_format_plane_width(fb->width, format, i); - height = drm_format_plane_height(fb->height, format, i); + cpp = fb->format->cpp[i]; + width = drm_framebuffer_plane_width(fb->width, fb, i); + height = drm_framebuffer_plane_height(fb->height, fb, i); intel_fb_offset_to_xy(&x, &y, fb, i); @@ -2688,7 +2683,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, mutex_lock(&dev->struct_mutex); - obj = i915_gem_object_create_stolen_for_preallocated(dev, + obj = i915_gem_object_create_stolen_for_preallocated(dev_priv, base_aligned, base_aligned, size_aligned); @@ -2700,7 +2695,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, if (plane_config->tiling == I915_TILING_X) obj->tiling_and_stride = fb->pitches[0] | I915_TILING_X; - mode_cmd.pixel_format = fb->pixel_format; + mode_cmd.pixel_format = fb->format->format; mode_cmd.width = fb->width; mode_cmd.height = fb->height; mode_cmd.pitches[0] = fb->pitches[0]; @@ -2795,7 +2790,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc, * simplest solution is to just disable the primary plane now and * pretend the BIOS never had it enabled. */ - to_intel_plane_state(plane_state)->base.visible = false; + plane_state->visible = false; crtc_state->plane_mask &= ~(1 << drm_plane_index(primary)); intel_pre_disable_primary_noatomic(&intel_crtc->base); intel_plane->disable_plane(primary, &intel_crtc->base); @@ -2844,7 +2839,7 @@ valid_fb: static int skl_max_plane_width(const struct drm_framebuffer *fb, int plane, unsigned int rotation) { - int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + int cpp = fb->format->cpp[plane]; switch (fb->modifier) { case DRM_FORMAT_MOD_NONE: @@ -2923,7 +2918,7 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state) * TODO: linear and Y-tiled seem fine, Yf untested, */ if (fb->modifier == I915_FORMAT_MOD_X_TILED) { - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + int cpp = fb->format->cpp[0]; while ((x + w) * cpp > fb->pitches[0]) { if (offset == 0) { @@ -2991,7 +2986,7 @@ int skl_check_plane_surface(struct intel_plane_state *plane_state) * Handle the AUX surface first since * the main surface setup depends on it. */ - if (fb->pixel_format == DRM_FORMAT_NV12) { + if (fb->format->format == DRM_FORMAT_NV12) { ret = skl_check_nv12_aux_surface(plane_state); if (ret) return ret; @@ -3046,7 +3041,7 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, I915_WRITE(PRIMCNSTALPHA(plane), 0); } - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_C8: dspcntr |= DISPPLANE_8BPP; break; @@ -3161,7 +3156,7 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) dspcntr |= DISPPLANE_PIPE_CSC_ENABLE; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_C8: dspcntr |= DISPPLANE_8BPP; break; @@ -3275,12 +3270,12 @@ u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane, * linear buffers or in number of tiles for tiled buffers. */ if (drm_rotation_90_or_270(rotation)) { - int cpp = drm_format_plane_cpp(fb->pixel_format, plane); + int cpp = fb->format->cpp[plane]; stride /= intel_tile_height(dev_priv, fb->modifier, cpp); } else { stride /= intel_fb_stride_alignment(dev_priv, fb->modifier, - fb->pixel_format); + fb->format->format); } return stride; @@ -3375,7 +3370,8 @@ static void skylake_update_primary_plane(struct drm_plane *plane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); struct drm_framebuffer *fb = plane_state->base.fb; - int pipe = intel_crtc->pipe; + enum plane_id plane_id = to_intel_plane(plane)->id; + enum pipe pipe = to_intel_plane(plane)->pipe; u32 plane_ctl; unsigned int rotation = plane_state->base.rotation; u32 stride = skl_plane_stride(fb, 0, rotation); @@ -3394,7 +3390,7 @@ static void skylake_update_primary_plane(struct drm_plane *plane, PLANE_CTL_PIPE_GAMMA_ENABLE | PLANE_CTL_PIPE_CSC_ENABLE; - plane_ctl |= skl_plane_ctl_format(fb->pixel_format); + plane_ctl |= skl_plane_ctl_format(fb->format->format); plane_ctl |= skl_plane_ctl_tiling(fb->modifier); plane_ctl |= PLANE_CTL_PLANE_GAMMA_DISABLE; plane_ctl |= skl_plane_ctl_rotation(rotation); @@ -3410,30 +3406,30 @@ static void skylake_update_primary_plane(struct drm_plane *plane, intel_crtc->adjusted_x = src_x; intel_crtc->adjusted_y = src_y; - I915_WRITE(PLANE_CTL(pipe, 0), plane_ctl); - I915_WRITE(PLANE_OFFSET(pipe, 0), (src_y << 16) | src_x); - I915_WRITE(PLANE_STRIDE(pipe, 0), stride); - I915_WRITE(PLANE_SIZE(pipe, 0), (src_h << 16) | src_w); + I915_WRITE(PLANE_CTL(pipe, plane_id), plane_ctl); + I915_WRITE(PLANE_OFFSET(pipe, plane_id), (src_y << 16) | src_x); + I915_WRITE(PLANE_STRIDE(pipe, plane_id), stride); + I915_WRITE(PLANE_SIZE(pipe, plane_id), (src_h << 16) | src_w); if (scaler_id >= 0) { uint32_t ps_ctrl = 0; WARN_ON(!dst_w || !dst_h); - ps_ctrl = PS_SCALER_EN | PS_PLANE_SEL(0) | + ps_ctrl = PS_SCALER_EN | PS_PLANE_SEL(plane_id) | crtc_state->scaler_state.scalers[scaler_id].mode; I915_WRITE(SKL_PS_CTRL(pipe, scaler_id), ps_ctrl); I915_WRITE(SKL_PS_PWR_GATE(pipe, scaler_id), 0); I915_WRITE(SKL_PS_WIN_POS(pipe, scaler_id), (dst_x << 16) | dst_y); I915_WRITE(SKL_PS_WIN_SZ(pipe, scaler_id), (dst_w << 16) | dst_h); - I915_WRITE(PLANE_POS(pipe, 0), 0); + I915_WRITE(PLANE_POS(pipe, plane_id), 0); } else { - I915_WRITE(PLANE_POS(pipe, 0), (dst_y << 16) | dst_x); + I915_WRITE(PLANE_POS(pipe, plane_id), (dst_y << 16) | dst_x); } - I915_WRITE(PLANE_SURF(pipe, 0), + I915_WRITE(PLANE_SURF(pipe, plane_id), intel_plane_ggtt_offset(plane_state) + surf_addr); - POSTING_READ(PLANE_SURF(pipe, 0)); + POSTING_READ(PLANE_SURF(pipe, plane_id)); } static void skylake_disable_primary_plane(struct drm_plane *primary, @@ -3441,12 +3437,12 @@ static void skylake_disable_primary_plane(struct drm_plane *primary, { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; + enum plane_id plane_id = to_intel_plane(primary)->id; + enum pipe pipe = to_intel_plane(primary)->pipe; - I915_WRITE(PLANE_CTL(pipe, 0), 0); - I915_WRITE(PLANE_SURF(pipe, 0), 0); - POSTING_READ(PLANE_SURF(pipe, 0)); + I915_WRITE(PLANE_CTL(pipe, plane_id), 0); + I915_WRITE(PLANE_SURF(pipe, plane_id), 0); + POSTING_READ(PLANE_SURF(pipe, plane_id)); } /* Assume fb object is pinned & idle & fenced and just update base pointers */ @@ -3555,23 +3551,19 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv) state = drm_atomic_helper_duplicate_state(dev, ctx); if (IS_ERR(state)) { ret = PTR_ERR(state); - state = NULL; DRM_ERROR("Duplicating state failed with %i\n", ret); - goto err; + return; } ret = drm_atomic_helper_disable_all(dev, ctx); if (ret) { DRM_ERROR("Suspending crtc's failed with %i\n", ret); - goto err; + drm_atomic_state_put(state); + return; } dev_priv->modeset_restore_state = state; state->acquire_ctx = ctx; - return; - -err: - drm_atomic_state_put(state); } void intel_finish_reset(struct drm_i915_private *dev_priv) @@ -4224,9 +4216,8 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) udelay(100); } -bool intel_has_pending_fb_unpin(struct drm_device *dev) +bool intel_has_pending_fb_unpin(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *crtc; /* Note that we don't need to be called with mode_config.lock here @@ -4236,7 +4227,7 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev) * cannot claim and pin a new fb without at least acquring the * struct_mutex and so serialising with us. */ - for_each_intel_crtc(dev, crtc) { + for_each_intel_crtc(&dev_priv->drm, crtc) { if (atomic_read(&crtc->unpin_work_count) == 0) continue; @@ -4766,7 +4757,7 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, } /* Check src format */ - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_XRGB8888: @@ -4782,7 +4773,7 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, default: DRM_DEBUG_KMS("[PLANE:%d:%s] FB:%d unsupported scaling format 0x%x\n", intel_plane->base.base.id, intel_plane->base.name, - fb->base.id, fb->pixel_format); + fb->base.id, fb->format->format); return -EINVAL; } @@ -5017,11 +5008,9 @@ intel_pre_disable_primary_noatomic(struct drm_crtc *crtc) * event which is after the vblank start event, so we need to have a * wait-for-vblank between disabling the plane and the pipe. */ - if (HAS_GMCH_DISPLAY(dev_priv)) { - intel_set_memory_cxsr(dev_priv, false); - dev_priv->wm.vlv.cxsr = false; + if (HAS_GMCH_DISPLAY(dev_priv) && + intel_set_memory_cxsr(dev_priv, false)) intel_wait_for_vblank(dev_priv, pipe); - } } static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state) @@ -5096,11 +5085,9 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state) * event which is after the vblank start event, so we need to have a * wait-for-vblank between disabling the plane and the pipe. */ - if (old_crtc_state->base.active) { - intel_set_memory_cxsr(dev_priv, false); - dev_priv->wm.vlv.cxsr = false; + if (old_crtc_state->base.active && + intel_set_memory_cxsr(dev_priv, false)) intel_wait_for_vblank(dev_priv, crtc->pipe); - } } /* @@ -5110,10 +5097,8 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state) * * WaCxSRDisabledForSpriteScaling:ivb */ - if (pipe_config->disable_lp_wm) { - ilk_disable_lp_wm(dev); + if (pipe_config->disable_lp_wm && ilk_disable_lp_wm(dev)) intel_wait_for_vblank(dev_priv, crtc->pipe); - } /* * If we're doing a modeset, we're done. No need to do any pre-vblank @@ -5461,10 +5446,7 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, intel_ddi_enable_transcoder_func(crtc); if (dev_priv->display.initial_watermarks != NULL) - dev_priv->display.initial_watermarks(old_intel_state, - pipe_config); - else - intel_update_watermarks(intel_crtc); + dev_priv->display.initial_watermarks(old_intel_state, pipe_config); /* XXX: Do the pipe assertions at the right place for BXT DSI. */ if (!transcoder_is_dsi(cpu_transcoder)) @@ -5801,8 +5783,10 @@ static int intel_compute_max_dotclk(struct drm_i915_private *dev_priv) { int max_cdclk_freq = dev_priv->max_cdclk_freq; - if (INTEL_INFO(dev_priv)->gen >= 9 || - IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) + if (IS_GEMINILAKE(dev_priv)) + return 2 * max_cdclk_freq; + else if (INTEL_INFO(dev_priv)->gen >= 9 || + IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) return max_cdclk_freq; else if (IS_CHERRYVIEW(dev_priv)) return max_cdclk_freq*95/100; @@ -5838,6 +5822,8 @@ static void intel_update_max_cdclk(struct drm_i915_private *dev_priv) max_cdclk = 308571; dev_priv->max_cdclk_freq = skl_calc_cdclk(max_cdclk, vco); + } else if (IS_GEMINILAKE(dev_priv)) { + dev_priv->max_cdclk_freq = 316800; } else if (IS_BROXTON(dev_priv)) { dev_priv->max_cdclk_freq = 624000; } else if (IS_BROADWELL(dev_priv)) { @@ -5925,6 +5911,26 @@ static int bxt_de_pll_vco(struct drm_i915_private *dev_priv, int cdclk) return dev_priv->cdclk_pll.ref * ratio; } +static int glk_de_pll_vco(struct drm_i915_private *dev_priv, int cdclk) +{ + int ratio; + + if (cdclk == dev_priv->cdclk_pll.ref) + return 0; + + switch (cdclk) { + default: + MISSING_CASE(cdclk); + case 79200: + case 158400: + case 316800: + ratio = 33; + break; + } + + return dev_priv->cdclk_pll.ref * ratio; +} + static void bxt_de_pll_disable(struct drm_i915_private *dev_priv) { I915_WRITE(BXT_DE_PLL_ENABLE, 0); @@ -5966,7 +5972,10 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv, int cdclk) u32 val, divider; int vco, ret; - vco = bxt_de_pll_vco(dev_priv, cdclk); + if (IS_GEMINILAKE(dev_priv)) + vco = glk_de_pll_vco(dev_priv, cdclk); + else + vco = bxt_de_pll_vco(dev_priv, cdclk); DRM_DEBUG_DRIVER("Changing CDCLK to %d kHz (VCO %d kHz)\n", cdclk, vco); @@ -5979,6 +5988,7 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv, int cdclk) divider = BXT_CDCLK_CD2X_DIV_SEL_2; break; case 3: + WARN(IS_GEMINILAKE(dev_priv), "Unsupported divider\n"); divider = BXT_CDCLK_CD2X_DIV_SEL_1_5; break; case 2: @@ -6088,6 +6098,8 @@ sanitize: void bxt_init_cdclk(struct drm_i915_private *dev_priv) { + int cdclk; + bxt_sanitize_cdclk(dev_priv); if (dev_priv->cdclk_freq != 0 && dev_priv->cdclk_pll.vco != 0) @@ -6098,7 +6110,12 @@ void bxt_init_cdclk(struct drm_i915_private *dev_priv) * - The initial CDCLK needs to be read from VBT. * Need to make this change after VBT has changes for BXT. */ - bxt_set_cdclk(dev_priv, bxt_calc_cdclk(0)); + if (IS_GEMINILAKE(dev_priv)) + cdclk = glk_calc_cdclk(0); + else + cdclk = bxt_calc_cdclk(0); + + bxt_set_cdclk(dev_priv, cdclk); } void bxt_uninit_cdclk(struct drm_i915_private *dev_priv) @@ -6513,6 +6530,16 @@ static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, return 200000; } +static int glk_calc_cdclk(int max_pixclk) +{ + if (max_pixclk > 2 * 158400) + return 316800; + else if (max_pixclk > 2 * 79200) + return 158400; + else + return 79200; +} + static int bxt_calc_cdclk(int max_pixclk) { if (max_pixclk > 576000) @@ -6575,15 +6602,27 @@ static int valleyview_modeset_calc_cdclk(struct drm_atomic_state *state) static int bxt_modeset_calc_cdclk(struct drm_atomic_state *state) { + struct drm_i915_private *dev_priv = to_i915(state->dev); int max_pixclk = ilk_max_pixel_rate(state); struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + int cdclk; - intel_state->cdclk = intel_state->dev_cdclk = - bxt_calc_cdclk(max_pixclk); + if (IS_GEMINILAKE(dev_priv)) + cdclk = glk_calc_cdclk(max_pixclk); + else + cdclk = bxt_calc_cdclk(max_pixclk); - if (!intel_state->active_crtcs) - intel_state->dev_cdclk = bxt_calc_cdclk(0); + intel_state->cdclk = intel_state->dev_cdclk = cdclk; + + if (!intel_state->active_crtcs) { + if (IS_GEMINILAKE(dev_priv)) + cdclk = glk_calc_cdclk(0); + else + cdclk = bxt_calc_cdclk(0); + + intel_state->dev_cdclk = cdclk; + } return 0; } @@ -6833,13 +6872,13 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc) if (!intel_crtc->active) return; - if (to_intel_plane_state(crtc->primary->state)->base.visible) { + if (crtc->primary->state->visible) { WARN_ON(intel_crtc->flip_work); intel_pre_disable_primary_noatomic(crtc); intel_crtc_disable_planes(crtc, 1 << drm_plane_index(crtc->primary)); - to_intel_plane_state(crtc->primary->state)->base.visible = false; + crtc->primary->state->visible = false; } state = drm_atomic_state_alloc(crtc->dev); @@ -7291,6 +7330,7 @@ static int broxton_get_display_clock_speed(struct drm_i915_private *dev_priv) div = 2; break; case BXT_CDCLK_CD2X_DIV_SEL_1_5: + WARN(IS_GEMINILAKE(dev_priv), "Unsupported divider\n"); div = 3; break; case BXT_CDCLK_CD2X_DIV_SEL_2: @@ -7510,7 +7550,7 @@ static unsigned int intel_hpll_vco(struct drm_i915_private *dev_priv) vco_table = ctg_vco; else if (IS_G4X(dev_priv)) vco_table = elk_vco; - else if (IS_CRESTLINE(dev_priv)) + else if (IS_I965GM(dev_priv)) vco_table = cl_vco; else if (IS_PINEVIEW(dev_priv)) vco_table = pnv_vco; @@ -8122,7 +8162,8 @@ static void i9xx_compute_dpll(struct intel_crtc *crtc, else dpll |= DPLLB_MODE_DAC_SERIAL; - if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || IS_G33(dev_priv)) { + if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || + IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) { dpll |= (crtc_state->pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; } @@ -8357,7 +8398,6 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode, mode->type = DRM_MODE_TYPE_DRIVER; mode->clock = pipe_config->base.adjusted_mode.crtc_clock; - mode->flags |= pipe_config->base.adjusted_mode.flags; mode->hsync = drm_mode_hsync(mode); mode->vrefresh = drm_mode_vrefresh(mode); @@ -8696,6 +8736,8 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, fb = &intel_fb->base; + fb->dev = dev; + if (INTEL_GEN(dev_priv) >= 4) { if (val & DISPPLANE_TILED) { plane_config->tiling = I915_TILING_X; @@ -8705,8 +8747,7 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, pixel_format = val & DISPPLANE_PIXFORMAT_MASK; fourcc = i9xx_format_to_fourcc(pixel_format); - fb->pixel_format = fourcc; - fb->bits_per_pixel = drm_format_plane_cpp(fourcc, 0) * 8; + fb->format = drm_format_info(fourcc); if (INTEL_GEN(dev_priv) >= 4) { if (plane_config->tiling) @@ -8727,14 +8768,14 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, fb->pitches[0] = val & 0xffffffc0; aligned_height = intel_fb_align_height(dev, fb->height, - fb->pixel_format, + fb->format->format, fb->modifier); plane_config->size = fb->pitches[0] * aligned_height; DRM_DEBUG_KMS("pipe/plane %c/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe_name(pipe), plane, fb->width, fb->height, - fb->bits_per_pixel, base, fb->pitches[0], + fb->format->cpp[0] * 8, base, fb->pitches[0], plane_config->size); plane_config->fb = intel_fb; @@ -8835,7 +8876,7 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, >> DPLL_MD_UDI_MULTIPLIER_SHIFT) + 1; pipe_config->dpll_hw_state.dpll_md = tmp; } else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || - IS_G33(dev_priv)) { + IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) { tmp = I915_READ(DPLL(crtc->pipe)); pipe_config->pixel_multiplier = ((tmp & SDVO_MULTIPLIER_MASK) @@ -8888,9 +8929,8 @@ out: return ret; } -static void ironlake_init_pch_refclk(struct drm_device *dev) +static void ironlake_init_pch_refclk(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_encoder *encoder; int i; u32 val, final; @@ -8902,7 +8942,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) bool using_ssc_source = false; /* We need to take the global config into account */ - for_each_intel_encoder(dev, encoder) { + for_each_intel_encoder(&dev_priv->drm, encoder) { switch (encoder->type) { case INTEL_OUTPUT_LVDS: has_panel = true; @@ -9158,10 +9198,9 @@ static void lpt_program_fdi_mphy(struct drm_i915_private *dev_priv) * - Sequence to enable CLKOUT_DP without spread * - Sequence to enable CLKOUT_DP for FDI usage and configure PCH FDI I/O */ -static void lpt_enable_clkout_dp(struct drm_device *dev, bool with_spread, - bool with_fdi) +static void lpt_enable_clkout_dp(struct drm_i915_private *dev_priv, + bool with_spread, bool with_fdi) { - struct drm_i915_private *dev_priv = to_i915(dev); uint32_t reg, tmp; if (WARN(with_fdi && !with_spread, "FDI requires downspread\n")) @@ -9199,9 +9238,8 @@ static void lpt_enable_clkout_dp(struct drm_device *dev, bool with_spread, } /* Sequence to disable CLKOUT_DP */ -static void lpt_disable_clkout_dp(struct drm_device *dev) +static void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); uint32_t reg, tmp; mutex_lock(&dev_priv->sb_lock); @@ -9286,12 +9324,12 @@ static void lpt_bend_clkout_dp(struct drm_i915_private *dev_priv, int steps) #undef BEND_IDX -static void lpt_init_pch_refclk(struct drm_device *dev) +static void lpt_init_pch_refclk(struct drm_i915_private *dev_priv) { struct intel_encoder *encoder; bool has_vga = false; - for_each_intel_encoder(dev, encoder) { + for_each_intel_encoder(&dev_priv->drm, encoder) { switch (encoder->type) { case INTEL_OUTPUT_ANALOG: has_vga = true; @@ -9302,24 +9340,22 @@ static void lpt_init_pch_refclk(struct drm_device *dev) } if (has_vga) { - lpt_bend_clkout_dp(to_i915(dev), 0); - lpt_enable_clkout_dp(dev, true, true); + lpt_bend_clkout_dp(dev_priv, 0); + lpt_enable_clkout_dp(dev_priv, true, true); } else { - lpt_disable_clkout_dp(dev); + lpt_disable_clkout_dp(dev_priv); } } /* * Initialize reference clocks when the driver loads */ -void intel_init_pch_refclk(struct drm_device *dev) +void intel_init_pch_refclk(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv)) - ironlake_init_pch_refclk(dev); + ironlake_init_pch_refclk(dev_priv); else if (HAS_PCH_LPT(dev_priv)) - lpt_init_pch_refclk(dev); + lpt_init_pch_refclk(dev_priv); } static void ironlake_set_pipeconf(struct drm_crtc *crtc) @@ -9726,6 +9762,8 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, fb = &intel_fb->base; + fb->dev = dev; + val = I915_READ(PLANE_CTL(pipe, 0)); if (!(val & PLANE_CTL_ENABLE)) goto error; @@ -9734,8 +9772,7 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, fourcc = skl_format_to_fourcc(pixel_format, val & PLANE_CTL_ORDER_RGBX, val & PLANE_CTL_ALPHA_MASK); - fb->pixel_format = fourcc; - fb->bits_per_pixel = drm_format_plane_cpp(fourcc, 0) * 8; + fb->format = drm_format_info(fourcc); tiling = val & PLANE_CTL_TILED_MASK; switch (tiling) { @@ -9768,18 +9805,18 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, val = I915_READ(PLANE_STRIDE(pipe, 0)); stride_mult = intel_fb_stride_alignment(dev_priv, fb->modifier, - fb->pixel_format); + fb->format->format); fb->pitches[0] = (val & 0x3ff) * stride_mult; aligned_height = intel_fb_align_height(dev, fb->height, - fb->pixel_format, + fb->format->format, fb->modifier); plane_config->size = fb->pitches[0] * aligned_height; DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe_name(pipe), fb->width, fb->height, - fb->bits_per_pixel, base, fb->pitches[0], + fb->format->cpp[0] * 8, base, fb->pitches[0], plane_config->size); plane_config->fb = intel_fb; @@ -9838,6 +9875,8 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, fb = &intel_fb->base; + fb->dev = dev; + if (INTEL_GEN(dev_priv) >= 4) { if (val & DISPPLANE_TILED) { plane_config->tiling = I915_TILING_X; @@ -9847,8 +9886,7 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, pixel_format = val & DISPPLANE_PIXFORMAT_MASK; fourcc = i9xx_format_to_fourcc(pixel_format); - fb->pixel_format = fourcc; - fb->bits_per_pixel = drm_format_plane_cpp(fourcc, 0) * 8; + fb->format = drm_format_info(fourcc); base = I915_READ(DSPSURF(pipe)) & 0xfffff000; if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { @@ -9869,14 +9907,14 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, fb->pitches[0] = val & 0xffffffc0; aligned_height = intel_fb_align_height(dev, fb->height, - fb->pixel_format, + fb->format->format, fb->modifier); plane_config->size = fb->pitches[0] * aligned_height; DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", pipe_name(pipe), fb->width, fb->height, - fb->bits_per_pixel, base, fb->pitches[0], + fb->format->cpp[0] * 8, base, fb->pitches[0], plane_config->size); plane_config->fb = intel_fb; @@ -10166,7 +10204,6 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv) */ void hsw_enable_pc8(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; uint32_t val; DRM_DEBUG_KMS("Enabling package C8+\n"); @@ -10177,19 +10214,18 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv) I915_WRITE(SOUTH_DSPCLK_GATE_D, val); } - lpt_disable_clkout_dp(dev); + lpt_disable_clkout_dp(dev_priv); hsw_disable_lcpll(dev_priv, true, true); } void hsw_disable_pc8(struct drm_i915_private *dev_priv) { - struct drm_device *dev = &dev_priv->drm; uint32_t val; DRM_DEBUG_KMS("Disabling package C8+\n"); hsw_restore_lcpll(dev_priv); - lpt_init_pch_refclk(dev); + lpt_init_pch_refclk(dev_priv); if (HAS_PCH_LPT_LP(dev_priv)) { val = I915_READ(SOUTH_DSPCLK_GATE_D); @@ -10639,7 +10675,7 @@ static void haswell_get_ddi_port_state(struct intel_crtc *crtc, if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) skylake_get_ddi_pll(dev_priv, port, pipe_config); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_get_ddi_pll(dev_priv, port, pipe_config); else haswell_get_ddi_pll(dev_priv, port, pipe_config); @@ -10684,7 +10720,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, active = hsw_get_transcoder_state(crtc, pipe_config, &power_domain_mask); - if (IS_BROXTON(dev_priv) && + if (IS_GEN9_LP(dev_priv) && bxt_get_dsi_transcoder_state(crtc, pipe_config, &power_domain_mask)) { WARN_ON(active); active = true; @@ -10704,7 +10740,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, I915_READ(GAMMA_MODE(crtc->pipe)) & GAMMA_MODE_MODE_MASK; if (INTEL_GEN(dev_priv) >= 9) { - skl_init_scalers(dev_priv, crtc, pipe_config); + intel_crtc_init_scalers(crtc, pipe_config); pipe_config->scaler_state.scaler_id = -1; pipe_config->scaler_state.scaler_users &= ~(1 << SKL_CRTC_INDEX); @@ -10885,7 +10921,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, I915_WRITE(CURPOS(pipe), pos); - if (IS_845G(dev_priv) || IS_I865G(dev_priv)) + if (IS_I845G(dev_priv) || IS_I865G(dev_priv)) i845_update_cursor(crtc, base, plane_state); else i9xx_update_cursor(crtc, base, plane_state); @@ -10903,11 +10939,11 @@ static bool cursor_size_ok(struct drm_i915_private *dev_priv, * the precision of the register. Everything else requires * square cursors, limited to a few power-of-two sizes. */ - if (IS_845G(dev_priv) || IS_I865G(dev_priv)) { + if (IS_I845G(dev_priv) || IS_I865G(dev_priv)) { if ((width & 63) != 0) return false; - if (width > (IS_845G(dev_priv) ? 64 : 512)) + if (width > (IS_I845G(dev_priv) ? 64 : 512)) return false; if (height > 1023) @@ -10997,7 +11033,7 @@ intel_framebuffer_create_for_mode(struct drm_device *dev, struct drm_i915_gem_object *obj; struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - obj = i915_gem_object_create(dev, + obj = i915_gem_object_create(to_i915(dev), intel_framebuffer_size_for_mode(mode, bpp)); if (IS_ERR(obj)) return ERR_CAST(obj); @@ -11035,7 +11071,7 @@ mode_fits_in_fbdev(struct drm_device *dev, fb = &dev_priv->fbdev->fb->base; if (fb->pitches[0] < intel_framebuffer_pitch_for_width(mode->hdisplay, - fb->bits_per_pixel)) + fb->format->cpp[0] * 8)) return NULL; if (obj->base.size < mode->vdisplay * fb->pitches[0]) @@ -11063,7 +11099,7 @@ static int intel_modeset_setup_plane_state(struct drm_atomic_state *state, return PTR_ERR(plane_state); if (mode) - drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay); + drm_mode_get_hv_timing(mode, &hdisplay, &vdisplay); else hdisplay = vdisplay = 0; @@ -12138,7 +12174,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, return -EBUSY; /* Can't change pixel format via MI display flips. */ - if (fb->pixel_format != crtc->primary->fb->pixel_format) + if (fb->format != crtc->primary->fb->format) return -EINVAL; /* @@ -12257,7 +12293,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, INIT_WORK(&work->mmio_work, intel_mmio_flip_work_func); queue_work(system_unbound_wq, &work->mmio_work); } else { - request = i915_gem_request_alloc(engine, engine->last_context); + request = i915_gem_request_alloc(engine, + dev_priv->kernel_context); if (IS_ERR(request)) { ret = PTR_ERR(request); goto cleanup_unpin; @@ -12424,7 +12461,7 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, } was_visible = old_plane_state->base.visible; - visible = to_intel_plane_state(plane_state)->base.visible; + visible = plane_state->visible; if (!was_crtc_enabled && WARN_ON(was_visible)) was_visible = false; @@ -12440,7 +12477,7 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, * only combine the results from all planes in the current place? */ if (!is_crtc_enabled) - to_intel_plane_state(plane_state)->base.visible = visible = false; + plane_state->visible = visible = false; if (!was_visible && !visible) return 0; @@ -12786,39 +12823,7 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, DRM_DEBUG_KMS("ips: %i, double wide: %i\n", pipe_config->ips_enabled, pipe_config->double_wide); - if (IS_BROXTON(dev_priv)) { - DRM_DEBUG_KMS("dpll_hw_state: ebb0: 0x%x, ebb4: 0x%x," - "pll0: 0x%x, pll1: 0x%x, pll2: 0x%x, pll3: 0x%x, " - "pll6: 0x%x, pll8: 0x%x, pll9: 0x%x, pll10: 0x%x, pcsdw12: 0x%x\n", - pipe_config->dpll_hw_state.ebb0, - pipe_config->dpll_hw_state.ebb4, - pipe_config->dpll_hw_state.pll0, - pipe_config->dpll_hw_state.pll1, - pipe_config->dpll_hw_state.pll2, - pipe_config->dpll_hw_state.pll3, - pipe_config->dpll_hw_state.pll6, - pipe_config->dpll_hw_state.pll8, - pipe_config->dpll_hw_state.pll9, - pipe_config->dpll_hw_state.pll10, - pipe_config->dpll_hw_state.pcsdw12); - } else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { - DRM_DEBUG_KMS("dpll_hw_state: " - "ctrl1: 0x%x, cfgcr1: 0x%x, cfgcr2: 0x%x\n", - pipe_config->dpll_hw_state.ctrl1, - pipe_config->dpll_hw_state.cfgcr1, - pipe_config->dpll_hw_state.cfgcr2); - } else if (HAS_DDI(dev_priv)) { - DRM_DEBUG_KMS("dpll_hw_state: wrpll: 0x%x spll: 0x%x\n", - pipe_config->dpll_hw_state.wrpll, - pipe_config->dpll_hw_state.spll); - } else { - DRM_DEBUG_KMS("dpll_hw_state: dpll: 0x%x, dpll_md: 0x%x, " - "fp0: 0x%x, fp1: 0x%x\n", - pipe_config->dpll_hw_state.dpll, - pipe_config->dpll_hw_state.dpll_md, - pipe_config->dpll_hw_state.fp0, - pipe_config->dpll_hw_state.fp1); - } + intel_dpll_dump_hw_state(dev_priv, &pipe_config->dpll_hw_state); DRM_DEBUG_KMS("planes on this crtc\n"); list_for_each_entry(plane, &dev->mode_config.plane_list, head) { @@ -12838,7 +12843,7 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, DRM_DEBUG_KMS("[PLANE:%d:%s] FB:%d, fb = %ux%u format = %s\n", plane->base.id, plane->name, fb->base.id, fb->width, fb->height, - drm_get_format_name(fb->pixel_format, &format_name)); + drm_get_format_name(fb->format->format, &format_name)); if (INTEL_GEN(dev_priv) >= 9) DRM_DEBUG_KMS("\tscaler:%d src %dx%d+%d+%d dst %dx%d+%d+%d\n", state->scaler_id, @@ -12983,7 +12988,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, * computation to clearly distinguish it from the adjusted mode, which * can be changed by the connectors in the below retry loop. */ - drm_crtc_get_hv_timing(&pipe_config->base.mode, + drm_mode_get_hv_timing(&pipe_config->base.mode, &pipe_config->pipe_src_w, &pipe_config->pipe_src_h); @@ -13162,6 +13167,31 @@ intel_compare_link_m_n(const struct intel_link_m_n *m_n, return false; } +static void __printf(3, 4) +pipe_config_err(bool adjust, const char *name, const char *format, ...) +{ + char *level; + unsigned int category; + struct va_format vaf; + va_list args; + + if (adjust) { + level = KERN_DEBUG; + category = DRM_UT_KMS; + } else { + level = KERN_ERR; + category = DRM_UT_NONE; + } + + va_start(args, format); + vaf.fmt = format; + vaf.va = &args; + + drm_printk(level, category, "mismatch in %s %pV", name, &vaf); + + va_end(args); +} + static bool intel_pipe_config_compare(struct drm_i915_private *dev_priv, struct intel_crtc_state *current_config, @@ -13170,17 +13200,9 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, { bool ret = true; -#define INTEL_ERR_OR_DBG_KMS(fmt, ...) \ - do { \ - if (!adjust) \ - DRM_ERROR(fmt, ##__VA_ARGS__); \ - else \ - DRM_DEBUG_KMS(fmt, ##__VA_ARGS__); \ - } while (0) - #define PIPE_CONF_CHECK_X(name) \ if (current_config->name != pipe_config->name) { \ - INTEL_ERR_OR_DBG_KMS("mismatch in " #name " " \ + pipe_config_err(adjust, __stringify(name), \ "(expected 0x%08x, found 0x%08x)\n", \ current_config->name, \ pipe_config->name); \ @@ -13189,7 +13211,7 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, #define PIPE_CONF_CHECK_I(name) \ if (current_config->name != pipe_config->name) { \ - INTEL_ERR_OR_DBG_KMS("mismatch in " #name " " \ + pipe_config_err(adjust, __stringify(name), \ "(expected %i, found %i)\n", \ current_config->name, \ pipe_config->name); \ @@ -13198,7 +13220,7 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, #define PIPE_CONF_CHECK_P(name) \ if (current_config->name != pipe_config->name) { \ - INTEL_ERR_OR_DBG_KMS("mismatch in " #name " " \ + pipe_config_err(adjust, __stringify(name), \ "(expected %p, found %p)\n", \ current_config->name, \ pipe_config->name); \ @@ -13209,7 +13231,7 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, if (!intel_compare_link_m_n(¤t_config->name, \ &pipe_config->name,\ adjust)) { \ - INTEL_ERR_OR_DBG_KMS("mismatch in " #name " " \ + pipe_config_err(adjust, __stringify(name), \ "(expected tu %i gmch %i/%i link %i/%i, " \ "found tu %i, gmch %i/%i link %i/%i)\n", \ current_config->name.tu, \ @@ -13235,7 +13257,7 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, &pipe_config->name, adjust) && \ !intel_compare_link_m_n(¤t_config->alt_name, \ &pipe_config->name, adjust)) { \ - INTEL_ERR_OR_DBG_KMS("mismatch in " #name " " \ + pipe_config_err(adjust, __stringify(name), \ "(expected tu %i gmch %i/%i link %i/%i, " \ "or tu %i gmch %i/%i link %i/%i, " \ "found tu %i, gmch %i/%i link %i/%i)\n", \ @@ -13259,8 +13281,9 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, #define PIPE_CONF_CHECK_FLAGS(name, mask) \ if ((current_config->name ^ pipe_config->name) & (mask)) { \ - INTEL_ERR_OR_DBG_KMS("mismatch in " #name "(" #mask ") " \ - "(expected %i, found %i)\n", \ + pipe_config_err(adjust, __stringify(name), \ + "(%x) (expected %i, found %i)\n", \ + (mask), \ current_config->name & (mask), \ pipe_config->name & (mask)); \ ret = false; \ @@ -13268,7 +13291,7 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, #define PIPE_CONF_CHECK_CLOCK_FUZZY(name) \ if (!intel_fuzzy_clock_check(current_config->name, pipe_config->name)) { \ - INTEL_ERR_OR_DBG_KMS("mismatch in " #name " " \ + pipe_config_err(adjust, __stringify(name), \ "(expected %i, found %i)\n", \ current_config->name, \ pipe_config->name); \ @@ -13385,7 +13408,6 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, #undef PIPE_CONF_CHECK_FLAGS #undef PIPE_CONF_CHECK_CLOCK_FUZZY #undef PIPE_CONF_QUIRK -#undef INTEL_ERR_OR_DBG_KMS return ret; } @@ -13686,9 +13708,9 @@ verify_single_dpll_state(struct drm_i915_private *dev_priv, } if (!crtc) { - I915_STATE_WARN(pll->active_mask & ~pll->config.crtc_mask, + I915_STATE_WARN(pll->active_mask & ~pll->state.crtc_mask, "more active pll users than references: %x vs %x\n", - pll->active_mask, pll->config.crtc_mask); + pll->active_mask, pll->state.crtc_mask); return; } @@ -13704,11 +13726,11 @@ verify_single_dpll_state(struct drm_i915_private *dev_priv, "pll active mismatch (didn't expect pipe %c in active mask 0x%02x)\n", pipe_name(drm_crtc_index(crtc)), pll->active_mask); - I915_STATE_WARN(!(pll->config.crtc_mask & crtc_mask), + I915_STATE_WARN(!(pll->state.crtc_mask & crtc_mask), "pll enabled crtcs mismatch (expected 0x%x in 0x%02x)\n", - crtc_mask, pll->config.crtc_mask); + crtc_mask, pll->state.crtc_mask); - I915_STATE_WARN(pll->on && memcmp(&pll->config.hw_state, + I915_STATE_WARN(pll->on && memcmp(&pll->state.hw_state, &dpll_hw_state, sizeof(dpll_hw_state)), "pll hw state mismatch\n"); @@ -13734,7 +13756,7 @@ verify_shared_dpll_state(struct drm_device *dev, struct drm_crtc *crtc, I915_STATE_WARN(pll->active_mask & crtc_mask, "pll active mismatch (didn't expect pipe %c in active mask)\n", pipe_name(drm_crtc_index(crtc))); - I915_STATE_WARN(pll->config.crtc_mask & crtc_mask, + I915_STATE_WARN(pll->state.crtc_mask & crtc_mask, "pll enabled crtcs mismatch (found %x in enabled mask)\n", pipe_name(drm_crtc_index(crtc))); } @@ -13817,7 +13839,6 @@ static void intel_modeset_clear_plls(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_shared_dpll_config *shared_dpll = NULL; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; int i; @@ -13838,10 +13859,7 @@ static void intel_modeset_clear_plls(struct drm_atomic_state *state) if (!old_dpll) continue; - if (!shared_dpll) - shared_dpll = intel_atomic_get_shared_dpll_state(state); - - intel_shared_dpll_config_put(shared_dpll, old_dpll, intel_crtc); + intel_release_shared_dpll(old_dpll, intel_crtc, state); } } @@ -13910,14 +13928,34 @@ static int haswell_mode_set_planes_workaround(struct drm_atomic_state *state) return 0; } +static int intel_lock_all_pipes(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + + /* Add all pipes to the state */ + for_each_crtc(state->dev, crtc) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + } + + return 0; +} + static int intel_modeset_all_pipes(struct drm_atomic_state *state) { struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; - int ret = 0; - /* add all active pipes to the state */ + /* + * Add all pipes to the state, and force + * a modeset on all the active ones. + */ for_each_crtc(state->dev, crtc) { + struct drm_crtc_state *crtc_state; + int ret; + crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); @@ -13929,14 +13967,14 @@ static int intel_modeset_all_pipes(struct drm_atomic_state *state) ret = drm_atomic_add_affected_connectors(state, crtc); if (ret) - break; + return ret; ret = drm_atomic_add_affected_planes(state, crtc); if (ret) - break; + return ret; } - return ret; + return 0; } static int intel_modeset_checks(struct drm_atomic_state *state) @@ -13982,12 +14020,24 @@ static int intel_modeset_checks(struct drm_atomic_state *state) if (ret < 0) return ret; + /* + * Writes to dev_priv->atomic_cdclk_freq must protected by + * holding all the crtc locks, even if we don't end up + * touching the hardware + */ + if (intel_state->cdclk != dev_priv->atomic_cdclk_freq) { + ret = intel_lock_all_pipes(state); + if (ret < 0) + return ret; + } + + /* All pipes must be switched off while we change the cdclk. */ if (intel_state->dev_cdclk != dev_priv->cdclk_freq || - intel_state->cdclk_pll_vco != dev_priv->cdclk_pll.vco) + intel_state->cdclk_pll_vco != dev_priv->cdclk_pll.vco) { ret = intel_modeset_all_pipes(state); - - if (ret < 0) - return ret; + if (ret < 0) + return ret; + } DRM_DEBUG_KMS("New cdclk calculated to be atomic %u, actual %u\n", intel_state->cdclk, intel_state->dev_cdclk); @@ -14581,7 +14631,7 @@ static int intel_atomic_commit(struct drm_device *dev, drm_atomic_helper_swap_state(state, true); dev_priv->wm.distrust_bios_wm = false; - intel_shared_dpll_commit(state); + intel_shared_dpll_swap_state(state); intel_atomic_track_fbs(state); if (intel_state->modeset) { @@ -14691,6 +14741,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .page_flip = intel_crtc_page_flip, .atomic_duplicate_state = intel_crtc_duplicate_state, .atomic_destroy_state = intel_crtc_destroy_state, + .set_crc_source = intel_crtc_set_crc_source, }; /** @@ -14949,6 +15000,141 @@ const struct drm_plane_funcs intel_plane_funcs = { .atomic_destroy_state = intel_plane_destroy_state, }; +static int +intel_legacy_cursor_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->dev); + int ret; + struct drm_plane_state *old_plane_state, *new_plane_state; + struct intel_plane *intel_plane = to_intel_plane(plane); + struct drm_framebuffer *old_fb; + struct drm_crtc_state *crtc_state = crtc->state; + struct i915_vma *old_vma; + + /* + * When crtc is inactive or there is a modeset pending, + * wait for it to complete in the slowpath + */ + if (!crtc_state->active || needs_modeset(crtc_state) || + to_intel_crtc_state(crtc_state)->update_pipe) + goto slow; + + old_plane_state = plane->state; + + /* + * If any parameters change that may affect watermarks, + * take the slowpath. Only changing fb or position should be + * in the fastpath. + */ + if (old_plane_state->crtc != crtc || + old_plane_state->src_w != src_w || + old_plane_state->src_h != src_h || + old_plane_state->crtc_w != crtc_w || + old_plane_state->crtc_h != crtc_h || + !old_plane_state->visible || + old_plane_state->fb->modifier != fb->modifier) + goto slow; + + new_plane_state = intel_plane_duplicate_state(plane); + if (!new_plane_state) + return -ENOMEM; + + drm_atomic_set_fb_for_plane(new_plane_state, fb); + + new_plane_state->src_x = src_x; + new_plane_state->src_y = src_y; + new_plane_state->src_w = src_w; + new_plane_state->src_h = src_h; + new_plane_state->crtc_x = crtc_x; + new_plane_state->crtc_y = crtc_y; + new_plane_state->crtc_w = crtc_w; + new_plane_state->crtc_h = crtc_h; + + ret = intel_plane_atomic_check_with_state(to_intel_crtc_state(crtc->state), + to_intel_plane_state(new_plane_state)); + if (ret) + goto out_free; + + /* Visibility changed, must take slowpath. */ + if (!new_plane_state->visible) + goto slow_free; + + ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex); + if (ret) + goto out_free; + + if (INTEL_INFO(dev_priv)->cursor_needs_physical) { + int align = IS_I830(dev_priv) ? 16 * 1024 : 256; + + ret = i915_gem_object_attach_phys(intel_fb_obj(fb), align); + if (ret) { + DRM_DEBUG_KMS("failed to attach phys object\n"); + goto out_unlock; + } + } else { + struct i915_vma *vma; + + vma = intel_pin_and_fence_fb_obj(fb, new_plane_state->rotation); + if (IS_ERR(vma)) { + DRM_DEBUG_KMS("failed to pin object\n"); + + ret = PTR_ERR(vma); + goto out_unlock; + } + + to_intel_plane_state(new_plane_state)->vma = vma; + } + + old_fb = old_plane_state->fb; + old_vma = to_intel_plane_state(old_plane_state)->vma; + + i915_gem_track_fb(intel_fb_obj(old_fb), intel_fb_obj(fb), + intel_plane->frontbuffer_bit); + + /* Swap plane state */ + new_plane_state->fence = old_plane_state->fence; + *to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state); + new_plane_state->fence = NULL; + new_plane_state->fb = old_fb; + to_intel_plane_state(new_plane_state)->vma = old_vma; + + intel_plane->update_plane(plane, + to_intel_crtc_state(crtc->state), + to_intel_plane_state(plane->state)); + + intel_cleanup_plane_fb(plane, new_plane_state); + +out_unlock: + mutex_unlock(&dev_priv->drm.struct_mutex); +out_free: + intel_plane_destroy_state(plane, new_plane_state); + return ret; + +slow_free: + intel_plane_destroy_state(plane, new_plane_state); +slow: + return drm_atomic_helper_update_plane(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); +} + +static const struct drm_plane_funcs intel_cursor_plane_funcs = { + .update_plane = intel_legacy_cursor_update, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = intel_plane_destroy, + .set_property = drm_atomic_helper_plane_set_property, + .atomic_get_property = intel_plane_atomic_get_property, + .atomic_set_property = intel_plane_atomic_set_property, + .atomic_duplicate_state = intel_plane_duplicate_state, + .atomic_destroy_state = intel_plane_destroy_state, +}; + static struct intel_plane * intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) { @@ -14988,6 +15174,7 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) primary->plane = (enum plane) !pipe; else primary->plane = (enum plane) pipe; + primary->id = PLANE_PRIMARY; primary->frontbuffer_bit = INTEL_FRONTBUFFER_PRIMARY(pipe); primary->check_plane = intel_check_primary_plane; @@ -15187,13 +15374,14 @@ intel_cursor_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) cursor->max_downscale = 1; cursor->pipe = pipe; cursor->plane = pipe; + cursor->id = PLANE_CURSOR; cursor->frontbuffer_bit = INTEL_FRONTBUFFER_CURSOR(pipe); cursor->check_plane = intel_check_cursor_plane; cursor->update_plane = intel_update_cursor_plane; cursor->disable_plane = intel_disable_cursor_plane; ret = drm_universal_plane_init(&dev_priv->drm, &cursor->base, - 0, &intel_plane_funcs, + 0, &intel_cursor_plane_funcs, intel_cursor_formats, ARRAY_SIZE(intel_cursor_formats), DRM_PLANE_TYPE_CURSOR, @@ -15221,14 +15409,18 @@ fail: return ERR_PTR(ret); } -static void skl_init_scalers(struct drm_i915_private *dev_priv, - struct intel_crtc *crtc, - struct intel_crtc_state *crtc_state) +static void intel_crtc_init_scalers(struct intel_crtc *crtc, + struct intel_crtc_state *crtc_state) { struct intel_crtc_scaler_state *scaler_state = &crtc_state->scaler_state; + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); int i; + crtc->num_scalers = dev_priv->info.num_scalers[crtc->pipe]; + if (!crtc->num_scalers) + return; + for (i = 0; i < crtc->num_scalers; i++) { struct intel_scaler *scaler = &scaler_state->scalers[i]; @@ -15260,21 +15452,12 @@ static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) intel_crtc->base.state = &crtc_state->base; crtc_state->base.crtc = &intel_crtc->base; - /* initialize shared scalers */ - if (INTEL_GEN(dev_priv) >= 9) { - if (pipe == PIPE_C) - intel_crtc->num_scalers = 1; - else - intel_crtc->num_scalers = SKL_NUM_SCALERS; - - skl_init_scalers(dev_priv, intel_crtc, crtc_state); - } - primary = intel_primary_plane_create(dev_priv, pipe); if (IS_ERR(primary)) { ret = PTR_ERR(primary); goto fail; } + intel_crtc->plane_ids_mask |= BIT(primary->id); for_each_sprite(dev_priv, pipe, sprite) { struct intel_plane *plane; @@ -15284,6 +15467,7 @@ static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) ret = PTR_ERR(plane); goto fail; } + intel_crtc->plane_ids_mask |= BIT(plane->id); } cursor = intel_cursor_plane_create(dev_priv, pipe); @@ -15291,6 +15475,7 @@ static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) ret = PTR_ERR(cursor); goto fail; } + intel_crtc->plane_ids_mask |= BIT(cursor->id); ret = drm_crtc_init_with_planes(&dev_priv->drm, &intel_crtc->base, &primary->base, &cursor->base, @@ -15308,6 +15493,9 @@ static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) intel_crtc->wm.cxsr_allowed = true; + /* initialize shared scalers */ + intel_crtc_init_scalers(intel_crtc, crtc_state); + BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL); dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = intel_crtc; @@ -15444,7 +15632,7 @@ void intel_pps_unlock_regs_wa(struct drm_i915_private *dev_priv) static void intel_pps_init(struct drm_i915_private *dev_priv) { - if (HAS_PCH_SPLIT(dev_priv) || IS_BROXTON(dev_priv)) + if (HAS_PCH_SPLIT(dev_priv) || IS_GEN9_LP(dev_priv)) dev_priv->pps_mmio_base = PCH_PPS_BASE; else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) dev_priv->pps_mmio_base = VLV_PPS_BASE; @@ -15454,9 +15642,8 @@ static void intel_pps_init(struct drm_i915_private *dev_priv) intel_pps_unlock_regs_wa(dev_priv); } -static void intel_setup_outputs(struct drm_device *dev) +static void intel_setup_outputs(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_encoder *encoder; bool dpd_is_edp = false; @@ -15467,22 +15654,22 @@ static void intel_setup_outputs(struct drm_device *dev) * prevent the registeration of both eDP and LVDS and the incorrect * sharing of the PPS. */ - intel_lvds_init(dev); + intel_lvds_init(dev_priv); if (intel_crt_present(dev_priv)) - intel_crt_init(dev); + intel_crt_init(dev_priv); - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { /* * FIXME: Broxton doesn't support port detection via the * DDI_BUF_CTL_A or SFUSE_STRAP registers, find another way to * detect the ports. */ - intel_ddi_init(dev, PORT_A); - intel_ddi_init(dev, PORT_B); - intel_ddi_init(dev, PORT_C); + intel_ddi_init(dev_priv, PORT_A); + intel_ddi_init(dev_priv, PORT_B); + intel_ddi_init(dev_priv, PORT_C); - intel_dsi_init(dev); + intel_dsi_init(dev_priv); } else if (HAS_DDI(dev_priv)) { int found; @@ -15494,18 +15681,18 @@ static void intel_setup_outputs(struct drm_device *dev) found = I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_INIT_DISPLAY_DETECTED; /* WaIgnoreDDIAStrap: skl */ if (found || IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) - intel_ddi_init(dev, PORT_A); + intel_ddi_init(dev_priv, PORT_A); /* DDI B, C and D detection is indicated by the SFUSE_STRAP * register */ found = I915_READ(SFUSE_STRAP); if (found & SFUSE_STRAP_DDIB_DETECTED) - intel_ddi_init(dev, PORT_B); + intel_ddi_init(dev_priv, PORT_B); if (found & SFUSE_STRAP_DDIC_DETECTED) - intel_ddi_init(dev, PORT_C); + intel_ddi_init(dev_priv, PORT_C); if (found & SFUSE_STRAP_DDID_DETECTED) - intel_ddi_init(dev, PORT_D); + intel_ddi_init(dev_priv, PORT_D); /* * On SKL we don't have a way to detect DDI-E so we rely on VBT. */ @@ -15513,35 +15700,35 @@ static void intel_setup_outputs(struct drm_device *dev) (dev_priv->vbt.ddi_port_info[PORT_E].supports_dp || dev_priv->vbt.ddi_port_info[PORT_E].supports_dvi || dev_priv->vbt.ddi_port_info[PORT_E].supports_hdmi)) - intel_ddi_init(dev, PORT_E); + intel_ddi_init(dev_priv, PORT_E); } else if (HAS_PCH_SPLIT(dev_priv)) { int found; dpd_is_edp = intel_dp_is_edp(dev_priv, PORT_D); if (has_edp_a(dev_priv)) - intel_dp_init(dev, DP_A, PORT_A); + intel_dp_init(dev_priv, DP_A, PORT_A); if (I915_READ(PCH_HDMIB) & SDVO_DETECTED) { /* PCH SDVOB multiplex with HDMIB */ - found = intel_sdvo_init(dev, PCH_SDVOB, PORT_B); + found = intel_sdvo_init(dev_priv, PCH_SDVOB, PORT_B); if (!found) - intel_hdmi_init(dev, PCH_HDMIB, PORT_B); + intel_hdmi_init(dev_priv, PCH_HDMIB, PORT_B); if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED)) - intel_dp_init(dev, PCH_DP_B, PORT_B); + intel_dp_init(dev_priv, PCH_DP_B, PORT_B); } if (I915_READ(PCH_HDMIC) & SDVO_DETECTED) - intel_hdmi_init(dev, PCH_HDMIC, PORT_C); + intel_hdmi_init(dev_priv, PCH_HDMIC, PORT_C); if (!dpd_is_edp && I915_READ(PCH_HDMID) & SDVO_DETECTED) - intel_hdmi_init(dev, PCH_HDMID, PORT_D); + intel_hdmi_init(dev_priv, PCH_HDMID, PORT_D); if (I915_READ(PCH_DP_C) & DP_DETECTED) - intel_dp_init(dev, PCH_DP_C, PORT_C); + intel_dp_init(dev_priv, PCH_DP_C, PORT_C); if (I915_READ(PCH_DP_D) & DP_DETECTED) - intel_dp_init(dev, PCH_DP_D, PORT_D); + intel_dp_init(dev_priv, PCH_DP_D, PORT_D); } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { bool has_edp, has_port; @@ -15563,16 +15750,16 @@ static void intel_setup_outputs(struct drm_device *dev) has_edp = intel_dp_is_edp(dev_priv, PORT_B); has_port = intel_bios_is_port_present(dev_priv, PORT_B); if (I915_READ(VLV_DP_B) & DP_DETECTED || has_port) - has_edp &= intel_dp_init(dev, VLV_DP_B, PORT_B); + has_edp &= intel_dp_init(dev_priv, VLV_DP_B, PORT_B); if ((I915_READ(VLV_HDMIB) & SDVO_DETECTED || has_port) && !has_edp) - intel_hdmi_init(dev, VLV_HDMIB, PORT_B); + intel_hdmi_init(dev_priv, VLV_HDMIB, PORT_B); has_edp = intel_dp_is_edp(dev_priv, PORT_C); has_port = intel_bios_is_port_present(dev_priv, PORT_C); if (I915_READ(VLV_DP_C) & DP_DETECTED || has_port) - has_edp &= intel_dp_init(dev, VLV_DP_C, PORT_C); + has_edp &= intel_dp_init(dev_priv, VLV_DP_C, PORT_C); if ((I915_READ(VLV_HDMIC) & SDVO_DETECTED || has_port) && !has_edp) - intel_hdmi_init(dev, VLV_HDMIC, PORT_C); + intel_hdmi_init(dev_priv, VLV_HDMIC, PORT_C); if (IS_CHERRYVIEW(dev_priv)) { /* @@ -15581,63 +15768,63 @@ static void intel_setup_outputs(struct drm_device *dev) */ has_port = intel_bios_is_port_present(dev_priv, PORT_D); if (I915_READ(CHV_DP_D) & DP_DETECTED || has_port) - intel_dp_init(dev, CHV_DP_D, PORT_D); + intel_dp_init(dev_priv, CHV_DP_D, PORT_D); if (I915_READ(CHV_HDMID) & SDVO_DETECTED || has_port) - intel_hdmi_init(dev, CHV_HDMID, PORT_D); + intel_hdmi_init(dev_priv, CHV_HDMID, PORT_D); } - intel_dsi_init(dev); + intel_dsi_init(dev_priv); } else if (!IS_GEN2(dev_priv) && !IS_PINEVIEW(dev_priv)) { bool found = false; if (I915_READ(GEN3_SDVOB) & SDVO_DETECTED) { DRM_DEBUG_KMS("probing SDVOB\n"); - found = intel_sdvo_init(dev, GEN3_SDVOB, PORT_B); + found = intel_sdvo_init(dev_priv, GEN3_SDVOB, PORT_B); if (!found && IS_G4X(dev_priv)) { DRM_DEBUG_KMS("probing HDMI on SDVOB\n"); - intel_hdmi_init(dev, GEN4_HDMIB, PORT_B); + intel_hdmi_init(dev_priv, GEN4_HDMIB, PORT_B); } if (!found && IS_G4X(dev_priv)) - intel_dp_init(dev, DP_B, PORT_B); + intel_dp_init(dev_priv, DP_B, PORT_B); } /* Before G4X SDVOC doesn't have its own detect register */ if (I915_READ(GEN3_SDVOB) & SDVO_DETECTED) { DRM_DEBUG_KMS("probing SDVOC\n"); - found = intel_sdvo_init(dev, GEN3_SDVOC, PORT_C); + found = intel_sdvo_init(dev_priv, GEN3_SDVOC, PORT_C); } if (!found && (I915_READ(GEN3_SDVOC) & SDVO_DETECTED)) { if (IS_G4X(dev_priv)) { DRM_DEBUG_KMS("probing HDMI on SDVOC\n"); - intel_hdmi_init(dev, GEN4_HDMIC, PORT_C); + intel_hdmi_init(dev_priv, GEN4_HDMIC, PORT_C); } if (IS_G4X(dev_priv)) - intel_dp_init(dev, DP_C, PORT_C); + intel_dp_init(dev_priv, DP_C, PORT_C); } if (IS_G4X(dev_priv) && (I915_READ(DP_D) & DP_DETECTED)) - intel_dp_init(dev, DP_D, PORT_D); + intel_dp_init(dev_priv, DP_D, PORT_D); } else if (IS_GEN2(dev_priv)) - intel_dvo_init(dev); + intel_dvo_init(dev_priv); if (SUPPORTS_TV(dev_priv)) - intel_tv_init(dev); + intel_tv_init(dev_priv); - intel_psr_init(dev); + intel_psr_init(dev_priv); - for_each_intel_encoder(dev, encoder) { + for_each_intel_encoder(&dev_priv->drm, encoder) { encoder->base.possible_crtcs = encoder->crtc_mask; encoder->base.possible_clones = intel_encoder_clones(encoder); } - intel_init_pch_refclk(dev); + intel_init_pch_refclk(dev_priv); - drm_helper_move_panel_connectors_to_head(dev); + drm_helper_move_panel_connectors_to_head(&dev_priv->drm); } static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) @@ -15874,7 +16061,7 @@ static int intel_framebuffer_init(struct drm_device *dev, if (mode_cmd->offsets[0] != 0) return -EINVAL; - drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &intel_fb->base, mode_cmd); intel_fb->obj = obj; ret = intel_fill_fb_info(dev_priv, &intel_fb->base); @@ -15912,6 +16099,17 @@ intel_user_framebuffer_create(struct drm_device *dev, return fb; } +static void intel_atomic_state_free(struct drm_atomic_state *state) +{ + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + + drm_atomic_state_default_release(state); + + i915_sw_fence_fini(&intel_state->commit_ready); + + kfree(state); +} + static const struct drm_mode_config_funcs intel_mode_funcs = { .fb_create = intel_user_framebuffer_create, .output_poll_changed = intel_fbdev_output_poll_changed, @@ -15919,6 +16117,7 @@ static const struct drm_mode_config_funcs intel_mode_funcs = { .atomic_commit = intel_atomic_commit, .atomic_state_alloc = intel_atomic_state_alloc, .atomic_state_clear = intel_atomic_state_clear, + .atomic_state_free = intel_atomic_state_free, }; /** @@ -15999,7 +16198,7 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) dev_priv->display.get_display_clock_speed = skylake_get_display_clock_speed; - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) dev_priv->display.get_display_clock_speed = broxton_get_display_clock_speed; else if (IS_BROADWELL(dev_priv)) @@ -16014,14 +16213,14 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) else if (IS_GEN5(dev_priv)) dev_priv->display.get_display_clock_speed = ilk_get_display_clock_speed; - else if (IS_I945G(dev_priv) || IS_BROADWATER(dev_priv) || + else if (IS_I945G(dev_priv) || IS_I965G(dev_priv) || IS_GEN6(dev_priv) || IS_IVYBRIDGE(dev_priv)) dev_priv->display.get_display_clock_speed = i945_get_display_clock_speed; else if (IS_GM45(dev_priv)) dev_priv->display.get_display_clock_speed = gm45_get_display_clock_speed; - else if (IS_CRESTLINE(dev_priv)) + else if (IS_I965GM(dev_priv)) dev_priv->display.get_display_clock_speed = i965gm_get_display_clock_speed; else if (IS_PINEVIEW(dev_priv)) @@ -16033,7 +16232,7 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) else if (IS_I915G(dev_priv)) dev_priv->display.get_display_clock_speed = i915_get_display_clock_speed; - else if (IS_I945GM(dev_priv) || IS_845G(dev_priv)) + else if (IS_I945GM(dev_priv) || IS_I845G(dev_priv)) dev_priv->display.get_display_clock_speed = i9xx_misc_get_display_clock_speed; else if (IS_I915GM(dev_priv)) @@ -16072,7 +16271,7 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv) valleyview_modeset_commit_cdclk; dev_priv->display.modeset_calc_cdclk = valleyview_modeset_calc_cdclk; - } else if (IS_BROXTON(dev_priv)) { + } else if (IS_GEN9_LP(dev_priv)) { dev_priv->display.modeset_commit_cdclk = bxt_modeset_commit_cdclk; dev_priv->display.modeset_calc_cdclk = @@ -16470,8 +16669,8 @@ int intel_modeset_init(struct drm_device *dev) dev->mode_config.max_height = 8192; } - if (IS_845G(dev_priv) || IS_I865G(dev_priv)) { - dev->mode_config.cursor_width = IS_845G(dev_priv) ? 64 : 512; + if (IS_I845G(dev_priv) || IS_I865G(dev_priv)) { + dev->mode_config.cursor_width = IS_I845G(dev_priv) ? 64 : 512; dev->mode_config.cursor_height = 1023; } else if (IS_GEN2(dev_priv)) { dev->mode_config.cursor_width = GEN2_CURSOR_WIDTH; @@ -16508,7 +16707,7 @@ int intel_modeset_init(struct drm_device *dev) /* Just disable it once at startup */ i915_disable_vga(dev_priv); - intel_setup_outputs(dev); + intel_setup_outputs(dev_priv); drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev); @@ -16661,7 +16860,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) * Temporarily change the plane mapping and disable everything * ... */ plane = crtc->plane; - to_intel_plane_state(crtc->base.primary->state)->base.visible = true; + crtc->base.primary->state->visible = true; crtc->plane = !plane; intel_crtc_disable_noatomic(&crtc->base); crtc->plane = plane; @@ -16813,7 +17012,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) dev_priv->active_crtcs = 0; for_each_intel_crtc(dev, crtc) { - struct intel_crtc_state *crtc_state = crtc->config; + struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); __drm_atomic_helper_crtc_destroy_state(&crtc_state->base); memset(crtc_state, 0, sizeof(*crtc_state)); @@ -16832,34 +17032,41 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) DRM_DEBUG_KMS("[CRTC:%d:%s] hw state readout: %s\n", crtc->base.base.id, crtc->base.name, - enableddisabled(crtc->active)); + enableddisabled(crtc_state->base.active)); } for (i = 0; i < dev_priv->num_shared_dpll; i++) { struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; pll->on = pll->funcs.get_hw_state(dev_priv, pll, - &pll->config.hw_state); - pll->config.crtc_mask = 0; + &pll->state.hw_state); + pll->state.crtc_mask = 0; for_each_intel_crtc(dev, crtc) { - if (crtc->active && crtc->config->shared_dpll == pll) - pll->config.crtc_mask |= 1 << crtc->pipe; + struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); + + if (crtc_state->base.active && + crtc_state->shared_dpll == pll) + pll->state.crtc_mask |= 1 << crtc->pipe; } - pll->active_mask = pll->config.crtc_mask; + pll->active_mask = pll->state.crtc_mask; DRM_DEBUG_KMS("%s hw state readout: crtc_mask 0x%08x, on %i\n", - pll->name, pll->config.crtc_mask, pll->on); + pll->name, pll->state.crtc_mask, pll->on); } for_each_intel_encoder(dev, encoder) { pipe = 0; if (encoder->get_hw_state(encoder, &pipe)) { + struct intel_crtc_state *crtc_state; + crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + crtc_state = to_intel_crtc_state(crtc->base.state); encoder->base.crtc = &crtc->base; - crtc->config->output_types |= 1 << encoder->type; - encoder->get_config(encoder, crtc->config); + crtc_state->output_types |= 1 << encoder->type; + encoder->get_config(encoder, crtc_state); } else { encoder->base.crtc = NULL; } @@ -16900,14 +17107,16 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) } for_each_intel_crtc(dev, crtc) { + struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); int pixclk = 0; - crtc->base.hwmode = crtc->config->base.adjusted_mode; + crtc->base.hwmode = crtc_state->base.adjusted_mode; memset(&crtc->base.mode, 0, sizeof(crtc->base.mode)); - if (crtc->base.state->active) { - intel_mode_from_pipe_config(&crtc->base.mode, crtc->config); - intel_mode_from_pipe_config(&crtc->base.state->adjusted_mode, crtc->config); + if (crtc_state->base.active) { + intel_mode_from_pipe_config(&crtc->base.mode, crtc_state); + intel_mode_from_pipe_config(&crtc_state->base.adjusted_mode, crtc_state); WARN_ON(drm_atomic_set_mode_for_crtc(crtc->base.state, &crtc->base.mode)); /* @@ -16915,29 +17124,21 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) * the atomic core happy. It wants a valid mode if the * crtc's enabled, so we do the above call. * - * At this point some state updated by the connectors - * in their ->detect() callback has not run yet, so - * no recalculation can be done yet. - * - * Even if we could do a recalculation and modeset - * right now it would cause a double modeset if - * fbdev or userspace chooses a different initial mode. - * - * If that happens, someone indicated they wanted a - * mode change, which means it's safe to do a full - * recalculation. + * But we don't set all the derived state fully, hence + * set a flag to indicate that a full recalculation is + * needed on the next commit. */ - crtc->base.state->mode.private_flags = I915_MODE_FLAG_INHERITED; + crtc_state->base.mode.private_flags = I915_MODE_FLAG_INHERITED; if (INTEL_GEN(dev_priv) >= 9 || IS_BROADWELL(dev_priv)) - pixclk = ilk_pipe_pixel_rate(crtc->config); + pixclk = ilk_pipe_pixel_rate(crtc_state); else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - pixclk = crtc->config->base.adjusted_mode.crtc_clock; + pixclk = crtc_state->base.adjusted_mode.crtc_clock; else WARN_ON(dev_priv->display.modeset_calc_cdclk); /* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */ - if (IS_BROADWELL(dev_priv) && crtc->config->ips_enabled) + if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled) pixclk = DIV_ROUND_UP(pixclk * 100, 95); drm_calc_timestamping_constants(&crtc->base, &crtc->base.hwmode); @@ -16946,7 +17147,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) dev_priv->min_pixclk[crtc->pipe] = pixclk; - intel_pipe_config_sanity_check(dev_priv, crtc->config); + intel_pipe_config_sanity_check(dev_priv, crtc_state); } } @@ -17120,7 +17321,7 @@ void intel_modeset_cleanup(struct drm_device *dev) intel_cleanup_gt_powersave(dev_priv); - intel_teardown_gmbus(dev); + intel_teardown_gmbus(dev_priv); } void intel_connector_attach_encoder(struct intel_connector *connector, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0b8e8eb85c19..d1670b8afbf5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -156,38 +156,28 @@ static u8 intel_dp_max_lane_count(struct intel_dp *intel_dp) u8 source_max, sink_max; source_max = intel_dig_port->max_lanes; - sink_max = drm_dp_max_lane_count(intel_dp->dpcd); + sink_max = intel_dp->max_sink_lane_count; return min(source_max, sink_max); } -/* - * The units on the numbers in the next two are... bizarre. Examples will - * make it clearer; this one parallels an example in the eDP spec. - * - * intel_dp_max_data_rate for one lane of 2.7GHz evaluates as: - * - * 270000 * 1 * 8 / 10 == 216000 - * - * The actual data capacity of that configuration is 2.16Gbit/s, so the - * units are decakilobits. ->clock in a drm_display_mode is in kilohertz - - * or equivalently, kilopixels per second - so for 1680x1050R it'd be - * 119000. At 18bpp that's 2142000 kilobits per second. - * - * Thus the strange-looking division by 10 in intel_dp_link_required, to - * get the result in decakilobits instead of kilobits. - */ - -static int +int intel_dp_link_required(int pixel_clock, int bpp) { - return (pixel_clock * bpp + 9) / 10; + /* pixel_clock is in kHz, divide bpp by 8 for bit to Byte conversion */ + return DIV_ROUND_UP(pixel_clock * bpp, 8); } -static int +int intel_dp_max_data_rate(int max_link_clock, int max_lanes) { - return (max_link_clock * max_lanes * 8) / 10; + /* max_link_clock is the link symbol clock (LS_Clk) in kHz and not the + * link rate that is generally expressed in Gbps. Since, 8 bits of data + * is transmitted every LS_Clk per lane, there is no need to account for + * the channel encoding that is done in the PHY layer here. + */ + + return max_link_clock * max_lanes; } static int @@ -223,7 +213,7 @@ intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates) *sink_rates = default_rates; - return (intel_dp_max_link_bw(intel_dp) >> 3) + 1; + return (intel_dp->max_sink_link_bw >> 3) + 1; } static int @@ -233,7 +223,7 @@ intel_dp_source_rates(struct intel_dp *intel_dp, const int **source_rates) struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev); int size; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { *source_rates = bxt_rates; size = ARRAY_SIZE(bxt_rates); } else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { @@ -288,6 +278,44 @@ static int intel_dp_common_rates(struct intel_dp *intel_dp, common_rates); } +static int intel_dp_link_rate_index(struct intel_dp *intel_dp, + int *common_rates, int link_rate) +{ + int common_len; + int index; + + common_len = intel_dp_common_rates(intel_dp, common_rates); + for (index = 0; index < common_len; index++) { + if (link_rate == common_rates[common_len - index - 1]) + return common_len - index - 1; + } + + return -1; +} + +int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, + int link_rate, uint8_t lane_count) +{ + int common_rates[DP_MAX_SUPPORTED_RATES]; + int link_rate_index; + + link_rate_index = intel_dp_link_rate_index(intel_dp, + common_rates, + link_rate); + if (link_rate_index > 0) { + intel_dp->max_sink_link_bw = drm_dp_link_rate_to_bw_code(common_rates[link_rate_index - 1]); + intel_dp->max_sink_lane_count = lane_count; + } else if (lane_count > 1) { + intel_dp->max_sink_link_bw = intel_dp_max_link_bw(intel_dp); + intel_dp->max_sink_lane_count = lane_count >> 1; + } else { + DRM_ERROR("Link Training Unsuccessful\n"); + return -1; + } + + return 0; +} + static enum drm_mode_status intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) @@ -465,14 +493,50 @@ vlv_power_sequencer_kick(struct intel_dp *intel_dp) } } +static enum pipe vlv_find_free_pps(struct drm_i915_private *dev_priv) +{ + struct intel_encoder *encoder; + unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B); + + /* + * We don't have power sequencer currently. + * Pick one that's not used by other ports. + */ + for_each_intel_encoder(&dev_priv->drm, encoder) { + struct intel_dp *intel_dp; + + if (encoder->type != INTEL_OUTPUT_DP && + encoder->type != INTEL_OUTPUT_EDP) + continue; + + intel_dp = enc_to_intel_dp(&encoder->base); + + if (encoder->type == INTEL_OUTPUT_EDP) { + WARN_ON(intel_dp->active_pipe != INVALID_PIPE && + intel_dp->active_pipe != intel_dp->pps_pipe); + + if (intel_dp->pps_pipe != INVALID_PIPE) + pipes &= ~(1 << intel_dp->pps_pipe); + } else { + WARN_ON(intel_dp->pps_pipe != INVALID_PIPE); + + if (intel_dp->active_pipe != INVALID_PIPE) + pipes &= ~(1 << intel_dp->active_pipe); + } + } + + if (pipes == 0) + return INVALID_PIPE; + + return ffs(pipes) - 1; +} + static enum pipe vlv_power_sequencer_pipe(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_encoder *encoder; - unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B); enum pipe pipe; lockdep_assert_held(&dev_priv->pps_mutex); @@ -480,33 +544,20 @@ vlv_power_sequencer_pipe(struct intel_dp *intel_dp) /* We should never land here with regular DP ports */ WARN_ON(!is_edp(intel_dp)); + WARN_ON(intel_dp->active_pipe != INVALID_PIPE && + intel_dp->active_pipe != intel_dp->pps_pipe); + if (intel_dp->pps_pipe != INVALID_PIPE) return intel_dp->pps_pipe; - /* - * We don't have power sequencer currently. - * Pick one that's not used by other ports. - */ - for_each_intel_encoder(dev, encoder) { - struct intel_dp *tmp; - - if (encoder->type != INTEL_OUTPUT_EDP) - continue; - - tmp = enc_to_intel_dp(&encoder->base); - - if (tmp->pps_pipe != INVALID_PIPE) - pipes &= ~(1 << tmp->pps_pipe); - } + pipe = vlv_find_free_pps(dev_priv); /* * Didn't find one. This should not happen since there * are two power sequencers and up to two eDP ports. */ - if (WARN_ON(pipes == 0)) + if (WARN_ON(pipe == INVALID_PIPE)) pipe = PIPE_A; - else - pipe = ffs(pipes) - 1; vlv_steal_power_sequencer(dev, pipe); intel_dp->pps_pipe = pipe; @@ -646,7 +697,7 @@ void intel_power_sequencer_reset(struct drm_i915_private *dev_priv) struct intel_encoder *encoder; if (WARN_ON(!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv) && - !IS_BROXTON(dev_priv))) + !IS_GEN9_LP(dev_priv))) return; /* @@ -662,11 +713,18 @@ void intel_power_sequencer_reset(struct drm_i915_private *dev_priv) for_each_intel_encoder(dev, encoder) { struct intel_dp *intel_dp; - if (encoder->type != INTEL_OUTPUT_EDP) + if (encoder->type != INTEL_OUTPUT_DP && + encoder->type != INTEL_OUTPUT_EDP) continue; intel_dp = enc_to_intel_dp(&encoder->base); - if (IS_BROXTON(dev_priv)) + + WARN_ON(intel_dp->active_pipe != INVALID_PIPE); + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + if (IS_GEN9_LP(dev_priv)) intel_dp->pps_reset = true; else intel_dp->pps_pipe = INVALID_PIPE; @@ -689,7 +747,7 @@ static void intel_pps_get_registers(struct drm_i915_private *dev_priv, memset(regs, 0, sizeof(*regs)); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) pps_idx = bxt_power_sequencer_idx(intel_dp); else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) pps_idx = vlv_power_sequencer_pipe(intel_dp); @@ -698,7 +756,7 @@ static void intel_pps_get_registers(struct drm_i915_private *dev_priv, regs->pp_stat = PP_STATUS(pps_idx); regs->pp_on = PP_ON_DELAYS(pps_idx); regs->pp_off = PP_OFF_DELAYS(pps_idx); - if (!IS_BROXTON(dev_priv)) + if (!IS_GEN9_LP(dev_priv)) regs->pp_div = PP_DIVISOR(pps_idx); } @@ -1655,7 +1713,9 @@ found: * VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry */ pipe_config->limited_color_range = - bpp != 18 && drm_match_cea_mode(adjusted_mode) > 1; + bpp != 18 && + drm_default_rgb_quant_range(adjusted_mode) == + HDMI_QUANTIZATION_RANGE_LIMITED; } else { pipe_config->limited_color_range = intel_dp->limited_color_range; @@ -2402,6 +2462,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D3); } else { + struct intel_lspcon *lspcon = dp_to_lspcon(intel_dp); + /* * When turning on, we need to retry for 1ms to give the sink * time to wake up. @@ -2413,6 +2475,9 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) break; msleep(1); } + + if (ret == 1 && lspcon->active) + lspcon_wait_pcon_mode(lspcon); } if (ret != 1) @@ -2820,6 +2885,11 @@ static void vlv_detach_power_sequencer(struct intel_dp *intel_dp) enum pipe pipe = intel_dp->pps_pipe; i915_reg_t pp_on_reg = PP_ON_DELAYS(pipe); + WARN_ON(intel_dp->active_pipe != INVALID_PIPE); + + if (WARN_ON(pipe != PIPE_A && pipe != PIPE_B)) + return; + edp_panel_vdd_off_sync(intel_dp); /* @@ -2847,29 +2917,27 @@ static void vlv_steal_power_sequencer(struct drm_device *dev, lockdep_assert_held(&dev_priv->pps_mutex); - if (WARN_ON(pipe != PIPE_A && pipe != PIPE_B)) - return; - for_each_intel_encoder(dev, encoder) { struct intel_dp *intel_dp; enum port port; - if (encoder->type != INTEL_OUTPUT_EDP) + if (encoder->type != INTEL_OUTPUT_DP && + encoder->type != INTEL_OUTPUT_EDP) continue; intel_dp = enc_to_intel_dp(&encoder->base); port = dp_to_dig_port(intel_dp)->port; + WARN(intel_dp->active_pipe == pipe, + "stealing pipe %c power sequencer from active (e)DP port %c\n", + pipe_name(pipe), port_name(port)); + if (intel_dp->pps_pipe != pipe) continue; DRM_DEBUG_KMS("stealing pipe %c power sequencer from port %c\n", pipe_name(pipe), port_name(port)); - WARN(encoder->base.crtc, - "stealing pipe %c power sequencer from active eDP port %c\n", - pipe_name(pipe), port_name(port)); - /* make sure vdd is off before we steal it */ vlv_detach_power_sequencer(intel_dp); } @@ -2885,19 +2953,17 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp) lockdep_assert_held(&dev_priv->pps_mutex); - if (!is_edp(intel_dp)) - return; + WARN_ON(intel_dp->active_pipe != INVALID_PIPE); - if (intel_dp->pps_pipe == crtc->pipe) - return; - - /* - * If another power sequencer was being used on this - * port previously make sure to turn off vdd there while - * we still have control of it. - */ - if (intel_dp->pps_pipe != INVALID_PIPE) + if (intel_dp->pps_pipe != INVALID_PIPE && + intel_dp->pps_pipe != crtc->pipe) { + /* + * If another power sequencer was being used on this + * port previously make sure to turn off vdd there while + * we still have control of it. + */ vlv_detach_power_sequencer(intel_dp); + } /* * We may be stealing the power @@ -2905,6 +2971,11 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp) */ vlv_steal_power_sequencer(dev, crtc->pipe); + intel_dp->active_pipe = crtc->pipe; + + if (!is_edp(intel_dp)) + return; + /* now it's all ours */ intel_dp->pps_pipe = crtc->pipe; @@ -2973,6 +3044,32 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE; } +static bool intel_dp_get_y_cord_status(struct intel_dp *intel_dp) +{ + uint8_t psr_caps = 0; + + drm_dp_dpcd_readb(&intel_dp->aux, DP_PSR_CAPS, &psr_caps); + return psr_caps & DP_PSR2_SU_Y_COORDINATE_REQUIRED; +} + +static bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp) +{ + uint8_t dprx = 0; + + drm_dp_dpcd_readb(&intel_dp->aux, + DP_DPRX_FEATURE_ENUMERATION_LIST, + &dprx); + return dprx & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED; +} + +static bool intel_dp_get_alpm_status(struct intel_dp *intel_dp) +{ + uint8_t alpm_caps = 0; + + drm_dp_dpcd_readb(&intel_dp->aux, DP_RECEIVER_ALPM_CAP, &alpm_caps); + return alpm_caps & DP_ALPM_CAP; +} + /* These are source-specific values. */ uint8_t intel_dp_voltage_max(struct intel_dp *intel_dp) @@ -2980,7 +3077,7 @@ intel_dp_voltage_max(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp)); enum port port = dp_to_dig_port(intel_dp)->port; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; else if (INTEL_GEN(dev_priv) >= 9) { if (dev_priv->vbt.edp.low_vswing && port == PORT_A) @@ -3343,7 +3440,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp) if (HAS_DDI(dev_priv)) { signal_levels = ddi_signal_levels(intel_dp); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) signal_levels = 0; else mask = DDI_BUF_EMP_MASK; @@ -3491,6 +3588,12 @@ intel_dp_link_down(struct intel_dp *intel_dp) msleep(intel_dp->panel_power_down_delay); intel_dp->DP = DP; + + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + pps_lock(intel_dp); + intel_dp->active_pipe = INVALID_PIPE; + pps_unlock(intel_dp); + } } bool @@ -3545,6 +3648,16 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp) dev_priv->psr.psr2_support = dev_priv->psr.aux_frame_sync; DRM_DEBUG_KMS("PSR2 %s on sink", dev_priv->psr.psr2_support ? "supported" : "not supported"); + + if (dev_priv->psr.psr2_support) { + dev_priv->psr.y_cord_support = + intel_dp_get_y_cord_status(intel_dp); + dev_priv->psr.colorimetry_support = + intel_dp_get_colorimetry_status(intel_dp); + dev_priv->psr.alpm = + intel_dp_get_alpm_status(intel_dp); + } + } /* Read the eDP Display control capabilities registers */ @@ -3569,7 +3682,12 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp) if (val == 0) break; - /* Value read is in kHz while drm clock is saved in deca-kHz */ + /* Value read multiplied by 200kHz gives the per-lane + * link rate in kHz. The source rates are, however, + * stored in terms of LS_Clk kHz. The full conversion + * back to symbols is + * (val * 200kHz)*(8/10 ch. encoding)*(1/8 bit to Byte) + */ intel_dp->sink_rates[i] = (val * 200) / 10; } intel_dp->num_sink_rates = i; @@ -3835,7 +3953,7 @@ static uint8_t intel_dp_autotest_edid(struct intel_dp *intel_dp) DRM_DEBUG_KMS("EDID read had %d NACKs, %d DEFERs\n", intel_dp->aux.i2c_nack_count, intel_dp->aux.i2c_defer_count); - intel_dp->compliance_test_data = INTEL_DP_RESOLUTION_FAILSAFE; + intel_dp->compliance.test_data.edid = INTEL_DP_RESOLUTION_FAILSAFE; } else { struct edid *block = intel_connector->detect_edid; @@ -3851,11 +3969,11 @@ static uint8_t intel_dp_autotest_edid(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Failed to write EDID checksum\n"); test_result = DP_TEST_ACK | DP_TEST_EDID_CHECKSUM_WRITE; - intel_dp->compliance_test_data = INTEL_DP_RESOLUTION_STANDARD; + intel_dp->compliance.test_data.edid = INTEL_DP_RESOLUTION_STANDARD; } /* Set test active flag here so userspace doesn't interrupt things */ - intel_dp->compliance_test_active = 1; + intel_dp->compliance.test_active = 1; return test_result; } @@ -3881,22 +3999,22 @@ static void intel_dp_handle_test_request(struct intel_dp *intel_dp) switch (rxdata) { case DP_TEST_LINK_TRAINING: DRM_DEBUG_KMS("LINK_TRAINING test requested\n"); - intel_dp->compliance_test_type = DP_TEST_LINK_TRAINING; + intel_dp->compliance.test_type = DP_TEST_LINK_TRAINING; response = intel_dp_autotest_link_training(intel_dp); break; case DP_TEST_LINK_VIDEO_PATTERN: DRM_DEBUG_KMS("TEST_PATTERN test requested\n"); - intel_dp->compliance_test_type = DP_TEST_LINK_VIDEO_PATTERN; + intel_dp->compliance.test_type = DP_TEST_LINK_VIDEO_PATTERN; response = intel_dp_autotest_video_pattern(intel_dp); break; case DP_TEST_LINK_EDID_READ: DRM_DEBUG_KMS("EDID test requested\n"); - intel_dp->compliance_test_type = DP_TEST_LINK_EDID_READ; + intel_dp->compliance.test_type = DP_TEST_LINK_EDID_READ; response = intel_dp_autotest_edid(intel_dp); break; case DP_TEST_LINK_PHY_TEST_PATTERN: DRM_DEBUG_KMS("PHY_PATTERN test requested\n"); - intel_dp->compliance_test_type = DP_TEST_LINK_PHY_TEST_PATTERN; + intel_dp->compliance.test_type = DP_TEST_LINK_PHY_TEST_PATTERN; response = intel_dp_autotest_phy_pattern(intel_dp); break; default: @@ -4020,7 +4138,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) return; /* if link training is requested we should perform it always */ - if ((intel_dp->compliance_test_type == DP_TEST_LINK_TRAINING) || + if ((intel_dp->compliance.test_type == DP_TEST_LINK_TRAINING) || (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count))) { DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", intel_encoder->base.name); @@ -4054,9 +4172,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) * Clearing compliance test variables to allow capturing * of values for next automated test request. */ - intel_dp->compliance_test_active = 0; - intel_dp->compliance_test_type = 0; - intel_dp->compliance_test_data = 0; + memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance)); /* * Now read the DPCD to see if it's actually running @@ -4148,9 +4264,10 @@ static enum drm_connector_status edp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = to_i915(dev); enum drm_connector_status status; - status = intel_panel_detect(dev); + status = intel_panel_detect(dev_priv); if (status == connector_status_unknown) status = connector_status_connected; @@ -4289,14 +4406,14 @@ static bool bxt_digital_port_connected(struct drm_i915_private *dev_priv, * * Return %true if @port is connected, %false otherwise. */ -static bool intel_digital_port_connected(struct drm_i915_private *dev_priv, - struct intel_digital_port *port) +bool intel_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) { if (HAS_PCH_IBX(dev_priv)) return ibx_digital_port_connected(dev_priv, port); else if (HAS_PCH_SPLIT(dev_priv)) return cpt_digital_port_connected(dev_priv, port); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) return bxt_digital_port_connected(dev_priv, port); else if (IS_GM45(dev_priv)) return gm45_digital_port_connected(dev_priv, port); @@ -4373,9 +4490,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) status = connector_status_disconnected; if (status == connector_status_disconnected) { - intel_dp->compliance_test_active = 0; - intel_dp->compliance_test_type = 0; - intel_dp->compliance_test_data = 0; + memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance)); if (intel_dp->is_mst) { DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n", @@ -4396,6 +4511,12 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) yesno(intel_dp_source_supports_hbr2(intel_dp)), yesno(drm_dp_tps3_supported(intel_dp->dpcd))); + /* Set the max lane count for sink */ + intel_dp->max_sink_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); + + /* Set the max link BW for sink */ + intel_dp->max_sink_link_bw = intel_dp_max_link_bw(intel_dp); + intel_dp_print_rates(intel_dp); intel_dp_read_desc(intel_dp); @@ -4751,27 +4872,41 @@ static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp) edp_panel_vdd_schedule_off(intel_dp); } +static enum pipe vlv_active_pipe(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp)); + + if ((intel_dp->DP & DP_PORT_EN) == 0) + return INVALID_PIPE; + + if (IS_CHERRYVIEW(dev_priv)) + return DP_PORT_TO_PIPE_CHV(intel_dp->DP); + else + return PORT_TO_PIPE(intel_dp->DP); +} + void intel_dp_encoder_reset(struct drm_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); - struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); - struct intel_lspcon *lspcon = &intel_dig_port->lspcon; - struct intel_dp *intel_dp = &intel_dig_port->dp; + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_lspcon *lspcon = dp_to_lspcon(intel_dp); if (!HAS_DDI(dev_priv)) intel_dp->DP = I915_READ(intel_dp->output_reg); - if (IS_GEN9(dev_priv) && lspcon->active) + if (lspcon->active) lspcon_resume(lspcon); - if (to_intel_encoder(encoder)->type != INTEL_OUTPUT_EDP) - return; - pps_lock(intel_dp); - /* Reinit the power sequencer, in case BIOS did something with it. */ - intel_dp_pps_init(encoder->dev, intel_dp); - intel_edp_panel_vdd_sanitize(intel_dp); + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + intel_dp->active_pipe = vlv_active_pipe(intel_dp); + + if (is_edp(intel_dp)) { + /* Reinit the power sequencer, in case BIOS did something with it. */ + intel_dp_pps_init(encoder->dev, intel_dp); + intel_edp_panel_vdd_sanitize(intel_dp); + } pps_unlock(intel_dp); } @@ -4879,7 +5014,7 @@ bool intel_dp_is_edp(struct drm_i915_private *dev_priv, enum port port) if (INTEL_GEN(dev_priv) < 5) return false; - if (port == PORT_A) + if (INTEL_GEN(dev_priv) < 9 && port == PORT_A) return true; return intel_bios_is_port_edp(dev_priv, port); @@ -4926,7 +5061,7 @@ intel_pps_readout_hw_state(struct drm_i915_private *dev_priv, pp_on = I915_READ(regs.pp_on); pp_off = I915_READ(regs.pp_off); - if (!IS_BROXTON(dev_priv)) { + if (!IS_GEN9_LP(dev_priv)) { I915_WRITE(regs.pp_ctrl, pp_ctl); pp_div = I915_READ(regs.pp_div); } @@ -4944,7 +5079,7 @@ intel_pps_readout_hw_state(struct drm_i915_private *dev_priv, seq->t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> PANEL_POWER_DOWN_DELAY_SHIFT; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { u16 tmp = (pp_ctl & BXT_POWER_CYCLE_DELAY_MASK) >> BXT_POWER_CYCLE_DELAY_SHIFT; if (tmp > 0) @@ -5101,7 +5236,7 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, (seq->t10 << PANEL_POWER_DOWN_DELAY_SHIFT); /* Compute the divisor for the pp clock, simply match the Bspec * formula. */ - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { pp_div = I915_READ(regs.pp_ctrl); pp_div &= ~BXT_POWER_CYCLE_DELAY_MASK; pp_div |= (DIV_ROUND_UP((seq->t11_t12 + 1), 1000) @@ -5127,7 +5262,7 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, I915_WRITE(regs.pp_on, pp_on); I915_WRITE(regs.pp_off, pp_off); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) I915_WRITE(regs.pp_ctrl, pp_div); else I915_WRITE(regs.pp_div, pp_div); @@ -5135,7 +5270,7 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, DRM_DEBUG_KMS("panel power sequencer register settings: PP_ON %#x, PP_OFF %#x, PP_DIV %#x\n", I915_READ(regs.pp_on), I915_READ(regs.pp_off), - IS_BROXTON(dev_priv) ? + IS_GEN9_LP(dev_priv) ? (I915_READ(regs.pp_ctrl) & BXT_POWER_CYCLE_DELAY_MASK) : I915_READ(regs.pp_div)); } @@ -5515,7 +5650,7 @@ intel_dp_drrs_init(struct intel_connector *intel_connector, } downclock_mode = intel_find_panel_downclock - (dev, fixed_mode, connector); + (dev_priv, fixed_mode, connector); if (!downclock_mode) { DRM_DEBUG_KMS("Downclock mode is not found. DRRS not supported\n"); @@ -5624,10 +5759,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, * If the current pipe isn't valid, try the PPS pipe, and if that * fails just assume pipe A. */ - if (IS_CHERRYVIEW(dev_priv)) - pipe = DP_PORT_TO_PIPE_CHV(intel_dp->DP); - else - pipe = PORT_TO_PIPE(intel_dp->DP); + pipe = vlv_active_pipe(intel_dp); if (pipe != PIPE_A && pipe != PIPE_B) pipe = intel_dp->pps_pipe; @@ -5676,6 +5808,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, return false; intel_dp->pps_pipe = INVALID_PIPE; + intel_dp->active_pipe = INVALID_PIPE; /* intel_dp vfuncs */ if (INTEL_GEN(dev_priv) >= 9) @@ -5704,6 +5837,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, else type = DRM_MODE_CONNECTOR_DisplayPort; + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + intel_dp->active_pipe = vlv_active_pipe(intel_dp); + /* * For eDP we always set the encoder type to INTEL_OUTPUT_EDP, but * for DP the encoder type can be set by the caller to @@ -5793,11 +5929,10 @@ fail: return false; } -bool intel_dp_init(struct drm_device *dev, +bool intel_dp_init(struct drm_i915_private *dev_priv, i915_reg_t output_reg, enum port port) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_digital_port *intel_dig_port; struct intel_encoder *intel_encoder; struct drm_encoder *encoder; @@ -5814,8 +5949,9 @@ bool intel_dp_init(struct drm_device *dev, intel_encoder = &intel_dig_port->base; encoder = &intel_encoder->base; - if (drm_encoder_init(dev, &intel_encoder->base, &intel_dp_enc_funcs, - DRM_MODE_ENCODER_TMDS, "DP %c", port_name(port))) + if (drm_encoder_init(&dev_priv->drm, &intel_encoder->base, + &intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS, + "DP %c", port_name(port))) goto err_encoder_init; intel_encoder->compute_config = intel_dp_compute_config; diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index b029d1026a28..38e3ca2f6f18 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -37,6 +37,8 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); struct intel_digital_port *intel_dig_port = intel_mst->primary; struct intel_dp *intel_dp = &intel_dig_port->dp; + struct intel_connector *connector = + to_intel_connector(conn_state->connector); struct drm_atomic_state *state; int bpp; int lane_count, slots; @@ -58,6 +60,8 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, state = pipe_config->base.state; + if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, connector->port)) + pipe_config->has_audio = true; mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp); pipe_config->pbn = mst_pbn; @@ -83,6 +87,7 @@ static void intel_mst_disable_dp(struct intel_encoder *encoder, struct intel_dp *intel_dp = &intel_dig_port->dp; struct intel_connector *connector = to_intel_connector(old_conn_state->connector); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); int ret; DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links); @@ -93,6 +98,10 @@ static void intel_mst_disable_dp(struct intel_encoder *encoder, if (ret) { DRM_ERROR("failed to update payload %d\n", ret); } + if (old_crtc_state->has_audio) { + intel_audio_codec_disable(encoder); + intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); + } } static void intel_mst_post_disable_dp(struct intel_encoder *encoder, @@ -205,6 +214,10 @@ static void intel_mst_enable_dp(struct intel_encoder *encoder, ret = drm_dp_check_act_status(&intel_dp->mst_mgr); ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr); + if (pipe_config->has_audio) { + intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); + intel_audio_codec_enable(encoder, pipe_config, conn_state); + } } static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder, @@ -227,6 +240,9 @@ static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder, enum transcoder cpu_transcoder = pipe_config->cpu_transcoder; u32 temp, flags = 0; + pipe_config->has_audio = + intel_ddi_is_audio_enabled(dev_priv, crtc); + temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); if (temp & TRANS_DDI_PHSYNC) flags |= DRM_MODE_FLAG_PHSYNC; @@ -334,7 +350,17 @@ static enum drm_mode_status intel_dp_mst_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_dp *intel_dp = intel_connector->mst_port; int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; + int bpp = 24; /* MST uses fixed bpp */ + int max_rate, mode_rate, max_lanes, max_link_clock; + + max_link_clock = intel_dp_max_link_rate(intel_dp); + max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); + + max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); + mode_rate = intel_dp_link_required(mode->clock, bpp); /* TODO - validate mode against available PBN for link */ if (mode->clock < 10000) @@ -343,7 +369,7 @@ intel_dp_mst_mode_valid(struct drm_connector *connector, if (mode->flags & DRM_MODE_FLAG_DBLCLK) return MODE_H_ILLEGAL; - if (mode->clock > max_dotclk) + if (mode_rate > max_rate || mode->clock > max_dotclk) return MODE_CLOCK_HIGH; return MODE_OK; @@ -561,7 +587,8 @@ intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_ba /* create encoders */ intel_dp_create_fake_mst_encoders(intel_dig_port); - ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, &intel_dp->aux, 16, 3, conn_base_id); + ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev, + &intel_dp->aux, 16, 3, conn_base_id); if (ret) { intel_dp->can_mst = false; return ret; diff --git a/drivers/gpu/drm/i915/intel_dpio_phy.c b/drivers/gpu/drm/i915/intel_dpio_phy.c index 7a8e82dabbf2..09b670929786 100644 --- a/drivers/gpu/drm/i915/intel_dpio_phy.c +++ b/drivers/gpu/drm/i915/intel_dpio_phy.c @@ -131,6 +131,18 @@ struct bxt_ddi_phy_info { enum dpio_phy rcomp_phy; /** + * @reset_delay: delay in us to wait before setting the common reset + * bit in BXT_PHY_CTL_FAMILY, which effectively enables the phy. + */ + int reset_delay; + + /** + * @pwron_mask: Mask with the appropriate bit set that would cause the + * punit to power this phy if written to BXT_P_CR_GT_DISP_PWRON. + */ + u32 pwron_mask; + + /** * @channel: struct containing per channel information. */ struct { @@ -145,6 +157,7 @@ static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = { [DPIO_PHY0] = { .dual_channel = true, .rcomp_phy = DPIO_PHY1, + .pwron_mask = BIT(0), .channel = { [DPIO_CH0] = { .port = PORT_B }, @@ -154,6 +167,7 @@ static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = { [DPIO_PHY1] = { .dual_channel = false, .rcomp_phy = -1, + .pwron_mask = BIT(1), .channel = { [DPIO_CH0] = { .port = PORT_A }, @@ -161,20 +175,77 @@ static const struct bxt_ddi_phy_info bxt_ddi_phy_info[] = { }, }; +static const struct bxt_ddi_phy_info glk_ddi_phy_info[] = { + [DPIO_PHY0] = { + .dual_channel = false, + .rcomp_phy = DPIO_PHY1, + .pwron_mask = BIT(0), + .reset_delay = 20, + + .channel = { + [DPIO_CH0] = { .port = PORT_B }, + } + }, + [DPIO_PHY1] = { + .dual_channel = false, + .rcomp_phy = -1, + .pwron_mask = BIT(3), + .reset_delay = 20, + + .channel = { + [DPIO_CH0] = { .port = PORT_A }, + } + }, + [DPIO_PHY2] = { + .dual_channel = false, + .rcomp_phy = DPIO_PHY1, + .pwron_mask = BIT(1), + .reset_delay = 20, + + .channel = { + [DPIO_CH0] = { .port = PORT_C }, + } + }, +}; + static u32 bxt_phy_port_mask(const struct bxt_ddi_phy_info *phy_info) { return (phy_info->dual_channel * BIT(phy_info->channel[DPIO_CH1].port)) | BIT(phy_info->channel[DPIO_CH0].port); } -void bxt_port_to_phy_channel(enum port port, +static const struct bxt_ddi_phy_info * +bxt_get_phy_list(struct drm_i915_private *dev_priv, int *count) +{ + if (IS_GEMINILAKE(dev_priv)) { + *count = ARRAY_SIZE(glk_ddi_phy_info); + return glk_ddi_phy_info; + } else { + *count = ARRAY_SIZE(bxt_ddi_phy_info); + return bxt_ddi_phy_info; + } +} + +static const struct bxt_ddi_phy_info * +bxt_get_phy_info(struct drm_i915_private *dev_priv, enum dpio_phy phy) +{ + int count; + const struct bxt_ddi_phy_info *phy_list = + bxt_get_phy_list(dev_priv, &count); + + return &phy_list[phy]; +} + +void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port, enum dpio_phy *phy, enum dpio_channel *ch) { - const struct bxt_ddi_phy_info *phy_info; - int i; + const struct bxt_ddi_phy_info *phy_info, *phys; + int i, count; - for (i = 0; i < ARRAY_SIZE(bxt_ddi_phy_info); i++) { - phy_info = &bxt_ddi_phy_info[i]; + phys = bxt_get_phy_list(dev_priv, &count); + + for (i = 0; i < count; i++) { + phy_info = &phys[i]; if (port == phy_info->channel[DPIO_CH0].port) { *phy = i; @@ -203,7 +274,7 @@ void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv, enum dpio_phy phy; enum dpio_channel ch; - bxt_port_to_phy_channel(port, &phy, &ch); + bxt_port_to_phy_channel(dev_priv, port, &phy, &ch); /* * While we write to the group register to program all lanes at once we @@ -241,10 +312,12 @@ void bxt_ddi_phy_set_signal_level(struct drm_i915_private *dev_priv, bool bxt_ddi_phy_is_enabled(struct drm_i915_private *dev_priv, enum dpio_phy phy) { - const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy]; + const struct bxt_ddi_phy_info *phy_info; enum port port; - if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & GT_DISPLAY_POWER_ON(phy))) + phy_info = bxt_get_phy_info(dev_priv, phy); + + if (!(I915_READ(BXT_P_CR_GT_DISP_PWRON) & phy_info->pwron_mask)) return false; if ((I915_READ(BXT_PORT_CL1CM_DW0(phy)) & @@ -255,14 +328,6 @@ bool bxt_ddi_phy_is_enabled(struct drm_i915_private *dev_priv, return false; } - if (phy_info->rcomp_phy == -1 && - !(I915_READ(BXT_PORT_REF_DW3(phy)) & GRC_DONE)) { - DRM_DEBUG_DRIVER("DDI PHY %d powered, but GRC isn't done\n", - phy); - - return false; - } - if (!(I915_READ(BXT_PHY_CTL_FAMILY(phy)) & COMMON_RESET_DIS)) { DRM_DEBUG_DRIVER("DDI PHY %d powered, but still in reset\n", phy); @@ -306,9 +371,11 @@ static void bxt_phy_wait_grc_done(struct drm_i915_private *dev_priv, static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv, enum dpio_phy phy) { - const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy]; + const struct bxt_ddi_phy_info *phy_info; u32 val; + phy_info = bxt_get_phy_info(dev_priv, phy); + if (bxt_ddi_phy_is_enabled(dev_priv, phy)) { /* Still read out the GRC value for state verification */ if (phy_info->rcomp_phy != -1) @@ -317,7 +384,6 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv, if (bxt_ddi_phy_verify_state(dev_priv, phy)) { DRM_DEBUG_DRIVER("DDI PHY %d already enabled, " "won't reprogram it\n", phy); - return; } @@ -326,7 +392,7 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv, } val = I915_READ(BXT_P_CR_GT_DISP_PWRON); - val |= GT_DISPLAY_POWER_ON(phy); + val |= phy_info->pwron_mask; I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val); /* @@ -367,6 +433,9 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv, if (phy_info->rcomp_phy != -1) { uint32_t grc_code; + + bxt_phy_wait_grc_done(dev_priv, phy_info->rcomp_phy); + /* * PHY0 isn't connected to an RCOMP resistor so copy over * the corresponding calibrated value from PHY1, and disable @@ -384,31 +453,34 @@ static void _bxt_ddi_phy_init(struct drm_i915_private *dev_priv, I915_WRITE(BXT_PORT_REF_DW8(phy), val); } + if (phy_info->reset_delay) + udelay(phy_info->reset_delay); + val = I915_READ(BXT_PHY_CTL_FAMILY(phy)); val |= COMMON_RESET_DIS; I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val); - - if (phy_info->rcomp_phy == -1) - bxt_phy_wait_grc_done(dev_priv, phy); - } void bxt_ddi_phy_uninit(struct drm_i915_private *dev_priv, enum dpio_phy phy) { + const struct bxt_ddi_phy_info *phy_info; uint32_t val; + phy_info = bxt_get_phy_info(dev_priv, phy); + val = I915_READ(BXT_PHY_CTL_FAMILY(phy)); val &= ~COMMON_RESET_DIS; I915_WRITE(BXT_PHY_CTL_FAMILY(phy), val); val = I915_READ(BXT_P_CR_GT_DISP_PWRON); - val &= ~GT_DISPLAY_POWER_ON(phy); + val &= ~phy_info->pwron_mask; I915_WRITE(BXT_P_CR_GT_DISP_PWRON, val); } void bxt_ddi_phy_init(struct drm_i915_private *dev_priv, enum dpio_phy phy) { - const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy]; + const struct bxt_ddi_phy_info *phy_info = + bxt_get_phy_info(dev_priv, phy); enum dpio_phy rcomp_phy = phy_info->rcomp_phy; bool was_enabled; @@ -461,10 +533,12 @@ __phy_reg_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy, bool bxt_ddi_phy_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy) { - const struct bxt_ddi_phy_info *phy_info = &bxt_ddi_phy_info[phy]; + const struct bxt_ddi_phy_info *phy_info; uint32_t mask; bool ok; + phy_info = bxt_get_phy_info(dev_priv, phy); + #define _CHK(reg, mask, exp, fmt, ...) \ __phy_reg_verify_state(dev_priv, phy, reg, mask, exp, fmt, \ ## __VA_ARGS__) @@ -540,7 +614,7 @@ void bxt_ddi_phy_set_lane_optim_mask(struct intel_encoder *encoder, enum dpio_channel ch; int lane; - bxt_port_to_phy_channel(port, &phy, &ch); + bxt_port_to_phy_channel(dev_priv, port, &phy, &ch); for (lane = 0; lane < 4; lane++) { u32 val = I915_READ(BXT_PORT_TX_DW14_LN(phy, ch, lane)); @@ -568,7 +642,7 @@ bxt_ddi_phy_get_lane_lat_optim_mask(struct intel_encoder *encoder) int lane; uint8_t mask; - bxt_port_to_phy_channel(port, &phy, &ch); + bxt_port_to_phy_channel(dev_priv, port, &phy, &ch); mask = 0; for (lane = 0; lane < 4; lane++) { diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c index a2f0e070d38d..e59e43a9f3a6 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c @@ -23,6 +23,25 @@ #include "intel_drv.h" +/** + * DOC: Display PLLs + * + * Display PLLs used for driving outputs vary by platform. While some have + * per-pipe or per-encoder dedicated PLLs, others allow the use of any PLL + * from a pool. In the latter scenario, it is possible that multiple pipes + * share a PLL if their configurations match. + * + * This file provides an abstraction over display PLLs. The function + * intel_shared_dpll_init() initializes the PLLs for the given platform. The + * users of a PLL are tracked and that tracking is integrated with the atomic + * modest interface. During an atomic operation, a PLL can be requested for a + * given CRTC and encoder configuration by calling intel_get_shared_dpll() and + * a previously used PLL can be released with intel_release_shared_dpll(). + * Changes to the users are first staged in the atomic state, and then made + * effective by calling intel_shared_dpll_swap_state() during the atomic + * commit phase. + */ + struct intel_shared_dpll * skl_find_link_pll(struct drm_i915_private *dev_priv, int clock) { @@ -38,11 +57,11 @@ skl_find_link_pll(struct drm_i915_private *dev_priv, int clock) pll = &dev_priv->shared_dplls[i]; /* Only want to check enabled timings first */ - if (pll->config.crtc_mask == 0) + if (pll->state.crtc_mask == 0) continue; - if (memcmp(&dpll_hw_state, &pll->config.hw_state, - sizeof(pll->config.hw_state)) == 0) { + if (memcmp(&dpll_hw_state, &pll->state.hw_state, + sizeof(pll->state.hw_state)) == 0) { found = true; break; } @@ -52,8 +71,8 @@ skl_find_link_pll(struct drm_i915_private *dev_priv, int clock) for (i = DPLL_ID_SKL_DPLL1; ((found == false) && (i <= DPLL_ID_SKL_DPLL3)); i++) { pll = &dev_priv->shared_dplls[i]; - if (pll->config.crtc_mask == 0) { - pll->config.hw_state = dpll_hw_state; + if (pll->state.crtc_mask == 0) { + pll->state.hw_state = dpll_hw_state; break; } } @@ -61,6 +80,45 @@ skl_find_link_pll(struct drm_i915_private *dev_priv, int clock) return pll; } +static void +intel_atomic_duplicate_dpll_state(struct drm_i915_private *dev_priv, + struct intel_shared_dpll_state *shared_dpll) +{ + enum intel_dpll_id i; + + /* Copy shared dpll state */ + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; + + shared_dpll[i] = pll->state; + } +} + +static struct intel_shared_dpll_state * +intel_atomic_get_shared_dpll_state(struct drm_atomic_state *s) +{ + struct intel_atomic_state *state = to_intel_atomic_state(s); + + WARN_ON(!drm_modeset_is_locked(&s->dev->mode_config.connection_mutex)); + + if (!state->dpll_set) { + state->dpll_set = true; + + intel_atomic_duplicate_dpll_state(to_i915(s->dev), + state->shared_dpll); + } + + return state->shared_dpll; +} + +/** + * intel_get_shared_dpll_by_id - get a DPLL given its id + * @dev_priv: i915 device instance + * @id: pll id + * + * Returns: + * A pointer to the DPLL with @id + */ struct intel_shared_dpll * intel_get_shared_dpll_by_id(struct drm_i915_private *dev_priv, enum intel_dpll_id id) @@ -68,6 +126,14 @@ intel_get_shared_dpll_by_id(struct drm_i915_private *dev_priv, return &dev_priv->shared_dplls[id]; } +/** + * intel_get_shared_dpll_id - get the id of a DPLL + * @dev_priv: i915 device instance + * @pll: the DPLL + * + * Returns: + * The id of @pll + */ enum intel_dpll_id intel_get_shared_dpll_id(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll) @@ -79,28 +145,6 @@ intel_get_shared_dpll_id(struct drm_i915_private *dev_priv, return (enum intel_dpll_id) (pll - dev_priv->shared_dplls); } -void -intel_shared_dpll_config_get(struct intel_shared_dpll_config *config, - struct intel_shared_dpll *pll, - struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum intel_dpll_id id = intel_get_shared_dpll_id(dev_priv, pll); - - config[id].crtc_mask |= 1 << crtc->pipe; -} - -void -intel_shared_dpll_config_put(struct intel_shared_dpll_config *config, - struct intel_shared_dpll *pll, - struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum intel_dpll_id id = intel_get_shared_dpll_id(dev_priv, pll); - - config[id].crtc_mask &= ~(1 << crtc->pipe); -} - /* For ILK+ */ void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, @@ -118,6 +162,13 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv, pll->name, onoff(state), onoff(cur_state)); } +/** + * intel_prepare_shared_dpll - call a dpll's prepare hook + * @crtc: CRTC which has a shared dpll + * + * This calls the PLL's prepare hook if it has one and if the PLL is not + * already enabled. The prepare hook is platform specific. + */ void intel_prepare_shared_dpll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; @@ -128,24 +179,22 @@ void intel_prepare_shared_dpll(struct intel_crtc *crtc) return; mutex_lock(&dev_priv->dpll_lock); - WARN_ON(!pll->config.crtc_mask); + WARN_ON(!pll->state.crtc_mask); if (!pll->active_mask) { DRM_DEBUG_DRIVER("setting up %s\n", pll->name); WARN_ON(pll->on); assert_shared_dpll_disabled(dev_priv, pll); - pll->funcs.mode_set(dev_priv, pll); + pll->funcs.prepare(dev_priv, pll); } mutex_unlock(&dev_priv->dpll_lock); } /** - * intel_enable_shared_dpll - enable PCH PLL - * @dev_priv: i915 private structure - * @pipe: pipe PLL to enable + * intel_enable_shared_dpll - enable a CRTC's shared DPLL + * @crtc: CRTC which has a shared DPLL * - * The PCH PLL needs to be enabled before the PCH transcoder, since it - * drives the transcoder clock. + * Enable the shared DPLL used by @crtc. */ void intel_enable_shared_dpll(struct intel_crtc *crtc) { @@ -161,7 +210,7 @@ void intel_enable_shared_dpll(struct intel_crtc *crtc) mutex_lock(&dev_priv->dpll_lock); old_mask = pll->active_mask; - if (WARN_ON(!(pll->config.crtc_mask & crtc_mask)) || + if (WARN_ON(!(pll->state.crtc_mask & crtc_mask)) || WARN_ON(pll->active_mask & crtc_mask)) goto out; @@ -186,6 +235,12 @@ out: mutex_unlock(&dev_priv->dpll_lock); } +/** + * intel_disable_shared_dpll - disable a CRTC's shared DPLL + * @crtc: CRTC which has a shared DPLL + * + * Disable the shared DPLL used by @crtc. + */ void intel_disable_shared_dpll(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); @@ -230,7 +285,7 @@ intel_find_shared_dpll(struct intel_crtc *crtc, { struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct intel_shared_dpll *pll; - struct intel_shared_dpll_config *shared_dpll; + struct intel_shared_dpll_state *shared_dpll; enum intel_dpll_id i; shared_dpll = intel_atomic_get_shared_dpll_state(crtc_state->base.state); @@ -270,7 +325,7 @@ static void intel_reference_shared_dpll(struct intel_shared_dpll *pll, struct intel_crtc_state *crtc_state) { - struct intel_shared_dpll_config *shared_dpll; + struct intel_shared_dpll_state *shared_dpll; struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); enum intel_dpll_id i = pll->id; @@ -284,13 +339,24 @@ intel_reference_shared_dpll(struct intel_shared_dpll *pll, DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name, pipe_name(crtc->pipe)); - intel_shared_dpll_config_get(shared_dpll, pll, crtc); + shared_dpll[pll->id].crtc_mask |= 1 << crtc->pipe; } -void intel_shared_dpll_commit(struct drm_atomic_state *state) +/** + * intel_shared_dpll_swap_state - make atomic DPLL configuration effective + * @state: atomic state + * + * This is the dpll version of drm_atomic_helper_swap_state() since the + * helper does not handle driver-specific global state. + * + * For consistency with atomic helpers this function does a complete swap, + * i.e. it also puts the current state into @state, even though there is no + * need for that at this moment. + */ +void intel_shared_dpll_swap_state(struct drm_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->dev); - struct intel_shared_dpll_config *shared_dpll; + struct intel_shared_dpll_state *shared_dpll; struct intel_shared_dpll *pll; enum intel_dpll_id i; @@ -299,8 +365,13 @@ void intel_shared_dpll_commit(struct drm_atomic_state *state) shared_dpll = to_intel_atomic_state(state)->shared_dpll; for (i = 0; i < dev_priv->num_shared_dpll; i++) { + struct intel_shared_dpll_state tmp; + pll = &dev_priv->shared_dplls[i]; - pll->config = shared_dpll[i]; + + tmp = pll->state; + pll->state = shared_dpll[i]; + shared_dpll[i] = tmp; } } @@ -323,11 +394,11 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, return val & DPLL_VCO_ENABLE; } -static void ibx_pch_dpll_mode_set(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll) +static void ibx_pch_dpll_prepare(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) { - I915_WRITE(PCH_FP0(pll->id), pll->config.hw_state.fp0); - I915_WRITE(PCH_FP1(pll->id), pll->config.hw_state.fp1); + I915_WRITE(PCH_FP0(pll->id), pll->state.hw_state.fp0); + I915_WRITE(PCH_FP1(pll->id), pll->state.hw_state.fp1); } static void ibx_assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) @@ -349,7 +420,7 @@ static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv, /* PCH refclock must be enabled first */ ibx_assert_pch_refclk_enabled(dev_priv); - I915_WRITE(PCH_DPLL(pll->id), pll->config.hw_state.dpll); + I915_WRITE(PCH_DPLL(pll->id), pll->state.hw_state.dpll); /* Wait for the clocks to stabilize. */ POSTING_READ(PCH_DPLL(pll->id)); @@ -360,7 +431,7 @@ static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv, * * So write it again. */ - I915_WRITE(PCH_DPLL(pll->id), pll->config.hw_state.dpll); + I915_WRITE(PCH_DPLL(pll->id), pll->state.hw_state.dpll); POSTING_READ(PCH_DPLL(pll->id)); udelay(200); } @@ -412,8 +483,19 @@ ibx_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, return pll; } +static void ibx_dump_hw_state(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state) +{ + DRM_DEBUG_KMS("dpll_hw_state: dpll: 0x%x, dpll_md: 0x%x, " + "fp0: 0x%x, fp1: 0x%x\n", + hw_state->dpll, + hw_state->dpll_md, + hw_state->fp0, + hw_state->fp1); +} + static const struct intel_shared_dpll_funcs ibx_pch_dpll_funcs = { - .mode_set = ibx_pch_dpll_mode_set, + .prepare = ibx_pch_dpll_prepare, .enable = ibx_pch_dpll_enable, .disable = ibx_pch_dpll_disable, .get_hw_state = ibx_pch_dpll_get_hw_state, @@ -422,7 +504,7 @@ static const struct intel_shared_dpll_funcs ibx_pch_dpll_funcs = { static void hsw_ddi_wrpll_enable(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll) { - I915_WRITE(WRPLL_CTL(pll->id), pll->config.hw_state.wrpll); + I915_WRITE(WRPLL_CTL(pll->id), pll->state.hw_state.wrpll); POSTING_READ(WRPLL_CTL(pll->id)); udelay(20); } @@ -430,7 +512,7 @@ static void hsw_ddi_wrpll_enable(struct drm_i915_private *dev_priv, static void hsw_ddi_spll_enable(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll) { - I915_WRITE(SPLL_CTL, pll->config.hw_state.spll); + I915_WRITE(SPLL_CTL, pll->state.hw_state.spll); POSTING_READ(SPLL_CTL); udelay(20); } @@ -798,6 +880,13 @@ hsw_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, return pll; } +static void hsw_dump_hw_state(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state) +{ + DRM_DEBUG_KMS("dpll_hw_state: wrpll: 0x%x spll: 0x%x\n", + hw_state->wrpll, hw_state->spll); +} + static const struct intel_shared_dpll_funcs hsw_ddi_wrpll_funcs = { .enable = hsw_ddi_wrpll_enable, .disable = hsw_ddi_wrpll_disable, @@ -873,7 +962,7 @@ static void skl_ddi_pll_write_ctrl1(struct drm_i915_private *dev_priv, val &= ~(DPLL_CTRL1_HDMI_MODE(pll->id) | DPLL_CTRL1_SSC(pll->id) | DPLL_CTRL1_LINK_RATE_MASK(pll->id)); - val |= pll->config.hw_state.ctrl1 << (pll->id * 6); + val |= pll->state.hw_state.ctrl1 << (pll->id * 6); I915_WRITE(DPLL_CTRL1, val); POSTING_READ(DPLL_CTRL1); @@ -886,8 +975,8 @@ static void skl_ddi_pll_enable(struct drm_i915_private *dev_priv, skl_ddi_pll_write_ctrl1(dev_priv, pll); - I915_WRITE(regs[pll->id].cfgcr1, pll->config.hw_state.cfgcr1); - I915_WRITE(regs[pll->id].cfgcr2, pll->config.hw_state.cfgcr2); + I915_WRITE(regs[pll->id].cfgcr1, pll->state.hw_state.cfgcr1); + I915_WRITE(regs[pll->id].cfgcr2, pll->state.hw_state.cfgcr2); POSTING_READ(regs[pll->id].cfgcr1); POSTING_READ(regs[pll->id].cfgcr2); @@ -1353,6 +1442,16 @@ skl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, return pll; } +static void skl_dump_hw_state(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state) +{ + DRM_DEBUG_KMS("dpll_hw_state: " + "ctrl1: 0x%x, cfgcr1: 0x%x, cfgcr2: 0x%x\n", + hw_state->ctrl1, + hw_state->cfgcr1, + hw_state->cfgcr2); +} + static const struct intel_shared_dpll_funcs skl_ddi_pll_funcs = { .enable = skl_ddi_pll_enable, .disable = skl_ddi_pll_disable, @@ -1373,13 +1472,23 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv, enum dpio_phy phy; enum dpio_channel ch; - bxt_port_to_phy_channel(port, &phy, &ch); + bxt_port_to_phy_channel(dev_priv, port, &phy, &ch); /* Non-SSC reference */ temp = I915_READ(BXT_PORT_PLL_ENABLE(port)); temp |= PORT_PLL_REF_SEL; I915_WRITE(BXT_PORT_PLL_ENABLE(port), temp); + if (IS_GEMINILAKE(dev_priv)) { + temp = I915_READ(BXT_PORT_PLL_ENABLE(port)); + temp |= PORT_PLL_POWER_ENABLE; + I915_WRITE(BXT_PORT_PLL_ENABLE(port), temp); + + if (wait_for_us((I915_READ(BXT_PORT_PLL_ENABLE(port)) & + PORT_PLL_POWER_STATE), 200)) + DRM_ERROR("Power state not set for PLL:%d\n", port); + } + /* Disable 10 bit clock */ temp = I915_READ(BXT_PORT_PLL_EBB_4(phy, ch)); temp &= ~PORT_PLL_10BIT_CLK_ENABLE; @@ -1388,31 +1497,31 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv, /* Write P1 & P2 */ temp = I915_READ(BXT_PORT_PLL_EBB_0(phy, ch)); temp &= ~(PORT_PLL_P1_MASK | PORT_PLL_P2_MASK); - temp |= pll->config.hw_state.ebb0; + temp |= pll->state.hw_state.ebb0; I915_WRITE(BXT_PORT_PLL_EBB_0(phy, ch), temp); /* Write M2 integer */ temp = I915_READ(BXT_PORT_PLL(phy, ch, 0)); temp &= ~PORT_PLL_M2_MASK; - temp |= pll->config.hw_state.pll0; + temp |= pll->state.hw_state.pll0; I915_WRITE(BXT_PORT_PLL(phy, ch, 0), temp); /* Write N */ temp = I915_READ(BXT_PORT_PLL(phy, ch, 1)); temp &= ~PORT_PLL_N_MASK; - temp |= pll->config.hw_state.pll1; + temp |= pll->state.hw_state.pll1; I915_WRITE(BXT_PORT_PLL(phy, ch, 1), temp); /* Write M2 fraction */ temp = I915_READ(BXT_PORT_PLL(phy, ch, 2)); temp &= ~PORT_PLL_M2_FRAC_MASK; - temp |= pll->config.hw_state.pll2; + temp |= pll->state.hw_state.pll2; I915_WRITE(BXT_PORT_PLL(phy, ch, 2), temp); /* Write M2 fraction enable */ temp = I915_READ(BXT_PORT_PLL(phy, ch, 3)); temp &= ~PORT_PLL_M2_FRAC_ENABLE; - temp |= pll->config.hw_state.pll3; + temp |= pll->state.hw_state.pll3; I915_WRITE(BXT_PORT_PLL(phy, ch, 3), temp); /* Write coeff */ @@ -1420,24 +1529,24 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv, temp &= ~PORT_PLL_PROP_COEFF_MASK; temp &= ~PORT_PLL_INT_COEFF_MASK; temp &= ~PORT_PLL_GAIN_CTL_MASK; - temp |= pll->config.hw_state.pll6; + temp |= pll->state.hw_state.pll6; I915_WRITE(BXT_PORT_PLL(phy, ch, 6), temp); /* Write calibration val */ temp = I915_READ(BXT_PORT_PLL(phy, ch, 8)); temp &= ~PORT_PLL_TARGET_CNT_MASK; - temp |= pll->config.hw_state.pll8; + temp |= pll->state.hw_state.pll8; I915_WRITE(BXT_PORT_PLL(phy, ch, 8), temp); temp = I915_READ(BXT_PORT_PLL(phy, ch, 9)); temp &= ~PORT_PLL_LOCK_THRESHOLD_MASK; - temp |= pll->config.hw_state.pll9; + temp |= pll->state.hw_state.pll9; I915_WRITE(BXT_PORT_PLL(phy, ch, 9), temp); temp = I915_READ(BXT_PORT_PLL(phy, ch, 10)); temp &= ~PORT_PLL_DCO_AMP_OVR_EN_H; temp &= ~PORT_PLL_DCO_AMP_MASK; - temp |= pll->config.hw_state.pll10; + temp |= pll->state.hw_state.pll10; I915_WRITE(BXT_PORT_PLL(phy, ch, 10), temp); /* Recalibrate with new settings */ @@ -1445,7 +1554,7 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv, temp |= PORT_PLL_RECALIBRATE; I915_WRITE(BXT_PORT_PLL_EBB_4(phy, ch), temp); temp &= ~PORT_PLL_10BIT_CLK_ENABLE; - temp |= pll->config.hw_state.ebb4; + temp |= pll->state.hw_state.ebb4; I915_WRITE(BXT_PORT_PLL_EBB_4(phy, ch), temp); /* Enable PLL */ @@ -1458,6 +1567,12 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv, 200)) DRM_ERROR("PLL %d not locked\n", port); + if (IS_GEMINILAKE(dev_priv)) { + temp = I915_READ(BXT_PORT_TX_DW5_LN0(phy, ch)); + temp |= DCC_DELAY_RANGE_2; + I915_WRITE(BXT_PORT_TX_DW5_GRP(phy, ch), temp); + } + /* * While we write to the group register to program all lanes at once we * can read only lane registers and we pick lanes 0/1 for that. @@ -1465,7 +1580,7 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv, temp = I915_READ(BXT_PORT_PCS_DW12_LN01(phy, ch)); temp &= ~LANE_STAGGER_MASK; temp &= ~LANESTAGGER_STRAP_OVRD; - temp |= pll->config.hw_state.pcsdw12; + temp |= pll->state.hw_state.pcsdw12; I915_WRITE(BXT_PORT_PCS_DW12_GRP(phy, ch), temp); } @@ -1479,6 +1594,16 @@ static void bxt_ddi_pll_disable(struct drm_i915_private *dev_priv, temp &= ~PORT_PLL_ENABLE; I915_WRITE(BXT_PORT_PLL_ENABLE(port), temp); POSTING_READ(BXT_PORT_PLL_ENABLE(port)); + + if (IS_GEMINILAKE(dev_priv)) { + temp = I915_READ(BXT_PORT_PLL_ENABLE(port)); + temp &= ~PORT_PLL_POWER_ENABLE; + I915_WRITE(BXT_PORT_PLL_ENABLE(port), temp); + + if (wait_for_us(!(I915_READ(BXT_PORT_PLL_ENABLE(port)) & + PORT_PLL_POWER_STATE), 200)) + DRM_ERROR("Power state not reset for PLL:%d\n", port); + } } static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, @@ -1491,7 +1616,7 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, enum dpio_phy phy; enum dpio_channel ch; - bxt_port_to_phy_channel(port, &phy, &ch); + bxt_port_to_phy_channel(dev_priv, port, &phy, &ch); if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) return false; @@ -1759,6 +1884,25 @@ bxt_get_dpll(struct intel_crtc *crtc, return pll; } +static void bxt_dump_hw_state(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state) +{ + DRM_DEBUG_KMS("dpll_hw_state: ebb0: 0x%x, ebb4: 0x%x," + "pll0: 0x%x, pll1: 0x%x, pll2: 0x%x, pll3: 0x%x, " + "pll6: 0x%x, pll8: 0x%x, pll9: 0x%x, pll10: 0x%x, pcsdw12: 0x%x\n", + hw_state->ebb0, + hw_state->ebb4, + hw_state->pll0, + hw_state->pll1, + hw_state->pll2, + hw_state->pll3, + hw_state->pll6, + hw_state->pll8, + hw_state->pll9, + hw_state->pll10, + hw_state->pcsdw12); +} + static const struct intel_shared_dpll_funcs bxt_ddi_pll_funcs = { .enable = bxt_ddi_pll_enable, .disable = bxt_ddi_pll_disable, @@ -1799,6 +1943,9 @@ struct intel_dpll_mgr { struct intel_shared_dpll *(*get_dpll)(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, struct intel_encoder *encoder); + + void (*dump_hw_state)(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state); }; static const struct dpll_info pch_plls[] = { @@ -1810,6 +1957,7 @@ static const struct dpll_info pch_plls[] = { static const struct intel_dpll_mgr pch_pll_mgr = { .dpll_info = pch_plls, .get_dpll = ibx_get_dpll, + .dump_hw_state = ibx_dump_hw_state, }; static const struct dpll_info hsw_plls[] = { @@ -1825,6 +1973,7 @@ static const struct dpll_info hsw_plls[] = { static const struct intel_dpll_mgr hsw_pll_mgr = { .dpll_info = hsw_plls, .get_dpll = hsw_get_dpll, + .dump_hw_state = hsw_dump_hw_state, }; static const struct dpll_info skl_plls[] = { @@ -1838,6 +1987,7 @@ static const struct dpll_info skl_plls[] = { static const struct intel_dpll_mgr skl_pll_mgr = { .dpll_info = skl_plls, .get_dpll = skl_get_dpll, + .dump_hw_state = skl_dump_hw_state, }; static const struct dpll_info bxt_plls[] = { @@ -1850,8 +2000,15 @@ static const struct dpll_info bxt_plls[] = { static const struct intel_dpll_mgr bxt_pll_mgr = { .dpll_info = bxt_plls, .get_dpll = bxt_get_dpll, + .dump_hw_state = bxt_dump_hw_state, }; +/** + * intel_shared_dpll_init - Initialize shared DPLLs + * @dev: drm device + * + * Initialize shared DPLLs for @dev. + */ void intel_shared_dpll_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -1861,7 +2018,7 @@ void intel_shared_dpll_init(struct drm_device *dev) if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) dpll_mgr = &skl_pll_mgr; - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) dpll_mgr = &bxt_pll_mgr; else if (HAS_DDI(dev_priv)) dpll_mgr = &hsw_pll_mgr; @@ -1895,6 +2052,21 @@ void intel_shared_dpll_init(struct drm_device *dev) intel_ddi_pll_init(dev); } +/** + * intel_get_shared_dpll - get a shared DPLL for CRTC and encoder combination + * @crtc: CRTC + * @crtc_state: atomic state for @crtc + * @encoder: encoder + * + * Find an appropriate DPLL for the given CRTC and encoder combination. A + * reference from the @crtc to the returned pll is registered in the atomic + * state. That configuration is made effective by calling + * intel_shared_dpll_swap_state(). The reference should be released by calling + * intel_release_shared_dpll(). + * + * Returns: + * A shared DPLL to be used by @crtc and @encoder with the given @crtc_state. + */ struct intel_shared_dpll * intel_get_shared_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, @@ -1908,3 +2080,48 @@ intel_get_shared_dpll(struct intel_crtc *crtc, return dpll_mgr->get_dpll(crtc, crtc_state, encoder); } + +/** + * intel_release_shared_dpll - end use of DPLL by CRTC in atomic state + * @dpll: dpll in use by @crtc + * @crtc: crtc + * @state: atomic state + * + * This function releases the reference from @crtc to @dpll from the + * atomic @state. The new configuration is made effective by calling + * intel_shared_dpll_swap_state(). + */ +void intel_release_shared_dpll(struct intel_shared_dpll *dpll, + struct intel_crtc *crtc, + struct drm_atomic_state *state) +{ + struct intel_shared_dpll_state *shared_dpll_state; + + shared_dpll_state = intel_atomic_get_shared_dpll_state(state); + shared_dpll_state[dpll->id].crtc_mask &= ~(1 << crtc->pipe); +} + +/** + * intel_shared_dpll_dump_hw_state - write hw_state to dmesg + * @dev_priv: i915 drm device + * @hw_state: hw state to be written to the log + * + * Write the relevant values in @hw_state to dmesg using DRM_DEBUG_KMS. + */ +void intel_dpll_dump_hw_state(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state) +{ + if (dev_priv->dpll_mgr) { + dev_priv->dpll_mgr->dump_hw_state(dev_priv, hw_state); + } else { + /* fallback for platforms that don't use the shared dpll + * infrastructure + */ + DRM_DEBUG_KMS("dpll_hw_state: dpll: 0x%x, dpll_md: 0x%x, " + "fp0: 0x%x, fp1: 0x%x\n", + hw_state->dpll, + hw_state->dpll_md, + hw_state->fp0, + hw_state->fp1); + } +} diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.h b/drivers/gpu/drm/i915/intel_dpll_mgr.h index f4385353bc11..af1497eb4f9c 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.h @@ -40,32 +40,72 @@ struct intel_encoder; struct intel_shared_dpll; struct intel_dpll_mgr; +/** + * enum intel_dpll_id - possible DPLL ids + * + * Enumeration of possible IDs for a DPLL. Real shared dpll ids must be >= 0. + */ enum intel_dpll_id { - DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */ - /* real shared dpll ids must be >= 0 */ + /** + * @DPLL_ID_PRIVATE: non-shared dpll in use + */ + DPLL_ID_PRIVATE = -1, + + /** + * @DPLL_ID_PCH_PLL_A: DPLL A in ILK, SNB and IVB + */ DPLL_ID_PCH_PLL_A = 0, + /** + * @DPLL_ID_PCH_PLL_B: DPLL B in ILK, SNB and IVB + */ DPLL_ID_PCH_PLL_B = 1, - /* hsw/bdw */ + + + /** + * @DPLL_ID_WRPLL1: HSW and BDW WRPLL1 + */ DPLL_ID_WRPLL1 = 0, + /** + * @DPLL_ID_WRPLL2: HSW and BDW WRPLL2 + */ DPLL_ID_WRPLL2 = 1, + /** + * @DPLL_ID_SPLL: HSW and BDW SPLL + */ DPLL_ID_SPLL = 2, + /** + * @DPLL_ID_LCPLL_810: HSW and BDW 0.81 GHz LCPLL + */ DPLL_ID_LCPLL_810 = 3, + /** + * @DPLL_ID_LCPLL_1350: HSW and BDW 1.35 GHz LCPLL + */ DPLL_ID_LCPLL_1350 = 4, + /** + * @DPLL_ID_LCPLL_2700: HSW and BDW 2.7 GHz LCPLL + */ DPLL_ID_LCPLL_2700 = 5, - /* skl */ + + /** + * @DPLL_ID_SKL_DPLL0: SKL and later DPLL0 + */ DPLL_ID_SKL_DPLL0 = 0, + /** + * @DPLL_ID_SKL_DPLL1: SKL and later DPLL1 + */ DPLL_ID_SKL_DPLL1 = 1, + /** + * @DPLL_ID_SKL_DPLL2: SKL and later DPLL2 + */ DPLL_ID_SKL_DPLL2 = 2, + /** + * @DPLL_ID_SKL_DPLL3: SKL and later DPLL3 + */ DPLL_ID_SKL_DPLL3 = 3, }; #define I915_NUM_PLLS 6 -/** Inform the state checker that the DPLL is kept enabled even if not - * in use by any crtc. - */ -#define INTEL_DPLL_ALWAYS_ON (1 << 0) - struct intel_dpll_hw_state { /* i9xx, pch plls */ uint32_t dpll; @@ -93,36 +133,120 @@ struct intel_dpll_hw_state { pcsdw12; }; -struct intel_shared_dpll_config { - unsigned crtc_mask; /* mask of CRTCs sharing this PLL */ +/** + * struct intel_shared_dpll_state - hold the DPLL atomic state + * + * This structure holds an atomic state for the DPLL, that can represent + * either its current state (in struct &intel_shared_dpll) or a desired + * future state which would be applied by an atomic mode set (stored in + * a struct &intel_atomic_state). + * + * See also intel_get_shared_dpll() and intel_release_shared_dpll(). + */ +struct intel_shared_dpll_state { + /** + * @crtc_mask: mask of CRTC using this DPLL, active or not + */ + unsigned crtc_mask; + + /** + * @hw_state: hardware configuration for the DPLL stored in + * struct &intel_dpll_hw_state. + */ struct intel_dpll_hw_state hw_state; }; +/** + * struct intel_shared_dpll_funcs - platform specific hooks for managing DPLLs + */ struct intel_shared_dpll_funcs { - /* The mode_set hook is optional and should be used together with the - * intel_prepare_shared_dpll function. */ - void (*mode_set)(struct drm_i915_private *dev_priv, - struct intel_shared_dpll *pll); + /** + * @prepare: + * + * Optional hook to perform operations prior to enabling the PLL. + * Called from intel_prepare_shared_dpll() function unless the PLL + * is already enabled. + */ + void (*prepare)(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll); + + /** + * @enable: + * + * Hook for enabling the pll, called from intel_enable_shared_dpll() + * if the pll is not already enabled. + */ void (*enable)(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll); + + /** + * @disable: + * + * Hook for disabling the pll, called from intel_disable_shared_dpll() + * only when it is safe to disable the pll, i.e., there are no more + * tracked users for it. + */ void (*disable)(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll); + + /** + * @get_hw_state: + * + * Hook for reading the values currently programmed to the DPLL + * registers. This is used for initial hw state readout and state + * verification after a mode set. + */ bool (*get_hw_state)(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, struct intel_dpll_hw_state *hw_state); }; +/** + * struct intel_shared_dpll - display PLL with tracked state and users + */ struct intel_shared_dpll { - struct intel_shared_dpll_config config; + /** + * @state: + * + * Store the state for the pll, including the its hw state + * and CRTCs using it. + */ + struct intel_shared_dpll_state state; - unsigned active_mask; /* mask of active CRTCs (i.e. DPMS on) */ - bool on; /* is the PLL actually active? Disabled during modeset */ + /** + * @active_mask: mask of active CRTCs (i.e. DPMS on) using this DPLL + */ + unsigned active_mask; + + /** + * @on: is the PLL actually active? Disabled during modeset + */ + bool on; + + /** + * @name: DPLL name; used for logging + */ const char *name; - /* should match the index in the dev_priv->shared_dplls array */ + + /** + * @id: unique indentifier for this DPLL; should match the index in the + * dev_priv->shared_dplls array + */ enum intel_dpll_id id; + /** + * @funcs: platform specific hooks + */ struct intel_shared_dpll_funcs funcs; +#define INTEL_DPLL_ALWAYS_ON (1 << 0) + /** + * @flags: + * + * INTEL_DPLL_ALWAYS_ON + * Inform the state checker that the DPLL is kept enabled even if + * not in use by any CRTC. + */ uint32_t flags; }; @@ -138,14 +262,6 @@ intel_get_shared_dpll_by_id(struct drm_i915_private *dev_priv, enum intel_dpll_id intel_get_shared_dpll_id(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll); -void -intel_shared_dpll_config_get(struct intel_shared_dpll_config *config, - struct intel_shared_dpll *pll, - struct intel_crtc *crtc); -void -intel_shared_dpll_config_put(struct intel_shared_dpll_config *config, - struct intel_shared_dpll *pll, - struct intel_crtc *crtc); void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, bool state); @@ -154,12 +270,18 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, struct intel_crtc_state *state, struct intel_encoder *encoder); +void intel_release_shared_dpll(struct intel_shared_dpll *dpll, + struct intel_crtc *crtc, + struct drm_atomic_state *state); void intel_prepare_shared_dpll(struct intel_crtc *crtc); void intel_enable_shared_dpll(struct intel_crtc *crtc); void intel_disable_shared_dpll(struct intel_crtc *crtc); -void intel_shared_dpll_commit(struct drm_atomic_state *state); +void intel_shared_dpll_swap_state(struct drm_atomic_state *state); void intel_shared_dpll_init(struct drm_device *dev); +void intel_dpll_dump_hw_state(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state); + /* BXT dpll related functions */ bool bxt_ddi_dp_set_dpll_hw_state(int clock, struct intel_dpll_hw_state *dpll_hw_state); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 03a2112004f9..344f238b283f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -28,10 +28,12 @@ #include <linux/async.h> #include <linux/i2c.h> #include <linux/hdmi.h> +#include <linux/sched/clock.h> #include <drm/i915_drm.h> #include "i915_drv.h" #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_helper.h> #include <drm/drm_dp_dual_mode_helper.h> #include <drm/drm_dp_mst_helper.h> @@ -358,7 +360,7 @@ struct intel_atomic_state { /* SKL/KBL Only */ unsigned int cdclk_pll_vco; - struct intel_shared_dpll_config shared_dpll[I915_NUM_PLLS]; + struct intel_shared_dpll_state shared_dpll[I915_NUM_PLLS]; /* * Current watermarks can't be trusted during hardware readout, so @@ -694,8 +696,9 @@ struct intel_crtc { * some outputs connected to this crtc. */ bool active; - unsigned long enabled_power_domains; bool lowfreq_avail; + u8 plane_ids_mask; + unsigned long enabled_power_domains; struct intel_overlay *overlay; struct intel_flip_work *flip_work; @@ -769,7 +772,8 @@ struct intel_plane_wm_parameters { struct intel_plane { struct drm_plane base; - int plane; + u8 plane; + enum plane_id id; enum pipe pipe; bool can_scale; int max_downscale; @@ -843,11 +847,13 @@ struct intel_hdmi { enum hdmi_picture_aspect aspect_ratio; struct intel_connector *attached_connector; void (*write_infoframe)(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, enum hdmi_infoframe_type type, const void *frame, ssize_t len); void (*set_infoframes)(struct drm_encoder *encoder, bool enable, - const struct drm_display_mode *adjusted_mode); + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state); bool (*infoframe_enabled)(struct drm_encoder *encoder, const struct intel_crtc_state *pipe_config); }; @@ -883,6 +889,16 @@ struct intel_dp_desc { u8 sw_minor_rev; } __packed; +struct intel_dp_compliance_data { + unsigned long edid; +}; + +struct intel_dp_compliance { + unsigned long test_type; + struct intel_dp_compliance_data test_data; + bool test_active; +}; + struct intel_dp { i915_reg_t output_reg; i915_reg_t aux_ch_ctl_reg; @@ -905,6 +921,10 @@ struct intel_dp { /* sink rates as reported by DP_SUPPORTED_LINK_RATES */ uint8_t num_sink_rates; int sink_rates[DP_MAX_SUPPORTED_RATES]; + /* Max lane count for the sink as per DPCD registers */ + uint8_t max_sink_lane_count; + /* Max link BW for the sink as per DPCD registers */ + int max_sink_link_bw; /* sink or branch descriptor */ struct intel_dp_desc desc; struct drm_dp_aux aux; @@ -928,6 +948,12 @@ struct intel_dp { */ enum pipe pps_pipe; /* + * Pipe currently driving the port. Used for preventing + * the use of the PPS for any pipe currentrly driving + * external DP as that will mess things up on VLV. + */ + enum pipe active_pipe; + /* * Set if the sequencer may be reset due to a power transition, * requiring a reinitialization. Only relevant on BXT. */ @@ -958,9 +984,7 @@ struct intel_dp { void (*prepare_link_retrain)(struct intel_dp *intel_dp); /* Displayport compliance testing */ - unsigned long compliance_test_type; - unsigned long compliance_test_data; - bool compliance_test_active; + struct intel_dp_compliance compliance; }; struct intel_lspcon { @@ -1093,6 +1117,12 @@ dp_to_dig_port(struct intel_dp *intel_dp) return container_of(intel_dp, struct intel_digital_port, dp); } +static inline struct intel_lspcon * +dp_to_lspcon(struct intel_dp *intel_dp) +{ + return &dp_to_dig_port(intel_dp)->lspcon; +} + static inline struct intel_digital_port * hdmi_to_dig_port(struct intel_hdmi *intel_hdmi) { @@ -1145,7 +1175,7 @@ void gen9_enable_guc_interrupts(struct drm_i915_private *dev_priv); void gen9_disable_guc_interrupts(struct drm_i915_private *dev_priv); /* intel_crt.c */ -void intel_crt_init(struct drm_device *dev); +void intel_crt_init(struct drm_i915_private *dev_priv); void intel_crt_reset(struct drm_encoder *encoder); /* intel_ddi.c */ @@ -1156,7 +1186,7 @@ void intel_ddi_fdi_post_disable(struct intel_encoder *intel_encoder, struct drm_connector_state *old_conn_state); void intel_prepare_dp_ddi_buffers(struct intel_encoder *encoder); void hsw_fdi_link_train(struct drm_crtc *crtc); -void intel_ddi_init(struct drm_device *dev, enum port port); +void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port); enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder); bool intel_ddi_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe); void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc); @@ -1169,6 +1199,8 @@ bool intel_ddi_pll_select(struct intel_crtc *crtc, void intel_ddi_set_pipe_settings(struct drm_crtc *crtc); void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp); bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); +bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv, + struct intel_crtc *intel_crtc); void intel_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config); struct intel_encoder * @@ -1196,6 +1228,8 @@ void intel_audio_codec_enable(struct intel_encoder *encoder, void intel_audio_codec_disable(struct intel_encoder *encoder); void i915_audio_component_init(struct drm_i915_private *dev_priv); void i915_audio_component_cleanup(struct drm_i915_private *dev_priv); +void intel_audio_init(struct drm_i915_private *dev_priv); +void intel_audio_deinit(struct drm_i915_private *dev_priv); /* intel_display.c */ enum transcoder intel_crtc_pch_transcoder(struct intel_crtc *crtc); @@ -1213,7 +1247,7 @@ unsigned int intel_fb_xy_to_linear(int x, int y, void intel_add_fb_offsets(int *x, int *y, const struct intel_plane_state *state, int plane); unsigned int intel_rotation_info_size(const struct intel_rotation_info *rot_info); -bool intel_has_pending_fb_unpin(struct drm_device *dev); +bool intel_has_pending_fb_unpin(struct drm_i915_private *dev_priv); void intel_mark_busy(struct drm_i915_private *dev_priv); void intel_mark_idle(struct drm_i915_private *dev_priv); void intel_crtc_restore_mode(struct drm_crtc *crtc); @@ -1384,12 +1418,15 @@ void intel_csr_ucode_suspend(struct drm_i915_private *); void intel_csr_ucode_resume(struct drm_i915_private *); /* intel_dp.c */ -bool intel_dp_init(struct drm_device *dev, i915_reg_t output_reg, enum port port); +bool intel_dp_init(struct drm_i915_private *dev_priv, i915_reg_t output_reg, + enum port port); bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); void intel_dp_set_link_params(struct intel_dp *intel_dp, int link_rate, uint8_t lane_count, bool link_mst); +int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, + int link_rate, uint8_t lane_count); void intel_dp_start_link_train(struct intel_dp *intel_dp); void intel_dp_stop_link_train(struct intel_dp *intel_dp); void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode); @@ -1451,6 +1488,10 @@ bool intel_dp_read_dpcd(struct intel_dp *intel_dp); bool __intel_dp_read_desc(struct intel_dp *intel_dp, struct intel_dp_desc *desc); bool intel_dp_read_desc(struct intel_dp *intel_dp); +int intel_dp_link_required(int pixel_clock, int bpp); +int intel_dp_max_data_rate(int max_link_clock, int max_lanes); +bool intel_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port); /* intel_dp_aux_backlight.c */ int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector); @@ -1459,13 +1500,13 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector); int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id); void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port); /* intel_dsi.c */ -void intel_dsi_init(struct drm_device *dev); +void intel_dsi_init(struct drm_i915_private *dev_priv); /* intel_dsi_dcs_backlight.c */ int intel_dsi_dcs_init_backlight_funcs(struct intel_connector *intel_connector); /* intel_dvo.c */ -void intel_dvo_init(struct drm_device *dev); +void intel_dvo_init(struct drm_i915_private *dev_priv); /* intel_hotplug.c */ void intel_hpd_poll_init(struct drm_i915_private *dev_priv); @@ -1529,7 +1570,8 @@ void intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv); void intel_fbc_handle_fifo_underrun_irq(struct drm_i915_private *dev_priv); /* intel_hdmi.c */ -void intel_hdmi_init(struct drm_device *dev, i915_reg_t hdmi_reg, enum port port); +void intel_hdmi_init(struct drm_i915_private *dev_priv, i915_reg_t hdmi_reg, + enum port port); void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); @@ -1540,7 +1582,7 @@ void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable); /* intel_lvds.c */ -void intel_lvds_init(struct drm_device *dev); +void intel_lvds_init(struct drm_i915_private *dev_priv); struct intel_encoder *intel_get_lvds_encoder(struct drm_device *dev); bool intel_is_dual_link_lvds(struct drm_device *dev); @@ -1585,9 +1627,9 @@ int intel_panel_setup_backlight(struct drm_connector *connector, void intel_panel_enable_backlight(struct intel_connector *connector); void intel_panel_disable_backlight(struct intel_connector *connector); void intel_panel_destroy_backlight(struct drm_connector *connector); -enum drm_connector_status intel_panel_detect(struct drm_device *dev); +enum drm_connector_status intel_panel_detect(struct drm_i915_private *dev_priv); extern struct drm_display_mode *intel_find_panel_downclock( - struct drm_device *dev, + struct drm_i915_private *dev_priv, struct drm_display_mode *fixed_mode, struct drm_connector *connector); @@ -1613,7 +1655,7 @@ void intel_psr_invalidate(struct drm_i915_private *dev_priv, void intel_psr_flush(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits, enum fb_op_origin origin); -void intel_psr_init(struct drm_device *dev); +void intel_psr_init(struct drm_i915_private *dev_priv); void intel_psr_single_frame_update(struct drm_i915_private *dev_priv, unsigned frontbuffer_bits); @@ -1717,7 +1759,7 @@ int ilk_wm_max_level(const struct drm_i915_private *dev_priv); void intel_update_watermarks(struct intel_crtc *crtc); void intel_init_pm(struct drm_i915_private *dev_priv); void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv); -void intel_pm_setup(struct drm_device *dev); +void intel_pm_setup(struct drm_i915_private *dev_priv); void intel_gpu_ips_init(struct drm_i915_private *dev_priv); void intel_gpu_ips_teardown(void); void intel_init_gt_powersave(struct drm_i915_private *dev_priv); @@ -1758,7 +1800,7 @@ static inline int intel_enable_rc6(void) } /* intel_sdvo.c */ -bool intel_sdvo_init(struct drm_device *dev, +bool intel_sdvo_init(struct drm_i915_private *dev_priv, i915_reg_t reg, enum port port); @@ -1773,7 +1815,7 @@ void intel_pipe_update_start(struct intel_crtc *crtc); void intel_pipe_update_end(struct intel_crtc *crtc, struct intel_flip_work *work); /* intel_tv.c */ -void intel_tv_init(struct drm_device *dev); +void intel_tv_init(struct drm_i915_private *dev_priv); /* intel_atomic.c */ int intel_connector_atomic_get_property(struct drm_connector *connector, @@ -1785,8 +1827,6 @@ void intel_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); struct drm_atomic_state *intel_atomic_state_alloc(struct drm_device *dev); void intel_atomic_state_clear(struct drm_atomic_state *); -struct intel_shared_dpll_config * -intel_atomic_get_shared_dpll_state(struct drm_atomic_state *s); static inline struct intel_crtc_state * intel_atomic_get_crtc_state(struct drm_atomic_state *state, @@ -1800,6 +1840,20 @@ intel_atomic_get_crtc_state(struct drm_atomic_state *state, return to_intel_crtc_state(crtc_state); } +static inline struct intel_crtc_state * +intel_atomic_get_existing_crtc_state(struct drm_atomic_state *state, + struct intel_crtc *crtc) +{ + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_existing_crtc_state(state, &crtc->base); + + if (crtc_state) + return to_intel_crtc_state(crtc_state); + else + return NULL; +} + static inline struct intel_plane_state * intel_atomic_get_existing_plane_state(struct drm_atomic_state *state, struct intel_plane *plane) @@ -1821,6 +1875,8 @@ struct drm_plane_state *intel_plane_duplicate_state(struct drm_plane *plane); void intel_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); extern const struct drm_plane_helper_funcs intel_plane_helper_funcs; +int intel_plane_atomic_check_with_state(struct intel_crtc_state *crtc_state, + struct intel_plane_state *intel_state); /* intel_color.c */ void intel_color_init(struct drm_crtc *crtc); @@ -1831,4 +1887,16 @@ void intel_color_load_luts(struct drm_crtc_state *crtc_state); /* intel_lspcon.c */ bool lspcon_init(struct intel_digital_port *intel_dig_port); void lspcon_resume(struct intel_lspcon *lspcon); +void lspcon_wait_pcon_mode(struct intel_lspcon *lspcon); + +/* intel_pipe_crc.c */ +int intel_pipe_crc_create(struct drm_minor *minor); +void intel_pipe_crc_cleanup(struct drm_minor *minor); +#ifdef CONFIG_DEBUG_FS +int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name, + size_t *values_cnt); +#else +#define intel_crtc_set_crc_source NULL +#endif +extern const struct file_operations i915_display_crc_ctl_fops; #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 5b72c50d6f76..16732e7bc08e 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -340,7 +340,7 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder, /* DSI uses short packets for sync events, so clear mode flags for DSI */ adjusted_mode->flags = 0; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { /* Dual link goes to DSI transcoder A. */ if (intel_dsi->ports == BIT(PORT_C)) pipe_config->cpu_transcoder = TRANSCODER_DSI_C; @@ -379,7 +379,8 @@ static void bxt_dsi_device_ready(struct intel_encoder *encoder) val &= ~ULPS_STATE_MASK; val |= (ULPS_STATE_ENTER | DEVICE_READY); I915_WRITE(MIPI_DEVICE_READY(port), val); - usleep_range(2, 3); + /* at least 2us - relaxed for hrtimer subsystem optimization */ + usleep_range(10, 50); /* 3. Exit ULPS */ val = I915_READ(MIPI_DEVICE_READY(port)); @@ -441,7 +442,7 @@ static void intel_dsi_device_ready(struct intel_encoder *encoder) if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) vlv_dsi_device_ready(encoder); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_dsi_device_ready(encoder); } @@ -464,7 +465,7 @@ static void intel_dsi_port_enable(struct intel_encoder *encoder) } for_each_dsi_port(port, intel_dsi->ports) { - i915_reg_t port_ctrl = IS_BROXTON(dev_priv) ? + i915_reg_t port_ctrl = IS_GEN9_LP(dev_priv) ? BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); u32 temp; @@ -476,7 +477,10 @@ static void intel_dsi_port_enable(struct intel_encoder *encoder) if (intel_dsi->ports == (BIT(PORT_A) | BIT(PORT_C))) { temp |= (intel_dsi->dual_link - 1) << DUAL_LINK_MODE_SHIFT; - temp |= intel_crtc->pipe ? + if (IS_BROXTON(dev_priv)) + temp |= LANE_CONFIGURATION_DUAL_LINK_A; + else + temp |= intel_crtc->pipe ? LANE_CONFIGURATION_DUAL_LINK_B : LANE_CONFIGURATION_DUAL_LINK_A; } @@ -494,7 +498,7 @@ static void intel_dsi_port_disable(struct intel_encoder *encoder) enum port port; for_each_dsi_port(port, intel_dsi->ports) { - i915_reg_t port_ctrl = IS_BROXTON(dev_priv) ? + i915_reg_t port_ctrl = IS_GEN9_LP(dev_priv) ? BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); u32 temp; @@ -663,7 +667,7 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder) DRM_DEBUG_KMS("\n"); for_each_dsi_port(port, intel_dsi->ports) { /* Common bit for both MIPI Port A & MIPI Port C on VLV/CHV */ - i915_reg_t port_ctrl = IS_BROXTON(dev_priv) ? + i915_reg_t port_ctrl = IS_GEN9_LP(dev_priv) ? BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(PORT_A); u32 val; @@ -695,8 +699,6 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder) I915_WRITE(MIPI_DEVICE_READY(port), 0x00); usleep_range(2000, 2500); } - - intel_disable_dsi_pll(encoder); } static void intel_dsi_post_disable(struct intel_encoder *encoder, @@ -712,6 +714,8 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder, intel_dsi_clear_device_ready(encoder); + intel_disable_dsi_pll(encoder); + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { u32 val; @@ -755,12 +759,12 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, * configuration, otherwise accessing DSI registers will hang the * machine. See BSpec North Display Engine registers/MIPI[BXT]. */ - if (IS_BROXTON(dev_priv) && !intel_dsi_pll_is_enabled(dev_priv)) + if (IS_GEN9_LP(dev_priv) && !intel_dsi_pll_is_enabled(dev_priv)) goto out_put_power; /* XXX: this only works for one DSI output */ for_each_dsi_port(port, intel_dsi->ports) { - i915_reg_t ctrl_reg = IS_BROXTON(dev_priv) ? + i915_reg_t ctrl_reg = IS_GEN9_LP(dev_priv) ? BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); bool enabled = I915_READ(ctrl_reg) & DPI_ENABLE; @@ -785,7 +789,7 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, if (!(I915_READ(MIPI_DEVICE_READY(port)) & DEVICE_READY)) continue; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { u32 tmp = I915_READ(MIPI_CTRL(port)); tmp &= BXT_PIPE_SELECT_MASK; tmp >>= BXT_PIPE_SELECT_SHIFT; @@ -973,7 +977,7 @@ static void intel_dsi_get_config(struct intel_encoder *encoder, u32 pclk; DRM_DEBUG_KMS("\n"); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) bxt_dsi_get_pipe_config(encoder, pipe_config); pclk = intel_dsi_get_pclk(encoder, pipe_config->pipe_bpp, @@ -1065,7 +1069,7 @@ static void set_dsi_timings(struct drm_encoder *encoder, hbp = txbyteclkhs(hbp, bpp, lane_count, intel_dsi->burst_mode_ratio); for_each_dsi_port(port, intel_dsi->ports) { - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { /* * Program hdisplay and vdisplay on MIPI transcoder. * This is different from calculated hactive and @@ -1152,7 +1156,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder, tmp &= ~READ_REQUEST_PRIORITY_MASK; I915_WRITE(MIPI_CTRL(port), tmp | READ_REQUEST_PRIORITY_HIGH); - } else if (IS_BROXTON(dev_priv)) { + } else if (IS_GEN9_LP(dev_priv)) { enum pipe pipe = intel_crtc->pipe; tmp = I915_READ(MIPI_CTRL(port)); @@ -1190,7 +1194,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder, if (intel_dsi->clock_stop) tmp |= CLOCKSTOP; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { tmp |= BXT_DPHY_DEFEATURE_EN; if (!is_cmd_mode(intel_dsi)) tmp |= BXT_DEFEATURE_DPI_FIFO_CTR; @@ -1241,7 +1245,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder, I915_WRITE(MIPI_INIT_COUNT(port), txclkesc(intel_dsi->escape_clk_div, 100)); - if (IS_BROXTON(dev_priv) && (!intel_dsi->dual_link)) { + if (IS_GEN9_LP(dev_priv) && (!intel_dsi->dual_link)) { /* * BXT spec says write MIPI_INIT_COUNT for * both the ports, even if only one is @@ -1424,15 +1428,15 @@ static void intel_dsi_add_properties(struct intel_connector *connector) } } -void intel_dsi_init(struct drm_device *dev) +void intel_dsi_init(struct drm_i915_private *dev_priv) { + struct drm_device *dev = &dev_priv->drm; struct intel_dsi *intel_dsi; struct intel_encoder *intel_encoder; struct drm_encoder *encoder; struct intel_connector *intel_connector; struct drm_connector *connector; struct drm_display_mode *scan, *fixed_mode = NULL; - struct drm_i915_private *dev_priv = to_i915(dev); enum port port; unsigned int i; @@ -1444,7 +1448,7 @@ void intel_dsi_init(struct drm_device *dev) if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { dev_priv->mipi_mmio_base = VLV_MIPI_BASE; - } else if (IS_BROXTON(dev_priv)) { + } else if (IS_GEN9_LP(dev_priv)) { dev_priv->mipi_mmio_base = BXT_MIPI_BASE; } else { DRM_ERROR("Unsupported Mipi device to reg base"); @@ -1485,7 +1489,7 @@ void intel_dsi_init(struct drm_device *dev) * On BYT/CHV, pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI * port C. BXT isn't limited like this. */ - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) intel_encoder->crtc_mask = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C); else if (port == PORT_A) intel_encoder->crtc_mask = BIT(PIPE_A); diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c index 47cd1b20fb3e..8f683b8b1816 100644 --- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c +++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c @@ -29,6 +29,7 @@ #include <drm/drm_edid.h> #include <drm/i915_drm.h> #include <drm/drm_panel.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <video/mipi_display.h> #include <asm/intel-mid.h> @@ -305,19 +306,44 @@ static void chv_exec_gpio(struct drm_i915_private *dev_priv, mutex_unlock(&dev_priv->sb_lock); } +static void bxt_exec_gpio(struct drm_i915_private *dev_priv, + u8 gpio_source, u8 gpio_index, bool value) +{ + /* XXX: this table is a quick ugly hack. */ + static struct gpio_desc *bxt_gpio_table[U8_MAX + 1]; + struct gpio_desc *gpio_desc = bxt_gpio_table[gpio_index]; + + if (!gpio_desc) { + gpio_desc = devm_gpiod_get_index(dev_priv->drm.dev, + "panel", gpio_index, + value ? GPIOD_OUT_LOW : + GPIOD_OUT_HIGH); + + if (IS_ERR_OR_NULL(gpio_desc)) { + DRM_ERROR("GPIO index %u request failed (%ld)\n", + gpio_index, PTR_ERR(gpio_desc)); + return; + } + + bxt_gpio_table[gpio_index] = gpio_desc; + } + + gpiod_set_value(gpio_desc, value); +} + static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data) { struct drm_device *dev = intel_dsi->base.base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - u8 gpio_source, gpio_index; + u8 gpio_source, gpio_index = 0, gpio_number; bool value; DRM_DEBUG_KMS("\n"); if (dev_priv->vbt.dsi.seq_version >= 3) - data++; + gpio_index = *data++; - gpio_index = *data++; + gpio_number = *data++; /* gpio source in sequence v2 only */ if (dev_priv->vbt.dsi.seq_version == 2) @@ -329,11 +355,11 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data) value = *data++ & 1; if (IS_VALLEYVIEW(dev_priv)) - vlv_exec_gpio(dev_priv, gpio_source, gpio_index, value); + vlv_exec_gpio(dev_priv, gpio_source, gpio_number, value); else if (IS_CHERRYVIEW(dev_priv)) - chv_exec_gpio(dev_priv, gpio_source, gpio_index, value); + chv_exec_gpio(dev_priv, gpio_source, gpio_number, value); else - DRM_DEBUG_KMS("GPIO element not supported on this platform\n"); + bxt_exec_gpio(dev_priv, gpio_source, gpio_index, value); return data; } diff --git a/drivers/gpu/drm/i915/intel_dsi_pll.c b/drivers/gpu/drm/i915/intel_dsi_pll.c index 56eff6004bc0..61440e5c2563 100644 --- a/drivers/gpu/drm/i915/intel_dsi_pll.c +++ b/drivers/gpu/drm/i915/intel_dsi_pll.c @@ -156,8 +156,10 @@ static void vlv_enable_dsi_pll(struct intel_encoder *encoder, vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, config->dsi_pll.ctrl & ~DSI_PLL_VCO_EN); - /* wait at least 0.5 us after ungating before enabling VCO */ - usleep_range(1, 10); + /* wait at least 0.5 us after ungating before enabling VCO, + * allow hrtimer subsystem optimization by relaxing timing + */ + usleep_range(10, 50); vlv_cck_write(dev_priv, CCK_REG_DSI_PLL_CONTROL, config->dsi_pll.ctrl); @@ -351,7 +353,7 @@ static u32 bxt_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp, u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp, struct intel_crtc_state *config) { - if (IS_BROXTON(to_i915(encoder->base.dev))) + if (IS_GEN9_LP(to_i915(encoder->base.dev))) return bxt_dsi_get_pclk(encoder, pipe_bpp, config); else return vlv_dsi_get_pclk(encoder, pipe_bpp, config); @@ -504,7 +506,7 @@ static void bxt_enable_dsi_pll(struct intel_encoder *encoder, bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv) { - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) return bxt_dsi_pll_is_enabled(dev_priv); MISSING_CASE(INTEL_DEVID(dev_priv)); @@ -519,7 +521,7 @@ int intel_compute_dsi_pll(struct intel_encoder *encoder, if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) return vlv_compute_dsi_pll(encoder, config); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) return bxt_compute_dsi_pll(encoder, config); return -ENODEV; @@ -532,7 +534,7 @@ void intel_enable_dsi_pll(struct intel_encoder *encoder, if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) vlv_enable_dsi_pll(encoder, config); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_enable_dsi_pll(encoder, config); } @@ -542,7 +544,7 @@ void intel_disable_dsi_pll(struct intel_encoder *encoder) if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) vlv_disable_dsi_pll(encoder); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_disable_dsi_pll(encoder); } @@ -566,7 +568,7 @@ void intel_dsi_reset_clocks(struct intel_encoder *encoder, enum port port) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) bxt_dsi_reset_clocks(encoder, port); else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) vlv_dsi_reset_clocks(encoder, port); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 708645443046..50da89dcb92b 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -422,9 +422,8 @@ static enum port intel_dvo_port(i915_reg_t dvo_reg) return PORT_C; } -void intel_dvo_init(struct drm_device *dev) +void intel_dvo_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_encoder *intel_encoder; struct intel_dvo *intel_dvo; struct intel_connector *intel_connector; @@ -511,7 +510,7 @@ void intel_dvo_init(struct drm_device *dev) continue; port = intel_dvo_port(dvo->dvo_reg); - drm_encoder_init(dev, &intel_encoder->base, + drm_encoder_init(&dev_priv->drm, &intel_encoder->base, &intel_dvo_enc_funcs, encoder_type, "DVO %c", port_name(port)); @@ -523,14 +522,14 @@ void intel_dvo_init(struct drm_device *dev) case INTEL_DVO_CHIP_TMDS: intel_encoder->cloneable = (1 << INTEL_OUTPUT_ANALOG) | (1 << INTEL_OUTPUT_DVO); - drm_connector_init(dev, connector, + drm_connector_init(&dev_priv->drm, connector, &intel_dvo_connector_funcs, DRM_MODE_CONNECTOR_DVII); encoder_type = DRM_MODE_ENCODER_TMDS; break; case INTEL_DVO_CHIP_LVDS: intel_encoder->cloneable = 0; - drm_connector_init(dev, connector, + drm_connector_init(&dev_priv->drm, connector, &intel_dvo_connector_funcs, DRM_MODE_CONNECTOR_LVDS); encoder_type = DRM_MODE_ENCODER_LVDS; diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c index 3da4d466e332..371acf109e34 100644 --- a/drivers/gpu/drm/i915/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/intel_engine_cs.c @@ -111,13 +111,12 @@ intel_engine_setup(struct drm_i915_private *dev_priv, /** * intel_engines_init() - allocate, populate and init the Engine Command Streamers - * @dev: DRM device. + * @dev_priv: i915 device private * * Return: non-zero if the initialization failed. */ -int intel_engines_init(struct drm_device *dev) +int intel_engines_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_device_info *device_info = mkwrite_device_info(dev_priv); unsigned int ring_mask = INTEL_INFO(dev_priv)->ring_mask; unsigned int mask = 0; @@ -257,7 +256,7 @@ int intel_engine_create_scratch(struct intel_engine_cs *engine, int size) WARN_ON(engine->scratch); - obj = i915_gem_object_create_stolen(&engine->i915->drm, size); + obj = i915_gem_object_create_stolen(engine->i915, size); if (!obj) obj = i915_gem_object_create_internal(engine->i915, size); if (IS_ERR(obj)) { @@ -265,7 +264,7 @@ int intel_engine_create_scratch(struct intel_engine_cs *engine, int size) return PTR_ERR(obj); } - vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL); + vma = i915_vma_instance(obj, &engine->i915->ggtt.base, NULL); if (IS_ERR(vma)) { ret = PTR_ERR(vma); goto err_unref; @@ -305,15 +304,30 @@ int intel_engine_init_common(struct intel_engine_cs *engine) { int ret; - ret = intel_engine_init_breadcrumbs(engine); + /* We may need to do things with the shrinker which + * require us to immediately switch back to the default + * context. This can cause a problem as pinning the + * default context also requires GTT space which may not + * be available. To avoid this we always pin the default + * context. + */ + ret = engine->context_pin(engine, engine->i915->kernel_context); if (ret) return ret; + ret = intel_engine_init_breadcrumbs(engine); + if (ret) + goto err_unpin; + ret = i915_gem_render_state_init(engine); if (ret) - return ret; + goto err_unpin; return 0; + +err_unpin: + engine->context_unpin(engine, engine->i915->kernel_context); + return ret; } /** @@ -331,6 +345,8 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine) intel_engine_fini_breadcrumbs(engine); intel_engine_cleanup_cmd_parser(engine); i915_gem_batch_pool_fini(&engine->batch_pool); + + engine->context_unpin(engine, engine->i915->kernel_context); } u64 intel_engine_get_active_head(struct intel_engine_cs *engine) diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c index f3a1d6a5cabe..89fe5c8464df 100644 --- a/drivers/gpu/drm/i915/intel_fbc.c +++ b/drivers/gpu/drm/i915/intel_fbc.c @@ -188,7 +188,7 @@ static void g4x_fbc_activate(struct drm_i915_private *dev_priv) u32 dpfc_ctl; dpfc_ctl = DPFC_CTL_PLANE(params->crtc.plane) | DPFC_SR_EN; - if (drm_format_plane_cpp(params->fb.pixel_format, 0) == 2) + if (params->fb.format->cpp[0] == 2) dpfc_ctl |= DPFC_CTL_LIMIT_2X; else dpfc_ctl |= DPFC_CTL_LIMIT_1X; @@ -235,7 +235,7 @@ static void ilk_fbc_activate(struct drm_i915_private *dev_priv) int threshold = dev_priv->fbc.threshold; dpfc_ctl = DPFC_CTL_PLANE(params->crtc.plane); - if (drm_format_plane_cpp(params->fb.pixel_format, 0) == 2) + if (params->fb.format->cpp[0] == 2) threshold++; switch (threshold) { @@ -305,7 +305,7 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv) if (IS_IVYBRIDGE(dev_priv)) dpfc_ctl |= IVB_DPFC_CTL_PLANE(params->crtc.plane); - if (drm_format_plane_cpp(params->fb.pixel_format, 0) == 2) + if (params->fb.format->cpp[0] == 2) threshold++; switch (threshold) { @@ -541,7 +541,7 @@ static int find_compression_threshold(struct drm_i915_private *dev_priv, IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) end = ggtt->stolen_size - 8 * 1024 * 1024; else - end = ggtt->stolen_usable_size; + end = U64_MAX; /* HACK: This code depends on what we will do in *_enable_fbc. If that * code changes, this code needs to change as well. @@ -584,7 +584,7 @@ static int intel_fbc_alloc_cfb(struct intel_crtc *crtc) WARN_ON(drm_mm_node_allocated(&fbc->compressed_fb)); size = intel_fbc_calculate_cfb_size(dev_priv, &fbc->state_cache); - fb_cpp = drm_format_plane_cpp(fbc->state_cache.fb.pixel_format, 0); + fb_cpp = fbc->state_cache.fb.format->cpp[0]; ret = find_compression_threshold(dev_priv, &fbc->compressed_fb, size, fb_cpp); @@ -754,7 +754,7 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc, if (!cache->plane.visible) return; - cache->fb.pixel_format = fb->pixel_format; + cache->fb.format = fb->format; cache->fb.stride = fb->pitches[0]; cache->vma = plane_state->vma; @@ -812,7 +812,7 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc) return false; } - if (!pixel_format_is_valid(dev_priv, cache->fb.pixel_format)) { + if (!pixel_format_is_valid(dev_priv, cache->fb.format->format)) { fbc->no_fbc_reason = "pixel format is invalid"; return false; } @@ -883,7 +883,7 @@ static void intel_fbc_get_reg_params(struct intel_crtc *crtc, params->crtc.plane = crtc->plane; params->crtc.fence_y_offset = get_crtc_fence_y_offset(crtc); - params->fb.pixel_format = cache->fb.pixel_format; + params->fb.format = cache->fb.format; params->fb.stride = cache->fb.stride; params->cfb_size = intel_fbc_calculate_cfb_size(dev_priv, cache); @@ -1284,7 +1284,7 @@ void intel_fbc_init_pipe_state(struct drm_i915_private *dev_priv) for_each_intel_crtc(&dev_priv->drm, crtc) if (intel_crtc_active(crtc) && - to_intel_plane_state(crtc->base.primary->state)->base.visible) + crtc->base.primary->state->visible) dev_priv->fbc.visible_pipes_mask |= (1 << crtc->pipe); } @@ -1305,7 +1305,7 @@ static int intel_sanitize_fbc_option(struct drm_i915_private *dev_priv) if (!HAS_FBC(dev_priv)) return 0; - if (IS_BROADWELL(dev_priv)) + if (IS_BROADWELL(dev_priv) || INTEL_GEN(dev_priv) >= 9) return 1; return 0; diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index f4a8c4fc57c4..1b8ba2e77539 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -145,9 +145,9 @@ static int intelfb_alloc(struct drm_fb_helper *helper, * important and we should probably use that space with FBC or other * features. */ if (size * 2 < ggtt->stolen_usable_size) - obj = i915_gem_object_create_stolen(dev, size); + obj = i915_gem_object_create_stolen(dev_priv, size); if (obj == NULL) - obj = i915_gem_object_create(dev, size); + obj = i915_gem_object_create(dev_priv, size); if (IS_ERR(obj)) { DRM_ERROR("failed to allocate framebuffer\n"); ret = PTR_ERR(obj); @@ -261,7 +261,7 @@ static int intelfb_create(struct drm_fb_helper *helper, /* This driver doesn't need a VT switch to restore the mode on resume */ info->skip_vt_switch = true; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); /* If the object is shmemfs backed, it will have given us zeroed pages. @@ -447,7 +447,7 @@ retry: connector->name); /* go for command line mode first */ - modes[i] = drm_pick_cmdline_mode(fb_conn, width, height); + modes[i] = drm_pick_cmdline_mode(fb_conn); /* try for preferred next */ if (!modes[i]) { @@ -621,7 +621,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, * rather than the current pipe's, since they differ. */ cur_size = intel_crtc->config->base.adjusted_mode.crtc_hdisplay; - cur_size = cur_size * fb->base.bits_per_pixel / 8; + cur_size = cur_size * fb->base.format->cpp[0]; if (fb->base.pitches[0] < cur_size) { DRM_DEBUG_KMS("fb not wide enough for plane %c (%d vs %d)\n", pipe_name(intel_crtc->pipe), @@ -632,14 +632,14 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, cur_size = intel_crtc->config->base.adjusted_mode.crtc_vdisplay; cur_size = intel_fb_align_height(dev, cur_size, - fb->base.pixel_format, + fb->base.format->format, fb->base.modifier); cur_size *= fb->base.pitches[0]; DRM_DEBUG_KMS("pipe %c area: %dx%d, bpp: %d, size: %d\n", pipe_name(intel_crtc->pipe), intel_crtc->config->base.adjusted_mode.crtc_hdisplay, intel_crtc->config->base.adjusted_mode.crtc_vdisplay, - fb->base.bits_per_pixel, + fb->base.format->cpp[0] * 8, cur_size); if (cur_size > max_size) { @@ -660,7 +660,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, goto out; } - ifbdev->preferred_bpp = fb->base.bits_per_pixel; + ifbdev->preferred_bpp = fb->base.format->cpp[0] * 8; ifbdev->fb = fb; drm_framebuffer_reference(&ifbdev->fb->base); @@ -713,8 +713,7 @@ int intel_fbdev_init(struct drm_device *dev) if (!intel_fbdev_init_bios(dev, ifbdev)) ifbdev->preferred_bpp = 32; - ret = drm_fb_helper_init(dev, &ifbdev->helper, - INTEL_INFO(dev_priv)->num_pipes, 4); + ret = drm_fb_helper_init(dev, &ifbdev->helper, 4); if (ret) { kfree(ifbdev); return ret; diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h index 324ea902558b..25691f0e4c50 100644 --- a/drivers/gpu/drm/i915/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h @@ -23,15 +23,6 @@ #ifndef _INTEL_GUC_FWIF_H #define _INTEL_GUC_FWIF_H -/* - * This file is partially autogenerated, although currently with some manual - * fixups afterwards. In future, it should be entirely autogenerated, in order - * to ensure that the definitions herein remain in sync with those used by the - * GuC's own firmware. - * - * EDITING THIS FILE IS THEREFORE NOT RECOMMENDED - YOUR CHANGES MAY BE LOST. - */ - #define GFXCORE_FAMILY_GEN9 12 #define GFXCORE_FAMILY_UNKNOWN 0x7fffffff @@ -154,7 +145,7 @@ * The GuC firmware layout looks like this: * * +-------------------------------+ - * | guc_css_header | + * | uc_css_header | * | | * | contains major/minor version | * +-------------------------------+ @@ -181,9 +172,16 @@ * 3. Length info of each component can be found in header, in dwords. * 4. Modulus and exponent key are not required by driver. They may not appear * in fw. So driver will load a truncated firmware in this case. + * + * HuC firmware layout is same as GuC firmware. + * + * HuC firmware css header is different. However, the only difference is where + * the version information is saved. The uc_css_header is unified to support + * both. Driver should get HuC version from uc_css_header.huc_sw_version, while + * uc_css_header.guc_sw_version for GuC. */ -struct guc_css_header { +struct uc_css_header { uint32_t module_type; /* header_size includes all non-uCode bits, including css_header, rsa * key, modulus key and exponent data. */ @@ -214,8 +212,16 @@ struct guc_css_header { char username[8]; char buildnumber[12]; - uint32_t device_id; - uint32_t guc_sw_version; + union { + struct { + uint32_t branch_client_version; + uint32_t sw_version; + } guc; + struct { + uint32_t sw_version; + uint32_t reserved; + } huc; + }; uint32_t prod_preprod_fw; uint32_t reserved[12]; uint32_t header_info; @@ -489,18 +495,19 @@ union guc_log_control { } __packed; /* This Action will be programmed in C180 - SOFT_SCRATCH_O_REG */ -enum host2guc_action { - HOST2GUC_ACTION_DEFAULT = 0x0, - HOST2GUC_ACTION_SAMPLE_FORCEWAKE = 0x6, - HOST2GUC_ACTION_ALLOCATE_DOORBELL = 0x10, - HOST2GUC_ACTION_DEALLOCATE_DOORBELL = 0x20, - HOST2GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE = 0x30, - HOST2GUC_ACTION_FORCE_LOG_BUFFER_FLUSH = 0x302, - HOST2GUC_ACTION_ENTER_S_STATE = 0x501, - HOST2GUC_ACTION_EXIT_S_STATE = 0x502, - HOST2GUC_ACTION_SLPC_REQUEST = 0x3003, - HOST2GUC_ACTION_UK_LOG_ENABLE_LOGGING = 0x0E000, - HOST2GUC_ACTION_LIMIT +enum intel_guc_action { + INTEL_GUC_ACTION_DEFAULT = 0x0, + INTEL_GUC_ACTION_SAMPLE_FORCEWAKE = 0x6, + INTEL_GUC_ACTION_ALLOCATE_DOORBELL = 0x10, + INTEL_GUC_ACTION_DEALLOCATE_DOORBELL = 0x20, + INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE = 0x30, + INTEL_GUC_ACTION_FORCE_LOG_BUFFER_FLUSH = 0x302, + INTEL_GUC_ACTION_ENTER_S_STATE = 0x501, + INTEL_GUC_ACTION_EXIT_S_STATE = 0x502, + INTEL_GUC_ACTION_SLPC_REQUEST = 0x3003, + INTEL_GUC_ACTION_AUTHENTICATE_HUC = 0x4000, + INTEL_GUC_ACTION_UK_LOG_ENABLE_LOGGING = 0x0E000, + INTEL_GUC_ACTION_LIMIT }; /* @@ -509,22 +516,22 @@ enum host2guc_action { * by the fact that all the MASK bits are set. The remaining bits * give more detail. */ -#define GUC2HOST_RESPONSE_MASK ((u32)0xF0000000) -#define GUC2HOST_IS_RESPONSE(x) ((u32)(x) >= GUC2HOST_RESPONSE_MASK) -#define GUC2HOST_STATUS(x) (GUC2HOST_RESPONSE_MASK | (x)) +#define INTEL_GUC_RECV_MASK ((u32)0xF0000000) +#define INTEL_GUC_RECV_IS_RESPONSE(x) ((u32)(x) >= INTEL_GUC_RECV_MASK) +#define INTEL_GUC_RECV_STATUS(x) (INTEL_GUC_RECV_MASK | (x)) /* GUC will return status back to SOFT_SCRATCH_O_REG */ -enum guc2host_status { - GUC2HOST_STATUS_SUCCESS = GUC2HOST_STATUS(0x0), - GUC2HOST_STATUS_ALLOCATE_DOORBELL_FAIL = GUC2HOST_STATUS(0x10), - GUC2HOST_STATUS_DEALLOCATE_DOORBELL_FAIL = GUC2HOST_STATUS(0x20), - GUC2HOST_STATUS_GENERIC_FAIL = GUC2HOST_STATUS(0x0000F000) +enum intel_guc_status { + INTEL_GUC_STATUS_SUCCESS = INTEL_GUC_RECV_STATUS(0x0), + INTEL_GUC_STATUS_ALLOCATE_DOORBELL_FAIL = INTEL_GUC_RECV_STATUS(0x10), + INTEL_GUC_STATUS_DEALLOCATE_DOORBELL_FAIL = INTEL_GUC_RECV_STATUS(0x20), + INTEL_GUC_STATUS_GENERIC_FAIL = INTEL_GUC_RECV_STATUS(0x0000F000) }; /* This action will be programmed in C1BC - SOFT_SCRATCH_15_REG */ -enum guc2host_message { - GUC2HOST_MSG_CRASH_DUMP_POSTED = (1 << 1), - GUC2HOST_MSG_FLUSH_LOG_BUFFER = (1 << 3) +enum intel_guc_recv_message { + INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED = BIT(1), + INTEL_GUC_RECV_MSG_FLUSH_LOG_BUFFER = BIT(3) }; #endif diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 34d6ad2cf7c1..2f1cf9aea04e 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -28,7 +28,7 @@ */ #include <linux/firmware.h> #include "i915_drv.h" -#include "intel_guc.h" +#include "intel_uc.h" /** * DOC: GuC-specific firmware loader @@ -51,12 +51,6 @@ * 512K. In order to exclude 0-512K address space from GGTT, all gfx objects * used by GuC is pinned with PIN_OFFSET_BIAS along with size of WOPCM. * - * Firmware log: - * Firmware log is enabled by setting i915.guc_log_level to non-negative level. - * Log data is printed out via reading debugfs i915_guc_log_dump. Reading from - * i915_guc_load_status will print out firmware loading status and scratch - * registers value. - * */ #define SKL_FW_MAJOR 6 @@ -81,16 +75,16 @@ MODULE_FIRMWARE(I915_BXT_GUC_UCODE); MODULE_FIRMWARE(I915_KBL_GUC_UCODE); /* User-friendly representation of an enum */ -const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status) +const char *intel_uc_fw_status_repr(enum intel_uc_fw_status status) { switch (status) { - case GUC_FIRMWARE_FAIL: + case INTEL_UC_FIRMWARE_FAIL: return "FAIL"; - case GUC_FIRMWARE_NONE: + case INTEL_UC_FIRMWARE_NONE: return "NONE"; - case GUC_FIRMWARE_PENDING: + case INTEL_UC_FIRMWARE_PENDING: return "PENDING"; - case GUC_FIRMWARE_SUCCESS: + case INTEL_UC_FIRMWARE_SUCCESS: return "SUCCESS"; default: return "UNKNOWN!"; @@ -220,14 +214,14 @@ static void guc_params_init(struct drm_i915_private *dev_priv) params[GUC_CTL_DEBUG] = GUC_LOG_DISABLED; if (guc->ads_vma) { - u32 ads = i915_ggtt_offset(guc->ads_vma) >> PAGE_SHIFT; + u32 ads = guc_ggtt_offset(guc->ads_vma) >> PAGE_SHIFT; params[GUC_CTL_DEBUG] |= ads << GUC_ADS_ADDR_SHIFT; params[GUC_CTL_DEBUG] |= GUC_ADS_ENABLED; } /* If GuC submission is enabled, set up additional parameters here */ if (i915.enable_guc_submission) { - u32 pgs = i915_ggtt_offset(dev_priv->guc.ctx_pool_vma); + u32 pgs = guc_ggtt_offset(dev_priv->guc.ctx_pool_vma); u32 ctx_in_16 = GUC_MAX_GPU_CONTEXTS / 16; pgs >>= PAGE_SHIFT; @@ -278,7 +272,7 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv, struct i915_vma *vma) { - struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; unsigned long offset; struct sg_table *sg = vma->pages; u32 status, rsa[UOS_RSA_SCRATCH_MAX_COUNT]; @@ -297,7 +291,7 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv, I915_WRITE(DMA_COPY_SIZE, guc_fw->header_size + guc_fw->ucode_size); /* Set the source address for the new blob */ - offset = i915_ggtt_offset(vma) + guc_fw->header_offset; + offset = guc_ggtt_offset(vma) + guc_fw->header_offset; I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset)); I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF); @@ -334,12 +328,12 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv, return ret; } -static u32 guc_wopcm_size(struct drm_i915_private *dev_priv) +u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv) { u32 wopcm_size = GUC_WOPCM_TOP; /* On BXT, the top of WOPCM is reserved for RC6 context */ - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) wopcm_size -= BXT_GUC_WOPCM_RC6_RESERVED; return wopcm_size; @@ -350,29 +344,27 @@ static u32 guc_wopcm_size(struct drm_i915_private *dev_priv) */ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) { - struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; struct i915_vma *vma; int ret; - ret = i915_gem_object_set_to_gtt_domain(guc_fw->guc_fw_obj, false); + ret = i915_gem_object_set_to_gtt_domain(guc_fw->obj, false); if (ret) { DRM_DEBUG_DRIVER("set-domain failed %d\n", ret); return ret; } - vma = i915_gem_object_ggtt_pin(guc_fw->guc_fw_obj, NULL, 0, 0, 0); + vma = i915_gem_object_ggtt_pin(guc_fw->obj, NULL, 0, 0, + PIN_OFFSET_BIAS | GUC_WOPCM_TOP); if (IS_ERR(vma)) { DRM_DEBUG_DRIVER("pin failed %d\n", (int)PTR_ERR(vma)); return PTR_ERR(vma); } - /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */ - I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); - intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); /* init WOPCM */ - I915_WRITE(GUC_WOPCM_SIZE, guc_wopcm_size(dev_priv)); + I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv)); I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE); /* Enable MIA caching. GuC clock gating is disabled. */ @@ -388,7 +380,7 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0)) I915_WRITE(GEN6_GFXPAUSE, 0x30FFF); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) I915_WRITE(GEN9LP_GT_PM_CONFIG, GT_DOORBELL_ENABLE); else I915_WRITE(GEN9_GT_PM_CONFIG, GT_DOORBELL_ENABLE); @@ -437,7 +429,7 @@ static int guc_hw_reset(struct drm_i915_private *dev_priv) /** * intel_guc_setup() - finish preparing the GuC for activity - * @dev: drm device + * @dev_priv: i915 device private * * Called from gem_init_hw() during driver loading and also after a GPU reset. * @@ -448,17 +440,16 @@ static int guc_hw_reset(struct drm_i915_private *dev_priv) * * Return: non-zero code on error */ -int intel_guc_setup(struct drm_device *dev) +int intel_guc_setup(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; - const char *fw_path = guc_fw->guc_fw_path; + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; + const char *fw_path = guc_fw->path; int retries, ret, err; DRM_DEBUG_DRIVER("GuC fw status: path %s, fetch %s, load %s\n", fw_path, - intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), - intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + intel_uc_fw_status_repr(guc_fw->fetch_status), + intel_uc_fw_status_repr(guc_fw->load_status)); /* Loading forbidden, or no firmware to load? */ if (!i915.enable_guc_loading) { @@ -476,10 +467,10 @@ int intel_guc_setup(struct drm_device *dev) } /* Fetch failed, or already fetched but failed to load? */ - if (guc_fw->guc_fw_fetch_status != GUC_FIRMWARE_SUCCESS) { + if (guc_fw->fetch_status != INTEL_UC_FIRMWARE_SUCCESS) { err = -EIO; goto fail; - } else if (guc_fw->guc_fw_load_status == GUC_FIRMWARE_FAIL) { + } else if (guc_fw->load_status == INTEL_UC_FIRMWARE_FAIL) { err = -ENOEXEC; goto fail; } @@ -487,11 +478,14 @@ int intel_guc_setup(struct drm_device *dev) guc_interrupts_release(dev_priv); gen9_reset_guc_interrupts(dev_priv); - guc_fw->guc_fw_load_status = GUC_FIRMWARE_PENDING; + /* We need to notify the guc whenever we change the GGTT */ + i915_ggtt_enable_guc(dev_priv); + + guc_fw->load_status = INTEL_UC_FIRMWARE_PENDING; DRM_DEBUG_DRIVER("GuC fw status: fetch %s, load %s\n", - intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), - intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + intel_uc_fw_status_repr(guc_fw->fetch_status), + intel_uc_fw_status_repr(guc_fw->load_status)); err = i915_guc_submission_init(dev_priv); if (err) @@ -512,6 +506,7 @@ int intel_guc_setup(struct drm_device *dev) if (err) goto fail; + intel_huc_load(dev_priv); err = guc_ucode_xfer(dev_priv); if (!err) break; @@ -523,11 +518,13 @@ int intel_guc_setup(struct drm_device *dev) "retry %d more time(s)\n", err, retries); } - guc_fw->guc_fw_load_status = GUC_FIRMWARE_SUCCESS; + guc_fw->load_status = INTEL_UC_FIRMWARE_SUCCESS; DRM_DEBUG_DRIVER("GuC fw status: fetch %s, load %s\n", - intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), - intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); + intel_uc_fw_status_repr(guc_fw->fetch_status), + intel_uc_fw_status_repr(guc_fw->load_status)); + + intel_guc_auth_huc(dev_priv); if (i915.enable_guc_submission) { if (i915.guc_log_level >= 0) @@ -542,12 +539,13 @@ int intel_guc_setup(struct drm_device *dev) return 0; fail: - if (guc_fw->guc_fw_load_status == GUC_FIRMWARE_PENDING) - guc_fw->guc_fw_load_status = GUC_FIRMWARE_FAIL; + if (guc_fw->load_status == INTEL_UC_FIRMWARE_PENDING) + guc_fw->load_status = INTEL_UC_FIRMWARE_FAIL; guc_interrupts_release(dev_priv); i915_guc_submission_disable(dev_priv); i915_guc_submission_fini(dev_priv); + i915_ggtt_disable_guc(dev_priv); /* * We've failed to load the firmware :( @@ -588,141 +586,156 @@ fail: return ret; } -static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) +void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, + struct intel_uc_fw *uc_fw) { - struct pci_dev *pdev = dev->pdev; + struct pci_dev *pdev = dev_priv->drm.pdev; struct drm_i915_gem_object *obj; - const struct firmware *fw; - struct guc_css_header *css; + const struct firmware *fw = NULL; + struct uc_css_header *css; size_t size; int err; - DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n", - intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status)); + DRM_DEBUG_DRIVER("before requesting firmware: uC fw fetch status %s\n", + intel_uc_fw_status_repr(uc_fw->fetch_status)); - err = request_firmware(&fw, guc_fw->guc_fw_path, &pdev->dev); + err = request_firmware(&fw, uc_fw->path, &pdev->dev); if (err) goto fail; if (!fw) goto fail; - DRM_DEBUG_DRIVER("fetch GuC fw from %s succeeded, fw %p\n", - guc_fw->guc_fw_path, fw); + DRM_DEBUG_DRIVER("fetch uC fw from %s succeeded, fw %p\n", + uc_fw->path, fw); /* Check the size of the blob before examining buffer contents */ - if (fw->size < sizeof(struct guc_css_header)) { + if (fw->size < sizeof(struct uc_css_header)) { DRM_NOTE("Firmware header is missing\n"); goto fail; } - css = (struct guc_css_header *)fw->data; + css = (struct uc_css_header *)fw->data; /* Firmware bits always start from header */ - guc_fw->header_offset = 0; - guc_fw->header_size = (css->header_size_dw - css->modulus_size_dw - + uc_fw->header_offset = 0; + uc_fw->header_size = (css->header_size_dw - css->modulus_size_dw - css->key_size_dw - css->exponent_size_dw) * sizeof(u32); - if (guc_fw->header_size != sizeof(struct guc_css_header)) { + if (uc_fw->header_size != sizeof(struct uc_css_header)) { DRM_NOTE("CSS header definition mismatch\n"); goto fail; } /* then, uCode */ - guc_fw->ucode_offset = guc_fw->header_offset + guc_fw->header_size; - guc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32); + uc_fw->ucode_offset = uc_fw->header_offset + uc_fw->header_size; + uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32); /* now RSA */ if (css->key_size_dw != UOS_RSA_SCRATCH_MAX_COUNT) { DRM_NOTE("RSA key size is bad\n"); goto fail; } - guc_fw->rsa_offset = guc_fw->ucode_offset + guc_fw->ucode_size; - guc_fw->rsa_size = css->key_size_dw * sizeof(u32); + uc_fw->rsa_offset = uc_fw->ucode_offset + uc_fw->ucode_size; + uc_fw->rsa_size = css->key_size_dw * sizeof(u32); /* At least, it should have header, uCode and RSA. Size of all three. */ - size = guc_fw->header_size + guc_fw->ucode_size + guc_fw->rsa_size; + size = uc_fw->header_size + uc_fw->ucode_size + uc_fw->rsa_size; if (fw->size < size) { DRM_NOTE("Missing firmware components\n"); goto fail; } - /* Header and uCode will be loaded to WOPCM. Size of the two. */ - size = guc_fw->header_size + guc_fw->ucode_size; - if (size > guc_wopcm_size(to_i915(dev))) { - DRM_NOTE("Firmware is too large to fit in WOPCM\n"); - goto fail; - } - /* * The GuC firmware image has the version number embedded at a well-known * offset within the firmware blob; note that major / minor version are * TWO bytes each (i.e. u16), although all pointers and offsets are defined * in terms of bytes (u8). */ - guc_fw->guc_fw_major_found = css->guc_sw_version >> 16; - guc_fw->guc_fw_minor_found = css->guc_sw_version & 0xFFFF; - - if (guc_fw->guc_fw_major_found != guc_fw->guc_fw_major_wanted || - guc_fw->guc_fw_minor_found < guc_fw->guc_fw_minor_wanted) { - DRM_NOTE("GuC firmware version %d.%d, required %d.%d\n", - guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found, - guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted); + switch (uc_fw->fw) { + case INTEL_UC_FW_TYPE_GUC: + /* Header and uCode will be loaded to WOPCM. Size of the two. */ + size = uc_fw->header_size + uc_fw->ucode_size; + + /* Top 32k of WOPCM is reserved (8K stack + 24k RC6 context). */ + if (size > intel_guc_wopcm_size(dev_priv)) { + DRM_ERROR("Firmware is too large to fit in WOPCM\n"); + goto fail; + } + uc_fw->major_ver_found = css->guc.sw_version >> 16; + uc_fw->minor_ver_found = css->guc.sw_version & 0xFFFF; + break; + + case INTEL_UC_FW_TYPE_HUC: + uc_fw->major_ver_found = css->huc.sw_version >> 16; + uc_fw->minor_ver_found = css->huc.sw_version & 0xFFFF; + break; + + default: + DRM_ERROR("Unknown firmware type %d\n", uc_fw->fw); + err = -ENOEXEC; + goto fail; + } + + if (uc_fw->major_ver_found != uc_fw->major_ver_wanted || + uc_fw->minor_ver_found < uc_fw->minor_ver_wanted) { + DRM_NOTE("uC firmware version %d.%d, required %d.%d\n", + uc_fw->major_ver_found, uc_fw->minor_ver_found, + uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted); err = -ENOEXEC; goto fail; } DRM_DEBUG_DRIVER("firmware version %d.%d OK (minimum %d.%d)\n", - guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found, - guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted); + uc_fw->major_ver_found, uc_fw->minor_ver_found, + uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted); - mutex_lock(&dev->struct_mutex); - obj = i915_gem_object_create_from_data(dev, fw->data, fw->size); - mutex_unlock(&dev->struct_mutex); + mutex_lock(&dev_priv->drm.struct_mutex); + obj = i915_gem_object_create_from_data(dev_priv, fw->data, fw->size); + mutex_unlock(&dev_priv->drm.struct_mutex); if (IS_ERR_OR_NULL(obj)) { err = obj ? PTR_ERR(obj) : -ENOMEM; goto fail; } - guc_fw->guc_fw_obj = obj; - guc_fw->guc_fw_size = fw->size; + uc_fw->obj = obj; + uc_fw->size = fw->size; - DRM_DEBUG_DRIVER("GuC fw fetch status SUCCESS, obj %p\n", - guc_fw->guc_fw_obj); + DRM_DEBUG_DRIVER("uC fw fetch status SUCCESS, obj %p\n", + uc_fw->obj); release_firmware(fw); - guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_SUCCESS; + uc_fw->fetch_status = INTEL_UC_FIRMWARE_SUCCESS; return; fail: - DRM_WARN("Failed to fetch valid GuC firmware from %s (error %d)\n", - guc_fw->guc_fw_path, err); - DRM_DEBUG_DRIVER("GuC fw fetch status FAIL; err %d, fw %p, obj %p\n", - err, fw, guc_fw->guc_fw_obj); + DRM_WARN("Failed to fetch valid uC firmware from %s (error %d)\n", + uc_fw->path, err); + DRM_DEBUG_DRIVER("uC fw fetch status FAIL; err %d, fw %p, obj %p\n", + err, fw, uc_fw->obj); - mutex_lock(&dev->struct_mutex); - obj = guc_fw->guc_fw_obj; + mutex_lock(&dev_priv->drm.struct_mutex); + obj = uc_fw->obj; if (obj) i915_gem_object_put(obj); - guc_fw->guc_fw_obj = NULL; - mutex_unlock(&dev->struct_mutex); + uc_fw->obj = NULL; + mutex_unlock(&dev_priv->drm.struct_mutex); release_firmware(fw); /* OK even if fw is NULL */ - guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_FAIL; + uc_fw->fetch_status = INTEL_UC_FIRMWARE_FAIL; } /** * intel_guc_init() - define parameters and fetch firmware - * @dev: drm device + * @dev_priv: i915 device private * * Called early during driver load, but after GEM is initialised. * * The firmware will be transferred to the GuC's memory later, * when intel_guc_setup() is called. */ -void intel_guc_init(struct drm_device *dev) +void intel_guc_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; const char *fw_path; if (!HAS_GUC(dev_priv)) { @@ -740,24 +753,23 @@ void intel_guc_init(struct drm_device *dev) fw_path = NULL; } else if (IS_SKYLAKE(dev_priv)) { fw_path = I915_SKL_GUC_UCODE; - guc_fw->guc_fw_major_wanted = SKL_FW_MAJOR; - guc_fw->guc_fw_minor_wanted = SKL_FW_MINOR; + guc_fw->major_ver_wanted = SKL_FW_MAJOR; + guc_fw->minor_ver_wanted = SKL_FW_MINOR; } else if (IS_BROXTON(dev_priv)) { fw_path = I915_BXT_GUC_UCODE; - guc_fw->guc_fw_major_wanted = BXT_FW_MAJOR; - guc_fw->guc_fw_minor_wanted = BXT_FW_MINOR; + guc_fw->major_ver_wanted = BXT_FW_MAJOR; + guc_fw->minor_ver_wanted = BXT_FW_MINOR; } else if (IS_KABYLAKE(dev_priv)) { fw_path = I915_KBL_GUC_UCODE; - guc_fw->guc_fw_major_wanted = KBL_FW_MAJOR; - guc_fw->guc_fw_minor_wanted = KBL_FW_MINOR; + guc_fw->major_ver_wanted = KBL_FW_MAJOR; + guc_fw->minor_ver_wanted = KBL_FW_MINOR; } else { fw_path = ""; /* unknown device */ } - guc_fw->guc_dev = dev; - guc_fw->guc_fw_path = fw_path; - guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE; - guc_fw->guc_fw_load_status = GUC_FIRMWARE_NONE; + guc_fw->path = fw_path; + guc_fw->fetch_status = INTEL_UC_FIRMWARE_NONE; + guc_fw->load_status = INTEL_UC_FIRMWARE_NONE; /* Early (and silent) return if GuC loading is disabled */ if (!i915.enable_guc_loading) @@ -767,30 +779,29 @@ void intel_guc_init(struct drm_device *dev) if (*fw_path == '\0') return; - guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_PENDING; + guc_fw->fetch_status = INTEL_UC_FIRMWARE_PENDING; DRM_DEBUG_DRIVER("GuC firmware pending, path %s\n", fw_path); - guc_fw_fetch(dev, guc_fw); + intel_uc_fw_fetch(dev_priv, guc_fw); /* status must now be FAIL or SUCCESS */ } /** * intel_guc_fini() - clean up all allocated resources - * @dev: drm device + * @dev_priv: i915 device private */ -void intel_guc_fini(struct drm_device *dev) +void intel_guc_fini(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; + struct intel_uc_fw *guc_fw = &dev_priv->guc.fw; - mutex_lock(&dev->struct_mutex); + mutex_lock(&dev_priv->drm.struct_mutex); guc_interrupts_release(dev_priv); i915_guc_submission_disable(dev_priv); i915_guc_submission_fini(dev_priv); - if (guc_fw->guc_fw_obj) - i915_gem_object_put(guc_fw->guc_fw_obj); - guc_fw->guc_fw_obj = NULL; - mutex_unlock(&dev->struct_mutex); + if (guc_fw->obj) + i915_gem_object_put(guc_fw->obj); + guc_fw->obj = NULL; + mutex_unlock(&dev_priv->drm.struct_mutex); - guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE; + guc_fw->fetch_status = INTEL_UC_FIRMWARE_NONE; } diff --git a/drivers/gpu/drm/i915/intel_guc_log.c b/drivers/gpu/drm/i915/intel_guc_log.c new file mode 100644 index 000000000000..5c0f9a49da0e --- /dev/null +++ b/drivers/gpu/drm/i915/intel_guc_log.c @@ -0,0 +1,658 @@ +/* + * Copyright © 2014-2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ +#include <linux/debugfs.h> +#include <linux/relay.h> +#include "i915_drv.h" + +static void guc_log_capture_logs(struct intel_guc *guc); + +/** + * DOC: GuC firmware log + * + * Firmware log is enabled by setting i915.guc_log_level to non-negative level. + * Log data is printed out via reading debugfs i915_guc_log_dump. Reading from + * i915_guc_load_status will print out firmware loading status and scratch + * registers value. + * + */ + +static int guc_log_flush_complete(struct intel_guc *guc) +{ + u32 action[] = { + INTEL_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE + }; + + return intel_guc_send(guc, action, ARRAY_SIZE(action)); +} + +static int guc_log_flush(struct intel_guc *guc) +{ + u32 action[] = { + INTEL_GUC_ACTION_FORCE_LOG_BUFFER_FLUSH, + 0 + }; + + return intel_guc_send(guc, action, ARRAY_SIZE(action)); +} + +static int guc_log_control(struct intel_guc *guc, u32 control_val) +{ + u32 action[] = { + INTEL_GUC_ACTION_UK_LOG_ENABLE_LOGGING, + control_val + }; + + return intel_guc_send(guc, action, ARRAY_SIZE(action)); +} + + +/* + * Sub buffer switch callback. Called whenever relay has to switch to a new + * sub buffer, relay stays on the same sub buffer if 0 is returned. + */ +static int subbuf_start_callback(struct rchan_buf *buf, + void *subbuf, + void *prev_subbuf, + size_t prev_padding) +{ + /* Use no-overwrite mode by default, where relay will stop accepting + * new data if there are no empty sub buffers left. + * There is no strict synchronization enforced by relay between Consumer + * and Producer. In overwrite mode, there is a possibility of getting + * inconsistent/garbled data, the producer could be writing on to the + * same sub buffer from which Consumer is reading. This can't be avoided + * unless Consumer is fast enough and can always run in tandem with + * Producer. + */ + if (relay_buf_full(buf)) + return 0; + + return 1; +} + +/* + * file_create() callback. Creates relay file in debugfs. + */ +static struct dentry *create_buf_file_callback(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *buf_file; + + /* This to enable the use of a single buffer for the relay channel and + * correspondingly have a single file exposed to User, through which + * it can collect the logs in order without any post-processing. + * Need to set 'is_global' even if parent is NULL for early logging. + */ + *is_global = 1; + + if (!parent) + return NULL; + + /* Not using the channel filename passed as an argument, since for each + * channel relay appends the corresponding CPU number to the filename + * passed in relay_open(). This should be fine as relay just needs a + * dentry of the file associated with the channel buffer and that file's + * name need not be same as the filename passed as an argument. + */ + buf_file = debugfs_create_file("guc_log", mode, + parent, buf, &relay_file_operations); + return buf_file; +} + +/* + * file_remove() default callback. Removes relay file in debugfs. + */ +static int remove_buf_file_callback(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +/* relay channel callbacks */ +static struct rchan_callbacks relay_callbacks = { + .subbuf_start = subbuf_start_callback, + .create_buf_file = create_buf_file_callback, + .remove_buf_file = remove_buf_file_callback, +}; + +static void guc_log_remove_relay_file(struct intel_guc *guc) +{ + relay_close(guc->log.relay_chan); +} + +static int guc_log_create_relay_channel(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct rchan *guc_log_relay_chan; + size_t n_subbufs, subbuf_size; + + /* Keep the size of sub buffers same as shared log buffer */ + subbuf_size = guc->log.vma->obj->base.size; + + /* Store up to 8 snapshots, which is large enough to buffer sufficient + * boot time logs and provides enough leeway to User, in terms of + * latency, for consuming the logs from relay. Also doesn't take + * up too much memory. + */ + n_subbufs = 8; + + guc_log_relay_chan = relay_open(NULL, NULL, subbuf_size, + n_subbufs, &relay_callbacks, dev_priv); + if (!guc_log_relay_chan) { + DRM_ERROR("Couldn't create relay chan for GuC logging\n"); + return -ENOMEM; + } + + GEM_BUG_ON(guc_log_relay_chan->subbuf_size < subbuf_size); + guc->log.relay_chan = guc_log_relay_chan; + return 0; +} + +static int guc_log_create_relay_file(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct dentry *log_dir; + int ret; + + /* For now create the log file in /sys/kernel/debug/dri/0 dir */ + log_dir = dev_priv->drm.primary->debugfs_root; + + /* If /sys/kernel/debug/dri/0 location do not exist, then debugfs is + * not mounted and so can't create the relay file. + * The relay API seems to fit well with debugfs only, for availing relay + * there are 3 requirements which can be met for debugfs file only in a + * straightforward/clean manner :- + * i) Need the associated dentry pointer of the file, while opening the + * relay channel. + * ii) Should be able to use 'relay_file_operations' fops for the file. + * iii) Set the 'i_private' field of file's inode to the pointer of + * relay channel buffer. + */ + if (!log_dir) { + DRM_ERROR("Debugfs dir not available yet for GuC log file\n"); + return -ENODEV; + } + + ret = relay_late_setup_files(guc->log.relay_chan, "guc_log", log_dir); + if (ret) { + DRM_ERROR("Couldn't associate relay chan with file %d\n", ret); + return ret; + } + + return 0; +} + +static void guc_move_to_next_buf(struct intel_guc *guc) +{ + /* Make sure the updates made in the sub buffer are visible when + * Consumer sees the following update to offset inside the sub buffer. + */ + smp_wmb(); + + /* All data has been written, so now move the offset of sub buffer. */ + relay_reserve(guc->log.relay_chan, guc->log.vma->obj->base.size); + + /* Switch to the next sub buffer */ + relay_flush(guc->log.relay_chan); +} + +static void *guc_get_write_buffer(struct intel_guc *guc) +{ + if (!guc->log.relay_chan) + return NULL; + + /* Just get the base address of a new sub buffer and copy data into it + * ourselves. NULL will be returned in no-overwrite mode, if all sub + * buffers are full. Could have used the relay_write() to indirectly + * copy the data, but that would have been bit convoluted, as we need to + * write to only certain locations inside a sub buffer which cannot be + * done without using relay_reserve() along with relay_write(). So its + * better to use relay_reserve() alone. + */ + return relay_reserve(guc->log.relay_chan, 0); +} + +static bool guc_check_log_buf_overflow(struct intel_guc *guc, + enum guc_log_buffer_type type, + unsigned int full_cnt) +{ + unsigned int prev_full_cnt = guc->log.prev_overflow_count[type]; + bool overflow = false; + + if (full_cnt != prev_full_cnt) { + overflow = true; + + guc->log.prev_overflow_count[type] = full_cnt; + guc->log.total_overflow_count[type] += full_cnt - prev_full_cnt; + + if (full_cnt < prev_full_cnt) { + /* buffer_full_cnt is a 4 bit counter */ + guc->log.total_overflow_count[type] += 16; + } + DRM_ERROR_RATELIMITED("GuC log buffer overflow\n"); + } + + return overflow; +} + +static unsigned int guc_get_log_buffer_size(enum guc_log_buffer_type type) +{ + switch (type) { + case GUC_ISR_LOG_BUFFER: + return (GUC_LOG_ISR_PAGES + 1) * PAGE_SIZE; + case GUC_DPC_LOG_BUFFER: + return (GUC_LOG_DPC_PAGES + 1) * PAGE_SIZE; + case GUC_CRASH_DUMP_LOG_BUFFER: + return (GUC_LOG_CRASH_PAGES + 1) * PAGE_SIZE; + default: + MISSING_CASE(type); + } + + return 0; +} + +static void guc_read_update_log_buffer(struct intel_guc *guc) +{ + unsigned int buffer_size, read_offset, write_offset, bytes_to_copy, full_cnt; + struct guc_log_buffer_state *log_buf_state, *log_buf_snapshot_state; + struct guc_log_buffer_state log_buf_state_local; + enum guc_log_buffer_type type; + void *src_data, *dst_data; + bool new_overflow; + + if (WARN_ON(!guc->log.buf_addr)) + return; + + /* Get the pointer to shared GuC log buffer */ + log_buf_state = src_data = guc->log.buf_addr; + + /* Get the pointer to local buffer to store the logs */ + log_buf_snapshot_state = dst_data = guc_get_write_buffer(guc); + + /* Actual logs are present from the 2nd page */ + src_data += PAGE_SIZE; + dst_data += PAGE_SIZE; + + for (type = GUC_ISR_LOG_BUFFER; type < GUC_MAX_LOG_BUFFER; type++) { + /* Make a copy of the state structure, inside GuC log buffer + * (which is uncached mapped), on the stack to avoid reading + * from it multiple times. + */ + memcpy(&log_buf_state_local, log_buf_state, + sizeof(struct guc_log_buffer_state)); + buffer_size = guc_get_log_buffer_size(type); + read_offset = log_buf_state_local.read_ptr; + write_offset = log_buf_state_local.sampled_write_ptr; + full_cnt = log_buf_state_local.buffer_full_cnt; + + /* Bookkeeping stuff */ + guc->log.flush_count[type] += log_buf_state_local.flush_to_file; + new_overflow = guc_check_log_buf_overflow(guc, type, full_cnt); + + /* Update the state of shared log buffer */ + log_buf_state->read_ptr = write_offset; + log_buf_state->flush_to_file = 0; + log_buf_state++; + + if (unlikely(!log_buf_snapshot_state)) + continue; + + /* First copy the state structure in snapshot buffer */ + memcpy(log_buf_snapshot_state, &log_buf_state_local, + sizeof(struct guc_log_buffer_state)); + + /* The write pointer could have been updated by GuC firmware, + * after sending the flush interrupt to Host, for consistency + * set write pointer value to same value of sampled_write_ptr + * in the snapshot buffer. + */ + log_buf_snapshot_state->write_ptr = write_offset; + log_buf_snapshot_state++; + + /* Now copy the actual logs. */ + if (unlikely(new_overflow)) { + /* copy the whole buffer in case of overflow */ + read_offset = 0; + write_offset = buffer_size; + } else if (unlikely((read_offset > buffer_size) || + (write_offset > buffer_size))) { + DRM_ERROR("invalid log buffer state\n"); + /* copy whole buffer as offsets are unreliable */ + read_offset = 0; + write_offset = buffer_size; + } + + /* Just copy the newly written data */ + if (read_offset > write_offset) { + i915_memcpy_from_wc(dst_data, src_data, write_offset); + bytes_to_copy = buffer_size - read_offset; + } else { + bytes_to_copy = write_offset - read_offset; + } + i915_memcpy_from_wc(dst_data + read_offset, + src_data + read_offset, bytes_to_copy); + + src_data += buffer_size; + dst_data += buffer_size; + } + + if (log_buf_snapshot_state) + guc_move_to_next_buf(guc); + else { + /* Used rate limited to avoid deluge of messages, logs might be + * getting consumed by User at a slow rate. + */ + DRM_ERROR_RATELIMITED("no sub-buffer to capture logs\n"); + guc->log.capture_miss_count++; + } +} + +static void guc_log_cleanup(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + /* First disable the flush interrupt */ + gen9_disable_guc_interrupts(dev_priv); + + if (guc->log.flush_wq) + destroy_workqueue(guc->log.flush_wq); + + guc->log.flush_wq = NULL; + + if (guc->log.relay_chan) + guc_log_remove_relay_file(guc); + + guc->log.relay_chan = NULL; + + if (guc->log.buf_addr) + i915_gem_object_unpin_map(guc->log.vma->obj); + + guc->log.buf_addr = NULL; +} + +static void capture_logs_work(struct work_struct *work) +{ + struct intel_guc *guc = + container_of(work, struct intel_guc, log.flush_work); + + guc_log_capture_logs(guc); +} + +static int guc_log_create_extras(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + void *vaddr; + int ret; + + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + /* Nothing to do */ + if (i915.guc_log_level < 0) + return 0; + + if (!guc->log.buf_addr) { + /* Create a WC (Uncached for read) vmalloc mapping of log + * buffer pages, so that we can directly get the data + * (up-to-date) from memory. + */ + vaddr = i915_gem_object_pin_map(guc->log.vma->obj, I915_MAP_WC); + if (IS_ERR(vaddr)) { + ret = PTR_ERR(vaddr); + DRM_ERROR("Couldn't map log buffer pages %d\n", ret); + return ret; + } + + guc->log.buf_addr = vaddr; + } + + if (!guc->log.relay_chan) { + /* Create a relay channel, so that we have buffers for storing + * the GuC firmware logs, the channel will be linked with a file + * later on when debugfs is registered. + */ + ret = guc_log_create_relay_channel(guc); + if (ret) + return ret; + } + + if (!guc->log.flush_wq) { + INIT_WORK(&guc->log.flush_work, capture_logs_work); + + /* + * GuC log buffer flush work item has to do register access to + * send the ack to GuC and this work item, if not synced before + * suspend, can potentially get executed after the GFX device is + * suspended. + * By marking the WQ as freezable, we don't have to bother about + * flushing of this work item from the suspend hooks, the pending + * work item if any will be either executed before the suspend + * or scheduled later on resume. This way the handling of work + * item can be kept same between system suspend & rpm suspend. + */ + guc->log.flush_wq = alloc_ordered_workqueue("i915-guc_log", + WQ_HIGHPRI | WQ_FREEZABLE); + if (guc->log.flush_wq == NULL) { + DRM_ERROR("Couldn't allocate the wq for GuC logging\n"); + return -ENOMEM; + } + } + + return 0; +} + +void intel_guc_log_create(struct intel_guc *guc) +{ + struct i915_vma *vma; + unsigned long offset; + uint32_t size, flags; + + if (i915.guc_log_level > GUC_LOG_VERBOSITY_MAX) + i915.guc_log_level = GUC_LOG_VERBOSITY_MAX; + + /* The first page is to save log buffer state. Allocate one + * extra page for others in case for overlap */ + size = (1 + GUC_LOG_DPC_PAGES + 1 + + GUC_LOG_ISR_PAGES + 1 + + GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT; + + vma = guc->log.vma; + if (!vma) { + /* We require SSE 4.1 for fast reads from the GuC log buffer and + * it should be present on the chipsets supporting GuC based + * submisssions. + */ + if (WARN_ON(!i915_has_memcpy_from_wc())) { + /* logging will not be enabled */ + i915.guc_log_level = -1; + return; + } + + vma = intel_guc_allocate_vma(guc, size); + if (IS_ERR(vma)) { + /* logging will be off */ + i915.guc_log_level = -1; + return; + } + + guc->log.vma = vma; + + if (guc_log_create_extras(guc)) { + guc_log_cleanup(guc); + i915_vma_unpin_and_release(&guc->log.vma); + i915.guc_log_level = -1; + return; + } + } + + /* each allocated unit is a page */ + flags = GUC_LOG_VALID | GUC_LOG_NOTIFY_ON_HALF_FULL | + (GUC_LOG_DPC_PAGES << GUC_LOG_DPC_SHIFT) | + (GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) | + (GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT); + + offset = guc_ggtt_offset(vma) >> PAGE_SHIFT; /* in pages */ + guc->log.flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags; +} + +static int guc_log_late_setup(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + int ret; + + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + if (i915.guc_log_level < 0) + return -EINVAL; + + /* If log_level was set as -1 at boot time, then setup needed to + * handle log buffer flush interrupts would not have been done yet, + * so do that now. + */ + ret = guc_log_create_extras(guc); + if (ret) + goto err; + + ret = guc_log_create_relay_file(guc); + if (ret) + goto err; + + return 0; +err: + guc_log_cleanup(guc); + /* logging will remain off */ + i915.guc_log_level = -1; + return ret; +} + +static void guc_log_capture_logs(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + guc_read_update_log_buffer(guc); + + /* Generally device is expected to be active only at this + * time, so get/put should be really quick. + */ + intel_runtime_pm_get(dev_priv); + guc_log_flush_complete(guc); + intel_runtime_pm_put(dev_priv); +} + +static void guc_flush_logs(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + if (!i915.enable_guc_submission || (i915.guc_log_level < 0)) + return; + + /* First disable the interrupts, will be renabled afterwards */ + gen9_disable_guc_interrupts(dev_priv); + + /* Before initiating the forceful flush, wait for any pending/ongoing + * flush to complete otherwise forceful flush may not actually happen. + */ + flush_work(&guc->log.flush_work); + + /* Ask GuC to update the log buffer state */ + guc_log_flush(guc); + + /* GuC would have updated log buffer by now, so capture it */ + guc_log_capture_logs(guc); +} + +int i915_guc_log_control(struct drm_i915_private *dev_priv, u64 control_val) +{ + struct intel_guc *guc = &dev_priv->guc; + + union guc_log_control log_param; + int ret; + + log_param.value = control_val; + + if (log_param.verbosity < GUC_LOG_VERBOSITY_MIN || + log_param.verbosity > GUC_LOG_VERBOSITY_MAX) + return -EINVAL; + + /* This combination doesn't make sense & won't have any effect */ + if (!log_param.logging_enabled && (i915.guc_log_level < 0)) + return 0; + + ret = guc_log_control(guc, log_param.value); + if (ret < 0) { + DRM_DEBUG_DRIVER("guc_logging_control action failed %d\n", ret); + return ret; + } + + i915.guc_log_level = log_param.verbosity; + + /* If log_level was set as -1 at boot time, then the relay channel file + * wouldn't have been created by now and interrupts also would not have + * been enabled. + */ + if (!dev_priv->guc.log.relay_chan) { + ret = guc_log_late_setup(guc); + if (!ret) + gen9_enable_guc_interrupts(dev_priv); + } else if (!log_param.logging_enabled) { + /* Once logging is disabled, GuC won't generate logs & send an + * interrupt. But there could be some data in the log buffer + * which is yet to be captured. So request GuC to update the log + * buffer state and then collect the left over logs. + */ + guc_flush_logs(guc); + + /* As logging is disabled, update log level to reflect that */ + i915.guc_log_level = -1; + } else { + /* In case interrupts were disabled, enable them now */ + gen9_enable_guc_interrupts(dev_priv); + } + + return ret; +} + +void i915_guc_log_register(struct drm_i915_private *dev_priv) +{ + if (!i915.enable_guc_submission) + return; + + mutex_lock(&dev_priv->drm.struct_mutex); + guc_log_late_setup(&dev_priv->guc); + mutex_unlock(&dev_priv->drm.struct_mutex); +} + +void i915_guc_log_unregister(struct drm_i915_private *dev_priv) +{ + if (!i915.enable_guc_submission) + return; + + mutex_lock(&dev_priv->drm.struct_mutex); + guc_log_cleanup(&dev_priv->guc); + mutex_unlock(&dev_priv->drm.struct_mutex); +} diff --git a/drivers/gpu/drm/i915/intel_gvt.c b/drivers/gpu/drm/i915/intel_gvt.c index 290384e86c63..d23c0fcff751 100644 --- a/drivers/gpu/drm/i915/intel_gvt.c +++ b/drivers/gpu/drm/i915/intel_gvt.c @@ -67,6 +67,11 @@ int intel_gvt_init(struct drm_i915_private *dev_priv) return 0; } + if (intel_vgpu_active(dev_priv)) { + DRM_DEBUG_DRIVER("GVT-g is disabled for guest\n"); + goto bail; + } + if (!is_supported_device(dev_priv)) { DRM_DEBUG_DRIVER("Unsupported device. GVT-g is disabled\n"); goto bail; diff --git a/drivers/gpu/drm/i915/intel_hangcheck.c b/drivers/gpu/drm/i915/intel_hangcheck.c index 53df5b11bff4..f05971f5586f 100644 --- a/drivers/gpu/drm/i915/intel_hangcheck.c +++ b/drivers/gpu/drm/i915/intel_hangcheck.c @@ -236,13 +236,13 @@ head_stuck(struct intel_engine_cs *engine, u64 acthd) memset(&engine->hangcheck.instdone, 0, sizeof(engine->hangcheck.instdone)); - return HANGCHECK_ACTIVE; + return ENGINE_ACTIVE_HEAD; } if (!subunits_stuck(engine)) - return HANGCHECK_ACTIVE; + return ENGINE_ACTIVE_SUBUNITS; - return HANGCHECK_HUNG; + return ENGINE_DEAD; } static enum intel_engine_hangcheck_action @@ -253,11 +253,11 @@ engine_stuck(struct intel_engine_cs *engine, u64 acthd) u32 tmp; ha = head_stuck(engine, acthd); - if (ha != HANGCHECK_HUNG) + if (ha != ENGINE_DEAD) return ha; if (IS_GEN2(dev_priv)) - return HANGCHECK_HUNG; + return ENGINE_DEAD; /* Is the chip hanging on a WAIT_FOR_EVENT? * If so we can simply poke the RB_WAIT bit @@ -270,25 +270,144 @@ engine_stuck(struct intel_engine_cs *engine, u64 acthd) "Kicking stuck wait on %s", engine->name); I915_WRITE_CTL(engine, tmp); - return HANGCHECK_KICK; + return ENGINE_WAIT_KICK; } if (INTEL_GEN(dev_priv) >= 6 && tmp & RING_WAIT_SEMAPHORE) { switch (semaphore_passed(engine)) { default: - return HANGCHECK_HUNG; + return ENGINE_DEAD; case 1: i915_handle_error(dev_priv, 0, "Kicking stuck semaphore on %s", engine->name); I915_WRITE_CTL(engine, tmp); - return HANGCHECK_KICK; + return ENGINE_WAIT_KICK; case 0: - return HANGCHECK_WAIT; + return ENGINE_WAIT; } } - return HANGCHECK_HUNG; + return ENGINE_DEAD; +} + +static void hangcheck_load_sample(struct intel_engine_cs *engine, + struct intel_engine_hangcheck *hc) +{ + /* We don't strictly need an irq-barrier here, as we are not + * serving an interrupt request, be paranoid in case the + * barrier has side-effects (such as preventing a broken + * cacheline snoop) and so be sure that we can see the seqno + * advance. If the seqno should stick, due to a stale + * cacheline, we would erroneously declare the GPU hung. + */ + if (engine->irq_seqno_barrier) + engine->irq_seqno_barrier(engine); + + hc->acthd = intel_engine_get_active_head(engine); + hc->seqno = intel_engine_get_seqno(engine); +} + +static void hangcheck_store_sample(struct intel_engine_cs *engine, + const struct intel_engine_hangcheck *hc) +{ + engine->hangcheck.acthd = hc->acthd; + engine->hangcheck.seqno = hc->seqno; + engine->hangcheck.action = hc->action; + engine->hangcheck.stalled = hc->stalled; +} + +static enum intel_engine_hangcheck_action +hangcheck_get_action(struct intel_engine_cs *engine, + const struct intel_engine_hangcheck *hc) +{ + if (engine->hangcheck.seqno != hc->seqno) + return ENGINE_ACTIVE_SEQNO; + + if (i915_seqno_passed(hc->seqno, intel_engine_last_submit(engine))) + return ENGINE_IDLE; + + return engine_stuck(engine, hc->acthd); +} + +static void hangcheck_accumulate_sample(struct intel_engine_cs *engine, + struct intel_engine_hangcheck *hc) +{ + unsigned long timeout = I915_ENGINE_DEAD_TIMEOUT; + + hc->action = hangcheck_get_action(engine, hc); + + /* We always increment the progress + * if the engine is busy and still processing + * the same request, so that no single request + * can run indefinitely (such as a chain of + * batches). The only time we do not increment + * the hangcheck score on this ring, if this + * engine is in a legitimate wait for another + * engine. In that case the waiting engine is a + * victim and we want to be sure we catch the + * right culprit. Then every time we do kick + * the ring, make it as a progress as the seqno + * advancement might ensure and if not, it + * will catch the hanging engine. + */ + + switch (hc->action) { + case ENGINE_IDLE: + case ENGINE_ACTIVE_SEQNO: + /* Clear head and subunit states on seqno movement */ + hc->acthd = 0; + + memset(&engine->hangcheck.instdone, 0, + sizeof(engine->hangcheck.instdone)); + + /* Intentional fall through */ + case ENGINE_WAIT_KICK: + case ENGINE_WAIT: + engine->hangcheck.action_timestamp = jiffies; + break; + + case ENGINE_ACTIVE_HEAD: + case ENGINE_ACTIVE_SUBUNITS: + /* Seqno stuck with still active engine gets leeway, + * in hopes that it is just a long shader. + */ + timeout = I915_SEQNO_DEAD_TIMEOUT; + break; + + case ENGINE_DEAD: + break; + + default: + MISSING_CASE(hc->action); + } + + hc->stalled = time_after(jiffies, + engine->hangcheck.action_timestamp + timeout); +} + +static void hangcheck_declare_hang(struct drm_i915_private *i915, + unsigned int hung, + unsigned int stuck) +{ + struct intel_engine_cs *engine; + char msg[80]; + unsigned int tmp; + int len; + + /* If some rings hung but others were still busy, only + * blame the hanging rings in the synopsis. + */ + if (stuck != hung) + hung &= ~stuck; + len = scnprintf(msg, sizeof(msg), + "%s on ", stuck == hung ? "No progress" : "Hang"); + for_each_engine_masked(engine, i915, hung, tmp) + len += scnprintf(msg + len, sizeof(msg) - len, + "%s, ", engine->name); + msg[len-2] = '\0'; + + return i915_handle_error(i915, hung, msg); } /* @@ -308,10 +427,6 @@ static void i915_hangcheck_elapsed(struct work_struct *work) enum intel_engine_id id; unsigned int hung = 0, stuck = 0; int busy_count = 0; -#define BUSY 1 -#define KICK 5 -#define HUNG 20 -#define ACTIVE_DECAY 15 if (!i915.enable_hangcheck) return; @@ -319,6 +434,9 @@ static void i915_hangcheck_elapsed(struct work_struct *work) if (!READ_ONCE(dev_priv->gt.awake)) return; + if (i915_terminally_wedged(&dev_priv->gpu_error)) + return; + /* As enabling the GPU requires fairly extensive mmio access, * periodically arm the mmio checker to see if we are triggering * any invalid access. @@ -326,112 +444,26 @@ static void i915_hangcheck_elapsed(struct work_struct *work) intel_uncore_arm_unclaimed_mmio_detection(dev_priv); for_each_engine(engine, dev_priv, id) { - bool busy = intel_engine_has_waiter(engine); - u64 acthd; - u32 seqno; - u32 submit; + struct intel_engine_hangcheck cur_state, *hc = &cur_state; + const bool busy = intel_engine_has_waiter(engine); semaphore_clear_deadlocks(dev_priv); - /* We don't strictly need an irq-barrier here, as we are not - * serving an interrupt request, be paranoid in case the - * barrier has side-effects (such as preventing a broken - * cacheline snoop) and so be sure that we can see the seqno - * advance. If the seqno should stick, due to a stale - * cacheline, we would erroneously declare the GPU hung. - */ - if (engine->irq_seqno_barrier) - engine->irq_seqno_barrier(engine); - - acthd = intel_engine_get_active_head(engine); - seqno = intel_engine_get_seqno(engine); - submit = intel_engine_last_submit(engine); - - if (engine->hangcheck.seqno == seqno) { - if (i915_seqno_passed(seqno, submit)) { - engine->hangcheck.action = HANGCHECK_IDLE; - } else { - /* We always increment the hangcheck score - * if the engine is busy and still processing - * the same request, so that no single request - * can run indefinitely (such as a chain of - * batches). The only time we do not increment - * the hangcheck score on this ring, if this - * engine is in a legitimate wait for another - * engine. In that case the waiting engine is a - * victim and we want to be sure we catch the - * right culprit. Then every time we do kick - * the ring, add a small increment to the - * score so that we can catch a batch that is - * being repeatedly kicked and so responsible - * for stalling the machine. - */ - engine->hangcheck.action = - engine_stuck(engine, acthd); - - switch (engine->hangcheck.action) { - case HANGCHECK_IDLE: - case HANGCHECK_WAIT: - break; - case HANGCHECK_ACTIVE: - engine->hangcheck.score += BUSY; - break; - case HANGCHECK_KICK: - engine->hangcheck.score += KICK; - break; - case HANGCHECK_HUNG: - engine->hangcheck.score += HUNG; - break; - } - } - - if (engine->hangcheck.score >= HANGCHECK_SCORE_RING_HUNG) { - hung |= intel_engine_flag(engine); - if (engine->hangcheck.action != HANGCHECK_HUNG) - stuck |= intel_engine_flag(engine); - } - } else { - engine->hangcheck.action = HANGCHECK_ACTIVE; - - /* Gradually reduce the count so that we catch DoS - * attempts across multiple batches. - */ - if (engine->hangcheck.score > 0) - engine->hangcheck.score -= ACTIVE_DECAY; - if (engine->hangcheck.score < 0) - engine->hangcheck.score = 0; - - /* Clear head and subunit states on seqno movement */ - acthd = 0; - - memset(&engine->hangcheck.instdone, 0, - sizeof(engine->hangcheck.instdone)); + hangcheck_load_sample(engine, hc); + hangcheck_accumulate_sample(engine, hc); + hangcheck_store_sample(engine, hc); + + if (engine->hangcheck.stalled) { + hung |= intel_engine_flag(engine); + if (hc->action != ENGINE_DEAD) + stuck |= intel_engine_flag(engine); } - engine->hangcheck.seqno = seqno; - engine->hangcheck.acthd = acthd; busy_count += busy; } - if (hung) { - char msg[80]; - unsigned int tmp; - int len; - - /* If some rings hung but others were still busy, only - * blame the hanging rings in the synopsis. - */ - if (stuck != hung) - hung &= ~stuck; - len = scnprintf(msg, sizeof(msg), - "%s on ", stuck == hung ? "No progress" : "Hang"); - for_each_engine_masked(engine, dev_priv, hung, tmp) - len += scnprintf(msg + len, sizeof(msg) - len, - "%s, ", engine->name); - msg[len-2] = '\0'; - - return i915_handle_error(dev_priv, hung, msg); - } + if (hung) + hangcheck_declare_hang(dev_priv, hung, stuck); /* Reset timer in case GPU hangs without another request being added */ if (busy_count) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index fb88e32e25a3..ebae2bd83918 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -36,6 +36,7 @@ #include <drm/drm_edid.h> #include "intel_drv.h" #include <drm/i915_drm.h> +#include <drm/intel_lpe_audio.h> #include "i915_drv.h" static struct drm_device *intel_hdmi_to_dev(struct intel_hdmi *intel_hdmi) @@ -133,6 +134,7 @@ hsw_dip_data_reg(struct drm_i915_private *dev_priv, } static void g4x_write_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, enum hdmi_infoframe_type type, const void *frame, ssize_t len) { @@ -187,13 +189,14 @@ static bool g4x_infoframe_enabled(struct drm_encoder *encoder, } static void ibx_write_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, enum hdmi_infoframe_type type, const void *frame, ssize_t len) { const uint32_t *data = frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); i915_reg_t reg = TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); int i; @@ -246,13 +249,14 @@ static bool ibx_infoframe_enabled(struct drm_encoder *encoder, } static void cpt_write_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, enum hdmi_infoframe_type type, const void *frame, ssize_t len) { const uint32_t *data = frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); i915_reg_t reg = TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); int i; @@ -303,13 +307,14 @@ static bool cpt_infoframe_enabled(struct drm_encoder *encoder, } static void vlv_write_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, enum hdmi_infoframe_type type, const void *frame, ssize_t len) { const uint32_t *data = frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); i915_reg_t reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); int i; @@ -361,14 +366,14 @@ static bool vlv_infoframe_enabled(struct drm_encoder *encoder, } static void hsw_write_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, enum hdmi_infoframe_type type, const void *frame, ssize_t len) { const uint32_t *data = frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); - enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder; + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; i915_reg_t ctl_reg = HSW_TVIDEO_DIP_CTL(cpu_transcoder); i915_reg_t data_reg; int i; @@ -425,6 +430,7 @@ static bool hsw_infoframe_enabled(struct drm_encoder *encoder, * bytes by one. */ static void intel_write_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, union hdmi_infoframe *frame) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); @@ -443,14 +449,15 @@ static void intel_write_infoframe(struct drm_encoder *encoder, buffer[3] = 0; len++; - intel_hdmi->write_infoframe(encoder, frame->any.type, buffer, len); + intel_hdmi->write_infoframe(encoder, crtc_state, frame->any.type, buffer, len); } static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, - const struct drm_display_mode *adjusted_mode) + const struct intel_crtc_state *crtc_state) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + const struct drm_display_mode *adjusted_mode = + &crtc_state->base.adjusted_mode; union hdmi_infoframe frame; int ret; @@ -461,19 +468,17 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, return; } - if (intel_hdmi->rgb_quant_range_selectable) { - if (intel_crtc->config->limited_color_range) - frame.avi.quantization_range = - HDMI_QUANTIZATION_RANGE_LIMITED; - else - frame.avi.quantization_range = - HDMI_QUANTIZATION_RANGE_FULL; - } + drm_hdmi_avi_infoframe_quant_range(&frame.avi, adjusted_mode, + crtc_state->limited_color_range ? + HDMI_QUANTIZATION_RANGE_LIMITED : + HDMI_QUANTIZATION_RANGE_FULL, + intel_hdmi->rgb_quant_range_selectable); - intel_write_infoframe(encoder, &frame); + intel_write_infoframe(encoder, crtc_state, &frame); } -static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) +static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state) { union hdmi_infoframe frame; int ret; @@ -486,27 +491,28 @@ static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) frame.spd.sdi = HDMI_SPD_SDI_PC; - intel_write_infoframe(encoder, &frame); + intel_write_infoframe(encoder, crtc_state, &frame); } static void intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder, - const struct drm_display_mode *adjusted_mode) + const struct intel_crtc_state *crtc_state) { union hdmi_infoframe frame; int ret; ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi, - adjusted_mode); + &crtc_state->base.adjusted_mode); if (ret < 0) return; - intel_write_infoframe(encoder, &frame); + intel_write_infoframe(encoder, crtc_state, &frame); } static void g4x_set_infoframes(struct drm_encoder *encoder, bool enable, - const struct drm_display_mode *adjusted_mode) + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); @@ -560,28 +566,22 @@ static void g4x_set_infoframes(struct drm_encoder *encoder, I915_WRITE(reg, val); POSTING_READ(reg); - intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); - intel_hdmi_set_spd_infoframe(encoder); - intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_avi_infoframe(encoder, crtc_state); + intel_hdmi_set_spd_infoframe(encoder, crtc_state); + intel_hdmi_set_hdmi_infoframe(encoder, crtc_state); } -static bool hdmi_sink_is_deep_color(struct drm_encoder *encoder) +static bool hdmi_sink_is_deep_color(const struct drm_connector_state *conn_state) { - struct drm_device *dev = encoder->dev; - struct drm_connector *connector; - - WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + struct drm_connector *connector = conn_state->connector; /* * HDMI cloning is only supported on g4x which doesn't * support deep color or GCP infoframes anyway so no * need to worry about multiple HDMI sinks here. */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - if (connector->encoder == encoder) - return connector->display_info.bpc > 8; - return false; + return connector->display_info.bpc > 8; } /* @@ -627,15 +627,17 @@ static bool gcp_default_phase_possible(int pipe_bpp, mode->crtc_htotal/2 % pixels_per_group == 0); } -static bool intel_hdmi_set_gcp_infoframe(struct drm_encoder *encoder) +static bool intel_hdmi_set_gcp_infoframe(struct drm_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); - struct intel_crtc *crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); i915_reg_t reg; u32 val = 0; if (HAS_DDI(dev_priv)) - reg = HSW_TVIDEO_DIP_GCP(crtc->config->cpu_transcoder); + reg = HSW_TVIDEO_DIP_GCP(crtc_state->cpu_transcoder); else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) reg = VLV_TVIDEO_DIP_GCP(crtc->pipe); else if (HAS_PCH_SPLIT(dev_priv)) @@ -644,12 +646,12 @@ static bool intel_hdmi_set_gcp_infoframe(struct drm_encoder *encoder) return false; /* Indicate color depth whenever the sink supports deep color */ - if (hdmi_sink_is_deep_color(encoder)) + if (hdmi_sink_is_deep_color(conn_state)) val |= GCP_COLOR_INDICATION; /* Enable default_phase whenever the display mode is suitably aligned */ - if (gcp_default_phase_possible(crtc->config->pipe_bpp, - &crtc->config->base.adjusted_mode)) + if (gcp_default_phase_possible(crtc_state->pipe_bpp, + &crtc_state->base.adjusted_mode)) val |= GCP_DEFAULT_PHASE_ENABLE; I915_WRITE(reg, val); @@ -659,10 +661,11 @@ static bool intel_hdmi_set_gcp_infoframe(struct drm_encoder *encoder) static void ibx_set_infoframes(struct drm_encoder *encoder, bool enable, - const struct drm_display_mode *adjusted_mode) + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); struct intel_hdmi *intel_hdmi = &intel_dig_port->hdmi; i915_reg_t reg = TVIDEO_DIP_CTL(intel_crtc->pipe); @@ -698,23 +701,24 @@ static void ibx_set_infoframes(struct drm_encoder *encoder, VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); - if (intel_hdmi_set_gcp_infoframe(encoder)) + if (intel_hdmi_set_gcp_infoframe(encoder, crtc_state, conn_state)) val |= VIDEO_DIP_ENABLE_GCP; I915_WRITE(reg, val); POSTING_READ(reg); - intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); - intel_hdmi_set_spd_infoframe(encoder); - intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_avi_infoframe(encoder, crtc_state); + intel_hdmi_set_spd_infoframe(encoder, crtc_state); + intel_hdmi_set_hdmi_infoframe(encoder, crtc_state); } static void cpt_set_infoframes(struct drm_encoder *encoder, bool enable, - const struct drm_display_mode *adjusted_mode) + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); i915_reg_t reg = TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); @@ -740,24 +744,25 @@ static void cpt_set_infoframes(struct drm_encoder *encoder, val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); - if (intel_hdmi_set_gcp_infoframe(encoder)) + if (intel_hdmi_set_gcp_infoframe(encoder, crtc_state, conn_state)) val |= VIDEO_DIP_ENABLE_GCP; I915_WRITE(reg, val); POSTING_READ(reg); - intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); - intel_hdmi_set_spd_infoframe(encoder); - intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_avi_infoframe(encoder, crtc_state); + intel_hdmi_set_spd_infoframe(encoder, crtc_state); + intel_hdmi_set_hdmi_infoframe(encoder, crtc_state); } static void vlv_set_infoframes(struct drm_encoder *encoder, bool enable, - const struct drm_display_mode *adjusted_mode) + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); i915_reg_t reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); u32 val = I915_READ(reg); @@ -792,25 +797,25 @@ static void vlv_set_infoframes(struct drm_encoder *encoder, VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_ENABLE_GCP); - if (intel_hdmi_set_gcp_infoframe(encoder)) + if (intel_hdmi_set_gcp_infoframe(encoder, crtc_state, conn_state)) val |= VIDEO_DIP_ENABLE_GCP; I915_WRITE(reg, val); POSTING_READ(reg); - intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); - intel_hdmi_set_spd_infoframe(encoder); - intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_avi_infoframe(encoder, crtc_state); + intel_hdmi_set_spd_infoframe(encoder, crtc_state); + intel_hdmi_set_hdmi_infoframe(encoder, crtc_state); } static void hsw_set_infoframes(struct drm_encoder *encoder, bool enable, - const struct drm_display_mode *adjusted_mode) + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->dev); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); - i915_reg_t reg = HSW_TVIDEO_DIP_CTL(intel_crtc->config->cpu_transcoder); + i915_reg_t reg = HSW_TVIDEO_DIP_CTL(crtc_state->cpu_transcoder); u32 val = I915_READ(reg); assert_hdmi_port_disabled(intel_hdmi); @@ -825,15 +830,15 @@ static void hsw_set_infoframes(struct drm_encoder *encoder, return; } - if (intel_hdmi_set_gcp_infoframe(encoder)) + if (intel_hdmi_set_gcp_infoframe(encoder, crtc_state, conn_state)) val |= VIDEO_DIP_ENABLE_GCP_HSW; I915_WRITE(reg, val); POSTING_READ(reg); - intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); - intel_hdmi_set_spd_infoframe(encoder); - intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_avi_infoframe(encoder, crtc_state); + intel_hdmi_set_spd_infoframe(encoder, crtc_state); + intel_hdmi_set_hdmi_infoframe(encoder, crtc_state); } void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable) @@ -852,31 +857,32 @@ void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable) adapter, enable); } -static void intel_hdmi_prepare(struct intel_encoder *encoder) +static void intel_hdmi_prepare(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); - const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; + const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode; u32 hdmi_val; intel_dp_dual_mode_set_tmds_output(intel_hdmi, true); hdmi_val = SDVO_ENCODING_HDMI; - if (!HAS_PCH_SPLIT(dev_priv) && crtc->config->limited_color_range) + if (!HAS_PCH_SPLIT(dev_priv) && crtc_state->limited_color_range) hdmi_val |= HDMI_COLOR_RANGE_16_235; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) hdmi_val |= SDVO_HSYNC_ACTIVE_HIGH; - if (crtc->config->pipe_bpp > 24) + if (crtc_state->pipe_bpp > 24) hdmi_val |= HDMI_COLOR_FORMAT_12bpc; else hdmi_val |= SDVO_COLOR_FORMAT_8bpc; - if (crtc->config->has_hdmi_sink) + if (crtc_state->has_hdmi_sink) hdmi_val |= HDMI_MODE_SELECT_HDMI; if (HAS_PCH_CPT(dev_priv)) @@ -979,9 +985,9 @@ static void intel_enable_hdmi_audio(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc); - WARN_ON(!crtc->config->has_hdmi_sink); + WARN_ON(!pipe_config->has_hdmi_sink); DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n", pipe_name(crtc->pipe)); intel_audio_codec_enable(encoder, pipe_config, conn_state); @@ -1015,14 +1021,13 @@ static void ibx_enable_hdmi(struct intel_encoder *encoder, { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); u32 temp; temp = I915_READ(intel_hdmi->hdmi_reg); temp |= SDVO_ENABLE; - if (crtc->config->has_audio) + if (pipe_config->has_audio) temp |= SDVO_AUDIO_ENABLE; /* @@ -1066,7 +1071,7 @@ static void cpt_enable_hdmi(struct intel_encoder *encoder, { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); enum pipe pipe = crtc->pipe; u32 temp; @@ -1128,7 +1133,7 @@ static void intel_disable_hdmi(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); - struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc); u32 temp; temp = I915_READ(intel_hdmi->hdmi_reg); @@ -1170,7 +1175,7 @@ static void intel_disable_hdmi(struct intel_encoder *encoder, intel_set_pch_fifo_underrun_reporting(dev_priv, PIPE_A, true); } - intel_hdmi->set_infoframes(&encoder->base, false, NULL); + intel_hdmi->set_infoframes(&encoder->base, false, old_crtc_state, old_conn_state); intel_dp_dual_mode_set_tmds_output(intel_hdmi, false); } @@ -1246,7 +1251,7 @@ hdmi_port_clock_valid(struct intel_hdmi *hdmi, return MODE_CLOCK_HIGH; /* BXT DPLL can't generate 223-240 MHz */ - if (IS_BROXTON(dev_priv) && clock > 223333 && clock < 240000) + if (IS_GEN9_LP(dev_priv) && clock > 223333 && clock < 240000) return MODE_CLOCK_RANGE; /* CHV DPLL can't generate 216-240 MHz */ @@ -1325,7 +1330,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, /* See CEA-861-E - 5.1 Default Encoding Parameters */ pipe_config->limited_color_range = pipe_config->has_hdmi_sink && - drm_match_cea_mode(adjusted_mode) > 1; + drm_default_rgb_quant_range(adjusted_mode) == + HDMI_QUANTIZATION_RANGE_LIMITED; } else { pipe_config->limited_color_range = intel_hdmi->limited_color_range; @@ -1642,13 +1648,12 @@ static void intel_hdmi_pre_enable(struct intel_encoder *encoder, struct drm_connector_state *conn_state) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); - const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; - intel_hdmi_prepare(encoder); + intel_hdmi_prepare(encoder, pipe_config); intel_hdmi->set_infoframes(&encoder->base, pipe_config->has_hdmi_sink, - adjusted_mode); + pipe_config, conn_state); } static void vlv_hdmi_pre_enable(struct intel_encoder *encoder, @@ -1659,7 +1664,6 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder, struct intel_hdmi *intel_hdmi = &dport->hdmi; struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; vlv_phy_pre_encoder_enable(encoder); @@ -1669,7 +1673,7 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder, intel_hdmi->set_infoframes(&encoder->base, pipe_config->has_hdmi_sink, - adjusted_mode); + pipe_config, conn_state); g4x_enable_hdmi(encoder, pipe_config, conn_state); @@ -1680,7 +1684,7 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { - intel_hdmi_prepare(encoder); + intel_hdmi_prepare(encoder, pipe_config); vlv_phy_pre_pll_enable(encoder); } @@ -1689,7 +1693,7 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { - intel_hdmi_prepare(encoder); + intel_hdmi_prepare(encoder, pipe_config); chv_phy_pre_pll_enable(encoder); } @@ -1732,9 +1736,6 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder, struct intel_hdmi *intel_hdmi = &dport->hdmi; struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = - to_intel_crtc(encoder->base.crtc); - const struct drm_display_mode *adjusted_mode = &intel_crtc->config->base.adjusted_mode; chv_phy_pre_encoder_enable(encoder); @@ -1743,8 +1744,8 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder, chv_set_phy_signal_level(encoder, 128, 102, false); intel_hdmi->set_infoframes(&encoder->base, - intel_crtc->config->has_hdmi_sink, - adjusted_mode); + pipe_config->has_hdmi_sink, + pipe_config, conn_state); g4x_enable_hdmi(encoder, pipe_config, conn_state); @@ -1809,13 +1810,13 @@ static u8 intel_hdmi_ddc_pin(struct drm_i915_private *dev_priv, switch (port) { case PORT_B: - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) ddc_pin = GMBUS_PIN_1_BXT; else ddc_pin = GMBUS_PIN_DPB; break; case PORT_C: - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) ddc_pin = GMBUS_PIN_2_BXT; else ddc_pin = GMBUS_PIN_DPC; @@ -1933,10 +1934,9 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, } } -void intel_hdmi_init(struct drm_device *dev, +void intel_hdmi_init(struct drm_i915_private *dev_priv, i915_reg_t hdmi_reg, enum port port) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_digital_port *intel_dig_port; struct intel_encoder *intel_encoder; struct intel_connector *intel_connector; @@ -1953,8 +1953,9 @@ void intel_hdmi_init(struct drm_device *dev, intel_encoder = &intel_dig_port->base; - drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs, - DRM_MODE_ENCODER_TMDS, "HDMI %c", port_name(port)); + drm_encoder_init(&dev_priv->drm, &intel_encoder->base, + &intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS, + "HDMI %c", port_name(port)); intel_encoder->compute_config = intel_hdmi_compute_config; if (HAS_PCH_SPLIT(dev_priv)) { diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c index 3d546c019de0..b62e3f8ad415 100644 --- a/drivers/gpu/drm/i915/intel_hotplug.c +++ b/drivers/gpu/drm/i915/intel_hotplug.c @@ -180,7 +180,7 @@ static void intel_hpd_irq_storm_disable(struct drm_i915_private *dev_priv) /* Enable polling and queue hotplug re-enabling. */ if (hpd_disabled) { - drm_kms_helper_poll_enable_locked(dev); + drm_kms_helper_poll_enable(dev); mod_delayed_work(system_wq, &dev_priv->hotplug.reenable_work, msecs_to_jiffies(HPD_STORM_REENABLE_DELAY)); } @@ -511,7 +511,7 @@ static void i915_hpd_poll_init_work(struct work_struct *work) } if (enabled) - drm_kms_helper_poll_enable_locked(dev); + drm_kms_helper_poll_enable(dev); mutex_unlock(&dev->mode_config.mutex); diff --git a/drivers/gpu/drm/i915/intel_huc.c b/drivers/gpu/drm/i915/intel_huc.c new file mode 100644 index 000000000000..c144609425f6 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_huc.c @@ -0,0 +1,338 @@ +/* + * Copyright © 2016-2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ +#include <linux/firmware.h> +#include "i915_drv.h" +#include "intel_uc.h" + +/** + * DOC: HuC Firmware + * + * Motivation: + * GEN9 introduces a new dedicated firmware for usage in media HEVC (High + * Efficiency Video Coding) operations. Userspace can use the firmware + * capabilities by adding HuC specific commands to batch buffers. + * + * Implementation: + * The same firmware loader is used as the GuC. However, the actual + * loading to HW is deferred until GEM initialization is done. + * + * Note that HuC firmware loading must be done before GuC loading. + */ + +#define BXT_HUC_FW_MAJOR 01 +#define BXT_HUC_FW_MINOR 07 +#define BXT_BLD_NUM 1398 + +#define SKL_HUC_FW_MAJOR 01 +#define SKL_HUC_FW_MINOR 07 +#define SKL_BLD_NUM 1398 + +#define KBL_HUC_FW_MAJOR 02 +#define KBL_HUC_FW_MINOR 00 +#define KBL_BLD_NUM 1810 + +#define HUC_FW_PATH(platform, major, minor, bld_num) \ + "i915/" __stringify(platform) "_huc_ver" __stringify(major) "_" \ + __stringify(minor) "_" __stringify(bld_num) ".bin" + +#define I915_SKL_HUC_UCODE HUC_FW_PATH(skl, SKL_HUC_FW_MAJOR, \ + SKL_HUC_FW_MINOR, SKL_BLD_NUM) +MODULE_FIRMWARE(I915_SKL_HUC_UCODE); + +#define I915_BXT_HUC_UCODE HUC_FW_PATH(bxt, BXT_HUC_FW_MAJOR, \ + BXT_HUC_FW_MINOR, BXT_BLD_NUM) +MODULE_FIRMWARE(I915_BXT_HUC_UCODE); + +#define I915_KBL_HUC_UCODE HUC_FW_PATH(kbl, KBL_HUC_FW_MAJOR, \ + KBL_HUC_FW_MINOR, KBL_BLD_NUM) +MODULE_FIRMWARE(I915_KBL_HUC_UCODE); + +/** + * huc_ucode_xfer() - DMA's the firmware + * @dev_priv: the drm_i915_private device + * + * Transfer the firmware image to RAM for execution by the microcontroller. + * + * Return: 0 on success, non-zero on failure + */ +static int huc_ucode_xfer(struct drm_i915_private *dev_priv) +{ + struct intel_uc_fw *huc_fw = &dev_priv->huc.fw; + struct i915_vma *vma; + unsigned long offset = 0; + u32 size; + int ret; + + ret = i915_gem_object_set_to_gtt_domain(huc_fw->obj, false); + if (ret) { + DRM_DEBUG_DRIVER("set-domain failed %d\n", ret); + return ret; + } + + vma = i915_gem_object_ggtt_pin(huc_fw->obj, NULL, 0, 0, + PIN_OFFSET_BIAS | GUC_WOPCM_TOP); + if (IS_ERR(vma)) { + DRM_DEBUG_DRIVER("pin failed %d\n", (int)PTR_ERR(vma)); + return PTR_ERR(vma); + } + + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + + /* init WOPCM */ + I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv)); + I915_WRITE(DMA_GUC_WOPCM_OFFSET, GUC_WOPCM_OFFSET_VALUE | + HUC_LOADING_AGENT_GUC); + + /* Set the source address for the uCode */ + offset = guc_ggtt_offset(vma) + huc_fw->header_offset; + I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset)); + I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF); + + /* Hardware doesn't look at destination address for HuC. Set it to 0, + * but still program the correct address space. + */ + I915_WRITE(DMA_ADDR_1_LOW, 0); + I915_WRITE(DMA_ADDR_1_HIGH, DMA_ADDRESS_SPACE_WOPCM); + + size = huc_fw->header_size + huc_fw->ucode_size; + I915_WRITE(DMA_COPY_SIZE, size); + + /* Start the DMA */ + I915_WRITE(DMA_CTRL, _MASKED_BIT_ENABLE(HUC_UKERNEL | START_DMA)); + + /* Wait for DMA to finish */ + ret = wait_for((I915_READ(DMA_CTRL) & START_DMA) == 0, 100); + + DRM_DEBUG_DRIVER("HuC DMA transfer wait over with ret %d\n", ret); + + /* Disable the bits once DMA is over */ + I915_WRITE(DMA_CTRL, _MASKED_BIT_DISABLE(HUC_UKERNEL)); + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + + /* + * We keep the object pages for reuse during resume. But we can unpin it + * now that DMA has completed, so it doesn't continue to take up space. + */ + i915_vma_unpin(vma); + + return ret; +} + +/** + * intel_huc_init() - initiate HuC firmware loading request + * @dev_priv: the drm_i915_private device + * + * Called early during driver load, but after GEM is initialised. The loading + * will continue only when driver explicitly specify firmware name and version. + * All other cases are considered as INTEL_UC_FIRMWARE_NONE either because HW + * is not capable or driver yet support it. And there will be no error message + * for INTEL_UC_FIRMWARE_NONE cases. + * + * The DMA-copying to HW is done later when intel_huc_load() is called. + */ +void intel_huc_init(struct drm_i915_private *dev_priv) +{ + struct intel_huc *huc = &dev_priv->huc; + struct intel_uc_fw *huc_fw = &huc->fw; + const char *fw_path = NULL; + + huc_fw->path = NULL; + huc_fw->fetch_status = INTEL_UC_FIRMWARE_NONE; + huc_fw->load_status = INTEL_UC_FIRMWARE_NONE; + huc_fw->fw = INTEL_UC_FW_TYPE_HUC; + + if (!HAS_HUC_UCODE(dev_priv)) + return; + + if (IS_SKYLAKE(dev_priv)) { + fw_path = I915_SKL_HUC_UCODE; + huc_fw->major_ver_wanted = SKL_HUC_FW_MAJOR; + huc_fw->minor_ver_wanted = SKL_HUC_FW_MINOR; + } else if (IS_BROXTON(dev_priv)) { + fw_path = I915_BXT_HUC_UCODE; + huc_fw->major_ver_wanted = BXT_HUC_FW_MAJOR; + huc_fw->minor_ver_wanted = BXT_HUC_FW_MINOR; + } else if (IS_KABYLAKE(dev_priv)) { + fw_path = I915_KBL_HUC_UCODE; + huc_fw->major_ver_wanted = KBL_HUC_FW_MAJOR; + huc_fw->minor_ver_wanted = KBL_HUC_FW_MINOR; + } + + huc_fw->path = fw_path; + huc_fw->fetch_status = INTEL_UC_FIRMWARE_PENDING; + + DRM_DEBUG_DRIVER("HuC firmware pending, path %s\n", fw_path); + + WARN(huc_fw->path == NULL, "HuC present but no fw path\n"); + + intel_uc_fw_fetch(dev_priv, huc_fw); +} + +/** + * intel_huc_load() - load HuC uCode to device + * @dev_priv: the drm_i915_private device + * + * Called from guc_setup() during driver loading and also after a GPU reset. + * Be note that HuC loading must be done before GuC loading. + * + * The firmware image should have already been fetched into memory by the + * earlier call to intel_huc_init(), so here we need only check that + * is succeeded, and then transfer the image to the h/w. + * + * Return: non-zero code on error + */ +int intel_huc_load(struct drm_i915_private *dev_priv) +{ + struct intel_uc_fw *huc_fw = &dev_priv->huc.fw; + int err; + + if (huc_fw->fetch_status == INTEL_UC_FIRMWARE_NONE) + return 0; + + DRM_DEBUG_DRIVER("%s fw status: fetch %s, load %s\n", + huc_fw->path, + intel_uc_fw_status_repr(huc_fw->fetch_status), + intel_uc_fw_status_repr(huc_fw->load_status)); + + if (huc_fw->fetch_status == INTEL_UC_FIRMWARE_SUCCESS && + huc_fw->load_status == INTEL_UC_FIRMWARE_FAIL) + return -ENOEXEC; + + huc_fw->load_status = INTEL_UC_FIRMWARE_PENDING; + + switch (huc_fw->fetch_status) { + case INTEL_UC_FIRMWARE_FAIL: + /* something went wrong :( */ + err = -EIO; + goto fail; + + case INTEL_UC_FIRMWARE_NONE: + case INTEL_UC_FIRMWARE_PENDING: + default: + /* "can't happen" */ + WARN_ONCE(1, "HuC fw %s invalid fetch_status %s [%d]\n", + huc_fw->path, + intel_uc_fw_status_repr(huc_fw->fetch_status), + huc_fw->fetch_status); + err = -ENXIO; + goto fail; + + case INTEL_UC_FIRMWARE_SUCCESS: + break; + } + + err = huc_ucode_xfer(dev_priv); + if (err) + goto fail; + + huc_fw->load_status = INTEL_UC_FIRMWARE_SUCCESS; + + DRM_DEBUG_DRIVER("%s fw status: fetch %s, load %s\n", + huc_fw->path, + intel_uc_fw_status_repr(huc_fw->fetch_status), + intel_uc_fw_status_repr(huc_fw->load_status)); + + return 0; + +fail: + if (huc_fw->load_status == INTEL_UC_FIRMWARE_PENDING) + huc_fw->load_status = INTEL_UC_FIRMWARE_FAIL; + + DRM_ERROR("Failed to complete HuC uCode load with ret %d\n", err); + + return err; +} + +/** + * intel_huc_fini() - clean up resources allocated for HuC + * @dev_priv: the drm_i915_private device + * + * Cleans up by releasing the huc firmware GEM obj. + */ +void intel_huc_fini(struct drm_i915_private *dev_priv) +{ + struct intel_uc_fw *huc_fw = &dev_priv->huc.fw; + + mutex_lock(&dev_priv->drm.struct_mutex); + if (huc_fw->obj) + i915_gem_object_put(huc_fw->obj); + huc_fw->obj = NULL; + mutex_unlock(&dev_priv->drm.struct_mutex); + + huc_fw->fetch_status = INTEL_UC_FIRMWARE_NONE; +} + +/** + * intel_guc_auth_huc() - authenticate ucode + * @dev_priv: the drm_i915_device + * + * Triggers a HuC fw authentication request to the GuC via intel_guc_action_ + * authenticate_huc interface. + */ +void intel_guc_auth_huc(struct drm_i915_private *dev_priv) +{ + struct intel_guc *guc = &dev_priv->guc; + struct intel_huc *huc = &dev_priv->huc; + struct i915_vma *vma; + int ret; + u32 data[2]; + + if (huc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS) + return; + + vma = i915_gem_object_ggtt_pin(huc->fw.obj, NULL, 0, 0, + PIN_OFFSET_BIAS | GUC_WOPCM_TOP); + if (IS_ERR(vma)) { + DRM_ERROR("failed to pin huc fw object %d\n", + (int)PTR_ERR(vma)); + return; + } + + /* Specify auth action and where public signature is. */ + data[0] = INTEL_GUC_ACTION_AUTHENTICATE_HUC; + data[1] = guc_ggtt_offset(vma) + huc->fw.rsa_offset; + + ret = intel_guc_send(guc, data, ARRAY_SIZE(data)); + if (ret) { + DRM_ERROR("HuC: GuC did not ack Auth request %d\n", ret); + goto out; + } + + /* Check authentication status, it should be done by now */ + ret = intel_wait_for_register(dev_priv, + HUC_STATUS2, + HUC_FW_VERIFIED, + HUC_FW_VERIFIED, + 50); + + if (ret) { + DRM_ERROR("HuC: Authentication failed %d\n", ret); + goto out; + } + +out: + i915_vma_unpin(vma); +} + diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 83f260bb4eef..bce1ba80f277 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -72,7 +72,7 @@ static const struct gmbus_pin gmbus_pins_bxt[] = { static const struct gmbus_pin *get_gmbus_pin(struct drm_i915_private *dev_priv, unsigned int pin) { - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) return &gmbus_pins_bxt[pin]; else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) return &gmbus_pins_skl[pin]; @@ -87,7 +87,7 @@ bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv, { unsigned int size; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) size = ARRAY_SIZE(gmbus_pins_bxt); else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) size = ARRAY_SIZE(gmbus_pins_skl); @@ -111,10 +111,8 @@ to_intel_gmbus(struct i2c_adapter *i2c) } void -intel_i2c_reset(struct drm_device *dev) +intel_i2c_reset(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - I915_WRITE(GMBUS0, 0); I915_WRITE(GMBUS4, 0); } @@ -141,7 +139,7 @@ static u32 get_reserved(struct intel_gmbus *bus) u32 reserved = 0; /* On most chips, these bits must be preserved in software. */ - if (!IS_I830(dev_priv) && !IS_845G(dev_priv)) + if (!IS_I830(dev_priv) && !IS_I845G(dev_priv)) reserved = I915_READ_NOTRACE(bus->gpio_reg) & (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE); @@ -211,7 +209,7 @@ intel_gpio_pre_xfer(struct i2c_adapter *adapter) adapter); struct drm_i915_private *dev_priv = bus->dev_priv; - intel_i2c_reset(&dev_priv->drm); + intel_i2c_reset(dev_priv); intel_i2c_quirk_set(dev_priv, true); set_data(bus, 1); set_clock(bus, 1); @@ -617,11 +615,10 @@ static const struct i2c_algorithm gmbus_algorithm = { /** * intel_gmbus_setup - instantiate all Intel i2c GMBuses - * @dev: DRM device + * @dev_priv: i915 device private */ -int intel_setup_gmbus(struct drm_device *dev) +int intel_setup_gmbus(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct pci_dev *pdev = dev_priv->drm.pdev; struct intel_gmbus *bus; unsigned int pin; @@ -678,7 +675,7 @@ int intel_setup_gmbus(struct drm_device *dev) goto err; } - intel_i2c_reset(&dev_priv->drm); + intel_i2c_reset(dev_priv); return 0; @@ -724,9 +721,8 @@ void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit) mutex_unlock(&dev_priv->gmbus_mutex); } -void intel_teardown_gmbus(struct drm_device *dev) +void intel_teardown_gmbus(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_gmbus *bus; unsigned int pin; diff --git a/drivers/gpu/drm/i915/intel_lpe_audio.c b/drivers/gpu/drm/i915/intel_lpe_audio.c new file mode 100644 index 000000000000..7a5b41b1c024 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_lpe_audio.c @@ -0,0 +1,392 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> + * Jerome Anand <jerome.anand@intel.com> + * based on VED patches + * + */ + +/** + * DOC: LPE Audio integration for HDMI or DP playback + * + * Motivation: + * Atom platforms (e.g. valleyview and cherryTrail) integrates a DMA-based + * interface as an alternative to the traditional HDaudio path. While this + * mode is unrelated to the LPE aka SST audio engine, the documentation refers + * to this mode as LPE so we keep this notation for the sake of consistency. + * + * The interface is handled by a separate standalone driver maintained in the + * ALSA subsystem for simplicity. To minimize the interaction between the two + * subsystems, a bridge is setup between the hdmi-lpe-audio and i915: + * 1. Create a platform device to share MMIO/IRQ resources + * 2. Make the platform device child of i915 device for runtime PM. + * 3. Create IRQ chip to forward the LPE audio irqs. + * the hdmi-lpe-audio driver probes the lpe audio device and creates a new + * sound card + * + * Threats: + * Due to the restriction in Linux platform device model, user need manually + * uninstall the hdmi-lpe-audio driver before uninstalling i915 module, + * otherwise we might run into use-after-free issues after i915 removes the + * platform device: even though hdmi-lpe-audio driver is released, the modules + * is still in "installed" status. + * + * Implementation: + * The MMIO/REG platform resources are created according to the registers + * specification. + * When forwarding LPE audio irqs, the flow control handler selection depends + * on the platform, for example on valleyview handle_simple_irq is enough. + * + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/pci.h> + +#include "i915_drv.h" +#include <linux/delay.h> +#include <drm/intel_lpe_audio.h> + +#define HAS_LPE_AUDIO(dev_priv) ((dev_priv)->lpe_audio.platdev != NULL) + +static struct platform_device * +lpe_audio_platdev_create(struct drm_i915_private *dev_priv) +{ + int ret; + struct drm_device *dev = &dev_priv->drm; + struct platform_device_info pinfo = {}; + struct resource *rsc; + struct platform_device *platdev; + struct intel_hdmi_lpe_audio_pdata *pdata; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + rsc = kcalloc(2, sizeof(*rsc), GFP_KERNEL); + if (!rsc) { + kfree(pdata); + return ERR_PTR(-ENOMEM); + } + + rsc[0].start = rsc[0].end = dev_priv->lpe_audio.irq; + rsc[0].flags = IORESOURCE_IRQ; + rsc[0].name = "hdmi-lpe-audio-irq"; + + rsc[1].start = pci_resource_start(dev->pdev, 0) + + I915_HDMI_LPE_AUDIO_BASE; + rsc[1].end = pci_resource_start(dev->pdev, 0) + + I915_HDMI_LPE_AUDIO_BASE + I915_HDMI_LPE_AUDIO_SIZE - 1; + rsc[1].flags = IORESOURCE_MEM; + rsc[1].name = "hdmi-lpe-audio-mmio"; + + pinfo.parent = dev->dev; + pinfo.name = "hdmi-lpe-audio"; + pinfo.id = -1; + pinfo.res = rsc; + pinfo.num_res = 2; + pinfo.data = pdata; + pinfo.size_data = sizeof(*pdata); + pinfo.dma_mask = DMA_BIT_MASK(32); + + spin_lock_init(&pdata->lpe_audio_slock); + + platdev = platform_device_register_full(&pinfo); + if (IS_ERR(platdev)) { + ret = PTR_ERR(platdev); + DRM_ERROR("Failed to allocate LPE audio platform device\n"); + goto err; + } + + kfree(rsc); + + return platdev; + +err: + kfree(rsc); + kfree(pdata); + return ERR_PTR(ret); +} + +static void lpe_audio_platdev_destroy(struct drm_i915_private *dev_priv) +{ + platform_device_unregister(dev_priv->lpe_audio.platdev); + kfree(dev_priv->lpe_audio.platdev->dev.dma_mask); +} + +static void lpe_audio_irq_unmask(struct irq_data *d) +{ + struct drm_i915_private *dev_priv = d->chip_data; + unsigned long irqflags; + u32 val = (I915_LPE_PIPE_A_INTERRUPT | + I915_LPE_PIPE_B_INTERRUPT); + + if (IS_CHERRYVIEW(dev_priv)) + val |= I915_LPE_PIPE_C_INTERRUPT; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + + dev_priv->irq_mask &= ~val; + I915_WRITE(VLV_IIR, val); + I915_WRITE(VLV_IIR, val); + I915_WRITE(VLV_IMR, dev_priv->irq_mask); + POSTING_READ(VLV_IMR); + + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); +} + +static void lpe_audio_irq_mask(struct irq_data *d) +{ + struct drm_i915_private *dev_priv = d->chip_data; + unsigned long irqflags; + u32 val = (I915_LPE_PIPE_A_INTERRUPT | + I915_LPE_PIPE_B_INTERRUPT); + + if (IS_CHERRYVIEW(dev_priv)) + val |= I915_LPE_PIPE_C_INTERRUPT; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + + dev_priv->irq_mask |= val; + I915_WRITE(VLV_IMR, dev_priv->irq_mask); + I915_WRITE(VLV_IIR, val); + I915_WRITE(VLV_IIR, val); + POSTING_READ(VLV_IIR); + + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); +} + +static struct irq_chip lpe_audio_irqchip = { + .name = "hdmi_lpe_audio_irqchip", + .irq_mask = lpe_audio_irq_mask, + .irq_unmask = lpe_audio_irq_unmask, +}; + +static int lpe_audio_irq_init(struct drm_i915_private *dev_priv) +{ + int irq = dev_priv->lpe_audio.irq; + + WARN_ON(!intel_irqs_enabled(dev_priv)); + irq_set_chip_and_handler_name(irq, + &lpe_audio_irqchip, + handle_simple_irq, + "hdmi_lpe_audio_irq_handler"); + + return irq_set_chip_data(irq, dev_priv); +} + +static bool lpe_audio_detect(struct drm_i915_private *dev_priv) +{ + int lpe_present = false; + + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + static const struct pci_device_id atom_hdaudio_ids[] = { + /* Baytrail */ + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0f04)}, + /* Braswell */ + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2284)}, + {} + }; + + if (!pci_dev_present(atom_hdaudio_ids)) { + DRM_INFO("%s\n", "HDaudio controller not detected, using LPE audio instead\n"); + lpe_present = true; + } + } + return lpe_present; +} + +static int lpe_audio_setup(struct drm_i915_private *dev_priv) +{ + int ret; + + dev_priv->lpe_audio.irq = irq_alloc_desc(0); + if (dev_priv->lpe_audio.irq < 0) { + DRM_ERROR("Failed to allocate IRQ desc: %d\n", + dev_priv->lpe_audio.irq); + ret = dev_priv->lpe_audio.irq; + goto err; + } + + DRM_DEBUG("irq = %d\n", dev_priv->lpe_audio.irq); + + ret = lpe_audio_irq_init(dev_priv); + + if (ret) { + DRM_ERROR("Failed to initialize irqchip for lpe audio: %d\n", + ret); + goto err_free_irq; + } + + dev_priv->lpe_audio.platdev = lpe_audio_platdev_create(dev_priv); + + if (IS_ERR(dev_priv->lpe_audio.platdev)) { + ret = PTR_ERR(dev_priv->lpe_audio.platdev); + DRM_ERROR("Failed to create lpe audio platform device: %d\n", + ret); + goto err_free_irq; + } + + /* enable chicken bit; at least this is required for Dell Wyse 3040 + * with DP outputs (but only sometimes by some reason!) + */ + I915_WRITE(VLV_AUD_CHICKEN_BIT_REG, VLV_CHICKEN_BIT_DBG_ENABLE); + + return 0; +err_free_irq: + irq_free_desc(dev_priv->lpe_audio.irq); +err: + dev_priv->lpe_audio.irq = -1; + dev_priv->lpe_audio.platdev = NULL; + return ret; +} + +/** + * intel_lpe_audio_irq_handler() - forwards the LPE audio irq + * @dev_priv: the i915 drm device private data + * + * the LPE Audio irq is forwarded to the irq handler registered by LPE audio + * driver. + */ +void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv) +{ + int ret; + + if (!HAS_LPE_AUDIO(dev_priv)) + return; + + ret = generic_handle_irq(dev_priv->lpe_audio.irq); + if (ret) + DRM_ERROR_RATELIMITED("error handling LPE audio irq: %d\n", + ret); +} + +/** + * intel_lpe_audio_init() - detect and setup the bridge between HDMI LPE Audio + * driver and i915 + * @dev_priv: the i915 drm device private data + * + * Return: 0 if successful. non-zero if detection or + * llocation/initialization fails + */ +int intel_lpe_audio_init(struct drm_i915_private *dev_priv) +{ + int ret = -ENODEV; + + if (lpe_audio_detect(dev_priv)) { + ret = lpe_audio_setup(dev_priv); + if (ret < 0) + DRM_ERROR("failed to setup LPE Audio bridge\n"); + } + return ret; +} + +/** + * intel_lpe_audio_teardown() - destroy the bridge between HDMI LPE + * audio driver and i915 + * @dev_priv: the i915 drm device private data + * + * release all the resources for LPE audio <-> i915 bridge. + */ +void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv) +{ + struct irq_desc *desc; + + if (!HAS_LPE_AUDIO(dev_priv)) + return; + + desc = irq_to_desc(dev_priv->lpe_audio.irq); + + lpe_audio_irq_mask(&desc->irq_data); + + lpe_audio_platdev_destroy(dev_priv); + + irq_free_desc(dev_priv->lpe_audio.irq); +} + + +/** + * intel_lpe_audio_notify() - notify lpe audio event + * audio driver and i915 + * @dev_priv: the i915 drm device private data + * @eld : ELD data + * @port: port id + * @tmds_clk_speed: tmds clock frequency in Hz + * + * Notify lpe audio driver of eld change. + */ +void intel_lpe_audio_notify(struct drm_i915_private *dev_priv, + void *eld, int port, int pipe, int tmds_clk_speed, + bool dp_output, int link_rate) +{ + unsigned long irq_flags; + struct intel_hdmi_lpe_audio_pdata *pdata = NULL; + u32 audio_enable; + + if (!HAS_LPE_AUDIO(dev_priv)) + return; + + pdata = dev_get_platdata( + &(dev_priv->lpe_audio.platdev->dev)); + + spin_lock_irqsave(&pdata->lpe_audio_slock, irq_flags); + + audio_enable = I915_READ(VLV_AUD_PORT_EN_DBG(port)); + + if (eld != NULL) { + memcpy(pdata->eld.eld_data, eld, + HDMI_MAX_ELD_BYTES); + pdata->eld.port_id = port; + pdata->eld.pipe_id = pipe; + pdata->hdmi_connected = true; + + pdata->dp_output = dp_output; + if (tmds_clk_speed) + pdata->tmds_clock_speed = tmds_clk_speed; + if (link_rate) + pdata->link_rate = link_rate; + + /* Unmute the amp for both DP and HDMI */ + I915_WRITE(VLV_AUD_PORT_EN_DBG(port), + audio_enable & ~VLV_AMP_MUTE); + + } else { + memset(pdata->eld.eld_data, 0, + HDMI_MAX_ELD_BYTES); + pdata->hdmi_connected = false; + pdata->dp_output = false; + + /* Mute the amp for both DP and HDMI */ + I915_WRITE(VLV_AUD_PORT_EN_DBG(port), + audio_enable | VLV_AMP_MUTE); + } + + if (pdata->notify_audio_lpe) + pdata->notify_audio_lpe(dev_priv->lpe_audio.platdev); + else + pdata->notify_pending = true; + + spin_unlock_irqrestore(&pdata->lpe_audio_slock, + irq_flags); +} diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index beabc17e7c8a..ebf8023d21e6 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -230,8 +230,6 @@ enum { static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, struct intel_engine_cs *engine); -static int intel_lr_context_pin(struct i915_gem_context *ctx, - struct intel_engine_cs *engine); static void execlists_init_reg_state(u32 *reg_state, struct i915_gem_context *ctx, struct intel_engine_cs *engine, @@ -362,7 +360,8 @@ execlists_update_context_pdps(struct i915_hw_ppgtt *ppgtt, u32 *reg_state) static u64 execlists_update_context(struct drm_i915_gem_request *rq) { struct intel_context *ce = &rq->ctx->engine[rq->engine->id]; - struct i915_hw_ppgtt *ppgtt = rq->ctx->ppgtt; + struct i915_hw_ppgtt *ppgtt = + rq->ctx->ppgtt ?: rq->i915->mm.aliasing_ppgtt; u32 *reg_state = ce->lrc_reg_state; reg_state[CTX_RING_TAIL+1] = rq->tail; @@ -415,7 +414,7 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) static bool ctx_single_port_submission(const struct i915_gem_context *ctx) { return (IS_ENABLED(CONFIG_DRM_I915_GVT) && - ctx->execlists_force_single_submission); + i915_gem_context_force_single_submission(ctx)); } static bool can_merge_ctx(const struct i915_gem_context *prev, @@ -514,15 +513,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) RB_CLEAR_NODE(&cursor->priotree.node); cursor->priotree.priority = INT_MAX; - /* We keep the previous context alive until we retire the - * following request. This ensures that any the context object - * is still pinned for any residual writes the HW makes into it - * on the context switch into the next object following the - * breadcrumb. Otherwise, we may retire the context too early. - */ - cursor->previous_context = engine->last_context; - engine->last_context = cursor->ctx; - __i915_gem_request_submit(cursor); last = cursor; submit = true; @@ -695,7 +685,6 @@ pt_lock_engine(struct i915_priotree *pt, struct intel_engine_cs *locked) static void execlists_schedule(struct drm_i915_gem_request *request, int prio) { - static DEFINE_MUTEX(lock); struct intel_engine_cs *engine = NULL; struct i915_dependency *dep, *p; struct i915_dependency stack; @@ -704,8 +693,8 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio) if (prio <= READ_ONCE(request->priotree.priority)) return; - /* Need global lock to use the temporary link inside i915_dependency */ - mutex_lock(&lock); + /* Need BKL in order to use the temporary link inside i915_dependency */ + lockdep_assert_held(&request->i915->drm.struct_mutex); stack.signaler = &request->priotree; list_add(&stack.dfs_link, &dfs); @@ -734,7 +723,7 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio) if (prio > READ_ONCE(p->signaler->priority)) list_move_tail(&p->dfs_link, &dfs); - p = list_next_entry(dep, dfs_link); + list_safe_reset_next(dep, p, dfs_link); if (!RB_EMPTY_NODE(&pt->node)) continue; @@ -772,80 +761,14 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio) if (engine) spin_unlock_irq(&engine->timeline->lock); - mutex_unlock(&lock); - /* XXX Do we need to preempt to make room for us and our deps? */ } -int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request) -{ - struct intel_engine_cs *engine = request->engine; - struct intel_context *ce = &request->ctx->engine[engine->id]; - int ret; - - /* Flush enough space to reduce the likelihood of waiting after - * we start building the request - in which case we will just - * have to repeat work. - */ - request->reserved_space += EXECLISTS_REQUEST_SIZE; - - if (!ce->state) { - ret = execlists_context_deferred_alloc(request->ctx, engine); - if (ret) - return ret; - } - - request->ring = ce->ring; - - ret = intel_lr_context_pin(request->ctx, engine); - if (ret) - return ret; - - if (i915.enable_guc_submission) { - /* - * Check that the GuC has space for the request before - * going any further, as the i915_add_request() call - * later on mustn't fail ... - */ - ret = i915_guc_wq_reserve(request); - if (ret) - goto err_unpin; - } - - ret = intel_ring_begin(request, 0); - if (ret) - goto err_unreserve; - - if (!ce->initialised) { - ret = engine->init_context(request); - if (ret) - goto err_unreserve; - - ce->initialised = true; - } - - /* Note that after this point, we have committed to using - * this request as it is being used to both track the - * state of engine initialisation and liveness of the - * golden renderstate above. Think twice before you try - * to cancel/unwind this request now. - */ - - request->reserved_space -= EXECLISTS_REQUEST_SIZE; - return 0; - -err_unreserve: - if (i915.enable_guc_submission) - i915_guc_wq_unreserve(request); -err_unpin: - intel_lr_context_unpin(request->ctx, engine); - return ret; -} - -static int intel_lr_context_pin(struct i915_gem_context *ctx, - struct intel_engine_cs *engine) +static int execlists_context_pin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) { struct intel_context *ce = &ctx->engine[engine->id]; + unsigned int flags; void *vaddr; int ret; @@ -854,8 +777,20 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx, if (ce->pin_count++) return 0; - ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN, - PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL); + if (!ce->state) { + ret = execlists_context_deferred_alloc(ctx, engine); + if (ret) + goto err; + } + GEM_BUG_ON(!ce->state); + + flags = PIN_GLOBAL; + if (ctx->ggtt_offset_bias) + flags |= PIN_OFFSET_BIAS | ctx->ggtt_offset_bias; + if (i915_gem_context_is_kernel(ctx)) + flags |= PIN_HIGH; + + ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN, flags); if (ret) goto err; @@ -865,7 +800,7 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx, goto unpin_vma; } - ret = intel_ring_pin(ce->ring); + ret = intel_ring_pin(ce->ring, ctx->ggtt_offset_bias); if (ret) goto unpin_map; @@ -877,12 +812,6 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx, ce->state->obj->mm.dirty = true; - /* Invalidate GuC TLB. */ - if (i915.enable_guc_submission) { - struct drm_i915_private *dev_priv = ctx->i915; - I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE); - } - i915_gem_context_get(ctx); return 0; @@ -895,8 +824,8 @@ err: return ret; } -void intel_lr_context_unpin(struct i915_gem_context *ctx, - struct intel_engine_cs *engine) +static void execlists_context_unpin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) { struct intel_context *ce = &ctx->engine[engine->id]; @@ -914,6 +843,63 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx, i915_gem_context_put(ctx); } +static int execlists_request_alloc(struct drm_i915_gem_request *request) +{ + struct intel_engine_cs *engine = request->engine; + struct intel_context *ce = &request->ctx->engine[engine->id]; + int ret; + + GEM_BUG_ON(!ce->pin_count); + + /* Flush enough space to reduce the likelihood of waiting after + * we start building the request - in which case we will just + * have to repeat work. + */ + request->reserved_space += EXECLISTS_REQUEST_SIZE; + + GEM_BUG_ON(!ce->ring); + request->ring = ce->ring; + + if (i915.enable_guc_submission) { + /* + * Check that the GuC has space for the request before + * going any further, as the i915_add_request() call + * later on mustn't fail ... + */ + ret = i915_guc_wq_reserve(request); + if (ret) + goto err; + } + + ret = intel_ring_begin(request, 0); + if (ret) + goto err_unreserve; + + if (!ce->initialised) { + ret = engine->init_context(request); + if (ret) + goto err_unreserve; + + ce->initialised = true; + } + + /* Note that after this point, we have committed to using + * this request as it is being used to both track the + * state of engine initialisation and liveness of the + * golden renderstate above. Think twice before you try + * to cancel/unwind this request now. + */ + + request->reserved_space -= EXECLISTS_REQUEST_SIZE; + return 0; + +err_unreserve: + if (i915.enable_guc_submission) + i915_guc_wq_unreserve(request); +err: + return ret; +} + static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req) { int ret, i; @@ -1236,11 +1222,11 @@ static int lrc_setup_wa_ctx_obj(struct intel_engine_cs *engine, u32 size) struct i915_vma *vma; int err; - obj = i915_gem_object_create(&engine->i915->drm, PAGE_ALIGN(size)); + obj = i915_gem_object_create(engine->i915, PAGE_ALIGN(size)); if (IS_ERR(obj)) return PTR_ERR(obj); - vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL); + vma = i915_vma_instance(obj, &engine->i915->ggtt.base, NULL); if (IS_ERR(vma)) { err = PTR_ERR(vma); goto err; @@ -1334,15 +1320,6 @@ out: return ret; } -static void lrc_init_hws(struct intel_engine_cs *engine) -{ - struct drm_i915_private *dev_priv = engine->i915; - - I915_WRITE(RING_HWS_PGA(engine->mmio_base), - engine->status_page.ggtt_offset); - POSTING_READ(RING_HWS_PGA(engine->mmio_base)); -} - static int gen8_init_common_ring(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; @@ -1352,20 +1329,19 @@ static int gen8_init_common_ring(struct intel_engine_cs *engine) if (ret) return ret; - lrc_init_hws(engine); - intel_engine_reset_breadcrumbs(engine); + intel_engine_init_hangcheck(engine); I915_WRITE(RING_HWSTAM(engine->mmio_base), 0xffffffff); - I915_WRITE(RING_MODE_GEN7(engine), _MASKED_BIT_DISABLE(GFX_REPLAY_MODE) | _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE)); + I915_WRITE(RING_HWS_PGA(engine->mmio_base), + engine->status_page.ggtt_offset); + POSTING_READ(RING_HWS_PGA(engine->mmio_base)); DRM_DEBUG_DRIVER("Execlists enabled for %s\n", engine->name); - intel_engine_init_hangcheck(engine); - /* After a GPU reset, we may have requests to replay */ if (!execlists_elsp_idle(engine)) { engine->execlist_port[0].count = 0; @@ -1414,7 +1390,20 @@ static void reset_common_ring(struct intel_engine_cs *engine, { struct drm_i915_private *dev_priv = engine->i915; struct execlist_port *port = engine->execlist_port; - struct intel_context *ce = &request->ctx->engine[engine->id]; + struct intel_context *ce; + + /* If the request was innocent, we leave the request in the ELSP + * and will try to replay it on restarting. The context image may + * have been corrupted by the reset, in which case we may have + * to service a new GPU hang, but more likely we can continue on + * without impact. + * + * If the request was guilty, we presume the context is corrupt + * and have to at least restore the RING register in the context + * image back to the expected values to skip over the guilty request. + */ + if (!request || request->fence.error != -EIO) + return; /* We want a simple context + ring to execute the breadcrumb update. * We cannot rely on the context being intact across the GPU hang, @@ -1423,6 +1412,7 @@ static void reset_common_ring(struct intel_engine_cs *engine, * future request will be after userspace has had the opportunity * to recreate its own state. */ + ce = &request->ctx->engine[engine->id]; execlists_init_reg_state(ce->lrc_reg_state, request->ctx, engine, ce->ring); @@ -1784,13 +1774,12 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *engine) if (engine->cleanup) engine->cleanup(engine); - intel_engine_cleanup_common(engine); - if (engine->status_page.vma) { i915_gem_object_unpin_map(engine->status_page.vma->obj); engine->status_page.vma = NULL; } - intel_lr_context_unpin(dev_priv->kernel_context, engine); + + intel_engine_cleanup_common(engine); lrc_destroy_wa_ctx_obj(engine); engine->i915 = NULL; @@ -1815,6 +1804,12 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine) /* Default vfuncs which can be overriden by each engine. */ engine->init_hw = gen8_init_common_ring; engine->reset_hw = reset_common_ring; + + engine->context_pin = execlists_context_pin; + engine->context_unpin = execlists_context_unpin; + + engine->request_alloc = execlists_request_alloc; + engine->emit_flush = gen8_emit_flush; engine->emit_breadcrumb = gen8_emit_breadcrumb; engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_sz; @@ -1897,18 +1892,6 @@ logical_ring_init(struct intel_engine_cs *engine) if (ret) goto error; - ret = execlists_context_deferred_alloc(dctx, engine); - if (ret) - goto error; - - /* As this is the default context, always pin it */ - ret = intel_lr_context_pin(dctx, engine); - if (ret) { - DRM_ERROR("Failed to pin context for %s: %d\n", - engine->name, ret); - goto error; - } - /* And setup the hardware status page. */ ret = lrc_setup_hws(engine, dctx->engine[engine->id].state); if (ret) { @@ -1943,7 +1926,7 @@ int logical_render_ring_init(struct intel_engine_cs *engine) engine->emit_breadcrumb = gen8_emit_breadcrumb_render; engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_render_sz; - ret = intel_engine_create_scratch(engine, 4096); + ret = intel_engine_create_scratch(engine, PAGE_SIZE); if (ret) return ret; @@ -2119,19 +2102,12 @@ static void execlists_init_reg_state(u32 *reg_state, ASSIGN_CTX_REG(reg_state, CTX_PDP0_LDW, GEN8_RING_PDP_LDW(engine, 0), 0); - if (USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { + if (ppgtt && USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { /* 64b PPGTT (48bit canonical) * PDP0_DESCRIPTOR contains the base address to PML4 and * other PDP Descriptors are ignored. */ ASSIGN_CTX_PML4(ppgtt, reg_state); - } else { - /* 32b PPGTT - * PDP*_DESCRIPTOR contains the base address of space supported. - * With dynamic page allocation, PDPs may not be allocated at - * this point. Point the unallocated PDPs to the scratch page - */ - execlists_update_context_pdps(ppgtt, reg_state); } if (engine->id == RCS) { @@ -2225,18 +2201,19 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, WARN_ON(ce->state); - context_size = round_up(intel_lr_context_size(engine), 4096); + context_size = round_up(intel_lr_context_size(engine), + I915_GTT_PAGE_SIZE); /* One extra page as the sharing data between driver and GuC */ context_size += PAGE_SIZE * LRC_PPHWSP_PN; - ctx_obj = i915_gem_object_create(&ctx->i915->drm, context_size); + ctx_obj = i915_gem_object_create(ctx->i915, context_size); if (IS_ERR(ctx_obj)) { DRM_DEBUG_DRIVER("Alloc LRC backing obj failed.\n"); return PTR_ERR(ctx_obj); } - vma = i915_vma_create(ctx_obj, &ctx->i915->ggtt.base, NULL); + vma = i915_vma_instance(ctx_obj, &ctx->i915->ggtt.base, NULL); if (IS_ERR(vma)) { ret = PTR_ERR(vma); goto error_deref_obj; diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index c1f546180ba2..0c852c024227 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -26,7 +26,7 @@ #include "intel_ringbuffer.h" -#define GEN8_LR_CONTEXT_ALIGN 4096 +#define GEN8_LR_CONTEXT_ALIGN I915_GTT_MIN_ALIGNMENT /* Execlists regs */ #define RING_ELSP(engine) _MMIO((engine)->mmio_base + 0x230) @@ -63,14 +63,12 @@ enum { }; /* Logical Rings */ -int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request); -int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request); void intel_logical_ring_stop(struct intel_engine_cs *engine); void intel_logical_ring_cleanup(struct intel_engine_cs *engine); int logical_render_ring_init(struct intel_engine_cs *engine); int logical_xcs_ring_init(struct intel_engine_cs *engine); -int intel_engines_init(struct drm_device *dev); +int intel_engines_init(struct drm_i915_private *dev_priv); /* Logical Ring Contexts */ @@ -79,13 +77,10 @@ int intel_engines_init(struct drm_device *dev); #define LRC_PPHWSP_PN (LRC_GUCSHR_PN + 1) #define LRC_STATE_PN (LRC_PPHWSP_PN + 1) +struct drm_i915_private; struct i915_gem_context; uint32_t intel_lr_context_size(struct intel_engine_cs *engine); -void intel_lr_context_unpin(struct i915_gem_context *ctx, - struct intel_engine_cs *engine); - -struct drm_i915_private; void intel_lr_context_resume(struct drm_i915_private *dev_priv); uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx, diff --git a/drivers/gpu/drm/i915/intel_lspcon.c b/drivers/gpu/drm/i915/intel_lspcon.c index daa523410953..c300647ef604 100644 --- a/drivers/gpu/drm/i915/intel_lspcon.c +++ b/drivers/gpu/drm/i915/intel_lspcon.c @@ -35,21 +35,59 @@ static struct intel_dp *lspcon_to_intel_dp(struct intel_lspcon *lspcon) return &dig_port->dp; } +static const char *lspcon_mode_name(enum drm_lspcon_mode mode) +{ + switch (mode) { + case DRM_LSPCON_MODE_PCON: + return "PCON"; + case DRM_LSPCON_MODE_LS: + return "LS"; + case DRM_LSPCON_MODE_INVALID: + return "INVALID"; + default: + MISSING_CASE(mode); + return "INVALID"; + } +} + static enum drm_lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon) { - enum drm_lspcon_mode current_mode = DRM_LSPCON_MODE_INVALID; + enum drm_lspcon_mode current_mode; struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc; - if (drm_lspcon_get_mode(adapter, ¤t_mode)) + if (drm_lspcon_get_mode(adapter, ¤t_mode)) { DRM_ERROR("Error reading LSPCON mode\n"); - else - DRM_DEBUG_KMS("Current LSPCON mode %s\n", - current_mode == DRM_LSPCON_MODE_PCON ? "PCON" : "LS"); + return DRM_LSPCON_MODE_INVALID; + } + return current_mode; +} + +static enum drm_lspcon_mode lspcon_wait_mode(struct intel_lspcon *lspcon, + enum drm_lspcon_mode mode) +{ + enum drm_lspcon_mode current_mode; + + current_mode = lspcon_get_current_mode(lspcon); + if (current_mode == mode || current_mode == DRM_LSPCON_MODE_INVALID) + goto out; + + DRM_DEBUG_KMS("Waiting for LSPCON mode %s to settle\n", + lspcon_mode_name(mode)); + + wait_for((current_mode = lspcon_get_current_mode(lspcon)) == mode || + current_mode == DRM_LSPCON_MODE_INVALID, 100); + if (current_mode != mode) + DRM_DEBUG_KMS("LSPCON mode hasn't settled\n"); + +out: + DRM_DEBUG_KMS("Current LSPCON mode %s\n", + lspcon_mode_name(current_mode)); + return current_mode; } static int lspcon_change_mode(struct intel_lspcon *lspcon, - enum drm_lspcon_mode mode, bool force) + enum drm_lspcon_mode mode) { int err; enum drm_lspcon_mode current_mode; @@ -77,10 +115,30 @@ static int lspcon_change_mode(struct intel_lspcon *lspcon, return 0; } +static bool lspcon_wake_native_aux_ch(struct intel_lspcon *lspcon) +{ + uint8_t rev; + + if (drm_dp_dpcd_readb(&lspcon_to_intel_dp(lspcon)->aux, DP_DPCD_REV, + &rev) != 1) { + DRM_DEBUG_KMS("Native AUX CH down\n"); + return false; + } + + DRM_DEBUG_KMS("Native AUX CH up, DPCD version: %d.%d\n", + rev >> 4, rev & 0xf); + + return true; +} + static bool lspcon_probe(struct intel_lspcon *lspcon) { enum drm_dp_dual_mode_type adaptor_type; struct i2c_adapter *adapter = &lspcon_to_intel_dp(lspcon)->aux.ddc; + enum drm_lspcon_mode expected_mode; + + expected_mode = lspcon_wake_native_aux_ch(lspcon) ? + DRM_LSPCON_MODE_PCON : DRM_LSPCON_MODE_LS; /* Lets probe the adaptor and check its type */ adaptor_type = drm_dp_dual_mode_detect(adapter); @@ -92,7 +150,7 @@ static bool lspcon_probe(struct intel_lspcon *lspcon) /* Yay ... got a LSPCON device */ DRM_DEBUG_KMS("LSPCON detected\n"); - lspcon->mode = lspcon_get_current_mode(lspcon); + lspcon->mode = lspcon_wait_mode(lspcon, expected_mode); lspcon->active = true; return true; } @@ -100,6 +158,8 @@ static bool lspcon_probe(struct intel_lspcon *lspcon) static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon) { struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev); unsigned long start = jiffies; if (!lspcon->desc_valid) @@ -115,7 +175,8 @@ static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon) if (!__intel_dp_read_desc(intel_dp, &desc)) return; - if (!memcmp(&intel_dp->desc, &desc, sizeof(desc))) { + if (intel_digital_port_connected(dev_priv, dig_port) && + !memcmp(&intel_dp->desc, &desc, sizeof(desc))) { DRM_DEBUG_KMS("LSPCON recovering in PCON mode after %u ms\n", jiffies_to_msecs(jiffies - start)); return; @@ -132,14 +193,29 @@ static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon) void lspcon_resume(struct intel_lspcon *lspcon) { - lspcon_resume_in_pcon_wa(lspcon); + enum drm_lspcon_mode expected_mode; + + if (lspcon_wake_native_aux_ch(lspcon)) { + expected_mode = DRM_LSPCON_MODE_PCON; + lspcon_resume_in_pcon_wa(lspcon); + } else { + expected_mode = DRM_LSPCON_MODE_LS; + } + + if (lspcon_wait_mode(lspcon, expected_mode) == DRM_LSPCON_MODE_PCON) + return; - if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON, true)) + if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON)) DRM_ERROR("LSPCON resume failed\n"); else DRM_DEBUG_KMS("LSPCON resume success\n"); } +void lspcon_wait_pcon_mode(struct intel_lspcon *lspcon) +{ + lspcon_wait_mode(lspcon, DRM_LSPCON_MODE_PCON); +} + bool lspcon_init(struct intel_digital_port *intel_dig_port) { struct intel_dp *dp = &intel_dig_port->dp; @@ -166,8 +242,7 @@ bool lspcon_init(struct intel_digital_port *intel_dig_port) * 2.0 sinks. */ if (lspcon->active && lspcon->mode != DRM_LSPCON_MODE_PCON) { - if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON, - true) < 0) { + if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON) < 0) { DRM_ERROR("LSPCON mode change to PCON failed\n"); return false; } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index d12ef0047d49..9ca4dc4d2378 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -460,13 +460,13 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector, bool force) { - struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = to_i915(connector->dev); enum drm_connector_status status; DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); - status = intel_panel_detect(dev); + status = intel_panel_detect(dev_priv); if (status != connector_status_unknown) return status; @@ -971,9 +971,9 @@ static bool intel_lvds_supported(struct drm_i915_private *dev_priv) * Create the connector, register the LVDS DDC bus, and try to figure out what * modes we can display on the LVDS panel (if present). */ -void intel_lvds_init(struct drm_device *dev) +void intel_lvds_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_device *dev = &dev_priv->drm; struct intel_lvds_encoder *lvds_encoder; struct intel_encoder *intel_encoder; struct intel_lvds_connector *lvds_connector; diff --git a/drivers/gpu/drm/i915/intel_mocs.c b/drivers/gpu/drm/i915/intel_mocs.c index 80bb9247ce66..c787fc4e6eb9 100644 --- a/drivers/gpu/drm/i915/intel_mocs.c +++ b/drivers/gpu/drm/i915/intel_mocs.c @@ -182,7 +182,7 @@ static bool get_mocs_settings(struct drm_i915_private *dev_priv, table->size = ARRAY_SIZE(skylake_mocs_table); table->table = skylake_mocs_table; result = true; - } else if (IS_BROXTON(dev_priv)) { + } else if (IS_GEN9_LP(dev_priv)) { table->size = ARRAY_SIZE(broxton_mocs_table); table->table = broxton_mocs_table; result = true; @@ -380,7 +380,7 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req, /** * intel_mocs_init_l3cc_table() - program the mocs control table - * @dev: The the device to be programmed. + * @dev_priv: i915 device private * * This function simply programs the mocs registers for the given table * starting at the given address. This register set is programmed in pairs. @@ -392,9 +392,8 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req, * * Return: Nothing. */ -void intel_mocs_init_l3cc_table(struct drm_device *dev) +void intel_mocs_init_l3cc_table(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_mocs_table table; unsigned int i; diff --git a/drivers/gpu/drm/i915/intel_mocs.h b/drivers/gpu/drm/i915/intel_mocs.h index a8bd9f7bfece..ce4a5dfa5f94 100644 --- a/drivers/gpu/drm/i915/intel_mocs.h +++ b/drivers/gpu/drm/i915/intel_mocs.h @@ -53,7 +53,7 @@ #include "i915_drv.h" int intel_rcs_context_init_mocs(struct drm_i915_gem_request *req); -void intel_mocs_init_l3cc_table(struct drm_device *dev); +void intel_mocs_init_l3cc_table(struct drm_i915_private *dev_priv); int intel_mocs_init_engine(struct intel_engine_cs *engine); #endif diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index f4429f67a4e3..4a862a358c70 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -982,7 +982,18 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv) opregion->vbt_size = vbt_size; } else { vbt = base + OPREGION_VBT_OFFSET; - vbt_size = OPREGION_ASLE_EXT_OFFSET - OPREGION_VBT_OFFSET; + /* + * The VBT specification says that if the ASLE ext + * mailbox is not used its area is reserved, but + * on some CHT boards the VBT extends into the + * ASLE ext area. Allow this even though it is + * against the spec, so we do not end up rejecting + * the VBT on those boards (and end up not finding the + * LCD panel because of this). + */ + vbt_size = (mboxes & MBOX_ASLE_EXT) ? + OPREGION_ASLE_EXT_OFFSET : OPREGION_SIZE; + vbt_size -= OPREGION_VBT_OFFSET; if (intel_bios_is_valid_vbt(vbt, vbt_size)) { DRM_DEBUG_KMS("Found valid VBT in ACPI OpRegion (Mailbox #4)\n"); opregion->vbt = vbt; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index e589e17876dc..0608fad7f593 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -187,6 +187,29 @@ struct intel_overlay { struct i915_gem_active last_flip; }; +static void i830_overlay_clock_gating(struct drm_i915_private *dev_priv, + bool enable) +{ + struct pci_dev *pdev = dev_priv->drm.pdev; + u8 val; + + /* WA_OVERLAY_CLKGATE:alm */ + if (enable) + I915_WRITE(DSPCLK_GATE_D, 0); + else + I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE); + + /* WA_DISABLE_L2CACHE_CLOCK_GATING:alm */ + pci_bus_read_config_byte(pdev->bus, + PCI_DEVFN(0, 0), I830_CLOCK_GATE, &val); + if (enable) + val &= ~I830_L2_CACHE_CLOCK_GATE_DISABLE; + else + val |= I830_L2_CACHE_CLOCK_GATE_DISABLE; + pci_bus_write_config_byte(pdev->bus, + PCI_DEVFN(0, 0), I830_CLOCK_GATE, val); +} + static struct overlay_registers __iomem * intel_overlay_map_regs(struct intel_overlay *overlay) { @@ -262,6 +285,9 @@ static int intel_overlay_on(struct intel_overlay *overlay) overlay->active = true; + if (IS_I830(dev_priv)) + i830_overlay_clock_gating(dev_priv, false); + ring = req->ring; intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_ON); intel_ring_emit(ring, overlay->flip_addr | OFC_UPDATE); @@ -272,8 +298,30 @@ static int intel_overlay_on(struct intel_overlay *overlay) return intel_overlay_do_wait_request(overlay, req, NULL); } +static void intel_overlay_flip_prepare(struct intel_overlay *overlay, + struct i915_vma *vma) +{ + enum pipe pipe = overlay->crtc->pipe; + + WARN_ON(overlay->old_vma); + + i915_gem_track_fb(overlay->vma ? overlay->vma->obj : NULL, + vma ? vma->obj : NULL, + INTEL_FRONTBUFFER_OVERLAY(pipe)); + + intel_frontbuffer_flip_prepare(overlay->i915, + INTEL_FRONTBUFFER_OVERLAY(pipe)); + + overlay->old_vma = overlay->vma; + if (vma) + overlay->vma = i915_vma_get(vma); + else + overlay->vma = NULL; +} + /* overlay needs to be enabled in OCMD reg */ static int intel_overlay_continue(struct intel_overlay *overlay, + struct i915_vma *vma, bool load_polyphase_filter) { struct drm_i915_private *dev_priv = overlay->i915; @@ -308,53 +356,57 @@ static int intel_overlay_continue(struct intel_overlay *overlay, intel_ring_emit(ring, flip_addr); intel_ring_advance(ring); + intel_overlay_flip_prepare(overlay, vma); + intel_overlay_submit_request(overlay, req, NULL); return 0; } -static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active, - struct drm_i915_gem_request *req) +static void intel_overlay_release_old_vma(struct intel_overlay *overlay) { - struct intel_overlay *overlay = - container_of(active, typeof(*overlay), last_flip); struct i915_vma *vma; vma = fetch_and_zero(&overlay->old_vma); if (WARN_ON(!vma)) return; - i915_gem_track_fb(vma->obj, NULL, - INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe)); + intel_frontbuffer_flip_complete(overlay->i915, + INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe)); i915_gem_object_unpin_from_display_plane(vma); i915_vma_put(vma); } +static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active, + struct drm_i915_gem_request *req) +{ + struct intel_overlay *overlay = + container_of(active, typeof(*overlay), last_flip); + + intel_overlay_release_old_vma(overlay); +} + static void intel_overlay_off_tail(struct i915_gem_active *active, struct drm_i915_gem_request *req) { struct intel_overlay *overlay = container_of(active, typeof(*overlay), last_flip); - struct i915_vma *vma; - - /* never have the overlay hw on without showing a frame */ - vma = fetch_and_zero(&overlay->vma); - if (WARN_ON(!vma)) - return; + struct drm_i915_private *dev_priv = overlay->i915; - i915_gem_object_unpin_from_display_plane(vma); - i915_vma_put(vma); + intel_overlay_release_old_vma(overlay); overlay->crtc->overlay = NULL; overlay->crtc = NULL; overlay->active = false; + + if (IS_I830(dev_priv)) + i830_overlay_clock_gating(dev_priv, true); } /* overlay needs to be disabled in OCMD reg */ static int intel_overlay_off(struct intel_overlay *overlay) { - struct drm_i915_private *dev_priv = overlay->i915; struct drm_i915_gem_request *req; struct intel_ring *ring; u32 flip_addr = overlay->flip_addr; @@ -379,25 +431,21 @@ static int intel_overlay_off(struct intel_overlay *overlay) } ring = req->ring; + /* wait for overlay to go idle */ intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); intel_ring_emit(ring, flip_addr); intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + /* turn overlay off */ - if (IS_I830(dev_priv)) { - /* Workaround: Don't disable the overlay fully, since otherwise - * it dies on the next OVERLAY_ON cmd. */ - intel_ring_emit(ring, MI_NOOP); - intel_ring_emit(ring, MI_NOOP); - intel_ring_emit(ring, MI_NOOP); - } else { - intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF); - intel_ring_emit(ring, flip_addr); - intel_ring_emit(ring, - MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - } + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF); + intel_ring_emit(ring, flip_addr); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_advance(ring); + intel_overlay_flip_prepare(overlay, NULL); + return intel_overlay_do_wait_request(overlay, req, intel_overlay_off_tail); } @@ -542,51 +590,57 @@ static int uv_vsubsampling(u32 format) static u32 calc_swidthsw(struct drm_i915_private *dev_priv, u32 offset, u32 width) { - u32 mask, shift, ret; - if (IS_GEN2(dev_priv)) { - mask = 0x1f; - shift = 5; - } else { - mask = 0x3f; - shift = 6; - } - ret = ((offset + width + mask) >> shift) - (offset >> shift); - if (!IS_GEN2(dev_priv)) - ret <<= 1; - ret -= 1; - return ret << 2; + u32 sw; + + if (IS_GEN2(dev_priv)) + sw = ALIGN((offset & 31) + width, 32); + else + sw = ALIGN((offset & 63) + width, 64); + + if (sw == 0) + return 0; + + return (sw - 32) >> 3; } -static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = { - 0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0, - 0x3000, 0xb500, 0x19d0, 0x1880, 0xb440, - 0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0, - 0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380, - 0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320, - 0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0, - 0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260, - 0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200, - 0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0, - 0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160, - 0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120, - 0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0, - 0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0, - 0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060, - 0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040, - 0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020, - 0xb000, 0x3000, 0x0800, 0x3000, 0xb000 +static const u16 y_static_hcoeffs[N_PHASES][N_HORIZ_Y_TAPS] = { + [ 0] = { 0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0, }, + [ 1] = { 0x3000, 0xb500, 0x19d0, 0x1880, 0xb440, }, + [ 2] = { 0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0, }, + [ 3] = { 0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380, }, + [ 4] = { 0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320, }, + [ 5] = { 0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0, }, + [ 6] = { 0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260, }, + [ 7] = { 0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200, }, + [ 8] = { 0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0, }, + [ 9] = { 0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160, }, + [10] = { 0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120, }, + [11] = { 0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0, }, + [12] = { 0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0, }, + [13] = { 0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060, }, + [14] = { 0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040, }, + [15] = { 0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020, }, + [16] = { 0xb000, 0x3000, 0x0800, 0x3000, 0xb000, }, }; -static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = { - 0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60, - 0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40, - 0xb040, 0x1b20, 0x29e0, 0xb060, 0x1bd8, 0x2880, - 0xb080, 0x1c88, 0x3e60, 0xb0a0, 0x1d28, 0x3c00, - 0xb0c0, 0x1db8, 0x39e0, 0xb0e0, 0x1e40, 0x37e0, - 0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0, - 0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240, - 0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0, - 0x3000, 0x0800, 0x3000 +static const u16 uv_static_hcoeffs[N_PHASES][N_HORIZ_UV_TAPS] = { + [ 0] = { 0x3000, 0x1800, 0x1800, }, + [ 1] = { 0xb000, 0x18d0, 0x2e60, }, + [ 2] = { 0xb000, 0x1990, 0x2ce0, }, + [ 3] = { 0xb020, 0x1a68, 0x2b40, }, + [ 4] = { 0xb040, 0x1b20, 0x29e0, }, + [ 5] = { 0xb060, 0x1bd8, 0x2880, }, + [ 6] = { 0xb080, 0x1c88, 0x3e60, }, + [ 7] = { 0xb0a0, 0x1d28, 0x3c00, }, + [ 8] = { 0xb0c0, 0x1db8, 0x39e0, }, + [ 9] = { 0xb0e0, 0x1e40, 0x37e0, }, + [10] = { 0xb100, 0x1eb8, 0x3620, }, + [11] = { 0xb100, 0x1f18, 0x34a0, }, + [12] = { 0xb100, 0x1f68, 0x3360, }, + [13] = { 0xb0e0, 0x1fa8, 0x3240, }, + [14] = { 0xb0c0, 0x1fe0, 0x3140, }, + [15] = { 0xb060, 0x1ff0, 0x30a0, }, + [16] = { 0x3000, 0x0800, 0x3000, }, }; static void update_polyphase_filter(struct overlay_registers __iomem *regs) @@ -659,31 +713,32 @@ static bool update_scaling_factors(struct intel_overlay *overlay, static void update_colorkey(struct intel_overlay *overlay, struct overlay_registers __iomem *regs) { + const struct intel_plane_state *state = + to_intel_plane_state(overlay->crtc->base.primary->state); u32 key = overlay->color_key; - u32 flags; + u32 format = 0; + u32 flags = 0; - flags = 0; if (overlay->color_key_enabled) flags |= DST_KEY_ENABLE; - switch (overlay->crtc->base.primary->fb->bits_per_pixel) { - case 8: + if (state->base.visible) + format = state->base.fb->format->format; + + switch (format) { + case DRM_FORMAT_C8: key = 0; flags |= CLK_RGB8I_MASK; break; - - case 16: - if (overlay->crtc->base.primary->fb->depth == 15) { - key = RGB15_TO_COLORKEY(key); - flags |= CLK_RGB15_MASK; - } else { - key = RGB16_TO_COLORKEY(key); - flags |= CLK_RGB16_MASK; - } + case DRM_FORMAT_XRGB1555: + key = RGB15_TO_COLORKEY(key); + flags |= CLK_RGB15_MASK; break; - - case 24: - case 32: + case DRM_FORMAT_RGB565: + key = RGB16_TO_COLORKEY(key); + flags |= CLK_RGB16_MASK; + break; + default: flags |= CLK_RGB24_MASK; break; } @@ -756,8 +811,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, if (ret != 0) return ret; - vma = i915_gem_object_pin_to_display_plane(new_bo, 0, - &i915_ggtt_view_normal); + vma = i915_gem_object_pin_to_display_plane(new_bo, 0, NULL); if (IS_ERR(vma)) return PTR_ERR(vma); @@ -836,18 +890,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, intel_overlay_unmap_regs(overlay, regs); - ret = intel_overlay_continue(overlay, scale_changed); + ret = intel_overlay_continue(overlay, vma, scale_changed); if (ret) goto out_unpin; - i915_gem_track_fb(overlay->vma ? overlay->vma->obj : NULL, - vma->obj, INTEL_FRONTBUFFER_OVERLAY(pipe)); - - overlay->old_vma = overlay->vma; - overlay->vma = vma; - - intel_frontbuffer_flip(dev_priv, INTEL_FRONTBUFFER_OVERLAY(pipe)); - return 0; out_unpin: @@ -921,12 +967,13 @@ static void update_pfit_vscale_ratio(struct intel_overlay *overlay) static int check_overlay_dst(struct intel_overlay *overlay, struct drm_intel_overlay_put_image *rec) { - struct drm_display_mode *mode = &overlay->crtc->base.mode; + const struct intel_crtc_state *pipe_config = + overlay->crtc->config; - if (rec->dst_x < mode->hdisplay && - rec->dst_x + rec->dst_width <= mode->hdisplay && - rec->dst_y < mode->vdisplay && - rec->dst_y + rec->dst_height <= mode->vdisplay) + if (rec->dst_x < pipe_config->pipe_src_w && + rec->dst_x + rec->dst_width <= pipe_config->pipe_src_w && + rec->dst_y < pipe_config->pipe_src_h && + rec->dst_y + rec->dst_height <= pipe_config->pipe_src_h) return 0; else return -EINVAL; @@ -958,7 +1005,7 @@ static int check_overlay_src(struct drm_i915_private *dev_priv, u32 tmp; /* check src dimensions */ - if (IS_845G(dev_priv) || IS_I830(dev_priv)) { + if (IS_I845G(dev_priv) || IS_I830(dev_priv)) { if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY || rec->src_width > IMAGE_MAX_WIDTH_LEGACY) return -EINVAL; @@ -1010,7 +1057,7 @@ static int check_overlay_src(struct drm_i915_private *dev_priv, return -EINVAL; /* stride checking */ - if (IS_I830(dev_priv) || IS_845G(dev_priv)) + if (IS_I830(dev_priv) || IS_I845G(dev_priv)) stride_mask = 255; else stride_mask = 63; @@ -1058,33 +1105,6 @@ static int check_overlay_src(struct drm_i915_private *dev_priv, return 0; } -/** - * Return the pipe currently connected to the panel fitter, - * or -1 if the panel fitter is not present or not in use - */ -static int intel_panel_fitter_pipe(struct drm_i915_private *dev_priv) -{ - u32 pfit_control; - - /* i830 doesn't have a panel fitter */ - if (INTEL_GEN(dev_priv) <= 3 && - (IS_I830(dev_priv) || !IS_MOBILE(dev_priv))) - return -1; - - pfit_control = I915_READ(PFIT_CONTROL); - - /* See if the panel fitter is in use */ - if ((pfit_control & PFIT_ENABLE) == 0) - return -1; - - /* 965 can place panel fitter on either pipe */ - if (IS_GEN4(dev_priv)) - return (pfit_control >> 29) & 0x3; - - /* older chips can only use pipe 1 */ - return 1; -} - int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -1146,7 +1166,6 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, goto out_unlock; if (overlay->crtc != crtc) { - struct drm_display_mode *mode = &crtc->base.mode; ret = intel_overlay_switch_off(overlay); if (ret != 0) goto out_unlock; @@ -1159,8 +1178,8 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, crtc->overlay = overlay; /* line too wide, i.e. one-line-mode */ - if (mode->hdisplay > 1024 && - intel_panel_fitter_pipe(dev_priv) == crtc->pipe) { + if (crtc->config->pipe_src_w > 1024 && + crtc->config->gmch_pfit.control & PFIT_ENABLE) { overlay->pfit_active = true; update_pfit_vscale_ratio(overlay); } else @@ -1215,6 +1234,7 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, mutex_unlock(&dev->struct_mutex); drm_modeset_unlock_all(dev); + i915_gem_object_put(new_bo); kfree(params); @@ -1392,10 +1412,9 @@ void intel_setup_overlay(struct drm_i915_private *dev_priv) reg_bo = NULL; if (!OVERLAY_NEEDS_PHYSICAL(dev_priv)) - reg_bo = i915_gem_object_create_stolen(&dev_priv->drm, - PAGE_SIZE); + reg_bo = i915_gem_object_create_stolen(dev_priv, PAGE_SIZE); if (reg_bo == NULL) - reg_bo = i915_gem_object_create(&dev_priv->drm, PAGE_SIZE); + reg_bo = i915_gem_object_create(dev_priv, PAGE_SIZE); if (IS_ERR(reg_bo)) goto out_free; overlay->reg_bo = reg_bo; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 08ab6d762ca4..1a6ff26dea20 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -48,7 +48,7 @@ intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, /** * intel_find_panel_downclock - find the reduced downclock for LVDS in EDID - * @dev: drm device + * @dev_priv: i915 device instance * @fixed_mode : panel native mode * @connector: LVDS/eDP connector * @@ -56,7 +56,7 @@ intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, * Find the reduced downclock for LVDS/eDP in EDID. */ struct drm_display_mode * -intel_find_panel_downclock(struct drm_device *dev, +intel_find_panel_downclock(struct drm_i915_private *dev_priv, struct drm_display_mode *fixed_mode, struct drm_connector *connector) { @@ -94,7 +94,7 @@ intel_find_panel_downclock(struct drm_device *dev, } if (temp_downclock < fixed_mode->clock) - return drm_mode_duplicate(dev, tmp_mode); + return drm_mode_duplicate(&dev_priv->drm, tmp_mode); else return NULL; } @@ -375,10 +375,8 @@ out: } enum drm_connector_status -intel_panel_detect(struct drm_device *dev) +intel_panel_detect(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - /* Assume that the BIOS does not lie through the OpRegion... */ if (!i915.panel_ignore_lid && dev_priv->opregion.lid_state) { return *dev_priv->opregion.lid_state & 0x1 ? @@ -1039,10 +1037,7 @@ static void bxt_enable_backlight(struct intel_connector *connector) enum pipe pipe = intel_get_pipe_from_connector(connector); u32 pwm_ctl, val; - /* To use 2nd set of backlight registers, utility pin has to be - * enabled with PWM mode. - * The field should only be changed when the utility pin is disabled - */ + /* Controller 1 uses the utility pin. */ if (panel->backlight.controller == 1) { val = I915_READ(UTIL_PIN_CTL); if (val & UTIL_PIN_ENABLE) { @@ -1332,8 +1327,7 @@ static u32 i9xx_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) */ static u32 i965_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz) { - struct drm_device *dev = connector->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); int clock; if (IS_G4X(dev_priv)) @@ -1608,19 +1602,11 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused) struct intel_panel *panel = &connector->panel; u32 pwm_ctl, val; - /* - * For BXT hard coding the Backlight controller to 0. - * TODO : Read the controller value from VBT and generalize - */ - panel->backlight.controller = 0; + panel->backlight.controller = dev_priv->vbt.backlight.controller; pwm_ctl = I915_READ(BXT_BLC_PWM_CTL(panel->backlight.controller)); - /* Keeping the check if controller 1 is to be programmed. - * This will come into affect once the VBT parsing - * is fixed for controller selection, and controller 1 is used - * for a prticular display configuration. - */ + /* Controller 1 uses the utility pin. */ if (panel->backlight.controller == 1) { val = I915_READ(UTIL_PIN_CTL); panel->backlight.util_pin_active_low = @@ -1756,7 +1742,7 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel) intel_dsi_dcs_init_backlight_funcs(connector) == 0) return; - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { panel->backlight.setup = bxt_setup_backlight; panel->backlight.enable = bxt_enable_backlight; panel->backlight.disable = bxt_disable_backlight; diff --git a/drivers/gpu/drm/i915/intel_pipe_crc.c b/drivers/gpu/drm/i915/intel_pipe_crc.c new file mode 100644 index 000000000000..c0b1f99da37b --- /dev/null +++ b/drivers/gpu/drm/i915/intel_pipe_crc.c @@ -0,0 +1,1011 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Author: Damien Lespiau <damien.lespiau@intel.com> + * + */ + +#include <linux/seq_file.h> +#include <linux/circ_buf.h> +#include <linux/ctype.h> +#include <linux/debugfs.h> +#include "intel_drv.h" + +struct pipe_crc_info { + const char *name; + struct drm_i915_private *dev_priv; + enum pipe pipe; +}; + +/* As the drm_debugfs_init() routines are called before dev->dev_private is + * allocated we need to hook into the minor for release. + */ +static int drm_add_fake_info_node(struct drm_minor *minor, + struct dentry *ent, const void *key) +{ + struct drm_info_node *node; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (node == NULL) { + debugfs_remove(ent); + return -ENOMEM; + } + + node->minor = minor; + node->dent = ent; + node->info_ent = (void *) key; + + mutex_lock(&minor->debugfs_lock); + list_add(&node->list, &minor->debugfs_list); + mutex_unlock(&minor->debugfs_lock); + + return 0; +} + +static int i915_pipe_crc_open(struct inode *inode, struct file *filep) +{ + struct pipe_crc_info *info = inode->i_private; + struct drm_i915_private *dev_priv = info->dev_priv; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; + + if (info->pipe >= INTEL_INFO(dev_priv)->num_pipes) + return -ENODEV; + + spin_lock_irq(&pipe_crc->lock); + + if (pipe_crc->opened) { + spin_unlock_irq(&pipe_crc->lock); + return -EBUSY; /* already open */ + } + + pipe_crc->opened = true; + filep->private_data = inode->i_private; + + spin_unlock_irq(&pipe_crc->lock); + + return 0; +} + +static int i915_pipe_crc_release(struct inode *inode, struct file *filep) +{ + struct pipe_crc_info *info = inode->i_private; + struct drm_i915_private *dev_priv = info->dev_priv; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; + + spin_lock_irq(&pipe_crc->lock); + pipe_crc->opened = false; + spin_unlock_irq(&pipe_crc->lock); + + return 0; +} + +/* (6 fields, 8 chars each, space separated (5) + '\n') */ +#define PIPE_CRC_LINE_LEN (6 * 8 + 5 + 1) +/* account for \'0' */ +#define PIPE_CRC_BUFFER_LEN (PIPE_CRC_LINE_LEN + 1) + +static int pipe_crc_data_count(struct intel_pipe_crc *pipe_crc) +{ + assert_spin_locked(&pipe_crc->lock); + return CIRC_CNT(pipe_crc->head, pipe_crc->tail, + INTEL_PIPE_CRC_ENTRIES_NR); +} + +static ssize_t +i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count, + loff_t *pos) +{ + struct pipe_crc_info *info = filep->private_data; + struct drm_i915_private *dev_priv = info->dev_priv; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe]; + char buf[PIPE_CRC_BUFFER_LEN]; + int n_entries; + ssize_t bytes_read; + + /* + * Don't allow user space to provide buffers not big enough to hold + * a line of data. + */ + if (count < PIPE_CRC_LINE_LEN) + return -EINVAL; + + if (pipe_crc->source == INTEL_PIPE_CRC_SOURCE_NONE) + return 0; + + /* nothing to read */ + spin_lock_irq(&pipe_crc->lock); + while (pipe_crc_data_count(pipe_crc) == 0) { + int ret; + + if (filep->f_flags & O_NONBLOCK) { + spin_unlock_irq(&pipe_crc->lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_lock_irq(pipe_crc->wq, + pipe_crc_data_count(pipe_crc), pipe_crc->lock); + if (ret) { + spin_unlock_irq(&pipe_crc->lock); + return ret; + } + } + + /* We now have one or more entries to read */ + n_entries = count / PIPE_CRC_LINE_LEN; + + bytes_read = 0; + while (n_entries > 0) { + struct intel_pipe_crc_entry *entry = + &pipe_crc->entries[pipe_crc->tail]; + + if (CIRC_CNT(pipe_crc->head, pipe_crc->tail, + INTEL_PIPE_CRC_ENTRIES_NR) < 1) + break; + + BUILD_BUG_ON_NOT_POWER_OF_2(INTEL_PIPE_CRC_ENTRIES_NR); + pipe_crc->tail = (pipe_crc->tail + 1) & + (INTEL_PIPE_CRC_ENTRIES_NR - 1); + + bytes_read += snprintf(buf, PIPE_CRC_BUFFER_LEN, + "%8u %8x %8x %8x %8x %8x\n", + entry->frame, entry->crc[0], + entry->crc[1], entry->crc[2], + entry->crc[3], entry->crc[4]); + + spin_unlock_irq(&pipe_crc->lock); + + if (copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN)) + return -EFAULT; + + user_buf += PIPE_CRC_LINE_LEN; + n_entries--; + + spin_lock_irq(&pipe_crc->lock); + } + + spin_unlock_irq(&pipe_crc->lock); + + return bytes_read; +} + +static const struct file_operations i915_pipe_crc_fops = { + .owner = THIS_MODULE, + .open = i915_pipe_crc_open, + .read = i915_pipe_crc_read, + .release = i915_pipe_crc_release, +}; + +static struct pipe_crc_info i915_pipe_crc_data[I915_MAX_PIPES] = { + { + .name = "i915_pipe_A_crc", + .pipe = PIPE_A, + }, + { + .name = "i915_pipe_B_crc", + .pipe = PIPE_B, + }, + { + .name = "i915_pipe_C_crc", + .pipe = PIPE_C, + }, +}; + +static int i915_pipe_crc_create(struct dentry *root, struct drm_minor *minor, + enum pipe pipe) +{ + struct drm_i915_private *dev_priv = to_i915(minor->dev); + struct dentry *ent; + struct pipe_crc_info *info = &i915_pipe_crc_data[pipe]; + + info->dev_priv = dev_priv; + ent = debugfs_create_file(info->name, S_IRUGO, root, info, + &i915_pipe_crc_fops); + if (!ent) + return -ENOMEM; + + return drm_add_fake_info_node(minor, ent, info); +} + +static const char * const pipe_crc_sources[] = { + "none", + "plane1", + "plane2", + "pf", + "pipe", + "TV", + "DP-B", + "DP-C", + "DP-D", + "auto", +}; + +static const char *pipe_crc_source_name(enum intel_pipe_crc_source source) +{ + BUILD_BUG_ON(ARRAY_SIZE(pipe_crc_sources) != INTEL_PIPE_CRC_SOURCE_MAX); + return pipe_crc_sources[source]; +} + +static int display_crc_ctl_show(struct seq_file *m, void *data) +{ + struct drm_i915_private *dev_priv = m->private; + int i; + + for (i = 0; i < I915_MAX_PIPES; i++) + seq_printf(m, "%c %s\n", pipe_name(i), + pipe_crc_source_name(dev_priv->pipe_crc[i].source)); + + return 0; +} + +static int display_crc_ctl_open(struct inode *inode, struct file *file) +{ + return single_open(file, display_crc_ctl_show, inode->i_private); +} + +static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, + uint32_t *val) +{ + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) + *source = INTEL_PIPE_CRC_SOURCE_PIPE; + + switch (*source) { + case INTEL_PIPE_CRC_SOURCE_PIPE: + *val = PIPE_CRC_ENABLE | PIPE_CRC_INCLUDE_BORDER_I8XX; + break; + case INTEL_PIPE_CRC_SOURCE_NONE: + *val = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int i9xx_pipe_crc_auto_source(struct drm_i915_private *dev_priv, + enum pipe pipe, + enum intel_pipe_crc_source *source) +{ + struct drm_device *dev = &dev_priv->drm; + struct intel_encoder *encoder; + struct intel_crtc *crtc; + struct intel_digital_port *dig_port; + int ret = 0; + + *source = INTEL_PIPE_CRC_SOURCE_PIPE; + + drm_modeset_lock_all(dev); + for_each_intel_encoder(dev, encoder) { + if (!encoder->base.crtc) + continue; + + crtc = to_intel_crtc(encoder->base.crtc); + + if (crtc->pipe != pipe) + continue; + + switch (encoder->type) { + case INTEL_OUTPUT_TVOUT: + *source = INTEL_PIPE_CRC_SOURCE_TV; + break; + case INTEL_OUTPUT_DP: + case INTEL_OUTPUT_EDP: + dig_port = enc_to_dig_port(&encoder->base); + switch (dig_port->port) { + case PORT_B: + *source = INTEL_PIPE_CRC_SOURCE_DP_B; + break; + case PORT_C: + *source = INTEL_PIPE_CRC_SOURCE_DP_C; + break; + case PORT_D: + *source = INTEL_PIPE_CRC_SOURCE_DP_D; + break; + default: + WARN(1, "nonexisting DP port %c\n", + port_name(dig_port->port)); + break; + } + break; + default: + break; + } + } + drm_modeset_unlock_all(dev); + + return ret; +} + +static int vlv_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, + enum pipe pipe, + enum intel_pipe_crc_source *source, + uint32_t *val) +{ + bool need_stable_symbols = false; + + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) { + int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source); + if (ret) + return ret; + } + + switch (*source) { + case INTEL_PIPE_CRC_SOURCE_PIPE: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_VLV; + break; + case INTEL_PIPE_CRC_SOURCE_DP_B: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_VLV; + need_stable_symbols = true; + break; + case INTEL_PIPE_CRC_SOURCE_DP_C: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_VLV; + need_stable_symbols = true; + break; + case INTEL_PIPE_CRC_SOURCE_DP_D: + if (!IS_CHERRYVIEW(dev_priv)) + return -EINVAL; + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_VLV; + need_stable_symbols = true; + break; + case INTEL_PIPE_CRC_SOURCE_NONE: + *val = 0; + break; + default: + return -EINVAL; + } + + /* + * When the pipe CRC tap point is after the transcoders we need + * to tweak symbol-level features to produce a deterministic series of + * symbols for a given frame. We need to reset those features only once + * a frame (instead of every nth symbol): + * - DC-balance: used to ensure a better clock recovery from the data + * link (SDVO) + * - DisplayPort scrambling: used for EMI reduction + */ + if (need_stable_symbols) { + uint32_t tmp = I915_READ(PORT_DFT2_G4X); + + tmp |= DC_BALANCE_RESET_VLV; + switch (pipe) { + case PIPE_A: + tmp |= PIPE_A_SCRAMBLE_RESET; + break; + case PIPE_B: + tmp |= PIPE_B_SCRAMBLE_RESET; + break; + case PIPE_C: + tmp |= PIPE_C_SCRAMBLE_RESET; + break; + default: + return -EINVAL; + } + I915_WRITE(PORT_DFT2_G4X, tmp); + } + + return 0; +} + +static int i9xx_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, + enum pipe pipe, + enum intel_pipe_crc_source *source, + uint32_t *val) +{ + bool need_stable_symbols = false; + + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) { + int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source); + if (ret) + return ret; + } + + switch (*source) { + case INTEL_PIPE_CRC_SOURCE_PIPE: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_I9XX; + break; + case INTEL_PIPE_CRC_SOURCE_TV: + if (!SUPPORTS_TV(dev_priv)) + return -EINVAL; + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_TV_PRE; + break; + case INTEL_PIPE_CRC_SOURCE_DP_B: + if (!IS_G4X(dev_priv)) + return -EINVAL; + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_G4X; + need_stable_symbols = true; + break; + case INTEL_PIPE_CRC_SOURCE_DP_C: + if (!IS_G4X(dev_priv)) + return -EINVAL; + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_G4X; + need_stable_symbols = true; + break; + case INTEL_PIPE_CRC_SOURCE_DP_D: + if (!IS_G4X(dev_priv)) + return -EINVAL; + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_G4X; + need_stable_symbols = true; + break; + case INTEL_PIPE_CRC_SOURCE_NONE: + *val = 0; + break; + default: + return -EINVAL; + } + + /* + * When the pipe CRC tap point is after the transcoders we need + * to tweak symbol-level features to produce a deterministic series of + * symbols for a given frame. We need to reset those features only once + * a frame (instead of every nth symbol): + * - DC-balance: used to ensure a better clock recovery from the data + * link (SDVO) + * - DisplayPort scrambling: used for EMI reduction + */ + if (need_stable_symbols) { + uint32_t tmp = I915_READ(PORT_DFT2_G4X); + + WARN_ON(!IS_G4X(dev_priv)); + + I915_WRITE(PORT_DFT_I9XX, + I915_READ(PORT_DFT_I9XX) | DC_BALANCE_RESET); + + if (pipe == PIPE_A) + tmp |= PIPE_A_SCRAMBLE_RESET; + else + tmp |= PIPE_B_SCRAMBLE_RESET; + + I915_WRITE(PORT_DFT2_G4X, tmp); + } + + return 0; +} + +static void vlv_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + uint32_t tmp = I915_READ(PORT_DFT2_G4X); + + switch (pipe) { + case PIPE_A: + tmp &= ~PIPE_A_SCRAMBLE_RESET; + break; + case PIPE_B: + tmp &= ~PIPE_B_SCRAMBLE_RESET; + break; + case PIPE_C: + tmp &= ~PIPE_C_SCRAMBLE_RESET; + break; + default: + return; + } + if (!(tmp & PIPE_SCRAMBLE_RESET_MASK)) + tmp &= ~DC_BALANCE_RESET_VLV; + I915_WRITE(PORT_DFT2_G4X, tmp); + +} + +static void g4x_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + uint32_t tmp = I915_READ(PORT_DFT2_G4X); + + if (pipe == PIPE_A) + tmp &= ~PIPE_A_SCRAMBLE_RESET; + else + tmp &= ~PIPE_B_SCRAMBLE_RESET; + I915_WRITE(PORT_DFT2_G4X, tmp); + + if (!(tmp & PIPE_SCRAMBLE_RESET_MASK)) { + I915_WRITE(PORT_DFT_I9XX, + I915_READ(PORT_DFT_I9XX) & ~DC_BALANCE_RESET); + } +} + +static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source, + uint32_t *val) +{ + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) + *source = INTEL_PIPE_CRC_SOURCE_PIPE; + + switch (*source) { + case INTEL_PIPE_CRC_SOURCE_PLANE1: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_ILK; + break; + case INTEL_PIPE_CRC_SOURCE_PLANE2: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_ILK; + break; + case INTEL_PIPE_CRC_SOURCE_PIPE: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_ILK; + break; + case INTEL_PIPE_CRC_SOURCE_NONE: + *val = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv, + bool enable) +{ + struct drm_device *dev = &dev_priv->drm; + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_A); + struct intel_crtc_state *pipe_config; + struct drm_atomic_state *state; + int ret = 0; + + drm_modeset_lock_all(dev); + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto unlock; + } + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(&crtc->base); + pipe_config = intel_atomic_get_crtc_state(state, crtc); + if (IS_ERR(pipe_config)) { + ret = PTR_ERR(pipe_config); + goto put_state; + } + + pipe_config->pch_pfit.force_thru = enable; + if (pipe_config->cpu_transcoder == TRANSCODER_EDP && + pipe_config->pch_pfit.enabled != enable) + pipe_config->base.connectors_changed = true; + + ret = drm_atomic_commit(state); + +put_state: + drm_atomic_state_put(state); +unlock: + WARN(ret, "Toggling workaround to %i returns %i\n", enable, ret); + drm_modeset_unlock_all(dev); +} + +static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv, + enum pipe pipe, + enum intel_pipe_crc_source *source, + uint32_t *val) +{ + if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) + *source = INTEL_PIPE_CRC_SOURCE_PF; + + switch (*source) { + case INTEL_PIPE_CRC_SOURCE_PLANE1: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PRIMARY_IVB; + break; + case INTEL_PIPE_CRC_SOURCE_PLANE2: + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB; + break; + case INTEL_PIPE_CRC_SOURCE_PF: + if (IS_HASWELL(dev_priv) && pipe == PIPE_A) + hsw_trans_edp_pipe_A_crc_wa(dev_priv, true); + + *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB; + break; + case INTEL_PIPE_CRC_SOURCE_NONE: + *val = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int get_new_crc_ctl_reg(struct drm_i915_private *dev_priv, + enum pipe pipe, + enum intel_pipe_crc_source *source, u32 *val) +{ + if (IS_GEN2(dev_priv)) + return i8xx_pipe_crc_ctl_reg(source, val); + else if (INTEL_GEN(dev_priv) < 5) + return i9xx_pipe_crc_ctl_reg(dev_priv, pipe, source, val); + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + return vlv_pipe_crc_ctl_reg(dev_priv, pipe, source, val); + else if (IS_GEN5(dev_priv) || IS_GEN6(dev_priv)) + return ilk_pipe_crc_ctl_reg(source, val); + else + return ivb_pipe_crc_ctl_reg(dev_priv, pipe, source, val); +} + +static int pipe_crc_set_source(struct drm_i915_private *dev_priv, + enum pipe pipe, + enum intel_pipe_crc_source source) +{ + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + enum intel_display_power_domain power_domain; + u32 val = 0; /* shut up gcc */ + int ret; + + if (pipe_crc->source == source) + return 0; + + /* forbid changing the source without going back to 'none' */ + if (pipe_crc->source && source) + return -EINVAL; + + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) { + DRM_DEBUG_KMS("Trying to capture CRC while pipe is off\n"); + return -EIO; + } + + ret = get_new_crc_ctl_reg(dev_priv, pipe, &source, &val); + if (ret != 0) + goto out; + + /* none -> real source transition */ + if (source) { + struct intel_pipe_crc_entry *entries; + + DRM_DEBUG_DRIVER("collecting CRCs for pipe %c, %s\n", + pipe_name(pipe), pipe_crc_source_name(source)); + + entries = kcalloc(INTEL_PIPE_CRC_ENTRIES_NR, + sizeof(pipe_crc->entries[0]), + GFP_KERNEL); + if (!entries) { + ret = -ENOMEM; + goto out; + } + + /* + * When IPS gets enabled, the pipe CRC changes. Since IPS gets + * enabled and disabled dynamically based on package C states, + * user space can't make reliable use of the CRCs, so let's just + * completely disable it. + */ + hsw_disable_ips(crtc); + + spin_lock_irq(&pipe_crc->lock); + kfree(pipe_crc->entries); + pipe_crc->entries = entries; + pipe_crc->head = 0; + pipe_crc->tail = 0; + spin_unlock_irq(&pipe_crc->lock); + } + + pipe_crc->source = source; + + I915_WRITE(PIPE_CRC_CTL(pipe), val); + POSTING_READ(PIPE_CRC_CTL(pipe)); + + /* real source -> none transition */ + if (!source) { + struct intel_pipe_crc_entry *entries; + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, + pipe); + + DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n", + pipe_name(pipe)); + + drm_modeset_lock(&crtc->base.mutex, NULL); + if (crtc->base.state->active) + intel_wait_for_vblank(dev_priv, pipe); + drm_modeset_unlock(&crtc->base.mutex); + + spin_lock_irq(&pipe_crc->lock); + entries = pipe_crc->entries; + pipe_crc->entries = NULL; + pipe_crc->head = 0; + pipe_crc->tail = 0; + spin_unlock_irq(&pipe_crc->lock); + + kfree(entries); + + if (IS_G4X(dev_priv)) + g4x_undo_pipe_scramble_reset(dev_priv, pipe); + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + vlv_undo_pipe_scramble_reset(dev_priv, pipe); + else if (IS_HASWELL(dev_priv) && pipe == PIPE_A) + hsw_trans_edp_pipe_A_crc_wa(dev_priv, false); + + hsw_enable_ips(crtc); + } + + ret = 0; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; +} + +/* + * Parse pipe CRC command strings: + * command: wsp* object wsp+ name wsp+ source wsp* + * object: 'pipe' + * name: (A | B | C) + * source: (none | plane1 | plane2 | pf) + * wsp: (#0x20 | #0x9 | #0xA)+ + * + * eg.: + * "pipe A plane1" -> Start CRC computations on plane1 of pipe A + * "pipe A none" -> Stop CRC + */ +static int display_crc_ctl_tokenize(char *buf, char *words[], int max_words) +{ + int n_words = 0; + + while (*buf) { + char *end; + + /* skip leading white space */ + buf = skip_spaces(buf); + if (!*buf) + break; /* end of buffer */ + + /* find end of word */ + for (end = buf; *end && !isspace(*end); end++) + ; + + if (n_words == max_words) { + DRM_DEBUG_DRIVER("too many words, allowed <= %d\n", + max_words); + return -EINVAL; /* ran out of words[] before bytes */ + } + + if (*end) + *end++ = '\0'; + words[n_words++] = buf; + buf = end; + } + + return n_words; +} + +enum intel_pipe_crc_object { + PIPE_CRC_OBJECT_PIPE, +}; + +static const char * const pipe_crc_objects[] = { + "pipe", +}; + +static int +display_crc_ctl_parse_object(const char *buf, enum intel_pipe_crc_object *o) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pipe_crc_objects); i++) + if (!strcmp(buf, pipe_crc_objects[i])) { + *o = i; + return 0; + } + + return -EINVAL; +} + +static int display_crc_ctl_parse_pipe(const char *buf, enum pipe *pipe) +{ + const char name = buf[0]; + + if (name < 'A' || name >= pipe_name(I915_MAX_PIPES)) + return -EINVAL; + + *pipe = name - 'A'; + + return 0; +} + +static int +display_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *s) +{ + int i; + + if (!buf) { + *s = INTEL_PIPE_CRC_SOURCE_NONE; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(pipe_crc_sources); i++) + if (!strcmp(buf, pipe_crc_sources[i])) { + *s = i; + return 0; + } + + return -EINVAL; +} + +static int display_crc_ctl_parse(struct drm_i915_private *dev_priv, + char *buf, size_t len) +{ +#define N_WORDS 3 + int n_words; + char *words[N_WORDS]; + enum pipe pipe; + enum intel_pipe_crc_object object; + enum intel_pipe_crc_source source; + + n_words = display_crc_ctl_tokenize(buf, words, N_WORDS); + if (n_words != N_WORDS) { + DRM_DEBUG_DRIVER("tokenize failed, a command is %d words\n", + N_WORDS); + return -EINVAL; + } + + if (display_crc_ctl_parse_object(words[0], &object) < 0) { + DRM_DEBUG_DRIVER("unknown object %s\n", words[0]); + return -EINVAL; + } + + if (display_crc_ctl_parse_pipe(words[1], &pipe) < 0) { + DRM_DEBUG_DRIVER("unknown pipe %s\n", words[1]); + return -EINVAL; + } + + if (display_crc_ctl_parse_source(words[2], &source) < 0) { + DRM_DEBUG_DRIVER("unknown source %s\n", words[2]); + return -EINVAL; + } + + return pipe_crc_set_source(dev_priv, pipe, source); +} + +static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_i915_private *dev_priv = m->private; + char *tmpbuf; + int ret; + + if (len == 0) + return 0; + + if (len > PAGE_SIZE - 1) { + DRM_DEBUG_DRIVER("expected <%lu bytes into pipe crc control\n", + PAGE_SIZE); + return -E2BIG; + } + + tmpbuf = kmalloc(len + 1, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + + if (copy_from_user(tmpbuf, ubuf, len)) { + ret = -EFAULT; + goto out; + } + tmpbuf[len] = '\0'; + + ret = display_crc_ctl_parse(dev_priv, tmpbuf, len); + +out: + kfree(tmpbuf); + if (ret < 0) + return ret; + + *offp += len; + return len; +} + +const struct file_operations i915_display_crc_ctl_fops = { + .owner = THIS_MODULE, + .open = display_crc_ctl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = display_crc_ctl_write +}; + +void intel_display_crc_init(struct drm_i915_private *dev_priv) +{ + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) { + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; + + pipe_crc->opened = false; + spin_lock_init(&pipe_crc->lock); + init_waitqueue_head(&pipe_crc->wq); + } +} + +int intel_pipe_crc_create(struct drm_minor *minor) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) { + ret = i915_pipe_crc_create(minor->debugfs_root, minor, i); + if (ret) + return ret; + } + + return 0; +} + +void intel_pipe_crc_cleanup(struct drm_minor *minor) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) { + struct drm_info_list *info_list = + (struct drm_info_list *)&i915_pipe_crc_data[i]; + + drm_debugfs_remove_files(info_list, 1, minor); + } +} + +int intel_crtc_set_crc_source(struct drm_crtc *crtc, const char *source_name, + size_t *values_cnt) +{ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[crtc->index]; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + enum intel_display_power_domain power_domain; + enum intel_pipe_crc_source source; + u32 val = 0; /* shut up gcc */ + int ret = 0; + + if (display_crc_ctl_parse_source(source_name, &source) < 0) { + DRM_DEBUG_DRIVER("unknown source %s\n", source_name); + return -EINVAL; + } + + power_domain = POWER_DOMAIN_PIPE(crtc->index); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) { + DRM_DEBUG_KMS("Trying to capture CRC while pipe is off\n"); + return -EIO; + } + + ret = get_new_crc_ctl_reg(dev_priv, crtc->index, &source, &val); + if (ret != 0) + goto out; + + if (source) { + /* + * When IPS gets enabled, the pipe CRC changes. Since IPS gets + * enabled and disabled dynamically based on package C states, + * user space can't make reliable use of the CRCs, so let's just + * completely disable it. + */ + hsw_disable_ips(intel_crtc); + } + + I915_WRITE(PIPE_CRC_CTL(crtc->index), val); + POSTING_READ(PIPE_CRC_CTL(crtc->index)); + + if (!source) { + if (IS_G4X(dev_priv)) + g4x_undo_pipe_scramble_reset(dev_priv, crtc->index); + else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) + vlv_undo_pipe_scramble_reset(dev_priv, crtc->index); + else if (IS_HASWELL(dev_priv) && crtc->index == PIPE_A) + hsw_trans_edp_pipe_A_crc_wa(dev_priv, false); + + hsw_enable_ips(intel_crtc); + } + + pipe_crc->skipped = 0; + *values_cnt = 5; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; +} diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ae2c0bb4b2e8..249623d45be0 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -312,23 +312,30 @@ static void chv_set_memory_pm5(struct drm_i915_private *dev_priv, bool enable) #define FW_WM(value, plane) \ (((value) << DSPFW_ ## plane ## _SHIFT) & DSPFW_ ## plane ## _MASK) -void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) +static bool _intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) { + bool was_enabled; u32 val; if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + was_enabled = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN; I915_WRITE(FW_BLC_SELF_VLV, enable ? FW_CSPWRDWNEN : 0); POSTING_READ(FW_BLC_SELF_VLV); - dev_priv->wm.vlv.cxsr = enable; - } else if (IS_G4X(dev_priv) || IS_CRESTLINE(dev_priv)) { + } else if (IS_G4X(dev_priv) || IS_I965GM(dev_priv)) { + was_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; I915_WRITE(FW_BLC_SELF, enable ? FW_BLC_SELF_EN : 0); POSTING_READ(FW_BLC_SELF); } else if (IS_PINEVIEW(dev_priv)) { - val = I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN; - val |= enable ? PINEVIEW_SELF_REFRESH_EN : 0; + val = I915_READ(DSPFW3); + was_enabled = val & PINEVIEW_SELF_REFRESH_EN; + if (enable) + val |= PINEVIEW_SELF_REFRESH_EN; + else + val &= ~PINEVIEW_SELF_REFRESH_EN; I915_WRITE(DSPFW3, val); POSTING_READ(DSPFW3); } else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv)) { + was_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN; val = enable ? _MASKED_BIT_ENABLE(FW_BLC_SELF_EN) : _MASKED_BIT_DISABLE(FW_BLC_SELF_EN); I915_WRITE(FW_BLC_SELF, val); @@ -339,17 +346,33 @@ void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) * and yet it does have the related watermark in * FW_BLC_SELF. What's going on? */ + was_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN; val = enable ? _MASKED_BIT_ENABLE(INSTPM_SELF_EN) : _MASKED_BIT_DISABLE(INSTPM_SELF_EN); I915_WRITE(INSTPM, val); POSTING_READ(INSTPM); } else { - return; + return false; } - DRM_DEBUG_KMS("memory self-refresh is %s\n", enableddisabled(enable)); + DRM_DEBUG_KMS("memory self-refresh is %s (was %s)\n", + enableddisabled(enable), + enableddisabled(was_enabled)); + + return was_enabled; } +bool intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) +{ + bool ret; + + mutex_lock(&dev_priv->wm.wm_mutex); + ret = _intel_set_memory_cxsr(dev_priv, enable); + dev_priv->wm.vlv.cxsr = enable; + mutex_unlock(&dev_priv->wm.wm_mutex); + + return ret; +} /* * Latency for FIFO fetches is dependent on several factors: @@ -370,12 +393,15 @@ static const int pessimal_latency_ns = 5000; #define VLV_FIFO_START(dsparb, dsparb2, lo_shift, hi_shift) \ ((((dsparb) >> (lo_shift)) & 0xff) | ((((dsparb2) >> (hi_shift)) & 0x1) << 8)) -static int vlv_get_fifo_size(struct drm_i915_private *dev_priv, - enum pipe pipe, int plane) +static int vlv_get_fifo_size(struct intel_plane *plane) { + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); int sprite0_start, sprite1_start, size; - switch (pipe) { + if (plane->id == PLANE_CURSOR) + return 63; + + switch (plane->pipe) { uint32_t dsparb, dsparb2, dsparb3; case PIPE_A: dsparb = I915_READ(DSPARB); @@ -399,24 +425,21 @@ static int vlv_get_fifo_size(struct drm_i915_private *dev_priv, return 0; } - switch (plane) { - case 0: + switch (plane->id) { + case PLANE_PRIMARY: size = sprite0_start; break; - case 1: + case PLANE_SPRITE0: size = sprite1_start - sprite0_start; break; - case 2: + case PLANE_SPRITE1: size = 512 - 1 - sprite1_start; break; default: return 0; } - DRM_DEBUG_KMS("Pipe %c %s %c FIFO size: %d\n", - pipe_name(pipe), plane == 0 ? "primary" : "sprite", - plane == 0 ? plane_name(pipe) : sprite_name(pipe, plane - 1), - size); + DRM_DEBUG_KMS("%s FIFO size: %d\n", plane->base.name, size); return size; } @@ -652,7 +675,7 @@ static void pineview_update_wm(struct intel_crtc *unused_crtc) &crtc->config->base.adjusted_mode; const struct drm_framebuffer *fb = crtc->base.primary->state->fb; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + int cpp = fb->format->cpp[0]; int clock = adjusted_mode->crtc_clock; /* Display SR */ @@ -727,7 +750,7 @@ static bool g4x_compute_wm0(struct drm_i915_private *dev_priv, clock = adjusted_mode->crtc_clock; htotal = adjusted_mode->crtc_htotal; hdisplay = crtc->config->pipe_src_w; - cpp = drm_format_plane_cpp(fb->pixel_format, 0); + cpp = fb->format->cpp[0]; /* Use the small buffer method to calculate plane watermark */ entries = ((clock * cpp / 1000) * display_latency_ns) / 1000; @@ -816,7 +839,7 @@ static bool g4x_compute_srwm(struct drm_i915_private *dev_priv, clock = adjusted_mode->crtc_clock; htotal = adjusted_mode->crtc_htotal; hdisplay = crtc->config->pipe_src_w; - cpp = drm_format_plane_cpp(fb->pixel_format, 0); + cpp = fb->format->cpp[0]; line_time_us = max(htotal * 1000 / clock, 1); line_count = (latency_ns / line_time_us + 1000) / 1000; @@ -842,71 +865,77 @@ static bool g4x_compute_srwm(struct drm_i915_private *dev_priv, #define FW_WM_VLV(value, plane) \ (((value) << DSPFW_ ## plane ## _SHIFT) & DSPFW_ ## plane ## _MASK_VLV) -static void vlv_write_wm_values(struct intel_crtc *crtc, +static void vlv_write_wm_values(struct drm_i915_private *dev_priv, const struct vlv_wm_values *wm) { - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum pipe pipe = crtc->pipe; + enum pipe pipe; - I915_WRITE(VLV_DDL(pipe), - (wm->ddl[pipe].cursor << DDL_CURSOR_SHIFT) | - (wm->ddl[pipe].sprite[1] << DDL_SPRITE_SHIFT(1)) | - (wm->ddl[pipe].sprite[0] << DDL_SPRITE_SHIFT(0)) | - (wm->ddl[pipe].primary << DDL_PLANE_SHIFT)); + for_each_pipe(dev_priv, pipe) { + I915_WRITE(VLV_DDL(pipe), + (wm->ddl[pipe].plane[PLANE_CURSOR] << DDL_CURSOR_SHIFT) | + (wm->ddl[pipe].plane[PLANE_SPRITE1] << DDL_SPRITE_SHIFT(1)) | + (wm->ddl[pipe].plane[PLANE_SPRITE0] << DDL_SPRITE_SHIFT(0)) | + (wm->ddl[pipe].plane[PLANE_PRIMARY] << DDL_PLANE_SHIFT)); + } + + /* + * Zero the (unused) WM1 watermarks, and also clear all the + * high order bits so that there are no out of bounds values + * present in the registers during the reprogramming. + */ + I915_WRITE(DSPHOWM, 0); + I915_WRITE(DSPHOWM1, 0); + I915_WRITE(DSPFW4, 0); + I915_WRITE(DSPFW5, 0); + I915_WRITE(DSPFW6, 0); I915_WRITE(DSPFW1, FW_WM(wm->sr.plane, SR) | - FW_WM(wm->pipe[PIPE_B].cursor, CURSORB) | - FW_WM_VLV(wm->pipe[PIPE_B].primary, PLANEB) | - FW_WM_VLV(wm->pipe[PIPE_A].primary, PLANEA)); + FW_WM(wm->pipe[PIPE_B].plane[PLANE_CURSOR], CURSORB) | + FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_PRIMARY], PLANEB) | + FW_WM_VLV(wm->pipe[PIPE_A].plane[PLANE_PRIMARY], PLANEA)); I915_WRITE(DSPFW2, - FW_WM_VLV(wm->pipe[PIPE_A].sprite[1], SPRITEB) | - FW_WM(wm->pipe[PIPE_A].cursor, CURSORA) | - FW_WM_VLV(wm->pipe[PIPE_A].sprite[0], SPRITEA)); + FW_WM_VLV(wm->pipe[PIPE_A].plane[PLANE_SPRITE1], SPRITEB) | + FW_WM(wm->pipe[PIPE_A].plane[PLANE_CURSOR], CURSORA) | + FW_WM_VLV(wm->pipe[PIPE_A].plane[PLANE_SPRITE0], SPRITEA)); I915_WRITE(DSPFW3, FW_WM(wm->sr.cursor, CURSOR_SR)); if (IS_CHERRYVIEW(dev_priv)) { I915_WRITE(DSPFW7_CHV, - FW_WM_VLV(wm->pipe[PIPE_B].sprite[1], SPRITED) | - FW_WM_VLV(wm->pipe[PIPE_B].sprite[0], SPRITEC)); + FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE1], SPRITED) | + FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEC)); I915_WRITE(DSPFW8_CHV, - FW_WM_VLV(wm->pipe[PIPE_C].sprite[1], SPRITEF) | - FW_WM_VLV(wm->pipe[PIPE_C].sprite[0], SPRITEE)); + FW_WM_VLV(wm->pipe[PIPE_C].plane[PLANE_SPRITE1], SPRITEF) | + FW_WM_VLV(wm->pipe[PIPE_C].plane[PLANE_SPRITE0], SPRITEE)); I915_WRITE(DSPFW9_CHV, - FW_WM_VLV(wm->pipe[PIPE_C].primary, PLANEC) | - FW_WM(wm->pipe[PIPE_C].cursor, CURSORC)); + FW_WM_VLV(wm->pipe[PIPE_C].plane[PLANE_PRIMARY], PLANEC) | + FW_WM(wm->pipe[PIPE_C].plane[PLANE_CURSOR], CURSORC)); I915_WRITE(DSPHOWM, FW_WM(wm->sr.plane >> 9, SR_HI) | - FW_WM(wm->pipe[PIPE_C].sprite[1] >> 8, SPRITEF_HI) | - FW_WM(wm->pipe[PIPE_C].sprite[0] >> 8, SPRITEE_HI) | - FW_WM(wm->pipe[PIPE_C].primary >> 8, PLANEC_HI) | - FW_WM(wm->pipe[PIPE_B].sprite[1] >> 8, SPRITED_HI) | - FW_WM(wm->pipe[PIPE_B].sprite[0] >> 8, SPRITEC_HI) | - FW_WM(wm->pipe[PIPE_B].primary >> 8, PLANEB_HI) | - FW_WM(wm->pipe[PIPE_A].sprite[1] >> 8, SPRITEB_HI) | - FW_WM(wm->pipe[PIPE_A].sprite[0] >> 8, SPRITEA_HI) | - FW_WM(wm->pipe[PIPE_A].primary >> 8, PLANEA_HI)); + FW_WM(wm->pipe[PIPE_C].plane[PLANE_SPRITE1] >> 8, SPRITEF_HI) | + FW_WM(wm->pipe[PIPE_C].plane[PLANE_SPRITE0] >> 8, SPRITEE_HI) | + FW_WM(wm->pipe[PIPE_C].plane[PLANE_PRIMARY] >> 8, PLANEC_HI) | + FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE1] >> 8, SPRITED_HI) | + FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0] >> 8, SPRITEC_HI) | + FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY] >> 8, PLANEB_HI) | + FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE1] >> 8, SPRITEB_HI) | + FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0] >> 8, SPRITEA_HI) | + FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY] >> 8, PLANEA_HI)); } else { I915_WRITE(DSPFW7, - FW_WM_VLV(wm->pipe[PIPE_B].sprite[1], SPRITED) | - FW_WM_VLV(wm->pipe[PIPE_B].sprite[0], SPRITEC)); + FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE1], SPRITED) | + FW_WM_VLV(wm->pipe[PIPE_B].plane[PLANE_SPRITE0], SPRITEC)); I915_WRITE(DSPHOWM, FW_WM(wm->sr.plane >> 9, SR_HI) | - FW_WM(wm->pipe[PIPE_B].sprite[1] >> 8, SPRITED_HI) | - FW_WM(wm->pipe[PIPE_B].sprite[0] >> 8, SPRITEC_HI) | - FW_WM(wm->pipe[PIPE_B].primary >> 8, PLANEB_HI) | - FW_WM(wm->pipe[PIPE_A].sprite[1] >> 8, SPRITEB_HI) | - FW_WM(wm->pipe[PIPE_A].sprite[0] >> 8, SPRITEA_HI) | - FW_WM(wm->pipe[PIPE_A].primary >> 8, PLANEA_HI)); + FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE1] >> 8, SPRITED_HI) | + FW_WM(wm->pipe[PIPE_B].plane[PLANE_SPRITE0] >> 8, SPRITEC_HI) | + FW_WM(wm->pipe[PIPE_B].plane[PLANE_PRIMARY] >> 8, PLANEB_HI) | + FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE1] >> 8, SPRITEB_HI) | + FW_WM(wm->pipe[PIPE_A].plane[PLANE_SPRITE0] >> 8, SPRITEA_HI) | + FW_WM(wm->pipe[PIPE_A].plane[PLANE_PRIMARY] >> 8, PLANEA_HI)); } - /* zero (unused) WM1 watermarks */ - I915_WRITE(DSPFW4, 0); - I915_WRITE(DSPFW5, 0); - I915_WRITE(DSPFW6, 0); - I915_WRITE(DSPHOWM1, 0); - POSTING_READ(DSPFW1); } @@ -949,24 +978,26 @@ static void vlv_setup_wm_latency(struct drm_i915_private *dev_priv) } } -static uint16_t vlv_compute_wm_level(struct intel_plane *plane, - struct intel_crtc *crtc, - const struct intel_plane_state *state, +static uint16_t vlv_compute_wm_level(const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state, int level) { + struct intel_plane *plane = to_intel_plane(plane_state->base.plane); struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + const struct drm_display_mode *adjusted_mode = + &crtc_state->base.adjusted_mode; int clock, htotal, cpp, width, wm; if (dev_priv->wm.pri_latency[level] == 0) return USHRT_MAX; - if (!state->base.visible) + if (!plane_state->base.visible) return 0; - cpp = drm_format_plane_cpp(state->base.fb->pixel_format, 0); - clock = crtc->config->base.adjusted_mode.crtc_clock; - htotal = crtc->config->base.adjusted_mode.crtc_htotal; - width = crtc->config->pipe_src_w; + cpp = plane_state->base.fb->format->cpp[0]; + clock = adjusted_mode->crtc_clock; + htotal = adjusted_mode->crtc_htotal; + width = crtc_state->pipe_src_w; if (WARN_ON(htotal == 0)) htotal = 1; @@ -1004,7 +1035,7 @@ static void vlv_compute_fifo(struct intel_crtc *crtc) if (state->base.visible) { wm_state->num_active_planes++; - total_rate += drm_format_plane_cpp(state->base.fb->pixel_format, 0); + total_rate += state->base.fb->format->cpp[0]; } } @@ -1023,7 +1054,7 @@ static void vlv_compute_fifo(struct intel_crtc *crtc) continue; } - rate = drm_format_plane_cpp(state->base.fb->pixel_format, 0); + rate = state->base.fb->format->cpp[0]; plane->wm.fifo_size = fifo_size * rate / total_rate; fifo_left -= plane->wm.fifo_size; } @@ -1053,48 +1084,45 @@ static void vlv_compute_fifo(struct intel_crtc *crtc) WARN_ON(fifo_left != 0); } +static u16 vlv_invert_wm_value(u16 wm, u16 fifo_size) +{ + if (wm > fifo_size) + return USHRT_MAX; + else + return fifo_size - wm; +} + static void vlv_invert_wms(struct intel_crtc *crtc) { struct vlv_wm_state *wm_state = &crtc->wm_state; int level; for (level = 0; level < wm_state->num_levels; level++) { - struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); const int sr_fifo_size = - INTEL_INFO(to_i915(dev))->num_pipes * 512 - 1; + INTEL_INFO(dev_priv)->num_pipes * 512 - 1; struct intel_plane *plane; - wm_state->sr[level].plane = sr_fifo_size - wm_state->sr[level].plane; - wm_state->sr[level].cursor = 63 - wm_state->sr[level].cursor; - - for_each_intel_plane_on_crtc(dev, crtc, plane) { - switch (plane->base.type) { - int sprite; - case DRM_PLANE_TYPE_CURSOR: - wm_state->wm[level].cursor = plane->wm.fifo_size - - wm_state->wm[level].cursor; - break; - case DRM_PLANE_TYPE_PRIMARY: - wm_state->wm[level].primary = plane->wm.fifo_size - - wm_state->wm[level].primary; - break; - case DRM_PLANE_TYPE_OVERLAY: - sprite = plane->plane; - wm_state->wm[level].sprite[sprite] = plane->wm.fifo_size - - wm_state->wm[level].sprite[sprite]; - break; - } + wm_state->sr[level].plane = + vlv_invert_wm_value(wm_state->sr[level].plane, + sr_fifo_size); + wm_state->sr[level].cursor = + vlv_invert_wm_value(wm_state->sr[level].cursor, + 63); + + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { + wm_state->wm[level].plane[plane->id] = + vlv_invert_wm_value(wm_state->wm[level].plane[plane->id], + plane->wm.fifo_size); } } } static void vlv_compute_wm(struct intel_crtc *crtc) { - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct vlv_wm_state *wm_state = &crtc->wm_state; struct intel_plane *plane; - int sr_fifo_size = INTEL_INFO(dev_priv)->num_pipes * 512 - 1; int level; memset(wm_state, 0, sizeof(*wm_state)); @@ -1109,45 +1137,27 @@ static void vlv_compute_wm(struct intel_crtc *crtc) if (wm_state->num_active_planes != 1) wm_state->cxsr = false; - if (wm_state->cxsr) { - for (level = 0; level < wm_state->num_levels; level++) { - wm_state->sr[level].plane = sr_fifo_size; - wm_state->sr[level].cursor = 63; - } - } - - for_each_intel_plane_on_crtc(dev, crtc, plane) { + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { struct intel_plane_state *state = to_intel_plane_state(plane->base.state); + int level; if (!state->base.visible) continue; /* normal watermarks */ for (level = 0; level < wm_state->num_levels; level++) { - int wm = vlv_compute_wm_level(plane, crtc, state, level); - int max_wm = plane->base.type == DRM_PLANE_TYPE_CURSOR ? 63 : 511; + int wm = vlv_compute_wm_level(crtc->config, state, level); + int max_wm = plane->wm.fifo_size; /* hack */ if (WARN_ON(level == 0 && wm > max_wm)) wm = max_wm; - if (wm > plane->wm.fifo_size) + if (wm > max_wm) break; - switch (plane->base.type) { - int sprite; - case DRM_PLANE_TYPE_CURSOR: - wm_state->wm[level].cursor = wm; - break; - case DRM_PLANE_TYPE_PRIMARY: - wm_state->wm[level].primary = wm; - break; - case DRM_PLANE_TYPE_OVERLAY: - sprite = plane->plane; - wm_state->wm[level].sprite[sprite] = wm; - break; - } + wm_state->wm[level].plane[plane->id] = wm; } wm_state->num_levels = level; @@ -1156,26 +1166,15 @@ static void vlv_compute_wm(struct intel_crtc *crtc) continue; /* maxfifo watermarks */ - switch (plane->base.type) { - int sprite, level; - case DRM_PLANE_TYPE_CURSOR: + if (plane->id == PLANE_CURSOR) { for (level = 0; level < wm_state->num_levels; level++) wm_state->sr[level].cursor = - wm_state->wm[level].cursor; - break; - case DRM_PLANE_TYPE_PRIMARY: - for (level = 0; level < wm_state->num_levels; level++) - wm_state->sr[level].plane = - min(wm_state->sr[level].plane, - wm_state->wm[level].primary); - break; - case DRM_PLANE_TYPE_OVERLAY: - sprite = plane->plane; + wm_state->wm[level].plane[PLANE_CURSOR]; + } else { for (level = 0; level < wm_state->num_levels; level++) wm_state->sr[level].plane = - min(wm_state->sr[level].plane, - wm_state->wm[level].sprite[sprite]); - break; + max(wm_state->sr[level].plane, + wm_state->wm[level].plane[plane->id]); } } @@ -1199,17 +1198,23 @@ static void vlv_pipe_set_fifo_size(struct intel_crtc *crtc) int sprite0_start = 0, sprite1_start = 0, fifo_size = 0; for_each_intel_plane_on_crtc(dev, crtc, plane) { - if (plane->base.type == DRM_PLANE_TYPE_CURSOR) { - WARN_ON(plane->wm.fifo_size != 63); - continue; - } - - if (plane->base.type == DRM_PLANE_TYPE_PRIMARY) + switch (plane->id) { + case PLANE_PRIMARY: sprite0_start = plane->wm.fifo_size; - else if (plane->plane == 0) + break; + case PLANE_SPRITE0: sprite1_start = sprite0_start + plane->wm.fifo_size; - else + break; + case PLANE_SPRITE1: fifo_size = sprite1_start + plane->wm.fifo_size; + break; + case PLANE_CURSOR: + WARN_ON(plane->wm.fifo_size != 63); + break; + default: + MISSING_CASE(plane->id); + break; + } } WARN_ON(fifo_size != 512 - 1); @@ -1218,6 +1223,8 @@ static void vlv_pipe_set_fifo_size(struct intel_crtc *crtc) pipe_name(crtc->pipe), sprite0_start, sprite1_start, fifo_size); + spin_lock(&dev_priv->wm.dsparb_lock); + switch (crtc->pipe) { uint32_t dsparb, dsparb2, dsparb3; case PIPE_A: @@ -1274,20 +1281,24 @@ static void vlv_pipe_set_fifo_size(struct intel_crtc *crtc) default: break; } + + POSTING_READ(DSPARB); + + spin_unlock(&dev_priv->wm.dsparb_lock); } #undef VLV_FIFO -static void vlv_merge_wm(struct drm_device *dev, +static void vlv_merge_wm(struct drm_i915_private *dev_priv, struct vlv_wm_values *wm) { struct intel_crtc *crtc; int num_active_crtcs = 0; - wm->level = to_i915(dev)->wm.max_level; + wm->level = dev_priv->wm.max_level; wm->cxsr = true; - for_each_intel_crtc(dev, crtc) { + for_each_intel_crtc(&dev_priv->drm, crtc) { const struct vlv_wm_state *wm_state = &crtc->wm_state; if (!crtc->active) @@ -1306,7 +1317,7 @@ static void vlv_merge_wm(struct drm_device *dev, if (num_active_crtcs > 1) wm->level = VLV_WM_LEVEL_PM2; - for_each_intel_crtc(dev, crtc) { + for_each_intel_crtc(&dev_priv->drm, crtc) { struct vlv_wm_state *wm_state = &crtc->wm_state; enum pipe pipe = crtc->pipe; @@ -1317,63 +1328,70 @@ static void vlv_merge_wm(struct drm_device *dev, if (wm->cxsr) wm->sr = wm_state->sr[wm->level]; - wm->ddl[pipe].primary = DDL_PRECISION_HIGH | 2; - wm->ddl[pipe].sprite[0] = DDL_PRECISION_HIGH | 2; - wm->ddl[pipe].sprite[1] = DDL_PRECISION_HIGH | 2; - wm->ddl[pipe].cursor = DDL_PRECISION_HIGH | 2; + wm->ddl[pipe].plane[PLANE_PRIMARY] = DDL_PRECISION_HIGH | 2; + wm->ddl[pipe].plane[PLANE_SPRITE0] = DDL_PRECISION_HIGH | 2; + wm->ddl[pipe].plane[PLANE_SPRITE1] = DDL_PRECISION_HIGH | 2; + wm->ddl[pipe].plane[PLANE_CURSOR] = DDL_PRECISION_HIGH | 2; } } +static bool is_disabling(int old, int new, int threshold) +{ + return old >= threshold && new < threshold; +} + +static bool is_enabling(int old, int new, int threshold) +{ + return old < threshold && new >= threshold; +} + static void vlv_update_wm(struct intel_crtc *crtc) { - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum pipe pipe = crtc->pipe; - struct vlv_wm_values wm = {}; + struct vlv_wm_values *old_wm = &dev_priv->wm.vlv; + struct vlv_wm_values new_wm = {}; vlv_compute_wm(crtc); - vlv_merge_wm(dev, &wm); + vlv_merge_wm(dev_priv, &new_wm); - if (memcmp(&dev_priv->wm.vlv, &wm, sizeof(wm)) == 0) { + if (memcmp(old_wm, &new_wm, sizeof(new_wm)) == 0) { /* FIXME should be part of crtc atomic commit */ vlv_pipe_set_fifo_size(crtc); + return; } - if (wm.level < VLV_WM_LEVEL_DDR_DVFS && - dev_priv->wm.vlv.level >= VLV_WM_LEVEL_DDR_DVFS) + if (is_disabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_DDR_DVFS)) chv_set_memory_dvfs(dev_priv, false); - if (wm.level < VLV_WM_LEVEL_PM5 && - dev_priv->wm.vlv.level >= VLV_WM_LEVEL_PM5) + if (is_disabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_PM5)) chv_set_memory_pm5(dev_priv, false); - if (!wm.cxsr && dev_priv->wm.vlv.cxsr) - intel_set_memory_cxsr(dev_priv, false); + if (is_disabling(old_wm->cxsr, new_wm.cxsr, true)) + _intel_set_memory_cxsr(dev_priv, false); /* FIXME should be part of crtc atomic commit */ vlv_pipe_set_fifo_size(crtc); - vlv_write_wm_values(crtc, &wm); + vlv_write_wm_values(dev_priv, &new_wm); DRM_DEBUG_KMS("Setting FIFO watermarks - %c: plane=%d, cursor=%d, " "sprite0=%d, sprite1=%d, SR: plane=%d, cursor=%d level=%d cxsr=%d\n", - pipe_name(pipe), wm.pipe[pipe].primary, wm.pipe[pipe].cursor, - wm.pipe[pipe].sprite[0], wm.pipe[pipe].sprite[1], - wm.sr.plane, wm.sr.cursor, wm.level, wm.cxsr); + pipe_name(pipe), new_wm.pipe[pipe].plane[PLANE_PRIMARY], new_wm.pipe[pipe].plane[PLANE_CURSOR], + new_wm.pipe[pipe].plane[PLANE_SPRITE0], new_wm.pipe[pipe].plane[PLANE_SPRITE1], + new_wm.sr.plane, new_wm.sr.cursor, new_wm.level, new_wm.cxsr); - if (wm.cxsr && !dev_priv->wm.vlv.cxsr) - intel_set_memory_cxsr(dev_priv, true); + if (is_enabling(old_wm->cxsr, new_wm.cxsr, true)) + _intel_set_memory_cxsr(dev_priv, true); - if (wm.level >= VLV_WM_LEVEL_PM5 && - dev_priv->wm.vlv.level < VLV_WM_LEVEL_PM5) + if (is_enabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_PM5)) chv_set_memory_pm5(dev_priv, true); - if (wm.level >= VLV_WM_LEVEL_DDR_DVFS && - dev_priv->wm.vlv.level < VLV_WM_LEVEL_DDR_DVFS) + if (is_enabling(old_wm->level, new_wm.level, VLV_WM_LEVEL_DDR_DVFS)) chv_set_memory_dvfs(dev_priv, true); - dev_priv->wm.vlv = wm; + *old_wm = new_wm; } #define single_plane_enabled(mask) is_power_of_2(mask) @@ -1455,7 +1473,7 @@ static void i965_update_wm(struct intel_crtc *unused_crtc) int clock = adjusted_mode->crtc_clock; int htotal = adjusted_mode->crtc_htotal; int hdisplay = crtc->config->pipe_src_w; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + int cpp = fb->format->cpp[0]; unsigned long line_time_us; int entries; @@ -1541,7 +1559,7 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc) if (IS_GEN2(dev_priv)) cpp = 4; else - cpp = drm_format_plane_cpp(fb->pixel_format, 0); + cpp = fb->format->cpp[0]; planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, wm_info, fifo_size, cpp, @@ -1568,7 +1586,7 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc) if (IS_GEN2(dev_priv)) cpp = 4; else - cpp = drm_format_plane_cpp(fb->pixel_format, 0); + cpp = fb->format->cpp[0]; planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock, wm_info, fifo_size, cpp, @@ -1621,7 +1639,7 @@ static void i9xx_update_wm(struct intel_crtc *unused_crtc) if (IS_I915GM(dev_priv) || IS_I945GM(dev_priv)) cpp = 4; else - cpp = drm_format_plane_cpp(fb->pixel_format, 0); + cpp = fb->format->cpp[0]; line_time_us = max(htotal * 1000 / clock, 1); @@ -1781,13 +1799,14 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate, uint32_t mem_value, bool is_lp) { - int cpp = pstate->base.fb ? - drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; uint32_t method1, method2; + int cpp; if (!cstate->base.active || !pstate->base.visible) return 0; + cpp = pstate->base.fb->format->cpp[0]; + method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value); if (!is_lp) @@ -1809,13 +1828,14 @@ static uint32_t ilk_compute_spr_wm(const struct intel_crtc_state *cstate, const struct intel_plane_state *pstate, uint32_t mem_value) { - int cpp = pstate->base.fb ? - drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; uint32_t method1, method2; + int cpp; if (!cstate->base.active || !pstate->base.visible) return 0; + cpp = pstate->base.fb->format->cpp[0]; + method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value); method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate), cstate->base.adjusted_mode.crtc_htotal, @@ -1853,12 +1873,13 @@ static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate, const struct intel_plane_state *pstate, uint32_t pri_val) { - int cpp = pstate->base.fb ? - drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; + int cpp; if (!cstate->base.active || !pstate->base.visible) return 0; + cpp = pstate->base.fb->format->cpp[0]; + return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->base.dst), cpp); } @@ -2867,28 +2888,6 @@ bool ilk_disable_lp_wm(struct drm_device *dev) #define SKL_SAGV_BLOCK_TIME 30 /* µs */ /* - * Return the index of a plane in the SKL DDB and wm result arrays. Primary - * plane is always in slot 0, cursor is always in slot I915_MAX_PLANES-1, and - * other universal planes are in indices 1..n. Note that this may leave unused - * indices between the top "sprite" plane and the cursor. - */ -static int -skl_wm_plane_id(const struct intel_plane *plane) -{ - switch (plane->base.type) { - case DRM_PLANE_TYPE_PRIMARY: - return 0; - case DRM_PLANE_TYPE_CURSOR: - return PLANE_CURSOR; - case DRM_PLANE_TYPE_OVERLAY: - return plane->plane + 1; - default: - MISSING_CASE(plane->base.type); - return plane->plane; - } -} - -/* * FIXME: We still don't have the proper code detect if we need to apply the WA, * so assume we'll always need it in order to avoid underruns. */ @@ -3010,7 +3009,6 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) struct intel_crtc *crtc; struct intel_plane *plane; struct intel_crtc_state *cstate; - struct skl_plane_wm *wm; enum pipe pipe; int level, latency; @@ -3037,7 +3035,8 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) return false; for_each_intel_plane_on_crtc(dev, crtc, plane) { - wm = &cstate->wm.skl.optimal.planes[skl_wm_plane_id(plane)]; + struct skl_plane_wm *wm = + &cstate->wm.skl.optimal.planes[plane->id]; /* Skip this plane if it's not enabled */ if (!wm->wm[0].plane_en) @@ -3140,28 +3139,29 @@ static void skl_ddb_entry_init_from_hw(struct skl_ddb_entry *entry, u32 reg) void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv, struct skl_ddb_allocation *ddb /* out */) { - enum pipe pipe; - int plane; - u32 val; + struct intel_crtc *crtc; memset(ddb, 0, sizeof(*ddb)); - for_each_pipe(dev_priv, pipe) { + for_each_intel_crtc(&dev_priv->drm, crtc) { enum intel_display_power_domain power_domain; + enum plane_id plane_id; + enum pipe pipe = crtc->pipe; power_domain = POWER_DOMAIN_PIPE(pipe); if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) continue; - for_each_universal_plane(dev_priv, pipe, plane) { - val = I915_READ(PLANE_BUF_CFG(pipe, plane)); - skl_ddb_entry_init_from_hw(&ddb->plane[pipe][plane], - val); - } + for_each_plane_id_on_crtc(crtc, plane_id) { + u32 val; - val = I915_READ(CUR_BUF_CFG(pipe)); - skl_ddb_entry_init_from_hw(&ddb->plane[pipe][PLANE_CURSOR], - val); + if (plane_id != PLANE_CURSOR) + val = I915_READ(PLANE_BUF_CFG(pipe, plane_id)); + else + val = I915_READ(CUR_BUF_CFG(pipe)); + + skl_ddb_entry_init_from_hw(&ddb->plane[pipe][plane_id], val); + } intel_display_power_put(dev_priv, power_domain); } @@ -3213,13 +3213,17 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate, int y) { struct intel_plane_state *intel_pstate = to_intel_plane_state(pstate); - struct drm_framebuffer *fb = pstate->fb; uint32_t down_scale_amount, data_rate; uint32_t width = 0, height = 0; - unsigned format = fb ? fb->pixel_format : DRM_FORMAT_XRGB8888; + struct drm_framebuffer *fb; + u32 format; if (!intel_pstate->base.visible) return 0; + + fb = pstate->fb; + format = fb->format->format; + if (pstate->plane->type == DRM_PLANE_TYPE_CURSOR) return 0; if (y && format != DRM_FORMAT_NV12) @@ -3235,13 +3239,13 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate, if (format == DRM_FORMAT_NV12) { if (y) /* y-plane data rate */ data_rate = width * height * - drm_format_plane_cpp(format, 0); + fb->format->cpp[0]; else /* uv-plane data rate */ data_rate = (width / 2) * (height / 2) * - drm_format_plane_cpp(format, 1); + fb->format->cpp[1]; } else { /* for packed formats */ - data_rate = width * height * drm_format_plane_cpp(format, 0); + data_rate = width * height * fb->format->cpp[0]; } down_scale_amount = skl_plane_downscale_amount(intel_pstate); @@ -3262,30 +3266,28 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate, struct drm_crtc_state *cstate = &intel_cstate->base; struct drm_atomic_state *state = cstate->state; struct drm_plane *plane; - const struct intel_plane *intel_plane; const struct drm_plane_state *pstate; - unsigned int rate, total_data_rate = 0; - int id; + unsigned int total_data_rate = 0; if (WARN_ON(!state)) return 0; /* Calculate and cache data rate for each plane */ drm_atomic_crtc_state_for_each_plane_state(plane, pstate, cstate) { - id = skl_wm_plane_id(to_intel_plane(plane)); - intel_plane = to_intel_plane(plane); + enum plane_id plane_id = to_intel_plane(plane)->id; + unsigned int rate; /* packed/uv */ rate = skl_plane_relative_data_rate(intel_cstate, pstate, 0); - plane_data_rate[id] = rate; + plane_data_rate[plane_id] = rate; total_data_rate += rate; /* y-plane */ rate = skl_plane_relative_data_rate(intel_cstate, pstate, 1); - plane_y_data_rate[id] = rate; + plane_y_data_rate[plane_id] = rate; total_data_rate += rate; } @@ -3307,7 +3309,7 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate, return 0; /* For packed formats, no y-plane, return 0 */ - if (y && fb->pixel_format != DRM_FORMAT_NV12) + if (y && fb->format->format != DRM_FORMAT_NV12) return 0; /* For Non Y-tile return 8-blocks */ @@ -3322,15 +3324,15 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate, swap(src_w, src_h); /* Halve UV plane width and height for NV12 */ - if (fb->pixel_format == DRM_FORMAT_NV12 && !y) { + if (fb->format->format == DRM_FORMAT_NV12 && !y) { src_w /= 2; src_h /= 2; } - if (fb->pixel_format == DRM_FORMAT_NV12 && !y) - plane_bpp = drm_format_plane_cpp(fb->pixel_format, 1); + if (fb->format->format == DRM_FORMAT_NV12 && !y) + plane_bpp = fb->format->cpp[1]; else - plane_bpp = drm_format_plane_cpp(fb->pixel_format, 0); + plane_bpp = fb->format->cpp[0]; if (drm_rotation_90_or_270(pstate->rotation)) { switch (plane_bpp) { @@ -3364,17 +3366,16 @@ skl_ddb_calc_min(const struct intel_crtc_state *cstate, int num_active, struct drm_plane *plane; drm_atomic_crtc_state_for_each_plane_state(plane, pstate, &cstate->base) { - struct intel_plane *intel_plane = to_intel_plane(plane); - int id = skl_wm_plane_id(intel_plane); + enum plane_id plane_id = to_intel_plane(plane)->id; - if (id == PLANE_CURSOR) + if (plane_id == PLANE_CURSOR) continue; if (!pstate->visible) continue; - minimum[id] = skl_ddb_min_alloc(pstate, 0); - y_minimum[id] = skl_ddb_min_alloc(pstate, 1); + minimum[plane_id] = skl_ddb_min_alloc(pstate, 0); + y_minimum[plane_id] = skl_ddb_min_alloc(pstate, 1); } minimum[PLANE_CURSOR] = skl_cursor_allocation(num_active); @@ -3394,8 +3395,8 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, uint16_t minimum[I915_MAX_PLANES] = {}; uint16_t y_minimum[I915_MAX_PLANES] = {}; unsigned int total_data_rate; + enum plane_id plane_id; int num_active; - int id, i; unsigned plane_data_rate[I915_MAX_PLANES] = {}; unsigned plane_y_data_rate[I915_MAX_PLANES] = {}; @@ -3426,9 +3427,9 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, * proportional to the data rate. */ - for (i = 0; i < I915_MAX_PLANES; i++) { - alloc_size -= minimum[i]; - alloc_size -= y_minimum[i]; + for_each_plane_id_on_crtc(intel_crtc, plane_id) { + alloc_size -= minimum[plane_id]; + alloc_size -= y_minimum[plane_id]; } ddb->plane[pipe][PLANE_CURSOR].start = alloc->end - minimum[PLANE_CURSOR]; @@ -3447,28 +3448,28 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, return 0; start = alloc->start; - for (id = 0; id < I915_MAX_PLANES; id++) { + for_each_plane_id_on_crtc(intel_crtc, plane_id) { unsigned int data_rate, y_data_rate; uint16_t plane_blocks, y_plane_blocks = 0; - if (id == PLANE_CURSOR) + if (plane_id == PLANE_CURSOR) continue; - data_rate = plane_data_rate[id]; + data_rate = plane_data_rate[plane_id]; /* * allocation for (packed formats) or (uv-plane part of planar format): * promote the expression to 64 bits to avoid overflowing, the * result is < available as data_rate / total_data_rate < 1 */ - plane_blocks = minimum[id]; + plane_blocks = minimum[plane_id]; plane_blocks += div_u64((uint64_t)alloc_size * data_rate, total_data_rate); /* Leave disabled planes at (0,0) */ if (data_rate) { - ddb->plane[pipe][id].start = start; - ddb->plane[pipe][id].end = start + plane_blocks; + ddb->plane[pipe][plane_id].start = start; + ddb->plane[pipe][plane_id].end = start + plane_blocks; } start += plane_blocks; @@ -3476,15 +3477,15 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, /* * allocation for y_plane part of planar format: */ - y_data_rate = plane_y_data_rate[id]; + y_data_rate = plane_y_data_rate[plane_id]; - y_plane_blocks = y_minimum[id]; + y_plane_blocks = y_minimum[plane_id]; y_plane_blocks += div_u64((uint64_t)alloc_size * y_data_rate, total_data_rate); if (y_data_rate) { - ddb->y_plane[pipe][id].start = start; - ddb->y_plane[pipe][id].end = start + y_plane_blocks; + ddb->y_plane[pipe][plane_id].start = start; + ddb->y_plane[pipe][plane_id].end = start + y_plane_blocks; } start += y_plane_blocks; @@ -3499,32 +3500,35 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, * should allow pixel_rate up to ~2 GHz which seems sufficient since max * 2xcdclk is 1350 MHz and the pixel rate should never exceed that. */ -static uint32_t skl_wm_method1(uint32_t pixel_rate, uint8_t cpp, uint32_t latency) +static uint_fixed_16_16_t skl_wm_method1(uint32_t pixel_rate, uint8_t cpp, + uint32_t latency) { - uint32_t wm_intermediate_val, ret; + uint32_t wm_intermediate_val; + uint_fixed_16_16_t ret; if (latency == 0) - return UINT_MAX; - - wm_intermediate_val = latency * pixel_rate * cpp / 512; - ret = DIV_ROUND_UP(wm_intermediate_val, 1000); + return FP_16_16_MAX; + wm_intermediate_val = latency * pixel_rate * cpp; + ret = fixed_16_16_div_round_up_u64(wm_intermediate_val, 1000 * 512); return ret; } -static uint32_t skl_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, - uint32_t latency, uint32_t plane_blocks_per_line) +static uint_fixed_16_16_t skl_wm_method2(uint32_t pixel_rate, + uint32_t pipe_htotal, + uint32_t latency, + uint_fixed_16_16_t plane_blocks_per_line) { - uint32_t ret; uint32_t wm_intermediate_val; + uint_fixed_16_16_t ret; if (latency == 0) - return UINT_MAX; + return FP_16_16_MAX; wm_intermediate_val = latency * pixel_rate; - ret = DIV_ROUND_UP(wm_intermediate_val, pipe_htotal * 1000) * - plane_blocks_per_line; - + wm_intermediate_val = DIV_ROUND_UP(wm_intermediate_val, + pipe_htotal * 1000); + ret = mul_u32_fixed_16_16(wm_intermediate_val, plane_blocks_per_line); return ret; } @@ -3564,24 +3568,36 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, struct drm_plane_state *pstate = &intel_pstate->base; struct drm_framebuffer *fb = pstate->fb; uint32_t latency = dev_priv->wm.skl_latency[level]; - uint32_t method1, method2; - uint32_t plane_bytes_per_line, plane_blocks_per_line; + uint_fixed_16_16_t method1, method2; + uint_fixed_16_16_t plane_blocks_per_line; + uint_fixed_16_16_t selected_result; + uint32_t interm_pbpl; + uint32_t plane_bytes_per_line; uint32_t res_blocks, res_lines; - uint32_t selected_result; uint8_t cpp; uint32_t width = 0, height = 0; uint32_t plane_pixel_rate; - uint32_t y_tile_minimum, y_min_scanlines; + uint_fixed_16_16_t y_tile_minimum; + uint32_t y_min_scanlines; struct intel_atomic_state *state = to_intel_atomic_state(cstate->base.state); bool apply_memory_bw_wa = skl_needs_memory_bw_wa(state); + bool y_tiled, x_tiled; if (latency == 0 || !cstate->base.active || !intel_pstate->base.visible) { *enabled = false; return 0; } - if (apply_memory_bw_wa && fb->modifier == I915_FORMAT_MOD_X_TILED) + y_tiled = fb->modifier == I915_FORMAT_MOD_Y_TILED || + fb->modifier == I915_FORMAT_MOD_Yf_TILED; + x_tiled = fb->modifier == I915_FORMAT_MOD_X_TILED; + + /* Display WA #1141: kbl. */ + if (IS_KABYLAKE(dev_priv) && dev_priv->ipc_enabled) + latency += 4; + + if (apply_memory_bw_wa && x_tiled) latency += 15; width = drm_rect_width(&intel_pstate->base.src) >> 16; @@ -3590,13 +3606,13 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, if (drm_rotation_90_or_270(pstate->rotation)) swap(width, height); - cpp = drm_format_plane_cpp(fb->pixel_format, 0); + cpp = fb->format->cpp[0]; plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate, intel_pstate); if (drm_rotation_90_or_270(pstate->rotation)) { - int cpp = (fb->pixel_format == DRM_FORMAT_NV12) ? - drm_format_plane_cpp(fb->pixel_format, 1) : - drm_format_plane_cpp(fb->pixel_format, 0); + int cpp = (fb->format->format == DRM_FORMAT_NV12) ? + fb->format->cpp[1] : + fb->format->cpp[0]; switch (cpp) { case 1: @@ -3620,16 +3636,17 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, y_min_scanlines *= 2; plane_bytes_per_line = width * cpp; - if (fb->modifier == I915_FORMAT_MOD_Y_TILED || - fb->modifier == I915_FORMAT_MOD_Yf_TILED) { + if (y_tiled) { + interm_pbpl = DIV_ROUND_UP(plane_bytes_per_line * + y_min_scanlines, 512); plane_blocks_per_line = - DIV_ROUND_UP(plane_bytes_per_line * y_min_scanlines, 512); - plane_blocks_per_line /= y_min_scanlines; - } else if (fb->modifier == DRM_FORMAT_MOD_NONE) { - plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512) - + 1; + fixed_16_16_div_round_up(interm_pbpl, y_min_scanlines); + } else if (x_tiled) { + interm_pbpl = DIV_ROUND_UP(plane_bytes_per_line, 512); + plane_blocks_per_line = u32_to_fixed_16_16(interm_pbpl); } else { - plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512); + interm_pbpl = DIV_ROUND_UP(plane_bytes_per_line, 512) + 1; + plane_blocks_per_line = u32_to_fixed_16_16(interm_pbpl); } method1 = skl_wm_method1(plane_pixel_rate, cpp, latency); @@ -3638,28 +3655,29 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, latency, plane_blocks_per_line); - y_tile_minimum = plane_blocks_per_line * y_min_scanlines; + y_tile_minimum = mul_u32_fixed_16_16(y_min_scanlines, + plane_blocks_per_line); - if (fb->modifier == I915_FORMAT_MOD_Y_TILED || - fb->modifier == I915_FORMAT_MOD_Yf_TILED) { - selected_result = max(method2, y_tile_minimum); + if (y_tiled) { + selected_result = max_fixed_16_16(method2, y_tile_minimum); } else { if ((cpp * cstate->base.adjusted_mode.crtc_htotal / 512 < 1) && (plane_bytes_per_line / 512 < 1)) selected_result = method2; - else if ((ddb_allocation / plane_blocks_per_line) >= 1) - selected_result = min(method1, method2); + else if ((ddb_allocation / + fixed_16_16_to_u32_round_up(plane_blocks_per_line)) >= 1) + selected_result = min_fixed_16_16(method1, method2); else selected_result = method1; } - res_blocks = selected_result + 1; - res_lines = DIV_ROUND_UP(selected_result, plane_blocks_per_line); + res_blocks = fixed_16_16_to_u32_round_up(selected_result) + 1; + res_lines = DIV_ROUND_UP(selected_result.val, + plane_blocks_per_line.val); if (level >= 1 && level <= 7) { - if (fb->modifier == I915_FORMAT_MOD_Y_TILED || - fb->modifier == I915_FORMAT_MOD_Yf_TILED) { - res_blocks += y_tile_minimum; + if (y_tiled) { + res_blocks += fixed_16_16_to_u32_round_up(y_tile_minimum); res_lines += y_min_scanlines; } else { res_blocks++; @@ -3676,12 +3694,12 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, if (level) { return 0; } else { + struct drm_plane *plane = pstate->plane; + DRM_DEBUG_KMS("Requested display configuration exceeds system watermark limitations\n"); - DRM_DEBUG_KMS("Plane %d.%d: blocks required = %u/%u, lines required = %u/31\n", - to_intel_crtc(cstate->base.crtc)->pipe, - skl_wm_plane_id(to_intel_plane(pstate->plane)), + DRM_DEBUG_KMS("[PLANE:%d:%s] blocks required = %u/%u, lines required = %u/31\n", + plane->base.id, plane->name, res_blocks, ddb_allocation, res_lines); - return -EINVAL; } } @@ -3708,7 +3726,6 @@ skl_compute_wm_level(const struct drm_i915_private *dev_priv, uint16_t ddb_blocks; enum pipe pipe = intel_crtc->pipe; int ret; - int i = skl_wm_plane_id(intel_plane); if (state) intel_pstate = @@ -3731,7 +3748,7 @@ skl_compute_wm_level(const struct drm_i915_private *dev_priv, WARN_ON(!intel_pstate->base.fb); - ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][i]); + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][intel_plane->id]); ret = skl_compute_plane_wm(dev_priv, cstate, @@ -3750,7 +3767,10 @@ skl_compute_wm_level(const struct drm_i915_private *dev_priv, static uint32_t skl_compute_linetime_wm(struct intel_crtc_state *cstate) { + struct drm_atomic_state *state = cstate->base.state; + struct drm_i915_private *dev_priv = to_i915(state->dev); uint32_t pixel_rate; + uint32_t linetime_wm; if (!cstate->base.active) return 0; @@ -3760,8 +3780,14 @@ skl_compute_linetime_wm(struct intel_crtc_state *cstate) if (WARN_ON(pixel_rate == 0)) return 0; - return DIV_ROUND_UP(8 * cstate->base.adjusted_mode.crtc_htotal * 1000, - pixel_rate); + linetime_wm = DIV_ROUND_UP(8 * cstate->base.adjusted_mode.crtc_htotal * + 1000, pixel_rate); + + /* Display WA #1135: bxt. */ + if (IS_BROXTON(dev_priv) && dev_priv->ipc_enabled) + linetime_wm = DIV_ROUND_UP(linetime_wm, 2); + + return linetime_wm; } static void skl_compute_transition_wm(struct intel_crtc_state *cstate, @@ -3794,7 +3820,7 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, for_each_intel_plane_mask(&dev_priv->drm, intel_plane, cstate->base.plane_mask) { - wm = &pipe_wm->planes[skl_wm_plane_id(intel_plane)]; + wm = &pipe_wm->planes[intel_plane->id]; for (level = 0; level <= max_level; level++) { ret = skl_compute_wm_level(dev_priv, ddb, cstate, @@ -3838,7 +3864,7 @@ static void skl_write_wm_level(struct drm_i915_private *dev_priv, static void skl_write_plane_wm(struct intel_crtc *intel_crtc, const struct skl_plane_wm *wm, const struct skl_ddb_allocation *ddb, - int plane) + enum plane_id plane_id) { struct drm_crtc *crtc = &intel_crtc->base; struct drm_device *dev = crtc->dev; @@ -3847,16 +3873,16 @@ static void skl_write_plane_wm(struct intel_crtc *intel_crtc, enum pipe pipe = intel_crtc->pipe; for (level = 0; level <= max_level; level++) { - skl_write_wm_level(dev_priv, PLANE_WM(pipe, plane, level), + skl_write_wm_level(dev_priv, PLANE_WM(pipe, plane_id, level), &wm->wm[level]); } - skl_write_wm_level(dev_priv, PLANE_WM_TRANS(pipe, plane), + skl_write_wm_level(dev_priv, PLANE_WM_TRANS(pipe, plane_id), &wm->trans_wm); - skl_ddb_entry_write(dev_priv, PLANE_BUF_CFG(pipe, plane), - &ddb->plane[pipe][plane]); - skl_ddb_entry_write(dev_priv, PLANE_NV12_BUF_CFG(pipe, plane), - &ddb->y_plane[pipe][plane]); + skl_ddb_entry_write(dev_priv, PLANE_BUF_CFG(pipe, plane_id), + &ddb->plane[pipe][plane_id]); + skl_ddb_entry_write(dev_priv, PLANE_NV12_BUF_CFG(pipe, plane_id), + &ddb->y_plane[pipe][plane_id]); } static void skl_write_cursor_wm(struct intel_crtc *intel_crtc, @@ -3961,17 +3987,16 @@ skl_ddb_add_affected_planes(struct intel_crtc_state *cstate) struct drm_plane_state *plane_state; struct drm_plane *plane; enum pipe pipe = intel_crtc->pipe; - int id; WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc)); drm_for_each_plane_mask(plane, dev, cstate->base.plane_mask) { - id = skl_wm_plane_id(to_intel_plane(plane)); + enum plane_id plane_id = to_intel_plane(plane)->id; - if (skl_ddb_entry_equal(&cur_ddb->plane[pipe][id], - &new_ddb->plane[pipe][id]) && - skl_ddb_entry_equal(&cur_ddb->y_plane[pipe][id], - &new_ddb->y_plane[pipe][id])) + if (skl_ddb_entry_equal(&cur_ddb->plane[pipe][plane_id], + &new_ddb->plane[pipe][plane_id]) && + skl_ddb_entry_equal(&cur_ddb->y_plane[pipe][plane_id], + &new_ddb->y_plane[pipe][plane_id])) continue; plane_state = drm_atomic_get_plane_state(state, plane); @@ -4083,7 +4108,6 @@ skl_print_wm_changes(const struct drm_atomic_state *state) const struct intel_plane *intel_plane; const struct skl_ddb_allocation *old_ddb = &dev_priv->wm.skl_hw.ddb; const struct skl_ddb_allocation *new_ddb = &intel_state->wm_results.ddb; - int id; int i; for_each_crtc_in_state(state, crtc, cstate, i) { @@ -4091,11 +4115,11 @@ skl_print_wm_changes(const struct drm_atomic_state *state) enum pipe pipe = intel_crtc->pipe; for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { + enum plane_id plane_id = intel_plane->id; const struct skl_ddb_entry *old, *new; - id = skl_wm_plane_id(intel_plane); - old = &old_ddb->plane[pipe][id]; - new = &new_ddb->plane[pipe][id]; + old = &old_ddb->plane[pipe][plane_id]; + new = &new_ddb->plane[pipe][plane_id]; if (skl_ddb_entry_equal(old, new)) continue; @@ -4185,17 +4209,21 @@ static void skl_atomic_update_crtc_wm(struct intel_atomic_state *state, struct skl_pipe_wm *pipe_wm = &cstate->wm.skl.optimal; const struct skl_ddb_allocation *ddb = &state->wm_results.ddb; enum pipe pipe = crtc->pipe; - int plane; + enum plane_id plane_id; if (!(state->wm_results.dirty_pipes & drm_crtc_mask(&crtc->base))) return; I915_WRITE(PIPE_WM_LINETIME(pipe), pipe_wm->linetime); - for_each_universal_plane(dev_priv, pipe, plane) - skl_write_plane_wm(crtc, &pipe_wm->planes[plane], ddb, plane); - - skl_write_cursor_wm(crtc, &pipe_wm->planes[PLANE_CURSOR], ddb); + for_each_plane_id_on_crtc(crtc, plane_id) { + if (plane_id != PLANE_CURSOR) + skl_write_plane_wm(crtc, &pipe_wm->planes[plane_id], + ddb, plane_id); + else + skl_write_cursor_wm(crtc, &pipe_wm->planes[plane_id], + ddb); + } } static void skl_initial_wm(struct intel_atomic_state *state, @@ -4310,32 +4338,29 @@ static inline void skl_wm_level_from_reg_val(uint32_t val, void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc, struct skl_pipe_wm *out) { - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = to_i915(crtc->dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_plane *intel_plane; - struct skl_plane_wm *wm; enum pipe pipe = intel_crtc->pipe; - int level, id, max_level; + int level, max_level; + enum plane_id plane_id; uint32_t val; max_level = ilk_wm_max_level(dev_priv); - for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { - id = skl_wm_plane_id(intel_plane); - wm = &out->planes[id]; + for_each_plane_id_on_crtc(intel_crtc, plane_id) { + struct skl_plane_wm *wm = &out->planes[plane_id]; for (level = 0; level <= max_level; level++) { - if (id != PLANE_CURSOR) - val = I915_READ(PLANE_WM(pipe, id, level)); + if (plane_id != PLANE_CURSOR) + val = I915_READ(PLANE_WM(pipe, plane_id, level)); else val = I915_READ(CUR_WM(pipe, level)); skl_wm_level_from_reg_val(val, &wm->wm[level]); } - if (id != PLANE_CURSOR) - val = I915_READ(PLANE_WM_TRANS(pipe, id)); + if (plane_id != PLANE_CURSOR) + val = I915_READ(PLANE_WM_TRANS(pipe, plane_id)); else val = I915_READ(CUR_WM_TRANS(pipe)); @@ -4443,67 +4468,67 @@ static void vlv_read_wm_values(struct drm_i915_private *dev_priv, for_each_pipe(dev_priv, pipe) { tmp = I915_READ(VLV_DDL(pipe)); - wm->ddl[pipe].primary = + wm->ddl[pipe].plane[PLANE_PRIMARY] = (tmp >> DDL_PLANE_SHIFT) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); - wm->ddl[pipe].cursor = + wm->ddl[pipe].plane[PLANE_CURSOR] = (tmp >> DDL_CURSOR_SHIFT) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); - wm->ddl[pipe].sprite[0] = + wm->ddl[pipe].plane[PLANE_SPRITE0] = (tmp >> DDL_SPRITE_SHIFT(0)) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); - wm->ddl[pipe].sprite[1] = + wm->ddl[pipe].plane[PLANE_SPRITE1] = (tmp >> DDL_SPRITE_SHIFT(1)) & (DDL_PRECISION_HIGH | DRAIN_LATENCY_MASK); } tmp = I915_READ(DSPFW1); wm->sr.plane = _FW_WM(tmp, SR); - wm->pipe[PIPE_B].cursor = _FW_WM(tmp, CURSORB); - wm->pipe[PIPE_B].primary = _FW_WM_VLV(tmp, PLANEB); - wm->pipe[PIPE_A].primary = _FW_WM_VLV(tmp, PLANEA); + wm->pipe[PIPE_B].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORB); + wm->pipe[PIPE_B].plane[PLANE_PRIMARY] = _FW_WM_VLV(tmp, PLANEB); + wm->pipe[PIPE_A].plane[PLANE_PRIMARY] = _FW_WM_VLV(tmp, PLANEA); tmp = I915_READ(DSPFW2); - wm->pipe[PIPE_A].sprite[1] = _FW_WM_VLV(tmp, SPRITEB); - wm->pipe[PIPE_A].cursor = _FW_WM(tmp, CURSORA); - wm->pipe[PIPE_A].sprite[0] = _FW_WM_VLV(tmp, SPRITEA); + wm->pipe[PIPE_A].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITEB); + wm->pipe[PIPE_A].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORA); + wm->pipe[PIPE_A].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEA); tmp = I915_READ(DSPFW3); wm->sr.cursor = _FW_WM(tmp, CURSOR_SR); if (IS_CHERRYVIEW(dev_priv)) { tmp = I915_READ(DSPFW7_CHV); - wm->pipe[PIPE_B].sprite[1] = _FW_WM_VLV(tmp, SPRITED); - wm->pipe[PIPE_B].sprite[0] = _FW_WM_VLV(tmp, SPRITEC); + wm->pipe[PIPE_B].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITED); + wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEC); tmp = I915_READ(DSPFW8_CHV); - wm->pipe[PIPE_C].sprite[1] = _FW_WM_VLV(tmp, SPRITEF); - wm->pipe[PIPE_C].sprite[0] = _FW_WM_VLV(tmp, SPRITEE); + wm->pipe[PIPE_C].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITEF); + wm->pipe[PIPE_C].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEE); tmp = I915_READ(DSPFW9_CHV); - wm->pipe[PIPE_C].primary = _FW_WM_VLV(tmp, PLANEC); - wm->pipe[PIPE_C].cursor = _FW_WM(tmp, CURSORC); + wm->pipe[PIPE_C].plane[PLANE_PRIMARY] = _FW_WM_VLV(tmp, PLANEC); + wm->pipe[PIPE_C].plane[PLANE_CURSOR] = _FW_WM(tmp, CURSORC); tmp = I915_READ(DSPHOWM); wm->sr.plane |= _FW_WM(tmp, SR_HI) << 9; - wm->pipe[PIPE_C].sprite[1] |= _FW_WM(tmp, SPRITEF_HI) << 8; - wm->pipe[PIPE_C].sprite[0] |= _FW_WM(tmp, SPRITEE_HI) << 8; - wm->pipe[PIPE_C].primary |= _FW_WM(tmp, PLANEC_HI) << 8; - wm->pipe[PIPE_B].sprite[1] |= _FW_WM(tmp, SPRITED_HI) << 8; - wm->pipe[PIPE_B].sprite[0] |= _FW_WM(tmp, SPRITEC_HI) << 8; - wm->pipe[PIPE_B].primary |= _FW_WM(tmp, PLANEB_HI) << 8; - wm->pipe[PIPE_A].sprite[1] |= _FW_WM(tmp, SPRITEB_HI) << 8; - wm->pipe[PIPE_A].sprite[0] |= _FW_WM(tmp, SPRITEA_HI) << 8; - wm->pipe[PIPE_A].primary |= _FW_WM(tmp, PLANEA_HI) << 8; + wm->pipe[PIPE_C].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITEF_HI) << 8; + wm->pipe[PIPE_C].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEE_HI) << 8; + wm->pipe[PIPE_C].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEC_HI) << 8; + wm->pipe[PIPE_B].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITED_HI) << 8; + wm->pipe[PIPE_B].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEC_HI) << 8; + wm->pipe[PIPE_B].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEB_HI) << 8; + wm->pipe[PIPE_A].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITEB_HI) << 8; + wm->pipe[PIPE_A].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEA_HI) << 8; + wm->pipe[PIPE_A].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEA_HI) << 8; } else { tmp = I915_READ(DSPFW7); - wm->pipe[PIPE_B].sprite[1] = _FW_WM_VLV(tmp, SPRITED); - wm->pipe[PIPE_B].sprite[0] = _FW_WM_VLV(tmp, SPRITEC); + wm->pipe[PIPE_B].plane[PLANE_SPRITE1] = _FW_WM_VLV(tmp, SPRITED); + wm->pipe[PIPE_B].plane[PLANE_SPRITE0] = _FW_WM_VLV(tmp, SPRITEC); tmp = I915_READ(DSPHOWM); wm->sr.plane |= _FW_WM(tmp, SR_HI) << 9; - wm->pipe[PIPE_B].sprite[1] |= _FW_WM(tmp, SPRITED_HI) << 8; - wm->pipe[PIPE_B].sprite[0] |= _FW_WM(tmp, SPRITEC_HI) << 8; - wm->pipe[PIPE_B].primary |= _FW_WM(tmp, PLANEB_HI) << 8; - wm->pipe[PIPE_A].sprite[1] |= _FW_WM(tmp, SPRITEB_HI) << 8; - wm->pipe[PIPE_A].sprite[0] |= _FW_WM(tmp, SPRITEA_HI) << 8; - wm->pipe[PIPE_A].primary |= _FW_WM(tmp, PLANEA_HI) << 8; + wm->pipe[PIPE_B].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITED_HI) << 8; + wm->pipe[PIPE_B].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEC_HI) << 8; + wm->pipe[PIPE_B].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEB_HI) << 8; + wm->pipe[PIPE_A].plane[PLANE_SPRITE1] |= _FW_WM(tmp, SPRITEB_HI) << 8; + wm->pipe[PIPE_A].plane[PLANE_SPRITE0] |= _FW_WM(tmp, SPRITEA_HI) << 8; + wm->pipe[PIPE_A].plane[PLANE_PRIMARY] |= _FW_WM(tmp, PLANEA_HI) << 8; } } @@ -4520,21 +4545,8 @@ void vlv_wm_get_hw_state(struct drm_device *dev) vlv_read_wm_values(dev_priv, wm); - for_each_intel_plane(dev, plane) { - switch (plane->base.type) { - int sprite; - case DRM_PLANE_TYPE_CURSOR: - plane->wm.fifo_size = 63; - break; - case DRM_PLANE_TYPE_PRIMARY: - plane->wm.fifo_size = vlv_get_fifo_size(dev_priv, plane->pipe, 0); - break; - case DRM_PLANE_TYPE_OVERLAY: - sprite = plane->plane; - plane->wm.fifo_size = vlv_get_fifo_size(dev_priv, plane->pipe, sprite + 1); - break; - } - } + for_each_intel_plane(dev, plane) + plane->wm.fifo_size = vlv_get_fifo_size(plane); wm->cxsr = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN; wm->level = VLV_WM_LEVEL_PM2; @@ -4575,8 +4587,11 @@ void vlv_wm_get_hw_state(struct drm_device *dev) for_each_pipe(dev_priv, pipe) DRM_DEBUG_KMS("Initial watermarks: pipe %c, plane=%d, cursor=%d, sprite0=%d, sprite1=%d\n", - pipe_name(pipe), wm->pipe[pipe].primary, wm->pipe[pipe].cursor, - wm->pipe[pipe].sprite[0], wm->pipe[pipe].sprite[1]); + pipe_name(pipe), + wm->pipe[pipe].plane[PLANE_PRIMARY], + wm->pipe[pipe].plane[PLANE_CURSOR], + wm->pipe[pipe].plane[PLANE_SPRITE0], + wm->pipe[pipe].plane[PLANE_SPRITE1]); DRM_DEBUG_KMS("Initial watermarks: SR plane=%d, SR cursor=%d level=%d cxsr=%d\n", wm->sr.plane, wm->sr.cursor, wm->level, wm->cxsr); @@ -4996,8 +5011,18 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) if (dev_priv->rps.cur_freq <= val) return; - /* Wake up the media well, as that takes a lot less - * power than the Render well. */ + /* The punit delays the write of the frequency and voltage until it + * determines the GPU is awake. During normal usage we don't want to + * waste power changing the frequency if the GPU is sleeping (rc6). + * However, the GPU and driver is now idle and we do not want to delay + * switching to minimum voltage (reducing power whilst idle) as we do + * not expect to be woken in the near future and so must flush the + * change by waking the device. + * + * We choose to take the media powerwell (either would do to trick the + * punit into committing the voltage change) as that takes a lot less + * power than the render powerwell. + */ intel_uncore_forcewake_get(dev_priv, FORCEWAKE_MEDIA); valleyview_set_rps(dev_priv, val); intel_uncore_forcewake_put(dev_priv, FORCEWAKE_MEDIA); @@ -5219,7 +5244,7 @@ int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enable_rc6) if (!enable_rc6) return 0; - if (IS_BROXTON(dev_priv) && !bxt_check_bios_rc6_setup(dev_priv)) { + if (IS_GEN9_LP(dev_priv) && !bxt_check_bios_rc6_setup(dev_priv)) { DRM_INFO("RC6 disabled by BIOS\n"); return 0; } @@ -5253,7 +5278,7 @@ static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv) /* All of these values are in units of 50MHz */ /* static values from HW: RP0 > RP1 > RPn (min_freq) */ - if (IS_BROXTON(dev_priv)) { + if (IS_GEN9_LP(dev_priv)) { u32 rp_state_cap = I915_READ(BXT_RP_STATE_CAP); dev_priv->rps.rp0_freq = (rp_state_cap >> 16) & 0xff; dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff; @@ -5816,7 +5841,7 @@ static void valleyview_setup_pctx(struct drm_i915_private *dev_priv) int pcbr_offset; pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base; - pctx = i915_gem_object_create_stolen_for_preallocated(&dev_priv->drm, + pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv, pcbr_offset, I915_GTT_OFFSET_NONE, pctx_size); @@ -5833,7 +5858,7 @@ static void valleyview_setup_pctx(struct drm_i915_private *dev_priv) * overlap with other ranges, such as the frame buffer, protected * memory, or any other relevant ranges. */ - pctx = i915_gem_object_create_stolen(&dev_priv->drm, pctx_size); + pctx = i915_gem_object_create_stolen(dev_priv, pctx_size); if (!pctx) { DRM_DEBUG("not enough stolen space for PCTX, disabling\n"); goto out; @@ -6784,7 +6809,7 @@ static void __intel_autoenable_gt_powersave(struct work_struct *work) goto out; rcs = dev_priv->engine[RCS]; - if (rcs->last_context) + if (rcs->last_retired_context) goto out; if (!rcs->init_context) @@ -7595,8 +7620,6 @@ static void i85x_init_clock_gating(struct drm_i915_private *dev_priv) static void i830_init_clock_gating(struct drm_i915_private *dev_priv) { - I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE); - I915_WRITE(MEM_MODE, _MASKED_BIT_ENABLE(MEM_DISPLAY_A_TRICKLE_FEED_DISABLE) | _MASKED_BIT_ENABLE(MEM_DISPLAY_B_TRICKLE_FEED_DISABLE)); @@ -7633,7 +7656,7 @@ void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv) dev_priv->display.init_clock_gating = skylake_init_clock_gating; else if (IS_KABYLAKE(dev_priv)) dev_priv->display.init_clock_gating = kabylake_init_clock_gating; - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) dev_priv->display.init_clock_gating = bxt_init_clock_gating; else if (IS_BROADWELL(dev_priv)) dev_priv->display.init_clock_gating = broadwell_init_clock_gating; @@ -7651,9 +7674,9 @@ void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv) dev_priv->display.init_clock_gating = ironlake_init_clock_gating; else if (IS_G4X(dev_priv)) dev_priv->display.init_clock_gating = g4x_init_clock_gating; - else if (IS_CRESTLINE(dev_priv)) + else if (IS_I965GM(dev_priv)) dev_priv->display.init_clock_gating = crestline_init_clock_gating; - else if (IS_BROADWATER(dev_priv)) + else if (IS_I965G(dev_priv)) dev_priv->display.init_clock_gating = broadwater_init_clock_gating; else if (IS_GEN3(dev_priv)) dev_priv->display.init_clock_gating = gen3_init_clock_gating; @@ -7702,10 +7725,7 @@ void intel_init_pm(struct drm_i915_private *dev_priv) DRM_DEBUG_KMS("Failed to read display plane latency. " "Disable CxSR\n"); } - } else if (IS_CHERRYVIEW(dev_priv)) { - vlv_setup_wm_latency(dev_priv); - dev_priv->display.update_wm = vlv_update_wm; - } else if (IS_VALLEYVIEW(dev_priv)) { + } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { vlv_setup_wm_latency(dev_priv); dev_priv->display.update_wm = vlv_update_wm; } else if (IS_PINEVIEW(dev_priv)) { @@ -7849,6 +7869,7 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, } I915_WRITE_FW(GEN6_PCODE_DATA, val); + I915_WRITE_FW(GEN6_PCODE_DATA1, 0); I915_WRITE_FW(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox); if (intel_wait_for_register_fw(dev_priv, @@ -8041,10 +8062,8 @@ void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req) queue_work(req->i915->wq, &boost->work); } -void intel_pm_setup(struct drm_device *dev) +void intel_pm_setup(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - mutex_init(&dev_priv->rps.hw_lock); spin_lock_init(&dev_priv->rps.client_lock); diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c index c6be70686b4a..c3780d0d2baf 100644 --- a/drivers/gpu/drm/i915/intel_psr.c +++ b/drivers/gpu/drm/i915/intel_psr.c @@ -122,13 +122,26 @@ static void vlv_psr_setup_vsc(struct intel_dp *intel_dp) static void skl_psr_setup_su_vsc(struct intel_dp *intel_dp) { struct edp_vsc_psr psr_vsc; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); /* Prepare VSC Header for SU as per EDP 1.4 spec, Table 6.11 */ memset(&psr_vsc, 0, sizeof(psr_vsc)); psr_vsc.sdp_header.HB0 = 0; psr_vsc.sdp_header.HB1 = 0x7; - psr_vsc.sdp_header.HB2 = 0x3; - psr_vsc.sdp_header.HB3 = 0xb; + if (dev_priv->psr.colorimetry_support && + dev_priv->psr.y_cord_support) { + psr_vsc.sdp_header.HB2 = 0x5; + psr_vsc.sdp_header.HB3 = 0x13; + } else if (dev_priv->psr.y_cord_support) { + psr_vsc.sdp_header.HB2 = 0x4; + psr_vsc.sdp_header.HB3 = 0xe; + } else { + psr_vsc.sdp_header.HB2 = 0x3; + psr_vsc.sdp_header.HB3 = 0xc; + } + intel_psr_write_vsc(intel_dp, &psr_vsc); } @@ -196,7 +209,11 @@ static void hsw_psr_enable_sink(struct intel_dp *intel_dp) drm_dp_dpcd_writeb(&intel_dp->aux, DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF, DP_AUX_FRAME_SYNC_ENABLE); - + /* Enable ALPM at sink for psr2 */ + if (dev_priv->psr.psr2_support && dev_priv->psr.alpm) + drm_dp_dpcd_writeb(&intel_dp->aux, + DP_RECEIVER_ALPM_CONFIG, + DP_ALPM_ENABLE); if (dev_priv->psr.link_standby) drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE); @@ -248,7 +265,7 @@ static void vlv_psr_activate(struct intel_dp *intel_dp) VLV_EDP_PSR_ACTIVE_ENTRY); } -static void hsw_psr_enable_source(struct intel_dp *intel_dp) +static void intel_enable_source_psr1(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = dig_port->base.base.dev; @@ -299,14 +316,31 @@ static void hsw_psr_enable_source(struct intel_dp *intel_dp) val |= EDP_PSR_TP1_TP2_SEL; I915_WRITE(EDP_PSR_CTL, val); +} - if (!dev_priv->psr.psr2_support) - return; +static void intel_enable_source_psr2(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + /* + * Let's respect VBT in case VBT asks a higher idle_frame value. + * Let's use 6 as the minimum to cover all known cases including + * the off-by-one issue that HW has in some cases. Also there are + * cases where sink should be able to train + * with the 5 or 6 idle patterns. + */ + uint32_t idle_frames = max(6, dev_priv->vbt.psr.idle_frames); + uint32_t val; + + val = idle_frames << EDP_PSR_IDLE_FRAME_SHIFT; /* FIXME: selective update is probably totally broken because it doesn't * mesh at all with our frontbuffer tracking. And the hw alone isn't * good enough. */ - val = EDP_PSR2_ENABLE | EDP_SU_TRACK_ENABLE; + val |= EDP_PSR2_ENABLE | + EDP_SU_TRACK_ENABLE | + EDP_FRAMES_BEFORE_SU_ENTRY; if (dev_priv->vbt.psr.tp2_tp3_wakeup_time > 5) val |= EDP_PSR2_TP2_TIME_2500; @@ -320,6 +354,19 @@ static void hsw_psr_enable_source(struct intel_dp *intel_dp) I915_WRITE(EDP_PSR2_CTL, val); } +static void hsw_psr_enable_source(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + + /* psr1 and psr2 are mutually exclusive.*/ + if (dev_priv->psr.psr2_support) + intel_enable_source_psr2(intel_dp); + else + intel_enable_source_psr1(intel_dp); +} + static bool intel_psr_match_conditions(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); @@ -387,6 +434,22 @@ static bool intel_psr_match_conditions(struct intel_dp *intel_dp) return false; } + /* PSR2 is restricted to work with panel resolutions upto 3200x2000 */ + if (intel_crtc->config->pipe_src_w > 3200 || + intel_crtc->config->pipe_src_h > 2000) { + dev_priv->psr.psr2_support = false; + return false; + } + + /* + * FIXME:enable psr2 only for y-cordinate psr2 panels + * After gtc implementation , remove this restriction. + */ + if (!dev_priv->psr.y_cord_support && dev_priv->psr.psr2_support) { + DRM_DEBUG_KMS("PSR2 disabled, panel does not support Y coordinate\n"); + return false; + } + dev_priv->psr.source_ok = true; return true; } @@ -397,7 +460,10 @@ static void intel_psr_activate(struct intel_dp *intel_dp) struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - WARN_ON(I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE); + if (dev_priv->psr.psr2_support) + WARN_ON(I915_READ(EDP_PSR2_CTL) & EDP_PSR2_ENABLE); + else + WARN_ON(I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE); WARN_ON(dev_priv->psr.active); lockdep_assert_held(&dev_priv->psr.lock); @@ -426,6 +492,8 @@ void intel_psr_enable(struct intel_dp *intel_dp) struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc); + enum transcoder cpu_transcoder = crtc->config->cpu_transcoder; + u32 chicken; if (!HAS_PSR(dev_priv)) { DRM_DEBUG_KMS("PSR not supported on this platform\n"); @@ -449,26 +517,34 @@ void intel_psr_enable(struct intel_dp *intel_dp) dev_priv->psr.busy_frontbuffer_bits = 0; if (HAS_DDI(dev_priv)) { - hsw_psr_setup_vsc(intel_dp); - if (dev_priv->psr.psr2_support) { - /* PSR2 is restricted to work with panel resolutions upto 3200x2000 */ - if (crtc->config->pipe_src_w > 3200 || - crtc->config->pipe_src_h > 2000) - dev_priv->psr.psr2_support = false; - else - skl_psr_setup_su_vsc(intel_dp); + skl_psr_setup_su_vsc(intel_dp); + chicken = PSR2_VSC_ENABLE_PROG_HEADER; + if (dev_priv->psr.y_cord_support) + chicken |= PSR2_ADD_VERTICAL_LINE_COUNT; + I915_WRITE(CHICKEN_TRANS(cpu_transcoder), chicken); + I915_WRITE(EDP_PSR_DEBUG_CTL, + EDP_PSR_DEBUG_MASK_MEMUP | + EDP_PSR_DEBUG_MASK_HPD | + EDP_PSR_DEBUG_MASK_LPSP | + EDP_PSR_DEBUG_MASK_MAX_SLEEP | + EDP_PSR_DEBUG_MASK_DISP_REG_WRITE); + } else { + /* set up vsc header for psr1 */ + hsw_psr_setup_vsc(intel_dp); + /* + * Per Spec: Avoid continuous PSR exit by masking MEMUP + * and HPD. also mask LPSP to avoid dependency on other + * drivers that might block runtime_pm besides + * preventing other hw tracking issues now we can rely + * on frontbuffer tracking. + */ + I915_WRITE(EDP_PSR_DEBUG_CTL, + EDP_PSR_DEBUG_MASK_MEMUP | + EDP_PSR_DEBUG_MASK_HPD | + EDP_PSR_DEBUG_MASK_LPSP); } - /* - * Per Spec: Avoid continuous PSR exit by masking MEMUP and HPD. - * Also mask LPSP to avoid dependency on other drivers that - * might block runtime_pm besides preventing other hw tracking - * issues now we can rely on frontbuffer tracking. - */ - I915_WRITE(EDP_PSR_DEBUG_CTL, EDP_PSR_DEBUG_MASK_MEMUP | - EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP); - /* Enable PSR on the panel */ hsw_psr_enable_sink(intel_dp); @@ -544,20 +620,42 @@ static void hsw_psr_disable(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = to_i915(dev); if (dev_priv->psr.active) { - I915_WRITE(EDP_PSR_CTL, - I915_READ(EDP_PSR_CTL) & ~EDP_PSR_ENABLE); + i915_reg_t psr_ctl; + u32 psr_status_mask; + + if (dev_priv->psr.aux_frame_sync) + drm_dp_dpcd_writeb(&intel_dp->aux, + DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF, + 0); + + if (dev_priv->psr.psr2_support) { + psr_ctl = EDP_PSR2_CTL; + psr_status_mask = EDP_PSR2_STATUS_STATE_MASK; + + I915_WRITE(psr_ctl, + I915_READ(psr_ctl) & + ~(EDP_PSR2_ENABLE | EDP_SU_TRACK_ENABLE)); + + } else { + psr_ctl = EDP_PSR_STATUS_CTL; + psr_status_mask = EDP_PSR_STATUS_STATE_MASK; + + I915_WRITE(psr_ctl, + I915_READ(psr_ctl) & ~EDP_PSR_ENABLE); + } /* Wait till PSR is idle */ if (intel_wait_for_register(dev_priv, - EDP_PSR_STATUS_CTL, - EDP_PSR_STATUS_STATE_MASK, - 0, + psr_ctl, psr_status_mask, 0, 2000)) DRM_ERROR("Timed out waiting for PSR Idle State\n"); dev_priv->psr.active = false; } else { - WARN_ON(I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE); + if (dev_priv->psr.psr2_support) + WARN_ON(I915_READ(EDP_PSR2_CTL) & EDP_PSR2_ENABLE); + else + WARN_ON(I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE); } } @@ -608,13 +706,24 @@ static void intel_psr_work(struct work_struct *work) * and be ready for re-enable. */ if (HAS_DDI(dev_priv)) { - if (intel_wait_for_register(dev_priv, - EDP_PSR_STATUS_CTL, - EDP_PSR_STATUS_STATE_MASK, - 0, - 50)) { - DRM_ERROR("Timed out waiting for PSR Idle for re-enable\n"); - return; + if (dev_priv->psr.psr2_support) { + if (intel_wait_for_register(dev_priv, + EDP_PSR2_STATUS_CTL, + EDP_PSR2_STATUS_STATE_MASK, + 0, + 50)) { + DRM_ERROR("Timed out waiting for PSR2 Idle for re-enable\n"); + return; + } + } else { + if (intel_wait_for_register(dev_priv, + EDP_PSR_STATUS_CTL, + EDP_PSR_STATUS_STATE_MASK, + 0, + 50)) { + DRM_ERROR("Timed out waiting for PSR Idle for re-enable\n"); + return; + } } } else { if (intel_wait_for_register(dev_priv, @@ -656,11 +765,19 @@ static void intel_psr_exit(struct drm_i915_private *dev_priv) return; if (HAS_DDI(dev_priv)) { - val = I915_READ(EDP_PSR_CTL); - - WARN_ON(!(val & EDP_PSR_ENABLE)); - - I915_WRITE(EDP_PSR_CTL, val & ~EDP_PSR_ENABLE); + if (dev_priv->psr.aux_frame_sync) + drm_dp_dpcd_writeb(&intel_dp->aux, + DP_SINK_DEVICE_AUX_FRAME_SYNC_CONF, + 0); + if (dev_priv->psr.psr2_support) { + val = I915_READ(EDP_PSR2_CTL); + WARN_ON(!(val & EDP_PSR2_ENABLE)); + I915_WRITE(EDP_PSR2_CTL, val & ~EDP_PSR2_ENABLE); + } else { + val = I915_READ(EDP_PSR_CTL); + WARN_ON(!(val & EDP_PSR_ENABLE)); + I915_WRITE(EDP_PSR_CTL, val & ~EDP_PSR_ENABLE); + } } else { val = I915_READ(VLV_PSRCTL(pipe)); @@ -813,15 +930,13 @@ void intel_psr_flush(struct drm_i915_private *dev_priv, /** * intel_psr_init - Init basic PSR work and mutex. - * @dev: DRM device + * @dev_priv: i915 device private * * This function is called only once at driver load to initialize basic * PSR stuff. */ -void intel_psr_init(struct drm_device *dev) +void intel_psr_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); - dev_priv->psr_mmio_base = IS_HASWELL(dev_priv) ? HSW_EDP_PSR_BASE : BDW_EDP_PSR_BASE; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 91cb4c422ad5..91bc4abf5d3e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -599,10 +599,62 @@ out: static void reset_ring_common(struct intel_engine_cs *engine, struct drm_i915_gem_request *request) { - struct intel_ring *ring = request->ring; + /* Try to restore the logical GPU state to match the continuation + * of the request queue. If we skip the context/PD restore, then + * the next request may try to execute assuming that its context + * is valid and loaded on the GPU and so may try to access invalid + * memory, prompting repeated GPU hangs. + * + * If the request was guilty, we still restore the logical state + * in case the next request requires it (e.g. the aliasing ppgtt), + * but skip over the hung batch. + * + * If the request was innocent, we try to replay the request with + * the restored context. + */ + if (request) { + struct drm_i915_private *dev_priv = request->i915; + struct intel_context *ce = &request->ctx->engine[engine->id]; + struct i915_hw_ppgtt *ppgtt; + + /* FIXME consider gen8 reset */ + + if (ce->state) { + I915_WRITE(CCID, + i915_ggtt_offset(ce->state) | + BIT(8) /* must be set! */ | + CCID_EXTENDED_STATE_SAVE | + CCID_EXTENDED_STATE_RESTORE | + CCID_EN); + } - ring->head = request->postfix; - ring->last_retired_head = -1; + ppgtt = request->ctx->ppgtt ?: engine->i915->mm.aliasing_ppgtt; + if (ppgtt) { + u32 pd_offset = ppgtt->pd.base.ggtt_offset << 10; + + I915_WRITE(RING_PP_DIR_DCLV(engine), PP_DIR_DCLV_2G); + I915_WRITE(RING_PP_DIR_BASE(engine), pd_offset); + + /* Wait for the PD reload to complete */ + if (intel_wait_for_register(dev_priv, + RING_PP_DIR_BASE(engine), + BIT(0), 0, + 10)) + DRM_ERROR("Wait for reload of ppgtt page-directory timed out\n"); + + ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine); + } + + /* If the rq hung, jump to its breadcrumb and skip the batch */ + if (request->fence.error == -EIO) { + struct intel_ring *ring = request->ring; + + ring->head = request->postfix; + ring->last_retired_head = -1; + } + } else { + engine->legacy_active_context = NULL; + } } static int intel_ring_workarounds_emit(struct drm_i915_gem_request *req) @@ -1728,7 +1780,7 @@ static int init_status_page(struct intel_engine_cs *engine) void *vaddr; int ret; - obj = i915_gem_object_create_internal(engine->i915, 4096); + obj = i915_gem_object_create_internal(engine->i915, PAGE_SIZE); if (IS_ERR(obj)) { DRM_ERROR("Failed to allocate status page\n"); return PTR_ERR(obj); @@ -1738,7 +1790,7 @@ static int init_status_page(struct intel_engine_cs *engine) if (ret) goto err; - vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL); + vma = i915_vma_instance(obj, &engine->i915->ggtt.base, NULL); if (IS_ERR(vma)) { ret = PTR_ERR(vma); goto err; @@ -1769,7 +1821,7 @@ static int init_status_page(struct intel_engine_cs *engine) engine->status_page.vma = vma; engine->status_page.ggtt_offset = i915_ggtt_offset(vma); - engine->status_page.page_addr = memset(vaddr, 0, 4096); + engine->status_page.page_addr = memset(vaddr, 0, PAGE_SIZE); DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n", engine->name, i915_ggtt_offset(vma)); @@ -1797,10 +1849,9 @@ static int init_phys_status_page(struct intel_engine_cs *engine) return 0; } -int intel_ring_pin(struct intel_ring *ring) +int intel_ring_pin(struct intel_ring *ring, unsigned int offset_bias) { - /* Ring wraparound at offset 0 sometimes hangs. No idea why. */ - unsigned int flags = PIN_GLOBAL | PIN_OFFSET_BIAS | 4096; + unsigned int flags; enum i915_map_type map; struct i915_vma *vma = ring->vma; void *addr; @@ -1810,6 +1861,9 @@ int intel_ring_pin(struct intel_ring *ring) map = HAS_LLC(ring->engine->i915) ? I915_MAP_WB : I915_MAP_WC; + flags = PIN_GLOBAL; + if (offset_bias) + flags |= PIN_OFFSET_BIAS | offset_bias; if (vma->obj->stolen) flags |= PIN_MAPPABLE; @@ -1861,16 +1915,16 @@ intel_ring_create_vma(struct drm_i915_private *dev_priv, int size) struct drm_i915_gem_object *obj; struct i915_vma *vma; - obj = i915_gem_object_create_stolen(&dev_priv->drm, size); + obj = i915_gem_object_create_stolen(dev_priv, size); if (!obj) - obj = i915_gem_object_create(&dev_priv->drm, size); + obj = i915_gem_object_create(dev_priv, size); if (IS_ERR(obj)) return ERR_CAST(obj); /* mark ring buffers as read-only from GPU side by default */ obj->gt_ro = 1; - vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + vma = i915_vma_instance(obj, &dev_priv->ggtt.base, NULL); if (IS_ERR(vma)) goto err; @@ -1904,7 +1958,7 @@ intel_engine_create_ring(struct intel_engine_cs *engine, int size) * of the buffer. */ ring->effective_size = size; - if (IS_I830(engine->i915) || IS_845G(engine->i915)) + if (IS_I830(engine->i915) || IS_I845G(engine->i915)) ring->effective_size -= 2 * CACHELINE_BYTES; ring->last_retired_head = -1; @@ -1931,8 +1985,26 @@ intel_ring_free(struct intel_ring *ring) kfree(ring); } -static int intel_ring_context_pin(struct i915_gem_context *ctx, - struct intel_engine_cs *engine) +static int context_pin(struct i915_gem_context *ctx, unsigned int flags) +{ + struct i915_vma *vma = ctx->engine[RCS].state; + int ret; + + /* Clear this page out of any CPU caches for coherent swap-in/out. + * We only want to do this on the first bind so that we do not stall + * on an active context (which by nature is already on the GPU). + */ + if (!(vma->flags & I915_VMA_GLOBAL_BIND)) { + ret = i915_gem_object_set_to_gtt_domain(vma->obj, false); + if (ret) + return ret; + } + + return i915_vma_pin(vma, 0, ctx->ggtt_alignment, PIN_GLOBAL | flags); +} + +static int intel_ring_context_pin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) { struct intel_context *ce = &ctx->engine[engine->id]; int ret; @@ -1943,13 +2015,15 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx, return 0; if (ce->state) { - struct i915_vma *vma; + unsigned int flags; - vma = i915_gem_context_pin_legacy(ctx, PIN_HIGH); - if (IS_ERR(vma)) { - ret = PTR_ERR(vma); + flags = 0; + if (i915_gem_context_is_kernel(ctx)) + flags = PIN_HIGH; + + ret = context_pin(ctx, flags); + if (ret) goto error; - } } /* The kernel context is only used as a placeholder for flushing the @@ -1959,7 +2033,7 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx, * as during eviction we cannot allocate and pin the renderstate in * order to initialise the context. */ - if (ctx == ctx->i915->kernel_context) + if (i915_gem_context_is_kernel(ctx)) ce->initialised = true; i915_gem_context_get(ctx); @@ -1970,12 +2044,13 @@ error: return ret; } -static void intel_ring_context_unpin(struct i915_gem_context *ctx, - struct intel_engine_cs *engine) +static void intel_ring_context_unpin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) { struct intel_context *ce = &ctx->engine[engine->id]; lockdep_assert_held(&ctx->i915->drm.struct_mutex); + GEM_BUG_ON(ce->pin_count == 0); if (--ce->pin_count) return; @@ -2000,17 +2075,6 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine) if (ret) goto error; - /* We may need to do things with the shrinker which - * require us to immediately switch back to the default - * context. This can cause a problem as pinning the - * default context also requires GTT space which may not - * be available. To avoid this we always pin the default - * context. - */ - ret = intel_ring_context_pin(dev_priv->kernel_context, engine); - if (ret) - goto error; - ring = intel_engine_create_ring(engine, 32 * PAGE_SIZE); if (IS_ERR(ring)) { ret = PTR_ERR(ring); @@ -2028,7 +2092,8 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine) goto error; } - ret = intel_ring_pin(ring); + /* Ring wraparound at offset 0 sometimes hangs. No idea why. */ + ret = intel_ring_pin(ring, I915_GTT_PAGE_SIZE); if (ret) { intel_ring_free(ring); goto error; @@ -2069,8 +2134,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine) intel_engine_cleanup_common(engine); - intel_ring_context_unpin(dev_priv->kernel_context, engine); - engine->i915 = NULL; dev_priv->engine[engine->id] = NULL; kfree(engine); @@ -2087,16 +2150,19 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv) } } -int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request) +static int ring_request_alloc(struct drm_i915_gem_request *request) { int ret; + GEM_BUG_ON(!request->ctx->engine[request->engine->id].pin_count); + /* Flush enough space to reduce the likelihood of waiting after * we start building the request - in which case we will just * have to repeat work. */ request->reserved_space += LEGACY_REQUEST_SIZE; + GEM_BUG_ON(!request->engine->buffer); request->ring = request->engine->buffer; ret = intel_ring_begin(request, 0); @@ -2444,11 +2510,11 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv, if (INTEL_GEN(dev_priv) >= 8 && !dev_priv->semaphore) { struct i915_vma *vma; - obj = i915_gem_object_create(&dev_priv->drm, 4096); + obj = i915_gem_object_create(dev_priv, PAGE_SIZE); if (IS_ERR(obj)) goto err; - vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL); + vma = i915_vma_instance(obj, &dev_priv->ggtt.base, NULL); if (IS_ERR(vma)) goto err_obj; @@ -2576,6 +2642,11 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv, engine->init_hw = init_ring_common; engine->reset_hw = reset_ring_common; + engine->context_pin = intel_ring_context_pin; + engine->context_unpin = intel_ring_context_unpin; + + engine->request_alloc = ring_request_alloc; + engine->emit_breadcrumb = i9xx_emit_breadcrumb; engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz; if (i915.semaphores) { @@ -2600,7 +2671,7 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv, engine->emit_bb_start = gen6_emit_bb_start; else if (INTEL_GEN(dev_priv) >= 4) engine->emit_bb_start = i965_emit_bb_start; - else if (IS_I830(dev_priv) || IS_845G(dev_priv)) + else if (IS_I830(dev_priv) || IS_I845G(dev_priv)) engine->emit_bb_start = i830_emit_bb_start; else engine->emit_bb_start = i915_emit_bb_start; @@ -2656,7 +2727,7 @@ int intel_init_render_ring_buffer(struct intel_engine_cs *engine) return ret; if (INTEL_GEN(dev_priv) >= 6) { - ret = intel_engine_create_scratch(engine, 4096); + ret = intel_engine_create_scratch(engine, PAGE_SIZE); if (ret) return ret; } else if (HAS_BROKEN_CS_TLB(dev_priv)) { diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 3466b4e77e7c..79c2b8d72322 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -65,14 +65,37 @@ struct intel_hw_status_page { GEN8_SEMAPHORE_OFFSET(from, (__ring)->id)) enum intel_engine_hangcheck_action { - HANGCHECK_IDLE = 0, - HANGCHECK_WAIT, - HANGCHECK_ACTIVE, - HANGCHECK_KICK, - HANGCHECK_HUNG, + ENGINE_IDLE = 0, + ENGINE_WAIT, + ENGINE_ACTIVE_SEQNO, + ENGINE_ACTIVE_HEAD, + ENGINE_ACTIVE_SUBUNITS, + ENGINE_WAIT_KICK, + ENGINE_DEAD, }; -#define HANGCHECK_SCORE_RING_HUNG 31 +static inline const char * +hangcheck_action_to_str(const enum intel_engine_hangcheck_action a) +{ + switch (a) { + case ENGINE_IDLE: + return "idle"; + case ENGINE_WAIT: + return "wait"; + case ENGINE_ACTIVE_SEQNO: + return "active seqno"; + case ENGINE_ACTIVE_HEAD: + return "active head"; + case ENGINE_ACTIVE_SUBUNITS: + return "active subunits"; + case ENGINE_WAIT_KICK: + return "wait kick"; + case ENGINE_DEAD: + return "dead"; + } + + return "unknown"; +} #define I915_MAX_SLICES 3 #define I915_MAX_SUBSLICES 3 @@ -104,10 +127,11 @@ struct intel_instdone { struct intel_engine_hangcheck { u64 acthd; u32 seqno; - int score; enum intel_engine_hangcheck_action action; + unsigned long action_timestamp; int deadlock; struct intel_instdone instdone; + bool stalled; }; struct intel_ring { @@ -242,6 +266,11 @@ struct intel_engine_cs { void (*reset_hw)(struct intel_engine_cs *engine, struct drm_i915_gem_request *req); + int (*context_pin)(struct intel_engine_cs *engine, + struct i915_gem_context *ctx); + void (*context_unpin)(struct intel_engine_cs *engine, + struct i915_gem_context *ctx); + int (*request_alloc)(struct drm_i915_gem_request *req); int (*init_context)(struct drm_i915_gem_request *req); int (*emit_flush)(struct drm_i915_gem_request *request, @@ -355,7 +384,24 @@ struct intel_engine_cs { bool preempt_wa; u32 ctx_desc_template; - struct i915_gem_context *last_context; + /* Contexts are pinned whilst they are active on the GPU. The last + * context executed remains active whilst the GPU is idle - the + * switch away and write to the context object only occurs on the + * next execution. Contexts are only unpinned on retirement of the + * following request ensuring that we can always write to the object + * on the context switch even after idling. Across suspend, we switch + * to the kernel context and trash it as the save may not happen + * before the hardware is powered down. + */ + struct i915_gem_context *last_retired_context; + + /* We track the current MI_SET_CONTEXT in order to eliminate + * redudant context switches. This presumes that requests are not + * reordered! Or when they are the tracking is updated along with + * the emission of individual requests into the legacy command + * stream (ring). + */ + struct i915_gem_context *legacy_active_context; struct intel_engine_hangcheck hangcheck; @@ -437,7 +483,7 @@ intel_write_status_page(struct intel_engine_cs *engine, struct intel_ring * intel_engine_create_ring(struct intel_engine_cs *engine, int size); -int intel_ring_pin(struct intel_ring *ring); +int intel_ring_pin(struct intel_ring *ring, unsigned int offset_bias); void intel_ring_unpin(struct intel_ring *ring); void intel_ring_free(struct intel_ring *ring); @@ -446,8 +492,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine); void intel_legacy_submission_resume(struct drm_i915_private *dev_priv); -int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request); - int __must_check intel_ring_begin(struct drm_i915_gem_request *req, int n); int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req); diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 87b4af092d54..c0b7e95b5b8e 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -453,6 +453,57 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv, BIT(POWER_DOMAIN_AUX_C) | \ BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_POWERWELL_2_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_TRANSCODER_A) | \ + BIT(POWER_DOMAIN_PIPE_B) | \ + BIT(POWER_DOMAIN_TRANSCODER_B) | \ + BIT(POWER_DOMAIN_PIPE_C) | \ + BIT(POWER_DOMAIN_TRANSCODER_C) | \ + BIT(POWER_DOMAIN_PIPE_B_PANEL_FITTER) | \ + BIT(POWER_DOMAIN_PIPE_C_PANEL_FITTER) | \ + BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \ + BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \ + BIT(POWER_DOMAIN_AUX_B) | \ + BIT(POWER_DOMAIN_AUX_C) | \ + BIT(POWER_DOMAIN_AUDIO) | \ + BIT(POWER_DOMAIN_VGA) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_DDI_A_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_A_LANES) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_DDI_B_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_DDI_C_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DPIO_CMN_A_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_A_LANES) | \ + BIT(POWER_DOMAIN_AUX_A) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DPIO_CMN_B_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_B_LANES) | \ + BIT(POWER_DOMAIN_AUX_B) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DPIO_CMN_C_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_C_LANES) | \ + BIT(POWER_DOMAIN_AUX_C) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_AUX_A_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_AUX_A) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_AUX_B_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_AUX_B) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_AUX_C_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_AUX_C) | \ + BIT(POWER_DOMAIN_INIT)) +#define GLK_DISPLAY_DC_OFF_POWER_DOMAINS ( \ + GLK_DISPLAY_POWERWELL_2_POWER_DOMAINS | \ + BIT(POWER_DOMAIN_MODESET) | \ + BIT(POWER_DOMAIN_AUX_A) | \ + BIT(POWER_DOMAIN_INIT)) + static void assert_can_enable_dc9(struct drm_i915_private *dev_priv) { WARN_ONCE((I915_READ(DC_STATE_EN) & DC_STATE_EN_DC9), @@ -530,7 +581,7 @@ static u32 gen9_dc_mask(struct drm_i915_private *dev_priv) u32 mask; mask = DC_STATE_EN_UPTO_DC5; - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) mask |= DC_STATE_EN_DC9; else mask |= DC_STATE_EN_UPTO_DC6; @@ -694,7 +745,7 @@ gen9_sanitize_power_well_requests(struct drm_i915_private *dev_priv, } static void skl_set_power_well(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well, bool enable) + struct i915_power_well *power_well, bool enable) { uint32_t tmp, fuse_status; uint32_t req_mask, state_mask; @@ -720,11 +771,14 @@ static void skl_set_power_well(struct drm_i915_private *dev_priv, return; } break; - case SKL_DISP_PW_DDI_A_E: + case SKL_DISP_PW_MISC_IO: + case SKL_DISP_PW_DDI_A_E: /* GLK_DISP_PW_DDI_A */ case SKL_DISP_PW_DDI_B: case SKL_DISP_PW_DDI_C: case SKL_DISP_PW_DDI_D: - case SKL_DISP_PW_MISC_IO: + case GLK_DISP_PW_AUX_A: + case GLK_DISP_PW_AUX_B: + case GLK_DISP_PW_AUX_C: break; default: WARN(1, "Unknown power well %lu\n", power_well->id); @@ -884,6 +938,12 @@ static void bxt_verify_ddi_phy_power_wells(struct drm_i915_private *dev_priv) power_well = lookup_power_well(dev_priv, BXT_DPIO_CMN_BC); if (power_well->count > 0) bxt_ddi_phy_verify_state(dev_priv, power_well->data); + + if (IS_GEMINILAKE(dev_priv)) { + power_well = lookup_power_well(dev_priv, GLK_DPIO_CMN_C); + if (power_well->count > 0) + bxt_ddi_phy_verify_state(dev_priv, power_well->data); + } } static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv, @@ -911,7 +971,7 @@ static void gen9_dc_off_power_well_enable(struct drm_i915_private *dev_priv, gen9_assert_dbuf_enabled(dev_priv); - if (IS_BROXTON(dev_priv)) + if (IS_GEN9_LP(dev_priv)) bxt_verify_ddi_phy_power_wells(dev_priv); } @@ -2161,6 +2221,91 @@ static struct i915_power_well bxt_power_wells[] = { }, }; +static struct i915_power_well glk_power_wells[] = { + { + .name = "always-on", + .always_on = 1, + .domains = POWER_DOMAIN_MASK, + .ops = &i9xx_always_on_power_well_ops, + }, + { + .name = "power well 1", + /* Handled by the DMC firmware */ + .domains = 0, + .ops = &skl_power_well_ops, + .id = SKL_DISP_PW_1, + }, + { + .name = "DC off", + .domains = GLK_DISPLAY_DC_OFF_POWER_DOMAINS, + .ops = &gen9_dc_off_power_well_ops, + .id = SKL_DISP_PW_DC_OFF, + }, + { + .name = "power well 2", + .domains = GLK_DISPLAY_POWERWELL_2_POWER_DOMAINS, + .ops = &skl_power_well_ops, + .id = SKL_DISP_PW_2, + }, + { + .name = "dpio-common-a", + .domains = GLK_DPIO_CMN_A_POWER_DOMAINS, + .ops = &bxt_dpio_cmn_power_well_ops, + .id = BXT_DPIO_CMN_A, + .data = DPIO_PHY1, + }, + { + .name = "dpio-common-b", + .domains = GLK_DPIO_CMN_B_POWER_DOMAINS, + .ops = &bxt_dpio_cmn_power_well_ops, + .id = BXT_DPIO_CMN_BC, + .data = DPIO_PHY0, + }, + { + .name = "dpio-common-c", + .domains = GLK_DPIO_CMN_C_POWER_DOMAINS, + .ops = &bxt_dpio_cmn_power_well_ops, + .id = GLK_DPIO_CMN_C, + .data = DPIO_PHY2, + }, + { + .name = "AUX A", + .domains = GLK_DISPLAY_AUX_A_POWER_DOMAINS, + .ops = &skl_power_well_ops, + .id = GLK_DISP_PW_AUX_A, + }, + { + .name = "AUX B", + .domains = GLK_DISPLAY_AUX_B_POWER_DOMAINS, + .ops = &skl_power_well_ops, + .id = GLK_DISP_PW_AUX_B, + }, + { + .name = "AUX C", + .domains = GLK_DISPLAY_AUX_C_POWER_DOMAINS, + .ops = &skl_power_well_ops, + .id = GLK_DISP_PW_AUX_C, + }, + { + .name = "DDI A power well", + .domains = GLK_DISPLAY_DDI_A_POWER_DOMAINS, + .ops = &skl_power_well_ops, + .id = GLK_DISP_PW_DDI_A, + }, + { + .name = "DDI B power well", + .domains = GLK_DISPLAY_DDI_B_POWER_DOMAINS, + .ops = &skl_power_well_ops, + .id = SKL_DISP_PW_DDI_B, + }, + { + .name = "DDI C power well", + .domains = GLK_DISPLAY_DDI_C_POWER_DOMAINS, + .ops = &skl_power_well_ops, + .id = SKL_DISP_PW_DDI_C, + }, +}; + static int sanitize_disable_power_well_option(const struct drm_i915_private *dev_priv, int disable_power_well) @@ -2181,7 +2326,7 @@ static uint32_t get_allowed_dc_mask(const struct drm_i915_private *dev_priv, if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { max_dc = 2; mask = 0; - } else if (IS_BROXTON(dev_priv)) { + } else if (IS_GEN9_LP(dev_priv)) { max_dc = 1; /* * DC9 has a separate HW flow from the rest of the DC states, @@ -2257,6 +2402,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) set_power_wells(power_domains, skl_power_wells); } else if (IS_BROXTON(dev_priv)) { set_power_wells(power_domains, bxt_power_wells); + } else if (IS_GEMINILAKE(dev_priv)) { + set_power_wells(power_domains, glk_power_wells); } else if (IS_CHERRYVIEW(dev_priv)) { set_power_wells(power_domains, chv_power_wells); } else if (IS_VALLEYVIEW(dev_priv)) { @@ -2585,7 +2732,7 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume) if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { skl_display_core_init(dev_priv, resume); - } else if (IS_BROXTON(dev_priv)) { + } else if (IS_GEN9_LP(dev_priv)) { bxt_display_core_init(dev_priv, resume); } else if (IS_CHERRYVIEW(dev_priv)) { mutex_lock(&power_domains->lock); @@ -2624,7 +2771,7 @@ void intel_power_domains_suspend(struct drm_i915_private *dev_priv) if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) skl_display_core_uninit(dev_priv); - else if (IS_BROXTON(dev_priv)) + else if (IS_GEN9_LP(dev_priv)) bxt_display_core_uninit(dev_priv); } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 27808e91cb5a..2ad13903a054 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1296,7 +1296,7 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder, if (INTEL_GEN(dev_priv) >= 4) { /* done in crtc_mode_set as the dpll_md reg must be written early */ } else if (IS_I945G(dev_priv) || IS_I945GM(dev_priv) || - IS_G33(dev_priv)) { + IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) { /* done in crtc_mode_set as it lives inside the dpll register */ } else { sdvox |= (crtc_state->pixel_multiplier - 1) @@ -2342,9 +2342,9 @@ intel_sdvo_is_hdmi_connector(struct intel_sdvo *intel_sdvo, int device) } static u8 -intel_sdvo_get_slave_addr(struct drm_device *dev, struct intel_sdvo *sdvo) +intel_sdvo_get_slave_addr(struct drm_i915_private *dev_priv, + struct intel_sdvo *sdvo) { - struct drm_i915_private *dev_priv = to_i915(dev); struct sdvo_device_mapping *my_mapping, *other_mapping; if (sdvo->port == PORT_B) { @@ -2934,9 +2934,9 @@ static const struct i2c_algorithm intel_sdvo_ddc_proxy = { static bool intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo, - struct drm_device *dev) + struct drm_i915_private *dev_priv) { - struct pci_dev *pdev = dev->pdev; + struct pci_dev *pdev = dev_priv->drm.pdev; sdvo->ddc.owner = THIS_MODULE; sdvo->ddc.class = I2C_CLASS_DDC; @@ -2957,10 +2957,9 @@ static void assert_sdvo_port_valid(const struct drm_i915_private *dev_priv, WARN_ON(port != PORT_B && port != PORT_C); } -bool intel_sdvo_init(struct drm_device *dev, +bool intel_sdvo_init(struct drm_i915_private *dev_priv, i915_reg_t sdvo_reg, enum port port) { - struct drm_i915_private *dev_priv = to_i915(dev); struct intel_encoder *intel_encoder; struct intel_sdvo *intel_sdvo; int i; @@ -2973,16 +2972,18 @@ bool intel_sdvo_init(struct drm_device *dev, intel_sdvo->sdvo_reg = sdvo_reg; intel_sdvo->port = port; - intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, intel_sdvo) >> 1; + intel_sdvo->slave_addr = + intel_sdvo_get_slave_addr(dev_priv, intel_sdvo) >> 1; intel_sdvo_select_i2c_bus(dev_priv, intel_sdvo); - if (!intel_sdvo_init_ddc_proxy(intel_sdvo, dev)) + if (!intel_sdvo_init_ddc_proxy(intel_sdvo, dev_priv)) goto err_i2c_bus; /* encoder type will be decided later */ intel_encoder = &intel_sdvo->base; intel_encoder->type = INTEL_OUTPUT_SDVO; intel_encoder->port = port; - drm_encoder_init(dev, &intel_encoder->base, &intel_sdvo_enc_funcs, 0, + drm_encoder_init(&dev_priv->drm, &intel_encoder->base, + &intel_sdvo_enc_funcs, 0, "SDVO %c", port_name(port)); /* Read the regs to test if we can talk to the device */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 242a73e66d82..9ef54688872a 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -203,8 +203,8 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(drm_plane); struct drm_framebuffer *fb = plane_state->base.fb; - const int pipe = intel_plane->pipe; - const int plane = intel_plane->plane + 1; + enum plane_id plane_id = intel_plane->id; + enum pipe pipe = intel_plane->pipe; u32 plane_ctl; const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; u32 surf_addr = plane_state->main.offset; @@ -223,15 +223,15 @@ skl_update_plane(struct drm_plane *drm_plane, PLANE_CTL_PIPE_GAMMA_ENABLE | PLANE_CTL_PIPE_CSC_ENABLE; - plane_ctl |= skl_plane_ctl_format(fb->pixel_format); + plane_ctl |= skl_plane_ctl_format(fb->format->format); plane_ctl |= skl_plane_ctl_tiling(fb->modifier); plane_ctl |= skl_plane_ctl_rotation(rotation); if (key->flags) { - I915_WRITE(PLANE_KEYVAL(pipe, plane), key->min_value); - I915_WRITE(PLANE_KEYMAX(pipe, plane), key->max_value); - I915_WRITE(PLANE_KEYMSK(pipe, plane), key->channel_mask); + I915_WRITE(PLANE_KEYVAL(pipe, plane_id), key->min_value); + I915_WRITE(PLANE_KEYMAX(pipe, plane_id), key->max_value); + I915_WRITE(PLANE_KEYMSK(pipe, plane_id), key->channel_mask); } if (key->flags & I915_SET_COLORKEY_DESTINATION) @@ -245,36 +245,36 @@ skl_update_plane(struct drm_plane *drm_plane, crtc_w--; crtc_h--; - I915_WRITE(PLANE_OFFSET(pipe, plane), (y << 16) | x); - I915_WRITE(PLANE_STRIDE(pipe, plane), stride); - I915_WRITE(PLANE_SIZE(pipe, plane), (src_h << 16) | src_w); + I915_WRITE(PLANE_OFFSET(pipe, plane_id), (y << 16) | x); + I915_WRITE(PLANE_STRIDE(pipe, plane_id), stride); + I915_WRITE(PLANE_SIZE(pipe, plane_id), (src_h << 16) | src_w); /* program plane scaler */ if (plane_state->scaler_id >= 0) { int scaler_id = plane_state->scaler_id; const struct intel_scaler *scaler; - DRM_DEBUG_KMS("plane = %d PS_PLANE_SEL(plane) = 0x%x\n", plane, - PS_PLANE_SEL(plane)); + DRM_DEBUG_KMS("plane = %d PS_PLANE_SEL(plane) = 0x%x\n", + plane_id, PS_PLANE_SEL(plane_id)); scaler = &crtc_state->scaler_state.scalers[scaler_id]; I915_WRITE(SKL_PS_CTRL(pipe, scaler_id), - PS_SCALER_EN | PS_PLANE_SEL(plane) | scaler->mode); + PS_SCALER_EN | PS_PLANE_SEL(plane_id) | scaler->mode); I915_WRITE(SKL_PS_PWR_GATE(pipe, scaler_id), 0); I915_WRITE(SKL_PS_WIN_POS(pipe, scaler_id), (crtc_x << 16) | crtc_y); I915_WRITE(SKL_PS_WIN_SZ(pipe, scaler_id), ((crtc_w + 1) << 16)|(crtc_h + 1)); - I915_WRITE(PLANE_POS(pipe, plane), 0); + I915_WRITE(PLANE_POS(pipe, plane_id), 0); } else { - I915_WRITE(PLANE_POS(pipe, plane), (crtc_y << 16) | crtc_x); + I915_WRITE(PLANE_POS(pipe, plane_id), (crtc_y << 16) | crtc_x); } - I915_WRITE(PLANE_CTL(pipe, plane), plane_ctl); - I915_WRITE(PLANE_SURF(pipe, plane), + I915_WRITE(PLANE_CTL(pipe, plane_id), plane_ctl); + I915_WRITE(PLANE_SURF(pipe, plane_id), intel_plane_ggtt_offset(plane_state) + surf_addr); - POSTING_READ(PLANE_SURF(pipe, plane)); + POSTING_READ(PLANE_SURF(pipe, plane_id)); } static void @@ -283,20 +283,20 @@ skl_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc) struct drm_device *dev = dplane->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(dplane); - const int pipe = intel_plane->pipe; - const int plane = intel_plane->plane + 1; + enum plane_id plane_id = intel_plane->id; + enum pipe pipe = intel_plane->pipe; - I915_WRITE(PLANE_CTL(pipe, plane), 0); + I915_WRITE(PLANE_CTL(pipe, plane_id), 0); - I915_WRITE(PLANE_SURF(pipe, plane), 0); - POSTING_READ(PLANE_SURF(pipe, plane)); + I915_WRITE(PLANE_SURF(pipe, plane_id), 0); + POSTING_READ(PLANE_SURF(pipe, plane_id)); } static void chv_update_csc(struct intel_plane *intel_plane, uint32_t format) { struct drm_i915_private *dev_priv = to_i915(intel_plane->base.dev); - int plane = intel_plane->plane; + enum plane_id plane_id = intel_plane->id; /* Seems RGB data bypasses the CSC always */ if (!format_is_yuv(format)) @@ -312,23 +312,23 @@ chv_update_csc(struct intel_plane *intel_plane, uint32_t format) * Cb and Cr apparently come in as signed already, so no * need for any offset. For Y we need to remove the offset. */ - I915_WRITE(SPCSCYGOFF(plane), SPCSC_OOFF(0) | SPCSC_IOFF(-64)); - I915_WRITE(SPCSCCBOFF(plane), SPCSC_OOFF(0) | SPCSC_IOFF(0)); - I915_WRITE(SPCSCCROFF(plane), SPCSC_OOFF(0) | SPCSC_IOFF(0)); - - I915_WRITE(SPCSCC01(plane), SPCSC_C1(4769) | SPCSC_C0(6537)); - I915_WRITE(SPCSCC23(plane), SPCSC_C1(-3330) | SPCSC_C0(0)); - I915_WRITE(SPCSCC45(plane), SPCSC_C1(-1605) | SPCSC_C0(4769)); - I915_WRITE(SPCSCC67(plane), SPCSC_C1(4769) | SPCSC_C0(0)); - I915_WRITE(SPCSCC8(plane), SPCSC_C0(8263)); - - I915_WRITE(SPCSCYGICLAMP(plane), SPCSC_IMAX(940) | SPCSC_IMIN(64)); - I915_WRITE(SPCSCCBICLAMP(plane), SPCSC_IMAX(448) | SPCSC_IMIN(-448)); - I915_WRITE(SPCSCCRICLAMP(plane), SPCSC_IMAX(448) | SPCSC_IMIN(-448)); - - I915_WRITE(SPCSCYGOCLAMP(plane), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); - I915_WRITE(SPCSCCBOCLAMP(plane), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); - I915_WRITE(SPCSCCROCLAMP(plane), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); + I915_WRITE(SPCSCYGOFF(plane_id), SPCSC_OOFF(0) | SPCSC_IOFF(-64)); + I915_WRITE(SPCSCCBOFF(plane_id), SPCSC_OOFF(0) | SPCSC_IOFF(0)); + I915_WRITE(SPCSCCROFF(plane_id), SPCSC_OOFF(0) | SPCSC_IOFF(0)); + + I915_WRITE(SPCSCC01(plane_id), SPCSC_C1(4769) | SPCSC_C0(6537)); + I915_WRITE(SPCSCC23(plane_id), SPCSC_C1(-3330) | SPCSC_C0(0)); + I915_WRITE(SPCSCC45(plane_id), SPCSC_C1(-1605) | SPCSC_C0(4769)); + I915_WRITE(SPCSCC67(plane_id), SPCSC_C1(4769) | SPCSC_C0(0)); + I915_WRITE(SPCSCC8(plane_id), SPCSC_C0(8263)); + + I915_WRITE(SPCSCYGICLAMP(plane_id), SPCSC_IMAX(940) | SPCSC_IMIN(64)); + I915_WRITE(SPCSCCBICLAMP(plane_id), SPCSC_IMAX(448) | SPCSC_IMIN(-448)); + I915_WRITE(SPCSCCRICLAMP(plane_id), SPCSC_IMAX(448) | SPCSC_IMIN(-448)); + + I915_WRITE(SPCSCYGOCLAMP(plane_id), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); + I915_WRITE(SPCSCCBOCLAMP(plane_id), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); + I915_WRITE(SPCSCCROCLAMP(plane_id), SPCSC_OMAX(1023) | SPCSC_OMIN(0)); } static void @@ -340,8 +340,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(dplane); struct drm_framebuffer *fb = plane_state->base.fb; - int pipe = intel_plane->pipe; - int plane = intel_plane->plane; + enum pipe pipe = intel_plane->pipe; + enum plane_id plane_id = intel_plane->id; u32 sprctl; u32 sprsurf_offset, linear_offset; unsigned int rotation = plane_state->base.rotation; @@ -357,7 +357,7 @@ vlv_update_plane(struct drm_plane *dplane, sprctl = SP_ENABLE; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_YUYV: sprctl |= SP_FORMAT_YUV422 | SP_YUV_ORDER_YUYV; break; @@ -434,32 +434,32 @@ vlv_update_plane(struct drm_plane *dplane, linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); if (key->flags) { - I915_WRITE(SPKEYMINVAL(pipe, plane), key->min_value); - I915_WRITE(SPKEYMAXVAL(pipe, plane), key->max_value); - I915_WRITE(SPKEYMSK(pipe, plane), key->channel_mask); + I915_WRITE(SPKEYMINVAL(pipe, plane_id), key->min_value); + I915_WRITE(SPKEYMAXVAL(pipe, plane_id), key->max_value); + I915_WRITE(SPKEYMSK(pipe, plane_id), key->channel_mask); } if (key->flags & I915_SET_COLORKEY_SOURCE) sprctl |= SP_SOURCE_KEY; if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B) - chv_update_csc(intel_plane, fb->pixel_format); + chv_update_csc(intel_plane, fb->format->format); - I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); - I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); + I915_WRITE(SPSTRIDE(pipe, plane_id), fb->pitches[0]); + I915_WRITE(SPPOS(pipe, plane_id), (crtc_y << 16) | crtc_x); if (fb->modifier == I915_FORMAT_MOD_X_TILED) - I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x); + I915_WRITE(SPTILEOFF(pipe, plane_id), (y << 16) | x); else - I915_WRITE(SPLINOFF(pipe, plane), linear_offset); + I915_WRITE(SPLINOFF(pipe, plane_id), linear_offset); - I915_WRITE(SPCONSTALPHA(pipe, plane), 0); + I915_WRITE(SPCONSTALPHA(pipe, plane_id), 0); - I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w); - I915_WRITE(SPCNTR(pipe, plane), sprctl); - I915_WRITE(SPSURF(pipe, plane), + I915_WRITE(SPSIZE(pipe, plane_id), (crtc_h << 16) | crtc_w); + I915_WRITE(SPCNTR(pipe, plane_id), sprctl); + I915_WRITE(SPSURF(pipe, plane_id), intel_plane_ggtt_offset(plane_state) + sprsurf_offset); - POSTING_READ(SPSURF(pipe, plane)); + POSTING_READ(SPSURF(pipe, plane_id)); } static void @@ -468,13 +468,13 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc) struct drm_device *dev = dplane->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *intel_plane = to_intel_plane(dplane); - int pipe = intel_plane->pipe; - int plane = intel_plane->plane; + enum pipe pipe = intel_plane->pipe; + enum plane_id plane_id = intel_plane->id; - I915_WRITE(SPCNTR(pipe, plane), 0); + I915_WRITE(SPCNTR(pipe, plane_id), 0); - I915_WRITE(SPSURF(pipe, plane), 0); - POSTING_READ(SPSURF(pipe, plane)); + I915_WRITE(SPSURF(pipe, plane_id), 0); + POSTING_READ(SPSURF(pipe, plane_id)); } static void @@ -502,7 +502,7 @@ ivb_update_plane(struct drm_plane *plane, sprctl = SPRITE_ENABLE; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_XBGR8888: sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX; break; @@ -640,7 +640,7 @@ ilk_update_plane(struct drm_plane *plane, dvscntr = DVS_ENABLE; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_XBGR8888: dvscntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_XBGR; break; @@ -866,7 +866,7 @@ intel_check_sprite_plane(struct drm_plane *plane, src_y = src->y1 >> 16; src_h = drm_rect_height(src) >> 16; - if (format_is_yuv(fb->pixel_format)) { + if (format_is_yuv(fb->format->format)) { src_x &= ~1; src_w &= ~1; @@ -885,7 +885,7 @@ intel_check_sprite_plane(struct drm_plane *plane, /* Check size restrictions when scaling */ if (state->base.visible && (src_w != crtc_w || src_h != crtc_h)) { unsigned int width_bytes; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + int cpp = fb->format->cpp[0]; WARN_ON(!can_scale); @@ -1112,6 +1112,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, intel_plane->pipe = pipe; intel_plane->plane = plane; + intel_plane->id = PLANE_SPRITE0 + plane; intel_plane->frontbuffer_bit = INTEL_FRONTBUFFER_SPRITE(pipe, plane); intel_plane->check_plane = intel_check_sprite_plane; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 78cdfc6833d6..eb692e4ffe01 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1537,9 +1537,9 @@ static const struct drm_encoder_funcs intel_tv_enc_funcs = { }; void -intel_tv_init(struct drm_device *dev) +intel_tv_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_device *dev = &dev_priv->drm; struct drm_connector *connector; struct intel_tv *intel_tv; struct intel_encoder *intel_encoder; diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c new file mode 100644 index 000000000000..c46bc8594f22 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_uc.c @@ -0,0 +1,116 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "i915_drv.h" +#include "intel_uc.h" + +void intel_uc_init_early(struct drm_i915_private *dev_priv) +{ + mutex_init(&dev_priv->guc.send_mutex); +} + +/* + * Read GuC command/status register (SOFT_SCRATCH_0) + * Return true if it contains a response rather than a command + */ +static bool intel_guc_recv(struct intel_guc *guc, u32 *status) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + + u32 val = I915_READ(SOFT_SCRATCH(0)); + *status = val; + return INTEL_GUC_RECV_IS_RESPONSE(val); +} + +int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + u32 status; + int i; + int ret; + + if (WARN_ON(len < 1 || len > 15)) + return -EINVAL; + + mutex_lock(&guc->send_mutex); + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + + dev_priv->guc.action_count += 1; + dev_priv->guc.action_cmd = action[0]; + + for (i = 0; i < len; i++) + I915_WRITE(SOFT_SCRATCH(i), action[i]); + + POSTING_READ(SOFT_SCRATCH(i - 1)); + + I915_WRITE(GUC_SEND_INTERRUPT, GUC_SEND_TRIGGER); + + /* + * Fast commands should complete in less than 10us, so sample quickly + * up to that length of time, then switch to a slower sleep-wait loop. + * No inte_guc_send command should ever take longer than 10ms. + */ + ret = wait_for_us(intel_guc_recv(guc, &status), 10); + if (ret) + ret = wait_for(intel_guc_recv(guc, &status), 10); + if (status != INTEL_GUC_STATUS_SUCCESS) { + /* + * Either the GuC explicitly returned an error (which + * we convert to -EIO here) or no response at all was + * received within the timeout limit (-ETIMEDOUT) + */ + if (ret != -ETIMEDOUT) + ret = -EIO; + + DRM_WARN("INTEL_GUC_SEND: Action 0x%X failed;" + " ret=%d status=0x%08X response=0x%08X\n", + action[0], ret, status, I915_READ(SOFT_SCRATCH(15))); + + dev_priv->guc.action_fail += 1; + dev_priv->guc.action_err = ret; + } + dev_priv->guc.action_status = status; + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + mutex_unlock(&guc->send_mutex); + + return ret; +} + +int intel_guc_sample_forcewake(struct intel_guc *guc) +{ + struct drm_i915_private *dev_priv = guc_to_i915(guc); + u32 action[2]; + + action[0] = INTEL_GUC_ACTION_SAMPLE_FORCEWAKE; + /* WaRsDisableCoarsePowerGating:skl,bxt */ + if (!intel_enable_rc6() || NEEDS_WaRsDisableCoarsePowerGating(dev_priv)) + action[1] = 0; + else + /* bit 0 and 1 are for Render and Media domain separately */ + action[1] = GUC_FORCEWAKE_RENDER | GUC_FORCEWAKE_MEDIA; + + return intel_guc_send(guc, action, ARRAY_SIZE(action)); +} + diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_uc.h index 0053258e03d3..d74f4d3ad8dc 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -21,13 +21,15 @@ * IN THE SOFTWARE. * */ -#ifndef _INTEL_GUC_H_ -#define _INTEL_GUC_H_ +#ifndef _INTEL_UC_H_ +#define _INTEL_UC_H_ #include "intel_guc_fwif.h" #include "i915_guc_reg.h" #include "intel_ringbuffer.h" +#include "i915_vma.h" + struct drm_i915_gem_request; /* @@ -74,7 +76,7 @@ struct i915_guc_client { uint32_t proc_desc_offset; uint32_t doorbell_offset; - uint32_t cookie; + uint32_t doorbell_cookie; uint16_t doorbell_id; uint16_t padding[3]; /* Maintain alignment */ @@ -91,30 +93,35 @@ struct i915_guc_client { uint64_t submissions[I915_NUM_ENGINES]; }; -enum intel_guc_fw_status { - GUC_FIRMWARE_FAIL = -1, - GUC_FIRMWARE_NONE = 0, - GUC_FIRMWARE_PENDING, - GUC_FIRMWARE_SUCCESS +enum intel_uc_fw_status { + INTEL_UC_FIRMWARE_FAIL = -1, + INTEL_UC_FIRMWARE_NONE = 0, + INTEL_UC_FIRMWARE_PENDING, + INTEL_UC_FIRMWARE_SUCCESS +}; + +enum intel_uc_fw_type { + INTEL_UC_FW_TYPE_GUC, + INTEL_UC_FW_TYPE_HUC }; /* * This structure encapsulates all the data needed during the process * of fetching, caching, and loading the firmware image into the GuC. */ -struct intel_guc_fw { - struct drm_device * guc_dev; - const char * guc_fw_path; - size_t guc_fw_size; - struct drm_i915_gem_object * guc_fw_obj; - enum intel_guc_fw_status guc_fw_fetch_status; - enum intel_guc_fw_status guc_fw_load_status; - - uint16_t guc_fw_major_wanted; - uint16_t guc_fw_minor_wanted; - uint16_t guc_fw_major_found; - uint16_t guc_fw_minor_found; - +struct intel_uc_fw { + const char *path; + size_t size; + struct drm_i915_gem_object *obj; + enum intel_uc_fw_status fetch_status; + enum intel_uc_fw_status load_status; + + uint16_t major_ver_wanted; + uint16_t minor_ver_wanted; + uint16_t major_ver_found; + uint16_t minor_ver_found; + + enum intel_uc_fw_type fw; uint32_t header_size; uint32_t header_offset; uint32_t rsa_size; @@ -140,10 +147,10 @@ struct intel_guc_log { }; struct intel_guc { - struct intel_guc_fw guc_fw; + struct intel_uc_fw fw; struct intel_guc_log log; - /* GuC2Host interrupt related state */ + /* intel_guc_recv interrupt related state */ bool interrupts_enabled; struct i915_vma *ads_vma; @@ -165,17 +172,32 @@ struct intel_guc { uint64_t submissions[I915_NUM_ENGINES]; uint32_t last_seqno[I915_NUM_ENGINES]; - /* To serialize the Host2GuC actions */ - struct mutex action_lock; + /* To serialize the intel_guc_send actions */ + struct mutex send_mutex; +}; + +struct intel_huc { + /* Generic uC firmware management */ + struct intel_uc_fw fw; + + /* HuC-specific additions */ }; +/* intel_uc.c */ +void intel_uc_init_early(struct drm_i915_private *dev_priv); +int intel_guc_send(struct intel_guc *guc, const u32 *action, u32 len); +int intel_guc_sample_forcewake(struct intel_guc *guc); + /* intel_guc_loader.c */ -extern void intel_guc_init(struct drm_device *dev); -extern int intel_guc_setup(struct drm_device *dev); -extern void intel_guc_fini(struct drm_device *dev); -extern const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status); -extern int intel_guc_suspend(struct drm_device *dev); -extern int intel_guc_resume(struct drm_device *dev); +extern void intel_guc_init(struct drm_i915_private *dev_priv); +extern int intel_guc_setup(struct drm_i915_private *dev_priv); +extern void intel_guc_fini(struct drm_i915_private *dev_priv); +extern const char *intel_uc_fw_status_repr(enum intel_uc_fw_status status); +extern int intel_guc_suspend(struct drm_i915_private *dev_priv); +extern int intel_guc_resume(struct drm_i915_private *dev_priv); +void intel_uc_fw_fetch(struct drm_i915_private *dev_priv, + struct intel_uc_fw *uc_fw); +u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv); /* i915_guc_submission.c */ int i915_guc_submission_init(struct drm_i915_private *dev_priv); @@ -184,10 +206,26 @@ int i915_guc_wq_reserve(struct drm_i915_gem_request *rq); void i915_guc_wq_unreserve(struct drm_i915_gem_request *request); void i915_guc_submission_disable(struct drm_i915_private *dev_priv); void i915_guc_submission_fini(struct drm_i915_private *dev_priv); -void i915_guc_capture_logs(struct drm_i915_private *dev_priv); -void i915_guc_flush_logs(struct drm_i915_private *dev_priv); -void i915_guc_register(struct drm_i915_private *dev_priv); -void i915_guc_unregister(struct drm_i915_private *dev_priv); +struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size); + +/* intel_guc_log.c */ +void intel_guc_log_create(struct intel_guc *guc); +void i915_guc_log_register(struct drm_i915_private *dev_priv); +void i915_guc_log_unregister(struct drm_i915_private *dev_priv); int i915_guc_log_control(struct drm_i915_private *dev_priv, u64 control_val); +static inline u32 guc_ggtt_offset(struct i915_vma *vma) +{ + u32 offset = i915_ggtt_offset(vma); + GEM_BUG_ON(offset < GUC_WOPCM_TOP); + GEM_BUG_ON(range_overflows_t(u64, offset, vma->size, GUC_GGTT_TOP)); + return offset; +} + +/* intel_huc.c */ +void intel_huc_init(struct drm_i915_private *dev_priv); +void intel_huc_fini(struct drm_i915_private *dev_priv); +int intel_huc_load(struct drm_i915_private *dev_priv); +void intel_guc_auth_huc(struct drm_i915_private *dev_priv); + #endif diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 0bffd3f0c15d..abe08885a5ba 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -421,8 +421,7 @@ static void __intel_uncore_early_sanitize(struct drm_i915_private *dev_priv, GT_FIFO_CTL_RC6_POLICY_STALL); } - /* Enable Decoupled MMIO only on BXT C stepping onwards */ - if (!IS_BXT_REVID(dev_priv, BXT_REVID_C0, REVID_FOREVER)) + if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_B_LAST)) info->has_decoupled_mmio = false; intel_uncore_forcewake_reset(dev_priv, restore_forcewake); @@ -626,7 +625,14 @@ find_fw_domain(struct drm_i915_private *dev_priv, u32 offset) dev_priv->uncore.fw_domains_table_entries, fw_range_cmp); - return entry ? entry->domains : 0; + if (!entry) + return 0; + + WARN(entry->domains & ~dev_priv->uncore.fw_domains, + "Uninitialized forcewake domain(s) 0x%x accessed at 0x%x\n", + entry->domains & ~dev_priv->uncore.fw_domains, offset); + + return entry->domains; } static void @@ -1813,7 +1819,7 @@ static reset_func intel_get_gpu_reset(struct drm_i915_private *dev_priv) return ironlake_do_reset; else if (IS_G4X(dev_priv)) return g4x_do_reset; - else if (IS_G33(dev_priv)) + else if (IS_G33(dev_priv) || IS_PINEVIEW(dev_priv)) return g33_do_reset; else if (INTEL_INFO(dev_priv)->gen >= 3) return i915_do_reset; diff --git a/drivers/gpu/drm/i915/intel_vbt_defs.h b/drivers/gpu/drm/i915/intel_vbt_defs.h index 8886cab19f98..a92e7762f596 100644 --- a/drivers/gpu/drm/i915/intel_vbt_defs.h +++ b/drivers/gpu/drm/i915/intel_vbt_defs.h @@ -399,10 +399,12 @@ struct lvds_dvo_timing { u8 vblank_hi:4; u8 vactive_hi:4; u8 hsync_off_lo; - u8 hsync_pulse_width; - u8 vsync_pulse_width:4; - u8 vsync_off:4; - u8 rsvd0:6; + u8 hsync_pulse_width_lo; + u8 vsync_pulse_width_lo:4; + u8 vsync_off_lo:4; + u8 vsync_pulse_width_hi:2; + u8 vsync_off_hi:2; + u8 hsync_pulse_width_hi:2; u8 hsync_off_hi:2; u8 himage_lo; u8 vimage_lo; @@ -414,7 +416,7 @@ struct lvds_dvo_timing { u8 digital:2; u8 vsync_positive:1; u8 hsync_positive:1; - u8 rsvd2:1; + u8 non_interlaced:1; } __packed; struct lvds_pnp_id { diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 359cd2765552..f645275e6e63 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -207,8 +207,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, struct drm_device *drm = data; struct drm_encoder *encoder; struct imx_hdmi *hdmi; - struct resource *iores; - int irq; int ret; if (!pdev->dev.of_node) @@ -223,14 +221,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, hdmi->dev = &pdev->dev; encoder = &hdmi->encoder; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENXIO; - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); /* * If we failed to find the CRTC(s) which this encoder is @@ -249,7 +239,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); - ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); + ret = dw_hdmi_bind(pdev, encoder, plat_data); /* * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), @@ -264,7 +254,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, static void dw_hdmi_imx_unbind(struct device *dev, struct device *master, void *data) { - return dw_hdmi_unbind(dev, master, data); + return dw_hdmi_unbind(dev); } static const struct component_ops dw_hdmi_imx_ops = { diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 33404295b447..f562cb7964b0 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -357,8 +357,8 @@ static int imx_drm_bind(struct device *dev) * this value would be used to check framebuffer size limitation * at drm_mode_addfb(). */ - drm->mode_config.min_width = 64; - drm->mode_config.min_height = 64; + drm->mode_config.min_width = 1; + drm->mode_config.min_height = 1; drm->mode_config.max_width = 4096; drm->mode_config.max_height = 4096; drm->mode_config.funcs = &imx_drm_mode_config_funcs; @@ -389,8 +389,7 @@ static int imx_drm_bind(struct device *dev) dev_warn(dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); legacyfb_depth = 16; } - imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, - drm->mode_config.num_crtc, MAX_CRTC); + imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, MAX_CRTC); if (IS_ERR(imxdrm->fbhelper)) { ret = PTR_ERR(imxdrm->fbhelper); imxdrm->fbhelper = NULL; diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 516d06490465..88cd11d30134 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -454,10 +454,8 @@ static int imx_ldb_register(struct drm_device *drm, DRM_MODE_ENCODER_LVDS, NULL); if (imx_ldb_ch->bridge) { - imx_ldb_ch->bridge->encoder = encoder; - - imx_ldb_ch->encoder.bridge = imx_ldb_ch->bridge; - ret = drm_bridge_attach(drm, imx_ldb_ch->bridge); + ret = drm_bridge_attach(&imx_ldb_ch->encoder, + imx_ldb_ch->bridge, NULL); if (ret) { DRM_ERROR("Failed to initialize bridge with drm\n"); return ret; @@ -738,8 +736,6 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, for (i = 0; i < 2; i++) { struct imx_ldb_channel *channel = &imx_ldb->channel[i]; - if (channel->bridge) - drm_bridge_detach(channel->bridge); if (channel->panel) drm_panel_detach(channel->panel); diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c index 3b602ee33c44..4826bb781723 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/imx-tve.c @@ -98,6 +98,8 @@ /* TVE_TST_MODE_REG */ #define TVE_TVDAC_TEST_MODE_MASK (0x7 << 0) +#define IMX_TVE_DAC_VOLTAGE 2750000 + enum { TVE_MODE_TVOUT, TVE_MODE_VGA, @@ -150,13 +152,11 @@ __releases(&tve->lock) static void tve_enable(struct imx_tve *tve) { - int ret; - if (!tve->enabled) { tve->enabled = true; clk_prepare_enable(tve->clk); - ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, - TVE_EN, TVE_EN); + regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, + TVE_EN, TVE_EN); } /* clear interrupt status register */ @@ -174,12 +174,9 @@ static void tve_enable(struct imx_tve *tve) static void tve_disable(struct imx_tve *tve) { - int ret; - if (tve->enabled) { tve->enabled = false; - ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, - TVE_EN, 0); + regmap_update_bits(tve->regmap, TVE_COM_CONF_REG, TVE_EN, 0); clk_disable_unprepare(tve->clk); } } @@ -621,9 +618,8 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) tve->dac_reg = devm_regulator_get(dev, "dac"); if (!IS_ERR(tve->dac_reg)) { - ret = regulator_set_voltage(tve->dac_reg, 2750000, 2750000); - if (ret) - return ret; + if (regulator_get_voltage(tve->dac_reg) != IMX_TVE_DAC_VOLTAGE) + dev_warn(dev, "dac voltage is not %d uV\n", IMX_TVE_DAC_VOLTAGE); ret = regulator_enable(tve->dac_reg); if (ret) return ret; diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index e74a0ad52950..8b5294d47cee 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -77,7 +77,7 @@ drm_plane_state_to_eba(struct drm_plane_state *state) BUG_ON(!cma_obj); return cma_obj->paddr + fb->offsets[0] + fb->pitches[0] * y + - drm_format_plane_cpp(fb->pixel_format, 0) * x; + fb->format->cpp[0] * x; } static inline unsigned long @@ -92,11 +92,11 @@ drm_plane_state_to_ubo(struct drm_plane_state *state) cma_obj = drm_fb_cma_get_gem_obj(fb, 1); BUG_ON(!cma_obj); - x /= drm_format_horz_chroma_subsampling(fb->pixel_format); - y /= drm_format_vert_chroma_subsampling(fb->pixel_format); + x /= drm_format_horz_chroma_subsampling(fb->format->format); + y /= drm_format_vert_chroma_subsampling(fb->format->format); return cma_obj->paddr + fb->offsets[1] + fb->pitches[1] * y + - drm_format_plane_cpp(fb->pixel_format, 1) * x - eba; + fb->format->cpp[1] * x - eba; } static inline unsigned long @@ -111,11 +111,11 @@ drm_plane_state_to_vbo(struct drm_plane_state *state) cma_obj = drm_fb_cma_get_gem_obj(fb, 2); BUG_ON(!cma_obj); - x /= drm_format_horz_chroma_subsampling(fb->pixel_format); - y /= drm_format_vert_chroma_subsampling(fb->pixel_format); + x /= drm_format_horz_chroma_subsampling(fb->format->format); + y /= drm_format_vert_chroma_subsampling(fb->format->format); return cma_obj->paddr + fb->offsets[2] + fb->pitches[2] * y + - drm_format_plane_cpp(fb->pixel_format, 2) * x - eba; + fb->format->cpp[2] * x - eba; } void ipu_plane_put_resources(struct ipu_plane *ipu_plane) @@ -281,7 +281,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, */ if (old_fb && (state->src_w != old_state->src_w || state->src_h != old_state->src_h || - fb->pixel_format != old_fb->pixel_format)) + fb->format != old_fb->format)) crtc_state->mode_changed = true; eba = drm_plane_state_to_eba(state); @@ -295,7 +295,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, if (old_fb && fb->pitches[0] != old_fb->pitches[0]) crtc_state->mode_changed = true; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: case DRM_FORMAT_YUV422: @@ -315,7 +315,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, if (vbo & 0x7 || vbo > 0xfffff8) return -EINVAL; - if (old_fb && (fb->pixel_format == old_fb->pixel_format)) { + if (old_fb && (fb->format == old_fb->format)) { old_vbo = drm_plane_state_to_vbo(old_state); if (vbo != old_vbo) crtc_state->mode_changed = true; @@ -332,7 +332,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, if (ubo & 0x7 || ubo > 0xfffff8) return -EINVAL; - if (old_fb && (fb->pixel_format == old_fb->pixel_format)) { + if (old_fb && (fb->format == old_fb->format)) { old_ubo = drm_plane_state_to_ubo(old_state); if (ubo != old_ubo) crtc_state->mode_changed = true; @@ -348,8 +348,8 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, * The x/y offsets must be even in case of horizontal/vertical * chroma subsampling. */ - hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); - vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); + hsub = drm_format_horz_chroma_subsampling(fb->format->format); + vsub = drm_format_vert_chroma_subsampling(fb->format->format); if (((state->src_x >> 16) & (hsub - 1)) || ((state->src_y >> 16) & (vsub - 1))) return -EINVAL; @@ -392,13 +392,13 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true); break; case IPU_DP_FLOW_SYNC_FG: - ics = ipu_drm_fourcc_to_colorspace(state->fb->pixel_format); + ics = ipu_drm_fourcc_to_colorspace(state->fb->format->format); ipu_dp_setup_channel(ipu_plane->dp, ics, IPUV3_COLORSPACE_UNKNOWN); ipu_dp_set_window_pos(ipu_plane->dp, state->crtc_x, state->crtc_y); /* Enable local alpha on partial plane */ - switch (state->fb->pixel_format) { + switch (state->fb->format->format) { case DRM_FORMAT_ARGB1555: case DRM_FORMAT_ABGR1555: case DRM_FORMAT_RGBA5551: @@ -421,11 +421,11 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ipu_cpmem_zero(ipu_plane->ipu_ch); ipu_cpmem_set_resolution(ipu_plane->ipu_ch, state->src_w >> 16, state->src_h >> 16); - ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->pixel_format); + ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->format->format); ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1); ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]); - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: case DRM_FORMAT_YUV422: @@ -434,9 +434,9 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, case DRM_FORMAT_YVU444: ubo = drm_plane_state_to_ubo(state); vbo = drm_plane_state_to_vbo(state); - if (fb->pixel_format == DRM_FORMAT_YVU420 || - fb->pixel_format == DRM_FORMAT_YVU422 || - fb->pixel_format == DRM_FORMAT_YVU444) + if (fb->format->format == DRM_FORMAT_YVU420 || + fb->format->format == DRM_FORMAT_YVU422 || + fb->format->format == DRM_FORMAT_YVU444) swap(ubo, vbo); ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch, diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 8582a83c0d9b..d5c06fd89f90 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -191,9 +191,7 @@ static int imx_pd_register(struct drm_device *drm, drm_panel_attach(imxpd->panel, &imxpd->connector); if (imxpd->bridge) { - imxpd->bridge->encoder = encoder; - encoder->bridge = imxpd->bridge; - ret = drm_bridge_attach(drm, imxpd->bridge); + ret = drm_bridge_attach(encoder, imxpd->bridge, NULL); if (ret < 0) { dev_err(imxpd->dev, "failed to attach bridge: %d\n", ret); @@ -286,8 +284,6 @@ static void imx_pd_unbind(struct device *dev, struct device *master, { struct imx_parallel_display *imxpd = dev_get_drvdata(dev); - if (imxpd->bridge) - drm_bridge_detach(imxpd->bridge); if (imxpd->panel) drm_panel_detach(imxpd->panel); diff --git a/drivers/gpu/drm/lib/drm_random.c b/drivers/gpu/drm/lib/drm_random.c new file mode 100644 index 000000000000..7b12a68c3b54 --- /dev/null +++ b/drivers/gpu/drm/lib/drm_random.c @@ -0,0 +1,41 @@ +#include <linux/bitops.h> +#include <linux/kernel.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "drm_random.h" + +static inline u32 drm_prandom_u32_max_state(u32 ep_ro, struct rnd_state *state) +{ + return upper_32_bits((u64)prandom_u32_state(state) * ep_ro); +} + +void drm_random_reorder(unsigned int *order, unsigned int count, + struct rnd_state *state) +{ + unsigned int i, j; + + for (i = 0; i < count; ++i) { + BUILD_BUG_ON(sizeof(unsigned int) > sizeof(u32)); + j = drm_prandom_u32_max_state(count, state); + swap(order[i], order[j]); + } +} +EXPORT_SYMBOL(drm_random_reorder); + +unsigned int *drm_random_order(unsigned int count, struct rnd_state *state) +{ + unsigned int *order, i; + + order = kmalloc_array(count, sizeof(*order), GFP_TEMPORARY); + if (!order) + return order; + + for (i = 0; i < count; i++) + order[i] = i; + + drm_random_reorder(order, count, state); + return order; +} +EXPORT_SYMBOL(drm_random_order); diff --git a/drivers/gpu/drm/lib/drm_random.h b/drivers/gpu/drm/lib/drm_random.h new file mode 100644 index 000000000000..a78644bea7f9 --- /dev/null +++ b/drivers/gpu/drm/lib/drm_random.h @@ -0,0 +1,25 @@ +#ifndef __DRM_RANDOM_H__ +#define __DRM_RANDOM_H__ + +/* This is a temporary home for a couple of utility functions that should + * be transposed to lib/ at the earliest convenience. + */ + +#include <linux/random.h> + +#define DRM_RND_STATE_INITIALIZER(seed__) ({ \ + struct rnd_state state__; \ + prandom_seed_state(&state__, (seed__)); \ + state__; \ +}) + +#define DRM_RND_STATE(name__, seed__) \ + struct rnd_state name__ = DRM_RND_STATE_INITIALIZER(seed__) + +unsigned int *drm_random_order(unsigned int count, + struct rnd_state *state); +void drm_random_reorder(unsigned int *order, + unsigned int count, + struct rnd_state *state); + +#endif /* !__DRM_RANDOM_H__ */ diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 90fb831ef031..3bd3bd688d1a 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -63,6 +63,7 @@ enum mtk_dpi_out_color_format { struct mtk_dpi { struct mtk_ddp_comp ddp_comp; struct drm_encoder encoder; + struct drm_bridge *bridge; void __iomem *regs; struct device *dev; struct clk *engine_clk; @@ -620,8 +621,7 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) /* Currently DPI0 is fixed to be driven by OVL1 */ dpi->encoder.possible_crtcs = BIT(1); - dpi->encoder.bridge->encoder = &dpi->encoder; - ret = drm_bridge_attach(dpi->encoder.dev, dpi->encoder.bridge); + ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL); if (ret) { dev_err(dev, "Failed to attach bridge: %d\n", ret); goto err_cleanup; @@ -718,9 +718,9 @@ static int mtk_dpi_probe(struct platform_device *pdev) dev_info(dev, "Found bridge node: %s\n", bridge_node->full_name); - dpi->encoder.bridge = of_drm_find_bridge(bridge_node); + dpi->bridge = of_drm_find_bridge(bridge_node); of_node_put(bridge_node); - if (!dpi->encoder.bridge) + if (!dpi->bridge) return -EPROBE_DEFER; comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DPI); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index 01a21dd835b5..a73de1e669c2 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -170,8 +170,8 @@ static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe) { - struct mtk_drm_private *priv = drm->dev_private; - struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base); @@ -181,8 +181,8 @@ int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe) void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe) { - struct mtk_drm_private *priv = drm->dev_private; - struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); + struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; mtk_ddp_comp_disable_vblank(ovl); @@ -588,7 +588,6 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev, goto unprepare; drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE); drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, false, MTK_LUT_SIZE); - priv->crtc[pipe] = &mtk_crtc->base; priv->num_pipes++; return 0; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 4b7fe7eaec01..b5f88e6d078e 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -321,7 +321,8 @@ static void mtk_drm_unbind(struct device *dev) { struct mtk_drm_private *private = dev_get_drvdata(dev); - drm_put_dev(private->drm); + drm_dev_unregister(private->drm); + drm_dev_unref(private->drm); private->drm = NULL; } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h index aa9389446785..df322a7a5fcb 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h @@ -32,7 +32,6 @@ struct mtk_drm_private { struct drm_device *drm; struct device *dma_dev; - struct drm_crtc *crtc[MAX_CRTC]; unsigned int num_pipes; struct device_node *mutex_node; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c index 147df85399ab..d4246c9dceae 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_fb.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c @@ -82,7 +82,7 @@ static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev, if (!mtk_fb) return ERR_PTR(-ENOMEM); - drm_helper_mode_fill_fb_struct(&mtk_fb->base, mode); + drm_helper_mode_fill_fb_struct(dev, &mtk_fb->base, mode); mtk_fb->gem_obj = obj; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c index c461a232cbf5..e405e89ed5e5 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c @@ -133,9 +133,9 @@ static void mtk_plane_atomic_update(struct drm_plane *plane, mtk_gem = to_mtk_gem_obj(gem); addr = mtk_gem->dma_addr; pitch = fb->pitches[0]; - format = fb->pixel_format; + format = fb->format->format; - addr += (plane->state->src.x1 >> 16) * drm_format_plane_cpp(format, 0); + addr += (plane->state->src.x1 >> 16) * fb->format->cpp[0]; addr += (plane->state->src.y1 >> 16) * pitch; state->pending.enable = true; diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 2c42f90809d8..dd71cbb1a622 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -622,26 +622,6 @@ static const struct drm_connector_helper_funcs .get_modes = mtk_dsi_connector_get_modes, }; -static int mtk_drm_attach_bridge(struct drm_bridge *bridge, - struct drm_encoder *encoder) -{ - int ret; - - if (!bridge) - return -ENOENT; - - encoder->bridge = bridge; - bridge->encoder = encoder; - ret = drm_bridge_attach(encoder->dev, bridge); - if (ret) { - DRM_ERROR("Failed to attach bridge to drm\n"); - encoder->bridge = NULL; - bridge->encoder = NULL; - } - - return ret; -} - static int mtk_dsi_create_connector(struct drm_device *drm, struct mtk_dsi *dsi) { int ret; @@ -692,8 +672,10 @@ static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi) dsi->encoder.possible_crtcs = 1; /* If there's a bridge, attach to it and let it create the connector */ - ret = mtk_drm_attach_bridge(dsi->bridge, &dsi->encoder); + ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL); if (ret) { + DRM_ERROR("Failed to attach bridge to drm\n"); + /* Otherwise create our own connector and attach to a panel */ ret = mtk_dsi_create_connector(drm, dsi); if (ret) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 0e8c4d9af340..c26251260b83 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -149,6 +149,7 @@ struct hdmi_audio_param { struct mtk_hdmi { struct drm_bridge bridge; + struct drm_bridge *next_bridge; struct drm_connector conn; struct device *dev; struct phy *phy; @@ -1314,9 +1315,9 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge) return ret; } - if (bridge->next) { - bridge->next->encoder = bridge->encoder; - ret = drm_bridge_attach(bridge->encoder->dev, bridge->next); + if (hdmi->next_bridge) { + ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, + bridge); if (ret) { dev_err(hdmi->dev, "Failed to attach external bridge: %d\n", ret); @@ -1510,8 +1511,8 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, of_node_put(ep); if (!of_device_is_compatible(remote, "hdmi-connector")) { - hdmi->bridge.next = of_drm_find_bridge(remote); - if (!hdmi->bridge.next) { + hdmi->next_bridge = of_drm_find_bridge(remote); + if (!hdmi->next_bridge) { dev_err(dev, "Waiting for external bridge\n"); of_node_put(remote); return -EPROBE_DEFER; diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile index 2591978b8aad..92cf84530f49 100644 --- a/drivers/gpu/drm/meson/Makefile +++ b/drivers/gpu/drm/meson/Makefile @@ -1,4 +1,4 @@ -meson-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o -meson-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o +meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o +meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o -obj-$(CONFIG_DRM_MESON) += meson.o +obj-$(CONFIG_DRM_MESON) += meson-drm.o diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index ff1f6019b97b..6f2fd82ed483 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -279,7 +279,6 @@ static int meson_drv_probe(struct platform_device *pdev) drm->mode_config.funcs = &meson_mode_config_funcs; priv->fbdev = drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_crtc, drm->mode_config.num_connector); if (IS_ERR(priv->fbdev)) { ret = PTR_ERR(priv->fbdev); @@ -329,8 +328,7 @@ static struct platform_driver meson_drm_platform_driver = { .probe = meson_drv_probe, .remove = meson_drv_remove, .driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, + .name = "meson-drm", .of_match_table = dt_match, }, }; diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 7890e30eb584..a32d3b6e2e12 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -116,7 +116,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane, if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB; - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_XRGB8888: /* For XRGB, replace the pixel's alpha by 0xFF */ writel_bits_relaxed(OSD_REPLACE_EN, OSD_REPLACE_EN, diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c index 1f2f9ca25901..1ffdafea27e4 100644 --- a/drivers/gpu/drm/mga/mga_dma.c +++ b/drivers/gpu/drm/mga/mga_dma.c @@ -392,6 +392,24 @@ int mga_driver_load(struct drm_device *dev, unsigned long flags) drm_mga_private_t *dev_priv; int ret; + /* There are PCI versions of the G450. These cards have the + * same PCI ID as the AGP G450, but have an additional PCI-to-PCI + * bridge chip. We detect these cards, which are not currently + * supported by this driver, by looking at the device ID of the + * bus the "card" is on. If vendor is 0x3388 (Hint Corp) and the + * device is 0x0021 (HB6 Universal PCI-PCI bridge), we reject the + * device. + */ + if ((dev->pdev->device == 0x0525) && dev->pdev->bus->self + && (dev->pdev->bus->self->vendor == 0x3388) + && (dev->pdev->bus->self->device == 0x0021) + && dev->agp) { + /* FIXME: This should be quirked in the pci core, but oh well + * the hw probably stopped existing. */ + arch_phys_wc_del(dev->agp->agp_mtrr); + kfree(dev->agp); + dev->agp = NULL; + } dev_priv = kzalloc(sizeof(drm_mga_private_t), GFP_KERNEL); if (!dev_priv) return -ENOMEM; @@ -698,7 +716,7 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev, static int mga_do_dma_bootstrap(struct drm_device *dev, drm_mga_dma_bootstrap_t *dma_bs) { - const int is_agp = (dma_bs->agp_mode != 0) && drm_pci_device_is_agp(dev); + const int is_agp = (dma_bs->agp_mode != 0) && dev->agp; int err; drm_mga_private_t *const dev_priv = (drm_mga_private_t *) dev->dev_private; @@ -1127,12 +1145,10 @@ int mga_dma_buffers(struct drm_device *dev, void *data, /** * Called just before the module is unloaded. */ -int mga_driver_unload(struct drm_device *dev) +void mga_driver_unload(struct drm_device *dev) { kfree(dev->dev_private); dev->dev_private = NULL; - - return 0; } /** diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 25b2a1a424e6..63ba0699d107 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -37,8 +37,6 @@ #include <drm/drm_pciids.h> -static int mga_driver_device_is_agp(struct drm_device *dev); - static struct pci_device_id pciidlist[] = { mga_PCI_IDS }; @@ -66,7 +64,6 @@ static struct drm_driver driver = { .lastclose = mga_driver_lastclose, .set_busid = drm_pci_set_busid, .dma_quiescent = mga_driver_dma_quiescent, - .device_is_agp = mga_driver_device_is_agp, .get_vblank_counter = mga_get_vblank_counter, .enable_vblank = mga_enable_vblank, .disable_vblank = mga_disable_vblank, @@ -107,37 +104,3 @@ module_exit(mga_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL and additional rights"); - -/** - * Determine if the device really is AGP or not. - * - * In addition to the usual tests performed by \c drm_device_is_agp, this - * function detects PCI G450 cards that appear to the system exactly like - * AGP G450 cards. - * - * \param dev The device to be tested. - * - * \returns - * If the device is a PCI G450, zero is returned. Otherwise 2 is returned. - */ -static int mga_driver_device_is_agp(struct drm_device *dev) -{ - const struct pci_dev *const pdev = dev->pdev; - - /* There are PCI versions of the G450. These cards have the - * same PCI ID as the AGP G450, but have an additional PCI-to-PCI - * bridge chip. We detect these cards, which are not currently - * supported by this driver, by looking at the device ID of the - * bus the "card" is on. If vendor is 0x3388 (Hint Corp) and the - * device is 0x0021 (HB6 Universal PCI-PCI bridge), we reject the - * device. - */ - - if ((pdev->device == 0x0525) && pdev->bus->self - && (pdev->bus->self->vendor == 0x3388) - && (pdev->bus->self->device == 0x0021)) { - return 0; - } - - return 2; -} diff --git a/drivers/gpu/drm/mga/mga_drv.h b/drivers/gpu/drm/mga/mga_drv.h index bb312339e0b0..45cf363d25ad 100644 --- a/drivers/gpu/drm/mga/mga_drv.h +++ b/drivers/gpu/drm/mga/mga_drv.h @@ -166,7 +166,7 @@ extern int mga_dma_reset(struct drm_device *dev, void *data, extern int mga_dma_buffers(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int mga_driver_load(struct drm_device *dev, unsigned long flags); -extern int mga_driver_unload(struct drm_device *dev); +extern void mga_driver_unload(struct drm_device *dev); extern void mga_driver_lastclose(struct drm_device *dev); extern int mga_driver_dma_quiescent(struct drm_device *dev); @@ -266,7 +266,7 @@ do { \ do { \ if (MGA_VERBOSE) { \ DRM_INFO("BEGIN_DMA(%d)\n", (n)); \ - DRM_INFO(" space=0x%x req=0x%Zx\n", \ + DRM_INFO(" space=0x%x req=0x%zx\n", \ dev_priv->prim.space, (n) * DMA_BLOCK_SIZE); \ } \ prim = dev_priv->prim.start; \ @@ -313,7 +313,7 @@ do { \ #define DMA_WRITE(offset, val) \ do { \ if (MGA_VERBOSE) \ - DRM_INFO(" DMA_WRITE( 0x%08x ) at 0x%04Zx\n", \ + DRM_INFO(" DMA_WRITE( 0x%08x ) at 0x%04zx\n", \ (u32)(val), write + (offset) * sizeof(u32)); \ *(volatile u32 *)(prim + write + (offset) * sizeof(u32)) = val; \ } while (0) diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig index 520e5e668d6c..db58578719d2 100644 --- a/drivers/gpu/drm/mgag200/Kconfig +++ b/drivers/gpu/drm/mgag200/Kconfig @@ -1,6 +1,6 @@ config DRM_MGAG200 tristate "Kernel modesetting driver for MGA G200 server engines" - depends on DRM && PCI + depends on DRM && PCI && MMU select DRM_KMS_HELPER select DRM_TTM help diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index b0b874264f9d..9ac007880328 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -36,6 +36,7 @@ static const struct pci_device_id pciidlist[] = { { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH }, { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER }, { PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 }, + { PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 }, {0,} }; diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 3e02ac20777c..c88b6ec88dd2 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -15,6 +15,7 @@ #include <video/vga.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_helper.h> #include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo_driver.h> @@ -179,6 +180,7 @@ enum mga_type { G200_WB, G200_EV, G200_EH, + G200_EH3, G200_ER, G200_EW3, }; @@ -257,7 +259,7 @@ int mgag200_framebuffer_init(struct drm_device *dev, int mgag200_driver_load(struct drm_device *dev, unsigned long flags); -int mgag200_driver_unload(struct drm_device *dev); +void mgag200_driver_unload(struct drm_device *dev); int mgag200_gem_create(struct drm_device *dev, u32 size, bool iskernel, struct drm_gem_object **obj); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 88dd2214114d..a449bb91213a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -24,7 +24,7 @@ static void mga_dirty_update(struct mga_fbdev *mfbdev, struct drm_gem_object *obj; struct mgag200_bo *bo; int src_offset, dst_offset; - int bpp = (mfbdev->mfb.base.bits_per_pixel + 7)/8; + int bpp = mfbdev->mfb.base.format->cpp[0]; int ret = -EBUSY; bool unmap = false; bool store_for_later = false; @@ -217,7 +217,7 @@ static int mgag200fb_create(struct drm_fb_helper *helper, info->apertures->ranges[0].base = mdev->dev->mode_config.fb_base; info->apertures->ranges[0].size = mdev->mc.vram_size; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &mfbdev->helper, sizes->fb_width, sizes->fb_height); @@ -286,7 +286,7 @@ int mgag200_fbdev_init(struct mga_device *mdev) drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs); ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, - mdev->num_crtc, MGAG200FB_CONN_LIMIT); + MGAG200FB_CONN_LIMIT); if (ret) goto err_fb_helper; diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c index 10535e3b75f2..77d1c4771786 100644 --- a/drivers/gpu/drm/mgag200/mgag200_i2c.c +++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c @@ -106,6 +106,7 @@ struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev) clock = 2; break; case G200_EH: + case G200_EH3: case G200_ER: data = 2; clock = 1; diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index e79cbc25ae3c..dce8a3eb5a10 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -34,7 +34,7 @@ int mgag200_framebuffer_init(struct drm_device *dev, { int ret; - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &gfb->base, mode_cmd); gfb->obj = obj; ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); if (ret) { @@ -145,6 +145,8 @@ static int mga_vram_init(struct mga_device *mdev) } mem = pci_iomap(mdev->dev->pdev, 0, 0); + if (!mem) + return -ENOMEM; mdev->mc.vram_size = mga_probe_vram(mdev, mem); @@ -262,18 +264,17 @@ err_mm: return r; } -int mgag200_driver_unload(struct drm_device *dev) +void mgag200_driver_unload(struct drm_device *dev) { struct mga_device *mdev = dev->dev_private; if (mdev == NULL) - return 0; + return; mgag200_modeset_fini(mdev); mgag200_fbdev_fini(mdev); drm_mode_config_cleanup(dev); mgag200_mm_fini(mdev); dev->dev_private = NULL; - return 0; } int mgag200_gem_create(struct drm_device *dev, diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 3a03ac4045d8..3938120e5051 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -38,11 +38,11 @@ static void mga_crtc_load_lut(struct drm_crtc *crtc) WREG8(DAC_INDEX + MGA1064_INDEX, 0); - if (fb && fb->bits_per_pixel == 16) { - int inc = (fb->depth == 15) ? 8 : 4; + if (fb && fb->format->cpp[0] * 8 == 16) { + int inc = (fb->format->depth == 15) ? 8 : 4; u8 r, b; for (i = 0; i < MGAG200_LUT_SIZE; i += inc) { - if (fb->depth == 16) { + if (fb->format->depth == 16) { if (i > (MGAG200_LUT_SIZE >> 1)) { r = b = 0; } else { @@ -497,34 +497,70 @@ static int mga_g200eh_set_plls(struct mga_device *mdev, long clock) bool pll_locked = false; m = n = p = 0; - vcomax = 800000; - vcomin = 400000; - pllreffreq = 33333; - delta = 0xffffffff; + if (mdev->type == G200_EH3) { + vcomax = 3000000; + vcomin = 1500000; + pllreffreq = 25000; - for (testp = 16; testp > 0; testp >>= 1) { - if (clock * testp > vcomax) - continue; - if (clock * testp < vcomin) - continue; + delta = 0xffffffff; - for (testm = 1; testm < 33; testm++) { - for (testn = 17; testn < 257; testn++) { - computed = (pllreffreq * testn) / - (testm * testp); + testp = 0; + + for (testm = 150; testm >= 6; testm--) { + if (clock * testm > vcomax) + continue; + if (clock * testm < vcomin) + continue; + for (testn = 120; testn >= 60; testn--) { + computed = (pllreffreq * testn) / testm; if (computed > clock) tmpdelta = computed - clock; else tmpdelta = clock - computed; if (tmpdelta < delta) { delta = tmpdelta; - n = testn - 1; - m = (testm - 1); - p = testp - 1; + n = testn; + m = testm; + p = testp; + } + if (delta == 0) + break; + } + if (delta == 0) + break; + } + } else { + + vcomax = 800000; + vcomin = 400000; + pllreffreq = 33333; + + delta = 0xffffffff; + + for (testp = 16; testp > 0; testp >>= 1) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testm = 1; testm < 33; testm++) { + for (testn = 17; testn < 257; testn++) { + computed = (pllreffreq * testn) / + (testm * testp); + if (computed > clock) + tmpdelta = computed - clock; + else + tmpdelta = clock - computed; + if (tmpdelta < delta) { + delta = tmpdelta; + n = testn - 1; + m = (testm - 1); + p = testp - 1; + } + if ((clock * testp) >= 600000) + p |= 0x80; } - if ((clock * testp) >= 600000) - p |= 0x80; } } } @@ -674,6 +710,7 @@ static int mga_crtc_set_plls(struct mga_device *mdev, long clock) return mga_g200ev_set_plls(mdev, clock); break; case G200_EH: + case G200_EH3: return mga_g200eh_set_plls(mdev, clock); break; case G200_ER: @@ -880,6 +917,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct mga_device *mdev = dev->dev_private; + const struct drm_framebuffer *fb = crtc->primary->fb; int hdisplay, hsyncstart, hsyncend, htotal; int vdisplay, vsyncstart, vsyncend, vtotal; int pitch; @@ -902,7 +940,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, /* 0x48: */ 0, 0, 0, 0, 0, 0, 0, 0 }; - bppshift = mdev->bpp_shifts[(crtc->primary->fb->bits_per_pixel >> 3) - 1]; + bppshift = mdev->bpp_shifts[fb->format->cpp[0] - 1]; switch (mdev->type) { case G200_SE_A: @@ -932,6 +970,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, option2 = 0x0000b000; break; case G200_EH: + case G200_EH3: dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 | MGA1064_MISC_CTL_DAC_RAM_CS; option = 0x00000120; @@ -941,12 +980,12 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, break; } - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_8bits; break; case 16: - if (crtc->primary->fb->depth == 15) + if (fb->format->depth == 15) dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_15bits; else dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_16bits; @@ -978,7 +1017,8 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, if ((mdev->type == G200_EV || mdev->type == G200_WB || mdev->type == G200_EH || - mdev->type == G200_EW3) && + mdev->type == G200_EW3 || + mdev->type == G200_EH3) && (i >= 0x44) && (i <= 0x4e)) continue; @@ -997,8 +1037,8 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, WREG_SEQ(3, 0); WREG_SEQ(4, 0xe); - pitch = crtc->primary->fb->pitches[0] / (crtc->primary->fb->bits_per_pixel / 8); - if (crtc->primary->fb->bits_per_pixel == 24) + pitch = fb->pitches[0] / fb->format->cpp[0]; + if (fb->format->cpp[0] * 8 == 24) pitch = (pitch * 3) >> (4 - bppshift); else pitch = pitch >> (4 - bppshift); @@ -1075,7 +1115,7 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, ((vdisplay & 0xc00) >> 7) | ((vsyncstart & 0xc00) >> 5) | ((vdisplay & 0x400) >> 3); - if (crtc->primary->fb->bits_per_pixel == 24) + if (fb->format->cpp[0] * 8 == 24) ext_vga[3] = (((1 << bppshift) * 3) - 1) | 0x80; else ext_vga[3] = ((1 << bppshift) - 1) | 0x80; @@ -1138,9 +1178,9 @@ static int mga_crtc_mode_set(struct drm_crtc *crtc, u32 bpp; u32 mb; - if (crtc->primary->fb->bits_per_pixel > 16) + if (fb->format->cpp[0] * 8 > 16) bpp = 32; - else if (crtc->primary->fb->bits_per_pixel > 8) + else if (fb->format->cpp[0] * 8 > 8) bpp = 16; else bpp = 8; diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c index 5e20220ef4c6..657598bb1e6b 100644 --- a/drivers/gpu/drm/mgag200/mgag200_ttm.c +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -236,8 +236,6 @@ struct ttm_bo_driver mgag200_bo_driver = { .verify_access = mgag200_bo_verify_access, .io_mem_reserve = &mgag200_ttm_io_mem_reserve, .io_mem_free = &mgag200_ttm_io_mem_free, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int mgag200_mm_init(struct mga_device *mdev) diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index d96b2b6898a3..5b8e23d051f2 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -4,6 +4,7 @@ config DRM_MSM depends on DRM depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on OF && COMMON_CLK + depends on MMU select REGULATOR select DRM_KMS_HELPER select DRM_PANEL @@ -71,3 +72,10 @@ config DRM_MSM_DSI_28NM_8960_PHY help Choose this option if the 28nm DSI PHY 8960 variant is used on the platform. + +config DRM_MSM_DSI_14NM_PHY + bool "Enable DSI 14nm PHY driver in MSM DRM (used by MSM8996/APQ8096)" + depends on DRM_MSM_DSI + default y + help + Choose this option if DSI PHY on 8996 is used on the platform. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 028c24df2291..39055362da95 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -76,11 +76,13 @@ msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/phy/dsi_phy_28nm.o msm-$(CONFIG_DRM_MSM_DSI_20NM_PHY) += dsi/phy/dsi_phy_20nm.o msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/phy/dsi_phy_28nm_8960.o +msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/phy/dsi_phy_14nm.o ifeq ($(CONFIG_DRM_MSM_DSI_PLL),y) msm-y += dsi/pll/dsi_pll.o msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/pll/dsi_pll_28nm.o msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o +msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/pll/dsi_pll_14nm.o endif obj-$(CONFIG_DRM_MSM) += msm.o diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index b8647198c11c..4414cf73735d 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -12,6 +12,7 @@ */ #include "msm_gem.h" +#include "msm_mmu.h" #include "a5xx_gpu.h" extern bool hang_debug; @@ -327,7 +328,7 @@ static int a5xx_hw_init(struct msm_gpu *gpu) /* Enable RBBM error reporting bits */ gpu_write(gpu, REG_A5XX_RBBM_AHB_CNTL0, 0x00000001); - if (adreno_gpu->quirks & ADRENO_QUIRK_FAULT_DETECT_MASK) { + if (adreno_gpu->info->quirks & ADRENO_QUIRK_FAULT_DETECT_MASK) { /* * Mask out the activity signals from RB1-3 to avoid false * positives @@ -381,7 +382,7 @@ static int a5xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A5XX_PC_DBG_ECO_CNTL, (0x400 << 11 | 0x300 << 22)); - if (adreno_gpu->quirks & ADRENO_QUIRK_TWO_PASS_USE_WFI) + if (adreno_gpu->info->quirks & ADRENO_QUIRK_TWO_PASS_USE_WFI) gpu_rmw(gpu, REG_A5XX_PC_DBG_ECO_CNTL, 0, (1 << 8)); gpu_write(gpu, REG_A5XX_PC_DBG_ECO_CNTL, 0xc0200100); @@ -573,6 +574,19 @@ static bool a5xx_idle(struct msm_gpu *gpu) return true; } +static int a5xx_fault_handler(void *arg, unsigned long iova, int flags) +{ + struct msm_gpu *gpu = arg; + pr_warn_ratelimited("*** gpu fault: iova=%08lx, flags=%d (%u,%u,%u,%u)\n", + iova, flags, + gpu_read(gpu, REG_A5XX_CP_SCRATCH_REG(4)), + gpu_read(gpu, REG_A5XX_CP_SCRATCH_REG(5)), + gpu_read(gpu, REG_A5XX_CP_SCRATCH_REG(6)), + gpu_read(gpu, REG_A5XX_CP_SCRATCH_REG(7))); + + return -EFAULT; +} + static void a5xx_cp_err_irq(struct msm_gpu *gpu) { u32 status = gpu_read(gpu, REG_A5XX_CP_INTERRUPT_STATUS); @@ -884,5 +898,8 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) return ERR_PTR(ret); } + if (gpu->aspace) + msm_mmu_set_fault_handler(gpu->aspace->mmu, gpu, a5xx_fault_handler); + return gpu; } diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index 893eb2b2531b..ece39b16a864 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -75,12 +75,14 @@ static const struct adreno_info gpulist[] = { .gmem = (SZ_1M + SZ_512K), .init = a4xx_gpu_init, }, { - .rev = ADRENO_REV(5, 3, 0, ANY_ID), + .rev = ADRENO_REV(5, 3, 0, 2), .revn = 530, .name = "A530", .pm4fw = "a530_pm4.fw", .pfpfw = "a530_pfp.fw", .gmem = SZ_1M, + .quirks = ADRENO_QUIRK_TWO_PASS_USE_WFI | + ADRENO_QUIRK_FAULT_DETECT_MASK, .init = a5xx_gpu_init, .gpmufw = "a530v3_gpmu.fw2", }, @@ -181,22 +183,51 @@ static void set_gpu_pdev(struct drm_device *dev, priv->gpu_pdev = pdev; } -static const struct { - const char *str; - uint32_t flag; -} quirks[] = { - { "qcom,gpu-quirk-two-pass-use-wfi", ADRENO_QUIRK_TWO_PASS_USE_WFI }, - { "qcom,gpu-quirk-fault-detect-mask", ADRENO_QUIRK_FAULT_DETECT_MASK }, -}; +static int find_chipid(struct device *dev, u32 *chipid) +{ + struct device_node *node = dev->of_node; + const char *compat; + int ret; + + /* first search the compat strings for qcom,adreno-XYZ.W: */ + ret = of_property_read_string_index(node, "compatible", 0, &compat); + if (ret == 0) { + unsigned rev, patch; + + if (sscanf(compat, "qcom,adreno-%u.%u", &rev, &patch) == 2) { + *chipid = 0; + *chipid |= (rev / 100) << 24; /* core */ + rev %= 100; + *chipid |= (rev / 10) << 16; /* major */ + rev %= 10; + *chipid |= rev << 8; /* minor */ + *chipid |= patch; + + return 0; + } + } + + /* and if that fails, fall back to legacy "qcom,chipid" property: */ + ret = of_property_read_u32(node, "qcom,chipid", chipid); + if (ret) + return ret; + + dev_warn(dev, "Using legacy qcom,chipid binding!\n"); + dev_warn(dev, "Use compatible qcom,adreno-%u%u%u.%u instead.\n", + (*chipid >> 24) & 0xff, (*chipid >> 16) & 0xff, + (*chipid >> 8) & 0xff, *chipid & 0xff); + + return 0; +} static int adreno_bind(struct device *dev, struct device *master, void *data) { static struct adreno_platform_config config = {}; struct device_node *child, *node = dev->of_node; u32 val; - int ret, i; + int ret; - ret = of_property_read_u32(node, "qcom,chipid", &val); + ret = find_chipid(dev, &val); if (ret) { dev_err(dev, "could not find chipid: %d\n", ret); return ret; @@ -224,14 +255,12 @@ static int adreno_bind(struct device *dev, struct device *master, void *data) } if (!config.fast_rate) { - dev_err(dev, "could not find clk rates\n"); - return -ENXIO; + dev_warn(dev, "could not find clk rates\n"); + /* This is a safe low speed for all devices: */ + config.fast_rate = 200000000; + config.slow_rate = 27000000; } - for (i = 0; i < ARRAY_SIZE(quirks); i++) - if (of_property_read_bool(node, quirks[i].str)) - config.quirks |= quirks[i].flag; - dev->platform_data = &config; set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev)); return 0; @@ -260,6 +289,7 @@ static int adreno_remove(struct platform_device *pdev) } static const struct of_device_id dt_match[] = { + { .compatible = "qcom,adreno" }, { .compatible = "qcom,adreno-3xx" }, /* for backwards compat w/ downstream kgsl DT files: */ { .compatible = "qcom,kgsl-3d0" }, diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 686a580c711a..c9bd1e6225f4 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -352,7 +352,6 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, adreno_gpu->gmem = adreno_gpu->info->gmem; adreno_gpu->revn = adreno_gpu->info->revn; adreno_gpu->rev = config->rev; - adreno_gpu->quirks = config->quirks; gpu->fast_rate = config->fast_rate; gpu->slow_rate = config->slow_rate; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index e8d55b0306ed..42e444a67630 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -75,6 +75,7 @@ struct adreno_info { const char *pm4fw, *pfpfw; const char *gpmufw; uint32_t gmem; + enum adreno_quirks quirks; struct msm_gpu *(*init)(struct drm_device *dev); }; @@ -116,8 +117,6 @@ struct adreno_gpu { * code (a3xx_gpu.c) and stored in this common location. */ const unsigned int *reg_offsets; - - uint32_t quirks; }; #define to_adreno_gpu(x) container_of(x, struct adreno_gpu, base) @@ -128,7 +127,6 @@ struct adreno_platform_config { #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING struct msm_bus_scale_pdata *bus_scale_table; #endif - uint32_t quirks; }; #define ADRENO_IDLE_TIMEOUT msecs_to_jiffies(1000) diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index ec572f8389ed..311c1c1e7d6c 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -18,9 +18,7 @@ struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi) if (!msm_dsi || !msm_dsi_device_connected(msm_dsi)) return NULL; - return (msm_dsi->device_flags & MIPI_DSI_MODE_VIDEO) ? - msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID] : - msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID]; + return msm_dsi->encoder; } static int dsi_get_phy(struct msm_dsi *msm_dsi) @@ -187,14 +185,13 @@ void __exit msm_dsi_unregister(void) } int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, - struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]) + struct drm_encoder *encoder) { struct msm_drm_private *priv = dev->dev_private; struct drm_bridge *ext_bridge; - int ret, i; + int ret; - if (WARN_ON(!encoders[MSM_DSI_VIDEO_ENCODER_ID] || - !encoders[MSM_DSI_CMD_ENCODER_ID])) + if (WARN_ON(!encoder)) return -EINVAL; msm_dsi->dev = dev; @@ -205,6 +202,8 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, goto fail; } + msm_dsi->encoder = encoder; + msm_dsi->bridge = msm_dsi_manager_bridge_init(msm_dsi->id); if (IS_ERR(msm_dsi->bridge)) { ret = PTR_ERR(msm_dsi->bridge); @@ -213,11 +212,6 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, goto fail; } - for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { - encoders[i]->bridge = msm_dsi->bridge; - msm_dsi->encoders[i] = encoders[i]; - } - /* * check if the dsi encoder output is connected to a panel or an * external bridge. We create a connector only if we're connected to a diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 03f115f532c2..32369975d155 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -27,14 +27,24 @@ #define DSI_1 1 #define DSI_MAX 2 +struct msm_dsi_phy_shared_timings; +struct msm_dsi_phy_clk_request; + enum msm_dsi_phy_type { MSM_DSI_PHY_28NM_HPM, MSM_DSI_PHY_28NM_LP, MSM_DSI_PHY_20NM, MSM_DSI_PHY_28NM_8960, + MSM_DSI_PHY_14NM, MSM_DSI_PHY_MAX }; +enum msm_dsi_phy_usecase { + MSM_DSI_PHY_STANDALONE, + MSM_DSI_PHY_MASTER, + MSM_DSI_PHY_SLAVE, +}; + #define DSI_DEV_REGULATOR_MAX 8 #define DSI_BUS_CLK_MAX 4 @@ -73,8 +83,8 @@ struct msm_dsi { struct device *phy_dev; bool phy_enabled; - /* the encoders we are hooked to (outside of dsi block) */ - struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]; + /* the encoder we are hooked to (outside of dsi block) */ + struct drm_encoder *encoder; int id; }; @@ -84,12 +94,9 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id); void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge); struct drm_connector *msm_dsi_manager_connector_init(u8 id); struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id); -int msm_dsi_manager_phy_enable(int id, - const unsigned long bit_rate, const unsigned long esc_rate, - u32 *clk_pre, u32 *clk_post); -void msm_dsi_manager_phy_disable(int id); int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg); bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len); +void msm_dsi_manager_attach_dsi_device(int id, u32 device_flags); int msm_dsi_manager_register(struct msm_dsi *msm_dsi); void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); @@ -111,6 +118,8 @@ int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, struct clk **byte_clk_provider, struct clk **pixel_clk_provider); void msm_dsi_pll_save_state(struct msm_dsi_pll *pll); int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll); +int msm_dsi_pll_set_usecase(struct msm_dsi_pll *pll, + enum msm_dsi_phy_usecase uc); #else static inline struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, enum msm_dsi_phy_type type, int id) { @@ -131,6 +140,11 @@ static inline int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll) { return 0; } +static inline int msm_dsi_pll_set_usecase(struct msm_dsi_pll *pll, + enum msm_dsi_phy_usecase uc) +{ + return -ENODEV; +} #endif /* dsi host */ @@ -146,7 +160,8 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, u32 len); int msm_dsi_host_enable(struct mipi_dsi_host *host); int msm_dsi_host_disable(struct mipi_dsi_host *host); -int msm_dsi_host_power_on(struct mipi_dsi_host *host); +int msm_dsi_host_power_on(struct mipi_dsi_host *host, + struct msm_dsi_phy_shared_timings *phy_shared_timings); int msm_dsi_host_power_off(struct mipi_dsi_host *host); int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, struct drm_display_mode *mode); @@ -157,6 +172,9 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer); void msm_dsi_host_unregister(struct mipi_dsi_host *host); int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, struct msm_dsi_pll *src_pll); +void msm_dsi_host_reset_phy(struct mipi_dsi_host *host); +void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host, + struct msm_dsi_phy_clk_request *clk_req); void msm_dsi_host_destroy(struct mipi_dsi_host *host); int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, struct drm_device *dev); @@ -164,14 +182,27 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi); /* dsi phy */ struct msm_dsi_phy; +struct msm_dsi_phy_shared_timings { + u32 clk_post; + u32 clk_pre; + bool clk_pre_inc_by_2; +}; + +struct msm_dsi_phy_clk_request { + unsigned long bitclk_rate; + unsigned long escclk_rate; +}; + void msm_dsi_phy_driver_register(void); void msm_dsi_phy_driver_unregister(void); int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, - const unsigned long bit_rate, const unsigned long esc_rate); + struct msm_dsi_phy_clk_request *clk_req); void msm_dsi_phy_disable(struct msm_dsi_phy *phy); -void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, - u32 *clk_pre, u32 *clk_post); +void msm_dsi_phy_get_shared_timings(struct msm_dsi_phy *phy, + struct msm_dsi_phy_shared_timings *shared_timing); struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy); +void msm_dsi_phy_set_usecase(struct msm_dsi_phy *phy, + enum msm_dsi_phy_usecase uc); #endif /* __DSI_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h index 39dff7d5e89b..b3d70ea42891 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.xml.h +++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h @@ -8,19 +8,10 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36965 bytes, from 2016-11-26 23:01:08) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 27887 bytes, from 2015-10-22 16:34:52) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) - -Copyright (C) 2013-2015 by the following authors: +- /local/mnt/workspace/source_trees/envytools/rnndb/../rnndb/dsi/dsi.xml ( 33004 bytes, from 2017-01-11 05:19:19) +- /local/mnt/workspace/source_trees/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-05-09 06:32:54) + +Copyright (C) 2013-2017 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) - Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) @@ -1304,5 +1295,257 @@ static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) #define REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG 0x00000018 +#define REG_DSI_14nm_PHY_CMN_REVISION_ID0 0x00000000 + +#define REG_DSI_14nm_PHY_CMN_REVISION_ID1 0x00000004 + +#define REG_DSI_14nm_PHY_CMN_REVISION_ID2 0x00000008 + +#define REG_DSI_14nm_PHY_CMN_REVISION_ID3 0x0000000c + +#define REG_DSI_14nm_PHY_CMN_CLK_CFG0 0x00000010 +#define DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_3_0__MASK 0x000000f0 +#define DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_3_0__SHIFT 4 +static inline uint32_t DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_3_0(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_3_0__SHIFT) & DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_3_0__MASK; +} +#define DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_7_4__MASK 0x000000f0 +#define DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_7_4__SHIFT 4 +static inline uint32_t DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_7_4(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_7_4__SHIFT) & DSI_14nm_PHY_CMN_CLK_CFG0_DIV_CTRL_7_4__MASK; +} + +#define REG_DSI_14nm_PHY_CMN_CLK_CFG1 0x00000014 +#define DSI_14nm_PHY_CMN_CLK_CFG1_DSICLK_SEL 0x00000001 + +#define REG_DSI_14nm_PHY_CMN_GLBL_TEST_CTRL 0x00000018 +#define DSI_14nm_PHY_CMN_GLBL_TEST_CTRL_BITCLK_HS_SEL 0x00000004 + +#define REG_DSI_14nm_PHY_CMN_CTRL_0 0x0000001c + +#define REG_DSI_14nm_PHY_CMN_CTRL_1 0x00000020 + +#define REG_DSI_14nm_PHY_CMN_HW_TRIGGER 0x00000024 + +#define REG_DSI_14nm_PHY_CMN_SW_CFG0 0x00000028 + +#define REG_DSI_14nm_PHY_CMN_SW_CFG1 0x0000002c + +#define REG_DSI_14nm_PHY_CMN_SW_CFG2 0x00000030 + +#define REG_DSI_14nm_PHY_CMN_HW_CFG0 0x00000034 + +#define REG_DSI_14nm_PHY_CMN_HW_CFG1 0x00000038 + +#define REG_DSI_14nm_PHY_CMN_HW_CFG2 0x0000003c + +#define REG_DSI_14nm_PHY_CMN_HW_CFG3 0x00000040 + +#define REG_DSI_14nm_PHY_CMN_HW_CFG4 0x00000044 + +#define REG_DSI_14nm_PHY_CMN_PLL_CNTRL 0x00000048 +#define DSI_14nm_PHY_CMN_PLL_CNTRL_PLL_START 0x00000001 + +#define REG_DSI_14nm_PHY_CMN_LDO_CNTRL 0x0000004c +#define DSI_14nm_PHY_CMN_LDO_CNTRL_VREG_CTRL__MASK 0x0000003f +#define DSI_14nm_PHY_CMN_LDO_CNTRL_VREG_CTRL__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_CMN_LDO_CNTRL_VREG_CTRL(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_CMN_LDO_CNTRL_VREG_CTRL__SHIFT) & DSI_14nm_PHY_CMN_LDO_CNTRL_VREG_CTRL__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN(uint32_t i0) { return 0x00000000 + 0x80*i0; } + +static inline uint32_t REG_DSI_14nm_PHY_LN_CFG0(uint32_t i0) { return 0x00000000 + 0x80*i0; } +#define DSI_14nm_PHY_LN_CFG0_PREPARE_DLY__MASK 0x000000c0 +#define DSI_14nm_PHY_LN_CFG0_PREPARE_DLY__SHIFT 6 +static inline uint32_t DSI_14nm_PHY_LN_CFG0_PREPARE_DLY(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_CFG0_PREPARE_DLY__SHIFT) & DSI_14nm_PHY_LN_CFG0_PREPARE_DLY__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_CFG1(uint32_t i0) { return 0x00000004 + 0x80*i0; } +#define DSI_14nm_PHY_LN_CFG1_HALFBYTECLK_EN 0x00000001 + +static inline uint32_t REG_DSI_14nm_PHY_LN_CFG2(uint32_t i0) { return 0x00000008 + 0x80*i0; } + +static inline uint32_t REG_DSI_14nm_PHY_LN_CFG3(uint32_t i0) { return 0x0000000c + 0x80*i0; } + +static inline uint32_t REG_DSI_14nm_PHY_LN_TEST_DATAPATH(uint32_t i0) { return 0x00000010 + 0x80*i0; } + +static inline uint32_t REG_DSI_14nm_PHY_LN_TEST_STR(uint32_t i0) { return 0x00000014 + 0x80*i0; } + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_4(uint32_t i0) { return 0x00000018 + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_4_HS_EXIT__MASK 0x000000ff +#define DSI_14nm_PHY_LN_TIMING_CTRL_4_HS_EXIT__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_4_HS_EXIT(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_4_HS_EXIT__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_4_HS_EXIT__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_5(uint32_t i0) { return 0x0000001c + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_5_HS_ZERO__MASK 0x000000ff +#define DSI_14nm_PHY_LN_TIMING_CTRL_5_HS_ZERO__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_5_HS_ZERO(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_5_HS_ZERO__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_5_HS_ZERO__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_6(uint32_t i0) { return 0x00000020 + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_6_HS_PREPARE__MASK 0x000000ff +#define DSI_14nm_PHY_LN_TIMING_CTRL_6_HS_PREPARE__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_6_HS_PREPARE(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_6_HS_PREPARE__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_6_HS_PREPARE__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_7(uint32_t i0) { return 0x00000024 + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_7_HS_TRAIL__MASK 0x000000ff +#define DSI_14nm_PHY_LN_TIMING_CTRL_7_HS_TRAIL__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_7_HS_TRAIL(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_7_HS_TRAIL__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_7_HS_TRAIL__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_8(uint32_t i0) { return 0x00000028 + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_8_HS_RQST__MASK 0x000000ff +#define DSI_14nm_PHY_LN_TIMING_CTRL_8_HS_RQST__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_8_HS_RQST(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_8_HS_RQST__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_8_HS_RQST__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_9(uint32_t i0) { return 0x0000002c + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_GO__MASK 0x00000007 +#define DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_GO__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_GO(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_GO__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_GO__MASK; +} +#define DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_SURE__MASK 0x00000070 +#define DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_SURE__SHIFT 4 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_SURE(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_SURE__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_SURE__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_10(uint32_t i0) { return 0x00000030 + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_10_TA_GET__MASK 0x00000007 +#define DSI_14nm_PHY_LN_TIMING_CTRL_10_TA_GET__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_10_TA_GET(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_10_TA_GET__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_10_TA_GET__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_TIMING_CTRL_11(uint32_t i0) { return 0x00000034 + 0x80*i0; } +#define DSI_14nm_PHY_LN_TIMING_CTRL_11_TRIG3_CMD__MASK 0x000000ff +#define DSI_14nm_PHY_LN_TIMING_CTRL_11_TRIG3_CMD__SHIFT 0 +static inline uint32_t DSI_14nm_PHY_LN_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) +{ + return ((val) << DSI_14nm_PHY_LN_TIMING_CTRL_11_TRIG3_CMD__SHIFT) & DSI_14nm_PHY_LN_TIMING_CTRL_11_TRIG3_CMD__MASK; +} + +static inline uint32_t REG_DSI_14nm_PHY_LN_STRENGTH_CTRL_0(uint32_t i0) { return 0x00000038 + 0x80*i0; } + +static inline uint32_t REG_DSI_14nm_PHY_LN_STRENGTH_CTRL_1(uint32_t i0) { return 0x0000003c + 0x80*i0; } + +static inline uint32_t REG_DSI_14nm_PHY_LN_VREG_CNTRL(uint32_t i0) { return 0x00000064 + 0x80*i0; } + +#define REG_DSI_14nm_PHY_PLL_IE_TRIM 0x00000000 + +#define REG_DSI_14nm_PHY_PLL_IP_TRIM 0x00000004 + +#define REG_DSI_14nm_PHY_PLL_IPTAT_TRIM 0x00000010 + +#define REG_DSI_14nm_PHY_PLL_CLKBUFLR_EN 0x0000001c + +#define REG_DSI_14nm_PHY_PLL_SYSCLK_EN_RESET 0x00000028 + +#define REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL 0x0000002c + +#define REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL2 0x00000030 + +#define REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL3 0x00000034 + +#define REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL4 0x00000038 + +#define REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL5 0x0000003c + +#define REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF1 0x00000040 + +#define REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF2 0x00000044 + +#define REG_DSI_14nm_PHY_PLL_KVCO_COUNT1 0x00000048 + +#define REG_DSI_14nm_PHY_PLL_KVCO_COUNT2 0x0000004c + +#define REG_DSI_14nm_PHY_PLL_VREF_CFG1 0x0000005c + +#define REG_DSI_14nm_PHY_PLL_KVCO_CODE 0x00000058 + +#define REG_DSI_14nm_PHY_PLL_VCO_DIV_REF1 0x0000006c + +#define REG_DSI_14nm_PHY_PLL_VCO_DIV_REF2 0x00000070 + +#define REG_DSI_14nm_PHY_PLL_VCO_COUNT1 0x00000074 + +#define REG_DSI_14nm_PHY_PLL_VCO_COUNT2 0x00000078 + +#define REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP1 0x0000007c + +#define REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP2 0x00000080 + +#define REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP3 0x00000084 + +#define REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP_EN 0x00000088 + +#define REG_DSI_14nm_PHY_PLL_PLL_VCO_TUNE 0x0000008c + +#define REG_DSI_14nm_PHY_PLL_DEC_START 0x00000090 + +#define REG_DSI_14nm_PHY_PLL_SSC_EN_CENTER 0x00000094 + +#define REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER1 0x00000098 + +#define REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER2 0x0000009c + +#define REG_DSI_14nm_PHY_PLL_SSC_PER1 0x000000a0 + +#define REG_DSI_14nm_PHY_PLL_SSC_PER2 0x000000a4 + +#define REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE1 0x000000a8 + +#define REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE2 0x000000ac + +#define REG_DSI_14nm_PHY_PLL_DIV_FRAC_START1 0x000000b4 + +#define REG_DSI_14nm_PHY_PLL_DIV_FRAC_START2 0x000000b8 + +#define REG_DSI_14nm_PHY_PLL_DIV_FRAC_START3 0x000000bc + +#define REG_DSI_14nm_PHY_PLL_TXCLK_EN 0x000000c0 + +#define REG_DSI_14nm_PHY_PLL_PLL_CRCTRL 0x000000c4 + +#define REG_DSI_14nm_PHY_PLL_RESET_SM_READY_STATUS 0x000000cc + +#define REG_DSI_14nm_PHY_PLL_PLL_MISC1 0x000000e8 + +#define REG_DSI_14nm_PHY_PLL_CP_SET_CUR 0x000000f0 + +#define REG_DSI_14nm_PHY_PLL_PLL_ICPMSET 0x000000f4 + +#define REG_DSI_14nm_PHY_PLL_PLL_ICPCSET 0x000000f8 + +#define REG_DSI_14nm_PHY_PLL_PLL_ICP_SET 0x000000fc + +#define REG_DSI_14nm_PHY_PLL_PLL_LPF1 0x00000100 + +#define REG_DSI_14nm_PHY_PLL_PLL_LPF2_POSTDIV 0x00000104 + +#define REG_DSI_14nm_PHY_PLL_PLL_BANDGAP 0x00000108 + #endif /* DSI_XML */ diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c index 63436d8ee470..a5d75c9b3a73 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -94,6 +94,30 @@ static const struct msm_dsi_config msm8994_dsi_cfg = { .num_dsi = 2, }; +/* + * TODO: core_mmss_clk fails to enable for some reason, but things work fine + * without it too. Figure out why it doesn't enable and uncomment below + */ +static const char * const dsi_8996_bus_clk_names[] = { + "mdp_core_clk", "iface_clk", "bus_clk", /* "core_mmss_clk", */ +}; + +static const struct msm_dsi_config msm8996_dsi_cfg = { + .io_offset = DSI_6G_REG_SHIFT, + .reg_cfg = { + .num = 2, + .regs = { + {"vdda", 18160, 1 }, /* 1.25 V */ + {"vcca", 17000, 32 }, /* 0.925 V */ + {"vddio", 100000, 100 },/* 1.8 V */ + }, + }, + .bus_clk_names = dsi_8996_bus_clk_names, + .num_bus_clks = ARRAY_SIZE(dsi_8996_bus_clk_names), + .io_start = { 0x994000, 0x996000 }, + .num_dsi = 2, +}; + static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { {MSM_DSI_VER_MAJOR_V2, MSM_DSI_V2_VER_MINOR_8064, &apq8064_dsi_cfg}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0, @@ -106,6 +130,7 @@ static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { &msm8974_apq8084_dsi_cfg}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3, &msm8994_dsi_cfg}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3_1, &msm8916_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_4_1, &msm8996_dsi_cfg}, }; const struct msm_dsi_cfg_handler *msm_dsi_cfg_get(u32 major, u32 minor) diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h index eeacc3232494..00a5da2663c6 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.h +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -24,6 +24,7 @@ #define MSM_DSI_6G_VER_MINOR_V1_2 0x10020000 #define MSM_DSI_6G_VER_MINOR_V1_3 0x10030000 #define MSM_DSI_6G_VER_MINOR_V1_3_1 0x10030001 +#define MSM_DSI_6G_VER_MINOR_V1_4_1 0x10040001 #define MSM_DSI_V2_VER_MINOR_8064 0x0 diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 3819fdefcae2..1fc07ce24686 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -691,17 +691,6 @@ static int dsi_calc_clk_rate(struct msm_dsi_host *msm_host) return 0; } -static void dsi_phy_sw_reset(struct msm_dsi_host *msm_host) -{ - DBG(""); - dsi_write(msm_host, REG_DSI_PHY_RESET, DSI_PHY_RESET_RESET); - /* Make sure fully reset */ - wmb(); - udelay(1000); - dsi_write(msm_host, REG_DSI_PHY_RESET, 0); - udelay(100); -} - static void dsi_intr_ctrl(struct msm_dsi_host *msm_host, u32 mask, int enable) { u32 intr; @@ -756,7 +745,7 @@ static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( } static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, - u32 clk_pre, u32 clk_post) + struct msm_dsi_phy_shared_timings *phy_shared_timings) { u32 flags = msm_host->mode_flags; enum mipi_dsi_pixel_format mipi_fmt = msm_host->format; @@ -819,10 +808,16 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, data |= DSI_TRIG_CTRL_BLOCK_DMA_WITHIN_FRAME; dsi_write(msm_host, REG_DSI_TRIG_CTRL, data); - data = DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(clk_post) | - DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE(clk_pre); + data = DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(phy_shared_timings->clk_post) | + DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE(phy_shared_timings->clk_pre); dsi_write(msm_host, REG_DSI_CLKOUT_TIMING_CTRL, data); + if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && + (cfg_hnd->minor > MSM_DSI_6G_VER_MINOR_V1_0) && + phy_shared_timings->clk_pre_inc_by_2) + dsi_write(msm_host, REG_DSI_T_CLK_PRE_EXTEND, + DSI_T_CLK_PRE_EXTEND_INC_BY_2_BYTECLK); + data = 0; if (!(flags & MIPI_DSI_MODE_EOT_PACKET)) data |= DSI_EOT_PACKET_CTRL_TX_EOT_APPEND; @@ -1482,6 +1477,8 @@ static int dsi_host_attach(struct mipi_dsi_host *host, msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; + msm_dsi_manager_attach_dsi_device(msm_host->id, dsi->mode_flags); + /* Some gpios defined in panel DT need to be controlled by host */ ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); if (ret) @@ -1557,8 +1554,9 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, prop = of_find_property(ep, "data-lanes", &len); if (!prop) { - dev_dbg(dev, "failed to find data lane mapping\n"); - return -EINVAL; + dev_dbg(dev, + "failed to find data lane mapping, using default\n"); + return 0; } num_lanes = len / sizeof(u32); @@ -1615,7 +1613,7 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) struct device *dev = &msm_host->pdev->dev; struct device_node *np = dev->of_node; struct device_node *endpoint, *device_node; - int ret; + int ret = 0; /* * Get the endpoint of the output port of the DSI host. In our case, @@ -1639,8 +1637,7 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) /* Get panel node from the output port's endpoint data */ device_node = of_graph_get_remote_port_parent(endpoint); if (!device_node) { - dev_err(dev, "%s: no valid device\n", __func__); - ret = -ENODEV; + dev_dbg(dev, "%s: no valid device\n", __func__); goto err; } @@ -2118,6 +2115,28 @@ exit: return ret; } +void msm_dsi_host_reset_phy(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + + DBG(""); + dsi_write(msm_host, REG_DSI_PHY_RESET, DSI_PHY_RESET_RESET); + /* Make sure fully reset */ + wmb(); + udelay(1000); + dsi_write(msm_host, REG_DSI_PHY_RESET, 0); + udelay(100); +} + +void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host, + struct msm_dsi_phy_clk_request *clk_req) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + + clk_req->bitclk_rate = msm_host->byte_clk_rate * 8; + clk_req->escclk_rate = msm_host->esc_clk_rate; +} + int msm_dsi_host_enable(struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); @@ -2165,10 +2184,10 @@ static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable) SFPB_GPREG_MASTER_PORT_EN(en)); } -int msm_dsi_host_power_on(struct mipi_dsi_host *host) +int msm_dsi_host_power_on(struct mipi_dsi_host *host, + struct msm_dsi_phy_shared_timings *phy_shared_timings) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - u32 clk_pre = 0, clk_post = 0; int ret = 0; mutex_lock(&msm_host->dev_mutex); @@ -2179,12 +2198,6 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) msm_dsi_sfpb_config(msm_host, true); - ret = dsi_calc_clk_rate(msm_host); - if (ret) { - pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); - goto unlock_ret; - } - ret = dsi_host_regulator_enable(msm_host); if (ret) { pr_err("%s:Failed to enable vregs.ret=%d\n", @@ -2192,23 +2205,6 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) goto unlock_ret; } - ret = dsi_bus_clk_enable(msm_host); - if (ret) { - pr_err("%s: failed to enable bus clocks, %d\n", __func__, ret); - goto fail_disable_reg; - } - - dsi_phy_sw_reset(msm_host); - ret = msm_dsi_manager_phy_enable(msm_host->id, - msm_host->byte_clk_rate * 8, - msm_host->esc_clk_rate, - &clk_pre, &clk_post); - dsi_bus_clk_disable(msm_host); - if (ret) { - pr_err("%s: failed to enable phy, %d\n", __func__, ret); - goto fail_disable_reg; - } - ret = dsi_clk_ctrl(msm_host, 1); if (ret) { pr_err("%s: failed to enable clocks. ret=%d\n", __func__, ret); @@ -2224,7 +2220,7 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) dsi_timing_setup(msm_host); dsi_sw_reset(msm_host); - dsi_ctrl_config(msm_host, true, clk_pre, clk_post); + dsi_ctrl_config(msm_host, true, phy_shared_timings); if (msm_host->disp_en_gpio) gpiod_set_value(msm_host->disp_en_gpio, 1); @@ -2253,15 +2249,13 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host) goto unlock_ret; } - dsi_ctrl_config(msm_host, false, 0, 0); + dsi_ctrl_config(msm_host, false, NULL); if (msm_host->disp_en_gpio) gpiod_set_value(msm_host->disp_en_gpio, 0); pinctrl_pm_select_sleep_state(&msm_host->pdev->dev); - msm_dsi_manager_phy_disable(msm_host->id); - dsi_clk_ctrl(msm_host, 0); dsi_host_regulator_disable(msm_host); @@ -2281,6 +2275,7 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, struct drm_display_mode *mode) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + int ret; if (msm_host->mode) { drm_mode_destroy(msm_host->dev, msm_host->mode); @@ -2293,6 +2288,12 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, return -ENOMEM; } + ret = dsi_calc_clk_rate(msm_host); + if (ret) { + pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index c8d1f19c9a6d..921270ea6059 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -72,11 +72,12 @@ static int dsi_mgr_parse_dual_dsi(struct device_node *np, int id) return 0; } -static int dsi_mgr_host_register(int id) +static int dsi_mgr_setup_components(int id) { struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); struct msm_dsi *clk_master_dsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); + struct msm_dsi *clk_slave_dsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); struct msm_dsi_pll *src_pll; int ret; @@ -85,15 +86,16 @@ static int dsi_mgr_host_register(int id) if (ret) return ret; + msm_dsi_phy_set_usecase(msm_dsi->phy, MSM_DSI_PHY_STANDALONE); src_pll = msm_dsi_phy_get_pll(msm_dsi->phy); ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); } else if (!other_dsi) { ret = 0; } else { - struct msm_dsi *mdsi = IS_MASTER_DSI_LINK(id) ? - msm_dsi : other_dsi; - struct msm_dsi *sdsi = IS_MASTER_DSI_LINK(id) ? - other_dsi : msm_dsi; + struct msm_dsi *master_link_dsi = IS_MASTER_DSI_LINK(id) ? + msm_dsi : other_dsi; + struct msm_dsi *slave_link_dsi = IS_MASTER_DSI_LINK(id) ? + other_dsi : msm_dsi; /* Register slave host first, so that slave DSI device * has a chance to probe, and do not block the master * DSI device's probe. @@ -101,14 +103,18 @@ static int dsi_mgr_host_register(int id) * because only master DSI device adds the panel to global * panel list. The panel's device is the master DSI device. */ - ret = msm_dsi_host_register(sdsi->host, false); + ret = msm_dsi_host_register(slave_link_dsi->host, false); if (ret) return ret; - ret = msm_dsi_host_register(mdsi->host, true); + ret = msm_dsi_host_register(master_link_dsi->host, true); if (ret) return ret; /* PLL0 is to drive both 2 DSI link clocks in Dual DSI mode. */ + msm_dsi_phy_set_usecase(clk_master_dsi->phy, + MSM_DSI_PHY_MASTER); + msm_dsi_phy_set_usecase(clk_slave_dsi->phy, + MSM_DSI_PHY_SLAVE); src_pll = msm_dsi_phy_get_pll(clk_master_dsi->phy); ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); if (ret) @@ -119,6 +125,84 @@ static int dsi_mgr_host_register(int id) return ret; } +static int enable_phy(struct msm_dsi *msm_dsi, int src_pll_id, + struct msm_dsi_phy_shared_timings *shared_timings) +{ + struct msm_dsi_phy_clk_request clk_req; + int ret; + + msm_dsi_host_get_phy_clk_req(msm_dsi->host, &clk_req); + + ret = msm_dsi_phy_enable(msm_dsi->phy, src_pll_id, &clk_req); + msm_dsi_phy_get_shared_timings(msm_dsi->phy, shared_timings); + + return ret; +} + +static int +dsi_mgr_phy_enable(int id, + struct msm_dsi_phy_shared_timings shared_timings[DSI_MAX]) +{ + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); + struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); + int src_pll_id = IS_DUAL_DSI() ? DSI_CLOCK_MASTER : id; + int ret; + + /* In case of dual DSI, some registers in PHY1 have been programmed + * during PLL0 clock's set_rate. The PHY1 reset called by host1 here + * will silently reset those PHY1 registers. Therefore we need to reset + * and enable both PHYs before any PLL clock operation. + */ + if (IS_DUAL_DSI() && mdsi && sdsi) { + if (!mdsi->phy_enabled && !sdsi->phy_enabled) { + msm_dsi_host_reset_phy(mdsi->host); + msm_dsi_host_reset_phy(sdsi->host); + + ret = enable_phy(mdsi, src_pll_id, + &shared_timings[DSI_CLOCK_MASTER]); + if (ret) + return ret; + ret = enable_phy(sdsi, src_pll_id, + &shared_timings[DSI_CLOCK_SLAVE]); + if (ret) { + msm_dsi_phy_disable(mdsi->phy); + return ret; + } + } + } else { + msm_dsi_host_reset_phy(mdsi->host); + ret = enable_phy(msm_dsi, src_pll_id, &shared_timings[id]); + if (ret) + return ret; + } + + msm_dsi->phy_enabled = true; + + return 0; +} + +static void dsi_mgr_phy_disable(int id) +{ + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); + struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); + + /* disable DSI phy + * In dual-dsi configuration, the phy should be disabled for the + * first controller only when the second controller is disabled. + */ + msm_dsi->phy_enabled = false; + if (IS_DUAL_DSI() && mdsi && sdsi) { + if (!mdsi->phy_enabled && !sdsi->phy_enabled) { + msm_dsi_phy_disable(sdsi->phy); + msm_dsi_phy_disable(mdsi->phy); + } + } else { + msm_dsi_phy_disable(msm_dsi->phy); + } +} + struct dsi_connector { struct drm_connector base; int id; @@ -168,6 +252,16 @@ static enum drm_connector_status dsi_mgr_connector_detect( msm_dsi->panel = msm_dsi_host_get_panel( other_dsi->host, NULL); + + if (msm_dsi->panel && kms->funcs->set_encoder_mode) { + bool cmd_mode = !(msm_dsi->device_flags & + MIPI_DSI_MODE_VIDEO); + struct drm_encoder *encoder = + msm_dsi_get_encoder(msm_dsi); + + kms->funcs->set_encoder_mode(kms, encoder, cmd_mode); + } + if (msm_dsi->panel && IS_DUAL_DSI()) drm_object_attach_property(&connector->base, connector->dev->mode_config.tile_property, 0); @@ -344,22 +438,31 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); struct mipi_dsi_host *host = msm_dsi->host; struct drm_panel *panel = msm_dsi->panel; + struct msm_dsi_phy_shared_timings phy_shared_timings[DSI_MAX]; bool is_dual_dsi = IS_DUAL_DSI(); int ret; DBG("id=%d", id); - if (!msm_dsi_device_connected(msm_dsi) || - (is_dual_dsi && (DSI_1 == id))) + if (!msm_dsi_device_connected(msm_dsi)) return; - ret = msm_dsi_host_power_on(host); + ret = dsi_mgr_phy_enable(id, phy_shared_timings); + if (ret) + goto phy_en_fail; + + /* Do nothing with the host if it is DSI 1 in case of dual DSI */ + if (is_dual_dsi && (DSI_1 == id)) + return; + + ret = msm_dsi_host_power_on(host, &phy_shared_timings[id]); if (ret) { pr_err("%s: power on host %d failed, %d\n", __func__, id, ret); goto host_on_fail; } if (is_dual_dsi && msm_dsi1) { - ret = msm_dsi_host_power_on(msm_dsi1->host); + ret = msm_dsi_host_power_on(msm_dsi1->host, + &phy_shared_timings[DSI_1]); if (ret) { pr_err("%s: power on host1 failed, %d\n", __func__, ret); @@ -418,6 +521,8 @@ panel_prep_fail: host1_on_fail: msm_dsi_host_power_off(host); host_on_fail: + dsi_mgr_phy_disable(id); +phy_en_fail: return; } @@ -443,10 +548,17 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) DBG("id=%d", id); - if (!msm_dsi_device_connected(msm_dsi) || - (is_dual_dsi && (DSI_1 == id))) + if (!msm_dsi_device_connected(msm_dsi)) return; + /* + * Do nothing with the host if it is DSI 1 in case of dual DSI. + * It is safe to call dsi_mgr_phy_disable() here because a single PHY + * won't be diabled until both PHYs request disable. + */ + if (is_dual_dsi && (DSI_1 == id)) + goto disable_phy; + if (panel) { ret = drm_panel_disable(panel); if (ret) @@ -481,6 +593,9 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) pr_err("%s: host1 power off failed, %d\n", __func__, ret); } + +disable_phy: + dsi_mgr_phy_disable(id); } static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, @@ -540,7 +655,7 @@ struct drm_connector *msm_dsi_manager_connector_init(u8 id) struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct drm_connector *connector = NULL; struct dsi_connector *dsi_connector; - int ret, i; + int ret; dsi_connector = kzalloc(sizeof(*dsi_connector), GFP_KERNEL); if (!dsi_connector) @@ -566,9 +681,7 @@ struct drm_connector *msm_dsi_manager_connector_init(u8 id) connector->interlace_allowed = 0; connector->doublescan_allowed = 0; - for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) - drm_mode_connector_attach_encoder(connector, - msm_dsi->encoders[i]); + drm_mode_connector_attach_encoder(connector, msm_dsi->encoder); return connector; } @@ -579,6 +692,7 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id) struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct drm_bridge *bridge = NULL; struct dsi_bridge *dsi_bridge; + struct drm_encoder *encoder; int ret; dsi_bridge = devm_kzalloc(msm_dsi->dev->dev, @@ -590,10 +704,12 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id) dsi_bridge->id = id; + encoder = msm_dsi->encoder; + bridge = &dsi_bridge->base; bridge->funcs = &dsi_mgr_bridge_funcs; - ret = drm_bridge_attach(msm_dsi->dev, bridge); + ret = drm_bridge_attach(encoder, bridge, NULL); if (ret) goto fail; @@ -619,20 +735,10 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id) ext_bridge = msm_dsi->external_bridge = msm_dsi_host_get_bridge(msm_dsi->host); - /* - * HACK: we may not know the external DSI bridge device's mode - * flags here. We'll get to know them only when the device - * attaches to the dsi host. For now, assume the bridge supports - * DSI video mode - */ - encoder = msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID]; + encoder = msm_dsi->encoder; /* link the internal dsi bridge to the external bridge */ - int_bridge->next = ext_bridge; - /* set the external bridge's encoder as dsi's encoder */ - ext_bridge->encoder = encoder; - - drm_bridge_attach(dev, ext_bridge); + drm_bridge_attach(encoder, ext_bridge, int_bridge); /* * we need the drm_connector created by the external bridge @@ -657,68 +763,6 @@ void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge) { } -int msm_dsi_manager_phy_enable(int id, - const unsigned long bit_rate, const unsigned long esc_rate, - u32 *clk_pre, u32 *clk_post) -{ - struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); - struct msm_dsi_phy *phy = msm_dsi->phy; - int src_pll_id = IS_DUAL_DSI() ? DSI_CLOCK_MASTER : id; - struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy); - int ret; - - ret = msm_dsi_phy_enable(phy, src_pll_id, bit_rate, esc_rate); - if (ret) - return ret; - - /* - * Reset DSI PHY silently changes its PLL registers to reset status, - * which will confuse clock driver and result in wrong output rate of - * link clocks. Restore PLL status if its PLL is being used as clock - * source. - */ - if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER)) { - ret = msm_dsi_pll_restore_state(pll); - if (ret) { - pr_err("%s: failed to restore pll state\n", __func__); - msm_dsi_phy_disable(phy); - return ret; - } - } - - msm_dsi->phy_enabled = true; - msm_dsi_phy_get_clk_pre_post(phy, clk_pre, clk_post); - - return 0; -} - -void msm_dsi_manager_phy_disable(int id) -{ - struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); - struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); - struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); - struct msm_dsi_phy *phy = msm_dsi->phy; - struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy); - - /* Save PLL status if it is a clock source */ - if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER)) - msm_dsi_pll_save_state(pll); - - /* disable DSI phy - * In dual-dsi configuration, the phy should be disabled for the - * first controller only when the second controller is disabled. - */ - msm_dsi->phy_enabled = false; - if (IS_DUAL_DSI() && mdsi && sdsi) { - if (!mdsi->phy_enabled && !sdsi->phy_enabled) { - msm_dsi_phy_disable(sdsi->phy); - msm_dsi_phy_disable(mdsi->phy); - } - } else { - msm_dsi_phy_disable(phy); - } -} - int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg) { struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); @@ -782,6 +826,33 @@ bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len) return true; } +void msm_dsi_manager_attach_dsi_device(int id, u32 device_flags) +{ + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct drm_device *dev = msm_dsi->dev; + struct msm_drm_private *priv; + struct msm_kms *kms; + struct drm_encoder *encoder; + + /* + * drm_device pointer is assigned to msm_dsi only in the modeset_init + * path. If mipi_dsi_attach() happens in DSI driver's probe path + * (generally the case when we're connected to a drm_panel of the type + * mipi_dsi_device), this would be NULL. In such cases, try to set the + * encoder mode in the DSI connector's detect() op. + */ + if (!dev) + return; + + priv = dev->dev_private; + kms = priv->kms; + encoder = msm_dsi_get_encoder(msm_dsi); + + if (encoder && kms->funcs->set_encoder_mode) + if (!(device_flags & MIPI_DSI_MODE_VIDEO)) + kms->funcs->set_encoder_mode(kms, encoder, true); +} + int msm_dsi_manager_register(struct msm_dsi *msm_dsi) { struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; @@ -806,7 +877,7 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi) goto fail; } - ret = dsi_mgr_host_register(id); + ret = dsi_mgr_setup_components(id); if (ret) { pr_err("%s: failed to register mipi dsi host for DSI %d\n", __func__, id); diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index f39386ed75e4..0c2eb9c9a1fc 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -54,8 +54,10 @@ static void dsi_dphy_timing_calc_clk_zero(struct msm_dsi_dphy_timing *timing, } int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, - const unsigned long bit_rate, const unsigned long esc_rate) + struct msm_dsi_phy_clk_request *clk_req) { + const unsigned long bit_rate = clk_req->bitclk_rate; + const unsigned long esc_rate = clk_req->escclk_rate; s32 ui, lpx; s32 tmax, tmin; s32 pcnt0 = 10; @@ -115,8 +117,8 @@ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, temp = ((timing->hs_exit >> 1) + 1) * 2 * ui; temp = 60 * coeff + 52 * ui - 24 * ui - temp; tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; - timing->clk_post = linear_inter(tmax, tmin, pcnt2, 0, false); - + timing->shared_timings.clk_post = linear_inter(tmax, tmin, pcnt2, 0, + false); tmax = 63; temp = ((timing->clk_prepare >> 1) + 1) * 2 * ui; temp += ((timing->clk_zero >> 1) + 1) * 2 * ui; @@ -124,17 +126,21 @@ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; if (tmin > tmax) { temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false); - timing->clk_pre = temp >> 1; + timing->shared_timings.clk_pre = temp >> 1; + timing->shared_timings.clk_pre_inc_by_2 = true; } else { - timing->clk_pre = linear_inter(tmax, tmin, pcnt2, 0, false); + timing->shared_timings.clk_pre = + linear_inter(tmax, tmin, pcnt2, 0, false); + timing->shared_timings.clk_pre_inc_by_2 = false; } timing->ta_go = 3; timing->ta_sure = 0; timing->ta_get = 4; - DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", - timing->clk_pre, timing->clk_post, timing->clk_zero, + DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + timing->shared_timings.clk_pre, timing->shared_timings.clk_post, + timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero, timing->clk_trail, timing->clk_prepare, timing->hs_exit, timing->hs_zero, timing->hs_prepare, timing->hs_trail, timing->hs_rqst); @@ -142,6 +148,123 @@ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, return 0; } +int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing, + struct msm_dsi_phy_clk_request *clk_req) +{ + const unsigned long bit_rate = clk_req->bitclk_rate; + const unsigned long esc_rate = clk_req->escclk_rate; + s32 ui, ui_x8, lpx; + s32 tmax, tmin; + s32 pcnt0 = 50; + s32 pcnt1 = 50; + s32 pcnt2 = 10; + s32 pcnt3 = 30; + s32 pcnt4 = 10; + s32 pcnt5 = 2; + s32 coeff = 1000; /* Precision, should avoid overflow */ + s32 hb_en, hb_en_ckln, pd_ckln, pd; + s32 val, val_ckln; + s32 temp; + + if (!bit_rate || !esc_rate) + return -EINVAL; + + timing->hs_halfbyte_en = 0; + hb_en = 0; + timing->hs_halfbyte_en_ckln = 0; + hb_en_ckln = 0; + timing->hs_prep_dly_ckln = (bit_rate > 100000000) ? 0 : 3; + pd_ckln = timing->hs_prep_dly_ckln; + timing->hs_prep_dly = (bit_rate > 120000000) ? 0 : 1; + pd = timing->hs_prep_dly; + + val = (hb_en << 2) + (pd << 1); + val_ckln = (hb_en_ckln << 2) + (pd_ckln << 1); + + ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000); + ui_x8 = ui << 3; + lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000); + + temp = S_DIV_ROUND_UP(38 * coeff - val_ckln * ui, ui_x8); + tmin = max_t(s32, temp, 0); + temp = (95 * coeff - val_ckln * ui) / ui_x8; + tmax = max_t(s32, temp, 0); + timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false); + + temp = 300 * coeff - ((timing->clk_prepare << 3) + val_ckln) * ui; + tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3; + tmax = (tmin > 255) ? 511 : 255; + timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false); + + tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8); + temp = 105 * coeff + 12 * ui - 20 * coeff; + tmax = (temp + 3 * ui) / ui_x8; + timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false); + + temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui - val * ui, ui_x8); + tmin = max_t(s32, temp, 0); + temp = (85 * coeff + 6 * ui - val * ui) / ui_x8; + tmax = max_t(s32, temp, 0); + timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false); + + temp = 145 * coeff + 10 * ui - ((timing->hs_prepare << 3) + val) * ui; + tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3; + tmax = 255; + timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false); + + tmin = DIV_ROUND_UP(60 * coeff + 4 * ui + 3 * ui, ui_x8); + temp = 105 * coeff + 12 * ui - 20 * coeff; + tmax = (temp + 3 * ui) / ui_x8; + timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false); + + temp = 50 * coeff + ((hb_en << 2) - 8) * ui; + timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8); + + tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1; + tmax = 255; + timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false); + + temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui; + timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8); + + temp = 60 * coeff + 52 * ui - 43 * ui; + tmin = DIV_ROUND_UP(temp, ui_x8) - 1; + tmax = 63; + timing->shared_timings.clk_post = + linear_inter(tmax, tmin, pcnt2, 0, false); + + temp = 8 * ui + ((timing->clk_prepare << 3) + val_ckln) * ui; + temp += (((timing->clk_zero + 3) << 3) + 11 - (pd_ckln << 1)) * ui; + temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) : + (((timing->hs_rqst_ckln << 3) + 8) * ui); + tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1; + tmax = 63; + if (tmin > tmax) { + temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false); + timing->shared_timings.clk_pre = temp >> 1; + timing->shared_timings.clk_pre_inc_by_2 = 1; + } else { + timing->shared_timings.clk_pre = + linear_inter(tmax, tmin, pcnt2, 0, false); + timing->shared_timings.clk_pre_inc_by_2 = 0; + } + + timing->ta_go = 3; + timing->ta_sure = 0; + timing->ta_get = 4; + + DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + timing->shared_timings.clk_pre, timing->shared_timings.clk_post, + timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero, + timing->clk_trail, timing->clk_prepare, timing->hs_exit, + timing->hs_zero, timing->hs_prepare, timing->hs_trail, + timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en, + timing->hs_halfbyte_en_ckln, timing->hs_prep_dly, + timing->hs_prep_dly_ckln); + + return 0; +} + void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, u32 bit_mask) { @@ -268,6 +391,10 @@ static const struct of_device_id dsi_phy_dt_match[] = { { .compatible = "qcom,dsi-phy-28nm-8960", .data = &dsi_phy_28nm_8960_cfgs }, #endif +#ifdef CONFIG_DRM_MSM_DSI_14NM_PHY + { .compatible = "qcom,dsi-phy-14nm", + .data = &dsi_phy_14nm_cfgs }, +#endif {} }; @@ -295,6 +422,24 @@ static int dsi_phy_get_id(struct msm_dsi_phy *phy) return -EINVAL; } +int msm_dsi_phy_init_common(struct msm_dsi_phy *phy) +{ + struct platform_device *pdev = phy->pdev; + int ret = 0; + + phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", + "DSI_PHY_REG"); + if (IS_ERR(phy->reg_base)) { + dev_err(&pdev->dev, "%s: failed to map phy regulator base\n", + __func__); + ret = -ENOMEM; + goto fail; + } + +fail: + return ret; +} + static int dsi_phy_driver_probe(struct platform_device *pdev) { struct msm_dsi_phy *phy; @@ -331,15 +476,6 @@ static int dsi_phy_driver_probe(struct platform_device *pdev) goto fail; } - phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", - "DSI_PHY_REG"); - if (IS_ERR(phy->reg_base)) { - dev_err(dev, "%s: failed to map phy regulator base\n", - __func__); - ret = -ENOMEM; - goto fail; - } - ret = dsi_phy_regulator_init(phy); if (ret) { dev_err(dev, "%s: failed to init regulator\n", __func__); @@ -353,6 +489,12 @@ static int dsi_phy_driver_probe(struct platform_device *pdev) goto fail; } + if (phy->cfg->ops.init) { + ret = phy->cfg->ops.init(phy); + if (ret) + goto fail; + } + /* PLL init will call into clk_register which requires * register access, so we need to enable power and ahb clock. */ @@ -410,7 +552,7 @@ void __exit msm_dsi_phy_driver_unregister(void) } int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, - const unsigned long bit_rate, const unsigned long esc_rate) + struct msm_dsi_phy_clk_request *clk_req) { struct device *dev = &phy->pdev->dev; int ret; @@ -418,21 +560,52 @@ int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, if (!phy || !phy->cfg->ops.enable) return -EINVAL; + ret = dsi_phy_enable_resource(phy); + if (ret) { + dev_err(dev, "%s: resource enable failed, %d\n", + __func__, ret); + goto res_en_fail; + } + ret = dsi_phy_regulator_enable(phy); if (ret) { dev_err(dev, "%s: regulator enable failed, %d\n", __func__, ret); - return ret; + goto reg_en_fail; } - ret = phy->cfg->ops.enable(phy, src_pll_id, bit_rate, esc_rate); + ret = phy->cfg->ops.enable(phy, src_pll_id, clk_req); if (ret) { dev_err(dev, "%s: phy enable failed, %d\n", __func__, ret); - dsi_phy_regulator_disable(phy); - return ret; + goto phy_en_fail; + } + + /* + * Resetting DSI PHY silently changes its PLL registers to reset status, + * which will confuse clock driver and result in wrong output rate of + * link clocks. Restore PLL status if its PLL is being used as clock + * source. + */ + if (phy->usecase != MSM_DSI_PHY_SLAVE) { + ret = msm_dsi_pll_restore_state(phy->pll); + if (ret) { + dev_err(dev, "%s: failed to restore pll state, %d\n", + __func__, ret); + goto pll_restor_fail; + } } return 0; + +pll_restor_fail: + if (phy->cfg->ops.disable) + phy->cfg->ops.disable(phy); +phy_en_fail: + dsi_phy_regulator_disable(phy); +reg_en_fail: + dsi_phy_disable_resource(phy); +res_en_fail: + return ret; } void msm_dsi_phy_disable(struct msm_dsi_phy *phy) @@ -440,21 +613,21 @@ void msm_dsi_phy_disable(struct msm_dsi_phy *phy) if (!phy || !phy->cfg->ops.disable) return; + /* Save PLL status if it is a clock source */ + if (phy->usecase != MSM_DSI_PHY_SLAVE) + msm_dsi_pll_save_state(phy->pll); + phy->cfg->ops.disable(phy); dsi_phy_regulator_disable(phy); + dsi_phy_disable_resource(phy); } -void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, - u32 *clk_pre, u32 *clk_post) +void msm_dsi_phy_get_shared_timings(struct msm_dsi_phy *phy, + struct msm_dsi_phy_shared_timings *shared_timings) { - if (!phy) - return; - - if (clk_pre) - *clk_pre = phy->timing.clk_pre; - if (clk_post) - *clk_post = phy->timing.clk_post; + memcpy(shared_timings, &phy->timing.shared_timings, + sizeof(*shared_timings)); } struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) @@ -465,3 +638,9 @@ struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) return phy->pll; } +void msm_dsi_phy_set_usecase(struct msm_dsi_phy *phy, + enum msm_dsi_phy_usecase uc) +{ + if (phy) + phy->usecase = uc; +} diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h index f24a85439b94..1733f6608a09 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -22,8 +22,9 @@ #define dsi_phy_write(offset, data) msm_writel((data), (offset)) struct msm_dsi_phy_ops { + int (*init) (struct msm_dsi_phy *phy); int (*enable)(struct msm_dsi_phy *phy, int src_pll_id, - const unsigned long bit_rate, const unsigned long esc_rate); + struct msm_dsi_phy_clk_request *clk_req); void (*disable)(struct msm_dsi_phy *phy); }; @@ -46,6 +47,7 @@ extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_14nm_cfgs; struct msm_dsi_dphy_timing { u32 clk_pre; @@ -61,12 +63,22 @@ struct msm_dsi_dphy_timing { u32 ta_go; u32 ta_sure; u32 ta_get; + + struct msm_dsi_phy_shared_timings shared_timings; + + /* For PHY v2 only */ + u32 hs_rqst_ckln; + u32 hs_prep_dly; + u32 hs_prep_dly_ckln; + u8 hs_halfbyte_en; + u8 hs_halfbyte_en_ckln; }; struct msm_dsi_phy { struct platform_device *pdev; void __iomem *base; void __iomem *reg_base; + void __iomem *lane_base; int id; struct clk *ahb_clk; @@ -75,6 +87,7 @@ struct msm_dsi_phy { struct msm_dsi_dphy_timing timing; const struct msm_dsi_phy_cfg *cfg; + enum msm_dsi_phy_usecase usecase; bool regulator_ldo_mode; struct msm_dsi_pll *pll; @@ -84,9 +97,12 @@ struct msm_dsi_phy { * PHY internal functions */ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, - const unsigned long bit_rate, const unsigned long esc_rate); + struct msm_dsi_phy_clk_request *clk_req); +int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing, + struct msm_dsi_phy_clk_request *clk_req); void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, u32 bit_mask); +int msm_dsi_phy_init_common(struct msm_dsi_phy *phy); #endif /* __DSI_PHY_H__ */ diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c new file mode 100644 index 000000000000..513f4234adc1 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi_phy.h" +#include "dsi.xml.h" + +#define PHY_14NM_CKLN_IDX 4 + +static void dsi_14nm_dphy_set_timing(struct msm_dsi_phy *phy, + struct msm_dsi_dphy_timing *timing, + int lane_idx) +{ + void __iomem *base = phy->lane_base; + bool clk_ln = (lane_idx == PHY_14NM_CKLN_IDX); + u32 zero = clk_ln ? timing->clk_zero : timing->hs_zero; + u32 prepare = clk_ln ? timing->clk_prepare : timing->hs_prepare; + u32 trail = clk_ln ? timing->clk_trail : timing->hs_trail; + u32 rqst = clk_ln ? timing->hs_rqst_ckln : timing->hs_rqst; + u32 prep_dly = clk_ln ? timing->hs_prep_dly_ckln : timing->hs_prep_dly; + u32 halfbyte_en = clk_ln ? timing->hs_halfbyte_en_ckln : + timing->hs_halfbyte_en; + + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_4(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_4_HS_EXIT(timing->hs_exit)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_5(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_5_HS_ZERO(zero)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_6(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_6_HS_PREPARE(prepare)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_7(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_7_HS_TRAIL(trail)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_8(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_8_HS_RQST(rqst)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_CFG0(lane_idx), + DSI_14nm_PHY_LN_CFG0_PREPARE_DLY(prep_dly)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_CFG1(lane_idx), + halfbyte_en ? DSI_14nm_PHY_LN_CFG1_HALFBYTECLK_EN : 0); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_9(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_GO(timing->ta_go) | + DSI_14nm_PHY_LN_TIMING_CTRL_9_TA_SURE(timing->ta_sure)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_10(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_10_TA_GET(timing->ta_get)); + dsi_phy_write(base + REG_DSI_14nm_PHY_LN_TIMING_CTRL_11(lane_idx), + DSI_14nm_PHY_LN_TIMING_CTRL_11_TRIG3_CMD(0xa0)); +} + +static int dsi_14nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, + struct msm_dsi_phy_clk_request *clk_req) +{ + struct msm_dsi_dphy_timing *timing = &phy->timing; + u32 data; + int i; + int ret; + void __iomem *base = phy->base; + void __iomem *lane_base = phy->lane_base; + + if (msm_dsi_dphy_timing_calc_v2(timing, clk_req)) { + dev_err(&phy->pdev->dev, + "%s: D-PHY timing calculation failed\n", __func__); + return -EINVAL; + } + + data = 0x1c; + if (phy->usecase != MSM_DSI_PHY_STANDALONE) + data |= DSI_14nm_PHY_CMN_LDO_CNTRL_VREG_CTRL(32); + dsi_phy_write(base + REG_DSI_14nm_PHY_CMN_LDO_CNTRL, data); + + dsi_phy_write(base + REG_DSI_14nm_PHY_CMN_GLBL_TEST_CTRL, 0x1); + + /* 4 data lanes + 1 clk lane configuration */ + for (i = 0; i < 5; i++) { + dsi_phy_write(lane_base + REG_DSI_14nm_PHY_LN_VREG_CNTRL(i), + 0x1d); + + dsi_phy_write(lane_base + + REG_DSI_14nm_PHY_LN_STRENGTH_CTRL_0(i), 0xff); + dsi_phy_write(lane_base + + REG_DSI_14nm_PHY_LN_STRENGTH_CTRL_1(i), + (i == PHY_14NM_CKLN_IDX) ? 0x00 : 0x06); + + dsi_phy_write(lane_base + REG_DSI_14nm_PHY_LN_CFG3(i), + (i == PHY_14NM_CKLN_IDX) ? 0x8f : 0x0f); + dsi_phy_write(lane_base + REG_DSI_14nm_PHY_LN_CFG2(i), 0x10); + dsi_phy_write(lane_base + REG_DSI_14nm_PHY_LN_TEST_DATAPATH(i), + 0); + dsi_phy_write(lane_base + REG_DSI_14nm_PHY_LN_TEST_STR(i), + 0x88); + + dsi_14nm_dphy_set_timing(phy, timing, i); + } + + /* Make sure PLL is not start */ + dsi_phy_write(base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 0x00); + + wmb(); /* make sure everything is written before reset and enable */ + + /* reset digital block */ + dsi_phy_write(base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0x80); + wmb(); /* ensure reset is asserted */ + udelay(100); + dsi_phy_write(base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0x00); + + msm_dsi_phy_set_src_pll(phy, src_pll_id, + REG_DSI_14nm_PHY_CMN_GLBL_TEST_CTRL, + DSI_14nm_PHY_CMN_GLBL_TEST_CTRL_BITCLK_HS_SEL); + + ret = msm_dsi_pll_set_usecase(phy->pll, phy->usecase); + if (ret) { + dev_err(&phy->pdev->dev, "%s: set pll usecase failed, %d\n", + __func__, ret); + return ret; + } + + /* Remove power down from PLL and all lanes */ + dsi_phy_write(base + REG_DSI_14nm_PHY_CMN_CTRL_0, 0xff); + + return 0; +} + +static void dsi_14nm_phy_disable(struct msm_dsi_phy *phy) +{ + dsi_phy_write(phy->base + REG_DSI_14nm_PHY_CMN_GLBL_TEST_CTRL, 0); + dsi_phy_write(phy->base + REG_DSI_14nm_PHY_CMN_CTRL_0, 0); + + /* ensure that the phy is completely disabled */ + wmb(); +} + +static int dsi_14nm_phy_init(struct msm_dsi_phy *phy) +{ + struct platform_device *pdev = phy->pdev; + + phy->lane_base = msm_ioremap(pdev, "dsi_phy_lane", + "DSI_PHY_LANE"); + if (IS_ERR(phy->lane_base)) { + dev_err(&pdev->dev, "%s: failed to map phy lane base\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +const struct msm_dsi_phy_cfg dsi_phy_14nm_cfgs = { + .type = MSM_DSI_PHY_14NM, + .src_pll_truthtable = { {false, false}, {true, false} }, + .reg_cfg = { + .num = 1, + .regs = { + {"vcca", 17000, 32}, + }, + }, + .ops = { + .enable = dsi_14nm_phy_enable, + .disable = dsi_14nm_phy_disable, + .init = dsi_14nm_phy_init, + }, + .io_start = { 0x994400, 0x996400 }, + .num_dsi_phy = 2, +}; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c index c757e2070cac..1ca6c69516f5 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c @@ -72,7 +72,7 @@ static void dsi_20nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) } static int dsi_20nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, - const unsigned long bit_rate, const unsigned long esc_rate) + struct msm_dsi_phy_clk_request *clk_req) { struct msm_dsi_dphy_timing *timing = &phy->timing; int i; @@ -81,7 +81,7 @@ static int dsi_20nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, DBG(""); - if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { + if (msm_dsi_dphy_timing_calc(timing, clk_req)) { dev_err(&phy->pdev->dev, "%s: D-PHY timing calculation failed\n", __func__); return -EINVAL; @@ -145,6 +145,7 @@ const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs = { .ops = { .enable = dsi_20nm_phy_enable, .disable = dsi_20nm_phy_disable, + .init = msm_dsi_phy_init_common, }, .io_start = { 0xfd998300, 0xfd9a0300 }, .num_dsi_phy = 2, diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c index 63d7fba31380..4972b52cbe44 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c @@ -67,7 +67,7 @@ static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) } static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, - const unsigned long bit_rate, const unsigned long esc_rate) + struct msm_dsi_phy_clk_request *clk_req) { struct msm_dsi_dphy_timing *timing = &phy->timing; int i; @@ -75,7 +75,7 @@ static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, DBG(""); - if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { + if (msm_dsi_dphy_timing_calc(timing, clk_req)) { dev_err(&phy->pdev->dev, "%s: D-PHY timing calculation failed\n", __func__); return -EINVAL; @@ -144,6 +144,7 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs = { .ops = { .enable = dsi_28nm_phy_enable, .disable = dsi_28nm_phy_disable, + .init = msm_dsi_phy_init_common, }, .io_start = { 0xfd922b00, 0xfd923100 }, .num_dsi_phy = 2, @@ -161,6 +162,7 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = { .ops = { .enable = dsi_28nm_phy_enable, .disable = dsi_28nm_phy_disable, + .init = msm_dsi_phy_init_common, }, .io_start = { 0x1a98500 }, .num_dsi_phy = 1, diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c index 7bdb9de54968..398004463498 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c @@ -124,14 +124,14 @@ static void dsi_28nm_phy_lane_config(struct msm_dsi_phy *phy) } static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, - const unsigned long bit_rate, const unsigned long esc_rate) + struct msm_dsi_phy_clk_request *clk_req) { struct msm_dsi_dphy_timing *timing = &phy->timing; void __iomem *base = phy->base; DBG(""); - if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { + if (msm_dsi_dphy_timing_calc(timing, clk_req)) { dev_err(&phy->pdev->dev, "%s: D-PHY timing calculation failed\n", __func__); return -EINVAL; @@ -191,6 +191,7 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs = { .ops = { .enable = dsi_28nm_phy_enable, .disable = dsi_28nm_phy_disable, + .init = msm_dsi_phy_init_common, }, .io_start = { 0x4700300, 0x5800300 }, .num_dsi_phy = 2, diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c index 5cd438f91afe..bc289f5c9078 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c @@ -140,6 +140,15 @@ int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll) return 0; } +int msm_dsi_pll_set_usecase(struct msm_dsi_pll *pll, + enum msm_dsi_phy_usecase uc) +{ + if (pll->set_usecase) + return pll->set_usecase(pll, uc); + + return 0; +} + struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, enum msm_dsi_phy_type type, int id) { @@ -154,6 +163,9 @@ struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, case MSM_DSI_PHY_28NM_8960: pll = msm_dsi_pll_28nm_8960_init(pdev, id); break; + case MSM_DSI_PHY_14NM: + pll = msm_dsi_pll_14nm_init(pdev, id); + break; default: pll = ERR_PTR(-ENXIO); break; diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h index 2cf1664723e8..f63e7ada74a8 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h @@ -41,6 +41,8 @@ struct msm_dsi_pll { void (*destroy)(struct msm_dsi_pll *pll); void (*save_state)(struct msm_dsi_pll *pll); int (*restore_state)(struct msm_dsi_pll *pll); + int (*set_usecase)(struct msm_dsi_pll *pll, + enum msm_dsi_phy_usecase uc); }; #define hw_clk_to_pll(x) container_of(x, struct msm_dsi_pll, clk_hw) @@ -104,5 +106,14 @@ static inline struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init( } #endif +#ifdef CONFIG_DRM_MSM_DSI_14NM_PHY +struct msm_dsi_pll *msm_dsi_pll_14nm_init(struct platform_device *pdev, int id); +#else +static inline struct msm_dsi_pll * +msm_dsi_pll_14nm_init(struct platform_device *pdev, int id) +{ + return ERR_PTR(-ENODEV); +} +#endif #endif /* __DSI_PLL_H__ */ diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_14nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_14nm.c new file mode 100644 index 000000000000..fe15aa64086f --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_14nm.c @@ -0,0 +1,1104 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#include "dsi_pll.h" +#include "dsi.xml.h" + +/* + * DSI PLL 14nm - clock diagram (eg: DSI0): + * + * dsi0n1_postdiv_clk + * | + * | + * +----+ | +----+ + * dsi0vco_clk ---| n1 |--o--| /8 |-- dsi0pllbyte + * +----+ | +----+ + * | dsi0n1_postdivby2_clk + * | +----+ | + * o---| /2 |--o--|\ + * | +----+ | \ +----+ + * | | |--| n2 |-- dsi0pll + * o--------------| / +----+ + * |/ + */ + +#define POLL_MAX_READS 15 +#define POLL_TIMEOUT_US 1000 + +#define NUM_PROVIDED_CLKS 2 + +#define VCO_REF_CLK_RATE 19200000 +#define VCO_MIN_RATE 1300000000UL +#define VCO_MAX_RATE 2600000000UL + +#define DSI_BYTE_PLL_CLK 0 +#define DSI_PIXEL_PLL_CLK 1 + +#define DSI_PLL_DEFAULT_VCO_POSTDIV 1 + +struct dsi_pll_input { + u32 fref; /* reference clk */ + u32 fdata; /* bit clock rate */ + u32 dsiclk_sel; /* Mux configuration (see diagram) */ + u32 ssc_en; /* SSC enable/disable */ + u32 ldo_en; + + /* fixed params */ + u32 refclk_dbler_en; + u32 vco_measure_time; + u32 kvco_measure_time; + u32 bandgap_timer; + u32 pll_wakeup_timer; + u32 plllock_cnt; + u32 plllock_rng; + u32 ssc_center; + u32 ssc_adj_period; + u32 ssc_spread; + u32 ssc_freq; + u32 pll_ie_trim; + u32 pll_ip_trim; + u32 pll_iptat_trim; + u32 pll_cpcset_cur; + u32 pll_cpmset_cur; + + u32 pll_icpmset; + u32 pll_icpcset; + + u32 pll_icpmset_p; + u32 pll_icpmset_m; + + u32 pll_icpcset_p; + u32 pll_icpcset_m; + + u32 pll_lpf_res1; + u32 pll_lpf_cap1; + u32 pll_lpf_cap2; + u32 pll_c3ctrl; + u32 pll_r3ctrl; +}; + +struct dsi_pll_output { + u32 pll_txclk_en; + u32 dec_start; + u32 div_frac_start; + u32 ssc_period; + u32 ssc_step_size; + u32 plllock_cmp; + u32 pll_vco_div_ref; + u32 pll_vco_count; + u32 pll_kvco_div_ref; + u32 pll_kvco_count; + u32 pll_misc1; + u32 pll_lpf2_postdiv; + u32 pll_resetsm_cntrl; + u32 pll_resetsm_cntrl2; + u32 pll_resetsm_cntrl5; + u32 pll_kvco_code; + + u32 cmn_clk_cfg0; + u32 cmn_clk_cfg1; + u32 cmn_ldo_cntrl; + + u32 pll_postdiv; + u32 fcvo; +}; + +struct pll_14nm_cached_state { + unsigned long vco_rate; + u8 n2postdiv; + u8 n1postdiv; +}; + +struct dsi_pll_14nm { + struct msm_dsi_pll base; + + int id; + struct platform_device *pdev; + + void __iomem *phy_cmn_mmio; + void __iomem *mmio; + + int vco_delay; + + struct dsi_pll_input in; + struct dsi_pll_output out; + + /* protects REG_DSI_14nm_PHY_CMN_CLK_CFG0 register */ + spinlock_t postdiv_lock; + + u64 vco_current_rate; + u64 vco_ref_clk_rate; + + /* private clocks: */ + struct clk_hw *hws[NUM_DSI_CLOCKS_MAX]; + u32 num_hws; + + /* clock-provider: */ + struct clk_hw_onecell_data *hw_data; + + struct pll_14nm_cached_state cached_state; + + enum msm_dsi_phy_usecase uc; + struct dsi_pll_14nm *slave; +}; + +#define to_pll_14nm(x) container_of(x, struct dsi_pll_14nm, base) + +/* + * Private struct for N1/N2 post-divider clocks. These clocks are similar to + * the generic clk_divider class of clocks. The only difference is that it + * also sets the slave DSI PLL's post-dividers if in Dual DSI mode + */ +struct dsi_pll_14nm_postdiv { + struct clk_hw hw; + + /* divider params */ + u8 shift; + u8 width; + u8 flags; /* same flags as used by clk_divider struct */ + + struct dsi_pll_14nm *pll; +}; + +#define to_pll_14nm_postdiv(_hw) container_of(_hw, struct dsi_pll_14nm_postdiv, hw) + +/* + * Global list of private DSI PLL struct pointers. We need this for Dual DSI + * mode, where the master PLL's clk_ops needs access the slave's private data + */ +static struct dsi_pll_14nm *pll_14nm_list[DSI_MAX]; + +static bool pll_14nm_poll_for_ready(struct dsi_pll_14nm *pll_14nm, + u32 nb_tries, u32 timeout_us) +{ + bool pll_locked = false; + void __iomem *base = pll_14nm->mmio; + u32 tries, val; + + tries = nb_tries; + while (tries--) { + val = pll_read(base + + REG_DSI_14nm_PHY_PLL_RESET_SM_READY_STATUS); + pll_locked = !!(val & BIT(5)); + + if (pll_locked) + break; + + udelay(timeout_us); + } + + if (!pll_locked) { + tries = nb_tries; + while (tries--) { + val = pll_read(base + + REG_DSI_14nm_PHY_PLL_RESET_SM_READY_STATUS); + pll_locked = !!(val & BIT(0)); + + if (pll_locked) + break; + + udelay(timeout_us); + } + } + + DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* "); + + return pll_locked; +} + +static void dsi_pll_14nm_input_init(struct dsi_pll_14nm *pll) +{ + pll->in.fref = pll->vco_ref_clk_rate; + pll->in.fdata = 0; + pll->in.dsiclk_sel = 1; /* Use the /2 path in Mux */ + pll->in.ldo_en = 0; /* disabled for now */ + + /* fixed input */ + pll->in.refclk_dbler_en = 0; + pll->in.vco_measure_time = 5; + pll->in.kvco_measure_time = 5; + pll->in.bandgap_timer = 4; + pll->in.pll_wakeup_timer = 5; + pll->in.plllock_cnt = 1; + pll->in.plllock_rng = 0; + + /* + * SSC is enabled by default. We might need DT props for configuring + * some SSC params like PPM and center/down spread etc. + */ + pll->in.ssc_en = 1; + pll->in.ssc_center = 0; /* down spread by default */ + pll->in.ssc_spread = 5; /* PPM / 1000 */ + pll->in.ssc_freq = 31500; /* default recommended */ + pll->in.ssc_adj_period = 37; + + pll->in.pll_ie_trim = 4; + pll->in.pll_ip_trim = 4; + pll->in.pll_cpcset_cur = 1; + pll->in.pll_cpmset_cur = 1; + pll->in.pll_icpmset = 4; + pll->in.pll_icpcset = 4; + pll->in.pll_icpmset_p = 0; + pll->in.pll_icpmset_m = 0; + pll->in.pll_icpcset_p = 0; + pll->in.pll_icpcset_m = 0; + pll->in.pll_lpf_res1 = 3; + pll->in.pll_lpf_cap1 = 11; + pll->in.pll_lpf_cap2 = 1; + pll->in.pll_iptat_trim = 7; + pll->in.pll_c3ctrl = 2; + pll->in.pll_r3ctrl = 1; +} + +#define CEIL(x, y) (((x) + ((y) - 1)) / (y)) + +static void pll_14nm_ssc_calc(struct dsi_pll_14nm *pll) +{ + u32 period, ssc_period; + u32 ref, rem; + u64 step_size; + + DBG("vco=%lld ref=%lld", pll->vco_current_rate, pll->vco_ref_clk_rate); + + ssc_period = pll->in.ssc_freq / 500; + period = (u32)pll->vco_ref_clk_rate / 1000; + ssc_period = CEIL(period, ssc_period); + ssc_period -= 1; + pll->out.ssc_period = ssc_period; + + DBG("ssc freq=%d spread=%d period=%d", pll->in.ssc_freq, + pll->in.ssc_spread, pll->out.ssc_period); + + step_size = (u32)pll->vco_current_rate; + ref = pll->vco_ref_clk_rate; + ref /= 1000; + step_size = div_u64(step_size, ref); + step_size <<= 20; + step_size = div_u64(step_size, 1000); + step_size *= pll->in.ssc_spread; + step_size = div_u64(step_size, 1000); + step_size *= (pll->in.ssc_adj_period + 1); + + rem = 0; + step_size = div_u64_rem(step_size, ssc_period + 1, &rem); + if (rem) + step_size++; + + DBG("step_size=%lld", step_size); + + step_size &= 0x0ffff; /* take lower 16 bits */ + + pll->out.ssc_step_size = step_size; +} + +static void pll_14nm_dec_frac_calc(struct dsi_pll_14nm *pll) +{ + struct dsi_pll_input *pin = &pll->in; + struct dsi_pll_output *pout = &pll->out; + u64 multiplier = BIT(20); + u64 dec_start_multiple, dec_start, pll_comp_val; + u32 duration, div_frac_start; + u64 vco_clk_rate = pll->vco_current_rate; + u64 fref = pll->vco_ref_clk_rate; + + DBG("vco_clk_rate=%lld ref_clk_rate=%lld", vco_clk_rate, fref); + + dec_start_multiple = div_u64(vco_clk_rate * multiplier, fref); + div_u64_rem(dec_start_multiple, multiplier, &div_frac_start); + + dec_start = div_u64(dec_start_multiple, multiplier); + + pout->dec_start = (u32)dec_start; + pout->div_frac_start = div_frac_start; + + if (pin->plllock_cnt == 0) + duration = 1024; + else if (pin->plllock_cnt == 1) + duration = 256; + else if (pin->plllock_cnt == 2) + duration = 128; + else + duration = 32; + + pll_comp_val = duration * dec_start_multiple; + pll_comp_val = div_u64(pll_comp_val, multiplier); + do_div(pll_comp_val, 10); + + pout->plllock_cmp = (u32)pll_comp_val; + + pout->pll_txclk_en = 1; + pout->cmn_ldo_cntrl = 0x3c; +} + +static u32 pll_14nm_kvco_slop(u32 vrate) +{ + u32 slop = 0; + + if (vrate > VCO_MIN_RATE && vrate <= 1800000000UL) + slop = 600; + else if (vrate > 1800000000UL && vrate < 2300000000UL) + slop = 400; + else if (vrate > 2300000000UL && vrate < VCO_MAX_RATE) + slop = 280; + + return slop; +} + +static void pll_14nm_calc_vco_count(struct dsi_pll_14nm *pll) +{ + struct dsi_pll_input *pin = &pll->in; + struct dsi_pll_output *pout = &pll->out; + u64 vco_clk_rate = pll->vco_current_rate; + u64 fref = pll->vco_ref_clk_rate; + u64 data; + u32 cnt; + + data = fref * pin->vco_measure_time; + do_div(data, 1000000); + data &= 0x03ff; /* 10 bits */ + data -= 2; + pout->pll_vco_div_ref = data; + + data = div_u64(vco_clk_rate, 1000000); /* unit is Mhz */ + data *= pin->vco_measure_time; + do_div(data, 10); + pout->pll_vco_count = data; + + data = fref * pin->kvco_measure_time; + do_div(data, 1000000); + data &= 0x03ff; /* 10 bits */ + data -= 1; + pout->pll_kvco_div_ref = data; + + cnt = pll_14nm_kvco_slop(vco_clk_rate); + cnt *= 2; + cnt /= 100; + cnt *= pin->kvco_measure_time; + pout->pll_kvco_count = cnt; + + pout->pll_misc1 = 16; + pout->pll_resetsm_cntrl = 48; + pout->pll_resetsm_cntrl2 = pin->bandgap_timer << 3; + pout->pll_resetsm_cntrl5 = pin->pll_wakeup_timer; + pout->pll_kvco_code = 0; +} + +static void pll_db_commit_ssc(struct dsi_pll_14nm *pll) +{ + void __iomem *base = pll->mmio; + struct dsi_pll_input *pin = &pll->in; + struct dsi_pll_output *pout = &pll->out; + u8 data; + + data = pin->ssc_adj_period; + data &= 0x0ff; + pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER1, data); + data = (pin->ssc_adj_period >> 8); + data &= 0x03; + pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_ADJ_PER2, data); + + data = pout->ssc_period; + data &= 0x0ff; + pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_PER1, data); + data = (pout->ssc_period >> 8); + data &= 0x0ff; + pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_PER2, data); + + data = pout->ssc_step_size; + data &= 0x0ff; + pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE1, data); + data = (pout->ssc_step_size >> 8); + data &= 0x0ff; + pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_STEP_SIZE2, data); + + data = (pin->ssc_center & 0x01); + data <<= 1; + data |= 0x01; /* enable */ + pll_write(base + REG_DSI_14nm_PHY_PLL_SSC_EN_CENTER, data); + + wmb(); /* make sure register committed */ +} + +static void pll_db_commit_common(struct dsi_pll_14nm *pll, + struct dsi_pll_input *pin, + struct dsi_pll_output *pout) +{ + void __iomem *base = pll->mmio; + u8 data; + + /* confgiure the non frequency dependent pll registers */ + data = 0; + pll_write(base + REG_DSI_14nm_PHY_PLL_SYSCLK_EN_RESET, data); + + data = pout->pll_txclk_en; + pll_write(base + REG_DSI_14nm_PHY_PLL_TXCLK_EN, data); + + data = pout->pll_resetsm_cntrl; + pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL, data); + data = pout->pll_resetsm_cntrl2; + pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL2, data); + data = pout->pll_resetsm_cntrl5; + pll_write(base + REG_DSI_14nm_PHY_PLL_RESETSM_CNTRL5, data); + + data = pout->pll_vco_div_ref & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_DIV_REF1, data); + data = (pout->pll_vco_div_ref >> 8) & 0x3; + pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_DIV_REF2, data); + + data = pout->pll_kvco_div_ref & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF1, data); + data = (pout->pll_kvco_div_ref >> 8) & 0x3; + pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_DIV_REF2, data); + + data = pout->pll_misc1; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_MISC1, data); + + data = pin->pll_ie_trim; + pll_write(base + REG_DSI_14nm_PHY_PLL_IE_TRIM, data); + + data = pin->pll_ip_trim; + pll_write(base + REG_DSI_14nm_PHY_PLL_IP_TRIM, data); + + data = pin->pll_cpmset_cur << 3 | pin->pll_cpcset_cur; + pll_write(base + REG_DSI_14nm_PHY_PLL_CP_SET_CUR, data); + + data = pin->pll_icpcset_p << 3 | pin->pll_icpcset_m; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICPCSET, data); + + data = pin->pll_icpmset_p << 3 | pin->pll_icpcset_m; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICPMSET, data); + + data = pin->pll_icpmset << 3 | pin->pll_icpcset; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_ICP_SET, data); + + data = pin->pll_lpf_cap2 << 4 | pin->pll_lpf_cap1; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_LPF1, data); + + data = pin->pll_iptat_trim; + pll_write(base + REG_DSI_14nm_PHY_PLL_IPTAT_TRIM, data); + + data = pin->pll_c3ctrl | pin->pll_r3ctrl << 4; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_CRCTRL, data); +} + +static void pll_14nm_software_reset(struct dsi_pll_14nm *pll_14nm) +{ + void __iomem *cmn_base = pll_14nm->phy_cmn_mmio; + + /* de assert pll start and apply pll sw reset */ + + /* stop pll */ + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 0); + + /* pll sw reset */ + pll_write_udelay(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0x20, 10); + wmb(); /* make sure register committed */ + + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_1, 0); + wmb(); /* make sure register committed */ +} + +static void pll_db_commit_14nm(struct dsi_pll_14nm *pll, + struct dsi_pll_input *pin, + struct dsi_pll_output *pout) +{ + void __iomem *base = pll->mmio; + void __iomem *cmn_base = pll->phy_cmn_mmio; + u8 data; + + DBG("DSI%d PLL", pll->id); + + data = pout->cmn_ldo_cntrl; + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_LDO_CNTRL, data); + + pll_db_commit_common(pll, pin, pout); + + pll_14nm_software_reset(pll); + + data = pin->dsiclk_sel; /* set dsiclk_sel = 1 */ + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG1, data); + + data = 0xff; /* data, clk, pll normal operation */ + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CTRL_0, data); + + /* configure the frequency dependent pll registers */ + data = pout->dec_start; + pll_write(base + REG_DSI_14nm_PHY_PLL_DEC_START, data); + + data = pout->div_frac_start & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START1, data); + data = (pout->div_frac_start >> 8) & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START2, data); + data = (pout->div_frac_start >> 16) & 0xf; + pll_write(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START3, data); + + data = pout->plllock_cmp & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP1, data); + + data = (pout->plllock_cmp >> 8) & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP2, data); + + data = (pout->plllock_cmp >> 16) & 0x3; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP3, data); + + data = pin->plllock_cnt << 1 | pin->plllock_rng << 3; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLLLOCK_CMP_EN, data); + + data = pout->pll_vco_count & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_COUNT1, data); + data = (pout->pll_vco_count >> 8) & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_VCO_COUNT2, data); + + data = pout->pll_kvco_count & 0xff; + pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_COUNT1, data); + data = (pout->pll_kvco_count >> 8) & 0x3; + pll_write(base + REG_DSI_14nm_PHY_PLL_KVCO_COUNT2, data); + + data = (pout->pll_postdiv - 1) << 4 | pin->pll_lpf_res1; + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_LPF2_POSTDIV, data); + + if (pin->ssc_en) + pll_db_commit_ssc(pll); + + wmb(); /* make sure register committed */ +} + +/* + * VCO clock Callbacks + */ +static int dsi_pll_14nm_vco_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + struct dsi_pll_input *pin = &pll_14nm->in; + struct dsi_pll_output *pout = &pll_14nm->out; + + DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_14nm->id, rate, + parent_rate); + + pll_14nm->vco_current_rate = rate; + pll_14nm->vco_ref_clk_rate = VCO_REF_CLK_RATE; + + dsi_pll_14nm_input_init(pll_14nm); + + /* + * This configures the post divider internal to the VCO. It's + * fixed to divide by 1 for now. + * + * tx_band = pll_postdiv. + * 0: divided by 1 + * 1: divided by 2 + * 2: divided by 4 + * 3: divided by 8 + */ + pout->pll_postdiv = DSI_PLL_DEFAULT_VCO_POSTDIV; + + pll_14nm_dec_frac_calc(pll_14nm); + + if (pin->ssc_en) + pll_14nm_ssc_calc(pll_14nm); + + pll_14nm_calc_vco_count(pll_14nm); + + /* commit the slave DSI PLL registers if we're master. Note that we + * don't lock the slave PLL. We just ensure that the PLL/PHY registers + * of the master and slave are identical + */ + if (pll_14nm->uc == MSM_DSI_PHY_MASTER) { + struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave; + + pll_db_commit_14nm(pll_14nm_slave, pin, pout); + } + + pll_db_commit_14nm(pll_14nm, pin, pout); + + return 0; +} + +static unsigned long dsi_pll_14nm_vco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + void __iomem *base = pll_14nm->mmio; + u64 vco_rate, multiplier = BIT(20); + u32 div_frac_start; + u32 dec_start; + u64 ref_clk = parent_rate; + + dec_start = pll_read(base + REG_DSI_14nm_PHY_PLL_DEC_START); + dec_start &= 0x0ff; + + DBG("dec_start = %x", dec_start); + + div_frac_start = (pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START3) + & 0xf) << 16; + div_frac_start |= (pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START2) + & 0xff) << 8; + div_frac_start |= pll_read(base + REG_DSI_14nm_PHY_PLL_DIV_FRAC_START1) + & 0xff; + + DBG("div_frac_start = %x", div_frac_start); + + vco_rate = ref_clk * dec_start; + + vco_rate += ((ref_clk * div_frac_start) / multiplier); + + /* + * Recalculating the rate from dec_start and frac_start doesn't end up + * the rate we originally set. Convert the freq to KHz, round it up and + * convert it back to MHz. + */ + vco_rate = DIV_ROUND_UP_ULL(vco_rate, 1000) * 1000; + + DBG("returning vco rate = %lu", (unsigned long)vco_rate); + + return (unsigned long)vco_rate; +} + +static const struct clk_ops clk_ops_dsi_pll_14nm_vco = { + .round_rate = msm_dsi_pll_helper_clk_round_rate, + .set_rate = dsi_pll_14nm_vco_set_rate, + .recalc_rate = dsi_pll_14nm_vco_recalc_rate, + .prepare = msm_dsi_pll_helper_clk_prepare, + .unprepare = msm_dsi_pll_helper_clk_unprepare, +}; + +/* + * N1 and N2 post-divider clock callbacks + */ +#define div_mask(width) ((1 << (width)) - 1) +static unsigned long dsi_pll_14nm_postdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw); + struct dsi_pll_14nm *pll_14nm = postdiv->pll; + void __iomem *base = pll_14nm->phy_cmn_mmio; + u8 shift = postdiv->shift; + u8 width = postdiv->width; + u32 val; + + DBG("DSI%d PLL parent rate=%lu", pll_14nm->id, parent_rate); + + val = pll_read(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0) >> shift; + val &= div_mask(width); + + return divider_recalc_rate(hw, parent_rate, val, NULL, + postdiv->flags); +} + +static long dsi_pll_14nm_postdiv_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw); + struct dsi_pll_14nm *pll_14nm = postdiv->pll; + + DBG("DSI%d PLL parent rate=%lu", pll_14nm->id, rate); + + return divider_round_rate(hw, rate, prate, NULL, + postdiv->width, + postdiv->flags); +} + +static int dsi_pll_14nm_postdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct dsi_pll_14nm_postdiv *postdiv = to_pll_14nm_postdiv(hw); + struct dsi_pll_14nm *pll_14nm = postdiv->pll; + void __iomem *base = pll_14nm->phy_cmn_mmio; + spinlock_t *lock = &pll_14nm->postdiv_lock; + u8 shift = postdiv->shift; + u8 width = postdiv->width; + unsigned int value; + unsigned long flags = 0; + u32 val; + + DBG("DSI%d PLL parent rate=%lu parent rate %lu", pll_14nm->id, rate, + parent_rate); + + value = divider_get_val(rate, parent_rate, NULL, postdiv->width, + postdiv->flags); + + spin_lock_irqsave(lock, flags); + + val = pll_read(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0); + val &= ~(div_mask(width) << shift); + + val |= value << shift; + pll_write(base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, val); + + /* If we're master in dual DSI mode, then the slave PLL's post-dividers + * follow the master's post dividers + */ + if (pll_14nm->uc == MSM_DSI_PHY_MASTER) { + struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave; + void __iomem *slave_base = pll_14nm_slave->phy_cmn_mmio; + + pll_write(slave_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, val); + } + + spin_unlock_irqrestore(lock, flags); + + return 0; +} + +static const struct clk_ops clk_ops_dsi_pll_14nm_postdiv = { + .recalc_rate = dsi_pll_14nm_postdiv_recalc_rate, + .round_rate = dsi_pll_14nm_postdiv_round_rate, + .set_rate = dsi_pll_14nm_postdiv_set_rate, +}; + +/* + * PLL Callbacks + */ + +static int dsi_pll_14nm_enable_seq(struct msm_dsi_pll *pll) +{ + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + void __iomem *base = pll_14nm->mmio; + void __iomem *cmn_base = pll_14nm->phy_cmn_mmio; + bool locked; + + DBG(""); + + pll_write(base + REG_DSI_14nm_PHY_PLL_VREF_CFG1, 0x10); + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 1); + + locked = pll_14nm_poll_for_ready(pll_14nm, POLL_MAX_READS, + POLL_TIMEOUT_US); + + if (unlikely(!locked)) + dev_err(&pll_14nm->pdev->dev, "DSI PLL lock failed\n"); + else + DBG("DSI PLL lock success"); + + return locked ? 0 : -EINVAL; +} + +static void dsi_pll_14nm_disable_seq(struct msm_dsi_pll *pll) +{ + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + void __iomem *cmn_base = pll_14nm->phy_cmn_mmio; + + DBG(""); + + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_PLL_CNTRL, 0); +} + +static void dsi_pll_14nm_save_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + struct pll_14nm_cached_state *cached_state = &pll_14nm->cached_state; + void __iomem *cmn_base = pll_14nm->phy_cmn_mmio; + u32 data; + + data = pll_read(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0); + + cached_state->n1postdiv = data & 0xf; + cached_state->n2postdiv = (data >> 4) & 0xf; + + DBG("DSI%d PLL save state %x %x", pll_14nm->id, + cached_state->n1postdiv, cached_state->n2postdiv); + + cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw); +} + +static int dsi_pll_14nm_restore_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + struct pll_14nm_cached_state *cached_state = &pll_14nm->cached_state; + void __iomem *cmn_base = pll_14nm->phy_cmn_mmio; + u32 data; + int ret; + + ret = dsi_pll_14nm_vco_set_rate(&pll->clk_hw, + cached_state->vco_rate, 0); + if (ret) { + dev_err(&pll_14nm->pdev->dev, + "restore vco rate failed. ret=%d\n", ret); + return ret; + } + + data = cached_state->n1postdiv | (cached_state->n2postdiv << 4); + + DBG("DSI%d PLL restore state %x %x", pll_14nm->id, + cached_state->n1postdiv, cached_state->n2postdiv); + + pll_write(cmn_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, data); + + /* also restore post-dividers for slave DSI PLL */ + if (pll_14nm->uc == MSM_DSI_PHY_MASTER) { + struct dsi_pll_14nm *pll_14nm_slave = pll_14nm->slave; + void __iomem *slave_base = pll_14nm_slave->phy_cmn_mmio; + + pll_write(slave_base + REG_DSI_14nm_PHY_CMN_CLK_CFG0, data); + } + + return 0; +} + +static int dsi_pll_14nm_set_usecase(struct msm_dsi_pll *pll, + enum msm_dsi_phy_usecase uc) +{ + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + void __iomem *base = pll_14nm->mmio; + u32 clkbuflr_en, bandgap = 0; + + switch (uc) { + case MSM_DSI_PHY_STANDALONE: + clkbuflr_en = 0x1; + break; + case MSM_DSI_PHY_MASTER: + clkbuflr_en = 0x3; + pll_14nm->slave = pll_14nm_list[(pll_14nm->id + 1) % DSI_MAX]; + break; + case MSM_DSI_PHY_SLAVE: + clkbuflr_en = 0x0; + bandgap = 0x3; + break; + default: + return -EINVAL; + } + + pll_write(base + REG_DSI_14nm_PHY_PLL_CLKBUFLR_EN, clkbuflr_en); + if (bandgap) + pll_write(base + REG_DSI_14nm_PHY_PLL_PLL_BANDGAP, bandgap); + + pll_14nm->uc = uc; + + return 0; +} + +static int dsi_pll_14nm_get_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, + struct clk **pixel_clk_provider) +{ + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + struct clk_hw_onecell_data *hw_data = pll_14nm->hw_data; + + if (byte_clk_provider) + *byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk; + if (pixel_clk_provider) + *pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk; + + return 0; +} + +static void dsi_pll_14nm_destroy(struct msm_dsi_pll *pll) +{ + struct dsi_pll_14nm *pll_14nm = to_pll_14nm(pll); + struct platform_device *pdev = pll_14nm->pdev; + int num_hws = pll_14nm->num_hws; + + of_clk_del_provider(pdev->dev.of_node); + + while (num_hws--) + clk_hw_unregister(pll_14nm->hws[num_hws]); +} + +static struct clk_hw *pll_14nm_postdiv_register(struct dsi_pll_14nm *pll_14nm, + const char *name, + const char *parent_name, + unsigned long flags, + u8 shift) +{ + struct dsi_pll_14nm_postdiv *pll_postdiv; + struct device *dev = &pll_14nm->pdev->dev; + struct clk_init_data postdiv_init = { + .parent_names = (const char *[]) { parent_name }, + .num_parents = 1, + .name = name, + .flags = flags, + .ops = &clk_ops_dsi_pll_14nm_postdiv, + }; + int ret; + + pll_postdiv = devm_kzalloc(dev, sizeof(*pll_postdiv), GFP_KERNEL); + if (!pll_postdiv) + return ERR_PTR(-ENOMEM); + + pll_postdiv->pll = pll_14nm; + pll_postdiv->shift = shift; + /* both N1 and N2 postdividers are 4 bits wide */ + pll_postdiv->width = 4; + /* range of each divider is from 1 to 15 */ + pll_postdiv->flags = CLK_DIVIDER_ONE_BASED; + pll_postdiv->hw.init = &postdiv_init; + + ret = clk_hw_register(dev, &pll_postdiv->hw); + if (ret) + return ERR_PTR(ret); + + return &pll_postdiv->hw; +} + +static int pll_14nm_register(struct dsi_pll_14nm *pll_14nm) +{ + char clk_name[32], parent[32], vco_name[32]; + struct clk_init_data vco_init = { + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .name = vco_name, + .flags = CLK_IGNORE_UNUSED, + .ops = &clk_ops_dsi_pll_14nm_vco, + }; + struct device *dev = &pll_14nm->pdev->dev; + struct clk_hw **hws = pll_14nm->hws; + struct clk_hw_onecell_data *hw_data; + struct clk_hw *hw; + int num = 0; + int ret; + + DBG("DSI%d", pll_14nm->id); + + hw_data = devm_kzalloc(dev, sizeof(*hw_data) + + NUM_PROVIDED_CLKS * sizeof(struct clk_hw *), + GFP_KERNEL); + if (!hw_data) + return -ENOMEM; + + snprintf(vco_name, 32, "dsi%dvco_clk", pll_14nm->id); + pll_14nm->base.clk_hw.init = &vco_init; + + ret = clk_hw_register(dev, &pll_14nm->base.clk_hw); + if (ret) + return ret; + + hws[num++] = &pll_14nm->base.clk_hw; + + snprintf(clk_name, 32, "dsi%dn1_postdiv_clk", pll_14nm->id); + snprintf(parent, 32, "dsi%dvco_clk", pll_14nm->id); + + /* N1 postdiv, bits 0-3 in REG_DSI_14nm_PHY_CMN_CLK_CFG0 */ + hw = pll_14nm_postdiv_register(pll_14nm, clk_name, parent, + CLK_SET_RATE_PARENT, 0); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + hws[num++] = hw; + + snprintf(clk_name, 32, "dsi%dpllbyte", pll_14nm->id); + snprintf(parent, 32, "dsi%dn1_postdiv_clk", pll_14nm->id); + + /* DSI Byte clock = VCO_CLK / N1 / 8 */ + hw = clk_hw_register_fixed_factor(dev, clk_name, parent, + CLK_SET_RATE_PARENT, 1, 8); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + hws[num++] = hw; + hw_data->hws[DSI_BYTE_PLL_CLK] = hw; + + snprintf(clk_name, 32, "dsi%dn1_postdivby2_clk", pll_14nm->id); + snprintf(parent, 32, "dsi%dn1_postdiv_clk", pll_14nm->id); + + /* + * Skip the mux for now, force DSICLK_SEL to 1, Add a /2 divider + * on the way. Don't let it set parent. + */ + hw = clk_hw_register_fixed_factor(dev, clk_name, parent, 0, 1, 2); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + hws[num++] = hw; + + snprintf(clk_name, 32, "dsi%dpll", pll_14nm->id); + snprintf(parent, 32, "dsi%dn1_postdivby2_clk", pll_14nm->id); + + /* DSI pixel clock = VCO_CLK / N1 / 2 / N2 + * This is the output of N2 post-divider, bits 4-7 in + * REG_DSI_14nm_PHY_CMN_CLK_CFG0. Don't let it set parent. + */ + hw = pll_14nm_postdiv_register(pll_14nm, clk_name, parent, 0, 4); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + hws[num++] = hw; + hw_data->hws[DSI_PIXEL_PLL_CLK] = hw; + + pll_14nm->num_hws = num; + + hw_data->num = NUM_PROVIDED_CLKS; + pll_14nm->hw_data = hw_data; + + ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + pll_14nm->hw_data); + if (ret) { + dev_err(dev, "failed to register clk provider: %d\n", ret); + return ret; + } + + return 0; +} + +struct msm_dsi_pll *msm_dsi_pll_14nm_init(struct platform_device *pdev, int id) +{ + struct dsi_pll_14nm *pll_14nm; + struct msm_dsi_pll *pll; + int ret; + + if (!pdev) + return ERR_PTR(-ENODEV); + + pll_14nm = devm_kzalloc(&pdev->dev, sizeof(*pll_14nm), GFP_KERNEL); + if (!pll_14nm) + return ERR_PTR(-ENOMEM); + + DBG("PLL%d", id); + + pll_14nm->pdev = pdev; + pll_14nm->id = id; + pll_14nm_list[id] = pll_14nm; + + pll_14nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY"); + if (IS_ERR_OR_NULL(pll_14nm->phy_cmn_mmio)) { + dev_err(&pdev->dev, "failed to map CMN PHY base\n"); + return ERR_PTR(-ENOMEM); + } + + pll_14nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL"); + if (IS_ERR_OR_NULL(pll_14nm->mmio)) { + dev_err(&pdev->dev, "failed to map PLL base\n"); + return ERR_PTR(-ENOMEM); + } + + spin_lock_init(&pll_14nm->postdiv_lock); + + pll = &pll_14nm->base; + pll->min_rate = VCO_MIN_RATE; + pll->max_rate = VCO_MAX_RATE; + pll->get_provider = dsi_pll_14nm_get_provider; + pll->destroy = dsi_pll_14nm_destroy; + pll->disable_seq = dsi_pll_14nm_disable_seq; + pll->save_state = dsi_pll_14nm_save_state; + pll->restore_state = dsi_pll_14nm_restore_state; + pll->set_usecase = dsi_pll_14nm_set_usecase; + + pll_14nm->vco_delay = 1; + + pll->en_seq_cnt = 1; + pll->enable_seqs[0] = dsi_pll_14nm_enable_seq; + + ret = pll_14nm_register(pll_14nm); + if (ret) { + dev_err(&pdev->dev, "failed to register PLL: %d\n", ret); + return ERR_PTR(ret); + } + + return pll; +} diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c index 2bc73f82f3f5..931a5c97cccf 100644 --- a/drivers/gpu/drm/msm/edp/edp_bridge.c +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c @@ -106,7 +106,7 @@ struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp) bridge = &edp_bridge->base; bridge->funcs = &edp_bridge_funcs; - ret = drm_bridge_attach(edp->dev, bridge); + ret = drm_bridge_attach(edp->encoder, bridge, NULL); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index bacbd5d8df0e..4e6d1bf27474 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -227,7 +227,7 @@ struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi) bridge = &hdmi_bridge->base; bridge->funcs = &msm_hdmi_bridge_funcs; - ret = drm_bridge_attach(hdmi->dev, bridge); + ret = drm_bridge_attach(hdmi->encoder, bridge, NULL); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index b782efd4b95f..94ea963519b2 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -260,8 +260,7 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, struct drm_encoder *encoder; struct drm_connector *connector; struct device_node *panel_node; - struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM]; - int i, dsi_id; + int dsi_id; int ret; switch (intf_type) { @@ -322,22 +321,19 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, if (!priv->dsi[dsi_id]) break; - for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { - dsi_encs[i] = mdp4_dsi_encoder_init(dev); - if (IS_ERR(dsi_encs[i])) { - ret = PTR_ERR(dsi_encs[i]); - dev_err(dev->dev, - "failed to construct DSI encoder: %d\n", - ret); - return ret; - } - - /* TODO: Add DMA_S later? */ - dsi_encs[i]->possible_crtcs = 1 << DMA_P; - priv->encoders[priv->num_encoders++] = dsi_encs[i]; + encoder = mdp4_dsi_encoder_init(dev); + if (IS_ERR(encoder)) { + ret = PTR_ERR(encoder); + dev_err(dev->dev, + "failed to construct DSI encoder: %d\n", ret); + return ret; } - ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs); + /* TODO: Add DMA_S later? */ + encoder->possible_crtcs = 1 << DMA_P; + priv->encoders[priv->num_encoders++] = encoder; + + ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, encoder); if (ret) { dev_err(dev->dev, "failed to initialize DSI: %d\n", ret); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 911e4690d36a..53619d07677e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -43,7 +43,7 @@ enum mdp4_frame_format mdp4_get_frame_format(struct drm_framebuffer *fb) if (fb->modifier == DRM_FORMAT_MOD_SAMSUNG_64_32_TILE) is_tile = true; - if (fb->pixel_format == DRM_FORMAT_NV12 && is_tile) + if (fb->format->format == DRM_FORMAT_NV12 && is_tile) return FRAME_TILE_YCBCR_420; return FRAME_LINEAR; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h index 27d5371acee0..e6dfc518d4db 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h @@ -8,19 +8,11 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 36965 bytes, from 2016-11-26 23:01:08) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 27887 bytes, from 2015-10-22 16:34:52) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) - -Copyright (C) 2013-2016 by the following authors: +- /local/mnt/workspace/source_trees/envytools/rnndb/../rnndb/mdp/mdp5.xml ( 37411 bytes, from 2017-01-11 05:19:19) +- /local/mnt/workspace/source_trees/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-05-09 06:32:54) +- /local/mnt/workspace/source_trees/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2016-01-07 08:45:55) + +Copyright (C) 2013-2017 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) - Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) @@ -65,16 +57,19 @@ enum mdp5_intfnum { }; enum mdp5_pipe { - SSPP_VIG0 = 0, - SSPP_VIG1 = 1, - SSPP_VIG2 = 2, - SSPP_RGB0 = 3, - SSPP_RGB1 = 4, - SSPP_RGB2 = 5, - SSPP_DMA0 = 6, - SSPP_DMA1 = 7, - SSPP_VIG3 = 8, - SSPP_RGB3 = 9, + SSPP_NONE = 0, + SSPP_VIG0 = 1, + SSPP_VIG1 = 2, + SSPP_VIG2 = 3, + SSPP_RGB0 = 4, + SSPP_RGB1 = 5, + SSPP_RGB2 = 6, + SSPP_DMA0 = 7, + SSPP_DMA1 = 8, + SSPP_VIG3 = 9, + SSPP_RGB3 = 10, + SSPP_CURSOR0 = 11, + SSPP_CURSOR1 = 12, }; enum mdp5_ctl_mode { @@ -532,6 +527,7 @@ static inline uint32_t MDP5_CTL_LAYER_EXT_REG_CURSOR1(enum mdp_mixer_stage_id va static inline uint32_t __offset_PIPE(enum mdp5_pipe idx) { switch (idx) { + case SSPP_NONE: return (INVALID_IDX(idx)); case SSPP_VIG0: return (mdp5_cfg->pipe_vig.base[0]); case SSPP_VIG1: return (mdp5_cfg->pipe_vig.base[1]); case SSPP_VIG2: return (mdp5_cfg->pipe_vig.base[2]); @@ -542,6 +538,8 @@ static inline uint32_t __offset_PIPE(enum mdp5_pipe idx) case SSPP_DMA1: return (mdp5_cfg->pipe_dma.base[1]); case SSPP_VIG3: return (mdp5_cfg->pipe_vig.base[3]); case SSPP_RGB3: return (mdp5_cfg->pipe_rgb.base[3]); + case SSPP_CURSOR0: return (mdp5_cfg->pipe_cursor.base[0]); + case SSPP_CURSOR1: return (mdp5_cfg->pipe_cursor.base[1]); default: return INVALID_IDX(idx); } } @@ -1073,6 +1071,10 @@ static inline uint32_t REG_MDP5_LM_BLEND_COLOR_OUT(uint32_t i0) { return 0x00000 #define MDP5_LM_BLEND_COLOR_OUT_STAGE1_FG_ALPHA 0x00000004 #define MDP5_LM_BLEND_COLOR_OUT_STAGE2_FG_ALPHA 0x00000008 #define MDP5_LM_BLEND_COLOR_OUT_STAGE3_FG_ALPHA 0x00000010 +#define MDP5_LM_BLEND_COLOR_OUT_STAGE4_FG_ALPHA 0x00000020 +#define MDP5_LM_BLEND_COLOR_OUT_STAGE5_FG_ALPHA 0x00000040 +#define MDP5_LM_BLEND_COLOR_OUT_STAGE6_FG_ALPHA 0x00000080 +#define MDP5_LM_BLEND_COLOR_OUT_SPLIT_LEFT_RIGHT 0x80000000 static inline uint32_t REG_MDP5_LM_OUT_SIZE(uint32_t i0) { return 0x00000004 + __offset_LM(i0); } #define MDP5_LM_OUT_SIZE_HEIGHT__MASK 0xffff0000 diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index 618b2ffed9b4..34ab553f6897 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -421,6 +421,16 @@ const struct mdp5_cfg_hw msm8x96_config = { MDP_PIPE_CAP_SW_PIX_EXT | 0, }, + .pipe_cursor = { + .count = 2, + .base = { 0x34000, 0x36000 }, + .caps = MDP_PIPE_CAP_HFLIP | + MDP_PIPE_CAP_VFLIP | + MDP_PIPE_CAP_SW_PIX_EXT | + MDP_PIPE_CAP_CURSOR | + 0, + }, + .lm = { .count = 6, .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h index 050e1618c836..b1c7daaede86 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h @@ -32,7 +32,7 @@ extern const struct mdp5_cfg_hw *mdp5_cfg; typedef DECLARE_BITMAP(mdp5_smp_state_t, MAX_SMP_BLOCKS); #define MDP5_SUB_BLOCK_DEFINITION \ - int count; \ + unsigned int count; \ uint32_t base[MAX_BASES] struct mdp5_sub_block { @@ -85,6 +85,7 @@ struct mdp5_cfg_hw { struct mdp5_pipe_block pipe_vig; struct mdp5_pipe_block pipe_rgb; struct mdp5_pipe_block pipe_dma; + struct mdp5_pipe_block pipe_cursor; struct mdp5_lm_block lm; struct mdp5_sub_block dspp; struct mdp5_sub_block ad; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c index c627ab6d0061..df1c8adec3f3 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c @@ -16,16 +16,6 @@ #include "drm_crtc.h" #include "drm_crtc_helper.h" -struct mdp5_cmd_encoder { - struct drm_encoder base; - struct mdp5_interface intf; - bool enabled; - uint32_t bsc; - - struct mdp5_ctl *ctl; -}; -#define to_mdp5_cmd_encoder(x) container_of(x, struct mdp5_cmd_encoder, base) - static struct mdp5_kms *get_kms(struct drm_encoder *encoder) { struct msm_drm_private *priv = encoder->dev->dev_private; @@ -36,47 +26,8 @@ static struct mdp5_kms *get_kms(struct drm_encoder *encoder) #include <mach/board.h> #include <linux/msm-bus.h> #include <linux/msm-bus-board.h> -#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ - { \ - .src = MSM_BUS_MASTER_MDP_PORT0, \ - .dst = MSM_BUS_SLAVE_EBI_CH0, \ - .ab = (ab_val), \ - .ib = (ib_val), \ - } - -static struct msm_bus_vectors mdp_bus_vectors[] = { - MDP_BUS_VECTOR_ENTRY(0, 0), - MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000), -}; -static struct msm_bus_paths mdp_bus_usecases[] = { { - .num_paths = 1, - .vectors = &mdp_bus_vectors[0], -}, { - .num_paths = 1, - .vectors = &mdp_bus_vectors[1], -} }; -static struct msm_bus_scale_pdata mdp_bus_scale_table = { - .usecase = mdp_bus_usecases, - .num_usecases = ARRAY_SIZE(mdp_bus_usecases), - .name = "mdss_mdp", -}; - -static void bs_init(struct mdp5_cmd_encoder *mdp5_cmd_enc) -{ - mdp5_cmd_enc->bsc = msm_bus_scale_register_client( - &mdp_bus_scale_table); - DBG("bus scale client: %08x", mdp5_cmd_enc->bsc); -} - -static void bs_fini(struct mdp5_cmd_encoder *mdp5_cmd_enc) -{ - if (mdp5_cmd_enc->bsc) { - msm_bus_scale_unregister_client(mdp5_cmd_enc->bsc); - mdp5_cmd_enc->bsc = 0; - } -} -static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx) +static void bs_set(struct mdp5_encoder *mdp5_cmd_enc, int idx) { if (mdp5_cmd_enc->bsc) { DBG("set bus scaling: %d", idx); @@ -89,14 +40,12 @@ static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx) } } #else -static void bs_init(struct mdp5_cmd_encoder *mdp5_cmd_enc) {} -static void bs_fini(struct mdp5_cmd_encoder *mdp5_cmd_enc) {} -static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx) {} +static void bs_set(struct mdp5_encoder *mdp5_cmd_enc, int idx) {} #endif #define VSYNC_CLK_RATE 19200000 static int pingpong_tearcheck_setup(struct drm_encoder *encoder, - struct drm_display_mode *mode) + struct drm_display_mode *mode) { struct mdp5_kms *mdp5_kms = get_kms(encoder); struct device *dev = encoder->dev->dev; @@ -176,23 +125,11 @@ static void pingpong_tearcheck_disable(struct drm_encoder *encoder) clk_disable_unprepare(mdp5_kms->vsync_clk); } -static void mdp5_cmd_encoder_destroy(struct drm_encoder *encoder) -{ - struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); - bs_fini(mdp5_cmd_enc); - drm_encoder_cleanup(encoder); - kfree(mdp5_cmd_enc); -} - -static const struct drm_encoder_funcs mdp5_cmd_encoder_funcs = { - .destroy = mdp5_cmd_encoder_destroy, -}; - -static void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); + struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); mode = adjusted_mode; @@ -209,9 +146,9 @@ static void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, mdp5_cmd_enc->ctl); } -static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) +void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) { - struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); + struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; struct mdp5_interface *intf = &mdp5_cmd_enc->intf; @@ -228,9 +165,9 @@ static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) mdp5_cmd_enc->enabled = false; } -static void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) +void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) { - struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); + struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl; struct mdp5_interface *intf = &mdp5_cmd_enc->intf; @@ -248,16 +185,10 @@ static void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) mdp5_cmd_enc->enabled = true; } -static const struct drm_encoder_helper_funcs mdp5_cmd_encoder_helper_funcs = { - .mode_set = mdp5_cmd_encoder_mode_set, - .disable = mdp5_cmd_encoder_disable, - .enable = mdp5_cmd_encoder_enable, -}; - int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, - struct drm_encoder *slave_encoder) + struct drm_encoder *slave_encoder) { - struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder); + struct mdp5_encoder *mdp5_cmd_enc = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms; int intf_num; u32 data = 0; @@ -292,43 +223,3 @@ int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, return 0; } - -/* initialize command mode encoder */ -struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, - struct mdp5_interface *intf, struct mdp5_ctl *ctl) -{ - struct drm_encoder *encoder = NULL; - struct mdp5_cmd_encoder *mdp5_cmd_enc; - int ret; - - if (WARN_ON((intf->type != INTF_DSI) && - (intf->mode != MDP5_INTF_DSI_MODE_COMMAND))) { - ret = -EINVAL; - goto fail; - } - - mdp5_cmd_enc = kzalloc(sizeof(*mdp5_cmd_enc), GFP_KERNEL); - if (!mdp5_cmd_enc) { - ret = -ENOMEM; - goto fail; - } - - memcpy(&mdp5_cmd_enc->intf, intf, sizeof(mdp5_cmd_enc->intf)); - encoder = &mdp5_cmd_enc->base; - mdp5_cmd_enc->ctl = ctl; - - drm_encoder_init(dev, encoder, &mdp5_cmd_encoder_funcs, - DRM_MODE_ENCODER_DSI, NULL); - - drm_encoder_helper_add(encoder, &mdp5_cmd_encoder_helper_funcs); - - bs_init(mdp5_cmd_enc); - - return encoder; - -fail: - if (encoder) - mdp5_cmd_encoder_destroy(encoder); - - return ERR_PTR(ret); -} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 1ce8a01a5a28..d0c8b38b96ce 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -177,6 +177,21 @@ static void mdp5_crtc_destroy(struct drm_crtc *crtc) kfree(mdp5_crtc); } +static inline u32 mdp5_lm_use_fg_alpha_mask(enum mdp_mixer_stage_id stage) +{ + switch (stage) { + case STAGE0: return MDP5_LM_BLEND_COLOR_OUT_STAGE0_FG_ALPHA; + case STAGE1: return MDP5_LM_BLEND_COLOR_OUT_STAGE1_FG_ALPHA; + case STAGE2: return MDP5_LM_BLEND_COLOR_OUT_STAGE2_FG_ALPHA; + case STAGE3: return MDP5_LM_BLEND_COLOR_OUT_STAGE3_FG_ALPHA; + case STAGE4: return MDP5_LM_BLEND_COLOR_OUT_STAGE4_FG_ALPHA; + case STAGE5: return MDP5_LM_BLEND_COLOR_OUT_STAGE5_FG_ALPHA; + case STAGE6: return MDP5_LM_BLEND_COLOR_OUT_STAGE6_FG_ALPHA; + default: + return 0; + } +} + /* * blend_setup() - blend all the planes of a CRTC * @@ -195,8 +210,10 @@ static void blend_setup(struct drm_crtc *crtc) uint32_t lm = mdp5_crtc->lm; uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; unsigned long flags; - uint8_t stage[STAGE_MAX + 1]; + enum mdp5_pipe stage[STAGE_MAX + 1] = { SSPP_NONE }; int i, plane_cnt = 0; + bool bg_alpha_enabled = false; + u32 mixer_op_mode = 0; #define blender(stage) ((stage) - STAGE0) hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); @@ -218,6 +235,11 @@ static void blend_setup(struct drm_crtc *crtc) if (!pstates[STAGE_BASE]) { ctl_blend_flags |= MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT; DBG("Border Color is enabled"); + } else if (plane_cnt) { + format = to_mdp_format(msm_framebuffer_format(pstates[STAGE_BASE]->base.fb)); + + if (format->alpha_enable) + bg_alpha_enabled = true; } /* The reset for blending */ @@ -232,6 +254,12 @@ static void blend_setup(struct drm_crtc *crtc) MDP5_LM_BLEND_OP_MODE_BG_ALPHA(BG_CONST); fg_alpha = pstates[i]->alpha; bg_alpha = 0xFF - pstates[i]->alpha; + + if (!format->alpha_enable && bg_alpha_enabled) + mixer_op_mode = 0; + else + mixer_op_mode |= mdp5_lm_use_fg_alpha_mask(i); + DBG("Stage %d fg_alpha %x bg_alpha %x", i, fg_alpha, bg_alpha); if (format->alpha_enable && pstates[i]->premultiplied) { @@ -268,6 +296,8 @@ static void blend_setup(struct drm_crtc *crtc) blender(i)), bg_alpha); } + mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), mixer_op_mode); + mdp5_ctl_blend(mdp5_crtc->ctl, stage, plane_cnt, ctl_blend_flags); out: @@ -370,6 +400,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, struct plane_state pstates[STAGE_MAX + 1]; const struct mdp5_cfg_hw *hw_cfg; const struct drm_plane_state *pstate; + bool cursor_plane = false; int cnt = 0, base = 0, i; DBG("%s: check", crtc->name); @@ -379,6 +410,9 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, pstates[cnt].state = to_mdp5_plane_state(pstate); cnt++; + + if (plane->type == DRM_PLANE_TYPE_CURSOR) + cursor_plane = true; } /* assign a stage based on sorted zpos property */ @@ -390,6 +424,10 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, if ((cnt > 0) && !is_fullscreen(state, &pstates[0].state->base)) base++; + /* trigger a warning if cursor isn't the highest zorder */ + WARN_ON(cursor_plane && + (pstates[cnt - 1].plane->type != DRM_PLANE_TYPE_CURSOR)); + /* verify that there are not too many planes attached to crtc * and that we don't have conflicting mixer stages: */ @@ -401,7 +439,10 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, } for (i = 0; i < cnt; i++) { - pstates[i].state->stage = STAGE_BASE + i + base; + if (cursor_plane && (i == (cnt - 1))) + pstates[i].state->stage = hw_cfg->lm.nb_stages; + else + pstates[i].state->stage = STAGE_BASE + i + base; DBG("%s: assign pipe %s on stage=%d", crtc->name, pstates[i].plane->name, pstates[i].state->stage); @@ -612,6 +653,16 @@ static const struct drm_crtc_funcs mdp5_crtc_funcs = { .cursor_move = mdp5_crtc_cursor_move, }; +static const struct drm_crtc_funcs mdp5_crtc_no_lm_cursor_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = mdp5_crtc_destroy, + .page_flip = drm_atomic_helper_page_flip, + .set_property = drm_atomic_helper_crtc_set_property, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = { .mode_set_nofb = mdp5_crtc_mode_set_nofb, .disable = mdp5_crtc_disable, @@ -727,6 +778,13 @@ void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, mdp5_ctl_set_pipeline(ctl, intf, lm); } +struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc) +{ + struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + + return mdp5_crtc->ctl; +} + int mdp5_crtc_get_lm(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); @@ -745,7 +803,8 @@ void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc) /* initialize crtc */ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, - struct drm_plane *plane, int id) + struct drm_plane *plane, + struct drm_plane *cursor_plane, int id) { struct drm_crtc *crtc = NULL; struct mdp5_crtc *mdp5_crtc; @@ -766,8 +825,12 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq; mdp5_crtc->err.irq = mdp5_crtc_err_irq; - drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs, - NULL); + if (cursor_plane) + drm_crtc_init_with_planes(dev, crtc, plane, cursor_plane, + &mdp5_crtc_no_lm_cursor_funcs, NULL); + else + drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &mdp5_crtc_funcs, NULL); drm_flip_work_init(&mdp5_crtc->unref_cursor_work, "unref cursor", unref_cursor_worker); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index d021edc3b307..8b93f7e13200 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -326,6 +326,8 @@ static u32 mdp_ctl_blend_mask(enum mdp5_pipe pipe, case SSPP_DMA1: return MDP5_CTL_LAYER_REG_DMA1(stage); case SSPP_VIG3: return MDP5_CTL_LAYER_REG_VIG3(stage); case SSPP_RGB3: return MDP5_CTL_LAYER_REG_RGB3(stage); + case SSPP_CURSOR0: + case SSPP_CURSOR1: default: return 0; } } @@ -333,7 +335,7 @@ static u32 mdp_ctl_blend_mask(enum mdp5_pipe pipe, static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe, enum mdp_mixer_stage_id stage) { - if (stage < STAGE6) + if (stage < STAGE6 && (pipe != SSPP_CURSOR0 && pipe != SSPP_CURSOR1)) return 0; switch (pipe) { @@ -347,12 +349,14 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe, case SSPP_DMA1: return MDP5_CTL_LAYER_EXT_REG_DMA1_BIT3; case SSPP_VIG3: return MDP5_CTL_LAYER_EXT_REG_VIG3_BIT3; case SSPP_RGB3: return MDP5_CTL_LAYER_EXT_REG_RGB3_BIT3; + case SSPP_CURSOR0: return MDP5_CTL_LAYER_EXT_REG_CURSOR0(stage); + case SSPP_CURSOR1: return MDP5_CTL_LAYER_EXT_REG_CURSOR1(stage); default: return 0; } } -int mdp5_ctl_blend(struct mdp5_ctl *ctl, u8 *stage, u32 stage_cnt, - u32 ctl_blend_op_flags) +int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt, + u32 ctl_blend_op_flags) { unsigned long flags; u32 blend_cfg = 0, blend_ext_cfg = 0; @@ -365,7 +369,7 @@ int mdp5_ctl_blend(struct mdp5_ctl *ctl, u8 *stage, u32 stage_cnt, start_stage = STAGE_BASE; } - for (i = start_stage; i < start_stage + stage_cnt; i++) { + for (i = start_stage; stage_cnt && i <= STAGE_MAX; i++) { blend_cfg |= mdp_ctl_blend_mask(stage[i], i); blend_ext_cfg |= mdp_ctl_blend_ext_mask(stage[i], i); } @@ -422,6 +426,8 @@ u32 mdp_ctl_flush_mask_pipe(enum mdp5_pipe pipe) case SSPP_DMA1: return MDP5_CTL_FLUSH_DMA1; case SSPP_VIG3: return MDP5_CTL_FLUSH_VIG3; case SSPP_RGB3: return MDP5_CTL_FLUSH_RGB3; + case SSPP_CURSOR0: return MDP5_CTL_FLUSH_CURSOR_0; + case SSPP_CURSOR1: return MDP5_CTL_FLUSH_CURSOR_1; default: return 0; } } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h index 96148c6f863c..fda00d33e4db 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h @@ -56,8 +56,8 @@ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable); * (call mdp5_ctl_commit() with mdp_ctl_flush_mask_ctl() mask) */ #define MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT BIT(0) -int mdp5_ctl_blend(struct mdp5_ctl *ctl, u8 *stage, u32 stage_cnt, - u32 ctl_blend_op_flags); +int mdp5_ctl_blend(struct mdp5_ctl *ctl, enum mdp5_pipe *stage, u32 stage_cnt, + u32 ctl_blend_op_flags); /** * mdp_ctl_flush_mask...() - Register FLUSH masks diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index fe0c22230883..80fa482ae8ed 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -21,17 +21,6 @@ #include "drm_crtc.h" #include "drm_crtc_helper.h" -struct mdp5_encoder { - struct drm_encoder base; - struct mdp5_interface intf; - spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */ - bool enabled; - uint32_t bsc; - - struct mdp5_ctl *ctl; -}; -#define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base) - static struct mdp5_kms *get_kms(struct drm_encoder *encoder) { struct msm_drm_private *priv = encoder->dev->dev_private; @@ -112,9 +101,9 @@ static const struct drm_encoder_funcs mdp5_encoder_funcs = { .destroy = mdp5_encoder_destroy, }; -static void mdp5_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); @@ -221,7 +210,7 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder, mdp5_encoder->ctl); } -static void mdp5_encoder_disable(struct drm_encoder *encoder) +static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); @@ -256,7 +245,7 @@ static void mdp5_encoder_disable(struct drm_encoder *encoder) mdp5_encoder->enabled = false; } -static void mdp5_encoder_enable(struct drm_encoder *encoder) +static void mdp5_vid_encoder_enable(struct drm_encoder *encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_kms *mdp5_kms = get_kms(encoder); @@ -279,6 +268,41 @@ static void mdp5_encoder_enable(struct drm_encoder *encoder) mdp5_encoder->enabled = true; } +static void mdp5_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); + struct mdp5_interface *intf = &mdp5_encoder->intf; + + if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) + mdp5_cmd_encoder_mode_set(encoder, mode, adjusted_mode); + else + mdp5_vid_encoder_mode_set(encoder, mode, adjusted_mode); +} + +static void mdp5_encoder_disable(struct drm_encoder *encoder) +{ + struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); + struct mdp5_interface *intf = &mdp5_encoder->intf; + + if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) + mdp5_cmd_encoder_disable(encoder); + else + mdp5_vid_encoder_disable(encoder); +} + +static void mdp5_encoder_enable(struct drm_encoder *encoder) +{ + struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); + struct mdp5_interface *intf = &mdp5_encoder->intf; + + if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) + mdp5_cmd_encoder_disable(encoder); + else + mdp5_vid_encoder_enable(encoder); +} + static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = { .mode_set = mdp5_encoder_mode_set, .disable = mdp5_encoder_disable, @@ -303,8 +327,8 @@ u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder) return mdp5_read(mdp5_kms, REG_MDP5_INTF_FRAME_COUNT(intf)); } -int mdp5_encoder_set_split_display(struct drm_encoder *encoder, - struct drm_encoder *slave_encoder) +int mdp5_vid_encoder_set_split_display(struct drm_encoder *encoder, + struct drm_encoder *slave_encoder) { struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); struct mdp5_encoder *mdp5_slave_enc = to_mdp5_encoder(slave_encoder); @@ -342,6 +366,23 @@ int mdp5_encoder_set_split_display(struct drm_encoder *encoder, return 0; } +void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode) +{ + struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); + struct mdp5_interface *intf = &mdp5_encoder->intf; + + /* TODO: Expand this to set writeback modes too */ + if (cmd_mode) { + WARN_ON(intf->type != INTF_DSI); + intf->mode = MDP5_INTF_DSI_MODE_COMMAND; + } else { + if (intf->type == INTF_DSI) + intf->mode = MDP5_INTF_DSI_MODE_VIDEO; + else + intf->mode = MDP5_INTF_MODE_NONE; + } +} + /* initialize encoder */ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, struct mdp5_interface *intf, struct mdp5_ctl *ctl) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index c396d459a9d0..3eb0749223d9 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -148,7 +148,15 @@ static int mdp5_set_split_display(struct msm_kms *kms, return mdp5_cmd_encoder_set_split_display(encoder, slave_encoder); else - return mdp5_encoder_set_split_display(encoder, slave_encoder); + return mdp5_vid_encoder_set_split_display(encoder, + slave_encoder); +} + +static void mdp5_set_encoder_mode(struct msm_kms *kms, + struct drm_encoder *encoder, + bool cmd_mode) +{ + mdp5_encoder_set_intf_mode(encoder, cmd_mode); } static void mdp5_kms_destroy(struct msm_kms *kms) @@ -230,6 +238,7 @@ static const struct mdp_kms_funcs kms_funcs = { .get_format = mdp_get_format, .round_pixclk = mdp5_round_pixclk, .set_split_display = mdp5_set_split_display, + .set_encoder_mode = mdp5_set_encoder_mode, .destroy = mdp5_kms_destroy, #ifdef CONFIG_DEBUG_FS .debugfs_init = mdp5_kms_debugfs_init, @@ -267,7 +276,7 @@ int mdp5_enable(struct mdp5_kms *mdp5_kms) static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms, enum mdp5_intf_type intf_type, int intf_num, - enum mdp5_intf_mode intf_mode, struct mdp5_ctl *ctl) + struct mdp5_ctl *ctl) { struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; @@ -275,21 +284,15 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms, struct mdp5_interface intf = { .num = intf_num, .type = intf_type, - .mode = intf_mode, + .mode = MDP5_INTF_MODE_NONE, }; - if ((intf_type == INTF_DSI) && - (intf_mode == MDP5_INTF_DSI_MODE_COMMAND)) - encoder = mdp5_cmd_encoder_init(dev, &intf, ctl); - else - encoder = mdp5_encoder_init(dev, &intf, ctl); - + encoder = mdp5_encoder_init(dev, &intf, ctl); if (IS_ERR(encoder)) { dev_err(dev->dev, "failed to construct encoder\n"); return encoder; } - encoder->possible_crtcs = (1 << priv->num_crtcs) - 1; priv->encoders[priv->num_encoders++] = encoder; return encoder; @@ -338,8 +341,7 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) break; } - encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num, - MDP5_INTF_MODE_NONE, ctl); + encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num, ctl); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; @@ -357,8 +359,7 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) break; } - encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num, - MDP5_INTF_MODE_NONE, ctl); + encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num, ctl); if (IS_ERR(encoder)) { ret = PTR_ERR(encoder); break; @@ -369,9 +370,6 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) case INTF_DSI: { int dsi_id = get_dsi_id_from_intf(hw_cfg, intf_num); - struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM]; - enum mdp5_intf_mode mode; - int i; if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) { dev_err(dev->dev, "failed to find dsi from intf %d\n", @@ -389,19 +387,13 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) break; } - for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { - mode = (i == MSM_DSI_CMD_ENCODER_ID) ? - MDP5_INTF_DSI_MODE_COMMAND : - MDP5_INTF_DSI_MODE_VIDEO; - dsi_encs[i] = construct_encoder(mdp5_kms, INTF_DSI, - intf_num, mode, ctl); - if (IS_ERR(dsi_encs[i])) { - ret = PTR_ERR(dsi_encs[i]); - break; - } + encoder = construct_encoder(mdp5_kms, INTF_DSI, intf_num, ctl); + if (IS_ERR(encoder)) { + ret = PTR_ERR(encoder); + break; } - ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs); + ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, encoder); break; } default: @@ -418,20 +410,48 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; const struct mdp5_cfg_hw *hw_cfg; - int i, ret; + unsigned int num_crtcs; + int i, ret, pi = 0, ci = 0; + struct drm_plane *primary[MAX_BASES] = { NULL }; + struct drm_plane *cursor[MAX_BASES] = { NULL }; hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); - /* Construct planes equaling the number of hw pipes, and CRTCs - * for the N layer-mixers (LM). The first N planes become primary + /* + * Construct encoders and modeset initialize connector devices + * for each external display interface. + */ + for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) { + ret = modeset_init_intf(mdp5_kms, i); + if (ret) + goto fail; + } + + /* + * We should ideally have less number of encoders (set up by parsing + * the MDP5 interfaces) than the number of layer mixers present in HW, + * but let's be safe here anyway + */ + num_crtcs = min(priv->num_encoders, mdp5_cfg->lm.count); + + /* + * Construct planes equaling the number of hw pipes, and CRTCs for the + * N encoders set up by the driver. The first N planes become primary * planes for the CRTCs, with the remainder as overlay planes: */ for (i = 0; i < mdp5_kms->num_hwpipes; i++) { - bool primary = i < mdp5_cfg->lm.count; + struct mdp5_hw_pipe *hwpipe = mdp5_kms->hwpipes[i]; struct drm_plane *plane; - struct drm_crtc *crtc; + enum drm_plane_type type; - plane = mdp5_plane_init(dev, primary); + if (i < num_crtcs) + type = DRM_PLANE_TYPE_PRIMARY; + else if (hwpipe->caps & MDP_PIPE_CAP_CURSOR) + type = DRM_PLANE_TYPE_CURSOR; + else + type = DRM_PLANE_TYPE_OVERLAY; + + plane = mdp5_plane_init(dev, type); if (IS_ERR(plane)) { ret = PTR_ERR(plane); dev_err(dev->dev, "failed to construct plane %d (%d)\n", i, ret); @@ -439,10 +459,16 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) } priv->planes[priv->num_planes++] = plane; - if (!primary) - continue; + if (type == DRM_PLANE_TYPE_PRIMARY) + primary[pi++] = plane; + if (type == DRM_PLANE_TYPE_CURSOR) + cursor[ci++] = plane; + } + + for (i = 0; i < num_crtcs; i++) { + struct drm_crtc *crtc; - crtc = mdp5_crtc_init(dev, plane, i); + crtc = mdp5_crtc_init(dev, primary[i], cursor[i], i); if (IS_ERR(crtc)) { ret = PTR_ERR(crtc); dev_err(dev->dev, "failed to construct crtc %d (%d)\n", i, ret); @@ -451,13 +477,14 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) priv->crtcs[priv->num_crtcs++] = crtc; } - /* Construct encoders and modeset initialize connector devices - * for each external display interface. + /* + * Now that we know the number of crtcs we've created, set the possible + * crtcs for the encoders */ - for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) { - ret = modeset_init_intf(mdp5_kms, i); - if (ret) - goto fail; + for (i = 0; i < priv->num_encoders; i++) { + struct drm_encoder *encoder = priv->encoders[i]; + + encoder->possible_crtcs = (1 << priv->num_crtcs) - 1; } return 0; @@ -773,6 +800,9 @@ static int hwpipe_init(struct mdp5_kms *mdp5_kms) static const enum mdp5_pipe dma_planes[] = { SSPP_DMA0, SSPP_DMA1, }; + static const enum mdp5_pipe cursor_planes[] = { + SSPP_CURSOR0, SSPP_CURSOR1, + }; const struct mdp5_cfg_hw *hw_cfg; int ret; @@ -796,6 +826,13 @@ static int hwpipe_init(struct mdp5_kms *mdp5_kms) if (ret) return ret; + /* Construct cursor pipes: */ + ret = construct_pipes(mdp5_kms, hw_cfg->pipe_cursor.count, + cursor_planes, hw_cfg->pipe_cursor.base, + hw_cfg->pipe_cursor.caps); + if (ret) + return ret; + return 0; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index cdfc63d90c7b..9de471191eba 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -126,6 +126,17 @@ struct mdp5_interface { enum mdp5_intf_mode mode; }; +struct mdp5_encoder { + struct drm_encoder base; + struct mdp5_interface intf; + spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */ + bool enabled; + uint32_t bsc; + + struct mdp5_ctl *ctl; +}; +#define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base) + static inline void mdp5_write(struct mdp5_kms *mdp5_kms, u32 reg, u32 data) { msm_writel(data, mdp5_kms->mmio + reg); @@ -156,6 +167,7 @@ static inline const char *pipe2name(enum mdp5_pipe pipe) NAME(RGB0), NAME(RGB1), NAME(RGB2), NAME(DMA0), NAME(DMA1), NAME(VIG3), NAME(RGB3), + NAME(CURSOR0), NAME(CURSOR1), #undef NAME }; return names[pipe]; @@ -231,8 +243,10 @@ void mdp5_irq_domain_fini(struct mdp5_kms *mdp5_kms); uint32_t mdp5_plane_get_flush(struct drm_plane *plane); enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane); -struct drm_plane *mdp5_plane_init(struct drm_device *dev, bool primary); +struct drm_plane *mdp5_plane_init(struct drm_device *dev, + enum drm_plane_type type); +struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc); uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc); int mdp5_crtc_get_lm(struct drm_crtc *crtc); @@ -240,25 +254,36 @@ void mdp5_crtc_set_pipeline(struct drm_crtc *crtc, struct mdp5_interface *intf, struct mdp5_ctl *ctl); void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc); struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, - struct drm_plane *plane, int id); + struct drm_plane *plane, + struct drm_plane *cursor_plane, int id); struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, struct mdp5_interface *intf, struct mdp5_ctl *ctl); -int mdp5_encoder_set_split_display(struct drm_encoder *encoder, - struct drm_encoder *slave_encoder); +int mdp5_vid_encoder_set_split_display(struct drm_encoder *encoder, + struct drm_encoder *slave_encoder); +void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode); int mdp5_encoder_get_linecount(struct drm_encoder *encoder); u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder); #ifdef CONFIG_DRM_MSM_DSI -struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, - struct mdp5_interface *intf, struct mdp5_ctl *ctl); +void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void mdp5_cmd_encoder_disable(struct drm_encoder *encoder); +void mdp5_cmd_encoder_enable(struct drm_encoder *encoder); int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, - struct drm_encoder *slave_encoder); + struct drm_encoder *slave_encoder); #else -static inline struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, - struct mdp5_interface *intf, struct mdp5_ctl *ctl) +static inline void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} +static inline void mdp5_cmd_encoder_disable(struct drm_encoder *encoder) +{ +} +static inline void mdp5_cmd_encoder_enable(struct drm_encoder *encoder) { - return ERR_PTR(-EINVAL); } static inline int mdp5_cmd_encoder_set_split_display( struct drm_encoder *encoder, struct drm_encoder *slave_encoder) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c index 1ae9dc8d260d..35c4dabb0c0c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c @@ -53,6 +53,14 @@ struct mdp5_hw_pipe *mdp5_pipe_assign(struct drm_atomic_state *s, if (caps & ~cur->caps) continue; + /* + * don't assign a cursor pipe to a plane that isn't going to + * be used as a cursor + */ + if (cur->caps & MDP_PIPE_CAP_CURSOR && + plane->type != DRM_PLANE_TYPE_CURSOR) + continue; + /* possible candidate, take the one with the * fewest unneeded caps bits set: */ diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 25d9d0a97156..0ffb8affef35 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -29,6 +29,11 @@ struct mdp5_plane { static int mdp5_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, + struct drm_rect *src, struct drm_rect *dest); + +static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, @@ -45,7 +50,7 @@ static struct mdp5_kms *get_kms(struct drm_plane *plane) static bool plane_enabled(struct drm_plane_state *state) { - return state->fb && state->crtc; + return state->visible; } static void mdp5_plane_destroy(struct drm_plane *plane) @@ -246,6 +251,19 @@ static const struct drm_plane_funcs mdp5_plane_funcs = { .atomic_print_state = mdp5_plane_atomic_print_state, }; +static const struct drm_plane_funcs mdp5_cursor_plane_funcs = { + .update_plane = mdp5_update_cursor_plane_legacy, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = mdp5_plane_destroy, + .set_property = drm_atomic_helper_plane_set_property, + .atomic_set_property = mdp5_plane_atomic_set_property, + .atomic_get_property = mdp5_plane_atomic_get_property, + .reset = mdp5_plane_reset, + .atomic_duplicate_state = mdp5_plane_duplicate_state, + .atomic_destroy_state = mdp5_plane_destroy_state, + .atomic_print_state = mdp5_plane_atomic_print_state, +}; + static int mdp5_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { @@ -272,15 +290,20 @@ static void mdp5_plane_cleanup_fb(struct drm_plane *plane, msm_framebuffer_cleanup(fb, mdp5_kms->id); } -static int mdp5_plane_atomic_check(struct drm_plane *plane, - struct drm_plane_state *state) +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) +static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state, + struct drm_plane_state *state) { struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(state); + struct drm_plane *plane = state->plane; struct drm_plane_state *old_state = plane->state; struct mdp5_cfg *config = mdp5_cfg_get_config(get_kms(plane)->cfg); bool new_hwpipe = false; uint32_t max_width, max_height; uint32_t caps = 0; + struct drm_rect clip; + int min_scale, max_scale; + int ret; DBG("%s: check (%d -> %d)", plane->name, plane_enabled(old_state), plane_enabled(state)); @@ -296,6 +319,18 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, return -ERANGE; } + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + min_scale = FRAC_16_16(1, 8); + max_scale = FRAC_16_16(8, 1); + + ret = drm_plane_helper_check_state(state, &clip, min_scale, + max_scale, true, true); + if (ret) + return ret; + if (plane_enabled(state)) { unsigned int rotation; const struct mdp_format *format; @@ -321,6 +356,9 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, if (rotation & DRM_REFLECT_Y) caps |= MDP_PIPE_CAP_VFLIP; + if (plane->type == DRM_PLANE_TYPE_CURSOR) + caps |= MDP_PIPE_CAP_CURSOR; + /* (re)allocate hw pipe if we don't have one or caps-mismatch: */ if (!mdp5_state->hwpipe || (caps & ~mdp5_state->hwpipe->caps)) new_hwpipe = true; @@ -356,6 +394,23 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, return 0; } +static int mdp5_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + + crtc = state->crtc ? state->crtc : plane->state->crtc; + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + return mdp5_plane_atomic_check_with_state(crtc_state, state); +} + static void mdp5_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { @@ -368,10 +423,7 @@ static void mdp5_plane_atomic_update(struct drm_plane *plane, ret = mdp5_plane_mode_set(plane, state->crtc, state->fb, - state->crtc_x, state->crtc_y, - state->crtc_w, state->crtc_h, - state->src_x, state->src_y, - state->src_w, state->src_h); + &state->src, &state->dst); /* atomic_check should have ensured that this doesn't fail */ WARN_ON(ret < 0); } @@ -664,10 +716,7 @@ static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe, static int mdp5_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) + struct drm_rect *src, struct drm_rect *dest) { struct drm_plane_state *pstate = plane->state; struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(pstate)->hwpipe; @@ -683,10 +732,14 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, uint32_t pix_format; unsigned int rotation; bool vflip, hflip; + int crtc_x, crtc_y; + unsigned int crtc_w, crtc_h; + uint32_t src_x, src_y; + uint32_t src_w, src_h; unsigned long flags; int ret; - nplanes = drm_format_num_planes(fb->pixel_format); + nplanes = fb->format->num_planes; /* bad formats should already be rejected: */ if (WARN_ON(nplanes > pipe2nclients(pipe))) @@ -695,6 +748,16 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, format = to_mdp_format(msm_framebuffer_format(fb)); pix_format = format->base.pixel_format; + src_x = src->x1; + src_y = src->y1; + src_w = drm_rect_width(src); + src_h = drm_rect_height(src); + + crtc_x = dest->x1; + crtc_y = dest->y1; + crtc_w = drm_rect_width(dest); + crtc_h = drm_rect_height(dest); + /* src values are in Q16 fixed point, convert to integer: */ src_x = src_x >> 16; src_y = src_y >> 16; @@ -818,12 +881,88 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, return ret; } +static int mdp5_update_cursor_plane_legacy(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + struct drm_plane_state *plane_state, *new_plane_state; + struct mdp5_plane_state *mdp5_pstate; + struct drm_crtc_state *crtc_state = crtc->state; + int ret; + + if (!crtc_state->active || drm_atomic_crtc_needs_modeset(crtc_state)) + goto slow; + + plane_state = plane->state; + mdp5_pstate = to_mdp5_plane_state(plane_state); + + /* don't use fast path if we don't have a hwpipe allocated yet */ + if (!mdp5_pstate->hwpipe) + goto slow; + + /* only allow changing of position(crtc x/y or src x/y) in fast path */ + if (plane_state->crtc != crtc || + plane_state->src_w != src_w || + plane_state->src_h != src_h || + plane_state->crtc_w != crtc_w || + plane_state->crtc_h != crtc_h || + !plane_state->fb || + plane_state->fb != fb) + goto slow; + + new_plane_state = mdp5_plane_duplicate_state(plane); + if (!new_plane_state) + return -ENOMEM; + + new_plane_state->src_x = src_x; + new_plane_state->src_y = src_y; + new_plane_state->src_w = src_w; + new_plane_state->src_h = src_h; + new_plane_state->crtc_x = crtc_x; + new_plane_state->crtc_y = crtc_y; + new_plane_state->crtc_w = crtc_w; + new_plane_state->crtc_h = crtc_h; + + ret = mdp5_plane_atomic_check_with_state(crtc_state, new_plane_state); + if (ret) + goto slow_free; + + if (new_plane_state->visible) { + struct mdp5_ctl *ctl; + + ret = mdp5_plane_mode_set(plane, crtc, fb, + &new_plane_state->src, + &new_plane_state->dst); + WARN_ON(ret < 0); + + ctl = mdp5_crtc_get_ctl(crtc); + + mdp5_ctl_commit(ctl, mdp5_plane_get_flush(plane)); + } + + *to_mdp5_plane_state(plane_state) = + *to_mdp5_plane_state(new_plane_state); + + mdp5_plane_destroy_state(plane, new_plane_state); + + return 0; +slow_free: + mdp5_plane_destroy_state(plane, new_plane_state); +slow: + return drm_atomic_helper_update_plane(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); +} + enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) { struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state); if (WARN_ON(!pstate->hwpipe)) - return 0; + return SSPP_NONE; return pstate->hwpipe->pipe; } @@ -839,12 +978,12 @@ uint32_t mdp5_plane_get_flush(struct drm_plane *plane) } /* initialize plane */ -struct drm_plane *mdp5_plane_init(struct drm_device *dev, bool primary) +struct drm_plane *mdp5_plane_init(struct drm_device *dev, + enum drm_plane_type type) { struct drm_plane *plane = NULL; struct mdp5_plane *mdp5_plane; int ret; - enum drm_plane_type type; mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL); if (!mdp5_plane) { @@ -857,10 +996,16 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev, bool primary) mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats, ARRAY_SIZE(mdp5_plane->formats), false); - type = primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; - ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, - mdp5_plane->formats, mdp5_plane->nformats, - type, NULL); + if (type == DRM_PLANE_TYPE_CURSOR) + ret = drm_universal_plane_init(dev, plane, 0xff, + &mdp5_cursor_plane_funcs, + mdp5_plane->formats, mdp5_plane->nformats, + type, NULL); + else + ret = drm_universal_plane_init(dev, plane, 0xff, + &mdp5_plane_funcs, + mdp5_plane->formats, mdp5_plane->nformats, + type, NULL); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.h b/drivers/gpu/drm/msm/mdp/mdp_kms.h index 303130320748..7574cdfef418 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.h @@ -112,6 +112,7 @@ const struct msm_format *mdp_get_format(struct msm_kms *kms, uint32_t format); #define MDP_PIPE_CAP_CSC BIT(3) #define MDP_PIPE_CAP_DECIMATION BIT(4) #define MDP_PIPE_CAP_SW_PIX_EXT BIT(5) +#define MDP_PIPE_CAP_CURSOR BIT(6) static inline bool pipe_supports_yuv(uint32_t pipe_caps) { diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 30b5d23e53b4..9633a68b14d7 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -93,11 +93,6 @@ static void msm_atomic_wait_for_commit_done(struct drm_device *dev, if (!crtc->state->enable) continue; - /* Legacy cursor ioctls are completely unsynced, and userspace - * relies on that (by doing tons of cursor updates). */ - if (old_state->legacy_cursor_update) - continue; - kms->funcs->wait_for_crtc_commit_done(kms, crtc); } } @@ -151,20 +146,29 @@ static void commit_worker(struct work_struct *work) complete_commit(container_of(work, struct msm_commit, work), true); } +/* + * this func is identical to the drm_atomic_helper_check, but we keep this + * because we might eventually need to have a more finegrained check + * sequence without using the atomic helpers. + * + * In the past, we first called drm_atomic_helper_check_planes, and then + * drm_atomic_helper_check_modeset. We needed this because the MDP5 plane's + * ->atomic_check could update ->mode_changed for pixel format changes. + * This, however isn't needed now because if there is a pixel format change, + * we just assign a new hwpipe for it with a new SMP allocation. We might + * eventually hit a condition where we would need to do a full modeset if + * we run out of planes. There, we'd probably need to set mode_changed. + */ int msm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { int ret; - /* - * msm ->atomic_check can update ->mode_changed for pixel format - * changes, hence must be run before we check the modeset changes. - */ - ret = drm_atomic_helper_check_planes(dev, state); + ret = drm_atomic_helper_check_modeset(dev, state); if (ret) return ret; - ret = drm_atomic_helper_check_modeset(dev, state); + ret = drm_atomic_helper_check_planes(dev, state); if (ret) return ret; diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c index c1b40f5adb60..387f0616e115 100644 --- a/drivers/gpu/drm/msm/msm_debugfs.c +++ b/drivers/gpu/drm/msm/msm_debugfs.c @@ -52,7 +52,11 @@ static int msm_gem_show(struct drm_device *dev, struct seq_file *m) static int msm_mm_show(struct drm_device *dev, struct seq_file *m) { - return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm); + struct drm_printer p = drm_seq_file_printer(m); + + drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p); + + return 0; } static int msm_fb_show(struct drm_device *dev, struct seq_file *m) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index e29bb66f55b1..70226eaa5cac 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -91,6 +91,25 @@ module_param(dumpstate, bool, 0600); * Util/helpers: */ +struct clk *msm_clk_get(struct platform_device *pdev, const char *name) +{ + struct clk *clk; + char name2[32]; + + clk = devm_clk_get(&pdev->dev, name); + if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER) + return clk; + + snprintf(name2, sizeof(name2), "%s_clk", name); + + clk = devm_clk_get(&pdev->dev, name2); + if (!IS_ERR(clk)) + dev_warn(&pdev->dev, "Using legacy clk name binding. Use " + "\"%s\" instead of \"%s\"\n", name, name2); + + return clk; +} + void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, const char *dbgname) { @@ -985,6 +1004,7 @@ static int add_display_components(struct device *dev, * as components. */ static const struct of_device_id msm_gpu_match[] = { + { .compatible = "qcom,adreno" }, { .compatible = "qcom,adreno-3xx" }, { .compatible = "qcom,kgsl-3d0" }, { }, diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index ed4dad3ca133..c3b14876edaa 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -206,7 +206,7 @@ void msm_gem_shrinker_cleanup(struct drm_device *dev); int msm_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma); int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); -int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +int msm_gem_fault(struct vm_fault *vmf); uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj); int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id, uint64_t *iova); @@ -275,16 +275,11 @@ int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev, struct drm_encoder *encoder); struct msm_dsi; -enum msm_dsi_encoder_id { - MSM_DSI_VIDEO_ENCODER_ID = 0, - MSM_DSI_CMD_ENCODER_ID = 1, - MSM_DSI_ENCODER_NUM = 2 -}; #ifdef CONFIG_DRM_MSM_DSI void __init msm_dsi_register(void); void __exit msm_dsi_unregister(void); int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, - struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]); + struct drm_encoder *encoder); #else static inline void __init msm_dsi_register(void) { @@ -293,8 +288,8 @@ static inline void __exit msm_dsi_unregister(void) { } static inline int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, - struct drm_device *dev, - struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]) + struct drm_device *dev, + struct drm_encoder *encoder) { return -EINVAL; } @@ -318,6 +313,7 @@ static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; } static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {} #endif +struct clk *msm_clk_get(struct platform_device *pdev, const char *name); void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, const char *dbgname); void msm_writel(u32 data, void __iomem *addr); diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 9acf544e7a8f..5cf165c9c3a9 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -41,7 +41,7 @@ static int msm_framebuffer_create_handle(struct drm_framebuffer *fb, static void msm_framebuffer_destroy(struct drm_framebuffer *fb) { struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + int i, n = fb->format->num_planes; DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); @@ -65,10 +65,10 @@ static const struct drm_framebuffer_funcs msm_framebuffer_funcs = { void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) { struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + int i, n = fb->format->num_planes; seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n", - fb->width, fb->height, (char *)&fb->pixel_format, + fb->width, fb->height, (char *)&fb->format->format, drm_framebuffer_read_refcount(fb), fb->base.id); for (i = 0; i < n; i++) { @@ -87,7 +87,7 @@ void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) int msm_framebuffer_prepare(struct drm_framebuffer *fb, int id) { struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int ret, i, n = drm_format_num_planes(fb->pixel_format); + int ret, i, n = fb->format->num_planes; uint64_t iova; for (i = 0; i < n; i++) { @@ -103,7 +103,7 @@ int msm_framebuffer_prepare(struct drm_framebuffer *fb, int id) void msm_framebuffer_cleanup(struct drm_framebuffer *fb, int id) { struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + int i, n = fb->format->num_planes; for (i = 0; i < n; i++) msm_gem_put_iova(msm_fb->planes[i], id); @@ -217,7 +217,7 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, msm_fb->planes[i] = bos[i]; } - drm_helper_mode_fill_fb_struct(fb, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs); if (ret) { diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index bffe93498512..6b1b375653f7 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -148,7 +148,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, strcpy(fbi->fix.id, "msm"); - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); dev->mode_config.fb_base = paddr; @@ -174,10 +174,8 @@ fail_unlock: fail: if (ret) { - if (fb) { - drm_framebuffer_unregister_private(fb); + if (fb) drm_framebuffer_remove(fb); - } } return ret; @@ -203,8 +201,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs); - ret = drm_fb_helper_init(dev, helper, - priv->num_crtcs, priv->num_connectors); + ret = drm_fb_helper_init(dev, helper, priv->num_connectors); if (ret) { dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret); goto fail; @@ -247,7 +244,6 @@ void msm_fbdev_free(struct drm_device *dev) /* this will free the backing object */ if (fbdev->fb) { msm_gem_put_vaddr(fbdev->bo); - drm_framebuffer_unregister_private(fbdev->fb); drm_framebuffer_remove(fbdev->fb); } diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 1974ccb781de..59811f29607d 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -54,8 +54,7 @@ static struct page **get_pages_vram(struct drm_gem_object *obj, if (!p) return ERR_PTR(-ENOMEM); - ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, - npages, 0, DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, npages); if (ret) { drm_free_large(p); return ERR_PTR(ret); @@ -192,8 +191,9 @@ int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma) return msm_gem_mmap_obj(vma->vm_private_data, vma); } -int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int msm_gem_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; struct msm_drm_private *priv = dev->dev_private; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 489676568a10..1172fe7a9252 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -95,13 +95,13 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, */ submit->bos[i].flags = 0; - ret = copy_from_user_inatomic(&submit_bo, userptr, sizeof(submit_bo)); - if (unlikely(ret)) { + if (copy_from_user_inatomic(&submit_bo, userptr, sizeof(submit_bo))) { pagefault_enable(); spin_unlock(&file->table_lock); - ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo)); - if (ret) + if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) { + ret = -EFAULT; goto out; + } spin_lock(&file->table_lock); pagefault_disable(); } @@ -317,9 +317,10 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob uint64_t iova; bool valid; - ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc)); - if (ret) + if (copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc))) { + ret = -EFAULT; goto out; + } if (submit_reloc.submit_offset % 4) { DRM_ERROR("non-aligned reloc offset: %u\n", diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index a311d26ccb21..b654eca7636a 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -45,8 +45,7 @@ msm_gem_map_vma(struct msm_gem_address_space *aspace, if (WARN_ON(drm_mm_node_allocated(&vma->node))) return 0; - ret = drm_mm_insert_node(&aspace->mm, &vma->node, npages, - 0, DRM_MM_SEARCH_DEFAULT); + ret = drm_mm_insert_node(&aspace->mm, &vma->node, npages); if (ret) return ret; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index b28527a65d09..99e05aacbee1 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -560,8 +560,7 @@ static irqreturn_t irq_handler(int irq, void *data) } static const char *clk_names[] = { - "core_clk", "iface_clk", "rbbmtimer_clk", "mem_clk", - "mem_iface_clk", "alt_mem_iface_clk", + "core", "iface", "rbbmtimer", "mem", "mem_iface", "alt_mem_iface", }; int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, @@ -625,13 +624,13 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, /* Acquire clocks: */ for (i = 0; i < ARRAY_SIZE(clk_names); i++) { - gpu->grp_clks[i] = devm_clk_get(&pdev->dev, clk_names[i]); + gpu->grp_clks[i] = msm_clk_get(pdev, clk_names[i]); DBG("grp_clks[%s]: %p", clk_names[i], gpu->grp_clks[i]); if (IS_ERR(gpu->grp_clks[i])) gpu->grp_clks[i] = NULL; } - gpu->ebi1_clk = devm_clk_get(&pdev->dev, "bus_clk"); + gpu->ebi1_clk = msm_clk_get(pdev, "bus"); DBG("ebi1_clk: %p", gpu->ebi1_clk); if (IS_ERR(gpu->ebi1_clk)) gpu->ebi1_clk = NULL; diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 61aaaa1de6eb..7f5779daf5c8 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -24,9 +24,12 @@ struct msm_iommu { }; #define to_msm_iommu(x) container_of(x, struct msm_iommu, base) -static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev, +static int msm_fault_handler(struct iommu_domain *domain, struct device *dev, unsigned long iova, int flags, void *arg) { + struct msm_iommu *iommu = arg; + if (iommu->base.handler) + return iommu->base.handler(iommu->base.arg, iova, flags); pr_warn_ratelimited("*** fault: iova=%08lx, flags=%d\n", iova, flags); return 0; } @@ -136,7 +139,7 @@ struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain) iommu->domain = domain; msm_mmu_init(&iommu->base, dev, &funcs); - iommu_set_fault_handler(domain, msm_fault_handler, dev); + iommu_set_fault_handler(domain, msm_fault_handler, iommu); return &iommu->base; } diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index e470f4cf8f76..117635d2b8c5 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -56,6 +56,9 @@ struct msm_kms_funcs { struct drm_encoder *encoder, struct drm_encoder *slave_encoder, bool is_cmd_mode); + void (*set_encoder_mode)(struct msm_kms *kms, + struct drm_encoder *encoder, + bool cmd_mode); /* cleanup: */ void (*destroy)(struct msm_kms *kms); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index f85c879e68d2..aa2c5d4580c8 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -33,6 +33,8 @@ struct msm_mmu_funcs { struct msm_mmu { const struct msm_mmu_funcs *funcs; struct device *dev; + int (*handler)(void *arg, unsigned long iova, int flags); + void *arg; }; static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev, @@ -45,4 +47,11 @@ static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev, struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain); struct msm_mmu *msm_gpummu_new(struct device *dev, struct msm_gpu *gpu); +static inline void msm_mmu_set_fault_handler(struct msm_mmu *mmu, void *arg, + int (*handler)(void *arg, unsigned long iova, int flags)) +{ + mmu->arg = arg; + mmu->handler = handler; +} + #endif /* __MSM_MMU_H__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c index 081890336ce7..e10a4eda4078 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -46,7 +46,7 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) { struct drm_crtc *crtc = &mxsfb->pipe.crtc; struct drm_device *drm = crtc->dev; - const u32 format = crtc->primary->state->fb->pixel_format; + const u32 format = crtc->primary->state->fb->format->format; u32 ctrl, ctrl1; ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 79a18bf48b54..cdfbe0284635 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -218,7 +218,7 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) drm_kms_helper_poll_init(drm); - mxsfb->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + mxsfb->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(mxsfb->fbdev)) { mxsfb->fbdev = NULL; @@ -395,8 +395,8 @@ static int mxsfb_probe(struct platform_device *pdev) pdev->id_entry = of_id->data; drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev); - if (!drm) - return -ENOMEM; + if (IS_ERR(drm)) + return PTR_ERR(drm); ret = mxsfb_load(drm, 0); if (ret) diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 2922a82cba8e..c02a13406a81 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -1,6 +1,6 @@ config DRM_NOUVEAU tristate "Nouveau (NVIDIA) cards" - depends on DRM && PCI + depends on DRM && PCI && MMU select FW_LOADER select DRM_KMS_HELPER select DRM_TTM @@ -16,6 +16,7 @@ config DRM_NOUVEAU select INPUT if ACPI && X86 select THERMAL if ACPI && X86 select ACPI_VIDEO if ACPI && X86 + select DRM_VM help Choose this option for open-source NVIDIA support. diff --git a/drivers/gpu/drm/nouveau/dispnv04/arb.c b/drivers/gpu/drm/nouveau/dispnv04/arb.c index a555681c3096..90075b676256 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/arb.c +++ b/drivers/gpu/drm/nouveau/dispnv04/arb.c @@ -198,7 +198,7 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp, int *burst, int *lwm) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; struct nv_fifo_info fifo_data; struct nv_sim_state sim_data; int MClk = nouveau_hw_get_clock(dev, PLL_MEMORY); @@ -227,7 +227,7 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp, sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1); } - if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_TNT) nv04_calc_arb(&fifo_data, &sim_data); else nv10_calc_arb(&fifo_data, &sim_data); @@ -254,7 +254,7 @@ nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm { struct nouveau_drm *drm = nouveau_drm(dev); - if (drm->device.info.family < NV_DEVICE_INFO_V0_KELVIN) + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_KELVIN) nv04_update_arb(dev, vclk, bpp, burst, lwm); else if ((dev->pdev->device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ || (dev->pdev->device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) { diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 59d1d1c5de5f..ab7b69c11d40 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -113,8 +113,8 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod { struct drm_device *dev = crtc->dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_bios *bios = nvxx_bios(&drm->device); - struct nvkm_clk *clk = nvxx_clk(&drm->device); + struct nvkm_bios *bios = nvxx_bios(&drm->client.device); + struct nvkm_clk *clk = nvxx_clk(&drm->client.device); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nv04_mode_state *state = &nv04_display(dev)->mode_reg; struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index]; @@ -138,7 +138,7 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod * has yet been observed in allowing the use a single stage pll on all * nv43 however. the behaviour of single stage use is untested on nv40 */ - if (drm->device.info.chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2)) + if (drm->client.device.info.chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2)) memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2)); @@ -148,10 +148,10 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK; /* The blob uses this always, so let's do the same */ - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE; /* again nv40 and some nv43 act more like nv3x as described above */ - if (drm->device.info.chipset < 0x41) + if (drm->client.device.info.chipset < 0x41) state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL | NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL; state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK; @@ -270,7 +270,7 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode) horizEnd = horizTotal - 2; horizBlankEnd = horizTotal + 4; #if 0 - if (dev->overlayAdaptor && drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + if (dev->overlayAdaptor && drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) /* This reportedly works around some video overlay bandwidth problems */ horizTotal += 2; #endif @@ -460,6 +460,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; struct nv04_crtc_reg *savep = &nv04_display(dev)->saved_reg.crtc_reg[nv_crtc->index]; + const struct drm_framebuffer *fb = crtc->primary->fb; struct drm_encoder *encoder; bool lvds_output = false, tmds_output = false, tv_output = false, off_chip_digital = false; @@ -504,7 +505,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 | NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 | NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM; - if (drm->device.info.chipset >= 0x11) + if (drm->client.device.info.chipset >= 0x11) regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32; if (mode->flags & DRM_MODE_FLAG_DBLSCAN) regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE; @@ -545,47 +546,47 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) * 1 << 30 on 0x60.830), for no apparent reason */ regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1; regp->crtc_830 = mode->crtc_vdisplay - 3; regp->crtc_834 = mode->crtc_vdisplay - 1; - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) /* This is what the blob does */ regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) regp->crtc_cfg = NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC; else regp->crtc_cfg = NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC; /* Some misc regs */ - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) { regp->CRTC[NV_CIO_CRE_85] = 0xFF; regp->CRTC[NV_CIO_CRE_86] = 0x1; } - regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->primary->fb->depth + 1) / 8; + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (fb->format->depth + 1) / 8; /* Enable slaved mode (called MODE_TV in nv4ref.h) */ if (lvds_output || tmds_output || tv_output) regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7); /* Generic PRAMDAC regs */ - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) /* Only bit that bios and blob set. */ regp->nv10_cursync = (1 << 25); regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; - if (crtc->primary->fb->depth == 16) + if (fb->format->depth == 16) regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; - if (drm->device.info.chipset >= 0x11) + if (drm->client.device.info.chipset >= 0x11) regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG; regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */ @@ -648,7 +649,7 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, nv_crtc_mode_set_vga(crtc, adjusted_mode); /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */ - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk); nv_crtc_mode_set_regs(crtc, adjusted_mode); nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock); @@ -709,7 +710,7 @@ static void nv_crtc_prepare(struct drm_crtc *crtc) /* Some more preparation. */ NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) { uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900); NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000); } @@ -847,16 +848,16 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, nv_crtc->fb.offset = fb->nvbo->bo.offset; - if (nv_crtc->lut.depth != drm_fb->depth) { - nv_crtc->lut.depth = drm_fb->depth; + if (nv_crtc->lut.depth != drm_fb->format->depth) { + nv_crtc->lut.depth = drm_fb->format->depth; nv_crtc_gamma_load(crtc); } /* Update the framebuffer format. */ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3; - regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->primary->fb->depth + 1) / 8; + regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (drm_fb->format->depth + 1) / 8; regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; - if (crtc->primary->fb->depth == 16) + if (drm_fb->format->depth == 16) regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX); NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL, @@ -873,11 +874,11 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, /* Update the framebuffer location. */ regp->fb_start = nv_crtc->fb.offset & ~3; - regp->fb_start += (y * drm_fb->pitches[0]) + (x * drm_fb->bits_per_pixel / 8); + regp->fb_start += (y * drm_fb->pitches[0]) + (x * drm_fb->format->cpp[0]); nv_set_crtc_base(dev, nv_crtc->index, regp->fb_start); /* Update the arbitration parameters. */ - nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel, + nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->format->cpp[0] * 8, &arb_burst, &arb_lwm); regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst; @@ -885,7 +886,7 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX); crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KELVIN) { regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8; crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47); } @@ -966,7 +967,7 @@ static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, { struct nouveau_drm *drm = nouveau_drm(dev); - if (drm->device.info.chipset == 0x11) { + if (drm->client.device.info.chipset == 0x11) { pixel = ((pixel & 0x000000ff) << 24) | ((pixel & 0x0000ff00) << 8) | ((pixel & 0x00ff0000) >> 8) | @@ -1007,7 +1008,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, if (ret) goto out; - if (drm->device.info.chipset >= 0x11) + if (drm->client.device.info.chipset >= 0x11) nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); else nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); @@ -1123,8 +1124,9 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs); drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); - ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, NULL, &nv_crtc->cursor.nvbo); + ret = nouveau_bo_new(&nouveau_drm(dev)->client, 64*64*4, 0x100, + TTM_PL_FLAG_VRAM, 0, 0x0000, NULL, NULL, + &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, false); if (!ret) { diff --git a/drivers/gpu/drm/nouveau/dispnv04/cursor.c b/drivers/gpu/drm/nouveau/dispnv04/cursor.c index c83116a308a4..f26e44ea7389 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/cursor.c +++ b/drivers/gpu/drm/nouveau/dispnv04/cursor.c @@ -55,7 +55,7 @@ nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) nv_fix_nv40_hw_cursor(dev, nv_crtc->index); } diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c index b6cc7766e6f7..4feab0a5419d 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/dac.c +++ b/drivers/gpu/drm/nouveau/dispnv04/dac.c @@ -66,7 +66,7 @@ int nv04_dac_output_offset(struct drm_encoder *encoder) static int sample_load_twice(struct drm_device *dev, bool sense[2]) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; int i; for (i = 0; i < 2; i++) { @@ -80,19 +80,19 @@ static int sample_load_twice(struct drm_device *dev, bool sense[2]) * use a 10ms timeout (guards against crtc being inactive, in * which case blank state would never change) */ - if (nvif_msec(&drm->device, 10, + if (nvif_msec(&drm->client.device, 10, if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) break; ) < 0) return -EBUSY; - if (nvif_msec(&drm->device, 10, + if (nvif_msec(&drm->client.device, 10, if ( (nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) break; ) < 0) return -EBUSY; - if (nvif_msec(&drm->device, 10, + if (nvif_msec(&drm->client.device, 10, if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 1)) break; ) < 0) @@ -133,7 +133,7 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) { struct drm_device *dev = encoder->dev; - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; struct nouveau_drm *drm = nouveau_drm(dev); uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode; uint8_t saved_palette0[3], saved_palette_mask; @@ -236,8 +236,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &nouveau_drm(dev)->device.object; - struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; + struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, @@ -288,7 +288,7 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ routput = (saved_routput & 0xfffffece) | head << 8; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CURIE) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CURIE) { if (dcb->type == DCB_OUTPUT_TV) routput |= 0x1a << 16; else @@ -403,7 +403,7 @@ static void nv04_dac_mode_set(struct drm_encoder *encoder, } /* This could use refinement for flatpanels, but it should work this way */ - if (drm->device.info.chipset < 0x44) + if (drm->client.device.info.chipset < 0x44) NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); else NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c index c2947ef7d4fc..9805d2cdc1a1 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c @@ -281,7 +281,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index]; @@ -290,6 +290,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct drm_display_mode *output_mode = &nv_encoder->mode; struct drm_connector *connector = &nv_connector->base; + const struct drm_framebuffer *fb = encoder->crtc->primary->fb; uint32_t mode_ratio, panel_ratio; NV_DEBUG(drm, "Output mode on CRTC %d:\n", nv_crtc->index); @@ -415,8 +416,8 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, /* Output property. */ if ((nv_connector->dithering_mode == DITHERING_MODE_ON) || (nv_connector->dithering_mode == DITHERING_MODE_AUTO && - encoder->crtc->primary->fb->depth > connector->display_info.bpc * 3)) { - if (drm->device.info.chipset == 0x11) + fb->format->depth > connector->display_info.bpc * 3)) { + if (drm->client.device.info.chipset == 0x11) regp->dither = savep->dither | 0x00010000; else { int i; @@ -427,7 +428,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder, } } } else { - if (drm->device.info.chipset != 0x11) { + if (drm->client.device.info.chipset != 0x11) { /* reset them */ int i; for (i = 0; i < 3; i++) { @@ -463,7 +464,7 @@ static void nv04_dfp_commit(struct drm_encoder *encoder) NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); /* This could use refinement for flatpanels, but it should work this way */ - if (drm->device.info.chipset < 0x44) + if (drm->client.device.info.chipset < 0x44) NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); else NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); @@ -485,7 +486,7 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode) { #ifdef __powerpc__ struct drm_device *dev = encoder->dev; - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; /* BIOS scripts usually take care of the backlight, thanks * Apple for your consistency. @@ -623,7 +624,7 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; struct dcb_output *dcb = nouveau_encoder(encoder)->dcb; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_PRI); struct nvkm_i2c_bus_probe info[] = { { diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index 34c0f2f67548..5b9d549aa791 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -35,7 +35,7 @@ int nv04_display_create(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct dcb_table *dcb = &drm->vbios.dcb; struct drm_connector *connector, *ct; struct drm_encoder *encoder; @@ -48,7 +48,7 @@ nv04_display_create(struct drm_device *dev) if (!disp) return -ENOMEM; - nvif_object_map(&drm->device.object); + nvif_object_map(&drm->client.device.object); nouveau_display(dev)->priv = disp; nouveau_display(dev)->dtor = nv04_display_destroy; @@ -139,7 +139,7 @@ nv04_display_destroy(struct drm_device *dev) nouveau_display(dev)->priv = NULL; kfree(disp); - nvif_object_unmap(&drm->device.object); + nvif_object_unmap(&drm->client.device.object); } int diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h index 7030307d2d48..bea4543554ba 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h @@ -129,7 +129,7 @@ nv_two_heads(struct drm_device *dev) struct nouveau_drm *drm = nouveau_drm(dev); const int impl = dev->pdev->device & 0x0ff0; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS && impl != 0x0100 && + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS && impl != 0x0100 && impl != 0x0150 && impl != 0x01a0 && impl != 0x0200) return true; @@ -148,7 +148,7 @@ nv_two_reg_pll(struct drm_device *dev) struct nouveau_drm *drm = nouveau_drm(dev); const int impl = dev->pdev->device & 0x0ff0; - if (impl == 0x0310 || impl == 0x0340 || drm->device.info.family >= NV_DEVICE_INFO_V0_CURIE) + if (impl == 0x0310 || impl == 0x0340 || drm->client.device.info.family >= NV_DEVICE_INFO_V0_CURIE) return true; return false; } @@ -170,7 +170,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, u16 table, struct dcb_output *outp, int crtc) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_bios *bios = nvxx_bios(&drm->device); + struct nvkm_bios *bios = nvxx_bios(&drm->client.device); struct nvbios_init init = { .subdev = &bios->subdev, .bios = bios, diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c index e64f52464ecf..b98599002831 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/hw.c +++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c @@ -89,7 +89,7 @@ NVSetOwner(struct drm_device *dev, int owner) if (owner == 1) owner *= 3; - if (drm->device.info.chipset == 0x11) { + if (drm->client.device.info.chipset == 0x11) { /* This might seem stupid, but the blob does it and * omitting it often locks the system up. */ @@ -100,7 +100,7 @@ NVSetOwner(struct drm_device *dev, int owner) /* CR44 is always changed on CRTC0 */ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner); - if (drm->device.info.chipset == 0x11) { /* set me harder */ + if (drm->client.device.info.chipset == 0x11) { /* set me harder */ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); } @@ -149,7 +149,7 @@ nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1, pllvals->NM1 = pll1 & 0xffff; if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2) pllvals->NM2 = pll2 & 0xffff; - else if (drm->device.info.chipset == 0x30 || drm->device.info.chipset == 0x35) { + else if (drm->client.device.info.chipset == 0x30 || drm->client.device.info.chipset == 0x35) { pllvals->M1 &= 0xf; /* only 4 bits */ if (pll1 & NV30_RAMDAC_ENABLE_VCO2) { pllvals->M2 = (pll1 >> 4) & 0x7; @@ -165,8 +165,8 @@ nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype, struct nvkm_pll_vals *pllvals) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &drm->device.object; - struct nvkm_bios *bios = nvxx_bios(&drm->device); + struct nvif_object *device = &drm->client.device.object; + struct nvkm_bios *bios = nvxx_bios(&drm->client.device); uint32_t reg1, pll1, pll2 = 0; struct nvbios_pll pll_lim; int ret; @@ -184,7 +184,7 @@ nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype, pll2 = nvif_rd32(device, reg2); } - if (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && reg1 >= NV_PRAMDAC_VPLL_COEFF) { + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CELSIUS && reg1 >= NV_PRAMDAC_VPLL_COEFF) { uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580); /* check whether vpll has been forced into single stage mode */ @@ -253,7 +253,7 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) */ struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; struct nvkm_clk *clk = nvxx_clk(device); struct nvkm_bios *bios = nvxx_bios(device); struct nvbios_pll pll_lim; @@ -392,21 +392,21 @@ nv_save_state_ramdac(struct drm_device *dev, int head, struct nv04_crtc_reg *regp = &state->crtc_reg[head]; int i; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC); nouveau_hw_get_pllvals(dev, head ? PLL_VPLL1 : PLL_VPLL0, ®p->pllvals); state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT); if (nv_two_heads(dev)) state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); - if (drm->device.info.chipset == 0x11) + if (drm->client.device.info.chipset == 0x11) regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11); regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL); if (nv_gf4_disp_arch(dev)) regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630); - if (drm->device.info.chipset >= 0x30) + if (drm->client.device.info.chipset >= 0x30) regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634); regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP); @@ -448,7 +448,7 @@ nv_save_state_ramdac(struct drm_device *dev, int head, if (nv_gf4_disp_arch(dev)) regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) { regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20); regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24); regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34); @@ -464,26 +464,26 @@ nv_load_state_ramdac(struct drm_device *dev, int head, struct nv04_mode_state *state) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_clk *clk = nvxx_clk(&drm->device); + struct nvkm_clk *clk = nvxx_clk(&drm->client.device); struct nv04_crtc_reg *regp = &state->crtc_reg[head]; uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF; int i; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync); clk->pll_prog(clk, pllreg, ®p->pllvals); NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); if (nv_two_heads(dev)) NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk); - if (drm->device.info.chipset == 0x11) + if (drm->client.device.info.chipset == 0x11) NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither); NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl); if (nv_gf4_disp_arch(dev)) NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630); - if (drm->device.info.chipset >= 0x30) + if (drm->client.device.info.chipset >= 0x30) NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634); NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup); @@ -520,7 +520,7 @@ nv_load_state_ramdac(struct drm_device *dev, int head, if (nv_gf4_disp_arch(dev)) NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) { NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20); NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24); NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34); @@ -601,10 +601,10 @@ nv_save_state_ext(struct drm_device *dev, int head, rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); rd_cio_state(dev, head, regp, NV_CIO_CRE_21); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KELVIN) rd_cio_state(dev, head, regp, NV_CIO_CRE_47); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) rd_cio_state(dev, head, regp, 0x9f); rd_cio_state(dev, head, regp, NV_CIO_CRE_49); @@ -613,14 +613,14 @@ nv_save_state_ext(struct drm_device *dev, int head, rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830); regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850); if (nv_two_heads(dev)) @@ -632,7 +632,7 @@ nv_save_state_ext(struct drm_device *dev, int head, rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB); rd_cio_state(dev, head, regp, NV_CIO_CRE_4B); @@ -661,12 +661,12 @@ nv_load_state_ext(struct drm_device *dev, int head, struct nv04_mode_state *state) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; struct nv04_crtc_reg *regp = &state->crtc_reg[head]; uint32_t reg900; int i; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { if (nv_two_heads(dev)) /* setting ENGINE_CTRL (EC) *must* come before * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in @@ -678,20 +678,20 @@ nv_load_state_ext(struct drm_device *dev, int head, nvif_wr32(device, NV_PVIDEO_INTR_EN, 0); nvif_wr32(device, NV_PVIDEO_OFFSET_BUFF(0), 0); nvif_wr32(device, NV_PVIDEO_OFFSET_BUFF(1), 0); - nvif_wr32(device, NV_PVIDEO_LIMIT(0), drm->device.info.ram_size - 1); - nvif_wr32(device, NV_PVIDEO_LIMIT(1), drm->device.info.ram_size - 1); - nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), drm->device.info.ram_size - 1); - nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), drm->device.info.ram_size - 1); + nvif_wr32(device, NV_PVIDEO_LIMIT(0), drm->client.device.info.ram_size - 1); + nvif_wr32(device, NV_PVIDEO_LIMIT(1), drm->client.device.info.ram_size - 1); + nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), drm->client.device.info.ram_size - 1); + nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), drm->client.device.info.ram_size - 1); nvif_wr32(device, NV_PBUS_POWERCTRL_2, 0); NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg); NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830); NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) { + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) { NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850); reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900); @@ -714,23 +714,23 @@ nv_load_state_ext(struct drm_device *dev, int head, wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KELVIN) wr_cio_state(dev, head, regp, NV_CIO_CRE_47); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) wr_cio_state(dev, head, regp, 0x9f); wr_cio_state(dev, head, regp, NV_CIO_CRE_49); wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) nv_fix_nv40_hw_cursor(dev, head); wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB); wr_cio_state(dev, head, regp, NV_CIO_CRE_4B); @@ -738,14 +738,14 @@ nv_load_state_ext(struct drm_device *dev, int head, } /* NV11 and NV20 stop at 0x52. */ if (nv_gf4_disp_arch(dev)) { - if (drm->device.info.family < NV_DEVICE_INFO_V0_KELVIN) { + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_KELVIN) { /* Not waiting for vertical retrace before modifying CRE_53/CRE_54 causes lockups. */ - nvif_msec(&drm->device, 650, + nvif_msec(&drm->client.device, 650, if ( (nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 8)) break; ); - nvif_msec(&drm->device, 650, + nvif_msec(&drm->client.device, 650, if (!(nvif_rd32(device, NV_PRMCIO_INP0__COLOR) & 8)) break; ); @@ -771,7 +771,7 @@ static void nv_save_state_palette(struct drm_device *dev, int head, struct nv04_mode_state *state) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; int head_offset = head * NV_PRMDIO_SIZE, i; nvif_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, @@ -790,7 +790,7 @@ void nouveau_hw_load_state_palette(struct drm_device *dev, int head, struct nv04_mode_state *state) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; int head_offset = head * NV_PRMDIO_SIZE, i; nvif_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset, @@ -810,7 +810,7 @@ void nouveau_hw_save_state(struct drm_device *dev, int head, { struct nouveau_drm *drm = nouveau_drm(dev); - if (drm->device.info.chipset == 0x11) + if (drm->client.device.info.chipset == 0x11) /* NB: no attempt is made to restore the bad pll later on */ nouveau_hw_fix_bad_vpll(dev, head); nv_save_state_ramdac(dev, head, state); diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.h b/drivers/gpu/drm/nouveau/dispnv04/hw.h index 3bded60c5596..3a2be47fb4f1 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/hw.h +++ b/drivers/gpu/drm/nouveau/dispnv04/hw.h @@ -60,7 +60,7 @@ extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp, static inline uint32_t NVReadCRTC(struct drm_device *dev, int head, uint32_t reg) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; uint32_t val; if (head) reg += NV_PCRTC0_SIZE; @@ -71,7 +71,7 @@ static inline uint32_t NVReadCRTC(struct drm_device *dev, static inline void NVWriteCRTC(struct drm_device *dev, int head, uint32_t reg, uint32_t val) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; if (head) reg += NV_PCRTC0_SIZE; nvif_wr32(device, reg, val); @@ -80,7 +80,7 @@ static inline void NVWriteCRTC(struct drm_device *dev, static inline uint32_t NVReadRAMDAC(struct drm_device *dev, int head, uint32_t reg) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; uint32_t val; if (head) reg += NV_PRAMDAC0_SIZE; @@ -91,7 +91,7 @@ static inline uint32_t NVReadRAMDAC(struct drm_device *dev, static inline void NVWriteRAMDAC(struct drm_device *dev, int head, uint32_t reg, uint32_t val) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; if (head) reg += NV_PRAMDAC0_SIZE; nvif_wr32(device, reg, val); @@ -120,7 +120,7 @@ static inline void nv_write_tmds(struct drm_device *dev, static inline void NVWriteVgaCrtc(struct drm_device *dev, int head, uint8_t index, uint8_t value) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); nvif_wr08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value); } @@ -128,7 +128,7 @@ static inline void NVWriteVgaCrtc(struct drm_device *dev, static inline uint8_t NVReadVgaCrtc(struct drm_device *dev, int head, uint8_t index) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; uint8_t val; nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); val = nvif_rd08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE); @@ -165,13 +165,13 @@ static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_ static inline uint8_t NVReadPRMVIO(struct drm_device *dev, int head, uint32_t reg) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; struct nouveau_drm *drm = nouveau_drm(dev); uint8_t val; /* Only NV4x have two pvio ranges; other twoHeads cards MUST call * NVSetOwner for the relevant head to be programmed */ - if (head && drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (head && drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) reg += NV_PRMVIO_SIZE; val = nvif_rd08(device, reg); @@ -181,12 +181,12 @@ static inline uint8_t NVReadPRMVIO(struct drm_device *dev, static inline void NVWritePRMVIO(struct drm_device *dev, int head, uint32_t reg, uint8_t value) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; struct nouveau_drm *drm = nouveau_drm(dev); /* Only NV4x have two pvio ranges; other twoHeads cards MUST call * NVSetOwner for the relevant head to be programmed */ - if (head && drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (head && drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) reg += NV_PRMVIO_SIZE; nvif_wr08(device, reg, value); @@ -194,14 +194,14 @@ static inline void NVWritePRMVIO(struct drm_device *dev, static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20); } static inline bool NVGetEnablePalette(struct drm_device *dev, int head) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); return !(nvif_rd08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20); } @@ -209,7 +209,7 @@ static inline bool NVGetEnablePalette(struct drm_device *dev, int head) static inline void NVWriteVgaAttr(struct drm_device *dev, int head, uint8_t index, uint8_t value) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; if (NVGetEnablePalette(dev, head)) index &= ~0x20; else @@ -223,7 +223,7 @@ static inline void NVWriteVgaAttr(struct drm_device *dev, static inline uint8_t NVReadVgaAttr(struct drm_device *dev, int head, uint8_t index) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; uint8_t val; if (NVGetEnablePalette(dev, head)) index &= ~0x20; @@ -259,10 +259,10 @@ static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect) static inline bool nv_heads_tied(struct drm_device *dev) { - struct nvif_object *device = &nouveau_drm(dev)->device.object; + struct nvif_object *device = &nouveau_drm(dev)->client.device.object; struct nouveau_drm *drm = nouveau_drm(dev); - if (drm->device.info.chipset == 0x11) + if (drm->client.device.info.chipset == 0x11) return !!(nvif_rd32(device, NV_PBUS_DEBUG_1) & (1 << 28)); return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4; @@ -318,7 +318,7 @@ NVLockVgaCrtcs(struct drm_device *dev, bool lock) NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX, lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE); /* NV11 has independently lockable extended crtcs, except when tied */ - if (drm->device.info.chipset == 0x11 && !nv_heads_tied(dev)) + if (drm->client.device.info.chipset == 0x11 && !nv_heads_tied(dev)) NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX, lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE); @@ -335,7 +335,7 @@ static inline int nv_cursor_width(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - return drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE; + return drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE; } static inline void @@ -357,7 +357,7 @@ nv_set_crtc_base(struct drm_device *dev, int head, uint32_t offset) NVWriteCRTC(dev, head, NV_PCRTC_START, offset); - if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT) { + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_TNT) { /* * Hilarious, the 24th bit doesn't want to stick to * PCRTC_START... @@ -382,7 +382,7 @@ nv_show_cursor(struct drm_device *dev, int head, bool show) *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1); - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) nv_fix_nv40_hw_cursor(dev, head); } @@ -398,7 +398,7 @@ nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp) bpp = 8; /* Alignment requirements taken from the Haiku driver */ - if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_TNT) mask = 128 / bpp - 1; else mask = 512 / bpp - 1; diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index a79514d440b3..5319f2a7f24d 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -97,7 +97,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_w, uint32_t src_h) { struct nouveau_drm *drm = nouveau_drm(plane->dev); - struct nvif_object *dev = &drm->device.object; + struct nvif_object *dev = &drm->client.device.object; struct nouveau_plane *nv_plane = container_of(plane, struct nouveau_plane, base); struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); @@ -119,7 +119,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (format > 0xffff) return -ERANGE; - if (drm->device.info.chipset >= 0x30) { + if (drm->client.device.info.chipset >= 0x30) { if (crtc_w < (src_w >> 1) || crtc_h < (src_h >> 1)) return -ERANGE; } else { @@ -145,16 +145,16 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, nvif_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x); nvif_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w); - if (fb->pixel_format != DRM_FORMAT_UYVY) + if (fb->format->format != DRM_FORMAT_UYVY) format |= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8; - if (fb->pixel_format == DRM_FORMAT_NV12) + if (fb->format->format == DRM_FORMAT_NV12) format |= NV_PVIDEO_FORMAT_PLANAR; if (nv_plane->iturbt_709) format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709; if (nv_plane->colorkey & (1 << 24)) format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY; - if (fb->pixel_format == DRM_FORMAT_NV12) { + if (fb->format->format == DRM_FORMAT_NV12) { nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0); nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip), nv_fb->nvbo->bo.offset + fb->offsets[1]); @@ -174,7 +174,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, static int nv10_disable_plane(struct drm_plane *plane) { - struct nvif_object *dev = &nouveau_drm(plane->dev)->device.object; + struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; struct nouveau_plane *nv_plane = container_of(plane, struct nouveau_plane, base); @@ -198,7 +198,7 @@ nv_destroy_plane(struct drm_plane *plane) static void nv10_set_params(struct nouveau_plane *plane) { - struct nvif_object *dev = &nouveau_drm(plane->base.dev)->device.object; + struct nvif_object *dev = &nouveau_drm(plane->base.dev)->client.device.object; u32 luma = (plane->brightness - 512) << 16 | plane->contrast; u32 chroma = ((sin_mul(plane->hue, plane->saturation) & 0xffff) << 16) | (cos_mul(plane->hue, plane->saturation) & 0xffff); @@ -268,7 +268,7 @@ nv10_overlay_init(struct drm_device *device) if (!plane) return; - switch (drm->device.info.chipset) { + switch (drm->client.device.info.chipset) { case 0x10: case 0x11: case 0x15: @@ -347,7 +347,7 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { - struct nvif_object *dev = &nouveau_drm(plane->dev)->device.object; + struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; struct nouveau_plane *nv_plane = container_of(plane, struct nouveau_plane, base); struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); @@ -411,7 +411,7 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (nv_plane->colorkey & (1 << 24)) overlay |= 0x10; - if (fb->pixel_format == DRM_FORMAT_YUYV) + if (fb->format->format == DRM_FORMAT_YUYV) overlay |= 0x100; nvif_wr32(dev, NV_PVIDEO_OVERLAY, overlay); @@ -427,7 +427,7 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, static int nv04_disable_plane(struct drm_plane *plane) { - struct nvif_object *dev = &nouveau_drm(plane->dev)->device.object; + struct nvif_object *dev = &nouveau_drm(plane->dev)->client.device.object; struct nouveau_plane *nv_plane = container_of(plane, struct nouveau_plane, base); @@ -495,7 +495,7 @@ err: void nouveau_overlay_init(struct drm_device *device) { - struct nvif_device *dev = &nouveau_drm(device)->device; + struct nvif_device *dev = &nouveau_drm(device)->client.device; if (dev->info.chipset < 0x10) nv04_overlay_init(device); else if (dev->info.chipset <= 0x40) diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c index 477a8d072af4..01664357d3e1 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c @@ -54,7 +54,7 @@ static struct nvkm_i2c_bus_probe nv04_tv_encoder_info[] = { int nv04_tv_identify(struct drm_device *dev, int i2c_index) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus = nvkm_i2c_bus_find(i2c, i2c_index); if (bus) { return nvkm_i2c_bus_probe(bus, "TV encoder", @@ -206,7 +206,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) struct drm_encoder *encoder; struct drm_device *dev = connector->dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus = nvkm_i2c_bus_find(i2c, entry->i2c_index); int type, ret; diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c index 434d1e29f279..6d99f11fee4e 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c @@ -46,7 +46,7 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); uint32_t testval, regoffset = nv04_dac_output_offset(encoder); uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end, fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c; @@ -130,7 +130,7 @@ static bool get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); if (device->quirk && device->quirk->tv_pin_mask) { *pin_mask = device->quirk->tv_pin_mask; @@ -154,8 +154,8 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) return connector_status_disconnected; if (reliable) { - if (drm->device.info.chipset == 0x42 || - drm->device.info.chipset == 0x43) + if (drm->client.device.info.chipset == 0x42 || + drm->client.device.info.chipset == 0x43) tv_enc->pin_mask = nv42_tv_sample_load(encoder) >> 28 & 0xe; else @@ -362,7 +362,7 @@ static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); @@ -435,7 +435,7 @@ static void nv17_tv_prepare(struct drm_encoder *encoder) /* Set the DACCLK register */ dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1; - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) dacclk |= 0x1a << 16; if (tv_norm->kind == CTV_ENC_MODE) { @@ -492,7 +492,7 @@ static void nv17_tv_mode_set(struct drm_encoder *encoder, tv_regs->ptv_614 = 0x13; } - if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_RANKINE) { tv_regs->ptv_500 = 0xe8e0; tv_regs->ptv_504 = 0x1710; tv_regs->ptv_604 = 0x0; @@ -587,7 +587,7 @@ static void nv17_tv_commit(struct drm_encoder *encoder) nv17_tv_state_load(dev, &to_tv_enc(encoder)->state); /* This could use refinement for flatpanels, but it should work */ - if (drm->device.info.chipset < 0x44) + if (drm->client.device.info.chipset < 0x44) NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h index 1b07521cde0d..29773b325bd9 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h @@ -130,13 +130,13 @@ void nv17_ctv_update_rescaler(struct drm_encoder *encoder); static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t val) { - struct nvif_device *device = &nouveau_drm(dev)->device; + struct nvif_device *device = &nouveau_drm(dev)->client.device; nvif_wr32(&device->object, reg, val); } static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg) { - struct nvif_device *device = &nouveau_drm(dev)->device; + struct nvif_device *device = &nouveau_drm(dev)->client.device; return nvif_rd32(&device->object, reg); } diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl826e.h b/drivers/gpu/drm/nouveau/include/nvif/cl826e.h index 05e6ef7cd190..91e33db21a2f 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl826e.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl826e.h @@ -10,5 +10,5 @@ struct g82_channel_dma_v0 { __u64 offset; }; -#define G82_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 +#define NV826E_V0_NTFY_NON_STALL_INTERRUPT 0x00 #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl826f.h b/drivers/gpu/drm/nouveau/include/nvif/cl826f.h index cecafcb1e954..e34efd4ec537 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl826f.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl826f.h @@ -11,5 +11,5 @@ struct g82_channel_gpfifo_v0 { __u64 vm; }; -#define G82_CHANNEL_GPFIFO_V0_NTFY_UEVENT 0x00 +#define NV826F_V0_NTFY_NON_STALL_INTERRUPT 0x00 #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl906f.h b/drivers/gpu/drm/nouveau/include/nvif/cl906f.h index 2caf0838fcfd..a2d5410a491b 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl906f.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl906f.h @@ -10,5 +10,6 @@ struct fermi_channel_gpfifo_v0 { __u64 vm; }; -#define FERMI_CHANNEL_GPFIFO_V0_NTFY_UEVENT 0x00 +#define NV906F_V0_NTFY_NON_STALL_INTERRUPT 0x00 +#define NV906F_V0_NTFY_KILLED 0x01 #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/cla06f.h b/drivers/gpu/drm/nouveau/include/nvif/cla06f.h index 46301ec018ce..2efa3d048bb9 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cla06f.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cla06f.h @@ -25,5 +25,6 @@ struct kepler_channel_gpfifo_a_v0 { __u64 vm; }; -#define NVA06F_V0_NTFY_UEVENT 0x00 +#define NVA06F_V0_NTFY_NON_STALL_INTERRUPT 0x00 +#define NVA06F_V0_NTFY_KILLED 0x01 #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/class.h b/drivers/gpu/drm/nouveau/include/nvif/class.h index 82235f30277c..3a2c0137d4b4 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/class.h +++ b/drivers/gpu/drm/nouveau/include/nvif/class.h @@ -2,23 +2,31 @@ #define __NVIF_CLASS_H__ /* these class numbers are made up by us, and not nvidia-assigned */ -#define NVIF_CLASS_CONTROL /* if0001.h */ -1 -#define NVIF_CLASS_PERFMON /* if0002.h */ -2 -#define NVIF_CLASS_PERFDOM /* if0003.h */ -3 -#define NVIF_CLASS_SW_NV04 /* if0004.h */ -4 -#define NVIF_CLASS_SW_NV10 /* if0005.h */ -5 -#define NVIF_CLASS_SW_NV50 /* if0005.h */ -6 -#define NVIF_CLASS_SW_GF100 /* if0005.h */ -7 +#define NVIF_CLASS_CLIENT /* if0000.h */ -0x00000000 + +#define NVIF_CLASS_CONTROL /* if0001.h */ -0x00000001 + +#define NVIF_CLASS_PERFMON /* if0002.h */ -0x00000002 +#define NVIF_CLASS_PERFDOM /* if0003.h */ -0x00000003 + +#define NVIF_CLASS_SW_NV04 /* if0004.h */ -0x00000004 +#define NVIF_CLASS_SW_NV10 /* if0005.h */ -0x00000005 +#define NVIF_CLASS_SW_NV50 /* if0005.h */ -0x00000006 +#define NVIF_CLASS_SW_GF100 /* if0005.h */ -0x00000007 /* the below match nvidia-assigned (either in hw, or sw) class numbers */ +#define NV_NULL_CLASS 0x00000030 + #define NV_DEVICE /* cl0080.h */ 0x00000080 #define NV_DMA_FROM_MEMORY /* cl0002.h */ 0x00000002 #define NV_DMA_TO_MEMORY /* cl0002.h */ 0x00000003 #define NV_DMA_IN_MEMORY /* cl0002.h */ 0x0000003d +#define NV50_TWOD 0x0000502d #define FERMI_TWOD_A 0x0000902d +#define NV50_MEMORY_TO_MEMORY_FORMAT 0x00005039 #define FERMI_MEMORY_TO_MEMORY_FORMAT_A 0x00009039 #define KEPLER_INLINE_TO_MEMORY_A 0x0000a040 @@ -99,6 +107,12 @@ #define GF110_DISP_OVERLAY_CONTROL_DMA /* cl507e.h */ 0x0000907e #define GK104_DISP_OVERLAY_CONTROL_DMA /* cl507e.h */ 0x0000917e +#define NV50_TESLA 0x00005097 +#define G82_TESLA 0x00008297 +#define GT200_TESLA 0x00008397 +#define GT214_TESLA 0x00008597 +#define GT21A_TESLA 0x00008697 + #define FERMI_A /* cl9097.h */ 0x00009097 #define FERMI_B /* cl9097.h */ 0x00009197 #define FERMI_C /* cl9097.h */ 0x00009297 @@ -140,6 +154,8 @@ #define FERMI_DECOMPRESS 0x000090b8 +#define NV50_COMPUTE 0x000050c0 +#define GT214_COMPUTE 0x000085c0 #define FERMI_COMPUTE_A 0x000090c0 #define FERMI_COMPUTE_B 0x000091c0 #define KEPLER_COMPUTE_A 0x0000a0c0 diff --git a/drivers/gpu/drm/nouveau/include/nvif/client.h b/drivers/gpu/drm/nouveau/include/nvif/client.h index 4a7f6f7b836d..b52a8eadce01 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/client.h +++ b/drivers/gpu/drm/nouveau/include/nvif/client.h @@ -11,8 +11,7 @@ struct nvif_client { bool super; }; -int nvif_client_init(const char *drv, const char *name, u64 device, - const char *cfg, const char *dbg, +int nvif_client_init(struct nvif_client *parent, const char *name, u64 device, struct nvif_client *); void nvif_client_fini(struct nvif_client *); int nvif_client_ioctl(struct nvif_client *, void *, u32); diff --git a/drivers/gpu/drm/nouveau/include/nvif/driver.h b/drivers/gpu/drm/nouveau/include/nvif/driver.h index 8bd39e69229c..0c6f48d8140a 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/driver.h +++ b/drivers/gpu/drm/nouveau/include/nvif/driver.h @@ -1,5 +1,7 @@ #ifndef __NVIF_DRIVER_H__ #define __NVIF_DRIVER_H__ +#include <nvif/os.h> +struct nvif_client; struct nvif_driver { const char *name; @@ -14,9 +16,11 @@ struct nvif_driver { bool keep; }; +int nvif_driver_init(const char *drv, const char *cfg, const char *dbg, + const char *name, u64 device, struct nvif_client *); + extern const struct nvif_driver nvif_driver_nvkm; extern const struct nvif_driver nvif_driver_drm; extern const struct nvif_driver nvif_driver_lib; extern const struct nvif_driver nvif_driver_null; - #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0000.h b/drivers/gpu/drm/nouveau/include/nvif/if0000.h index 85c44e8a1201..c2c0fc41e017 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/if0000.h +++ b/drivers/gpu/drm/nouveau/include/nvif/if0000.h @@ -1,9 +1,16 @@ #ifndef __NVIF_IF0000_H__ #define __NVIF_IF0000_H__ -#define NV_CLIENT_DEVLIST 0x00 +struct nvif_client_v0 { + __u8 version; + __u8 pad01[7]; + __u64 device; + char name[32]; +}; + +#define NVIF_CLIENT_V0_DEVLIST 0x00 -struct nv_client_devlist_v0 { +struct nvif_client_devlist_v0 { __u8 version; __u8 count; __u8 pad02[6]; diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/client.h b/drivers/gpu/drm/nouveau/include/nvkm/core/client.h index eaf5905a87a3..e876634da10a 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/client.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/client.h @@ -1,5 +1,6 @@ #ifndef __NVKM_CLIENT_H__ #define __NVKM_CLIENT_H__ +#define nvkm_client(p) container_of((p), struct nvkm_client, object) #include <core/object.h> struct nvkm_client { @@ -8,9 +9,8 @@ struct nvkm_client { u64 device; u32 debug; - struct nvkm_client_notify *notify[16]; + struct nvkm_client_notify *notify[32]; struct rb_root objroot; - struct rb_root dmaroot; bool super; void *data; @@ -19,15 +19,11 @@ struct nvkm_client { struct nvkm_vm *vm; }; -bool nvkm_client_insert(struct nvkm_client *, struct nvkm_object *); -void nvkm_client_remove(struct nvkm_client *, struct nvkm_object *); -struct nvkm_object *nvkm_client_search(struct nvkm_client *, u64 object); - int nvkm_client_new(const char *name, u64 device, const char *cfg, - const char *dbg, struct nvkm_client **); -void nvkm_client_del(struct nvkm_client **); -int nvkm_client_init(struct nvkm_client *); -int nvkm_client_fini(struct nvkm_client *, bool suspend); + const char *dbg, + int (*)(const void *, u32, const void *, u32), + struct nvkm_client **); +struct nvkm_client *nvkm_client_search(struct nvkm_client *, u64 handle); int nvkm_client_notify_new(struct nvkm_object *, struct nvkm_event *, void *data, u32 size); @@ -37,8 +33,8 @@ int nvkm_client_notify_put(struct nvkm_client *, int index); /* logging for client-facing objects */ #define nvif_printk(o,l,p,f,a...) do { \ - struct nvkm_object *_object = (o); \ - struct nvkm_client *_client = _object->client; \ + const struct nvkm_object *_object = (o); \ + const struct nvkm_client *_client = _object->client; \ if (_client->debug >= NV_DBG_##l) \ printk(KERN_##p "nouveau: %s:%08x:%08x: "f, _client->name, \ _object->handle, _object->oclass, ##a); \ diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/device.h b/drivers/gpu/drm/nouveau/include/nvkm/core/device.h index 6bc712f32c8b..d426b86e2712 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/device.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/device.h @@ -262,7 +262,7 @@ extern const struct nvkm_sclass nvkm_udevice_sclass; /* device logging */ #define nvdev_printk_(d,l,p,f,a...) do { \ - struct nvkm_device *_device = (d); \ + const struct nvkm_device *_device = (d); \ if (_device->debug >= (l)) \ dev_##p(_device->dev, f, ##a); \ } while(0) diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h b/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h index 9ebfd8782366..d4cd2fbfde88 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/engine.h @@ -20,6 +20,7 @@ struct nvkm_engine_func { int (*fini)(struct nvkm_engine *, bool suspend); void (*intr)(struct nvkm_engine *); void (*tile)(struct nvkm_engine *, int region, struct nvkm_fb_tile *); + bool (*chsw_load)(struct nvkm_engine *); struct { int (*sclass)(struct nvkm_oclass *, int index, @@ -44,4 +45,5 @@ int nvkm_engine_new_(const struct nvkm_engine_func *, struct nvkm_device *, struct nvkm_engine *nvkm_engine_ref(struct nvkm_engine *); void nvkm_engine_unref(struct nvkm_engine **); void nvkm_engine_tile(struct nvkm_engine *, int region); +bool nvkm_engine_chsw_load(struct nvkm_engine *); #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h b/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h index 9363b839a9da..33ca6769266a 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h @@ -6,9 +6,10 @@ struct nvkm_vma; struct nvkm_vm; enum nvkm_memory_target { - NVKM_MEM_TARGET_INST, - NVKM_MEM_TARGET_VRAM, - NVKM_MEM_TARGET_HOST, + NVKM_MEM_TARGET_INST, /* instance memory */ + NVKM_MEM_TARGET_VRAM, /* video memory */ + NVKM_MEM_TARGET_HOST, /* coherent system memory */ + NVKM_MEM_TARGET_NCOH, /* non-coherent system memory */ }; struct nvkm_memory { diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h b/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h index d92fd41e4056..7bd4897a8a2a 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/mm.h @@ -5,7 +5,7 @@ struct nvkm_mm_node { struct list_head nl_entry; struct list_head fl_entry; - struct list_head rl_entry; + struct nvkm_mm_node *next; #define NVKM_MM_HEAP_ANY 0x00 u8 heap; @@ -38,4 +38,10 @@ int nvkm_mm_tail(struct nvkm_mm *, u8 heap, u8 type, u32 size_max, u32 size_min, u32 align, struct nvkm_mm_node **); void nvkm_mm_free(struct nvkm_mm *, struct nvkm_mm_node **); void nvkm_mm_dump(struct nvkm_mm *, const char *); + +static inline bool +nvkm_mm_contiguous(struct nvkm_mm_node *node) +{ + return !node->next; +} #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/object.h b/drivers/gpu/drm/nouveau/include/nvkm/core/object.h index dcd048b91fac..96dda350ada3 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/object.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/object.h @@ -62,6 +62,11 @@ int nvkm_object_wr32(struct nvkm_object *, u64 addr, u32 data); int nvkm_object_bind(struct nvkm_object *, struct nvkm_gpuobj *, int align, struct nvkm_gpuobj **); +bool nvkm_object_insert(struct nvkm_object *); +void nvkm_object_remove(struct nvkm_object *); +struct nvkm_object *nvkm_object_search(struct nvkm_client *, u64 object, + const struct nvkm_object_func *); + struct nvkm_sclass { int minver; int maxver; diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h b/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h index 57adefa8b08e..ca9ed3d68f44 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/subdev.h @@ -32,7 +32,7 @@ void nvkm_subdev_intr(struct nvkm_subdev *); /* subdev logging */ #define nvkm_printk_(s,l,p,f,a...) do { \ - struct nvkm_subdev *_subdev = (s); \ + const struct nvkm_subdev *_subdev = (s); \ if (_subdev->debug >= (l)) { \ dev_##p(_subdev->device->dev, "%s: "f, \ nvkm_subdev_name[_subdev->index], ##a); \ diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/dma.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/dma.h index 114bfb737a81..d2a6532ce3b9 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/dma.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/dma.h @@ -12,9 +12,6 @@ struct nvkm_dmaobj { u32 access; u64 start; u64 limit; - - struct rb_node rb; - u64 handle; /*XXX HANDLE MERGE */ }; struct nvkm_dma { @@ -22,8 +19,7 @@ struct nvkm_dma { struct nvkm_engine engine; }; -struct nvkm_dmaobj * -nvkm_dma_search(struct nvkm_dma *, struct nvkm_client *, u64 object); +struct nvkm_dmaobj *nvkm_dmaobj_search(struct nvkm_client *, u64 object); int nv04_dma_new(struct nvkm_device *, int, struct nvkm_dma **); int nv50_dma_new(struct nvkm_device *, int, struct nvkm_dma **); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h index e6baf039c269..7e498e65b1e8 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h @@ -4,13 +4,26 @@ #include <core/engine.h> struct nvkm_fifo_chan; +enum nvkm_falcon_dmaidx { + FALCON_DMAIDX_UCODE = 0, + FALCON_DMAIDX_VIRT = 1, + FALCON_DMAIDX_PHYS_VID = 2, + FALCON_DMAIDX_PHYS_SYS_COH = 3, + FALCON_DMAIDX_PHYS_SYS_NCOH = 4, +}; + struct nvkm_falcon { const struct nvkm_falcon_func *func; - struct nvkm_engine engine; - + const struct nvkm_subdev *owner; + const char *name; u32 addr; - u8 version; - u8 secret; + + struct mutex mutex; + const struct nvkm_subdev *user; + + u8 version; + u8 secret; + bool debug; struct nvkm_memory *core; bool external; @@ -19,15 +32,25 @@ struct nvkm_falcon { u32 limit; u32 *data; u32 size; + u8 ports; } code; struct { u32 limit; u32 *data; u32 size; + u8 ports; } data; + + struct nvkm_engine engine; }; +int nvkm_falcon_v1_new(struct nvkm_subdev *owner, const char *name, u32 addr, + struct nvkm_falcon **); +void nvkm_falcon_del(struct nvkm_falcon **); +int nvkm_falcon_get(struct nvkm_falcon *, const struct nvkm_subdev *); +void nvkm_falcon_put(struct nvkm_falcon *, const struct nvkm_subdev *); + int nvkm_falcon_new_(const struct nvkm_falcon_func *, struct nvkm_device *, int index, bool enable, u32 addr, struct nvkm_engine **); @@ -42,6 +65,51 @@ struct nvkm_falcon_func { } data; void (*init)(struct nvkm_falcon *); void (*intr)(struct nvkm_falcon *, struct nvkm_fifo_chan *); + void (*load_imem)(struct nvkm_falcon *, void *, u32, u32, u16, u8, bool); + void (*load_dmem)(struct nvkm_falcon *, void *, u32, u32, u8); + void (*read_dmem)(struct nvkm_falcon *, u32, u32, u8, void *); + void (*bind_context)(struct nvkm_falcon *, struct nvkm_gpuobj *); + int (*wait_for_halt)(struct nvkm_falcon *, u32); + int (*clear_interrupt)(struct nvkm_falcon *, u32); + void (*set_start_addr)(struct nvkm_falcon *, u32 start_addr); + void (*start)(struct nvkm_falcon *); + int (*enable)(struct nvkm_falcon *falcon); + void (*disable)(struct nvkm_falcon *falcon); + struct nvkm_sclass sclass[]; }; + +static inline u32 +nvkm_falcon_rd32(struct nvkm_falcon *falcon, u32 addr) +{ + return nvkm_rd32(falcon->owner->device, falcon->addr + addr); +} + +static inline void +nvkm_falcon_wr32(struct nvkm_falcon *falcon, u32 addr, u32 data) +{ + nvkm_wr32(falcon->owner->device, falcon->addr + addr, data); +} + +static inline u32 +nvkm_falcon_mask(struct nvkm_falcon *falcon, u32 addr, u32 mask, u32 val) +{ + struct nvkm_device *device = falcon->owner->device; + + return nvkm_mask(device, falcon->addr + addr, mask, val); +} + +void nvkm_falcon_load_imem(struct nvkm_falcon *, void *, u32, u32, u16, u8, + bool); +void nvkm_falcon_load_dmem(struct nvkm_falcon *, void *, u32, u32, u8); +void nvkm_falcon_read_dmem(struct nvkm_falcon *, u32, u32, u8, void *); +void nvkm_falcon_bind_context(struct nvkm_falcon *, struct nvkm_gpuobj *); +void nvkm_falcon_set_start_addr(struct nvkm_falcon *, u32); +void nvkm_falcon_start(struct nvkm_falcon *); +int nvkm_falcon_wait_for_halt(struct nvkm_falcon *, u32); +int nvkm_falcon_clear_interrupt(struct nvkm_falcon *, u32); +int nvkm_falcon_enable(struct nvkm_falcon *); +void nvkm_falcon_disable(struct nvkm_falcon *); +int nvkm_falcon_reset(struct nvkm_falcon *); + #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h index ed92fec5292c..24efa900d8ca 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/fifo.h @@ -40,6 +40,7 @@ struct nvkm_fifo { struct nvkm_event uevent; /* async user trigger */ struct nvkm_event cevent; /* channel creation event */ + struct nvkm_event kevent; /* channel killed */ }; void nvkm_fifo_pause(struct nvkm_fifo *, unsigned long *); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/power_budget.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/power_budget.h new file mode 100644 index 000000000000..f5f4a14c4030 --- /dev/null +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/power_budget.h @@ -0,0 +1,26 @@ +#ifndef __NVBIOS_POWER_BUDGET_H__ +#define __NVBIOS_POWER_BUDGET_H__ + +#include <nvkm/subdev/bios.h> + +struct nvbios_power_budget_entry { + u32 min_w; + u32 avg_w; + u32 max_w; +}; + +struct nvbios_power_budget { + u32 offset; + u8 ver; + u8 hlen; + u8 elen; + u8 ecount; + u8 cap_entry; +}; + +int nvbios_power_budget_header(struct nvkm_bios *, + struct nvbios_power_budget *); +int nvbios_power_budget_entry(struct nvkm_bios *, struct nvbios_power_budget *, + u8 idx, struct nvbios_power_budget_entry *); + +#endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h index 794e432578b2..0b26a4c860ec 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h @@ -29,7 +29,7 @@ struct nvkm_mem { u8 page_shift; struct nvkm_mm_node *tag; - struct list_head regions; + struct nvkm_mm_node *mem; dma_addr_t *pages; u32 memtype; u64 offset; diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h index 3c2ddd975273..b7a9b041e130 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/iccsense.h @@ -8,6 +8,9 @@ struct nvkm_iccsense { bool data_valid; struct list_head sensors; struct list_head rails; + + u32 power_w_max; + u32 power_w_crit; }; int gf100_iccsense_new(struct nvkm_device *, int index, struct nvkm_iccsense **); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h index 27d25b18d85c..e68ba636741b 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h @@ -9,6 +9,7 @@ struct nvkm_mc { void nvkm_mc_enable(struct nvkm_device *, enum nvkm_devidx); void nvkm_mc_disable(struct nvkm_device *, enum nvkm_devidx); +bool nvkm_mc_enabled(struct nvkm_device *, enum nvkm_devidx); void nvkm_mc_reset(struct nvkm_device *, enum nvkm_devidx); void nvkm_mc_intr(struct nvkm_device *, bool *handled); void nvkm_mc_intr_unarm(struct nvkm_device *); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h index e6523e2cea9f..ac2a695963c1 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h @@ -43,6 +43,7 @@ int nv40_pci_new(struct nvkm_device *, int, struct nvkm_pci **); int nv46_pci_new(struct nvkm_device *, int, struct nvkm_pci **); int nv4c_pci_new(struct nvkm_device *, int, struct nvkm_pci **); int g84_pci_new(struct nvkm_device *, int, struct nvkm_pci **); +int g92_pci_new(struct nvkm_device *, int, struct nvkm_pci **); int g94_pci_new(struct nvkm_device *, int, struct nvkm_pci **); int gf100_pci_new(struct nvkm_device *, int, struct nvkm_pci **); int gf106_pci_new(struct nvkm_device *, int, struct nvkm_pci **); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h index f37538eb1fe5..179b6ed3f595 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h @@ -1,10 +1,12 @@ #ifndef __NVKM_PMU_H__ #define __NVKM_PMU_H__ #include <core/subdev.h> +#include <engine/falcon.h> struct nvkm_pmu { const struct nvkm_pmu_func *func; struct nvkm_subdev subdev; + struct nvkm_falcon *falcon; struct { u32 base; @@ -35,6 +37,7 @@ int gk110_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **); int gk208_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **); int gk20a_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **); int gm107_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **); +int gm20b_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **); int gp100_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **); int gp102_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/secboot.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/secboot.h index b04c38c07761..5dbd8aa4f8c2 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/secboot.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/secboot.h @@ -26,7 +26,7 @@ #include <core/subdev.h> enum nvkm_secboot_falcon { - NVKM_SECBOOT_FALCON_PMU = 0, + NVKM_SECBOOT_FALCON_PMU = 0, NVKM_SECBOOT_FALCON_RESERVED = 1, NVKM_SECBOOT_FALCON_FECS = 2, NVKM_SECBOOT_FALCON_GPCCS = 3, @@ -35,22 +35,23 @@ enum nvkm_secboot_falcon { }; /** - * @base: base IO address of the falcon performing secure boot - * @irq_mask: IRQ mask of the falcon performing secure boot - * @enable_mask: enable mask of the falcon performing secure boot + * @wpr_set: whether the WPR region is currently set */ struct nvkm_secboot { const struct nvkm_secboot_func *func; + struct nvkm_acr *acr; struct nvkm_subdev subdev; + struct nvkm_falcon *boot_falcon; - enum nvkm_devidx devidx; - u32 base; + u64 wpr_addr; + u32 wpr_size; + + bool wpr_set; }; #define nvkm_secboot(p) container_of((p), struct nvkm_secboot, subdev) bool nvkm_secboot_is_managed(struct nvkm_secboot *, enum nvkm_secboot_falcon); -int nvkm_secboot_reset(struct nvkm_secboot *, u32 falcon); -int nvkm_secboot_start(struct nvkm_secboot *, u32 falcon); +int nvkm_secboot_reset(struct nvkm_secboot *, enum nvkm_secboot_falcon); int gm200_secboot_new(struct nvkm_device *, int, struct nvkm_secboot **); int gm20b_secboot_new(struct nvkm_device *, int, struct nvkm_secboot **); diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h index 82d3e28918fd..6a567fe347b3 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h @@ -48,10 +48,8 @@ void nvkm_timer_alarm_cancel(struct nvkm_timer *, struct nvkm_alarm *); } while (_taken = nvkm_timer_read(_tmr) - _time0, _taken < _nsecs); \ \ if (_taken >= _nsecs) { \ - if (_warn) { \ - dev_warn(_device->dev, "timeout at %s:%d/%s()!\n", \ - __FILE__, __LINE__, __func__); \ - } \ + if (_warn) \ + dev_WARN(_device->dev, "timeout\n"); \ _taken = -ETIMEDOUT; \ } \ _taken; \ diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/top.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/top.h index 71ebbfd4484f..d23209b62c25 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/top.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/top.h @@ -11,6 +11,7 @@ struct nvkm_top { u32 nvkm_top_reset(struct nvkm_device *, enum nvkm_devidx); u32 nvkm_top_intr(struct nvkm_device *, u32 intr, u64 *subdevs); u32 nvkm_top_intr_mask(struct nvkm_device *, enum nvkm_devidx); +int nvkm_top_fault_id(struct nvkm_device *, enum nvkm_devidx); enum nvkm_devidx nvkm_top_fault(struct nvkm_device *, int fault); enum nvkm_devidx nvkm_top_engine(struct nvkm_device *, int, int *runl, int *engn); diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 7bd4683216d0..f98f800cc011 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -87,7 +87,7 @@ nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret) s32 nouveau_abi16_swclass(struct nouveau_drm *drm) { - switch (drm->device.info.family) { + switch (drm->client.device.info.family) { case NV_DEVICE_INFO_V0_TNT: return NVIF_CLASS_SW_NV04; case NV_DEVICE_INFO_V0_CELSIUS: @@ -175,7 +175,7 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) { struct nouveau_cli *cli = nouveau_cli(file_priv); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; struct nvkm_gr *gr = nvxx_gr(device); struct drm_nouveau_getparam *getparam = data; @@ -199,7 +199,7 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) if (!nvxx_device(device)->func->pci) getparam->value = 3; else - if (drm_pci_device_is_agp(dev)) + if (pci_find_capability(dev->pdev, PCI_CAP_ID_AGP)) getparam->value = 0; else if (!pci_is_pcie(dev->pdev)) @@ -321,7 +321,7 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) } /* Named memory object area */ - ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART, + ret = nouveau_gem_new(cli, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART, 0, 0, &chan->ntfy); if (ret == 0) ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false); diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 8b1ca4add2ed..380f340204e8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -65,7 +65,7 @@ static int nv40_get_intensity(struct backlight_device *bd) { struct nouveau_drm *drm = bl_get_data(bd); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) >> 16; @@ -76,7 +76,7 @@ static int nv40_set_intensity(struct backlight_device *bd) { struct nouveau_drm *drm = bl_get_data(bd); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; int val = bd->props.brightness; int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT); @@ -96,7 +96,7 @@ static int nv40_backlight_init(struct drm_connector *connector) { struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; struct backlight_properties props; struct backlight_device *bd; struct backlight_connector bl_connector; @@ -133,7 +133,7 @@ nv50_get_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; int or = nv_encoder->or; u32 div = 1025; u32 val; @@ -148,7 +148,7 @@ nv50_set_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; int or = nv_encoder->or; u32 div = 1025; u32 val = (bd->props.brightness * div) / 100; @@ -169,7 +169,7 @@ nva3_get_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; int or = nv_encoder->or; u32 div, val; @@ -187,7 +187,7 @@ nva3_set_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; int or = nv_encoder->or; u32 div, val; @@ -213,7 +213,7 @@ static int nv50_backlight_init(struct drm_connector *connector) { struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; struct nouveau_encoder *nv_encoder; struct backlight_properties props; struct backlight_device *bd; @@ -231,9 +231,9 @@ nv50_backlight_init(struct drm_connector *connector) if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or))) return 0; - if (drm->device.info.chipset <= 0xa0 || - drm->device.info.chipset == 0xaa || - drm->device.info.chipset == 0xac) + if (drm->client.device.info.chipset <= 0xa0 || + drm->client.device.info.chipset == 0xaa || + drm->client.device.info.chipset == 0xac) ops = &nv50_bl_ops; else ops = &nva3_bl_ops; @@ -265,7 +265,7 @@ int nouveau_backlight_init(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; struct drm_connector *connector; if (apple_gmux_present()) { diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 23ffe8571a99..9a0772ad495a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -215,7 +215,7 @@ int call_lvds_script(struct drm_device *dev, struct dcb_output *dcbent, int head */ struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; struct nvbios *bios = &drm->vbios; uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer]; uint32_t sel_clk_binding, sel_clk; @@ -319,7 +319,7 @@ static int get_fp_strap(struct drm_device *dev, struct nvbios *bios) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; /* * The fp strap is normally dictated by the "User Strap" in @@ -333,10 +333,10 @@ get_fp_strap(struct drm_device *dev, struct nvbios *bios) if (bios->major_version < 5 && bios->data[0x48] & 0x4) return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_MAXWELL) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_MAXWELL) return nvif_rd32(device, 0x001800) & 0x0000000f; else - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf; else return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 16) & 0xf; @@ -638,7 +638,7 @@ int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head, */ struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; struct nvbios *bios = &drm->vbios; int cv = bios->chip_version; uint16_t clktable = 0, scriptptr; @@ -1255,7 +1255,7 @@ olddcb_table(struct drm_device *dev) struct nouveau_drm *drm = nouveau_drm(dev); u8 *dcb = NULL; - if (drm->device.info.family > NV_DEVICE_INFO_V0_TNT) + if (drm->client.device.info.family > NV_DEVICE_INFO_V0_TNT) dcb = ROMPTR(dev, drm->vbios.data[0x36]); if (!dcb) { NV_WARN(drm, "No DCB data found in VBIOS\n"); @@ -1918,7 +1918,7 @@ static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bio */ struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; uint8_t bytes_to_write; uint16_t hwsq_entry_offset; int i; @@ -2012,7 +2012,7 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev) static bool NVInitVBIOS(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_bios *bios = nvxx_bios(&drm->device); + struct nvkm_bios *bios = nvxx_bios(&drm->client.device); struct nvbios *legacy = &drm->vbios; memset(legacy, 0, sizeof(struct nvbios)); @@ -2064,7 +2064,7 @@ nouveau_bios_posted(struct drm_device *dev) struct nouveau_drm *drm = nouveau_drm(dev); unsigned htotal; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) return true; htotal = NVReadVgaCrtc(dev, 0, 0x06); diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index dd07ca140d12..548f36d33924 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -48,7 +48,7 @@ nv10_bo_update_tile_region(struct drm_device *dev, struct nouveau_drm_tile *reg, { struct nouveau_drm *drm = nouveau_drm(dev); int i = reg - drm->tile.reg; - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); struct nvkm_fb *fb = device->fb; struct nvkm_fb_tile *tile = &fb->tile.region[i]; @@ -100,7 +100,7 @@ nv10_bo_set_tiling(struct drm_device *dev, u32 addr, u32 size, u32 pitch, u32 flags) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_fb *fb = nvxx_fb(&drm->device); + struct nvkm_fb *fb = nvxx_fb(&drm->client.device); struct nouveau_drm_tile *tile, *found = NULL; int i; @@ -139,60 +139,62 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo) kfree(nvbo); } +static inline u64 +roundup_64(u64 x, u32 y) +{ + x += y - 1; + do_div(x, y); + return x * y; +} + static void nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags, - int *align, int *size) + int *align, u64 *size) { struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; if (device->info.family < NV_DEVICE_INFO_V0_TESLA) { if (nvbo->tile_mode) { if (device->info.chipset >= 0x40) { *align = 65536; - *size = roundup(*size, 64 * nvbo->tile_mode); + *size = roundup_64(*size, 64 * nvbo->tile_mode); } else if (device->info.chipset >= 0x30) { *align = 32768; - *size = roundup(*size, 64 * nvbo->tile_mode); + *size = roundup_64(*size, 64 * nvbo->tile_mode); } else if (device->info.chipset >= 0x20) { *align = 16384; - *size = roundup(*size, 64 * nvbo->tile_mode); + *size = roundup_64(*size, 64 * nvbo->tile_mode); } else if (device->info.chipset >= 0x10) { *align = 16384; - *size = roundup(*size, 32 * nvbo->tile_mode); + *size = roundup_64(*size, 32 * nvbo->tile_mode); } } } else { - *size = roundup(*size, (1 << nvbo->page_shift)); + *size = roundup_64(*size, (1 << nvbo->page_shift)); *align = max((1 << nvbo->page_shift), *align); } - *size = roundup(*size, PAGE_SIZE); + *size = roundup_64(*size, PAGE_SIZE); } int -nouveau_bo_new(struct drm_device *dev, int size, int align, +nouveau_bo_new(struct nouveau_cli *cli, u64 size, int align, uint32_t flags, uint32_t tile_mode, uint32_t tile_flags, struct sg_table *sg, struct reservation_object *robj, struct nouveau_bo **pnvbo) { - struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_drm *drm = nouveau_drm(cli->dev); struct nouveau_bo *nvbo; size_t acc_size; int ret; int type = ttm_bo_type_device; - int lpg_shift = 12; - int max_size; - - if (drm->client.vm) - lpg_shift = drm->client.vm->mmu->lpg_shift; - max_size = INT_MAX & ~((1 << lpg_shift) - 1); - if (size <= 0 || size > max_size) { - NV_WARN(drm, "skipped size %x\n", (u32)size); + if (!size) { + NV_WARN(drm, "skipped size %016llx\n", size); return -EINVAL; } @@ -208,8 +210,9 @@ nouveau_bo_new(struct drm_device *dev, int size, int align, nvbo->tile_mode = tile_mode; nvbo->tile_flags = tile_flags; nvbo->bo.bdev = &drm->ttm.bdev; + nvbo->cli = cli; - if (!nvxx_device(&drm->device)->func->cpu_coherent) + if (!nvxx_device(&drm->client.device)->func->cpu_coherent) nvbo->force_coherent = flags & TTM_PL_FLAG_UNCACHED; nvbo->page_shift = 12; @@ -255,10 +258,10 @@ static void set_placement_range(struct nouveau_bo *nvbo, uint32_t type) { struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); - u32 vram_pages = drm->device.info.ram_size >> PAGE_SHIFT; + u32 vram_pages = drm->client.device.info.ram_size >> PAGE_SHIFT; unsigned i, fpfn, lpfn; - if (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CELSIUS && nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) && nvbo->bo.mem.num_pages < vram_pages / 4) { /* @@ -316,12 +319,12 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype, bool contig) if (ret) return ret; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA && + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA && memtype == TTM_PL_FLAG_VRAM && contig) { if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) { if (bo->mem.mem_type == TTM_PL_VRAM) { struct nvkm_mem *mem = bo->mem.mm_node; - if (!list_is_singular(&mem->regions)) + if (!nvkm_mm_contiguous(mem->mem)) evict = true; } nvbo->tile_flags &= ~NOUVEAU_GEM_TILE_NONCONTIG; @@ -443,7 +446,7 @@ void nouveau_bo_sync_for_device(struct nouveau_bo *nvbo) { struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); struct ttm_dma_tt *ttm_dma = (struct ttm_dma_tt *)nvbo->bo.ttm; int i; @@ -463,7 +466,7 @@ void nouveau_bo_sync_for_cpu(struct nouveau_bo *nvbo) { struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); struct ttm_dma_tt *ttm_dma = (struct ttm_dma_tt *)nvbo->bo.ttm; int i; @@ -579,9 +582,9 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { /* Some BARs do not support being ioremapped WC */ - if (nvxx_bar(&drm->device)->iomap_uncached) { + if (nvxx_bar(&drm->client.device)->iomap_uncached) { man->available_caching = TTM_PL_FLAG_UNCACHED; man->default_caching = TTM_PL_FLAG_UNCACHED; } @@ -594,7 +597,7 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, } break; case TTM_PL_TT: - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) man->func = &nouveau_gart_manager; else if (!drm->agp.bridge) @@ -654,20 +657,20 @@ nve0_bo_move_init(struct nouveau_channel *chan, u32 handle) static int nve0_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - struct nvkm_mem *node = old_mem->mm_node; + struct nvkm_mem *mem = old_reg->mm_node; int ret = RING_SPACE(chan, 10); if (ret == 0) { BEGIN_NVC0(chan, NvSubCopy, 0x0400, 8); - OUT_RING (chan, upper_32_bits(node->vma[0].offset)); - OUT_RING (chan, lower_32_bits(node->vma[0].offset)); - OUT_RING (chan, upper_32_bits(node->vma[1].offset)); - OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, upper_32_bits(mem->vma[0].offset)); + OUT_RING (chan, lower_32_bits(mem->vma[0].offset)); + OUT_RING (chan, upper_32_bits(mem->vma[1].offset)); + OUT_RING (chan, lower_32_bits(mem->vma[1].offset)); OUT_RING (chan, PAGE_SIZE); OUT_RING (chan, PAGE_SIZE); OUT_RING (chan, PAGE_SIZE); - OUT_RING (chan, new_mem->num_pages); + OUT_RING (chan, new_reg->num_pages); BEGIN_IMC0(chan, NvSubCopy, 0x0300, 0x0386); } return ret; @@ -686,15 +689,15 @@ nvc0_bo_move_init(struct nouveau_channel *chan, u32 handle) static int nvc0_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - struct nvkm_mem *node = old_mem->mm_node; - u64 src_offset = node->vma[0].offset; - u64 dst_offset = node->vma[1].offset; - u32 page_count = new_mem->num_pages; + struct nvkm_mem *mem = old_reg->mm_node; + u64 src_offset = mem->vma[0].offset; + u64 dst_offset = mem->vma[1].offset; + u32 page_count = new_reg->num_pages; int ret; - page_count = new_mem->num_pages; + page_count = new_reg->num_pages; while (page_count) { int line_count = (page_count > 8191) ? 8191 : page_count; @@ -724,15 +727,15 @@ nvc0_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, static int nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - struct nvkm_mem *node = old_mem->mm_node; - u64 src_offset = node->vma[0].offset; - u64 dst_offset = node->vma[1].offset; - u32 page_count = new_mem->num_pages; + struct nvkm_mem *mem = old_reg->mm_node; + u64 src_offset = mem->vma[0].offset; + u64 dst_offset = mem->vma[1].offset; + u32 page_count = new_reg->num_pages; int ret; - page_count = new_mem->num_pages; + page_count = new_reg->num_pages; while (page_count) { int line_count = (page_count > 2047) ? 2047 : page_count; @@ -763,15 +766,15 @@ nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, static int nva3_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - struct nvkm_mem *node = old_mem->mm_node; - u64 src_offset = node->vma[0].offset; - u64 dst_offset = node->vma[1].offset; - u32 page_count = new_mem->num_pages; + struct nvkm_mem *mem = old_reg->mm_node; + u64 src_offset = mem->vma[0].offset; + u64 dst_offset = mem->vma[1].offset; + u32 page_count = new_reg->num_pages; int ret; - page_count = new_mem->num_pages; + page_count = new_reg->num_pages; while (page_count) { int line_count = (page_count > 8191) ? 8191 : page_count; @@ -801,35 +804,35 @@ nva3_bo_move_copy(struct nouveau_channel *chan, struct ttm_buffer_object *bo, static int nv98_bo_move_exec(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - struct nvkm_mem *node = old_mem->mm_node; + struct nvkm_mem *mem = old_reg->mm_node; int ret = RING_SPACE(chan, 7); if (ret == 0) { BEGIN_NV04(chan, NvSubCopy, 0x0320, 6); - OUT_RING (chan, upper_32_bits(node->vma[0].offset)); - OUT_RING (chan, lower_32_bits(node->vma[0].offset)); - OUT_RING (chan, upper_32_bits(node->vma[1].offset)); - OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, upper_32_bits(mem->vma[0].offset)); + OUT_RING (chan, lower_32_bits(mem->vma[0].offset)); + OUT_RING (chan, upper_32_bits(mem->vma[1].offset)); + OUT_RING (chan, lower_32_bits(mem->vma[1].offset)); OUT_RING (chan, 0x00000000 /* COPY */); - OUT_RING (chan, new_mem->num_pages << PAGE_SHIFT); + OUT_RING (chan, new_reg->num_pages << PAGE_SHIFT); } return ret; } static int nv84_bo_move_exec(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - struct nvkm_mem *node = old_mem->mm_node; + struct nvkm_mem *mem = old_reg->mm_node; int ret = RING_SPACE(chan, 7); if (ret == 0) { BEGIN_NV04(chan, NvSubCopy, 0x0304, 6); - OUT_RING (chan, new_mem->num_pages << PAGE_SHIFT); - OUT_RING (chan, upper_32_bits(node->vma[0].offset)); - OUT_RING (chan, lower_32_bits(node->vma[0].offset)); - OUT_RING (chan, upper_32_bits(node->vma[1].offset)); - OUT_RING (chan, lower_32_bits(node->vma[1].offset)); + OUT_RING (chan, new_reg->num_pages << PAGE_SHIFT); + OUT_RING (chan, upper_32_bits(mem->vma[0].offset)); + OUT_RING (chan, lower_32_bits(mem->vma[0].offset)); + OUT_RING (chan, upper_32_bits(mem->vma[1].offset)); + OUT_RING (chan, lower_32_bits(mem->vma[1].offset)); OUT_RING (chan, 0x00000000 /* MODE_COPY, QUERY_NONE */); } return ret; @@ -853,14 +856,14 @@ nv50_bo_move_init(struct nouveau_channel *chan, u32 handle) static int nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - struct nvkm_mem *node = old_mem->mm_node; - u64 length = (new_mem->num_pages << PAGE_SHIFT); - u64 src_offset = node->vma[0].offset; - u64 dst_offset = node->vma[1].offset; - int src_tiled = !!node->memtype; - int dst_tiled = !!((struct nvkm_mem *)new_mem->mm_node)->memtype; + struct nvkm_mem *mem = old_reg->mm_node; + u64 length = (new_reg->num_pages << PAGE_SHIFT); + u64 src_offset = mem->vma[0].offset; + u64 dst_offset = mem->vma[1].offset; + int src_tiled = !!mem->memtype; + int dst_tiled = !!((struct nvkm_mem *)new_reg->mm_node)->memtype; int ret; while (length) { @@ -940,20 +943,20 @@ nv04_bo_move_init(struct nouveau_channel *chan, u32 handle) static inline uint32_t nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo, - struct nouveau_channel *chan, struct ttm_mem_reg *mem) + struct nouveau_channel *chan, struct ttm_mem_reg *reg) { - if (mem->mem_type == TTM_PL_TT) + if (reg->mem_type == TTM_PL_TT) return NvDmaTT; return chan->vram.handle; } static int nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, - struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + struct ttm_mem_reg *old_reg, struct ttm_mem_reg *new_reg) { - u32 src_offset = old_mem->start << PAGE_SHIFT; - u32 dst_offset = new_mem->start << PAGE_SHIFT; - u32 page_count = new_mem->num_pages; + u32 src_offset = old_reg->start << PAGE_SHIFT; + u32 dst_offset = new_reg->start << PAGE_SHIFT; + u32 page_count = new_reg->num_pages; int ret; ret = RING_SPACE(chan, 3); @@ -961,10 +964,10 @@ nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, return ret; BEGIN_NV04(chan, NvSubCopy, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2); - OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_mem)); - OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem)); + OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_reg)); + OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_reg)); - page_count = new_mem->num_pages; + page_count = new_reg->num_pages; while (page_count) { int line_count = (page_count > 2047) ? 2047 : page_count; @@ -995,33 +998,33 @@ nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, static int nouveau_bo_move_prep(struct nouveau_drm *drm, struct ttm_buffer_object *bo, - struct ttm_mem_reg *mem) + struct ttm_mem_reg *reg) { - struct nvkm_mem *old_node = bo->mem.mm_node; - struct nvkm_mem *new_node = mem->mm_node; - u64 size = (u64)mem->num_pages << PAGE_SHIFT; + struct nvkm_mem *old_mem = bo->mem.mm_node; + struct nvkm_mem *new_mem = reg->mm_node; + u64 size = (u64)reg->num_pages << PAGE_SHIFT; int ret; - ret = nvkm_vm_get(drm->client.vm, size, old_node->page_shift, - NV_MEM_ACCESS_RW, &old_node->vma[0]); + ret = nvkm_vm_get(drm->client.vm, size, old_mem->page_shift, + NV_MEM_ACCESS_RW, &old_mem->vma[0]); if (ret) return ret; - ret = nvkm_vm_get(drm->client.vm, size, new_node->page_shift, - NV_MEM_ACCESS_RW, &old_node->vma[1]); + ret = nvkm_vm_get(drm->client.vm, size, new_mem->page_shift, + NV_MEM_ACCESS_RW, &old_mem->vma[1]); if (ret) { - nvkm_vm_put(&old_node->vma[0]); + nvkm_vm_put(&old_mem->vma[0]); return ret; } - nvkm_vm_map(&old_node->vma[0], old_node); - nvkm_vm_map(&old_node->vma[1], new_node); + nvkm_vm_map(&old_mem->vma[0], old_mem); + nvkm_vm_map(&old_mem->vma[1], new_mem); return 0; } static int nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, - bool no_wait_gpu, struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_reg) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct nouveau_channel *chan = drm->ttm.chan; @@ -1033,8 +1036,8 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, * old nvkm_mem node, these will get cleaned up after ttm has * destroyed the ttm_mem_reg */ - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { - ret = nouveau_bo_move_prep(drm, bo, new_mem); + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nouveau_bo_move_prep(drm, bo, new_reg); if (ret) return ret; } @@ -1042,14 +1045,14 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, mutex_lock_nested(&cli->mutex, SINGLE_DEPTH_NESTING); ret = nouveau_fence_sync(nouveau_bo(bo), chan, true, intr); if (ret == 0) { - ret = drm->ttm.move(chan, bo, &bo->mem, new_mem); + ret = drm->ttm.move(chan, bo, &bo->mem, new_reg); if (ret == 0) { ret = nouveau_fence_new(chan, false, &fence); if (ret == 0) { ret = ttm_bo_move_accel_cleanup(bo, &fence->base, evict, - new_mem); + new_reg); nouveau_fence_unref(&fence); } } @@ -1124,7 +1127,7 @@ nouveau_bo_move_init(struct nouveau_drm *drm) static int nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, - bool no_wait_gpu, struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_reg) { struct ttm_place placement_memtype = { .fpfn = 0, @@ -1132,35 +1135,35 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING }; struct ttm_placement placement; - struct ttm_mem_reg tmp_mem; + struct ttm_mem_reg tmp_reg; int ret; placement.num_placement = placement.num_busy_placement = 1; placement.placement = placement.busy_placement = &placement_memtype; - tmp_mem = *new_mem; - tmp_mem.mm_node = NULL; - ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_gpu); + tmp_reg = *new_reg; + tmp_reg.mm_node = NULL; + ret = ttm_bo_mem_space(bo, &placement, &tmp_reg, intr, no_wait_gpu); if (ret) return ret; - ret = ttm_tt_bind(bo->ttm, &tmp_mem); + ret = ttm_tt_bind(bo->ttm, &tmp_reg); if (ret) goto out; - ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, &tmp_mem); + ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, &tmp_reg); if (ret) goto out; - ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, new_mem); + ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, new_reg); out: - ttm_bo_mem_put(bo, &tmp_mem); + ttm_bo_mem_put(bo, &tmp_reg); return ret; } static int nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, - bool no_wait_gpu, struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_reg) { struct ttm_place placement_memtype = { .fpfn = 0, @@ -1168,33 +1171,34 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING }; struct ttm_placement placement; - struct ttm_mem_reg tmp_mem; + struct ttm_mem_reg tmp_reg; int ret; placement.num_placement = placement.num_busy_placement = 1; placement.placement = placement.busy_placement = &placement_memtype; - tmp_mem = *new_mem; - tmp_mem.mm_node = NULL; - ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait_gpu); + tmp_reg = *new_reg; + tmp_reg.mm_node = NULL; + ret = ttm_bo_mem_space(bo, &placement, &tmp_reg, intr, no_wait_gpu); if (ret) return ret; - ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, &tmp_mem); + ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, &tmp_reg); if (ret) goto out; - ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, new_mem); + ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_gpu, new_reg); if (ret) goto out; out: - ttm_bo_mem_put(bo, &tmp_mem); + ttm_bo_mem_put(bo, &tmp_reg); return ret; } static void -nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) +nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, bool evict, + struct ttm_mem_reg *new_reg) { struct nouveau_bo *nvbo = nouveau_bo(bo); struct nvkm_vma *vma; @@ -1204,10 +1208,10 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) return; list_for_each_entry(vma, &nvbo->vma_list, head) { - if (new_mem && new_mem->mem_type != TTM_PL_SYSTEM && - (new_mem->mem_type == TTM_PL_VRAM || + if (new_reg && new_reg->mem_type != TTM_PL_SYSTEM && + (new_reg->mem_type == TTM_PL_VRAM || nvbo->page_shift != vma->vm->mmu->lpg_shift)) { - nvkm_vm_map(vma, new_mem->mm_node); + nvkm_vm_map(vma, new_reg->mm_node); } else { WARN_ON(ttm_bo_wait(bo, false, false)); nvkm_vm_unmap(vma); @@ -1216,20 +1220,20 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) } static int -nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem, +nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_reg, struct nouveau_drm_tile **new_tile) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct drm_device *dev = drm->dev; struct nouveau_bo *nvbo = nouveau_bo(bo); - u64 offset = new_mem->start << PAGE_SHIFT; + u64 offset = new_reg->start << PAGE_SHIFT; *new_tile = NULL; - if (new_mem->mem_type != TTM_PL_VRAM) + if (new_reg->mem_type != TTM_PL_VRAM) return 0; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { - *new_tile = nv10_bo_set_tiling(dev, offset, new_mem->size, + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) { + *new_tile = nv10_bo_set_tiling(dev, offset, new_reg->size, nvbo->tile_mode, nvbo->tile_flags); } @@ -1252,11 +1256,11 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo, static int nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, - bool no_wait_gpu, struct ttm_mem_reg *new_mem) + bool no_wait_gpu, struct ttm_mem_reg *new_reg) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct nouveau_bo *nvbo = nouveau_bo(bo); - struct ttm_mem_reg *old_mem = &bo->mem; + struct ttm_mem_reg *old_reg = &bo->mem; struct nouveau_drm_tile *new_tile = NULL; int ret = 0; @@ -1267,31 +1271,31 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, if (nvbo->pin_refcnt) NV_WARN(drm, "Moving pinned object %p!\n", nvbo); - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { - ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile); + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { + ret = nouveau_bo_vm_bind(bo, new_reg, &new_tile); if (ret) return ret; } /* Fake bo copy. */ - if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) { + if (old_reg->mem_type == TTM_PL_SYSTEM && !bo->ttm) { BUG_ON(bo->mem.mm_node != NULL); - bo->mem = *new_mem; - new_mem->mm_node = NULL; + bo->mem = *new_reg; + new_reg->mm_node = NULL; goto out; } /* Hardware assisted copy. */ if (drm->ttm.move) { - if (new_mem->mem_type == TTM_PL_SYSTEM) + if (new_reg->mem_type == TTM_PL_SYSTEM) ret = nouveau_bo_move_flipd(bo, evict, intr, - no_wait_gpu, new_mem); - else if (old_mem->mem_type == TTM_PL_SYSTEM) + no_wait_gpu, new_reg); + else if (old_reg->mem_type == TTM_PL_SYSTEM) ret = nouveau_bo_move_flips(bo, evict, intr, - no_wait_gpu, new_mem); + no_wait_gpu, new_reg); else ret = nouveau_bo_move_m2mf(bo, evict, intr, - no_wait_gpu, new_mem); + no_wait_gpu, new_reg); if (!ret) goto out; } @@ -1299,10 +1303,10 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, /* Fallback to software copy. */ ret = ttm_bo_wait(bo, intr, no_wait_gpu); if (ret == 0) - ret = ttm_bo_move_memcpy(bo, intr, no_wait_gpu, new_mem); + ret = ttm_bo_move_memcpy(bo, intr, no_wait_gpu, new_reg); out: - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { if (ret) nouveau_bo_vm_cleanup(bo, NULL, &new_tile); else @@ -1322,54 +1326,54 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) } static int -nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) { - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct ttm_mem_type_manager *man = &bdev->man[reg->mem_type]; struct nouveau_drm *drm = nouveau_bdev(bdev); - struct nvkm_device *device = nvxx_device(&drm->device); - struct nvkm_mem *node = mem->mm_node; + struct nvkm_device *device = nvxx_device(&drm->client.device); + struct nvkm_mem *mem = reg->mm_node; int ret; - mem->bus.addr = NULL; - mem->bus.offset = 0; - mem->bus.size = mem->num_pages << PAGE_SHIFT; - mem->bus.base = 0; - mem->bus.is_iomem = false; + reg->bus.addr = NULL; + reg->bus.offset = 0; + reg->bus.size = reg->num_pages << PAGE_SHIFT; + reg->bus.base = 0; + reg->bus.is_iomem = false; if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) return -EINVAL; - switch (mem->mem_type) { + switch (reg->mem_type) { case TTM_PL_SYSTEM: /* System memory */ return 0; case TTM_PL_TT: #if IS_ENABLED(CONFIG_AGP) if (drm->agp.bridge) { - mem->bus.offset = mem->start << PAGE_SHIFT; - mem->bus.base = drm->agp.base; - mem->bus.is_iomem = !drm->agp.cma; + reg->bus.offset = reg->start << PAGE_SHIFT; + reg->bus.base = drm->agp.base; + reg->bus.is_iomem = !drm->agp.cma; } #endif - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA || !node->memtype) + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA || !mem->memtype) /* untiled */ break; /* fallthrough, tiled memory */ case TTM_PL_VRAM: - mem->bus.offset = mem->start << PAGE_SHIFT; - mem->bus.base = device->func->resource_addr(device, 1); - mem->bus.is_iomem = true; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { - struct nvkm_bar *bar = nvxx_bar(&drm->device); + reg->bus.offset = reg->start << PAGE_SHIFT; + reg->bus.base = device->func->resource_addr(device, 1); + reg->bus.is_iomem = true; + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + struct nvkm_bar *bar = nvxx_bar(&drm->client.device); int page_shift = 12; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_FERMI) - page_shift = node->page_shift; + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) + page_shift = mem->page_shift; - ret = nvkm_bar_umap(bar, node->size << 12, page_shift, - &node->bar_vma); + ret = nvkm_bar_umap(bar, mem->size << 12, page_shift, + &mem->bar_vma); if (ret) return ret; - nvkm_vm_map(&node->bar_vma, node); - mem->bus.offset = node->bar_vma.offset; + nvkm_vm_map(&mem->bar_vma, mem); + reg->bus.offset = mem->bar_vma.offset; } break; default: @@ -1379,15 +1383,15 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) } static void -nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) { - struct nvkm_mem *node = mem->mm_node; + struct nvkm_mem *mem = reg->mm_node; - if (!node->bar_vma.node) + if (!mem->bar_vma.node) return; - nvkm_vm_unmap(&node->bar_vma); - nvkm_vm_put(&node->bar_vma); + nvkm_vm_unmap(&mem->bar_vma); + nvkm_vm_put(&mem->bar_vma); } static int @@ -1395,7 +1399,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct nouveau_bo *nvbo = nouveau_bo(bo); - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); u32 mappable = device->func->resource_size(device, 1) >> PAGE_SHIFT; int i, ret; @@ -1403,7 +1407,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) * nothing to do here. */ if (bo->mem.mem_type != TTM_PL_VRAM) { - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA || + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA || !nouveau_bo_tile_layout(nvbo)) return 0; @@ -1418,7 +1422,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) } /* make sure bo is in mappable vram */ - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA || + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA || bo->mem.start + bo->mem.num_pages < mappable) return 0; @@ -1460,7 +1464,7 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm) } drm = nouveau_bdev(ttm->bdev); - device = nvxx_device(&drm->device); + device = nvxx_device(&drm->client.device); dev = drm->dev; pdev = device->dev; @@ -1517,7 +1521,7 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm) return; drm = nouveau_bdev(ttm->bdev); - device = nvxx_device(&drm->device); + device = nvxx_device(&drm->client.device); dev = drm->dev; pdev = device->dev; @@ -1570,8 +1574,6 @@ struct ttm_bo_driver nouveau_bo_driver = { .fault_reserve_notify = &nouveau_ttm_fault_reserve_notify, .io_mem_reserve = &nouveau_ttm_io_mem_reserve, .io_mem_free = &nouveau_ttm_io_mem_free, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; struct nvkm_vma * diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index e42360983229..b06a5385d6dd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -26,6 +26,8 @@ struct nouveau_bo { struct list_head vma_list; unsigned page_shift; + struct nouveau_cli *cli; + u32 tile_mode; u32 tile_flags; struct nouveau_drm_tile *tile; @@ -69,7 +71,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) extern struct ttm_bo_driver nouveau_bo_driver; void nouveau_bo_move_init(struct nouveau_drm *); -int nouveau_bo_new(struct drm_device *, int size, int align, u32 flags, +int nouveau_bo_new(struct nouveau_cli *, u64 size, int align, u32 flags, u32 tile_mode, u32 tile_flags, struct sg_table *sg, struct reservation_object *robj, struct nouveau_bo **); diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index f9b3c811187e..dbc41fa86ee8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -45,10 +45,20 @@ MODULE_PARM_DESC(vram_pushbuf, "Create DMA push buffers in VRAM"); int nouveau_vram_pushbuf; module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400); +static int +nouveau_channel_killed(struct nvif_notify *ntfy) +{ + struct nouveau_channel *chan = container_of(ntfy, typeof(*chan), kill); + struct nouveau_cli *cli = (void *)chan->user.client; + NV_PRINTK(warn, cli, "channel %d killed!\n", chan->chid); + atomic_set(&chan->killed, 1); + return NVIF_NOTIFY_DROP; +} + int nouveau_channel_idle(struct nouveau_channel *chan) { - if (likely(chan && chan->fence)) { + if (likely(chan && chan->fence && !atomic_read(&chan->killed))) { struct nouveau_cli *cli = (void *)chan->user.client; struct nouveau_fence *fence = NULL; int ret; @@ -78,6 +88,7 @@ nouveau_channel_del(struct nouveau_channel **pchan) nvif_object_fini(&chan->nvsw); nvif_object_fini(&chan->gart); nvif_object_fini(&chan->vram); + nvif_notify_fini(&chan->kill); nvif_object_fini(&chan->user); nvif_object_fini(&chan->push.ctxdma); nouveau_bo_vma_del(chan->push.buffer, &chan->push.vma); @@ -107,13 +118,14 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device, chan->device = device; chan->drm = drm; + atomic_set(&chan->killed, 0); /* allocate memory for dma push buffer */ target = TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED; if (nouveau_vram_pushbuf) target = TTM_PL_FLAG_VRAM; - ret = nouveau_bo_new(drm->dev, size, 0, target, 0, 0, NULL, NULL, + ret = nouveau_bo_new(cli, size, 0, target, 0, 0, NULL, NULL, &chan->push.buffer); if (ret == 0) { ret = nouveau_bo_pin(chan->push.buffer, target, false); @@ -301,12 +313,26 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) { struct nvif_device *device = chan->device; struct nouveau_cli *cli = (void *)chan->user.client; + struct nouveau_drm *drm = chan->drm; struct nvkm_mmu *mmu = nvxx_mmu(device); struct nv_dma_v0 args = {}; int ret, i; nvif_object_map(&chan->user); + if (chan->user.oclass >= FERMI_CHANNEL_GPFIFO) { + ret = nvif_notify_init(&chan->user, nouveau_channel_killed, + true, NV906F_V0_NTFY_KILLED, + NULL, 0, 0, &chan->kill); + if (ret == 0) + ret = nvif_notify_get(&chan->kill); + if (ret) { + NV_ERROR(drm, "Failed to request channel kill " + "notification: %d\n", ret); + return ret; + } + } + /* allocate dma objects to cover all allowed vram, and gart */ if (device->info.family < NV_DEVICE_INFO_V0_FERMI) { if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) { diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.h b/drivers/gpu/drm/nouveau/nouveau_chan.h index 48062c94f36d..46b947ba1cf4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.h +++ b/drivers/gpu/drm/nouveau/nouveau_chan.h @@ -1,7 +1,7 @@ #ifndef __NOUVEAU_CHAN_H__ #define __NOUVEAU_CHAN_H__ - #include <nvif/object.h> +#include <nvif/notify.h> struct nvif_device; struct nouveau_channel { @@ -38,6 +38,9 @@ struct nouveau_channel { u32 user_put; struct nvif_object user; + + struct nvif_notify kill; + atomic_t killed; }; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 947c200655b4..f5add64c093f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -33,6 +33,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> #include "nouveau_reg.h" #include "nouveau_drv.h" @@ -418,7 +419,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; int i, panel = -ENODEV; @@ -520,7 +521,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector, return; nv_connector->detected_encoder = nv_encoder; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { connector->interlace_allowed = true; connector->doublescan_allowed = true; } else @@ -530,8 +531,8 @@ nouveau_connector_set_encoder(struct drm_connector *connector, connector->interlace_allowed = false; } else { connector->doublescan_allowed = true; - if (drm->device.info.family == NV_DEVICE_INFO_V0_KELVIN || - (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_KELVIN || + (drm->client.device.info.family == NV_DEVICE_INFO_V0_CELSIUS && (dev->pdev->device & 0x0ff0) != 0x0100 && (dev->pdev->device & 0x0ff0) != 0x0150)) /* HW is broken */ @@ -769,7 +770,7 @@ nouveau_connector_set_property(struct drm_connector *connector, struct drm_encoder *encoder = to_drm_encoder(nv_encoder); int ret; - if (connector->dev->mode_config.funcs->atomic_commit) + if (drm_drv_uses_atomic_modeset(connector->dev)) return drm_atomic_helper_connector_set_property(connector, property, value); ret = connector->funcs->atomic_set_property(&nv_connector->base, @@ -983,17 +984,17 @@ get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi) /* Note: these limits are conservative, some Fermi's * can do 297 MHz. Unclear how this can be determined. */ - if (drm->device.info.family >= NV_DEVICE_INFO_V0_KEPLER) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER) return 297000; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_FERMI) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) return 225000; } if (dcb->location != DCB_LOC_ON_CHIP || - drm->device.info.chipset >= 0x46) + drm->client.device.info.chipset >= 0x46) return 165000; - else if (drm->device.info.chipset >= 0x40) + else if (drm->client.device.info.chipset >= 0x40) return 155000; - else if (drm->device.info.chipset >= 0x18) + else if (drm->client.device.info.chipset >= 0x18) return 135000; else return 112000; @@ -1040,7 +1041,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, clock = clock * (connector->display_info.bpc * 3) / 10; break; default: - BUG_ON(1); + BUG(); return MODE_BAD; } @@ -1074,7 +1075,7 @@ nouveau_connector_helper_funcs = { static int nouveau_connector_dpms(struct drm_connector *connector, int mode) { - if (connector->dev->mode_config.funcs->atomic_commit) + if (drm_drv_uses_atomic_modeset(connector->dev)) return drm_atomic_helper_connector_dpms(connector, mode); return drm_helper_connector_dpms(connector, mode); } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 096983c42a1f..a4d1a059bd3d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -30,6 +30,7 @@ #include <nvif/notify.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_dp_helper.h> #include "nouveau_crtc.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index 411c12cdb249..fd64dfdc7d4f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -259,8 +259,9 @@ nouveau_debugfs_init(struct nouveau_drm *drm) if (!drm->debugfs) return -ENOMEM; - ret = nvif_object_init(&drm->device.object, 0, NVIF_CLASS_CONTROL, - NULL, 0, &drm->debugfs->ctrl); + ret = nvif_object_init(&drm->client.device.object, 0, + NVIF_CLASS_CONTROL, NULL, 0, + &drm->debugfs->ctrl); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 6a157763dfc3..72fdba1a1c5d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -58,27 +58,30 @@ int nouveau_display_vblank_enable(struct drm_device *dev, unsigned int pipe) { struct drm_crtc *crtc; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - if (nv_crtc->index == pipe) { - nvif_notify_get(&nv_crtc->vblank); - return 0; - } - } - return -EINVAL; + struct nouveau_crtc *nv_crtc; + + crtc = drm_crtc_from_index(dev, pipe); + if (!crtc) + return -EINVAL; + + nv_crtc = nouveau_crtc(crtc); + nvif_notify_get(&nv_crtc->vblank); + + return 0; } void nouveau_display_vblank_disable(struct drm_device *dev, unsigned int pipe) { struct drm_crtc *crtc; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - if (nv_crtc->index == pipe) { - nvif_notify_put(&nv_crtc->vblank); - return; - } - } + struct nouveau_crtc *nv_crtc; + + crtc = drm_crtc_from_index(dev, pipe); + if (!crtc) + return; + + nv_crtc = nouveau_crtc(crtc); + nvif_notify_put(&nv_crtc->vblank); } static inline int @@ -162,7 +165,7 @@ nouveau_display_vblstamp(struct drm_device *dev, unsigned int pipe, list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (nouveau_crtc(crtc)->index == pipe) { struct drm_display_mode *mode; - if (dev->mode_config.funcs->atomic_commit) + if (drm_drv_uses_atomic_modeset(dev)) mode = &crtc->state->adjusted_mode; else mode = &crtc->hwmode; @@ -259,7 +262,7 @@ nouveau_framebuffer_new(struct drm_device *dev, if (!(fb = *pfb = kzalloc(sizeof(*fb), GFP_KERNEL))) return -ENOMEM; - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &fb->base, mode_cmd); fb->nvbo = nvbo; ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs); @@ -493,7 +496,7 @@ int nouveau_display_create(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); struct nouveau_display *disp; int ret; @@ -510,15 +513,15 @@ nouveau_display_create(struct drm_device *dev) dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; - if (drm->device.info.family < NV_DEVICE_INFO_V0_CELSIUS) { + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_CELSIUS) { dev->mode_config.max_width = 2048; dev->mode_config.max_height = 2048; } else - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { dev->mode_config.max_width = 4096; dev->mode_config.max_height = 4096; } else - if (drm->device.info.family < NV_DEVICE_INFO_V0_FERMI) { + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) { dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } else { @@ -529,7 +532,7 @@ nouveau_display_create(struct drm_device *dev) dev->mode_config.preferred_depth = 24; dev->mode_config.prefer_shadow = 1; - if (drm->device.info.chipset < 0x11) + if (drm->client.device.info.chipset < 0x11) dev->mode_config.async_page_flip = false; else dev->mode_config.async_page_flip = true; @@ -556,7 +559,7 @@ nouveau_display_create(struct drm_device *dev) int i; for (i = 0, ret = -ENODEV; ret && i < ARRAY_SIZE(oclass); i++) { - ret = nvif_object_init(&drm->device.object, 0, + ret = nvif_object_init(&drm->client.device.object, 0, oclass[i], NULL, 0, &disp->disp); } @@ -739,7 +742,7 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime) struct nouveau_display *disp = nouveau_display(dev); struct drm_crtc *crtc; - if (dev->mode_config.funcs->atomic_commit) { + if (drm_drv_uses_atomic_modeset(dev)) { if (!runtime) { disp->suspend = nouveau_atomic_suspend(dev); if (IS_ERR(disp->suspend)) { @@ -785,7 +788,7 @@ nouveau_display_resume(struct drm_device *dev, bool runtime) struct drm_crtc *crtc; int ret; - if (dev->mode_config.funcs->atomic_commit) { + if (drm_drv_uses_atomic_modeset(dev)) { nouveau_display_init(dev); if (disp->suspend) { drm_atomic_helper_resume(dev, disp->suspend); @@ -948,7 +951,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Initialize a page flip struct */ *s = (struct nouveau_page_flip_state) - { { }, event, crtc, fb->bits_per_pixel, fb->pitches[0], + { { }, event, crtc, fb->format->cpp[0] * 8, fb->pitches[0], new_bo->bo.offset }; /* Keep vblanks on during flip, for the target crtc of this flip */ @@ -1055,6 +1058,7 @@ int nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args) { + struct nouveau_cli *cli = nouveau_cli(file_priv); struct nouveau_bo *bo; uint32_t domain; int ret; @@ -1064,12 +1068,12 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, args->size = roundup(args->size, PAGE_SIZE); /* Use VRAM if there is any ; otherwise fallback to system memory */ - if (nouveau_drm(dev)->device.info.ram_size != 0) + if (nouveau_drm(dev)->client.device.info.ram_size != 0) domain = NOUVEAU_GEM_DOMAIN_VRAM; else domain = NOUVEAU_GEM_DOMAIN_GART; - ret = nouveau_gem_new(dev, args->size, 0, domain, 0, 0, &bo); + ret = nouveau_gem_new(cli, args->size, 0, domain, 0, 0, &bo); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index bc85a45f91cd..468ed1d3bb26 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -37,6 +37,8 @@ #include <core/pci.h> #include <core/tegra.h> +#include <nvif/driver.h> + #include <nvif/class.h> #include <nvif/cl0002.h> #include <nvif/cla06f.h> @@ -109,35 +111,53 @@ nouveau_name(struct drm_device *dev) return nouveau_platform_name(dev->platformdev); } +static void +nouveau_cli_fini(struct nouveau_cli *cli) +{ + nvkm_vm_ref(NULL, &nvxx_client(&cli->base)->vm, NULL); + usif_client_fini(cli); + nvif_device_fini(&cli->device); + nvif_client_fini(&cli->base); +} + static int -nouveau_cli_create(struct drm_device *dev, const char *sname, - int size, void **pcli) +nouveau_cli_init(struct nouveau_drm *drm, const char *sname, + struct nouveau_cli *cli) { - struct nouveau_cli *cli = *pcli = kzalloc(size, GFP_KERNEL); + u64 device = nouveau_name(drm->dev); int ret; - if (cli) { - snprintf(cli->name, sizeof(cli->name), "%s", sname); - cli->dev = dev; - ret = nvif_client_init(NULL, cli->name, nouveau_name(dev), - nouveau_config, nouveau_debug, + snprintf(cli->name, sizeof(cli->name), "%s", sname); + cli->dev = drm->dev; + mutex_init(&cli->mutex); + usif_client_init(cli); + + if (cli == &drm->client) { + ret = nvif_driver_init(NULL, nouveau_config, nouveau_debug, + cli->name, device, &cli->base); + } else { + ret = nvif_client_init(&drm->client.base, cli->name, device, &cli->base); - if (ret == 0) { - mutex_init(&cli->mutex); - usif_client_init(cli); - } - return ret; } - return -ENOMEM; -} + if (ret) { + NV_ERROR(drm, "Client allocation failed: %d\n", ret); + goto done; + } -static void -nouveau_cli_destroy(struct nouveau_cli *cli) -{ - nvkm_vm_ref(NULL, &nvxx_client(&cli->base)->vm, NULL); - nvif_client_fini(&cli->base); - usif_client_fini(cli); - kfree(cli); + ret = nvif_device_init(&cli->base.object, 0, NV_DEVICE, + &(struct nv_device_v0) { + .device = ~0, + }, sizeof(struct nv_device_v0), + &cli->device); + if (ret) { + NV_ERROR(drm, "Device allocation failed: %d\n", ret); + goto done; + } + +done: + if (ret) + nouveau_cli_fini(cli); + return ret; } static void @@ -161,7 +181,7 @@ nouveau_accel_fini(struct nouveau_drm *drm) static void nouveau_accel_init(struct nouveau_drm *drm) { - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; struct nvif_sclass *sclass; u32 arg0, arg1; int ret, i, n; @@ -215,7 +235,7 @@ nouveau_accel_init(struct nouveau_drm *drm) } if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) { - ret = nouveau_channel_new(drm, &drm->device, + ret = nouveau_channel_new(drm, &drm->client.device, NVA06F_V0_ENGINE_CE0 | NVA06F_V0_ENGINE_CE1, 0, &drm->cechan); @@ -228,7 +248,7 @@ nouveau_accel_init(struct nouveau_drm *drm) if (device->info.chipset >= 0xa3 && device->info.chipset != 0xaa && device->info.chipset != 0xac) { - ret = nouveau_channel_new(drm, &drm->device, + ret = nouveau_channel_new(drm, &drm->client.device, NvDmaFB, NvDmaTT, &drm->cechan); if (ret) NV_ERROR(drm, "failed to create ce channel, %d\n", ret); @@ -240,7 +260,8 @@ nouveau_accel_init(struct nouveau_drm *drm) arg1 = NvDmaTT; } - ret = nouveau_channel_new(drm, &drm->device, arg0, arg1, &drm->channel); + ret = nouveau_channel_new(drm, &drm->client.device, + arg0, arg1, &drm->channel); if (ret) { NV_ERROR(drm, "failed to create kernel channel, %d\n", ret); nouveau_accel_fini(drm); @@ -280,8 +301,8 @@ nouveau_accel_init(struct nouveau_drm *drm) } if (device->info.family < NV_DEVICE_INFO_V0_FERMI) { - ret = nvkm_gpuobj_new(nvxx_device(&drm->device), 32, 0, false, - NULL, &drm->notify); + ret = nvkm_gpuobj_new(nvxx_device(&drm->client.device), 32, 0, + false, NULL, &drm->notify); if (ret) { NV_ERROR(drm, "failed to allocate notifier, %d\n", ret); nouveau_accel_fini(drm); @@ -407,12 +428,17 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) struct nouveau_drm *drm; int ret; - ret = nouveau_cli_create(dev, "DRM", sizeof(*drm), (void **)&drm); + if (!(drm = kzalloc(sizeof(*drm), GFP_KERNEL))) + return -ENOMEM; + dev->dev_private = drm; + drm->dev = dev; + + ret = nouveau_cli_init(drm, "DRM", &drm->client); if (ret) return ret; - dev->dev_private = drm; - drm->dev = dev; + dev->irq_enabled = true; + nvxx_client(&drm->client.base)->debug = nvkm_dbgopt(nouveau_debug, "DRM"); @@ -421,33 +447,24 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) nouveau_get_hdmi_dev(drm); - ret = nvif_device_init(&drm->client.base.object, 0, NV_DEVICE, - &(struct nv_device_v0) { - .device = ~0, - }, sizeof(struct nv_device_v0), - &drm->device); - if (ret) - goto fail_device; - - dev->irq_enabled = true; - /* workaround an odd issue on nvc1 by disabling the device's * nosnoop capability. hopefully won't cause issues until a * better fix is found - assuming there is one... */ - if (drm->device.info.chipset == 0xc1) - nvif_mask(&drm->device.object, 0x00088080, 0x00000800, 0x00000000); + if (drm->client.device.info.chipset == 0xc1) + nvif_mask(&drm->client.device.object, 0x00088080, 0x00000800, 0x00000000); nouveau_vga_init(drm); - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { - if (!nvxx_device(&drm->device)->mmu) { + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + if (!nvxx_device(&drm->client.device)->mmu) { ret = -ENOSYS; goto fail_device; } - ret = nvkm_vm_new(nvxx_device(&drm->device), 0, (1ULL << 40), - 0x1000, NULL, &drm->client.vm); + ret = nvkm_vm_new(nvxx_device(&drm->client.device), + 0, (1ULL << 40), 0x1000, NULL, + &drm->client.vm); if (ret) goto fail_device; @@ -497,12 +514,12 @@ fail_bios: fail_ttm: nouveau_vga_fini(drm); fail_device: - nvif_device_fini(&drm->device); - nouveau_cli_destroy(&drm->client); + nouveau_cli_fini(&drm->client); + kfree(drm); return ret; } -static int +static void nouveau_drm_unload(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); @@ -527,11 +544,10 @@ nouveau_drm_unload(struct drm_device *dev) nouveau_ttm_fini(drm); nouveau_vga_fini(drm); - nvif_device_fini(&drm->device); if (drm->hdmi_device) pci_dev_put(drm->hdmi_device); - nouveau_cli_destroy(&drm->client); - return 0; + nouveau_cli_fini(&drm->client); + kfree(drm); } void @@ -561,7 +577,6 @@ static int nouveau_do_suspend(struct drm_device *dev, bool runtime) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_cli *cli; int ret; nouveau_led_suspend(dev); @@ -591,7 +606,7 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime) goto fail_display; } - NV_INFO(drm, "suspending client object trees...\n"); + NV_INFO(drm, "suspending fence...\n"); if (drm->fence && nouveau_fence(drm)->suspend) { if (!nouveau_fence(drm)->suspend(drm)) { ret = -ENOMEM; @@ -599,13 +614,7 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime) } } - list_for_each_entry(cli, &drm->clients, head) { - ret = nvif_client_suspend(&cli->base); - if (ret) - goto fail_client; - } - - NV_INFO(drm, "suspending kernel object tree...\n"); + NV_INFO(drm, "suspending object tree...\n"); ret = nvif_client_suspend(&drm->client.base); if (ret) goto fail_client; @@ -613,10 +622,6 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime) return 0; fail_client: - list_for_each_entry_continue_reverse(cli, &drm->clients, head) { - nvif_client_resume(&cli->base); - } - if (drm->fence && nouveau_fence(drm)->resume) nouveau_fence(drm)->resume(drm); @@ -632,19 +637,14 @@ static int nouveau_do_resume(struct drm_device *dev, bool runtime) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_cli *cli; - NV_INFO(drm, "resuming kernel object tree...\n"); + NV_INFO(drm, "resuming object tree...\n"); nvif_client_resume(&drm->client.base); - NV_INFO(drm, "resuming client object trees...\n"); + NV_INFO(drm, "resuming fence...\n"); if (drm->fence && nouveau_fence(drm)->resume) nouveau_fence(drm)->resume(drm); - list_for_each_entry(cli, &drm->clients, head) { - nvif_client_resume(&cli->base); - } - nouveau_run_vbios_init(dev); if (dev->mode_config.num_crtc) { @@ -759,7 +759,7 @@ nouveau_pmops_runtime_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *drm_dev = pci_get_drvdata(pdev); - struct nvif_device *device = &nouveau_drm(drm_dev)->device; + struct nvif_device *device = &nouveau_drm(drm_dev)->client.device; int ret; if (nouveau_runtime_pm == 0) @@ -845,20 +845,20 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) get_task_comm(tmpname, current); snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid)); - ret = nouveau_cli_create(dev, name, sizeof(*cli), (void **)&cli); + if (!(cli = kzalloc(sizeof(*cli), GFP_KERNEL))) + return ret; + ret = nouveau_cli_init(drm, name, cli); if (ret) - goto out_suspend; + goto done; cli->base.super = false; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { - ret = nvkm_vm_new(nvxx_device(&drm->device), 0, (1ULL << 40), - 0x1000, NULL, &cli->vm); - if (ret) { - nouveau_cli_destroy(cli); - goto out_suspend; - } + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { + ret = nvkm_vm_new(nvxx_device(&drm->client.device), 0, + (1ULL << 40), 0x1000, NULL, &cli->vm); + if (ret) + goto done; nvxx_client(&cli->base)->vm = cli->vm; } @@ -869,10 +869,14 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) list_add(&cli->head, &drm->clients); mutex_unlock(&drm->client.mutex); -out_suspend: +done: + if (ret && cli) { + nouveau_cli_fini(cli); + kfree(cli); + } + pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); - return ret; } @@ -899,7 +903,8 @@ static void nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv) { struct nouveau_cli *cli = nouveau_cli(fpriv); - nouveau_cli_destroy(cli); + nouveau_cli_fini(cli); + kfree(cli); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 42c1fa53d431..eadec2f49ad3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -86,14 +86,17 @@ enum nouveau_drm_handle { struct nouveau_cli { struct nvif_client base; + struct drm_device *dev; + struct mutex mutex; + + struct nvif_device device; + struct nvkm_vm *vm; /*XXX*/ struct list_head head; - struct mutex mutex; void *abi16; struct list_head objects; struct list_head notifys; char name[32]; - struct drm_device *dev; }; static inline struct nouveau_cli * @@ -111,7 +114,6 @@ struct nouveau_drm { struct nouveau_cli client; struct drm_device *dev; - struct nvif_device device; struct list_head clients; struct { diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index fa2d0a978ccc..442e25c17383 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -41,6 +41,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_atomic.h> #include "nouveau_drv.h" #include "nouveau_gem.h" @@ -59,7 +60,7 @@ nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct nouveau_fbdev *fbcon = info->par; struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; int ret; if (info->state != FBINFO_STATE_RUNNING) @@ -91,7 +92,7 @@ nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image) { struct nouveau_fbdev *fbcon = info->par; struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; int ret; if (info->state != FBINFO_STATE_RUNNING) @@ -123,7 +124,7 @@ nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) { struct nouveau_fbdev *fbcon = info->par; struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; int ret; if (info->state != FBINFO_STATE_RUNNING) @@ -265,10 +266,10 @@ nouveau_fbcon_accel_init(struct drm_device *dev) struct fb_info *info = fbcon->helper.fbdev; int ret; - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) ret = nv04_fbcon_accel_init(info); else - if (drm->device.info.family < NV_DEVICE_INFO_V0_FERMI) + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI) ret = nv50_fbcon_accel_init(info); else ret = nvc0_fbcon_accel_init(info); @@ -323,7 +324,7 @@ nouveau_fbcon_create(struct drm_fb_helper *helper, container_of(helper, struct nouveau_fbdev, helper); struct drm_device *dev = fbcon->helper.dev; struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; struct fb_info *info; struct nouveau_framebuffer *fb; struct nouveau_channel *chan; @@ -340,8 +341,9 @@ nouveau_fbcon_create(struct drm_fb_helper *helper, mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); - ret = nouveau_gem_new(dev, mode_cmd.pitches[0] * mode_cmd.height, - 0, NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, &nvbo); + ret = nouveau_gem_new(&drm->client, mode_cmd.pitches[0] * + mode_cmd.height, 0, NOUVEAU_GEM_DOMAIN_VRAM, + 0, 0x0000, &nvbo); if (ret) { NV_ERROR(drm, "failed to allocate framebuffer\n"); goto out; @@ -400,7 +402,8 @@ nouveau_fbcon_create(struct drm_fb_helper *helper, info->screen_base = nvbo_kmap_obj_iovirtual(fb->nvbo); info->screen_size = fb->nvbo->bo.mem.num_pages << PAGE_SHIFT; - drm_fb_helper_fill_fix(info, fb->base.pitches[0], fb->base.depth); + drm_fb_helper_fill_fix(info, fb->base.pitches[0], + fb->base.format->depth); drm_fb_helper_fill_var(info, &fbcon->helper, sizes->fb_width, sizes->fb_height); /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ @@ -530,8 +533,7 @@ nouveau_fbcon_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); - ret = drm_fb_helper_init(dev, &fbcon->helper, - dev->mode_config.num_crtc, 4); + ret = drm_fb_helper_init(dev, &fbcon->helper, 4); if (ret) goto free; @@ -539,16 +541,16 @@ nouveau_fbcon_init(struct drm_device *dev) if (ret) goto fini; - if (drm->device.info.ram_size <= 32 * 1024 * 1024) + if (drm->client.device.info.ram_size <= 32 * 1024 * 1024) preferred_bpp = 8; else - if (drm->device.info.ram_size <= 64 * 1024 * 1024) + if (drm->client.device.info.ram_size <= 64 * 1024 * 1024) preferred_bpp = 16; else preferred_bpp = 32; /* disable all the possible outputs/crtcs before entering KMS mode */ - if (!dev->mode_config.funcs->atomic_commit) + if (!drm_drv_uses_atomic_modeset(dev)) drm_helper_disable_unused_functions(dev); ret = drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp); diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 88ee60d1b907..99e14e3e0fe4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -190,7 +190,7 @@ nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_cha return; ret = nvif_notify_init(&chan->user, nouveau_fence_wait_uevent_handler, - false, G82_CHANNEL_DMA_V0_NTFY_UEVENT, + false, NV826E_V0_NTFY_NON_STALL_INTERRUPT, &(struct nvif_notify_uevent_req) { }, sizeof(struct nvif_notify_uevent_req), sizeof(struct nvif_notify_uevent_rep), diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 201b52b750dd..ca5397beb357 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -175,11 +175,11 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) } int -nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain, +nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain, uint32_t tile_mode, uint32_t tile_flags, struct nouveau_bo **pnvbo) { - struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_drm *drm = nouveau_drm(cli->dev); struct nouveau_bo *nvbo; u32 flags = 0; int ret; @@ -194,7 +194,7 @@ nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain, if (domain & NOUVEAU_GEM_DOMAIN_COHERENT) flags |= TTM_PL_FLAG_UNCACHED; - ret = nouveau_bo_new(dev, size, align, flags, tile_mode, + ret = nouveau_bo_new(cli, size, align, flags, tile_mode, tile_flags, NULL, NULL, pnvbo); if (ret) return ret; @@ -206,12 +206,12 @@ nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain, */ nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART; - if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) + if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) nvbo->valid_domains &= domain; /* Initialize the embedded gem-object. We return a single gem-reference * to the caller, instead of a normal nouveau_bo ttm reference. */ - ret = drm_gem_object_init(dev, &nvbo->gem, nvbo->bo.mem.size); + ret = drm_gem_object_init(drm->dev, &nvbo->gem, nvbo->bo.mem.size); if (ret) { nouveau_bo_ref(NULL, pnvbo); return -ENOMEM; @@ -257,7 +257,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, { struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_cli *cli = nouveau_cli(file_priv); - struct nvkm_fb *fb = nvxx_fb(&drm->device); + struct nvkm_fb *fb = nvxx_fb(&drm->client.device); struct drm_nouveau_gem_new *req = data; struct nouveau_bo *nvbo = NULL; int ret = 0; @@ -267,7 +267,7 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, return -EINVAL; } - ret = nouveau_gem_new(dev, req->info.size, req->align, + ret = nouveau_gem_new(cli, req->info.size, req->align, req->info.domain, req->info.tile_mode, req->info.tile_flags, &nvbo); if (ret) @@ -496,7 +496,7 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, return ret; } - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) { + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { if (nvbo->bo.offset == b->presumed.offset && ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || @@ -767,7 +767,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, push[i].length); } } else - if (drm->device.info.chipset >= 0x25) { + if (drm->client.device.info.chipset >= 0x25) { ret = RING_SPACE(chan, req->nr_push * 2); if (ret) { NV_PRINTK(err, cli, "cal_space: %d\n", ret); @@ -840,7 +840,7 @@ out_next: req->suffix0 = 0x00000000; req->suffix1 = 0x00000000; } else - if (drm->device.info.chipset >= 0x25) { + if (drm->client.device.info.chipset >= 0x25) { req->suffix0 = 0x00020000; req->suffix1 = 0x00000000; } else { diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h index 7e32da2e037a..8fa6ed9ddd3a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.h +++ b/drivers/gpu/drm/nouveau/nouveau_gem.h @@ -16,7 +16,7 @@ nouveau_gem_object(struct drm_gem_object *gem) } /* nouveau_gem.c */ -extern int nouveau_gem_new(struct drm_device *, int size, int align, +extern int nouveau_gem_new(struct nouveau_cli *, u64 size, int align, uint32_t domain, uint32_t tile_mode, uint32_t tile_flags, struct nouveau_bo **); extern void nouveau_gem_object_del(struct drm_gem_object *); diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c index 71f764bf4cc6..23b1670c1c2f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c +++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c @@ -43,7 +43,7 @@ nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); int temp = nvkm_therm_temp_get(therm); if (temp < 0) @@ -69,7 +69,7 @@ nouveau_hwmon_temp1_auto_point1_temp(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST) * 1000); @@ -81,7 +81,7 @@ nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -102,7 +102,7 @@ nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000); @@ -114,7 +114,7 @@ nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -134,7 +134,7 @@ nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) * 1000); @@ -145,7 +145,7 @@ nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -165,7 +165,7 @@ nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000); @@ -176,7 +176,7 @@ nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -197,7 +197,7 @@ nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) * 1000); @@ -209,7 +209,7 @@ nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -230,7 +230,7 @@ nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) * 1000); @@ -243,7 +243,7 @@ nouveau_hwmon_set_critical_temp_hyst(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -263,7 +263,7 @@ nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) * 1000); @@ -275,7 +275,7 @@ nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -296,7 +296,7 @@ nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000); @@ -309,7 +309,7 @@ nouveau_hwmon_set_emergency_temp_hyst(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; if (kstrtol(buf, 10, &value) == -EINVAL) @@ -349,7 +349,7 @@ nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); return snprintf(buf, PAGE_SIZE, "%d\n", nvkm_therm_fan_sense(therm)); } @@ -362,7 +362,7 @@ nouveau_hwmon_get_pwm1_enable(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); int ret; ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MODE); @@ -378,7 +378,7 @@ nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; int ret; @@ -401,7 +401,7 @@ nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); int ret; ret = therm->fan_get(therm); @@ -417,7 +417,7 @@ nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); int ret = -ENODEV; long value; @@ -441,7 +441,7 @@ nouveau_hwmon_get_pwm1_min(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); int ret; ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY); @@ -457,7 +457,7 @@ nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; int ret; @@ -481,7 +481,7 @@ nouveau_hwmon_get_pwm1_max(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); int ret; ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY); @@ -497,7 +497,7 @@ nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); long value; int ret; @@ -521,7 +521,7 @@ nouveau_hwmon_get_in0_input(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_volt *volt = nvxx_volt(&drm->device); + struct nvkm_volt *volt = nvxx_volt(&drm->client.device); int ret; ret = nvkm_volt_get(volt); @@ -540,7 +540,7 @@ nouveau_hwmon_get_in0_min(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_volt *volt = nvxx_volt(&drm->device); + struct nvkm_volt *volt = nvxx_volt(&drm->client.device); if (!volt || !volt->min_uv) return -ENODEV; @@ -557,7 +557,7 @@ nouveau_hwmon_get_in0_max(struct device *d, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_volt *volt = nvxx_volt(&drm->device); + struct nvkm_volt *volt = nvxx_volt(&drm->client.device); if (!volt || !volt->max_uv) return -ENODEV; @@ -584,7 +584,7 @@ nouveau_hwmon_get_power1_input(struct device *d, struct device_attribute *a, { struct drm_device *dev = dev_get_drvdata(d); struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->device); + struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); int result = nvkm_iccsense_read_all(iccsense); if (result < 0) @@ -596,6 +596,32 @@ nouveau_hwmon_get_power1_input(struct device *d, struct device_attribute *a, static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, nouveau_hwmon_get_power1_input, NULL, 0); +static ssize_t +nouveau_hwmon_get_power1_max(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); + return sprintf(buf, "%i\n", iccsense->power_w_max); +} + +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO, + nouveau_hwmon_get_power1_max, NULL, 0); + +static ssize_t +nouveau_hwmon_get_power1_crit(struct device *d, struct device_attribute *a, + char *buf) +{ + struct drm_device *dev = dev_get_drvdata(d); + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); + return sprintf(buf, "%i\n", iccsense->power_w_crit); +} + +static SENSOR_DEVICE_ATTR(power1_crit, S_IRUGO, + nouveau_hwmon_get_power1_crit, NULL, 0); + static struct attribute *hwmon_default_attributes[] = { &sensor_dev_attr_name.dev_attr.attr, &sensor_dev_attr_update_rate.dev_attr.attr, @@ -639,6 +665,12 @@ static struct attribute *hwmon_power_attributes[] = { NULL }; +static struct attribute *hwmon_power_caps_attributes[] = { + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_crit.dev_attr.attr, + NULL +}; + static const struct attribute_group hwmon_default_attrgroup = { .attrs = hwmon_default_attributes, }; @@ -657,6 +689,9 @@ static const struct attribute_group hwmon_in0_attrgroup = { static const struct attribute_group hwmon_power_attrgroup = { .attrs = hwmon_power_attributes, }; +static const struct attribute_group hwmon_power_caps_attrgroup = { + .attrs = hwmon_power_caps_attributes, +}; #endif int @@ -664,9 +699,9 @@ nouveau_hwmon_init(struct drm_device *dev) { #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_therm *therm = nvxx_therm(&drm->device); - struct nvkm_volt *volt = nvxx_volt(&drm->device); - struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->device); + struct nvkm_therm *therm = nvxx_therm(&drm->client.device); + struct nvkm_volt *volt = nvxx_volt(&drm->client.device); + struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); struct nouveau_hwmon *hwmon; struct device *hwmon_dev; int ret = 0; @@ -728,8 +763,16 @@ nouveau_hwmon_init(struct drm_device *dev) if (iccsense && iccsense->data_valid && !list_empty(&iccsense->rails)) { ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_power_attrgroup); + if (ret) goto error; + + if (iccsense->power_w_max && iccsense->power_w_crit) { + ret = sysfs_create_group(&hwmon_dev->kobj, + &hwmon_power_caps_attrgroup); + if (ret) + goto error; + } } hwmon->hwmon = hwmon_dev; @@ -759,6 +802,7 @@ nouveau_hwmon_fini(struct drm_device *dev) sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup); sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_in0_attrgroup); sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_power_attrgroup); + sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_power_caps_attrgroup); hwmon_device_unregister(hwmon->hwmon); } diff --git a/drivers/gpu/drm/nouveau/nouveau_led.c b/drivers/gpu/drm/nouveau/nouveau_led.c index 3e2f1b6cd4df..2c5e0628da12 100644 --- a/drivers/gpu/drm/nouveau/nouveau_led.c +++ b/drivers/gpu/drm/nouveau/nouveau_led.c @@ -38,7 +38,7 @@ nouveau_led_get_brightness(struct led_classdev *led) { struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev; struct nouveau_drm *drm = nouveau_drm(drm_dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; u32 div, duty; div = nvif_rd32(device, 0x61c880) & 0x00ffffff; @@ -55,7 +55,7 @@ nouveau_led_set_brightness(struct led_classdev *led, enum led_brightness value) { struct drm_device *drm_dev = container_of(led, struct nouveau_led, led)->dev; struct nouveau_drm *drm = nouveau_drm(drm_dev); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; u32 input_clk = 27e6; /* PDISPLAY.SOR[1].PWM is connected to the crystal */ u32 freq = 100; /* this is what nvidia uses and it should be good-enough */ @@ -78,7 +78,7 @@ int nouveau_led_init(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvkm_gpio *gpio = nvxx_gpio(&drm->device); + struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); struct dcb_gpio_func logo_led; int ret; @@ -102,6 +102,7 @@ nouveau_led_init(struct drm_device *dev) ret = led_classdev_register(dev->dev, &drm->led->led); if (ret) { kfree(drm->led); + drm->led = NULL; return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_nvif.c b/drivers/gpu/drm/nouveau/nouveau_nvif.c index 15f0925ea13b..b3f29b1ce9ea 100644 --- a/drivers/gpu/drm/nouveau/nouveau_nvif.c +++ b/drivers/gpu/drm/nouveau/nouveau_nvif.c @@ -60,20 +60,15 @@ nvkm_client_ioctl(void *priv, bool super, void *data, u32 size, void **hack) static int nvkm_client_resume(void *priv) { - return nvkm_client_init(priv); + struct nvkm_client *client = priv; + return nvkm_object_init(&client->object); } static int nvkm_client_suspend(void *priv) { - return nvkm_client_fini(priv, true); -} - -static void -nvkm_client_driver_fini(void *priv) -{ struct nvkm_client *client = priv; - nvkm_client_del(&client); + return nvkm_object_fini(&client->object, true); } static int @@ -108,23 +103,14 @@ static int nvkm_client_driver_init(const char *name, u64 device, const char *cfg, const char *dbg, void **ppriv) { - struct nvkm_client *client; - int ret; - - ret = nvkm_client_new(name, device, cfg, dbg, &client); - *ppriv = client; - if (ret) - return ret; - - client->ntfy = nvkm_client_ntfy; - return 0; + return nvkm_client_new(name, device, cfg, dbg, nvkm_client_ntfy, + (struct nvkm_client **)ppriv); } const struct nvif_driver nvif_driver_nvkm = { .name = "nvkm", .init = nvkm_client_driver_init, - .fini = nvkm_client_driver_fini, .suspend = nvkm_client_suspend, .resume = nvkm_client_resume, .ioctl = nvkm_client_ioctl, diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index a0a9704cfe2b..1fefc93af1d7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -60,6 +60,7 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sg) { + struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_bo *nvbo; struct reservation_object *robj = attach->dmabuf->resv; u32 flags = 0; @@ -68,7 +69,7 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, flags = TTM_PL_FLAG_TT; ww_mutex_lock(&robj->lock, NULL); - ret = nouveau_bo_new(dev, attach->dmabuf->size, 0, flags, 0, 0, + ret = nouveau_bo_new(&drm->client, attach->dmabuf->size, 0, flags, 0, 0, sg, robj, &nvbo); ww_mutex_unlock(&robj->lock); if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index db35ab5883ac..b7ab268f7d6f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -24,10 +24,10 @@ nouveau_sgdma_destroy(struct ttm_tt *ttm) } static int -nv04_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *mem) +nv04_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *reg) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; - struct nvkm_mem *node = mem->mm_node; + struct nvkm_mem *node = reg->mm_node; if (ttm->sg) { node->sg = ttm->sg; @@ -36,7 +36,7 @@ nv04_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *mem) node->sg = NULL; node->pages = nvbe->ttm.dma_address; } - node->size = (mem->num_pages << PAGE_SHIFT) >> 12; + node->size = (reg->num_pages << PAGE_SHIFT) >> 12; nvkm_vm_map(&node->vma[0], node); nvbe->node = node; @@ -58,10 +58,10 @@ static struct ttm_backend_func nv04_sgdma_backend = { }; static int -nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *mem) +nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *reg) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; - struct nvkm_mem *node = mem->mm_node; + struct nvkm_mem *node = reg->mm_node; /* noop: bound in move_notify() */ if (ttm->sg) { @@ -71,7 +71,7 @@ nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *mem) node->sg = NULL; node->pages = nvbe->ttm.dma_address; } - node->size = (mem->num_pages << PAGE_SHIFT) >> 12; + node->size = (reg->num_pages << PAGE_SHIFT) >> 12; return 0; } @@ -100,7 +100,7 @@ nouveau_sgdma_create_ttm(struct ttm_bo_device *bdev, if (!nvbe) return NULL; - if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) nvbe->ttm.ttm.func = &nv04_sgdma_backend; else nvbe->ttm.ttm.func = &nv50_sgdma_backend; diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index a6dbe8258040..13e5cc5f07fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -36,7 +36,7 @@ static int nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) { struct nouveau_drm *drm = nouveau_bdev(man->bdev); - struct nvkm_fb *fb = nvxx_fb(&drm->device); + struct nvkm_fb *fb = nvxx_fb(&drm->client.device); man->priv = fb; return 0; } @@ -64,53 +64,53 @@ nvkm_mem_node_cleanup(struct nvkm_mem *node) static void nouveau_vram_manager_del(struct ttm_mem_type_manager *man, - struct ttm_mem_reg *mem) + struct ttm_mem_reg *reg) { struct nouveau_drm *drm = nouveau_bdev(man->bdev); - struct nvkm_ram *ram = nvxx_fb(&drm->device)->ram; - nvkm_mem_node_cleanup(mem->mm_node); - ram->func->put(ram, (struct nvkm_mem **)&mem->mm_node); + struct nvkm_ram *ram = nvxx_fb(&drm->client.device)->ram; + nvkm_mem_node_cleanup(reg->mm_node); + ram->func->put(ram, (struct nvkm_mem **)®->mm_node); } static int nouveau_vram_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, const struct ttm_place *place, - struct ttm_mem_reg *mem) + struct ttm_mem_reg *reg) { struct nouveau_drm *drm = nouveau_bdev(man->bdev); - struct nvkm_ram *ram = nvxx_fb(&drm->device)->ram; + struct nvkm_ram *ram = nvxx_fb(&drm->client.device)->ram; struct nouveau_bo *nvbo = nouveau_bo(bo); struct nvkm_mem *node; u32 size_nc = 0; int ret; - if (drm->device.info.ram_size == 0) + if (drm->client.device.info.ram_size == 0) return -ENOMEM; if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) size_nc = 1 << nvbo->page_shift; - ret = ram->func->get(ram, mem->num_pages << PAGE_SHIFT, - mem->page_alignment << PAGE_SHIFT, size_nc, + ret = ram->func->get(ram, reg->num_pages << PAGE_SHIFT, + reg->page_alignment << PAGE_SHIFT, size_nc, (nvbo->tile_flags >> 8) & 0x3ff, &node); if (ret) { - mem->mm_node = NULL; + reg->mm_node = NULL; return (ret == -ENOSPC) ? 0 : ret; } node->page_shift = nvbo->page_shift; - mem->mm_node = node; - mem->start = node->offset >> PAGE_SHIFT; + reg->mm_node = node; + reg->start = node->offset >> PAGE_SHIFT; return 0; } const struct ttm_mem_type_manager_func nouveau_vram_manager = { - nouveau_vram_manager_init, - nouveau_vram_manager_fini, - nouveau_vram_manager_new, - nouveau_vram_manager_del, + .init = nouveau_vram_manager_init, + .takedown = nouveau_vram_manager_fini, + .get_node = nouveau_vram_manager_new, + .put_node = nouveau_vram_manager_del, }; static int @@ -127,18 +127,18 @@ nouveau_gart_manager_fini(struct ttm_mem_type_manager *man) static void nouveau_gart_manager_del(struct ttm_mem_type_manager *man, - struct ttm_mem_reg *mem) + struct ttm_mem_reg *reg) { - nvkm_mem_node_cleanup(mem->mm_node); - kfree(mem->mm_node); - mem->mm_node = NULL; + nvkm_mem_node_cleanup(reg->mm_node); + kfree(reg->mm_node); + reg->mm_node = NULL; } static int nouveau_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, const struct ttm_place *place, - struct ttm_mem_reg *mem) + struct ttm_mem_reg *reg) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct nouveau_bo *nvbo = nouveau_bo(bo); @@ -150,7 +150,7 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man, node->page_shift = 12; - switch (drm->device.info.family) { + switch (drm->client.device.info.family) { case NV_DEVICE_INFO_V0_TNT: case NV_DEVICE_INFO_V0_CELSIUS: case NV_DEVICE_INFO_V0_KELVIN: @@ -158,7 +158,7 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man, case NV_DEVICE_INFO_V0_CURIE: break; case NV_DEVICE_INFO_V0_TESLA: - if (drm->device.info.chipset != 0x50) + if (drm->client.device.info.chipset != 0x50) node->memtype = (nvbo->tile_flags & 0x7f00) >> 8; break; case NV_DEVICE_INFO_V0_FERMI: @@ -169,12 +169,12 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man, break; default: NV_WARN(drm, "%s: unhandled family type %x\n", __func__, - drm->device.info.family); + drm->client.device.info.family); break; } - mem->mm_node = node; - mem->start = 0; + reg->mm_node = node; + reg->start = 0; return 0; } @@ -184,11 +184,11 @@ nouveau_gart_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) } const struct ttm_mem_type_manager_func nouveau_gart_manager = { - nouveau_gart_manager_init, - nouveau_gart_manager_fini, - nouveau_gart_manager_new, - nouveau_gart_manager_del, - nouveau_gart_manager_debug + .init = nouveau_gart_manager_init, + .takedown = nouveau_gart_manager_fini, + .get_node = nouveau_gart_manager_new, + .put_node = nouveau_gart_manager_del, + .debug = nouveau_gart_manager_debug }; /*XXX*/ @@ -197,7 +197,7 @@ static int nv04_gart_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) { struct nouveau_drm *drm = nouveau_bdev(man->bdev); - struct nvkm_mmu *mmu = nvxx_mmu(&drm->device); + struct nvkm_mmu *mmu = nvxx_mmu(&drm->client.device); struct nv04_mmu *priv = (void *)mmu; struct nvkm_vm *vm = NULL; nvkm_vm_ref(priv->vm, &vm, NULL); @@ -215,20 +215,20 @@ nv04_gart_manager_fini(struct ttm_mem_type_manager *man) } static void -nv04_gart_manager_del(struct ttm_mem_type_manager *man, struct ttm_mem_reg *mem) +nv04_gart_manager_del(struct ttm_mem_type_manager *man, struct ttm_mem_reg *reg) { - struct nvkm_mem *node = mem->mm_node; + struct nvkm_mem *node = reg->mm_node; if (node->vma[0].node) nvkm_vm_put(&node->vma[0]); - kfree(mem->mm_node); - mem->mm_node = NULL; + kfree(reg->mm_node); + reg->mm_node = NULL; } static int nv04_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, const struct ttm_place *place, - struct ttm_mem_reg *mem) + struct ttm_mem_reg *reg) { struct nvkm_mem *node; int ret; @@ -239,15 +239,15 @@ nv04_gart_manager_new(struct ttm_mem_type_manager *man, node->page_shift = 12; - ret = nvkm_vm_get(man->priv, mem->num_pages << 12, node->page_shift, + ret = nvkm_vm_get(man->priv, reg->num_pages << 12, node->page_shift, NV_MEM_ACCESS_RW, &node->vma[0]); if (ret) { kfree(node); return ret; } - mem->mm_node = node; - mem->start = node->vma[0].offset >> PAGE_SHIFT; + reg->mm_node = node; + reg->start = node->vma[0].offset >> PAGE_SHIFT; return 0; } @@ -257,11 +257,11 @@ nv04_gart_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) } const struct ttm_mem_type_manager_func nv04_gart_manager = { - nv04_gart_manager_init, - nv04_gart_manager_fini, - nv04_gart_manager_new, - nv04_gart_manager_del, - nv04_gart_manager_debug + .init = nv04_gart_manager_init, + .takedown = nv04_gart_manager_fini, + .get_node = nv04_gart_manager_new, + .put_node = nv04_gart_manager_del, + .debug = nv04_gart_manager_debug }; int @@ -339,7 +339,7 @@ nouveau_ttm_global_release(struct nouveau_drm *drm) int nouveau_ttm_init(struct nouveau_drm *drm) { - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); struct nvkm_pci *pci = device->pci; struct drm_device *dev = drm->dev; u8 bits; @@ -352,8 +352,8 @@ nouveau_ttm_init(struct nouveau_drm *drm) drm->agp.cma = pci->agp.cma; } - bits = nvxx_mmu(&drm->device)->dma_bits; - if (nvxx_device(&drm->device)->func->pci) { + bits = nvxx_mmu(&drm->client.device)->dma_bits; + if (nvxx_device(&drm->client.device)->func->pci) { if (drm->agp.bridge) bits = 32; } else if (device->func->tegra) { @@ -396,7 +396,7 @@ nouveau_ttm_init(struct nouveau_drm *drm) } /* VRAM init */ - drm->gem.vram_available = drm->device.info.ram_user; + drm->gem.vram_available = drm->client.device.info.ram_user; arch_io_reserve_memtype_wc(device->func->resource_addr(device, 1), device->func->resource_size(device, 1)); @@ -413,7 +413,7 @@ nouveau_ttm_init(struct nouveau_drm *drm) /* GART init */ if (!drm->agp.bridge) { - drm->gem.gart_available = nvxx_mmu(&drm->device)->limit; + drm->gem.gart_available = nvxx_mmu(&drm->client.device)->limit; } else { drm->gem.gart_available = drm->agp.size; } @@ -433,7 +433,7 @@ nouveau_ttm_init(struct nouveau_drm *drm) void nouveau_ttm_fini(struct nouveau_drm *drm) { - struct nvkm_device *device = nvxx_device(&drm->device); + struct nvkm_device *device = nvxx_device(&drm->client.device); ttm_bo_clean_mm(&drm->ttm.bdev, TTM_PL_VRAM); ttm_bo_clean_mm(&drm->ttm.bdev, TTM_PL_TT); diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c index 1fba38622744..afbdbed1a690 100644 --- a/drivers/gpu/drm/nouveau/nouveau_usif.c +++ b/drivers/gpu/drm/nouveau/nouveau_usif.c @@ -103,7 +103,7 @@ usif_notify(const void *header, u32 length, const void *data, u32 size) } break; default: - BUG_ON(1); + BUG(); break; } diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index c6a180a0c284..eef22c6b9665 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -13,13 +13,13 @@ static unsigned int nouveau_vga_set_decode(void *priv, bool state) { struct nouveau_drm *drm = nouveau_drm(priv); - struct nvif_object *device = &drm->device.object; + struct nvif_object *device = &drm->client.device.object; - if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE && - drm->device.info.chipset >= 0x4c) + if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE && + drm->client.device.info.chipset >= 0x4c) nvif_wr32(device, 0x088060, state); else - if (drm->device.info.chipset >= 0x40) + if (drm->client.device.info.chipset >= 0x40) nvif_wr32(device, 0x088054, state); else nvif_wr32(device, 0x001854, state); diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c index 6a2b187e3c3b..01731dbeb3d8 100644 --- a/drivers/gpu/drm/nouveau/nv04_fbcon.c +++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c @@ -136,7 +136,7 @@ nv04_fbcon_accel_init(struct fb_info *info) struct drm_device *dev = nfbdev->helper.dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_channel *chan = drm->channel; - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; int surface_fmt, pattern_fmt, rect_fmt; int ret; diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c index 79bc01111351..6477b7069e14 100644 --- a/drivers/gpu/drm/nouveau/nv17_fence.c +++ b/drivers/gpu/drm/nouveau/nv17_fence.c @@ -76,9 +76,9 @@ nv17_fence_context_new(struct nouveau_channel *chan) { struct nv10_fence_priv *priv = chan->drm->fence; struct nv10_fence_chan *fctx; - struct ttm_mem_reg *mem = &priv->bo->bo.mem; - u32 start = mem->start * PAGE_SIZE; - u32 limit = start + mem->size - 1; + struct ttm_mem_reg *reg = &priv->bo->bo.mem; + u32 start = reg->start * PAGE_SIZE; + u32 limit = start + reg->size - 1; int ret = 0; fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); @@ -129,7 +129,7 @@ nv17_fence_create(struct nouveau_drm *drm) priv->base.context_base = dma_fence_context_alloc(priv->base.contexts); spin_lock_init(&priv->lock); - ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM, 0, 0x0000, NULL, NULL, &priv->bo); if (!ret) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM, false); diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 32097fd615fd..0b4440ffbeae 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -447,18 +447,18 @@ nv50_dmac_ctxdma_new(struct nv50_dmac *dmac, struct nouveau_framebuffer *fb) args.base.target = NV_DMA_V0_TARGET_VRAM; args.base.access = NV_DMA_V0_ACCESS_RDWR; args.base.start = 0; - args.base.limit = drm->device.info.ram_user - 1; + args.base.limit = drm->client.device.info.ram_user - 1; - if (drm->device.info.chipset < 0x80) { + if (drm->client.device.info.chipset < 0x80) { args.nv50.part = NV50_DMA_V0_PART_256; argc += sizeof(args.nv50); } else - if (drm->device.info.chipset < 0xc0) { + if (drm->client.device.info.chipset < 0xc0) { args.nv50.part = NV50_DMA_V0_PART_256; args.nv50.kind = kind; argc += sizeof(args.nv50); } else - if (drm->device.info.chipset < 0xd0) { + if (drm->client.device.info.chipset < 0xd0) { args.gf100.kind = kind; argc += sizeof(args.gf100); } else { @@ -848,7 +848,7 @@ nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, asyw->image.kind = (fb->nvbo->tile_flags & 0x0000ff00) >> 8; if (asyw->image.kind) { asyw->image.layout = 0; - if (drm->device.info.chipset >= 0xc0) + if (drm->client.device.info.chipset >= 0xc0) asyw->image.block = fb->nvbo->tile_mode >> 4; else asyw->image.block = fb->nvbo->tile_mode; @@ -1153,7 +1153,7 @@ nv50_curs_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, if (asyw->state.fb->width != asyw->state.fb->height) return -EINVAL; - switch (asyw->state.fb->pixel_format) { + switch (asyw->state.fb->format->format) { case DRM_FORMAT_ARGB8888: asyh->curs.format = 1; break; default: WARN_ON(1); @@ -1397,7 +1397,7 @@ nv50_base_ntfy_wait_begun(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw) { struct nouveau_drm *drm = nouveau_drm(wndw->plane.dev); struct nv50_disp *disp = nv50_disp(wndw->plane.dev); - if (nvif_msec(&drm->device, 2000ULL, + if (nvif_msec(&drm->client.device, 2000ULL, u32 data = nouveau_bo_rd32(disp->sync, asyw->ntfy.offset / 4); if ((data & 0xc0000000) == 0x40000000) break; @@ -1418,12 +1418,10 @@ static int nv50_base_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, struct nv50_head_atom *asyh) { - const u32 format = asyw->state.fb->pixel_format; - const struct drm_format_info *info; + const struct drm_framebuffer *fb = asyw->state.fb; int ret; - info = drm_format_info(format); - if (!info || !info->depth) + if (!fb->format->depth) return -EINVAL; ret = drm_plane_helper_check_state(&asyw->state, &asyw->clip, @@ -1433,14 +1431,14 @@ nv50_base_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, if (ret) return ret; - asyh->base.depth = info->depth; - asyh->base.cpp = info->cpp[0]; + asyh->base.depth = fb->format->depth; + asyh->base.cpp = fb->format->cpp[0]; asyh->base.x = asyw->state.src.x1 >> 16; asyh->base.y = asyw->state.src.y1 >> 16; asyh->base.w = asyw->state.fb->width; asyh->base.h = asyw->state.fb->height; - switch (format) { + switch (fb->format->format) { case DRM_FORMAT_C8 : asyw->image.format = 0x1e; break; case DRM_FORMAT_RGB565 : asyw->image.format = 0xe8; break; case DRM_FORMAT_XRGB1555 : @@ -1524,7 +1522,7 @@ nv50_base_new(struct nouveau_drm *drm, struct nv50_head *head, return ret; } - ret = nv50_base_create(&drm->device, disp->disp, base->id, + ret = nv50_base_create(&drm->client.device, disp->disp, base->id, disp->sync->bo.offset, &base->chan); if (ret) return ret; @@ -2396,7 +2394,7 @@ static int nv50_head_create(struct drm_device *dev, int index) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvif_device *device = &drm->device; + struct nvif_device *device = &drm->client.device; struct nv50_disp *disp = nv50_disp(dev); struct nv50_head *head; struct nv50_base *base; @@ -2430,7 +2428,7 @@ nv50_head_create(struct drm_device *dev, int index) drm_crtc_helper_add(crtc, &nv50_head_help); drm_mode_crtc_set_gamma_size(crtc, 256); - ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(&drm->client, 8192, 0x100, TTM_PL_FLAG_VRAM, 0, 0x0000, NULL, NULL, &head->base.lut.nvbo); if (!ret) { ret = nouveau_bo_pin(head->base.lut.nvbo, TTM_PL_FLAG_VRAM, true); @@ -2669,7 +2667,7 @@ static int nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) { struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus; struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; @@ -3419,7 +3417,7 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, mstm->outp = outp; mstm->mgr.cbs = &nv50_mstm; - ret = drm_dp_mst_topology_mgr_init(&mstm->mgr, dev->dev, aux, aux_max, + ret = drm_dp_mst_topology_mgr_init(&mstm->mgr, dev, aux, aux_max, max_payloads, conn_base_id); if (ret) return ret; @@ -3625,7 +3623,7 @@ nv50_sor_enable(struct drm_encoder *encoder) nv50_audio_enable(encoder, mode); break; default: - BUG_ON(1); + BUG(); break; } @@ -3659,7 +3657,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) { struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; int type, ret; @@ -3798,7 +3796,7 @@ nv50_pior_enable(struct drm_encoder *encoder) proto = 0x0; break; default: - BUG_ON(1); + BUG(); break; } @@ -3844,7 +3842,7 @@ static int nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) { struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nvkm_i2c *i2c = nvxx_i2c(&drm->device); + struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus = NULL; struct nvkm_i2c_aux *aux = NULL; struct i2c_adapter *ddc; @@ -3917,7 +3915,7 @@ nv50_disp_atomic_commit_core(struct nouveau_drm *drm, u32 interlock) evo_data(push, 0x00000000); nouveau_bo_wr32(disp->sync, 0, 0x00000000); evo_kick(push, core); - if (nvif_msec(&drm->device, 2000ULL, + if (nvif_msec(&drm->client.device, 2000ULL, if (nouveau_bo_rd32(disp->sync, 0)) break; usleep_range(1, 2); @@ -4435,7 +4433,7 @@ module_param_named(atomic, nouveau_atomic, int, 0400); int nv50_display_create(struct drm_device *dev) { - struct nvif_device *device = &nouveau_drm(dev)->device; + struct nvif_device *device = &nouveau_drm(dev)->client.device; struct nouveau_drm *drm = nouveau_drm(dev); struct dcb_table *dcb = &drm->vbios.dcb; struct drm_connector *connector, *tmp; @@ -4459,7 +4457,7 @@ nv50_display_create(struct drm_device *dev) dev->driver->driver_features |= DRIVER_ATOMIC; /* small shared memory area we use for notifiers and semaphores */ - ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM, 0, 0x0000, NULL, NULL, &disp->sync); if (!ret) { ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM, true); diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c index f68c7054fd53..a369d978e267 100644 --- a/drivers/gpu/drm/nouveau/nv50_fence.c +++ b/drivers/gpu/drm/nouveau/nv50_fence.c @@ -37,9 +37,9 @@ nv50_fence_context_new(struct nouveau_channel *chan) { struct nv10_fence_priv *priv = chan->drm->fence; struct nv10_fence_chan *fctx; - struct ttm_mem_reg *mem = &priv->bo->bo.mem; - u32 start = mem->start * PAGE_SIZE; - u32 limit = start + mem->size - 1; + struct ttm_mem_reg *reg = &priv->bo->bo.mem; + u32 start = reg->start * PAGE_SIZE; + u32 limit = start + reg->size - 1; int ret; fctx = chan->fence = kzalloc(sizeof(*fctx), GFP_KERNEL); @@ -82,7 +82,7 @@ nv50_fence_create(struct nouveau_drm *drm) priv->base.context_base = dma_fence_context_alloc(priv->base.contexts); spin_lock_init(&priv->lock); - ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM, 0, 0x0000, NULL, NULL, &priv->bo); if (!ret) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM, false); diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c index f0b322bec7df..bd7a8a1e4ad9 100644 --- a/drivers/gpu/drm/nouveau/nv84_fence.c +++ b/drivers/gpu/drm/nouveau/nv84_fence.c @@ -197,7 +197,7 @@ nv84_fence_destroy(struct nouveau_drm *drm) int nv84_fence_create(struct nouveau_drm *drm) { - struct nvkm_fifo *fifo = nvxx_fifo(&drm->device); + struct nvkm_fifo *fifo = nvxx_fifo(&drm->client.device); struct nv84_fence_priv *priv; u32 domain; int ret; @@ -219,14 +219,14 @@ nv84_fence_create(struct nouveau_drm *drm) mutex_init(&priv->mutex); /* Use VRAM if there is any ; otherwise fallback to system memory */ - domain = drm->device.info.ram_size != 0 ? TTM_PL_FLAG_VRAM : + domain = drm->client.device.info.ram_size != 0 ? TTM_PL_FLAG_VRAM : /* * fences created in sysmem must be non-cached or we * will lose CPU/GPU coherency! */ TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED; - ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0, domain, 0, - 0, NULL, NULL, &priv->bo); + ret = nouveau_bo_new(&drm->client, 16 * priv->base.contexts, 0, + domain, 0, 0, NULL, NULL, &priv->bo); if (ret == 0) { ret = nouveau_bo_pin(priv->bo, domain, false); if (ret == 0) { @@ -239,7 +239,7 @@ nv84_fence_create(struct nouveau_drm *drm) } if (ret == 0) - ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0, + ret = nouveau_bo_new(&drm->client, 16 * priv->base.contexts, 0, TTM_PL_FLAG_TT | TTM_PL_FLAG_UNCACHED, 0, 0, NULL, NULL, &priv->bo_gart); if (ret == 0) { diff --git a/drivers/gpu/drm/nouveau/nvif/Kbuild b/drivers/gpu/drm/nouveau/nvif/Kbuild index ff8ed3a04d06..067b5e9f5ec1 100644 --- a/drivers/gpu/drm/nouveau/nvif/Kbuild +++ b/drivers/gpu/drm/nouveau/nvif/Kbuild @@ -1,4 +1,5 @@ nvif-y := nvif/object.o nvif-y += nvif/client.o nvif-y += nvif/device.o +nvif-y += nvif/driver.o nvif-y += nvif/notify.o diff --git a/drivers/gpu/drm/nouveau/nvif/client.c b/drivers/gpu/drm/nouveau/nvif/client.c index 29c20dfd894d..12db54965c20 100644 --- a/drivers/gpu/drm/nouveau/nvif/client.c +++ b/drivers/gpu/drm/nouveau/nvif/client.c @@ -26,6 +26,9 @@ #include <nvif/driver.h> #include <nvif/ioctl.h> +#include <nvif/class.h> +#include <nvif/if0000.h> + int nvif_client_ioctl(struct nvif_client *client, void *data, u32 size) { @@ -47,37 +50,29 @@ nvif_client_resume(struct nvif_client *client) void nvif_client_fini(struct nvif_client *client) { + nvif_object_fini(&client->object); if (client->driver) { - client->driver->fini(client->object.priv); + if (client->driver->fini) + client->driver->fini(client->object.priv); client->driver = NULL; - client->object.client = NULL; - nvif_object_fini(&client->object); } } -static const struct nvif_driver * -nvif_drivers[] = { -#ifdef __KERNEL__ - &nvif_driver_nvkm, -#else - &nvif_driver_drm, - &nvif_driver_lib, - &nvif_driver_null, -#endif - NULL -}; - int -nvif_client_init(const char *driver, const char *name, u64 device, - const char *cfg, const char *dbg, struct nvif_client *client) +nvif_client_init(struct nvif_client *parent, const char *name, u64 device, + struct nvif_client *client) { + struct nvif_client_v0 args = { .device = device }; struct { struct nvif_ioctl_v0 ioctl; struct nvif_ioctl_nop_v0 nop; - } args = {}; - int ret, i; + } nop = {}; + int ret; - ret = nvif_object_init(NULL, 0, 0, NULL, 0, &client->object); + strncpy(args.name, name, sizeof(args.name)); + ret = nvif_object_init(parent != client ? &parent->object : NULL, + 0, NVIF_CLASS_CLIENT, &args, sizeof(args), + &client->object); if (ret) return ret; @@ -85,19 +80,11 @@ nvif_client_init(const char *driver, const char *name, u64 device, client->object.handle = ~0; client->route = NVIF_IOCTL_V0_ROUTE_NVIF; client->super = true; - - for (i = 0, ret = -EINVAL; (client->driver = nvif_drivers[i]); i++) { - if (!driver || !strcmp(client->driver->name, driver)) { - ret = client->driver->init(name, device, cfg, dbg, - &client->object.priv); - if (!ret || driver) - break; - } - } + client->driver = parent->driver; if (ret == 0) { - ret = nvif_client_ioctl(client, &args, sizeof(args)); - client->version = args.nop.version; + ret = nvif_client_ioctl(client, &nop, sizeof(nop)); + client->version = nop.nop.version; } if (ret) diff --git a/drivers/gpu/drm/nouveau/nvif/driver.c b/drivers/gpu/drm/nouveau/nvif/driver.c new file mode 100644 index 000000000000..701330956e33 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvif/driver.c @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <nvif/driver.h> +#include <nvif/client.h> + +static const struct nvif_driver * +nvif_driver[] = { +#ifdef __KERNEL__ + &nvif_driver_nvkm, +#else + &nvif_driver_drm, + &nvif_driver_lib, + &nvif_driver_null, +#endif + NULL +}; + +int +nvif_driver_init(const char *drv, const char *cfg, const char *dbg, + const char *name, u64 device, struct nvif_client *client) +{ + int ret = -EINVAL, i; + + for (i = 0; (client->driver = nvif_driver[i]); i++) { + if (!drv || !strcmp(client->driver->name, drv)) { + ret = client->driver->init(name, device, cfg, dbg, + &client->object.priv); + if (ret == 0) + break; + client->driver->fini(client->object.priv); + } + } + + if (ret == 0) + ret = nvif_client_init(client, name, device, client); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/Kbuild b/drivers/gpu/drm/nouveau/nvkm/Kbuild index 2832147b676c..e664378f6eda 100644 --- a/drivers/gpu/drm/nouveau/nvkm/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/Kbuild @@ -1,3 +1,4 @@ include $(src)/nvkm/core/Kbuild +include $(src)/nvkm/falcon/Kbuild include $(src)/nvkm/subdev/Kbuild include $(src)/nvkm/engine/Kbuild diff --git a/drivers/gpu/drm/nouveau/nvkm/core/client.c b/drivers/gpu/drm/nouveau/nvkm/core/client.c index e1943910858e..0d3a896892b4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/client.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/client.c @@ -31,6 +31,43 @@ #include <nvif/if0000.h> #include <nvif/unpack.h> +static int +nvkm_uclient_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, + struct nvkm_object **pobject) +{ + union { + struct nvif_client_v0 v0; + } *args = argv; + struct nvkm_client *client; + int ret = -ENOSYS; + + if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))){ + args->v0.name[sizeof(args->v0.name) - 1] = 0; + ret = nvkm_client_new(args->v0.name, args->v0.device, NULL, + NULL, oclass->client->ntfy, &client); + if (ret) + return ret; + } else + return ret; + + client->object.client = oclass->client; + client->object.handle = oclass->handle; + client->object.route = oclass->route; + client->object.token = oclass->token; + client->object.object = oclass->object; + client->debug = oclass->client->debug; + *pobject = &client->object; + return 0; +} + +const struct nvkm_sclass +nvkm_uclient_sclass = { + .oclass = NVIF_CLASS_CLIENT, + .minver = 0, + .maxver = 0, + .ctor = nvkm_uclient_new, +}; + struct nvkm_client_notify { struct nvkm_client *client; struct nvkm_notify n; @@ -138,17 +175,30 @@ nvkm_client_notify_new(struct nvkm_object *object, return ret; } +static const struct nvkm_object_func nvkm_client; +struct nvkm_client * +nvkm_client_search(struct nvkm_client *client, u64 handle) +{ + struct nvkm_object *object; + + object = nvkm_object_search(client, handle, &nvkm_client); + if (IS_ERR(object)) + return (void *)object; + + return nvkm_client(object); +} + static int -nvkm_client_mthd_devlist(struct nvkm_object *object, void *data, u32 size) +nvkm_client_mthd_devlist(struct nvkm_client *client, void *data, u32 size) { union { - struct nv_client_devlist_v0 v0; + struct nvif_client_devlist_v0 v0; } *args = data; int ret = -ENOSYS; - nvif_ioctl(object, "client devlist size %d\n", size); + nvif_ioctl(&client->object, "client devlist size %d\n", size); if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { - nvif_ioctl(object, "client devlist vers %d count %d\n", + nvif_ioctl(&client->object, "client devlist vers %d count %d\n", args->v0.version, args->v0.count); if (size == sizeof(args->v0.device[0]) * args->v0.count) { ret = nvkm_device_list(args->v0.device, args->v0.count); @@ -167,9 +217,10 @@ nvkm_client_mthd_devlist(struct nvkm_object *object, void *data, u32 size) static int nvkm_client_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) { + struct nvkm_client *client = nvkm_client(object); switch (mthd) { - case NV_CLIENT_DEVLIST: - return nvkm_client_mthd_devlist(object, data, size); + case NVIF_CLIENT_V0_DEVLIST: + return nvkm_client_mthd_devlist(client, data, size); default: break; } @@ -190,7 +241,8 @@ nvkm_client_child_get(struct nvkm_object *object, int index, const struct nvkm_sclass *sclass; switch (index) { - case 0: sclass = &nvkm_udevice_sclass; break; + case 0: sclass = &nvkm_uclient_sclass; break; + case 1: sclass = &nvkm_udevice_sclass; break; default: return -EINVAL; } @@ -200,110 +252,54 @@ nvkm_client_child_get(struct nvkm_object *object, int index, return 0; } -static const struct nvkm_object_func -nvkm_client_object_func = { - .mthd = nvkm_client_mthd, - .sclass = nvkm_client_child_get, -}; - -void -nvkm_client_remove(struct nvkm_client *client, struct nvkm_object *object) -{ - if (!RB_EMPTY_NODE(&object->node)) - rb_erase(&object->node, &client->objroot); -} - -bool -nvkm_client_insert(struct nvkm_client *client, struct nvkm_object *object) -{ - struct rb_node **ptr = &client->objroot.rb_node; - struct rb_node *parent = NULL; - - while (*ptr) { - struct nvkm_object *this = - container_of(*ptr, typeof(*this), node); - parent = *ptr; - if (object->object < this->object) - ptr = &parent->rb_left; - else - if (object->object > this->object) - ptr = &parent->rb_right; - else - return false; - } - - rb_link_node(&object->node, parent, ptr); - rb_insert_color(&object->node, &client->objroot); - return true; -} - -struct nvkm_object * -nvkm_client_search(struct nvkm_client *client, u64 handle) -{ - struct rb_node *node = client->objroot.rb_node; - while (node) { - struct nvkm_object *object = - container_of(node, typeof(*object), node); - if (handle < object->object) - node = node->rb_left; - else - if (handle > object->object) - node = node->rb_right; - else - return object; - } - return NULL; -} - -int -nvkm_client_fini(struct nvkm_client *client, bool suspend) +static int +nvkm_client_fini(struct nvkm_object *object, bool suspend) { - struct nvkm_object *object = &client->object; + struct nvkm_client *client = nvkm_client(object); const char *name[2] = { "fini", "suspend" }; int i; nvif_debug(object, "%s notify\n", name[suspend]); for (i = 0; i < ARRAY_SIZE(client->notify); i++) nvkm_client_notify_put(client, i); - return nvkm_object_fini(&client->object, suspend); -} - -int -nvkm_client_init(struct nvkm_client *client) -{ - return nvkm_object_init(&client->object); + return 0; } -void -nvkm_client_del(struct nvkm_client **pclient) +static void * +nvkm_client_dtor(struct nvkm_object *object) { - struct nvkm_client *client = *pclient; + struct nvkm_client *client = nvkm_client(object); int i; - if (client) { - nvkm_client_fini(client, false); - for (i = 0; i < ARRAY_SIZE(client->notify); i++) - nvkm_client_notify_del(client, i); - nvkm_object_dtor(&client->object); - kfree(*pclient); - *pclient = NULL; - } + for (i = 0; i < ARRAY_SIZE(client->notify); i++) + nvkm_client_notify_del(client, i); + return client; } +static const struct nvkm_object_func +nvkm_client = { + .dtor = nvkm_client_dtor, + .fini = nvkm_client_fini, + .mthd = nvkm_client_mthd, + .sclass = nvkm_client_child_get, +}; + int nvkm_client_new(const char *name, u64 device, const char *cfg, - const char *dbg, struct nvkm_client **pclient) + const char *dbg, + int (*ntfy)(const void *, u32, const void *, u32), + struct nvkm_client **pclient) { - struct nvkm_oclass oclass = {}; + struct nvkm_oclass oclass = { .base = nvkm_uclient_sclass }; struct nvkm_client *client; if (!(client = *pclient = kzalloc(sizeof(*client), GFP_KERNEL))) return -ENOMEM; oclass.client = client; - nvkm_object_ctor(&nvkm_client_object_func, &oclass, &client->object); + nvkm_object_ctor(&nvkm_client, &oclass, &client->object); snprintf(client->name, sizeof(client->name), "%s", name); client->device = device; client->debug = nvkm_dbgopt(dbg, "CLIENT"); client->objroot = RB_ROOT; - client->dmaroot = RB_ROOT; + client->ntfy = ntfy; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/core/engine.c b/drivers/gpu/drm/nouveau/nvkm/core/engine.c index ee8e5831fe37..b6c916954a10 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/engine.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/engine.c @@ -27,6 +27,14 @@ #include <subdev/fb.h> +bool +nvkm_engine_chsw_load(struct nvkm_engine *engine) +{ + if (engine->func->chsw_load) + return engine->func->chsw_load(engine); + return false; +} + void nvkm_engine_unref(struct nvkm_engine **pengine) { diff --git a/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c b/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c index b0db51847c36..be19bbe56bba 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c @@ -29,7 +29,8 @@ #include <nvif/ioctl.h> static int -nvkm_ioctl_nop(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_nop(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_nop_v0 v0; @@ -46,7 +47,8 @@ nvkm_ioctl_nop(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_sclass(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_sclass(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_sclass_v0 v0; @@ -78,12 +80,12 @@ nvkm_ioctl_sclass(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_new(struct nvkm_object *parent, void *data, u32 size) +nvkm_ioctl_new(struct nvkm_client *client, + struct nvkm_object *parent, void *data, u32 size) { union { struct nvif_ioctl_new_v0 v0; } *args = data; - struct nvkm_client *client = parent->client; struct nvkm_object *object = NULL; struct nvkm_oclass oclass; int ret = -ENOSYS, i = 0; @@ -104,9 +106,11 @@ nvkm_ioctl_new(struct nvkm_object *parent, void *data, u32 size) do { memset(&oclass, 0x00, sizeof(oclass)); - oclass.client = client; oclass.handle = args->v0.handle; + oclass.route = args->v0.route; + oclass.token = args->v0.token; oclass.object = args->v0.object; + oclass.client = client; oclass.parent = parent; ret = parent->func->sclass(parent, i++, &oclass); if (ret) @@ -125,10 +129,7 @@ nvkm_ioctl_new(struct nvkm_object *parent, void *data, u32 size) ret = nvkm_object_init(object); if (ret == 0) { list_add(&object->head, &parent->tree); - object->route = args->v0.route; - object->token = args->v0.token; - object->object = args->v0.object; - if (nvkm_client_insert(client, object)) { + if (nvkm_object_insert(object)) { client->data = object; return 0; } @@ -142,7 +143,8 @@ nvkm_ioctl_new(struct nvkm_object *parent, void *data, u32 size) } static int -nvkm_ioctl_del(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_del(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_del none; @@ -156,11 +158,12 @@ nvkm_ioctl_del(struct nvkm_object *object, void *data, u32 size) nvkm_object_del(&object); } - return ret; + return ret ? ret : 1; } static int -nvkm_ioctl_mthd(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_mthd(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_mthd_v0 v0; @@ -179,7 +182,8 @@ nvkm_ioctl_mthd(struct nvkm_object *object, void *data, u32 size) static int -nvkm_ioctl_rd(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_rd(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_rd_v0 v0; @@ -218,7 +222,8 @@ nvkm_ioctl_rd(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_wr(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_wr(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_wr_v0 v0; @@ -246,7 +251,8 @@ nvkm_ioctl_wr(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_map(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_map(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_map_v0 v0; @@ -264,7 +270,8 @@ nvkm_ioctl_map(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_unmap(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_unmap(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_unmap none; @@ -280,7 +287,8 @@ nvkm_ioctl_unmap(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_ntfy_new(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_ntfy_new(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { union { struct nvif_ioctl_ntfy_new_v0 v0; @@ -306,9 +314,9 @@ nvkm_ioctl_ntfy_new(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_ntfy_del(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_ntfy_del(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { - struct nvkm_client *client = object->client; union { struct nvif_ioctl_ntfy_del_v0 v0; } *args = data; @@ -325,9 +333,9 @@ nvkm_ioctl_ntfy_del(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_ntfy_get(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_ntfy_get(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { - struct nvkm_client *client = object->client; union { struct nvif_ioctl_ntfy_get_v0 v0; } *args = data; @@ -344,9 +352,9 @@ nvkm_ioctl_ntfy_get(struct nvkm_object *object, void *data, u32 size) } static int -nvkm_ioctl_ntfy_put(struct nvkm_object *object, void *data, u32 size) +nvkm_ioctl_ntfy_put(struct nvkm_client *client, + struct nvkm_object *object, void *data, u32 size) { - struct nvkm_client *client = object->client; union { struct nvif_ioctl_ntfy_put_v0 v0; } *args = data; @@ -364,7 +372,7 @@ nvkm_ioctl_ntfy_put(struct nvkm_object *object, void *data, u32 size) static struct { int version; - int (*func)(struct nvkm_object *, void *, u32); + int (*func)(struct nvkm_client *, struct nvkm_object *, void *, u32); } nvkm_ioctl_v0[] = { { 0x00, nvkm_ioctl_nop }, @@ -389,13 +397,10 @@ nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type, struct nvkm_object *object; int ret; - if (handle) - object = nvkm_client_search(client, handle); - else - object = &client->object; - if (unlikely(!object)) { + object = nvkm_object_search(client, handle, NULL); + if (IS_ERR(object)) { nvif_ioctl(&client->object, "object not found\n"); - return -ENOENT; + return PTR_ERR(object); } if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != object->route) { @@ -407,7 +412,7 @@ nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type, if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { if (nvkm_ioctl_v0[type].version == 0) - ret = nvkm_ioctl_v0[type].func(object, data, size); + ret = nvkm_ioctl_v0[type].func(client, object, data, size); } return ret; @@ -436,12 +441,13 @@ nvkm_ioctl(struct nvkm_client *client, bool supervisor, &args->v0.route, &args->v0.token); } - nvif_ioctl(object, "return %d\n", ret); - if (hack) { - *hack = client->data; - client->data = NULL; + if (ret != 1) { + nvif_ioctl(object, "return %d\n", ret); + if (hack) { + *hack = client->data; + client->data = NULL; + } } - client->super = false; return ret; } diff --git a/drivers/gpu/drm/nouveau/nvkm/core/mm.c b/drivers/gpu/drm/nouveau/nvkm/core/mm.c index 09a1eee8fd33..fd19d652a7ab 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/mm.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/mm.c @@ -147,6 +147,7 @@ nvkm_mm_head(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, if (!this) return -ENOMEM; + this->next = NULL; this->type = type; list_del(&this->fl_entry); *pnode = this; @@ -225,6 +226,7 @@ nvkm_mm_tail(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, if (!this) return -ENOMEM; + this->next = NULL; this->type = type; list_del(&this->fl_entry); *pnode = this; diff --git a/drivers/gpu/drm/nouveau/nvkm/core/object.c b/drivers/gpu/drm/nouveau/nvkm/core/object.c index 67aa7223dcd7..89d2e9da11c7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/object.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/object.c @@ -25,6 +25,65 @@ #include <core/client.h> #include <core/engine.h> +struct nvkm_object * +nvkm_object_search(struct nvkm_client *client, u64 handle, + const struct nvkm_object_func *func) +{ + struct nvkm_object *object; + + if (handle) { + struct rb_node *node = client->objroot.rb_node; + while (node) { + object = rb_entry(node, typeof(*object), node); + if (handle < object->object) + node = node->rb_left; + else + if (handle > object->object) + node = node->rb_right; + else + goto done; + } + return ERR_PTR(-ENOENT); + } else { + object = &client->object; + } + +done: + if (unlikely(func && object->func != func)) + return ERR_PTR(-EINVAL); + return object; +} + +void +nvkm_object_remove(struct nvkm_object *object) +{ + if (!RB_EMPTY_NODE(&object->node)) + rb_erase(&object->node, &object->client->objroot); +} + +bool +nvkm_object_insert(struct nvkm_object *object) +{ + struct rb_node **ptr = &object->client->objroot.rb_node; + struct rb_node *parent = NULL; + + while (*ptr) { + struct nvkm_object *this = rb_entry(*ptr, typeof(*this), node); + parent = *ptr; + if (object->object < this->object) + ptr = &parent->rb_left; + else + if (object->object > this->object) + ptr = &parent->rb_right; + else + return false; + } + + rb_link_node(&object->node, parent, ptr); + rb_insert_color(&object->node, &object->client->objroot); + return true; +} + int nvkm_object_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) { @@ -214,7 +273,7 @@ nvkm_object_del(struct nvkm_object **pobject) struct nvkm_object *object = *pobject; if (object && !WARN_ON(!object->func)) { *pobject = nvkm_object_dtor(object); - nvkm_client_remove(object->client, object); + nvkm_object_remove(object); list_del(&object->head); kfree(*pobject); *pobject = NULL; @@ -230,6 +289,9 @@ nvkm_object_ctor(const struct nvkm_object_func *func, object->engine = nvkm_engine_ref(oclass->engine); object->oclass = oclass->base.oclass; object->handle = oclass->handle; + object->route = oclass->route; + object->token = oclass->token; + object->object = oclass->object; INIT_LIST_HEAD(&object->head); INIT_LIST_HEAD(&object->tree); RB_CLEAR_NODE(&object->node); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index cceda959b47c..273562dd6bbd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -993,7 +993,7 @@ nv92_chipset = { .mc = g84_mc_new, .mmu = nv50_mmu_new, .mxm = nv50_mxm_new, - .pci = g84_pci_new, + .pci = g92_pci_new, .therm = g84_therm_new, .timer = nv41_timer_new, .volt = nv40_volt_new, @@ -2138,6 +2138,7 @@ nv12b_chipset = { .ltc = gm200_ltc_new, .mc = gk20a_mc_new, .mmu = gf100_mmu_new, + .pmu = gm20b_pmu_new, .secboot = gm20b_secboot_new, .timer = gk20a_timer_new, .top = gk104_top_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacnv50.c index 0a1381a84552..070ec5e18fdb 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacnv50.c @@ -137,7 +137,6 @@ nv50_disp_dmac_new_(const struct nv50_disp_dmac_func *func, const struct nvkm_oclass *oclass, struct nvkm_object **pobject) { - struct nvkm_device *device = root->disp->base.engine.subdev.device; struct nvkm_client *client = oclass->client; struct nvkm_dmaobj *dmaobj; struct nv50_disp_dmac *chan; @@ -153,9 +152,9 @@ nv50_disp_dmac_new_(const struct nv50_disp_dmac_func *func, if (ret) return ret; - dmaobj = nvkm_dma_search(device->dma, client, push); - if (!dmaobj) - return -ENOENT; + dmaobj = nvkm_dmaobj_search(client, push); + if (IS_ERR(dmaobj)) + return PTR_ERR(dmaobj); if (dmaobj->limit - dmaobj->start != 0xfff) return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c index 4510cb6e10a8..627b9ee1ddd2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c @@ -39,13 +39,6 @@ g94_sor_loff(struct nvkm_output_dp *outp) } /******************************************************************************* - * TMDS/LVDS - ******************************************************************************/ -static const struct nvkm_output_func -g94_sor_output_func = { -}; - -/******************************************************************************* * DisplayPort ******************************************************************************/ u32 diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c index f11ebdd16c77..11b7b8fd5dda 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/base.c @@ -28,24 +28,6 @@ #include <nvif/class.h> -struct nvkm_dmaobj * -nvkm_dma_search(struct nvkm_dma *dma, struct nvkm_client *client, u64 object) -{ - struct rb_node *node = client->dmaroot.rb_node; - while (node) { - struct nvkm_dmaobj *dmaobj = - container_of(node, typeof(*dmaobj), rb); - if (object < dmaobj->handle) - node = node->rb_left; - else - if (object > dmaobj->handle) - node = node->rb_right; - else - return dmaobj; - } - return NULL; -} - static int nvkm_dma_oclass_new(struct nvkm_device *device, const struct nvkm_oclass *oclass, void *data, u32 size, @@ -53,34 +35,12 @@ nvkm_dma_oclass_new(struct nvkm_device *device, { struct nvkm_dma *dma = nvkm_dma(oclass->engine); struct nvkm_dmaobj *dmaobj = NULL; - struct nvkm_client *client = oclass->client; - struct rb_node **ptr = &client->dmaroot.rb_node; - struct rb_node *parent = NULL; int ret; ret = dma->func->class_new(dma, oclass, data, size, &dmaobj); if (dmaobj) *pobject = &dmaobj->object; - if (ret) - return ret; - - dmaobj->handle = oclass->object; - - while (*ptr) { - struct nvkm_dmaobj *obj = container_of(*ptr, typeof(*obj), rb); - parent = *ptr; - if (dmaobj->handle < obj->handle) - ptr = &parent->rb_left; - else - if (dmaobj->handle > obj->handle) - ptr = &parent->rb_right; - else - return -EEXIST; - } - - rb_link_node(&dmaobj->rb, parent, ptr); - rb_insert_color(&dmaobj->rb, &client->dmaroot); - return 0; + return ret; } static const struct nvkm_device_oclass diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c b/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c index 13c661b1ef14..d20cc0681a88 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/dma/user.c @@ -31,6 +31,19 @@ #include <nvif/cl0002.h> #include <nvif/unpack.h> +static const struct nvkm_object_func nvkm_dmaobj_func; +struct nvkm_dmaobj * +nvkm_dmaobj_search(struct nvkm_client *client, u64 handle) +{ + struct nvkm_object *object; + + object = nvkm_object_search(client, handle, &nvkm_dmaobj_func); + if (IS_ERR(object)) + return (void *)object; + + return nvkm_dmaobj(object); +} + static int nvkm_dmaobj_bind(struct nvkm_object *base, struct nvkm_gpuobj *gpuobj, int align, struct nvkm_gpuobj **pgpuobj) @@ -42,10 +55,7 @@ nvkm_dmaobj_bind(struct nvkm_object *base, struct nvkm_gpuobj *gpuobj, static void * nvkm_dmaobj_dtor(struct nvkm_object *base) { - struct nvkm_dmaobj *dmaobj = nvkm_dmaobj(base); - if (!RB_EMPTY_NODE(&dmaobj->rb)) - rb_erase(&dmaobj->rb, &dmaobj->object.client->dmaroot); - return dmaobj; + return nvkm_dmaobj(base); } static const struct nvkm_object_func @@ -74,7 +84,6 @@ nvkm_dmaobj_ctor(const struct nvkm_dmaobj_func *func, struct nvkm_dma *dma, nvkm_object_ctor(&nvkm_dmaobj_func, oclass, &dmaobj->object); dmaobj->func = func; dmaobj->dma = dma; - RB_CLEAR_NODE(&dmaobj->rb); nvif_ioctl(parent, "create dma size %d\n", *psize); if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c index 1c9682ae3a6b..660ca7aa95ea 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/base.c @@ -32,6 +32,17 @@ #include <nvif/unpack.h> void +nvkm_fifo_recover_chan(struct nvkm_fifo *fifo, int chid) +{ + unsigned long flags; + if (WARN_ON(!fifo->func->recover_chan)) + return; + spin_lock_irqsave(&fifo->lock, flags); + fifo->func->recover_chan(fifo, chid); + spin_unlock_irqrestore(&fifo->lock, flags); +} + +void nvkm_fifo_pause(struct nvkm_fifo *fifo, unsigned long *flags) { return fifo->func->pause(fifo, flags); @@ -55,19 +66,29 @@ nvkm_fifo_chan_put(struct nvkm_fifo *fifo, unsigned long flags, } struct nvkm_fifo_chan * -nvkm_fifo_chan_inst(struct nvkm_fifo *fifo, u64 inst, unsigned long *rflags) +nvkm_fifo_chan_inst_locked(struct nvkm_fifo *fifo, u64 inst) { struct nvkm_fifo_chan *chan; - unsigned long flags; - spin_lock_irqsave(&fifo->lock, flags); list_for_each_entry(chan, &fifo->chan, head) { if (chan->inst->addr == inst) { list_del(&chan->head); list_add(&chan->head, &fifo->chan); - *rflags = flags; return chan; } } + return NULL; +} + +struct nvkm_fifo_chan * +nvkm_fifo_chan_inst(struct nvkm_fifo *fifo, u64 inst, unsigned long *rflags) +{ + struct nvkm_fifo_chan *chan; + unsigned long flags; + spin_lock_irqsave(&fifo->lock, flags); + if ((chan = nvkm_fifo_chan_inst_locked(fifo, inst))) { + *rflags = flags; + return chan; + } spin_unlock_irqrestore(&fifo->lock, flags); return NULL; } @@ -90,9 +111,34 @@ nvkm_fifo_chan_chid(struct nvkm_fifo *fifo, int chid, unsigned long *rflags) return NULL; } +void +nvkm_fifo_kevent(struct nvkm_fifo *fifo, int chid) +{ + nvkm_event_send(&fifo->kevent, 1, chid, NULL, 0); +} + static int -nvkm_fifo_event_ctor(struct nvkm_object *object, void *data, u32 size, - struct nvkm_notify *notify) +nvkm_fifo_kevent_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nvkm_fifo_chan *chan = nvkm_fifo_chan(object); + if (size == 0) { + notify->size = 0; + notify->types = 1; + notify->index = chan->chid; + return 0; + } + return -ENOSYS; +} + +static const struct nvkm_event_func +nvkm_fifo_kevent_func = { + .ctor = nvkm_fifo_kevent_ctor, +}; + +static int +nvkm_fifo_cevent_ctor(struct nvkm_object *object, void *data, u32 size, + struct nvkm_notify *notify) { if (size == 0) { notify->size = 0; @@ -104,10 +150,16 @@ nvkm_fifo_event_ctor(struct nvkm_object *object, void *data, u32 size, } static const struct nvkm_event_func -nvkm_fifo_event_func = { - .ctor = nvkm_fifo_event_ctor, +nvkm_fifo_cevent_func = { + .ctor = nvkm_fifo_cevent_ctor, }; +void +nvkm_fifo_cevent(struct nvkm_fifo *fifo) +{ + nvkm_event_send(&fifo->cevent, 1, 0, NULL, 0); +} + static void nvkm_fifo_uevent_fini(struct nvkm_event *event, int type, int index) { @@ -241,6 +293,7 @@ nvkm_fifo_dtor(struct nvkm_engine *engine) void *data = fifo; if (fifo->func->dtor) data = fifo->func->dtor(fifo); + nvkm_event_fini(&fifo->kevent); nvkm_event_fini(&fifo->cevent); nvkm_event_fini(&fifo->uevent); return data; @@ -283,5 +336,9 @@ nvkm_fifo_ctor(const struct nvkm_fifo_func *func, struct nvkm_device *device, return ret; } - return nvkm_event_init(&nvkm_fifo_event_func, 1, 1, &fifo->cevent); + ret = nvkm_event_init(&nvkm_fifo_cevent_func, 1, 1, &fifo->cevent); + if (ret) + return ret; + + return nvkm_event_init(&nvkm_fifo_kevent_func, 1, nr, &fifo->kevent); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c index dc6d4678f228..fab760ae922f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c @@ -371,9 +371,9 @@ nvkm_fifo_chan_ctor(const struct nvkm_fifo_chan_func *func, /* allocate push buffer ctxdma instance */ if (push) { - dmaobj = nvkm_dma_search(device->dma, oclass->client, push); - if (!dmaobj) - return -ENOENT; + dmaobj = nvkm_dmaobj_search(client, push); + if (IS_ERR(dmaobj)) + return PTR_ERR(dmaobj); ret = nvkm_object_bind(&dmaobj->object, chan->inst, -16, &chan->push); @@ -410,6 +410,6 @@ nvkm_fifo_chan_ctor(const struct nvkm_fifo_chan_func *func, base + user * chan->chid; chan->size = user; - nvkm_event_send(&fifo->cevent, 1, 0, NULL, 0); + nvkm_fifo_cevent(fifo); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h index 55dc415c5c08..d8019bdacd61 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.h @@ -29,5 +29,5 @@ struct nvkm_fifo_chan_oclass { struct nvkm_sclass base; }; -int g84_fifo_chan_ntfy(struct nvkm_fifo_chan *, u32, struct nvkm_event **); +int gf100_fifo_chan_ntfy(struct nvkm_fifo_chan *, u32, struct nvkm_event **); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c index 15a992b3580a..61797c4dd07a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chang84.c @@ -30,12 +30,12 @@ #include <nvif/cl826e.h> -int +static int g84_fifo_chan_ntfy(struct nvkm_fifo_chan *chan, u32 type, struct nvkm_event **pevent) { switch (type) { - case G82_CHANNEL_DMA_V0_NTFY_UEVENT: + case NV826E_V0_NTFY_NON_STALL_INTERRUPT: *pevent = &chan->fifo->uevent; return 0; default: diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c index ec68ea9747d5..cd468ab1db12 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c @@ -68,7 +68,14 @@ gf100_fifo_runlist_commit(struct gf100_fifo *fifo) } nvkm_done(cur); - target = (nvkm_memory_target(cur) == NVKM_MEM_TARGET_HOST) ? 0x3 : 0x0; + switch (nvkm_memory_target(cur)) { + case NVKM_MEM_TARGET_VRAM: target = 0; break; + case NVKM_MEM_TARGET_NCOH: target = 3; break; + default: + mutex_unlock(&subdev->mutex); + WARN_ON(1); + return; + } nvkm_wr32(device, 0x002270, (nvkm_memory_addr(cur) >> 12) | (target << 28)); @@ -183,6 +190,7 @@ gf100_fifo_recover(struct gf100_fifo *fifo, struct nvkm_engine *engine, if (engine != &fifo->base.engine) fifo->recover.mask |= 1ULL << engine->subdev.index; schedule_work(&fifo->recover.work); + nvkm_fifo_kevent(&fifo->base, chid); } static const struct nvkm_enum diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c index 38c0910722c0..3a24788c3185 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c @@ -27,11 +27,71 @@ #include <core/client.h> #include <core/gpuobj.h> #include <subdev/bar.h> +#include <subdev/timer.h> #include <subdev/top.h> #include <engine/sw.h> #include <nvif/class.h> +struct gk104_fifo_engine_status { + bool busy; + bool faulted; + bool chsw; + bool save; + bool load; + struct { + bool tsg; + u32 id; + } prev, next, *chan; +}; + +static void +gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn, + struct gk104_fifo_engine_status *status) +{ + struct nvkm_engine *engine = fifo->engine[engn].engine; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x002640 + (engn * 0x08)); + + status->busy = !!(stat & 0x80000000); + status->faulted = !!(stat & 0x40000000); + status->next.tsg = !!(stat & 0x10000000); + status->next.id = (stat & 0x0fff0000) >> 16; + status->chsw = !!(stat & 0x00008000); + status->save = !!(stat & 0x00004000); + status->load = !!(stat & 0x00002000); + status->prev.tsg = !!(stat & 0x00001000); + status->prev.id = (stat & 0x00000fff); + status->chan = NULL; + + if (status->busy && status->chsw) { + if (status->load && status->save) { + if (engine && nvkm_engine_chsw_load(engine)) + status->chan = &status->next; + else + status->chan = &status->prev; + } else + if (status->load) { + status->chan = &status->next; + } else { + status->chan = &status->prev; + } + } else + if (status->load) { + status->chan = &status->prev; + } + + nvkm_debug(subdev, "engine %02d: busy %d faulted %d chsw %d " + "save %d load %d %sid %d%s-> %sid %d%s\n", + engn, status->busy, status->faulted, + status->chsw, status->save, status->load, + status->prev.tsg ? "tsg" : "ch", status->prev.id, + status->chan == &status->prev ? "*" : " ", + status->next.tsg ? "tsg" : "ch", status->next.id, + status->chan == &status->next ? "*" : " "); +} + static int gk104_fifo_class_get(struct nvkm_fifo *base, int index, const struct nvkm_fifo_chan_oclass **psclass) @@ -83,10 +143,13 @@ gk104_fifo_runlist_commit(struct gk104_fifo *fifo, int runl) } nvkm_done(mem); - if (nvkm_memory_target(mem) == NVKM_MEM_TARGET_VRAM) - target = 0; - else - target = 3; + switch (nvkm_memory_target(mem)) { + case NVKM_MEM_TARGET_VRAM: target = 0; break; + case NVKM_MEM_TARGET_NCOH: target = 3; break; + default: + WARN_ON(1); + return; + } nvkm_wr32(device, 0x002270, (nvkm_memory_addr(mem) >> 12) | (target << 28)); @@ -149,31 +212,137 @@ gk104_fifo_recover_work(struct work_struct *w) nvkm_mask(device, 0x002630, runm, 0x00000000); } +static void gk104_fifo_recover_engn(struct gk104_fifo *fifo, int engn); + static void -gk104_fifo_recover(struct gk104_fifo *fifo, struct nvkm_engine *engine, - struct gk104_fifo_chan *chan) +gk104_fifo_recover_runl(struct gk104_fifo *fifo, int runl) { struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_device *device = subdev->device; - u32 chid = chan->base.chid; - int engn; + const u32 runm = BIT(runl); - nvkm_error(subdev, "%s engine fault on channel %d, recovering...\n", - nvkm_subdev_name[engine->subdev.index], chid); assert_spin_locked(&fifo->base.lock); + if (fifo->recover.runm & runm) + return; + fifo->recover.runm |= runm; - nvkm_mask(device, 0x800004 + (chid * 0x08), 0x00000800, 0x00000800); - list_del_init(&chan->head); - chan->killed = true; + /* Block runlist to prevent channel assignment(s) from changing. */ + nvkm_mask(device, 0x002630, runm, runm); - for (engn = 0; engn < fifo->engine_nr; engn++) { - if (fifo->engine[engn].engine == engine) { - fifo->recover.engm |= BIT(engn); + /* Schedule recovery. */ + nvkm_warn(subdev, "runlist %d: scheduled for recovery\n", runl); + schedule_work(&fifo->recover.work); +} + +static void +gk104_fifo_recover_chan(struct nvkm_fifo *base, int chid) +{ + struct gk104_fifo *fifo = gk104_fifo(base); + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 stat = nvkm_rd32(device, 0x800004 + (chid * 0x08)); + const u32 runl = (stat & 0x000f0000) >> 16; + const bool used = (stat & 0x00000001); + unsigned long engn, engm = fifo->runlist[runl].engm; + struct gk104_fifo_chan *chan; + + assert_spin_locked(&fifo->base.lock); + if (!used) + return; + + /* Lookup SW state for channel, and mark it as dead. */ + list_for_each_entry(chan, &fifo->runlist[runl].chan, head) { + if (chan->base.chid == chid) { + list_del_init(&chan->head); + chan->killed = true; + nvkm_fifo_kevent(&fifo->base, chid); break; } } - fifo->recover.runm |= BIT(chan->runl); + /* Disable channel. */ + nvkm_wr32(device, 0x800004 + (chid * 0x08), stat | 0x00000800); + nvkm_warn(subdev, "channel %d: killed\n", chid); + + /* Block channel assignments from changing during recovery. */ + gk104_fifo_recover_runl(fifo, runl); + + /* Schedule recovery for any engines the channel is on. */ + for_each_set_bit(engn, &engm, fifo->engine_nr) { + struct gk104_fifo_engine_status status; + gk104_fifo_engine_status(fifo, engn, &status); + if (!status.chan || status.chan->id != chid) + continue; + gk104_fifo_recover_engn(fifo, engn); + } +} + +static void +gk104_fifo_recover_engn(struct gk104_fifo *fifo, int engn) +{ + struct nvkm_engine *engine = fifo->engine[engn].engine; + struct nvkm_subdev *subdev = &fifo->base.engine.subdev; + struct nvkm_device *device = subdev->device; + const u32 runl = fifo->engine[engn].runl; + const u32 engm = BIT(engn); + struct gk104_fifo_engine_status status; + int mmui = -1; + + assert_spin_locked(&fifo->base.lock); + if (fifo->recover.engm & engm) + return; + fifo->recover.engm |= engm; + + /* Block channel assignments from changing during recovery. */ + gk104_fifo_recover_runl(fifo, runl); + + /* Determine which channel (if any) is currently on the engine. */ + gk104_fifo_engine_status(fifo, engn, &status); + if (status.chan) { + /* The channel is not longer viable, kill it. */ + gk104_fifo_recover_chan(&fifo->base, status.chan->id); + } + + /* Determine MMU fault ID for the engine, if we're not being + * called from the fault handler already. + */ + if (!status.faulted && engine) { + mmui = nvkm_top_fault_id(device, engine->subdev.index); + if (mmui < 0) { + const struct nvkm_enum *en = fifo->func->fault.engine; + for (; en && en->name; en++) { + if (en->data2 == engine->subdev.index) { + mmui = en->value; + break; + } + } + } + WARN_ON(mmui < 0); + } + + /* Trigger a MMU fault for the engine. + * + * No good idea why this is needed, but nvgpu does something similar, + * and it makes recovery from CTXSW_TIMEOUT a lot more reliable. + */ + if (mmui >= 0) { + nvkm_wr32(device, 0x002a30 + (engn * 0x04), 0x00000100 | mmui); + + /* Wait for fault to trigger. */ + nvkm_msec(device, 2000, + gk104_fifo_engine_status(fifo, engn, &status); + if (status.faulted) + break; + ); + + /* Release MMU fault trigger, and ACK the fault. */ + nvkm_wr32(device, 0x002a30 + (engn * 0x04), 0x00000000); + nvkm_wr32(device, 0x00259c, BIT(mmui)); + nvkm_wr32(device, 0x002100, 0x10000000); + } + + /* Schedule recovery. */ + nvkm_warn(subdev, "engine %d: scheduled for recovery\n", engn); schedule_work(&fifo->recover.work); } @@ -211,34 +380,30 @@ static void gk104_fifo_intr_sched_ctxsw(struct gk104_fifo *fifo) { struct nvkm_device *device = fifo->base.engine.subdev.device; - struct gk104_fifo_chan *chan; - unsigned long flags; + unsigned long flags, engm = 0; u32 engn; + /* We need to ACK the SCHED_ERROR here, and prevent it reasserting, + * as MMU_FAULT cannot be triggered while it's pending. + */ spin_lock_irqsave(&fifo->base.lock, flags); + nvkm_mask(device, 0x002140, 0x00000100, 0x00000000); + nvkm_wr32(device, 0x002100, 0x00000100); + for (engn = 0; engn < fifo->engine_nr; engn++) { - struct nvkm_engine *engine = fifo->engine[engn].engine; - int runl = fifo->engine[engn].runl; - u32 stat = nvkm_rd32(device, 0x002640 + (engn * 0x08)); - u32 busy = (stat & 0x80000000); - u32 next = (stat & 0x0fff0000) >> 16; - u32 chsw = (stat & 0x00008000); - u32 save = (stat & 0x00004000); - u32 load = (stat & 0x00002000); - u32 prev = (stat & 0x00000fff); - u32 chid = load ? next : prev; - (void)save; - - if (!busy || !chsw) + struct gk104_fifo_engine_status status; + + gk104_fifo_engine_status(fifo, engn, &status); + if (!status.busy || !status.chsw) continue; - list_for_each_entry(chan, &fifo->runlist[runl].chan, head) { - if (chan->base.chid == chid && engine) { - gk104_fifo_recover(fifo, engine, chan); - break; - } - } + engm |= BIT(engn); } + + for_each_set_bit(engn, &engm, fifo->engine_nr) + gk104_fifo_recover_engn(fifo, engn); + + nvkm_mask(device, 0x002140, 0x00000100, 0x00000100); spin_unlock_irqrestore(&fifo->base.lock, flags); } @@ -301,6 +466,7 @@ gk104_fifo_intr_fault(struct gk104_fifo *fifo, int unit) struct nvkm_fifo_chan *chan; unsigned long flags; char gpcid[8] = "", en[16] = ""; + int engn; er = nvkm_enum_find(fifo->func->fault.reason, reason); eu = nvkm_enum_find(fifo->func->fault.engine, unit); @@ -342,7 +508,8 @@ gk104_fifo_intr_fault(struct gk104_fifo *fifo, int unit) snprintf(en, sizeof(en), "%s", eu->name); } - chan = nvkm_fifo_chan_inst(&fifo->base, (u64)inst << 12, &flags); + spin_lock_irqsave(&fifo->base.lock, flags); + chan = nvkm_fifo_chan_inst_locked(&fifo->base, (u64)inst << 12); nvkm_error(subdev, "%s fault at %010llx engine %02x [%s] client %02x [%s%s] " @@ -353,9 +520,23 @@ gk104_fifo_intr_fault(struct gk104_fifo *fifo, int unit) (u64)inst << 12, chan ? chan->object.client->name : "unknown"); - if (engine && chan) - gk104_fifo_recover(fifo, engine, (void *)chan); - nvkm_fifo_chan_put(&fifo->base, flags, &chan); + + /* Kill the channel that caused the fault. */ + if (chan) + gk104_fifo_recover_chan(&fifo->base, chan->chid); + + /* Channel recovery will probably have already done this for the + * correct engine(s), but just in case we can't find the channel + * information... + */ + for (engn = 0; engn < fifo->engine_nr && engine; engn++) { + if (fifo->engine[engn].engine == engine) { + gk104_fifo_recover_engn(fifo, engn); + break; + } + } + + spin_unlock_irqrestore(&fifo->base.lock, flags); } static const struct nvkm_bitfield gk104_fifo_pbdma_intr_0[] = { @@ -716,6 +897,7 @@ gk104_fifo_ = { .intr = gk104_fifo_intr, .uevent_init = gk104_fifo_uevent_init, .uevent_fini = gk104_fifo_uevent_fini, + .recover_chan = gk104_fifo_recover_chan, .class_get = gk104_fifo_class_get, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c index 12d964260a29..f9e0377d3d24 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c @@ -32,6 +32,23 @@ #include <nvif/cl906f.h> #include <nvif/unpack.h> +int +gf100_fifo_chan_ntfy(struct nvkm_fifo_chan *chan, u32 type, + struct nvkm_event **pevent) +{ + switch (type) { + case NV906F_V0_NTFY_NON_STALL_INTERRUPT: + *pevent = &chan->fifo->uevent; + return 0; + case NV906F_V0_NTFY_KILLED: + *pevent = &chan->fifo->kevent; + return 0; + default: + break; + } + return -EINVAL; +} + static u32 gf100_fifo_gpfifo_engine_addr(struct nvkm_engine *engine) { @@ -184,7 +201,7 @@ gf100_fifo_gpfifo_func = { .dtor = gf100_fifo_gpfifo_dtor, .init = gf100_fifo_gpfifo_init, .fini = gf100_fifo_gpfifo_fini, - .ntfy = g84_fifo_chan_ntfy, + .ntfy = gf100_fifo_chan_ntfy, .engine_ctor = gf100_fifo_gpfifo_engine_ctor, .engine_dtor = gf100_fifo_gpfifo_engine_dtor, .engine_init = gf100_fifo_gpfifo_engine_init, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c index a2df4f3e7763..8abf6f8ef445 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c @@ -50,6 +50,7 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan) ) < 0) { nvkm_error(subdev, "channel %d [%s] kick timeout\n", chan->base.chid, client->name); + nvkm_fifo_recover_chan(&fifo->base, chan->base.chid); ret = -ETIMEDOUT; } mutex_unlock(&subdev->mutex); @@ -213,7 +214,7 @@ gk104_fifo_gpfifo_func = { .dtor = gk104_fifo_gpfifo_dtor, .init = gk104_fifo_gpfifo_init, .fini = gk104_fifo_gpfifo_fini, - .ntfy = g84_fifo_chan_ntfy, + .ntfy = gf100_fifo_chan_ntfy, .engine_ctor = gk104_fifo_gpfifo_engine_ctor, .engine_dtor = gk104_fifo_gpfifo_engine_dtor, .engine_init = gk104_fifo_gpfifo_engine_init, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h index f6dfb37d9429..f889b13b5e41 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h @@ -6,6 +6,12 @@ int nvkm_fifo_ctor(const struct nvkm_fifo_func *, struct nvkm_device *, int index, int nr, struct nvkm_fifo *); void nvkm_fifo_uevent(struct nvkm_fifo *); +void nvkm_fifo_cevent(struct nvkm_fifo *); +void nvkm_fifo_kevent(struct nvkm_fifo *, int chid); +void nvkm_fifo_recover_chan(struct nvkm_fifo *, int chid); + +struct nvkm_fifo_chan * +nvkm_fifo_chan_inst_locked(struct nvkm_fifo *, u64 inst); struct nvkm_fifo_chan_oclass; struct nvkm_fifo_func { @@ -18,6 +24,7 @@ struct nvkm_fifo_func { void (*start)(struct nvkm_fifo *, unsigned long *); void (*uevent_init)(struct nvkm_fifo *); void (*uevent_fini)(struct nvkm_fifo *); + void (*recover_chan)(struct nvkm_fifo *, int chid); int (*class_get)(struct nvkm_fifo *, int index, const struct nvkm_fifo_chan_oclass **); const struct nvkm_fifo_chan_oclass *chan[]; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c index 467065d1b4e6..cd8cf6f7024c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/base.c @@ -25,6 +25,15 @@ #include <engine/fifo.h> +static bool +nvkm_gr_chsw_load(struct nvkm_engine *engine) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + if (gr->func->chsw_load) + return gr->func->chsw_load(gr); + return false; +} + static void nvkm_gr_tile(struct nvkm_engine *engine, int region, struct nvkm_fb_tile *tile) { @@ -106,6 +115,15 @@ nvkm_gr_init(struct nvkm_engine *engine) return gr->func->init(gr); } +static int +nvkm_gr_fini(struct nvkm_engine *engine, bool suspend) +{ + struct nvkm_gr *gr = nvkm_gr(engine); + if (gr->func->fini) + return gr->func->fini(gr, suspend); + return 0; +} + static void * nvkm_gr_dtor(struct nvkm_engine *engine) { @@ -120,8 +138,10 @@ nvkm_gr = { .dtor = nvkm_gr_dtor, .oneinit = nvkm_gr_oneinit, .init = nvkm_gr_init, + .fini = nvkm_gr_fini, .intr = nvkm_gr_intr, .tile = nvkm_gr_tile, + .chsw_load = nvkm_gr_chsw_load, .fifo.cclass = nvkm_gr_cclass_new, .fifo.sclass = nvkm_gr_oclass_get, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c index ce913300539f..da1ba74682b4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/g84.c @@ -25,6 +25,8 @@ #include <subdev/timer.h> +#include <nvif/class.h> + static const struct nvkm_bitfield nv50_gr_status[] = { { 0x00000001, "BUSY" }, /* set when any bit is set */ { 0x00000002, "DISPATCH" }, @@ -180,11 +182,11 @@ g84_gr = { .tlb_flush = g84_gr_tlb_flush, .units = nv50_gr_units, .sclass = { - { -1, -1, 0x0030, &nv50_gr_object }, - { -1, -1, 0x502d, &nv50_gr_object }, - { -1, -1, 0x5039, &nv50_gr_object }, - { -1, -1, 0x50c0, &nv50_gr_object }, - { -1, -1, 0x8297, &nv50_gr_object }, + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, G82_TESLA, &nv50_gr_object }, {} } }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c index f65a5b0a1a4d..f9acb8a944d2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -702,6 +702,22 @@ gf100_gr_pack_mmio[] = { * PGRAPH engine/subdev functions ******************************************************************************/ +static bool +gf100_gr_chsw_load(struct nvkm_gr *base) +{ + struct gf100_gr *gr = gf100_gr(base); + if (!gr->firmware) { + u32 trace = nvkm_rd32(gr->base.engine.subdev.device, 0x40981c); + if (trace & 0x00000040) + return true; + } else { + u32 mthd = nvkm_rd32(gr->base.engine.subdev.device, 0x409808); + if (mthd & 0x00080000) + return true; + } + return false; +} + int gf100_gr_rops(struct gf100_gr *gr) { @@ -1136,7 +1152,7 @@ gf100_gr_trap_intr(struct gf100_gr *gr) if (trap & 0x00000008) { u32 stat = nvkm_rd32(device, 0x408030); - nvkm_snprintbf(error, sizeof(error), gf100_m2mf_error, + nvkm_snprintbf(error, sizeof(error), gf100_ccache_error, stat & 0x3fffffff); nvkm_error(subdev, "CCACHE %08x [%s]\n", stat, error); nvkm_wr32(device, 0x408030, 0xc0000000); @@ -1391,26 +1407,11 @@ gf100_gr_intr(struct nvkm_gr *base) } static void -gf100_gr_init_fw(struct gf100_gr *gr, u32 fuc_base, +gf100_gr_init_fw(struct nvkm_falcon *falcon, struct gf100_gr_fuc *code, struct gf100_gr_fuc *data) { - struct nvkm_device *device = gr->base.engine.subdev.device; - int i; - - nvkm_wr32(device, fuc_base + 0x01c0, 0x01000000); - for (i = 0; i < data->size / 4; i++) - nvkm_wr32(device, fuc_base + 0x01c4, data->data[i]); - - nvkm_wr32(device, fuc_base + 0x0180, 0x01000000); - for (i = 0; i < code->size / 4; i++) { - if ((i & 0x3f) == 0) - nvkm_wr32(device, fuc_base + 0x0188, i >> 6); - nvkm_wr32(device, fuc_base + 0x0184, code->data[i]); - } - - /* code must be padded to 0x40 words */ - for (; i & 0x3f; i++) - nvkm_wr32(device, fuc_base + 0x0184, 0); + nvkm_falcon_load_dmem(falcon, data->data, 0x0, data->size, 0); + nvkm_falcon_load_imem(falcon, code->data, 0x0, code->size, 0, 0, false); } static void @@ -1455,162 +1456,149 @@ gf100_gr_init_csdata(struct gf100_gr *gr, nvkm_wr32(device, falcon + 0x01c4, star + 4); } -int -gf100_gr_init_ctxctl(struct gf100_gr *gr) +/* Initialize context from an external (secure or not) firmware */ +static int +gf100_gr_init_ctxctl_ext(struct gf100_gr *gr) { - const struct gf100_grctx_func *grctx = gr->func->grctx; struct nvkm_subdev *subdev = &gr->base.engine.subdev; struct nvkm_device *device = subdev->device; struct nvkm_secboot *sb = device->secboot; - int i; int ret = 0; - if (gr->firmware) { - /* load fuc microcode */ - nvkm_mc_unk260(device, 0); - - /* securely-managed falcons must be reset using secure boot */ - if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_FECS)) - ret = nvkm_secboot_reset(sb, NVKM_SECBOOT_FALCON_FECS); - else - gf100_gr_init_fw(gr, 0x409000, &gr->fuc409c, - &gr->fuc409d); - if (ret) - return ret; + /* load fuc microcode */ + nvkm_mc_unk260(device, 0); - if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_GPCCS)) - ret = nvkm_secboot_reset(sb, NVKM_SECBOOT_FALCON_GPCCS); - else - gf100_gr_init_fw(gr, 0x41a000, &gr->fuc41ac, - &gr->fuc41ad); - if (ret) - return ret; + /* securely-managed falcons must be reset using secure boot */ + if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_FECS)) + ret = nvkm_secboot_reset(sb, NVKM_SECBOOT_FALCON_FECS); + else + gf100_gr_init_fw(gr->fecs, &gr->fuc409c, &gr->fuc409d); + if (ret) + return ret; - nvkm_mc_unk260(device, 1); - - /* start both of them running */ - nvkm_wr32(device, 0x409840, 0xffffffff); - nvkm_wr32(device, 0x41a10c, 0x00000000); - nvkm_wr32(device, 0x40910c, 0x00000000); - - if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_GPCCS)) - nvkm_secboot_start(sb, NVKM_SECBOOT_FALCON_GPCCS); - else - nvkm_wr32(device, 0x41a100, 0x00000002); - if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_FECS)) - nvkm_secboot_start(sb, NVKM_SECBOOT_FALCON_FECS); - else - nvkm_wr32(device, 0x409100, 0x00000002); - if (nvkm_msec(device, 2000, - if (nvkm_rd32(device, 0x409800) & 0x00000001) - break; - ) < 0) - return -EBUSY; + if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_GPCCS)) + ret = nvkm_secboot_reset(sb, NVKM_SECBOOT_FALCON_GPCCS); + else + gf100_gr_init_fw(gr->gpccs, &gr->fuc41ac, &gr->fuc41ad); + if (ret) + return ret; + + nvkm_mc_unk260(device, 1); + + /* start both of them running */ + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x41a10c, 0x00000000); + nvkm_wr32(device, 0x40910c, 0x00000000); + + nvkm_falcon_start(gr->gpccs); + nvkm_falcon_start(gr->fecs); - nvkm_wr32(device, 0x409840, 0xffffffff); - nvkm_wr32(device, 0x409500, 0x7fffffff); - nvkm_wr32(device, 0x409504, 0x00000021); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800) & 0x00000001) + break; + ) < 0) + return -EBUSY; + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0x7fffffff); + nvkm_wr32(device, 0x409504, 0x00000021); + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0x00000000); + nvkm_wr32(device, 0x409504, 0x00000010); + if (nvkm_msec(device, 2000, + if ((gr->size = nvkm_rd32(device, 0x409800))) + break; + ) < 0) + return -EBUSY; + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0x00000000); + nvkm_wr32(device, 0x409504, 0x00000016); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800)) + break; + ) < 0) + return -EBUSY; + + nvkm_wr32(device, 0x409840, 0xffffffff); + nvkm_wr32(device, 0x409500, 0x00000000); + nvkm_wr32(device, 0x409504, 0x00000025); + if (nvkm_msec(device, 2000, + if (nvkm_rd32(device, 0x409800)) + break; + ) < 0) + return -EBUSY; - nvkm_wr32(device, 0x409840, 0xffffffff); - nvkm_wr32(device, 0x409500, 0x00000000); - nvkm_wr32(device, 0x409504, 0x00000010); + if (device->chipset >= 0xe0) { + nvkm_wr32(device, 0x409800, 0x00000000); + nvkm_wr32(device, 0x409500, 0x00000001); + nvkm_wr32(device, 0x409504, 0x00000030); if (nvkm_msec(device, 2000, - if ((gr->size = nvkm_rd32(device, 0x409800))) + if (nvkm_rd32(device, 0x409800)) break; ) < 0) return -EBUSY; - nvkm_wr32(device, 0x409840, 0xffffffff); - nvkm_wr32(device, 0x409500, 0x00000000); - nvkm_wr32(device, 0x409504, 0x00000016); + nvkm_wr32(device, 0x409810, 0xb00095c8); + nvkm_wr32(device, 0x409800, 0x00000000); + nvkm_wr32(device, 0x409500, 0x00000001); + nvkm_wr32(device, 0x409504, 0x00000031); if (nvkm_msec(device, 2000, if (nvkm_rd32(device, 0x409800)) break; ) < 0) return -EBUSY; - nvkm_wr32(device, 0x409840, 0xffffffff); - nvkm_wr32(device, 0x409500, 0x00000000); - nvkm_wr32(device, 0x409504, 0x00000025); + nvkm_wr32(device, 0x409810, 0x00080420); + nvkm_wr32(device, 0x409800, 0x00000000); + nvkm_wr32(device, 0x409500, 0x00000001); + nvkm_wr32(device, 0x409504, 0x00000032); if (nvkm_msec(device, 2000, if (nvkm_rd32(device, 0x409800)) break; ) < 0) return -EBUSY; - if (device->chipset >= 0xe0) { - nvkm_wr32(device, 0x409800, 0x00000000); - nvkm_wr32(device, 0x409500, 0x00000001); - nvkm_wr32(device, 0x409504, 0x00000030); - if (nvkm_msec(device, 2000, - if (nvkm_rd32(device, 0x409800)) - break; - ) < 0) - return -EBUSY; - - nvkm_wr32(device, 0x409810, 0xb00095c8); - nvkm_wr32(device, 0x409800, 0x00000000); - nvkm_wr32(device, 0x409500, 0x00000001); - nvkm_wr32(device, 0x409504, 0x00000031); - if (nvkm_msec(device, 2000, - if (nvkm_rd32(device, 0x409800)) - break; - ) < 0) - return -EBUSY; - - nvkm_wr32(device, 0x409810, 0x00080420); - nvkm_wr32(device, 0x409800, 0x00000000); - nvkm_wr32(device, 0x409500, 0x00000001); - nvkm_wr32(device, 0x409504, 0x00000032); - if (nvkm_msec(device, 2000, - if (nvkm_rd32(device, 0x409800)) - break; - ) < 0) - return -EBUSY; + nvkm_wr32(device, 0x409614, 0x00000070); + nvkm_wr32(device, 0x409614, 0x00000770); + nvkm_wr32(device, 0x40802c, 0x00000001); + } - nvkm_wr32(device, 0x409614, 0x00000070); - nvkm_wr32(device, 0x409614, 0x00000770); - nvkm_wr32(device, 0x40802c, 0x00000001); + if (gr->data == NULL) { + int ret = gf100_grctx_generate(gr); + if (ret) { + nvkm_error(subdev, "failed to construct context\n"); + return ret; } + } - if (gr->data == NULL) { - int ret = gf100_grctx_generate(gr); - if (ret) { - nvkm_error(subdev, "failed to construct context\n"); - return ret; - } - } + return 0; +} + +static int +gf100_gr_init_ctxctl_int(struct gf100_gr *gr) +{ + const struct gf100_grctx_func *grctx = gr->func->grctx; + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + struct nvkm_device *device = subdev->device; - return 0; - } else if (!gr->func->fecs.ucode) { return -ENOSYS; } /* load HUB microcode */ nvkm_mc_unk260(device, 0); - nvkm_wr32(device, 0x4091c0, 0x01000000); - for (i = 0; i < gr->func->fecs.ucode->data.size / 4; i++) - nvkm_wr32(device, 0x4091c4, gr->func->fecs.ucode->data.data[i]); - - nvkm_wr32(device, 0x409180, 0x01000000); - for (i = 0; i < gr->func->fecs.ucode->code.size / 4; i++) { - if ((i & 0x3f) == 0) - nvkm_wr32(device, 0x409188, i >> 6); - nvkm_wr32(device, 0x409184, gr->func->fecs.ucode->code.data[i]); - } + nvkm_falcon_load_dmem(gr->fecs, gr->func->fecs.ucode->data.data, 0x0, + gr->func->fecs.ucode->data.size, 0); + nvkm_falcon_load_imem(gr->fecs, gr->func->fecs.ucode->code.data, 0x0, + gr->func->fecs.ucode->code.size, 0, 0, false); /* load GPC microcode */ - nvkm_wr32(device, 0x41a1c0, 0x01000000); - for (i = 0; i < gr->func->gpccs.ucode->data.size / 4; i++) - nvkm_wr32(device, 0x41a1c4, gr->func->gpccs.ucode->data.data[i]); - - nvkm_wr32(device, 0x41a180, 0x01000000); - for (i = 0; i < gr->func->gpccs.ucode->code.size / 4; i++) { - if ((i & 0x3f) == 0) - nvkm_wr32(device, 0x41a188, i >> 6); - nvkm_wr32(device, 0x41a184, gr->func->gpccs.ucode->code.data[i]); - } + nvkm_falcon_load_dmem(gr->gpccs, gr->func->gpccs.ucode->data.data, 0x0, + gr->func->gpccs.ucode->data.size, 0); + nvkm_falcon_load_imem(gr->gpccs, gr->func->gpccs.ucode->code.data, 0x0, + gr->func->gpccs.ucode->code.size, 0, 0, false); nvkm_mc_unk260(device, 1); /* load register lists */ @@ -1642,6 +1630,19 @@ gf100_gr_init_ctxctl(struct gf100_gr *gr) return 0; } +int +gf100_gr_init_ctxctl(struct gf100_gr *gr) +{ + int ret; + + if (gr->firmware) + ret = gf100_gr_init_ctxctl_ext(gr); + else + ret = gf100_gr_init_ctxctl_int(gr); + + return ret; +} + static int gf100_gr_oneinit(struct nvkm_gr *base) { @@ -1711,10 +1712,32 @@ static int gf100_gr_init_(struct nvkm_gr *base) { struct gf100_gr *gr = gf100_gr(base); + struct nvkm_subdev *subdev = &base->engine.subdev; + u32 ret; + nvkm_pmu_pgob(gr->base.engine.subdev.device->pmu, false); + + ret = nvkm_falcon_get(gr->fecs, subdev); + if (ret) + return ret; + + ret = nvkm_falcon_get(gr->gpccs, subdev); + if (ret) + return ret; + return gr->func->init(gr); } +static int +gf100_gr_fini_(struct nvkm_gr *base, bool suspend) +{ + struct gf100_gr *gr = gf100_gr(base); + struct nvkm_subdev *subdev = &gr->base.engine.subdev; + nvkm_falcon_put(gr->gpccs, subdev); + nvkm_falcon_put(gr->fecs, subdev); + return 0; +} + void gf100_gr_dtor_fw(struct gf100_gr_fuc *fuc) { @@ -1737,6 +1760,9 @@ gf100_gr_dtor(struct nvkm_gr *base) gr->func->dtor(gr); kfree(gr->data); + nvkm_falcon_del(&gr->gpccs); + nvkm_falcon_del(&gr->fecs); + gf100_gr_dtor_fw(&gr->fuc409c); gf100_gr_dtor_fw(&gr->fuc409d); gf100_gr_dtor_fw(&gr->fuc41ac); @@ -1755,10 +1781,12 @@ gf100_gr_ = { .dtor = gf100_gr_dtor, .oneinit = gf100_gr_oneinit, .init = gf100_gr_init_, + .fini = gf100_gr_fini_, .intr = gf100_gr_intr, .units = gf100_gr_units, .chan_new = gf100_gr_chan_new, .object_get = gf100_gr_object_get, + .chsw_load = gf100_gr_chsw_load, }; int @@ -1828,6 +1856,7 @@ int gf100_gr_ctor(const struct gf100_gr_func *func, struct nvkm_device *device, int index, struct gf100_gr *gr) { + struct nvkm_subdev *subdev = &gr->base.engine.subdev; int ret; gr->func = func; @@ -1840,7 +1869,11 @@ gf100_gr_ctor(const struct gf100_gr_func *func, struct nvkm_device *device, if (ret) return ret; - return 0; + ret = nvkm_falcon_v1_new(subdev, "FECS", 0x409000, &gr->fecs); + if (ret) + return ret; + + return nvkm_falcon_v1_new(subdev, "GPCCS", 0x41a000, &gr->gpccs); } int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h index 268b8d60ff73..db6ee3b06841 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h @@ -29,6 +29,7 @@ #include <core/gpuobj.h> #include <subdev/ltc.h> #include <subdev/mmu.h> +#include <engine/falcon.h> #define GPC_MAX 32 #define TPC_MAX_PER_GPC 8 @@ -75,6 +76,8 @@ struct gf100_gr { const struct gf100_gr_func *func; struct nvkm_gr base; + struct nvkm_falcon *fecs; + struct nvkm_falcon *gpccs; struct gf100_gr_fuc fuc409c; struct gf100_gr_fuc fuc409d; struct gf100_gr_fuc fuc41ac; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c index 2e68919f00b2..c711a55ce392 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt200.c @@ -23,6 +23,8 @@ */ #include "nv50.h" +#include <nvif/class.h> + static const struct nvkm_gr_func gt200_gr = { .init = nv50_gr_init, @@ -31,11 +33,11 @@ gt200_gr = { .tlb_flush = g84_gr_tlb_flush, .units = nv50_gr_units, .sclass = { - { -1, -1, 0x0030, &nv50_gr_object }, - { -1, -1, 0x502d, &nv50_gr_object }, - { -1, -1, 0x5039, &nv50_gr_object }, - { -1, -1, 0x50c0, &nv50_gr_object }, - { -1, -1, 0x8397, &nv50_gr_object }, + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT200_TESLA, &nv50_gr_object }, {} } }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c index 2bf7aac360cc..fa103df32ec7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gt215.c @@ -23,6 +23,8 @@ */ #include "nv50.h" +#include <nvif/class.h> + static const struct nvkm_gr_func gt215_gr = { .init = nv50_gr_init, @@ -31,12 +33,12 @@ gt215_gr = { .tlb_flush = g84_gr_tlb_flush, .units = nv50_gr_units, .sclass = { - { -1, -1, 0x0030, &nv50_gr_object }, - { -1, -1, 0x502d, &nv50_gr_object }, - { -1, -1, 0x5039, &nv50_gr_object }, - { -1, -1, 0x50c0, &nv50_gr_object }, - { -1, -1, 0x8597, &nv50_gr_object }, - { -1, -1, 0x85c0, &nv50_gr_object }, + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT214_TESLA, &nv50_gr_object }, + { -1, -1, GT214_COMPUTE, &nv50_gr_object }, {} } }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c index 95d5219faf93..eb1a90644752 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp79.c @@ -23,6 +23,8 @@ */ #include "nv50.h" +#include <nvif/class.h> + static const struct nvkm_gr_func mcp79_gr = { .init = nv50_gr_init, @@ -30,11 +32,11 @@ mcp79_gr = { .chan_new = nv50_gr_chan_new, .units = nv50_gr_units, .sclass = { - { -1, -1, 0x0030, &nv50_gr_object }, - { -1, -1, 0x502d, &nv50_gr_object }, - { -1, -1, 0x5039, &nv50_gr_object }, - { -1, -1, 0x50c0, &nv50_gr_object }, - { -1, -1, 0x8397, &nv50_gr_object }, + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT200_TESLA, &nv50_gr_object }, {} } }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c index 027b58e5976b..c91eb56e9327 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/mcp89.c @@ -23,6 +23,8 @@ */ #include "nv50.h" +#include <nvif/class.h> + static const struct nvkm_gr_func mcp89_gr = { .init = nv50_gr_init, @@ -31,12 +33,12 @@ mcp89_gr = { .tlb_flush = g84_gr_tlb_flush, .units = nv50_gr_units, .sclass = { - { -1, -1, 0x0030, &nv50_gr_object }, - { -1, -1, 0x502d, &nv50_gr_object }, - { -1, -1, 0x5039, &nv50_gr_object }, - { -1, -1, 0x50c0, &nv50_gr_object }, - { -1, -1, 0x85c0, &nv50_gr_object }, - { -1, -1, 0x8697, &nv50_gr_object }, + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, + { -1, -1, GT214_COMPUTE, &nv50_gr_object }, + { -1, -1, GT21A_TESLA, &nv50_gr_object }, {} } }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c index fca67de43f2b..df16ffda1749 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv50.c @@ -27,6 +27,8 @@ #include <core/gpuobj.h> #include <engine/fifo.h> +#include <nvif/class.h> + u64 nv50_gr_units(struct nvkm_gr *gr) { @@ -778,11 +780,11 @@ nv50_gr = { .chan_new = nv50_gr_chan_new, .units = nv50_gr_units, .sclass = { - { -1, -1, 0x0030, &nv50_gr_object }, - { -1, -1, 0x502d, &nv50_gr_object }, - { -1, -1, 0x5039, &nv50_gr_object }, - { -1, -1, 0x5097, &nv50_gr_object }, - { -1, -1, 0x50c0, &nv50_gr_object }, + { -1, -1, NV_NULL_CLASS, &nv50_gr_object }, + { -1, -1, NV50_TWOD, &nv50_gr_object }, + { -1, -1, NV50_MEMORY_TO_MEMORY_FORMAT, &nv50_gr_object }, + { -1, -1, NV50_TESLA, &nv50_gr_object }, + { -1, -1, NV50_COMPUTE, &nv50_gr_object }, {} } }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h index d8adcdf6985a..2a52d9f026ec 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/priv.h @@ -15,6 +15,7 @@ struct nvkm_gr_func { void *(*dtor)(struct nvkm_gr *); int (*oneinit)(struct nvkm_gr *); int (*init)(struct nvkm_gr *); + int (*fini)(struct nvkm_gr *, bool); void (*intr)(struct nvkm_gr *); void (*tile)(struct nvkm_gr *, int region, struct nvkm_fb_tile *); int (*tlb_flush)(struct nvkm_gr *); @@ -24,6 +25,7 @@ struct nvkm_gr_func { /* Returns chipset-specific counts of units packed into an u64. */ u64 (*units)(struct nvkm_gr *); + bool (*chsw_load)(struct nvkm_gr *); struct nvkm_sclass sclass[]; }; diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild b/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild new file mode 100644 index 000000000000..584863db9bfc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild @@ -0,0 +1,2 @@ +nvkm-y += nvkm/falcon/base.o +nvkm-y += nvkm/falcon/v1.o diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/base.c b/drivers/gpu/drm/nouveau/nvkm/falcon/base.c new file mode 100644 index 000000000000..4852f313762f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/base.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <subdev/mc.h> + +void +nvkm_falcon_load_imem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u16 tag, u8 port, bool secure) +{ + if (secure && !falcon->secret) { + nvkm_warn(falcon->user, + "writing with secure tag on a non-secure falcon!\n"); + return; + } + + falcon->func->load_imem(falcon, data, start, size, tag, port, + secure); +} + +void +nvkm_falcon_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u8 port) +{ + falcon->func->load_dmem(falcon, data, start, size, port); +} + +void +nvkm_falcon_read_dmem(struct nvkm_falcon *falcon, u32 start, u32 size, u8 port, + void *data) +{ + falcon->func->read_dmem(falcon, start, size, port, data); +} + +void +nvkm_falcon_bind_context(struct nvkm_falcon *falcon, struct nvkm_gpuobj *inst) +{ + if (!falcon->func->bind_context) { + nvkm_error(falcon->user, + "Context binding not supported on this falcon!\n"); + return; + } + + falcon->func->bind_context(falcon, inst); +} + +void +nvkm_falcon_set_start_addr(struct nvkm_falcon *falcon, u32 start_addr) +{ + falcon->func->set_start_addr(falcon, start_addr); +} + +void +nvkm_falcon_start(struct nvkm_falcon *falcon) +{ + falcon->func->start(falcon); +} + +int +nvkm_falcon_enable(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + enum nvkm_devidx id = falcon->owner->index; + int ret; + + nvkm_mc_enable(device, id); + ret = falcon->func->enable(falcon); + if (ret) { + nvkm_mc_disable(device, id); + return ret; + } + + return 0; +} + +void +nvkm_falcon_disable(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + enum nvkm_devidx id = falcon->owner->index; + + /* already disabled, return or wait_idle will timeout */ + if (!nvkm_mc_enabled(device, id)) + return; + + falcon->func->disable(falcon); + + nvkm_mc_disable(device, id); +} + +int +nvkm_falcon_reset(struct nvkm_falcon *falcon) +{ + nvkm_falcon_disable(falcon); + return nvkm_falcon_enable(falcon); +} + +int +nvkm_falcon_wait_for_halt(struct nvkm_falcon *falcon, u32 ms) +{ + return falcon->func->wait_for_halt(falcon, ms); +} + +int +nvkm_falcon_clear_interrupt(struct nvkm_falcon *falcon, u32 mask) +{ + return falcon->func->clear_interrupt(falcon, mask); +} + +void +nvkm_falcon_put(struct nvkm_falcon *falcon, const struct nvkm_subdev *user) +{ + mutex_lock(&falcon->mutex); + if (falcon->user == user) { + nvkm_debug(falcon->user, "released %s falcon\n", falcon->name); + falcon->user = NULL; + } + mutex_unlock(&falcon->mutex); +} + +int +nvkm_falcon_get(struct nvkm_falcon *falcon, const struct nvkm_subdev *user) +{ + mutex_lock(&falcon->mutex); + if (falcon->user) { + nvkm_error(user, "%s falcon already acquired by %s!\n", + falcon->name, nvkm_subdev_name[falcon->user->index]); + mutex_unlock(&falcon->mutex); + return -EBUSY; + } + + nvkm_debug(user, "acquired %s falcon\n", falcon->name); + falcon->user = user; + mutex_unlock(&falcon->mutex); + return 0; +} + +void +nvkm_falcon_ctor(const struct nvkm_falcon_func *func, + struct nvkm_subdev *subdev, const char *name, u32 addr, + struct nvkm_falcon *falcon) +{ + u32 reg; + + falcon->func = func; + falcon->owner = subdev; + falcon->name = name; + falcon->addr = addr; + mutex_init(&falcon->mutex); + + reg = nvkm_falcon_rd32(falcon, 0x12c); + falcon->version = reg & 0xf; + falcon->secret = (reg >> 4) & 0x3; + falcon->code.ports = (reg >> 8) & 0xf; + falcon->data.ports = (reg >> 12) & 0xf; + + reg = nvkm_falcon_rd32(falcon, 0x108); + falcon->code.limit = (reg & 0x1ff) << 8; + falcon->data.limit = (reg & 0x3fe00) >> 1; + + reg = nvkm_falcon_rd32(falcon, 0xc08); + falcon->debug = (reg >> 20) & 0x1; +} + +void +nvkm_falcon_del(struct nvkm_falcon **pfalcon) +{ + if (*pfalcon) { + kfree(*pfalcon); + *pfalcon = NULL; + } +} diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/priv.h b/drivers/gpu/drm/nouveau/nvkm/falcon/priv.h new file mode 100644 index 000000000000..97b56f759d0b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/priv.h @@ -0,0 +1,8 @@ +#ifndef __NVKM_FALCON_PRIV_H__ +#define __NVKM_FALCON_PRIV_H__ +#include <engine/falcon.h> + +void +nvkm_falcon_ctor(const struct nvkm_falcon_func *, struct nvkm_subdev *, + const char *, u32, struct nvkm_falcon *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/v1.c b/drivers/gpu/drm/nouveau/nvkm/falcon/v1.c new file mode 100644 index 000000000000..b537f111f39c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/v1.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <core/gpuobj.h> +#include <core/memory.h> +#include <subdev/timer.h> + +static void +nvkm_falcon_v1_load_imem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u16 tag, u8 port, bool secure) +{ + u8 rem = size % 4; + u32 reg; + int i; + + size -= rem; + + reg = start | BIT(24) | (secure ? BIT(28) : 0); + nvkm_falcon_wr32(falcon, 0x180 + (port * 16), reg); + for (i = 0; i < size / 4; i++) { + /* write new tag every 256B */ + if ((i & 0x3f) == 0) + nvkm_falcon_wr32(falcon, 0x188, tag++); + nvkm_falcon_wr32(falcon, 0x184, ((u32 *)data)[i]); + } + + /* + * If size is not a multiple of 4, mask the last work to ensure garbage + * does not get written + */ + if (rem) { + u32 extra = ((u32 *)data)[i]; + + /* write new tag every 256B */ + if ((i & 0x3f) == 0) + nvkm_falcon_wr32(falcon, 0x188, tag++); + nvkm_falcon_wr32(falcon, 0x184, extra & (BIT(rem * 8) - 1)); + ++i; + } + + /* code must be padded to 0x40 words */ + for (; i & 0x3f; i++) + nvkm_falcon_wr32(falcon, 0x184, 0); +} + +static void +nvkm_falcon_v1_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start, + u32 size, u8 port) +{ + u8 rem = size % 4; + int i; + + size -= rem; + + nvkm_falcon_wr32(falcon, 0x1c0 + (port * 16), start | (0x1 << 24)); + for (i = 0; i < size / 4; i++) + nvkm_falcon_wr32(falcon, 0x1c4, ((u32 *)data)[i]); + + /* + * If size is not a multiple of 4, mask the last work to ensure garbage + * does not get read + */ + if (rem) { + u32 extra = ((u32 *)data)[i]; + + nvkm_falcon_wr32(falcon, 0x1c4, extra & (BIT(rem * 8) - 1)); + } +} + +static void +nvkm_falcon_v1_read_dmem(struct nvkm_falcon *falcon, u32 start, u32 size, + u8 port, void *data) +{ + u8 rem = size % 4; + int i; + + size -= rem; + + nvkm_falcon_wr32(falcon, 0x1c0 + (port * 16), start | (0x1 << 25)); + for (i = 0; i < size / 4; i++) + ((u32 *)data)[i] = nvkm_falcon_rd32(falcon, 0x1c4); + + /* + * If size is not a multiple of 4, mask the last work to ensure garbage + * does not get read + */ + if (rem) { + u32 extra = nvkm_falcon_rd32(falcon, 0x1c4); + + for (i = size; i < size + rem; i++) { + ((u8 *)data)[i] = (u8)(extra & 0xff); + extra >>= 8; + } + } +} + +static void +nvkm_falcon_v1_bind_context(struct nvkm_falcon *falcon, struct nvkm_gpuobj *ctx) +{ + u32 inst_loc; + + /* disable instance block binding */ + if (ctx == NULL) { + nvkm_falcon_wr32(falcon, 0x10c, 0x0); + return; + } + + nvkm_falcon_wr32(falcon, 0x10c, 0x1); + + /* setup apertures - virtual */ + nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_UCODE, 0x4); + nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_VIRT, 0x0); + /* setup apertures - physical */ + nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_PHYS_VID, 0x4); + nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_PHYS_SYS_COH, 0x5); + nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_PHYS_SYS_NCOH, 0x6); + + /* Set context */ + switch (nvkm_memory_target(ctx->memory)) { + case NVKM_MEM_TARGET_VRAM: inst_loc = 0; break; + case NVKM_MEM_TARGET_NCOH: inst_loc = 3; break; + default: + WARN_ON(1); + return; + } + + /* Enable context */ + nvkm_falcon_mask(falcon, 0x048, 0x1, 0x1); + nvkm_falcon_wr32(falcon, 0x480, + ((ctx->addr >> 12) & 0xfffffff) | + (inst_loc << 28) | (1 << 30)); +} + +static void +nvkm_falcon_v1_set_start_addr(struct nvkm_falcon *falcon, u32 start_addr) +{ + nvkm_falcon_wr32(falcon, 0x104, start_addr); +} + +static void +nvkm_falcon_v1_start(struct nvkm_falcon *falcon) +{ + u32 reg = nvkm_falcon_rd32(falcon, 0x100); + + if (reg & BIT(6)) + nvkm_falcon_wr32(falcon, 0x130, 0x2); + else + nvkm_falcon_wr32(falcon, 0x100, 0x2); +} + +static int +nvkm_falcon_v1_wait_for_halt(struct nvkm_falcon *falcon, u32 ms) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + ret = nvkm_wait_msec(device, ms, falcon->addr + 0x100, 0x10, 0x10); + if (ret < 0) + return ret; + + return 0; +} + +static int +nvkm_falcon_v1_clear_interrupt(struct nvkm_falcon *falcon, u32 mask) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + /* clear interrupt(s) */ + nvkm_falcon_mask(falcon, 0x004, mask, mask); + /* wait until interrupts are cleared */ + ret = nvkm_wait_msec(device, 10, falcon->addr + 0x008, mask, 0x0); + if (ret < 0) + return ret; + + return 0; +} + +static int +falcon_v1_wait_idle(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + ret = nvkm_wait_msec(device, 10, falcon->addr + 0x04c, 0xffff, 0x0); + if (ret < 0) + return ret; + + return 0; +} + +static int +nvkm_falcon_v1_enable(struct nvkm_falcon *falcon) +{ + struct nvkm_device *device = falcon->owner->device; + int ret; + + ret = nvkm_wait_msec(device, 10, falcon->addr + 0x10c, 0x6, 0x0); + if (ret < 0) { + nvkm_error(falcon->user, "Falcon mem scrubbing timeout\n"); + return ret; + } + + ret = falcon_v1_wait_idle(falcon); + if (ret) + return ret; + + /* enable IRQs */ + nvkm_falcon_wr32(falcon, 0x010, 0xff); + + return 0; +} + +static void +nvkm_falcon_v1_disable(struct nvkm_falcon *falcon) +{ + /* disable IRQs and wait for any previous code to complete */ + nvkm_falcon_wr32(falcon, 0x014, 0xff); + falcon_v1_wait_idle(falcon); +} + +static const struct nvkm_falcon_func +nvkm_falcon_v1 = { + .load_imem = nvkm_falcon_v1_load_imem, + .load_dmem = nvkm_falcon_v1_load_dmem, + .read_dmem = nvkm_falcon_v1_read_dmem, + .bind_context = nvkm_falcon_v1_bind_context, + .start = nvkm_falcon_v1_start, + .wait_for_halt = nvkm_falcon_v1_wait_for_halt, + .clear_interrupt = nvkm_falcon_v1_clear_interrupt, + .enable = nvkm_falcon_v1_enable, + .disable = nvkm_falcon_v1_disable, + .set_start_addr = nvkm_falcon_v1_set_start_addr, +}; + +int +nvkm_falcon_v1_new(struct nvkm_subdev *owner, const char *name, u32 addr, + struct nvkm_falcon **pfalcon) +{ + struct nvkm_falcon *falcon; + if (!(falcon = *pfalcon = kzalloc(sizeof(*falcon), GFP_KERNEL))) + return -ENOMEM; + nvkm_falcon_ctor(&nvkm_falcon_v1, owner, name, addr, falcon); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild index be57220a2e01..6b4f1e06a38f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/Kbuild @@ -19,6 +19,7 @@ nvkm-y += nvkm/subdev/bios/pcir.o nvkm-y += nvkm/subdev/bios/perf.o nvkm-y += nvkm/subdev/bios/pll.o nvkm-y += nvkm/subdev/bios/pmu.o +nvkm-y += nvkm/subdev/bios/power_budget.o nvkm-y += nvkm/subdev/bios/ramcfg.o nvkm-y += nvkm/subdev/bios/rammap.o nvkm-y += nvkm/subdev/bios/shadow.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c new file mode 100644 index 000000000000..617bfffce4ad --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/power_budget.c @@ -0,0 +1,126 @@ +/* + * Copyright 2016 Karol Herbst + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Karol Herbst + */ +#include <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/power_budget.h> + +static u32 +nvbios_power_budget_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, + u8 *len) +{ + struct bit_entry bit_P; + u32 power_budget; + + if (bit_entry(bios, 'P', &bit_P) || bit_P.version != 2 || + bit_P.length < 0x2c) + return 0; + + power_budget = nvbios_rd32(bios, bit_P.offset + 0x2c); + if (!power_budget) + return 0; + + *ver = nvbios_rd08(bios, power_budget); + switch (*ver) { + case 0x20: + case 0x30: + *hdr = nvbios_rd08(bios, power_budget + 0x1); + *len = nvbios_rd08(bios, power_budget + 0x2); + *cnt = nvbios_rd08(bios, power_budget + 0x3); + return power_budget; + default: + break; + } + + return 0; +} + +int +nvbios_power_budget_header(struct nvkm_bios *bios, + struct nvbios_power_budget *budget) +{ + struct nvkm_subdev *subdev = &bios->subdev; + u8 ver, hdr, cnt, len, cap_entry; + u32 header; + + if (!bios || !budget) + return -EINVAL; + + header = nvbios_power_budget_table(bios, &ver, &hdr, &cnt, &len); + if (!header || !cnt) + return -ENODEV; + + switch (ver) { + case 0x20: + cap_entry = nvbios_rd08(bios, header + 0x9); + break; + case 0x30: + cap_entry = nvbios_rd08(bios, header + 0xa); + break; + default: + cap_entry = 0xff; + } + + if (cap_entry >= cnt && cap_entry != 0xff) { + nvkm_warn(subdev, + "invalid cap_entry in power budget table found\n"); + budget->cap_entry = 0xff; + return -EINVAL; + } + + budget->offset = header; + budget->ver = ver; + budget->hlen = hdr; + budget->elen = len; + budget->ecount = cnt; + + budget->cap_entry = cap_entry; + + return 0; +} + +int +nvbios_power_budget_entry(struct nvkm_bios *bios, + struct nvbios_power_budget *budget, + u8 idx, struct nvbios_power_budget_entry *entry) +{ + u32 entry_offset; + + if (!bios || !budget || !budget->offset || idx >= budget->ecount + || !entry) + return -EINVAL; + + entry_offset = budget->offset + budget->hlen + idx * budget->elen; + + if (budget->ver >= 0x20) { + entry->min_w = nvbios_rd32(bios, entry_offset + 0x2); + entry->avg_w = nvbios_rd32(bios, entry_offset + 0x6); + entry->max_w = nvbios_rd32(bios, entry_offset + 0xa); + } else { + entry->min_w = 0; + entry->max_w = nvbios_rd32(bios, entry_offset + 0x2); + entry->avg_w = entry->max_w; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c index 5841f297973c..da1770e47490 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/nv50.c @@ -112,7 +112,7 @@ read_pll_src(struct nv50_clk *clk, u32 base) M = (coef & 0x000000ff) >> 0; break; default: - BUG_ON(1); + BUG(); } if (M) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c index c714b097719c..59362f8dee22 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c @@ -50,7 +50,7 @@ nv50_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq) ret = nv04_pll_calc(subdev, &info, freq, &N1, &M1, &N2, &M2, &P); if (!ret) { nvkm_error(subdev, "failed pll calculation\n"); - return ret; + return -EINVAL; } switch (info.type) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c index 093223d1df4f..6758da93a3a1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c @@ -445,7 +445,7 @@ gf100_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin, { struct nvkm_ltc *ltc = ram->fb->subdev.device->ltc; struct nvkm_mm *mm = &ram->vram; - struct nvkm_mm_node *r; + struct nvkm_mm_node **node, *r; struct nvkm_mem *mem; int type = (memtype & 0x0ff); int back = (memtype & 0x800); @@ -462,7 +462,6 @@ gf100_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin, if (!mem) return -ENOMEM; - INIT_LIST_HEAD(&mem->regions); mem->size = size; mutex_lock(&ram->fb->subdev.mutex); @@ -478,6 +477,7 @@ gf100_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin, } mem->memtype = type; + node = &mem->mem; do { if (back) ret = nvkm_mm_tail(mm, 0, 1, size, ncmin, align, &r); @@ -489,13 +489,13 @@ gf100_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin, return ret; } - list_add_tail(&r->rl_entry, &mem->regions); + *node = r; + node = &r->next; size -= r->length; } while (size); mutex_unlock(&ram->fb->subdev.mutex); - r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry); - mem->offset = (u64)r->offset << NVKM_RAM_MM_SHIFT; + mem->offset = (u64)mem->mem->offset << NVKM_RAM_MM_SHIFT; *pmem = mem; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c index 7904fa41acef..fb8a1239743d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c @@ -989,7 +989,7 @@ gk104_pll_calc_hiclk(int target_khz, int crystal, int *N1, int *fN1, int *M1, int *P1, int *N2, int *M2, int *P2) { - int best_clk = 0, best_err = target_khz, p_ref, n_ref; + int best_err = target_khz, p_ref, n_ref; bool upper = false; *M1 = 1; @@ -1010,7 +1010,6 @@ gk104_pll_calc_hiclk(int target_khz, int crystal, /* we found a better combination */ if (cur_err < best_err) { best_err = cur_err; - best_clk = cur_clk; *N2 = cur_N; *N1 = n_ref; *P1 = p_ref; @@ -1022,7 +1021,6 @@ gk104_pll_calc_hiclk(int target_khz, int crystal, - target_khz; if (cur_err < best_err) { best_err = cur_err; - best_clk = cur_clk; *N2 = cur_N; *N1 = n_ref; *P1 = p_ref; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c index 0a0e44b75577..017a91de74a0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/rammcp77.c @@ -39,7 +39,7 @@ mcp77_ram_init(struct nvkm_ram *base) u32 flush = ((ram->base.size - (ram->poller_base + 0x40)) >> 5) - 1; /* Enable NISO poller for various clients and set their associated - * read address, only for MCP77/78 and MCP79/7A. (fd#25701) + * read address, only for MCP77/78 and MCP79/7A. (fd#27501) */ nvkm_wr32(device, 0x100c18, dniso); nvkm_mask(device, 0x100c14, 0x00000000, 0x00000001); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c index 87bde8ff2d6b..6549b0588309 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c @@ -496,15 +496,12 @@ nv50_ram_tidy(struct nvkm_ram *base) void __nv50_ram_put(struct nvkm_ram *ram, struct nvkm_mem *mem) { - struct nvkm_mm_node *this; - - while (!list_empty(&mem->regions)) { - this = list_first_entry(&mem->regions, typeof(*this), rl_entry); - - list_del(&this->rl_entry); - nvkm_mm_free(&ram->vram, &this); + struct nvkm_mm_node *next = mem->mem; + struct nvkm_mm_node *node; + while ((node = next)) { + next = node->next; + nvkm_mm_free(&ram->vram, &node); } - nvkm_mm_free(&ram->tags, &mem->tag); } @@ -530,7 +527,7 @@ nv50_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin, { struct nvkm_mm *heap = &ram->vram; struct nvkm_mm *tags = &ram->tags; - struct nvkm_mm_node *r; + struct nvkm_mm_node **node, *r; struct nvkm_mem *mem; int comp = (memtype & 0x300) >> 8; int type = (memtype & 0x07f); @@ -559,11 +556,11 @@ nv50_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin, comp = 0; } - INIT_LIST_HEAD(&mem->regions); mem->memtype = (comp << 7) | type; mem->size = max; type = nv50_fb_memtype[type]; + node = &mem->mem; do { if (back) ret = nvkm_mm_tail(heap, 0, type, max, min, align, &r); @@ -575,13 +572,13 @@ nv50_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin, return ret; } - list_add_tail(&r->rl_entry, &mem->regions); + *node = r; + node = &r->next; max -= r->length; } while (max); mutex_unlock(&ram->fb->subdev.mutex); - r = list_first_entry(&mem->regions, struct nvkm_mm_node, rl_entry); - mem->offset = (u64)r->offset << NVKM_RAM_MM_SHIFT; + mem->offset = (u64)mem->mem->offset << NVKM_RAM_MM_SHIFT; *pmem = mem; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c index f0af2a381eea..fecfa6afcf54 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/iccsense/base.c @@ -26,6 +26,7 @@ #include <subdev/bios.h> #include <subdev/bios/extdev.h> #include <subdev/bios/iccsense.h> +#include <subdev/bios/power_budget.h> #include <subdev/i2c.h> static bool @@ -216,10 +217,25 @@ nvkm_iccsense_oneinit(struct nvkm_subdev *subdev) { struct nvkm_iccsense *iccsense = nvkm_iccsense(subdev); struct nvkm_bios *bios = subdev->device->bios; + struct nvbios_power_budget budget; struct nvbios_iccsense stbl; - int i; + int i, ret; - if (!bios || nvbios_iccsense_parse(bios, &stbl) || !stbl.nr_entry) + if (!bios) + return 0; + + ret = nvbios_power_budget_header(bios, &budget); + if (!ret && budget.cap_entry != 0xff) { + struct nvbios_power_budget_entry entry; + ret = nvbios_power_budget_entry(bios, &budget, + budget.cap_entry, &entry); + if (!ret) { + iccsense->power_w_max = entry.avg_w; + iccsense->power_w_crit = entry.max_w; + } + } + + if (nvbios_iccsense_parse(bios, &stbl) || !stbl.nr_entry) return 0; iccsense->data_valid = true; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c index a6a7fa0d7679..9dec58ec3d9f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c @@ -116,7 +116,7 @@ struct gk20a_instmem { static enum nvkm_memory_target gk20a_instobj_target(struct nvkm_memory *memory) { - return NVKM_MEM_TARGET_HOST; + return NVKM_MEM_TARGET_NCOH; } static u64 @@ -305,11 +305,11 @@ gk20a_instobj_dtor_iommu(struct nvkm_memory *memory) struct gk20a_instobj_iommu *node = gk20a_instobj_iommu(memory); struct gk20a_instmem *imem = node->base.imem; struct device *dev = imem->base.subdev.device->dev; - struct nvkm_mm_node *r; + struct nvkm_mm_node *r = node->base.mem.mem; unsigned long flags; int i; - if (unlikely(list_empty(&node->base.mem.regions))) + if (unlikely(!r)) goto out; spin_lock_irqsave(&imem->lock, flags); @@ -320,9 +320,6 @@ gk20a_instobj_dtor_iommu(struct nvkm_memory *memory) spin_unlock_irqrestore(&imem->lock, flags); - r = list_first_entry(&node->base.mem.regions, struct nvkm_mm_node, - rl_entry); - /* clear IOMMU bit to unmap pages */ r->offset &= ~BIT(imem->iommu_bit - imem->iommu_pgshift); @@ -404,10 +401,7 @@ gk20a_instobj_ctor_dma(struct gk20a_instmem *imem, u32 npages, u32 align, node->r.length = (npages << PAGE_SHIFT) >> 12; node->base.mem.offset = node->handle; - - INIT_LIST_HEAD(&node->base.mem.regions); - list_add_tail(&node->r.rl_entry, &node->base.mem.regions); - + node->base.mem.mem = &node->r; return 0; } @@ -484,10 +478,7 @@ gk20a_instobj_ctor_iommu(struct gk20a_instmem *imem, u32 npages, u32 align, r->offset |= BIT(imem->iommu_bit - imem->iommu_pgshift); node->base.mem.offset = ((u64)r->offset) << imem->iommu_pgshift; - - INIT_LIST_HEAD(&node->base.mem.regions); - list_add_tail(&r->rl_entry, &node->base.mem.regions); - + node->base.mem.mem = r; return 0; release_area: diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c index 6b25e25f9eba..09f669ac6630 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/base.c @@ -161,6 +161,16 @@ nvkm_mc_enable(struct nvkm_device *device, enum nvkm_devidx devidx) } } +bool +nvkm_mc_enabled(struct nvkm_device *device, enum nvkm_devidx devidx) +{ + u64 pmc_enable = nvkm_mc_reset_mask(device, false, devidx); + + return (pmc_enable != 0) && + ((nvkm_rd32(device, 0x000200) & pmc_enable) == pmc_enable); +} + + static int nvkm_mc_fini(struct nvkm_subdev *subdev, bool suspend) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c index 5df9669ea39c..d06ad2c372bf 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c @@ -31,7 +31,7 @@ nvkm_vm_map_at(struct nvkm_vma *vma, u64 delta, struct nvkm_mem *node) { struct nvkm_vm *vm = vma->vm; struct nvkm_mmu *mmu = vm->mmu; - struct nvkm_mm_node *r; + struct nvkm_mm_node *r = node->mem; int big = vma->node->type != mmu->func->spg_shift; u32 offset = vma->node->offset + (delta >> 12); u32 bits = vma->node->type - 12; @@ -41,7 +41,7 @@ nvkm_vm_map_at(struct nvkm_vma *vma, u64 delta, struct nvkm_mem *node) u32 end, len; delta = 0; - list_for_each_entry(r, &node->regions, rl_entry) { + while (r) { u64 phys = (u64)r->offset << 12; u32 num = r->length >> bits; @@ -65,7 +65,8 @@ nvkm_vm_map_at(struct nvkm_vma *vma, u64 delta, struct nvkm_mem *node) delta += (u64)len << vma->node->type; } - } + r = r->next; + }; mmu->func->flush(vm); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild index 2a31b7d66a6d..87bf41cef0c6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild @@ -6,6 +6,7 @@ nvkm-y += nvkm/subdev/pci/nv40.o nvkm-y += nvkm/subdev/pci/nv46.o nvkm-y += nvkm/subdev/pci/nv4c.o nvkm-y += nvkm/subdev/pci/g84.o +nvkm-y += nvkm/subdev/pci/g92.o nvkm-y += nvkm/subdev/pci/g94.o nvkm-y += nvkm/subdev/pci/gf100.o nvkm-y += nvkm/subdev/pci/gf106.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c new file mode 100644 index 000000000000..48874359d5f6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c @@ -0,0 +1,57 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +int +g92_pcie_version_supported(struct nvkm_pci *pci) +{ + if ((nvkm_pci_rd32(pci, 0x460) & 0x200) == 0x200) + return 2; + return 1; +} + +static const struct nvkm_pci_func +g92_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv46_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +g92_pci_new(struct nvkm_device *device, int index, struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&g92_pci_func, device, index, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c index 43444123bc04..09adb37a5664 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c @@ -23,14 +23,6 @@ */ #include "priv.h" -int -g94_pcie_version_supported(struct nvkm_pci *pci) -{ - if ((nvkm_pci_rd32(pci, 0x460) & 0x200) == 0x200) - return 2; - return 1; -} - static const struct nvkm_pci_func g94_pci_func = { .init = g84_pci_init, @@ -47,7 +39,7 @@ g94_pci_func = { .pcie.set_version = g84_pcie_set_version, .pcie.version = g84_pcie_version, - .pcie.version_supported = g94_pcie_version_supported, + .pcie.version_supported = g92_pcie_version_supported, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c index e30ea676baf6..00a5e7d3ee9d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c @@ -92,7 +92,7 @@ gf100_pci_func = { .pcie.set_version = gf100_pcie_set_version, .pcie.version = gf100_pcie_version, - .pcie.version_supported = g94_pcie_version_supported, + .pcie.version_supported = g92_pcie_version_supported, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c index c3b798c5c6dd..11bf419afe3f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c @@ -39,7 +39,7 @@ gf106_pci_func = { .pcie.set_version = gf100_pcie_set_version, .pcie.version = gf100_pcie_version, - .pcie.version_supported = g94_pcie_version_supported, + .pcie.version_supported = g92_pcie_version_supported, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h index 23de3180aae5..86921ec962d6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h @@ -44,7 +44,7 @@ enum nvkm_pcie_speed g84_pcie_max_speed(struct nvkm_pci *); int g84_pcie_init(struct nvkm_pci *); int g84_pcie_set_link(struct nvkm_pci *, enum nvkm_pcie_speed, u8); -int g94_pcie_version_supported(struct nvkm_pci *); +int g92_pcie_version_supported(struct nvkm_pci *); void gf100_pcie_set_version(struct nvkm_pci *, u8); int gf100_pcie_version(struct nvkm_pci *); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild index 51fb4bf94a44..ca57c1e491b0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/Kbuild @@ -8,5 +8,6 @@ nvkm-y += nvkm/subdev/pmu/gk110.o nvkm-y += nvkm/subdev/pmu/gk208.o nvkm-y += nvkm/subdev/pmu/gk20a.o nvkm-y += nvkm/subdev/pmu/gm107.o +nvkm-y += nvkm/subdev/pmu/gm20b.o nvkm-y += nvkm/subdev/pmu/gp100.o nvkm-y += nvkm/subdev/pmu/gp102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c index e611ce80f8ef..a73f690eb4b5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c @@ -116,6 +116,8 @@ nvkm_pmu_init(struct nvkm_subdev *subdev) static void * nvkm_pmu_dtor(struct nvkm_subdev *subdev) { + struct nvkm_pmu *pmu = nvkm_pmu(subdev); + nvkm_falcon_del(&pmu->falcon); return nvkm_pmu(subdev); } @@ -129,15 +131,22 @@ nvkm_pmu = { }; int +nvkm_pmu_ctor(const struct nvkm_pmu_func *func, struct nvkm_device *device, + int index, struct nvkm_pmu *pmu) +{ + nvkm_subdev_ctor(&nvkm_pmu, device, index, &pmu->subdev); + pmu->func = func; + INIT_WORK(&pmu->recv.work, nvkm_pmu_recv); + init_waitqueue_head(&pmu->recv.wait); + return nvkm_falcon_v1_new(&pmu->subdev, "PMU", 0x10a000, &pmu->falcon); +} + +int nvkm_pmu_new_(const struct nvkm_pmu_func *func, struct nvkm_device *device, int index, struct nvkm_pmu **ppmu) { struct nvkm_pmu *pmu; if (!(pmu = *ppmu = kzalloc(sizeof(*pmu), GFP_KERNEL))) return -ENOMEM; - nvkm_subdev_ctor(&nvkm_pmu, device, index, &pmu->subdev); - pmu->func = func; - INIT_WORK(&pmu->recv.work, nvkm_pmu_recv); - init_waitqueue_head(&pmu->recv.wait); - return 0; + return nvkm_pmu_ctor(func, device, index, *ppmu); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c index f996d90c9f0d..9ca0db796cbe 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c @@ -19,7 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#define gk20a_pmu(p) container_of((p), struct gk20a_pmu, base.subdev) +#define gk20a_pmu(p) container_of((p), struct gk20a_pmu, base) #include "priv.h" #include <subdev/clk.h> @@ -43,9 +43,8 @@ struct gk20a_pmu { }; struct gk20a_pmu_dvfs_dev_status { - unsigned long total; - unsigned long busy; - int cur_state; + u32 total; + u32 busy; }; static int @@ -56,13 +55,12 @@ gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state) return nvkm_clk_astate(clk, *state, 0, false); } -static int +static void gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state) { struct nvkm_clk *clk = pmu->base.subdev.device->clk; *state = clk->pstate; - return 0; } static int @@ -90,28 +88,26 @@ gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu *pmu, *state = level; - if (level == cur_level) - return 0; - else - return 1; + return (level != cur_level); } -static int +static void gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu *pmu, struct gk20a_pmu_dvfs_dev_status *status) { - struct nvkm_device *device = pmu->base.subdev.device; - status->busy = nvkm_rd32(device, 0x10a508 + (BUSY_SLOT * 0x10)); - status->total= nvkm_rd32(device, 0x10a508 + (CLK_SLOT * 0x10)); - return 0; + struct nvkm_falcon *falcon = pmu->base.falcon; + + status->busy = nvkm_falcon_rd32(falcon, 0x508 + (BUSY_SLOT * 0x10)); + status->total= nvkm_falcon_rd32(falcon, 0x508 + (CLK_SLOT * 0x10)); } static void gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu *pmu) { - struct nvkm_device *device = pmu->base.subdev.device; - nvkm_wr32(device, 0x10a508 + (BUSY_SLOT * 0x10), 0x80000000); - nvkm_wr32(device, 0x10a508 + (CLK_SLOT * 0x10), 0x80000000); + struct nvkm_falcon *falcon = pmu->base.falcon; + + nvkm_falcon_wr32(falcon, 0x508 + (BUSY_SLOT * 0x10), 0x80000000); + nvkm_falcon_wr32(falcon, 0x508 + (CLK_SLOT * 0x10), 0x80000000); } static void @@ -127,7 +123,7 @@ gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm) struct nvkm_timer *tmr = device->timer; struct nvkm_volt *volt = device->volt; u32 utilization = 0; - int state, ret; + int state; /* * The PMU is initialized before CLK and VOLT, so we have to make sure the @@ -136,11 +132,7 @@ gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm) if (!clk || !volt) goto resched; - ret = gk20a_pmu_dvfs_get_dev_status(pmu, &status); - if (ret) { - nvkm_warn(subdev, "failed to get device status\n"); - goto resched; - } + gk20a_pmu_dvfs_get_dev_status(pmu, &status); if (status.total) utilization = div_u64((u64)status.busy * 100, status.total); @@ -150,11 +142,7 @@ gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm) nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n", utilization, data->avg_load); - ret = gk20a_pmu_dvfs_get_cur_state(pmu, &state); - if (ret) { - nvkm_warn(subdev, "failed to get current state\n"); - goto resched; - } + gk20a_pmu_dvfs_get_cur_state(pmu, &state); if (gk20a_pmu_dvfs_get_target_state(pmu, &state, data->avg_load)) { nvkm_trace(subdev, "set new state to %d\n", state); @@ -166,32 +154,36 @@ resched: nvkm_timer_alarm(tmr, 100000000, alarm); } -static int -gk20a_pmu_fini(struct nvkm_subdev *subdev, bool suspend) +static void +gk20a_pmu_fini(struct nvkm_pmu *pmu) { - struct gk20a_pmu *pmu = gk20a_pmu(subdev); - nvkm_timer_alarm_cancel(subdev->device->timer, &pmu->alarm); - return 0; -} + struct gk20a_pmu *gpmu = gk20a_pmu(pmu); + nvkm_timer_alarm_cancel(pmu->subdev.device->timer, &gpmu->alarm); -static void * -gk20a_pmu_dtor(struct nvkm_subdev *subdev) -{ - return gk20a_pmu(subdev); + nvkm_falcon_put(pmu->falcon, &pmu->subdev); } static int -gk20a_pmu_init(struct nvkm_subdev *subdev) +gk20a_pmu_init(struct nvkm_pmu *pmu) { - struct gk20a_pmu *pmu = gk20a_pmu(subdev); - struct nvkm_device *device = pmu->base.subdev.device; + struct gk20a_pmu *gpmu = gk20a_pmu(pmu); + struct nvkm_subdev *subdev = &pmu->subdev; + struct nvkm_device *device = pmu->subdev.device; + struct nvkm_falcon *falcon = pmu->falcon; + int ret; + + ret = nvkm_falcon_get(falcon, subdev); + if (ret) { + nvkm_error(subdev, "cannot acquire %s falcon!\n", falcon->name); + return ret; + } /* init pwr perf counter */ - nvkm_wr32(device, 0x10a504 + (BUSY_SLOT * 0x10), 0x00200001); - nvkm_wr32(device, 0x10a50c + (BUSY_SLOT * 0x10), 0x00000002); - nvkm_wr32(device, 0x10a50c + (CLK_SLOT * 0x10), 0x00000003); + nvkm_falcon_wr32(falcon, 0x504 + (BUSY_SLOT * 0x10), 0x00200001); + nvkm_falcon_wr32(falcon, 0x50c + (BUSY_SLOT * 0x10), 0x00000002); + nvkm_falcon_wr32(falcon, 0x50c + (CLK_SLOT * 0x10), 0x00000003); - nvkm_timer_alarm(device->timer, 2000000000, &pmu->alarm); + nvkm_timer_alarm(device->timer, 2000000000, &gpmu->alarm); return 0; } @@ -202,26 +194,26 @@ gk20a_dvfs_data= { .p_smooth = 1, }; -static const struct nvkm_subdev_func +static const struct nvkm_pmu_func gk20a_pmu = { .init = gk20a_pmu_init, .fini = gk20a_pmu_fini, - .dtor = gk20a_pmu_dtor, + .reset = gt215_pmu_reset, }; int gk20a_pmu_new(struct nvkm_device *device, int index, struct nvkm_pmu **ppmu) { - static const struct nvkm_pmu_func func = {}; struct gk20a_pmu *pmu; if (!(pmu = kzalloc(sizeof(*pmu), GFP_KERNEL))) return -ENOMEM; - pmu->base.func = &func; *ppmu = &pmu->base; - nvkm_subdev_ctor(&gk20a_pmu, device, index, &pmu->base.subdev); + nvkm_pmu_ctor(&gk20a_pmu, device, index, &pmu->base); + pmu->data = &gk20a_dvfs_data; nvkm_alarm_init(&pmu->alarm, gk20a_pmu_dvfs_work); + return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c new file mode 100644 index 000000000000..0b8a1cc4a0ee --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "priv.h" + +static const struct nvkm_pmu_func +gm20b_pmu = { + .reset = gt215_pmu_reset, +}; + +int +gm20b_pmu_new(struct nvkm_device *device, int index, struct nvkm_pmu **ppmu) +{ + return nvkm_pmu_new_(&gm20b_pmu, device, index, ppmu); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h index 2e2179a4ad17..096cba069f72 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/priv.h @@ -4,6 +4,8 @@ #include <subdev/pmu.h> #include <subdev/pmu/fuc/os.h> +int nvkm_pmu_ctor(const struct nvkm_pmu_func *, struct nvkm_device *, + int index, struct nvkm_pmu *); int nvkm_pmu_new_(const struct nvkm_pmu_func *, struct nvkm_device *, int index, struct nvkm_pmu **); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/Kbuild index b02b868a6589..5076d1500f47 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/Kbuild @@ -1,3 +1,7 @@ nvkm-y += nvkm/subdev/secboot/base.o +nvkm-y += nvkm/subdev/secboot/ls_ucode_gr.o +nvkm-y += nvkm/subdev/secboot/acr.o +nvkm-y += nvkm/subdev/secboot/acr_r352.o +nvkm-y += nvkm/subdev/secboot/acr_r361.o nvkm-y += nvkm/subdev/secboot/gm200.o nvkm-y += nvkm/subdev/secboot/gm20b.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.c new file mode 100644 index 000000000000..75dc06557877 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "acr.h" + +#include <core/firmware.h> + +/** + * Convenience function to duplicate a firmware file in memory and check that + * it has the required minimum size. + */ +void * +nvkm_acr_load_firmware(const struct nvkm_subdev *subdev, const char *name, + size_t min_size) +{ + const struct firmware *fw; + void *blob; + int ret; + + ret = nvkm_firmware_get(subdev->device, name, &fw); + if (ret) + return ERR_PTR(ret); + if (fw->size < min_size) { + nvkm_error(subdev, "%s is smaller than expected size %zu\n", + name, min_size); + nvkm_firmware_put(fw); + return ERR_PTR(-EINVAL); + } + blob = kmemdup(fw->data, fw->size, GFP_KERNEL); + nvkm_firmware_put(fw); + if (!blob) + return ERR_PTR(-ENOMEM); + + return blob; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.h new file mode 100644 index 000000000000..97795b342b6f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef __NVKM_SECBOOT_ACR_H__ +#define __NVKM_SECBOOT_ACR_H__ + +#include "priv.h" + +struct nvkm_acr; + +/** + * struct nvkm_acr_func - properties and functions specific to an ACR + * + * @load: make the ACR ready to run on the given secboot device + * @reset: reset the specified falcon + * @start: start the specified falcon (assumed to have been reset) + */ +struct nvkm_acr_func { + void (*dtor)(struct nvkm_acr *); + int (*oneinit)(struct nvkm_acr *, struct nvkm_secboot *); + int (*fini)(struct nvkm_acr *, struct nvkm_secboot *, bool); + int (*load)(struct nvkm_acr *, struct nvkm_secboot *, + struct nvkm_gpuobj *, u64); + int (*reset)(struct nvkm_acr *, struct nvkm_secboot *, + enum nvkm_secboot_falcon); + int (*start)(struct nvkm_acr *, struct nvkm_secboot *, + enum nvkm_secboot_falcon); +}; + +/** + * struct nvkm_acr - instance of an ACR + * + * @boot_falcon: ID of the falcon that will perform secure boot + * @managed_falcons: bitfield of falcons managed by this ACR + * @start_address: virtual start address of the HS bootloader + */ +struct nvkm_acr { + const struct nvkm_acr_func *func; + const struct nvkm_subdev *subdev; + + enum nvkm_secboot_falcon boot_falcon; + unsigned long managed_falcons; + u32 start_address; +}; + +void *nvkm_acr_load_firmware(const struct nvkm_subdev *, const char *, size_t); + +struct nvkm_acr *acr_r352_new(unsigned long); +struct nvkm_acr *acr_r361_new(unsigned long); + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c new file mode 100644 index 000000000000..1aa37ea18580 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c @@ -0,0 +1,936 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "acr_r352.h" + +#include <core/gpuobj.h> +#include <core/firmware.h> +#include <engine/falcon.h> + +/** + * struct hsf_fw_header - HS firmware descriptor + * @sig_dbg_offset: offset of the debug signature + * @sig_dbg_size: size of the debug signature + * @sig_prod_offset: offset of the production signature + * @sig_prod_size: size of the production signature + * @patch_loc: offset of the offset (sic) of where the signature is + * @patch_sig: offset of the offset (sic) to add to sig_*_offset + * @hdr_offset: offset of the load header (see struct hs_load_header) + * @hdr_size: size of above header + * + * This structure is embedded in the HS firmware image at + * hs_bin_hdr.header_offset. + */ +struct hsf_fw_header { + u32 sig_dbg_offset; + u32 sig_dbg_size; + u32 sig_prod_offset; + u32 sig_prod_size; + u32 patch_loc; + u32 patch_sig; + u32 hdr_offset; + u32 hdr_size; +}; + +/** + * struct acr_r352_flcn_bl_desc - DMEM bootloader descriptor + * @signature: 16B signature for secure code. 0s if no secure code + * @ctx_dma: DMA context to be used by BL while loading code/data + * @code_dma_base: 256B-aligned Physical FB Address where code is located + * (falcon's $xcbase register) + * @non_sec_code_off: offset from code_dma_base where the non-secure code is + * located. The offset must be multiple of 256 to help perf + * @non_sec_code_size: the size of the nonSecure code part. + * @sec_code_off: offset from code_dma_base where the secure code is + * located. The offset must be multiple of 256 to help perf + * @sec_code_size: offset from code_dma_base where the secure code is + * located. The offset must be multiple of 256 to help perf + * @code_entry_point: code entry point which will be invoked by BL after + * code is loaded. + * @data_dma_base: 256B aligned Physical FB Address where data is located. + * (falcon's $xdbase register) + * @data_size: size of data block. Should be multiple of 256B + * + * Structure used by the bootloader to load the rest of the code. This has + * to be filled by host and copied into DMEM at offset provided in the + * hsflcn_bl_desc.bl_desc_dmem_load_off. + */ +struct acr_r352_flcn_bl_desc { + u32 reserved[4]; + u32 signature[4]; + u32 ctx_dma; + u32 code_dma_base; + u32 non_sec_code_off; + u32 non_sec_code_size; + u32 sec_code_off; + u32 sec_code_size; + u32 code_entry_point; + u32 data_dma_base; + u32 data_size; + u32 code_dma_base1; + u32 data_dma_base1; +}; + +/** + * acr_r352_generate_flcn_bl_desc - generate generic BL descriptor for LS image + */ +static void +acr_r352_generate_flcn_bl_desc(const struct nvkm_acr *acr, + const struct ls_ucode_img *_img, u64 wpr_addr, + void *_desc) +{ + struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img); + struct acr_r352_flcn_bl_desc *desc = _desc; + const struct ls_ucode_img_desc *pdesc = &_img->ucode_desc; + u64 base, addr_code, addr_data; + + base = wpr_addr + img->lsb_header.ucode_off + pdesc->app_start_offset; + addr_code = (base + pdesc->app_resident_code_offset) >> 8; + addr_data = (base + pdesc->app_resident_data_offset) >> 8; + + desc->ctx_dma = FALCON_DMAIDX_UCODE; + desc->code_dma_base = lower_32_bits(addr_code); + desc->code_dma_base1 = upper_32_bits(addr_code); + desc->non_sec_code_off = pdesc->app_resident_code_offset; + desc->non_sec_code_size = pdesc->app_resident_code_size; + desc->code_entry_point = pdesc->app_imem_entry; + desc->data_dma_base = lower_32_bits(addr_data); + desc->data_dma_base1 = upper_32_bits(addr_data); + desc->data_size = pdesc->app_resident_data_size; +} + + +/** + * struct hsflcn_acr_desc - data section of the HS firmware + * + * This header is to be copied at the beginning of DMEM by the HS bootloader. + * + * @signature: signature of ACR ucode + * @wpr_region_id: region ID holding the WPR header and its details + * @wpr_offset: offset from the WPR region holding the wpr header + * @regions: region descriptors + * @nonwpr_ucode_blob_size: size of LS blob + * @nonwpr_ucode_blob_start: FB location of LS blob is + */ +struct hsflcn_acr_desc { + union { + u8 reserved_dmem[0x200]; + u32 signatures[4]; + } ucode_reserved_space; + u32 wpr_region_id; + u32 wpr_offset; + u32 mmu_mem_range; +#define FLCN_ACR_MAX_REGIONS 2 + struct { + u32 no_regions; + struct { + u32 start_addr; + u32 end_addr; + u32 region_id; + u32 read_mask; + u32 write_mask; + u32 client_mask; + } region_props[FLCN_ACR_MAX_REGIONS]; + } regions; + u32 ucode_blob_size; + u64 ucode_blob_base __aligned(8); + struct { + u32 vpr_enabled; + u32 vpr_start; + u32 vpr_end; + u32 hdcp_policies; + } vpr_desc; +}; + + +/* + * Low-secure blob creation + */ + +/** + * ls_ucode_img_load() - create a lsf_ucode_img and load it + */ +struct ls_ucode_img * +acr_r352_ls_ucode_img_load(const struct acr_r352 *acr, + enum nvkm_secboot_falcon falcon_id) +{ + const struct nvkm_subdev *subdev = acr->base.subdev; + struct ls_ucode_img_r352 *img; + int ret; + + img = kzalloc(sizeof(*img), GFP_KERNEL); + if (!img) + return ERR_PTR(-ENOMEM); + + img->base.falcon_id = falcon_id; + + ret = acr->func->ls_func[falcon_id]->load(subdev, &img->base); + + if (ret) { + kfree(img->base.ucode_data); + kfree(img->base.sig); + kfree(img); + return ERR_PTR(ret); + } + + /* Check that the signature size matches our expectations... */ + if (img->base.sig_size != sizeof(img->lsb_header.signature)) { + nvkm_error(subdev, "invalid signature size for %s falcon!\n", + nvkm_secboot_falcon_name[falcon_id]); + return ERR_PTR(-EINVAL); + } + + /* Copy signature to the right place */ + memcpy(&img->lsb_header.signature, img->base.sig, img->base.sig_size); + + /* not needed? the signature should already have the right value */ + img->lsb_header.signature.falcon_id = falcon_id; + + return &img->base; +} + +#define LSF_LSB_HEADER_ALIGN 256 +#define LSF_BL_DATA_ALIGN 256 +#define LSF_BL_DATA_SIZE_ALIGN 256 +#define LSF_BL_CODE_SIZE_ALIGN 256 +#define LSF_UCODE_DATA_ALIGN 4096 + +/** + * acr_r352_ls_img_fill_headers - fill the WPR and LSB headers of an image + * @acr: ACR to use + * @img: image to generate for + * @offset: offset in the WPR region where this image starts + * + * Allocate space in the WPR area from offset and write the WPR and LSB headers + * accordingly. + * + * Return: offset at the end of this image. + */ +static u32 +acr_r352_ls_img_fill_headers(struct acr_r352 *acr, + struct ls_ucode_img_r352 *img, u32 offset) +{ + struct ls_ucode_img *_img = &img->base; + struct acr_r352_lsf_wpr_header *whdr = &img->wpr_header; + struct acr_r352_lsf_lsb_header *lhdr = &img->lsb_header; + struct ls_ucode_img_desc *desc = &_img->ucode_desc; + const struct acr_r352_ls_func *func = + acr->func->ls_func[_img->falcon_id]; + + /* Fill WPR header */ + whdr->falcon_id = _img->falcon_id; + whdr->bootstrap_owner = acr->base.boot_falcon; + whdr->status = LSF_IMAGE_STATUS_COPY; + + /* Skip bootstrapping falcons started by someone else than ACR */ + if (acr->lazy_bootstrap & BIT(_img->falcon_id)) + whdr->lazy_bootstrap = 1; + + /* Align, save off, and include an LSB header size */ + offset = ALIGN(offset, LSF_LSB_HEADER_ALIGN); + whdr->lsb_offset = offset; + offset += sizeof(*lhdr); + + /* + * Align, save off, and include the original (static) ucode + * image size + */ + offset = ALIGN(offset, LSF_UCODE_DATA_ALIGN); + lhdr->ucode_off = offset; + offset += _img->ucode_size; + + /* + * For falcons that use a boot loader (BL), we append a loader + * desc structure on the end of the ucode image and consider + * this the boot loader data. The host will then copy the loader + * desc args to this space within the WPR region (before locking + * down) and the HS bin will then copy them to DMEM 0 for the + * loader. + */ + lhdr->bl_code_size = ALIGN(desc->bootloader_size, + LSF_BL_CODE_SIZE_ALIGN); + lhdr->ucode_size = ALIGN(desc->app_resident_data_offset, + LSF_BL_CODE_SIZE_ALIGN) + lhdr->bl_code_size; + lhdr->data_size = ALIGN(desc->app_size, LSF_BL_CODE_SIZE_ALIGN) + + lhdr->bl_code_size - lhdr->ucode_size; + /* + * Though the BL is located at 0th offset of the image, the VA + * is different to make sure that it doesn't collide the actual + * OS VA range + */ + lhdr->bl_imem_off = desc->bootloader_imem_offset; + lhdr->app_code_off = desc->app_start_offset + + desc->app_resident_code_offset; + lhdr->app_code_size = desc->app_resident_code_size; + lhdr->app_data_off = desc->app_start_offset + + desc->app_resident_data_offset; + lhdr->app_data_size = desc->app_resident_data_size; + + lhdr->flags = func->lhdr_flags; + if (_img->falcon_id == acr->base.boot_falcon) + lhdr->flags |= LSF_FLAG_DMACTL_REQ_CTX; + + /* Align and save off BL descriptor size */ + lhdr->bl_data_size = ALIGN(func->bl_desc_size, LSF_BL_DATA_SIZE_ALIGN); + + /* + * Align, save off, and include the additional BL data + */ + offset = ALIGN(offset, LSF_BL_DATA_ALIGN); + lhdr->bl_data_off = offset; + offset += lhdr->bl_data_size; + + return offset; +} + +/** + * acr_r352_ls_fill_headers - fill WPR and LSB headers of all managed images + */ +int +acr_r352_ls_fill_headers(struct acr_r352 *acr, struct list_head *imgs) +{ + struct ls_ucode_img_r352 *img; + struct list_head *l; + u32 count = 0; + u32 offset; + + /* Count the number of images to manage */ + list_for_each(l, imgs) + count++; + + /* + * Start with an array of WPR headers at the base of the WPR. + * The expectation here is that the secure falcon will do a single DMA + * read of this array and cache it internally so it's ok to pack these. + * Also, we add 1 to the falcon count to indicate the end of the array. + */ + offset = sizeof(img->wpr_header) * (count + 1); + + /* + * Walk the managed falcons, accounting for the LSB structs + * as well as the ucode images. + */ + list_for_each_entry(img, imgs, base.node) { + offset = acr_r352_ls_img_fill_headers(acr, img, offset); + } + + return offset; +} + +/** + * acr_r352_ls_write_wpr - write the WPR blob contents + */ +int +acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs, + struct nvkm_gpuobj *wpr_blob, u32 wpr_addr) +{ + struct ls_ucode_img *_img; + u32 pos = 0; + + nvkm_kmap(wpr_blob); + + list_for_each_entry(_img, imgs, node) { + struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img); + const struct acr_r352_ls_func *ls_func = + acr->func->ls_func[_img->falcon_id]; + u8 gdesc[ls_func->bl_desc_size]; + + nvkm_gpuobj_memcpy_to(wpr_blob, pos, &img->wpr_header, + sizeof(img->wpr_header)); + + nvkm_gpuobj_memcpy_to(wpr_blob, img->wpr_header.lsb_offset, + &img->lsb_header, sizeof(img->lsb_header)); + + /* Generate and write BL descriptor */ + memset(gdesc, 0, ls_func->bl_desc_size); + ls_func->generate_bl_desc(&acr->base, _img, wpr_addr, gdesc); + + nvkm_gpuobj_memcpy_to(wpr_blob, img->lsb_header.bl_data_off, + gdesc, ls_func->bl_desc_size); + + /* Copy ucode */ + nvkm_gpuobj_memcpy_to(wpr_blob, img->lsb_header.ucode_off, + _img->ucode_data, _img->ucode_size); + + pos += sizeof(img->wpr_header); + } + + nvkm_wo32(wpr_blob, pos, NVKM_SECBOOT_FALCON_INVALID); + + nvkm_done(wpr_blob); + + return 0; +} + +/* Both size and address of WPR need to be 128K-aligned */ +#define WPR_ALIGNMENT 0x20000 +/** + * acr_r352_prepare_ls_blob() - prepare the LS blob + * + * For each securely managed falcon, load the FW, signatures and bootloaders and + * prepare a ucode blob. Then, compute the offsets in the WPR region for each + * blob, and finally write the headers and ucode blobs into a GPU object that + * will be copied into the WPR region by the HS firmware. + */ +static int +acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size) +{ + const struct nvkm_subdev *subdev = acr->base.subdev; + struct list_head imgs; + struct ls_ucode_img *img, *t; + unsigned long managed_falcons = acr->base.managed_falcons; + int managed_count = 0; + u32 image_wpr_size; + int falcon_id; + int ret; + + INIT_LIST_HEAD(&imgs); + + /* Load all LS blobs */ + for_each_set_bit(falcon_id, &managed_falcons, NVKM_SECBOOT_FALCON_END) { + struct ls_ucode_img *img; + + img = acr->func->ls_ucode_img_load(acr, falcon_id); + if (IS_ERR(img)) { + ret = PTR_ERR(img); + goto cleanup; + } + + list_add_tail(&img->node, &imgs); + managed_count++; + } + + /* + * Fill the WPR and LSF headers with the right offsets and compute + * required WPR size + */ + image_wpr_size = acr->func->ls_fill_headers(acr, &imgs); + image_wpr_size = ALIGN(image_wpr_size, WPR_ALIGNMENT); + + /* Allocate GPU object that will contain the WPR region */ + ret = nvkm_gpuobj_new(subdev->device, image_wpr_size, WPR_ALIGNMENT, + false, NULL, &acr->ls_blob); + if (ret) + goto cleanup; + + nvkm_debug(subdev, "%d managed LS falcons, WPR size is %d bytes\n", + managed_count, image_wpr_size); + + /* If WPR address and size are not fixed, set them to fit the LS blob */ + if (wpr_size == 0) { + wpr_addr = acr->ls_blob->addr; + wpr_size = image_wpr_size; + /* + * But if the WPR region is set by the bootloader, it is illegal for + * the HS blob to be larger than this region. + */ + } else if (image_wpr_size > wpr_size) { + nvkm_error(subdev, "WPR region too small for FW blob!\n"); + nvkm_error(subdev, "required: %dB\n", image_wpr_size); + nvkm_error(subdev, "available: %dB\n", wpr_size); + ret = -ENOSPC; + goto cleanup; + } + + /* Write LS blob */ + ret = acr->func->ls_write_wpr(acr, &imgs, acr->ls_blob, wpr_addr); + if (ret) + nvkm_gpuobj_del(&acr->ls_blob); + +cleanup: + list_for_each_entry_safe(img, t, &imgs, node) { + kfree(img->ucode_data); + kfree(img->sig); + kfree(img); + } + + return ret; +} + + + + +/** + * acr_r352_hsf_patch_signature() - patch HS blob with correct signature + */ +static void +acr_r352_hsf_patch_signature(struct nvkm_secboot *sb, void *acr_image) +{ + struct fw_bin_header *hsbin_hdr = acr_image; + struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset; + void *hs_data = acr_image + hsbin_hdr->data_offset; + void *sig; + u32 sig_size; + + /* Falcon in debug or production mode? */ + if (sb->boot_falcon->debug) { + sig = acr_image + fw_hdr->sig_dbg_offset; + sig_size = fw_hdr->sig_dbg_size; + } else { + sig = acr_image + fw_hdr->sig_prod_offset; + sig_size = fw_hdr->sig_prod_size; + } + + /* Patch signature */ + memcpy(hs_data + fw_hdr->patch_loc, sig + fw_hdr->patch_sig, sig_size); +} + +static void +acr_r352_fixup_hs_desc(struct acr_r352 *acr, struct nvkm_secboot *sb, + struct hsflcn_acr_desc *desc) +{ + struct nvkm_gpuobj *ls_blob = acr->ls_blob; + + /* WPR region information if WPR is not fixed */ + if (sb->wpr_size == 0) { + u32 wpr_start = ls_blob->addr; + u32 wpr_end = wpr_start + ls_blob->size; + + desc->wpr_region_id = 1; + desc->regions.no_regions = 2; + desc->regions.region_props[0].start_addr = wpr_start >> 8; + desc->regions.region_props[0].end_addr = wpr_end >> 8; + desc->regions.region_props[0].region_id = 1; + desc->regions.region_props[0].read_mask = 0xf; + desc->regions.region_props[0].write_mask = 0xc; + desc->regions.region_props[0].client_mask = 0x2; + } else { + desc->ucode_blob_base = ls_blob->addr; + desc->ucode_blob_size = ls_blob->size; + } +} + +static void +acr_r352_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc, + u64 offset) +{ + struct acr_r352_flcn_bl_desc *bl_desc = _bl_desc; + u64 addr_code, addr_data; + + addr_code = offset >> 8; + addr_data = (offset + hdr->data_dma_base) >> 8; + + bl_desc->ctx_dma = FALCON_DMAIDX_VIRT; + bl_desc->code_dma_base = lower_32_bits(addr_code); + bl_desc->non_sec_code_off = hdr->non_sec_code_off; + bl_desc->non_sec_code_size = hdr->non_sec_code_size; + bl_desc->sec_code_off = hdr->app[0].sec_code_off; + bl_desc->sec_code_size = hdr->app[0].sec_code_size; + bl_desc->code_entry_point = 0; + bl_desc->data_dma_base = lower_32_bits(addr_data); + bl_desc->data_size = hdr->data_size; +} + +/** + * acr_r352_prepare_hs_blob - load and prepare a HS blob and BL descriptor + * + * @sb secure boot instance to prepare for + * @fw name of the HS firmware to load + * @blob pointer to gpuobj that will be allocated to receive the HS FW payload + * @bl_desc pointer to the BL descriptor to write for this firmware + * @patch whether we should patch the HS descriptor (only for HS loaders) + */ +static int +acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb, + const char *fw, struct nvkm_gpuobj **blob, + struct hsf_load_header *load_header, bool patch) +{ + struct nvkm_subdev *subdev = &sb->subdev; + void *acr_image; + struct fw_bin_header *hsbin_hdr; + struct hsf_fw_header *fw_hdr; + struct hsf_load_header *load_hdr; + void *acr_data; + int ret; + + acr_image = nvkm_acr_load_firmware(subdev, fw, 0); + if (IS_ERR(acr_image)) + return PTR_ERR(acr_image); + + hsbin_hdr = acr_image; + fw_hdr = acr_image + hsbin_hdr->header_offset; + load_hdr = acr_image + fw_hdr->hdr_offset; + acr_data = acr_image + hsbin_hdr->data_offset; + + /* Patch signature */ + acr_r352_hsf_patch_signature(sb, acr_image); + + /* Patch descriptor with WPR information? */ + if (patch) { + struct hsflcn_acr_desc *desc; + + desc = acr_data + load_hdr->data_dma_base; + acr_r352_fixup_hs_desc(acr, sb, desc); + } + + if (load_hdr->num_apps > ACR_R352_MAX_APPS) { + nvkm_error(subdev, "more apps (%d) than supported (%d)!", + load_hdr->num_apps, ACR_R352_MAX_APPS); + ret = -EINVAL; + goto cleanup; + } + memcpy(load_header, load_hdr, sizeof(*load_header) + + (sizeof(load_hdr->app[0]) * load_hdr->num_apps)); + + /* Create ACR blob and copy HS data to it */ + ret = nvkm_gpuobj_new(subdev->device, ALIGN(hsbin_hdr->data_size, 256), + 0x1000, false, NULL, blob); + if (ret) + goto cleanup; + + nvkm_kmap(*blob); + nvkm_gpuobj_memcpy_to(*blob, 0, acr_data, hsbin_hdr->data_size); + nvkm_done(*blob); + +cleanup: + kfree(acr_image); + + return ret; +} + +static int +acr_r352_prepare_hsbl_blob(struct acr_r352 *acr) +{ + const struct nvkm_subdev *subdev = acr->base.subdev; + struct fw_bin_header *hdr; + struct fw_bl_desc *hsbl_desc; + + acr->hsbl_blob = nvkm_acr_load_firmware(subdev, "acr/bl", 0); + if (IS_ERR(acr->hsbl_blob)) { + int ret = PTR_ERR(acr->hsbl_blob); + + acr->hsbl_blob = NULL; + return ret; + } + + hdr = acr->hsbl_blob; + hsbl_desc = acr->hsbl_blob + hdr->header_offset; + + /* virtual start address for boot vector */ + acr->base.start_address = hsbl_desc->start_tag << 8; + + return 0; +} + +/** + * acr_r352_load_blobs - load blobs common to all ACR V1 versions. + * + * This includes the LS blob, HS ucode loading blob, and HS bootloader. + * + * The HS ucode unload blob is only used on dGPU if the WPR region is variable. + */ +int +acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb) +{ + int ret; + + /* Firmware already loaded? */ + if (acr->firmware_ok) + return 0; + + /* Load and prepare the managed falcon's firmwares */ + ret = acr_r352_prepare_ls_blob(acr, sb->wpr_addr, sb->wpr_size); + if (ret) + return ret; + + /* Load the HS firmware that will load the LS firmwares */ + if (!acr->load_blob) { + ret = acr_r352_prepare_hs_blob(acr, sb, "acr/ucode_load", + &acr->load_blob, + &acr->load_bl_header, true); + if (ret) + return ret; + } + + /* If the ACR region is dynamically programmed, we need an unload FW */ + if (sb->wpr_size == 0) { + ret = acr_r352_prepare_hs_blob(acr, sb, "acr/ucode_unload", + &acr->unload_blob, + &acr->unload_bl_header, false); + if (ret) + return ret; + } + + /* Load the HS firmware bootloader */ + if (!acr->hsbl_blob) { + ret = acr_r352_prepare_hsbl_blob(acr); + if (ret) + return ret; + } + + acr->firmware_ok = true; + nvkm_debug(&sb->subdev, "LS blob successfully created\n"); + + return 0; +} + +/** + * acr_r352_load() - prepare HS falcon to run the specified blob, mapped + * at GPU address offset. + */ +static int +acr_r352_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb, + struct nvkm_gpuobj *blob, u64 offset) +{ + struct acr_r352 *acr = acr_r352(_acr); + struct nvkm_falcon *falcon = sb->boot_falcon; + struct fw_bin_header *hdr = acr->hsbl_blob; + struct fw_bl_desc *hsbl_desc = acr->hsbl_blob + hdr->header_offset; + void *blob_data = acr->hsbl_blob + hdr->data_offset; + void *hsbl_code = blob_data + hsbl_desc->code_off; + void *hsbl_data = blob_data + hsbl_desc->data_off; + u32 code_size = ALIGN(hsbl_desc->code_size, 256); + const struct hsf_load_header *load_hdr; + const u32 bl_desc_size = acr->func->hs_bl_desc_size; + u8 bl_desc[bl_desc_size]; + + /* Find the bootloader descriptor for our blob and copy it */ + if (blob == acr->load_blob) { + load_hdr = &acr->load_bl_header; + } else if (blob == acr->unload_blob) { + load_hdr = &acr->unload_bl_header; + } else { + nvkm_error(_acr->subdev, "invalid secure boot blob!\n"); + return -EINVAL; + } + + /* + * Copy HS bootloader data + */ + nvkm_falcon_load_dmem(falcon, hsbl_data, 0x0, hsbl_desc->data_size, 0); + + /* Copy HS bootloader code to end of IMEM */ + nvkm_falcon_load_imem(falcon, hsbl_code, falcon->code.limit - code_size, + code_size, hsbl_desc->start_tag, 0, false); + + /* Generate the BL header */ + memset(bl_desc, 0, bl_desc_size); + acr->func->generate_hs_bl_desc(load_hdr, bl_desc, offset); + + /* + * Copy HS BL header where the HS descriptor expects it to be + */ + nvkm_falcon_load_dmem(falcon, bl_desc, hsbl_desc->dmem_load_off, + bl_desc_size, 0); + + return 0; +} + +static int +acr_r352_shutdown(struct acr_r352 *acr, struct nvkm_secboot *sb) +{ + int i; + + /* Run the unload blob to unprotect the WPR region */ + if (acr->unload_blob && sb->wpr_set) { + int ret; + + nvkm_debug(&sb->subdev, "running HS unload blob\n"); + ret = sb->func->run_blob(sb, acr->unload_blob); + if (ret) + return ret; + nvkm_debug(&sb->subdev, "HS unload blob completed\n"); + } + + for (i = 0; i < NVKM_SECBOOT_FALCON_END; i++) + acr->falcon_state[i] = NON_SECURE; + + sb->wpr_set = false; + + return 0; +} + +static int +acr_r352_bootstrap(struct acr_r352 *acr, struct nvkm_secboot *sb) +{ + int ret; + + if (sb->wpr_set) + return 0; + + /* Make sure all blobs are ready */ + ret = acr_r352_load_blobs(acr, sb); + if (ret) + return ret; + + nvkm_debug(&sb->subdev, "running HS load blob\n"); + ret = sb->func->run_blob(sb, acr->load_blob); + /* clear halt interrupt */ + nvkm_falcon_clear_interrupt(sb->boot_falcon, 0x10); + if (ret) + return ret; + nvkm_debug(&sb->subdev, "HS load blob completed\n"); + + sb->wpr_set = true; + + return 0; +} + +/* + * acr_r352_reset() - execute secure boot from the prepared state + * + * Load the HS bootloader and ask the falcon to run it. This will in turn + * load the HS firmware and run it, so once the falcon stops all the managed + * falcons should have their LS firmware loaded and be ready to run. + */ +static int +acr_r352_reset(struct nvkm_acr *_acr, struct nvkm_secboot *sb, + enum nvkm_secboot_falcon falcon) +{ + struct acr_r352 *acr = acr_r352(_acr); + int ret; + + /* + * Dummy GM200 implementation: perform secure boot each time we are + * called on FECS. Since only FECS and GPCCS are managed and started + * together, this ought to be safe. + * + * Once we have proper PMU firmware and support, this will be changed + * to a proper call to the PMU method. + */ + if (falcon != NVKM_SECBOOT_FALCON_FECS) + goto end; + + ret = acr_r352_shutdown(acr, sb); + if (ret) + return ret; + + acr_r352_bootstrap(acr, sb); + if (ret) + return ret; + +end: + acr->falcon_state[falcon] = RESET; + return 0; +} + +static int +acr_r352_start(struct nvkm_acr *_acr, struct nvkm_secboot *sb, + enum nvkm_secboot_falcon falcon) +{ + struct acr_r352 *acr = acr_r352(_acr); + const struct nvkm_subdev *subdev = &sb->subdev; + int base; + + switch (falcon) { + case NVKM_SECBOOT_FALCON_FECS: + base = 0x409000; + break; + case NVKM_SECBOOT_FALCON_GPCCS: + base = 0x41a000; + break; + default: + nvkm_error(subdev, "cannot start unhandled falcon!\n"); + return -EINVAL; + } + + nvkm_wr32(subdev->device, base + 0x130, 0x00000002); + acr->falcon_state[falcon] = RUNNING; + + return 0; +} + +static int +acr_r352_fini(struct nvkm_acr *_acr, struct nvkm_secboot *sb, bool suspend) +{ + struct acr_r352 *acr = acr_r352(_acr); + + return acr_r352_shutdown(acr, sb); +} + +static void +acr_r352_dtor(struct nvkm_acr *_acr) +{ + struct acr_r352 *acr = acr_r352(_acr); + + nvkm_gpuobj_del(&acr->unload_blob); + + kfree(acr->hsbl_blob); + nvkm_gpuobj_del(&acr->load_blob); + nvkm_gpuobj_del(&acr->ls_blob); + + kfree(acr); +} + +const struct acr_r352_ls_func +acr_r352_ls_fecs_func = { + .load = acr_ls_ucode_load_fecs, + .generate_bl_desc = acr_r352_generate_flcn_bl_desc, + .bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc), +}; + +const struct acr_r352_ls_func +acr_r352_ls_gpccs_func = { + .load = acr_ls_ucode_load_gpccs, + .generate_bl_desc = acr_r352_generate_flcn_bl_desc, + .bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc), + /* GPCCS will be loaded using PRI */ + .lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD, +}; + +const struct acr_r352_func +acr_r352_func = { + .generate_hs_bl_desc = acr_r352_generate_hs_bl_desc, + .hs_bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc), + .ls_ucode_img_load = acr_r352_ls_ucode_img_load, + .ls_fill_headers = acr_r352_ls_fill_headers, + .ls_write_wpr = acr_r352_ls_write_wpr, + .ls_func = { + [NVKM_SECBOOT_FALCON_FECS] = &acr_r352_ls_fecs_func, + [NVKM_SECBOOT_FALCON_GPCCS] = &acr_r352_ls_gpccs_func, + }, +}; + +static const struct nvkm_acr_func +acr_r352_base_func = { + .dtor = acr_r352_dtor, + .fini = acr_r352_fini, + .load = acr_r352_load, + .reset = acr_r352_reset, + .start = acr_r352_start, +}; + +struct nvkm_acr * +acr_r352_new_(const struct acr_r352_func *func, + enum nvkm_secboot_falcon boot_falcon, + unsigned long managed_falcons) +{ + struct acr_r352 *acr; + + acr = kzalloc(sizeof(*acr), GFP_KERNEL); + if (!acr) + return ERR_PTR(-ENOMEM); + + acr->base.boot_falcon = boot_falcon; + acr->base.managed_falcons = managed_falcons; + acr->base.func = &acr_r352_base_func; + acr->func = func; + + return &acr->base; +} + +struct nvkm_acr * +acr_r352_new(unsigned long managed_falcons) +{ + return acr_r352_new_(&acr_r352_func, NVKM_SECBOOT_FALCON_PMU, + managed_falcons); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h new file mode 100644 index 000000000000..ad5923b0fd3c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef __NVKM_SECBOOT_ACR_R352_H__ +#define __NVKM_SECBOOT_ACR_R352_H__ + +#include "acr.h" +#include "ls_ucode.h" + +struct ls_ucode_img; + +#define ACR_R352_MAX_APPS 8 + +/* + * + * LS blob structures + * + */ + +/** + * struct acr_r352_lsf_lsb_header - LS firmware header + * @signature: signature to verify the firmware against + * @ucode_off: offset of the ucode blob in the WPR region. The ucode + * blob contains the bootloader, code and data of the + * LS falcon + * @ucode_size: size of the ucode blob, including bootloader + * @data_size: size of the ucode blob data + * @bl_code_size: size of the bootloader code + * @bl_imem_off: offset in imem of the bootloader + * @bl_data_off: offset of the bootloader data in WPR region + * @bl_data_size: size of the bootloader data + * @app_code_off: offset of the app code relative to ucode_off + * @app_code_size: size of the app code + * @app_data_off: offset of the app data relative to ucode_off + * @app_data_size: size of the app data + * @flags: flags for the secure bootloader + * + * This structure is written into the WPR region for each managed falcon. Each + * instance is referenced by the lsb_offset member of the corresponding + * lsf_wpr_header. + */ +struct acr_r352_lsf_lsb_header { + /** + * LS falcon signatures + * @prd_keys: signature to use in production mode + * @dgb_keys: signature to use in debug mode + * @b_prd_present: whether the production key is present + * @b_dgb_present: whether the debug key is present + * @falcon_id: ID of the falcon the ucode applies to + */ + struct { + u8 prd_keys[2][16]; + u8 dbg_keys[2][16]; + u32 b_prd_present; + u32 b_dbg_present; + u32 falcon_id; + } signature; + u32 ucode_off; + u32 ucode_size; + u32 data_size; + u32 bl_code_size; + u32 bl_imem_off; + u32 bl_data_off; + u32 bl_data_size; + u32 app_code_off; + u32 app_code_size; + u32 app_data_off; + u32 app_data_size; + u32 flags; +#define LSF_FLAG_LOAD_CODE_AT_0 1 +#define LSF_FLAG_DMACTL_REQ_CTX 4 +#define LSF_FLAG_FORCE_PRIV_LOAD 8 +}; + +/** + * struct acr_r352_lsf_wpr_header - LS blob WPR Header + * @falcon_id: LS falcon ID + * @lsb_offset: offset of the lsb_lsf_header in the WPR region + * @bootstrap_owner: secure falcon reponsible for bootstrapping the LS falcon + * @lazy_bootstrap: skip bootstrapping by ACR + * @status: bootstrapping status + * + * An array of these is written at the beginning of the WPR region, one for + * each managed falcon. The array is terminated by an instance which falcon_id + * is LSF_FALCON_ID_INVALID. + */ +struct acr_r352_lsf_wpr_header { + u32 falcon_id; + u32 lsb_offset; + u32 bootstrap_owner; + u32 lazy_bootstrap; + u32 status; +#define LSF_IMAGE_STATUS_NONE 0 +#define LSF_IMAGE_STATUS_COPY 1 +#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED 2 +#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED 3 +#define LSF_IMAGE_STATUS_VALIDATION_DONE 4 +#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED 5 +#define LSF_IMAGE_STATUS_BOOTSTRAP_READY 6 +}; + +/** + * struct ls_ucode_img_r352 - ucode image augmented with r352 headers + */ +struct ls_ucode_img_r352 { + struct ls_ucode_img base; + + struct acr_r352_lsf_wpr_header wpr_header; + struct acr_r352_lsf_lsb_header lsb_header; +}; +#define ls_ucode_img_r352(i) container_of(i, struct ls_ucode_img_r352, base) + + +/* + * HS blob structures + */ + +struct hsf_load_header_app { + u32 sec_code_off; + u32 sec_code_size; +}; + +/** + * struct hsf_load_header - HS firmware load header + */ +struct hsf_load_header { + u32 non_sec_code_off; + u32 non_sec_code_size; + u32 data_dma_base; + u32 data_size; + u32 num_apps; + struct hsf_load_header_app app[0]; +}; + +/** + * struct acr_r352_ls_func - manages a single LS firmware + * + * @load: load the external firmware into a ls_ucode_img + * @generate_bl_desc: function called on a block of bl_desc_size to generate the + * proper bootloader descriptor for this LS firmware + * @bl_desc_size: size of the bootloader descriptor + * @lhdr_flags: LS flags + */ +struct acr_r352_ls_func { + int (*load)(const struct nvkm_subdev *, struct ls_ucode_img *); + void (*generate_bl_desc)(const struct nvkm_acr *, + const struct ls_ucode_img *, u64, void *); + u32 bl_desc_size; + u32 lhdr_flags; +}; + +struct acr_r352; + +/** + * struct acr_r352_func - manages nuances between ACR versions + * + * @generate_hs_bl_desc: function called on a block of bl_desc_size to generate + * the proper HS bootloader descriptor + * @hs_bl_desc_size: size of the HS bootloader descriptor + */ +struct acr_r352_func { + void (*generate_hs_bl_desc)(const struct hsf_load_header *, void *, + u64); + u32 hs_bl_desc_size; + + struct ls_ucode_img *(*ls_ucode_img_load)(const struct acr_r352 *, + enum nvkm_secboot_falcon); + int (*ls_fill_headers)(struct acr_r352 *, struct list_head *); + int (*ls_write_wpr)(struct acr_r352 *, struct list_head *, + struct nvkm_gpuobj *, u32); + + const struct acr_r352_ls_func *ls_func[NVKM_SECBOOT_FALCON_END]; +}; + +/** + * struct acr_r352 - ACR data for driver release 352 (and beyond) + */ +struct acr_r352 { + struct nvkm_acr base; + const struct acr_r352_func *func; + + /* + * HS FW - lock WPR region (dGPU only) and load LS FWs + * on Tegra the HS FW copies the LS blob into the fixed WPR instead + */ + struct nvkm_gpuobj *load_blob; + struct { + struct hsf_load_header load_bl_header; + struct hsf_load_header_app __load_apps[ACR_R352_MAX_APPS]; + }; + + /* HS FW - unlock WPR region (dGPU only) */ + struct nvkm_gpuobj *unload_blob; + struct { + struct hsf_load_header unload_bl_header; + struct hsf_load_header_app __unload_apps[ACR_R352_MAX_APPS]; + }; + + /* HS bootloader */ + void *hsbl_blob; + + /* LS FWs, to be loaded by the HS ACR */ + struct nvkm_gpuobj *ls_blob; + + /* Firmware already loaded? */ + bool firmware_ok; + + /* Falcons to lazy-bootstrap */ + u32 lazy_bootstrap; + + /* To keep track of the state of all managed falcons */ + enum { + /* In non-secure state, no firmware loaded, no privileges*/ + NON_SECURE = 0, + /* In low-secure mode and ready to be started */ + RESET, + /* In low-secure mode and running */ + RUNNING, + } falcon_state[NVKM_SECBOOT_FALCON_END]; +}; +#define acr_r352(acr) container_of(acr, struct acr_r352, base) + +struct nvkm_acr *acr_r352_new_(const struct acr_r352_func *, + enum nvkm_secboot_falcon, unsigned long); + +struct ls_ucode_img *acr_r352_ls_ucode_img_load(const struct acr_r352 *, + enum nvkm_secboot_falcon); +int acr_r352_ls_fill_headers(struct acr_r352 *, struct list_head *); +int acr_r352_ls_write_wpr(struct acr_r352 *, struct list_head *, + struct nvkm_gpuobj *, u32); + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c new file mode 100644 index 000000000000..f0aff1d98474 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "acr_r352.h" + +#include <engine/falcon.h> + +/** + * struct acr_r361_flcn_bl_desc - DMEM bootloader descriptor + * @signature: 16B signature for secure code. 0s if no secure code + * @ctx_dma: DMA context to be used by BL while loading code/data + * @code_dma_base: 256B-aligned Physical FB Address where code is located + * (falcon's $xcbase register) + * @non_sec_code_off: offset from code_dma_base where the non-secure code is + * located. The offset must be multiple of 256 to help perf + * @non_sec_code_size: the size of the nonSecure code part. + * @sec_code_off: offset from code_dma_base where the secure code is + * located. The offset must be multiple of 256 to help perf + * @sec_code_size: offset from code_dma_base where the secure code is + * located. The offset must be multiple of 256 to help perf + * @code_entry_point: code entry point which will be invoked by BL after + * code is loaded. + * @data_dma_base: 256B aligned Physical FB Address where data is located. + * (falcon's $xdbase register) + * @data_size: size of data block. Should be multiple of 256B + * + * Structure used by the bootloader to load the rest of the code. This has + * to be filled by host and copied into DMEM at offset provided in the + * hsflcn_bl_desc.bl_desc_dmem_load_off. + */ +struct acr_r361_flcn_bl_desc { + u32 reserved[4]; + u32 signature[4]; + u32 ctx_dma; + struct flcn_u64 code_dma_base; + u32 non_sec_code_off; + u32 non_sec_code_size; + u32 sec_code_off; + u32 sec_code_size; + u32 code_entry_point; + struct flcn_u64 data_dma_base; + u32 data_size; +}; + +static void +acr_r361_generate_flcn_bl_desc(const struct nvkm_acr *acr, + const struct ls_ucode_img *_img, u64 wpr_addr, + void *_desc) +{ + struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img); + struct acr_r361_flcn_bl_desc *desc = _desc; + const struct ls_ucode_img_desc *pdesc = &img->base.ucode_desc; + u64 base, addr_code, addr_data; + + base = wpr_addr + img->lsb_header.ucode_off + pdesc->app_start_offset; + addr_code = base + pdesc->app_resident_code_offset; + addr_data = base + pdesc->app_resident_data_offset; + + desc->ctx_dma = FALCON_DMAIDX_UCODE; + desc->code_dma_base = u64_to_flcn64(addr_code); + desc->non_sec_code_off = pdesc->app_resident_code_offset; + desc->non_sec_code_size = pdesc->app_resident_code_size; + desc->code_entry_point = pdesc->app_imem_entry; + desc->data_dma_base = u64_to_flcn64(addr_data); + desc->data_size = pdesc->app_resident_data_size; +} + +static void +acr_r361_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc, + u64 offset) +{ + struct acr_r361_flcn_bl_desc *bl_desc = _bl_desc; + + bl_desc->ctx_dma = FALCON_DMAIDX_VIRT; + bl_desc->code_dma_base = u64_to_flcn64(offset); + bl_desc->non_sec_code_off = hdr->non_sec_code_off; + bl_desc->non_sec_code_size = hdr->non_sec_code_size; + bl_desc->sec_code_off = hdr->app[0].sec_code_off; + bl_desc->sec_code_size = hdr->app[0].sec_code_size; + bl_desc->code_entry_point = 0; + bl_desc->data_dma_base = u64_to_flcn64(offset + hdr->data_dma_base); + bl_desc->data_size = hdr->data_size; +} + +const struct acr_r352_ls_func +acr_r361_ls_fecs_func = { + .load = acr_ls_ucode_load_fecs, + .generate_bl_desc = acr_r361_generate_flcn_bl_desc, + .bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc), +}; + +const struct acr_r352_ls_func +acr_r361_ls_gpccs_func = { + .load = acr_ls_ucode_load_gpccs, + .generate_bl_desc = acr_r361_generate_flcn_bl_desc, + .bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc), + /* GPCCS will be loaded using PRI */ + .lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD, +}; + +const struct acr_r352_func +acr_r361_func = { + .generate_hs_bl_desc = acr_r361_generate_hs_bl_desc, + .hs_bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc), + .ls_ucode_img_load = acr_r352_ls_ucode_img_load, + .ls_fill_headers = acr_r352_ls_fill_headers, + .ls_write_wpr = acr_r352_ls_write_wpr, + .ls_func = { + [NVKM_SECBOOT_FALCON_FECS] = &acr_r361_ls_fecs_func, + [NVKM_SECBOOT_FALCON_GPCCS] = &acr_r361_ls_gpccs_func, + }, +}; + +struct nvkm_acr * +acr_r361_new(unsigned long managed_falcons) +{ + return acr_r352_new_(&acr_r361_func, NVKM_SECBOOT_FALCON_PMU, + managed_falcons); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c index 314be2192b7d..27c9dfffb9a6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c @@ -19,184 +19,108 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ + +/* + * Secure boot is the process by which NVIDIA-signed firmware is loaded into + * some of the falcons of a GPU. For production devices this is the only way + * for the firmware to access useful (but sensitive) registers. + * + * A Falcon microprocessor supporting advanced security modes can run in one of + * three modes: + * + * - Non-secure (NS). In this mode, functionality is similar to Falcon + * architectures before security modes were introduced (pre-Maxwell), but + * capability is restricted. In particular, certain registers may be + * inaccessible for reads and/or writes, and physical memory access may be + * disabled (on certain Falcon instances). This is the only possible mode that + * can be used if you don't have microcode cryptographically signed by NVIDIA. + * + * - Heavy Secure (HS). In this mode, the microprocessor is a black box - it's + * not possible to read or write any Falcon internal state or Falcon registers + * from outside the Falcon (for example, from the host system). The only way + * to enable this mode is by loading microcode that has been signed by NVIDIA. + * (The loading process involves tagging the IMEM block as secure, writing the + * signature into a Falcon register, and starting execution. The hardware will + * validate the signature, and if valid, grant HS privileges.) + * + * - Light Secure (LS). In this mode, the microprocessor has more privileges + * than NS but fewer than HS. Some of the microprocessor state is visible to + * host software to ease debugging. The only way to enable this mode is by HS + * microcode enabling LS mode. Some privileges available to HS mode are not + * available here. LS mode is introduced in GM20x. + * + * Secure boot consists in temporarily switching a HS-capable falcon (typically + * PMU) into HS mode in order to validate the LS firmwares of managed falcons, + * load them, and switch managed falcons into LS mode. Once secure boot + * completes, no falcon remains in HS mode. + * + * Secure boot requires a write-protected memory region (WPR) which can only be + * written by the secure falcon. On dGPU, the driver sets up the WPR region in + * video memory. On Tegra, it is set up by the bootloader and its location and + * size written into memory controller registers. + * + * The secure boot process takes place as follows: + * + * 1) A LS blob is constructed that contains all the LS firmwares we want to + * load, along with their signatures and bootloaders. + * + * 2) A HS blob (also called ACR) is created that contains the signed HS + * firmware in charge of loading the LS firmwares into their respective + * falcons. + * + * 3) The HS blob is loaded (via its own bootloader) and executed on the + * HS-capable falcon. It authenticates itself, switches the secure falcon to + * HS mode and setup the WPR region around the LS blob (dGPU) or copies the + * LS blob into the WPR region (Tegra). + * + * 4) The LS blob is now secure from all external tampering. The HS falcon + * checks the signatures of the LS firmwares and, if valid, switches the + * managed falcons to LS mode and makes them ready to run the LS firmware. + * + * 5) The managed falcons remain in LS mode and can be started. + * + */ + #include "priv.h" +#include "acr.h" #include <subdev/mc.h> #include <subdev/timer.h> +#include <subdev/pmu.h> -static const char * -managed_falcons_names[] = { +const char * +nvkm_secboot_falcon_name[] = { [NVKM_SECBOOT_FALCON_PMU] = "PMU", [NVKM_SECBOOT_FALCON_RESERVED] = "<reserved>", [NVKM_SECBOOT_FALCON_FECS] = "FECS", [NVKM_SECBOOT_FALCON_GPCCS] = "GPCCS", [NVKM_SECBOOT_FALCON_END] = "<invalid>", }; - -/* - * Helper falcon functions - */ - -static int -falcon_clear_halt_interrupt(struct nvkm_device *device, u32 base) -{ - int ret; - - /* clear halt interrupt */ - nvkm_mask(device, base + 0x004, 0x10, 0x10); - /* wait until halt interrupt is cleared */ - ret = nvkm_wait_msec(device, 10, base + 0x008, 0x10, 0x0); - if (ret < 0) - return ret; - - return 0; -} - -static int -falcon_wait_idle(struct nvkm_device *device, u32 base) -{ - int ret; - - ret = nvkm_wait_msec(device, 10, base + 0x04c, 0xffff, 0x0); - if (ret < 0) - return ret; - - return 0; -} - -static int -nvkm_secboot_falcon_enable(struct nvkm_secboot *sb) -{ - struct nvkm_device *device = sb->subdev.device; - int ret; - - /* enable engine */ - nvkm_mc_enable(device, sb->devidx); - ret = nvkm_wait_msec(device, 10, sb->base + 0x10c, 0x6, 0x0); - if (ret < 0) { - nvkm_error(&sb->subdev, "Falcon mem scrubbing timeout\n"); - nvkm_mc_disable(device, sb->devidx); - return ret; - } - - ret = falcon_wait_idle(device, sb->base); - if (ret) - return ret; - - /* enable IRQs */ - nvkm_wr32(device, sb->base + 0x010, 0xff); - nvkm_mc_intr_mask(device, sb->devidx, true); - - return 0; -} - -static int -nvkm_secboot_falcon_disable(struct nvkm_secboot *sb) -{ - struct nvkm_device *device = sb->subdev.device; - - /* disable IRQs and wait for any previous code to complete */ - nvkm_mc_intr_mask(device, sb->devidx, false); - nvkm_wr32(device, sb->base + 0x014, 0xff); - - falcon_wait_idle(device, sb->base); - - /* disable engine */ - nvkm_mc_disable(device, sb->devidx); - - return 0; -} - -int -nvkm_secboot_falcon_reset(struct nvkm_secboot *sb) -{ - int ret; - - ret = nvkm_secboot_falcon_disable(sb); - if (ret) - return ret; - - ret = nvkm_secboot_falcon_enable(sb); - if (ret) - return ret; - - return 0; -} - -/** - * nvkm_secboot_falcon_run - run the falcon that will perform secure boot - * - * This function is to be called after all chip-specific preparations have - * been completed. It will start the falcon to perform secure boot, wait for - * it to halt, and report if an error occurred. - */ -int -nvkm_secboot_falcon_run(struct nvkm_secboot *sb) -{ - struct nvkm_device *device = sb->subdev.device; - int ret; - - /* Start falcon */ - nvkm_wr32(device, sb->base + 0x100, 0x2); - - /* Wait for falcon halt */ - ret = nvkm_wait_msec(device, 100, sb->base + 0x100, 0x10, 0x10); - if (ret < 0) - return ret; - - /* If mailbox register contains an error code, then ACR has failed */ - ret = nvkm_rd32(device, sb->base + 0x040); - if (ret) { - nvkm_error(&sb->subdev, "ACR boot failed, ret 0x%08x", ret); - falcon_clear_halt_interrupt(device, sb->base); - return -EINVAL; - } - - return 0; -} - - /** * nvkm_secboot_reset() - reset specified falcon */ int -nvkm_secboot_reset(struct nvkm_secboot *sb, u32 falcon) +nvkm_secboot_reset(struct nvkm_secboot *sb, enum nvkm_secboot_falcon falcon) { /* Unmanaged falcon? */ - if (!(BIT(falcon) & sb->func->managed_falcons)) { + if (!(BIT(falcon) & sb->acr->managed_falcons)) { nvkm_error(&sb->subdev, "cannot reset unmanaged falcon!\n"); return -EINVAL; } - return sb->func->reset(sb, falcon); -} - -/** - * nvkm_secboot_start() - start specified falcon - */ -int -nvkm_secboot_start(struct nvkm_secboot *sb, u32 falcon) -{ - /* Unmanaged falcon? */ - if (!(BIT(falcon) & sb->func->managed_falcons)) { - nvkm_error(&sb->subdev, "cannot start unmanaged falcon!\n"); - return -EINVAL; - } - - return sb->func->start(sb, falcon); + return sb->acr->func->reset(sb->acr, sb, falcon); } /** * nvkm_secboot_is_managed() - check whether a given falcon is securely-managed */ bool -nvkm_secboot_is_managed(struct nvkm_secboot *secboot, - enum nvkm_secboot_falcon fid) +nvkm_secboot_is_managed(struct nvkm_secboot *sb, enum nvkm_secboot_falcon fid) { - if (!secboot) + if (!sb) return false; - return secboot->func->managed_falcons & BIT(fid); + return sb->acr->managed_falcons & BIT(fid); } static int @@ -205,9 +129,19 @@ nvkm_secboot_oneinit(struct nvkm_subdev *subdev) struct nvkm_secboot *sb = nvkm_secboot(subdev); int ret = 0; + switch (sb->acr->boot_falcon) { + case NVKM_SECBOOT_FALCON_PMU: + sb->boot_falcon = subdev->device->pmu->falcon; + break; + default: + nvkm_error(subdev, "Unmanaged boot falcon %s!\n", + nvkm_secboot_falcon_name[sb->acr->boot_falcon]); + return -EINVAL; + } + /* Call chip-specific init function */ - if (sb->func->init) - ret = sb->func->init(sb); + if (sb->func->oneinit) + ret = sb->func->oneinit(sb); if (ret) { nvkm_error(subdev, "Secure Boot initialization failed: %d\n", ret); @@ -249,7 +183,7 @@ nvkm_secboot = { }; int -nvkm_secboot_ctor(const struct nvkm_secboot_func *func, +nvkm_secboot_ctor(const struct nvkm_secboot_func *func, struct nvkm_acr *acr, struct nvkm_device *device, int index, struct nvkm_secboot *sb) { @@ -257,22 +191,14 @@ nvkm_secboot_ctor(const struct nvkm_secboot_func *func, nvkm_subdev_ctor(&nvkm_secboot, device, index, &sb->subdev); sb->func = func; - - /* setup the performing falcon's base address and masks */ - switch (func->boot_falcon) { - case NVKM_SECBOOT_FALCON_PMU: - sb->devidx = NVKM_SUBDEV_PMU; - sb->base = 0x10a000; - break; - default: - nvkm_error(&sb->subdev, "invalid secure boot falcon\n"); - return -EINVAL; - }; + sb->acr = acr; + acr->subdev = &sb->subdev; nvkm_debug(&sb->subdev, "securely managed falcons:\n"); - for_each_set_bit(fid, &sb->func->managed_falcons, + for_each_set_bit(fid, &sb->acr->managed_falcons, NVKM_SECBOOT_FALCON_END) - nvkm_debug(&sb->subdev, "- %s\n", managed_falcons_names[fid]); + nvkm_debug(&sb->subdev, "- %s\n", + nvkm_secboot_falcon_name[fid]); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c index ec48e4ace37a..813c4eb0b25f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c @@ -20,1313 +20,84 @@ * DEALINGS IN THE SOFTWARE. */ -/* - * Secure boot is the process by which NVIDIA-signed firmware is loaded into - * some of the falcons of a GPU. For production devices this is the only way - * for the firmware to access useful (but sensitive) registers. - * - * A Falcon microprocessor supporting advanced security modes can run in one of - * three modes: - * - * - Non-secure (NS). In this mode, functionality is similar to Falcon - * architectures before security modes were introduced (pre-Maxwell), but - * capability is restricted. In particular, certain registers may be - * inaccessible for reads and/or writes, and physical memory access may be - * disabled (on certain Falcon instances). This is the only possible mode that - * can be used if you don't have microcode cryptographically signed by NVIDIA. - * - * - Heavy Secure (HS). In this mode, the microprocessor is a black box - it's - * not possible to read or write any Falcon internal state or Falcon registers - * from outside the Falcon (for example, from the host system). The only way - * to enable this mode is by loading microcode that has been signed by NVIDIA. - * (The loading process involves tagging the IMEM block as secure, writing the - * signature into a Falcon register, and starting execution. The hardware will - * validate the signature, and if valid, grant HS privileges.) - * - * - Light Secure (LS). In this mode, the microprocessor has more privileges - * than NS but fewer than HS. Some of the microprocessor state is visible to - * host software to ease debugging. The only way to enable this mode is by HS - * microcode enabling LS mode. Some privileges available to HS mode are not - * available here. LS mode is introduced in GM20x. - * - * Secure boot consists in temporarily switching a HS-capable falcon (typically - * PMU) into HS mode in order to validate the LS firmwares of managed falcons, - * load them, and switch managed falcons into LS mode. Once secure boot - * completes, no falcon remains in HS mode. - * - * Secure boot requires a write-protected memory region (WPR) which can only be - * written by the secure falcon. On dGPU, the driver sets up the WPR region in - * video memory. On Tegra, it is set up by the bootloader and its location and - * size written into memory controller registers. - * - * The secure boot process takes place as follows: - * - * 1) A LS blob is constructed that contains all the LS firmwares we want to - * load, along with their signatures and bootloaders. - * - * 2) A HS blob (also called ACR) is created that contains the signed HS - * firmware in charge of loading the LS firmwares into their respective - * falcons. - * - * 3) The HS blob is loaded (via its own bootloader) and executed on the - * HS-capable falcon. It authenticates itself, switches the secure falcon to - * HS mode and setup the WPR region around the LS blob (dGPU) or copies the - * LS blob into the WPR region (Tegra). - * - * 4) The LS blob is now secure from all external tampering. The HS falcon - * checks the signatures of the LS firmwares and, if valid, switches the - * managed falcons to LS mode and makes them ready to run the LS firmware. - * - * 5) The managed falcons remain in LS mode and can be started. - * - */ -#include "priv.h" +#include "acr.h" +#include "gm200.h" #include <core/gpuobj.h> -#include <core/firmware.h> #include <subdev/fb.h> - -enum { - FALCON_DMAIDX_UCODE = 0, - FALCON_DMAIDX_VIRT = 1, - FALCON_DMAIDX_PHYS_VID = 2, - FALCON_DMAIDX_PHYS_SYS_COH = 3, - FALCON_DMAIDX_PHYS_SYS_NCOH = 4, -}; - -/** - * struct fw_bin_header - header of firmware files - * @bin_magic: always 0x3b1d14f0 - * @bin_ver: version of the bin format - * @bin_size: entire image size including this header - * @header_offset: offset of the firmware/bootloader header in the file - * @data_offset: offset of the firmware/bootloader payload in the file - * @data_size: size of the payload - * - * This header is located at the beginning of the HS firmware and HS bootloader - * files, to describe where the headers and data can be found. - */ -struct fw_bin_header { - u32 bin_magic; - u32 bin_ver; - u32 bin_size; - u32 header_offset; - u32 data_offset; - u32 data_size; -}; - -/** - * struct fw_bl_desc - firmware bootloader descriptor - * @start_tag: starting tag of bootloader - * @desc_dmem_load_off: DMEM offset of flcn_bl_dmem_desc - * @code_off: offset of code section - * @code_size: size of code section - * @data_off: offset of data section - * @data_size: size of data section - * - * This structure is embedded in bootloader firmware files at to describe the - * IMEM and DMEM layout expected by the bootloader. - */ -struct fw_bl_desc { - u32 start_tag; - u32 dmem_load_off; - u32 code_off; - u32 code_size; - u32 data_off; - u32 data_size; -}; - - -/* - * - * LS blob structures - * - */ - -/** - * struct lsf_ucode_desc - LS falcon signatures - * @prd_keys: signature to use when the GPU is in production mode - * @dgb_keys: signature to use when the GPU is in debug mode - * @b_prd_present: whether the production key is present - * @b_dgb_present: whether the debug key is present - * @falcon_id: ID of the falcon the ucode applies to - * - * Directly loaded from a signature file. - */ -struct lsf_ucode_desc { - u8 prd_keys[2][16]; - u8 dbg_keys[2][16]; - u32 b_prd_present; - u32 b_dbg_present; - u32 falcon_id; -}; - -/** - * struct lsf_lsb_header - LS firmware header - * @signature: signature to verify the firmware against - * @ucode_off: offset of the ucode blob in the WPR region. The ucode - * blob contains the bootloader, code and data of the - * LS falcon - * @ucode_size: size of the ucode blob, including bootloader - * @data_size: size of the ucode blob data - * @bl_code_size: size of the bootloader code - * @bl_imem_off: offset in imem of the bootloader - * @bl_data_off: offset of the bootloader data in WPR region - * @bl_data_size: size of the bootloader data - * @app_code_off: offset of the app code relative to ucode_off - * @app_code_size: size of the app code - * @app_data_off: offset of the app data relative to ucode_off - * @app_data_size: size of the app data - * @flags: flags for the secure bootloader - * - * This structure is written into the WPR region for each managed falcon. Each - * instance is referenced by the lsb_offset member of the corresponding - * lsf_wpr_header. - */ -struct lsf_lsb_header { - struct lsf_ucode_desc signature; - u32 ucode_off; - u32 ucode_size; - u32 data_size; - u32 bl_code_size; - u32 bl_imem_off; - u32 bl_data_off; - u32 bl_data_size; - u32 app_code_off; - u32 app_code_size; - u32 app_data_off; - u32 app_data_size; - u32 flags; -#define LSF_FLAG_LOAD_CODE_AT_0 1 -#define LSF_FLAG_DMACTL_REQ_CTX 4 -#define LSF_FLAG_FORCE_PRIV_LOAD 8 -}; - -/** - * struct lsf_wpr_header - LS blob WPR Header - * @falcon_id: LS falcon ID - * @lsb_offset: offset of the lsb_lsf_header in the WPR region - * @bootstrap_owner: secure falcon reponsible for bootstrapping the LS falcon - * @lazy_bootstrap: skip bootstrapping by ACR - * @status: bootstrapping status - * - * An array of these is written at the beginning of the WPR region, one for - * each managed falcon. The array is terminated by an instance which falcon_id - * is LSF_FALCON_ID_INVALID. - */ -struct lsf_wpr_header { - u32 falcon_id; - u32 lsb_offset; - u32 bootstrap_owner; - u32 lazy_bootstrap; - u32 status; -#define LSF_IMAGE_STATUS_NONE 0 -#define LSF_IMAGE_STATUS_COPY 1 -#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED 2 -#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED 3 -#define LSF_IMAGE_STATUS_VALIDATION_DONE 4 -#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED 5 -#define LSF_IMAGE_STATUS_BOOTSTRAP_READY 6 -}; - - -/** - * struct ls_ucode_img_desc - descriptor of firmware image - * @descriptor_size: size of this descriptor - * @image_size: size of the whole image - * @bootloader_start_offset: start offset of the bootloader in ucode image - * @bootloader_size: size of the bootloader - * @bootloader_imem_offset: start off set of the bootloader in IMEM - * @bootloader_entry_point: entry point of the bootloader in IMEM - * @app_start_offset: start offset of the LS firmware - * @app_size: size of the LS firmware's code and data - * @app_imem_offset: offset of the app in IMEM - * @app_imem_entry: entry point of the app in IMEM - * @app_dmem_offset: offset of the data in DMEM - * @app_resident_code_offset: offset of app code from app_start_offset - * @app_resident_code_size: size of the code - * @app_resident_data_offset: offset of data from app_start_offset - * @app_resident_data_size: size of data - * - * A firmware image contains the code, data, and bootloader of a given LS - * falcon in a single blob. This structure describes where everything is. - * - * This can be generated from a (bootloader, code, data) set if they have - * been loaded separately, or come directly from a file. - */ -struct ls_ucode_img_desc { - u32 descriptor_size; - u32 image_size; - u32 tools_version; - u32 app_version; - char date[64]; - u32 bootloader_start_offset; - u32 bootloader_size; - u32 bootloader_imem_offset; - u32 bootloader_entry_point; - u32 app_start_offset; - u32 app_size; - u32 app_imem_offset; - u32 app_imem_entry; - u32 app_dmem_offset; - u32 app_resident_code_offset; - u32 app_resident_code_size; - u32 app_resident_data_offset; - u32 app_resident_data_size; - u32 nb_overlays; - struct {u32 start; u32 size; } load_ovl[64]; - u32 compressed; -}; - -/** - * struct ls_ucode_img - temporary storage for loaded LS firmwares - * @node: to link within lsf_ucode_mgr - * @falcon_id: ID of the falcon this LS firmware is for - * @ucode_desc: loaded or generated map of ucode_data - * @ucode_header: header of the firmware - * @ucode_data: firmware payload (code and data) - * @ucode_size: size in bytes of data in ucode_data - * @wpr_header: WPR header to be written to the LS blob - * @lsb_header: LSB header to be written to the LS blob - * - * Preparing the WPR LS blob requires information about all the LS firmwares - * (size, etc) to be known. This structure contains all the data of one LS - * firmware. - */ -struct ls_ucode_img { - struct list_head node; - enum nvkm_secboot_falcon falcon_id; - - struct ls_ucode_img_desc ucode_desc; - u32 *ucode_header; - u8 *ucode_data; - u32 ucode_size; - - struct lsf_wpr_header wpr_header; - struct lsf_lsb_header lsb_header; -}; - -/** - * struct ls_ucode_mgr - manager for all LS falcon firmwares - * @count: number of managed LS falcons - * @wpr_size: size of the required WPR region in bytes - * @img_list: linked list of lsf_ucode_img - */ -struct ls_ucode_mgr { - u16 count; - u32 wpr_size; - struct list_head img_list; -}; - - -/* - * - * HS blob structures - * - */ - -/** - * struct hsf_fw_header - HS firmware descriptor - * @sig_dbg_offset: offset of the debug signature - * @sig_dbg_size: size of the debug signature - * @sig_prod_offset: offset of the production signature - * @sig_prod_size: size of the production signature - * @patch_loc: offset of the offset (sic) of where the signature is - * @patch_sig: offset of the offset (sic) to add to sig_*_offset - * @hdr_offset: offset of the load header (see struct hs_load_header) - * @hdr_size: size of above header - * - * This structure is embedded in the HS firmware image at - * hs_bin_hdr.header_offset. - */ -struct hsf_fw_header { - u32 sig_dbg_offset; - u32 sig_dbg_size; - u32 sig_prod_offset; - u32 sig_prod_size; - u32 patch_loc; - u32 patch_sig; - u32 hdr_offset; - u32 hdr_size; -}; - -/** - * struct hsf_load_header - HS firmware load header - */ -struct hsf_load_header { - u32 non_sec_code_off; - u32 non_sec_code_size; - u32 data_dma_base; - u32 data_size; - u32 num_apps; - struct { - u32 sec_code_off; - u32 sec_code_size; - } app[0]; -}; - -/** - * Convenience function to duplicate a firmware file in memory and check that - * it has the required minimum size. - */ -static void * -gm200_secboot_load_firmware(struct nvkm_subdev *subdev, const char *name, - size_t min_size) -{ - const struct firmware *fw; - void *blob; - int ret; - - ret = nvkm_firmware_get(subdev->device, name, &fw); - if (ret) - return ERR_PTR(ret); - if (fw->size < min_size) { - nvkm_error(subdev, "%s is smaller than expected size %zu\n", - name, min_size); - nvkm_firmware_put(fw); - return ERR_PTR(-EINVAL); - } - blob = kmemdup(fw->data, fw->size, GFP_KERNEL); - nvkm_firmware_put(fw); - if (!blob) - return ERR_PTR(-ENOMEM); - - return blob; -} - - -/* - * Low-secure blob creation - */ - -#define BL_DESC_BLK_SIZE 256 -/** - * Build a ucode image and descriptor from provided bootloader, code and data. - * - * @bl: bootloader image, including 16-bytes descriptor - * @code: LS firmware code segment - * @data: LS firmware data segment - * @desc: ucode descriptor to be written - * - * Return: allocated ucode image with corresponding descriptor information. desc - * is also updated to contain the right offsets within returned image. - */ -static void * -ls_ucode_img_build(const struct firmware *bl, const struct firmware *code, - const struct firmware *data, struct ls_ucode_img_desc *desc) -{ - struct fw_bin_header *bin_hdr = (void *)bl->data; - struct fw_bl_desc *bl_desc = (void *)bl->data + bin_hdr->header_offset; - void *bl_data = (void *)bl->data + bin_hdr->data_offset; - u32 pos = 0; - void *image; - - desc->bootloader_start_offset = pos; - desc->bootloader_size = ALIGN(bl_desc->code_size, sizeof(u32)); - desc->bootloader_imem_offset = bl_desc->start_tag * 256; - desc->bootloader_entry_point = bl_desc->start_tag * 256; - - pos = ALIGN(pos + desc->bootloader_size, BL_DESC_BLK_SIZE); - desc->app_start_offset = pos; - desc->app_size = ALIGN(code->size, BL_DESC_BLK_SIZE) + - ALIGN(data->size, BL_DESC_BLK_SIZE); - desc->app_imem_offset = 0; - desc->app_imem_entry = 0; - desc->app_dmem_offset = 0; - desc->app_resident_code_offset = 0; - desc->app_resident_code_size = ALIGN(code->size, BL_DESC_BLK_SIZE); - - pos = ALIGN(pos + desc->app_resident_code_size, BL_DESC_BLK_SIZE); - desc->app_resident_data_offset = pos - desc->app_start_offset; - desc->app_resident_data_size = ALIGN(data->size, BL_DESC_BLK_SIZE); - - desc->image_size = ALIGN(bl_desc->code_size, BL_DESC_BLK_SIZE) + - desc->app_size; - - image = kzalloc(desc->image_size, GFP_KERNEL); - if (!image) - return ERR_PTR(-ENOMEM); - - memcpy(image + desc->bootloader_start_offset, bl_data, - bl_desc->code_size); - memcpy(image + desc->app_start_offset, code->data, code->size); - memcpy(image + desc->app_start_offset + desc->app_resident_data_offset, - data->data, data->size); - - return image; -} - -/** - * ls_ucode_img_load_generic() - load and prepare a LS ucode image - * - * Load the LS microcode, bootloader and signature and pack them into a single - * blob. Also generate the corresponding ucode descriptor. - */ -static int -ls_ucode_img_load_generic(struct nvkm_subdev *subdev, - struct ls_ucode_img *img, const char *falcon_name, - const u32 falcon_id) -{ - const struct firmware *bl, *code, *data; - struct lsf_ucode_desc *lsf_desc; - char f[64]; - int ret; - - img->ucode_header = NULL; - - snprintf(f, sizeof(f), "gr/%s_bl", falcon_name); - ret = nvkm_firmware_get(subdev->device, f, &bl); - if (ret) - goto error; - - snprintf(f, sizeof(f), "gr/%s_inst", falcon_name); - ret = nvkm_firmware_get(subdev->device, f, &code); - if (ret) - goto free_bl; - - snprintf(f, sizeof(f), "gr/%s_data", falcon_name); - ret = nvkm_firmware_get(subdev->device, f, &data); - if (ret) - goto free_inst; - - img->ucode_data = ls_ucode_img_build(bl, code, data, - &img->ucode_desc); - if (IS_ERR(img->ucode_data)) { - ret = PTR_ERR(img->ucode_data); - goto free_data; - } - img->ucode_size = img->ucode_desc.image_size; - - snprintf(f, sizeof(f), "gr/%s_sig", falcon_name); - lsf_desc = gm200_secboot_load_firmware(subdev, f, sizeof(*lsf_desc)); - if (IS_ERR(lsf_desc)) { - ret = PTR_ERR(lsf_desc); - goto free_image; - } - /* not needed? the signature should already have the right value */ - lsf_desc->falcon_id = falcon_id; - memcpy(&img->lsb_header.signature, lsf_desc, sizeof(*lsf_desc)); - img->falcon_id = lsf_desc->falcon_id; - kfree(lsf_desc); - - /* success path - only free requested firmware files */ - goto free_data; - -free_image: - kfree(img->ucode_data); -free_data: - nvkm_firmware_put(data); -free_inst: - nvkm_firmware_put(code); -free_bl: - nvkm_firmware_put(bl); -error: - return ret; -} - -typedef int (*lsf_load_func)(struct nvkm_subdev *, struct ls_ucode_img *); - -static int -ls_ucode_img_load_fecs(struct nvkm_subdev *subdev, struct ls_ucode_img *img) -{ - return ls_ucode_img_load_generic(subdev, img, "fecs", - NVKM_SECBOOT_FALCON_FECS); -} - -static int -ls_ucode_img_load_gpccs(struct nvkm_subdev *subdev, struct ls_ucode_img *img) -{ - return ls_ucode_img_load_generic(subdev, img, "gpccs", - NVKM_SECBOOT_FALCON_GPCCS); -} - -/** - * ls_ucode_img_load() - create a lsf_ucode_img and load it - */ -static struct ls_ucode_img * -ls_ucode_img_load(struct nvkm_subdev *subdev, lsf_load_func load_func) -{ - struct ls_ucode_img *img; - int ret; - - img = kzalloc(sizeof(*img), GFP_KERNEL); - if (!img) - return ERR_PTR(-ENOMEM); - - ret = load_func(subdev, img); - if (ret) { - kfree(img); - return ERR_PTR(ret); - } - - return img; -} - -static const lsf_load_func lsf_load_funcs[] = { - [NVKM_SECBOOT_FALCON_END] = NULL, /* reserve enough space */ - [NVKM_SECBOOT_FALCON_FECS] = ls_ucode_img_load_fecs, - [NVKM_SECBOOT_FALCON_GPCCS] = ls_ucode_img_load_gpccs, -}; - -/** - * ls_ucode_img_populate_bl_desc() - populate a DMEM BL descriptor for LS image - * @img: ucode image to generate against - * @desc: descriptor to populate - * @sb: secure boot state to use for base addresses - * - * Populate the DMEM BL descriptor with the information contained in a - * ls_ucode_desc. - * - */ -static void -ls_ucode_img_populate_bl_desc(struct ls_ucode_img *img, u64 wpr_addr, - struct gm200_flcn_bl_desc *desc) -{ - struct ls_ucode_img_desc *pdesc = &img->ucode_desc; - u64 addr_base; - - addr_base = wpr_addr + img->lsb_header.ucode_off + - pdesc->app_start_offset; - - memset(desc, 0, sizeof(*desc)); - desc->ctx_dma = FALCON_DMAIDX_UCODE; - desc->code_dma_base.lo = lower_32_bits( - (addr_base + pdesc->app_resident_code_offset)); - desc->code_dma_base.hi = upper_32_bits( - (addr_base + pdesc->app_resident_code_offset)); - desc->non_sec_code_size = pdesc->app_resident_code_size; - desc->data_dma_base.lo = lower_32_bits( - (addr_base + pdesc->app_resident_data_offset)); - desc->data_dma_base.hi = upper_32_bits( - (addr_base + pdesc->app_resident_data_offset)); - desc->data_size = pdesc->app_resident_data_size; - desc->code_entry_point = pdesc->app_imem_entry; -} - -#define LSF_LSB_HEADER_ALIGN 256 -#define LSF_BL_DATA_ALIGN 256 -#define LSF_BL_DATA_SIZE_ALIGN 256 -#define LSF_BL_CODE_SIZE_ALIGN 256 -#define LSF_UCODE_DATA_ALIGN 4096 - -/** - * ls_ucode_img_fill_headers - fill the WPR and LSB headers of an image - * @gsb: secure boot device used - * @img: image to generate for - * @offset: offset in the WPR region where this image starts - * - * Allocate space in the WPR area from offset and write the WPR and LSB headers - * accordingly. - * - * Return: offset at the end of this image. - */ -static u32 -ls_ucode_img_fill_headers(struct gm200_secboot *gsb, struct ls_ucode_img *img, - u32 offset) -{ - struct lsf_wpr_header *whdr = &img->wpr_header; - struct lsf_lsb_header *lhdr = &img->lsb_header; - struct ls_ucode_img_desc *desc = &img->ucode_desc; - - if (img->ucode_header) { - nvkm_fatal(&gsb->base.subdev, - "images withough loader are not supported yet!\n"); - return offset; - } - - /* Fill WPR header */ - whdr->falcon_id = img->falcon_id; - whdr->bootstrap_owner = gsb->base.func->boot_falcon; - whdr->status = LSF_IMAGE_STATUS_COPY; - - /* Align, save off, and include an LSB header size */ - offset = ALIGN(offset, LSF_LSB_HEADER_ALIGN); - whdr->lsb_offset = offset; - offset += sizeof(struct lsf_lsb_header); - - /* - * Align, save off, and include the original (static) ucode - * image size - */ - offset = ALIGN(offset, LSF_UCODE_DATA_ALIGN); - lhdr->ucode_off = offset; - offset += img->ucode_size; - - /* - * For falcons that use a boot loader (BL), we append a loader - * desc structure on the end of the ucode image and consider - * this the boot loader data. The host will then copy the loader - * desc args to this space within the WPR region (before locking - * down) and the HS bin will then copy them to DMEM 0 for the - * loader. - */ - lhdr->bl_code_size = ALIGN(desc->bootloader_size, - LSF_BL_CODE_SIZE_ALIGN); - lhdr->ucode_size = ALIGN(desc->app_resident_data_offset, - LSF_BL_CODE_SIZE_ALIGN) + lhdr->bl_code_size; - lhdr->data_size = ALIGN(desc->app_size, LSF_BL_CODE_SIZE_ALIGN) + - lhdr->bl_code_size - lhdr->ucode_size; - /* - * Though the BL is located at 0th offset of the image, the VA - * is different to make sure that it doesn't collide the actual - * OS VA range - */ - lhdr->bl_imem_off = desc->bootloader_imem_offset; - lhdr->app_code_off = desc->app_start_offset + - desc->app_resident_code_offset; - lhdr->app_code_size = desc->app_resident_code_size; - lhdr->app_data_off = desc->app_start_offset + - desc->app_resident_data_offset; - lhdr->app_data_size = desc->app_resident_data_size; - - lhdr->flags = 0; - if (img->falcon_id == gsb->base.func->boot_falcon) - lhdr->flags = LSF_FLAG_DMACTL_REQ_CTX; - - /* GPCCS will be loaded using PRI */ - if (img->falcon_id == NVKM_SECBOOT_FALCON_GPCCS) - lhdr->flags |= LSF_FLAG_FORCE_PRIV_LOAD; - - /* Align (size bloat) and save off BL descriptor size */ - lhdr->bl_data_size = ALIGN(sizeof(struct gm200_flcn_bl_desc), - LSF_BL_DATA_SIZE_ALIGN); - /* - * Align, save off, and include the additional BL data - */ - offset = ALIGN(offset, LSF_BL_DATA_ALIGN); - lhdr->bl_data_off = offset; - offset += lhdr->bl_data_size; - - return offset; -} - -static void -ls_ucode_mgr_init(struct ls_ucode_mgr *mgr) -{ - memset(mgr, 0, sizeof(*mgr)); - INIT_LIST_HEAD(&mgr->img_list); -} - -static void -ls_ucode_mgr_cleanup(struct ls_ucode_mgr *mgr) -{ - struct ls_ucode_img *img, *t; - - list_for_each_entry_safe(img, t, &mgr->img_list, node) { - kfree(img->ucode_data); - kfree(img->ucode_header); - kfree(img); - } -} - -static void -ls_ucode_mgr_add_img(struct ls_ucode_mgr *mgr, struct ls_ucode_img *img) -{ - mgr->count++; - list_add_tail(&img->node, &mgr->img_list); -} - -/** - * ls_ucode_mgr_fill_headers - fill WPR and LSB headers of all managed images - */ -static void -ls_ucode_mgr_fill_headers(struct gm200_secboot *gsb, struct ls_ucode_mgr *mgr) -{ - struct ls_ucode_img *img; - u32 offset; - - /* - * Start with an array of WPR headers at the base of the WPR. - * The expectation here is that the secure falcon will do a single DMA - * read of this array and cache it internally so it's ok to pack these. - * Also, we add 1 to the falcon count to indicate the end of the array. - */ - offset = sizeof(struct lsf_wpr_header) * (mgr->count + 1); - - /* - * Walk the managed falcons, accounting for the LSB structs - * as well as the ucode images. - */ - list_for_each_entry(img, &mgr->img_list, node) { - offset = ls_ucode_img_fill_headers(gsb, img, offset); - } - - mgr->wpr_size = offset; -} - -/** - * ls_ucode_mgr_write_wpr - write the WPR blob contents - */ -static int -ls_ucode_mgr_write_wpr(struct gm200_secboot *gsb, struct ls_ucode_mgr *mgr, - struct nvkm_gpuobj *wpr_blob) -{ - struct ls_ucode_img *img; - u32 pos = 0; - - nvkm_kmap(wpr_blob); - - list_for_each_entry(img, &mgr->img_list, node) { - nvkm_gpuobj_memcpy_to(wpr_blob, pos, &img->wpr_header, - sizeof(img->wpr_header)); - - nvkm_gpuobj_memcpy_to(wpr_blob, img->wpr_header.lsb_offset, - &img->lsb_header, sizeof(img->lsb_header)); - - /* Generate and write BL descriptor */ - if (!img->ucode_header) { - u8 desc[gsb->func->bl_desc_size]; - struct gm200_flcn_bl_desc gdesc; - - ls_ucode_img_populate_bl_desc(img, gsb->wpr_addr, - &gdesc); - gsb->func->fixup_bl_desc(&gdesc, &desc); - nvkm_gpuobj_memcpy_to(wpr_blob, - img->lsb_header.bl_data_off, - &desc, gsb->func->bl_desc_size); - } - - /* Copy ucode */ - nvkm_gpuobj_memcpy_to(wpr_blob, img->lsb_header.ucode_off, - img->ucode_data, img->ucode_size); - - pos += sizeof(img->wpr_header); - } - - nvkm_wo32(wpr_blob, pos, NVKM_SECBOOT_FALCON_INVALID); - - nvkm_done(wpr_blob); - - return 0; -} - -/* Both size and address of WPR need to be 128K-aligned */ -#define WPR_ALIGNMENT 0x20000 -/** - * gm200_secboot_prepare_ls_blob() - prepare the LS blob - * - * For each securely managed falcon, load the FW, signatures and bootloaders and - * prepare a ucode blob. Then, compute the offsets in the WPR region for each - * blob, and finally write the headers and ucode blobs into a GPU object that - * will be copied into the WPR region by the HS firmware. - */ -static int -gm200_secboot_prepare_ls_blob(struct gm200_secboot *gsb) -{ - struct nvkm_secboot *sb = &gsb->base; - struct nvkm_device *device = sb->subdev.device; - struct ls_ucode_mgr mgr; - int falcon_id; - int ret; - - ls_ucode_mgr_init(&mgr); - - /* Load all LS blobs */ - for_each_set_bit(falcon_id, &gsb->base.func->managed_falcons, - NVKM_SECBOOT_FALCON_END) { - struct ls_ucode_img *img; - - img = ls_ucode_img_load(&sb->subdev, lsf_load_funcs[falcon_id]); - - if (IS_ERR(img)) { - ret = PTR_ERR(img); - goto cleanup; - } - ls_ucode_mgr_add_img(&mgr, img); - } - - /* - * Fill the WPR and LSF headers with the right offsets and compute - * required WPR size - */ - ls_ucode_mgr_fill_headers(gsb, &mgr); - mgr.wpr_size = ALIGN(mgr.wpr_size, WPR_ALIGNMENT); - - /* Allocate GPU object that will contain the WPR region */ - ret = nvkm_gpuobj_new(device, mgr.wpr_size, WPR_ALIGNMENT, false, NULL, - &gsb->ls_blob); - if (ret) - goto cleanup; - - nvkm_debug(&sb->subdev, "%d managed LS falcons, WPR size is %d bytes\n", - mgr.count, mgr.wpr_size); - - /* If WPR address and size are not fixed, set them to fit the LS blob */ - if (!gsb->wpr_size) { - gsb->wpr_addr = gsb->ls_blob->addr; - gsb->wpr_size = gsb->ls_blob->size; - } - - /* Write LS blob */ - ret = ls_ucode_mgr_write_wpr(gsb, &mgr, gsb->ls_blob); - if (ret) - nvkm_gpuobj_del(&gsb->ls_blob); - -cleanup: - ls_ucode_mgr_cleanup(&mgr); - - return ret; -} - -/* - * High-secure blob creation - */ - -/** - * gm200_secboot_hsf_patch_signature() - patch HS blob with correct signature - */ -static void -gm200_secboot_hsf_patch_signature(struct gm200_secboot *gsb, void *acr_image) -{ - struct nvkm_secboot *sb = &gsb->base; - struct fw_bin_header *hsbin_hdr = acr_image; - struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset; - void *hs_data = acr_image + hsbin_hdr->data_offset; - void *sig; - u32 sig_size; - - /* Falcon in debug or production mode? */ - if ((nvkm_rd32(sb->subdev.device, sb->base + 0xc08) >> 20) & 0x1) { - sig = acr_image + fw_hdr->sig_dbg_offset; - sig_size = fw_hdr->sig_dbg_size; - } else { - sig = acr_image + fw_hdr->sig_prod_offset; - sig_size = fw_hdr->sig_prod_size; - } - - /* Patch signature */ - memcpy(hs_data + fw_hdr->patch_loc, sig + fw_hdr->patch_sig, sig_size); -} - -/** - * gm200_secboot_populate_hsf_bl_desc() - populate BL descriptor for HS image - */ -static void -gm200_secboot_populate_hsf_bl_desc(void *acr_image, - struct gm200_flcn_bl_desc *bl_desc) -{ - struct fw_bin_header *hsbin_hdr = acr_image; - struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset; - struct hsf_load_header *load_hdr = acr_image + fw_hdr->hdr_offset; - - /* - * Descriptor for the bootloader that will load the ACR image into - * IMEM/DMEM memory. - */ - fw_hdr = acr_image + hsbin_hdr->header_offset; - load_hdr = acr_image + fw_hdr->hdr_offset; - memset(bl_desc, 0, sizeof(*bl_desc)); - bl_desc->ctx_dma = FALCON_DMAIDX_VIRT; - bl_desc->non_sec_code_off = load_hdr->non_sec_code_off; - bl_desc->non_sec_code_size = load_hdr->non_sec_code_size; - bl_desc->sec_code_off = load_hdr->app[0].sec_code_off; - bl_desc->sec_code_size = load_hdr->app[0].sec_code_size; - bl_desc->code_entry_point = 0; - /* - * We need to set code_dma_base to the virtual address of the acr_blob, - * and add this address to data_dma_base before writing it into DMEM - */ - bl_desc->code_dma_base.lo = 0; - bl_desc->data_dma_base.lo = load_hdr->data_dma_base; - bl_desc->data_size = load_hdr->data_size; -} - -/** - * gm200_secboot_prepare_hs_blob - load and prepare a HS blob and BL descriptor - * - * @gsb secure boot instance to prepare for - * @fw name of the HS firmware to load - * @blob pointer to gpuobj that will be allocated to receive the HS FW payload - * @bl_desc pointer to the BL descriptor to write for this firmware - * @patch whether we should patch the HS descriptor (only for HS loaders) - */ -static int -gm200_secboot_prepare_hs_blob(struct gm200_secboot *gsb, const char *fw, - struct nvkm_gpuobj **blob, - struct gm200_flcn_bl_desc *bl_desc, bool patch) -{ - struct nvkm_subdev *subdev = &gsb->base.subdev; - void *acr_image; - struct fw_bin_header *hsbin_hdr; - struct hsf_fw_header *fw_hdr; - void *acr_data; - struct hsf_load_header *load_hdr; - struct hsflcn_acr_desc *desc; - int ret; - - acr_image = gm200_secboot_load_firmware(subdev, fw, 0); - if (IS_ERR(acr_image)) - return PTR_ERR(acr_image); - hsbin_hdr = acr_image; - - /* Patch signature */ - gm200_secboot_hsf_patch_signature(gsb, acr_image); - - acr_data = acr_image + hsbin_hdr->data_offset; - - /* Patch descriptor? */ - if (patch) { - fw_hdr = acr_image + hsbin_hdr->header_offset; - load_hdr = acr_image + fw_hdr->hdr_offset; - desc = acr_data + load_hdr->data_dma_base; - gsb->func->fixup_hs_desc(gsb, desc); - } - - /* Generate HS BL descriptor */ - gm200_secboot_populate_hsf_bl_desc(acr_image, bl_desc); - - /* Create ACR blob and copy HS data to it */ - ret = nvkm_gpuobj_new(subdev->device, ALIGN(hsbin_hdr->data_size, 256), - 0x1000, false, NULL, blob); - if (ret) - goto cleanup; - - nvkm_kmap(*blob); - nvkm_gpuobj_memcpy_to(*blob, 0, acr_data, hsbin_hdr->data_size); - nvkm_done(*blob); - -cleanup: - kfree(acr_image); - - return ret; -} - -/* - * High-secure bootloader blob creation - */ - -static int -gm200_secboot_prepare_hsbl_blob(struct gm200_secboot *gsb) -{ - struct nvkm_subdev *subdev = &gsb->base.subdev; - - gsb->hsbl_blob = gm200_secboot_load_firmware(subdev, "acr/bl", 0); - if (IS_ERR(gsb->hsbl_blob)) { - int ret = PTR_ERR(gsb->hsbl_blob); - - gsb->hsbl_blob = NULL; - return ret; - } - - return 0; -} +#include <engine/falcon.h> +#include <subdev/mc.h> /** - * gm20x_secboot_prepare_blobs - load blobs common to all GM20X GPUs. + * gm200_secboot_run_blob() - run the given high-secure blob * - * This includes the LS blob, HS ucode loading blob, and HS bootloader. - * - * The HS ucode unload blob is only used on dGPU. */ int -gm20x_secboot_prepare_blobs(struct gm200_secboot *gsb) -{ - int ret; - - /* Load and prepare the managed falcon's firmwares */ - if (!gsb->ls_blob) { - ret = gm200_secboot_prepare_ls_blob(gsb); - if (ret) - return ret; - } - - /* Load the HS firmware that will load the LS firmwares */ - if (!gsb->acr_load_blob) { - ret = gm200_secboot_prepare_hs_blob(gsb, "acr/ucode_load", - &gsb->acr_load_blob, - &gsb->acr_load_bl_desc, true); - if (ret) - return ret; - } - - /* Load the HS firmware bootloader */ - if (!gsb->hsbl_blob) { - ret = gm200_secboot_prepare_hsbl_blob(gsb); - if (ret) - return ret; - } - - return 0; -} - -static int -gm200_secboot_prepare_blobs(struct gm200_secboot *gsb) -{ - int ret; - - ret = gm20x_secboot_prepare_blobs(gsb); - if (ret) - return ret; - - /* dGPU only: load the HS firmware that unprotects the WPR region */ - if (!gsb->acr_unload_blob) { - ret = gm200_secboot_prepare_hs_blob(gsb, "acr/ucode_unload", - &gsb->acr_unload_blob, - &gsb->acr_unload_bl_desc, false); - if (ret) - return ret; - } - - return 0; -} - -static int -gm200_secboot_blobs_ready(struct gm200_secboot *gsb) +gm200_secboot_run_blob(struct nvkm_secboot *sb, struct nvkm_gpuobj *blob) { + struct gm200_secboot *gsb = gm200_secboot(sb); struct nvkm_subdev *subdev = &gsb->base.subdev; + struct nvkm_falcon *falcon = gsb->base.boot_falcon; + struct nvkm_vma vma; int ret; - /* firmware already loaded, nothing to do... */ - if (gsb->firmware_ok) - return 0; - - ret = gsb->func->prepare_blobs(gsb); - if (ret) { - nvkm_error(subdev, "failed to load secure firmware\n"); - return ret; - } - - gsb->firmware_ok = true; - - return 0; -} - - -/* - * Secure Boot Execution - */ - -/** - * gm200_secboot_load_hs_bl() - load HS bootloader into DMEM and IMEM - */ -static void -gm200_secboot_load_hs_bl(struct gm200_secboot *gsb, void *data, u32 data_size) -{ - struct nvkm_device *device = gsb->base.subdev.device; - struct fw_bin_header *hdr = gsb->hsbl_blob; - struct fw_bl_desc *hsbl_desc = gsb->hsbl_blob + hdr->header_offset; - void *blob_data = gsb->hsbl_blob + hdr->data_offset; - void *hsbl_code = blob_data + hsbl_desc->code_off; - void *hsbl_data = blob_data + hsbl_desc->data_off; - u32 code_size = ALIGN(hsbl_desc->code_size, 256); - const u32 base = gsb->base.base; - u32 blk; - u32 tag; - int i; - - /* - * Copy HS bootloader data - */ - nvkm_wr32(device, base + 0x1c0, (0x00000000 | (0x1 << 24))); - for (i = 0; i < hsbl_desc->data_size / 4; i++) - nvkm_wr32(device, base + 0x1c4, ((u32 *)hsbl_data)[i]); - - /* - * Copy HS bootloader interface structure where the HS descriptor - * expects it to be - */ - nvkm_wr32(device, base + 0x1c0, - (hsbl_desc->dmem_load_off | (0x1 << 24))); - for (i = 0; i < data_size / 4; i++) - nvkm_wr32(device, base + 0x1c4, ((u32 *)data)[i]); - - /* Copy HS bootloader code to end of IMEM */ - blk = (nvkm_rd32(device, base + 0x108) & 0x1ff) - (code_size >> 8); - tag = hsbl_desc->start_tag; - nvkm_wr32(device, base + 0x180, ((blk & 0xff) << 8) | (0x1 << 24)); - for (i = 0; i < code_size / 4; i++) { - /* write new tag every 256B */ - if ((i & 0x3f) == 0) { - nvkm_wr32(device, base + 0x188, tag & 0xffff); - tag++; - } - nvkm_wr32(device, base + 0x184, ((u32 *)hsbl_code)[i]); - } - nvkm_wr32(device, base + 0x188, 0); -} - -/** - * gm200_secboot_setup_falcon() - set up the secure falcon for secure boot - */ -static int -gm200_secboot_setup_falcon(struct gm200_secboot *gsb) -{ - struct nvkm_device *device = gsb->base.subdev.device; - struct fw_bin_header *hdr = gsb->hsbl_blob; - struct fw_bl_desc *hsbl_desc = gsb->hsbl_blob + hdr->header_offset; - /* virtual start address for boot vector */ - u32 virt_addr = hsbl_desc->start_tag << 8; - const u32 base = gsb->base.base; - const u32 reg_base = base + 0xe00; - u32 inst_loc; - int ret; - - ret = nvkm_secboot_falcon_reset(&gsb->base); + ret = nvkm_falcon_get(falcon, subdev); if (ret) return ret; - /* setup apertures - virtual */ - nvkm_wr32(device, reg_base + 4 * (FALCON_DMAIDX_UCODE), 0x4); - nvkm_wr32(device, reg_base + 4 * (FALCON_DMAIDX_VIRT), 0x0); - /* setup apertures - physical */ - nvkm_wr32(device, reg_base + 4 * (FALCON_DMAIDX_PHYS_VID), 0x4); - nvkm_wr32(device, reg_base + 4 * (FALCON_DMAIDX_PHYS_SYS_COH), - 0x4 | 0x1); - nvkm_wr32(device, reg_base + 4 * (FALCON_DMAIDX_PHYS_SYS_NCOH), - 0x4 | 0x2); - - /* Set context */ - if (nvkm_memory_target(gsb->inst->memory) == NVKM_MEM_TARGET_VRAM) - inst_loc = 0x0; /* FB */ - else - inst_loc = 0x3; /* Non-coherent sysmem */ - - nvkm_mask(device, base + 0x048, 0x1, 0x1); - nvkm_wr32(device, base + 0x480, - ((gsb->inst->addr >> 12) & 0xfffffff) | - (inst_loc << 28) | (1 << 30)); - - /* Set boot vector to code's starting virtual address */ - nvkm_wr32(device, base + 0x104, virt_addr); - - return 0; -} - -/** - * gm200_secboot_run_hs_blob() - run the given high-secure blob - */ -static int -gm200_secboot_run_hs_blob(struct gm200_secboot *gsb, struct nvkm_gpuobj *blob, - struct gm200_flcn_bl_desc *desc) -{ - struct nvkm_vma vma; - u64 vma_addr; - const u32 bl_desc_size = gsb->func->bl_desc_size; - u8 bl_desc[bl_desc_size]; - int ret; - /* Map the HS firmware so the HS bootloader can see it */ ret = nvkm_gpuobj_map(blob, gsb->vm, NV_MEM_ACCESS_RW, &vma); - if (ret) + if (ret) { + nvkm_falcon_put(falcon, subdev); return ret; + } - /* Add the mapping address to the DMA bases */ - vma_addr = flcn64_to_u64(desc->code_dma_base) + vma.offset; - desc->code_dma_base.lo = lower_32_bits(vma_addr); - desc->code_dma_base.hi = upper_32_bits(vma_addr); - vma_addr = flcn64_to_u64(desc->data_dma_base) + vma.offset; - desc->data_dma_base.lo = lower_32_bits(vma_addr); - desc->data_dma_base.hi = upper_32_bits(vma_addr); - - /* Fixup the BL header */ - gsb->func->fixup_bl_desc(desc, &bl_desc); - - /* Reset the falcon and make it ready to run the HS bootloader */ - ret = gm200_secboot_setup_falcon(gsb); + /* Reset and set the falcon up */ + ret = nvkm_falcon_reset(falcon); if (ret) - goto done; + goto end; + nvkm_falcon_bind_context(falcon, gsb->inst); /* Load the HS bootloader into the falcon's IMEM/DMEM */ - gm200_secboot_load_hs_bl(gsb, &bl_desc, bl_desc_size); - - /* Start the HS bootloader */ - ret = nvkm_secboot_falcon_run(&gsb->base); + ret = sb->acr->func->load(sb->acr, &gsb->base, blob, vma.offset); if (ret) - goto done; - -done: - /* Restore the original DMA addresses */ - vma_addr = flcn64_to_u64(desc->code_dma_base) - vma.offset; - desc->code_dma_base.lo = lower_32_bits(vma_addr); - desc->code_dma_base.hi = upper_32_bits(vma_addr); - vma_addr = flcn64_to_u64(desc->data_dma_base) - vma.offset; - desc->data_dma_base.lo = lower_32_bits(vma_addr); - desc->data_dma_base.hi = upper_32_bits(vma_addr); - - /* We don't need the ACR firmware anymore */ - nvkm_gpuobj_unmap(&vma); + goto end; - return ret; -} + /* Disable interrupts as we will poll for the HALT bit */ + nvkm_mc_intr_mask(sb->subdev.device, falcon->owner->index, false); -/* - * gm200_secboot_reset() - execute secure boot from the prepared state - * - * Load the HS bootloader and ask the falcon to run it. This will in turn - * load the HS firmware and run it, so once the falcon stops all the managed - * falcons should have their LS firmware loaded and be ready to run. - */ -int -gm200_secboot_reset(struct nvkm_secboot *sb, enum nvkm_secboot_falcon falcon) -{ - struct gm200_secboot *gsb = gm200_secboot(sb); - int ret; + /* Set default error value in mailbox register */ + nvkm_falcon_wr32(falcon, 0x040, 0xdeada5a5); - /* Make sure all blobs are ready */ - ret = gm200_secboot_blobs_ready(gsb); + /* Start the HS bootloader */ + nvkm_falcon_set_start_addr(falcon, sb->acr->start_address); + nvkm_falcon_start(falcon); + ret = nvkm_falcon_wait_for_halt(falcon, 100); if (ret) - return ret; - - /* - * Dummy GM200 implementation: perform secure boot each time we are - * called on FECS. Since only FECS and GPCCS are managed and started - * together, this ought to be safe. - * - * Once we have proper PMU firmware and support, this will be changed - * to a proper call to the PMU method. - */ - if (falcon != NVKM_SECBOOT_FALCON_FECS) goto end; - /* If WPR is set and we have an unload blob, run it to unlock WPR */ - if (gsb->acr_unload_blob && - gsb->falcon_state[NVKM_SECBOOT_FALCON_FECS] != NON_SECURE) { - ret = gm200_secboot_run_hs_blob(gsb, gsb->acr_unload_blob, - &gsb->acr_unload_bl_desc); - if (ret) - return ret; + /* If mailbox register contains an error code, then ACR has failed */ + ret = nvkm_falcon_rd32(falcon, 0x040); + if (ret) { + nvkm_error(subdev, "ACR boot failed, ret 0x%08x", ret); + ret = -EINVAL; + goto end; } - /* Reload all managed falcons */ - ret = gm200_secboot_run_hs_blob(gsb, gsb->acr_load_blob, - &gsb->acr_load_bl_desc); - if (ret) - return ret; - end: - gsb->falcon_state[falcon] = RESET; - return 0; -} + /* Reenable interrupts */ + nvkm_mc_intr_mask(sb->subdev.device, falcon->owner->index, true); -int -gm200_secboot_start(struct nvkm_secboot *sb, enum nvkm_secboot_falcon falcon) -{ - struct gm200_secboot *gsb = gm200_secboot(sb); - int base; - - switch (falcon) { - case NVKM_SECBOOT_FALCON_FECS: - base = 0x409000; - break; - case NVKM_SECBOOT_FALCON_GPCCS: - base = 0x41a000; - break; - default: - nvkm_error(&sb->subdev, "cannot start unhandled falcon!\n"); - return -EINVAL; - } - - nvkm_wr32(sb->subdev.device, base + 0x130, 0x00000002); - gsb->falcon_state[falcon] = RUNNING; + /* We don't need the ACR firmware anymore */ + nvkm_gpuobj_unmap(&vma); + nvkm_falcon_put(falcon, subdev); - return 0; + return ret; } - - int -gm200_secboot_init(struct nvkm_secboot *sb) +gm200_secboot_oneinit(struct nvkm_secboot *sb) { struct gm200_secboot *gsb = gm200_secboot(sb); struct nvkm_device *device = sb->subdev.device; @@ -1361,24 +132,22 @@ gm200_secboot_init(struct nvkm_secboot *sb) nvkm_wo32(gsb->inst, 0x20c, upper_32_bits(vm_area_len - 1)); nvkm_done(gsb->inst); + if (sb->acr->func->oneinit) { + ret = sb->acr->func->oneinit(sb->acr, sb); + if (ret) + return ret; + } + return 0; } -static int +int gm200_secboot_fini(struct nvkm_secboot *sb, bool suspend) { - struct gm200_secboot *gsb = gm200_secboot(sb); int ret = 0; - int i; - /* Run the unload blob to unprotect the WPR region */ - if (gsb->acr_unload_blob && - gsb->falcon_state[NVKM_SECBOOT_FALCON_FECS] != NON_SECURE) - ret = gm200_secboot_run_hs_blob(gsb, gsb->acr_unload_blob, - &gsb->acr_unload_bl_desc); - - for (i = 0; i < NVKM_SECBOOT_FALCON_END; i++) - gsb->falcon_state[i] = NON_SECURE; + if (sb->acr->func->fini) + ret = sb->acr->func->fini(sb->acr, sb, suspend); return ret; } @@ -1388,11 +157,7 @@ gm200_secboot_dtor(struct nvkm_secboot *sb) { struct gm200_secboot *gsb = gm200_secboot(sb); - nvkm_gpuobj_del(&gsb->acr_unload_blob); - - kfree(gsb->hsbl_blob); - nvkm_gpuobj_del(&gsb->acr_load_blob); - nvkm_gpuobj_del(&gsb->ls_blob); + sb->acr->func->dtor(sb->acr); nvkm_vm_ref(NULL, &gsb->vm, gsb->pgd); nvkm_gpuobj_del(&gsb->pgd); @@ -1405,50 +170,9 @@ gm200_secboot_dtor(struct nvkm_secboot *sb) static const struct nvkm_secboot_func gm200_secboot = { .dtor = gm200_secboot_dtor, - .init = gm200_secboot_init, + .oneinit = gm200_secboot_oneinit, .fini = gm200_secboot_fini, - .reset = gm200_secboot_reset, - .start = gm200_secboot_start, - .managed_falcons = BIT(NVKM_SECBOOT_FALCON_FECS) | - BIT(NVKM_SECBOOT_FALCON_GPCCS), - .boot_falcon = NVKM_SECBOOT_FALCON_PMU, -}; - -/** - * gm200_fixup_bl_desc - just copy the BL descriptor - * - * Use the GM200 descriptor format by default. - */ -static void -gm200_secboot_fixup_bl_desc(const struct gm200_flcn_bl_desc *desc, void *ret) -{ - memcpy(ret, desc, sizeof(*desc)); -} - -static void -gm200_secboot_fixup_hs_desc(struct gm200_secboot *gsb, - struct hsflcn_acr_desc *desc) -{ - desc->ucode_blob_base = gsb->ls_blob->addr; - desc->ucode_blob_size = gsb->ls_blob->size; - - desc->wpr_offset = 0; - - /* WPR region information for the HS binary to set up */ - desc->wpr_region_id = 1; - desc->regions.no_regions = 1; - desc->regions.region_props[0].region_id = 1; - desc->regions.region_props[0].start_addr = gsb->wpr_addr >> 8; - desc->regions.region_props[0].end_addr = - (gsb->wpr_addr + gsb->wpr_size) >> 8; -} - -static const struct gm200_secboot_func -gm200_secboot_func = { - .bl_desc_size = sizeof(struct gm200_flcn_bl_desc), - .fixup_bl_desc = gm200_secboot_fixup_bl_desc, - .fixup_hs_desc = gm200_secboot_fixup_hs_desc, - .prepare_blobs = gm200_secboot_prepare_blobs, + .run_blob = gm200_secboot_run_blob, }; int @@ -1457,6 +181,12 @@ gm200_secboot_new(struct nvkm_device *device, int index, { int ret; struct gm200_secboot *gsb; + struct nvkm_acr *acr; + + acr = acr_r361_new(BIT(NVKM_SECBOOT_FALCON_FECS) | + BIT(NVKM_SECBOOT_FALCON_GPCCS)); + if (IS_ERR(acr)) + return PTR_ERR(acr); gsb = kzalloc(sizeof(*gsb), GFP_KERNEL); if (!gsb) { @@ -1465,15 +195,14 @@ gm200_secboot_new(struct nvkm_device *device, int index, } *psb = &gsb->base; - ret = nvkm_secboot_ctor(&gm200_secboot, device, index, &gsb->base); + ret = nvkm_secboot_ctor(&gm200_secboot, acr, device, index, &gsb->base); if (ret) return ret; - gsb->func = &gm200_secboot_func; - return 0; } + MODULE_FIRMWARE("nvidia/gm200/acr/bl.bin"); MODULE_FIRMWARE("nvidia/gm200/acr/ucode_load.bin"); MODULE_FIRMWARE("nvidia/gm200/acr/ucode_unload.bin"); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.h new file mode 100644 index 000000000000..45adf1a3bc20 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NVKM_SECBOOT_GM200_H__ +#define __NVKM_SECBOOT_GM200_H__ + +#include "priv.h" + +struct gm200_secboot { + struct nvkm_secboot base; + + /* Instance block & address space used for HS FW execution */ + struct nvkm_gpuobj *inst; + struct nvkm_gpuobj *pgd; + struct nvkm_vm *vm; +}; +#define gm200_secboot(sb) container_of(sb, struct gm200_secboot, base) + +int gm200_secboot_oneinit(struct nvkm_secboot *); +int gm200_secboot_fini(struct nvkm_secboot *, bool); +void *gm200_secboot_dtor(struct nvkm_secboot *); +int gm200_secboot_run_blob(struct nvkm_secboot *, struct nvkm_gpuobj *); + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c index d5395ebfe8d3..6707b8edc086 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c @@ -20,103 +20,8 @@ * DEALINGS IN THE SOFTWARE. */ -#include "priv.h" - -#include <core/gpuobj.h> - -/* - * The BL header format used by GM20B's firmware is slightly different - * from the one of GM200. Fix the differences here. - */ -struct gm20b_flcn_bl_desc { - u32 reserved[4]; - u32 signature[4]; - u32 ctx_dma; - u32 code_dma_base; - u32 non_sec_code_off; - u32 non_sec_code_size; - u32 sec_code_off; - u32 sec_code_size; - u32 code_entry_point; - u32 data_dma_base; - u32 data_size; -}; - -static int -gm20b_secboot_prepare_blobs(struct gm200_secboot *gsb) -{ - struct nvkm_subdev *subdev = &gsb->base.subdev; - int acr_size; - int ret; - - ret = gm20x_secboot_prepare_blobs(gsb); - if (ret) - return ret; - - acr_size = gsb->acr_load_blob->size; - /* - * On Tegra the WPR region is set by the bootloader. It is illegal for - * the HS blob to be larger than this region. - */ - if (acr_size > gsb->wpr_size) { - nvkm_error(subdev, "WPR region too small for FW blob!\n"); - nvkm_error(subdev, "required: %dB\n", acr_size); - nvkm_error(subdev, "WPR size: %dB\n", gsb->wpr_size); - return -ENOSPC; - } - - return 0; -} - -/** - * gm20b_secboot_fixup_bl_desc - adapt BL descriptor to format used by GM20B FW - * - * There is only a slight format difference (DMA addresses being 32-bits and - * 256B-aligned) to address. - */ -static void -gm20b_secboot_fixup_bl_desc(const struct gm200_flcn_bl_desc *desc, void *ret) -{ - struct gm20b_flcn_bl_desc *gdesc = ret; - u64 addr; - - memcpy(gdesc->reserved, desc->reserved, sizeof(gdesc->reserved)); - memcpy(gdesc->signature, desc->signature, sizeof(gdesc->signature)); - gdesc->ctx_dma = desc->ctx_dma; - addr = desc->code_dma_base.hi; - addr <<= 32; - addr |= desc->code_dma_base.lo; - gdesc->code_dma_base = lower_32_bits(addr >> 8); - gdesc->non_sec_code_off = desc->non_sec_code_off; - gdesc->non_sec_code_size = desc->non_sec_code_size; - gdesc->sec_code_off = desc->sec_code_off; - gdesc->sec_code_size = desc->sec_code_size; - gdesc->code_entry_point = desc->code_entry_point; - addr = desc->data_dma_base.hi; - addr <<= 32; - addr |= desc->data_dma_base.lo; - gdesc->data_dma_base = lower_32_bits(addr >> 8); - gdesc->data_size = desc->data_size; -} - -static void -gm20b_secboot_fixup_hs_desc(struct gm200_secboot *gsb, - struct hsflcn_acr_desc *desc) -{ - desc->ucode_blob_base = gsb->ls_blob->addr; - desc->ucode_blob_size = gsb->ls_blob->size; - - desc->wpr_offset = 0; -} - -static const struct gm200_secboot_func -gm20b_secboot_func = { - .bl_desc_size = sizeof(struct gm20b_flcn_bl_desc), - .fixup_bl_desc = gm20b_secboot_fixup_bl_desc, - .fixup_hs_desc = gm20b_secboot_fixup_hs_desc, - .prepare_blobs = gm20b_secboot_prepare_blobs, -}; - +#include "acr.h" +#include "gm200.h" #ifdef CONFIG_ARCH_TEGRA #define TEGRA_MC_BASE 0x70019000 @@ -144,15 +49,15 @@ gm20b_tegra_read_wpr(struct gm200_secboot *gsb) nvkm_error(&sb->subdev, "Cannot map Tegra MC registers\n"); return PTR_ERR(mc); } - gsb->wpr_addr = ioread32_native(mc + MC_SECURITY_CARVEOUT2_BOM_0) | + sb->wpr_addr = ioread32_native(mc + MC_SECURITY_CARVEOUT2_BOM_0) | ((u64)ioread32_native(mc + MC_SECURITY_CARVEOUT2_BOM_HI_0) << 32); - gsb->wpr_size = ioread32_native(mc + MC_SECURITY_CARVEOUT2_SIZE_128K) + sb->wpr_size = ioread32_native(mc + MC_SECURITY_CARVEOUT2_SIZE_128K) << 17; cfg = ioread32_native(mc + MC_SECURITY_CARVEOUT2_CFG0); iounmap(mc); /* Check that WPR settings are valid */ - if (gsb->wpr_size == 0) { + if (sb->wpr_size == 0) { nvkm_error(&sb->subdev, "WPR region is empty\n"); return -EINVAL; } @@ -174,7 +79,7 @@ gm20b_tegra_read_wpr(struct gm200_secboot *gsb) #endif static int -gm20b_secboot_init(struct nvkm_secboot *sb) +gm20b_secboot_oneinit(struct nvkm_secboot *sb) { struct gm200_secboot *gsb = gm200_secboot(sb); int ret; @@ -183,17 +88,15 @@ gm20b_secboot_init(struct nvkm_secboot *sb) if (ret) return ret; - return gm200_secboot_init(sb); + return gm200_secboot_oneinit(sb); } static const struct nvkm_secboot_func gm20b_secboot = { .dtor = gm200_secboot_dtor, - .init = gm20b_secboot_init, - .reset = gm200_secboot_reset, - .start = gm200_secboot_start, - .managed_falcons = BIT(NVKM_SECBOOT_FALCON_FECS), - .boot_falcon = NVKM_SECBOOT_FALCON_PMU, + .oneinit = gm20b_secboot_oneinit, + .fini = gm200_secboot_fini, + .run_blob = gm200_secboot_run_blob, }; int @@ -202,6 +105,11 @@ gm20b_secboot_new(struct nvkm_device *device, int index, { int ret; struct gm200_secboot *gsb; + struct nvkm_acr *acr; + + acr = acr_r352_new(BIT(NVKM_SECBOOT_FALCON_FECS)); + if (IS_ERR(acr)) + return PTR_ERR(acr); gsb = kzalloc(sizeof(*gsb), GFP_KERNEL); if (!gsb) { @@ -210,12 +118,10 @@ gm20b_secboot_new(struct nvkm_device *device, int index, } *psb = &gsb->base; - ret = nvkm_secboot_ctor(&gm20b_secboot, device, index, &gsb->base); + ret = nvkm_secboot_ctor(&gm20b_secboot, acr, device, index, &gsb->base); if (ret) return ret; - gsb->func = &gm20b_secboot_func; - return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h new file mode 100644 index 000000000000..00886cee57eb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NVKM_SECBOOT_LS_UCODE_H__ +#define __NVKM_SECBOOT_LS_UCODE_H__ + +#include <core/os.h> +#include <core/subdev.h> +#include <subdev/secboot.h> + + +/** + * struct ls_ucode_img_desc - descriptor of firmware image + * @descriptor_size: size of this descriptor + * @image_size: size of the whole image + * @bootloader_start_offset: start offset of the bootloader in ucode image + * @bootloader_size: size of the bootloader + * @bootloader_imem_offset: start off set of the bootloader in IMEM + * @bootloader_entry_point: entry point of the bootloader in IMEM + * @app_start_offset: start offset of the LS firmware + * @app_size: size of the LS firmware's code and data + * @app_imem_offset: offset of the app in IMEM + * @app_imem_entry: entry point of the app in IMEM + * @app_dmem_offset: offset of the data in DMEM + * @app_resident_code_offset: offset of app code from app_start_offset + * @app_resident_code_size: size of the code + * @app_resident_data_offset: offset of data from app_start_offset + * @app_resident_data_size: size of data + * + * A firmware image contains the code, data, and bootloader of a given LS + * falcon in a single blob. This structure describes where everything is. + * + * This can be generated from a (bootloader, code, data) set if they have + * been loaded separately, or come directly from a file. + */ +struct ls_ucode_img_desc { + u32 descriptor_size; + u32 image_size; + u32 tools_version; + u32 app_version; + char date[64]; + u32 bootloader_start_offset; + u32 bootloader_size; + u32 bootloader_imem_offset; + u32 bootloader_entry_point; + u32 app_start_offset; + u32 app_size; + u32 app_imem_offset; + u32 app_imem_entry; + u32 app_dmem_offset; + u32 app_resident_code_offset; + u32 app_resident_code_size; + u32 app_resident_data_offset; + u32 app_resident_data_size; + u32 nb_overlays; + struct {u32 start; u32 size; } load_ovl[64]; + u32 compressed; +}; + +/** + * struct ls_ucode_img - temporary storage for loaded LS firmwares + * @node: to link within lsf_ucode_mgr + * @falcon_id: ID of the falcon this LS firmware is for + * @ucode_desc: loaded or generated map of ucode_data + * @ucode_data: firmware payload (code and data) + * @ucode_size: size in bytes of data in ucode_data + * @sig: signature for this firmware + * @sig:size: size of the signature in bytes + * + * Preparing the WPR LS blob requires information about all the LS firmwares + * (size, etc) to be known. This structure contains all the data of one LS + * firmware. + */ +struct ls_ucode_img { + struct list_head node; + enum nvkm_secboot_falcon falcon_id; + + struct ls_ucode_img_desc ucode_desc; + u8 *ucode_data; + u32 ucode_size; + + u8 *sig; + u32 sig_size; +}; + +/** + * struct fw_bin_header - header of firmware files + * @bin_magic: always 0x3b1d14f0 + * @bin_ver: version of the bin format + * @bin_size: entire image size including this header + * @header_offset: offset of the firmware/bootloader header in the file + * @data_offset: offset of the firmware/bootloader payload in the file + * @data_size: size of the payload + * + * This header is located at the beginning of the HS firmware and HS bootloader + * files, to describe where the headers and data can be found. + */ +struct fw_bin_header { + u32 bin_magic; + u32 bin_ver; + u32 bin_size; + u32 header_offset; + u32 data_offset; + u32 data_size; +}; + +/** + * struct fw_bl_desc - firmware bootloader descriptor + * @start_tag: starting tag of bootloader + * @desc_dmem_load_off: DMEM offset of flcn_bl_dmem_desc + * @code_off: offset of code section + * @code_size: size of code section + * @data_off: offset of data section + * @data_size: size of data section + * + * This structure is embedded in bootloader firmware files at to describe the + * IMEM and DMEM layout expected by the bootloader. + */ +struct fw_bl_desc { + u32 start_tag; + u32 dmem_load_off; + u32 code_off; + u32 code_size; + u32 data_off; + u32 data_size; +}; + +int acr_ls_ucode_load_fecs(const struct nvkm_subdev *, struct ls_ucode_img *); +int acr_ls_ucode_load_gpccs(const struct nvkm_subdev *, struct ls_ucode_img *); + + +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c new file mode 100644 index 000000000000..40a6df77bb8a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +#include "ls_ucode.h" +#include "acr.h" + +#include <core/firmware.h> + +#define BL_DESC_BLK_SIZE 256 +/** + * Build a ucode image and descriptor from provided bootloader, code and data. + * + * @bl: bootloader image, including 16-bytes descriptor + * @code: LS firmware code segment + * @data: LS firmware data segment + * @desc: ucode descriptor to be written + * + * Return: allocated ucode image with corresponding descriptor information. desc + * is also updated to contain the right offsets within returned image. + */ +static void * +ls_ucode_img_build(const struct firmware *bl, const struct firmware *code, + const struct firmware *data, struct ls_ucode_img_desc *desc) +{ + struct fw_bin_header *bin_hdr = (void *)bl->data; + struct fw_bl_desc *bl_desc = (void *)bl->data + bin_hdr->header_offset; + void *bl_data = (void *)bl->data + bin_hdr->data_offset; + u32 pos = 0; + void *image; + + desc->bootloader_start_offset = pos; + desc->bootloader_size = ALIGN(bl_desc->code_size, sizeof(u32)); + desc->bootloader_imem_offset = bl_desc->start_tag * 256; + desc->bootloader_entry_point = bl_desc->start_tag * 256; + + pos = ALIGN(pos + desc->bootloader_size, BL_DESC_BLK_SIZE); + desc->app_start_offset = pos; + desc->app_size = ALIGN(code->size, BL_DESC_BLK_SIZE) + + ALIGN(data->size, BL_DESC_BLK_SIZE); + desc->app_imem_offset = 0; + desc->app_imem_entry = 0; + desc->app_dmem_offset = 0; + desc->app_resident_code_offset = 0; + desc->app_resident_code_size = ALIGN(code->size, BL_DESC_BLK_SIZE); + + pos = ALIGN(pos + desc->app_resident_code_size, BL_DESC_BLK_SIZE); + desc->app_resident_data_offset = pos - desc->app_start_offset; + desc->app_resident_data_size = ALIGN(data->size, BL_DESC_BLK_SIZE); + + desc->image_size = ALIGN(bl_desc->code_size, BL_DESC_BLK_SIZE) + + desc->app_size; + + image = kzalloc(desc->image_size, GFP_KERNEL); + if (!image) + return ERR_PTR(-ENOMEM); + + memcpy(image + desc->bootloader_start_offset, bl_data, + bl_desc->code_size); + memcpy(image + desc->app_start_offset, code->data, code->size); + memcpy(image + desc->app_start_offset + desc->app_resident_data_offset, + data->data, data->size); + + return image; +} + +/** + * ls_ucode_img_load_gr() - load and prepare a LS GR ucode image + * + * Load the LS microcode, bootloader and signature and pack them into a single + * blob. Also generate the corresponding ucode descriptor. + */ +static int +ls_ucode_img_load_gr(const struct nvkm_subdev *subdev, struct ls_ucode_img *img, + const char *falcon_name) +{ + const struct firmware *bl, *code, *data, *sig; + char f[64]; + int ret; + + snprintf(f, sizeof(f), "gr/%s_bl", falcon_name); + ret = nvkm_firmware_get(subdev->device, f, &bl); + if (ret) + goto error; + + snprintf(f, sizeof(f), "gr/%s_inst", falcon_name); + ret = nvkm_firmware_get(subdev->device, f, &code); + if (ret) + goto free_bl; + + snprintf(f, sizeof(f), "gr/%s_data", falcon_name); + ret = nvkm_firmware_get(subdev->device, f, &data); + if (ret) + goto free_inst; + + snprintf(f, sizeof(f), "gr/%s_sig", falcon_name); + ret = nvkm_firmware_get(subdev->device, f, &sig); + if (ret) + goto free_data; + img->sig = kmemdup(sig->data, sig->size, GFP_KERNEL); + if (!img->sig) { + ret = -ENOMEM; + goto free_sig; + } + img->sig_size = sig->size; + + img->ucode_data = ls_ucode_img_build(bl, code, data, + &img->ucode_desc); + if (IS_ERR(img->ucode_data)) { + ret = PTR_ERR(img->ucode_data); + goto free_data; + } + img->ucode_size = img->ucode_desc.image_size; + +free_sig: + nvkm_firmware_put(sig); +free_data: + nvkm_firmware_put(data); +free_inst: + nvkm_firmware_put(code); +free_bl: + nvkm_firmware_put(bl); +error: + return ret; +} + +int +acr_ls_ucode_load_fecs(const struct nvkm_subdev *subdev, + struct ls_ucode_img *img) +{ + return ls_ucode_img_load_gr(subdev, img, "fecs"); +} + +int +acr_ls_ucode_load_gpccs(const struct nvkm_subdev *subdev, + struct ls_ucode_img *img) +{ + return ls_ucode_img_load_gr(subdev, img, "gpccs"); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h index a9a8a0e1017e..936a65f5658c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h @@ -27,20 +27,16 @@ #include <subdev/mmu.h> struct nvkm_secboot_func { - int (*init)(struct nvkm_secboot *); + int (*oneinit)(struct nvkm_secboot *); int (*fini)(struct nvkm_secboot *, bool suspend); void *(*dtor)(struct nvkm_secboot *); - int (*reset)(struct nvkm_secboot *, enum nvkm_secboot_falcon); - int (*start)(struct nvkm_secboot *, enum nvkm_secboot_falcon); - - /* ID of the falcon that will perform secure boot */ - enum nvkm_secboot_falcon boot_falcon; - /* Bit-mask of IDs of managed falcons */ - unsigned long managed_falcons; + int (*run_blob)(struct nvkm_secboot *, struct nvkm_gpuobj *); }; -int nvkm_secboot_ctor(const struct nvkm_secboot_func *, struct nvkm_device *, - int index, struct nvkm_secboot *); +extern const char *nvkm_secboot_falcon_name[]; + +int nvkm_secboot_ctor(const struct nvkm_secboot_func *, struct nvkm_acr *, + struct nvkm_device *, int, struct nvkm_secboot *); int nvkm_secboot_falcon_reset(struct nvkm_secboot *); int nvkm_secboot_falcon_run(struct nvkm_secboot *); @@ -48,187 +44,20 @@ struct flcn_u64 { u32 lo; u32 hi; }; + static inline u64 flcn64_to_u64(const struct flcn_u64 f) { return ((u64)f.hi) << 32 | f.lo; } -/** - * struct gm200_flcn_bl_desc - DMEM bootloader descriptor - * @signature: 16B signature for secure code. 0s if no secure code - * @ctx_dma: DMA context to be used by BL while loading code/data - * @code_dma_base: 256B-aligned Physical FB Address where code is located - * (falcon's $xcbase register) - * @non_sec_code_off: offset from code_dma_base where the non-secure code is - * located. The offset must be multiple of 256 to help perf - * @non_sec_code_size: the size of the nonSecure code part. - * @sec_code_off: offset from code_dma_base where the secure code is - * located. The offset must be multiple of 256 to help perf - * @sec_code_size: offset from code_dma_base where the secure code is - * located. The offset must be multiple of 256 to help perf - * @code_entry_point: code entry point which will be invoked by BL after - * code is loaded. - * @data_dma_base: 256B aligned Physical FB Address where data is located. - * (falcon's $xdbase register) - * @data_size: size of data block. Should be multiple of 256B - * - * Structure used by the bootloader to load the rest of the code. This has - * to be filled by host and copied into DMEM at offset provided in the - * hsflcn_bl_desc.bl_desc_dmem_load_off. - */ -struct gm200_flcn_bl_desc { - u32 reserved[4]; - u32 signature[4]; - u32 ctx_dma; - struct flcn_u64 code_dma_base; - u32 non_sec_code_off; - u32 non_sec_code_size; - u32 sec_code_off; - u32 sec_code_size; - u32 code_entry_point; - struct flcn_u64 data_dma_base; - u32 data_size; -}; - -/** - * struct hsflcn_acr_desc - data section of the HS firmware - * - * This header is to be copied at the beginning of DMEM by the HS bootloader. - * - * @signature: signature of ACR ucode - * @wpr_region_id: region ID holding the WPR header and its details - * @wpr_offset: offset from the WPR region holding the wpr header - * @regions: region descriptors - * @nonwpr_ucode_blob_size: size of LS blob - * @nonwpr_ucode_blob_start: FB location of LS blob is - */ -struct hsflcn_acr_desc { - union { - u8 reserved_dmem[0x200]; - u32 signatures[4]; - } ucode_reserved_space; - u32 wpr_region_id; - u32 wpr_offset; - u32 mmu_mem_range; -#define FLCN_ACR_MAX_REGIONS 2 - struct { - u32 no_regions; - struct { - u32 start_addr; - u32 end_addr; - u32 region_id; - u32 read_mask; - u32 write_mask; - u32 client_mask; - } region_props[FLCN_ACR_MAX_REGIONS]; - } regions; - u32 ucode_blob_size; - u64 ucode_blob_base __aligned(8); - struct { - u32 vpr_enabled; - u32 vpr_start; - u32 vpr_end; - u32 hdcp_policies; - } vpr_desc; -}; - -/** - * Contains the whole secure boot state, allowing it to be performed as needed - * @wpr_addr: physical address of the WPR region - * @wpr_size: size in bytes of the WPR region - * @ls_blob: LS blob of all the LS firmwares, signatures, bootloaders - * @ls_blob_size: size of the LS blob - * @ls_blob_nb_regions: number of LS firmwares that will be loaded - * @acr_blob: HS blob - * @acr_blob_vma: mapping of the HS blob into the secure falcon's VM - * @acr_bl_desc: bootloader descriptor of the HS blob - * @hsbl_blob: HS blob bootloader - * @inst: instance block for HS falcon - * @pgd: page directory for the HS falcon - * @vm: address space used by the HS falcon - * @falcon_state: current state of the managed falcons - * @firmware_ok: whether the firmware blobs have been created - */ -struct gm200_secboot { - struct nvkm_secboot base; - const struct gm200_secboot_func *func; - - /* - * Address and size of the WPR region. On dGPU this will be the - * address of the LS blob. On Tegra this is a fixed region set by the - * bootloader - */ - u64 wpr_addr; - u32 wpr_size; - - /* - * HS FW - lock WPR region (dGPU only) and load LS FWs - * on Tegra the HS FW copies the LS blob into the fixed WPR instead - */ - struct nvkm_gpuobj *acr_load_blob; - struct gm200_flcn_bl_desc acr_load_bl_desc; - - /* HS FW - unlock WPR region (dGPU only) */ - struct nvkm_gpuobj *acr_unload_blob; - struct gm200_flcn_bl_desc acr_unload_bl_desc; - - /* HS bootloader */ - void *hsbl_blob; - - /* LS FWs, to be loaded by the HS ACR */ - struct nvkm_gpuobj *ls_blob; - - /* Instance block & address space used for HS FW execution */ - struct nvkm_gpuobj *inst; - struct nvkm_gpuobj *pgd; - struct nvkm_vm *vm; - - /* To keep track of the state of all managed falcons */ - enum { - /* In non-secure state, no firmware loaded, no privileges*/ - NON_SECURE = 0, - /* In low-secure mode and ready to be started */ - RESET, - /* In low-secure mode and running */ - RUNNING, - } falcon_state[NVKM_SECBOOT_FALCON_END]; - - bool firmware_ok; -}; -#define gm200_secboot(sb) container_of(sb, struct gm200_secboot, base) - -/** - * Contains functions we wish to abstract between GM200-like implementations - * @bl_desc_size: size of the BL descriptor used by this chip. - * @fixup_bl_desc: hook that generates the proper BL descriptor format from - * the generic GM200 format into a data array of size - * bl_desc_size - * @fixup_hs_desc: hook that twiddles the HS descriptor before it is used - * @prepare_blobs: prepares the various blobs needed for secure booting - */ -struct gm200_secboot_func { - /* - * Size of the bootloader descriptor for this chip. A block of this - * size is allocated before booting a falcon and the fixup_bl_desc - * callback is called on it - */ - u32 bl_desc_size; - void (*fixup_bl_desc)(const struct gm200_flcn_bl_desc *, void *); - - /* - * Chip-specific modifications of the HS descriptor can be done here. - * On dGPU this is used to fill the information about the WPR region - * we want the HS FW to set up. - */ - void (*fixup_hs_desc)(struct gm200_secboot *, struct hsflcn_acr_desc *); - int (*prepare_blobs)(struct gm200_secboot *); -}; +static inline struct flcn_u64 u64_to_flcn64(u64 u) +{ + struct flcn_u64 ret; -int gm200_secboot_init(struct nvkm_secboot *); -void *gm200_secboot_dtor(struct nvkm_secboot *); -int gm200_secboot_reset(struct nvkm_secboot *, u32); -int gm200_secboot_start(struct nvkm_secboot *, u32); + ret.hi = upper_32_bits(u); + ret.lo = lower_32_bits(u); -int gm20x_secboot_prepare_blobs(struct gm200_secboot *); + return ret; +} #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c index 8894fee30cbc..df949fa7d05d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c @@ -64,10 +64,9 @@ nvkm_therm_update_trip(struct nvkm_therm *therm) } static int -nvkm_therm_update_linear(struct nvkm_therm *therm) +nvkm_therm_compute_linear_duty(struct nvkm_therm *therm, u8 linear_min_temp, + u8 linear_max_temp) { - u8 linear_min_temp = therm->fan->bios.linear_min_temp; - u8 linear_max_temp = therm->fan->bios.linear_max_temp; u8 temp = therm->func->temp_get(therm); u16 duty; @@ -85,6 +84,21 @@ nvkm_therm_update_linear(struct nvkm_therm *therm) return duty; } +static int +nvkm_therm_update_linear(struct nvkm_therm *therm) +{ + u8 min = therm->fan->bios.linear_min_temp; + u8 max = therm->fan->bios.linear_max_temp; + return nvkm_therm_compute_linear_duty(therm, min, max); +} + +static int +nvkm_therm_update_linear_fallback(struct nvkm_therm *therm) +{ + u8 max = therm->bios_sensor.thrs_fan_boost.temp; + return nvkm_therm_compute_linear_duty(therm, 30, max); +} + static void nvkm_therm_update(struct nvkm_therm *therm, int mode) { @@ -119,6 +133,8 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) case NVBIOS_THERM_FAN_OTHER: if (therm->cstate) duty = therm->cstate; + else + duty = nvkm_therm_update_linear_fallback(therm); poll = false; break; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c index fe063d5728e2..67ada1d9a28c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/top/base.c @@ -95,6 +95,20 @@ nvkm_top_intr(struct nvkm_device *device, u32 intr, u64 *psubdevs) return intr & ~handled; } +int +nvkm_top_fault_id(struct nvkm_device *device, enum nvkm_devidx devidx) +{ + struct nvkm_top *top = device->top; + struct nvkm_top_device *info; + + list_for_each_entry(info, &top->device, head) { + if (info->index == devidx && info->fault >= 0) + return info->fault; + } + + return -ENOENT; +} + enum nvkm_devidx nvkm_top_fault(struct nvkm_device *device, int fault) { diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c index dc026a843712..ac5800c72cb4 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c @@ -18,7 +18,7 @@ #include <linux/jiffies.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/of_device.h> @@ -1253,7 +1253,7 @@ static int dsicm_probe(struct platform_device *pdev) dsicm_hw_reset(ddata); if (ddata->use_dsi_backlight) { - memset(&props, 0, sizeof(struct backlight_properties)); + memset(&props, 0, sizeof(props)); props.max_brightness = 255; props.type = BACKLIGHT_RAW; diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c index 746cb8d9cba1..5ab39e0060f2 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c @@ -909,6 +909,7 @@ static struct spi_driver acx565akm_driver = { module_spi_driver(acx565akm_driver); +MODULE_ALIAS("spi:sony,acx565akm"); MODULE_AUTHOR("Nokia Corporation"); MODULE_DESCRIPTION("acx565akm LCD Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index c839f6456db2..d956e6266368 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -620,6 +620,19 @@ u32 dispc_wb_get_framedone_irq(void) return DISPC_IRQ_FRAMEDONEWB; } +void dispc_mgr_enable(enum omap_channel channel, bool enable) +{ + mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable); + /* flush posted write */ + mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); +} +EXPORT_SYMBOL(dispc_mgr_enable); + +static bool dispc_mgr_is_enabled(enum omap_channel channel) +{ + return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); +} + bool dispc_mgr_go_busy(enum omap_channel channel) { return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1; @@ -2493,6 +2506,25 @@ static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, return -EINVAL; } + if (*decim_x > 4 && color_mode != OMAP_DSS_COLOR_NV12) { + /* + * Let's disable all scaling that requires horizontal + * decimation with higher factor than 4, until we have + * better estimates of what we can and can not + * do. However, NV12 color format appears to work Ok + * with all decimation factors. + * + * When decimating horizontally by more that 4 the dss + * is not able to fetch the data in burst mode. When + * this happens it is hard to tell if there enough + * bandwidth. Despite what theory says this appears to + * be true also for 16-bit color formats. + */ + DSSERR("Not enough bandwidth, too much downscaling (x-decimation factor %d > 4)", *decim_x); + + return -EINVAL; + } + *core_clk = dispc.feat->calc_core_clk(pclk, in_width, in_height, out_width, out_height, mem_to_mem); return 0; @@ -2901,20 +2933,6 @@ enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channe } EXPORT_SYMBOL(dispc_mgr_get_supported_outputs); -void dispc_mgr_enable(enum omap_channel channel, bool enable) -{ - mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable); - /* flush posted write */ - mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); -} -EXPORT_SYMBOL(dispc_mgr_enable); - -bool dispc_mgr_is_enabled(enum omap_channel channel) -{ - return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); -} -EXPORT_SYMBOL(dispc_mgr_is_enabled); - void dispc_wb_enable(bool enable) { dispc_ovl_enable(OMAP_DSS_WB, enable); diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index f060bda31235..f74615d005a8 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -4336,7 +4336,7 @@ static void print_dsi_vm(const char *str, wc = DIV_ROUND_UP(t->hact * t->bitspp, 8); pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */ - bl = t->hss + t->hsa + t->hse + t->hbp + t->hfront_porch; + bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp; tot = bl + pps; #define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk)) @@ -4345,14 +4345,14 @@ static void print_dsi_vm(const char *str, "%u/%u/%u/%u/%u/%u = %u + %u = %u\n", str, byteclk, - t->hss, t->hsa, t->hse, t->hbp, pps, t->hfront_porch, + t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp, bl, pps, tot, TO_DSI_T(t->hss), TO_DSI_T(t->hsa), TO_DSI_T(t->hse), TO_DSI_T(t->hbp), TO_DSI_T(pps), - TO_DSI_T(t->hfront_porch), + TO_DSI_T(t->hfp), TO_DSI_T(bl), TO_DSI_T(pps), @@ -4367,7 +4367,7 @@ static void print_dispc_vm(const char *str, const struct videomode *vm) int hact, bl, tot; hact = vm->hactive; - bl = vm->hsync_len + vm->hbp + vm->hfront_porch; + bl = vm->hsync_len + vm->hback_porch + vm->hfront_porch; tot = hact + bl; #define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck)) @@ -4376,10 +4376,10 @@ static void print_dispc_vm(const char *str, const struct videomode *vm) "%u/%u/%u/%u = %u + %u = %u\n", str, pck, - vm->hsync_len, vm->hbp, hact, vm->hfront_porch, + vm->hsync_len, vm->hback_porch, hact, vm->hfront_porch, bl, hact, tot, TO_DISPC_T(vm->hsync_len), - TO_DISPC_T(vm->hbp), + TO_DISPC_T(vm->hback_porch), TO_DISPC_T(hact), TO_DISPC_T(vm->hfront_porch), TO_DISPC_T(bl), @@ -4401,12 +4401,12 @@ static void print_dsi_dispc_vm(const char *str, dsi_tput = (u64)byteclk * t->ndl * 8; pck = (u32)div64_u64(dsi_tput, t->bitspp); dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl); - dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfront_porch; + dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp; vm.pixelclock = pck; vm.hsync_len = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk); - vm.hbp = div64_u64((u64)t->hbp * pck, byteclk); - vm.hfront_porch = div64_u64((u64)t->hfront_porch * pck, byteclk); + vm.hback_porch = div64_u64((u64)t->hbp * pck, byteclk); + vm.hfront_porch = div64_u64((u64)t->hfp * pck, byteclk); vm.hactive = t->hact; print_dispc_vm(str, &vm); diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c index 136d30484d02..bf626acae271 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c +++ b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c @@ -119,8 +119,7 @@ static void __init omapdss_omapify_node(struct device_node *node) static void __init omapdss_add_to_list(struct device_node *node, bool root) { - struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node), - GFP_KERNEL); + struct dss_conv_node *n = kmalloc(sizeof(*n), GFP_KERNEL); if (n) { n->node = node; n->root = root; diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index b420dde8c0fb..5b3b961127bd 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -856,7 +856,6 @@ int dispc_runtime_get(void); void dispc_runtime_put(void); void dispc_mgr_enable(enum omap_channel channel, bool enable); -bool dispc_mgr_is_enabled(enum omap_channel channel); u32 dispc_mgr_get_vsync_irq(enum omap_channel channel); u32 dispc_mgr_get_framedone_irq(enum omap_channel channel); u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel); diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 2580e8673908..f90e2d22c5ec 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -162,7 +162,7 @@ static int omap_connector_mode_valid(struct drm_connector *connector, dssdrv->get_timings(dssdev, &t); - if (memcmp(&vm, &t, sizeof(struct videomode))) + if (memcmp(&vm, &t, sizeof(vm))) r = -EINVAL; else r = 0; @@ -217,7 +217,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, omap_dss_get_device(dssdev); - omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL); + omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL); if (!omap_connector) goto fail; @@ -240,8 +240,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, connector->interlace_allowed = 1; connector->doublescan_allowed = 0; - drm_connector_register(connector); - return connector; fail: diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 8dea89030e66..b68c70eb395f 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -36,26 +36,18 @@ struct omap_crtc { struct videomode vm; - struct omap_drm_irq vblank_irq; - struct omap_drm_irq error_irq; - bool ignore_digit_sync_lost; + bool enabled; bool pending; wait_queue_head_t pending_wait; + struct drm_pending_vblank_event *event; }; /* ----------------------------------------------------------------------------- * Helper Functions */ -uint32_t pipe2vbl(struct drm_crtc *crtc) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - - return dispc_mgr_get_vsync_irq(omap_crtc->channel); -} - struct videomode *omap_crtc_timings(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -68,6 +60,19 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) return omap_crtc->channel; } +static bool omap_crtc_is_pending(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + unsigned long flags; + bool pending; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + pending = omap_crtc->pending; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + return pending; +} + int omap_crtc_wait_pending(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -77,7 +82,7 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc) * a single frame refresh even on slower displays. */ return wait_event_timeout(omap_crtc->pending_wait, - !omap_crtc->pending, + !omap_crtc_is_pending(crtc), msecs_to_jiffies(250)); } @@ -135,14 +140,15 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) u32 framedone_irq, vsync_irq; int ret; + if (WARN_ON(omap_crtc->enabled == enable)) + return; + if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) { dispc_mgr_enable(channel, enable); + omap_crtc->enabled = enable; return; } - if (dispc_mgr_is_enabled(channel) == enable) - return; - if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { /* * Digit output produces some sync lost interrupts during the @@ -173,6 +179,7 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) } dispc_mgr_enable(channel, enable); + omap_crtc->enabled = enable; ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); if (ret) { @@ -259,26 +266,9 @@ static const struct dss_mgr_ops mgr_ops = { * Setup, Flush and Page Flip */ -static void omap_crtc_complete_page_flip(struct drm_crtc *crtc) -{ - struct drm_pending_vblank_event *event; - struct drm_device *dev = crtc->dev; - unsigned long flags; - - event = crtc->state->event; - - if (!event) - return; - - spin_lock_irqsave(&dev->event_lock, flags); - drm_crtc_send_vblank_event(crtc, event); - spin_unlock_irqrestore(&dev->event_lock, flags); -} - -static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus) { - struct omap_crtc *omap_crtc = - container_of(irq, struct omap_crtc, error_irq); + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); if (omap_crtc->ignore_digit_sync_lost) { irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; @@ -289,29 +279,38 @@ static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); } -static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +void omap_crtc_vblank_irq(struct drm_crtc *crtc) { - struct omap_crtc *omap_crtc = - container_of(irq, struct omap_crtc, vblank_irq); - struct drm_device *dev = omap_crtc->base.dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + bool pending; - if (dispc_mgr_go_busy(omap_crtc->channel)) + spin_lock(&crtc->dev->event_lock); + /* + * If the dispc is busy we're racing the flush operation. Try again on + * the next vblank interrupt. + */ + if (dispc_mgr_go_busy(omap_crtc->channel)) { + spin_unlock(&crtc->dev->event_lock); return; + } - DBG("%s: apply done", omap_crtc->name); - - __omap_irq_unregister(dev, &omap_crtc->vblank_irq); + /* Send the vblank event if one has been requested. */ + if (omap_crtc->event) { + drm_crtc_send_vblank_event(crtc, omap_crtc->event); + omap_crtc->event = NULL; + } - rmb(); - WARN_ON(!omap_crtc->pending); + pending = omap_crtc->pending; omap_crtc->pending = false; - wmb(); + spin_unlock(&crtc->dev->event_lock); - /* wake up userspace */ - omap_crtc_complete_page_flip(&omap_crtc->base); + if (pending) + drm_crtc_vblank_put(crtc); - /* wake up omap_atomic_complete */ + /* Wake up omap_atomic_complete. */ wake_up(&omap_crtc->pending_wait); + + DBG("%s: apply done", omap_crtc->name); } /* ----------------------------------------------------------------------------- @@ -324,9 +323,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) DBG("%s", omap_crtc->name); - WARN_ON(omap_crtc->vblank_irq.registered); - omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); - drm_crtc_cleanup(crtc); kfree(omap_crtc); @@ -335,17 +331,18 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) static void omap_crtc_enable(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + int ret; DBG("%s", omap_crtc->name); - rmb(); + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_vblank_on(crtc); + ret = drm_crtc_vblank_get(crtc); + WARN_ON(ret != 0); + WARN_ON(omap_crtc->pending); omap_crtc->pending = true; - wmb(); - - omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); - - drm_crtc_vblank_on(crtc); + spin_unlock_irq(&crtc->dev->event_lock); } static void omap_crtc_disable(struct drm_crtc *crtc) @@ -390,16 +387,15 @@ static int omap_crtc_atomic_check(struct drm_crtc *crtc, } static void omap_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) + struct drm_crtc_state *old_crtc_state) { } static void omap_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) + struct drm_crtc_state *old_crtc_state) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - - WARN_ON(omap_crtc->vblank_irq.registered); + int ret; if (crtc->state->color_mgmt_changed) { struct drm_color_lut *lut = NULL; @@ -414,18 +410,24 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, dispc_mgr_set_gamma(omap_crtc->channel, lut, length); } - if (dispc_mgr_is_enabled(omap_crtc->channel)) { + /* Only flush the CRTC if it is currently enabled. */ + if (!omap_crtc->enabled) + return; + + DBG("%s: GO", omap_crtc->name); - DBG("%s: GO", omap_crtc->name); + ret = drm_crtc_vblank_get(crtc); + WARN_ON(ret != 0); - rmb(); - WARN_ON(omap_crtc->pending); - omap_crtc->pending = true; - wmb(); + spin_lock_irq(&crtc->dev->event_lock); + dispc_mgr_go(omap_crtc->channel); - dispc_mgr_go(omap_crtc->channel); - omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); - } + WARN_ON(omap_crtc->pending); + omap_crtc->pending = true; + + if (crtc->state->event) + omap_crtc->event = crtc->state->event; + spin_unlock_irq(&crtc->dev->event_lock); } static bool omap_crtc_is_plane_prop(struct drm_crtc *crtc, @@ -546,14 +548,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, omap_crtc->channel = channel; omap_crtc->name = channel_names[channel]; - omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc); - omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq; - - omap_crtc->error_irq.irqmask = - dispc_mgr_get_sync_lost_irq(channel); - omap_crtc->error_irq.irq = omap_crtc_error_irq; - omap_irq_register(dev, &omap_crtc->error_irq); - ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &omap_crtc_funcs, NULL); if (ret < 0) { diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c index 479bf24050f8..19b716745623 100644 --- a/drivers/gpu/drm/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -50,7 +50,11 @@ static int mm_show(struct seq_file *m, void *arg) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; - return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm); + struct drm_printer p = drm_seq_file_printer(m); + + drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p); + + return 0; } #ifdef CONFIG_DRM_FBDEV_EMULATION @@ -119,13 +123,4 @@ int omap_debugfs_init(struct drm_minor *minor) return ret; } -void omap_debugfs_cleanup(struct drm_minor *minor) -{ - drm_debugfs_remove_files(omap_debugfs_list, - ARRAY_SIZE(omap_debugfs_list), minor); - if (dmm_is_available()) - drm_debugfs_remove_files(omap_dmm_debugfs_list, - ARRAY_SIZE(omap_dmm_debugfs_list), minor); -} - #endif diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index 4ceed7a9762f..3cab06661a08 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -224,7 +224,7 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, int rows = (1 + area->y1 - area->y0); int i = columns*rows; - pat = alloc_dma(txn, sizeof(struct pat), &pat_pa); + pat = alloc_dma(txn, sizeof(*pat), &pat_pa); if (txn->last_pat) txn->last_pat->next_pa = (uint32_t)pat_pa; @@ -735,7 +735,7 @@ static int omap_dmm_probe(struct platform_device *dev) /* alloc engines */ omap_dmm->engines = kcalloc(omap_dmm->num_engines, - sizeof(struct refill_engine), GFP_KERNEL); + sizeof(*omap_dmm->engines), GFP_KERNEL); if (!omap_dmm->engines) { ret = -ENOMEM; goto fail; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index fdc83cbcde61..3f2554235225 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -96,8 +96,22 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit) dispc_runtime_get(); drm_atomic_helper_commit_modeset_disables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, 0); + + /* With the current dss dispc implementation we have to enable + * the new modeset before we can commit planes. The dispc ovl + * configuration relies on the video mode configuration been + * written into the HW when the ovl configuration is + * calculated. + * + * This approach is not ideal because after a mode change the + * plane update is executed only after the first vblank + * interrupt. The dispc implementation should be fixed so that + * it is able use uncommitted drm state information. + */ drm_atomic_helper_commit_modeset_enables(dev, old_state); + omap_atomic_wait_for_completion(dev, old_state); + + drm_atomic_helper_commit_planes(dev, old_state, 0); omap_atomic_wait_for_completion(dev, old_state); @@ -315,8 +329,6 @@ static int omap_modeset_init(struct drm_device *dev) drm_mode_config_init(dev); - omap_drm_irq_install(dev); - ret = omap_modeset_init_properties(dev); if (ret < 0) return ret; @@ -489,12 +501,9 @@ static int omap_modeset_init(struct drm_device *dev) drm_mode_config_reset(dev); - return 0; -} + omap_drm_irq_install(dev); -static void omap_modeset_free(struct drm_device *dev) -{ - drm_mode_config_cleanup(dev); + return 0; } /* @@ -632,95 +641,6 @@ static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = * drm driver funcs */ -/** - * load - setup chip and create an initial config - * @dev: DRM device - * @flags: startup flags - * - * The driver load routine has to do several things: - * - initialize the memory manager - * - allocate initial config memory - * - setup the DRM framebuffer with the allocated memory - */ -static int dev_load(struct drm_device *dev, unsigned long flags) -{ - struct omap_drm_platform_data *pdata = dev->dev->platform_data; - struct omap_drm_private *priv; - unsigned int i; - int ret; - - DBG("load: dev=%p", dev); - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->omaprev = pdata->omaprev; - - dev->dev_private = priv; - - priv->wq = alloc_ordered_workqueue("omapdrm", 0); - init_waitqueue_head(&priv->commit.wait); - spin_lock_init(&priv->commit.lock); - - spin_lock_init(&priv->list_lock); - INIT_LIST_HEAD(&priv->obj_list); - - omap_gem_init(dev); - - ret = omap_modeset_init(dev); - if (ret) { - dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); - dev->dev_private = NULL; - kfree(priv); - return ret; - } - - /* Initialize vblank handling, start with all CRTCs disabled. */ - ret = drm_vblank_init(dev, priv->num_crtcs); - if (ret) - dev_warn(dev->dev, "could not init vblank\n"); - - for (i = 0; i < priv->num_crtcs; i++) - drm_crtc_vblank_off(priv->crtcs[i]); - - priv->fbdev = omap_fbdev_init(dev); - - /* store off drm_device for use in pm ops */ - dev_set_drvdata(dev->dev, dev); - - drm_kms_helper_poll_init(dev); - - return 0; -} - -static int dev_unload(struct drm_device *dev) -{ - struct omap_drm_private *priv = dev->dev_private; - - DBG("unload: dev=%p", dev); - - drm_kms_helper_poll_fini(dev); - - if (priv->fbdev) - omap_fbdev_free(dev); - - omap_modeset_free(dev); - omap_gem_deinit(dev); - - destroy_workqueue(priv->wq); - - drm_vblank_cleanup(dev); - omap_drm_irq_uninstall(dev); - - kfree(dev->dev_private); - dev->dev_private = NULL; - - dev_set_drvdata(dev->dev, NULL); - - return 0; -} - static int dev_open(struct drm_device *dev, struct drm_file *file) { file->driver_priv = NULL; @@ -805,8 +725,6 @@ static const struct file_operations omapdriver_fops = { static struct drm_driver omap_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .load = dev_load, - .unload = dev_unload, .open = dev_open, .lastclose = dev_lastclose, .get_vblank_counter = drm_vblank_no_hw_counter, @@ -814,7 +732,6 @@ static struct drm_driver omap_drm_driver = { .disable_vblank = omap_irq_disable_vblank, #ifdef CONFIG_DEBUG_FS .debugfs_init = omap_debugfs_init, - .debugfs_cleanup = omap_debugfs_cleanup, #endif .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, @@ -836,30 +753,125 @@ static struct drm_driver omap_drm_driver = { .patchlevel = DRIVER_PATCHLEVEL, }; -static int pdev_probe(struct platform_device *device) +static int pdev_probe(struct platform_device *pdev) { - int r; + struct omap_drm_platform_data *pdata = pdev->dev.platform_data; + struct omap_drm_private *priv; + struct drm_device *ddev; + unsigned int i; + int ret; + + DBG("%s", pdev->name); if (omapdss_is_initialized() == false) return -EPROBE_DEFER; omap_crtc_pre_init(); - r = omap_connect_dssdevs(); - if (r) { - omap_crtc_pre_uninit(); - return r; + ret = omap_connect_dssdevs(); + if (ret) + goto err_crtc_uninit; + + /* Allocate and initialize the driver private structure. */ + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err_disconnect_dssdevs; } - DBG("%s", device->name); - return drm_platform_init(&omap_drm_driver, device); + priv->omaprev = pdata->omaprev; + priv->wq = alloc_ordered_workqueue("omapdrm", 0); + + init_waitqueue_head(&priv->commit.wait); + spin_lock_init(&priv->commit.lock); + spin_lock_init(&priv->list_lock); + INIT_LIST_HEAD(&priv->obj_list); + + /* Allocate and initialize the DRM device. */ + ddev = drm_dev_alloc(&omap_drm_driver, &pdev->dev); + if (IS_ERR(ddev)) { + ret = PTR_ERR(ddev); + goto err_free_priv; + } + + ddev->dev_private = priv; + platform_set_drvdata(pdev, ddev); + + omap_gem_init(ddev); + + ret = omap_modeset_init(ddev); + if (ret) { + dev_err(&pdev->dev, "omap_modeset_init failed: ret=%d\n", ret); + goto err_free_drm_dev; + } + + /* Initialize vblank handling, start with all CRTCs disabled. */ + ret = drm_vblank_init(ddev, priv->num_crtcs); + if (ret) { + dev_err(&pdev->dev, "could not init vblank\n"); + goto err_cleanup_modeset; + } + + for (i = 0; i < priv->num_crtcs; i++) + drm_crtc_vblank_off(priv->crtcs[i]); + + priv->fbdev = omap_fbdev_init(ddev); + + drm_kms_helper_poll_init(ddev); + + /* + * Register the DRM device with the core and the connectors with + * sysfs. + */ + ret = drm_dev_register(ddev, 0); + if (ret) + goto err_cleanup_helpers; + + return 0; + +err_cleanup_helpers: + drm_kms_helper_poll_fini(ddev); + if (priv->fbdev) + omap_fbdev_free(ddev); +err_cleanup_modeset: + drm_mode_config_cleanup(ddev); + omap_drm_irq_uninstall(ddev); +err_free_drm_dev: + omap_gem_deinit(ddev); + drm_dev_unref(ddev); +err_free_priv: + destroy_workqueue(priv->wq); + kfree(priv); +err_disconnect_dssdevs: + omap_disconnect_dssdevs(); +err_crtc_uninit: + omap_crtc_pre_uninit(); + return ret; } -static int pdev_remove(struct platform_device *device) +static int pdev_remove(struct platform_device *pdev) { + struct drm_device *ddev = platform_get_drvdata(pdev); + struct omap_drm_private *priv = ddev->dev_private; + DBG(""); - drm_put_dev(platform_get_drvdata(device)); + drm_dev_unregister(ddev); + + drm_kms_helper_poll_fini(ddev); + + if (priv->fbdev) + omap_fbdev_free(ddev); + + drm_mode_config_cleanup(ddev); + + omap_drm_irq_uninstall(ddev); + omap_gem_deinit(ddev); + + drm_dev_unref(ddev); + + destroy_workqueue(priv->wq); + kfree(priv); omap_disconnect_dssdevs(); omap_crtc_pre_uninit(); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 7d9dd5400cef..65977982f15f 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -48,19 +48,6 @@ struct omap_drm_window { uint32_t src_w, src_h; }; -/* For transiently registering for different DSS irqs that various parts - * of the KMS code need during setup/configuration. We these are not - * necessarily the same as what drm_vblank_get/put() are requesting, and - * the hysteresis in drm_vblank_put() is not necessarily desirable for - * internal housekeeping related irq usage. - */ -struct omap_drm_irq { - struct list_head node; - uint32_t irqmask; - bool registered; - void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus); -}; - /* For KMS code that needs to wait for a certain # of IRQs: */ struct omap_irq_wait; @@ -101,9 +88,9 @@ struct omap_drm_private { struct drm_property *zorder_prop; /* irq handling: */ - struct list_head irq_list; /* list of omap_drm_irq */ - uint32_t vblank_mask; /* irq bits set for userspace vblank */ - struct omap_drm_irq error_handler; + spinlock_t wait_lock; /* protects the wait_list */ + struct list_head wait_list; /* list of omap_irq_wait */ + uint32_t irq_mask; /* enabled irqs in addition to wait_list */ /* atomic commit */ struct { @@ -116,7 +103,6 @@ struct omap_drm_private { #ifdef CONFIG_DEBUG_FS int omap_debugfs_init(struct drm_minor *minor); -void omap_debugfs_cleanup(struct drm_minor *minor); void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m); void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); @@ -128,10 +114,6 @@ int omap_gem_resume(struct device *dev); int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe); void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe); -void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); -void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); -void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); -void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); void omap_drm_irq_uninstall(struct drm_device *dev); int omap_drm_irq_install(struct drm_device *dev); @@ -155,6 +137,8 @@ void omap_crtc_pre_uninit(void); struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_plane *plane, enum omap_channel channel, int id); int omap_crtc_wait_pending(struct drm_crtc *crtc); +void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus); +void omap_crtc_vblank_irq(struct drm_crtc *crtc); struct drm_plane *omap_plane_init(struct drm_device *dev, int id, enum drm_plane_type type, @@ -204,7 +188,7 @@ int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma); int omap_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma); -int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +int omap_gem_fault(struct vm_fault *vmf); int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op); int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op); int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op); @@ -233,32 +217,6 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, struct dma_buf *buffer); /* map crtc to vblank mask */ -uint32_t pipe2vbl(struct drm_crtc *crtc); struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder); -/* should these be made into common util helpers? - */ - -static inline int objects_lookup( - struct drm_file *filp, uint32_t pixel_format, - struct drm_gem_object **bos, const uint32_t *handles) -{ - int i, n = drm_format_num_planes(pixel_format); - - for (i = 0; i < n; i++) { - bos[i] = drm_gem_object_lookup(filp, handles[i]); - if (!bos[i]) - goto fail; - - } - - return 0; - -fail: - while (--i > 0) - drm_gem_object_unreference_unlocked(bos[i]); - - return -ENOENT; -} - #endif /* __OMAP_DRV_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index a20f30039aee..86c977b7189a 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -117,7 +117,7 @@ static int omap_encoder_update(struct drm_encoder *encoder, dssdrv->get_timings(dssdev, &t); - if (memcmp(vm, &t, sizeof(struct videomode))) + if (memcmp(vm, &t, sizeof(*vm))) ret = -EINVAL; else ret = 0; diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 5f3337f1e9aa..29dc677dd4d3 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -29,37 +29,30 @@ * framebuffer funcs */ -/* per-format info: */ -struct format { +/* DSS to DRM formats mapping */ +static const struct { enum omap_color_mode dss_format; uint32_t pixel_format; - struct { - int stride_bpp; /* this times width is stride */ - int sub_y; /* sub-sample in y dimension */ - } planes[4]; - bool yuv; -}; - -static const struct format formats[] = { +} formats[] = { /* 16bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */ - { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */ - { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */ - { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */ - { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */ - { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */ - { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */ + { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565 }, /* RGB16-565 */ + { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444 }, /* RGB12x-4444 */ + { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444 }, /* xRGB12-4444 */ + { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444 }, /* RGBA12-4444 */ + { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444 }, /* ARGB16-4444 */ + { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555 }, /* xRGB15-1555 */ + { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555 }, /* ARGB16-1555 */ /* 24bpp RGB: */ - { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */ + { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888 }, /* RGB24-888 */ /* 32bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */ - { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */ - { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */ - { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */ + { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888 }, /* RGBx24-8888 */ + { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888 }, /* xRGB24-8888 */ + { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888 }, /* RGBA32-8888 */ + { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888 }, /* ARGB32-8888 */ /* YUV: */ - { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true }, - { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true }, - { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, + { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12 }, + { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV }, + { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY }, }; /* convert from overlay's pixel formats bitmask to an array of fourcc's */ @@ -89,8 +82,9 @@ struct plane { struct omap_framebuffer { struct drm_framebuffer base; int pin_count; - const struct format *format; - struct plane planes[4]; + const struct drm_format_info *format; + enum omap_color_mode dss_format; + struct plane planes[2]; /* lock for pinning (pin_count and planes.paddr) */ struct mutex lock; }; @@ -107,7 +101,7 @@ static int omap_framebuffer_create_handle(struct drm_framebuffer *fb, static void omap_framebuffer_destroy(struct drm_framebuffer *fb) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + int i, n = fb->format->num_planes; DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); @@ -128,13 +122,13 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { }; static uint32_t get_linear_addr(struct plane *plane, - const struct format *format, int n, int x, int y) + const struct drm_format_info *format, int n, int x, int y) { uint32_t offset; - offset = plane->offset + - (x * format->planes[n].stride_bpp) + - (y * plane->pitch / format->planes[n].sub_y); + offset = plane->offset + + (x * format->cpp[n] / (n == 0 ? 1 : format->hsub)) + + (y * plane->pitch / (n == 0 ? 1 : format->vsub)); return plane->paddr + offset; } @@ -153,11 +147,11 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, struct omap_drm_window *win, struct omap_overlay_info *info) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - const struct format *format = omap_fb->format; + const struct drm_format_info *format = omap_fb->format; struct plane *plane = &omap_fb->planes[0]; uint32_t x, y, orient = 0; - info->color_mode = format->dss_format; + info->color_mode = omap_fb->dss_format; info->pos_x = win->crtc_x; info->pos_y = win->crtc_y; @@ -231,9 +225,9 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, } /* convert to pixels: */ - info->screen_width /= format->planes[0].stride_bpp; + info->screen_width /= format->cpp[0]; - if (format->dss_format == OMAP_DSS_COLOR_NV12) { + if (omap_fb->dss_format == OMAP_DSS_COLOR_NV12) { plane = &omap_fb->planes[1]; if (info->rotation_type == OMAP_DSS_ROT_TILER) { @@ -252,7 +246,7 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int omap_framebuffer_pin(struct drm_framebuffer *fb) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int ret, i, n = drm_format_num_planes(fb->pixel_format); + int ret, i, n = fb->format->num_planes; mutex_lock(&omap_fb->lock); @@ -292,7 +286,7 @@ fail: void omap_framebuffer_unpin(struct drm_framebuffer *fb) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + int i, n = fb->format->num_planes; mutex_lock(&omap_fb->lock); @@ -343,10 +337,10 @@ struct drm_connector *omap_framebuffer_get_next_connector( void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + int i, n = fb->format->num_planes; seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, - (char *)&fb->pixel_format); + (char *)&fb->format->format); for (i = 0; i < n; i++) { struct plane *plane = &omap_fb->planes[i]; @@ -360,47 +354,58 @@ void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) { + unsigned int num_planes = drm_format_num_planes(mode_cmd->pixel_format); struct drm_gem_object *bos[4]; struct drm_framebuffer *fb; - int ret; + int i; - ret = objects_lookup(file, mode_cmd->pixel_format, - bos, mode_cmd->handles); - if (ret) - return ERR_PTR(ret); + for (i = 0; i < num_planes; i++) { + bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); + if (!bos[i]) { + fb = ERR_PTR(-ENOENT); + goto error; + } + } fb = omap_framebuffer_init(dev, mode_cmd, bos); - if (IS_ERR(fb)) { - int i, n = drm_format_num_planes(mode_cmd->pixel_format); - for (i = 0; i < n; i++) - drm_gem_object_unreference_unlocked(bos[i]); - return fb; - } + if (IS_ERR(fb)) + goto error; + + return fb; + +error: + while (--i > 0) + drm_gem_object_unreference_unlocked(bos[i]); + return fb; } struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) { + const struct drm_format_info *format = NULL; struct omap_framebuffer *omap_fb = NULL; struct drm_framebuffer *fb = NULL; - const struct format *format = NULL; - int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format); + enum omap_color_mode dss_format = 0; + unsigned int pitch = mode_cmd->pitches[0]; + int ret, i; DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, (char *)&mode_cmd->pixel_format); + format = drm_format_info(mode_cmd->pixel_format); + for (i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].pixel_format == mode_cmd->pixel_format) { - format = &formats[i]; + dss_format = formats[i].dss_format; break; } } - if (!format) { - dev_err(dev->dev, "unsupported pixel format: %4.4s\n", - (char *)&mode_cmd->pixel_format); + if (!format || !dss_format) { + dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n", + (char *)&mode_cmd->pixel_format); ret = -EINVAL; goto fail; } @@ -413,40 +418,39 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, fb = &omap_fb->base; omap_fb->format = format; + omap_fb->dss_format = dss_format; mutex_init(&omap_fb->lock); - for (i = 0; i < n; i++) { - struct plane *plane = &omap_fb->planes[i]; - int size, pitch = mode_cmd->pitches[i]; - - if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) { - dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n", - pitch, mode_cmd->width * format->planes[i].stride_bpp); - ret = -EINVAL; - goto fail; - } + /* + * The code below assumes that no format use more than two planes, and + * that the two planes of multiplane formats need the same number of + * bytes per pixel. + */ + if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) { + dev_dbg(dev->dev, "pitches differ between planes 0 and 1\n"); + ret = -EINVAL; + goto fail; + } - if (pitch % format->planes[i].stride_bpp != 0) { - dev_err(dev->dev, - "buffer pitch (%d bytes) is not a multiple of pixel size (%d bytes)\n", - pitch, format->planes[i].stride_bpp); - ret = -EINVAL; - goto fail; - } + if (pitch % format->cpp[0]) { + dev_dbg(dev->dev, + "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n", + pitch, format->cpp[0]); + ret = -EINVAL; + goto fail; + } - size = pitch * mode_cmd->height / format->planes[i].sub_y; + for (i = 0; i < format->num_planes; i++) { + struct plane *plane = &omap_fb->planes[i]; + unsigned int vsub = i == 0 ? 1 : format->vsub; + unsigned int size; - if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) { - dev_err(dev->dev, "provided buffer object is too small! %d < %d\n", - bos[i]->size - mode_cmd->offsets[i], size); - ret = -EINVAL; - goto fail; - } + size = pitch * mode_cmd->height / vsub; - if (i > 0 && pitch != mode_cmd->pitches[i - 1]) { - dev_err(dev->dev, - "pitches are not the same between framebuffer planes %d != %d\n", - pitch, mode_cmd->pitches[i - 1]); + if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) { + dev_dbg(dev->dev, + "provided buffer object is too small! %d < %d\n", + bos[i]->size - mode_cmd->offsets[i], size); ret = -EINVAL; goto fail; } @@ -457,7 +461,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, plane->paddr = 0; } - drm_helper_mode_fill_fb_struct(fb, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); if (ret) { diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 8d8ac173f55d..942c4d483008 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -190,7 +190,7 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, strcpy(fbi->fix.id, MODULE_NAME); - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); dev->mode_config.fb_base = paddr; @@ -225,10 +225,8 @@ fail: drm_fb_helper_release_fbi(helper); - if (fb) { - drm_framebuffer_unregister_private(fb); + if (fb) drm_framebuffer_remove(fb); - } } return ret; @@ -265,8 +263,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs); - ret = drm_fb_helper_init(dev, helper, - priv->num_crtcs, priv->num_connectors); + ret = drm_fb_helper_init(dev, helper, priv->num_connectors); if (ret) { dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret); goto fail; @@ -314,10 +311,8 @@ void omap_fbdev_free(struct drm_device *dev) omap_gem_put_paddr(fbdev->bo); /* this will free the backing object */ - if (fbdev->fb) { - drm_framebuffer_unregister_private(fbdev->fb); + if (fbdev->fb) drm_framebuffer_remove(fbdev->fb); - } kfree(fbdev); diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 74a9968df421..5d5a9f517c30 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -518,7 +518,6 @@ static int fault_2d(struct drm_gem_object *obj, /** * omap_gem_fault - pagefault handler for GEM objects - * @vma: the VMA of the GEM object * @vmf: fault detail * * Invoked when a fault occurs on an mmap of a GEM managed area. GEM @@ -529,8 +528,9 @@ static int fault_2d(struct drm_gem_object *obj, * vma->vm_private_data points to the GEM object that is backing this * mapping. */ -int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int omap_gem_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj = vma->vm_private_data; struct omap_gem_object *omap_obj = to_omap_bo(obj); struct drm_device *dev = obj->dev; diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 60e1e8016708..9adfa7c99695 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -19,25 +19,24 @@ #include "omap_drv.h" -static DEFINE_SPINLOCK(list_lock); - -static void omap_irq_error_handler(struct omap_drm_irq *irq, - uint32_t irqstatus) -{ - DRM_ERROR("errors: %08x\n", irqstatus); -} +struct omap_irq_wait { + struct list_head node; + wait_queue_head_t wq; + uint32_t irqmask; + int count; +}; -/* call with list_lock and dispc runtime held */ +/* call with wait_lock and dispc runtime held */ static void omap_irq_update(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - struct omap_drm_irq *irq; - uint32_t irqmask = priv->vblank_mask; + struct omap_irq_wait *wait; + uint32_t irqmask = priv->irq_mask; - assert_spin_locked(&list_lock); + assert_spin_locked(&priv->wait_lock); - list_for_each_entry(irq, &priv->irq_list, node) - irqmask |= irq->irqmask; + list_for_each_entry(wait, &priv->wait_list, node) + irqmask |= wait->irqmask; DBG("irqmask=%08x", irqmask); @@ -45,90 +44,48 @@ static void omap_irq_update(struct drm_device *dev) dispc_read_irqenable(); /* flush posted write */ } -void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) -{ - struct omap_drm_private *priv = dev->dev_private; - unsigned long flags; - - spin_lock_irqsave(&list_lock, flags); - - if (!WARN_ON(irq->registered)) { - irq->registered = true; - list_add(&irq->node, &priv->irq_list); - omap_irq_update(dev); - } - - spin_unlock_irqrestore(&list_lock, flags); -} - -void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) -{ - dispc_runtime_get(); - - __omap_irq_register(dev, irq); - - dispc_runtime_put(); -} - -void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) +static void omap_irq_wait_handler(struct omap_irq_wait *wait) { - unsigned long flags; - - spin_lock_irqsave(&list_lock, flags); - - if (!WARN_ON(!irq->registered)) { - irq->registered = false; - list_del(&irq->node); - omap_irq_update(dev); - } - - spin_unlock_irqrestore(&list_lock, flags); -} - -void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) -{ - dispc_runtime_get(); - - __omap_irq_unregister(dev, irq); - - dispc_runtime_put(); -} - -struct omap_irq_wait { - struct omap_drm_irq irq; - int count; -}; - -static DECLARE_WAIT_QUEUE_HEAD(wait_event); - -static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus) -{ - struct omap_irq_wait *wait = - container_of(irq, struct omap_irq_wait, irq); wait->count--; - wake_up_all(&wait_event); + wake_up(&wait->wq); } struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, uint32_t irqmask, int count) { + struct omap_drm_private *priv = dev->dev_private; struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL); - wait->irq.irq = wait_irq; - wait->irq.irqmask = irqmask; + unsigned long flags; + + init_waitqueue_head(&wait->wq); + wait->irqmask = irqmask; wait->count = count; - omap_irq_register(dev, &wait->irq); + + spin_lock_irqsave(&priv->wait_lock, flags); + list_add(&wait->node, &priv->wait_list); + omap_irq_update(dev); + spin_unlock_irqrestore(&priv->wait_lock, flags); + return wait; } int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, unsigned long timeout) { - int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout); - omap_irq_unregister(dev, &wait->irq); + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + int ret; + + ret = wait_event_timeout(wait->wq, (wait->count <= 0), timeout); + + spin_lock_irqsave(&priv->wait_lock, flags); + list_del(&wait->node); + omap_irq_update(dev); + spin_unlock_irqrestore(&priv->wait_lock, flags); + kfree(wait); - if (ret == 0) - return -1; - return 0; + + return ret == 0 ? -1 : 0; } /** @@ -152,10 +109,10 @@ int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe) DBG("dev=%p, crtc=%u", dev, pipe); - spin_lock_irqsave(&list_lock, flags); - priv->vblank_mask |= pipe2vbl(crtc); + spin_lock_irqsave(&priv->wait_lock, flags); + priv->irq_mask |= dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc)); omap_irq_update(dev); - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags); return 0; } @@ -177,17 +134,66 @@ void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe) DBG("dev=%p, crtc=%u", dev, pipe); - spin_lock_irqsave(&list_lock, flags); - priv->vblank_mask &= ~pipe2vbl(crtc); + spin_lock_irqsave(&priv->wait_lock, flags); + priv->irq_mask &= ~dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc)); omap_irq_update(dev); - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags); +} + +static void omap_irq_fifo_underflow(struct omap_drm_private *priv, + u32 irqstatus) +{ + static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + static const struct { + const char *name; + u32 mask; + } sources[] = { + { "gfx", DISPC_IRQ_GFX_FIFO_UNDERFLOW }, + { "vid1", DISPC_IRQ_VID1_FIFO_UNDERFLOW }, + { "vid2", DISPC_IRQ_VID2_FIFO_UNDERFLOW }, + { "vid3", DISPC_IRQ_VID3_FIFO_UNDERFLOW }, + }; + + const u32 mask = DISPC_IRQ_GFX_FIFO_UNDERFLOW + | DISPC_IRQ_VID1_FIFO_UNDERFLOW + | DISPC_IRQ_VID2_FIFO_UNDERFLOW + | DISPC_IRQ_VID3_FIFO_UNDERFLOW; + unsigned int i; + + spin_lock(&priv->wait_lock); + irqstatus &= priv->irq_mask & mask; + spin_unlock(&priv->wait_lock); + + if (!irqstatus) + return; + + if (!__ratelimit(&_rs)) + return; + + DRM_ERROR("FIFO underflow on "); + + for (i = 0; i < ARRAY_SIZE(sources); ++i) { + if (sources[i].mask & irqstatus) + pr_cont("%s ", sources[i].name); + } + + pr_cont("(0x%08x)\n", irqstatus); +} + +static void omap_irq_ocp_error_handler(u32 irqstatus) +{ + if (!(irqstatus & DISPC_IRQ_OCP_ERR)) + return; + + DRM_ERROR("OCP error\n"); } static irqreturn_t omap_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; struct omap_drm_private *priv = dev->dev_private; - struct omap_drm_irq *handler, *n; + struct omap_irq_wait *wait, *n; unsigned long flags; unsigned int id; u32 irqstatus; @@ -200,24 +206,37 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) for (id = 0; id < priv->num_crtcs; id++) { struct drm_crtc *crtc = priv->crtcs[id]; + enum omap_channel channel = omap_crtc_channel(crtc); - if (irqstatus & pipe2vbl(crtc)) + if (irqstatus & dispc_mgr_get_vsync_irq(channel)) { drm_handle_vblank(dev, id); + omap_crtc_vblank_irq(crtc); + } + + if (irqstatus & dispc_mgr_get_sync_lost_irq(channel)) + omap_crtc_error_irq(crtc, irqstatus); } - spin_lock_irqsave(&list_lock, flags); - list_for_each_entry_safe(handler, n, &priv->irq_list, node) { - if (handler->irqmask & irqstatus) { - spin_unlock_irqrestore(&list_lock, flags); - handler->irq(handler, handler->irqmask & irqstatus); - spin_lock_irqsave(&list_lock, flags); - } + omap_irq_ocp_error_handler(irqstatus); + omap_irq_fifo_underflow(priv, irqstatus); + + spin_lock_irqsave(&priv->wait_lock, flags); + list_for_each_entry_safe(wait, n, &priv->wait_list, node) { + if (wait->irqmask & irqstatus) + omap_irq_wait_handler(wait); } - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags); return IRQ_HANDLED; } +static const u32 omap_underflow_irqs[] = { + [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, +}; + /* * We need a special version, instead of just using drm_irq_install(), * because we need to register the irq via omapdss. Once omapdss and @@ -228,10 +247,25 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) int omap_drm_irq_install(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - struct omap_drm_irq *error_handler = &priv->error_handler; + unsigned int num_mgrs = dss_feat_get_num_mgrs(); + unsigned int max_planes; + unsigned int i; int ret; - INIT_LIST_HEAD(&priv->irq_list); + spin_lock_init(&priv->wait_lock); + INIT_LIST_HEAD(&priv->wait_list); + + priv->irq_mask = DISPC_IRQ_OCP_ERR; + + max_planes = min(ARRAY_SIZE(priv->planes), + ARRAY_SIZE(omap_underflow_irqs)); + for (i = 0; i < max_planes; ++i) { + if (priv->planes[i]) + priv->irq_mask |= omap_underflow_irqs[i]; + } + + for (i = 0; i < num_mgrs; ++i) + priv->irq_mask |= dispc_mgr_get_sync_lost_irq(i); dispc_runtime_get(); dispc_clear_irqstatus(0xffffffff); @@ -241,16 +275,6 @@ int omap_drm_irq_install(struct drm_device *dev) if (ret < 0) return ret; - error_handler->irq = omap_irq_error_handler; - error_handler->irqmask = DISPC_IRQ_OCP_ERR; - - /* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think - * we just need to ignore it while enabling tv-out - */ - error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; - - omap_irq_register(dev, error_handler); - dev->irq_enabled = true; return 0; diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 82b2c23d6769..386d90af70f7 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -43,8 +43,6 @@ struct omap_plane { uint32_t nformats; uint32_t formats[32]; - - struct omap_drm_irq error_irq; }; struct omap_plane_state { @@ -204,8 +202,6 @@ static void omap_plane_destroy(struct drm_plane *plane) DBG("%s", omap_plane->name); - omap_irq_unregister(plane->dev, &omap_plane->error_irq); - drm_plane_cleanup(plane); kfree(omap_plane); @@ -332,14 +328,6 @@ static const struct drm_plane_funcs omap_plane_funcs = { .atomic_get_property = omap_plane_atomic_get_property, }; -static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) -{ - struct omap_plane *omap_plane = - container_of(irq, struct omap_plane, error_irq); - DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name, - irqstatus); -} - static const char *plane_names[] = { [OMAP_DSS_GFX] = "gfx", [OMAP_DSS_VIDEO1] = "vid1", @@ -347,13 +335,6 @@ static const char *plane_names[] = { [OMAP_DSS_VIDEO3] = "vid3", }; -static const uint32_t error_irqs[] = { - [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, - [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, - [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, - [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, -}; - /* initialize plane */ struct drm_plane *omap_plane_init(struct drm_device *dev, int id, enum drm_plane_type type, @@ -377,10 +358,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, plane = &omap_plane->base; - omap_plane->error_irq.irqmask = error_irqs[id]; - omap_plane->error_irq.irq = omap_plane_error_irq; - omap_irq_register(dev, &omap_plane->error_irq); - ret = drm_universal_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs, omap_plane->formats, omap_plane->nformats, type, NULL); @@ -394,7 +371,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, return plane; error: - omap_irq_unregister(plane->dev, &omap_plane->error_irq); kfree(omap_plane); return NULL; } diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 06aaf79de8c8..89eb0422821c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -668,6 +668,48 @@ static const struct panel_desc avic_tm070ddh03 = { }, }; +static const struct drm_display_mode boe_nv101wxmn51_modes[] = { + { + .clock = 71900, + .hdisplay = 1280, + .hsync_start = 1280 + 48, + .hsync_end = 1280 + 48 + 32, + .htotal = 1280 + 48 + 32 + 80, + .vdisplay = 800, + .vsync_start = 800 + 3, + .vsync_end = 800 + 3 + 5, + .vtotal = 800 + 3 + 5 + 24, + .vrefresh = 60, + }, + { + .clock = 57500, + .hdisplay = 1280, + .hsync_start = 1280 + 48, + .hsync_end = 1280 + 48 + 32, + .htotal = 1280 + 48 + 32 + 80, + .vdisplay = 800, + .vsync_start = 800 + 3, + .vsync_end = 800 + 3 + 5, + .vtotal = 800 + 3 + 5 + 24, + .vrefresh = 48, + }, +}; + +static const struct panel_desc boe_nv101wxmn51 = { + .modes = boe_nv101wxmn51_modes, + .num_modes = ARRAY_SIZE(boe_nv101wxmn51_modes), + .bpc = 8, + .size = { + .width = 217, + .height = 136, + }, + .delay = { + .prepare = 210, + .enable = 50, + .unprepare = 160, + }, +}; + static const struct drm_display_mode chunghwa_claa070wp03xg_mode = { .clock = 66770, .hdisplay = 800, @@ -760,6 +802,8 @@ static const struct panel_desc edt_et057090dhu = { .width = 115, .height = 86, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE, }; static const struct drm_display_mode edt_etm0700g0dh6_mode = { @@ -784,6 +828,8 @@ static const struct panel_desc edt_etm0700g0dh6 = { .width = 152, .height = 91, }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE, }; static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = { @@ -1277,6 +1323,29 @@ static const struct panel_desc nec_nl4827hc19_05b = { .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, }; +static const struct drm_display_mode netron_dy_e231732_mode = { + .clock = 66000, + .hdisplay = 1024, + .hsync_start = 1024 + 160, + .hsync_end = 1024 + 160 + 70, + .htotal = 1024 + 160 + 70 + 90, + .vdisplay = 600, + .vsync_start = 600 + 127, + .vsync_end = 600 + 127 + 20, + .vtotal = 600 + 127 + 20 + 3, + .vrefresh = 60, +}; + +static const struct panel_desc netron_dy_e231732 = { + .modes = &netron_dy_e231732_mode, + .num_modes = 1, + .size = { + .width = 154, + .height = 87, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, +}; + static const struct drm_display_mode nvd_9128_mode = { .clock = 29500, .hdisplay = 800, @@ -1632,6 +1701,30 @@ static const struct panel_desc starry_kr122ea0sra = { }, }; +static const struct display_timing tianma_tm070jdhg30_timing = { + .pixelclock = { 62600000, 68200000, 78100000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 15, 64, 159 }, + .hback_porch = { 5, 5, 5 }, + .hsync_len = { 1, 1, 256 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 3, 40, 99 }, + .vback_porch = { 2, 2, 2 }, + .vsync_len = { 1, 1, 128 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc tianma_tm070jdhg30 = { + .timings = &tianma_tm070jdhg30_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 151, + .height = 95, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, +}; + static const struct drm_display_mode tpk_f07a_0102_mode = { .clock = 33260, .hdisplay = 800, @@ -1748,6 +1841,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "avic,tm070ddh03", .data = &avic_tm070ddh03, }, { + .compatible = "boe,nv101wxmn51", + .data = &boe_nv101wxmn51, + }, { .compatible = "chunghwa,claa070wp03xg", .data = &chunghwa_claa070wp03xg, }, { @@ -1826,6 +1922,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "nec,nl4827hc19-05b", .data = &nec_nl4827hc19_05b, }, { + .compatible = "netron-dy,e231732", + .data = &netron_dy_e231732, + }, { .compatible = "nvd,9128", .data = &nvd_9128, }, { @@ -1868,6 +1967,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "starry,kr122ea0sra", .data = &starry_kr122ea0sra, }, { + .compatible = "tianma,tm070jdhg30", + .data = &tianma_tm070jdhg30, + }, { .compatible = "tpk,f07a-0102", .data = &tpk_f07a_0102, }, { diff --git a/drivers/gpu/drm/qxl/Kconfig b/drivers/gpu/drm/qxl/Kconfig index da45b11b66b8..378da5918e6c 100644 --- a/drivers/gpu/drm/qxl/Kconfig +++ b/drivers/gpu/drm/qxl/Kconfig @@ -1,6 +1,6 @@ config DRM_QXL tristate "QXL virtual GPU" - depends on DRM && PCI + depends on DRM && PCI && MMU select DRM_KMS_HELPER select DRM_TTM select CRC32 diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c index 241af9131dc8..d58751c94618 100644 --- a/drivers/gpu/drm/qxl/qxl_debugfs.c +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -84,8 +84,18 @@ int qxl_debugfs_init(struct drm_minor *minor) { #if defined(CONFIG_DEBUG_FS) + int r; + struct qxl_device *dev = + (struct qxl_device *) minor->dev->dev_private; + drm_debugfs_create_files(qxl_debugfs_list, QXL_DEBUGFS_ENTRIES, minor->debugfs_root, minor); + + r = qxl_ttm_debugfs_init(dev); + if (r) { + DRM_ERROR("Failed to init TTM debugfs\n"); + return r; + } #endif return 0; } @@ -123,8 +133,8 @@ int qxl_debugfs_add_files(struct qxl_device *qdev, qdev->debugfs_count = i; #if defined(CONFIG_DEBUG_FS) drm_debugfs_create_files(files, nfiles, - qdev->ddev->primary->debugfs_root, - qdev->ddev->primary); + qdev->ddev.primary->debugfs_root, + qdev->ddev.primary); #endif return 0; } @@ -137,7 +147,7 @@ void qxl_debugfs_remove_files(struct qxl_device *qdev) for (i = 0; i < qdev->debugfs_count; i++) { drm_debugfs_remove_files(qdev->debugfs[i].files, qdev->debugfs[i].num_files, - qdev->ddev->primary); + qdev->ddev.primary); } #endif } diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 4b5eab8a47b3..1094cd33eb06 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -136,7 +136,7 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) static void qxl_update_offset_props(struct qxl_device *qdev) { - struct drm_device *dev = qdev->ddev; + struct drm_device *dev = &qdev->ddev; struct drm_connector *connector; struct qxl_output *output; struct qxl_head *head; @@ -156,7 +156,7 @@ static void qxl_update_offset_props(struct qxl_device *qdev) void qxl_display_read_client_monitors_config(struct qxl_device *qdev) { - struct drm_device *dev = qdev->ddev; + struct drm_device *dev = &qdev->ddev; int status; status = qxl_display_copy_rom_client_monitors_config(qdev); @@ -174,10 +174,10 @@ void qxl_display_read_client_monitors_config(struct qxl_device *qdev) drm_modeset_lock_all(dev); qxl_update_offset_props(qdev); drm_modeset_unlock_all(dev); - if (!drm_helper_hpd_irq_event(qdev->ddev)) { + if (!drm_helper_hpd_irq_event(dev)) { /* notify that the monitor configuration changed, to adjust at the arbitrary resolution */ - drm_kms_helper_hotplug_event(qdev->ddev); + drm_kms_helper_hotplug_event(dev); } } @@ -624,12 +624,12 @@ qxl_framebuffer_init(struct drm_device *dev, int ret; qfb->obj = obj; + drm_helper_mode_fill_fb_struct(dev, &qfb->base, mode_cmd); ret = drm_framebuffer_init(dev, &qfb->base, funcs); if (ret) { qfb->obj = NULL; return ret; } - drm_helper_mode_fill_fb_struct(&qfb->base, mode_cmd); return 0; } @@ -1036,7 +1036,7 @@ static int qxl_mode_create_hotplug_mode_update_property(struct qxl_device *qdev) return 0; qdev->hotplug_mode_update_property = - drm_property_create_range(qdev->ddev, DRM_MODE_PROP_IMMUTABLE, + drm_property_create_range(&qdev->ddev, DRM_MODE_PROP_IMMUTABLE, "hotplug_mode_update", 0, 1); return 0; @@ -1077,7 +1077,6 @@ static int qdev_output_init(struct drm_device *dev, int num_output) dev->mode_config.suggested_x_property, 0); drm_object_attach_property(&connector->base, dev->mode_config.suggested_y_property, 0); - drm_connector_register(connector); return 0; } @@ -1176,28 +1175,28 @@ int qxl_modeset_init(struct qxl_device *qdev) int i; int ret; - drm_mode_config_init(qdev->ddev); + drm_mode_config_init(&qdev->ddev); ret = qxl_create_monitors_object(qdev); if (ret) return ret; - qdev->ddev->mode_config.funcs = (void *)&qxl_mode_funcs; + qdev->ddev.mode_config.funcs = (void *)&qxl_mode_funcs; /* modes will be validated against the framebuffer size */ - qdev->ddev->mode_config.min_width = 320; - qdev->ddev->mode_config.min_height = 200; - qdev->ddev->mode_config.max_width = 8192; - qdev->ddev->mode_config.max_height = 8192; + qdev->ddev.mode_config.min_width = 320; + qdev->ddev.mode_config.min_height = 200; + qdev->ddev.mode_config.max_width = 8192; + qdev->ddev.mode_config.max_height = 8192; - qdev->ddev->mode_config.fb_base = qdev->vram_base; + qdev->ddev.mode_config.fb_base = qdev->vram_base; - drm_mode_create_suggested_offset_properties(qdev->ddev); + drm_mode_create_suggested_offset_properties(&qdev->ddev); qxl_mode_create_hotplug_mode_update_property(qdev); for (i = 0 ; i < qxl_num_crtc; ++i) { - qdev_crtc_init(qdev->ddev, i); - qdev_output_init(qdev->ddev, i); + qdev_crtc_init(&qdev->ddev, i); + qdev_output_init(&qdev->ddev, i); } qdev->mode_info.mode_config_initialized = true; @@ -1215,7 +1214,7 @@ void qxl_modeset_fini(struct qxl_device *qdev) qxl_destroy_monitors_object(qdev); if (qdev->mode_info.mode_config_initialized) { - drm_mode_config_cleanup(qdev->ddev); + drm_mode_config_cleanup(&qdev->ddev); qdev->mode_info.mode_config_initialized = false; } } diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c index 9b728edf1b49..4d8681e84e68 100644 --- a/drivers/gpu/drm/qxl/qxl_draw.c +++ b/drivers/gpu/drm/qxl/qxl_draw.c @@ -283,7 +283,7 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, struct qxl_rect *rects; int stride = qxl_fb->base.pitches[0]; /* depth is not actually interesting, we don't mask with it */ - int depth = qxl_fb->base.bits_per_pixel; + int depth = qxl_fb->base.format->cpp[0] * 8; uint8_t *surface_base; struct qxl_release *release; struct qxl_bo *clips_bo; diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 460bbceae297..8e17c241e63c 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -62,20 +62,71 @@ static struct pci_driver qxl_pci_driver; static int qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct qxl_device *qdev; + int ret; + if (pdev->revision < 4) { DRM_ERROR("qxl too old, doesn't support client_monitors_config," " use xf86-video-qxl in user mode"); return -EINVAL; /* TODO: ENODEV ? */ } - return drm_get_pci_dev(pdev, ent, &qxl_driver); + + qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL); + if (!qdev) + return -ENOMEM; + + ret = pci_enable_device(pdev); + if (ret) + goto free_dev; + + ret = qxl_device_init(qdev, &qxl_driver, pdev, ent->driver_data); + if (ret) + goto disable_pci; + + ret = drm_vblank_init(&qdev->ddev, 1); + if (ret) + goto unload; + + ret = qxl_modeset_init(qdev); + if (ret) + goto vblank_cleanup; + + drm_kms_helper_poll_init(&qdev->ddev); + + /* Complete initialization. */ + ret = drm_dev_register(&qdev->ddev, ent->driver_data); + if (ret) + goto modeset_cleanup; + + return 0; + +modeset_cleanup: + qxl_modeset_fini(qdev); +vblank_cleanup: + drm_vblank_cleanup(&qdev->ddev); +unload: + qxl_device_fini(qdev); +disable_pci: + pci_disable_device(pdev); +free_dev: + kfree(qdev); + return ret; } static void qxl_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); + struct qxl_device *qdev = dev->dev_private; + + drm_dev_unregister(dev); + + qxl_modeset_fini(qdev); + qxl_device_fini(qdev); - drm_put_dev(dev); + dev->dev_private = NULL; + kfree(qdev); + drm_dev_unref(dev); } static const struct file_operations qxl_fops = { @@ -230,8 +281,6 @@ static struct pci_driver qxl_pci_driver = { static struct drm_driver qxl_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, - .load = qxl_driver_load, - .unload = qxl_driver_unload, .get_vblank_counter = qxl_noop_get_vblank_counter, .enable_vblank = qxl_noop_enable_vblank, .disable_vblank = qxl_noop_disable_vblank, diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 785aad42e9bb..785c17b56f73 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -43,6 +43,7 @@ #include <ttm/ttm_placement.h> #include <ttm/ttm_module.h> +#include <drm/drm_encoder.h> #include <drm/drm_gem.h> /* just for ttm_validate_buffer */ @@ -241,9 +242,7 @@ void qxl_debugfs_remove_files(struct qxl_device *qdev); struct qxl_device; struct qxl_device { - struct device *dev; - struct drm_device *ddev; - struct pci_dev *pdev; + struct drm_device ddev; unsigned long flags; resource_size_t vram_base, vram_size; @@ -335,8 +334,9 @@ __printf(2,3) void qxl_io_log(struct qxl_device *qdev, const char *fmt, ...); extern const struct drm_ioctl_desc qxl_ioctls[]; extern int qxl_max_ioctl; -int qxl_driver_load(struct drm_device *dev, unsigned long flags); -int qxl_driver_unload(struct drm_device *dev); +int qxl_device_init(struct qxl_device *qdev, struct drm_driver *drv, + struct pci_dev *pdev, unsigned long flags); +void qxl_device_fini(struct qxl_device *qdev); int qxl_modeset_init(struct qxl_device *qdev); void qxl_modeset_fini(struct qxl_device *qdev); @@ -530,6 +530,7 @@ int qxl_garbage_collect(struct qxl_device *qdev); int qxl_debugfs_init(struct drm_minor *minor); void qxl_debugfs_takedown(struct drm_minor *minor); +int qxl_ttm_debugfs_init(struct qxl_device *qdev); /* qxl_prime.c */ int qxl_gem_prime_pin(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index fd7e5e94be5b..d479b7a7abe4 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -268,7 +268,7 @@ static int qxlfb_create(struct qxl_fbdev *qfbdev, info->par = qfbdev; - qxl_framebuffer_init(qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj, + qxl_framebuffer_init(&qdev->ddev, &qfbdev->qfb, &mode_cmd, gobj, &qxlfb_fb_funcs); fb = &qfbdev->qfb.base; @@ -279,7 +279,7 @@ static int qxlfb_create(struct qxl_fbdev *qfbdev, qfbdev->shadow = shadow; strcpy(info->fix.id, "qxldrmfb"); - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; info->fbops = &qxlfb_ops; @@ -297,7 +297,7 @@ static int qxlfb_create(struct qxl_fbdev *qfbdev, sizes->fb_height); /* setup aperture base/size for vesafb takeover */ - info->apertures->ranges[0].base = qdev->ddev->mode_config.fb_base; + info->apertures->ranges[0].base = qdev->ddev.mode_config.fb_base; info->apertures->ranges[0].size = qdev->vram_size; info->fix.mmio_start = 0; @@ -316,7 +316,8 @@ static int qxlfb_create(struct qxl_fbdev *qfbdev, qdev->fbdev_info = info; qdev->fbdev_qfb = &qfbdev->qfb; DRM_INFO("fb mappable at 0x%lX, size %lu\n", info->fix.smem_start, (unsigned long)info->screen_size); - DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n", fb->depth, fb->pitches[0], fb->width, fb->height); + DRM_INFO("fb: depth %d, pitch %d, width %d, height %d\n", + fb->format->depth, fb->pitches[0], fb->width, fb->height); return 0; out_destroy_fbi: @@ -394,11 +395,10 @@ int qxl_fbdev_init(struct qxl_device *qdev) spin_lock_init(&qfbdev->delayed_ops_lock); INIT_LIST_HEAD(&qfbdev->delayed_ops); - drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper, + drm_fb_helper_prepare(&qdev->ddev, &qfbdev->helper, &qxl_fb_helper_funcs); - ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, - qxl_num_crtc /* num_crtc - QXL supports just 1 */, + ret = drm_fb_helper_init(&qdev->ddev, &qfbdev->helper, QXLFB_CONN_LIMIT); if (ret) goto free; @@ -425,7 +425,7 @@ void qxl_fbdev_fini(struct qxl_device *qdev) if (!qdev->mode_info.qfbdev) return; - qxl_fbdev_destroy(qdev->ddev, qdev->mode_info.qfbdev); + qxl_fbdev_destroy(&qdev->ddev, qdev->mode_info.qfbdev); kfree(qdev->mode_info.qfbdev); qdev->mode_info.qfbdev = NULL; } diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 5a4c8c492683..0b82a87916ae 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -64,7 +64,7 @@ static int qxl_map_ioctl(struct drm_device *dev, void *data, struct qxl_device *qdev = dev->dev_private; struct drm_qxl_map *qxl_map = data; - return qxl_mode_dumb_mmap(file_priv, qdev->ddev, qxl_map->handle, + return qxl_mode_dumb_mmap(file_priv, &qdev->ddev, qxl_map->handle, &qxl_map->offset); } @@ -375,7 +375,7 @@ static int qxl_clientcap_ioctl(struct drm_device *dev, void *data, byte = param->index / 8; idx = param->index % 8; - if (qdev->pdev->revision < 4) + if (dev->pdev->revision < 4) return -ENOSYS; if (byte >= 58) diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c index 0bf1e20c6e44..23a40106ab53 100644 --- a/drivers/gpu/drm/qxl/qxl_irq.c +++ b/drivers/gpu/drm/qxl/qxl_irq.c @@ -90,7 +90,7 @@ int qxl_irq_init(struct qxl_device *qdev) atomic_set(&qdev->irq_received_cursor, 0); atomic_set(&qdev->irq_received_io_cmd, 0); qdev->irq_received_error = 0; - ret = drm_irq_install(qdev->ddev, qdev->ddev->pdev->irq); + ret = drm_irq_install(&qdev->ddev, qdev->ddev.pdev->irq); qdev->ram_header->int_mask = QXL_INTERRUPT_MASK; if (unlikely(ret != 0)) { DRM_ERROR("Failed installing irq: %d\n", ret); diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index af685f1d91f8..2dcd5c14cb56 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -115,16 +115,21 @@ static void qxl_gc_work(struct work_struct *work) qxl_garbage_collect(qdev); } -static int qxl_device_init(struct qxl_device *qdev, - struct drm_device *ddev, +int qxl_device_init(struct qxl_device *qdev, + struct drm_driver *drv, struct pci_dev *pdev, unsigned long flags) { int r, sb; - qdev->dev = &pdev->dev; - qdev->ddev = ddev; - qdev->pdev = pdev; + r = drm_dev_init(&qdev->ddev, drv, &pdev->dev); + if (r) + return r; + + qdev->ddev.pdev = pdev; + pci_set_drvdata(pdev, &qdev->ddev); + qdev->ddev.dev_private = qdev; + qdev->flags = flags; mutex_init(&qdev->gem.mutex); @@ -263,7 +268,7 @@ static int qxl_device_init(struct qxl_device *qdev, return 0; } -static void qxl_device_fini(struct qxl_device *qdev) +void qxl_device_fini(struct qxl_device *qdev) { if (qdev->current_release_bo[0]) qxl_bo_unref(&qdev->current_release_bo[0]); @@ -284,56 +289,3 @@ static void qxl_device_fini(struct qxl_device *qdev) qdev->mode_info.num_modes = 0; qxl_debugfs_remove_files(qdev); } - -int qxl_driver_unload(struct drm_device *dev) -{ - struct qxl_device *qdev = dev->dev_private; - - if (qdev == NULL) - return 0; - - drm_vblank_cleanup(dev); - - qxl_modeset_fini(qdev); - qxl_device_fini(qdev); - - kfree(qdev); - dev->dev_private = NULL; - return 0; -} - -int qxl_driver_load(struct drm_device *dev, unsigned long flags) -{ - struct qxl_device *qdev; - int r; - - qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL); - if (qdev == NULL) - return -ENOMEM; - - dev->dev_private = qdev; - - r = qxl_device_init(qdev, dev, dev->pdev, flags); - if (r) - goto out; - - r = drm_vblank_init(dev, 1); - if (r) - goto unload; - - r = qxl_modeset_init(qdev); - if (r) - goto unload; - - drm_kms_helper_poll_init(qdev->ddev); - - return 0; -unload: - qxl_driver_unload(dev); - -out: - kfree(qdev); - return r; -} - - diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index fa5440dc9a19..dbc13510a1f8 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -93,7 +93,7 @@ int qxl_bo_create(struct qxl_device *qdev, if (bo == NULL) return -ENOMEM; size = roundup(size, PAGE_SIZE); - r = drm_gem_object_init(qdev->ddev, &bo->gem_base, size); + r = drm_gem_object_init(&qdev->ddev, &bo->gem_base, size); if (unlikely(r)) { kfree(bo); return r; @@ -113,7 +113,7 @@ int qxl_bo_create(struct qxl_device *qdev, NULL, NULL, &qxl_ttm_bo_destroy); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) - dev_err(qdev->dev, + dev_err(qdev->ddev.dev, "object_init failed for (%lu, 0x%08X)\n", size, domain); return r; @@ -223,7 +223,7 @@ struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo) int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + struct drm_device *ddev = bo->gem_base.dev; int r; if (bo->pin_count) { @@ -240,17 +240,17 @@ int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) *gpu_addr = qxl_bo_gpu_offset(bo); } if (unlikely(r != 0)) - dev_err(qdev->dev, "%p pin failed\n", bo); + dev_err(ddev->dev, "%p pin failed\n", bo); return r; } int qxl_bo_unpin(struct qxl_bo *bo) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; + struct drm_device *ddev = bo->gem_base.dev; int r, i; if (!bo->pin_count) { - dev_warn(qdev->dev, "%p unpin not necessary\n", bo); + dev_warn(ddev->dev, "%p unpin not necessary\n", bo); return 0; } bo->pin_count--; @@ -260,7 +260,7 @@ int qxl_bo_unpin(struct qxl_bo *bo) bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (unlikely(r != 0)) - dev_err(qdev->dev, "%p validate failed for unpin\n", bo); + dev_err(ddev->dev, "%p validate failed for unpin\n", bo); return r; } @@ -270,9 +270,9 @@ void qxl_bo_force_delete(struct qxl_device *qdev) if (list_empty(&qdev->gem.objects)) return; - dev_err(qdev->dev, "Userspace still has active objects !\n"); + dev_err(qdev->ddev.dev, "Userspace still has active objects !\n"); list_for_each_entry_safe(bo, n, &qdev->gem.objects, list) { - dev_err(qdev->dev, "%p %p %lu %lu force free\n", + dev_err(qdev->ddev.dev, "%p %p %lu %lu force free\n", &bo->gem_base, bo, (unsigned long)bo->gem_base.size, *((unsigned long *)&bo->gem_base.refcount)); mutex_lock(&qdev->gem.mutex); diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index 4d8311373ba3..0374fd93f4d6 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -34,8 +34,8 @@ static inline int qxl_bo_reserve(struct qxl_bo *bo, bool no_wait) r = ttm_bo_reserve(&bo->tbo, true, no_wait, NULL); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; - dev_err(qdev->dev, "%p reserve failed\n", bo); + struct drm_device *ddev = bo->gem_base.dev; + dev_err(ddev->dev, "%p reserve failed\n", bo); } return r; } @@ -70,8 +70,8 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, r = ttm_bo_reserve(&bo->tbo, true, no_wait, NULL); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) { - struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; - dev_err(qdev->dev, "%p reserve failed for wait\n", + struct drm_device *ddev = bo->gem_base.dev; + dev_err(ddev->dev, "%p reserve failed for wait\n", bo); } return r; diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 11761330a6b8..7d1cab57c89e 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -35,7 +35,6 @@ #include "qxl_object.h" #include <linux/delay.h> -static int qxl_ttm_debugfs_init(struct qxl_device *qdev); static struct qxl_device *qxl_get_qdev(struct ttm_bo_device *bdev) { @@ -106,15 +105,15 @@ static void qxl_ttm_global_fini(struct qxl_device *qdev) static struct vm_operations_struct qxl_ttm_vm_ops; static const struct vm_operations_struct *ttm_vm_ops; -static int qxl_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int qxl_ttm_fault(struct vm_fault *vmf) { struct ttm_buffer_object *bo; int r; - bo = (struct ttm_buffer_object *)vma->vm_private_data; + bo = (struct ttm_buffer_object *)vmf->vma->vm_private_data; if (bo == NULL) return VM_FAULT_NOPAGE; - r = ttm_vm_ops->fault(vma, vmf); + r = ttm_vm_ops->fault(vmf); return r; } @@ -367,6 +366,7 @@ static int qxl_bo_move(struct ttm_buffer_object *bo, } static void qxl_bo_move_notify(struct ttm_buffer_object *bo, + bool evict, struct ttm_mem_reg *new_mem) { struct qxl_bo *qbo; @@ -394,8 +394,6 @@ static struct ttm_bo_driver qxl_bo_driver = { .io_mem_reserve = &qxl_ttm_io_mem_reserve, .io_mem_free = &qxl_ttm_io_mem_free, .move_notify = &qxl_bo_move_notify, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int qxl_ttm_init(struct qxl_device *qdev) @@ -410,7 +408,7 @@ int qxl_ttm_init(struct qxl_device *qdev) r = ttm_bo_device_init(&qdev->mman.bdev, qdev->mman.bo_global_ref.ref.object, &qxl_bo_driver, - qdev->ddev->anon_inode->i_mapping, + qdev->ddev.anon_inode->i_mapping, DRM_FILE_PAGE_OFFSET, 0); if (r) { DRM_ERROR("failed initializing buffer object driver(%d).\n", r); @@ -436,11 +434,6 @@ int qxl_ttm_init(struct qxl_device *qdev) ((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024)); DRM_INFO("qxl: %uM of Surface memory size\n", (unsigned)qdev->surfaceram_size / (1024 * 1024)); - r = qxl_ttm_debugfs_init(qdev); - if (r) { - DRM_ERROR("Failed to init debugfs\n"); - return r; - } return 0; } @@ -463,17 +456,17 @@ static int qxl_mm_dump_table(struct seq_file *m, void *data) struct drm_mm *mm = (struct drm_mm *)node->info_ent->data; struct drm_device *dev = node->minor->dev; struct qxl_device *rdev = dev->dev_private; - int ret; struct ttm_bo_global *glob = rdev->mman.bdev.glob; + struct drm_printer p = drm_seq_file_printer(m); spin_lock(&glob->lru_lock); - ret = drm_mm_dump_table(m, mm); + drm_mm_print(mm, &p); spin_unlock(&glob->lru_lock); - return ret; + return 0; } #endif -static int qxl_ttm_debugfs_init(struct qxl_device *qdev) +int qxl_ttm_debugfs_init(struct qxl_device *qdev) { #if defined(CONFIG_DEBUG_FS) static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES]; diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index ab89eed9ddd9..4b86e8b45009 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -181,7 +181,7 @@ #define HW_ASSISTED_I2C_STATUS_FAILURE 2 #define HW_ASSISTED_I2C_STATUS_SUCCESS 1 -#pragma pack(1) /* BIOS data must use byte aligment */ +#pragma pack(1) /* BIOS data must use byte alignment */ /* Define offset to location of ROM header. */ @@ -3883,7 +3883,7 @@ typedef struct _ATOM_GPIO_PIN_ASSIGNMENT }ATOM_GPIO_PIN_ASSIGNMENT; //ucGPIO_ID pre-define id for multiple usage -//from SMU7.x, if ucGPIO_ID=PP_AC_DC_SWITCH_GPIO_PINID in GPIO_LUTTable, AC/DC swithing feature is enable +//from SMU7.x, if ucGPIO_ID=PP_AC_DC_SWITCH_GPIO_PINID in GPIO_LUTTable, AC/DC switching feature is enable #define PP_AC_DC_SWITCH_GPIO_PINID 60 //from SMU7.x, if ucGPIO_ID=VDDC_REGULATOR_VRHOT_GPIO_PINID in GPIO_LUTable, VRHot feature is enable #define VDDC_VRHOT_GPIO_PINID 61 @@ -7909,7 +7909,7 @@ typedef struct _ATOM_POWERPLAY_INFO_V3 /*********************************************************************************/ -#pragma pack() // BIOS data must use byte aligment +#pragma pack() // BIOS data must use byte alignment // // AMD ACPI Table diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 05f4ebe31ce2..3c492a0aa6bd 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1195,7 +1195,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); radeon_bo_unreserve(rbo); - switch (target_fb->pixel_format) { + switch (target_fb->format->format) { case DRM_FORMAT_C8: fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP) | EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED)); @@ -1261,7 +1261,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, break; default: DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format, &format_name)); + drm_get_format_name(target_fb->format->format, &format_name)); return -EINVAL; } @@ -1277,7 +1277,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, /* Calculate the macrotile mode index. */ tile_split_bytes = 64 << tile_split; - tileb = 8 * 8 * target_fb->bits_per_pixel / 8; + tileb = 8 * 8 * target_fb->format->cpp[0]; tileb = min(tile_split_bytes, tileb); for (index = 0; tileb > 64; index++) @@ -1285,13 +1285,14 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, if (index >= 16) { DRM_ERROR("Wrong screen bpp (%u) or tile split (%u)\n", - target_fb->bits_per_pixel, tile_split); + target_fb->format->cpp[0] * 8, + tile_split); return -EINVAL; } num_banks = (rdev->config.cik.macrotile_mode_array[index] >> 6) & 0x3; } else { - switch (target_fb->bits_per_pixel) { + switch (target_fb->format->cpp[0] * 8) { case 8: index = 10; break; @@ -1414,7 +1415,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, WREG32(EVERGREEN_GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width); WREG32(EVERGREEN_GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height); - fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + fb_pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1); @@ -1510,7 +1511,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); radeon_bo_unreserve(rbo); - switch (target_fb->pixel_format) { + switch (target_fb->format->format) { case DRM_FORMAT_C8: fb_format = AVIVO_D1GRPH_CONTROL_DEPTH_8BPP | @@ -1563,7 +1564,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, break; default: DRM_ERROR("Unsupported screen format %s\n", - drm_get_format_name(target_fb->pixel_format, &format_name)); + drm_get_format_name(target_fb->format->format, &format_name)); return -EINVAL; } @@ -1621,7 +1622,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, WREG32(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width); WREG32(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height); - fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); + fb_pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; WREG32(AVIVO_D1GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index f5e84f4b58e6..e3399310d41d 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -3225,13 +3225,19 @@ void r100_bandwidth_update(struct radeon_device *rdev) radeon_update_display_priority(rdev); if (rdev->mode_info.crtcs[0]->base.enabled) { + const struct drm_framebuffer *fb = + rdev->mode_info.crtcs[0]->base.primary->fb; + mode1 = &rdev->mode_info.crtcs[0]->base.mode; - pixel_bytes1 = rdev->mode_info.crtcs[0]->base.primary->fb->bits_per_pixel / 8; + pixel_bytes1 = fb->format->cpp[0]; } if (!(rdev->flags & RADEON_SINGLE_CRTC)) { if (rdev->mode_info.crtcs[1]->base.enabled) { + const struct drm_framebuffer *fb = + rdev->mode_info.crtcs[1]->base.primary->fb; + mode2 = &rdev->mode_info.crtcs[1]->base.mode; - pixel_bytes2 = rdev->mode_info.crtcs[1]->base.primary->fb->bits_per_pixel / 8; + pixel_bytes2 = fb->format->cpp[0]; } } diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index c829cfb02fc4..04c0ed41374f 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -596,52 +596,56 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev) #ifdef CONFIG_ACPI static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) { - bool ret = false; struct acpi_table_header *hdr; acpi_size tbl_size; UEFI_ACPI_VFCT *vfct; - GOP_VBIOS_CONTENT *vbios; - VFCT_IMAGE_HEADER *vhdr; + unsigned offset; if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr))) return false; tbl_size = hdr->length; if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); - goto out_unmap; + return false; } vfct = (UEFI_ACPI_VFCT *)hdr; - if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { - DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); - goto out_unmap; - } + offset = vfct->VBIOSImageOffset; - vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); - vhdr = &vbios->VbiosHeader; - DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", - vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction, - vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength); - - if (vhdr->PCIBus != rdev->pdev->bus->number || - vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) || - vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) || - vhdr->VendorID != rdev->pdev->vendor || - vhdr->DeviceID != rdev->pdev->device) { - DRM_INFO("ACPI VFCT table is not for this card\n"); - goto out_unmap; - } + while (offset < tbl_size) { + GOP_VBIOS_CONTENT *vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + offset); + VFCT_IMAGE_HEADER *vhdr = &vbios->VbiosHeader; - if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { - DRM_ERROR("ACPI VFCT image truncated\n"); - goto out_unmap; - } + offset += sizeof(VFCT_IMAGE_HEADER); + if (offset > tbl_size) { + DRM_ERROR("ACPI VFCT image header truncated\n"); + return false; + } - rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); - ret = !!rdev->bios; + offset += vhdr->ImageLength; + if (offset > tbl_size) { + DRM_ERROR("ACPI VFCT image truncated\n"); + return false; + } + + if (vhdr->ImageLength && + vhdr->PCIBus == rdev->pdev->bus->number && + vhdr->PCIDevice == PCI_SLOT(rdev->pdev->devfn) && + vhdr->PCIFunction == PCI_FUNC(rdev->pdev->devfn) && + vhdr->VendorID == rdev->pdev->vendor && + vhdr->DeviceID == rdev->pdev->device) { + rdev->bios = kmemdup(&vbios->VbiosContent, + vhdr->ImageLength, + GFP_KERNEL); + + if (!rdev->bios) + return false; + return true; + } + } -out_unmap: - return ret; + DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); + return false; } #else static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 510ea371dacc..a8442f7196d6 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -121,7 +121,8 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) VRAM, also but everything into VRAM on AGP cards and older IGP chips to avoid image corruptions */ if (p->ring == R600_RING_TYPE_UVD_INDEX && - (i == 0 || drm_pci_device_is_agp(p->rdev->ddev) || + (i == 0 || pci_find_capability(p->rdev->ddev->pdev, + PCI_CAP_ID_AGP) || p->rdev->family == CHIP_RS780 || p->rdev->family == CHIP_RS880)) { diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 8a1df2a1afbd..4b0c388be3f5 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1549,8 +1549,6 @@ failed: return r; } -static void radeon_debugfs_remove_files(struct radeon_device *rdev); - /** * radeon_device_fini - tear down the driver * @@ -1577,7 +1575,6 @@ void radeon_device_fini(struct radeon_device *rdev) rdev->rmmio = NULL; if (rdev->family >= CHIP_BONAIRE) radeon_doorbell_fini(rdev); - radeon_debugfs_remove_files(rdev); } @@ -1954,16 +1951,3 @@ int radeon_debugfs_add_files(struct radeon_device *rdev, #endif return 0; } - -static void radeon_debugfs_remove_files(struct radeon_device *rdev) -{ -#if defined(CONFIG_DEBUG_FS) - unsigned i; - - for (i = 0; i < rdev->debugfs_count; i++) { - drm_debugfs_remove_files(rdev->debugfs[i].files, - rdev->debugfs[i].num_files, - rdev->ddev->primary); - } -#endif -} diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index e7409e8a9f87..aea8b62835a4 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -549,19 +549,19 @@ static int radeon_crtc_page_flip_target(struct drm_crtc *crtc, if (!ASIC_IS_AVIVO(rdev)) { /* crtc offset is from display base addr not FB location */ base -= radeon_crtc->legacy_display_base_addr; - pitch_pixels = fb->pitches[0] / (fb->bits_per_pixel / 8); + pitch_pixels = fb->pitches[0] / fb->format->cpp[0]; if (tiling_flags & RADEON_TILING_MACRO) { if (ASIC_IS_R300(rdev)) { base &= ~0x7ff; } else { - int byteshift = fb->bits_per_pixel >> 4; + int byteshift = fb->format->cpp[0] * 8 >> 4; int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11; base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8); } } else { int offset = crtc->y * pitch_pixels + crtc->x; - switch (fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: default: offset *= 1; @@ -1327,7 +1327,7 @@ radeon_framebuffer_init(struct drm_device *dev, { int ret; rfb->obj = obj; - drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &rfb->base, mode_cmd); ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs); if (ret) { rfb->obj = NULL; diff --git a/drivers/gpu/drm/radeon/radeon_dp_mst.c b/drivers/gpu/drm/radeon/radeon_dp_mst.c index 6d1237d6e1b8..7d5ada3980dc 100644 --- a/drivers/gpu/drm/radeon/radeon_dp_mst.c +++ b/drivers/gpu/drm/radeon/radeon_dp_mst.c @@ -667,7 +667,7 @@ radeon_dp_mst_init(struct radeon_connector *radeon_connector) return 0; radeon_connector->mst_mgr.cbs = &mst_cbs; - return drm_dp_mst_topology_mgr_init(&radeon_connector->mst_mgr, dev->dev, + return drm_dp_mst_topology_mgr_init(&radeon_connector->mst_mgr, dev, &radeon_connector->ddc_bus->aux, 16, 6, radeon_connector->base.base.id); } diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 30bd4a6a9d46..956c425e639e 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -103,7 +103,7 @@ #define KMS_DRIVER_MINOR 49 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); -int radeon_driver_unload_kms(struct drm_device *dev); +void radeon_driver_unload_kms(struct drm_device *dev); void radeon_driver_lastclose_kms(struct drm_device *dev); int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv); void radeon_driver_postclose_kms(struct drm_device *dev, diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 899b6a1644bd..2be4fe9c7217 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -263,7 +263,7 @@ static int radeonfb_create(struct drm_fb_helper *helper, strcpy(info->fix.id, "radeondrmfb"); - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &radeonfb_ops; @@ -290,7 +290,7 @@ static int radeonfb_create(struct drm_fb_helper *helper, DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base); DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo)); - DRM_INFO("fb depth is %d\n", fb->depth); + DRM_INFO("fb depth is %d\n", fb->format->depth); DRM_INFO(" pitch is %d\n", fb->pitches[0]); vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); @@ -366,7 +366,6 @@ int radeon_fbdev_init(struct radeon_device *rdev) &radeon_fb_helper_funcs); ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, - rdev->num_crtc, RADEONFB_CONN_LIMIT); if (ret) goto free; diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index c084cadcbf21..1b7528df7f7f 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -85,10 +85,8 @@ static void radeon_hotplug_work_func(struct work_struct *work) return; mutex_lock(&mode_config->mutex); - if (mode_config->num_connector) { - list_for_each_entry(connector, &mode_config->connector_list, head) - radeon_connector_hotplug(connector); - } + list_for_each_entry(connector, &mode_config->connector_list, head) + radeon_connector_hotplug(connector); mutex_unlock(&mode_config->mutex); /* Just fire off a uevent and let userspace tell us what to do */ drm_helper_hpd_irq_event(dev); @@ -103,10 +101,8 @@ static void radeon_dp_work_func(struct work_struct *work) struct drm_connector *connector; /* this should take a mutex */ - if (mode_config->num_connector) { - list_for_each_entry(connector, &mode_config->connector_list, head) - radeon_connector_hotplug(connector); - } + list_for_each_entry(connector, &mode_config->connector_list, head) + radeon_connector_hotplug(connector); } /** * radeon_driver_irq_preinstall_kms - drm irq preinstall callback diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 4388ddeec8d2..56f35c06742c 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -53,12 +53,12 @@ static inline bool radeon_has_atpx(void) { return false; } * the rest of the device (CP, writeback, etc.). * Returns 0 on success. */ -int radeon_driver_unload_kms(struct drm_device *dev) +void radeon_driver_unload_kms(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; if (rdev == NULL) - return 0; + return; if (rdev->rmmio == NULL) goto done_free; @@ -78,7 +78,6 @@ int radeon_driver_unload_kms(struct drm_device *dev) done_free: kfree(rdev); dev->dev_private = NULL; - return 0; } /** @@ -106,7 +105,7 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) dev->dev_private = (void *)rdev; /* update BUS flag */ - if (drm_pci_device_is_agp(dev)) { + if (pci_find_capability(dev->pdev, PCI_CAP_ID_AGP)) { flags |= RADEON_IS_AGP; } else if (pci_is_pcie(dev->pdev)) { flags |= RADEON_IS_PCIE; diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index d0de4022fff9..ce6cb6666212 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -402,7 +402,7 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc, target_fb = crtc->primary->fb; } - switch (target_fb->bits_per_pixel) { + switch (target_fb->format->cpp[0] * 8) { case 8: format = 2; break; @@ -476,10 +476,9 @@ retry: crtc_offset_cntl = 0; - pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8); - crtc_pitch = (((pitch_pixels * target_fb->bits_per_pixel) + - ((target_fb->bits_per_pixel * 8) - 1)) / - (target_fb->bits_per_pixel * 8)); + pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; + crtc_pitch = DIV_ROUND_UP(pitch_pixels * target_fb->format->cpp[0] * 8, + target_fb->format->cpp[0] * 8 * 8); crtc_pitch |= crtc_pitch << 16; crtc_offset_cntl |= RADEON_CRTC_GUI_TRIG_OFFSET_LEFT_EN; @@ -504,14 +503,14 @@ retry: crtc_tile_x0_y0 = x | (y << 16); base &= ~0x7ff; } else { - int byteshift = target_fb->bits_per_pixel >> 4; + int byteshift = target_fb->format->cpp[0] * 8 >> 4; int tile_addr = (((y >> 3) * pitch_pixels + x) >> (8 - byteshift)) << 11; base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); crtc_offset_cntl |= (y % 16); } } else { int offset = y * pitch_pixels + x; - switch (target_fb->bits_per_pixel) { + switch (target_fb->format->cpp[0] * 8) { case 8: offset *= 1; break; @@ -579,6 +578,7 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod struct drm_device *dev = crtc->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + const struct drm_framebuffer *fb = crtc->primary->fb; struct drm_encoder *encoder; int format; int hsync_start; @@ -602,7 +602,7 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod } } - switch (crtc->primary->fb->bits_per_pixel) { + switch (fb->format->cpp[0] * 8) { case 8: format = 2; break; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index f1da484864a9..ad282648fc8b 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -32,6 +32,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_dp_helper.h> #include <drm/drm_dp_mst_helper.h> #include <drm/drm_fixed.h> diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 41b72ce6613f..74b276060c20 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -765,6 +765,7 @@ int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved, } void radeon_bo_move_notify(struct ttm_buffer_object *bo, + bool evict, struct ttm_mem_reg *new_mem) { struct radeon_bo *rbo; diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index a10bb3deee54..9ffd8215d38a 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -150,6 +150,7 @@ extern void radeon_bo_get_tiling_flags(struct radeon_bo *bo, extern int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved, bool force_drop); extern void radeon_bo_move_notify(struct ttm_buffer_object *bo, + bool evict, struct ttm_mem_reg *new_mem); extern int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo); extern int radeon_bo_get_surface_reg(struct radeon_bo *bo); diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 0cf03ccbf0a7..684f1703aa5c 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -502,6 +502,8 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ mem->bus.addr = ioremap_nocache(mem->bus.base + mem->bus.offset, mem->bus.size); + if (!mem->bus.addr) + return -ENOMEM; /* * Alpha: Use just the bus offset plus @@ -871,8 +873,6 @@ static struct ttm_bo_driver radeon_bo_driver = { .fault_reserve_notify = &radeon_bo_fault_reserve_notify, .io_mem_reserve = &radeon_ttm_io_mem_reserve, .io_mem_free = &radeon_ttm_io_mem_free, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int radeon_ttm_init(struct radeon_device *rdev) @@ -979,19 +979,19 @@ void radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size) static struct vm_operations_struct radeon_ttm_vm_ops; static const struct vm_operations_struct *ttm_vm_ops = NULL; -static int radeon_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int radeon_ttm_fault(struct vm_fault *vmf) { struct ttm_buffer_object *bo; struct radeon_device *rdev; int r; - bo = (struct ttm_buffer_object *)vma->vm_private_data; + bo = (struct ttm_buffer_object *)vmf->vma->vm_private_data; if (bo == NULL) { return VM_FAULT_NOPAGE; } rdev = radeon_get_rdev(bo->bdev); down_read(&rdev->pm.mclk_lock); - r = ttm_vm_ops->fault(vma, vmf); + r = ttm_vm_ops->fault(vmf); up_read(&rdev->pm.mclk_lock); return r; } @@ -1033,13 +1033,13 @@ static int radeon_mm_dump_table(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct radeon_device *rdev = dev->dev_private; struct drm_mm *mm = (struct drm_mm *)rdev->mman.bdev.man[ttm_pl].priv; - int ret; struct ttm_bo_global *glob = rdev->mman.bdev.glob; + struct drm_printer p = drm_seq_file_printer(m); spin_lock(&glob->lru_lock); - ret = drm_mm_dump_table(m, mm); + drm_mm_print(mm, &p); spin_unlock(&glob->lru_lock); - return ret; + return 0; } static int ttm_pl_vram = TTM_PL_VRAM; diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 2944916f7102..d12b8978142f 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2912,29 +2912,6 @@ static int si_init_smc_spll_table(struct radeon_device *rdev) return ret; } -struct si_dpm_quirk { - u32 chip_vendor; - u32 chip_device; - u32 subsys_vendor; - u32 subsys_device; - u32 max_sclk; - u32 max_mclk; -}; - -/* cards with dpm stability problems */ -static struct si_dpm_quirk si_dpm_quirk_list[] = { - /* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */ - { PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6810, 0x174b, 0xe271, 85000, 90000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x1462, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x1043, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6811, 0x148c, 0x2015, 0, 120000 }, - { PCI_VENDOR_ID_ATI, 0x6810, 0x1682, 0x9275, 0, 120000 }, - { 0, 0, 0, 0 }, -}; - static u16 si_get_lower_of_leakage_and_vce_voltage(struct radeon_device *rdev, u16 vce_voltage) { @@ -2997,18 +2974,8 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; u32 max_sclk = 0, max_mclk = 0; int i; - struct si_dpm_quirk *p = si_dpm_quirk_list; - /* limit all SI kickers */ - if (rdev->family == CHIP_PITCAIRN) { - if ((rdev->pdev->revision == 0x81) || - (rdev->pdev->device == 0x6810) || - (rdev->pdev->device == 0x6811) || - (rdev->pdev->device == 0x6816) || - (rdev->pdev->device == 0x6817) || - (rdev->pdev->device == 0x6806)) - max_mclk = 120000; - } else if (rdev->family == CHIP_HAINAN) { + if (rdev->family == CHIP_HAINAN) { if ((rdev->pdev->revision == 0x81) || (rdev->pdev->revision == 0x83) || (rdev->pdev->revision == 0xC3) || @@ -3018,18 +2985,6 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, max_sclk = 75000; } } - /* Apply dpm quirks */ - while (p && p->chip_device != 0) { - if (rdev->pdev->vendor == p->chip_vendor && - rdev->pdev->device == p->chip_device && - rdev->pdev->subsystem_vendor == p->subsys_vendor && - rdev->pdev->subsystem_device == p->subsys_device) { - max_sclk = p->max_sclk; - max_mclk = p->max_mclk; - break; - } - ++p; - } if (rps->vce_active) { rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk; diff --git a/drivers/gpu/drm/radeon/vce_v1_0.c b/drivers/gpu/drm/radeon/vce_v1_0.c index a01efe39a820..f541a4b5ac51 100644 --- a/drivers/gpu/drm/radeon/vce_v1_0.c +++ b/drivers/gpu/drm/radeon/vce_v1_0.c @@ -196,7 +196,7 @@ int vce_v1_0_load_fw(struct radeon_device *rdev, uint32_t *data) memset(&data[5], 0, 44); memcpy(&data[16], &sign[1], rdev->vce_fw->size - sizeof(*sign)); - data += le32_to_cpu(data[4]) / 4; + data += (le32_to_cpu(sign->len) + 64) / 4; data[0] = sign->val[i].sigval[0]; data[1] = sign->val[i].sigval[1]; data[2] = sign->val[i].sigval[2]; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 7fc10a9c34c3..a050a3699857 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -15,6 +15,7 @@ #define __RCAR_DU_ENCODER_H__ #include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> struct rcar_du_device; struct rcar_du_hdmienc; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index f9515f53cc5b..c4c5d1abcff8 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -124,10 +124,7 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, hdmienc->renc = renc; /* Link the bridge to the encoder. */ - bridge->encoder = encoder; - encoder->bridge = bridge; - - ret = drm_bridge_attach(rcdu->ddev, bridge); + ret = drm_bridge_attach(encoder, bridge, NULL); if (ret) { drm_encoder_cleanup(encoder); return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index b5d3f16cfa12..ff61f6032f2c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -662,7 +662,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) drm_kms_helper_poll_init(dev); if (dev->mode_config.num_connector) { - fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, + fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_connector); if (IS_ERR(fbdev)) return PTR_ERR(fbdev); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index a74f8ed8ca2e..dcde6288da6c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -567,10 +567,10 @@ static int rcar_du_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } - rstate->format = rcar_du_format_info(state->fb->pixel_format); + rstate->format = rcar_du_format_info(state->fb->format->format); if (rstate->format == NULL) { dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, - state->fb->pixel_format); + state->fb->format->format); return -EINVAL; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index 83ebd162f3ef..b5bfbe50bd87 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -201,10 +201,10 @@ static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } - rstate->format = rcar_du_format_info(state->fb->pixel_format); + rstate->format = rcar_du_format_info(state->fb->format->format); if (rstate->format == NULL) { dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, - state->fb->pixel_format); + state->fb->format->format); return -EINVAL; } diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 6f7f9c59f05b..0e4eb845cbb0 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -21,6 +21,17 @@ config ROCKCHIP_ANALOGIX_DP for the Analogix Core DP driver. If you want to enable DP on RK3288 based SoC, you should selet this option. +config ROCKCHIP_CDN_DP + tristate "Rockchip cdn DP" + depends on DRM_ROCKCHIP + depends on EXTCON + select SND_SOC_HDMI_CODEC if SND_SOC + help + This selects support for Rockchip SoC specific extensions + for the cdn DP driver. If you want to enable Dp on + RK3399 based SoC, you should select this + option. + config ROCKCHIP_DW_HDMI tristate "Rockchip specific extensions for Synopsys DW HDMI" depends on DRM_ROCKCHIP diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 9746365694ba..c931e2a7d8de 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -7,6 +7,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o +obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp.o +cdn-dp-objs := cdn-dp-core.o cdn-dp-reg.o obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c new file mode 100644 index 000000000000..fd79a70b8552 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -0,0 +1,1262 @@ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <zyw@rock-chips.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_of.h> + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/extcon.h> +#include <linux/firmware.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/mfd/syscon.h> +#include <linux/phy/phy.h> + +#include <sound/hdmi-codec.h> + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" +#include "rockchip_drm_vop.h" + +#define connector_to_dp(c) \ + container_of(c, struct cdn_dp_device, connector) + +#define encoder_to_dp(c) \ + container_of(c, struct cdn_dp_device, encoder) + +#define GRF_SOC_CON9 0x6224 +#define DP_SEL_VOP_LIT BIT(12) +#define GRF_SOC_CON26 0x6268 +#define UPHY_SEL_BIT 3 +#define UPHY_SEL_MASK BIT(19) +#define DPTX_HPD_SEL (3 << 12) +#define DPTX_HPD_DEL (2 << 12) +#define DPTX_HPD_SEL_MASK (3 << 28) + +#define CDN_FW_TIMEOUT_MS (64 * 1000) +#define CDN_DPCD_TIMEOUT_MS 5000 +#define CDN_DP_FIRMWARE "rockchip/dptx.bin" + +struct cdn_dp_data { + u8 max_phy; +}; + +struct cdn_dp_data rk3399_cdn_dp = { + .max_phy = 2, +}; + +static const struct of_device_id cdn_dp_dt_ids[] = { + { .compatible = "rockchip,rk3399-cdn-dp", + .data = (void *)&rk3399_cdn_dp }, + {} +}; + +MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids); + +static int cdn_dp_grf_write(struct cdn_dp_device *dp, + unsigned int reg, unsigned int val) +{ + int ret; + + ret = clk_prepare_enable(dp->grf_clk); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to prepare_enable grf clock\n"); + return ret; + } + + ret = regmap_write(dp->grf, reg, val); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret); + return ret; + } + + clk_disable_unprepare(dp->grf_clk); + + return 0; +} + +static int cdn_dp_clk_enable(struct cdn_dp_device *dp) +{ + int ret; + u32 rate; + + ret = clk_prepare_enable(dp->pclk); + if (ret < 0) { + DRM_DEV_ERROR(dp->dev, "cannot enable dp pclk %d\n", ret); + goto err_pclk; + } + + ret = clk_prepare_enable(dp->core_clk); + if (ret < 0) { + DRM_DEV_ERROR(dp->dev, "cannot enable core_clk %d\n", ret); + goto err_core_clk; + } + + ret = pm_runtime_get_sync(dp->dev); + if (ret < 0) { + DRM_DEV_ERROR(dp->dev, "cannot get pm runtime %d\n", ret); + goto err_pm_runtime_get; + } + + reset_control_assert(dp->core_rst); + reset_control_assert(dp->dptx_rst); + reset_control_assert(dp->apb_rst); + reset_control_deassert(dp->core_rst); + reset_control_deassert(dp->dptx_rst); + reset_control_deassert(dp->apb_rst); + + rate = clk_get_rate(dp->core_clk); + if (!rate) { + DRM_DEV_ERROR(dp->dev, "get clk rate failed: %d\n", rate); + goto err_set_rate; + } + + cdn_dp_set_fw_clk(dp, rate); + cdn_dp_clock_reset(dp); + + return 0; + +err_set_rate: + pm_runtime_put(dp->dev); +err_pm_runtime_get: + clk_disable_unprepare(dp->core_clk); +err_core_clk: + clk_disable_unprepare(dp->pclk); +err_pclk: + return ret; +} + +static void cdn_dp_clk_disable(struct cdn_dp_device *dp) +{ + pm_runtime_put_sync(dp->dev); + clk_disable_unprepare(dp->pclk); + clk_disable_unprepare(dp->core_clk); +} + +static int cdn_dp_get_port_lanes(struct cdn_dp_port *port) +{ + struct extcon_dev *edev = port->extcon; + union extcon_property_value property; + int dptx; + u8 lanes; + + dptx = extcon_get_state(edev, EXTCON_DISP_DP); + if (dptx > 0) { + extcon_get_property(edev, EXTCON_DISP_DP, + EXTCON_PROP_USB_SS, &property); + if (property.intval) + lanes = 2; + else + lanes = 4; + } else { + lanes = 0; + } + + return lanes; +} + +static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) +{ + int ret; + u8 value; + + *sink_count = 0; + ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1); + if (ret) + return ret; + + *sink_count = DP_GET_SINK_COUNT(value); + return 0; +} + +static struct cdn_dp_port *cdn_dp_connected_port(struct cdn_dp_device *dp) +{ + struct cdn_dp_port *port; + int i, lanes; + + for (i = 0; i < dp->ports; i++) { + port = dp->port[i]; + lanes = cdn_dp_get_port_lanes(port); + if (lanes) + return port; + } + return NULL; +} + +static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(CDN_DPCD_TIMEOUT_MS); + struct cdn_dp_port *port; + u8 sink_count = 0; + + if (dp->active_port < 0 || dp->active_port >= dp->ports) { + DRM_DEV_ERROR(dp->dev, "active_port is wrong!\n"); + return false; + } + + port = dp->port[dp->active_port]; + + /* + * Attempt to read sink count, retry in case the sink may not be ready. + * + * Sinks are *supposed* to come up within 1ms from an off state, but + * some docks need more time to power up. + */ + while (time_before(jiffies, timeout)) { + if (!extcon_get_state(port->extcon, EXTCON_DISP_DP)) + return false; + + if (!cdn_dp_get_sink_count(dp, &sink_count)) + return sink_count ? true : false; + + usleep_range(5000, 10000); + } + + DRM_DEV_ERROR(dp->dev, "Get sink capability timed out\n"); + return false; +} + +static enum drm_connector_status +cdn_dp_connector_detect(struct drm_connector *connector, bool force) +{ + struct cdn_dp_device *dp = connector_to_dp(connector); + enum drm_connector_status status = connector_status_disconnected; + + mutex_lock(&dp->lock); + if (dp->connected) + status = connector_status_connected; + mutex_unlock(&dp->lock); + + return status; +} + +static void cdn_dp_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs cdn_dp_atomic_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = cdn_dp_connector_detect, + .destroy = cdn_dp_connector_destroy, + .fill_modes = drm_helper_probe_single_connector_modes, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int cdn_dp_connector_get_modes(struct drm_connector *connector) +{ + struct cdn_dp_device *dp = connector_to_dp(connector); + struct edid *edid; + int ret = 0; + + mutex_lock(&dp->lock); + edid = dp->edid; + if (edid) { + DRM_DEV_DEBUG_KMS(dp->dev, "got edid: width[%d] x height[%d]\n", + edid->width_cm, edid->height_cm); + + dp->sink_has_audio = drm_detect_monitor_audio(edid); + ret = drm_add_edid_modes(connector, edid); + if (ret) { + drm_mode_connector_update_edid_property(connector, + edid); + drm_edid_to_eld(connector, edid); + } + } + mutex_unlock(&dp->lock); + + return ret; +} + +static struct drm_encoder * +cdn_dp_connector_best_encoder(struct drm_connector *connector) +{ + struct cdn_dp_device *dp = connector_to_dp(connector); + + return &dp->encoder; +} + +static int cdn_dp_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct cdn_dp_device *dp = connector_to_dp(connector); + struct drm_display_info *display_info = &dp->connector.display_info; + u32 requested, actual, rate, sink_max, source_max = 0; + u8 lanes, bpc; + + /* If DP is disconnected, every mode is invalid */ + if (!dp->connected) + return MODE_BAD; + + switch (display_info->bpc) { + case 10: + bpc = 10; + break; + case 6: + bpc = 6; + break; + default: + bpc = 8; + break; + } + + requested = mode->clock * bpc * 3 / 1000; + + source_max = dp->lanes; + sink_max = drm_dp_max_lane_count(dp->dpcd); + lanes = min(source_max, sink_max); + + source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE); + sink_max = drm_dp_max_link_rate(dp->dpcd); + rate = min(source_max, sink_max); + + actual = rate * lanes / 100; + + /* efficiency is about 0.8 */ + actual = actual * 8 / 10; + + if (requested > actual) { + DRM_DEV_DEBUG_KMS(dp->dev, + "requested=%d, actual=%d, clock=%d\n", + requested, actual, mode->clock); + return MODE_CLOCK_HIGH; + } + + return MODE_OK; +} + +static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = { + .get_modes = cdn_dp_connector_get_modes, + .best_encoder = cdn_dp_connector_best_encoder, + .mode_valid = cdn_dp_connector_mode_valid, +}; + +static int cdn_dp_firmware_init(struct cdn_dp_device *dp) +{ + int ret; + const u32 *iram_data, *dram_data; + const struct firmware *fw = dp->fw; + const struct cdn_firmware_header *hdr; + + hdr = (struct cdn_firmware_header *)fw->data; + if (fw->size != le32_to_cpu(hdr->size_bytes)) { + DRM_DEV_ERROR(dp->dev, "firmware is invalid\n"); + return -EINVAL; + } + + iram_data = (const u32 *)(fw->data + hdr->header_size); + dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size); + + ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size, + dram_data, hdr->dram_size); + if (ret) + return ret; + + ret = cdn_dp_set_firmware_active(dp, true); + if (ret) { + DRM_DEV_ERROR(dp->dev, "active ucpu failed: %d\n", ret); + return ret; + } + + return cdn_dp_event_config(dp); +} + +static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) +{ + int ret; + + if (!cdn_dp_check_sink_connection(dp)) + return -ENODEV; + + ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, + DP_RECEIVER_CAP_SIZE); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); + return ret; + } + + kfree(dp->edid); + dp->edid = drm_do_get_edid(&dp->connector, + cdn_dp_get_edid_block, dp); + return 0; +} + +static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port) +{ + union extcon_property_value property; + int ret; + + ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, + (port->id << UPHY_SEL_BIT) | UPHY_SEL_MASK); + if (ret) + return ret; + + if (!port->phy_enabled) { + ret = phy_power_on(port->phy); + if (ret) { + DRM_DEV_ERROR(dp->dev, "phy power on failed: %d\n", + ret); + goto err_phy; + } + port->phy_enabled = true; + } + + ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, + DPTX_HPD_SEL_MASK | DPTX_HPD_SEL); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to write HPD_SEL %d\n", ret); + goto err_power_on; + } + + ret = cdn_dp_get_hpd_status(dp); + if (ret <= 0) { + if (!ret) + DRM_DEV_ERROR(dp->dev, "hpd does not exist\n"); + goto err_power_on; + } + + ret = extcon_get_property(port->extcon, EXTCON_DISP_DP, + EXTCON_PROP_USB_TYPEC_POLARITY, &property); + if (ret) { + DRM_DEV_ERROR(dp->dev, "get property failed\n"); + goto err_power_on; + } + + port->lanes = cdn_dp_get_port_lanes(port); + ret = cdn_dp_set_host_cap(dp, port->lanes, property.intval); + if (ret) { + DRM_DEV_ERROR(dp->dev, "set host capabilities failed: %d\n", + ret); + goto err_power_on; + } + + dp->active_port = port->id; + return 0; + +err_power_on: + if (phy_power_off(port->phy)) + DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret); + else + port->phy_enabled = false; + +err_phy: + cdn_dp_grf_write(dp, GRF_SOC_CON26, + DPTX_HPD_SEL_MASK | DPTX_HPD_DEL); + return ret; +} + +static int cdn_dp_disable_phy(struct cdn_dp_device *dp, + struct cdn_dp_port *port) +{ + int ret; + + if (port->phy_enabled) { + ret = phy_power_off(port->phy); + if (ret) { + DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret); + return ret; + } + } + + port->phy_enabled = false; + port->lanes = 0; + dp->active_port = -1; + return 0; +} + +static int cdn_dp_disable(struct cdn_dp_device *dp) +{ + int ret, i; + + if (!dp->active) + return 0; + + for (i = 0; i < dp->ports; i++) + cdn_dp_disable_phy(dp, dp->port[i]); + + ret = cdn_dp_grf_write(dp, GRF_SOC_CON26, + DPTX_HPD_SEL_MASK | DPTX_HPD_DEL); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to clear hpd sel %d\n", + ret); + return ret; + } + + cdn_dp_set_firmware_active(dp, false); + cdn_dp_clk_disable(dp); + dp->active = false; + dp->link.rate = 0; + dp->link.num_lanes = 0; + if (!dp->connected) { + kfree(dp->edid); + dp->edid = NULL; + } + + return 0; +} + +static int cdn_dp_enable(struct cdn_dp_device *dp) +{ + int ret, i, lanes; + struct cdn_dp_port *port; + + port = cdn_dp_connected_port(dp); + if (!port) { + DRM_DEV_ERROR(dp->dev, + "Can't enable without connection\n"); + return -ENODEV; + } + + if (dp->active) + return 0; + + ret = cdn_dp_clk_enable(dp); + if (ret) + return ret; + + ret = cdn_dp_firmware_init(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "firmware init failed: %d", ret); + goto err_clk_disable; + } + + /* only enable the port that connected with downstream device */ + for (i = port->id; i < dp->ports; i++) { + port = dp->port[i]; + lanes = cdn_dp_get_port_lanes(port); + if (lanes) { + ret = cdn_dp_enable_phy(dp, port); + if (ret) + continue; + + ret = cdn_dp_get_sink_capability(dp); + if (ret) { + cdn_dp_disable_phy(dp, port); + } else { + dp->active = true; + dp->lanes = port->lanes; + return 0; + } + } + } + +err_clk_disable: + cdn_dp_clk_disable(dp); + return ret; +} + +static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct cdn_dp_device *dp = encoder_to_dp(encoder); + struct drm_display_info *display_info = &dp->connector.display_info; + struct video_info *video = &dp->video_info; + + switch (display_info->bpc) { + case 10: + video->color_depth = 10; + break; + case 6: + video->color_depth = 6; + break; + default: + video->color_depth = 8; + break; + } + + video->color_fmt = PXL_RGB; + video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); + video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + + memcpy(&dp->mode, adjusted, sizeof(*mode)); +} + +static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) +{ + u8 link_status[DP_LINK_STATUS_SIZE]; + struct cdn_dp_port *port = cdn_dp_connected_port(dp); + u8 sink_lanes = drm_dp_max_lane_count(dp->dpcd); + + if (!port || !dp->link.rate || !dp->link.num_lanes) + return false; + + if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, + DP_LINK_STATUS_SIZE)) { + DRM_ERROR("Failed to get link status\n"); + return false; + } + + /* if link training is requested we should perform it always */ + return drm_dp_channel_eq_ok(link_status, min(port->lanes, sink_lanes)); +} + +static void cdn_dp_encoder_enable(struct drm_encoder *encoder) +{ + struct cdn_dp_device *dp = encoder_to_dp(encoder); + int ret, val; + struct rockchip_crtc_state *state; + + ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder); + if (ret < 0) { + DRM_DEV_ERROR(dp->dev, "Could not get vop id, %d", ret); + return; + } + + DRM_DEV_DEBUG_KMS(dp->dev, "vop %s output to cdn-dp\n", + (ret) ? "LIT" : "BIG"); + state = to_rockchip_crtc_state(encoder->crtc->state); + if (ret) { + val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16); + state->output_mode = ROCKCHIP_OUT_MODE_P888; + } else { + val = DP_SEL_VOP_LIT << 16; + state->output_mode = ROCKCHIP_OUT_MODE_AAAA; + } + + ret = cdn_dp_grf_write(dp, GRF_SOC_CON9, val); + if (ret) + return; + + mutex_lock(&dp->lock); + + ret = cdn_dp_enable(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to enable encoder %d\n", + ret); + goto out; + } + if (!cdn_dp_check_link_status(dp)) { + ret = cdn_dp_train_link(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed link train %d\n", ret); + goto out; + } + } + + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); + goto out; + } + + ret = cdn_dp_config_video(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to config video %d\n", ret); + goto out; + } + + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); + goto out; + } +out: + mutex_unlock(&dp->lock); +} + +static void cdn_dp_encoder_disable(struct drm_encoder *encoder) +{ + struct cdn_dp_device *dp = encoder_to_dp(encoder); + int ret; + + mutex_lock(&dp->lock); + if (dp->active) { + ret = cdn_dp_disable(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to disable encoder %d\n", + ret); + } + } + mutex_unlock(&dp->lock); + + /* + * In the following 2 cases, we need to run the event_work to re-enable + * the DP: + * 1. If there is not just one port device is connected, and remove one + * device from a port, the DP will be disabled here, at this case, + * run the event_work to re-open DP for the other port. + * 2. If re-training or re-config failed, the DP will be disabled here. + * run the event_work to re-connect it. + */ + if (!dp->connected && cdn_dp_connected_port(dp)) + schedule_work(&dp->event_work); +} + +static int cdn_dp_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + + s->output_mode = ROCKCHIP_OUT_MODE_AAAA; + s->output_type = DRM_MODE_CONNECTOR_DisplayPort; + + return 0; +} + +static const struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = { + .mode_set = cdn_dp_encoder_mode_set, + .enable = cdn_dp_encoder_enable, + .disable = cdn_dp_encoder_disable, + .atomic_check = cdn_dp_encoder_atomic_check, +}; + +static const struct drm_encoder_funcs cdn_dp_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int cdn_dp_parse_dt(struct cdn_dp_device *dp) +{ + struct device *dev = dp->dev; + struct device_node *np = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + + dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(dp->grf)) { + DRM_DEV_ERROR(dev, "cdn-dp needs rockchip,grf property\n"); + return PTR_ERR(dp->grf); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dp->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(dp->regs)) { + DRM_DEV_ERROR(dev, "ioremap reg failed\n"); + return PTR_ERR(dp->regs); + } + + dp->core_clk = devm_clk_get(dev, "core-clk"); + if (IS_ERR(dp->core_clk)) { + DRM_DEV_ERROR(dev, "cannot get core_clk_dp\n"); + return PTR_ERR(dp->core_clk); + } + + dp->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dp->pclk)) { + DRM_DEV_ERROR(dev, "cannot get pclk\n"); + return PTR_ERR(dp->pclk); + } + + dp->spdif_clk = devm_clk_get(dev, "spdif"); + if (IS_ERR(dp->spdif_clk)) { + DRM_DEV_ERROR(dev, "cannot get spdif_clk\n"); + return PTR_ERR(dp->spdif_clk); + } + + dp->grf_clk = devm_clk_get(dev, "grf"); + if (IS_ERR(dp->grf_clk)) { + DRM_DEV_ERROR(dev, "cannot get grf clk\n"); + return PTR_ERR(dp->grf_clk); + } + + dp->spdif_rst = devm_reset_control_get(dev, "spdif"); + if (IS_ERR(dp->spdif_rst)) { + DRM_DEV_ERROR(dev, "no spdif reset control found\n"); + return PTR_ERR(dp->spdif_rst); + } + + dp->dptx_rst = devm_reset_control_get(dev, "dptx"); + if (IS_ERR(dp->dptx_rst)) { + DRM_DEV_ERROR(dev, "no uphy reset control found\n"); + return PTR_ERR(dp->dptx_rst); + } + + dp->core_rst = devm_reset_control_get(dev, "core"); + if (IS_ERR(dp->core_rst)) { + DRM_DEV_ERROR(dev, "no core reset control found\n"); + return PTR_ERR(dp->core_rst); + } + + dp->apb_rst = devm_reset_control_get(dev, "apb"); + if (IS_ERR(dp->apb_rst)) { + DRM_DEV_ERROR(dev, "no apb reset control found\n"); + return PTR_ERR(dp->apb_rst); + } + + return 0; +} + +static int cdn_dp_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + struct audio_info audio = { + .sample_width = params->sample_width, + .sample_rate = params->sample_rate, + .channels = params->channels, + }; + int ret; + + mutex_lock(&dp->lock); + if (!dp->active) { + ret = -ENODEV; + goto out; + } + + switch (daifmt->fmt) { + case HDMI_I2S: + audio.format = AFMT_I2S; + break; + case HDMI_SPDIF: + audio.format = AFMT_SPDIF; + break; + default: + DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt); + ret = -EINVAL; + goto out; + } + + ret = cdn_dp_audio_config(dp, &audio); + if (!ret) + dp->audio_info = audio; + +out: + mutex_unlock(&dp->lock); + return ret; +} + +static void cdn_dp_audio_shutdown(struct device *dev, void *data) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + int ret; + + mutex_lock(&dp->lock); + if (!dp->active) + goto out; + + ret = cdn_dp_audio_stop(dp, &dp->audio_info); + if (!ret) + dp->audio_info.format = AFMT_UNUSED; +out: + mutex_unlock(&dp->lock); +} + +static int cdn_dp_audio_digital_mute(struct device *dev, void *data, + bool enable) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + int ret; + + mutex_lock(&dp->lock); + if (!dp->active) { + ret = -ENODEV; + goto out; + } + + ret = cdn_dp_audio_mute(dp, enable); + +out: + mutex_unlock(&dp->lock); + return ret; +} + +static int cdn_dp_audio_get_eld(struct device *dev, void *data, + u8 *buf, size_t len) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + + memcpy(buf, dp->connector.eld, min(sizeof(dp->connector.eld), len)); + + return 0; +} + +static const struct hdmi_codec_ops audio_codec_ops = { + .hw_params = cdn_dp_audio_hw_params, + .audio_shutdown = cdn_dp_audio_shutdown, + .digital_mute = cdn_dp_audio_digital_mute, + .get_eld = cdn_dp_audio_get_eld, +}; + +static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp, + struct device *dev) +{ + struct hdmi_codec_pdata codec_data = { + .i2s = 1, + .spdif = 1, + .ops = &audio_codec_ops, + .max_i2s_channels = 8, + }; + + dp->audio_pdev = platform_device_register_data( + dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, + &codec_data, sizeof(codec_data)); + + return PTR_ERR_OR_ZERO(dp->audio_pdev); +} + +static int cdn_dp_request_firmware(struct cdn_dp_device *dp) +{ + int ret; + unsigned long timeout = jiffies + msecs_to_jiffies(CDN_FW_TIMEOUT_MS); + unsigned long sleep = 1000; + + WARN_ON(!mutex_is_locked(&dp->lock)); + + if (dp->fw_loaded) + return 0; + + /* Drop the lock before getting the firmware to avoid blocking boot */ + mutex_unlock(&dp->lock); + + while (time_before(jiffies, timeout)) { + ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev); + if (ret == -ENOENT) { + msleep(sleep); + sleep *= 2; + continue; + } else if (ret) { + DRM_DEV_ERROR(dp->dev, + "failed to request firmware: %d\n", ret); + goto out; + } + + dp->fw_loaded = true; + ret = 0; + goto out; + } + + DRM_DEV_ERROR(dp->dev, "Timed out trying to load firmware\n"); + ret = -ETIMEDOUT; +out: + mutex_lock(&dp->lock); + return ret; +} + +static void cdn_dp_pd_event_work(struct work_struct *work) +{ + struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device, + event_work); + struct drm_connector *connector = &dp->connector; + enum drm_connector_status old_status; + + int ret; + + mutex_lock(&dp->lock); + + if (dp->suspended) + goto out; + + ret = cdn_dp_request_firmware(dp); + if (ret) + goto out; + + dp->connected = true; + + /* Not connected, notify userspace to disable the block */ + if (!cdn_dp_connected_port(dp)) { + DRM_DEV_INFO(dp->dev, "Not connected. Disabling cdn\n"); + dp->connected = false; + + /* Connected but not enabled, enable the block */ + } else if (!dp->active) { + DRM_DEV_INFO(dp->dev, "Connected, not enabled. Enabling cdn\n"); + ret = cdn_dp_enable(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Enable dp failed %d\n", ret); + dp->connected = false; + } + + /* Enabled and connected to a dongle without a sink, notify userspace */ + } else if (!cdn_dp_check_sink_connection(dp)) { + DRM_DEV_INFO(dp->dev, "Connected without sink. Assert hpd\n"); + dp->connected = false; + + /* Enabled and connected with a sink, re-train if requested */ + } else if (!cdn_dp_check_link_status(dp)) { + unsigned int rate = dp->link.rate; + unsigned int lanes = dp->link.num_lanes; + struct drm_display_mode *mode = &dp->mode; + + DRM_DEV_INFO(dp->dev, "Connected with sink. Re-train link\n"); + ret = cdn_dp_train_link(dp); + if (ret) { + dp->connected = false; + DRM_DEV_ERROR(dp->dev, "Train link failed %d\n", ret); + goto out; + } + + /* If training result is changed, update the video config */ + if (mode->clock && + (rate != dp->link.rate || lanes != dp->link.num_lanes)) { + ret = cdn_dp_config_video(dp); + if (ret) { + dp->connected = false; + DRM_DEV_ERROR(dp->dev, + "Failed to config video %d\n", + ret); + } + } + } + +out: + mutex_unlock(&dp->lock); + + old_status = connector->status; + connector->status = connector->funcs->detect(connector, false); + if (old_status != connector->status) + drm_kms_helper_hotplug_event(dp->drm_dev); +} + +static int cdn_dp_pd_event(struct notifier_block *nb, + unsigned long event, void *priv) +{ + struct cdn_dp_port *port = container_of(nb, struct cdn_dp_port, + event_nb); + struct cdn_dp_device *dp = port->dp; + + /* + * It would be nice to be able to just do the work inline right here. + * However, we need to make a bunch of calls that might sleep in order + * to turn on the block/phy, so use a worker instead. + */ + schedule_work(&dp->event_work); + + return NOTIFY_DONE; +} + +static int cdn_dp_bind(struct device *dev, struct device *master, void *data) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + struct drm_encoder *encoder; + struct drm_connector *connector; + struct cdn_dp_port *port; + struct drm_device *drm_dev = data; + int ret, i; + + ret = cdn_dp_parse_dt(dp); + if (ret < 0) + return ret; + + dp->drm_dev = drm_dev; + dp->connected = false; + dp->active = false; + dp->active_port = -1; + + INIT_WORK(&dp->event_work, cdn_dp_pd_event_work); + + encoder = &dp->encoder; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, + dev->of_node); + DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + + ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + if (ret) { + DRM_ERROR("failed to initialize encoder with drm\n"); + return ret; + } + + drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs); + + connector = &dp->connector; + connector->polled = DRM_CONNECTOR_POLL_HPD; + connector->dpms = DRM_MODE_DPMS_OFF; + + ret = drm_connector_init(drm_dev, connector, + &cdn_dp_atomic_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + DRM_ERROR("failed to initialize connector with drm\n"); + goto err_free_encoder; + } + + drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs); + + ret = drm_mode_connector_attach_encoder(connector, encoder); + if (ret) { + DRM_ERROR("failed to attach connector and encoder\n"); + goto err_free_connector; + } + + cdn_dp_audio_codec_init(dp, dev); + + for (i = 0; i < dp->ports; i++) { + port = dp->port[i]; + + port->event_nb.notifier_call = cdn_dp_pd_event; + ret = devm_extcon_register_notifier(dp->dev, port->extcon, + EXTCON_DISP_DP, + &port->event_nb); + if (ret) { + DRM_DEV_ERROR(dev, + "register EXTCON_DISP_DP notifier err\n"); + goto err_free_connector; + } + } + + pm_runtime_enable(dev); + + schedule_work(&dp->event_work); + + return 0; + +err_free_connector: + drm_connector_cleanup(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); + return ret; +} + +static void cdn_dp_unbind(struct device *dev, struct device *master, void *data) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + struct drm_encoder *encoder = &dp->encoder; + struct drm_connector *connector = &dp->connector; + + cancel_work_sync(&dp->event_work); + platform_device_unregister(dp->audio_pdev); + cdn_dp_encoder_disable(encoder); + encoder->funcs->destroy(encoder); + connector->funcs->destroy(connector); + + pm_runtime_disable(dev); + release_firmware(dp->fw); + kfree(dp->edid); + dp->edid = NULL; +} + +static const struct component_ops cdn_dp_component_ops = { + .bind = cdn_dp_bind, + .unbind = cdn_dp_unbind, +}; + +int cdn_dp_suspend(struct device *dev) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&dp->lock); + if (dp->active) + ret = cdn_dp_disable(dp); + dp->suspended = true; + mutex_unlock(&dp->lock); + + return ret; +} + +int cdn_dp_resume(struct device *dev) +{ + struct cdn_dp_device *dp = dev_get_drvdata(dev); + + mutex_lock(&dp->lock); + dp->suspended = false; + if (dp->fw_loaded) + schedule_work(&dp->event_work); + mutex_unlock(&dp->lock); + + return 0; +} + +static int cdn_dp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct cdn_dp_data *dp_data; + struct cdn_dp_port *port; + struct cdn_dp_device *dp; + struct extcon_dev *extcon; + struct phy *phy; + int i; + + dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; + dp->dev = dev; + + match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node); + dp_data = (struct cdn_dp_data *)match->data; + + for (i = 0; i < dp_data->max_phy; i++) { + extcon = extcon_get_edev_by_phandle(dev, i); + phy = devm_of_phy_get_by_index(dev, dev->of_node, i); + + if (PTR_ERR(extcon) == -EPROBE_DEFER || + PTR_ERR(phy) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (IS_ERR(extcon) || IS_ERR(phy)) + continue; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!dp) + return -ENOMEM; + + port->extcon = extcon; + port->phy = phy; + port->dp = dp; + port->id = i; + dp->port[dp->ports++] = port; + } + + if (!dp->ports) { + DRM_DEV_ERROR(dev, "missing extcon or phy\n"); + return -EINVAL; + } + + mutex_init(&dp->lock); + dev_set_drvdata(dev, dp); + + return component_add(dev, &cdn_dp_component_ops); +} + +static int cdn_dp_remove(struct platform_device *pdev) +{ + struct cdn_dp_device *dp = platform_get_drvdata(pdev); + + cdn_dp_suspend(dp->dev); + component_del(&pdev->dev, &cdn_dp_component_ops); + + return 0; +} + +static void cdn_dp_shutdown(struct platform_device *pdev) +{ + struct cdn_dp_device *dp = platform_get_drvdata(pdev); + + cdn_dp_suspend(dp->dev); +} + +static const struct dev_pm_ops cdn_dp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cdn_dp_suspend, + cdn_dp_resume) +}; + +static struct platform_driver cdn_dp_driver = { + .probe = cdn_dp_probe, + .remove = cdn_dp_remove, + .shutdown = cdn_dp_shutdown, + .driver = { + .name = "cdn-dp", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cdn_dp_dt_ids), + .pm = &cdn_dp_pm_ops, + }, +}; + +module_platform_driver(cdn_dp_driver); + +MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>"); +MODULE_DESCRIPTION("cdn DP Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h new file mode 100644 index 000000000000..f57e296401b8 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 Chris Zhong <zyw@rock-chips.com> + * Copyright (C) 2016 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CDN_DP_CORE_H +#define _CDN_DP_CORE_H + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_panel.h> +#include "rockchip_drm_drv.h" + +#define MAX_PHY 2 + +enum audio_format { + AFMT_I2S = 0, + AFMT_SPDIF = 1, + AFMT_UNUSED, +}; + +struct audio_info { + enum audio_format format; + int sample_rate; + int channels; + int sample_width; +}; + +enum vic_pxl_encoding_format { + PXL_RGB = 0x1, + YCBCR_4_4_4 = 0x2, + YCBCR_4_2_2 = 0x4, + YCBCR_4_2_0 = 0x8, + Y_ONLY = 0x10, +}; + +struct video_info { + bool h_sync_polarity; + bool v_sync_polarity; + bool interlaced; + int color_depth; + enum vic_pxl_encoding_format color_fmt; +}; + +struct cdn_firmware_header { + u32 size_bytes; /* size of the entire header+image(s) in bytes */ + u32 header_size; /* size of just the header in bytes */ + u32 iram_size; /* size of iram */ + u32 dram_size; /* size of dram */ +}; + +struct cdn_dp_port { + struct cdn_dp_device *dp; + struct notifier_block event_nb; + struct extcon_dev *extcon; + struct phy *phy; + u8 lanes; + bool phy_enabled; + u8 id; +}; + +struct cdn_dp_device { + struct device *dev; + struct drm_device *drm_dev; + struct drm_connector connector; + struct drm_encoder encoder; + struct drm_display_mode mode; + struct platform_device *audio_pdev; + struct work_struct event_work; + struct edid *edid; + + struct mutex lock; + bool connected; + bool active; + bool suspended; + + const struct firmware *fw; /* cdn dp firmware */ + unsigned int fw_version; /* cdn fw version */ + bool fw_loaded; + + void __iomem *regs; + struct regmap *grf; + struct clk *core_clk; + struct clk *pclk; + struct clk *spdif_clk; + struct clk *grf_clk; + struct reset_control *spdif_rst; + struct reset_control *dptx_rst; + struct reset_control *apb_rst; + struct reset_control *core_rst; + struct audio_info audio_info; + struct video_info video_info; + struct drm_dp_link link; + struct cdn_dp_port *port[MAX_PHY]; + u8 ports; + u8 lanes; + int active_port; + + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + bool sink_has_audio; +}; +#endif /* _CDN_DP_CORE_H */ diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c new file mode 100644 index 000000000000..319dbbaa3609 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -0,0 +1,979 @@ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <zyw@rock-chips.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/reset.h> + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +#define CDN_DP_SPDIF_CLK 200000000 +#define FW_ALIVE_TIMEOUT_US 1000000 +#define MAILBOX_RETRY_US 1000 +#define MAILBOX_TIMEOUT_US 5000000 +#define LINK_TRAINING_RETRY_MS 20 +#define LINK_TRAINING_TIMEOUT_MS 500 + +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk) +{ + writel(clk / 1000000, dp->regs + SW_CLK_H); +} + +void cdn_dp_clock_reset(struct cdn_dp_device *dp) +{ + u32 val; + + val = DPTX_FRMR_DATA_CLK_RSTN_EN | + DPTX_FRMR_DATA_CLK_EN | + DPTX_PHY_DATA_RSTN_EN | + DPTX_PHY_DATA_CLK_EN | + DPTX_PHY_CHAR_RSTN_EN | + DPTX_PHY_CHAR_CLK_EN | + SOURCE_AUX_SYS_CLK_RSTN_EN | + SOURCE_AUX_SYS_CLK_EN | + DPTX_SYS_CLK_RSTN_EN | + DPTX_SYS_CLK_EN | + CFG_DPTX_VIF_CLK_RSTN_EN | + CFG_DPTX_VIF_CLK_EN; + writel(val, dp->regs + SOURCE_DPTX_CAR); + + val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN; + writel(val, dp->regs + SOURCE_PHY_CAR); + + val = SOURCE_PKT_SYS_RSTN_EN | + SOURCE_PKT_SYS_CLK_EN | + SOURCE_PKT_DATA_RSTN_EN | + SOURCE_PKT_DATA_CLK_EN; + writel(val, dp->regs + SOURCE_PKT_CAR); + + val = SPDIF_CDR_CLK_RSTN_EN | + SPDIF_CDR_CLK_EN | + SOURCE_AIF_SYS_RSTN_EN | + SOURCE_AIF_SYS_CLK_EN | + SOURCE_AIF_CLK_RSTN_EN | + SOURCE_AIF_CLK_EN; + writel(val, dp->regs + SOURCE_AIF_CAR); + + val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN | + SOURCE_CIPHER_SYS_CLK_EN | + SOURCE_CIPHER_CHAR_CLK_RSTN_EN | + SOURCE_CIPHER_CHAR_CLK_EN; + writel(val, dp->regs + SOURCE_CIPHER_CAR); + + val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN | + SOURCE_CRYPTO_SYS_CLK_EN; + writel(val, dp->regs + SOURCE_CRYPTO_CAR); + + /* enable Mailbox and PIF interrupt */ + writel(0, dp->regs + APB_INT_MASK); +} + +static int cdn_dp_mailbox_read(struct cdn_dp_device *dp) +{ + int val, ret; + + ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR, + val, !val, MAILBOX_RETRY_US, + MAILBOX_TIMEOUT_US); + if (ret < 0) + return ret; + + return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff; +} + +static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val) +{ + int ret, full; + + ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR, + full, !full, MAILBOX_RETRY_US, + MAILBOX_TIMEOUT_US); + if (ret < 0) + return ret; + + writel(val, dp->regs + MAILBOX0_WR_DATA); + + return 0; +} + +static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, + u8 module_id, u8 opcode, + u8 req_size) +{ + u32 mbox_size, i; + u8 header[4]; + int ret; + + /* read the header of the message */ + for (i = 0; i < 4; i++) { + ret = cdn_dp_mailbox_read(dp); + if (ret < 0) + return ret; + + header[i] = ret; + } + + mbox_size = (header[2] << 8) | header[3]; + + if (opcode != header[0] || module_id != header[1] || + req_size != mbox_size) { + /* + * If the message in mailbox is not what we want, we need to + * clear the mailbox by reading its contents. + */ + for (i = 0; i < mbox_size; i++) + if (cdn_dp_mailbox_read(dp) < 0) + break; + + return -EINVAL; + } + + return 0; +} + +static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp, + u8 *buff, u8 buff_size) +{ + u32 i; + int ret; + + for (i = 0; i < buff_size; i++) { + ret = cdn_dp_mailbox_read(dp); + if (ret < 0) + return ret; + + buff[i] = ret; + } + + return 0; +} + +static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id, + u8 opcode, u16 size, u8 *message) +{ + u8 header[4]; + int ret, i; + + header[0] = opcode; + header[1] = module_id; + header[2] = (size >> 8) & 0xff; + header[3] = size & 0xff; + + for (i = 0; i < 4; i++) { + ret = cdp_dp_mailbox_write(dp, header[i]); + if (ret) + return ret; + } + + for (i = 0; i < size; i++) { + ret = cdp_dp_mailbox_write(dp, message[i]); + if (ret) + return ret; + } + + return 0; +} + +static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val) +{ + u8 msg[6]; + + msg[0] = (addr >> 8) & 0xff; + msg[1] = addr & 0xff; + msg[2] = (val >> 24) & 0xff; + msg[3] = (val >> 16) & 0xff; + msg[4] = (val >> 8) & 0xff; + msg[5] = val & 0xff; + return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER, + sizeof(msg), msg); +} + +static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, + u8 start_bit, u8 bits_no, u32 val) +{ + u8 field[8]; + + field[0] = (addr >> 8) & 0xff; + field[1] = addr & 0xff; + field[2] = start_bit; + field[3] = bits_no; + field[4] = (val >> 24) & 0xff; + field[5] = (val >> 16) & 0xff; + field[6] = (val >> 8) & 0xff; + field[7] = val & 0xff; + + return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD, + sizeof(field), field); +} + +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len) +{ + u8 msg[5], reg[5]; + int ret; + + msg[0] = (len >> 8) & 0xff; + msg[1] = len & 0xff; + msg[2] = (addr >> 16) & 0xff; + msg[3] = (addr >> 8) & 0xff; + msg[4] = addr & 0xff; + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD, + sizeof(msg), msg); + if (ret) + goto err_dpcd_read; + + ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, + DPTX_READ_DPCD, + sizeof(reg) + len); + if (ret) + goto err_dpcd_read; + + ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); + if (ret) + goto err_dpcd_read; + + ret = cdn_dp_mailbox_read_receive(dp, data, len); + +err_dpcd_read: + return ret; +} + +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value) +{ + u8 msg[6], reg[5]; + int ret; + + msg[0] = 0; + msg[1] = 1; + msg[2] = (addr >> 16) & 0xff; + msg[3] = (addr >> 8) & 0xff; + msg[4] = addr & 0xff; + msg[5] = value; + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD, + sizeof(msg), msg); + if (ret) + goto err_dpcd_write; + + ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, + DPTX_WRITE_DPCD, sizeof(reg)); + if (ret) + goto err_dpcd_write; + + ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); + if (ret) + goto err_dpcd_write; + + if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) + ret = -EINVAL; + +err_dpcd_write: + if (ret) + DRM_DEV_ERROR(dp->dev, "dpcd write failed: %d\n", ret); + return ret; +} + +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, + u32 i_size, const u32 *d_mem, u32 d_size) +{ + u32 reg; + int i, ret; + + /* reset ucpu before load firmware*/ + writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET, + dp->regs + APB_CTRL); + + for (i = 0; i < i_size; i += 4) + writel(*i_mem++, dp->regs + ADDR_IMEM + i); + + for (i = 0; i < d_size; i += 4) + writel(*d_mem++, dp->regs + ADDR_DMEM + i); + + /* un-reset ucpu */ + writel(0, dp->regs + APB_CTRL); + + /* check the keep alive register to make sure fw working */ + ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE, + reg, reg, 2000, FW_ALIVE_TIMEOUT_US); + if (ret < 0) { + DRM_DEV_ERROR(dp->dev, "failed to loaded the FW reg = %x\n", + reg); + return -EINVAL; + } + + reg = readl(dp->regs + VER_L) & 0xff; + dp->fw_version = reg; + reg = readl(dp->regs + VER_H) & 0xff; + dp->fw_version |= reg << 8; + reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff; + dp->fw_version |= reg << 16; + reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff; + dp->fw_version |= reg << 24; + + dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version); + + return 0; +} + +int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable) +{ + u8 msg[5]; + int ret, i; + + msg[0] = GENERAL_MAIN_CONTROL; + msg[1] = MB_MODULE_ID_GENERAL; + msg[2] = 0; + msg[3] = 1; + msg[4] = enable ? FW_ACTIVE : FW_STANDBY; + + for (i = 0; i < sizeof(msg); i++) { + ret = cdp_dp_mailbox_write(dp, msg[i]); + if (ret) + goto err_set_firmware_active; + } + + /* read the firmware state */ + for (i = 0; i < sizeof(msg); i++) { + ret = cdn_dp_mailbox_read(dp); + if (ret < 0) + goto err_set_firmware_active; + + msg[i] = ret; + } + + ret = 0; + +err_set_firmware_active: + if (ret < 0) + DRM_DEV_ERROR(dp->dev, "set firmware active failed\n"); + return ret; +} + +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip) +{ + u8 msg[8]; + int ret; + + msg[0] = CDN_DP_MAX_LINK_RATE; + msg[1] = lanes | SCRAMBLER_EN; + msg[2] = VOLTAGE_LEVEL_2; + msg[3] = PRE_EMPHASIS_LEVEL_3; + msg[4] = PTS1 | PTS2 | PTS3 | PTS4; + msg[5] = FAST_LT_NOT_SUPPORT; + msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL; + msg[7] = ENHANCED; + + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, + DPTX_SET_HOST_CAPABILITIES, + sizeof(msg), msg); + if (ret) + goto err_set_host_cap; + + ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL, + AUX_HOST_INVERT); + +err_set_host_cap: + if (ret) + DRM_DEV_ERROR(dp->dev, "set host cap failed: %d\n", ret); + return ret; +} + +int cdn_dp_event_config(struct cdn_dp_device *dp) +{ + u8 msg[5]; + int ret; + + memset(msg, 0, sizeof(msg)); + + msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING; + + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT, + sizeof(msg), msg); + if (ret) + DRM_DEV_ERROR(dp->dev, "set event config failed: %d\n", ret); + + return ret; +} + +u32 cdn_dp_get_event(struct cdn_dp_device *dp) +{ + return readl(dp->regs + SW_EVENTS0); +} + +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp) +{ + u8 status; + int ret; + + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE, + 0, NULL); + if (ret) + goto err_get_hpd; + + ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, + DPTX_HPD_STATE, sizeof(status)); + if (ret) + goto err_get_hpd; + + ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status)); + if (ret) + goto err_get_hpd; + + return status; + +err_get_hpd: + DRM_DEV_ERROR(dp->dev, "get hpd status failed: %d\n", ret); + return ret; +} + +int cdn_dp_get_edid_block(void *data, u8 *edid, + unsigned int block, size_t length) +{ + struct cdn_dp_device *dp = data; + u8 msg[2], reg[2], i; + int ret; + + for (i = 0; i < 4; i++) { + msg[0] = block / 2; + msg[1] = block % 2; + + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID, + sizeof(msg), msg); + if (ret) + continue; + + ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, + DPTX_GET_EDID, + sizeof(reg) + length); + if (ret) + continue; + + ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg)); + if (ret) + continue; + + ret = cdn_dp_mailbox_read_receive(dp, edid, length); + if (ret) + continue; + + if (reg[0] == length && reg[1] == block / 2) + break; + } + + if (ret) + DRM_DEV_ERROR(dp->dev, "get block[%d] edid failed: %d\n", block, + ret); + + return ret; +} + +static int cdn_dp_training_start(struct cdn_dp_device *dp) +{ + unsigned long timeout; + u8 msg, event[2]; + int ret; + + msg = LINK_TRAINING_RUN; + + /* start training */ + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL, + sizeof(msg), &msg); + if (ret) + goto err_training_start; + + timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); + while (time_before(jiffies, timeout)) { + msleep(LINK_TRAINING_RETRY_MS); + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, + DPTX_READ_EVENT, 0, NULL); + if (ret) + goto err_training_start; + + ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, + DPTX_READ_EVENT, + sizeof(event)); + if (ret) + goto err_training_start; + + ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event)); + if (ret) + goto err_training_start; + + if (event[1] & EQ_PHASE_FINISHED) + return 0; + } + + ret = -ETIMEDOUT; + +err_training_start: + DRM_DEV_ERROR(dp->dev, "training failed: %d\n", ret); + return ret; +} + +static int cdn_dp_get_training_status(struct cdn_dp_device *dp) +{ + u8 status[10]; + int ret; + + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT, + 0, NULL); + if (ret) + goto err_get_training_status; + + ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX, + DPTX_READ_LINK_STAT, + sizeof(status)); + if (ret) + goto err_get_training_status; + + ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status)); + if (ret) + goto err_get_training_status; + + dp->link.rate = status[0]; + dp->link.num_lanes = status[1]; + +err_get_training_status: + if (ret) + DRM_DEV_ERROR(dp->dev, "get training status failed: %d\n", ret); + return ret; +} + +int cdn_dp_train_link(struct cdn_dp_device *dp) +{ + int ret; + + ret = cdn_dp_training_start(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret); + return ret; + } + + ret = cdn_dp_get_training_status(dp); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret); + return ret; + } + + DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate, + dp->link.num_lanes); + return ret; +} + +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active) +{ + u8 msg; + int ret; + + msg = !!active; + + ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO, + sizeof(msg), &msg); + if (ret) + DRM_DEV_ERROR(dp->dev, "set video status failed: %d\n", ret); + + return ret; +} + +static int cdn_dp_get_msa_misc(struct video_info *video, + struct drm_display_mode *mode) +{ + u32 msa_misc; + u8 val[2] = {0}; + + switch (video->color_fmt) { + case PXL_RGB: + case Y_ONLY: + val[0] = 0; + break; + /* set YUV default color space conversion to BT601 */ + case YCBCR_4_4_4: + val[0] = 6 + BT_601 * 8; + break; + case YCBCR_4_2_2: + val[0] = 5 + BT_601 * 8; + break; + case YCBCR_4_2_0: + val[0] = 5; + break; + }; + + switch (video->color_depth) { + case 6: + val[1] = 0; + break; + case 8: + val[1] = 1; + break; + case 10: + val[1] = 2; + break; + case 12: + val[1] = 3; + break; + case 16: + val[1] = 4; + break; + }; + + msa_misc = 2 * val[0] + 32 * val[1] + + ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0); + + return msa_misc; +} + +int cdn_dp_config_video(struct cdn_dp_device *dp) +{ + struct video_info *video = &dp->video_info; + struct drm_display_mode *mode = &dp->mode; + u64 symbol; + u32 val, link_rate, rem; + u8 bit_per_pix, tu_size_reg = TU_SIZE; + int ret; + + bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ? + (video->color_depth * 2) : (video->color_depth * 3); + + link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate) / 1000; + + ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE); + if (ret) + goto err_config_video; + + ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0); + if (ret) + goto err_config_video; + + /* + * get a best tu_size and valid symbol: + * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 + * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) + * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set + * TU += 2 and repeat 2nd step. + */ + do { + tu_size_reg += 2; + symbol = tu_size_reg * mode->clock * bit_per_pix; + do_div(symbol, dp->link.num_lanes * link_rate * 8); + rem = do_div(symbol, 1000); + if (tu_size_reg > 64) { + ret = -EINVAL; + goto err_config_video; + } + } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || + (rem > 850) || (rem < 100)); + + val = symbol + (tu_size_reg << 8); + val |= TU_CNT_RST_EN; + ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val); + if (ret) + goto err_config_video; + + /* set the FIFO Buffer size */ + val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; + val /= (dp->link.num_lanes * link_rate); + val = div_u64(8 * (symbol + 1), bit_per_pix) - val; + val += 2; + ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val); + + switch (video->color_depth) { + case 6: + val = BCS_6; + break; + case 8: + val = BCS_8; + break; + case 10: + val = BCS_10; + break; + case 12: + val = BCS_12; + break; + case 16: + val = BCS_16; + break; + }; + + val += video->color_fmt << 8; + ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val); + if (ret) + goto err_config_video; + + val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0; + val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0; + ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val); + if (ret) + goto err_config_video; + + val = (mode->hsync_start - mode->hdisplay) << 16; + val |= mode->htotal - mode->hsync_end; + ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val); + if (ret) + goto err_config_video; + + val = mode->hdisplay * bit_per_pix / 8; + ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val); + if (ret) + goto err_config_video; + + val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); + ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val); + if (ret) + goto err_config_video; + + val = mode->hsync_end - mode->hsync_start; + val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15); + ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val); + if (ret) + goto err_config_video; + + val = mode->vtotal; + val |= (mode->vtotal - mode->vsync_start) << 16; + ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val); + if (ret) + goto err_config_video; + + val = mode->vsync_end - mode->vsync_start; + val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15); + ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val); + if (ret) + goto err_config_video; + + val = cdn_dp_get_msa_misc(video, mode); + ret = cdn_dp_reg_write(dp, MSA_MISC, val); + if (ret) + goto err_config_video; + + ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1); + if (ret) + goto err_config_video; + + val = mode->hsync_end - mode->hsync_start; + val |= mode->hdisplay << 16; + ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val); + if (ret) + goto err_config_video; + + val = mode->vdisplay; + val |= (mode->vtotal - mode->vsync_start) << 16; + ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val); + if (ret) + goto err_config_video; + + val = mode->vtotal; + ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val); + if (ret) + goto err_config_video; + + ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, 0); + +err_config_video: + if (ret) + DRM_DEV_ERROR(dp->dev, "config video failed: %d\n", ret); + return ret; +} + +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio) +{ + u32 val; + int ret; + + ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0); + if (ret) { + DRM_DEV_ERROR(dp->dev, "audio stop failed: %d\n", ret); + return ret; + } + + val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; + val |= SPDIF_FIFO_MID_RANGE(0xe0); + val |= SPDIF_JITTER_THRSH(0xe0); + val |= SPDIF_JITTER_AVG_WIN(7); + writel(val, dp->regs + SPDIF_CTRL_ADDR); + + /* clearn the audio config and reset */ + writel(0, dp->regs + AUDIO_SRC_CNTL); + writel(0, dp->regs + AUDIO_SRC_CNFG); + writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL); + writel(0, dp->regs + AUDIO_SRC_CNTL); + + /* reset smpl2pckt component */ + writel(0, dp->regs + SMPL2PKT_CNTL); + writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL); + writel(0, dp->regs + SMPL2PKT_CNTL); + + /* reset FIFO */ + writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL); + writel(0, dp->regs + FIFO_CNTL); + + if (audio->format == AFMT_SPDIF) + clk_disable_unprepare(dp->spdif_clk); + + return 0; +} + +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable) +{ + int ret; + + ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable); + if (ret) + DRM_DEV_ERROR(dp->dev, "audio mute failed: %d\n", ret); + + return ret; +} + +static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, + struct audio_info *audio) +{ + int sub_pckt_num = 1, i2s_port_en_val = 0xf, i; + u32 val; + + if (audio->channels == 2) { + if (dp->link.num_lanes == 1) + sub_pckt_num = 2; + else + sub_pckt_num = 4; + + i2s_port_en_val = 1; + } else if (audio->channels == 4) { + i2s_port_en_val = 3; + } + + writel(0x0, dp->regs + SPDIF_CTRL_ADDR); + + writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); + + val = MAX_NUM_CH(audio->channels); + val |= NUM_OF_I2S_PORTS(audio->channels); + val |= AUDIO_TYPE_LPCM; + val |= CFG_SUB_PCKT_NUM(sub_pckt_num); + writel(val, dp->regs + SMPL2PKT_CNFG); + + if (audio->sample_width == 16) + val = 0; + else if (audio->sample_width == 24) + val = 1 << 9; + else + val = 2 << 9; + + val |= AUDIO_CH_NUM(audio->channels); + val |= I2S_DEC_PORT_EN(i2s_port_en_val); + val |= TRANS_SMPL_WIDTH_32; + writel(val, dp->regs + AUDIO_SRC_CNFG); + + for (i = 0; i < (audio->channels + 1) / 2; i++) { + if (audio->sample_width == 16) + val = (0x02 << 8) | (0x02 << 20); + else if (audio->sample_width == 24) + val = (0x0b << 8) | (0x0b << 20); + + val |= ((2 * i) << 4) | ((2 * i + 1) << 16); + writel(val, dp->regs + STTS_BIT_CH(i)); + } + + switch (audio->sample_rate) { + case 32000: + val = SAMPLING_FREQ(3) | + ORIGINAL_SAMP_FREQ(0xc); + break; + case 44100: + val = SAMPLING_FREQ(0) | + ORIGINAL_SAMP_FREQ(0xf); + break; + case 48000: + val = SAMPLING_FREQ(2) | + ORIGINAL_SAMP_FREQ(0xd); + break; + case 88200: + val = SAMPLING_FREQ(8) | + ORIGINAL_SAMP_FREQ(0x7); + break; + case 96000: + val = SAMPLING_FREQ(0xa) | + ORIGINAL_SAMP_FREQ(5); + break; + case 176400: + val = SAMPLING_FREQ(0xc) | + ORIGINAL_SAMP_FREQ(3); + break; + case 192000: + val = SAMPLING_FREQ(0xe) | + ORIGINAL_SAMP_FREQ(1); + break; + } + val |= 4; + writel(val, dp->regs + COM_CH_STTS_BITS); + + writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); + writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL); +} + +static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp) +{ + u32 val; + + val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; + val |= SPDIF_FIFO_MID_RANGE(0xe0); + val |= SPDIF_JITTER_THRSH(0xe0); + val |= SPDIF_JITTER_AVG_WIN(7); + writel(val, dp->regs + SPDIF_CTRL_ADDR); + + writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); + + val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); + writel(val, dp->regs + SMPL2PKT_CNFG); + writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); + + val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; + val |= SPDIF_FIFO_MID_RANGE(0xe0); + val |= SPDIF_JITTER_THRSH(0xe0); + val |= SPDIF_JITTER_AVG_WIN(7); + writel(val, dp->regs + SPDIF_CTRL_ADDR); + + clk_prepare_enable(dp->spdif_clk); + clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK); +} + +int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio) +{ + int ret; + + /* reset the spdif clk before config */ + if (audio->format == AFMT_SPDIF) { + reset_control_assert(dp->spdif_rst); + reset_control_deassert(dp->spdif_rst); + } + + ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC); + if (ret) + goto err_audio_config; + + ret = cdn_dp_reg_write(dp, CM_CTRL, 0); + if (ret) + goto err_audio_config; + + if (audio->format == AFMT_I2S) + cdn_dp_audio_config_i2s(dp, audio); + else if (audio->format == AFMT_SPDIF) + cdn_dp_audio_config_spdif(dp); + + ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN); + +err_audio_config: + if (ret) + DRM_DEV_ERROR(dp->dev, "audio config failed: %d\n", ret); + return ret; +} diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h new file mode 100644 index 000000000000..b5f215324694 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h @@ -0,0 +1,483 @@ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <zyw@rock-chips.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CDN_DP_REG_H +#define _CDN_DP_REG_H + +#include <linux/bitops.h> + +#define ADDR_IMEM 0x10000 +#define ADDR_DMEM 0x20000 + +/* APB CFG addr */ +#define APB_CTRL 0 +#define XT_INT_CTRL 0x04 +#define MAILBOX_FULL_ADDR 0x08 +#define MAILBOX_EMPTY_ADDR 0x0c +#define MAILBOX0_WR_DATA 0x10 +#define MAILBOX0_RD_DATA 0x14 +#define KEEP_ALIVE 0x18 +#define VER_L 0x1c +#define VER_H 0x20 +#define VER_LIB_L_ADDR 0x24 +#define VER_LIB_H_ADDR 0x28 +#define SW_DEBUG_L 0x2c +#define SW_DEBUG_H 0x30 +#define MAILBOX_INT_MASK 0x34 +#define MAILBOX_INT_STATUS 0x38 +#define SW_CLK_L 0x3c +#define SW_CLK_H 0x40 +#define SW_EVENTS0 0x44 +#define SW_EVENTS1 0x48 +#define SW_EVENTS2 0x4c +#define SW_EVENTS3 0x50 +#define XT_OCD_CTRL 0x60 +#define APB_INT_MASK 0x6c +#define APB_STATUS_MASK 0x70 + +/* audio decoder addr */ +#define AUDIO_SRC_CNTL 0x30000 +#define AUDIO_SRC_CNFG 0x30004 +#define COM_CH_STTS_BITS 0x30008 +#define STTS_BIT_CH(x) (0x3000c + ((x) << 2)) +#define SPDIF_CTRL_ADDR 0x3004c +#define SPDIF_CH1_CS_3100_ADDR 0x30050 +#define SPDIF_CH1_CS_6332_ADDR 0x30054 +#define SPDIF_CH1_CS_9564_ADDR 0x30058 +#define SPDIF_CH1_CS_12796_ADDR 0x3005c +#define SPDIF_CH1_CS_159128_ADDR 0x30060 +#define SPDIF_CH1_CS_191160_ADDR 0x30064 +#define SPDIF_CH2_CS_3100_ADDR 0x30068 +#define SPDIF_CH2_CS_6332_ADDR 0x3006c +#define SPDIF_CH2_CS_9564_ADDR 0x30070 +#define SPDIF_CH2_CS_12796_ADDR 0x30074 +#define SPDIF_CH2_CS_159128_ADDR 0x30078 +#define SPDIF_CH2_CS_191160_ADDR 0x3007c +#define SMPL2PKT_CNTL 0x30080 +#define SMPL2PKT_CNFG 0x30084 +#define FIFO_CNTL 0x30088 +#define FIFO_STTS 0x3008c + +/* source pif addr */ +#define SOURCE_PIF_WR_ADDR 0x30800 +#define SOURCE_PIF_WR_REQ 0x30804 +#define SOURCE_PIF_RD_ADDR 0x30808 +#define SOURCE_PIF_RD_REQ 0x3080c +#define SOURCE_PIF_DATA_WR 0x30810 +#define SOURCE_PIF_DATA_RD 0x30814 +#define SOURCE_PIF_FIFO1_FLUSH 0x30818 +#define SOURCE_PIF_FIFO2_FLUSH 0x3081c +#define SOURCE_PIF_STATUS 0x30820 +#define SOURCE_PIF_INTERRUPT_SOURCE 0x30824 +#define SOURCE_PIF_INTERRUPT_MASK 0x30828 +#define SOURCE_PIF_PKT_ALLOC_REG 0x3082c +#define SOURCE_PIF_PKT_ALLOC_WR_EN 0x30830 +#define SOURCE_PIF_SW_RESET 0x30834 + +/* bellow registers need access by mailbox */ +/* source car addr */ +#define SOURCE_HDTX_CAR 0x0900 +#define SOURCE_DPTX_CAR 0x0904 +#define SOURCE_PHY_CAR 0x0908 +#define SOURCE_CEC_CAR 0x090c +#define SOURCE_CBUS_CAR 0x0910 +#define SOURCE_PKT_CAR 0x0918 +#define SOURCE_AIF_CAR 0x091c +#define SOURCE_CIPHER_CAR 0x0920 +#define SOURCE_CRYPTO_CAR 0x0924 + +/* clock meters addr */ +#define CM_CTRL 0x0a00 +#define CM_I2S_CTRL 0x0a04 +#define CM_SPDIF_CTRL 0x0a08 +#define CM_VID_CTRL 0x0a0c +#define CM_LANE_CTRL 0x0a10 +#define I2S_NM_STABLE 0x0a14 +#define I2S_NCTS_STABLE 0x0a18 +#define SPDIF_NM_STABLE 0x0a1c +#define SPDIF_NCTS_STABLE 0x0a20 +#define NMVID_MEAS_STABLE 0x0a24 +#define I2S_MEAS 0x0a40 +#define SPDIF_MEAS 0x0a80 +#define NMVID_MEAS 0x0ac0 + +/* source vif addr */ +#define BND_HSYNC2VSYNC 0x0b00 +#define HSYNC2VSYNC_F1_L1 0x0b04 +#define HSYNC2VSYNC_F2_L1 0x0b08 +#define HSYNC2VSYNC_STATUS 0x0b0c +#define HSYNC2VSYNC_POL_CTRL 0x0b10 + +/* dptx phy addr */ +#define DP_TX_PHY_CONFIG_REG 0x2000 +#define DP_TX_PHY_STATUS_REG 0x2004 +#define DP_TX_PHY_SW_RESET 0x2008 +#define DP_TX_PHY_SCRAMBLER_SEED 0x200c +#define DP_TX_PHY_TRAINING_01_04 0x2010 +#define DP_TX_PHY_TRAINING_05_08 0x2014 +#define DP_TX_PHY_TRAINING_09_10 0x2018 +#define TEST_COR 0x23fc + +/* dptx hpd addr */ +#define HPD_IRQ_DET_MIN_TIMER 0x2100 +#define HPD_IRQ_DET_MAX_TIMER 0x2104 +#define HPD_UNPLGED_DET_MIN_TIMER 0x2108 +#define HPD_STABLE_TIMER 0x210c +#define HPD_FILTER_TIMER 0x2110 +#define HPD_EVENT_MASK 0x211c +#define HPD_EVENT_DET 0x2120 + +/* dpyx framer addr */ +#define DP_FRAMER_GLOBAL_CONFIG 0x2200 +#define DP_SW_RESET 0x2204 +#define DP_FRAMER_TU 0x2208 +#define DP_FRAMER_PXL_REPR 0x220c +#define DP_FRAMER_SP 0x2210 +#define AUDIO_PACK_CONTROL 0x2214 +#define DP_VC_TABLE(x) (0x2218 + ((x) << 2)) +#define DP_VB_ID 0x2258 +#define DP_MTPH_LVP_CONTROL 0x225c +#define DP_MTPH_SYMBOL_VALUES 0x2260 +#define DP_MTPH_ECF_CONTROL 0x2264 +#define DP_MTPH_ACT_CONTROL 0x2268 +#define DP_MTPH_STATUS 0x226c +#define DP_INTERRUPT_SOURCE 0x2270 +#define DP_INTERRUPT_MASK 0x2274 +#define DP_FRONT_BACK_PORCH 0x2278 +#define DP_BYTE_COUNT 0x227c + +/* dptx stream addr */ +#define MSA_HORIZONTAL_0 0x2280 +#define MSA_HORIZONTAL_1 0x2284 +#define MSA_VERTICAL_0 0x2288 +#define MSA_VERTICAL_1 0x228c +#define MSA_MISC 0x2290 +#define STREAM_CONFIG 0x2294 +#define AUDIO_PACK_STATUS 0x2298 +#define VIF_STATUS 0x229c +#define PCK_STUFF_STATUS_0 0x22a0 +#define PCK_STUFF_STATUS_1 0x22a4 +#define INFO_PACK_STATUS 0x22a8 +#define RATE_GOVERNOR_STATUS 0x22ac +#define DP_HORIZONTAL 0x22b0 +#define DP_VERTICAL_0 0x22b4 +#define DP_VERTICAL_1 0x22b8 +#define DP_BLOCK_SDP 0x22bc + +/* dptx glbl addr */ +#define DPTX_LANE_EN 0x2300 +#define DPTX_ENHNCD 0x2304 +#define DPTX_INT_MASK 0x2308 +#define DPTX_INT_STATUS 0x230c + +/* dp aux addr */ +#define DP_AUX_HOST_CONTROL 0x2800 +#define DP_AUX_INTERRUPT_SOURCE 0x2804 +#define DP_AUX_INTERRUPT_MASK 0x2808 +#define DP_AUX_SWAP_INVERSION_CONTROL 0x280c +#define DP_AUX_SEND_NACK_TRANSACTION 0x2810 +#define DP_AUX_CLEAR_RX 0x2814 +#define DP_AUX_CLEAR_TX 0x2818 +#define DP_AUX_TIMER_STOP 0x281c +#define DP_AUX_TIMER_CLEAR 0x2820 +#define DP_AUX_RESET_SW 0x2824 +#define DP_AUX_DIVIDE_2M 0x2828 +#define DP_AUX_TX_PREACHARGE_LENGTH 0x282c +#define DP_AUX_FREQUENCY_1M_MAX 0x2830 +#define DP_AUX_FREQUENCY_1M_MIN 0x2834 +#define DP_AUX_RX_PRE_MIN 0x2838 +#define DP_AUX_RX_PRE_MAX 0x283c +#define DP_AUX_TIMER_PRESET 0x2840 +#define DP_AUX_NACK_FORMAT 0x2844 +#define DP_AUX_TX_DATA 0x2848 +#define DP_AUX_RX_DATA 0x284c +#define DP_AUX_TX_STATUS 0x2850 +#define DP_AUX_RX_STATUS 0x2854 +#define DP_AUX_RX_CYCLE_COUNTER 0x2858 +#define DP_AUX_MAIN_STATES 0x285c +#define DP_AUX_MAIN_TIMER 0x2860 +#define DP_AUX_AFE_OUT 0x2864 + +/* crypto addr */ +#define CRYPTO_HDCP_REVISION 0x5800 +#define HDCP_CRYPTO_CONFIG 0x5804 +#define CRYPTO_INTERRUPT_SOURCE 0x5808 +#define CRYPTO_INTERRUPT_MASK 0x580c +#define CRYPTO22_CONFIG 0x5818 +#define CRYPTO22_STATUS 0x581c +#define SHA_256_DATA_IN 0x583c +#define SHA_256_DATA_OUT_(x) (0x5850 + ((x) << 2)) +#define AES_32_KEY_(x) (0x5870 + ((x) << 2)) +#define AES_32_DATA_IN 0x5880 +#define AES_32_DATA_OUT_(x) (0x5884 + ((x) << 2)) +#define CRYPTO14_CONFIG 0x58a0 +#define CRYPTO14_STATUS 0x58a4 +#define CRYPTO14_PRNM_OUT 0x58a8 +#define CRYPTO14_KM_0 0x58ac +#define CRYPTO14_KM_1 0x58b0 +#define CRYPTO14_AN_0 0x58b4 +#define CRYPTO14_AN_1 0x58b8 +#define CRYPTO14_YOUR_KSV_0 0x58bc +#define CRYPTO14_YOUR_KSV_1 0x58c0 +#define CRYPTO14_MI_0 0x58c4 +#define CRYPTO14_MI_1 0x58c8 +#define CRYPTO14_TI_0 0x58cc +#define CRYPTO14_KI_0 0x58d0 +#define CRYPTO14_KI_1 0x58d4 +#define CRYPTO14_BLOCKS_NUM 0x58d8 +#define CRYPTO14_KEY_MEM_DATA_0 0x58dc +#define CRYPTO14_KEY_MEM_DATA_1 0x58e0 +#define CRYPTO14_SHA1_MSG_DATA 0x58e4 +#define CRYPTO14_SHA1_V_VALUE_(x) (0x58e8 + ((x) << 2)) +#define TRNG_CTRL 0x58fc +#define TRNG_DATA_RDY 0x5900 +#define TRNG_DATA 0x5904 + +/* cipher addr */ +#define HDCP_REVISION 0x60000 +#define INTERRUPT_SOURCE 0x60004 +#define INTERRUPT_MASK 0x60008 +#define HDCP_CIPHER_CONFIG 0x6000c +#define AES_128_KEY_0 0x60010 +#define AES_128_KEY_1 0x60014 +#define AES_128_KEY_2 0x60018 +#define AES_128_KEY_3 0x6001c +#define AES_128_RANDOM_0 0x60020 +#define AES_128_RANDOM_1 0x60024 +#define CIPHER14_KM_0 0x60028 +#define CIPHER14_KM_1 0x6002c +#define CIPHER14_STATUS 0x60030 +#define CIPHER14_RI_PJ_STATUS 0x60034 +#define CIPHER_MODE 0x60038 +#define CIPHER14_AN_0 0x6003c +#define CIPHER14_AN_1 0x60040 +#define CIPHER22_AUTH 0x60044 +#define CIPHER14_R0_DP_STATUS 0x60048 +#define CIPHER14_BOOTSTRAP 0x6004c + +#define DPTX_FRMR_DATA_CLK_RSTN_EN BIT(11) +#define DPTX_FRMR_DATA_CLK_EN BIT(10) +#define DPTX_PHY_DATA_RSTN_EN BIT(9) +#define DPTX_PHY_DATA_CLK_EN BIT(8) +#define DPTX_PHY_CHAR_RSTN_EN BIT(7) +#define DPTX_PHY_CHAR_CLK_EN BIT(6) +#define SOURCE_AUX_SYS_CLK_RSTN_EN BIT(5) +#define SOURCE_AUX_SYS_CLK_EN BIT(4) +#define DPTX_SYS_CLK_RSTN_EN BIT(3) +#define DPTX_SYS_CLK_EN BIT(2) +#define CFG_DPTX_VIF_CLK_RSTN_EN BIT(1) +#define CFG_DPTX_VIF_CLK_EN BIT(0) + +#define SOURCE_PHY_RSTN_EN BIT(1) +#define SOURCE_PHY_CLK_EN BIT(0) + +#define SOURCE_PKT_SYS_RSTN_EN BIT(3) +#define SOURCE_PKT_SYS_CLK_EN BIT(2) +#define SOURCE_PKT_DATA_RSTN_EN BIT(1) +#define SOURCE_PKT_DATA_CLK_EN BIT(0) + +#define SPDIF_CDR_CLK_RSTN_EN BIT(5) +#define SPDIF_CDR_CLK_EN BIT(4) +#define SOURCE_AIF_SYS_RSTN_EN BIT(3) +#define SOURCE_AIF_SYS_CLK_EN BIT(2) +#define SOURCE_AIF_CLK_RSTN_EN BIT(1) +#define SOURCE_AIF_CLK_EN BIT(0) + +#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN BIT(3) +#define SOURCE_CIPHER_SYS_CLK_EN BIT(2) +#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN BIT(1) +#define SOURCE_CIPHER_CHAR_CLK_EN BIT(0) + +#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN BIT(1) +#define SOURCE_CRYPTO_SYS_CLK_EN BIT(0) + +#define APB_IRAM_PATH BIT(2) +#define APB_DRAM_PATH BIT(1) +#define APB_XT_RESET BIT(0) + +#define MAILBOX_INT_MASK_BIT BIT(1) +#define PIF_INT_MASK_BIT BIT(0) +#define ALL_INT_MASK 3 + +/* mailbox */ +#define MB_OPCODE_ID 0 +#define MB_MODULE_ID 1 +#define MB_SIZE_MSB_ID 2 +#define MB_SIZE_LSB_ID 3 +#define MB_DATA_ID 4 + +#define MB_MODULE_ID_DP_TX 0x01 +#define MB_MODULE_ID_HDCP_TX 0x07 +#define MB_MODULE_ID_HDCP_RX 0x08 +#define MB_MODULE_ID_HDCP_GENERAL 0x09 +#define MB_MODULE_ID_GENERAL 0x0a + +/* general opcode */ +#define GENERAL_MAIN_CONTROL 0x01 +#define GENERAL_TEST_ECHO 0x02 +#define GENERAL_BUS_SETTINGS 0x03 +#define GENERAL_TEST_ACCESS 0x04 + +#define DPTX_SET_POWER_MNG 0x00 +#define DPTX_SET_HOST_CAPABILITIES 0x01 +#define DPTX_GET_EDID 0x02 +#define DPTX_READ_DPCD 0x03 +#define DPTX_WRITE_DPCD 0x04 +#define DPTX_ENABLE_EVENT 0x05 +#define DPTX_WRITE_REGISTER 0x06 +#define DPTX_READ_REGISTER 0x07 +#define DPTX_WRITE_FIELD 0x08 +#define DPTX_TRAINING_CONTROL 0x09 +#define DPTX_READ_EVENT 0x0a +#define DPTX_READ_LINK_STAT 0x0b +#define DPTX_SET_VIDEO 0x0c +#define DPTX_SET_AUDIO 0x0d +#define DPTX_GET_LAST_AUX_STAUS 0x0e +#define DPTX_SET_LINK_BREAK_POINT 0x0f +#define DPTX_FORCE_LANES 0x10 +#define DPTX_HPD_STATE 0x11 + +#define FW_STANDBY 0 +#define FW_ACTIVE 1 + +#define DPTX_EVENT_ENABLE_HPD BIT(0) +#define DPTX_EVENT_ENABLE_TRAINING BIT(1) + +#define LINK_TRAINING_NOT_ACTIVE 0 +#define LINK_TRAINING_RUN 1 +#define LINK_TRAINING_RESTART 2 + +#define CONTROL_VIDEO_IDLE 0 +#define CONTROL_VIDEO_VALID 1 + +#define TU_CNT_RST_EN BIT(15) +#define VIF_BYPASS_INTERLACE BIT(13) +#define INTERLACE_FMT_DET BIT(12) +#define INTERLACE_DTCT_WIN 0x20 + +#define DP_FRAMER_SP_INTERLACE_EN BIT(2) +#define DP_FRAMER_SP_HSP BIT(1) +#define DP_FRAMER_SP_VSP BIT(0) + +/* capability */ +#define AUX_HOST_INVERT 3 +#define FAST_LT_SUPPORT 1 +#define FAST_LT_NOT_SUPPORT 0 +#define LANE_MAPPING_NORMAL 0x1b +#define LANE_MAPPING_FLIPPED 0xe4 +#define ENHANCED 1 +#define SCRAMBLER_EN BIT(4) + +#define FULL_LT_STARTED BIT(0) +#define FASE_LT_STARTED BIT(1) +#define CLK_RECOVERY_FINISHED BIT(2) +#define EQ_PHASE_FINISHED BIT(3) +#define FASE_LT_START_FINISHED BIT(4) +#define CLK_RECOVERY_FAILED BIT(5) +#define EQ_PHASE_FAILED BIT(6) +#define FASE_LT_FAILED BIT(7) + +#define DPTX_HPD_EVENT BIT(0) +#define DPTX_TRAINING_EVENT BIT(1) +#define HDCP_TX_STATUS_EVENT BIT(4) +#define HDCP2_TX_IS_KM_STORED_EVENT BIT(5) +#define HDCP2_TX_STORE_KM_EVENT BIT(6) +#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT BIT(7) + +#define TU_SIZE 30 +#define CDN_DP_MAX_LINK_RATE DP_LINK_BW_5_4 + +/* audio */ +#define AUDIO_PACK_EN BIT(8) +#define SAMPLING_FREQ(x) (((x) & 0xf) << 16) +#define ORIGINAL_SAMP_FREQ(x) (((x) & 0xf) << 24) +#define SYNC_WR_TO_CH_ZERO BIT(1) +#define I2S_DEC_START BIT(1) +#define AUDIO_SW_RST BIT(0) +#define SMPL2PKT_EN BIT(1) +#define MAX_NUM_CH(x) (((x) & 0x1f) - 1) +#define NUM_OF_I2S_PORTS(x) ((((x) / 2 - 1) & 0x3) << 5) +#define AUDIO_TYPE_LPCM (2 << 7) +#define CFG_SUB_PCKT_NUM(x) ((((x) - 1) & 0x7) << 11) +#define AUDIO_CH_NUM(x) ((((x) - 1) & 0x1f) << 2) +#define TRANS_SMPL_WIDTH_16 0 +#define TRANS_SMPL_WIDTH_24 BIT(11) +#define TRANS_SMPL_WIDTH_32 (2 << 11) +#define I2S_DEC_PORT_EN(x) (((x) & 0xf) << 17) +#define SPDIF_ENABLE BIT(21) +#define SPDIF_AVG_SEL BIT(20) +#define SPDIF_JITTER_BYPASS BIT(19) +#define SPDIF_FIFO_MID_RANGE(x) (((x) & 0xff) << 11) +#define SPDIF_JITTER_THRSH(x) (((x) & 0xff) << 3) +#define SPDIF_JITTER_AVG_WIN(x) ((x) & 0x7) + +/* Reference cycles when using lane clock as reference */ +#define LANE_REF_CYC 0x8000 + +enum voltage_swing_level { + VOLTAGE_LEVEL_0, + VOLTAGE_LEVEL_1, + VOLTAGE_LEVEL_2, + VOLTAGE_LEVEL_3, +}; + +enum pre_emphasis_level { + PRE_EMPHASIS_LEVEL_0, + PRE_EMPHASIS_LEVEL_1, + PRE_EMPHASIS_LEVEL_2, + PRE_EMPHASIS_LEVEL_3, +}; + +enum pattern_set { + PTS1 = BIT(0), + PTS2 = BIT(1), + PTS3 = BIT(2), + PTS4 = BIT(3), + DP_NONE = BIT(4) +}; + +enum vic_color_depth { + BCS_6 = 0x1, + BCS_8 = 0x2, + BCS_10 = 0x4, + BCS_12 = 0x8, + BCS_16 = 0x10, +}; + +enum vic_bt_type { + BT_601 = 0x0, + BT_709 = 0x1, +}; + +void cdn_dp_clock_reset(struct cdn_dp_device *dp); + +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk); +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, + u32 i_size, const u32 *d_mem, u32 d_size); +int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable); +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip); +int cdn_dp_event_config(struct cdn_dp_device *dp); +u32 cdn_dp_get_event(struct cdn_dp_device *dp); +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp); +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value); +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len); +int cdn_dp_get_edid_block(void *dp, u8 *edid, + unsigned int block, size_t length); +int cdn_dp_train_link(struct cdn_dp_device *dp); +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active); +int cdn_dp_config_video(struct cdn_dp_device *dp); +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio); +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable); +int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio); +#endif /* _CDN_DP_REG_H */ diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 0665fb915579..a6d4a0236e8f 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -257,8 +257,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, struct drm_device *drm = data; struct drm_encoder *encoder; struct rockchip_hdmi *hdmi; - struct resource *iores; - int irq; int ret; if (!pdev->dev.of_node) @@ -273,14 +271,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, hdmi->dev = &pdev->dev; encoder = &hdmi->encoder; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENXIO; - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); /* * If we failed to find the CRTC(s) which this encoder is @@ -301,7 +291,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); - ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data); + ret = dw_hdmi_bind(pdev, encoder, plat_data); /* * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), @@ -316,7 +306,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, void *data) { - return dw_hdmi_unbind(dev, master, data); + return dw_hdmi_unbind(dev); } static const struct component_ops dw_hdmi_rockchip_ops = { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 2390c8577617..b360e6251836 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -14,19 +14,19 @@ * GNU General Public License for more details. */ -#include <asm/dma-iommu.h> - #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_of.h> #include <linux/dma-mapping.h> +#include <linux/dma-iommu.h> #include <linux/pm_runtime.h> #include <linux/module.h> #include <linux/of_graph.h> #include <linux/component.h> #include <linux/console.h> +#include <linux/iommu.h> #include "rockchip_drm_drv.h" #include "rockchip_drm_fb.h" @@ -50,28 +50,31 @@ static struct drm_driver rockchip_drm_driver; int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, struct device *dev) { - struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping; + struct rockchip_drm_private *private = drm_dev->dev_private; int ret; if (!is_support_iommu) return 0; - ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); - if (ret) + ret = iommu_attach_device(private->domain, dev); + if (ret) { + dev_err(dev, "Failed to attach iommu device\n"); return ret; + } - dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); - - return arm_iommu_attach_device(dev, mapping); + return 0; } void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, struct device *dev) { + struct rockchip_drm_private *private = drm_dev->dev_private; + struct iommu_domain *domain = private->domain; + if (!is_support_iommu) return; - arm_iommu_detach_device(dev); + iommu_detach_device(domain, dev); } int rockchip_register_crtc_funcs(struct drm_crtc *crtc, @@ -99,24 +102,11 @@ void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc) priv->crtc_funcs[pipe] = NULL; } -static struct drm_crtc *rockchip_crtc_from_pipe(struct drm_device *drm, - int pipe) -{ - struct drm_crtc *crtc; - int i = 0; - - list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) - if (i++ == pipe) - return crtc; - - return NULL; -} - static int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe) { struct rockchip_drm_private *priv = dev->dev_private; - struct drm_crtc *crtc = rockchip_crtc_from_pipe(dev, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); if (crtc && priv->crtc_funcs[pipe] && priv->crtc_funcs[pipe]->enable_vblank) @@ -129,18 +119,53 @@ static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe) { struct rockchip_drm_private *priv = dev->dev_private; - struct drm_crtc *crtc = rockchip_crtc_from_pipe(dev, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); if (crtc && priv->crtc_funcs[pipe] && priv->crtc_funcs[pipe]->enable_vblank) priv->crtc_funcs[pipe]->disable_vblank(crtc); } +static int rockchip_drm_init_iommu(struct drm_device *drm_dev) +{ + struct rockchip_drm_private *private = drm_dev->dev_private; + struct iommu_domain_geometry *geometry; + u64 start, end; + + if (!is_support_iommu) + return 0; + + private->domain = iommu_domain_alloc(&platform_bus_type); + if (!private->domain) + return -ENOMEM; + + geometry = &private->domain->geometry; + start = geometry->aperture_start; + end = geometry->aperture_end; + + DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n", + start, end); + drm_mm_init(&private->mm, start, end - start + 1); + mutex_init(&private->mm_lock); + + return 0; +} + +static void rockchip_iommu_cleanup(struct drm_device *drm_dev) +{ + struct rockchip_drm_private *private = drm_dev->dev_private; + + if (!is_support_iommu) + return; + + drm_mm_takedown(&private->mm); + iommu_domain_free(private->domain); +} + static int rockchip_drm_bind(struct device *dev) { struct drm_device *drm_dev; struct rockchip_drm_private *private; - struct dma_iommu_mapping *mapping = NULL; int ret; drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev); @@ -164,38 +189,14 @@ static int rockchip_drm_bind(struct device *dev) rockchip_drm_mode_config_init(drm_dev); - dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), - GFP_KERNEL); - if (!dev->dma_parms) { - ret = -ENOMEM; + ret = rockchip_drm_init_iommu(drm_dev); + if (ret) goto err_config_cleanup; - } - - if (is_support_iommu) { - /* TODO(djkurtz): fetch the mapping start/size from somewhere */ - mapping = arm_iommu_create_mapping(&platform_bus_type, - 0x00000000, - SZ_2G); - if (IS_ERR(mapping)) { - ret = PTR_ERR(mapping); - goto err_config_cleanup; - } - - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); - if (ret) - goto err_release_mapping; - - dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); - - ret = arm_iommu_attach_device(dev, mapping); - if (ret) - goto err_release_mapping; - } /* Try to bind all sub drivers. */ ret = component_bind_all(dev, drm_dev); if (ret) - goto err_detach_device; + goto err_iommu_cleanup; /* init kms poll for handling hpd */ drm_kms_helper_poll_init(drm_dev); @@ -220,8 +221,6 @@ static int rockchip_drm_bind(struct device *dev) if (ret) goto err_fbdev_fini; - if (is_support_iommu) - arm_iommu_release_mapping(mapping); return 0; err_fbdev_fini: rockchip_drm_fbdev_fini(drm_dev); @@ -230,12 +229,8 @@ err_vblank_cleanup: err_kms_helper_poll_fini: drm_kms_helper_poll_fini(drm_dev); component_unbind_all(dev, drm_dev); -err_detach_device: - if (is_support_iommu) - arm_iommu_detach_device(dev); -err_release_mapping: - if (is_support_iommu) - arm_iommu_release_mapping(mapping); +err_iommu_cleanup: + rockchip_iommu_cleanup(drm_dev); err_config_cleanup: drm_mode_config_cleanup(drm_dev); drm_dev->dev_private = NULL; @@ -252,8 +247,7 @@ static void rockchip_drm_unbind(struct device *dev) drm_vblank_cleanup(drm_dev); drm_kms_helper_poll_fini(drm_dev); component_unbind_all(dev, drm_dev); - if (is_support_iommu) - arm_iommu_detach_device(dev); + rockchip_iommu_cleanup(drm_dev); drm_mode_config_cleanup(drm_dev); drm_dev->dev_private = NULL; drm_dev_unregister(drm_dev); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index fb6226cf84b7..adc39302bec5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -30,6 +30,7 @@ struct drm_device; struct drm_connector; +struct iommu_domain; /* * Rockchip drm private crtc funcs. @@ -60,7 +61,10 @@ struct rockchip_drm_private { struct drm_gem_object *fbdev_bo; const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; struct drm_atomic_state *state; - + struct iommu_domain *domain; + /* protect drm_mm on multi-threads */ + struct mutex mm_lock; + struct drm_mm mm; struct list_head psr_list; spinlock_t psr_list_lock; }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 0f6eda023bd0..c9ccdf8f44bb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -92,7 +92,7 @@ rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cm if (!rockchip_fb) return ERR_PTR(-ENOMEM); - drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &rockchip_fb->fb, mode_cmd); for (i = 0; i < num_planes; i++) rockchip_fb->obj[i] = obj[i]; @@ -213,7 +213,7 @@ rockchip_drm_framebuffer_init(struct drm_device *dev, rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1); if (IS_ERR(rockchip_fb)) - return NULL; + return ERR_CAST(rockchip_fb); return &rockchip_fb->fb; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c index 8f639c8597a5..70ad50dd594d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -94,7 +94,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, fbi->fbops = &rockchip_drm_fbdev_ops; fb = helper->fb; - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); offset = fbi->var.xoffset * bytes_per_pixel; @@ -106,7 +106,8 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, fbi->fix.smem_len = rk_obj->base.size; DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%zu\n", - fb->width, fb->height, fb->depth, rk_obj->kvaddr, + fb->width, fb->height, fb->format->depth, + rk_obj->kvaddr, offset, size); fbi->skip_vt_switch = true; @@ -128,19 +129,16 @@ int rockchip_drm_fbdev_init(struct drm_device *dev) { struct rockchip_drm_private *private = dev->dev_private; struct drm_fb_helper *helper; - unsigned int num_crtc; int ret; if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) return -EINVAL; - num_crtc = dev->mode_config.num_crtc; - helper = &private->fbdev_helper; drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs); - ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR); + ret = drm_fb_helper_init(dev, helper, ROCKCHIP_MAX_CONNECTOR); if (ret < 0) { dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n", ret); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index b70f9423379c..df9e57064f19 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -16,11 +16,146 @@ #include <drm/drmP.h> #include <drm/drm_gem.h> #include <drm/drm_vma_manager.h> +#include <linux/iommu.h> #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" -static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, +static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj) +{ + struct drm_device *drm = rk_obj->base.dev; + struct rockchip_drm_private *private = drm->dev_private; + int prot = IOMMU_READ | IOMMU_WRITE; + ssize_t ret; + + mutex_lock(&private->mm_lock); + + ret = drm_mm_insert_node_generic(&private->mm, &rk_obj->mm, + rk_obj->base.size, PAGE_SIZE, + 0, 0); + + mutex_unlock(&private->mm_lock); + if (ret < 0) { + DRM_ERROR("out of I/O virtual memory: %zd\n", ret); + return ret; + } + + rk_obj->dma_addr = rk_obj->mm.start; + + ret = iommu_map_sg(private->domain, rk_obj->dma_addr, rk_obj->sgt->sgl, + rk_obj->sgt->nents, prot); + if (ret < rk_obj->base.size) { + DRM_ERROR("failed to map buffer: size=%zd request_size=%zd\n", + ret, rk_obj->base.size); + ret = -ENOMEM; + goto err_remove_node; + } + + rk_obj->size = ret; + + return 0; + +err_remove_node: + drm_mm_remove_node(&rk_obj->mm); + + return ret; +} + +static int rockchip_gem_iommu_unmap(struct rockchip_gem_object *rk_obj) +{ + struct drm_device *drm = rk_obj->base.dev; + struct rockchip_drm_private *private = drm->dev_private; + + iommu_unmap(private->domain, rk_obj->dma_addr, rk_obj->size); + + mutex_lock(&private->mm_lock); + + drm_mm_remove_node(&rk_obj->mm); + + mutex_unlock(&private->mm_lock); + + return 0; +} + +static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj) +{ + struct drm_device *drm = rk_obj->base.dev; + int ret, i; + struct scatterlist *s; + + rk_obj->pages = drm_gem_get_pages(&rk_obj->base); + if (IS_ERR(rk_obj->pages)) + return PTR_ERR(rk_obj->pages); + + rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT; + + rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->pages, rk_obj->num_pages); + if (IS_ERR(rk_obj->sgt)) { + ret = PTR_ERR(rk_obj->sgt); + goto err_put_pages; + } + + /* + * Fake up the SG table so that dma_sync_sg_for_device() can be used + * to flush the pages associated with it. + * + * TODO: Replace this by drm_clflush_sg() once it can be implemented + * without relying on symbols that are not exported. + */ + for_each_sg(rk_obj->sgt->sgl, s, rk_obj->sgt->nents, i) + sg_dma_address(s) = sg_phys(s); + + dma_sync_sg_for_device(drm->dev, rk_obj->sgt->sgl, rk_obj->sgt->nents, + DMA_TO_DEVICE); + + return 0; + +err_put_pages: + drm_gem_put_pages(&rk_obj->base, rk_obj->pages, false, false); + return ret; +} + +static void rockchip_gem_put_pages(struct rockchip_gem_object *rk_obj) +{ + sg_free_table(rk_obj->sgt); + kfree(rk_obj->sgt); + drm_gem_put_pages(&rk_obj->base, rk_obj->pages, true, true); +} + +static int rockchip_gem_alloc_iommu(struct rockchip_gem_object *rk_obj, + bool alloc_kmap) +{ + int ret; + + ret = rockchip_gem_get_pages(rk_obj); + if (ret < 0) + return ret; + + ret = rockchip_gem_iommu_map(rk_obj); + if (ret < 0) + goto err_free; + + if (alloc_kmap) { + rk_obj->kvaddr = vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); + if (!rk_obj->kvaddr) { + DRM_ERROR("failed to vmap() buffer\n"); + ret = -ENOMEM; + goto err_unmap; + } + } + + return 0; + +err_unmap: + rockchip_gem_iommu_unmap(rk_obj); +err_free: + rockchip_gem_put_pages(rk_obj); + + return ret; +} + +static int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj, bool alloc_kmap) { struct drm_gem_object *obj = &rk_obj->base; @@ -42,7 +177,27 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, return 0; } -static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj) +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, + bool alloc_kmap) +{ + struct drm_gem_object *obj = &rk_obj->base; + struct drm_device *drm = obj->dev; + struct rockchip_drm_private *private = drm->dev_private; + + if (private->domain) + return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap); + else + return rockchip_gem_alloc_dma(rk_obj, alloc_kmap); +} + +static void rockchip_gem_free_iommu(struct rockchip_gem_object *rk_obj) +{ + vunmap(rk_obj->kvaddr); + rockchip_gem_iommu_unmap(rk_obj); + rockchip_gem_put_pages(rk_obj); +} + +static void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj) { struct drm_gem_object *obj = &rk_obj->base; struct drm_device *drm = obj->dev; @@ -51,23 +206,68 @@ static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj) rk_obj->dma_attrs); } -static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, - struct vm_area_struct *vma) +static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj) +{ + if (rk_obj->pages) + rockchip_gem_free_iommu(rk_obj); + else + rockchip_gem_free_dma(rk_obj); +} +static int rockchip_drm_gem_object_mmap_iommu(struct drm_gem_object *obj, + struct vm_area_struct *vma) { + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); + unsigned int i, count = obj->size >> PAGE_SHIFT; + unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + unsigned long uaddr = vma->vm_start; + unsigned long offset = vma->vm_pgoff; + unsigned long end = user_count + offset; int ret; + + if (user_count == 0) + return -ENXIO; + if (end > count) + return -ENXIO; + + for (i = offset; i < end; i++) { + ret = vm_insert_page(vma, uaddr, rk_obj->pages[i]); + if (ret) + return ret; + uaddr += PAGE_SIZE; + } + + return 0; +} + +static int rockchip_drm_gem_object_mmap_dma(struct drm_gem_object *obj, + struct vm_area_struct *vma) +{ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); struct drm_device *drm = obj->dev; + return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, + obj->size, rk_obj->dma_attrs); +} + +static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, + struct vm_area_struct *vma) +{ + int ret; + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); + /* - * dma_alloc_attrs() allocated a struct page table for rk_obj, so clear + * We allocated a struct page table for rk_obj, so clear * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). */ vma->vm_flags &= ~VM_PFNMAP; vma->vm_pgoff = 0; - ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, - obj->size, rk_obj->dma_attrs); + if (rk_obj->pages) + ret = rockchip_drm_gem_object_mmap_iommu(obj, vma); + else + ret = rockchip_drm_gem_object_mmap_dma(obj, vma); + if (ret) drm_gem_vm_close(vma); @@ -101,6 +301,12 @@ int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) return rockchip_drm_gem_object_mmap(obj, vma); } +static void rockchip_gem_release_object(struct rockchip_gem_object *rk_obj) +{ + drm_gem_object_release(&rk_obj->base); + kfree(rk_obj); +} + struct rockchip_gem_object * rockchip_gem_create_object(struct drm_device *drm, unsigned int size, bool alloc_kmap) @@ -117,7 +323,7 @@ struct rockchip_gem_object * obj = &rk_obj->base; - drm_gem_private_object_init(drm, obj, size); + drm_gem_object_init(drm, obj, size); ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap); if (ret) @@ -126,7 +332,7 @@ struct rockchip_gem_object * return rk_obj; err_free_rk_obj: - kfree(rk_obj); + rockchip_gem_release_object(rk_obj); return ERR_PTR(ret); } @@ -138,13 +344,11 @@ void rockchip_gem_free_object(struct drm_gem_object *obj) { struct rockchip_gem_object *rk_obj; - drm_gem_free_mmap_offset(obj); - rk_obj = to_rockchip_obj(obj); rockchip_gem_free_buf(rk_obj); - kfree(rk_obj); + rockchip_gem_release_object(rk_obj); } /* @@ -253,6 +457,9 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) struct sg_table *sgt; int ret; + if (rk_obj->pages) + return drm_prime_pages_to_sg(rk_obj->pages, rk_obj->num_pages); + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) return ERR_PTR(-ENOMEM); @@ -273,6 +480,10 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) { struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); + if (rk_obj->pages) + return vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); + if (rk_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING) return NULL; @@ -281,5 +492,12 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - /* Nothing to do */ + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); + + if (rk_obj->pages) { + vunmap(vaddr); + return; + } + + /* Nothing to do if allocated by DMA mapping API. */ } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h index 18b3488db4ec..3f6ea4d18a5c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -23,7 +23,15 @@ struct rockchip_gem_object { void *kvaddr; dma_addr_t dma_addr; + /* Used when IOMMU is disabled */ unsigned long dma_attrs; + + /* Used when IOMMU is enabled */ + struct drm_mm_node mm; + unsigned long num_pages; + struct page **pages; + struct sg_table *sgt; + size_t size; }; struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index c7eba305c488..76c79ac57df0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -531,6 +531,8 @@ static int vop_enable(struct drm_crtc *crtc) } memcpy(vop->regs, vop->regsbak, vop->len); + vop_cfg_done(vop); + /* * At here, vop clock & iommu is enable, R/W vop regs would be safe. */ @@ -582,6 +584,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc) spin_unlock(&vop->reg_lock); } + vop_cfg_done(vop); + drm_crtc_vblank_off(crtc); /* @@ -668,7 +672,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, if (!state->visible) return 0; - ret = vop_convert_format(fb->pixel_format); + ret = vop_convert_format(fb->format->format); if (ret < 0) return ret; @@ -676,7 +680,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, * Src.x1 can be odd when do clip, but yuv plane start point * need align with 2 pixel. */ - if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2)) + if (is_yuv_support(fb->format->format) && ((state->src.x1 >> 16) % 2)) return -EINVAL; return 0; @@ -749,21 +753,21 @@ static void vop_plane_atomic_update(struct drm_plane *plane, dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start; dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); - offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0); + offset = (src->x1 >> 16) * fb->format->cpp[0]; offset += (src->y1 >> 16) * fb->pitches[0]; dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; - format = vop_convert_format(fb->pixel_format); + format = vop_convert_format(fb->format->format); spin_lock(&vop->reg_lock); VOP_WIN_SET(vop, win, format, format); VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2); VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); - if (is_yuv_support(fb->pixel_format)) { - int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); - int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); - int bpp = drm_format_plane_cpp(fb->pixel_format, 1); + if (is_yuv_support(fb->format->format)) { + int hsub = drm_format_horz_chroma_subsampling(fb->format->format); + int vsub = drm_format_vert_chroma_subsampling(fb->format->format); + int bpp = fb->format->cpp[1]; uv_obj = rockchip_fb_get_gem_obj(fb, 1); rk_uv_obj = to_rockchip_obj(uv_obj); @@ -779,16 +783,16 @@ static void vop_plane_atomic_update(struct drm_plane *plane, if (win->phy->scl) scl_vop_cal_scl_fac(vop, win, actual_w, actual_h, drm_rect_width(dest), drm_rect_height(dest), - fb->pixel_format); + fb->format->format); VOP_WIN_SET(vop, win, act_info, act_info); VOP_WIN_SET(vop, win, dsp_info, dsp_info); VOP_WIN_SET(vop, win, dsp_st, dsp_st); - rb_swap = has_rb_swapped(fb->pixel_format); + rb_swap = has_rb_swapped(fb->format->format); VOP_WIN_SET(vop, win, rb_swap, rb_swap); - if (is_alpha_support(fb->pixel_format)) { + if (is_alpha_support(fb->format->format)) { VOP_WIN_SET(vop, win, dst_alpha_ctl, DST_FACTOR_M0(ALPHA_SRC_INVERSE)); val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) | @@ -932,9 +936,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc) vop_dsp_hold_valid_irq_disable(vop); } - pin_pol = 0x8; - pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1; - pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1); + pin_pol = BIT(DCLK_INVERT); + pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? + 0 : BIT(HSYNC_POSITIVE); + pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? + 0 : BIT(VSYNC_POSITIVE); VOP_CTRL_SET(vop, pin_pol, pin_pol); switch (s->output_type) { @@ -954,6 +960,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc) VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol); VOP_CTRL_SET(vop, mipi_en, 1); break; + case DRM_MODE_CONNECTOR_DisplayPort: + pin_pol &= ~BIT(DCLK_INVERT); + VOP_CTRL_SET(vop, dp_pin_pol, pin_pol); + VOP_CTRL_SET(vop, dp_en, 1); + break; default: DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n", s->output_type); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 1dbc52615257..5a4faa85dbd2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -45,6 +45,7 @@ struct vop_ctrl { struct vop_reg edp_en; struct vop_reg hdmi_en; struct vop_reg mipi_en; + struct vop_reg dp_en; struct vop_reg out_mode; struct vop_reg dither_down; struct vop_reg dither_up; @@ -53,6 +54,7 @@ struct vop_ctrl { struct vop_reg hdmi_pin_pol; struct vop_reg edp_pin_pol; struct vop_reg mipi_pin_pol; + struct vop_reg dp_pin_pol; struct vop_reg htotal_pw; struct vop_reg hact_st_end; @@ -244,6 +246,13 @@ enum scale_down_mode { SCALE_DOWN_AVG = 0x1 }; +enum vop_pol { + HSYNC_POSITIVE = 0, + VSYNC_POSITIVE = 1, + DEN_NEGATIVE = 2, + DCLK_INVERT = 3 +}; + #define FRAC_16_16(mult, div) (((mult) << 16) / (div)) #define SCL_FT_DEFAULT_FIXPOINT_SHIFT 12 #define SCL_MAX_VSKIPLINES 4 diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 35c51f3402f2..91fbc7b52147 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -284,6 +284,7 @@ static const struct vop_data rk3288_vop = { static const struct vop_ctrl rk3399_ctrl_data = { .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22), .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23), + .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12), .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13), .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14), @@ -293,6 +294,7 @@ static const struct vop_ctrl rk3399_ctrl_data = { .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19), .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0), .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), + .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20), .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24), .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28), diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c index d47dff95fe52..2a5b8466d806 100644 --- a/drivers/gpu/drm/savage/savage_bci.c +++ b/drivers/gpu/drm/savage/savage_bci.c @@ -655,13 +655,11 @@ void savage_driver_lastclose(struct drm_device *dev) } } -int savage_driver_unload(struct drm_device *dev) +void savage_driver_unload(struct drm_device *dev) { drm_savage_private_t *dev_priv = dev->dev_private; kfree(dev_priv); - - return 0; } static int savage_do_init_bci(struct drm_device * dev, drm_savage_init_t * init) diff --git a/drivers/gpu/drm/savage/savage_drv.h b/drivers/gpu/drm/savage/savage_drv.h index 37b699571ad0..44a1009b6ecb 100644 --- a/drivers/gpu/drm/savage/savage_drv.h +++ b/drivers/gpu/drm/savage/savage_drv.h @@ -210,7 +210,7 @@ extern uint32_t *savage_dma_alloc(drm_savage_private_t * dev_priv, extern int savage_driver_load(struct drm_device *dev, unsigned long chipset); extern int savage_driver_firstopen(struct drm_device *dev); extern void savage_driver_lastclose(struct drm_device *dev); -extern int savage_driver_unload(struct drm_device *dev); +extern void savage_driver_unload(struct drm_device *dev); extern void savage_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile new file mode 100644 index 000000000000..4aebfc7f27d4 --- /dev/null +++ b/drivers/gpu/drm/selftests/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DRM_DEBUG_MM_SELFTEST) += test-drm_mm.o diff --git a/drivers/gpu/drm/selftests/drm_mm_selftests.h b/drivers/gpu/drm/selftests/drm_mm_selftests.h new file mode 100644 index 000000000000..37bbdac52896 --- /dev/null +++ b/drivers/gpu/drm/selftests/drm_mm_selftests.h @@ -0,0 +1,24 @@ +/* List each unit test as selftest(name, function) + * + * The name is used as both an enum and expanded as igt__name to create + * a module parameter. It must be unique and legal for a C identifier. + * + * Tests are executed in order by igt/drm_mm + */ +selftest(sanitycheck, igt_sanitycheck) /* keep first (selfcheck for igt) */ +selftest(init, igt_init) +selftest(debug, igt_debug) +selftest(reserve, igt_reserve) +selftest(insert, igt_insert) +selftest(replace, igt_replace) +selftest(insert_range, igt_insert_range) +selftest(align, igt_align) +selftest(align32, igt_align32) +selftest(align64, igt_align64) +selftest(evict, igt_evict) +selftest(evict_range, igt_evict_range) +selftest(bottomup, igt_bottomup) +selftest(topdown, igt_topdown) +selftest(color, igt_color) +selftest(color_evict, igt_color_evict) +selftest(color_evict_range, igt_color_evict_range) diff --git a/drivers/gpu/drm/selftests/drm_selftest.c b/drivers/gpu/drm/selftests/drm_selftest.c new file mode 100644 index 000000000000..e29ed9faef5b --- /dev/null +++ b/drivers/gpu/drm/selftests/drm_selftest.c @@ -0,0 +1,109 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/compiler.h> + +#define selftest(name, func) __idx_##name, +enum { +#include TESTS +}; +#undef selftest + +#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f }, +static struct drm_selftest { + bool enabled; + const char *name; + int (*func)(void *); +} selftests[] = { +#include TESTS +}; +#undef selftest + +/* Embed the line number into the parameter name so that we can order tests */ +#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n)) +#define selftest_0(n, func, id) \ +module_param_named(id, selftests[__idx_##n].enabled, bool, 0400); +#define selftest(n, func) selftest_0(n, func, param(n)) +#include TESTS +#undef selftest + +static void set_default_test_all(struct drm_selftest *st, unsigned long count) +{ + unsigned long i; + + for (i = 0; i < count; i++) + if (st[i].enabled) + return; + + for (i = 0; i < count; i++) + st[i].enabled = true; +} + +static int run_selftests(struct drm_selftest *st, + unsigned long count, + void *data) +{ + int err = 0; + + set_default_test_all(st, count); + + /* Tests are listed in natural order in drm_*_selftests.h */ + for (; count--; st++) { + if (!st->enabled) + continue; + + pr_debug("drm: Running %s\n", st->name); + err = st->func(data); + if (err) + break; + } + + if (WARN(err > 0 || err == -ENOTTY, + "%s returned %d, conflicting with selftest's magic values!\n", + st->name, err)) + err = -1; + + rcu_barrier(); + return err; +} + +static int __maybe_unused +__drm_subtests(const char *caller, + const struct drm_subtest *st, + int count, + void *data) +{ + int err; + + for (; count--; st++) { + pr_debug("Running %s/%s\n", caller, st->name); + err = st->func(data); + if (err) { + pr_err("%s: %s failed with error %d\n", + caller, st->name, err); + return err; + } + } + + return 0; +} diff --git a/drivers/gpu/drm/selftests/drm_selftest.h b/drivers/gpu/drm/selftests/drm_selftest.h new file mode 100644 index 000000000000..c784ec02ff53 --- /dev/null +++ b/drivers/gpu/drm/selftests/drm_selftest.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __DRM_SELFTEST_H__ +#define __DRM_SELFTEST_H__ + +struct drm_subtest { + int (*func)(void *data); + const char *name; +}; + +static int __drm_subtests(const char *caller, + const struct drm_subtest *st, + int count, + void *data); +#define drm_subtests(T, data) \ + __drm_subtests(__func__, T, ARRAY_SIZE(T), data) + +#define SUBTEST(x) { x, #x } + +#endif /* __DRM_SELFTEST_H__ */ diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c new file mode 100644 index 000000000000..1e71bc182ca9 --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_mm.c @@ -0,0 +1,2276 @@ +/* + * Test cases for the drm_mm range manager + */ + +#define pr_fmt(fmt) "drm_mm: " fmt + +#include <linux/module.h> +#include <linux/prime_numbers.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/vmalloc.h> + +#include <drm/drm_mm.h> + +#include "../lib/drm_random.h" + +#define TESTS "drm_mm_selftests.h" +#include "drm_selftest.h" + +static unsigned int random_seed; +static unsigned int max_iterations = 8192; +static unsigned int max_prime = 128; + +enum { + BEST, + BOTTOMUP, + TOPDOWN, + EVICT, +}; + +static const struct insert_mode { + const char *name; + enum drm_mm_insert_mode mode; +} insert_modes[] = { + [BEST] = { "best", DRM_MM_INSERT_BEST }, + [BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW }, + [TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH }, + [EVICT] = { "evict", DRM_MM_INSERT_EVICT }, + {} +}, evict_modes[] = { + { "bottom-up", DRM_MM_INSERT_LOW }, + { "top-down", DRM_MM_INSERT_HIGH }, + {} +}; + +static int igt_sanitycheck(void *ignored) +{ + pr_info("%s - ok!\n", __func__); + return 0; +} + +static bool assert_no_holes(const struct drm_mm *mm) +{ + struct drm_mm_node *hole; + u64 hole_start, hole_end; + unsigned long count; + + count = 0; + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) + count++; + if (count) { + pr_err("Expected to find no holes (after reserve), found %lu instead\n", count); + return false; + } + + drm_mm_for_each_node(hole, mm) { + if (drm_mm_hole_follows(hole)) { + pr_err("Hole follows node, expected none!\n"); + return false; + } + } + + return true; +} + +static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end) +{ + struct drm_mm_node *hole; + u64 hole_start, hole_end; + unsigned long count; + bool ok = true; + + if (end <= start) + return true; + + count = 0; + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { + if (start != hole_start || end != hole_end) { + if (ok) + pr_err("empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n", + hole_start, hole_end, + start, end); + ok = false; + } + count++; + } + if (count != 1) { + pr_err("Expected to find one hole, found %lu instead\n", count); + ok = false; + } + + return ok; +} + +static bool assert_continuous(const struct drm_mm *mm, u64 size) +{ + struct drm_mm_node *node, *check, *found; + unsigned long n; + u64 addr; + + if (!assert_no_holes(mm)) + return false; + + n = 0; + addr = 0; + drm_mm_for_each_node(node, mm) { + if (node->start != addr) { + pr_err("node[%ld] list out of order, expected %llx found %llx\n", + n, addr, node->start); + return false; + } + + if (node->size != size) { + pr_err("node[%ld].size incorrect, expected %llx, found %llx\n", + n, size, node->size); + return false; + } + + if (drm_mm_hole_follows(node)) { + pr_err("node[%ld] is followed by a hole!\n", n); + return false; + } + + found = NULL; + drm_mm_for_each_node_in_range(check, mm, addr, addr + size) { + if (node != check) { + pr_err("lookup return wrong node, expected start %llx, found %llx\n", + node->start, check->start); + return false; + } + found = check; + } + if (!found) { + pr_err("lookup failed for node %llx + %llx\n", + addr, size); + return false; + } + + addr += size; + n++; + } + + return true; +} + +static u64 misalignment(struct drm_mm_node *node, u64 alignment) +{ + u64 rem; + + if (!alignment) + return 0; + + div64_u64_rem(node->start, alignment, &rem); + return rem; +} + +static bool assert_node(struct drm_mm_node *node, struct drm_mm *mm, + u64 size, u64 alignment, unsigned long color) +{ + bool ok = true; + + if (!drm_mm_node_allocated(node) || node->mm != mm) { + pr_err("node not allocated\n"); + ok = false; + } + + if (node->size != size) { + pr_err("node has wrong size, found %llu, expected %llu\n", + node->size, size); + ok = false; + } + + if (misalignment(node, alignment)) { + pr_err("node is misalinged, start %llx rem %llu, expected alignment %llu\n", + node->start, misalignment(node, alignment), alignment); + ok = false; + } + + if (node->color != color) { + pr_err("node has wrong color, found %lu, expected %lu\n", + node->color, color); + ok = false; + } + + return ok; +} + +#define show_mm(mm) do { \ + struct drm_printer __p = drm_debug_printer(__func__); \ + drm_mm_print((mm), &__p); } while (0) + +static int igt_init(void *ignored) +{ + const unsigned int size = 4096; + struct drm_mm mm; + struct drm_mm_node tmp; + int ret = -EINVAL; + + /* Start with some simple checks on initialising the struct drm_mm */ + memset(&mm, 0, sizeof(mm)); + if (drm_mm_initialized(&mm)) { + pr_err("zeroed mm claims to be initialized\n"); + return ret; + } + + memset(&mm, 0xff, sizeof(mm)); + drm_mm_init(&mm, 0, size); + if (!drm_mm_initialized(&mm)) { + pr_err("mm claims not to be initialized\n"); + goto out; + } + + if (!drm_mm_clean(&mm)) { + pr_err("mm not empty on creation\n"); + goto out; + } + + /* After creation, it should all be one massive hole */ + if (!assert_one_hole(&mm, 0, size)) { + ret = -EINVAL; + goto out; + } + + memset(&tmp, 0, sizeof(tmp)); + tmp.start = 0; + tmp.size = size; + ret = drm_mm_reserve_node(&mm, &tmp); + if (ret) { + pr_err("failed to reserve whole drm_mm\n"); + goto out; + } + + /* After filling the range entirely, there should be no holes */ + if (!assert_no_holes(&mm)) { + ret = -EINVAL; + goto out; + } + + /* And then after emptying it again, the massive hole should be back */ + drm_mm_remove_node(&tmp); + if (!assert_one_hole(&mm, 0, size)) { + ret = -EINVAL; + goto out; + } + +out: + if (ret) + show_mm(&mm); + drm_mm_takedown(&mm); + return ret; +} + +static int igt_debug(void *ignored) +{ + struct drm_mm mm; + struct drm_mm_node nodes[2]; + int ret; + + /* Create a small drm_mm with a couple of nodes and a few holes, and + * check that the debug iterator doesn't explode over a trivial drm_mm. + */ + + drm_mm_init(&mm, 0, 4096); + + memset(nodes, 0, sizeof(nodes)); + nodes[0].start = 512; + nodes[0].size = 1024; + ret = drm_mm_reserve_node(&mm, &nodes[0]); + if (ret) { + pr_err("failed to reserve node[0] {start=%lld, size=%lld)\n", + nodes[0].start, nodes[0].size); + return ret; + } + + nodes[1].size = 1024; + nodes[1].start = 4096 - 512 - nodes[1].size; + ret = drm_mm_reserve_node(&mm, &nodes[1]); + if (ret) { + pr_err("failed to reserve node[1] {start=%lld, size=%lld)\n", + nodes[1].start, nodes[1].size); + return ret; + } + + show_mm(&mm); + return 0; +} + +static struct drm_mm_node *set_node(struct drm_mm_node *node, + u64 start, u64 size) +{ + node->start = start; + node->size = size; + return node; +} + +static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node) +{ + int err; + + err = drm_mm_reserve_node(mm, node); + if (likely(err == -ENOSPC)) + return true; + + if (!err) { + pr_err("impossible reserve succeeded, node %llu + %llu\n", + node->start, node->size); + drm_mm_remove_node(node); + } else { + pr_err("impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n", + err, -ENOSPC, node->start, node->size); + } + return false; +} + +static bool check_reserve_boundaries(struct drm_mm *mm, + unsigned int count, + u64 size) +{ + const struct boundary { + u64 start, size; + const char *name; + } boundaries[] = { +#define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" } + B(0, 0), + B(-size, 0), + B(size, 0), + B(size * count, 0), + B(-size, size), + B(-size, -size), + B(-size, 2*size), + B(0, -size), + B(size, -size), + B(count*size, size), + B(count*size, -size), + B(count*size, count*size), + B(count*size, -count*size), + B(count*size, -(count+1)*size), + B((count+1)*size, size), + B((count+1)*size, -size), + B((count+1)*size, -2*size), +#undef B + }; + struct drm_mm_node tmp = {}; + int n; + + for (n = 0; n < ARRAY_SIZE(boundaries); n++) { + if (!expect_reserve_fail(mm, + set_node(&tmp, + boundaries[n].start, + boundaries[n].size))) { + pr_err("boundary[%d:%s] failed, count=%u, size=%lld\n", + n, boundaries[n].name, count, size); + return false; + } + } + + return true; +} + +static int __igt_reserve(unsigned int count, u64 size) +{ + DRM_RND_STATE(prng, random_seed); + struct drm_mm mm; + struct drm_mm_node tmp, *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + int ret, err; + + /* For exercising drm_mm_reserve_node(), we want to check that + * reservations outside of the drm_mm range are rejected, and to + * overlapping and otherwise already occupied ranges. Afterwards, + * the tree and nodes should be intact. + */ + + DRM_MM_BUG_ON(!count); + DRM_MM_BUG_ON(!size); + + ret = -ENOMEM; + order = drm_random_order(count, &prng); + if (!order) + goto err; + + nodes = vzalloc(sizeof(*nodes) * count); + if (!nodes) + goto err_order; + + ret = -EINVAL; + drm_mm_init(&mm, 0, count * size); + + if (!check_reserve_boundaries(&mm, count, size)) + goto out; + + for (n = 0; n < count; n++) { + nodes[n].start = order[n] * size; + nodes[n].size = size; + + err = drm_mm_reserve_node(&mm, &nodes[n]); + if (err) { + pr_err("reserve failed, step %d, start %llu\n", + n, nodes[n].start); + ret = err; + goto out; + } + + if (!drm_mm_node_allocated(&nodes[n])) { + pr_err("reserved node not allocated! step %d, start %llu\n", + n, nodes[n].start); + goto out; + } + + if (!expect_reserve_fail(&mm, &nodes[n])) + goto out; + } + + /* After random insertion the nodes should be in order */ + if (!assert_continuous(&mm, size)) + goto out; + + /* Repeated use should then fail */ + drm_random_reorder(order, count, &prng); + for (n = 0; n < count; n++) { + if (!expect_reserve_fail(&mm, + set_node(&tmp, order[n] * size, 1))) + goto out; + + /* Remove and reinsert should work */ + drm_mm_remove_node(&nodes[order[n]]); + err = drm_mm_reserve_node(&mm, &nodes[order[n]]); + if (err) { + pr_err("reserve failed, step %d, start %llu\n", + n, nodes[n].start); + ret = err; + goto out; + } + } + + if (!assert_continuous(&mm, size)) + goto out; + + /* Overlapping use should then fail */ + for (n = 0; n < count; n++) { + if (!expect_reserve_fail(&mm, set_node(&tmp, 0, size*count))) + goto out; + } + for (n = 0; n < count; n++) { + if (!expect_reserve_fail(&mm, + set_node(&tmp, + size * n, + size * (count - n)))) + goto out; + } + + /* Remove several, reinsert, check full */ + for_each_prime_number(n, min(max_prime, count)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + } + + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + err = drm_mm_reserve_node(&mm, node); + if (err) { + pr_err("reserve failed, step %d/%d, start %llu\n", + m, n, node->start); + ret = err; + goto out; + } + } + + o += n; + + if (!assert_continuous(&mm, size)) + goto out; + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + vfree(nodes); +err_order: + kfree(order); +err: + return ret; +} + +static int igt_reserve(void *ignored) +{ + const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); + int n, ret; + + for_each_prime_number_from(n, 1, 54) { + u64 size = BIT_ULL(n); + + ret = __igt_reserve(count, size - 1); + if (ret) + return ret; + + ret = __igt_reserve(count, size); + if (ret) + return ret; + + ret = __igt_reserve(count, size + 1); + if (ret) + return ret; + } + + return 0; +} + +static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node, + u64 size, u64 alignment, unsigned long color, + const struct insert_mode *mode) +{ + int err; + + err = drm_mm_insert_node_generic(mm, node, + size, alignment, color, + mode->mode); + if (err) { + pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n", + size, alignment, color, mode->name, err); + return false; + } + + if (!assert_node(node, mm, size, alignment, color)) { + drm_mm_remove_node(node); + return false; + } + + return true; +} + +static bool expect_insert_fail(struct drm_mm *mm, u64 size) +{ + struct drm_mm_node tmp = {}; + int err; + + err = drm_mm_insert_node(mm, &tmp, size); + if (likely(err == -ENOSPC)) + return true; + + if (!err) { + pr_err("impossible insert succeeded, node %llu + %llu\n", + tmp.start, tmp.size); + drm_mm_remove_node(&tmp); + } else { + pr_err("impossible insert failed with wrong error %d [expected %d], size %llu\n", + err, -ENOSPC, size); + } + return false; +} + +static int __igt_insert(unsigned int count, u64 size, bool replace) +{ + DRM_RND_STATE(prng, random_seed); + const struct insert_mode *mode; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + int ret; + + /* Fill a range with lots of nodes, check it doesn't fail too early */ + + DRM_MM_BUG_ON(!count); + DRM_MM_BUG_ON(!size); + + ret = -ENOMEM; + nodes = vmalloc(count * sizeof(*nodes)); + if (!nodes) + goto err; + + order = drm_random_order(count, &prng); + if (!order) + goto err_nodes; + + ret = -EINVAL; + drm_mm_init(&mm, 0, count * size); + + for (mode = insert_modes; mode->name; mode++) { + for (n = 0; n < count; n++) { + struct drm_mm_node tmp; + + node = replace ? &tmp : &nodes[n]; + memset(node, 0, sizeof(*node)); + if (!expect_insert(&mm, node, size, 0, n, mode)) { + pr_err("%s insert failed, size %llu step %d\n", + mode->name, size, n); + goto out; + } + + if (replace) { + drm_mm_replace_node(&tmp, &nodes[n]); + if (drm_mm_node_allocated(&tmp)) { + pr_err("replaced old-node still allocated! step %d\n", + n); + goto out; + } + + if (!assert_node(&nodes[n], &mm, size, 0, n)) { + pr_err("replaced node did not inherit parameters, size %llu step %d\n", + size, n); + goto out; + } + + if (tmp.start != nodes[n].start) { + pr_err("replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n", + tmp.start, size, + nodes[n].start, nodes[n].size); + goto out; + } + } + } + + /* After random insertion the nodes should be in order */ + if (!assert_continuous(&mm, size)) + goto out; + + /* Repeated use should then fail */ + if (!expect_insert_fail(&mm, size)) + goto out; + + /* Remove one and reinsert, as the only hole it should refill itself */ + for (n = 0; n < count; n++) { + u64 addr = nodes[n].start; + + drm_mm_remove_node(&nodes[n]); + if (!expect_insert(&mm, &nodes[n], size, 0, n, mode)) { + pr_err("%s reinsert failed, size %llu step %d\n", + mode->name, size, n); + goto out; + } + + if (nodes[n].start != addr) { + pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n", + mode->name, n, addr, nodes[n].start); + goto out; + } + + if (!assert_continuous(&mm, size)) + goto out; + } + + /* Remove several, reinsert, check full */ + for_each_prime_number(n, min(max_prime, count)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + } + + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + if (!expect_insert(&mm, node, size, 0, n, mode)) { + pr_err("%s multiple reinsert failed, size %llu step %d\n", + mode->name, size, n); + goto out; + } + } + + o += n; + + if (!assert_continuous(&mm, size)) + goto out; + + if (!expect_insert_fail(&mm, size)) + goto out; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +err: + return ret; +} + +static int igt_insert(void *ignored) +{ + const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); + unsigned int n; + int ret; + + for_each_prime_number_from(n, 1, 54) { + u64 size = BIT_ULL(n); + + ret = __igt_insert(count, size - 1, false); + if (ret) + return ret; + + ret = __igt_insert(count, size, false); + if (ret) + return ret; + + ret = __igt_insert(count, size + 1, false); + } + + return 0; +} + +static int igt_replace(void *ignored) +{ + const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); + unsigned int n; + int ret; + + /* Reuse igt_insert to exercise replacement by inserting a dummy node, + * then replacing it with the intended node. We want to check that + * the tree is intact and all the information we need is carried + * across to the target node. + */ + + for_each_prime_number_from(n, 1, 54) { + u64 size = BIT_ULL(n); + + ret = __igt_insert(count, size - 1, true); + if (ret) + return ret; + + ret = __igt_insert(count, size, true); + if (ret) + return ret; + + ret = __igt_insert(count, size + 1, true); + } + + return 0; +} + +static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node, + u64 size, u64 alignment, unsigned long color, + u64 range_start, u64 range_end, + const struct insert_mode *mode) +{ + int err; + + err = drm_mm_insert_node_in_range(mm, node, + size, alignment, color, + range_start, range_end, + mode->mode); + if (err) { + pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n", + size, alignment, color, mode->name, + range_start, range_end, err); + return false; + } + + if (!assert_node(node, mm, size, alignment, color)) { + drm_mm_remove_node(node); + return false; + } + + return true; +} + +static bool expect_insert_in_range_fail(struct drm_mm *mm, + u64 size, + u64 range_start, + u64 range_end) +{ + struct drm_mm_node tmp = {}; + int err; + + err = drm_mm_insert_node_in_range(mm, &tmp, + size, 0, 0, + range_start, range_end, + 0); + if (likely(err == -ENOSPC)) + return true; + + if (!err) { + pr_err("impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n", + tmp.start, tmp.size, range_start, range_end); + drm_mm_remove_node(&tmp); + } else { + pr_err("impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n", + err, -ENOSPC, size, range_start, range_end); + } + + return false; +} + +static bool assert_contiguous_in_range(struct drm_mm *mm, + u64 size, + u64 start, + u64 end) +{ + struct drm_mm_node *node; + unsigned int n; + + if (!expect_insert_in_range_fail(mm, size, start, end)) + return false; + + n = div64_u64(start + size - 1, size); + drm_mm_for_each_node(node, mm) { + if (node->start < start || node->start + node->size > end) { + pr_err("node %d out of range, address [%llx + %llu], range [%llx, %llx]\n", + n, node->start, node->start + node->size, start, end); + return false; + } + + if (node->start != n * size) { + pr_err("node %d out of order, expected start %llx, found %llx\n", + n, n * size, node->start); + return false; + } + + if (node->size != size) { + pr_err("node %d has wrong size, expected size %llx, found %llx\n", + n, size, node->size); + return false; + } + + if (drm_mm_hole_follows(node) && + drm_mm_hole_node_end(node) < end) { + pr_err("node %d is followed by a hole!\n", n); + return false; + } + + n++; + } + + drm_mm_for_each_node_in_range(node, mm, 0, start) { + if (node) { + pr_err("node before start: node=%llx+%llu, start=%llx\n", + node->start, node->size, start); + return false; + } + } + + drm_mm_for_each_node_in_range(node, mm, end, U64_MAX) { + if (node) { + pr_err("node after end: node=%llx+%llu, end=%llx\n", + node->start, node->size, end); + return false; + } + } + + return true; +} + +static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end) +{ + const struct insert_mode *mode; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int n, start_n, end_n; + int ret; + + DRM_MM_BUG_ON(!count); + DRM_MM_BUG_ON(!size); + DRM_MM_BUG_ON(end <= start); + + /* Very similar to __igt_insert(), but now instead of populating the + * full range of the drm_mm, we try to fill a small portion of it. + */ + + ret = -ENOMEM; + nodes = vzalloc(count * sizeof(*nodes)); + if (!nodes) + goto err; + + ret = -EINVAL; + drm_mm_init(&mm, 0, count * size); + + start_n = div64_u64(start + size - 1, size); + end_n = div64_u64(end - size, size); + + for (mode = insert_modes; mode->name; mode++) { + for (n = start_n; n <= end_n; n++) { + if (!expect_insert_in_range(&mm, &nodes[n], + size, size, n, + start, end, mode)) { + pr_err("%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n", + mode->name, size, n, + start_n, end_n, + start, end); + goto out; + } + } + + if (!assert_contiguous_in_range(&mm, size, start, end)) { + pr_err("%s: range [%llx, %llx] not full after initialisation, size=%llu\n", + mode->name, start, end, size); + goto out; + } + + /* Remove one and reinsert, it should refill itself */ + for (n = start_n; n <= end_n; n++) { + u64 addr = nodes[n].start; + + drm_mm_remove_node(&nodes[n]); + if (!expect_insert_in_range(&mm, &nodes[n], + size, size, n, + start, end, mode)) { + pr_err("%s reinsert failed, step %d\n", mode->name, n); + goto out; + } + + if (nodes[n].start != addr) { + pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n", + mode->name, n, addr, nodes[n].start); + goto out; + } + } + + if (!assert_contiguous_in_range(&mm, size, start, end)) { + pr_err("%s: range [%llx, %llx] not full after reinsertion, size=%llu\n", + mode->name, start, end, size); + goto out; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + vfree(nodes); +err: + return ret; +} + +static int insert_outside_range(void) +{ + struct drm_mm mm; + const unsigned int start = 1024; + const unsigned int end = 2048; + const unsigned int size = end - start; + + drm_mm_init(&mm, start, size); + + if (!expect_insert_in_range_fail(&mm, 1, 0, start)) + return -EINVAL; + + if (!expect_insert_in_range_fail(&mm, size, + start - size/2, start + (size+1)/2)) + return -EINVAL; + + if (!expect_insert_in_range_fail(&mm, size, + end - (size+1)/2, end + size/2)) + return -EINVAL; + + if (!expect_insert_in_range_fail(&mm, 1, end, end + size)) + return -EINVAL; + + drm_mm_takedown(&mm); + return 0; +} + +static int igt_insert_range(void *ignored) +{ + const unsigned int count = min_t(unsigned int, BIT(13), max_iterations); + unsigned int n; + int ret; + + /* Check that requests outside the bounds of drm_mm are rejected. */ + ret = insert_outside_range(); + if (ret) + return ret; + + for_each_prime_number_from(n, 1, 50) { + const u64 size = BIT_ULL(n); + const u64 max = count * size; + + ret = __igt_insert_range(count, size, 0, max); + if (ret) + return ret; + + ret = __igt_insert_range(count, size, 1, max); + if (ret) + return ret; + + ret = __igt_insert_range(count, size, 0, max - 1); + if (ret) + return ret; + + ret = __igt_insert_range(count, size, 0, max/2); + if (ret) + return ret; + + ret = __igt_insert_range(count, size, max/2, max); + if (ret) + return ret; + + ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1); + if (ret) + return ret; + } + + return 0; +} + +static int igt_align(void *ignored) +{ + const struct insert_mode *mode; + const unsigned int max_count = min(8192u, max_prime); + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int prime; + int ret = -EINVAL; + + /* For each of the possible insertion modes, we pick a few + * arbitrary alignments and check that the inserted node + * meets our requirements. + */ + + nodes = vzalloc(max_count * sizeof(*nodes)); + if (!nodes) + goto err; + + drm_mm_init(&mm, 1, U64_MAX - 2); + + for (mode = insert_modes; mode->name; mode++) { + unsigned int i = 0; + + for_each_prime_number_from(prime, 1, max_count) { + u64 size = next_prime_number(prime); + + if (!expect_insert(&mm, &nodes[i], + size, prime, i, + mode)) { + pr_err("%s insert failed with alignment=%d", + mode->name, prime); + goto out; + } + + i++; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + vfree(nodes); +err: + return ret; +} + +static int igt_align_pot(int max) +{ + struct drm_mm mm; + struct drm_mm_node *node, *next; + int bit; + int ret = -EINVAL; + + /* Check that we can align to the full u64 address space */ + + drm_mm_init(&mm, 1, U64_MAX - 2); + + for (bit = max - 1; bit; bit--) { + u64 align, size; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + ret = -ENOMEM; + goto out; + } + + align = BIT_ULL(bit); + size = BIT_ULL(bit-1) + 1; + if (!expect_insert(&mm, node, + size, align, bit, + &insert_modes[0])) { + pr_err("insert failed with alignment=%llx [%d]", + align, bit); + goto out; + } + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) { + drm_mm_remove_node(node); + kfree(node); + } + drm_mm_takedown(&mm); + return ret; +} + +static int igt_align32(void *ignored) +{ + return igt_align_pot(32); +} + +static int igt_align64(void *ignored) +{ + return igt_align_pot(64); +} + +static void show_scan(const struct drm_mm_scan *scan) +{ + pr_info("scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n", + scan->hit_start, scan->hit_end, + scan->size, scan->alignment, scan->color); +} + +static void show_holes(const struct drm_mm *mm, int count) +{ + u64 hole_start, hole_end; + struct drm_mm_node *hole; + + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { + struct drm_mm_node *next = list_next_entry(hole, node_list); + const char *node1 = NULL, *node2 = NULL; + + if (hole->allocated) + node1 = kasprintf(GFP_KERNEL, + "[%llx + %lld, color=%ld], ", + hole->start, hole->size, hole->color); + + if (next->allocated) + node2 = kasprintf(GFP_KERNEL, + ", [%llx + %lld, color=%ld]", + next->start, next->size, next->color); + + pr_info("%sHole [%llx - %llx, size %lld]%s\n", + node1, + hole_start, hole_end, hole_end - hole_start, + node2); + + kfree(node2); + kfree(node1); + + if (!--count) + break; + } +} + +struct evict_node { + struct drm_mm_node node; + struct list_head link; +}; + +static bool evict_nodes(struct drm_mm_scan *scan, + struct evict_node *nodes, + unsigned int *order, + unsigned int count, + bool use_color, + struct list_head *evict_list) +{ + struct evict_node *e, *en; + unsigned int i; + + for (i = 0; i < count; i++) { + e = &nodes[order ? order[i] : i]; + list_add(&e->link, evict_list); + if (drm_mm_scan_add_block(scan, &e->node)) + break; + } + list_for_each_entry_safe(e, en, evict_list, link) { + if (!drm_mm_scan_remove_block(scan, &e->node)) + list_del(&e->link); + } + if (list_empty(evict_list)) { + pr_err("Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n", + scan->size, count, scan->alignment, scan->color); + return false; + } + + list_for_each_entry(e, evict_list, link) + drm_mm_remove_node(&e->node); + + if (use_color) { + struct drm_mm_node *node; + + while ((node = drm_mm_scan_color_evict(scan))) { + e = container_of(node, typeof(*e), node); + drm_mm_remove_node(&e->node); + list_add(&e->link, evict_list); + } + } else { + if (drm_mm_scan_color_evict(scan)) { + pr_err("drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n"); + return false; + } + } + + return true; +} + +static bool evict_nothing(struct drm_mm *mm, + unsigned int total_size, + struct evict_node *nodes) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + struct drm_mm_node *node; + unsigned int n; + + drm_mm_scan_init(&scan, mm, 1, 0, 0, 0); + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + list_add(&e->link, &evict_list); + drm_mm_scan_add_block(&scan, &e->node); + } + list_for_each_entry(e, &evict_list, link) + drm_mm_scan_remove_block(&scan, &e->node); + + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + + if (!drm_mm_node_allocated(&e->node)) { + pr_err("node[%d] no longer allocated!\n", n); + return false; + } + + e->link.next = NULL; + } + + drm_mm_for_each_node(node, mm) { + e = container_of(node, typeof(*e), node); + e->link.next = &e->link; + } + + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + + if (!e->link.next) { + pr_err("node[%d] no longer connected!\n", n); + return false; + } + } + + return assert_continuous(mm, nodes[0].node.size); +} + +static bool evict_everything(struct drm_mm *mm, + unsigned int total_size, + struct evict_node *nodes) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + unsigned int n; + int err; + + drm_mm_scan_init(&scan, mm, total_size, 0, 0, 0); + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + list_add(&e->link, &evict_list); + if (drm_mm_scan_add_block(&scan, &e->node)) + break; + } + + err = 0; + list_for_each_entry(e, &evict_list, link) { + if (!drm_mm_scan_remove_block(&scan, &e->node)) { + if (!err) { + pr_err("Node %lld not marked for eviction!\n", + e->node.start); + err = -EINVAL; + } + } + } + if (err) + return false; + + list_for_each_entry(e, &evict_list, link) + drm_mm_remove_node(&e->node); + + if (!assert_one_hole(mm, 0, total_size)) + return false; + + list_for_each_entry(e, &evict_list, link) { + err = drm_mm_reserve_node(mm, &e->node); + if (err) { + pr_err("Failed to reinsert node after eviction: start=%llx\n", + e->node.start); + return false; + } + } + + return assert_continuous(mm, nodes[0].node.size); +} + +static int evict_something(struct drm_mm *mm, + u64 range_start, u64 range_end, + struct evict_node *nodes, + unsigned int *order, + unsigned int count, + unsigned int size, + unsigned int alignment, + const struct insert_mode *mode) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + struct drm_mm_node tmp; + int err; + + drm_mm_scan_init_with_range(&scan, mm, + size, alignment, 0, + range_start, range_end, + mode->mode); + if (!evict_nodes(&scan, + nodes, order, count, false, + &evict_list)) + return -EINVAL; + + memset(&tmp, 0, sizeof(tmp)); + err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0, + DRM_MM_INSERT_EVICT); + if (err) { + pr_err("Failed to insert into eviction hole: size=%d, align=%d\n", + size, alignment); + show_scan(&scan); + show_holes(mm, 3); + return err; + } + + if (tmp.start < range_start || tmp.start + tmp.size > range_end) { + pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", + tmp.start, tmp.size, range_start, range_end); + err = -EINVAL; + } + + if (!assert_node(&tmp, mm, size, alignment, 0) || + drm_mm_hole_follows(&tmp)) { + pr_err("Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n", + tmp.size, size, + alignment, misalignment(&tmp, alignment), + tmp.start, drm_mm_hole_follows(&tmp)); + err = -EINVAL; + } + + drm_mm_remove_node(&tmp); + if (err) + return err; + + list_for_each_entry(e, &evict_list, link) { + err = drm_mm_reserve_node(mm, &e->node); + if (err) { + pr_err("Failed to reinsert node after eviction: start=%llx\n", + e->node.start); + return err; + } + } + + if (!assert_continuous(mm, nodes[0].node.size)) { + pr_err("range is no longer continuous\n"); + return -EINVAL; + } + + return 0; +} + +static int igt_evict(void *ignored) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int size = 8192; + const struct insert_mode *mode; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + int ret, err; + + /* Here we populate a full drm_mm and then try and insert a new node + * by evicting other nodes in a random order. The drm_mm_scan should + * pick the first matching hole it finds from the random list. We + * repeat that for different allocation strategies, alignments and + * sizes to try and stress the hole finder. + */ + + ret = -ENOMEM; + nodes = vzalloc(size * sizeof(*nodes)); + if (!nodes) + goto err; + + order = drm_random_order(size, &prng); + if (!order) + goto err_nodes; + + ret = -EINVAL; + drm_mm_init(&mm, 0, size); + for (n = 0; n < size; n++) { + err = drm_mm_insert_node(&mm, &nodes[n].node, 1); + if (err) { + pr_err("insert failed, step %d\n", n); + ret = err; + goto out; + } + } + + /* First check that using the scanner doesn't break the mm */ + if (!evict_nothing(&mm, size, nodes)) { + pr_err("evict_nothing() failed\n"); + goto out; + } + if (!evict_everything(&mm, size, nodes)) { + pr_err("evict_everything() failed\n"); + goto out; + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= size; n <<= 1) { + drm_random_reorder(order, size, &prng); + err = evict_something(&mm, 0, U64_MAX, + nodes, order, size, + n, 1, + mode); + if (err) { + pr_err("%s evict_something(size=%u) failed\n", + mode->name, n); + ret = err; + goto out; + } + } + + for (n = 1; n < size; n <<= 1) { + drm_random_reorder(order, size, &prng); + err = evict_something(&mm, 0, U64_MAX, + nodes, order, size, + size/2, n, + mode); + if (err) { + pr_err("%s evict_something(size=%u, alignment=%u) failed\n", + mode->name, size/2, n); + ret = err; + goto out; + } + } + + for_each_prime_number_from(n, 1, min(size, max_prime)) { + unsigned int nsize = (size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, size, &prng); + err = evict_something(&mm, 0, U64_MAX, + nodes, order, size, + nsize, n, + mode); + if (err) { + pr_err("%s evict_something(size=%u, alignment=%u) failed\n", + mode->name, nsize, n); + ret = err; + goto out; + } + } + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +err: + return ret; +} + +static int igt_evict_range(void *ignored) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int size = 8192; + const unsigned int range_size = size / 2; + const unsigned int range_start = size / 4; + const unsigned int range_end = range_start + range_size; + const struct insert_mode *mode; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + int ret, err; + + /* Like igt_evict() but now we are limiting the search to a + * small portion of the full drm_mm. + */ + + ret = -ENOMEM; + nodes = vzalloc(size * sizeof(*nodes)); + if (!nodes) + goto err; + + order = drm_random_order(size, &prng); + if (!order) + goto err_nodes; + + ret = -EINVAL; + drm_mm_init(&mm, 0, size); + for (n = 0; n < size; n++) { + err = drm_mm_insert_node(&mm, &nodes[n].node, 1); + if (err) { + pr_err("insert failed, step %d\n", n); + ret = err; + goto out; + } + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= range_size; n <<= 1) { + drm_random_reorder(order, size, &prng); + err = evict_something(&mm, range_start, range_end, + nodes, order, size, + n, 1, + mode); + if (err) { + pr_err("%s evict_something(size=%u) failed with range [%u, %u]\n", + mode->name, n, range_start, range_end); + goto out; + } + } + + for (n = 1; n <= range_size; n <<= 1) { + drm_random_reorder(order, size, &prng); + err = evict_something(&mm, range_start, range_end, + nodes, order, size, + range_size/2, n, + mode); + if (err) { + pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", + mode->name, range_size/2, n, range_start, range_end); + goto out; + } + } + + for_each_prime_number_from(n, 1, min(range_size, max_prime)) { + unsigned int nsize = (range_size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, size, &prng); + err = evict_something(&mm, range_start, range_end, + nodes, order, size, + nsize, n, + mode); + if (err) { + pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", + mode->name, nsize, n, range_start, range_end); + goto out; + } + } + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +err: + return ret; +} + +static unsigned int node_index(const struct drm_mm_node *node) +{ + return div64_u64(node->start, node->size); +} + +static int igt_topdown(void *ignored) +{ + const struct insert_mode *topdown = &insert_modes[TOPDOWN]; + DRM_RND_STATE(prng, random_seed); + const unsigned int count = 8192; + unsigned int size; + unsigned long *bitmap = NULL; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + int ret; + + /* When allocating top-down, we expect to be returned a node + * from a suitable hole at the top of the drm_mm. We check that + * the returned node does match the highest available slot. + */ + + ret = -ENOMEM; + nodes = vzalloc(count * sizeof(*nodes)); + if (!nodes) + goto err; + + bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), + GFP_TEMPORARY); + if (!bitmap) + goto err_nodes; + + order = drm_random_order(count, &prng); + if (!order) + goto err_bitmap; + + ret = -EINVAL; + for (size = 1; size <= 64; size <<= 1) { + drm_mm_init(&mm, 0, size*count); + for (n = 0; n < count; n++) { + if (!expect_insert(&mm, &nodes[n], + size, 0, n, + topdown)) { + pr_err("insert failed, size %u step %d\n", size, n); + goto out; + } + + if (drm_mm_hole_follows(&nodes[n])) { + pr_err("hole after topdown insert %d, start=%llx\n, size=%u", + n, nodes[n].start, size); + goto out; + } + + if (!assert_one_hole(&mm, 0, size*(count - n - 1))) + goto out; + } + + if (!assert_continuous(&mm, size)) + goto out; + + drm_random_reorder(order, count, &prng); + for_each_prime_number_from(n, 1, min(count, max_prime)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + __set_bit(node_index(node), bitmap); + } + + for (m = 0; m < n; m++) { + unsigned int last; + + node = &nodes[order[(o + m) % count]]; + if (!expect_insert(&mm, node, + size, 0, 0, + topdown)) { + pr_err("insert failed, step %d/%d\n", m, n); + goto out; + } + + if (drm_mm_hole_follows(node)) { + pr_err("hole after topdown insert %d/%d, start=%llx\n", + m, n, node->start); + goto out; + } + + last = find_last_bit(bitmap, count); + if (node_index(node) != last) { + pr_err("node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n", + m, n, size, last, node_index(node)); + goto out; + } + + __clear_bit(last, bitmap); + } + + DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); + + o += n; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_bitmap: + kfree(bitmap); +err_nodes: + vfree(nodes); +err: + return ret; +} + +static int igt_bottomup(void *ignored) +{ + const struct insert_mode *bottomup = &insert_modes[BOTTOMUP]; + DRM_RND_STATE(prng, random_seed); + const unsigned int count = 8192; + unsigned int size; + unsigned long *bitmap; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + int ret; + + /* Like igt_topdown, but instead of searching for the last hole, + * we search for the first. + */ + + ret = -ENOMEM; + nodes = vzalloc(count * sizeof(*nodes)); + if (!nodes) + goto err; + + bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), + GFP_TEMPORARY); + if (!bitmap) + goto err_nodes; + + order = drm_random_order(count, &prng); + if (!order) + goto err_bitmap; + + ret = -EINVAL; + for (size = 1; size <= 64; size <<= 1) { + drm_mm_init(&mm, 0, size*count); + for (n = 0; n < count; n++) { + if (!expect_insert(&mm, &nodes[n], + size, 0, n, + bottomup)) { + pr_err("bottomup insert failed, size %u step %d\n", size, n); + goto out; + } + + if (!assert_one_hole(&mm, size*(n + 1), size*count)) + goto out; + } + + if (!assert_continuous(&mm, size)) + goto out; + + drm_random_reorder(order, count, &prng); + for_each_prime_number_from(n, 1, min(count, max_prime)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + __set_bit(node_index(node), bitmap); + } + + for (m = 0; m < n; m++) { + unsigned int first; + + node = &nodes[order[(o + m) % count]]; + if (!expect_insert(&mm, node, + size, 0, 0, + bottomup)) { + pr_err("insert failed, step %d/%d\n", m, n); + goto out; + } + + first = find_first_bit(bitmap, count); + if (node_index(node) != first) { + pr_err("node %d/%d not inserted into bottom hole, expected %d, found %d\n", + m, n, first, node_index(node)); + goto out; + } + __clear_bit(first, bitmap); + } + + DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); + + o += n; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_bitmap: + kfree(bitmap); +err_nodes: + vfree(nodes); +err: + return ret; +} + +static void separate_adjacent_colors(const struct drm_mm_node *node, + unsigned long color, + u64 *start, + u64 *end) +{ + if (node->allocated && node->color != color) + ++*start; + + node = list_next_entry(node, node_list); + if (node->allocated && node->color != color) + --*end; +} + +static bool colors_abutt(const struct drm_mm_node *node) +{ + if (!drm_mm_hole_follows(node) && + list_next_entry(node, node_list)->allocated) { + pr_err("colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n", + node->color, node->start, node->size, + list_next_entry(node, node_list)->color, + list_next_entry(node, node_list)->start, + list_next_entry(node, node_list)->size); + return true; + } + + return false; +} + +static int igt_color(void *ignored) +{ + const unsigned int count = min(4096u, max_iterations); + const struct insert_mode *mode; + struct drm_mm mm; + struct drm_mm_node *node, *nn; + unsigned int n; + int ret = -EINVAL, err; + + /* Color adjustment complicates everything. First we just check + * that when we insert a node we apply any color_adjustment callback. + * The callback we use should ensure that there is a gap between + * any two nodes, and so after each insertion we check that those + * holes are inserted and that they are preserved. + */ + + drm_mm_init(&mm, 0, U64_MAX); + + for (n = 1; n <= count; n++) { + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + ret = -ENOMEM; + goto out; + } + + if (!expect_insert(&mm, node, + n, 0, n, + &insert_modes[0])) { + pr_err("insert failed, step %d\n", n); + kfree(node); + goto out; + } + } + + drm_mm_for_each_node_safe(node, nn, &mm) { + if (node->color != node->size) { + pr_err("invalid color stored: expected %lld, found %ld\n", + node->size, node->color); + + goto out; + } + + drm_mm_remove_node(node); + kfree(node); + } + + /* Now, let's start experimenting with applying a color callback */ + mm.color_adjust = separate_adjacent_colors; + for (mode = insert_modes; mode->name; mode++) { + u64 last; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + ret = -ENOMEM; + goto out; + } + + node->size = 1 + 2*count; + node->color = node->size; + + err = drm_mm_reserve_node(&mm, node); + if (err) { + pr_err("initial reserve failed!\n"); + ret = err; + goto out; + } + + last = node->start + node->size; + + for (n = 1; n <= count; n++) { + int rem; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + ret = -ENOMEM; + goto out; + } + + node->start = last; + node->size = n + count; + node->color = node->size; + + err = drm_mm_reserve_node(&mm, node); + if (err != -ENOSPC) { + pr_err("reserve %d did not report color overlap! err=%d\n", + n, err); + goto out; + } + + node->start += n + 1; + rem = misalignment(node, n + count); + node->start += n + count - rem; + + err = drm_mm_reserve_node(&mm, node); + if (err) { + pr_err("reserve %d failed, err=%d\n", n, err); + ret = err; + goto out; + } + + last = node->start + node->size; + } + + for (n = 1; n <= count; n++) { + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + ret = -ENOMEM; + goto out; + } + + if (!expect_insert(&mm, node, + n, n, n, + mode)) { + pr_err("%s insert failed, step %d\n", + mode->name, n); + kfree(node); + goto out; + } + } + + drm_mm_for_each_node_safe(node, nn, &mm) { + u64 rem; + + if (node->color != node->size) { + pr_err("%s invalid color stored: expected %lld, found %ld\n", + mode->name, node->size, node->color); + + goto out; + } + + if (colors_abutt(node)) + goto out; + + div64_u64_rem(node->start, node->size, &rem); + if (rem) { + pr_err("%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n", + mode->name, node->start, node->size, rem); + goto out; + } + + drm_mm_remove_node(node); + kfree(node); + } + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, nn, &mm) { + drm_mm_remove_node(node); + kfree(node); + } + drm_mm_takedown(&mm); + return ret; +} + +static int evict_color(struct drm_mm *mm, + u64 range_start, u64 range_end, + struct evict_node *nodes, + unsigned int *order, + unsigned int count, + unsigned int size, + unsigned int alignment, + unsigned long color, + const struct insert_mode *mode) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + struct drm_mm_node tmp; + int err; + + drm_mm_scan_init_with_range(&scan, mm, + size, alignment, color, + range_start, range_end, + mode->mode); + if (!evict_nodes(&scan, + nodes, order, count, true, + &evict_list)) + return -EINVAL; + + memset(&tmp, 0, sizeof(tmp)); + err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color, + DRM_MM_INSERT_EVICT); + if (err) { + pr_err("Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n", + size, alignment, color, err); + show_scan(&scan); + show_holes(mm, 3); + return err; + } + + if (tmp.start < range_start || tmp.start + tmp.size > range_end) { + pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", + tmp.start, tmp.size, range_start, range_end); + err = -EINVAL; + } + + if (colors_abutt(&tmp)) + err = -EINVAL; + + if (!assert_node(&tmp, mm, size, alignment, color)) { + pr_err("Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n", + tmp.size, size, + alignment, misalignment(&tmp, alignment), tmp.start); + err = -EINVAL; + } + + drm_mm_remove_node(&tmp); + if (err) + return err; + + list_for_each_entry(e, &evict_list, link) { + err = drm_mm_reserve_node(mm, &e->node); + if (err) { + pr_err("Failed to reinsert node after eviction: start=%llx\n", + e->node.start); + return err; + } + } + + return 0; +} + +static int igt_color_evict(void *ignored) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int total_size = min(8192u, max_iterations); + const struct insert_mode *mode; + unsigned long color = 0; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + int ret, err; + + /* Check that the drm_mm_scan also honours color adjustment when + * choosing its victims to create a hole. Our color_adjust does not + * allow two nodes to be placed together without an intervening hole + * enlarging the set of victims that must be evicted. + */ + + ret = -ENOMEM; + nodes = vzalloc(total_size * sizeof(*nodes)); + if (!nodes) + goto err; + + order = drm_random_order(total_size, &prng); + if (!order) + goto err_nodes; + + ret = -EINVAL; + drm_mm_init(&mm, 0, 2*total_size - 1); + mm.color_adjust = separate_adjacent_colors; + for (n = 0; n < total_size; n++) { + if (!expect_insert(&mm, &nodes[n].node, + 1, 0, color++, + &insert_modes[0])) { + pr_err("insert failed, step %d\n", n); + goto out; + } + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= total_size; n <<= 1) { + drm_random_reorder(order, total_size, &prng); + err = evict_color(&mm, 0, U64_MAX, + nodes, order, total_size, + n, 1, color++, + mode); + if (err) { + pr_err("%s evict_color(size=%u) failed\n", + mode->name, n); + goto out; + } + } + + for (n = 1; n < total_size; n <<= 1) { + drm_random_reorder(order, total_size, &prng); + err = evict_color(&mm, 0, U64_MAX, + nodes, order, total_size, + total_size/2, n, color++, + mode); + if (err) { + pr_err("%s evict_color(size=%u, alignment=%u) failed\n", + mode->name, total_size/2, n); + goto out; + } + } + + for_each_prime_number_from(n, 1, min(total_size, max_prime)) { + unsigned int nsize = (total_size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, total_size, &prng); + err = evict_color(&mm, 0, U64_MAX, + nodes, order, total_size, + nsize, n, color++, + mode); + if (err) { + pr_err("%s evict_color(size=%u, alignment=%u) failed\n", + mode->name, nsize, n); + goto out; + } + } + } + + ret = 0; +out: + if (ret) + show_mm(&mm); + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +err: + return ret; +} + +static int igt_color_evict_range(void *ignored) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int total_size = 8192; + const unsigned int range_size = total_size / 2; + const unsigned int range_start = total_size / 4; + const unsigned int range_end = range_start + range_size; + const struct insert_mode *mode; + unsigned long color = 0; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + int ret, err; + + /* Like igt_color_evict(), but limited to small portion of the full + * drm_mm range. + */ + + ret = -ENOMEM; + nodes = vzalloc(total_size * sizeof(*nodes)); + if (!nodes) + goto err; + + order = drm_random_order(total_size, &prng); + if (!order) + goto err_nodes; + + ret = -EINVAL; + drm_mm_init(&mm, 0, 2*total_size - 1); + mm.color_adjust = separate_adjacent_colors; + for (n = 0; n < total_size; n++) { + if (!expect_insert(&mm, &nodes[n].node, + 1, 0, color++, + &insert_modes[0])) { + pr_err("insert failed, step %d\n", n); + goto out; + } + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= range_size; n <<= 1) { + drm_random_reorder(order, range_size, &prng); + err = evict_color(&mm, range_start, range_end, + nodes, order, total_size, + n, 1, color++, + mode); + if (err) { + pr_err("%s evict_color(size=%u) failed for range [%x, %x]\n", + mode->name, n, range_start, range_end); + goto out; + } + } + + for (n = 1; n < range_size; n <<= 1) { + drm_random_reorder(order, total_size, &prng); + err = evict_color(&mm, range_start, range_end, + nodes, order, total_size, + range_size/2, n, color++, + mode); + if (err) { + pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", + mode->name, total_size/2, n, range_start, range_end); + goto out; + } + } + + for_each_prime_number_from(n, 1, min(range_size, max_prime)) { + unsigned int nsize = (range_size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, total_size, &prng); + err = evict_color(&mm, range_start, range_end, + nodes, order, total_size, + nsize, n, color++, + mode); + if (err) { + pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", + mode->name, nsize, n, range_start, range_end); + goto out; + } + } + } + + ret = 0; +out: + if (ret) + show_mm(&mm); + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +err: + return ret; +} + +#include "drm_selftest.c" + +static int __init test_drm_mm_init(void) +{ + int err; + + while (!random_seed) + random_seed = get_random_int(); + + pr_info("Testing DRM range manger (struct drm_mm), with random_seed=0x%x max_iterations=%u max_prime=%u\n", + random_seed, max_iterations, max_prime); + err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); + + return err > 0 ? 0 : err; +} + +static void __exit test_drm_mm_exit(void) +{ +} + +module_init(test_drm_mm_init); +module_exit(test_drm_mm_exit); + +module_param(random_seed, uint, 0400); +module_param(max_iterations, uint, 0400); +module_param(max_prime, uint, 0400); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index dddbdd62bed0..445476551695 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -174,7 +174,7 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) if (scrtc->started) return; - format = shmob_drm_format_info(crtc->primary->fb->pixel_format); + format = shmob_drm_format_info(crtc->primary->fb->format->format); if (WARN_ON(format == NULL)) return; @@ -376,10 +376,10 @@ static int shmob_drm_crtc_mode_set(struct drm_crtc *crtc, const struct shmob_drm_format_info *format; void *cache; - format = shmob_drm_format_info(crtc->primary->fb->pixel_format); + format = shmob_drm_format_info(crtc->primary->fb->format->format); if (format == NULL) { dev_dbg(sdev->dev, "mode_set: unsupported format %08x\n", - crtc->primary->fb->pixel_format); + crtc->primary->fb->format->format); return -EINVAL; } diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h index 38ed4ff8aaf2..818b31549ddc 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h @@ -16,6 +16,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> struct backlight_device; struct shmob_drm_device; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 38dd55f4af81..33cec3d42389 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -104,7 +104,7 @@ static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev, * DRM operations */ -static int shmob_drm_unload(struct drm_device *dev) +static void shmob_drm_unload(struct drm_device *dev) { drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); @@ -112,8 +112,6 @@ static int shmob_drm_unload(struct drm_device *dev) drm_irq_uninstall(dev); dev->dev_private = NULL; - - return 0; } static int shmob_drm_load(struct drm_device *dev, unsigned long flags) diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c index 1805bb23b113..2023a93cee2b 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c @@ -183,10 +183,10 @@ shmob_drm_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, struct shmob_drm_device *sdev = plane->dev->dev_private; const struct shmob_drm_format_info *format; - format = shmob_drm_format_info(fb->pixel_format); + format = shmob_drm_format_info(fb->format->format); if (format == NULL) { dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n", - fb->pixel_format); + fb->format->format); return -EINVAL; } diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index a836451920f0..7f05da13ea5e 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -54,15 +54,13 @@ static int sis_driver_load(struct drm_device *dev, unsigned long chipset) return 0; } -static int sis_driver_unload(struct drm_device *dev) +static void sis_driver_unload(struct drm_device *dev) { drm_sis_private_t *dev_priv = dev->dev_private; idr_destroy(&dev_priv->object_idr); kfree(dev_priv); - - return 0; } static const struct file_operations sis_driver_fops = { diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 03defda77766..1622db24cd39 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -109,8 +109,7 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, if (pool == AGP_TYPE) { retval = drm_mm_insert_node(&dev_priv->agp_mm, &item->mm_node, - mem->size, 0, - DRM_MM_SEARCH_DEFAULT); + mem->size); offset = item->mm_node.start; } else { #if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) @@ -122,8 +121,7 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, #else retval = drm_mm_insert_node(&dev_priv->vram_mm, &item->mm_node, - mem->size, 0, - DRM_MM_SEARCH_DEFAULT); + mem->size); offset = item->mm_node.start; #endif } diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index d20f7c0b4eac..c35db12435c3 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -13,7 +13,6 @@ sti-drm-y := \ sti_dvo.o \ sti_awg_utils.o \ sti_vtg.o \ - sti_vtac.o \ sti_hda.o \ sti_tvout.o \ sti_hqvdp.o \ diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index e992bed98dcb..d45a4335df5d 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -134,21 +134,6 @@ sti_crtc_mode_set_nofb(struct drm_crtc *crtc) sti_crtc_mode_set(crtc, &crtc->state->adjusted_mode); } -static void sti_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ - struct sti_mixer *mixer = to_sti_mixer(crtc); - - if (crtc->state->event) { - crtc->state->event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - mixer->pending_event = crtc->state->event; - crtc->state->event = NULL; - } -} - static void sti_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { @@ -156,6 +141,8 @@ static void sti_crtc_atomic_flush(struct drm_crtc *crtc, struct sti_mixer *mixer = to_sti_mixer(crtc); struct sti_compositor *compo = dev_get_drvdata(mixer->dev); struct drm_plane *p; + struct drm_pending_vblank_event *event; + unsigned long flags; DRM_DEBUG_DRIVER("\n"); @@ -220,13 +207,24 @@ static void sti_crtc_atomic_flush(struct drm_crtc *crtc, break; } } + + event = crtc->state->event; + if (event) { + crtc->state->event = NULL; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + } } static const struct drm_crtc_helper_funcs sti_crtc_helper_funcs = { .enable = sti_crtc_enable, .disable = sti_crtc_disabling, .mode_set_nofb = sti_crtc_mode_set_nofb, - .atomic_begin = sti_crtc_atomic_begin, .atomic_flush = sti_crtc_atomic_flush, }; @@ -250,7 +248,6 @@ int sti_crtc_vblank_cb(struct notifier_block *nb, struct sti_compositor *compo; struct drm_crtc *crtc = data; struct sti_mixer *mixer; - unsigned long flags; struct sti_private *priv; unsigned int pipe; @@ -267,14 +264,6 @@ int sti_crtc_vblank_cb(struct notifier_block *nb, drm_crtc_handle_vblank(crtc); - spin_lock_irqsave(&crtc->dev->event_lock, flags); - if (mixer->pending_event) { - drm_crtc_send_vblank_event(crtc, mixer->pending_event); - drm_crtc_vblank_put(crtc); - mixer->pending_event = NULL; - } - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); - if (mixer->status == STI_MIXER_DISABLING) { struct drm_plane *p; @@ -317,19 +306,12 @@ void sti_crtc_disable_vblank(struct drm_device *drm_dev, unsigned int pipe) struct sti_private *priv = drm_dev->dev_private; struct sti_compositor *compo = priv->compo; struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe]; - struct drm_crtc *crtc = &compo->mixer[pipe]->drm_crtc; struct sti_vtg *vtg = compo->vtg[pipe]; DRM_DEBUG_DRIVER("\n"); if (sti_vtg_unregister_client(vtg, vtg_vblank_nb)) DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); - - /* free the resources of the pending requests */ - if (compo->mixer[pipe]->pending_event) { - drm_crtc_vblank_put(crtc); - compo->mixer[pipe]->pending_event = NULL; - } } static int sti_crtc_late_register(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index ff71e25ab5bf..20fc0fbfa849 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -58,7 +58,9 @@ static int sti_drm_fps_set(void *data, u64 val) list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { struct sti_plane *plane = to_sti_plane(p); + memset(&plane->fps_info, 0, sizeof(plane->fps_info)); plane->fps_info.output = (val >> i) & 1; + i++; } @@ -89,38 +91,9 @@ static struct drm_info_list sti_drm_dbg_list[] = { {"fps_get", sti_drm_fps_dbg_show, 0}, }; -static int sti_drm_debugfs_create(struct dentry *root, - struct drm_minor *minor, - const char *name, - const struct file_operations *fops) -{ - struct drm_device *dev = minor->dev; - struct drm_info_node *node; - struct dentry *ent; - - ent = debugfs_create_file(name, S_IRUGO | S_IWUSR, root, dev, fops); - if (IS_ERR(ent)) - return PTR_ERR(ent); - - node = kmalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - debugfs_remove(ent); - return -ENOMEM; - } - - node->minor = minor; - node->dent = ent; - node->info_ent = (void *)fops; - - mutex_lock(&minor->debugfs_lock); - list_add(&node->list, &minor->debugfs_list); - mutex_unlock(&minor->debugfs_lock); - - return 0; -} - static int sti_drm_dbg_init(struct drm_minor *minor) { + struct dentry *dentry; int ret; ret = drm_debugfs_create_files(sti_drm_dbg_list, @@ -129,10 +102,13 @@ static int sti_drm_dbg_init(struct drm_minor *minor) if (ret) goto err; - ret = sti_drm_debugfs_create(minor->debugfs_root, minor, "fps_show", + dentry = debugfs_create_file("fps_show", S_IRUGO | S_IWUSR, + minor->debugfs_root, minor->dev, &sti_drm_fps_fops); - if (ret) + if (!dentry) { + ret = -ENOMEM; goto err; + } DRM_INFO("%s: debugfs installed\n", DRIVER_NAME); return 0; @@ -141,61 +117,6 @@ err: return ret; } -static void sti_drm_dbg_cleanup(struct drm_minor *minor) -{ - drm_debugfs_remove_files(sti_drm_dbg_list, - ARRAY_SIZE(sti_drm_dbg_list), minor); - - drm_debugfs_remove_files((struct drm_info_list *)&sti_drm_fps_fops, - 1, minor); -} - -static void sti_atomic_schedule(struct sti_private *private, - struct drm_atomic_state *state) -{ - private->commit.state = state; - schedule_work(&private->commit.work); -} - -static void sti_atomic_complete(struct sti_private *private, - struct drm_atomic_state *state) -{ - struct drm_device *drm = private->drm_dev; - - /* - * Everything below can be run asynchronously without the need to grab - * any modeset locks at all under one condition: It must be guaranteed - * that the asynchronous work has either been cancelled (if the driver - * supports it, which at least requires that the framebuffers get - * cleaned up with drm_atomic_helper_cleanup_planes()) or completed - * before the new state gets committed on the software side with - * drm_atomic_helper_swap_state(). - * - * This scheme allows new atomic state updates to be prepared and - * checked in parallel to the asynchronous completion of the previous - * update. Which is important since compositors need to figure out the - * composition of the next frame right after having submitted the - * current layout. - */ - - drm_atomic_helper_commit_modeset_disables(drm, state); - drm_atomic_helper_commit_planes(drm, state, 0); - drm_atomic_helper_commit_modeset_enables(drm, state); - - drm_atomic_helper_wait_for_vblanks(drm, state); - - drm_atomic_helper_cleanup_planes(drm, state); - drm_atomic_state_put(state); -} - -static void sti_atomic_work(struct work_struct *work) -{ - struct sti_private *private = container_of(work, - struct sti_private, commit.work); - - sti_atomic_complete(private, private->commit.state); -} - static int sti_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { @@ -216,62 +137,18 @@ static int sti_atomic_check(struct drm_device *dev, return ret; } -static int sti_atomic_commit(struct drm_device *drm, - struct drm_atomic_state *state, bool nonblock) -{ - struct sti_private *private = drm->dev_private; - int err; - - err = drm_atomic_helper_prepare_planes(drm, state); - if (err) - return err; - - /* serialize outstanding nonblocking commits */ - mutex_lock(&private->commit.lock); - flush_work(&private->commit.work); - - /* - * This is the point of no return - everything below never fails except - * when the hw goes bonghits. Which means we can commit the new state on - * the software side now. - */ - - drm_atomic_helper_swap_state(state, true); - - drm_atomic_state_get(state); - if (nonblock) - sti_atomic_schedule(private, state); - else - sti_atomic_complete(private, state); - - mutex_unlock(&private->commit.lock); - return 0; -} - static void sti_output_poll_changed(struct drm_device *ddev) { struct sti_private *private = ddev->dev_private; - if (!ddev->mode_config.num_connector) - return; - - if (private->fbdev) { - drm_fbdev_cma_hotplug_event(private->fbdev); - return; - } - - private->fbdev = drm_fbdev_cma_init(ddev, 32, - ddev->mode_config.num_crtc, - ddev->mode_config.num_connector); - if (IS_ERR(private->fbdev)) - private->fbdev = NULL; + drm_fbdev_cma_hotplug_event(private->fbdev); } static const struct drm_mode_config_funcs sti_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = sti_output_poll_changed, .atomic_check = sti_atomic_check, - .atomic_commit = sti_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, }; static void sti_mode_config_init(struct drm_device *dev) @@ -326,7 +203,6 @@ static struct drm_driver sti_driver = { .gem_prime_mmap = drm_gem_cma_prime_mmap, .debugfs_init = sti_drm_dbg_init, - .debugfs_cleanup = sti_drm_dbg_cleanup, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -352,9 +228,6 @@ static int sti_init(struct drm_device *ddev) dev_set_drvdata(ddev->dev, ddev); private->drm_dev = ddev; - mutex_init(&private->commit.lock); - INIT_WORK(&private->commit.work, sti_atomic_work); - drm_mode_config_init(ddev); sti_mode_config_init(ddev); @@ -375,6 +248,7 @@ static void sti_cleanup(struct drm_device *ddev) drm_kms_helper_poll_fini(ddev); drm_vblank_cleanup(ddev); + component_unbind_all(ddev->dev, ddev); kfree(private); ddev->dev_private = NULL; } @@ -382,6 +256,8 @@ static void sti_cleanup(struct drm_device *ddev) static int sti_bind(struct device *dev) { struct drm_device *ddev; + struct sti_private *private; + struct drm_fbdev_cma *fbdev; int ret; ddev = drm_dev_alloc(&sti_driver, dev); @@ -404,6 +280,17 @@ static int sti_bind(struct device *dev) drm_mode_config_reset(ddev); + private = ddev->dev_private; + if (ddev->mode_config.num_connector) { + fbdev = drm_fbdev_cma_init(ddev, 32, + ddev->mode_config.num_connector); + if (IS_ERR(fbdev)) { + DRM_DEBUG_DRIVER("Warning: fails to create fbdev\n"); + fbdev = NULL; + } + private->fbdev = fbdev; + } + return 0; err_register: @@ -476,7 +363,6 @@ static struct platform_driver sti_platform_driver = { static struct platform_driver * const drivers[] = { &sti_tvout_driver, - &sti_vtac_driver, &sti_hqvdp_driver, &sti_hdmi_driver, &sti_hda_driver, diff --git a/drivers/gpu/drm/sti/sti_drv.h b/drivers/gpu/drm/sti/sti_drv.h index 78ebe5e30f53..6502ed2d3351 100644 --- a/drivers/gpu/drm/sti/sti_drv.h +++ b/drivers/gpu/drm/sti/sti_drv.h @@ -25,16 +25,9 @@ struct sti_private { struct drm_property *plane_zorder_property; struct drm_device *drm_dev; struct drm_fbdev_cma *fbdev; - - struct { - struct drm_atomic_state *state; - struct work_struct work; - struct mutex lock; - } commit; }; extern struct platform_driver sti_tvout_driver; -extern struct platform_driver sti_vtac_driver; extern struct platform_driver sti_hqvdp_driver; extern struct platform_driver sti_hdmi_driver; extern struct platform_driver sti_hda_driver; diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index e8c1ed08a9f7..bb23318a44b7 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -195,13 +195,6 @@ static struct drm_info_list dvo_debugfs_files[] = { { "dvo", dvo_dbg_show, 0, NULL }, }; -static void dvo_debugfs_exit(struct sti_dvo *dvo, struct drm_minor *minor) -{ - drm_debugfs_remove_files(dvo_debugfs_files, - ARRAY_SIZE(dvo_debugfs_files), - minor); -} - static int dvo_debugfs_init(struct sti_dvo *dvo, struct drm_minor *minor) { unsigned int i; @@ -478,14 +471,13 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) return err; } - err = drm_bridge_attach(drm_dev, bridge); + err = drm_bridge_attach(encoder, bridge, NULL); if (err) { DRM_ERROR("Failed to attach bridge\n"); return err; } dvo->bridge = bridge; - encoder->bridge = bridge; connector->encoder = encoder; dvo->encoder = encoder; @@ -515,9 +507,6 @@ static void sti_dvo_unbind(struct device *dev, struct device *master, void *data) { struct sti_dvo *dvo = dev_get_drvdata(dev); - struct drm_device *drm_dev = data; - - dvo_debugfs_exit(dvo, drm_dev->primary); drm_bridge_remove(dvo->bridge); } diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index 81df3097b545..86279f5022c2 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -610,7 +610,6 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane, struct sti_plane *plane = to_sti_plane(drm_plane); struct sti_gdp *gdp = to_sti_gdp(plane); struct drm_crtc *crtc = state->crtc; - struct sti_compositor *compo = dev_get_drvdata(gdp->dev); struct drm_framebuffer *fb = state->fb; struct drm_crtc_state *crtc_state; struct sti_mixer *mixer; @@ -636,10 +635,10 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane, src_w = clamp_val(state->src_w >> 16, 0, GAM_GDP_SIZE_MAX); src_h = clamp_val(state->src_h >> 16, 0, GAM_GDP_SIZE_MAX); - format = sti_gdp_fourcc2format(fb->pixel_format); + format = sti_gdp_fourcc2format(fb->format->format); if (format == -1) { DRM_ERROR("Format not supported by GDP %.4s\n", - (char *)&fb->pixel_format); + (char *)&fb->format->format); return -EINVAL; } @@ -648,45 +647,30 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane, return -EINVAL; } - if (!gdp->vtg) { - /* Register gdp callback */ - gdp->vtg = compo->vtg[mixer->id]; - if (sti_vtg_register_client(gdp->vtg, - &gdp->vtg_field_nb, crtc)) { - DRM_ERROR("Cannot register VTG notifier\n"); + /* Set gdp clock */ + if (mode->clock && gdp->clk_pix) { + struct clk *clkp; + int rate = mode->clock * 1000; + int res; + + /* + * According to the mixer used, the gdp pixel clock + * should have a different parent clock. + */ + if (mixer->id == STI_MIXER_MAIN) + clkp = gdp->clk_main_parent; + else + clkp = gdp->clk_aux_parent; + + if (clkp) + clk_set_parent(gdp->clk_pix, clkp); + + res = clk_set_rate(gdp->clk_pix, rate); + if (res < 0) { + DRM_ERROR("Cannot set rate (%dHz) for gdp\n", + rate); return -EINVAL; } - - /* Set and enable gdp clock */ - if (gdp->clk_pix) { - struct clk *clkp; - int rate = mode->clock * 1000; - int res; - - /* - * According to the mixer used, the gdp pixel clock - * should have a different parent clock. - */ - if (mixer->id == STI_MIXER_MAIN) - clkp = gdp->clk_main_parent; - else - clkp = gdp->clk_aux_parent; - - if (clkp) - clk_set_parent(gdp->clk_pix, clkp); - - res = clk_set_rate(gdp->clk_pix, rate); - if (res < 0) { - DRM_ERROR("Cannot set rate (%dHz) for gdp\n", - rate); - return -EINVAL; - } - - if (clk_prepare_enable(gdp->clk_pix)) { - DRM_ERROR("Failed to prepare/enable gdp\n"); - return -EINVAL; - } - } } DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", @@ -724,6 +708,31 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, if (!crtc || !fb) return; + if ((oldstate->fb == state->fb) && + (oldstate->crtc_x == state->crtc_x) && + (oldstate->crtc_y == state->crtc_y) && + (oldstate->crtc_w == state->crtc_w) && + (oldstate->crtc_h == state->crtc_h) && + (oldstate->src_x == state->src_x) && + (oldstate->src_y == state->src_y) && + (oldstate->src_w == state->src_w) && + (oldstate->src_h == state->src_h)) { + /* No change since last update, do not post cmd */ + DRM_DEBUG_DRIVER("No change, not posting cmd\n"); + plane->status = STI_PLANE_UPDATED; + return; + } + + if (!gdp->vtg) { + struct sti_compositor *compo = dev_get_drvdata(gdp->dev); + struct sti_mixer *mixer = to_sti_mixer(crtc); + + /* Register gdp callback */ + gdp->vtg = compo->vtg[mixer->id]; + sti_vtg_register_client(gdp->vtg, &gdp->vtg_field_nb, crtc); + clk_prepare_enable(gdp->clk_pix); + } + mode = &crtc->mode; dst_x = state->crtc_x; dst_y = state->crtc_y; @@ -745,7 +754,7 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, /* build the top field */ top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; - format = sti_gdp_fourcc2format(fb->pixel_format); + format = sti_gdp_fourcc2format(fb->format->format); top_field->gam_gdp_ctl |= format; top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; @@ -753,11 +762,11 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane, cma_obj = drm_fb_cma_get_gem_obj(fb, 0); DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, - (char *)&fb->pixel_format, + (char *)&fb->format->format, (unsigned long)cma_obj->paddr); /* pixel memory location */ - bpp = drm_format_plane_cpp(fb->pixel_format, 0); + bpp = fb->format->cpp[0]; top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0]; top_field->gam_gdp_pml += src_x * bpp; top_field->gam_gdp_pml += src_y * fb->pitches[0]; diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 96f336dd0e29..0c0a75bc8bc3 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -365,13 +365,6 @@ static struct drm_info_list hda_debugfs_files[] = { { "hda", hda_dbg_show, 0, NULL }, }; -static void hda_debugfs_exit(struct sti_hda *hda, struct drm_minor *minor) -{ - drm_debugfs_remove_files(hda_debugfs_files, - ARRAY_SIZE(hda_debugfs_files), - minor); -} - static int hda_debugfs_init(struct sti_hda *hda, struct drm_minor *minor) { unsigned int i; @@ -707,9 +700,8 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) bridge->driver_private = hda; bridge->funcs = &sti_hda_bridge_funcs; - drm_bridge_attach(drm_dev, bridge); + drm_bridge_attach(encoder, bridge, NULL); - encoder->bridge = bridge; connector->encoder = encoder; drm_connector = (struct drm_connector *)connector; @@ -740,10 +732,6 @@ err_sysfs: static void sti_hda_unbind(struct device *dev, struct device *master, void *data) { - struct sti_hda *hda = dev_get_drvdata(dev); - struct drm_device *drm_dev = data; - - hda_debugfs_exit(hda, drm_dev->primary); } static const struct component_ops sti_hda_ops = { diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 376b0763c874..ce2dcba679d5 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -95,7 +95,6 @@ #define HDMI_CFG_HDCP_EN BIT(2) #define HDMI_CFG_ESS_NOT_OESS BIT(3) #define HDMI_CFG_H_SYNC_POL_NEG BIT(4) -#define HDMI_CFG_SINK_TERM_DET_EN BIT(5) #define HDMI_CFG_V_SYNC_POL_NEG BIT(6) #define HDMI_CFG_422_EN BIT(8) #define HDMI_CFG_FIFO_OVERRUN_CLR BIT(12) @@ -159,7 +158,6 @@ struct sti_hdmi_connector { struct drm_encoder *encoder; struct sti_hdmi *hdmi; struct drm_property *colorspace_property; - struct drm_property *hdmi_mode_property; }; #define to_sti_hdmi_connector(x) \ @@ -266,12 +264,9 @@ static void hdmi_config(struct sti_hdmi *hdmi) /* Select encryption type and the framing mode */ conf |= HDMI_CFG_ESS_NOT_OESS; - if (hdmi->hdmi_mode == HDMI_MODE_HDMI) + if (hdmi->hdmi_monitor) conf |= HDMI_CFG_HDMI_NOT_DVI; - /* Enable sink term detection */ - conf |= HDMI_CFG_SINK_TERM_DET_EN; - /* Set Hsync polarity */ if (hdmi->mode.flags & DRM_MODE_FLAG_NHSYNC) { DRM_DEBUG_DRIVER("H Sync Negative\n"); @@ -607,9 +602,6 @@ static void hdmi_dbg_cfg(struct seq_file *s, int val) tmp = val & HDMI_CFG_ESS_NOT_OESS; DBGFS_PRINT_STR("HDCP mode:", tmp ? "ESS enable" : "OESS enable"); seq_puts(s, "\t\t\t\t\t"); - tmp = val & HDMI_CFG_SINK_TERM_DET_EN; - DBGFS_PRINT_STR("Sink term detection:", tmp ? "enable" : "disable"); - seq_puts(s, "\t\t\t\t\t"); tmp = val & HDMI_CFG_H_SYNC_POL_NEG; DBGFS_PRINT_STR("Hsync polarity:", tmp ? "inverted" : "normal"); seq_puts(s, "\t\t\t\t\t"); @@ -731,13 +723,6 @@ static struct drm_info_list hdmi_debugfs_files[] = { { "hdmi", hdmi_dbg_show, 0, NULL }, }; -static void hdmi_debugfs_exit(struct sti_hdmi *hdmi, struct drm_minor *minor) -{ - drm_debugfs_remove_files(hdmi_debugfs_files, - ARRAY_SIZE(hdmi_debugfs_files), - minor); -} - static int hdmi_debugfs_init(struct sti_hdmi *hdmi, struct drm_minor *minor) { unsigned int i; @@ -788,6 +773,95 @@ static void sti_hdmi_disable(struct drm_bridge *bridge) hdmi->enabled = false; } +/** + * sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent + * clocks. None-coherent clocks means that audio and TMDS clocks have not the + * same source (drifts between clocks). In this case assumption is that CTS is + * automatically calculated by hardware. + * + * @audio_fs: audio frame clock frequency in Hz + * + * Values computed are based on table described in HDMI specification 1.4b + * + * Returns n value. + */ +static int sti_hdmi_audio_get_non_coherent_n(unsigned int audio_fs) +{ + unsigned int n; + + switch (audio_fs) { + case 32000: + n = 4096; + break; + case 44100: + n = 6272; + break; + case 48000: + n = 6144; + break; + case 88200: + n = 6272 * 2; + break; + case 96000: + n = 6144 * 2; + break; + case 176400: + n = 6272 * 4; + break; + case 192000: + n = 6144 * 4; + break; + default: + /* Not pre-defined, recommended value: 128 * fs / 1000 */ + n = (audio_fs * 128) / 1000; + } + + return n; +} + +static int hdmi_audio_configure(struct sti_hdmi *hdmi) +{ + int audio_cfg, n; + struct hdmi_audio_params *params = &hdmi->audio; + struct hdmi_audio_infoframe *info = ¶ms->cea; + + DRM_DEBUG_DRIVER("\n"); + + if (!hdmi->enabled) + return 0; + + /* update N parameter */ + n = sti_hdmi_audio_get_non_coherent_n(params->sample_rate); + + DRM_DEBUG_DRIVER("Audio rate = %d Hz, TMDS clock = %d Hz, n = %d\n", + params->sample_rate, hdmi->mode.clock * 1000, n); + hdmi_write(hdmi, n, HDMI_AUDN); + + /* update HDMI registers according to configuration */ + audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID | + HDMI_AUD_CFG_ONE_BIT_INVALID; + + switch (info->channels) { + case 8: + audio_cfg |= HDMI_AUD_CFG_CH78_VALID; + case 6: + audio_cfg |= HDMI_AUD_CFG_CH56_VALID; + case 4: + audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH; + case 2: + audio_cfg |= HDMI_AUD_CFG_CH12_VALID; + break; + default: + DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n", + info->channels); + return -EINVAL; + } + + hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG); + + return hdmi_audio_infoframe_config(hdmi); +} + static void sti_hdmi_pre_enable(struct drm_bridge *bridge) { struct sti_hdmi *hdmi = bridge->driver_private; @@ -826,9 +900,12 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge) if (hdmi_avi_infoframe_config(hdmi)) DRM_ERROR("Unable to configure AVI infoframe\n"); - /* Program AUDIO infoframe */ - if (hdmi_audio_infoframe_config(hdmi)) - DRM_ERROR("Unable to configure AUDIO infoframe\n"); + if (hdmi->audio.enabled) { + if (hdmi_audio_configure(hdmi)) + DRM_ERROR("Unable to configure audio\n"); + } else { + hdmi_audio_infoframe_config(hdmi); + } /* Program VS infoframe */ if (hdmi_vendor_infoframe_config(hdmi)) @@ -892,6 +969,11 @@ static int sti_hdmi_connector_get_modes(struct drm_connector *connector) if (!edid) goto fail; + hdmi->hdmi_monitor = drm_detect_hdmi_monitor(edid); + DRM_DEBUG_KMS("%s : %dx%d cm\n", + (hdmi->hdmi_monitor ? "hdmi monitor" : "dvi monitor"), + edid->width_cm, edid->height_cm); + count = drm_add_edid_modes(connector, edid); drm_mode_connector_update_edid_property(connector, edid); drm_edid_to_eld(connector, edid); @@ -975,19 +1057,6 @@ static void sti_hdmi_connector_init_property(struct drm_device *drm_dev, } hdmi_connector->colorspace_property = prop; drm_object_attach_property(&connector->base, prop, hdmi->colorspace); - - /* hdmi_mode property */ - hdmi->hdmi_mode = DEFAULT_HDMI_MODE; - prop = drm_property_create_enum(drm_dev, 0, "hdmi_mode", - hdmi_mode_names, - ARRAY_SIZE(hdmi_mode_names)); - if (!prop) { - DRM_ERROR("fails to create colorspace property\n"); - return; - } - hdmi_connector->hdmi_mode_property = prop; - drm_object_attach_property(&connector->base, prop, hdmi->hdmi_mode); - } static int @@ -1005,11 +1074,6 @@ sti_hdmi_connector_set_property(struct drm_connector *connector, return 0; } - if (property == hdmi_connector->hdmi_mode_property) { - hdmi->hdmi_mode = val; - return 0; - } - DRM_ERROR("failed to set hdmi connector property\n"); return -EINVAL; } @@ -1029,11 +1093,6 @@ sti_hdmi_connector_get_property(struct drm_connector *connector, return 0; } - if (property == hdmi_connector->hdmi_mode_property) { - *val = hdmi->hdmi_mode; - return 0; - } - DRM_ERROR("failed to get hdmi connector property\n"); return -EINVAL; } @@ -1078,97 +1137,6 @@ static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev) return NULL; } -/** - * sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent - * clocks. None-coherent clocks means that audio and TMDS clocks have not the - * same source (drifts between clocks). In this case assumption is that CTS is - * automatically calculated by hardware. - * - * @audio_fs: audio frame clock frequency in Hz - * - * Values computed are based on table described in HDMI specification 1.4b - * - * Returns n value. - */ -static int sti_hdmi_audio_get_non_coherent_n(unsigned int audio_fs) -{ - unsigned int n; - - switch (audio_fs) { - case 32000: - n = 4096; - break; - case 44100: - n = 6272; - break; - case 48000: - n = 6144; - break; - case 88200: - n = 6272 * 2; - break; - case 96000: - n = 6144 * 2; - break; - case 176400: - n = 6272 * 4; - break; - case 192000: - n = 6144 * 4; - break; - default: - /* Not pre-defined, recommended value: 128 * fs / 1000 */ - n = (audio_fs * 128) / 1000; - } - - return n; -} - -static int hdmi_audio_configure(struct sti_hdmi *hdmi, - struct hdmi_audio_params *params) -{ - int audio_cfg, n; - struct hdmi_audio_infoframe *info = ¶ms->cea; - - DRM_DEBUG_DRIVER("\n"); - - if (!hdmi->enabled) - return 0; - - /* update N parameter */ - n = sti_hdmi_audio_get_non_coherent_n(params->sample_rate); - - DRM_DEBUG_DRIVER("Audio rate = %d Hz, TMDS clock = %d Hz, n = %d\n", - params->sample_rate, hdmi->mode.clock * 1000, n); - hdmi_write(hdmi, n, HDMI_AUDN); - - /* update HDMI registers according to configuration */ - audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID | - HDMI_AUD_CFG_ONE_BIT_INVALID; - - switch (info->channels) { - case 8: - audio_cfg |= HDMI_AUD_CFG_CH78_VALID; - case 6: - audio_cfg |= HDMI_AUD_CFG_CH56_VALID; - case 4: - audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH; - case 2: - audio_cfg |= HDMI_AUD_CFG_CH12_VALID; - break; - default: - DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n", - info->channels); - return -EINVAL; - } - - hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG); - - hdmi->audio = *params; - - return hdmi_audio_infoframe_config(hdmi); -} - static void hdmi_audio_shutdown(struct device *dev, void *data) { struct sti_hdmi *hdmi = dev_get_drvdata(dev); @@ -1192,17 +1160,9 @@ static int hdmi_audio_hw_params(struct device *dev, { struct sti_hdmi *hdmi = dev_get_drvdata(dev); int ret; - struct hdmi_audio_params audio = { - .sample_width = params->sample_width, - .sample_rate = params->sample_rate, - .cea = params->cea, - }; DRM_DEBUG_DRIVER("\n"); - if (!hdmi->enabled) - return 0; - if ((daifmt->fmt != HDMI_I2S) || daifmt->bit_clk_inv || daifmt->frame_clk_inv || daifmt->bit_clk_master || daifmt->frame_clk_master) { @@ -1213,9 +1173,13 @@ static int hdmi_audio_hw_params(struct device *dev, return -EINVAL; } - audio.enabled = true; + hdmi->audio.sample_width = params->sample_width; + hdmi->audio.sample_rate = params->sample_rate; + hdmi->audio.cea = params->cea; + + hdmi->audio.enabled = true; - ret = hdmi_audio_configure(hdmi, &audio); + ret = hdmi_audio_configure(hdmi); if (ret < 0) return ret; @@ -1308,9 +1272,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) bridge->driver_private = hdmi; bridge->funcs = &sti_hdmi_bridge_funcs; - drm_bridge_attach(drm_dev, bridge); + drm_bridge_attach(encoder, bridge, NULL); - encoder->bridge = bridge; connector->encoder = encoder; drm_connector = (struct drm_connector *)connector; @@ -1360,10 +1323,6 @@ err_sysfs: static void sti_hdmi_unbind(struct device *dev, struct device *master, void *data) { - struct sti_hdmi *hdmi = dev_get_drvdata(dev); - struct drm_device *drm_dev = data; - - hdmi_debugfs_exit(hdmi, drm_dev->primary); } static const struct component_ops sti_hdmi_ops = { diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h index 119bc3582ac7..407012350f1a 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.h +++ b/drivers/gpu/drm/sti/sti_hdmi.h @@ -30,19 +30,6 @@ struct hdmi_audio_params { struct hdmi_audio_infoframe cea; }; -/* values for the framing mode property */ -enum sti_hdmi_modes { - HDMI_MODE_HDMI, - HDMI_MODE_DVI, -}; - -static const struct drm_prop_enum_list hdmi_mode_names[] = { - { HDMI_MODE_HDMI, "hdmi" }, - { HDMI_MODE_DVI, "dvi" }, -}; - -#define DEFAULT_HDMI_MODE HDMI_MODE_HDMI - static const struct drm_prop_enum_list colorspace_mode_names[] = { { HDMI_COLORSPACE_RGB, "rgb" }, { HDMI_COLORSPACE_YUV422, "yuv422" }, @@ -73,7 +60,7 @@ static const struct drm_prop_enum_list colorspace_mode_names[] = { * @reset: reset control of the hdmi phy * @ddc_adapt: i2c ddc adapter * @colorspace: current colorspace selected - * @hdmi_mode: select framing for HDMI or DVI + * @hdmi_monitor: true if HDMI monitor detected else DVI monitor assumed * @audio_pdev: ASoC hdmi-codec platform device * @audio: hdmi audio parameters. * @drm_connector: hdmi connector @@ -98,7 +85,7 @@ struct sti_hdmi { struct reset_control *reset; struct i2c_adapter *ddc_adapt; enum hdmi_colorspace colorspace; - enum sti_hdmi_modes hdmi_mode; + bool hdmi_monitor; struct platform_device *audio_pdev; struct hdmi_audio_params audio; struct drm_connector *drm_connector; diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c index f88130f2eb48..66f843148ef7 100644 --- a/drivers/gpu/drm/sti/sti_hqvdp.c +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -332,6 +332,7 @@ struct sti_hqvdp_cmd { * @hqvdp_cmd_paddr: physical address of hqvdp_cmd * @vtg: vtg for main data path * @xp70_initialized: true if xp70 is already initialized + * @vtg_registered: true if registered to VTG */ struct sti_hqvdp { struct device *dev; @@ -347,6 +348,7 @@ struct sti_hqvdp { u32 hqvdp_cmd_paddr; struct sti_vtg *vtg; bool xp70_initialized; + bool vtg_registered; }; #define to_sti_hqvdp(x) container_of(x, struct sti_hqvdp, plane) @@ -771,7 +773,7 @@ static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp) DRM_ERROR("XP70 could not revert to idle\n"); hqvdp->plane.status = STI_PLANE_DISABLED; - hqvdp->xp70_initialized = false; + hqvdp->vtg_registered = false; } /** @@ -1035,9 +1037,9 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, src_w = state->src_w >> 16; src_h = state->src_h >> 16; - if (!sti_hqvdp_check_hw_scaling(hqvdp, mode, - src_w, src_h, - dst_w, dst_h)) { + if (mode->clock && !sti_hqvdp_check_hw_scaling(hqvdp, mode, + src_w, src_h, + dst_w, dst_h)) { DRM_ERROR("Scaling beyond HW capabilities\n"); return -EINVAL; } @@ -1064,10 +1066,11 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, return -EINVAL; } - if (!hqvdp->xp70_initialized) { + if (!hqvdp->xp70_initialized) /* Start HQVDP XP70 coprocessor */ sti_hqvdp_start_xp70(hqvdp); + if (!hqvdp->vtg_registered) { /* Prevent VTG shutdown */ if (clk_prepare_enable(hqvdp->clk_pix_main)) { DRM_ERROR("Failed to prepare/enable pix main clk\n"); @@ -1081,6 +1084,7 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane, DRM_ERROR("Cannot register VTG notifier\n"); return -EINVAL; } + hqvdp->vtg_registered = true; } DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", @@ -1113,6 +1117,21 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, if (!crtc || !fb) return; + if ((oldstate->fb == state->fb) && + (oldstate->crtc_x == state->crtc_x) && + (oldstate->crtc_y == state->crtc_y) && + (oldstate->crtc_w == state->crtc_w) && + (oldstate->crtc_h == state->crtc_h) && + (oldstate->src_x == state->src_x) && + (oldstate->src_y == state->src_y) && + (oldstate->src_w == state->src_w) && + (oldstate->src_h == state->src_h)) { + /* No change since last update, do not post cmd */ + DRM_DEBUG_DRIVER("No change, not posting cmd\n"); + plane->status = STI_PLANE_UPDATED; + return; + } + mode = &crtc->mode; dst_x = state->crtc_x; dst_y = state->crtc_y; @@ -1147,7 +1166,7 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane, cma_obj = drm_fb_cma_get_gem_obj(fb, 0); DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, - (char *)&fb->pixel_format, + (char *)&fb->format->format, (unsigned long)cma_obj->paddr); /* Buffer planes address */ diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h index 830a3c42d886..e64a00e61049 100644 --- a/drivers/gpu/drm/sti/sti_mixer.h +++ b/drivers/gpu/drm/sti/sti_mixer.h @@ -28,7 +28,6 @@ enum sti_mixer_status { * @regs: mixer registers * @id: id of the mixer * @drm_crtc: crtc object link to the mixer - * @pending_event: set if a flip event is pending on crtc * @status: to know the status of the mixer */ struct sti_mixer { @@ -36,7 +35,6 @@ struct sti_mixer { void __iomem *regs; int id; struct drm_crtc drm_crtc; - struct drm_pending_vblank_event *pending_event; enum sti_mixer_status status; }; diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c index ca4b3719a64a..427d8f58c6b1 100644 --- a/drivers/gpu/drm/sti/sti_plane.c +++ b/drivers/gpu/drm/sti/sti_plane.c @@ -65,9 +65,18 @@ void sti_plane_update_fps(struct sti_plane *plane, fps->last_timestamp = now; fps->last_frame_counter = fps->curr_frame_counter; - fpks = (num_frames * 1000000) / ms_since_last; - snprintf(plane->fps_info.fps_str, FPS_LENGTH, "%-6s @ %d.%.3d fps", - sti_plane_to_str(plane), fpks / 1000, fpks % 1000); + + if (plane->drm_plane.fb) { + fpks = (num_frames * 1000000) / ms_since_last; + snprintf(plane->fps_info.fps_str, FPS_LENGTH, + "%-8s %4dx%-4d %.4s @ %3d.%-3.3d fps (%s)", + plane->drm_plane.name, + plane->drm_plane.fb->width, + plane->drm_plane.fb->height, + (char *)&plane->drm_plane.fb->format->format, + fpks / 1000, fpks % 1000, + sti_plane_to_str(plane)); + } if (fps->curr_field_counter) { /* Compute number of field updates */ @@ -75,7 +84,7 @@ void sti_plane_update_fps(struct sti_plane *plane, fps->last_field_counter = fps->curr_field_counter; fipks = (num_fields * 1000000) / ms_since_last; snprintf(plane->fps_info.fips_str, - FPS_LENGTH, " - %d.%.3d field/sec", + FPS_LENGTH, " - %3d.%-3.3d field/sec", fipks / 1000, fipks % 1000); } else { plane->fps_info.fips_str[0] = '\0'; diff --git a/drivers/gpu/drm/sti/sti_plane.h b/drivers/gpu/drm/sti/sti_plane.h index ce3e8d6c88bb..c36c13faaa18 100644 --- a/drivers/gpu/drm/sti/sti_plane.h +++ b/drivers/gpu/drm/sti/sti_plane.h @@ -48,7 +48,7 @@ enum sti_plane_status { STI_PLANE_DISABLED, }; -#define FPS_LENGTH 64 +#define FPS_LENGTH 128 struct sti_fps_info { bool output; unsigned int curr_frame_counter; diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c index ad46d3558d91..8b8ea717c121 100644 --- a/drivers/gpu/drm/sti/sti_tvout.c +++ b/drivers/gpu/drm/sti/sti_tvout.c @@ -567,13 +567,6 @@ static struct drm_info_list tvout_debugfs_files[] = { { "tvout", tvout_dbg_show, 0, NULL }, }; -static void tvout_debugfs_exit(struct sti_tvout *tvout, struct drm_minor *minor) -{ - drm_debugfs_remove_files(tvout_debugfs_files, - ARRAY_SIZE(tvout_debugfs_files), - minor); -} - static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor) { unsigned int i; @@ -627,7 +620,6 @@ static void sti_tvout_early_unregister(struct drm_encoder *encoder) if (!tvout->debugfs_registered) return; - tvout_debugfs_exit(tvout, encoder->dev->primary); tvout->debugfs_registered = false; } diff --git a/drivers/gpu/drm/sti/sti_vtac.c b/drivers/gpu/drm/sti/sti_vtac.c deleted file mode 100644 index cf7fe8a1db42..000000000000 --- a/drivers/gpu/drm/sti/sti_vtac.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) STMicroelectronics SA 2014 - * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. - * License terms: GNU General Public License (GPL), version 2 - */ - -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> - -#include <drm/drmP.h> - -#include "sti_drv.h" - -/* registers offset */ -#define VTAC_CONFIG 0x00 -#define VTAC_RX_FIFO_CONFIG 0x04 -#define VTAC_FIFO_CONFIG_VAL 0x04 - -#define VTAC_SYS_CFG8521 0x824 -#define VTAC_SYS_CFG8522 0x828 - -/* Number of phyts per pixel */ -#define VTAC_2_5_PPP 0x0005 -#define VTAC_3_PPP 0x0006 -#define VTAC_4_PPP 0x0008 -#define VTAC_5_PPP 0x000A -#define VTAC_6_PPP 0x000C -#define VTAC_13_PPP 0x001A -#define VTAC_14_PPP 0x001C -#define VTAC_15_PPP 0x001E -#define VTAC_16_PPP 0x0020 -#define VTAC_17_PPP 0x0022 -#define VTAC_18_PPP 0x0024 - -/* enable bits */ -#define VTAC_ENABLE 0x3003 - -#define VTAC_TX_PHY_ENABLE_CLK_PHY BIT(0) -#define VTAC_TX_PHY_ENABLE_CLK_DLL BIT(1) -#define VTAC_TX_PHY_PLL_NOT_OSC_MODE BIT(3) -#define VTAC_TX_PHY_RST_N_DLL_SWITCH BIT(4) -#define VTAC_TX_PHY_PROG_N3 BIT(9) - - -/** - * VTAC mode structure - * - * @vid_in_width: Video Data Resolution - * @phyts_width: Width of phyt buses(phyt low and phyt high). - * @phyts_per_pixel: Number of phyts sent per pixel - */ -struct sti_vtac_mode { - u32 vid_in_width; - u32 phyts_width; - u32 phyts_per_pixel; -}; - -static const struct sti_vtac_mode vtac_mode_main = { - .vid_in_width = 0x2, - .phyts_width = 0x2, - .phyts_per_pixel = VTAC_5_PPP, -}; -static const struct sti_vtac_mode vtac_mode_aux = { - .vid_in_width = 0x1, - .phyts_width = 0x0, - .phyts_per_pixel = VTAC_17_PPP, -}; - -/** - * VTAC structure - * - * @dev: pointer to device structure - * @regs: ioremapped registers for RX and TX devices - * @phy_regs: phy registers for TX device - * @clk: clock - * @mode: main or auxillary configuration mode - */ -struct sti_vtac { - struct device *dev; - void __iomem *regs; - void __iomem *phy_regs; - struct clk *clk; - const struct sti_vtac_mode *mode; -}; - -static void sti_vtac_rx_set_config(struct sti_vtac *vtac) -{ - u32 config; - - /* Enable VTAC clock */ - if (clk_prepare_enable(vtac->clk)) - DRM_ERROR("Failed to prepare/enable vtac_rx clock.\n"); - - writel(VTAC_FIFO_CONFIG_VAL, vtac->regs + VTAC_RX_FIFO_CONFIG); - - config = VTAC_ENABLE; - config |= vtac->mode->vid_in_width << 4; - config |= vtac->mode->phyts_width << 16; - config |= vtac->mode->phyts_per_pixel << 23; - writel(config, vtac->regs + VTAC_CONFIG); -} - -static void sti_vtac_tx_set_config(struct sti_vtac *vtac) -{ - u32 phy_config; - u32 config; - - /* Enable VTAC clock */ - if (clk_prepare_enable(vtac->clk)) - DRM_ERROR("Failed to prepare/enable vtac_tx clock.\n"); - - /* Configure vtac phy */ - phy_config = 0x00000000; - writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8522); - phy_config = VTAC_TX_PHY_ENABLE_CLK_PHY; - writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config |= VTAC_TX_PHY_PROG_N3; - writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config |= VTAC_TX_PHY_ENABLE_CLK_DLL; - writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config |= VTAC_TX_PHY_RST_N_DLL_SWITCH; - writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521); - phy_config |= VTAC_TX_PHY_PLL_NOT_OSC_MODE; - writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521); - - /* Configure vtac tx */ - config = VTAC_ENABLE; - config |= vtac->mode->vid_in_width << 4; - config |= vtac->mode->phyts_width << 16; - config |= vtac->mode->phyts_per_pixel << 23; - writel(config, vtac->regs + VTAC_CONFIG); -} - -static const struct of_device_id vtac_of_match[] = { - { - .compatible = "st,vtac-main", - .data = &vtac_mode_main, - }, { - .compatible = "st,vtac-aux", - .data = &vtac_mode_aux, - }, { - /* end node */ - } -}; -MODULE_DEVICE_TABLE(of, vtac_of_match); - -static int sti_vtac_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - const struct of_device_id *id; - struct sti_vtac *vtac; - struct resource *res; - - vtac = devm_kzalloc(dev, sizeof(*vtac), GFP_KERNEL); - if (!vtac) - return -ENOMEM; - - vtac->dev = dev; - - id = of_match_node(vtac_of_match, np); - if (!id) - return -ENOMEM; - - vtac->mode = id->data; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - DRM_ERROR("Invalid resource\n"); - return -ENOMEM; - } - vtac->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(vtac->regs)) - return PTR_ERR(vtac->regs); - - - vtac->clk = devm_clk_get(dev, "vtac"); - if (IS_ERR(vtac->clk)) { - DRM_ERROR("Cannot get vtac clock\n"); - return PTR_ERR(vtac->clk); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) { - vtac->phy_regs = devm_ioremap_nocache(dev, res->start, - resource_size(res)); - sti_vtac_tx_set_config(vtac); - } else { - - sti_vtac_rx_set_config(vtac); - } - - platform_set_drvdata(pdev, vtac); - DRM_INFO("%s %s\n", __func__, dev_name(vtac->dev)); - - return 0; -} - -static int sti_vtac_remove(struct platform_device *pdev) -{ - return 0; -} - -struct platform_driver sti_vtac_driver = { - .driver = { - .name = "sti-vtac", - .owner = THIS_MODULE, - .of_match_table = vtac_of_match, - }, - .probe = sti_vtac_probe, - .remove = sti_vtac_remove, -}; - -MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); -MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c index a8882bdd0f8b..2dcba1d3a122 100644 --- a/drivers/gpu/drm/sti/sti_vtg.c +++ b/drivers/gpu/drm/sti/sti_vtg.c @@ -17,7 +17,6 @@ #include "sti_vtg.h" #define VTG_MODE_MASTER 0 -#define VTG_MODE_SLAVE_BY_EXT0 1 /* registers offset */ #define VTG_MODE 0x0000 @@ -132,7 +131,6 @@ struct sti_vtg_sync_params { * @irq_status: store the IRQ status value * @notifier_list: notifier callback * @crtc: the CRTC for vblank event - * @slave: slave vtg * @link: List node to link the structure in lookup list */ struct sti_vtg { @@ -144,7 +142,6 @@ struct sti_vtg { u32 irq_status; struct raw_notifier_head notifier_list; struct drm_crtc *crtc; - struct sti_vtg *slave; struct list_head link; }; @@ -166,10 +163,6 @@ struct sti_vtg *of_vtg_find(struct device_node *np) static void vtg_reset(struct sti_vtg *vtg) { - /* reset slave and then master */ - if (vtg->slave) - vtg_reset(vtg->slave); - writel(1, vtg->regs + VTG_DRST_AUTOC); } @@ -259,10 +252,6 @@ static void vtg_set_mode(struct sti_vtg *vtg, { unsigned int i; - if (vtg->slave) - vtg_set_mode(vtg->slave, VTG_MODE_SLAVE_BY_EXT0, - vtg->sync_params, mode); - /* Set the number of clock cycles per line */ writel(mode->htotal, vtg->regs + VTG_CLKLN); @@ -318,11 +307,7 @@ void sti_vtg_set_config(struct sti_vtg *vtg, vtg_reset(vtg); - /* enable irq for the vtg vblank synchro */ - if (vtg->slave) - vtg_enable_irq(vtg->slave); - else - vtg_enable_irq(vtg); + vtg_enable_irq(vtg); } /** @@ -365,18 +350,12 @@ u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x) int sti_vtg_register_client(struct sti_vtg *vtg, struct notifier_block *nb, struct drm_crtc *crtc) { - if (vtg->slave) - return sti_vtg_register_client(vtg->slave, nb, crtc); - vtg->crtc = crtc; return raw_notifier_chain_register(&vtg->notifier_list, nb); } int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb) { - if (vtg->slave) - return sti_vtg_unregister_client(vtg->slave, nb); - return raw_notifier_chain_unregister(&vtg->notifier_list, nb); } @@ -410,7 +389,6 @@ static irqreturn_t vtg_irq(int irq, void *arg) static int vtg_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np; struct sti_vtg *vtg; struct resource *res; int ret; @@ -429,30 +407,25 @@ static int vtg_probe(struct platform_device *pdev) return -ENOMEM; } vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!vtg->regs) { + DRM_ERROR("failed to remap I/O memory\n"); + return -ENOMEM; + } - np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0); - if (np) { - vtg->slave = of_vtg_find(np); - of_node_put(np); + vtg->irq = platform_get_irq(pdev, 0); + if (vtg->irq < 0) { + DRM_ERROR("Failed to get VTG interrupt\n"); + return vtg->irq; + } - if (!vtg->slave) - return -EPROBE_DEFER; - } else { - vtg->irq = platform_get_irq(pdev, 0); - if (vtg->irq < 0) { - DRM_ERROR("Failed to get VTG interrupt\n"); - return vtg->irq; - } - - RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); - - ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq, - vtg_irq_thread, IRQF_ONESHOT, - dev_name(dev), vtg); - if (ret < 0) { - DRM_ERROR("Failed to register VTG interrupt\n"); - return ret; - } + RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); + + ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq, + vtg_irq_thread, IRQF_ONESHOT, + dev_name(dev), vtg); + if (ret < 0) { + DRM_ERROR("Failed to register VTG interrupt\n"); + return ret; } vtg_register(vtg); diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 2e08f969bb64..08ce15070f80 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -189,10 +189,11 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", interlaced ? "on" : "off"); - ret = sun4i_backend_drm_format_to_layer(plane, fb->pixel_format, &val); + ret = sun4i_backend_drm_format_to_layer(plane, fb->format->format, + &val); if (ret) { DRM_DEBUG_DRIVER("Invalid format\n"); - return val; + return ret; } regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer), @@ -218,7 +219,7 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); /* Compute the start of the displayed memory */ - bpp = drm_format_plane_cpp(fb->pixel_format, 0); + bpp = fb->format->cpp[0]; paddr = gem->paddr + fb->offsets[0]; paddr += (state->src_x >> 16) * bpp; paddr += (state->src_y >> 16) * fb->pitches[0]; diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c index 8b6ce619ad81..2c3beff8b53e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c @@ -40,9 +40,7 @@ struct drm_fbdev_cma *sun4i_framebuffer_init(struct drm_device *drm) drm->mode_config.funcs = &sun4i_de_mode_config_funcs; - return drm_fbdev_cma_init(drm, 32, - drm->mode_config.num_crtc, - drm->mode_config.num_connector); + return drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); } void sun4i_framebuffer_free(struct drm_device *drm) diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index f5e86fe7750e..757208f51731 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -208,6 +208,7 @@ int sun4i_rgb_init(struct drm_device *drm) struct sun4i_drv *drv = drm->dev_private; struct sun4i_tcon *tcon = drv->tcon; struct drm_encoder *encoder; + struct drm_bridge *bridge; struct sun4i_rgb *rgb; int ret; @@ -218,8 +219,8 @@ int sun4i_rgb_init(struct drm_device *drm) encoder = &rgb->encoder; tcon->panel = sun4i_tcon_find_panel(tcon->dev->of_node); - encoder->bridge = sun4i_tcon_find_bridge(tcon->dev->of_node); - if (IS_ERR(tcon->panel) && IS_ERR(encoder->bridge)) { + bridge = sun4i_tcon_find_bridge(tcon->dev->of_node); + if (IS_ERR(tcon->panel) && IS_ERR(bridge)) { dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n"); return 0; } @@ -260,16 +261,12 @@ int sun4i_rgb_init(struct drm_device *drm) } } - if (!IS_ERR(encoder->bridge)) { - encoder->bridge->encoder = &rgb->encoder; - - ret = drm_bridge_attach(drm, encoder->bridge); + if (!IS_ERR(bridge)) { + ret = drm_bridge_attach(encoder, bridge, NULL); if (ret) { dev_err(drm->dev, "Couldn't attach our bridge\n"); goto err_cleanup_connector; } - } else { - encoder->bridge = NULL; } return 0; diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4010d69cbd08..7561a95a54e3 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -511,7 +511,7 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, if (!state->crtc) return 0; - err = tegra_dc_format(state->fb->pixel_format, &plane_state->format, + err = tegra_dc_format(state->fb->format->format, &plane_state->format, &plane_state->swap); if (err < 0) return err; @@ -531,7 +531,7 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, * error out if the user tries to display a framebuffer with such a * configuration. */ - if (drm_format_num_planes(state->fb->pixel_format) > 2) { + if (state->fb->format->num_planes > 2) { if (state->fb->pitches[2] != state->fb->pitches[1]) { DRM_ERROR("unsupported UV-plane configuration\n"); return -EINVAL; @@ -568,7 +568,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.dst.y = plane->state->crtc_y; window.dst.w = plane->state->crtc_w; window.dst.h = plane->state->crtc_h; - window.bits_per_pixel = fb->bits_per_pixel; + window.bits_per_pixel = fb->format->cpp[0] * 8; window.bottom_up = tegra_fb_is_bottom_up(fb); /* copy from state */ @@ -576,7 +576,7 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.format = state->format; window.swap = state->swap; - for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { + for (i = 0; i < fb->format->num_planes; i++) { struct tegra_bo *bo = tegra_fb_get_plane(fb, i); window.base[i] = bo->paddr + fb->offsets[i]; diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b8be3ee4d3b8..ef215fef63d6 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -214,7 +214,7 @@ free: return err; } -static int tegra_drm_unload(struct drm_device *drm) +static void tegra_drm_unload(struct drm_device *drm) { struct host1x_device *device = to_host1x_device(drm->dev); struct tegra_drm *tegra = drm->dev_private; @@ -227,7 +227,7 @@ static int tegra_drm_unload(struct drm_device *drm) err = host1x_device_exit(device); if (err < 0) - return err; + return; if (tegra->domain) { iommu_domain_free(tegra->domain); @@ -235,8 +235,6 @@ static int tegra_drm_unload(struct drm_device *drm) } kfree(tegra); - - return 0; } static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) @@ -806,23 +804,10 @@ static const struct file_operations tegra_drm_fops = { .llseek = noop_llseek, }; -static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, - unsigned int pipe) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) { - if (pipe == drm_crtc_index(crtc)) - return crtc; - } - - return NULL; -} - static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, unsigned int pipe) { - struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); struct tegra_dc *dc = to_tegra_dc(crtc); if (!crtc) @@ -833,7 +818,7 @@ static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, static int tegra_drm_enable_vblank(struct drm_device *drm, unsigned int pipe) { - struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); struct tegra_dc *dc = to_tegra_dc(crtc); if (!crtc) @@ -846,7 +831,7 @@ static int tegra_drm_enable_vblank(struct drm_device *drm, unsigned int pipe) static void tegra_drm_disable_vblank(struct drm_device *drm, unsigned int pipe) { - struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe); struct tegra_dc *dc = to_tegra_dc(crtc); if (crtc) @@ -875,8 +860,9 @@ static int tegra_debugfs_framebuffers(struct seq_file *s, void *data) list_for_each_entry(fb, &drm->mode_config.fb_list, head) { seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n", - fb->base.id, fb->width, fb->height, fb->depth, - fb->bits_per_pixel, + fb->base.id, fb->width, fb->height, + fb->format->depth, + fb->format->cpp[0] * 8, drm_framebuffer_read_refcount(fb)); } @@ -890,8 +876,11 @@ static int tegra_debugfs_iova(struct seq_file *s, void *data) struct drm_info_node *node = (struct drm_info_node *)s->private; struct drm_device *drm = node->minor->dev; struct tegra_drm *tegra = drm->dev_private; + struct drm_printer p = drm_seq_file_printer(s); + + drm_mm_print(&tegra->mm, &p); - return drm_mm_dump_table(s, &tegra->mm); + return 0; } static struct drm_info_list tegra_debugfs_list[] = { @@ -905,12 +894,6 @@ static int tegra_debugfs_init(struct drm_minor *minor) ARRAY_SIZE(tegra_debugfs_list), minor->debugfs_root, minor); } - -static void tegra_debugfs_cleanup(struct drm_minor *minor) -{ - drm_debugfs_remove_files(tegra_debugfs_list, - ARRAY_SIZE(tegra_debugfs_list), minor); -} #endif static struct drm_driver tegra_drm_driver = { @@ -928,7 +911,6 @@ static struct drm_driver tegra_drm_driver = { #if defined(CONFIG_DEBUG_FS) .debugfs_init = tegra_debugfs_init, - .debugfs_cleanup = tegra_debugfs_cleanup, #endif .gem_free_object_unlocked = tegra_bo_free_object, @@ -991,10 +973,6 @@ static int host1x_drm_probe(struct host1x_device *dev) if (err < 0) goto unref; - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, - driver->major, driver->minor, driver->patchlevel, - driver->date, drm->primary->index); - return 0; unref: diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 0ddcce1b420d..5205790dd679 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -17,6 +17,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_fb_helper.h> #include <drm/drm_fixed.h> diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index e4a5ab0a9677..f142f6a4db25 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -32,7 +32,7 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, { struct tegra_fb *fb = to_tegra_fb(framebuffer); - if (index >= drm_format_num_planes(framebuffer->pixel_format)) + if (index >= framebuffer->format->num_planes) return NULL; return fb->planes[index]; @@ -114,7 +114,7 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm, fb->num_planes = num_planes; - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(drm, &fb->base, mode_cmd); for (i = 0; i < fb->num_planes; i++) fb->planes[i] = planes[i]; @@ -246,7 +246,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, info->flags = FBINFO_FLAG_DEFAULT; info->fbops = &tegra_fb_ops; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, helper, fb->width, fb->height); offset = info->var.xoffset * bytes_per_pixel + @@ -271,8 +271,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, return 0; destroy: - drm_framebuffer_unregister_private(fb); - tegra_fb_destroy(fb); + drm_framebuffer_remove(fb); release: drm_fb_helper_release_fbi(helper); return err; @@ -310,7 +309,7 @@ static int tegra_fbdev_init(struct tegra_fbdev *fbdev, struct drm_device *drm = fbdev->base.dev; int err; - err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); + err = drm_fb_helper_init(drm, &fbdev->base, max_connectors); if (err < 0) { dev_err(drm->dev, "failed to initialize DRM FB helper: %d\n", err); @@ -342,10 +341,8 @@ static void tegra_fbdev_exit(struct tegra_fbdev *fbdev) drm_fb_helper_unregister_fbi(&fbdev->base); drm_fb_helper_release_fbi(&fbdev->base); - if (fbdev->fb) { - drm_framebuffer_unregister_private(&fbdev->fb->base); + if (fbdev->fb) drm_framebuffer_remove(&fbdev->fb->base); - } drm_fb_helper_fini(&fbdev->base); tegra_fbdev_free(fbdev); diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 7d853e6b5ff0..17e62ecb5d4d 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -128,8 +128,8 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) if (!bo->mm) return -ENOMEM; - err = drm_mm_insert_node_generic(&tegra->mm, bo->mm, bo->gem.size, - PAGE_SIZE, 0, 0, 0); + err = drm_mm_insert_node_generic(&tegra->mm, + bo->mm, bo->gem.size, PAGE_SIZE, 0, 0); if (err < 0) { dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n", err); @@ -441,8 +441,9 @@ int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, return 0; } -static int tegra_bo_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int tegra_bo_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *gem = vma->vm_private_data; struct tegra_bo *bo = to_tegra_bo(gem); struct page *page; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 6dfdb145f3bb..f80bf9385e41 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -91,7 +91,7 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) start = gem->paddr + fb->offsets[0] + crtc->y * fb->pitches[0] + - crtc->x * drm_format_plane_cpp(fb->pixel_format, 0); + crtc->x * fb->format->cpp[0]; end = start + (crtc->mode.vdisplay * fb->pitches[0]); @@ -399,7 +399,7 @@ static void tilcdc_crtc_set_mode(struct drm_crtc *crtc) if (info->tft_alt_mode) reg |= LCDC_TFT_ALT_ENABLE; if (priv->rev == 2) { - switch (fb->pixel_format) { + switch (fb->format->format) { case DRM_FORMAT_BGR565: case DRM_FORMAT_RGB565: break; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index bd0a3bd07167..372d86fbb093 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -403,8 +403,7 @@ static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) drm_mode_config_reset(ddev); priv->fbdev = drm_fbdev_cma_init(ddev, bpp, - ddev->mode_config.num_crtc, - ddev->mode_config.num_connector); + ddev->mode_config.num_connector); if (IS_ERR(priv->fbdev)) { ret = PTR_ERR(priv->fbdev); goto init_failed; @@ -507,7 +506,9 @@ static int tilcdc_mm_show(struct seq_file *m, void *arg) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; - return drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm); + struct drm_printer p = drm_seq_file_printer(m); + drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p); + return 0; } static struct drm_info_list tilcdc_debugfs_list[] = { @@ -537,17 +538,6 @@ static int tilcdc_debugfs_init(struct drm_minor *minor) return ret; } - -static void tilcdc_debugfs_cleanup(struct drm_minor *minor) -{ - struct tilcdc_module *mod; - drm_debugfs_remove_files(tilcdc_debugfs_list, - ARRAY_SIZE(tilcdc_debugfs_list), minor); - - list_for_each_entry(mod, &module_list, list) - if (mod->funcs->debugfs_cleanup) - mod->funcs->debugfs_cleanup(mod, minor); -} #endif static const struct file_operations fops = { @@ -587,7 +577,6 @@ static struct drm_driver tilcdc_driver = { .gem_prime_mmap = drm_gem_cma_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = tilcdc_debugfs_init, - .debugfs_cleanup = tilcdc_debugfs_cleanup, #endif .fops = &fops, .name = "tilcdc", diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 0e71daf5b5cb..8caa11bc7aec 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -111,8 +111,6 @@ struct tilcdc_module_ops { #ifdef CONFIG_DEBUG_FS /* create debugfs nodes (can be NULL): */ int (*debugfs_init)(struct tilcdc_module *mod, struct drm_minor *minor); - /* cleanup debugfs nodes (can be NULL): */ - void (*debugfs_cleanup)(struct tilcdc_module *mod, struct drm_minor *minor); #endif }; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index c67d7cd7d57e..b0dd5e8634ae 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -167,10 +167,8 @@ int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) int ret; priv->external_encoder->possible_crtcs = BIT(0); - priv->external_encoder->bridge = bridge; - bridge->encoder = priv->external_encoder; - ret = drm_bridge_attach(ddev, bridge); + ret = drm_bridge_attach(priv->external_encoder, bridge, NULL); if (ret) { dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret); return ret; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_plane.c b/drivers/gpu/drm/tilcdc/tilcdc_plane.c index 8a6a50d74aff..ba0d66c0d8ac 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_plane.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_plane.c @@ -69,7 +69,7 @@ static int tilcdc_plane_atomic_check(struct drm_plane *plane, } pitch = crtc_state->mode.hdisplay * - drm_format_plane_cpp(state->fb->pixel_format, 0); + state->fb->format->cpp[0]; if (state->fb->pitches[0] != pitch) { dev_err(plane->dev->dev, "Invalid pitch: fb and crtc widths must be the same"); @@ -77,7 +77,7 @@ static int tilcdc_plane_atomic_check(struct drm_plane *plane, } if (state->fb && old_state->fb && - state->fb->pixel_format != old_state->fb->pixel_format) { + state->fb->format != old_state->fb->format) { dev_dbg(plane->dev->dev, "%s(): pixel format change requires mode_change\n", __func__); diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig new file mode 100644 index 000000000000..3504c53846da --- /dev/null +++ b/drivers/gpu/drm/tinydrm/Kconfig @@ -0,0 +1,21 @@ +menuconfig DRM_TINYDRM + tristate "Support for simple displays" + depends on DRM + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + help + Choose this option if you have a tinydrm supported display. + If M is selected the module will be called tinydrm. + +config TINYDRM_MIPI_DBI + tristate + +config TINYDRM_MI0283QT + tristate "DRM support for MI0283QT" + depends on DRM_TINYDRM && SPI + select TINYDRM_MIPI_DBI + help + DRM driver for the Multi-Inno MI0283QT display panel + If M is selected the module will be called mi0283qt. diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile new file mode 100644 index 000000000000..7a3604cf4fc2 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_DRM_TINYDRM) += core/ + +# Controllers +obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o + +# Displays +obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile new file mode 100644 index 000000000000..fb221e6f8885 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/core/Makefile @@ -0,0 +1,3 @@ +tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o + +obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c new file mode 100644 index 000000000000..6a257dd08ee0 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/tinydrm/tinydrm.h> +#include <linux/device.h> +#include <linux/dma-buf.h> + +/** + * DOC: overview + * + * This library provides driver helpers for very simple display hardware. + * + * It is based on &drm_simple_display_pipe coupled with a &drm_connector which + * has only one fixed &drm_display_mode. The framebuffers are backed by the + * cma helper and have support for framebuffer flushing (dirty). + * fbdev support is also included. + * + */ + +/** + * DOC: core + * + * The driver allocates &tinydrm_device, initializes it using + * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init() + * and registers the DRM device using devm_tinydrm_register(). + */ + +/** + * tinydrm_lastclose - DRM lastclose helper + * @drm: DRM device + * + * This function ensures that fbdev is restored when drm_lastclose() is called + * on the last drm_release(). Drivers can use this as their + * &drm_driver->lastclose callback. + */ +void tinydrm_lastclose(struct drm_device *drm) +{ + struct tinydrm_device *tdev = drm->dev_private; + + DRM_DEBUG_KMS("\n"); + drm_fbdev_cma_restore_mode(tdev->fbdev_cma); +} +EXPORT_SYMBOL(tinydrm_lastclose); + +/** + * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from + * another driver's scatter/gather table of pinned pages + * @drm: DRM device to import into + * @attach: DMA-BUF attachment + * @sgt: Scatter/gather table of pinned pages + * + * This function imports a scatter/gather table exported via DMA-BUF by + * another driver using drm_gem_cma_prime_import_sg_table(). It sets the + * kernel virtual address on the CMA object. Drivers should use this as their + * &drm_driver->gem_prime_import_sg_table callback if they need the virtual + * address. tinydrm_gem_cma_free_object() should be used in combination with + * this function. + * + * Returns: + * A pointer to a newly created GEM object or an ERR_PTR-encoded negative + * error code on failure. + */ +struct drm_gem_object * +tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm, + struct dma_buf_attachment *attach, + struct sg_table *sgt) +{ + struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *obj; + void *vaddr; + + vaddr = dma_buf_vmap(attach->dmabuf); + if (!vaddr) { + DRM_ERROR("Failed to vmap PRIME buffer\n"); + return ERR_PTR(-ENOMEM); + } + + obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt); + if (IS_ERR(obj)) { + dma_buf_vunmap(attach->dmabuf, vaddr); + return obj; + } + + cma_obj = to_drm_gem_cma_obj(obj); + cma_obj->vaddr = vaddr; + + return obj; +} +EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table); + +/** + * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM + * object + * @gem_obj: GEM object to free + * + * This function frees the backing memory of the CMA GEM object, cleans up the + * GEM object state and frees the memory used to store the object itself using + * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel + * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers + * can use this as their &drm_driver->gem_free_object callback. + */ +void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj) +{ + if (gem_obj->import_attach) { + struct drm_gem_cma_object *cma_obj; + + cma_obj = to_drm_gem_cma_obj(gem_obj); + dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr); + cma_obj->vaddr = NULL; + } + + drm_gem_cma_free_object(gem_obj); +} +EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object); + +const struct file_operations tinydrm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .llseek = no_llseek, + .mmap = drm_gem_cma_mmap, +}; +EXPORT_SYMBOL(tinydrm_fops); + +static struct drm_framebuffer * +tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct tinydrm_device *tdev = drm->dev_private; + + return drm_fb_cma_create_with_funcs(drm, file_priv, mode_cmd, + tdev->fb_funcs); +} + +static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = { + .fb_create = tinydrm_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev, + const struct drm_framebuffer_funcs *fb_funcs, + struct drm_driver *driver) +{ + struct drm_device *drm; + + mutex_init(&tdev->dirty_lock); + tdev->fb_funcs = fb_funcs; + + /* + * We don't embed drm_device, because that prevent us from using + * devm_kzalloc() to allocate tinydrm_device in the driver since + * drm_dev_unref() frees the structure. The devm_ functions provide + * for easy error handling. + */ + drm = drm_dev_alloc(driver, parent); + if (IS_ERR(drm)) + return PTR_ERR(drm); + + tdev->drm = drm; + drm->dev_private = tdev; + drm_mode_config_init(drm); + drm->mode_config.funcs = &tinydrm_mode_config_funcs; + + return 0; +} + +static void tinydrm_fini(struct tinydrm_device *tdev) +{ + drm_mode_config_cleanup(tdev->drm); + mutex_destroy(&tdev->dirty_lock); + tdev->drm->dev_private = NULL; + drm_dev_unref(tdev->drm); +} + +static void devm_tinydrm_release(void *data) +{ + tinydrm_fini(data); +} + +/** + * devm_tinydrm_init - Initialize tinydrm device + * @parent: Parent device object + * @tdev: tinydrm device + * @fb_funcs: Framebuffer functions + * @driver: DRM driver + * + * This function initializes @tdev, the underlying DRM device and it's + * mode_config. Resources will be automatically freed on driver detach (devres) + * using drm_mode_config_cleanup() and drm_dev_unref(). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev, + const struct drm_framebuffer_funcs *fb_funcs, + struct drm_driver *driver) +{ + int ret; + + ret = tinydrm_init(parent, tdev, fb_funcs, driver); + if (ret) + return ret; + + ret = devm_add_action(parent, devm_tinydrm_release, tdev); + if (ret) + tinydrm_fini(tdev); + + return ret; +} +EXPORT_SYMBOL(devm_tinydrm_init); + +static int tinydrm_register(struct tinydrm_device *tdev) +{ + struct drm_device *drm = tdev->drm; + int bpp = drm->mode_config.preferred_depth; + struct drm_fbdev_cma *fbdev; + int ret; + + ret = drm_dev_register(tdev->drm, 0); + if (ret) + return ret; + + fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32, + drm->mode_config.num_connector, + tdev->fb_funcs); + if (IS_ERR(fbdev)) + DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev)); + else + tdev->fbdev_cma = fbdev; + + return 0; +} + +static void tinydrm_unregister(struct tinydrm_device *tdev) +{ + struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma; + + drm_crtc_force_disable_all(tdev->drm); + /* don't restore fbdev in lastclose, keep pipeline disabled */ + tdev->fbdev_cma = NULL; + drm_dev_unregister(tdev->drm); + if (fbdev_cma) + drm_fbdev_cma_fini(fbdev_cma); +} + +static void devm_tinydrm_register_release(void *data) +{ + tinydrm_unregister(data); +} + +/** + * devm_tinydrm_register - Register tinydrm device + * @tdev: tinydrm device + * + * This function registers the underlying DRM device and fbdev. + * These resources will be automatically unregistered on driver detach (devres) + * and the display pipeline will be disabled. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int devm_tinydrm_register(struct tinydrm_device *tdev) +{ + struct device *dev = tdev->drm->dev; + int ret; + + ret = tinydrm_register(tdev); + if (ret) + return ret; + + ret = devm_add_action(dev, devm_tinydrm_register_release, tdev); + if (ret) + tinydrm_unregister(tdev); + + return ret; +} +EXPORT_SYMBOL(devm_tinydrm_register); + +/** + * tinydrm_shutdown - Shutdown tinydrm + * @tdev: tinydrm device + * + * This function makes sure that the display pipeline is disabled. + * Used by drivers in their shutdown callback to turn off the display + * on machine shutdown and reboot. + */ +void tinydrm_shutdown(struct tinydrm_device *tdev) +{ + drm_crtc_force_disable_all(tdev->drm); +} +EXPORT_SYMBOL(tinydrm_shutdown); + +/** + * tinydrm_suspend - Suspend tinydrm + * @tdev: tinydrm device + * + * Used in driver PM operations to suspend tinydrm. + * Suspends fbdev and DRM. + * Resume with tinydrm_resume(). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int tinydrm_suspend(struct tinydrm_device *tdev) +{ + struct drm_atomic_state *state; + + if (tdev->suspend_state) { + DRM_ERROR("Failed to suspend: state already set\n"); + return -EINVAL; + } + + drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1); + state = drm_atomic_helper_suspend(tdev->drm); + if (IS_ERR(state)) { + drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); + return PTR_ERR(state); + } + + tdev->suspend_state = state; + + return 0; +} +EXPORT_SYMBOL(tinydrm_suspend); + +/** + * tinydrm_resume - Resume tinydrm + * @tdev: tinydrm device + * + * Used in driver PM operations to resume tinydrm. + * Suspend with tinydrm_suspend(). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int tinydrm_resume(struct tinydrm_device *tdev) +{ + struct drm_atomic_state *state = tdev->suspend_state; + int ret; + + if (!state) { + DRM_ERROR("Failed to resume: state is not set\n"); + return -EINVAL; + } + + tdev->suspend_state = NULL; + + ret = drm_atomic_helper_resume(tdev->drm, state); + if (ret) { + DRM_ERROR("Error resuming state: %d\n", ret); + return ret; + } + + drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); + + return 0; +} +EXPORT_SYMBOL(tinydrm_resume); + +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c new file mode 100644 index 000000000000..3ccda6c1e159 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/tinydrm/tinydrm.h> +#include <drm/tinydrm/tinydrm-helpers.h> +#include <linux/backlight.h> +#include <linux/pm.h> +#include <linux/spi/spi.h> +#include <linux/swab.h> + +static unsigned int spi_max; +module_param(spi_max, uint, 0400); +MODULE_PARM_DESC(spi_max, "Set a lower SPI max transfer size"); + +/** + * tinydrm_merge_clips - Merge clip rectangles + * @dst: Destination clip rectangle + * @src: Source clip rectangle(s) + * @num_clips: Number of @src clip rectangles + * @flags: Dirty fb ioctl flags + * @max_width: Maximum width of @dst + * @max_height: Maximum height of @dst + * + * This function merges @src clip rectangle(s) into @dst. If @src is NULL, + * @max_width and @min_width is used to set a full @dst clip rectangle. + * + * Returns: + * true if it's a full clip, false otherwise + */ +bool tinydrm_merge_clips(struct drm_clip_rect *dst, + struct drm_clip_rect *src, unsigned int num_clips, + unsigned int flags, u32 max_width, u32 max_height) +{ + unsigned int i; + + if (!src || !num_clips) { + dst->x1 = 0; + dst->x2 = max_width; + dst->y1 = 0; + dst->y2 = max_height; + return true; + } + + dst->x1 = ~0; + dst->y1 = ~0; + dst->x2 = 0; + dst->y2 = 0; + + for (i = 0; i < num_clips; i++) { + if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) + i++; + dst->x1 = min(dst->x1, src[i].x1); + dst->x2 = max(dst->x2, src[i].x2); + dst->y1 = min(dst->y1, src[i].y1); + dst->y2 = max(dst->y2, src[i].y2); + } + + if (dst->x2 > max_width || dst->y2 > max_height || + dst->x1 >= dst->x2 || dst->y1 >= dst->y2) { + DRM_DEBUG_KMS("Illegal clip: x1=%u, x2=%u, y1=%u, y2=%u\n", + dst->x1, dst->x2, dst->y1, dst->y2); + dst->x1 = 0; + dst->y1 = 0; + dst->x2 = max_width; + dst->y2 = max_height; + } + + return (dst->x2 - dst->x1) == max_width && + (dst->y2 - dst->y1) == max_height; +} +EXPORT_SYMBOL(tinydrm_merge_clips); + +/** + * tinydrm_memcpy - Copy clip buffer + * @dst: Destination buffer + * @vaddr: Source buffer + * @fb: DRM framebuffer + * @clip: Clip rectangle area to copy + */ +void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, + struct drm_clip_rect *clip) +{ + unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); + unsigned int pitch = fb->pitches[0]; + void *src = vaddr + (clip->y1 * pitch) + (clip->x1 * cpp); + size_t len = (clip->x2 - clip->x1) * cpp; + unsigned int y; + + for (y = clip->y1; y < clip->y2; y++) { + memcpy(dst, src, len); + src += pitch; + dst += len; + } +} +EXPORT_SYMBOL(tinydrm_memcpy); + +/** + * tinydrm_swab16 - Swap bytes into clip buffer + * @dst: RGB565 destination buffer + * @vaddr: RGB565 source buffer + * @fb: DRM framebuffer + * @clip: Clip rectangle area to copy + */ +void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb, + struct drm_clip_rect *clip) +{ + size_t len = (clip->x2 - clip->x1) * sizeof(u16); + unsigned int x, y; + u16 *src, *buf; + + /* + * The cma memory is write-combined so reads are uncached. + * Speed up by fetching one line at a time. + */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + for (y = clip->y1; y < clip->y2; y++) { + src = vaddr + (y * fb->pitches[0]); + src += clip->x1; + memcpy(buf, src, len); + src = buf; + for (x = clip->x1; x < clip->x2; x++) + *dst++ = swab16(*src++); + } + + kfree(buf); +} +EXPORT_SYMBOL(tinydrm_swab16); + +/** + * tinydrm_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer + * @dst: RGB565 destination buffer + * @vaddr: XRGB8888 source buffer + * @fb: DRM framebuffer + * @clip: Clip rectangle area to copy + * @swap: Swap bytes + * + * Drivers can use this function for RGB565 devices that don't natively + * support XRGB8888. + */ +void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr, + struct drm_framebuffer *fb, + struct drm_clip_rect *clip, bool swap) +{ + size_t len = (clip->x2 - clip->x1) * sizeof(u32); + unsigned int x, y; + u32 *src, *buf; + u16 val16; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + for (y = clip->y1; y < clip->y2; y++) { + src = vaddr + (y * fb->pitches[0]); + src += clip->x1; + memcpy(buf, src, len); + src = buf; + for (x = clip->x1; x < clip->x2; x++) { + val16 = ((*src & 0x00F80000) >> 8) | + ((*src & 0x0000FC00) >> 5) | + ((*src & 0x000000F8) >> 3); + src++; + if (swap) + *dst++ = swab16(val16); + else + *dst++ = val16; + } + } + + kfree(buf); +} +EXPORT_SYMBOL(tinydrm_xrgb8888_to_rgb565); + +/** + * tinydrm_of_find_backlight - Find backlight device in device-tree + * @dev: Device + * + * This function looks for a DT node pointed to by a property named 'backlight' + * and uses of_find_backlight_by_node() to get the backlight device. + * Additionally if the brightness property is zero, it is set to + * max_brightness. + * + * Returns: + * NULL if there's no backlight property. + * Error pointer -EPROBE_DEFER if the DT node is found, but no backlight device + * is found. + * If the backlight device is found, a pointer to the structure is returned. + */ +struct backlight_device *tinydrm_of_find_backlight(struct device *dev) +{ + struct backlight_device *backlight; + struct device_node *np; + + np = of_parse_phandle(dev->of_node, "backlight", 0); + if (!np) + return NULL; + + backlight = of_find_backlight_by_node(np); + of_node_put(np); + + if (!backlight) + return ERR_PTR(-EPROBE_DEFER); + + if (!backlight->props.brightness) { + backlight->props.brightness = backlight->props.max_brightness; + DRM_DEBUG_KMS("Backlight brightness set to %d\n", + backlight->props.brightness); + } + + return backlight; +} +EXPORT_SYMBOL(tinydrm_of_find_backlight); + +/** + * tinydrm_enable_backlight - Enable backlight helper + * @backlight: Backlight device + * + * Returns: + * Zero on success, negative error code on failure. + */ +int tinydrm_enable_backlight(struct backlight_device *backlight) +{ + unsigned int old_state; + int ret; + + if (!backlight) + return 0; + + old_state = backlight->props.state; + backlight->props.state &= ~BL_CORE_FBBLANK; + DRM_DEBUG_KMS("Backlight state: 0x%x -> 0x%x\n", old_state, + backlight->props.state); + + ret = backlight_update_status(backlight); + if (ret) + DRM_ERROR("Failed to enable backlight %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(tinydrm_enable_backlight); + +/** + * tinydrm_disable_backlight - Disable backlight helper + * @backlight: Backlight device + * + * Returns: + * Zero on success, negative error code on failure. + */ +int tinydrm_disable_backlight(struct backlight_device *backlight) +{ + unsigned int old_state; + int ret; + + if (!backlight) + return 0; + + old_state = backlight->props.state; + backlight->props.state |= BL_CORE_FBBLANK; + DRM_DEBUG_KMS("Backlight state: 0x%x -> 0x%x\n", old_state, + backlight->props.state); + ret = backlight_update_status(backlight); + if (ret) + DRM_ERROR("Failed to disable backlight %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(tinydrm_disable_backlight); + +#if IS_ENABLED(CONFIG_SPI) + +/** + * tinydrm_spi_max_transfer_size - Determine max SPI transfer size + * @spi: SPI device + * @max_len: Maximum buffer size needed (optional) + * + * This function returns the maximum size to use for SPI transfers. It checks + * the SPI master, the optional @max_len and the module parameter spi_max and + * returns the smallest. + * + * Returns: + * Maximum size for SPI transfers + */ +size_t tinydrm_spi_max_transfer_size(struct spi_device *spi, size_t max_len) +{ + size_t ret; + + ret = min(spi_max_transfer_size(spi), spi->master->max_dma_len); + if (max_len) + ret = min(ret, max_len); + if (spi_max) + ret = min_t(size_t, ret, spi_max); + ret &= ~0x3; + if (ret < 4) + ret = 4; + + return ret; +} +EXPORT_SYMBOL(tinydrm_spi_max_transfer_size); + +/** + * tinydrm_spi_bpw_supported - Check if bits per word is supported + * @spi: SPI device + * @bpw: Bits per word + * + * This function checks to see if the SPI master driver supports @bpw. + * + * Returns: + * True if @bpw is supported, false otherwise. + */ +bool tinydrm_spi_bpw_supported(struct spi_device *spi, u8 bpw) +{ + u32 bpw_mask = spi->master->bits_per_word_mask; + + if (bpw == 8) + return true; + + if (!bpw_mask) { + dev_warn_once(&spi->dev, + "bits_per_word_mask not set, assume 8-bit only\n"); + return false; + } + + if (bpw_mask & SPI_BPW_MASK(bpw)) + return true; + + return false; +} +EXPORT_SYMBOL(tinydrm_spi_bpw_supported); + +static void +tinydrm_dbg_spi_print(struct spi_device *spi, struct spi_transfer *tr, + const void *buf, int idx, bool tx) +{ + u32 speed_hz = tr->speed_hz ? tr->speed_hz : spi->max_speed_hz; + char linebuf[3 * 32]; + + hex_dump_to_buffer(buf, tr->len, 16, + DIV_ROUND_UP(tr->bits_per_word, 8), + linebuf, sizeof(linebuf), false); + + printk(KERN_DEBUG + " tr(%i): speed=%u%s, bpw=%i, len=%u, %s_buf=[%s%s]\n", idx, + speed_hz > 1000000 ? speed_hz / 1000000 : speed_hz / 1000, + speed_hz > 1000000 ? "MHz" : "kHz", tr->bits_per_word, tr->len, + tx ? "tx" : "rx", linebuf, tr->len > 16 ? " ..." : ""); +} + +/* called through tinydrm_dbg_spi_message() */ +void _tinydrm_dbg_spi_message(struct spi_device *spi, struct spi_message *m) +{ + struct spi_transfer *tmp; + struct list_head *pos; + int i = 0; + + list_for_each(pos, &m->transfers) { + tmp = list_entry(pos, struct spi_transfer, transfer_list); + + if (tmp->tx_buf) + tinydrm_dbg_spi_print(spi, tmp, tmp->tx_buf, i, true); + if (tmp->rx_buf) + tinydrm_dbg_spi_print(spi, tmp, tmp->rx_buf, i, false); + i++; + } +} +EXPORT_SYMBOL(_tinydrm_dbg_spi_message); + +/** + * tinydrm_spi_transfer - SPI transfer helper + * @spi: SPI device + * @speed_hz: Override speed (optional) + * @header: Optional header transfer + * @bpw: Bits per word + * @buf: Buffer to transfer + * @len: Buffer length + * + * This SPI transfer helper breaks up the transfer of @buf into chunks which + * the SPI master driver can handle. If the machine is Little Endian and the + * SPI master driver doesn't support 16 bits per word, it swaps the bytes and + * does a 8-bit transfer. + * If @header is set, it is prepended to each SPI message. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz, + struct spi_transfer *header, u8 bpw, const void *buf, + size_t len) +{ + struct spi_transfer tr = { + .bits_per_word = bpw, + .speed_hz = speed_hz, + }; + struct spi_message m; + u16 *swap_buf = NULL; + size_t max_chunk; + size_t chunk; + int ret = 0; + + if (WARN_ON_ONCE(bpw != 8 && bpw != 16)) + return -EINVAL; + + max_chunk = tinydrm_spi_max_transfer_size(spi, 0); + + if (drm_debug & DRM_UT_DRIVER) + pr_debug("[drm:%s] bpw=%u, max_chunk=%zu, transfers:\n", + __func__, bpw, max_chunk); + + if (bpw == 16 && !tinydrm_spi_bpw_supported(spi, 16)) { + tr.bits_per_word = 8; + if (tinydrm_machine_little_endian()) { + swap_buf = kmalloc(min(len, max_chunk), GFP_KERNEL); + if (!swap_buf) + return -ENOMEM; + } + } + + spi_message_init(&m); + if (header) + spi_message_add_tail(header, &m); + spi_message_add_tail(&tr, &m); + + while (len) { + chunk = min(len, max_chunk); + + tr.tx_buf = buf; + tr.len = chunk; + + if (swap_buf) { + const u16 *buf16 = buf; + unsigned int i; + + for (i = 0; i < chunk / 2; i++) + swap_buf[i] = swab16(buf16[i]); + + tr.tx_buf = swap_buf; + } + + buf += chunk; + len -= chunk; + + tinydrm_dbg_spi_message(spi, &m); + ret = spi_sync(spi, &m); + if (ret) + return ret; + }; + + return 0; +} +EXPORT_SYMBOL(tinydrm_spi_transfer); + +#endif /* CONFIG_SPI */ diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c new file mode 100644 index 000000000000..ec43fb7ad9e4 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_modes.h> +#include <drm/tinydrm/tinydrm.h> + +struct tinydrm_connector { + struct drm_connector base; + const struct drm_display_mode *mode; +}; + +static inline struct tinydrm_connector * +to_tinydrm_connector(struct drm_connector *connector) +{ + return container_of(connector, struct tinydrm_connector, base); +} + +static int tinydrm_connector_get_modes(struct drm_connector *connector) +{ + struct tinydrm_connector *tconn = to_tinydrm_connector(connector); + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, tconn->mode); + if (!mode) { + DRM_ERROR("Failed to duplicate mode\n"); + return 0; + } + + if (mode->name[0] == '\0') + drm_mode_set_name(mode); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + + if (mode->width_mm) { + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + } + + return 1; +} + +static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = { + .get_modes = tinydrm_connector_get_modes, + .best_encoder = drm_atomic_helper_best_encoder, +}; + +static enum drm_connector_status +tinydrm_connector_detect(struct drm_connector *connector, bool force) +{ + if (drm_device_is_unplugged(connector->dev)) + return connector_status_disconnected; + + return connector->status; +} + +static void tinydrm_connector_destroy(struct drm_connector *connector) +{ + struct tinydrm_connector *tconn = to_tinydrm_connector(connector); + + drm_connector_cleanup(connector); + kfree(tconn); +} + +static const struct drm_connector_funcs tinydrm_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .reset = drm_atomic_helper_connector_reset, + .detect = tinydrm_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tinydrm_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +struct drm_connector * +tinydrm_connector_create(struct drm_device *drm, + const struct drm_display_mode *mode, + int connector_type) +{ + struct tinydrm_connector *tconn; + struct drm_connector *connector; + int ret; + + tconn = kzalloc(sizeof(*tconn), GFP_KERNEL); + if (!tconn) + return ERR_PTR(-ENOMEM); + + tconn->mode = mode; + connector = &tconn->base; + + drm_connector_helper_add(connector, &tinydrm_connector_hfuncs); + ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs, + connector_type); + if (ret) { + kfree(tconn); + return ERR_PTR(ret); + } + + connector->status = connector_status_connected; + + return connector; +} + +/** + * tinydrm_display_pipe_update - Display pipe update helper + * @pipe: Simple display pipe + * @old_state: Old plane state + * + * This function does a full framebuffer flush if the plane framebuffer + * has changed. It also handles vblank events. Drivers can use this as their + * &drm_simple_display_pipe_funcs->update callback. + */ +void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_state) +{ + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); + struct drm_framebuffer *fb = pipe->plane.state->fb; + struct drm_crtc *crtc = &tdev->pipe.crtc; + + if (fb && (fb != old_state->fb)) { + pipe->plane.fb = fb; + if (fb->funcs->dirty) + fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0); + } + + if (crtc->state->event) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + crtc->state->event = NULL; + } +} +EXPORT_SYMBOL(tinydrm_display_pipe_update); + +/** + * tinydrm_display_pipe_prepare_fb - Display pipe prepare_fb helper + * @pipe: Simple display pipe + * @plane_state: Plane state + * + * This function uses drm_fb_cma_prepare_fb() to check if the plane FB has an + * dma-buf attached, extracts the exclusive fence and attaches it to plane + * state for the atomic helper to wait on. Drivers can use this as their + * &drm_simple_display_pipe_funcs->prepare_fb callback. + */ +int tinydrm_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + return drm_fb_cma_prepare_fb(&pipe->plane, plane_state); +} +EXPORT_SYMBOL(tinydrm_display_pipe_prepare_fb); + +static int tinydrm_rotate_mode(struct drm_display_mode *mode, + unsigned int rotation) +{ + if (rotation == 0 || rotation == 180) { + return 0; + } else if (rotation == 90 || rotation == 270) { + swap(mode->hdisplay, mode->vdisplay); + swap(mode->hsync_start, mode->vsync_start); + swap(mode->hsync_end, mode->vsync_end); + swap(mode->htotal, mode->vtotal); + swap(mode->width_mm, mode->height_mm); + return 0; + } else { + return -EINVAL; + } +} + +/** + * tinydrm_display_pipe_init - Initialize display pipe + * @tdev: tinydrm device + * @funcs: Display pipe functions + * @connector_type: Connector type + * @formats: Array of supported formats (DRM_FORMAT\_\*) + * @format_count: Number of elements in @formats + * @mode: Supported mode + * @rotation: Initial @mode rotation in degrees Counter Clock Wise + * + * This function sets up a &drm_simple_display_pipe with a &drm_connector that + * has one fixed &drm_display_mode which is rotated according to @rotation. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int +tinydrm_display_pipe_init(struct tinydrm_device *tdev, + const struct drm_simple_display_pipe_funcs *funcs, + int connector_type, + const uint32_t *formats, + unsigned int format_count, + const struct drm_display_mode *mode, + unsigned int rotation) +{ + struct drm_device *drm = tdev->drm; + struct drm_display_mode *mode_copy; + struct drm_connector *connector; + int ret; + + mode_copy = devm_kmalloc(drm->dev, sizeof(*mode_copy), GFP_KERNEL); + if (!mode_copy) + return -ENOMEM; + + *mode_copy = *mode; + ret = tinydrm_rotate_mode(mode_copy, rotation); + if (ret) { + DRM_ERROR("Illegal rotation value %u\n", rotation); + return -EINVAL; + } + + drm->mode_config.min_width = mode_copy->hdisplay; + drm->mode_config.max_width = mode_copy->hdisplay; + drm->mode_config.min_height = mode_copy->vdisplay; + drm->mode_config.max_height = mode_copy->vdisplay; + + connector = tinydrm_connector_create(drm, mode_copy, connector_type); + if (IS_ERR(connector)) + return PTR_ERR(connector); + + ret = drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats, + format_count, connector); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(tinydrm_display_pipe_init); diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c new file mode 100644 index 000000000000..b29fe86158f7 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c @@ -0,0 +1,279 @@ +/* + * DRM driver for Multi-Inno MI0283QT panels + * + * Copyright 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/tinydrm/ili9341.h> +#include <drm/tinydrm/mipi-dbi.h> +#include <drm/tinydrm/tinydrm-helpers.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <video/mipi_display.h> + +static int mi0283qt_init(struct mipi_dbi *mipi) +{ + struct tinydrm_device *tdev = &mipi->tinydrm; + struct device *dev = tdev->drm->dev; + u8 addr_mode; + int ret; + + DRM_DEBUG_KMS("\n"); + + ret = regulator_enable(mipi->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator %d\n", ret); + return ret; + } + + /* Avoid flicker by skipping setup if the bootloader has done it */ + if (mipi_dbi_display_is_on(mipi)) + return 0; + + mipi_dbi_hw_reset(mipi); + ret = mipi_dbi_command(mipi, MIPI_DCS_SOFT_RESET); + if (ret) { + dev_err(dev, "Error sending command %d\n", ret); + regulator_disable(mipi->regulator); + return ret; + } + + msleep(20); + + mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_OFF); + + mipi_dbi_command(mipi, ILI9341_PWCTRLB, 0x00, 0x83, 0x30); + mipi_dbi_command(mipi, ILI9341_PWRSEQ, 0x64, 0x03, 0x12, 0x81); + mipi_dbi_command(mipi, ILI9341_DTCTRLA, 0x85, 0x01, 0x79); + mipi_dbi_command(mipi, ILI9341_PWCTRLA, 0x39, 0x2c, 0x00, 0x34, 0x02); + mipi_dbi_command(mipi, ILI9341_PUMPCTRL, 0x20); + mipi_dbi_command(mipi, ILI9341_DTCTRLB, 0x00, 0x00); + + /* Power Control */ + mipi_dbi_command(mipi, ILI9341_PWCTRL1, 0x26); + mipi_dbi_command(mipi, ILI9341_PWCTRL2, 0x11); + /* VCOM */ + mipi_dbi_command(mipi, ILI9341_VMCTRL1, 0x35, 0x3e); + mipi_dbi_command(mipi, ILI9341_VMCTRL2, 0xbe); + + /* Memory Access Control */ + mipi_dbi_command(mipi, MIPI_DCS_SET_PIXEL_FORMAT, 0x55); + + switch (mipi->rotation) { + default: + addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY | + ILI9341_MADCTL_MX; + break; + case 90: + addr_mode = ILI9341_MADCTL_MY; + break; + case 180: + addr_mode = ILI9341_MADCTL_MV; + break; + case 270: + addr_mode = ILI9341_MADCTL_MX; + break; + } + addr_mode |= ILI9341_MADCTL_BGR; + mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); + + /* Frame Rate */ + mipi_dbi_command(mipi, ILI9341_FRMCTR1, 0x00, 0x1b); + + /* Gamma */ + mipi_dbi_command(mipi, ILI9341_EN3GAM, 0x08); + mipi_dbi_command(mipi, MIPI_DCS_SET_GAMMA_CURVE, 0x01); + mipi_dbi_command(mipi, ILI9341_PGAMCTRL, + 0x1f, 0x1a, 0x18, 0x0a, 0x0f, 0x06, 0x45, 0x87, + 0x32, 0x0a, 0x07, 0x02, 0x07, 0x05, 0x00); + mipi_dbi_command(mipi, ILI9341_NGAMCTRL, + 0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3a, 0x78, + 0x4d, 0x05, 0x18, 0x0d, 0x38, 0x3a, 0x1f); + + /* DDRAM */ + mipi_dbi_command(mipi, ILI9341_ETMOD, 0x07); + + /* Display */ + mipi_dbi_command(mipi, ILI9341_DISCTRL, 0x0a, 0x82, 0x27, 0x00); + mipi_dbi_command(mipi, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(100); + + mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON); + msleep(100); + + return 0; +} + +static void mi0283qt_fini(void *data) +{ + struct mipi_dbi *mipi = data; + + DRM_DEBUG_KMS("\n"); + regulator_disable(mipi->regulator); +} + +static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = { + .enable = mipi_dbi_pipe_enable, + .disable = mipi_dbi_pipe_disable, + .update = tinydrm_display_pipe_update, + .prepare_fb = tinydrm_display_pipe_prepare_fb, +}; + +static const struct drm_display_mode mi0283qt_mode = { + TINYDRM_MODE(320, 240, 58, 43), +}; + +static struct drm_driver mi0283qt_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | + DRIVER_ATOMIC, + TINYDRM_GEM_DRIVER_OPS, + .lastclose = tinydrm_lastclose, + .debugfs_init = mipi_dbi_debugfs_init, + .name = "mi0283qt", + .desc = "Multi-Inno MI0283QT", + .date = "20160614", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id mi0283qt_of_match[] = { + { .compatible = "multi-inno,mi0283qt" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mi0283qt_of_match); + +static const struct spi_device_id mi0283qt_id[] = { + { "mi0283qt", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, mi0283qt_id); + +static int mi0283qt_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct tinydrm_device *tdev; + struct mipi_dbi *mipi; + struct gpio_desc *dc; + u32 rotation = 0; + int ret; + + mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL); + if (!mipi) + return -ENOMEM; + + mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(mipi->reset)) { + dev_err(dev, "Failed to get gpio 'reset'\n"); + return PTR_ERR(mipi->reset); + } + + dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); + if (IS_ERR(dc)) { + dev_err(dev, "Failed to get gpio 'dc'\n"); + return PTR_ERR(dc); + } + + mipi->regulator = devm_regulator_get(dev, "power"); + if (IS_ERR(mipi->regulator)) + return PTR_ERR(mipi->regulator); + + mipi->backlight = tinydrm_of_find_backlight(dev); + if (IS_ERR(mipi->backlight)) + return PTR_ERR(mipi->backlight); + + device_property_read_u32(dev, "rotation", &rotation); + + ret = mipi_dbi_spi_init(spi, mipi, dc, &mi0283qt_pipe_funcs, + &mi0283qt_driver, &mi0283qt_mode, rotation); + if (ret) + return ret; + + ret = mi0283qt_init(mipi); + if (ret) + return ret; + + /* use devres to fini after drm unregister (drv->remove is before) */ + ret = devm_add_action(dev, mi0283qt_fini, mipi); + if (ret) { + mi0283qt_fini(mipi); + return ret; + } + + tdev = &mipi->tinydrm; + + ret = devm_tinydrm_register(tdev); + if (ret) + return ret; + + spi_set_drvdata(spi, mipi); + + DRM_DEBUG_DRIVER("Initialized %s:%s @%uMHz on minor %d\n", + tdev->drm->driver->name, dev_name(dev), + spi->max_speed_hz / 1000000, + tdev->drm->primary->index); + + return 0; +} + +static void mi0283qt_shutdown(struct spi_device *spi) +{ + struct mipi_dbi *mipi = spi_get_drvdata(spi); + + tinydrm_shutdown(&mipi->tinydrm); +} + +static int __maybe_unused mi0283qt_pm_suspend(struct device *dev) +{ + struct mipi_dbi *mipi = dev_get_drvdata(dev); + int ret; + + ret = tinydrm_suspend(&mipi->tinydrm); + if (ret) + return ret; + + mi0283qt_fini(mipi); + + return 0; +} + +static int __maybe_unused mi0283qt_pm_resume(struct device *dev) +{ + struct mipi_dbi *mipi = dev_get_drvdata(dev); + int ret; + + ret = mi0283qt_init(mipi); + if (ret) + return ret; + + return tinydrm_resume(&mipi->tinydrm); +} + +static const struct dev_pm_ops mi0283qt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mi0283qt_pm_suspend, mi0283qt_pm_resume) +}; + +static struct spi_driver mi0283qt_spi_driver = { + .driver = { + .name = "mi0283qt", + .owner = THIS_MODULE, + .of_match_table = mi0283qt_of_match, + .pm = &mi0283qt_pm_ops, + }, + .id_table = mi0283qt_id, + .probe = mi0283qt_probe, + .shutdown = mi0283qt_shutdown, +}; +module_spi_driver(mi0283qt_spi_driver); + +MODULE_DESCRIPTION("Multi-Inno MI0283QT DRM driver"); +MODULE_AUTHOR("Noralf Trønnes"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c new file mode 100644 index 000000000000..29c0939f5247 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -0,0 +1,1005 @@ +/* + * MIPI Display Bus Interface (DBI) LCD controller support + * + * Copyright 2016 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/tinydrm/mipi-dbi.h> +#include <drm/tinydrm/tinydrm-helpers.h> +#include <linux/debugfs.h> +#include <linux/dma-buf.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <video/mipi_display.h> + +#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */ + +#define DCS_POWER_MODE_DISPLAY BIT(2) +#define DCS_POWER_MODE_DISPLAY_NORMAL_MODE BIT(3) +#define DCS_POWER_MODE_SLEEP_MODE BIT(4) +#define DCS_POWER_MODE_PARTIAL_MODE BIT(5) +#define DCS_POWER_MODE_IDLE_MODE BIT(6) +#define DCS_POWER_MODE_RESERVED_MASK (BIT(0) | BIT(1) | BIT(7)) + +/** + * DOC: overview + * + * This library provides helpers for MIPI Display Bus Interface (DBI) + * compatible display controllers. + * + * Many controllers for tiny lcd displays are MIPI compliant and can use this + * library. If a controller uses registers 0x2A and 0x2B to set the area to + * update and uses register 0x2C to write to frame memory, it is most likely + * MIPI compliant. + * + * Only MIPI Type 1 displays are supported since a full frame memory is needed. + * + * There are 3 MIPI DBI implementation types: + * + * A. Motorola 6800 type parallel bus + * + * B. Intel 8080 type parallel bus + * + * C. SPI type with 3 options: + * + * 1. 9-bit with the Data/Command signal as the ninth bit + * 2. Same as above except it's sent as 16 bits + * 3. 8-bit with the Data/Command signal as a separate D/CX pin + * + * Currently mipi_dbi only supports Type C options 1 and 3 with + * mipi_dbi_spi_init(). + */ + +#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \ +({ \ + if (!len) \ + DRM_DEBUG_DRIVER("cmd=%02x\n", cmd); \ + else if (len <= 32) \ + DRM_DEBUG_DRIVER("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\ + else \ + DRM_DEBUG_DRIVER("cmd=%02x, len=%zu\n", cmd, len); \ +}) + +static const u8 mipi_dbi_dcs_read_commands[] = { + MIPI_DCS_GET_DISPLAY_ID, + MIPI_DCS_GET_RED_CHANNEL, + MIPI_DCS_GET_GREEN_CHANNEL, + MIPI_DCS_GET_BLUE_CHANNEL, + MIPI_DCS_GET_DISPLAY_STATUS, + MIPI_DCS_GET_POWER_MODE, + MIPI_DCS_GET_ADDRESS_MODE, + MIPI_DCS_GET_PIXEL_FORMAT, + MIPI_DCS_GET_DISPLAY_MODE, + MIPI_DCS_GET_SIGNAL_MODE, + MIPI_DCS_GET_DIAGNOSTIC_RESULT, + MIPI_DCS_READ_MEMORY_START, + MIPI_DCS_READ_MEMORY_CONTINUE, + MIPI_DCS_GET_SCANLINE, + MIPI_DCS_GET_DISPLAY_BRIGHTNESS, + MIPI_DCS_GET_CONTROL_DISPLAY, + MIPI_DCS_GET_POWER_SAVE, + MIPI_DCS_GET_CABC_MIN_BRIGHTNESS, + MIPI_DCS_READ_DDB_START, + MIPI_DCS_READ_DDB_CONTINUE, + 0, /* sentinel */ +}; + +static bool mipi_dbi_command_is_read(struct mipi_dbi *mipi, u8 cmd) +{ + unsigned int i; + + if (!mipi->read_commands) + return false; + + for (i = 0; i < 0xff; i++) { + if (!mipi->read_commands[i]) + return false; + if (cmd == mipi->read_commands[i]) + return true; + } + + return false; +} + +/** + * mipi_dbi_command_read - MIPI DCS read command + * @mipi: MIPI structure + * @cmd: Command + * @val: Value read + * + * Send MIPI DCS read command to the controller. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_command_read(struct mipi_dbi *mipi, u8 cmd, u8 *val) +{ + if (!mipi->read_commands) + return -EACCES; + + if (!mipi_dbi_command_is_read(mipi, cmd)) + return -EINVAL; + + return mipi_dbi_command_buf(mipi, cmd, val, 1); +} +EXPORT_SYMBOL(mipi_dbi_command_read); + +/** + * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array + * @mipi: MIPI structure + * @cmd: Command + * @data: Parameter buffer + * @len: Buffer length + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len) +{ + int ret; + + mutex_lock(&mipi->cmdlock); + ret = mipi->command(mipi, cmd, data, len); + mutex_unlock(&mipi->cmdlock); + + return ret; +} +EXPORT_SYMBOL(mipi_dbi_command_buf); + +static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, + struct drm_clip_rect *clip, bool swap) +{ + struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + struct dma_buf_attachment *import_attach = cma_obj->base.import_attach; + struct drm_format_name_buf format_name; + void *src = cma_obj->vaddr; + int ret = 0; + + if (import_attach) { + ret = dma_buf_begin_cpu_access(import_attach->dmabuf, + DMA_FROM_DEVICE); + if (ret) + return ret; + } + + switch (fb->format->format) { + case DRM_FORMAT_RGB565: + if (swap) + tinydrm_swab16(dst, src, fb, clip); + else + tinydrm_memcpy(dst, src, fb, clip); + break; + case DRM_FORMAT_XRGB8888: + tinydrm_xrgb8888_to_rgb565(dst, src, fb, clip, swap); + break; + default: + dev_err_once(fb->dev->dev, "Format is not supported: %s\n", + drm_get_format_name(fb->format->format, + &format_name)); + return -EINVAL; + } + + if (import_attach) + ret = dma_buf_end_cpu_access(import_attach->dmabuf, + DMA_FROM_DEVICE); + return ret; +} + +static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int flags, unsigned int color, + struct drm_clip_rect *clips, + unsigned int num_clips) +{ + struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + struct tinydrm_device *tdev = fb->dev->dev_private; + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + bool swap = mipi->swap_bytes; + struct drm_clip_rect clip; + int ret = 0; + bool full; + void *tr; + + mutex_lock(&tdev->dirty_lock); + + if (!mipi->enabled) + goto out_unlock; + + /* fbdev can flush even when we're not interested */ + if (tdev->pipe.plane.fb != fb) + goto out_unlock; + + full = tinydrm_merge_clips(&clip, clips, num_clips, flags, + fb->width, fb->height); + + DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id, + clip.x1, clip.x2, clip.y1, clip.y2); + + if (!mipi->dc || !full || swap || + fb->format->format == DRM_FORMAT_XRGB8888) { + tr = mipi->tx_buf; + ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap); + if (ret) + goto out_unlock; + } else { + tr = cma_obj->vaddr; + } + + mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS, + (clip.x1 >> 8) & 0xFF, clip.x1 & 0xFF, + (clip.x2 >> 8) & 0xFF, (clip.x2 - 1) & 0xFF); + mipi_dbi_command(mipi, MIPI_DCS_SET_PAGE_ADDRESS, + (clip.y1 >> 8) & 0xFF, clip.y1 & 0xFF, + (clip.y2 >> 8) & 0xFF, (clip.y2 - 1) & 0xFF); + + ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, tr, + (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2); + +out_unlock: + mutex_unlock(&tdev->dirty_lock); + + if (ret) + dev_err_once(fb->dev->dev, "Failed to update display %d\n", + ret); + + return ret; +} + +static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = { + .destroy = drm_fb_cma_destroy, + .create_handle = drm_fb_cma_create_handle, + .dirty = mipi_dbi_fb_dirty, +}; + +/** + * mipi_dbi_pipe_enable - MIPI DBI pipe enable helper + * @pipe: Display pipe + * @crtc_state: CRTC state + * + * This function enables backlight. Drivers can use this as their + * &drm_simple_display_pipe_funcs->enable callback. + */ +void mipi_dbi_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state) +{ + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + struct drm_framebuffer *fb = pipe->plane.fb; + + DRM_DEBUG_KMS("\n"); + + mipi->enabled = true; + if (fb) + fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0); + + tinydrm_enable_backlight(mipi->backlight); +} +EXPORT_SYMBOL(mipi_dbi_pipe_enable); + +static void mipi_dbi_blank(struct mipi_dbi *mipi) +{ + struct drm_device *drm = mipi->tinydrm.drm; + u16 height = drm->mode_config.min_height; + u16 width = drm->mode_config.min_width; + size_t len = width * height * 2; + + memset(mipi->tx_buf, 0, len); + + mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS, 0, 0, + (width >> 8) & 0xFF, (width - 1) & 0xFF); + mipi_dbi_command(mipi, MIPI_DCS_SET_PAGE_ADDRESS, 0, 0, + (height >> 8) & 0xFF, (height - 1) & 0xFF); + mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, + (u8 *)mipi->tx_buf, len); +} + +/** + * mipi_dbi_pipe_disable - MIPI DBI pipe disable helper + * @pipe: Display pipe + * + * This function disables backlight if present or if not the + * display memory is blanked. Drivers can use this as their + * &drm_simple_display_pipe_funcs->disable callback. + */ +void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + + DRM_DEBUG_KMS("\n"); + + mipi->enabled = false; + + if (mipi->backlight) + tinydrm_disable_backlight(mipi->backlight); + else + mipi_dbi_blank(mipi); +} +EXPORT_SYMBOL(mipi_dbi_pipe_disable); + +static const uint32_t mipi_dbi_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, +}; + +/** + * mipi_dbi_init - MIPI DBI initialization + * @dev: Parent device + * @mipi: &mipi_dbi structure to initialize + * @pipe_funcs: Display pipe functions + * @driver: DRM driver + * @mode: Display mode + * @rotation: Initial rotation in degrees Counter Clock Wise + * + * This function initializes a &mipi_dbi structure and it's underlying + * @tinydrm_device. It also sets up the display pipeline. + * + * Supported formats: Native RGB565 and emulated XRGB8888. + * + * Objects created by this function will be automatically freed on driver + * detach (devres). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi, + const struct drm_simple_display_pipe_funcs *pipe_funcs, + struct drm_driver *driver, + const struct drm_display_mode *mode, unsigned int rotation) +{ + size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16); + struct tinydrm_device *tdev = &mipi->tinydrm; + int ret; + + if (!mipi->command) + return -EINVAL; + + mutex_init(&mipi->cmdlock); + + mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL); + if (!mipi->tx_buf) + return -ENOMEM; + + ret = devm_tinydrm_init(dev, tdev, &mipi_dbi_fb_funcs, driver); + if (ret) + return ret; + + /* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */ + ret = tinydrm_display_pipe_init(tdev, pipe_funcs, + DRM_MODE_CONNECTOR_VIRTUAL, + mipi_dbi_formats, + ARRAY_SIZE(mipi_dbi_formats), mode, + rotation); + if (ret) + return ret; + + tdev->drm->mode_config.preferred_depth = 16; + mipi->rotation = rotation; + + drm_mode_config_reset(tdev->drm); + + DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n", + tdev->drm->mode_config.preferred_depth, rotation); + + return 0; +} +EXPORT_SYMBOL(mipi_dbi_init); + +/** + * mipi_dbi_hw_reset - Hardware reset of controller + * @mipi: MIPI DBI structure + * + * Reset controller if the &mipi_dbi->reset gpio is set. + */ +void mipi_dbi_hw_reset(struct mipi_dbi *mipi) +{ + if (!mipi->reset) + return; + + gpiod_set_value_cansleep(mipi->reset, 0); + msleep(20); + gpiod_set_value_cansleep(mipi->reset, 1); + msleep(120); +} +EXPORT_SYMBOL(mipi_dbi_hw_reset); + +/** + * mipi_dbi_display_is_on - Check if display is on + * @mipi: MIPI DBI structure + * + * This function checks the Power Mode register (if readable) to see if + * display output is turned on. This can be used to see if the bootloader + * has already turned on the display avoiding flicker when the pipeline is + * enabled. + * + * Returns: + * true if the display can be verified to be on, false otherwise. + */ +bool mipi_dbi_display_is_on(struct mipi_dbi *mipi) +{ + u8 val; + + if (mipi_dbi_command_read(mipi, MIPI_DCS_GET_POWER_MODE, &val)) + return false; + + val &= ~DCS_POWER_MODE_RESERVED_MASK; + + if (val != (DCS_POWER_MODE_DISPLAY | + DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE)) + return false; + + DRM_DEBUG_DRIVER("Display is ON\n"); + + return true; +} +EXPORT_SYMBOL(mipi_dbi_display_is_on); + +#if IS_ENABLED(CONFIG_SPI) + +/* + * Many controllers have a max speed of 10MHz, but can be pushed way beyond + * that. Increase reliability by running pixel data at max speed and the rest + * at 10MHz, preventing transfer glitches from messing up the init settings. + */ +static u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len) +{ + if (len > 64) + return 0; /* use default */ + + return min_t(u32, 10000000, spi->max_speed_hz); +} + +/* + * MIPI DBI Type C Option 1 + * + * If the SPI controller doesn't have 9 bits per word support, + * use blocks of 9 bytes to send 8x 9-bit words using a 8-bit SPI transfer. + * Pad partial blocks with MIPI_DCS_NOP (zero). + * This is how the D/C bit (x) is added: + * x7654321 + * 0x765432 + * 10x76543 + * 210x7654 + * 3210x765 + * 43210x76 + * 543210x7 + * 6543210x + * 76543210 + */ + +static int mipi_dbi_spi1e_transfer(struct mipi_dbi *mipi, int dc, + const void *buf, size_t len, + unsigned int bpw) +{ + bool swap_bytes = (bpw == 16 && tinydrm_machine_little_endian()); + size_t chunk, max_chunk = mipi->tx_buf9_len; + struct spi_device *spi = mipi->spi; + struct spi_transfer tr = { + .tx_buf = mipi->tx_buf9, + .bits_per_word = 8, + }; + struct spi_message m; + const u8 *src = buf; + int i, ret; + u8 *dst; + + if (drm_debug & DRM_UT_DRIVER) + pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n", + __func__, dc, max_chunk); + + tr.speed_hz = mipi_dbi_spi_cmd_max_speed(spi, len); + spi_message_init_with_transfers(&m, &tr, 1); + + if (!dc) { + if (WARN_ON_ONCE(len != 1)) + return -EINVAL; + + /* Command: pad no-op's (zeroes) at beginning of block */ + dst = mipi->tx_buf9; + memset(dst, 0, 9); + dst[8] = *src; + tr.len = 9; + + tinydrm_dbg_spi_message(spi, &m); + + return spi_sync(spi, &m); + } + + /* max with room for adding one bit per byte */ + max_chunk = max_chunk / 9 * 8; + /* but no bigger than len */ + max_chunk = min(max_chunk, len); + /* 8 byte blocks */ + max_chunk = max_t(size_t, 8, max_chunk & ~0x7); + + while (len) { + size_t added = 0; + + chunk = min(len, max_chunk); + len -= chunk; + dst = mipi->tx_buf9; + + if (chunk < 8) { + u8 val, carry = 0; + + /* Data: pad no-op's (zeroes) at end of block */ + memset(dst, 0, 9); + + if (swap_bytes) { + for (i = 1; i < (chunk + 1); i++) { + val = src[1]; + *dst++ = carry | BIT(8 - i) | (val >> i); + carry = val << (8 - i); + i++; + val = src[0]; + *dst++ = carry | BIT(8 - i) | (val >> i); + carry = val << (8 - i); + src += 2; + } + *dst++ = carry; + } else { + for (i = 1; i < (chunk + 1); i++) { + val = *src++; + *dst++ = carry | BIT(8 - i) | (val >> i); + carry = val << (8 - i); + } + *dst++ = carry; + } + + chunk = 8; + added = 1; + } else { + for (i = 0; i < chunk; i += 8) { + if (swap_bytes) { + *dst++ = BIT(7) | (src[1] >> 1); + *dst++ = (src[1] << 7) | BIT(6) | (src[0] >> 2); + *dst++ = (src[0] << 6) | BIT(5) | (src[3] >> 3); + *dst++ = (src[3] << 5) | BIT(4) | (src[2] >> 4); + *dst++ = (src[2] << 4) | BIT(3) | (src[5] >> 5); + *dst++ = (src[5] << 3) | BIT(2) | (src[4] >> 6); + *dst++ = (src[4] << 2) | BIT(1) | (src[7] >> 7); + *dst++ = (src[7] << 1) | BIT(0); + *dst++ = src[6]; + } else { + *dst++ = BIT(7) | (src[0] >> 1); + *dst++ = (src[0] << 7) | BIT(6) | (src[1] >> 2); + *dst++ = (src[1] << 6) | BIT(5) | (src[2] >> 3); + *dst++ = (src[2] << 5) | BIT(4) | (src[3] >> 4); + *dst++ = (src[3] << 4) | BIT(3) | (src[4] >> 5); + *dst++ = (src[4] << 3) | BIT(2) | (src[5] >> 6); + *dst++ = (src[5] << 2) | BIT(1) | (src[6] >> 7); + *dst++ = (src[6] << 1) | BIT(0); + *dst++ = src[7]; + } + + src += 8; + added++; + } + } + + tr.len = chunk + added; + + tinydrm_dbg_spi_message(spi, &m); + ret = spi_sync(spi, &m); + if (ret) + return ret; + }; + + return 0; +} + +static int mipi_dbi_spi1_transfer(struct mipi_dbi *mipi, int dc, + const void *buf, size_t len, + unsigned int bpw) +{ + struct spi_device *spi = mipi->spi; + struct spi_transfer tr = { + .bits_per_word = 9, + }; + const u16 *src16 = buf; + const u8 *src8 = buf; + struct spi_message m; + size_t max_chunk; + u16 *dst16; + int ret; + + if (!tinydrm_spi_bpw_supported(spi, 9)) + return mipi_dbi_spi1e_transfer(mipi, dc, buf, len, bpw); + + tr.speed_hz = mipi_dbi_spi_cmd_max_speed(spi, len); + max_chunk = mipi->tx_buf9_len; + dst16 = mipi->tx_buf9; + + if (drm_debug & DRM_UT_DRIVER) + pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n", + __func__, dc, max_chunk); + + max_chunk = min(max_chunk / 2, len); + + spi_message_init_with_transfers(&m, &tr, 1); + tr.tx_buf = dst16; + + while (len) { + size_t chunk = min(len, max_chunk); + unsigned int i; + + if (bpw == 16 && tinydrm_machine_little_endian()) { + for (i = 0; i < (chunk * 2); i += 2) { + dst16[i] = *src16 >> 8; + dst16[i + 1] = *src16++ & 0xFF; + if (dc) { + dst16[i] |= 0x0100; + dst16[i + 1] |= 0x0100; + } + } + } else { + for (i = 0; i < chunk; i++) { + dst16[i] = *src8++; + if (dc) + dst16[i] |= 0x0100; + } + } + + tr.len = chunk; + len -= chunk; + + tinydrm_dbg_spi_message(spi, &m); + ret = spi_sync(spi, &m); + if (ret) + return ret; + }; + + return 0; +} + +static int mipi_dbi_typec1_command(struct mipi_dbi *mipi, u8 cmd, + u8 *parameters, size_t num) +{ + unsigned int bpw = (cmd == MIPI_DCS_WRITE_MEMORY_START) ? 16 : 8; + int ret; + + if (mipi_dbi_command_is_read(mipi, cmd)) + return -ENOTSUPP; + + MIPI_DBI_DEBUG_COMMAND(cmd, parameters, num); + + ret = mipi_dbi_spi1_transfer(mipi, 0, &cmd, 1, 8); + if (ret || !num) + return ret; + + return mipi_dbi_spi1_transfer(mipi, 1, parameters, num, bpw); +} + +/* MIPI DBI Type C Option 3 */ + +static int mipi_dbi_typec3_command_read(struct mipi_dbi *mipi, u8 cmd, + u8 *data, size_t len) +{ + struct spi_device *spi = mipi->spi; + u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED, + spi->max_speed_hz / 2); + struct spi_transfer tr[2] = { + { + .speed_hz = speed_hz, + .tx_buf = &cmd, + .len = 1, + }, { + .speed_hz = speed_hz, + .len = len, + }, + }; + struct spi_message m; + u8 *buf; + int ret; + + if (!len) + return -EINVAL; + + /* + * Support non-standard 24-bit and 32-bit Nokia read commands which + * start with a dummy clock, so we need to read an extra byte. + */ + if (cmd == MIPI_DCS_GET_DISPLAY_ID || + cmd == MIPI_DCS_GET_DISPLAY_STATUS) { + if (!(len == 3 || len == 4)) + return -EINVAL; + + tr[1].len = len + 1; + } + + buf = kmalloc(tr[1].len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + tr[1].rx_buf = buf; + gpiod_set_value_cansleep(mipi->dc, 0); + + spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr)); + ret = spi_sync(spi, &m); + if (ret) + goto err_free; + + tinydrm_dbg_spi_message(spi, &m); + + if (tr[1].len == len) { + memcpy(data, buf, len); + } else { + unsigned int i; + + for (i = 0; i < len; i++) + data[i] = (buf[i] << 1) | !!(buf[i + 1] & BIT(7)); + } + + MIPI_DBI_DEBUG_COMMAND(cmd, data, len); + +err_free: + kfree(buf); + + return ret; +} + +static int mipi_dbi_typec3_command(struct mipi_dbi *mipi, u8 cmd, + u8 *par, size_t num) +{ + struct spi_device *spi = mipi->spi; + unsigned int bpw = 8; + u32 speed_hz; + int ret; + + if (mipi_dbi_command_is_read(mipi, cmd)) + return mipi_dbi_typec3_command_read(mipi, cmd, par, num); + + MIPI_DBI_DEBUG_COMMAND(cmd, par, num); + + gpiod_set_value_cansleep(mipi->dc, 0); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); + ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1); + if (ret || !num) + return ret; + + if (cmd == MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes) + bpw = 16; + + gpiod_set_value_cansleep(mipi->dc, 1); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); + + return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num); +} + +/** + * mipi_dbi_spi_init - Initialize MIPI DBI SPI interfaced controller + * @spi: SPI device + * @dc: D/C gpio (optional) + * @mipi: &mipi_dbi structure to initialize + * @pipe_funcs: Display pipe functions + * @driver: DRM driver + * @mode: Display mode + * @rotation: Initial rotation in degrees Counter Clock Wise + * + * This function sets &mipi_dbi->command, enables &mipi->read_commands for the + * usual read commands and initializes @mipi using mipi_dbi_init(). + * + * If @dc is set, a Type C Option 3 interface is assumed, if not + * Type C Option 1. + * + * If the SPI master driver doesn't support the necessary bits per word, + * the following transformation is used: + * + * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command. + * - 16-bit: if big endian send as 8-bit, if little endian swap bytes + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi, + struct gpio_desc *dc, + const struct drm_simple_display_pipe_funcs *pipe_funcs, + struct drm_driver *driver, + const struct drm_display_mode *mode, + unsigned int rotation) +{ + size_t tx_size = tinydrm_spi_max_transfer_size(spi, 0); + struct device *dev = &spi->dev; + int ret; + + if (tx_size < 16) { + DRM_ERROR("SPI transmit buffer too small: %zu\n", tx_size); + return -EINVAL; + } + + /* + * Even though it's not the SPI device that does DMA (the master does), + * the dma mask is necessary for the dma_alloc_wc() in + * drm_gem_cma_create(). The dma_addr returned will be a physical + * adddress which might be different from the bus address, but this is + * not a problem since the address will not be used. + * The virtual address is used in the transfer and the SPI core + * re-maps it on the SPI master device using the DMA streaming API + * (spi_map_buf()). + */ + if (!dev->coherent_dma_mask) { + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_warn(dev, "Failed to set dma mask %d\n", ret); + return ret; + } + } + + mipi->spi = spi; + mipi->read_commands = mipi_dbi_dcs_read_commands; + + if (dc) { + mipi->command = mipi_dbi_typec3_command; + mipi->dc = dc; + if (tinydrm_machine_little_endian() && + !tinydrm_spi_bpw_supported(spi, 16)) + mipi->swap_bytes = true; + } else { + mipi->command = mipi_dbi_typec1_command; + mipi->tx_buf9_len = tx_size; + mipi->tx_buf9 = devm_kmalloc(dev, tx_size, GFP_KERNEL); + if (!mipi->tx_buf9) + return -ENOMEM; + } + + return mipi_dbi_init(dev, mipi, pipe_funcs, driver, mode, rotation); +} +EXPORT_SYMBOL(mipi_dbi_spi_init); + +#endif /* CONFIG_SPI */ + +#ifdef CONFIG_DEBUG_FS + +static ssize_t mipi_dbi_debugfs_command_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *m = file->private_data; + struct mipi_dbi *mipi = m->private; + u8 val, cmd = 0, parameters[64]; + char *buf, *pos, *token; + unsigned int i; + int ret; + + buf = memdup_user_nul(ubuf, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + /* strip trailing whitespace */ + for (i = count - 1; i > 0; i--) + if (isspace(buf[i])) + buf[i] = '\0'; + else + break; + i = 0; + pos = buf; + while (pos) { + token = strsep(&pos, " "); + if (!token) { + ret = -EINVAL; + goto err_free; + } + + ret = kstrtou8(token, 16, &val); + if (ret < 0) + goto err_free; + + if (token == buf) + cmd = val; + else + parameters[i++] = val; + + if (i == 64) { + ret = -E2BIG; + goto err_free; + } + } + + ret = mipi_dbi_command_buf(mipi, cmd, parameters, i); + +err_free: + kfree(buf); + + return ret < 0 ? ret : count; +} + +static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused) +{ + struct mipi_dbi *mipi = m->private; + u8 cmd, val[4]; + size_t len, i; + int ret; + + for (cmd = 0; cmd < 255; cmd++) { + if (!mipi_dbi_command_is_read(mipi, cmd)) + continue; + + switch (cmd) { + case MIPI_DCS_READ_MEMORY_START: + case MIPI_DCS_READ_MEMORY_CONTINUE: + len = 2; + break; + case MIPI_DCS_GET_DISPLAY_ID: + len = 3; + break; + case MIPI_DCS_GET_DISPLAY_STATUS: + len = 4; + break; + default: + len = 1; + break; + } + + seq_printf(m, "%02x: ", cmd); + ret = mipi_dbi_command_buf(mipi, cmd, val, len); + if (ret) { + seq_puts(m, "XX\n"); + continue; + } + + for (i = 0; i < len; i++) + seq_printf(m, "%02x", val[i]); + seq_puts(m, "\n"); + } + + return 0; +} + +static int mipi_dbi_debugfs_command_open(struct inode *inode, + struct file *file) +{ + return single_open(file, mipi_dbi_debugfs_command_show, + inode->i_private); +} + +static const struct file_operations mipi_dbi_debugfs_command_fops = { + .owner = THIS_MODULE, + .open = mipi_dbi_debugfs_command_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = mipi_dbi_debugfs_command_write, +}; + +static const struct drm_info_list mipi_dbi_debugfs_list[] = { + { "fb", drm_fb_cma_debugfs_show, 0 }, +}; + +/** + * mipi_dbi_debugfs_init - Create debugfs entries + * @minor: DRM minor + * + * This function creates a 'command' debugfs file for sending commands to the + * controller or getting the read command values. + * Drivers can use this as their &drm_driver->debugfs_init callback. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_debugfs_init(struct drm_minor *minor) +{ + struct tinydrm_device *tdev = minor->dev->dev_private; + struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev); + umode_t mode = S_IFREG | S_IWUSR; + + if (mipi->read_commands) + mode |= S_IRUGO; + debugfs_create_file("command", mode, minor->debugfs_root, mipi, + &mipi_dbi_debugfs_command_fops); + + return drm_debugfs_create_files(mipi_dbi_debugfs_list, + ARRAY_SIZE(mipi_dbi_debugfs_list), + minor->debugfs_root, minor); +} +EXPORT_SYMBOL(mipi_dbi_debugfs_init); + +#endif + +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index ffc6cb55c78c..17478f38dea3 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -163,6 +163,7 @@ static void ttm_bo_release_list(struct kref *list_kref) void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; + struct ttm_mem_type_manager *man; lockdep_assert_held(&bo->resv->lock.base); @@ -170,11 +171,13 @@ void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) BUG_ON(!list_empty(&bo->lru)); - list_add(&bo->lru, bdev->driver->lru_tail(bo)); + man = &bdev->man[bo->mem.mem_type]; + list_add_tail(&bo->lru, &man->lru[bo->priority]); kref_get(&bo->list_kref); if (bo->ttm && !(bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) { - list_add(&bo->swap, bdev->driver->swap_lru_tail(bo)); + list_add_tail(&bo->swap, + &bo->glob->swap_lru[bo->priority]); kref_get(&bo->list_kref); } } @@ -188,11 +191,6 @@ static void ttm_bo_ref_bug(struct kref *list_kref) void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) { - struct ttm_bo_device *bdev = bo->bdev; - - if (bdev->driver->lru_removal) - bdev->driver->lru_removal(bo); - if (!list_empty(&bo->swap)) { list_del_init(&bo->swap); kref_put(&bo->list_kref, ttm_bo_ref_bug); @@ -201,6 +199,11 @@ void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) list_del_init(&bo->lru); kref_put(&bo->list_kref, ttm_bo_ref_bug); } + + /* + * TODO: Add a driver hook to delete from + * driver-specific LRU's here. + */ } void ttm_bo_del_sub_from_lru(struct ttm_buffer_object *bo) @@ -213,30 +216,13 @@ EXPORT_SYMBOL(ttm_bo_del_sub_from_lru); void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo) { - struct ttm_bo_device *bdev = bo->bdev; - lockdep_assert_held(&bo->resv->lock.base); - if (bdev->driver->lru_removal) - bdev->driver->lru_removal(bo); - ttm_bo_del_from_lru(bo); ttm_bo_add_to_lru(bo); } EXPORT_SYMBOL(ttm_bo_move_to_lru_tail); -struct list_head *ttm_bo_default_lru_tail(struct ttm_buffer_object *bo) -{ - return bo->bdev->man[bo->mem.mem_type].lru.prev; -} -EXPORT_SYMBOL(ttm_bo_default_lru_tail); - -struct list_head *ttm_bo_default_swap_lru_tail(struct ttm_buffer_object *bo) -{ - return bo->glob->swap_lru.prev; -} -EXPORT_SYMBOL(ttm_bo_default_swap_lru_tail); - /* * Call bo->mutex locked. */ @@ -327,7 +313,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, if (bo->mem.mem_type == TTM_PL_SYSTEM) { if (bdev->driver->move_notify) - bdev->driver->move_notify(bo, mem); + bdev->driver->move_notify(bo, evict, mem); bo->mem = *mem; mem->mm_node = NULL; goto moved; @@ -335,7 +321,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, } if (bdev->driver->move_notify) - bdev->driver->move_notify(bo, mem); + bdev->driver->move_notify(bo, evict, mem); if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) @@ -351,7 +337,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_mem_reg tmp_mem = *mem; *mem = bo->mem; bo->mem = tmp_mem; - bdev->driver->move_notify(bo, mem); + bdev->driver->move_notify(bo, false, mem); bo->mem = *mem; *mem = tmp_mem; } @@ -399,7 +385,7 @@ out_err: static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) { if (bo->bdev->driver->move_notify) - bo->bdev->driver->move_notify(bo, NULL); + bo->bdev->driver->move_notify(bo, false, NULL); ttm_tt_destroy(bo->ttm); bo->ttm = NULL; @@ -719,20 +705,27 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, struct ttm_mem_type_manager *man = &bdev->man[mem_type]; struct ttm_buffer_object *bo; int ret = -EBUSY; + unsigned i; spin_lock(&glob->lru_lock); - list_for_each_entry(bo, &man->lru, lru) { - ret = __ttm_bo_reserve(bo, false, true, NULL); - if (ret) - continue; + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { + list_for_each_entry(bo, &man->lru[i], lru) { + ret = __ttm_bo_reserve(bo, false, true, NULL); + if (ret) + continue; - if (place && !bdev->driver->eviction_valuable(bo, place)) { - __ttm_bo_unreserve(bo); - ret = -EBUSY; - continue; + if (place && !bdev->driver->eviction_valuable(bo, + place)) { + __ttm_bo_unreserve(bo); + ret = -EBUSY; + continue; + } + + break; } - break; + if (!ret) + break; } if (ret) { @@ -1173,6 +1166,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev, } atomic_inc(&bo->glob->bo_count); drm_vma_node_reset(&bo->vma_node); + bo->priority = 0; /* * For ttm_bo_type_device buffers, allocate @@ -1267,29 +1261,27 @@ int ttm_bo_create(struct ttm_bo_device *bdev, EXPORT_SYMBOL(ttm_bo_create); static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, - unsigned mem_type, bool allow_errors) + unsigned mem_type) { struct ttm_mem_type_manager *man = &bdev->man[mem_type]; struct ttm_bo_global *glob = bdev->glob; struct dma_fence *fence; int ret; + unsigned i; /* * Can't use standard list traversal since we're unlocking. */ spin_lock(&glob->lru_lock); - while (!list_empty(&man->lru)) { - spin_unlock(&glob->lru_lock); - ret = ttm_mem_evict_first(bdev, mem_type, NULL, false, false); - if (ret) { - if (allow_errors) { + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { + while (!list_empty(&man->lru[i])) { + spin_unlock(&glob->lru_lock); + ret = ttm_mem_evict_first(bdev, mem_type, NULL, false, false); + if (ret) return ret; - } else { - pr_err("Cleanup eviction failed\n"); - } + spin_lock(&glob->lru_lock); } - spin_lock(&glob->lru_lock); } spin_unlock(&glob->lru_lock); @@ -1300,13 +1292,8 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, if (fence) { ret = dma_fence_wait(fence, false); dma_fence_put(fence); - if (ret) { - if (allow_errors) { - return ret; - } else { - pr_err("Cleanup eviction failed\n"); - } - } + if (ret) + return ret; } return 0; @@ -1335,7 +1322,11 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type) ret = 0; if (mem_type > 0) { - ttm_bo_force_list_clean(bdev, mem_type, false); + ret = ttm_bo_force_list_clean(bdev, mem_type); + if (ret) { + pr_err("Cleanup eviction failed\n"); + return ret; + } ret = (*man->func->takedown)(man); } @@ -1358,7 +1349,7 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type) return 0; } - return ttm_bo_force_list_clean(bdev, mem_type, true); + return ttm_bo_force_list_clean(bdev, mem_type); } EXPORT_SYMBOL(ttm_bo_evict_mm); @@ -1367,6 +1358,7 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, { int ret = -EINVAL; struct ttm_mem_type_manager *man; + unsigned i; BUG_ON(type >= TTM_NUM_MEM_TYPES); man = &bdev->man[type]; @@ -1392,7 +1384,8 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, man->use_type = true; man->size = p_size; - INIT_LIST_HEAD(&man->lru); + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) + INIT_LIST_HEAD(&man->lru[i]); man->move = NULL; return 0; @@ -1424,6 +1417,7 @@ int ttm_bo_global_init(struct drm_global_reference *ref) container_of(ref, struct ttm_bo_global_ref, ref); struct ttm_bo_global *glob = ref->object; int ret; + unsigned i; mutex_init(&glob->device_list_mutex); spin_lock_init(&glob->lru_lock); @@ -1435,7 +1429,8 @@ int ttm_bo_global_init(struct drm_global_reference *ref) goto out_no_drp; } - INIT_LIST_HEAD(&glob->swap_lru); + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) + INIT_LIST_HEAD(&glob->swap_lru[i]); INIT_LIST_HEAD(&glob->device_list); ttm_mem_init_shrink(&glob->shrink, ttm_bo_swapout); @@ -1494,8 +1489,9 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev) if (list_empty(&bdev->ddestroy)) TTM_DEBUG("Delayed destroy list was clean\n"); - if (list_empty(&bdev->man[0].lru)) - TTM_DEBUG("Swap list was clean\n"); + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) + if (list_empty(&bdev->man[0].lru[0])) + TTM_DEBUG("Swap list %d was clean\n", i); spin_unlock(&glob->lru_lock); drm_vma_offset_manager_destroy(&bdev->vma_manager); @@ -1645,11 +1641,15 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) container_of(shrink, struct ttm_bo_global, shrink); struct ttm_buffer_object *bo; int ret = -EBUSY; - uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM); + unsigned i; spin_lock(&glob->lru_lock); - list_for_each_entry(bo, &glob->swap_lru, swap) { - ret = __ttm_bo_reserve(bo, false, true, NULL); + for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) { + list_for_each_entry(bo, &glob->swap_lru[i], swap) { + ret = __ttm_bo_reserve(bo, false, true, NULL); + if (!ret) + break; + } if (!ret) break; } @@ -1674,7 +1674,8 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) * Move to system cached */ - if ((bo->mem.placement & swap_placement) != swap_placement) { + if (bo->mem.mem_type != TTM_PL_SYSTEM || + bo->ttm->caching_state != tt_cached) { struct ttm_mem_reg evict_mem; evict_mem = bo->mem; diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index aa0bd054d3e9..90a6c0b03afc 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -54,9 +54,8 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, { struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; struct drm_mm *mm = &rman->mm; - struct drm_mm_node *node = NULL; - enum drm_mm_search_flags sflags = DRM_MM_SEARCH_BEST; - enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT; + struct drm_mm_node *node; + enum drm_mm_insert_mode mode; unsigned long lpfn; int ret; @@ -68,16 +67,15 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, if (!node) return -ENOMEM; - if (place->flags & TTM_PL_FLAG_TOPDOWN) { - sflags = DRM_MM_SEARCH_BELOW; - aflags = DRM_MM_CREATE_TOP; - } + mode = DRM_MM_INSERT_BEST; + if (place->flags & TTM_PL_FLAG_TOPDOWN) + mode = DRM_MM_INSERT_HIGH; spin_lock(&rman->lock); - ret = drm_mm_insert_node_in_range_generic(mm, node, mem->num_pages, + ret = drm_mm_insert_node_in_range(mm, node, + mem->num_pages, mem->page_alignment, 0, - place->fpfn, lpfn, - sflags, aflags); + place->fpfn, lpfn, mode); spin_unlock(&rman->lock); if (unlikely(ret)) { @@ -141,17 +139,18 @@ static void ttm_bo_man_debug(struct ttm_mem_type_manager *man, const char *prefix) { struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; + struct drm_printer p = drm_debug_printer(prefix); spin_lock(&rman->lock); - drm_mm_debug_table(&rman->mm, prefix); + drm_mm_print(&rman->mm, &p); spin_unlock(&rman->lock); } const struct ttm_mem_type_manager_func ttm_bo_manager_func = { - ttm_bo_man_init, - ttm_bo_man_takedown, - ttm_bo_man_get_node, - ttm_bo_man_put_node, - ttm_bo_man_debug + .init = ttm_bo_man_init, + .takedown = ttm_bo_man_takedown, + .get_node = ttm_bo_man_get_node, + .put_node = ttm_bo_man_put_node, + .debug = ttm_bo_man_debug }; EXPORT_SYMBOL(ttm_bo_manager_func); diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 68ef993ab431..35ffb3754feb 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -43,7 +43,6 @@ #define TTM_BO_VM_NUM_PREFAULT 16 static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, - struct vm_area_struct *vma, struct vm_fault *vmf) { int ret = 0; @@ -66,8 +65,11 @@ static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT) goto out_unlock; - up_read(&vma->vm_mm->mmap_sem); + ttm_bo_reference(bo); + up_read(&vmf->vma->vm_mm->mmap_sem); (void) dma_fence_wait(bo->moving, true); + ttm_bo_unreserve(bo); + ttm_bo_unref(&bo); goto out_unlock; } @@ -89,8 +91,9 @@ out_unlock: return ret; } -static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int ttm_bo_vm_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct ttm_buffer_object *bo = (struct ttm_buffer_object *) vma->vm_private_data; struct ttm_bo_device *bdev = bo->bdev; @@ -120,8 +123,10 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) { if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { - up_read(&vma->vm_mm->mmap_sem); + ttm_bo_reference(bo); + up_read(&vmf->vma->vm_mm->mmap_sem); (void) ttm_bo_wait_unreserved(bo); + ttm_bo_unref(&bo); } return VM_FAULT_RETRY; @@ -163,9 +168,16 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) * Wait for buffer data in transit, due to a pipelined * move. */ - ret = ttm_bo_vm_fault_idle(bo, vma, vmf); + ret = ttm_bo_vm_fault_idle(bo, vmf); if (unlikely(ret != 0)) { retval = ret; + + if (retval == VM_FAULT_RETRY && + !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { + /* The BO has already been unreserved. */ + return retval; + } + goto out_unlock; } diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c index f154fb1929bd..913f4318cdc0 100644 --- a/drivers/gpu/drm/ttm/ttm_lock.c +++ b/drivers/gpu/drm/ttm/ttm_lock.c @@ -33,7 +33,7 @@ #include <linux/atomic.h> #include <linux/errno.h> #include <linux/wait.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/module.h> #define TTM_WRITE_LOCK_PENDING (1 << 0) diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index f338a576efc8..2a75ab80527a 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -100,7 +100,7 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len); void udl_urb_completion(struct urb *urb); int udl_driver_load(struct drm_device *dev, unsigned long flags); -int udl_driver_unload(struct drm_device *dev); +void udl_driver_unload(struct drm_device *dev); int udl_fbdev_init(struct drm_device *dev); void udl_fbdev_cleanup(struct drm_device *dev); @@ -134,7 +134,7 @@ void udl_gem_put_pages(struct udl_gem_object *obj); int udl_gem_vmap(struct udl_gem_object *obj); void udl_gem_vunmap(struct udl_gem_object *obj); int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); -int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); +int udl_gem_fault(struct vm_fault *vmf); int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, int width, int height); diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 167f42c67c7c..8e8d60e9a1a2 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -89,7 +89,7 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, int bytes_identical = 0; struct urb *urb; int aligned_x; - int bpp = (fb->base.bits_per_pixel / 8); + int bpp = fb->base.format->cpp[0]; if (!fb->active_16) return 0; @@ -330,7 +330,7 @@ udl_framebuffer_init(struct drm_device *dev, int ret; ufb->obj = obj; - drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &ufb->base, mode_cmd); ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); return ret; } @@ -395,7 +395,7 @@ static int udlfb_create(struct drm_fb_helper *helper, info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &udlfb_ops; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &ufbdev->helper, sizes->fb_width, sizes->fb_height); DRM_DEBUG_KMS("allocated %dx%d vmal %p\n", @@ -441,8 +441,7 @@ int udl_fbdev_init(struct drm_device *dev) drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs); - ret = drm_fb_helper_init(dev, &ufbdev->helper, - 1, 1); + ret = drm_fb_helper_init(dev, &ufbdev->helper, 1); if (ret) goto free; diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 3c0c4bd3f750..775c50e4f02c 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -100,8 +100,9 @@ int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) return ret; } -int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int udl_gem_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct udl_gem_object *obj = to_udl_bo(vma->vm_private_data); struct page *page; unsigned int page_offset; diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 873f010d9616..a9d93b871a15 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -367,7 +367,7 @@ int udl_drop_usb(struct drm_device *dev) return 0; } -int udl_driver_unload(struct drm_device *dev) +void udl_driver_unload(struct drm_device *dev) { struct udl_device *udl = dev->dev_private; @@ -379,5 +379,4 @@ int udl_driver_unload(struct drm_device *dev) udl_fbdev_cleanup(dev); udl_modeset_cleanup(dev); kfree(udl); - return 0; } diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index e53df59cb139..e1517d07cb7d 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -2,10 +2,12 @@ config DRM_VC4 tristate "Broadcom VC4 Graphics" depends on ARCH_BCM2835 || COMPILE_TEST depends on DRM + depends on COMMON_CLK select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER select DRM_PANEL + select DRM_MIPI_DSI help Choose this option if you have a system that has a Broadcom VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835. diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index 7757f69a8a77..61f45d122bd0 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -8,6 +8,7 @@ vc4-y := \ vc4_crtc.o \ vc4_drv.o \ vc4_dpi.o \ + vc4_dsi.o \ vc4_kms.o \ vc4_gem.o \ vc4_hdmi.o \ diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 7aadce1f7e7a..0c06844af445 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -156,7 +156,8 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, const struct drm_display_mode *mode) { struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); u32 val; int fifo_lines; int vblank_lines; @@ -272,9 +273,7 @@ int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id, int *max_error, struct timeval *vblank_time, unsigned flags) { - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; - struct drm_crtc *crtc = &vc4_crtc->base; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); struct drm_crtc_state *state = crtc->state; /* Helper routine in DRM core does all the work: */ @@ -349,38 +348,40 @@ static u32 vc4_get_fifo_full_level(u32 format) } /* - * Returns the clock select bit for the connector attached to the - * CRTC. + * Returns the encoder attached to the CRTC. + * + * VC4 can only scan out to one encoder at a time, while the DRM core + * allows drivers to push pixels to more than one encoder from the + * same CRTC. */ -static int vc4_get_clock_select(struct drm_crtc *crtc) +static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) { struct drm_connector *connector; drm_for_each_connector(connector, crtc->dev) { if (connector->state->crtc == crtc) { - struct drm_encoder *encoder = connector->encoder; - struct vc4_encoder *vc4_encoder = - to_vc4_encoder(encoder); - - return vc4_encoder->clock_select; + return connector->encoder; } } - return -1; + return NULL; } static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); + struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_crtc_state *state = crtc->state; struct drm_display_mode *mode = &state->adjusted_mode; bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; - u32 format = PV_CONTROL_FORMAT_24; + bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 || + vc4_encoder->type == VC4_ENCODER_TYPE_DSI1); + u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24; bool debug_dump_regs = false; - int clock_select = vc4_get_clock_select(crtc); if (debug_dump_regs) { DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc)); @@ -436,17 +437,19 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) */ CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS | + (is_dsi ? PV_VCONTROL_DSI : 0) | PV_VCONTROL_INTERLACE | VC4_SET_FIELD(mode->htotal * pixel_rep / 2, PV_VCONTROL_ODD_DELAY)); CRTC_WRITE(PV_VSYNCD_EVEN, 0); } else { - CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS); + CRTC_WRITE(PV_V_CONTROL, + PV_VCONTROL_CONTINUOUS | + (is_dsi ? PV_VCONTROL_DSI : 0)); } CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep); - CRTC_WRITE(PV_CONTROL, VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | VC4_SET_FIELD(vc4_get_fifo_full_level(format), @@ -455,7 +458,8 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) PV_CONTROL_CLR_AT_START | PV_CONTROL_TRIGGER_UNDERFLOW | PV_CONTROL_WAIT_HSTART | - VC4_SET_FIELD(clock_select, PV_CONTROL_CLK_SELECT) | + VC4_SET_FIELD(vc4_encoder->clock_select, + PV_CONTROL_CLK_SELECT) | PV_CONTROL_FIFO_CLR | PV_CONTROL_EN); @@ -589,7 +593,7 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, spin_lock_irqsave(&vc4->hvs->mm_lock, flags); ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, - dlist_count, 1, 0); + dlist_count); spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); if (ret) return ret; @@ -652,8 +656,8 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc, int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id) { - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); CRTC_WRITE(PV_INTEN, PV_INT_VFP_START); @@ -662,8 +666,8 @@ int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id) void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id) { - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); CRTC_WRITE(PV_INTEN, 0); } @@ -937,7 +941,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_crtc *vc4_crtc; struct drm_crtc *crtc; struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp; @@ -975,7 +978,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) &vc4_crtc_funcs, NULL); drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); primary_plane->crtc = crtc; - vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc; vc4_crtc->channel = vc4_crtc->data->hvs_channel; drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index caf817bac885..5db06bdb5f27 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -18,6 +18,7 @@ static const struct drm_info_list vc4_debugfs_list[] = { {"bo_stats", vc4_bo_stats_debugfs, 0}, {"dpi_regs", vc4_dpi_debugfs_regs, 0}, + {"dsi1_regs", vc4_dsi_debugfs_regs, 0, (void *)(uintptr_t)1}, {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, {"vec_regs", vc4_vec_debugfs_regs, 0}, {"hvs_regs", vc4_hvs_debugfs_regs, 0}, @@ -36,9 +37,3 @@ vc4_debugfs_init(struct drm_minor *minor) return drm_debugfs_create_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES, minor->debugfs_root, minor); } - -void -vc4_debugfs_cleanup(struct drm_minor *minor) -{ - drm_debugfs_remove_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES, minor); -} diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index ac09ca7ff430..a459745e96f7 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -145,7 +145,6 @@ static struct drm_driver vc4_drm_driver = { #if defined(CONFIG_DEBUG_FS) .debugfs_init = vc4_debugfs_init, - .debugfs_cleanup = vc4_debugfs_cleanup, #endif .gem_create_object = vc4_create_object, @@ -296,6 +295,7 @@ static struct platform_driver *const component_drivers[] = { &vc4_hdmi_driver, &vc4_vec_driver, &vc4_dpi_driver, + &vc4_dsi_driver, &vc4_hvs_driver, &vc4_crtc_driver, &vc4_v3d_driver, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index b5c4bb14d0d1..0e59f3ee1b83 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -9,14 +9,16 @@ #include "drmP.h" #include "drm_gem_cma_helper.h" +#include <drm/drm_encoder.h> + struct vc4_dev { struct drm_device *dev; struct vc4_hdmi *hdmi; struct vc4_hvs *hvs; - struct vc4_crtc *crtc[3]; struct vc4_v3d *v3d; struct vc4_dpi *dpi; + struct vc4_dsi *dsi1; struct vc4_vec *vec; struct drm_fbdev_cma *fbdev; @@ -456,7 +458,6 @@ int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id, /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); -void vc4_debugfs_cleanup(struct drm_minor *minor); /* vc4_drv.c */ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); @@ -465,6 +466,10 @@ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); extern struct platform_driver vc4_dpi_driver; int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused); +/* vc4_dsi.c */ +extern struct platform_driver vc4_dsi_driver; +int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused); + /* vc4_gem.c */ void vc4_gem_init(struct drm_device *dev); void vc4_gem_destroy(struct drm_device *dev); diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c new file mode 100644 index 000000000000..2736b0331beb --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -0,0 +1,1725 @@ +/* + * Copyright (C) 2016 Broadcom + * + * 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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * DOC: VC4 DSI0/DSI1 module + * + * BCM2835 contains two DSI modules, DSI0 and DSI1. DSI0 is a + * single-lane DSI controller, while DSI1 is a more modern 4-lane DSI + * controller. + * + * Most Raspberry Pi boards expose DSI1 as their "DISPLAY" connector, + * while the compute module brings both DSI0 and DSI1 out. + * + * This driver has been tested for DSI1 video-mode display only + * currently, with most of the information necessary for DSI0 + * hopefully present. + */ + +#include "drm_atomic_helper.h" +#include "drm_crtc_helper.h" +#include "drm_edid.h" +#include "drm_mipi_dsi.h" +#include "drm_panel.h" +#include "linux/clk.h" +#include "linux/clk-provider.h" +#include "linux/completion.h" +#include "linux/component.h" +#include "linux/dmaengine.h" +#include "linux/i2c.h" +#include "linux/of_address.h" +#include "linux/of_platform.h" +#include "linux/pm_runtime.h" +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define DSI_CMD_FIFO_DEPTH 16 +#define DSI_PIX_FIFO_DEPTH 256 +#define DSI_PIX_FIFO_WIDTH 4 + +#define DSI0_CTRL 0x00 + +/* Command packet control. */ +#define DSI0_TXPKT1C 0x04 /* AKA PKTC */ +#define DSI1_TXPKT1C 0x04 +# define DSI_TXPKT1C_TRIG_CMD_MASK VC4_MASK(31, 24) +# define DSI_TXPKT1C_TRIG_CMD_SHIFT 24 +# define DSI_TXPKT1C_CMD_REPEAT_MASK VC4_MASK(23, 10) +# define DSI_TXPKT1C_CMD_REPEAT_SHIFT 10 + +# define DSI_TXPKT1C_DISPLAY_NO_MASK VC4_MASK(9, 8) +# define DSI_TXPKT1C_DISPLAY_NO_SHIFT 8 +/* Short, trigger, BTA, or a long packet that fits all in CMDFIFO. */ +# define DSI_TXPKT1C_DISPLAY_NO_SHORT 0 +/* Primary display where cmdfifo provides part of the payload and + * pixelvalve the rest. + */ +# define DSI_TXPKT1C_DISPLAY_NO_PRIMARY 1 +/* Secondary display where cmdfifo provides part of the payload and + * pixfifo the rest. + */ +# define DSI_TXPKT1C_DISPLAY_NO_SECONDARY 2 + +# define DSI_TXPKT1C_CMD_TX_TIME_MASK VC4_MASK(7, 6) +# define DSI_TXPKT1C_CMD_TX_TIME_SHIFT 6 + +# define DSI_TXPKT1C_CMD_CTRL_MASK VC4_MASK(5, 4) +# define DSI_TXPKT1C_CMD_CTRL_SHIFT 4 +/* Command only. Uses TXPKT1H and DISPLAY_NO */ +# define DSI_TXPKT1C_CMD_CTRL_TX 0 +/* Command with BTA for either ack or read data. */ +# define DSI_TXPKT1C_CMD_CTRL_RX 1 +/* Trigger according to TRIG_CMD */ +# define DSI_TXPKT1C_CMD_CTRL_TRIG 2 +/* BTA alone for getting error status after a command, or a TE trigger + * without a previous command. + */ +# define DSI_TXPKT1C_CMD_CTRL_BTA 3 + +# define DSI_TXPKT1C_CMD_MODE_LP BIT(3) +# define DSI_TXPKT1C_CMD_TYPE_LONG BIT(2) +# define DSI_TXPKT1C_CMD_TE_EN BIT(1) +# define DSI_TXPKT1C_CMD_EN BIT(0) + +/* Command packet header. */ +#define DSI0_TXPKT1H 0x08 /* AKA PKTH */ +#define DSI1_TXPKT1H 0x08 +# define DSI_TXPKT1H_BC_CMDFIFO_MASK VC4_MASK(31, 24) +# define DSI_TXPKT1H_BC_CMDFIFO_SHIFT 24 +# define DSI_TXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) +# define DSI_TXPKT1H_BC_PARAM_SHIFT 8 +# define DSI_TXPKT1H_BC_DT_MASK VC4_MASK(7, 0) +# define DSI_TXPKT1H_BC_DT_SHIFT 0 + +#define DSI0_RXPKT1H 0x0c /* AKA RX1_PKTH */ +#define DSI1_RXPKT1H 0x14 +# define DSI_RXPKT1H_CRC_ERR BIT(31) +# define DSI_RXPKT1H_DET_ERR BIT(30) +# define DSI_RXPKT1H_ECC_ERR BIT(29) +# define DSI_RXPKT1H_COR_ERR BIT(28) +# define DSI_RXPKT1H_INCOMP_PKT BIT(25) +# define DSI_RXPKT1H_PKT_TYPE_LONG BIT(24) +/* Byte count if DSI_RXPKT1H_PKT_TYPE_LONG */ +# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) +# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 +/* Short return bytes if !DSI_RXPKT1H_PKT_TYPE_LONG */ +# define DSI_RXPKT1H_SHORT_1_MASK VC4_MASK(23, 16) +# define DSI_RXPKT1H_SHORT_1_SHIFT 16 +# define DSI_RXPKT1H_SHORT_0_MASK VC4_MASK(15, 8) +# define DSI_RXPKT1H_SHORT_0_SHIFT 8 +# define DSI_RXPKT1H_DT_LP_CMD_MASK VC4_MASK(7, 0) +# define DSI_RXPKT1H_DT_LP_CMD_SHIFT 0 + +#define DSI0_RXPKT2H 0x10 /* AKA RX2_PKTH */ +#define DSI1_RXPKT2H 0x18 +# define DSI_RXPKT1H_DET_ERR BIT(30) +# define DSI_RXPKT1H_ECC_ERR BIT(29) +# define DSI_RXPKT1H_COR_ERR BIT(28) +# define DSI_RXPKT1H_INCOMP_PKT BIT(25) +# define DSI_RXPKT1H_BC_PARAM_MASK VC4_MASK(23, 8) +# define DSI_RXPKT1H_BC_PARAM_SHIFT 8 +# define DSI_RXPKT1H_DT_MASK VC4_MASK(7, 0) +# define DSI_RXPKT1H_DT_SHIFT 0 + +#define DSI0_TXPKT_CMD_FIFO 0x14 /* AKA CMD_DATAF */ +#define DSI1_TXPKT_CMD_FIFO 0x1c + +#define DSI0_DISP0_CTRL 0x18 +# define DSI_DISP0_PIX_CLK_DIV_MASK VC4_MASK(21, 13) +# define DSI_DISP0_PIX_CLK_DIV_SHIFT 13 +# define DSI_DISP0_LP_STOP_CTRL_MASK VC4_MASK(12, 11) +# define DSI_DISP0_LP_STOP_CTRL_SHIFT 11 +# define DSI_DISP0_LP_STOP_DISABLE 0 +# define DSI_DISP0_LP_STOP_PERLINE 1 +# define DSI_DISP0_LP_STOP_PERFRAME 2 + +/* Transmit RGB pixels and null packets only during HACTIVE, instead + * of going to LP-STOP. + */ +# define DSI_DISP_HACTIVE_NULL BIT(10) +/* Transmit blanking packet only during vblank, instead of allowing LP-STOP. */ +# define DSI_DISP_VBLP_CTRL BIT(9) +/* Transmit blanking packet only during HFP, instead of allowing LP-STOP. */ +# define DSI_DISP_HFP_CTRL BIT(8) +/* Transmit blanking packet only during HBP, instead of allowing LP-STOP. */ +# define DSI_DISP_HBP_CTRL BIT(7) +# define DSI_DISP0_CHANNEL_MASK VC4_MASK(6, 5) +# define DSI_DISP0_CHANNEL_SHIFT 5 +/* Enables end events for HSYNC/VSYNC, not just start events. */ +# define DSI_DISP0_ST_END BIT(4) +# define DSI_DISP0_PFORMAT_MASK VC4_MASK(3, 2) +# define DSI_DISP0_PFORMAT_SHIFT 2 +# define DSI_PFORMAT_RGB565 0 +# define DSI_PFORMAT_RGB666_PACKED 1 +# define DSI_PFORMAT_RGB666 2 +# define DSI_PFORMAT_RGB888 3 +/* Default is VIDEO mode. */ +# define DSI_DISP0_COMMAND_MODE BIT(1) +# define DSI_DISP0_ENABLE BIT(0) + +#define DSI0_DISP1_CTRL 0x1c +#define DSI1_DISP1_CTRL 0x2c +/* Format of the data written to TXPKT_PIX_FIFO. */ +# define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1) +# define DSI_DISP1_PFORMAT_SHIFT 1 +# define DSI_DISP1_PFORMAT_16BIT 0 +# define DSI_DISP1_PFORMAT_24BIT 1 +# define DSI_DISP1_PFORMAT_32BIT_LE 2 +# define DSI_DISP1_PFORMAT_32BIT_BE 3 + +/* DISP1 is always command mode. */ +# define DSI_DISP1_ENABLE BIT(0) + +#define DSI0_TXPKT_PIX_FIFO 0x20 /* AKA PIX_FIFO */ + +#define DSI0_INT_STAT 0x24 +#define DSI0_INT_EN 0x28 +# define DSI1_INT_PHY_D3_ULPS BIT(30) +# define DSI1_INT_PHY_D3_STOP BIT(29) +# define DSI1_INT_PHY_D2_ULPS BIT(28) +# define DSI1_INT_PHY_D2_STOP BIT(27) +# define DSI1_INT_PHY_D1_ULPS BIT(26) +# define DSI1_INT_PHY_D1_STOP BIT(25) +# define DSI1_INT_PHY_D0_ULPS BIT(24) +# define DSI1_INT_PHY_D0_STOP BIT(23) +# define DSI1_INT_FIFO_ERR BIT(22) +# define DSI1_INT_PHY_DIR_RTF BIT(21) +# define DSI1_INT_PHY_RXLPDT BIT(20) +# define DSI1_INT_PHY_RXTRIG BIT(19) +# define DSI1_INT_PHY_D0_LPDT BIT(18) +# define DSI1_INT_PHY_DIR_FTR BIT(17) + +/* Signaled when the clock lane enters the given state. */ +# define DSI1_INT_PHY_CLOCK_ULPS BIT(16) +# define DSI1_INT_PHY_CLOCK_HS BIT(15) +# define DSI1_INT_PHY_CLOCK_STOP BIT(14) + +/* Signaled on timeouts */ +# define DSI1_INT_PR_TO BIT(13) +# define DSI1_INT_TA_TO BIT(12) +# define DSI1_INT_LPRX_TO BIT(11) +# define DSI1_INT_HSTX_TO BIT(10) + +/* Contention on a line when trying to drive the line low */ +# define DSI1_INT_ERR_CONT_LP1 BIT(9) +# define DSI1_INT_ERR_CONT_LP0 BIT(8) + +/* Control error: incorrect line state sequence on data lane 0. */ +# define DSI1_INT_ERR_CONTROL BIT(7) +/* LPDT synchronization error (bits received not a multiple of 8. */ + +# define DSI1_INT_ERR_SYNC_ESC BIT(6) +/* Signaled after receiving an error packet from the display in + * response to a read. + */ +# define DSI1_INT_RXPKT2 BIT(5) +/* Signaled after receiving a packet. The header and optional short + * response will be in RXPKT1H, and a long response will be in the + * RXPKT_FIFO. + */ +# define DSI1_INT_RXPKT1 BIT(4) +# define DSI1_INT_TXPKT2_DONE BIT(3) +# define DSI1_INT_TXPKT2_END BIT(2) +/* Signaled after all repeats of TXPKT1 are transferred. */ +# define DSI1_INT_TXPKT1_DONE BIT(1) +/* Signaled after each TXPKT1 repeat is scheduled. */ +# define DSI1_INT_TXPKT1_END BIT(0) + +#define DSI1_INTERRUPTS_ALWAYS_ENABLED (DSI1_INT_ERR_SYNC_ESC | \ + DSI1_INT_ERR_CONTROL | \ + DSI1_INT_ERR_CONT_LP0 | \ + DSI1_INT_ERR_CONT_LP1 | \ + DSI1_INT_HSTX_TO | \ + DSI1_INT_LPRX_TO | \ + DSI1_INT_TA_TO | \ + DSI1_INT_PR_TO) + +#define DSI0_STAT 0x2c +#define DSI0_HSTX_TO_CNT 0x30 +#define DSI0_LPRX_TO_CNT 0x34 +#define DSI0_TA_TO_CNT 0x38 +#define DSI0_PR_TO_CNT 0x3c +#define DSI0_PHYC 0x40 +# define DSI1_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(25, 20) +# define DSI1_PHYC_ESC_CLK_LPDT_SHIFT 20 +# define DSI1_PHYC_HS_CLK_CONTINUOUS BIT(18) +# define DSI0_PHYC_ESC_CLK_LPDT_MASK VC4_MASK(17, 12) +# define DSI0_PHYC_ESC_CLK_LPDT_SHIFT 12 +# define DSI1_PHYC_CLANE_ULPS BIT(17) +# define DSI1_PHYC_CLANE_ENABLE BIT(16) +# define DSI_PHYC_DLANE3_ULPS BIT(13) +# define DSI_PHYC_DLANE3_ENABLE BIT(12) +# define DSI0_PHYC_HS_CLK_CONTINUOUS BIT(10) +# define DSI0_PHYC_CLANE_ULPS BIT(9) +# define DSI_PHYC_DLANE2_ULPS BIT(9) +# define DSI0_PHYC_CLANE_ENABLE BIT(8) +# define DSI_PHYC_DLANE2_ENABLE BIT(8) +# define DSI_PHYC_DLANE1_ULPS BIT(5) +# define DSI_PHYC_DLANE1_ENABLE BIT(4) +# define DSI_PHYC_DLANE0_FORCE_STOP BIT(2) +# define DSI_PHYC_DLANE0_ULPS BIT(1) +# define DSI_PHYC_DLANE0_ENABLE BIT(0) + +#define DSI0_HS_CLT0 0x44 +#define DSI0_HS_CLT1 0x48 +#define DSI0_HS_CLT2 0x4c +#define DSI0_HS_DLT3 0x50 +#define DSI0_HS_DLT4 0x54 +#define DSI0_HS_DLT5 0x58 +#define DSI0_HS_DLT6 0x5c +#define DSI0_HS_DLT7 0x60 + +#define DSI0_PHY_AFEC0 0x64 +# define DSI0_PHY_AFEC0_DDR2CLK_EN BIT(26) +# define DSI0_PHY_AFEC0_DDRCLK_EN BIT(25) +# define DSI0_PHY_AFEC0_LATCH_ULPS BIT(24) +# define DSI1_PHY_AFEC0_IDR_DLANE3_MASK VC4_MASK(31, 29) +# define DSI1_PHY_AFEC0_IDR_DLANE3_SHIFT 29 +# define DSI1_PHY_AFEC0_IDR_DLANE2_MASK VC4_MASK(28, 26) +# define DSI1_PHY_AFEC0_IDR_DLANE2_SHIFT 26 +# define DSI1_PHY_AFEC0_IDR_DLANE1_MASK VC4_MASK(27, 23) +# define DSI1_PHY_AFEC0_IDR_DLANE1_SHIFT 23 +# define DSI1_PHY_AFEC0_IDR_DLANE0_MASK VC4_MASK(22, 20) +# define DSI1_PHY_AFEC0_IDR_DLANE0_SHIFT 20 +# define DSI1_PHY_AFEC0_IDR_CLANE_MASK VC4_MASK(19, 17) +# define DSI1_PHY_AFEC0_IDR_CLANE_SHIFT 17 +# define DSI0_PHY_AFEC0_ACTRL_DLANE1_MASK VC4_MASK(23, 20) +# define DSI0_PHY_AFEC0_ACTRL_DLANE1_SHIFT 20 +# define DSI0_PHY_AFEC0_ACTRL_DLANE0_MASK VC4_MASK(19, 16) +# define DSI0_PHY_AFEC0_ACTRL_DLANE0_SHIFT 16 +# define DSI0_PHY_AFEC0_ACTRL_CLANE_MASK VC4_MASK(15, 12) +# define DSI0_PHY_AFEC0_ACTRL_CLANE_SHIFT 12 +# define DSI1_PHY_AFEC0_DDR2CLK_EN BIT(16) +# define DSI1_PHY_AFEC0_DDRCLK_EN BIT(15) +# define DSI1_PHY_AFEC0_LATCH_ULPS BIT(14) +# define DSI1_PHY_AFEC0_RESET BIT(13) +# define DSI1_PHY_AFEC0_PD BIT(12) +# define DSI0_PHY_AFEC0_RESET BIT(11) +# define DSI1_PHY_AFEC0_PD_BG BIT(11) +# define DSI0_PHY_AFEC0_PD BIT(10) +# define DSI1_PHY_AFEC0_PD_DLANE3 BIT(10) +# define DSI0_PHY_AFEC0_PD_BG BIT(9) +# define DSI1_PHY_AFEC0_PD_DLANE2 BIT(9) +# define DSI0_PHY_AFEC0_PD_DLANE1 BIT(8) +# define DSI1_PHY_AFEC0_PD_DLANE1 BIT(8) +# define DSI_PHY_AFEC0_PTATADJ_MASK VC4_MASK(7, 4) +# define DSI_PHY_AFEC0_PTATADJ_SHIFT 4 +# define DSI_PHY_AFEC0_CTATADJ_MASK VC4_MASK(3, 0) +# define DSI_PHY_AFEC0_CTATADJ_SHIFT 0 + +#define DSI0_PHY_AFEC1 0x68 +# define DSI0_PHY_AFEC1_IDR_DLANE1_MASK VC4_MASK(10, 8) +# define DSI0_PHY_AFEC1_IDR_DLANE1_SHIFT 8 +# define DSI0_PHY_AFEC1_IDR_DLANE0_MASK VC4_MASK(6, 4) +# define DSI0_PHY_AFEC1_IDR_DLANE0_SHIFT 4 +# define DSI0_PHY_AFEC1_IDR_CLANE_MASK VC4_MASK(2, 0) +# define DSI0_PHY_AFEC1_IDR_CLANE_SHIFT 0 + +#define DSI0_TST_SEL 0x6c +#define DSI0_TST_MON 0x70 +#define DSI0_ID 0x74 +# define DSI_ID_VALUE 0x00647369 + +#define DSI1_CTRL 0x00 +# define DSI_CTRL_HS_CLKC_MASK VC4_MASK(15, 14) +# define DSI_CTRL_HS_CLKC_SHIFT 14 +# define DSI_CTRL_HS_CLKC_BYTE 0 +# define DSI_CTRL_HS_CLKC_DDR2 1 +# define DSI_CTRL_HS_CLKC_DDR 2 + +# define DSI_CTRL_RX_LPDT_EOT_DISABLE BIT(13) +# define DSI_CTRL_LPDT_EOT_DISABLE BIT(12) +# define DSI_CTRL_HSDT_EOT_DISABLE BIT(11) +# define DSI_CTRL_SOFT_RESET_CFG BIT(10) +# define DSI_CTRL_CAL_BYTE BIT(9) +# define DSI_CTRL_INV_BYTE BIT(8) +# define DSI_CTRL_CLR_LDF BIT(7) +# define DSI0_CTRL_CLR_PBCF BIT(6) +# define DSI1_CTRL_CLR_RXF BIT(6) +# define DSI0_CTRL_CLR_CPBCF BIT(5) +# define DSI1_CTRL_CLR_PDF BIT(5) +# define DSI0_CTRL_CLR_PDF BIT(4) +# define DSI1_CTRL_CLR_CDF BIT(4) +# define DSI0_CTRL_CLR_CDF BIT(3) +# define DSI0_CTRL_CTRL2 BIT(2) +# define DSI1_CTRL_DISABLE_DISP_CRCC BIT(2) +# define DSI0_CTRL_CTRL1 BIT(1) +# define DSI1_CTRL_DISABLE_DISP_ECCC BIT(1) +# define DSI0_CTRL_CTRL0 BIT(0) +# define DSI1_CTRL_EN BIT(0) +# define DSI0_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ + DSI0_CTRL_CLR_PBCF | \ + DSI0_CTRL_CLR_CPBCF | \ + DSI0_CTRL_CLR_PDF | \ + DSI0_CTRL_CLR_CDF) +# define DSI1_CTRL_RESET_FIFOS (DSI_CTRL_CLR_LDF | \ + DSI1_CTRL_CLR_RXF | \ + DSI1_CTRL_CLR_PDF | \ + DSI1_CTRL_CLR_CDF) + +#define DSI1_TXPKT2C 0x0c +#define DSI1_TXPKT2H 0x10 +#define DSI1_TXPKT_PIX_FIFO 0x20 +#define DSI1_RXPKT_FIFO 0x24 +#define DSI1_DISP0_CTRL 0x28 +#define DSI1_INT_STAT 0x30 +#define DSI1_INT_EN 0x34 +/* State reporting bits. These mostly behave like INT_STAT, where + * writing a 1 clears the bit. + */ +#define DSI1_STAT 0x38 +# define DSI1_STAT_PHY_D3_ULPS BIT(31) +# define DSI1_STAT_PHY_D3_STOP BIT(30) +# define DSI1_STAT_PHY_D2_ULPS BIT(29) +# define DSI1_STAT_PHY_D2_STOP BIT(28) +# define DSI1_STAT_PHY_D1_ULPS BIT(27) +# define DSI1_STAT_PHY_D1_STOP BIT(26) +# define DSI1_STAT_PHY_D0_ULPS BIT(25) +# define DSI1_STAT_PHY_D0_STOP BIT(24) +# define DSI1_STAT_FIFO_ERR BIT(23) +# define DSI1_STAT_PHY_RXLPDT BIT(22) +# define DSI1_STAT_PHY_RXTRIG BIT(21) +# define DSI1_STAT_PHY_D0_LPDT BIT(20) +/* Set when in forward direction */ +# define DSI1_STAT_PHY_DIR BIT(19) +# define DSI1_STAT_PHY_CLOCK_ULPS BIT(18) +# define DSI1_STAT_PHY_CLOCK_HS BIT(17) +# define DSI1_STAT_PHY_CLOCK_STOP BIT(16) +# define DSI1_STAT_PR_TO BIT(15) +# define DSI1_STAT_TA_TO BIT(14) +# define DSI1_STAT_LPRX_TO BIT(13) +# define DSI1_STAT_HSTX_TO BIT(12) +# define DSI1_STAT_ERR_CONT_LP1 BIT(11) +# define DSI1_STAT_ERR_CONT_LP0 BIT(10) +# define DSI1_STAT_ERR_CONTROL BIT(9) +# define DSI1_STAT_ERR_SYNC_ESC BIT(8) +# define DSI1_STAT_RXPKT2 BIT(7) +# define DSI1_STAT_RXPKT1 BIT(6) +# define DSI1_STAT_TXPKT2_BUSY BIT(5) +# define DSI1_STAT_TXPKT2_DONE BIT(4) +# define DSI1_STAT_TXPKT2_END BIT(3) +# define DSI1_STAT_TXPKT1_BUSY BIT(2) +# define DSI1_STAT_TXPKT1_DONE BIT(1) +# define DSI1_STAT_TXPKT1_END BIT(0) + +#define DSI1_HSTX_TO_CNT 0x3c +#define DSI1_LPRX_TO_CNT 0x40 +#define DSI1_TA_TO_CNT 0x44 +#define DSI1_PR_TO_CNT 0x48 +#define DSI1_PHYC 0x4c + +#define DSI1_HS_CLT0 0x50 +# define DSI_HS_CLT0_CZERO_MASK VC4_MASK(26, 18) +# define DSI_HS_CLT0_CZERO_SHIFT 18 +# define DSI_HS_CLT0_CPRE_MASK VC4_MASK(17, 9) +# define DSI_HS_CLT0_CPRE_SHIFT 9 +# define DSI_HS_CLT0_CPREP_MASK VC4_MASK(8, 0) +# define DSI_HS_CLT0_CPREP_SHIFT 0 + +#define DSI1_HS_CLT1 0x54 +# define DSI_HS_CLT1_CTRAIL_MASK VC4_MASK(17, 9) +# define DSI_HS_CLT1_CTRAIL_SHIFT 9 +# define DSI_HS_CLT1_CPOST_MASK VC4_MASK(8, 0) +# define DSI_HS_CLT1_CPOST_SHIFT 0 + +#define DSI1_HS_CLT2 0x58 +# define DSI_HS_CLT2_WUP_MASK VC4_MASK(23, 0) +# define DSI_HS_CLT2_WUP_SHIFT 0 + +#define DSI1_HS_DLT3 0x5c +# define DSI_HS_DLT3_EXIT_MASK VC4_MASK(26, 18) +# define DSI_HS_DLT3_EXIT_SHIFT 18 +# define DSI_HS_DLT3_ZERO_MASK VC4_MASK(17, 9) +# define DSI_HS_DLT3_ZERO_SHIFT 9 +# define DSI_HS_DLT3_PRE_MASK VC4_MASK(8, 0) +# define DSI_HS_DLT3_PRE_SHIFT 0 + +#define DSI1_HS_DLT4 0x60 +# define DSI_HS_DLT4_ANLAT_MASK VC4_MASK(22, 18) +# define DSI_HS_DLT4_ANLAT_SHIFT 18 +# define DSI_HS_DLT4_TRAIL_MASK VC4_MASK(17, 9) +# define DSI_HS_DLT4_TRAIL_SHIFT 9 +# define DSI_HS_DLT4_LPX_MASK VC4_MASK(8, 0) +# define DSI_HS_DLT4_LPX_SHIFT 0 + +#define DSI1_HS_DLT5 0x64 +# define DSI_HS_DLT5_INIT_MASK VC4_MASK(23, 0) +# define DSI_HS_DLT5_INIT_SHIFT 0 + +#define DSI1_HS_DLT6 0x68 +# define DSI_HS_DLT6_TA_GET_MASK VC4_MASK(31, 24) +# define DSI_HS_DLT6_TA_GET_SHIFT 24 +# define DSI_HS_DLT6_TA_SURE_MASK VC4_MASK(23, 16) +# define DSI_HS_DLT6_TA_SURE_SHIFT 16 +# define DSI_HS_DLT6_TA_GO_MASK VC4_MASK(15, 8) +# define DSI_HS_DLT6_TA_GO_SHIFT 8 +# define DSI_HS_DLT6_LP_LPX_MASK VC4_MASK(7, 0) +# define DSI_HS_DLT6_LP_LPX_SHIFT 0 + +#define DSI1_HS_DLT7 0x6c +# define DSI_HS_DLT7_LP_WUP_MASK VC4_MASK(23, 0) +# define DSI_HS_DLT7_LP_WUP_SHIFT 0 + +#define DSI1_PHY_AFEC0 0x70 + +#define DSI1_PHY_AFEC1 0x74 +# define DSI1_PHY_AFEC1_ACTRL_DLANE3_MASK VC4_MASK(19, 16) +# define DSI1_PHY_AFEC1_ACTRL_DLANE3_SHIFT 16 +# define DSI1_PHY_AFEC1_ACTRL_DLANE2_MASK VC4_MASK(15, 12) +# define DSI1_PHY_AFEC1_ACTRL_DLANE2_SHIFT 12 +# define DSI1_PHY_AFEC1_ACTRL_DLANE1_MASK VC4_MASK(11, 8) +# define DSI1_PHY_AFEC1_ACTRL_DLANE1_SHIFT 8 +# define DSI1_PHY_AFEC1_ACTRL_DLANE0_MASK VC4_MASK(7, 4) +# define DSI1_PHY_AFEC1_ACTRL_DLANE0_SHIFT 4 +# define DSI1_PHY_AFEC1_ACTRL_CLANE_MASK VC4_MASK(3, 0) +# define DSI1_PHY_AFEC1_ACTRL_CLANE_SHIFT 0 + +#define DSI1_TST_SEL 0x78 +#define DSI1_TST_MON 0x7c +#define DSI1_PHY_TST1 0x80 +#define DSI1_PHY_TST2 0x84 +#define DSI1_PHY_FIFO_STAT 0x88 +/* Actually, all registers in the range that aren't otherwise claimed + * will return the ID. + */ +#define DSI1_ID 0x8c + +/* General DSI hardware state. */ +struct vc4_dsi { + struct platform_device *pdev; + + struct mipi_dsi_host dsi_host; + struct drm_encoder *encoder; + struct drm_connector *connector; + struct drm_panel *panel; + + void __iomem *regs; + + struct dma_chan *reg_dma_chan; + dma_addr_t reg_dma_paddr; + u32 *reg_dma_mem; + dma_addr_t reg_paddr; + + /* Whether we're on bcm2835's DSI0 or DSI1. */ + int port; + + /* DSI channel for the panel we're connected to. */ + u32 channel; + u32 lanes; + enum mipi_dsi_pixel_format format; + u32 mode_flags; + + /* Input clock from CPRMAN to the digital PHY, for the DSI + * escape clock. + */ + struct clk *escape_clock; + + /* Input clock to the analog PHY, used to generate the DSI bit + * clock. + */ + struct clk *pll_phy_clock; + + /* HS Clocks generated within the DSI analog PHY. */ + struct clk_fixed_factor phy_clocks[3]; + + struct clk_hw_onecell_data *clk_onecell; + + /* Pixel clock output to the pixelvalve, generated from the HS + * clock. + */ + struct clk *pixel_clock; + + struct completion xfer_completion; + int xfer_result; +}; + +#define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host) + +static inline void +dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) +{ + struct dma_chan *chan = dsi->reg_dma_chan; + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + int ret; + + /* DSI0 should be able to write normally. */ + if (!chan) { + writel(val, dsi->regs + offset); + return; + } + + *dsi->reg_dma_mem = val; + + tx = chan->device->device_prep_dma_memcpy(chan, + dsi->reg_paddr + offset, + dsi->reg_dma_paddr, + 4, 0); + if (!tx) { + DRM_ERROR("Failed to set up DMA register write\n"); + return; + } + + cookie = tx->tx_submit(tx); + ret = dma_submit_error(cookie); + if (ret) { + DRM_ERROR("Failed to submit DMA: %d\n", ret); + return; + } + ret = dma_sync_wait(chan, cookie); + if (ret) + DRM_ERROR("Failed to wait for DMA: %d\n", ret); +} + +#define DSI_READ(offset) readl(dsi->regs + (offset)) +#define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) +#define DSI_PORT_READ(offset) \ + DSI_READ(dsi->port ? DSI1_##offset : DSI0_##offset) +#define DSI_PORT_WRITE(offset, val) \ + DSI_WRITE(dsi->port ? DSI1_##offset : DSI0_##offset, val) +#define DSI_PORT_BIT(bit) (dsi->port ? DSI1_##bit : DSI0_##bit) + +/* VC4 DSI encoder KMS struct */ +struct vc4_dsi_encoder { + struct vc4_encoder base; + struct vc4_dsi *dsi; +}; + +static inline struct vc4_dsi_encoder * +to_vc4_dsi_encoder(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vc4_dsi_encoder, base.base); +} + +/* VC4 DSI connector KMS struct */ +struct vc4_dsi_connector { + struct drm_connector base; + struct vc4_dsi *dsi; +}; + +static inline struct vc4_dsi_connector * +to_vc4_dsi_connector(struct drm_connector *connector) +{ + return container_of(connector, struct vc4_dsi_connector, base); +} + +#define DSI_REG(reg) { reg, #reg } +static const struct { + u32 reg; + const char *name; +} dsi0_regs[] = { + DSI_REG(DSI0_CTRL), + DSI_REG(DSI0_STAT), + DSI_REG(DSI0_HSTX_TO_CNT), + DSI_REG(DSI0_LPRX_TO_CNT), + DSI_REG(DSI0_TA_TO_CNT), + DSI_REG(DSI0_PR_TO_CNT), + DSI_REG(DSI0_DISP0_CTRL), + DSI_REG(DSI0_DISP1_CTRL), + DSI_REG(DSI0_INT_STAT), + DSI_REG(DSI0_INT_EN), + DSI_REG(DSI0_PHYC), + DSI_REG(DSI0_HS_CLT0), + DSI_REG(DSI0_HS_CLT1), + DSI_REG(DSI0_HS_CLT2), + DSI_REG(DSI0_HS_DLT3), + DSI_REG(DSI0_HS_DLT4), + DSI_REG(DSI0_HS_DLT5), + DSI_REG(DSI0_HS_DLT6), + DSI_REG(DSI0_HS_DLT7), + DSI_REG(DSI0_PHY_AFEC0), + DSI_REG(DSI0_PHY_AFEC1), + DSI_REG(DSI0_ID), +}; + +static const struct { + u32 reg; + const char *name; +} dsi1_regs[] = { + DSI_REG(DSI1_CTRL), + DSI_REG(DSI1_STAT), + DSI_REG(DSI1_HSTX_TO_CNT), + DSI_REG(DSI1_LPRX_TO_CNT), + DSI_REG(DSI1_TA_TO_CNT), + DSI_REG(DSI1_PR_TO_CNT), + DSI_REG(DSI1_DISP0_CTRL), + DSI_REG(DSI1_DISP1_CTRL), + DSI_REG(DSI1_INT_STAT), + DSI_REG(DSI1_INT_EN), + DSI_REG(DSI1_PHYC), + DSI_REG(DSI1_HS_CLT0), + DSI_REG(DSI1_HS_CLT1), + DSI_REG(DSI1_HS_CLT2), + DSI_REG(DSI1_HS_DLT3), + DSI_REG(DSI1_HS_DLT4), + DSI_REG(DSI1_HS_DLT5), + DSI_REG(DSI1_HS_DLT6), + DSI_REG(DSI1_HS_DLT7), + DSI_REG(DSI1_PHY_AFEC0), + DSI_REG(DSI1_PHY_AFEC1), + DSI_REG(DSI1_ID), +}; + +static void vc4_dsi_dump_regs(struct vc4_dsi *dsi) +{ + int i; + + if (dsi->port == 0) { + for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + dsi0_regs[i].reg, dsi0_regs[i].name, + DSI_READ(dsi0_regs[i].reg)); + } + } else { + for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + dsi1_regs[i].reg, dsi1_regs[i].name, + DSI_READ(dsi1_regs[i].reg)); + } + } +} + +#ifdef CONFIG_DEBUG_FS +int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *drm = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + int dsi_index = (uintptr_t)node->info_ent->data; + struct vc4_dsi *dsi = (dsi_index == 1 ? vc4->dsi1 : NULL); + int i; + + if (!dsi) + return 0; + + if (dsi->port == 0) { + for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { + seq_printf(m, "0x%04x (%s): 0x%08x\n", + dsi0_regs[i].reg, dsi0_regs[i].name, + DSI_READ(dsi0_regs[i].reg)); + } + } else { + for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { + seq_printf(m, "0x%04x (%s): 0x%08x\n", + dsi1_regs[i].reg, dsi1_regs[i].name, + DSI_READ(dsi1_regs[i].reg)); + } + } + + return 0; +} +#endif + +static enum drm_connector_status +vc4_dsi_connector_detect(struct drm_connector *connector, bool force) +{ + struct vc4_dsi_connector *vc4_connector = + to_vc4_dsi_connector(connector); + struct vc4_dsi *dsi = vc4_connector->dsi; + + if (dsi->panel) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void vc4_dsi_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static int vc4_dsi_connector_get_modes(struct drm_connector *connector) +{ + struct vc4_dsi_connector *vc4_connector = + to_vc4_dsi_connector(connector); + struct vc4_dsi *dsi = vc4_connector->dsi; + + if (dsi->panel) + return drm_panel_get_modes(dsi->panel); + + return 0; +} + +static const struct drm_connector_funcs vc4_dsi_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = vc4_dsi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vc4_dsi_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs vc4_dsi_connector_helper_funcs = { + .get_modes = vc4_dsi_connector_get_modes, +}; + +static struct drm_connector *vc4_dsi_connector_init(struct drm_device *dev, + struct vc4_dsi *dsi) +{ + struct drm_connector *connector = NULL; + struct vc4_dsi_connector *dsi_connector; + int ret = 0; + + dsi_connector = devm_kzalloc(dev->dev, sizeof(*dsi_connector), + GFP_KERNEL); + if (!dsi_connector) { + ret = -ENOMEM; + goto fail; + } + connector = &dsi_connector->base; + + dsi_connector->dsi = dsi; + + drm_connector_init(dev, connector, &vc4_dsi_connector_funcs, + DRM_MODE_CONNECTOR_DSI); + drm_connector_helper_add(connector, &vc4_dsi_connector_helper_funcs); + + connector->polled = 0; + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_mode_connector_attach_encoder(connector, dsi->encoder); + + return connector; + +fail: + if (connector) + vc4_dsi_connector_destroy(connector); + + return ERR_PTR(ret); +} + +static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs vc4_dsi_encoder_funcs = { + .destroy = vc4_dsi_encoder_destroy, +}; + +static void vc4_dsi_latch_ulps(struct vc4_dsi *dsi, bool latch) +{ + u32 afec0 = DSI_PORT_READ(PHY_AFEC0); + + if (latch) + afec0 |= DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); + else + afec0 &= ~DSI_PORT_BIT(PHY_AFEC0_LATCH_ULPS); + + DSI_PORT_WRITE(PHY_AFEC0, afec0); +} + +/* Enters or exits Ultra Low Power State. */ +static void vc4_dsi_ulps(struct vc4_dsi *dsi, bool ulps) +{ + bool continuous = dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS; + u32 phyc_ulps = ((continuous ? DSI_PORT_BIT(PHYC_CLANE_ULPS) : 0) | + DSI_PHYC_DLANE0_ULPS | + (dsi->lanes > 1 ? DSI_PHYC_DLANE1_ULPS : 0) | + (dsi->lanes > 2 ? DSI_PHYC_DLANE2_ULPS : 0) | + (dsi->lanes > 3 ? DSI_PHYC_DLANE3_ULPS : 0)); + u32 stat_ulps = ((continuous ? DSI1_STAT_PHY_CLOCK_ULPS : 0) | + DSI1_STAT_PHY_D0_ULPS | + (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_ULPS : 0) | + (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_ULPS : 0) | + (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_ULPS : 0)); + u32 stat_stop = ((continuous ? DSI1_STAT_PHY_CLOCK_STOP : 0) | + DSI1_STAT_PHY_D0_STOP | + (dsi->lanes > 1 ? DSI1_STAT_PHY_D1_STOP : 0) | + (dsi->lanes > 2 ? DSI1_STAT_PHY_D2_STOP : 0) | + (dsi->lanes > 3 ? DSI1_STAT_PHY_D3_STOP : 0)); + int ret; + + DSI_PORT_WRITE(STAT, stat_ulps); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) | phyc_ulps); + ret = wait_for((DSI_PORT_READ(STAT) & stat_ulps) == stat_ulps, 200); + if (ret) { + dev_warn(&dsi->pdev->dev, + "Timeout waiting for DSI ULPS entry: STAT 0x%08x", + DSI_PORT_READ(STAT)); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); + vc4_dsi_latch_ulps(dsi, false); + return; + } + + /* The DSI module can't be disabled while the module is + * generating ULPS state. So, to be able to disable the + * module, we have the AFE latch the ULPS state and continue + * on to having the module enter STOP. + */ + vc4_dsi_latch_ulps(dsi, ulps); + + DSI_PORT_WRITE(STAT, stat_stop); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); + ret = wait_for((DSI_PORT_READ(STAT) & stat_stop) == stat_stop, 200); + if (ret) { + dev_warn(&dsi->pdev->dev, + "Timeout waiting for DSI STOP entry: STAT 0x%08x", + DSI_PORT_READ(STAT)); + DSI_PORT_WRITE(PHYC, DSI_PORT_READ(PHYC) & ~phyc_ulps); + return; + } +} + +static u32 +dsi_hs_timing(u32 ui_ns, u32 ns, u32 ui) +{ + /* The HS timings have to be rounded up to a multiple of 8 + * because we're using the byte clock. + */ + return roundup(ui + DIV_ROUND_UP(ns, ui_ns), 8); +} + +/* ESC always runs at 100Mhz. */ +#define ESC_TIME_NS 10 + +static u32 +dsi_esc_timing(u32 ns) +{ + return DIV_ROUND_UP(ns, ESC_TIME_NS); +} + +static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) +{ + struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); + struct vc4_dsi *dsi = vc4_encoder->dsi; + struct device *dev = &dsi->pdev->dev; + + drm_panel_disable(dsi->panel); + + vc4_dsi_ulps(dsi, true); + + drm_panel_unprepare(dsi->panel); + + clk_disable_unprepare(dsi->pll_phy_clock); + clk_disable_unprepare(dsi->escape_clock); + clk_disable_unprepare(dsi->pixel_clock); + + pm_runtime_put(dev); +} + +static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) +{ + struct drm_display_mode *mode = &encoder->crtc->mode; + struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); + struct vc4_dsi *dsi = vc4_encoder->dsi; + struct device *dev = &dsi->pdev->dev; + u32 format = 0, divider = 0; + bool debug_dump_regs = false; + unsigned long hs_clock; + u32 ui_ns; + /* Minimum LP state duration in escape clock cycles. */ + u32 lpx = dsi_esc_timing(60); + unsigned long pixel_clock_hz = mode->clock * 1000; + unsigned long dsip_clock; + unsigned long phy_clock; + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret) { + DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->port); + return; + } + + ret = drm_panel_prepare(dsi->panel); + if (ret) { + DRM_ERROR("Panel failed to prepare\n"); + return; + } + + if (debug_dump_regs) { + DRM_INFO("DSI regs before:\n"); + vc4_dsi_dump_regs(dsi); + } + + switch (dsi->format) { + case MIPI_DSI_FMT_RGB888: + format = DSI_PFORMAT_RGB888; + divider = 24 / dsi->lanes; + break; + case MIPI_DSI_FMT_RGB666: + format = DSI_PFORMAT_RGB666; + divider = 24 / dsi->lanes; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + format = DSI_PFORMAT_RGB666_PACKED; + divider = 18 / dsi->lanes; + break; + case MIPI_DSI_FMT_RGB565: + format = DSI_PFORMAT_RGB565; + divider = 16 / dsi->lanes; + break; + } + + phy_clock = pixel_clock_hz * divider; + ret = clk_set_rate(dsi->pll_phy_clock, phy_clock); + if (ret) { + dev_err(&dsi->pdev->dev, + "Failed to set phy clock to %ld: %d\n", phy_clock, ret); + } + + /* Reset the DSI and all its fifos. */ + DSI_PORT_WRITE(CTRL, + DSI_CTRL_SOFT_RESET_CFG | + DSI_PORT_BIT(CTRL_RESET_FIFOS)); + + DSI_PORT_WRITE(CTRL, + DSI_CTRL_HSDT_EOT_DISABLE | + DSI_CTRL_RX_LPDT_EOT_DISABLE); + + /* Clear all stat bits so we see what has happened during enable. */ + DSI_PORT_WRITE(STAT, DSI_PORT_READ(STAT)); + + /* Set AFE CTR00/CTR1 to release powerdown of analog. */ + if (dsi->port == 0) { + u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | + VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ)); + + if (dsi->lanes < 2) + afec0 |= DSI0_PHY_AFEC0_PD_DLANE1; + + if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) + afec0 |= DSI0_PHY_AFEC0_RESET; + + DSI_PORT_WRITE(PHY_AFEC0, afec0); + + DSI_PORT_WRITE(PHY_AFEC1, + VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE1) | + VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE0) | + VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_CLANE)); + } else { + u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | + VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_CLANE) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE0) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE1) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE2) | + VC4_SET_FIELD(6, DSI1_PHY_AFEC0_IDR_DLANE3)); + + if (dsi->lanes < 4) + afec0 |= DSI1_PHY_AFEC0_PD_DLANE3; + if (dsi->lanes < 3) + afec0 |= DSI1_PHY_AFEC0_PD_DLANE2; + if (dsi->lanes < 2) + afec0 |= DSI1_PHY_AFEC0_PD_DLANE1; + + afec0 |= DSI1_PHY_AFEC0_RESET; + + DSI_PORT_WRITE(PHY_AFEC0, afec0); + + DSI_PORT_WRITE(PHY_AFEC1, 0); + + /* AFEC reset hold time */ + mdelay(1); + } + + ret = clk_prepare_enable(dsi->escape_clock); + if (ret) { + DRM_ERROR("Failed to turn on DSI escape clock: %d\n", ret); + return; + } + + ret = clk_prepare_enable(dsi->pll_phy_clock); + if (ret) { + DRM_ERROR("Failed to turn on DSI PLL: %d\n", ret); + return; + } + + hs_clock = clk_get_rate(dsi->pll_phy_clock); + + /* Yes, we set the DSI0P/DSI1P pixel clock to the byte rate, + * not the pixel clock rate. DSIxP take from the APHY's byte, + * DDR2, or DDR4 clock (we use byte) and feed into the PV at + * that rate. Separately, a value derived from PIX_CLK_DIV + * and HS_CLKC is fed into the PV to divide down to the actual + * pixel clock for pushing pixels into DSI. + */ + dsip_clock = phy_clock / 8; + ret = clk_set_rate(dsi->pixel_clock, dsip_clock); + if (ret) { + dev_err(dev, "Failed to set pixel clock to %ldHz: %d\n", + dsip_clock, ret); + } + + ret = clk_prepare_enable(dsi->pixel_clock); + if (ret) { + DRM_ERROR("Failed to turn on DSI pixel clock: %d\n", ret); + return; + } + + /* How many ns one DSI unit interval is. Note that the clock + * is DDR, so there's an extra divide by 2. + */ + ui_ns = DIV_ROUND_UP(500000000, hs_clock); + + DSI_PORT_WRITE(HS_CLT0, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 262, 0), + DSI_HS_CLT0_CZERO) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 0, 8), + DSI_HS_CLT0_CPRE) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 38, 0), + DSI_HS_CLT0_CPREP)); + + DSI_PORT_WRITE(HS_CLT1, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 0), + DSI_HS_CLT1_CTRAIL) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 60, 52), + DSI_HS_CLT1_CPOST)); + + DSI_PORT_WRITE(HS_CLT2, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 1000000, 0), + DSI_HS_CLT2_WUP)); + + DSI_PORT_WRITE(HS_DLT3, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 100, 0), + DSI_HS_DLT3_EXIT) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 105, 6), + DSI_HS_DLT3_ZERO) | + VC4_SET_FIELD(dsi_hs_timing(ui_ns, 40, 4), + DSI_HS_DLT3_PRE)); + + DSI_PORT_WRITE(HS_DLT4, + VC4_SET_FIELD(dsi_hs_timing(ui_ns, lpx * ESC_TIME_NS, 0), + DSI_HS_DLT4_LPX) | + VC4_SET_FIELD(max(dsi_hs_timing(ui_ns, 0, 8), + dsi_hs_timing(ui_ns, 60, 4)), + DSI_HS_DLT4_TRAIL) | + VC4_SET_FIELD(0, DSI_HS_DLT4_ANLAT)); + + DSI_PORT_WRITE(HS_DLT5, VC4_SET_FIELD(dsi_hs_timing(ui_ns, 1000, 5000), + DSI_HS_DLT5_INIT)); + + DSI_PORT_WRITE(HS_DLT6, + VC4_SET_FIELD(lpx * 5, DSI_HS_DLT6_TA_GET) | + VC4_SET_FIELD(lpx, DSI_HS_DLT6_TA_SURE) | + VC4_SET_FIELD(lpx * 4, DSI_HS_DLT6_TA_GO) | + VC4_SET_FIELD(lpx, DSI_HS_DLT6_LP_LPX)); + + DSI_PORT_WRITE(HS_DLT7, + VC4_SET_FIELD(dsi_esc_timing(1000000), + DSI_HS_DLT7_LP_WUP)); + + DSI_PORT_WRITE(PHYC, + DSI_PHYC_DLANE0_ENABLE | + (dsi->lanes >= 2 ? DSI_PHYC_DLANE1_ENABLE : 0) | + (dsi->lanes >= 3 ? DSI_PHYC_DLANE2_ENABLE : 0) | + (dsi->lanes >= 4 ? DSI_PHYC_DLANE3_ENABLE : 0) | + DSI_PORT_BIT(PHYC_CLANE_ENABLE) | + ((dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? + 0 : DSI_PORT_BIT(PHYC_HS_CLK_CONTINUOUS)) | + (dsi->port == 0 ? + VC4_SET_FIELD(lpx - 1, DSI0_PHYC_ESC_CLK_LPDT) : + VC4_SET_FIELD(lpx - 1, DSI1_PHYC_ESC_CLK_LPDT))); + + DSI_PORT_WRITE(CTRL, + DSI_PORT_READ(CTRL) | + DSI_CTRL_CAL_BYTE); + + /* HS timeout in HS clock cycles: disabled. */ + DSI_PORT_WRITE(HSTX_TO_CNT, 0); + /* LP receive timeout in HS clocks. */ + DSI_PORT_WRITE(LPRX_TO_CNT, 0xffffff); + /* Bus turnaround timeout */ + DSI_PORT_WRITE(TA_TO_CNT, 100000); + /* Display reset sequence timeout */ + DSI_PORT_WRITE(PR_TO_CNT, 100000); + + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { + DSI_PORT_WRITE(DISP0_CTRL, + VC4_SET_FIELD(divider, DSI_DISP0_PIX_CLK_DIV) | + VC4_SET_FIELD(format, DSI_DISP0_PFORMAT) | + VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME, + DSI_DISP0_LP_STOP_CTRL) | + DSI_DISP0_ST_END | + DSI_DISP0_ENABLE); + } else { + DSI_PORT_WRITE(DISP0_CTRL, + DSI_DISP0_COMMAND_MODE | + DSI_DISP0_ENABLE); + } + + /* Set up DISP1 for transferring long command payloads through + * the pixfifo. + */ + DSI_PORT_WRITE(DISP1_CTRL, + VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE, + DSI_DISP1_PFORMAT) | + DSI_DISP1_ENABLE); + + /* Ungate the block. */ + if (dsi->port == 0) + DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); + else + DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN); + + /* Bring AFE out of reset. */ + if (dsi->port == 0) { + } else { + DSI_PORT_WRITE(PHY_AFEC0, + DSI_PORT_READ(PHY_AFEC0) & + ~DSI1_PHY_AFEC0_RESET); + } + + vc4_dsi_ulps(dsi, false); + + if (debug_dump_regs) { + DRM_INFO("DSI regs after:\n"); + vc4_dsi_dump_regs(dsi); + } + + ret = drm_panel_enable(dsi->panel); + if (ret) { + DRM_ERROR("Panel failed to enable\n"); + drm_panel_unprepare(dsi->panel); + return; + } +} + +static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct vc4_dsi *dsi = host_to_dsi(host); + struct mipi_dsi_packet packet; + u32 pkth = 0, pktc = 0; + int i, ret; + bool is_long = mipi_dsi_packet_format_is_long(msg->type); + u32 cmd_fifo_len = 0, pix_fifo_len = 0; + + mipi_dsi_create_packet(&packet, msg); + + pkth |= VC4_SET_FIELD(packet.header[0], DSI_TXPKT1H_BC_DT); + pkth |= VC4_SET_FIELD(packet.header[1] | + (packet.header[2] << 8), + DSI_TXPKT1H_BC_PARAM); + if (is_long) { + /* Divide data across the various FIFOs we have available. + * The command FIFO takes byte-oriented data, but is of + * limited size. The pixel FIFO (never actually used for + * pixel data in reality) is word oriented, and substantially + * larger. So, we use the pixel FIFO for most of the data, + * sending the residual bytes in the command FIFO at the start. + * + * With this arrangement, the command FIFO will never get full. + */ + if (packet.payload_length <= 16) { + cmd_fifo_len = packet.payload_length; + pix_fifo_len = 0; + } else { + cmd_fifo_len = (packet.payload_length % + DSI_PIX_FIFO_WIDTH); + pix_fifo_len = ((packet.payload_length - cmd_fifo_len) / + DSI_PIX_FIFO_WIDTH); + } + + WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH); + + pkth |= VC4_SET_FIELD(cmd_fifo_len, DSI_TXPKT1H_BC_CMDFIFO); + } + + if (msg->rx_len) { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_RX, + DSI_TXPKT1C_CMD_CTRL); + } else { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_CMD_CTRL_TX, + DSI_TXPKT1C_CMD_CTRL); + } + + for (i = 0; i < cmd_fifo_len; i++) + DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); + for (i = 0; i < pix_fifo_len; i++) { + const u8 *pix = packet.payload + cmd_fifo_len + i * 4; + + DSI_PORT_WRITE(TXPKT_PIX_FIFO, + pix[0] | + pix[1] << 8 | + pix[2] << 16 | + pix[3] << 24); + } + + if (msg->flags & MIPI_DSI_MSG_USE_LPM) + pktc |= DSI_TXPKT1C_CMD_MODE_LP; + if (is_long) + pktc |= DSI_TXPKT1C_CMD_TYPE_LONG; + + /* Send one copy of the packet. Larger repeats are used for pixel + * data in command mode. + */ + pktc |= VC4_SET_FIELD(1, DSI_TXPKT1C_CMD_REPEAT); + + pktc |= DSI_TXPKT1C_CMD_EN; + if (pix_fifo_len) { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SECONDARY, + DSI_TXPKT1C_DISPLAY_NO); + } else { + pktc |= VC4_SET_FIELD(DSI_TXPKT1C_DISPLAY_NO_SHORT, + DSI_TXPKT1C_DISPLAY_NO); + } + + /* Enable the appropriate interrupt for the transfer completion. */ + dsi->xfer_result = 0; + reinit_completion(&dsi->xfer_completion); + DSI_PORT_WRITE(INT_STAT, DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); + if (msg->rx_len) { + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | + DSI1_INT_PHY_DIR_RTF)); + } else { + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | + DSI1_INT_TXPKT1_DONE)); + } + + /* Send the packet. */ + DSI_PORT_WRITE(TXPKT1H, pkth); + DSI_PORT_WRITE(TXPKT1C, pktc); + + if (!wait_for_completion_timeout(&dsi->xfer_completion, + msecs_to_jiffies(1000))) { + dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout"); + dev_err(&dsi->pdev->dev, "instat: 0x%08x\n", + DSI_PORT_READ(INT_STAT)); + ret = -ETIMEDOUT; + } else { + ret = dsi->xfer_result; + } + + DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + + if (ret) + goto reset_fifo_and_return; + + if (ret == 0 && msg->rx_len) { + u32 rxpkt1h = DSI_PORT_READ(RXPKT1H); + u8 *msg_rx = msg->rx_buf; + + if (rxpkt1h & DSI_RXPKT1H_PKT_TYPE_LONG) { + u32 rxlen = VC4_GET_FIELD(rxpkt1h, + DSI_RXPKT1H_BC_PARAM); + + if (rxlen != msg->rx_len) { + DRM_ERROR("DSI returned %db, expecting %db\n", + rxlen, (int)msg->rx_len); + ret = -ENXIO; + goto reset_fifo_and_return; + } + + for (i = 0; i < msg->rx_len; i++) + msg_rx[i] = DSI_READ(DSI1_RXPKT_FIFO); + } else { + /* FINISHME: Handle AWER */ + + msg_rx[0] = VC4_GET_FIELD(rxpkt1h, + DSI_RXPKT1H_SHORT_0); + if (msg->rx_len > 1) { + msg_rx[1] = VC4_GET_FIELD(rxpkt1h, + DSI_RXPKT1H_SHORT_1); + } + } + } + + return ret; + +reset_fifo_and_return: + DRM_ERROR("DSI transfer failed, resetting: %d\n", ret); + + DSI_PORT_WRITE(TXPKT1C, DSI_PORT_READ(TXPKT1C) & ~DSI_TXPKT1C_CMD_EN); + udelay(1); + DSI_PORT_WRITE(CTRL, + DSI_PORT_READ(CTRL) | + DSI_PORT_BIT(CTRL_RESET_FIFOS)); + + DSI_PORT_WRITE(TXPKT1C, 0); + DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + return ret; +} + +static int vc4_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct vc4_dsi *dsi = host_to_dsi(host); + int ret = 0; + + dsi->lanes = device->lanes; + dsi->channel = device->channel; + dsi->format = device->format; + dsi->mode_flags = device->mode_flags; + + if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) { + dev_err(&dsi->pdev->dev, + "Only VIDEO mode panels supported currently.\n"); + return 0; + } + + dsi->panel = of_drm_find_panel(device->dev.of_node); + if (!dsi->panel) + return 0; + + ret = drm_panel_attach(dsi->panel, dsi->connector); + if (ret != 0) + return ret; + + drm_helper_hpd_irq_event(dsi->connector->dev); + + return 0; +} + +static int vc4_dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct vc4_dsi *dsi = host_to_dsi(host); + + if (dsi->panel) { + int ret = drm_panel_detach(dsi->panel); + + if (ret) + return ret; + + dsi->panel = NULL; + + drm_helper_hpd_irq_event(dsi->connector->dev); + } + + return 0; +} + +static const struct mipi_dsi_host_ops vc4_dsi_host_ops = { + .attach = vc4_dsi_host_attach, + .detach = vc4_dsi_host_detach, + .transfer = vc4_dsi_host_transfer, +}; + +static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = { + .disable = vc4_dsi_encoder_disable, + .enable = vc4_dsi_encoder_enable, +}; + +static const struct of_device_id vc4_dsi_dt_match[] = { + { .compatible = "brcm,bcm2835-dsi1", (void *)(uintptr_t)1 }, + {} +}; + +static void dsi_handle_error(struct vc4_dsi *dsi, + irqreturn_t *ret, u32 stat, u32 bit, + const char *type) +{ + if (!(stat & bit)) + return; + + DRM_ERROR("DSI%d: %s error\n", dsi->port, type); + *ret = IRQ_HANDLED; +} + +static irqreturn_t vc4_dsi_irq_handler(int irq, void *data) +{ + struct vc4_dsi *dsi = data; + u32 stat = DSI_PORT_READ(INT_STAT); + irqreturn_t ret = IRQ_NONE; + + DSI_PORT_WRITE(INT_STAT, stat); + + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_SYNC_ESC, "LPDT sync"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_CONTROL, "data lane 0 sequence"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_CONT_LP0, "LP0 contention"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_ERR_CONT_LP1, "LP1 contention"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_HSTX_TO, "HSTX timeout"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_LPRX_TO, "LPRX timeout"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_TA_TO, "turnaround timeout"); + dsi_handle_error(dsi, &ret, stat, + DSI1_INT_PR_TO, "peripheral reset timeout"); + + if (stat & (DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF)) { + complete(&dsi->xfer_completion); + ret = IRQ_HANDLED; + } else if (stat & DSI1_INT_HSTX_TO) { + complete(&dsi->xfer_completion); + dsi->xfer_result = -ETIMEDOUT; + ret = IRQ_HANDLED; + } + + return ret; +} + +/** + * Exposes clocks generated by the analog PHY that are consumed by + * CPRMAN (clk-bcm2835.c). + */ +static int +vc4_dsi_init_phy_clocks(struct vc4_dsi *dsi) +{ + struct device *dev = &dsi->pdev->dev; + const char *parent_name = __clk_get_name(dsi->pll_phy_clock); + static const struct { + const char *dsi0_name, *dsi1_name; + int div; + } phy_clocks[] = { + { "dsi0_byte", "dsi1_byte", 8 }, + { "dsi0_ddr2", "dsi1_ddr2", 4 }, + { "dsi0_ddr", "dsi1_ddr", 2 }, + }; + int i; + + dsi->clk_onecell = devm_kzalloc(dev, + sizeof(*dsi->clk_onecell) + + ARRAY_SIZE(phy_clocks) * + sizeof(struct clk_hw *), + GFP_KERNEL); + if (!dsi->clk_onecell) + return -ENOMEM; + dsi->clk_onecell->num = ARRAY_SIZE(phy_clocks); + + for (i = 0; i < ARRAY_SIZE(phy_clocks); i++) { + struct clk_fixed_factor *fix = &dsi->phy_clocks[i]; + struct clk_init_data init; + int ret; + + /* We just use core fixed factor clock ops for the PHY + * clocks. The clocks are actually gated by the + * PHY_AFEC0_DDRCLK_EN bits, which we should be + * setting if we use the DDR/DDR2 clocks. However, + * vc4_dsi_encoder_enable() is setting up both AFEC0, + * setting both our parent DSI PLL's rate and this + * clock's rate, so it knows if DDR/DDR2 are going to + * be used and could enable the gates itself. + */ + fix->mult = 1; + fix->div = phy_clocks[i].div; + fix->hw.init = &init; + + memset(&init, 0, sizeof(init)); + init.parent_names = &parent_name; + init.num_parents = 1; + if (dsi->port == 1) + init.name = phy_clocks[i].dsi1_name; + else + init.name = phy_clocks[i].dsi0_name; + init.ops = &clk_fixed_factor_ops; + + ret = devm_clk_hw_register(dev, &fix->hw); + if (ret) + return ret; + + dsi->clk_onecell->hws[i] = &fix->hw; + } + + return of_clk_add_hw_provider(dev->of_node, + of_clk_hw_onecell_get, + dsi->clk_onecell); +} + +static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_dsi *dsi; + struct vc4_dsi_encoder *vc4_dsi_encoder; + const struct of_device_id *match; + dma_cap_mask_t dma_mask; + int ret; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + match = of_match_device(vc4_dsi_dt_match, dev); + if (!match) + return -ENODEV; + + dsi->port = (uintptr_t)match->data; + + vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder), + GFP_KERNEL); + if (!vc4_dsi_encoder) + return -ENOMEM; + vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1; + vc4_dsi_encoder->dsi = dsi; + dsi->encoder = &vc4_dsi_encoder->base.base; + + dsi->pdev = pdev; + dsi->regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(dsi->regs)) + return PTR_ERR(dsi->regs); + + if (DSI_PORT_READ(ID) != DSI_ID_VALUE) { + dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", + DSI_PORT_READ(ID), DSI_ID_VALUE); + return -ENODEV; + } + + /* DSI1 has a broken AXI slave that doesn't respond to writes + * from the ARM. It does handle writes from the DMA engine, + * so set up a channel for talking to it. + */ + if (dsi->port == 1) { + dsi->reg_dma_mem = dma_alloc_coherent(dev, 4, + &dsi->reg_dma_paddr, + GFP_KERNEL); + if (!dsi->reg_dma_mem) { + DRM_ERROR("Failed to get DMA memory\n"); + return -ENOMEM; + } + + dma_cap_zero(dma_mask); + dma_cap_set(DMA_MEMCPY, dma_mask); + dsi->reg_dma_chan = dma_request_chan_by_mask(&dma_mask); + if (IS_ERR(dsi->reg_dma_chan)) { + ret = PTR_ERR(dsi->reg_dma_chan); + if (ret != -EPROBE_DEFER) + DRM_ERROR("Failed to get DMA channel: %d\n", + ret); + return ret; + } + + /* Get the physical address of the device's registers. The + * struct resource for the regs gives us the bus address + * instead. + */ + dsi->reg_paddr = be32_to_cpup(of_get_address(dev->of_node, + 0, NULL, NULL)); + } + + init_completion(&dsi->xfer_completion); + /* At startup enable error-reporting interrupts and nothing else. */ + DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + /* Clear any existing interrupt state. */ + DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT)); + + ret = devm_request_irq(dev, platform_get_irq(pdev, 0), + vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get interrupt: %d\n", ret); + return ret; + } + + dsi->escape_clock = devm_clk_get(dev, "escape"); + if (IS_ERR(dsi->escape_clock)) { + ret = PTR_ERR(dsi->escape_clock); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get escape clock: %d\n", ret); + return ret; + } + + dsi->pll_phy_clock = devm_clk_get(dev, "phy"); + if (IS_ERR(dsi->pll_phy_clock)) { + ret = PTR_ERR(dsi->pll_phy_clock); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get phy clock: %d\n", ret); + return ret; + } + + dsi->pixel_clock = devm_clk_get(dev, "pixel"); + if (IS_ERR(dsi->pixel_clock)) { + ret = PTR_ERR(dsi->pixel_clock); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get pixel clock: %d\n", ret); + return ret; + } + + /* The esc clock rate is supposed to always be 100Mhz. */ + ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); + if (ret) { + dev_err(dev, "Failed to set esc clock: %d\n", ret); + return ret; + } + + ret = vc4_dsi_init_phy_clocks(dsi); + if (ret) + return ret; + + if (dsi->port == 1) + vc4->dsi1 = dsi; + + drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); + drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); + + dsi->connector = vc4_dsi_connector_init(drm, dsi); + if (IS_ERR(dsi->connector)) { + ret = PTR_ERR(dsi->connector); + goto err_destroy_encoder; + } + + dsi->dsi_host.ops = &vc4_dsi_host_ops; + dsi->dsi_host.dev = dev; + + mipi_dsi_host_register(&dsi->dsi_host); + + dev_set_drvdata(dev, dsi); + + pm_runtime_enable(dev); + + return 0; + +err_destroy_encoder: + vc4_dsi_encoder_destroy(dsi->encoder); + + return ret; +} + +static void vc4_dsi_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_dsi *dsi = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + + vc4_dsi_connector_destroy(dsi->connector); + vc4_dsi_encoder_destroy(dsi->encoder); + + mipi_dsi_host_unregister(&dsi->dsi_host); + + clk_disable_unprepare(dsi->pll_phy_clock); + clk_disable_unprepare(dsi->escape_clock); + + if (dsi->port == 1) + vc4->dsi1 = NULL; +} + +static const struct component_ops vc4_dsi_ops = { + .bind = vc4_dsi_bind, + .unbind = vc4_dsi_unbind, +}; + +static int vc4_dsi_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_dsi_ops); +} + +static int vc4_dsi_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_dsi_ops); + return 0; +} + +struct platform_driver vc4_dsi_driver = { + .probe = vc4_dsi_dev_probe, + .remove = vc4_dsi_dev_remove, + .driver = { + .name = "vc4_dsi", + .of_match_table = vc4_dsi_dt_match, + }, +}; diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index ab3016982466..1eef98c3331d 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -26,6 +26,7 @@ #include <linux/pm_runtime.h> #include <linux/device.h> #include <linux/io.h> +#include <linux/sched/signal.h> #include "uapi/drm/vc4_drm.h" #include "vc4_drv.h" diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index c4cb2e26de32..93d5994f3a04 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -356,15 +356,11 @@ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) return; } - if (vc4_encoder->rgb_range_selectable) { - if (vc4_encoder->limited_rgb_range) { - frame.avi.quantization_range = - HDMI_QUANTIZATION_RANGE_LIMITED; - } else { - frame.avi.quantization_range = - HDMI_QUANTIZATION_RANGE_FULL; - } - } + drm_hdmi_avi_infoframe_quant_range(&frame.avi, mode, + vc4_encoder->limited_rgb_range ? + HDMI_QUANTIZATION_RANGE_LIMITED : + HDMI_QUANTIZATION_RANGE_FULL, + vc4_encoder->rgb_range_selectable); vc4_hdmi_write_infoframe(encoder, &frame); } @@ -463,7 +459,9 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, VC4_HD_CSC_CTL_ORDER); - if (vc4_encoder->hdmi_monitor && drm_match_cea_mode(mode) > 1) { + if (vc4_encoder->hdmi_monitor && + drm_default_rgb_quant_range(mode) == + HDMI_QUANTIZATION_RANGE_LIMITED) { /* CEA VICs other than #1 requre limited range RGB * output unless overridden by an AVI infoframe. * Apply a colorspace conversion to squash 0-255 down diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 6fbab1c82cb1..f7f7677f6d8d 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -141,8 +141,7 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, int ret, i; u32 __iomem *dst_kernel; - ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS, 1, - 0); + ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS); if (ret) { DRM_ERROR("Failed to allocate space for filter kernel: %d\n", ret); @@ -170,6 +169,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) struct vc4_dev *vc4 = drm->dev_private; struct vc4_hvs *hvs = NULL; int ret; + u32 dispctrl; hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); if (!hvs) @@ -211,6 +211,19 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) return ret; vc4->hvs = hvs; + + dispctrl = HVS_READ(SCALER_DISPCTRL); + + dispctrl |= SCALER_DISPCTRL_ENABLE; + + /* Set DSP3 (PV1) to use HVS channel 2, which would otherwise + * be unused. + */ + dispctrl &= ~SCALER_DISPCTRL_DSP3_MUX_MASK; + dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); + + HVS_WRITE(SCALER_DISPCTRL, dispctrl); + return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index be8dd8262f27..ad7925a9e0ea 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -231,7 +231,6 @@ int vc4_kms_load(struct drm_device *dev) drm_mode_config_reset(dev); vc4->fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_crtc, dev->mode_config.num_connector); if (IS_ERR(vc4->fbdev)) vc4->fbdev = NULL; diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 686cdd3c86f2..f7a229df572d 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -295,8 +295,8 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); u32 subpixel_src_mask = (1 << 16) - 1; - u32 format = fb->pixel_format; - int num_planes = drm_format_num_planes(format); + u32 format = fb->format->format; + int num_planes = fb->format->num_planes; u32 h_subsample = 1; u32 v_subsample = 1; int i; @@ -369,7 +369,7 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) */ if (vc4_state->crtc_x < 0) { for (i = 0; i < num_planes; i++) { - u32 cpp = drm_format_plane_cpp(fb->pixel_format, i); + u32 cpp = fb->format->cpp[i]; u32 subs = ((i == 0) ? 1 : h_subsample); vc4_state->offsets[i] += (cpp * @@ -496,7 +496,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); struct drm_framebuffer *fb = state->fb; u32 ctl0_offset = vc4_state->dlist_count; - const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); + const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); int num_planes = drm_format_num_planes(format->drm); u32 scl0, scl1; u32 lbm_size; @@ -514,9 +514,9 @@ static int vc4_plane_mode_set(struct drm_plane *plane, if (lbm_size) { if (!vc4_state->lbm.allocated) { spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); - ret = drm_mm_insert_node(&vc4->hvs->lbm_mm, - &vc4_state->lbm, - lbm_size, 32, 0); + ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, + &vc4_state->lbm, + lbm_size, 32, 0, 0); spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); } else { WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 39f6886b2410..385405a2df05 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -190,6 +190,8 @@ # define PV_VCONTROL_ODD_DELAY_SHIFT 6 # define PV_VCONTROL_ODD_FIRST BIT(5) # define PV_VCONTROL_INTERLACE BIT(4) +# define PV_VCONTROL_DSI BIT(3) +# define PV_VCONTROL_COMMAND BIT(2) # define PV_VCONTROL_CONTINUOUS BIT(1) # define PV_VCONTROL_VIDEN BIT(0) @@ -244,6 +246,9 @@ # define SCALER_DISPCTRL_ENABLE BIT(31) # define SCALER_DISPCTRL_DSP2EISLUR BIT(15) # define SCALER_DISPCTRL_DSP1EISLUR BIT(14) +# define SCALER_DISPCTRL_DSP3_MUX_MASK VC4_MASK(19, 18) +# define SCALER_DISPCTRL_DSP3_MUX_SHIFT 18 + /* Enables Display 0 short line and underrun contribution to * SCALER_DISPSTAT_IRQDISP0. Note that short frame contributions are * always enabled. diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 477e07f0ecb6..a1f42d125e6e 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -50,8 +50,9 @@ static void vgem_gem_free_object(struct drm_gem_object *obj) kfree(vgem_obj); } -static int vgem_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int vgem_gem_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct drm_vgem_gem_object *obj = vma->vm_private_data; /* We don't use vmf->pgoff since that has the fake offset */ unsigned long vaddr = vmf->address; @@ -287,7 +288,7 @@ static int vgem_prime_mmap(struct drm_gem_object *obj, if (!obj->filp) return -ENODEV; - ret = obj->filp->f_op->mmap(obj->filp, vma); + ret = call_mmap(obj->filp, vma); if (ret) return ret; diff --git a/drivers/gpu/drm/vgem/vgem_drv.h b/drivers/gpu/drm/vgem/vgem_drv.h index 1f8798ad329c..cb59c7ab98b9 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.h +++ b/drivers/gpu/drm/vgem/vgem_drv.h @@ -31,6 +31,7 @@ #include <drm/drmP.h> #include <drm/drm_gem.h> +#include <drm/drm_cache.h> #include <uapi/drm/vgem_drm.h> diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c index da25dfe7b80e..3109c8308eb5 100644 --- a/drivers/gpu/drm/vgem/vgem_fence.c +++ b/drivers/gpu/drm/vgem/vgem_fence.c @@ -190,12 +190,12 @@ int vgem_fence_attach_ioctl(struct drm_device *dev, /* Expose the fence via the dma-buf */ ret = 0; - ww_mutex_lock(&resv->lock, NULL); + reservation_object_lock(resv, NULL); if (arg->flags & VGEM_FENCE_WRITE) reservation_object_add_excl_fence(resv, fence); else if ((ret = reservation_object_reserve_shared(resv)) == 0) reservation_object_add_shared_fence(resv, fence); - ww_mutex_unlock(&resv->lock); + reservation_object_unlock(resv); /* Record the fence in our idr for later signaling */ if (ret == 0) { diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index 286a785fab4f..9873942ca8f4 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -134,7 +134,7 @@ extern int via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file extern int via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_driver_load(struct drm_device *dev, unsigned long chipset); -extern int via_driver_unload(struct drm_device *dev); +extern void via_driver_unload(struct drm_device *dev); extern int via_init_context(struct drm_device *dev, int context); extern int via_final_context(struct drm_device *dev, int context); diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c index 0b3522dba6e8..2ad865870372 100644 --- a/drivers/gpu/drm/via/via_map.c +++ b/drivers/gpu/drm/via/via_map.c @@ -116,13 +116,11 @@ int via_driver_load(struct drm_device *dev, unsigned long chipset) return 0; } -int via_driver_unload(struct drm_device *dev) +void via_driver_unload(struct drm_device *dev) { drm_via_private_t *dev_priv = dev->dev_private; idr_destroy(&dev_priv->object_idr); kfree(dev_priv); - - return 0; } diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c index a04ef1c992d9..4217d66a5cc6 100644 --- a/drivers/gpu/drm/via/via_mm.c +++ b/drivers/gpu/drm/via/via_mm.c @@ -140,11 +140,11 @@ int via_mem_alloc(struct drm_device *dev, void *data, if (mem->type == VIA_MEM_AGP) retval = drm_mm_insert_node(&dev_priv->agp_mm, &item->mm_node, - tmpSize, 0, DRM_MM_SEARCH_DEFAULT); + tmpSize); else retval = drm_mm_insert_node(&dev_priv->vram_mm, &item->mm_node, - tmpSize, 0, DRM_MM_SEARCH_DEFAULT); + tmpSize); if (retval) goto fail_alloc; diff --git a/drivers/gpu/drm/virtio/Kconfig b/drivers/gpu/drm/virtio/Kconfig index 81d1807ac228..0c384d9a2b75 100644 --- a/drivers/gpu/drm/virtio/Kconfig +++ b/drivers/gpu/drm/virtio/Kconfig @@ -1,6 +1,6 @@ config DRM_VIRTIO_GPU tristate "Virtio GPU driver" - depends on DRM && VIRTIO + depends on DRM && VIRTIO && MMU select DRM_KMS_HELPER select DRM_TTM help diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 58048709c34e..fad5a1cc5903 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -88,12 +88,13 @@ virtio_gpu_framebuffer_init(struct drm_device *dev, bo = gem_to_virtio_gpu_obj(obj); + drm_helper_mode_fill_fb_struct(dev, &vgfb->base, mode_cmd); + ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs); if (ret) { vgfb->obj = NULL; return ret; } - drm_helper_mode_fill_fb_struct(&vgfb->base, mode_cmd); spin_lock_init(&vgfb->dirty_lock); vgfb->x1 = vgfb->y1 = INT_MAX; diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 3b97d50fd392..43e1d5916c6c 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -83,10 +83,6 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) if (ret) goto err_free; - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, - driver->major, driver->minor, driver->patchlevel, - driver->date, dev->primary->index); - return 0; err_free: diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 08906c8ce3fa..2f766735c16d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -35,6 +35,7 @@ #include <drm/drm_gem.h> #include <drm/drm_atomic.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> #include <ttm/ttm_bo_api.h> #include <ttm/ttm_bo_driver.h> #include <ttm/ttm_placement.h> @@ -214,7 +215,7 @@ extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS]; /* virtio_kms.c */ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags); -int virtio_gpu_driver_unload(struct drm_device *dev); +void virtio_gpu_driver_unload(struct drm_device *dev); int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file); diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c index cde9f3758106..163a67db8cf1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fb.c +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c @@ -43,7 +43,7 @@ static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb, struct drm_device *dev = fb->base.dev; struct virtio_gpu_device *vgdev = dev->dev_private; bool store_for_later = false; - int bpp = fb->base.bits_per_pixel / 8; + int bpp = fb->base.format->cpp[0]; int x2, y2; unsigned long flags; struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->obj); @@ -333,7 +333,7 @@ static int virtio_gpufb_create(struct drm_fb_helper *helper, info->screen_buffer = obj->vmap; info->screen_size = obj->gem_base.size; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &vfbdev->helper, sizes->fb_width, sizes->fb_height); @@ -387,7 +387,6 @@ int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev) drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper, &virtio_gpu_fb_helper_funcs); ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper, - vgdev->num_scanouts, VIRTIO_GPUFB_CONN_LIMIT); if (ret) { kfree(vgfbdev); diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 1235519853f4..491866865c33 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -166,13 +166,17 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) INIT_WORK(&vgdev->config_changed_work, virtio_gpu_config_changed_work_func); +#ifdef __LITTLE_ENDIAN if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_VIRGL)) vgdev->has_virgl_3d = true; DRM_INFO("virgl 3d acceleration %s\n", - vgdev->has_virgl_3d ? "enabled" : "not available"); + vgdev->has_virgl_3d ? "enabled" : "not supported by host"); +#else + DRM_INFO("virgl 3d acceleration not supported by guest\n"); +#endif ret = vgdev->vdev->config->find_vqs(vgdev->vdev, 2, vqs, - callbacks, names); + callbacks, names, NULL); if (ret) { DRM_ERROR("failed to find virt queues\n"); goto err_vqs; @@ -246,7 +250,7 @@ static void virtio_gpu_cleanup_cap_cache(struct virtio_gpu_device *vgdev) } } -int virtio_gpu_driver_unload(struct drm_device *dev) +void virtio_gpu_driver_unload(struct drm_device *dev) { struct virtio_gpu_device *vgdev = dev->dev_private; @@ -262,7 +266,6 @@ int virtio_gpu_driver_unload(struct drm_device *dev) virtio_gpu_cleanup_cap_cache(vgdev); kfree(vgdev->capsets); kfree(vgdev); - return 0; } int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) diff --git a/drivers/gpu/drm/virtio/virtgpu_ttm.c b/drivers/gpu/drm/virtio/virtgpu_ttm.c index 4a1de9f81193..70ec8ca8d9b1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ttm.c +++ b/drivers/gpu/drm/virtio/virtgpu_ttm.c @@ -114,18 +114,17 @@ static void virtio_gpu_ttm_global_fini(struct virtio_gpu_device *vgdev) static struct vm_operations_struct virtio_gpu_ttm_vm_ops; static const struct vm_operations_struct *ttm_vm_ops; -static int virtio_gpu_ttm_fault(struct vm_area_struct *vma, - struct vm_fault *vmf) +static int virtio_gpu_ttm_fault(struct vm_fault *vmf) { struct ttm_buffer_object *bo; struct virtio_gpu_device *vgdev; int r; - bo = (struct ttm_buffer_object *)vma->vm_private_data; + bo = (struct ttm_buffer_object *)vmf->vma->vm_private_data; if (bo == NULL) return VM_FAULT_NOPAGE; vgdev = virtio_gpu_get_vgdev(bo->bdev); - r = ttm_vm_ops->fault(vma, vmf); + r = ttm_vm_ops->fault(vmf); return r; } #endif @@ -198,11 +197,11 @@ static void ttm_bo_man_debug(struct ttm_mem_type_manager *man, } static const struct ttm_mem_type_manager_func virtio_gpu_bo_manager_func = { - ttm_bo_man_init, - ttm_bo_man_takedown, - ttm_bo_man_get_node, - ttm_bo_man_put_node, - ttm_bo_man_debug + .init = ttm_bo_man_init, + .takedown = ttm_bo_man_takedown, + .get_node = ttm_bo_man_get_node, + .put_node = ttm_bo_man_put_node, + .debug = ttm_bo_man_debug }; static int virtio_gpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, @@ -386,6 +385,7 @@ static int virtio_gpu_bo_move(struct ttm_buffer_object *bo, } static void virtio_gpu_bo_move_notify(struct ttm_buffer_object *tbo, + bool evict, struct ttm_mem_reg *new_mem) { struct virtio_gpu_object *bo; @@ -433,8 +433,6 @@ static struct ttm_bo_driver virtio_gpu_bo_driver = { .io_mem_free = &virtio_gpu_ttm_io_mem_free, .move_notify = &virtio_gpu_bo_move_notify, .swap_notify = &virtio_gpu_bo_swap_notify, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; int virtio_gpu_ttm_init(struct virtio_gpu_device *vgdev) diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig index fb7b82aad763..8c308dac99c5 100644 --- a/drivers/gpu/drm/vmwgfx/Kconfig +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -1,6 +1,6 @@ config DRM_VMWGFX tristate "DRM driver for VMware Virtual GPU" - depends on DRM && PCI && X86 + depends on DRM && PCI && X86 && MMU select FB_DEFERRED_IO select FB_CFB_FILLRECT select FB_CFB_COPYAREA diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h index 531d22025fec..babe7cb84fc2 100644 --- a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h @@ -980,6 +980,8 @@ svga3dsurface_get_mip_size(surf_size_struct base_level, u32 mip_level) size.width = max_t(u32, base_level.width >> mip_level, 1); size.height = max_t(u32, base_level.height >> mip_level, 1); size.depth = max_t(u32, base_level.depth >> mip_level, 1); + size.pad64 = 0; + return size; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index c894a48a74a6..4c7f24a67a2e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -825,6 +825,7 @@ static int vmw_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) * (currently only resources). */ static void vmw_move_notify(struct ttm_buffer_object *bo, + bool evict, struct ttm_mem_reg *mem) { vmw_resource_move_notify(bo, mem); @@ -839,7 +840,7 @@ static void vmw_move_notify(struct ttm_buffer_object *bo, */ static void vmw_swap_notify(struct ttm_buffer_object *bo) { - ttm_bo_wait(bo, false, false); + (void) ttm_bo_wait(bo, false, false); } @@ -858,6 +859,4 @@ struct ttm_bo_driver vmw_bo_driver = { .fault_reserve_notify = &vmw_ttm_fault_reserve_notify, .io_mem_reserve = &vmw_ttm_io_mem_reserve, .io_mem_free = &vmw_ttm_io_mem_free, - .lru_tail = &ttm_bo_default_lru_tail, - .swap_lru_tail = &ttm_bo_default_swap_lru_tail, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c index aa04fb0159a7..77cb7c627e09 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -673,16 +673,10 @@ static bool vmw_cmdbuf_try_alloc(struct vmw_cmdbuf_man *man, memset(info->node, 0, sizeof(*info->node)); spin_lock_bh(&man->lock); - ret = drm_mm_insert_node_generic(&man->mm, info->node, info->page_size, - 0, 0, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + ret = drm_mm_insert_node(&man->mm, info->node, info->page_size); if (ret) { vmw_cmdbuf_man_process(man); - ret = drm_mm_insert_node_generic(&man->mm, info->node, - info->page_size, 0, 0, - DRM_MM_SEARCH_DEFAULT, - DRM_MM_CREATE_DEFAULT); + ret = drm_mm_insert_node(&man->mm, info->node, info->page_size); } spin_unlock_bh(&man->lock); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 18061a4bc2f2..d08f26973d0b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -199,9 +199,14 @@ static const struct drm_ioctl_desc vmw_ioctls[] = { VMW_IOCTL_DEF(VMW_PRESENT_READBACK, vmw_present_readback_ioctl, DRM_MASTER | DRM_AUTH), + /* + * The permissions of the below ioctl are overridden in + * vmw_generic_ioctl(). We require either + * DRM_MASTER or capable(CAP_SYS_ADMIN). + */ VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT, vmw_kms_update_layout_ioctl, - DRM_MASTER | DRM_CONTROL_ALLOW), + DRM_RENDER_ALLOW), VMW_IOCTL_DEF(VMW_CREATE_SHADER, vmw_shader_define_ioctl, DRM_AUTH | DRM_RENDER_ALLOW), @@ -951,7 +956,7 @@ out_err0: return ret; } -static int vmw_driver_unload(struct drm_device *dev) +static void vmw_driver_unload(struct drm_device *dev) { struct vmw_private *dev_priv = vmw_priv(dev); enum vmw_res_type i; @@ -998,8 +1003,6 @@ static int vmw_driver_unload(struct drm_device *dev) idr_destroy(&dev_priv->res_idr[i]); kfree(dev_priv); - - return 0; } static void vmw_postclose(struct drm_device *dev, @@ -1125,6 +1128,10 @@ static long vmw_generic_ioctl(struct file *filp, unsigned int cmd, return (long) vmw_execbuf_ioctl(dev, arg, file_priv, _IOC_SIZE(cmd)); + } else if (nr == DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT) { + if (!drm_is_current_master(file_priv) && + !capable(CAP_SYS_ADMIN)) + return -EACCES; } if (unlikely(ioctl->cmd != cmd)) @@ -1295,7 +1302,7 @@ static void __vmw_svga_enable(struct vmw_private *dev_priv) */ void vmw_svga_enable(struct vmw_private *dev_priv) { - ttm_read_lock(&dev_priv->reservation_sem, false); + (void) ttm_read_lock(&dev_priv->reservation_sem, false); __vmw_svga_enable(dev_priv); ttm_read_unlock(&dev_priv->reservation_sem); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 1e59a486bba8..59ff4197173a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -41,9 +41,9 @@ #include <drm/ttm/ttm_module.h> #include "vmwgfx_fence.h" -#define VMWGFX_DRIVER_DATE "20160210" +#define VMWGFX_DRIVER_DATE "20170221" #define VMWGFX_DRIVER_MAJOR 2 -#define VMWGFX_DRIVER_MINOR 11 +#define VMWGFX_DRIVER_MINOR 12 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 7a96798b9c0a..e9005b9a5e8c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -83,7 +83,7 @@ static int vmw_fb_setcolreg(unsigned regno, unsigned red, unsigned green, return 1; } - switch (par->set_fb->depth) { + switch (par->set_fb->format->depth) { case 24: case 32: pal[regno] = ((red & 0xff00) << 8) | @@ -91,8 +91,9 @@ static int vmw_fb_setcolreg(unsigned regno, unsigned red, unsigned green, ((blue & 0xff00) >> 8); break; default: - DRM_ERROR("Bad depth %u, bpp %u.\n", par->set_fb->depth, - par->set_fb->bits_per_pixel); + DRM_ERROR("Bad depth %u, bpp %u.\n", + par->set_fb->format->depth, + par->set_fb->format->cpp[0] * 8); return 1; } @@ -197,7 +198,7 @@ static void vmw_fb_dirty_flush(struct work_struct *work) * Handle panning when copying from vmalloc to framebuffer. * Clip dirty area to framebuffer. */ - cpp = (cur_fb->bits_per_pixel + 7) / 8; + cpp = cur_fb->format->cpp[0]; max_x = par->fb_x + cur_fb->width; max_y = par->fb_y + cur_fb->height; @@ -486,7 +487,7 @@ static int vmw_fb_kms_framebuffer(struct fb_info *info) cur_fb = par->set_fb; if (cur_fb && cur_fb->width == mode_cmd.width && cur_fb->height == mode_cmd.height && - cur_fb->pixel_format == mode_cmd.pixel_format && + cur_fb->format->format == mode_cmd.pixel_format && cur_fb->pitches[0] == mode_cmd.pitches[0]) return 0; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index 170b61be1e4e..fec7348cea2c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -164,9 +164,9 @@ static void vmw_gmrid_man_debug(struct ttm_mem_type_manager *man, } const struct ttm_mem_type_manager_func vmw_gmrid_manager_func = { - vmw_gmrid_man_init, - vmw_gmrid_man_takedown, - vmw_gmrid_man_get_node, - vmw_gmrid_man_put_node, - vmw_gmrid_man_debug + .init = vmw_gmrid_man_init, + .takedown = vmw_gmrid_man_takedown, + .get_node = vmw_gmrid_man_get_node, + .put_node = vmw_gmrid_man_put_node, + .debug = vmw_gmrid_man_debug }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index e7daf59bac80..d492d57d5309 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -583,7 +583,7 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, goto out_err1; } - drm_helper_mode_fill_fb_struct(&vfbs->base.base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd); vfbs->surface = vmw_surface_reference(surface); vfbs->base.user_handle = mode_cmd->handles[0]; vfbs->is_dmabuf_proxy = is_dmabuf_proxy; @@ -757,7 +757,7 @@ static int vmw_create_dmabuf_proxy(struct drm_device *dev, struct vmw_surface **srf_out) { uint32_t format; - struct drm_vmw_size content_base_size; + struct drm_vmw_size content_base_size = {0}; struct vmw_resource *res; unsigned int bytes_pp; struct drm_format_name_buf format_name; @@ -864,7 +864,7 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, goto out_err1; } - drm_helper_mode_fill_fb_struct(&vfbd->base.base, mode_cmd); + drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd); vfbd->base.dmabuf = true; vfbd->buffer = vmw_dmabuf_reference(dmabuf); vfbd->base.user_handle = mode_cmd->handles[0]; @@ -1671,7 +1671,7 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, * 1. Bounding box (assuming 32bpp) must be < prim_bb_mem * 2. Total pixels (assuming 32bpp) must be < prim_bb_mem */ - u64 bb_mem = bounding_box.w * bounding_box.h * 4; + u64 bb_mem = (u64) bounding_box.w * bounding_box.h * 4; u64 pixel_mem = total_pixels * 4; if (bb_mem > dev_priv->prim_bb_mem) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index f42ce9a1c3ac..cb36e1d70133 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -30,6 +30,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_encoder.h> #include "vmwgfx_drv.h" /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 23ec673d5e16..3806148e1bdb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -97,7 +97,8 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) fb = entry->base.crtc.primary->fb; return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0], - fb->bits_per_pixel, fb->depth); + fb->format->cpp[0] * 8, + fb->format->depth); } if (!list_empty(&lds->active)) { @@ -105,7 +106,7 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) fb = entry->base.crtc.primary->fb; vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0], - fb->bits_per_pixel, fb->depth); + fb->format->cpp[0] * 8, fb->format->depth); } /* Make sure we always show something. */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c index b6126a5f1269..941bcfd131ff 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c @@ -319,18 +319,17 @@ int vmw_otables_setup(struct vmw_private *dev_priv) int ret; if (dev_priv->has_dx) { - *otables = kmalloc(sizeof(dx_tables), GFP_KERNEL); + *otables = kmemdup(dx_tables, sizeof(dx_tables), GFP_KERNEL); if (*otables == NULL) return -ENOMEM; - memcpy(*otables, dx_tables, sizeof(dx_tables)); dev_priv->otable_batch.num_otables = ARRAY_SIZE(dx_tables); } else { - *otables = kmalloc(sizeof(pre_dx_tables), GFP_KERNEL); + *otables = kmemdup(pre_dx_tables, sizeof(pre_dx_tables), + GFP_KERNEL); if (*otables == NULL) return -ENOMEM; - memcpy(*otables, pre_dx_tables, sizeof(pre_dx_tables)); dev_priv->otable_batch.num_otables = ARRAY_SIZE(pre_dx_tables); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 8e86d6d4141b..65b3f0369636 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -1760,7 +1760,7 @@ void vmw_resource_unpin(struct vmw_resource *res) struct vmw_private *dev_priv = res->dev_priv; int ret; - ttm_read_lock(&dev_priv->reservation_sem, false); + (void) ttm_read_lock(&dev_priv->reservation_sem, false); mutex_lock(&dev_priv->cmdbuf_mutex); ret = vmw_resource_reserve(res, false, true); @@ -1770,7 +1770,7 @@ void vmw_resource_unpin(struct vmw_resource *res) if (--res->pin_count == 0 && res->backup) { struct vmw_dma_buffer *vbo = res->backup; - ttm_bo_reserve(&vbo->base, false, false, NULL); + (void) ttm_bo_reserve(&vbo->base, false, false, NULL); vmw_bo_pin_reserved(vbo, false); ttm_bo_unreserve(&vbo->base); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index f42359084adc..d4268efc37d2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -598,7 +598,7 @@ static int do_dmabuf_define_gmrfb(struct vmw_private *dev_priv, struct vmw_dma_buffer *buf = container_of(framebuffer, struct vmw_framebuffer_dmabuf, base)->buffer; - int depth = framebuffer->base.depth; + int depth = framebuffer->base.format->depth; struct { uint32_t header; SVGAFifoCmdDefineGMRFB body; @@ -618,7 +618,7 @@ static int do_dmabuf_define_gmrfb(struct vmw_private *dev_priv, } cmd->header = SVGA_CMD_DEFINE_GMRFB; - cmd->body.format.bitsPerPixel = framebuffer->base.bits_per_pixel; + cmd->body.format.bitsPerPixel = framebuffer->base.format->cpp[0] * 8; cmd->body.format.colorDepth = depth; cmd->body.format.reserved = 0; cmd->body.bytesPerLine = framebuffer->base.pitches[0]; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 94ad8d2acf9a..b27cd18ee66a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -424,7 +424,7 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, */ if (new_content_type == SEPARATE_DMA) { - switch (new_fb->bits_per_pixel) { + switch (new_fb->format->cpp[0] * 8) { case 32: content_srf.format = SVGA3D_X8R8G8B8; break; diff --git a/drivers/gpu/drm/zte/Kconfig b/drivers/gpu/drm/zte/Kconfig index 4065b2840f1c..5b36421ef3e5 100644 --- a/drivers/gpu/drm/zte/Kconfig +++ b/drivers/gpu/drm/zte/Kconfig @@ -4,5 +4,7 @@ config DRM_ZTE select DRM_KMS_CMA_HELPER select DRM_KMS_FB_HELPER select DRM_KMS_HELPER + select SND_SOC_HDMI_CODEC if SND_SOC + select VIDEOMODE_HELPERS help Choose this option to enable DRM on ZTE ZX SoCs. diff --git a/drivers/gpu/drm/zte/Makefile b/drivers/gpu/drm/zte/Makefile index 699180bfd57c..01352b56c418 100644 --- a/drivers/gpu/drm/zte/Makefile +++ b/drivers/gpu/drm/zte/Makefile @@ -2,6 +2,7 @@ zxdrm-y := \ zx_drm_drv.o \ zx_hdmi.o \ zx_plane.o \ + zx_tvenc.o \ zx_vou.o obj-$(CONFIG_DRM_ZTE) += zxdrm.o diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index 3e76f72c92ff..5c6944a1e72c 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -141,7 +141,7 @@ static int zx_drm_bind(struct device *dev) drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); - priv->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + priv->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); if (IS_ERR(priv->fbdev)) { ret = PTR_ERR(priv->fbdev); @@ -247,6 +247,7 @@ static struct platform_driver zx_drm_platform_driver = { static struct platform_driver *drivers[] = { &zx_crtc_driver, &zx_hdmi_driver, + &zx_tvenc_driver, &zx_drm_platform_driver, }; diff --git a/drivers/gpu/drm/zte/zx_drm_drv.h b/drivers/gpu/drm/zte/zx_drm_drv.h index e65cd18a6cba..5ca035b079c7 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.h +++ b/drivers/gpu/drm/zte/zx_drm_drv.h @@ -13,6 +13,7 @@ extern struct platform_driver zx_crtc_driver; extern struct platform_driver zx_hdmi_driver; +extern struct platform_driver zx_tvenc_driver; static inline u32 zx_readl(void __iomem *reg) { diff --git a/drivers/gpu/drm/zte/zx_hdmi.c b/drivers/gpu/drm/zte/zx_hdmi.c index 6bf6c364811e..c47b9cbfe270 100644 --- a/drivers/gpu/drm/zte/zx_hdmi.c +++ b/drivers/gpu/drm/zte/zx_hdmi.c @@ -25,6 +25,8 @@ #include <drm/drm_of.h> #include <drm/drmP.h> +#include <sound/hdmi-codec.h> + #include "zx_hdmi_regs.h" #include "zx_vou.h" @@ -49,17 +51,11 @@ struct zx_hdmi { bool sink_is_hdmi; bool sink_has_audio; const struct vou_inf *inf; + struct platform_device *audio_pdev; }; #define to_zx_hdmi(x) container_of(x, struct zx_hdmi, x) -static const struct vou_inf vou_inf_hdmi = { - .id = VOU_HDMI, - .data_sel = VOU_YUV444, - .clocks_en_bits = BIT(24) | BIT(18) | BIT(6), - .clocks_sel_bits = BIT(13) | BIT(2), -}; - static inline u8 hdmi_readb(struct zx_hdmi *hdmi, u16 offset) { return readl_relaxed(hdmi->mmio + offset * 4); @@ -238,14 +234,14 @@ static void zx_hdmi_encoder_enable(struct drm_encoder *encoder) zx_hdmi_hw_enable(hdmi); - vou_inf_enable(hdmi->inf, encoder->crtc); + vou_inf_enable(VOU_HDMI, encoder->crtc); } static void zx_hdmi_encoder_disable(struct drm_encoder *encoder) { struct zx_hdmi *hdmi = to_zx_hdmi(encoder); - vou_inf_disable(hdmi->inf, encoder->crtc); + vou_inf_disable(VOU_HDMI, encoder->crtc); zx_hdmi_hw_disable(hdmi); @@ -366,6 +362,142 @@ static irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static int zx_hdmi_audio_startup(struct device *dev, void *data) +{ + struct zx_hdmi *hdmi = dev_get_drvdata(dev); + struct drm_encoder *encoder = &hdmi->encoder; + + vou_inf_hdmi_audio_sel(encoder->crtc, VOU_HDMI_AUD_SPDIF); + + return 0; +} + +static void zx_hdmi_audio_shutdown(struct device *dev, void *data) +{ + struct zx_hdmi *hdmi = dev_get_drvdata(dev); + + /* Disable audio input */ + hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, 0); +} + +static inline int zx_hdmi_audio_get_n(unsigned int fs) +{ + unsigned int n; + + if (fs && (fs % 44100) == 0) + n = 6272 * (fs / 44100); + else + n = fs * 128 / 1000; + + return n; +} + +static int zx_hdmi_audio_hw_params(struct device *dev, + void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct zx_hdmi *hdmi = dev_get_drvdata(dev); + struct hdmi_audio_infoframe *cea = ¶ms->cea; + union hdmi_infoframe frame; + int n; + + /* We only support spdif for now */ + if (daifmt->fmt != HDMI_SPDIF) { + DRM_DEV_ERROR(hdmi->dev, "invalid daifmt %d\n", daifmt->fmt); + return -EINVAL; + } + + switch (params->sample_width) { + case 16: + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, + SPDIF_SAMPLE_SIZE_16BIT); + break; + case 20: + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, + SPDIF_SAMPLE_SIZE_20BIT); + break; + case 24: + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, + SPDIF_SAMPLE_SIZE_24BIT); + break; + default: + DRM_DEV_ERROR(hdmi->dev, "invalid sample width %d\n", + params->sample_width); + return -EINVAL; + } + + /* CTS is calculated by hardware, and we only need to take care of N */ + n = zx_hdmi_audio_get_n(params->sample_rate); + hdmi_writeb(hdmi, N_SVAL1, n & 0xff); + hdmi_writeb(hdmi, N_SVAL2, (n >> 8) & 0xff); + hdmi_writeb(hdmi, N_SVAL3, (n >> 16) & 0xf); + + /* Enable spdif mode */ + hdmi_writeb_mask(hdmi, AUD_MODE, SPDIF_EN, SPDIF_EN); + + /* Enable audio input */ + hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, AUD_IN_EN); + + memcpy(&frame.audio, cea, sizeof(*cea)); + + return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AUDIO); +} + +static int zx_hdmi_audio_digital_mute(struct device *dev, void *data, + bool enable) +{ + struct zx_hdmi *hdmi = dev_get_drvdata(dev); + + if (enable) + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, + TPI_AUD_MUTE); + else + hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, 0); + + return 0; +} + +static int zx_hdmi_audio_get_eld(struct device *dev, void *data, + uint8_t *buf, size_t len) +{ + struct zx_hdmi *hdmi = dev_get_drvdata(dev); + struct drm_connector *connector = &hdmi->connector; + + memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); + + return 0; +} + +static const struct hdmi_codec_ops zx_hdmi_codec_ops = { + .audio_startup = zx_hdmi_audio_startup, + .hw_params = zx_hdmi_audio_hw_params, + .audio_shutdown = zx_hdmi_audio_shutdown, + .digital_mute = zx_hdmi_audio_digital_mute, + .get_eld = zx_hdmi_audio_get_eld, +}; + +static struct hdmi_codec_pdata zx_hdmi_codec_pdata = { + .ops = &zx_hdmi_codec_ops, + .spdif = 1, +}; + +static int zx_hdmi_audio_register(struct zx_hdmi *hdmi) +{ + struct platform_device *pdev; + + pdev = platform_device_register_data(hdmi->dev, HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &zx_hdmi_codec_pdata, + sizeof(zx_hdmi_codec_pdata)); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + hdmi->audio_pdev = pdev; + + return 0; +} + static int zx_hdmi_i2c_read(struct zx_hdmi *hdmi, struct i2c_msg *msg) { int len = msg->len; @@ -523,7 +655,6 @@ static int zx_hdmi_bind(struct device *dev, struct device *master, void *data) hdmi->dev = dev; hdmi->drm = drm; - hdmi->inf = &vou_inf_hdmi; dev_set_drvdata(dev, hdmi); @@ -566,6 +697,12 @@ static int zx_hdmi_bind(struct device *dev, struct device *master, void *data) return ret; } + ret = zx_hdmi_audio_register(hdmi); + if (ret) { + DRM_DEV_ERROR(dev, "failed to register audio: %d\n", ret); + return ret; + } + ret = zx_hdmi_register(drm, hdmi); if (ret) { DRM_DEV_ERROR(dev, "failed to register hdmi: %d\n", ret); @@ -590,6 +727,9 @@ static void zx_hdmi_unbind(struct device *dev, struct device *master, hdmi->connector.funcs->destroy(&hdmi->connector); hdmi->encoder.funcs->destroy(&hdmi->encoder); + + if (hdmi->audio_pdev) + platform_device_unregister(hdmi->audio_pdev); } static const struct component_ops zx_hdmi_component_ops = { diff --git a/drivers/gpu/drm/zte/zx_hdmi_regs.h b/drivers/gpu/drm/zte/zx_hdmi_regs.h index de911f66b658..c6d5d8211725 100644 --- a/drivers/gpu/drm/zte/zx_hdmi_regs.h +++ b/drivers/gpu/drm/zte/zx_hdmi_regs.h @@ -52,5 +52,19 @@ #define TPI_INFO_TRANS_RPT BIT(6) #define TPI_DDC_MASTER_EN 0x06f8 #define HW_DDC_MASTER BIT(7) +#define N_SVAL1 0xa03 +#define N_SVAL2 0xa04 +#define N_SVAL3 0xa05 +#define AUD_EN 0xa13 +#define AUD_IN_EN BIT(0) +#define AUD_MODE 0xa14 +#define SPDIF_EN BIT(1) +#define TPI_AUD_CONFIG 0xa62 +#define SPDIF_SAMPLE_SIZE_SHIFT 6 +#define SPDIF_SAMPLE_SIZE_MASK (0x3 << SPDIF_SAMPLE_SIZE_SHIFT) +#define SPDIF_SAMPLE_SIZE_16BIT (0x1 << SPDIF_SAMPLE_SIZE_SHIFT) +#define SPDIF_SAMPLE_SIZE_20BIT (0x2 << SPDIF_SAMPLE_SIZE_SHIFT) +#define SPDIF_SAMPLE_SIZE_24BIT (0x3 << SPDIF_SAMPLE_SIZE_SHIFT) +#define TPI_AUD_MUTE BIT(4) #endif /* __ZX_HDMI_REGS_H__ */ diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c index 546eb92a94e8..d646ac931663 100644 --- a/drivers/gpu/drm/zte/zx_plane.c +++ b/drivers/gpu/drm/zte/zx_plane.c @@ -21,16 +21,6 @@ #include "zx_plane_regs.h" #include "zx_vou.h" -struct zx_plane { - struct drm_plane plane; - void __iomem *layer; - void __iomem *csc; - void __iomem *hbsc; - void __iomem *rsz; -}; - -#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane) - static const uint32_t gl_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888, @@ -40,6 +30,261 @@ static const uint32_t gl_formats[] = { DRM_FORMAT_ARGB4444, }; +static const uint32_t vl_formats[] = { + DRM_FORMAT_NV12, /* Semi-planar YUV420 */ + DRM_FORMAT_YUV420, /* Planar YUV420 */ + DRM_FORMAT_YUYV, /* Packed YUV422 */ + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUV444, /* YUV444 8bit */ + /* + * TODO: add formats below that HW supports: + * - YUV420 P010 + * - YUV420 Hantro + * - YUV444 10bit + */ +}; + +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + +static int zx_vl_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + struct drm_framebuffer *fb = plane_state->fb; + struct drm_crtc *crtc = plane_state->crtc; + struct drm_crtc_state *crtc_state; + struct drm_rect clip; + int min_scale = FRAC_16_16(1, 8); + int max_scale = FRAC_16_16(8, 1); + + if (!crtc || !fb) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state, + crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + /* nothing to check when disabling or disabled */ + if (!crtc_state->enable) + return 0; + + /* plane must be enabled */ + if (!plane_state->crtc) + return -EINVAL; + + clip.x1 = 0; + clip.y1 = 0; + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + + return drm_plane_helper_check_state(plane_state, &clip, + min_scale, max_scale, + true, true); +} + +static int zx_vl_get_fmt(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_NV12: + return VL_FMT_YUV420; + case DRM_FORMAT_YUV420: + return VL_YUV420_PLANAR | VL_FMT_YUV420; + case DRM_FORMAT_YUYV: + return VL_YUV422_YUYV | VL_FMT_YUV422; + case DRM_FORMAT_YVYU: + return VL_YUV422_YVYU | VL_FMT_YUV422; + case DRM_FORMAT_UYVY: + return VL_YUV422_UYVY | VL_FMT_YUV422; + case DRM_FORMAT_VYUY: + return VL_YUV422_VYUY | VL_FMT_YUV422; + case DRM_FORMAT_YUV444: + return VL_FMT_YUV444_8BIT; + default: + WARN_ONCE(1, "invalid pixel format %d\n", format); + return -EINVAL; + } +} + +static inline void zx_vl_set_update(struct zx_plane *zplane) +{ + void __iomem *layer = zplane->layer; + + zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE); +} + +static inline void zx_vl_rsz_set_update(struct zx_plane *zplane) +{ + zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1); +} + +static int zx_vl_rsz_get_fmt(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_YUV420: + return RSZ_VL_FMT_YCBCR420; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + return RSZ_VL_FMT_YCBCR422; + case DRM_FORMAT_YUV444: + return RSZ_VL_FMT_YCBCR444; + default: + WARN_ONCE(1, "invalid pixel format %d\n", format); + return -EINVAL; + } +} + +static inline u32 rsz_step_value(u32 src, u32 dst) +{ + u32 val = 0; + + if (src == dst) + val = 0; + else if (src < dst) + val = RSZ_PARA_STEP((src << 16) / dst); + else if (src > dst) + val = RSZ_DATA_STEP(src / dst) | + RSZ_PARA_STEP(((src << 16) / dst) & 0xffff); + + return val; +} + +static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format, + u32 src_w, u32 src_h, u32 dst_w, u32 dst_h) +{ + void __iomem *rsz = zplane->rsz; + u32 src_chroma_w = src_w; + u32 src_chroma_h = src_h; + int fmt; + + /* Set up source and destination resolution */ + zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1)); + zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1)); + + /* Configure data format for VL RSZ */ + fmt = zx_vl_rsz_get_fmt(format); + if (fmt >= 0) + zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt); + + /* Calculate Chroma height and width */ + if (fmt == RSZ_VL_FMT_YCBCR420) { + src_chroma_w = src_w >> 1; + src_chroma_h = src_h >> 1; + } else if (fmt == RSZ_VL_FMT_YCBCR422) { + src_chroma_w = src_w >> 1; + } + + /* Set up Luma and Chroma step registers */ + zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w)); + zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h)); + zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w)); + zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h)); + + zx_vl_rsz_set_update(zplane); +} + +static void zx_vl_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct zx_plane *zplane = to_zx_plane(plane); + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_rect *src = &state->src; + struct drm_rect *dst = &state->dst; + struct drm_gem_cma_object *cma_obj; + void __iomem *layer = zplane->layer; + void __iomem *hbsc = zplane->hbsc; + void __iomem *paddr_reg; + dma_addr_t paddr; + u32 src_x, src_y, src_w, src_h; + u32 dst_x, dst_y, dst_w, dst_h; + uint32_t format; + int fmt; + int num_planes; + int i; + + if (!fb) + return; + + format = fb->format->format; + + src_x = src->x1 >> 16; + src_y = src->y1 >> 16; + src_w = drm_rect_width(src) >> 16; + src_h = drm_rect_height(src) >> 16; + + dst_x = dst->x1; + dst_y = dst->y1; + dst_w = drm_rect_width(dst); + dst_h = drm_rect_height(dst); + + /* Set up data address registers for Y, Cb and Cr planes */ + num_planes = drm_format_num_planes(format); + paddr_reg = layer + VL_Y; + for (i = 0; i < num_planes; i++) { + cma_obj = drm_fb_cma_get_gem_obj(fb, i); + paddr = cma_obj->paddr + fb->offsets[i]; + paddr += src_y * fb->pitches[i]; + paddr += src_x * drm_format_plane_cpp(format, i); + zx_writel(paddr_reg, paddr); + paddr_reg += 4; + } + + /* Set up source height/width register */ + zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h)); + + /* Set up start position register */ + zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y)); + + /* Set up end position register */ + zx_writel(layer + VL_POS_END, + GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h)); + + /* Strides of Cb and Cr planes should be identical */ + zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) | + CHROMA_STRIDE(fb->pitches[1])); + + /* Set up video layer data format */ + fmt = zx_vl_get_fmt(format); + if (fmt >= 0) + zx_writel(layer + VL_CTRL1, fmt); + + /* Always use scaler since it exists (set for not bypass) */ + zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE, + VL_SCALER_BYPASS_MODE); + + zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h); + + /* Enable HBSC block */ + zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN); + + zx_vou_layer_enable(plane); + + zx_vl_set_update(zplane); +} + +static void zx_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct zx_plane *zplane = to_zx_plane(plane); + void __iomem *hbsc = zplane->hbsc; + + zx_vou_layer_disable(plane); + + /* Disable HBSC block */ + zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0); +} + +static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = { + .atomic_check = zx_vl_plane_atomic_check, + .atomic_update = zx_vl_plane_atomic_update, + .atomic_disable = zx_plane_atomic_disable, +}; + static int zx_gl_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *plane_state) { @@ -107,14 +352,6 @@ static inline void zx_gl_rsz_set_update(struct zx_plane *zplane) zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1); } -void zx_plane_set_update(struct drm_plane *plane) -{ - struct zx_plane *zplane = to_zx_plane(plane); - - zx_gl_rsz_set_update(zplane); - zx_gl_set_update(zplane); -} - static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h, u32 dst_w, u32 dst_h) { @@ -146,7 +383,7 @@ static void zx_gl_plane_atomic_update(struct drm_plane *plane, if (!fb) return; - format = fb->pixel_format; + format = fb->format->format; stride = fb->pitches[0]; src_x = plane->state->src_x >> 16; @@ -159,7 +396,7 @@ static void zx_gl_plane_atomic_update(struct drm_plane *plane, dst_w = plane->state->crtc_w; dst_h = plane->state->crtc_h; - bpp = drm_format_plane_cpp(format, 0); + bpp = fb->format->cpp[0]; cma_obj = drm_fb_cma_get_gem_obj(fb, 0); paddr = cma_obj->paddr + fb->offsets[0]; @@ -207,12 +444,15 @@ static void zx_gl_plane_atomic_update(struct drm_plane *plane, /* Enable HBSC block */ zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN); + zx_vou_layer_enable(plane); + zx_gl_set_update(zplane); } static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = { .atomic_check = zx_gl_plane_atomic_check, .atomic_update = zx_gl_plane_atomic_update, + .atomic_disable = zx_plane_atomic_disable, }; static void zx_plane_destroy(struct drm_plane *plane) @@ -230,6 +470,28 @@ static const struct drm_plane_funcs zx_plane_funcs = { .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; +void zx_plane_set_update(struct drm_plane *plane) +{ + struct zx_plane *zplane = to_zx_plane(plane); + + /* Do nothing if the plane is not enabled */ + if (!plane->state->crtc) + return; + + switch (plane->type) { + case DRM_PLANE_TYPE_PRIMARY: + zx_gl_rsz_set_update(zplane); + zx_gl_set_update(zplane); + break; + case DRM_PLANE_TYPE_OVERLAY: + zx_vl_rsz_set_update(zplane); + zx_vl_set_update(zplane); + break; + default: + WARN_ONCE(1, "unsupported plane type %d\n", plane->type); + } +} + static void zx_plane_hbsc_init(struct zx_plane *zplane) { void __iomem *hbsc = zplane->hbsc; @@ -248,28 +510,16 @@ static void zx_plane_hbsc_init(struct zx_plane *zplane) zx_writel(hbsc + HBSC_THRESHOLD_COL3, (0x3c0 << 16) | 0x40); } -struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev, - struct zx_layer_data *data, - enum drm_plane_type type) +int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane, + enum drm_plane_type type) { const struct drm_plane_helper_funcs *helper; - struct zx_plane *zplane; - struct drm_plane *plane; + struct drm_plane *plane = &zplane->plane; + struct device *dev = zplane->dev; const uint32_t *formats; unsigned int format_count; int ret; - zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL); - if (!zplane) - return ERR_PTR(-ENOMEM); - - plane = &zplane->plane; - - zplane->layer = data->layer; - zplane->hbsc = data->hbsc; - zplane->csc = data->csc; - zplane->rsz = data->rsz; - zx_plane_hbsc_init(zplane); switch (type) { @@ -279,10 +529,12 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev, format_count = ARRAY_SIZE(gl_formats); break; case DRM_PLANE_TYPE_OVERLAY: - /* TODO: add video layer (vl) support */ + helper = &zx_vl_plane_helper_funcs; + formats = vl_formats; + format_count = ARRAY_SIZE(vl_formats); break; default: - return ERR_PTR(-ENODEV); + return -ENODEV; } ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK, @@ -290,10 +542,10 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev, type, NULL); if (ret) { DRM_DEV_ERROR(dev, "failed to init universal plane: %d\n", ret); - return ERR_PTR(ret); + return ret; } drm_plane_helper_add(plane, helper); - return plane; + return 0; } diff --git a/drivers/gpu/drm/zte/zx_plane.h b/drivers/gpu/drm/zte/zx_plane.h index 2b82cd558d9d..933611ddffd0 100644 --- a/drivers/gpu/drm/zte/zx_plane.h +++ b/drivers/gpu/drm/zte/zx_plane.h @@ -11,16 +11,20 @@ #ifndef __ZX_PLANE_H__ #define __ZX_PLANE_H__ -struct zx_layer_data { +struct zx_plane { + struct drm_plane plane; + struct device *dev; void __iomem *layer; void __iomem *csc; void __iomem *hbsc; void __iomem *rsz; + const struct vou_layer_bits *bits; }; -struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev, - struct zx_layer_data *data, - enum drm_plane_type type); +#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane) + +int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane, + enum drm_plane_type type); void zx_plane_set_update(struct drm_plane *plane); #endif /* __ZX_PLANE_H__ */ diff --git a/drivers/gpu/drm/zte/zx_plane_regs.h b/drivers/gpu/drm/zte/zx_plane_regs.h index 3dde6716a558..65f271aeabed 100644 --- a/drivers/gpu/drm/zte/zx_plane_regs.h +++ b/drivers/gpu/drm/zte/zx_plane_regs.h @@ -46,6 +46,37 @@ #define GL_POS_X(x) (((x) << GL_POS_X_SHIFT) & GL_POS_X_MASK) #define GL_POS_Y(x) (((x) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK) +/* VL registers */ +#define VL_CTRL0 0x00 +#define VL_UPDATE BIT(3) +#define VL_CTRL1 0x04 +#define VL_YUV420_PLANAR BIT(5) +#define VL_YUV422_SHIFT 3 +#define VL_YUV422_YUYV (0 << VL_YUV422_SHIFT) +#define VL_YUV422_YVYU (1 << VL_YUV422_SHIFT) +#define VL_YUV422_UYVY (2 << VL_YUV422_SHIFT) +#define VL_YUV422_VYUY (3 << VL_YUV422_SHIFT) +#define VL_FMT_YUV420 0 +#define VL_FMT_YUV422 1 +#define VL_FMT_YUV420_P010 2 +#define VL_FMT_YUV420_HANTRO 3 +#define VL_FMT_YUV444_8BIT 4 +#define VL_FMT_YUV444_10BIT 5 +#define VL_CTRL2 0x08 +#define VL_SCALER_BYPASS_MODE BIT(0) +#define VL_STRIDE 0x0c +#define LUMA_STRIDE_SHIFT 16 +#define LUMA_STRIDE_MASK (0xffff << LUMA_STRIDE_SHIFT) +#define CHROMA_STRIDE_SHIFT 0 +#define CHROMA_STRIDE_MASK (0xffff << CHROMA_STRIDE_SHIFT) +#define VL_SRC_SIZE 0x10 +#define VL_Y 0x14 +#define VL_POS_START 0x30 +#define VL_POS_END 0x34 + +#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK) +#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK) + /* CSC registers */ #define CSC_CTRL0 0x30 #define CSC_COV_MODE_SHIFT 16 @@ -69,6 +100,18 @@ #define RSZ_DEST_CFG 0x04 #define RSZ_ENABLE_CFG 0x14 +#define RSZ_VL_LUMA_HOR 0x08 +#define RSZ_VL_LUMA_VER 0x0c +#define RSZ_VL_CHROMA_HOR 0x10 +#define RSZ_VL_CHROMA_VER 0x14 +#define RSZ_VL_CTRL_CFG 0x18 +#define RSZ_VL_FMT_SHIFT 3 +#define RSZ_VL_FMT_MASK (0x3 << RSZ_VL_FMT_SHIFT) +#define RSZ_VL_FMT_YCBCR420 (0x0 << RSZ_VL_FMT_SHIFT) +#define RSZ_VL_FMT_YCBCR422 (0x1 << RSZ_VL_FMT_SHIFT) +#define RSZ_VL_FMT_YCBCR444 (0x2 << RSZ_VL_FMT_SHIFT) +#define RSZ_VL_ENABLE_CFG 0x1c + #define RSZ_VER_SHIFT 16 #define RSZ_VER_MASK (0xffff << RSZ_VER_SHIFT) #define RSZ_HOR_SHIFT 0 @@ -77,6 +120,14 @@ #define RSZ_VER(x) (((x) << RSZ_VER_SHIFT) & RSZ_VER_MASK) #define RSZ_HOR(x) (((x) << RSZ_HOR_SHIFT) & RSZ_HOR_MASK) +#define RSZ_DATA_STEP_SHIFT 16 +#define RSZ_DATA_STEP_MASK (0xffff << RSZ_DATA_STEP_SHIFT) +#define RSZ_PARA_STEP_SHIFT 0 +#define RSZ_PARA_STEP_MASK (0xffff << RSZ_PARA_STEP_SHIFT) + +#define RSZ_DATA_STEP(x) (((x) << RSZ_DATA_STEP_SHIFT) & RSZ_DATA_STEP_MASK) +#define RSZ_PARA_STEP(x) (((x) << RSZ_PARA_STEP_SHIFT) & RSZ_PARA_STEP_MASK) + /* HBSC registers */ #define HBSC_SATURATION 0x00 #define HBSC_HUE 0x04 diff --git a/drivers/gpu/drm/zte/zx_tvenc.c b/drivers/gpu/drm/zte/zx_tvenc.c new file mode 100644 index 000000000000..b56dc69843fc --- /dev/null +++ b/drivers/gpu/drm/zte/zx_tvenc.c @@ -0,0 +1,407 @@ +/* + * Copyright 2017 Linaro Ltd. + * Copyright 2017 ZTE Corporation. + * + * 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/clk.h> +#include <linux/component.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drmP.h> + +#include "zx_drm_drv.h" +#include "zx_tvenc_regs.h" +#include "zx_vou.h" + +struct zx_tvenc_pwrctrl { + struct regmap *regmap; + u32 reg; + u32 mask; +}; + +struct zx_tvenc { + struct drm_connector connector; + struct drm_encoder encoder; + struct device *dev; + void __iomem *mmio; + const struct vou_inf *inf; + struct zx_tvenc_pwrctrl pwrctrl; +}; + +#define to_zx_tvenc(x) container_of(x, struct zx_tvenc, x) + +struct zx_tvenc_mode { + struct drm_display_mode mode; + u32 video_info; + u32 video_res; + u32 field1_param; + u32 field2_param; + u32 burst_line_odd1; + u32 burst_line_even1; + u32 burst_line_odd2; + u32 burst_line_even2; + u32 line_timing_param; + u32 weight_value; + u32 blank_black_level; + u32 burst_level; + u32 control_param; + u32 sub_carrier_phase1; + u32 phase_line_incr_cvbs; +}; + +/* + * The CRM cannot directly provide a suitable frequency, and we have to + * ask a multiplied rate from CRM and use the divider in VOU to get the + * desired one. + */ +#define TVENC_CLOCK_MULTIPLIER 4 + +static const struct zx_tvenc_mode tvenc_mode_pal = { + .mode = { + .clock = 13500 * TVENC_CLOCK_MULTIPLIER, + .hdisplay = 720, + .hsync_start = 720 + 12, + .hsync_end = 720 + 12 + 2, + .htotal = 720 + 12 + 2 + 130, + .vdisplay = 576, + .vsync_start = 576 + 2, + .vsync_end = 576 + 2 + 2, + .vtotal = 576 + 2 + 2 + 20, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE, + }, + .video_info = 0x00040040, + .video_res = 0x05a9c760, + .field1_param = 0x0004d416, + .field2_param = 0x0009b94f, + .burst_line_odd1 = 0x0004d406, + .burst_line_even1 = 0x0009b53e, + .burst_line_odd2 = 0x0004d805, + .burst_line_even2 = 0x0009b93f, + .line_timing_param = 0x06a96fdf, + .weight_value = 0x00c188a0, + .blank_black_level = 0x0000fcfc, + .burst_level = 0x00001595, + .control_param = 0x00000001, + .sub_carrier_phase1 = 0x1504c566, + .phase_line_incr_cvbs = 0xc068db8c, +}; + +static const struct zx_tvenc_mode tvenc_mode_ntsc = { + .mode = { + .clock = 13500 * TVENC_CLOCK_MULTIPLIER, + .hdisplay = 720, + .hsync_start = 720 + 16, + .hsync_end = 720 + 16 + 2, + .htotal = 720 + 16 + 2 + 120, + .vdisplay = 480, + .vsync_start = 480 + 3, + .vsync_end = 480 + 3 + 2, + .vtotal = 480 + 3 + 2 + 17, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE, + }, + .video_info = 0x00040080, + .video_res = 0x05a8375a, + .field1_param = 0x00041817, + .field2_param = 0x0008351e, + .burst_line_odd1 = 0x00041006, + .burst_line_even1 = 0x0008290d, + .burst_line_odd2 = 0x00000000, + .burst_line_even2 = 0x00000000, + .line_timing_param = 0x06a8ef9e, + .weight_value = 0x00b68197, + .blank_black_level = 0x0000f0f0, + .burst_level = 0x0000009c, + .control_param = 0x00000001, + .sub_carrier_phase1 = 0x10f83e10, + .phase_line_incr_cvbs = 0x80000000, +}; + +static const struct zx_tvenc_mode *tvenc_modes[] = { + &tvenc_mode_pal, + &tvenc_mode_ntsc, +}; + +static const struct zx_tvenc_mode * +zx_tvenc_find_zmode(struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) { + const struct zx_tvenc_mode *zmode = tvenc_modes[i]; + + if (drm_mode_equal(mode, &zmode->mode)) + return zmode; + } + + return NULL; +} + +static void zx_tvenc_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct zx_tvenc *tvenc = to_zx_tvenc(encoder); + const struct zx_tvenc_mode *zmode; + struct vou_div_config configs[] = { + { VOU_DIV_INF, VOU_DIV_4 }, + { VOU_DIV_TVENC, VOU_DIV_1 }, + { VOU_DIV_LAYER, VOU_DIV_2 }, + }; + + zx_vou_config_dividers(encoder->crtc, configs, ARRAY_SIZE(configs)); + + zmode = zx_tvenc_find_zmode(mode); + if (!zmode) { + DRM_DEV_ERROR(tvenc->dev, "failed to find zmode\n"); + return; + } + + zx_writel(tvenc->mmio + VENC_VIDEO_INFO, zmode->video_info); + zx_writel(tvenc->mmio + VENC_VIDEO_RES, zmode->video_res); + zx_writel(tvenc->mmio + VENC_FIELD1_PARAM, zmode->field1_param); + zx_writel(tvenc->mmio + VENC_FIELD2_PARAM, zmode->field2_param); + zx_writel(tvenc->mmio + VENC_LINE_O_1, zmode->burst_line_odd1); + zx_writel(tvenc->mmio + VENC_LINE_E_1, zmode->burst_line_even1); + zx_writel(tvenc->mmio + VENC_LINE_O_2, zmode->burst_line_odd2); + zx_writel(tvenc->mmio + VENC_LINE_E_2, zmode->burst_line_even2); + zx_writel(tvenc->mmio + VENC_LINE_TIMING_PARAM, + zmode->line_timing_param); + zx_writel(tvenc->mmio + VENC_WEIGHT_VALUE, zmode->weight_value); + zx_writel(tvenc->mmio + VENC_BLANK_BLACK_LEVEL, + zmode->blank_black_level); + zx_writel(tvenc->mmio + VENC_BURST_LEVEL, zmode->burst_level); + zx_writel(tvenc->mmio + VENC_CONTROL_PARAM, zmode->control_param); + zx_writel(tvenc->mmio + VENC_SUB_CARRIER_PHASE1, + zmode->sub_carrier_phase1); + zx_writel(tvenc->mmio + VENC_PHASE_LINE_INCR_CVBS, + zmode->phase_line_incr_cvbs); +} + +static void zx_tvenc_encoder_enable(struct drm_encoder *encoder) +{ + struct zx_tvenc *tvenc = to_zx_tvenc(encoder); + struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl; + + /* Set bit to power up TVENC DAC */ + regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, + pwrctrl->mask); + + vou_inf_enable(VOU_TV_ENC, encoder->crtc); + + zx_writel(tvenc->mmio + VENC_ENABLE, 1); +} + +static void zx_tvenc_encoder_disable(struct drm_encoder *encoder) +{ + struct zx_tvenc *tvenc = to_zx_tvenc(encoder); + struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl; + + zx_writel(tvenc->mmio + VENC_ENABLE, 0); + + vou_inf_disable(VOU_TV_ENC, encoder->crtc); + + /* Clear bit to power down TVENC DAC */ + regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0); +} + +static const struct drm_encoder_helper_funcs zx_tvenc_encoder_helper_funcs = { + .enable = zx_tvenc_encoder_enable, + .disable = zx_tvenc_encoder_disable, + .mode_set = zx_tvenc_encoder_mode_set, +}; + +static const struct drm_encoder_funcs zx_tvenc_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int zx_tvenc_connector_get_modes(struct drm_connector *connector) +{ + struct zx_tvenc *tvenc = to_zx_tvenc(connector); + struct device *dev = tvenc->dev; + int i; + + for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) { + const struct zx_tvenc_mode *zmode = tvenc_modes[i]; + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &zmode->mode); + if (!mode) { + DRM_DEV_ERROR(dev, "failed to duplicate drm mode\n"); + continue; + } + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + } + + return i; +} + +static enum drm_mode_status +zx_tvenc_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct zx_tvenc *tvenc = to_zx_tvenc(connector); + const struct zx_tvenc_mode *zmode; + + zmode = zx_tvenc_find_zmode(mode); + if (!zmode) { + DRM_DEV_ERROR(tvenc->dev, "unsupported mode: %s\n", mode->name); + return MODE_NOMODE; + } + + return MODE_OK; +} + +static struct drm_connector_helper_funcs zx_tvenc_connector_helper_funcs = { + .get_modes = zx_tvenc_connector_get_modes, + .mode_valid = zx_tvenc_connector_mode_valid, +}; + +static const struct drm_connector_funcs zx_tvenc_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int zx_tvenc_register(struct drm_device *drm, struct zx_tvenc *tvenc) +{ + struct drm_encoder *encoder = &tvenc->encoder; + struct drm_connector *connector = &tvenc->connector; + + /* + * The tvenc is designed to use aux channel, as there is a deflicker + * block for the channel. + */ + encoder->possible_crtcs = BIT(1); + + drm_encoder_init(drm, encoder, &zx_tvenc_encoder_funcs, + DRM_MODE_ENCODER_TVDAC, NULL); + drm_encoder_helper_add(encoder, &zx_tvenc_encoder_helper_funcs); + + connector->interlace_allowed = true; + + drm_connector_init(drm, connector, &zx_tvenc_connector_funcs, + DRM_MODE_CONNECTOR_Composite); + drm_connector_helper_add(connector, &zx_tvenc_connector_helper_funcs); + + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; +} + +static int zx_tvenc_pwrctrl_init(struct zx_tvenc *tvenc) +{ + struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl; + struct device *dev = tvenc->dev; + struct of_phandle_args out_args; + struct regmap *regmap; + int ret; + + ret = of_parse_phandle_with_fixed_args(dev->of_node, + "zte,tvenc-power-control", 2, 0, &out_args); + if (ret) + return ret; + + regmap = syscon_node_to_regmap(out_args.np); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto out; + } + + pwrctrl->regmap = regmap; + pwrctrl->reg = out_args.args[0]; + pwrctrl->mask = out_args.args[1]; + +out: + of_node_put(out_args.np); + return ret; +} + +static int zx_tvenc_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = data; + struct resource *res; + struct zx_tvenc *tvenc; + int ret; + + tvenc = devm_kzalloc(dev, sizeof(*tvenc), GFP_KERNEL); + if (!tvenc) + return -ENOMEM; + + tvenc->dev = dev; + dev_set_drvdata(dev, tvenc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tvenc->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(tvenc->mmio)) { + ret = PTR_ERR(tvenc->mmio); + DRM_DEV_ERROR(dev, "failed to remap tvenc region: %d\n", ret); + return ret; + } + + ret = zx_tvenc_pwrctrl_init(tvenc); + if (ret) { + DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret); + return ret; + } + + ret = zx_tvenc_register(drm, tvenc); + if (ret) { + DRM_DEV_ERROR(dev, "failed to register tvenc: %d\n", ret); + return ret; + } + + return 0; +} + +static void zx_tvenc_unbind(struct device *dev, struct device *master, + void *data) +{ + /* Nothing to do */ +} + +static const struct component_ops zx_tvenc_component_ops = { + .bind = zx_tvenc_bind, + .unbind = zx_tvenc_unbind, +}; + +static int zx_tvenc_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &zx_tvenc_component_ops); +} + +static int zx_tvenc_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &zx_tvenc_component_ops); + return 0; +} + +static const struct of_device_id zx_tvenc_of_match[] = { + { .compatible = "zte,zx296718-tvenc", }, + { /* end */ }, +}; +MODULE_DEVICE_TABLE(of, zx_tvenc_of_match); + +struct platform_driver zx_tvenc_driver = { + .probe = zx_tvenc_probe, + .remove = zx_tvenc_remove, + .driver = { + .name = "zx-tvenc", + .of_match_table = zx_tvenc_of_match, + }, +}; diff --git a/drivers/gpu/drm/zte/zx_tvenc_regs.h b/drivers/gpu/drm/zte/zx_tvenc_regs.h new file mode 100644 index 000000000000..bd91f5dcc1f3 --- /dev/null +++ b/drivers/gpu/drm/zte/zx_tvenc_regs.h @@ -0,0 +1,31 @@ +/* + * Copyright 2017 Linaro Ltd. + * Copyright 2017 ZTE Corporation. + * + * 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 __ZX_TVENC_REGS_H__ +#define __ZX_TVENC_REGS_H__ + +#define VENC_VIDEO_INFO 0x04 +#define VENC_VIDEO_RES 0x08 +#define VENC_FIELD1_PARAM 0x10 +#define VENC_FIELD2_PARAM 0x14 +#define VENC_LINE_O_1 0x18 +#define VENC_LINE_E_1 0x1c +#define VENC_LINE_O_2 0x20 +#define VENC_LINE_E_2 0x24 +#define VENC_LINE_TIMING_PARAM 0x28 +#define VENC_WEIGHT_VALUE 0x2c +#define VENC_BLANK_BLACK_LEVEL 0x30 +#define VENC_BURST_LEVEL 0x34 +#define VENC_CONTROL_PARAM 0x3c +#define VENC_SUB_CARRIER_PHASE1 0x40 +#define VENC_PHASE_LINE_INCR_CVBS 0x48 +#define VENC_ENABLE 0xa8 + +#endif /* __ZX_TVENC_REGS_H__ */ diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c index 73fe15c17c32..cf92d675feaa 100644 --- a/drivers/gpu/drm/zte/zx_vou.c +++ b/drivers/gpu/drm/zte/zx_vou.c @@ -40,6 +40,7 @@ struct zx_crtc_regs { u32 fir_active; u32 fir_htiming; u32 fir_vtiming; + u32 sec_vtiming; u32 timing_shift; u32 timing_pi_shift; }; @@ -48,6 +49,7 @@ static const struct zx_crtc_regs main_crtc_regs = { .fir_active = FIR_MAIN_ACTIVE, .fir_htiming = FIR_MAIN_H_TIMING, .fir_vtiming = FIR_MAIN_V_TIMING, + .sec_vtiming = SEC_MAIN_V_TIMING, .timing_shift = TIMING_MAIN_SHIFT, .timing_pi_shift = TIMING_MAIN_PI_SHIFT, }; @@ -56,6 +58,7 @@ static const struct zx_crtc_regs aux_crtc_regs = { .fir_active = FIR_AUX_ACTIVE, .fir_htiming = FIR_AUX_H_TIMING, .fir_vtiming = FIR_AUX_V_TIMING, + .sec_vtiming = SEC_AUX_V_TIMING, .timing_shift = TIMING_AUX_SHIFT, .timing_pi_shift = TIMING_AUX_PI_SHIFT, }; @@ -65,7 +68,17 @@ struct zx_crtc_bits { u32 polarity_shift; u32 int_frame_mask; u32 tc_enable; - u32 gl_enable; + u32 sec_vactive_shift; + u32 sec_vactive_mask; + u32 interlace_select; + u32 pi_enable; + u32 div_vga_shift; + u32 div_pic_shift; + u32 div_tvenc_shift; + u32 div_hdmi_pnx_shift; + u32 div_hdmi_shift; + u32 div_inf_shift; + u32 div_layer_shift; }; static const struct zx_crtc_bits main_crtc_bits = { @@ -73,7 +86,17 @@ static const struct zx_crtc_bits main_crtc_bits = { .polarity_shift = MAIN_POL_SHIFT, .int_frame_mask = TIMING_INT_MAIN_FRAME, .tc_enable = MAIN_TC_EN, - .gl_enable = OSD_CTRL0_GL0_EN, + .sec_vactive_shift = SEC_VACT_MAIN_SHIFT, + .sec_vactive_mask = SEC_VACT_MAIN_MASK, + .interlace_select = MAIN_INTERLACE_SEL, + .pi_enable = MAIN_PI_EN, + .div_vga_shift = VGA_MAIN_DIV_SHIFT, + .div_pic_shift = PIC_MAIN_DIV_SHIFT, + .div_tvenc_shift = TVENC_MAIN_DIV_SHIFT, + .div_hdmi_pnx_shift = HDMI_MAIN_PNX_DIV_SHIFT, + .div_hdmi_shift = HDMI_MAIN_DIV_SHIFT, + .div_inf_shift = INF_MAIN_DIV_SHIFT, + .div_layer_shift = LAYER_MAIN_DIV_SHIFT, }; static const struct zx_crtc_bits aux_crtc_bits = { @@ -81,7 +104,17 @@ static const struct zx_crtc_bits aux_crtc_bits = { .polarity_shift = AUX_POL_SHIFT, .int_frame_mask = TIMING_INT_AUX_FRAME, .tc_enable = AUX_TC_EN, - .gl_enable = OSD_CTRL0_GL1_EN, + .sec_vactive_shift = SEC_VACT_AUX_SHIFT, + .sec_vactive_mask = SEC_VACT_AUX_MASK, + .interlace_select = AUX_INTERLACE_SEL, + .pi_enable = AUX_PI_EN, + .div_vga_shift = VGA_AUX_DIV_SHIFT, + .div_pic_shift = PIC_AUX_DIV_SHIFT, + .div_tvenc_shift = TVENC_AUX_DIV_SHIFT, + .div_hdmi_pnx_shift = HDMI_AUX_PNX_DIV_SHIFT, + .div_hdmi_shift = HDMI_AUX_DIV_SHIFT, + .div_inf_shift = INF_AUX_DIV_SHIFT, + .div_layer_shift = LAYER_AUX_DIV_SHIFT, }; struct zx_crtc { @@ -97,6 +130,40 @@ struct zx_crtc { #define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc) +struct vou_layer_bits { + u32 enable; + u32 chnsel; + u32 clksel; +}; + +static const struct vou_layer_bits zx_gl_bits[GL_NUM] = { + { + .enable = OSD_CTRL0_GL0_EN, + .chnsel = OSD_CTRL0_GL0_SEL, + .clksel = VOU_CLK_GL0_SEL, + }, { + .enable = OSD_CTRL0_GL1_EN, + .chnsel = OSD_CTRL0_GL1_SEL, + .clksel = VOU_CLK_GL1_SEL, + }, +}; + +static const struct vou_layer_bits zx_vl_bits[VL_NUM] = { + { + .enable = OSD_CTRL0_VL0_EN, + .chnsel = OSD_CTRL0_VL0_SEL, + .clksel = VOU_CLK_VL0_SEL, + }, { + .enable = OSD_CTRL0_VL1_EN, + .chnsel = OSD_CTRL0_VL1_SEL, + .clksel = VOU_CLK_VL1_SEL, + }, { + .enable = OSD_CTRL0_VL2_EN, + .chnsel = OSD_CTRL0_VL2_SEL, + .clksel = VOU_CLK_VL2_SEL, + }, +}; + struct zx_vou_hw { struct device *dev; void __iomem *osd; @@ -112,6 +179,33 @@ struct zx_vou_hw { struct zx_crtc *aux_crtc; }; +enum vou_inf_data_sel { + VOU_YUV444 = 0, + VOU_RGB_101010 = 1, + VOU_RGB_888 = 2, + VOU_RGB_666 = 3, +}; + +struct vou_inf { + enum vou_inf_id id; + enum vou_inf_data_sel data_sel; + u32 clocks_en_bits; + u32 clocks_sel_bits; +}; + +static struct vou_inf vou_infs[] = { + [VOU_HDMI] = { + .data_sel = VOU_YUV444, + .clocks_en_bits = BIT(24) | BIT(18) | BIT(6), + .clocks_sel_bits = BIT(13) | BIT(2), + }, + [VOU_TV_ENC] = { + .data_sel = VOU_YUV444, + .clocks_en_bits = BIT(15), + .clocks_sel_bits = BIT(11) | BIT(0), + }, +}; + static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc) { struct zx_crtc *zcrtc = to_zx_crtc(crtc); @@ -119,20 +213,30 @@ static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc) return zcrtc->vou; } -void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc) +void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc, + enum vou_inf_hdmi_audio aud) +{ + struct zx_crtc *zcrtc = to_zx_crtc(crtc); + struct zx_vou_hw *vou = zcrtc->vou; + + zx_writel_mask(vou->vouctl + VOU_INF_HDMI_CTRL, VOU_HDMI_AUD_MASK, aud); +} + +void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc) { struct zx_crtc *zcrtc = to_zx_crtc(crtc); struct zx_vou_hw *vou = zcrtc->vou; + struct vou_inf *inf = &vou_infs[id]; bool is_main = zcrtc->chn_type == VOU_CHN_MAIN; - u32 data_sel_shift = inf->id << 1; + u32 data_sel_shift = id << 1; /* Select data format */ zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift, inf->data_sel << data_sel_shift); /* Select channel */ - zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << inf->id, - zcrtc->chn_type << inf->id); + zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << id, + zcrtc->chn_type << id); /* Select interface clocks */ zx_writel_mask(vou->vouctl + VOU_CLK_SEL, inf->clocks_sel_bits, @@ -143,20 +247,79 @@ void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc) inf->clocks_en_bits); /* Enable the device */ - zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 1 << inf->id); + zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 1 << id); } -void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc) +void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc) { struct zx_vou_hw *vou = crtc_to_vou(crtc); + struct vou_inf *inf = &vou_infs[id]; /* Disable the device */ - zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 0); + zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 0); /* Disable interface clocks */ zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0); } +void zx_vou_config_dividers(struct drm_crtc *crtc, + struct vou_div_config *configs, int num) +{ + struct zx_crtc *zcrtc = to_zx_crtc(crtc); + struct zx_vou_hw *vou = zcrtc->vou; + const struct zx_crtc_bits *bits = zcrtc->bits; + int i; + + /* Clear update flag bit */ + zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, 0); + + for (i = 0; i < num; i++) { + struct vou_div_config *cfg = configs + i; + u32 reg, shift; + + switch (cfg->id) { + case VOU_DIV_VGA: + reg = VOU_CLK_SEL; + shift = bits->div_vga_shift; + break; + case VOU_DIV_PIC: + reg = VOU_CLK_SEL; + shift = bits->div_pic_shift; + break; + case VOU_DIV_TVENC: + reg = VOU_DIV_PARA; + shift = bits->div_tvenc_shift; + break; + case VOU_DIV_HDMI_PNX: + reg = VOU_DIV_PARA; + shift = bits->div_hdmi_pnx_shift; + break; + case VOU_DIV_HDMI: + reg = VOU_DIV_PARA; + shift = bits->div_hdmi_shift; + break; + case VOU_DIV_INF: + reg = VOU_DIV_PARA; + shift = bits->div_inf_shift; + break; + case VOU_DIV_LAYER: + reg = VOU_DIV_PARA; + shift = bits->div_layer_shift; + break; + default: + continue; + } + + /* Each divider occupies 3 bits */ + zx_writel_mask(vou->vouctl + reg, 0x7 << shift, + cfg->val << shift); + } + + /* Set update flag bit to get dividers effected */ + zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, + DIV_PARA_UPDATE); +} + static inline void vou_chn_set_update(struct zx_crtc *zcrtc) { zx_writel(zcrtc->chnreg + CHN_UPDATE, 1); @@ -165,11 +328,13 @@ static inline void vou_chn_set_update(struct zx_crtc *zcrtc) static void zx_crtc_enable(struct drm_crtc *crtc) { struct drm_display_mode *mode = &crtc->state->adjusted_mode; + bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; struct zx_crtc *zcrtc = to_zx_crtc(crtc); struct zx_vou_hw *vou = zcrtc->vou; const struct zx_crtc_regs *regs = zcrtc->regs; const struct zx_crtc_bits *bits = zcrtc->bits; struct videomode vm; + u32 scan_mask; u32 pol = 0; u32 val; int ret; @@ -177,7 +342,7 @@ static void zx_crtc_enable(struct drm_crtc *crtc) drm_display_mode_to_videomode(mode, &vm); /* Set up timing parameters */ - val = V_ACTIVE(vm.vactive - 1); + val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1); val |= H_ACTIVE(vm.hactive - 1); zx_writel(vou->timing + regs->fir_active, val); @@ -191,6 +356,25 @@ static void zx_crtc_enable(struct drm_crtc *crtc) val |= FRONT_PORCH(vm.vfront_porch - 1); zx_writel(vou->timing + regs->fir_vtiming, val); + if (interlaced) { + u32 shift = bits->sec_vactive_shift; + u32 mask = bits->sec_vactive_mask; + + val = zx_readl(vou->timing + SEC_V_ACTIVE); + val &= ~mask; + val |= ((vm.vactive / 2 - 1) << shift) & mask; + zx_writel(vou->timing + SEC_V_ACTIVE, val); + + val = SYNC_WIDE(vm.vsync_len - 1); + /* + * The vback_porch for the second field needs to shift one on + * the value for the first field. + */ + val |= BACK_PORCH(vm.vback_porch); + val |= FRONT_PORCH(vm.vfront_porch - 1); + zx_writel(vou->timing + regs->sec_vtiming, val); + } + /* Set up polarities */ if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) pol |= 1 << POL_VSYNC_SHIFT; @@ -201,9 +385,17 @@ static void zx_crtc_enable(struct drm_crtc *crtc) pol << bits->polarity_shift); /* Setup SHIFT register by following what ZTE BSP does */ - zx_writel(vou->timing + regs->timing_shift, H_SHIFT_VAL); + val = H_SHIFT_VAL; + if (interlaced) + val |= V_SHIFT_VAL << 16; + zx_writel(vou->timing + regs->timing_shift, val); zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL); + /* Progressive or interlace scan select */ + scan_mask = bits->interlace_select | bits->pi_enable; + zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask, + interlaced ? scan_mask : 0); + /* Enable TIMING_CTRL */ zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, bits->tc_enable); @@ -214,16 +406,16 @@ static void zx_crtc_enable(struct drm_crtc *crtc) zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK, vm.vactive << CHN_SCREEN_H_SHIFT); + /* Configure channel interlace buffer control */ + zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN, + interlaced ? CHN_INTERLACE_EN : 0); + /* Update channel */ vou_chn_set_update(zcrtc); /* Enable channel */ zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, CHN_ENABLE); - /* Enable Graphic Layer */ - zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable, - bits->gl_enable); - drm_crtc_vblank_on(crtc); ret = clk_set_rate(zcrtc->pixclk, mode->clock * 1000); @@ -247,9 +439,6 @@ static void zx_crtc_disable(struct drm_crtc *crtc) drm_crtc_vblank_off(crtc); - /* Disable Graphic Layer */ - zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable, 0); - /* Disable channel */ zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, 0); @@ -294,7 +483,7 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou, enum vou_chn_type chn_type) { struct device *dev = vou->dev; - struct zx_layer_data data; + struct zx_plane *zplane; struct zx_crtc *zcrtc; int ret; @@ -305,19 +494,27 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou, zcrtc->vou = vou; zcrtc->chn_type = chn_type; + zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL); + if (!zplane) + return -ENOMEM; + + zplane->dev = dev; + if (chn_type == VOU_CHN_MAIN) { - data.layer = vou->osd + MAIN_GL_OFFSET; - data.csc = vou->osd + MAIN_CSC_OFFSET; - data.hbsc = vou->osd + MAIN_HBSC_OFFSET; - data.rsz = vou->otfppu + MAIN_RSZ_OFFSET; + zplane->layer = vou->osd + MAIN_GL_OFFSET; + zplane->csc = vou->osd + MAIN_CSC_OFFSET; + zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET; + zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET; + zplane->bits = &zx_gl_bits[0]; zcrtc->chnreg = vou->osd + OSD_MAIN_CHN; zcrtc->regs = &main_crtc_regs; zcrtc->bits = &main_crtc_bits; } else { - data.layer = vou->osd + AUX_GL_OFFSET; - data.csc = vou->osd + AUX_CSC_OFFSET; - data.hbsc = vou->osd + AUX_HBSC_OFFSET; - data.rsz = vou->otfppu + AUX_RSZ_OFFSET; + zplane->layer = vou->osd + AUX_GL_OFFSET; + zplane->csc = vou->osd + AUX_CSC_OFFSET; + zplane->hbsc = vou->osd + AUX_HBSC_OFFSET; + zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET; + zplane->bits = &zx_gl_bits[1]; zcrtc->chnreg = vou->osd + OSD_AUX_CHN; zcrtc->regs = &aux_crtc_regs; zcrtc->bits = &aux_crtc_bits; @@ -331,13 +528,14 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou, return ret; } - zcrtc->primary = zx_plane_init(drm, dev, &data, DRM_PLANE_TYPE_PRIMARY); - if (IS_ERR(zcrtc->primary)) { - ret = PTR_ERR(zcrtc->primary); + ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_PRIMARY); + if (ret) { DRM_DEV_ERROR(dev, "failed to init primary plane: %d\n", ret); return ret; } + zcrtc->primary = &zplane->plane; + ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL, &zx_crtc_funcs, NULL); if (ret) { @@ -355,17 +553,6 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou, return 0; } -static inline struct drm_crtc *zx_find_crtc(struct drm_device *drm, int pipe) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) - if (crtc->index == pipe) - return crtc; - - return NULL; -} - int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe) { struct drm_crtc *crtc; @@ -373,7 +560,7 @@ int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe) struct zx_vou_hw *vou; u32 int_frame_mask; - crtc = zx_find_crtc(drm, pipe); + crtc = drm_crtc_from_index(drm, pipe); if (!crtc) return 0; @@ -393,7 +580,7 @@ void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe) struct zx_crtc *zcrtc; struct zx_vou_hw *vou; - crtc = zx_find_crtc(drm, pipe); + crtc = drm_crtc_from_index(drm, pipe); if (!crtc) return; @@ -404,6 +591,78 @@ void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe) zcrtc->bits->int_frame_mask, 0); } +void zx_vou_layer_enable(struct drm_plane *plane) +{ + struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc); + struct zx_vou_hw *vou = zcrtc->vou; + struct zx_plane *zplane = to_zx_plane(plane); + const struct vou_layer_bits *bits = zplane->bits; + + if (zcrtc->chn_type == VOU_CHN_MAIN) { + zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0); + zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0); + } else { + zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, + bits->chnsel); + zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, + bits->clksel); + } + + zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable); +} + +void zx_vou_layer_disable(struct drm_plane *plane) +{ + struct zx_crtc *zcrtc = to_zx_crtc(plane->crtc); + struct zx_vou_hw *vou = zcrtc->vou; + struct zx_plane *zplane = to_zx_plane(plane); + const struct vou_layer_bits *bits = zplane->bits; + + zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0); +} + +static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou) +{ + struct device *dev = vou->dev; + struct zx_plane *zplane; + int i; + int ret; + + /* + * VL0 has some quirks on scaling support which need special handling. + * Let's leave it out for now. + */ + for (i = 1; i < VL_NUM; i++) { + zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL); + if (!zplane) { + DRM_DEV_ERROR(dev, "failed to allocate zplane %d\n", i); + return; + } + + zplane->layer = vou->osd + OSD_VL_OFFSET(i); + zplane->hbsc = vou->osd + HBSC_VL_OFFSET(i); + zplane->rsz = vou->otfppu + RSZ_VL_OFFSET(i); + zplane->bits = &zx_vl_bits[i]; + + ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_OVERLAY); + if (ret) { + DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i); + continue; + } + } +} + +static inline void zx_osd_int_update(struct zx_crtc *zcrtc) +{ + struct drm_crtc *crtc = &zcrtc->crtc; + struct drm_plane *plane; + + vou_chn_set_update(zcrtc); + + drm_for_each_plane_mask(plane, crtc->dev, crtc->state->plane_mask) + zx_plane_set_update(plane); +} + static irqreturn_t vou_irq_handler(int irq, void *dev_id) { struct zx_vou_hw *vou = dev_id; @@ -423,15 +682,11 @@ static irqreturn_t vou_irq_handler(int irq, void *dev_id) state = zx_readl(vou->osd + OSD_INT_STA); zx_writel(vou->osd + OSD_INT_CLRSTA, state); - if (state & OSD_INT_MAIN_UPT) { - vou_chn_set_update(vou->main_crtc); - zx_plane_set_update(vou->main_crtc->primary); - } + if (state & OSD_INT_MAIN_UPT) + zx_osd_int_update(vou->main_crtc); - if (state & OSD_INT_AUX_UPT) { - vou_chn_set_update(vou->aux_crtc); - zx_plane_set_update(vou->aux_crtc->primary); - } + if (state & OSD_INT_AUX_UPT) + zx_osd_int_update(vou->aux_crtc); if (state & OSD_INT_ERROR) DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state); @@ -462,19 +717,9 @@ static void vou_dtrc_init(struct zx_vou_hw *vou) static void vou_hw_init(struct zx_vou_hw *vou) { - /* Set GL0 to main channel and GL1 to aux channel */ - zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL0_SEL, 0); - zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL1_SEL, - OSD_CTRL0_GL1_SEL); - /* Release reset for all VOU modules */ zx_writel(vou->vouctl + VOU_SOFT_RST, ~0); - /* Select main clock for GL0 and aux clock for GL1 module */ - zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL0_SEL, 0); - zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL1_SEL, - VOU_CLK_GL1_SEL); - /* Enable clock auto-gating for all VOU modules */ zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0); @@ -611,6 +856,8 @@ static int zx_crtc_bind(struct device *dev, struct device *master, void *data) goto disable_ppu_clk; } + zx_overlay_init(drm, vou); + return 0; disable_ppu_clk: diff --git a/drivers/gpu/drm/zte/zx_vou.h b/drivers/gpu/drm/zte/zx_vou.h index 349e06cd86f4..57e3c31ee6a5 100644 --- a/drivers/gpu/drm/zte/zx_vou.h +++ b/drivers/gpu/drm/zte/zx_vou.h @@ -23,24 +23,48 @@ enum vou_inf_id { VOU_VGA = 5, }; -enum vou_inf_data_sel { - VOU_YUV444 = 0, - VOU_RGB_101010 = 1, - VOU_RGB_888 = 2, - VOU_RGB_666 = 3, +enum vou_inf_hdmi_audio { + VOU_HDMI_AUD_SPDIF = BIT(0), + VOU_HDMI_AUD_I2S = BIT(1), + VOU_HDMI_AUD_DSD = BIT(2), + VOU_HDMI_AUD_HBR = BIT(3), + VOU_HDMI_AUD_PARALLEL = BIT(4), }; -struct vou_inf { - enum vou_inf_id id; - enum vou_inf_data_sel data_sel; - u32 clocks_en_bits; - u32 clocks_sel_bits; +void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc, + enum vou_inf_hdmi_audio aud); +void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc); +void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc); + +enum vou_div_id { + VOU_DIV_VGA, + VOU_DIV_PIC, + VOU_DIV_TVENC, + VOU_DIV_HDMI_PNX, + VOU_DIV_HDMI, + VOU_DIV_INF, + VOU_DIV_LAYER, +}; + +enum vou_div_val { + VOU_DIV_1 = 0, + VOU_DIV_2 = 1, + VOU_DIV_4 = 3, + VOU_DIV_8 = 7, }; -void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc); -void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc); +struct vou_div_config { + enum vou_div_id id; + enum vou_div_val val; +}; + +void zx_vou_config_dividers(struct drm_crtc *crtc, + struct vou_div_config *configs, int num); int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe); void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe); +void zx_vou_layer_enable(struct drm_plane *plane); +void zx_vou_layer_disable(struct drm_plane *plane); + #endif /* __ZX_VOU_H__ */ diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h index f44e7a4ae441..c066ef123434 100644 --- a/drivers/gpu/drm/zte/zx_vou_regs.h +++ b/drivers/gpu/drm/zte/zx_vou_regs.h @@ -22,6 +22,15 @@ #define AUX_HBSC_OFFSET 0x860 #define AUX_RSZ_OFFSET 0x800 +#define OSD_VL0_OFFSET 0x040 +#define OSD_VL_OFFSET(i) (OSD_VL0_OFFSET + 0x050 * (i)) + +#define HBSC_VL0_OFFSET 0x760 +#define HBSC_VL_OFFSET(i) (HBSC_VL0_OFFSET + 0x040 * (i)) + +#define RSZ_VL1_U0 0xa00 +#define RSZ_VL_OFFSET(i) (RSZ_VL1_U0 + 0x200 * (i)) + /* OSD (GPC_GLOBAL) registers */ #define OSD_INT_STA 0x04 #define OSD_INT_CLRSTA 0x08 @@ -42,6 +51,12 @@ ) #define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT) #define OSD_CTRL0 0x10 +#define OSD_CTRL0_VL0_EN BIT(13) +#define OSD_CTRL0_VL0_SEL BIT(12) +#define OSD_CTRL0_VL1_EN BIT(11) +#define OSD_CTRL0_VL1_SEL BIT(10) +#define OSD_CTRL0_VL2_EN BIT(9) +#define OSD_CTRL0_VL2_SEL BIT(8) #define OSD_CTRL0_GL0_EN BIT(7) #define OSD_CTRL0_GL0_SEL BIT(6) #define OSD_CTRL0_GL1_EN BIT(5) @@ -60,6 +75,8 @@ #define CHN_SCREEN_H_SHIFT 5 #define CHN_SCREEN_H_MASK (0x1fff << CHN_SCREEN_H_SHIFT) #define CHN_UPDATE 0x08 +#define CHN_INTERLACE_BUF_CTRL 0x24 +#define CHN_INTERLACE_EN BIT(2) /* TIMING_CTRL registers */ #define TIMING_TC_ENABLE 0x04 @@ -102,6 +119,19 @@ #define TIMING_MAIN_SHIFT 0x2c #define TIMING_AUX_SHIFT 0x30 #define H_SHIFT_VAL 0x0048 +#define V_SHIFT_VAL 0x0001 +#define SCAN_CTRL 0x34 +#define AUX_PI_EN BIT(19) +#define MAIN_PI_EN BIT(18) +#define AUX_INTERLACE_SEL BIT(1) +#define MAIN_INTERLACE_SEL BIT(0) +#define SEC_V_ACTIVE 0x38 +#define SEC_VACT_MAIN_SHIFT 0 +#define SEC_VACT_MAIN_MASK (0xffff << SEC_VACT_MAIN_SHIFT) +#define SEC_VACT_AUX_SHIFT 16 +#define SEC_VACT_AUX_MASK (0xffff << SEC_VACT_AUX_SHIFT) +#define SEC_MAIN_V_TIMING 0x3c +#define SEC_AUX_V_TIMING 0x40 #define TIMING_MAIN_PI_SHIFT 0x68 #define TIMING_AUX_PI_SHIFT 0x6c #define H_PI_SHIFT_VAL 0x000f @@ -146,10 +176,31 @@ #define VOU_INF_DATA_SEL 0x08 #define VOU_SOFT_RST 0x14 #define VOU_CLK_SEL 0x18 +#define VGA_AUX_DIV_SHIFT 29 +#define VGA_MAIN_DIV_SHIFT 26 +#define PIC_MAIN_DIV_SHIFT 23 +#define PIC_AUX_DIV_SHIFT 20 +#define VOU_CLK_VL2_SEL BIT(8) +#define VOU_CLK_VL1_SEL BIT(7) +#define VOU_CLK_VL0_SEL BIT(6) #define VOU_CLK_GL1_SEL BIT(5) #define VOU_CLK_GL0_SEL BIT(4) +#define VOU_DIV_PARA 0x1c +#define DIV_PARA_UPDATE BIT(31) +#define TVENC_AUX_DIV_SHIFT 28 +#define HDMI_AUX_PNX_DIV_SHIFT 25 +#define HDMI_MAIN_PNX_DIV_SHIFT 22 +#define HDMI_AUX_DIV_SHIFT 19 +#define HDMI_MAIN_DIV_SHIFT 16 +#define TVENC_MAIN_DIV_SHIFT 13 +#define INF_AUX_DIV_SHIFT 9 +#define INF_MAIN_DIV_SHIFT 6 +#define LAYER_AUX_DIV_SHIFT 3 +#define LAYER_MAIN_DIV_SHIFT 0 #define VOU_CLK_REQEN 0x20 #define VOU_CLK_EN 0x24 +#define VOU_INF_HDMI_CTRL 0x30 +#define VOU_HDMI_AUD_MASK 0x1f /* OTFPPU_CTRL registers */ #define OTFPPU_RSZ_DATA_SOURCE 0x04 diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index c27858ae0552..eeb021fe6410 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -399,6 +399,7 @@ static int host1x_device_add(struct host1x *host1x, dev_set_name(&device->dev, "%s", driver->driver.name); of_dma_configure(&device->dev, host1x->dev->of_node); device->dev.release = host1x_device_release; + device->dev.of_node = host1x->dev->of_node; device->dev.bus = &host1x_bus_type; device->dev.parent = host1x->dev; diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 97218af4fe75..8368e6f766ee 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -1238,12 +1238,6 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) platform_device_put(pdev); goto err_register; } - - /* - * Set of_node only after calling platform_device_add. Otherwise - * the platform:imx-ipuv3-crtc modalias won't be used. - */ - pdev->dev.of_node = of_node; } return 0; diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c index 63c7292f427a..24e12b87a0cb 100644 --- a/drivers/gpu/ipu-v3/ipu-csi.c +++ b/drivers/gpu/ipu-v3/ipu-csi.c @@ -544,6 +544,7 @@ void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) spin_unlock_irqrestore(&csi->lock, flags); } +EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, u32 r_value, u32 g_value, u32 b_value, diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index 0f5b2dd24507..92f1452dad57 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -41,7 +41,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/list.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/spinlock.h> #include <linux/poll.h> diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index acfb522a432a..c6c9c51c806f 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -30,7 +30,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/uaccess.h> diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 0dd1167b2c9b..9c113f62472d 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -487,7 +487,7 @@ static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize, int offset, const char *device_name) { /* * the fixup that need to be done: - * - change Usage Maximum in the Comsumer Control + * - change Usage Maximum in the Consumer Control * (report ID 3) to a reasonable value */ if (*rsize >= offset + 31 && diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index 76d06cf87b2a..fb77dec720a4 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -25,7 +25,7 @@ #include <linux/cdev.h> #include <linux/poll.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/hid-roccat.h> #include <linux/module.h> diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index f0e2757cb909..ec530454e6f6 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -33,7 +33,7 @@ #include <linux/slab.h> #include <linux/hid.h> #include <linux/mutex.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/string.h> #include <linux/hidraw.h> diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 700145b15088..774bd701dae0 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -27,6 +27,7 @@ #include <linux/poll.h> #include <linux/slab.h> +#include <linux/sched/signal.h> #include <linux/module.h> #include <linux/init.h> #include <linux/input.h> diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c index 3deef6cc7d7c..727f968ac1cb 100644 --- a/drivers/hsi/clients/cmt_speech.c +++ b/drivers/hsi/clients/cmt_speech.c @@ -31,7 +31,7 @@ #include <linux/slab.h> #include <linux/fs.h> #include <linux/poll.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/ioctl.h> #include <linux/uaccess.h> #include <linux/pm_qos.h> @@ -1098,9 +1098,9 @@ static void cs_hsi_stop(struct cs_hsi_iface *hi) kfree(hi); } -static int cs_char_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int cs_char_vma_fault(struct vm_fault *vmf) { - struct cs_char *csdata = vma->vm_private_data; + struct cs_char *csdata = vmf->vma->vm_private_data; struct page *page; page = virt_to_page(csdata->mmap_base); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index f7f6b9144b07..da6b59ba5940 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -34,6 +34,8 @@ #include <linux/kernel_stat.h> #include <linux/clockchips.h> #include <linux/cpu.h> +#include <linux/sched/task_stack.h> + #include <asm/hyperv.h> #include <asm/hypervisor.h> #include <asm/mshyperv.h> diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 6dca2fd3d303..6d1208b2b6d2 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -861,7 +861,7 @@ static ssize_t fan1_pulses_store(struct device *dev, * (i.e. closed or open-loop). * * Following documentation about hwmon's sysfs interface, a pwm1_enable node - * should accept followings: + * should accept the following: * * 0 : no fan speed control (i.e. fan at full speed) * 1 : manual fan speed control enabled (use pwm[1-*]) (open-loop) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index e8d55a153a65..e88afe1a435c 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -1188,9 +1188,9 @@ static void msc_mmap_close(struct vm_area_struct *vma) mutex_unlock(&msc->buf_mutex); } -static int msc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int msc_mmap_fault(struct vm_fault *vmf) { - struct msc_iter *iter = vma->vm_file->private_data; + struct msc_iter *iter = vmf->vma->vm_file->private_data; struct msc *msc = iter->msc; vmf->page = msc_buffer_get_page(msc, vmf->pgoff); @@ -1198,7 +1198,7 @@ static int msc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; get_page(vmf->page); - vmf->page->mapping = vma->vm_file->f_mapping; + vmf->page->mapping = vmf->vma->vm_file->f_mapping; vmf->page->index = vmf->pgoff; return 0; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 0cdc8443deab..8adc0f1d7ad0 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -128,6 +128,7 @@ config I2C_I801 DNV (SOC) Broxton (SOC) Lewisburg (PCH) + Gemini Lake (SOC) This driver can also be built as a module. If so, the module will be called i2c-i801. @@ -886,6 +887,16 @@ config I2C_ST This driver can also be built as module. If so, the module will be called i2c-st. +config I2C_STM32F4 + tristate "STMicroelectronics STM32F4 I2C support" + depends on ARCH_STM32 || COMPILE_TEST + help + Enable this option to add support for STM32 I2C controller embedded + in STM32F4 SoCs. + + This driver can also be built as module. If so, the module + will be called i2c-stm32f4. + config I2C_STU300 tristate "ST Microelectronics DDC I2C interface" depends on MACH_U300 @@ -919,6 +930,17 @@ config I2C_TEGRA If you say yes to this option, support will be included for the I2C controller embedded in NVIDIA Tegra SOCs +config I2C_TEGRA_BPMP + tristate "NVIDIA Tegra BPMP I2C controller" + depends on TEGRA_BPMP + help + If you say yes to this option, support will be included for the I2C + controller embedded in NVIDIA Tegra SoCs accessed via the BPMP. + + This I2C driver is a 'virtual' I2C driver. The real driver is part + of the BPMP firmware, and this driver merely communicates with that + real driver. + config I2C_UNIPHIER tristate "UniPhier FIFO-less I2C controller" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1c1bac87a9db..30b60855fbcd 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -85,9 +85,11 @@ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o obj-$(CONFIG_I2C_ST) += i2c-st.o +obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o obj-$(CONFIG_I2C_STU300) += i2c-stu300.o obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o +obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 0b86c6173e07..fabbb9e49161 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -820,7 +820,7 @@ static u32 at91_twi_func(struct i2c_adapter *adapter) | I2C_FUNC_SMBUS_READ_BLOCK_DATA; } -static struct i2c_algorithm at91_twi_algorithm = { +static const struct i2c_algorithm at91_twi_algorithm = { .master_xfer = at91_twi_xfer, .functionality = at91_twi_func, }; @@ -1180,6 +1180,7 @@ static int at91_twi_suspend_noirq(struct device *dev) static int at91_twi_resume_noirq(struct device *dev) { + struct at91_twi_dev *twi_dev = dev_get_drvdata(dev); int ret; if (!pm_runtime_status_suspended(dev)) { @@ -1191,6 +1192,8 @@ static int at91_twi_resume_noirq(struct device *dev) pm_runtime_mark_last_busy(dev); pm_request_autosuspend(dev); + at91_init_twi_bus(twi_dev); + return 0; } diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index c3436f627028..cd07a69e2e93 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -195,7 +195,9 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) } if (val & BCM2835_I2C_S_DONE) { - if (i2c_dev->curr_msg->flags & I2C_M_RD) { + if (!i2c_dev->curr_msg) { + dev_err(i2c_dev->dev, "Got unexpected interrupt (from firmware?)\n"); + } else if (i2c_dev->curr_msg->flags & I2C_M_RD) { bcm2835_drain_rxfifo(i2c_dev); val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); } diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 29d00c4f7824..9fe942b8c610 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -563,7 +563,7 @@ static u32 bfin_twi_functionality(struct i2c_adapter *adap) I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK; } -static struct i2c_algorithm bfin_twi_algorithm = { +static const struct i2c_algorithm bfin_twi_algorithm = { .master_xfer = bfin_twi_master_xfer, .smbus_xfer = bfin_twi_smbus_xfer, .functionality = bfin_twi_functionality, diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index 9b36a7b3befd..eb76b76f4754 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -154,8 +154,10 @@ static int ec_i2c_parse_response(const u8 *buf, struct i2c_msg i2c_msgs[], resp = (const struct ec_response_i2c_passthru *)buf; if (resp->i2c_status & EC_I2C_STATUS_TIMEOUT) return -ETIMEDOUT; + else if (resp->i2c_status & EC_I2C_STATUS_NAK) + return -ENXIO; else if (resp->i2c_status & EC_I2C_STATUS_ERROR) - return -EREMOTEIO; + return -EIO; /* Other side could send us back fewer messages, but not more */ if (resp->num_msgs > *num) @@ -222,10 +224,8 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], } result = ec_i2c_parse_response(msg->data, i2c_msgs, &num); - if (result < 0) { - dev_err(dev, "Error parsing EC i2c message %d\n", result); + if (result < 0) goto exit; - } /* Indicate success by saying how many messages were sent */ result = num; diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index e9db857c6226..7a3faa551cf8 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -820,7 +820,7 @@ static u32 i2c_dw_func(struct i2c_adapter *adap) return dev->functionality; } -static struct i2c_algorithm i2c_dw_algo = { +static const struct i2c_algorithm i2c_dw_algo = { .master_xfer = i2c_dw_xfer, .functionality = i2c_dw_func, }; diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 5ce71ce7b6c4..bdeab0174fec 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -715,7 +715,7 @@ static u32 pch_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; } -static struct i2c_algorithm pch_algorithm = { +static const struct i2c_algorithm pch_algorithm = { .master_xfer = pch_i2c_xfer, .functionality = pch_i2c_func }; diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index 96bb4e749012..312912708854 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -347,7 +347,7 @@ static int em_i2c_unreg_slave(struct i2c_client *slave) return 0; } -static struct i2c_algorithm em_i2c_algo = { +static const struct i2c_algorithm em_i2c_algo = { .master_xfer = em_i2c_xfer, .functionality = em_i2c_func, .reg_slave = em_i2c_reg_slave, diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index bea607149972..cbd93ce0661f 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -130,12 +130,32 @@ /* I2C_TRANS_STATUS register bits */ #define HSI2C_MASTER_BUSY (1u << 17) #define HSI2C_SLAVE_BUSY (1u << 16) + +/* I2C_TRANS_STATUS register bits for Exynos5 variant */ #define HSI2C_TIMEOUT_AUTO (1u << 4) #define HSI2C_NO_DEV (1u << 3) #define HSI2C_NO_DEV_ACK (1u << 2) #define HSI2C_TRANS_ABORT (1u << 1) #define HSI2C_TRANS_DONE (1u << 0) +/* I2C_TRANS_STATUS register bits for Exynos7 variant */ +#define HSI2C_MASTER_ST_MASK 0xf +#define HSI2C_MASTER_ST_IDLE 0x0 +#define HSI2C_MASTER_ST_START 0x1 +#define HSI2C_MASTER_ST_RESTART 0x2 +#define HSI2C_MASTER_ST_STOP 0x3 +#define HSI2C_MASTER_ST_MASTER_ID 0x4 +#define HSI2C_MASTER_ST_ADDR0 0x5 +#define HSI2C_MASTER_ST_ADDR1 0x6 +#define HSI2C_MASTER_ST_ADDR2 0x7 +#define HSI2C_MASTER_ST_ADDR_SR 0x8 +#define HSI2C_MASTER_ST_READ 0x9 +#define HSI2C_MASTER_ST_WRITE 0xa +#define HSI2C_MASTER_ST_NO_ACK 0xb +#define HSI2C_MASTER_ST_LOSE 0xc +#define HSI2C_MASTER_ST_WAIT 0xd +#define HSI2C_MASTER_ST_WAIT_CMD 0xe + /* I2C_ADDR register bits */ #define HSI2C_SLV_ADDR_SLV(x) ((x & 0x3ff) << 0) #define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10) @@ -437,6 +457,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) int_status = readl(i2c->regs + HSI2C_INT_STATUS); writel(int_status, i2c->regs + HSI2C_INT_STATUS); + trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); /* handle interrupt related to the transfer status */ if (i2c->variant->hw == HSI2C_EXYNOS7) { @@ -460,8 +481,12 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) i2c->state = -ETIMEDOUT; goto stop; } + + if ((trans_status & HSI2C_MASTER_ST_MASK) == HSI2C_MASTER_ST_LOSE) { + i2c->state = -EAGAIN; + goto stop; + } } else if (int_status & HSI2C_INT_I2C) { - trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); if (trans_status & HSI2C_NO_DEV_ACK) { dev_dbg(i2c->dev, "No ACK from device\n"); i2c->state = -ENXIO; @@ -502,8 +527,13 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) fifo_level = HSI2C_TX_FIFO_LVL(fifo_status); len = i2c->variant->fifo_depth - fifo_level; - if (len > (i2c->msg->len - i2c->msg_ptr)) + if (len > (i2c->msg->len - i2c->msg_ptr)) { + u32 int_en = readl(i2c->regs + HSI2C_INT_ENABLE); + + int_en &= ~HSI2C_INT_TX_ALMOSTEMPTY_EN; + writel(int_en, i2c->regs + HSI2C_INT_ENABLE); len = i2c->msg->len - i2c->msg_ptr; + } while (len > 0) { byte = i2c->msg->buf[i2c->msg_ptr++]; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index e242db43774b..6484fa6dbb84 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -65,6 +65,7 @@ * Lewisburg (PCH) 0xa1a3 32 hard yes yes yes * Lewisburg Supersku (PCH) 0xa223 32 hard yes yes yes * Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes yes + * Gemini Lake (SOC) 0x31d4 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -213,6 +214,7 @@ #define PCI_DEVICE_ID_INTEL_BRASWELL_SMBUS 0x2292 #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0 +#define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 @@ -1012,6 +1014,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) }, diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 412b91d255ad..961c5f42d956 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -37,6 +37,8 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/sched/signal.h> + #include <asm/irq.h> #include <linux/io.h> #include <linux/i2c.h> diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 3310f2e0dbd3..e86801a63120 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -538,7 +538,7 @@ static u32 lpi2c_imx_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_READ_BLOCK_DATA; } -static struct i2c_algorithm lpi2c_imx_algo = { +static const struct i2c_algorithm lpi2c_imx_algo = { .master_xfer = lpi2c_imx_xfer, .functionality = lpi2c_imx_func, }; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 47fc1f1acff7..95ed17183e73 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1037,7 +1037,7 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter) | I2C_FUNC_SMBUS_READ_BLOCK_DATA; } -static struct i2c_algorithm i2c_imx_algo = { +static const struct i2c_algorithm i2c_imx_algo = { .master_xfer = i2c_imx_xfer, .functionality = i2c_imx_func, }; diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 565a49a0c564..96caf378b1dc 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -15,7 +15,7 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index b4dec0841bc2..a50bd6891e27 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -977,11 +977,32 @@ mv64xxx_i2c_remove(struct platform_device *dev) return 0; } +#ifdef CONFIG_PM +static int mv64xxx_i2c_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(pdev); + + mv64xxx_i2c_hw_init(drv_data); + + return 0; +} + +static const struct dev_pm_ops mv64xxx_i2c_pm = { + .resume = mv64xxx_i2c_resume, +}; + +#define mv64xxx_i2c_pm_ops (&mv64xxx_i2c_pm) +#else +#define mv64xxx_i2c_pm_ops NULL +#endif + static struct platform_driver mv64xxx_i2c_driver = { .probe = mv64xxx_i2c_probe, .remove = mv64xxx_i2c_remove, .driver = { .name = MV64XXX_I2C_CTLR_NAME, + .pm = mv64xxx_i2c_pm_ops, .of_match_table = mv64xxx_i2c_of_match_table, }, }; diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 374b35e7e450..3241bb9d6c18 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -296,7 +296,7 @@ static u32 nforce2_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_BLOCK_DATA : 0); } -static struct i2c_algorithm smbus_algorithm = { +static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = nforce2_access, .functionality = nforce2_func, }; diff --git a/drivers/i2c/busses/i2c-octeon-core.h b/drivers/i2c/busses/i2c-octeon-core.h index e160f838c254..aa3c8f4771c1 100644 --- a/drivers/i2c/busses/i2c-octeon-core.h +++ b/drivers/i2c/busses/i2c-octeon-core.h @@ -6,7 +6,6 @@ #include <linux/i2c-smbus.h> #include <linux/io.h> #include <linux/kernel.h> -#include <linux/pci.h> /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ @@ -118,9 +117,6 @@ struct octeon_i2c { void (*hlc_int_disable)(struct octeon_i2c *); atomic_t int_enable_cnt; atomic_t hlc_int_enable_cnt; -#if IS_ENABLED(CONFIG_I2C_THUNDERX) - struct msix_entry i2c_msix; -#endif struct i2c_smbus_alert_setup alert_data; struct i2c_client *ara; }; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index c7da0c42baee..1ebb5e947e0b 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1504,7 +1504,7 @@ static int omap_i2c_runtime_resume(struct device *dev) return 0; } -static struct dev_pm_ops omap_i2c_pm_ops = { +static const struct dev_pm_ops omap_i2c_pm_ops = { SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend, omap_i2c_runtime_resume, NULL) }; diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index 6263ea82d6ac..8f11d347b3ec 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -80,6 +80,7 @@ #define ICIER_TEIE 0x40 #define ICIER_RIE 0x20 #define ICIER_NAKIE 0x10 +#define ICIER_SPIE 0x08 #define ICSR2_NACKF 0x10 @@ -216,11 +217,10 @@ static irqreturn_t riic_tend_isr(int irq, void *data) return IRQ_NONE; } - if (riic->is_last || riic->err) + if (riic->is_last || riic->err) { + riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER); writeb(ICCR2_SP, riic->base + RIIC_ICCR2); - - writeb(0, riic->base + RIIC_ICIER); - complete(&riic->msg_done); + } return IRQ_HANDLED; } @@ -240,13 +240,13 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data) if (riic->bytes_left == 1) { /* STOP must come before we set ACKBT! */ - if (riic->is_last) + if (riic->is_last) { + riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER); writeb(ICCR2_SP, riic->base + RIIC_ICCR2); + } riic_clear_set_bit(riic, 0, ICMR3_ACKBT, RIIC_ICMR3); - writeb(0, riic->base + RIIC_ICIER); - complete(&riic->msg_done); } else { riic_clear_set_bit(riic, ICMR3_ACKBT, 0, RIIC_ICMR3); } @@ -259,6 +259,21 @@ static irqreturn_t riic_rdrf_isr(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t riic_stop_isr(int irq, void *data) +{ + struct riic_dev *riic = data; + + /* read back registers to confirm writes have fully propagated */ + writeb(0, riic->base + RIIC_ICSR2); + readb(riic->base + RIIC_ICSR2); + writeb(0, riic->base + RIIC_ICIER); + readb(riic->base + RIIC_ICIER); + + complete(&riic->msg_done); + + return IRQ_HANDLED; +} + static u32 riic_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; @@ -326,6 +341,7 @@ static struct riic_irq_desc riic_irqs[] = { { .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" }, { .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" }, { .res_num = 2, .isr = riic_tdre_isr, .name = "riic-tdre" }, + { .res_num = 3, .isr = riic_stop_isr, .name = "riic-stop" }, { .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" }, }; diff --git a/drivers/i2c/busses/i2c-robotfuzz-osif.c b/drivers/i2c/busses/i2c-robotfuzz-osif.c index 89d8b41b6668..9c0f52b7ff7e 100644 --- a/drivers/i2c/busses/i2c-robotfuzz-osif.c +++ b/drivers/i2c/busses/i2c-robotfuzz-osif.c @@ -117,7 +117,7 @@ static u32 osif_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm osif_algorithm = { +static const struct i2c_algorithm osif_algorithm = { .master_xfer = osif_xfer, .functionality = osif_func, }; diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 3d9ebe6e5716..3d7559348745 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -781,7 +781,7 @@ static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; } -static struct i2c_algorithm sh_mobile_i2c_algorithm = { +static const struct i2c_algorithm sh_mobile_i2c_algorithm = { .functionality = sh_mobile_i2c_func, .master_xfer = sh_mobile_i2c_xfer, }; diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 1371547ce1a3..1eb9fa82dcfd 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -776,7 +776,7 @@ static u32 st_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm st_i2c_algo = { +static const struct i2c_algorithm st_i2c_algo = { .master_xfer = st_i2c_xfer, .functionality = st_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c new file mode 100644 index 000000000000..f9dd7e86b861 --- /dev/null +++ b/drivers/i2c/busses/i2c-stm32f4.c @@ -0,0 +1,897 @@ +/* + * Driver for STMicroelectronics STM32 I2C controller + * + * This I2C controller is described in the STM32F429/439 Soc reference manual. + * Please see below a link to the documentation: + * http://www.st.com/resource/en/reference_manual/DM00031020.pdf + * + * Copyright (C) M'boumba Cedric Madianga 2016 + * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com> + * + * This driver is based on i2c-st.c + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +/* STM32F4 I2C offset registers */ +#define STM32F4_I2C_CR1 0x00 +#define STM32F4_I2C_CR2 0x04 +#define STM32F4_I2C_DR 0x10 +#define STM32F4_I2C_SR1 0x14 +#define STM32F4_I2C_SR2 0x18 +#define STM32F4_I2C_CCR 0x1C +#define STM32F4_I2C_TRISE 0x20 +#define STM32F4_I2C_FLTR 0x24 + +/* STM32F4 I2C control 1*/ +#define STM32F4_I2C_CR1_POS BIT(11) +#define STM32F4_I2C_CR1_ACK BIT(10) +#define STM32F4_I2C_CR1_STOP BIT(9) +#define STM32F4_I2C_CR1_START BIT(8) +#define STM32F4_I2C_CR1_PE BIT(0) + +/* STM32F4 I2C control 2 */ +#define STM32F4_I2C_CR2_FREQ_MASK GENMASK(5, 0) +#define STM32F4_I2C_CR2_FREQ(n) ((n) & STM32F4_I2C_CR2_FREQ_MASK) +#define STM32F4_I2C_CR2_ITBUFEN BIT(10) +#define STM32F4_I2C_CR2_ITEVTEN BIT(9) +#define STM32F4_I2C_CR2_ITERREN BIT(8) +#define STM32F4_I2C_CR2_IRQ_MASK (STM32F4_I2C_CR2_ITBUFEN | \ + STM32F4_I2C_CR2_ITEVTEN | \ + STM32F4_I2C_CR2_ITERREN) + +/* STM32F4 I2C Status 1 */ +#define STM32F4_I2C_SR1_AF BIT(10) +#define STM32F4_I2C_SR1_ARLO BIT(9) +#define STM32F4_I2C_SR1_BERR BIT(8) +#define STM32F4_I2C_SR1_TXE BIT(7) +#define STM32F4_I2C_SR1_RXNE BIT(6) +#define STM32F4_I2C_SR1_BTF BIT(2) +#define STM32F4_I2C_SR1_ADDR BIT(1) +#define STM32F4_I2C_SR1_SB BIT(0) +#define STM32F4_I2C_SR1_ITEVTEN_MASK (STM32F4_I2C_SR1_BTF | \ + STM32F4_I2C_SR1_ADDR | \ + STM32F4_I2C_SR1_SB) +#define STM32F4_I2C_SR1_ITBUFEN_MASK (STM32F4_I2C_SR1_TXE | \ + STM32F4_I2C_SR1_RXNE) +#define STM32F4_I2C_SR1_ITERREN_MASK (STM32F4_I2C_SR1_AF | \ + STM32F4_I2C_SR1_ARLO | \ + STM32F4_I2C_SR1_BERR) + +/* STM32F4 I2C Status 2 */ +#define STM32F4_I2C_SR2_BUSY BIT(1) + +/* STM32F4 I2C Control Clock */ +#define STM32F4_I2C_CCR_CCR_MASK GENMASK(11, 0) +#define STM32F4_I2C_CCR_CCR(n) ((n) & STM32F4_I2C_CCR_CCR_MASK) +#define STM32F4_I2C_CCR_FS BIT(15) +#define STM32F4_I2C_CCR_DUTY BIT(14) + +/* STM32F4 I2C Trise */ +#define STM32F4_I2C_TRISE_VALUE_MASK GENMASK(5, 0) +#define STM32F4_I2C_TRISE_VALUE(n) ((n) & STM32F4_I2C_TRISE_VALUE_MASK) + +#define STM32F4_I2C_MIN_STANDARD_FREQ 2U +#define STM32F4_I2C_MIN_FAST_FREQ 6U +#define STM32F4_I2C_MAX_FREQ 46U +#define HZ_TO_MHZ 1000000 + +enum stm32f4_i2c_speed { + STM32F4_I2C_SPEED_STANDARD, /* 100 kHz */ + STM32F4_I2C_SPEED_FAST, /* 400 kHz */ + STM32F4_I2C_SPEED_END, +}; + +/** + * struct stm32f4_i2c_msg - client specific data + * @addr: 8-bit slave addr, including r/w bit + * @count: number of bytes to be transferred + * @buf: data buffer + * @result: result of the transfer + * @stop: last I2C msg to be sent, i.e. STOP to be generated + */ +struct stm32f4_i2c_msg { + u8 addr; + u32 count; + u8 *buf; + int result; + bool stop; +}; + +/** + * struct stm32f4_i2c_dev - private data of the controller + * @adap: I2C adapter for this controller + * @dev: device for this controller + * @base: virtual memory area + * @complete: completion of I2C message + * @clk: hw i2c clock + * @speed: I2C clock frequency of the controller. Standard or Fast are supported + * @parent_rate: I2C clock parent rate in MHz + * @msg: I2C transfer information + */ +struct stm32f4_i2c_dev { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; + struct completion complete; + struct clk *clk; + int speed; + int parent_rate; + struct stm32f4_i2c_msg msg; +}; + +static inline void stm32f4_i2c_set_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) | mask, reg); +} + +static inline void stm32f4_i2c_clr_bits(void __iomem *reg, u32 mask) +{ + writel_relaxed(readl_relaxed(reg) & ~mask, reg); +} + +static void stm32f4_i2c_disable_irq(struct stm32f4_i2c_dev *i2c_dev) +{ + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2; + + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_IRQ_MASK); +} + +static int stm32f4_i2c_set_periph_clk_freq(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 freq; + u32 cr2 = 0; + + i2c_dev->parent_rate = clk_get_rate(i2c_dev->clk); + freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ); + + if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) { + /* + * To reach 100 kHz, the parent clk frequency should be between + * a minimum value of 2 MHz and a maximum value of 46 MHz due + * to hardware limitation + */ + if (freq < STM32F4_I2C_MIN_STANDARD_FREQ || + freq > STM32F4_I2C_MAX_FREQ) { + dev_err(i2c_dev->dev, + "bad parent clk freq for standard mode\n"); + return -EINVAL; + } + } else { + /* + * To be as close as possible to 400 kHz, the parent clk + * frequency should be between a minimum value of 6 MHz and a + * maximum value of 46 MHz due to hardware limitation + */ + if (freq < STM32F4_I2C_MIN_FAST_FREQ || + freq > STM32F4_I2C_MAX_FREQ) { + dev_err(i2c_dev->dev, + "bad parent clk freq for fast mode\n"); + return -EINVAL; + } + } + + cr2 |= STM32F4_I2C_CR2_FREQ(freq); + writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2); + + return 0; +} + +static void stm32f4_i2c_set_rise_time(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 freq = DIV_ROUND_UP(i2c_dev->parent_rate, HZ_TO_MHZ); + u32 trise; + + /* + * These bits must be programmed with the maximum SCL rise time given in + * the I2C bus specification, incremented by 1. + * + * In standard mode, the maximum allowed SCL rise time is 1000 ns. + * If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to + * 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be + * programmed with 0x9. (1000 ns / 125 ns + 1) + * So, for I2C standard mode TRISE = FREQ[5:0] + 1 + * + * In fast mode, the maximum allowed SCL rise time is 300 ns. + * If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to + * 0x08 so period = 125 ns therefore the TRISE[5:0] bits must be + * programmed with 0x3. (300 ns / 125 ns + 1) + * So, for I2C fast mode TRISE = FREQ[5:0] * 300 / 1000 + 1 + * + * Function stm32f4_i2c_set_periph_clk_freq made sure that parent rate + * is not higher than 46 MHz . As a result trise is at most 4 bits wide + * and so fits into the TRISE bits [5:0]. + */ + if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) + trise = freq + 1; + else + trise = freq * 3 / 10 + 1; + + writel_relaxed(STM32F4_I2C_TRISE_VALUE(trise), + i2c_dev->base + STM32F4_I2C_TRISE); +} + +static void stm32f4_i2c_set_speed_mode(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 val; + u32 ccr = 0; + + if (i2c_dev->speed == STM32F4_I2C_SPEED_STANDARD) { + /* + * In standard mode: + * t_scl_high = t_scl_low = CCR * I2C parent clk period + * So to reach 100 kHz, we have: + * CCR = I2C parent rate / 100 kHz >> 1 + * + * For example with parent rate = 2 MHz: + * CCR = 2000000 / (100000 << 1) = 10 + * t_scl_high = t_scl_low = 10 * (1 / 2000000) = 5000 ns + * t_scl_high + t_scl_low = 10000 ns so 100 kHz is reached + * + * Function stm32f4_i2c_set_periph_clk_freq made sure that + * parent rate is not higher than 46 MHz . As a result val + * is at most 8 bits wide and so fits into the CCR bits [11:0]. + */ + val = i2c_dev->parent_rate / (100000 << 1); + } else { + /* + * In fast mode, we compute CCR with duty = 0 as with low + * frequencies we are not able to reach 400 kHz. + * In that case: + * t_scl_high = CCR * I2C parent clk period + * t_scl_low = 2 * CCR * I2C parent clk period + * So, CCR = I2C parent rate / (400 kHz * 3) + * + * For example with parent rate = 6 MHz: + * CCR = 6000000 / (400000 * 3) = 5 + * t_scl_high = 5 * (1 / 6000000) = 833 ns > 600 ns + * t_scl_low = 2 * 5 * (1 / 6000000) = 1667 ns > 1300 ns + * t_scl_high + t_scl_low = 2500 ns so 400 kHz is reached + * + * Function stm32f4_i2c_set_periph_clk_freq made sure that + * parent rate is not higher than 46 MHz . As a result val + * is at most 6 bits wide and so fits into the CCR bits [11:0]. + */ + val = DIV_ROUND_UP(i2c_dev->parent_rate, 400000 * 3); + + /* Select Fast mode */ + ccr |= STM32F4_I2C_CCR_FS; + } + + ccr |= STM32F4_I2C_CCR_CCR(val); + writel_relaxed(ccr, i2c_dev->base + STM32F4_I2C_CCR); +} + +/** + * stm32f4_i2c_hw_config() - Prepare I2C block + * @i2c_dev: Controller's private data + */ +static int stm32f4_i2c_hw_config(struct stm32f4_i2c_dev *i2c_dev) +{ + int ret; + + ret = stm32f4_i2c_set_periph_clk_freq(i2c_dev); + if (ret) + return ret; + + stm32f4_i2c_set_rise_time(i2c_dev); + + stm32f4_i2c_set_speed_mode(i2c_dev); + + /* Enable I2C */ + writel_relaxed(STM32F4_I2C_CR1_PE, i2c_dev->base + STM32F4_I2C_CR1); + + return 0; +} + +static int stm32f4_i2c_wait_free_bus(struct stm32f4_i2c_dev *i2c_dev) +{ + u32 status; + int ret; + + ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F4_I2C_SR2, + status, + !(status & STM32F4_I2C_SR2_BUSY), + 10, 1000); + if (ret) { + dev_dbg(i2c_dev->dev, "bus not free\n"); + ret = -EBUSY; + } + + return ret; +} + +/** + * stm32f4_i2c_write_ byte() - Write a byte in the data register + * @i2c_dev: Controller's private data + * @byte: Data to write in the register + */ +static void stm32f4_i2c_write_byte(struct stm32f4_i2c_dev *i2c_dev, u8 byte) +{ + writel_relaxed(byte, i2c_dev->base + STM32F4_I2C_DR); +} + +/** + * stm32f4_i2c_write_msg() - Fill the data register in write mode + * @i2c_dev: Controller's private data + * + * This function fills the data register with I2C transfer buffer + */ +static void stm32f4_i2c_write_msg(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + + stm32f4_i2c_write_byte(i2c_dev, *msg->buf++); + msg->count--; +} + +static void stm32f4_i2c_read_msg(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + u32 rbuf; + + rbuf = readl_relaxed(i2c_dev->base + STM32F4_I2C_DR); + *msg->buf++ = rbuf; + msg->count--; +} + +static void stm32f4_i2c_terminate_xfer(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2; + + stm32f4_i2c_disable_irq(i2c_dev); + + reg = i2c_dev->base + STM32F4_I2C_CR1; + if (msg->stop) + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); + else + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); + + complete(&i2c_dev->complete); +} + +/** + * stm32f4_i2c_handle_write() - Handle FIFO empty interrupt in case of write + * @i2c_dev: Controller's private data + */ +static void stm32f4_i2c_handle_write(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2; + + if (msg->count) { + stm32f4_i2c_write_msg(i2c_dev); + if (!msg->count) { + /* + * Disable buffer interrupts for RX not empty and TX + * empty events + */ + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN); + } + } else { + stm32f4_i2c_terminate_xfer(i2c_dev); + } +} + +/** + * stm32f4_i2c_handle_read() - Handle FIFO empty interrupt in case of read + * @i2c_dev: Controller's private data + * + * This function is called when a new data is received in data register + */ +static void stm32f4_i2c_handle_read(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2; + + switch (msg->count) { + case 1: + stm32f4_i2c_disable_irq(i2c_dev); + stm32f4_i2c_read_msg(i2c_dev); + complete(&i2c_dev->complete); + break; + /* + * For 2-byte reception, 3-byte reception and for Data N-2, N-1 and N + * for N-byte reception with N > 3, we do not have to read the data + * register when RX not empty event occurs as we have to wait for byte + * transferred finished event before reading data. + * So, here we just disable buffer interrupt in order to avoid another + * system preemption due to RX not empty event. + */ + case 2: + case 3: + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR2_ITBUFEN); + break; + /* + * For N byte reception with N > 3 we directly read data register + * until N-2 data. + */ + default: + stm32f4_i2c_read_msg(i2c_dev); + } +} + +/** + * stm32f4_i2c_handle_rx_done() - Handle byte transfer finished interrupt + * in case of read + * @i2c_dev: Controller's private data + * + * This function is called when a new data is received in the shift register + * but data register has not been read yet. + */ +static void stm32f4_i2c_handle_rx_done(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg; + u32 mask; + int i; + + switch (msg->count) { + case 2: + /* + * In order to correctly send the Stop or Repeated Start + * condition on the I2C bus, the STOP/START bit has to be set + * before reading the last two bytes (data N-1 and N). + * After that, we could read the last two bytes, disable + * remaining interrupts and notify the end of xfer to the + * client + */ + reg = i2c_dev->base + STM32F4_I2C_CR1; + if (msg->stop) + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); + else + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); + + for (i = 2; i > 0; i--) + stm32f4_i2c_read_msg(i2c_dev); + + reg = i2c_dev->base + STM32F4_I2C_CR2; + mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN; + stm32f4_i2c_clr_bits(reg, mask); + + complete(&i2c_dev->complete); + break; + case 3: + /* + * In order to correctly generate the NACK pulse after the last + * received data byte, we have to enable NACK before reading N-2 + * data + */ + reg = i2c_dev->base + STM32F4_I2C_CR1; + stm32f4_i2c_clr_bits(reg, STM32F4_I2C_CR1_ACK); + stm32f4_i2c_read_msg(i2c_dev); + break; + default: + stm32f4_i2c_read_msg(i2c_dev); + } +} + +/** + * stm32f4_i2c_handle_rx_addr() - Handle address matched interrupt in case of + * master receiver + * @i2c_dev: Controller's private data + */ +static void stm32f4_i2c_handle_rx_addr(struct stm32f4_i2c_dev *i2c_dev) +{ + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + u32 cr1; + + switch (msg->count) { + case 0: + stm32f4_i2c_terminate_xfer(i2c_dev); + + /* Clear ADDR flag */ + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + break; + case 1: + /* + * Single byte reception: + * Enable NACK and reset POS (Acknowledge position). + * Then, clear ADDR flag and set STOP or RepSTART. + * In that way, the NACK and STOP or RepStart pulses will be + * sent as soon as the byte will be received in shift register + */ + cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1); + cr1 &= ~(STM32F4_I2C_CR1_ACK | STM32F4_I2C_CR1_POS); + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + + if (msg->stop) + cr1 |= STM32F4_I2C_CR1_STOP; + else + cr1 |= STM32F4_I2C_CR1_START; + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + break; + case 2: + /* + * 2-byte reception: + * Enable NACK, set POS (NACK position) and clear ADDR flag. + * In that way, NACK will be sent for the next byte which will + * be received in the shift register instead of the current + * one. + */ + cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1); + cr1 &= ~STM32F4_I2C_CR1_ACK; + cr1 |= STM32F4_I2C_CR1_POS; + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + break; + + default: + /* + * N-byte reception: + * Enable ACK, reset POS (ACK postion) and clear ADDR flag. + * In that way, ACK will be sent as soon as the current byte + * will be received in the shift register + */ + cr1 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR1); + cr1 |= STM32F4_I2C_CR1_ACK; + cr1 &= ~STM32F4_I2C_CR1_POS; + writel_relaxed(cr1, i2c_dev->base + STM32F4_I2C_CR1); + + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + break; + } +} + +/** + * stm32f4_i2c_isr_event() - Interrupt routine for I2C bus event + * @irq: interrupt number + * @data: Controller's private data + */ +static irqreturn_t stm32f4_i2c_isr_event(int irq, void *data) +{ + struct stm32f4_i2c_dev *i2c_dev = data; + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + u32 possible_status = STM32F4_I2C_SR1_ITEVTEN_MASK; + u32 status, ien, event, cr2; + + cr2 = readl_relaxed(i2c_dev->base + STM32F4_I2C_CR2); + ien = cr2 & STM32F4_I2C_CR2_IRQ_MASK; + + /* Update possible_status if buffer interrupt is enabled */ + if (ien & STM32F4_I2C_CR2_ITBUFEN) + possible_status |= STM32F4_I2C_SR1_ITBUFEN_MASK; + + status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1); + event = status & possible_status; + if (!event) { + dev_dbg(i2c_dev->dev, + "spurious evt irq (status=0x%08x, ien=0x%08x)\n", + status, ien); + return IRQ_NONE; + } + + /* Start condition generated */ + if (event & STM32F4_I2C_SR1_SB) + stm32f4_i2c_write_byte(i2c_dev, msg->addr); + + /* I2C Address sent */ + if (event & STM32F4_I2C_SR1_ADDR) { + if (msg->addr & I2C_M_RD) + stm32f4_i2c_handle_rx_addr(i2c_dev); + else + readl_relaxed(i2c_dev->base + STM32F4_I2C_SR2); + + /* + * Enable buffer interrupts for RX not empty and TX empty + * events + */ + cr2 |= STM32F4_I2C_CR2_ITBUFEN; + writel_relaxed(cr2, i2c_dev->base + STM32F4_I2C_CR2); + } + + /* TX empty */ + if ((event & STM32F4_I2C_SR1_TXE) && !(msg->addr & I2C_M_RD)) + stm32f4_i2c_handle_write(i2c_dev); + + /* RX not empty */ + if ((event & STM32F4_I2C_SR1_RXNE) && (msg->addr & I2C_M_RD)) + stm32f4_i2c_handle_read(i2c_dev); + + /* + * The BTF (Byte Transfer finished) event occurs when: + * - in reception : a new byte is received in the shift register + * but the previous byte has not been read yet from data register + * - in transmission: a new byte should be sent but the data register + * has not been written yet + */ + if (event & STM32F4_I2C_SR1_BTF) { + if (msg->addr & I2C_M_RD) + stm32f4_i2c_handle_rx_done(i2c_dev); + else + stm32f4_i2c_handle_write(i2c_dev); + } + + return IRQ_HANDLED; +} + +/** + * stm32f4_i2c_isr_error() - Interrupt routine for I2C bus error + * @irq: interrupt number + * @data: Controller's private data + */ +static irqreturn_t stm32f4_i2c_isr_error(int irq, void *data) +{ + struct stm32f4_i2c_dev *i2c_dev = data; + struct stm32f4_i2c_msg *msg = &i2c_dev->msg; + void __iomem *reg; + u32 status; + + status = readl_relaxed(i2c_dev->base + STM32F4_I2C_SR1); + + /* Arbitration lost */ + if (status & STM32F4_I2C_SR1_ARLO) { + status &= ~STM32F4_I2C_SR1_ARLO; + writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1); + msg->result = -EAGAIN; + } + + /* + * Acknowledge failure: + * In master transmitter mode a Stop must be generated by software + */ + if (status & STM32F4_I2C_SR1_AF) { + if (!(msg->addr & I2C_M_RD)) { + reg = i2c_dev->base + STM32F4_I2C_CR1; + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_STOP); + } + status &= ~STM32F4_I2C_SR1_AF; + writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1); + msg->result = -EIO; + } + + /* Bus error */ + if (status & STM32F4_I2C_SR1_BERR) { + status &= ~STM32F4_I2C_SR1_BERR; + writel_relaxed(status, i2c_dev->base + STM32F4_I2C_SR1); + msg->result = -EIO; + } + + stm32f4_i2c_disable_irq(i2c_dev); + complete(&i2c_dev->complete); + + return IRQ_HANDLED; +} + +/** + * stm32f4_i2c_xfer_msg() - Transfer a single I2C message + * @i2c_dev: Controller's private data + * @msg: I2C message to transfer + * @is_first: first message of the sequence + * @is_last: last message of the sequence + */ +static int stm32f4_i2c_xfer_msg(struct stm32f4_i2c_dev *i2c_dev, + struct i2c_msg *msg, bool is_first, + bool is_last) +{ + struct stm32f4_i2c_msg *f4_msg = &i2c_dev->msg; + void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR1; + unsigned long timeout; + u32 mask; + int ret; + + f4_msg->addr = i2c_8bit_addr_from_msg(msg); + f4_msg->buf = msg->buf; + f4_msg->count = msg->len; + f4_msg->result = 0; + f4_msg->stop = is_last; + + reinit_completion(&i2c_dev->complete); + + /* Enable events and errors interrupts */ + mask = STM32F4_I2C_CR2_ITEVTEN | STM32F4_I2C_CR2_ITERREN; + stm32f4_i2c_set_bits(i2c_dev->base + STM32F4_I2C_CR2, mask); + + if (is_first) { + ret = stm32f4_i2c_wait_free_bus(i2c_dev); + if (ret) + return ret; + + /* START generation */ + stm32f4_i2c_set_bits(reg, STM32F4_I2C_CR1_START); + } + + timeout = wait_for_completion_timeout(&i2c_dev->complete, + i2c_dev->adap.timeout); + ret = f4_msg->result; + + if (!timeout) + ret = -ETIMEDOUT; + + return ret; +} + +/** + * stm32f4_i2c_xfer() - Transfer combined I2C message + * @i2c_adap: Adapter pointer to the controller + * @msgs: Pointer to data to be written. + * @num: Number of messages to be executed + */ +static int stm32f4_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], + int num) +{ + struct stm32f4_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + int ret, i; + + ret = clk_enable(i2c_dev->clk); + if (ret) { + dev_err(i2c_dev->dev, "Failed to enable clock\n"); + return ret; + } + + for (i = 0; i < num && !ret; i++) + ret = stm32f4_i2c_xfer_msg(i2c_dev, &msgs[i], i == 0, + i == num - 1); + + clk_disable(i2c_dev->clk); + + return (ret < 0) ? ret : num; +} + +static u32 stm32f4_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm stm32f4_i2c_algo = { + .master_xfer = stm32f4_i2c_xfer, + .functionality = stm32f4_i2c_func, +}; + +static int stm32f4_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct stm32f4_i2c_dev *i2c_dev; + struct resource *res; + u32 irq_event, irq_error, clk_rate; + struct i2c_adapter *adap; + struct reset_control *rst; + int ret; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c_dev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c_dev->base)) + return PTR_ERR(i2c_dev->base); + + irq_event = irq_of_parse_and_map(np, 0); + if (!irq_event) { + dev_err(&pdev->dev, "IRQ event missing or invalid\n"); + return -EINVAL; + } + + irq_error = irq_of_parse_and_map(np, 1); + if (!irq_error) { + dev_err(&pdev->dev, "IRQ error missing or invalid\n"); + return -EINVAL; + } + + i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c_dev->clk)) { + dev_err(&pdev->dev, "Error: Missing controller clock\n"); + return PTR_ERR(i2c_dev->clk); + } + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) { + dev_err(i2c_dev->dev, "Failed to prepare_enable clock\n"); + return ret; + } + + rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(rst)) { + dev_err(&pdev->dev, "Error: Missing controller reset\n"); + ret = PTR_ERR(rst); + goto clk_free; + } + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + + i2c_dev->speed = STM32F4_I2C_SPEED_STANDARD; + ret = of_property_read_u32(np, "clock-frequency", &clk_rate); + if (!ret && clk_rate >= 400000) + i2c_dev->speed = STM32F4_I2C_SPEED_FAST; + + i2c_dev->dev = &pdev->dev; + + ret = devm_request_irq(&pdev->dev, irq_event, stm32f4_i2c_isr_event, 0, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq event %i\n", + irq_event); + goto clk_free; + } + + ret = devm_request_irq(&pdev->dev, irq_error, stm32f4_i2c_isr_error, 0, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq error %i\n", + irq_error); + goto clk_free; + } + + ret = stm32f4_i2c_hw_config(i2c_dev); + if (ret) + goto clk_free; + + adap = &i2c_dev->adap; + i2c_set_adapdata(adap, i2c_dev); + snprintf(adap->name, sizeof(adap->name), "STM32 I2C(%pa)", &res->start); + adap->owner = THIS_MODULE; + adap->timeout = 2 * HZ; + adap->retries = 0; + adap->algo = &stm32f4_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + init_completion(&i2c_dev->complete); + + ret = i2c_add_adapter(adap); + if (ret) + goto clk_free; + + platform_set_drvdata(pdev, i2c_dev); + + clk_disable(i2c_dev->clk); + + dev_info(i2c_dev->dev, "STM32F4 I2C driver registered\n"); + + return 0; + +clk_free: + clk_disable_unprepare(i2c_dev->clk); + return ret; +} + +static int stm32f4_i2c_remove(struct platform_device *pdev) +{ + struct stm32f4_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c_dev->adap); + + clk_unprepare(i2c_dev->clk); + + return 0; +} + +static const struct of_device_id stm32f4_i2c_match[] = { + { .compatible = "st,stm32f4-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32f4_i2c_match); + +static struct platform_driver stm32f4_i2c_driver = { + .driver = { + .name = "stm32f4-i2c", + .of_match_table = stm32f4_i2c_match, + }, + .probe = stm32f4_i2c_probe, + .remove = stm32f4_i2c_remove, +}; + +module_platform_driver(stm32f4_i2c_driver); + +MODULE_AUTHOR("M'boumba Cedric Madianga <cedric.madianga@gmail.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32F4 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c new file mode 100644 index 000000000000..9eed69d5e17e --- /dev/null +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -0,0 +1,346 @@ +/* + * drivers/i2c/busses/i2c-tegra-bpmp.c + * + * Copyright (c) 2016 NVIDIA Corporation. All rights reserved. + * + * Author: Shardar Shariff Md <smohammed@nvidia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include <soc/tegra/bpmp-abi.h> +#include <soc/tegra/bpmp.h> + +/* + * Serialized I2C message header size is 6 bytes and includes address, flags + * and length + */ +#define SERIALI2C_HDR_SIZE 6 + +struct tegra_bpmp_i2c { + struct i2c_adapter adapter; + struct device *dev; + + struct tegra_bpmp *bpmp; + unsigned int bus; +}; + +/* + * Linux flags are translated to BPMP defined I2C flags that are used in BPMP + * firmware I2C driver to avoid any issues in future if Linux I2C flags are + * changed. + */ +static int tegra_bpmp_xlate_flags(u16 flags, u16 *out) +{ + if (flags & I2C_M_TEN) { + *out |= SERIALI2C_TEN; + flags &= ~I2C_M_TEN; + } + + if (flags & I2C_M_RD) { + *out |= SERIALI2C_RD; + flags &= ~I2C_M_RD; + } + + if (flags & I2C_M_STOP) { + *out |= SERIALI2C_STOP; + flags &= ~I2C_M_STOP; + } + + if (flags & I2C_M_NOSTART) { + *out |= SERIALI2C_NOSTART; + flags &= ~I2C_M_NOSTART; + } + + if (flags & I2C_M_REV_DIR_ADDR) { + *out |= SERIALI2C_REV_DIR_ADDR; + flags &= ~I2C_M_REV_DIR_ADDR; + } + + if (flags & I2C_M_IGNORE_NAK) { + *out |= SERIALI2C_IGNORE_NAK; + flags &= ~I2C_M_IGNORE_NAK; + } + + if (flags & I2C_M_NO_RD_ACK) { + *out |= SERIALI2C_NO_RD_ACK; + flags &= ~I2C_M_NO_RD_ACK; + } + + if (flags & I2C_M_RECV_LEN) { + *out |= SERIALI2C_RECV_LEN; + flags &= ~I2C_M_RECV_LEN; + } + + return (flags != 0) ? -EINVAL : 0; +} + +/** + * The serialized I2C format is simply the following: + * [addr little-endian][flags little-endian][len little-endian][data if write] + * [addr little-endian][flags little-endian][len little-endian][data if write] + * ... + * + * The flags are translated from Linux kernel representation to seriali2c + * representation. Any undefined flag being set causes an error. + * + * The data is there only for writes. Reads have the data transferred in the + * other direction, and thus data is not present. + * + * See deserialize_i2c documentation for the data format in the other direction. + */ +static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c, + struct mrq_i2c_request *request, + struct i2c_msg *msgs, + unsigned int num) +{ + char *buf = request->xfer.data_buf; + unsigned int i, j, pos = 0; + int err; + + for (i = 0; i < num; i++) { + struct i2c_msg *msg = &msgs[i]; + u16 flags = 0; + + err = tegra_bpmp_xlate_flags(msg->flags, &flags); + if (err < 0) + return err; + + buf[pos++] = msg->addr & 0xff; + buf[pos++] = (msg->addr & 0xff00) >> 8; + buf[pos++] = flags & 0xff; + buf[pos++] = (flags & 0xff00) >> 8; + buf[pos++] = msg->len & 0xff; + buf[pos++] = (msg->len & 0xff00) >> 8; + + if ((flags & SERIALI2C_RD) == 0) { + for (j = 0; j < msg->len; j++) + buf[pos++] = msg->buf[j]; + } + } + + request->xfer.data_size = pos; + + return 0; +} + +/** + * The data in the BPMP -> CPU direction is composed of sequential blocks for + * those messages that have I2C_M_RD. So, for example, if you have: + * + * - !I2C_M_RD, len == 5, data == a0 01 02 03 04 + * - !I2C_M_RD, len == 1, data == a0 + * - I2C_M_RD, len == 2, data == [uninitialized buffer 1] + * - !I2C_M_RD, len == 1, data == a2 + * - I2C_M_RD, len == 2, data == [uninitialized buffer 2] + * + * ...then the data in the BPMP -> CPU direction would be 4 bytes total, and + * would contain 2 bytes that will go to uninitialized buffer 1, and 2 bytes + * that will go to uninitialized buffer 2. + */ +static int tegra_bpmp_i2c_deserialize(struct tegra_bpmp_i2c *i2c, + struct mrq_i2c_response *response, + struct i2c_msg *msgs, + unsigned int num) +{ + size_t size = response->xfer.data_size, len = 0, pos = 0; + char *buf = response->xfer.data_buf; + unsigned int i; + + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RD) + len += msgs[i].len; + + if (len != size) + return -EINVAL; + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_RD) { + memcpy(msgs[i].buf, buf + pos, msgs[i].len); + pos += msgs[i].len; + } + } + + return 0; +} + +static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num) +{ + size_t tx_len = 0, rx_len = 0; + unsigned int i; + + for (i = 0; i < num; i++) + if (!(msgs[i].flags & I2C_M_RD)) + tx_len += SERIALI2C_HDR_SIZE + msgs[i].len; + + if (tx_len > TEGRA_I2C_IPC_MAX_IN_BUF_SIZE) + return -EINVAL; + + for (i = 0; i < num; i++) + if ((msgs[i].flags & I2C_M_RD)) + rx_len += msgs[i].len; + + if (rx_len > TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE) + return -EINVAL; + + return 0; +} + +static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c, + struct mrq_i2c_request *request, + struct mrq_i2c_response *response) +{ + struct tegra_bpmp_message msg; + int err; + + request->cmd = CMD_I2C_XFER; + request->xfer.bus_id = i2c->bus; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_I2C; + msg.tx.data = request; + msg.tx.size = sizeof(*request); + msg.rx.data = response; + msg.rx.size = sizeof(*response); + + if (irqs_disabled()) + err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg); + else + err = tegra_bpmp_transfer(i2c->bpmp, &msg); + + return err; +} + +static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter); + struct mrq_i2c_response response; + struct mrq_i2c_request request; + int err; + + err = tegra_bpmp_i2c_msg_len_check(msgs, num); + if (err < 0) { + dev_err(i2c->dev, "unsupported message length\n"); + return err; + } + + memset(&request, 0, sizeof(request)); + memset(&response, 0, sizeof(response)); + + err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num); + if (err < 0) { + dev_err(i2c->dev, "failed to serialize message: %d\n", err); + return err; + } + + err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response); + if (err < 0) { + dev_err(i2c->dev, "failed to transfer message: %d\n", err); + return err; + } + + err = tegra_bpmp_i2c_deserialize(i2c, &response, msgs, num); + if (err < 0) { + dev_err(i2c->dev, "failed to deserialize message: %d\n", err); + return err; + } + + return num; +} + +static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART; +} + +static const struct i2c_algorithm tegra_bpmp_i2c_algo = { + .master_xfer = tegra_bpmp_i2c_xfer, + .functionality = tegra_bpmp_i2c_func, +}; + +static int tegra_bpmp_i2c_probe(struct platform_device *pdev) +{ + struct tegra_bpmp_i2c *i2c; + u32 value; + int err; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->dev = &pdev->dev; + + i2c->bpmp = dev_get_drvdata(pdev->dev.parent); + if (!i2c->bpmp) + return -ENODEV; + + err = of_property_read_u32(pdev->dev.of_node, "nvidia,bpmp-bus-id", + &value); + if (err < 0) + return err; + + i2c->bus = value; + + i2c_set_adapdata(&i2c->adapter, i2c); + i2c->adapter.owner = THIS_MODULE; + strlcpy(i2c->adapter.name, "Tegra BPMP I2C adapter", + sizeof(i2c->adapter.name)); + i2c->adapter.algo = &tegra_bpmp_i2c_algo; + i2c->adapter.dev.parent = &pdev->dev; + i2c->adapter.dev.of_node = pdev->dev.of_node; + + platform_set_drvdata(pdev, i2c); + + return i2c_add_adapter(&i2c->adapter); +} + +static int tegra_bpmp_i2c_remove(struct platform_device *pdev) +{ + struct tegra_bpmp_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adapter); + + return 0; +} + +static const struct of_device_id tegra_bpmp_i2c_of_match[] = { + { .compatible = "nvidia,tegra186-bpmp-i2c", }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_bpmp_i2c_of_match); + +static struct platform_driver tegra_bpmp_i2c_driver = { + .driver = { + .name = "tegra-bpmp-i2c", + .of_match_table = tegra_bpmp_i2c_of_match, + }, + .probe = tegra_bpmp_i2c_probe, + .remove = tegra_bpmp_i2c_remove, +}; +module_platform_driver(tegra_bpmp_i2c_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra BPMP I2C bus contoller driver"); +MODULE_AUTHOR("Shardar Shariff Md <smohammed@nvidia.com>"); +MODULE_AUTHOR("Juha-Matti Tilli"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c index bba5b429f69c..1d4c2beacf2e 100644 --- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -188,11 +188,11 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev, i2c->hlc_int_enable = thunder_i2c_hlc_int_enable; i2c->hlc_int_disable = thunder_i2c_hlc_int_disable; - ret = pci_enable_msix(pdev, &i2c->i2c_msix, 1); - if (ret) + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX); + if (ret < 0) goto error; - ret = devm_request_irq(dev, i2c->i2c_msix.vector, octeon_i2c_isr, 0, + ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), octeon_i2c_isr, 0, DRV_NAME, i2c); if (ret) goto error; diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index 0ab1e55558bc..dbe7e44c9321 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -372,7 +372,7 @@ static u32 xgene_slimpro_i2c_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_I2C_BLOCK; } -static struct i2c_algorithm xgene_slimpro_i2c_algorithm = { +static const struct i2c_algorithm xgene_slimpro_i2c_algorithm = { .smbus_xfer = xgene_slimpro_i2c_xfer, .functionality = xgene_slimpro_i2c_func, }; diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index 84a8b2eccffb..66b464d52c9c 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -334,7 +334,7 @@ static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter) I2C_FUNC_10BIT_ADDR; } -static struct i2c_algorithm xlp9xx_i2c_algo = { +static const struct i2c_algorithm xlp9xx_i2c_algo = { .master_xfer = xlp9xx_i2c_xfer, .functionality = xlp9xx_i2c_functionality, }; diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c index ad17d88d8573..484bfa15d58e 100644 --- a/drivers/i2c/busses/i2c-xlr.c +++ b/drivers/i2c/busses/i2c-xlr.c @@ -335,7 +335,7 @@ static u32 xlr_func(struct i2c_adapter *adap) return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C; } -static struct i2c_algorithm xlr_i2c_algo = { +static const struct i2c_algorithm xlr_i2c_algo = { .master_xfer = xlr_i2c_xfer, .functionality = xlr_func, }; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index bfb6ba7cac00..d2402bbf6729 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -3705,6 +3705,39 @@ int i2c_slave_unregister(struct i2c_client *client) return ret; } EXPORT_SYMBOL_GPL(i2c_slave_unregister); + +/** + * i2c_detect_slave_mode - detect operation mode + * @dev: The device owning the bus + * + * This checks the device nodes for an I2C slave by checking the address + * used in the reg property. If the address match the I2C_OWN_SLAVE_ADDRESS + * flag this means the device is configured to act as a I2C slave and it will + * be listening at that address. + * + * Returns true if an I2C own slave address is detected, otherwise returns + * false. + */ +bool i2c_detect_slave_mode(struct device *dev) +{ + if (IS_BUILTIN(CONFIG_OF) && dev->of_node) { + struct device_node *child; + u32 reg; + + for_each_child_of_node(dev->of_node, child) { + of_property_read_u32(child, "reg", ®); + if (reg & I2C_OWN_SLAVE_ADDRESS) { + of_node_put(child); + return true; + } + } + } else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) { + dev_dbg(dev, "ACPI slave is not supported yet\n"); + } + return false; +} +EXPORT_SYMBOL_GPL(i2c_detect_slave_mode); + #endif MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); diff --git a/drivers/i2c/muxes/i2c-mux-mlxcpld.c b/drivers/i2c/muxes/i2c-mux-mlxcpld.c index b7ca249ec9c3..e53f2abd1350 100644 --- a/drivers/i2c/muxes/i2c-mux-mlxcpld.c +++ b/drivers/i2c/muxes/i2c-mux-mlxcpld.c @@ -40,7 +40,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/version.h> #include <linux/i2c/mlxcpld.h> #define CPLD_MUX_MAX_NCHANS 8 diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 4ea7e691afc7..77840f7845a1 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -90,6 +90,7 @@ static const struct of_device_id pca9541_of_match[] = { { .compatible = "nxp,pca9541" }, {} }; +MODULE_DEVICE_TABLE(of, pca9541_of_match); #endif /* diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index dd18b9ccb1f4..dfc1c0e37c40 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -41,14 +41,20 @@ #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/i2c/pca954x.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/pm.h> #include <linux/slab.h> +#include <linux/spinlock.h> #define PCA954X_MAX_NCHANS 8 +#define PCA954X_IRQ_OFFSET 4 + enum pca_type { pca_9540, pca_9542, @@ -63,6 +69,7 @@ enum pca_type { struct chip_desc { u8 nchans; u8 enable; /* used for muxes only */ + u8 has_irq; enum muxtype { pca954x_ismux = 0, pca954x_isswi @@ -75,6 +82,10 @@ struct pca954x { u8 last_chan; /* last register value */ u8 deselect; struct i2c_client *client; + + struct irq_domain *irq; + unsigned int irq_mask; + spinlock_t lock; }; /* Provide specs for the PCA954x types we know about */ @@ -84,17 +95,26 @@ static const struct chip_desc chips[] = { .enable = 0x4, .muxtype = pca954x_ismux, }, + [pca_9542] = { + .nchans = 2, + .enable = 0x4, + .has_irq = 1, + .muxtype = pca954x_ismux, + }, [pca_9543] = { .nchans = 2, + .has_irq = 1, .muxtype = pca954x_isswi, }, [pca_9544] = { .nchans = 4, .enable = 0x4, + .has_irq = 1, .muxtype = pca954x_ismux, }, [pca_9545] = { .nchans = 4, + .has_irq = 1, .muxtype = pca954x_isswi, }, [pca_9547] = { @@ -110,7 +130,7 @@ static const struct chip_desc chips[] = { static const struct i2c_device_id pca954x_id[] = { { "pca9540", pca_9540 }, - { "pca9542", pca_9540 }, + { "pca9542", pca_9542 }, { "pca9543", pca_9543 }, { "pca9544", pca_9544 }, { "pca9545", pca_9545 }, @@ -124,7 +144,7 @@ MODULE_DEVICE_TABLE(i2c, pca954x_id); #ifdef CONFIG_ACPI static const struct acpi_device_id pca954x_acpi_ids[] = { { .id = "PCA9540", .driver_data = pca_9540 }, - { .id = "PCA9542", .driver_data = pca_9540 }, + { .id = "PCA9542", .driver_data = pca_9542 }, { .id = "PCA9543", .driver_data = pca_9543 }, { .id = "PCA9544", .driver_data = pca_9544 }, { .id = "PCA9545", .driver_data = pca_9545 }, @@ -148,6 +168,7 @@ static const struct of_device_id pca954x_of_match[] = { { .compatible = "nxp,pca9548", .data = &chips[pca_9548] }, {} }; +MODULE_DEVICE_TABLE(of, pca954x_of_match); #endif /* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer() @@ -217,6 +238,114 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan) return pca954x_reg_write(muxc->parent, client, data->last_chan); } +static irqreturn_t pca954x_irq_handler(int irq, void *dev_id) +{ + struct pca954x *data = dev_id; + unsigned int child_irq; + int ret, i, handled = 0; + + ret = i2c_smbus_read_byte(data->client); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < data->chip->nchans; i++) { + if (ret & BIT(PCA954X_IRQ_OFFSET + i)) { + child_irq = irq_linear_revmap(data->irq, i); + handle_nested_irq(child_irq); + handled++; + } + } + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static void pca954x_irq_mask(struct irq_data *idata) +{ + struct pca954x *data = irq_data_get_irq_chip_data(idata); + unsigned int pos = idata->hwirq; + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + + data->irq_mask &= ~BIT(pos); + if (!data->irq_mask) + disable_irq(data->client->irq); + + spin_unlock_irqrestore(&data->lock, flags); +} + +static void pca954x_irq_unmask(struct irq_data *idata) +{ + struct pca954x *data = irq_data_get_irq_chip_data(idata); + unsigned int pos = idata->hwirq; + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + + if (!data->irq_mask) + enable_irq(data->client->irq); + data->irq_mask |= BIT(pos); + + spin_unlock_irqrestore(&data->lock, flags); +} + +static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type) +{ + if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW) + return -EINVAL; + return 0; +} + +static struct irq_chip pca954x_irq_chip = { + .name = "i2c-mux-pca954x", + .irq_mask = pca954x_irq_mask, + .irq_unmask = pca954x_irq_unmask, + .irq_set_type = pca954x_irq_set_type, +}; + +static int pca954x_irq_setup(struct i2c_mux_core *muxc) +{ + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + int c, err, irq; + + if (!data->chip->has_irq || client->irq <= 0) + return 0; + + spin_lock_init(&data->lock); + + data->irq = irq_domain_add_linear(client->dev.of_node, + data->chip->nchans, + &irq_domain_simple_ops, data); + if (!data->irq) + return -ENODEV; + + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_create_mapping(data->irq, c); + irq_set_chip_data(irq, data); + irq_set_chip_and_handler(irq, &pca954x_irq_chip, + handle_simple_irq); + } + + err = devm_request_threaded_irq(&client->dev, data->client->irq, NULL, + pca954x_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + "pca954x", data); + if (err) + goto err_req_irq; + + disable_irq(data->client->irq); + + return 0; +err_req_irq: + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_find_mapping(data->irq, c); + irq_dispose_mapping(irq); + } + irq_domain_remove(data->irq); + + return err; +} + /* * I2C init/probing/exit functions */ @@ -281,6 +410,10 @@ static int pca954x_probe(struct i2c_client *client, idle_disconnect_dt = of_node && of_property_read_bool(of_node, "i2c-mux-idle-disconnect"); + ret = pca954x_irq_setup(muxc); + if (ret) + goto fail_del_adapters; + /* Now create an adapter for each channel */ for (num = 0; num < data->chip->nchans; num++) { bool idle_disconnect_pd = false; @@ -306,7 +439,7 @@ static int pca954x_probe(struct i2c_client *client, dev_err(&client->dev, "failed to register multiplexed adapter" " %d as bus %d\n", num, force); - goto virt_reg_failed; + goto fail_del_adapters; } } @@ -317,7 +450,7 @@ static int pca954x_probe(struct i2c_client *client, return 0; -virt_reg_failed: +fail_del_adapters: i2c_mux_del_adapters(muxc); return ret; } @@ -325,6 +458,16 @@ virt_reg_failed: static int pca954x_remove(struct i2c_client *client) { struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + int c, irq; + + if (data->irq) { + for (c = 0; c < data->chip->nchans; c++) { + irq = irq_find_mapping(data->irq, c); + irq_dispose_mapping(irq); + } + irq_domain_remove(data->irq); + } i2c_mux_del_adapters(muxc); return 0; diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index b6940992a6ff..968038482d2f 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -447,7 +447,7 @@ void ide_acpi_get_timing(ide_hwif_t *hwif) memcpy(&hwif->acpidata->gtm, out_obj->buffer.pointer, sizeof(struct GTM_buffer)); - DEBPRINT("_GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%Zx\n", + DEBPRINT("_GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%zx\n", out_obj->buffer.pointer, out_obj->buffer.length, sizeof(struct GTM_buffer)); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index aef00511ca86..74f1b7dc03f7 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -28,6 +28,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/sched/task_stack.h> #include <linux/delay.h> #include <linux/timer.h> #include <linux/seq_file.h> diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 3c1b7974d66d..d8a552b47718 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1136,7 +1136,7 @@ static ssize_t idetape_chrdev_read(struct file *file, char __user *buf, ssize_t ret = 0; int rc; - ide_debug_log(IDE_DBG_FUNC, "count %Zd", count); + ide_debug_log(IDE_DBG_FUNC, "count %zd", count); if (tape->chrdev_dir != IDETAPE_DIR_READ) { if (test_bit(ilog2(IDE_AFLAG_DETECT_BS), &drive->atapi_flags)) @@ -1195,7 +1195,7 @@ static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf, if (tape->write_prot) return -EACCES; - ide_debug_log(IDE_DBG_FUNC, "count %Zd", count); + ide_debug_log(IDE_DBG_FUNC, "count %zd", count); /* Initialize write operation */ rc = idetape_init_rw(drive, IDETAPE_DIR_WRITE); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 247b9faccce1..4c0007cb74e3 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -19,6 +19,7 @@ #include <linux/delay.h> #include <linux/hdreg.h> #include <linux/ide.h> +#include <linux/nmi.h> #include <linux/scatterlist.h> #include <linux/uaccess.h> diff --git a/drivers/ide/palm_bk3710.c b/drivers/ide/palm_bk3710.c index 46427ea01753..157f2d1fb7e1 100644 --- a/drivers/ide/palm_bk3710.c +++ b/drivers/ide/palm_bk3710.c @@ -300,7 +300,7 @@ static const struct ide_port_ops palm_bk3710_ports_ops = { .cable_detect = palm_bk3710_cable_detect, }; -static struct ide_port_info palm_bk3710_port_info = { +static struct ide_port_info palm_bk3710_port_info __initdata = { .init_dma = palm_bk3710_init_dma, .port_ops = &palm_bk3710_ports_ops, .dma_ops = &sff_dma_ops, diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 7d8ea3d5fda6..5805b041dd0f 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -125,7 +125,7 @@ static struct cpuidle_state *cpuidle_state_table; */ static struct cpuidle_state nehalem_cstates[] = { { - .name = "C1-NHM", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 3, @@ -133,7 +133,7 @@ static struct cpuidle_state nehalem_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-NHM", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -141,7 +141,7 @@ static struct cpuidle_state nehalem_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-NHM", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 20, @@ -149,7 +149,7 @@ static struct cpuidle_state nehalem_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-NHM", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 200, @@ -162,7 +162,7 @@ static struct cpuidle_state nehalem_cstates[] = { static struct cpuidle_state snb_cstates[] = { { - .name = "C1-SNB", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -170,7 +170,7 @@ static struct cpuidle_state snb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-SNB", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -178,7 +178,7 @@ static struct cpuidle_state snb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-SNB", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 80, @@ -186,7 +186,7 @@ static struct cpuidle_state snb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-SNB", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 104, @@ -194,7 +194,7 @@ static struct cpuidle_state snb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7-SNB", + .name = "C7", .desc = "MWAIT 0x30", .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 109, @@ -207,7 +207,7 @@ static struct cpuidle_state snb_cstates[] = { static struct cpuidle_state byt_cstates[] = { { - .name = "C1-BYT", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -215,7 +215,7 @@ static struct cpuidle_state byt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6N-BYT", + .name = "C6N", .desc = "MWAIT 0x58", .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 300, @@ -223,7 +223,7 @@ static struct cpuidle_state byt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6S-BYT", + .name = "C6S", .desc = "MWAIT 0x52", .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 500, @@ -231,7 +231,7 @@ static struct cpuidle_state byt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7-BYT", + .name = "C7", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 1200, @@ -239,7 +239,7 @@ static struct cpuidle_state byt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7S-BYT", + .name = "C7S", .desc = "MWAIT 0x64", .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 10000, @@ -252,7 +252,7 @@ static struct cpuidle_state byt_cstates[] = { static struct cpuidle_state cht_cstates[] = { { - .name = "C1-CHT", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -260,7 +260,7 @@ static struct cpuidle_state cht_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6N-CHT", + .name = "C6N", .desc = "MWAIT 0x58", .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 80, @@ -268,7 +268,7 @@ static struct cpuidle_state cht_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6S-CHT", + .name = "C6S", .desc = "MWAIT 0x52", .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 200, @@ -276,7 +276,7 @@ static struct cpuidle_state cht_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7-CHT", + .name = "C7", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 1200, @@ -284,7 +284,7 @@ static struct cpuidle_state cht_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7S-CHT", + .name = "C7S", .desc = "MWAIT 0x64", .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 10000, @@ -297,7 +297,7 @@ static struct cpuidle_state cht_cstates[] = { static struct cpuidle_state ivb_cstates[] = { { - .name = "C1-IVB", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -305,7 +305,7 @@ static struct cpuidle_state ivb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-IVB", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -313,7 +313,7 @@ static struct cpuidle_state ivb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-IVB", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, @@ -321,7 +321,7 @@ static struct cpuidle_state ivb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-IVB", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 80, @@ -329,7 +329,7 @@ static struct cpuidle_state ivb_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7-IVB", + .name = "C7", .desc = "MWAIT 0x30", .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 87, @@ -342,7 +342,7 @@ static struct cpuidle_state ivb_cstates[] = { static struct cpuidle_state ivt_cstates[] = { { - .name = "C1-IVT", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -350,7 +350,7 @@ static struct cpuidle_state ivt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-IVT", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -358,7 +358,7 @@ static struct cpuidle_state ivt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-IVT", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, @@ -366,7 +366,7 @@ static struct cpuidle_state ivt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-IVT", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 82, @@ -379,7 +379,7 @@ static struct cpuidle_state ivt_cstates[] = { static struct cpuidle_state ivt_cstates_4s[] = { { - .name = "C1-IVT-4S", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -387,7 +387,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-IVT-4S", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -395,7 +395,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-IVT-4S", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, @@ -403,7 +403,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-IVT-4S", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 84, @@ -416,7 +416,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { static struct cpuidle_state ivt_cstates_8s[] = { { - .name = "C1-IVT-8S", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -424,7 +424,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-IVT-8S", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -432,7 +432,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-IVT-8S", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 59, @@ -440,7 +440,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-IVT-8S", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 88, @@ -453,7 +453,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { static struct cpuidle_state hsw_cstates[] = { { - .name = "C1-HSW", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -461,7 +461,7 @@ static struct cpuidle_state hsw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-HSW", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -469,7 +469,7 @@ static struct cpuidle_state hsw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-HSW", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 33, @@ -477,7 +477,7 @@ static struct cpuidle_state hsw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-HSW", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 133, @@ -485,7 +485,7 @@ static struct cpuidle_state hsw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7s-HSW", + .name = "C7s", .desc = "MWAIT 0x32", .flags = MWAIT2flg(0x32) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 166, @@ -493,7 +493,7 @@ static struct cpuidle_state hsw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C8-HSW", + .name = "C8", .desc = "MWAIT 0x40", .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 300, @@ -501,7 +501,7 @@ static struct cpuidle_state hsw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C9-HSW", + .name = "C9", .desc = "MWAIT 0x50", .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 600, @@ -509,7 +509,7 @@ static struct cpuidle_state hsw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C10-HSW", + .name = "C10", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 2600, @@ -521,7 +521,7 @@ static struct cpuidle_state hsw_cstates[] = { }; static struct cpuidle_state bdw_cstates[] = { { - .name = "C1-BDW", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -529,7 +529,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-BDW", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -537,7 +537,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-BDW", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 40, @@ -545,7 +545,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-BDW", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 133, @@ -553,7 +553,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7s-BDW", + .name = "C7s", .desc = "MWAIT 0x32", .flags = MWAIT2flg(0x32) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 166, @@ -561,7 +561,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C8-BDW", + .name = "C8", .desc = "MWAIT 0x40", .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 300, @@ -569,7 +569,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C9-BDW", + .name = "C9", .desc = "MWAIT 0x50", .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 600, @@ -577,7 +577,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C10-BDW", + .name = "C10", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 2600, @@ -590,7 +590,7 @@ static struct cpuidle_state bdw_cstates[] = { static struct cpuidle_state skl_cstates[] = { { - .name = "C1-SKL", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -598,7 +598,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-SKL", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -606,7 +606,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C3-SKL", + .name = "C3", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 70, @@ -614,7 +614,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-SKL", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 85, @@ -622,7 +622,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7s-SKL", + .name = "C7s", .desc = "MWAIT 0x33", .flags = MWAIT2flg(0x33) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 124, @@ -630,7 +630,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C8-SKL", + .name = "C8", .desc = "MWAIT 0x40", .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 200, @@ -638,7 +638,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C9-SKL", + .name = "C9", .desc = "MWAIT 0x50", .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 480, @@ -646,7 +646,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C10-SKL", + .name = "C10", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 890, @@ -659,7 +659,7 @@ static struct cpuidle_state skl_cstates[] = { static struct cpuidle_state skx_cstates[] = { { - .name = "C1-SKX", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -667,7 +667,7 @@ static struct cpuidle_state skx_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-SKX", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -675,7 +675,7 @@ static struct cpuidle_state skx_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-SKX", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 133, @@ -688,7 +688,7 @@ static struct cpuidle_state skx_cstates[] = { static struct cpuidle_state atom_cstates[] = { { - .name = "C1E-ATM", + .name = "C1E", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 10, @@ -696,7 +696,7 @@ static struct cpuidle_state atom_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C2-ATM", + .name = "C2", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10), .exit_latency = 20, @@ -704,7 +704,7 @@ static struct cpuidle_state atom_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C4-ATM", + .name = "C4", .desc = "MWAIT 0x30", .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 100, @@ -712,7 +712,7 @@ static struct cpuidle_state atom_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-ATM", + .name = "C6", .desc = "MWAIT 0x52", .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 140, @@ -724,7 +724,7 @@ static struct cpuidle_state atom_cstates[] = { }; static struct cpuidle_state tangier_cstates[] = { { - .name = "C1-TNG", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -732,7 +732,7 @@ static struct cpuidle_state tangier_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C4-TNG", + .name = "C4", .desc = "MWAIT 0x30", .flags = MWAIT2flg(0x30) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 100, @@ -740,7 +740,7 @@ static struct cpuidle_state tangier_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-TNG", + .name = "C6", .desc = "MWAIT 0x52", .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 140, @@ -748,7 +748,7 @@ static struct cpuidle_state tangier_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7-TNG", + .name = "C7", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 1200, @@ -756,7 +756,7 @@ static struct cpuidle_state tangier_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C9-TNG", + .name = "C9", .desc = "MWAIT 0x64", .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 10000, @@ -768,7 +768,7 @@ static struct cpuidle_state tangier_cstates[] = { }; static struct cpuidle_state avn_cstates[] = { { - .name = "C1-AVN", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -776,7 +776,7 @@ static struct cpuidle_state avn_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-AVN", + .name = "C6", .desc = "MWAIT 0x51", .flags = MWAIT2flg(0x51) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 15, @@ -788,7 +788,7 @@ static struct cpuidle_state avn_cstates[] = { }; static struct cpuidle_state knl_cstates[] = { { - .name = "C1-KNL", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 1, @@ -796,7 +796,7 @@ static struct cpuidle_state knl_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze }, { - .name = "C6-KNL", + .name = "C6", .desc = "MWAIT 0x10", .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 120, @@ -809,7 +809,7 @@ static struct cpuidle_state knl_cstates[] = { static struct cpuidle_state bxt_cstates[] = { { - .name = "C1-BXT", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -817,7 +817,7 @@ static struct cpuidle_state bxt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-BXT", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -825,7 +825,7 @@ static struct cpuidle_state bxt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-BXT", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 133, @@ -833,7 +833,7 @@ static struct cpuidle_state bxt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C7s-BXT", + .name = "C7s", .desc = "MWAIT 0x31", .flags = MWAIT2flg(0x31) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 155, @@ -841,7 +841,7 @@ static struct cpuidle_state bxt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C8-BXT", + .name = "C8", .desc = "MWAIT 0x40", .flags = MWAIT2flg(0x40) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 1000, @@ -849,7 +849,7 @@ static struct cpuidle_state bxt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C9-BXT", + .name = "C9", .desc = "MWAIT 0x50", .flags = MWAIT2flg(0x50) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 2000, @@ -857,7 +857,7 @@ static struct cpuidle_state bxt_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C10-BXT", + .name = "C10", .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 10000, @@ -870,7 +870,7 @@ static struct cpuidle_state bxt_cstates[] = { static struct cpuidle_state dnv_cstates[] = { { - .name = "C1-DNV", + .name = "C1", .desc = "MWAIT 0x00", .flags = MWAIT2flg(0x00), .exit_latency = 2, @@ -878,7 +878,7 @@ static struct cpuidle_state dnv_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C1E-DNV", + .name = "C1E", .desc = "MWAIT 0x01", .flags = MWAIT2flg(0x01), .exit_latency = 10, @@ -886,7 +886,7 @@ static struct cpuidle_state dnv_cstates[] = { .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { - .name = "C6-DNV", + .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 50, @@ -961,9 +961,9 @@ static void auto_demotion_disable(void) { unsigned long long msr_bits; - rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits); + rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr_bits); msr_bits &= ~(icpu->auto_demotion_disable_flags); - wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits); + wrmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr_bits); } static void c1e_promotion_disable(void) { @@ -1273,7 +1273,7 @@ static void sklh_idle_state_table_update(void) if ((mwait_substates & (0xF << 28)) == 0) return; - rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr); + rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr); /* PC10 is not enabled in PKG C-state limit */ if ((msr & 0xF) != 8) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 4972986f6455..d2b465140a6b 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -20,7 +20,7 @@ #include <linux/cdev.h> #include <linux/slab.h> #include <linux/poll.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/iio/iio.h> #include "iio_core.h" diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 670917387eda..66f86027ed47 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -92,4 +92,6 @@ source "drivers/infiniband/hw/hfi1/Kconfig" source "drivers/infiniband/hw/qedr/Kconfig" +source "drivers/infiniband/hw/bnxt_re/Kconfig" + endif # INFINIBAND diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile index edaae9f9853c..e426ac877d19 100644 --- a/drivers/infiniband/core/Makefile +++ b/drivers/infiniband/core/Makefile @@ -13,6 +13,7 @@ ib_core-y := packer.o ud_header.o verbs.o cq.o rw.o sysfs.o \ multicast.o mad.o smi.o agent.o mad_rmpp.o ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o ib_core-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += umem_odp.o umem_rbtree.o +ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o ib_cm-y := cm.o diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index ae04826e82fc..b1371eb9f46c 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -314,14 +314,13 @@ static void make_default_gid(struct net_device *dev, union ib_gid *gid) int ib_cache_gid_add(struct ib_device *ib_dev, u8 port, union ib_gid *gid, struct ib_gid_attr *attr) { - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; struct ib_gid_table *table; int ix; int ret = 0; struct net_device *idev; int empty; - table = ports_table[port - rdma_start_port(ib_dev)]; + table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid; if (!memcmp(gid, &zgid, sizeof(*gid))) return -EINVAL; @@ -369,11 +368,10 @@ out_unlock: int ib_cache_gid_del(struct ib_device *ib_dev, u8 port, union ib_gid *gid, struct ib_gid_attr *attr) { - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; struct ib_gid_table *table; int ix; - table = ports_table[port - rdma_start_port(ib_dev)]; + table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid; mutex_lock(&table->lock); write_lock_irq(&table->rwlock); @@ -399,12 +397,11 @@ out_unlock: int ib_cache_gid_del_all_netdev_gids(struct ib_device *ib_dev, u8 port, struct net_device *ndev) { - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; struct ib_gid_table *table; int ix; bool deleted = false; - table = ports_table[port - rdma_start_port(ib_dev)]; + table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid; mutex_lock(&table->lock); write_lock_irq(&table->rwlock); @@ -428,10 +425,9 @@ int ib_cache_gid_del_all_netdev_gids(struct ib_device *ib_dev, u8 port, static int __ib_cache_gid_get(struct ib_device *ib_dev, u8 port, int index, union ib_gid *gid, struct ib_gid_attr *attr) { - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; struct ib_gid_table *table; - table = ports_table[port - rdma_start_port(ib_dev)]; + table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid; if (index < 0 || index >= table->sz) return -EINVAL; @@ -455,14 +451,13 @@ static int _ib_cache_gid_table_find(struct ib_device *ib_dev, unsigned long mask, u8 *port, u16 *index) { - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; struct ib_gid_table *table; u8 p; int local_index; unsigned long flags; for (p = 0; p < ib_dev->phys_port_cnt; p++) { - table = ports_table[p]; + table = ib_dev->cache.ports[p].gid; read_lock_irqsave(&table->rwlock, flags); local_index = find_gid(table, gid, val, false, mask, NULL); if (local_index >= 0) { @@ -503,18 +498,16 @@ int ib_find_cached_gid_by_port(struct ib_device *ib_dev, u16 *index) { int local_index; - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; struct ib_gid_table *table; unsigned long mask = GID_ATTR_FIND_MASK_GID | GID_ATTR_FIND_MASK_GID_TYPE; struct ib_gid_attr val = {.ndev = ndev, .gid_type = gid_type}; unsigned long flags; - if (port < rdma_start_port(ib_dev) || - port > rdma_end_port(ib_dev)) + if (!rdma_is_port_valid(ib_dev, port)) return -ENOENT; - table = ports_table[port - rdma_start_port(ib_dev)]; + table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid; if (ndev) mask |= GID_ATTR_FIND_MASK_NETDEV; @@ -562,21 +555,17 @@ static int ib_cache_gid_find_by_filter(struct ib_device *ib_dev, void *context, u16 *index) { - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; struct ib_gid_table *table; unsigned int i; unsigned long flags; bool found = false; - if (!ports_table) - return -EOPNOTSUPP; - if (port < rdma_start_port(ib_dev) || - port > rdma_end_port(ib_dev) || + if (!rdma_is_port_valid(ib_dev, port) || !rdma_protocol_roce(ib_dev, port)) return -EPROTONOSUPPORT; - table = ports_table[port - rdma_start_port(ib_dev)]; + table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid; read_lock_irqsave(&table->rwlock, flags); for (i = 0; i < table->sz; i++) { @@ -668,14 +657,13 @@ void ib_cache_gid_set_default_gid(struct ib_device *ib_dev, u8 port, unsigned long gid_type_mask, enum ib_cache_gid_default_mode mode) { - struct ib_gid_table **ports_table = ib_dev->cache.gid_cache; union ib_gid gid; struct ib_gid_attr gid_attr; struct ib_gid_attr zattr_type = zattr; struct ib_gid_table *table; unsigned int gid_type; - table = ports_table[port - rdma_start_port(ib_dev)]; + table = ib_dev->cache.ports[port - rdma_start_port(ib_dev)].gid; make_default_gid(ndev, &gid); memset(&gid_attr, 0, sizeof(gid_attr)); @@ -766,71 +754,64 @@ static int gid_table_reserve_default(struct ib_device *ib_dev, u8 port, static int _gid_table_setup_one(struct ib_device *ib_dev) { u8 port; - struct ib_gid_table **table; + struct ib_gid_table *table; int err = 0; - table = kcalloc(ib_dev->phys_port_cnt, sizeof(*table), GFP_KERNEL); - if (!table) - return -ENOMEM; - for (port = 0; port < ib_dev->phys_port_cnt; port++) { u8 rdma_port = port + rdma_start_port(ib_dev); - table[port] = + table = alloc_gid_table( ib_dev->port_immutable[rdma_port].gid_tbl_len); - if (!table[port]) { + if (!table) { err = -ENOMEM; goto rollback_table_setup; } err = gid_table_reserve_default(ib_dev, port + rdma_start_port(ib_dev), - table[port]); + table); if (err) goto rollback_table_setup; + ib_dev->cache.ports[port].gid = table; } - ib_dev->cache.gid_cache = table; return 0; rollback_table_setup: for (port = 0; port < ib_dev->phys_port_cnt; port++) { + table = ib_dev->cache.ports[port].gid; + cleanup_gid_table_port(ib_dev, port + rdma_start_port(ib_dev), - table[port]); - release_gid_table(table[port]); + table); + release_gid_table(table); } - kfree(table); return err; } static void gid_table_release_one(struct ib_device *ib_dev) { - struct ib_gid_table **table = ib_dev->cache.gid_cache; + struct ib_gid_table *table; u8 port; - if (!table) - return; - - for (port = 0; port < ib_dev->phys_port_cnt; port++) - release_gid_table(table[port]); - - kfree(table); - ib_dev->cache.gid_cache = NULL; + for (port = 0; port < ib_dev->phys_port_cnt; port++) { + table = ib_dev->cache.ports[port].gid; + release_gid_table(table); + ib_dev->cache.ports[port].gid = NULL; + } } static void gid_table_cleanup_one(struct ib_device *ib_dev) { - struct ib_gid_table **table = ib_dev->cache.gid_cache; + struct ib_gid_table *table; u8 port; - if (!table) - return; - - for (port = 0; port < ib_dev->phys_port_cnt; port++) + for (port = 0; port < ib_dev->phys_port_cnt; port++) { + table = ib_dev->cache.ports[port].gid; cleanup_gid_table_port(ib_dev, port + rdma_start_port(ib_dev), - table[port]); + table); + } } static int gid_table_setup_one(struct ib_device *ib_dev) @@ -860,12 +841,12 @@ int ib_get_cached_gid(struct ib_device *device, { int res; unsigned long flags; - struct ib_gid_table **ports_table = device->cache.gid_cache; - struct ib_gid_table *table = ports_table[port_num - rdma_start_port(device)]; + struct ib_gid_table *table; - if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port_num)) return -EINVAL; + table = device->cache.ports[port_num - rdma_start_port(device)].gid; read_lock_irqsave(&table->rwlock, flags); res = __ib_cache_gid_get(device, port_num, index, gid, gid_attr); read_unlock_irqrestore(&table->rwlock, flags); @@ -912,12 +893,12 @@ int ib_get_cached_pkey(struct ib_device *device, unsigned long flags; int ret = 0; - if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port_num)) return -EINVAL; read_lock_irqsave(&device->cache.lock, flags); - cache = device->cache.pkey_cache[port_num - rdma_start_port(device)]; + cache = device->cache.ports[port_num - rdma_start_port(device)].pkey; if (index < 0 || index >= cache->table_len) ret = -EINVAL; @@ -941,12 +922,12 @@ int ib_find_cached_pkey(struct ib_device *device, int ret = -ENOENT; int partial_ix = -1; - if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port_num)) return -EINVAL; read_lock_irqsave(&device->cache.lock, flags); - cache = device->cache.pkey_cache[port_num - rdma_start_port(device)]; + cache = device->cache.ports[port_num - rdma_start_port(device)].pkey; *index = -1; @@ -981,12 +962,12 @@ int ib_find_exact_cached_pkey(struct ib_device *device, int i; int ret = -ENOENT; - if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port_num)) return -EINVAL; read_lock_irqsave(&device->cache.lock, flags); - cache = device->cache.pkey_cache[port_num - rdma_start_port(device)]; + cache = device->cache.ports[port_num - rdma_start_port(device)].pkey; *index = -1; @@ -1010,17 +991,36 @@ int ib_get_cached_lmc(struct ib_device *device, unsigned long flags; int ret = 0; - if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port_num)) return -EINVAL; read_lock_irqsave(&device->cache.lock, flags); - *lmc = device->cache.lmc_cache[port_num - rdma_start_port(device)]; + *lmc = device->cache.ports[port_num - rdma_start_port(device)].lmc; read_unlock_irqrestore(&device->cache.lock, flags); return ret; } EXPORT_SYMBOL(ib_get_cached_lmc); +int ib_get_cached_port_state(struct ib_device *device, + u8 port_num, + enum ib_port_state *port_state) +{ + unsigned long flags; + int ret = 0; + + if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + return -EINVAL; + + read_lock_irqsave(&device->cache.lock, flags); + *port_state = device->cache.ports[port_num + - rdma_start_port(device)].port_state; + read_unlock_irqrestore(&device->cache.lock, flags); + + return ret; +} +EXPORT_SYMBOL(ib_get_cached_port_state); + static void ib_cache_update(struct ib_device *device, u8 port) { @@ -1033,14 +1033,13 @@ static void ib_cache_update(struct ib_device *device, int i; int ret; struct ib_gid_table *table; - struct ib_gid_table **ports_table = device->cache.gid_cache; bool use_roce_gid_table = rdma_cap_roce_gid_table(device, port); - if (port < rdma_start_port(device) || port > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port)) return; - table = ports_table[port - rdma_start_port(device)]; + table = device->cache.ports[port - rdma_start_port(device)].gid; tprops = kmalloc(sizeof *tprops, GFP_KERNEL); if (!tprops) @@ -1092,9 +1091,10 @@ static void ib_cache_update(struct ib_device *device, write_lock_irq(&device->cache.lock); - old_pkey_cache = device->cache.pkey_cache[port - rdma_start_port(device)]; + old_pkey_cache = device->cache.ports[port - + rdma_start_port(device)].pkey; - device->cache.pkey_cache[port - rdma_start_port(device)] = pkey_cache; + device->cache.ports[port - rdma_start_port(device)].pkey = pkey_cache; if (!use_roce_gid_table) { write_lock(&table->rwlock); for (i = 0; i < gid_cache->table_len; i++) { @@ -1104,7 +1104,9 @@ static void ib_cache_update(struct ib_device *device, write_unlock(&table->rwlock); } - device->cache.lmc_cache[port - rdma_start_port(device)] = tprops->lmc; + device->cache.ports[port - rdma_start_port(device)].lmc = tprops->lmc; + device->cache.ports[port - rdma_start_port(device)].port_state = + tprops->state; write_unlock_irq(&device->cache.lock); @@ -1157,22 +1159,17 @@ int ib_cache_setup_one(struct ib_device *device) rwlock_init(&device->cache.lock); - device->cache.pkey_cache = - kzalloc(sizeof *device->cache.pkey_cache * + device->cache.ports = + kzalloc(sizeof(*device->cache.ports) * (rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL); - device->cache.lmc_cache = kmalloc(sizeof *device->cache.lmc_cache * - (rdma_end_port(device) - - rdma_start_port(device) + 1), - GFP_KERNEL); - if (!device->cache.pkey_cache || - !device->cache.lmc_cache) { + if (!device->cache.ports) { err = -ENOMEM; - goto free; + goto out; } err = gid_table_setup_one(device); if (err) - goto free; + goto out; for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) ib_cache_update(device, p + rdma_start_port(device)); @@ -1187,9 +1184,7 @@ int ib_cache_setup_one(struct ib_device *device) err: gid_table_cleanup_one(device); -free: - kfree(device->cache.pkey_cache); - kfree(device->cache.lmc_cache); +out: return err; } @@ -1203,14 +1198,11 @@ void ib_cache_release_one(struct ib_device *device) * all the device's resources when the cache could no * longer be accessed. */ - if (device->cache.pkey_cache) - for (p = 0; - p <= rdma_end_port(device) - rdma_start_port(device); ++p) - kfree(device->cache.pkey_cache[p]); + for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) + kfree(device->cache.ports[p].pkey); gid_table_release_one(device); - kfree(device->cache.pkey_cache); - kfree(device->cache.lmc_cache); + kfree(device->cache.ports); } void ib_cache_cleanup_one(struct ib_device *device) diff --git a/drivers/infiniband/core/cgroup.c b/drivers/infiniband/core/cgroup.c new file mode 100644 index 000000000000..126ac5f99db7 --- /dev/null +++ b/drivers/infiniband/core/cgroup.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Parav Pandit <pandit.parav@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "core_priv.h" + +/** + * ib_device_register_rdmacg - register with rdma cgroup. + * @device: device to register to participate in resource + * accounting by rdma cgroup. + * + * Register with the rdma cgroup. Should be called before + * exposing rdma device to user space applications to avoid + * resource accounting leak. + * Returns 0 on success or otherwise failure code. + */ +int ib_device_register_rdmacg(struct ib_device *device) +{ + device->cg_device.name = device->name; + return rdmacg_register_device(&device->cg_device); +} + +/** + * ib_device_unregister_rdmacg - unregister with rdma cgroup. + * @device: device to unregister. + * + * Unregister with the rdma cgroup. Should be called after + * all the resources are deallocated, and after a stage when any + * other resource allocation by user application cannot be done + * for this device to avoid any leak in accounting. + */ +void ib_device_unregister_rdmacg(struct ib_device *device) +{ + rdmacg_unregister_device(&device->cg_device); +} + +int ib_rdmacg_try_charge(struct ib_rdmacg_object *cg_obj, + struct ib_device *device, + enum rdmacg_resource_type resource_index) +{ + return rdmacg_try_charge(&cg_obj->cg, &device->cg_device, + resource_index); +} +EXPORT_SYMBOL(ib_rdmacg_try_charge); + +void ib_rdmacg_uncharge(struct ib_rdmacg_object *cg_obj, + struct ib_device *device, + enum rdmacg_resource_type resource_index) +{ + rdmacg_uncharge(cg_obj->cg, &device->cg_device, + resource_index); +} +EXPORT_SYMBOL(ib_rdmacg_uncharge); diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index cf1edfa1cbac..6535f09dc575 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -3409,6 +3409,8 @@ static void cm_process_send_error(struct ib_mad_send_buf *msg, if (msg != cm_id_priv->msg || state != cm_id_priv->id.state) goto discard; + pr_debug_ratelimited("CM: failed sending MAD in state %d. (%s)\n", + state, ib_wc_status_msg(wc_status)); switch (state) { case IB_CM_REQ_SENT: case IB_CM_MRA_REQ_RCVD: diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 4eb5a80e5d81..acd10d666f1c 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -198,6 +198,7 @@ struct cma_device { atomic_t refcount; struct list_head id_list; enum ib_gid_type *default_gid_type; + u8 *default_roce_tos; }; struct rdma_bind_list { @@ -269,8 +270,7 @@ struct cma_device *cma_enum_devices_by_ibdev(cma_device_filter filter, int cma_get_default_gid_type(struct cma_device *cma_dev, unsigned int port) { - if (port < rdma_start_port(cma_dev->device) || - port > rdma_end_port(cma_dev->device)) + if (!rdma_is_port_valid(cma_dev->device, port)) return -EINVAL; return cma_dev->default_gid_type[port - rdma_start_port(cma_dev->device)]; @@ -282,8 +282,7 @@ int cma_set_default_gid_type(struct cma_device *cma_dev, { unsigned long supported_gids; - if (port < rdma_start_port(cma_dev->device) || - port > rdma_end_port(cma_dev->device)) + if (!rdma_is_port_valid(cma_dev->device, port)) return -EINVAL; supported_gids = roce_gid_type_mask_support(cma_dev->device, port); @@ -297,6 +296,25 @@ int cma_set_default_gid_type(struct cma_device *cma_dev, return 0; } +int cma_get_default_roce_tos(struct cma_device *cma_dev, unsigned int port) +{ + if (!rdma_is_port_valid(cma_dev->device, port)) + return -EINVAL; + + return cma_dev->default_roce_tos[port - rdma_start_port(cma_dev->device)]; +} + +int cma_set_default_roce_tos(struct cma_device *cma_dev, unsigned int port, + u8 default_roce_tos) +{ + if (!rdma_is_port_valid(cma_dev->device, port)) + return -EINVAL; + + cma_dev->default_roce_tos[port - rdma_start_port(cma_dev->device)] = + default_roce_tos; + + return 0; +} struct ib_device *cma_get_ib_dev(struct cma_device *cma_dev) { return cma_dev->device; @@ -343,6 +361,7 @@ struct rdma_id_private { u32 options; u8 srq; u8 tos; + bool tos_set; u8 reuseaddr; u8 afonly; enum ib_gid_type gid_type; @@ -709,6 +728,7 @@ static int cma_resolve_ib_dev(struct rdma_id_private *id_priv) union ib_gid gid, sgid, *dgid; u16 pkey, index; u8 p; + enum ib_port_state port_state; int i; cma_dev = NULL; @@ -724,6 +744,8 @@ static int cma_resolve_ib_dev(struct rdma_id_private *id_priv) if (ib_find_cached_pkey(cur_dev->device, p, pkey, &index)) continue; + if (ib_get_cached_port_state(cur_dev->device, p, &port_state)) + continue; for (i = 0; !ib_get_cached_gid(cur_dev->device, p, i, &gid, NULL); i++) { @@ -735,7 +757,8 @@ static int cma_resolve_ib_dev(struct rdma_id_private *id_priv) } if (!cma_dev && (gid.global.subnet_prefix == - dgid->global.subnet_prefix)) { + dgid->global.subnet_prefix) && + port_state == IB_PORT_ACTIVE) { cma_dev = cur_dev; sgid = gid; id_priv->id.port_num = p; @@ -778,6 +801,7 @@ struct rdma_cm_id *rdma_create_id(struct net *net, id_priv->id.event_handler = event_handler; id_priv->id.ps = ps; id_priv->id.qp_type = qp_type; + id_priv->tos_set = false; spin_lock_init(&id_priv->lock); mutex_init(&id_priv->qp_mutex); init_completion(&id_priv->comp); @@ -1689,6 +1713,7 @@ static int cma_rep_recv(struct rdma_id_private *id_priv) return 0; reject: + pr_debug_ratelimited("RDMA CM: CONNECT_ERROR: failed to handle reply. status %d\n", ret); cma_modify_qp_err(id_priv); ib_send_cm_rej(id_priv->cm_id.ib, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0, NULL, 0); @@ -1760,6 +1785,8 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) /* ignore event */ goto out; case IB_CM_REJ_RECEIVED: + pr_debug_ratelimited("RDMA CM: REJECTED: %s\n", rdma_reject_msg(&id_priv->id, + ib_event->param.rej_rcvd.reason)); cma_modify_qp_err(id_priv); event.status = ib_event->param.rej_rcvd.reason; event.event = RDMA_CM_EVENT_REJECTED; @@ -2266,6 +2293,7 @@ void rdma_set_service_type(struct rdma_cm_id *id, int tos) id_priv = container_of(id, struct rdma_id_private, id); id_priv->tos = (u8) tos; + id_priv->tos_set = true; } EXPORT_SYMBOL(rdma_set_service_type); @@ -2285,6 +2313,8 @@ static void cma_query_handler(int status, struct ib_sa_path_rec *path_rec, work->new_state = RDMA_CM_ADDR_RESOLVED; work->event.event = RDMA_CM_EVENT_ROUTE_ERROR; work->event.status = status; + pr_debug_ratelimited("RDMA CM: ROUTE_ERROR: failed to query path. status %d\n", + status); } queue_work(cma_wq, &work->work); @@ -2498,6 +2528,9 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv) struct cma_work *work; int ret; struct net_device *ndev = NULL; + u8 default_roce_tos = id_priv->cma_dev->default_roce_tos[id_priv->id.port_num - + rdma_start_port(id_priv->cma_dev->device)]; + u8 tos = id_priv->tos_set ? id_priv->tos : default_roce_tos; work = kzalloc(sizeof *work, GFP_KERNEL); @@ -2571,7 +2604,8 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv) route->path_rec->reversible = 1; route->path_rec->pkey = cpu_to_be16(0xffff); route->path_rec->mtu_selector = IB_SA_EQ; - route->path_rec->sl = iboe_tos_to_sl(ndev, id_priv->tos); + route->path_rec->sl = iboe_tos_to_sl(ndev, tos); + route->path_rec->traffic_class = tos; route->path_rec->mtu = iboe_get_mtu(ndev->mtu); route->path_rec->rate_selector = IB_SA_EQ; route->path_rec->rate = iboe_get_rate(ndev); @@ -2650,8 +2684,8 @@ static void cma_set_loopback(struct sockaddr *addr) static int cma_bind_loopback(struct rdma_id_private *id_priv) { struct cma_device *cma_dev, *cur_dev; - struct ib_port_attr port_attr; union ib_gid gid; + enum ib_port_state port_state; u16 pkey; int ret; u8 p; @@ -2667,8 +2701,8 @@ static int cma_bind_loopback(struct rdma_id_private *id_priv) cma_dev = cur_dev; for (p = 1; p <= cur_dev->device->phys_port_cnt; ++p) { - if (!ib_query_port(cur_dev->device, p, &port_attr) && - port_attr.state == IB_PORT_ACTIVE) { + if (!ib_get_cached_port_state(cur_dev->device, p, &port_state) && + port_state == IB_PORT_ACTIVE) { cma_dev = cur_dev; goto port_found; } @@ -2718,8 +2752,14 @@ static void addr_handler(int status, struct sockaddr *src_addr, goto out; memcpy(cma_src_addr(id_priv), src_addr, rdma_addr_size(src_addr)); - if (!status && !id_priv->cma_dev) + if (!status && !id_priv->cma_dev) { status = cma_acquire_dev(id_priv, NULL); + if (status) + pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to acquire device. status %d\n", + status); + } else { + pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to resolve IP. status %d\n", status); + } if (status) { if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_RESOLVED, @@ -2831,20 +2871,26 @@ int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, int ret; id_priv = container_of(id, struct rdma_id_private, id); + memcpy(cma_dst_addr(id_priv), dst_addr, rdma_addr_size(dst_addr)); if (id_priv->state == RDMA_CM_IDLE) { ret = cma_bind_addr(id, src_addr, dst_addr); - if (ret) + if (ret) { + memset(cma_dst_addr(id_priv), 0, rdma_addr_size(dst_addr)); return ret; + } } - if (cma_family(id_priv) != dst_addr->sa_family) + if (cma_family(id_priv) != dst_addr->sa_family) { + memset(cma_dst_addr(id_priv), 0, rdma_addr_size(dst_addr)); return -EINVAL; + } - if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_BOUND, RDMA_CM_ADDR_QUERY)) + if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_BOUND, RDMA_CM_ADDR_QUERY)) { + memset(cma_dst_addr(id_priv), 0, rdma_addr_size(dst_addr)); return -EINVAL; + } atomic_inc(&id_priv->refcount); - memcpy(cma_dst_addr(id_priv), dst_addr, rdma_addr_size(dst_addr)); if (cma_any_addr(dst_addr)) { ret = cma_resolve_loopback(id_priv); } else { @@ -2960,6 +3006,43 @@ err: return ret == -ENOSPC ? -EADDRNOTAVAIL : ret; } +static int cma_port_is_unique(struct rdma_bind_list *bind_list, + struct rdma_id_private *id_priv) +{ + struct rdma_id_private *cur_id; + struct sockaddr *daddr = cma_dst_addr(id_priv); + struct sockaddr *saddr = cma_src_addr(id_priv); + __be16 dport = cma_port(daddr); + + hlist_for_each_entry(cur_id, &bind_list->owners, node) { + struct sockaddr *cur_daddr = cma_dst_addr(cur_id); + struct sockaddr *cur_saddr = cma_src_addr(cur_id); + __be16 cur_dport = cma_port(cur_daddr); + + if (id_priv == cur_id) + continue; + + /* different dest port -> unique */ + if (!cma_any_port(cur_daddr) && + (dport != cur_dport)) + continue; + + /* different src address -> unique */ + if (!cma_any_addr(saddr) && + !cma_any_addr(cur_saddr) && + cma_addr_cmp(saddr, cur_saddr)) + continue; + + /* different dst address -> unique */ + if (!cma_any_addr(cur_daddr) && + cma_addr_cmp(daddr, cur_daddr)) + continue; + + return -EADDRNOTAVAIL; + } + return 0; +} + static int cma_alloc_any_port(enum rdma_port_space ps, struct rdma_id_private *id_priv) { @@ -2972,9 +3055,19 @@ static int cma_alloc_any_port(enum rdma_port_space ps, remaining = (high - low) + 1; rover = prandom_u32() % remaining + low; retry: - if (last_used_port != rover && - !cma_ps_find(net, ps, (unsigned short)rover)) { - int ret = cma_alloc_port(ps, id_priv, rover); + if (last_used_port != rover) { + struct rdma_bind_list *bind_list; + int ret; + + bind_list = cma_ps_find(net, ps, (unsigned short)rover); + + if (!bind_list) { + ret = cma_alloc_port(ps, id_priv, rover); + } else { + ret = cma_port_is_unique(bind_list, id_priv); + if (!ret) + cma_bind_port(bind_list, id_priv); + } /* * Remember previously used port number in order to avoid * re-using same port immediately after it is closed. @@ -3203,6 +3296,7 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) { struct rdma_id_private *id_priv; int ret; + struct sockaddr *daddr; if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6 && addr->sa_family != AF_IB) @@ -3242,6 +3336,9 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) if (ret) goto err2; + daddr = cma_dst_addr(id_priv); + daddr->sa_family = addr->sa_family; + return 0; err2: if (id_priv->cma_dev) @@ -3306,10 +3403,13 @@ static int cma_sidr_rep_handler(struct ib_cm_id *cm_id, if (rep->status != IB_SIDR_SUCCESS) { event.event = RDMA_CM_EVENT_UNREACHABLE; event.status = ib_event->param.sidr_rep_rcvd.status; + pr_debug_ratelimited("RDMA CM: UNREACHABLE: bad SIDR reply. status %d\n", + event.status); break; } ret = cma_set_qkey(id_priv, rep->qkey); if (ret) { + pr_debug_ratelimited("RDMA CM: ADDR_ERROR: failed to set qkey. status %d\n", ret); event.event = RDMA_CM_EVENT_ADDR_ERROR; event.status = ret; break; @@ -3581,6 +3681,9 @@ static int cma_accept_iw(struct rdma_id_private *id_priv, struct iw_cm_conn_param iw_param; int ret; + if (!conn_param) + return -EINVAL; + ret = cma_modify_qp_rtr(id_priv, conn_param); if (ret) return ret; @@ -3758,10 +3861,17 @@ static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast) if (!status) status = cma_set_qkey(id_priv, be32_to_cpu(multicast->rec.qkey)); + else + pr_debug_ratelimited("RDMA CM: MULTICAST_ERROR: failed to join multicast. status %d\n", + status); mutex_lock(&id_priv->qp_mutex); - if (!status && id_priv->id.qp) + if (!status && id_priv->id.qp) { status = ib_attach_mcast(id_priv->id.qp, &multicast->rec.mgid, be16_to_cpu(multicast->rec.mlid)); + if (status) + pr_debug_ratelimited("RDMA CM: MULTICAST_ERROR: failed to attach QP. status %d\n", + status); + } mutex_unlock(&id_priv->qp_mutex); memset(&event, 0, sizeof event); @@ -4227,15 +4337,21 @@ static void cma_add_one(struct ib_device *device) cma_dev->default_gid_type = kcalloc(device->phys_port_cnt, sizeof(*cma_dev->default_gid_type), GFP_KERNEL); - if (!cma_dev->default_gid_type) { - kfree(cma_dev); - return; - } + if (!cma_dev->default_gid_type) + goto free_cma_dev; + + cma_dev->default_roce_tos = kcalloc(device->phys_port_cnt, + sizeof(*cma_dev->default_roce_tos), + GFP_KERNEL); + if (!cma_dev->default_roce_tos) + goto free_gid_type; + for (i = rdma_start_port(device); i <= rdma_end_port(device); i++) { supported_gids = roce_gid_type_mask_support(device, i); WARN_ON(!supported_gids); cma_dev->default_gid_type[i - rdma_start_port(device)] = find_first_bit(&supported_gids, BITS_PER_LONG); + cma_dev->default_roce_tos[i - rdma_start_port(device)] = 0; } init_completion(&cma_dev->comp); @@ -4248,6 +4364,16 @@ static void cma_add_one(struct ib_device *device) list_for_each_entry(id_priv, &listen_any_list, list) cma_listen_on_dev(id_priv, cma_dev); mutex_unlock(&lock); + + return; + +free_gid_type: + kfree(cma_dev->default_gid_type); + +free_cma_dev: + kfree(cma_dev); + + return; } static int cma_remove_id_dev(struct rdma_id_private *id_priv) @@ -4316,6 +4442,7 @@ static void cma_remove_one(struct ib_device *device, void *client_data) mutex_unlock(&lock); cma_process_remove(cma_dev); + kfree(cma_dev->default_roce_tos); kfree(cma_dev->default_gid_type); kfree(cma_dev); } diff --git a/drivers/infiniband/core/cma_configfs.c b/drivers/infiniband/core/cma_configfs.c index 41573df1d9fc..54076a3e8007 100644 --- a/drivers/infiniband/core/cma_configfs.c +++ b/drivers/infiniband/core/cma_configfs.c @@ -139,8 +139,50 @@ static ssize_t default_roce_mode_store(struct config_item *item, CONFIGFS_ATTR(, default_roce_mode); +static ssize_t default_roce_tos_show(struct config_item *item, char *buf) +{ + struct cma_device *cma_dev; + struct cma_dev_port_group *group; + ssize_t ret; + u8 tos; + + ret = cma_configfs_params_get(item, &cma_dev, &group); + if (ret) + return ret; + + tos = cma_get_default_roce_tos(cma_dev, group->port_num); + cma_configfs_params_put(cma_dev); + + return sprintf(buf, "%u\n", tos); +} + +static ssize_t default_roce_tos_store(struct config_item *item, + const char *buf, size_t count) +{ + struct cma_device *cma_dev; + struct cma_dev_port_group *group; + ssize_t ret; + u8 tos; + + ret = kstrtou8(buf, 0, &tos); + if (ret) + return ret; + + ret = cma_configfs_params_get(item, &cma_dev, &group); + if (ret) + return ret; + + ret = cma_set_default_roce_tos(cma_dev, group->port_num, tos); + cma_configfs_params_put(cma_dev); + + return ret ? ret : strnlen(buf, count); +} + +CONFIGFS_ATTR(, default_roce_tos); + static struct configfs_attribute *cma_configfs_attributes[] = { &attr_default_roce_mode, + &attr_default_roce_tos, NULL, }; diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index d29372624f3a..cb7d372e4bdf 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -35,6 +35,7 @@ #include <linux/list.h> #include <linux/spinlock.h> +#include <linux/cgroup_rdma.h> #include <rdma/ib_verbs.h> @@ -62,6 +63,9 @@ int cma_get_default_gid_type(struct cma_device *cma_dev, int cma_set_default_gid_type(struct cma_device *cma_dev, unsigned int port, enum ib_gid_type default_gid_type); +int cma_get_default_roce_tos(struct cma_device *cma_dev, unsigned int port); +int cma_set_default_roce_tos(struct cma_device *a_dev, unsigned int port, + u8 default_roce_tos); struct ib_device *cma_get_ib_dev(struct cma_device *cma_dev); int ib_device_register_sysfs(struct ib_device *device, @@ -121,6 +125,35 @@ int ib_cache_setup_one(struct ib_device *device); void ib_cache_cleanup_one(struct ib_device *device); void ib_cache_release_one(struct ib_device *device); +#ifdef CONFIG_CGROUP_RDMA +int ib_device_register_rdmacg(struct ib_device *device); +void ib_device_unregister_rdmacg(struct ib_device *device); + +int ib_rdmacg_try_charge(struct ib_rdmacg_object *cg_obj, + struct ib_device *device, + enum rdmacg_resource_type resource_index); + +void ib_rdmacg_uncharge(struct ib_rdmacg_object *cg_obj, + struct ib_device *device, + enum rdmacg_resource_type resource_index); +#else +static inline int ib_device_register_rdmacg(struct ib_device *device) +{ return 0; } + +static inline void ib_device_unregister_rdmacg(struct ib_device *device) +{ } + +static inline int ib_rdmacg_try_charge(struct ib_rdmacg_object *cg_obj, + struct ib_device *device, + enum rdmacg_resource_type resource_index) +{ return 0; } + +static inline void ib_rdmacg_uncharge(struct ib_rdmacg_object *cg_obj, + struct ib_device *device, + enum rdmacg_resource_type resource_index) +{ } +#endif + static inline bool rdma_is_upper_dev_rcu(struct net_device *dev, struct net_device *upper) { diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c index a754fc727de5..e95510117a6d 100644 --- a/drivers/infiniband/core/cq.c +++ b/drivers/infiniband/core/cq.c @@ -58,8 +58,8 @@ static int __ib_process_cq(struct ib_cq *cq, int budget) * %IB_POLL_DIRECT CQ. It does not offload CQ processing to a different * context and does not ask for completion interrupts from the HCA. * - * Note: for compatibility reasons -1 can be passed in %budget for unlimited - * polling. Do not use this feature in new code, it will be removed soon. + * Note: do not pass -1 as %budget unless it is guaranteed that the number + * of completions that will be processed is small. */ int ib_process_cq_direct(struct ib_cq *cq, int budget) { @@ -120,7 +120,7 @@ static void ib_cq_completion_workqueue(struct ib_cq *cq, void *private) * * This is the proper interface to allocate a CQ for in-kernel users. A * CQ allocated with this interface will automatically be polled from the - * specified context. The ULP needs must use wr->wr_cqe instead of wr->wr_id + * specified context. The ULP must use wr->wr_cqe instead of wr->wr_id * to use this CQ abstraction. */ struct ib_cq *ib_alloc_cq(struct ib_device *dev, void *private, diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 571974cd3919..593d2ce6ec7c 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -333,6 +333,15 @@ int ib_register_device(struct ib_device *device, int ret; struct ib_client *client; struct ib_udata uhw = {.outlen = 0, .inlen = 0}; + struct device *parent = device->dev.parent; + + WARN_ON_ONCE(!parent); + if (!device->dev.dma_ops) + device->dev.dma_ops = parent->dma_ops; + if (!device->dev.dma_mask) + device->dev.dma_mask = parent->dma_mask; + if (!device->dev.coherent_dma_mask) + device->dev.coherent_dma_mask = parent->coherent_dma_mask; mutex_lock(&device_mutex); @@ -360,10 +369,18 @@ int ib_register_device(struct ib_device *device, goto out; } + ret = ib_device_register_rdmacg(device); + if (ret) { + pr_warn("Couldn't register device with rdma cgroup\n"); + ib_cache_cleanup_one(device); + goto out; + } + memset(&device->attrs, 0, sizeof(device->attrs)); ret = device->query_device(device, &device->attrs, &uhw); if (ret) { pr_warn("Couldn't query the device attributes\n"); + ib_device_unregister_rdmacg(device); ib_cache_cleanup_one(device); goto out; } @@ -372,6 +389,7 @@ int ib_register_device(struct ib_device *device, if (ret) { pr_warn("Couldn't register device %s with driver model\n", device->name); + ib_device_unregister_rdmacg(device); ib_cache_cleanup_one(device); goto out; } @@ -421,6 +439,7 @@ void ib_unregister_device(struct ib_device *device) mutex_unlock(&device_mutex); + ib_device_unregister_rdmacg(device); ib_device_unregister_sysfs(device); ib_cache_cleanup_one(device); @@ -659,7 +678,7 @@ int ib_query_port(struct ib_device *device, union ib_gid gid; int err; - if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port_num)) return -EINVAL; memset(port_attr, 0, sizeof(*port_attr)); @@ -825,7 +844,7 @@ int ib_modify_port(struct ib_device *device, if (!device->modify_port) return -ENOSYS; - if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, port_num)) return -EINVAL; return device->modify_port(device, port_num, port_modify_mask, diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index a009f7132c73..57f231f1c721 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -316,7 +316,9 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device, /* Validate device and port */ port_priv = ib_get_mad_port(device, port_num); if (!port_priv) { - dev_notice(&device->dev, "ib_register_mad_agent: Invalid port\n"); + dev_notice(&device->dev, + "ib_register_mad_agent: Invalid port %d\n", + port_num); ret = ERR_PTR(-ENODEV); goto error1; } diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c index 0621f4455732..db958d3207ef 100644 --- a/drivers/infiniband/core/roce_gid_mgmt.c +++ b/drivers/infiniband/core/roce_gid_mgmt.c @@ -144,7 +144,6 @@ static enum bonding_slave_state is_eth_active_slave_of_bonding_rcu(struct net_de static int is_eth_port_of_netdev(struct ib_device *ib_dev, u8 port, struct net_device *rdma_ndev, void *cookie) { - struct net_device *event_ndev = (struct net_device *)cookie; struct net_device *real_dev; int res; @@ -152,11 +151,11 @@ static int is_eth_port_of_netdev(struct ib_device *ib_dev, u8 port, return 0; rcu_read_lock(); - real_dev = rdma_vlan_dev_real_dev(event_ndev); + real_dev = rdma_vlan_dev_real_dev(cookie); if (!real_dev) - real_dev = event_ndev; + real_dev = cookie; - res = ((rdma_is_upper_dev_rcu(rdma_ndev, event_ndev) && + res = ((rdma_is_upper_dev_rcu(rdma_ndev, cookie) && (is_eth_active_slave_of_bonding_rcu(rdma_ndev, real_dev) & REQUIRED_BOND_STATES)) || real_dev == rdma_ndev); @@ -192,17 +191,16 @@ static int pass_all_filter(struct ib_device *ib_dev, u8 port, static int upper_device_filter(struct ib_device *ib_dev, u8 port, struct net_device *rdma_ndev, void *cookie) { - struct net_device *event_ndev = (struct net_device *)cookie; int res; if (!rdma_ndev) return 0; - if (rdma_ndev == event_ndev) + if (rdma_ndev == cookie) return 1; rcu_read_lock(); - res = rdma_is_upper_dev_rcu(rdma_ndev, event_ndev); + res = rdma_is_upper_dev_rcu(rdma_ndev, cookie); rcu_read_unlock(); return res; @@ -379,18 +377,14 @@ static void _add_netdev_ips(struct ib_device *ib_dev, u8 port, static void add_netdev_ips(struct ib_device *ib_dev, u8 port, struct net_device *rdma_ndev, void *cookie) { - struct net_device *event_ndev = (struct net_device *)cookie; - - enum_netdev_default_gids(ib_dev, port, event_ndev, rdma_ndev); - _add_netdev_ips(ib_dev, port, event_ndev); + enum_netdev_default_gids(ib_dev, port, cookie, rdma_ndev); + _add_netdev_ips(ib_dev, port, cookie); } static void del_netdev_ips(struct ib_device *ib_dev, u8 port, struct net_device *rdma_ndev, void *cookie) { - struct net_device *event_ndev = (struct net_device *)cookie; - - ib_cache_gid_del_all_netdev_gids(ib_dev, port, event_ndev); + ib_cache_gid_del_all_netdev_gids(ib_dev, port, cookie); } static void enum_all_gids_of_dev_cb(struct ib_device *ib_dev, @@ -460,7 +454,7 @@ static void handle_netdev_upper(struct ib_device *ib_dev, u8 port, u8 port, struct net_device *ndev)) { - struct net_device *ndev = (struct net_device *)cookie; + struct net_device *ndev = cookie; struct upper_list *upper_iter; struct upper_list *upper_temp; LIST_HEAD(upper_list); @@ -519,9 +513,7 @@ static void del_netdev_default_ips_join(struct ib_device *ib_dev, u8 port, static void del_netdev_default_ips(struct ib_device *ib_dev, u8 port, struct net_device *rdma_ndev, void *cookie) { - struct net_device *event_ndev = (struct net_device *)cookie; - - bond_delete_netdev_default_gids(ib_dev, port, event_ndev, rdma_ndev); + bond_delete_netdev_default_gids(ib_dev, port, cookie, rdma_ndev); } /* The following functions operate on all IB devices. netdevice_event and diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index c1fb545e8d78..daadf3130c9f 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -1258,7 +1258,7 @@ int ib_device_register_sysfs(struct ib_device *device, int ret; int i; - device->dev.parent = device->dma_device; + WARN_ON_ONCE(!device->dev.parent); ret = dev_set_name(class_dev, "%s", device->name); if (ret) return ret; diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index e0a995b85a2d..cc0d51fb06e3 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -1290,7 +1290,7 @@ static void ib_ucm_add_one(struct ib_device *device) goto err; ucm_dev->dev.class = &cm_class; - ucm_dev->dev.parent = device->dma_device; + ucm_dev->dev.parent = device->dev.parent; ucm_dev->dev.devt = ucm_dev->cdev.dev; ucm_dev->dev.release = ib_ucm_release_dev; dev_set_name(&ucm_dev->dev, "ucm%d", ucm_dev->devnum); diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 4609b921f899..27f155d2df8d 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -34,7 +34,8 @@ #include <linux/mm.h> #include <linux/dma-mapping.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/mm.h> #include <linux/export.h> #include <linux/hugetlb.h> #include <linux/slab.h> @@ -99,9 +100,6 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, if (dmasync) dma_attrs |= DMA_ATTR_WRITE_BARRIER; - if (!size) - return ERR_PTR(-EINVAL); - /* * If the combination of the addr and size requested for this memory * region causes an integer overflow, return error. diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index 6b079a31dced..cb2742b548bb 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -32,6 +32,8 @@ #include <linux/types.h> #include <linux/sched.h> +#include <linux/sched/mm.h> +#include <linux/sched/task.h> #include <linux/pid.h> #include <linux/slab.h> #include <linux/export.h> @@ -239,6 +241,71 @@ static const struct mmu_notifier_ops ib_umem_notifiers = { .invalidate_range_end = ib_umem_notifier_invalidate_range_end, }; +struct ib_umem *ib_alloc_odp_umem(struct ib_ucontext *context, + unsigned long addr, + size_t size) +{ + struct ib_umem *umem; + struct ib_umem_odp *odp_data; + int pages = size >> PAGE_SHIFT; + int ret; + + umem = kzalloc(sizeof(*umem), GFP_KERNEL); + if (!umem) + return ERR_PTR(-ENOMEM); + + umem->context = context; + umem->length = size; + umem->address = addr; + umem->page_size = PAGE_SIZE; + umem->writable = 1; + + odp_data = kzalloc(sizeof(*odp_data), GFP_KERNEL); + if (!odp_data) { + ret = -ENOMEM; + goto out_umem; + } + odp_data->umem = umem; + + mutex_init(&odp_data->umem_mutex); + init_completion(&odp_data->notifier_completion); + + odp_data->page_list = vzalloc(pages * sizeof(*odp_data->page_list)); + if (!odp_data->page_list) { + ret = -ENOMEM; + goto out_odp_data; + } + + odp_data->dma_list = vzalloc(pages * sizeof(*odp_data->dma_list)); + if (!odp_data->dma_list) { + ret = -ENOMEM; + goto out_page_list; + } + + down_write(&context->umem_rwsem); + context->odp_mrs_count++; + rbt_ib_umem_insert(&odp_data->interval_tree, &context->umem_tree); + if (likely(!atomic_read(&context->notifier_count))) + odp_data->mn_counters_active = true; + else + list_add(&odp_data->no_private_counters, + &context->no_private_counters); + up_write(&context->umem_rwsem); + + umem->odp_data = odp_data; + + return umem; + +out_page_list: + vfree(odp_data->page_list); +out_odp_data: + kfree(odp_data); +out_umem: + kfree(umem); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(ib_alloc_odp_umem); + int ib_umem_odp_get(struct ib_ucontext *context, struct ib_umem *umem) { int ret_val; @@ -270,18 +337,20 @@ int ib_umem_odp_get(struct ib_ucontext *context, struct ib_umem *umem) init_completion(&umem->odp_data->notifier_completion); - umem->odp_data->page_list = vzalloc(ib_umem_num_pages(umem) * + if (ib_umem_num_pages(umem)) { + umem->odp_data->page_list = vzalloc(ib_umem_num_pages(umem) * sizeof(*umem->odp_data->page_list)); - if (!umem->odp_data->page_list) { - ret_val = -ENOMEM; - goto out_odp_data; - } + if (!umem->odp_data->page_list) { + ret_val = -ENOMEM; + goto out_odp_data; + } - umem->odp_data->dma_list = vzalloc(ib_umem_num_pages(umem) * + umem->odp_data->dma_list = vzalloc(ib_umem_num_pages(umem) * sizeof(*umem->odp_data->dma_list)); - if (!umem->odp_data->dma_list) { - ret_val = -ENOMEM; - goto out_page_list; + if (!umem->odp_data->dma_list) { + ret_val = -ENOMEM; + goto out_page_list; + } } /* @@ -466,6 +535,7 @@ static int ib_umem_odp_map_dma_single_page( } umem->odp_data->dma_list[page_index] = dma_addr | access_mask; umem->odp_data->page_list[page_index] = page; + umem->npages++; stored_page = 1; } else if (umem->odp_data->page_list[page_index] == page) { umem->odp_data->dma_list[page_index] |= access_mask; @@ -505,7 +575,8 @@ out: * for failure. * An -EAGAIN error code is returned when a concurrent mmu notifier prevents * the function from completing its task. - * + * An -ENOENT error code indicates that userspace process is being terminated + * and mm was already destroyed. * @umem: the umem to map and pin * @user_virt: the address from which we need to map. * @bcnt: the minimal number of bytes to pin and map. The mapping might be @@ -553,7 +624,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt, owning_mm = get_task_mm(owning_process); if (owning_mm == NULL) { - ret = -EINVAL; + ret = -ENOENT; goto out_put_task; } @@ -665,6 +736,7 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem *umem, u64 virt, put_page(page); umem->odp_data->page_list[idx] = NULL; umem->odp_data->dma_list[idx] = 0; + umem->npages--; } } mutex_unlock(&umem->odp_data->umem_mutex); diff --git a/drivers/infiniband/core/umem_rbtree.c b/drivers/infiniband/core/umem_rbtree.c index 727d788448f5..d176597b4d78 100644 --- a/drivers/infiniband/core/umem_rbtree.c +++ b/drivers/infiniband/core/umem_rbtree.c @@ -78,17 +78,32 @@ int rbt_ib_umem_for_each_in_range(struct rb_root *root, void *cookie) { int ret_val = 0; - struct umem_odp_node *node; + struct umem_odp_node *node, *next; struct ib_umem_odp *umem; if (unlikely(start == last)) return ret_val; - for (node = rbt_ib_umem_iter_first(root, start, last - 1); node; - node = rbt_ib_umem_iter_next(node, start, last - 1)) { + for (node = rbt_ib_umem_iter_first(root, start, last - 1); + node; node = next) { + next = rbt_ib_umem_iter_next(node, start, last - 1); umem = container_of(node, struct ib_umem_odp, interval_tree); ret_val = cb(umem->umem, start, last, cookie) || ret_val; } return ret_val; } +EXPORT_SYMBOL(rbt_ib_umem_for_each_in_range); + +struct ib_umem_odp *rbt_ib_umem_lookup(struct rb_root *root, + u64 addr, u64 length) +{ + struct umem_odp_node *node; + + node = rbt_ib_umem_iter_first(root, addr, addr + length - 1); + if (node) + return container_of(node, struct ib_umem_odp, interval_tree); + return NULL; + +} +EXPORT_SYMBOL(rbt_ib_umem_lookup); diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index 249b403b43a4..aca7ff7abedc 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -1188,7 +1188,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num, if (cdev_add(&port->cdev, base, 1)) goto err_cdev; - port->dev = device_create(umad_class, device->dma_device, + port->dev = device_create(umad_class, device->dev.parent, port->cdev.dev, port, "umad%d", port->dev_num); if (IS_ERR(port->dev)) @@ -1207,7 +1207,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num, if (cdev_add(&port->sm_cdev, base, 1)) goto err_sm_cdev; - port->sm_dev = device_create(umad_class, device->dma_device, + port->sm_dev = device_create(umad_class, device->dev.parent, port->sm_cdev.dev, port, "issm%d", port->dev_num); if (IS_ERR(port->sm_dev)) diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index 455034ac994e..e1bedf0bac04 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -228,6 +228,7 @@ struct ib_uverbs_flow_spec { struct ib_uverbs_flow_spec_ipv4 ipv4; struct ib_uverbs_flow_spec_tcp_udp tcp_udp; struct ib_uverbs_flow_spec_ipv6 ipv6; + struct ib_uverbs_flow_spec_action_tag flow_tag; }; }; diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 700782203483..7b7a76e1279a 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -316,6 +316,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, struct ib_udata udata; struct ib_ucontext *ucontext; struct file *filp; + struct ib_rdmacg_object cg_obj; int ret; if (out_len < sizeof resp) @@ -335,13 +336,18 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, (unsigned long) cmd.response + sizeof resp, in_len - sizeof cmd, out_len - sizeof resp); + ret = ib_rdmacg_try_charge(&cg_obj, ib_dev, RDMACG_RESOURCE_HCA_HANDLE); + if (ret) + goto err; + ucontext = ib_dev->alloc_ucontext(ib_dev, &udata); if (IS_ERR(ucontext)) { ret = PTR_ERR(ucontext); - goto err; + goto err_alloc; } ucontext->device = ib_dev; + ucontext->cg_obj = cg_obj; INIT_LIST_HEAD(&ucontext->pd_list); INIT_LIST_HEAD(&ucontext->mr_list); INIT_LIST_HEAD(&ucontext->mw_list); @@ -407,6 +413,9 @@ err_free: put_pid(ucontext->tgid); ib_dev->dealloc_ucontext(ucontext); +err_alloc: + ib_rdmacg_uncharge(&cg_obj, ib_dev, RDMACG_RESOURCE_HCA_HANDLE); + err: mutex_unlock(&file->mutex); return ret; @@ -561,6 +570,13 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file, return -ENOMEM; init_uobj(uobj, 0, file->ucontext, &pd_lock_class); + ret = ib_rdmacg_try_charge(&uobj->cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + if (ret) { + kfree(uobj); + return ret; + } + down_write(&uobj->mutex); pd = ib_dev->alloc_pd(ib_dev, file->ucontext, &udata); @@ -605,6 +621,7 @@ err_idr: ib_dealloc_pd(pd); err: + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); put_uobj_write(uobj); return ret; } @@ -637,6 +654,8 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file, if (ret) goto err_put; + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + uobj->live = 0; put_uobj_write(uobj); @@ -1006,6 +1025,10 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, goto err_put; } } + ret = ib_rdmacg_try_charge(&uobj->cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + if (ret) + goto err_charge; mr = pd->device->reg_user_mr(pd, cmd.start, cmd.length, cmd.hca_va, cmd.access_flags, &udata); @@ -1054,6 +1077,9 @@ err_unreg: ib_dereg_mr(mr); err_put: + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + +err_charge: put_pd_read(pd); err_free: @@ -1178,6 +1204,8 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file, if (ret) return ret; + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + idr_remove_uobj(&ib_uverbs_mr_idr, uobj); mutex_lock(&file->mutex); @@ -1226,6 +1254,11 @@ ssize_t ib_uverbs_alloc_mw(struct ib_uverbs_file *file, in_len - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr), out_len - sizeof(resp)); + ret = ib_rdmacg_try_charge(&uobj->cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + if (ret) + goto err_charge; + mw = pd->device->alloc_mw(pd, cmd.mw_type, &udata); if (IS_ERR(mw)) { ret = PTR_ERR(mw); @@ -1271,6 +1304,9 @@ err_unalloc: uverbs_dealloc_mw(mw); err_put: + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + +err_charge: put_pd_read(pd); err_free: @@ -1306,6 +1342,8 @@ ssize_t ib_uverbs_dealloc_mw(struct ib_uverbs_file *file, if (ret) return ret; + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + idr_remove_uobj(&ib_uverbs_mw_idr, uobj); mutex_lock(&file->mutex); @@ -1405,6 +1443,11 @@ static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file, if (cmd_sz > offsetof(typeof(*cmd), flags) + sizeof(cmd->flags)) attr.flags = cmd->flags; + ret = ib_rdmacg_try_charge(&obj->uobject.cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + if (ret) + goto err_charge; + cq = ib_dev->create_cq(ib_dev, &attr, file->ucontext, uhw); if (IS_ERR(cq)) { @@ -1452,6 +1495,10 @@ err_free: ib_destroy_cq(cq); err_file: + ib_rdmacg_uncharge(&obj->uobject.cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + +err_charge: if (ev_file) ib_uverbs_release_ucq(file, ev_file, obj); @@ -1732,6 +1779,8 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file, if (ret) return ret; + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + idr_remove_uobj(&ib_uverbs_cq_idr, uobj); mutex_lock(&file->mutex); @@ -1891,7 +1940,8 @@ static int create_qp(struct ib_uverbs_file *file, IB_QP_CREATE_CROSS_CHANNEL | IB_QP_CREATE_MANAGED_SEND | IB_QP_CREATE_MANAGED_RECV | - IB_QP_CREATE_SCATTER_FCS)) { + IB_QP_CREATE_SCATTER_FCS | + IB_QP_CREATE_CVLAN_STRIPPING)) { ret = -EINVAL; goto err_put; } @@ -1904,6 +1954,11 @@ static int create_qp(struct ib_uverbs_file *file, goto err_put; } + ret = ib_rdmacg_try_charge(&obj->uevent.uobject.cg_obj, device, + RDMACG_RESOURCE_HCA_OBJECT); + if (ret) + goto err_put; + if (cmd->qp_type == IB_QPT_XRC_TGT) qp = ib_create_qp(pd, &attr); else @@ -1911,7 +1966,7 @@ static int create_qp(struct ib_uverbs_file *file, if (IS_ERR(qp)) { ret = PTR_ERR(qp); - goto err_put; + goto err_create; } if (cmd->qp_type != IB_QPT_XRC_TGT) { @@ -1992,6 +2047,10 @@ err_cb: err_destroy: ib_destroy_qp(qp); +err_create: + ib_rdmacg_uncharge(&obj->uevent.uobject.cg_obj, device, + RDMACG_RESOURCE_HCA_OBJECT); + err_put: if (xrcd) put_xrcd_read(xrcd_uobj); @@ -2518,6 +2577,8 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, if (ret) return ret; + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + if (obj->uxrcd) atomic_dec(&obj->uxrcd->refcnt); @@ -2969,11 +3030,16 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file, memset(&attr.dmac, 0, sizeof(attr.dmac)); memcpy(attr.grh.dgid.raw, cmd.attr.grh.dgid, 16); + ret = ib_rdmacg_try_charge(&uobj->cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + if (ret) + goto err_charge; + ah = pd->device->create_ah(pd, &attr, &udata); if (IS_ERR(ah)) { ret = PTR_ERR(ah); - goto err_put; + goto err_create; } ah->device = pd->device; @@ -3012,7 +3078,10 @@ err_copy: err_destroy: ib_destroy_ah(ah); -err_put: +err_create: + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + +err_charge: put_pd_read(pd); err: @@ -3046,6 +3115,8 @@ ssize_t ib_uverbs_destroy_ah(struct ib_uverbs_file *file, if (ret) return ret; + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + idr_remove_uobj(&ib_uverbs_ah_idr, uobj); mutex_lock(&file->mutex); @@ -3143,6 +3214,25 @@ out_put: return ret ? ret : in_len; } +static int kern_spec_to_ib_spec_action(struct ib_uverbs_flow_spec *kern_spec, + union ib_flow_spec *ib_spec) +{ + ib_spec->type = kern_spec->type; + switch (ib_spec->type) { + case IB_FLOW_SPEC_ACTION_TAG: + if (kern_spec->flow_tag.size != + sizeof(struct ib_uverbs_flow_spec_action_tag)) + return -EINVAL; + + ib_spec->flow_tag.size = sizeof(struct ib_flow_spec_action_tag); + ib_spec->flow_tag.tag_id = kern_spec->flow_tag.tag_id; + break; + default: + return -EINVAL; + } + return 0; +} + static size_t kern_spec_filter_sz(struct ib_uverbs_flow_spec_hdr *spec) { /* Returns user space filter size, includes padding */ @@ -3167,8 +3257,8 @@ static ssize_t spec_filter_size(void *kern_spec_filter, u16 kern_filter_size, return kern_filter_size; } -static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec, - union ib_flow_spec *ib_spec) +static int kern_spec_to_ib_spec_filter(struct ib_uverbs_flow_spec *kern_spec, + union ib_flow_spec *ib_spec) { ssize_t actual_filter_sz; ssize_t kern_filter_sz; @@ -3263,6 +3353,18 @@ static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec, return 0; } +static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec, + union ib_flow_spec *ib_spec) +{ + if (kern_spec->reserved) + return -EINVAL; + + if (kern_spec->type >= IB_FLOW_SPEC_ACTION_TAG) + return kern_spec_to_ib_spec_action(kern_spec, ib_spec); + else + return kern_spec_to_ib_spec_filter(kern_spec, ib_spec); +} + int ib_uverbs_ex_create_wq(struct ib_uverbs_file *file, struct ib_device *ib_dev, struct ib_udata *ucore, @@ -3325,6 +3427,9 @@ int ib_uverbs_ex_create_wq(struct ib_uverbs_file *file, wq_init_attr.wq_context = file; wq_init_attr.wq_type = cmd.wq_type; wq_init_attr.event_handler = ib_uverbs_wq_event_handler; + if (ucore->inlen >= (offsetof(typeof(cmd), create_flags) + + sizeof(cmd.create_flags))) + wq_init_attr.create_flags = cmd.create_flags; obj->uevent.events_reported = 0; INIT_LIST_HEAD(&obj->uevent.event_list); wq = pd->device->create_wq(pd, &wq_init_attr, uhw); @@ -3480,7 +3585,7 @@ int ib_uverbs_ex_modify_wq(struct ib_uverbs_file *file, if (!cmd.attr_mask) return -EINVAL; - if (cmd.attr_mask > (IB_WQ_STATE | IB_WQ_CUR_STATE)) + if (cmd.attr_mask > (IB_WQ_STATE | IB_WQ_CUR_STATE | IB_WQ_FLAGS)) return -EINVAL; wq = idr_read_wq(cmd.wq_handle, file->ucontext); @@ -3489,6 +3594,10 @@ int ib_uverbs_ex_modify_wq(struct ib_uverbs_file *file, wq_attr.curr_wq_state = cmd.curr_wq_state; wq_attr.wq_state = cmd.wq_state; + if (cmd.attr_mask & IB_WQ_FLAGS) { + wq_attr.flags = cmd.flags; + wq_attr.flags_mask = cmd.flags_mask; + } ret = wq->device->modify_wq(wq, &wq_attr, cmd.attr_mask, uhw); put_wq_read(wq); return ret; @@ -3822,10 +3931,16 @@ int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file, err = -EINVAL; goto err_free; } + + err = ib_rdmacg_try_charge(&uobj->cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + if (err) + goto err_free; + flow_id = ib_create_flow(qp, flow_attr, IB_FLOW_DOMAIN_USER); if (IS_ERR(flow_id)) { err = PTR_ERR(flow_id); - goto err_free; + goto err_create; } flow_id->uobject = uobj; uobj->object = flow_id; @@ -3858,6 +3973,8 @@ err_copy: idr_remove_uobj(&ib_uverbs_rule_idr, uobj); destroy_flow: ib_destroy_flow(flow_id); +err_create: + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); err_free: kfree(flow_attr); err_put: @@ -3897,8 +4014,11 @@ int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file, flow_id = uobj->object; ret = ib_destroy_flow(flow_id); - if (!ret) + if (!ret) { + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); uobj->live = 0; + } put_uobj_write(uobj); @@ -3966,6 +4086,11 @@ static int __uverbs_create_xsrq(struct ib_uverbs_file *file, obj->uevent.events_reported = 0; INIT_LIST_HEAD(&obj->uevent.event_list); + ret = ib_rdmacg_try_charge(&obj->uevent.uobject.cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); + if (ret) + goto err_put_cq; + srq = pd->device->create_srq(pd, &attr, udata); if (IS_ERR(srq)) { ret = PTR_ERR(srq); @@ -4030,6 +4155,8 @@ err_destroy: ib_destroy_srq(srq); err_put: + ib_rdmacg_uncharge(&obj->uevent.uobject.cg_obj, ib_dev, + RDMACG_RESOURCE_HCA_OBJECT); put_pd_read(pd); err_put_cq: @@ -4216,6 +4343,8 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file, if (ret) return ret; + ib_rdmacg_uncharge(&uobj->cg_obj, ib_dev, RDMACG_RESOURCE_HCA_OBJECT); + if (srq_type == IB_SRQT_XRC) { us = container_of(obj, struct ib_usrq_object, uevent); atomic_dec(&us->uxrcd->refcnt); @@ -4323,6 +4452,12 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file, resp.max_wq_type_rq = attr.max_wq_type_rq; resp.response_length += sizeof(resp.max_wq_type_rq); + + if (ucore->outlen < resp.response_length + sizeof(resp.raw_packet_caps)) + goto end; + + resp.raw_packet_caps = attr.raw_packet_caps; + resp.response_length += sizeof(resp.raw_packet_caps); end: err = ib_copy_to_udata(ucore, &resp, resp.response_length); return err; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index b3f95d453fba..35c788a32e26 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -51,6 +51,7 @@ #include <rdma/ib.h> #include "uverbs.h" +#include "core_priv.h" MODULE_AUTHOR("Roland Dreier"); MODULE_DESCRIPTION("InfiniBand userspace verbs access"); @@ -237,6 +238,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, idr_remove_uobj(&ib_uverbs_ah_idr, uobj); ib_destroy_ah(ah); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); kfree(uobj); } @@ -246,6 +249,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, idr_remove_uobj(&ib_uverbs_mw_idr, uobj); uverbs_dealloc_mw(mw); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); kfree(uobj); } @@ -254,6 +259,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, idr_remove_uobj(&ib_uverbs_rule_idr, uobj); ib_destroy_flow(flow_id); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); kfree(uobj); } @@ -266,6 +273,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, if (qp == qp->real_qp) ib_uverbs_detach_umcast(qp, uqp); ib_destroy_qp(qp); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); ib_uverbs_release_uevent(file, &uqp->uevent); kfree(uqp); } @@ -298,6 +307,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, idr_remove_uobj(&ib_uverbs_srq_idr, uobj); ib_destroy_srq(srq); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); ib_uverbs_release_uevent(file, uevent); kfree(uevent); } @@ -310,6 +321,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, idr_remove_uobj(&ib_uverbs_cq_idr, uobj); ib_destroy_cq(cq); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); ib_uverbs_release_ucq(file, ev_file, ucq); kfree(ucq); } @@ -319,6 +332,8 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, idr_remove_uobj(&ib_uverbs_mr_idr, uobj); ib_dereg_mr(mr); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); kfree(uobj); } @@ -339,11 +354,16 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, idr_remove_uobj(&ib_uverbs_pd_idr, uobj); ib_dealloc_pd(pd); + ib_rdmacg_uncharge(&uobj->cg_obj, context->device, + RDMACG_RESOURCE_HCA_OBJECT); kfree(uobj); } put_pid(context->tgid); + ib_rdmacg_uncharge(&context->cg_obj, context->device, + RDMACG_RESOURCE_HCA_HANDLE); + return context->device->dealloc_ucontext(context); } @@ -1174,7 +1194,7 @@ static void ib_uverbs_add_one(struct ib_device *device) if (cdev_add(&uverbs_dev->cdev, base, 1)) goto err_cdev; - uverbs_dev->dev = device_create(uverbs_class, device->dma_device, + uverbs_dev->dev = device_create(uverbs_class, device->dev.parent, uverbs_dev->cdev.dev, uverbs_dev, "uverbs%d", uverbs_dev->devnum); if (IS_ERR(uverbs_dev->dev)) diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 71580cc28c9e..85ed5051fdfd 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1205,8 +1205,7 @@ int ib_resolve_eth_dmac(struct ib_device *device, { int ret = 0; - if (ah_attr->port_num < rdma_start_port(device) || - ah_attr->port_num > rdma_end_port(device)) + if (!rdma_is_port_valid(device, ah_attr->port_num)) return -EINVAL; if (!rdma_cap_eth_ah(device, ah_attr->port_num)) @@ -1949,17 +1948,12 @@ static void ib_drain_qp_done(struct ib_cq *cq, struct ib_wc *wc) */ static void __ib_drain_sq(struct ib_qp *qp) { + struct ib_cq *cq = qp->send_cq; struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR }; struct ib_drain_cqe sdrain; struct ib_send_wr swr = {}, *bad_swr; int ret; - if (qp->send_cq->poll_ctx == IB_POLL_DIRECT) { - WARN_ONCE(qp->send_cq->poll_ctx == IB_POLL_DIRECT, - "IB_POLL_DIRECT poll_ctx not supported for drain\n"); - return; - } - swr.wr_cqe = &sdrain.cqe; sdrain.cqe.done = ib_drain_qp_done; init_completion(&sdrain.done); @@ -1976,7 +1970,11 @@ static void __ib_drain_sq(struct ib_qp *qp) return; } - wait_for_completion(&sdrain.done); + if (cq->poll_ctx == IB_POLL_DIRECT) + while (wait_for_completion_timeout(&sdrain.done, HZ / 10) <= 0) + ib_process_cq_direct(cq, -1); + else + wait_for_completion(&sdrain.done); } /* @@ -1984,17 +1982,12 @@ static void __ib_drain_sq(struct ib_qp *qp) */ static void __ib_drain_rq(struct ib_qp *qp) { + struct ib_cq *cq = qp->recv_cq; struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR }; struct ib_drain_cqe rdrain; struct ib_recv_wr rwr = {}, *bad_rwr; int ret; - if (qp->recv_cq->poll_ctx == IB_POLL_DIRECT) { - WARN_ONCE(qp->recv_cq->poll_ctx == IB_POLL_DIRECT, - "IB_POLL_DIRECT poll_ctx not supported for drain\n"); - return; - } - rwr.wr_cqe = &rdrain.cqe; rdrain.cqe.done = ib_drain_qp_done; init_completion(&rdrain.done); @@ -2011,7 +2004,11 @@ static void __ib_drain_rq(struct ib_qp *qp) return; } - wait_for_completion(&rdrain.done); + if (cq->poll_ctx == IB_POLL_DIRECT) + while (wait_for_completion_timeout(&rdrain.done, HZ / 10) <= 0) + ib_process_cq_direct(cq, -1); + else + wait_for_completion(&rdrain.done); } /** @@ -2028,8 +2025,7 @@ static void __ib_drain_rq(struct ib_qp *qp) * ensure there is room in the CQ and SQ for the drain work request and * completion. * - * allocate the CQ using ib_alloc_cq() and the CQ poll context cannot be - * IB_POLL_DIRECT. + * allocate the CQ using ib_alloc_cq(). * * ensure that there are no other contexts that are posting WRs concurrently. * Otherwise the drain is not guaranteed. @@ -2057,8 +2053,7 @@ EXPORT_SYMBOL(ib_drain_sq); * ensure there is room in the CQ and RQ for the drain work request and * completion. * - * allocate the CQ using ib_alloc_cq() and the CQ poll context cannot be - * IB_POLL_DIRECT. + * allocate the CQ using ib_alloc_cq(). * * ensure that there are no other contexts that are posting WRs concurrently. * Otherwise the drain is not guaranteed. @@ -2082,8 +2077,7 @@ EXPORT_SYMBOL(ib_drain_rq); * ensure there is room in the CQ(s), SQ, and RQ for drain work requests * and completions. * - * allocate the CQs using ib_alloc_cq() and the CQ poll context cannot be - * IB_POLL_DIRECT. + * allocate the CQs using ib_alloc_cq(). * * ensure that there are no other contexts that are posting WRs concurrently. * Otherwise the drain is not guaranteed. diff --git a/drivers/infiniband/hw/Makefile b/drivers/infiniband/hw/Makefile index ed553de2ca12..34c93abf0fe0 100644 --- a/drivers/infiniband/hw/Makefile +++ b/drivers/infiniband/hw/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_INFINIBAND_USNIC) += usnic/ obj-$(CONFIG_INFINIBAND_HFI1) += hfi1/ obj-$(CONFIG_INFINIBAND_HNS) += hns/ obj-$(CONFIG_INFINIBAND_QEDR) += qedr/ +obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re/ diff --git a/drivers/infiniband/hw/bnxt_re/Kconfig b/drivers/infiniband/hw/bnxt_re/Kconfig new file mode 100644 index 000000000000..19982a4a9bba --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/Kconfig @@ -0,0 +1,9 @@ +config INFINIBAND_BNXT_RE + tristate "Broadcom Netxtreme HCA support" + depends on ETHERNET && NETDEVICES && PCI && INET && DCB + select NET_VENDOR_BROADCOM + select BNXT + ---help--- + This driver supports Broadcom NetXtreme-E 10/25/40/50 gigabit + RoCE HCAs. To compile this driver as a module, choose M here: + the module will be called bnxt_re. diff --git a/drivers/infiniband/hw/bnxt_re/Makefile b/drivers/infiniband/hw/bnxt_re/Makefile new file mode 100644 index 000000000000..036f84efbc73 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/Makefile @@ -0,0 +1,6 @@ + +ccflags-y := -Idrivers/net/ethernet/broadcom/bnxt +obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o +bnxt_re-y := main.o ib_verbs.o \ + qplib_res.o qplib_rcfw.o \ + qplib_sp.o qplib_fp.o diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h new file mode 100644 index 000000000000..ebf7be8d4139 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h @@ -0,0 +1,146 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: Slow Path Operators (header) + * + */ + +#ifndef __BNXT_RE_H__ +#define __BNXT_RE_H__ +#define ROCE_DRV_MODULE_NAME "bnxt_re" +#define ROCE_DRV_MODULE_VERSION "1.0.0" + +#define BNXT_RE_DESC "Broadcom NetXtreme-C/E RoCE Driver" + +#define BNXT_RE_PAGE_SIZE_4K BIT(12) +#define BNXT_RE_PAGE_SIZE_8K BIT(13) +#define BNXT_RE_PAGE_SIZE_64K BIT(16) +#define BNXT_RE_PAGE_SIZE_2M BIT(21) +#define BNXT_RE_PAGE_SIZE_8M BIT(23) +#define BNXT_RE_PAGE_SIZE_1G BIT(30) + +#define BNXT_RE_MAX_QPC_COUNT (64 * 1024) +#define BNXT_RE_MAX_MRW_COUNT (64 * 1024) +#define BNXT_RE_MAX_SRQC_COUNT (64 * 1024) +#define BNXT_RE_MAX_CQ_COUNT (64 * 1024) + +struct bnxt_re_work { + struct work_struct work; + unsigned long event; + struct bnxt_re_dev *rdev; + struct net_device *vlan_dev; +}; + +struct bnxt_re_sqp_entries { + struct bnxt_qplib_sge sge; + u64 wrid; + /* For storing the actual qp1 cqe */ + struct bnxt_qplib_cqe cqe; + struct bnxt_re_qp *qp1_qp; +}; + +#define BNXT_RE_MIN_MSIX 2 +#define BNXT_RE_MAX_MSIX 16 +#define BNXT_RE_AEQ_IDX 0 +#define BNXT_RE_NQ_IDX 1 + +struct bnxt_re_dev { + struct ib_device ibdev; + struct list_head list; + unsigned long flags; +#define BNXT_RE_FLAG_NETDEV_REGISTERED 0 +#define BNXT_RE_FLAG_IBDEV_REGISTERED 1 +#define BNXT_RE_FLAG_GOT_MSIX 2 +#define BNXT_RE_FLAG_RCFW_CHANNEL_EN 8 +#define BNXT_RE_FLAG_QOS_WORK_REG 16 + struct net_device *netdev; + unsigned int version, major, minor; + struct bnxt_en_dev *en_dev; + struct bnxt_msix_entry msix_entries[BNXT_RE_MAX_MSIX]; + int num_msix; + + int id; + + struct delayed_work worker; + u8 cur_prio_map; + + /* FP Notification Queue (CQ & SRQ) */ + struct tasklet_struct nq_task; + + /* RCFW Channel */ + struct bnxt_qplib_rcfw rcfw; + + /* NQ */ + struct bnxt_qplib_nq nq; + + /* Device Resources */ + struct bnxt_qplib_dev_attr dev_attr; + struct bnxt_qplib_ctx qplib_ctx; + struct bnxt_qplib_res qplib_res; + struct bnxt_qplib_dpi dpi_privileged; + + atomic_t qp_count; + struct mutex qp_lock; /* protect qp list */ + struct list_head qp_list; + + atomic_t cq_count; + atomic_t srq_count; + atomic_t mr_count; + atomic_t mw_count; + /* Max of 2 lossless traffic class supported per port */ + u16 cosq[2]; + + /* QP for for handling QP1 packets */ + u32 sqp_id; + struct bnxt_re_qp *qp1_sqp; + struct bnxt_re_ah *sqp_ah; + struct bnxt_re_sqp_entries sqp_tbl[1024]; +}; + +#define to_bnxt_re_dev(ptr, member) \ + container_of((ptr), struct bnxt_re_dev, member) + +#define BNXT_RE_ROCE_V1_PACKET 0 +#define BNXT_RE_ROCEV2_IPV4_PACKET 2 +#define BNXT_RE_ROCEV2_IPV6_PACKET 3 + +static inline struct device *rdev_to_dev(struct bnxt_re_dev *rdev) +{ + if (rdev) + return &rdev->ibdev.dev; + return NULL; +} + +#endif diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c new file mode 100644 index 000000000000..33af2e3de399 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -0,0 +1,3202 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: IB Verbs interpreter + */ + +#include <linux/interrupt.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/if_ether.h> + +#include <rdma/ib_verbs.h> +#include <rdma/ib_user_verbs.h> +#include <rdma/ib_umem.h> +#include <rdma/ib_addr.h> +#include <rdma/ib_mad.h> +#include <rdma/ib_cache.h> + +#include "bnxt_ulp.h" + +#include "roce_hsi.h" +#include "qplib_res.h" +#include "qplib_sp.h" +#include "qplib_fp.h" +#include "qplib_rcfw.h" + +#include "bnxt_re.h" +#include "ib_verbs.h" +#include <rdma/bnxt_re-abi.h> + +static int bnxt_re_build_sgl(struct ib_sge *ib_sg_list, + struct bnxt_qplib_sge *sg_list, int num) +{ + int i, total = 0; + + for (i = 0; i < num; i++) { + sg_list[i].addr = ib_sg_list[i].addr; + sg_list[i].lkey = ib_sg_list[i].lkey; + sg_list[i].size = ib_sg_list[i].length; + total += sg_list[i].size; + } + return total; +} + +/* Device */ +struct net_device *bnxt_re_get_netdev(struct ib_device *ibdev, u8 port_num) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct net_device *netdev = NULL; + + rcu_read_lock(); + if (rdev) + netdev = rdev->netdev; + if (netdev) + dev_hold(netdev); + + rcu_read_unlock(); + return netdev; +} + +int bnxt_re_query_device(struct ib_device *ibdev, + struct ib_device_attr *ib_attr, + struct ib_udata *udata) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; + + memset(ib_attr, 0, sizeof(*ib_attr)); + + ib_attr->fw_ver = (u64)(unsigned long)(dev_attr->fw_ver); + bnxt_qplib_get_guid(rdev->netdev->dev_addr, + (u8 *)&ib_attr->sys_image_guid); + ib_attr->max_mr_size = ~0ull; + ib_attr->page_size_cap = BNXT_RE_PAGE_SIZE_4K | BNXT_RE_PAGE_SIZE_8K | + BNXT_RE_PAGE_SIZE_64K | BNXT_RE_PAGE_SIZE_2M | + BNXT_RE_PAGE_SIZE_8M | BNXT_RE_PAGE_SIZE_1G; + + ib_attr->vendor_id = rdev->en_dev->pdev->vendor; + ib_attr->vendor_part_id = rdev->en_dev->pdev->device; + ib_attr->hw_ver = rdev->en_dev->pdev->subsystem_device; + ib_attr->max_qp = dev_attr->max_qp; + ib_attr->max_qp_wr = dev_attr->max_qp_wqes; + ib_attr->device_cap_flags = + IB_DEVICE_CURR_QP_STATE_MOD + | IB_DEVICE_RC_RNR_NAK_GEN + | IB_DEVICE_SHUTDOWN_PORT + | IB_DEVICE_SYS_IMAGE_GUID + | IB_DEVICE_LOCAL_DMA_LKEY + | IB_DEVICE_RESIZE_MAX_WR + | IB_DEVICE_PORT_ACTIVE_EVENT + | IB_DEVICE_N_NOTIFY_CQ + | IB_DEVICE_MEM_WINDOW + | IB_DEVICE_MEM_WINDOW_TYPE_2B + | IB_DEVICE_MEM_MGT_EXTENSIONS; + ib_attr->max_sge = dev_attr->max_qp_sges; + ib_attr->max_sge_rd = dev_attr->max_qp_sges; + ib_attr->max_cq = dev_attr->max_cq; + ib_attr->max_cqe = dev_attr->max_cq_wqes; + ib_attr->max_mr = dev_attr->max_mr; + ib_attr->max_pd = dev_attr->max_pd; + ib_attr->max_qp_rd_atom = dev_attr->max_qp_rd_atom; + ib_attr->max_qp_init_rd_atom = dev_attr->max_qp_rd_atom; + ib_attr->atomic_cap = IB_ATOMIC_HCA; + ib_attr->masked_atomic_cap = IB_ATOMIC_HCA; + + ib_attr->max_ee_rd_atom = 0; + ib_attr->max_res_rd_atom = 0; + ib_attr->max_ee_init_rd_atom = 0; + ib_attr->max_ee = 0; + ib_attr->max_rdd = 0; + ib_attr->max_mw = dev_attr->max_mw; + ib_attr->max_raw_ipv6_qp = 0; + ib_attr->max_raw_ethy_qp = dev_attr->max_raw_ethy_qp; + ib_attr->max_mcast_grp = 0; + ib_attr->max_mcast_qp_attach = 0; + ib_attr->max_total_mcast_qp_attach = 0; + ib_attr->max_ah = dev_attr->max_ah; + + ib_attr->max_fmr = dev_attr->max_fmr; + ib_attr->max_map_per_fmr = 1; /* ? */ + + ib_attr->max_srq = dev_attr->max_srq; + ib_attr->max_srq_wr = dev_attr->max_srq_wqes; + ib_attr->max_srq_sge = dev_attr->max_srq_sges; + + ib_attr->max_fast_reg_page_list_len = MAX_PBL_LVL_1_PGS; + + ib_attr->max_pkeys = 1; + ib_attr->local_ca_ack_delay = 0; + return 0; +} + +int bnxt_re_modify_device(struct ib_device *ibdev, + int device_modify_mask, + struct ib_device_modify *device_modify) +{ + switch (device_modify_mask) { + case IB_DEVICE_MODIFY_SYS_IMAGE_GUID: + /* Modify the GUID requires the modification of the GID table */ + /* GUID should be made as READ-ONLY */ + break; + case IB_DEVICE_MODIFY_NODE_DESC: + /* Node Desc should be made as READ-ONLY */ + break; + default: + break; + } + return 0; +} + +static void __to_ib_speed_width(struct net_device *netdev, u8 *speed, u8 *width) +{ + struct ethtool_link_ksettings lksettings; + u32 espeed; + + if (netdev->ethtool_ops && netdev->ethtool_ops->get_link_ksettings) { + memset(&lksettings, 0, sizeof(lksettings)); + rtnl_lock(); + netdev->ethtool_ops->get_link_ksettings(netdev, &lksettings); + rtnl_unlock(); + espeed = lksettings.base.speed; + } else { + espeed = SPEED_UNKNOWN; + } + switch (espeed) { + case SPEED_1000: + *speed = IB_SPEED_SDR; + *width = IB_WIDTH_1X; + break; + case SPEED_10000: + *speed = IB_SPEED_QDR; + *width = IB_WIDTH_1X; + break; + case SPEED_20000: + *speed = IB_SPEED_DDR; + *width = IB_WIDTH_4X; + break; + case SPEED_25000: + *speed = IB_SPEED_EDR; + *width = IB_WIDTH_1X; + break; + case SPEED_40000: + *speed = IB_SPEED_QDR; + *width = IB_WIDTH_4X; + break; + case SPEED_50000: + break; + default: + *speed = IB_SPEED_SDR; + *width = IB_WIDTH_1X; + break; + } +} + +/* Port */ +int bnxt_re_query_port(struct ib_device *ibdev, u8 port_num, + struct ib_port_attr *port_attr) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; + + memset(port_attr, 0, sizeof(*port_attr)); + + if (netif_running(rdev->netdev) && netif_carrier_ok(rdev->netdev)) { + port_attr->state = IB_PORT_ACTIVE; + port_attr->phys_state = 5; + } else { + port_attr->state = IB_PORT_DOWN; + port_attr->phys_state = 3; + } + port_attr->max_mtu = IB_MTU_4096; + port_attr->active_mtu = iboe_get_mtu(rdev->netdev->mtu); + port_attr->gid_tbl_len = dev_attr->max_sgid; + port_attr->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_REINIT_SUP | + IB_PORT_DEVICE_MGMT_SUP | + IB_PORT_VENDOR_CLASS_SUP | + IB_PORT_IP_BASED_GIDS; + + /* Max MSG size set to 2G for now */ + port_attr->max_msg_sz = 0x80000000; + port_attr->bad_pkey_cntr = 0; + port_attr->qkey_viol_cntr = 0; + port_attr->pkey_tbl_len = dev_attr->max_pkey; + port_attr->lid = 0; + port_attr->sm_lid = 0; + port_attr->lmc = 0; + port_attr->max_vl_num = 4; + port_attr->sm_sl = 0; + port_attr->subnet_timeout = 0; + port_attr->init_type_reply = 0; + /* call the underlying netdev's ethtool hooks to query speed settings + * for which we acquire rtnl_lock _only_ if it's registered with + * IB stack to avoid race in the NETDEV_UNREG path + */ + if (test_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags)) + __to_ib_speed_width(rdev->netdev, &port_attr->active_speed, + &port_attr->active_width); + return 0; +} + +int bnxt_re_modify_port(struct ib_device *ibdev, u8 port_num, + int port_modify_mask, + struct ib_port_modify *port_modify) +{ + switch (port_modify_mask) { + case IB_PORT_SHUTDOWN: + break; + case IB_PORT_INIT_TYPE: + break; + case IB_PORT_RESET_QKEY_CNTR: + break; + default: + break; + } + return 0; +} + +int bnxt_re_get_port_immutable(struct ib_device *ibdev, u8 port_num, + struct ib_port_immutable *immutable) +{ + struct ib_port_attr port_attr; + + if (bnxt_re_query_port(ibdev, port_num, &port_attr)) + return -EINVAL; + + immutable->pkey_tbl_len = port_attr.pkey_tbl_len; + immutable->gid_tbl_len = port_attr.gid_tbl_len; + immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; + immutable->core_cap_flags |= RDMA_CORE_CAP_PROT_ROCE_UDP_ENCAP; + immutable->max_mad_size = IB_MGMT_MAD_SIZE; + return 0; +} + +int bnxt_re_query_pkey(struct ib_device *ibdev, u8 port_num, + u16 index, u16 *pkey) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + + /* Ignore port_num */ + + memset(pkey, 0, sizeof(*pkey)); + return bnxt_qplib_get_pkey(&rdev->qplib_res, + &rdev->qplib_res.pkey_tbl, index, pkey); +} + +int bnxt_re_query_gid(struct ib_device *ibdev, u8 port_num, + int index, union ib_gid *gid) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + int rc = 0; + + /* Ignore port_num */ + memset(gid, 0, sizeof(*gid)); + rc = bnxt_qplib_get_sgid(&rdev->qplib_res, + &rdev->qplib_res.sgid_tbl, index, + (struct bnxt_qplib_gid *)gid); + return rc; +} + +int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num, + unsigned int index, void **context) +{ + int rc = 0; + struct bnxt_re_gid_ctx *ctx, **ctx_tbl; + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_qplib_sgid_tbl *sgid_tbl = &rdev->qplib_res.sgid_tbl; + + /* Delete the entry from the hardware */ + ctx = *context; + if (!ctx) + return -EINVAL; + + if (sgid_tbl && sgid_tbl->active) { + if (ctx->idx >= sgid_tbl->max) + return -EINVAL; + ctx->refcnt--; + if (!ctx->refcnt) { + rc = bnxt_qplib_del_sgid + (sgid_tbl, + &sgid_tbl->tbl[ctx->idx], true); + if (rc) + dev_err(rdev_to_dev(rdev), + "Failed to remove GID: %#x", rc); + ctx_tbl = sgid_tbl->ctx; + ctx_tbl[ctx->idx] = NULL; + kfree(ctx); + } + } else { + return -EINVAL; + } + return rc; +} + +int bnxt_re_add_gid(struct ib_device *ibdev, u8 port_num, + unsigned int index, const union ib_gid *gid, + const struct ib_gid_attr *attr, void **context) +{ + int rc; + u32 tbl_idx = 0; + u16 vlan_id = 0xFFFF; + struct bnxt_re_gid_ctx *ctx, **ctx_tbl; + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_qplib_sgid_tbl *sgid_tbl = &rdev->qplib_res.sgid_tbl; + + if ((attr->ndev) && is_vlan_dev(attr->ndev)) + vlan_id = vlan_dev_vlan_id(attr->ndev); + + rc = bnxt_qplib_add_sgid(sgid_tbl, (struct bnxt_qplib_gid *)gid, + rdev->qplib_res.netdev->dev_addr, + vlan_id, true, &tbl_idx); + if (rc == -EALREADY) { + ctx_tbl = sgid_tbl->ctx; + ctx_tbl[tbl_idx]->refcnt++; + *context = ctx_tbl[tbl_idx]; + return 0; + } + + if (rc < 0) { + dev_err(rdev_to_dev(rdev), "Failed to add GID: %#x", rc); + return rc; + } + + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + ctx_tbl = sgid_tbl->ctx; + ctx->idx = tbl_idx; + ctx->refcnt = 1; + ctx_tbl[tbl_idx] = ctx; + + return rc; +} + +enum rdma_link_layer bnxt_re_get_link_layer(struct ib_device *ibdev, + u8 port_num) +{ + return IB_LINK_LAYER_ETHERNET; +} + +/* Protection Domains */ +int bnxt_re_dealloc_pd(struct ib_pd *ib_pd) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + int rc; + + if (ib_pd->uobject && pd->dpi.dbr) { + struct ib_ucontext *ib_uctx = ib_pd->uobject->context; + struct bnxt_re_ucontext *ucntx; + + /* Free DPI only if this is the first PD allocated by the + * application and mark the context dpi as NULL + */ + ucntx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + + rc = bnxt_qplib_dealloc_dpi(&rdev->qplib_res, + &rdev->qplib_res.dpi_tbl, + &pd->dpi); + if (rc) + dev_err(rdev_to_dev(rdev), "Failed to deallocate HW DPI"); + /* Don't fail, continue*/ + ucntx->dpi = NULL; + } + + rc = bnxt_qplib_dealloc_pd(&rdev->qplib_res, + &rdev->qplib_res.pd_tbl, + &pd->qplib_pd); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to deallocate HW PD"); + return rc; + } + + kfree(pd); + return 0; +} + +struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev, + struct ib_ucontext *ucontext, + struct ib_udata *udata) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_re_ucontext *ucntx = container_of(ucontext, + struct bnxt_re_ucontext, + ib_uctx); + struct bnxt_re_pd *pd; + int rc; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return ERR_PTR(-ENOMEM); + + pd->rdev = rdev; + if (bnxt_qplib_alloc_pd(&rdev->qplib_res.pd_tbl, &pd->qplib_pd)) { + dev_err(rdev_to_dev(rdev), "Failed to allocate HW PD"); + rc = -ENOMEM; + goto fail; + } + + if (udata) { + struct bnxt_re_pd_resp resp; + + if (!ucntx->dpi) { + /* Allocate DPI in alloc_pd to avoid failing of + * ibv_devinfo and family of application when DPIs + * are depleted. + */ + if (bnxt_qplib_alloc_dpi(&rdev->qplib_res.dpi_tbl, + &pd->dpi, ucntx)) { + rc = -ENOMEM; + goto dbfail; + } + ucntx->dpi = &pd->dpi; + } + + resp.pdid = pd->qplib_pd.id; + /* Still allow mapping this DBR to the new user PD. */ + resp.dpi = ucntx->dpi->dpi; + resp.dbr = (u64)ucntx->dpi->umdbr; + + rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to copy user response\n"); + goto dbfail; + } + } + + return &pd->ib_pd; +dbfail: + (void)bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl, + &pd->qplib_pd); +fail: + kfree(pd); + return ERR_PTR(rc); +} + +/* Address Handles */ +int bnxt_re_destroy_ah(struct ib_ah *ib_ah) +{ + struct bnxt_re_ah *ah = container_of(ib_ah, struct bnxt_re_ah, ib_ah); + struct bnxt_re_dev *rdev = ah->rdev; + int rc; + + rc = bnxt_qplib_destroy_ah(&rdev->qplib_res, &ah->qplib_ah); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to destroy HW AH"); + return rc; + } + kfree(ah); + return 0; +} + +struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd, + struct ib_ah_attr *ah_attr, + struct ib_udata *udata) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_re_ah *ah; + int rc; + u16 vlan_tag; + u8 nw_type; + + struct ib_gid_attr sgid_attr; + + if (!(ah_attr->ah_flags & IB_AH_GRH)) { + dev_err(rdev_to_dev(rdev), "Failed to alloc AH: GRH not set"); + return ERR_PTR(-EINVAL); + } + ah = kzalloc(sizeof(*ah), GFP_ATOMIC); + if (!ah) + return ERR_PTR(-ENOMEM); + + ah->rdev = rdev; + ah->qplib_ah.pd = &pd->qplib_pd; + + /* Supply the configuration for the HW */ + memcpy(ah->qplib_ah.dgid.data, ah_attr->grh.dgid.raw, + sizeof(union ib_gid)); + /* + * If RoCE V2 is enabled, stack will have two entries for + * each GID entry. Avoiding this duplicte entry in HW. Dividing + * the GID index by 2 for RoCE V2 + */ + ah->qplib_ah.sgid_index = ah_attr->grh.sgid_index / 2; + ah->qplib_ah.host_sgid_index = ah_attr->grh.sgid_index; + ah->qplib_ah.traffic_class = ah_attr->grh.traffic_class; + ah->qplib_ah.flow_label = ah_attr->grh.flow_label; + ah->qplib_ah.hop_limit = ah_attr->grh.hop_limit; + ah->qplib_ah.sl = ah_attr->sl; + if (ib_pd->uobject && + !rdma_is_multicast_addr((struct in6_addr *) + ah_attr->grh.dgid.raw) && + !rdma_link_local_addr((struct in6_addr *) + ah_attr->grh.dgid.raw)) { + union ib_gid sgid; + + rc = ib_get_cached_gid(&rdev->ibdev, 1, + ah_attr->grh.sgid_index, &sgid, + &sgid_attr); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to query gid at index %d", + ah_attr->grh.sgid_index); + goto fail; + } + if (sgid_attr.ndev) { + if (is_vlan_dev(sgid_attr.ndev)) + vlan_tag = vlan_dev_vlan_id(sgid_attr.ndev); + dev_put(sgid_attr.ndev); + } + /* Get network header type for this GID */ + nw_type = ib_gid_to_network_type(sgid_attr.gid_type, &sgid); + switch (nw_type) { + case RDMA_NETWORK_IPV4: + ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V2IPV4; + break; + case RDMA_NETWORK_IPV6: + ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V2IPV6; + break; + default: + ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V1; + break; + } + rc = rdma_addr_find_l2_eth_by_grh(&sgid, &ah_attr->grh.dgid, + ah_attr->dmac, &vlan_tag, + &sgid_attr.ndev->ifindex, + NULL); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to get dmac\n"); + goto fail; + } + } + + memcpy(ah->qplib_ah.dmac, ah_attr->dmac, ETH_ALEN); + rc = bnxt_qplib_create_ah(&rdev->qplib_res, &ah->qplib_ah); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to allocate HW AH"); + goto fail; + } + + /* Write AVID to shared page. */ + if (ib_pd->uobject) { + struct ib_ucontext *ib_uctx = ib_pd->uobject->context; + struct bnxt_re_ucontext *uctx; + unsigned long flag; + u32 *wrptr; + + uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + spin_lock_irqsave(&uctx->sh_lock, flag); + wrptr = (u32 *)(uctx->shpg + BNXT_RE_AVID_OFFT); + *wrptr = ah->qplib_ah.id; + wmb(); /* make sure cache is updated. */ + spin_unlock_irqrestore(&uctx->sh_lock, flag); + } + + return &ah->ib_ah; + +fail: + kfree(ah); + return ERR_PTR(rc); +} + +int bnxt_re_modify_ah(struct ib_ah *ib_ah, struct ib_ah_attr *ah_attr) +{ + return 0; +} + +int bnxt_re_query_ah(struct ib_ah *ib_ah, struct ib_ah_attr *ah_attr) +{ + struct bnxt_re_ah *ah = container_of(ib_ah, struct bnxt_re_ah, ib_ah); + + memcpy(ah_attr->grh.dgid.raw, ah->qplib_ah.dgid.data, + sizeof(union ib_gid)); + ah_attr->grh.sgid_index = ah->qplib_ah.host_sgid_index; + ah_attr->grh.traffic_class = ah->qplib_ah.traffic_class; + ah_attr->sl = ah->qplib_ah.sl; + memcpy(ah_attr->dmac, ah->qplib_ah.dmac, ETH_ALEN); + ah_attr->ah_flags = IB_AH_GRH; + ah_attr->port_num = 1; + ah_attr->static_rate = 0; + return 0; +} + +/* Queue Pairs */ +int bnxt_re_destroy_qp(struct ib_qp *ib_qp) +{ + struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); + struct bnxt_re_dev *rdev = qp->rdev; + int rc; + + rc = bnxt_qplib_destroy_qp(&rdev->qplib_res, &qp->qplib_qp); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to destroy HW QP"); + return rc; + } + if (ib_qp->qp_type == IB_QPT_GSI && rdev->qp1_sqp) { + rc = bnxt_qplib_destroy_ah(&rdev->qplib_res, + &rdev->sqp_ah->qplib_ah); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to destroy HW AH for shadow QP"); + return rc; + } + + rc = bnxt_qplib_destroy_qp(&rdev->qplib_res, + &rdev->qp1_sqp->qplib_qp); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to destroy Shadow QP"); + return rc; + } + mutex_lock(&rdev->qp_lock); + list_del(&rdev->qp1_sqp->list); + atomic_dec(&rdev->qp_count); + mutex_unlock(&rdev->qp_lock); + + kfree(rdev->sqp_ah); + kfree(rdev->qp1_sqp); + } + + if (qp->rumem && !IS_ERR(qp->rumem)) + ib_umem_release(qp->rumem); + if (qp->sumem && !IS_ERR(qp->sumem)) + ib_umem_release(qp->sumem); + + mutex_lock(&rdev->qp_lock); + list_del(&qp->list); + atomic_dec(&rdev->qp_count); + mutex_unlock(&rdev->qp_lock); + kfree(qp); + return 0; +} + +static u8 __from_ib_qp_type(enum ib_qp_type type) +{ + switch (type) { + case IB_QPT_GSI: + return CMDQ_CREATE_QP1_TYPE_GSI; + case IB_QPT_RC: + return CMDQ_CREATE_QP_TYPE_RC; + case IB_QPT_UD: + return CMDQ_CREATE_QP_TYPE_UD; + default: + return IB_QPT_MAX; + } +} + +static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, + struct bnxt_re_qp *qp, struct ib_udata *udata) +{ + struct bnxt_re_qp_req ureq; + struct bnxt_qplib_qp *qplib_qp = &qp->qplib_qp; + struct ib_umem *umem; + int bytes = 0; + struct ib_ucontext *context = pd->ib_pd.uobject->context; + struct bnxt_re_ucontext *cntx = container_of(context, + struct bnxt_re_ucontext, + ib_uctx); + if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) + return -EFAULT; + + bytes = (qplib_qp->sq.max_wqe * BNXT_QPLIB_MAX_SQE_ENTRY_SIZE); + /* Consider mapping PSN search memory only for RC QPs. */ + if (qplib_qp->type == CMDQ_CREATE_QP_TYPE_RC) + bytes += (qplib_qp->sq.max_wqe * sizeof(struct sq_psn_search)); + bytes = PAGE_ALIGN(bytes); + umem = ib_umem_get(context, ureq.qpsva, bytes, + IB_ACCESS_LOCAL_WRITE, 1); + if (IS_ERR(umem)) + return PTR_ERR(umem); + + qp->sumem = umem; + qplib_qp->sq.sglist = umem->sg_head.sgl; + qplib_qp->sq.nmap = umem->nmap; + qplib_qp->qp_handle = ureq.qp_handle; + + if (!qp->qplib_qp.srq) { + bytes = (qplib_qp->rq.max_wqe * BNXT_QPLIB_MAX_RQE_ENTRY_SIZE); + bytes = PAGE_ALIGN(bytes); + umem = ib_umem_get(context, ureq.qprva, bytes, + IB_ACCESS_LOCAL_WRITE, 1); + if (IS_ERR(umem)) + goto rqfail; + qp->rumem = umem; + qplib_qp->rq.sglist = umem->sg_head.sgl; + qplib_qp->rq.nmap = umem->nmap; + } + + qplib_qp->dpi = cntx->dpi; + return 0; +rqfail: + ib_umem_release(qp->sumem); + qp->sumem = NULL; + qplib_qp->sq.sglist = NULL; + qplib_qp->sq.nmap = 0; + + return PTR_ERR(umem); +} + +static struct bnxt_re_ah *bnxt_re_create_shadow_qp_ah + (struct bnxt_re_pd *pd, + struct bnxt_qplib_res *qp1_res, + struct bnxt_qplib_qp *qp1_qp) +{ + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_re_ah *ah; + union ib_gid sgid; + int rc; + + ah = kzalloc(sizeof(*ah), GFP_KERNEL); + if (!ah) + return NULL; + + memset(ah, 0, sizeof(*ah)); + ah->rdev = rdev; + ah->qplib_ah.pd = &pd->qplib_pd; + + rc = bnxt_re_query_gid(&rdev->ibdev, 1, 0, &sgid); + if (rc) + goto fail; + + /* supply the dgid data same as sgid */ + memcpy(ah->qplib_ah.dgid.data, &sgid.raw, + sizeof(union ib_gid)); + ah->qplib_ah.sgid_index = 0; + + ah->qplib_ah.traffic_class = 0; + ah->qplib_ah.flow_label = 0; + ah->qplib_ah.hop_limit = 1; + ah->qplib_ah.sl = 0; + /* Have DMAC same as SMAC */ + ether_addr_copy(ah->qplib_ah.dmac, rdev->netdev->dev_addr); + + rc = bnxt_qplib_create_ah(&rdev->qplib_res, &ah->qplib_ah); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to allocate HW AH for Shadow QP"); + goto fail; + } + + return ah; + +fail: + kfree(ah); + return NULL; +} + +static struct bnxt_re_qp *bnxt_re_create_shadow_qp + (struct bnxt_re_pd *pd, + struct bnxt_qplib_res *qp1_res, + struct bnxt_qplib_qp *qp1_qp) +{ + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_re_qp *qp; + int rc; + + qp = kzalloc(sizeof(*qp), GFP_KERNEL); + if (!qp) + return NULL; + + memset(qp, 0, sizeof(*qp)); + qp->rdev = rdev; + + /* Initialize the shadow QP structure from the QP1 values */ + ether_addr_copy(qp->qplib_qp.smac, rdev->netdev->dev_addr); + + qp->qplib_qp.pd = &pd->qplib_pd; + qp->qplib_qp.qp_handle = (u64)(unsigned long)(&qp->qplib_qp); + qp->qplib_qp.type = IB_QPT_UD; + + qp->qplib_qp.max_inline_data = 0; + qp->qplib_qp.sig_type = true; + + /* Shadow QP SQ depth should be same as QP1 RQ depth */ + qp->qplib_qp.sq.max_wqe = qp1_qp->rq.max_wqe; + qp->qplib_qp.sq.max_sge = 2; + + qp->qplib_qp.scq = qp1_qp->scq; + qp->qplib_qp.rcq = qp1_qp->rcq; + + qp->qplib_qp.rq.max_wqe = qp1_qp->rq.max_wqe; + qp->qplib_qp.rq.max_sge = qp1_qp->rq.max_sge; + + qp->qplib_qp.mtu = qp1_qp->mtu; + + qp->qplib_qp.sq_hdr_buf_size = 0; + qp->qplib_qp.rq_hdr_buf_size = BNXT_QPLIB_MAX_GRH_HDR_SIZE_IPV6; + qp->qplib_qp.dpi = &rdev->dpi_privileged; + + rc = bnxt_qplib_create_qp(qp1_res, &qp->qplib_qp); + if (rc) + goto fail; + + rdev->sqp_id = qp->qplib_qp.id; + + spin_lock_init(&qp->sq_lock); + INIT_LIST_HEAD(&qp->list); + mutex_lock(&rdev->qp_lock); + list_add_tail(&qp->list, &rdev->qp_list); + atomic_inc(&rdev->qp_count); + mutex_unlock(&rdev->qp_lock); + return qp; +fail: + kfree(qp); + return NULL; +} + +struct ib_qp *bnxt_re_create_qp(struct ib_pd *ib_pd, + struct ib_qp_init_attr *qp_init_attr, + struct ib_udata *udata) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; + struct bnxt_re_qp *qp; + struct bnxt_re_cq *cq; + int rc, entries; + + if ((qp_init_attr->cap.max_send_wr > dev_attr->max_qp_wqes) || + (qp_init_attr->cap.max_recv_wr > dev_attr->max_qp_wqes) || + (qp_init_attr->cap.max_send_sge > dev_attr->max_qp_sges) || + (qp_init_attr->cap.max_recv_sge > dev_attr->max_qp_sges) || + (qp_init_attr->cap.max_inline_data > dev_attr->max_inline_data)) + return ERR_PTR(-EINVAL); + + qp = kzalloc(sizeof(*qp), GFP_KERNEL); + if (!qp) + return ERR_PTR(-ENOMEM); + + qp->rdev = rdev; + ether_addr_copy(qp->qplib_qp.smac, rdev->netdev->dev_addr); + qp->qplib_qp.pd = &pd->qplib_pd; + qp->qplib_qp.qp_handle = (u64)(unsigned long)(&qp->qplib_qp); + qp->qplib_qp.type = __from_ib_qp_type(qp_init_attr->qp_type); + if (qp->qplib_qp.type == IB_QPT_MAX) { + dev_err(rdev_to_dev(rdev), "QP type 0x%x not supported", + qp->qplib_qp.type); + rc = -EINVAL; + goto fail; + } + qp->qplib_qp.max_inline_data = qp_init_attr->cap.max_inline_data; + qp->qplib_qp.sig_type = ((qp_init_attr->sq_sig_type == + IB_SIGNAL_ALL_WR) ? true : false); + + entries = roundup_pow_of_two(qp_init_attr->cap.max_send_wr + 1); + qp->qplib_qp.sq.max_wqe = min_t(u32, entries, + dev_attr->max_qp_wqes + 1); + + qp->qplib_qp.sq.max_sge = qp_init_attr->cap.max_send_sge; + if (qp->qplib_qp.sq.max_sge > dev_attr->max_qp_sges) + qp->qplib_qp.sq.max_sge = dev_attr->max_qp_sges; + + if (qp_init_attr->send_cq) { + cq = container_of(qp_init_attr->send_cq, struct bnxt_re_cq, + ib_cq); + if (!cq) { + dev_err(rdev_to_dev(rdev), "Send CQ not found"); + rc = -EINVAL; + goto fail; + } + qp->qplib_qp.scq = &cq->qplib_cq; + } + + if (qp_init_attr->recv_cq) { + cq = container_of(qp_init_attr->recv_cq, struct bnxt_re_cq, + ib_cq); + if (!cq) { + dev_err(rdev_to_dev(rdev), "Receive CQ not found"); + rc = -EINVAL; + goto fail; + } + qp->qplib_qp.rcq = &cq->qplib_cq; + } + + if (qp_init_attr->srq) { + dev_err(rdev_to_dev(rdev), "SRQ not supported"); + rc = -ENOTSUPP; + goto fail; + } else { + /* Allocate 1 more than what's provided so posting max doesn't + * mean empty + */ + entries = roundup_pow_of_two(qp_init_attr->cap.max_recv_wr + 1); + qp->qplib_qp.rq.max_wqe = min_t(u32, entries, + dev_attr->max_qp_wqes + 1); + + qp->qplib_qp.rq.max_sge = qp_init_attr->cap.max_recv_sge; + if (qp->qplib_qp.rq.max_sge > dev_attr->max_qp_sges) + qp->qplib_qp.rq.max_sge = dev_attr->max_qp_sges; + } + + qp->qplib_qp.mtu = ib_mtu_enum_to_int(iboe_get_mtu(rdev->netdev->mtu)); + + if (qp_init_attr->qp_type == IB_QPT_GSI) { + qp->qplib_qp.rq.max_sge = dev_attr->max_qp_sges; + if (qp->qplib_qp.rq.max_sge > dev_attr->max_qp_sges) + qp->qplib_qp.rq.max_sge = dev_attr->max_qp_sges; + qp->qplib_qp.sq.max_sge++; + if (qp->qplib_qp.sq.max_sge > dev_attr->max_qp_sges) + qp->qplib_qp.sq.max_sge = dev_attr->max_qp_sges; + + qp->qplib_qp.rq_hdr_buf_size = + BNXT_QPLIB_MAX_QP1_RQ_HDR_SIZE_V2; + + qp->qplib_qp.sq_hdr_buf_size = + BNXT_QPLIB_MAX_QP1_SQ_HDR_SIZE_V2; + qp->qplib_qp.dpi = &rdev->dpi_privileged; + rc = bnxt_qplib_create_qp1(&rdev->qplib_res, &qp->qplib_qp); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to create HW QP1"); + goto fail; + } + /* Create a shadow QP to handle the QP1 traffic */ + rdev->qp1_sqp = bnxt_re_create_shadow_qp(pd, &rdev->qplib_res, + &qp->qplib_qp); + if (!rdev->qp1_sqp) { + rc = -EINVAL; + dev_err(rdev_to_dev(rdev), + "Failed to create Shadow QP for QP1"); + goto qp_destroy; + } + rdev->sqp_ah = bnxt_re_create_shadow_qp_ah(pd, &rdev->qplib_res, + &qp->qplib_qp); + if (!rdev->sqp_ah) { + bnxt_qplib_destroy_qp(&rdev->qplib_res, + &rdev->qp1_sqp->qplib_qp); + rc = -EINVAL; + dev_err(rdev_to_dev(rdev), + "Failed to create AH entry for ShadowQP"); + goto qp_destroy; + } + + } else { + qp->qplib_qp.max_rd_atomic = dev_attr->max_qp_rd_atom; + qp->qplib_qp.max_dest_rd_atomic = dev_attr->max_qp_init_rd_atom; + if (udata) { + rc = bnxt_re_init_user_qp(rdev, pd, qp, udata); + if (rc) + goto fail; + } else { + qp->qplib_qp.dpi = &rdev->dpi_privileged; + } + + rc = bnxt_qplib_create_qp(&rdev->qplib_res, &qp->qplib_qp); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to create HW QP"); + goto fail; + } + } + + qp->ib_qp.qp_num = qp->qplib_qp.id; + spin_lock_init(&qp->sq_lock); + + if (udata) { + struct bnxt_re_qp_resp resp; + + resp.qpid = qp->ib_qp.qp_num; + resp.rsvd = 0; + rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to copy QP udata"); + goto qp_destroy; + } + } + INIT_LIST_HEAD(&qp->list); + mutex_lock(&rdev->qp_lock); + list_add_tail(&qp->list, &rdev->qp_list); + atomic_inc(&rdev->qp_count); + mutex_unlock(&rdev->qp_lock); + + return &qp->ib_qp; +qp_destroy: + bnxt_qplib_destroy_qp(&rdev->qplib_res, &qp->qplib_qp); +fail: + kfree(qp); + return ERR_PTR(rc); +} + +static u8 __from_ib_qp_state(enum ib_qp_state state) +{ + switch (state) { + case IB_QPS_RESET: + return CMDQ_MODIFY_QP_NEW_STATE_RESET; + case IB_QPS_INIT: + return CMDQ_MODIFY_QP_NEW_STATE_INIT; + case IB_QPS_RTR: + return CMDQ_MODIFY_QP_NEW_STATE_RTR; + case IB_QPS_RTS: + return CMDQ_MODIFY_QP_NEW_STATE_RTS; + case IB_QPS_SQD: + return CMDQ_MODIFY_QP_NEW_STATE_SQD; + case IB_QPS_SQE: + return CMDQ_MODIFY_QP_NEW_STATE_SQE; + case IB_QPS_ERR: + default: + return CMDQ_MODIFY_QP_NEW_STATE_ERR; + } +} + +static enum ib_qp_state __to_ib_qp_state(u8 state) +{ + switch (state) { + case CMDQ_MODIFY_QP_NEW_STATE_RESET: + return IB_QPS_RESET; + case CMDQ_MODIFY_QP_NEW_STATE_INIT: + return IB_QPS_INIT; + case CMDQ_MODIFY_QP_NEW_STATE_RTR: + return IB_QPS_RTR; + case CMDQ_MODIFY_QP_NEW_STATE_RTS: + return IB_QPS_RTS; + case CMDQ_MODIFY_QP_NEW_STATE_SQD: + return IB_QPS_SQD; + case CMDQ_MODIFY_QP_NEW_STATE_SQE: + return IB_QPS_SQE; + case CMDQ_MODIFY_QP_NEW_STATE_ERR: + default: + return IB_QPS_ERR; + } +} + +static u32 __from_ib_mtu(enum ib_mtu mtu) +{ + switch (mtu) { + case IB_MTU_256: + return CMDQ_MODIFY_QP_PATH_MTU_MTU_256; + case IB_MTU_512: + return CMDQ_MODIFY_QP_PATH_MTU_MTU_512; + case IB_MTU_1024: + return CMDQ_MODIFY_QP_PATH_MTU_MTU_1024; + case IB_MTU_2048: + return CMDQ_MODIFY_QP_PATH_MTU_MTU_2048; + case IB_MTU_4096: + return CMDQ_MODIFY_QP_PATH_MTU_MTU_4096; + default: + return CMDQ_MODIFY_QP_PATH_MTU_MTU_2048; + } +} + +static enum ib_mtu __to_ib_mtu(u32 mtu) +{ + switch (mtu & CREQ_QUERY_QP_RESP_SB_PATH_MTU_MASK) { + case CMDQ_MODIFY_QP_PATH_MTU_MTU_256: + return IB_MTU_256; + case CMDQ_MODIFY_QP_PATH_MTU_MTU_512: + return IB_MTU_512; + case CMDQ_MODIFY_QP_PATH_MTU_MTU_1024: + return IB_MTU_1024; + case CMDQ_MODIFY_QP_PATH_MTU_MTU_2048: + return IB_MTU_2048; + case CMDQ_MODIFY_QP_PATH_MTU_MTU_4096: + return IB_MTU_4096; + default: + return IB_MTU_2048; + } +} + +static int __from_ib_access_flags(int iflags) +{ + int qflags = 0; + + if (iflags & IB_ACCESS_LOCAL_WRITE) + qflags |= BNXT_QPLIB_ACCESS_LOCAL_WRITE; + if (iflags & IB_ACCESS_REMOTE_READ) + qflags |= BNXT_QPLIB_ACCESS_REMOTE_READ; + if (iflags & IB_ACCESS_REMOTE_WRITE) + qflags |= BNXT_QPLIB_ACCESS_REMOTE_WRITE; + if (iflags & IB_ACCESS_REMOTE_ATOMIC) + qflags |= BNXT_QPLIB_ACCESS_REMOTE_ATOMIC; + if (iflags & IB_ACCESS_MW_BIND) + qflags |= BNXT_QPLIB_ACCESS_MW_BIND; + if (iflags & IB_ZERO_BASED) + qflags |= BNXT_QPLIB_ACCESS_ZERO_BASED; + if (iflags & IB_ACCESS_ON_DEMAND) + qflags |= BNXT_QPLIB_ACCESS_ON_DEMAND; + return qflags; +}; + +static enum ib_access_flags __to_ib_access_flags(int qflags) +{ + enum ib_access_flags iflags = 0; + + if (qflags & BNXT_QPLIB_ACCESS_LOCAL_WRITE) + iflags |= IB_ACCESS_LOCAL_WRITE; + if (qflags & BNXT_QPLIB_ACCESS_REMOTE_WRITE) + iflags |= IB_ACCESS_REMOTE_WRITE; + if (qflags & BNXT_QPLIB_ACCESS_REMOTE_READ) + iflags |= IB_ACCESS_REMOTE_READ; + if (qflags & BNXT_QPLIB_ACCESS_REMOTE_ATOMIC) + iflags |= IB_ACCESS_REMOTE_ATOMIC; + if (qflags & BNXT_QPLIB_ACCESS_MW_BIND) + iflags |= IB_ACCESS_MW_BIND; + if (qflags & BNXT_QPLIB_ACCESS_ZERO_BASED) + iflags |= IB_ZERO_BASED; + if (qflags & BNXT_QPLIB_ACCESS_ON_DEMAND) + iflags |= IB_ACCESS_ON_DEMAND; + return iflags; +}; + +static int bnxt_re_modify_shadow_qp(struct bnxt_re_dev *rdev, + struct bnxt_re_qp *qp1_qp, + int qp_attr_mask) +{ + struct bnxt_re_qp *qp = rdev->qp1_sqp; + int rc = 0; + + if (qp_attr_mask & IB_QP_STATE) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_STATE; + qp->qplib_qp.state = qp1_qp->qplib_qp.state; + } + if (qp_attr_mask & IB_QP_PKEY_INDEX) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_PKEY; + qp->qplib_qp.pkey_index = qp1_qp->qplib_qp.pkey_index; + } + + if (qp_attr_mask & IB_QP_QKEY) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_QKEY; + /* Using a Random QKEY */ + qp->qplib_qp.qkey = 0x81818181; + } + if (qp_attr_mask & IB_QP_SQ_PSN) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_SQ_PSN; + qp->qplib_qp.sq.psn = qp1_qp->qplib_qp.sq.psn; + } + + rc = bnxt_qplib_modify_qp(&rdev->qplib_res, &qp->qplib_qp); + if (rc) + dev_err(rdev_to_dev(rdev), + "Failed to modify Shadow QP for QP1"); + return rc; +} + +int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, + int qp_attr_mask, struct ib_udata *udata) +{ + struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); + struct bnxt_re_dev *rdev = qp->rdev; + struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; + enum ib_qp_state curr_qp_state, new_qp_state; + int rc, entries; + int status; + union ib_gid sgid; + struct ib_gid_attr sgid_attr; + u8 nw_type; + + qp->qplib_qp.modify_flags = 0; + if (qp_attr_mask & IB_QP_STATE) { + curr_qp_state = __to_ib_qp_state(qp->qplib_qp.cur_qp_state); + new_qp_state = qp_attr->qp_state; + if (!ib_modify_qp_is_ok(curr_qp_state, new_qp_state, + ib_qp->qp_type, qp_attr_mask, + IB_LINK_LAYER_ETHERNET)) { + dev_err(rdev_to_dev(rdev), + "Invalid attribute mask: %#x specified ", + qp_attr_mask); + dev_err(rdev_to_dev(rdev), + "for qpn: %#x type: %#x", + ib_qp->qp_num, ib_qp->qp_type); + dev_err(rdev_to_dev(rdev), + "curr_qp_state=0x%x, new_qp_state=0x%x\n", + curr_qp_state, new_qp_state); + return -EINVAL; + } + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_STATE; + qp->qplib_qp.state = __from_ib_qp_state(qp_attr->qp_state); + } + if (qp_attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_EN_SQD_ASYNC_NOTIFY; + qp->qplib_qp.en_sqd_async_notify = true; + } + if (qp_attr_mask & IB_QP_ACCESS_FLAGS) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_ACCESS; + qp->qplib_qp.access = + __from_ib_access_flags(qp_attr->qp_access_flags); + /* LOCAL_WRITE access must be set to allow RC receive */ + qp->qplib_qp.access |= BNXT_QPLIB_ACCESS_LOCAL_WRITE; + } + if (qp_attr_mask & IB_QP_PKEY_INDEX) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_PKEY; + qp->qplib_qp.pkey_index = qp_attr->pkey_index; + } + if (qp_attr_mask & IB_QP_QKEY) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_QKEY; + qp->qplib_qp.qkey = qp_attr->qkey; + } + if (qp_attr_mask & IB_QP_AV) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_DGID | + CMDQ_MODIFY_QP_MODIFY_MASK_FLOW_LABEL | + CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX | + CMDQ_MODIFY_QP_MODIFY_MASK_HOP_LIMIT | + CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS | + CMDQ_MODIFY_QP_MODIFY_MASK_DEST_MAC | + CMDQ_MODIFY_QP_MODIFY_MASK_VLAN_ID; + memcpy(qp->qplib_qp.ah.dgid.data, qp_attr->ah_attr.grh.dgid.raw, + sizeof(qp->qplib_qp.ah.dgid.data)); + qp->qplib_qp.ah.flow_label = qp_attr->ah_attr.grh.flow_label; + /* If RoCE V2 is enabled, stack will have two entries for + * each GID entry. Avoiding this duplicte entry in HW. Dividing + * the GID index by 2 for RoCE V2 + */ + qp->qplib_qp.ah.sgid_index = + qp_attr->ah_attr.grh.sgid_index / 2; + qp->qplib_qp.ah.host_sgid_index = + qp_attr->ah_attr.grh.sgid_index; + qp->qplib_qp.ah.hop_limit = qp_attr->ah_attr.grh.hop_limit; + qp->qplib_qp.ah.traffic_class = + qp_attr->ah_attr.grh.traffic_class; + qp->qplib_qp.ah.sl = qp_attr->ah_attr.sl; + ether_addr_copy(qp->qplib_qp.ah.dmac, qp_attr->ah_attr.dmac); + + status = ib_get_cached_gid(&rdev->ibdev, 1, + qp_attr->ah_attr.grh.sgid_index, + &sgid, &sgid_attr); + if (!status && sgid_attr.ndev) { + memcpy(qp->qplib_qp.smac, sgid_attr.ndev->dev_addr, + ETH_ALEN); + dev_put(sgid_attr.ndev); + nw_type = ib_gid_to_network_type(sgid_attr.gid_type, + &sgid); + switch (nw_type) { + case RDMA_NETWORK_IPV4: + qp->qplib_qp.nw_type = + CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV4; + break; + case RDMA_NETWORK_IPV6: + qp->qplib_qp.nw_type = + CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV6; + break; + default: + qp->qplib_qp.nw_type = + CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV1; + break; + } + } + } + + if (qp_attr_mask & IB_QP_PATH_MTU) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU; + qp->qplib_qp.path_mtu = __from_ib_mtu(qp_attr->path_mtu); + } else if (qp_attr->qp_state == IB_QPS_RTR) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU; + qp->qplib_qp.path_mtu = + __from_ib_mtu(iboe_get_mtu(rdev->netdev->mtu)); + } + + if (qp_attr_mask & IB_QP_TIMEOUT) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_TIMEOUT; + qp->qplib_qp.timeout = qp_attr->timeout; + } + if (qp_attr_mask & IB_QP_RETRY_CNT) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_RETRY_CNT; + qp->qplib_qp.retry_cnt = qp_attr->retry_cnt; + } + if (qp_attr_mask & IB_QP_RNR_RETRY) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_RNR_RETRY; + qp->qplib_qp.rnr_retry = qp_attr->rnr_retry; + } + if (qp_attr_mask & IB_QP_MIN_RNR_TIMER) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER; + qp->qplib_qp.min_rnr_timer = qp_attr->min_rnr_timer; + } + if (qp_attr_mask & IB_QP_RQ_PSN) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_RQ_PSN; + qp->qplib_qp.rq.psn = qp_attr->rq_psn; + } + if (qp_attr_mask & IB_QP_MAX_QP_RD_ATOMIC) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_MAX_RD_ATOMIC; + qp->qplib_qp.max_rd_atomic = qp_attr->max_rd_atomic; + } + if (qp_attr_mask & IB_QP_SQ_PSN) { + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_SQ_PSN; + qp->qplib_qp.sq.psn = qp_attr->sq_psn; + } + if (qp_attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC; + qp->qplib_qp.max_dest_rd_atomic = qp_attr->max_dest_rd_atomic; + } + if (qp_attr_mask & IB_QP_CAP) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SIZE | + CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SIZE | + CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SGE | + CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SGE | + CMDQ_MODIFY_QP_MODIFY_MASK_MAX_INLINE_DATA; + if ((qp_attr->cap.max_send_wr >= dev_attr->max_qp_wqes) || + (qp_attr->cap.max_recv_wr >= dev_attr->max_qp_wqes) || + (qp_attr->cap.max_send_sge >= dev_attr->max_qp_sges) || + (qp_attr->cap.max_recv_sge >= dev_attr->max_qp_sges) || + (qp_attr->cap.max_inline_data >= + dev_attr->max_inline_data)) { + dev_err(rdev_to_dev(rdev), + "Create QP failed - max exceeded"); + return -EINVAL; + } + entries = roundup_pow_of_two(qp_attr->cap.max_send_wr); + qp->qplib_qp.sq.max_wqe = min_t(u32, entries, + dev_attr->max_qp_wqes + 1); + qp->qplib_qp.sq.max_sge = qp_attr->cap.max_send_sge; + if (qp->qplib_qp.rq.max_wqe) { + entries = roundup_pow_of_two(qp_attr->cap.max_recv_wr); + qp->qplib_qp.rq.max_wqe = + min_t(u32, entries, dev_attr->max_qp_wqes + 1); + qp->qplib_qp.rq.max_sge = qp_attr->cap.max_recv_sge; + } else { + /* SRQ was used prior, just ignore the RQ caps */ + } + } + if (qp_attr_mask & IB_QP_DEST_QPN) { + qp->qplib_qp.modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_DEST_QP_ID; + qp->qplib_qp.dest_qpn = qp_attr->dest_qp_num; + } + rc = bnxt_qplib_modify_qp(&rdev->qplib_res, &qp->qplib_qp); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to modify HW QP"); + return rc; + } + if (ib_qp->qp_type == IB_QPT_GSI && rdev->qp1_sqp) + rc = bnxt_re_modify_shadow_qp(rdev, qp, qp_attr_mask); + return rc; +} + +int bnxt_re_query_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, + int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr) +{ + struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); + struct bnxt_re_dev *rdev = qp->rdev; + struct bnxt_qplib_qp qplib_qp; + int rc; + + memset(&qplib_qp, 0, sizeof(struct bnxt_qplib_qp)); + qplib_qp.id = qp->qplib_qp.id; + qplib_qp.ah.host_sgid_index = qp->qplib_qp.ah.host_sgid_index; + + rc = bnxt_qplib_query_qp(&rdev->qplib_res, &qplib_qp); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to query HW QP"); + return rc; + } + qp_attr->qp_state = __to_ib_qp_state(qplib_qp.state); + qp_attr->en_sqd_async_notify = qplib_qp.en_sqd_async_notify ? 1 : 0; + qp_attr->qp_access_flags = __to_ib_access_flags(qplib_qp.access); + qp_attr->pkey_index = qplib_qp.pkey_index; + qp_attr->qkey = qplib_qp.qkey; + memcpy(qp_attr->ah_attr.grh.dgid.raw, qplib_qp.ah.dgid.data, + sizeof(qplib_qp.ah.dgid.data)); + qp_attr->ah_attr.grh.flow_label = qplib_qp.ah.flow_label; + qp_attr->ah_attr.grh.sgid_index = qplib_qp.ah.host_sgid_index; + qp_attr->ah_attr.grh.hop_limit = qplib_qp.ah.hop_limit; + qp_attr->ah_attr.grh.traffic_class = qplib_qp.ah.traffic_class; + qp_attr->ah_attr.sl = qplib_qp.ah.sl; + ether_addr_copy(qp_attr->ah_attr.dmac, qplib_qp.ah.dmac); + qp_attr->path_mtu = __to_ib_mtu(qplib_qp.path_mtu); + qp_attr->timeout = qplib_qp.timeout; + qp_attr->retry_cnt = qplib_qp.retry_cnt; + qp_attr->rnr_retry = qplib_qp.rnr_retry; + qp_attr->min_rnr_timer = qplib_qp.min_rnr_timer; + qp_attr->rq_psn = qplib_qp.rq.psn; + qp_attr->max_rd_atomic = qplib_qp.max_rd_atomic; + qp_attr->sq_psn = qplib_qp.sq.psn; + qp_attr->max_dest_rd_atomic = qplib_qp.max_dest_rd_atomic; + qp_init_attr->sq_sig_type = qplib_qp.sig_type ? IB_SIGNAL_ALL_WR : + IB_SIGNAL_REQ_WR; + qp_attr->dest_qp_num = qplib_qp.dest_qpn; + + qp_attr->cap.max_send_wr = qp->qplib_qp.sq.max_wqe; + qp_attr->cap.max_send_sge = qp->qplib_qp.sq.max_sge; + qp_attr->cap.max_recv_wr = qp->qplib_qp.rq.max_wqe; + qp_attr->cap.max_recv_sge = qp->qplib_qp.rq.max_sge; + qp_attr->cap.max_inline_data = qp->qplib_qp.max_inline_data; + qp_init_attr->cap = qp_attr->cap; + + return 0; +} + +/* Routine for sending QP1 packets for RoCE V1 an V2 + */ +static int bnxt_re_build_qp1_send_v2(struct bnxt_re_qp *qp, + struct ib_send_wr *wr, + struct bnxt_qplib_swqe *wqe, + int payload_size) +{ + struct ib_device *ibdev = &qp->rdev->ibdev; + struct bnxt_re_ah *ah = container_of(ud_wr(wr)->ah, struct bnxt_re_ah, + ib_ah); + struct bnxt_qplib_ah *qplib_ah = &ah->qplib_ah; + struct bnxt_qplib_sge sge; + union ib_gid sgid; + u8 nw_type; + u16 ether_type; + struct ib_gid_attr sgid_attr; + union ib_gid dgid; + bool is_eth = false; + bool is_vlan = false; + bool is_grh = false; + bool is_udp = false; + u8 ip_version = 0; + u16 vlan_id = 0xFFFF; + void *buf; + int i, rc = 0, size; + + memset(&qp->qp1_hdr, 0, sizeof(qp->qp1_hdr)); + + rc = ib_get_cached_gid(ibdev, 1, + qplib_ah->host_sgid_index, &sgid, + &sgid_attr); + if (rc) { + dev_err(rdev_to_dev(qp->rdev), + "Failed to query gid at index %d", + qplib_ah->host_sgid_index); + return rc; + } + if (sgid_attr.ndev) { + if (is_vlan_dev(sgid_attr.ndev)) + vlan_id = vlan_dev_vlan_id(sgid_attr.ndev); + dev_put(sgid_attr.ndev); + } + /* Get network header type for this GID */ + nw_type = ib_gid_to_network_type(sgid_attr.gid_type, &sgid); + switch (nw_type) { + case RDMA_NETWORK_IPV4: + nw_type = BNXT_RE_ROCEV2_IPV4_PACKET; + break; + case RDMA_NETWORK_IPV6: + nw_type = BNXT_RE_ROCEV2_IPV6_PACKET; + break; + default: + nw_type = BNXT_RE_ROCE_V1_PACKET; + break; + } + memcpy(&dgid.raw, &qplib_ah->dgid, 16); + is_udp = sgid_attr.gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP; + if (is_udp) { + if (ipv6_addr_v4mapped((struct in6_addr *)&sgid)) { + ip_version = 4; + ether_type = ETH_P_IP; + } else { + ip_version = 6; + ether_type = ETH_P_IPV6; + } + is_grh = false; + } else { + ether_type = ETH_P_IBOE; + is_grh = true; + } + + is_eth = true; + is_vlan = (vlan_id && (vlan_id < 0x1000)) ? true : false; + + ib_ud_header_init(payload_size, !is_eth, is_eth, is_vlan, is_grh, + ip_version, is_udp, 0, &qp->qp1_hdr); + + /* ETH */ + ether_addr_copy(qp->qp1_hdr.eth.dmac_h, ah->qplib_ah.dmac); + ether_addr_copy(qp->qp1_hdr.eth.smac_h, qp->qplib_qp.smac); + + /* For vlan, check the sgid for vlan existence */ + + if (!is_vlan) { + qp->qp1_hdr.eth.type = cpu_to_be16(ether_type); + } else { + qp->qp1_hdr.vlan.type = cpu_to_be16(ether_type); + qp->qp1_hdr.vlan.tag = cpu_to_be16(vlan_id); + } + + if (is_grh || (ip_version == 6)) { + memcpy(qp->qp1_hdr.grh.source_gid.raw, sgid.raw, sizeof(sgid)); + memcpy(qp->qp1_hdr.grh.destination_gid.raw, qplib_ah->dgid.data, + sizeof(sgid)); + qp->qp1_hdr.grh.hop_limit = qplib_ah->hop_limit; + } + + if (ip_version == 4) { + qp->qp1_hdr.ip4.tos = 0; + qp->qp1_hdr.ip4.id = 0; + qp->qp1_hdr.ip4.frag_off = htons(IP_DF); + qp->qp1_hdr.ip4.ttl = qplib_ah->hop_limit; + + memcpy(&qp->qp1_hdr.ip4.saddr, sgid.raw + 12, 4); + memcpy(&qp->qp1_hdr.ip4.daddr, qplib_ah->dgid.data + 12, 4); + qp->qp1_hdr.ip4.check = ib_ud_ip4_csum(&qp->qp1_hdr); + } + + if (is_udp) { + qp->qp1_hdr.udp.dport = htons(ROCE_V2_UDP_DPORT); + qp->qp1_hdr.udp.sport = htons(0x8CD1); + qp->qp1_hdr.udp.csum = 0; + } + + /* BTH */ + if (wr->opcode == IB_WR_SEND_WITH_IMM) { + qp->qp1_hdr.bth.opcode = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE; + qp->qp1_hdr.immediate_present = 1; + } else { + qp->qp1_hdr.bth.opcode = IB_OPCODE_UD_SEND_ONLY; + } + if (wr->send_flags & IB_SEND_SOLICITED) + qp->qp1_hdr.bth.solicited_event = 1; + /* pad_count */ + qp->qp1_hdr.bth.pad_count = (4 - payload_size) & 3; + + /* P_key for QP1 is for all members */ + qp->qp1_hdr.bth.pkey = cpu_to_be16(0xFFFF); + qp->qp1_hdr.bth.destination_qpn = IB_QP1; + qp->qp1_hdr.bth.ack_req = 0; + qp->send_psn++; + qp->send_psn &= BTH_PSN_MASK; + qp->qp1_hdr.bth.psn = cpu_to_be32(qp->send_psn); + /* DETH */ + /* Use the priviledged Q_Key for QP1 */ + qp->qp1_hdr.deth.qkey = cpu_to_be32(IB_QP1_QKEY); + qp->qp1_hdr.deth.source_qpn = IB_QP1; + + /* Pack the QP1 to the transmit buffer */ + buf = bnxt_qplib_get_qp1_sq_buf(&qp->qplib_qp, &sge); + if (buf) { + size = ib_ud_header_pack(&qp->qp1_hdr, buf); + for (i = wqe->num_sge; i; i--) { + wqe->sg_list[i].addr = wqe->sg_list[i - 1].addr; + wqe->sg_list[i].lkey = wqe->sg_list[i - 1].lkey; + wqe->sg_list[i].size = wqe->sg_list[i - 1].size; + } + + /* + * Max Header buf size for IPV6 RoCE V2 is 86, + * which is same as the QP1 SQ header buffer. + * Header buf size for IPV4 RoCE V2 can be 66. + * ETH(14) + VLAN(4)+ IP(20) + UDP (8) + BTH(20). + * Subtract 20 bytes from QP1 SQ header buf size + */ + if (is_udp && ip_version == 4) + sge.size -= 20; + /* + * Max Header buf size for RoCE V1 is 78. + * ETH(14) + VLAN(4) + GRH(40) + BTH(20). + * Subtract 8 bytes from QP1 SQ header buf size + */ + if (!is_udp) + sge.size -= 8; + + /* Subtract 4 bytes for non vlan packets */ + if (!is_vlan) + sge.size -= 4; + + wqe->sg_list[0].addr = sge.addr; + wqe->sg_list[0].lkey = sge.lkey; + wqe->sg_list[0].size = sge.size; + wqe->num_sge++; + + } else { + dev_err(rdev_to_dev(qp->rdev), "QP1 buffer is empty!"); + rc = -ENOMEM; + } + return rc; +} + +/* For the MAD layer, it only provides the recv SGE the size of + * ib_grh + MAD datagram. No Ethernet headers, Ethertype, BTH, DETH, + * nor RoCE iCRC. The Cu+ solution must provide buffer for the entire + * receive packet (334 bytes) with no VLAN and then copy the GRH + * and the MAD datagram out to the provided SGE. + */ +static int bnxt_re_build_qp1_shadow_qp_recv(struct bnxt_re_qp *qp, + struct ib_recv_wr *wr, + struct bnxt_qplib_swqe *wqe, + int payload_size) +{ + struct bnxt_qplib_sge ref, sge; + u32 rq_prod_index; + struct bnxt_re_sqp_entries *sqp_entry; + + rq_prod_index = bnxt_qplib_get_rq_prod_index(&qp->qplib_qp); + + if (!bnxt_qplib_get_qp1_rq_buf(&qp->qplib_qp, &sge)) + return -ENOMEM; + + /* Create 1 SGE to receive the entire + * ethernet packet + */ + /* Save the reference from ULP */ + ref.addr = wqe->sg_list[0].addr; + ref.lkey = wqe->sg_list[0].lkey; + ref.size = wqe->sg_list[0].size; + + sqp_entry = &qp->rdev->sqp_tbl[rq_prod_index]; + + /* SGE 1 */ + wqe->sg_list[0].addr = sge.addr; + wqe->sg_list[0].lkey = sge.lkey; + wqe->sg_list[0].size = BNXT_QPLIB_MAX_QP1_RQ_HDR_SIZE_V2; + sge.size -= wqe->sg_list[0].size; + + sqp_entry->sge.addr = ref.addr; + sqp_entry->sge.lkey = ref.lkey; + sqp_entry->sge.size = ref.size; + /* Store the wrid for reporting completion */ + sqp_entry->wrid = wqe->wr_id; + /* change the wqe->wrid to table index */ + wqe->wr_id = rq_prod_index; + return 0; +} + +static int is_ud_qp(struct bnxt_re_qp *qp) +{ + return qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_UD; +} + +static int bnxt_re_build_send_wqe(struct bnxt_re_qp *qp, + struct ib_send_wr *wr, + struct bnxt_qplib_swqe *wqe) +{ + struct bnxt_re_ah *ah = NULL; + + if (is_ud_qp(qp)) { + ah = container_of(ud_wr(wr)->ah, struct bnxt_re_ah, ib_ah); + wqe->send.q_key = ud_wr(wr)->remote_qkey; + wqe->send.dst_qp = ud_wr(wr)->remote_qpn; + wqe->send.avid = ah->qplib_ah.id; + } + switch (wr->opcode) { + case IB_WR_SEND: + wqe->type = BNXT_QPLIB_SWQE_TYPE_SEND; + break; + case IB_WR_SEND_WITH_IMM: + wqe->type = BNXT_QPLIB_SWQE_TYPE_SEND_WITH_IMM; + wqe->send.imm_data = wr->ex.imm_data; + break; + case IB_WR_SEND_WITH_INV: + wqe->type = BNXT_QPLIB_SWQE_TYPE_SEND_WITH_INV; + wqe->send.inv_key = wr->ex.invalidate_rkey; + break; + default: + return -EINVAL; + } + if (wr->send_flags & IB_SEND_SIGNALED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SIGNAL_COMP; + if (wr->send_flags & IB_SEND_FENCE) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_UC_FENCE; + if (wr->send_flags & IB_SEND_SOLICITED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SOLICIT_EVENT; + if (wr->send_flags & IB_SEND_INLINE) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_INLINE; + + return 0; +} + +static int bnxt_re_build_rdma_wqe(struct ib_send_wr *wr, + struct bnxt_qplib_swqe *wqe) +{ + switch (wr->opcode) { + case IB_WR_RDMA_WRITE: + wqe->type = BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE; + break; + case IB_WR_RDMA_WRITE_WITH_IMM: + wqe->type = BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE_WITH_IMM; + wqe->rdma.imm_data = wr->ex.imm_data; + break; + case IB_WR_RDMA_READ: + wqe->type = BNXT_QPLIB_SWQE_TYPE_RDMA_READ; + wqe->rdma.inv_key = wr->ex.invalidate_rkey; + break; + default: + return -EINVAL; + } + wqe->rdma.remote_va = rdma_wr(wr)->remote_addr; + wqe->rdma.r_key = rdma_wr(wr)->rkey; + if (wr->send_flags & IB_SEND_SIGNALED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SIGNAL_COMP; + if (wr->send_flags & IB_SEND_FENCE) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_UC_FENCE; + if (wr->send_flags & IB_SEND_SOLICITED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SOLICIT_EVENT; + if (wr->send_flags & IB_SEND_INLINE) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_INLINE; + + return 0; +} + +static int bnxt_re_build_atomic_wqe(struct ib_send_wr *wr, + struct bnxt_qplib_swqe *wqe) +{ + switch (wr->opcode) { + case IB_WR_ATOMIC_CMP_AND_SWP: + wqe->type = BNXT_QPLIB_SWQE_TYPE_ATOMIC_CMP_AND_SWP; + wqe->atomic.swap_data = atomic_wr(wr)->swap; + break; + case IB_WR_ATOMIC_FETCH_AND_ADD: + wqe->type = BNXT_QPLIB_SWQE_TYPE_ATOMIC_FETCH_AND_ADD; + wqe->atomic.cmp_data = atomic_wr(wr)->compare_add; + break; + default: + return -EINVAL; + } + wqe->atomic.remote_va = atomic_wr(wr)->remote_addr; + wqe->atomic.r_key = atomic_wr(wr)->rkey; + if (wr->send_flags & IB_SEND_SIGNALED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SIGNAL_COMP; + if (wr->send_flags & IB_SEND_FENCE) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_UC_FENCE; + if (wr->send_flags & IB_SEND_SOLICITED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SOLICIT_EVENT; + return 0; +} + +static int bnxt_re_build_inv_wqe(struct ib_send_wr *wr, + struct bnxt_qplib_swqe *wqe) +{ + wqe->type = BNXT_QPLIB_SWQE_TYPE_LOCAL_INV; + wqe->local_inv.inv_l_key = wr->ex.invalidate_rkey; + + if (wr->send_flags & IB_SEND_SIGNALED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SIGNAL_COMP; + if (wr->send_flags & IB_SEND_FENCE) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_UC_FENCE; + if (wr->send_flags & IB_SEND_SOLICITED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SOLICIT_EVENT; + + return 0; +} + +static int bnxt_re_build_reg_wqe(struct ib_reg_wr *wr, + struct bnxt_qplib_swqe *wqe) +{ + struct bnxt_re_mr *mr = container_of(wr->mr, struct bnxt_re_mr, ib_mr); + struct bnxt_qplib_frpl *qplib_frpl = &mr->qplib_frpl; + int access = wr->access; + + wqe->frmr.pbl_ptr = (__le64 *)qplib_frpl->hwq.pbl_ptr[0]; + wqe->frmr.pbl_dma_ptr = qplib_frpl->hwq.pbl_dma_ptr[0]; + wqe->frmr.page_list = mr->pages; + wqe->frmr.page_list_len = mr->npages; + wqe->frmr.levels = qplib_frpl->hwq.level + 1; + wqe->type = BNXT_QPLIB_SWQE_TYPE_REG_MR; + + if (wr->wr.send_flags & IB_SEND_FENCE) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_UC_FENCE; + if (wr->wr.send_flags & IB_SEND_SIGNALED) + wqe->flags |= BNXT_QPLIB_SWQE_FLAGS_SIGNAL_COMP; + + if (access & IB_ACCESS_LOCAL_WRITE) + wqe->frmr.access_cntl |= SQ_FR_PMR_ACCESS_CNTL_LOCAL_WRITE; + if (access & IB_ACCESS_REMOTE_READ) + wqe->frmr.access_cntl |= SQ_FR_PMR_ACCESS_CNTL_REMOTE_READ; + if (access & IB_ACCESS_REMOTE_WRITE) + wqe->frmr.access_cntl |= SQ_FR_PMR_ACCESS_CNTL_REMOTE_WRITE; + if (access & IB_ACCESS_REMOTE_ATOMIC) + wqe->frmr.access_cntl |= SQ_FR_PMR_ACCESS_CNTL_REMOTE_ATOMIC; + if (access & IB_ACCESS_MW_BIND) + wqe->frmr.access_cntl |= SQ_FR_PMR_ACCESS_CNTL_WINDOW_BIND; + + wqe->frmr.l_key = wr->key; + wqe->frmr.length = wr->mr->length; + wqe->frmr.pbl_pg_sz_log = (wr->mr->page_size >> PAGE_SHIFT_4K) - 1; + wqe->frmr.va = wr->mr->iova; + return 0; +} + +static int bnxt_re_copy_inline_data(struct bnxt_re_dev *rdev, + struct ib_send_wr *wr, + struct bnxt_qplib_swqe *wqe) +{ + /* Copy the inline data to the data field */ + u8 *in_data; + u32 i, sge_len; + void *sge_addr; + + in_data = wqe->inline_data; + for (i = 0; i < wr->num_sge; i++) { + sge_addr = (void *)(unsigned long) + wr->sg_list[i].addr; + sge_len = wr->sg_list[i].length; + + if ((sge_len + wqe->inline_len) > + BNXT_QPLIB_SWQE_MAX_INLINE_LENGTH) { + dev_err(rdev_to_dev(rdev), + "Inline data size requested > supported value"); + return -EINVAL; + } + sge_len = wr->sg_list[i].length; + + memcpy(in_data, sge_addr, sge_len); + in_data += wr->sg_list[i].length; + wqe->inline_len += wr->sg_list[i].length; + } + return wqe->inline_len; +} + +static int bnxt_re_copy_wr_payload(struct bnxt_re_dev *rdev, + struct ib_send_wr *wr, + struct bnxt_qplib_swqe *wqe) +{ + int payload_sz = 0; + + if (wr->send_flags & IB_SEND_INLINE) + payload_sz = bnxt_re_copy_inline_data(rdev, wr, wqe); + else + payload_sz = bnxt_re_build_sgl(wr->sg_list, wqe->sg_list, + wqe->num_sge); + + return payload_sz; +} + +static int bnxt_re_post_send_shadow_qp(struct bnxt_re_dev *rdev, + struct bnxt_re_qp *qp, + struct ib_send_wr *wr) +{ + struct bnxt_qplib_swqe wqe; + int rc = 0, payload_sz = 0; + unsigned long flags; + + spin_lock_irqsave(&qp->sq_lock, flags); + memset(&wqe, 0, sizeof(wqe)); + while (wr) { + /* House keeping */ + memset(&wqe, 0, sizeof(wqe)); + + /* Common */ + wqe.num_sge = wr->num_sge; + if (wr->num_sge > qp->qplib_qp.sq.max_sge) { + dev_err(rdev_to_dev(rdev), + "Limit exceeded for Send SGEs"); + rc = -EINVAL; + goto bad; + } + + payload_sz = bnxt_re_copy_wr_payload(qp->rdev, wr, &wqe); + if (payload_sz < 0) { + rc = -EINVAL; + goto bad; + } + wqe.wr_id = wr->wr_id; + + wqe.type = BNXT_QPLIB_SWQE_TYPE_SEND; + + rc = bnxt_re_build_send_wqe(qp, wr, &wqe); + if (!rc) + rc = bnxt_qplib_post_send(&qp->qplib_qp, &wqe); +bad: + if (rc) { + dev_err(rdev_to_dev(rdev), + "Post send failed opcode = %#x rc = %d", + wr->opcode, rc); + break; + } + wr = wr->next; + } + bnxt_qplib_post_send_db(&qp->qplib_qp); + spin_unlock_irqrestore(&qp->sq_lock, flags); + return rc; +} + +int bnxt_re_post_send(struct ib_qp *ib_qp, struct ib_send_wr *wr, + struct ib_send_wr **bad_wr) +{ + struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); + struct bnxt_qplib_swqe wqe; + int rc = 0, payload_sz = 0; + unsigned long flags; + + spin_lock_irqsave(&qp->sq_lock, flags); + while (wr) { + /* House keeping */ + memset(&wqe, 0, sizeof(wqe)); + + /* Common */ + wqe.num_sge = wr->num_sge; + if (wr->num_sge > qp->qplib_qp.sq.max_sge) { + dev_err(rdev_to_dev(qp->rdev), + "Limit exceeded for Send SGEs"); + rc = -EINVAL; + goto bad; + } + + payload_sz = bnxt_re_copy_wr_payload(qp->rdev, wr, &wqe); + if (payload_sz < 0) { + rc = -EINVAL; + goto bad; + } + wqe.wr_id = wr->wr_id; + + switch (wr->opcode) { + case IB_WR_SEND: + case IB_WR_SEND_WITH_IMM: + if (ib_qp->qp_type == IB_QPT_GSI) { + rc = bnxt_re_build_qp1_send_v2(qp, wr, &wqe, + payload_sz); + if (rc) + goto bad; + wqe.rawqp1.lflags |= + SQ_SEND_RAWETH_QP1_LFLAGS_ROCE_CRC; + } + switch (wr->send_flags) { + case IB_SEND_IP_CSUM: + wqe.rawqp1.lflags |= + SQ_SEND_RAWETH_QP1_LFLAGS_IP_CHKSUM; + break; + default: + break; + } + /* Fall thru to build the wqe */ + case IB_WR_SEND_WITH_INV: + rc = bnxt_re_build_send_wqe(qp, wr, &wqe); + break; + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + case IB_WR_RDMA_READ: + rc = bnxt_re_build_rdma_wqe(wr, &wqe); + break; + case IB_WR_ATOMIC_CMP_AND_SWP: + case IB_WR_ATOMIC_FETCH_AND_ADD: + rc = bnxt_re_build_atomic_wqe(wr, &wqe); + break; + case IB_WR_RDMA_READ_WITH_INV: + dev_err(rdev_to_dev(qp->rdev), + "RDMA Read with Invalidate is not supported"); + rc = -EINVAL; + goto bad; + case IB_WR_LOCAL_INV: + rc = bnxt_re_build_inv_wqe(wr, &wqe); + break; + case IB_WR_REG_MR: + rc = bnxt_re_build_reg_wqe(reg_wr(wr), &wqe); + break; + default: + /* Unsupported WRs */ + dev_err(rdev_to_dev(qp->rdev), + "WR (%#x) is not supported", wr->opcode); + rc = -EINVAL; + goto bad; + } + if (!rc) + rc = bnxt_qplib_post_send(&qp->qplib_qp, &wqe); +bad: + if (rc) { + dev_err(rdev_to_dev(qp->rdev), + "post_send failed op:%#x qps = %#x rc = %d\n", + wr->opcode, qp->qplib_qp.state, rc); + *bad_wr = wr; + break; + } + wr = wr->next; + } + bnxt_qplib_post_send_db(&qp->qplib_qp); + spin_unlock_irqrestore(&qp->sq_lock, flags); + + return rc; +} + +static int bnxt_re_post_recv_shadow_qp(struct bnxt_re_dev *rdev, + struct bnxt_re_qp *qp, + struct ib_recv_wr *wr) +{ + struct bnxt_qplib_swqe wqe; + int rc = 0, payload_sz = 0; + + memset(&wqe, 0, sizeof(wqe)); + while (wr) { + /* House keeping */ + memset(&wqe, 0, sizeof(wqe)); + + /* Common */ + wqe.num_sge = wr->num_sge; + if (wr->num_sge > qp->qplib_qp.rq.max_sge) { + dev_err(rdev_to_dev(rdev), + "Limit exceeded for Receive SGEs"); + rc = -EINVAL; + break; + } + payload_sz = bnxt_re_build_sgl(wr->sg_list, wqe.sg_list, + wr->num_sge); + wqe.wr_id = wr->wr_id; + wqe.type = BNXT_QPLIB_SWQE_TYPE_RECV; + + rc = bnxt_qplib_post_recv(&qp->qplib_qp, &wqe); + if (rc) + break; + + wr = wr->next; + } + if (!rc) + bnxt_qplib_post_recv_db(&qp->qplib_qp); + return rc; +} + +int bnxt_re_post_recv(struct ib_qp *ib_qp, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); + struct bnxt_qplib_swqe wqe; + int rc = 0, payload_sz = 0; + + while (wr) { + /* House keeping */ + memset(&wqe, 0, sizeof(wqe)); + + /* Common */ + wqe.num_sge = wr->num_sge; + if (wr->num_sge > qp->qplib_qp.rq.max_sge) { + dev_err(rdev_to_dev(qp->rdev), + "Limit exceeded for Receive SGEs"); + rc = -EINVAL; + *bad_wr = wr; + break; + } + + payload_sz = bnxt_re_build_sgl(wr->sg_list, wqe.sg_list, + wr->num_sge); + wqe.wr_id = wr->wr_id; + wqe.type = BNXT_QPLIB_SWQE_TYPE_RECV; + + if (ib_qp->qp_type == IB_QPT_GSI) + rc = bnxt_re_build_qp1_shadow_qp_recv(qp, wr, &wqe, + payload_sz); + if (!rc) + rc = bnxt_qplib_post_recv(&qp->qplib_qp, &wqe); + if (rc) { + *bad_wr = wr; + break; + } + wr = wr->next; + } + bnxt_qplib_post_recv_db(&qp->qplib_qp); + return rc; +} + +/* Completion Queues */ +int bnxt_re_destroy_cq(struct ib_cq *ib_cq) +{ + struct bnxt_re_cq *cq = container_of(ib_cq, struct bnxt_re_cq, ib_cq); + struct bnxt_re_dev *rdev = cq->rdev; + int rc; + + rc = bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to destroy HW CQ"); + return rc; + } + if (cq->umem && !IS_ERR(cq->umem)) + ib_umem_release(cq->umem); + + if (cq) { + kfree(cq->cql); + kfree(cq); + } + atomic_dec(&rdev->cq_count); + rdev->nq.budget--; + return 0; +} + +struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev, + const struct ib_cq_init_attr *attr, + struct ib_ucontext *context, + struct ib_udata *udata) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; + struct bnxt_re_cq *cq = NULL; + int rc, entries; + int cqe = attr->cqe; + + /* Validate CQ fields */ + if (cqe < 1 || cqe > dev_attr->max_cq_wqes) { + dev_err(rdev_to_dev(rdev), "Failed to create CQ -max exceeded"); + return ERR_PTR(-EINVAL); + } + cq = kzalloc(sizeof(*cq), GFP_KERNEL); + if (!cq) + return ERR_PTR(-ENOMEM); + + cq->rdev = rdev; + cq->qplib_cq.cq_handle = (u64)(unsigned long)(&cq->qplib_cq); + + entries = roundup_pow_of_two(cqe + 1); + if (entries > dev_attr->max_cq_wqes + 1) + entries = dev_attr->max_cq_wqes + 1; + + if (context) { + struct bnxt_re_cq_req req; + struct bnxt_re_ucontext *uctx = container_of + (context, + struct bnxt_re_ucontext, + ib_uctx); + if (ib_copy_from_udata(&req, udata, sizeof(req))) { + rc = -EFAULT; + goto fail; + } + + cq->umem = ib_umem_get(context, req.cq_va, + entries * sizeof(struct cq_base), + IB_ACCESS_LOCAL_WRITE, 1); + if (IS_ERR(cq->umem)) { + rc = PTR_ERR(cq->umem); + goto fail; + } + cq->qplib_cq.sghead = cq->umem->sg_head.sgl; + cq->qplib_cq.nmap = cq->umem->nmap; + cq->qplib_cq.dpi = uctx->dpi; + } else { + cq->max_cql = min_t(u32, entries, MAX_CQL_PER_POLL); + cq->cql = kcalloc(cq->max_cql, sizeof(struct bnxt_qplib_cqe), + GFP_KERNEL); + if (!cq->cql) { + rc = -ENOMEM; + goto fail; + } + + cq->qplib_cq.dpi = &rdev->dpi_privileged; + cq->qplib_cq.sghead = NULL; + cq->qplib_cq.nmap = 0; + } + cq->qplib_cq.max_wqe = entries; + cq->qplib_cq.cnq_hw_ring_id = rdev->nq.ring_id; + + rc = bnxt_qplib_create_cq(&rdev->qplib_res, &cq->qplib_cq); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to create HW CQ"); + goto fail; + } + + cq->ib_cq.cqe = entries; + cq->cq_period = cq->qplib_cq.period; + rdev->nq.budget++; + + atomic_inc(&rdev->cq_count); + + if (context) { + struct bnxt_re_cq_resp resp; + + resp.cqid = cq->qplib_cq.id; + resp.tail = cq->qplib_cq.hwq.cons; + resp.phase = cq->qplib_cq.period; + resp.rsvd = 0; + rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to copy CQ udata"); + bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); + goto c2fail; + } + } + + return &cq->ib_cq; + +c2fail: + if (context) + ib_umem_release(cq->umem); +fail: + kfree(cq->cql); + kfree(cq); + return ERR_PTR(rc); +} + +static u8 __req_to_ib_wc_status(u8 qstatus) +{ + switch (qstatus) { + case CQ_REQ_STATUS_OK: + return IB_WC_SUCCESS; + case CQ_REQ_STATUS_BAD_RESPONSE_ERR: + return IB_WC_BAD_RESP_ERR; + case CQ_REQ_STATUS_LOCAL_LENGTH_ERR: + return IB_WC_LOC_LEN_ERR; + case CQ_REQ_STATUS_LOCAL_QP_OPERATION_ERR: + return IB_WC_LOC_QP_OP_ERR; + case CQ_REQ_STATUS_LOCAL_PROTECTION_ERR: + return IB_WC_LOC_PROT_ERR; + case CQ_REQ_STATUS_MEMORY_MGT_OPERATION_ERR: + return IB_WC_GENERAL_ERR; + case CQ_REQ_STATUS_REMOTE_INVALID_REQUEST_ERR: + return IB_WC_REM_INV_REQ_ERR; + case CQ_REQ_STATUS_REMOTE_ACCESS_ERR: + return IB_WC_REM_ACCESS_ERR; + case CQ_REQ_STATUS_REMOTE_OPERATION_ERR: + return IB_WC_REM_OP_ERR; + case CQ_REQ_STATUS_RNR_NAK_RETRY_CNT_ERR: + return IB_WC_RNR_RETRY_EXC_ERR; + case CQ_REQ_STATUS_TRANSPORT_RETRY_CNT_ERR: + return IB_WC_RETRY_EXC_ERR; + case CQ_REQ_STATUS_WORK_REQUEST_FLUSHED_ERR: + return IB_WC_WR_FLUSH_ERR; + default: + return IB_WC_GENERAL_ERR; + } + return 0; +} + +static u8 __rawqp1_to_ib_wc_status(u8 qstatus) +{ + switch (qstatus) { + case CQ_RES_RAWETH_QP1_STATUS_OK: + return IB_WC_SUCCESS; + case CQ_RES_RAWETH_QP1_STATUS_LOCAL_ACCESS_ERROR: + return IB_WC_LOC_ACCESS_ERR; + case CQ_RES_RAWETH_QP1_STATUS_HW_LOCAL_LENGTH_ERR: + return IB_WC_LOC_LEN_ERR; + case CQ_RES_RAWETH_QP1_STATUS_LOCAL_PROTECTION_ERR: + return IB_WC_LOC_PROT_ERR; + case CQ_RES_RAWETH_QP1_STATUS_LOCAL_QP_OPERATION_ERR: + return IB_WC_LOC_QP_OP_ERR; + case CQ_RES_RAWETH_QP1_STATUS_MEMORY_MGT_OPERATION_ERR: + return IB_WC_GENERAL_ERR; + case CQ_RES_RAWETH_QP1_STATUS_WORK_REQUEST_FLUSHED_ERR: + return IB_WC_WR_FLUSH_ERR; + case CQ_RES_RAWETH_QP1_STATUS_HW_FLUSH_ERR: + return IB_WC_WR_FLUSH_ERR; + default: + return IB_WC_GENERAL_ERR; + } +} + +static u8 __rc_to_ib_wc_status(u8 qstatus) +{ + switch (qstatus) { + case CQ_RES_RC_STATUS_OK: + return IB_WC_SUCCESS; + case CQ_RES_RC_STATUS_LOCAL_ACCESS_ERROR: + return IB_WC_LOC_ACCESS_ERR; + case CQ_RES_RC_STATUS_LOCAL_LENGTH_ERR: + return IB_WC_LOC_LEN_ERR; + case CQ_RES_RC_STATUS_LOCAL_PROTECTION_ERR: + return IB_WC_LOC_PROT_ERR; + case CQ_RES_RC_STATUS_LOCAL_QP_OPERATION_ERR: + return IB_WC_LOC_QP_OP_ERR; + case CQ_RES_RC_STATUS_MEMORY_MGT_OPERATION_ERR: + return IB_WC_GENERAL_ERR; + case CQ_RES_RC_STATUS_REMOTE_INVALID_REQUEST_ERR: + return IB_WC_REM_INV_REQ_ERR; + case CQ_RES_RC_STATUS_WORK_REQUEST_FLUSHED_ERR: + return IB_WC_WR_FLUSH_ERR; + case CQ_RES_RC_STATUS_HW_FLUSH_ERR: + return IB_WC_WR_FLUSH_ERR; + default: + return IB_WC_GENERAL_ERR; + } +} + +static void bnxt_re_process_req_wc(struct ib_wc *wc, struct bnxt_qplib_cqe *cqe) +{ + switch (cqe->type) { + case BNXT_QPLIB_SWQE_TYPE_SEND: + wc->opcode = IB_WC_SEND; + break; + case BNXT_QPLIB_SWQE_TYPE_SEND_WITH_IMM: + wc->opcode = IB_WC_SEND; + wc->wc_flags |= IB_WC_WITH_IMM; + break; + case BNXT_QPLIB_SWQE_TYPE_SEND_WITH_INV: + wc->opcode = IB_WC_SEND; + wc->wc_flags |= IB_WC_WITH_INVALIDATE; + break; + case BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE: + wc->opcode = IB_WC_RDMA_WRITE; + break; + case BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE_WITH_IMM: + wc->opcode = IB_WC_RDMA_WRITE; + wc->wc_flags |= IB_WC_WITH_IMM; + break; + case BNXT_QPLIB_SWQE_TYPE_RDMA_READ: + wc->opcode = IB_WC_RDMA_READ; + break; + case BNXT_QPLIB_SWQE_TYPE_ATOMIC_CMP_AND_SWP: + wc->opcode = IB_WC_COMP_SWAP; + break; + case BNXT_QPLIB_SWQE_TYPE_ATOMIC_FETCH_AND_ADD: + wc->opcode = IB_WC_FETCH_ADD; + break; + case BNXT_QPLIB_SWQE_TYPE_LOCAL_INV: + wc->opcode = IB_WC_LOCAL_INV; + break; + case BNXT_QPLIB_SWQE_TYPE_REG_MR: + wc->opcode = IB_WC_REG_MR; + break; + default: + wc->opcode = IB_WC_SEND; + break; + } + + wc->status = __req_to_ib_wc_status(cqe->status); +} + +static int bnxt_re_check_packet_type(u16 raweth_qp1_flags, + u16 raweth_qp1_flags2) +{ + bool is_udp = false, is_ipv6 = false, is_ipv4 = false; + + /* raweth_qp1_flags Bit 9-6 indicates itype */ + if ((raweth_qp1_flags & CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_ROCE) + != CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_ROCE) + return -1; + + if (raweth_qp1_flags2 & + CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_IP_CS_CALC && + raweth_qp1_flags2 & + CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_L4_CS_CALC) { + is_udp = true; + /* raweth_qp1_flags2 Bit 8 indicates ip_type. 0-v4 1 - v6 */ + (raweth_qp1_flags2 & + CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_IP_TYPE) ? + (is_ipv6 = true) : (is_ipv4 = true); + return ((is_ipv6) ? + BNXT_RE_ROCEV2_IPV6_PACKET : + BNXT_RE_ROCEV2_IPV4_PACKET); + } else { + return BNXT_RE_ROCE_V1_PACKET; + } +} + +static int bnxt_re_to_ib_nw_type(int nw_type) +{ + u8 nw_hdr_type = 0xFF; + + switch (nw_type) { + case BNXT_RE_ROCE_V1_PACKET: + nw_hdr_type = RDMA_NETWORK_ROCE_V1; + break; + case BNXT_RE_ROCEV2_IPV4_PACKET: + nw_hdr_type = RDMA_NETWORK_IPV4; + break; + case BNXT_RE_ROCEV2_IPV6_PACKET: + nw_hdr_type = RDMA_NETWORK_IPV6; + break; + } + return nw_hdr_type; +} + +static bool bnxt_re_is_loopback_packet(struct bnxt_re_dev *rdev, + void *rq_hdr_buf) +{ + u8 *tmp_buf = NULL; + struct ethhdr *eth_hdr; + u16 eth_type; + bool rc = false; + + tmp_buf = (u8 *)rq_hdr_buf; + /* + * If dest mac is not same as I/F mac, this could be a + * loopback address or multicast address, check whether + * it is a loopback packet + */ + if (!ether_addr_equal(tmp_buf, rdev->netdev->dev_addr)) { + tmp_buf += 4; + /* Check the ether type */ + eth_hdr = (struct ethhdr *)tmp_buf; + eth_type = ntohs(eth_hdr->h_proto); + switch (eth_type) { + case ETH_P_IBOE: + rc = true; + break; + case ETH_P_IP: + case ETH_P_IPV6: { + u32 len; + struct udphdr *udp_hdr; + + len = (eth_type == ETH_P_IP ? sizeof(struct iphdr) : + sizeof(struct ipv6hdr)); + tmp_buf += sizeof(struct ethhdr) + len; + udp_hdr = (struct udphdr *)tmp_buf; + if (ntohs(udp_hdr->dest) == + ROCE_V2_UDP_DPORT) + rc = true; + break; + } + default: + break; + } + } + + return rc; +} + +static int bnxt_re_process_raw_qp_pkt_rx(struct bnxt_re_qp *qp1_qp, + struct bnxt_qplib_cqe *cqe) +{ + struct bnxt_re_dev *rdev = qp1_qp->rdev; + struct bnxt_re_sqp_entries *sqp_entry = NULL; + struct bnxt_re_qp *qp = rdev->qp1_sqp; + struct ib_send_wr *swr; + struct ib_ud_wr udwr; + struct ib_recv_wr rwr; + int pkt_type = 0; + u32 tbl_idx; + void *rq_hdr_buf; + dma_addr_t rq_hdr_buf_map; + dma_addr_t shrq_hdr_buf_map; + u32 offset = 0; + u32 skip_bytes = 0; + struct ib_sge s_sge[2]; + struct ib_sge r_sge[2]; + int rc; + + memset(&udwr, 0, sizeof(udwr)); + memset(&rwr, 0, sizeof(rwr)); + memset(&s_sge, 0, sizeof(s_sge)); + memset(&r_sge, 0, sizeof(r_sge)); + + swr = &udwr.wr; + tbl_idx = cqe->wr_id; + + rq_hdr_buf = qp1_qp->qplib_qp.rq_hdr_buf + + (tbl_idx * qp1_qp->qplib_qp.rq_hdr_buf_size); + rq_hdr_buf_map = bnxt_qplib_get_qp_buf_from_index(&qp1_qp->qplib_qp, + tbl_idx); + + /* Shadow QP header buffer */ + shrq_hdr_buf_map = bnxt_qplib_get_qp_buf_from_index(&qp->qplib_qp, + tbl_idx); + sqp_entry = &rdev->sqp_tbl[tbl_idx]; + + /* Store this cqe */ + memcpy(&sqp_entry->cqe, cqe, sizeof(struct bnxt_qplib_cqe)); + sqp_entry->qp1_qp = qp1_qp; + + /* Find packet type from the cqe */ + + pkt_type = bnxt_re_check_packet_type(cqe->raweth_qp1_flags, + cqe->raweth_qp1_flags2); + if (pkt_type < 0) { + dev_err(rdev_to_dev(rdev), "Invalid packet\n"); + return -EINVAL; + } + + /* Adjust the offset for the user buffer and post in the rq */ + + if (pkt_type == BNXT_RE_ROCEV2_IPV4_PACKET) + offset = 20; + + /* + * QP1 loopback packet has 4 bytes of internal header before + * ether header. Skip these four bytes. + */ + if (bnxt_re_is_loopback_packet(rdev, rq_hdr_buf)) + skip_bytes = 4; + + /* First send SGE . Skip the ether header*/ + s_sge[0].addr = rq_hdr_buf_map + BNXT_QPLIB_MAX_QP1_RQ_ETH_HDR_SIZE + + skip_bytes; + s_sge[0].lkey = 0xFFFFFFFF; + s_sge[0].length = offset ? BNXT_QPLIB_MAX_GRH_HDR_SIZE_IPV4 : + BNXT_QPLIB_MAX_GRH_HDR_SIZE_IPV6; + + /* Second Send SGE */ + s_sge[1].addr = s_sge[0].addr + s_sge[0].length + + BNXT_QPLIB_MAX_QP1_RQ_BDETH_HDR_SIZE; + if (pkt_type != BNXT_RE_ROCE_V1_PACKET) + s_sge[1].addr += 8; + s_sge[1].lkey = 0xFFFFFFFF; + s_sge[1].length = 256; + + /* First recv SGE */ + + r_sge[0].addr = shrq_hdr_buf_map; + r_sge[0].lkey = 0xFFFFFFFF; + r_sge[0].length = 40; + + r_sge[1].addr = sqp_entry->sge.addr + offset; + r_sge[1].lkey = sqp_entry->sge.lkey; + r_sge[1].length = BNXT_QPLIB_MAX_GRH_HDR_SIZE_IPV6 + 256 - offset; + + /* Create receive work request */ + rwr.num_sge = 2; + rwr.sg_list = r_sge; + rwr.wr_id = tbl_idx; + rwr.next = NULL; + + rc = bnxt_re_post_recv_shadow_qp(rdev, qp, &rwr); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to post Rx buffers to shadow QP"); + return -ENOMEM; + } + + swr->num_sge = 2; + swr->sg_list = s_sge; + swr->wr_id = tbl_idx; + swr->opcode = IB_WR_SEND; + swr->next = NULL; + + udwr.ah = &rdev->sqp_ah->ib_ah; + udwr.remote_qpn = rdev->qp1_sqp->qplib_qp.id; + udwr.remote_qkey = rdev->qp1_sqp->qplib_qp.qkey; + + /* post data received in the send queue */ + rc = bnxt_re_post_send_shadow_qp(rdev, qp, swr); + + return 0; +} + +static void bnxt_re_process_res_rawqp1_wc(struct ib_wc *wc, + struct bnxt_qplib_cqe *cqe) +{ + wc->opcode = IB_WC_RECV; + wc->status = __rawqp1_to_ib_wc_status(cqe->status); + wc->wc_flags |= IB_WC_GRH; +} + +static void bnxt_re_process_res_rc_wc(struct ib_wc *wc, + struct bnxt_qplib_cqe *cqe) +{ + wc->opcode = IB_WC_RECV; + wc->status = __rc_to_ib_wc_status(cqe->status); + + if (cqe->flags & CQ_RES_RC_FLAGS_IMM) + wc->wc_flags |= IB_WC_WITH_IMM; + if (cqe->flags & CQ_RES_RC_FLAGS_INV) + wc->wc_flags |= IB_WC_WITH_INVALIDATE; + if ((cqe->flags & (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM)) == + (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM)) + wc->opcode = IB_WC_RECV_RDMA_WITH_IMM; +} + +static void bnxt_re_process_res_shadow_qp_wc(struct bnxt_re_qp *qp, + struct ib_wc *wc, + struct bnxt_qplib_cqe *cqe) +{ + u32 tbl_idx; + struct bnxt_re_dev *rdev = qp->rdev; + struct bnxt_re_qp *qp1_qp = NULL; + struct bnxt_qplib_cqe *orig_cqe = NULL; + struct bnxt_re_sqp_entries *sqp_entry = NULL; + int nw_type; + + tbl_idx = cqe->wr_id; + + sqp_entry = &rdev->sqp_tbl[tbl_idx]; + qp1_qp = sqp_entry->qp1_qp; + orig_cqe = &sqp_entry->cqe; + + wc->wr_id = sqp_entry->wrid; + wc->byte_len = orig_cqe->length; + wc->qp = &qp1_qp->ib_qp; + + wc->ex.imm_data = orig_cqe->immdata; + wc->src_qp = orig_cqe->src_qp; + memcpy(wc->smac, orig_cqe->smac, ETH_ALEN); + wc->port_num = 1; + wc->vendor_err = orig_cqe->status; + + wc->opcode = IB_WC_RECV; + wc->status = __rawqp1_to_ib_wc_status(orig_cqe->status); + wc->wc_flags |= IB_WC_GRH; + + nw_type = bnxt_re_check_packet_type(orig_cqe->raweth_qp1_flags, + orig_cqe->raweth_qp1_flags2); + if (nw_type >= 0) { + wc->network_hdr_type = bnxt_re_to_ib_nw_type(nw_type); + wc->wc_flags |= IB_WC_WITH_NETWORK_HDR_TYPE; + } +} + +static void bnxt_re_process_res_ud_wc(struct ib_wc *wc, + struct bnxt_qplib_cqe *cqe) +{ + wc->opcode = IB_WC_RECV; + wc->status = __rc_to_ib_wc_status(cqe->status); + + if (cqe->flags & CQ_RES_RC_FLAGS_IMM) + wc->wc_flags |= IB_WC_WITH_IMM; + if (cqe->flags & CQ_RES_RC_FLAGS_INV) + wc->wc_flags |= IB_WC_WITH_INVALIDATE; + if ((cqe->flags & (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM)) == + (CQ_RES_RC_FLAGS_RDMA | CQ_RES_RC_FLAGS_IMM)) + wc->opcode = IB_WC_RECV_RDMA_WITH_IMM; +} + +int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc) +{ + struct bnxt_re_cq *cq = container_of(ib_cq, struct bnxt_re_cq, ib_cq); + struct bnxt_re_qp *qp; + struct bnxt_qplib_cqe *cqe; + int i, ncqe, budget; + u32 tbl_idx; + struct bnxt_re_sqp_entries *sqp_entry = NULL; + unsigned long flags; + + spin_lock_irqsave(&cq->cq_lock, flags); + budget = min_t(u32, num_entries, cq->max_cql); + if (!cq->cql) { + dev_err(rdev_to_dev(cq->rdev), "POLL CQ : no CQL to use"); + goto exit; + } + cqe = &cq->cql[0]; + while (budget) { + ncqe = bnxt_qplib_poll_cq(&cq->qplib_cq, cqe, budget); + if (!ncqe) + break; + + for (i = 0; i < ncqe; i++, cqe++) { + /* Transcribe each qplib_wqe back to ib_wc */ + memset(wc, 0, sizeof(*wc)); + + wc->wr_id = cqe->wr_id; + wc->byte_len = cqe->length; + qp = container_of + ((struct bnxt_qplib_qp *) + (unsigned long)(cqe->qp_handle), + struct bnxt_re_qp, qplib_qp); + if (!qp) { + dev_err(rdev_to_dev(cq->rdev), + "POLL CQ : bad QP handle"); + continue; + } + wc->qp = &qp->ib_qp; + wc->ex.imm_data = cqe->immdata; + wc->src_qp = cqe->src_qp; + memcpy(wc->smac, cqe->smac, ETH_ALEN); + wc->port_num = 1; + wc->vendor_err = cqe->status; + + switch (cqe->opcode) { + case CQ_BASE_CQE_TYPE_REQ: + if (qp->qplib_qp.id == + qp->rdev->qp1_sqp->qplib_qp.id) { + /* Handle this completion with + * the stored completion + */ + memset(wc, 0, sizeof(*wc)); + continue; + } + bnxt_re_process_req_wc(wc, cqe); + break; + case CQ_BASE_CQE_TYPE_RES_RAWETH_QP1: + if (!cqe->status) { + int rc = 0; + + rc = bnxt_re_process_raw_qp_pkt_rx + (qp, cqe); + if (!rc) { + memset(wc, 0, sizeof(*wc)); + continue; + } + cqe->status = -1; + } + /* Errors need not be looped back. + * But change the wr_id to the one + * stored in the table + */ + tbl_idx = cqe->wr_id; + sqp_entry = &cq->rdev->sqp_tbl[tbl_idx]; + wc->wr_id = sqp_entry->wrid; + bnxt_re_process_res_rawqp1_wc(wc, cqe); + break; + case CQ_BASE_CQE_TYPE_RES_RC: + bnxt_re_process_res_rc_wc(wc, cqe); + break; + case CQ_BASE_CQE_TYPE_RES_UD: + if (qp->qplib_qp.id == + qp->rdev->qp1_sqp->qplib_qp.id) { + /* Handle this completion with + * the stored completion + */ + if (cqe->status) { + continue; + } else { + bnxt_re_process_res_shadow_qp_wc + (qp, wc, cqe); + break; + } + } + bnxt_re_process_res_ud_wc(wc, cqe); + break; + default: + dev_err(rdev_to_dev(cq->rdev), + "POLL CQ : type 0x%x not handled", + cqe->opcode); + continue; + } + wc++; + budget--; + } + } +exit: + spin_unlock_irqrestore(&cq->cq_lock, flags); + return num_entries - budget; +} + +int bnxt_re_req_notify_cq(struct ib_cq *ib_cq, + enum ib_cq_notify_flags ib_cqn_flags) +{ + struct bnxt_re_cq *cq = container_of(ib_cq, struct bnxt_re_cq, ib_cq); + int type = 0; + + /* Trigger on the very next completion */ + if (ib_cqn_flags & IB_CQ_NEXT_COMP) + type = DBR_DBR_TYPE_CQ_ARMALL; + /* Trigger on the next solicited completion */ + else if (ib_cqn_flags & IB_CQ_SOLICITED) + type = DBR_DBR_TYPE_CQ_ARMSE; + + bnxt_qplib_req_notify_cq(&cq->qplib_cq, type); + + return 0; +} + +/* Memory Regions */ +struct ib_mr *bnxt_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_re_mr *mr; + u64 pbl = 0; + int rc; + + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + mr->rdev = rdev; + mr->qplib_mr.pd = &pd->qplib_pd; + mr->qplib_mr.flags = __from_ib_access_flags(mr_access_flags); + mr->qplib_mr.type = CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR; + + /* Allocate and register 0 as the address */ + rc = bnxt_qplib_alloc_mrw(&rdev->qplib_res, &mr->qplib_mr); + if (rc) + goto fail; + + mr->qplib_mr.hwq.level = PBL_LVL_MAX; + mr->qplib_mr.total_size = -1; /* Infinte length */ + rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &mr->qplib_mr, &pbl, 0, false); + if (rc) + goto fail_mr; + + mr->ib_mr.lkey = mr->qplib_mr.lkey; + if (mr_access_flags & (IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ | + IB_ACCESS_REMOTE_ATOMIC)) + mr->ib_mr.rkey = mr->ib_mr.lkey; + atomic_inc(&rdev->mr_count); + + return &mr->ib_mr; + +fail_mr: + bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); +fail: + kfree(mr); + return ERR_PTR(rc); +} + +int bnxt_re_dereg_mr(struct ib_mr *ib_mr) +{ + struct bnxt_re_mr *mr = container_of(ib_mr, struct bnxt_re_mr, ib_mr); + struct bnxt_re_dev *rdev = mr->rdev; + int rc = 0; + + if (mr->npages && mr->pages) { + rc = bnxt_qplib_free_fast_reg_page_list(&rdev->qplib_res, + &mr->qplib_frpl); + kfree(mr->pages); + mr->npages = 0; + mr->pages = NULL; + } + rc = bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); + + if (!IS_ERR(mr->ib_umem) && mr->ib_umem) + ib_umem_release(mr->ib_umem); + + kfree(mr); + atomic_dec(&rdev->mr_count); + return rc; +} + +static int bnxt_re_set_page(struct ib_mr *ib_mr, u64 addr) +{ + struct bnxt_re_mr *mr = container_of(ib_mr, struct bnxt_re_mr, ib_mr); + + if (unlikely(mr->npages == mr->qplib_frpl.max_pg_ptrs)) + return -ENOMEM; + + mr->pages[mr->npages++] = addr; + return 0; +} + +int bnxt_re_map_mr_sg(struct ib_mr *ib_mr, struct scatterlist *sg, int sg_nents, + unsigned int *sg_offset) +{ + struct bnxt_re_mr *mr = container_of(ib_mr, struct bnxt_re_mr, ib_mr); + + mr->npages = 0; + return ib_sg_to_pages(ib_mr, sg, sg_nents, sg_offset, bnxt_re_set_page); +} + +struct ib_mr *bnxt_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type, + u32 max_num_sg) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_re_mr *mr = NULL; + int rc; + + if (type != IB_MR_TYPE_MEM_REG) { + dev_dbg(rdev_to_dev(rdev), "MR type 0x%x not supported", type); + return ERR_PTR(-EINVAL); + } + if (max_num_sg > MAX_PBL_LVL_1_PGS) + return ERR_PTR(-EINVAL); + + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + mr->rdev = rdev; + mr->qplib_mr.pd = &pd->qplib_pd; + mr->qplib_mr.flags = BNXT_QPLIB_FR_PMR; + mr->qplib_mr.type = CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR; + + rc = bnxt_qplib_alloc_mrw(&rdev->qplib_res, &mr->qplib_mr); + if (rc) + goto fail; + + mr->ib_mr.lkey = mr->qplib_mr.lkey; + mr->ib_mr.rkey = mr->ib_mr.lkey; + + mr->pages = kcalloc(max_num_sg, sizeof(u64), GFP_KERNEL); + if (!mr->pages) { + rc = -ENOMEM; + goto fail; + } + rc = bnxt_qplib_alloc_fast_reg_page_list(&rdev->qplib_res, + &mr->qplib_frpl, max_num_sg); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to allocate HW FR page list"); + goto fail_mr; + } + + atomic_inc(&rdev->mr_count); + return &mr->ib_mr; + +fail_mr: + bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); +fail: + kfree(mr->pages); + kfree(mr); + return ERR_PTR(rc); +} + +/* Fast Memory Regions */ +struct ib_fmr *bnxt_re_alloc_fmr(struct ib_pd *ib_pd, int mr_access_flags, + struct ib_fmr_attr *fmr_attr) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_re_fmr *fmr; + int rc; + + if (fmr_attr->max_pages > MAX_PBL_LVL_2_PGS || + fmr_attr->max_maps > rdev->dev_attr.max_map_per_fmr) { + dev_err(rdev_to_dev(rdev), "Allocate FMR exceeded Max limit"); + return ERR_PTR(-ENOMEM); + } + fmr = kzalloc(sizeof(*fmr), GFP_KERNEL); + if (!fmr) + return ERR_PTR(-ENOMEM); + + fmr->rdev = rdev; + fmr->qplib_fmr.pd = &pd->qplib_pd; + fmr->qplib_fmr.type = CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR; + + rc = bnxt_qplib_alloc_mrw(&rdev->qplib_res, &fmr->qplib_fmr); + if (rc) + goto fail; + + fmr->qplib_fmr.flags = __from_ib_access_flags(mr_access_flags); + fmr->ib_fmr.lkey = fmr->qplib_fmr.lkey; + fmr->ib_fmr.rkey = fmr->ib_fmr.lkey; + + atomic_inc(&rdev->mr_count); + return &fmr->ib_fmr; +fail: + kfree(fmr); + return ERR_PTR(rc); +} + +int bnxt_re_map_phys_fmr(struct ib_fmr *ib_fmr, u64 *page_list, int list_len, + u64 iova) +{ + struct bnxt_re_fmr *fmr = container_of(ib_fmr, struct bnxt_re_fmr, + ib_fmr); + struct bnxt_re_dev *rdev = fmr->rdev; + int rc; + + fmr->qplib_fmr.va = iova; + fmr->qplib_fmr.total_size = list_len * PAGE_SIZE; + + rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &fmr->qplib_fmr, page_list, + list_len, true); + if (rc) + dev_err(rdev_to_dev(rdev), "Failed to map FMR for lkey = 0x%x!", + fmr->ib_fmr.lkey); + return rc; +} + +int bnxt_re_unmap_fmr(struct list_head *fmr_list) +{ + struct bnxt_re_dev *rdev; + struct bnxt_re_fmr *fmr; + struct ib_fmr *ib_fmr; + int rc = 0; + + /* Validate each FMRs inside the fmr_list */ + list_for_each_entry(ib_fmr, fmr_list, list) { + fmr = container_of(ib_fmr, struct bnxt_re_fmr, ib_fmr); + rdev = fmr->rdev; + + if (rdev) { + rc = bnxt_qplib_dereg_mrw(&rdev->qplib_res, + &fmr->qplib_fmr, true); + if (rc) + break; + } + } + return rc; +} + +int bnxt_re_dealloc_fmr(struct ib_fmr *ib_fmr) +{ + struct bnxt_re_fmr *fmr = container_of(ib_fmr, struct bnxt_re_fmr, + ib_fmr); + struct bnxt_re_dev *rdev = fmr->rdev; + int rc; + + rc = bnxt_qplib_free_mrw(&rdev->qplib_res, &fmr->qplib_fmr); + if (rc) + dev_err(rdev_to_dev(rdev), "Failed to free FMR"); + + kfree(fmr); + atomic_dec(&rdev->mr_count); + return rc; +} + +/* uverbs */ +struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, + u64 virt_addr, int mr_access_flags, + struct ib_udata *udata) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct bnxt_re_mr *mr; + struct ib_umem *umem; + u64 *pbl_tbl, *pbl_tbl_orig; + int i, umem_pgs, pages, page_shift, rc; + struct scatterlist *sg; + int entry; + + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + mr->rdev = rdev; + mr->qplib_mr.pd = &pd->qplib_pd; + mr->qplib_mr.flags = __from_ib_access_flags(mr_access_flags); + mr->qplib_mr.type = CMDQ_ALLOCATE_MRW_MRW_FLAGS_MR; + + umem = ib_umem_get(ib_pd->uobject->context, start, length, + mr_access_flags, 0); + if (IS_ERR(umem)) { + dev_err(rdev_to_dev(rdev), "Failed to get umem"); + rc = -EFAULT; + goto free_mr; + } + mr->ib_umem = umem; + + rc = bnxt_qplib_alloc_mrw(&rdev->qplib_res, &mr->qplib_mr); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to allocate MR"); + goto release_umem; + } + /* The fixed portion of the rkey is the same as the lkey */ + mr->ib_mr.rkey = mr->qplib_mr.rkey; + + mr->qplib_mr.va = virt_addr; + umem_pgs = ib_umem_page_count(umem); + if (!umem_pgs) { + dev_err(rdev_to_dev(rdev), "umem is invalid!"); + rc = -EINVAL; + goto free_mrw; + } + mr->qplib_mr.total_size = length; + + pbl_tbl = kcalloc(umem_pgs, sizeof(u64 *), GFP_KERNEL); + if (!pbl_tbl) { + rc = -EINVAL; + goto free_mrw; + } + pbl_tbl_orig = pbl_tbl; + + page_shift = ilog2(umem->page_size); + if (umem->hugetlb) { + dev_err(rdev_to_dev(rdev), "umem hugetlb not supported!"); + rc = -EFAULT; + goto fail; + } + if (umem->page_size != PAGE_SIZE) { + dev_err(rdev_to_dev(rdev), "umem page size unsupported!"); + rc = -EFAULT; + goto fail; + } + /* Map umem buf ptrs to the PBL */ + for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { + pages = sg_dma_len(sg) >> page_shift; + for (i = 0; i < pages; i++, pbl_tbl++) + *pbl_tbl = sg_dma_address(sg) + (i << page_shift); + } + rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &mr->qplib_mr, pbl_tbl_orig, + umem_pgs, false); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to register user MR"); + goto fail; + } + + kfree(pbl_tbl_orig); + + mr->ib_mr.lkey = mr->qplib_mr.lkey; + mr->ib_mr.rkey = mr->qplib_mr.lkey; + atomic_inc(&rdev->mr_count); + + return &mr->ib_mr; +fail: + kfree(pbl_tbl_orig); +free_mrw: + bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); +release_umem: + ib_umem_release(umem); +free_mr: + kfree(mr); + return ERR_PTR(rc); +} + +struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev, + struct ib_udata *udata) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); + struct bnxt_re_uctx_resp resp; + struct bnxt_re_ucontext *uctx; + struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; + int rc; + + dev_dbg(rdev_to_dev(rdev), "ABI version requested %d", + ibdev->uverbs_abi_ver); + + if (ibdev->uverbs_abi_ver != BNXT_RE_ABI_VERSION) { + dev_dbg(rdev_to_dev(rdev), " is different from the device %d ", + BNXT_RE_ABI_VERSION); + return ERR_PTR(-EPERM); + } + + uctx = kzalloc(sizeof(*uctx), GFP_KERNEL); + if (!uctx) + return ERR_PTR(-ENOMEM); + + uctx->rdev = rdev; + + uctx->shpg = (void *)__get_free_page(GFP_KERNEL); + if (!uctx->shpg) { + rc = -ENOMEM; + goto fail; + } + spin_lock_init(&uctx->sh_lock); + + resp.dev_id = rdev->en_dev->pdev->devfn; /*Temp, Use idr_alloc instead*/ + resp.max_qp = rdev->qplib_ctx.qpc_count; + resp.pg_size = PAGE_SIZE; + resp.cqe_sz = sizeof(struct cq_base); + resp.max_cqd = dev_attr->max_cq_wqes; + resp.rsvd = 0; + + rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); + if (rc) { + dev_err(rdev_to_dev(rdev), "Failed to copy user context"); + rc = -EFAULT; + goto cfail; + } + + return &uctx->ib_uctx; +cfail: + free_page((unsigned long)uctx->shpg); + uctx->shpg = NULL; +fail: + kfree(uctx); + return ERR_PTR(rc); +} + +int bnxt_re_dealloc_ucontext(struct ib_ucontext *ib_uctx) +{ + struct bnxt_re_ucontext *uctx = container_of(ib_uctx, + struct bnxt_re_ucontext, + ib_uctx); + if (uctx->shpg) + free_page((unsigned long)uctx->shpg); + kfree(uctx); + return 0; +} + +/* Helper function to mmap the virtual memory from user app */ +int bnxt_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma) +{ + struct bnxt_re_ucontext *uctx = container_of(ib_uctx, + struct bnxt_re_ucontext, + ib_uctx); + struct bnxt_re_dev *rdev = uctx->rdev; + u64 pfn; + + if (vma->vm_end - vma->vm_start != PAGE_SIZE) + return -EINVAL; + + if (vma->vm_pgoff) { + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + PAGE_SIZE, vma->vm_page_prot)) { + dev_err(rdev_to_dev(rdev), "Failed to map DPI"); + return -EAGAIN; + } + } else { + pfn = virt_to_phys(uctx->shpg) >> PAGE_SHIFT; + if (remap_pfn_range(vma, vma->vm_start, + pfn, PAGE_SIZE, vma->vm_page_prot)) { + dev_err(rdev_to_dev(rdev), + "Failed to map shared page"); + return -EAGAIN; + } + } + + return 0; +} diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h new file mode 100644 index 000000000000..b4084c252f06 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -0,0 +1,197 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: IB Verbs interpreter (header) + */ + +#ifndef __BNXT_RE_IB_VERBS_H__ +#define __BNXT_RE_IB_VERBS_H__ + +struct bnxt_re_gid_ctx { + u32 idx; + u32 refcnt; +}; + +struct bnxt_re_pd { + struct bnxt_re_dev *rdev; + struct ib_pd ib_pd; + struct bnxt_qplib_pd qplib_pd; + struct bnxt_qplib_dpi dpi; +}; + +struct bnxt_re_ah { + struct bnxt_re_dev *rdev; + struct ib_ah ib_ah; + struct bnxt_qplib_ah qplib_ah; +}; + +struct bnxt_re_qp { + struct list_head list; + struct bnxt_re_dev *rdev; + struct ib_qp ib_qp; + spinlock_t sq_lock; /* protect sq */ + struct bnxt_qplib_qp qplib_qp; + struct ib_umem *sumem; + struct ib_umem *rumem; + /* QP1 */ + u32 send_psn; + struct ib_ud_header qp1_hdr; +}; + +struct bnxt_re_cq { + struct bnxt_re_dev *rdev; + spinlock_t cq_lock; /* protect cq */ + u16 cq_count; + u16 cq_period; + struct ib_cq ib_cq; + struct bnxt_qplib_cq qplib_cq; + struct bnxt_qplib_cqe *cql; +#define MAX_CQL_PER_POLL 1024 + u32 max_cql; + struct ib_umem *umem; +}; + +struct bnxt_re_mr { + struct bnxt_re_dev *rdev; + struct ib_mr ib_mr; + struct ib_umem *ib_umem; + struct bnxt_qplib_mrw qplib_mr; + u32 npages; + u64 *pages; + struct bnxt_qplib_frpl qplib_frpl; +}; + +struct bnxt_re_frpl { + struct bnxt_re_dev *rdev; + struct bnxt_qplib_frpl qplib_frpl; + u64 *page_list; +}; + +struct bnxt_re_fmr { + struct bnxt_re_dev *rdev; + struct ib_fmr ib_fmr; + struct bnxt_qplib_mrw qplib_fmr; +}; + +struct bnxt_re_mw { + struct bnxt_re_dev *rdev; + struct ib_mw ib_mw; + struct bnxt_qplib_mrw qplib_mw; +}; + +struct bnxt_re_ucontext { + struct bnxt_re_dev *rdev; + struct ib_ucontext ib_uctx; + struct bnxt_qplib_dpi *dpi; + void *shpg; + spinlock_t sh_lock; /* protect shpg */ +}; + +struct net_device *bnxt_re_get_netdev(struct ib_device *ibdev, u8 port_num); + +int bnxt_re_query_device(struct ib_device *ibdev, + struct ib_device_attr *ib_attr, + struct ib_udata *udata); +int bnxt_re_modify_device(struct ib_device *ibdev, + int device_modify_mask, + struct ib_device_modify *device_modify); +int bnxt_re_query_port(struct ib_device *ibdev, u8 port_num, + struct ib_port_attr *port_attr); +int bnxt_re_modify_port(struct ib_device *ibdev, u8 port_num, + int port_modify_mask, + struct ib_port_modify *port_modify); +int bnxt_re_get_port_immutable(struct ib_device *ibdev, u8 port_num, + struct ib_port_immutable *immutable); +int bnxt_re_query_pkey(struct ib_device *ibdev, u8 port_num, + u16 index, u16 *pkey); +int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num, + unsigned int index, void **context); +int bnxt_re_add_gid(struct ib_device *ibdev, u8 port_num, + unsigned int index, const union ib_gid *gid, + const struct ib_gid_attr *attr, void **context); +int bnxt_re_query_gid(struct ib_device *ibdev, u8 port_num, + int index, union ib_gid *gid); +enum rdma_link_layer bnxt_re_get_link_layer(struct ib_device *ibdev, + u8 port_num); +struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev, + struct ib_ucontext *context, + struct ib_udata *udata); +int bnxt_re_dealloc_pd(struct ib_pd *pd); +struct ib_ah *bnxt_re_create_ah(struct ib_pd *pd, + struct ib_ah_attr *ah_attr, + struct ib_udata *udata); +int bnxt_re_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr); +int bnxt_re_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr); +int bnxt_re_destroy_ah(struct ib_ah *ah); +struct ib_qp *bnxt_re_create_qp(struct ib_pd *pd, + struct ib_qp_init_attr *qp_init_attr, + struct ib_udata *udata); +int bnxt_re_modify_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr, + int qp_attr_mask, struct ib_udata *udata); +int bnxt_re_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr, + int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr); +int bnxt_re_destroy_qp(struct ib_qp *qp); +int bnxt_re_post_send(struct ib_qp *qp, struct ib_send_wr *send_wr, + struct ib_send_wr **bad_send_wr); +int bnxt_re_post_recv(struct ib_qp *qp, struct ib_recv_wr *recv_wr, + struct ib_recv_wr **bad_recv_wr); +struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev, + const struct ib_cq_init_attr *attr, + struct ib_ucontext *context, + struct ib_udata *udata); +int bnxt_re_destroy_cq(struct ib_cq *cq); +int bnxt_re_poll_cq(struct ib_cq *cq, int num_entries, struct ib_wc *wc); +int bnxt_re_req_notify_cq(struct ib_cq *cq, enum ib_cq_notify_flags flags); +struct ib_mr *bnxt_re_get_dma_mr(struct ib_pd *pd, int mr_access_flags); + +int bnxt_re_map_mr_sg(struct ib_mr *ib_mr, struct scatterlist *sg, int sg_nents, + unsigned int *sg_offset); +struct ib_mr *bnxt_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type mr_type, + u32 max_num_sg); +int bnxt_re_dereg_mr(struct ib_mr *mr); +struct ib_fmr *bnxt_re_alloc_fmr(struct ib_pd *pd, int mr_access_flags, + struct ib_fmr_attr *fmr_attr); +int bnxt_re_map_phys_fmr(struct ib_fmr *fmr, u64 *page_list, int list_len, + u64 iova); +int bnxt_re_unmap_fmr(struct list_head *fmr_list); +int bnxt_re_dealloc_fmr(struct ib_fmr *fmr); +struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt_addr, int mr_access_flags, + struct ib_udata *udata); +struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev, + struct ib_udata *udata); +int bnxt_re_dealloc_ucontext(struct ib_ucontext *context); +int bnxt_re_mmap(struct ib_ucontext *context, struct vm_area_struct *vma); +#endif /* __BNXT_RE_IB_VERBS_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c new file mode 100644 index 000000000000..5d355401179b --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -0,0 +1,1315 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: Main component of the bnxt_re driver + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/rculist.h> +#include <linux/spinlock.h> +#include <linux/pci.h> +#include <net/dcbnl.h> +#include <net/ipv6.h> +#include <net/addrconf.h> +#include <linux/if_ether.h> + +#include <rdma/ib_verbs.h> +#include <rdma/ib_user_verbs.h> +#include <rdma/ib_umem.h> +#include <rdma/ib_addr.h> + +#include "bnxt_ulp.h" +#include "roce_hsi.h" +#include "qplib_res.h" +#include "qplib_sp.h" +#include "qplib_fp.h" +#include "qplib_rcfw.h" +#include "bnxt_re.h" +#include "ib_verbs.h" +#include <rdma/bnxt_re-abi.h> +#include "bnxt.h" +static char version[] = + BNXT_RE_DESC " v" ROCE_DRV_MODULE_VERSION "\n"; + +MODULE_AUTHOR("Eddie Wai <eddie.wai@broadcom.com>"); +MODULE_DESCRIPTION(BNXT_RE_DESC " Driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(ROCE_DRV_MODULE_VERSION); + +/* globals */ +static struct list_head bnxt_re_dev_list = LIST_HEAD_INIT(bnxt_re_dev_list); +/* Mutex to protect the list of bnxt_re devices added */ +static DEFINE_MUTEX(bnxt_re_dev_lock); +static struct workqueue_struct *bnxt_re_wq; + +/* for handling bnxt_en callbacks later */ +static void bnxt_re_stop(void *p) +{ +} + +static void bnxt_re_start(void *p) +{ +} + +static void bnxt_re_sriov_config(void *p, int num_vfs) +{ +} + +static struct bnxt_ulp_ops bnxt_re_ulp_ops = { + .ulp_async_notifier = NULL, + .ulp_stop = bnxt_re_stop, + .ulp_start = bnxt_re_start, + .ulp_sriov_config = bnxt_re_sriov_config +}; + +/* RoCE -> Net driver */ + +/* Driver registration routines used to let the networking driver (bnxt_en) + * to know that the RoCE driver is now installed + */ +static int bnxt_re_unregister_netdev(struct bnxt_re_dev *rdev, bool lock_wait) +{ + struct bnxt_en_dev *en_dev; + int rc; + + if (!rdev) + return -EINVAL; + + en_dev = rdev->en_dev; + /* Acquire rtnl lock if it is not invokded from netdev event */ + if (lock_wait) + rtnl_lock(); + + rc = en_dev->en_ops->bnxt_unregister_device(rdev->en_dev, + BNXT_ROCE_ULP); + if (lock_wait) + rtnl_unlock(); + return rc; +} + +static int bnxt_re_register_netdev(struct bnxt_re_dev *rdev) +{ + struct bnxt_en_dev *en_dev; + int rc = 0; + + if (!rdev) + return -EINVAL; + + en_dev = rdev->en_dev; + + rtnl_lock(); + rc = en_dev->en_ops->bnxt_register_device(en_dev, BNXT_ROCE_ULP, + &bnxt_re_ulp_ops, rdev); + rtnl_unlock(); + return rc; +} + +static int bnxt_re_free_msix(struct bnxt_re_dev *rdev, bool lock_wait) +{ + struct bnxt_en_dev *en_dev; + int rc; + + if (!rdev) + return -EINVAL; + + en_dev = rdev->en_dev; + + if (lock_wait) + rtnl_lock(); + + rc = en_dev->en_ops->bnxt_free_msix(rdev->en_dev, BNXT_ROCE_ULP); + + if (lock_wait) + rtnl_unlock(); + return rc; +} + +static int bnxt_re_request_msix(struct bnxt_re_dev *rdev) +{ + int rc = 0, num_msix_want = BNXT_RE_MIN_MSIX, num_msix_got; + struct bnxt_en_dev *en_dev; + + if (!rdev) + return -EINVAL; + + en_dev = rdev->en_dev; + + rtnl_lock(); + num_msix_got = en_dev->en_ops->bnxt_request_msix(en_dev, BNXT_ROCE_ULP, + rdev->msix_entries, + num_msix_want); + if (num_msix_got < BNXT_RE_MIN_MSIX) { + rc = -EINVAL; + goto done; + } + if (num_msix_got != num_msix_want) { + dev_warn(rdev_to_dev(rdev), + "Requested %d MSI-X vectors, got %d\n", + num_msix_want, num_msix_got); + } + rdev->num_msix = num_msix_got; +done: + rtnl_unlock(); + return rc; +} + +static void bnxt_re_init_hwrm_hdr(struct bnxt_re_dev *rdev, struct input *hdr, + u16 opcd, u16 crid, u16 trid) +{ + hdr->req_type = cpu_to_le16(opcd); + hdr->cmpl_ring = cpu_to_le16(crid); + hdr->target_id = cpu_to_le16(trid); +} + +static void bnxt_re_fill_fw_msg(struct bnxt_fw_msg *fw_msg, void *msg, + int msg_len, void *resp, int resp_max_len, + int timeout) +{ + fw_msg->msg = msg; + fw_msg->msg_len = msg_len; + fw_msg->resp = resp; + fw_msg->resp_max_len = resp_max_len; + fw_msg->timeout = timeout; +} + +static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, u16 fw_ring_id, + bool lock_wait) +{ + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct hwrm_ring_free_input req = {0}; + struct hwrm_ring_free_output resp; + struct bnxt_fw_msg fw_msg; + bool do_unlock = false; + int rc = -EINVAL; + + if (!en_dev) + return rc; + + memset(&fw_msg, 0, sizeof(fw_msg)); + if (lock_wait) { + rtnl_lock(); + do_unlock = true; + } + + bnxt_re_init_hwrm_hdr(rdev, (void *)&req, HWRM_RING_FREE, -1, -1); + req.ring_type = RING_ALLOC_REQ_RING_TYPE_L2_CMPL; + req.ring_id = cpu_to_le16(fw_ring_id); + bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, + sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); + rc = en_dev->en_ops->bnxt_send_fw_msg(en_dev, BNXT_ROCE_ULP, &fw_msg); + if (rc) + dev_err(rdev_to_dev(rdev), + "Failed to free HW ring:%d :%#x", req.ring_id, rc); + if (do_unlock) + rtnl_unlock(); + return rc; +} + +static int bnxt_re_net_ring_alloc(struct bnxt_re_dev *rdev, dma_addr_t *dma_arr, + int pages, int type, u32 ring_mask, + u32 map_index, u16 *fw_ring_id) +{ + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct hwrm_ring_alloc_input req = {0}; + struct hwrm_ring_alloc_output resp; + struct bnxt_fw_msg fw_msg; + int rc = -EINVAL; + + if (!en_dev) + return rc; + + memset(&fw_msg, 0, sizeof(fw_msg)); + rtnl_lock(); + bnxt_re_init_hwrm_hdr(rdev, (void *)&req, HWRM_RING_ALLOC, -1, -1); + req.enables = 0; + req.page_tbl_addr = cpu_to_le64(dma_arr[0]); + if (pages > 1) { + /* Page size is in log2 units */ + req.page_size = BNXT_PAGE_SHIFT; + req.page_tbl_depth = 1; + } + req.fbo = 0; + /* Association of ring index with doorbell index and MSIX number */ + req.logical_id = cpu_to_le16(map_index); + req.length = cpu_to_le32(ring_mask + 1); + req.ring_type = RING_ALLOC_REQ_RING_TYPE_L2_CMPL; + req.int_mode = RING_ALLOC_REQ_INT_MODE_MSIX; + bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, + sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); + rc = en_dev->en_ops->bnxt_send_fw_msg(en_dev, BNXT_ROCE_ULP, &fw_msg); + if (!rc) + *fw_ring_id = le16_to_cpu(resp.ring_id); + + rtnl_unlock(); + return rc; +} + +static int bnxt_re_net_stats_ctx_free(struct bnxt_re_dev *rdev, + u32 fw_stats_ctx_id, bool lock_wait) +{ + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct hwrm_stat_ctx_free_input req = {0}; + struct bnxt_fw_msg fw_msg; + bool do_unlock = false; + int rc = -EINVAL; + + if (!en_dev) + return rc; + + memset(&fw_msg, 0, sizeof(fw_msg)); + if (lock_wait) { + rtnl_lock(); + do_unlock = true; + } + + bnxt_re_init_hwrm_hdr(rdev, (void *)&req, HWRM_STAT_CTX_FREE, -1, -1); + req.stat_ctx_id = cpu_to_le32(fw_stats_ctx_id); + bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&req, + sizeof(req), DFLT_HWRM_CMD_TIMEOUT); + rc = en_dev->en_ops->bnxt_send_fw_msg(en_dev, BNXT_ROCE_ULP, &fw_msg); + if (rc) + dev_err(rdev_to_dev(rdev), + "Failed to free HW stats context %#x", rc); + + if (do_unlock) + rtnl_unlock(); + return rc; +} + +static int bnxt_re_net_stats_ctx_alloc(struct bnxt_re_dev *rdev, + dma_addr_t dma_map, + u32 *fw_stats_ctx_id) +{ + struct hwrm_stat_ctx_alloc_output resp = {0}; + struct hwrm_stat_ctx_alloc_input req = {0}; + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct bnxt_fw_msg fw_msg; + int rc = -EINVAL; + + *fw_stats_ctx_id = INVALID_STATS_CTX_ID; + + if (!en_dev) + return rc; + + memset(&fw_msg, 0, sizeof(fw_msg)); + rtnl_lock(); + + bnxt_re_init_hwrm_hdr(rdev, (void *)&req, HWRM_STAT_CTX_ALLOC, -1, -1); + req.update_period_ms = cpu_to_le32(1000); + req.stats_dma_addr = cpu_to_le64(dma_map); + bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, + sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); + rc = en_dev->en_ops->bnxt_send_fw_msg(en_dev, BNXT_ROCE_ULP, &fw_msg); + if (!rc) + *fw_stats_ctx_id = le32_to_cpu(resp.stat_ctx_id); + + rtnl_unlock(); + return rc; +} + +/* Device */ + +static bool is_bnxt_re_dev(struct net_device *netdev) +{ + struct ethtool_drvinfo drvinfo; + + if (netdev->ethtool_ops && netdev->ethtool_ops->get_drvinfo) { + memset(&drvinfo, 0, sizeof(drvinfo)); + netdev->ethtool_ops->get_drvinfo(netdev, &drvinfo); + + if (strcmp(drvinfo.driver, "bnxt_en")) + return false; + return true; + } + return false; +} + +static struct bnxt_re_dev *bnxt_re_from_netdev(struct net_device *netdev) +{ + struct bnxt_re_dev *rdev; + + rcu_read_lock(); + list_for_each_entry_rcu(rdev, &bnxt_re_dev_list, list) { + if (rdev->netdev == netdev) { + rcu_read_unlock(); + return rdev; + } + } + rcu_read_unlock(); + return NULL; +} + +static void bnxt_re_dev_unprobe(struct net_device *netdev, + struct bnxt_en_dev *en_dev) +{ + dev_put(netdev); + module_put(en_dev->pdev->driver->driver.owner); +} + +static struct bnxt_en_dev *bnxt_re_dev_probe(struct net_device *netdev) +{ + struct bnxt *bp = netdev_priv(netdev); + struct bnxt_en_dev *en_dev; + struct pci_dev *pdev; + + /* Call bnxt_en's RoCE probe via indirect API */ + if (!bp->ulp_probe) + return ERR_PTR(-EINVAL); + + en_dev = bp->ulp_probe(netdev); + if (IS_ERR(en_dev)) + return en_dev; + + pdev = en_dev->pdev; + if (!pdev) + return ERR_PTR(-EINVAL); + + if (!(en_dev->flags & BNXT_EN_FLAG_ROCE_CAP)) { + dev_dbg(&pdev->dev, + "%s: probe error: RoCE is not supported on this device", + ROCE_DRV_MODULE_NAME); + return ERR_PTR(-ENODEV); + } + + /* Bump net device reference count */ + if (!try_module_get(pdev->driver->driver.owner)) + return ERR_PTR(-ENODEV); + + dev_hold(netdev); + + return en_dev; +} + +static void bnxt_re_unregister_ib(struct bnxt_re_dev *rdev) +{ + ib_unregister_device(&rdev->ibdev); +} + +static int bnxt_re_register_ib(struct bnxt_re_dev *rdev) +{ + struct ib_device *ibdev = &rdev->ibdev; + + /* ib device init */ + ibdev->owner = THIS_MODULE; + ibdev->node_type = RDMA_NODE_IB_CA; + strlcpy(ibdev->name, "bnxt_re%d", IB_DEVICE_NAME_MAX); + strlcpy(ibdev->node_desc, BNXT_RE_DESC " HCA", + strlen(BNXT_RE_DESC) + 5); + ibdev->phys_port_cnt = 1; + + bnxt_qplib_get_guid(rdev->netdev->dev_addr, (u8 *)&ibdev->node_guid); + + ibdev->num_comp_vectors = 1; + ibdev->dev.parent = &rdev->en_dev->pdev->dev; + ibdev->local_dma_lkey = BNXT_QPLIB_RSVD_LKEY; + + /* User space */ + ibdev->uverbs_abi_ver = BNXT_RE_ABI_VERSION; + ibdev->uverbs_cmd_mask = + (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) | + (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) | + (1ull << IB_USER_VERBS_CMD_QUERY_PORT) | + (1ull << IB_USER_VERBS_CMD_ALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_DEALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_REG_MR) | + (1ull << IB_USER_VERBS_CMD_REREG_MR) | + (1ull << IB_USER_VERBS_CMD_DEREG_MR) | + (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) | + (1ull << IB_USER_VERBS_CMD_CREATE_CQ) | + (1ull << IB_USER_VERBS_CMD_RESIZE_CQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_CQ) | + (1ull << IB_USER_VERBS_CMD_CREATE_QP) | + (1ull << IB_USER_VERBS_CMD_MODIFY_QP) | + (1ull << IB_USER_VERBS_CMD_QUERY_QP) | + (1ull << IB_USER_VERBS_CMD_DESTROY_QP) | + (1ull << IB_USER_VERBS_CMD_CREATE_SRQ) | + (1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) | + (1ull << IB_USER_VERBS_CMD_QUERY_SRQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) | + (1ull << IB_USER_VERBS_CMD_CREATE_AH) | + (1ull << IB_USER_VERBS_CMD_MODIFY_AH) | + (1ull << IB_USER_VERBS_CMD_QUERY_AH) | + (1ull << IB_USER_VERBS_CMD_DESTROY_AH); + /* POLL_CQ and REQ_NOTIFY_CQ is directly handled in libbnxt_re */ + + /* Kernel verbs */ + ibdev->query_device = bnxt_re_query_device; + ibdev->modify_device = bnxt_re_modify_device; + + ibdev->query_port = bnxt_re_query_port; + ibdev->modify_port = bnxt_re_modify_port; + ibdev->get_port_immutable = bnxt_re_get_port_immutable; + ibdev->query_pkey = bnxt_re_query_pkey; + ibdev->query_gid = bnxt_re_query_gid; + ibdev->get_netdev = bnxt_re_get_netdev; + ibdev->add_gid = bnxt_re_add_gid; + ibdev->del_gid = bnxt_re_del_gid; + ibdev->get_link_layer = bnxt_re_get_link_layer; + + ibdev->alloc_pd = bnxt_re_alloc_pd; + ibdev->dealloc_pd = bnxt_re_dealloc_pd; + + ibdev->create_ah = bnxt_re_create_ah; + ibdev->modify_ah = bnxt_re_modify_ah; + ibdev->query_ah = bnxt_re_query_ah; + ibdev->destroy_ah = bnxt_re_destroy_ah; + + ibdev->create_qp = bnxt_re_create_qp; + ibdev->modify_qp = bnxt_re_modify_qp; + ibdev->query_qp = bnxt_re_query_qp; + ibdev->destroy_qp = bnxt_re_destroy_qp; + + ibdev->post_send = bnxt_re_post_send; + ibdev->post_recv = bnxt_re_post_recv; + + ibdev->create_cq = bnxt_re_create_cq; + ibdev->destroy_cq = bnxt_re_destroy_cq; + ibdev->poll_cq = bnxt_re_poll_cq; + ibdev->req_notify_cq = bnxt_re_req_notify_cq; + + ibdev->get_dma_mr = bnxt_re_get_dma_mr; + ibdev->dereg_mr = bnxt_re_dereg_mr; + ibdev->alloc_mr = bnxt_re_alloc_mr; + ibdev->map_mr_sg = bnxt_re_map_mr_sg; + ibdev->alloc_fmr = bnxt_re_alloc_fmr; + ibdev->map_phys_fmr = bnxt_re_map_phys_fmr; + ibdev->unmap_fmr = bnxt_re_unmap_fmr; + ibdev->dealloc_fmr = bnxt_re_dealloc_fmr; + + ibdev->reg_user_mr = bnxt_re_reg_user_mr; + ibdev->alloc_ucontext = bnxt_re_alloc_ucontext; + ibdev->dealloc_ucontext = bnxt_re_dealloc_ucontext; + ibdev->mmap = bnxt_re_mmap; + + return ib_register_device(ibdev, NULL); +} + +static ssize_t show_rev(struct device *device, struct device_attribute *attr, + char *buf) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(device, ibdev.dev); + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", rdev->en_dev->pdev->vendor); +} + +static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr, + char *buf) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(device, ibdev.dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", rdev->dev_attr.fw_ver); +} + +static ssize_t show_hca(struct device *device, struct device_attribute *attr, + char *buf) +{ + struct bnxt_re_dev *rdev = to_bnxt_re_dev(device, ibdev.dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", rdev->ibdev.node_desc); +} + +static DEVICE_ATTR(hw_rev, 0444, show_rev, NULL); +static DEVICE_ATTR(fw_rev, 0444, show_fw_ver, NULL); +static DEVICE_ATTR(hca_type, 0444, show_hca, NULL); + +static struct device_attribute *bnxt_re_attributes[] = { + &dev_attr_hw_rev, + &dev_attr_fw_rev, + &dev_attr_hca_type +}; + +static void bnxt_re_dev_remove(struct bnxt_re_dev *rdev) +{ + dev_put(rdev->netdev); + rdev->netdev = NULL; + + mutex_lock(&bnxt_re_dev_lock); + list_del_rcu(&rdev->list); + mutex_unlock(&bnxt_re_dev_lock); + + synchronize_rcu(); + flush_workqueue(bnxt_re_wq); + + ib_dealloc_device(&rdev->ibdev); + /* rdev is gone */ +} + +static struct bnxt_re_dev *bnxt_re_dev_add(struct net_device *netdev, + struct bnxt_en_dev *en_dev) +{ + struct bnxt_re_dev *rdev; + + /* Allocate bnxt_re_dev instance here */ + rdev = (struct bnxt_re_dev *)ib_alloc_device(sizeof(*rdev)); + if (!rdev) { + dev_err(NULL, "%s: bnxt_re_dev allocation failure!", + ROCE_DRV_MODULE_NAME); + return NULL; + } + /* Default values */ + rdev->netdev = netdev; + dev_hold(rdev->netdev); + rdev->en_dev = en_dev; + rdev->id = rdev->en_dev->pdev->devfn; + INIT_LIST_HEAD(&rdev->qp_list); + mutex_init(&rdev->qp_lock); + atomic_set(&rdev->qp_count, 0); + atomic_set(&rdev->cq_count, 0); + atomic_set(&rdev->srq_count, 0); + atomic_set(&rdev->mr_count, 0); + atomic_set(&rdev->mw_count, 0); + rdev->cosq[0] = 0xFFFF; + rdev->cosq[1] = 0xFFFF; + + mutex_lock(&bnxt_re_dev_lock); + list_add_tail_rcu(&rdev->list, &bnxt_re_dev_list); + mutex_unlock(&bnxt_re_dev_lock); + return rdev; +} + +static int bnxt_re_aeq_handler(struct bnxt_qplib_rcfw *rcfw, + struct creq_func_event *aeqe) +{ + switch (aeqe->event) { + case CREQ_FUNC_EVENT_EVENT_TX_WQE_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_TX_DATA_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_RX_WQE_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_RX_DATA_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CQ_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_TQM_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CFCQ_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CFCS_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CFCC_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CFCM_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_TIM_ERROR: + break; + default: + return -EINVAL; + } + return 0; +} + +static int bnxt_re_cqn_handler(struct bnxt_qplib_nq *nq, + struct bnxt_qplib_cq *handle) +{ + struct bnxt_re_cq *cq = container_of(handle, struct bnxt_re_cq, + qplib_cq); + + if (!cq) { + dev_err(NULL, "%s: CQ is NULL, CQN not handled", + ROCE_DRV_MODULE_NAME); + return -EINVAL; + } + if (cq->ib_cq.comp_handler) { + /* Lock comp_handler? */ + (*cq->ib_cq.comp_handler)(&cq->ib_cq, cq->ib_cq.cq_context); + } + + return 0; +} + +static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev) +{ + if (rdev->nq.hwq.max_elements) + bnxt_qplib_disable_nq(&rdev->nq); + + if (rdev->qplib_res.rcfw) + bnxt_qplib_cleanup_res(&rdev->qplib_res); +} + +static int bnxt_re_init_res(struct bnxt_re_dev *rdev) +{ + int rc = 0; + + bnxt_qplib_init_res(&rdev->qplib_res); + + if (rdev->msix_entries[BNXT_RE_NQ_IDX].vector <= 0) + return -EINVAL; + + rc = bnxt_qplib_enable_nq(rdev->en_dev->pdev, &rdev->nq, + rdev->msix_entries[BNXT_RE_NQ_IDX].vector, + rdev->msix_entries[BNXT_RE_NQ_IDX].db_offset, + &bnxt_re_cqn_handler, + NULL); + + if (rc) + dev_err(rdev_to_dev(rdev), "Failed to enable NQ: %#x", rc); + + return rc; +} + +static void bnxt_re_free_res(struct bnxt_re_dev *rdev, bool lock_wait) +{ + if (rdev->nq.hwq.max_elements) { + bnxt_re_net_ring_free(rdev, rdev->nq.ring_id, lock_wait); + bnxt_qplib_free_nq(&rdev->nq); + } + if (rdev->qplib_res.dpi_tbl.max) { + bnxt_qplib_dealloc_dpi(&rdev->qplib_res, + &rdev->qplib_res.dpi_tbl, + &rdev->dpi_privileged); + } + if (rdev->qplib_res.rcfw) { + bnxt_qplib_free_res(&rdev->qplib_res); + rdev->qplib_res.rcfw = NULL; + } +} + +static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev) +{ + int rc = 0; + + /* Configure and allocate resources for qplib */ + rdev->qplib_res.rcfw = &rdev->rcfw; + rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr); + if (rc) + goto fail; + + rc = bnxt_qplib_alloc_res(&rdev->qplib_res, rdev->en_dev->pdev, + rdev->netdev, &rdev->dev_attr); + if (rc) + goto fail; + + rc = bnxt_qplib_alloc_dpi(&rdev->qplib_res.dpi_tbl, + &rdev->dpi_privileged, + rdev); + if (rc) + goto fail; + + rdev->nq.hwq.max_elements = BNXT_RE_MAX_CQ_COUNT + + BNXT_RE_MAX_SRQC_COUNT + 2; + rc = bnxt_qplib_alloc_nq(rdev->en_dev->pdev, &rdev->nq); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to allocate NQ memory: %#x", rc); + goto fail; + } + rc = bnxt_re_net_ring_alloc + (rdev, rdev->nq.hwq.pbl[PBL_LVL_0].pg_map_arr, + rdev->nq.hwq.pbl[rdev->nq.hwq.level].pg_count, + HWRM_RING_ALLOC_CMPL, BNXT_QPLIB_NQE_MAX_CNT - 1, + rdev->msix_entries[BNXT_RE_NQ_IDX].ring_idx, + &rdev->nq.ring_id); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to allocate NQ ring: %#x", rc); + goto free_nq; + } + return 0; +free_nq: + bnxt_qplib_free_nq(&rdev->nq); +fail: + rdev->qplib_res.rcfw = NULL; + return rc; +} + +static void bnxt_re_dispatch_event(struct ib_device *ibdev, struct ib_qp *qp, + u8 port_num, enum ib_event_type event) +{ + struct ib_event ib_event; + + ib_event.device = ibdev; + if (qp) + ib_event.element.qp = qp; + else + ib_event.element.port_num = port_num; + ib_event.event = event; + ib_dispatch_event(&ib_event); +} + +#define HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_IVLAN 0x02 +static int bnxt_re_query_hwrm_pri2cos(struct bnxt_re_dev *rdev, u8 dir, + u64 *cid_map) +{ + struct hwrm_queue_pri2cos_qcfg_input req = {0}; + struct bnxt *bp = netdev_priv(rdev->netdev); + struct hwrm_queue_pri2cos_qcfg_output resp; + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct bnxt_fw_msg fw_msg; + u32 flags = 0; + u8 *qcfgmap, *tmp_map; + int rc = 0, i; + + if (!cid_map) + return -EINVAL; + + memset(&fw_msg, 0, sizeof(fw_msg)); + bnxt_re_init_hwrm_hdr(rdev, (void *)&req, + HWRM_QUEUE_PRI2COS_QCFG, -1, -1); + flags |= (dir & 0x01); + flags |= HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_IVLAN; + req.flags = cpu_to_le32(flags); + req.port_id = bp->pf.port_id; + + bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, + sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); + rc = en_dev->en_ops->bnxt_send_fw_msg(en_dev, BNXT_ROCE_ULP, &fw_msg); + if (rc) + return rc; + + if (resp.queue_cfg_info) { + dev_warn(rdev_to_dev(rdev), + "Asymmetric cos queue configuration detected"); + dev_warn(rdev_to_dev(rdev), + " on device, QoS may not be fully functional\n"); + } + qcfgmap = &resp.pri0_cos_queue_id; + tmp_map = (u8 *)cid_map; + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) + tmp_map[i] = qcfgmap[i]; + + return rc; +} + +static bool bnxt_re_is_qp1_or_shadow_qp(struct bnxt_re_dev *rdev, + struct bnxt_re_qp *qp) +{ + return (qp->ib_qp.qp_type == IB_QPT_GSI) || (qp == rdev->qp1_sqp); +} + +static void bnxt_re_dev_stop(struct bnxt_re_dev *rdev) +{ + int mask = IB_QP_STATE; + struct ib_qp_attr qp_attr; + struct bnxt_re_qp *qp; + + qp_attr.qp_state = IB_QPS_ERR; + mutex_lock(&rdev->qp_lock); + list_for_each_entry(qp, &rdev->qp_list, list) { + /* Modify the state of all QPs except QP1/Shadow QP */ + if (!bnxt_re_is_qp1_or_shadow_qp(rdev, qp)) { + if (qp->qplib_qp.state != + CMDQ_MODIFY_QP_NEW_STATE_RESET && + qp->qplib_qp.state != + CMDQ_MODIFY_QP_NEW_STATE_ERR) { + bnxt_re_dispatch_event(&rdev->ibdev, &qp->ib_qp, + 1, IB_EVENT_QP_FATAL); + bnxt_re_modify_qp(&qp->ib_qp, &qp_attr, mask, + NULL); + } + } + } + mutex_unlock(&rdev->qp_lock); +} + +static u32 bnxt_re_get_priority_mask(struct bnxt_re_dev *rdev) +{ + u32 prio_map = 0, tmp_map = 0; + struct net_device *netdev; + struct dcb_app app; + + netdev = rdev->netdev; + + memset(&app, 0, sizeof(app)); + app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; + app.protocol = ETH_P_IBOE; + tmp_map = dcb_ieee_getapp_mask(netdev, &app); + prio_map = tmp_map; + + app.selector = IEEE_8021QAZ_APP_SEL_DGRAM; + app.protocol = ROCE_V2_UDP_DPORT; + tmp_map = dcb_ieee_getapp_mask(netdev, &app); + prio_map |= tmp_map; + + if (!prio_map) + prio_map = -EFAULT; + return prio_map; +} + +static void bnxt_re_parse_cid_map(u8 prio_map, u8 *cid_map, u16 *cosq) +{ + u16 prio; + u8 id; + + for (prio = 0, id = 0; prio < 8; prio++) { + if (prio_map & (1 << prio)) { + cosq[id] = cid_map[prio]; + id++; + if (id == 2) /* Max 2 tcs supported */ + break; + } + } +} + +static int bnxt_re_setup_qos(struct bnxt_re_dev *rdev) +{ + u8 prio_map = 0; + u64 cid_map; + int rc; + + /* Get priority for roce */ + rc = bnxt_re_get_priority_mask(rdev); + if (rc < 0) + return rc; + prio_map = (u8)rc; + + if (prio_map == rdev->cur_prio_map) + return 0; + rdev->cur_prio_map = prio_map; + /* Get cosq id for this priority */ + rc = bnxt_re_query_hwrm_pri2cos(rdev, 0, &cid_map); + if (rc) { + dev_warn(rdev_to_dev(rdev), "no cos for p_mask %x\n", prio_map); + return rc; + } + /* Parse CoS IDs for app priority */ + bnxt_re_parse_cid_map(prio_map, (u8 *)&cid_map, rdev->cosq); + + /* Config BONO. */ + rc = bnxt_qplib_map_tc2cos(&rdev->qplib_res, rdev->cosq); + if (rc) { + dev_warn(rdev_to_dev(rdev), "no tc for cos{%x, %x}\n", + rdev->cosq[0], rdev->cosq[1]); + return rc; + } + + return 0; +} + +static void bnxt_re_ib_unreg(struct bnxt_re_dev *rdev, bool lock_wait) +{ + int i, rc; + + if (test_and_clear_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags)) { + for (i = 0; i < ARRAY_SIZE(bnxt_re_attributes); i++) + device_remove_file(&rdev->ibdev.dev, + bnxt_re_attributes[i]); + /* Cleanup ib dev */ + bnxt_re_unregister_ib(rdev); + } + if (test_and_clear_bit(BNXT_RE_FLAG_QOS_WORK_REG, &rdev->flags)) + cancel_delayed_work(&rdev->worker); + + bnxt_re_cleanup_res(rdev); + bnxt_re_free_res(rdev, lock_wait); + + if (test_and_clear_bit(BNXT_RE_FLAG_RCFW_CHANNEL_EN, &rdev->flags)) { + rc = bnxt_qplib_deinit_rcfw(&rdev->rcfw); + if (rc) + dev_warn(rdev_to_dev(rdev), + "Failed to deinitialize RCFW: %#x", rc); + bnxt_re_net_stats_ctx_free(rdev, rdev->qplib_ctx.stats.fw_id, + lock_wait); + bnxt_qplib_free_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx); + bnxt_qplib_disable_rcfw_channel(&rdev->rcfw); + bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id, lock_wait); + bnxt_qplib_free_rcfw_channel(&rdev->rcfw); + } + if (test_and_clear_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags)) { + rc = bnxt_re_free_msix(rdev, lock_wait); + if (rc) + dev_warn(rdev_to_dev(rdev), + "Failed to free MSI-X vectors: %#x", rc); + } + if (test_and_clear_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags)) { + rc = bnxt_re_unregister_netdev(rdev, lock_wait); + if (rc) + dev_warn(rdev_to_dev(rdev), + "Failed to unregister with netdev: %#x", rc); + } +} + +static void bnxt_re_set_resource_limits(struct bnxt_re_dev *rdev) +{ + u32 i; + + rdev->qplib_ctx.qpc_count = BNXT_RE_MAX_QPC_COUNT; + rdev->qplib_ctx.mrw_count = BNXT_RE_MAX_MRW_COUNT; + rdev->qplib_ctx.srqc_count = BNXT_RE_MAX_SRQC_COUNT; + rdev->qplib_ctx.cq_count = BNXT_RE_MAX_CQ_COUNT; + for (i = 0; i < MAX_TQM_ALLOC_REQ; i++) + rdev->qplib_ctx.tqm_count[i] = + rdev->dev_attr.tqm_alloc_reqs[i]; +} + +/* worker thread for polling periodic events. Now used for QoS programming*/ +static void bnxt_re_worker(struct work_struct *work) +{ + struct bnxt_re_dev *rdev = container_of(work, struct bnxt_re_dev, + worker.work); + + bnxt_re_setup_qos(rdev); + schedule_delayed_work(&rdev->worker, msecs_to_jiffies(30000)); +} + +static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev) +{ + int i, j, rc; + + /* Registered a new RoCE device instance to netdev */ + rc = bnxt_re_register_netdev(rdev); + if (rc) { + pr_err("Failed to register with netedev: %#x\n", rc); + return -EINVAL; + } + set_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags); + + rc = bnxt_re_request_msix(rdev); + if (rc) { + pr_err("Failed to get MSI-X vectors: %#x\n", rc); + rc = -EINVAL; + goto fail; + } + set_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags); + + /* Establish RCFW Communication Channel to initialize the context + * memory for the function and all child VFs + */ + rc = bnxt_qplib_alloc_rcfw_channel(rdev->en_dev->pdev, &rdev->rcfw); + if (rc) + goto fail; + + rc = bnxt_re_net_ring_alloc + (rdev, rdev->rcfw.creq.pbl[PBL_LVL_0].pg_map_arr, + rdev->rcfw.creq.pbl[rdev->rcfw.creq.level].pg_count, + HWRM_RING_ALLOC_CMPL, BNXT_QPLIB_CREQE_MAX_CNT - 1, + rdev->msix_entries[BNXT_RE_AEQ_IDX].ring_idx, + &rdev->rcfw.creq_ring_id); + if (rc) { + pr_err("Failed to allocate CREQ: %#x\n", rc); + goto free_rcfw; + } + rc = bnxt_qplib_enable_rcfw_channel + (rdev->en_dev->pdev, &rdev->rcfw, + rdev->msix_entries[BNXT_RE_AEQ_IDX].vector, + rdev->msix_entries[BNXT_RE_AEQ_IDX].db_offset, + 0, &bnxt_re_aeq_handler); + if (rc) { + pr_err("Failed to enable RCFW channel: %#x\n", rc); + goto free_ring; + } + + rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr); + if (rc) + goto disable_rcfw; + bnxt_re_set_resource_limits(rdev); + + rc = bnxt_qplib_alloc_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx, 0); + if (rc) { + pr_err("Failed to allocate QPLIB context: %#x\n", rc); + goto disable_rcfw; + } + rc = bnxt_re_net_stats_ctx_alloc(rdev, + rdev->qplib_ctx.stats.dma_map, + &rdev->qplib_ctx.stats.fw_id); + if (rc) { + pr_err("Failed to allocate stats context: %#x\n", rc); + goto free_ctx; + } + + rc = bnxt_qplib_init_rcfw(&rdev->rcfw, &rdev->qplib_ctx, 0); + if (rc) { + pr_err("Failed to initialize RCFW: %#x\n", rc); + goto free_sctx; + } + set_bit(BNXT_RE_FLAG_RCFW_CHANNEL_EN, &rdev->flags); + + /* Resources based on the 'new' device caps */ + rc = bnxt_re_alloc_res(rdev); + if (rc) { + pr_err("Failed to allocate resources: %#x\n", rc); + goto fail; + } + rc = bnxt_re_init_res(rdev); + if (rc) { + pr_err("Failed to initialize resources: %#x\n", rc); + goto fail; + } + + rc = bnxt_re_setup_qos(rdev); + if (rc) + pr_info("RoCE priority not yet configured\n"); + + INIT_DELAYED_WORK(&rdev->worker, bnxt_re_worker); + set_bit(BNXT_RE_FLAG_QOS_WORK_REG, &rdev->flags); + schedule_delayed_work(&rdev->worker, msecs_to_jiffies(30000)); + + /* Register ib dev */ + rc = bnxt_re_register_ib(rdev); + if (rc) { + pr_err("Failed to register with IB: %#x\n", rc); + goto fail; + } + dev_info(rdev_to_dev(rdev), "Device registered successfully"); + for (i = 0; i < ARRAY_SIZE(bnxt_re_attributes); i++) { + rc = device_create_file(&rdev->ibdev.dev, + bnxt_re_attributes[i]); + if (rc) { + dev_err(rdev_to_dev(rdev), + "Failed to create IB sysfs: %#x", rc); + /* Must clean up all created device files */ + for (j = 0; j < i; j++) + device_remove_file(&rdev->ibdev.dev, + bnxt_re_attributes[j]); + bnxt_re_unregister_ib(rdev); + goto fail; + } + } + set_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags); + bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1, IB_EVENT_PORT_ACTIVE); + bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1, IB_EVENT_GID_CHANGE); + + return 0; +free_sctx: + bnxt_re_net_stats_ctx_free(rdev, rdev->qplib_ctx.stats.fw_id, true); +free_ctx: + bnxt_qplib_free_ctx(rdev->en_dev->pdev, &rdev->qplib_ctx); +disable_rcfw: + bnxt_qplib_disable_rcfw_channel(&rdev->rcfw); +free_ring: + bnxt_re_net_ring_free(rdev, rdev->rcfw.creq_ring_id, true); +free_rcfw: + bnxt_qplib_free_rcfw_channel(&rdev->rcfw); +fail: + bnxt_re_ib_unreg(rdev, true); + return rc; +} + +static void bnxt_re_dev_unreg(struct bnxt_re_dev *rdev) +{ + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct net_device *netdev = rdev->netdev; + + bnxt_re_dev_remove(rdev); + + if (netdev) + bnxt_re_dev_unprobe(netdev, en_dev); +} + +static int bnxt_re_dev_reg(struct bnxt_re_dev **rdev, struct net_device *netdev) +{ + struct bnxt_en_dev *en_dev; + int rc = 0; + + if (!is_bnxt_re_dev(netdev)) + return -ENODEV; + + en_dev = bnxt_re_dev_probe(netdev); + if (IS_ERR(en_dev)) { + if (en_dev != ERR_PTR(-ENODEV)) + pr_err("%s: Failed to probe\n", ROCE_DRV_MODULE_NAME); + rc = PTR_ERR(en_dev); + goto exit; + } + *rdev = bnxt_re_dev_add(netdev, en_dev); + if (!*rdev) { + rc = -ENOMEM; + bnxt_re_dev_unprobe(netdev, en_dev); + goto exit; + } +exit: + return rc; +} + +static void bnxt_re_remove_one(struct bnxt_re_dev *rdev) +{ + pci_dev_put(rdev->en_dev->pdev); +} + +/* Handle all deferred netevents tasks */ +static void bnxt_re_task(struct work_struct *work) +{ + struct bnxt_re_work *re_work; + struct bnxt_re_dev *rdev; + int rc = 0; + + re_work = container_of(work, struct bnxt_re_work, work); + rdev = re_work->rdev; + + if (re_work->event != NETDEV_REGISTER && + !test_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags)) + return; + + switch (re_work->event) { + case NETDEV_REGISTER: + rc = bnxt_re_ib_reg(rdev); + if (rc) + dev_err(rdev_to_dev(rdev), + "Failed to register with IB: %#x", rc); + break; + case NETDEV_UP: + bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1, + IB_EVENT_PORT_ACTIVE); + break; + case NETDEV_DOWN: + bnxt_re_dev_stop(rdev); + break; + case NETDEV_CHANGE: + if (!netif_carrier_ok(rdev->netdev)) + bnxt_re_dev_stop(rdev); + else if (netif_carrier_ok(rdev->netdev)) + bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1, + IB_EVENT_PORT_ACTIVE); + break; + default: + break; + } + kfree(re_work); +} + +static void bnxt_re_init_one(struct bnxt_re_dev *rdev) +{ + pci_dev_get(rdev->en_dev->pdev); +} + +/* + * "Notifier chain callback can be invoked for the same chain from + * different CPUs at the same time". + * + * For cases when the netdev is already present, our call to the + * register_netdevice_notifier() will actually get the rtnl_lock() + * before sending NETDEV_REGISTER and (if up) NETDEV_UP + * events. + * + * But for cases when the netdev is not already present, the notifier + * chain is subjected to be invoked from different CPUs simultaneously. + * + * This is protected by the netdev_mutex. + */ +static int bnxt_re_netdev_event(struct notifier_block *notifier, + unsigned long event, void *ptr) +{ + struct net_device *real_dev, *netdev = netdev_notifier_info_to_dev(ptr); + struct bnxt_re_work *re_work; + struct bnxt_re_dev *rdev; + int rc = 0; + bool sch_work = false; + + real_dev = rdma_vlan_dev_real_dev(netdev); + if (!real_dev) + real_dev = netdev; + + rdev = bnxt_re_from_netdev(real_dev); + if (!rdev && event != NETDEV_REGISTER) + goto exit; + if (real_dev != netdev) + goto exit; + + switch (event) { + case NETDEV_REGISTER: + if (rdev) + break; + rc = bnxt_re_dev_reg(&rdev, real_dev); + if (rc == -ENODEV) + break; + if (rc) { + pr_err("Failed to register with the device %s: %#x\n", + real_dev->name, rc); + break; + } + bnxt_re_init_one(rdev); + sch_work = true; + break; + + case NETDEV_UNREGISTER: + bnxt_re_ib_unreg(rdev, false); + bnxt_re_remove_one(rdev); + bnxt_re_dev_unreg(rdev); + break; + + default: + sch_work = true; + break; + } + if (sch_work) { + /* Allocate for the deferred task */ + re_work = kzalloc(sizeof(*re_work), GFP_ATOMIC); + if (re_work) { + re_work->rdev = rdev; + re_work->event = event; + re_work->vlan_dev = (real_dev == netdev ? + NULL : netdev); + INIT_WORK(&re_work->work, bnxt_re_task); + queue_work(bnxt_re_wq, &re_work->work); + } + } + +exit: + return NOTIFY_DONE; +} + +static struct notifier_block bnxt_re_netdev_notifier = { + .notifier_call = bnxt_re_netdev_event +}; + +static int __init bnxt_re_mod_init(void) +{ + int rc = 0; + + pr_info("%s: %s", ROCE_DRV_MODULE_NAME, version); + + bnxt_re_wq = create_singlethread_workqueue("bnxt_re"); + if (!bnxt_re_wq) + return -ENOMEM; + + INIT_LIST_HEAD(&bnxt_re_dev_list); + + rc = register_netdevice_notifier(&bnxt_re_netdev_notifier); + if (rc) { + pr_err("%s: Cannot register to netdevice_notifier", + ROCE_DRV_MODULE_NAME); + goto err_netdev; + } + return 0; + +err_netdev: + destroy_workqueue(bnxt_re_wq); + + return rc; +} + +static void __exit bnxt_re_mod_exit(void) +{ + unregister_netdevice_notifier(&bnxt_re_netdev_notifier); + if (bnxt_re_wq) + destroy_workqueue(bnxt_re_wq); +} + +module_init(bnxt_re_mod_init); +module_exit(bnxt_re_mod_exit); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c new file mode 100644 index 000000000000..43d08b5e9085 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -0,0 +1,2167 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: Fast Path Operators + */ + +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/prefetch.h> + +#include "roce_hsi.h" + +#include "qplib_res.h" +#include "qplib_rcfw.h" +#include "qplib_sp.h" +#include "qplib_fp.h" + +static void bnxt_qplib_arm_cq_enable(struct bnxt_qplib_cq *cq); + +static void bnxt_qplib_free_qp_hdr_buf(struct bnxt_qplib_res *res, + struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_q *rq = &qp->rq; + struct bnxt_qplib_q *sq = &qp->sq; + + if (qp->rq_hdr_buf) + dma_free_coherent(&res->pdev->dev, + rq->hwq.max_elements * qp->rq_hdr_buf_size, + qp->rq_hdr_buf, qp->rq_hdr_buf_map); + if (qp->sq_hdr_buf) + dma_free_coherent(&res->pdev->dev, + sq->hwq.max_elements * qp->sq_hdr_buf_size, + qp->sq_hdr_buf, qp->sq_hdr_buf_map); + qp->rq_hdr_buf = NULL; + qp->sq_hdr_buf = NULL; + qp->rq_hdr_buf_map = 0; + qp->sq_hdr_buf_map = 0; + qp->sq_hdr_buf_size = 0; + qp->rq_hdr_buf_size = 0; +} + +static int bnxt_qplib_alloc_qp_hdr_buf(struct bnxt_qplib_res *res, + struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_q *rq = &qp->rq; + struct bnxt_qplib_q *sq = &qp->rq; + int rc = 0; + + if (qp->sq_hdr_buf_size && sq->hwq.max_elements) { + qp->sq_hdr_buf = dma_alloc_coherent(&res->pdev->dev, + sq->hwq.max_elements * + qp->sq_hdr_buf_size, + &qp->sq_hdr_buf_map, GFP_KERNEL); + if (!qp->sq_hdr_buf) { + rc = -ENOMEM; + dev_err(&res->pdev->dev, + "QPLIB: Failed to create sq_hdr_buf"); + goto fail; + } + } + + if (qp->rq_hdr_buf_size && rq->hwq.max_elements) { + qp->rq_hdr_buf = dma_alloc_coherent(&res->pdev->dev, + rq->hwq.max_elements * + qp->rq_hdr_buf_size, + &qp->rq_hdr_buf_map, + GFP_KERNEL); + if (!qp->rq_hdr_buf) { + rc = -ENOMEM; + dev_err(&res->pdev->dev, + "QPLIB: Failed to create rq_hdr_buf"); + goto fail; + } + } + return 0; + +fail: + bnxt_qplib_free_qp_hdr_buf(res, qp); + return rc; +} + +static void bnxt_qplib_service_nq(unsigned long data) +{ + struct bnxt_qplib_nq *nq = (struct bnxt_qplib_nq *)data; + struct bnxt_qplib_hwq *hwq = &nq->hwq; + struct nq_base *nqe, **nq_ptr; + int num_cqne_processed = 0; + u32 sw_cons, raw_cons; + u16 type; + int budget = nq->budget; + u64 q_handle; + + /* Service the NQ until empty */ + raw_cons = hwq->cons; + while (budget--) { + sw_cons = HWQ_CMP(raw_cons, hwq); + nq_ptr = (struct nq_base **)hwq->pbl_ptr; + nqe = &nq_ptr[NQE_PG(sw_cons)][NQE_IDX(sw_cons)]; + if (!NQE_CMP_VALID(nqe, raw_cons, hwq->max_elements)) + break; + + type = le16_to_cpu(nqe->info10_type) & NQ_BASE_TYPE_MASK; + switch (type) { + case NQ_BASE_TYPE_CQ_NOTIFICATION: + { + struct nq_cn *nqcne = (struct nq_cn *)nqe; + + q_handle = le32_to_cpu(nqcne->cq_handle_low); + q_handle |= (u64)le32_to_cpu(nqcne->cq_handle_high) + << 32; + bnxt_qplib_arm_cq_enable((struct bnxt_qplib_cq *) + ((unsigned long)q_handle)); + if (!nq->cqn_handler(nq, (struct bnxt_qplib_cq *) + ((unsigned long)q_handle))) + num_cqne_processed++; + else + dev_warn(&nq->pdev->dev, + "QPLIB: cqn - type 0x%x not handled", + type); + break; + } + case NQ_BASE_TYPE_DBQ_EVENT: + break; + default: + dev_warn(&nq->pdev->dev, + "QPLIB: nqe with type = 0x%x not handled", + type); + break; + } + raw_cons++; + } + if (hwq->cons != raw_cons) { + hwq->cons = raw_cons; + NQ_DB_REARM(nq->bar_reg_iomem, hwq->cons, hwq->max_elements); + } +} + +static irqreturn_t bnxt_qplib_nq_irq(int irq, void *dev_instance) +{ + struct bnxt_qplib_nq *nq = dev_instance; + struct bnxt_qplib_hwq *hwq = &nq->hwq; + struct nq_base **nq_ptr; + u32 sw_cons; + + /* Prefetch the NQ element */ + sw_cons = HWQ_CMP(hwq->cons, hwq); + nq_ptr = (struct nq_base **)nq->hwq.pbl_ptr; + prefetch(&nq_ptr[NQE_PG(sw_cons)][NQE_IDX(sw_cons)]); + + /* Fan out to CPU affinitized kthreads? */ + tasklet_schedule(&nq->worker); + + return IRQ_HANDLED; +} + +void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq) +{ + /* Make sure the HW is stopped! */ + synchronize_irq(nq->vector); + tasklet_disable(&nq->worker); + tasklet_kill(&nq->worker); + + if (nq->requested) { + free_irq(nq->vector, nq); + nq->requested = false; + } + if (nq->bar_reg_iomem) + iounmap(nq->bar_reg_iomem); + nq->bar_reg_iomem = NULL; + + nq->cqn_handler = NULL; + nq->srqn_handler = NULL; + nq->vector = 0; +} + +int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, + int msix_vector, int bar_reg_offset, + int (*cqn_handler)(struct bnxt_qplib_nq *nq, + struct bnxt_qplib_cq *), + int (*srqn_handler)(struct bnxt_qplib_nq *nq, + void *, u8 event)) +{ + resource_size_t nq_base; + int rc; + + nq->pdev = pdev; + nq->vector = msix_vector; + + nq->cqn_handler = cqn_handler; + + nq->srqn_handler = srqn_handler; + + tasklet_init(&nq->worker, bnxt_qplib_service_nq, (unsigned long)nq); + + nq->requested = false; + rc = request_irq(nq->vector, bnxt_qplib_nq_irq, 0, "bnxt_qplib_nq", nq); + if (rc) { + dev_err(&nq->pdev->dev, + "Failed to request IRQ for NQ: %#x", rc); + bnxt_qplib_disable_nq(nq); + goto fail; + } + nq->requested = true; + nq->bar_reg = NQ_CONS_PCI_BAR_REGION; + nq->bar_reg_off = bar_reg_offset; + nq_base = pci_resource_start(pdev, nq->bar_reg); + if (!nq_base) { + rc = -ENOMEM; + goto fail; + } + nq->bar_reg_iomem = ioremap_nocache(nq_base + nq->bar_reg_off, 4); + if (!nq->bar_reg_iomem) { + rc = -ENOMEM; + goto fail; + } + NQ_DB_REARM(nq->bar_reg_iomem, nq->hwq.cons, nq->hwq.max_elements); + + return 0; +fail: + bnxt_qplib_disable_nq(nq); + return rc; +} + +void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq) +{ + if (nq->hwq.max_elements) + bnxt_qplib_free_hwq(nq->pdev, &nq->hwq); +} + +int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq) +{ + nq->pdev = pdev; + if (!nq->hwq.max_elements || + nq->hwq.max_elements > BNXT_QPLIB_NQE_MAX_CNT) + nq->hwq.max_elements = BNXT_QPLIB_NQE_MAX_CNT; + + if (bnxt_qplib_alloc_init_hwq(nq->pdev, &nq->hwq, NULL, 0, + &nq->hwq.max_elements, + BNXT_QPLIB_MAX_NQE_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_L2_CMPL)) + return -ENOMEM; + + nq->budget = 8; + return 0; +} + +/* QP */ +int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_create_qp1 req; + struct creq_create_qp1_resp *resp; + struct bnxt_qplib_pbl *pbl; + struct bnxt_qplib_q *sq = &qp->sq; + struct bnxt_qplib_q *rq = &qp->rq; + int rc; + u16 cmd_flags = 0; + u32 qp_flags = 0; + + RCFW_CMD_PREP(req, CREATE_QP1, cmd_flags); + + /* General */ + req.type = qp->type; + req.dpi = cpu_to_le32(qp->dpi->dpi); + req.qp_handle = cpu_to_le64(qp->qp_handle); + + /* SQ */ + sq->hwq.max_elements = sq->max_wqe; + rc = bnxt_qplib_alloc_init_hwq(res->pdev, &sq->hwq, NULL, 0, + &sq->hwq.max_elements, + BNXT_QPLIB_MAX_SQE_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_QUEUE); + if (rc) + goto exit; + + sq->swq = kcalloc(sq->hwq.max_elements, sizeof(*sq->swq), GFP_KERNEL); + if (!sq->swq) { + rc = -ENOMEM; + goto fail_sq; + } + pbl = &sq->hwq.pbl[PBL_LVL_0]; + req.sq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); + req.sq_pg_size_sq_lvl = + ((sq->hwq.level & CMDQ_CREATE_QP1_SQ_LVL_MASK) + << CMDQ_CREATE_QP1_SQ_LVL_SFT) | + (pbl->pg_size == ROCE_PG_SIZE_4K ? + CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_4K : + pbl->pg_size == ROCE_PG_SIZE_8K ? + CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_8K : + pbl->pg_size == ROCE_PG_SIZE_64K ? + CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_64K : + pbl->pg_size == ROCE_PG_SIZE_2M ? + CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_2M : + pbl->pg_size == ROCE_PG_SIZE_8M ? + CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_8M : + pbl->pg_size == ROCE_PG_SIZE_1G ? + CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_1G : + CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_4K); + + if (qp->scq) + req.scq_cid = cpu_to_le32(qp->scq->id); + + qp_flags |= CMDQ_CREATE_QP1_QP_FLAGS_RESERVED_LKEY_ENABLE; + + /* RQ */ + if (rq->max_wqe) { + rq->hwq.max_elements = qp->rq.max_wqe; + rc = bnxt_qplib_alloc_init_hwq(res->pdev, &rq->hwq, NULL, 0, + &rq->hwq.max_elements, + BNXT_QPLIB_MAX_RQE_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_QUEUE); + if (rc) + goto fail_sq; + + rq->swq = kcalloc(rq->hwq.max_elements, sizeof(*rq->swq), + GFP_KERNEL); + if (!rq->swq) { + rc = -ENOMEM; + goto fail_rq; + } + pbl = &rq->hwq.pbl[PBL_LVL_0]; + req.rq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); + req.rq_pg_size_rq_lvl = + ((rq->hwq.level & CMDQ_CREATE_QP1_RQ_LVL_MASK) << + CMDQ_CREATE_QP1_RQ_LVL_SFT) | + (pbl->pg_size == ROCE_PG_SIZE_4K ? + CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_4K : + pbl->pg_size == ROCE_PG_SIZE_8K ? + CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_8K : + pbl->pg_size == ROCE_PG_SIZE_64K ? + CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_64K : + pbl->pg_size == ROCE_PG_SIZE_2M ? + CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_2M : + pbl->pg_size == ROCE_PG_SIZE_8M ? + CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_8M : + pbl->pg_size == ROCE_PG_SIZE_1G ? + CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_1G : + CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_4K); + if (qp->rcq) + req.rcq_cid = cpu_to_le32(qp->rcq->id); + } + + /* Header buffer - allow hdr_buf pass in */ + rc = bnxt_qplib_alloc_qp_hdr_buf(res, qp); + if (rc) { + rc = -ENOMEM; + goto fail; + } + req.qp_flags = cpu_to_le32(qp_flags); + req.sq_size = cpu_to_le32(sq->hwq.max_elements); + req.rq_size = cpu_to_le32(rq->hwq.max_elements); + + req.sq_fwo_sq_sge = + cpu_to_le16((sq->max_sge & CMDQ_CREATE_QP1_SQ_SGE_MASK) << + CMDQ_CREATE_QP1_SQ_SGE_SFT); + req.rq_fwo_rq_sge = + cpu_to_le16((rq->max_sge & CMDQ_CREATE_QP1_RQ_SGE_MASK) << + CMDQ_CREATE_QP1_RQ_SGE_SFT); + + req.pd_id = cpu_to_le32(qp->pd->id); + + resp = (struct creq_create_qp1_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&res->pdev->dev, "QPLIB: FP: CREATE_QP1 send failed"); + rc = -EINVAL; + goto fail; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_QP1 timed out"); + rc = -ETIMEDOUT; + goto fail; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_QP1 failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + rc = -EINVAL; + goto fail; + } + qp->id = le32_to_cpu(resp->xid); + qp->cur_qp_state = CMDQ_MODIFY_QP_NEW_STATE_RESET; + sq->flush_in_progress = false; + rq->flush_in_progress = false; + + return 0; + +fail: + bnxt_qplib_free_qp_hdr_buf(res, qp); +fail_rq: + bnxt_qplib_free_hwq(res->pdev, &rq->hwq); + kfree(rq->swq); +fail_sq: + bnxt_qplib_free_hwq(res->pdev, &sq->hwq); + kfree(sq->swq); +exit: + return rc; +} + +int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct sq_send *hw_sq_send_hdr, **hw_sq_send_ptr; + struct cmdq_create_qp req; + struct creq_create_qp_resp *resp; + struct bnxt_qplib_pbl *pbl; + struct sq_psn_search **psn_search_ptr; + unsigned long int psn_search, poff = 0; + struct bnxt_qplib_q *sq = &qp->sq; + struct bnxt_qplib_q *rq = &qp->rq; + struct bnxt_qplib_hwq *xrrq; + int i, rc, req_size, psn_sz; + u16 cmd_flags = 0, max_ssge; + u32 sw_prod, qp_flags = 0; + + RCFW_CMD_PREP(req, CREATE_QP, cmd_flags); + + /* General */ + req.type = qp->type; + req.dpi = cpu_to_le32(qp->dpi->dpi); + req.qp_handle = cpu_to_le64(qp->qp_handle); + + /* SQ */ + psn_sz = (qp->type == CMDQ_CREATE_QP_TYPE_RC) ? + sizeof(struct sq_psn_search) : 0; + sq->hwq.max_elements = sq->max_wqe; + rc = bnxt_qplib_alloc_init_hwq(res->pdev, &sq->hwq, sq->sglist, + sq->nmap, &sq->hwq.max_elements, + BNXT_QPLIB_MAX_SQE_ENTRY_SIZE, + psn_sz, + PAGE_SIZE, HWQ_TYPE_QUEUE); + if (rc) + goto exit; + + sq->swq = kcalloc(sq->hwq.max_elements, sizeof(*sq->swq), GFP_KERNEL); + if (!sq->swq) { + rc = -ENOMEM; + goto fail_sq; + } + hw_sq_send_ptr = (struct sq_send **)sq->hwq.pbl_ptr; + if (psn_sz) { + psn_search_ptr = (struct sq_psn_search **) + &hw_sq_send_ptr[get_sqe_pg + (sq->hwq.max_elements)]; + psn_search = (unsigned long int) + &hw_sq_send_ptr[get_sqe_pg(sq->hwq.max_elements)] + [get_sqe_idx(sq->hwq.max_elements)]; + if (psn_search & ~PAGE_MASK) { + /* If the psn_search does not start on a page boundary, + * then calculate the offset + */ + poff = (psn_search & ~PAGE_MASK) / + BNXT_QPLIB_MAX_PSNE_ENTRY_SIZE; + } + for (i = 0; i < sq->hwq.max_elements; i++) + sq->swq[i].psn_search = + &psn_search_ptr[get_psne_pg(i + poff)] + [get_psne_idx(i + poff)]; + } + pbl = &sq->hwq.pbl[PBL_LVL_0]; + req.sq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); + req.sq_pg_size_sq_lvl = + ((sq->hwq.level & CMDQ_CREATE_QP_SQ_LVL_MASK) + << CMDQ_CREATE_QP_SQ_LVL_SFT) | + (pbl->pg_size == ROCE_PG_SIZE_4K ? + CMDQ_CREATE_QP_SQ_PG_SIZE_PG_4K : + pbl->pg_size == ROCE_PG_SIZE_8K ? + CMDQ_CREATE_QP_SQ_PG_SIZE_PG_8K : + pbl->pg_size == ROCE_PG_SIZE_64K ? + CMDQ_CREATE_QP_SQ_PG_SIZE_PG_64K : + pbl->pg_size == ROCE_PG_SIZE_2M ? + CMDQ_CREATE_QP_SQ_PG_SIZE_PG_2M : + pbl->pg_size == ROCE_PG_SIZE_8M ? + CMDQ_CREATE_QP_SQ_PG_SIZE_PG_8M : + pbl->pg_size == ROCE_PG_SIZE_1G ? + CMDQ_CREATE_QP_SQ_PG_SIZE_PG_1G : + CMDQ_CREATE_QP_SQ_PG_SIZE_PG_4K); + + /* initialize all SQ WQEs to LOCAL_INVALID (sq prep for hw fetch) */ + hw_sq_send_ptr = (struct sq_send **)sq->hwq.pbl_ptr; + for (sw_prod = 0; sw_prod < sq->hwq.max_elements; sw_prod++) { + hw_sq_send_hdr = &hw_sq_send_ptr[get_sqe_pg(sw_prod)] + [get_sqe_idx(sw_prod)]; + hw_sq_send_hdr->wqe_type = SQ_BASE_WQE_TYPE_LOCAL_INVALID; + } + + if (qp->scq) + req.scq_cid = cpu_to_le32(qp->scq->id); + + qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_RESERVED_LKEY_ENABLE; + qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_FR_PMR_ENABLED; + if (qp->sig_type) + qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_FORCE_COMPLETION; + + /* RQ */ + if (rq->max_wqe) { + rq->hwq.max_elements = rq->max_wqe; + rc = bnxt_qplib_alloc_init_hwq(res->pdev, &rq->hwq, rq->sglist, + rq->nmap, &rq->hwq.max_elements, + BNXT_QPLIB_MAX_RQE_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_QUEUE); + if (rc) + goto fail_sq; + + rq->swq = kcalloc(rq->hwq.max_elements, sizeof(*rq->swq), + GFP_KERNEL); + if (!rq->swq) { + rc = -ENOMEM; + goto fail_rq; + } + pbl = &rq->hwq.pbl[PBL_LVL_0]; + req.rq_pbl = cpu_to_le64(pbl->pg_map_arr[0]); + req.rq_pg_size_rq_lvl = + ((rq->hwq.level & CMDQ_CREATE_QP_RQ_LVL_MASK) << + CMDQ_CREATE_QP_RQ_LVL_SFT) | + (pbl->pg_size == ROCE_PG_SIZE_4K ? + CMDQ_CREATE_QP_RQ_PG_SIZE_PG_4K : + pbl->pg_size == ROCE_PG_SIZE_8K ? + CMDQ_CREATE_QP_RQ_PG_SIZE_PG_8K : + pbl->pg_size == ROCE_PG_SIZE_64K ? + CMDQ_CREATE_QP_RQ_PG_SIZE_PG_64K : + pbl->pg_size == ROCE_PG_SIZE_2M ? + CMDQ_CREATE_QP_RQ_PG_SIZE_PG_2M : + pbl->pg_size == ROCE_PG_SIZE_8M ? + CMDQ_CREATE_QP_RQ_PG_SIZE_PG_8M : + pbl->pg_size == ROCE_PG_SIZE_1G ? + CMDQ_CREATE_QP_RQ_PG_SIZE_PG_1G : + CMDQ_CREATE_QP_RQ_PG_SIZE_PG_4K); + } + + if (qp->rcq) + req.rcq_cid = cpu_to_le32(qp->rcq->id); + req.qp_flags = cpu_to_le32(qp_flags); + req.sq_size = cpu_to_le32(sq->hwq.max_elements); + req.rq_size = cpu_to_le32(rq->hwq.max_elements); + qp->sq_hdr_buf = NULL; + qp->rq_hdr_buf = NULL; + + rc = bnxt_qplib_alloc_qp_hdr_buf(res, qp); + if (rc) + goto fail_rq; + + /* CTRL-22434: Irrespective of the requested SGE count on the SQ + * always create the QP with max send sges possible if the requested + * inline size is greater than 0. + */ + max_ssge = qp->max_inline_data ? 6 : sq->max_sge; + req.sq_fwo_sq_sge = cpu_to_le16( + ((max_ssge & CMDQ_CREATE_QP_SQ_SGE_MASK) + << CMDQ_CREATE_QP_SQ_SGE_SFT) | 0); + req.rq_fwo_rq_sge = cpu_to_le16( + ((rq->max_sge & CMDQ_CREATE_QP_RQ_SGE_MASK) + << CMDQ_CREATE_QP_RQ_SGE_SFT) | 0); + /* ORRQ and IRRQ */ + if (psn_sz) { + xrrq = &qp->orrq; + xrrq->max_elements = + ORD_LIMIT_TO_ORRQ_SLOTS(qp->max_rd_atomic); + req_size = xrrq->max_elements * + BNXT_QPLIB_MAX_ORRQE_ENTRY_SIZE + PAGE_SIZE - 1; + req_size &= ~(PAGE_SIZE - 1); + rc = bnxt_qplib_alloc_init_hwq(res->pdev, xrrq, NULL, 0, + &xrrq->max_elements, + BNXT_QPLIB_MAX_ORRQE_ENTRY_SIZE, + 0, req_size, HWQ_TYPE_CTX); + if (rc) + goto fail_buf_free; + pbl = &xrrq->pbl[PBL_LVL_0]; + req.orrq_addr = cpu_to_le64(pbl->pg_map_arr[0]); + + xrrq = &qp->irrq; + xrrq->max_elements = IRD_LIMIT_TO_IRRQ_SLOTS( + qp->max_dest_rd_atomic); + req_size = xrrq->max_elements * + BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE + PAGE_SIZE - 1; + req_size &= ~(PAGE_SIZE - 1); + + rc = bnxt_qplib_alloc_init_hwq(res->pdev, xrrq, NULL, 0, + &xrrq->max_elements, + BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE, + 0, req_size, HWQ_TYPE_CTX); + if (rc) + goto fail_orrq; + + pbl = &xrrq->pbl[PBL_LVL_0]; + req.irrq_addr = cpu_to_le64(pbl->pg_map_arr[0]); + } + req.pd_id = cpu_to_le32(qp->pd->id); + + resp = (struct creq_create_qp_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_QP send failed"); + rc = -EINVAL; + goto fail; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_QP timed out"); + rc = -ETIMEDOUT; + goto fail; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_QP failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + rc = -EINVAL; + goto fail; + } + qp->id = le32_to_cpu(resp->xid); + qp->cur_qp_state = CMDQ_MODIFY_QP_NEW_STATE_RESET; + sq->flush_in_progress = false; + rq->flush_in_progress = false; + + return 0; + +fail: + if (qp->irrq.max_elements) + bnxt_qplib_free_hwq(res->pdev, &qp->irrq); +fail_orrq: + if (qp->orrq.max_elements) + bnxt_qplib_free_hwq(res->pdev, &qp->orrq); +fail_buf_free: + bnxt_qplib_free_qp_hdr_buf(res, qp); +fail_rq: + bnxt_qplib_free_hwq(res->pdev, &rq->hwq); + kfree(rq->swq); +fail_sq: + bnxt_qplib_free_hwq(res->pdev, &sq->hwq); + kfree(sq->swq); +exit: + return rc; +} + +static void __modify_flags_from_init_state(struct bnxt_qplib_qp *qp) +{ + switch (qp->state) { + case CMDQ_MODIFY_QP_NEW_STATE_RTR: + /* INIT->RTR, configure the path_mtu to the default + * 2048 if not being requested + */ + if (!(qp->modify_flags & + CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU)) { + qp->modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU; + qp->path_mtu = + CMDQ_MODIFY_QP_PATH_MTU_MTU_2048; + } + qp->modify_flags &= + ~CMDQ_MODIFY_QP_MODIFY_MASK_VLAN_ID; + /* Bono FW require the max_dest_rd_atomic to be >= 1 */ + if (qp->max_dest_rd_atomic < 1) + qp->max_dest_rd_atomic = 1; + qp->modify_flags &= ~CMDQ_MODIFY_QP_MODIFY_MASK_SRC_MAC; + /* Bono FW 20.6.5 requires SGID_INDEX configuration */ + if (!(qp->modify_flags & + CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX)) { + qp->modify_flags |= + CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX; + qp->ah.sgid_index = 0; + } + break; + default: + break; + } +} + +static void __modify_flags_from_rtr_state(struct bnxt_qplib_qp *qp) +{ + switch (qp->state) { + case CMDQ_MODIFY_QP_NEW_STATE_RTS: + /* Bono FW requires the max_rd_atomic to be >= 1 */ + if (qp->max_rd_atomic < 1) + qp->max_rd_atomic = 1; + /* Bono FW does not allow PKEY_INDEX, + * DGID, FLOW_LABEL, SGID_INDEX, HOP_LIMIT, + * TRAFFIC_CLASS, DEST_MAC, PATH_MTU, RQ_PSN, + * MIN_RNR_TIMER, MAX_DEST_RD_ATOMIC, DEST_QP_ID + * modification + */ + qp->modify_flags &= + ~(CMDQ_MODIFY_QP_MODIFY_MASK_PKEY | + CMDQ_MODIFY_QP_MODIFY_MASK_DGID | + CMDQ_MODIFY_QP_MODIFY_MASK_FLOW_LABEL | + CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX | + CMDQ_MODIFY_QP_MODIFY_MASK_HOP_LIMIT | + CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS | + CMDQ_MODIFY_QP_MODIFY_MASK_DEST_MAC | + CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU | + CMDQ_MODIFY_QP_MODIFY_MASK_RQ_PSN | + CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER | + CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC | + CMDQ_MODIFY_QP_MODIFY_MASK_DEST_QP_ID); + break; + default: + break; + } +} + +static void __filter_modify_flags(struct bnxt_qplib_qp *qp) +{ + switch (qp->cur_qp_state) { + case CMDQ_MODIFY_QP_NEW_STATE_RESET: + break; + case CMDQ_MODIFY_QP_NEW_STATE_INIT: + __modify_flags_from_init_state(qp); + break; + case CMDQ_MODIFY_QP_NEW_STATE_RTR: + __modify_flags_from_rtr_state(qp); + break; + case CMDQ_MODIFY_QP_NEW_STATE_RTS: + break; + case CMDQ_MODIFY_QP_NEW_STATE_SQD: + break; + case CMDQ_MODIFY_QP_NEW_STATE_SQE: + break; + case CMDQ_MODIFY_QP_NEW_STATE_ERR: + break; + default: + break; + } +} + +int bnxt_qplib_modify_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_modify_qp req; + struct creq_modify_qp_resp *resp; + u16 cmd_flags = 0, pkey; + u32 temp32[4]; + u32 bmask; + + RCFW_CMD_PREP(req, MODIFY_QP, cmd_flags); + + /* Filter out the qp_attr_mask based on the state->new transition */ + __filter_modify_flags(qp); + bmask = qp->modify_flags; + req.modify_mask = cpu_to_le32(qp->modify_flags); + req.qp_cid = cpu_to_le32(qp->id); + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_STATE) { + req.network_type_en_sqd_async_notify_new_state = + (qp->state & CMDQ_MODIFY_QP_NEW_STATE_MASK) | + (qp->en_sqd_async_notify ? + CMDQ_MODIFY_QP_EN_SQD_ASYNC_NOTIFY : 0); + } + req.network_type_en_sqd_async_notify_new_state |= qp->nw_type; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_ACCESS) + req.access = qp->access; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_PKEY) { + if (!bnxt_qplib_get_pkey(res, &res->pkey_tbl, + qp->pkey_index, &pkey)) + req.pkey = cpu_to_le16(pkey); + } + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_QKEY) + req.qkey = cpu_to_le32(qp->qkey); + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_DGID) { + memcpy(temp32, qp->ah.dgid.data, sizeof(struct bnxt_qplib_gid)); + req.dgid[0] = cpu_to_le32(temp32[0]); + req.dgid[1] = cpu_to_le32(temp32[1]); + req.dgid[2] = cpu_to_le32(temp32[2]); + req.dgid[3] = cpu_to_le32(temp32[3]); + } + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_FLOW_LABEL) + req.flow_label = cpu_to_le32(qp->ah.flow_label); + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX) + req.sgid_index = cpu_to_le16(res->sgid_tbl.hw_id + [qp->ah.sgid_index]); + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_HOP_LIMIT) + req.hop_limit = qp->ah.hop_limit; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS) + req.traffic_class = qp->ah.traffic_class; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_DEST_MAC) + memcpy(req.dest_mac, qp->ah.dmac, 6); + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU) + req.path_mtu = qp->path_mtu; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_TIMEOUT) + req.timeout = qp->timeout; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_RETRY_CNT) + req.retry_cnt = qp->retry_cnt; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_RNR_RETRY) + req.rnr_retry = qp->rnr_retry; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER) + req.min_rnr_timer = qp->min_rnr_timer; + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_RQ_PSN) + req.rq_psn = cpu_to_le32(qp->rq.psn); + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_SQ_PSN) + req.sq_psn = cpu_to_le32(qp->sq.psn); + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_MAX_RD_ATOMIC) + req.max_rd_atomic = + ORD_LIMIT_TO_ORRQ_SLOTS(qp->max_rd_atomic); + + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC) + req.max_dest_rd_atomic = + IRD_LIMIT_TO_IRRQ_SLOTS(qp->max_dest_rd_atomic); + + req.sq_size = cpu_to_le32(qp->sq.hwq.max_elements); + req.rq_size = cpu_to_le32(qp->rq.hwq.max_elements); + req.sq_sge = cpu_to_le16(qp->sq.max_sge); + req.rq_sge = cpu_to_le16(qp->rq.max_sge); + req.max_inline_data = cpu_to_le32(qp->max_inline_data); + if (bmask & CMDQ_MODIFY_QP_MODIFY_MASK_DEST_QP_ID) + req.dest_qp_id = cpu_to_le32(qp->dest_qpn); + + req.vlan_pcp_vlan_dei_vlan_id = cpu_to_le16(qp->vlan_id); + + resp = (struct creq_modify_qp_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: MODIFY_QP send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: FP: MODIFY_QP timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: MODIFY_QP failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + qp->cur_qp_state = qp->state; + return 0; +} + +int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_query_qp req; + struct creq_query_qp_resp *resp; + struct creq_query_qp_resp_sb *sb; + u16 cmd_flags = 0; + u32 temp32[4]; + int i; + + RCFW_CMD_PREP(req, QUERY_QP, cmd_flags); + + req.qp_cid = cpu_to_le32(qp->id); + req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; + resp = (struct creq_query_qp_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + (void **)&sb, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: QUERY_QP send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: FP: QUERY_QP timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: QUERY_QP failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + /* Extract the context from the side buffer */ + qp->state = sb->en_sqd_async_notify_state & + CREQ_QUERY_QP_RESP_SB_STATE_MASK; + qp->en_sqd_async_notify = sb->en_sqd_async_notify_state & + CREQ_QUERY_QP_RESP_SB_EN_SQD_ASYNC_NOTIFY ? + true : false; + qp->access = sb->access; + qp->pkey_index = le16_to_cpu(sb->pkey); + qp->qkey = le32_to_cpu(sb->qkey); + + temp32[0] = le32_to_cpu(sb->dgid[0]); + temp32[1] = le32_to_cpu(sb->dgid[1]); + temp32[2] = le32_to_cpu(sb->dgid[2]); + temp32[3] = le32_to_cpu(sb->dgid[3]); + memcpy(qp->ah.dgid.data, temp32, sizeof(qp->ah.dgid.data)); + + qp->ah.flow_label = le32_to_cpu(sb->flow_label); + + qp->ah.sgid_index = 0; + for (i = 0; i < res->sgid_tbl.max; i++) { + if (res->sgid_tbl.hw_id[i] == le16_to_cpu(sb->sgid_index)) { + qp->ah.sgid_index = i; + break; + } + } + if (i == res->sgid_tbl.max) + dev_warn(&res->pdev->dev, "QPLIB: SGID not found??"); + + qp->ah.hop_limit = sb->hop_limit; + qp->ah.traffic_class = sb->traffic_class; + memcpy(qp->ah.dmac, sb->dest_mac, 6); + qp->ah.vlan_id = (le16_to_cpu(sb->path_mtu_dest_vlan_id) & + CREQ_QUERY_QP_RESP_SB_VLAN_ID_MASK) >> + CREQ_QUERY_QP_RESP_SB_VLAN_ID_SFT; + qp->path_mtu = (le16_to_cpu(sb->path_mtu_dest_vlan_id) & + CREQ_QUERY_QP_RESP_SB_PATH_MTU_MASK) >> + CREQ_QUERY_QP_RESP_SB_PATH_MTU_SFT; + qp->timeout = sb->timeout; + qp->retry_cnt = sb->retry_cnt; + qp->rnr_retry = sb->rnr_retry; + qp->min_rnr_timer = sb->min_rnr_timer; + qp->rq.psn = le32_to_cpu(sb->rq_psn); + qp->max_rd_atomic = ORRQ_SLOTS_TO_ORD_LIMIT(sb->max_rd_atomic); + qp->sq.psn = le32_to_cpu(sb->sq_psn); + qp->max_dest_rd_atomic = + IRRQ_SLOTS_TO_IRD_LIMIT(sb->max_dest_rd_atomic); + qp->sq.max_wqe = qp->sq.hwq.max_elements; + qp->rq.max_wqe = qp->rq.hwq.max_elements; + qp->sq.max_sge = le16_to_cpu(sb->sq_sge); + qp->rq.max_sge = le16_to_cpu(sb->rq_sge); + qp->max_inline_data = le32_to_cpu(sb->max_inline_data); + qp->dest_qpn = le32_to_cpu(sb->dest_qp_id); + memcpy(qp->smac, sb->src_mac, 6); + qp->vlan_id = le16_to_cpu(sb->vlan_pcp_vlan_dei_vlan_id); + return 0; +} + +static void __clean_cq(struct bnxt_qplib_cq *cq, u64 qp) +{ + struct bnxt_qplib_hwq *cq_hwq = &cq->hwq; + struct cq_base *hw_cqe, **hw_cqe_ptr; + int i; + + for (i = 0; i < cq_hwq->max_elements; i++) { + hw_cqe_ptr = (struct cq_base **)cq_hwq->pbl_ptr; + hw_cqe = &hw_cqe_ptr[CQE_PG(i)][CQE_IDX(i)]; + if (!CQE_CMP_VALID(hw_cqe, i, cq_hwq->max_elements)) + continue; + switch (hw_cqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK) { + case CQ_BASE_CQE_TYPE_REQ: + case CQ_BASE_CQE_TYPE_TERMINAL: + { + struct cq_req *cqe = (struct cq_req *)hw_cqe; + + if (qp == le64_to_cpu(cqe->qp_handle)) + cqe->qp_handle = 0; + break; + } + case CQ_BASE_CQE_TYPE_RES_RC: + case CQ_BASE_CQE_TYPE_RES_UD: + case CQ_BASE_CQE_TYPE_RES_RAWETH_QP1: + { + struct cq_res_rc *cqe = (struct cq_res_rc *)hw_cqe; + + if (qp == le64_to_cpu(cqe->qp_handle)) + cqe->qp_handle = 0; + break; + } + default: + break; + } + } +} + +int bnxt_qplib_destroy_qp(struct bnxt_qplib_res *res, + struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_destroy_qp req; + struct creq_destroy_qp_resp *resp; + unsigned long flags; + u16 cmd_flags = 0; + + RCFW_CMD_PREP(req, DESTROY_QP, cmd_flags); + + req.qp_cid = cpu_to_le32(qp->id); + resp = (struct creq_destroy_qp_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: DESTROY_QP send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: FP: DESTROY_QP timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: DESTROY_QP failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + + /* Must walk the associated CQs to nullified the QP ptr */ + spin_lock_irqsave(&qp->scq->hwq.lock, flags); + + __clean_cq(qp->scq, (u64)(unsigned long)qp); + + if (qp->rcq && qp->rcq != qp->scq) { + spin_lock(&qp->rcq->hwq.lock); + __clean_cq(qp->rcq, (u64)(unsigned long)qp); + spin_unlock(&qp->rcq->hwq.lock); + } + + spin_unlock_irqrestore(&qp->scq->hwq.lock, flags); + + bnxt_qplib_free_qp_hdr_buf(res, qp); + bnxt_qplib_free_hwq(res->pdev, &qp->sq.hwq); + kfree(qp->sq.swq); + + bnxt_qplib_free_hwq(res->pdev, &qp->rq.hwq); + kfree(qp->rq.swq); + + if (qp->irrq.max_elements) + bnxt_qplib_free_hwq(res->pdev, &qp->irrq); + if (qp->orrq.max_elements) + bnxt_qplib_free_hwq(res->pdev, &qp->orrq); + + return 0; +} + +void *bnxt_qplib_get_qp1_sq_buf(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_sge *sge) +{ + struct bnxt_qplib_q *sq = &qp->sq; + u32 sw_prod; + + memset(sge, 0, sizeof(*sge)); + + if (qp->sq_hdr_buf) { + sw_prod = HWQ_CMP(sq->hwq.prod, &sq->hwq); + sge->addr = (dma_addr_t)(qp->sq_hdr_buf_map + + sw_prod * qp->sq_hdr_buf_size); + sge->lkey = 0xFFFFFFFF; + sge->size = qp->sq_hdr_buf_size; + return qp->sq_hdr_buf + sw_prod * sge->size; + } + return NULL; +} + +u32 bnxt_qplib_get_rq_prod_index(struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_q *rq = &qp->rq; + + return HWQ_CMP(rq->hwq.prod, &rq->hwq); +} + +dma_addr_t bnxt_qplib_get_qp_buf_from_index(struct bnxt_qplib_qp *qp, u32 index) +{ + return (qp->rq_hdr_buf_map + index * qp->rq_hdr_buf_size); +} + +void *bnxt_qplib_get_qp1_rq_buf(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_sge *sge) +{ + struct bnxt_qplib_q *rq = &qp->rq; + u32 sw_prod; + + memset(sge, 0, sizeof(*sge)); + + if (qp->rq_hdr_buf) { + sw_prod = HWQ_CMP(rq->hwq.prod, &rq->hwq); + sge->addr = (dma_addr_t)(qp->rq_hdr_buf_map + + sw_prod * qp->rq_hdr_buf_size); + sge->lkey = 0xFFFFFFFF; + sge->size = qp->rq_hdr_buf_size; + return qp->rq_hdr_buf + sw_prod * sge->size; + } + return NULL; +} + +void bnxt_qplib_post_send_db(struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_q *sq = &qp->sq; + struct dbr_dbr db_msg = { 0 }; + u32 sw_prod; + + sw_prod = HWQ_CMP(sq->hwq.prod, &sq->hwq); + + db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) & + DBR_DBR_INDEX_MASK); + db_msg.type_xid = + cpu_to_le32(((qp->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | + DBR_DBR_TYPE_SQ); + /* Flush all the WQE writes to HW */ + wmb(); + __iowrite64_copy(qp->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64)); +} + +int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_swqe *wqe) +{ + struct bnxt_qplib_q *sq = &qp->sq; + struct bnxt_qplib_swq *swq; + struct sq_send *hw_sq_send_hdr, **hw_sq_send_ptr; + struct sq_sge *hw_sge; + u32 sw_prod; + u8 wqe_size16; + int i, rc = 0, data_len = 0, pkt_num = 0; + __le32 temp32; + + if (qp->state != CMDQ_MODIFY_QP_NEW_STATE_RTS) { + rc = -EINVAL; + goto done; + } + if (HWQ_CMP((sq->hwq.prod + 1), &sq->hwq) == + HWQ_CMP(sq->hwq.cons, &sq->hwq)) { + rc = -ENOMEM; + goto done; + } + sw_prod = HWQ_CMP(sq->hwq.prod, &sq->hwq); + swq = &sq->swq[sw_prod]; + swq->wr_id = wqe->wr_id; + swq->type = wqe->type; + swq->flags = wqe->flags; + if (qp->sig_type) + swq->flags |= SQ_SEND_FLAGS_SIGNAL_COMP; + swq->start_psn = sq->psn & BTH_PSN_MASK; + + hw_sq_send_ptr = (struct sq_send **)sq->hwq.pbl_ptr; + hw_sq_send_hdr = &hw_sq_send_ptr[get_sqe_pg(sw_prod)] + [get_sqe_idx(sw_prod)]; + + memset(hw_sq_send_hdr, 0, BNXT_QPLIB_MAX_SQE_ENTRY_SIZE); + + if (wqe->flags & BNXT_QPLIB_SWQE_FLAGS_INLINE) { + /* Copy the inline data */ + if (wqe->inline_len > BNXT_QPLIB_SWQE_MAX_INLINE_LENGTH) { + dev_warn(&sq->hwq.pdev->dev, + "QPLIB: Inline data length > 96 detected"); + data_len = BNXT_QPLIB_SWQE_MAX_INLINE_LENGTH; + } else { + data_len = wqe->inline_len; + } + memcpy(hw_sq_send_hdr->data, wqe->inline_data, data_len); + wqe_size16 = (data_len + 15) >> 4; + } else { + for (i = 0, hw_sge = (struct sq_sge *)hw_sq_send_hdr->data; + i < wqe->num_sge; i++, hw_sge++) { + hw_sge->va_or_pa = cpu_to_le64(wqe->sg_list[i].addr); + hw_sge->l_key = cpu_to_le32(wqe->sg_list[i].lkey); + hw_sge->size = cpu_to_le32(wqe->sg_list[i].size); + data_len += wqe->sg_list[i].size; + } + /* Each SGE entry = 1 WQE size16 */ + wqe_size16 = wqe->num_sge; + } + + /* Specifics */ + switch (wqe->type) { + case BNXT_QPLIB_SWQE_TYPE_SEND: + if (qp->type == CMDQ_CREATE_QP1_TYPE_GSI) { + /* Assemble info for Raw Ethertype QPs */ + struct sq_send_raweth_qp1 *sqe = + (struct sq_send_raweth_qp1 *)hw_sq_send_hdr; + + sqe->wqe_type = wqe->type; + sqe->flags = wqe->flags; + sqe->wqe_size = wqe_size16 + + ((offsetof(typeof(*sqe), data) + 15) >> 4); + sqe->cfa_action = cpu_to_le16(wqe->rawqp1.cfa_action); + sqe->lflags = cpu_to_le16(wqe->rawqp1.lflags); + sqe->length = cpu_to_le32(data_len); + sqe->cfa_meta = cpu_to_le32((wqe->rawqp1.cfa_meta & + SQ_SEND_RAWETH_QP1_CFA_META_VLAN_VID_MASK) << + SQ_SEND_RAWETH_QP1_CFA_META_VLAN_VID_SFT); + + break; + } + /* else, just fall thru */ + case BNXT_QPLIB_SWQE_TYPE_SEND_WITH_IMM: + case BNXT_QPLIB_SWQE_TYPE_SEND_WITH_INV: + { + struct sq_send *sqe = (struct sq_send *)hw_sq_send_hdr; + + sqe->wqe_type = wqe->type; + sqe->flags = wqe->flags; + sqe->wqe_size = wqe_size16 + + ((offsetof(typeof(*sqe), data) + 15) >> 4); + sqe->inv_key_or_imm_data = cpu_to_le32( + wqe->send.inv_key); + if (qp->type == CMDQ_CREATE_QP_TYPE_UD) { + sqe->q_key = cpu_to_le32(wqe->send.q_key); + sqe->dst_qp = cpu_to_le32( + wqe->send.dst_qp & SQ_SEND_DST_QP_MASK); + sqe->length = cpu_to_le32(data_len); + sqe->avid = cpu_to_le32(wqe->send.avid & + SQ_SEND_AVID_MASK); + sq->psn = (sq->psn + 1) & BTH_PSN_MASK; + } else { + sqe->length = cpu_to_le32(data_len); + sqe->dst_qp = 0; + sqe->avid = 0; + if (qp->mtu) + pkt_num = (data_len + qp->mtu - 1) / qp->mtu; + if (!pkt_num) + pkt_num = 1; + sq->psn = (sq->psn + pkt_num) & BTH_PSN_MASK; + } + break; + } + case BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE: + case BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE_WITH_IMM: + case BNXT_QPLIB_SWQE_TYPE_RDMA_READ: + { + struct sq_rdma *sqe = (struct sq_rdma *)hw_sq_send_hdr; + + sqe->wqe_type = wqe->type; + sqe->flags = wqe->flags; + sqe->wqe_size = wqe_size16 + + ((offsetof(typeof(*sqe), data) + 15) >> 4); + sqe->imm_data = cpu_to_le32(wqe->rdma.inv_key); + sqe->length = cpu_to_le32((u32)data_len); + sqe->remote_va = cpu_to_le64(wqe->rdma.remote_va); + sqe->remote_key = cpu_to_le32(wqe->rdma.r_key); + if (qp->mtu) + pkt_num = (data_len + qp->mtu - 1) / qp->mtu; + if (!pkt_num) + pkt_num = 1; + sq->psn = (sq->psn + pkt_num) & BTH_PSN_MASK; + break; + } + case BNXT_QPLIB_SWQE_TYPE_ATOMIC_CMP_AND_SWP: + case BNXT_QPLIB_SWQE_TYPE_ATOMIC_FETCH_AND_ADD: + { + struct sq_atomic *sqe = (struct sq_atomic *)hw_sq_send_hdr; + + sqe->wqe_type = wqe->type; + sqe->flags = wqe->flags; + sqe->remote_key = cpu_to_le32(wqe->atomic.r_key); + sqe->remote_va = cpu_to_le64(wqe->atomic.remote_va); + sqe->swap_data = cpu_to_le64(wqe->atomic.swap_data); + sqe->cmp_data = cpu_to_le64(wqe->atomic.cmp_data); + if (qp->mtu) + pkt_num = (data_len + qp->mtu - 1) / qp->mtu; + if (!pkt_num) + pkt_num = 1; + sq->psn = (sq->psn + pkt_num) & BTH_PSN_MASK; + break; + } + case BNXT_QPLIB_SWQE_TYPE_LOCAL_INV: + { + struct sq_localinvalidate *sqe = + (struct sq_localinvalidate *)hw_sq_send_hdr; + + sqe->wqe_type = wqe->type; + sqe->flags = wqe->flags; + sqe->inv_l_key = cpu_to_le32(wqe->local_inv.inv_l_key); + + break; + } + case BNXT_QPLIB_SWQE_TYPE_FAST_REG_MR: + { + struct sq_fr_pmr *sqe = (struct sq_fr_pmr *)hw_sq_send_hdr; + + sqe->wqe_type = wqe->type; + sqe->flags = wqe->flags; + sqe->access_cntl = wqe->frmr.access_cntl | + SQ_FR_PMR_ACCESS_CNTL_LOCAL_WRITE; + sqe->zero_based_page_size_log = + (wqe->frmr.pg_sz_log & SQ_FR_PMR_PAGE_SIZE_LOG_MASK) << + SQ_FR_PMR_PAGE_SIZE_LOG_SFT | + (wqe->frmr.zero_based ? SQ_FR_PMR_ZERO_BASED : 0); + sqe->l_key = cpu_to_le32(wqe->frmr.l_key); + temp32 = cpu_to_le32(wqe->frmr.length); + memcpy(sqe->length, &temp32, sizeof(wqe->frmr.length)); + sqe->numlevels_pbl_page_size_log = + ((wqe->frmr.pbl_pg_sz_log << + SQ_FR_PMR_PBL_PAGE_SIZE_LOG_SFT) & + SQ_FR_PMR_PBL_PAGE_SIZE_LOG_MASK) | + ((wqe->frmr.levels << SQ_FR_PMR_NUMLEVELS_SFT) & + SQ_FR_PMR_NUMLEVELS_MASK); + + for (i = 0; i < wqe->frmr.page_list_len; i++) + wqe->frmr.pbl_ptr[i] = cpu_to_le64( + wqe->frmr.page_list[i] | + PTU_PTE_VALID); + sqe->pblptr = cpu_to_le64(wqe->frmr.pbl_dma_ptr); + sqe->va = cpu_to_le64(wqe->frmr.va); + + break; + } + case BNXT_QPLIB_SWQE_TYPE_BIND_MW: + { + struct sq_bind *sqe = (struct sq_bind *)hw_sq_send_hdr; + + sqe->wqe_type = wqe->type; + sqe->flags = wqe->flags; + sqe->access_cntl = wqe->bind.access_cntl; + sqe->mw_type_zero_based = wqe->bind.mw_type | + (wqe->bind.zero_based ? SQ_BIND_ZERO_BASED : 0); + sqe->parent_l_key = cpu_to_le32(wqe->bind.parent_l_key); + sqe->l_key = cpu_to_le32(wqe->bind.r_key); + sqe->va = cpu_to_le64(wqe->bind.va); + temp32 = cpu_to_le32(wqe->bind.length); + memcpy(&sqe->length, &temp32, sizeof(wqe->bind.length)); + break; + } + default: + /* Bad wqe, return error */ + rc = -EINVAL; + goto done; + } + swq->next_psn = sq->psn & BTH_PSN_MASK; + if (swq->psn_search) { + swq->psn_search->opcode_start_psn = cpu_to_le32( + ((swq->start_psn << SQ_PSN_SEARCH_START_PSN_SFT) & + SQ_PSN_SEARCH_START_PSN_MASK) | + ((wqe->type << SQ_PSN_SEARCH_OPCODE_SFT) & + SQ_PSN_SEARCH_OPCODE_MASK)); + swq->psn_search->flags_next_psn = cpu_to_le32( + ((swq->next_psn << SQ_PSN_SEARCH_NEXT_PSN_SFT) & + SQ_PSN_SEARCH_NEXT_PSN_MASK)); + } + + sq->hwq.prod++; +done: + return rc; +} + +void bnxt_qplib_post_recv_db(struct bnxt_qplib_qp *qp) +{ + struct bnxt_qplib_q *rq = &qp->rq; + struct dbr_dbr db_msg = { 0 }; + u32 sw_prod; + + sw_prod = HWQ_CMP(rq->hwq.prod, &rq->hwq); + db_msg.index = cpu_to_le32((sw_prod << DBR_DBR_INDEX_SFT) & + DBR_DBR_INDEX_MASK); + db_msg.type_xid = + cpu_to_le32(((qp->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | + DBR_DBR_TYPE_RQ); + + /* Flush the writes to HW Rx WQE before the ringing Rx DB */ + wmb(); + __iowrite64_copy(qp->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64)); +} + +int bnxt_qplib_post_recv(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_swqe *wqe) +{ + struct bnxt_qplib_q *rq = &qp->rq; + struct rq_wqe *rqe, **rqe_ptr; + struct sq_sge *hw_sge; + u32 sw_prod; + int i, rc = 0; + + if (qp->state == CMDQ_MODIFY_QP_NEW_STATE_ERR) { + dev_err(&rq->hwq.pdev->dev, + "QPLIB: FP: QP (0x%x) is in the 0x%x state", + qp->id, qp->state); + rc = -EINVAL; + goto done; + } + if (HWQ_CMP((rq->hwq.prod + 1), &rq->hwq) == + HWQ_CMP(rq->hwq.cons, &rq->hwq)) { + dev_err(&rq->hwq.pdev->dev, + "QPLIB: FP: QP (0x%x) RQ is full!", qp->id); + rc = -EINVAL; + goto done; + } + sw_prod = HWQ_CMP(rq->hwq.prod, &rq->hwq); + rq->swq[sw_prod].wr_id = wqe->wr_id; + + rqe_ptr = (struct rq_wqe **)rq->hwq.pbl_ptr; + rqe = &rqe_ptr[RQE_PG(sw_prod)][RQE_IDX(sw_prod)]; + + memset(rqe, 0, BNXT_QPLIB_MAX_RQE_ENTRY_SIZE); + + /* Calculate wqe_size16 and data_len */ + for (i = 0, hw_sge = (struct sq_sge *)rqe->data; + i < wqe->num_sge; i++, hw_sge++) { + hw_sge->va_or_pa = cpu_to_le64(wqe->sg_list[i].addr); + hw_sge->l_key = cpu_to_le32(wqe->sg_list[i].lkey); + hw_sge->size = cpu_to_le32(wqe->sg_list[i].size); + } + rqe->wqe_type = wqe->type; + rqe->flags = wqe->flags; + rqe->wqe_size = wqe->num_sge + + ((offsetof(typeof(*rqe), data) + 15) >> 4); + + /* Supply the rqe->wr_id index to the wr_id_tbl for now */ + rqe->wr_id[0] = cpu_to_le32(sw_prod); + + rq->hwq.prod++; +done: + return rc; +} + +/* CQ */ + +/* Spinlock must be held */ +static void bnxt_qplib_arm_cq_enable(struct bnxt_qplib_cq *cq) +{ + struct dbr_dbr db_msg = { 0 }; + + db_msg.type_xid = + cpu_to_le32(((cq->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | + DBR_DBR_TYPE_CQ_ARMENA); + /* Flush memory writes before enabling the CQ */ + wmb(); + __iowrite64_copy(cq->dbr_base, &db_msg, sizeof(db_msg) / sizeof(u64)); +} + +static void bnxt_qplib_arm_cq(struct bnxt_qplib_cq *cq, u32 arm_type) +{ + struct bnxt_qplib_hwq *cq_hwq = &cq->hwq; + struct dbr_dbr db_msg = { 0 }; + u32 sw_cons; + + /* Ring DB */ + sw_cons = HWQ_CMP(cq_hwq->cons, cq_hwq); + db_msg.index = cpu_to_le32((sw_cons << DBR_DBR_INDEX_SFT) & + DBR_DBR_INDEX_MASK); + db_msg.type_xid = + cpu_to_le32(((cq->id << DBR_DBR_XID_SFT) & DBR_DBR_XID_MASK) | + arm_type); + /* flush memory writes before arming the CQ */ + wmb(); + __iowrite64_copy(cq->dpi->dbr, &db_msg, sizeof(db_msg) / sizeof(u64)); +} + +int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_create_cq req; + struct creq_create_cq_resp *resp; + struct bnxt_qplib_pbl *pbl; + u16 cmd_flags = 0; + int rc; + + cq->hwq.max_elements = cq->max_wqe; + rc = bnxt_qplib_alloc_init_hwq(res->pdev, &cq->hwq, cq->sghead, + cq->nmap, &cq->hwq.max_elements, + BNXT_QPLIB_MAX_CQE_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_QUEUE); + if (rc) + goto exit; + + RCFW_CMD_PREP(req, CREATE_CQ, cmd_flags); + + if (!cq->dpi) { + dev_err(&rcfw->pdev->dev, + "QPLIB: FP: CREATE_CQ failed due to NULL DPI"); + return -EINVAL; + } + req.dpi = cpu_to_le32(cq->dpi->dpi); + req.cq_handle = cpu_to_le64(cq->cq_handle); + + req.cq_size = cpu_to_le32(cq->hwq.max_elements); + pbl = &cq->hwq.pbl[PBL_LVL_0]; + req.pg_size_lvl = cpu_to_le32( + ((cq->hwq.level & CMDQ_CREATE_CQ_LVL_MASK) << + CMDQ_CREATE_CQ_LVL_SFT) | + (pbl->pg_size == ROCE_PG_SIZE_4K ? CMDQ_CREATE_CQ_PG_SIZE_PG_4K : + pbl->pg_size == ROCE_PG_SIZE_8K ? CMDQ_CREATE_CQ_PG_SIZE_PG_8K : + pbl->pg_size == ROCE_PG_SIZE_64K ? CMDQ_CREATE_CQ_PG_SIZE_PG_64K : + pbl->pg_size == ROCE_PG_SIZE_2M ? CMDQ_CREATE_CQ_PG_SIZE_PG_2M : + pbl->pg_size == ROCE_PG_SIZE_8M ? CMDQ_CREATE_CQ_PG_SIZE_PG_8M : + pbl->pg_size == ROCE_PG_SIZE_1G ? CMDQ_CREATE_CQ_PG_SIZE_PG_1G : + CMDQ_CREATE_CQ_PG_SIZE_PG_4K)); + + req.pbl = cpu_to_le64(pbl->pg_map_arr[0]); + + req.cq_fco_cnq_id = cpu_to_le32( + (cq->cnq_hw_ring_id & CMDQ_CREATE_CQ_CNQ_ID_MASK) << + CMDQ_CREATE_CQ_CNQ_ID_SFT); + + resp = (struct creq_create_cq_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_CQ send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_CQ timed out"); + rc = -ETIMEDOUT; + goto fail; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: CREATE_CQ failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + rc = -EINVAL; + goto fail; + } + cq->id = le32_to_cpu(resp->xid); + cq->dbr_base = res->dpi_tbl.dbr_bar_reg_iomem; + cq->period = BNXT_QPLIB_QUEUE_START_PERIOD; + init_waitqueue_head(&cq->waitq); + + bnxt_qplib_arm_cq_enable(cq); + return 0; + +fail: + bnxt_qplib_free_hwq(res->pdev, &cq->hwq); +exit: + return rc; +} + +int bnxt_qplib_destroy_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_destroy_cq req; + struct creq_destroy_cq_resp *resp; + u16 cmd_flags = 0; + + RCFW_CMD_PREP(req, DESTROY_CQ, cmd_flags); + + req.cq_cid = cpu_to_le32(cq->id); + resp = (struct creq_destroy_cq_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: DESTROY_CQ send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: FP: DESTROY_CQ timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: FP: DESTROY_CQ failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + bnxt_qplib_free_hwq(res->pdev, &cq->hwq); + return 0; +} + +static int __flush_sq(struct bnxt_qplib_q *sq, struct bnxt_qplib_qp *qp, + struct bnxt_qplib_cqe **pcqe, int *budget) +{ + u32 sw_prod, sw_cons; + struct bnxt_qplib_cqe *cqe; + int rc = 0; + + /* Now complete all outstanding SQEs with FLUSHED_ERR */ + sw_prod = HWQ_CMP(sq->hwq.prod, &sq->hwq); + cqe = *pcqe; + while (*budget) { + sw_cons = HWQ_CMP(sq->hwq.cons, &sq->hwq); + if (sw_cons == sw_prod) { + sq->flush_in_progress = false; + break; + } + memset(cqe, 0, sizeof(*cqe)); + cqe->status = CQ_REQ_STATUS_WORK_REQUEST_FLUSHED_ERR; + cqe->opcode = CQ_BASE_CQE_TYPE_REQ; + cqe->qp_handle = (u64)(unsigned long)qp; + cqe->wr_id = sq->swq[sw_cons].wr_id; + cqe->src_qp = qp->id; + cqe->type = sq->swq[sw_cons].type; + cqe++; + (*budget)--; + sq->hwq.cons++; + } + *pcqe = cqe; + if (!(*budget) && HWQ_CMP(sq->hwq.cons, &sq->hwq) != sw_prod) + /* Out of budget */ + rc = -EAGAIN; + + return rc; +} + +static int __flush_rq(struct bnxt_qplib_q *rq, struct bnxt_qplib_qp *qp, + int opcode, struct bnxt_qplib_cqe **pcqe, int *budget) +{ + struct bnxt_qplib_cqe *cqe; + u32 sw_prod, sw_cons; + int rc = 0; + + /* Flush the rest of the RQ */ + sw_prod = HWQ_CMP(rq->hwq.prod, &rq->hwq); + cqe = *pcqe; + while (*budget) { + sw_cons = HWQ_CMP(rq->hwq.cons, &rq->hwq); + if (sw_cons == sw_prod) + break; + memset(cqe, 0, sizeof(*cqe)); + cqe->status = + CQ_RES_RC_STATUS_WORK_REQUEST_FLUSHED_ERR; + cqe->opcode = opcode; + cqe->qp_handle = (unsigned long)qp; + cqe->wr_id = rq->swq[sw_cons].wr_id; + cqe++; + (*budget)--; + rq->hwq.cons++; + } + *pcqe = cqe; + if (!*budget && HWQ_CMP(rq->hwq.cons, &rq->hwq) != sw_prod) + /* Out of budget */ + rc = -EAGAIN; + + return rc; +} + +static int bnxt_qplib_cq_process_req(struct bnxt_qplib_cq *cq, + struct cq_req *hwcqe, + struct bnxt_qplib_cqe **pcqe, int *budget) +{ + struct bnxt_qplib_qp *qp; + struct bnxt_qplib_q *sq; + struct bnxt_qplib_cqe *cqe; + u32 sw_cons, cqe_cons; + int rc = 0; + + qp = (struct bnxt_qplib_qp *)((unsigned long) + le64_to_cpu(hwcqe->qp_handle)); + if (!qp) { + dev_err(&cq->hwq.pdev->dev, + "QPLIB: FP: Process Req qp is NULL"); + return -EINVAL; + } + sq = &qp->sq; + + cqe_cons = HWQ_CMP(le16_to_cpu(hwcqe->sq_cons_idx), &sq->hwq); + if (cqe_cons > sq->hwq.max_elements) { + dev_err(&cq->hwq.pdev->dev, + "QPLIB: FP: CQ Process req reported "); + dev_err(&cq->hwq.pdev->dev, + "QPLIB: sq_cons_idx 0x%x which exceeded max 0x%x", + cqe_cons, sq->hwq.max_elements); + return -EINVAL; + } + /* If we were in the middle of flushing the SQ, continue */ + if (sq->flush_in_progress) + goto flush; + + /* Require to walk the sq's swq to fabricate CQEs for all previously + * signaled SWQEs due to CQE aggregation from the current sq cons + * to the cqe_cons + */ + cqe = *pcqe; + while (*budget) { + sw_cons = HWQ_CMP(sq->hwq.cons, &sq->hwq); + if (sw_cons == cqe_cons) + break; + memset(cqe, 0, sizeof(*cqe)); + cqe->opcode = CQ_BASE_CQE_TYPE_REQ; + cqe->qp_handle = (u64)(unsigned long)qp; + cqe->src_qp = qp->id; + cqe->wr_id = sq->swq[sw_cons].wr_id; + cqe->type = sq->swq[sw_cons].type; + + /* For the last CQE, check for status. For errors, regardless + * of the request being signaled or not, it must complete with + * the hwcqe error status + */ + if (HWQ_CMP((sw_cons + 1), &sq->hwq) == cqe_cons && + hwcqe->status != CQ_REQ_STATUS_OK) { + cqe->status = hwcqe->status; + dev_err(&cq->hwq.pdev->dev, + "QPLIB: FP: CQ Processed Req "); + dev_err(&cq->hwq.pdev->dev, + "QPLIB: wr_id[%d] = 0x%llx with status 0x%x", + sw_cons, cqe->wr_id, cqe->status); + cqe++; + (*budget)--; + sq->flush_in_progress = true; + /* Must block new posting of SQ and RQ */ + qp->state = CMDQ_MODIFY_QP_NEW_STATE_ERR; + } else { + if (sq->swq[sw_cons].flags & + SQ_SEND_FLAGS_SIGNAL_COMP) { + cqe->status = CQ_REQ_STATUS_OK; + cqe++; + (*budget)--; + } + } + sq->hwq.cons++; + } + *pcqe = cqe; + if (!*budget && HWQ_CMP(sq->hwq.cons, &sq->hwq) != cqe_cons) { + /* Out of budget */ + rc = -EAGAIN; + goto done; + } + if (!sq->flush_in_progress) + goto done; +flush: + /* Require to walk the sq's swq to fabricate CQEs for all + * previously posted SWQEs due to the error CQE received + */ + rc = __flush_sq(sq, qp, pcqe, budget); + if (!rc) + sq->flush_in_progress = false; +done: + return rc; +} + +static int bnxt_qplib_cq_process_res_rc(struct bnxt_qplib_cq *cq, + struct cq_res_rc *hwcqe, + struct bnxt_qplib_cqe **pcqe, + int *budget) +{ + struct bnxt_qplib_qp *qp; + struct bnxt_qplib_q *rq; + struct bnxt_qplib_cqe *cqe; + u32 wr_id_idx; + int rc = 0; + + qp = (struct bnxt_qplib_qp *)((unsigned long) + le64_to_cpu(hwcqe->qp_handle)); + if (!qp) { + dev_err(&cq->hwq.pdev->dev, "QPLIB: process_cq RC qp is NULL"); + return -EINVAL; + } + cqe = *pcqe; + cqe->opcode = hwcqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK; + cqe->length = le32_to_cpu(hwcqe->length); + cqe->invrkey = le32_to_cpu(hwcqe->imm_data_or_inv_r_key); + cqe->mr_handle = le64_to_cpu(hwcqe->mr_handle); + cqe->flags = le16_to_cpu(hwcqe->flags); + cqe->status = hwcqe->status; + cqe->qp_handle = (u64)(unsigned long)qp; + + wr_id_idx = le32_to_cpu(hwcqe->srq_or_rq_wr_id) & + CQ_RES_RC_SRQ_OR_RQ_WR_ID_MASK; + rq = &qp->rq; + if (wr_id_idx > rq->hwq.max_elements) { + dev_err(&cq->hwq.pdev->dev, "QPLIB: FP: CQ Process RC "); + dev_err(&cq->hwq.pdev->dev, + "QPLIB: wr_id idx 0x%x exceeded RQ max 0x%x", + wr_id_idx, rq->hwq.max_elements); + return -EINVAL; + } + if (rq->flush_in_progress) + goto flush_rq; + + cqe->wr_id = rq->swq[wr_id_idx].wr_id; + cqe++; + (*budget)--; + rq->hwq.cons++; + *pcqe = cqe; + + if (hwcqe->status != CQ_RES_RC_STATUS_OK) { + rq->flush_in_progress = true; +flush_rq: + rc = __flush_rq(rq, qp, CQ_BASE_CQE_TYPE_RES_RC, pcqe, budget); + if (!rc) + rq->flush_in_progress = false; + } + return rc; +} + +static int bnxt_qplib_cq_process_res_ud(struct bnxt_qplib_cq *cq, + struct cq_res_ud *hwcqe, + struct bnxt_qplib_cqe **pcqe, + int *budget) +{ + struct bnxt_qplib_qp *qp; + struct bnxt_qplib_q *rq; + struct bnxt_qplib_cqe *cqe; + u32 wr_id_idx; + int rc = 0; + + qp = (struct bnxt_qplib_qp *)((unsigned long) + le64_to_cpu(hwcqe->qp_handle)); + if (!qp) { + dev_err(&cq->hwq.pdev->dev, "QPLIB: process_cq UD qp is NULL"); + return -EINVAL; + } + cqe = *pcqe; + cqe->opcode = hwcqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK; + cqe->length = le32_to_cpu(hwcqe->length); + cqe->invrkey = le32_to_cpu(hwcqe->imm_data); + cqe->flags = le16_to_cpu(hwcqe->flags); + cqe->status = hwcqe->status; + cqe->qp_handle = (u64)(unsigned long)qp; + memcpy(cqe->smac, hwcqe->src_mac, 6); + wr_id_idx = le32_to_cpu(hwcqe->src_qp_high_srq_or_rq_wr_id) + & CQ_RES_UD_SRQ_OR_RQ_WR_ID_MASK; + cqe->src_qp = le16_to_cpu(hwcqe->src_qp_low) | + ((le32_to_cpu( + hwcqe->src_qp_high_srq_or_rq_wr_id) & + CQ_RES_UD_SRC_QP_HIGH_MASK) >> 8); + + rq = &qp->rq; + if (wr_id_idx > rq->hwq.max_elements) { + dev_err(&cq->hwq.pdev->dev, "QPLIB: FP: CQ Process UD "); + dev_err(&cq->hwq.pdev->dev, + "QPLIB: wr_id idx %#x exceeded RQ max %#x", + wr_id_idx, rq->hwq.max_elements); + return -EINVAL; + } + if (rq->flush_in_progress) + goto flush_rq; + + cqe->wr_id = rq->swq[wr_id_idx].wr_id; + cqe++; + (*budget)--; + rq->hwq.cons++; + *pcqe = cqe; + + if (hwcqe->status != CQ_RES_RC_STATUS_OK) { + rq->flush_in_progress = true; +flush_rq: + rc = __flush_rq(rq, qp, CQ_BASE_CQE_TYPE_RES_UD, pcqe, budget); + if (!rc) + rq->flush_in_progress = false; + } + return rc; +} + +static int bnxt_qplib_cq_process_res_raweth_qp1(struct bnxt_qplib_cq *cq, + struct cq_res_raweth_qp1 *hwcqe, + struct bnxt_qplib_cqe **pcqe, + int *budget) +{ + struct bnxt_qplib_qp *qp; + struct bnxt_qplib_q *rq; + struct bnxt_qplib_cqe *cqe; + u32 wr_id_idx; + int rc = 0; + + qp = (struct bnxt_qplib_qp *)((unsigned long) + le64_to_cpu(hwcqe->qp_handle)); + if (!qp) { + dev_err(&cq->hwq.pdev->dev, + "QPLIB: process_cq Raw/QP1 qp is NULL"); + return -EINVAL; + } + cqe = *pcqe; + cqe->opcode = hwcqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK; + cqe->flags = le16_to_cpu(hwcqe->flags); + cqe->qp_handle = (u64)(unsigned long)qp; + + wr_id_idx = + le32_to_cpu(hwcqe->raweth_qp1_payload_offset_srq_or_rq_wr_id) + & CQ_RES_RAWETH_QP1_SRQ_OR_RQ_WR_ID_MASK; + cqe->src_qp = qp->id; + if (qp->id == 1 && !cqe->length) { + /* Add workaround for the length misdetection */ + cqe->length = 296; + } else { + cqe->length = le16_to_cpu(hwcqe->length); + } + cqe->pkey_index = qp->pkey_index; + memcpy(cqe->smac, qp->smac, 6); + + cqe->raweth_qp1_flags = le16_to_cpu(hwcqe->raweth_qp1_flags); + cqe->raweth_qp1_flags2 = le32_to_cpu(hwcqe->raweth_qp1_flags2); + + rq = &qp->rq; + if (wr_id_idx > rq->hwq.max_elements) { + dev_err(&cq->hwq.pdev->dev, "QPLIB: FP: CQ Process Raw/QP1 RQ wr_id "); + dev_err(&cq->hwq.pdev->dev, "QPLIB: ix 0x%x exceeded RQ max 0x%x", + wr_id_idx, rq->hwq.max_elements); + return -EINVAL; + } + if (rq->flush_in_progress) + goto flush_rq; + + cqe->wr_id = rq->swq[wr_id_idx].wr_id; + cqe++; + (*budget)--; + rq->hwq.cons++; + *pcqe = cqe; + + if (hwcqe->status != CQ_RES_RC_STATUS_OK) { + rq->flush_in_progress = true; +flush_rq: + rc = __flush_rq(rq, qp, CQ_BASE_CQE_TYPE_RES_RAWETH_QP1, pcqe, + budget); + if (!rc) + rq->flush_in_progress = false; + } + return rc; +} + +static int bnxt_qplib_cq_process_terminal(struct bnxt_qplib_cq *cq, + struct cq_terminal *hwcqe, + struct bnxt_qplib_cqe **pcqe, + int *budget) +{ + struct bnxt_qplib_qp *qp; + struct bnxt_qplib_q *sq, *rq; + struct bnxt_qplib_cqe *cqe; + u32 sw_cons = 0, cqe_cons; + int rc = 0; + u8 opcode = 0; + + /* Check the Status */ + if (hwcqe->status != CQ_TERMINAL_STATUS_OK) + dev_warn(&cq->hwq.pdev->dev, + "QPLIB: FP: CQ Process Terminal Error status = 0x%x", + hwcqe->status); + + qp = (struct bnxt_qplib_qp *)((unsigned long) + le64_to_cpu(hwcqe->qp_handle)); + if (!qp) { + dev_err(&cq->hwq.pdev->dev, + "QPLIB: FP: CQ Process terminal qp is NULL"); + return -EINVAL; + } + /* Must block new posting of SQ and RQ */ + qp->state = CMDQ_MODIFY_QP_NEW_STATE_ERR; + + sq = &qp->sq; + rq = &qp->rq; + + cqe_cons = le16_to_cpu(hwcqe->sq_cons_idx); + if (cqe_cons == 0xFFFF) + goto do_rq; + + if (cqe_cons > sq->hwq.max_elements) { + dev_err(&cq->hwq.pdev->dev, + "QPLIB: FP: CQ Process terminal reported "); + dev_err(&cq->hwq.pdev->dev, + "QPLIB: sq_cons_idx 0x%x which exceeded max 0x%x", + cqe_cons, sq->hwq.max_elements); + goto do_rq; + } + /* If we were in the middle of flushing, continue */ + if (sq->flush_in_progress) + goto flush_sq; + + /* Terminal CQE can also include aggregated successful CQEs prior. + * So we must complete all CQEs from the current sq's cons to the + * cq_cons with status OK + */ + cqe = *pcqe; + while (*budget) { + sw_cons = HWQ_CMP(sq->hwq.cons, &sq->hwq); + if (sw_cons == cqe_cons) + break; + if (sq->swq[sw_cons].flags & SQ_SEND_FLAGS_SIGNAL_COMP) { + memset(cqe, 0, sizeof(*cqe)); + cqe->status = CQ_REQ_STATUS_OK; + cqe->opcode = CQ_BASE_CQE_TYPE_REQ; + cqe->qp_handle = (u64)(unsigned long)qp; + cqe->src_qp = qp->id; + cqe->wr_id = sq->swq[sw_cons].wr_id; + cqe->type = sq->swq[sw_cons].type; + cqe++; + (*budget)--; + } + sq->hwq.cons++; + } + *pcqe = cqe; + if (!(*budget) && sw_cons != cqe_cons) { + /* Out of budget */ + rc = -EAGAIN; + goto sq_done; + } + sq->flush_in_progress = true; +flush_sq: + rc = __flush_sq(sq, qp, pcqe, budget); + if (!rc) + sq->flush_in_progress = false; +sq_done: + if (rc) + return rc; +do_rq: + cqe_cons = le16_to_cpu(hwcqe->rq_cons_idx); + if (cqe_cons == 0xFFFF) { + goto done; + } else if (cqe_cons > rq->hwq.max_elements) { + dev_err(&cq->hwq.pdev->dev, + "QPLIB: FP: CQ Processed terminal "); + dev_err(&cq->hwq.pdev->dev, + "QPLIB: reported rq_cons_idx 0x%x exceeds max 0x%x", + cqe_cons, rq->hwq.max_elements); + goto done; + } + /* Terminal CQE requires all posted RQEs to complete with FLUSHED_ERR + * from the current rq->cons to the rq->prod regardless what the + * rq->cons the terminal CQE indicates + */ + rq->flush_in_progress = true; + switch (qp->type) { + case CMDQ_CREATE_QP1_TYPE_GSI: + opcode = CQ_BASE_CQE_TYPE_RES_RAWETH_QP1; + break; + case CMDQ_CREATE_QP_TYPE_RC: + opcode = CQ_BASE_CQE_TYPE_RES_RC; + break; + case CMDQ_CREATE_QP_TYPE_UD: + opcode = CQ_BASE_CQE_TYPE_RES_UD; + break; + } + + rc = __flush_rq(rq, qp, opcode, pcqe, budget); + if (!rc) + rq->flush_in_progress = false; +done: + return rc; +} + +static int bnxt_qplib_cq_process_cutoff(struct bnxt_qplib_cq *cq, + struct cq_cutoff *hwcqe) +{ + /* Check the Status */ + if (hwcqe->status != CQ_CUTOFF_STATUS_OK) { + dev_err(&cq->hwq.pdev->dev, + "QPLIB: FP: CQ Process Cutoff Error status = 0x%x", + hwcqe->status); + return -EINVAL; + } + clear_bit(CQ_FLAGS_RESIZE_IN_PROG, &cq->flags); + wake_up_interruptible(&cq->waitq); + + return 0; +} + +int bnxt_qplib_poll_cq(struct bnxt_qplib_cq *cq, struct bnxt_qplib_cqe *cqe, + int num_cqes) +{ + struct cq_base *hw_cqe, **hw_cqe_ptr; + unsigned long flags; + u32 sw_cons, raw_cons; + int budget, rc = 0; + + spin_lock_irqsave(&cq->hwq.lock, flags); + raw_cons = cq->hwq.cons; + budget = num_cqes; + + while (budget) { + sw_cons = HWQ_CMP(raw_cons, &cq->hwq); + hw_cqe_ptr = (struct cq_base **)cq->hwq.pbl_ptr; + hw_cqe = &hw_cqe_ptr[CQE_PG(sw_cons)][CQE_IDX(sw_cons)]; + + /* Check for Valid bit */ + if (!CQE_CMP_VALID(hw_cqe, raw_cons, cq->hwq.max_elements)) + break; + + /* From the device's respective CQE format to qplib_wc*/ + switch (hw_cqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK) { + case CQ_BASE_CQE_TYPE_REQ: + rc = bnxt_qplib_cq_process_req(cq, + (struct cq_req *)hw_cqe, + &cqe, &budget); + break; + case CQ_BASE_CQE_TYPE_RES_RC: + rc = bnxt_qplib_cq_process_res_rc(cq, + (struct cq_res_rc *) + hw_cqe, &cqe, + &budget); + break; + case CQ_BASE_CQE_TYPE_RES_UD: + rc = bnxt_qplib_cq_process_res_ud + (cq, (struct cq_res_ud *)hw_cqe, &cqe, + &budget); + break; + case CQ_BASE_CQE_TYPE_RES_RAWETH_QP1: + rc = bnxt_qplib_cq_process_res_raweth_qp1 + (cq, (struct cq_res_raweth_qp1 *) + hw_cqe, &cqe, &budget); + break; + case CQ_BASE_CQE_TYPE_TERMINAL: + rc = bnxt_qplib_cq_process_terminal + (cq, (struct cq_terminal *)hw_cqe, + &cqe, &budget); + break; + case CQ_BASE_CQE_TYPE_CUT_OFF: + bnxt_qplib_cq_process_cutoff + (cq, (struct cq_cutoff *)hw_cqe); + /* Done processing this CQ */ + goto exit; + default: + dev_err(&cq->hwq.pdev->dev, + "QPLIB: process_cq unknown type 0x%lx", + hw_cqe->cqe_type_toggle & + CQ_BASE_CQE_TYPE_MASK); + rc = -EINVAL; + break; + } + if (rc < 0) { + if (rc == -EAGAIN) + break; + /* Error while processing the CQE, just skip to the + * next one + */ + dev_err(&cq->hwq.pdev->dev, + "QPLIB: process_cqe error rc = 0x%x", rc); + } + raw_cons++; + } + if (cq->hwq.cons != raw_cons) { + cq->hwq.cons = raw_cons; + bnxt_qplib_arm_cq(cq, DBR_DBR_TYPE_CQ); + } +exit: + spin_unlock_irqrestore(&cq->hwq.lock, flags); + return num_cqes - budget; +} + +void bnxt_qplib_req_notify_cq(struct bnxt_qplib_cq *cq, u32 arm_type) +{ + unsigned long flags; + + spin_lock_irqsave(&cq->hwq.lock, flags); + if (arm_type) + bnxt_qplib_arm_cq(cq, arm_type); + + spin_unlock_irqrestore(&cq->hwq.lock, flags); +} diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h new file mode 100644 index 000000000000..f0150f8da1e3 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -0,0 +1,439 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: Fast Path Operators (header) + */ + +#ifndef __BNXT_QPLIB_FP_H__ +#define __BNXT_QPLIB_FP_H__ + +struct bnxt_qplib_sge { + u64 addr; + u32 lkey; + u32 size; +}; + +#define BNXT_QPLIB_MAX_SQE_ENTRY_SIZE sizeof(struct sq_send) + +#define SQE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_MAX_SQE_ENTRY_SIZE) +#define SQE_MAX_IDX_PER_PG (SQE_CNT_PER_PG - 1) + +static inline u32 get_sqe_pg(u32 val) +{ + return ((val & ~SQE_MAX_IDX_PER_PG) / SQE_CNT_PER_PG); +} + +static inline u32 get_sqe_idx(u32 val) +{ + return (val & SQE_MAX_IDX_PER_PG); +} + +#define BNXT_QPLIB_MAX_PSNE_ENTRY_SIZE sizeof(struct sq_psn_search) + +#define PSNE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_MAX_PSNE_ENTRY_SIZE) +#define PSNE_MAX_IDX_PER_PG (PSNE_CNT_PER_PG - 1) + +static inline u32 get_psne_pg(u32 val) +{ + return ((val & ~PSNE_MAX_IDX_PER_PG) / PSNE_CNT_PER_PG); +} + +static inline u32 get_psne_idx(u32 val) +{ + return (val & PSNE_MAX_IDX_PER_PG); +} + +#define BNXT_QPLIB_QP_MAX_SGL 6 + +struct bnxt_qplib_swq { + u64 wr_id; + u8 type; + u8 flags; + u32 start_psn; + u32 next_psn; + struct sq_psn_search *psn_search; +}; + +struct bnxt_qplib_swqe { + /* General */ + u64 wr_id; + u8 reqs_type; + u8 type; +#define BNXT_QPLIB_SWQE_TYPE_SEND 0 +#define BNXT_QPLIB_SWQE_TYPE_SEND_WITH_IMM 1 +#define BNXT_QPLIB_SWQE_TYPE_SEND_WITH_INV 2 +#define BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE 4 +#define BNXT_QPLIB_SWQE_TYPE_RDMA_WRITE_WITH_IMM 5 +#define BNXT_QPLIB_SWQE_TYPE_RDMA_READ 6 +#define BNXT_QPLIB_SWQE_TYPE_ATOMIC_CMP_AND_SWP 8 +#define BNXT_QPLIB_SWQE_TYPE_ATOMIC_FETCH_AND_ADD 11 +#define BNXT_QPLIB_SWQE_TYPE_LOCAL_INV 12 +#define BNXT_QPLIB_SWQE_TYPE_FAST_REG_MR 13 +#define BNXT_QPLIB_SWQE_TYPE_REG_MR 13 +#define BNXT_QPLIB_SWQE_TYPE_BIND_MW 14 +#define BNXT_QPLIB_SWQE_TYPE_RECV 128 +#define BNXT_QPLIB_SWQE_TYPE_RECV_RDMA_IMM 129 + u8 flags; +#define BNXT_QPLIB_SWQE_FLAGS_SIGNAL_COMP BIT(0) +#define BNXT_QPLIB_SWQE_FLAGS_RD_ATOMIC_FENCE BIT(1) +#define BNXT_QPLIB_SWQE_FLAGS_UC_FENCE BIT(2) +#define BNXT_QPLIB_SWQE_FLAGS_SOLICIT_EVENT BIT(3) +#define BNXT_QPLIB_SWQE_FLAGS_INLINE BIT(4) + struct bnxt_qplib_sge sg_list[BNXT_QPLIB_QP_MAX_SGL]; + int num_sge; + /* Max inline data is 96 bytes */ + u32 inline_len; +#define BNXT_QPLIB_SWQE_MAX_INLINE_LENGTH 96 + u8 inline_data[BNXT_QPLIB_SWQE_MAX_INLINE_LENGTH]; + + union { + /* Send, with imm, inval key */ + struct { + union { + __be32 imm_data; + u32 inv_key; + }; + u32 q_key; + u32 dst_qp; + u16 avid; + } send; + + /* Send Raw Ethernet and QP1 */ + struct { + u16 lflags; + u16 cfa_action; + u32 cfa_meta; + } rawqp1; + + /* RDMA write, with imm, read */ + struct { + union { + __be32 imm_data; + u32 inv_key; + }; + u64 remote_va; + u32 r_key; + } rdma; + + /* Atomic cmp/swap, fetch/add */ + struct { + u64 remote_va; + u32 r_key; + u64 swap_data; + u64 cmp_data; + } atomic; + + /* Local Invalidate */ + struct { + u32 inv_l_key; + } local_inv; + + /* FR-PMR */ + struct { + u8 access_cntl; + u8 pg_sz_log; + bool zero_based; + u32 l_key; + u32 length; + u8 pbl_pg_sz_log; +#define BNXT_QPLIB_SWQE_PAGE_SIZE_4K 0 +#define BNXT_QPLIB_SWQE_PAGE_SIZE_8K 1 +#define BNXT_QPLIB_SWQE_PAGE_SIZE_64K 4 +#define BNXT_QPLIB_SWQE_PAGE_SIZE_256K 6 +#define BNXT_QPLIB_SWQE_PAGE_SIZE_1M 8 +#define BNXT_QPLIB_SWQE_PAGE_SIZE_2M 9 +#define BNXT_QPLIB_SWQE_PAGE_SIZE_4M 10 +#define BNXT_QPLIB_SWQE_PAGE_SIZE_1G 18 + u8 levels; +#define PAGE_SHIFT_4K 12 + __le64 *pbl_ptr; + dma_addr_t pbl_dma_ptr; + u64 *page_list; + u16 page_list_len; + u64 va; + } frmr; + + /* Bind */ + struct { + u8 access_cntl; +#define BNXT_QPLIB_BIND_SWQE_ACCESS_LOCAL_WRITE BIT(0) +#define BNXT_QPLIB_BIND_SWQE_ACCESS_REMOTE_READ BIT(1) +#define BNXT_QPLIB_BIND_SWQE_ACCESS_REMOTE_WRITE BIT(2) +#define BNXT_QPLIB_BIND_SWQE_ACCESS_REMOTE_ATOMIC BIT(3) +#define BNXT_QPLIB_BIND_SWQE_ACCESS_WINDOW_BIND BIT(4) + bool zero_based; + u8 mw_type; + u32 parent_l_key; + u32 r_key; + u64 va; + u32 length; + } bind; + }; +}; + +#define BNXT_QPLIB_MAX_RQE_ENTRY_SIZE sizeof(struct rq_wqe) + +#define RQE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_MAX_RQE_ENTRY_SIZE) +#define RQE_MAX_IDX_PER_PG (RQE_CNT_PER_PG - 1) +#define RQE_PG(x) (((x) & ~RQE_MAX_IDX_PER_PG) / RQE_CNT_PER_PG) +#define RQE_IDX(x) ((x) & RQE_MAX_IDX_PER_PG) + +struct bnxt_qplib_q { + struct bnxt_qplib_hwq hwq; + struct bnxt_qplib_swq *swq; + struct scatterlist *sglist; + u32 nmap; + u32 max_wqe; + u16 max_sge; + u32 psn; + bool flush_in_progress; +}; + +struct bnxt_qplib_qp { + struct bnxt_qplib_pd *pd; + struct bnxt_qplib_dpi *dpi; + u64 qp_handle; + u32 id; + u8 type; + u8 sig_type; + u32 modify_flags; + u8 state; + u8 cur_qp_state; + u32 max_inline_data; + u32 mtu; + u8 path_mtu; + bool en_sqd_async_notify; + u16 pkey_index; + u32 qkey; + u32 dest_qp_id; + u8 access; + u8 timeout; + u8 retry_cnt; + u8 rnr_retry; + u32 min_rnr_timer; + u32 max_rd_atomic; + u32 max_dest_rd_atomic; + u32 dest_qpn; + u8 smac[6]; + u16 vlan_id; + u8 nw_type; + struct bnxt_qplib_ah ah; + +#define BTH_PSN_MASK ((1 << 24) - 1) + /* SQ */ + struct bnxt_qplib_q sq; + /* RQ */ + struct bnxt_qplib_q rq; + /* SRQ */ + struct bnxt_qplib_srq *srq; + /* CQ */ + struct bnxt_qplib_cq *scq; + struct bnxt_qplib_cq *rcq; + /* IRRQ and ORRQ */ + struct bnxt_qplib_hwq irrq; + struct bnxt_qplib_hwq orrq; + /* Header buffer for QP1 */ + int sq_hdr_buf_size; + int rq_hdr_buf_size; +/* + * Buffer space for ETH(14), IP or GRH(40), UDP header(8) + * and ib_bth + ib_deth (20). + * Max required is 82 when RoCE V2 is enabled + */ +#define BNXT_QPLIB_MAX_QP1_SQ_HDR_SIZE_V2 86 + /* Ethernet header = 14 */ + /* ib_grh = 40 (provided by MAD) */ + /* ib_bth + ib_deth = 20 */ + /* MAD = 256 (provided by MAD) */ + /* iCRC = 4 */ +#define BNXT_QPLIB_MAX_QP1_RQ_ETH_HDR_SIZE 14 +#define BNXT_QPLIB_MAX_QP1_RQ_HDR_SIZE_V2 512 +#define BNXT_QPLIB_MAX_GRH_HDR_SIZE_IPV4 20 +#define BNXT_QPLIB_MAX_GRH_HDR_SIZE_IPV6 40 +#define BNXT_QPLIB_MAX_QP1_RQ_BDETH_HDR_SIZE 20 + void *sq_hdr_buf; + dma_addr_t sq_hdr_buf_map; + void *rq_hdr_buf; + dma_addr_t rq_hdr_buf_map; +}; + +#define BNXT_QPLIB_MAX_CQE_ENTRY_SIZE sizeof(struct cq_base) + +#define CQE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_MAX_CQE_ENTRY_SIZE) +#define CQE_MAX_IDX_PER_PG (CQE_CNT_PER_PG - 1) +#define CQE_PG(x) (((x) & ~CQE_MAX_IDX_PER_PG) / CQE_CNT_PER_PG) +#define CQE_IDX(x) ((x) & CQE_MAX_IDX_PER_PG) + +#define ROCE_CQE_CMP_V 0 +#define CQE_CMP_VALID(hdr, raw_cons, cp_bit) \ + (!!((hdr)->cqe_type_toggle & CQ_BASE_TOGGLE) == \ + !((raw_cons) & (cp_bit))) + +struct bnxt_qplib_cqe { + u8 status; + u8 type; + u8 opcode; + u32 length; + u64 wr_id; + union { + __be32 immdata; + u32 invrkey; + }; + u64 qp_handle; + u64 mr_handle; + u16 flags; + u8 smac[6]; + u32 src_qp; + u16 raweth_qp1_flags; + u16 raweth_qp1_errors; + u16 raweth_qp1_cfa_code; + u32 raweth_qp1_flags2; + u32 raweth_qp1_metadata; + u8 raweth_qp1_payload_offset; + u16 pkey_index; +}; + +#define BNXT_QPLIB_QUEUE_START_PERIOD 0x01 +struct bnxt_qplib_cq { + struct bnxt_qplib_dpi *dpi; + void __iomem *dbr_base; + u32 max_wqe; + u32 id; + u16 count; + u16 period; + struct bnxt_qplib_hwq hwq; + u32 cnq_hw_ring_id; + bool resize_in_progress; + struct scatterlist *sghead; + u32 nmap; + u64 cq_handle; + +#define CQ_RESIZE_WAIT_TIME_MS 500 + unsigned long flags; +#define CQ_FLAGS_RESIZE_IN_PROG 1 + wait_queue_head_t waitq; +}; + +#define BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE sizeof(struct xrrq_irrq) +#define BNXT_QPLIB_MAX_ORRQE_ENTRY_SIZE sizeof(struct xrrq_orrq) +#define IRD_LIMIT_TO_IRRQ_SLOTS(x) (2 * (x) + 2) +#define IRRQ_SLOTS_TO_IRD_LIMIT(s) (((s) >> 1) - 1) +#define ORD_LIMIT_TO_ORRQ_SLOTS(x) ((x) + 1) +#define ORRQ_SLOTS_TO_ORD_LIMIT(s) ((s) - 1) + +#define BNXT_QPLIB_MAX_NQE_ENTRY_SIZE sizeof(struct nq_base) + +#define NQE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_MAX_NQE_ENTRY_SIZE) +#define NQE_MAX_IDX_PER_PG (NQE_CNT_PER_PG - 1) +#define NQE_PG(x) (((x) & ~NQE_MAX_IDX_PER_PG) / NQE_CNT_PER_PG) +#define NQE_IDX(x) ((x) & NQE_MAX_IDX_PER_PG) + +#define NQE_CMP_VALID(hdr, raw_cons, cp_bit) \ + (!!(le32_to_cpu((hdr)->info63_v[0]) & NQ_BASE_V) == \ + !((raw_cons) & (cp_bit))) + +#define BNXT_QPLIB_NQE_MAX_CNT (128 * 1024) + +#define NQ_CONS_PCI_BAR_REGION 2 +#define NQ_DB_KEY_CP (0x2 << CMPL_DOORBELL_KEY_SFT) +#define NQ_DB_IDX_VALID CMPL_DOORBELL_IDX_VALID +#define NQ_DB_IRQ_DIS CMPL_DOORBELL_MASK +#define NQ_DB_CP_FLAGS_REARM (NQ_DB_KEY_CP | \ + NQ_DB_IDX_VALID) +#define NQ_DB_CP_FLAGS (NQ_DB_KEY_CP | \ + NQ_DB_IDX_VALID | \ + NQ_DB_IRQ_DIS) +#define NQ_DB_REARM(db, raw_cons, cp_bit) \ + writel(NQ_DB_CP_FLAGS_REARM | ((raw_cons) & ((cp_bit) - 1)), db) +#define NQ_DB(db, raw_cons, cp_bit) \ + writel(NQ_DB_CP_FLAGS | ((raw_cons) & ((cp_bit) - 1)), db) + +struct bnxt_qplib_nq { + struct pci_dev *pdev; + + int vector; + int budget; + bool requested; + struct tasklet_struct worker; + struct bnxt_qplib_hwq hwq; + + u16 bar_reg; + u16 bar_reg_off; + u16 ring_id; + void __iomem *bar_reg_iomem; + + int (*cqn_handler) + (struct bnxt_qplib_nq *nq, + struct bnxt_qplib_cq *cq); + int (*srqn_handler) + (struct bnxt_qplib_nq *nq, + void *srq, + u8 event); +}; + +void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq); +int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, + int msix_vector, int bar_reg_offset, + int (*cqn_handler)(struct bnxt_qplib_nq *nq, + struct bnxt_qplib_cq *cq), + int (*srqn_handler)(struct bnxt_qplib_nq *nq, + void *srq, + u8 event)); +int bnxt_qplib_create_qp1(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp); +int bnxt_qplib_create_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp); +int bnxt_qplib_modify_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp); +int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp); +int bnxt_qplib_destroy_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp); +void *bnxt_qplib_get_qp1_sq_buf(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_sge *sge); +void *bnxt_qplib_get_qp1_rq_buf(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_sge *sge); +u32 bnxt_qplib_get_rq_prod_index(struct bnxt_qplib_qp *qp); +dma_addr_t bnxt_qplib_get_qp_buf_from_index(struct bnxt_qplib_qp *qp, + u32 index); +void bnxt_qplib_post_send_db(struct bnxt_qplib_qp *qp); +int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_swqe *wqe); +void bnxt_qplib_post_recv_db(struct bnxt_qplib_qp *qp); +int bnxt_qplib_post_recv(struct bnxt_qplib_qp *qp, + struct bnxt_qplib_swqe *wqe); +int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq); +int bnxt_qplib_destroy_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq); +int bnxt_qplib_poll_cq(struct bnxt_qplib_cq *cq, struct bnxt_qplib_cqe *cqe, + int num); +void bnxt_qplib_req_notify_cq(struct bnxt_qplib_cq *cq, u32 arm_type); +void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq); +int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq); +#endif /* __BNXT_QPLIB_FP_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c new file mode 100644 index 000000000000..23fb7260662b --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -0,0 +1,694 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: RDMA Controller HW interface + */ +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/pci.h> +#include <linux/prefetch.h> +#include "roce_hsi.h" +#include "qplib_res.h" +#include "qplib_rcfw.h" +static void bnxt_qplib_service_creq(unsigned long data); + +/* Hardware communication channel */ +int bnxt_qplib_rcfw_wait_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie) +{ + u16 cbit; + int rc; + + cookie &= RCFW_MAX_COOKIE_VALUE; + cbit = cookie % RCFW_MAX_OUTSTANDING_CMD; + if (!test_bit(cbit, rcfw->cmdq_bitmap)) + dev_warn(&rcfw->pdev->dev, + "QPLIB: CMD bit %d for cookie 0x%x is not set?", + cbit, cookie); + + rc = wait_event_timeout(rcfw->waitq, + !test_bit(cbit, rcfw->cmdq_bitmap), + msecs_to_jiffies(RCFW_CMD_WAIT_TIME_MS)); + if (!rc) { + dev_warn(&rcfw->pdev->dev, + "QPLIB: Bono Error: timeout %d msec, msg {0x%x}\n", + RCFW_CMD_WAIT_TIME_MS, cookie); + } + + return rc; +}; + +int bnxt_qplib_rcfw_block_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie) +{ + u32 count = -1; + u16 cbit; + + cookie &= RCFW_MAX_COOKIE_VALUE; + cbit = cookie % RCFW_MAX_OUTSTANDING_CMD; + if (!test_bit(cbit, rcfw->cmdq_bitmap)) + goto done; + do { + bnxt_qplib_service_creq((unsigned long)rcfw); + } while (test_bit(cbit, rcfw->cmdq_bitmap) && --count); +done: + return count; +}; + +void *bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, + struct cmdq_base *req, void **crsbe, + u8 is_block) +{ + struct bnxt_qplib_crsq *crsq = &rcfw->crsq; + struct bnxt_qplib_cmdqe *cmdqe, **cmdq_ptr; + struct bnxt_qplib_hwq *cmdq = &rcfw->cmdq; + struct bnxt_qplib_hwq *crsb = &rcfw->crsb; + struct bnxt_qplib_crsqe *crsqe = NULL; + struct bnxt_qplib_crsbe **crsb_ptr; + u32 sw_prod, cmdq_prod; + u8 retry_cnt = 0xFF; + dma_addr_t dma_addr; + unsigned long flags; + u32 size, opcode; + u16 cookie, cbit; + int pg, idx; + u8 *preq; + +retry: + opcode = req->opcode; + if (!test_bit(FIRMWARE_INITIALIZED_FLAG, &rcfw->flags) && + (opcode != CMDQ_BASE_OPCODE_QUERY_FUNC && + opcode != CMDQ_BASE_OPCODE_INITIALIZE_FW)) { + dev_err(&rcfw->pdev->dev, + "QPLIB: RCFW not initialized, reject opcode 0x%x", + opcode); + return NULL; + } + + if (test_bit(FIRMWARE_INITIALIZED_FLAG, &rcfw->flags) && + opcode == CMDQ_BASE_OPCODE_INITIALIZE_FW) { + dev_err(&rcfw->pdev->dev, "QPLIB: RCFW already initialized!"); + return NULL; + } + + /* Cmdq are in 16-byte units, each request can consume 1 or more + * cmdqe + */ + spin_lock_irqsave(&cmdq->lock, flags); + if (req->cmd_size > cmdq->max_elements - + ((HWQ_CMP(cmdq->prod, cmdq) - HWQ_CMP(cmdq->cons, cmdq)) & + (cmdq->max_elements - 1))) { + dev_err(&rcfw->pdev->dev, "QPLIB: RCFW: CMDQ is full!"); + spin_unlock_irqrestore(&cmdq->lock, flags); + + if (!retry_cnt--) + return NULL; + goto retry; + } + + retry_cnt = 0xFF; + + cookie = atomic_inc_return(&rcfw->seq_num) & RCFW_MAX_COOKIE_VALUE; + cbit = cookie % RCFW_MAX_OUTSTANDING_CMD; + if (is_block) + cookie |= RCFW_CMD_IS_BLOCKING; + req->cookie = cpu_to_le16(cookie); + if (test_and_set_bit(cbit, rcfw->cmdq_bitmap)) { + dev_err(&rcfw->pdev->dev, + "QPLIB: RCFW MAX outstanding cmd reached!"); + atomic_dec(&rcfw->seq_num); + spin_unlock_irqrestore(&cmdq->lock, flags); + + if (!retry_cnt--) + return NULL; + goto retry; + } + /* Reserve a resp buffer slot if requested */ + if (req->resp_size && crsbe) { + spin_lock(&crsb->lock); + sw_prod = HWQ_CMP(crsb->prod, crsb); + crsb_ptr = (struct bnxt_qplib_crsbe **)crsb->pbl_ptr; + *crsbe = (void *)&crsb_ptr[get_crsb_pg(sw_prod)] + [get_crsb_idx(sw_prod)]; + bnxt_qplib_crsb_dma_next(crsb->pbl_dma_ptr, sw_prod, &dma_addr); + req->resp_addr = cpu_to_le64(dma_addr); + crsb->prod++; + spin_unlock(&crsb->lock); + + req->resp_size = (sizeof(struct bnxt_qplib_crsbe) + + BNXT_QPLIB_CMDQE_UNITS - 1) / + BNXT_QPLIB_CMDQE_UNITS; + } + cmdq_ptr = (struct bnxt_qplib_cmdqe **)cmdq->pbl_ptr; + preq = (u8 *)req; + size = req->cmd_size * BNXT_QPLIB_CMDQE_UNITS; + do { + pg = 0; + idx = 0; + + /* Locate the next cmdq slot */ + sw_prod = HWQ_CMP(cmdq->prod, cmdq); + cmdqe = &cmdq_ptr[get_cmdq_pg(sw_prod)][get_cmdq_idx(sw_prod)]; + if (!cmdqe) { + dev_err(&rcfw->pdev->dev, + "QPLIB: RCFW request failed with no cmdqe!"); + goto done; + } + /* Copy a segment of the req cmd to the cmdq */ + memset(cmdqe, 0, sizeof(*cmdqe)); + memcpy(cmdqe, preq, min_t(u32, size, sizeof(*cmdqe))); + preq += min_t(u32, size, sizeof(*cmdqe)); + size -= min_t(u32, size, sizeof(*cmdqe)); + cmdq->prod++; + } while (size > 0); + + cmdq_prod = cmdq->prod; + if (rcfw->flags & FIRMWARE_FIRST_FLAG) { + /* The very first doorbell write is required to set this flag + * which prompts the FW to reset its internal pointers + */ + cmdq_prod |= FIRMWARE_FIRST_FLAG; + rcfw->flags &= ~FIRMWARE_FIRST_FLAG; + } + sw_prod = HWQ_CMP(crsq->prod, crsq); + crsqe = &crsq->crsq[sw_prod]; + memset(crsqe, 0, sizeof(*crsqe)); + crsq->prod++; + crsqe->req_size = req->cmd_size; + + /* ring CMDQ DB */ + writel(cmdq_prod, rcfw->cmdq_bar_reg_iomem + + rcfw->cmdq_bar_reg_prod_off); + writel(RCFW_CMDQ_TRIG_VAL, rcfw->cmdq_bar_reg_iomem + + rcfw->cmdq_bar_reg_trig_off); +done: + spin_unlock_irqrestore(&cmdq->lock, flags); + /* Return the CREQ response pointer */ + return crsqe ? &crsqe->qp_event : NULL; +} + +/* Completions */ +static int bnxt_qplib_process_func_event(struct bnxt_qplib_rcfw *rcfw, + struct creq_func_event *func_event) +{ + switch (func_event->event) { + case CREQ_FUNC_EVENT_EVENT_TX_WQE_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_TX_DATA_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_RX_WQE_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_RX_DATA_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CQ_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_TQM_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CFCQ_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CFCS_ERROR: + /* SRQ ctx error, call srq_handler?? + * But there's no SRQ handle! + */ + break; + case CREQ_FUNC_EVENT_EVENT_CFCC_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_CFCM_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_TIM_ERROR: + break; + case CREQ_FUNC_EVENT_EVENT_VF_COMM_REQUEST: + break; + case CREQ_FUNC_EVENT_EVENT_RESOURCE_EXHAUSTED: + break; + default: + return -EINVAL; + } + return 0; +} + +static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, + struct creq_qp_event *qp_event) +{ + struct bnxt_qplib_crsq *crsq = &rcfw->crsq; + struct bnxt_qplib_hwq *cmdq = &rcfw->cmdq; + struct bnxt_qplib_crsqe *crsqe; + u16 cbit, cookie, blocked = 0; + unsigned long flags; + u32 sw_cons; + + switch (qp_event->event) { + case CREQ_QP_EVENT_EVENT_QP_ERROR_NOTIFICATION: + dev_dbg(&rcfw->pdev->dev, + "QPLIB: Received QP error notification"); + break; + default: + /* Command Response */ + spin_lock_irqsave(&cmdq->lock, flags); + sw_cons = HWQ_CMP(crsq->cons, crsq); + crsqe = &crsq->crsq[sw_cons]; + crsq->cons++; + memcpy(&crsqe->qp_event, qp_event, sizeof(crsqe->qp_event)); + + cookie = le16_to_cpu(crsqe->qp_event.cookie); + blocked = cookie & RCFW_CMD_IS_BLOCKING; + cookie &= RCFW_MAX_COOKIE_VALUE; + cbit = cookie % RCFW_MAX_OUTSTANDING_CMD; + if (!test_and_clear_bit(cbit, rcfw->cmdq_bitmap)) + dev_warn(&rcfw->pdev->dev, + "QPLIB: CMD bit %d was not requested", cbit); + + cmdq->cons += crsqe->req_size; + spin_unlock_irqrestore(&cmdq->lock, flags); + if (!blocked) + wake_up(&rcfw->waitq); + break; + } + return 0; +} + +/* SP - CREQ Completion handlers */ +static void bnxt_qplib_service_creq(unsigned long data) +{ + struct bnxt_qplib_rcfw *rcfw = (struct bnxt_qplib_rcfw *)data; + struct bnxt_qplib_hwq *creq = &rcfw->creq; + struct creq_base *creqe, **creq_ptr; + u32 sw_cons, raw_cons; + unsigned long flags; + u32 type; + + /* Service the CREQ until empty */ + spin_lock_irqsave(&creq->lock, flags); + raw_cons = creq->cons; + while (1) { + sw_cons = HWQ_CMP(raw_cons, creq); + creq_ptr = (struct creq_base **)creq->pbl_ptr; + creqe = &creq_ptr[get_creq_pg(sw_cons)][get_creq_idx(sw_cons)]; + if (!CREQ_CMP_VALID(creqe, raw_cons, creq->max_elements)) + break; + + type = creqe->type & CREQ_BASE_TYPE_MASK; + switch (type) { + case CREQ_BASE_TYPE_QP_EVENT: + if (!bnxt_qplib_process_qp_event + (rcfw, (struct creq_qp_event *)creqe)) + rcfw->creq_qp_event_processed++; + else { + dev_warn(&rcfw->pdev->dev, "QPLIB: crsqe with"); + dev_warn(&rcfw->pdev->dev, + "QPLIB: type = 0x%x not handled", + type); + } + break; + case CREQ_BASE_TYPE_FUNC_EVENT: + if (!bnxt_qplib_process_func_event + (rcfw, (struct creq_func_event *)creqe)) + rcfw->creq_func_event_processed++; + else + dev_warn + (&rcfw->pdev->dev, "QPLIB:aeqe:%#x Not handled", + type); + break; + default: + dev_warn(&rcfw->pdev->dev, "QPLIB: creqe with "); + dev_warn(&rcfw->pdev->dev, + "QPLIB: op_event = 0x%x not handled", type); + break; + } + raw_cons++; + } + if (creq->cons != raw_cons) { + creq->cons = raw_cons; + CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, raw_cons, + creq->max_elements); + } + spin_unlock_irqrestore(&creq->lock, flags); +} + +static irqreturn_t bnxt_qplib_creq_irq(int irq, void *dev_instance) +{ + struct bnxt_qplib_rcfw *rcfw = dev_instance; + struct bnxt_qplib_hwq *creq = &rcfw->creq; + struct creq_base **creq_ptr; + u32 sw_cons; + + /* Prefetch the CREQ element */ + sw_cons = HWQ_CMP(creq->cons, creq); + creq_ptr = (struct creq_base **)rcfw->creq.pbl_ptr; + prefetch(&creq_ptr[get_creq_pg(sw_cons)][get_creq_idx(sw_cons)]); + + tasklet_schedule(&rcfw->worker); + + return IRQ_HANDLED; +} + +/* RCFW */ +int bnxt_qplib_deinit_rcfw(struct bnxt_qplib_rcfw *rcfw) +{ + struct creq_deinitialize_fw_resp *resp; + struct cmdq_deinitialize_fw req; + u16 cmd_flags = 0; + + RCFW_CMD_PREP(req, DEINITIALIZE_FW, cmd_flags); + resp = (struct creq_deinitialize_fw_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) + return -EINVAL; + + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) + return -ETIMEDOUT; + + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) + return -EFAULT; + + clear_bit(FIRMWARE_INITIALIZED_FLAG, &rcfw->flags); + return 0; +} + +static int __get_pbl_pg_idx(struct bnxt_qplib_pbl *pbl) +{ + return (pbl->pg_size == ROCE_PG_SIZE_4K ? + CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_4K : + pbl->pg_size == ROCE_PG_SIZE_8K ? + CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_8K : + pbl->pg_size == ROCE_PG_SIZE_64K ? + CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_64K : + pbl->pg_size == ROCE_PG_SIZE_2M ? + CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_2M : + pbl->pg_size == ROCE_PG_SIZE_8M ? + CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_8M : + pbl->pg_size == ROCE_PG_SIZE_1G ? + CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_1G : + CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_4K); +} + +int bnxt_qplib_init_rcfw(struct bnxt_qplib_rcfw *rcfw, + struct bnxt_qplib_ctx *ctx, int is_virtfn) +{ + struct creq_initialize_fw_resp *resp; + struct cmdq_initialize_fw req; + u16 cmd_flags = 0, level; + + RCFW_CMD_PREP(req, INITIALIZE_FW, cmd_flags); + + /* + * VFs need not setup the HW context area, PF + * shall setup this area for VF. Skipping the + * HW programming + */ + if (is_virtfn) + goto skip_ctx_setup; + + level = ctx->qpc_tbl.level; + req.qpc_pg_size_qpc_lvl = (level << CMDQ_INITIALIZE_FW_QPC_LVL_SFT) | + __get_pbl_pg_idx(&ctx->qpc_tbl.pbl[level]); + level = ctx->mrw_tbl.level; + req.mrw_pg_size_mrw_lvl = (level << CMDQ_INITIALIZE_FW_MRW_LVL_SFT) | + __get_pbl_pg_idx(&ctx->mrw_tbl.pbl[level]); + level = ctx->srqc_tbl.level; + req.srq_pg_size_srq_lvl = (level << CMDQ_INITIALIZE_FW_SRQ_LVL_SFT) | + __get_pbl_pg_idx(&ctx->srqc_tbl.pbl[level]); + level = ctx->cq_tbl.level; + req.cq_pg_size_cq_lvl = (level << CMDQ_INITIALIZE_FW_CQ_LVL_SFT) | + __get_pbl_pg_idx(&ctx->cq_tbl.pbl[level]); + level = ctx->srqc_tbl.level; + req.srq_pg_size_srq_lvl = (level << CMDQ_INITIALIZE_FW_SRQ_LVL_SFT) | + __get_pbl_pg_idx(&ctx->srqc_tbl.pbl[level]); + level = ctx->cq_tbl.level; + req.cq_pg_size_cq_lvl = (level << CMDQ_INITIALIZE_FW_CQ_LVL_SFT) | + __get_pbl_pg_idx(&ctx->cq_tbl.pbl[level]); + level = ctx->tim_tbl.level; + req.tim_pg_size_tim_lvl = (level << CMDQ_INITIALIZE_FW_TIM_LVL_SFT) | + __get_pbl_pg_idx(&ctx->tim_tbl.pbl[level]); + level = ctx->tqm_pde_level; + req.tqm_pg_size_tqm_lvl = (level << CMDQ_INITIALIZE_FW_TQM_LVL_SFT) | + __get_pbl_pg_idx(&ctx->tqm_pde.pbl[level]); + + req.qpc_page_dir = + cpu_to_le64(ctx->qpc_tbl.pbl[PBL_LVL_0].pg_map_arr[0]); + req.mrw_page_dir = + cpu_to_le64(ctx->mrw_tbl.pbl[PBL_LVL_0].pg_map_arr[0]); + req.srq_page_dir = + cpu_to_le64(ctx->srqc_tbl.pbl[PBL_LVL_0].pg_map_arr[0]); + req.cq_page_dir = + cpu_to_le64(ctx->cq_tbl.pbl[PBL_LVL_0].pg_map_arr[0]); + req.tim_page_dir = + cpu_to_le64(ctx->tim_tbl.pbl[PBL_LVL_0].pg_map_arr[0]); + req.tqm_page_dir = + cpu_to_le64(ctx->tqm_pde.pbl[PBL_LVL_0].pg_map_arr[0]); + + req.number_of_qp = cpu_to_le32(ctx->qpc_tbl.max_elements); + req.number_of_mrw = cpu_to_le32(ctx->mrw_tbl.max_elements); + req.number_of_srq = cpu_to_le32(ctx->srqc_tbl.max_elements); + req.number_of_cq = cpu_to_le32(ctx->cq_tbl.max_elements); + + req.max_qp_per_vf = cpu_to_le32(ctx->vf_res.max_qp_per_vf); + req.max_mrw_per_vf = cpu_to_le32(ctx->vf_res.max_mrw_per_vf); + req.max_srq_per_vf = cpu_to_le32(ctx->vf_res.max_srq_per_vf); + req.max_cq_per_vf = cpu_to_le32(ctx->vf_res.max_cq_per_vf); + req.max_gid_per_vf = cpu_to_le32(ctx->vf_res.max_gid_per_vf); + +skip_ctx_setup: + req.stat_ctx_id = cpu_to_le32(ctx->stats.fw_id); + resp = (struct creq_initialize_fw_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, + "QPLIB: RCFW: INITIALIZE_FW send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, + "QPLIB: RCFW: INITIALIZE_FW timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, + "QPLIB: RCFW: INITIALIZE_FW failed"); + return -EINVAL; + } + set_bit(FIRMWARE_INITIALIZED_FLAG, &rcfw->flags); + return 0; +} + +void bnxt_qplib_free_rcfw_channel(struct bnxt_qplib_rcfw *rcfw) +{ + bnxt_qplib_free_hwq(rcfw->pdev, &rcfw->crsb); + kfree(rcfw->crsq.crsq); + bnxt_qplib_free_hwq(rcfw->pdev, &rcfw->cmdq); + bnxt_qplib_free_hwq(rcfw->pdev, &rcfw->creq); + + rcfw->pdev = NULL; +} + +int bnxt_qplib_alloc_rcfw_channel(struct pci_dev *pdev, + struct bnxt_qplib_rcfw *rcfw) +{ + rcfw->pdev = pdev; + rcfw->creq.max_elements = BNXT_QPLIB_CREQE_MAX_CNT; + if (bnxt_qplib_alloc_init_hwq(rcfw->pdev, &rcfw->creq, NULL, 0, + &rcfw->creq.max_elements, + BNXT_QPLIB_CREQE_UNITS, 0, PAGE_SIZE, + HWQ_TYPE_L2_CMPL)) { + dev_err(&rcfw->pdev->dev, + "QPLIB: HW channel CREQ allocation failed"); + goto fail; + } + rcfw->cmdq.max_elements = BNXT_QPLIB_CMDQE_MAX_CNT; + if (bnxt_qplib_alloc_init_hwq(rcfw->pdev, &rcfw->cmdq, NULL, 0, + &rcfw->cmdq.max_elements, + BNXT_QPLIB_CMDQE_UNITS, 0, PAGE_SIZE, + HWQ_TYPE_CTX)) { + dev_err(&rcfw->pdev->dev, + "QPLIB: HW channel CMDQ allocation failed"); + goto fail; + } + + rcfw->crsq.max_elements = rcfw->cmdq.max_elements; + rcfw->crsq.crsq = kcalloc(rcfw->crsq.max_elements, + sizeof(*rcfw->crsq.crsq), GFP_KERNEL); + if (!rcfw->crsq.crsq) + goto fail; + + rcfw->crsb.max_elements = BNXT_QPLIB_CRSBE_MAX_CNT; + if (bnxt_qplib_alloc_init_hwq(rcfw->pdev, &rcfw->crsb, NULL, 0, + &rcfw->crsb.max_elements, + BNXT_QPLIB_CRSBE_UNITS, 0, PAGE_SIZE, + HWQ_TYPE_CTX)) { + dev_err(&rcfw->pdev->dev, + "QPLIB: HW channel CRSB allocation failed"); + goto fail; + } + return 0; + +fail: + bnxt_qplib_free_rcfw_channel(rcfw); + return -ENOMEM; +} + +void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw) +{ + unsigned long indx; + + /* Make sure the HW channel is stopped! */ + synchronize_irq(rcfw->vector); + tasklet_disable(&rcfw->worker); + tasklet_kill(&rcfw->worker); + + if (rcfw->requested) { + free_irq(rcfw->vector, rcfw); + rcfw->requested = false; + } + if (rcfw->cmdq_bar_reg_iomem) + iounmap(rcfw->cmdq_bar_reg_iomem); + rcfw->cmdq_bar_reg_iomem = NULL; + + if (rcfw->creq_bar_reg_iomem) + iounmap(rcfw->creq_bar_reg_iomem); + rcfw->creq_bar_reg_iomem = NULL; + + indx = find_first_bit(rcfw->cmdq_bitmap, rcfw->bmap_size); + if (indx != rcfw->bmap_size) + dev_err(&rcfw->pdev->dev, + "QPLIB: disabling RCFW with pending cmd-bit %lx", indx); + kfree(rcfw->cmdq_bitmap); + rcfw->bmap_size = 0; + + rcfw->aeq_handler = NULL; + rcfw->vector = 0; +} + +int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev, + struct bnxt_qplib_rcfw *rcfw, + int msix_vector, + int cp_bar_reg_off, int virt_fn, + int (*aeq_handler)(struct bnxt_qplib_rcfw *, + struct creq_func_event *)) +{ + resource_size_t res_base; + struct cmdq_init init; + u16 bmap_size; + int rc; + + /* General */ + atomic_set(&rcfw->seq_num, 0); + rcfw->flags = FIRMWARE_FIRST_FLAG; + bmap_size = BITS_TO_LONGS(RCFW_MAX_OUTSTANDING_CMD * + sizeof(unsigned long)); + rcfw->cmdq_bitmap = kzalloc(bmap_size, GFP_KERNEL); + if (!rcfw->cmdq_bitmap) + return -ENOMEM; + rcfw->bmap_size = bmap_size; + + /* CMDQ */ + rcfw->cmdq_bar_reg = RCFW_COMM_PCI_BAR_REGION; + res_base = pci_resource_start(pdev, rcfw->cmdq_bar_reg); + if (!res_base) + return -ENOMEM; + + rcfw->cmdq_bar_reg_iomem = ioremap_nocache(res_base + + RCFW_COMM_BASE_OFFSET, + RCFW_COMM_SIZE); + if (!rcfw->cmdq_bar_reg_iomem) { + dev_err(&rcfw->pdev->dev, + "QPLIB: CMDQ BAR region %d mapping failed", + rcfw->cmdq_bar_reg); + return -ENOMEM; + } + + rcfw->cmdq_bar_reg_prod_off = virt_fn ? RCFW_VF_COMM_PROD_OFFSET : + RCFW_PF_COMM_PROD_OFFSET; + + rcfw->cmdq_bar_reg_trig_off = RCFW_COMM_TRIG_OFFSET; + + /* CRSQ */ + rcfw->crsq.prod = 0; + rcfw->crsq.cons = 0; + + /* CREQ */ + rcfw->creq_bar_reg = RCFW_COMM_CONS_PCI_BAR_REGION; + res_base = pci_resource_start(pdev, rcfw->creq_bar_reg); + if (!res_base) + dev_err(&rcfw->pdev->dev, + "QPLIB: CREQ BAR region %d resc start is 0!", + rcfw->creq_bar_reg); + rcfw->creq_bar_reg_iomem = ioremap_nocache(res_base + cp_bar_reg_off, + 4); + if (!rcfw->creq_bar_reg_iomem) { + dev_err(&rcfw->pdev->dev, + "QPLIB: CREQ BAR region %d mapping failed", + rcfw->creq_bar_reg); + return -ENOMEM; + } + rcfw->creq_qp_event_processed = 0; + rcfw->creq_func_event_processed = 0; + + rcfw->vector = msix_vector; + if (aeq_handler) + rcfw->aeq_handler = aeq_handler; + + tasklet_init(&rcfw->worker, bnxt_qplib_service_creq, + (unsigned long)rcfw); + + rcfw->requested = false; + rc = request_irq(rcfw->vector, bnxt_qplib_creq_irq, 0, + "bnxt_qplib_creq", rcfw); + if (rc) { + dev_err(&rcfw->pdev->dev, + "QPLIB: Failed to request IRQ for CREQ rc = 0x%x", rc); + bnxt_qplib_disable_rcfw_channel(rcfw); + return rc; + } + rcfw->requested = true; + + init_waitqueue_head(&rcfw->waitq); + + CREQ_DB_REARM(rcfw->creq_bar_reg_iomem, 0, rcfw->creq.max_elements); + + init.cmdq_pbl = cpu_to_le64(rcfw->cmdq.pbl[PBL_LVL_0].pg_map_arr[0]); + init.cmdq_size_cmdq_lvl = cpu_to_le16( + ((BNXT_QPLIB_CMDQE_MAX_CNT << CMDQ_INIT_CMDQ_SIZE_SFT) & + CMDQ_INIT_CMDQ_SIZE_MASK) | + ((rcfw->cmdq.level << CMDQ_INIT_CMDQ_LVL_SFT) & + CMDQ_INIT_CMDQ_LVL_MASK)); + init.creq_ring_id = cpu_to_le16(rcfw->creq_ring_id); + + /* Write to the Bono mailbox register */ + __iowrite32_copy(rcfw->cmdq_bar_reg_iomem, &init, sizeof(init) / 4); + return 0; +} diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h new file mode 100644 index 000000000000..d3567d75bf58 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h @@ -0,0 +1,231 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: RDMA Controller HW interface (header) + */ + +#ifndef __BNXT_QPLIB_RCFW_H__ +#define __BNXT_QPLIB_RCFW_H__ + +#define RCFW_CMDQ_TRIG_VAL 1 +#define RCFW_COMM_PCI_BAR_REGION 0 +#define RCFW_COMM_CONS_PCI_BAR_REGION 2 +#define RCFW_COMM_BASE_OFFSET 0x600 +#define RCFW_PF_COMM_PROD_OFFSET 0xc +#define RCFW_VF_COMM_PROD_OFFSET 0xc +#define RCFW_COMM_TRIG_OFFSET 0x100 +#define RCFW_COMM_SIZE 0x104 + +#define RCFW_DBR_PCI_BAR_REGION 2 + +#define RCFW_CMD_PREP(req, CMD, cmd_flags) \ + do { \ + memset(&(req), 0, sizeof((req))); \ + (req).opcode = CMDQ_BASE_OPCODE_##CMD; \ + (req).cmd_size = (sizeof((req)) + \ + BNXT_QPLIB_CMDQE_UNITS - 1) / \ + BNXT_QPLIB_CMDQE_UNITS; \ + (req).flags = cpu_to_le16(cmd_flags); \ + } while (0) + +#define RCFW_CMD_WAIT_TIME_MS 20000 /* 20 Seconds timeout */ + +/* CMDQ elements */ +#define BNXT_QPLIB_CMDQE_MAX_CNT 256 +#define BNXT_QPLIB_CMDQE_UNITS sizeof(struct bnxt_qplib_cmdqe) +#define BNXT_QPLIB_CMDQE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_CMDQE_UNITS) + +#define MAX_CMDQ_IDX (BNXT_QPLIB_CMDQE_MAX_CNT - 1) +#define MAX_CMDQ_IDX_PER_PG (BNXT_QPLIB_CMDQE_CNT_PER_PG - 1) + +#define RCFW_MAX_OUTSTANDING_CMD BNXT_QPLIB_CMDQE_MAX_CNT +#define RCFW_MAX_COOKIE_VALUE 0x7FFF +#define RCFW_CMD_IS_BLOCKING 0x8000 + +/* Cmdq contains a fix number of a 16-Byte slots */ +struct bnxt_qplib_cmdqe { + u8 data[16]; +}; + +static inline u32 get_cmdq_pg(u32 val) +{ + return (val & ~MAX_CMDQ_IDX_PER_PG) / BNXT_QPLIB_CMDQE_CNT_PER_PG; +} + +static inline u32 get_cmdq_idx(u32 val) +{ + return val & MAX_CMDQ_IDX_PER_PG; +} + +/* Crsq buf is 1024-Byte */ +struct bnxt_qplib_crsbe { + u8 data[1024]; +}; + +/* CRSQ SB */ +#define BNXT_QPLIB_CRSBE_MAX_CNT 4 +#define BNXT_QPLIB_CRSBE_UNITS sizeof(struct bnxt_qplib_crsbe) +#define BNXT_QPLIB_CRSBE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_CRSBE_UNITS) + +#define MAX_CRSB_IDX (BNXT_QPLIB_CRSBE_MAX_CNT - 1) +#define MAX_CRSB_IDX_PER_PG (BNXT_QPLIB_CRSBE_CNT_PER_PG - 1) + +static inline u32 get_crsb_pg(u32 val) +{ + return (val & ~MAX_CRSB_IDX_PER_PG) / BNXT_QPLIB_CRSBE_CNT_PER_PG; +} + +static inline u32 get_crsb_idx(u32 val) +{ + return val & MAX_CRSB_IDX_PER_PG; +} + +static inline void bnxt_qplib_crsb_dma_next(dma_addr_t *pg_map_arr, + u32 prod, dma_addr_t *dma_addr) +{ + *dma_addr = pg_map_arr[(prod) / BNXT_QPLIB_CRSBE_CNT_PER_PG]; + *dma_addr += ((prod) % BNXT_QPLIB_CRSBE_CNT_PER_PG) * + BNXT_QPLIB_CRSBE_UNITS; +} + +/* CREQ */ +/* Allocate 1 per QP for async error notification for now */ +#define BNXT_QPLIB_CREQE_MAX_CNT (64 * 1024) +#define BNXT_QPLIB_CREQE_UNITS 16 /* 16-Bytes per prod unit */ +#define BNXT_QPLIB_CREQE_CNT_PER_PG (PAGE_SIZE / BNXT_QPLIB_CREQE_UNITS) + +#define MAX_CREQ_IDX (BNXT_QPLIB_CREQE_MAX_CNT - 1) +#define MAX_CREQ_IDX_PER_PG (BNXT_QPLIB_CREQE_CNT_PER_PG - 1) + +static inline u32 get_creq_pg(u32 val) +{ + return (val & ~MAX_CREQ_IDX_PER_PG) / BNXT_QPLIB_CREQE_CNT_PER_PG; +} + +static inline u32 get_creq_idx(u32 val) +{ + return val & MAX_CREQ_IDX_PER_PG; +} + +#define BNXT_QPLIB_CREQE_PER_PG (PAGE_SIZE / sizeof(struct creq_base)) + +#define CREQ_CMP_VALID(hdr, raw_cons, cp_bit) \ + (!!((hdr)->v & CREQ_BASE_V) == \ + !((raw_cons) & (cp_bit))) + +#define CREQ_DB_KEY_CP (0x2 << CMPL_DOORBELL_KEY_SFT) +#define CREQ_DB_IDX_VALID CMPL_DOORBELL_IDX_VALID +#define CREQ_DB_IRQ_DIS CMPL_DOORBELL_MASK +#define CREQ_DB_CP_FLAGS_REARM (CREQ_DB_KEY_CP | \ + CREQ_DB_IDX_VALID) +#define CREQ_DB_CP_FLAGS (CREQ_DB_KEY_CP | \ + CREQ_DB_IDX_VALID | \ + CREQ_DB_IRQ_DIS) +#define CREQ_DB_REARM(db, raw_cons, cp_bit) \ + writel(CREQ_DB_CP_FLAGS_REARM | ((raw_cons) & ((cp_bit) - 1)), db) +#define CREQ_DB(db, raw_cons, cp_bit) \ + writel(CREQ_DB_CP_FLAGS | ((raw_cons) & ((cp_bit) - 1)), db) + +/* HWQ */ +struct bnxt_qplib_crsqe { + struct creq_qp_event qp_event; + u32 req_size; +}; + +struct bnxt_qplib_crsq { + struct bnxt_qplib_crsqe *crsq; + u32 prod; + u32 cons; + u32 max_elements; +}; + +/* RCFW Communication Channels */ +struct bnxt_qplib_rcfw { + struct pci_dev *pdev; + int vector; + struct tasklet_struct worker; + bool requested; + unsigned long *cmdq_bitmap; + u32 bmap_size; + unsigned long flags; +#define FIRMWARE_INITIALIZED_FLAG 1 +#define FIRMWARE_FIRST_FLAG BIT(31) + wait_queue_head_t waitq; + int (*aeq_handler)(struct bnxt_qplib_rcfw *, + struct creq_func_event *); + atomic_t seq_num; + + /* Bar region info */ + void __iomem *cmdq_bar_reg_iomem; + u16 cmdq_bar_reg; + u16 cmdq_bar_reg_prod_off; + u16 cmdq_bar_reg_trig_off; + u16 creq_ring_id; + u16 creq_bar_reg; + void __iomem *creq_bar_reg_iomem; + + /* Cmd-Resp and Async Event notification queue */ + struct bnxt_qplib_hwq creq; + u64 creq_qp_event_processed; + u64 creq_func_event_processed; + + /* Actual Cmd and Resp Queues */ + struct bnxt_qplib_hwq cmdq; + struct bnxt_qplib_crsq crsq; + struct bnxt_qplib_hwq crsb; +}; + +void bnxt_qplib_free_rcfw_channel(struct bnxt_qplib_rcfw *rcfw); +int bnxt_qplib_alloc_rcfw_channel(struct pci_dev *pdev, + struct bnxt_qplib_rcfw *rcfw); +void bnxt_qplib_disable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw); +int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev, + struct bnxt_qplib_rcfw *rcfw, + int msix_vector, + int cp_bar_reg_off, int virt_fn, + int (*aeq_handler) + (struct bnxt_qplib_rcfw *, + struct creq_func_event *)); + +int bnxt_qplib_rcfw_block_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie); +int bnxt_qplib_rcfw_wait_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie); +void *bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, + struct cmdq_base *req, void **crsbe, + u8 is_block); + +int bnxt_qplib_deinit_rcfw(struct bnxt_qplib_rcfw *rcfw); +int bnxt_qplib_init_rcfw(struct bnxt_qplib_rcfw *rcfw, + struct bnxt_qplib_ctx *ctx, int is_virtfn); +#endif /* __BNXT_QPLIB_RCFW_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c new file mode 100644 index 000000000000..62447b3badec --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -0,0 +1,825 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: QPLib resource manager + */ + +#include <linux/spinlock.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/inetdevice.h> +#include <linux/dma-mapping.h> +#include <linux/if_vlan.h> +#include "roce_hsi.h" +#include "qplib_res.h" +#include "qplib_sp.h" +#include "qplib_rcfw.h" + +static void bnxt_qplib_free_stats_ctx(struct pci_dev *pdev, + struct bnxt_qplib_stats *stats); +static int bnxt_qplib_alloc_stats_ctx(struct pci_dev *pdev, + struct bnxt_qplib_stats *stats); + +/* PBL */ +static void __free_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl, + bool is_umem) +{ + int i; + + if (!is_umem) { + for (i = 0; i < pbl->pg_count; i++) { + if (pbl->pg_arr[i]) + dma_free_coherent(&pdev->dev, pbl->pg_size, + (void *)((unsigned long) + pbl->pg_arr[i] & + PAGE_MASK), + pbl->pg_map_arr[i]); + else + dev_warn(&pdev->dev, + "QPLIB: PBL free pg_arr[%d] empty?!", + i); + pbl->pg_arr[i] = NULL; + } + } + kfree(pbl->pg_arr); + pbl->pg_arr = NULL; + kfree(pbl->pg_map_arr); + pbl->pg_map_arr = NULL; + pbl->pg_count = 0; + pbl->pg_size = 0; +} + +static int __alloc_pbl(struct pci_dev *pdev, struct bnxt_qplib_pbl *pbl, + struct scatterlist *sghead, u32 pages, u32 pg_size) +{ + struct scatterlist *sg; + bool is_umem = false; + int i; + + /* page ptr arrays */ + pbl->pg_arr = kcalloc(pages, sizeof(void *), GFP_KERNEL); + if (!pbl->pg_arr) + return -ENOMEM; + + pbl->pg_map_arr = kcalloc(pages, sizeof(dma_addr_t), GFP_KERNEL); + if (!pbl->pg_map_arr) { + kfree(pbl->pg_arr); + pbl->pg_arr = NULL; + return -ENOMEM; + } + pbl->pg_count = 0; + pbl->pg_size = pg_size; + + if (!sghead) { + for (i = 0; i < pages; i++) { + pbl->pg_arr[i] = dma_alloc_coherent(&pdev->dev, + pbl->pg_size, + &pbl->pg_map_arr[i], + GFP_KERNEL); + if (!pbl->pg_arr[i]) + goto fail; + memset(pbl->pg_arr[i], 0, pbl->pg_size); + pbl->pg_count++; + } + } else { + i = 0; + is_umem = true; + for_each_sg(sghead, sg, pages, i) { + pbl->pg_map_arr[i] = sg_dma_address(sg); + pbl->pg_arr[i] = sg_virt(sg); + if (!pbl->pg_arr[i]) + goto fail; + + pbl->pg_count++; + } + } + + return 0; + +fail: + __free_pbl(pdev, pbl, is_umem); + return -ENOMEM; +} + +/* HWQ */ +void bnxt_qplib_free_hwq(struct pci_dev *pdev, struct bnxt_qplib_hwq *hwq) +{ + int i; + + if (!hwq->max_elements) + return; + if (hwq->level >= PBL_LVL_MAX) + return; + + for (i = 0; i < hwq->level + 1; i++) { + if (i == hwq->level) + __free_pbl(pdev, &hwq->pbl[i], hwq->is_user); + else + __free_pbl(pdev, &hwq->pbl[i], false); + } + + hwq->level = PBL_LVL_MAX; + hwq->max_elements = 0; + hwq->element_size = 0; + hwq->prod = 0; + hwq->cons = 0; + hwq->cp_bit = 0; +} + +/* All HWQs are power of 2 in size */ +int bnxt_qplib_alloc_init_hwq(struct pci_dev *pdev, struct bnxt_qplib_hwq *hwq, + struct scatterlist *sghead, int nmap, + u32 *elements, u32 element_size, u32 aux, + u32 pg_size, enum bnxt_qplib_hwq_type hwq_type) +{ + u32 pages, slots, size, aux_pages = 0, aux_size = 0; + dma_addr_t *src_phys_ptr, **dst_virt_ptr; + int i, rc; + + hwq->level = PBL_LVL_MAX; + + slots = roundup_pow_of_two(*elements); + if (aux) { + aux_size = roundup_pow_of_two(aux); + aux_pages = (slots * aux_size) / pg_size; + if ((slots * aux_size) % pg_size) + aux_pages++; + } + size = roundup_pow_of_two(element_size); + + if (!sghead) { + hwq->is_user = false; + pages = (slots * size) / pg_size + aux_pages; + if ((slots * size) % pg_size) + pages++; + if (!pages) + return -EINVAL; + } else { + hwq->is_user = true; + pages = nmap; + } + + /* Alloc the 1st memory block; can be a PDL/PTL/PBL */ + if (sghead && (pages == MAX_PBL_LVL_0_PGS)) + rc = __alloc_pbl(pdev, &hwq->pbl[PBL_LVL_0], sghead, + pages, pg_size); + else + rc = __alloc_pbl(pdev, &hwq->pbl[PBL_LVL_0], NULL, 1, pg_size); + if (rc) + goto fail; + + hwq->level = PBL_LVL_0; + + if (pages > MAX_PBL_LVL_0_PGS) { + if (pages > MAX_PBL_LVL_1_PGS) { + /* 2 levels of indirection */ + rc = __alloc_pbl(pdev, &hwq->pbl[PBL_LVL_1], NULL, + MAX_PBL_LVL_1_PGS_FOR_LVL_2, pg_size); + if (rc) + goto fail; + /* Fill in lvl0 PBL */ + dst_virt_ptr = + (dma_addr_t **)hwq->pbl[PBL_LVL_0].pg_arr; + src_phys_ptr = hwq->pbl[PBL_LVL_1].pg_map_arr; + for (i = 0; i < hwq->pbl[PBL_LVL_1].pg_count; i++) + dst_virt_ptr[PTR_PG(i)][PTR_IDX(i)] = + src_phys_ptr[i] | PTU_PDE_VALID; + hwq->level = PBL_LVL_1; + + rc = __alloc_pbl(pdev, &hwq->pbl[PBL_LVL_2], sghead, + pages, pg_size); + if (rc) + goto fail; + + /* Fill in lvl1 PBL */ + dst_virt_ptr = + (dma_addr_t **)hwq->pbl[PBL_LVL_1].pg_arr; + src_phys_ptr = hwq->pbl[PBL_LVL_2].pg_map_arr; + for (i = 0; i < hwq->pbl[PBL_LVL_2].pg_count; i++) { + dst_virt_ptr[PTR_PG(i)][PTR_IDX(i)] = + src_phys_ptr[i] | PTU_PTE_VALID; + } + if (hwq_type == HWQ_TYPE_QUEUE) { + /* Find the last pg of the size */ + i = hwq->pbl[PBL_LVL_2].pg_count; + dst_virt_ptr[PTR_PG(i - 1)][PTR_IDX(i - 1)] |= + PTU_PTE_LAST; + if (i > 1) + dst_virt_ptr[PTR_PG(i - 2)] + [PTR_IDX(i - 2)] |= + PTU_PTE_NEXT_TO_LAST; + } + hwq->level = PBL_LVL_2; + } else { + u32 flag = hwq_type == HWQ_TYPE_L2_CMPL ? 0 : + PTU_PTE_VALID; + + /* 1 level of indirection */ + rc = __alloc_pbl(pdev, &hwq->pbl[PBL_LVL_1], sghead, + pages, pg_size); + if (rc) + goto fail; + /* Fill in lvl0 PBL */ + dst_virt_ptr = + (dma_addr_t **)hwq->pbl[PBL_LVL_0].pg_arr; + src_phys_ptr = hwq->pbl[PBL_LVL_1].pg_map_arr; + for (i = 0; i < hwq->pbl[PBL_LVL_1].pg_count; i++) { + dst_virt_ptr[PTR_PG(i)][PTR_IDX(i)] = + src_phys_ptr[i] | flag; + } + if (hwq_type == HWQ_TYPE_QUEUE) { + /* Find the last pg of the size */ + i = hwq->pbl[PBL_LVL_1].pg_count; + dst_virt_ptr[PTR_PG(i - 1)][PTR_IDX(i - 1)] |= + PTU_PTE_LAST; + if (i > 1) + dst_virt_ptr[PTR_PG(i - 2)] + [PTR_IDX(i - 2)] |= + PTU_PTE_NEXT_TO_LAST; + } + hwq->level = PBL_LVL_1; + } + } + hwq->pdev = pdev; + spin_lock_init(&hwq->lock); + hwq->prod = 0; + hwq->cons = 0; + *elements = hwq->max_elements = slots; + hwq->element_size = size; + + /* For direct access to the elements */ + hwq->pbl_ptr = hwq->pbl[hwq->level].pg_arr; + hwq->pbl_dma_ptr = hwq->pbl[hwq->level].pg_map_arr; + + return 0; + +fail: + bnxt_qplib_free_hwq(pdev, hwq); + return -ENOMEM; +} + +/* Context Tables */ +void bnxt_qplib_free_ctx(struct pci_dev *pdev, + struct bnxt_qplib_ctx *ctx) +{ + int i; + + bnxt_qplib_free_hwq(pdev, &ctx->qpc_tbl); + bnxt_qplib_free_hwq(pdev, &ctx->mrw_tbl); + bnxt_qplib_free_hwq(pdev, &ctx->srqc_tbl); + bnxt_qplib_free_hwq(pdev, &ctx->cq_tbl); + bnxt_qplib_free_hwq(pdev, &ctx->tim_tbl); + for (i = 0; i < MAX_TQM_ALLOC_REQ; i++) + bnxt_qplib_free_hwq(pdev, &ctx->tqm_tbl[i]); + bnxt_qplib_free_hwq(pdev, &ctx->tqm_pde); + bnxt_qplib_free_stats_ctx(pdev, &ctx->stats); +} + +/* + * Routine: bnxt_qplib_alloc_ctx + * Description: + * Context tables are memories which are used by the chip fw. + * The 6 tables defined are: + * QPC ctx - holds QP states + * MRW ctx - holds memory region and window + * SRQ ctx - holds shared RQ states + * CQ ctx - holds completion queue states + * TQM ctx - holds Tx Queue Manager context + * TIM ctx - holds timer context + * Depending on the size of the tbl requested, either a 1 Page Buffer List + * or a 1-to-2-stage indirection Page Directory List + 1 PBL is used + * instead. + * Table might be employed as follows: + * For 0 < ctx size <= 1 PAGE, 0 level of ind is used + * For 1 PAGE < ctx size <= 512 entries size, 1 level of ind is used + * For 512 < ctx size <= MAX, 2 levels of ind is used + * Returns: + * 0 if success, else -ERRORS + */ +int bnxt_qplib_alloc_ctx(struct pci_dev *pdev, + struct bnxt_qplib_ctx *ctx, + bool virt_fn) +{ + int i, j, k, rc = 0; + int fnz_idx = -1; + __le64 **pbl_ptr; + + if (virt_fn) + goto stats_alloc; + + /* QPC Tables */ + ctx->qpc_tbl.max_elements = ctx->qpc_count; + rc = bnxt_qplib_alloc_init_hwq(pdev, &ctx->qpc_tbl, NULL, 0, + &ctx->qpc_tbl.max_elements, + BNXT_QPLIB_MAX_QP_CTX_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_CTX); + if (rc) + goto fail; + + /* MRW Tables */ + ctx->mrw_tbl.max_elements = ctx->mrw_count; + rc = bnxt_qplib_alloc_init_hwq(pdev, &ctx->mrw_tbl, NULL, 0, + &ctx->mrw_tbl.max_elements, + BNXT_QPLIB_MAX_MRW_CTX_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_CTX); + if (rc) + goto fail; + + /* SRQ Tables */ + ctx->srqc_tbl.max_elements = ctx->srqc_count; + rc = bnxt_qplib_alloc_init_hwq(pdev, &ctx->srqc_tbl, NULL, 0, + &ctx->srqc_tbl.max_elements, + BNXT_QPLIB_MAX_SRQ_CTX_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_CTX); + if (rc) + goto fail; + + /* CQ Tables */ + ctx->cq_tbl.max_elements = ctx->cq_count; + rc = bnxt_qplib_alloc_init_hwq(pdev, &ctx->cq_tbl, NULL, 0, + &ctx->cq_tbl.max_elements, + BNXT_QPLIB_MAX_CQ_CTX_ENTRY_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_CTX); + if (rc) + goto fail; + + /* TQM Buffer */ + ctx->tqm_pde.max_elements = 512; + rc = bnxt_qplib_alloc_init_hwq(pdev, &ctx->tqm_pde, NULL, 0, + &ctx->tqm_pde.max_elements, sizeof(u64), + 0, PAGE_SIZE, HWQ_TYPE_CTX); + if (rc) + goto fail; + + for (i = 0; i < MAX_TQM_ALLOC_REQ; i++) { + if (!ctx->tqm_count[i]) + continue; + ctx->tqm_tbl[i].max_elements = ctx->qpc_count * + ctx->tqm_count[i]; + rc = bnxt_qplib_alloc_init_hwq(pdev, &ctx->tqm_tbl[i], NULL, 0, + &ctx->tqm_tbl[i].max_elements, 1, + 0, PAGE_SIZE, HWQ_TYPE_CTX); + if (rc) + goto fail; + } + pbl_ptr = (__le64 **)ctx->tqm_pde.pbl_ptr; + for (i = 0, j = 0; i < MAX_TQM_ALLOC_REQ; + i++, j += MAX_TQM_ALLOC_BLK_SIZE) { + if (!ctx->tqm_tbl[i].max_elements) + continue; + if (fnz_idx == -1) + fnz_idx = i; + switch (ctx->tqm_tbl[i].level) { + case PBL_LVL_2: + for (k = 0; k < ctx->tqm_tbl[i].pbl[PBL_LVL_1].pg_count; + k++) + pbl_ptr[PTR_PG(j + k)][PTR_IDX(j + k)] = + cpu_to_le64( + ctx->tqm_tbl[i].pbl[PBL_LVL_1].pg_map_arr[k] + | PTU_PTE_VALID); + break; + case PBL_LVL_1: + case PBL_LVL_0: + default: + pbl_ptr[PTR_PG(j)][PTR_IDX(j)] = cpu_to_le64( + ctx->tqm_tbl[i].pbl[PBL_LVL_0].pg_map_arr[0] | + PTU_PTE_VALID); + break; + } + } + if (fnz_idx == -1) + fnz_idx = 0; + ctx->tqm_pde_level = ctx->tqm_tbl[fnz_idx].level == PBL_LVL_2 ? + PBL_LVL_2 : ctx->tqm_tbl[fnz_idx].level + 1; + + /* TIM Buffer */ + ctx->tim_tbl.max_elements = ctx->qpc_count * 16; + rc = bnxt_qplib_alloc_init_hwq(pdev, &ctx->tim_tbl, NULL, 0, + &ctx->tim_tbl.max_elements, 1, + 0, PAGE_SIZE, HWQ_TYPE_CTX); + if (rc) + goto fail; + +stats_alloc: + /* Stats */ + rc = bnxt_qplib_alloc_stats_ctx(pdev, &ctx->stats); + if (rc) + goto fail; + + return 0; + +fail: + bnxt_qplib_free_ctx(pdev, ctx); + return rc; +} + +/* GUID */ +void bnxt_qplib_get_guid(u8 *dev_addr, u8 *guid) +{ + u8 mac[ETH_ALEN]; + + /* MAC-48 to EUI-64 mapping */ + memcpy(mac, dev_addr, ETH_ALEN); + guid[0] = mac[0] ^ 2; + guid[1] = mac[1]; + guid[2] = mac[2]; + guid[3] = 0xff; + guid[4] = 0xfe; + guid[5] = mac[3]; + guid[6] = mac[4]; + guid[7] = mac[5]; +} + +static void bnxt_qplib_free_sgid_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_sgid_tbl *sgid_tbl) +{ + kfree(sgid_tbl->tbl); + kfree(sgid_tbl->hw_id); + kfree(sgid_tbl->ctx); + sgid_tbl->tbl = NULL; + sgid_tbl->hw_id = NULL; + sgid_tbl->ctx = NULL; + sgid_tbl->max = 0; + sgid_tbl->active = 0; +} + +static int bnxt_qplib_alloc_sgid_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_sgid_tbl *sgid_tbl, + u16 max) +{ + sgid_tbl->tbl = kcalloc(max, sizeof(struct bnxt_qplib_gid), GFP_KERNEL); + if (!sgid_tbl->tbl) + return -ENOMEM; + + sgid_tbl->hw_id = kcalloc(max, sizeof(u16), GFP_KERNEL); + if (!sgid_tbl->hw_id) + goto out_free1; + + sgid_tbl->ctx = kcalloc(max, sizeof(void *), GFP_KERNEL); + if (!sgid_tbl->ctx) + goto out_free2; + + sgid_tbl->max = max; + return 0; +out_free2: + kfree(sgid_tbl->hw_id); + sgid_tbl->hw_id = NULL; +out_free1: + kfree(sgid_tbl->tbl); + sgid_tbl->tbl = NULL; + return -ENOMEM; +}; + +static void bnxt_qplib_cleanup_sgid_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_sgid_tbl *sgid_tbl) +{ + int i; + + for (i = 0; i < sgid_tbl->max; i++) { + if (memcmp(&sgid_tbl->tbl[i], &bnxt_qplib_gid_zero, + sizeof(bnxt_qplib_gid_zero))) + bnxt_qplib_del_sgid(sgid_tbl, &sgid_tbl->tbl[i], true); + } + memset(sgid_tbl->tbl, 0, sizeof(struct bnxt_qplib_gid) * sgid_tbl->max); + memset(sgid_tbl->hw_id, -1, sizeof(u16) * sgid_tbl->max); + sgid_tbl->active = 0; +} + +static void bnxt_qplib_init_sgid_tbl(struct bnxt_qplib_sgid_tbl *sgid_tbl, + struct net_device *netdev) +{ + memset(sgid_tbl->tbl, 0, sizeof(struct bnxt_qplib_gid) * sgid_tbl->max); + memset(sgid_tbl->hw_id, -1, sizeof(u16) * sgid_tbl->max); +} + +static void bnxt_qplib_free_pkey_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl) +{ + if (!pkey_tbl->tbl) + dev_dbg(&res->pdev->dev, "QPLIB: PKEY tbl not present"); + else + kfree(pkey_tbl->tbl); + + pkey_tbl->tbl = NULL; + pkey_tbl->max = 0; + pkey_tbl->active = 0; +} + +static int bnxt_qplib_alloc_pkey_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl, + u16 max) +{ + pkey_tbl->tbl = kcalloc(max, sizeof(u16), GFP_KERNEL); + if (!pkey_tbl->tbl) + return -ENOMEM; + + pkey_tbl->max = max; + return 0; +}; + +/* PDs */ +int bnxt_qplib_alloc_pd(struct bnxt_qplib_pd_tbl *pdt, struct bnxt_qplib_pd *pd) +{ + u32 bit_num; + + bit_num = find_first_bit(pdt->tbl, pdt->max); + if (bit_num == pdt->max) + return -ENOMEM; + + /* Found unused PD */ + clear_bit(bit_num, pdt->tbl); + pd->id = bit_num; + return 0; +} + +int bnxt_qplib_dealloc_pd(struct bnxt_qplib_res *res, + struct bnxt_qplib_pd_tbl *pdt, + struct bnxt_qplib_pd *pd) +{ + if (test_and_set_bit(pd->id, pdt->tbl)) { + dev_warn(&res->pdev->dev, "Freeing an unused PD? pdn = %d", + pd->id); + return -EINVAL; + } + pd->id = 0; + return 0; +} + +static void bnxt_qplib_free_pd_tbl(struct bnxt_qplib_pd_tbl *pdt) +{ + kfree(pdt->tbl); + pdt->tbl = NULL; + pdt->max = 0; +} + +static int bnxt_qplib_alloc_pd_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_pd_tbl *pdt, + u32 max) +{ + u32 bytes; + + bytes = max >> 3; + if (!bytes) + bytes = 1; + pdt->tbl = kmalloc(bytes, GFP_KERNEL); + if (!pdt->tbl) + return -ENOMEM; + + pdt->max = max; + memset((u8 *)pdt->tbl, 0xFF, bytes); + + return 0; +} + +/* DPIs */ +int bnxt_qplib_alloc_dpi(struct bnxt_qplib_dpi_tbl *dpit, + struct bnxt_qplib_dpi *dpi, + void *app) +{ + u32 bit_num; + + bit_num = find_first_bit(dpit->tbl, dpit->max); + if (bit_num == dpit->max) + return -ENOMEM; + + /* Found unused DPI */ + clear_bit(bit_num, dpit->tbl); + dpit->app_tbl[bit_num] = app; + + dpi->dpi = bit_num; + dpi->dbr = dpit->dbr_bar_reg_iomem + (bit_num * PAGE_SIZE); + dpi->umdbr = dpit->unmapped_dbr + (bit_num * PAGE_SIZE); + + return 0; +} + +int bnxt_qplib_dealloc_dpi(struct bnxt_qplib_res *res, + struct bnxt_qplib_dpi_tbl *dpit, + struct bnxt_qplib_dpi *dpi) +{ + if (dpi->dpi >= dpit->max) { + dev_warn(&res->pdev->dev, "Invalid DPI? dpi = %d", dpi->dpi); + return -EINVAL; + } + if (test_and_set_bit(dpi->dpi, dpit->tbl)) { + dev_warn(&res->pdev->dev, "Freeing an unused DPI? dpi = %d", + dpi->dpi); + return -EINVAL; + } + if (dpit->app_tbl) + dpit->app_tbl[dpi->dpi] = NULL; + memset(dpi, 0, sizeof(*dpi)); + + return 0; +} + +static void bnxt_qplib_free_dpi_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_dpi_tbl *dpit) +{ + kfree(dpit->tbl); + kfree(dpit->app_tbl); + if (dpit->dbr_bar_reg_iomem) + pci_iounmap(res->pdev, dpit->dbr_bar_reg_iomem); + memset(dpit, 0, sizeof(*dpit)); +} + +static int bnxt_qplib_alloc_dpi_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_dpi_tbl *dpit, + u32 dbr_offset) +{ + u32 dbr_bar_reg = RCFW_DBR_PCI_BAR_REGION; + resource_size_t bar_reg_base; + u32 dbr_len, bytes; + + if (dpit->dbr_bar_reg_iomem) { + dev_err(&res->pdev->dev, + "QPLIB: DBR BAR region %d already mapped", dbr_bar_reg); + return -EALREADY; + } + + bar_reg_base = pci_resource_start(res->pdev, dbr_bar_reg); + if (!bar_reg_base) { + dev_err(&res->pdev->dev, + "QPLIB: BAR region %d resc start failed", dbr_bar_reg); + return -ENOMEM; + } + + dbr_len = pci_resource_len(res->pdev, dbr_bar_reg) - dbr_offset; + if (!dbr_len || ((dbr_len & (PAGE_SIZE - 1)) != 0)) { + dev_err(&res->pdev->dev, "QPLIB: Invalid DBR length %d", + dbr_len); + return -ENOMEM; + } + + dpit->dbr_bar_reg_iomem = ioremap_nocache(bar_reg_base + dbr_offset, + dbr_len); + if (!dpit->dbr_bar_reg_iomem) { + dev_err(&res->pdev->dev, + "QPLIB: FP: DBR BAR region %d mapping failed", + dbr_bar_reg); + return -ENOMEM; + } + + dpit->unmapped_dbr = bar_reg_base + dbr_offset; + dpit->max = dbr_len / PAGE_SIZE; + + dpit->app_tbl = kcalloc(dpit->max, sizeof(void *), GFP_KERNEL); + if (!dpit->app_tbl) { + pci_iounmap(res->pdev, dpit->dbr_bar_reg_iomem); + dev_err(&res->pdev->dev, + "QPLIB: DPI app tbl allocation failed"); + return -ENOMEM; + } + + bytes = dpit->max >> 3; + if (!bytes) + bytes = 1; + + dpit->tbl = kmalloc(bytes, GFP_KERNEL); + if (!dpit->tbl) { + pci_iounmap(res->pdev, dpit->dbr_bar_reg_iomem); + kfree(dpit->app_tbl); + dpit->app_tbl = NULL; + dev_err(&res->pdev->dev, + "QPLIB: DPI tbl allocation failed for size = %d", + bytes); + return -ENOMEM; + } + + memset((u8 *)dpit->tbl, 0xFF, bytes); + + return 0; +} + +/* PKEYs */ +static void bnxt_qplib_cleanup_pkey_tbl(struct bnxt_qplib_pkey_tbl *pkey_tbl) +{ + memset(pkey_tbl->tbl, 0, sizeof(u16) * pkey_tbl->max); + pkey_tbl->active = 0; +} + +static void bnxt_qplib_init_pkey_tbl(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl) +{ + u16 pkey = 0xFFFF; + + memset(pkey_tbl->tbl, 0, sizeof(u16) * pkey_tbl->max); + + /* pkey default = 0xFFFF */ + bnxt_qplib_add_pkey(res, pkey_tbl, &pkey, false); +} + +/* Stats */ +static void bnxt_qplib_free_stats_ctx(struct pci_dev *pdev, + struct bnxt_qplib_stats *stats) +{ + if (stats->dma) { + dma_free_coherent(&pdev->dev, stats->size, + stats->dma, stats->dma_map); + } + memset(stats, 0, sizeof(*stats)); + stats->fw_id = -1; +} + +static int bnxt_qplib_alloc_stats_ctx(struct pci_dev *pdev, + struct bnxt_qplib_stats *stats) +{ + memset(stats, 0, sizeof(*stats)); + stats->fw_id = -1; + stats->size = sizeof(struct ctx_hw_stats); + stats->dma = dma_alloc_coherent(&pdev->dev, stats->size, + &stats->dma_map, GFP_KERNEL); + if (!stats->dma) { + dev_err(&pdev->dev, "QPLIB: Stats DMA allocation failed"); + return -ENOMEM; + } + return 0; +} + +void bnxt_qplib_cleanup_res(struct bnxt_qplib_res *res) +{ + bnxt_qplib_cleanup_pkey_tbl(&res->pkey_tbl); + bnxt_qplib_cleanup_sgid_tbl(res, &res->sgid_tbl); +} + +int bnxt_qplib_init_res(struct bnxt_qplib_res *res) +{ + bnxt_qplib_init_sgid_tbl(&res->sgid_tbl, res->netdev); + bnxt_qplib_init_pkey_tbl(res, &res->pkey_tbl); + + return 0; +} + +void bnxt_qplib_free_res(struct bnxt_qplib_res *res) +{ + bnxt_qplib_free_pkey_tbl(res, &res->pkey_tbl); + bnxt_qplib_free_sgid_tbl(res, &res->sgid_tbl); + bnxt_qplib_free_pd_tbl(&res->pd_tbl); + bnxt_qplib_free_dpi_tbl(res, &res->dpi_tbl); + + res->netdev = NULL; + res->pdev = NULL; +} + +int bnxt_qplib_alloc_res(struct bnxt_qplib_res *res, struct pci_dev *pdev, + struct net_device *netdev, + struct bnxt_qplib_dev_attr *dev_attr) +{ + int rc = 0; + + res->pdev = pdev; + res->netdev = netdev; + + rc = bnxt_qplib_alloc_sgid_tbl(res, &res->sgid_tbl, dev_attr->max_sgid); + if (rc) + goto fail; + + rc = bnxt_qplib_alloc_pkey_tbl(res, &res->pkey_tbl, dev_attr->max_pkey); + if (rc) + goto fail; + + rc = bnxt_qplib_alloc_pd_tbl(res, &res->pd_tbl, dev_attr->max_pd); + if (rc) + goto fail; + + rc = bnxt_qplib_alloc_dpi_tbl(res, &res->dpi_tbl, dev_attr->l2_db_size); + if (rc) + goto fail; + + return 0; +fail: + bnxt_qplib_free_res(res); + return rc; +} diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h new file mode 100644 index 000000000000..6277d802ca4b --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h @@ -0,0 +1,223 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: QPLib resource manager (header) + */ + +#ifndef __BNXT_QPLIB_RES_H__ +#define __BNXT_QPLIB_RES_H__ + +extern const struct bnxt_qplib_gid bnxt_qplib_gid_zero; + +#define PTR_CNT_PER_PG (PAGE_SIZE / sizeof(void *)) +#define PTR_MAX_IDX_PER_PG (PTR_CNT_PER_PG - 1) +#define PTR_PG(x) (((x) & ~PTR_MAX_IDX_PER_PG) / PTR_CNT_PER_PG) +#define PTR_IDX(x) ((x) & PTR_MAX_IDX_PER_PG) + +#define HWQ_CMP(idx, hwq) ((idx) & ((hwq)->max_elements - 1)) + +enum bnxt_qplib_hwq_type { + HWQ_TYPE_CTX, + HWQ_TYPE_QUEUE, + HWQ_TYPE_L2_CMPL +}; + +#define MAX_PBL_LVL_0_PGS 1 +#define MAX_PBL_LVL_1_PGS 512 +#define MAX_PBL_LVL_1_PGS_SHIFT 9 +#define MAX_PBL_LVL_1_PGS_FOR_LVL_2 256 +#define MAX_PBL_LVL_2_PGS (256 * 512) + +enum bnxt_qplib_pbl_lvl { + PBL_LVL_0, + PBL_LVL_1, + PBL_LVL_2, + PBL_LVL_MAX +}; + +#define ROCE_PG_SIZE_4K (4 * 1024) +#define ROCE_PG_SIZE_8K (8 * 1024) +#define ROCE_PG_SIZE_64K (64 * 1024) +#define ROCE_PG_SIZE_2M (2 * 1024 * 1024) +#define ROCE_PG_SIZE_8M (8 * 1024 * 1024) +#define ROCE_PG_SIZE_1G (1024 * 1024 * 1024) + +struct bnxt_qplib_pbl { + u32 pg_count; + u32 pg_size; + void **pg_arr; + dma_addr_t *pg_map_arr; +}; + +struct bnxt_qplib_hwq { + struct pci_dev *pdev; + /* lock to protect qplib_hwq */ + spinlock_t lock; + struct bnxt_qplib_pbl pbl[PBL_LVL_MAX]; + enum bnxt_qplib_pbl_lvl level; /* 0, 1, or 2 */ + /* ptr for easy access to the PBL entries */ + void **pbl_ptr; + /* ptr for easy access to the dma_addr */ + dma_addr_t *pbl_dma_ptr; + u32 max_elements; + u16 element_size; /* Size of each entry */ + + u32 prod; /* raw */ + u32 cons; /* raw */ + u8 cp_bit; + u8 is_user; +}; + +/* Tables */ +struct bnxt_qplib_pd_tbl { + unsigned long *tbl; + u32 max; +}; + +struct bnxt_qplib_sgid_tbl { + struct bnxt_qplib_gid *tbl; + u16 *hw_id; + u16 max; + u16 active; + void *ctx; +}; + +struct bnxt_qplib_pkey_tbl { + u16 *tbl; + u16 max; + u16 active; +}; + +struct bnxt_qplib_dpi { + u32 dpi; + void __iomem *dbr; + u64 umdbr; +}; + +struct bnxt_qplib_dpi_tbl { + void **app_tbl; + unsigned long *tbl; + u16 max; + void __iomem *dbr_bar_reg_iomem; + u64 unmapped_dbr; +}; + +struct bnxt_qplib_stats { + dma_addr_t dma_map; + void *dma; + u32 size; + u32 fw_id; +}; + +struct bnxt_qplib_vf_res { + u32 max_qp_per_vf; + u32 max_mrw_per_vf; + u32 max_srq_per_vf; + u32 max_cq_per_vf; + u32 max_gid_per_vf; +}; + +#define BNXT_QPLIB_MAX_QP_CTX_ENTRY_SIZE 448 +#define BNXT_QPLIB_MAX_SRQ_CTX_ENTRY_SIZE 64 +#define BNXT_QPLIB_MAX_CQ_CTX_ENTRY_SIZE 64 +#define BNXT_QPLIB_MAX_MRW_CTX_ENTRY_SIZE 128 + +struct bnxt_qplib_ctx { + u32 qpc_count; + struct bnxt_qplib_hwq qpc_tbl; + u32 mrw_count; + struct bnxt_qplib_hwq mrw_tbl; + u32 srqc_count; + struct bnxt_qplib_hwq srqc_tbl; + u32 cq_count; + struct bnxt_qplib_hwq cq_tbl; + struct bnxt_qplib_hwq tim_tbl; +#define MAX_TQM_ALLOC_REQ 32 +#define MAX_TQM_ALLOC_BLK_SIZE 8 + u8 tqm_count[MAX_TQM_ALLOC_REQ]; + struct bnxt_qplib_hwq tqm_pde; + u32 tqm_pde_level; + struct bnxt_qplib_hwq tqm_tbl[MAX_TQM_ALLOC_REQ]; + struct bnxt_qplib_stats stats; + struct bnxt_qplib_vf_res vf_res; +}; + +struct bnxt_qplib_res { + struct pci_dev *pdev; + struct net_device *netdev; + + struct bnxt_qplib_rcfw *rcfw; + + struct bnxt_qplib_pd_tbl pd_tbl; + struct bnxt_qplib_sgid_tbl sgid_tbl; + struct bnxt_qplib_pkey_tbl pkey_tbl; + struct bnxt_qplib_dpi_tbl dpi_tbl; +}; + +#define to_bnxt_qplib(ptr, type, member) \ + container_of(ptr, type, member) + +struct bnxt_qplib_pd; +struct bnxt_qplib_dev_attr; + +void bnxt_qplib_free_hwq(struct pci_dev *pdev, struct bnxt_qplib_hwq *hwq); +int bnxt_qplib_alloc_init_hwq(struct pci_dev *pdev, struct bnxt_qplib_hwq *hwq, + struct scatterlist *sl, int nmap, u32 *elements, + u32 elements_per_page, u32 aux, u32 pg_size, + enum bnxt_qplib_hwq_type hwq_type); +void bnxt_qplib_get_guid(u8 *dev_addr, u8 *guid); +int bnxt_qplib_alloc_pd(struct bnxt_qplib_pd_tbl *pd_tbl, + struct bnxt_qplib_pd *pd); +int bnxt_qplib_dealloc_pd(struct bnxt_qplib_res *res, + struct bnxt_qplib_pd_tbl *pd_tbl, + struct bnxt_qplib_pd *pd); +int bnxt_qplib_alloc_dpi(struct bnxt_qplib_dpi_tbl *dpit, + struct bnxt_qplib_dpi *dpi, + void *app); +int bnxt_qplib_dealloc_dpi(struct bnxt_qplib_res *res, + struct bnxt_qplib_dpi_tbl *dpi_tbl, + struct bnxt_qplib_dpi *dpi); +void bnxt_qplib_cleanup_res(struct bnxt_qplib_res *res); +int bnxt_qplib_init_res(struct bnxt_qplib_res *res); +void bnxt_qplib_free_res(struct bnxt_qplib_res *res); +int bnxt_qplib_alloc_res(struct bnxt_qplib_res *res, struct pci_dev *pdev, + struct net_device *netdev, + struct bnxt_qplib_dev_attr *dev_attr); +void bnxt_qplib_free_ctx(struct pci_dev *pdev, + struct bnxt_qplib_ctx *ctx); +int bnxt_qplib_alloc_ctx(struct pci_dev *pdev, + struct bnxt_qplib_ctx *ctx, + bool virt_fn); +#endif /* __BNXT_QPLIB_RES_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c new file mode 100644 index 000000000000..7b31eccedf11 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c @@ -0,0 +1,838 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: Slow Path Operators + */ + +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/pci.h> + +#include "roce_hsi.h" + +#include "qplib_res.h" +#include "qplib_rcfw.h" +#include "qplib_sp.h" + +const struct bnxt_qplib_gid bnxt_qplib_gid_zero = {{ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 } }; + +/* Device */ +int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, + struct bnxt_qplib_dev_attr *attr) +{ + struct cmdq_query_func req; + struct creq_query_func_resp *resp; + struct creq_query_func_resp_sb *sb; + u16 cmd_flags = 0; + u32 temp; + u8 *tqm_alloc; + int i; + + RCFW_CMD_PREP(req, QUERY_FUNC, cmd_flags); + + req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; + resp = (struct creq_query_func_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, (void **)&sb, + 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: QUERY_FUNC send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: SP: QUERY_FUNC timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: QUERY_FUNC failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + /* Extract the context from the side buffer */ + attr->max_qp = le32_to_cpu(sb->max_qp); + attr->max_qp_rd_atom = + sb->max_qp_rd_atom > BNXT_QPLIB_MAX_OUT_RD_ATOM ? + BNXT_QPLIB_MAX_OUT_RD_ATOM : sb->max_qp_rd_atom; + attr->max_qp_init_rd_atom = + sb->max_qp_init_rd_atom > BNXT_QPLIB_MAX_OUT_RD_ATOM ? + BNXT_QPLIB_MAX_OUT_RD_ATOM : sb->max_qp_init_rd_atom; + attr->max_qp_wqes = le16_to_cpu(sb->max_qp_wr); + attr->max_qp_sges = sb->max_sge; + attr->max_cq = le32_to_cpu(sb->max_cq); + attr->max_cq_wqes = le32_to_cpu(sb->max_cqe); + attr->max_cq_sges = attr->max_qp_sges; + attr->max_mr = le32_to_cpu(sb->max_mr); + attr->max_mw = le32_to_cpu(sb->max_mw); + + attr->max_mr_size = le64_to_cpu(sb->max_mr_size); + attr->max_pd = 64 * 1024; + attr->max_raw_ethy_qp = le32_to_cpu(sb->max_raw_eth_qp); + attr->max_ah = le32_to_cpu(sb->max_ah); + + attr->max_fmr = le32_to_cpu(sb->max_fmr); + attr->max_map_per_fmr = sb->max_map_per_fmr; + + attr->max_srq = le16_to_cpu(sb->max_srq); + attr->max_srq_wqes = le32_to_cpu(sb->max_srq_wr) - 1; + attr->max_srq_sges = sb->max_srq_sge; + /* Bono only reports 1 PKEY for now, but it can support > 1 */ + attr->max_pkey = le32_to_cpu(sb->max_pkeys); + + attr->max_inline_data = le32_to_cpu(sb->max_inline_data); + attr->l2_db_size = (sb->l2_db_space_size + 1) * PAGE_SIZE; + attr->max_sgid = le32_to_cpu(sb->max_gid); + + strlcpy(attr->fw_ver, "20.6.28.0", sizeof(attr->fw_ver)); + + for (i = 0; i < MAX_TQM_ALLOC_REQ / 4; i++) { + temp = le32_to_cpu(sb->tqm_alloc_reqs[i]); + tqm_alloc = (u8 *)&temp; + attr->tqm_alloc_reqs[i * 4] = *tqm_alloc; + attr->tqm_alloc_reqs[i * 4 + 1] = *(++tqm_alloc); + attr->tqm_alloc_reqs[i * 4 + 2] = *(++tqm_alloc); + attr->tqm_alloc_reqs[i * 4 + 3] = *(++tqm_alloc); + } + return 0; +} + +/* SGID */ +int bnxt_qplib_get_sgid(struct bnxt_qplib_res *res, + struct bnxt_qplib_sgid_tbl *sgid_tbl, int index, + struct bnxt_qplib_gid *gid) +{ + if (index > sgid_tbl->max) { + dev_err(&res->pdev->dev, + "QPLIB: Index %d exceeded SGID table max (%d)", + index, sgid_tbl->max); + return -EINVAL; + } + memcpy(gid, &sgid_tbl->tbl[index], sizeof(*gid)); + return 0; +} + +int bnxt_qplib_del_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, + struct bnxt_qplib_gid *gid, bool update) +{ + struct bnxt_qplib_res *res = to_bnxt_qplib(sgid_tbl, + struct bnxt_qplib_res, + sgid_tbl); + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + int index; + + if (!sgid_tbl) { + dev_err(&res->pdev->dev, "QPLIB: SGID table not allocated"); + return -EINVAL; + } + /* Do we need a sgid_lock here? */ + if (!sgid_tbl->active) { + dev_err(&res->pdev->dev, + "QPLIB: SGID table has no active entries"); + return -ENOMEM; + } + for (index = 0; index < sgid_tbl->max; index++) { + if (!memcmp(&sgid_tbl->tbl[index], gid, sizeof(*gid))) + break; + } + if (index == sgid_tbl->max) { + dev_warn(&res->pdev->dev, "GID not found in the SGID table"); + return 0; + } + /* Remove GID from the SGID table */ + if (update) { + struct cmdq_delete_gid req; + struct creq_delete_gid_resp *resp; + u16 cmd_flags = 0; + + RCFW_CMD_PREP(req, DELETE_GID, cmd_flags); + if (sgid_tbl->hw_id[index] == 0xFFFF) { + dev_err(&res->pdev->dev, + "QPLIB: GID entry contains an invalid HW id"); + return -EINVAL; + } + req.gid_index = cpu_to_le16(sgid_tbl->hw_id[index]); + resp = (struct creq_delete_gid_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, NULL, + 0); + if (!resp) { + dev_err(&res->pdev->dev, + "QPLIB: SP: DELETE_GID send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, + le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&res->pdev->dev, + "QPLIB: SP: DELETE_GID timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&res->pdev->dev, + "QPLIB: SP: DELETE_GID failed "); + dev_err(&res->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + } + memcpy(&sgid_tbl->tbl[index], &bnxt_qplib_gid_zero, + sizeof(bnxt_qplib_gid_zero)); + sgid_tbl->active--; + dev_dbg(&res->pdev->dev, + "QPLIB: SGID deleted hw_id[0x%x] = 0x%x active = 0x%x", + index, sgid_tbl->hw_id[index], sgid_tbl->active); + sgid_tbl->hw_id[index] = (u16)-1; + + /* unlock */ + return 0; +} + +int bnxt_qplib_add_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, + struct bnxt_qplib_gid *gid, u8 *smac, u16 vlan_id, + bool update, u32 *index) +{ + struct bnxt_qplib_res *res = to_bnxt_qplib(sgid_tbl, + struct bnxt_qplib_res, + sgid_tbl); + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + int i, free_idx, rc = 0; + + if (!sgid_tbl) { + dev_err(&res->pdev->dev, "QPLIB: SGID table not allocated"); + return -EINVAL; + } + /* Do we need a sgid_lock here? */ + if (sgid_tbl->active == sgid_tbl->max) { + dev_err(&res->pdev->dev, "QPLIB: SGID table is full"); + return -ENOMEM; + } + free_idx = sgid_tbl->max; + for (i = 0; i < sgid_tbl->max; i++) { + if (!memcmp(&sgid_tbl->tbl[i], gid, sizeof(*gid))) { + dev_dbg(&res->pdev->dev, + "QPLIB: SGID entry already exist in entry %d!", + i); + *index = i; + return -EALREADY; + } else if (!memcmp(&sgid_tbl->tbl[i], &bnxt_qplib_gid_zero, + sizeof(bnxt_qplib_gid_zero)) && + free_idx == sgid_tbl->max) { + free_idx = i; + } + } + if (free_idx == sgid_tbl->max) { + dev_err(&res->pdev->dev, + "QPLIB: SGID table is FULL but count is not MAX??"); + return -ENOMEM; + } + if (update) { + struct cmdq_add_gid req; + struct creq_add_gid_resp *resp; + u16 cmd_flags = 0; + u32 temp32[4]; + u16 temp16[3]; + + RCFW_CMD_PREP(req, ADD_GID, cmd_flags); + + memcpy(temp32, gid->data, sizeof(struct bnxt_qplib_gid)); + req.gid[0] = cpu_to_be32(temp32[3]); + req.gid[1] = cpu_to_be32(temp32[2]); + req.gid[2] = cpu_to_be32(temp32[1]); + req.gid[3] = cpu_to_be32(temp32[0]); + if (vlan_id != 0xFFFF) + req.vlan = cpu_to_le16((vlan_id & + CMDQ_ADD_GID_VLAN_VLAN_ID_MASK) | + CMDQ_ADD_GID_VLAN_TPID_TPID_8100 | + CMDQ_ADD_GID_VLAN_VLAN_EN); + + /* MAC in network format */ + memcpy(temp16, smac, 6); + req.src_mac[0] = cpu_to_be16(temp16[0]); + req.src_mac[1] = cpu_to_be16(temp16[1]); + req.src_mac[2] = cpu_to_be16(temp16[2]); + + resp = (struct creq_add_gid_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&res->pdev->dev, + "QPLIB: SP: ADD_GID send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, + le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&res->pdev->dev, + "QPIB: SP: ADD_GID timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&res->pdev->dev, "QPLIB: SP: ADD_GID failed "); + dev_err(&res->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + sgid_tbl->hw_id[free_idx] = le32_to_cpu(resp->xid); + } + /* Add GID to the sgid_tbl */ + memcpy(&sgid_tbl->tbl[free_idx], gid, sizeof(*gid)); + sgid_tbl->active++; + dev_dbg(&res->pdev->dev, + "QPLIB: SGID added hw_id[0x%x] = 0x%x active = 0x%x", + free_idx, sgid_tbl->hw_id[free_idx], sgid_tbl->active); + + *index = free_idx; + /* unlock */ + return rc; +} + +/* pkeys */ +int bnxt_qplib_get_pkey(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 index, + u16 *pkey) +{ + if (index == 0xFFFF) { + *pkey = 0xFFFF; + return 0; + } + if (index > pkey_tbl->max) { + dev_err(&res->pdev->dev, + "QPLIB: Index %d exceeded PKEY table max (%d)", + index, pkey_tbl->max); + return -EINVAL; + } + memcpy(pkey, &pkey_tbl->tbl[index], sizeof(*pkey)); + return 0; +} + +int bnxt_qplib_del_pkey(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 *pkey, + bool update) +{ + int i, rc = 0; + + if (!pkey_tbl) { + dev_err(&res->pdev->dev, "QPLIB: PKEY table not allocated"); + return -EINVAL; + } + + /* Do we need a pkey_lock here? */ + if (!pkey_tbl->active) { + dev_err(&res->pdev->dev, + "QPLIB: PKEY table has no active entries"); + return -ENOMEM; + } + for (i = 0; i < pkey_tbl->max; i++) { + if (!memcmp(&pkey_tbl->tbl[i], pkey, sizeof(*pkey))) + break; + } + if (i == pkey_tbl->max) { + dev_err(&res->pdev->dev, + "QPLIB: PKEY 0x%04x not found in the pkey table", + *pkey); + return -ENOMEM; + } + memset(&pkey_tbl->tbl[i], 0, sizeof(*pkey)); + pkey_tbl->active--; + + /* unlock */ + return rc; +} + +int bnxt_qplib_add_pkey(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 *pkey, + bool update) +{ + int i, free_idx, rc = 0; + + if (!pkey_tbl) { + dev_err(&res->pdev->dev, "QPLIB: PKEY table not allocated"); + return -EINVAL; + } + + /* Do we need a pkey_lock here? */ + if (pkey_tbl->active == pkey_tbl->max) { + dev_err(&res->pdev->dev, "QPLIB: PKEY table is full"); + return -ENOMEM; + } + free_idx = pkey_tbl->max; + for (i = 0; i < pkey_tbl->max; i++) { + if (!memcmp(&pkey_tbl->tbl[i], pkey, sizeof(*pkey))) + return -EALREADY; + else if (!pkey_tbl->tbl[i] && free_idx == pkey_tbl->max) + free_idx = i; + } + if (free_idx == pkey_tbl->max) { + dev_err(&res->pdev->dev, + "QPLIB: PKEY table is FULL but count is not MAX??"); + return -ENOMEM; + } + /* Add PKEY to the pkey_tbl */ + memcpy(&pkey_tbl->tbl[free_idx], pkey, sizeof(*pkey)); + pkey_tbl->active++; + + /* unlock */ + return rc; +} + +/* AH */ +int bnxt_qplib_create_ah(struct bnxt_qplib_res *res, struct bnxt_qplib_ah *ah) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_create_ah req; + struct creq_create_ah_resp *resp; + u16 cmd_flags = 0; + u32 temp32[4]; + u16 temp16[3]; + + RCFW_CMD_PREP(req, CREATE_AH, cmd_flags); + + memcpy(temp32, ah->dgid.data, sizeof(struct bnxt_qplib_gid)); + req.dgid[0] = cpu_to_le32(temp32[0]); + req.dgid[1] = cpu_to_le32(temp32[1]); + req.dgid[2] = cpu_to_le32(temp32[2]); + req.dgid[3] = cpu_to_le32(temp32[3]); + + req.type = ah->nw_type; + req.hop_limit = ah->hop_limit; + req.sgid_index = cpu_to_le16(res->sgid_tbl.hw_id[ah->sgid_index]); + req.dest_vlan_id_flow_label = cpu_to_le32((ah->flow_label & + CMDQ_CREATE_AH_FLOW_LABEL_MASK) | + CMDQ_CREATE_AH_DEST_VLAN_ID_MASK); + req.pd_id = cpu_to_le32(ah->pd->id); + req.traffic_class = ah->traffic_class; + + /* MAC in network format */ + memcpy(temp16, ah->dmac, 6); + req.dest_mac[0] = cpu_to_le16(temp16[0]); + req.dest_mac[1] = cpu_to_le16(temp16[1]); + req.dest_mac[2] = cpu_to_le16(temp16[2]); + + resp = (struct creq_create_ah_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 1); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: CREATE_AH send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_block_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: SP: CREATE_AH timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: CREATE_AH failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + ah->id = le32_to_cpu(resp->xid); + return 0; +} + +int bnxt_qplib_destroy_ah(struct bnxt_qplib_res *res, struct bnxt_qplib_ah *ah) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_destroy_ah req; + struct creq_destroy_ah_resp *resp; + u16 cmd_flags = 0; + + /* Clean up the AH table in the device */ + RCFW_CMD_PREP(req, DESTROY_AH, cmd_flags); + + req.ah_cid = cpu_to_le32(ah->id); + + resp = (struct creq_destroy_ah_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 1); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: DESTROY_AH send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_block_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: SP: DESTROY_AH timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: DESTROY_AH failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + return 0; +} + +/* MRW */ +int bnxt_qplib_free_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_deallocate_key req; + struct creq_deallocate_key_resp *resp; + u16 cmd_flags = 0; + + if (mrw->lkey == 0xFFFFFFFF) { + dev_info(&res->pdev->dev, + "QPLIB: SP: Free a reserved lkey MRW"); + return 0; + } + + RCFW_CMD_PREP(req, DEALLOCATE_KEY, cmd_flags); + + req.mrw_flags = mrw->type; + + if ((mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE1) || + (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A) || + (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B)) + req.key = cpu_to_le32(mrw->rkey); + else + req.key = cpu_to_le32(mrw->lkey); + + resp = (struct creq_deallocate_key_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&res->pdev->dev, "QPLIB: SP: FREE_MR send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&res->pdev->dev, "QPLIB: SP: FREE_MR timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&res->pdev->dev, "QPLIB: SP: FREE_MR failed "); + dev_err(&res->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + /* Free the qplib's MRW memory */ + if (mrw->hwq.max_elements) + bnxt_qplib_free_hwq(res->pdev, &mrw->hwq); + + return 0; +} + +int bnxt_qplib_alloc_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_allocate_mrw req; + struct creq_allocate_mrw_resp *resp; + u16 cmd_flags = 0; + unsigned long tmp; + + RCFW_CMD_PREP(req, ALLOCATE_MRW, cmd_flags); + + req.pd_id = cpu_to_le32(mrw->pd->id); + req.mrw_flags = mrw->type; + if ((mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR && + mrw->flags & BNXT_QPLIB_FR_PMR) || + mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A || + mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B) + req.access = CMDQ_ALLOCATE_MRW_ACCESS_CONSUMER_OWNED_KEY; + tmp = (unsigned long)mrw; + req.mrw_handle = cpu_to_le64(tmp); + + resp = (struct creq_allocate_mrw_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, 0); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: ALLOC_MRW send failed"); + return -EINVAL; + } + if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { + /* Cmd timed out */ + dev_err(&rcfw->pdev->dev, "QPLIB: SP: ALLOC_MRW timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: ALLOC_MRW failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + if ((mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE1) || + (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A) || + (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B)) + mrw->rkey = le32_to_cpu(resp->xid); + else + mrw->lkey = le32_to_cpu(resp->xid); + return 0; +} + +int bnxt_qplib_dereg_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw, + bool block) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_deregister_mr req; + struct creq_deregister_mr_resp *resp; + u16 cmd_flags = 0; + int rc; + + RCFW_CMD_PREP(req, DEREGISTER_MR, cmd_flags); + + req.lkey = cpu_to_le32(mrw->lkey); + resp = (struct creq_deregister_mr_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, block); + if (!resp) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: DEREG_MR send failed"); + return -EINVAL; + } + if (block) + rc = bnxt_qplib_rcfw_block_for_resp(rcfw, + le16_to_cpu(req.cookie)); + else + rc = bnxt_qplib_rcfw_wait_for_resp(rcfw, + le16_to_cpu(req.cookie)); + if (!rc) { + /* Cmd timed out */ + dev_err(&res->pdev->dev, "QPLIB: SP: DEREG_MR timed out"); + return -ETIMEDOUT; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&rcfw->pdev->dev, "QPLIB: SP: DEREG_MR failed "); + dev_err(&rcfw->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + + /* Free the qplib's MR memory */ + if (mrw->hwq.max_elements) { + mrw->va = 0; + mrw->total_size = 0; + bnxt_qplib_free_hwq(res->pdev, &mrw->hwq); + } + + return 0; +} + +int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr, + u64 *pbl_tbl, int num_pbls, bool block) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_register_mr req; + struct creq_register_mr_resp *resp; + u16 cmd_flags = 0, level; + int pg_ptrs, pages, i, rc; + dma_addr_t **pbl_ptr; + u32 pg_size; + + if (num_pbls) { + pg_ptrs = roundup_pow_of_two(num_pbls); + pages = pg_ptrs >> MAX_PBL_LVL_1_PGS_SHIFT; + if (!pages) + pages++; + + if (pages > MAX_PBL_LVL_1_PGS) { + dev_err(&res->pdev->dev, "QPLIB: SP: Reg MR pages "); + dev_err(&res->pdev->dev, + "requested (0x%x) exceeded max (0x%x)", + pages, MAX_PBL_LVL_1_PGS); + return -ENOMEM; + } + /* Free the hwq if it already exist, must be a rereg */ + if (mr->hwq.max_elements) + bnxt_qplib_free_hwq(res->pdev, &mr->hwq); + + mr->hwq.max_elements = pages; + rc = bnxt_qplib_alloc_init_hwq(res->pdev, &mr->hwq, NULL, 0, + &mr->hwq.max_elements, + PAGE_SIZE, 0, PAGE_SIZE, + HWQ_TYPE_CTX); + if (rc) { + dev_err(&res->pdev->dev, + "SP: Reg MR memory allocation failed"); + return -ENOMEM; + } + /* Write to the hwq */ + pbl_ptr = (dma_addr_t **)mr->hwq.pbl_ptr; + for (i = 0; i < num_pbls; i++) + pbl_ptr[PTR_PG(i)][PTR_IDX(i)] = + (pbl_tbl[i] & PAGE_MASK) | PTU_PTE_VALID; + } + + RCFW_CMD_PREP(req, REGISTER_MR, cmd_flags); + + /* Configure the request */ + if (mr->hwq.level == PBL_LVL_MAX) { + level = 0; + req.pbl = 0; + pg_size = PAGE_SIZE; + } else { + level = mr->hwq.level + 1; + req.pbl = cpu_to_le64(mr->hwq.pbl[PBL_LVL_0].pg_map_arr[0]); + pg_size = mr->hwq.pbl[PBL_LVL_0].pg_size; + } + req.log2_pg_size_lvl = (level << CMDQ_REGISTER_MR_LVL_SFT) | + ((ilog2(pg_size) << + CMDQ_REGISTER_MR_LOG2_PG_SIZE_SFT) & + CMDQ_REGISTER_MR_LOG2_PG_SIZE_MASK); + req.access = (mr->flags & 0xFFFF); + req.va = cpu_to_le64(mr->va); + req.key = cpu_to_le32(mr->lkey); + req.mr_size = cpu_to_le64(mr->total_size); + + resp = (struct creq_register_mr_resp *) + bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, + NULL, block); + if (!resp) { + dev_err(&res->pdev->dev, "SP: REG_MR send failed"); + rc = -EINVAL; + goto fail; + } + if (block) + rc = bnxt_qplib_rcfw_block_for_resp(rcfw, + le16_to_cpu(req.cookie)); + else + rc = bnxt_qplib_rcfw_wait_for_resp(rcfw, + le16_to_cpu(req.cookie)); + if (!rc) { + /* Cmd timed out */ + dev_err(&res->pdev->dev, "SP: REG_MR timed out"); + rc = -ETIMEDOUT; + goto fail; + } + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&res->pdev->dev, "QPLIB: SP: REG_MR failed "); + dev_err(&res->pdev->dev, + "QPLIB: SP: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + rc = -EINVAL; + goto fail; + } + return 0; + +fail: + if (mr->hwq.max_elements) + bnxt_qplib_free_hwq(res->pdev, &mr->hwq); + return rc; +} + +int bnxt_qplib_alloc_fast_reg_page_list(struct bnxt_qplib_res *res, + struct bnxt_qplib_frpl *frpl, + int max_pg_ptrs) +{ + int pg_ptrs, pages, rc; + + /* Re-calculate the max to fit the HWQ allocation model */ + pg_ptrs = roundup_pow_of_two(max_pg_ptrs); + pages = pg_ptrs >> MAX_PBL_LVL_1_PGS_SHIFT; + if (!pages) + pages++; + + if (pages > MAX_PBL_LVL_1_PGS) + return -ENOMEM; + + frpl->hwq.max_elements = pages; + rc = bnxt_qplib_alloc_init_hwq(res->pdev, &frpl->hwq, NULL, 0, + &frpl->hwq.max_elements, PAGE_SIZE, 0, + PAGE_SIZE, HWQ_TYPE_CTX); + if (!rc) + frpl->max_pg_ptrs = pg_ptrs; + + return rc; +} + +int bnxt_qplib_free_fast_reg_page_list(struct bnxt_qplib_res *res, + struct bnxt_qplib_frpl *frpl) +{ + bnxt_qplib_free_hwq(res->pdev, &frpl->hwq); + return 0; +} + +int bnxt_qplib_map_tc2cos(struct bnxt_qplib_res *res, u16 *cids) +{ + struct bnxt_qplib_rcfw *rcfw = res->rcfw; + struct cmdq_map_tc_to_cos req; + struct creq_map_tc_to_cos_resp *resp; + u16 cmd_flags = 0; + int tleft; + + RCFW_CMD_PREP(req, MAP_TC_TO_COS, cmd_flags); + req.cos0 = cpu_to_le16(cids[0]); + req.cos1 = cpu_to_le16(cids[1]); + + resp = bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, NULL, 0); + if (!resp) { + dev_err(&res->pdev->dev, "QPLIB: SP: MAP_TC2COS send failed"); + return -EINVAL; + } + + tleft = bnxt_qplib_rcfw_block_for_resp(rcfw, le16_to_cpu(req.cookie)); + if (!tleft) { + dev_err(&res->pdev->dev, "QPLIB: SP: MAP_TC2COS timed out"); + return -ETIMEDOUT; + } + + if (resp->status || + le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { + dev_err(&res->pdev->dev, "QPLIB: SP: MAP_TC2COS failed "); + dev_err(&res->pdev->dev, + "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", + resp->status, le16_to_cpu(req.cookie), + le16_to_cpu(resp->cookie)); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.h b/drivers/infiniband/hw/bnxt_re/qplib_sp.h new file mode 100644 index 000000000000..1442a617e968 --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.h @@ -0,0 +1,160 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: Slow Path Operators (header) + * + */ + +#ifndef __BNXT_QPLIB_SP_H__ +#define __BNXT_QPLIB_SP_H__ + +struct bnxt_qplib_dev_attr { + char fw_ver[32]; + u16 max_sgid; + u16 max_mrw; + u32 max_qp; +#define BNXT_QPLIB_MAX_OUT_RD_ATOM 126 + u32 max_qp_rd_atom; + u32 max_qp_init_rd_atom; + u32 max_qp_wqes; + u32 max_qp_sges; + u32 max_cq; + u32 max_cq_wqes; + u32 max_cq_sges; + u32 max_mr; + u64 max_mr_size; + u32 max_pd; + u32 max_mw; + u32 max_raw_ethy_qp; + u32 max_ah; + u32 max_fmr; + u32 max_map_per_fmr; + u32 max_srq; + u32 max_srq_wqes; + u32 max_srq_sges; + u32 max_pkey; + u32 max_inline_data; + u32 l2_db_size; + u8 tqm_alloc_reqs[MAX_TQM_ALLOC_REQ]; +}; + +struct bnxt_qplib_pd { + u32 id; +}; + +struct bnxt_qplib_gid { + u8 data[16]; +}; + +struct bnxt_qplib_ah { + struct bnxt_qplib_gid dgid; + struct bnxt_qplib_pd *pd; + u32 id; + u8 sgid_index; + /* For Query AH if the hw table and SW table are differnt */ + u8 host_sgid_index; + u8 traffic_class; + u32 flow_label; + u8 hop_limit; + u8 sl; + u8 dmac[6]; + u16 vlan_id; + u8 nw_type; +}; + +struct bnxt_qplib_mrw { + struct bnxt_qplib_pd *pd; + int type; + u32 flags; +#define BNXT_QPLIB_FR_PMR 0x80000000 + u32 lkey; + u32 rkey; +#define BNXT_QPLIB_RSVD_LKEY 0xFFFFFFFF + u64 va; + u64 total_size; + u32 npages; + u64 mr_handle; + struct bnxt_qplib_hwq hwq; +}; + +struct bnxt_qplib_frpl { + int max_pg_ptrs; + struct bnxt_qplib_hwq hwq; +}; + +#define BNXT_QPLIB_ACCESS_LOCAL_WRITE BIT(0) +#define BNXT_QPLIB_ACCESS_REMOTE_READ BIT(1) +#define BNXT_QPLIB_ACCESS_REMOTE_WRITE BIT(2) +#define BNXT_QPLIB_ACCESS_REMOTE_ATOMIC BIT(3) +#define BNXT_QPLIB_ACCESS_MW_BIND BIT(4) +#define BNXT_QPLIB_ACCESS_ZERO_BASED BIT(5) +#define BNXT_QPLIB_ACCESS_ON_DEMAND BIT(6) + +int bnxt_qplib_get_sgid(struct bnxt_qplib_res *res, + struct bnxt_qplib_sgid_tbl *sgid_tbl, int index, + struct bnxt_qplib_gid *gid); +int bnxt_qplib_del_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, + struct bnxt_qplib_gid *gid, bool update); +int bnxt_qplib_add_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, + struct bnxt_qplib_gid *gid, u8 *mac, u16 vlan_id, + bool update, u32 *index); +int bnxt_qplib_get_pkey(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 index, + u16 *pkey); +int bnxt_qplib_del_pkey(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 *pkey, + bool update); +int bnxt_qplib_add_pkey(struct bnxt_qplib_res *res, + struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 *pkey, + bool update); +int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, + struct bnxt_qplib_dev_attr *attr); +int bnxt_qplib_create_ah(struct bnxt_qplib_res *res, struct bnxt_qplib_ah *ah); +int bnxt_qplib_destroy_ah(struct bnxt_qplib_res *res, struct bnxt_qplib_ah *ah); +int bnxt_qplib_alloc_mrw(struct bnxt_qplib_res *res, + struct bnxt_qplib_mrw *mrw); +int bnxt_qplib_dereg_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw, + bool block); +int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr, + u64 *pbl_tbl, int num_pbls, bool block); +int bnxt_qplib_free_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr); +int bnxt_qplib_alloc_fast_reg_mr(struct bnxt_qplib_res *res, + struct bnxt_qplib_mrw *mr, int max); +int bnxt_qplib_alloc_fast_reg_page_list(struct bnxt_qplib_res *res, + struct bnxt_qplib_frpl *frpl, int max); +int bnxt_qplib_free_fast_reg_page_list(struct bnxt_qplib_res *res, + struct bnxt_qplib_frpl *frpl); +int bnxt_qplib_map_tc2cos(struct bnxt_qplib_res *res, u16 *cids); +#endif /* __BNXT_QPLIB_SP_H__*/ diff --git a/drivers/infiniband/hw/bnxt_re/roce_hsi.h b/drivers/infiniband/hw/bnxt_re/roce_hsi.h new file mode 100644 index 000000000000..fc23477ac52f --- /dev/null +++ b/drivers/infiniband/hw/bnxt_re/roce_hsi.h @@ -0,0 +1,2821 @@ +/* + * Broadcom NetXtreme-E RoCE driver. + * + * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term + * Broadcom refers to Broadcom Limited and/or its subsidiaries. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Description: RoCE HSI File - Autogenerated + */ + +#ifndef __BNXT_RE_HSI_H__ +#define __BNXT_RE_HSI_H__ + +/* include bnxt_hsi.h from bnxt_en driver */ +#include "bnxt_hsi.h" + +/* CMP Door Bell Format (4 bytes) */ +struct cmpl_doorbell { + __le32 key_mask_valid_idx; + #define CMPL_DOORBELL_IDX_MASK 0xffffffUL + #define CMPL_DOORBELL_IDX_SFT 0 + #define CMPL_DOORBELL_RESERVED_MASK 0x3000000UL + #define CMPL_DOORBELL_RESERVED_SFT 24 + #define CMPL_DOORBELL_IDX_VALID 0x4000000UL + #define CMPL_DOORBELL_MASK 0x8000000UL + #define CMPL_DOORBELL_KEY_MASK 0xf0000000UL + #define CMPL_DOORBELL_KEY_SFT 28 + #define CMPL_DOORBELL_KEY_CMPL (0x2UL << 28) +}; + +/* Status Door Bell Format (4 bytes) */ +struct status_doorbell { + __le32 key_idx; + #define STATUS_DOORBELL_IDX_MASK 0xffffffUL + #define STATUS_DOORBELL_IDX_SFT 0 + #define STATUS_DOORBELL_RESERVED_MASK 0xf000000UL + #define STATUS_DOORBELL_RESERVED_SFT 24 + #define STATUS_DOORBELL_KEY_MASK 0xf0000000UL + #define STATUS_DOORBELL_KEY_SFT 28 + #define STATUS_DOORBELL_KEY_STAT (0x3UL << 28) +}; + +/* RoCE Host Structures */ + +/* Doorbell Structures */ +/* 64b Doorbell Format (8 bytes) */ +struct dbr_dbr { + __le32 index; + #define DBR_DBR_INDEX_MASK 0xfffffUL + #define DBR_DBR_INDEX_SFT 0 + #define DBR_DBR_RESERVED12_MASK 0xfff00000UL + #define DBR_DBR_RESERVED12_SFT 20 + __le32 type_xid; + #define DBR_DBR_XID_MASK 0xfffffUL + #define DBR_DBR_XID_SFT 0 + #define DBR_DBR_RESERVED8_MASK 0xff00000UL + #define DBR_DBR_RESERVED8_SFT 20 + #define DBR_DBR_TYPE_MASK 0xf0000000UL + #define DBR_DBR_TYPE_SFT 28 + #define DBR_DBR_TYPE_SQ (0x0UL << 28) + #define DBR_DBR_TYPE_RQ (0x1UL << 28) + #define DBR_DBR_TYPE_SRQ (0x2UL << 28) + #define DBR_DBR_TYPE_SRQ_ARM (0x3UL << 28) + #define DBR_DBR_TYPE_CQ (0x4UL << 28) + #define DBR_DBR_TYPE_CQ_ARMSE (0x5UL << 28) + #define DBR_DBR_TYPE_CQ_ARMALL (0x6UL << 28) + #define DBR_DBR_TYPE_CQ_ARMENA (0x7UL << 28) + #define DBR_DBR_TYPE_SRQ_ARMENA (0x8UL << 28) + #define DBR_DBR_TYPE_CQ_CUTOFF_ACK (0x9UL << 28) + #define DBR_DBR_TYPE_NULL (0xfUL << 28) +}; + +/* 32b Doorbell Format (4 bytes) */ +struct dbr_dbr32 { + __le32 type_abs_incr_xid; + #define DBR_DBR32_XID_MASK 0xfffffUL + #define DBR_DBR32_XID_SFT 0 + #define DBR_DBR32_RESERVED4_MASK 0xf00000UL + #define DBR_DBR32_RESERVED4_SFT 20 + #define DBR_DBR32_INCR_MASK 0xf000000UL + #define DBR_DBR32_INCR_SFT 24 + #define DBR_DBR32_ABS 0x10000000UL + #define DBR_DBR32_TYPE_MASK 0xe0000000UL + #define DBR_DBR32_TYPE_SFT 29 + #define DBR_DBR32_TYPE_SQ (0x0UL << 29) +}; + +/* SQ WQE Structures */ +/* Base SQ WQE (8 bytes) */ +struct sq_base { + u8 wqe_type; + #define SQ_BASE_WQE_TYPE_SEND 0x0UL + #define SQ_BASE_WQE_TYPE_SEND_W_IMMEAD 0x1UL + #define SQ_BASE_WQE_TYPE_SEND_W_INVALID 0x2UL + #define SQ_BASE_WQE_TYPE_WRITE_WQE 0x4UL + #define SQ_BASE_WQE_TYPE_WRITE_W_IMMEAD 0x5UL + #define SQ_BASE_WQE_TYPE_READ_WQE 0x6UL + #define SQ_BASE_WQE_TYPE_ATOMIC_CS 0x8UL + #define SQ_BASE_WQE_TYPE_ATOMIC_FA 0xbUL + #define SQ_BASE_WQE_TYPE_LOCAL_INVALID 0xcUL + #define SQ_BASE_WQE_TYPE_FR_PMR 0xdUL + #define SQ_BASE_WQE_TYPE_BIND 0xeUL + u8 unused_0[7]; +}; + +/* WQE SGE (16 bytes) */ +struct sq_sge { + __le64 va_or_pa; + __le32 l_key; + __le32 size; +}; + +/* PSN Search Structure (8 bytes) */ +struct sq_psn_search { + __le32 opcode_start_psn; + #define SQ_PSN_SEARCH_START_PSN_MASK 0xffffffUL + #define SQ_PSN_SEARCH_START_PSN_SFT 0 + #define SQ_PSN_SEARCH_OPCODE_MASK 0xff000000UL + #define SQ_PSN_SEARCH_OPCODE_SFT 24 + __le32 flags_next_psn; + #define SQ_PSN_SEARCH_NEXT_PSN_MASK 0xffffffUL + #define SQ_PSN_SEARCH_NEXT_PSN_SFT 0 + #define SQ_PSN_SEARCH_FLAGS_MASK 0xff000000UL + #define SQ_PSN_SEARCH_FLAGS_SFT 24 +}; + +/* Send SQ WQE (40 bytes) */ +struct sq_send { + u8 wqe_type; + #define SQ_SEND_WQE_TYPE_SEND 0x0UL + #define SQ_SEND_WQE_TYPE_SEND_W_IMMEAD 0x1UL + #define SQ_SEND_WQE_TYPE_SEND_W_INVALID 0x2UL + u8 flags; + #define SQ_SEND_FLAGS_SIGNAL_COMP 0x1UL + #define SQ_SEND_FLAGS_RD_OR_ATOMIC_FENCE 0x2UL + #define SQ_SEND_FLAGS_UC_FENCE 0x4UL + #define SQ_SEND_FLAGS_SE 0x8UL + #define SQ_SEND_FLAGS_INLINE 0x10UL + u8 wqe_size; + u8 reserved8_1; + __le32 inv_key_or_imm_data; + __le32 length; + __le32 q_key; + __le32 dst_qp; + #define SQ_SEND_DST_QP_MASK 0xffffffUL + #define SQ_SEND_DST_QP_SFT 0 + #define SQ_SEND_RESERVED8_2_MASK 0xff000000UL + #define SQ_SEND_RESERVED8_2_SFT 24 + __le32 avid; + #define SQ_SEND_AVID_MASK 0xfffffUL + #define SQ_SEND_AVID_SFT 0 + #define SQ_SEND_RESERVED_AVID_MASK 0xfff00000UL + #define SQ_SEND_RESERVED_AVID_SFT 20 + __le64 reserved64; + __le32 data[24]; +}; + +/* Send Raw Ethernet and QP1 SQ WQE (40 bytes) */ +struct sq_send_raweth_qp1 { + u8 wqe_type; + #define SQ_SEND_RAWETH_QP1_WQE_TYPE_SEND 0x0UL + u8 flags; + #define SQ_SEND_RAWETH_QP1_FLAGS_SIGNAL_COMP 0x1UL + #define SQ_SEND_RAWETH_QP1_FLAGS_RD_OR_ATOMIC_FENCE 0x2UL + #define SQ_SEND_RAWETH_QP1_FLAGS_UC_FENCE 0x4UL + #define SQ_SEND_RAWETH_QP1_FLAGS_SE 0x8UL + #define SQ_SEND_RAWETH_QP1_FLAGS_INLINE 0x10UL + u8 wqe_size; + u8 reserved8; + __le16 lflags; + #define SQ_SEND_RAWETH_QP1_LFLAGS_TCP_UDP_CHKSUM 0x1UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_IP_CHKSUM 0x2UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_NOCRC 0x4UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_STAMP 0x8UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_T_IP_CHKSUM 0x10UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_RESERVED1_1 0x20UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_RESERVED1_2 0x40UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_RESERVED1_3 0x80UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_ROCE_CRC 0x100UL + #define SQ_SEND_RAWETH_QP1_LFLAGS_FCOE_CRC 0x200UL + __le16 cfa_action; + __le32 length; + __le32 reserved32_1; + __le32 cfa_meta; + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_VID_MASK 0xfffUL + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_VID_SFT 0 + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_DE 0x1000UL + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_PRI_MASK 0xe000UL + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_PRI_SFT 13 + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_MASK 0x70000UL + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_SFT 16 + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_TPID88A8 (0x0UL << 16) + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_TPID8100 (0x1UL << 16) + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_TPID9100 (0x2UL << 16) + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_TPID9200 (0x3UL << 16) + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_TPID9300 (0x4UL << 16) + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_TPIDCFG (0x5UL << 16) + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_LAST \ + SQ_SEND_RAWETH_QP1_CFA_META_VLAN_TPID_TPIDCFG + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_RESERVED_MASK 0xff80000UL + #define SQ_SEND_RAWETH_QP1_CFA_META_VLAN_RESERVED_SFT 19 + #define SQ_SEND_RAWETH_QP1_CFA_META_KEY_MASK 0xf0000000UL + #define SQ_SEND_RAWETH_QP1_CFA_META_KEY_SFT 28 + #define SQ_SEND_RAWETH_QP1_CFA_META_KEY_NONE (0x0UL << 28) + #define SQ_SEND_RAWETH_QP1_CFA_META_KEY_VLAN_TAG (0x1UL << 28) + #define SQ_SEND_RAWETH_QP1_CFA_META_KEY_LAST \ + SQ_SEND_RAWETH_QP1_CFA_META_KEY_VLAN_TAG + __le32 reserved32_2; + __le64 reserved64; + __le32 data[24]; +}; + +/* RDMA SQ WQE (40 bytes) */ +struct sq_rdma { + u8 wqe_type; + #define SQ_RDMA_WQE_TYPE_WRITE_WQE 0x4UL + #define SQ_RDMA_WQE_TYPE_WRITE_W_IMMEAD 0x5UL + #define SQ_RDMA_WQE_TYPE_READ_WQE 0x6UL + u8 flags; + #define SQ_RDMA_FLAGS_SIGNAL_COMP 0x1UL + #define SQ_RDMA_FLAGS_RD_OR_ATOMIC_FENCE 0x2UL + #define SQ_RDMA_FLAGS_UC_FENCE 0x4UL + #define SQ_RDMA_FLAGS_SE 0x8UL + #define SQ_RDMA_FLAGS_INLINE 0x10UL + u8 wqe_size; + u8 reserved8; + __le32 imm_data; + __le32 length; + __le32 reserved32_1; + __le64 remote_va; + __le32 remote_key; + __le32 reserved32_2; + __le32 data[24]; +}; + +/* Atomic SQ WQE (40 bytes) */ +struct sq_atomic { + u8 wqe_type; + #define SQ_ATOMIC_WQE_TYPE_ATOMIC_CS 0x8UL + #define SQ_ATOMIC_WQE_TYPE_ATOMIC_FA 0xbUL + u8 flags; + #define SQ_ATOMIC_FLAGS_SIGNAL_COMP 0x1UL + #define SQ_ATOMIC_FLAGS_RD_OR_ATOMIC_FENCE 0x2UL + #define SQ_ATOMIC_FLAGS_UC_FENCE 0x4UL + #define SQ_ATOMIC_FLAGS_SE 0x8UL + #define SQ_ATOMIC_FLAGS_INLINE 0x10UL + __le16 reserved16; + __le32 remote_key; + __le64 remote_va; + __le64 swap_data; + __le64 cmp_data; + __le32 data[24]; +}; + +/* Local Invalidate SQ WQE (40 bytes) */ +struct sq_localinvalidate { + u8 wqe_type; + #define SQ_LOCALINVALIDATE_WQE_TYPE_LOCAL_INVALID 0xcUL + u8 flags; + #define SQ_LOCALINVALIDATE_FLAGS_SIGNAL_COMP 0x1UL + #define SQ_LOCALINVALIDATE_FLAGS_RD_OR_ATOMIC_FENCE 0x2UL + #define SQ_LOCALINVALIDATE_FLAGS_UC_FENCE 0x4UL + #define SQ_LOCALINVALIDATE_FLAGS_SE 0x8UL + #define SQ_LOCALINVALIDATE_FLAGS_INLINE 0x10UL + __le16 reserved16; + __le32 inv_l_key; + __le64 reserved64; + __le32 reserved128[4]; + __le32 data[24]; +}; + +/* FR-PMR SQ WQE (40 bytes) */ +struct sq_fr_pmr { + u8 wqe_type; + #define SQ_FR_PMR_WQE_TYPE_FR_PMR 0xdUL + u8 flags; + #define SQ_FR_PMR_FLAGS_SIGNAL_COMP 0x1UL + #define SQ_FR_PMR_FLAGS_RD_OR_ATOMIC_FENCE 0x2UL + #define SQ_FR_PMR_FLAGS_UC_FENCE 0x4UL + #define SQ_FR_PMR_FLAGS_SE 0x8UL + #define SQ_FR_PMR_FLAGS_INLINE 0x10UL + u8 access_cntl; + #define SQ_FR_PMR_ACCESS_CNTL_LOCAL_WRITE 0x1UL + #define SQ_FR_PMR_ACCESS_CNTL_REMOTE_READ 0x2UL + #define SQ_FR_PMR_ACCESS_CNTL_REMOTE_WRITE 0x4UL + #define SQ_FR_PMR_ACCESS_CNTL_REMOTE_ATOMIC 0x8UL + #define SQ_FR_PMR_ACCESS_CNTL_WINDOW_BIND 0x10UL + u8 zero_based_page_size_log; + #define SQ_FR_PMR_PAGE_SIZE_LOG_MASK 0x1fUL + #define SQ_FR_PMR_PAGE_SIZE_LOG_SFT 0 + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_4K 0x0UL + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_8K 0x1UL + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_64K 0x4UL + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_256K 0x6UL + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_1M 0x8UL + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_2M 0x9UL + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_4M 0xaUL + #define SQ_FR_PMR_PAGE_SIZE_LOG_PGSZ_1G 0x12UL + #define SQ_FR_PMR_ZERO_BASED 0x20UL + #define SQ_FR_PMR_RESERVED2_MASK 0xc0UL + #define SQ_FR_PMR_RESERVED2_SFT 6 + __le32 l_key; + u8 length[5]; + u8 reserved8_1; + u8 reserved8_2; + u8 numlevels_pbl_page_size_log; + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_MASK 0x1fUL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_SFT 0 + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_4K 0x0UL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_8K 0x1UL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_64K 0x4UL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_256K 0x6UL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_1M 0x8UL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_2M 0x9UL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_4M 0xaUL + #define SQ_FR_PMR_PBL_PAGE_SIZE_LOG_PGSZ_1G 0x12UL + #define SQ_FR_PMR_RESERVED1 0x20UL + #define SQ_FR_PMR_NUMLEVELS_MASK 0xc0UL + #define SQ_FR_PMR_NUMLEVELS_SFT 6 + #define SQ_FR_PMR_NUMLEVELS_PHYSICAL (0x0UL << 6) + #define SQ_FR_PMR_NUMLEVELS_LAYER1 (0x1UL << 6) + #define SQ_FR_PMR_NUMLEVELS_LAYER2 (0x2UL << 6) + __le64 pblptr; + __le64 va; + __le32 data[24]; +}; + +/* Bind SQ WQE (40 bytes) */ +struct sq_bind { + u8 wqe_type; + #define SQ_BIND_WQE_TYPE_BIND 0xeUL + u8 flags; + #define SQ_BIND_FLAGS_SIGNAL_COMP 0x1UL + #define SQ_BIND_FLAGS_RD_OR_ATOMIC_FENCE 0x2UL + #define SQ_BIND_FLAGS_UC_FENCE 0x4UL + #define SQ_BIND_FLAGS_SE 0x8UL + #define SQ_BIND_FLAGS_INLINE 0x10UL + u8 access_cntl; + #define SQ_BIND_ACCESS_CNTL_LOCAL_WRITE 0x1UL + #define SQ_BIND_ACCESS_CNTL_REMOTE_READ 0x2UL + #define SQ_BIND_ACCESS_CNTL_REMOTE_WRITE 0x4UL + #define SQ_BIND_ACCESS_CNTL_REMOTE_ATOMIC 0x8UL + #define SQ_BIND_ACCESS_CNTL_WINDOW_BIND 0x10UL + u8 reserved8_1; + u8 mw_type_zero_based; + #define SQ_BIND_ZERO_BASED 0x1UL + #define SQ_BIND_MW_TYPE 0x2UL + #define SQ_BIND_MW_TYPE_TYPE1 (0x0UL << 1) + #define SQ_BIND_MW_TYPE_TYPE2 (0x1UL << 1) + #define SQ_BIND_RESERVED6_MASK 0xfcUL + #define SQ_BIND_RESERVED6_SFT 2 + u8 reserved8_2; + __le16 reserved16; + __le32 parent_l_key; + __le32 l_key; + __le64 va; + u8 length[5]; + u8 data_reserved24[99]; + #define SQ_BIND_RESERVED24_MASK 0xffffff00UL + #define SQ_BIND_RESERVED24_SFT 8 + #define SQ_BIND_DATA_MASK 0xffffffffUL + #define SQ_BIND_DATA_SFT 0 +}; + +/* RQ/SRQ WQE Structures */ +/* RQ/SRQ WQE (40 bytes) */ +struct rq_wqe { + u8 wqe_type; + #define RQ_WQE_WQE_TYPE_RCV 0x80UL + u8 flags; + u8 wqe_size; + u8 reserved8; + __le32 reserved32; + __le32 wr_id[2]; + #define RQ_WQE_WR_ID_MASK 0xfffffUL + #define RQ_WQE_WR_ID_SFT 0 + #define RQ_WQE_RESERVED44_MASK 0xfff00000UL + #define RQ_WQE_RESERVED44_SFT 20 + __le32 reserved128[4]; + __le32 data[24]; +}; + +/* CQ CQE Structures */ +/* Base CQE (32 bytes) */ +struct cq_base { + __le64 reserved64_1; + __le64 reserved64_2; + __le64 reserved64_3; + u8 cqe_type_toggle; + #define CQ_BASE_TOGGLE 0x1UL + #define CQ_BASE_CQE_TYPE_MASK 0x1eUL + #define CQ_BASE_CQE_TYPE_SFT 1 + #define CQ_BASE_CQE_TYPE_REQ (0x0UL << 1) + #define CQ_BASE_CQE_TYPE_RES_RC (0x1UL << 1) + #define CQ_BASE_CQE_TYPE_RES_UD (0x2UL << 1) + #define CQ_BASE_CQE_TYPE_RES_RAWETH_QP1 (0x3UL << 1) + #define CQ_BASE_CQE_TYPE_TERMINAL (0xeUL << 1) + #define CQ_BASE_CQE_TYPE_CUT_OFF (0xfUL << 1) + #define CQ_BASE_RESERVED3_MASK 0xe0UL + #define CQ_BASE_RESERVED3_SFT 5 + u8 status; + __le16 reserved16; + __le32 reserved32; +}; + +/* Requester CQ CQE (32 bytes) */ +struct cq_req { + __le64 qp_handle; + __le16 sq_cons_idx; + __le16 reserved16_1; + __le32 reserved32_2; + __le64 reserved64; + u8 cqe_type_toggle; + #define CQ_REQ_TOGGLE 0x1UL + #define CQ_REQ_CQE_TYPE_MASK 0x1eUL + #define CQ_REQ_CQE_TYPE_SFT 1 + #define CQ_REQ_CQE_TYPE_REQ (0x0UL << 1) + #define CQ_REQ_RESERVED3_MASK 0xe0UL + #define CQ_REQ_RESERVED3_SFT 5 + u8 status; + #define CQ_REQ_STATUS_OK 0x0UL + #define CQ_REQ_STATUS_BAD_RESPONSE_ERR 0x1UL + #define CQ_REQ_STATUS_LOCAL_LENGTH_ERR 0x2UL + #define CQ_REQ_STATUS_LOCAL_QP_OPERATION_ERR 0x3UL + #define CQ_REQ_STATUS_LOCAL_PROTECTION_ERR 0x4UL + #define CQ_REQ_STATUS_MEMORY_MGT_OPERATION_ERR 0x5UL + #define CQ_REQ_STATUS_REMOTE_INVALID_REQUEST_ERR 0x6UL + #define CQ_REQ_STATUS_REMOTE_ACCESS_ERR 0x7UL + #define CQ_REQ_STATUS_REMOTE_OPERATION_ERR 0x8UL + #define CQ_REQ_STATUS_RNR_NAK_RETRY_CNT_ERR 0x9UL + #define CQ_REQ_STATUS_TRANSPORT_RETRY_CNT_ERR 0xaUL + #define CQ_REQ_STATUS_WORK_REQUEST_FLUSHED_ERR 0xbUL + __le16 reserved16_2; + __le32 reserved32_1; +}; + +/* Responder RC CQE (32 bytes) */ +struct cq_res_rc { + __le32 length; + __le32 imm_data_or_inv_r_key; + __le64 qp_handle; + __le64 mr_handle; + u8 cqe_type_toggle; + #define CQ_RES_RC_TOGGLE 0x1UL + #define CQ_RES_RC_CQE_TYPE_MASK 0x1eUL + #define CQ_RES_RC_CQE_TYPE_SFT 1 + #define CQ_RES_RC_CQE_TYPE_RES_RC (0x1UL << 1) + #define CQ_RES_RC_RESERVED3_MASK 0xe0UL + #define CQ_RES_RC_RESERVED3_SFT 5 + u8 status; + #define CQ_RES_RC_STATUS_OK 0x0UL + #define CQ_RES_RC_STATUS_LOCAL_ACCESS_ERROR 0x1UL + #define CQ_RES_RC_STATUS_LOCAL_LENGTH_ERR 0x2UL + #define CQ_RES_RC_STATUS_LOCAL_PROTECTION_ERR 0x3UL + #define CQ_RES_RC_STATUS_LOCAL_QP_OPERATION_ERR 0x4UL + #define CQ_RES_RC_STATUS_MEMORY_MGT_OPERATION_ERR 0x5UL + #define CQ_RES_RC_STATUS_REMOTE_INVALID_REQUEST_ERR 0x6UL + #define CQ_RES_RC_STATUS_WORK_REQUEST_FLUSHED_ERR 0x7UL + #define CQ_RES_RC_STATUS_HW_FLUSH_ERR 0x8UL + __le16 flags; + #define CQ_RES_RC_FLAGS_SRQ 0x1UL + #define CQ_RES_RC_FLAGS_SRQ_RQ (0x0UL << 0) + #define CQ_RES_RC_FLAGS_SRQ_SRQ (0x1UL << 0) + #define CQ_RES_RC_FLAGS_SRQ_LAST CQ_RES_RC_FLAGS_SRQ_SRQ + #define CQ_RES_RC_FLAGS_IMM 0x2UL + #define CQ_RES_RC_FLAGS_INV 0x4UL + #define CQ_RES_RC_FLAGS_RDMA 0x8UL + #define CQ_RES_RC_FLAGS_RDMA_SEND (0x0UL << 3) + #define CQ_RES_RC_FLAGS_RDMA_RDMA_WRITE (0x1UL << 3) + #define CQ_RES_RC_FLAGS_RDMA_LAST CQ_RES_RC_FLAGS_RDMA_RDMA_WRITE + __le32 srq_or_rq_wr_id; + #define CQ_RES_RC_SRQ_OR_RQ_WR_ID_MASK 0xfffffUL + #define CQ_RES_RC_SRQ_OR_RQ_WR_ID_SFT 0 + #define CQ_RES_RC_RESERVED12_MASK 0xfff00000UL + #define CQ_RES_RC_RESERVED12_SFT 20 +}; + +/* Responder UD CQE (32 bytes) */ +struct cq_res_ud { + __le32 length; + #define CQ_RES_UD_LENGTH_MASK 0x3fffUL + #define CQ_RES_UD_LENGTH_SFT 0 + #define CQ_RES_UD_RESERVED18_MASK 0xffffc000UL + #define CQ_RES_UD_RESERVED18_SFT 14 + __le32 imm_data; + __le64 qp_handle; + __le16 src_mac[3]; + __le16 src_qp_low; + u8 cqe_type_toggle; + #define CQ_RES_UD_TOGGLE 0x1UL + #define CQ_RES_UD_CQE_TYPE_MASK 0x1eUL + #define CQ_RES_UD_CQE_TYPE_SFT 1 + #define CQ_RES_UD_CQE_TYPE_RES_UD (0x2UL << 1) + #define CQ_RES_UD_RESERVED3_MASK 0xe0UL + #define CQ_RES_UD_RESERVED3_SFT 5 + u8 status; + #define CQ_RES_UD_STATUS_OK 0x0UL + #define CQ_RES_UD_STATUS_LOCAL_ACCESS_ERROR 0x1UL + #define CQ_RES_UD_STATUS_HW_LOCAL_LENGTH_ERR 0x2UL + #define CQ_RES_UD_STATUS_LOCAL_PROTECTION_ERR 0x3UL + #define CQ_RES_UD_STATUS_LOCAL_QP_OPERATION_ERR 0x4UL + #define CQ_RES_UD_STATUS_MEMORY_MGT_OPERATION_ERR 0x5UL + #define CQ_RES_UD_STATUS_WORK_REQUEST_FLUSHED_ERR 0x7UL + #define CQ_RES_UD_STATUS_HW_FLUSH_ERR 0x8UL + __le16 flags; + #define CQ_RES_UD_FLAGS_SRQ 0x1UL + #define CQ_RES_UD_FLAGS_SRQ_RQ (0x0UL << 0) + #define CQ_RES_UD_FLAGS_SRQ_SRQ (0x1UL << 0) + #define CQ_RES_UD_FLAGS_SRQ_LAST CQ_RES_UD_FLAGS_SRQ_SRQ + #define CQ_RES_UD_FLAGS_IMM 0x2UL + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_MASK 0xcUL + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_SFT 2 + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V1 (0x0UL << 2) + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV4 (0x2UL << 2) + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6 (0x3UL << 2) + #define CQ_RES_UD_FLAGS_ROCE_IP_VER_LAST \ + CQ_RES_UD_FLAGS_ROCE_IP_VER_V2IPV6 + __le32 src_qp_high_srq_or_rq_wr_id; + #define CQ_RES_UD_SRQ_OR_RQ_WR_ID_MASK 0xfffffUL + #define CQ_RES_UD_SRQ_OR_RQ_WR_ID_SFT 0 + #define CQ_RES_UD_RESERVED4_MASK 0xf00000UL + #define CQ_RES_UD_RESERVED4_SFT 20 + #define CQ_RES_UD_SRC_QP_HIGH_MASK 0xff000000UL + #define CQ_RES_UD_SRC_QP_HIGH_SFT 24 +}; + +/* Responder RawEth and QP1 CQE (32 bytes) */ +struct cq_res_raweth_qp1 { + __le16 length; + #define CQ_RES_RAWETH_QP1_LENGTH_MASK 0x3fffUL + #define CQ_RES_RAWETH_QP1_LENGTH_SFT 0 + #define CQ_RES_RAWETH_QP1_RESERVED2_MASK 0xc000UL + #define CQ_RES_RAWETH_QP1_RESERVED2_SFT 14 + __le16 raweth_qp1_flags; + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ERROR 0x1UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_RESERVED5_1_MASK 0x3eUL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_RESERVED5_1_SFT 1 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_MASK 0x3c0UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_SFT 6 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_NOT_KNOWN (0x0UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_IP (0x1UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_TCP (0x2UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_UDP (0x3UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_FCOE (0x4UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_ROCE (0x5UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_ICMP (0x7UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_PTP_WO_TIMESTAMP \ + (0x8UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_PTP_W_TIMESTAMP \ + (0x9UL << 6) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_LAST \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_ITYPE_PTP_W_TIMESTAMP + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_MASK 0x3ffUL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS_SFT 0 + #define CQ_RES_RAWETH_QP1_RESERVED6_MASK 0xfc00UL + #define CQ_RES_RAWETH_QP1_RESERVED6_SFT 10 + __le16 raweth_qp1_errors; + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_RESERVED4_MASK 0xfUL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_RESERVED4_SFT 0 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_IP_CS_ERROR 0x10UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_L4_CS_ERROR 0x20UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_IP_CS_ERROR 0x40UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_L4_CS_ERROR 0x80UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_CRC_ERROR 0x100UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_MASK 0xe00UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_SFT 9 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_NO_ERROR \ + (0x0UL << 9) + #define \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_T_L3_BAD_VERSION \ + (0x1UL << 9) + #define \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_T_L3_BAD_HDR_LEN \ + (0x2UL << 9) + #define \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_TUNNEL_TOTAL_ERROR \ + (0x3UL << 9) + #define \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_T_IP_TOTAL_ERROR \ + (0x4UL << 9) + #define \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_T_UDP_TOTAL_ERROR \ + (0x5UL << 9) + #define \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_T_L3_BAD_TTL \ + (0x6UL << 9) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_LAST \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_T_PKT_ERROR_T_L3_BAD_TTL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_MASK 0xf000UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_SFT 12 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_NO_ERROR \ + (0x0UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_L3_BAD_VERSION \ + (0x1UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_L3_BAD_HDR_LEN \ + (0x2UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_L3_BAD_TTL \ + (0x3UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_IP_TOTAL_ERROR \ + (0x4UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_UDP_TOTAL_ERROR \ + (0x5UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_L4_BAD_HDR_LEN \ + (0x6UL << 12) + #define \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_L4_BAD_HDR_LEN_TOO_SMALL\ + (0x7UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_L4_BAD_OPT_LEN \ + (0x8UL << 12) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_LAST \ + CQ_RES_RAWETH_QP1_RAWETH_QP1_ERRORS_PKT_ERROR_L4_BAD_OPT_LEN + __le16 raweth_qp1_cfa_code; + __le64 qp_handle; + __le32 raweth_qp1_flags2; + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_IP_CS_CALC 0x1UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_L4_CS_CALC 0x2UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_T_IP_CS_CALC 0x4UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_T_L4_CS_CALC 0x8UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_META_FORMAT_MASK 0xf0UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_META_FORMAT_SFT 4 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_META_FORMAT_NONE \ + (0x0UL << 4) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_META_FORMAT_VLAN \ + (0x1UL << 4) + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_META_FORMAT_LAST\ + CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_META_FORMAT_VLAN + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_FLAGS2_IP_TYPE 0x100UL + __le32 raweth_qp1_metadata; + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_METADATA_VID_MASK 0xfffUL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_METADATA_VID_SFT 0 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_METADATA_DE 0x1000UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_METADATA_PRI_MASK 0xe000UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_METADATA_PRI_SFT 13 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_METADATA_TPID_MASK 0xffff0000UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_METADATA_TPID_SFT 16 + u8 cqe_type_toggle; + #define CQ_RES_RAWETH_QP1_TOGGLE 0x1UL + #define CQ_RES_RAWETH_QP1_CQE_TYPE_MASK 0x1eUL + #define CQ_RES_RAWETH_QP1_CQE_TYPE_SFT 1 + #define CQ_RES_RAWETH_QP1_CQE_TYPE_RES_RAWETH_QP1 (0x3UL << 1) + #define CQ_RES_RAWETH_QP1_RESERVED3_MASK 0xe0UL + #define CQ_RES_RAWETH_QP1_RESERVED3_SFT 5 + u8 status; + #define CQ_RES_RAWETH_QP1_STATUS_OK 0x0UL + #define CQ_RES_RAWETH_QP1_STATUS_LOCAL_ACCESS_ERROR 0x1UL + #define CQ_RES_RAWETH_QP1_STATUS_HW_LOCAL_LENGTH_ERR 0x2UL + #define CQ_RES_RAWETH_QP1_STATUS_LOCAL_PROTECTION_ERR 0x3UL + #define CQ_RES_RAWETH_QP1_STATUS_LOCAL_QP_OPERATION_ERR 0x4UL + #define CQ_RES_RAWETH_QP1_STATUS_MEMORY_MGT_OPERATION_ERR 0x5UL + #define CQ_RES_RAWETH_QP1_STATUS_WORK_REQUEST_FLUSHED_ERR 0x7UL + #define CQ_RES_RAWETH_QP1_STATUS_HW_FLUSH_ERR 0x8UL + __le16 flags; + #define CQ_RES_RAWETH_QP1_FLAGS_SRQ 0x1UL + #define CQ_RES_RAWETH_QP1_FLAGS_SRQ_RQ 0x0UL + #define CQ_RES_RAWETH_QP1_FLAGS_SRQ_SRQ 0x1UL + #define CQ_RES_RAWETH_QP1_FLAGS_SRQ_LAST \ + CQ_RES_RAWETH_QP1_FLAGS_SRQ_SRQ + __le32 raweth_qp1_payload_offset_srq_or_rq_wr_id; + #define CQ_RES_RAWETH_QP1_SRQ_OR_RQ_WR_ID_MASK 0xfffffUL + #define CQ_RES_RAWETH_QP1_SRQ_OR_RQ_WR_ID_SFT 0 + #define CQ_RES_RAWETH_QP1_RESERVED4_MASK 0xf00000UL + #define CQ_RES_RAWETH_QP1_RESERVED4_SFT 20 + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_PAYLOAD_OFFSET_MASK 0xff000000UL + #define CQ_RES_RAWETH_QP1_RAWETH_QP1_PAYLOAD_OFFSET_SFT 24 +}; + +/* Terminal CQE (32 bytes) */ +struct cq_terminal { + __le64 qp_handle; + __le16 sq_cons_idx; + __le16 rq_cons_idx; + __le32 reserved32_1; + __le64 reserved64_3; + u8 cqe_type_toggle; + #define CQ_TERMINAL_TOGGLE 0x1UL + #define CQ_TERMINAL_CQE_TYPE_MASK 0x1eUL + #define CQ_TERMINAL_CQE_TYPE_SFT 1 + #define CQ_TERMINAL_CQE_TYPE_TERMINAL (0xeUL << 1) + #define CQ_TERMINAL_RESERVED3_MASK 0xe0UL + #define CQ_TERMINAL_RESERVED3_SFT 5 + u8 status; + #define CQ_TERMINAL_STATUS_OK 0x0UL + __le16 reserved16; + __le32 reserved32_2; +}; + +/* Cutoff CQE (32 bytes) */ +struct cq_cutoff { + __le64 reserved64_1; + __le64 reserved64_2; + __le64 reserved64_3; + u8 cqe_type_toggle; + #define CQ_CUTOFF_TOGGLE 0x1UL + #define CQ_CUTOFF_CQE_TYPE_MASK 0x1eUL + #define CQ_CUTOFF_CQE_TYPE_SFT 1 + #define CQ_CUTOFF_CQE_TYPE_CUT_OFF (0xfUL << 1) + #define CQ_CUTOFF_RESERVED3_MASK 0xe0UL + #define CQ_CUTOFF_RESERVED3_SFT 5 + u8 status; + #define CQ_CUTOFF_STATUS_OK 0x0UL + __le16 reserved16; + __le32 reserved32; +}; + +/* Notification Queue (NQ) Structures */ +/* Base NQ Record (16 bytes) */ +struct nq_base { + __le16 info10_type; + #define NQ_BASE_TYPE_MASK 0x3fUL + #define NQ_BASE_TYPE_SFT 0 + #define NQ_BASE_TYPE_CQ_NOTIFICATION 0x30UL + #define NQ_BASE_TYPE_SRQ_EVENT 0x32UL + #define NQ_BASE_TYPE_DBQ_EVENT 0x34UL + #define NQ_BASE_TYPE_QP_EVENT 0x38UL + #define NQ_BASE_TYPE_FUNC_EVENT 0x3aUL + #define NQ_BASE_INFO10_MASK 0xffc0UL + #define NQ_BASE_INFO10_SFT 6 + __le16 info16; + __le32 info32; + __le32 info63_v[2]; + #define NQ_BASE_V 0x1UL + #define NQ_BASE_INFO63_MASK 0xfffffffeUL + #define NQ_BASE_INFO63_SFT 1 +}; + +/* Completion Queue Notification (16 bytes) */ +struct nq_cn { + __le16 type; + #define NQ_CN_TYPE_MASK 0x3fUL + #define NQ_CN_TYPE_SFT 0 + #define NQ_CN_TYPE_CQ_NOTIFICATION 0x30UL + #define NQ_CN_RESERVED9_MASK 0xffc0UL + #define NQ_CN_RESERVED9_SFT 6 + __le16 reserved16; + __le32 cq_handle_low; + __le32 v; + #define NQ_CN_V 0x1UL + #define NQ_CN_RESERVED31_MASK 0xfffffffeUL + #define NQ_CN_RESERVED31_SFT 1 + __le32 cq_handle_high; +}; + +/* SRQ Event Notification (16 bytes) */ +struct nq_srq_event { + u8 type; + #define NQ_SRQ_EVENT_TYPE_MASK 0x3fUL + #define NQ_SRQ_EVENT_TYPE_SFT 0 + #define NQ_SRQ_EVENT_TYPE_SRQ_EVENT 0x32UL + #define NQ_SRQ_EVENT_RESERVED1_MASK 0xc0UL + #define NQ_SRQ_EVENT_RESERVED1_SFT 6 + u8 event; + #define NQ_SRQ_EVENT_EVENT_SRQ_THRESHOLD_EVENT 0x1UL + __le16 reserved16; + __le32 srq_handle_low; + __le32 v; + #define NQ_SRQ_EVENT_V 0x1UL + #define NQ_SRQ_EVENT_RESERVED31_MASK 0xfffffffeUL + #define NQ_SRQ_EVENT_RESERVED31_SFT 1 + __le32 srq_handle_high; +}; + +/* DBQ Async Event Notification (16 bytes) */ +struct nq_dbq_event { + u8 type; + #define NQ_DBQ_EVENT_TYPE_MASK 0x3fUL + #define NQ_DBQ_EVENT_TYPE_SFT 0 + #define NQ_DBQ_EVENT_TYPE_DBQ_EVENT 0x34UL + #define NQ_DBQ_EVENT_RESERVED1_MASK 0xc0UL + #define NQ_DBQ_EVENT_RESERVED1_SFT 6 + u8 event; + #define NQ_DBQ_EVENT_EVENT_DBQ_THRESHOLD_EVENT 0x1UL + __le16 db_pfid; + #define NQ_DBQ_EVENT_DB_PFID_MASK 0xfUL + #define NQ_DBQ_EVENT_DB_PFID_SFT 0 + #define NQ_DBQ_EVENT_RESERVED12_MASK 0xfff0UL + #define NQ_DBQ_EVENT_RESERVED12_SFT 4 + __le32 db_dpi; + #define NQ_DBQ_EVENT_DB_DPI_MASK 0xfffffUL + #define NQ_DBQ_EVENT_DB_DPI_SFT 0 + #define NQ_DBQ_EVENT_RESERVED12_2_MASK 0xfff00000UL + #define NQ_DBQ_EVENT_RESERVED12_2_SFT 20 + __le32 v; + #define NQ_DBQ_EVENT_V 0x1UL + #define NQ_DBQ_EVENT_RESERVED32_MASK 0xfffffffeUL + #define NQ_DBQ_EVENT_RESERVED32_SFT 1 + __le32 db_type_db_xid; + #define NQ_DBQ_EVENT_DB_XID_MASK 0xfffffUL + #define NQ_DBQ_EVENT_DB_XID_SFT 0 + #define NQ_DBQ_EVENT_RESERVED8_MASK 0xff00000UL + #define NQ_DBQ_EVENT_RESERVED8_SFT 20 + #define NQ_DBQ_EVENT_DB_TYPE_MASK 0xf0000000UL + #define NQ_DBQ_EVENT_DB_TYPE_SFT 28 +}; + +/* Read Request/Response Queue Structures */ +/* Input Read Request Queue (IRRQ) Message (32 bytes) */ +struct xrrq_irrq { + __le16 credits_type; + #define XRRQ_IRRQ_TYPE 0x1UL + #define XRRQ_IRRQ_TYPE_READ_REQ 0x0UL + #define XRRQ_IRRQ_TYPE_ATOMIC_REQ 0x1UL + #define XRRQ_IRRQ_RESERVED10_MASK 0x7feUL + #define XRRQ_IRRQ_RESERVED10_SFT 1 + #define XRRQ_IRRQ_CREDITS_MASK 0xf800UL + #define XRRQ_IRRQ_CREDITS_SFT 11 + __le16 reserved16; + __le32 reserved32; + __le32 psn; + #define XRRQ_IRRQ_PSN_MASK 0xffffffUL + #define XRRQ_IRRQ_PSN_SFT 0 + #define XRRQ_IRRQ_RESERVED8_1_MASK 0xff000000UL + #define XRRQ_IRRQ_RESERVED8_1_SFT 24 + __le32 msn; + #define XRRQ_IRRQ_MSN_MASK 0xffffffUL + #define XRRQ_IRRQ_MSN_SFT 0 + #define XRRQ_IRRQ_RESERVED8_2_MASK 0xff000000UL + #define XRRQ_IRRQ_RESERVED8_2_SFT 24 + __le64 va_or_atomic_result; + __le32 rdma_r_key; + __le32 length; +}; + +/* Output Read Request Queue (ORRQ) Message (32 bytes) */ +struct xrrq_orrq { + __le16 num_sges_type; + #define XRRQ_ORRQ_TYPE 0x1UL + #define XRRQ_ORRQ_TYPE_READ_REQ 0x0UL + #define XRRQ_ORRQ_TYPE_ATOMIC_REQ 0x1UL + #define XRRQ_ORRQ_RESERVED10_MASK 0x7feUL + #define XRRQ_ORRQ_RESERVED10_SFT 1 + #define XRRQ_ORRQ_NUM_SGES_MASK 0xf800UL + #define XRRQ_ORRQ_NUM_SGES_SFT 11 + __le16 reserved16; + __le32 length; + __le32 psn; + #define XRRQ_ORRQ_PSN_MASK 0xffffffUL + #define XRRQ_ORRQ_PSN_SFT 0 + #define XRRQ_ORRQ_RESERVED8_1_MASK 0xff000000UL + #define XRRQ_ORRQ_RESERVED8_1_SFT 24 + __le32 end_psn; + #define XRRQ_ORRQ_END_PSN_MASK 0xffffffUL + #define XRRQ_ORRQ_END_PSN_SFT 0 + #define XRRQ_ORRQ_RESERVED8_2_MASK 0xff000000UL + #define XRRQ_ORRQ_RESERVED8_2_SFT 24 + __le64 first_sge_phy_or_sing_sge_va; + __le32 single_sge_l_key; + __le32 single_sge_size; +}; + +/* Page Buffer List Memory Structures (PBL) */ +/* Page Table Entry (PTE) (8 bytes) */ +struct ptu_pte { + __le32 page_next_to_last_last_valid[2]; + #define PTU_PTE_VALID 0x1UL + #define PTU_PTE_LAST 0x2UL + #define PTU_PTE_NEXT_TO_LAST 0x4UL + #define PTU_PTE_PAGE_MASK 0xfffff000UL + #define PTU_PTE_PAGE_SFT 12 +}; + +/* Page Directory Entry (PDE) (8 bytes) */ +struct ptu_pde { + __le32 page_valid[2]; + #define PTU_PDE_VALID 0x1UL + #define PTU_PDE_PAGE_MASK 0xfffff000UL + #define PTU_PDE_PAGE_SFT 12 +}; + +/* RoCE Fastpath Host Structures */ +/* Command Queue (CMDQ) Interface */ +/* Init CMDQ (16 bytes) */ +struct cmdq_init { + __le64 cmdq_pbl; + __le16 cmdq_size_cmdq_lvl; + #define CMDQ_INIT_CMDQ_LVL_MASK 0x3UL + #define CMDQ_INIT_CMDQ_LVL_SFT 0 + #define CMDQ_INIT_CMDQ_SIZE_MASK 0xfffcUL + #define CMDQ_INIT_CMDQ_SIZE_SFT 2 + __le16 creq_ring_id; + __le32 prod_idx; +}; + +/* Update CMDQ producer index (16 bytes) */ +struct cmdq_update { + __le64 reserved64; + __le32 reserved32; + __le32 prod_idx; +}; + +/* CMDQ common header structure (16 bytes) */ +struct cmdq_base { + u8 opcode; + #define CMDQ_BASE_OPCODE_CREATE_QP 0x1UL + #define CMDQ_BASE_OPCODE_DESTROY_QP 0x2UL + #define CMDQ_BASE_OPCODE_MODIFY_QP 0x3UL + #define CMDQ_BASE_OPCODE_QUERY_QP 0x4UL + #define CMDQ_BASE_OPCODE_CREATE_SRQ 0x5UL + #define CMDQ_BASE_OPCODE_DESTROY_SRQ 0x6UL + #define CMDQ_BASE_OPCODE_QUERY_SRQ 0x8UL + #define CMDQ_BASE_OPCODE_CREATE_CQ 0x9UL + #define CMDQ_BASE_OPCODE_DESTROY_CQ 0xaUL + #define CMDQ_BASE_OPCODE_RESIZE_CQ 0xcUL + #define CMDQ_BASE_OPCODE_ALLOCATE_MRW 0xdUL + #define CMDQ_BASE_OPCODE_DEALLOCATE_KEY 0xeUL + #define CMDQ_BASE_OPCODE_REGISTER_MR 0xfUL + #define CMDQ_BASE_OPCODE_DEREGISTER_MR 0x10UL + #define CMDQ_BASE_OPCODE_ADD_GID 0x11UL + #define CMDQ_BASE_OPCODE_DELETE_GID 0x12UL + #define CMDQ_BASE_OPCODE_MODIFY_GID 0x17UL + #define CMDQ_BASE_OPCODE_QUERY_GID 0x18UL + #define CMDQ_BASE_OPCODE_CREATE_QP1 0x13UL + #define CMDQ_BASE_OPCODE_DESTROY_QP1 0x14UL + #define CMDQ_BASE_OPCODE_CREATE_AH 0x15UL + #define CMDQ_BASE_OPCODE_DESTROY_AH 0x16UL + #define CMDQ_BASE_OPCODE_INITIALIZE_FW 0x80UL + #define CMDQ_BASE_OPCODE_DEINITIALIZE_FW 0x81UL + #define CMDQ_BASE_OPCODE_STOP_FUNC 0x82UL + #define CMDQ_BASE_OPCODE_QUERY_FUNC 0x83UL + #define CMDQ_BASE_OPCODE_SET_FUNC_RESOURCES 0x84UL + #define CMDQ_BASE_OPCODE_READ_CONTEXT 0x85UL + #define CMDQ_BASE_OPCODE_VF_BACKCHANNEL_REQUEST 0x86UL + #define CMDQ_BASE_OPCODE_READ_VF_MEMORY 0x87UL + #define CMDQ_BASE_OPCODE_COMPLETE_VF_REQUEST 0x88UL + #define CMDQ_BASE_OPCODE_EXTEND_CONTEXT_ARRRAY 0x89UL + #define CMDQ_BASE_OPCODE_MAP_TC_TO_COS 0x8aUL + #define CMDQ_BASE_OPCODE_QUERY_VERSION 0x8bUL + #define CMDQ_BASE_OPCODE_MODIFY_CC 0x8cUL + #define CMDQ_BASE_OPCODE_QUERY_CC 0x8dUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; +}; + +/* Create QP command (96 bytes) */ +struct cmdq_create_qp { + u8 opcode; + #define CMDQ_CREATE_QP_OPCODE_CREATE_QP 0x1UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le64 qp_handle; + __le32 qp_flags; + #define CMDQ_CREATE_QP_QP_FLAGS_SRQ_USED 0x1UL + #define CMDQ_CREATE_QP_QP_FLAGS_FORCE_COMPLETION 0x2UL + #define CMDQ_CREATE_QP_QP_FLAGS_RESERVED_LKEY_ENABLE 0x4UL + #define CMDQ_CREATE_QP_QP_FLAGS_FR_PMR_ENABLED 0x8UL + u8 type; + #define CMDQ_CREATE_QP_TYPE_RC 0x2UL + #define CMDQ_CREATE_QP_TYPE_UD 0x4UL + #define CMDQ_CREATE_QP_TYPE_RAW_ETHERTYPE 0x6UL + u8 sq_pg_size_sq_lvl; + #define CMDQ_CREATE_QP_SQ_LVL_MASK 0xfUL + #define CMDQ_CREATE_QP_SQ_LVL_SFT 0 + #define CMDQ_CREATE_QP_SQ_LVL_LVL_0 0x0UL + #define CMDQ_CREATE_QP_SQ_LVL_LVL_1 0x1UL + #define CMDQ_CREATE_QP_SQ_LVL_LVL_2 0x2UL + #define CMDQ_CREATE_QP_SQ_PG_SIZE_MASK 0xf0UL + #define CMDQ_CREATE_QP_SQ_PG_SIZE_SFT 4 + #define CMDQ_CREATE_QP_SQ_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_CREATE_QP_SQ_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_CREATE_QP_SQ_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_CREATE_QP_SQ_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_CREATE_QP_SQ_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_CREATE_QP_SQ_PG_SIZE_PG_1G (0x5UL << 4) + u8 rq_pg_size_rq_lvl; + #define CMDQ_CREATE_QP_RQ_LVL_MASK 0xfUL + #define CMDQ_CREATE_QP_RQ_LVL_SFT 0 + #define CMDQ_CREATE_QP_RQ_LVL_LVL_0 0x0UL + #define CMDQ_CREATE_QP_RQ_LVL_LVL_1 0x1UL + #define CMDQ_CREATE_QP_RQ_LVL_LVL_2 0x2UL + #define CMDQ_CREATE_QP_RQ_PG_SIZE_MASK 0xf0UL + #define CMDQ_CREATE_QP_RQ_PG_SIZE_SFT 4 + #define CMDQ_CREATE_QP_RQ_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_CREATE_QP_RQ_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_CREATE_QP_RQ_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_CREATE_QP_RQ_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_CREATE_QP_RQ_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_CREATE_QP_RQ_PG_SIZE_PG_1G (0x5UL << 4) + u8 unused_0; + __le32 dpi; + __le32 sq_size; + __le32 rq_size; + __le16 sq_fwo_sq_sge; + #define CMDQ_CREATE_QP_SQ_SGE_MASK 0xfUL + #define CMDQ_CREATE_QP_SQ_SGE_SFT 0 + #define CMDQ_CREATE_QP_SQ_FWO_MASK 0xfff0UL + #define CMDQ_CREATE_QP_SQ_FWO_SFT 4 + __le16 rq_fwo_rq_sge; + #define CMDQ_CREATE_QP_RQ_SGE_MASK 0xfUL + #define CMDQ_CREATE_QP_RQ_SGE_SFT 0 + #define CMDQ_CREATE_QP_RQ_FWO_MASK 0xfff0UL + #define CMDQ_CREATE_QP_RQ_FWO_SFT 4 + __le32 scq_cid; + __le32 rcq_cid; + __le32 srq_cid; + __le32 pd_id; + __le64 sq_pbl; + __le64 rq_pbl; + __le64 irrq_addr; + __le64 orrq_addr; +}; + +/* Destroy QP command (24 bytes) */ +struct cmdq_destroy_qp { + u8 opcode; + #define CMDQ_DESTROY_QP_OPCODE_DESTROY_QP 0x2UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 qp_cid; + __le32 unused_0; +}; + +/* Modify QP command (112 bytes) */ +struct cmdq_modify_qp { + u8 opcode; + #define CMDQ_MODIFY_QP_OPCODE_MODIFY_QP 0x3UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 modify_mask; + #define CMDQ_MODIFY_QP_MODIFY_MASK_STATE 0x1UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_EN_SQD_ASYNC_NOTIFY 0x2UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_ACCESS 0x4UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_PKEY 0x8UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_QKEY 0x10UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_DGID 0x20UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_FLOW_LABEL 0x40UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX 0x80UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_HOP_LIMIT 0x100UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS 0x200UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_DEST_MAC 0x400UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU 0x1000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_TIMEOUT 0x2000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_RETRY_CNT 0x4000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_RNR_RETRY 0x8000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_RQ_PSN 0x10000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_MAX_RD_ATOMIC 0x20000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER 0x40000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_SQ_PSN 0x80000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC 0x100000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SIZE 0x200000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SIZE 0x400000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SGE 0x800000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SGE 0x1000000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_MAX_INLINE_DATA 0x2000000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_DEST_QP_ID 0x4000000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_SRC_MAC 0x8000000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_VLAN_ID 0x10000000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_ENABLE_CC 0x20000000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_TOS_ECN 0x40000000UL + #define CMDQ_MODIFY_QP_MODIFY_MASK_TOS_DSCP 0x80000000UL + __le32 qp_cid; + u8 network_type_en_sqd_async_notify_new_state; + #define CMDQ_MODIFY_QP_NEW_STATE_MASK 0xfUL + #define CMDQ_MODIFY_QP_NEW_STATE_SFT 0 + #define CMDQ_MODIFY_QP_NEW_STATE_RESET 0x0UL + #define CMDQ_MODIFY_QP_NEW_STATE_INIT 0x1UL + #define CMDQ_MODIFY_QP_NEW_STATE_RTR 0x2UL + #define CMDQ_MODIFY_QP_NEW_STATE_RTS 0x3UL + #define CMDQ_MODIFY_QP_NEW_STATE_SQD 0x4UL + #define CMDQ_MODIFY_QP_NEW_STATE_SQE 0x5UL + #define CMDQ_MODIFY_QP_NEW_STATE_ERR 0x6UL + #define CMDQ_MODIFY_QP_EN_SQD_ASYNC_NOTIFY 0x10UL + #define CMDQ_MODIFY_QP_NETWORK_TYPE_MASK 0xc0UL + #define CMDQ_MODIFY_QP_NETWORK_TYPE_SFT 6 + #define CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV1 (0x0UL << 6) + #define CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV4 (0x2UL << 6) + #define CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV6 (0x3UL << 6) + u8 access; + #define CMDQ_MODIFY_QP_ACCESS_LOCAL_WRITE 0x1UL + #define CMDQ_MODIFY_QP_ACCESS_REMOTE_WRITE 0x2UL + #define CMDQ_MODIFY_QP_ACCESS_REMOTE_READ 0x4UL + #define CMDQ_MODIFY_QP_ACCESS_REMOTE_ATOMIC 0x8UL + __le16 pkey; + __le32 qkey; + __le32 dgid[4]; + __le32 flow_label; + __le16 sgid_index; + u8 hop_limit; + u8 traffic_class; + __le16 dest_mac[3]; + u8 tos_dscp_tos_ecn; + #define CMDQ_MODIFY_QP_TOS_ECN_MASK 0x3UL + #define CMDQ_MODIFY_QP_TOS_ECN_SFT 0 + #define CMDQ_MODIFY_QP_TOS_DSCP_MASK 0xfcUL + #define CMDQ_MODIFY_QP_TOS_DSCP_SFT 2 + u8 path_mtu; + #define CMDQ_MODIFY_QP_PATH_MTU_MASK 0xf0UL + #define CMDQ_MODIFY_QP_PATH_MTU_SFT 4 + #define CMDQ_MODIFY_QP_PATH_MTU_MTU_256 (0x0UL << 4) + #define CMDQ_MODIFY_QP_PATH_MTU_MTU_512 (0x1UL << 4) + #define CMDQ_MODIFY_QP_PATH_MTU_MTU_1024 (0x2UL << 4) + #define CMDQ_MODIFY_QP_PATH_MTU_MTU_2048 (0x3UL << 4) + #define CMDQ_MODIFY_QP_PATH_MTU_MTU_4096 (0x4UL << 4) + #define CMDQ_MODIFY_QP_PATH_MTU_MTU_8192 (0x5UL << 4) + u8 timeout; + u8 retry_cnt; + u8 rnr_retry; + u8 min_rnr_timer; + __le32 rq_psn; + __le32 sq_psn; + u8 max_rd_atomic; + u8 max_dest_rd_atomic; + __le16 enable_cc; + #define CMDQ_MODIFY_QP_ENABLE_CC 0x1UL + __le32 sq_size; + __le32 rq_size; + __le16 sq_sge; + __le16 rq_sge; + __le32 max_inline_data; + __le32 dest_qp_id; + __le32 unused_3; + __le16 src_mac[3]; + __le16 vlan_pcp_vlan_dei_vlan_id; + #define CMDQ_MODIFY_QP_VLAN_ID_MASK 0xfffUL + #define CMDQ_MODIFY_QP_VLAN_ID_SFT 0 + #define CMDQ_MODIFY_QP_VLAN_DEI 0x1000UL + #define CMDQ_MODIFY_QP_VLAN_PCP_MASK 0xe000UL + #define CMDQ_MODIFY_QP_VLAN_PCP_SFT 13 +}; + +/* Query QP command (24 bytes) */ +struct cmdq_query_qp { + u8 opcode; + #define CMDQ_QUERY_QP_OPCODE_QUERY_QP 0x4UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 qp_cid; + __le32 unused_0; +}; + +/* Create SRQ command (48 bytes) */ +struct cmdq_create_srq { + u8 opcode; + #define CMDQ_CREATE_SRQ_OPCODE_CREATE_SRQ 0x5UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le64 srq_handle; + __le16 pg_size_lvl; + #define CMDQ_CREATE_SRQ_LVL_MASK 0x3UL + #define CMDQ_CREATE_SRQ_LVL_SFT 0 + #define CMDQ_CREATE_SRQ_LVL_LVL_0 0x0UL + #define CMDQ_CREATE_SRQ_LVL_LVL_1 0x1UL + #define CMDQ_CREATE_SRQ_LVL_LVL_2 0x2UL + #define CMDQ_CREATE_SRQ_PG_SIZE_MASK 0x1cUL + #define CMDQ_CREATE_SRQ_PG_SIZE_SFT 2 + #define CMDQ_CREATE_SRQ_PG_SIZE_PG_4K (0x0UL << 2) + #define CMDQ_CREATE_SRQ_PG_SIZE_PG_8K (0x1UL << 2) + #define CMDQ_CREATE_SRQ_PG_SIZE_PG_64K (0x2UL << 2) + #define CMDQ_CREATE_SRQ_PG_SIZE_PG_2M (0x3UL << 2) + #define CMDQ_CREATE_SRQ_PG_SIZE_PG_8M (0x4UL << 2) + #define CMDQ_CREATE_SRQ_PG_SIZE_PG_1G (0x5UL << 2) + __le16 eventq_id; + #define CMDQ_CREATE_SRQ_EVENTQ_ID_MASK 0xfffUL + #define CMDQ_CREATE_SRQ_EVENTQ_ID_SFT 0 + __le16 srq_size; + __le16 srq_fwo; + __le32 dpi; + __le32 pd_id; + __le64 pbl; +}; + +/* Destroy SRQ command (24 bytes) */ +struct cmdq_destroy_srq { + u8 opcode; + #define CMDQ_DESTROY_SRQ_OPCODE_DESTROY_SRQ 0x6UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 srq_cid; + __le32 unused_0; +}; + +/* Query SRQ command (24 bytes) */ +struct cmdq_query_srq { + u8 opcode; + #define CMDQ_QUERY_SRQ_OPCODE_QUERY_SRQ 0x8UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 srq_cid; + __le32 unused_0; +}; + +/* Create CQ command (48 bytes) */ +struct cmdq_create_cq { + u8 opcode; + #define CMDQ_CREATE_CQ_OPCODE_CREATE_CQ 0x9UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le64 cq_handle; + __le32 pg_size_lvl; + #define CMDQ_CREATE_CQ_LVL_MASK 0x3UL + #define CMDQ_CREATE_CQ_LVL_SFT 0 + #define CMDQ_CREATE_CQ_LVL_LVL_0 0x0UL + #define CMDQ_CREATE_CQ_LVL_LVL_1 0x1UL + #define CMDQ_CREATE_CQ_LVL_LVL_2 0x2UL + #define CMDQ_CREATE_CQ_PG_SIZE_MASK 0x1cUL + #define CMDQ_CREATE_CQ_PG_SIZE_SFT 2 + #define CMDQ_CREATE_CQ_PG_SIZE_PG_4K (0x0UL << 2) + #define CMDQ_CREATE_CQ_PG_SIZE_PG_8K (0x1UL << 2) + #define CMDQ_CREATE_CQ_PG_SIZE_PG_64K (0x2UL << 2) + #define CMDQ_CREATE_CQ_PG_SIZE_PG_2M (0x3UL << 2) + #define CMDQ_CREATE_CQ_PG_SIZE_PG_8M (0x4UL << 2) + #define CMDQ_CREATE_CQ_PG_SIZE_PG_1G (0x5UL << 2) + __le32 cq_fco_cnq_id; + #define CMDQ_CREATE_CQ_CNQ_ID_MASK 0xfffUL + #define CMDQ_CREATE_CQ_CNQ_ID_SFT 0 + #define CMDQ_CREATE_CQ_CQ_FCO_MASK 0xfffff000UL + #define CMDQ_CREATE_CQ_CQ_FCO_SFT 12 + __le32 dpi; + __le32 cq_size; + __le64 pbl; +}; + +/* Destroy CQ command (24 bytes) */ +struct cmdq_destroy_cq { + u8 opcode; + #define CMDQ_DESTROY_CQ_OPCODE_DESTROY_CQ 0xaUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 cq_cid; + __le32 unused_0; +}; + +/* Resize CQ command (40 bytes) */ +struct cmdq_resize_cq { + u8 opcode; + #define CMDQ_RESIZE_CQ_OPCODE_RESIZE_CQ 0xcUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 cq_cid; + __le32 new_cq_size_pg_size_lvl; + #define CMDQ_RESIZE_CQ_LVL_MASK 0x3UL + #define CMDQ_RESIZE_CQ_LVL_SFT 0 + #define CMDQ_RESIZE_CQ_LVL_LVL_0 0x0UL + #define CMDQ_RESIZE_CQ_LVL_LVL_1 0x1UL + #define CMDQ_RESIZE_CQ_LVL_LVL_2 0x2UL + #define CMDQ_RESIZE_CQ_PG_SIZE_MASK 0x1cUL + #define CMDQ_RESIZE_CQ_PG_SIZE_SFT 2 + #define CMDQ_RESIZE_CQ_PG_SIZE_PG_4K (0x0UL << 2) + #define CMDQ_RESIZE_CQ_PG_SIZE_PG_8K (0x1UL << 2) + #define CMDQ_RESIZE_CQ_PG_SIZE_PG_64K (0x2UL << 2) + #define CMDQ_RESIZE_CQ_PG_SIZE_PG_2M (0x3UL << 2) + #define CMDQ_RESIZE_CQ_PG_SIZE_PG_8M (0x4UL << 2) + #define CMDQ_RESIZE_CQ_PG_SIZE_PG_1G (0x5UL << 2) + #define CMDQ_RESIZE_CQ_NEW_CQ_SIZE_MASK 0x1fffe0UL + #define CMDQ_RESIZE_CQ_NEW_CQ_SIZE_SFT 5 + __le64 new_pbl; + __le32 new_cq_fco; + __le32 unused_2; +}; + +/* Allocate MRW command (32 bytes) */ +struct cmdq_allocate_mrw { + u8 opcode; + #define CMDQ_ALLOCATE_MRW_OPCODE_ALLOCATE_MRW 0xdUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le64 mrw_handle; + u8 mrw_flags; + #define CMDQ_ALLOCATE_MRW_MRW_FLAGS_MASK 0xfUL + #define CMDQ_ALLOCATE_MRW_MRW_FLAGS_SFT 0 + #define CMDQ_ALLOCATE_MRW_MRW_FLAGS_MR 0x0UL + #define CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR 0x1UL + #define CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE1 0x2UL + #define CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A 0x3UL + #define CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B 0x4UL + u8 access; + #define CMDQ_ALLOCATE_MRW_ACCESS_RESERVED_MASK 0x1fUL + #define CMDQ_ALLOCATE_MRW_ACCESS_RESERVED_SFT 0 + #define CMDQ_ALLOCATE_MRW_ACCESS_CONSUMER_OWNED_KEY 0x20UL + __le16 unused_1; + __le32 pd_id; +}; + +/* De-allocate key command (24 bytes) */ +struct cmdq_deallocate_key { + u8 opcode; + #define CMDQ_DEALLOCATE_KEY_OPCODE_DEALLOCATE_KEY 0xeUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + u8 mrw_flags; + #define CMDQ_DEALLOCATE_KEY_MRW_FLAGS_MASK 0xfUL + #define CMDQ_DEALLOCATE_KEY_MRW_FLAGS_SFT 0 + #define CMDQ_DEALLOCATE_KEY_MRW_FLAGS_MR 0x0UL + #define CMDQ_DEALLOCATE_KEY_MRW_FLAGS_PMR 0x1UL + #define CMDQ_DEALLOCATE_KEY_MRW_FLAGS_MW_TYPE1 0x2UL + #define CMDQ_DEALLOCATE_KEY_MRW_FLAGS_MW_TYPE2A 0x3UL + #define CMDQ_DEALLOCATE_KEY_MRW_FLAGS_MW_TYPE2B 0x4UL + u8 unused_1[3]; + __le32 key; +}; + +/* Register MR command (48 bytes) */ +struct cmdq_register_mr { + u8 opcode; + #define CMDQ_REGISTER_MR_OPCODE_REGISTER_MR 0xfUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + u8 log2_pg_size_lvl; + #define CMDQ_REGISTER_MR_LVL_MASK 0x3UL + #define CMDQ_REGISTER_MR_LVL_SFT 0 + #define CMDQ_REGISTER_MR_LVL_LVL_0 0x0UL + #define CMDQ_REGISTER_MR_LVL_LVL_1 0x1UL + #define CMDQ_REGISTER_MR_LVL_LVL_2 0x2UL + #define CMDQ_REGISTER_MR_LOG2_PG_SIZE_MASK 0x7cUL + #define CMDQ_REGISTER_MR_LOG2_PG_SIZE_SFT 2 + u8 access; + #define CMDQ_REGISTER_MR_ACCESS_LOCAL_WRITE 0x1UL + #define CMDQ_REGISTER_MR_ACCESS_REMOTE_READ 0x2UL + #define CMDQ_REGISTER_MR_ACCESS_REMOTE_WRITE 0x4UL + #define CMDQ_REGISTER_MR_ACCESS_REMOTE_ATOMIC 0x8UL + #define CMDQ_REGISTER_MR_ACCESS_MW_BIND 0x10UL + #define CMDQ_REGISTER_MR_ACCESS_ZERO_BASED 0x20UL + __le16 unused_1; + __le32 key; + __le64 pbl; + __le64 va; + __le64 mr_size; +}; + +/* Deregister MR command (24 bytes) */ +struct cmdq_deregister_mr { + u8 opcode; + #define CMDQ_DEREGISTER_MR_OPCODE_DEREGISTER_MR 0x10UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 lkey; + __le32 unused_0; +}; + +/* Add GID command (48 bytes) */ +struct cmdq_add_gid { + u8 opcode; + #define CMDQ_ADD_GID_OPCODE_ADD_GID 0x11UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __be32 gid[4]; + __be16 src_mac[3]; + __le16 vlan; + #define CMDQ_ADD_GID_VLAN_VLAN_ID_MASK 0xfffUL + #define CMDQ_ADD_GID_VLAN_VLAN_ID_SFT 0 + #define CMDQ_ADD_GID_VLAN_TPID_MASK 0x7000UL + #define CMDQ_ADD_GID_VLAN_TPID_SFT 12 + #define CMDQ_ADD_GID_VLAN_TPID_TPID_88A8 (0x0UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_TPID_8100 (0x1UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_TPID_9100 (0x2UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_TPID_9200 (0x3UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_TPID_9300 (0x4UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_TPID_CFG1 (0x5UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_TPID_CFG2 (0x6UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_TPID_CFG3 (0x7UL << 12) + #define CMDQ_ADD_GID_VLAN_TPID_LAST CMDQ_ADD_GID_VLAN_TPID_TPID_CFG3 + #define CMDQ_ADD_GID_VLAN_VLAN_EN 0x8000UL + __le16 ipid; + __le16 stats_ctx; + #define CMDQ_ADD_GID_STATS_CTX_STATS_CTX_ID_MASK 0x7fffUL + #define CMDQ_ADD_GID_STATS_CTX_STATS_CTX_ID_SFT 0 + #define CMDQ_ADD_GID_STATS_CTX_STATS_CTX_VALID 0x8000UL + __le32 unused_0; +}; + +/* Delete GID command (24 bytes) */ +struct cmdq_delete_gid { + u8 opcode; + #define CMDQ_DELETE_GID_OPCODE_DELETE_GID 0x12UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le16 gid_index; + __le16 unused_0; + __le32 unused_1; +}; + +/* Modify GID command (48 bytes) */ +struct cmdq_modify_gid { + u8 opcode; + #define CMDQ_MODIFY_GID_OPCODE_MODIFY_GID 0x17UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 gid[4]; + __le16 src_mac[3]; + __le16 vlan; + #define CMDQ_MODIFY_GID_VLAN_VLAN_ID_MASK 0xfffUL + #define CMDQ_MODIFY_GID_VLAN_VLAN_ID_SFT 0 + #define CMDQ_MODIFY_GID_VLAN_TPID_MASK 0x7000UL + #define CMDQ_MODIFY_GID_VLAN_TPID_SFT 12 + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_88A8 (0x0UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_8100 (0x1UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_9100 (0x2UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_9200 (0x3UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_9300 (0x4UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_CFG1 (0x5UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_CFG2 (0x6UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_TPID_CFG3 (0x7UL << 12) + #define CMDQ_MODIFY_GID_VLAN_TPID_LAST \ + CMDQ_MODIFY_GID_VLAN_TPID_TPID_CFG3 + #define CMDQ_MODIFY_GID_VLAN_VLAN_EN 0x8000UL + __le16 ipid; + __le16 gid_index; + __le16 stats_ctx; + #define CMDQ_MODIFY_GID_STATS_CTX_STATS_CTX_ID_MASK 0x7fffUL + #define CMDQ_MODIFY_GID_STATS_CTX_STATS_CTX_ID_SFT 0 + #define CMDQ_MODIFY_GID_STATS_CTX_STATS_CTX_VALID 0x8000UL + __le16 unused_0; +}; + +/* Query GID command (24 bytes) */ +struct cmdq_query_gid { + u8 opcode; + #define CMDQ_QUERY_GID_OPCODE_QUERY_GID 0x18UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le16 gid_index; + __le16 unused_0; + __le32 unused_1; +}; + +/* Create QP1 command (80 bytes) */ +struct cmdq_create_qp1 { + u8 opcode; + #define CMDQ_CREATE_QP1_OPCODE_CREATE_QP1 0x13UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le64 qp_handle; + __le32 qp_flags; + #define CMDQ_CREATE_QP1_QP_FLAGS_SRQ_USED 0x1UL + #define CMDQ_CREATE_QP1_QP_FLAGS_FORCE_COMPLETION 0x2UL + #define CMDQ_CREATE_QP1_QP_FLAGS_RESERVED_LKEY_ENABLE 0x4UL + u8 type; + #define CMDQ_CREATE_QP1_TYPE_GSI 0x1UL + u8 sq_pg_size_sq_lvl; + #define CMDQ_CREATE_QP1_SQ_LVL_MASK 0xfUL + #define CMDQ_CREATE_QP1_SQ_LVL_SFT 0 + #define CMDQ_CREATE_QP1_SQ_LVL_LVL_0 0x0UL + #define CMDQ_CREATE_QP1_SQ_LVL_LVL_1 0x1UL + #define CMDQ_CREATE_QP1_SQ_LVL_LVL_2 0x2UL + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_MASK 0xf0UL + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_SFT 4 + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_CREATE_QP1_SQ_PG_SIZE_PG_1G (0x5UL << 4) + u8 rq_pg_size_rq_lvl; + #define CMDQ_CREATE_QP1_RQ_LVL_MASK 0xfUL + #define CMDQ_CREATE_QP1_RQ_LVL_SFT 0 + #define CMDQ_CREATE_QP1_RQ_LVL_LVL_0 0x0UL + #define CMDQ_CREATE_QP1_RQ_LVL_LVL_1 0x1UL + #define CMDQ_CREATE_QP1_RQ_LVL_LVL_2 0x2UL + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_MASK 0xf0UL + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_SFT 4 + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_CREATE_QP1_RQ_PG_SIZE_PG_1G (0x5UL << 4) + u8 unused_0; + __le32 dpi; + __le32 sq_size; + __le32 rq_size; + __le16 sq_fwo_sq_sge; + #define CMDQ_CREATE_QP1_SQ_SGE_MASK 0xfUL + #define CMDQ_CREATE_QP1_SQ_SGE_SFT 0 + #define CMDQ_CREATE_QP1_SQ_FWO_MASK 0xfff0UL + #define CMDQ_CREATE_QP1_SQ_FWO_SFT 4 + __le16 rq_fwo_rq_sge; + #define CMDQ_CREATE_QP1_RQ_SGE_MASK 0xfUL + #define CMDQ_CREATE_QP1_RQ_SGE_SFT 0 + #define CMDQ_CREATE_QP1_RQ_FWO_MASK 0xfff0UL + #define CMDQ_CREATE_QP1_RQ_FWO_SFT 4 + __le32 scq_cid; + __le32 rcq_cid; + __le32 srq_cid; + __le32 pd_id; + __le64 sq_pbl; + __le64 rq_pbl; +}; + +/* Destroy QP1 command (24 bytes) */ +struct cmdq_destroy_qp1 { + u8 opcode; + #define CMDQ_DESTROY_QP1_OPCODE_DESTROY_QP1 0x14UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 qp1_cid; + __le32 unused_0; +}; + +/* Create AH command (64 bytes) */ +struct cmdq_create_ah { + u8 opcode; + #define CMDQ_CREATE_AH_OPCODE_CREATE_AH 0x15UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le64 ah_handle; + __le32 dgid[4]; + u8 type; + #define CMDQ_CREATE_AH_TYPE_V1 0x0UL + #define CMDQ_CREATE_AH_TYPE_V2IPV4 0x2UL + #define CMDQ_CREATE_AH_TYPE_V2IPV6 0x3UL + u8 hop_limit; + __le16 sgid_index; + __le32 dest_vlan_id_flow_label; + #define CMDQ_CREATE_AH_FLOW_LABEL_MASK 0xfffffUL + #define CMDQ_CREATE_AH_FLOW_LABEL_SFT 0 + #define CMDQ_CREATE_AH_DEST_VLAN_ID_MASK 0xfff00000UL + #define CMDQ_CREATE_AH_DEST_VLAN_ID_SFT 20 + __le32 pd_id; + __le32 unused_0; + __le16 dest_mac[3]; + u8 traffic_class; + u8 unused_1; +}; + +/* Destroy AH command (24 bytes) */ +struct cmdq_destroy_ah { + u8 opcode; + #define CMDQ_DESTROY_AH_OPCODE_DESTROY_AH 0x16UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 ah_cid; + __le32 unused_0; +}; + +/* Initialize Firmware command (112 bytes) */ +struct cmdq_initialize_fw { + u8 opcode; + #define CMDQ_INITIALIZE_FW_OPCODE_INITIALIZE_FW 0x80UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + u8 qpc_pg_size_qpc_lvl; + #define CMDQ_INITIALIZE_FW_QPC_LVL_MASK 0xfUL + #define CMDQ_INITIALIZE_FW_QPC_LVL_SFT 0 + #define CMDQ_INITIALIZE_FW_QPC_LVL_LVL_0 0x0UL + #define CMDQ_INITIALIZE_FW_QPC_LVL_LVL_1 0x1UL + #define CMDQ_INITIALIZE_FW_QPC_LVL_LVL_2 0x2UL + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_MASK 0xf0UL + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_SFT 4 + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_INITIALIZE_FW_QPC_PG_SIZE_PG_1G (0x5UL << 4) + u8 mrw_pg_size_mrw_lvl; + #define CMDQ_INITIALIZE_FW_MRW_LVL_MASK 0xfUL + #define CMDQ_INITIALIZE_FW_MRW_LVL_SFT 0 + #define CMDQ_INITIALIZE_FW_MRW_LVL_LVL_0 0x0UL + #define CMDQ_INITIALIZE_FW_MRW_LVL_LVL_1 0x1UL + #define CMDQ_INITIALIZE_FW_MRW_LVL_LVL_2 0x2UL + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_MASK 0xf0UL + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_SFT 4 + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_INITIALIZE_FW_MRW_PG_SIZE_PG_1G (0x5UL << 4) + u8 srq_pg_size_srq_lvl; + #define CMDQ_INITIALIZE_FW_SRQ_LVL_MASK 0xfUL + #define CMDQ_INITIALIZE_FW_SRQ_LVL_SFT 0 + #define CMDQ_INITIALIZE_FW_SRQ_LVL_LVL_0 0x0UL + #define CMDQ_INITIALIZE_FW_SRQ_LVL_LVL_1 0x1UL + #define CMDQ_INITIALIZE_FW_SRQ_LVL_LVL_2 0x2UL + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_MASK 0xf0UL + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_SFT 4 + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_INITIALIZE_FW_SRQ_PG_SIZE_PG_1G (0x5UL << 4) + u8 cq_pg_size_cq_lvl; + #define CMDQ_INITIALIZE_FW_CQ_LVL_MASK 0xfUL + #define CMDQ_INITIALIZE_FW_CQ_LVL_SFT 0 + #define CMDQ_INITIALIZE_FW_CQ_LVL_LVL_0 0x0UL + #define CMDQ_INITIALIZE_FW_CQ_LVL_LVL_1 0x1UL + #define CMDQ_INITIALIZE_FW_CQ_LVL_LVL_2 0x2UL + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_MASK 0xf0UL + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_SFT 4 + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_INITIALIZE_FW_CQ_PG_SIZE_PG_1G (0x5UL << 4) + u8 tqm_pg_size_tqm_lvl; + #define CMDQ_INITIALIZE_FW_TQM_LVL_MASK 0xfUL + #define CMDQ_INITIALIZE_FW_TQM_LVL_SFT 0 + #define CMDQ_INITIALIZE_FW_TQM_LVL_LVL_0 0x0UL + #define CMDQ_INITIALIZE_FW_TQM_LVL_LVL_1 0x1UL + #define CMDQ_INITIALIZE_FW_TQM_LVL_LVL_2 0x2UL + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_MASK 0xf0UL + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_SFT 4 + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_INITIALIZE_FW_TQM_PG_SIZE_PG_1G (0x5UL << 4) + u8 tim_pg_size_tim_lvl; + #define CMDQ_INITIALIZE_FW_TIM_LVL_MASK 0xfUL + #define CMDQ_INITIALIZE_FW_TIM_LVL_SFT 0 + #define CMDQ_INITIALIZE_FW_TIM_LVL_LVL_0 0x0UL + #define CMDQ_INITIALIZE_FW_TIM_LVL_LVL_1 0x1UL + #define CMDQ_INITIALIZE_FW_TIM_LVL_LVL_2 0x2UL + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_MASK 0xf0UL + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_SFT 4 + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_PG_4K (0x0UL << 4) + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_PG_8K (0x1UL << 4) + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_PG_64K (0x2UL << 4) + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_PG_2M (0x3UL << 4) + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_PG_8M (0x4UL << 4) + #define CMDQ_INITIALIZE_FW_TIM_PG_SIZE_PG_1G (0x5UL << 4) + __le16 reserved16; + __le64 qpc_page_dir; + __le64 mrw_page_dir; + __le64 srq_page_dir; + __le64 cq_page_dir; + __le64 tqm_page_dir; + __le64 tim_page_dir; + __le32 number_of_qp; + __le32 number_of_mrw; + __le32 number_of_srq; + __le32 number_of_cq; + __le32 max_qp_per_vf; + __le32 max_mrw_per_vf; + __le32 max_srq_per_vf; + __le32 max_cq_per_vf; + __le32 max_gid_per_vf; + __le32 stat_ctx_id; +}; + +/* De-initialize Firmware command (16 bytes) */ +struct cmdq_deinitialize_fw { + u8 opcode; + #define CMDQ_DEINITIALIZE_FW_OPCODE_DEINITIALIZE_FW 0x81UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; +}; + +/* Stop function command (16 bytes) */ +struct cmdq_stop_func { + u8 opcode; + #define CMDQ_STOP_FUNC_OPCODE_STOP_FUNC 0x82UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; +}; + +/* Query function command (16 bytes) */ +struct cmdq_query_func { + u8 opcode; + #define CMDQ_QUERY_FUNC_OPCODE_QUERY_FUNC 0x83UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; +}; + +/* Set function resources command (16 bytes) */ +struct cmdq_set_func_resources { + u8 opcode; + #define CMDQ_SET_FUNC_RESOURCES_OPCODE_SET_FUNC_RESOURCES 0x84UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; +}; + +/* Read hardware resource context command (24 bytes) */ +struct cmdq_read_context { + u8 opcode; + #define CMDQ_READ_CONTEXT_OPCODE_READ_CONTEXT 0x85UL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le32 type_xid; + #define CMDQ_READ_CONTEXT_XID_MASK 0xffffffUL + #define CMDQ_READ_CONTEXT_XID_SFT 0 + #define CMDQ_READ_CONTEXT_TYPE_MASK 0xff000000UL + #define CMDQ_READ_CONTEXT_TYPE_SFT 24 + #define CMDQ_READ_CONTEXT_TYPE_QPC (0x0UL << 24) + #define CMDQ_READ_CONTEXT_TYPE_CQ (0x1UL << 24) + #define CMDQ_READ_CONTEXT_TYPE_MRW (0x2UL << 24) + #define CMDQ_READ_CONTEXT_TYPE_SRQ (0x3UL << 24) + __le32 unused_0; +}; + +/* Map TC to COS. Can only be issued from a PF (24 bytes) */ +struct cmdq_map_tc_to_cos { + u8 opcode; + #define CMDQ_MAP_TC_TO_COS_OPCODE_MAP_TC_TO_COS 0x8aUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; + __le16 cos0; + #define CMDQ_MAP_TC_TO_COS_COS0_NO_CHANGE 0xffffUL + __le16 cos1; + #define CMDQ_MAP_TC_TO_COS_COS1_DISABLE 0x8000UL + #define CMDQ_MAP_TC_TO_COS_COS1_NO_CHANGE 0xffffUL + __le32 unused_0; +}; + +/* Query version command (16 bytes) */ +struct cmdq_query_version { + u8 opcode; + #define CMDQ_QUERY_VERSION_OPCODE_QUERY_VERSION 0x8bUL + u8 cmd_size; + __le16 flags; + __le16 cookie; + u8 resp_size; + u8 reserved8; + __le64 resp_addr; +}; + +/* Command-Response Event Queue (CREQ) Structures */ +/* Base CREQ Record (16 bytes) */ +struct creq_base { + u8 type; + #define CREQ_BASE_TYPE_MASK 0x3fUL + #define CREQ_BASE_TYPE_SFT 0 + #define CREQ_BASE_TYPE_QP_EVENT 0x38UL + #define CREQ_BASE_TYPE_FUNC_EVENT 0x3aUL + #define CREQ_BASE_RESERVED2_MASK 0xc0UL + #define CREQ_BASE_RESERVED2_SFT 6 + u8 reserved56[7]; + u8 v; + #define CREQ_BASE_V 0x1UL + #define CREQ_BASE_RESERVED7_MASK 0xfeUL + #define CREQ_BASE_RESERVED7_SFT 1 + u8 event; + __le16 reserved48[3]; +}; + +/* RoCE Function Async Event Notification (16 bytes) */ +struct creq_func_event { + u8 type; + #define CREQ_FUNC_EVENT_TYPE_MASK 0x3fUL + #define CREQ_FUNC_EVENT_TYPE_SFT 0 + #define CREQ_FUNC_EVENT_TYPE_FUNC_EVENT 0x3aUL + #define CREQ_FUNC_EVENT_RESERVED2_MASK 0xc0UL + #define CREQ_FUNC_EVENT_RESERVED2_SFT 6 + u8 reserved56[7]; + u8 v; + #define CREQ_FUNC_EVENT_V 0x1UL + #define CREQ_FUNC_EVENT_RESERVED7_MASK 0xfeUL + #define CREQ_FUNC_EVENT_RESERVED7_SFT 1 + u8 event; + #define CREQ_FUNC_EVENT_EVENT_TX_WQE_ERROR 0x1UL + #define CREQ_FUNC_EVENT_EVENT_TX_DATA_ERROR 0x2UL + #define CREQ_FUNC_EVENT_EVENT_RX_WQE_ERROR 0x3UL + #define CREQ_FUNC_EVENT_EVENT_RX_DATA_ERROR 0x4UL + #define CREQ_FUNC_EVENT_EVENT_CQ_ERROR 0x5UL + #define CREQ_FUNC_EVENT_EVENT_TQM_ERROR 0x6UL + #define CREQ_FUNC_EVENT_EVENT_CFCQ_ERROR 0x7UL + #define CREQ_FUNC_EVENT_EVENT_CFCS_ERROR 0x8UL + #define CREQ_FUNC_EVENT_EVENT_CFCC_ERROR 0x9UL + #define CREQ_FUNC_EVENT_EVENT_CFCM_ERROR 0xaUL + #define CREQ_FUNC_EVENT_EVENT_TIM_ERROR 0xbUL + #define CREQ_FUNC_EVENT_EVENT_VF_COMM_REQUEST 0x80UL + #define CREQ_FUNC_EVENT_EVENT_RESOURCE_EXHAUSTED 0x81UL + __le16 reserved48[3]; +}; + +/* RoCE Slowpath Command Completion (16 bytes) */ +struct creq_qp_event { + u8 type; + #define CREQ_QP_EVENT_TYPE_MASK 0x3fUL + #define CREQ_QP_EVENT_TYPE_SFT 0 + #define CREQ_QP_EVENT_TYPE_QP_EVENT 0x38UL + #define CREQ_QP_EVENT_RESERVED2_MASK 0xc0UL + #define CREQ_QP_EVENT_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 reserved32; + u8 v; + #define CREQ_QP_EVENT_V 0x1UL + #define CREQ_QP_EVENT_RESERVED7_MASK 0xfeUL + #define CREQ_QP_EVENT_RESERVED7_SFT 1 + u8 event; + #define CREQ_QP_EVENT_EVENT_CREATE_QP 0x1UL + #define CREQ_QP_EVENT_EVENT_DESTROY_QP 0x2UL + #define CREQ_QP_EVENT_EVENT_MODIFY_QP 0x3UL + #define CREQ_QP_EVENT_EVENT_QUERY_QP 0x4UL + #define CREQ_QP_EVENT_EVENT_CREATE_SRQ 0x5UL + #define CREQ_QP_EVENT_EVENT_DESTROY_SRQ 0x6UL + #define CREQ_QP_EVENT_EVENT_QUERY_SRQ 0x8UL + #define CREQ_QP_EVENT_EVENT_CREATE_CQ 0x9UL + #define CREQ_QP_EVENT_EVENT_DESTROY_CQ 0xaUL + #define CREQ_QP_EVENT_EVENT_RESIZE_CQ 0xcUL + #define CREQ_QP_EVENT_EVENT_ALLOCATE_MRW 0xdUL + #define CREQ_QP_EVENT_EVENT_DEALLOCATE_KEY 0xeUL + #define CREQ_QP_EVENT_EVENT_REGISTER_MR 0xfUL + #define CREQ_QP_EVENT_EVENT_DEREGISTER_MR 0x10UL + #define CREQ_QP_EVENT_EVENT_ADD_GID 0x11UL + #define CREQ_QP_EVENT_EVENT_DELETE_GID 0x12UL + #define CREQ_QP_EVENT_EVENT_MODIFY_GID 0x17UL + #define CREQ_QP_EVENT_EVENT_QUERY_GID 0x18UL + #define CREQ_QP_EVENT_EVENT_CREATE_QP1 0x13UL + #define CREQ_QP_EVENT_EVENT_DESTROY_QP1 0x14UL + #define CREQ_QP_EVENT_EVENT_CREATE_AH 0x15UL + #define CREQ_QP_EVENT_EVENT_DESTROY_AH 0x16UL + #define CREQ_QP_EVENT_EVENT_INITIALIZE_FW 0x80UL + #define CREQ_QP_EVENT_EVENT_DEINITIALIZE_FW 0x81UL + #define CREQ_QP_EVENT_EVENT_STOP_FUNC 0x82UL + #define CREQ_QP_EVENT_EVENT_QUERY_FUNC 0x83UL + #define CREQ_QP_EVENT_EVENT_SET_FUNC_RESOURCES 0x84UL + #define CREQ_QP_EVENT_EVENT_MAP_TC_TO_COS 0x8aUL + #define CREQ_QP_EVENT_EVENT_QUERY_VERSION 0x8bUL + #define CREQ_QP_EVENT_EVENT_MODIFY_CC 0x8cUL + #define CREQ_QP_EVENT_EVENT_QUERY_CC 0x8dUL + #define CREQ_QP_EVENT_EVENT_QP_ERROR_NOTIFICATION 0xc0UL + __le16 reserved48[3]; +}; + +/* Create QP command response (16 bytes) */ +struct creq_create_qp_resp { + u8 type; + #define CREQ_CREATE_QP_RESP_TYPE_MASK 0x3fUL + #define CREQ_CREATE_QP_RESP_TYPE_SFT 0 + #define CREQ_CREATE_QP_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_CREATE_QP_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_CREATE_QP_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_CREATE_QP_RESP_V 0x1UL + #define CREQ_CREATE_QP_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_CREATE_QP_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_CREATE_QP_RESP_EVENT_CREATE_QP 0x1UL + __le16 reserved48[3]; +}; + +/* Destroy QP command response (16 bytes) */ +struct creq_destroy_qp_resp { + u8 type; + #define CREQ_DESTROY_QP_RESP_TYPE_MASK 0x3fUL + #define CREQ_DESTROY_QP_RESP_TYPE_SFT 0 + #define CREQ_DESTROY_QP_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DESTROY_QP_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DESTROY_QP_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DESTROY_QP_RESP_V 0x1UL + #define CREQ_DESTROY_QP_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DESTROY_QP_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DESTROY_QP_RESP_EVENT_DESTROY_QP 0x2UL + __le16 reserved48[3]; +}; + +/* Modify QP command response (16 bytes) */ +struct creq_modify_qp_resp { + u8 type; + #define CREQ_MODIFY_QP_RESP_TYPE_MASK 0x3fUL + #define CREQ_MODIFY_QP_RESP_TYPE_SFT 0 + #define CREQ_MODIFY_QP_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_MODIFY_QP_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_MODIFY_QP_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_MODIFY_QP_RESP_V 0x1UL + #define CREQ_MODIFY_QP_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_MODIFY_QP_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_MODIFY_QP_RESP_EVENT_MODIFY_QP 0x3UL + __le16 reserved48[3]; +}; + +/* Query QP command response (16 bytes) */ +struct creq_query_qp_resp { + u8 type; + #define CREQ_QUERY_QP_RESP_TYPE_MASK 0x3fUL + #define CREQ_QUERY_QP_RESP_TYPE_SFT 0 + #define CREQ_QUERY_QP_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_QUERY_QP_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_QUERY_QP_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 size; + u8 v; + #define CREQ_QUERY_QP_RESP_V 0x1UL + #define CREQ_QUERY_QP_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_QUERY_QP_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_QUERY_QP_RESP_EVENT_QUERY_QP 0x4UL + __le16 reserved48[3]; +}; + +/* Query QP command response side buffer structure (104 bytes) */ +struct creq_query_qp_resp_sb { + u8 opcode; + #define CREQ_QUERY_QP_RESP_SB_OPCODE_QUERY_QP 0x4UL + u8 status; + __le16 cookie; + __le16 flags; + u8 resp_size; + u8 reserved8; + __le32 xid; + u8 en_sqd_async_notify_state; + #define CREQ_QUERY_QP_RESP_SB_STATE_MASK 0xfUL + #define CREQ_QUERY_QP_RESP_SB_STATE_SFT 0 + #define CREQ_QUERY_QP_RESP_SB_STATE_RESET 0x0UL + #define CREQ_QUERY_QP_RESP_SB_STATE_INIT 0x1UL + #define CREQ_QUERY_QP_RESP_SB_STATE_RTR 0x2UL + #define CREQ_QUERY_QP_RESP_SB_STATE_RTS 0x3UL + #define CREQ_QUERY_QP_RESP_SB_STATE_SQD 0x4UL + #define CREQ_QUERY_QP_RESP_SB_STATE_SQE 0x5UL + #define CREQ_QUERY_QP_RESP_SB_STATE_ERR 0x6UL + #define CREQ_QUERY_QP_RESP_SB_EN_SQD_ASYNC_NOTIFY 0x10UL + u8 access; + #define CREQ_QUERY_QP_RESP_SB_ACCESS_LOCAL_WRITE 0x1UL + #define CREQ_QUERY_QP_RESP_SB_ACCESS_REMOTE_WRITE 0x2UL + #define CREQ_QUERY_QP_RESP_SB_ACCESS_REMOTE_READ 0x4UL + #define CREQ_QUERY_QP_RESP_SB_ACCESS_REMOTE_ATOMIC 0x8UL + __le16 pkey; + __le32 qkey; + __le32 reserved32; + __le32 dgid[4]; + __le32 flow_label; + __le16 sgid_index; + u8 hop_limit; + u8 traffic_class; + __le16 dest_mac[3]; + __le16 path_mtu_dest_vlan_id; + #define CREQ_QUERY_QP_RESP_SB_DEST_VLAN_ID_MASK 0xfffUL + #define CREQ_QUERY_QP_RESP_SB_DEST_VLAN_ID_SFT 0 + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_MASK 0xf000UL + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_SFT 12 + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_MTU_256 (0x0UL << 12) + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_MTU_512 (0x1UL << 12) + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_MTU_1024 (0x2UL << 12) + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_MTU_2048 (0x3UL << 12) + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_MTU_4096 (0x4UL << 12) + #define CREQ_QUERY_QP_RESP_SB_PATH_MTU_MTU_8192 (0x5UL << 12) + u8 timeout; + u8 retry_cnt; + u8 rnr_retry; + u8 min_rnr_timer; + __le32 rq_psn; + __le32 sq_psn; + u8 max_rd_atomic; + u8 max_dest_rd_atomic; + u8 tos_dscp_tos_ecn; + #define CREQ_QUERY_QP_RESP_SB_TOS_ECN_MASK 0x3UL + #define CREQ_QUERY_QP_RESP_SB_TOS_ECN_SFT 0 + #define CREQ_QUERY_QP_RESP_SB_TOS_DSCP_MASK 0xfcUL + #define CREQ_QUERY_QP_RESP_SB_TOS_DSCP_SFT 2 + u8 enable_cc; + #define CREQ_QUERY_QP_RESP_SB_ENABLE_CC 0x1UL + #define CREQ_QUERY_QP_RESP_SB_RESERVED7_MASK 0xfeUL + #define CREQ_QUERY_QP_RESP_SB_RESERVED7_SFT 1 + __le32 sq_size; + __le32 rq_size; + __le16 sq_sge; + __le16 rq_sge; + __le32 max_inline_data; + __le32 dest_qp_id; + __le32 unused_1; + __le16 src_mac[3]; + __le16 vlan_pcp_vlan_dei_vlan_id; + #define CREQ_QUERY_QP_RESP_SB_VLAN_ID_MASK 0xfffUL + #define CREQ_QUERY_QP_RESP_SB_VLAN_ID_SFT 0 + #define CREQ_QUERY_QP_RESP_SB_VLAN_DEI 0x1000UL + #define CREQ_QUERY_QP_RESP_SB_VLAN_PCP_MASK 0xe000UL + #define CREQ_QUERY_QP_RESP_SB_VLAN_PCP_SFT 13 +}; + +/* Create SRQ command response (16 bytes) */ +struct creq_create_srq_resp { + u8 type; + #define CREQ_CREATE_SRQ_RESP_TYPE_MASK 0x3fUL + #define CREQ_CREATE_SRQ_RESP_TYPE_SFT 0 + #define CREQ_CREATE_SRQ_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_CREATE_SRQ_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_CREATE_SRQ_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_CREATE_SRQ_RESP_V 0x1UL + #define CREQ_CREATE_SRQ_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_CREATE_SRQ_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_CREATE_SRQ_RESP_EVENT_CREATE_SRQ 0x5UL + __le16 reserved48[3]; +}; + +/* Destroy SRQ command response (16 bytes) */ +struct creq_destroy_srq_resp { + u8 type; + #define CREQ_DESTROY_SRQ_RESP_TYPE_MASK 0x3fUL + #define CREQ_DESTROY_SRQ_RESP_TYPE_SFT 0 + #define CREQ_DESTROY_SRQ_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DESTROY_SRQ_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DESTROY_SRQ_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DESTROY_SRQ_RESP_V 0x1UL + #define CREQ_DESTROY_SRQ_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DESTROY_SRQ_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DESTROY_SRQ_RESP_EVENT_DESTROY_SRQ 0x6UL + __le16 enable_for_arm[3]; + #define CREQ_DESTROY_SRQ_RESP_ENABLE_FOR_ARM_MASK 0x30000UL + #define CREQ_DESTROY_SRQ_RESP_ENABLE_FOR_ARM_SFT 16 + #define CREQ_DESTROY_SRQ_RESP_RESERVED46_MASK 0xfffc0000UL + #define CREQ_DESTROY_SRQ_RESP_RESERVED46_SFT 18 +}; + +/* Query SRQ command response (16 bytes) */ +struct creq_query_srq_resp { + u8 type; + #define CREQ_QUERY_SRQ_RESP_TYPE_MASK 0x3fUL + #define CREQ_QUERY_SRQ_RESP_TYPE_SFT 0 + #define CREQ_QUERY_SRQ_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_QUERY_SRQ_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_QUERY_SRQ_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 size; + u8 v; + #define CREQ_QUERY_SRQ_RESP_V 0x1UL + #define CREQ_QUERY_SRQ_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_QUERY_SRQ_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_QUERY_SRQ_RESP_EVENT_QUERY_SRQ 0x8UL + __le16 reserved48[3]; +}; + +/* Query SRQ command response side buffer structure (24 bytes) */ +struct creq_query_srq_resp_sb { + u8 opcode; + #define CREQ_QUERY_SRQ_RESP_SB_OPCODE_QUERY_SRQ 0x8UL + u8 status; + __le16 cookie; + __le16 flags; + u8 resp_size; + u8 reserved8; + __le32 xid; + __le16 srq_limit; + __le16 reserved16; + __le32 data[4]; +}; + +/* Create CQ command Response (16 bytes) */ +struct creq_create_cq_resp { + u8 type; + #define CREQ_CREATE_CQ_RESP_TYPE_MASK 0x3fUL + #define CREQ_CREATE_CQ_RESP_TYPE_SFT 0 + #define CREQ_CREATE_CQ_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_CREATE_CQ_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_CREATE_CQ_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_CREATE_CQ_RESP_V 0x1UL + #define CREQ_CREATE_CQ_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_CREATE_CQ_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_CREATE_CQ_RESP_EVENT_CREATE_CQ 0x9UL + __le16 reserved48[3]; +}; + +/* Destroy CQ command response (16 bytes) */ +struct creq_destroy_cq_resp { + u8 type; + #define CREQ_DESTROY_CQ_RESP_TYPE_MASK 0x3fUL + #define CREQ_DESTROY_CQ_RESP_TYPE_SFT 0 + #define CREQ_DESTROY_CQ_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DESTROY_CQ_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DESTROY_CQ_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DESTROY_CQ_RESP_V 0x1UL + #define CREQ_DESTROY_CQ_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DESTROY_CQ_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DESTROY_CQ_RESP_EVENT_DESTROY_CQ 0xaUL + __le16 cq_arm_lvl; + #define CREQ_DESTROY_CQ_RESP_CQ_ARM_LVL_MASK 0x3UL + #define CREQ_DESTROY_CQ_RESP_CQ_ARM_LVL_SFT 0 + #define CREQ_DESTROY_CQ_RESP_RESERVED14_MASK 0xfffcUL + #define CREQ_DESTROY_CQ_RESP_RESERVED14_SFT 2 + __le16 total_cnq_events; + __le16 reserved16; +}; + +/* Resize CQ command response (16 bytes) */ +struct creq_resize_cq_resp { + u8 type; + #define CREQ_RESIZE_CQ_RESP_TYPE_MASK 0x3fUL + #define CREQ_RESIZE_CQ_RESP_TYPE_SFT 0 + #define CREQ_RESIZE_CQ_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_RESIZE_CQ_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_RESIZE_CQ_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_RESIZE_CQ_RESP_V 0x1UL + #define CREQ_RESIZE_CQ_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_RESIZE_CQ_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_RESIZE_CQ_RESP_EVENT_RESIZE_CQ 0xcUL + __le16 reserved48[3]; +}; + +/* Allocate MRW command response (16 bytes) */ +struct creq_allocate_mrw_resp { + u8 type; + #define CREQ_ALLOCATE_MRW_RESP_TYPE_MASK 0x3fUL + #define CREQ_ALLOCATE_MRW_RESP_TYPE_SFT 0 + #define CREQ_ALLOCATE_MRW_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_ALLOCATE_MRW_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_ALLOCATE_MRW_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_ALLOCATE_MRW_RESP_V 0x1UL + #define CREQ_ALLOCATE_MRW_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_ALLOCATE_MRW_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_ALLOCATE_MRW_RESP_EVENT_ALLOCATE_MRW 0xdUL + __le16 reserved48[3]; +}; + +/* De-allocate key command response (16 bytes) */ +struct creq_deallocate_key_resp { + u8 type; + #define CREQ_DEALLOCATE_KEY_RESP_TYPE_MASK 0x3fUL + #define CREQ_DEALLOCATE_KEY_RESP_TYPE_SFT 0 + #define CREQ_DEALLOCATE_KEY_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DEALLOCATE_KEY_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DEALLOCATE_KEY_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DEALLOCATE_KEY_RESP_V 0x1UL + #define CREQ_DEALLOCATE_KEY_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DEALLOCATE_KEY_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DEALLOCATE_KEY_RESP_EVENT_DEALLOCATE_KEY 0xeUL + __le16 reserved16; + __le32 bound_window_info; +}; + +/* Register MR command response (16 bytes) */ +struct creq_register_mr_resp { + u8 type; + #define CREQ_REGISTER_MR_RESP_TYPE_MASK 0x3fUL + #define CREQ_REGISTER_MR_RESP_TYPE_SFT 0 + #define CREQ_REGISTER_MR_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_REGISTER_MR_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_REGISTER_MR_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_REGISTER_MR_RESP_V 0x1UL + #define CREQ_REGISTER_MR_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_REGISTER_MR_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_REGISTER_MR_RESP_EVENT_REGISTER_MR 0xfUL + __le16 reserved48[3]; +}; + +/* Deregister MR command response (16 bytes) */ +struct creq_deregister_mr_resp { + u8 type; + #define CREQ_DEREGISTER_MR_RESP_TYPE_MASK 0x3fUL + #define CREQ_DEREGISTER_MR_RESP_TYPE_SFT 0 + #define CREQ_DEREGISTER_MR_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DEREGISTER_MR_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DEREGISTER_MR_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DEREGISTER_MR_RESP_V 0x1UL + #define CREQ_DEREGISTER_MR_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DEREGISTER_MR_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DEREGISTER_MR_RESP_EVENT_DEREGISTER_MR 0x10UL + __le16 reserved16; + __le32 bound_windows; +}; + +/* Add GID command response (16 bytes) */ +struct creq_add_gid_resp { + u8 type; + #define CREQ_ADD_GID_RESP_TYPE_MASK 0x3fUL + #define CREQ_ADD_GID_RESP_TYPE_SFT 0 + #define CREQ_ADD_GID_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_ADD_GID_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_ADD_GID_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_ADD_GID_RESP_V 0x1UL + #define CREQ_ADD_GID_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_ADD_GID_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_ADD_GID_RESP_EVENT_ADD_GID 0x11UL + __le16 reserved48[3]; +}; + +/* Delete GID command response (16 bytes) */ +struct creq_delete_gid_resp { + u8 type; + #define CREQ_DELETE_GID_RESP_TYPE_MASK 0x3fUL + #define CREQ_DELETE_GID_RESP_TYPE_SFT 0 + #define CREQ_DELETE_GID_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DELETE_GID_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DELETE_GID_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DELETE_GID_RESP_V 0x1UL + #define CREQ_DELETE_GID_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DELETE_GID_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DELETE_GID_RESP_EVENT_DELETE_GID 0x12UL + __le16 reserved48[3]; +}; + +/* Modify GID command response (16 bytes) */ +struct creq_modify_gid_resp { + u8 type; + #define CREQ_MODIFY_GID_RESP_TYPE_MASK 0x3fUL + #define CREQ_MODIFY_GID_RESP_TYPE_SFT 0 + #define CREQ_MODIFY_GID_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_MODIFY_GID_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_MODIFY_GID_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_MODIFY_GID_RESP_V 0x1UL + #define CREQ_MODIFY_GID_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_MODIFY_GID_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_MODIFY_GID_RESP_EVENT_ADD_GID 0x11UL + __le16 reserved48[3]; +}; + +/* Query GID command response (16 bytes) */ +struct creq_query_gid_resp { + u8 type; + #define CREQ_QUERY_GID_RESP_TYPE_MASK 0x3fUL + #define CREQ_QUERY_GID_RESP_TYPE_SFT 0 + #define CREQ_QUERY_GID_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_QUERY_GID_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_QUERY_GID_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 size; + u8 v; + #define CREQ_QUERY_GID_RESP_V 0x1UL + #define CREQ_QUERY_GID_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_QUERY_GID_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_QUERY_GID_RESP_EVENT_QUERY_GID 0x18UL + __le16 reserved48[3]; +}; + +/* Query GID command response side buffer structure (40 bytes) */ +struct creq_query_gid_resp_sb { + u8 opcode; + #define CREQ_QUERY_GID_RESP_SB_OPCODE_QUERY_GID 0x18UL + u8 status; + __le16 cookie; + __le16 flags; + u8 resp_size; + u8 reserved8; + __le32 gid[4]; + __le16 src_mac[3]; + __le16 vlan; + #define CREQ_QUERY_GID_RESP_SB_VLAN_VLAN_ID_MASK 0xfffUL + #define CREQ_QUERY_GID_RESP_SB_VLAN_VLAN_ID_SFT 0 + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_MASK 0x7000UL + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_SFT 12 + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_88A8 (0x0UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_8100 (0x1UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_9100 (0x2UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_9200 (0x3UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_9300 (0x4UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_CFG1 (0x5UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_CFG2 (0x6UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_CFG3 (0x7UL << 12) + #define CREQ_QUERY_GID_RESP_SB_VLAN_TPID_LAST \ + CREQ_QUERY_GID_RESP_SB_VLAN_TPID_TPID_CFG3 + #define CREQ_QUERY_GID_RESP_SB_VLAN_VLAN_EN 0x8000UL + __le16 ipid; + __le16 gid_index; + __le32 unused_0; +}; + +/* Create QP1 command response (16 bytes) */ +struct creq_create_qp1_resp { + u8 type; + #define CREQ_CREATE_QP1_RESP_TYPE_MASK 0x3fUL + #define CREQ_CREATE_QP1_RESP_TYPE_SFT 0 + #define CREQ_CREATE_QP1_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_CREATE_QP1_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_CREATE_QP1_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_CREATE_QP1_RESP_V 0x1UL + #define CREQ_CREATE_QP1_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_CREATE_QP1_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_CREATE_QP1_RESP_EVENT_CREATE_QP1 0x13UL + __le16 reserved48[3]; +}; + +/* Destroy QP1 command response (16 bytes) */ +struct creq_destroy_qp1_resp { + u8 type; + #define CREQ_DESTROY_QP1_RESP_TYPE_MASK 0x3fUL + #define CREQ_DESTROY_QP1_RESP_TYPE_SFT 0 + #define CREQ_DESTROY_QP1_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DESTROY_QP1_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DESTROY_QP1_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DESTROY_QP1_RESP_V 0x1UL + #define CREQ_DESTROY_QP1_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DESTROY_QP1_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DESTROY_QP1_RESP_EVENT_DESTROY_QP1 0x14UL + __le16 reserved48[3]; +}; + +/* Create AH command response (16 bytes) */ +struct creq_create_ah_resp { + u8 type; + #define CREQ_CREATE_AH_RESP_TYPE_MASK 0x3fUL + #define CREQ_CREATE_AH_RESP_TYPE_SFT 0 + #define CREQ_CREATE_AH_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_CREATE_AH_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_CREATE_AH_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_CREATE_AH_RESP_V 0x1UL + #define CREQ_CREATE_AH_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_CREATE_AH_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_CREATE_AH_RESP_EVENT_CREATE_AH 0x15UL + __le16 reserved48[3]; +}; + +/* Destroy AH command response (16 bytes) */ +struct creq_destroy_ah_resp { + u8 type; + #define CREQ_DESTROY_AH_RESP_TYPE_MASK 0x3fUL + #define CREQ_DESTROY_AH_RESP_TYPE_SFT 0 + #define CREQ_DESTROY_AH_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DESTROY_AH_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DESTROY_AH_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 xid; + u8 v; + #define CREQ_DESTROY_AH_RESP_V 0x1UL + #define CREQ_DESTROY_AH_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DESTROY_AH_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DESTROY_AH_RESP_EVENT_DESTROY_AH 0x16UL + __le16 reserved48[3]; +}; + +/* Initialize Firmware command response (16 bytes) */ +struct creq_initialize_fw_resp { + u8 type; + #define CREQ_INITIALIZE_FW_RESP_TYPE_MASK 0x3fUL + #define CREQ_INITIALIZE_FW_RESP_TYPE_SFT 0 + #define CREQ_INITIALIZE_FW_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_INITIALIZE_FW_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_INITIALIZE_FW_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 reserved32; + u8 v; + #define CREQ_INITIALIZE_FW_RESP_V 0x1UL + #define CREQ_INITIALIZE_FW_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_INITIALIZE_FW_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_INITIALIZE_FW_RESP_EVENT_INITIALIZE_FW 0x80UL + __le16 reserved48[3]; +}; + +/* De-initialize Firmware command response (16 bytes) */ +struct creq_deinitialize_fw_resp { + u8 type; + #define CREQ_DEINITIALIZE_FW_RESP_TYPE_MASK 0x3fUL + #define CREQ_DEINITIALIZE_FW_RESP_TYPE_SFT 0 + #define CREQ_DEINITIALIZE_FW_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_DEINITIALIZE_FW_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_DEINITIALIZE_FW_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 reserved32; + u8 v; + #define CREQ_DEINITIALIZE_FW_RESP_V 0x1UL + #define CREQ_DEINITIALIZE_FW_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_DEINITIALIZE_FW_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_DEINITIALIZE_FW_RESP_EVENT_DEINITIALIZE_FW 0x81UL + __le16 reserved48[3]; +}; + +/* Stop function command response (16 bytes) */ +struct creq_stop_func_resp { + u8 type; + #define CREQ_STOP_FUNC_RESP_TYPE_MASK 0x3fUL + #define CREQ_STOP_FUNC_RESP_TYPE_SFT 0 + #define CREQ_STOP_FUNC_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_STOP_FUNC_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_STOP_FUNC_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 reserved32; + u8 v; + #define CREQ_STOP_FUNC_RESP_V 0x1UL + #define CREQ_STOP_FUNC_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_STOP_FUNC_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_STOP_FUNC_RESP_EVENT_STOP_FUNC 0x82UL + __le16 reserved48[3]; +}; + +/* Query function command response (16 bytes) */ +struct creq_query_func_resp { + u8 type; + #define CREQ_QUERY_FUNC_RESP_TYPE_MASK 0x3fUL + #define CREQ_QUERY_FUNC_RESP_TYPE_SFT 0 + #define CREQ_QUERY_FUNC_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_QUERY_FUNC_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_QUERY_FUNC_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 size; + u8 v; + #define CREQ_QUERY_FUNC_RESP_V 0x1UL + #define CREQ_QUERY_FUNC_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_QUERY_FUNC_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_QUERY_FUNC_RESP_EVENT_QUERY_FUNC 0x83UL + __le16 reserved48[3]; +}; + +/* Query function command response side buffer structure (88 bytes) */ +struct creq_query_func_resp_sb { + u8 opcode; + #define CREQ_QUERY_FUNC_RESP_SB_OPCODE_QUERY_FUNC 0x83UL + u8 status; + __le16 cookie; + __le16 flags; + u8 resp_size; + u8 reserved8; + __le64 max_mr_size; + __le32 max_qp; + __le16 max_qp_wr; + __le16 dev_cap_flags; + #define CREQ_QUERY_FUNC_RESP_SB_DEV_CAP_FLAGS_RESIZE_QP 0x1UL + __le32 max_cq; + __le32 max_cqe; + __le32 max_pd; + u8 max_sge; + u8 max_srq_sge; + u8 max_qp_rd_atom; + u8 max_qp_init_rd_atom; + __le32 max_mr; + __le32 max_mw; + __le32 max_raw_eth_qp; + __le32 max_ah; + __le32 max_fmr; + __le32 max_srq_wr; + __le32 max_pkeys; + __le32 max_inline_data; + u8 max_map_per_fmr; + u8 l2_db_space_size; + __le16 max_srq; + __le32 max_gid; + __le32 tqm_alloc_reqs[8]; +}; + +/* Set resources command response (16 bytes) */ +struct creq_set_func_resources_resp { + u8 type; + #define CREQ_SET_FUNC_RESOURCES_RESP_TYPE_MASK 0x3fUL + #define CREQ_SET_FUNC_RESOURCES_RESP_TYPE_SFT 0 + #define CREQ_SET_FUNC_RESOURCES_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_SET_FUNC_RESOURCES_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_SET_FUNC_RESOURCES_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 reserved32; + u8 v; + #define CREQ_SET_FUNC_RESOURCES_RESP_V 0x1UL + #define CREQ_SET_FUNC_RESOURCES_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_SET_FUNC_RESOURCES_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_SET_FUNC_RESOURCES_RESP_EVENT_SET_FUNC_RESOURCES 0x84UL + __le16 reserved48[3]; +}; + +/* Map TC to COS response (16 bytes) */ +struct creq_map_tc_to_cos_resp { + u8 type; + #define CREQ_MAP_TC_TO_COS_RESP_TYPE_MASK 0x3fUL + #define CREQ_MAP_TC_TO_COS_RESP_TYPE_SFT 0 + #define CREQ_MAP_TC_TO_COS_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_MAP_TC_TO_COS_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_MAP_TC_TO_COS_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 reserved32; + u8 v; + #define CREQ_MAP_TC_TO_COS_RESP_V 0x1UL + #define CREQ_MAP_TC_TO_COS_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_MAP_TC_TO_COS_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_MAP_TC_TO_COS_RESP_EVENT_MAP_TC_TO_COS 0x8aUL + __le16 reserved48[3]; +}; + +/* Query version response (16 bytes) */ +struct creq_query_version_resp { + u8 type; + #define CREQ_QUERY_VERSION_RESP_TYPE_MASK 0x3fUL + #define CREQ_QUERY_VERSION_RESP_TYPE_SFT 0 + #define CREQ_QUERY_VERSION_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_QUERY_VERSION_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_QUERY_VERSION_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + u8 fw_maj; + u8 fw_minor; + u8 fw_bld; + u8 fw_rsvd; + u8 v; + #define CREQ_QUERY_VERSION_RESP_V 0x1UL + #define CREQ_QUERY_VERSION_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_QUERY_VERSION_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_QUERY_VERSION_RESP_EVENT_QUERY_VERSION 0x8bUL + __le16 reserved16; + u8 intf_maj; + u8 intf_minor; + u8 intf_bld; + u8 intf_rsvd; +}; + +/* Modify congestion control command response (16 bytes) */ +struct creq_modify_cc_resp { + u8 type; + #define CREQ_MODIFY_CC_RESP_TYPE_MASK 0x3fUL + #define CREQ_MODIFY_CC_RESP_TYPE_SFT 0 + #define CREQ_MODIFY_CC_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_MODIFY_CC_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_MODIFY_CC_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 reserved32; + u8 v; + #define CREQ_MODIFY_CC_RESP_V 0x1UL + #define CREQ_MODIFY_CC_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_MODIFY_CC_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_MODIFY_CC_RESP_EVENT_MODIFY_CC 0x8cUL + __le16 reserved48[3]; +}; + +/* Query congestion control command response (16 bytes) */ +struct creq_query_cc_resp { + u8 type; + #define CREQ_QUERY_CC_RESP_TYPE_MASK 0x3fUL + #define CREQ_QUERY_CC_RESP_TYPE_SFT 0 + #define CREQ_QUERY_CC_RESP_TYPE_QP_EVENT 0x38UL + #define CREQ_QUERY_CC_RESP_RESERVED2_MASK 0xc0UL + #define CREQ_QUERY_CC_RESP_RESERVED2_SFT 6 + u8 status; + __le16 cookie; + __le32 size; + u8 v; + #define CREQ_QUERY_CC_RESP_V 0x1UL + #define CREQ_QUERY_CC_RESP_RESERVED7_MASK 0xfeUL + #define CREQ_QUERY_CC_RESP_RESERVED7_SFT 1 + u8 event; + #define CREQ_QUERY_CC_RESP_EVENT_QUERY_CC 0x8dUL + __le16 reserved48[3]; +}; + +/* Query congestion control command response side buffer structure (32 bytes) */ +struct creq_query_cc_resp_sb { + u8 opcode; + #define CREQ_QUERY_CC_RESP_SB_OPCODE_QUERY_CC 0x8dUL + u8 status; + __le16 cookie; + __le16 flags; + u8 resp_size; + u8 reserved8; + u8 enable_cc; + #define CREQ_QUERY_CC_RESP_SB_ENABLE_CC 0x1UL + u8 g; + #define CREQ_QUERY_CC_RESP_SB_G_MASK 0x7UL + #define CREQ_QUERY_CC_RESP_SB_G_SFT 0 + u8 num_phases_per_state; + __le16 init_cr; + u8 unused_2; + __le16 unused_3; + u8 unused_4; + __le16 init_tr; + u8 tos_dscp_tos_ecn; + #define CREQ_QUERY_CC_RESP_SB_TOS_ECN_MASK 0x3UL + #define CREQ_QUERY_CC_RESP_SB_TOS_ECN_SFT 0 + #define CREQ_QUERY_CC_RESP_SB_TOS_DSCP_MASK 0xfcUL + #define CREQ_QUERY_CC_RESP_SB_TOS_DSCP_SFT 2 + __le64 reserved64; + __le64 reserved64_1; +}; + +/* QP error notification event (16 bytes) */ +struct creq_qp_error_notification { + u8 type; + #define CREQ_QP_ERROR_NOTIFICATION_TYPE_MASK 0x3fUL + #define CREQ_QP_ERROR_NOTIFICATION_TYPE_SFT 0 + #define CREQ_QP_ERROR_NOTIFICATION_TYPE_QP_EVENT 0x38UL + #define CREQ_QP_ERROR_NOTIFICATION_RESERVED2_MASK 0xc0UL + #define CREQ_QP_ERROR_NOTIFICATION_RESERVED2_SFT 6 + u8 status; + u8 req_slow_path_state; + u8 req_err_state_reason; + __le32 xid; + u8 v; + #define CREQ_QP_ERROR_NOTIFICATION_V 0x1UL + #define CREQ_QP_ERROR_NOTIFICATION_RESERVED7_MASK 0xfeUL + #define CREQ_QP_ERROR_NOTIFICATION_RESERVED7_SFT 1 + u8 event; + #define CREQ_QP_ERROR_NOTIFICATION_EVENT_QP_ERROR_NOTIFICATION 0xc0UL + u8 res_slow_path_state; + u8 res_err_state_reason; + __le16 sq_cons_idx; + __le16 rq_cons_idx; +}; + +/* RoCE Slowpath HSI Specification 1.6.0 */ +#define ROCE_SP_HSI_VERSION_MAJOR 1 +#define ROCE_SP_HSI_VERSION_MINOR 6 +#define ROCE_SP_HSI_VERSION_UPDATE 0 + +#define ROCE_SP_HSI_VERSION_STR "1.6.0" +/* + * Following is the signature for ROCE_SP_HSI message field that indicates not + * applicable (All F's). Need to cast it the size of the field if needed. + */ +#define ROCE_SP_HSI_NA_SIGNATURE ((__le32)(-1)) +#endif /* __BNXT_RE_HSI_H__ */ diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 6262dc035f3c..86ecd3ea6a4b 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -37,7 +37,7 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/list.h> -#include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/spinlock.h> #include <linux/ethtool.h> #include <linux/rtnetlink.h> @@ -1133,7 +1133,7 @@ static int iwch_query_port(struct ib_device *ibdev, dev = to_iwch_dev(ibdev); netdev = dev->rdev.port_info.lldevs[port-1]; - memset(props, 0, sizeof(struct ib_port_attr)); + /* props being zeroed by the caller, avoid zeroing it here */ props->max_mtu = IB_MTU_4096; props->active_mtu = ib_mtu_int_to_enum(netdev->mtu); @@ -1329,13 +1329,14 @@ static int iwch_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; - err = iwch_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; return 0; } @@ -1392,7 +1393,7 @@ int iwch_register_device(struct iwch_dev *dev) memcpy(dev->ibdev.node_desc, IWCH_NODE_DESC, sizeof(IWCH_NODE_DESC)); dev->ibdev.phys_port_cnt = dev->rdev.port_info.nports; dev->ibdev.num_comp_vectors = 1; - dev->ibdev.dma_device = &(dev->rdev.rnic_info.pdev->dev); + dev->ibdev.dev.parent = &dev->rdev.rnic_info.pdev->dev; dev->ibdev.query_device = iwch_query_device; dev->ibdev.query_port = iwch_query_port; dev->ibdev.query_pkey = iwch_query_pkey; diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 9398143d7c5e..03a1b0e64fc3 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -692,6 +692,10 @@ static int send_connect(struct c4iw_ep *ep) int ret; enum chip_type adapter_type = ep->com.dev->rdev.lldi.adapter_type; u32 isn = (prandom_u32() & ~7UL) - 1; + struct net_device *netdev; + u64 params; + + netdev = ep->com.dev->rdev.lldi.ports[0]; switch (CHELSIO_CHIP_VERSION(adapter_type)) { case CHELSIO_T4: @@ -768,6 +772,8 @@ static int send_connect(struct c4iw_ep *ep) opt2 |= T5_ISS_F; } + params = cxgb4_select_ntuple(netdev, ep->l2t); + if (ep->com.remote_addr.ss_family == AF_INET6) cxgb4_clip_get(ep->com.dev->rdev.lldi.ports[0], (const u32 *)&la6->sin6_addr.s6_addr, 1); @@ -809,18 +815,22 @@ static int send_connect(struct c4iw_ep *ep) req->opt0 = cpu_to_be64(opt0); if (is_t4(ep->com.dev->rdev.lldi.adapter_type)) { - req->params = cpu_to_be32(cxgb4_select_ntuple( - ep->com.dev->rdev.lldi.ports[0], - ep->l2t)); + req->params = cpu_to_be32(params); req->opt2 = cpu_to_be32(opt2); } else { - t5req->params = cpu_to_be64(FILTER_TUPLE_V( - cxgb4_select_ntuple( - ep->com.dev->rdev.lldi.ports[0], - ep->l2t))); - t5req->rsvd = cpu_to_be32(isn); - PDBG("%s snd_isn %u\n", __func__, t5req->rsvd); - t5req->opt2 = cpu_to_be32(opt2); + if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) { + t5req->params = + cpu_to_be64(FILTER_TUPLE_V(params)); + t5req->rsvd = cpu_to_be32(isn); + PDBG("%s snd_isn %u\n", __func__, t5req->rsvd); + t5req->opt2 = cpu_to_be32(opt2); + } else { + t6req->params = + cpu_to_be64(FILTER_TUPLE_V(params)); + t6req->rsvd = cpu_to_be32(isn); + PDBG("%s snd_isn %u\n", __func__, t6req->rsvd); + t6req->opt2 = cpu_to_be32(opt2); + } } } else { switch (CHELSIO_CHIP_VERSION(adapter_type)) { @@ -859,18 +869,24 @@ static int send_connect(struct c4iw_ep *ep) req6->opt0 = cpu_to_be64(opt0); if (is_t4(ep->com.dev->rdev.lldi.adapter_type)) { - req6->params = cpu_to_be32(cxgb4_select_ntuple( - ep->com.dev->rdev.lldi.ports[0], - ep->l2t)); + req6->params = cpu_to_be32(cxgb4_select_ntuple(netdev, + ep->l2t)); req6->opt2 = cpu_to_be32(opt2); } else { - t5req6->params = cpu_to_be64(FILTER_TUPLE_V( - cxgb4_select_ntuple( - ep->com.dev->rdev.lldi.ports[0], - ep->l2t))); - t5req6->rsvd = cpu_to_be32(isn); - PDBG("%s snd_isn %u\n", __func__, t5req6->rsvd); - t5req6->opt2 = cpu_to_be32(opt2); + if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) { + t5req6->params = + cpu_to_be64(FILTER_TUPLE_V(params)); + t5req6->rsvd = cpu_to_be32(isn); + PDBG("%s snd_isn %u\n", __func__, t5req6->rsvd); + t5req6->opt2 = cpu_to_be32(opt2); + } else { + t6req6->params = + cpu_to_be64(FILTER_TUPLE_V(params)); + t6req6->rsvd = cpu_to_be32(isn); + PDBG("%s snd_isn %u\n", __func__, t6req6->rsvd); + t6req6->opt2 = cpu_to_be32(opt2); + } + } } @@ -2517,18 +2533,18 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) struct sockaddr_in *sin = (struct sockaddr_in *) &child_ep->com.local_addr; - sin->sin_family = PF_INET; + sin->sin_family = AF_INET; sin->sin_port = local_port; sin->sin_addr.s_addr = *(__be32 *)local_ip; sin = (struct sockaddr_in *)&child_ep->com.local_addr; - sin->sin_family = PF_INET; + sin->sin_family = AF_INET; sin->sin_port = ((struct sockaddr_in *) &parent_ep->com.local_addr)->sin_port; sin->sin_addr.s_addr = *(__be32 *)local_ip; sin = (struct sockaddr_in *)&child_ep->com.remote_addr; - sin->sin_family = PF_INET; + sin->sin_family = AF_INET; sin->sin_port = peer_port; sin->sin_addr.s_addr = *(__be32 *)peer_ip; } else { diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index 40c0e7b9fc6e..4e4f1a732b01 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -214,6 +214,52 @@ static const struct file_operations wr_log_debugfs_fops = { .write = wr_log_clear, }; +static struct sockaddr_in zero_sin = { + .sin_family = AF_INET, +}; + +static struct sockaddr_in6 zero_sin6 = { + .sin6_family = AF_INET6, +}; + +static void set_ep_sin_addrs(struct c4iw_ep *ep, + struct sockaddr_in **lsin, + struct sockaddr_in **rsin, + struct sockaddr_in **m_lsin, + struct sockaddr_in **m_rsin) +{ + struct iw_cm_id *id = ep->com.cm_id; + + *lsin = (struct sockaddr_in *)&ep->com.local_addr; + *rsin = (struct sockaddr_in *)&ep->com.remote_addr; + if (id) { + *m_lsin = (struct sockaddr_in *)&id->m_local_addr; + *m_rsin = (struct sockaddr_in *)&id->m_remote_addr; + } else { + *m_lsin = &zero_sin; + *m_rsin = &zero_sin; + } +} + +static void set_ep_sin6_addrs(struct c4iw_ep *ep, + struct sockaddr_in6 **lsin6, + struct sockaddr_in6 **rsin6, + struct sockaddr_in6 **m_lsin6, + struct sockaddr_in6 **m_rsin6) +{ + struct iw_cm_id *id = ep->com.cm_id; + + *lsin6 = (struct sockaddr_in6 *)&ep->com.local_addr; + *rsin6 = (struct sockaddr_in6 *)&ep->com.remote_addr; + if (id) { + *m_lsin6 = (struct sockaddr_in6 *)&id->m_local_addr; + *m_rsin6 = (struct sockaddr_in6 *)&id->m_remote_addr; + } else { + *m_lsin6 = &zero_sin6; + *m_rsin6 = &zero_sin6; + } +} + static int dump_qp(int id, void *p, void *data) { struct c4iw_qp *qp = p; @@ -229,16 +275,15 @@ static int dump_qp(int id, void *p, void *data) return 1; if (qp->ep) { - if (qp->ep->com.local_addr.ss_family == AF_INET) { - struct sockaddr_in *lsin = (struct sockaddr_in *) - &qp->ep->com.cm_id->local_addr; - struct sockaddr_in *rsin = (struct sockaddr_in *) - &qp->ep->com.cm_id->remote_addr; - struct sockaddr_in *mapped_lsin = (struct sockaddr_in *) - &qp->ep->com.cm_id->m_local_addr; - struct sockaddr_in *mapped_rsin = (struct sockaddr_in *) - &qp->ep->com.cm_id->m_remote_addr; + struct c4iw_ep *ep = qp->ep; + + if (ep->com.local_addr.ss_family == AF_INET) { + struct sockaddr_in *lsin; + struct sockaddr_in *rsin; + struct sockaddr_in *m_lsin; + struct sockaddr_in *m_rsin; + set_ep_sin_addrs(ep, &lsin, &rsin, &m_lsin, &m_rsin); cc = snprintf(qpd->buf + qpd->pos, space, "rc qp sq id %u rq id %u state %u " "onchip %u ep tid %u state %u " @@ -246,23 +291,19 @@ static int dump_qp(int id, void *p, void *data) qp->wq.sq.qid, qp->wq.rq.qid, (int)qp->attr.state, qp->wq.sq.flags & T4_SQ_ONCHIP, - qp->ep->hwtid, (int)qp->ep->com.state, + ep->hwtid, (int)ep->com.state, &lsin->sin_addr, ntohs(lsin->sin_port), - ntohs(mapped_lsin->sin_port), + ntohs(m_lsin->sin_port), &rsin->sin_addr, ntohs(rsin->sin_port), - ntohs(mapped_rsin->sin_port)); + ntohs(m_rsin->sin_port)); } else { - struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *) - &qp->ep->com.cm_id->local_addr; - struct sockaddr_in6 *rsin6 = (struct sockaddr_in6 *) - &qp->ep->com.cm_id->remote_addr; - struct sockaddr_in6 *mapped_lsin6 = - (struct sockaddr_in6 *) - &qp->ep->com.cm_id->m_local_addr; - struct sockaddr_in6 *mapped_rsin6 = - (struct sockaddr_in6 *) - &qp->ep->com.cm_id->m_remote_addr; + struct sockaddr_in6 *lsin6; + struct sockaddr_in6 *rsin6; + struct sockaddr_in6 *m_lsin6; + struct sockaddr_in6 *m_rsin6; + set_ep_sin6_addrs(ep, &lsin6, &rsin6, &m_lsin6, + &m_rsin6); cc = snprintf(qpd->buf + qpd->pos, space, "rc qp sq id %u rq id %u state %u " "onchip %u ep tid %u state %u " @@ -270,13 +311,13 @@ static int dump_qp(int id, void *p, void *data) qp->wq.sq.qid, qp->wq.rq.qid, (int)qp->attr.state, qp->wq.sq.flags & T4_SQ_ONCHIP, - qp->ep->hwtid, (int)qp->ep->com.state, + ep->hwtid, (int)ep->com.state, &lsin6->sin6_addr, ntohs(lsin6->sin6_port), - ntohs(mapped_lsin6->sin6_port), + ntohs(m_lsin6->sin6_port), &rsin6->sin6_addr, ntohs(rsin6->sin6_port), - ntohs(mapped_rsin6->sin6_port)); + ntohs(m_rsin6->sin6_port)); } } else cc = snprintf(qpd->buf + qpd->pos, space, @@ -533,15 +574,12 @@ static int dump_ep(int id, void *p, void *data) return 1; if (ep->com.local_addr.ss_family == AF_INET) { - struct sockaddr_in *lsin = (struct sockaddr_in *) - &ep->com.cm_id->local_addr; - struct sockaddr_in *rsin = (struct sockaddr_in *) - &ep->com.cm_id->remote_addr; - struct sockaddr_in *mapped_lsin = (struct sockaddr_in *) - &ep->com.cm_id->m_local_addr; - struct sockaddr_in *mapped_rsin = (struct sockaddr_in *) - &ep->com.cm_id->m_remote_addr; + struct sockaddr_in *lsin; + struct sockaddr_in *rsin; + struct sockaddr_in *m_lsin; + struct sockaddr_in *m_rsin; + set_ep_sin_addrs(ep, &lsin, &rsin, &m_lsin, &m_rsin); cc = snprintf(epd->buf + epd->pos, space, "ep %p cm_id %p qp %p state %d flags 0x%lx " "history 0x%lx hwtid %d atid %d " @@ -553,19 +591,16 @@ static int dump_ep(int id, void *p, void *data) ep->stats.connect_neg_adv, ep->stats.abort_neg_adv, &lsin->sin_addr, ntohs(lsin->sin_port), - ntohs(mapped_lsin->sin_port), + ntohs(m_lsin->sin_port), &rsin->sin_addr, ntohs(rsin->sin_port), - ntohs(mapped_rsin->sin_port)); + ntohs(m_rsin->sin_port)); } else { - struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *) - &ep->com.cm_id->local_addr; - struct sockaddr_in6 *rsin6 = (struct sockaddr_in6 *) - &ep->com.cm_id->remote_addr; - struct sockaddr_in6 *mapped_lsin6 = (struct sockaddr_in6 *) - &ep->com.cm_id->m_local_addr; - struct sockaddr_in6 *mapped_rsin6 = (struct sockaddr_in6 *) - &ep->com.cm_id->m_remote_addr; + struct sockaddr_in6 *lsin6; + struct sockaddr_in6 *rsin6; + struct sockaddr_in6 *m_lsin6; + struct sockaddr_in6 *m_rsin6; + set_ep_sin6_addrs(ep, &lsin6, &rsin6, &m_lsin6, &m_rsin6); cc = snprintf(epd->buf + epd->pos, space, "ep %p cm_id %p qp %p state %d flags 0x%lx " "history 0x%lx hwtid %d atid %d " @@ -577,9 +612,9 @@ static int dump_ep(int id, void *p, void *data) ep->stats.connect_neg_adv, ep->stats.abort_neg_adv, &lsin6->sin6_addr, ntohs(lsin6->sin6_port), - ntohs(mapped_lsin6->sin6_port), + ntohs(m_lsin6->sin6_port), &rsin6->sin6_addr, ntohs(rsin6->sin6_port), - ntohs(mapped_rsin6->sin6_port)); + ntohs(m_rsin6->sin6_port)); } if (cc < space) epd->pos += cc; @@ -600,7 +635,7 @@ static int dump_listen_ep(int id, void *p, void *data) if (ep->com.local_addr.ss_family == AF_INET) { struct sockaddr_in *lsin = (struct sockaddr_in *) &ep->com.cm_id->local_addr; - struct sockaddr_in *mapped_lsin = (struct sockaddr_in *) + struct sockaddr_in *m_lsin = (struct sockaddr_in *) &ep->com.cm_id->m_local_addr; cc = snprintf(epd->buf + epd->pos, space, @@ -609,11 +644,11 @@ static int dump_listen_ep(int id, void *p, void *data) ep, ep->com.cm_id, (int)ep->com.state, ep->com.flags, ep->stid, ep->backlog, &lsin->sin_addr, ntohs(lsin->sin_port), - ntohs(mapped_lsin->sin_port)); + ntohs(m_lsin->sin_port)); } else { struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *) &ep->com.cm_id->local_addr; - struct sockaddr_in6 *mapped_lsin6 = (struct sockaddr_in6 *) + struct sockaddr_in6 *m_lsin6 = (struct sockaddr_in6 *) &ep->com.cm_id->m_local_addr; cc = snprintf(epd->buf + epd->pos, space, @@ -622,7 +657,7 @@ static int dump_listen_ep(int id, void *p, void *data) ep, ep->com.cm_id, (int)ep->com.state, ep->com.flags, ep->stid, ep->backlog, &lsin6->sin6_addr, ntohs(lsin6->sin6_port), - ntohs(mapped_lsin6->sin6_port)); + ntohs(m_lsin6->sin6_port)); } if (cc < space) epd->pos += cc; diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index d19662f635b1..5846c47c8d55 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -37,7 +37,7 @@ #include <linux/idr.h> #include <linux/completion.h> #include <linux/netdevice.h> -#include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/inet.h> diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c index 3345e1c312f7..df64417ab6f2 100644 --- a/drivers/infiniband/hw/cxgb4/provider.c +++ b/drivers/infiniband/hw/cxgb4/provider.c @@ -370,8 +370,7 @@ static int c4iw_query_port(struct ib_device *ibdev, u8 port, dev = to_c4iw_dev(ibdev); netdev = dev->rdev.lldi.ports[port-1]; - - memset(props, 0, sizeof(struct ib_port_attr)); + /* props being zeroed by the caller, avoid zeroing it here */ props->max_mtu = IB_MTU_4096; props->active_mtu = ib_mtu_int_to_enum(netdev->mtu); @@ -508,13 +507,14 @@ static int c4iw_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; - err = c4iw_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; return 0; } @@ -572,7 +572,7 @@ int c4iw_register_device(struct c4iw_dev *dev) memcpy(dev->ibdev.node_desc, C4IW_NODE_DESC, sizeof(C4IW_NODE_DESC)); dev->ibdev.phys_port_cnt = dev->rdev.lldi.nports; dev->ibdev.num_comp_vectors = dev->rdev.lldi.nciq; - dev->ibdev.dma_device = &(dev->rdev.lldi.pdev->dev); + dev->ibdev.dev.parent = &dev->rdev.lldi.pdev->dev; dev->ibdev.query_device = c4iw_query_device; dev->ibdev.query_port = c4iw_query_port; dev->ibdev.query_pkey = c4iw_query_pkey; diff --git a/drivers/infiniband/hw/hfi1/affinity.c b/drivers/infiniband/hw/hfi1/affinity.c index 7a3d906b3671..e2cd2cd3b28a 100644 --- a/drivers/infiniband/hw/hfi1/affinity.c +++ b/drivers/infiniband/hw/hfi1/affinity.c @@ -576,7 +576,7 @@ int hfi1_get_proc_affinity(int node) struct hfi1_affinity_node *entry; cpumask_var_t diff, hw_thread_mask, available_mask, intrs_mask; const struct cpumask *node_mask, - *proc_mask = tsk_cpus_allowed(current); + *proc_mask = ¤t->cpus_allowed; struct hfi1_affinity_node_list *affinity = &node_affinity; struct cpu_mask_set *set = &affinity->proc; diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index ef72bc2a9e1d..121a4c920f1b 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -7827,7 +7827,8 @@ static void handle_dcc_err(struct hfi1_devdata *dd, u32 unused, u64 reg) } /* just report this */ - dd_dev_info(dd, "DCC Error: fmconfig error: %s\n", extra); + dd_dev_info_ratelimited(dd, "DCC Error: fmconfig error: %s\n", + extra); reg &= ~DCC_ERR_FLG_FMCONFIG_ERR_SMASK; } @@ -7878,34 +7879,35 @@ static void handle_dcc_err(struct hfi1_devdata *dd, u32 unused, u64 reg) } /* just report this */ - dd_dev_info(dd, "DCC Error: PortRcv error: %s\n", extra); - dd_dev_info(dd, " hdr0 0x%llx, hdr1 0x%llx\n", - hdr0, hdr1); + dd_dev_info_ratelimited(dd, "DCC Error: PortRcv error: %s\n" + " hdr0 0x%llx, hdr1 0x%llx\n", + extra, hdr0, hdr1); reg &= ~DCC_ERR_FLG_RCVPORT_ERR_SMASK; } if (reg & DCC_ERR_FLG_EN_CSR_ACCESS_BLOCKED_UC_SMASK) { /* informative only */ - dd_dev_info(dd, "8051 access to LCB blocked\n"); + dd_dev_info_ratelimited(dd, "8051 access to LCB blocked\n"); reg &= ~DCC_ERR_FLG_EN_CSR_ACCESS_BLOCKED_UC_SMASK; } if (reg & DCC_ERR_FLG_EN_CSR_ACCESS_BLOCKED_HOST_SMASK) { /* informative only */ - dd_dev_info(dd, "host access to LCB blocked\n"); + dd_dev_info_ratelimited(dd, "host access to LCB blocked\n"); reg &= ~DCC_ERR_FLG_EN_CSR_ACCESS_BLOCKED_HOST_SMASK; } /* report any remaining errors */ if (reg) - dd_dev_info(dd, "DCC Error: %s\n", - dcc_err_string(buf, sizeof(buf), reg)); + dd_dev_info_ratelimited(dd, "DCC Error: %s\n", + dcc_err_string(buf, sizeof(buf), reg)); if (lcl_reason == 0) lcl_reason = OPA_LINKDOWN_REASON_UNKNOWN; if (do_bounce) { - dd_dev_info(dd, "%s: PortErrorAction bounce\n", __func__); + dd_dev_info_ratelimited(dd, "%s: PortErrorAction bounce\n", + __func__); set_link_down_reason(ppd, lcl_reason, 0, lcl_reason); queue_work(ppd->hfi1_wq, &ppd->link_bounce_work); } @@ -10508,16 +10510,18 @@ int set_link_state(struct hfi1_pportdata *ppd, u32 state) ppd->remote_link_down_reason = 0; } - ret1 = set_physical_link_state(dd, PLS_DISABLED); - if (ret1 != HCMD_SUCCESS) { - dd_dev_err(dd, - "Failed to transition to Disabled link state, return 0x%x\n", - ret1); - ret = -EINVAL; - break; + if (!dd->dc_shutdown) { + ret1 = set_physical_link_state(dd, PLS_DISABLED); + if (ret1 != HCMD_SUCCESS) { + dd_dev_err(dd, + "Failed to transition to Disabled link state, return 0x%x\n", + ret1); + ret = -EINVAL; + break; + } + dc_shutdown(dd); } ppd->host_link_state = HLS_DN_DISABLE; - dc_shutdown(dd); break; case HLS_DN_OFFLINE: if (ppd->host_link_state == HLS_DN_DISABLE) diff --git a/drivers/infiniband/hw/hfi1/common.h b/drivers/infiniband/hw/hfi1/common.h index da7be21bedb4..1b783bbee4bb 100644 --- a/drivers/infiniband/hw/hfi1/common.h +++ b/drivers/infiniband/hw/hfi1/common.h @@ -331,10 +331,6 @@ struct diag_pkt { #define FULL_MGMT_P_KEY 0xFFFF #define DEFAULT_P_KEY LIM_MGMT_P_KEY -#define HFI1_AETH_CREDIT_SHIFT 24 -#define HFI1_AETH_CREDIT_MASK 0x1F -#define HFI1_AETH_CREDIT_INVAL 0x1F -#define HFI1_MSN_MASK 0xFFFFFF #define HFI1_FECN_SHIFT 31 #define HFI1_FECN_MASK 1 #define HFI1_FECN_SMASK BIT(HFI1_FECN_SHIFT) diff --git a/drivers/infiniband/hw/hfi1/debugfs.c b/drivers/infiniband/hw/hfi1/debugfs.c index 8725f4c086cf..7fe9dd885746 100644 --- a/drivers/infiniband/hw/hfi1/debugfs.c +++ b/drivers/infiniband/hw/hfi1/debugfs.c @@ -50,6 +50,7 @@ #include <linux/kernel.h> #include <linux/export.h> #include <linux/module.h> +#include <linux/string.h> #include "hfi.h" #include "debugfs.h" @@ -503,18 +504,11 @@ static ssize_t asic_flags_write(struct file *file, const char __user *buf, ppd = private2ppd(file); dd = ppd->dd; - buff = kmalloc(count + 1, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - ret = copy_from_user(buff, buf, count); - if (ret > 0) { - ret = -EFAULT; - goto do_free; - } - /* zero terminate and read the expected integer */ - buff[count] = 0; + buff = memdup_user_nul(buf, count); + if (IS_ERR(buff)) + return PTR_ERR(buff); + ret = kstrtoull(buff, 0, &value); if (ret) goto do_free; @@ -692,15 +686,9 @@ static ssize_t __i2c_debugfs_write(struct file *file, const char __user *buf, if (i2c_addr == 0) return -EINVAL; - buff = kmalloc(count, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - ret = copy_from_user(buff, buf, count); - if (ret > 0) { - ret = -EFAULT; - goto _free; - } + buff = memdup_user(buf, count); + if (IS_ERR(buff)) + return PTR_ERR(buff); total_written = i2c_write(ppd, target, i2c_addr, offset, buff, count); if (total_written < 0) { @@ -805,15 +793,10 @@ static ssize_t __qsfp_debugfs_write(struct file *file, const char __user *buf, ppd = private2ppd(file); - buff = kmalloc(count, GFP_KERNEL); - if (!buff) - return -ENOMEM; + buff = memdup_user(buf, count); + if (IS_ERR(buff)) + return PTR_ERR(buff); - ret = copy_from_user(buff, buf, count); - if (ret > 0) { - ret = -EFAULT; - goto _free; - } total_written = qsfp_write(ppd, target, *ppos, buff, count); if (total_written < 0) { ret = total_written; diff --git a/drivers/infiniband/hw/hfi1/dma.c b/drivers/infiniband/hw/hfi1/dma.c deleted file mode 100644 index 7e8dab892848..000000000000 --- a/drivers/infiniband/hw/hfi1/dma.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright(c) 2015, 2016 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#include <linux/types.h> -#include <linux/scatterlist.h> - -#include "verbs.h" - -#define BAD_DMA_ADDRESS ((u64)0) - -/* - * The following functions implement driver specific replacements - * for the ib_dma_*() functions. - * - * These functions return kernel virtual addresses instead of - * device bus addresses since the driver uses the CPU to copy - * data instead of using hardware DMA. - */ - -static int hfi1_mapping_error(struct ib_device *dev, u64 dma_addr) -{ - return dma_addr == BAD_DMA_ADDRESS; -} - -static u64 hfi1_dma_map_single(struct ib_device *dev, void *cpu_addr, - size_t size, enum dma_data_direction direction) -{ - if (WARN_ON(!valid_dma_direction(direction))) - return BAD_DMA_ADDRESS; - - return (u64)cpu_addr; -} - -static void hfi1_dma_unmap_single(struct ib_device *dev, u64 addr, size_t size, - enum dma_data_direction direction) -{ - /* This is a stub, nothing to be done here */ -} - -static u64 hfi1_dma_map_page(struct ib_device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction direction) -{ - u64 addr; - - if (WARN_ON(!valid_dma_direction(direction))) - return BAD_DMA_ADDRESS; - - if (offset + size > PAGE_SIZE) - return BAD_DMA_ADDRESS; - - addr = (u64)page_address(page); - if (addr) - addr += offset; - - return addr; -} - -static void hfi1_dma_unmap_page(struct ib_device *dev, u64 addr, size_t size, - enum dma_data_direction direction) -{ - /* This is a stub, nothing to be done here */ -} - -static int hfi1_map_sg(struct ib_device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction direction) -{ - struct scatterlist *sg; - u64 addr; - int i; - int ret = nents; - - if (WARN_ON(!valid_dma_direction(direction))) - return BAD_DMA_ADDRESS; - - for_each_sg(sgl, sg, nents, i) { - addr = (u64)page_address(sg_page(sg)); - if (!addr) { - ret = 0; - break; - } - sg->dma_address = addr + sg->offset; -#ifdef CONFIG_NEED_SG_DMA_LENGTH - sg->dma_length = sg->length; -#endif - } - return ret; -} - -static void hfi1_unmap_sg(struct ib_device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction direction) -{ - /* This is a stub, nothing to be done here */ -} - -static void hfi1_sync_single_for_cpu(struct ib_device *dev, u64 addr, - size_t size, enum dma_data_direction dir) -{ -} - -static void hfi1_sync_single_for_device(struct ib_device *dev, u64 addr, - size_t size, - enum dma_data_direction dir) -{ -} - -static void *hfi1_dma_alloc_coherent(struct ib_device *dev, size_t size, - u64 *dma_handle, gfp_t flag) -{ - struct page *p; - void *addr = NULL; - - p = alloc_pages(flag, get_order(size)); - if (p) - addr = page_address(p); - if (dma_handle) - *dma_handle = (u64)addr; - return addr; -} - -static void hfi1_dma_free_coherent(struct ib_device *dev, size_t size, - void *cpu_addr, u64 dma_handle) -{ - free_pages((unsigned long)cpu_addr, get_order(size)); -} - -struct ib_dma_mapping_ops hfi1_dma_mapping_ops = { - .mapping_error = hfi1_mapping_error, - .map_single = hfi1_dma_map_single, - .unmap_single = hfi1_dma_unmap_single, - .map_page = hfi1_dma_map_page, - .unmap_page = hfi1_dma_unmap_page, - .map_sg = hfi1_map_sg, - .unmap_sg = hfi1_unmap_sg, - .sync_single_for_cpu = hfi1_sync_single_for_cpu, - .sync_single_for_device = hfi1_sync_single_for_device, - .alloc_coherent = hfi1_dma_alloc_coherent, - .free_coherent = hfi1_dma_free_coherent -}; diff --git a/drivers/infiniband/hw/hfi1/driver.c b/drivers/infiniband/hw/hfi1/driver.c index 4fbaee68012b..3881c951f6af 100644 --- a/drivers/infiniband/hw/hfi1/driver.c +++ b/drivers/infiniband/hw/hfi1/driver.c @@ -100,6 +100,11 @@ MODULE_VERSION(HFI1_DRIVER_VERSION); * MAX_PKT_RCV is the max # if packets processed per receive interrupt. */ #define MAX_PKT_RECV 64 +/* + * MAX_PKT_THREAD_RCV is the max # of packets processed before + * the qp_wait_list queue is flushed. + */ +#define MAX_PKT_RECV_THREAD (MAX_PKT_RECV * 4) #define EGR_HEAD_UPDATE_THRESHOLD 16 struct hfi1_ib_stats hfi1_stats; @@ -259,7 +264,7 @@ static inline void *get_egrbuf(const struct hfi1_ctxtdata *rcd, u64 rhf, * allowed size ranges for the respective type and, optionally, * return the proper encoding. */ -inline int hfi1_rcvbuf_validate(u32 size, u8 type, u16 *encoded) +int hfi1_rcvbuf_validate(u32 size, u8 type, u16 *encoded) { if (unlikely(!PAGE_ALIGNED(size))) return 0; @@ -279,7 +284,7 @@ static void rcv_hdrerr(struct hfi1_ctxtdata *rcd, struct hfi1_pportdata *ppd, struct ib_header *rhdr = packet->hdr; u32 rte = rhf_rcv_type_err(packet->rhf); int lnh = be16_to_cpu(rhdr->lrh[0]) & 3; - struct hfi1_ibport *ibp = &ppd->ibport_data; + struct hfi1_ibport *ibp = rcd_to_iport(rcd); struct hfi1_devdata *dd = ppd->dd; struct rvt_dev_info *rdi = &dd->verbs_dev.rdi; @@ -594,7 +599,7 @@ static void __prescan_rxq(struct hfi1_packet *packet) while (1) { struct hfi1_devdata *dd = rcd->dd; - struct hfi1_ibport *ibp = &rcd->ppd->ibport_data; + struct hfi1_ibport *ibp = rcd_to_iport(rcd); __le32 *rhf_addr = (__le32 *)rcd->rcvhdrq + mdata.ps_head + dd->rhf_offset; struct rvt_qp *qp; @@ -654,24 +659,68 @@ next: } } -static inline int skip_rcv_packet(struct hfi1_packet *packet, int thread) +static void process_rcv_qp_work(struct hfi1_ctxtdata *rcd) +{ + struct rvt_qp *qp, *nqp; + + /* + * Iterate over all QPs waiting to respond. + * The list won't change since the IRQ is only run on one CPU. + */ + list_for_each_entry_safe(qp, nqp, &rcd->qp_wait_list, rspwait) { + list_del_init(&qp->rspwait); + if (qp->r_flags & RVT_R_RSP_NAK) { + qp->r_flags &= ~RVT_R_RSP_NAK; + hfi1_send_rc_ack(rcd, qp, 0); + } + if (qp->r_flags & RVT_R_RSP_SEND) { + unsigned long flags; + + qp->r_flags &= ~RVT_R_RSP_SEND; + spin_lock_irqsave(&qp->s_lock, flags); + if (ib_rvt_state_ops[qp->state] & + RVT_PROCESS_OR_FLUSH_SEND) + hfi1_schedule_send(qp); + spin_unlock_irqrestore(&qp->s_lock, flags); + } + rvt_put_qp(qp); + } +} + +static noinline int max_packet_exceeded(struct hfi1_packet *packet, int thread) +{ + if (thread) { + if ((packet->numpkt & (MAX_PKT_RECV_THREAD - 1)) == 0) + /* allow defered processing */ + process_rcv_qp_work(packet->rcd); + cond_resched(); + return RCV_PKT_OK; + } else { + this_cpu_inc(*packet->rcd->dd->rcv_limit); + return RCV_PKT_LIMIT; + } +} + +static inline int check_max_packet(struct hfi1_packet *packet, int thread) { int ret = RCV_PKT_OK; + if (unlikely((packet->numpkt & (MAX_PKT_RECV - 1)) == 0)) + ret = max_packet_exceeded(packet, thread); + return ret; +} + +static noinline int skip_rcv_packet(struct hfi1_packet *packet, int thread) +{ + int ret; + /* Set up for the next packet */ packet->rhqoff += packet->rsize; if (packet->rhqoff >= packet->maxcnt) packet->rhqoff = 0; packet->numpkt++; - if (unlikely((packet->numpkt & (MAX_PKT_RECV - 1)) == 0)) { - if (thread) { - cond_resched(); - } else { - ret = RCV_PKT_LIMIT; - this_cpu_inc(*packet->rcd->dd->rcv_limit); - } - } + ret = check_max_packet(packet, thread); packet->rhf_addr = (__le32 *)packet->rcd->rcvhdrq + packet->rhqoff + packet->rcd->dd->rhf_offset; @@ -682,7 +731,7 @@ static inline int skip_rcv_packet(struct hfi1_packet *packet, int thread) static inline int process_rcv_packet(struct hfi1_packet *packet, int thread) { - int ret = RCV_PKT_OK; + int ret; packet->hdr = hfi1_get_msgheader(packet->rcd->dd, packet->rhf_addr); @@ -723,14 +772,7 @@ static inline int process_rcv_packet(struct hfi1_packet *packet, int thread) if (packet->rhqoff >= packet->maxcnt) packet->rhqoff = 0; - if (unlikely((packet->numpkt & (MAX_PKT_RECV - 1)) == 0)) { - if (thread) { - cond_resched(); - } else { - ret = RCV_PKT_LIMIT; - this_cpu_inc(*packet->rcd->dd->rcv_limit); - } - } + ret = check_max_packet(packet, thread); packet->rhf_addr = (__le32 *)packet->rcd->rcvhdrq + packet->rhqoff + packet->rcd->dd->rhf_offset; @@ -767,38 +809,6 @@ static inline void finish_packet(struct hfi1_packet *packet) packet->etail, rcv_intr_dynamic, packet->numpkt); } -static inline void process_rcv_qp_work(struct hfi1_packet *packet) -{ - struct hfi1_ctxtdata *rcd; - struct rvt_qp *qp, *nqp; - - rcd = packet->rcd; - rcd->head = packet->rhqoff; - - /* - * Iterate over all QPs waiting to respond. - * The list won't change since the IRQ is only run on one CPU. - */ - list_for_each_entry_safe(qp, nqp, &rcd->qp_wait_list, rspwait) { - list_del_init(&qp->rspwait); - if (qp->r_flags & RVT_R_RSP_NAK) { - qp->r_flags &= ~RVT_R_RSP_NAK; - hfi1_send_rc_ack(rcd, qp, 0); - } - if (qp->r_flags & RVT_R_RSP_SEND) { - unsigned long flags; - - qp->r_flags &= ~RVT_R_RSP_SEND; - spin_lock_irqsave(&qp->s_lock, flags); - if (ib_rvt_state_ops[qp->state] & - RVT_PROCESS_OR_FLUSH_SEND) - hfi1_schedule_send(qp); - spin_unlock_irqrestore(&qp->s_lock, flags); - } - rvt_put_qp(qp); - } -} - /* * Handle receive interrupts when using the no dma rtail option. */ @@ -826,7 +836,8 @@ int handle_receive_interrupt_nodma_rtail(struct hfi1_ctxtdata *rcd, int thread) last = RCV_PKT_DONE; process_rcv_update(last, &packet); } - process_rcv_qp_work(&packet); + process_rcv_qp_work(rcd); + rcd->head = packet.rhqoff; bail: finish_packet(&packet); return last; @@ -854,7 +865,8 @@ int handle_receive_interrupt_dma_rtail(struct hfi1_ctxtdata *rcd, int thread) last = RCV_PKT_DONE; process_rcv_update(last, &packet); } - process_rcv_qp_work(&packet); + process_rcv_qp_work(rcd); + rcd->head = packet.rhqoff; bail: finish_packet(&packet); return last; @@ -1024,7 +1036,8 @@ int handle_receive_interrupt(struct hfi1_ctxtdata *rcd, int thread) process_rcv_update(last, &packet); } - process_rcv_qp_work(&packet); + process_rcv_qp_work(rcd); + rcd->head = packet.rhqoff; bail: /* diff --git a/drivers/infiniband/hw/hfi1/efivar.c b/drivers/infiniband/hw/hfi1/efivar.c index 106349fc1fb9..d106d23016ba 100644 --- a/drivers/infiniband/hw/hfi1/efivar.c +++ b/drivers/infiniband/hw/hfi1/efivar.c @@ -45,6 +45,7 @@ * */ +#include <linux/ctype.h> #include "efivar.h" /* GUID for HFI1 variables in EFI */ @@ -150,15 +151,32 @@ fail: int read_hfi1_efi_var(struct hfi1_devdata *dd, const char *kind, unsigned long *size, void **return_data) { + char prefix_name[64]; char name[64]; + int result; + int i; /* create a common prefix */ - snprintf(name, sizeof(name), "%04x:%02x:%02x.%x-%s", + snprintf(prefix_name, sizeof(prefix_name), "%04x:%02x:%02x.%x", pci_domain_nr(dd->pcidev->bus), dd->pcidev->bus->number, PCI_SLOT(dd->pcidev->devfn), - PCI_FUNC(dd->pcidev->devfn), - kind); + PCI_FUNC(dd->pcidev->devfn)); + snprintf(name, sizeof(name), "%s-%s", prefix_name, kind); + result = read_efi_var(name, size, return_data); + + /* + * If reading the lowercase EFI variable fail, read the uppercase + * variable. + */ + if (result) { + /* Converting to uppercase */ + for (i = 0; prefix_name[i]; i++) + if (isalpha(prefix_name[i])) + prefix_name[i] = toupper(prefix_name[i]); + snprintf(name, sizeof(name), "%s-%s", prefix_name, kind); + result = read_efi_var(name, size, return_data); + } - return read_efi_var(name, size, return_data); + return result; } diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index bd786b7bd30b..f78c739b330a 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -48,6 +48,7 @@ #include <linux/cdev.h> #include <linux/vmalloc.h> #include <linux/io.h> +#include <linux/sched/mm.h> #include <rdma/ib.h> @@ -92,7 +93,7 @@ static unsigned int poll_next(struct file *, struct poll_table_struct *); static int user_event_ack(struct hfi1_ctxtdata *, int, unsigned long); static int set_ctxt_pkey(struct hfi1_ctxtdata *, unsigned, u16); static int manage_rcvq(struct hfi1_ctxtdata *, unsigned, int); -static int vma_fault(struct vm_area_struct *, struct vm_fault *); +static int vma_fault(struct vm_fault *); static long hfi1_file_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); @@ -185,7 +186,7 @@ static int hfi1_file_open(struct inode *inode, struct file *fp) if (fd) { fd->rec_cpu_num = -1; /* no cpu affinity by default */ fd->mm = current->mm; - atomic_inc(&fd->mm->mm_count); + mmgrab(fd->mm); fp->private_data = fd; } else { fp->private_data = NULL; @@ -695,7 +696,7 @@ done: * Local (non-chip) user memory is not mapped right away but as it is * accessed by the user-level code. */ -static int vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int vma_fault(struct vm_fault *vmf) { struct page *page; diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 751a0fb29fa5..0808e3c3ba39 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -356,12 +356,11 @@ struct hfi1_packet { u64 rhf; u32 maxcnt; u32 rhqoff; - u32 hdrqtail; - int numpkt; u16 tlen; - u16 hlen; s16 etail; - u16 rsize; + u8 hlen; + u8 numpkt; + u8 rsize; u8 updegr; u8 rcv_flags; u8 etype; @@ -1584,6 +1583,11 @@ static inline struct hfi1_ibport *to_iport(struct ib_device *ibdev, u8 port) return &dd->pport[pidx].ibport_data; } +static inline struct hfi1_ibport *rcd_to_iport(struct hfi1_ctxtdata *rcd) +{ + return &rcd->ppd->ibport_data; +} + void hfi1_process_ecn_slowpath(struct rvt_qp *qp, struct hfi1_packet *pkt, bool do_cnp); static inline bool process_ecn(struct rvt_qp *qp, struct hfi1_packet *pkt, @@ -1793,8 +1797,6 @@ int kdeth_process_expected(struct hfi1_packet *packet); int kdeth_process_eager(struct hfi1_packet *packet); int process_receive_invalid(struct hfi1_packet *packet); -void update_sge(struct rvt_sge_state *ss, u32 length); - /* global module parameter variables */ extern unsigned int hfi1_max_mtu; extern unsigned int hfi1_cu; @@ -1940,6 +1942,10 @@ static inline u64 hfi1_pkt_base_sdma_integrity(struct hfi1_devdata *dd) dev_info(&(dd)->pcidev->dev, "%s: " fmt, \ get_unit_name((dd)->unit), ##__VA_ARGS__) +#define dd_dev_info_ratelimited(dd, fmt, ...) \ + dev_info_ratelimited(&(dd)->pcidev->dev, "%s: " fmt, \ + get_unit_name((dd)->unit), ##__VA_ARGS__) + #define dd_dev_dbg(dd, fmt, ...) \ dev_dbg(&(dd)->pcidev->dev, "%s: " fmt, \ get_unit_name((dd)->unit), ##__VA_ARGS__) diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c index e3b5bc93bc70..f40864e9a3b2 100644 --- a/drivers/infiniband/hw/hfi1/init.c +++ b/drivers/infiniband/hw/hfi1/init.c @@ -297,14 +297,15 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u32 ctxt, * The resulting value will be rounded down to the closest * multiple of dd->rcv_entries.group_size. */ - rcd->egrbufs.buffers = kcalloc(rcd->egrbufs.count, - sizeof(*rcd->egrbufs.buffers), - GFP_KERNEL); + rcd->egrbufs.buffers = kzalloc_node( + rcd->egrbufs.count * sizeof(*rcd->egrbufs.buffers), + GFP_KERNEL, numa); if (!rcd->egrbufs.buffers) goto bail; - rcd->egrbufs.rcvtids = kcalloc(rcd->egrbufs.count, - sizeof(*rcd->egrbufs.rcvtids), - GFP_KERNEL); + rcd->egrbufs.rcvtids = kzalloc_node( + rcd->egrbufs.count * + sizeof(*rcd->egrbufs.rcvtids), + GFP_KERNEL, numa); if (!rcd->egrbufs.rcvtids) goto bail; rcd->egrbufs.size = eager_buffer_size; @@ -322,8 +323,8 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u32 ctxt, rcd->egrbufs.rcvtid_size = HFI1_MAX_EAGER_BUFFER_SIZE; if (ctxt < dd->first_user_ctxt) { /* N/A for PSM contexts */ - rcd->opstats = kzalloc(sizeof(*rcd->opstats), - GFP_KERNEL); + rcd->opstats = kzalloc_node(sizeof(*rcd->opstats), + GFP_KERNEL, numa); if (!rcd->opstats) goto bail; } diff --git a/drivers/infiniband/hw/hfi1/mad.c b/drivers/infiniband/hw/hfi1/mad.c index 6e595afca24c..09cda3c35e82 100644 --- a/drivers/infiniband/hw/hfi1/mad.c +++ b/drivers/infiniband/hw/hfi1/mad.c @@ -4406,7 +4406,7 @@ int hfi1_process_mad(struct ib_device *ibdev, int mad_flags, u8 port, switch (in_mad->base_version) { case OPA_MGMT_BASE_VERSION: if (unlikely(in_mad_size != sizeof(struct opa_mad))) { - dev_err(ibdev->dma_device, "invalid in_mad_size\n"); + dev_err(ibdev->dev.parent, "invalid in_mad_size\n"); return IB_MAD_RESULT_FAILURE; } return hfi1_process_opa_mad(ibdev, mad_flags, port, diff --git a/drivers/infiniband/hw/hfi1/pcie.c b/drivers/infiniband/hw/hfi1/pcie.c index 4ac8f330c5cb..0829fce06172 100644 --- a/drivers/infiniband/hw/hfi1/pcie.c +++ b/drivers/infiniband/hw/hfi1/pcie.c @@ -598,15 +598,6 @@ pci_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_CAN_RECOVER; } -static pci_ers_result_t -pci_link_reset(struct pci_dev *pdev) -{ - struct hfi1_devdata *dd = pci_get_drvdata(pdev); - - dd_dev_info(dd, "HFI1 link_reset function called, ignored\n"); - return PCI_ERS_RESULT_CAN_RECOVER; -} - static void pci_resume(struct pci_dev *pdev) { @@ -625,7 +616,6 @@ pci_resume(struct pci_dev *pdev) const struct pci_error_handlers hfi1_pci_err_handler = { .error_detected = pci_error_detected, .mmio_enabled = pci_mmio_enabled, - .link_reset = pci_link_reset, .slot_reset = pci_slot_reset, .resume = pci_resume, }; @@ -673,12 +663,12 @@ MODULE_PARM_DESC(pcie_retry, "Driver will try this many times to reach requested #define UNSET_PSET 255 #define DEFAULT_DISCRETE_PSET 2 /* discrete HFI */ -#define DEFAULT_MCP_PSET 4 /* MCP HFI */ +#define DEFAULT_MCP_PSET 6 /* MCP HFI */ static uint pcie_pset = UNSET_PSET; module_param(pcie_pset, uint, S_IRUGO); MODULE_PARM_DESC(pcie_pset, "PCIe Eq Pset value to use, range is 0-10"); -static uint pcie_ctle = 1; /* discrete on, integrated off */ +static uint pcie_ctle = 3; /* discrete on, integrated on */ module_param(pcie_ctle, uint, S_IRUGO); MODULE_PARM_DESC(pcie_ctle, "PCIe static CTLE mode, bit 0 - discrete on/off, bit 1 - integrated on/off"); diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index d752d6768a49..c4ebd097b20f 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -79,43 +79,6 @@ static inline unsigned mk_qpn(struct rvt_qpn_table *qpt, return (map - qpt->map) * RVT_BITS_PER_PAGE + off; } -/* - * Convert the AETH credit code into the number of credits. - */ -static const u16 credit_table[31] = { - 0, /* 0 */ - 1, /* 1 */ - 2, /* 2 */ - 3, /* 3 */ - 4, /* 4 */ - 6, /* 5 */ - 8, /* 6 */ - 12, /* 7 */ - 16, /* 8 */ - 24, /* 9 */ - 32, /* A */ - 48, /* B */ - 64, /* C */ - 96, /* D */ - 128, /* E */ - 192, /* F */ - 256, /* 10 */ - 384, /* 11 */ - 512, /* 12 */ - 768, /* 13 */ - 1024, /* 14 */ - 1536, /* 15 */ - 2048, /* 16 */ - 3072, /* 17 */ - 4096, /* 18 */ - 6144, /* 19 */ - 8192, /* 1A */ - 12288, /* 1B */ - 16384, /* 1C */ - 24576, /* 1D */ - 32768 /* 1E */ -}; - const struct rvt_operation_params hfi1_post_parms[RVT_OPERATION_MAX] = { [IB_WR_RDMA_WRITE] = { .length = sizeof(struct ib_rdma_wr), @@ -340,68 +303,6 @@ int hfi1_check_send_wqe(struct rvt_qp *qp, } /** - * hfi1_compute_aeth - compute the AETH (syndrome + MSN) - * @qp: the queue pair to compute the AETH for - * - * Returns the AETH. - */ -__be32 hfi1_compute_aeth(struct rvt_qp *qp) -{ - u32 aeth = qp->r_msn & HFI1_MSN_MASK; - - if (qp->ibqp.srq) { - /* - * Shared receive queues don't generate credits. - * Set the credit field to the invalid value. - */ - aeth |= HFI1_AETH_CREDIT_INVAL << HFI1_AETH_CREDIT_SHIFT; - } else { - u32 min, max, x; - u32 credits; - struct rvt_rwq *wq = qp->r_rq.wq; - u32 head; - u32 tail; - - /* sanity check pointers before trusting them */ - head = wq->head; - if (head >= qp->r_rq.size) - head = 0; - tail = wq->tail; - if (tail >= qp->r_rq.size) - tail = 0; - /* - * Compute the number of credits available (RWQEs). - * There is a small chance that the pair of reads are - * not atomic, which is OK, since the fuzziness is - * resolved as further ACKs go out. - */ - credits = head - tail; - if ((int)credits < 0) - credits += qp->r_rq.size; - /* - * Binary search the credit table to find the code to - * use. - */ - min = 0; - max = 31; - for (;;) { - x = (min + max) / 2; - if (credit_table[x] == credits) - break; - if (credit_table[x] > credits) { - max = x; - } else { - if (min == x) - break; - min = x; - } - } - aeth |= x << HFI1_AETH_CREDIT_SHIFT; - } - return cpu_to_be32(aeth); -} - -/** * _hfi1_schedule_send - schedule progress * @qp: the QP * @@ -457,44 +358,6 @@ void hfi1_schedule_send(struct rvt_qp *qp) _hfi1_schedule_send(qp); } -/** - * hfi1_get_credit - handle credit in aeth - * @qp: the qp - * @aeth: the Acknowledge Extended Transport Header - * - * The QP s_lock should be held. - */ -void hfi1_get_credit(struct rvt_qp *qp, u32 aeth) -{ - u32 credit = (aeth >> HFI1_AETH_CREDIT_SHIFT) & HFI1_AETH_CREDIT_MASK; - - lockdep_assert_held(&qp->s_lock); - /* - * If the credit is invalid, we can send - * as many packets as we like. Otherwise, we have to - * honor the credit field. - */ - if (credit == HFI1_AETH_CREDIT_INVAL) { - if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) { - qp->s_flags |= RVT_S_UNLIMITED_CREDIT; - if (qp->s_flags & RVT_S_WAIT_SSN_CREDIT) { - qp->s_flags &= ~RVT_S_WAIT_SSN_CREDIT; - hfi1_schedule_send(qp); - } - } - } else if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) { - /* Compute new LSN (i.e., MSN + credit) */ - credit = (aeth + credit_table[credit]) & HFI1_MSN_MASK; - if (cmp_msn(credit, qp->s_lsn) > 0) { - qp->s_lsn = credit; - if (qp->s_flags & RVT_S_WAIT_SSN_CREDIT) { - qp->s_flags &= ~RVT_S_WAIT_SSN_CREDIT; - hfi1_schedule_send(qp); - } - } - } -} - void hfi1_qp_wakeup(struct rvt_qp *qp, u32 flag) { unsigned long flags; @@ -744,7 +607,7 @@ void qp_iter_print(struct seq_file *s, struct qp_iter *iter) wqe = rvt_get_swqe_ptr(qp, qp->s_last); send_context = qp_to_send_context(qp, priv->s_sc); seq_printf(s, - "N %d %s QP %x R %u %s %u %u %u f=%x %u %u %u %u %u %u PSN %x %x %x %x %x (%u %u %u %u %u %u %u) RQP %x LID %x SL %u MTU %u %u %u %u SDE %p,%u SC %p,%u SCQ %u %u PID %d\n", + "N %d %s QP %x R %u %s %u %u %u f=%x %u %u %u %u %u %u SPSN %x %x %x %x %x RPSN %x (%u %u %u %u %u %u %u) RQP %x LID %x SL %u MTU %u %u %u %u %u SDE %p,%u SC %p,%u SCQ %u %u PID %d\n", iter->n, qp_idle(qp) ? "I" : "B", qp->ibqp.qp_num, @@ -763,6 +626,7 @@ void qp_iter_print(struct seq_file *s, struct qp_iter *iter) qp->s_last_psn, qp->s_psn, qp->s_next_psn, qp->s_sending_psn, qp->s_sending_hpsn, + qp->r_psn, qp->s_last, qp->s_acked, qp->s_cur, qp->s_tail, qp->s_head, qp->s_size, qp->s_avail, @@ -773,6 +637,7 @@ void qp_iter_print(struct seq_file *s, struct qp_iter *iter) qp->s_retry, qp->s_retry_cnt, qp->s_rnr_retry_cnt, + qp->s_rnr_retry, sde, sde ? sde->this_idx : 0, send_context, @@ -782,19 +647,6 @@ void qp_iter_print(struct seq_file *s, struct qp_iter *iter) qp->pid); } -void qp_comm_est(struct rvt_qp *qp) -{ - qp->r_flags |= RVT_R_COMM_EST; - if (qp->ibqp.event_handler) { - struct ib_event ev; - - ev.device = qp->ibqp.device; - ev.element.qp = &qp->ibqp; - ev.event = IB_EVENT_COMM_EST; - qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); - } -} - void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp, gfp_t gfp) { @@ -819,8 +671,6 @@ void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp, iowait_sleep, iowait_wakeup, iowait_sdma_drained); - setup_timer(&priv->s_rnr_timer, hfi1_rc_rnr_retry, (unsigned long)qp); - qp->s_timer.function = hfi1_rc_timeout; return priv; } @@ -861,7 +711,6 @@ void flush_qp_waiters(struct rvt_qp *qp) { lockdep_assert_held(&qp->s_lock); flush_iowait(qp); - hfi1_stop_rc_timers(qp); } void stop_send_queue(struct rvt_qp *qp) @@ -869,7 +718,6 @@ void stop_send_queue(struct rvt_qp *qp) struct hfi1_qp_priv *priv = qp->priv; cancel_work_sync(&priv->s_iowait.iowork); - hfi1_del_timers_sync(qp); } void quiesce_qp(struct rvt_qp *qp) @@ -961,17 +809,20 @@ int get_pmtu_from_attr(struct rvt_dev_info *rdi, struct rvt_qp *qp, void notify_error_qp(struct rvt_qp *qp) { - struct hfi1_ibdev *dev = to_idev(qp->ibqp.device); struct hfi1_qp_priv *priv = qp->priv; + seqlock_t *lock = priv->s_iowait.lock; - write_seqlock(&dev->iowait_lock); - if (!list_empty(&priv->s_iowait.list) && !(qp->s_flags & RVT_S_BUSY)) { - qp->s_flags &= ~RVT_S_ANY_WAIT_IO; - list_del_init(&priv->s_iowait.list); - priv->s_iowait.lock = NULL; - rvt_put_qp(qp); + if (lock) { + write_seqlock(lock); + if (!list_empty(&priv->s_iowait.list) && + !(qp->s_flags & RVT_S_BUSY)) { + qp->s_flags &= ~RVT_S_ANY_WAIT_IO; + list_del_init(&priv->s_iowait.list); + priv->s_iowait.lock = NULL; + rvt_put_qp(qp); + } + write_sequnlock(lock); } - write_sequnlock(&dev->iowait_lock); if (!(qp->s_flags & RVT_S_BUSY)) { qp->s_hdrwords = 0; diff --git a/drivers/infiniband/hw/hfi1/qp.h b/drivers/infiniband/hw/hfi1/qp.h index 587d84d65bb8..1eb9cd7b8c19 100644 --- a/drivers/infiniband/hw/hfi1/qp.h +++ b/drivers/infiniband/hw/hfi1/qp.h @@ -71,14 +71,6 @@ static inline void clear_ahg(struct rvt_qp *qp) } /** - * hfi1_compute_aeth - compute the AETH (syndrome + MSN) - * @qp: the queue pair to compute the AETH for - * - * Returns the AETH. - */ -__be32 hfi1_compute_aeth(struct rvt_qp *qp); - -/** * hfi1_create_qp - create a queue pair for a device * @ibpd: the protection domain who's device we create the queue pair for * @init_attr: the attributes of the queue pair @@ -91,14 +83,6 @@ __be32 hfi1_compute_aeth(struct rvt_qp *qp); struct ib_qp *hfi1_create_qp(struct ib_pd *ibpd, struct ib_qp_init_attr *init_attr, struct ib_udata *udata); -/** - * hfi1_get_credit - flush the send work queue of a QP - * @qp: the qp who's send work queue to flush - * @aeth: the Acknowledge Extended Transport Header - * - * The QP s_lock should be held. - */ -void hfi1_get_credit(struct rvt_qp *qp, u32 aeth); /** * hfi1_qp_wakeup - wake up on the indicated event @@ -131,12 +115,6 @@ int qp_iter_next(struct qp_iter *iter); */ void qp_iter_print(struct seq_file *s, struct qp_iter *iter); -/** - * qp_comm_est - handle trap with QP established - * @qp: the QP - */ -void qp_comm_est(struct rvt_qp *qp); - void _hfi1_schedule_send(struct rvt_qp *qp); void hfi1_schedule_send(struct rvt_qp *qp); diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c index 809b26eb6d3c..7382be11afca 100644 --- a/drivers/infiniband/hw/hfi1/rc.c +++ b/drivers/infiniband/hw/hfi1/rc.c @@ -57,133 +57,6 @@ /* cut down ridiculously long IB macro names */ #define OP(x) RC_OP(x) -/** - * hfi1_add_retry_timer - add/start a retry timer - * @qp - the QP - * - * add a retry timer on the QP - */ -static inline void hfi1_add_retry_timer(struct rvt_qp *qp) -{ - struct ib_qp *ibqp = &qp->ibqp; - struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); - - lockdep_assert_held(&qp->s_lock); - qp->s_flags |= RVT_S_TIMER; - /* 4.096 usec. * (1 << qp->timeout) */ - qp->s_timer.expires = jiffies + qp->timeout_jiffies + - rdi->busy_jiffies; - add_timer(&qp->s_timer); -} - -/** - * hfi1_add_rnr_timer - add/start an rnr timer - * @qp - the QP - * @to - timeout in usecs - * - * add an rnr timer on the QP - */ -void hfi1_add_rnr_timer(struct rvt_qp *qp, u32 to) -{ - struct hfi1_qp_priv *priv = qp->priv; - - lockdep_assert_held(&qp->s_lock); - qp->s_flags |= RVT_S_WAIT_RNR; - priv->s_rnr_timer.expires = jiffies + usecs_to_jiffies(to); - add_timer(&priv->s_rnr_timer); -} - -/** - * hfi1_mod_retry_timer - mod a retry timer - * @qp - the QP - * - * Modify a potentially already running retry - * timer - */ -static inline void hfi1_mod_retry_timer(struct rvt_qp *qp) -{ - struct ib_qp *ibqp = &qp->ibqp; - struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); - - lockdep_assert_held(&qp->s_lock); - qp->s_flags |= RVT_S_TIMER; - /* 4.096 usec. * (1 << qp->timeout) */ - mod_timer(&qp->s_timer, jiffies + qp->timeout_jiffies + - rdi->busy_jiffies); -} - -/** - * hfi1_stop_retry_timer - stop a retry timer - * @qp - the QP - * - * stop a retry timer and return if the timer - * had been pending. - */ -static inline int hfi1_stop_retry_timer(struct rvt_qp *qp) -{ - int rval = 0; - - lockdep_assert_held(&qp->s_lock); - /* Remove QP from retry */ - if (qp->s_flags & RVT_S_TIMER) { - qp->s_flags &= ~RVT_S_TIMER; - rval = del_timer(&qp->s_timer); - } - return rval; -} - -/** - * hfi1_stop_rc_timers - stop all timers - * @qp - the QP - * - * stop any pending timers - */ -void hfi1_stop_rc_timers(struct rvt_qp *qp) -{ - struct hfi1_qp_priv *priv = qp->priv; - - lockdep_assert_held(&qp->s_lock); - /* Remove QP from all timers */ - if (qp->s_flags & (RVT_S_TIMER | RVT_S_WAIT_RNR)) { - qp->s_flags &= ~(RVT_S_TIMER | RVT_S_WAIT_RNR); - del_timer(&qp->s_timer); - del_timer(&priv->s_rnr_timer); - } -} - -/** - * hfi1_stop_rnr_timer - stop an rnr timer - * @qp - the QP - * - * stop an rnr timer and return if the timer - * had been pending. - */ -static inline int hfi1_stop_rnr_timer(struct rvt_qp *qp) -{ - int rval = 0; - struct hfi1_qp_priv *priv = qp->priv; - - lockdep_assert_held(&qp->s_lock); - /* Remove QP from rnr timer */ - if (qp->s_flags & RVT_S_WAIT_RNR) { - qp->s_flags &= ~RVT_S_WAIT_RNR; - rval = del_timer(&priv->s_rnr_timer); - } - return rval; -} - -/** - * hfi1_del_timers_sync - wait for any timeout routines to exit - * @qp - the QP - */ -void hfi1_del_timers_sync(struct rvt_qp *qp) -{ - struct hfi1_qp_priv *priv = qp->priv; - - del_timer_sync(&qp->s_timer); - del_timer_sync(&priv->s_rnr_timer); -} - static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, u32 psn, u32 pmtu) { @@ -194,7 +67,7 @@ static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, ss->sg_list = wqe->sg_list + 1; ss->num_sge = wqe->wr.num_sge; ss->total_len = wqe->length; - hfi1_skip_sge(ss, len, 0); + rvt_skip_sge(ss, len, false); return wqe->length - len; } @@ -284,7 +157,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); e->sent = 1; } - ohdr->u.aeth = hfi1_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); hwords++; qp->s_ack_rdma_psn = e->psn; bth2 = mask_psn(qp->s_ack_rdma_psn++); @@ -293,7 +166,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, ps->s_txreq->ss = NULL; len = 0; qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE); - ohdr->u.at.aeth = hfi1_compute_aeth(qp); + ohdr->u.at.aeth = rvt_compute_aeth(qp); ib_u64_put(e->atomic_data, &ohdr->u.at.atomic_ack_eth); hwords += sizeof(ohdr->u.at) / sizeof(u32); bth2 = mask_psn(e->psn); @@ -315,7 +188,7 @@ static int make_rc_ack(struct hfi1_ibdev *dev, struct rvt_qp *qp, len = pmtu; middle = HFI1_CAP_IS_KSET(SDMA_AHG); } else { - ohdr->u.aeth = hfi1_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); hwords++; qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); e = &qp->s_ack_queue[qp->s_tail_ack_queue]; @@ -338,11 +211,11 @@ normal: ps->s_txreq->ss = NULL; if (qp->s_nak_state) ohdr->u.aeth = - cpu_to_be32((qp->r_msn & HFI1_MSN_MASK) | + cpu_to_be32((qp->r_msn & IB_MSN_MASK) | (qp->s_nak_state << - HFI1_AETH_CREDIT_SHIFT)); + IB_AETH_CREDIT_SHIFT)); else - ohdr->u.aeth = hfi1_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); hwords++; len = 0; bth0 = OP(ACKNOWLEDGE) << 24; @@ -414,7 +287,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) goto bail; /* We are in the error state, flush the work request. */ smp_read_barrier_depends(); /* see post_one_send() */ - if (qp->s_last == ACCESS_ONCE(qp->s_head)) + if (qp->s_last == READ_ONCE(qp->s_head)) goto bail; /* If DMAs are in progress, we can't flush immediately. */ if (iowait_sdma_pending(&priv->s_iowait)) { @@ -457,7 +330,8 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) newreq = 0; if (qp->s_cur == qp->s_tail) { /* Check if send work queue is empty. */ - if (qp->s_tail == qp->s_head) { + smp_read_barrier_depends(); /* see post_one_send() */ + if (qp->s_tail == READ_ONCE(qp->s_head)) { clear_ahg(qp); goto bail; } @@ -518,7 +392,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) case IB_WR_SEND_WITH_INV: /* If no credit, return. */ if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) && - cmp_msn(wqe->ssn, qp->s_lsn + 1) > 0) { + rvt_cmp_msn(wqe->ssn, qp->s_lsn + 1) > 0) { qp->s_flags |= RVT_S_WAIT_SSN_CREDIT; goto bail; } @@ -555,7 +429,7 @@ int hfi1_make_rc_req(struct rvt_qp *qp, struct hfi1_pkt_state *ps) case IB_WR_RDMA_WRITE_WITH_IMM: /* If no credit, return. */ if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) && - cmp_msn(wqe->ssn, qp->s_lsn + 1) > 0) { + rvt_cmp_msn(wqe->ssn, qp->s_lsn + 1) > 0) { qp->s_flags |= RVT_S_WAIT_SSN_CREDIT; goto bail; } @@ -840,7 +714,7 @@ bail_no_tx: void hfi1_send_rc_ack(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp, int is_fecn) { - struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); + struct hfi1_ibport *ibp = rcd_to_iport(rcd); struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); u64 pbc, pbc_flags = 0; u16 lrh0; @@ -853,6 +727,10 @@ void hfi1_send_rc_ack(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp, struct ib_header hdr; struct ib_other_headers *ohdr; unsigned long flags; + struct hfi1_qp_priv *priv = qp->priv; + + /* clear the defer count */ + priv->r_adefered = 0; /* Don't send ACK or NAK if a RDMA read or atomic is pending. */ if (qp->s_flags & RVT_S_RESP_PENDING) @@ -880,11 +758,11 @@ void hfi1_send_rc_ack(struct hfi1_ctxtdata *rcd, struct rvt_qp *qp, if (qp->s_mig_state == IB_MIG_MIGRATED) bth0 |= IB_BTH_MIG_REQ; if (qp->r_nak_state) - ohdr->u.aeth = cpu_to_be32((qp->r_msn & HFI1_MSN_MASK) | + ohdr->u.aeth = cpu_to_be32((qp->r_msn & IB_MSN_MASK) | (qp->r_nak_state << - HFI1_AETH_CREDIT_SHIFT)); + IB_AETH_CREDIT_SHIFT)); else - ohdr->u.aeth = hfi1_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); sc5 = ibp->sl_to_sc[qp->remote_ah_attr.sl]; /* set PBC_DC_INFO bit (aka SC[4]) in pbc_flags */ pbc_flags |= ((!!(sc5 & 0x10)) << PBC_DC_INFO_SHIFT); @@ -1038,7 +916,7 @@ done: * Back up requester to resend the last un-ACKed request. * The QP r_lock and s_lock should be held and interrupts disabled. */ -static void restart_rc(struct rvt_qp *qp, u32 psn, int wait) +void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait) { struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, qp->s_acked); struct hfi1_ibport *ibp; @@ -1075,44 +953,6 @@ static void restart_rc(struct rvt_qp *qp, u32 psn, int wait) } /* - * This is called from s_timer for missing responses. - */ -void hfi1_rc_timeout(unsigned long arg) -{ - struct rvt_qp *qp = (struct rvt_qp *)arg; - struct hfi1_ibport *ibp; - unsigned long flags; - - spin_lock_irqsave(&qp->r_lock, flags); - spin_lock(&qp->s_lock); - if (qp->s_flags & RVT_S_TIMER) { - ibp = to_iport(qp->ibqp.device, qp->port_num); - ibp->rvp.n_rc_timeouts++; - qp->s_flags &= ~RVT_S_TIMER; - del_timer(&qp->s_timer); - trace_hfi1_timeout(qp, qp->s_last_psn + 1); - restart_rc(qp, qp->s_last_psn + 1, 1); - hfi1_schedule_send(qp); - } - spin_unlock(&qp->s_lock); - spin_unlock_irqrestore(&qp->r_lock, flags); -} - -/* - * This is called from s_timer for RNR timeouts. - */ -void hfi1_rc_rnr_retry(unsigned long arg) -{ - struct rvt_qp *qp = (struct rvt_qp *)arg; - unsigned long flags; - - spin_lock_irqsave(&qp->s_lock, flags); - hfi1_stop_rnr_timer(qp); - hfi1_schedule_send(qp); - spin_unlock_irqrestore(&qp->s_lock, flags); -} - -/* * Set qp->s_sending_psn to the next PSN after the given one. * This would be psn+1 except when RDMA reads are present. */ @@ -1150,7 +990,7 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct ib_header *hdr) u32 psn; lockdep_assert_held(&qp->s_lock); - if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_OR_FLUSH_SEND)) + if (!(ib_rvt_state_ops[qp->state] & RVT_SEND_OR_FLUSH_OR_RECV_OK)) return; /* Find out where the BTH is */ @@ -1178,7 +1018,7 @@ void hfi1_rc_send_complete(struct rvt_qp *qp, struct ib_header *hdr) !(qp->s_flags & (RVT_S_TIMER | RVT_S_WAIT_RNR | RVT_S_WAIT_PSN)) && (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) - hfi1_add_retry_timer(qp); + rvt_add_retry_timer(qp); while (qp->s_last != qp->s_acked) { u32 s_last; @@ -1308,7 +1148,6 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, int ret = 0; u32 ack_psn; int diff; - unsigned long to; lockdep_assert_held(&qp->s_lock); /* @@ -1318,10 +1157,10 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, * request but will include an ACK'ed request(s). */ ack_psn = psn; - if (aeth >> 29) + if (aeth >> IB_AETH_NAK_SHIFT) ack_psn--; wqe = rvt_get_swqe_ptr(qp, qp->s_acked); - ibp = to_iport(qp->ibqp.device, qp->port_num); + ibp = rcd_to_iport(rcd); /* * The MSN might be for a later WQE than the PSN indicates so @@ -1357,7 +1196,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, /* Retry this request. */ if (!(qp->r_flags & RVT_R_RDMAR_SEQ)) { qp->r_flags |= RVT_R_RDMAR_SEQ; - restart_rc(qp, qp->s_last_psn + 1, 0); + hfi1_restart_rc(qp, qp->s_last_psn + 1, 0); if (list_empty(&qp->rspwait)) { qp->r_flags |= RVT_R_RSP_SEND; rvt_get_qp(qp); @@ -1398,7 +1237,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, break; } - switch (aeth >> 29) { + switch (aeth >> IB_AETH_NAK_SHIFT) { case 0: /* ACK */ this_cpu_inc(*ibp->rvp.rc_acks); if (qp->s_acked != qp->s_tail) { @@ -1406,7 +1245,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, * We are expecting more ACKs so * mod the retry timer. */ - hfi1_mod_retry_timer(qp); + rvt_mod_retry_timer(qp); /* * We can stop re-sending the earlier packets and * continue with the next packet the receiver wants. @@ -1415,7 +1254,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, reset_psn(qp, psn + 1); } else { /* No more acks - kill all timers */ - hfi1_stop_rc_timers(qp); + rvt_stop_rc_timers(qp); if (cmp_psn(qp->s_psn, psn) <= 0) { qp->s_state = OP(SEND_LAST); qp->s_psn = psn + 1; @@ -1425,7 +1264,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, qp->s_flags &= ~RVT_S_WAIT_ACK; hfi1_schedule_send(qp); } - hfi1_get_credit(qp, aeth); + rvt_get_credit(qp, aeth); qp->s_rnr_retry = qp->s_rnr_retry_cnt; qp->s_retry = qp->s_retry_cnt; update_last_psn(qp, psn); @@ -1452,11 +1291,8 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, reset_psn(qp, psn); qp->s_flags &= ~(RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_ACK); - hfi1_stop_rc_timers(qp); - to = - ib_hfi1_rnr_table[(aeth >> HFI1_AETH_CREDIT_SHIFT) & - HFI1_AETH_CREDIT_MASK]; - hfi1_add_rnr_timer(qp, to); + rvt_stop_rc_timers(qp); + rvt_add_rnr_timer(qp, aeth); return 0; case 3: /* NAK */ @@ -1464,8 +1300,8 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, goto bail_stop; /* The last valid PSN is the previous PSN. */ update_last_psn(qp, psn - 1); - switch ((aeth >> HFI1_AETH_CREDIT_SHIFT) & - HFI1_AETH_CREDIT_MASK) { + switch ((aeth >> IB_AETH_CREDIT_SHIFT) & + IB_AETH_CREDIT_MASK) { case 0: /* PSN sequence error */ ibp->rvp.n_seq_naks++; /* @@ -1474,7 +1310,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, * RDMA READ response which terminates the RDMA * READ. */ - restart_rc(qp, psn, 0); + hfi1_restart_rc(qp, psn, 0); hfi1_schedule_send(qp); break; @@ -1513,7 +1349,7 @@ reserved: } /* cannot be reached */ bail_stop: - hfi1_stop_rc_timers(qp); + rvt_stop_rc_timers(qp); return ret; } @@ -1528,7 +1364,7 @@ static void rdma_seq_err(struct rvt_qp *qp, struct hfi1_ibport *ibp, u32 psn, lockdep_assert_held(&qp->s_lock); /* Remove QP from retry timer */ - hfi1_stop_rc_timers(qp); + rvt_stop_rc_timers(qp); wqe = rvt_get_swqe_ptr(qp, qp->s_acked); @@ -1542,7 +1378,7 @@ static void rdma_seq_err(struct rvt_qp *qp, struct hfi1_ibport *ibp, u32 psn, ibp->rvp.n_rdma_seq++; qp->r_flags |= RVT_R_RDMAR_SEQ; - restart_rc(qp, qp->s_last_psn + 1, 0); + hfi1_restart_rc(qp, qp->s_last_psn + 1, 0); if (list_empty(&qp->rspwait)) { qp->r_flags |= RVT_R_RSP_SEND; rvt_get_qp(qp); @@ -1586,7 +1422,7 @@ static void rc_rcv_resp(struct hfi1_ibport *ibp, /* Ignore invalid responses. */ smp_read_barrier_depends(); /* see post_one_send */ - if (cmp_psn(psn, ACCESS_ONCE(qp->s_next_psn)) >= 0) + if (cmp_psn(psn, READ_ONCE(qp->s_next_psn)) >= 0) goto ack_done; /* Ignore duplicate responses. */ @@ -1595,8 +1431,8 @@ static void rc_rcv_resp(struct hfi1_ibport *ibp, /* Update credits for "ghost" ACKs */ if (diff == 0 && opcode == OP(ACKNOWLEDGE)) { aeth = be32_to_cpu(ohdr->u.aeth); - if ((aeth >> 29) == 0) - hfi1_get_credit(qp, aeth); + if ((aeth >> IB_AETH_NAK_SHIFT) == 0) + rvt_get_credit(qp, aeth); } goto ack_done; } @@ -1656,8 +1492,7 @@ read_middle: * We got a response so update the timeout. * 4.096 usec. * (1 << qp->timeout) */ - qp->s_flags |= RVT_S_TIMER; - mod_timer(&qp->s_timer, jiffies + qp->timeout_jiffies); + rvt_mod_retry_timer(qp); if (qp->s_flags & RVT_S_WAIT_ACK) { qp->s_flags &= ~RVT_S_WAIT_ACK; hfi1_schedule_send(qp); @@ -1673,7 +1508,7 @@ read_middle: qp->s_rdma_read_len -= pmtu; update_last_psn(qp, psn); spin_unlock_irqrestore(&qp->s_lock, flags); - hfi1_copy_sge(&qp->s_rdma_read_sge, data, pmtu, 0, 0); + hfi1_copy_sge(&qp->s_rdma_read_sge, data, pmtu, false, false); goto bail; case OP(RDMA_READ_RESPONSE_ONLY): @@ -1717,7 +1552,7 @@ read_last: if (unlikely(tlen != qp->s_rdma_read_len)) goto ack_len_err; aeth = be32_to_cpu(ohdr->u.aeth); - hfi1_copy_sge(&qp->s_rdma_read_sge, data, tlen, 0, 0); + hfi1_copy_sge(&qp->s_rdma_read_sge, data, tlen, false, false); WARN_ON(qp->s_rdma_read_sge.num_sge); (void)do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST), 0, rcd); @@ -1786,7 +1621,7 @@ static noinline int rc_rcv_error(struct ib_other_headers *ohdr, void *data, struct rvt_qp *qp, u32 opcode, u32 psn, int diff, struct hfi1_ctxtdata *rcd) { - struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); + struct hfi1_ibport *ibp = rcd_to_iport(rcd); struct rvt_ack_entry *e; unsigned long flags; u8 i, prev; @@ -1961,25 +1796,6 @@ send_ack: return 0; } -void hfi1_rc_error(struct rvt_qp *qp, enum ib_wc_status err) -{ - unsigned long flags; - int lastwqe; - - spin_lock_irqsave(&qp->s_lock, flags); - lastwqe = rvt_error_qp(qp, err); - spin_unlock_irqrestore(&qp->s_lock, flags); - - if (lastwqe) { - struct ib_event ev; - - ev.device = qp->ibqp.device; - ev.element.qp = &qp->ibqp; - ev.event = IB_EVENT_QP_LAST_WQE_REACHED; - qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); - } -} - static inline void update_ack_queue(struct rvt_qp *qp, unsigned n) { unsigned next; @@ -2095,7 +1911,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) void *data = packet->ebuf; u32 tlen = packet->tlen; struct rvt_qp *qp = packet->qp; - struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); + struct hfi1_ibport *ibp = rcd_to_iport(rcd); struct ib_other_headers *ohdr = packet->ohdr; u32 bth0, opcode; u32 hdrsize = packet->hlen; @@ -2107,7 +1923,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) struct ib_reth *reth; unsigned long flags; int ret, is_fecn = 0; - int copy_last = 0; + bool copy_last = false; u32 rkey; lockdep_assert_held(&qp->r_lock); @@ -2180,7 +1996,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet) } if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) - qp_comm_est(qp); + rvt_comm_est(qp); /* OK, process the packet. */ switch (opcode) { @@ -2201,7 +2017,7 @@ send_middle: qp->r_rcv_len += pmtu; if (unlikely(qp->r_rcv_len > qp->r_len)) goto nack_inv; - hfi1_copy_sge(&qp->r_sge, data, pmtu, 1, 0); + hfi1_copy_sge(&qp->r_sge, data, pmtu, true, false); break; case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE): @@ -2241,7 +2057,7 @@ send_last_inv: wc.wc_flags = IB_WC_WITH_INVALIDATE; goto send_last; case OP(RDMA_WRITE_LAST): - copy_last = ibpd_to_rvtpd(qp->ibqp.pd)->user; + copy_last = rvt_is_user_qp(qp); /* fall through */ case OP(SEND_LAST): no_immediate_data: @@ -2259,7 +2075,7 @@ send_last: wc.byte_len = tlen + qp->r_rcv_len; if (unlikely(wc.byte_len > qp->r_len)) goto nack_inv; - hfi1_copy_sge(&qp->r_sge, data, tlen, 1, copy_last); + hfi1_copy_sge(&qp->r_sge, data, tlen, true, copy_last); rvt_put_ss(&qp->r_sge); qp->r_msn++; if (!__test_and_clear_bit(RVT_R_WRID_VALID, &qp->r_aflags)) @@ -2297,7 +2113,7 @@ send_last: break; case OP(RDMA_WRITE_ONLY): - copy_last = 1; + copy_last = rvt_is_user_qp(qp); /* fall through */ case OP(RDMA_WRITE_FIRST): case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): @@ -2512,7 +2328,7 @@ rnr_nak: return; nack_op_err: - hfi1_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); qp->r_nak_state = IB_NAK_REMOTE_OPERATIONAL_ERROR; qp->r_ack_psn = qp->r_psn; /* Queue NAK for later */ @@ -2522,7 +2338,7 @@ nack_op_err: nack_inv_unlck: spin_unlock_irqrestore(&qp->s_lock, flags); nack_inv: - hfi1_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); qp->r_nak_state = IB_NAK_INVALID_REQUEST; qp->r_ack_psn = qp->r_psn; /* Queue NAK for later */ @@ -2532,7 +2348,7 @@ nack_inv: nack_acc_unlck: spin_unlock_irqrestore(&qp->s_lock, flags); nack_acc: - hfi1_rc_error(qp, IB_WC_LOC_PROT_ERR); + rvt_rc_error(qp, IB_WC_LOC_PROT_ERR); qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; qp->r_ack_psn = qp->r_psn; send_ack: @@ -2547,7 +2363,7 @@ void hfi1_rc_hdrerr( { int has_grh = rcv_flags & HFI1_HAS_GRH; struct ib_other_headers *ohdr; - struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num); + struct hfi1_ibport *ibp = rcd_to_iport(rcd); int diff; u32 opcode; u32 psn, bth0; diff --git a/drivers/infiniband/hw/hfi1/ruc.c b/drivers/infiniband/hw/hfi1/ruc.c index 717ed4b159d3..aa15bcbfb079 100644 --- a/drivers/infiniband/hw/hfi1/ruc.c +++ b/drivers/infiniband/hw/hfi1/ruc.c @@ -54,44 +54,6 @@ #include "trace.h" /* - * Convert the AETH RNR timeout code into the number of microseconds. - */ -const u32 ib_hfi1_rnr_table[32] = { - 655360, /* 00: 655.36 */ - 10, /* 01: .01 */ - 20, /* 02 .02 */ - 30, /* 03: .03 */ - 40, /* 04: .04 */ - 60, /* 05: .06 */ - 80, /* 06: .08 */ - 120, /* 07: .12 */ - 160, /* 08: .16 */ - 240, /* 09: .24 */ - 320, /* 0A: .32 */ - 480, /* 0B: .48 */ - 640, /* 0C: .64 */ - 960, /* 0D: .96 */ - 1280, /* 0E: 1.28 */ - 1920, /* 0F: 1.92 */ - 2560, /* 10: 2.56 */ - 3840, /* 11: 3.84 */ - 5120, /* 12: 5.12 */ - 7680, /* 13: 7.68 */ - 10240, /* 14: 10.24 */ - 15360, /* 15: 15.36 */ - 20480, /* 16: 20.48 */ - 30720, /* 17: 30.72 */ - 40960, /* 18: 40.96 */ - 61440, /* 19: 61.44 */ - 81920, /* 1A: 81.92 */ - 122880, /* 1B: 122.88 */ - 163840, /* 1C: 163.84 */ - 245760, /* 1D: 245.76 */ - 327680, /* 1E: 327.68 */ - 491520 /* 1F: 491.52 */ -}; - -/* * Validate a RWQE and fill in the SGE state. * Return 1 if OK. */ @@ -358,10 +320,9 @@ static void ruc_loopback(struct rvt_qp *sqp) u64 sdata; atomic64_t *maddr; enum ib_wc_status send_status; - int release; + bool release; int ret; - int copy_last = 0; - u32 to; + bool copy_last = false; int local_ops = 0; rcu_read_lock(); @@ -425,7 +386,7 @@ again: memset(&wc, 0, sizeof(wc)); send_status = IB_WC_SUCCESS; - release = 1; + release = true; sqp->s_sge.sge = wqe->sg_list[0]; sqp->s_sge.sg_list = wqe->sg_list + 1; sqp->s_sge.num_sge = wqe->wr.num_sge; @@ -476,7 +437,7 @@ send: /* skip copy_last set and qp_access_flags recheck */ goto do_write; case IB_WR_RDMA_WRITE: - copy_last = ibpd_to_rvtpd(qp->ibqp.pd)->user; + copy_last = rvt_is_user_qp(qp); if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_WRITE))) goto inv_err; do_write: @@ -500,7 +461,7 @@ do_write: wqe->rdma_wr.rkey, IB_ACCESS_REMOTE_READ))) goto acc_err; - release = 0; + release = false; sqp->s_sge.sg_list = NULL; sqp->s_sge.num_sge = 1; qp->r_sge.sge = wqe->sg_list[0]; @@ -618,8 +579,8 @@ rnr_nak: spin_lock_irqsave(&sqp->s_lock, flags); if (!(ib_rvt_state_ops[sqp->state] & RVT_PROCESS_RECV_OK)) goto clr_busy; - to = ib_hfi1_rnr_table[qp->r_min_rnr_timer]; - hfi1_add_rnr_timer(sqp, to); + rvt_add_rnr_timer(sqp, qp->r_min_rnr_timer << + IB_AETH_CREDIT_SHIFT); goto clr_busy; op_err: @@ -637,7 +598,7 @@ acc_err: wc.status = IB_WC_LOC_PROT_ERR; err: /* responder goes to error state */ - hfi1_rc_error(qp, wc.status); + rvt_rc_error(qp, wc.status); serr: spin_lock_irqsave(&sqp->s_lock, flags); diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index 1d81cac1fa6c..5cde1ecda0fe 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -856,7 +856,7 @@ struct sdma_engine *sdma_select_user_engine(struct hfi1_devdata *dd, { struct sdma_rht_node *rht_node; struct sdma_engine *sde = NULL; - const struct cpumask *current_mask = tsk_cpus_allowed(current); + const struct cpumask *current_mask = ¤t->cpus_allowed; unsigned long cpu_id; /* diff --git a/drivers/infiniband/hw/hfi1/trace.c b/drivers/infiniband/hw/hfi1/trace.c index 01f525cd985a..e86798af6903 100644 --- a/drivers/infiniband/hw/hfi1/trace.c +++ b/drivers/infiniband/hw/hfi1/trace.c @@ -130,14 +130,14 @@ const char *parse_everbs_hdrs( case OP(RC, ACKNOWLEDGE): trace_seq_printf(p, AETH_PRN, be32_to_cpu(eh->aeth) >> 24, parse_syndrome(be32_to_cpu(eh->aeth) >> 24), - be32_to_cpu(eh->aeth) & HFI1_MSN_MASK); + be32_to_cpu(eh->aeth) & IB_MSN_MASK); break; /* aeth + atomicacketh */ case OP(RC, ATOMIC_ACKNOWLEDGE): trace_seq_printf(p, AETH_PRN " " ATOMICACKETH_PRN, be32_to_cpu(eh->at.aeth) >> 24, parse_syndrome(be32_to_cpu(eh->at.aeth) >> 24), - be32_to_cpu(eh->at.aeth) & HFI1_MSN_MASK, + be32_to_cpu(eh->at.aeth) & IB_MSN_MASK, ib_u64_get(&eh->at.atomic_ack_eth)); break; /* atomiceth */ diff --git a/drivers/infiniband/hw/hfi1/uc.c b/drivers/infiniband/hw/hfi1/uc.c index b141a78ae38b..4b2a8400c823 100644 --- a/drivers/infiniband/hw/hfi1/uc.c +++ b/drivers/infiniband/hw/hfi1/uc.c @@ -296,7 +296,7 @@ bail_no_tx: */ void hfi1_uc_rcv(struct hfi1_packet *packet) { - struct hfi1_ibport *ibp = &packet->rcd->ppd->ibport_data; + struct hfi1_ibport *ibp = rcd_to_iport(packet->rcd); struct ib_header *hdr = packet->hdr; u32 rcv_flags = packet->rcv_flags; void *data = packet->ebuf; @@ -384,7 +384,7 @@ inv: } if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) - qp_comm_est(qp); + rvt_comm_est(qp); /* OK, process the packet. */ switch (opcode) { @@ -419,7 +419,7 @@ send_first: qp->r_rcv_len += pmtu; if (unlikely(qp->r_rcv_len > qp->r_len)) goto rewind; - hfi1_copy_sge(&qp->r_sge, data, pmtu, 0, 0); + hfi1_copy_sge(&qp->r_sge, data, pmtu, false, false); break; case OP(SEND_LAST_WITH_IMMEDIATE): @@ -444,7 +444,7 @@ send_last: if (unlikely(wc.byte_len > qp->r_len)) goto rewind; wc.opcode = IB_WC_RECV; - hfi1_copy_sge(&qp->r_sge, data, tlen, 0, 0); + hfi1_copy_sge(&qp->r_sge, data, tlen, false, false); rvt_put_ss(&qp->s_rdma_read_sge); last_imm: wc.wr_id = qp->r_wr_id; @@ -519,7 +519,7 @@ rdma_first: qp->r_rcv_len += pmtu; if (unlikely(qp->r_rcv_len > qp->r_len)) goto drop; - hfi1_copy_sge(&qp->r_sge, data, pmtu, 1, 0); + hfi1_copy_sge(&qp->r_sge, data, pmtu, true, false); break; case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE): @@ -548,7 +548,7 @@ rdma_last_imm: } wc.byte_len = qp->r_len; wc.opcode = IB_WC_RECV_RDMA_WITH_IMM; - hfi1_copy_sge(&qp->r_sge, data, tlen, 1, 0); + hfi1_copy_sge(&qp->r_sge, data, tlen, true, false); rvt_put_ss(&qp->r_sge); goto last_imm; @@ -564,7 +564,7 @@ rdma_last: tlen -= (hdrsize + pad + 4); if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) goto drop; - hfi1_copy_sge(&qp->r_sge, data, tlen, 1, 0); + hfi1_copy_sge(&qp->r_sge, data, tlen, true, false); rvt_put_ss(&qp->r_sge); break; @@ -584,5 +584,5 @@ drop: return; op_err: - hfi1_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); } diff --git a/drivers/infiniband/hw/hfi1/ud.c b/drivers/infiniband/hw/hfi1/ud.c index c071955c0272..13ea4eb6ef3d 100644 --- a/drivers/infiniband/hw/hfi1/ud.c +++ b/drivers/infiniband/hw/hfi1/ud.c @@ -167,7 +167,7 @@ static void ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe) ret = hfi1_rvt_get_rwqe(qp, 0); if (ret < 0) { - hfi1_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); goto bail_unlock; } if (!ret) { @@ -189,10 +189,10 @@ static void ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe) hfi1_make_grh(ibp, &grh, &grd, 0, 0); hfi1_copy_sge(&qp->r_sge, &grh, - sizeof(grh), 1, 0); + sizeof(grh), true, false); wc.wc_flags |= IB_WC_GRH; } else { - hfi1_skip_sge(&qp->r_sge, sizeof(struct ib_grh), 1); + rvt_skip_sge(&qp->r_sge, sizeof(struct ib_grh), true); } ssge.sg_list = swqe->sg_list + 1; ssge.sge = *swqe->sg_list; @@ -206,7 +206,7 @@ static void ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe) if (len > sge->sge_length) len = sge->sge_length; WARN_ON_ONCE(len == 0); - hfi1_copy_sge(&qp->r_sge, sge->vaddr, len, 1, 0); + hfi1_copy_sge(&qp->r_sge, sge->vaddr, len, true, false); sge->vaddr += len; sge->length -= len; sge->sge_length -= len; @@ -672,7 +672,7 @@ void hfi1_ud_rcv(struct hfi1_packet *packet) u32 src_qp; u16 dlid, pkey; int mgmt_pkey_idx = -1; - struct hfi1_ibport *ibp = &packet->rcd->ppd->ibport_data; + struct hfi1_ibport *ibp = rcd_to_iport(packet->rcd); struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); struct ib_header *hdr = packet->hdr; u32 rcv_flags = packet->rcv_flags; @@ -796,7 +796,7 @@ void hfi1_ud_rcv(struct hfi1_packet *packet) ret = hfi1_rvt_get_rwqe(qp, 0); if (ret < 0) { - hfi1_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); return; } if (!ret) { @@ -812,13 +812,13 @@ void hfi1_ud_rcv(struct hfi1_packet *packet) } if (has_grh) { hfi1_copy_sge(&qp->r_sge, &hdr->u.l.grh, - sizeof(struct ib_grh), 1, 0); + sizeof(struct ib_grh), true, false); wc.wc_flags |= IB_WC_GRH; } else { - hfi1_skip_sge(&qp->r_sge, sizeof(struct ib_grh), 1); + rvt_skip_sge(&qp->r_sge, sizeof(struct ib_grh), true); } hfi1_copy_sge(&qp->r_sge, data, wc.byte_len - sizeof(struct ib_grh), - 1, 0); + true, false); rvt_put_ss(&qp->r_sge); if (!test_and_clear_bit(RVT_R_WRID_VALID, &qp->r_aflags)) return; diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c index 64d26525435a..4a8295399e71 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c @@ -45,6 +45,7 @@ * */ #include <asm/page.h> +#include <linux/string.h> #include "user_exp_rcv.h" #include "trace.h" @@ -577,16 +578,10 @@ int hfi1_user_exp_rcv_clear(struct file *fp, struct hfi1_tid_info *tinfo) u32 *tidinfo; unsigned tididx; - tidinfo = kcalloc(tinfo->tidcnt, sizeof(*tidinfo), GFP_KERNEL); - if (!tidinfo) - return -ENOMEM; - - if (copy_from_user(tidinfo, (void __user *)(unsigned long) - tinfo->tidlist, sizeof(tidinfo[0]) * - tinfo->tidcnt)) { - ret = -EFAULT; - goto done; - } + tidinfo = memdup_user((void __user *)(unsigned long)tinfo->tidlist, + sizeof(tidinfo[0]) * tinfo->tidcnt); + if (IS_ERR(tidinfo)) + return PTR_ERR(tidinfo); mutex_lock(&uctxt->exp_lock); for (tididx = 0; tididx < tinfo->tidcnt; tididx++) { @@ -602,7 +597,7 @@ int hfi1_user_exp_rcv_clear(struct file *fp, struct hfi1_tid_info *tinfo) spin_unlock(&fd->tid_lock); tinfo->tidcnt = tididx; mutex_unlock(&uctxt->exp_lock); -done: + kfree(tidinfo); return ret; } diff --git a/drivers/infiniband/hw/hfi1/user_pages.c b/drivers/infiniband/hw/hfi1/user_pages.c index 20f4ddcac3b0..68295a12b771 100644 --- a/drivers/infiniband/hw/hfi1/user_pages.c +++ b/drivers/infiniband/hw/hfi1/user_pages.c @@ -46,7 +46,7 @@ */ #include <linux/mm.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/device.h> #include <linux/module.h> diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index 7d22f8ee98ef..e6811c4edc73 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -60,6 +60,7 @@ #include <linux/mmu_context.h> #include <linux/module.h> #include <linux/vmalloc.h> +#include <linux/string.h> #include "hfi.h" #include "sdma.h" @@ -725,30 +726,28 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec, */ if (req_opcode(req->info.ctrl) == EXPECTED) { u16 ntids = iovec[idx].iov_len / sizeof(*req->tids); + u32 *tmp; if (!ntids || ntids > MAX_TID_PAIR_ENTRIES) { ret = -EINVAL; goto free_req; } - req->tids = kcalloc(ntids, sizeof(*req->tids), GFP_KERNEL); - if (!req->tids) { - ret = -ENOMEM; - goto free_req; - } + /* * We have to copy all of the tids because they may vary * in size and, therefore, the TID count might not be * equal to the pkt count. However, there is no way to * tell at this point. */ - ret = copy_from_user(req->tids, iovec[idx].iov_base, - ntids * sizeof(*req->tids)); - if (ret) { + tmp = memdup_user(iovec[idx].iov_base, + ntids * sizeof(*req->tids)); + if (IS_ERR(tmp)) { + ret = PTR_ERR(tmp); SDMA_DBG(req, "Failed to copy %d TIDs (%d)", ntids, ret); - ret = -EFAULT; goto free_req; } + req->tids = tmp; req->n_tids = ntids; idx++; } diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 95ed4d6da510..222315fadab1 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c @@ -291,7 +291,7 @@ static void wss_insert(void *address) /* * Is the working set larger than the threshold? */ -static inline int wss_exceeds_threshold(void) +static inline bool wss_exceeds_threshold(void) { return atomic_read(&wss.total_count) >= wss.threshold; } @@ -419,18 +419,19 @@ __be64 ib_hfi1_sys_image_guid; * @ss: the SGE state * @data: the data to copy * @length: the length of the data + * @release: boolean to release MR * @copy_last: do a separate copy of the last 8 bytes */ void hfi1_copy_sge( struct rvt_sge_state *ss, void *data, u32 length, - int release, - int copy_last) + bool release, + bool copy_last) { struct rvt_sge *sge = &ss->sge; - int in_last = 0; int i; - int cacheless_copy = 0; + bool in_last = false; + bool cacheless_copy = false; if (sge_copy_mode == COPY_CACHELESS) { cacheless_copy = length >= PAGE_SIZE; @@ -454,19 +455,15 @@ void hfi1_copy_sge( if (length > 8) { length -= 8; } else { - copy_last = 0; - in_last = 1; + copy_last = false; + in_last = true; } } again: while (length) { - u32 len = sge->length; + u32 len = rvt_get_sge_length(sge, length); - if (len > length) - len = length; - if (len > sge->sge_length) - len = sge->sge_length; WARN_ON_ONCE(len == 0); if (unlikely(in_last)) { /* enforce byte transfer ordering */ @@ -477,77 +474,19 @@ again: } else { memcpy(sge->vaddr, data, len); } - sge->vaddr += len; - sge->length -= len; - sge->sge_length -= len; - if (sge->sge_length == 0) { - if (release) - rvt_put_mr(sge->mr); - if (--ss->num_sge) - *sge = *ss->sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - break; - sge->n = 0; - } - sge->vaddr = - sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = - sge->mr->map[sge->m]->segs[sge->n].length; - } + rvt_update_sge(ss, len, release); data += len; length -= len; } if (copy_last) { - copy_last = 0; - in_last = 1; + copy_last = false; + in_last = true; length = 8; goto again; } } -/** - * hfi1_skip_sge - skip over SGE memory - * @ss: the SGE state - * @length: the number of bytes to skip - */ -void hfi1_skip_sge(struct rvt_sge_state *ss, u32 length, int release) -{ - struct rvt_sge *sge = &ss->sge; - - while (length) { - u32 len = sge->length; - - if (len > length) - len = length; - if (len > sge->sge_length) - len = sge->sge_length; - WARN_ON_ONCE(len == 0); - sge->vaddr += len; - sge->length -= len; - sge->sge_length -= len; - if (sge->sge_length == 0) { - if (release) - rvt_put_mr(sge->mr); - if (--ss->num_sge) - *sge = *ss->sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - break; - sge->n = 0; - } - sge->vaddr = - sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = - sge->mr->map[sge->m]->segs[sge->n].length; - } - length -= len; - } -} - /* * Make sure the QP is ready and able to accept the given opcode. */ @@ -576,7 +515,7 @@ void hfi1_ib_rcv(struct hfi1_packet *packet) struct ib_header *hdr = packet->hdr; u32 tlen = packet->tlen; struct hfi1_pportdata *ppd = rcd->ppd; - struct hfi1_ibport *ibp = &ppd->ibport_data; + struct hfi1_ibport *ibp = rcd_to_iport(rcd); struct rvt_dev_info *rdi = &ppd->dd->verbs_dev.rdi; opcode_handler packet_handler; unsigned long flags; @@ -689,27 +628,6 @@ static void mem_timer(unsigned long data) hfi1_qp_wakeup(qp, RVT_S_WAIT_KMEM); } -void update_sge(struct rvt_sge_state *ss, u32 length) -{ - struct rvt_sge *sge = &ss->sge; - - sge->vaddr += length; - sge->length -= length; - sge->sge_length -= length; - if (sge->sge_length == 0) { - if (--ss->num_sge) - *sge = *ss->sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - return; - sge->n = 0; - } - sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = sge->mr->map[sge->m]->segs[sge->n].length; - } -} - /* * This is called with progress side lock held. */ @@ -798,7 +716,7 @@ static noinline int build_verbs_ulp_payload( len); if (ret) goto bail_txadd; - update_sge(ss, len); + rvt_update_sge(ss, len, false); length -= len; } return ret; @@ -1073,7 +991,7 @@ int hfi1_verbs_send_pio(struct rvt_qp *qp, struct hfi1_pkt_state *ps, if (slen > len) slen = len; - update_sge(ss, slen); + rvt_update_sge(ss, slen, false); seg_pio_copy_mid(pbuf, addr, slen); len -= slen; } @@ -1384,6 +1302,7 @@ static int query_port(struct rvt_dev_info *rdi, u8 port_num, struct hfi1_pportdata *ppd = &dd->pport[port_num - 1]; u16 lid = ppd->lid; + /* props being zeroed by the caller, avoid zeroing it here */ props->lid = lid ? lid : 0; props->lmc = ppd->lmc; /* OPA logical states match IB logical states */ @@ -1618,7 +1537,7 @@ static int cntr_names_initialized; * external strings. */ static int init_cntr_names(const char *names_in, - const int names_len, + const size_t names_len, int num_extra_names, int *num_cntrs, const char ***cntr_names) @@ -1784,7 +1703,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd) strlcpy(ibdev->name + lcpysz, "_%d", IB_DEVICE_NAME_MAX - lcpysz); ibdev->owner = THIS_MODULE; ibdev->phys_port_cnt = dd->num_pports; - ibdev->dma_device = &dd->pcidev->dev; + ibdev->dev.parent = &dd->pcidev->dev; ibdev->modify_device = modify_device; ibdev->alloc_hw_stats = alloc_hw_stats; ibdev->get_hw_stats = get_hw_stats; @@ -1845,6 +1764,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd) dd->verbs_dev.rdi.driver_f.mtu_to_path_mtu = mtu_to_path_mtu; dd->verbs_dev.rdi.driver_f.check_modify_qp = hfi1_check_modify_qp; dd->verbs_dev.rdi.driver_f.modify_qp = hfi1_modify_qp; + dd->verbs_dev.rdi.driver_f.notify_restart_rc = hfi1_restart_rc; dd->verbs_dev.rdi.driver_f.check_send_wqe = hfi1_check_send_wqe; /* completeion queue */ @@ -1910,7 +1830,7 @@ void hfi1_unregister_ib_device(struct hfi1_devdata *dd) void hfi1_cnp_rcv(struct hfi1_packet *packet) { - struct hfi1_ibport *ibp = &packet->rcd->ppd->ibport_data; + struct hfi1_ibport *ibp = rcd_to_iport(packet->rcd); struct hfi1_pportdata *ppd = ppd_from_ibp(ibp); struct ib_header *hdr = packet->hdr; struct rvt_qp *qp = packet->qp; diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index e6b893010e6d..3a0b589e41c2 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h @@ -127,7 +127,6 @@ struct hfi1_qp_priv { u8 s_sc; /* SC[0..4] for next packet */ u8 r_adefered; /* number of acks defered */ struct iowait s_iowait; - struct timer_list s_rnr_timer; struct rvt_qp *owner; }; @@ -260,15 +259,6 @@ int hfi1_process_mad(struct ib_device *ibdev, int mad_flags, u8 port, #define PSN_MODIFY_MASK 0xFFFFFF /* - * Compare the lower 24 bits of the msn values. - * Returns an integer <, ==, or > than zero. - */ -static inline int cmp_msn(u32 a, u32 b) -{ - return (((int)a) - ((int)b)) << 8; -} - -/* * Compare two PSNs * Returns an integer <, ==, or > than zero. */ @@ -299,9 +289,7 @@ void hfi1_put_txreq(struct verbs_txreq *tx); int hfi1_verbs_send(struct rvt_qp *qp, struct hfi1_pkt_state *ps); void hfi1_copy_sge(struct rvt_sge_state *ss, void *data, u32 length, - int release, int copy_last); - -void hfi1_skip_sge(struct rvt_sge_state *ss, u32 length, int release); + bool release, bool copy_last); void hfi1_cnp_rcv(struct hfi1_packet *packet); @@ -319,16 +307,8 @@ u8 ah_to_sc(struct ib_device *ibdev, struct ib_ah_attr *ah_attr); struct ib_ah *hfi1_create_qp0_ah(struct hfi1_ibport *ibp, u16 dlid); -void hfi1_rc_rnr_retry(unsigned long arg); -void hfi1_add_rnr_timer(struct rvt_qp *qp, u32 to); -void hfi1_rc_timeout(unsigned long arg); -void hfi1_del_timers_sync(struct rvt_qp *qp); -void hfi1_stop_rc_timers(struct rvt_qp *qp); - void hfi1_rc_send_complete(struct rvt_qp *qp, struct ib_header *hdr); -void hfi1_rc_error(struct rvt_qp *qp, enum ib_wc_status err); - void hfi1_ud_rcv(struct hfi1_packet *packet); int hfi1_lookup_pkey_idx(struct hfi1_ibport *ibp, u16 pkey); @@ -342,7 +322,7 @@ int hfi1_check_modify_qp(struct rvt_qp *qp, struct ib_qp_attr *attr, void hfi1_modify_qp(struct rvt_qp *qp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata); - +void hfi1_restart_rc(struct rvt_qp *qp, u32 psn, int wait); int hfi1_check_send_wqe(struct rvt_qp *qp, struct rvt_swqe *wqe); extern const u32 rc_only_opcode; diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 4953d9cb83a7..c3b41f95e70a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -32,6 +32,7 @@ */ #include <linux/acpi.h> #include <linux/of_platform.h> +#include <linux/module.h> #include <rdma/ib_addr.h> #include <rdma/ib_smi.h> #include <rdma/ib_user_verbs.h> @@ -249,7 +250,7 @@ static int hns_roce_query_port(struct ib_device *ib_dev, u8 port_num, assert(port_num > 0); port = port_num - 1; - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ props->max_mtu = hr_dev->caps.max_mtu; props->gid_tbl_len = hr_dev->caps.gid_table_len[port]; @@ -400,14 +401,15 @@ static int hns_roce_port_immutable(struct ib_device *ib_dev, u8 port_num, struct ib_port_attr attr; int ret; - ret = hns_roce_query_port(ib_dev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; + + ret = ib_query_port(ib_dev, port_num, &attr); if (ret) return ret; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; immutable->max_mad_size = IB_MGMT_MAD_SIZE; return 0; @@ -437,7 +439,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev) ib_dev->owner = THIS_MODULE; ib_dev->node_type = RDMA_NODE_IB_CA; - ib_dev->dma_device = dev; + ib_dev->dev.parent = dev; ib_dev->phys_port_cnt = hr_dev->caps.num_ports; ib_dev->local_dma_lkey = hr_dev->caps.reserved_lkey; diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index f036f32f15d3..3f44f2f91f03 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -101,7 +101,7 @@ static void hns_roce_ib_qp_event(struct hns_roce_qp *hr_qp, event.event = IB_EVENT_QP_ACCESS_ERR; break; default: - dev_dbg(ibqp->device->dma_device, "roce_ib: Unexpected event type %d on QP %06lx\n", + dev_dbg(ibqp->device->dev.parent, "roce_ib: Unexpected event type %d on QP %06lx\n", type, hr_qp->qpn); return; } diff --git a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c index 98923a8cf86d..f82483b3d1e7 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c +++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c @@ -450,6 +450,9 @@ static enum i40iw_status_code i40iw_sc_cqp_create(struct i40iw_sc_cqp *cqp, u32 cnt = 0, p1, p2, val = 0, err_code; enum i40iw_status_code ret_code; + *maj_err = 0; + *min_err = 0; + ret_code = i40iw_allocate_dma_mem(cqp->dev->hw, &cqp->sdbuf, 128, @@ -4498,9 +4501,9 @@ void i40iw_sc_vsi_init(struct i40iw_sc_vsi *vsi, struct i40iw_vsi_init_info *inf i40iw_fill_qos_list(info->params->qs_handle_list); for (i = 0; i < I40IW_MAX_USER_PRIORITY; i++) { - vsi->qos[i].qs_handle = - info->params->qs_handle_list[i]; - i40iw_debug(vsi->dev, I40IW_DEBUG_DCB, "qset[%d]: %d\n", i, vsi->qos[i].qs_handle); + vsi->qos[i].qs_handle = info->params->qs_handle_list[i]; + i40iw_debug(vsi->dev, I40IW_DEBUG_DCB, "qset[%d]: %d\n", i, + vsi->qos[i].qs_handle); spin_lock_init(&vsi->qos[i].lock); INIT_LIST_HEAD(&vsi->qos[i].qplist); } @@ -4851,46 +4854,46 @@ void i40iw_vsi_stats_free(struct i40iw_sc_vsi *vsi) } static struct i40iw_cqp_ops iw_cqp_ops = { - i40iw_sc_cqp_init, - i40iw_sc_cqp_create, - i40iw_sc_cqp_post_sq, - i40iw_sc_cqp_get_next_send_wqe, - i40iw_sc_cqp_destroy, - i40iw_sc_poll_for_cqp_op_done + .cqp_init = i40iw_sc_cqp_init, + .cqp_create = i40iw_sc_cqp_create, + .cqp_post_sq = i40iw_sc_cqp_post_sq, + .cqp_get_next_send_wqe = i40iw_sc_cqp_get_next_send_wqe, + .cqp_destroy = i40iw_sc_cqp_destroy, + .poll_for_cqp_op_done = i40iw_sc_poll_for_cqp_op_done }; static struct i40iw_ccq_ops iw_ccq_ops = { - i40iw_sc_ccq_init, - i40iw_sc_ccq_create, - i40iw_sc_ccq_destroy, - i40iw_sc_ccq_create_done, - i40iw_sc_ccq_get_cqe_info, - i40iw_sc_ccq_arm + .ccq_init = i40iw_sc_ccq_init, + .ccq_create = i40iw_sc_ccq_create, + .ccq_destroy = i40iw_sc_ccq_destroy, + .ccq_create_done = i40iw_sc_ccq_create_done, + .ccq_get_cqe_info = i40iw_sc_ccq_get_cqe_info, + .ccq_arm = i40iw_sc_ccq_arm }; static struct i40iw_ceq_ops iw_ceq_ops = { - i40iw_sc_ceq_init, - i40iw_sc_ceq_create, - i40iw_sc_cceq_create_done, - i40iw_sc_cceq_destroy_done, - i40iw_sc_cceq_create, - i40iw_sc_ceq_destroy, - i40iw_sc_process_ceq + .ceq_init = i40iw_sc_ceq_init, + .ceq_create = i40iw_sc_ceq_create, + .cceq_create_done = i40iw_sc_cceq_create_done, + .cceq_destroy_done = i40iw_sc_cceq_destroy_done, + .cceq_create = i40iw_sc_cceq_create, + .ceq_destroy = i40iw_sc_ceq_destroy, + .process_ceq = i40iw_sc_process_ceq }; static struct i40iw_aeq_ops iw_aeq_ops = { - i40iw_sc_aeq_init, - i40iw_sc_aeq_create, - i40iw_sc_aeq_destroy, - i40iw_sc_get_next_aeqe, - i40iw_sc_repost_aeq_entries, - i40iw_sc_aeq_create_done, - i40iw_sc_aeq_destroy_done + .aeq_init = i40iw_sc_aeq_init, + .aeq_create = i40iw_sc_aeq_create, + .aeq_destroy = i40iw_sc_aeq_destroy, + .get_next_aeqe = i40iw_sc_get_next_aeqe, + .repost_aeq_entries = i40iw_sc_repost_aeq_entries, + .aeq_create_done = i40iw_sc_aeq_create_done, + .aeq_destroy_done = i40iw_sc_aeq_destroy_done }; /* iwarp pd ops */ static struct i40iw_pd_ops iw_pd_ops = { - i40iw_sc_pd_init, + .pd_init = i40iw_sc_pd_init, }; static struct i40iw_priv_qp_ops iw_priv_qp_ops = { @@ -4909,53 +4912,51 @@ static struct i40iw_priv_qp_ops iw_priv_qp_ops = { }; static struct i40iw_priv_cq_ops iw_priv_cq_ops = { - i40iw_sc_cq_init, - i40iw_sc_cq_create, - i40iw_sc_cq_destroy, - i40iw_sc_cq_modify, + .cq_init = i40iw_sc_cq_init, + .cq_create = i40iw_sc_cq_create, + .cq_destroy = i40iw_sc_cq_destroy, + .cq_modify = i40iw_sc_cq_modify, }; static struct i40iw_mr_ops iw_mr_ops = { - i40iw_sc_alloc_stag, - i40iw_sc_mr_reg_non_shared, - i40iw_sc_mr_reg_shared, - i40iw_sc_dealloc_stag, - i40iw_sc_query_stag, - i40iw_sc_mw_alloc + .alloc_stag = i40iw_sc_alloc_stag, + .mr_reg_non_shared = i40iw_sc_mr_reg_non_shared, + .mr_reg_shared = i40iw_sc_mr_reg_shared, + .dealloc_stag = i40iw_sc_dealloc_stag, + .query_stag = i40iw_sc_query_stag, + .mw_alloc = i40iw_sc_mw_alloc }; static struct i40iw_cqp_misc_ops iw_cqp_misc_ops = { - i40iw_sc_manage_push_page, - i40iw_sc_manage_hmc_pm_func_table, - i40iw_sc_set_hmc_resource_profile, - i40iw_sc_commit_fpm_values, - i40iw_sc_query_fpm_values, - i40iw_sc_static_hmc_pages_allocated, - i40iw_sc_add_arp_cache_entry, - i40iw_sc_del_arp_cache_entry, - i40iw_sc_query_arp_cache_entry, - i40iw_sc_manage_apbvt_entry, - i40iw_sc_manage_qhash_table_entry, - i40iw_sc_alloc_local_mac_ipaddr_entry, - i40iw_sc_add_local_mac_ipaddr_entry, - i40iw_sc_del_local_mac_ipaddr_entry, - i40iw_sc_cqp_nop, - i40iw_sc_commit_fpm_values_done, - i40iw_sc_query_fpm_values_done, - i40iw_sc_manage_hmc_pm_func_table_done, - i40iw_sc_suspend_qp, - i40iw_sc_resume_qp + .manage_push_page = i40iw_sc_manage_push_page, + .manage_hmc_pm_func_table = i40iw_sc_manage_hmc_pm_func_table, + .set_hmc_resource_profile = i40iw_sc_set_hmc_resource_profile, + .commit_fpm_values = i40iw_sc_commit_fpm_values, + .query_fpm_values = i40iw_sc_query_fpm_values, + .static_hmc_pages_allocated = i40iw_sc_static_hmc_pages_allocated, + .add_arp_cache_entry = i40iw_sc_add_arp_cache_entry, + .del_arp_cache_entry = i40iw_sc_del_arp_cache_entry, + .query_arp_cache_entry = i40iw_sc_query_arp_cache_entry, + .manage_apbvt_entry = i40iw_sc_manage_apbvt_entry, + .manage_qhash_table_entry = i40iw_sc_manage_qhash_table_entry, + .alloc_local_mac_ipaddr_table_entry = i40iw_sc_alloc_local_mac_ipaddr_entry, + .add_local_mac_ipaddr_entry = i40iw_sc_add_local_mac_ipaddr_entry, + .del_local_mac_ipaddr_entry = i40iw_sc_del_local_mac_ipaddr_entry, + .cqp_nop = i40iw_sc_cqp_nop, + .commit_fpm_values_done = i40iw_sc_commit_fpm_values_done, + .query_fpm_values_done = i40iw_sc_query_fpm_values_done, + .manage_hmc_pm_func_table_done = i40iw_sc_manage_hmc_pm_func_table_done, + .update_suspend_qp = i40iw_sc_suspend_qp, + .update_resume_qp = i40iw_sc_resume_qp }; static struct i40iw_hmc_ops iw_hmc_ops = { - i40iw_sc_init_iw_hmc, - i40iw_sc_parse_fpm_query_buf, - i40iw_sc_configure_iw_fpm, - i40iw_sc_parse_fpm_commit_buf, - i40iw_sc_create_hmc_obj, - i40iw_sc_del_hmc_obj, - NULL, - NULL + .init_iw_hmc = i40iw_sc_init_iw_hmc, + .parse_fpm_query_buf = i40iw_sc_parse_fpm_query_buf, + .configure_iw_fpm = i40iw_sc_configure_iw_fpm, + .parse_fpm_commit_buf = i40iw_sc_parse_fpm_commit_buf, + .create_hmc_object = i40iw_sc_create_hmc_obj, + .del_hmc_object = i40iw_sc_del_hmc_obj }; /** diff --git a/drivers/infiniband/hw/i40iw/i40iw_uk.c b/drivers/infiniband/hw/i40iw/i40iw_uk.c index 2800f796271c..b0d3a0e8a9b5 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_uk.c +++ b/drivers/infiniband/hw/i40iw/i40iw_uk.c @@ -913,29 +913,29 @@ enum i40iw_status_code i40iw_get_wqe_shift(u32 wqdepth, u32 sge, u32 inline_data } static struct i40iw_qp_uk_ops iw_qp_uk_ops = { - i40iw_qp_post_wr, - i40iw_qp_ring_push_db, - i40iw_rdma_write, - i40iw_rdma_read, - i40iw_send, - i40iw_inline_rdma_write, - i40iw_inline_send, - i40iw_stag_local_invalidate, - i40iw_mw_bind, - i40iw_post_receive, - i40iw_nop + .iw_qp_post_wr = i40iw_qp_post_wr, + .iw_qp_ring_push_db = i40iw_qp_ring_push_db, + .iw_rdma_write = i40iw_rdma_write, + .iw_rdma_read = i40iw_rdma_read, + .iw_send = i40iw_send, + .iw_inline_rdma_write = i40iw_inline_rdma_write, + .iw_inline_send = i40iw_inline_send, + .iw_stag_local_invalidate = i40iw_stag_local_invalidate, + .iw_mw_bind = i40iw_mw_bind, + .iw_post_receive = i40iw_post_receive, + .iw_post_nop = i40iw_nop }; static struct i40iw_cq_ops iw_cq_ops = { - i40iw_cq_request_notification, - i40iw_cq_poll_completion, - i40iw_cq_post_entries, - i40iw_clean_cq + .iw_cq_request_notification = i40iw_cq_request_notification, + .iw_cq_poll_completion = i40iw_cq_poll_completion, + .iw_cq_post_entries = i40iw_cq_post_entries, + .iw_cq_clean = i40iw_clean_cq }; static struct i40iw_device_uk_ops iw_device_uk_ops = { - i40iw_cq_uk_init, - i40iw_qp_uk_init, + .iwarp_cq_uk_init = i40iw_cq_uk_init, + .iwarp_qp_uk_init = i40iw_qp_uk_init, }; /** diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 4c000d60d5c6..9b2849979756 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -97,8 +97,7 @@ static int i40iw_query_port(struct ib_device *ibdev, struct i40iw_device *iwdev = to_iwdev(ibdev); struct net_device *netdev = iwdev->netdev; - memset(props, 0, sizeof(*props)); - + /* props being zeroed by the caller, avoid zeroing it here */ props->max_mtu = IB_MTU_4096; props->active_mtu = ib_mtu_int_to_enum(netdev->mtu); @@ -2497,14 +2496,15 @@ static int i40iw_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; - err = i40iw_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; return 0; } @@ -2758,7 +2758,6 @@ static struct i40iw_ib_device *i40iw_init_rdma_device(struct i40iw_device *iwdev (1ull << IB_USER_VERBS_CMD_POST_SEND); iwibdev->ibdev.phys_port_cnt = 1; iwibdev->ibdev.num_comp_vectors = iwdev->ceqs_count; - iwibdev->ibdev.dma_device = &pcidev->dev; iwibdev->ibdev.dev.parent = &pcidev->dev; iwibdev->ibdev.query_port = i40iw_query_port; iwibdev->ibdev.modify_port = i40iw_modify_port; diff --git a/drivers/infiniband/hw/mlx4/alias_GUID.c b/drivers/infiniband/hw/mlx4/alias_GUID.c index 06020c54db20..ea24230ea0d4 100644 --- a/drivers/infiniband/hw/mlx4/alias_GUID.c +++ b/drivers/infiniband/hw/mlx4/alias_GUID.c @@ -499,6 +499,7 @@ static int set_guid_rec(struct ib_device *ibdev, struct list_head *head = &dev->sriov.alias_guid.ports_guid[port - 1].cb_list; + memset(&attr, 0, sizeof(attr)); err = __mlx4_ib_query_port(ibdev, port, &attr, 1); if (err) { pr_debug("mlx4_ib_query_port failed (err: %d), port: %d\n", diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 7031a8dd4d14..fba94df28cf1 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -39,6 +39,9 @@ #include <linux/inetdevice.h> #include <linux/rtnetlink.h> #include <linux/if_vlan.h> +#include <linux/sched/mm.h> +#include <linux/sched/task.h> + #include <net/ipv6.h> #include <net/addrconf.h> #include <net/devlink.h> @@ -678,7 +681,7 @@ static u8 state_to_phys_state(enum ib_port_state state) } static int eth_link_query_port(struct ib_device *ibdev, u8 port, - struct ib_port_attr *props, int netw_view) + struct ib_port_attr *props) { struct mlx4_ib_dev *mdev = to_mdev(ibdev); @@ -741,11 +744,11 @@ int __mlx4_ib_query_port(struct ib_device *ibdev, u8 port, { int err; - memset(props, 0, sizeof *props); + /* props being zeroed by the caller, avoid zeroing it here */ err = mlx4_ib_port_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND ? ib_link_query_port(ibdev, port, props, netw_view) : - eth_link_query_port(ibdev, port, props, netw_view); + eth_link_query_port(ibdev, port, props); return err; } @@ -1014,7 +1017,7 @@ static int mlx4_ib_modify_port(struct ib_device *ibdev, u8 port, int mask, mutex_lock(&mdev->cap_mask_mutex); - err = mlx4_ib_query_port(ibdev, port, &attr); + err = ib_query_port(ibdev, port, &attr); if (err) goto out; @@ -2537,24 +2540,27 @@ static int mlx4_port_immutable(struct ib_device *ibdev, u8 port_num, struct mlx4_ib_dev *mdev = to_mdev(ibdev); int err; - err = mlx4_ib_query_port(ibdev, port_num, &attr); - if (err) - return err; - - immutable->pkey_tbl_len = attr.pkey_tbl_len; - immutable->gid_tbl_len = attr.gid_tbl_len; - if (mlx4_ib_port_link_layer(ibdev, port_num) == IB_LINK_LAYER_INFINIBAND) { immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB; + immutable->max_mad_size = IB_MGMT_MAD_SIZE; } else { if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_IBOE) immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ROCE_V1_V2) immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE | RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP; + immutable->core_cap_flags |= RDMA_CORE_PORT_RAW_PACKET; + if (immutable->core_cap_flags & (RDMA_CORE_PORT_IBA_ROCE | + RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP)) + immutable->max_mad_size = IB_MGMT_MAD_SIZE; } - immutable->max_mad_size = IB_MGMT_MAD_SIZE; + err = ib_query_port(ibdev, port_num, &attr); + if (err) + return err; + + immutable->pkey_tbl_len = attr.pkey_tbl_len; + immutable->gid_tbl_len = attr.gid_tbl_len; return 0; } @@ -2625,7 +2631,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev->ib_dev.phys_port_cnt = mlx4_is_bonded(dev) ? 1 : ibdev->num_ports; ibdev->ib_dev.num_comp_vectors = dev->caps.num_comp_vectors; - ibdev->ib_dev.dma_device = &dev->persist->pdev->dev; + ibdev->ib_dev.dev.parent = &dev->persist->pdev->dev; ibdev->ib_dev.get_netdev = mlx4_ib_get_netdev; ibdev->ib_dev.add_gid = mlx4_ib_add_gid; ibdev->ib_dev.del_gid = mlx4_ib_del_gid; diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 7f3d976d81ed..64fed44b43a6 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -55,7 +55,7 @@ #define pr_fmt(fmt) "<" MLX4_IB_DRV_NAME "> %s: " fmt, __func__ #define mlx4_ib_warn(ibdev, format, arg...) \ - dev_warn((ibdev)->dma_device, MLX4_IB_DRV_NAME ": " format, ## arg) + dev_warn((ibdev)->dev.parent, MLX4_IB_DRV_NAME ": " format, ## arg) enum { MLX4_IB_SQ_MIN_WQE_SHIFT = 6, diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 5d73989d9771..433bcdbdd680 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -292,10 +292,10 @@ mlx4_alloc_priv_pages(struct ib_device *device, if (!mr->pages) return -ENOMEM; - mr->page_map = dma_map_single(device->dma_device, mr->pages, + mr->page_map = dma_map_single(device->dev.parent, mr->pages, mr->page_map_size, DMA_TO_DEVICE); - if (dma_mapping_error(device->dma_device, mr->page_map)) { + if (dma_mapping_error(device->dev.parent, mr->page_map)) { ret = -ENOMEM; goto err; } @@ -313,7 +313,7 @@ mlx4_free_priv_pages(struct mlx4_ib_mr *mr) if (mr->pages) { struct ib_device *device = mr->ibmr.device; - dma_unmap_single(device->dma_device, mr->page_map, + dma_unmap_single(device->dev.parent, mr->page_map, mr->page_map_size, DMA_TO_DEVICE); free_page((unsigned long)mr->pages); mr->pages = NULL; diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index c068add8838b..c34eebc7db65 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -76,10 +76,6 @@ enum { MLX4_IB_LSO_HEADER_SPARE = 128, }; -enum { - MLX4_IB_IBOE_ETHERTYPE = 0x8915 -}; - struct mlx4_ib_sqp { struct mlx4_ib_qp qp; int pkey_index; @@ -2424,11 +2420,31 @@ static u8 sl_to_vl(struct mlx4_ib_dev *dev, u8 sl, int port_num) return vl; } +static int fill_gid_by_hw_index(struct mlx4_ib_dev *ibdev, u8 port_num, + int index, union ib_gid *gid, + enum ib_gid_type *gid_type) +{ + struct mlx4_ib_iboe *iboe = &ibdev->iboe; + struct mlx4_port_gid_table *port_gid_table; + unsigned long flags; + + port_gid_table = &iboe->gids[port_num - 1]; + spin_lock_irqsave(&iboe->lock, flags); + memcpy(gid, &port_gid_table->gids[index].gid, sizeof(*gid)); + *gid_type = port_gid_table->gids[index].gid_type; + spin_unlock_irqrestore(&iboe->lock, flags); + if (!memcmp(gid, &zgid, sizeof(*gid))) + return -ENOENT; + + return 0; +} + #define MLX4_ROCEV2_QP1_SPORT 0xC000 static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_ud_wr *wr, void *wqe, unsigned *mlx_seg_len) { struct ib_device *ib_dev = sqp->qp.ibqp.device; + struct mlx4_ib_dev *ibdev = to_mdev(ib_dev); struct mlx4_wqe_mlx_seg *mlx = wqe; struct mlx4_wqe_ctrl_seg *ctrl = wqe; struct mlx4_wqe_inline_seg *inl = wqe + sizeof *mlx; @@ -2454,8 +2470,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_ud_wr *wr, is_eth = rdma_port_get_link_layer(sqp->qp.ibqp.device, sqp->qp.port) == IB_LINK_LAYER_ETHERNET; is_grh = mlx4_ib_ah_grh_present(ah); if (is_eth) { - struct ib_gid_attr gid_attr; - + enum ib_gid_type gid_type; if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) { /* When multi-function is enabled, the ib_core gid * indexes don't necessarily match the hw ones, so @@ -2466,18 +2481,11 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_ud_wr *wr, if (err) return err; } else { - err = ib_get_cached_gid(ib_dev, - be32_to_cpu(ah->av.ib.port_pd) >> 24, - ah->av.ib.gid_index, &sgid, - &gid_attr); - if (!err) { - if (gid_attr.ndev) - dev_put(gid_attr.ndev); - if (!memcmp(&sgid, &zgid, sizeof(sgid))) - err = -ENOENT; - } + err = fill_gid_by_hw_index(ibdev, sqp->qp.port, + ah->av.ib.gid_index, + &sgid, &gid_type); if (!err) { - is_udp = gid_attr.gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP; + is_udp = gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP; if (is_udp) { if (ipv6_addr_v4mapped((struct in6_addr *)&sgid)) ip_version = 4; @@ -2588,7 +2596,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_ud_wr *wr, u16 ether_type; u16 pcp = (be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 29) << 13; - ether_type = (!is_udp) ? MLX4_IB_IBOE_ETHERTYPE : + ether_type = (!is_udp) ? ETH_P_IBOE: (ip_version == 4 ? ETH_P_IP : ETH_P_IPV6); mlx->sched_prio = cpu_to_be16(pcp); @@ -2955,21 +2963,17 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, if (sqp->roce_v2_gsi) { struct mlx4_ib_ah *ah = to_mah(ud_wr(wr)->ah); - struct ib_gid_attr gid_attr; + enum ib_gid_type gid_type; union ib_gid gid; - if (!ib_get_cached_gid(ibqp->device, - be32_to_cpu(ah->av.ib.port_pd) >> 24, - ah->av.ib.gid_index, &gid, - &gid_attr)) { - if (gid_attr.ndev) - dev_put(gid_attr.ndev); - qp = (gid_attr.gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) ? - to_mqp(sqp->roce_v2_gsi) : qp; - } else { + if (!fill_gid_by_hw_index(mdev, sqp->qp.port, + ah->av.ib.gid_index, + &gid, &gid_type)) + qp = (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) ? + to_mqp(sqp->roce_v2_gsi) : qp; + else pr_err("Failed to get gid at index %d. RoCEv2 will not work properly\n", ah->av.ib.gid_index); - } } } diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index 69fb5ba94d0f..0ba5ba7540c8 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -226,6 +226,7 @@ static int add_port_entries(struct mlx4_ib_dev *device, int port_num) int ret = 0 ; struct ib_port_attr attr; + memset(&attr, 0, sizeof(attr)); /* get the physical gid and pkey table sizes.*/ ret = __mlx4_ib_query_port(&device->ib_dev, port_num, &attr, 1); if (ret) diff --git a/drivers/infiniband/hw/mlx5/Makefile b/drivers/infiniband/hw/mlx5/Makefile index 7493a83acd28..90ad2adc752f 100644 --- a/drivers/infiniband/hw/mlx5/Makefile +++ b/drivers/infiniband/hw/mlx5/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_MLX5_INFINIBAND) += mlx5_ib.o -mlx5_ib-y := main.o cq.o doorbell.o qp.o mem.o srq.o mr.o ah.o mad.o gsi.o ib_virt.o +mlx5_ib-y := main.o cq.o doorbell.o qp.o mem.o srq.o mr.o ah.o mad.o gsi.o ib_virt.o cmd.o mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o diff --git a/drivers/infiniband/hw/mlx5/cmd.c b/drivers/infiniband/hw/mlx5/cmd.c new file mode 100644 index 000000000000..cdc2d3017da7 --- /dev/null +++ b/drivers/infiniband/hw/mlx5/cmd.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "cmd.h" + +int mlx5_cmd_null_mkey(struct mlx5_core_dev *dev, u32 *null_mkey) +{ + u32 out[MLX5_ST_SZ_DW(query_special_contexts_out)] = {}; + u32 in[MLX5_ST_SZ_DW(query_special_contexts_in)] = {}; + int err; + + MLX5_SET(query_special_contexts_in, in, opcode, + MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS); + err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); + if (!err) + *null_mkey = MLX5_GET(query_special_contexts_out, out, + null_mkey); + return err; +} diff --git a/drivers/infiniband/hw/mlx5/cmd.h b/drivers/infiniband/hw/mlx5/cmd.h new file mode 100644 index 000000000000..7ca8a7b6434d --- /dev/null +++ b/drivers/infiniband/hw/mlx5/cmd.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX5_IB_CMD_H +#define MLX5_IB_CMD_H + +#include <linux/kernel.h> +#include <linux/mlx5/driver.h> + +int mlx5_cmd_null_mkey(struct mlx5_core_dev *dev, u32 *null_mkey); +#endif /* MLX5_IB_CMD_H */ diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c index 39e58489dcc2..8dacb49eabd9 100644 --- a/drivers/infiniband/hw/mlx5/mad.c +++ b/drivers/infiniband/hw/mlx5/mad.c @@ -42,12 +42,24 @@ enum { MLX5_IB_VENDOR_CLASS2 = 0xa }; +static bool can_do_mad_ifc(struct mlx5_ib_dev *dev, u8 port_num, + struct ib_mad *in_mad) +{ + if (in_mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED && + in_mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) + return true; + return dev->mdev->port_caps[port_num - 1].has_smi; +} + int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey, u8 port, const struct ib_wc *in_wc, const struct ib_grh *in_grh, const void *in_mad, void *response_mad) { u8 op_modifier = 0; + if (!can_do_mad_ifc(dev, port, (struct ib_mad *)in_mad)) + return -EPERM; + /* Key check traps can't be generated unless we have in_wc to * tell us where to send the trap. */ @@ -515,7 +527,7 @@ int mlx5_query_mad_ifc_port(struct ib_device *ibdev, u8 port, if (!in_mad || !out_mad) goto out; - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ init_query_mad(in_mad); in_mad->attr_id = IB_SMP_ATTR_PORT_INFO; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 9d8535385bb8..4dc0a8785fe0 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -41,6 +41,8 @@ #include <asm/pat.h> #endif #include <linux/sched.h> +#include <linux/sched/mm.h> +#include <linux/sched/task.h> #include <linux/delay.h> #include <rdma/ib_user_verbs.h> #include <rdma/ib_addr.h> @@ -65,10 +67,6 @@ MODULE_DESCRIPTION("Mellanox Connect-IB HCA IB driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(DRIVER_VERSION); -static int deprecated_prof_sel = 2; -module_param_named(prof_sel, deprecated_prof_sel, int, 0444); -MODULE_PARM_DESC(prof_sel, "profile selector. Deprecated here. Moved to module mlx5_core"); - static char mlx5_version[] = DRIVER_NAME ": Mellanox Connect-IB Infiniband driver v" DRIVER_VERSION " (" DRIVER_RELDATE ")\n"; @@ -175,7 +173,7 @@ static int mlx5_query_port_roce(struct ib_device *device, u8 port_num, enum ib_mtu ndev_ib_mtu; u16 qkey_viol_cntr; - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ props->port_cap_flags |= IB_PORT_CM_SUP; props->port_cap_flags |= IB_PORT_IP_BASED_GIDS; @@ -326,6 +324,27 @@ __be16 mlx5_get_roce_udp_sport(struct mlx5_ib_dev *dev, u8 port_num, return cpu_to_be16(MLX5_CAP_ROCE(dev->mdev, r_roce_min_src_udp_port)); } +int mlx5_get_roce_gid_type(struct mlx5_ib_dev *dev, u8 port_num, + int index, enum ib_gid_type *gid_type) +{ + struct ib_gid_attr attr; + union ib_gid gid; + int ret; + + ret = ib_get_cached_gid(&dev->ib_dev, port_num, index, &gid, &attr); + if (ret) + return ret; + + if (!attr.ndev) + return -ENODEV; + + dev_put(attr.ndev); + + *gid_type = attr.gid_type; + + return 0; +} + static int mlx5_use_mad_ifc(struct mlx5_ib_dev *dev) { if (MLX5_CAP_GEN(dev->mdev, port_type) == MLX5_CAP_PORT_TYPE_IB) @@ -565,8 +584,15 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, props->device_cap_flags |= IB_DEVICE_BLOCK_MULTICAST_LOOPBACK; if (MLX5_CAP_GEN(dev->mdev, eth_net_offloads)) { - if (MLX5_CAP_ETH(mdev, csum_cap)) + if (MLX5_CAP_ETH(mdev, csum_cap)) { + /* Legacy bit to support old userspace libraries */ props->device_cap_flags |= IB_DEVICE_RAW_IP_CSUM; + props->raw_packet_caps |= IB_RAW_PACKET_CAP_IP_CSUM; + } + + if (MLX5_CAP_ETH(dev->mdev, vlan_cap)) + props->raw_packet_caps |= + IB_RAW_PACKET_CAP_CVLAN_STRIPPING; if (field_avail(typeof(resp), tso_caps, uhw->outlen)) { max_tso = MLX5_CAP_ETH(mdev, max_lso_cap); @@ -605,8 +631,11 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, } if (MLX5_CAP_GEN(dev->mdev, eth_net_offloads) && - MLX5_CAP_ETH(dev->mdev, scatter_fcs)) + MLX5_CAP_ETH(dev->mdev, scatter_fcs)) { + /* Legacy bit to support old userspace libraries */ props->device_cap_flags |= IB_DEVICE_RAW_SCATTER_FCS; + props->raw_packet_caps |= IB_RAW_PACKET_CAP_SCATTER_FCS; + } if (mlx5_get_flow_namespace(dev->mdev, MLX5_FLOW_NAMESPACE_BYPASS)) props->device_cap_flags |= IB_DEVICE_MANAGED_FLOW_STEERING; @@ -831,7 +860,7 @@ static int mlx5_query_hca_port(struct ib_device *ibdev, u8 port, goto out; } - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ err = mlx5_query_hca_vport_context(mdev, 0, port, 0, rep); if (err) @@ -969,6 +998,31 @@ static int mlx5_ib_modify_device(struct ib_device *ibdev, int mask, return err; } +static int set_port_caps_atomic(struct mlx5_ib_dev *dev, u8 port_num, u32 mask, + u32 value) +{ + struct mlx5_hca_vport_context ctx = {}; + int err; + + err = mlx5_query_hca_vport_context(dev->mdev, 0, + port_num, 0, &ctx); + if (err) + return err; + + if (~ctx.cap_mask1_perm & mask) { + mlx5_ib_warn(dev, "trying to change bitmask 0x%X but change supported 0x%X\n", + mask, ctx.cap_mask1_perm); + return -EINVAL; + } + + ctx.cap_mask1 = value; + ctx.cap_mask1_perm = mask; + err = mlx5_core_modify_hca_vport_context(dev->mdev, 0, + port_num, 0, &ctx); + + return err; +} + static int mlx5_ib_modify_port(struct ib_device *ibdev, u8 port, int mask, struct ib_port_modify *props) { @@ -976,10 +1030,20 @@ static int mlx5_ib_modify_port(struct ib_device *ibdev, u8 port, int mask, struct ib_port_attr attr; u32 tmp; int err; + u32 change_mask; + u32 value; + bool is_ib = (mlx5_ib_port_link_layer(ibdev, port) == + IB_LINK_LAYER_INFINIBAND); + + if (MLX5_CAP_GEN(dev->mdev, ib_virt) && is_ib) { + change_mask = props->clr_port_cap_mask | props->set_port_cap_mask; + value = ~props->clr_port_cap_mask | props->set_port_cap_mask; + return set_port_caps_atomic(dev, port, change_mask, value); + } mutex_lock(&dev->cap_mask_mutex); - err = mlx5_ib_query_port(ibdev, port, &attr); + err = ib_query_port(ibdev, port, &attr); if (err) goto out; @@ -1661,6 +1725,7 @@ static void set_tos(void *outer_c, void *outer_v, u8 mask, u8 val) #define LAST_IPV6_FIELD traffic_class #define LAST_TCP_UDP_FIELD src_port #define LAST_TUNNEL_FIELD tunnel_id +#define LAST_FLOW_TAG_FIELD tag_id /* Field is the last supported field */ #define FIELDS_NOT_SUPPORTED(filter, field)\ @@ -1671,7 +1736,7 @@ static void set_tos(void *outer_c, void *outer_v, u8 mask, u8 val) sizeof(filter.field)) static int parse_flow_attr(u32 *match_c, u32 *match_v, - const union ib_flow_spec *ib_spec) + const union ib_flow_spec *ib_spec, u32 *tag_id) { void *misc_params_c = MLX5_ADDR_OF(fte_match_param, match_c, misc_parameters); @@ -1695,7 +1760,7 @@ static int parse_flow_attr(u32 *match_c, u32 *match_v, switch (ib_spec->type & ~IB_FLOW_SPEC_INNER) { case IB_FLOW_SPEC_ETH: if (FIELDS_NOT_SUPPORTED(ib_spec->eth.mask, LAST_ETH_FIELD)) - return -ENOTSUPP; + return -EOPNOTSUPP; ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, dmac_47_16), @@ -1743,7 +1808,7 @@ static int parse_flow_attr(u32 *match_c, u32 *match_v, break; case IB_FLOW_SPEC_IPV4: if (FIELDS_NOT_SUPPORTED(ib_spec->ipv4.mask, LAST_IPV4_FIELD)) - return -ENOTSUPP; + return -EOPNOTSUPP; MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype, 0xffff); @@ -1775,7 +1840,7 @@ static int parse_flow_attr(u32 *match_c, u32 *match_v, break; case IB_FLOW_SPEC_IPV6: if (FIELDS_NOT_SUPPORTED(ib_spec->ipv6.mask, LAST_IPV6_FIELD)) - return -ENOTSUPP; + return -EOPNOTSUPP; MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype, 0xffff); @@ -1816,7 +1881,7 @@ static int parse_flow_attr(u32 *match_c, u32 *match_v, case IB_FLOW_SPEC_TCP: if (FIELDS_NOT_SUPPORTED(ib_spec->tcp_udp.mask, LAST_TCP_UDP_FIELD)) - return -ENOTSUPP; + return -EOPNOTSUPP; MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, 0xff); @@ -1836,7 +1901,7 @@ static int parse_flow_attr(u32 *match_c, u32 *match_v, case IB_FLOW_SPEC_UDP: if (FIELDS_NOT_SUPPORTED(ib_spec->tcp_udp.mask, LAST_TCP_UDP_FIELD)) - return -ENOTSUPP; + return -EOPNOTSUPP; MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol, 0xff); @@ -1856,13 +1921,22 @@ static int parse_flow_attr(u32 *match_c, u32 *match_v, case IB_FLOW_SPEC_VXLAN_TUNNEL: if (FIELDS_NOT_SUPPORTED(ib_spec->tunnel.mask, LAST_TUNNEL_FIELD)) - return -ENOTSUPP; + return -EOPNOTSUPP; MLX5_SET(fte_match_set_misc, misc_params_c, vxlan_vni, ntohl(ib_spec->tunnel.mask.tunnel_id)); MLX5_SET(fte_match_set_misc, misc_params_v, vxlan_vni, ntohl(ib_spec->tunnel.val.tunnel_id)); break; + case IB_FLOW_SPEC_ACTION_TAG: + if (FIELDS_NOT_SUPPORTED(ib_spec->flow_tag, + LAST_FLOW_TAG_FIELD)) + return -EOPNOTSUPP; + if (ib_spec->flow_tag.tag_id >= BIT(24)) + return -EINVAL; + + *tag_id = ib_spec->flow_tag.tag_id; + break; default: return -EINVAL; } @@ -2046,6 +2120,7 @@ static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev, struct mlx5_flow_spec *spec; const void *ib_flow = (const void *)flow_attr + sizeof(*flow_attr); unsigned int spec_index; + u32 flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; int err = 0; if (!is_valid_attr(flow_attr)) @@ -2062,7 +2137,7 @@ static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev, for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) { err = parse_flow_attr(spec->match_criteria, - spec->match_value, ib_flow); + spec->match_value, ib_flow, &flow_tag); if (err < 0) goto free; @@ -2072,7 +2147,16 @@ static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev, spec->match_criteria_enable = get_match_criteria_enable(spec->match_criteria); flow_act.action = dst ? MLX5_FLOW_CONTEXT_ACTION_FWD_DEST : MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO; - flow_act.flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; + + if (flow_tag != MLX5_FS_DEFAULT_FLOW_TAG && + (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT || + flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT)) { + mlx5_ib_warn(dev, "Flow tag %u and attribute type %x isn't allowed in leftovers\n", + flow_tag, flow_attr->type); + err = -EINVAL; + goto free; + } + flow_act.flow_tag = flow_tag; handler->rule = mlx5_add_flow_rules(ft, spec, &flow_act, dst, 1); @@ -2542,6 +2626,35 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, ibdev->ib_active = false; } +static int set_has_smi_cap(struct mlx5_ib_dev *dev) +{ + struct mlx5_hca_vport_context vport_ctx; + int err; + int port; + + for (port = 1; port <= MLX5_CAP_GEN(dev->mdev, num_ports); port++) { + dev->mdev->port_caps[port - 1].has_smi = false; + if (MLX5_CAP_GEN(dev->mdev, port_type) == + MLX5_CAP_PORT_TYPE_IB) { + if (MLX5_CAP_GEN(dev->mdev, ib_virt)) { + err = mlx5_query_hca_vport_context(dev->mdev, 0, + port, 0, + &vport_ctx); + if (err) { + mlx5_ib_err(dev, "query_hca_vport_context for port=%d failed %d\n", + port, err); + return err; + } + dev->mdev->port_caps[port - 1].has_smi = + vport_ctx.has_smi; + } else { + dev->mdev->port_caps[port - 1].has_smi = true; + } + } + } + return 0; +} + static void get_ext_port_caps(struct mlx5_ib_dev *dev) { int port; @@ -2566,6 +2679,10 @@ static int get_port_caps(struct mlx5_ib_dev *dev) if (!dprops) goto out; + err = set_has_smi_cap(dev); + if (err) + goto out; + err = mlx5_ib_query_device(&dev->ib_dev, dprops, &uhw); if (err) { mlx5_ib_warn(dev, "query_device failed %d\n", err); @@ -2573,6 +2690,7 @@ static int get_port_caps(struct mlx5_ib_dev *dev) } for (port = 1; port <= MLX5_CAP_GEN(dev->mdev, num_ports); port++) { + memset(pprops, 0, sizeof(*pprops)); err = mlx5_ib_query_port(&dev->ib_dev, port, pprops); if (err) { mlx5_ib_warn(dev, "query_port %d failed %d\n", @@ -2867,11 +2985,13 @@ static u32 get_core_cap_flags(struct ib_device *ibdev) if (ll == IB_LINK_LAYER_INFINIBAND) return RDMA_CORE_PORT_IBA_IB; + ret = RDMA_CORE_PORT_RAW_PACKET; + if (!(l3_type_cap & MLX5_ROCE_L3_TYPE_IPV4_CAP)) - return 0; + return ret; if (!(l3_type_cap & MLX5_ROCE_L3_TYPE_IPV6_CAP)) - return 0; + return ret; if (roce_version_cap & MLX5_ROCE_VERSION_1_CAP) ret |= RDMA_CORE_PORT_IBA_ROCE; @@ -2890,7 +3010,9 @@ static int mlx5_port_immutable(struct ib_device *ibdev, u8 port_num, enum rdma_link_layer ll = mlx5_ib_port_link_layer(ibdev, port_num); int err; - err = mlx5_ib_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = get_core_cap_flags(ibdev); + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; @@ -3011,13 +3133,102 @@ static void mlx5_disable_eth(struct mlx5_ib_dev *dev) mlx5_nic_vport_disable_roce(dev->mdev); } +struct mlx5_ib_q_counter { + const char *name; + size_t offset; +}; + +#define INIT_Q_COUNTER(_name) \ + { .name = #_name, .offset = MLX5_BYTE_OFF(query_q_counter_out, _name)} + +static const struct mlx5_ib_q_counter basic_q_cnts[] = { + INIT_Q_COUNTER(rx_write_requests), + INIT_Q_COUNTER(rx_read_requests), + INIT_Q_COUNTER(rx_atomic_requests), + INIT_Q_COUNTER(out_of_buffer), +}; + +static const struct mlx5_ib_q_counter out_of_seq_q_cnts[] = { + INIT_Q_COUNTER(out_of_sequence), +}; + +static const struct mlx5_ib_q_counter retrans_q_cnts[] = { + INIT_Q_COUNTER(duplicate_request), + INIT_Q_COUNTER(rnr_nak_retry_err), + INIT_Q_COUNTER(packet_seq_err), + INIT_Q_COUNTER(implied_nak_seq_err), + INIT_Q_COUNTER(local_ack_timeout_err), +}; + static void mlx5_ib_dealloc_q_counters(struct mlx5_ib_dev *dev) { unsigned int i; - for (i = 0; i < dev->num_ports; i++) + for (i = 0; i < dev->num_ports; i++) { mlx5_core_dealloc_q_counter(dev->mdev, - dev->port[i].q_cnt_id); + dev->port[i].q_cnts.set_id); + kfree(dev->port[i].q_cnts.names); + kfree(dev->port[i].q_cnts.offsets); + } +} + +static int __mlx5_ib_alloc_q_counters(struct mlx5_ib_dev *dev, + const char ***names, + size_t **offsets, + u32 *num) +{ + u32 num_counters; + + num_counters = ARRAY_SIZE(basic_q_cnts); + + if (MLX5_CAP_GEN(dev->mdev, out_of_seq_cnt)) + num_counters += ARRAY_SIZE(out_of_seq_q_cnts); + + if (MLX5_CAP_GEN(dev->mdev, retransmission_q_counters)) + num_counters += ARRAY_SIZE(retrans_q_cnts); + + *names = kcalloc(num_counters, sizeof(**names), GFP_KERNEL); + if (!*names) + return -ENOMEM; + + *offsets = kcalloc(num_counters, sizeof(**offsets), GFP_KERNEL); + if (!*offsets) + goto err_names; + + *num = num_counters; + + return 0; + +err_names: + kfree(*names); + return -ENOMEM; +} + +static void mlx5_ib_fill_q_counters(struct mlx5_ib_dev *dev, + const char **names, + size_t *offsets) +{ + int i; + int j = 0; + + for (i = 0; i < ARRAY_SIZE(basic_q_cnts); i++, j++) { + names[j] = basic_q_cnts[i].name; + offsets[j] = basic_q_cnts[i].offset; + } + + if (MLX5_CAP_GEN(dev->mdev, out_of_seq_cnt)) { + for (i = 0; i < ARRAY_SIZE(out_of_seq_q_cnts); i++, j++) { + names[j] = out_of_seq_q_cnts[i].name; + offsets[j] = out_of_seq_q_cnts[i].offset; + } + } + + if (MLX5_CAP_GEN(dev->mdev, retransmission_q_counters)) { + for (i = 0; i < ARRAY_SIZE(retrans_q_cnts); i++, j++) { + names[j] = retrans_q_cnts[i].name; + offsets[j] = retrans_q_cnts[i].offset; + } + } } static int mlx5_ib_alloc_q_counters(struct mlx5_ib_dev *dev) @@ -3026,14 +3237,26 @@ static int mlx5_ib_alloc_q_counters(struct mlx5_ib_dev *dev) int ret; for (i = 0; i < dev->num_ports; i++) { + struct mlx5_ib_port *port = &dev->port[i]; + ret = mlx5_core_alloc_q_counter(dev->mdev, - &dev->port[i].q_cnt_id); + &port->q_cnts.set_id); if (ret) { mlx5_ib_warn(dev, "couldn't allocate queue counter for port %d, err %d\n", i + 1, ret); goto dealloc_counters; } + + ret = __mlx5_ib_alloc_q_counters(dev, + &port->q_cnts.names, + &port->q_cnts.offsets, + &port->q_cnts.num_counters); + if (ret) + goto dealloc_counters; + + mlx5_ib_fill_q_counters(dev, port->q_cnts.names, + port->q_cnts.offsets); } return 0; @@ -3041,62 +3264,39 @@ static int mlx5_ib_alloc_q_counters(struct mlx5_ib_dev *dev) dealloc_counters: while (--i >= 0) mlx5_core_dealloc_q_counter(dev->mdev, - dev->port[i].q_cnt_id); + dev->port[i].q_cnts.set_id); return ret; } -static const char * const names[] = { - "rx_write_requests", - "rx_read_requests", - "rx_atomic_requests", - "out_of_buffer", - "out_of_sequence", - "duplicate_request", - "rnr_nak_retry_err", - "packet_seq_err", - "implied_nak_seq_err", - "local_ack_timeout_err", -}; - -static const size_t stats_offsets[] = { - MLX5_BYTE_OFF(query_q_counter_out, rx_write_requests), - MLX5_BYTE_OFF(query_q_counter_out, rx_read_requests), - MLX5_BYTE_OFF(query_q_counter_out, rx_atomic_requests), - MLX5_BYTE_OFF(query_q_counter_out, out_of_buffer), - MLX5_BYTE_OFF(query_q_counter_out, out_of_sequence), - MLX5_BYTE_OFF(query_q_counter_out, duplicate_request), - MLX5_BYTE_OFF(query_q_counter_out, rnr_nak_retry_err), - MLX5_BYTE_OFF(query_q_counter_out, packet_seq_err), - MLX5_BYTE_OFF(query_q_counter_out, implied_nak_seq_err), - MLX5_BYTE_OFF(query_q_counter_out, local_ack_timeout_err), -}; - static struct rdma_hw_stats *mlx5_ib_alloc_hw_stats(struct ib_device *ibdev, u8 port_num) { - BUILD_BUG_ON(ARRAY_SIZE(names) != ARRAY_SIZE(stats_offsets)); + struct mlx5_ib_dev *dev = to_mdev(ibdev); + struct mlx5_ib_port *port = &dev->port[port_num - 1]; /* We support only per port stats */ if (port_num == 0) return NULL; - return rdma_alloc_hw_stats_struct(names, ARRAY_SIZE(names), + return rdma_alloc_hw_stats_struct(port->q_cnts.names, + port->q_cnts.num_counters, RDMA_HW_STATS_DEFAULT_LIFESPAN); } static int mlx5_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats, - u8 port, int index) + u8 port_num, int index) { struct mlx5_ib_dev *dev = to_mdev(ibdev); + struct mlx5_ib_port *port = &dev->port[port_num - 1]; int outlen = MLX5_ST_SZ_BYTES(query_q_counter_out); void *out; __be32 val; int ret; int i; - if (!port || !stats) + if (!stats) return -ENOSYS; out = mlx5_vzalloc(outlen); @@ -3104,18 +3304,19 @@ static int mlx5_ib_get_hw_stats(struct ib_device *ibdev, return -ENOMEM; ret = mlx5_core_query_q_counter(dev->mdev, - dev->port[port - 1].q_cnt_id, 0, + port->q_cnts.set_id, 0, out, outlen); if (ret) goto free; - for (i = 0; i < ARRAY_SIZE(names); i++) { - val = *(__be32 *)(out + stats_offsets[i]); + for (i = 0; i < port->q_cnts.num_counters; i++) { + val = *(__be32 *)(out + port->q_cnts.offsets[i]); stats->value[i] = (u64)be32_to_cpu(val); } + free: kvfree(out); - return ARRAY_SIZE(names); + return port->q_cnts.num_counters; } static void *mlx5_ib_add(struct mlx5_core_dev *mdev) @@ -3164,7 +3365,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) dev->ib_dev.phys_port_cnt = dev->num_ports; dev->ib_dev.num_comp_vectors = dev->mdev->priv.eq_table.num_comp_vectors; - dev->ib_dev.dma_device = &mdev->pdev->dev; + dev->ib_dev.dev.parent = &mdev->pdev->dev; dev->ib_dev.uverbs_abi_ver = MLX5_IB_UVERBS_ABI_VERSION; dev->ib_dev.uverbs_cmd_mask = @@ -3267,8 +3468,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) (1ull << IB_USER_VERBS_CMD_DEALLOC_MW); } - if (MLX5_CAP_GEN(dev->mdev, out_of_seq_cnt) && - MLX5_CAP_GEN(dev->mdev, retransmission_q_counters)) { + if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt)) { dev->ib_dev.get_hw_stats = mlx5_ib_get_hw_stats; dev->ib_dev.alloc_hw_stats = mlx5_ib_alloc_hw_stats; } @@ -3322,9 +3522,11 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) if (err) goto err_rsrc; - err = mlx5_ib_alloc_q_counters(dev); - if (err) - goto err_odp; + if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt)) { + err = mlx5_ib_alloc_q_counters(dev); + if (err) + goto err_odp; + } dev->mdev->priv.uar = mlx5_get_uars_page(dev->mdev); if (!dev->mdev->priv.uar) @@ -3373,7 +3575,8 @@ err_uar_page: mlx5_put_uars_page(dev->mdev, dev->mdev->priv.uar); err_q_cnt: - mlx5_ib_dealloc_q_counters(dev); + if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt)) + mlx5_ib_dealloc_q_counters(dev); err_odp: mlx5_ib_odp_remove_one(dev); @@ -3406,7 +3609,8 @@ static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context) mlx5_free_bfreg(dev->mdev, &dev->fp_bfreg); mlx5_free_bfreg(dev->mdev, &dev->bfreg); mlx5_put_uars_page(dev->mdev, mdev->priv.uar); - mlx5_ib_dealloc_q_counters(dev); + if (MLX5_CAP_GEN(dev->mdev, max_qp_cnt)) + mlx5_ib_dealloc_q_counters(dev); destroy_umrc_res(dev); mlx5_ib_odp_remove_one(dev); destroy_dev_resources(&dev->devr); @@ -3430,8 +3634,7 @@ static int __init mlx5_ib_init(void) { int err; - if (deprecated_prof_sel != 2) - pr_warn("prof_sel is deprecated for mlx5_ib, set it for mlx5_core\n"); + mlx5_ib_odp_init(); err = mlx5_register_interface(&mlx5_ib_interface); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index e1a4b93dce6b..3cd064b5f0bf 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -202,6 +202,7 @@ struct mlx5_ib_flow_db { #define MLX5_IB_UPD_XLT_ADDR BIT(3) #define MLX5_IB_UPD_XLT_PD BIT(4) #define MLX5_IB_UPD_XLT_ACCESS BIT(5) +#define MLX5_IB_UPD_XLT_INDIRECT BIT(6) /* Private QP creation flags to be passed in ib_qp_init_attr.create_flags. * @@ -220,6 +221,10 @@ struct wr_list { u16 next; }; +enum mlx5_ib_rq_flags { + MLX5_IB_RQ_CVLAN_STRIPPING = 1 << 0, +}; + struct mlx5_ib_wq { u64 *wrid; u32 *wr_data; @@ -308,6 +313,7 @@ struct mlx5_ib_rq { struct mlx5_db *doorbell; u32 tirn; u8 state; + u32 flags; }; struct mlx5_ib_sq { @@ -392,6 +398,7 @@ enum mlx5_ib_qp_flags { MLX5_IB_QP_SQPN_QP1 = 1 << 6, MLX5_IB_QP_CAP_SCATTER_FCS = 1 << 7, MLX5_IB_QP_RSS = 1 << 8, + MLX5_IB_QP_CVLAN_STRIPPING = 1 << 9, }; struct mlx5_umr_wr { @@ -497,6 +504,10 @@ struct mlx5_ib_mr { int live; void *descs_alloc; int access_flags; /* Needed for rereg MR */ + + struct mlx5_ib_mr *parent; + atomic_t num_leaf_free; + wait_queue_head_t q_leaf_free; }; struct mlx5_ib_mw { @@ -535,6 +546,10 @@ struct mlx5_cache_ent { struct dentry *dir; char name[4]; u32 order; + u32 xlt; + u32 access_mode; + u32 page; + u32 size; u32 cur; u32 miss; @@ -549,6 +564,7 @@ struct mlx5_cache_ent { struct work_struct work; struct delayed_work dwork; int pending; + struct completion compl; }; struct mlx5_mr_cache { @@ -579,8 +595,15 @@ struct mlx5_ib_resources { struct mutex mutex; }; +struct mlx5_ib_q_counters { + const char **names; + size_t *offsets; + u32 num_counters; + u16 set_id; +}; + struct mlx5_ib_port { - u16 q_cnt_id; + struct mlx5_ib_q_counters q_cnts; }; struct mlx5_roce { @@ -619,6 +642,7 @@ struct mlx5_ib_dev { * being used by a page fault handler. */ struct srcu_struct mr_srcu; + u32 null_mkey; #endif struct mlx5_ib_flow_db flow_db; /* protect resources needed as part of reset flow */ @@ -771,6 +795,9 @@ struct ib_mw *mlx5_ib_alloc_mw(struct ib_pd *pd, enum ib_mw_type type, int mlx5_ib_dealloc_mw(struct ib_mw *mw); int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int page_shift, int flags); +struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd, + int access_flags); +void mlx5_ib_free_implicit_mr(struct mlx5_ib_mr *mr); int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, u64 length, u64 virt_addr, int access_flags, struct ib_pd *pd, struct ib_udata *udata); @@ -824,7 +851,9 @@ void mlx5_ib_copy_pas(u64 *old, u64 *new, int step, int num); int mlx5_ib_get_cqe_size(struct mlx5_ib_dev *dev, struct ib_cq *ibcq); int mlx5_mr_cache_init(struct mlx5_ib_dev *dev); int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev); -int mlx5_mr_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift); + +struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, int entry); +void mlx5_mr_cache_free(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr); int mlx5_ib_check_mr_status(struct ib_mr *ibmr, u32 check_mask, struct ib_mr_status *mr_status); struct ib_wq *mlx5_ib_create_wq(struct ib_pd *pd, @@ -848,6 +877,9 @@ int __init mlx5_ib_odp_init(void); void mlx5_ib_odp_cleanup(void); void mlx5_ib_invalidate_range(struct ib_umem *umem, unsigned long start, unsigned long end); +void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent); +void mlx5_odp_populate_klm(struct mlx5_klm *pklm, size_t offset, + size_t nentries, struct mlx5_ib_mr *mr, int flags); #else /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */ static inline void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) { @@ -855,9 +887,13 @@ static inline void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) } static inline int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev) { return 0; } -static inline void mlx5_ib_odp_remove_one(struct mlx5_ib_dev *ibdev) {} +static inline void mlx5_ib_odp_remove_one(struct mlx5_ib_dev *ibdev) {} static inline int mlx5_ib_odp_init(void) { return 0; } -static inline void mlx5_ib_odp_cleanup(void) {} +static inline void mlx5_ib_odp_cleanup(void) {} +static inline void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent) {} +static inline void mlx5_odp_populate_klm(struct mlx5_klm *pklm, size_t offset, + size_t nentries, struct mlx5_ib_mr *mr, + int flags) {} #endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */ @@ -872,6 +908,8 @@ int mlx5_ib_set_vf_guid(struct ib_device *device, int vf, u8 port, __be16 mlx5_get_roce_udp_sport(struct mlx5_ib_dev *dev, u8 port_num, int index); +int mlx5_get_roce_gid_type(struct mlx5_ib_dev *dev, u8 port_num, + int index, enum ib_gid_type *gid_type); /* GSI QP helper functions */ struct ib_qp *mlx5_ib_gsi_create_qp(struct ib_pd *pd, diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 8cf2a67f9fb0..b8f9382a8b7d 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -49,6 +49,7 @@ enum { static int clean_mr(struct mlx5_ib_mr *mr); static int use_umr(struct mlx5_ib_dev *dev, int order); +static int unreg_umr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr); static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) { @@ -149,6 +150,9 @@ static void reg_mr_callback(int status, void *context) if (err) pr_err("Error inserting to mkey tree. 0x%x\n", -err); write_unlock_irqrestore(&table->lock, flags); + + if (!completion_done(&ent->compl)) + complete(&ent->compl); } static int add_keys(struct mlx5_ib_dev *dev, int c, int num) @@ -157,7 +161,6 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num) struct mlx5_cache_ent *ent = &cache->ent[c]; int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); struct mlx5_ib_mr *mr; - int npages = 1 << ent->order; void *mkc; u32 *in; int err = 0; @@ -185,11 +188,11 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num) MLX5_SET(mkc, mkc, free, 1); MLX5_SET(mkc, mkc, umr_en, 1); - MLX5_SET(mkc, mkc, access_mode, MLX5_MKC_ACCESS_MODE_MTT); + MLX5_SET(mkc, mkc, access_mode, ent->access_mode); MLX5_SET(mkc, mkc, qpn, 0xffffff); - MLX5_SET(mkc, mkc, translations_octword_size, (npages + 1) / 2); - MLX5_SET(mkc, mkc, log_page_size, 12); + MLX5_SET(mkc, mkc, translations_octword_size, ent->xlt); + MLX5_SET(mkc, mkc, log_page_size, ent->page); spin_lock_irq(&ent->lock); ent->pending++; @@ -447,6 +450,42 @@ static void cache_work_func(struct work_struct *work) __cache_work_func(ent); } +struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, int entry) +{ + struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_cache_ent *ent; + struct mlx5_ib_mr *mr; + int err; + + if (entry < 0 || entry >= MAX_MR_CACHE_ENTRIES) { + mlx5_ib_err(dev, "cache entry %d is out of range\n", entry); + return NULL; + } + + ent = &cache->ent[entry]; + while (1) { + spin_lock_irq(&ent->lock); + if (list_empty(&ent->head)) { + spin_unlock_irq(&ent->lock); + + err = add_keys(dev, entry, 1); + if (err && err != -EAGAIN) + return ERR_PTR(err); + + wait_for_completion(&ent->compl); + } else { + mr = list_first_entry(&ent->head, struct mlx5_ib_mr, + list); + list_del(&mr->list); + ent->cur--; + spin_unlock_irq(&ent->lock); + if (ent->cur < ent->limit) + queue_work(cache->wq, &ent->work); + return mr; + } + } +} + static struct mlx5_ib_mr *alloc_cached_mr(struct mlx5_ib_dev *dev, int order) { struct mlx5_mr_cache *cache = &dev->cache; @@ -456,12 +495,12 @@ static struct mlx5_ib_mr *alloc_cached_mr(struct mlx5_ib_dev *dev, int order) int i; c = order2idx(dev, order); - if (c < 0 || c >= MAX_MR_CACHE_ENTRIES) { + if (c < 0 || c > MAX_UMR_CACHE_ENTRY) { mlx5_ib_warn(dev, "order %d, cache index %d\n", order, c); return NULL; } - for (i = c; i < MAX_MR_CACHE_ENTRIES; i++) { + for (i = c; i < MAX_UMR_CACHE_ENTRY; i++) { ent = &cache->ent[i]; mlx5_ib_dbg(dev, "order %d, cache index %d\n", ent->order, i); @@ -488,7 +527,7 @@ static struct mlx5_ib_mr *alloc_cached_mr(struct mlx5_ib_dev *dev, int order) return mr; } -static void free_cached_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) +void mlx5_mr_cache_free(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) { struct mlx5_mr_cache *cache = &dev->cache; struct mlx5_cache_ent *ent; @@ -500,6 +539,10 @@ static void free_cached_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) mlx5_ib_warn(dev, "order %d, cache index %d\n", mr->order, c); return; } + + if (unreg_umr(dev, mr)) + return; + ent = &cache->ent[c]; spin_lock_irq(&ent->lock); list_add_tail(&mr->list, &ent->head); @@ -602,7 +645,6 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) { struct mlx5_mr_cache *cache = &dev->cache; struct mlx5_cache_ent *ent; - int limit; int err; int i; @@ -615,26 +657,35 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) setup_timer(&dev->delay_timer, delay_time_func, (unsigned long)dev); for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { - INIT_LIST_HEAD(&cache->ent[i].head); - spin_lock_init(&cache->ent[i].lock); - ent = &cache->ent[i]; INIT_LIST_HEAD(&ent->head); spin_lock_init(&ent->lock); ent->order = i + 2; ent->dev = dev; + ent->limit = 0; - if ((dev->mdev->profile->mask & MLX5_PROF_MASK_MR_CACHE) && - mlx5_core_is_pf(dev->mdev) && - use_umr(dev, ent->order)) - limit = dev->mdev->profile->mr_cache[i].limit; - else - limit = 0; - + init_completion(&ent->compl); INIT_WORK(&ent->work, cache_work_func); INIT_DELAYED_WORK(&ent->dwork, delayed_cache_work_func); - ent->limit = limit; queue_work(cache->wq, &ent->work); + + if (i > MAX_UMR_CACHE_ENTRY) { + mlx5_odp_init_mr_cache_entry(ent); + continue; + } + + if (!use_umr(dev, ent->order)) + continue; + + ent->page = PAGE_SHIFT; + ent->xlt = (1 << ent->order) * sizeof(struct mlx5_mtt) / + MLX5_IB_UMR_OCTOWORD; + ent->access_mode = MLX5_MKC_ACCESS_MODE_MTT; + if ((dev->mdev->profile->mask & MLX5_PROF_MASK_MR_CACHE) && + mlx5_core_is_pf(dev->mdev)) + ent->limit = dev->mdev->profile->mr_cache[i].limit; + else + ent->limit = 0; } err = mlx5_mr_cache_debugfs_init(dev); @@ -758,7 +809,7 @@ static int get_octo_len(u64 addr, u64 len, int page_size) static int use_umr(struct mlx5_ib_dev *dev, int order) { if (MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) - return order < MAX_MR_CACHE_ENTRIES + 2; + return order <= MAX_UMR_CACHE_ENTRY + 2; return order <= MLX5_MAX_UMR_SHIFT; } @@ -871,7 +922,7 @@ static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem, MLX5_IB_UPD_XLT_ENABLE); if (err) { - free_cached_mr(dev, mr); + mlx5_mr_cache_free(dev, mr); return ERR_PTR(err); } @@ -886,6 +937,10 @@ static inline int populate_xlt(struct mlx5_ib_mr *mr, int idx, int npages, { struct mlx5_ib_dev *dev = mr->dev; struct ib_umem *umem = mr->umem; + if (flags & MLX5_IB_UPD_XLT_INDIRECT) { + mlx5_odp_populate_klm(xlt, idx, npages, mr, flags); + return npages; + } npages = min_t(size_t, npages, ib_umem_num_pages(umem) - idx); @@ -911,7 +966,7 @@ int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, int page_shift, int flags) { struct mlx5_ib_dev *dev = mr->dev; - struct device *ddev = dev->ib_dev.dma_device; + struct device *ddev = dev->ib_dev.dev.parent; struct mlx5_ib_ucontext *uctx = NULL; int size; void *xlt; @@ -919,7 +974,9 @@ int mlx5_ib_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages, struct mlx5_umr_wr wr; struct ib_sge sg; int err = 0; - int desc_size = sizeof(struct mlx5_mtt); + int desc_size = (flags & MLX5_IB_UPD_XLT_INDIRECT) + ? sizeof(struct mlx5_klm) + : sizeof(struct mlx5_mtt); const int page_align = MLX5_UMR_MTT_ALIGNMENT / desc_size; const int page_mask = page_align - 1; size_t pages_mapped = 0; @@ -1091,6 +1148,7 @@ static struct mlx5_ib_mr *reg_create(struct ib_mr *ibmr, struct ib_pd *pd, goto err_2; } mr->mmkey.type = MLX5_MKEY_MR; + mr->desc_size = sizeof(struct mlx5_mtt); mr->umem = umem; mr->dev = dev; mr->live = 1; @@ -1136,6 +1194,18 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mlx5_ib_dbg(dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx, access_flags 0x%x\n", start, virt_addr, length, access_flags); + +#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING + if (!start && length == U64_MAX) { + if (!(access_flags & IB_ACCESS_ON_DEMAND) || + !(dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) + return ERR_PTR(-EINVAL); + + mr = mlx5_ib_alloc_implicit_mr(to_mpd(pd), access_flags); + return &mr->ibmr; + } +#endif + err = mr_umem_get(pd, start, length, access_flags, &umem, &npages, &page_shift, &ncont, &order); @@ -1341,9 +1411,9 @@ mlx5_alloc_priv_descs(struct ib_device *device, mr->descs = PTR_ALIGN(mr->descs_alloc, MLX5_UMR_ALIGN); - mr->desc_map = dma_map_single(device->dma_device, mr->descs, + mr->desc_map = dma_map_single(device->dev.parent, mr->descs, size, DMA_TO_DEVICE); - if (dma_mapping_error(device->dma_device, mr->desc_map)) { + if (dma_mapping_error(device->dev.parent, mr->desc_map)) { ret = -ENOMEM; goto err; } @@ -1362,7 +1432,7 @@ mlx5_free_priv_descs(struct mlx5_ib_mr *mr) struct ib_device *device = mr->ibmr.device; int size = mr->max_descs * mr->desc_size; - dma_unmap_single(device->dma_device, mr->desc_map, + dma_unmap_single(device->dev.parent, mr->desc_map, size, DMA_TO_DEVICE); kfree(mr->descs_alloc); mr->descs = NULL; @@ -1398,12 +1468,7 @@ static int clean_mr(struct mlx5_ib_mr *mr) return err; } } else { - err = unreg_umr(dev, mr); - if (err) { - mlx5_ib_warn(dev, "failed unregister\n"); - return err; - } - free_cached_mr(dev, mr); + mlx5_mr_cache_free(dev, mr); } if (!umred) @@ -1426,8 +1491,11 @@ int mlx5_ib_dereg_mr(struct ib_mr *ibmr) /* Wait for all running page-fault handlers to finish. */ synchronize_srcu(&dev->mr_srcu); /* Destroy all page mappings */ - mlx5_ib_invalidate_range(umem, ib_umem_start(umem), - ib_umem_end(umem)); + if (umem->odp_data->page_list) + mlx5_ib_invalidate_range(umem, ib_umem_start(umem), + ib_umem_end(umem)); + else + mlx5_ib_free_implicit_mr(mr); /* * We kill the umem before the MR for ODP, * so that there will not be any invalidations in diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index e5bc267aca73..d7b12f0750e2 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -34,6 +34,7 @@ #include <rdma/ib_umem_odp.h> #include "mlx5_ib.h" +#include "cmd.h" #define MAX_PREFETCH_LEN (4*1024*1024U) @@ -41,6 +42,140 @@ * a pagefault. */ #define MMU_NOTIFIER_TIMEOUT 1000 +#define MLX5_IMR_MTT_BITS (30 - PAGE_SHIFT) +#define MLX5_IMR_MTT_SHIFT (MLX5_IMR_MTT_BITS + PAGE_SHIFT) +#define MLX5_IMR_MTT_ENTRIES BIT_ULL(MLX5_IMR_MTT_BITS) +#define MLX5_IMR_MTT_SIZE BIT_ULL(MLX5_IMR_MTT_SHIFT) +#define MLX5_IMR_MTT_MASK (~(MLX5_IMR_MTT_SIZE - 1)) + +#define MLX5_KSM_PAGE_SHIFT MLX5_IMR_MTT_SHIFT + +static u64 mlx5_imr_ksm_entries; + +static int check_parent(struct ib_umem_odp *odp, + struct mlx5_ib_mr *parent) +{ + struct mlx5_ib_mr *mr = odp->private; + + return mr && mr->parent == parent; +} + +static struct ib_umem_odp *odp_next(struct ib_umem_odp *odp) +{ + struct mlx5_ib_mr *mr = odp->private, *parent = mr->parent; + struct ib_ucontext *ctx = odp->umem->context; + struct rb_node *rb; + + down_read(&ctx->umem_rwsem); + while (1) { + rb = rb_next(&odp->interval_tree.rb); + if (!rb) + goto not_found; + odp = rb_entry(rb, struct ib_umem_odp, interval_tree.rb); + if (check_parent(odp, parent)) + goto end; + } +not_found: + odp = NULL; +end: + up_read(&ctx->umem_rwsem); + return odp; +} + +static struct ib_umem_odp *odp_lookup(struct ib_ucontext *ctx, + u64 start, u64 length, + struct mlx5_ib_mr *parent) +{ + struct ib_umem_odp *odp; + struct rb_node *rb; + + down_read(&ctx->umem_rwsem); + odp = rbt_ib_umem_lookup(&ctx->umem_tree, start, length); + if (!odp) + goto end; + + while (1) { + if (check_parent(odp, parent)) + goto end; + rb = rb_next(&odp->interval_tree.rb); + if (!rb) + goto not_found; + odp = rb_entry(rb, struct ib_umem_odp, interval_tree.rb); + if (ib_umem_start(odp->umem) > start + length) + goto not_found; + } +not_found: + odp = NULL; +end: + up_read(&ctx->umem_rwsem); + return odp; +} + +void mlx5_odp_populate_klm(struct mlx5_klm *pklm, size_t offset, + size_t nentries, struct mlx5_ib_mr *mr, int flags) +{ + struct ib_pd *pd = mr->ibmr.pd; + struct ib_ucontext *ctx = pd->uobject->context; + struct mlx5_ib_dev *dev = to_mdev(pd->device); + struct ib_umem_odp *odp; + unsigned long va; + int i; + + if (flags & MLX5_IB_UPD_XLT_ZAP) { + for (i = 0; i < nentries; i++, pklm++) { + pklm->bcount = cpu_to_be32(MLX5_IMR_MTT_SIZE); + pklm->key = cpu_to_be32(dev->null_mkey); + pklm->va = 0; + } + return; + } + + odp = odp_lookup(ctx, offset * MLX5_IMR_MTT_SIZE, + nentries * MLX5_IMR_MTT_SIZE, mr); + + for (i = 0; i < nentries; i++, pklm++) { + pklm->bcount = cpu_to_be32(MLX5_IMR_MTT_SIZE); + va = (offset + i) * MLX5_IMR_MTT_SIZE; + if (odp && odp->umem->address == va) { + struct mlx5_ib_mr *mtt = odp->private; + + pklm->key = cpu_to_be32(mtt->ibmr.lkey); + odp = odp_next(odp); + } else { + pklm->key = cpu_to_be32(dev->null_mkey); + } + mlx5_ib_dbg(dev, "[%d] va %lx key %x\n", + i, va, be32_to_cpu(pklm->key)); + } +} + +static void mr_leaf_free_action(struct work_struct *work) +{ + struct ib_umem_odp *odp = container_of(work, struct ib_umem_odp, work); + int idx = ib_umem_start(odp->umem) >> MLX5_IMR_MTT_SHIFT; + struct mlx5_ib_mr *mr = odp->private, *imr = mr->parent; + + mr->parent = NULL; + synchronize_srcu(&mr->dev->mr_srcu); + + if (!READ_ONCE(odp->dying)) { + mr->parent = imr; + if (atomic_dec_and_test(&imr->num_leaf_free)) + wake_up(&imr->q_leaf_free); + return; + } + + ib_umem_release(odp->umem); + if (imr->live) + mlx5_ib_update_xlt(imr, idx, 1, 0, + MLX5_IB_UPD_XLT_INDIRECT | + MLX5_IB_UPD_XLT_ATOMIC); + mlx5_mr_cache_free(mr->dev, mr); + + if (atomic_dec_and_test(&imr->num_leaf_free)) + wake_up(&imr->q_leaf_free); +} + void mlx5_ib_invalidate_range(struct ib_umem *umem, unsigned long start, unsigned long end) { @@ -111,6 +246,13 @@ void mlx5_ib_invalidate_range(struct ib_umem *umem, unsigned long start, */ ib_umem_odp_unmap_dma_pages(umem, start, end); + + if (unlikely(!umem->npages && mr->parent && + !umem->odp_data->dying)) { + WRITE_ONCE(umem->odp_data->dying, 1); + atomic_inc(&mr->parent->num_leaf_free); + schedule_work(&umem->odp_data->work); + } } void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) @@ -147,6 +289,11 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) if (MLX5_CAP_ODP(dev->mdev, rc_odp_caps.atomic)) caps->per_transport_caps.rc_odp_caps |= IB_ODP_SUPPORT_ATOMIC; + if (MLX5_CAP_GEN(dev->mdev, fixed_buffer_size) && + MLX5_CAP_GEN(dev->mdev, null_mkey) && + MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) + caps->general_caps |= IB_ODP_SUPPORT_IMPLICIT; + return; } @@ -184,6 +331,197 @@ static void mlx5_ib_page_fault_resume(struct mlx5_ib_dev *dev, wq_num); } +static struct mlx5_ib_mr *implicit_mr_alloc(struct ib_pd *pd, + struct ib_umem *umem, + bool ksm, int access_flags) +{ + struct mlx5_ib_dev *dev = to_mdev(pd->device); + struct mlx5_ib_mr *mr; + int err; + + mr = mlx5_mr_cache_alloc(dev, ksm ? MLX5_IMR_KSM_CACHE_ENTRY : + MLX5_IMR_MTT_CACHE_ENTRY); + + if (IS_ERR(mr)) + return mr; + + mr->ibmr.pd = pd; + + mr->dev = dev; + mr->access_flags = access_flags; + mr->mmkey.iova = 0; + mr->umem = umem; + + if (ksm) { + err = mlx5_ib_update_xlt(mr, 0, + mlx5_imr_ksm_entries, + MLX5_KSM_PAGE_SHIFT, + MLX5_IB_UPD_XLT_INDIRECT | + MLX5_IB_UPD_XLT_ZAP | + MLX5_IB_UPD_XLT_ENABLE); + + } else { + err = mlx5_ib_update_xlt(mr, 0, + MLX5_IMR_MTT_ENTRIES, + PAGE_SHIFT, + MLX5_IB_UPD_XLT_ZAP | + MLX5_IB_UPD_XLT_ENABLE | + MLX5_IB_UPD_XLT_ATOMIC); + } + + if (err) + goto fail; + + mr->ibmr.lkey = mr->mmkey.key; + mr->ibmr.rkey = mr->mmkey.key; + + mr->live = 1; + + mlx5_ib_dbg(dev, "key %x dev %p mr %p\n", + mr->mmkey.key, dev->mdev, mr); + + return mr; + +fail: + mlx5_ib_err(dev, "Failed to register MKEY %d\n", err); + mlx5_mr_cache_free(dev, mr); + + return ERR_PTR(err); +} + +static struct ib_umem_odp *implicit_mr_get_data(struct mlx5_ib_mr *mr, + u64 io_virt, size_t bcnt) +{ + struct ib_ucontext *ctx = mr->ibmr.pd->uobject->context; + struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.pd->device); + struct ib_umem_odp *odp, *result = NULL; + u64 addr = io_virt & MLX5_IMR_MTT_MASK; + int nentries = 0, start_idx = 0, ret; + struct mlx5_ib_mr *mtt; + struct ib_umem *umem; + + mutex_lock(&mr->umem->odp_data->umem_mutex); + odp = odp_lookup(ctx, addr, 1, mr); + + mlx5_ib_dbg(dev, "io_virt:%llx bcnt:%zx addr:%llx odp:%p\n", + io_virt, bcnt, addr, odp); + +next_mr: + if (likely(odp)) { + if (nentries) + nentries++; + } else { + umem = ib_alloc_odp_umem(ctx, addr, MLX5_IMR_MTT_SIZE); + if (IS_ERR(umem)) { + mutex_unlock(&mr->umem->odp_data->umem_mutex); + return ERR_CAST(umem); + } + + mtt = implicit_mr_alloc(mr->ibmr.pd, umem, 0, mr->access_flags); + if (IS_ERR(mtt)) { + mutex_unlock(&mr->umem->odp_data->umem_mutex); + ib_umem_release(umem); + return ERR_CAST(mtt); + } + + odp = umem->odp_data; + odp->private = mtt; + mtt->umem = umem; + mtt->mmkey.iova = addr; + mtt->parent = mr; + INIT_WORK(&odp->work, mr_leaf_free_action); + + if (!nentries) + start_idx = addr >> MLX5_IMR_MTT_SHIFT; + nentries++; + } + + odp->dying = 0; + + /* Return first odp if region not covered by single one */ + if (likely(!result)) + result = odp; + + addr += MLX5_IMR_MTT_SIZE; + if (unlikely(addr < io_virt + bcnt)) { + odp = odp_next(odp); + if (odp && odp->umem->address != addr) + odp = NULL; + goto next_mr; + } + + if (unlikely(nentries)) { + ret = mlx5_ib_update_xlt(mr, start_idx, nentries, 0, + MLX5_IB_UPD_XLT_INDIRECT | + MLX5_IB_UPD_XLT_ATOMIC); + if (ret) { + mlx5_ib_err(dev, "Failed to update PAS\n"); + result = ERR_PTR(ret); + } + } + + mutex_unlock(&mr->umem->odp_data->umem_mutex); + return result; +} + +struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd, + int access_flags) +{ + struct ib_ucontext *ctx = pd->ibpd.uobject->context; + struct mlx5_ib_mr *imr; + struct ib_umem *umem; + + umem = ib_umem_get(ctx, 0, 0, IB_ACCESS_ON_DEMAND, 0); + if (IS_ERR(umem)) + return ERR_CAST(umem); + + imr = implicit_mr_alloc(&pd->ibpd, umem, 1, access_flags); + if (IS_ERR(imr)) { + ib_umem_release(umem); + return ERR_CAST(imr); + } + + imr->umem = umem; + init_waitqueue_head(&imr->q_leaf_free); + atomic_set(&imr->num_leaf_free, 0); + + return imr; +} + +static int mr_leaf_free(struct ib_umem *umem, u64 start, + u64 end, void *cookie) +{ + struct mlx5_ib_mr *mr = umem->odp_data->private, *imr = cookie; + + if (mr->parent != imr) + return 0; + + ib_umem_odp_unmap_dma_pages(umem, + ib_umem_start(umem), + ib_umem_end(umem)); + + if (umem->odp_data->dying) + return 0; + + WRITE_ONCE(umem->odp_data->dying, 1); + atomic_inc(&imr->num_leaf_free); + schedule_work(&umem->odp_data->work); + + return 0; +} + +void mlx5_ib_free_implicit_mr(struct mlx5_ib_mr *imr) +{ + struct ib_ucontext *ctx = imr->ibmr.pd->uobject->context; + + down_read(&ctx->umem_rwsem); + rbt_ib_umem_for_each_in_range(&ctx->umem_tree, 0, ULLONG_MAX, + mr_leaf_free, imr); + up_read(&ctx->umem_rwsem); + + wait_event(imr->q_leaf_free, !atomic_read(&imr->num_leaf_free)); +} + /* * Handle a single data segment in a page-fault WQE or RDMA region. * @@ -195,47 +533,43 @@ static void mlx5_ib_page_fault_resume(struct mlx5_ib_dev *dev, * -EFAULT when there's an error mapping the requested pages. The caller will * abort the page fault handling. */ -static int pagefault_single_data_segment(struct mlx5_ib_dev *mib_dev, +static int pagefault_single_data_segment(struct mlx5_ib_dev *dev, u32 key, u64 io_virt, size_t bcnt, u32 *bytes_committed, u32 *bytes_mapped) { int srcu_key; - unsigned int current_seq; + unsigned int current_seq = 0; u64 start_idx; int npages = 0, ret = 0; struct mlx5_ib_mr *mr; u64 access_mask = ODP_READ_ALLOWED_BIT; + struct ib_umem_odp *odp; + int implicit = 0; + size_t size; - srcu_key = srcu_read_lock(&mib_dev->mr_srcu); - mr = mlx5_ib_odp_find_mr_lkey(mib_dev, key); + srcu_key = srcu_read_lock(&dev->mr_srcu); + mr = mlx5_ib_odp_find_mr_lkey(dev, key); /* * If we didn't find the MR, it means the MR was closed while we were * handling the ODP event. In this case we return -EFAULT so that the * QP will be closed. */ if (!mr || !mr->ibmr.pd) { - pr_err("Failed to find relevant mr for lkey=0x%06x, probably the MR was destroyed\n", - key); + mlx5_ib_dbg(dev, "Failed to find relevant mr for lkey=0x%06x, probably the MR was destroyed\n", + key); ret = -EFAULT; goto srcu_unlock; } if (!mr->umem->odp_data) { - pr_debug("skipping non ODP MR (lkey=0x%06x) in page fault handler.\n", - key); + mlx5_ib_dbg(dev, "skipping non ODP MR (lkey=0x%06x) in page fault handler.\n", + key); if (bytes_mapped) *bytes_mapped += (bcnt - *bytes_committed); goto srcu_unlock; } - current_seq = ACCESS_ONCE(mr->umem->odp_data->notifiers_seq); - /* - * Ensure the sequence number is valid for some time before we call - * gup. - */ - smp_rmb(); - /* * Avoid branches - this code will perform correctly * in all iterations (in iteration 2 and above, @@ -244,63 +578,109 @@ static int pagefault_single_data_segment(struct mlx5_ib_dev *mib_dev, io_virt += *bytes_committed; bcnt -= *bytes_committed; + if (!mr->umem->odp_data->page_list) { + odp = implicit_mr_get_data(mr, io_virt, bcnt); + + if (IS_ERR(odp)) { + ret = PTR_ERR(odp); + goto srcu_unlock; + } + mr = odp->private; + implicit = 1; + + } else { + odp = mr->umem->odp_data; + } + +next_mr: + current_seq = READ_ONCE(odp->notifiers_seq); + /* + * Ensure the sequence number is valid for some time before we call + * gup. + */ + smp_rmb(); + + size = min_t(size_t, bcnt, ib_umem_end(odp->umem) - io_virt); start_idx = (io_virt - (mr->mmkey.iova & PAGE_MASK)) >> PAGE_SHIFT; if (mr->umem->writable) access_mask |= ODP_WRITE_ALLOWED_BIT; - npages = ib_umem_odp_map_dma_pages(mr->umem, io_virt, bcnt, - access_mask, current_seq); - if (npages < 0) { - ret = npages; + + ret = ib_umem_odp_map_dma_pages(mr->umem, io_virt, size, + access_mask, current_seq); + + if (ret < 0) goto srcu_unlock; - } - if (npages > 0) { - mutex_lock(&mr->umem->odp_data->umem_mutex); + if (ret > 0) { + int np = ret; + + mutex_lock(&odp->umem_mutex); if (!ib_umem_mmu_notifier_retry(mr->umem, current_seq)) { /* * No need to check whether the MTTs really belong to * this MR, since ib_umem_odp_map_dma_pages already * checks this. */ - ret = mlx5_ib_update_xlt(mr, start_idx, npages, + ret = mlx5_ib_update_xlt(mr, start_idx, np, PAGE_SHIFT, MLX5_IB_UPD_XLT_ATOMIC); } else { ret = -EAGAIN; } - mutex_unlock(&mr->umem->odp_data->umem_mutex); + mutex_unlock(&odp->umem_mutex); if (ret < 0) { if (ret != -EAGAIN) - pr_err("Failed to update mkey page tables\n"); + mlx5_ib_err(dev, "Failed to update mkey page tables\n"); goto srcu_unlock; } if (bytes_mapped) { - u32 new_mappings = npages * PAGE_SIZE - + u32 new_mappings = np * PAGE_SIZE - (io_virt - round_down(io_virt, PAGE_SIZE)); - *bytes_mapped += min_t(u32, new_mappings, bcnt); + *bytes_mapped += min_t(u32, new_mappings, size); } + + npages += np; + } + + bcnt -= size; + if (unlikely(bcnt)) { + struct ib_umem_odp *next; + + io_virt += size; + next = odp_next(odp); + if (unlikely(!next || next->umem->address != io_virt)) { + mlx5_ib_dbg(dev, "next implicit leaf removed at 0x%llx. got %p\n", + io_virt, next); + ret = -EAGAIN; + goto srcu_unlock_no_wait; + } + odp = next; + mr = odp->private; + goto next_mr; } srcu_unlock: if (ret == -EAGAIN) { - if (!mr->umem->odp_data->dying) { - struct ib_umem_odp *odp_data = mr->umem->odp_data; + if (implicit || !odp->dying) { unsigned long timeout = msecs_to_jiffies(MMU_NOTIFIER_TIMEOUT); if (!wait_for_completion_timeout( - &odp_data->notifier_completion, + &odp->notifier_completion, timeout)) { - pr_warn("timeout waiting for mmu notifier completion\n"); + mlx5_ib_warn(dev, "timeout waiting for mmu notifier. seq %d against %d\n", + current_seq, odp->notifiers_seq); } } else { /* The MR is being killed, kill the QP as well. */ ret = -EFAULT; } } - srcu_read_unlock(&mib_dev->mr_srcu, srcu_key); + +srcu_unlock_no_wait: + srcu_read_unlock(&dev->mr_srcu, srcu_key); *bytes_committed = 0; return ret ? ret : npages; } @@ -618,8 +998,8 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, goto resolve_page_fault; } else if (ret < 0 || total_wqe_bytes > bytes_mapped) { if (ret != -ENOENT) - mlx5_ib_err(dev, "Error getting user pages for page fault. Error: %d\n", - ret); + mlx5_ib_err(dev, "PAGE FAULT error: %d. QP 0x%x. type: 0x%x\n", + ret, pfault->wqe.wq_num, pfault->type); goto resolve_page_fault; } @@ -627,7 +1007,7 @@ static void mlx5_ib_mr_wqe_pfault_handler(struct mlx5_ib_dev *dev, resolve_page_fault: mlx5_ib_page_fault_resume(dev, pfault, resume_with_error); mlx5_ib_dbg(dev, "PAGE FAULT completed. QP 0x%x resume_with_error=%d, type: 0x%x\n", - pfault->token, resume_with_error, + pfault->wqe.wq_num, resume_with_error, pfault->type); free_page((unsigned long)buffer); } @@ -700,10 +1080,9 @@ static void mlx5_ib_mr_rdma_pfault_handler(struct mlx5_ib_dev *dev, ret = pagefault_single_data_segment(dev, rkey, address, prefetch_len, &bytes_committed, NULL); - if (ret < 0) { + if (ret < 0 && ret != -EAGAIN) { mlx5_ib_warn(dev, "Prefetch failed. ret: %d, QP 0x%x, address: 0x%.16llx, length = 0x%.16x\n", - ret, pfault->token, address, - prefetch_len); + ret, pfault->token, address, prefetch_len); } } } @@ -728,19 +1107,61 @@ void mlx5_ib_pfault(struct mlx5_core_dev *mdev, void *context, } } -int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev) +void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent) +{ + if (!(ent->dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) + return; + + switch (ent->order - 2) { + case MLX5_IMR_MTT_CACHE_ENTRY: + ent->page = PAGE_SHIFT; + ent->xlt = MLX5_IMR_MTT_ENTRIES * + sizeof(struct mlx5_mtt) / + MLX5_IB_UMR_OCTOWORD; + ent->access_mode = MLX5_MKC_ACCESS_MODE_MTT; + ent->limit = 0; + break; + + case MLX5_IMR_KSM_CACHE_ENTRY: + ent->page = MLX5_KSM_PAGE_SHIFT; + ent->xlt = mlx5_imr_ksm_entries * + sizeof(struct mlx5_klm) / + MLX5_IB_UMR_OCTOWORD; + ent->access_mode = MLX5_MKC_ACCESS_MODE_KSM; + ent->limit = 0; + break; + } +} + +int mlx5_ib_odp_init_one(struct mlx5_ib_dev *dev) { int ret; - ret = init_srcu_struct(&ibdev->mr_srcu); + ret = init_srcu_struct(&dev->mr_srcu); if (ret) return ret; + if (dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT) { + ret = mlx5_cmd_null_mkey(dev->mdev, &dev->null_mkey); + if (ret) { + mlx5_ib_err(dev, "Error getting null_mkey %d\n", ret); + return ret; + } + } + return 0; } -void mlx5_ib_odp_remove_one(struct mlx5_ib_dev *ibdev) +void mlx5_ib_odp_remove_one(struct mlx5_ib_dev *dev) +{ + cleanup_srcu_struct(&dev->mr_srcu); +} + +int mlx5_ib_odp_init(void) { - cleanup_srcu_struct(&ibdev->mr_srcu); + mlx5_imr_ksm_entries = BIT_ULL(get_order(TASK_SIZE) - + MLX5_IMR_MTT_BITS); + + return 0; } diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index e31bf11ae64f..ad8a2638e339 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -905,7 +905,10 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, else qp->bf.bfreg = &dev->bfreg; - qp->bf.buf_size = 1 << MLX5_CAP_GEN(dev->mdev, log_bf_reg_size); + /* We need to divide by two since each register is comprised of + * two buffers of identical size, namely odd and even + */ + qp->bf.buf_size = (1 << MLX5_CAP_GEN(dev->mdev, log_bf_reg_size)) / 2; uar_index = qp->bf.bfreg->index; err = calc_sq_size(dev, init_attr, qp); @@ -1141,7 +1144,8 @@ static int create_raw_packet_qp_rq(struct mlx5_ib_dev *dev, return -ENOMEM; rqc = MLX5_ADDR_OF(create_rq_in, in, ctx); - MLX5_SET(rqc, rqc, vsd, 1); + if (!(rq->flags & MLX5_IB_RQ_CVLAN_STRIPPING)) + MLX5_SET(rqc, rqc, vsd, 1); MLX5_SET(rqc, rqc, mem_rq_type, MLX5_RQC_MEM_RQ_TYPE_MEMORY_RQ_INLINE); MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RST); MLX5_SET(rqc, rqc, flush_in_error_en, 1); @@ -1238,6 +1242,8 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, if (qp->rq.wqe_cnt) { rq->base.container_mibqp = qp; + if (qp->flags & MLX5_IB_QP_CVLAN_STRIPPING) + rq->flags |= MLX5_IB_RQ_CVLAN_STRIPPING; err = create_raw_packet_qp_rq(dev, rq, in); if (err) goto err_destroy_sq; @@ -1559,6 +1565,14 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) qp->sq_signal_bits = MLX5_WQE_CTRL_CQ_UPDATE; + if (init_attr->create_flags & IB_QP_CREATE_CVLAN_STRIPPING) { + if (!(MLX5_CAP_GEN(dev->mdev, eth_net_offloads) && + MLX5_CAP_ETH(dev->mdev, vlan_cap)) || + (init_attr->qp_type != IB_QPT_RAW_PACKET)) + return -EOPNOTSUPP; + qp->flags |= MLX5_IB_QP_CVLAN_STRIPPING; + } + if (pd && pd->uobject) { if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) { mlx5_ib_dbg(dev, "copy failed\n"); @@ -2198,6 +2212,7 @@ static int mlx5_set_path(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, { enum rdma_link_layer ll = rdma_port_get_link_layer(&dev->ib_dev, port); int err; + enum ib_gid_type gid_type; if (attr_mask & IB_QP_PKEY_INDEX) path->pkey_index = cpu_to_be16(alt ? attr->alt_pkey_index : @@ -2216,10 +2231,16 @@ static int mlx5_set_path(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, if (ll == IB_LINK_LAYER_ETHERNET) { if (!(ah->ah_flags & IB_AH_GRH)) return -EINVAL; + err = mlx5_get_roce_gid_type(dev, port, ah->grh.sgid_index, + &gid_type); + if (err) + return err; memcpy(path->rmac, ah->dmac, sizeof(ah->dmac)); path->udp_sport = mlx5_get_roce_udp_sport(dev, port, ah->grh.sgid_index); path->dci_cfi_prio_sl = (ah->sl & 0x7) << 4; + if (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) + path->ecn_dscp = (ah->grh.traffic_class >> 2) & 0x3f; } else { path->fl_free_ar = (path_flags & MLX5_PATH_FLAG_FL) ? 0x80 : 0; path->fl_free_ar |= @@ -2422,7 +2443,7 @@ static int modify_raw_packet_qp_rq(struct mlx5_ib_dev *dev, if (raw_qp_param->set_mask & MLX5_RAW_QP_MOD_SET_RQ_Q_CTR_ID) { if (MLX5_CAP_GEN(dev->mdev, modify_rq_counter_set_id)) { MLX5_SET64(modify_rq_in, in, modify_bitmask, - MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_MODIFY_RQ_COUNTER_SET_ID); + MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_RQ_COUNTER_SET_ID); MLX5_SET(rqc, rqc, counter_set_id, raw_qp_param->rq_q_ctr_id); } else pr_info_once("%s: RAW PACKET QP counters are not supported on current FW\n", @@ -2777,7 +2798,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, qp->port) - 1; mibport = &dev->port[port_num]; context->qp_counter_set_usr_page |= - cpu_to_be32((u32)(mibport->q_cnt_id) << 24); + cpu_to_be32((u32)(mibport->q_cnts.set_id) << 24); } if (!ibqp->uobject && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) @@ -2805,7 +2826,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, raw_qp_param.operation = op; if (cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT) { - raw_qp_param.rq_q_ctr_id = mibport->q_cnt_id; + raw_qp_param.rq_q_ctr_id = mibport->q_cnts.set_id; raw_qp_param.set_mask |= MLX5_RAW_QP_MOD_SET_RQ_Q_CTR_ID; } @@ -3637,8 +3658,9 @@ static int set_psv_wr(struct ib_sig_domain *domain, psv_seg->ref_tag = cpu_to_be32(domain->sig.dif.ref_tag); break; default: - pr_err("Bad signature type given.\n"); - return 1; + pr_err("Bad signature type (%d) is given.\n", + domain->sig_type); + return -EINVAL; } *seg += sizeof(*psv_seg); @@ -3978,6 +4000,12 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, break; case IB_QPT_SMI: + if (unlikely(!mdev->port_caps[qp->port - 1].has_smi)) { + mlx5_ib_warn(dev, "Send SMP MADs is not allowed\n"); + err = -EPERM; + *bad_wr = wr; + goto out; + } case MLX5_IB_QPT_HW_GSI: set_datagram_seg(seg, wr); seg += sizeof(struct mlx5_wqe_datagram_seg); @@ -4579,6 +4607,7 @@ static int create_rq(struct mlx5_ib_rwq *rwq, struct ib_pd *pd, struct ib_wq_init_attr *init_attr) { struct mlx5_ib_dev *dev; + int has_net_offloads; __be64 *rq_pas0; void *in; void *rqc; @@ -4610,9 +4639,28 @@ static int create_rq(struct mlx5_ib_rwq *rwq, struct ib_pd *pd, MLX5_SET(wq, wq, log_wq_pg_sz, rwq->log_page_size); MLX5_SET(wq, wq, wq_signature, rwq->wq_sig); MLX5_SET64(wq, wq, dbr_addr, rwq->db.dma); + has_net_offloads = MLX5_CAP_GEN(dev->mdev, eth_net_offloads); + if (init_attr->create_flags & IB_WQ_FLAGS_CVLAN_STRIPPING) { + if (!(has_net_offloads && MLX5_CAP_ETH(dev->mdev, vlan_cap))) { + mlx5_ib_dbg(dev, "VLAN offloads are not supported\n"); + err = -EOPNOTSUPP; + goto out; + } + } else { + MLX5_SET(rqc, rqc, vsd, 1); + } + if (init_attr->create_flags & IB_WQ_FLAGS_SCATTER_FCS) { + if (!(has_net_offloads && MLX5_CAP_ETH(dev->mdev, scatter_fcs))) { + mlx5_ib_dbg(dev, "Scatter FCS is not supported\n"); + err = -EOPNOTSUPP; + goto out; + } + MLX5_SET(rqc, rqc, scatter_fcs, 1); + } rq_pas0 = (__be64 *)MLX5_ADDR_OF(wq, wq, pas); mlx5_ib_populate_pas(dev, rwq->umem, rwq->page_shift, rq_pas0, 0); err = mlx5_core_create_rq_tracked(dev->mdev, in, inlen, &rwq->core_qp); +out: kvfree(in); return err; } @@ -4896,10 +4944,37 @@ int mlx5_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr, MLX5_SET(modify_rq_in, in, rq_state, curr_wq_state); MLX5_SET(rqc, rqc, state, wq_state); + if (wq_attr_mask & IB_WQ_FLAGS) { + if (wq_attr->flags_mask & IB_WQ_FLAGS_CVLAN_STRIPPING) { + if (!(MLX5_CAP_GEN(dev->mdev, eth_net_offloads) && + MLX5_CAP_ETH(dev->mdev, vlan_cap))) { + mlx5_ib_dbg(dev, "VLAN offloads are not " + "supported\n"); + err = -EOPNOTSUPP; + goto out; + } + MLX5_SET64(modify_rq_in, in, modify_bitmask, + MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_VSD); + MLX5_SET(rqc, rqc, vsd, + (wq_attr->flags & IB_WQ_FLAGS_CVLAN_STRIPPING) ? 0 : 1); + } + } + + if (curr_wq_state == IB_WQS_RESET && wq_state == IB_WQS_RDY) { + if (MLX5_CAP_GEN(dev->mdev, modify_rq_counter_set_id)) { + MLX5_SET64(modify_rq_in, in, modify_bitmask, + MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_RQ_COUNTER_SET_ID); + MLX5_SET(rqc, rqc, counter_set_id, dev->port->q_cnts.set_id); + } else + pr_info_once("%s: Receive WQ counters are not supported on current FW\n", + dev->ib_dev.name); + } + err = mlx5_core_modify_rq(dev->mdev, rwq->core_qp.qpn, in, inlen); - kvfree(in); if (!err) rwq->ibwq.state = (wq_state == MLX5_RQC_STATE_ERR) ? IB_WQS_ERR : wq_state; +out: + kvfree(in); return err; } diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 6f4397ee1ed6..7cb145f9a6db 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -165,8 +165,6 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq, int err; int i; struct mlx5_wqe_srq_next_seg *next; - int page_shift; - int npages; err = mlx5_db_alloc(dev->mdev, &srq->db); if (err) { @@ -179,7 +177,6 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq, err = -ENOMEM; goto err_db; } - page_shift = srq->buf.page_shift; srq->head = 0; srq->tail = srq->msrq.max - 1; @@ -191,10 +188,8 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq, cpu_to_be16((i + 1) & (srq->msrq.max - 1)); } - npages = DIV_ROUND_UP(srq->buf.npages, 1 << (page_shift - PAGE_SHIFT)); - mlx5_ib_dbg(dev, "buf_size %d, page_shift %d, npages %d, calc npages %d\n", - buf_size, page_shift, srq->buf.npages, npages); - in->pas = mlx5_vzalloc(sizeof(*in->pas) * npages); + mlx5_ib_dbg(dev, "srq->buf.page_shift = %d\n", srq->buf.page_shift); + in->pas = mlx5_vzalloc(sizeof(*in->pas) * srq->buf.npages); if (!in->pas) { err = -ENOMEM; goto err_buf; @@ -208,7 +203,7 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq, } srq->wq_sig = !!srq_signature; - in->log_page_size = page_shift - MLX5_ADAPTER_PAGE_SHIFT; + in->log_page_size = srq->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT; if (MLX5_CAP_GEN(dev->mdev, cqe_version) == MLX5_CQE_VERSION_V1 && in->type == IB_SRQT_XRC) in->user_index = MLX5_IB_DEFAULT_UIDX; diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c index ded76c101dde..c309e5c96383 100644 --- a/drivers/infiniband/hw/mthca/mthca_main.c +++ b/drivers/infiniband/hw/mthca/mthca_main.c @@ -851,20 +851,18 @@ err_uar_table_free: static int mthca_enable_msi_x(struct mthca_dev *mdev) { - struct msix_entry entries[3]; int err; - entries[0].entry = 0; - entries[1].entry = 1; - entries[2].entry = 2; - - err = pci_enable_msix_exact(mdev->pdev, entries, ARRAY_SIZE(entries)); - if (err) + err = pci_alloc_irq_vectors(mdev->pdev, 3, 3, PCI_IRQ_MSIX); + if (err < 0) return err; - mdev->eq_table.eq[MTHCA_EQ_COMP ].msi_x_vector = entries[0].vector; - mdev->eq_table.eq[MTHCA_EQ_ASYNC].msi_x_vector = entries[1].vector; - mdev->eq_table.eq[MTHCA_EQ_CMD ].msi_x_vector = entries[2].vector; + mdev->eq_table.eq[MTHCA_EQ_COMP ].msi_x_vector = + pci_irq_vector(mdev->pdev, 0); + mdev->eq_table.eq[MTHCA_EQ_ASYNC].msi_x_vector = + pci_irq_vector(mdev->pdev, 1); + mdev->eq_table.eq[MTHCA_EQ_CMD ].msi_x_vector = + pci_irq_vector(mdev->pdev, 2); return 0; } @@ -1018,7 +1016,7 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type) err = mthca_setup_hca(mdev); if (err == -EBUSY && (mdev->mthca_flags & MTHCA_FLAG_MSI_X)) { if (mdev->mthca_flags & MTHCA_FLAG_MSI_X) - pci_disable_msix(pdev); + pci_free_irq_vectors(pdev); mdev->mthca_flags &= ~MTHCA_FLAG_MSI_X; err = mthca_setup_hca(mdev); @@ -1062,7 +1060,7 @@ err_cleanup: err_close: if (mdev->mthca_flags & MTHCA_FLAG_MSI_X) - pci_disable_msix(pdev); + pci_free_irq_vectors(pdev); mthca_close_hca(mdev); @@ -1113,7 +1111,7 @@ static void __mthca_remove_one(struct pci_dev *pdev) mthca_cmd_cleanup(mdev); if (mdev->mthca_flags & MTHCA_FLAG_MSI_X) - pci_disable_msix(pdev); + pci_free_irq_vectors(pdev); ib_dealloc_device(&mdev->ib_dev); pci_release_regions(pdev); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index d31708742ba5..22d0e6ee5af6 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -146,7 +146,7 @@ static int mthca_query_port(struct ib_device *ibdev, if (!in_mad || !out_mad) goto out; - memset(props, 0, sizeof *props); + /* props being zeroed by the caller, avoid zeroing it here */ init_query_mad(in_mad); in_mad->attr_id = IB_SMP_ATTR_PORT_INFO; @@ -212,7 +212,7 @@ static int mthca_modify_port(struct ib_device *ibdev, if (mutex_lock_interruptible(&to_mdev(ibdev)->cap_mask_mutex)) return -ERESTARTSYS; - err = mthca_query_port(ibdev, port, &attr); + err = ib_query_port(ibdev, port, &attr); if (err) goto out; @@ -1166,13 +1166,14 @@ static int mthca_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; - err = mthca_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB; immutable->max_mad_size = IB_MGMT_MAD_SIZE; return 0; @@ -1223,7 +1224,7 @@ int mthca_register_device(struct mthca_dev *dev) dev->ib_dev.node_type = RDMA_NODE_IB_CA; dev->ib_dev.phys_port_cnt = dev->limits.num_ports; dev->ib_dev.num_comp_vectors = 1; - dev->ib_dev.dma_device = &dev->pdev->dev; + dev->ib_dev.dev.parent = &dev->pdev->dev; dev->ib_dev.query_device = mthca_query_device; dev->ib_dev.query_port = mthca_query_port; dev->ib_dev.modify_device = mthca_modify_device; diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 8e703479e7ce..fb983df7c157 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -135,17 +135,17 @@ static void record_ird_ord(struct nes_cm_node *, u16, u16); /* instance of function pointers for client API */ /* set address of this instance to cm_core->cm_ops at cm_core alloc */ static const struct nes_cm_ops nes_cm_api = { - mini_cm_accelerated, - mini_cm_listen, - mini_cm_del_listen, - mini_cm_connect, - mini_cm_close, - mini_cm_accept, - mini_cm_reject, - mini_cm_recv_pkt, - mini_cm_dealloc_core, - mini_cm_get, - mini_cm_set + .accelerated = mini_cm_accelerated, + .listen = mini_cm_listen, + .stop_listener = mini_cm_del_listen, + .connect = mini_cm_connect, + .close = mini_cm_close, + .accept = mini_cm_accept, + .reject = mini_cm_reject, + .recv_pkt = mini_cm_recv_pkt, + .destroy_cm_core = mini_cm_dealloc_core, + .get = mini_cm_get, + .set = mini_cm_set }; static struct nes_cm_core *g_cm_core; diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 5a31f3c6a421..ccf0a4cffe9c 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -475,7 +475,7 @@ static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr struct nes_vnic *nesvnic = to_nesvnic(ibdev); struct net_device *netdev = nesvnic->netdev; - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ props->max_mtu = IB_MTU_4096; props->active_mtu = ib_mtu_int_to_enum(netdev->mtu); @@ -3660,13 +3660,14 @@ static int nes_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; + immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; + err = nes_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; return 0; } @@ -3730,7 +3731,6 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev) nesibdev->ibdev.phys_port_cnt = 1; nesibdev->ibdev.num_comp_vectors = 1; - nesibdev->ibdev.dma_device = &nesdev->pcidev->dev; nesibdev->ibdev.dev.parent = &nesdev->pcidev->dev; nesibdev->ibdev.query_device = nes_query_device; nesibdev->ibdev.query_port = nes_query_port; diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c index 14d33b0f3950..cd66e1e45dd7 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c @@ -59,7 +59,7 @@ static u16 ocrdma_hdr_type_to_proto_num(int devid, u8 hdr_type) { switch (hdr_type) { case OCRDMA_L3_TYPE_IB_GRH: - return (u16)0x8915; + return (u16)ETH_P_IBOE; case OCRDMA_L3_TYPE_IPV4: return (u16)0x0800; case OCRDMA_L3_TYPE_IPV6: @@ -94,7 +94,7 @@ static inline int set_av_attr(struct ocrdma_dev *dev, struct ocrdma_ah *ah, proto_num = ocrdma_hdr_type_to_proto_num(dev->id, ah->hdr_type); if (!proto_num) return -EINVAL; - nxthdr = (proto_num == 0x8915) ? 0x1b : 0x11; + nxthdr = (proto_num == ETH_P_IBOE) ? 0x1b : 0x11; /* VLAN */ if (!vlan_tag || (vlan_tag > 0xFFF)) vlan_tag = dev->pvid; diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c index 9a305201545e..aa6967197620 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c @@ -44,6 +44,7 @@ #include <linux/interrupt.h> #include <linux/log2.h> #include <linux/dma-mapping.h> +#include <linux/if_ether.h> #include <rdma/ib_verbs.h> #include <rdma/ib_user_verbs.h> @@ -2984,7 +2985,7 @@ static int ocrdma_parse_dcbxcfg_rsp(struct ocrdma_dev *dev, int ptype, OCRDMA_APP_PARAM_APP_PROTO_MASK; if ( - valid && proto == OCRDMA_APP_PROTO_ROCE && + valid && proto == ETH_P_IBOE && proto_sel == OCRDMA_PROTO_SELECT_L2) { for (slindx = 0; slindx < OCRDMA_MAX_SERVICE_LEVEL_INDEX; slindx++) { diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c index 896071502739..57c9a2ad0260 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c @@ -93,15 +93,16 @@ static int ocrdma_port_immutable(struct ib_device *ibdev, u8 port_num, int err; dev = get_ocrdma_dev(ibdev); - err = ocrdma_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; + if (ocrdma_is_udp_encap_supported(dev)) + immutable->core_cap_flags |= RDMA_CORE_CAP_PROT_ROCE_UDP_ENCAP; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; - if (ocrdma_is_udp_encap_supported(dev)) - immutable->core_cap_flags |= RDMA_CORE_CAP_PROT_ROCE_UDP_ENCAP; immutable->max_mad_size = IB_MGMT_MAD_SIZE; return 0; @@ -198,7 +199,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev) dev->ibdev.alloc_ucontext = ocrdma_alloc_ucontext; dev->ibdev.dealloc_ucontext = ocrdma_dealloc_ucontext; dev->ibdev.mmap = ocrdma_mmap; - dev->ibdev.dma_device = &dev->nic_info.pdev->dev; + dev->ibdev.dev.parent = &dev->nic_info.pdev->dev; dev->ibdev.process_mad = ocrdma_process_mad; dev->ibdev.get_port_immutable = ocrdma_port_immutable; diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h index 37df4481bb8f..6ef89c226ad8 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h +++ b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h @@ -1901,7 +1901,6 @@ struct ocrdma_eth_vlan { u8 smac[6]; __be16 eth_type; __be16 vlan_tag; -#define OCRDMA_ROCE_ETH_TYPE 0x8915 __be16 roce_eth_type; } __packed; @@ -2179,10 +2178,6 @@ enum OCRDMA_DCBX_PARAM_TYPE { OCRDMA_PARAMETER_TYPE_PEER = 0x02 }; -enum OCRDMA_DCBX_APP_PROTO { - OCRDMA_APP_PROTO_ROCE = 0x8915 -}; - enum OCRDMA_DCBX_PROTO { OCRDMA_PROTO_SELECT_L2 = 0x00, OCRDMA_PROTO_SELECT_L4 = 0x01 diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 6af44f8db3d5..bc9fb144e57b 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -210,6 +210,7 @@ int ocrdma_query_port(struct ib_device *ibdev, struct ocrdma_dev *dev; struct net_device *netdev; + /* props being zeroed by the caller, avoid zeroing it here */ dev = get_ocrdma_dev(ibdev); if (port > 1) { pr_err("%s(%d) invalid_port=0x%x\n", __func__, @@ -1170,8 +1171,7 @@ int ocrdma_destroy_cq(struct ib_cq *ibcq) dev->cq_tbl[cq->id] = NULL; indx = ocrdma_get_eq_table_index(dev, cq->eqn); - if (indx == -EINVAL) - BUG(); + BUG_ON(indx == -EINVAL); eq = &dev->eq_tbl[indx]; irq = ocrdma_get_irq(dev, eq); @@ -1741,8 +1741,7 @@ static void ocrdma_discard_cqes(struct ocrdma_qp *qp, struct ocrdma_cq *cq) wqe_idx = (le32_to_cpu(cqe->rq.buftag_qpn) >> OCRDMA_CQE_BUFTAG_SHIFT) & qp->srq->rq.max_wqe_idx; - if (wqe_idx < 1) - BUG(); + BUG_ON(wqe_idx < 1); spin_lock_irqsave(&qp->srq->q_lock, flags); ocrdma_hwq_inc_tail(&qp->srq->rq); ocrdma_srq_toggle_bit(qp->srq, wqe_idx - 1); @@ -2388,15 +2387,13 @@ static int ocrdma_srq_get_idx(struct ocrdma_srq *srq) if (srq->idx_bit_fields[row]) { indx = ffs(srq->idx_bit_fields[row]); indx = (row * 32) + (indx - 1); - if (indx >= srq->rq.max_cnt) - BUG(); + BUG_ON(indx >= srq->rq.max_cnt); ocrdma_srq_toggle_bit(srq, indx); break; } } - if (row == srq->bit_fields_len) - BUG(); + BUG_ON(row == srq->bit_fields_len); return indx + 1; /* Use from index 1 */ } @@ -2754,8 +2751,7 @@ static void ocrdma_update_free_srq_cqe(struct ib_wc *ibwc, srq = get_ocrdma_srq(qp->ibqp.srq); wqe_idx = (le32_to_cpu(cqe->rq.buftag_qpn) >> OCRDMA_CQE_BUFTAG_SHIFT) & srq->rq.max_wqe_idx; - if (wqe_idx < 1) - BUG(); + BUG_ON(wqe_idx < 1); ibwc->wr_id = srq->rqe_wr_id_tbl[wqe_idx]; spin_lock_irqsave(&srq->q_lock, flags); diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index 3ac8aa5ef37d..b9b47e5cc8b3 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -170,7 +170,7 @@ static int qedr_register_device(struct qedr_dev *dev) dev->ibdev.get_port_immutable = qedr_port_immutable; dev->ibdev.get_netdev = qedr_get_netdev; - dev->ibdev.dma_device = &dev->pdev->dev; + dev->ibdev.dev.parent = &dev->pdev->dev; dev->ibdev.get_link_layer = qedr_link_layer; dev->ibdev.get_dev_fw_str = qedr_get_dev_fw_str; diff --git a/drivers/infiniband/hw/qedr/qedr_cm.c b/drivers/infiniband/hw/qedr/qedr_cm.c index a9a8d8745d2e..699632893dd9 100644 --- a/drivers/infiniband/hw/qedr/qedr_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_cm.c @@ -287,7 +287,7 @@ static inline int qedr_gsi_build_header(struct qedr_dev *dev, has_udp = (sgid_attr.gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP); if (!has_udp) { /* RoCE v1 */ - ether_type = ETH_P_ROCE; + ether_type = ETH_P_IBOE; *roce_mode = ROCE_V1; } else if (ipv6_addr_v4mapped((struct in6_addr *)&sgid)) { /* RoCE v2 IPv4 */ diff --git a/drivers/infiniband/hw/qedr/qedr_cm.h b/drivers/infiniband/hw/qedr/qedr_cm.h index 9ba6e15cd93f..78efb1b056d1 100644 --- a/drivers/infiniband/hw/qedr/qedr_cm.h +++ b/drivers/infiniband/hw/qedr/qedr_cm.h @@ -37,7 +37,6 @@ #define QEDR_GSI_MAX_RECV_SGE (1) /* LL2 FW limitation */ -#define ETH_P_ROCE (0x8915) #define QEDR_ROCE_V2_UDP_SPORT (0000) static inline u32 qedr_get_ipv4_from_gid(u8 *gid) diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index c7d6c9a783bd..6b3bb32803bd 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -238,8 +238,8 @@ int qedr_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr *attr) } rdma_port = dev->ops->rdma_query_port(dev->rdma_ctx); - memset(attr, 0, sizeof(*attr)); + /* *attr being zeroed by the caller, avoid zeroing it here */ if (rdma_port->port_state == QED_RDMA_PORT_UP) { attr->state = IB_PORT_ACTIVE; attr->phys_state = 5; @@ -771,8 +771,10 @@ static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx, goto err0; q->pbl_tbl = qedr_alloc_pbl_tbl(dev, &q->pbl_info, GFP_KERNEL); - if (IS_ERR_OR_NULL(q->pbl_tbl)) + if (IS_ERR(q->pbl_tbl)) { + rc = PTR_ERR(q->pbl_tbl); goto err0; + } qedr_populate_pbls(dev, q->umem, q->pbl_tbl, &q->pbl_info); @@ -1086,30 +1088,6 @@ static inline int get_gid_info_from_table(struct ib_qp *ibqp, return 0; } -static void qedr_cleanup_user_sq(struct qedr_dev *dev, struct qedr_qp *qp) -{ - qedr_free_pbl(dev, &qp->usq.pbl_info, qp->usq.pbl_tbl); - ib_umem_release(qp->usq.umem); -} - -static void qedr_cleanup_user_rq(struct qedr_dev *dev, struct qedr_qp *qp) -{ - qedr_free_pbl(dev, &qp->urq.pbl_info, qp->urq.pbl_tbl); - ib_umem_release(qp->urq.umem); -} - -static void qedr_cleanup_kernel_sq(struct qedr_dev *dev, struct qedr_qp *qp) -{ - dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl); - kfree(qp->wqe_wr_id); -} - -static void qedr_cleanup_kernel_rq(struct qedr_dev *dev, struct qedr_qp *qp) -{ - dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl); - kfree(qp->rqe_wr_id); -} - static int qedr_check_qp_attrs(struct ib_pd *ibpd, struct qedr_dev *dev, struct ib_qp_init_attr *attrs) { @@ -1198,15 +1176,13 @@ static int qedr_copy_qp_uresp(struct qedr_dev *dev, return rc; } -static void qedr_set_qp_init_params(struct qedr_dev *dev, - struct qedr_qp *qp, - struct qedr_pd *pd, - struct ib_qp_init_attr *attrs) +static void qedr_set_common_qp_params(struct qedr_dev *dev, + struct qedr_qp *qp, + struct qedr_pd *pd, + struct ib_qp_init_attr *attrs) { - qp->pd = pd; - spin_lock_init(&qp->q_lock); - + qp->pd = pd; qp->qp_type = attrs->qp_type; qp->max_inline_data = attrs->cap.max_inline_data; qp->sq.max_sges = attrs->cap.max_send_sge; @@ -1215,103 +1191,161 @@ static void qedr_set_qp_init_params(struct qedr_dev *dev, qp->sq_cq = get_qedr_cq(attrs->send_cq); qp->rq_cq = get_qedr_cq(attrs->recv_cq); qp->dev = dev; + qp->rq.max_sges = attrs->cap.max_recv_sge; DP_DEBUG(dev, QEDR_MSG_QP, + "RQ params:\trq_max_sges = %d, rq_cq_id = %d\n", + qp->rq.max_sges, qp->rq_cq->icid); + DP_DEBUG(dev, QEDR_MSG_QP, "QP params:\tpd = %d, qp_type = %d, max_inline_data = %d, state = %d, signaled = %d, use_srq=%d\n", pd->pd_id, qp->qp_type, qp->max_inline_data, qp->state, qp->signaled, (attrs->srq) ? 1 : 0); DP_DEBUG(dev, QEDR_MSG_QP, "SQ params:\tsq_max_sges = %d, sq_cq_id = %d\n", qp->sq.max_sges, qp->sq_cq->icid); - qp->rq.max_sges = attrs->cap.max_recv_sge; - DP_DEBUG(dev, QEDR_MSG_QP, - "RQ params:\trq_max_sges = %d, rq_cq_id = %d\n", - qp->rq.max_sges, qp->rq_cq->icid); } -static inline void -qedr_init_qp_user_params(struct qed_rdma_create_qp_in_params *params, - struct qedr_create_qp_ureq *ureq) -{ - /* QP handle to be written in CQE */ - params->qp_handle_lo = ureq->qp_handle_lo; - params->qp_handle_hi = ureq->qp_handle_hi; -} - -static inline void -qedr_init_qp_kernel_doorbell_sq(struct qedr_dev *dev, struct qedr_qp *qp) +static void qedr_set_roce_db_info(struct qedr_dev *dev, struct qedr_qp *qp) { qp->sq.db = dev->db_addr + DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD); qp->sq.db_data.data.icid = qp->icid + 1; + qp->rq.db = dev->db_addr + + DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD); + qp->rq.db_data.data.icid = qp->icid; } static inline void -qedr_init_qp_kernel_doorbell_rq(struct qedr_dev *dev, struct qedr_qp *qp) +qedr_init_common_qp_in_params(struct qedr_dev *dev, + struct qedr_pd *pd, + struct qedr_qp *qp, + struct ib_qp_init_attr *attrs, + bool fmr_and_reserved_lkey, + struct qed_rdma_create_qp_in_params *params) { - qp->rq.db = dev->db_addr + - DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD); - qp->rq.db_data.data.icid = qp->icid; + /* QP handle to be written in an async event */ + params->qp_handle_async_lo = lower_32_bits((uintptr_t) qp); + params->qp_handle_async_hi = upper_32_bits((uintptr_t) qp); + + params->signal_all = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR); + params->fmr_and_reserved_lkey = fmr_and_reserved_lkey; + params->pd = pd->pd_id; + params->dpi = pd->uctx ? pd->uctx->dpi : dev->dpi; + params->sq_cq_id = get_qedr_cq(attrs->send_cq)->icid; + params->stats_queue = 0; + params->rq_cq_id = get_qedr_cq(attrs->recv_cq)->icid; + params->srq_id = 0; + params->use_srq = false; } -static inline int -qedr_init_qp_kernel_params_rq(struct qedr_dev *dev, - struct qedr_qp *qp, struct ib_qp_init_attr *attrs) +static inline void qedr_qp_user_print(struct qedr_dev *dev, struct qedr_qp *qp) { - /* Allocate driver internal RQ array */ - qp->rqe_wr_id = kcalloc(qp->rq.max_wr, sizeof(*qp->rqe_wr_id), - GFP_KERNEL); - if (!qp->rqe_wr_id) - return -ENOMEM; + DP_DEBUG(dev, QEDR_MSG_QP, "create qp: successfully created user QP. " + "qp=%p. " + "sq_addr=0x%llx, " + "sq_len=%zd, " + "rq_addr=0x%llx, " + "rq_len=%zd" + "\n", + qp, + qp->usq.buf_addr, + qp->usq.buf_len, qp->urq.buf_addr, qp->urq.buf_len); +} - DP_DEBUG(dev, QEDR_MSG_QP, "RQ max_wr set to %d.\n", qp->rq.max_wr); +static void qedr_cleanup_user(struct qedr_dev *dev, struct qedr_qp *qp) +{ + if (qp->usq.umem) + ib_umem_release(qp->usq.umem); + qp->usq.umem = NULL; - return 0; + if (qp->urq.umem) + ib_umem_release(qp->urq.umem); + qp->urq.umem = NULL; } -static inline int -qedr_init_qp_kernel_params_sq(struct qedr_dev *dev, - struct qedr_qp *qp, - struct ib_qp_init_attr *attrs, - struct qed_rdma_create_qp_in_params *params) +static int qedr_create_user_qp(struct qedr_dev *dev, + struct qedr_qp *qp, + struct ib_pd *ibpd, + struct ib_udata *udata, + struct ib_qp_init_attr *attrs) { - u32 temp_max_wr; + struct qed_rdma_create_qp_in_params in_params; + struct qed_rdma_create_qp_out_params out_params; + struct qedr_pd *pd = get_qedr_pd(ibpd); + struct ib_ucontext *ib_ctx = NULL; + struct qedr_ucontext *ctx = NULL; + struct qedr_create_qp_ureq ureq; + int rc = -EINVAL; - /* Allocate driver internal SQ array */ - temp_max_wr = attrs->cap.max_send_wr * dev->wq_multiplier; - temp_max_wr = min_t(u32, temp_max_wr, dev->attr.max_sqe); + ib_ctx = ibpd->uobject->context; + ctx = get_qedr_ucontext(ib_ctx); - /* temp_max_wr < attr->max_sqe < u16 so the casting is safe */ - qp->sq.max_wr = (u16)temp_max_wr; - qp->wqe_wr_id = kcalloc(qp->sq.max_wr, sizeof(*qp->wqe_wr_id), - GFP_KERNEL); - if (!qp->wqe_wr_id) - return -ENOMEM; + memset(&ureq, 0, sizeof(ureq)); + rc = ib_copy_from_udata(&ureq, udata, sizeof(ureq)); + if (rc) { + DP_ERR(dev, "Problem copying data from user space\n"); + return rc; + } - DP_DEBUG(dev, QEDR_MSG_QP, "SQ max_wr set to %d.\n", qp->sq.max_wr); + /* SQ - read access only (0), dma sync not required (0) */ + rc = qedr_init_user_queue(ib_ctx, dev, &qp->usq, ureq.sq_addr, + ureq.sq_len, 0, 0); + if (rc) + return rc; - /* QP handle to be written in CQE */ - params->qp_handle_lo = lower_32_bits((uintptr_t)qp); - params->qp_handle_hi = upper_32_bits((uintptr_t)qp); + /* RQ - read access only (0), dma sync not required (0) */ + rc = qedr_init_user_queue(ib_ctx, dev, &qp->urq, ureq.rq_addr, + ureq.rq_len, 0, 0); + + if (rc) + return rc; + + memset(&in_params, 0, sizeof(in_params)); + qedr_init_common_qp_in_params(dev, pd, qp, attrs, false, &in_params); + in_params.qp_handle_lo = ureq.qp_handle_lo; + in_params.qp_handle_hi = ureq.qp_handle_hi; + in_params.sq_num_pages = qp->usq.pbl_info.num_pbes; + in_params.sq_pbl_ptr = qp->usq.pbl_tbl->pa; + in_params.rq_num_pages = qp->urq.pbl_info.num_pbes; + in_params.rq_pbl_ptr = qp->urq.pbl_tbl->pa; + + qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx, + &in_params, &out_params); + + if (!qp->qed_qp) { + rc = -ENOMEM; + goto err1; + } + + qp->qp_id = out_params.qp_id; + qp->icid = out_params.icid; + + rc = qedr_copy_qp_uresp(dev, qp, udata); + if (rc) + goto err; + + qedr_qp_user_print(dev, qp); return 0; +err: + rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp); + if (rc) + DP_ERR(dev, "create qp: fatal fault. rc=%d", rc); + +err1: + qedr_cleanup_user(dev, qp); + return rc; } -static inline int qedr_init_qp_kernel_sq(struct qedr_dev *dev, - struct qedr_qp *qp, - struct ib_qp_init_attr *attrs) +static int +qedr_roce_create_kernel_qp(struct qedr_dev *dev, + struct qedr_qp *qp, + struct qed_rdma_create_qp_in_params *in_params, + u32 n_sq_elems, u32 n_rq_elems) { - u32 n_sq_elems, n_sq_entries; + struct qed_rdma_create_qp_out_params out_params; int rc; - /* A single work request may take up to QEDR_MAX_SQ_WQE_SIZE elements in - * the ring. The ring should allow at least a single WR, even if the - * user requested none, due to allocation issues. - */ - n_sq_entries = attrs->cap.max_send_wr; - n_sq_entries = min_t(u32, n_sq_entries, dev->attr.max_sqe); - n_sq_entries = max_t(u32, n_sq_entries, 1); - n_sq_elems = n_sq_entries * QEDR_MAX_SQE_ELEMENTS_PER_SQE; rc = dev->ops->common->chain_alloc(dev->cdev, QED_CHAIN_USE_TO_PRODUCE, QED_CHAIN_MODE_PBL, @@ -1319,31 +1353,13 @@ static inline int qedr_init_qp_kernel_sq(struct qedr_dev *dev, n_sq_elems, QEDR_SQE_ELEMENT_SIZE, &qp->sq.pbl); - if (rc) { - DP_ERR(dev, "failed to allocate QP %p SQ\n", qp); - return rc; - } - DP_DEBUG(dev, QEDR_MSG_SQ, - "SQ Pbl base addr = %llx max_send_wr=%d max_wr=%d capacity=%d, rc=%d\n", - qed_chain_get_pbl_phys(&qp->sq.pbl), attrs->cap.max_send_wr, - n_sq_entries, qed_chain_get_capacity(&qp->sq.pbl), rc); - return 0; -} + if (rc) + return rc; -static inline int qedr_init_qp_kernel_rq(struct qedr_dev *dev, - struct qedr_qp *qp, - struct ib_qp_init_attr *attrs) -{ - u32 n_rq_elems, n_rq_entries; - int rc; + in_params->sq_num_pages = qed_chain_get_page_cnt(&qp->sq.pbl); + in_params->sq_pbl_ptr = qed_chain_get_pbl_phys(&qp->sq.pbl); - /* A single work request may take up to QEDR_MAX_RQ_WQE_SIZE elements in - * the ring. There ring should allow at least a single WR, even if the - * user requested none, due to allocation issues. - */ - n_rq_entries = max_t(u32, attrs->cap.max_recv_wr, 1); - n_rq_elems = n_rq_entries * QEDR_MAX_RQE_ELEMENTS_PER_RQE; rc = dev->ops->common->chain_alloc(dev->cdev, QED_CHAIN_USE_TO_CONSUME_PRODUCE, QED_CHAIN_MODE_PBL, @@ -1351,136 +1367,102 @@ static inline int qedr_init_qp_kernel_rq(struct qedr_dev *dev, n_rq_elems, QEDR_RQE_ELEMENT_SIZE, &qp->rq.pbl); + if (rc) + return rc; - if (rc) { - DP_ERR(dev, "failed to allocate memory for QP %p RQ\n", qp); - return -ENOMEM; - } - - DP_DEBUG(dev, QEDR_MSG_RQ, - "RQ Pbl base addr = %llx max_recv_wr=%d max_wr=%d capacity=%d, rc=%d\n", - qed_chain_get_pbl_phys(&qp->rq.pbl), attrs->cap.max_recv_wr, - n_rq_entries, qed_chain_get_capacity(&qp->rq.pbl), rc); + in_params->rq_num_pages = qed_chain_get_page_cnt(&qp->rq.pbl); + in_params->rq_pbl_ptr = qed_chain_get_pbl_phys(&qp->rq.pbl); - /* n_rq_entries < u16 so the casting is safe */ - qp->rq.max_wr = (u16)n_rq_entries; + qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx, + in_params, &out_params); - return 0; -} + if (!qp->qed_qp) + return -EINVAL; -static inline void -qedr_init_qp_in_params_sq(struct qedr_dev *dev, - struct qedr_pd *pd, - struct qedr_qp *qp, - struct ib_qp_init_attr *attrs, - struct ib_udata *udata, - struct qed_rdma_create_qp_in_params *params) -{ - /* QP handle to be written in an async event */ - params->qp_handle_async_lo = lower_32_bits((uintptr_t)qp); - params->qp_handle_async_hi = upper_32_bits((uintptr_t)qp); + qp->qp_id = out_params.qp_id; + qp->icid = out_params.icid; - params->signal_all = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR); - params->fmr_and_reserved_lkey = !udata; - params->pd = pd->pd_id; - params->dpi = pd->uctx ? pd->uctx->dpi : dev->dpi; - params->sq_cq_id = get_qedr_cq(attrs->send_cq)->icid; - params->max_sq_sges = 0; - params->stats_queue = 0; + qedr_set_roce_db_info(dev, qp); - if (udata) { - params->sq_num_pages = qp->usq.pbl_info.num_pbes; - params->sq_pbl_ptr = qp->usq.pbl_tbl->pa; - } else { - params->sq_num_pages = qed_chain_get_page_cnt(&qp->sq.pbl); - params->sq_pbl_ptr = qed_chain_get_pbl_phys(&qp->sq.pbl); - } + return 0; } -static inline void -qedr_init_qp_in_params_rq(struct qedr_qp *qp, - struct ib_qp_init_attr *attrs, - struct ib_udata *udata, - struct qed_rdma_create_qp_in_params *params) +static void qedr_cleanup_kernel(struct qedr_dev *dev, struct qedr_qp *qp) { - params->rq_cq_id = get_qedr_cq(attrs->recv_cq)->icid; - params->srq_id = 0; - params->use_srq = false; + dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl); + kfree(qp->wqe_wr_id); - if (udata) { - params->rq_num_pages = qp->urq.pbl_info.num_pbes; - params->rq_pbl_ptr = qp->urq.pbl_tbl->pa; - } else { - params->rq_num_pages = qed_chain_get_page_cnt(&qp->rq.pbl); - params->rq_pbl_ptr = qed_chain_get_pbl_phys(&qp->rq.pbl); - } + dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl); + kfree(qp->rqe_wr_id); } -static inline void qedr_qp_user_print(struct qedr_dev *dev, struct qedr_qp *qp) +static int qedr_create_kernel_qp(struct qedr_dev *dev, + struct qedr_qp *qp, + struct ib_pd *ibpd, + struct ib_qp_init_attr *attrs) { - DP_DEBUG(dev, QEDR_MSG_QP, - "create qp: successfully created user QP. qp=%p, sq_addr=0x%llx, sq_len=%zd, rq_addr=0x%llx, rq_len=%zd\n", - qp, qp->usq.buf_addr, qp->usq.buf_len, qp->urq.buf_addr, - qp->urq.buf_len); -} + struct qed_rdma_create_qp_in_params in_params; + struct qedr_pd *pd = get_qedr_pd(ibpd); + int rc = -EINVAL; + u32 n_rq_elems; + u32 n_sq_elems; + u32 n_sq_entries; -static inline int qedr_init_user_qp(struct ib_ucontext *ib_ctx, - struct qedr_dev *dev, - struct qedr_qp *qp, - struct qedr_create_qp_ureq *ureq) -{ - int rc; + memset(&in_params, 0, sizeof(in_params)); - /* SQ - read access only (0), dma sync not required (0) */ - rc = qedr_init_user_queue(ib_ctx, dev, &qp->usq, ureq->sq_addr, - ureq->sq_len, 0, 0); - if (rc) - return rc; + /* A single work request may take up to QEDR_MAX_SQ_WQE_SIZE elements in + * the ring. The ring should allow at least a single WR, even if the + * user requested none, due to allocation issues. + * We should add an extra WR since the prod and cons indices of + * wqe_wr_id are managed in such a way that the WQ is considered full + * when (prod+1)%max_wr==cons. We currently don't do that because we + * double the number of entries due an iSER issue that pushes far more + * WRs than indicated. If we decline its ib_post_send() then we get + * error prints in the dmesg we'd like to avoid. + */ + qp->sq.max_wr = min_t(u32, attrs->cap.max_send_wr * dev->wq_multiplier, + dev->attr.max_sqe); - /* RQ - read access only (0), dma sync not required (0) */ - rc = qedr_init_user_queue(ib_ctx, dev, &qp->urq, ureq->rq_addr, - ureq->rq_len, 0, 0); + qp->wqe_wr_id = kzalloc(qp->sq.max_wr * sizeof(*qp->wqe_wr_id), + GFP_KERNEL); + if (!qp->wqe_wr_id) { + DP_ERR(dev, "create qp: failed SQ shadow memory allocation\n"); + return -ENOMEM; + } - if (rc) - qedr_cleanup_user_sq(dev, qp); - return rc; -} + /* QP handle to be written in CQE */ + in_params.qp_handle_lo = lower_32_bits((uintptr_t) qp); + in_params.qp_handle_hi = upper_32_bits((uintptr_t) qp); -static inline int -qedr_init_kernel_qp(struct qedr_dev *dev, - struct qedr_qp *qp, - struct ib_qp_init_attr *attrs, - struct qed_rdma_create_qp_in_params *params) -{ - int rc; + /* A single work request may take up to QEDR_MAX_RQ_WQE_SIZE elements in + * the ring. There ring should allow at least a single WR, even if the + * user requested none, due to allocation issues. + */ + qp->rq.max_wr = (u16) max_t(u32, attrs->cap.max_recv_wr, 1); - rc = qedr_init_qp_kernel_sq(dev, qp, attrs); - if (rc) { - DP_ERR(dev, "failed to init kernel QP %p SQ\n", qp); - return rc; + /* Allocate driver internal RQ array */ + qp->rqe_wr_id = kzalloc(qp->rq.max_wr * sizeof(*qp->rqe_wr_id), + GFP_KERNEL); + if (!qp->rqe_wr_id) { + DP_ERR(dev, + "create qp: failed RQ shadow memory allocation\n"); + kfree(qp->wqe_wr_id); + return -ENOMEM; } - rc = qedr_init_qp_kernel_params_sq(dev, qp, attrs, params); - if (rc) { - dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl); - DP_ERR(dev, "failed to init kernel QP %p SQ params\n", qp); - return rc; - } + qedr_init_common_qp_in_params(dev, pd, qp, attrs, true, &in_params); - rc = qedr_init_qp_kernel_rq(dev, qp, attrs); - if (rc) { - qedr_cleanup_kernel_sq(dev, qp); - DP_ERR(dev, "failed to init kernel QP %p RQ\n", qp); - return rc; - } + n_sq_entries = attrs->cap.max_send_wr; + n_sq_entries = min_t(u32, n_sq_entries, dev->attr.max_sqe); + n_sq_entries = max_t(u32, n_sq_entries, 1); + n_sq_elems = n_sq_entries * QEDR_MAX_SQE_ELEMENTS_PER_SQE; - rc = qedr_init_qp_kernel_params_rq(dev, qp, attrs); - if (rc) { - DP_ERR(dev, "failed to init kernel QP %p RQ params\n", qp); - qedr_cleanup_kernel_sq(dev, qp); - dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl); - return rc; - } + n_rq_elems = qp->rq.max_wr * QEDR_MAX_RQE_ELEMENTS_PER_RQE; + + rc = qedr_roce_create_kernel_qp(dev, qp, &in_params, + n_sq_elems, n_rq_elems); + if (rc) + qedr_cleanup_kernel(dev, qp); return rc; } @@ -1490,12 +1472,7 @@ struct ib_qp *qedr_create_qp(struct ib_pd *ibpd, struct ib_udata *udata) { struct qedr_dev *dev = get_qedr_dev(ibpd->device); - struct qed_rdma_create_qp_out_params out_params; - struct qed_rdma_create_qp_in_params in_params; struct qedr_pd *pd = get_qedr_pd(ibpd); - struct ib_ucontext *ib_ctx = NULL; - struct qedr_ucontext *ctx = NULL; - struct qedr_create_qp_ureq ureq; struct qedr_qp *qp; struct ib_qp *ibqp; int rc = 0; @@ -1510,101 +1487,42 @@ struct ib_qp *qedr_create_qp(struct ib_pd *ibpd, if (attrs->srq) return ERR_PTR(-EINVAL); - qp = kzalloc(sizeof(*qp), GFP_KERNEL); - if (!qp) - return ERR_PTR(-ENOMEM); - DP_DEBUG(dev, QEDR_MSG_QP, - "create qp: sq_cq=%p, sq_icid=%d, rq_cq=%p, rq_icid=%d\n", + "create qp: called from %s, event_handler=%p, eepd=%p sq_cq=%p, sq_icid=%d, rq_cq=%p, rq_icid=%d\n", + udata ? "user library" : "kernel", attrs->event_handler, pd, get_qedr_cq(attrs->send_cq), get_qedr_cq(attrs->send_cq)->icid, get_qedr_cq(attrs->recv_cq), get_qedr_cq(attrs->recv_cq)->icid); - qedr_set_qp_init_params(dev, qp, pd, attrs); + qp = kzalloc(sizeof(*qp), GFP_KERNEL); + if (!qp) { + DP_ERR(dev, "create qp: failed allocating memory\n"); + return ERR_PTR(-ENOMEM); + } + + qedr_set_common_qp_params(dev, qp, pd, attrs); if (attrs->qp_type == IB_QPT_GSI) { - if (udata) { - DP_ERR(dev, - "create qp: unexpected udata when creating GSI QP\n"); - goto err0; - } ibqp = qedr_create_gsi_qp(dev, attrs, qp); if (IS_ERR(ibqp)) kfree(qp); return ibqp; } - memset(&in_params, 0, sizeof(in_params)); - - if (udata) { - if (!(udata && ibpd->uobject && ibpd->uobject->context)) - goto err0; - - ib_ctx = ibpd->uobject->context; - ctx = get_qedr_ucontext(ib_ctx); - - memset(&ureq, 0, sizeof(ureq)); - if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) { - DP_ERR(dev, - "create qp: problem copying data from user space\n"); - goto err0; - } - - rc = qedr_init_user_qp(ib_ctx, dev, qp, &ureq); - if (rc) - goto err0; - - qedr_init_qp_user_params(&in_params, &ureq); - } else { - rc = qedr_init_kernel_qp(dev, qp, attrs, &in_params); - if (rc) - goto err0; - } - - qedr_init_qp_in_params_sq(dev, pd, qp, attrs, udata, &in_params); - qedr_init_qp_in_params_rq(qp, attrs, udata, &in_params); - - qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx, - &in_params, &out_params); + if (udata) + rc = qedr_create_user_qp(dev, qp, ibpd, udata, attrs); + else + rc = qedr_create_kernel_qp(dev, qp, ibpd, attrs); - if (!qp->qed_qp) - goto err1; + if (rc) + goto err; - qp->qp_id = out_params.qp_id; - qp->icid = out_params.icid; qp->ibqp.qp_num = qp->qp_id; - if (udata) { - rc = qedr_copy_qp_uresp(dev, qp, udata); - if (rc) - goto err2; - - qedr_qp_user_print(dev, qp); - } else { - qedr_init_qp_kernel_doorbell_sq(dev, qp); - qedr_init_qp_kernel_doorbell_rq(dev, qp); - } - - DP_DEBUG(dev, QEDR_MSG_QP, "created %s space QP %p\n", - udata ? "user" : "kernel", qp); - return &qp->ibqp; -err2: - rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp); - if (rc) - DP_ERR(dev, "create qp: fatal fault. rc=%d", rc); -err1: - if (udata) { - qedr_cleanup_user_sq(dev, qp); - qedr_cleanup_user_rq(dev, qp); - } else { - qedr_cleanup_kernel_sq(dev, qp); - qedr_cleanup_kernel_rq(dev, qp); - } - -err0: +err: kfree(qp); return ERR_PTR(-EFAULT); @@ -2085,6 +2003,24 @@ err: return rc; } +int qedr_free_qp_resources(struct qedr_dev *dev, struct qedr_qp *qp) +{ + int rc = 0; + + if (qp->qp_type != IB_QPT_GSI) { + rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp); + if (rc) + return rc; + } + + if (qp->ibqp.uobject && qp->ibqp.uobject->context) + qedr_cleanup_user(dev, qp); + else + qedr_cleanup_kernel(dev, qp); + + return 0; +} + int qedr_destroy_qp(struct ib_qp *ibqp) { struct qedr_qp *qp = get_qedr_qp(ibqp); @@ -2107,21 +2043,10 @@ int qedr_destroy_qp(struct ib_qp *ibqp) qedr_modify_qp(ibqp, &attr, attr_mask, NULL); } - if (qp->qp_type != IB_QPT_GSI) { - rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp); - if (rc) - return rc; - } else { + if (qp->qp_type == IB_QPT_GSI) qedr_destroy_gsi_qp(dev); - } - if (ibqp->uobject && ibqp->uobject->context) { - qedr_cleanup_user_sq(dev, qp); - qedr_cleanup_user_rq(dev, qp); - } else { - qedr_cleanup_kernel_sq(dev, qp); - qedr_cleanup_kernel_rq(dev, qp); - } + qedr_free_qp_resources(dev, qp); kfree(qp); @@ -2182,8 +2107,8 @@ static int init_mr_info(struct qedr_dev *dev, struct mr_info *info, goto done; info->pbl_table = qedr_alloc_pbl_tbl(dev, &info->pbl_info, GFP_KERNEL); - if (!info->pbl_table) { - rc = -ENOMEM; + if (IS_ERR(info->pbl_table)) { + rc = PTR_ERR(info->pbl_table); goto done; } @@ -2194,7 +2119,7 @@ static int init_mr_info(struct qedr_dev *dev, struct mr_info *info, * list and allocating another one */ tmp = qedr_alloc_pbl_tbl(dev, &info->pbl_info, GFP_KERNEL); - if (!tmp) { + if (IS_ERR(tmp)) { DP_DEBUG(dev, QEDR_MSG_MR, "Extra PBL is not allocated\n"); goto done; } @@ -3569,14 +3494,15 @@ int qedr_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; - err = qedr_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE | + RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE | - RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP; immutable->max_mad_size = IB_MGMT_MAD_SIZE; return 0; diff --git a/drivers/infiniband/hw/qib/qib_common.h b/drivers/infiniband/hw/qib/qib_common.h index 1d6e63eb1146..a4a1f56ce824 100644 --- a/drivers/infiniband/hw/qib/qib_common.h +++ b/drivers/infiniband/hw/qib/qib_common.h @@ -742,11 +742,7 @@ struct qib_tid_session_member { #define SIZE_OF_CRC 1 #define QIB_DEFAULT_P_KEY 0xFFFF -#define QIB_AETH_CREDIT_SHIFT 24 -#define QIB_AETH_CREDIT_MASK 0x1F -#define QIB_AETH_CREDIT_INVAL 0x1F #define QIB_PSN_MASK 0xFFFFFF -#define QIB_MSN_MASK 0xFFFFFF #define QIB_EAGER_TID_ID QLOGIC_IB_I_TID_MASK #define QIB_MULTICAST_QPN 0xFFFFFF diff --git a/drivers/infiniband/hw/qib/qib_dma.c b/drivers/infiniband/hw/qib/qib_dma.c deleted file mode 100644 index 59fe092b4b0f..000000000000 --- a/drivers/infiniband/hw/qib/qib_dma.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2006, 2009, 2010 QLogic, Corporation. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include <linux/types.h> -#include <linux/scatterlist.h> - -#include "qib_verbs.h" - -#define BAD_DMA_ADDRESS ((u64) 0) - -/* - * The following functions implement driver specific replacements - * for the ib_dma_*() functions. - * - * These functions return kernel virtual addresses instead of - * device bus addresses since the driver uses the CPU to copy - * data instead of using hardware DMA. - */ - -static int qib_mapping_error(struct ib_device *dev, u64 dma_addr) -{ - return dma_addr == BAD_DMA_ADDRESS; -} - -static u64 qib_dma_map_single(struct ib_device *dev, void *cpu_addr, - size_t size, enum dma_data_direction direction) -{ - BUG_ON(!valid_dma_direction(direction)); - return (u64) cpu_addr; -} - -static void qib_dma_unmap_single(struct ib_device *dev, u64 addr, size_t size, - enum dma_data_direction direction) -{ - BUG_ON(!valid_dma_direction(direction)); -} - -static u64 qib_dma_map_page(struct ib_device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction direction) -{ - u64 addr; - - BUG_ON(!valid_dma_direction(direction)); - - if (offset + size > PAGE_SIZE) { - addr = BAD_DMA_ADDRESS; - goto done; - } - - addr = (u64) page_address(page); - if (addr) - addr += offset; - /* TODO: handle highmem pages */ - -done: - return addr; -} - -static void qib_dma_unmap_page(struct ib_device *dev, u64 addr, size_t size, - enum dma_data_direction direction) -{ - BUG_ON(!valid_dma_direction(direction)); -} - -static int qib_map_sg(struct ib_device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction direction) -{ - struct scatterlist *sg; - u64 addr; - int i; - int ret = nents; - - BUG_ON(!valid_dma_direction(direction)); - - for_each_sg(sgl, sg, nents, i) { - addr = (u64) page_address(sg_page(sg)); - /* TODO: handle highmem pages */ - if (!addr) { - ret = 0; - break; - } - sg->dma_address = addr + sg->offset; -#ifdef CONFIG_NEED_SG_DMA_LENGTH - sg->dma_length = sg->length; -#endif - } - return ret; -} - -static void qib_unmap_sg(struct ib_device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction direction) -{ - BUG_ON(!valid_dma_direction(direction)); -} - -static void qib_sync_single_for_cpu(struct ib_device *dev, u64 addr, - size_t size, enum dma_data_direction dir) -{ -} - -static void qib_sync_single_for_device(struct ib_device *dev, u64 addr, - size_t size, - enum dma_data_direction dir) -{ -} - -static void *qib_dma_alloc_coherent(struct ib_device *dev, size_t size, - u64 *dma_handle, gfp_t flag) -{ - struct page *p; - void *addr = NULL; - - p = alloc_pages(flag, get_order(size)); - if (p) - addr = page_address(p); - if (dma_handle) - *dma_handle = (u64) addr; - return addr; -} - -static void qib_dma_free_coherent(struct ib_device *dev, size_t size, - void *cpu_addr, u64 dma_handle) -{ - free_pages((unsigned long) cpu_addr, get_order(size)); -} - -struct ib_dma_mapping_ops qib_dma_mapping_ops = { - .mapping_error = qib_mapping_error, - .map_single = qib_dma_map_single, - .unmap_single = qib_dma_unmap_single, - .map_page = qib_dma_map_page, - .unmap_page = qib_dma_unmap_page, - .map_sg = qib_map_sg, - .unmap_sg = qib_unmap_sg, - .sync_single_for_cpu = qib_sync_single_for_cpu, - .sync_single_for_device = qib_sync_single_for_device, - .alloc_coherent = qib_dma_alloc_coherent, - .free_coherent = qib_dma_free_coherent -}; diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index 2d1eacf1dfed..9396c1807cc3 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -893,7 +893,7 @@ bail: /* * qib_file_vma_fault - handle a VMA page fault. */ -static int qib_file_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int qib_file_vma_fault(struct vm_fault *vmf) { struct page *page; diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c index 92399d3ffd15..06de1cbcf67d 100644 --- a/drivers/infiniband/hw/qib/qib_iba6120.c +++ b/drivers/infiniband/hw/qib/qib_iba6120.c @@ -707,7 +707,7 @@ static void qib_6120_clear_freeze(struct qib_devdata *dd) /* disable error interrupts, to avoid confusion */ qib_write_kreg(dd, kr_errmask, 0ULL); - /* also disable interrupts; errormask is sometimes overwriten */ + /* also disable interrupts; errormask is sometimes overwritten */ qib_6120_set_intr_state(dd, 0); qib_cancel_sends(dd->pport); diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c index e55e31a69195..55a18384c22d 100644 --- a/drivers/infiniband/hw/qib/qib_iba7220.c +++ b/drivers/infiniband/hw/qib/qib_iba7220.c @@ -1259,7 +1259,7 @@ static void qib_7220_clear_freeze(struct qib_devdata *dd) /* disable error interrupts, to avoid confusion */ qib_write_kreg(dd, kr_errmask, 0ULL); - /* also disable interrupts; errormask is sometimes overwriten */ + /* also disable interrupts; errormask is sometimes overwritten */ qib_7220_set_intr_state(dd, 0); qib_cancel_sends(dd->pport); diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index c4a3616062f1..12c4208fd701 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -2053,7 +2053,7 @@ static void qib_7322_clear_freeze(struct qib_devdata *dd) qib_write_kreg_port(dd->pport + pidx, krp_errmask, 0ULL); - /* also disable interrupts; errormask is sometimes overwriten */ + /* also disable interrupts; errormask is sometimes overwritten */ qib_7322_set_intr_state(dd, 0); /* clear the freeze, and be sure chip saw it */ @@ -2893,7 +2893,6 @@ static void qib_setup_7322_cleanup(struct qib_devdata *dd) dd->cspec->gpio_mask &= ~mask; qib_write_kreg(dd, kr_gpio_mask, dd->cspec->gpio_mask); spin_unlock_irqrestore(&dd->cspec->gpio_lock, flags); - qib_qsfp_deinit(&dd->pport[i].cpspec->qsfp_data); } } } diff --git a/drivers/infiniband/hw/qib/qib_keys.c b/drivers/infiniband/hw/qib/qib_keys.c index 2c3c93572c17..8fdf79f8d4e4 100644 --- a/drivers/infiniband/hw/qib/qib_keys.c +++ b/drivers/infiniband/hw/qib/qib_keys.c @@ -158,10 +158,7 @@ int qib_rkey_ok(struct rvt_qp *qp, struct rvt_sge *sge, unsigned n, m; size_t off; - /* - * We use RKEY == zero for kernel virtual addresses - * (see qib_get_dma_mr and qib_dma.c). - */ + /* We use RKEY == zero for kernel virtual addresses */ rcu_read_lock(); if (rkey == 0) { struct rvt_pd *pd = ibpd_to_rvtpd(qp->ibqp.pd); diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c index 6abe1c621aa4..c379b8342a09 100644 --- a/drivers/infiniband/hw/qib/qib_pcie.c +++ b/drivers/infiniband/hw/qib/qib_pcie.c @@ -682,13 +682,6 @@ qib_pci_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_CAN_RECOVER; } -static pci_ers_result_t -qib_pci_link_reset(struct pci_dev *pdev) -{ - qib_devinfo(pdev, "QIB link_reset function called, ignored\n"); - return PCI_ERS_RESULT_CAN_RECOVER; -} - static void qib_pci_resume(struct pci_dev *pdev) { @@ -707,7 +700,6 @@ qib_pci_resume(struct pci_dev *pdev) const struct pci_error_handlers qib_pci_err_handler = { .error_detected = qib_pci_error_detected, .mmio_enabled = qib_pci_mmio_enabled, - .link_reset = qib_pci_link_reset, .slot_reset = qib_pci_slot_reset, .resume = qib_pci_resume, }; diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c index 99d31efe4c2f..2ac0c0f79e74 100644 --- a/drivers/infiniband/hw/qib/qib_qp.c +++ b/drivers/infiniband/hw/qib/qib_qp.c @@ -61,43 +61,6 @@ static inline unsigned find_next_offset(struct rvt_qpn_table *qpt, return off; } -/* - * Convert the AETH credit code into the number of credits. - */ -static u32 credit_table[31] = { - 0, /* 0 */ - 1, /* 1 */ - 2, /* 2 */ - 3, /* 3 */ - 4, /* 4 */ - 6, /* 5 */ - 8, /* 6 */ - 12, /* 7 */ - 16, /* 8 */ - 24, /* 9 */ - 32, /* A */ - 48, /* B */ - 64, /* C */ - 96, /* D */ - 128, /* E */ - 192, /* F */ - 256, /* 10 */ - 384, /* 11 */ - 512, /* 12 */ - 768, /* 13 */ - 1024, /* 14 */ - 1536, /* 15 */ - 2048, /* 16 */ - 3072, /* 17 */ - 4096, /* 18 */ - 6144, /* 19 */ - 8192, /* 1A */ - 12288, /* 1B */ - 16384, /* 1C */ - 24576, /* 1D */ - 32768 /* 1E */ -}; - const struct rvt_operation_params qib_post_parms[RVT_OPERATION_MAX] = { [IB_WR_RDMA_WRITE] = { .length = sizeof(struct ib_rdma_wr), @@ -354,66 +317,6 @@ u32 qib_mtu_from_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, u32 pmtu) return ib_mtu_enum_to_int(pmtu); } -/** - * qib_compute_aeth - compute the AETH (syndrome + MSN) - * @qp: the queue pair to compute the AETH for - * - * Returns the AETH. - */ -__be32 qib_compute_aeth(struct rvt_qp *qp) -{ - u32 aeth = qp->r_msn & QIB_MSN_MASK; - - if (qp->ibqp.srq) { - /* - * Shared receive queues don't generate credits. - * Set the credit field to the invalid value. - */ - aeth |= QIB_AETH_CREDIT_INVAL << QIB_AETH_CREDIT_SHIFT; - } else { - u32 min, max, x; - u32 credits; - struct rvt_rwq *wq = qp->r_rq.wq; - u32 head; - u32 tail; - - /* sanity check pointers before trusting them */ - head = wq->head; - if (head >= qp->r_rq.size) - head = 0; - tail = wq->tail; - if (tail >= qp->r_rq.size) - tail = 0; - /* - * Compute the number of credits available (RWQEs). - * XXX Not holding the r_rq.lock here so there is a small - * chance that the pair of reads are not atomic. - */ - credits = head - tail; - if ((int)credits < 0) - credits += qp->r_rq.size; - /* - * Binary search the credit table to find the code to - * use. - */ - min = 0; - max = 31; - for (;;) { - x = (min + max) / 2; - if (credit_table[x] == credits) - break; - if (credit_table[x] > credits) - max = x; - else if (min == x) - break; - else - min = x; - } - aeth |= x << QIB_AETH_CREDIT_SHIFT; - } - return cpu_to_be32(aeth); -} - void *qib_qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp, gfp_t gfp) { struct qib_qp_priv *priv; @@ -448,7 +351,6 @@ void qib_stop_send_queue(struct rvt_qp *qp) struct qib_qp_priv *priv = qp->priv; cancel_work_sync(&priv->s_work); - del_timer_sync(&qp->s_timer); } void qib_quiesce_qp(struct rvt_qp *qp) @@ -474,43 +376,6 @@ void qib_flush_qp_waiters(struct rvt_qp *qp) } /** - * qib_get_credit - flush the send work queue of a QP - * @qp: the qp who's send work queue to flush - * @aeth: the Acknowledge Extended Transport Header - * - * The QP s_lock should be held. - */ -void qib_get_credit(struct rvt_qp *qp, u32 aeth) -{ - u32 credit = (aeth >> QIB_AETH_CREDIT_SHIFT) & QIB_AETH_CREDIT_MASK; - - /* - * If the credit is invalid, we can send - * as many packets as we like. Otherwise, we have to - * honor the credit field. - */ - if (credit == QIB_AETH_CREDIT_INVAL) { - if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) { - qp->s_flags |= RVT_S_UNLIMITED_CREDIT; - if (qp->s_flags & RVT_S_WAIT_SSN_CREDIT) { - qp->s_flags &= ~RVT_S_WAIT_SSN_CREDIT; - qib_schedule_send(qp); - } - } - } else if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) { - /* Compute new LSN (i.e., MSN + credit) */ - credit = (aeth + credit_table[credit]) & QIB_MSN_MASK; - if (qib_cmp24(credit, qp->s_lsn) > 0) { - qp->s_lsn = credit; - if (qp->s_flags & RVT_S_WAIT_SSN_CREDIT) { - qp->s_flags &= ~RVT_S_WAIT_SSN_CREDIT; - qib_schedule_send(qp); - } - } - } -} - -/** * qib_check_send_wqe - validate wr/wqe * @qp - The qp * @wqe - The built wqe diff --git a/drivers/infiniband/hw/qib/qib_qsfp.c b/drivers/infiniband/hw/qib/qib_qsfp.c index 4c7c3c84a741..295d40a83bb6 100644 --- a/drivers/infiniband/hw/qib/qib_qsfp.c +++ b/drivers/infiniband/hw/qib/qib_qsfp.c @@ -485,16 +485,6 @@ void qib_qsfp_init(struct qib_qsfp_data *qd, dd->f_gpio_mod(dd, mask, mask, mask); } -void qib_qsfp_deinit(struct qib_qsfp_data *qd) -{ - /* - * There is nothing to do here for now. our work is scheduled - * with queue_work(), and flush_workqueue() from remove_one - * will block until all work setup with queue_work() - * completes. - */ -} - int qib_qsfp_dump(struct qib_pportdata *ppd, char *buf, int len) { struct qib_qsfp_cache cd; diff --git a/drivers/infiniband/hw/qib/qib_qsfp.h b/drivers/infiniband/hw/qib/qib_qsfp.h index 91908f533a2b..ad8dbd6ac0cf 100644 --- a/drivers/infiniband/hw/qib/qib_qsfp.h +++ b/drivers/infiniband/hw/qib/qib_qsfp.h @@ -186,4 +186,3 @@ extern int qib_refresh_qsfp_cache(struct qib_pportdata *ppd, extern int qib_qsfp_mod_present(struct qib_pportdata *ppd); extern void qib_qsfp_init(struct qib_qsfp_data *qd, void (*fevent)(struct work_struct *)); -extern void qib_qsfp_deinit(struct qib_qsfp_data *qd); diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c index 031433cb7206..12658e3fe154 100644 --- a/drivers/infiniband/hw/qib/qib_rc.c +++ b/drivers/infiniband/hw/qib/qib_rc.c @@ -38,7 +38,6 @@ /* cut down ridiculously long IB macro names */ #define OP(x) IB_OPCODE_RC_##x -static void rc_timeout(unsigned long arg); static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, u32 psn, u32 pmtu) @@ -50,19 +49,10 @@ static u32 restart_sge(struct rvt_sge_state *ss, struct rvt_swqe *wqe, ss->sg_list = wqe->sg_list + 1; ss->num_sge = wqe->wr.num_sge; ss->total_len = wqe->length; - qib_skip_sge(ss, len, 0); + rvt_skip_sge(ss, len, false); return wqe->length - len; } -static void start_timer(struct rvt_qp *qp) -{ - qp->s_flags |= RVT_S_TIMER; - qp->s_timer.function = rc_timeout; - /* 4.096 usec. * (1 << qp->timeout) */ - qp->s_timer.expires = jiffies + qp->timeout_jiffies; - add_timer(&qp->s_timer); -} - /** * qib_make_rc_ack - construct a response packet (ACK, NAK, or RDMA read) * @dev: the device for this QP @@ -144,7 +134,7 @@ static int qib_make_rc_ack(struct qib_ibdev *dev, struct rvt_qp *qp, qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); e->sent = 1; } - ohdr->u.aeth = qib_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); hwords++; qp->s_ack_rdma_psn = e->psn; bth2 = qp->s_ack_rdma_psn++ & QIB_PSN_MASK; @@ -153,7 +143,7 @@ static int qib_make_rc_ack(struct qib_ibdev *dev, struct rvt_qp *qp, qp->s_cur_sge = NULL; len = 0; qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE); - ohdr->u.at.aeth = qib_compute_aeth(qp); + ohdr->u.at.aeth = rvt_compute_aeth(qp); ib_u64_put(e->atomic_data, &ohdr->u.at.atomic_ack_eth); hwords += sizeof(ohdr->u.at) / sizeof(u32); bth2 = e->psn & QIB_PSN_MASK; @@ -174,7 +164,7 @@ static int qib_make_rc_ack(struct qib_ibdev *dev, struct rvt_qp *qp, if (len > pmtu) len = pmtu; else { - ohdr->u.aeth = qib_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); hwords++; qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); e = &qp->s_ack_queue[qp->s_tail_ack_queue]; @@ -197,11 +187,11 @@ normal: qp->s_cur_sge = NULL; if (qp->s_nak_state) ohdr->u.aeth = - cpu_to_be32((qp->r_msn & QIB_MSN_MASK) | + cpu_to_be32((qp->r_msn & IB_MSN_MASK) | (qp->s_nak_state << - QIB_AETH_CREDIT_SHIFT)); + IB_AETH_CREDIT_SHIFT)); else - ohdr->u.aeth = qib_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); hwords++; len = 0; bth0 = OP(ACKNOWLEDGE) << 24; @@ -257,7 +247,7 @@ int qib_make_rc_req(struct rvt_qp *qp, unsigned long *flags) goto bail; /* We are in the error state, flush the work request. */ smp_read_barrier_depends(); /* see post_one_send() */ - if (qp->s_last == ACCESS_ONCE(qp->s_head)) + if (qp->s_last == READ_ONCE(qp->s_head)) goto bail; /* If DMAs are in progress, we can't flush immediately. */ if (atomic_read(&priv->s_dma_busy)) { @@ -303,7 +293,8 @@ int qib_make_rc_req(struct rvt_qp *qp, unsigned long *flags) newreq = 0; if (qp->s_cur == qp->s_tail) { /* Check if send work queue is empty. */ - if (qp->s_tail == qp->s_head) + smp_read_barrier_depends(); /* see post_one_send() */ + if (qp->s_tail == READ_ONCE(qp->s_head)) goto bail; /* * If a fence is requested, wait for previous @@ -330,7 +321,7 @@ int qib_make_rc_req(struct rvt_qp *qp, unsigned long *flags) case IB_WR_SEND_WITH_IMM: /* If no credit, return. */ if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) && - qib_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) { + rvt_cmp_msn(wqe->ssn, qp->s_lsn + 1) > 0) { qp->s_flags |= RVT_S_WAIT_SSN_CREDIT; goto bail; } @@ -361,7 +352,7 @@ int qib_make_rc_req(struct rvt_qp *qp, unsigned long *flags) case IB_WR_RDMA_WRITE_WITH_IMM: /* If no credit, return. */ if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) && - qib_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) { + rvt_cmp_msn(wqe->ssn, qp->s_lsn + 1) > 0) { qp->s_flags |= RVT_S_WAIT_SSN_CREDIT; goto bail; } @@ -657,11 +648,11 @@ void qib_send_rc_ack(struct rvt_qp *qp) if (qp->s_mig_state == IB_MIG_MIGRATED) bth0 |= IB_BTH_MIG_REQ; if (qp->r_nak_state) - ohdr->u.aeth = cpu_to_be32((qp->r_msn & QIB_MSN_MASK) | + ohdr->u.aeth = cpu_to_be32((qp->r_msn & IB_MSN_MASK) | (qp->r_nak_state << - QIB_AETH_CREDIT_SHIFT)); + IB_AETH_CREDIT_SHIFT)); else - ohdr->u.aeth = qib_compute_aeth(qp); + ohdr->u.aeth = rvt_compute_aeth(qp); lrh0 |= ibp->sl_to_vl[qp->remote_ah_attr.sl] << 12 | qp->remote_ah_attr.sl << 4; hdr.lrh[0] = cpu_to_be16(lrh0); @@ -836,7 +827,7 @@ done: * Back up requester to resend the last un-ACKed request. * The QP r_lock and s_lock should be held and interrupts disabled. */ -static void qib_restart_rc(struct rvt_qp *qp, u32 psn, int wait) +void qib_restart_rc(struct rvt_qp *qp, u32 psn, int wait) { struct rvt_swqe *wqe = rvt_get_swqe_ptr(qp, qp->s_acked); struct qib_ibport *ibp; @@ -869,46 +860,6 @@ static void qib_restart_rc(struct rvt_qp *qp, u32 psn, int wait) } /* - * This is called from s_timer for missing responses. - */ -static void rc_timeout(unsigned long arg) -{ - struct rvt_qp *qp = (struct rvt_qp *)arg; - struct qib_ibport *ibp; - unsigned long flags; - - spin_lock_irqsave(&qp->r_lock, flags); - spin_lock(&qp->s_lock); - if (qp->s_flags & RVT_S_TIMER) { - ibp = to_iport(qp->ibqp.device, qp->port_num); - ibp->rvp.n_rc_timeouts++; - qp->s_flags &= ~RVT_S_TIMER; - del_timer(&qp->s_timer); - qib_restart_rc(qp, qp->s_last_psn + 1, 1); - qib_schedule_send(qp); - } - spin_unlock(&qp->s_lock); - spin_unlock_irqrestore(&qp->r_lock, flags); -} - -/* - * This is called from s_timer for RNR timeouts. - */ -void qib_rc_rnr_retry(unsigned long arg) -{ - struct rvt_qp *qp = (struct rvt_qp *)arg; - unsigned long flags; - - spin_lock_irqsave(&qp->s_lock, flags); - if (qp->s_flags & RVT_S_WAIT_RNR) { - qp->s_flags &= ~RVT_S_WAIT_RNR; - del_timer(&qp->s_timer); - qib_schedule_send(qp); - } - spin_unlock_irqrestore(&qp->s_lock, flags); -} - -/* * Set qp->s_sending_psn to the next PSN after the given one. * This would be psn+1 except when RDMA reads are present. */ @@ -944,7 +895,7 @@ void qib_rc_send_complete(struct rvt_qp *qp, struct ib_header *hdr) u32 opcode; u32 psn; - if (!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_OR_FLUSH_SEND)) + if (!(ib_rvt_state_ops[qp->state] & RVT_SEND_OR_FLUSH_OR_RECV_OK)) return; /* Find out where the BTH is */ @@ -971,7 +922,7 @@ void qib_rc_send_complete(struct rvt_qp *qp, struct ib_header *hdr) if ((psn & IB_BTH_REQ_ACK) && qp->s_acked != qp->s_tail && !(qp->s_flags & (RVT_S_TIMER | RVT_S_WAIT_RNR | RVT_S_WAIT_PSN)) && (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) - start_timer(qp); + rvt_add_retry_timer(qp); while (qp->s_last != qp->s_acked) { u32 s_last; @@ -1084,12 +1035,6 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, u32 ack_psn; int diff; - /* Remove QP from retry timer */ - if (qp->s_flags & (RVT_S_TIMER | RVT_S_WAIT_RNR)) { - qp->s_flags &= ~(RVT_S_TIMER | RVT_S_WAIT_RNR); - del_timer(&qp->s_timer); - } - /* * Note that NAKs implicitly ACK outstanding SEND and RDMA write * requests and implicitly NAK RDMA read and atomic requests issued @@ -1097,7 +1042,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, * request but will include an ACK'ed request(s). */ ack_psn = psn; - if (aeth >> 29) + if (aeth >> IB_AETH_NAK_SHIFT) ack_psn--; wqe = rvt_get_swqe_ptr(qp, qp->s_acked); ibp = to_iport(qp->ibqp.device, qp->port_num); @@ -1177,7 +1122,7 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, break; } - switch (aeth >> 29) { + switch (aeth >> IB_AETH_NAK_SHIFT) { case 0: /* ACK */ this_cpu_inc(*ibp->rvp.rc_acks); if (qp->s_acked != qp->s_tail) { @@ -1185,27 +1130,30 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, * We are expecting more ACKs so * reset the retransmit timer. */ - start_timer(qp); + rvt_mod_retry_timer(qp); /* * We can stop resending the earlier packets and * continue with the next packet the receiver wants. */ if (qib_cmp24(qp->s_psn, psn) <= 0) reset_psn(qp, psn + 1); - } else if (qib_cmp24(qp->s_psn, psn) <= 0) { - qp->s_state = OP(SEND_LAST); - qp->s_psn = psn + 1; + } else { + /* No more acks - kill all timers */ + rvt_stop_rc_timers(qp); + if (qib_cmp24(qp->s_psn, psn) <= 0) { + qp->s_state = OP(SEND_LAST); + qp->s_psn = psn + 1; + } } if (qp->s_flags & RVT_S_WAIT_ACK) { qp->s_flags &= ~RVT_S_WAIT_ACK; qib_schedule_send(qp); } - qib_get_credit(qp, aeth); + rvt_get_credit(qp, aeth); qp->s_rnr_retry = qp->s_rnr_retry_cnt; qp->s_retry = qp->s_retry_cnt; update_last_psn(qp, psn); - ret = 1; - goto bail; + return 1; case 1: /* RNR NAK */ ibp->rvp.n_rnr_naks++; @@ -1228,21 +1176,17 @@ static int do_rc_ack(struct rvt_qp *qp, u32 aeth, u32 psn, int opcode, reset_psn(qp, psn); qp->s_flags &= ~(RVT_S_WAIT_SSN_CREDIT | RVT_S_WAIT_ACK); - qp->s_flags |= RVT_S_WAIT_RNR; - qp->s_timer.function = qib_rc_rnr_retry; - qp->s_timer.expires = jiffies + usecs_to_jiffies( - ib_qib_rnr_table[(aeth >> QIB_AETH_CREDIT_SHIFT) & - QIB_AETH_CREDIT_MASK]); - add_timer(&qp->s_timer); - goto bail; + rvt_stop_rc_timers(qp); + rvt_add_rnr_timer(qp, aeth); + return 0; case 3: /* NAK */ if (qp->s_acked == qp->s_tail) goto bail; /* The last valid PSN is the previous PSN. */ update_last_psn(qp, psn - 1); - switch ((aeth >> QIB_AETH_CREDIT_SHIFT) & - QIB_AETH_CREDIT_MASK) { + switch ((aeth >> IB_AETH_CREDIT_SHIFT) & + IB_AETH_CREDIT_MASK) { case 0: /* PSN sequence error */ ibp->rvp.n_seq_naks++; /* @@ -1290,6 +1234,7 @@ reserved: } bail: + rvt_stop_rc_timers(qp); return ret; } @@ -1303,10 +1248,7 @@ static void rdma_seq_err(struct rvt_qp *qp, struct qib_ibport *ibp, u32 psn, struct rvt_swqe *wqe; /* Remove QP from retry timer */ - if (qp->s_flags & (RVT_S_TIMER | RVT_S_WAIT_RNR)) { - qp->s_flags &= ~(RVT_S_TIMER | RVT_S_WAIT_RNR); - del_timer(&qp->s_timer); - } + rvt_stop_rc_timers(qp); wqe = rvt_get_swqe_ptr(qp, qp->s_acked); @@ -1390,7 +1332,7 @@ static void qib_rc_rcv_resp(struct qib_ibport *ibp, /* Ignore invalid responses. */ smp_read_barrier_depends(); /* see post_one_send */ - if (qib_cmp24(psn, ACCESS_ONCE(qp->s_next_psn)) >= 0) + if (qib_cmp24(psn, READ_ONCE(qp->s_next_psn)) >= 0) goto ack_done; /* Ignore duplicate responses. */ @@ -1399,8 +1341,8 @@ static void qib_rc_rcv_resp(struct qib_ibport *ibp, /* Update credits for "ghost" ACKs */ if (diff == 0 && opcode == OP(ACKNOWLEDGE)) { aeth = be32_to_cpu(ohdr->u.aeth); - if ((aeth >> 29) == 0) - qib_get_credit(qp, aeth); + if ((aeth >> IB_AETH_NAK_SHIFT) == 0) + rvt_get_credit(qp, aeth); } goto ack_done; } @@ -1461,8 +1403,7 @@ read_middle: * We got a response so update the timeout. * 4.096 usec. * (1 << qp->timeout) */ - qp->s_flags |= RVT_S_TIMER; - mod_timer(&qp->s_timer, jiffies + qp->timeout_jiffies); + rvt_mod_retry_timer(qp); if (qp->s_flags & RVT_S_WAIT_ACK) { qp->s_flags &= ~RVT_S_WAIT_ACK; qib_schedule_send(qp); @@ -1764,25 +1705,6 @@ send_ack: return 0; } -void qib_rc_error(struct rvt_qp *qp, enum ib_wc_status err) -{ - unsigned long flags; - int lastwqe; - - spin_lock_irqsave(&qp->s_lock, flags); - lastwqe = rvt_error_qp(qp, err); - spin_unlock_irqrestore(&qp->s_lock, flags); - - if (lastwqe) { - struct ib_event ev; - - ev.device = qp->ibqp.device; - ev.element.qp = &qp->ibqp; - ev.event = IB_EVENT_QP_LAST_WQE_REACHED; - qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); - } -} - static inline void qib_update_ack_queue(struct rvt_qp *qp, unsigned n) { unsigned next; @@ -1894,17 +1816,8 @@ void qib_rc_rcv(struct qib_ctxtdata *rcd, struct ib_header *hdr, break; } - if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) { - qp->r_flags |= RVT_R_COMM_EST; - if (qp->ibqp.event_handler) { - struct ib_event ev; - - ev.device = qp->ibqp.device; - ev.element.qp = &qp->ibqp; - ev.event = IB_EVENT_COMM_EST; - qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); - } - } + if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) + rvt_comm_est(qp); /* OK, process the packet. */ switch (opcode) { @@ -2196,7 +2109,7 @@ rnr_nak: return; nack_op_err: - qib_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); qp->r_nak_state = IB_NAK_REMOTE_OPERATIONAL_ERROR; qp->r_ack_psn = qp->r_psn; /* Queue NAK for later */ @@ -2210,7 +2123,7 @@ nack_op_err: nack_inv_unlck: spin_unlock_irqrestore(&qp->s_lock, flags); nack_inv: - qib_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); qp->r_nak_state = IB_NAK_INVALID_REQUEST; qp->r_ack_psn = qp->r_psn; /* Queue NAK for later */ @@ -2224,7 +2137,7 @@ nack_inv: nack_acc_unlck: spin_unlock_irqrestore(&qp->s_lock, flags); nack_acc: - qib_rc_error(qp, IB_WC_LOC_PROT_ERR); + rvt_rc_error(qp, IB_WC_LOC_PROT_ERR); qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; qp->r_ack_psn = qp->r_psn; send_ack: diff --git a/drivers/infiniband/hw/qib/qib_ruc.c b/drivers/infiniband/hw/qib/qib_ruc.c index e54a2feeeb10..17655cc3e6fe 100644 --- a/drivers/infiniband/hw/qib/qib_ruc.c +++ b/drivers/infiniband/hw/qib/qib_ruc.c @@ -38,44 +38,6 @@ #include "qib_mad.h" /* - * Convert the AETH RNR timeout code into the number of microseconds. - */ -const u32 ib_qib_rnr_table[32] = { - 655360, /* 00: 655.36 */ - 10, /* 01: .01 */ - 20, /* 02 .02 */ - 30, /* 03: .03 */ - 40, /* 04: .04 */ - 60, /* 05: .06 */ - 80, /* 06: .08 */ - 120, /* 07: .12 */ - 160, /* 08: .16 */ - 240, /* 09: .24 */ - 320, /* 0A: .32 */ - 480, /* 0B: .48 */ - 640, /* 0C: .64 */ - 960, /* 0D: .96 */ - 1280, /* 0E: 1.28 */ - 1920, /* 0F: 1.92 */ - 2560, /* 10: 2.56 */ - 3840, /* 11: 3.84 */ - 5120, /* 12: 5.12 */ - 7680, /* 13: 7.68 */ - 10240, /* 14: 10.24 */ - 15360, /* 15: 15.36 */ - 20480, /* 16: 20.48 */ - 30720, /* 17: 30.72 */ - 40960, /* 18: 40.96 */ - 61440, /* 19: 61.44 */ - 81920, /* 1A: 81.92 */ - 122880, /* 1B: 122.88 */ - 163840, /* 1C: 163.84 */ - 245760, /* 1D: 245.76 */ - 327680, /* 1E: 327.68 */ - 491520 /* 1F: 491.52 */ -}; - -/* * Validate a RWQE and fill in the SGE state. * Return 1 if OK. */ @@ -599,11 +561,8 @@ rnr_nak: spin_lock_irqsave(&sqp->s_lock, flags); if (!(ib_rvt_state_ops[sqp->state] & RVT_PROCESS_RECV_OK)) goto clr_busy; - sqp->s_flags |= RVT_S_WAIT_RNR; - sqp->s_timer.function = qib_rc_rnr_retry; - sqp->s_timer.expires = jiffies + - usecs_to_jiffies(ib_qib_rnr_table[qp->r_min_rnr_timer]); - add_timer(&sqp->s_timer); + rvt_add_rnr_timer(sqp, qp->r_min_rnr_timer << + IB_AETH_CREDIT_SHIFT); goto clr_busy; op_err: @@ -621,7 +580,7 @@ acc_err: wc.status = IB_WC_LOC_PROT_ERR; err: /* responder goes to error state */ - qib_rc_error(qp, wc.status); + rvt_rc_error(qp, wc.status); serr: spin_lock_irqsave(&sqp->s_lock, flags); diff --git a/drivers/infiniband/hw/qib/qib_uc.c b/drivers/infiniband/hw/qib/qib_uc.c index 5b2d483451ad..b337b60fc40d 100644 --- a/drivers/infiniband/hw/qib/qib_uc.c +++ b/drivers/infiniband/hw/qib/qib_uc.c @@ -325,17 +325,8 @@ inv: goto inv; } - if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) { - qp->r_flags |= RVT_R_COMM_EST; - if (qp->ibqp.event_handler) { - struct ib_event ev; - - ev.device = qp->ibqp.device; - ev.element.qp = &qp->ibqp; - ev.event = IB_EVENT_COMM_EST; - qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); - } - } + if (qp->state == IB_QPS_RTR && !(qp->r_flags & RVT_R_COMM_EST)) + rvt_comm_est(qp); /* OK, process the packet. */ switch (opcode) { @@ -527,7 +518,7 @@ drop: return; op_err: - qib_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); return; } diff --git a/drivers/infiniband/hw/qib/qib_ud.c b/drivers/infiniband/hw/qib/qib_ud.c index f45cad1198b0..ddd4e7458750 100644 --- a/drivers/infiniband/hw/qib/qib_ud.c +++ b/drivers/infiniband/hw/qib/qib_ud.c @@ -152,7 +152,7 @@ static void qib_ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe) ret = qib_get_rwqe(qp, 0); if (ret < 0) { - qib_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); goto bail_unlock; } if (!ret) { @@ -177,7 +177,7 @@ static void qib_ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe) sizeof(grh), 1); wc.wc_flags |= IB_WC_GRH; } else - qib_skip_sge(&qp->r_sge, sizeof(struct ib_grh), 1); + rvt_skip_sge(&qp->r_sge, sizeof(struct ib_grh), true); ssge.sg_list = swqe->sg_list + 1; ssge.sge = *swqe->sg_list; ssge.num_sge = swqe->wr.num_sge; @@ -548,7 +548,7 @@ void qib_ud_rcv(struct qib_ibport *ibp, struct ib_header *hdr, ret = qib_get_rwqe(qp, 0); if (ret < 0) { - qib_rc_error(qp, IB_WC_LOC_QP_OP_ERR); + rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); return; } if (!ret) { @@ -567,7 +567,7 @@ void qib_ud_rcv(struct qib_ibport *ibp, struct ib_header *hdr, sizeof(struct ib_grh), 1); wc.wc_flags |= IB_WC_GRH; } else - qib_skip_sge(&qp->r_sge, sizeof(struct ib_grh), 1); + rvt_skip_sge(&qp->r_sge, sizeof(struct ib_grh), true); qib_copy_sge(&qp->r_sge, data, wc.byte_len - sizeof(struct ib_grh), 1); rvt_put_ss(&qp->r_sge); if (!test_and_clear_bit(RVT_R_WRID_VALID, &qp->r_aflags)) diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c index 75f08624ac05..ce83ba9a12ef 100644 --- a/drivers/infiniband/hw/qib/qib_user_pages.c +++ b/drivers/infiniband/hw/qib/qib_user_pages.c @@ -32,6 +32,7 @@ */ #include <linux/mm.h> +#include <linux/sched/signal.h> #include <linux/device.h> #include "qib.h" diff --git a/drivers/infiniband/hw/qib/qib_user_sdma.c b/drivers/infiniband/hw/qib/qib_user_sdma.c index 3e0677c51276..926f3c8eba69 100644 --- a/drivers/infiniband/hw/qib/qib_user_sdma.c +++ b/drivers/infiniband/hw/qib/qib_user_sdma.c @@ -144,8 +144,8 @@ qib_user_sdma_rb_search(struct rb_root *root, pid_t pid) struct rb_node *node = root->rb_node; while (node) { - sdma_rb_node = container_of(node, - struct qib_user_sdma_rb_node, node); + sdma_rb_node = rb_entry(node, struct qib_user_sdma_rb_node, + node); if (pid < sdma_rb_node->pid) node = node->rb_left; else if (pid > sdma_rb_node->pid) @@ -164,7 +164,7 @@ qib_user_sdma_rb_insert(struct rb_root *root, struct qib_user_sdma_rb_node *new) struct qib_user_sdma_rb_node *got; while (*node) { - got = container_of(*node, struct qib_user_sdma_rb_node, node); + got = rb_entry(*node, struct qib_user_sdma_rb_node, node); parent = *node; if (new->pid < got->pid) node = &((*node)->rb_left); diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c index 4b54c0ddd08a..83f8b5f24381 100644 --- a/drivers/infiniband/hw/qib/qib_verbs.c +++ b/drivers/infiniband/hw/qib/qib_verbs.c @@ -129,78 +129,16 @@ void qib_copy_sge(struct rvt_sge_state *ss, void *data, u32 length, int release) struct rvt_sge *sge = &ss->sge; while (length) { - u32 len = sge->length; + u32 len = rvt_get_sge_length(sge, length); - if (len > length) - len = length; - if (len > sge->sge_length) - len = sge->sge_length; - BUG_ON(len == 0); + WARN_ON_ONCE(len == 0); memcpy(sge->vaddr, data, len); - sge->vaddr += len; - sge->length -= len; - sge->sge_length -= len; - if (sge->sge_length == 0) { - if (release) - rvt_put_mr(sge->mr); - if (--ss->num_sge) - *sge = *ss->sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - break; - sge->n = 0; - } - sge->vaddr = - sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = - sge->mr->map[sge->m]->segs[sge->n].length; - } + rvt_update_sge(ss, len, release); data += len; length -= len; } } -/** - * qib_skip_sge - skip over SGE memory - XXX almost dup of prev func - * @ss: the SGE state - * @length: the number of bytes to skip - */ -void qib_skip_sge(struct rvt_sge_state *ss, u32 length, int release) -{ - struct rvt_sge *sge = &ss->sge; - - while (length) { - u32 len = sge->length; - - if (len > length) - len = length; - if (len > sge->sge_length) - len = sge->sge_length; - BUG_ON(len == 0); - sge->vaddr += len; - sge->length -= len; - sge->sge_length -= len; - if (sge->sge_length == 0) { - if (release) - rvt_put_mr(sge->mr); - if (--ss->num_sge) - *sge = *ss->sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - break; - sge->n = 0; - } - sge->vaddr = - sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = - sge->mr->map[sge->m]->segs[sge->n].length; - } - length -= len; - } -} - /* * Count the number of DMA descriptors needed to send length bytes of data. * Don't modify the qib_sge_state to get the count. @@ -468,27 +406,6 @@ static void mem_timer(unsigned long data) } } -static void update_sge(struct rvt_sge_state *ss, u32 length) -{ - struct rvt_sge *sge = &ss->sge; - - sge->vaddr += length; - sge->length -= length; - sge->sge_length -= length; - if (sge->sge_length == 0) { - if (--ss->num_sge) - *sge = *ss->sg_list++; - } else if (sge->length == 0 && sge->mr->lkey) { - if (++sge->n >= RVT_SEGSZ) { - if (++sge->m >= sge->mr->mapsz) - return; - sge->n = 0; - } - sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr; - sge->length = sge->mr->map[sge->m]->segs[sge->n].length; - } -} - #ifdef __LITTLE_ENDIAN static inline u32 get_upper_bits(u32 data, u32 shift) { @@ -646,11 +563,11 @@ static void copy_io(u32 __iomem *piobuf, struct rvt_sge_state *ss, data = clear_upper_bytes(v, extra, 0); } } - update_sge(ss, len); + rvt_update_sge(ss, len, false); length -= len; } /* Update address before sending packet. */ - update_sge(ss, length); + rvt_update_sge(ss, length, false); if (flush_wc) { /* must flush early everything before trigger word */ qib_flush_wc(); @@ -1069,7 +986,7 @@ static int qib_verbs_send_pio(struct rvt_qp *qp, struct ib_header *ibhdr, u32 *addr = (u32 *) ss->sge.vaddr; /* Update address before sending packet. */ - update_sge(ss, len); + rvt_update_sge(ss, len, false); if (flush_wc) { qib_pio_copy(piobuf, addr, dwords - 1); /* must flush early everything before trigger word */ @@ -1303,6 +1220,7 @@ static int qib_query_port(struct rvt_dev_info *rdi, u8 port_num, enum ib_mtu mtu; u16 lid = ppd->lid; + /* props being zeroed by the caller, avoid zeroing it here */ props->lid = lid ? lid : be16_to_cpu(IB_LID_PERMISSIVE); props->lmc = ppd->lmc; props->state = dd->f_iblink_state(ppd->lastibcstat); @@ -1632,7 +1550,7 @@ int qib_register_ib_device(struct qib_devdata *dd) ibdev->owner = THIS_MODULE; ibdev->node_guid = ppd->guid; ibdev->phys_port_cnt = dd->num_pports; - ibdev->dma_device = &dd->pcidev->dev; + ibdev->dev.parent = &dd->pcidev->dev; ibdev->modify_device = qib_modify_device; ibdev->process_mad = qib_process_mad; @@ -1659,6 +1577,7 @@ int qib_register_ib_device(struct qib_devdata *dd) dd->verbs_dev.rdi.driver_f.stop_send_queue = qib_stop_send_queue; dd->verbs_dev.rdi.driver_f.flush_qp_waiters = qib_flush_qp_waiters; dd->verbs_dev.rdi.driver_f.notify_error_qp = qib_notify_error_qp; + dd->verbs_dev.rdi.driver_f.notify_restart_rc = qib_restart_rc; dd->verbs_dev.rdi.driver_f.mtu_to_path_mtu = qib_mtu_to_path_mtu; dd->verbs_dev.rdi.driver_f.mtu_from_qp = qib_mtu_from_qp; dd->verbs_dev.rdi.driver_f.get_pmtu_from_attr = qib_get_pmtu_from_attr; diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h index 94fd30fdedac..212e8ce71be8 100644 --- a/drivers/infiniband/hw/qib/qib_verbs.h +++ b/drivers/infiniband/hw/qib/qib_verbs.h @@ -270,8 +270,6 @@ int qib_snapshot_counters(struct qib_pportdata *ppd, u64 *swords, int qib_get_counters(struct qib_pportdata *ppd, struct qib_verbs_counters *cntrs); -__be32 qib_compute_aeth(struct rvt_qp *qp); - /* * Functions provided by qib driver for rdmavt to use */ @@ -281,7 +279,7 @@ void qib_qp_priv_free(struct rvt_dev_info *rdi, struct rvt_qp *qp); void qib_notify_qp_reset(struct rvt_qp *qp); int qib_alloc_qpn(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt, enum ib_qp_type type, u8 port, gfp_t gfp); - +void qib_restart_rc(struct rvt_qp *qp, u32 psn, int wait); #ifdef CONFIG_DEBUG_FS struct qib_qp_iter; @@ -294,8 +292,6 @@ void qib_qp_iter_print(struct seq_file *s, struct qib_qp_iter *iter); #endif -void qib_get_credit(struct rvt_qp *qp, u32 aeth); - unsigned qib_pkt_delay(u32 plen, u8 snd_mult, u8 rcv_mult); void qib_verbs_sdma_desc_avail(struct qib_pportdata *ppd, unsigned avail); @@ -308,8 +304,6 @@ int qib_verbs_send(struct rvt_qp *qp, struct ib_header *hdr, void qib_copy_sge(struct rvt_sge_state *ss, void *data, u32 length, int release); -void qib_skip_sge(struct rvt_sge_state *ss, u32 length, int release); - void qib_uc_rcv(struct qib_ibport *ibp, struct ib_header *hdr, int has_grh, void *data, u32 tlen, struct rvt_qp *qp); @@ -326,8 +320,6 @@ void qib_rc_rnr_retry(unsigned long arg); void qib_rc_send_complete(struct rvt_qp *qp, struct ib_header *hdr); -void qib_rc_error(struct rvt_qp *qp, enum ib_wc_status err); - int qib_post_ud_send(struct rvt_qp *qp, struct ib_send_wr *wr); void qib_ud_rcv(struct qib_ibport *ibp, struct ib_header *hdr, diff --git a/drivers/infiniband/hw/usnic/usnic_common_pkt_hdr.h b/drivers/infiniband/hw/usnic/usnic_common_pkt_hdr.h index 596e0ed49a8e..bf7d197a9f42 100644 --- a/drivers/infiniband/hw/usnic/usnic_common_pkt_hdr.h +++ b/drivers/infiniband/hw/usnic/usnic_common_pkt_hdr.h @@ -34,7 +34,6 @@ #ifndef USNIC_CMN_PKT_HDR_H #define USNIC_CMN_PKT_HDR_H -#define USNIC_ROCE_ETHERTYPE (0x8915) #define USNIC_ROCE_GRH_VER (8) #define USNIC_PROTO_VER (1) #define USNIC_ROCE_GRH_VER_SHIFT (4) diff --git a/drivers/infiniband/hw/usnic/usnic_fwd.h b/drivers/infiniband/hw/usnic/usnic_fwd.h index 3a8add9ddf46..b2ac22be0731 100644 --- a/drivers/infiniband/hw/usnic/usnic_fwd.h +++ b/drivers/infiniband/hw/usnic/usnic_fwd.h @@ -36,6 +36,7 @@ #include <linux/if.h> #include <linux/netdevice.h> +#include <linux/if_ether.h> #include <linux/pci.h> #include <linux/in.h> @@ -97,7 +98,7 @@ static inline void usnic_fwd_init_usnic_filter(struct filter *filter, uint32_t usnic_id) { filter->type = FILTER_USNIC_ID; - filter->u.usnic.ethtype = USNIC_ROCE_ETHERTYPE; + filter->u.usnic.ethtype = ETH_P_IBOE; filter->u.usnic.flags = FILTER_FIELD_USNIC_ETHTYPE | FILTER_FIELD_USNIC_ID | FILTER_FIELD_USNIC_PROTO; diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index 0a89a955550b..c0c1e8b027b1 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -321,7 +321,9 @@ static int usnic_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; - err = usnic_ib_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_USNIC; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; @@ -380,7 +382,7 @@ static void *usnic_ib_device_add(struct pci_dev *dev) us_ibdev->ib_dev.node_type = RDMA_NODE_USNIC_UDP; us_ibdev->ib_dev.phys_port_cnt = USNIC_IB_PORT_CNT; us_ibdev->ib_dev.num_comp_vectors = USNIC_IB_NUM_COMP_VECTORS; - us_ibdev->ib_dev.dma_device = &dev->dev; + us_ibdev->ib_dev.dev.parent = &dev->dev; us_ibdev->ib_dev.uverbs_abi_ver = USNIC_UVERBS_ABI_VERSION; strlcpy(us_ibdev->ib_dev.name, "usnic_%d", IB_DEVICE_NAME_MAX); diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c index 69df8e353123..3284730d3c09 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c @@ -330,7 +330,7 @@ int usnic_ib_query_port(struct ib_device *ibdev, u8 port, mutex_lock(&us_ibdev->usdev_lock); __ethtool_get_link_ksettings(us_ibdev->netdev, &cmd); - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ props->lid = 0; props->lmc = 1; diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index 1ccee6ea5bc3..c49db7c33979 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -34,7 +34,8 @@ #include <linux/mm.h> #include <linux/dma-mapping.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/mm.h> #include <linux/hugetlb.h> #include <linux/iommu.h> #include <linux/workqueue.h> diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h index 71e1d55d69d6..3cd96c1b9502 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h @@ -196,13 +196,7 @@ struct pvrdma_dev { spinlock_t cmd_lock; /* Command lock. */ struct semaphore cmd_sema; struct completion cmd_done; - struct { - enum pvrdma_intr_type type; /* Intr type */ - struct msix_entry msix_entry[PVRDMA_MAX_INTERRUPTS]; - irq_handler_t handler[PVRDMA_MAX_INTERRUPTS]; - u8 enabled[PVRDMA_MAX_INTERRUPTS]; - u8 size; - } intr; + unsigned int nr_vectors; /* RDMA-related device information. */ union ib_gid *sgid_tbl; diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c index e429ca5b16aa..69bda611d313 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c @@ -373,7 +373,7 @@ retry: wc->sl = cqe->sl; wc->dlid_path_bits = cqe->dlid_path_bits; wc->port_num = cqe->port_num; - wc->vendor_err = 0; + wc->vendor_err = cqe->vendor_err; /* Update shared ring state */ pvrdma_idx_ring_inc(&cq->ring_state->rx.cons_head, cq->ibcq.cqe); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h index c06768635d65..e69d6f3cae32 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h @@ -149,12 +149,6 @@ enum pvrdma_intr_cause { PVRDMA_INTR_CAUSE_CQ = (1 << PVRDMA_INTR_VECTOR_CQ), }; -enum pvrdma_intr_type { - PVRDMA_INTR_TYPE_INTX, /* Legacy. */ - PVRDMA_INTR_TYPE_MSI, /* MSI. */ - PVRDMA_INTR_TYPE_MSIX, /* MSI-X. */ -}; - enum pvrdma_gos_bits { PVRDMA_GOS_BITS_UNK, /* Unknown. */ PVRDMA_GOS_BITS_32, /* 32-bit. */ diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c index bd8fbd3d2032..100bea5c42ff 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c @@ -132,13 +132,14 @@ static int pvrdma_port_immutable(struct ib_device *ibdev, u8 port_num, struct ib_port_attr attr; int err; - err = pvrdma_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE; immutable->max_mad_size = IB_MGMT_MAD_SIZE; return 0; } @@ -172,7 +173,7 @@ static int pvrdma_register_device(struct pvrdma_dev *dev) dev->flags = 0; dev->ib_dev.owner = THIS_MODULE; dev->ib_dev.num_comp_vectors = 1; - dev->ib_dev.dma_device = &dev->pdev->dev; + dev->ib_dev.dev.parent = &dev->pdev->dev; dev->ib_dev.uverbs_abi_ver = PVRDMA_UVERBS_ABI_VERSION; dev->ib_dev.uverbs_cmd_mask = (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) | @@ -282,7 +283,7 @@ static irqreturn_t pvrdma_intr0_handler(int irq, void *dev_id) dev_dbg(&dev->pdev->dev, "interrupt 0 (response) handler\n"); - if (dev->intr.type != PVRDMA_INTR_TYPE_MSIX) { + if (!dev->pdev->msix_enabled) { /* Legacy intr */ icr = pvrdma_read_reg(dev, PVRDMA_REG_ICR); if (icr == 0) @@ -489,31 +490,13 @@ static irqreturn_t pvrdma_intrx_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static void pvrdma_disable_msi_all(struct pvrdma_dev *dev) -{ - if (dev->intr.type == PVRDMA_INTR_TYPE_MSIX) - pci_disable_msix(dev->pdev); - else if (dev->intr.type == PVRDMA_INTR_TYPE_MSI) - pci_disable_msi(dev->pdev); -} - static void pvrdma_free_irq(struct pvrdma_dev *dev) { int i; dev_dbg(&dev->pdev->dev, "freeing interrupts\n"); - - if (dev->intr.type == PVRDMA_INTR_TYPE_MSIX) { - for (i = 0; i < dev->intr.size; i++) { - if (dev->intr.enabled[i]) { - free_irq(dev->intr.msix_entry[i].vector, dev); - dev->intr.enabled[i] = 0; - } - } - } else if (dev->intr.type == PVRDMA_INTR_TYPE_INTX || - dev->intr.type == PVRDMA_INTR_TYPE_MSI) { - free_irq(dev->pdev->irq, dev); - } + for (i = 0; i < dev->nr_vectors; i++) + free_irq(pci_irq_vector(dev->pdev, i), dev); } static void pvrdma_enable_intrs(struct pvrdma_dev *dev) @@ -528,126 +511,48 @@ static void pvrdma_disable_intrs(struct pvrdma_dev *dev) pvrdma_write_reg(dev, PVRDMA_REG_IMR, ~0); } -static int pvrdma_enable_msix(struct pci_dev *pdev, struct pvrdma_dev *dev) -{ - int i; - int ret; - - for (i = 0; i < PVRDMA_MAX_INTERRUPTS; i++) { - dev->intr.msix_entry[i].entry = i; - dev->intr.msix_entry[i].vector = i; - - switch (i) { - case 0: - /* CMD ring handler */ - dev->intr.handler[i] = pvrdma_intr0_handler; - break; - case 1: - /* Async event ring handler */ - dev->intr.handler[i] = pvrdma_intr1_handler; - break; - default: - /* Completion queue handler */ - dev->intr.handler[i] = pvrdma_intrx_handler; - break; - } - } - - ret = pci_enable_msix(pdev, dev->intr.msix_entry, - PVRDMA_MAX_INTERRUPTS); - if (!ret) { - dev->intr.type = PVRDMA_INTR_TYPE_MSIX; - dev->intr.size = PVRDMA_MAX_INTERRUPTS; - } else if (ret > 0) { - ret = pci_enable_msix(pdev, dev->intr.msix_entry, ret); - if (!ret) { - dev->intr.type = PVRDMA_INTR_TYPE_MSIX; - dev->intr.size = ret; - } else { - dev->intr.size = 0; - } - } - - dev_dbg(&pdev->dev, "using interrupt type %d, size %d\n", - dev->intr.type, dev->intr.size); - - return ret; -} - static int pvrdma_alloc_intrs(struct pvrdma_dev *dev) { - int ret = 0; - int i; + struct pci_dev *pdev = dev->pdev; + int ret = 0, i; - if (pci_find_capability(dev->pdev, PCI_CAP_ID_MSIX) && - pvrdma_enable_msix(dev->pdev, dev)) { - /* Try MSI */ - ret = pci_enable_msi(dev->pdev); - if (!ret) { - dev->intr.type = PVRDMA_INTR_TYPE_MSI; - } else { - /* Legacy INTR */ - dev->intr.type = PVRDMA_INTR_TYPE_INTX; - } + ret = pci_alloc_irq_vectors(pdev, 1, PVRDMA_MAX_INTERRUPTS, + PCI_IRQ_MSIX); + if (ret < 0) { + ret = pci_alloc_irq_vectors(pdev, 1, 1, + PCI_IRQ_MSI | PCI_IRQ_LEGACY); + if (ret < 0) + return ret; } + dev->nr_vectors = ret; - /* Request First IRQ */ - switch (dev->intr.type) { - case PVRDMA_INTR_TYPE_INTX: - case PVRDMA_INTR_TYPE_MSI: - ret = request_irq(dev->pdev->irq, pvrdma_intr0_handler, - IRQF_SHARED, DRV_NAME, dev); - if (ret) { - dev_err(&dev->pdev->dev, - "failed to request interrupt\n"); - goto disable_msi; - } - break; - case PVRDMA_INTR_TYPE_MSIX: - ret = request_irq(dev->intr.msix_entry[0].vector, - pvrdma_intr0_handler, 0, DRV_NAME, dev); - if (ret) { - dev_err(&dev->pdev->dev, - "failed to request interrupt 0\n"); - goto disable_msi; - } - dev->intr.enabled[0] = 1; - break; - default: - /* Not reached */ - break; + ret = request_irq(pci_irq_vector(dev->pdev, 0), pvrdma_intr0_handler, + pdev->msix_enabled ? 0 : IRQF_SHARED, DRV_NAME, dev); + if (ret) { + dev_err(&dev->pdev->dev, + "failed to request interrupt 0\n"); + goto out_free_vectors; } - /* For MSIX: request intr for each vector */ - if (dev->intr.size > 1) { - ret = request_irq(dev->intr.msix_entry[1].vector, - pvrdma_intr1_handler, 0, DRV_NAME, dev); + for (i = 1; i < dev->nr_vectors; i++) { + ret = request_irq(pci_irq_vector(dev->pdev, i), + i == 1 ? pvrdma_intr1_handler : + pvrdma_intrx_handler, + 0, DRV_NAME, dev); if (ret) { dev_err(&dev->pdev->dev, - "failed to request interrupt 1\n"); - goto free_irq; - } - dev->intr.enabled[1] = 1; - - for (i = 2; i < dev->intr.size; i++) { - ret = request_irq(dev->intr.msix_entry[i].vector, - pvrdma_intrx_handler, 0, - DRV_NAME, dev); - if (ret) { - dev_err(&dev->pdev->dev, - "failed to request interrupt %d\n", i); - goto free_irq; - } - dev->intr.enabled[i] = 1; + "failed to request interrupt %d\n", i); + goto free_irqs; } } return 0; -free_irq: - pvrdma_free_irq(dev); -disable_msi: - pvrdma_disable_msi_all(dev); +free_irqs: + while (--i >= 0) + free_irq(pci_irq_vector(dev->pdev, i), dev); +out_free_vectors: + pci_free_irq_vectors(pdev); return ret; } @@ -1091,7 +996,7 @@ err_free_uar_table: pvrdma_uar_table_cleanup(dev); err_free_intrs: pvrdma_free_irq(dev); - pvrdma_disable_msi_all(dev); + pci_free_irq_vectors(pdev); err_free_cq_ring: pvrdma_page_dir_cleanup(dev, &dev->cq_pdir); err_free_async_ring: @@ -1141,7 +1046,7 @@ static void pvrdma_pci_remove(struct pci_dev *pdev) pvrdma_disable_intrs(dev); pvrdma_free_irq(dev); - pvrdma_disable_msi_all(dev); + pci_free_irq_vectors(pdev); /* Deactivate pvrdma device */ pvrdma_write_reg(dev, PVRDMA_REG_CTL, PVRDMA_DEVICE_CTL_RESET); diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c index c8c01e558125..dbbfd35e7da7 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c @@ -151,7 +151,7 @@ static int pvrdma_set_rq_size(struct pvrdma_dev *dev, } static int pvrdma_set_sq_size(struct pvrdma_dev *dev, struct ib_qp_cap *req_cap, - enum ib_qp_type type, struct pvrdma_qp *qp) + struct pvrdma_qp *qp) { if (req_cap->max_send_wr > dev->dsr->caps.max_qp_wr || req_cap->max_send_sge > dev->dsr->caps.max_sge) { @@ -276,8 +276,7 @@ struct ib_qp *pvrdma_create_qp(struct ib_pd *pd, qp->is_kernel = true; ret = pvrdma_set_sq_size(to_vdev(pd->device), - &init_attr->cap, - init_attr->qp_type, qp); + &init_attr->cap, qp); if (ret) goto err_qp; diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c index c2aa52638dcb..fec17c49103b 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.c @@ -135,7 +135,7 @@ int pvrdma_query_port(struct ib_device *ibdev, u8 port, return err; } - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ props->state = pvrdma_port_state_to_ib(resp->attrs.state); props->max_mtu = pvrdma_mtu_to_ib(resp->attrs.max_mtu); @@ -275,7 +275,7 @@ int pvrdma_modify_port(struct ib_device *ibdev, u8 port, int mask, } mutex_lock(&vdev->port_mutex); - ret = pvrdma_query_port(ibdev, port, &attr); + ret = ib_query_port(ibdev, port, &attr); if (ret) goto out; diff --git a/drivers/infiniband/sw/rdmavt/Kconfig b/drivers/infiniband/sw/rdmavt/Kconfig index 1da8d01a6855..fdd001ce13d8 100644 --- a/drivers/infiniband/sw/rdmavt/Kconfig +++ b/drivers/infiniband/sw/rdmavt/Kconfig @@ -1,5 +1,6 @@ config INFINIBAND_RDMAVT tristate "RDMA verbs transport library" depends on 64BIT + select DMA_VIRT_OPS ---help--- This is a common software verbs provider for RDMA networks. diff --git a/drivers/infiniband/sw/rdmavt/Makefile b/drivers/infiniband/sw/rdmavt/Makefile index ccaa7992ac97..78b276a90401 100644 --- a/drivers/infiniband/sw/rdmavt/Makefile +++ b/drivers/infiniband/sw/rdmavt/Makefile @@ -7,7 +7,7 @@ # obj-$(CONFIG_INFINIBAND_RDMAVT) += rdmavt.o -rdmavt-y := vt.o ah.o cq.o dma.o mad.o mcast.o mmap.o mr.o pd.o qp.o srq.o \ - trace.o +rdmavt-y := vt.o ah.o cq.o mad.o mcast.o mmap.o mr.o pd.o qp.o \ + rc.o srq.o trace.o CFLAGS_trace.o = -I$(src) diff --git a/drivers/infiniband/sw/rdmavt/dma.c b/drivers/infiniband/sw/rdmavt/dma.c deleted file mode 100644 index f2cefb0d9180..000000000000 --- a/drivers/infiniband/sw/rdmavt/dma.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright(c) 2016 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#include <linux/types.h> -#include <linux/scatterlist.h> -#include <rdma/ib_verbs.h> - -#include "dma.h" - -#define BAD_DMA_ADDRESS ((u64)0) - -/* - * The following functions implement driver specific replacements - * for the ib_dma_*() functions. - * - * These functions return kernel virtual addresses instead of - * device bus addresses since the driver uses the CPU to copy - * data instead of using hardware DMA. - */ - -static int rvt_mapping_error(struct ib_device *dev, u64 dma_addr) -{ - return dma_addr == BAD_DMA_ADDRESS; -} - -static u64 rvt_dma_map_single(struct ib_device *dev, void *cpu_addr, - size_t size, enum dma_data_direction direction) -{ - if (WARN_ON(!valid_dma_direction(direction))) - return BAD_DMA_ADDRESS; - - return (u64)cpu_addr; -} - -static void rvt_dma_unmap_single(struct ib_device *dev, u64 addr, size_t size, - enum dma_data_direction direction) -{ - /* This is a stub, nothing to be done here */ -} - -static u64 rvt_dma_map_page(struct ib_device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction direction) -{ - u64 addr; - - if (WARN_ON(!valid_dma_direction(direction))) - return BAD_DMA_ADDRESS; - - addr = (u64)page_address(page); - if (addr) - addr += offset; - - return addr; -} - -static void rvt_dma_unmap_page(struct ib_device *dev, u64 addr, size_t size, - enum dma_data_direction direction) -{ - /* This is a stub, nothing to be done here */ -} - -static int rvt_map_sg(struct ib_device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction direction) -{ - struct scatterlist *sg; - u64 addr; - int i; - int ret = nents; - - if (WARN_ON(!valid_dma_direction(direction))) - return 0; - - for_each_sg(sgl, sg, nents, i) { - addr = (u64)page_address(sg_page(sg)); - if (!addr) { - ret = 0; - break; - } - sg->dma_address = addr + sg->offset; -#ifdef CONFIG_NEED_SG_DMA_LENGTH - sg->dma_length = sg->length; -#endif - } - return ret; -} - -static void rvt_unmap_sg(struct ib_device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction direction) -{ - /* This is a stub, nothing to be done here */ -} - -static int rvt_map_sg_attrs(struct ib_device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction direction, - unsigned long attrs) -{ - return rvt_map_sg(dev, sgl, nents, direction); -} - -static void rvt_unmap_sg_attrs(struct ib_device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction direction, - unsigned long attrs) -{ - return rvt_unmap_sg(dev, sg, nents, direction); -} - -static void rvt_sync_single_for_cpu(struct ib_device *dev, u64 addr, - size_t size, enum dma_data_direction dir) -{ -} - -static void rvt_sync_single_for_device(struct ib_device *dev, u64 addr, - size_t size, - enum dma_data_direction dir) -{ -} - -static void *rvt_dma_alloc_coherent(struct ib_device *dev, size_t size, - u64 *dma_handle, gfp_t flag) -{ - struct page *p; - void *addr = NULL; - - p = alloc_pages(flag, get_order(size)); - if (p) - addr = page_address(p); - if (dma_handle) - *dma_handle = (u64)addr; - return addr; -} - -static void rvt_dma_free_coherent(struct ib_device *dev, size_t size, - void *cpu_addr, u64 dma_handle) -{ - free_pages((unsigned long)cpu_addr, get_order(size)); -} - -struct ib_dma_mapping_ops rvt_default_dma_mapping_ops = { - .mapping_error = rvt_mapping_error, - .map_single = rvt_dma_map_single, - .unmap_single = rvt_dma_unmap_single, - .map_page = rvt_dma_map_page, - .unmap_page = rvt_dma_unmap_page, - .map_sg = rvt_map_sg, - .unmap_sg = rvt_unmap_sg, - .map_sg_attrs = rvt_map_sg_attrs, - .unmap_sg_attrs = rvt_unmap_sg_attrs, - .sync_single_for_cpu = rvt_sync_single_for_cpu, - .sync_single_for_device = rvt_sync_single_for_device, - .alloc_coherent = rvt_dma_alloc_coherent, - .free_coherent = rvt_dma_free_coherent -}; diff --git a/drivers/infiniband/sw/rdmavt/dma.h b/drivers/infiniband/sw/rdmavt/dma.h deleted file mode 100644 index 979f07e09195..000000000000 --- a/drivers/infiniband/sw/rdmavt/dma.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef DEF_RDMAVTDMA_H -#define DEF_RDMAVTDMA_H - -/* - * Copyright(c) 2016 Intel Corporation. - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -extern struct ib_dma_mapping_ops rvt_default_dma_mapping_ops; - -#endif /* DEF_RDMAVTDMA_H */ diff --git a/drivers/infiniband/sw/rdmavt/mad.c b/drivers/infiniband/sw/rdmavt/mad.c index f6e99778d7ca..bba241faca61 100644 --- a/drivers/infiniband/sw/rdmavt/mad.c +++ b/drivers/infiniband/sw/rdmavt/mad.c @@ -74,9 +74,9 @@ int rvt_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, u16 *out_mad_pkey_index) { /* - * MAD processing is quite different between hfi1 and qib. Therfore this - * is expected to be provided by the driver. Other drivers in the future - * may chose to implement this but it should not be made into a + * MAD processing is quite different between hfi1 and qib. Therefore + * this is expected to be provided by the driver. Other drivers in the + * future may choose to implement this but it should not be made into a * requirement. */ if (ibport_num_to_idx(ibdev, port_num) < 0) diff --git a/drivers/infiniband/sw/rdmavt/mr.c b/drivers/infiniband/sw/rdmavt/mr.c index 52fd15276ee6..ae30b6838d79 100644 --- a/drivers/infiniband/sw/rdmavt/mr.c +++ b/drivers/infiniband/sw/rdmavt/mr.c @@ -120,10 +120,19 @@ static void rvt_deinit_mregion(struct rvt_mregion *mr) mr->mapsz = 0; while (i) kfree(mr->map[--i]); + percpu_ref_exit(&mr->refcount); +} + +static void __rvt_mregion_complete(struct percpu_ref *ref) +{ + struct rvt_mregion *mr = container_of(ref, struct rvt_mregion, + refcount); + + complete(&mr->comp); } static int rvt_init_mregion(struct rvt_mregion *mr, struct ib_pd *pd, - int count) + int count, unsigned int percpu_flags) { int m, i = 0; struct rvt_dev_info *dev = ib_to_rvt(pd->device); @@ -133,19 +142,23 @@ static int rvt_init_mregion(struct rvt_mregion *mr, struct ib_pd *pd, for (; i < m; i++) { mr->map[i] = kzalloc_node(sizeof(*mr->map[0]), GFP_KERNEL, dev->dparms.node); - if (!mr->map[i]) { - rvt_deinit_mregion(mr); - return -ENOMEM; - } + if (!mr->map[i]) + goto bail; mr->mapsz++; } init_completion(&mr->comp); /* count returning the ptr to user */ - atomic_set(&mr->refcount, 1); + if (percpu_ref_init(&mr->refcount, &__rvt_mregion_complete, + percpu_flags, GFP_KERNEL)) + goto bail; + atomic_set(&mr->lkey_invalid, 0); mr->pd = pd; mr->max_segs = count; return 0; +bail: + rvt_deinit_mregion(mr); + return -ENOMEM; } /** @@ -180,8 +193,7 @@ static int rvt_alloc_lkey(struct rvt_mregion *mr, int dma_region) if (!tmr) { rcu_assign_pointer(dev->dma_mr, mr); mr->lkey_published = 1; - } else { - rvt_put_mr(mr); + rvt_get_mr(mr); } goto success; } @@ -239,11 +251,14 @@ static void rvt_free_lkey(struct rvt_mregion *mr) int freed = 0; spin_lock_irqsave(&rkt->lock, flags); - if (!mr->lkey_published) - goto out; - if (lkey == 0) { - RCU_INIT_POINTER(dev->dma_mr, NULL); + if (!lkey) { + if (mr->lkey_published) { + RCU_INIT_POINTER(dev->dma_mr, NULL); + rvt_put_mr(mr); + } } else { + if (!mr->lkey_published) + goto out; r = lkey >> (32 - dev->dparms.lkey_table_size); RCU_INIT_POINTER(rkt->table[r], NULL); } @@ -253,7 +268,7 @@ out: spin_unlock_irqrestore(&rkt->lock, flags); if (freed) { synchronize_rcu(); - rvt_put_mr(mr); + percpu_ref_kill(&mr->refcount); } } @@ -269,7 +284,7 @@ static struct rvt_mr *__rvt_alloc_mr(int count, struct ib_pd *pd) if (!mr) goto bail; - rval = rvt_init_mregion(&mr->mr, pd, count); + rval = rvt_init_mregion(&mr->mr, pd, count, 0); if (rval) goto bail; /* @@ -294,8 +309,8 @@ bail: static void __rvt_free_mr(struct rvt_mr *mr) { - rvt_deinit_mregion(&mr->mr); rvt_free_lkey(&mr->mr); + rvt_deinit_mregion(&mr->mr); kfree(mr); } @@ -305,8 +320,8 @@ static void __rvt_free_mr(struct rvt_mr *mr) * @acc: access flags * * Return: the memory region on success, otherwise returns an errno. - * Note that all DMA addresses should be created via the - * struct ib_dma_mapping_ops functions (see dma.c). + * Note that all DMA addresses should be created via the functions in + * struct dma_virt_ops. */ struct ib_mr *rvt_get_dma_mr(struct ib_pd *pd, int acc) { @@ -323,7 +338,7 @@ struct ib_mr *rvt_get_dma_mr(struct ib_pd *pd, int acc) goto bail; } - rval = rvt_init_mregion(&mr->mr, pd, 0); + rval = rvt_init_mregion(&mr->mr, pd, 0, 0); if (rval) { ret = ERR_PTR(rval); goto bail; @@ -445,8 +460,8 @@ int rvt_dereg_mr(struct ib_mr *ibmr) timeout = wait_for_completion_timeout(&mr->mr.comp, 5 * HZ); if (!timeout) { rvt_pr_err(rdi, - "rvt_dereg_mr timeout mr %p pd %p refcount %u\n", - mr, mr->mr.pd, atomic_read(&mr->mr.refcount)); + "rvt_dereg_mr timeout mr %p pd %p\n", + mr, mr->mr.pd); rvt_get_mr(&mr->mr); ret = -EBUSY; goto out; @@ -623,7 +638,8 @@ struct ib_fmr *rvt_alloc_fmr(struct ib_pd *pd, int mr_access_flags, if (!fmr) goto bail; - rval = rvt_init_mregion(&fmr->mr, pd, fmr_attr->max_pages); + rval = rvt_init_mregion(&fmr->mr, pd, fmr_attr->max_pages, + PERCPU_REF_INIT_ATOMIC); if (rval) goto bail; @@ -674,11 +690,12 @@ int rvt_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list, struct rvt_fmr *fmr = to_ifmr(ibfmr); struct rvt_lkey_table *rkt; unsigned long flags; - int m, n, i; + int m, n; + unsigned long i; u32 ps; struct rvt_dev_info *rdi = ib_to_rvt(ibfmr->device); - i = atomic_read(&fmr->mr.refcount); + i = atomic_long_read(&fmr->mr.refcount.count); if (i > 2) return -EBUSY; @@ -782,7 +799,7 @@ int rvt_lkey_ok(struct rvt_lkey_table *rkt, struct rvt_pd *pd, /* * We use LKEY == zero for kernel virtual addresses - * (see rvt_get_dma_mr and dma.c). + * (see rvt_get_dma_mr() and dma_virt_ops). */ rcu_read_lock(); if (sge->lkey == 0) { @@ -880,7 +897,7 @@ int rvt_rkey_ok(struct rvt_qp *qp, struct rvt_sge *sge, /* * We use RKEY == zero for kernel virtual addresses - * (see rvt_get_dma_mr and dma.c). + * (see rvt_get_dma_mr() and dma_virt_ops). */ rcu_read_lock(); if (rkey == 0) { diff --git a/drivers/infiniband/sw/rdmavt/pd.c b/drivers/infiniband/sw/rdmavt/pd.c index d1292f324c67..8a89afff3363 100644 --- a/drivers/infiniband/sw/rdmavt/pd.c +++ b/drivers/infiniband/sw/rdmavt/pd.c @@ -90,7 +90,7 @@ struct ib_pd *rvt_alloc_pd(struct ib_device *ibdev, spin_unlock(&dev->n_pds_lock); /* ib_alloc_pd() will initialize pd->ibpd. */ - pd->user = udata ? 1 : 0; + pd->user = !!udata; ret = &pd->ibpd; diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 2a13ac660f2b..f5ad8d4bfb39 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -51,10 +51,51 @@ #include <linux/vmalloc.h> #include <linux/slab.h> #include <rdma/ib_verbs.h> +#include <rdma/ib_hdrs.h> #include "qp.h" #include "vt.h" #include "trace.h" +static void rvt_rc_timeout(unsigned long arg); + +/* + * Convert the AETH RNR timeout code into the number of microseconds. + */ +static const u32 ib_rvt_rnr_table[32] = { + 655360, /* 00: 655.36 */ + 10, /* 01: .01 */ + 20, /* 02 .02 */ + 30, /* 03: .03 */ + 40, /* 04: .04 */ + 60, /* 05: .06 */ + 80, /* 06: .08 */ + 120, /* 07: .12 */ + 160, /* 08: .16 */ + 240, /* 09: .24 */ + 320, /* 0A: .32 */ + 480, /* 0B: .48 */ + 640, /* 0C: .64 */ + 960, /* 0D: .96 */ + 1280, /* 0E: 1.28 */ + 1920, /* 0F: 1.92 */ + 2560, /* 10: 2.56 */ + 3840, /* 11: 3.84 */ + 5120, /* 12: 5.12 */ + 7680, /* 13: 7.68 */ + 10240, /* 14: 10.24 */ + 15360, /* 15: 15.36 */ + 20480, /* 16: 20.48 */ + 30720, /* 17: 30.72 */ + 40960, /* 18: 40.96 */ + 61440, /* 19: 61.44 */ + 81920, /* 1A: 81.92 */ + 122880, /* 1B: 122.88 */ + 163840, /* 1C: 163.84 */ + 245760, /* 1D: 245.76 */ + 327680, /* 1E: 327.68 */ + 491520 /* 1F: 491.52 */ +}; + /* * Note that it is OK to post send work requests in the SQE and ERR * states; rvt_do_send() will process them and generate error @@ -200,7 +241,8 @@ int rvt_driver_qp_init(struct rvt_dev_info *rdi) if (!rdi->driver_f.free_all_qps || !rdi->driver_f.qp_priv_alloc || !rdi->driver_f.qp_priv_free || - !rdi->driver_f.notify_qp_reset) + !rdi->driver_f.notify_qp_reset || + !rdi->driver_f.notify_restart_rc) return -EINVAL; /* allocate parent object */ @@ -587,6 +629,7 @@ static void rvt_reset_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, /* Let drivers flush their waitlist */ rdi->driver_f.flush_qp_waiters(qp); + rvt_stop_rc_timers(qp); qp->s_flags &= ~(RVT_S_TIMER | RVT_S_ANY_WAIT); spin_unlock(&qp->s_lock); spin_unlock(&qp->s_hlock); @@ -594,7 +637,7 @@ static void rvt_reset_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, /* Stop the send queue and the retry timer */ rdi->driver_f.stop_send_queue(qp); - + rvt_del_timers_sync(qp); /* Wait for things to stop */ rdi->driver_f.quiesce_qp(qp); @@ -730,6 +773,11 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, if (!qp->s_ack_queue) goto bail_qp; } + /* initialize timers needed for rc qp */ + setup_timer(&qp->s_timer, rvt_rc_timeout, (unsigned long)qp); + hrtimer_init(&qp->s_rnr_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + qp->s_rnr_timer.function = rvt_rc_rnr_retry; /* * Driver needs to set up it's private QP structure and do any @@ -1868,3 +1916,184 @@ int rvt_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, } return 0; } + +/** + * qp_comm_est - handle trap with QP established + * @qp: the QP + */ +void rvt_comm_est(struct rvt_qp *qp) +{ + qp->r_flags |= RVT_R_COMM_EST; + if (qp->ibqp.event_handler) { + struct ib_event ev; + + ev.device = qp->ibqp.device; + ev.element.qp = &qp->ibqp; + ev.event = IB_EVENT_COMM_EST; + qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); + } +} +EXPORT_SYMBOL(rvt_comm_est); + +void rvt_rc_error(struct rvt_qp *qp, enum ib_wc_status err) +{ + unsigned long flags; + int lastwqe; + + spin_lock_irqsave(&qp->s_lock, flags); + lastwqe = rvt_error_qp(qp, err); + spin_unlock_irqrestore(&qp->s_lock, flags); + + if (lastwqe) { + struct ib_event ev; + + ev.device = qp->ibqp.device; + ev.element.qp = &qp->ibqp; + ev.event = IB_EVENT_QP_LAST_WQE_REACHED; + qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); + } +} +EXPORT_SYMBOL(rvt_rc_error); + +/* + * rvt_rnr_tbl_to_usec - return index into ib_rvt_rnr_table + * @index - the index + * return usec from an index into ib_rvt_rnr_table + */ +unsigned long rvt_rnr_tbl_to_usec(u32 index) +{ + return ib_rvt_rnr_table[(index & IB_AETH_CREDIT_MASK)]; +} +EXPORT_SYMBOL(rvt_rnr_tbl_to_usec); + +static inline unsigned long rvt_aeth_to_usec(u32 aeth) +{ + return ib_rvt_rnr_table[(aeth >> IB_AETH_CREDIT_SHIFT) & + IB_AETH_CREDIT_MASK]; +} + +/* + * rvt_add_retry_timer - add/start a retry timer + * @qp - the QP + * add a retry timer on the QP + */ +void rvt_add_retry_timer(struct rvt_qp *qp) +{ + struct ib_qp *ibqp = &qp->ibqp; + struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device); + + lockdep_assert_held(&qp->s_lock); + qp->s_flags |= RVT_S_TIMER; + /* 4.096 usec. * (1 << qp->timeout) */ + qp->s_timer.expires = jiffies + qp->timeout_jiffies + + rdi->busy_jiffies; + add_timer(&qp->s_timer); +} +EXPORT_SYMBOL(rvt_add_retry_timer); + +/** + * rvt_add_rnr_timer - add/start an rnr timer + * @qp - the QP + * @aeth - aeth of RNR timeout, simulated aeth for loopback + * add an rnr timer on the QP + */ +void rvt_add_rnr_timer(struct rvt_qp *qp, u32 aeth) +{ + u32 to; + + lockdep_assert_held(&qp->s_lock); + qp->s_flags |= RVT_S_WAIT_RNR; + to = rvt_aeth_to_usec(aeth); + hrtimer_start(&qp->s_rnr_timer, + ns_to_ktime(1000 * to), HRTIMER_MODE_REL); +} +EXPORT_SYMBOL(rvt_add_rnr_timer); + +/** + * rvt_stop_rc_timers - stop all timers + * @qp - the QP + * stop any pending timers + */ +void rvt_stop_rc_timers(struct rvt_qp *qp) +{ + lockdep_assert_held(&qp->s_lock); + /* Remove QP from all timers */ + if (qp->s_flags & (RVT_S_TIMER | RVT_S_WAIT_RNR)) { + qp->s_flags &= ~(RVT_S_TIMER | RVT_S_WAIT_RNR); + del_timer(&qp->s_timer); + hrtimer_try_to_cancel(&qp->s_rnr_timer); + } +} +EXPORT_SYMBOL(rvt_stop_rc_timers); + +/** + * rvt_stop_rnr_timer - stop an rnr timer + * @qp - the QP + * + * stop an rnr timer and return if the timer + * had been pending. + */ +static int rvt_stop_rnr_timer(struct rvt_qp *qp) +{ + int rval = 0; + + lockdep_assert_held(&qp->s_lock); + /* Remove QP from rnr timer */ + if (qp->s_flags & RVT_S_WAIT_RNR) { + qp->s_flags &= ~RVT_S_WAIT_RNR; + rval = hrtimer_try_to_cancel(&qp->s_rnr_timer); + } + return rval; +} + +/** + * rvt_del_timers_sync - wait for any timeout routines to exit + * @qp - the QP + */ +void rvt_del_timers_sync(struct rvt_qp *qp) +{ + del_timer_sync(&qp->s_timer); + hrtimer_cancel(&qp->s_rnr_timer); +} +EXPORT_SYMBOL(rvt_del_timers_sync); + +/** + * This is called from s_timer for missing responses. + */ +static void rvt_rc_timeout(unsigned long arg) +{ + struct rvt_qp *qp = (struct rvt_qp *)arg; + struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device); + unsigned long flags; + + spin_lock_irqsave(&qp->r_lock, flags); + spin_lock(&qp->s_lock); + if (qp->s_flags & RVT_S_TIMER) { + qp->s_flags &= ~RVT_S_TIMER; + del_timer(&qp->s_timer); + if (rdi->driver_f.notify_restart_rc) + rdi->driver_f.notify_restart_rc(qp, + qp->s_last_psn + 1, + 1); + rdi->driver_f.schedule_send(qp); + } + spin_unlock(&qp->s_lock); + spin_unlock_irqrestore(&qp->r_lock, flags); +} + +/* + * This is called from s_timer for RNR timeouts. + */ +enum hrtimer_restart rvt_rc_rnr_retry(struct hrtimer *t) +{ + struct rvt_qp *qp = container_of(t, struct rvt_qp, s_rnr_timer); + struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device); + unsigned long flags; + + spin_lock_irqsave(&qp->s_lock, flags); + rvt_stop_rnr_timer(qp); + rdi->driver_f.schedule_send(qp); + spin_unlock_irqrestore(&qp->s_lock, flags); + return HRTIMER_NORESTART; +} +EXPORT_SYMBOL(rvt_rc_rnr_retry); diff --git a/drivers/infiniband/sw/rdmavt/rc.c b/drivers/infiniband/sw/rdmavt/rc.c new file mode 100644 index 000000000000..6131cc558bdb --- /dev/null +++ b/drivers/infiniband/sw/rdmavt/rc.c @@ -0,0 +1,189 @@ +/* + * Copyright(c) 2016 Intel Corporation. + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <rdma/rdma_vt.h> +#include <rdma/ib_hdrs.h> + +/* + * Convert the AETH credit code into the number of credits. + */ +static const u16 credit_table[31] = { + 0, /* 0 */ + 1, /* 1 */ + 2, /* 2 */ + 3, /* 3 */ + 4, /* 4 */ + 6, /* 5 */ + 8, /* 6 */ + 12, /* 7 */ + 16, /* 8 */ + 24, /* 9 */ + 32, /* A */ + 48, /* B */ + 64, /* C */ + 96, /* D */ + 128, /* E */ + 192, /* F */ + 256, /* 10 */ + 384, /* 11 */ + 512, /* 12 */ + 768, /* 13 */ + 1024, /* 14 */ + 1536, /* 15 */ + 2048, /* 16 */ + 3072, /* 17 */ + 4096, /* 18 */ + 6144, /* 19 */ + 8192, /* 1A */ + 12288, /* 1B */ + 16384, /* 1C */ + 24576, /* 1D */ + 32768 /* 1E */ +}; + +/** + * rvt_compute_aeth - compute the AETH (syndrome + MSN) + * @qp: the queue pair to compute the AETH for + * + * Returns the AETH. + */ +__be32 rvt_compute_aeth(struct rvt_qp *qp) +{ + u32 aeth = qp->r_msn & IB_MSN_MASK; + + if (qp->ibqp.srq) { + /* + * Shared receive queues don't generate credits. + * Set the credit field to the invalid value. + */ + aeth |= IB_AETH_CREDIT_INVAL << IB_AETH_CREDIT_SHIFT; + } else { + u32 min, max, x; + u32 credits; + struct rvt_rwq *wq = qp->r_rq.wq; + u32 head; + u32 tail; + + /* sanity check pointers before trusting them */ + head = wq->head; + if (head >= qp->r_rq.size) + head = 0; + tail = wq->tail; + if (tail >= qp->r_rq.size) + tail = 0; + /* + * Compute the number of credits available (RWQEs). + * There is a small chance that the pair of reads are + * not atomic, which is OK, since the fuzziness is + * resolved as further ACKs go out. + */ + credits = head - tail; + if ((int)credits < 0) + credits += qp->r_rq.size; + /* + * Binary search the credit table to find the code to + * use. + */ + min = 0; + max = 31; + for (;;) { + x = (min + max) / 2; + if (credit_table[x] == credits) + break; + if (credit_table[x] > credits) { + max = x; + } else { + if (min == x) + break; + min = x; + } + } + aeth |= x << IB_AETH_CREDIT_SHIFT; + } + return cpu_to_be32(aeth); +} +EXPORT_SYMBOL(rvt_compute_aeth); + +/** + * rvt_get_credit - flush the send work queue of a QP + * @qp: the qp who's send work queue to flush + * @aeth: the Acknowledge Extended Transport Header + * + * The QP s_lock should be held. + */ +void rvt_get_credit(struct rvt_qp *qp, u32 aeth) +{ + struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device); + u32 credit = (aeth >> IB_AETH_CREDIT_SHIFT) & IB_AETH_CREDIT_MASK; + + lockdep_assert_held(&qp->s_lock); + /* + * If the credit is invalid, we can send + * as many packets as we like. Otherwise, we have to + * honor the credit field. + */ + if (credit == IB_AETH_CREDIT_INVAL) { + if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) { + qp->s_flags |= RVT_S_UNLIMITED_CREDIT; + if (qp->s_flags & RVT_S_WAIT_SSN_CREDIT) { + qp->s_flags &= ~RVT_S_WAIT_SSN_CREDIT; + rdi->driver_f.schedule_send(qp); + } + } + } else if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT)) { + /* Compute new LSN (i.e., MSN + credit) */ + credit = (aeth + credit_table[credit]) & IB_MSN_MASK; + if (rvt_cmp_msn(credit, qp->s_lsn) > 0) { + qp->s_lsn = credit; + if (qp->s_flags & RVT_S_WAIT_SSN_CREDIT) { + qp->s_flags &= ~RVT_S_WAIT_SSN_CREDIT; + rdi->driver_f.schedule_send(qp); + } + } + } +} +EXPORT_SYMBOL(rvt_get_credit); diff --git a/drivers/infiniband/sw/rdmavt/vt.c b/drivers/infiniband/sw/rdmavt/vt.c index d430c2f7cec4..0d7c6bb551d9 100644 --- a/drivers/infiniband/sw/rdmavt/vt.c +++ b/drivers/infiniband/sw/rdmavt/vt.c @@ -47,6 +47,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/dma-mapping.h> #include "vt.h" #include "trace.h" @@ -165,7 +166,7 @@ static int rvt_query_port(struct ib_device *ibdev, u8 port_num, return -EINVAL; rvp = rdi->ports[port_index]; - memset(props, 0, sizeof(*props)); + /* props being zeroed by the caller, avoid zeroing it here */ props->sm_lid = rvp->sm_lid; props->sm_sl = rvp->sm_sl; props->port_cap_flags = rvp->port_cap_flags; @@ -326,13 +327,14 @@ static int rvt_get_port_immutable(struct ib_device *ibdev, u8 port_num, if (port_index < 0) return -EINVAL; - err = rvt_query_port(ibdev, port_num, &attr); + immutable->core_cap_flags = rdi->dparms.core_cap_flags; + + err = ib_query_port(ibdev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = rdi->dparms.core_cap_flags; immutable->max_mad_size = rdi->dparms.max_mad_size; return 0; @@ -777,8 +779,7 @@ int rvt_register_device(struct rvt_dev_info *rdi) } /* DMA Operations */ - rdi->ibdev.dma_ops = - rdi->ibdev.dma_ops ? : &rvt_default_dma_mapping_ops; + rdi->ibdev.dev.dma_ops = rdi->ibdev.dev.dma_ops ? : &dma_virt_ops; /* Protection Domain */ spin_lock_init(&rdi->n_pds_lock); diff --git a/drivers/infiniband/sw/rdmavt/vt.h b/drivers/infiniband/sw/rdmavt/vt.h index 6b01eaa4461b..f363505312be 100644 --- a/drivers/infiniband/sw/rdmavt/vt.h +++ b/drivers/infiniband/sw/rdmavt/vt.h @@ -50,7 +50,6 @@ #include <rdma/rdma_vt.h> #include <linux/pci.h> -#include "dma.h" #include "pd.h" #include "qp.h" #include "ah.h" diff --git a/drivers/infiniband/sw/rxe/Kconfig b/drivers/infiniband/sw/rxe/Kconfig index 1e4e628fe7b0..7d1ac27ed251 100644 --- a/drivers/infiniband/sw/rxe/Kconfig +++ b/drivers/infiniband/sw/rxe/Kconfig @@ -2,6 +2,7 @@ config RDMA_RXE tristate "Software RDMA over Ethernet (RoCE) driver" depends on INET && PCI && INFINIBAND depends on NET_UDP_TUNNEL + select DMA_VIRT_OPS ---help--- This driver implements the InfiniBand RDMA transport over the Linux network stack. It enables a system with a diff --git a/drivers/infiniband/sw/rxe/Makefile b/drivers/infiniband/sw/rxe/Makefile index 3b3fb9d1c470..ec35ff022a42 100644 --- a/drivers/infiniband/sw/rxe/Makefile +++ b/drivers/infiniband/sw/rxe/Makefile @@ -14,7 +14,6 @@ rdma_rxe-y := \ rxe_qp.o \ rxe_cq.o \ rxe_mr.o \ - rxe_dma.o \ rxe_opcode.o \ rxe_mmap.o \ rxe_icrc.o \ diff --git a/drivers/infiniband/sw/rxe/rxe.c b/drivers/infiniband/sw/rxe/rxe.c index ab6c3c25d7ff..b12dd9b5a89d 100644 --- a/drivers/infiniband/sw/rxe/rxe.c +++ b/drivers/infiniband/sw/rxe/rxe.c @@ -178,7 +178,7 @@ static int rxe_init_ports(struct rxe_dev *rxe) return -ENOMEM; port->pkey_tbl[0] = 0xffff; - port->port_guid = rxe->ifc_ops->port_guid(rxe); + port->port_guid = rxe_port_guid(rxe); spin_lock_init(&port->port_lock); diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c index d369f24425f9..4cd55d5617f7 100644 --- a/drivers/infiniband/sw/rxe/rxe_comp.c +++ b/drivers/infiniband/sw/rxe/rxe_comp.c @@ -254,7 +254,7 @@ static inline enum comp_state check_ack(struct rxe_qp *qp, } break; default: - WARN_ON(1); + WARN_ON_ONCE(1); } /* Check operation validity. */ @@ -412,13 +412,21 @@ static void make_send_cqe(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } } +/* + * IBA Spec. Section 10.7.3.1 SIGNALED COMPLETIONS + * ---------8<---------8<------------- + * ...Note that if a completion error occurs, a Work Completion + * will always be generated, even if the signaling + * indicator requests an Unsignaled Completion. + * ---------8<---------8<------------- + */ static void do_complete(struct rxe_qp *qp, struct rxe_send_wqe *wqe) { struct rxe_cqe cqe; if ((qp->sq_sig_type == IB_SIGNAL_ALL_WR) || (wqe->wr.send_flags & IB_SEND_SIGNALED) || - (qp->req.state == QP_STATE_ERROR)) { + wqe->status != IB_WC_SUCCESS) { make_send_cqe(qp, wqe, &cqe); advance_consumer(qp->sq.queue); rxe_cq_post(qp->scq, &cqe, 0); @@ -503,57 +511,40 @@ static inline enum comp_state complete_wqe(struct rxe_qp *qp, return COMPST_GET_WQE; } -int rxe_completer(void *arg) +static void rxe_drain_resp_pkts(struct rxe_qp *qp, bool notify) { - struct rxe_qp *qp = (struct rxe_qp *)arg; - struct rxe_send_wqe *wqe = wqe; - struct sk_buff *skb = NULL; - struct rxe_pkt_info *pkt = NULL; - enum comp_state state; - - rxe_add_ref(qp); - - if (!qp->valid) { - while ((skb = skb_dequeue(&qp->resp_pkts))) { - rxe_drop_ref(qp); - kfree_skb(skb); - } - skb = NULL; - pkt = NULL; - - while (queue_head(qp->sq.queue)) - advance_consumer(qp->sq.queue); + struct sk_buff *skb; + struct rxe_send_wqe *wqe; - goto exit; + while ((skb = skb_dequeue(&qp->resp_pkts))) { + rxe_drop_ref(qp); + kfree_skb(skb); } - if (qp->req.state == QP_STATE_ERROR) { - while ((skb = skb_dequeue(&qp->resp_pkts))) { - rxe_drop_ref(qp); - kfree_skb(skb); - } - skb = NULL; - pkt = NULL; - - while ((wqe = queue_head(qp->sq.queue))) { + while ((wqe = queue_head(qp->sq.queue))) { + if (notify) { wqe->status = IB_WC_WR_FLUSH_ERR; do_complete(qp, wqe); + } else { + advance_consumer(qp->sq.queue); } - - goto exit; } +} - if (qp->req.state == QP_STATE_RESET) { - while ((skb = skb_dequeue(&qp->resp_pkts))) { - rxe_drop_ref(qp); - kfree_skb(skb); - } - skb = NULL; - pkt = NULL; +int rxe_completer(void *arg) +{ + struct rxe_qp *qp = (struct rxe_qp *)arg; + struct rxe_send_wqe *wqe = wqe; + struct sk_buff *skb = NULL; + struct rxe_pkt_info *pkt = NULL; + enum comp_state state; - while (queue_head(qp->sq.queue)) - advance_consumer(qp->sq.queue); + rxe_add_ref(qp); + if (!qp->valid || qp->req.state == QP_STATE_ERROR || + qp->req.state == QP_STATE_RESET) { + rxe_drain_resp_pkts(qp, qp->valid && + qp->req.state == QP_STATE_ERROR); goto exit; } @@ -639,6 +630,7 @@ int rxe_completer(void *arg) if (pkt) { rxe_drop_ref(pkt->qp); kfree_skb(skb); + skb = NULL; } goto done; @@ -662,6 +654,7 @@ int rxe_completer(void *arg) qp->qp_timeout_jiffies) mod_timer(&qp->retrans_timer, jiffies + qp->qp_timeout_jiffies); + WARN_ON_ONCE(skb); goto exit; case COMPST_ERROR_RETRY: @@ -674,8 +667,10 @@ int rxe_completer(void *arg) */ /* there is nothing to retry in this case */ - if (!wqe || (wqe->state == wqe_state_posted)) + if (!wqe || (wqe->state == wqe_state_posted)) { + WARN_ON_ONCE(skb); goto exit; + } if (qp->comp.retry_cnt > 0) { if (qp->comp.retry_cnt != 7) @@ -697,8 +692,10 @@ int rxe_completer(void *arg) if (pkt) { rxe_drop_ref(pkt->qp); kfree_skb(skb); + skb = NULL; } + WARN_ON_ONCE(skb); goto exit; } else { @@ -718,6 +715,9 @@ int rxe_completer(void *arg) mod_timer(&qp->rnr_nak_timer, jiffies + rnrnak_jiffies(aeth_syn(pkt) & ~AETH_TYPE_MASK)); + rxe_drop_ref(pkt->qp); + kfree_skb(skb); + skb = NULL; goto exit; } else { wqe->status = IB_WC_RNR_RETRY_EXC_ERR; @@ -726,14 +726,17 @@ int rxe_completer(void *arg) break; case COMPST_ERROR: + WARN_ON_ONCE(wqe->status == IB_WC_SUCCESS); do_complete(qp, wqe); rxe_qp_error(qp); if (pkt) { rxe_drop_ref(pkt->qp); kfree_skb(skb); + skb = NULL; } + WARN_ON_ONCE(skb); goto exit; } } @@ -742,6 +745,7 @@ exit: /* we come here if we are done with processing and want the task to * exit from the loop calling us */ + WARN_ON_ONCE(skb); rxe_drop_ref(qp); return -EAGAIN; @@ -749,6 +753,7 @@ done: /* we come here if we have processed a packet we want the task to call * us again to see if there is anything else to do */ + WARN_ON_ONCE(skb); rxe_drop_ref(qp); return 0; } diff --git a/drivers/infiniband/sw/rxe/rxe_cq.c b/drivers/infiniband/sw/rxe/rxe_cq.c index e5e6a5e7dee9..49fe42c23f4d 100644 --- a/drivers/infiniband/sw/rxe/rxe_cq.c +++ b/drivers/infiniband/sw/rxe/rxe_cq.c @@ -156,9 +156,9 @@ int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited) return 0; } -void rxe_cq_cleanup(void *arg) +void rxe_cq_cleanup(struct rxe_pool_entry *arg) { - struct rxe_cq *cq = arg; + struct rxe_cq *cq = container_of(arg, typeof(*cq), pelem); if (cq->queue) rxe_queue_cleanup(cq->queue); diff --git a/drivers/infiniband/sw/rxe/rxe_dma.c b/drivers/infiniband/sw/rxe/rxe_dma.c deleted file mode 100644 index a0f8af5851ae..000000000000 --- a/drivers/infiniband/sw/rxe/rxe_dma.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved. - * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "rxe.h" -#include "rxe_loc.h" - -#define DMA_BAD_ADDER ((u64)0) - -static int rxe_mapping_error(struct ib_device *dev, u64 dma_addr) -{ - return dma_addr == DMA_BAD_ADDER; -} - -static u64 rxe_dma_map_single(struct ib_device *dev, - void *cpu_addr, size_t size, - enum dma_data_direction direction) -{ - WARN_ON(!valid_dma_direction(direction)); - return (uintptr_t)cpu_addr; -} - -static void rxe_dma_unmap_single(struct ib_device *dev, - u64 addr, size_t size, - enum dma_data_direction direction) -{ - WARN_ON(!valid_dma_direction(direction)); -} - -static u64 rxe_dma_map_page(struct ib_device *dev, - struct page *page, - unsigned long offset, - size_t size, enum dma_data_direction direction) -{ - u64 addr; - - WARN_ON(!valid_dma_direction(direction)); - - if (offset + size > PAGE_SIZE) { - addr = DMA_BAD_ADDER; - goto done; - } - - addr = (uintptr_t)page_address(page); - if (addr) - addr += offset; - -done: - return addr; -} - -static void rxe_dma_unmap_page(struct ib_device *dev, - u64 addr, size_t size, - enum dma_data_direction direction) -{ - WARN_ON(!valid_dma_direction(direction)); -} - -static int rxe_map_sg(struct ib_device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction direction) -{ - struct scatterlist *sg; - u64 addr; - int i; - int ret = nents; - - WARN_ON(!valid_dma_direction(direction)); - - for_each_sg(sgl, sg, nents, i) { - addr = (uintptr_t)page_address(sg_page(sg)); - if (!addr) { - ret = 0; - break; - } - sg->dma_address = addr + sg->offset; -#ifdef CONFIG_NEED_SG_DMA_LENGTH - sg->dma_length = sg->length; -#endif - } - - return ret; -} - -static void rxe_unmap_sg(struct ib_device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction direction) -{ - WARN_ON(!valid_dma_direction(direction)); -} - -static int rxe_map_sg_attrs(struct ib_device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction direction, - unsigned long attrs) -{ - return rxe_map_sg(dev, sgl, nents, direction); -} - -static void rxe_unmap_sg_attrs(struct ib_device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction direction, - unsigned long attrs) -{ - rxe_unmap_sg(dev, sg, nents, direction); -} - -static void rxe_sync_single_for_cpu(struct ib_device *dev, - u64 addr, - size_t size, enum dma_data_direction dir) -{ -} - -static void rxe_sync_single_for_device(struct ib_device *dev, - u64 addr, - size_t size, enum dma_data_direction dir) -{ -} - -static void *rxe_dma_alloc_coherent(struct ib_device *dev, size_t size, - u64 *dma_handle, gfp_t flag) -{ - struct page *p; - void *addr = NULL; - - p = alloc_pages(flag, get_order(size)); - if (p) - addr = page_address(p); - - if (dma_handle) - *dma_handle = (uintptr_t)addr; - - return addr; -} - -static void rxe_dma_free_coherent(struct ib_device *dev, size_t size, - void *cpu_addr, u64 dma_handle) -{ - free_pages((unsigned long)cpu_addr, get_order(size)); -} - -struct ib_dma_mapping_ops rxe_dma_mapping_ops = { - .mapping_error = rxe_mapping_error, - .map_single = rxe_dma_map_single, - .unmap_single = rxe_dma_unmap_single, - .map_page = rxe_dma_map_page, - .unmap_page = rxe_dma_unmap_page, - .map_sg = rxe_map_sg, - .unmap_sg = rxe_unmap_sg, - .map_sg_attrs = rxe_map_sg_attrs, - .unmap_sg_attrs = rxe_unmap_sg_attrs, - .sync_single_for_cpu = rxe_sync_single_for_cpu, - .sync_single_for_device = rxe_sync_single_for_device, - .alloc_coherent = rxe_dma_alloc_coherent, - .free_coherent = rxe_dma_free_coherent -}; diff --git a/drivers/infiniband/sw/rxe/rxe_hdr.h b/drivers/infiniband/sw/rxe/rxe_hdr.h index d57b5e956ceb..6cb18406f5b8 100644 --- a/drivers/infiniband/sw/rxe/rxe_hdr.h +++ b/drivers/infiniband/sw/rxe/rxe_hdr.h @@ -53,8 +53,16 @@ struct rxe_pkt_info { }; /* Macros should be used only for received skb */ -#define SKB_TO_PKT(skb) ((struct rxe_pkt_info *)(skb)->cb) -#define PKT_TO_SKB(pkt) container_of((void *)(pkt), struct sk_buff, cb) +static inline struct rxe_pkt_info *SKB_TO_PKT(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct rxe_pkt_info) > sizeof(skb->cb)); + return (void *)skb->cb; +} + +static inline struct sk_buff *PKT_TO_SKB(struct rxe_pkt_info *pkt) +{ + return container_of((void *)pkt, struct sk_buff, cb); +} /* * IBA header types and methods diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index efe4c6a35442..183a9d379b41 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -64,7 +64,7 @@ int rxe_cq_resize_queue(struct rxe_cq *cq, int new_cqe, struct ib_udata *udata); int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited); -void rxe_cq_cleanup(void *arg); +void rxe_cq_cleanup(struct rxe_pool_entry *arg); /* rxe_mcast.c */ int rxe_mcast_get_grp(struct rxe_dev *rxe, union ib_gid *mgid, @@ -78,7 +78,7 @@ int rxe_mcast_drop_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp, void rxe_drop_all_mcast_groups(struct rxe_qp *qp); -void rxe_mc_cleanup(void *arg); +void rxe_mc_cleanup(struct rxe_pool_entry *arg); /* rxe_mmap.c */ struct rxe_mmap_info { @@ -137,10 +137,26 @@ int mem_check_range(struct rxe_mem *mem, u64 iova, size_t length); int rxe_mem_map_pages(struct rxe_dev *rxe, struct rxe_mem *mem, u64 *page, int num_pages, u64 iova); -void rxe_mem_cleanup(void *arg); +void rxe_mem_cleanup(struct rxe_pool_entry *arg); int advance_dma_data(struct rxe_dma_info *dma, unsigned int length); +/* rxe_net.c */ +int rxe_loopback(struct sk_buff *skb); +int rxe_send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, + struct sk_buff *skb); +__be64 rxe_port_guid(struct rxe_dev *rxe); +struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av, + int paylen, struct rxe_pkt_info *pkt); +int rxe_prepare(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, + struct sk_buff *skb, u32 *crc); +enum rdma_link_layer rxe_link_layer(struct rxe_dev *rxe, unsigned int port_num); +const char *rxe_parent_name(struct rxe_dev *rxe, unsigned int port_num); +struct device *rxe_dma_device(struct rxe_dev *rxe); +__be64 rxe_node_guid(struct rxe_dev *rxe); +int rxe_mcast_add(struct rxe_dev *rxe, union ib_gid *mgid); +int rxe_mcast_delete(struct rxe_dev *rxe, union ib_gid *mgid); + /* rxe_qp.c */ int rxe_qp_chk_init(struct rxe_dev *rxe, struct ib_qp_init_attr *init); @@ -162,7 +178,7 @@ void rxe_qp_error(struct rxe_qp *qp); void rxe_qp_destroy(struct rxe_qp *qp); -void rxe_qp_cleanup(void *arg); +void rxe_qp_cleanup(struct rxe_pool_entry *arg); static inline int qp_num(struct rxe_qp *qp) { @@ -221,10 +237,9 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, struct ib_srq_attr *attr, enum ib_srq_attr_mask mask, struct ib_udata *udata); -extern struct ib_dma_mapping_ops rxe_dma_mapping_ops; - void rxe_release(struct kref *kref); +void rxe_drain_req_pkts(struct rxe_qp *qp, bool notify); int rxe_completer(void *arg); int rxe_requester(void *arg); int rxe_responder(void *arg); @@ -256,9 +271,9 @@ static inline int rxe_xmit_packet(struct rxe_dev *rxe, struct rxe_qp *qp, if (pkt->mask & RXE_LOOPBACK_MASK) { memcpy(SKB_TO_PKT(skb), pkt, sizeof(*pkt)); - err = rxe->ifc_ops->loopback(skb); + err = rxe_loopback(skb); } else { - err = rxe->ifc_ops->send(rxe, pkt, skb); + err = rxe_send(rxe, pkt, skb); } if (err) { diff --git a/drivers/infiniband/sw/rxe/rxe_mcast.c b/drivers/infiniband/sw/rxe/rxe_mcast.c index fa95544ca7e0..522a7942c56c 100644 --- a/drivers/infiniband/sw/rxe/rxe_mcast.c +++ b/drivers/infiniband/sw/rxe/rxe_mcast.c @@ -61,7 +61,7 @@ int rxe_mcast_get_grp(struct rxe_dev *rxe, union ib_gid *mgid, rxe_add_key(grp, mgid); - err = rxe->ifc_ops->mcast_add(rxe, mgid); + err = rxe_mcast_add(rxe, mgid); if (err) goto err2; @@ -180,11 +180,11 @@ void rxe_drop_all_mcast_groups(struct rxe_qp *qp) } } -void rxe_mc_cleanup(void *arg) +void rxe_mc_cleanup(struct rxe_pool_entry *arg) { - struct rxe_mc_grp *grp = arg; + struct rxe_mc_grp *grp = container_of(arg, typeof(*grp), pelem); struct rxe_dev *rxe = grp->rxe; rxe_drop_key(grp); - rxe->ifc_ops->mcast_delete(rxe, &grp->mgid); + rxe_mcast_delete(rxe, &grp->mgid); } diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c index 86a6585b847d..37eea7441ca4 100644 --- a/drivers/infiniband/sw/rxe/rxe_mr.c +++ b/drivers/infiniband/sw/rxe/rxe_mr.c @@ -91,9 +91,9 @@ static void rxe_mem_init(int access, struct rxe_mem *mem) mem->map_shift = ilog2(RXE_BUF_PER_MAP); } -void rxe_mem_cleanup(void *arg) +void rxe_mem_cleanup(struct rxe_pool_entry *arg) { - struct rxe_mem *mem = arg; + struct rxe_mem *mem = container_of(arg, typeof(*mem), pelem); int i; if (mem->umem) @@ -125,7 +125,7 @@ static int rxe_mem_alloc(struct rxe_dev *rxe, struct rxe_mem *mem, int num_buf) goto err2; } - WARN_ON(!is_power_of_2(RXE_BUF_PER_MAP)); + BUILD_BUG_ON(!is_power_of_2(RXE_BUF_PER_MAP)); mem->map_shift = ilog2(RXE_BUF_PER_MAP); mem->map_mask = RXE_BUF_PER_MAP - 1; @@ -191,7 +191,7 @@ int rxe_mem_init_user(struct rxe_dev *rxe, struct rxe_pd *pd, u64 start, goto err1; } - WARN_ON(!is_power_of_2(umem->page_size)); + WARN_ON_ONCE(!is_power_of_2(umem->page_size)); mem->page_shift = ilog2(umem->page_size); mem->page_mask = umem->page_size - 1; @@ -377,7 +377,7 @@ int rxe_mem_copy(struct rxe_mem *mem, u64 iova, void *addr, int length, return 0; } - WARN_ON(!mem->map); + WARN_ON_ONCE(!mem->map); err = mem_check_range(mem, iova, length); if (err) { diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index d9d15561eb5d..d8610960630a 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -102,17 +102,17 @@ static __be64 rxe_mac_to_eui64(struct net_device *ndev) return eui64; } -static __be64 node_guid(struct rxe_dev *rxe) +__be64 rxe_node_guid(struct rxe_dev *rxe) { return rxe_mac_to_eui64(rxe->ndev); } -static __be64 port_guid(struct rxe_dev *rxe) +__be64 rxe_port_guid(struct rxe_dev *rxe) { return rxe_mac_to_eui64(rxe->ndev); } -static struct device *dma_device(struct rxe_dev *rxe) +struct device *rxe_dma_device(struct rxe_dev *rxe) { struct net_device *ndev; @@ -124,7 +124,7 @@ static struct device *dma_device(struct rxe_dev *rxe) return ndev->dev.parent; } -static int mcast_add(struct rxe_dev *rxe, union ib_gid *mgid) +int rxe_mcast_add(struct rxe_dev *rxe, union ib_gid *mgid) { int err; unsigned char ll_addr[ETH_ALEN]; @@ -135,7 +135,7 @@ static int mcast_add(struct rxe_dev *rxe, union ib_gid *mgid) return err; } -static int mcast_delete(struct rxe_dev *rxe, union ib_gid *mgid) +int rxe_mcast_delete(struct rxe_dev *rxe, union ib_gid *mgid) { int err; unsigned char ll_addr[ETH_ALEN]; @@ -243,8 +243,8 @@ static struct socket *rxe_setup_udp_tunnel(struct net *net, __be16 port, { int err; struct socket *sock; - struct udp_port_cfg udp_cfg = {0}; - struct udp_tunnel_sock_cfg tnl_cfg = {0}; + struct udp_port_cfg udp_cfg = { }; + struct udp_tunnel_sock_cfg tnl_cfg = { }; if (ipv6) { udp_cfg.family = AF_INET6; @@ -397,8 +397,8 @@ static int prepare6(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, return 0; } -static int prepare(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, - struct sk_buff *skb, u32 *crc) +int rxe_prepare(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, + struct sk_buff *skb, u32 *crc) { int err = 0; struct rxe_av *av = rxe_get_av(pkt); @@ -424,8 +424,7 @@ static void rxe_skb_tx_dtor(struct sk_buff *skb) rxe_run_task(&qp->req.task, 1); } -static int send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, - struct sk_buff *skb) +int rxe_send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, struct sk_buff *skb) { struct sk_buff *nskb; struct rxe_av *av; @@ -461,7 +460,7 @@ static int send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, return 0; } -static int loopback(struct sk_buff *skb) +int rxe_loopback(struct sk_buff *skb) { return rxe_rcv(skb); } @@ -471,8 +470,8 @@ static inline int addr_same(struct rxe_dev *rxe, struct rxe_av *av) return rxe->port.port_guid == av->grh.dgid.global.interface_id; } -static struct sk_buff *init_packet(struct rxe_dev *rxe, struct rxe_av *av, - int paylen, struct rxe_pkt_info *pkt) +struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av, + int paylen, struct rxe_pkt_info *pkt) { unsigned int hdr_len; struct sk_buff *skb; @@ -511,31 +510,16 @@ static struct sk_buff *init_packet(struct rxe_dev *rxe, struct rxe_av *av, * this is required by rxe_cfg to match rxe devices in * /sys/class/infiniband up with their underlying ethernet devices */ -static char *parent_name(struct rxe_dev *rxe, unsigned int port_num) +const char *rxe_parent_name(struct rxe_dev *rxe, unsigned int port_num) { return rxe->ndev->name; } -static enum rdma_link_layer link_layer(struct rxe_dev *rxe, - unsigned int port_num) +enum rdma_link_layer rxe_link_layer(struct rxe_dev *rxe, unsigned int port_num) { return IB_LINK_LAYER_ETHERNET; } -static struct rxe_ifc_ops ifc_ops = { - .node_guid = node_guid, - .port_guid = port_guid, - .dma_device = dma_device, - .mcast_add = mcast_add, - .mcast_delete = mcast_delete, - .prepare = prepare, - .send = send, - .loopback = loopback, - .init_packet = init_packet, - .parent_name = parent_name, - .link_layer = link_layer, -}; - struct rxe_dev *rxe_net_add(struct net_device *ndev) { int err; @@ -545,7 +529,6 @@ struct rxe_dev *rxe_net_add(struct net_device *ndev) if (!rxe) return NULL; - rxe->ifc_ops = &ifc_ops; rxe->ndev = ndev; err = rxe_add(rxe, ndev->mtu); @@ -658,7 +641,7 @@ struct notifier_block rxe_net_notifier = { .notifier_call = rxe_notify, }; -int rxe_net_ipv4_init(void) +static int rxe_net_ipv4_init(void) { recv_sockets.sk4 = rxe_setup_udp_tunnel(&init_net, htons(ROCE_V2_UDP_DPORT), false); @@ -671,7 +654,7 @@ int rxe_net_ipv4_init(void) return 0; } -int rxe_net_ipv6_init(void) +static int rxe_net_ipv6_init(void) { #if IS_ENABLED(CONFIG_IPV6) diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c index d723947a8542..75d11ee635ec 100644 --- a/drivers/infiniband/sw/rxe/rxe_pool.c +++ b/drivers/infiniband/sw/rxe/rxe_pool.c @@ -102,7 +102,7 @@ struct rxe_type_info rxe_type_info[RXE_NUM_TYPES] = { }, }; -static inline char *pool_name(struct rxe_pool *pool) +static inline const char *pool_name(struct rxe_pool *pool) { return rxe_type_info[pool->type].name; } @@ -112,13 +112,6 @@ static inline struct kmem_cache *pool_cache(struct rxe_pool *pool) return rxe_type_info[pool->type].cache; } -static inline enum rxe_elem_type rxe_type(void *arg) -{ - struct rxe_pool_entry *elem = arg; - - return elem->pool->type; -} - int rxe_cache_init(void) { int err; @@ -273,6 +266,7 @@ static u32 alloc_index(struct rxe_pool *pool) if (index >= range) index = find_first_zero_bit(pool->table, range); + WARN_ON_ONCE(index >= range); set_bit(index, pool->table); pool->last = index; return index + pool->min_index; @@ -461,7 +455,7 @@ void *rxe_pool_get_index(struct rxe_pool *pool, u32 index) out: spin_unlock_irqrestore(&pool->pool_lock, flags); - return node ? (void *)elem : NULL; + return node ? elem : NULL; } void *rxe_pool_get_key(struct rxe_pool *pool, void *key) @@ -497,5 +491,5 @@ void *rxe_pool_get_key(struct rxe_pool *pool, void *key) out: spin_unlock_irqrestore(&pool->pool_lock, flags); - return node ? ((void *)elem) : NULL; + return node ? elem : NULL; } diff --git a/drivers/infiniband/sw/rxe/rxe_pool.h b/drivers/infiniband/sw/rxe/rxe_pool.h index 4d04830adcae..47df28e43acf 100644 --- a/drivers/infiniband/sw/rxe/rxe_pool.h +++ b/drivers/infiniband/sw/rxe/rxe_pool.h @@ -57,10 +57,12 @@ enum rxe_elem_type { RXE_NUM_TYPES, /* keep me last */ }; +struct rxe_pool_entry; + struct rxe_type_info { - char *name; + const char *name; size_t size; - void (*cleanup)(void *obj); + void (*cleanup)(struct rxe_pool_entry *obj); enum rxe_pool_flags flags; u32 max_index; u32 min_index; @@ -91,7 +93,7 @@ struct rxe_pool { spinlock_t pool_lock; /* pool spinlock */ size_t elem_size; struct kref ref_cnt; - void (*cleanup)(void *obj); + void (*cleanup)(struct rxe_pool_entry *obj); enum rxe_pool_state state; enum rxe_pool_flags flags; enum rxe_elem_type type; diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index 44b2108253bd..f98a19e61a3d 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -273,13 +273,8 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, rxe_init_task(rxe, &qp->comp.task, qp, rxe_completer, "comp"); - init_timer(&qp->rnr_nak_timer); - qp->rnr_nak_timer.function = rnr_nak_timer; - qp->rnr_nak_timer.data = (unsigned long)qp; - - init_timer(&qp->retrans_timer); - qp->retrans_timer.function = retransmit_timer; - qp->retrans_timer.data = (unsigned long)qp; + setup_timer(&qp->rnr_nak_timer, rnr_nak_timer, (unsigned long)qp); + setup_timer(&qp->retrans_timer, retransmit_timer, (unsigned long)qp); qp->qp_timeout_jiffies = 0; /* Can't be set for UD/UC in modify_qp */ return 0; @@ -824,9 +819,9 @@ void rxe_qp_destroy(struct rxe_qp *qp) } /* called when the last reference to the qp is dropped */ -void rxe_qp_cleanup(void *arg) +void rxe_qp_cleanup(struct rxe_pool_entry *arg) { - struct rxe_qp *qp = arg; + struct rxe_qp *qp = container_of(arg, typeof(*qp), pelem); rxe_drop_all_mcast_groups(qp); diff --git a/drivers/infiniband/sw/rxe/rxe_recv.c b/drivers/infiniband/sw/rxe/rxe_recv.c index 252b4d637d45..50886031096f 100644 --- a/drivers/infiniband/sw/rxe/rxe_recv.c +++ b/drivers/infiniband/sw/rxe/rxe_recv.c @@ -389,7 +389,7 @@ int rxe_rcv(struct sk_buff *skb) calc_icrc = rxe_icrc_hdr(pkt, skb); calc_icrc = crc32_le(calc_icrc, (u8 *)payload_addr(pkt), payload_size(pkt)); - calc_icrc = cpu_to_be32(~calc_icrc); + calc_icrc = (__force u32)cpu_to_be32(~calc_icrc); if (unlikely(calc_icrc != pack_icrc)) { if (skb->protocol == htons(ETH_P_IPV6)) pr_warn_ratelimited("bad ICRC from %pI6c\n", diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index 73d4a97603a1..dbfde0dc6ff7 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -361,19 +361,14 @@ static inline int check_init_depth(struct rxe_qp *qp, struct rxe_send_wqe *wqe) return -EAGAIN; } -static inline int get_mtu(struct rxe_qp *qp, struct rxe_send_wqe *wqe) +static inline int get_mtu(struct rxe_qp *qp) { struct rxe_dev *rxe = to_rdev(qp->ibqp.device); - struct rxe_port *port; - struct rxe_av *av; if ((qp_type(qp) == IB_QPT_RC) || (qp_type(qp) == IB_QPT_UC)) return qp->mtu; - av = &wqe->av; - port = &rxe->port; - - return port->mtu_cap; + return rxe->port.mtu_cap; } static struct sk_buff *init_req_packet(struct rxe_qp *qp, @@ -409,7 +404,7 @@ static struct sk_buff *init_req_packet(struct rxe_qp *qp, /* init skb */ av = rxe_get_av(pkt); - skb = rxe->ifc_ops->init_packet(rxe, av, paylen, pkt); + skb = rxe_init_packet(rxe, av, paylen, pkt); if (unlikely(!skb)) return NULL; @@ -480,7 +475,7 @@ static int fill_packet(struct rxe_qp *qp, struct rxe_send_wqe *wqe, u32 *p; int err; - err = rxe->ifc_ops->prepare(rxe, pkt, skb, &crc); + err = rxe_prepare(rxe, pkt, skb, &crc); if (err) return err; @@ -599,8 +594,13 @@ int rxe_requester(void *arg) rxe_add_ref(qp); next_wqe: - if (unlikely(!qp->valid || qp->req.state == QP_STATE_ERROR)) + if (unlikely(!qp->valid)) + goto exit; + + if (unlikely(qp->req.state == QP_STATE_ERROR)) { + rxe_drain_req_pkts(qp, true); goto exit; + } if (unlikely(qp->req.state == QP_STATE_RESET)) { qp->req.wqe_index = consumer_index(qp->sq.queue); @@ -635,6 +635,7 @@ next_wqe: goto exit; } rmr->state = RXE_MEM_STATE_FREE; + rxe_drop_ref(rmr); wqe->state = wqe_state_done; wqe->status = IB_WC_SUCCESS; } else if (wqe->wr.opcode == IB_WR_REG_MR) { @@ -679,7 +680,7 @@ next_wqe: goto exit; } - mtu = get_mtu(qp, wqe); + mtu = get_mtu(qp); payload = (mask & RXE_WRITE_OR_SEND) ? wqe->dma.resid : 0; if (payload > mtu) { if (qp_type(qp) == IB_QPT_UD) { @@ -748,17 +749,8 @@ err: kfree_skb(skb); wqe->status = IB_WC_LOC_PROT_ERR; wqe->state = wqe_state_error; - - /* - * IBA Spec. Section 10.7.3.1 SIGNALED COMPLETIONS - * ---------8<---------8<------------- - * ...Note that if a completion error occurs, a Work Completion - * will always be generated, even if the signaling - * indicator requests an Unsignaled Completion. - * ---------8<---------8<------------- - */ - wqe->wr.send_flags |= IB_SEND_SIGNALED; __rxe_do_task(&qp->comp.task); + exit: rxe_drop_ref(qp); return -EAGAIN; diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 5bcf07328972..d404a8aba7af 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -307,7 +307,7 @@ static enum resp_states check_op_valid(struct rxe_qp *qp, break; default: - WARN_ON(1); + WARN_ON_ONCE(1); break; } @@ -418,7 +418,7 @@ static enum resp_states check_length(struct rxe_qp *qp, static enum resp_states check_rkey(struct rxe_qp *qp, struct rxe_pkt_info *pkt) { - struct rxe_mem *mem; + struct rxe_mem *mem = NULL; u64 va; u32 rkey; u32 resid; @@ -459,50 +459,50 @@ static enum resp_states check_rkey(struct rxe_qp *qp, mem = lookup_mem(qp->pd, access, rkey, lookup_remote); if (!mem) { state = RESPST_ERR_RKEY_VIOLATION; - goto err1; + goto err; } if (unlikely(mem->state == RXE_MEM_STATE_FREE)) { state = RESPST_ERR_RKEY_VIOLATION; - goto err1; + goto err; } if (mem_check_range(mem, va, resid)) { state = RESPST_ERR_RKEY_VIOLATION; - goto err2; + goto err; } if (pkt->mask & RXE_WRITE_MASK) { if (resid > mtu) { if (pktlen != mtu || bth_pad(pkt)) { state = RESPST_ERR_LENGTH; - goto err2; + goto err; } qp->resp.resid = mtu; } else { if (pktlen != resid) { state = RESPST_ERR_LENGTH; - goto err2; + goto err; } if ((bth_pad(pkt) != (0x3 & (-resid)))) { /* This case may not be exactly that * but nothing else fits. */ state = RESPST_ERR_LENGTH; - goto err2; + goto err; } } } - WARN_ON(qp->resp.mr); + WARN_ON_ONCE(qp->resp.mr); qp->resp.mr = mem; return RESPST_EXECUTE; -err2: - rxe_drop_ref(mem); -err1: +err: + if (mem) + rxe_drop_ref(mem); return state; } @@ -608,7 +608,7 @@ static struct sk_buff *prepare_ack_packet(struct rxe_qp *qp, pad = (-payload) & 0x3; paylen = rxe_opcode[opcode].length + payload + pad + RXE_ICRC_SIZE; - skb = rxe->ifc_ops->init_packet(rxe, &qp->pri_av, paylen, ack); + skb = rxe_init_packet(rxe, &qp->pri_av, paylen, ack); if (!skb) return NULL; @@ -637,7 +637,7 @@ static struct sk_buff *prepare_ack_packet(struct rxe_qp *qp, if (ack->mask & RXE_ATMACK_MASK) atmack_set_orig(ack, qp->resp.atomic_orig); - err = rxe->ifc_ops->prepare(rxe, ack, skb, &crc); + err = rxe_prepare(rxe, ack, skb, &crc); if (err) { kfree_skb(skb); return NULL; @@ -808,9 +808,10 @@ static enum resp_states execute(struct rxe_qp *qp, struct rxe_pkt_info *pkt) err = process_atomic(qp, pkt); if (err) return err; - } else + } else { /* Unreachable */ - WARN_ON(1); + WARN_ON_ONCE(1); + } /* We successfully processed this new request. */ qp->resp.msn++; @@ -906,6 +907,7 @@ static enum resp_states do_complete(struct rxe_qp *qp, return RESPST_ERROR; } rmr->state = RXE_MEM_STATE_FREE; + rxe_drop_ref(rmr); } wc->qp = &qp->ibqp; @@ -1206,6 +1208,19 @@ static enum resp_states do_class_d1e_error(struct rxe_qp *qp) } } +void rxe_drain_req_pkts(struct rxe_qp *qp, bool notify) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&qp->req_pkts))) { + rxe_drop_ref(qp); + kfree_skb(skb); + } + + while (!qp->srq && qp->rq.queue && queue_head(qp->rq.queue)) + advance_consumer(qp->rq.queue); +} + int rxe_responder(void *arg) { struct rxe_qp *qp = (struct rxe_qp *)arg; @@ -1373,21 +1388,10 @@ int rxe_responder(void *arg) goto exit; - case RESPST_RESET: { - struct sk_buff *skb; - - while ((skb = skb_dequeue(&qp->req_pkts))) { - rxe_drop_ref(qp); - kfree_skb(skb); - } - - while (!qp->srq && qp->rq.queue && - queue_head(qp->rq.queue)) - advance_consumer(qp->rq.queue); - + case RESPST_RESET: + rxe_drain_req_pkts(qp, false); qp->resp.wqe = NULL; goto exit; - } case RESPST_ERROR: qp->resp.goto_error = 0; @@ -1396,7 +1400,7 @@ int rxe_responder(void *arg) goto exit; default: - WARN_ON(1); + WARN_ON_ONCE(1); } } diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index beb7021ff18a..5113e502f6f9 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include <linux/dma-mapping.h> #include "rxe.h" #include "rxe_loc.h" #include "rxe_queue.h" @@ -86,6 +87,7 @@ static int rxe_query_port(struct ib_device *dev, port = &rxe->port; + /* *attr being zeroed by the caller, avoid zeroing it here */ *attr = port->attr; mutex_lock(&rxe->usdev_lock); @@ -168,7 +170,7 @@ static int rxe_query_pkey(struct ib_device *device, struct rxe_port *port; if (unlikely(port_num != 1)) { - dev_warn(device->dma_device, "invalid port_num = %d\n", + dev_warn(device->dev.parent, "invalid port_num = %d\n", port_num); goto err1; } @@ -176,7 +178,7 @@ static int rxe_query_pkey(struct ib_device *device, port = &rxe->port; if (unlikely(index >= port->attr.pkey_tbl_len)) { - dev_warn(device->dma_device, "invalid index = %d\n", + dev_warn(device->dev.parent, "invalid index = %d\n", index); goto err1; } @@ -234,7 +236,7 @@ static enum rdma_link_layer rxe_get_link_layer(struct ib_device *dev, { struct rxe_dev *rxe = to_rdev(dev); - return rxe->ifc_ops->link_layer(rxe, port_num); + return rxe_link_layer(rxe, port_num); } static struct ib_ucontext *rxe_alloc_ucontext(struct ib_device *dev, @@ -261,13 +263,14 @@ static int rxe_port_immutable(struct ib_device *dev, u8 port_num, int err; struct ib_port_attr attr; - err = rxe_query_port(dev, port_num, &attr); + immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP; + + err = ib_query_port(dev, port_num, &attr); if (err) return err; immutable->pkey_tbl_len = attr.pkey_tbl_len; immutable->gid_tbl_len = attr.gid_tbl_len; - immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP; immutable->max_mad_size = IB_MGMT_MAD_SIZE; return 0; @@ -1209,10 +1212,8 @@ static ssize_t rxe_show_parent(struct device *device, { struct rxe_dev *rxe = container_of(device, struct rxe_dev, ib_dev.dev); - char *name; - name = rxe->ifc_ops->parent_name(rxe, 1); - return snprintf(buf, 16, "%s\n", name); + return snprintf(buf, 16, "%s\n", rxe_parent_name(rxe, 1)); } static DEVICE_ATTR(parent, S_IRUGO, rxe_show_parent, NULL); @@ -1234,10 +1235,10 @@ int rxe_register_device(struct rxe_dev *rxe) dev->node_type = RDMA_NODE_IB_CA; dev->phys_port_cnt = 1; dev->num_comp_vectors = RXE_NUM_COMP_VECTORS; - dev->dma_device = rxe->ifc_ops->dma_device(rxe); + dev->dev.parent = rxe_dma_device(rxe); dev->local_dma_lkey = 0; - dev->node_guid = rxe->ifc_ops->node_guid(rxe); - dev->dma_ops = &rxe_dma_mapping_ops; + dev->node_guid = rxe_node_guid(rxe); + dev->dev.dma_ops = &dma_virt_ops; dev->uverbs_abi_ver = RXE_UVERBS_ABI_VERSION; dev->uverbs_cmd_mask = BIT_ULL(IB_USER_VERBS_CMD_GET_CONTEXT) diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h index cac1d52a08f0..e100c500ae85 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.h +++ b/drivers/infiniband/sw/rxe/rxe_verbs.h @@ -372,26 +372,6 @@ struct rxe_port { u32 qp_gsi_index; }; -/* callbacks from rdma_rxe to network interface layer */ -struct rxe_ifc_ops { - void (*release)(struct rxe_dev *rxe); - __be64 (*node_guid)(struct rxe_dev *rxe); - __be64 (*port_guid)(struct rxe_dev *rxe); - struct device *(*dma_device)(struct rxe_dev *rxe); - int (*mcast_add)(struct rxe_dev *rxe, union ib_gid *mgid); - int (*mcast_delete)(struct rxe_dev *rxe, union ib_gid *mgid); - int (*prepare)(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, - struct sk_buff *skb, u32 *crc); - int (*send)(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, - struct sk_buff *skb); - int (*loopback)(struct sk_buff *skb); - struct sk_buff *(*init_packet)(struct rxe_dev *rxe, struct rxe_av *av, - int paylen, struct rxe_pkt_info *pkt); - char *(*parent_name)(struct rxe_dev *rxe, unsigned int port_num); - enum rdma_link_layer (*link_layer)(struct rxe_dev *rxe, - unsigned int port_num); -}; - struct rxe_dev { struct ib_device ib_dev; struct ib_device_attr attr; @@ -400,8 +380,6 @@ struct rxe_dev { struct kref ref_cnt; struct mutex usdev_lock; - struct rxe_ifc_ops *ifc_ops; - struct net_device *ndev; int xmit_errors; @@ -475,6 +453,6 @@ static inline struct rxe_mem *to_rmw(struct ib_mw *mw) int rxe_register_device(struct rxe_dev *rxe); int rxe_unregister_device(struct rxe_dev *rxe); -void rxe_mc_cleanup(void *arg); +void rxe_mc_cleanup(struct rxe_pool_entry *arg); #endif /* RXE_VERBS_H */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index da12717a3eb7..bed233bf45c3 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -500,9 +500,9 @@ void ipoib_pkey_event(struct work_struct *work); void ipoib_ib_dev_cleanup(struct net_device *dev); int ipoib_ib_dev_open(struct net_device *dev); -int ipoib_ib_dev_up(struct net_device *dev); -int ipoib_ib_dev_down(struct net_device *dev); -int ipoib_ib_dev_stop(struct net_device *dev); +void ipoib_ib_dev_up(struct net_device *dev); +void ipoib_ib_dev_down(struct net_device *dev); +void ipoib_ib_dev_stop(struct net_device *dev); void ipoib_pkey_dev_check_presence(struct net_device *dev); int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port); @@ -513,7 +513,7 @@ void ipoib_mcast_carrier_on_task(struct work_struct *work); void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb); void ipoib_mcast_restart_task(struct work_struct *work); -int ipoib_mcast_start_thread(struct net_device *dev); +void ipoib_mcast_start_thread(struct net_device *dev); int ipoib_mcast_stop_thread(struct net_device *dev); void ipoib_mcast_dev_down(struct net_device *dev); @@ -593,7 +593,7 @@ void ipoib_pkey_open(struct ipoib_dev_priv *priv); void ipoib_drain_cq(struct net_device *dev); void ipoib_set_ethtool_ops(struct net_device *dev); -int ipoib_set_dev_features(struct ipoib_dev_priv *priv, struct ib_device *hca); +void ipoib_set_dev_features(struct ipoib_dev_priv *priv, struct ib_device *hca); #define IPOIB_FLAGS_RC 0x80 #define IPOIB_FLAGS_UC 0x40 diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 096c4f6fbd65..0cdf2b7f272f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -38,6 +38,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/moduleparam.h> +#include <linux/sched/signal.h> #include "ipoib.h" @@ -363,7 +364,7 @@ static int ipoib_cm_nonsrq_init_rx(struct net_device *dev, struct ib_cm_id *cm_i t = kmalloc(sizeof *t, GFP_KERNEL); if (!t) { ret = -ENOMEM; - goto err_free; + goto err_free_1; } ipoib_cm_init_rx_wr(dev, &t->wr, t->sge); @@ -410,6 +411,8 @@ err_count: err_free: kfree(t); + +err_free_1: ipoib_cm_free_rx_ring(dev, rx->rx_ring); return ret; @@ -820,9 +823,12 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc) wc->status != IB_WC_WR_FLUSH_ERR) { struct ipoib_neigh *neigh; - ipoib_dbg(priv, "failed cm send event " - "(status=%d, wrid=%d vend_err %x)\n", - wc->status, wr_id, wc->vendor_err); + if (wc->status != IB_WC_RNR_RETRY_EXC_ERR) + ipoib_warn(priv, "failed cm send event (status=%d, wrid=%d vend_err %x)\n", + wc->status, wr_id, wc->vendor_err); + else + ipoib_dbg(priv, "failed cm send event (status=%d, wrid=%d vend_err %x)\n", + wc->status, wr_id, wc->vendor_err); spin_lock_irqsave(&priv->lock, flags); neigh = tx->neigh; @@ -1015,9 +1021,10 @@ static int ipoib_cm_rep_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even while ((skb = __skb_dequeue(&skqueue))) { skb->dev = p->dev; - if (dev_queue_xmit(skb)) - ipoib_warn(priv, "dev_queue_xmit failed " - "to requeue packet\n"); + ret = dev_queue_xmit(skb); + if (ret) + ipoib_warn(priv, "%s:dev_queue_xmit failed to re-queue packet, ret:%d\n", + __func__, ret); } ret = ib_send_cm_rtu(cm_id, NULL, 0); @@ -1151,13 +1158,13 @@ static int ipoib_cm_tx_init(struct ipoib_cm_tx *p, u32 qpn, ret = ipoib_cm_modify_tx_init(p->dev, p->id, p->qp); if (ret) { ipoib_warn(priv, "failed to modify tx qp to rtr: %d\n", ret); - goto err_modify; + goto err_modify_send; } ret = ipoib_cm_send_req(p->dev, p->id, p->qp, qpn, pathrec); if (ret) { ipoib_warn(priv, "failed to send cm req: %d\n", ret); - goto err_send_cm; + goto err_modify_send; } ipoib_dbg(priv, "Request connection 0x%x for gid %pI6 qpn 0x%x\n", @@ -1165,8 +1172,7 @@ static int ipoib_cm_tx_init(struct ipoib_cm_tx *p, u32 qpn, return 0; -err_send_cm: -err_modify: +err_modify_send: ib_destroy_cm_id(p->id); err_id: p->id = NULL; @@ -1388,7 +1394,7 @@ static void ipoib_cm_tx_reap(struct work_struct *work) while (!list_empty(&priv->cm.reap_list)) { p = list_entry(priv->cm.reap_list.next, typeof(*p), list); - list_del(&p->list); + list_del_init(&p->list); spin_unlock_irqrestore(&priv->lock, flags); netif_tx_unlock_bh(dev); ipoib_cm_tx_destroy(p); @@ -1507,12 +1513,14 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr, ret = ipoib_set_mode(dev, buf); - rtnl_unlock(); - - if (!ret) - return count; + /* The assumption is that the function ipoib_set_mode returned + * with the rtnl held by it, if not the value -EBUSY returned, + * then no need to rtnl_unlock + */ + if (ret != -EBUSY) + rtnl_unlock(); - return ret; + return (!ret || ret == -EBUSY) ? count : ret; } static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, show_mode, set_mode); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c index 7b6d40ff1acf..bac455a1942d 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c @@ -65,7 +65,7 @@ static void ipoib_get_drvinfo(struct net_device *netdev, ib_get_device_fw_str(priv->ca, drvinfo->fw_version, sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, dev_name(priv->ca->dma_device), + strlcpy(drvinfo->bus_info, dev_name(priv->ca->dev.parent), sizeof(drvinfo->bus_info)); strlcpy(drvinfo->version, ipoib_driver_version, diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index 5038f9d2d753..12c4f84a6639 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -755,7 +755,7 @@ void ipoib_pkey_dev_check_presence(struct net_device *dev) set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags); } -int ipoib_ib_dev_up(struct net_device *dev) +void ipoib_ib_dev_up(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -763,15 +763,15 @@ int ipoib_ib_dev_up(struct net_device *dev) if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) { ipoib_dbg(priv, "PKEY is not assigned.\n"); - return 0; + return; } set_bit(IPOIB_FLAG_OPER_UP, &priv->flags); - return ipoib_mcast_start_thread(dev); + ipoib_mcast_start_thread(dev); } -int ipoib_ib_dev_down(struct net_device *dev) +void ipoib_ib_dev_down(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -784,8 +784,6 @@ int ipoib_ib_dev_down(struct net_device *dev) ipoib_mcast_dev_flush(dev); ipoib_flush_paths(dev); - - return 0; } static int recvs_pending(struct net_device *dev) @@ -840,7 +838,7 @@ void ipoib_drain_cq(struct net_device *dev) local_bh_enable(); } -int ipoib_ib_dev_stop(struct net_device *dev) +void ipoib_ib_dev_stop(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ib_qp_attr qp_attr; @@ -913,8 +911,6 @@ timeout: ipoib_flush_ah(dev); ib_req_notify_cq(priv->recv_cq, IB_CQ_NEXT_COMP); - - return 0; } int ipoib_ib_dev_init(struct net_device *dev, struct ib_device *ca, int port) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 3ce0765a05ab..d1d3fb7a6127 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -126,8 +126,7 @@ int ipoib_open(struct net_device *dev) goto err_disable; } - if (ipoib_ib_dev_up(dev)) - goto err_stop; + ipoib_ib_dev_up(dev); if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) { struct ipoib_dev_priv *cpriv; @@ -150,9 +149,6 @@ int ipoib_open(struct net_device *dev) return 0; -err_stop: - ipoib_ib_dev_stop(dev); - err_disable: clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); @@ -229,6 +225,10 @@ static int ipoib_change_mtu(struct net_device *dev, int new_mtu) priv->admin_mtu = new_mtu; + if (priv->mcast_mtu < priv->admin_mtu) + ipoib_dbg(priv, "MTU must be smaller than the underlying " + "link layer MTU - 4 (%u)\n", priv->mcast_mtu); + dev->mtu = min(priv->mcast_mtu, priv->admin_mtu); return 0; @@ -470,6 +470,13 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) { struct ipoib_dev_priv *priv = netdev_priv(dev); + if ((test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags) && + !strcmp(buf, "connected\n")) || + (!test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags) && + !strcmp(buf, "datagram\n"))) { + return 0; + } + /* flush paths if we switch modes so that connections are restarted */ if (IPOIB_CM_SUPPORTED(dev->dev_addr) && !strcmp(buf, "connected\n")) { set_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags); @@ -481,8 +488,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) priv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM; ipoib_flush_paths(dev); - rtnl_lock(); - return 0; + return (!rtnl_trylock()) ? -EBUSY : 0; } if (!strcmp(buf, "datagram\n")) { @@ -491,8 +497,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu)); rtnl_unlock(); ipoib_flush_paths(dev); - rtnl_lock(); - return 0; + return (!rtnl_trylock()) ? -EBUSY : 0; } return -EINVAL; @@ -716,6 +721,14 @@ int ipoib_check_sm_sendonly_fullmember_support(struct ipoib_dev_priv *priv) return ret; } +static void push_pseudo_header(struct sk_buff *skb, const char *daddr) +{ + struct ipoib_pseudo_header *phdr; + + phdr = (struct ipoib_pseudo_header *)skb_push(skb, sizeof(*phdr)); + memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); +} + void ipoib_flush_paths(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -834,10 +847,12 @@ static void path_rec_completion(int status, ipoib_put_ah(old_ah); while ((skb = __skb_dequeue(&skqueue))) { + int ret; skb->dev = dev; - if (dev_queue_xmit(skb)) - ipoib_warn(priv, "dev_queue_xmit failed " - "to requeue packet\n"); + ret = dev_queue_xmit(skb); + if (ret) + ipoib_warn(priv, "%s: dev_queue_xmit failed to re-queue packet, ret:%d\n", + __func__, ret); } } @@ -940,8 +955,7 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, } if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { - /* put pseudoheader back on for next time */ - skb_push(skb, IPOIB_PSEUDO_LEN); + push_pseudo_header(skb, neigh->daddr); __skb_queue_tail(&neigh->queue, skb); } else { ipoib_warn(priv, "queue length limit %d. Packet drop.\n", @@ -959,10 +973,12 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr, if (!path->query && path_rec_start(dev, path)) goto err_path; - if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) + if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { + push_pseudo_header(skb, neigh->daddr); __skb_queue_tail(&neigh->queue, skb); - else + } else { goto err_drop; + } } spin_unlock_irqrestore(&priv->lock, flags); @@ -998,8 +1014,7 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, } if (path) { if (skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { - /* put pseudoheader back on for next time */ - skb_push(skb, IPOIB_PSEUDO_LEN); + push_pseudo_header(skb, phdr->hwaddr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -1031,8 +1046,7 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, return; } else if ((path->query || !path_rec_start(dev, path)) && skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) { - /* put pseudoheader back on for next time */ - skb_push(skb, IPOIB_PSEUDO_LEN); + push_pseudo_header(skb, phdr->hwaddr); __skb_queue_tail(&path->queue, skb); } else { ++dev->stats.tx_dropped; @@ -1113,8 +1127,7 @@ send_using_neigh: } if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { - /* put pseudoheader back on for next time */ - skb_push(skb, sizeof(*phdr)); + push_pseudo_header(skb, phdr->hwaddr); spin_lock_irqsave(&priv->lock, flags); __skb_queue_tail(&neigh->queue, skb); spin_unlock_irqrestore(&priv->lock, flags); @@ -1146,7 +1159,6 @@ static int ipoib_hard_header(struct sk_buff *skb, unsigned short type, const void *daddr, const void *saddr, unsigned len) { - struct ipoib_pseudo_header *phdr; struct ipoib_header *header; header = (struct ipoib_header *) skb_push(skb, sizeof *header); @@ -1159,8 +1171,7 @@ static int ipoib_hard_header(struct sk_buff *skb, * destination address into skb hard header so we can figure out where * to send the packet later. */ - phdr = (struct ipoib_pseudo_header *) skb_push(skb, sizeof(*phdr)); - memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); + push_pseudo_header(skb, daddr); return IPOIB_HARD_LEN; } @@ -1286,7 +1297,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from path/mc list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); } else { np = &neigh->hnext; @@ -1450,7 +1461,7 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from parent list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); return; } else { @@ -1535,7 +1546,7 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from parent list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); } else { np = &neigh->hnext; @@ -1577,7 +1588,7 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv) rcu_dereference_protected(neigh->hnext, lockdep_is_held(&priv->lock))); /* remove from path/mc list */ - list_del(&neigh->list); + list_del_init(&neigh->list); call_rcu(&neigh->rcu, ipoib_neigh_reclaim); } } @@ -1984,7 +1995,7 @@ int ipoib_add_pkey_attr(struct net_device *dev) return device_create_file(&dev->dev, &dev_attr_pkey); } -int ipoib_set_dev_features(struct ipoib_dev_priv *priv, struct ib_device *hca) +void ipoib_set_dev_features(struct ipoib_dev_priv *priv, struct ib_device *hca) { priv->hca_caps = hca->attrs.device_cap_flags; @@ -1996,8 +2007,6 @@ int ipoib_set_dev_features(struct ipoib_dev_priv *priv, struct ib_device *hca) priv->dev->features |= priv->dev->hw_features; } - - return 0; } static struct net_device *ipoib_add_port(const char *format, @@ -2011,7 +2020,7 @@ static struct net_device *ipoib_add_port(const char *format, if (!priv) goto alloc_mem_failed; - SET_NETDEV_DEV(priv->dev, hca->dma_device); + SET_NETDEV_DEV(priv->dev, hca->dev.parent); priv->dev->dev_id = port - 1; result = ib_query_port(hca, port, &attr); @@ -2037,9 +2046,7 @@ static struct net_device *ipoib_add_port(const char *format, goto device_init_failed; } - result = ipoib_set_dev_features(priv, hca); - if (result) - goto device_init_failed; + ipoib_set_dev_features(priv, hca); /* * Set the full membership bit, so that we join the right diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index fddff403d5d2..69e146cdc306 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -314,9 +314,11 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, netif_tx_unlock_bh(dev); skb->dev = dev; - if (dev_queue_xmit(skb)) - ipoib_warn(priv, "dev_queue_xmit failed to requeue packet\n"); + ret = dev_queue_xmit(skb); + if (ret) + ipoib_warn(priv, "%s:dev_queue_xmit failed to re-queue packet, ret:%d\n", + __func__, ret); netif_tx_lock_bh(dev); } netif_tx_unlock_bh(dev); @@ -674,7 +676,7 @@ out: spin_unlock_irq(&priv->lock); } -int ipoib_mcast_start_thread(struct net_device *dev) +void ipoib_mcast_start_thread(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); unsigned long flags; @@ -684,8 +686,6 @@ int ipoib_mcast_start_thread(struct net_device *dev) spin_lock_irqsave(&priv->lock, flags); __ipoib_mcast_schedule_join_thread(priv, NULL, 0); spin_unlock_irqrestore(&priv->lock, flags); - - return 0; } int ipoib_mcast_stop_thread(struct net_device *dev) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index fd811115af49..3e10e3dac2e7 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -31,6 +31,7 @@ */ #include <linux/module.h> +#include <linux/sched/signal.h> #include <linux/init.h> #include <linux/seq_file.h> @@ -61,9 +62,7 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv, priv->parent = ppriv->dev; set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags); - result = ipoib_set_dev_features(priv, ppriv->ca); - if (result) - goto err; + ipoib_set_dev_features(priv, ppriv->ca); priv->pkey = pkey; @@ -168,11 +167,11 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) out: up_write(&ppriv->vlan_rwsem); + rtnl_unlock(); + if (result) free_netdev(priv->dev); - rtnl_unlock(); - return result; } @@ -196,7 +195,6 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey) list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) { if (priv->pkey == pkey && priv->child_type == IPOIB_LEGACY_CHILD) { - unregister_netdevice(priv->dev); list_del(&priv->list); dev = priv->dev; break; @@ -204,6 +202,11 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey) } up_write(&ppriv->vlan_rwsem); + if (dev) { + ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name); + unregister_netdevice(dev); + } + rtnl_unlock(); if (dev) { diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 30a6985909e0..5a887efb4bdf 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -652,7 +652,7 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, } if (iscsi_host_add(shost, - ib_conn->device->ib_device->dma_device)) { + ib_conn->device->ib_device->dev.parent)) { mutex_unlock(&iser_conn->state_mutex); goto free_host; } diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 6a9d1cb548ee..30b622f2ab73 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -597,7 +597,9 @@ static void iser_free_ib_conn_res(struct iser_conn *iser_conn, iser_conn, ib_conn->cma_id, ib_conn->qp); if (ib_conn->qp != NULL) { + mutex_lock(&ig.connlist_mutex); ib_conn->comp->active_qps--; + mutex_unlock(&ig.connlist_mutex); rdma_destroy_qp(ib_conn->cma_id); ib_conn->qp = NULL; } diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 314e95516068..91cbe86b25c8 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -728,7 +728,7 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id, iscsit_cause_connection_reinstatement(isert_conn->conn, 0); break; default: - isert_warn("conn %p teminating in state %d\n", + isert_warn("conn %p terminating in state %d\n", isert_conn, isert_conn->state); } mutex_unlock(&isert_conn->mutex); diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 36529e390e48..cee46266f434 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -40,6 +40,7 @@ #include <linux/parser.h> #include <linux/random.h> #include <linux/jiffies.h> +#include <linux/lockdep.h> #include <rdma/ib_cache.h> #include <linux/atomic.h> @@ -371,7 +372,6 @@ static struct srp_fr_pool *srp_create_fr_pool(struct ib_device *device, struct srp_fr_desc *d; struct ib_mr *mr; int i, ret = -EINVAL; - enum ib_mr_type mr_type; if (pool_size <= 0) goto err; @@ -385,13 +385,9 @@ static struct srp_fr_pool *srp_create_fr_pool(struct ib_device *device, spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->free_list); - if (device->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG) - mr_type = IB_MR_TYPE_SG_GAPS; - else - mr_type = IB_MR_TYPE_MEM_REG; - for (i = 0, d = &pool->desc[0]; i < pool->size; i++, d++) { - mr = ib_alloc_mr(pd, mr_type, max_page_list_len); + mr = ib_alloc_mr(pd, IB_MR_TYPE_MEM_REG, + max_page_list_len); if (IS_ERR(mr)) { ret = PTR_ERR(mr); if (ret == -ENOMEM) @@ -470,9 +466,13 @@ static struct srp_fr_pool *srp_alloc_fr_pool(struct srp_target_port *target) * completion handler can access the queue pair while it is * being destroyed. */ -static void srp_destroy_qp(struct ib_qp *qp) +static void srp_destroy_qp(struct srp_rdma_ch *ch, struct ib_qp *qp) { - ib_drain_rq(qp); + spin_lock_irq(&ch->lock); + ib_process_cq_direct(ch->send_cq, -1); + spin_unlock_irq(&ch->lock); + + ib_drain_qp(qp); ib_destroy_qp(qp); } @@ -546,7 +546,7 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch) } if (ch->qp) - srp_destroy_qp(ch->qp); + srp_destroy_qp(ch, ch->qp); if (ch->recv_cq) ib_free_cq(ch->recv_cq); if (ch->send_cq) @@ -570,7 +570,7 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch) return 0; err_qp: - srp_destroy_qp(qp); + srp_destroy_qp(ch, qp); err_send_cq: ib_free_cq(send_cq); @@ -613,7 +613,7 @@ static void srp_free_ch_ib(struct srp_target_port *target, ib_destroy_fmr_pool(ch->fmr_pool); } - srp_destroy_qp(ch->qp); + srp_destroy_qp(ch, ch->qp); ib_free_cq(ch->send_cq); ib_free_cq(ch->recv_cq); @@ -1804,6 +1804,8 @@ static struct srp_iu *__srp_get_tx_iu(struct srp_rdma_ch *ch, s32 rsv = (iu_type == SRP_IU_TSK_MGMT) ? 0 : SRP_TSK_MGMT_SQ_SIZE; struct srp_iu *iu; + lockdep_assert_held(&ch->lock); + ib_process_cq_direct(ch->send_cq, -1); if (list_empty(&ch->free_tx)) @@ -1824,6 +1826,11 @@ static struct srp_iu *__srp_get_tx_iu(struct srp_rdma_ch *ch, return iu; } +/* + * Note: if this function is called from inside ib_drain_sq() then it will + * be called without ch->lock being held. If ib_drain_sq() dequeues a WQE + * with status IB_WC_SUCCESS then that's a bug. + */ static void srp_send_done(struct ib_cq *cq, struct ib_wc *wc) { struct srp_iu *iu = container_of(wc->wr_cqe, struct srp_iu, cqe); @@ -1834,6 +1841,8 @@ static void srp_send_done(struct ib_cq *cq, struct ib_wc *wc) return; } + lockdep_assert_held(&ch->lock); + list_add(&iu->list, &ch->free_tx); } @@ -1889,17 +1898,24 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) { spin_lock_irqsave(&ch->lock, flags); ch->req_lim += be32_to_cpu(rsp->req_lim_delta); + if (rsp->tag == ch->tsk_mgmt_tag) { + ch->tsk_mgmt_status = -1; + if (be32_to_cpu(rsp->resp_data_len) >= 4) + ch->tsk_mgmt_status = rsp->data[3]; + complete(&ch->tsk_mgmt_done); + } else { + shost_printk(KERN_ERR, target->scsi_host, + "Received tsk mgmt response too late for tag %#llx\n", + rsp->tag); + } spin_unlock_irqrestore(&ch->lock, flags); - - ch->tsk_mgmt_status = -1; - if (be32_to_cpu(rsp->resp_data_len) >= 4) - ch->tsk_mgmt_status = rsp->data[3]; - complete(&ch->tsk_mgmt_done); } else { scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag); - if (scmnd) { + if (scmnd && scmnd->host_scribble) { req = (void *)scmnd->host_scribble; scmnd = srp_claim_req(ch, req, NULL, scmnd); + } else { + scmnd = NULL; } if (!scmnd) { shost_printk(KERN_ERR, target->scsi_host, @@ -2531,19 +2547,18 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth) } static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, - u8 func) + u8 func, u8 *status) { struct srp_target_port *target = ch->target; struct srp_rport *rport = target->rport; struct ib_device *dev = target->srp_host->srp_dev->dev; struct srp_iu *iu; struct srp_tsk_mgmt *tsk_mgmt; + int res; if (!ch->connected || target->qp_in_error) return -1; - init_completion(&ch->tsk_mgmt_done); - /* * Lock the rport mutex to avoid that srp_create_ch_ib() is * invoked while a task management function is being sent. @@ -2566,10 +2581,16 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, tsk_mgmt->opcode = SRP_TSK_MGMT; int_to_scsilun(lun, &tsk_mgmt->lun); - tsk_mgmt->tag = req_tag | SRP_TAG_TSK_MGMT; tsk_mgmt->tsk_mgmt_func = func; tsk_mgmt->task_tag = req_tag; + spin_lock_irq(&ch->lock); + ch->tsk_mgmt_tag = (ch->tsk_mgmt_tag + 1) | SRP_TAG_TSK_MGMT; + tsk_mgmt->tag = ch->tsk_mgmt_tag; + spin_unlock_irq(&ch->lock); + + init_completion(&ch->tsk_mgmt_done); + ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt, DMA_TO_DEVICE); if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) { @@ -2578,13 +2599,15 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, return -1; } + res = wait_for_completion_timeout(&ch->tsk_mgmt_done, + msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)); + if (res > 0 && status) + *status = ch->tsk_mgmt_status; mutex_unlock(&rport->mutex); - if (!wait_for_completion_timeout(&ch->tsk_mgmt_done, - msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS))) - return -1; + WARN_ON_ONCE(res < 0); - return 0; + return res > 0 ? 0 : -1; } static int srp_abort(struct scsi_cmnd *scmnd) @@ -2610,7 +2633,7 @@ static int srp_abort(struct scsi_cmnd *scmnd) shost_printk(KERN_ERR, target->scsi_host, "Sending SRP abort for tag %#x\n", tag); if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun, - SRP_TSK_ABORT_TASK) == 0) + SRP_TSK_ABORT_TASK, NULL) == 0) ret = SUCCESS; else if (target->rport->state == SRP_RPORT_LOST) ret = FAST_IO_FAIL; @@ -2628,14 +2651,15 @@ static int srp_reset_device(struct scsi_cmnd *scmnd) struct srp_target_port *target = host_to_target(scmnd->device->host); struct srp_rdma_ch *ch; int i; + u8 status; shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n"); ch = &target->ch[0]; if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun, - SRP_TSK_LUN_RESET)) + SRP_TSK_LUN_RESET, &status)) return FAILED; - if (ch->tsk_mgmt_status) + if (status) return FAILED; for (i = 0; i < target->ch_count; i++) { @@ -2664,9 +2688,8 @@ static int srp_slave_alloc(struct scsi_device *sdev) struct Scsi_Host *shost = sdev->host; struct srp_target_port *target = host_to_target(shost); struct srp_device *srp_dev = target->srp_host->srp_dev; - struct ib_device *ibdev = srp_dev->dev; - if (!(ibdev->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG)) + if (true) blk_queue_virt_boundary(sdev->request_queue, ~srp_dev->mr_page_mask); @@ -2910,7 +2933,7 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target) sprintf(target->target_name, "SRP.T10:%016llX", be64_to_cpu(target->id_ext)); - if (scsi_add_host(target->scsi_host, host->srp_dev->dev->dma_device)) + if (scsi_add_host(target->scsi_host, host->srp_dev->dev->dev.parent)) return -ENODEV; memcpy(ids.port_id, &target->id_ext, 8); @@ -3422,11 +3445,12 @@ static ssize_t srp_create_target(struct device *dev, ret = srp_connect_ch(ch, multich); if (ret) { shost_printk(KERN_ERR, target->scsi_host, - PFX "Connection %d/%d failed\n", + PFX "Connection %d/%d to %pI6 failed\n", ch_start + cpu_idx, - target->ch_count); + target->ch_count, + ch->target->orig_dgid.raw); if (node_idx == 0 && cpu_idx == 0) { - goto err_disconnect; + goto free_ch; } else { srp_free_ch_ib(target, ch); srp_free_req_data(target, ch); @@ -3473,6 +3497,7 @@ put: err_disconnect: srp_disconnect_target(target); +free_ch: for (i = 0; i < target->ch_count; i++) { ch = &target->ch[i]; srp_free_ch_ib(target, ch); @@ -3521,7 +3546,7 @@ static struct srp_host *srp_add_port(struct srp_device *device, u8 port) host->port = port; host->dev.class = &srp_class; - host->dev.parent = device->dev->dma_device; + host->dev.parent = device->dev->dev.parent; dev_set_name(&host->dev, "srp-%s-%d", device->dev->name, port); if (device_register(&host->dev)) diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index 21c69695f9d4..32ed40db3ca2 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h @@ -163,6 +163,7 @@ struct srp_rdma_ch { int max_ti_iu_len; int comp_vector; + u64 tsk_mgmt_tag; struct completion tsk_mgmt_done; u8 tsk_mgmt_status; bool connected; diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index d21ba9d857c3..7e314c2f2071 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -500,6 +500,7 @@ static int srpt_refresh_port(struct srpt_port *sport) struct ib_mad_reg_req reg_req; struct ib_port_modify port_modify; struct ib_port_attr port_attr; + __be16 *guid; int ret; memset(&port_modify, 0, sizeof(port_modify)); @@ -522,10 +523,17 @@ static int srpt_refresh_port(struct srpt_port *sport) if (ret) goto err_query_port; + sport->port_guid_wwn.priv = sport; + guid = (__be16 *)&sport->gid.global.interface_id; snprintf(sport->port_guid, sizeof(sport->port_guid), - "0x%016llx%016llx", - be64_to_cpu(sport->gid.global.subnet_prefix), - be64_to_cpu(sport->gid.global.interface_id)); + "%04x:%04x:%04x:%04x", + be16_to_cpu(guid[0]), be16_to_cpu(guid[1]), + be16_to_cpu(guid[2]), be16_to_cpu(guid[3])); + sport->port_gid_wwn.priv = sport; + snprintf(sport->port_gid, sizeof(sport->port_gid), + "0x%016llx%016llx", + be64_to_cpu(sport->gid.global.subnet_prefix), + be64_to_cpu(sport->gid.global.interface_id)); if (!sport->mad_agent) { memset(®_req, 0, sizeof(reg_req)); @@ -1838,6 +1846,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, struct srp_login_rej *rej; struct ib_cm_rep_param *rep_param; struct srpt_rdma_ch *ch, *tmp_ch; + __be16 *guid; u32 it_iu_len; int i, ret = 0; @@ -1983,26 +1992,30 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, goto destroy_ib; } - /* - * Use the initator port identifier as the session name, when - * checking against se_node_acl->initiatorname[] this can be - * with or without preceeding '0x'. - */ + guid = (__be16 *)¶m->primary_path->sgid.global.interface_id; + snprintf(ch->ini_guid, sizeof(ch->ini_guid), "%04x:%04x:%04x:%04x", + be16_to_cpu(guid[0]), be16_to_cpu(guid[1]), + be16_to_cpu(guid[2]), be16_to_cpu(guid[3])); snprintf(ch->sess_name, sizeof(ch->sess_name), "0x%016llx%016llx", be64_to_cpu(*(__be64 *)ch->i_port_id), be64_to_cpu(*(__be64 *)(ch->i_port_id + 8))); pr_debug("registering session %s\n", ch->sess_name); - ch->sess = target_alloc_session(&sport->port_tpg_1, 0, 0, + if (sport->port_guid_tpg.se_tpg_wwn) + ch->sess = target_alloc_session(&sport->port_guid_tpg, 0, 0, + TARGET_PROT_NORMAL, + ch->ini_guid, ch, NULL); + if (sport->port_gid_tpg.se_tpg_wwn && IS_ERR_OR_NULL(ch->sess)) + ch->sess = target_alloc_session(&sport->port_gid_tpg, 0, 0, TARGET_PROT_NORMAL, ch->sess_name, ch, NULL); /* Retry without leading "0x" */ - if (IS_ERR(ch->sess)) - ch->sess = target_alloc_session(&sport->port_tpg_1, 0, 0, + if (sport->port_gid_tpg.se_tpg_wwn && IS_ERR_OR_NULL(ch->sess)) + ch->sess = target_alloc_session(&sport->port_gid_tpg, 0, 0, TARGET_PROT_NORMAL, ch->sess_name + 2, ch, NULL); - if (IS_ERR(ch->sess)) { + if (IS_ERR_OR_NULL(ch->sess)) { pr_info("Rejected login because no ACL has been configured yet for initiator %s.\n", ch->sess_name); rej->reason = cpu_to_be32((PTR_ERR(ch->sess) == -ENOMEM) ? @@ -2420,7 +2433,7 @@ static int srpt_release_sdev(struct srpt_device *sdev) return 0; } -static struct srpt_port *__srpt_lookup_port(const char *name) +static struct se_wwn *__srpt_lookup_wwn(const char *name) { struct ib_device *dev; struct srpt_device *sdev; @@ -2435,23 +2448,25 @@ static struct srpt_port *__srpt_lookup_port(const char *name) for (i = 0; i < dev->phys_port_cnt; i++) { sport = &sdev->port[i]; - if (!strcmp(sport->port_guid, name)) - return sport; + if (strcmp(sport->port_guid, name) == 0) + return &sport->port_guid_wwn; + if (strcmp(sport->port_gid, name) == 0) + return &sport->port_gid_wwn; } } return NULL; } -static struct srpt_port *srpt_lookup_port(const char *name) +static struct se_wwn *srpt_lookup_wwn(const char *name) { - struct srpt_port *sport; + struct se_wwn *wwn; spin_lock(&srpt_dev_lock); - sport = __srpt_lookup_port(name); + wwn = __srpt_lookup_wwn(name); spin_unlock(&srpt_dev_lock); - return sport; + return wwn; } /** @@ -2464,8 +2479,7 @@ static void srpt_add_one(struct ib_device *device) struct ib_srq_init_attr srq_attr; int i; - pr_debug("device = %p, device->dma_ops = %p\n", device, - device->dma_ops); + pr_debug("device = %p\n", device); sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); if (!sdev) @@ -2643,11 +2657,19 @@ static char *srpt_get_fabric_name(void) return "srpt"; } +static struct srpt_port *srpt_tpg_to_sport(struct se_portal_group *tpg) +{ + return tpg->se_tpg_wwn->priv; +} + static char *srpt_get_fabric_wwn(struct se_portal_group *tpg) { - struct srpt_port *sport = container_of(tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(tpg); - return sport->port_guid; + WARN_ON_ONCE(tpg != &sport->port_guid_tpg && + tpg != &sport->port_gid_tpg); + return tpg == &sport->port_guid_tpg ? sport->port_guid : + sport->port_gid; } static u16 srpt_get_tag(struct se_portal_group *tpg) @@ -2737,6 +2759,19 @@ static int srpt_get_tcm_cmd_state(struct se_cmd *se_cmd) return srpt_get_cmd_state(ioctx); } +static int srpt_parse_guid(u64 *guid, const char *name) +{ + u16 w[4]; + int ret = -EINVAL; + + if (sscanf(name, "%hx:%hx:%hx:%hx", &w[0], &w[1], &w[2], &w[3]) != 4) + goto out; + *guid = get_unaligned_be64(w); + ret = 0; +out: + return ret; +} + /** * srpt_parse_i_port_id() - Parse an initiator port ID. * @name: ASCII representation of a 128-bit initiator port ID. @@ -2772,20 +2807,23 @@ out: */ static int srpt_init_nodeacl(struct se_node_acl *se_nacl, const char *name) { + u64 guid; u8 i_port_id[16]; + int ret; - if (srpt_parse_i_port_id(i_port_id, name) < 0) { + ret = srpt_parse_guid(&guid, name); + if (ret < 0) + ret = srpt_parse_i_port_id(i_port_id, name); + if (ret < 0) pr_err("invalid initiator port ID %s\n", name); - return -EINVAL; - } - return 0; + return ret; } static ssize_t srpt_tpg_attrib_srp_max_rdma_size_show(struct config_item *item, char *page) { struct se_portal_group *se_tpg = attrib_to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); return sprintf(page, "%u\n", sport->port_attrib.srp_max_rdma_size); } @@ -2794,7 +2832,7 @@ static ssize_t srpt_tpg_attrib_srp_max_rdma_size_store(struct config_item *item, const char *page, size_t count) { struct se_portal_group *se_tpg = attrib_to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); unsigned long val; int ret; @@ -2822,7 +2860,7 @@ static ssize_t srpt_tpg_attrib_srp_max_rsp_size_show(struct config_item *item, char *page) { struct se_portal_group *se_tpg = attrib_to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); return sprintf(page, "%u\n", sport->port_attrib.srp_max_rsp_size); } @@ -2831,7 +2869,7 @@ static ssize_t srpt_tpg_attrib_srp_max_rsp_size_store(struct config_item *item, const char *page, size_t count) { struct se_portal_group *se_tpg = attrib_to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); unsigned long val; int ret; @@ -2859,7 +2897,7 @@ static ssize_t srpt_tpg_attrib_srp_sq_size_show(struct config_item *item, char *page) { struct se_portal_group *se_tpg = attrib_to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); return sprintf(page, "%u\n", sport->port_attrib.srp_sq_size); } @@ -2868,7 +2906,7 @@ static ssize_t srpt_tpg_attrib_srp_sq_size_store(struct config_item *item, const char *page, size_t count) { struct se_portal_group *se_tpg = attrib_to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); unsigned long val; int ret; @@ -2906,7 +2944,7 @@ static struct configfs_attribute *srpt_tpg_attrib_attrs[] = { static ssize_t srpt_tpg_enable_show(struct config_item *item, char *page) { struct se_portal_group *se_tpg = to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); return snprintf(page, PAGE_SIZE, "%d\n", (sport->enabled) ? 1: 0); } @@ -2915,7 +2953,7 @@ static ssize_t srpt_tpg_enable_store(struct config_item *item, const char *page, size_t count) { struct se_portal_group *se_tpg = to_tpg(item); - struct srpt_port *sport = container_of(se_tpg, struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(se_tpg); struct srpt_device *sdev = sport->sdev; struct srpt_rdma_ch *ch; unsigned long tmp; @@ -2967,15 +3005,19 @@ static struct se_portal_group *srpt_make_tpg(struct se_wwn *wwn, struct config_group *group, const char *name) { - struct srpt_port *sport = container_of(wwn, struct srpt_port, port_wwn); + struct srpt_port *sport = wwn->priv; + static struct se_portal_group *tpg; int res; - /* Initialize sport->port_wwn and sport->port_tpg_1 */ - res = core_tpg_register(&sport->port_wwn, &sport->port_tpg_1, SCSI_PROTOCOL_SRP); + WARN_ON_ONCE(wwn != &sport->port_guid_wwn && + wwn != &sport->port_gid_wwn); + tpg = wwn == &sport->port_guid_wwn ? &sport->port_guid_tpg : + &sport->port_gid_tpg; + res = core_tpg_register(wwn, tpg, SCSI_PROTOCOL_SRP); if (res) return ERR_PTR(res); - return &sport->port_tpg_1; + return tpg; } /** @@ -2984,11 +3026,10 @@ static struct se_portal_group *srpt_make_tpg(struct se_wwn *wwn, */ static void srpt_drop_tpg(struct se_portal_group *tpg) { - struct srpt_port *sport = container_of(tpg, - struct srpt_port, port_tpg_1); + struct srpt_port *sport = srpt_tpg_to_sport(tpg); sport->enabled = false; - core_tpg_deregister(&sport->port_tpg_1); + core_tpg_deregister(tpg); } /** @@ -2999,19 +3040,7 @@ static struct se_wwn *srpt_make_tport(struct target_fabric_configfs *tf, struct config_group *group, const char *name) { - struct srpt_port *sport; - int ret; - - sport = srpt_lookup_port(name); - pr_debug("make_tport(%s)\n", name); - ret = -EINVAL; - if (!sport) - goto err; - - return &sport->port_wwn; - -err: - return ERR_PTR(ret); + return srpt_lookup_wwn(name) ? : ERR_PTR(-EINVAL); } /** @@ -3020,9 +3049,6 @@ err: */ static void srpt_drop_tport(struct se_wwn *wwn) { - struct srpt_port *sport = container_of(wwn, struct srpt_port, port_wwn); - - pr_debug("drop_tport(%s\n", config_item_name(&sport->port_wwn.wwn_group.cg_item)); } static ssize_t srpt_wwn_version_show(struct config_item *item, char *buf) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h index 581878782854..cc1183851af5 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.h +++ b/drivers/infiniband/ulp/srpt/ib_srpt.h @@ -258,6 +258,7 @@ enum rdma_ch_state { * against concurrent modification by the cm_id spinlock. * @sess: Session information associated with this SRP channel. * @sess_name: Session name. + * @ini_guid: Initiator port GUID. * @release_work: Allows scheduling of srpt_release_channel(). * @release_done: Enables waiting for srpt_release_channel() completion. */ @@ -284,6 +285,7 @@ struct srpt_rdma_ch { struct list_head cmd_wait_list; struct se_session *sess; u8 sess_name[36]; + u8 ini_guid[24]; struct work_struct release_work; struct completion *release_done; }; @@ -306,28 +308,34 @@ struct srpt_port_attrib { * @mad_agent: per-port management datagram processing information. * @enabled: Whether or not this target port is enabled. * @port_guid: ASCII representation of Port GUID + * @port_gid: ASCII representation of Port GID * @port: one-based port number. * @sm_lid: cached value of the port's sm_lid. * @lid: cached value of the port's lid. * @gid: cached value of the port's gid. * @port_acl_lock spinlock for port_acl_list: * @work: work structure for refreshing the aforementioned cached values. - * @port_tpg_1 Target portal group = 1 data. - * @port_wwn: Target core WWN data. + * @port_guid_tpg: TPG associated with target port GUID. + * @port_guid_wwn: WWN associated with target port GUID. + * @port_gid_tpg: TPG associated with target port GID. + * @port_gid_wwn: WWN associated with target port GID. * @port_acl_list: Head of the list with all node ACLs for this port. */ struct srpt_port { struct srpt_device *sdev; struct ib_mad_agent *mad_agent; bool enabled; - u8 port_guid[64]; + u8 port_guid[24]; + u8 port_gid[64]; u8 port; u16 sm_lid; u16 lid; union ib_gid gid; struct work_struct work; - struct se_portal_group port_tpg_1; - struct se_wwn port_wwn; + struct se_portal_group port_guid_tpg; + struct se_wwn port_guid_wwn; + struct se_portal_group port_gid_tpg; + struct se_wwn port_gid_wwn; struct srpt_port_attrib port_attrib; }; diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 6261874c07c9..ff8037798779 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -94,7 +94,6 @@ comment "Userland interfaces" config INPUT_MOUSEDEV tristate "Mouse interface" - default y help Say Y here if you want your mouse to be accessible as char devices 13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an @@ -109,7 +108,6 @@ config INPUT_MOUSEDEV config INPUT_MOUSEDEV_PSAUX bool "Provide legacy /dev/psaux device" - default y depends on INPUT_MOUSEDEV help Say Y here if you want your mouse also be accessible as char device @@ -118,7 +116,6 @@ config INPUT_MOUSEDEV_PSAUX If unsure, say Y. - config INPUT_MOUSEDEV_SCREEN_X int "Horizontal screen resolution" depends on INPUT_MOUSEDEV diff --git a/drivers/input/input.c b/drivers/input/input.c index d95c34ee5dc1..067d648028a2 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1749,7 +1749,7 @@ static const struct dev_pm_ops input_dev_pm_ops = { }; #endif /* CONFIG_PM */ -static struct device_type input_dev_type = { +static const struct device_type input_dev_type = { .groups = input_dev_attr_groups, .release = input_dev_release, .uevent = input_dev_uevent, @@ -2091,6 +2091,12 @@ int input_register_device(struct input_dev *dev) const char *path; int error; + if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) { + dev_err(&dev->dev, + "Absolute device without dev->absinfo, refusing to register\n"); + return -EINVAL; + } + if (dev->devres_managed) { devres = devres_alloc(devm_input_device_unregister, sizeof(struct input_devres), GFP_KERNEL); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index abd18f31b24f..065e67bf56dd 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -87,7 +87,7 @@ static int joydev_correct(int value, struct js_corr *corr) return 0; } - return value < -32767 ? -32767 : (value > 32767 ? 32767 : value); + return clamp(value, -32767, 32767); } static void joydev_pass_event(struct joydev_client *client, @@ -187,6 +187,17 @@ static void joydev_detach_client(struct joydev *joydev, synchronize_rcu(); } +static void joydev_refresh_state(struct joydev *joydev) +{ + struct input_dev *dev = joydev->handle.dev; + int i, val; + + for (i = 0; i < joydev->nabs; i++) { + val = input_abs_get_val(dev, joydev->abspam[i]); + joydev->abs[i] = joydev_correct(val, &joydev->corr[i]); + } +} + static int joydev_open_device(struct joydev *joydev) { int retval; @@ -201,6 +212,8 @@ static int joydev_open_device(struct joydev *joydev) retval = input_open_device(&joydev->handle); if (retval) joydev->open--; + else + joydev_refresh_state(joydev); } mutex_unlock(&joydev->mutex); @@ -872,7 +885,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, j = joydev->abspam[i]; if (input_abs_get_max(dev, j) == input_abs_get_min(dev, j)) { joydev->corr[i].type = JS_CORR_NONE; - joydev->abs[i] = input_abs_get_val(dev, j); continue; } joydev->corr[i].type = JS_CORR_BROKEN; @@ -887,10 +899,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, if (t) { joydev->corr[i].coef[2] = (1 << 29) / t; joydev->corr[i].coef[3] = (1 << 29) / t; - - joydev->abs[i] = - joydev_correct(input_abs_get_val(dev, j), - joydev->corr + i); } } diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c index 8aa6e4c497da..ff54e195d42c 100644 --- a/drivers/input/joystick/maplecontrol.c +++ b/drivers/input/joystick/maplecontrol.c @@ -139,7 +139,6 @@ static int probe_maple_controller(struct device *dev) idev->dev.parent = &mdev->dev; idev->name = mdev->product_name; idev->id.bustype = BUS_HOST; - input_set_drvdata(idev, pad); error = input_register_device(idev); if (error) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index c7d5b2b643d1..155fcb3b6230 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -320,18 +320,18 @@ static struct usb_device_id xpad_table[] = { XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */ + XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ + XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ - XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */ - XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ - XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */ - XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ - XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ - XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA Controllers */ XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */ XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ + XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */ + XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ + XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA Controllers */ { } }; @@ -389,6 +389,7 @@ struct usb_xpad { static int xpad_init_input(struct usb_xpad *xpad); static void xpad_deinit_input(struct usb_xpad *xpad); +static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num); /* * xpad_process_packet @@ -608,14 +609,36 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha } /* - * xpadone_process_buttons + * xpadone_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. This version is for the Xbox One controller. * - * Process a button update packet from an Xbox one controller. + * The report format was gleaned from + * https://github.com/kylelemons/xbox/blob/master/xbox.go */ -static void xpadone_process_buttons(struct usb_xpad *xpad, - struct input_dev *dev, - unsigned char *data) +static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) { + struct input_dev *dev = xpad->dev; + + /* the xbox button has its own special report */ + if (data[0] == 0X07) { + /* + * The Xbox One S controller requires these reports to be + * acked otherwise it continues sending them forever and + * won't report further mode button events. + */ + if (data[1] == 0x30) + xpadone_ack_mode_report(xpad, data[2]); + + input_report_key(dev, BTN_MODE, data[4] & 0x01); + input_sync(dev); + return; + } + /* check invalid packet */ + else if (data[0] != 0X20) + return; + /* menu/view buttons */ input_report_key(dev, BTN_START, data[4] & 0x04); input_report_key(dev, BTN_SELECT, data[4] & 0x08); @@ -678,34 +701,6 @@ static void xpadone_process_buttons(struct usb_xpad *xpad, input_sync(dev); } -/* - * xpadone_process_packet - * - * Completes a request by converting the data into events for the - * input subsystem. This version is for the Xbox One controller. - * - * The report format was gleaned from - * https://github.com/kylelemons/xbox/blob/master/xbox.go - */ - -static void xpadone_process_packet(struct usb_xpad *xpad, - u16 cmd, unsigned char *data) -{ - struct input_dev *dev = xpad->dev; - - switch (data[0]) { - case 0x20: - xpadone_process_buttons(xpad, dev, data); - break; - - case 0x07: - /* the xbox button has its own special report */ - input_report_key(dev, BTN_MODE, data[4] & 0x01); - input_sync(dev); - break; - } -} - static void xpad_irq_in(struct urb *urb) { struct usb_xpad *xpad = urb->context; @@ -850,10 +845,9 @@ static void xpad_irq_out(struct urb *urb) spin_unlock_irqrestore(&xpad->odata_lock, flags); } -static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) +static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad, + struct usb_endpoint_descriptor *ep_irq_out) { - struct usb_endpoint_descriptor *ep_irq_out; - int ep_irq_out_idx; int error; if (xpad->xtype == XTYPE_UNKNOWN) @@ -863,23 +857,17 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, GFP_KERNEL, &xpad->odata_dma); - if (!xpad->odata) { - error = -ENOMEM; - goto fail1; - } + if (!xpad->odata) + return -ENOMEM; spin_lock_init(&xpad->odata_lock); xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->irq_out) { error = -ENOMEM; - goto fail2; + goto err_free_coherent; } - /* Xbox One controller has in/out endpoints swapped. */ - ep_irq_out_idx = xpad->xtype == XTYPE_XBOXONE ? 0 : 1; - ep_irq_out = &intf->cur_altsetting->endpoint[ep_irq_out_idx].desc; - usb_fill_int_urb(xpad->irq_out, xpad->udev, usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), xpad->odata, XPAD_PKT_LEN, @@ -889,8 +877,9 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) return 0; - fail2: usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); - fail1: return error; +err_free_coherent: + usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); + return error; } static void xpad_stop_output(struct usb_xpad *xpad) @@ -974,6 +963,30 @@ static int xpad_start_xbox_one(struct usb_xpad *xpad) return retval; } +static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num) +{ + unsigned long flags; + struct xpad_output_packet *packet = + &xpad->out_packets[XPAD_OUT_CMD_IDX]; + static const u8 mode_report_ack[] = { + 0x01, 0x20, 0x00, 0x09, 0x00, 0x07, 0x20, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + spin_lock_irqsave(&xpad->odata_lock, flags); + + packet->len = sizeof(mode_report_ack); + memcpy(packet->data, mode_report_ack, packet->len); + packet->data[2] = seq_num; + packet->pending = true; + + /* Reset the sequence so we send out the ack now */ + xpad->last_out_packet = -1; + xpad_try_sending_next_out_packet(xpad); + + spin_unlock_irqrestore(&xpad->odata_lock, flags); +} + #ifdef CONFIG_JOYSTICK_XPAD_FF static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { @@ -1198,6 +1211,7 @@ static int xpad_led_probe(struct usb_xpad *xpad) led_cdev = &led->led_cdev; led_cdev->name = led->name; led_cdev->brightness_set = xpad_led_set; + led_cdev->flags = LED_CORE_SUSPENDRESUME; error = led_classdev_register(&xpad->udev->dev, led_cdev); if (error) @@ -1468,8 +1482,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id { struct usb_device *udev = interface_to_usbdev(intf); struct usb_xpad *xpad; - struct usb_endpoint_descriptor *ep_irq_in; - int ep_irq_in_idx; + struct usb_endpoint_descriptor *ep_irq_in, *ep_irq_out; int i, error; if (intf->cur_altsetting->desc.bNumEndpoints != 2) @@ -1539,13 +1552,26 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id goto err_free_in_urb; } - error = xpad_init_output(intf, xpad); - if (error) + ep_irq_in = ep_irq_out = NULL; + + for (i = 0; i < 2; i++) { + struct usb_endpoint_descriptor *ep = + &intf->cur_altsetting->endpoint[i].desc; + + if (usb_endpoint_dir_in(ep)) + ep_irq_in = ep; + else + ep_irq_out = ep; + } + + if (!ep_irq_in || !ep_irq_out) { + error = -ENODEV; goto err_free_in_urb; + } - /* Xbox One controller has in/out endpoints swapped. */ - ep_irq_in_idx = xpad->xtype == XTYPE_XBOXONE ? 1 : 0; - ep_irq_in = &intf->cur_altsetting->endpoint[ep_irq_in_idx].desc; + error = xpad_init_output(intf, xpad, ep_irq_out); + if (error) + goto err_free_in_urb; usb_fill_int_urb(xpad->irq_in, udev, usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), @@ -1662,8 +1688,16 @@ static int xpad_resume(struct usb_interface *intf) retval = xpad360w_start_input(xpad); } else { mutex_lock(&input->mutex); - if (input->users) + if (input->users) { retval = xpad_start_input(xpad); + } else if (xpad->xtype == XTYPE_XBOXONE) { + /* + * Even if there are no users, we'll send Xbox One pads + * the startup sequence so they don't sit there and + * blink until somebody opens the input device again. + */ + retval = xpad_start_xbox_one(xpad); + } mutex_unlock(&input->mutex); } diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index cbd75cf44739..97acd6524ad7 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -666,6 +666,17 @@ config KEYBOARD_TC3589X To compile this driver as a module, choose M here: the module will be called tc3589x-keypad. +config KEYBOARD_TM2_TOUCHKEY + tristate "TM2 touchkey support" + depends on I2C + depends on LEDS_CLASS + help + Say Y here to enable device driver for tm2-touchkey with + LED control for the Exynos5433 TM2 board. + + To compile this driver as a module, choose M here. + module will be called tm2-touchkey. + config KEYBOARD_TWL4030 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" depends on TWL4030_CORE diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index d9f4cfcf3410..7d9acff819a7 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o +obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c index f8cf2ccacefd..c255af21e71a 100644 --- a/drivers/input/keyboard/adc-keys.c +++ b/drivers/input/keyboard/adc-keys.c @@ -148,8 +148,6 @@ static int adc_keys_probe(struct platform_device *pdev) if (error) return error; - platform_set_drvdata(pdev, st); - poll_dev = devm_input_allocate_polled_device(dev); if (!poll_dev) { dev_err(dev, "failed to allocate input device\n"); diff --git a/drivers/input/keyboard/adp5520-keys.c b/drivers/input/keyboard/adp5520-keys.c index db1004dad108..f0b9b37bde58 100644 --- a/drivers/input/keyboard/adp5520-keys.c +++ b/drivers/input/keyboard/adp5520-keys.c @@ -107,8 +107,6 @@ static int adp5520_keys_probe(struct platform_device *pdev) input->phys = "adp5520-keys/input0"; input->dev.parent = &pdev->dev; - input_set_drvdata(input, dev); - input->id.bustype = BUS_I2C; input->id.vendor = 0x0001; input->id.product = 0x5520; diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c index 86a8b723ae15..e1cf63ee148f 100644 --- a/drivers/input/keyboard/bcm-keypad.c +++ b/drivers/input/keyboard/bcm-keypad.c @@ -213,7 +213,7 @@ static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp) /* Initialize the KPCR Keypad Configuration Register */ kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE; - error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols); + error = matrix_keypad_parse_properties(dev, &kp->n_rows, &kp->n_cols); if (error) { dev_err(dev, "failed to parse kp params\n"); return error; @@ -352,8 +352,6 @@ static int bcm_kp_probe(struct platform_device *pdev) kp->input_dev = input_dev; - platform_set_drvdata(pdev, kp); - error = bcm_kp_matrix_key_parse_dt(kp); if (error) return error; diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index 81b07dddae86..39bcbc38997f 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -268,8 +268,6 @@ static int bfin_kpad_probe(struct platform_device *pdev) input->phys = "bf54x-keys/input0"; input->dev.parent = &pdev->dev; - input_set_drvdata(input, bf54x_kpad); - input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 4401be225d64..1a1eacae3ea1 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -392,7 +392,6 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client, return error; dev_info(dev, "CAP11XX detected, revision 0x%02x\n", rev); - i2c_set_clientdata(i2c_client, priv); node = dev->of_node; if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) { diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 25943e9bc8bf..6a250d65f8fe 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -34,6 +34,8 @@ #include <linux/mfd/cros_ec.h> #include <linux/mfd/cros_ec_commands.h> +#include <asm/unaligned.h> + /* * @rows: Number of rows in the keypad * @cols: Number of columns in the keypad @@ -43,8 +45,9 @@ * @valid_keys: bitmap of existing keys for each matrix column * @old_kb_state: bitmap of keys pressed last scan * @dev: Device pointer - * @idev: Input device * @ec: Top level ChromeOS device to use to talk to EC + * @idev: The input device for the matrix keys. + * @bs_idev: The input device for non-matrix buttons and switches (or NULL). * @notifier: interrupt event notifier for transport devices */ struct cros_ec_keyb { @@ -57,12 +60,64 @@ struct cros_ec_keyb { uint8_t *old_kb_state; struct device *dev; - struct input_dev *idev; struct cros_ec_device *ec; + + struct input_dev *idev; + struct input_dev *bs_idev; struct notifier_block notifier; }; +/** + * cros_ec_bs_map - Struct mapping Linux keycodes to EC button/switch bitmap + * #defines + * + * @ev_type: The type of the input event to generate (e.g., EV_KEY). + * @code: A linux keycode + * @bit: A #define like EC_MKBP_POWER_BUTTON or EC_MKBP_LID_OPEN + * @inverted: If the #define and EV_SW have opposite meanings, this is true. + * Only applicable to switches. + */ +struct cros_ec_bs_map { + unsigned int ev_type; + unsigned int code; + u8 bit; + bool inverted; +}; + +/* cros_ec_keyb_bs - Map EC button/switch #defines into kernel ones */ +static const struct cros_ec_bs_map cros_ec_keyb_bs[] = { + /* Buttons */ + { + .ev_type = EV_KEY, + .code = KEY_POWER, + .bit = EC_MKBP_POWER_BUTTON, + }, + { + .ev_type = EV_KEY, + .code = KEY_VOLUMEUP, + .bit = EC_MKBP_VOL_UP, + }, + { + .ev_type = EV_KEY, + .code = KEY_VOLUMEDOWN, + .bit = EC_MKBP_VOL_DOWN, + }, + + /* Switches */ + { + .ev_type = EV_SW, + .code = SW_LID, + .bit = EC_MKBP_LID_OPEN, + .inverted = true, + }, + { + .ev_type = EV_SW, + .code = SW_TABLET_MODE, + .bit = EC_MKBP_TABLET_MODE, + }, +}; + /* * Returns true when there is at least one combination of pressed keys that * results in ghosting. @@ -149,20 +204,33 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, input_sync(ckdev->idev); } -static int cros_ec_keyb_open(struct input_dev *dev) +/** + * cros_ec_keyb_report_bs - Report non-matrixed buttons or switches + * + * This takes a bitmap of buttons or switches from the EC and reports events, + * syncing at the end. + * + * @ckdev: The keyboard device. + * @ev_type: The input event type (e.g., EV_KEY). + * @mask: A bitmap of buttons from the EC. + */ +static void cros_ec_keyb_report_bs(struct cros_ec_keyb *ckdev, + unsigned int ev_type, u32 mask) + { - struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + struct input_dev *idev = ckdev->bs_idev; + int i; - return blocking_notifier_chain_register(&ckdev->ec->event_notifier, - &ckdev->notifier); -} + for (i = 0; i < ARRAY_SIZE(cros_ec_keyb_bs); i++) { + const struct cros_ec_bs_map *map = &cros_ec_keyb_bs[i]; -static void cros_ec_keyb_close(struct input_dev *dev) -{ - struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + if (map->ev_type != ev_type) + continue; - blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, - &ckdev->notifier); + input_event(idev, ev_type, map->code, + !!(mask & BIT(map->bit)) ^ map->inverted); + } + input_sync(idev); } static int cros_ec_keyb_work(struct notifier_block *nb, @@ -170,22 +238,54 @@ static int cros_ec_keyb_work(struct notifier_block *nb, { struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, notifier); + u32 val; + unsigned int ev_type; + + switch (ckdev->ec->event_data.event_type) { + case EC_MKBP_EVENT_KEY_MATRIX: + /* + * If EC is not the wake source, discard key state changes + * during suspend. + */ + if (queued_during_suspend) + return NOTIFY_OK; - if (ckdev->ec->event_data.event_type != EC_MKBP_EVENT_KEY_MATRIX) + if (ckdev->ec->event_size != ckdev->cols) { + dev_err(ckdev->dev, + "Discarded incomplete key matrix event.\n"); + return NOTIFY_OK; + } + cros_ec_keyb_process(ckdev, + ckdev->ec->event_data.data.key_matrix, + ckdev->ec->event_size); + break; + + case EC_MKBP_EVENT_BUTTON: + case EC_MKBP_EVENT_SWITCH: + /* + * If EC is not the wake source, discard key state + * changes during suspend. Switches will be re-checked in + * cros_ec_keyb_resume() to be sure nothing is lost. + */ + if (queued_during_suspend) + return NOTIFY_OK; + + if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) { + val = get_unaligned_le32( + &ckdev->ec->event_data.data.buttons); + ev_type = EV_KEY; + } else { + val = get_unaligned_le32( + &ckdev->ec->event_data.data.switches); + ev_type = EV_SW; + } + cros_ec_keyb_report_bs(ckdev, ev_type, val); + break; + + default: return NOTIFY_DONE; - /* - * If EC is not the wake source, discard key state changes during - * suspend. - */ - if (queued_during_suspend) - return NOTIFY_OK; - if (ckdev->ec->event_size != ckdev->cols) { - dev_err(ckdev->dev, - "Discarded incomplete key matrix event.\n"); - return NOTIFY_OK; } - cros_ec_keyb_process(ckdev, ckdev->ec->event_data.data.key_matrix, - ckdev->ec->event_size); + return NOTIFY_OK; } @@ -213,23 +313,229 @@ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) } } -static int cros_ec_keyb_probe(struct platform_device *pdev) +/** + * cros_ec_keyb_info - Wrap the EC command EC_CMD_MKBP_INFO + * + * This wraps the EC_CMD_MKBP_INFO, abstracting out all of the marshalling and + * unmarshalling and different version nonsense into something simple. + * + * @ec_dev: The EC device + * @info_type: Either EC_MKBP_INFO_SUPPORTED or EC_MKBP_INFO_CURRENT. + * @event_type: Either EC_MKBP_EVENT_BUTTON or EC_MKBP_EVENT_SWITCH. Actually + * in some cases this could be EC_MKBP_EVENT_KEY_MATRIX or + * EC_MKBP_EVENT_HOST_EVENT too but we don't use in this driver. + * @result: Where we'll store the result; a union + * @result_size: The size of the result. Expected to be the size of one of + * the elements in the union. + * + * Returns 0 if no error or -error upon error. + */ +static int cros_ec_keyb_info(struct cros_ec_device *ec_dev, + enum ec_mkbp_info_type info_type, + enum ec_mkbp_event event_type, + union ec_response_get_next_data *result, + size_t result_size) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); - struct device *dev = &pdev->dev; - struct cros_ec_keyb *ckdev; + struct ec_params_mkbp_info *params; + struct cros_ec_command *msg; + int ret; + + msg = kzalloc(sizeof(*msg) + max_t(size_t, result_size, + sizeof(*params)), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->command = EC_CMD_MKBP_INFO; + msg->version = 1; + msg->outsize = sizeof(*params); + msg->insize = result_size; + params = (struct ec_params_mkbp_info *)msg->data; + params->info_type = info_type; + params->event_type = event_type; + + ret = cros_ec_cmd_xfer(ec_dev, msg); + if (ret < 0) { + dev_warn(ec_dev->dev, "Transfer error %d/%d: %d\n", + (int)info_type, (int)event_type, ret); + } else if (msg->result == EC_RES_INVALID_VERSION) { + /* With older ECs we just return 0 for everything */ + memset(result, 0, result_size); + ret = 0; + } else if (msg->result != EC_RES_SUCCESS) { + dev_warn(ec_dev->dev, "Error getting info %d/%d: %d\n", + (int)info_type, (int)event_type, msg->result); + ret = -EPROTO; + } else if (ret != result_size) { + dev_warn(ec_dev->dev, "Wrong size %d/%d: %d != %zu\n", + (int)info_type, (int)event_type, + ret, result_size); + ret = -EPROTO; + } else { + memcpy(result, msg->data, result_size); + ret = 0; + } + + kfree(msg); + + return ret; +} + +/** + * cros_ec_keyb_query_switches - Query the state of switches and report + * + * This will ask the EC about the current state of switches and report to the + * kernel. Note that we don't query for buttons because they are more + * transitory and we'll get an update on the next release / press. + * + * @ckdev: The keyboard device + * + * Returns 0 if no error or -error upon error. + */ +static int cros_ec_keyb_query_switches(struct cros_ec_keyb *ckdev) +{ + struct cros_ec_device *ec_dev = ckdev->ec; + union ec_response_get_next_data event_data = {}; + int ret; + + ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_CURRENT, + EC_MKBP_EVENT_SWITCH, &event_data, + sizeof(event_data.switches)); + if (ret) + return ret; + + cros_ec_keyb_report_bs(ckdev, EV_SW, + get_unaligned_le32(&event_data.switches)); + + return 0; +} + +/** + * cros_ec_keyb_resume - Resume the keyboard + * + * We use the resume notification as a chance to query the EC for switches. + * + * @dev: The keyboard device + * + * Returns 0 if no error or -error upon error. + */ +static __maybe_unused int cros_ec_keyb_resume(struct device *dev) +{ + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + + if (ckdev->bs_idev) + return cros_ec_keyb_query_switches(ckdev); + + return 0; +} + +/** + * cros_ec_keyb_register_bs - Register non-matrix buttons/switches + * + * Handles all the bits of the keyboard driver related to non-matrix buttons + * and switches, including asking the EC about which are present and telling + * the kernel to expect them. + * + * If this device has no support for buttons and switches we'll return no error + * but the ckdev->bs_idev will remain NULL when this function exits. + * + * @ckdev: The keyboard device + * + * Returns 0 if no error or -error upon error. + */ +static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev) +{ + struct cros_ec_device *ec_dev = ckdev->ec; + struct device *dev = ckdev->dev; struct input_dev *idev; - struct device_node *np; - int err; + union ec_response_get_next_data event_data = {}; + const char *phys; + u32 buttons; + u32 switches; + int ret; + int i; + + ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_SUPPORTED, + EC_MKBP_EVENT_BUTTON, &event_data, + sizeof(event_data.buttons)); + if (ret) + return ret; + buttons = get_unaligned_le32(&event_data.buttons); + + ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_SUPPORTED, + EC_MKBP_EVENT_SWITCH, &event_data, + sizeof(event_data.switches)); + if (ret) + return ret; + switches = get_unaligned_le32(&event_data.switches); + + if (!buttons && !switches) + return 0; - np = pdev->dev.of_node; - if (!np) - return -ENODEV; + /* + * We call the non-matrix buttons/switches 'input1', if present. + * Allocate phys before input dev, to ensure correct tear-down + * ordering. + */ + phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input1", ec_dev->phys_name); + if (!phys) + return -ENOMEM; - ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); - if (!ckdev) + idev = devm_input_allocate_device(dev); + if (!idev) return -ENOMEM; - err = matrix_keypad_parse_of_params(dev, &ckdev->rows, &ckdev->cols); + + idev->name = "cros_ec_buttons"; + idev->phys = phys; + __set_bit(EV_REP, idev->evbit); + + idev->id.bustype = BUS_VIRTUAL; + idev->id.version = 1; + idev->id.product = 0; + idev->dev.parent = dev; + + input_set_drvdata(idev, ckdev); + ckdev->bs_idev = idev; + + for (i = 0; i < ARRAY_SIZE(cros_ec_keyb_bs); i++) { + const struct cros_ec_bs_map *map = &cros_ec_keyb_bs[i]; + + if (buttons & BIT(map->bit)) + input_set_capability(idev, map->ev_type, map->code); + } + + ret = cros_ec_keyb_query_switches(ckdev); + if (ret) { + dev_err(dev, "cannot query switches\n"); + return ret; + } + + ret = input_register_device(ckdev->bs_idev); + if (ret) { + dev_err(dev, "cannot register input device\n"); + return ret; + } + + return 0; +} + +/** + * cros_ec_keyb_register_bs - Register matrix keys + * + * Handles all the bits of the keyboard driver related to matrix keys. + * + * @ckdev: The keyboard device + * + * Returns 0 if no error or -error upon error. + */ +static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) +{ + struct cros_ec_device *ec_dev = ckdev->ec; + struct device *dev = ckdev->dev; + struct input_dev *idev; + const char *phys; + int err; + + err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); if (err) return err; @@ -241,27 +547,28 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) if (!ckdev->old_kb_state) return -ENOMEM; + /* + * We call the keyboard matrix 'input0'. Allocate phys before input + * dev, to ensure correct tear-down ordering. + */ + phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", ec_dev->phys_name); + if (!phys) + return -ENOMEM; + idev = devm_input_allocate_device(dev); if (!idev) return -ENOMEM; - ckdev->ec = ec; - ckdev->notifier.notifier_call = cros_ec_keyb_work; - ckdev->dev = dev; - dev_set_drvdata(dev, ckdev); - idev->name = CROS_EC_DEV_NAME; - idev->phys = ec->phys_name; + idev->phys = phys; __set_bit(EV_REP, idev->evbit); idev->id.bustype = BUS_VIRTUAL; idev->id.version = 1; idev->id.product = 0; idev->dev.parent = dev; - idev->open = cros_ec_keyb_open; - idev->close = cros_ec_keyb_close; - ckdev->ghost_filter = of_property_read_bool(np, + ckdev->ghost_filter = of_property_read_bool(dev->of_node, "google,needs-ghost-filter"); err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, @@ -287,6 +594,57 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) return 0; } +static int cros_ec_keyb_probe(struct platform_device *pdev) +{ + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct cros_ec_keyb *ckdev; + int err; + + if (!dev->of_node) + return -ENODEV; + + ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); + if (!ckdev) + return -ENOMEM; + + ckdev->ec = ec; + ckdev->dev = dev; + dev_set_drvdata(dev, ckdev); + + err = cros_ec_keyb_register_matrix(ckdev); + if (err) { + dev_err(dev, "cannot register matrix inputs: %d\n", err); + return err; + } + + err = cros_ec_keyb_register_bs(ckdev); + if (err) { + dev_err(dev, "cannot register non-matrix inputs: %d\n", err); + return err; + } + + ckdev->notifier.notifier_call = cros_ec_keyb_work; + err = blocking_notifier_chain_register(&ckdev->ec->event_notifier, + &ckdev->notifier); + if (err) { + dev_err(dev, "cannot register notifier: %d\n", err); + return err; + } + + return 0; +} + +static int cros_ec_keyb_remove(struct platform_device *pdev) +{ + struct cros_ec_keyb *ckdev = dev_get_drvdata(&pdev->dev); + + blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, + &ckdev->notifier); + + return 0; +} + #ifdef CONFIG_OF static const struct of_device_id cros_ec_keyb_of_match[] = { { .compatible = "google,cros-ec-keyb" }, @@ -295,11 +653,15 @@ static const struct of_device_id cros_ec_keyb_of_match[] = { MODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match); #endif +static const SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); + static struct platform_driver cros_ec_keyb_driver = { .probe = cros_ec_keyb_probe, + .remove = cros_ec_keyb_remove, .driver = { .name = "cros-ec-keyb", .of_match_table = of_match_ptr(cros_ec_keyb_of_match), + .pm = &cros_ec_keyb_pm_ops, }, }; diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index f363d1d2907a..b20a5d044caa 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -172,7 +172,7 @@ static int __init davinci_ks_probe(struct platform_device *pdev) struct input_dev *key_dev; struct resource *res, *mem; struct device *dev = &pdev->dev; - struct davinci_ks_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct davinci_ks_platform_data *pdata = dev_get_platdata(dev); int error, i; if (pdata->device_enable) { @@ -255,7 +255,7 @@ static int __init davinci_ks_probe(struct platform_device *pdev) key_dev->name = "davinci_keyscan"; key_dev->phys = "davinci_keyscan/input0"; - key_dev->dev.parent = &pdev->dev; + key_dev->dev.parent = dev; key_dev->id.bustype = BUS_HOST; key_dev->id.vendor = 0x0001; key_dev->id.product = 0x0001; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 582462d0af75..da3d362f21b1 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -36,6 +36,8 @@ struct gpio_button_data { struct input_dev *input; struct gpio_desc *gpiod; + unsigned short *code; + struct timer_list release_timer; unsigned int release_delay; /* in msecs, for IRQ-only buttons */ @@ -52,6 +54,7 @@ struct gpio_keys_drvdata { const struct gpio_keys_platform_data *pdata; struct input_dev *input; struct mutex disable_lock; + unsigned short *keymap; struct gpio_button_data data[0]; }; @@ -203,7 +206,7 @@ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, if (only_disabled && !bdata->disabled) continue; - __set_bit(bdata->button->code, bits); + __set_bit(*bdata->code, bits); } ret = scnprintf(buf, PAGE_SIZE - 1, "%*pbl", n_events, bits); @@ -254,7 +257,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, if (bdata->button->type != type) continue; - if (test_bit(bdata->button->code, bits) && + if (test_bit(*bdata->code, bits) && !bdata->button->can_disable) { error = -EINVAL; goto out; @@ -269,7 +272,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, if (bdata->button->type != type) continue; - if (test_bit(bdata->button->code, bits)) + if (test_bit(*bdata->code, bits)) gpio_keys_disable_button(bdata); else gpio_keys_enable_button(bdata); @@ -371,7 +374,7 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) if (state) input_event(input, type, button->code, button->value); } else { - input_event(input, type, button->code, state); + input_event(input, type, *bdata->code, state); } input_sync(input); } @@ -411,7 +414,7 @@ static void gpio_keys_irq_timer(unsigned long _data) spin_lock_irqsave(&bdata->lock, flags); if (bdata->key_pressed) { - input_event(input, EV_KEY, bdata->button->code, 0); + input_event(input, EV_KEY, *bdata->code, 0); input_sync(input); bdata->key_pressed = false; } @@ -421,7 +424,6 @@ static void gpio_keys_irq_timer(unsigned long _data) static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; - const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned long flags; @@ -433,11 +435,11 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) if (bdata->button->wakeup) pm_wakeup_event(bdata->input->dev.parent, 0); - input_event(input, EV_KEY, button->code, 1); + input_event(input, EV_KEY, *bdata->code, 1); input_sync(input); if (!bdata->release_delay) { - input_event(input, EV_KEY, button->code, 0); + input_event(input, EV_KEY, *bdata->code, 0); input_sync(input); goto out; } @@ -465,12 +467,14 @@ static void gpio_keys_quiesce_key(void *data) static int gpio_keys_setup_key(struct platform_device *pdev, struct input_dev *input, - struct gpio_button_data *bdata, + struct gpio_keys_drvdata *ddata, const struct gpio_keys_button *button, + int idx, struct fwnode_handle *child) { const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; + struct gpio_button_data *bdata = &ddata->data[idx]; irq_handler_t isr; unsigned long irqflags; int irq; @@ -481,7 +485,10 @@ static int gpio_keys_setup_key(struct platform_device *pdev, spin_lock_init(&bdata->lock); if (child) { - bdata->gpiod = devm_get_gpiod_from_child(dev, NULL, child); + bdata->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, + child, + GPIOD_IN, + desc); if (IS_ERR(bdata->gpiod)) { error = PTR_ERR(bdata->gpiod); if (error == -ENOENT) { @@ -496,13 +503,6 @@ static int gpio_keys_setup_key(struct platform_device *pdev, error); return error; } - } else { - error = gpiod_direction_input(bdata->gpiod); - if (error) { - dev_err(dev, "Failed to configure GPIO %d as input: %d\n", - desc_to_gpio(bdata->gpiod), error); - return error; - } } } else if (gpio_is_valid(button->gpio)) { /* @@ -514,8 +514,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, if (button->active_low) flags |= GPIOF_ACTIVE_LOW; - error = devm_gpio_request_one(&pdev->dev, button->gpio, flags, - desc); + error = devm_gpio_request_one(dev, button->gpio, flags, desc); if (error < 0) { dev_err(dev, "Failed to request GPIO %d, error %d\n", button->gpio, error); @@ -577,16 +576,17 @@ static int gpio_keys_setup_key(struct platform_device *pdev, irqflags = 0; } - input_set_capability(input, button->type ?: EV_KEY, button->code); + bdata->code = &ddata->keymap[idx]; + *bdata->code = button->code; + input_set_capability(input, button->type ?: EV_KEY, *bdata->code); /* * Install custom action to cancel release timer and * workqueue item. */ - error = devm_add_action(&pdev->dev, gpio_keys_quiesce_key, bdata); + error = devm_add_action(dev, gpio_keys_quiesce_key, bdata); if (error) { - dev_err(&pdev->dev, - "failed to register quiesce action, error: %d\n", + dev_err(dev, "failed to register quiesce action, error: %d\n", error); return error; } @@ -598,8 +598,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev, if (!button->can_disable) irqflags |= IRQF_SHARED; - error = devm_request_any_context_irq(&pdev->dev, bdata->irq, - isr, irqflags, desc, bdata); + error = devm_request_any_context_irq(dev, bdata->irq, isr, irqflags, + desc, bdata); if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", bdata->irq, error); @@ -750,6 +750,12 @@ static int gpio_keys_probe(struct platform_device *pdev) return -ENOMEM; } + ddata->keymap = devm_kcalloc(dev, + pdata->nbuttons, sizeof(ddata->keymap[0]), + GFP_KERNEL); + if (!ddata->keymap) + return -ENOMEM; + input = devm_input_allocate_device(dev); if (!input) { dev_err(dev, "failed to allocate input device\n"); @@ -765,7 +771,7 @@ static int gpio_keys_probe(struct platform_device *pdev) input->name = pdata->name ? : pdev->name; input->phys = "gpio-keys/input0"; - input->dev.parent = &pdev->dev; + input->dev.parent = dev; input->open = gpio_keys_open; input->close = gpio_keys_close; @@ -774,25 +780,29 @@ static int gpio_keys_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0100; + input->keycode = ddata->keymap; + input->keycodesize = sizeof(ddata->keymap[0]); + input->keycodemax = pdata->nbuttons; + /* Enable auto repeat feature of Linux input subsystem */ if (pdata->rep) __set_bit(EV_REP, input->evbit); for (i = 0; i < pdata->nbuttons; i++) { const struct gpio_keys_button *button = &pdata->buttons[i]; - struct gpio_button_data *bdata = &ddata->data[i]; if (!dev_get_platdata(dev)) { - child = device_get_next_child_node(&pdev->dev, child); + child = device_get_next_child_node(dev, child); if (!child) { - dev_err(&pdev->dev, + dev_err(dev, "missing child device node for entry %d\n", i); return -EINVAL; } } - error = gpio_keys_setup_key(pdev, input, bdata, button, child); + error = gpio_keys_setup_key(pdev, input, ddata, + button, i, child); if (error) { fwnode_handle_put(child); return error; @@ -804,7 +814,7 @@ static int gpio_keys_probe(struct platform_device *pdev) fwnode_handle_put(child); - error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); + error = sysfs_create_group(&dev->kobj, &gpio_keys_attr_group); if (error) { dev_err(dev, "Unable to export keys/switches, error: %d\n", error); @@ -818,12 +828,12 @@ static int gpio_keys_probe(struct platform_device *pdev) goto err_remove_group; } - device_init_wakeup(&pdev->dev, wakeup); + device_init_wakeup(dev, wakeup); return 0; err_remove_group: - sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); + sysfs_remove_group(&dev->kobj, &gpio_keys_attr_group); return error; } @@ -831,8 +841,6 @@ static int gpio_keys_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); - device_init_wakeup(&pdev->dev, 0); - return 0; } diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index bed4f2086158..edc7262103b9 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -252,13 +252,13 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) size = sizeof(struct gpio_keys_polled_dev) + pdata->nbuttons * sizeof(struct gpio_keys_button_data); - bdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + bdev = devm_kzalloc(dev, size, GFP_KERNEL); if (!bdev) { dev_err(dev, "no memory for private data\n"); return -ENOMEM; } - poll_dev = devm_input_allocate_polled_device(&pdev->dev); + poll_dev = devm_input_allocate_polled_device(dev); if (!poll_dev) { dev_err(dev, "no memory for polled device\n"); return -ENOMEM; @@ -303,8 +303,10 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) return -EINVAL; } - bdata->gpiod = devm_get_gpiod_from_child(dev, NULL, - child); + bdata->gpiod = devm_fwnode_get_gpiod_from_child(dev, + NULL, child, + GPIOD_IN, + button->desc); if (IS_ERR(bdata->gpiod)) { error = PTR_ERR(bdata->gpiod); if (error != -EPROBE_DEFER) @@ -314,14 +316,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) fwnode_handle_put(child); return error; } - - error = gpiod_direction_input(bdata->gpiod); - if (error) { - dev_err(dev, "Failed to configure GPIO %d as input: %d\n", - desc_to_gpio(bdata->gpiod), error); - fwnode_handle_put(child); - return error; - } } else if (gpio_is_valid(button->gpio)) { /* * Legacy GPIO number so request the GPIO here and @@ -332,7 +326,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) if (button->active_low) flags |= GPIOF_ACTIVE_LOW; - error = devm_gpio_request_one(&pdev->dev, button->gpio, + error = devm_gpio_request_one(dev, button->gpio, flags, button->desc ? : DRV_NAME); if (error) { dev_err(dev, @@ -365,7 +359,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) bdev->poll_dev = poll_dev; bdev->dev = dev; bdev->pdata = pdata; - platform_set_drvdata(pdev, bdev); error = input_register_polled_device(poll_dev); if (error) { diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c index 80c81278ad2c..0116ac99f44c 100644 --- a/drivers/input/keyboard/jornada680_kbd.c +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -197,8 +197,6 @@ static int jornada680kbd_probe(struct platform_device *pdev) return -ENOMEM; } - platform_set_drvdata(pdev, jornadakbd); - jornadakbd->poll_dev = poll_dev; memcpy(jornadakbd->keymap, jornada_scancodes, diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c index 632523d4f5dc..1dd57ac0e7a2 100644 --- a/drivers/input/keyboard/lpc32xx-keys.c +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -145,7 +145,7 @@ static int lpc32xx_parse_dt(struct device *dev, u32 rows = 0, columns = 0; int err; - err = matrix_keypad_parse_of_params(dev, &rows, &columns); + err = matrix_keypad_parse_properties(dev, &rows, &columns); if (err) return err; if (rows != columns) { diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c index 5aa2361aef95..78e3567ec18c 100644 --- a/drivers/input/keyboard/maple_keyb.c +++ b/drivers/input/keyboard/maple_keyb.c @@ -196,7 +196,6 @@ static int probe_maple_kbd(struct device *dev) __clear_bit(KEY_RESERVED, idev->keybit); input_set_capability(idev, EV_MSC, MSC_SCAN); - input_set_drvdata(idev, kbd); error = input_register_device(idev); if (error) diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 7f12b6579f82..18839cd5f76e 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -545,8 +545,6 @@ static int matrix_keypad_remove(struct platform_device *pdev) { struct matrix_keypad *keypad = platform_get_drvdata(pdev); - device_init_wakeup(&pdev->dev, 0); - matrix_keypad_free_gpio(keypad); input_unregister_device(keypad->input_dev); kfree(keypad); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 5091133b7b8e..cd44d22d8770 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -241,7 +241,6 @@ static int max7359_probe(struct i2c_client *client, /* Initialize MAX7359 */ max7359_initialize(client); - i2c_set_clientdata(client, keypad); device_init_wakeup(&client->dev, 1); return 0; diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 0fd612dd76ed..884a74d8a7ed 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -12,14 +12,16 @@ * */ -#include <linux/module.h> -#include <linux/input.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/delay.h> #include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> #include <linux/interrupt.h> -#include <linux/i2c/mpr121_touchkey.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> /* Register definitions */ #define ELE_TOUCH_STATUS_0_ADDR 0x0 @@ -59,10 +61,9 @@ struct mpr121_touchkey { struct i2c_client *client; struct input_dev *input_dev; - unsigned int key_val; unsigned int statusbits; unsigned int keycount; - u16 keycodes[MPR121_MAX_KEY_COUNT]; + u32 keycodes[MPR121_MAX_KEY_COUNT]; }; struct mpr121_init_register { @@ -82,12 +83,49 @@ static const struct mpr121_init_register init_reg_table[] = { { AUTO_CONFIG_CTRL_ADDR, 0x0b }, }; +static void mpr121_vdd_supply_disable(void *data) +{ + struct regulator *vdd_supply = data; + + regulator_disable(vdd_supply); +} + +static struct regulator *mpr121_vdd_supply_init(struct device *dev) +{ + struct regulator *vdd_supply; + int err; + + vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(vdd_supply)) { + dev_err(dev, "failed to get vdd regulator: %ld\n", + PTR_ERR(vdd_supply)); + return vdd_supply; + } + + err = regulator_enable(vdd_supply); + if (err) { + dev_err(dev, "failed to enable vdd regulator: %d\n", err); + return ERR_PTR(err); + } + + err = devm_add_action(dev, mpr121_vdd_supply_disable, vdd_supply); + if (err) { + regulator_disable(vdd_supply); + dev_err(dev, "failed to add disable regulator action: %d\n", + err); + return ERR_PTR(err); + } + + return vdd_supply; +} + static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) { struct mpr121_touchkey *mpr121 = dev_id; struct i2c_client *client = mpr121->client; struct input_dev *input = mpr121->input_dev; - unsigned int key_num, key_val, pressed; + unsigned long bit_changed; + unsigned int key_num; int reg; reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); @@ -105,26 +143,29 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) reg &= TOUCH_STATUS_MASK; /* use old press bit to figure out which bit changed */ - key_num = ffs(reg ^ mpr121->statusbits) - 1; - pressed = reg & (1 << key_num); + bit_changed = reg ^ mpr121->statusbits; mpr121->statusbits = reg; + for_each_set_bit(key_num, &bit_changed, mpr121->keycount) { + unsigned int key_val, pressed; - key_val = mpr121->keycodes[key_num]; + pressed = reg & BIT(key_num); + key_val = mpr121->keycodes[key_num]; - input_event(input, EV_MSC, MSC_SCAN, key_num); - input_report_key(input, key_val, pressed); - input_sync(input); + input_event(input, EV_MSC, MSC_SCAN, key_num); + input_report_key(input, key_val, pressed); - dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, - pressed ? "pressed" : "released"); + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, + pressed ? "pressed" : "released"); + + } + input_sync(input); out: return IRQ_HANDLED; } -static int mpr121_phys_init(const struct mpr121_platform_data *pdata, - struct mpr121_touchkey *mpr121, - struct i2c_client *client) +static int mpr121_phys_init(struct mpr121_touchkey *mpr121, + struct i2c_client *client, int vdd_uv) { const struct mpr121_init_register *reg; unsigned char usl, lsl, tl, eleconf; @@ -154,9 +195,9 @@ static int mpr121_phys_init(const struct mpr121_platform_data *pdata, /* * Capacitance on sensing input varies and needs to be compensated. * The internal MPR121-auto-configuration can do this if it's - * registers are set properly (based on pdata->vdd_uv). + * registers are set properly (based on vdd_uv). */ - vdd = pdata->vdd_uv / 1000; + vdd = vdd_uv / 1000; usl = ((vdd - 700) * 256) / vdd; lsl = (usl * 65) / 100; tl = (usl * 90) / 100; @@ -187,72 +228,77 @@ err_i2c_write: static int mpr_touchkey_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct mpr121_platform_data *pdata = - dev_get_platdata(&client->dev); + struct device *dev = &client->dev; + struct regulator *vdd_supply; + int vdd_uv; struct mpr121_touchkey *mpr121; struct input_dev *input_dev; int error; int i; - if (!pdata) { - dev_err(&client->dev, "no platform data defined\n"); - return -EINVAL; - } - - if (!pdata->keymap || !pdata->keymap_size) { - dev_err(&client->dev, "missing keymap data\n"); + if (!client->irq) { + dev_err(dev, "irq number should not be zero\n"); return -EINVAL; } - if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) { - dev_err(&client->dev, "too many keys defined\n"); - return -EINVAL; - } + vdd_supply = mpr121_vdd_supply_init(dev); + if (IS_ERR(vdd_supply)) + return PTR_ERR(vdd_supply); - if (!client->irq) { - dev_err(&client->dev, "irq number should not be zero\n"); - return -EINVAL; - } + vdd_uv = regulator_get_voltage(vdd_supply); - mpr121 = devm_kzalloc(&client->dev, sizeof(*mpr121), - GFP_KERNEL); + mpr121 = devm_kzalloc(dev, sizeof(*mpr121), GFP_KERNEL); if (!mpr121) return -ENOMEM; - input_dev = devm_input_allocate_device(&client->dev); + input_dev = devm_input_allocate_device(dev); if (!input_dev) return -ENOMEM; mpr121->client = client; mpr121->input_dev = input_dev; - mpr121->keycount = pdata->keymap_size; + mpr121->keycount = device_property_read_u32_array(dev, "linux,keycodes", + NULL, 0); + if (mpr121->keycount > MPR121_MAX_KEY_COUNT) { + dev_err(dev, "too many keys defined (%d)\n", mpr121->keycount); + return -EINVAL; + } + + error = device_property_read_u32_array(dev, "linux,keycodes", + mpr121->keycodes, + mpr121->keycount); + if (error) { + dev_err(dev, + "failed to read linux,keycode property: %d\n", error); + return error; + } input_dev->name = "Freescale MPR121 Touchkey"; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->dev.parent = dev; + if (device_property_read_bool(dev, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_dev->keycode = mpr121->keycodes; input_dev->keycodesize = sizeof(mpr121->keycodes[0]); input_dev->keycodemax = mpr121->keycount; - for (i = 0; i < pdata->keymap_size; i++) { - input_set_capability(input_dev, EV_KEY, pdata->keymap[i]); - mpr121->keycodes[i] = pdata->keymap[i]; - } + for (i = 0; i < mpr121->keycount; i++) + input_set_capability(input_dev, EV_KEY, mpr121->keycodes[i]); - error = mpr121_phys_init(pdata, mpr121, client); + error = mpr121_phys_init(mpr121, client, vdd_uv); if (error) { - dev_err(&client->dev, "Failed to init register\n"); + dev_err(dev, "Failed to init register\n"); return error; } - error = devm_request_threaded_irq(&client->dev, client->irq, NULL, - mpr_touchkey_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - client->dev.driver->name, mpr121); + error = devm_request_threaded_irq(dev, client->irq, NULL, + mpr_touchkey_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev->driver->name, mpr121); if (error) { - dev_err(&client->dev, "Failed to register interrupt\n"); + dev_err(dev, "Failed to register interrupt\n"); return error; } @@ -261,13 +307,13 @@ static int mpr_touchkey_probe(struct i2c_client *client, return error; i2c_set_clientdata(client, mpr121); - device_init_wakeup(&client->dev, pdata->wakeup); + device_init_wakeup(dev, + device_property_read_bool(dev, "wakeup-source")); return 0; } -#ifdef CONFIG_PM_SLEEP -static int mpr_suspend(struct device *dev) +static int __maybe_unused mpr_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -279,7 +325,7 @@ static int mpr_suspend(struct device *dev) return 0; } -static int mpr_resume(struct device *dev) +static int __maybe_unused mpr_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); @@ -292,7 +338,6 @@ static int mpr_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume); @@ -302,10 +347,19 @@ static const struct i2c_device_id mpr121_id[] = { }; MODULE_DEVICE_TABLE(i2c, mpr121_id); +#ifdef CONFIG_OF +static const struct of_device_id mpr121_touchkey_dt_match_table[] = { + { .compatible = "fsl,mpr121-touchkey" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mpr121_touchkey_dt_match_table); +#endif + static struct i2c_driver mpr_touchkey_driver = { .driver = { .name = "mpr121", .pm = &mpr121_touchkey_pm_ops, + .of_match_table = of_match_ptr(mpr121_touchkey_dt_match_table), }, .id_table = mpr121_id, .probe = mpr_touchkey_probe, diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c index 7abfd34eb87e..c7f26fa3034c 100644 --- a/drivers/input/keyboard/nspire-keypad.c +++ b/drivers/input/keyboard/nspire-keypad.c @@ -249,8 +249,6 @@ static int nspire_keypad_probe(struct platform_device *pdev) return error; } - platform_set_drvdata(pdev, keypad); - dev_dbg(&pdev->dev, "TI-NSPIRE keypad at %pR (scan_interval=%uus, row_delay=%uus%s)\n", res, keypad->row_delay, keypad->scan_interval, diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 6639b2b8528a..ebc67ba41fe2 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -223,8 +223,8 @@ static int omap4_keypad_parse_dt(struct device *dev, struct device_node *np = dev->of_node; int err; - err = matrix_keypad_parse_of_params(dev, &keypad_data->rows, - &keypad_data->cols); + err = matrix_keypad_parse_properties(dev, &keypad_data->rows, + &keypad_data->cols); if (err) return err; @@ -375,7 +375,6 @@ static int omap4_keypad_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(&pdev->dev); - device_init_wakeup(&pdev->dev, false); free_irq(keypad_data->irq, keypad_data); err_free_keymap: kfree(keypad_data->keymap); @@ -401,8 +400,6 @@ static int omap4_keypad_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); - device_init_wakeup(&pdev->dev, false); - input_unregister_device(keypad_data->input); iounmap(keypad_data->base); diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c index f8502bb29176..d62b4068c077 100644 --- a/drivers/input/keyboard/opencores-kbd.c +++ b/drivers/input/keyboard/opencores-kbd.c @@ -75,8 +75,6 @@ static int opencores_kbd_probe(struct platform_device *pdev) input->name = pdev->name; input->phys = "opencores-kbd/input0"; - input_set_drvdata(input, opencores_kbd); - input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; @@ -112,8 +110,6 @@ static int opencores_kbd_probe(struct platform_device *pdev) return error; } - platform_set_drvdata(pdev, opencores_kbd); - return 0; } diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 5c68e3f096bc..97c5424f49b9 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -515,7 +515,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) int rc; unsigned int ctrl_val; - rc = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols); + rc = matrix_keypad_parse_properties(&pdev->dev, &rows, &cols); if (rc) return rc; diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index e24443376e75..3841fa30db33 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -126,7 +126,7 @@ static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad, u32 rows, cols; int error; - error = matrix_keypad_parse_of_params(dev, &rows, &cols); + error = matrix_keypad_parse_properties(dev, &rows, &cols); if (error) return error; diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 4e319eb9e19d..316414465c77 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -445,7 +445,6 @@ static int samsung_keypad_probe(struct platform_device *pdev) err_disable_runtime_pm: pm_runtime_disable(&pdev->dev); - device_init_wakeup(&pdev->dev, 0); err_unprepare_clk: clk_unprepare(keypad->clk); return error; @@ -456,7 +455,6 @@ static int samsung_keypad_remove(struct platform_device *pdev) struct samsung_keypad *keypad = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); - device_init_wakeup(&pdev->dev, 0); input_unregister_device(keypad->input_dev); diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 8083eaa0524a..7d25fa338ab4 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -283,8 +283,6 @@ static int spear_kbd_remove(struct platform_device *pdev) input_unregister_device(kbd->input); clk_unprepare(kbd->clk); - device_init_wakeup(&pdev->dev, 0); - return 0; } diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c index de7be4f03d91..babcfb165e4f 100644 --- a/drivers/input/keyboard/st-keyscan.c +++ b/drivers/input/keyboard/st-keyscan.c @@ -106,8 +106,8 @@ static int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data) struct device_node *np = dev->of_node; int error; - error = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows, - &keypad_data->n_cols); + error = matrix_keypad_parse_properties(dev, &keypad_data->n_rows, + &keypad_data->n_cols); if (error) { dev_err(dev, "failed to parse keypad params\n"); return error; diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index fe6e3f22eed7..8c6c0b9109c7 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -354,7 +354,7 @@ static int stmpe_keypad_probe(struct platform_device *pdev) input->id.bustype = BUS_I2C; input->dev.parent = &pdev->dev; - error = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols); + error = matrix_keypad_parse_properties(&pdev->dev, &rows, &cols); if (error) return error; diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c index cc8f7ddcee53..a37c172452e6 100644 --- a/drivers/input/keyboard/sun4i-lradc-keys.c +++ b/drivers/input/keyboard/sun4i-lradc-keys.c @@ -261,7 +261,6 @@ static int sun4i_lradc_probe(struct platform_device *pdev) if (error) return error; - platform_set_drvdata(pdev, lradc); return 0; } diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 3048ef3e3e16..44dd7689c571 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -24,18 +24,17 @@ * alternative licensing inquiries. */ -#include <linux/types.h> -#include <linux/module.h> -#include <linux/init.h> #include <linux/delay.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/gpio.h> #include <linux/i2c.h> +#include <linux/init.h> #include <linux/input.h> -#include <linux/input/tca8418_keypad.h> +#include <linux/input/matrix_keypad.h> +#include <linux/interrupt.h> +#include <linux/module.h> #include <linux/of.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/types.h> /* TCA8418 hardware limits */ #define TCA8418_MAX_ROWS 8 @@ -264,41 +263,25 @@ static int tca8418_configure(struct tca8418_keypad *keypad_data, } static int tca8418_keypad_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct device *dev = &client->dev; - const struct tca8418_keypad_platform_data *pdata = - dev_get_platdata(dev); struct tca8418_keypad *keypad_data; struct input_dev *input; - const struct matrix_keymap_data *keymap_data = NULL; u32 rows = 0, cols = 0; - bool rep = false; - bool irq_is_gpio = false; - int irq; int error, row_shift, max_keys; - /* Copy the platform data */ - if (pdata) { - if (!pdata->keymap_data) { - dev_err(dev, "no keymap data defined\n"); - return -EINVAL; - } - keymap_data = pdata->keymap_data; - rows = pdata->rows; - cols = pdata->cols; - rep = pdata->rep; - irq_is_gpio = pdata->irq_is_gpio; - } else { - struct device_node *np = dev->of_node; - int err; - - err = matrix_keypad_parse_of_params(dev, &rows, &cols); - if (err) - return err; - rep = of_property_read_bool(np, "keypad,autorepeat"); + /* Check i2c driver capabilities */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + dev_err(dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; } + error = matrix_keypad_parse_properties(dev, &rows, &cols); + if (error) + return error; + if (!rows || rows > TCA8418_MAX_ROWS) { dev_err(dev, "invalid rows\n"); return -EINVAL; @@ -309,13 +292,6 @@ static int tca8418_keypad_probe(struct i2c_client *client, return -EINVAL; } - /* Check i2c driver capabilities */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { - dev_err(dev, "%s adapter not supported\n", - dev_driver_string(&client->adapter->dev)); - return -ENODEV; - } - row_shift = get_count_order(cols); max_keys = rows << row_shift; @@ -345,27 +321,20 @@ static int tca8418_keypad_probe(struct i2c_client *client, input->id.product = 0x001; input->id.version = 0x0001; - error = matrix_keypad_build_keymap(keymap_data, NULL, rows, cols, - NULL, input); + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, NULL, input); if (error) { dev_err(dev, "Failed to build keymap\n"); return error; } - if (rep) + if (device_property_read_bool(dev, "keypad,autorepeat")) __set_bit(EV_REP, input->evbit); - input_set_capability(input, EV_MSC, MSC_SCAN); - - input_set_drvdata(input, keypad_data); - irq = client->irq; - if (irq_is_gpio) - irq = gpio_to_irq(irq); + input_set_capability(input, EV_MSC, MSC_SCAN); - error = devm_request_threaded_irq(dev, irq, NULL, tca8418_irq_handler, - IRQF_TRIGGER_FALLING | - IRQF_SHARED | - IRQF_ONESHOT, + error = devm_request_threaded_irq(dev, client->irq, + NULL, tca8418_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, client->name, keypad_data); if (error) { dev_err(dev, "Unable to claim irq %d; error %d\n", @@ -384,30 +353,21 @@ static int tca8418_keypad_probe(struct i2c_client *client, } static const struct i2c_device_id tca8418_id[] = { - { TCA8418_NAME, 8418, }, + { "tca8418", 8418, }, { } }; MODULE_DEVICE_TABLE(i2c, tca8418_id); -#ifdef CONFIG_OF static const struct of_device_id tca8418_dt_ids[] = { { .compatible = "ti,tca8418", }, { } }; MODULE_DEVICE_TABLE(of, tca8418_dt_ids); -/* - * The device tree based i2c loader looks for - * "i2c:" + second_component_of(property("compatible")) - * and therefore we need an alias to be found. - */ -MODULE_ALIAS("i2c:tca8418"); -#endif - static struct i2c_driver tca8418_keypad_driver = { .driver = { - .name = TCA8418_NAME, - .of_match_table = of_match_ptr(tca8418_dt_ids), + .name = "tca8418_keypad", + .of_match_table = tca8418_dt_ids, }, .probe = tca8418_keypad_probe, .id_table = tca8418_id, diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c new file mode 100644 index 000000000000..485900f953e0 --- /dev/null +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -0,0 +1,284 @@ +/* + * TM2 touchkey device driver + * + * Copyright 2005 Phil Blundell + * Copyright 2016 Samsung Electronics Co., Ltd. + * + * Author: Beomho Seo <beomho.seo@samsung.com> + * Author: Jaechul Lee <jcsing.lee@samsung.com> + * + * 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/bitops.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> + +#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey" +#define TM2_TOUCHKEY_KEYCODE_REG 0x03 +#define TM2_TOUCHKEY_BASE_REG 0x00 +#define TM2_TOUCHKEY_CMD_LED_ON 0x10 +#define TM2_TOUCHKEY_CMD_LED_OFF 0x20 +#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3) +#define TM2_TOUCHKEY_BIT_KEYCODE GENMASK(2, 0) +#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000 +#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000 + +enum { + TM2_TOUCHKEY_KEY_MENU = 0x1, + TM2_TOUCHKEY_KEY_BACK, +}; + +struct tm2_touchkey_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct led_classdev led_dev; + struct regulator *vdd; + struct regulator_bulk_data regulators[2]; +}; + +static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct tm2_touchkey_data *touchkey = + container_of(led_dev, struct tm2_touchkey_data, led_dev); + u32 volt; + u8 data; + + if (brightness == LED_OFF) { + volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN; + data = TM2_TOUCHKEY_CMD_LED_OFF; + } else { + volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX; + data = TM2_TOUCHKEY_CMD_LED_ON; + } + + regulator_set_voltage(touchkey->vdd, volt, volt); + i2c_smbus_write_byte_data(touchkey->client, + TM2_TOUCHKEY_BASE_REG, data); +} + +static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey) +{ + int error; + + error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators), + touchkey->regulators); + if (error) + return error; + + /* waiting for device initialization, at least 150ms */ + msleep(150); + + return 0; +} + +static void tm2_touchkey_power_disable(void *data) +{ + struct tm2_touchkey_data *touchkey = data; + + regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators), + touchkey->regulators); +} + +static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid) +{ + struct tm2_touchkey_data *touchkey = devid; + int data; + int key; + + data = i2c_smbus_read_byte_data(touchkey->client, + TM2_TOUCHKEY_KEYCODE_REG); + if (data < 0) { + dev_err(&touchkey->client->dev, + "failed to read i2c data: %d\n", data); + goto out; + } + + switch (data & TM2_TOUCHKEY_BIT_KEYCODE) { + case TM2_TOUCHKEY_KEY_MENU: + key = KEY_PHONE; + break; + + case TM2_TOUCHKEY_KEY_BACK: + key = KEY_BACK; + break; + + default: + dev_warn(&touchkey->client->dev, + "unhandled keycode, data %#02x\n", data); + goto out; + } + + if (data & TM2_TOUCHKEY_BIT_PRESS_EV) { + input_report_key(touchkey->input_dev, KEY_PHONE, 0); + input_report_key(touchkey->input_dev, KEY_BACK, 0); + } else { + input_report_key(touchkey->input_dev, key, 1); + } + + input_sync(touchkey->input_dev); + +out: + return IRQ_HANDLED; +} + +static int tm2_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tm2_touchkey_data *touchkey; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "incompatible I2C adapter\n"); + return -EIO; + } + + touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL); + if (!touchkey) + return -ENOMEM; + + touchkey->client = client; + i2c_set_clientdata(client, touchkey); + + touchkey->regulators[0].supply = "vcc"; + touchkey->regulators[1].supply = "vdd"; + error = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(touchkey->regulators), + touchkey->regulators); + if (error) { + dev_err(&client->dev, "failed to get regulators: %d\n", error); + return error; + } + + /* Save VDD for easy access */ + touchkey->vdd = touchkey->regulators[1].consumer; + + error = tm2_touchkey_power_enable(touchkey); + if (error) { + dev_err(&client->dev, "failed to power up device: %d\n", error); + return error; + } + + error = devm_add_action_or_reset(&client->dev, + tm2_touchkey_power_disable, touchkey); + if (error) { + dev_err(&client->dev, + "failed to install poweroff handler: %d\n", error); + return error; + } + + /* input device */ + touchkey->input_dev = devm_input_allocate_device(&client->dev); + if (!touchkey->input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME; + touchkey->input_dev->id.bustype = BUS_I2C; + + input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE); + input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK); + + error = input_register_device(touchkey->input_dev); + if (error) { + dev_err(&client->dev, + "failed to register input device: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, tm2_touchkey_irq_handler, + IRQF_ONESHOT, + TM2_TOUCHKEY_DEV_NAME, touchkey); + if (error) { + dev_err(&client->dev, + "failed to request threaded irq: %d\n", error); + return error; + } + + /* led device */ + touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME; + touchkey->led_dev.brightness = LED_FULL; + touchkey->led_dev.max_brightness = LED_FULL; + touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set; + + error = devm_led_classdev_register(&client->dev, &touchkey->led_dev); + if (error) { + dev_err(&client->dev, + "failed to register touchkey led: %d\n", error); + return error; + } + + return 0; +} + +static int __maybe_unused tm2_touchkey_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tm2_touchkey_data *touchkey = i2c_get_clientdata(client); + + disable_irq(client->irq); + tm2_touchkey_power_disable(touchkey); + + return 0; +} + +static int __maybe_unused tm2_touchkey_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tm2_touchkey_data *touchkey = i2c_get_clientdata(client); + int ret; + + enable_irq(client->irq); + + ret = tm2_touchkey_power_enable(touchkey); + if (ret) + dev_err(dev, "failed to enable power: %d\n", ret); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(tm2_touchkey_pm_ops, + tm2_touchkey_suspend, tm2_touchkey_resume); + +static const struct i2c_device_id tm2_touchkey_id_table[] = { + { TM2_TOUCHKEY_DEV_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table); + +static const struct of_device_id tm2_touchkey_of_match[] = { + { .compatible = "cypress,tm2-touchkey", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match); + +static struct i2c_driver tm2_touchkey_driver = { + .driver = { + .name = TM2_TOUCHKEY_DEV_NAME, + .pm = &tm2_touchkey_pm_ops, + .of_match_table = of_match_ptr(tm2_touchkey_of_match), + }, + .probe = tm2_touchkey_probe, + .id_table = tm2_touchkey_id_table, +}; +module_i2c_driver(tm2_touchkey_driver); + +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); +MODULE_AUTHOR("Jaechul Lee <jcsing.lee@samsung.com>"); +MODULE_DESCRIPTION("Samsung touchkey driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 323a0fb575a4..39e72b3219d8 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -374,8 +374,8 @@ static int twl4030_kp_probe(struct platform_device *pdev) kp->autorepeat = pdata->rep; keymap_data = pdata->keymap_data; } else { - error = matrix_keypad_parse_of_params(&pdev->dev, &kp->n_rows, - &kp->n_cols); + error = matrix_keypad_parse_properties(&pdev->dev, &kp->n_rows, + &kp->n_cols); if (error) return error; @@ -441,7 +441,6 @@ static int twl4030_kp_probe(struct platform_device *pdev) return -EIO; } - platform_set_drvdata(pdev, kp); return 0; } diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index 08b61f506db6..8ccefc15c7a4 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -14,18 +14,18 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * */ #include <linux/device.h> +#include <linux/export.h> #include <linux/gfp.h> -#include <linux/kernel.h> -#include <linux/types.h> #include <linux/input.h> -#include <linux/of.h> -#include <linux/export.h> -#include <linux/module.h> #include <linux/input/matrix_keypad.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/types.h> static bool matrix_keypad_map_key(struct input_dev *input_dev, unsigned int rows, unsigned int cols, @@ -49,18 +49,22 @@ static bool matrix_keypad_map_key(struct input_dev *input_dev, return true; } -#ifdef CONFIG_OF -int matrix_keypad_parse_of_params(struct device *dev, - unsigned int *rows, unsigned int *cols) +/** + * matrix_keypad_parse_properties() - Read properties of matrix keypad + * + * @dev: Device containing properties + * @rows: Returns number of matrix rows + * @cols: Returns number of matrix columns + * @return 0 if OK, <0 on error + */ +int matrix_keypad_parse_properties(struct device *dev, + unsigned int *rows, unsigned int *cols) { - struct device_node *np = dev->of_node; + *rows = *cols = 0; + + device_property_read_u32(dev, "keypad,num-rows", rows); + device_property_read_u32(dev, "keypad,num-columns", cols); - if (!np) { - dev_err(dev, "missing DT data"); - return -EINVAL; - } - of_property_read_u32(np, "keypad,num-rows", rows); - of_property_read_u32(np, "keypad,num-columns", cols); if (!*rows || !*cols) { dev_err(dev, "number of keypad rows/columns not specified\n"); return -EINVAL; @@ -68,62 +72,61 @@ int matrix_keypad_parse_of_params(struct device *dev, return 0; } -EXPORT_SYMBOL_GPL(matrix_keypad_parse_of_params); +EXPORT_SYMBOL_GPL(matrix_keypad_parse_properties); -static int matrix_keypad_parse_of_keymap(const char *propname, - unsigned int rows, unsigned int cols, - struct input_dev *input_dev) +static int matrix_keypad_parse_keymap(const char *propname, + unsigned int rows, unsigned int cols, + struct input_dev *input_dev) { struct device *dev = input_dev->dev.parent; - struct device_node *np = dev->of_node; unsigned int row_shift = get_count_order(cols); unsigned int max_keys = rows << row_shift; - unsigned int proplen, i, size; - const __be32 *prop; - - if (!np) - return -ENOENT; + u32 *keys; + int i; + int size; + int retval; if (!propname) propname = "linux,keymap"; - prop = of_get_property(np, propname, &proplen); - if (!prop) { - dev_err(dev, "OF: %s property not defined in %s\n", - propname, np->full_name); - return -ENOENT; + size = device_property_read_u32_array(dev, propname, NULL, 0); + if (size <= 0) { + dev_err(dev, "missing or malformed property %s: %d\n", + propname, size); + return size < 0 ? size : -EINVAL; } - if (proplen % sizeof(u32)) { - dev_err(dev, "OF: Malformed keycode property %s in %s\n", - propname, np->full_name); + if (size > max_keys) { + dev_err(dev, "%s size overflow (%d vs max %u)\n", + propname, size, max_keys); return -EINVAL; } - size = proplen / sizeof(u32); - if (size > max_keys) { - dev_err(dev, "OF: %s size overflow\n", propname); - return -EINVAL; + keys = kmalloc_array(size, sizeof(u32), GFP_KERNEL); + if (!keys) + return -ENOMEM; + + retval = device_property_read_u32_array(dev, propname, keys, size); + if (retval) { + dev_err(dev, "failed to read %s property: %d\n", + propname, retval); + goto out; } for (i = 0; i < size; i++) { - unsigned int key = be32_to_cpup(prop + i); - if (!matrix_keypad_map_key(input_dev, rows, cols, - row_shift, key)) - return -EINVAL; + row_shift, keys[i])) { + retval = -EINVAL; + goto out; + } } - return 0; -} -#else -static int matrix_keypad_parse_of_keymap(const char *propname, - unsigned int rows, unsigned int cols, - struct input_dev *input_dev) -{ - return -ENOSYS; + retval = 0; + +out: + kfree(keys); + return retval; } -#endif /** * matrix_keypad_build_keymap - convert platform keymap into matrix keymap @@ -192,8 +195,8 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, return -EINVAL; } } else { - error = matrix_keypad_parse_of_keymap(keymap_name, rows, cols, - input_dev); + error = matrix_keypad_parse_keymap(keymap_name, rows, cols, + input_dev); if (error) return error; } diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c index cf9908f1e5d5..45a09497f680 100644 --- a/drivers/input/misc/88pm80x_onkey.c +++ b/drivers/input/misc/88pm80x_onkey.c @@ -143,7 +143,6 @@ static int pm80x_onkey_remove(struct platform_device *pdev) { struct pm80x_onkey_info *info = platform_get_drvdata(pdev); - device_init_wakeup(&pdev->dev, 0); pm80x_free_irq(info->pm80x, info->irq, info); input_unregister_device(info->idev); kfree(info); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1ae4d9617ff8..5b6c52210d20 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -234,16 +234,6 @@ config INPUT_MMA8450 To compile this driver as a module, choose M here: the module will be called mma8450. -config INPUT_MPU3050 - tristate "MPU3050 Triaxial gyroscope sensor" - depends on I2C - help - Say Y here if you want to support InvenSense MPU3050 - connected via an I2C bus. - - To compile this driver as a module, choose M here: the - module will be called mpu3050. - config INPUT_APANEL tristate "Fujitsu Lifebook Application Panel buttons" depends on X86 && I2C && LEDS_CLASS diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 0b6d025f0487..b10523f2878e 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -48,7 +48,6 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o -obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 4f5ef5bb535b..a33ed5710b15 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -109,7 +109,6 @@ static int ab8500_ponkey_probe(struct platform_device *pdev) return error; } - platform_set_drvdata(pdev, ponkey); return 0; } diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c index 07ec465f1095..21dc1b8b2a4a 100644 --- a/drivers/input/misc/arizona-haptics.c +++ b/drivers/input/misc/arizona-haptics.c @@ -201,8 +201,6 @@ static int arizona_haptics_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, haptics); - return 0; } diff --git a/drivers/input/misc/atmel_captouch.c b/drivers/input/misc/atmel_captouch.c index 941265415a89..c4c0f4bb7627 100644 --- a/drivers/input/misc/atmel_captouch.c +++ b/drivers/input/misc/atmel_captouch.c @@ -191,7 +191,6 @@ static int atmel_captouch_probe(struct i2c_client *client, return -ENOMEM; capdev->client = client; - i2c_set_clientdata(client, capdev); err = atmel_read(capdev, REG_KEY_STATE, &capdev->prev_btn, sizeof(capdev->prev_btn)); diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c index a0fc18fdfc0c..799ce3d2820e 100644 --- a/drivers/input/misc/bfin_rotary.c +++ b/drivers/input/misc/bfin_rotary.c @@ -147,19 +147,18 @@ static int bfin_rotary_probe(struct platform_device *pdev) if (pdata->pin_list) { error = peripheral_request_list(pdata->pin_list, - dev_name(&pdev->dev)); + dev_name(dev)); if (error) { dev_err(dev, "requesting peripherals failed: %d\n", error); return error; } - error = devm_add_action(dev, bfin_rotary_free_action, - pdata->pin_list); + error = devm_add_action_or_reset(dev, bfin_rotary_free_action, + pdata->pin_list); if (error) { dev_err(dev, "setting cleanup action failed: %d\n", error); - peripheral_free_list(pdata->pin_list); return error; } } @@ -189,7 +188,7 @@ static int bfin_rotary_probe(struct platform_device *pdev) input->name = pdev->name; input->phys = "bfin-rotary/input0"; - input->dev.parent = &pdev->dev; + input->dev.parent = dev; input_set_drvdata(input, rotary); @@ -239,7 +238,7 @@ static int bfin_rotary_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rotary); - device_init_wakeup(&pdev->dev, 1); + device_init_wakeup(dev, 1); return 0; } diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 2124390ec38c..1fa85379f86c 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -207,7 +207,7 @@ static int bma150_set_mode(struct bma150_data *bma150, u8 mode) return error; if (mode == BMA150_MODE_NORMAL) - msleep(2); + usleep_range(2000, 2100); bma150->mode = mode; return 0; @@ -222,7 +222,7 @@ static int bma150_soft_reset(struct bma150_data *bma150) if (error) return error; - msleep(2); + usleep_range(2000, 2100); return 0; } diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index b4ff1e86d3d3..3e9c353d82ef 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -287,7 +287,6 @@ static int da9063_onkey_probe(struct platform_device *pdev) return error; } - platform_set_drvdata(pdev, onkey); return 0; } diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c index b6b7bd4e5462..82e272ebc0ed 100644 --- a/drivers/input/misc/dm355evm_keys.c +++ b/drivers/input/misc/dm355evm_keys.c @@ -195,8 +195,6 @@ static int dm355evm_keys_probe(struct platform_device *pdev) goto fail1; keys->irq = status; - input_set_drvdata(input, keys); - input->name = "DM355 EVM Controls"; input->phys = "dm355evm/input0"; input->dev.parent = &pdev->dev; diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 0a2b865b1000..fb089d36c0d6 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -538,7 +538,7 @@ static int drv260x_probe(struct i2c_client *client, haptics->input_dev = devm_input_allocate_device(dev); if (!haptics->input_dev) { - dev_err(&client->dev, "Failed to allocate input device\n"); + dev_err(dev, "Failed to allocate input device\n"); return -ENOMEM; } diff --git a/drivers/input/misc/e3x0-button.c b/drivers/input/misc/e3x0-button.c index 13bfca8a7b16..e956cf1d273f 100644 --- a/drivers/input/misc/e3x0-button.c +++ b/drivers/input/misc/e3x0-button.c @@ -120,17 +120,10 @@ static int e3x0_button_probe(struct platform_device *pdev) return error; } - platform_set_drvdata(pdev, input); device_init_wakeup(&pdev->dev, 1); return 0; } -static int e3x0_button_remove(struct platform_device *pdev) -{ - device_init_wakeup(&pdev->dev, 0); - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id e3x0_button_match[] = { { .compatible = "ettus,e3x0-button", }, @@ -146,7 +139,6 @@ static struct platform_driver e3x0_button_driver = { .pm = &e3x0_button_pm_ops, }, .probe = e3x0_button_probe, - .remove = e3x0_button_remove, }; module_platform_driver(e3x0_button_driver); diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c index 3bfdfcc20485..c6a29e57b5e4 100644 --- a/drivers/input/misc/gp2ap002a00f.c +++ b/drivers/input/misc/gp2ap002a00f.c @@ -210,8 +210,6 @@ static int gp2a_remove(struct i2c_client *client) struct gp2a_data *dt = i2c_get_clientdata(client); const struct gp2a_platform_data *pdata = dt->pdata; - device_init_wakeup(&client->dev, false); - free_irq(client->irq, dt); input_unregister_device(dt->input); diff --git a/drivers/input/misc/gpio_decoder.c b/drivers/input/misc/gpio_decoder.c index ca7e0bacb2d8..1dca526e6f1a 100644 --- a/drivers/input/misc/gpio_decoder.c +++ b/drivers/input/misc/gpio_decoder.c @@ -110,7 +110,6 @@ static int gpio_decoder_probe(struct platform_device *pdev) dev_err(dev, "failed to register polled device\n"); return err; } - platform_set_drvdata(pdev, decoder); return 0; } diff --git a/drivers/input/misc/gpio_tilt_polled.c b/drivers/input/misc/gpio_tilt_polled.c index f103b99d1852..6e217a45e39a 100644 --- a/drivers/input/misc/gpio_tilt_polled.c +++ b/drivers/input/misc/gpio_tilt_polled.c @@ -138,7 +138,7 @@ static int gpio_tilt_polled_probe(struct platform_device *pdev) input->name = pdev->name; input->phys = DRV_NAME"/input0"; - input->dev.parent = &pdev->dev; + input->dev.parent = dev; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; diff --git a/drivers/input/misc/hisi_powerkey.c b/drivers/input/misc/hisi_powerkey.c index 675539c529ce..dee6245f38d7 100644 --- a/drivers/input/misc/hisi_powerkey.c +++ b/drivers/input/misc/hisi_powerkey.c @@ -75,9 +75,9 @@ static int hi65xx_powerkey_probe(struct platform_device *pdev) struct input_dev *input; int irq, i, error; - input = devm_input_allocate_device(&pdev->dev); + input = devm_input_allocate_device(dev); if (!input) { - dev_err(&pdev->dev, "failed to allocate input device\n"); + dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; } @@ -111,19 +111,11 @@ static int hi65xx_powerkey_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { - dev_err(&pdev->dev, "failed to register input device: %d\n", - error); + dev_err(dev, "failed to register input device: %d\n", error); return error; } - device_init_wakeup(&pdev->dev, 1); - - return 0; -} - -static int hi65xx_powerkey_remove(struct platform_device *pdev) -{ - device_init_wakeup(&pdev->dev, 0); + device_init_wakeup(dev, 1); return 0; } @@ -133,7 +125,6 @@ static struct platform_driver hi65xx_powerkey_driver = { .name = "hi65xx-powerkey", }, .probe = hi65xx_powerkey_probe, - .remove = hi65xx_powerkey_remove, }; module_platform_driver(hi65xx_powerkey_driver); diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 19c73574458e..b60cdea73826 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -205,8 +205,6 @@ static int mma8450_probe(struct i2c_client *c, return err; } - i2c_set_clientdata(c, m); - return 0; } diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c deleted file mode 100644 index f088db31cfc7..000000000000 --- a/drivers/input/misc/mpu3050.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * MPU3050 Tri-axis gyroscope driver - * - * Copyright (C) 2011 Wistron Co.Ltd - * Joseph Lai <joseph_lai@wistron.com> - * - * Trimmed down by Alan Cox <alan@linux.intel.com> to produce this version - * - * This is a 'lite' version of the driver, while we consider the right way - * to present the other features to user space. In particular it requires the - * device has an IRQ, and it only provides an input interface, so is not much - * use for device orientation. A fuller version is available from the Meego - * tree. - * - * This program is based on bma023.c. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - */ - -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/mutex.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/input.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/pm_runtime.h> - -#define MPU3050_CHIP_ID 0x69 - -#define MPU3050_AUTO_DELAY 1000 - -#define MPU3050_MIN_VALUE -32768 -#define MPU3050_MAX_VALUE 32767 - -#define MPU3050_DEFAULT_POLL_INTERVAL 200 -#define MPU3050_DEFAULT_FS_RANGE 3 - -/* Register map */ -#define MPU3050_CHIP_ID_REG 0x00 -#define MPU3050_SMPLRT_DIV 0x15 -#define MPU3050_DLPF_FS_SYNC 0x16 -#define MPU3050_INT_CFG 0x17 -#define MPU3050_XOUT_H 0x1D -#define MPU3050_PWR_MGM 0x3E -#define MPU3050_PWR_MGM_POS 6 - -/* Register bits */ - -/* DLPF_FS_SYNC */ -#define MPU3050_EXT_SYNC_NONE 0x00 -#define MPU3050_EXT_SYNC_TEMP 0x20 -#define MPU3050_EXT_SYNC_GYROX 0x40 -#define MPU3050_EXT_SYNC_GYROY 0x60 -#define MPU3050_EXT_SYNC_GYROZ 0x80 -#define MPU3050_EXT_SYNC_ACCELX 0xA0 -#define MPU3050_EXT_SYNC_ACCELY 0xC0 -#define MPU3050_EXT_SYNC_ACCELZ 0xE0 -#define MPU3050_EXT_SYNC_MASK 0xE0 -#define MPU3050_FS_250DPS 0x00 -#define MPU3050_FS_500DPS 0x08 -#define MPU3050_FS_1000DPS 0x10 -#define MPU3050_FS_2000DPS 0x18 -#define MPU3050_FS_MASK 0x18 -#define MPU3050_DLPF_CFG_256HZ_NOLPF2 0x00 -#define MPU3050_DLPF_CFG_188HZ 0x01 -#define MPU3050_DLPF_CFG_98HZ 0x02 -#define MPU3050_DLPF_CFG_42HZ 0x03 -#define MPU3050_DLPF_CFG_20HZ 0x04 -#define MPU3050_DLPF_CFG_10HZ 0x05 -#define MPU3050_DLPF_CFG_5HZ 0x06 -#define MPU3050_DLPF_CFG_2100HZ_NOLPF 0x07 -#define MPU3050_DLPF_CFG_MASK 0x07 -/* INT_CFG */ -#define MPU3050_RAW_RDY_EN 0x01 -#define MPU3050_MPU_RDY_EN 0x02 -#define MPU3050_LATCH_INT_EN 0x04 -/* PWR_MGM */ -#define MPU3050_PWR_MGM_PLL_X 0x01 -#define MPU3050_PWR_MGM_PLL_Y 0x02 -#define MPU3050_PWR_MGM_PLL_Z 0x03 -#define MPU3050_PWR_MGM_CLKSEL 0x07 -#define MPU3050_PWR_MGM_STBY_ZG 0x08 -#define MPU3050_PWR_MGM_STBY_YG 0x10 -#define MPU3050_PWR_MGM_STBY_XG 0x20 -#define MPU3050_PWR_MGM_SLEEP 0x40 -#define MPU3050_PWR_MGM_RESET 0x80 -#define MPU3050_PWR_MGM_MASK 0x40 - -struct axis_data { - s16 x; - s16 y; - s16 z; -}; - -struct mpu3050_sensor { - struct i2c_client *client; - struct device *dev; - struct input_dev *idev; -}; - -/** - * mpu3050_xyz_read_reg - read the axes values - * @buffer: provide register addr and get register - * @length: length of register - * - * Reads the register values in one transaction or returns a negative - * error code on failure. - */ -static int mpu3050_xyz_read_reg(struct i2c_client *client, - u8 *buffer, int length) -{ - /* - * Annoying we can't make this const because the i2c layer doesn't - * declare input buffers const. - */ - char cmd = MPU3050_XOUT_H; - struct i2c_msg msg[] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &cmd, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = length, - .buf = buffer, - }, - }; - - return i2c_transfer(client->adapter, msg, 2); -} - -/** - * mpu3050_read_xyz - get co-ordinates from device - * @client: i2c address of sensor - * @coords: co-ordinates to update - * - * Return the converted X Y and Z co-ordinates from the sensor device - */ -static void mpu3050_read_xyz(struct i2c_client *client, - struct axis_data *coords) -{ - u16 buffer[3]; - - mpu3050_xyz_read_reg(client, (u8 *)buffer, 6); - coords->x = be16_to_cpu(buffer[0]); - coords->y = be16_to_cpu(buffer[1]); - coords->z = be16_to_cpu(buffer[2]); - dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__, - coords->x, coords->y, coords->z); -} - -/** - * mpu3050_set_power_mode - set the power mode - * @client: i2c client for the sensor - * @val: value to switch on/off of power, 1: normal power, 0: low power - * - * Put device to normal-power mode or low-power mode. - */ -static void mpu3050_set_power_mode(struct i2c_client *client, u8 val) -{ - u8 value; - - value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM); - value = (value & ~MPU3050_PWR_MGM_MASK) | - (((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^ - MPU3050_PWR_MGM_MASK); - i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value); -} - -/** - * mpu3050_input_open - called on input event open - * @input: input dev of opened device - * - * The input layer calls this function when input event is opened. The - * function will push the device to resume. Then, the device is ready - * to provide data. - */ -static int mpu3050_input_open(struct input_dev *input) -{ - struct mpu3050_sensor *sensor = input_get_drvdata(input); - int error; - - pm_runtime_get(sensor->dev); - - /* Enable interrupts */ - error = i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG, - MPU3050_LATCH_INT_EN | - MPU3050_RAW_RDY_EN | - MPU3050_MPU_RDY_EN); - if (error < 0) { - pm_runtime_put(sensor->dev); - return error; - } - - return 0; -} - -/** - * mpu3050_input_close - called on input event close - * @input: input dev of closed device - * - * The input layer calls this function when input event is closed. The - * function will push the device to suspend. - */ -static void mpu3050_input_close(struct input_dev *input) -{ - struct mpu3050_sensor *sensor = input_get_drvdata(input); - - pm_runtime_put(sensor->dev); -} - -/** - * mpu3050_interrupt_thread - handle an IRQ - * @irq: interrupt numner - * @data: the sensor - * - * Called by the kernel single threaded after an interrupt occurs. Read - * the sensor data and generate an input event for it. - */ -static irqreturn_t mpu3050_interrupt_thread(int irq, void *data) -{ - struct mpu3050_sensor *sensor = data; - struct axis_data axis; - - mpu3050_read_xyz(sensor->client, &axis); - - input_report_abs(sensor->idev, ABS_X, axis.x); - input_report_abs(sensor->idev, ABS_Y, axis.y); - input_report_abs(sensor->idev, ABS_Z, axis.z); - input_sync(sensor->idev); - - return IRQ_HANDLED; -} - -/** - * mpu3050_hw_init - initialize hardware - * @sensor: the sensor - * - * Called during device probe; configures the sampling method. - */ -static int mpu3050_hw_init(struct mpu3050_sensor *sensor) -{ - struct i2c_client *client = sensor->client; - int ret; - u8 reg; - - /* Reset */ - ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, - MPU3050_PWR_MGM_RESET); - if (ret < 0) - return ret; - - ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM); - if (ret < 0) - return ret; - - ret &= ~MPU3050_PWR_MGM_CLKSEL; - ret |= MPU3050_PWR_MGM_PLL_Z; - ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, ret); - if (ret < 0) - return ret; - - /* Output frequency divider. The poll interval */ - ret = i2c_smbus_write_byte_data(client, MPU3050_SMPLRT_DIV, - MPU3050_DEFAULT_POLL_INTERVAL - 1); - if (ret < 0) - return ret; - - /* Set low pass filter and full scale */ - reg = MPU3050_DEFAULT_FS_RANGE; - reg |= MPU3050_DLPF_CFG_42HZ << 3; - reg |= MPU3050_EXT_SYNC_NONE << 5; - ret = i2c_smbus_write_byte_data(client, MPU3050_DLPF_FS_SYNC, reg); - if (ret < 0) - return ret; - - return 0; -} - -/** - * mpu3050_probe - device detection callback - * @client: i2c client of found device - * @id: id match information - * - * The I2C layer calls us when it believes a sensor is present at this - * address. Probe to see if this is correct and to validate the device. - * - * If present install the relevant sysfs interfaces and input device. - */ -static int mpu3050_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct mpu3050_sensor *sensor; - struct input_dev *idev; - int ret; - int error; - - sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL); - idev = input_allocate_device(); - if (!sensor || !idev) { - dev_err(&client->dev, "failed to allocate driver data\n"); - error = -ENOMEM; - goto err_free_mem; - } - - sensor->client = client; - sensor->dev = &client->dev; - sensor->idev = idev; - - mpu3050_set_power_mode(client, 1); - msleep(10); - - ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG); - if (ret < 0) { - dev_err(&client->dev, "failed to detect device\n"); - error = -ENXIO; - goto err_free_mem; - } - - if (ret != MPU3050_CHIP_ID) { - dev_err(&client->dev, "unsupported chip id\n"); - error = -ENXIO; - goto err_free_mem; - } - - idev->name = "MPU3050"; - idev->id.bustype = BUS_I2C; - idev->dev.parent = &client->dev; - - idev->open = mpu3050_input_open; - idev->close = mpu3050_input_close; - - __set_bit(EV_ABS, idev->evbit); - input_set_abs_params(idev, ABS_X, - MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0); - input_set_abs_params(idev, ABS_Y, - MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0); - input_set_abs_params(idev, ABS_Z, - MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0); - - input_set_drvdata(idev, sensor); - - pm_runtime_set_active(&client->dev); - - error = mpu3050_hw_init(sensor); - if (error) - goto err_pm_set_suspended; - - error = request_threaded_irq(client->irq, - NULL, mpu3050_interrupt_thread, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "mpu3050", sensor); - if (error) { - dev_err(&client->dev, - "can't get IRQ %d, error %d\n", client->irq, error); - goto err_pm_set_suspended; - } - - error = input_register_device(idev); - if (error) { - dev_err(&client->dev, "failed to register input device\n"); - goto err_free_irq; - } - - pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY); - i2c_set_clientdata(client, sensor); - - return 0; - -err_free_irq: - free_irq(client->irq, sensor); -err_pm_set_suspended: - pm_runtime_set_suspended(&client->dev); -err_free_mem: - input_free_device(idev); - kfree(sensor); - return error; -} - -/** - * mpu3050_remove - remove a sensor - * @client: i2c client of sensor being removed - * - * Our sensor is going away, clean up the resources. - */ -static int mpu3050_remove(struct i2c_client *client) -{ - struct mpu3050_sensor *sensor = i2c_get_clientdata(client); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - - free_irq(client->irq, sensor); - input_unregister_device(sensor->idev); - kfree(sensor); - - return 0; -} - -#ifdef CONFIG_PM -/** - * mpu3050_suspend - called on device suspend - * @dev: device being suspended - * - * Put the device into sleep mode before we suspend the machine. - */ -static int mpu3050_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - mpu3050_set_power_mode(client, 0); - - return 0; -} - -/** - * mpu3050_resume - called on device resume - * @dev: device being resumed - * - * Put the device into powered mode on resume. - */ -static int mpu3050_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - mpu3050_set_power_mode(client, 1); - msleep(100); /* wait for gyro chip resume */ - - return 0; -} -#endif - -static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL); - -static const struct i2c_device_id mpu3050_ids[] = { - { "mpu3050", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mpu3050_ids); - -static const struct of_device_id mpu3050_of_match[] = { - { .compatible = "invn,mpu3050", }, - { }, -}; -MODULE_DEVICE_TABLE(of, mpu3050_of_match); - -static struct i2c_driver mpu3050_i2c_driver = { - .driver = { - .name = "mpu3050", - .pm = &mpu3050_pm, - .of_match_table = mpu3050_of_match, - }, - .probe = mpu3050_probe, - .remove = mpu3050_remove, - .id_table = mpu3050_ids, -}; - -module_i2c_driver(mpu3050_i2c_driver); - -MODULE_AUTHOR("Wistron Corp."); -MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index e317b75357a0..18ad956454f1 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -266,7 +266,6 @@ static int pm8941_pwrkey_remove(struct platform_device *pdev) { struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev); - device_init_wakeup(&pdev->dev, 0); unregister_reboot_notifier(&pwrkey->reboot_notifier); return 0; diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index 67aab86048ad..73323b0c72c1 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -438,13 +438,6 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) return 0; } -static int pmic8xxx_pwrkey_remove(struct platform_device *pdev) -{ - device_init_wakeup(&pdev->dev, 0); - - return 0; -} - static const struct of_device_id pm8xxx_pwr_key_id_table[] = { { .compatible = "qcom,pm8058-pwrkey", .data = &pm8058_pwrkey_shutdown }, { .compatible = "qcom,pm8921-pwrkey", .data = &pm8921_pwrkey_shutdown }, @@ -454,7 +447,6 @@ MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table); static struct platform_driver pmic8xxx_pwrkey_driver = { .probe = pmic8xxx_pwrkey_probe, - .remove = pmic8xxx_pwrkey_remove, .shutdown = pmic8xxx_pwrkey_shutdown, .driver = { .name = "pm8xxx-pwrkey", diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 5f9655d49a65..e53801dbd560 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -14,6 +14,7 @@ */ #include <linux/input.h> +#include <linux/regulator/consumer.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/of.h> @@ -25,29 +26,62 @@ struct pwm_beeper { struct input_dev *input; struct pwm_device *pwm; + struct regulator *amplifier; struct work_struct work; unsigned long period; + bool suspended; + bool amplifier_on; }; #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) -static void __pwm_beeper_set(struct pwm_beeper *beeper) +static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) { - unsigned long period = beeper->period; + struct pwm_state state; + int error; + + pwm_get_state(beeper->pwm, &state); + + state.enabled = true; + state.period = period; + pwm_set_relative_duty_cycle(&state, 50, 100); + + error = pwm_apply_state(beeper->pwm, &state); + if (error) + return error; + + if (!beeper->amplifier_on) { + error = regulator_enable(beeper->amplifier); + if (error) { + pwm_disable(beeper->pwm); + return error; + } + + beeper->amplifier_on = true; + } + + return 0; +} - if (period) { - pwm_config(beeper->pwm, period / 2, period); - pwm_enable(beeper->pwm); - } else - pwm_disable(beeper->pwm); +static void pwm_beeper_off(struct pwm_beeper *beeper) +{ + if (beeper->amplifier_on) { + regulator_disable(beeper->amplifier); + beeper->amplifier_on = false; + } + + pwm_disable(beeper->pwm); } static void pwm_beeper_work(struct work_struct *work) { - struct pwm_beeper *beeper = - container_of(work, struct pwm_beeper, work); + struct pwm_beeper *beeper = container_of(work, struct pwm_beeper, work); + unsigned long period = READ_ONCE(beeper->period); - __pwm_beeper_set(beeper); + if (period) + pwm_beeper_on(beeper, period); + else + pwm_beeper_off(beeper); } static int pwm_beeper_event(struct input_dev *input, @@ -73,7 +107,8 @@ static int pwm_beeper_event(struct input_dev *input, else beeper->period = HZ_TO_NANOSECONDS(value); - schedule_work(&beeper->work); + if (!beeper->suspended) + schedule_work(&beeper->work); return 0; } @@ -81,9 +116,7 @@ static int pwm_beeper_event(struct input_dev *input, static void pwm_beeper_stop(struct pwm_beeper *beeper) { cancel_work_sync(&beeper->work); - - if (beeper->period) - pwm_disable(beeper->pwm); + pwm_beeper_off(beeper); } static void pwm_beeper_close(struct input_dev *input) @@ -95,41 +128,50 @@ static void pwm_beeper_close(struct input_dev *input) static int pwm_beeper_probe(struct platform_device *pdev) { - unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; struct pwm_beeper *beeper; + struct pwm_state state; int error; - beeper = kzalloc(sizeof(*beeper), GFP_KERNEL); + beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL); if (!beeper) return -ENOMEM; - beeper->pwm = pwm_get(&pdev->dev, NULL); + beeper->pwm = devm_pwm_get(dev, NULL); if (IS_ERR(beeper->pwm)) { - dev_dbg(&pdev->dev, "unable to request PWM, trying legacy API\n"); - beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + error = PTR_ERR(beeper->pwm); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to request PWM device: %d\n", + error); + return error; } - if (IS_ERR(beeper->pwm)) { - error = PTR_ERR(beeper->pwm); - dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error); - goto err_free; + /* Sync up PWM state and ensure it is off. */ + pwm_init_state(beeper->pwm, &state); + state.enabled = false; + error = pwm_apply_state(beeper->pwm, &state); + if (error) { + dev_err(dev, "failed to apply initial PWM state: %d\n", + error); + return error; } - /* - * FIXME: pwm_apply_args() should be removed when switching to - * the atomic PWM API. - */ - pwm_apply_args(beeper->pwm); + beeper->amplifier = devm_regulator_get(dev, "amp"); + if (IS_ERR(beeper->amplifier)) { + error = PTR_ERR(beeper->amplifier); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'amp' regulator: %d\n", + error); + return error; + } INIT_WORK(&beeper->work, pwm_beeper_work); - beeper->input = input_allocate_device(); + beeper->input = devm_input_allocate_device(dev); if (!beeper->input) { - dev_err(&pdev->dev, "Failed to allocate input device\n"); - error = -ENOMEM; - goto err_pwm_free; + dev_err(dev, "Failed to allocate input device\n"); + return -ENOMEM; } - beeper->input->dev.parent = &pdev->dev; beeper->input->name = "pwm-beeper"; beeper->input->phys = "pwm/input0"; @@ -138,8 +180,8 @@ static int pwm_beeper_probe(struct platform_device *pdev) beeper->input->id.product = 0x0001; beeper->input->id.version = 0x0100; - beeper->input->evbit[0] = BIT(EV_SND); - beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); + input_set_capability(beeper->input, EV_SND, SND_TONE); + input_set_capability(beeper->input, EV_SND, SND_BELL); beeper->input->event = pwm_beeper_event; beeper->input->close = pwm_beeper_close; @@ -148,41 +190,28 @@ static int pwm_beeper_probe(struct platform_device *pdev) error = input_register_device(beeper->input); if (error) { - dev_err(&pdev->dev, "Failed to register input device: %d\n", error); - goto err_input_free; + dev_err(dev, "Failed to register input device: %d\n", error); + return error; } platform_set_drvdata(pdev, beeper); return 0; - -err_input_free: - input_free_device(beeper->input); -err_pwm_free: - pwm_free(beeper->pwm); -err_free: - kfree(beeper); - - return error; -} - -static int pwm_beeper_remove(struct platform_device *pdev) -{ - struct pwm_beeper *beeper = platform_get_drvdata(pdev); - - input_unregister_device(beeper->input); - - pwm_free(beeper->pwm); - - kfree(beeper); - - return 0; } static int __maybe_unused pwm_beeper_suspend(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); + /* + * Spinlock is taken here is not to protect write to + * beeper->suspended, but to ensure that pwm_beeper_event + * does not re-submit work once flag is set. + */ + spin_lock_irq(&beeper->input->event_lock); + beeper->suspended = true; + spin_unlock_irq(&beeper->input->event_lock); + pwm_beeper_stop(beeper); return 0; @@ -192,8 +221,12 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); - if (beeper->period) - __pwm_beeper_set(beeper); + spin_lock_irq(&beeper->input->event_lock); + beeper->suspended = false; + spin_unlock_irq(&beeper->input->event_lock); + + /* Let worker figure out if we should resume beeping */ + schedule_work(&beeper->work); return 0; } @@ -211,7 +244,6 @@ MODULE_DEVICE_TABLE(of, pwm_beeper_match); static struct platform_driver pwm_beeper_driver = { .probe = pwm_beeper_probe, - .remove = pwm_beeper_remove, .driver = { .name = "pwm-beeper", .pm = &pwm_beeper_pm_ops, diff --git a/drivers/input/misc/retu-pwrbutton.c b/drivers/input/misc/retu-pwrbutton.c index 30b459b6b344..64023ac08e2b 100644 --- a/drivers/input/misc/retu-pwrbutton.c +++ b/drivers/input/misc/retu-pwrbutton.c @@ -76,14 +76,8 @@ static int retu_pwrbutton_probe(struct platform_device *pdev) return 0; } -static int retu_pwrbutton_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver retu_pwrbutton_driver = { .probe = retu_pwrbutton_probe, - .remove = retu_pwrbutton_remove, .driver = { .name = "retu-pwrbutton", }, diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index ed7237f19539..4fd038d476a3 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -172,13 +172,6 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) return 0; } -static int sirfsoc_pwrc_remove(struct platform_device *pdev) -{ - device_init_wakeup(&pdev->dev, 0); - - return 0; -} - static int __maybe_unused sirfsoc_pwrc_resume(struct device *dev) { struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev); @@ -200,7 +193,6 @@ static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume); static struct platform_driver sirfsoc_pwrc_driver = { .probe = sirfsoc_pwrc_probe, - .remove = sirfsoc_pwrc_remove, .driver = { .name = "sirfsoc-pwrc", .pm = &sirfsoc_pwrc_pm_ops, diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 908b51089dee..ddb2f22fca7a 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -102,6 +102,8 @@ soc_button_device_create(struct platform_device *pdev, gpio_keys[n_buttons].active_low = 1; gpio_keys[n_buttons].desc = info->name; gpio_keys[n_buttons].wakeup = info->wakeup; + /* These devices often use cheap buttons, use 50 ms debounce */ + gpio_keys[n_buttons].debounce_interval = 50; n_buttons++; } @@ -167,12 +169,12 @@ static int soc_button_probe(struct platform_device *pdev) button_info = (struct soc_button_info *)id->driver_data; - if (gpiod_count(&pdev->dev, KBUILD_MODNAME) <= 0) { - dev_dbg(&pdev->dev, "no GPIO attached, ignoring...\n"); + if (gpiod_count(dev, KBUILD_MODNAME) <= 0) { + dev_dbg(dev, "no GPIO attached, ignoring...\n"); return -ENODEV; } - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index cc74a41bdb0d..a4455bb12ae0 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -95,7 +95,7 @@ static int tps6521x_pb_probe(struct platform_device *pdev) int error; int irq; - match = of_match_node(of_tps6521x_pb_match, pdev->dev.of_node); + match = of_match_node(of_tps6521x_pb_match, dev->of_node); if (!match) return -ENXIO; @@ -118,10 +118,9 @@ static int tps6521x_pb_probe(struct platform_device *pdev) input_set_capability(idev, EV_KEY, KEY_POWER); - pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL); + pwr->regmap = dev_get_regmap(dev->parent, NULL); pwr->dev = dev; pwr->idev = idev; - platform_set_drvdata(pdev, pwr); device_init_wakeup(dev, true); irq = platform_get_irq(pdev, 0); @@ -136,8 +135,7 @@ static int tps6521x_pb_probe(struct platform_device *pdev) IRQF_ONESHOT, pwr->data->name, pwr); if (error) { - dev_err(dev, "failed to request IRQ #%d: %d\n", - irq, error); + dev_err(dev, "failed to request IRQ #%d: %d\n", irq, error); return error; } diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index 603fc2fadf05..54162d2cbcfc 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -85,7 +85,6 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev) return err; } - platform_set_drvdata(pdev, pwr); device_init_wakeup(&pdev->dev, true); return 0; diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 328edc8c8786..72b28ebfe360 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1855,7 +1855,7 @@ static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) * Switch mouse to poll (remote) mode so motion data will not * get in our way */ - return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL); } static int alps_monitor_mode_send_word(struct psmouse *psmouse, u16 word) diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 30e3442518f8..d0122134f320 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -665,7 +665,7 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) char *data; /* Type 3 does not require a mode switch */ - if (dev->cfg.tp_type == TYPE3) + if (c->tp_type == TYPE3) return 0; data = kmalloc(c->um_size, GFP_KERNEL); diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index dc2394292088..fd8865c65caf 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -832,8 +832,8 @@ static int cyapa_prepare_wakeup_controls(struct cyapa *cyapa) int error; if (device_can_wakeup(dev)) { - error = sysfs_merge_group(&client->dev.kobj, - &cyapa_power_wakeup_group); + error = sysfs_merge_group(&dev->kobj, + &cyapa_power_wakeup_group); if (error) { dev_err(dev, "failed to add power wakeup group: %d\n", error); @@ -1312,7 +1312,7 @@ static int cyapa_probe(struct i2c_client *client, return error; } - error = sysfs_create_group(&client->dev.kobj, &cyapa_sysfs_group); + error = sysfs_create_group(&dev->kobj, &cyapa_sysfs_group); if (error) { dev_err(dev, "failed to create sysfs entries: %d\n", error); return error; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index f9600753eca5..1cbfa4a6e830 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -562,7 +562,7 @@ static int cyapa_gen3_bl_exit(struct cyapa *cyapa) * Wait for bootloader to exit, and operation mode to start. * Normally, this takes at least 50 ms. */ - usleep_range(50000, 100000); + msleep(50); /* * In addition, when a device boots for the first time after being * updated to new firmware, it must first calibrate its sensors, which @@ -789,7 +789,7 @@ static ssize_t cyapa_gen3_do_calibrate(struct device *dev, const char *buf, size_t count) { struct cyapa *cyapa = dev_get_drvdata(dev); - int tries; + unsigned long timeout; int ret; ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS); @@ -812,31 +812,28 @@ static ssize_t cyapa_gen3_do_calibrate(struct device *dev, goto out; } - tries = 20; /* max recalibration timeout 2s. */ + /* max recalibration timeout 2s. */ + timeout = jiffies + 2 * HZ; do { /* * For this recalibration, the max time will not exceed 2s. * The average time is approximately 500 - 700 ms, and we * will check the status every 100 - 200ms. */ - usleep_range(100000, 200000); - + msleep(100); ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS); if (ret < 0) { - dev_err(dev, "Error reading dev status: %d\n", - ret); + dev_err(dev, "Error reading dev status: %d\n", ret); goto out; } - if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL) - break; - } while (--tries); + if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL) { + dev_dbg(dev, "Calibration successful.\n"); + goto out; + } + } while (time_is_after_jiffies(timeout)); - if (tries == 0) { - dev_err(dev, "Failed to calibrate. Timeout.\n"); - ret = -ETIMEDOUT; - goto out; - } - dev_dbg(dev, "Calibration successful.\n"); + dev_err(dev, "Failed to calibrate. Timeout.\n"); + ret = -ETIMEDOUT; out: return ret < 0 ? ret : count; diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c index 28dcfc822bf6..21bad3e75fee 100644 --- a/drivers/input/mouse/cypress_ps2.c +++ b/drivers/input/mouse/cypress_ps2.c @@ -107,7 +107,7 @@ static int cypress_ps2_read_cmd_status(struct psmouse *psmouse, enum psmouse_state old_state; int pktsize; - ps2_begin_command(&psmouse->ps2dev); + ps2_begin_command(ps2dev); old_state = psmouse->state; psmouse->state = PSMOUSE_CMD_MODE; @@ -133,7 +133,7 @@ out: psmouse->state = old_state; psmouse->pktcnt = 0; - ps2_end_command(&psmouse->ps2dev); + ps2_end_command(ps2dev); return rc; } @@ -414,8 +414,6 @@ static int cypress_set_input_params(struct input_dev *input, __set_bit(BTN_RIGHT, input->keybit); __set_bit(BTN_MIDDLE, input->keybit); - input_set_drvdata(input, cytp); - return 0; } diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 1e1d0ad406f2..352050e9031d 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1041,8 +1041,7 @@ static int elan_probe(struct i2c_client *client, return -EIO; } - data = devm_kzalloc(&client->dev, sizeof(struct elan_tp_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct elan_tp_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1053,29 +1052,25 @@ static int elan_probe(struct i2c_client *client, init_completion(&data->fw_completion); mutex_init(&data->sysfs_mutex); - data->vcc = devm_regulator_get(&client->dev, "vcc"); + data->vcc = devm_regulator_get(dev, "vcc"); if (IS_ERR(data->vcc)) { error = PTR_ERR(data->vcc); if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get 'vcc' regulator: %d\n", + dev_err(dev, "Failed to get 'vcc' regulator: %d\n", error); return error; } error = regulator_enable(data->vcc); if (error) { - dev_err(&client->dev, - "Failed to enable regulator: %d\n", error); + dev_err(dev, "Failed to enable regulator: %d\n", error); return error; } - error = devm_add_action(&client->dev, - elan_disable_regulator, data); + error = devm_add_action(dev, elan_disable_regulator, data); if (error) { regulator_disable(data->vcc); - dev_err(&client->dev, - "Failed to add disable regulator action: %d\n", + dev_err(dev, "Failed to add disable regulator action: %d\n", error); return error; } @@ -1093,14 +1088,14 @@ static int elan_probe(struct i2c_client *client, if (error) return error; - dev_info(&client->dev, + dev_info(dev, "Elan Touchpad: Module ID: 0x%04x, Firmware: 0x%04x, Sample: 0x%04x, IAP: 0x%04x\n", data->product_id, data->fw_version, data->sm_version, data->iap_version); - dev_dbg(&client->dev, + dev_dbg(dev, "Elan Touchpad Extra Information:\n" " Max ABS X,Y: %d,%d\n" " Width X,Y: %d,%d\n" @@ -1118,38 +1113,33 @@ static int elan_probe(struct i2c_client *client, * Systems using device tree should set up interrupt via DTS, * the rest will use the default falling edge interrupts. */ - irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING; + irqflags = dev->of_node ? 0 : IRQF_TRIGGER_FALLING; - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, elan_isr, + error = devm_request_threaded_irq(dev, client->irq, NULL, elan_isr, irqflags | IRQF_ONESHOT, client->name, data); if (error) { - dev_err(&client->dev, "cannot register irq=%d\n", client->irq); + dev_err(dev, "cannot register irq=%d\n", client->irq); return error; } - error = sysfs_create_groups(&client->dev.kobj, elan_sysfs_groups); + error = sysfs_create_groups(&dev->kobj, elan_sysfs_groups); if (error) { - dev_err(&client->dev, "failed to create sysfs attributes: %d\n", - error); + dev_err(dev, "failed to create sysfs attributes: %d\n", error); return error; } - error = devm_add_action(&client->dev, - elan_remove_sysfs_groups, data); + error = devm_add_action(dev, elan_remove_sysfs_groups, data); if (error) { elan_remove_sysfs_groups(data); - dev_err(&client->dev, - "Failed to add sysfs cleanup action: %d\n", + dev_err(dev, "Failed to add sysfs cleanup action: %d\n", error); return error; } error = input_register_device(data->input); if (error) { - dev_err(&client->dev, "failed to register input device: %d\n", - error); + dev_err(dev, "failed to register input device: %d\n", error); return error; } @@ -1157,8 +1147,8 @@ static int elan_probe(struct i2c_client *client, * Systems using device tree should set up wakeup via DTS, * the rest will configure device as wakeup source by default. */ - if (!client->dev.of_node) - device_init_wakeup(&client->dev, true); + if (!dev->of_node) + device_init_wakeup(dev, true); return 0; } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index db7d1d666ac1..efc8ec342351 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1412,7 +1412,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[3]; - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 62be888e83d0..015509e0b140 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -713,8 +713,7 @@ static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) * the upper bound. (in practice, it takes about 3 loops.) */ for (timeo = 20; timeo > 0; timeo--) { - if (!ps2_sendbyte(&psmouse->ps2dev, - PSMOUSE_CMD_DISABLE, 20)) + if (!ps2_sendbyte(ps2dev, PSMOUSE_CMD_DISABLE, 20)) break; msleep(25); } @@ -740,7 +739,7 @@ static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) psmouse_set_state(psmouse, PSMOUSE_IGNORE); /* probably won't see an ACK, the touchpad will be off */ - ps2_sendbyte(&psmouse->ps2dev, 0xec, 20); + ps2_sendbyte(ps2dev, 0xec, 20); } return 0; diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 422da1cd9e76..ef9c97f5e3d7 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -402,7 +402,7 @@ int ps2pp_detect(struct psmouse *psmouse, bool set_properties) psmouse->set_resolution = ps2pp_set_resolution; psmouse->disconnect = ps2pp_disconnect; - error = device_create_file(&psmouse->ps2dev.serio->dev, + error = device_create_file(&ps2dev->serio->dev, &psmouse_attr_smartscroll.dattr); if (error) { psmouse_err(psmouse, diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c index 0a60717b91c6..25f0ecb90126 100644 --- a/drivers/input/mouse/maplemouse.c +++ b/drivers/input/mouse/maplemouse.c @@ -87,7 +87,6 @@ static int probe_maple_mouse(struct device *dev) mse->dev = input_dev; mse->mdev = mdev; - input_set_drvdata(input_dev, mse); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index bee267424972..a598b7223cef 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -127,6 +127,13 @@ struct psmouse_protocol { int (*init)(struct psmouse *); }; +static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) +{ + input_report_key(dev, BTN_LEFT, buttons & BIT(0)); + input_report_key(dev, BTN_MIDDLE, buttons & BIT(2)); + input_report_key(dev, BTN_RIGHT, buttons & BIT(1)); +} + /* * psmouse_process_byte() analyzes the PS/2 data stream and reports * relevant events to the input module once full packet has arrived. @@ -199,9 +206,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) } /* Generic PS/2 Mouse */ - input_report_key(dev, BTN_LEFT, packet[0] & 1); - input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); - input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); + psmouse_report_standard_buttons(dev, + packet[0] | psmouse->extra_buttons); input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); @@ -282,6 +288,30 @@ static int psmouse_handle_byte(struct psmouse *psmouse) return 0; } +static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data) +{ + switch (psmouse->oob_data_type) { + case PSMOUSE_OOB_NONE: + psmouse->oob_data_type = data; + break; + + case PSMOUSE_OOB_EXTRA_BTNS: + psmouse_report_standard_buttons(psmouse->dev, data); + input_sync(psmouse->dev); + + psmouse->extra_buttons = data; + psmouse->oob_data_type = PSMOUSE_OOB_NONE; + break; + + default: + psmouse_warn(psmouse, + "unknown OOB_DATA type: 0x%02x\n", + psmouse->oob_data_type); + psmouse->oob_data_type = PSMOUSE_OOB_NONE; + break; + } +} + /* * psmouse_interrupt() handles incoming characters, either passing them * for normal processing or gathering them as command response. @@ -306,6 +336,11 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, goto out; } + if (flags & SERIO_OOB_DATA) { + psmouse_handle_oob_data(psmouse, data); + goto out; + } + if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK)) if (ps2_handle_ack(&psmouse->ps2dev, data)) goto out; diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index e0ca6cda3d16..8c83b8e2505c 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -1,6 +1,9 @@ #ifndef _PSMOUSE_H #define _PSMOUSE_H +#define PSMOUSE_OOB_NONE 0x00 +#define PSMOUSE_OOB_EXTRA_BTNS 0x01 + #define PSMOUSE_CMD_SETSCALE11 0x00e6 #define PSMOUSE_CMD_SETSCALE21 0x00e7 #define PSMOUSE_CMD_SETRES 0x10e8 @@ -53,6 +56,8 @@ struct psmouse { unsigned char pktcnt; unsigned char pktsize; unsigned char type; + unsigned char oob_data_type; + unsigned char extra_buttons; bool ignore_parity; bool acks_disable_command; unsigned int model; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index a41d8328c064..597ee4b01d9f 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -597,15 +597,13 @@ static int synaptics_is_pt_packet(unsigned char *buf) return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; } -static void synaptics_pass_pt_packet(struct psmouse *psmouse, - struct serio *ptport, +static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) { - struct synaptics_data *priv = psmouse->private; struct psmouse *child = serio_get_drvdata(ptport); if (child && child->state == PSMOUSE_ACTIVATED) { - serio_interrupt(ptport, packet[1] | priv->pt_buttons, 0); + serio_interrupt(ptport, packet[1], 0); serio_interrupt(ptport, packet[4], 0); serio_interrupt(ptport, packet[5], 0); if (child->pktsize == 4) @@ -856,7 +854,6 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1; - char buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; int i; if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)) @@ -883,15 +880,18 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse, * physically wired to the touchpad. Re-route them through * the pass-through interface. */ - if (!priv->pt_port) - return; + if (priv->pt_port) { + u8 pt_buttons; - /* The trackstick expects at most 3 buttons */ - priv->pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) | - SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 | - SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2; + /* The trackstick expects at most 3 buttons */ + pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) | + SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 | + SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2; - synaptics_pass_pt_packet(psmouse, priv->pt_port, buf); + serio_interrupt(priv->pt_port, + PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA); + serio_interrupt(priv->pt_port, pt_buttons, SERIO_OOB_DATA); + } } static void synaptics_report_buttons(struct psmouse *psmouse, @@ -1132,7 +1132,7 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { if (priv->pt_port) - synaptics_pass_pt_packet(psmouse, priv->pt_port, + synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); } else synaptics_process_packet(psmouse); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 56faa7ec4434..116ae2546ace 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -183,7 +183,6 @@ struct synaptics_data { bool disable_gesture; /* disable gestures */ struct serio *pt_port; /* Pass-through serio port */ - unsigned char pt_buttons; /* Pass-through buttons */ /* * Last received Advanced Gesture Mode (AGM) packet. An AGM packet diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 7331084973e1..922ea02edcc3 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -379,7 +379,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) if (!set_properties) return 0; - if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) { + if (trackpoint_read(ps2dev, TP_EXT_BTN, &button_info)) { psmouse_warn(psmouse, "failed to get extended button data\n"); button_info = 0; } @@ -402,7 +402,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) trackpoint_defaults(psmouse->private); - error = trackpoint_power_on_reset(&psmouse->ps2dev); + error = trackpoint_power_on_reset(ps2dev); /* Write defaults to TP only if reset fails. */ if (error) diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index bb7762bf2879..7172b88cd064 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -9,9 +9,11 @@ config RMI4_CORE If unsure, say Y. +if RMI4_CORE + config RMI4_I2C tristate "RMI4 I2C Support" - depends on RMI4_CORE && I2C + depends on I2C help Say Y here if you want to support RMI4 devices connected to an I2C bus. @@ -20,7 +22,7 @@ config RMI4_I2C config RMI4_SPI tristate "RMI4 SPI Support" - depends on RMI4_CORE && SPI + depends on SPI help Say Y here if you want to support RMI4 devices connected to a SPI bus. @@ -29,7 +31,7 @@ config RMI4_SPI config RMI4_SMB tristate "RMI4 SMB Support" - depends on RMI4_CORE && I2C + depends on I2C help Say Y here if you want to support RMI4 devices connected to an SMB bus. @@ -40,13 +42,13 @@ config RMI4_SMB called rmi_smbus. config RMI4_F03 - bool "RMI4 Function 03 (PS2 Guest)" + bool "RMI4 Function 03 (PS2 Guest)" depends on RMI4_CORE - help - Say Y here if you want to add support for RMI4 function 03. + help + Say Y here if you want to add support for RMI4 function 03. - Function 03 provides PS2 guest support for RMI4 devices. This - includes support for TrackPoints on TouchPads. + Function 03 provides PS2 guest support for RMI4 devices. This + includes support for TrackPoints on TouchPads. config RMI4_F03_SERIO tristate @@ -57,12 +59,10 @@ config RMI4_F03_SERIO config RMI4_2D_SENSOR bool - depends on RMI4_CORE config RMI4_F11 bool "RMI4 Function 11 (2D pointing)" select RMI4_2D_SENSOR - depends on RMI4_CORE help Say Y here if you want to add support for RMI4 function 11. @@ -73,7 +73,6 @@ config RMI4_F11 config RMI4_F12 bool "RMI4 Function 12 (2D pointing)" select RMI4_2D_SENSOR - depends on RMI4_CORE help Say Y here if you want to add support for RMI4 function 12. @@ -83,7 +82,6 @@ config RMI4_F12 config RMI4_F30 bool "RMI4 Function 30 (GPIO LED)" - depends on RMI4_CORE help Say Y here if you want to add support for RMI4 function 30. @@ -92,7 +90,6 @@ config RMI4_F30 config RMI4_F34 bool "RMI4 Function 34 (Device reflash)" - depends on RMI4_CORE select FW_LOADER help Say Y here if you want to add support for RMI4 function 34. @@ -103,7 +100,6 @@ config RMI4_F34 config RMI4_F54 bool "RMI4 Function 54 (Analog diagnostics)" - depends on RMI4_CORE depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m) select VIDEOBUF2_VMALLOC select RMI4_F55 @@ -115,9 +111,10 @@ config RMI4_F54 config RMI4_F55 bool "RMI4 Function 55 (Sensor tuning)" - depends on RMI4_CORE help Say Y here if you want to add support for RMI4 function 55 Function 55 provides access to the RMI4 touch sensor tuning mechanism. + +endif # RMI_CORE diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index 07007ff8e29f..8bb866c7b985 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -144,8 +144,13 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) int input_flags = 0; if (sensor->report_abs) { - if (sensor->axis_align.swap_axes) + if (sensor->axis_align.swap_axes) { swap(sensor->max_x, sensor->max_y); + swap(sensor->axis_align.clip_x_low, + sensor->axis_align.clip_y_low); + swap(sensor->axis_align.clip_x_high, + sensor->axis_align.clip_y_high); + } sensor->min_x = sensor->axis_align.clip_x_low; if (sensor->axis_align.clip_x_high) diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 1c40d94ca506..ae1bffe45c75 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -55,7 +55,7 @@ static void rmi_release_device(struct device *dev) kfree(rmi_dev); } -static struct device_type rmi_device_type = { +static const struct device_type rmi_device_type = { .name = "rmi4_sensor", .release = rmi_release_device, }; @@ -134,7 +134,7 @@ static void rmi_release_function(struct device *dev) kfree(fn); } -static struct device_type rmi_function_type = { +static const struct device_type rmi_function_type = { .name = "rmi4_function", .release = rmi_release_function, }; @@ -261,10 +261,10 @@ int __rmi_register_function_handler(struct rmi_function_handler *handler, driver->probe = rmi_function_probe; driver->remove = rmi_function_remove; - error = driver_register(&handler->driver); + error = driver_register(driver); if (error) { pr_err("driver_register() failed for %s, error: %d\n", - handler->driver.name, error); + driver->name, error); return error; } diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index bf5c36e229ba..d64fc92858f2 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -265,6 +265,19 @@ static int rmi_irq_init(struct rmi_device *rmi_dev) return 0; } +struct rmi_function *rmi_find_function(struct rmi_device *rmi_dev, u8 number) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + + list_for_each_entry(entry, &data->function_list, node) { + if (entry->fd.function_number == number) + return entry; + } + + return NULL; +} + static int suspend_one_function(struct rmi_function *fn) { struct rmi_function_handler *fh; @@ -364,7 +377,7 @@ static void rmi_driver_set_input_name(struct rmi_device *rmi_dev, struct input_dev *input) { struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); - char *device_name = rmi_f01_get_product_ID(data->f01_container); + const char *device_name = rmi_f01_get_product_ID(data->f01_container); char *name; name = devm_kasprintf(&rmi_dev->dev, GFP_KERNEL, @@ -836,7 +849,7 @@ static int rmi_create_function(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt) { struct device *dev = &rmi_dev->dev; - struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_driver_data *data = dev_get_drvdata(dev); int *current_irq_count = ctx; struct rmi_function *fn; int i; @@ -1040,7 +1053,7 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) } if (data->bootloader_mode) - dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n"); + dev_warn(dev, "Device in bootloader mode.\n"); data->irq_count = irq_count; data->num_of_irq_regs = (data->irq_count + 7) / 8; @@ -1049,7 +1062,7 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) data->irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); if (!data->irq_memory) { dev_err(dev, "Failed to allocate memory for irq masks.\n"); - return retval; + return -ENOMEM; } data->irq_status = data->irq_memory + size * 0; diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 24f8f764d171..f1a2a2266022 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -93,6 +93,7 @@ bool rmi_is_physical_driver(struct device_driver *); int rmi_register_physical_driver(void); void rmi_unregister_physical_driver(void); void rmi_free_function_list(struct rmi_device *rmi_dev); +struct rmi_function *rmi_find_function(struct rmi_device *rmi_dev, u8 number); int rmi_enable_sensor(struct rmi_device *rmi_dev); int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, int (*callback)(struct rmi_device *rmi_dev, void *ctx, @@ -104,7 +105,20 @@ int rmi_init_functions(struct rmi_driver_data *data); int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx, const struct pdt_entry *pdt); -char *rmi_f01_get_product_ID(struct rmi_function *fn); +const char *rmi_f01_get_product_ID(struct rmi_function *fn); + +#ifdef CONFIG_RMI4_F03 +int rmi_f03_overwrite_button(struct rmi_function *fn, unsigned int button, + int value); +void rmi_f03_commit_buttons(struct rmi_function *fn); +#else +static inline int rmi_f03_overwrite_button(struct rmi_function *fn, + unsigned int button, int value) +{ + return 0; +} +static inline void rmi_f03_commit_buttons(struct rmi_function *fn) {} +#endif #ifdef CONFIG_RMI4_F34 int rmi_f34_create_sysfs(struct rmi_device *rmi_dev); diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index 18baf4ceb940..7f7e9176f7ea 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/of.h> +#include <asm/unaligned.h> #include "rmi_driver.h" #define RMI_PRODUCT_ID_LENGTH 10 @@ -54,6 +55,7 @@ struct f01_basic_properties { u8 product_id[RMI_PRODUCT_ID_LENGTH + 1]; u16 productinfo; u32 firmware_id; + u32 package_id; }; /* F01 device status bits */ @@ -220,8 +222,19 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev, has_build_id_query = !!(queries[0] & BIT(1)); } - if (has_package_id_query) + if (has_package_id_query) { + ret = rmi_read_block(rmi_dev, prod_info_addr, + queries, sizeof(__le64)); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read package info: %d\n", + ret); + return ret; + } + + props->package_id = get_unaligned_le64(queries); prod_info_addr++; + } if (has_build_id_query) { ret = rmi_read_block(rmi_dev, prod_info_addr, queries, @@ -241,13 +254,90 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev, return 0; } -char *rmi_f01_get_product_ID(struct rmi_function *fn) +const char *rmi_f01_get_product_ID(struct rmi_function *fn) { struct f01_data *f01 = dev_get_drvdata(&fn->dev); return f01->properties.product_id; } +static ssize_t rmi_driver_manufacturer_id_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + f01->properties.manufacturer_id); +} + +static DEVICE_ATTR(manufacturer_id, 0444, + rmi_driver_manufacturer_id_show, NULL); + +static ssize_t rmi_driver_dom_show(struct device *dev, + struct device_attribute *dattr, char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", f01->properties.dom); +} + +static DEVICE_ATTR(date_of_manufacture, 0444, rmi_driver_dom_show, NULL); + +static ssize_t rmi_driver_product_id_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", f01->properties.product_id); +} + +static DEVICE_ATTR(product_id, 0444, rmi_driver_product_id_show, NULL); + +static ssize_t rmi_driver_firmware_id_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", f01->properties.firmware_id); +} + +static DEVICE_ATTR(firmware_id, 0444, rmi_driver_firmware_id_show, NULL); + +static ssize_t rmi_driver_package_id_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + struct f01_data *f01 = dev_get_drvdata(&data->f01_container->dev); + + u32 package_id = f01->properties.package_id; + + return scnprintf(buf, PAGE_SIZE, "%04x.%04x\n", + package_id & 0xffff, (package_id >> 16) & 0xffff); +} + +static DEVICE_ATTR(package_id, 0444, rmi_driver_package_id_show, NULL); + +static struct attribute *rmi_f01_attrs[] = { + &dev_attr_manufacturer_id.attr, + &dev_attr_date_of_manufacture.attr, + &dev_attr_product_id.attr, + &dev_attr_firmware_id.attr, + &dev_attr_package_id.attr, + NULL +}; + +static struct attribute_group rmi_f01_attr_group = { + .attrs = rmi_f01_attrs, +}; + #ifdef CONFIG_OF static int rmi_f01_of_probe(struct device *dev, struct rmi_device_platform_data *pdata) @@ -480,9 +570,18 @@ static int rmi_f01_probe(struct rmi_function *fn) dev_set_drvdata(&fn->dev, f01); + error = sysfs_create_group(&fn->rmi_dev->dev.kobj, &rmi_f01_attr_group); + if (error) + dev_warn(&fn->dev, "Failed to create sysfs group: %d\n", error); + return 0; } +static void rmi_f01_remove(struct rmi_function *fn) +{ + sysfs_remove_group(&fn->rmi_dev->dev.kobj, &rmi_f01_attr_group); +} + static int rmi_f01_config(struct rmi_function *fn) { struct f01_data *f01 = dev_get_drvdata(&fn->dev); @@ -622,6 +721,7 @@ struct rmi_function_handler rmi_f01_handler = { }, .func = 0x01, .probe = rmi_f01_probe, + .remove = rmi_f01_remove, .config = rmi_f01_config, .attention = rmi_f01_attention, .suspend = rmi_f01_suspend, diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c index 8a7ca3e2f95e..77dad045a468 100644 --- a/drivers/input/rmi4/rmi_f03.c +++ b/drivers/input/rmi4/rmi_f03.c @@ -26,15 +26,53 @@ #define RMI_F03_BYTES_PER_DEVICE_SHIFT 4 #define RMI_F03_QUEUE_LENGTH 0x0F +#define PSMOUSE_OOB_EXTRA_BTNS 0x01 + struct f03_data { struct rmi_function *fn; struct serio *serio; + unsigned int overwrite_buttons; + u8 device_count; u8 rx_queue_length; }; +int rmi_f03_overwrite_button(struct rmi_function *fn, unsigned int button, + int value) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + unsigned int bit; + + if (button < BTN_LEFT || button > BTN_MIDDLE) + return -EINVAL; + + bit = BIT(button - BTN_LEFT); + + if (value) + f03->overwrite_buttons |= bit; + else + f03->overwrite_buttons &= ~bit; + + return 0; +} + +void rmi_f03_commit_buttons(struct rmi_function *fn) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + struct serio *serio = f03->serio; + + serio_pause_rx(serio); + if (serio->drv) { + serio->drv->interrupt(serio, PSMOUSE_OOB_EXTRA_BTNS, + SERIO_OOB_DATA); + serio->drv->interrupt(serio, f03->overwrite_buttons, + SERIO_OOB_DATA); + } + serio_continue_rx(serio); +} + static int rmi_f03_pt_write(struct serio *id, unsigned char val) { struct f03_data *f03 = id->port_data; @@ -175,9 +213,6 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) int i; int error; - if (!rmi_dev) - return -ENODEV; - if (drvdata->attn_data.data) { /* First grab the data passed by the transport device */ if (drvdata->attn_data.size < ob_len) { diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index f4b491e3e0fd..198678613382 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -16,30 +16,24 @@ /* Defs for Query 0 */ #define RMI_F30_EXTENDED_PATTERNS 0x01 -#define RMI_F30_HAS_MAPPABLE_BUTTONS (1 << 1) -#define RMI_F30_HAS_LED (1 << 2) -#define RMI_F30_HAS_GPIO (1 << 3) -#define RMI_F30_HAS_HAPTIC (1 << 4) -#define RMI_F30_HAS_GPIO_DRV_CTL (1 << 5) -#define RMI_F30_HAS_MECH_MOUSE_BTNS (1 << 6) +#define RMI_F30_HAS_MAPPABLE_BUTTONS BIT(1) +#define RMI_F30_HAS_LED BIT(2) +#define RMI_F30_HAS_GPIO BIT(3) +#define RMI_F30_HAS_HAPTIC BIT(4) +#define RMI_F30_HAS_GPIO_DRV_CTL BIT(5) +#define RMI_F30_HAS_MECH_MOUSE_BTNS BIT(6) /* Defs for Query 1 */ #define RMI_F30_GPIO_LED_COUNT 0x1F /* Defs for Control Registers */ #define RMI_F30_CTRL_1_GPIO_DEBOUNCE 0x01 -#define RMI_F30_CTRL_1_HALT (1 << 4) -#define RMI_F30_CTRL_1_HALTED (1 << 5) +#define RMI_F30_CTRL_1_HALT BIT(4) +#define RMI_F30_CTRL_1_HALTED BIT(5) #define RMI_F30_CTRL_10_NUM_MECH_MOUSE_BTNS 0x03 -struct rmi_f30_ctrl_data { - int address; - int length; - u8 *regs; -}; - #define RMI_F30_CTRL_MAX_REGS 32 -#define RMI_F30_CTRL_MAX_BYTES ((RMI_F30_CTRL_MAX_REGS + 7) >> 3) +#define RMI_F30_CTRL_MAX_BYTES DIV_ROUND_UP(RMI_F30_CTRL_MAX_REGS, 8) #define RMI_F30_CTRL_MAX_REG_BLOCKS 11 #define RMI_F30_CTRL_REGS_MAX_SIZE (RMI_F30_CTRL_MAX_BYTES \ @@ -54,6 +48,15 @@ struct rmi_f30_ctrl_data { + 1 \ + 1) +#define TRACKSTICK_RANGE_START 3 +#define TRACKSTICK_RANGE_END 6 + +struct rmi_f30_ctrl_data { + int address; + int length; + u8 *regs; +}; + struct f30_data { /* Query Data */ bool has_extended_pattern; @@ -76,18 +79,21 @@ struct f30_data { u16 *gpioled_key_map; struct input_dev *input; + + struct rmi_function *f03; + bool trackstick_buttons; }; static int rmi_f30_read_control_parameters(struct rmi_function *fn, struct f30_data *f30) { - struct rmi_device *rmi_dev = fn->rmi_dev; - int error = 0; + int error; - error = rmi_read_block(rmi_dev, fn->fd.control_base_addr, - f30->ctrl_regs, f30->ctrl_regs_size); + error = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr, + f30->ctrl_regs, f30->ctrl_regs_size); if (error) { - dev_err(&rmi_dev->dev, "%s : Could not read control registers at 0x%x error (%d)\n", + dev_err(&fn->dev, + "%s: Could not read control registers at 0x%x: %d\n", __func__, fn->fd.control_base_addr, error); return error; } @@ -95,24 +101,39 @@ static int rmi_f30_read_control_parameters(struct rmi_function *fn, return 0; } +static void rmi_f30_report_button(struct rmi_function *fn, + struct f30_data *f30, unsigned int button) +{ + unsigned int reg_num = button >> 3; + unsigned int bit_num = button & 0x07; + u16 key_code = f30->gpioled_key_map[button]; + bool key_down = !(f30->data_regs[reg_num] & BIT(bit_num)); + + if (f30->trackstick_buttons && + button >= TRACKSTICK_RANGE_START && + button <= TRACKSTICK_RANGE_END) { + rmi_f03_overwrite_button(f30->f03, key_code, key_down); + } else { + rmi_dbg(RMI_DEBUG_FN, &fn->dev, + "%s: call input report key (0x%04x) value (0x%02x)", + __func__, key_code, key_down); + + input_report_key(f30->input, key_code, key_down); + } +} + static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) { struct f30_data *f30 = dev_get_drvdata(&fn->dev); - struct rmi_device *rmi_dev = fn->rmi_dev; - struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); - int retval; - int gpiled = 0; - int value = 0; + struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev); + int error; int i; - int reg_num; - - if (!f30->input) - return 0; /* Read the gpi led data. */ if (drvdata->attn_data.data) { if (drvdata->attn_data.size < f30->register_count) { - dev_warn(&fn->dev, "F30 interrupted, but data is missing\n"); + dev_warn(&fn->dev, + "F30 interrupted, but data is missing\n"); return 0; } memcpy(f30->data_regs, drvdata->attn_data.data, @@ -120,72 +141,24 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) drvdata->attn_data.data += f30->register_count; drvdata->attn_data.size -= f30->register_count; } else { - retval = rmi_read_block(rmi_dev, fn->fd.data_base_addr, - f30->data_regs, f30->register_count); - - if (retval) { - dev_err(&fn->dev, "%s: Failed to read F30 data registers.\n", - __func__); - return retval; - } - } - - for (reg_num = 0; reg_num < f30->register_count; ++reg_num) { - for (i = 0; gpiled < f30->gpioled_count && i < 8; ++i, - ++gpiled) { - if (f30->gpioled_key_map[gpiled] != 0) { - /* buttons have pull up resistors */ - value = (((f30->data_regs[reg_num] >> i) & 0x01) - == 0); - - rmi_dbg(RMI_DEBUG_FN, &fn->dev, - "%s: call input report key (0x%04x) value (0x%02x)", - __func__, - f30->gpioled_key_map[gpiled], value); - input_report_key(f30->input, - f30->gpioled_key_map[gpiled], - value); - } - + error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr, + f30->data_regs, f30->register_count); + if (error) { + dev_err(&fn->dev, + "%s: Failed to read F30 data registers: %d\n", + __func__, error); + return error; } } - return 0; -} - -static int rmi_f30_register_device(struct rmi_function *fn) -{ - int i; - struct rmi_device *rmi_dev = fn->rmi_dev; - struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); - struct f30_data *f30 = dev_get_drvdata(&fn->dev); - struct input_dev *input_dev; - int button_count = 0; - - input_dev = drv_data->input; - if (!input_dev) { - dev_info(&fn->dev, "F30: no input device found, ignoring.\n"); - return -EINVAL; - } - - f30->input = input_dev; - - set_bit(EV_KEY, input_dev->evbit); - - input_dev->keycode = f30->gpioled_key_map; - input_dev->keycodesize = sizeof(u16); - input_dev->keycodemax = f30->gpioled_count; - - for (i = 0; i < f30->gpioled_count; i++) { - if (f30->gpioled_key_map[i] != 0) { - input_set_capability(input_dev, EV_KEY, - f30->gpioled_key_map[i]); - button_count++; - } + if (f30->has_gpio) { + for (i = 0; i < f30->gpioled_count; i++) + if (f30->gpioled_key_map[i] != KEY_RESERVED) + rmi_f30_report_button(fn, f30, i); + if (f30->trackstick_buttons) + rmi_f03_commit_buttons(f30->f03); } - if (button_count == 1) - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); return 0; } @@ -197,6 +170,12 @@ static int rmi_f30_config(struct rmi_function *fn) rmi_get_platform_data(fn->rmi_dev); int error; + if (pdata->f30_data.trackstick_buttons) { + /* Try [re-]establish link to F03. */ + f30->f03 = rmi_find_function(fn->rmi_dev, 0x03); + f30->trackstick_buttons = f30->f03 != NULL; + } + if (pdata->f30_data.disable) { drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask); } else { @@ -204,19 +183,20 @@ static int rmi_f30_config(struct rmi_function *fn) error = rmi_write_block(fn->rmi_dev, fn->fd.control_base_addr, f30->ctrl_regs, f30->ctrl_regs_size); if (error) { - dev_err(&fn->rmi_dev->dev, - "%s : Could not write control registers at 0x%x error (%d)\n", + dev_err(&fn->dev, + "%s: Could not write control registers at 0x%x: %d\n", __func__, fn->fd.control_base_addr, error); return error; } drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); } + return 0; } -static inline void rmi_f30_set_ctrl_data(struct rmi_f30_ctrl_data *ctrl, - int *ctrl_addr, int len, u8 **reg) +static void rmi_f30_set_ctrl_data(struct rmi_f30_ctrl_data *ctrl, + int *ctrl_addr, int len, u8 **reg) { ctrl->address = *ctrl_addr; ctrl->length = len; @@ -225,8 +205,7 @@ static inline void rmi_f30_set_ctrl_data(struct rmi_f30_ctrl_data *ctrl, *reg += len; } -static inline bool rmi_f30_is_valid_button(int button, - struct rmi_f30_ctrl_data *ctrl) +static bool rmi_f30_is_valid_button(int button, struct rmi_f30_ctrl_data *ctrl) { int byte_position = button >> 3; int bit_position = button & 0x07; @@ -239,32 +218,67 @@ static inline bool rmi_f30_is_valid_button(int button, (ctrl[3].regs[byte_position] & BIT(bit_position)); } -static inline int rmi_f30_initialize(struct rmi_function *fn) +static int rmi_f30_map_gpios(struct rmi_function *fn, + struct f30_data *f30) { - struct f30_data *f30; - struct rmi_device *rmi_dev = fn->rmi_dev; - const struct rmi_device_platform_data *pdata; - int retval = 0; - int control_address; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(fn->rmi_dev); + struct input_dev *input = f30->input; + unsigned int button = BTN_LEFT; + unsigned int trackstick_button = BTN_LEFT; + bool button_mapped = false; int i; - int button; - u8 buf[RMI_F30_QUERY_SIZE]; - u8 *ctrl_reg; - u8 *map_memory; - f30 = devm_kzalloc(&fn->dev, sizeof(struct f30_data), - GFP_KERNEL); - if (!f30) + f30->gpioled_key_map = devm_kcalloc(&fn->dev, + f30->gpioled_count, + sizeof(f30->gpioled_key_map[0]), + GFP_KERNEL); + if (!f30->gpioled_key_map) { + dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n"); return -ENOMEM; + } - dev_set_drvdata(&fn->dev, f30); + for (i = 0; i < f30->gpioled_count; i++) { + if (!rmi_f30_is_valid_button(i, f30->ctrl)) + continue; + + if (pdata->f30_data.trackstick_buttons && + i >= TRACKSTICK_RANGE_START && i < TRACKSTICK_RANGE_END) { + f30->gpioled_key_map[i] = trackstick_button++; + } else if (!pdata->f30_data.buttonpad || !button_mapped) { + f30->gpioled_key_map[i] = button; + input_set_capability(input, EV_KEY, button++); + button_mapped = true; + } + } + + input->keycode = f30->gpioled_key_map; + input->keycodesize = sizeof(f30->gpioled_key_map[0]); + input->keycodemax = f30->gpioled_count; + + /* + * Buttonpad could be also inferred from f30->has_mech_mouse_btns, + * but I am not sure, so use only the pdata info and the number of + * mapped buttons. + */ + if (pdata->f30_data.buttonpad || (button - BTN_LEFT == 1)) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + + return 0; +} - retval = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, buf, - RMI_F30_QUERY_SIZE); +static int rmi_f30_initialize(struct rmi_function *fn, struct f30_data *f30) +{ + u8 *ctrl_reg = f30->ctrl_regs; + int control_address = fn->fd.control_base_addr; + u8 buf[RMI_F30_QUERY_SIZE]; + int error; - if (retval) { - dev_err(&fn->dev, "Failed to read query register.\n"); - return retval; + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, + buf, RMI_F30_QUERY_SIZE); + if (error) { + dev_err(&fn->dev, "Failed to read query register\n"); + return error; } f30->has_extended_pattern = buf[0] & RMI_F30_EXTENDED_PATTERNS; @@ -276,101 +290,71 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) f30->has_mech_mouse_btns = buf[0] & RMI_F30_HAS_MECH_MOUSE_BTNS; f30->gpioled_count = buf[1] & RMI_F30_GPIO_LED_COUNT; - f30->register_count = (f30->gpioled_count + 7) >> 3; - - control_address = fn->fd.control_base_addr; - ctrl_reg = f30->ctrl_regs; + f30->register_count = DIV_ROUND_UP(f30->gpioled_count, 8); if (f30->has_gpio && f30->has_led) rmi_f30_set_ctrl_data(&f30->ctrl[0], &control_address, - f30->register_count, &ctrl_reg); + f30->register_count, &ctrl_reg); - rmi_f30_set_ctrl_data(&f30->ctrl[1], &control_address, sizeof(u8), - &ctrl_reg); + rmi_f30_set_ctrl_data(&f30->ctrl[1], &control_address, + sizeof(u8), &ctrl_reg); if (f30->has_gpio) { rmi_f30_set_ctrl_data(&f30->ctrl[2], &control_address, - f30->register_count, &ctrl_reg); + f30->register_count, &ctrl_reg); rmi_f30_set_ctrl_data(&f30->ctrl[3], &control_address, - f30->register_count, &ctrl_reg); + f30->register_count, &ctrl_reg); } if (f30->has_led) { - int ctrl5_len; - rmi_f30_set_ctrl_data(&f30->ctrl[4], &control_address, - f30->register_count, &ctrl_reg); - - if (f30->has_extended_pattern) - ctrl5_len = 6; - else - ctrl5_len = 2; + f30->register_count, &ctrl_reg); rmi_f30_set_ctrl_data(&f30->ctrl[5], &control_address, - ctrl5_len, &ctrl_reg); + f30->has_extended_pattern ? 6 : 2, + &ctrl_reg); } if (f30->has_led || f30->has_gpio_driver_control) { /* control 6 uses a byte per gpio/led */ rmi_f30_set_ctrl_data(&f30->ctrl[6], &control_address, - f30->gpioled_count, &ctrl_reg); + f30->gpioled_count, &ctrl_reg); } if (f30->has_mappable_buttons) { /* control 7 uses a byte per gpio/led */ rmi_f30_set_ctrl_data(&f30->ctrl[7], &control_address, - f30->gpioled_count, &ctrl_reg); + f30->gpioled_count, &ctrl_reg); } if (f30->has_haptic) { rmi_f30_set_ctrl_data(&f30->ctrl[8], &control_address, - f30->register_count, &ctrl_reg); + f30->register_count, &ctrl_reg); rmi_f30_set_ctrl_data(&f30->ctrl[9], &control_address, - sizeof(u8), &ctrl_reg); + sizeof(u8), &ctrl_reg); } if (f30->has_mech_mouse_btns) rmi_f30_set_ctrl_data(&f30->ctrl[10], &control_address, - sizeof(u8), &ctrl_reg); + sizeof(u8), &ctrl_reg); - f30->ctrl_regs_size = ctrl_reg - f30->ctrl_regs - ?: RMI_F30_CTRL_REGS_MAX_SIZE; + f30->ctrl_regs_size = ctrl_reg - + f30->ctrl_regs ?: RMI_F30_CTRL_REGS_MAX_SIZE; - retval = rmi_f30_read_control_parameters(fn, f30); - if (retval < 0) { + error = rmi_f30_read_control_parameters(fn, f30); + if (error) { dev_err(&fn->dev, - "Failed to initialize F19 control params.\n"); - return retval; - } - - map_memory = devm_kzalloc(&fn->dev, - (f30->gpioled_count * (sizeof(u16))), - GFP_KERNEL); - if (!map_memory) { - dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n"); - return -ENOMEM; + "Failed to initialize F30 control params: %d\n", + error); + return error; } - f30->gpioled_key_map = (u16 *)map_memory; - - pdata = rmi_get_platform_data(rmi_dev); if (f30->has_gpio) { - button = BTN_LEFT; - for (i = 0; i < f30->gpioled_count; i++) { - if (rmi_f30_is_valid_button(i, f30->ctrl)) { - f30->gpioled_key_map[i] = button++; - - /* - * buttonpad might be given by - * f30->has_mech_mouse_btns, but I am - * not sure, so use only the pdata info - */ - if (pdata->f30_data.buttonpad) - break; - } - } + error = rmi_f30_map_gpios(fn, f30); + if (error) + return error; } return 0; @@ -378,26 +362,33 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) static int rmi_f30_probe(struct rmi_function *fn) { - int rc; + struct rmi_device *rmi_dev = fn->rmi_dev; const struct rmi_device_platform_data *pdata = - rmi_get_platform_data(fn->rmi_dev); + rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + struct f30_data *f30; + int error; if (pdata->f30_data.disable) return 0; - rc = rmi_f30_initialize(fn); - if (rc < 0) - goto error_exit; + if (!drv_data->input) { + dev_info(&fn->dev, "F30: no input device found, ignoring\n"); + return -ENXIO; + } - rc = rmi_f30_register_device(fn); - if (rc < 0) - goto error_exit; + f30 = devm_kzalloc(&fn->dev, sizeof(*f30), GFP_KERNEL); + if (!f30) + return -ENOMEM; - return 0; + f30->input = drv_data->input; -error_exit: - return rc; + error = rmi_f30_initialize(fn, f30); + if (error) + return error; + dev_set_drvdata(&fn->dev, f30); + return 0; } struct rmi_function_handler rmi_f30_handler = { diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c index 9774dfbab9bb..425fe140e9df 100644 --- a/drivers/input/rmi4/rmi_f34.c +++ b/drivers/input/rmi4/rmi_f34.c @@ -157,6 +157,9 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data, i + 1, block_count); data += f34->v5.block_size; + f34->update_progress += f34->v5.block_size; + f34->update_status = (f34->update_progress * 100) / + f34->update_size; } return 0; @@ -174,7 +177,7 @@ static int rmi_f34_write_config(struct f34_data *f34, const void *data) F34_WRITE_CONFIG_BLOCK); } -int rmi_f34_enable_flash(struct f34_data *f34) +static int rmi_f34_enable_flash(struct f34_data *f34) { return rmi_f34_command(f34, F34_ENABLE_FLASH_PROG, F34_ENABLE_WAIT_MS, true); @@ -184,9 +187,14 @@ static int rmi_f34_flash_firmware(struct f34_data *f34, const struct rmi_f34_firmware *syn_fw) { struct rmi_function *fn = f34->fn; + u32 image_size = le32_to_cpu(syn_fw->image_size); + u32 config_size = le32_to_cpu(syn_fw->config_size); int ret; - if (syn_fw->image_size) { + f34->update_progress = 0; + f34->update_size = image_size + config_size; + + if (image_size) { dev_info(&fn->dev, "Erasing firmware...\n"); ret = rmi_f34_command(f34, F34_ERASE_ALL, F34_ERASE_WAIT_MS, true); @@ -194,18 +202,18 @@ static int rmi_f34_flash_firmware(struct f34_data *f34, return ret; dev_info(&fn->dev, "Writing firmware (%d bytes)...\n", - syn_fw->image_size); + image_size); ret = rmi_f34_write_firmware(f34, syn_fw->data); if (ret) return ret; } - if (syn_fw->config_size) { + if (config_size) { /* * We only need to erase config if we haven't updated * firmware. */ - if (!syn_fw->image_size) { + if (!image_size) { dev_info(&fn->dev, "Erasing config...\n"); ret = rmi_f34_command(f34, F34_ERASE_CONFIG, F34_ERASE_WAIT_MS, true); @@ -214,9 +222,8 @@ static int rmi_f34_flash_firmware(struct f34_data *f34, } dev_info(&fn->dev, "Writing config (%d bytes)...\n", - syn_fw->config_size); - ret = rmi_f34_write_config(f34, - &syn_fw->data[syn_fw->image_size]); + config_size); + ret = rmi_f34_write_config(f34, &syn_fw->data[image_size]); if (ret) return ret; } @@ -224,21 +231,23 @@ static int rmi_f34_flash_firmware(struct f34_data *f34, return 0; } -int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw) +static int rmi_f34_update_firmware(struct f34_data *f34, + const struct firmware *fw) { - const struct rmi_f34_firmware *syn_fw; + const struct rmi_f34_firmware *syn_fw = + (const struct rmi_f34_firmware *)fw->data; + u32 image_size = le32_to_cpu(syn_fw->image_size); + u32 config_size = le32_to_cpu(syn_fw->config_size); int ret; - syn_fw = (const struct rmi_f34_firmware *)fw->data; BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) != F34_FW_IMAGE_OFFSET); rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, - "FW size:%d, checksum:%08x, image_size:%d, config_size:%d\n", - (int)fw->size, + "FW size:%zd, checksum:%08x, image_size:%d, config_size:%d\n", + fw->size, le32_to_cpu(syn_fw->checksum), - le32_to_cpu(syn_fw->image_size), - le32_to_cpu(syn_fw->config_size)); + image_size, config_size); rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x\n", @@ -246,27 +255,25 @@ int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw) (int)sizeof(syn_fw->product_id), syn_fw->product_id, syn_fw->product_info[0], syn_fw->product_info[1]); - if (syn_fw->image_size && - syn_fw->image_size != f34->v5.fw_blocks * f34->v5.block_size) { + if (image_size && image_size != f34->v5.fw_blocks * f34->v5.block_size) { dev_err(&f34->fn->dev, "Bad firmware image: fw size %d, expected %d\n", - syn_fw->image_size, - f34->v5.fw_blocks * f34->v5.block_size); + image_size, f34->v5.fw_blocks * f34->v5.block_size); ret = -EILSEQ; goto out; } - if (syn_fw->config_size && - syn_fw->config_size != f34->v5.config_blocks * f34->v5.block_size) { + if (config_size && + config_size != f34->v5.config_blocks * f34->v5.block_size) { dev_err(&f34->fn->dev, "Bad firmware image: config size %d, expected %d\n", - syn_fw->config_size, + config_size, f34->v5.config_blocks * f34->v5.block_size); ret = -EILSEQ; goto out; } - if (syn_fw->image_size && !syn_fw->config_size) { + if (image_size && !config_size) { dev_err(&f34->fn->dev, "Bad firmware image: no config data\n"); ret = -EILSEQ; goto out; @@ -283,6 +290,63 @@ out: return ret; } +static int rmi_f34_status(struct rmi_function *fn) +{ + struct f34_data *f34 = dev_get_drvdata(&fn->dev); + + /* + * The status is the percentage complete, or once complete, + * zero for success or a negative return code. + */ + return f34->update_status; +} + +static ssize_t rmi_driver_bootloader_id_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + struct rmi_function *fn = data->f34_container; + struct f34_data *f34; + + if (fn) { + f34 = dev_get_drvdata(&fn->dev); + + if (f34->bl_version == 5) + return scnprintf(buf, PAGE_SIZE, "%c%c\n", + f34->bootloader_id[0], + f34->bootloader_id[1]); + else + return scnprintf(buf, PAGE_SIZE, "V%d.%d\n", + f34->bootloader_id[1], + f34->bootloader_id[0]); + } + + return 0; +} + +static DEVICE_ATTR(bootloader_id, 0444, rmi_driver_bootloader_id_show, NULL); + +static ssize_t rmi_driver_configuration_id_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + struct rmi_function *fn = data->f34_container; + struct f34_data *f34; + + if (fn) { + f34 = dev_get_drvdata(&fn->dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", f34->configuration_id); + } + + return 0; +} + +static DEVICE_ATTR(configuration_id, 0444, + rmi_driver_configuration_id_show, NULL); + static int rmi_firmware_update(struct rmi_driver_data *data, const struct firmware *fw) { @@ -346,7 +410,13 @@ static int rmi_firmware_update(struct rmi_driver_data *data, else ret = rmi_f34_update_firmware(f34, fw); - dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret); + if (ret) { + f34->update_status = ret; + dev_err(&f34->fn->dev, + "Firmware update failed, status: %d\n", ret); + } else { + dev_info(&f34->fn->dev, "Firmware update complete\n"); + } rmi_disable_irq(rmi_dev, false); @@ -377,9 +447,6 @@ static int rmi_firmware_update(struct rmi_driver_data *data, return ret; } -static int rmi_firmware_update(struct rmi_driver_data *data, - const struct firmware *fw); - static ssize_t rmi_driver_update_fw_store(struct device *dev, struct device_attribute *dattr, const char *buf, size_t count) @@ -414,8 +481,27 @@ static ssize_t rmi_driver_update_fw_store(struct device *dev, static DEVICE_ATTR(update_fw, 0200, NULL, rmi_driver_update_fw_store); +static ssize_t rmi_driver_update_fw_status_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct rmi_driver_data *data = dev_get_drvdata(dev); + int update_status = 0; + + if (data->f34_container) + update_status = rmi_f34_status(data->f34_container); + + return scnprintf(buf, PAGE_SIZE, "%d\n", update_status); +} + +static DEVICE_ATTR(update_fw_status, 0444, + rmi_driver_update_fw_status_show, NULL); + static struct attribute *rmi_firmware_attrs[] = { + &dev_attr_bootloader_id.attr, + &dev_attr_configuration_id.attr, &dev_attr_update_fw.attr, + &dev_attr_update_fw_status.attr, NULL }; @@ -441,8 +527,6 @@ static int rmi_f34_probe(struct rmi_function *fn) /* v5 code only supported version 0, try V7 probe */ if (version > 0) return rmi_f34v7_probe(f34); - else if (version != 0) - return -ENODEV; f34->bl_version = 5; diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h index 2c21056dc375..43a91349b28d 100644 --- a/drivers/input/rmi4/rmi_f34.h +++ b/drivers/input/rmi4/rmi_f34.h @@ -301,6 +301,10 @@ struct f34_data { unsigned char bootloader_id[5]; unsigned char configuration_id[CONFIG_ID_SIZE*2 + 1]; + int update_status; + int update_progress; + int update_size; + union { struct f34v5_data v5; struct f34v7_data v7; diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c index ca31f9539d9b..56c6c39ad31e 100644 --- a/drivers/input/rmi4/rmi_f34v7.c +++ b/drivers/input/rmi4/rmi_f34v7.c @@ -588,6 +588,7 @@ static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34) u16 block_count; block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size; + f34->update_size += block_count; if (block_count != f34->v7.blkcount.ui_firmware) { dev_err(&f34->fn->dev, @@ -604,6 +605,7 @@ static int rmi_f34v7_check_ui_config_size(struct f34_data *f34) u16 block_count; block_count = f34->v7.img.ui_config.size / f34->v7.block_size; + f34->update_size += block_count; if (block_count != f34->v7.blkcount.ui_config) { dev_err(&f34->fn->dev, "UI config size mismatch\n"); @@ -618,6 +620,7 @@ static int rmi_f34v7_check_dp_config_size(struct f34_data *f34) u16 block_count; block_count = f34->v7.img.dp_config.size / f34->v7.block_size; + f34->update_size += block_count; if (block_count != f34->v7.blkcount.dp_config) { dev_err(&f34->fn->dev, "Display config size mismatch\n"); @@ -632,6 +635,8 @@ static int rmi_f34v7_check_guest_code_size(struct f34_data *f34) u16 block_count; block_count = f34->v7.img.guest_code.size / f34->v7.block_size; + f34->update_size += block_count; + if (block_count != f34->v7.blkcount.guest_code) { dev_err(&f34->fn->dev, "Guest code size mismatch\n"); return -EINVAL; @@ -645,6 +650,7 @@ static int rmi_f34v7_check_bl_config_size(struct f34_data *f34) u16 block_count; block_count = f34->v7.img.bl_config.size / f34->v7.block_size; + f34->update_size += block_count; if (block_count != f34->v7.blkcount.bl_config) { dev_err(&f34->fn->dev, "Bootloader config size mismatch\n"); @@ -881,6 +887,9 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34, block_ptr += (transfer * f34->v7.block_size); remaining -= transfer; + f34->update_progress += transfer; + f34->update_status = (f34->update_progress * 100) / + f34->update_size; } while (remaining); return 0; @@ -1191,6 +1200,8 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw) rmi_f34v7_read_queries_bl_version(f34); f34->v7.image = fw->data; + f34->update_progress = 0; + f34->update_size = 0; ret = rmi_f34v7_parse_image_info(f34); if (ret < 0) diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 2e4ff5bac754..e420fd781d44 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -159,13 +159,12 @@ static int psif_open(struct serio *io) retval = clk_enable(psif->pclk); if (retval) - goto out; + return retval; psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); psif_writel(psif, IER, PSIF_BIT(RXRDY)); psif->open = true; -out: return retval; } @@ -210,16 +209,12 @@ static int __init psif_probe(struct platform_device *pdev) int ret; psif = kzalloc(sizeof(struct psif), GFP_KERNEL); - if (!psif) { - dev_dbg(&pdev->dev, "out of memory\n"); - ret = -ENOMEM; - goto out; - } + if (!psif) + return -ENOMEM; psif->pdev = pdev; io = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!io) { - dev_dbg(&pdev->dev, "out of memory\n"); ret = -ENOMEM; goto out_free_psif; } @@ -297,7 +292,6 @@ out_free_io: kfree(io); out_free_psif: kfree(psif); -out: return ret; } diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index c948866edf87..25151d9214e0 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -402,7 +402,6 @@ static int hv_kbd_remove(struct hv_device *hv_dev) { struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); - device_init_wakeup(&hv_dev->device, false); serio_unregister_port(kbd_dev->hv_serio); vmbus_close(hv_dev->channel); kfree(kbd_dev); diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index a7618776705a..05afd16ea9c9 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -923,6 +923,10 @@ static struct pnp_driver i8042_pnp_kbd_driver = { .name = "i8042 kbd", .id_table = pnp_kbd_devids, .probe = i8042_pnp_kbd_probe, + .driver = { + .probe_type = PROBE_FORCE_SYNCHRONOUS, + .suppress_bind_attrs = true, + }, }; static struct pnp_device_id pnp_aux_devids[] = { @@ -945,6 +949,10 @@ static struct pnp_driver i8042_pnp_aux_driver = { .name = "i8042 aux", .id_table = pnp_aux_devids, .probe = i8042_pnp_aux_probe, + .driver = { + .probe_type = PROBE_FORCE_SYNCHRONOUS, + .suppress_bind_attrs = true, + }, }; static void i8042_pnp_exit(void) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 62685a768913..c52da651269b 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -312,8 +312,10 @@ static int __i8042_command(unsigned char *param, int command) for (i = 0; i < ((command >> 12) & 0xf); i++) { error = i8042_wait_write(); - if (error) + if (error) { + dbg(" -- i8042 (wait write timeout)\n"); return error; + } dbg("%02x -> i8042 (parameter)\n", param[i]); i8042_write_data(param[i]); } @@ -321,7 +323,7 @@ static int __i8042_command(unsigned char *param, int command) for (i = 0; i < ((command >> 8) & 0xf); i++) { error = i8042_wait_read(); if (error) { - dbg(" -- i8042 (timeout)\n"); + dbg(" -- i8042 (wait read timeout)\n"); return error; } diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 5223cbf94262..14c40892ed82 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -243,18 +243,17 @@ static int xps2_of_probe(struct platform_device *ofdev) unsigned int irq; int error; - dev_info(dev, "Device Tree Probing \'%s\'\n", - ofdev->dev.of_node->name); + dev_info(dev, "Device Tree Probing \'%s\'\n", dev->of_node->name); /* Get iospace for the device */ - error = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); + error = of_address_to_resource(dev->of_node, 0, &r_mem); if (error) { dev_err(dev, "invalid address\n"); return error; } /* Get IRQ for the device */ - irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + irq = irq_of_parse_and_map(dev->of_node, 0); if (!irq) { dev_err(dev, "no IRQ found\n"); return -ENODEV; diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index 251ff2aa0633..7ed828a51f4c 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -240,8 +240,6 @@ static int pm860x_touch_probe(struct platform_device *pdev) if (!touch) return -ENOMEM; - platform_set_drvdata(pdev, touch); - touch->idev = devm_input_allocate_device(&pdev->dev); if (!touch->idev) { dev_err(&pdev->dev, "Failed to allocate input device!\n"); @@ -285,7 +283,6 @@ static int pm860x_touch_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, touch); return 0; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index efca0133e266..033599777651 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -546,18 +546,6 @@ config TOUCHSCREEN_INEXIO To compile this driver as a module, choose M here: the module will be called inexio. -config TOUCHSCREEN_INTEL_MID - tristate "Intel MID platform resistive touchscreen" - depends on INTEL_SCU_IPC - help - Say Y here if you have a Intel MID based touchscreen in - your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called intel_mid_touch. - config TOUCHSCREEN_MK712 tristate "ICS MicroClock MK712 touchscreen" help @@ -1177,6 +1165,17 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_ZET6223 + tristate "Zeitec ZET6223 touchscreen driver" + depends on I2C + help + Say Y here if you have a touchscreen using Zeitec ZET6223 + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called zet6223. + config TOUCHSCREEN_ZFORCE tristate "Neonode zForce infrared touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 81b86451782d..b622e5344137 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -42,7 +42,6 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o -obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o @@ -96,6 +95,7 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_ZET6223) += zet6223.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 1ce3ecbe37f8..f5793e3d945f 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1462,8 +1462,6 @@ static int ads7846_remove(struct spi_device *spi) { struct ads7846 *ts = spi_get_drvdata(spi); - device_init_wakeup(&spi->dev, false); - sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); ads7846_disable(ts); diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index 71b5a634cf6d..6562b17117f7 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -127,7 +127,6 @@ static int ar1021_i2c_probe(struct i2c_client *client, return error; } - i2c_set_clientdata(client, ar1021); return 0; } diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index 7ec0421c0dd8..8cf0b2be2df4 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -339,10 +339,8 @@ static int __init atmel_wm97xx_probe(struct platform_device *pdev) int ret; atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL); - if (!atmel_wm97xx) { - dev_dbg(&pdev->dev, "out of memory\n"); + if (!atmel_wm97xx) return -ENOMEM; - } atmel_wm97xx->wm = wm; atmel_wm97xx->regs = (void *)ATMEL_WM97XX_AC97C_IOMEM; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index e5d185fe69b9..2302aef2b2d4 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2509,7 +2509,7 @@ static void mxt_debug_init(struct mxt_data *data) dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN; else dbg->t37_pages = DIV_ROUND_UP(data->xsize * - data->info.matrix_ysize * + info->matrix_ysize * sizeof(u16), sizeof(dbg->t37_buf->data)); diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 931417eb4f5a..4fa5da8d5fa8 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -637,8 +637,6 @@ static int bu21013_remove(struct i2c_client *client) kfree(bu21013_data); - device_init_wakeup(&client->dev, false); - return 0; } diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c index 69828d015d45..69c08acae264 100644 --- a/drivers/input/touchscreen/colibri-vf50-ts.c +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -311,8 +311,6 @@ static int vf50_ts_probe(struct platform_device *pdev) return -ENOMEM; } - platform_set_drvdata(pdev, touchdev); - input->name = DRIVER_NAME; input->id.bustype = BUS_HOST; input->dev.parent = dev; diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 44deca88c579..beaf61ce775b 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -202,7 +202,7 @@ static int cyttsp4_si_get_cydata(struct cyttsp4 *cd) int rc; si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs; - dev_dbg(cd->dev, "%s: cydata size: %Zd\n", __func__, + dev_dbg(cd->dev, "%s: cydata size: %zd\n", __func__, si->si_ofs.cydata_size); p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL); @@ -430,13 +430,13 @@ static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd) for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) { dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__, cyttsp4_tch_abs_string[abs]); - dev_dbg(cd->dev, "%s: ofs =%2Zd\n", __func__, + dev_dbg(cd->dev, "%s: ofs =%2zd\n", __func__, si->si_ofs.tch_abs[abs].ofs); - dev_dbg(cd->dev, "%s: siz =%2Zd\n", __func__, + dev_dbg(cd->dev, "%s: siz =%2zd\n", __func__, si->si_ofs.tch_abs[abs].size); - dev_dbg(cd->dev, "%s: max =%2Zd\n", __func__, + dev_dbg(cd->dev, "%s: max =%2zd\n", __func__, si->si_ofs.tch_abs[abs].max); - dev_dbg(cd->dev, "%s: bofs=%2Zd\n", __func__, + dev_dbg(cd->dev, "%s: bofs=%2zd\n", __func__, si->si_ofs.tch_abs[abs].bofs); } @@ -586,62 +586,62 @@ static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd) static void cyttsp4_si_put_log_data(struct cyttsp4 *cd) { struct cyttsp4_sysinfo *si = &cd->sysinfo; - dev_dbg(cd->dev, "%s: cydata_ofs =%4Zd siz=%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: cydata_ofs =%4zd siz=%4zd\n", __func__, si->si_ofs.cydata_ofs, si->si_ofs.cydata_size); - dev_dbg(cd->dev, "%s: test_ofs =%4Zd siz=%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: test_ofs =%4zd siz=%4zd\n", __func__, si->si_ofs.test_ofs, si->si_ofs.test_size); - dev_dbg(cd->dev, "%s: pcfg_ofs =%4Zd siz=%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: pcfg_ofs =%4zd siz=%4zd\n", __func__, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size); - dev_dbg(cd->dev, "%s: opcfg_ofs =%4Zd siz=%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: opcfg_ofs =%4zd siz=%4zd\n", __func__, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size); - dev_dbg(cd->dev, "%s: ddata_ofs =%4Zd siz=%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: ddata_ofs =%4zd siz=%4zd\n", __func__, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size); - dev_dbg(cd->dev, "%s: mdata_ofs =%4Zd siz=%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: mdata_ofs =%4zd siz=%4zd\n", __func__, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size); - dev_dbg(cd->dev, "%s: cmd_ofs =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: cmd_ofs =%4zd\n", __func__, si->si_ofs.cmd_ofs); - dev_dbg(cd->dev, "%s: rep_ofs =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: rep_ofs =%4zd\n", __func__, si->si_ofs.rep_ofs); - dev_dbg(cd->dev, "%s: rep_sz =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: rep_sz =%4zd\n", __func__, si->si_ofs.rep_sz); - dev_dbg(cd->dev, "%s: num_btns =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: num_btns =%4zd\n", __func__, si->si_ofs.num_btns); - dev_dbg(cd->dev, "%s: num_btn_regs =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: num_btn_regs =%4zd\n", __func__, si->si_ofs.num_btn_regs); - dev_dbg(cd->dev, "%s: tt_stat_ofs =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: tt_stat_ofs =%4zd\n", __func__, si->si_ofs.tt_stat_ofs); - dev_dbg(cd->dev, "%s: tch_rec_size =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: tch_rec_size =%4zd\n", __func__, si->si_ofs.tch_rec_size); - dev_dbg(cd->dev, "%s: max_tchs =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: max_tchs =%4zd\n", __func__, si->si_ofs.max_tchs); - dev_dbg(cd->dev, "%s: mode_size =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: mode_size =%4zd\n", __func__, si->si_ofs.mode_size); - dev_dbg(cd->dev, "%s: data_size =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: data_size =%4zd\n", __func__, si->si_ofs.data_size); - dev_dbg(cd->dev, "%s: map_sz =%4Zd\n", __func__, + dev_dbg(cd->dev, "%s: map_sz =%4zd\n", __func__, si->si_ofs.map_sz); - dev_dbg(cd->dev, "%s: btn_rec_size =%2Zd\n", __func__, + dev_dbg(cd->dev, "%s: btn_rec_size =%2zd\n", __func__, si->si_ofs.btn_rec_size); - dev_dbg(cd->dev, "%s: btn_diff_ofs =%2Zd\n", __func__, + dev_dbg(cd->dev, "%s: btn_diff_ofs =%2zd\n", __func__, si->si_ofs.btn_diff_ofs); - dev_dbg(cd->dev, "%s: btn_diff_size =%2Zd\n", __func__, + dev_dbg(cd->dev, "%s: btn_diff_size =%2zd\n", __func__, si->si_ofs.btn_diff_size); - dev_dbg(cd->dev, "%s: max_x = 0x%04ZX (%Zd)\n", __func__, + dev_dbg(cd->dev, "%s: max_x = 0x%04zX (%zd)\n", __func__, si->si_ofs.max_x, si->si_ofs.max_x); - dev_dbg(cd->dev, "%s: x_origin = %Zd (%s)\n", __func__, + dev_dbg(cd->dev, "%s: x_origin = %zd (%s)\n", __func__, si->si_ofs.x_origin, si->si_ofs.x_origin == CY_NORMAL_ORIGIN ? "left corner" : "right corner"); - dev_dbg(cd->dev, "%s: max_y = 0x%04ZX (%Zd)\n", __func__, + dev_dbg(cd->dev, "%s: max_y = 0x%04zX (%zd)\n", __func__, si->si_ofs.max_y, si->si_ofs.max_y); - dev_dbg(cd->dev, "%s: y_origin = %Zd (%s)\n", __func__, + dev_dbg(cd->dev, "%s: y_origin = %zd (%s)\n", __func__, si->si_ofs.y_origin, si->si_ofs.y_origin == CY_NORMAL_ORIGIN ? "upper corner" : "lower corner"); - dev_dbg(cd->dev, "%s: max_p = 0x%04ZX (%Zd)\n", __func__, + dev_dbg(cd->dev, "%s: max_p = 0x%04zX (%zd)\n", __func__, si->si_ofs.max_p, si->si_ofs.max_p); dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__, @@ -1000,7 +1000,7 @@ static int cyttsp4_xy_worker(struct cyttsp4 *cd) dev_dbg(dev, "%s: Large area detected\n", __func__); if (num_cur_tch > si->si_ofs.max_tchs) { - dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%Zd)\n", + dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%zd)\n", __func__, num_cur_tch, si->si_ofs.max_tchs); num_cur_tch = si->si_ofs.max_tchs; } diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 28466e358fee..8cf8d8d5d4ef 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -67,7 +67,7 @@ #define EDT_SWITCH_MODE_RETRIES 10 #define EDT_SWITCH_MODE_DELAY 5 /* msec */ #define EDT_RAW_DATA_RETRIES 100 -#define EDT_RAW_DATA_DELAY 1 /* msec */ +#define EDT_RAW_DATA_DELAY 1000 /* usec */ enum edt_ver { M06, @@ -664,7 +664,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, } do { - msleep(EDT_RAW_DATA_DELAY); + usleep_range(EDT_RAW_DATA_DELAY, EDT_RAW_DATA_DELAY + 100); val = edt_ft5x06_register_read(tsdata, 0x08); if (val < 1) break; @@ -982,7 +982,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; } - input_set_drvdata(input, tsdata); i2c_set_clientdata(client, tsdata); irq_flags = irq_get_trigger_type(client->irq); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 09be6ced7151..16023867b9da 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -173,12 +173,11 @@ static int eeti_ts_probe(struct i2c_client *client, priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "failed to allocate driver data\n"); - goto err0; + return -ENOMEM; } mutex_init(&priv->mutex); input = input_allocate_device(); - if (!input) { dev_err(&client->dev, "Failed to allocate input device.\n"); goto err1; @@ -232,7 +231,6 @@ static int eeti_ts_probe(struct i2c_client *client, */ eeti_ts_stop(priv); - device_init_wakeup(&client->dev, 0); return 0; err3: @@ -243,7 +241,6 @@ err2: err1: input_free_device(input); kfree(priv); -err0: return err; } diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 1afc08b08155..752ae9cf4514 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -214,8 +214,6 @@ static int egalax_ts_probe(struct i2c_client *client, ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0); input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0); - input_set_drvdata(input_dev, ts); - error = devm_request_threaded_irq(&client->dev, client->irq, NULL, egalax_ts_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, @@ -229,7 +227,6 @@ static int egalax_ts_probe(struct i2c_client *client, if (error) return error; - i2c_set_clientdata(client, ts); return 0; } diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 3e6003d32e56..872750eeca93 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -1260,8 +1260,6 @@ static int elants_i2c_probe(struct i2c_client *client, input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res); input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res); - input_set_drvdata(ts->input, ts); - error = input_register_device(ts->input); if (error) { dev_err(&client->dev, diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c index d50ee490c9cc..47fe1f184bbc 100644 --- a/drivers/input/touchscreen/fsl-imx25-tcq.c +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c @@ -507,7 +507,7 @@ static int mx25_tcq_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct input_dev *idev; struct mx25_tcq_priv *priv; - struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent); + struct mx25_tsadc *tsadc = dev_get_drvdata(dev->parent); struct resource *res; void __iomem *mem; int error; diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index fe4848bd1f4c..6f76eeedf465 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -256,7 +256,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); - input_set_drvdata(input, priv); i2c_set_clientdata(client, priv); error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, @@ -280,7 +279,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, goto err_remove_sysfs; } - device_init_wakeup(&client->dev, 1); + device_init_wakeup(dev, 1); dev_dbg(dev, "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c deleted file mode 100644 index b4f0725a1c3d..000000000000 --- a/drivers/input/touchscreen/intel-mid-touch.c +++ /dev/null @@ -1,654 +0,0 @@ -/* - * Intel MID Resistive Touch Screen Driver - * - * Copyright (C) 2008 Intel Corp - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) - * Ramesh Agarwal (ramesh.agarwal@intel.com) - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * TODO: - * review conversion of r/m/w sequences - */ - -#include <linux/module.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/err.h> -#include <linux/param.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/irq.h> -#include <linux/delay.h> -#include <asm/intel_scu_ipc.h> -#include <linux/device.h> - -/* PMIC Interrupt registers */ -#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */ - -/* PMIC Interrupt registers */ -#define PMIC_REG_INT 0x04 /* PMIC interrupt register */ -#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */ - -/* ADC Interrupt registers */ -#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */ -#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */ - -/* ADC Control registers */ -#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */ - -/* ADC Channel Selection registers */ -#define PMICADDR0 0xA4 -#define END_OF_CHANNEL 0x1F - -/* ADC Result register */ -#define PMIC_REG_ADCSNS0H 0x64 - -/* ADC channels for touch screen */ -#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */ -#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */ -#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */ -#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */ - -/* Touch screen channel BIAS constants */ -#define MRST_XBIAS 0x20 -#define MRST_YBIAS 0x40 -#define MRST_ZBIAS 0x80 - -/* Touch screen coordinates */ -#define MRST_X_MIN 10 -#define MRST_X_MAX 1024 -#define MRST_X_FUZZ 5 -#define MRST_Y_MIN 10 -#define MRST_Y_MAX 1024 -#define MRST_Y_FUZZ 5 -#define MRST_PRESSURE_MIN 0 -#define MRST_PRESSURE_NOMINAL 50 -#define MRST_PRESSURE_MAX 100 - -#define WAIT_ADC_COMPLETION 10 /* msec */ - -/* PMIC ADC round robin delays */ -#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */ -#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */ - -/* PMIC Vendor Identifiers */ -#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */ -#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */ -#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */ -#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */ - -/* Touch screen device structure */ -struct mrstouch_dev { - struct device *dev; /* device associated with touch screen */ - struct input_dev *input; - char phys[32]; - u16 asr; /* Address selection register */ - int irq; - unsigned int vendor; /* PMIC vendor */ - unsigned int rev; /* PMIC revision */ - - int (*read_prepare)(struct mrstouch_dev *tsdev); - int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z); - int (*read_finish)(struct mrstouch_dev *tsdev); -}; - - -/*************************** NEC and Maxim Interface ************************/ - -static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev) -{ - return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20); -} - -/* - * Enables PENDET interrupt. - */ -static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev) -{ - int err; - - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20); - if (!err) - err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05); - - return err; -} - -/* - * Reads PMIC ADC touch screen result - * Reads ADC storage registers for higher 7 and lower 3 bits and - * converts the two readings into a single value and turns off gain bit - */ -static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm) -{ - int err; - u16 result; - u32 res; - - result = PMIC_REG_ADCSNS0H + offset; - - if (chan == MRST_TS_CHAN12) - result += 4; - - err = intel_scu_ipc_ioread32(result, &res); - if (err) - return err; - - /* Mash the bits up */ - - *vp = (res & 0xFF) << 3; /* Highest 7 bits */ - *vp |= (res >> 8) & 0x07; /* Lower 3 bits */ - *vp &= 0x3FF; - - res >>= 16; - - *vm = (res & 0xFF) << 3; /* Highest 7 bits */ - *vm |= (res >> 8) & 0x07; /* Lower 3 bits */ - *vm &= 0x3FF; - - return 0; -} - -/* - * Enables X, Y and Z bias values - * Enables YPYM for X channels and XPXM for Y channels - */ -static int mrstouch_ts_bias_set(uint offset, uint bias) -{ - int count; - u16 chan, start; - u16 reg[4]; - u8 data[4]; - - chan = PMICADDR0 + offset; - start = MRST_TS_CHAN10; - - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = bias | (start + count); - } - - return intel_scu_ipc_writev(reg, data, 4); -} - -/* To read touch screen channel values */ -static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev, - u16 *x, u16 *y, u16 *z) -{ - int err; - u16 xm, ym, zm; - - /* configure Y bias for X channels */ - err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read x+ and x- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm); - if (err) - goto ipc_error; - - /* configure x bias for y channels */ - err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read y+ and y- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym); - if (err) - goto ipc_error; - - /* configure z bias for x and y channels */ - err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* read z+ and z- channels */ - err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm); - if (err) - goto ipc_error; - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during adc read\n"); - return err; -} - - -/*************************** Freescale Interface ************************/ - -static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev) -{ - int err, count; - u16 chan; - u16 reg[5]; - u8 data[5]; - - /* Stop the ADC */ - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02); - if (err) - goto ipc_error; - - chan = PMICADDR0 + tsdev->asr; - - /* Set X BIAS */ - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = 0x2A; - } - reg[count] = chan++; /* Dummy */ - data[count] = 0; - - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* Set Y BIAS */ - for (count = 0; count <= 3; count++) { - reg[count] = chan++; - data[count] = 0x4A; - } - reg[count] = chan++; /* Dummy */ - data[count] = 0; - - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - /* Set Z BIAS */ - err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A); - if (err) - goto ipc_error; - - msleep(WAIT_ADC_COMPLETION); - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during %s\n", __func__); - return err; -} - -static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev, - u16 *x, u16 *y, u16 *z) -{ - int err; - u16 result; - u16 reg[4]; - u8 data[4]; - - result = PMIC_REG_ADCSNS0H + tsdev->asr; - - reg[0] = result + 4; - reg[1] = result + 5; - reg[2] = result + 16; - reg[3] = result + 17; - - err = intel_scu_ipc_readv(reg, data, 4); - if (err) - goto ipc_error; - - *x = data[0] << 3; /* Higher 7 bits */ - *x |= data[1] & 0x7; /* Lower 3 bits */ - *x &= 0x3FF; - - *y = data[2] << 3; /* Higher 7 bits */ - *y |= data[3] & 0x7; /* Lower 3 bits */ - *y &= 0x3FF; - - /* Read Z value */ - reg[0] = result + 28; - reg[1] = result + 29; - - err = intel_scu_ipc_readv(reg, data, 4); - if (err) - goto ipc_error; - - *z = data[0] << 3; /* Higher 7 bits */ - *z |= data[1] & 0x7; /* Lower 3 bits */ - *z &= 0x3FF; - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during %s\n", __func__); - return err; -} - -static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev) -{ - int err, count; - u16 chan; - u16 reg[5]; - u8 data[5]; - - /* Clear all TS channels */ - chan = PMICADDR0 + tsdev->asr; - for (count = 0; count <= 4; count++) { - reg[count] = chan++; - data[count] = 0; - } - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - for (count = 0; count <= 4; count++) { - reg[count] = chan++; - data[count] = 0; - } - err = intel_scu_ipc_writev(reg, data, 5); - if (err) - goto ipc_error; - - err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000); - if (err) - goto ipc_error; - - /* Start ADC */ - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02); - if (err) - goto ipc_error; - - return 0; - -ipc_error: - dev_err(tsdev->dev, "ipc error during %s\n", __func__); - return err; -} - -static void mrstouch_report_event(struct input_dev *input, - unsigned int x, unsigned int y, unsigned int z) -{ - if (z > MRST_PRESSURE_NOMINAL) { - /* Pen touched, report button touch and coordinates */ - input_report_key(input, BTN_TOUCH, 1); - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - } else { - input_report_key(input, BTN_TOUCH, 0); - } - - input_report_abs(input, ABS_PRESSURE, z); - input_sync(input); -} - -/* PENDET interrupt handler */ -static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id) -{ - struct mrstouch_dev *tsdev = dev_id; - u16 x, y, z; - - /* - * Should we lower thread priority? Probably not, since we are - * not spinning but sleeping... - */ - - if (tsdev->read_prepare(tsdev)) - goto out; - - do { - if (tsdev->read(tsdev, &x, &y, &z)) - break; - - mrstouch_report_event(tsdev->input, x, y, z); - } while (z > MRST_PRESSURE_NOMINAL); - - tsdev->read_finish(tsdev); - -out: - return IRQ_HANDLED; -} - -/* Utility to read PMIC ID */ -static int mrstouch_read_pmic_id(uint *vendor, uint *rev) -{ - int err; - u8 r; - - err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r); - if (err) - return err; - - *vendor = r & 0x7; - *rev = (r >> 3) & 0x7; - - return 0; -} - -/* - * Parse ADC channels to find end of the channel configured by other ADC user - * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels - */ -static int mrstouch_chan_parse(struct mrstouch_dev *tsdev) -{ - int found = 0; - int err, i; - u8 r8; - - for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { - err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8); - if (err) - return err; - - if (r8 == END_OF_CHANNEL) { - found = i; - break; - } - } - - if (tsdev->vendor == PMIC_VENDOR_FS) { - if (found > MRSTOUCH_MAX_CHANNELS - 18) - return -ENOSPC; - } else { - if (found > MRSTOUCH_MAX_CHANNELS - 4) - return -ENOSPC; - } - - return found; -} - - -/* - * Writes touch screen channels to ADC address selection registers - */ -static int mrstouch_ts_chan_set(uint offset) -{ - u16 chan; - - int ret, count; - - chan = PMICADDR0 + offset; - for (count = 0; count <= 3; count++) { - ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count); - if (ret) - return ret; - } - return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL); -} - -/* Initialize ADC */ -static int mrstouch_adc_init(struct mrstouch_dev *tsdev) -{ - int err, start; - u8 ra, rm; - - err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev); - if (err) { - dev_err(tsdev->dev, "Unable to read PMIC id\n"); - return err; - } - - switch (tsdev->vendor) { - case PMIC_VENDOR_NEC: - case PMIC_VENDOR_MAXIM: - tsdev->read_prepare = mrstouch_nec_adc_read_prepare; - tsdev->read = mrstouch_nec_adc_read; - tsdev->read_finish = mrstouch_nec_adc_read_finish; - break; - - case PMIC_VENDOR_FS: - tsdev->read_prepare = mrstouch_fs_adc_read_prepare; - tsdev->read = mrstouch_fs_adc_read; - tsdev->read_finish = mrstouch_fs_adc_read_finish; - break; - - default: - dev_err(tsdev->dev, - "Unsupported touchscreen: %d\n", tsdev->vendor); - return -ENXIO; - } - - start = mrstouch_chan_parse(tsdev); - if (start < 0) { - dev_err(tsdev->dev, "Unable to parse channels\n"); - return start; - } - - tsdev->asr = start; - - /* - * ADC power on, start, enable PENDET and set loop delay - * ADC loop delay is set to 4.5 ms approximately - * Loop delay more than this results in jitter in adc readings - * Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET - * interrupt generation sometimes. - */ - - if (tsdev->vendor == PMIC_VENDOR_FS) { - ra = 0xE0 | ADC_LOOP_DELAY0; - rm = 0x5; - } else { - /* NEC and MAXIm not consistent with loop delay 0 */ - ra = 0xE0 | ADC_LOOP_DELAY1; - rm = 0x0; - - /* configure touch screen channels */ - err = mrstouch_ts_chan_set(tsdev->asr); - if (err) - return err; - } - - err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7); - if (err) - return err; - - err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03); - if (err) - return err; - - return 0; -} - - -/* Probe function for touch screen driver */ -static int mrstouch_probe(struct platform_device *pdev) -{ - struct mrstouch_dev *tsdev; - struct input_dev *input; - int err; - int irq; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no interrupt assigned\n"); - return -EINVAL; - } - - tsdev = devm_kzalloc(&pdev->dev, sizeof(struct mrstouch_dev), - GFP_KERNEL); - if (!tsdev) { - dev_err(&pdev->dev, "unable to allocate memory\n"); - return -ENOMEM; - } - - input = devm_input_allocate_device(&pdev->dev); - if (!input) { - dev_err(&pdev->dev, "unable to allocate input device\n"); - return -ENOMEM; - } - - tsdev->dev = &pdev->dev; - tsdev->input = input; - tsdev->irq = irq; - - snprintf(tsdev->phys, sizeof(tsdev->phys), - "%s/input0", dev_name(tsdev->dev)); - - err = mrstouch_adc_init(tsdev); - if (err) { - dev_err(&pdev->dev, "ADC initialization failed\n"); - return err; - } - - input->name = "mrst_touchscreen"; - input->phys = tsdev->phys; - input->dev.parent = tsdev->dev; - - input->id.vendor = tsdev->vendor; - input->id.version = tsdev->rev; - - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_set_abs_params(tsdev->input, ABS_X, - MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0); - input_set_abs_params(tsdev->input, ABS_Y, - MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0); - input_set_abs_params(tsdev->input, ABS_PRESSURE, - MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0); - - err = devm_request_threaded_irq(&pdev->dev, tsdev->irq, NULL, - mrstouch_pendet_irq, IRQF_ONESHOT, - "mrstouch", tsdev); - if (err) { - dev_err(tsdev->dev, "unable to allocate irq\n"); - return err; - } - - err = input_register_device(tsdev->input); - if (err) { - dev_err(tsdev->dev, "unable to register input device\n"); - return err; - } - - return 0; -} - -static struct platform_driver mrstouch_driver = { - .driver = { - .name = "pmic_touch", - }, - .probe = mrstouch_probe, -}; -module_platform_driver(mrstouch_driver); - -MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com"); -MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c index 7fbb3b0c8571..e0baa7de4102 100644 --- a/drivers/input/touchscreen/lpc32xx_ts.c +++ b/drivers/input/touchscreen/lpc32xx_ts.c @@ -313,7 +313,6 @@ static int lpc32xx_ts_remove(struct platform_device *pdev) struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); struct resource *res; - device_init_wakeup(&pdev->dev, 0); free_irq(tsc->irq, tsc); input_unregister_device(tsc->dev); diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c index 82079cde849c..a595ae5284e3 100644 --- a/drivers/input/touchscreen/max11801_ts.c +++ b/drivers/input/touchscreen/max11801_ts.c @@ -199,7 +199,6 @@ static int max11801_ts_probe(struct i2c_client *client, __set_bit(BTN_TOUCH, input_dev->keybit); input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0); - input_set_drvdata(input_dev, data); max11801_ts_phy_init(data); @@ -216,7 +215,6 @@ static int max11801_ts_probe(struct i2c_client *client, if (error) return error; - i2c_set_clientdata(client, data); return 0; } diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 8b47e1fecb25..90fc07dc98a6 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -221,7 +221,6 @@ static int mcs5000_ts_probe(struct i2c_client *client, input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); - input_set_drvdata(input_dev, data); data->input_dev = input_dev; if (pdata->cfg_pin) diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 3bb0637d832e..37ff672c7802 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -461,7 +461,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, if (error) return error; } else { - dev_err(&client->dev, "platform data not defined\n"); + dev_err(dev, "platform data not defined\n"); return -EINVAL; } @@ -483,7 +483,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input->id.bustype = BUS_I2C; input->open = pixcir_input_open; input->close = pixcir_input_close; - input->dev.parent = &client->dev; + input->dev.parent = dev; if (pdata) { input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index 2658afa016c9..1252e49ccfa1 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -1087,8 +1087,6 @@ static int raydium_i2c_probe(struct i2c_client *client, ts->input->name = "Raydium Touchscreen"; ts->input->id.bustype = BUS_I2C; - input_set_drvdata(ts->input, ts); - input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, le16_to_cpu(ts->info.x_max), 0, 0); input_set_abs_params(ts->input, ABS_MT_POSITION_Y, diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c index 611156a2ef80..eeaf6ff03597 100644 --- a/drivers/input/touchscreen/rohm_bu21023.c +++ b/drivers/input/touchscreen/rohm_bu21023.c @@ -1189,8 +1189,7 @@ static int rohm_bu21023_i2c_probe(struct i2c_client *client, error = devm_add_action(dev, rohm_ts_remove_sysfs_group, dev); if (error) { rohm_ts_remove_sysfs_group(dev); - dev_err(&client->dev, - "Failed to add sysfs cleanup action: %d\n", + dev_err(dev, "Failed to add sysfs cleanup action: %d\n", error); return error; } diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index a4a103e1d11b..41d58e88cc8a 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -250,7 +250,7 @@ static int s3c2410ts_probe(struct platform_device *pdev) ts.dev = dev; - info = dev_get_platdata(&pdev->dev); + info = dev_get_platdata(dev); if (!info) { dev_err(dev, "no platform data, cannot attach\n"); return -EINVAL; diff --git a/drivers/input/touchscreen/sis_i2c.c b/drivers/input/touchscreen/sis_i2c.c index 8d93f8c9a403..67c2563031d6 100644 --- a/drivers/input/touchscreen/sis_i2c.c +++ b/drivers/input/touchscreen/sis_i2c.c @@ -316,7 +316,6 @@ static int sis_ts_probe(struct i2c_client *client, return -ENOMEM; ts->client = client; - i2c_set_clientdata(client, ts); ts->attn_gpio = devm_gpiod_get_optional(&client->dev, "attn", GPIOD_IN); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index e943678ce54c..be5615c6bf8f 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -237,7 +237,6 @@ static int st1232_ts_remove(struct i2c_client *client) { struct st1232_ts_data *ts = i2c_get_clientdata(client); - device_init_wakeup(&client->dev, 0); st1232_ts_power(ts, false); return 0; diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 642f4a53de50..ed29db3ec731 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -253,7 +253,6 @@ static int sx8654_probe(struct i2c_client *client, if (error) return error; - i2c_set_clientdata(client, sx8654); return 0; } diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index f2c5f0e47f77..e02b69f40ad8 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -18,8 +18,9 @@ * GNU General Public License for more details. */ -#include <linux/module.h> #include <linux/input.h> +#include <linux/module.h> +#include <linux/of.h> #include <linux/spi/spi.h> #include <linux/regmap.h> #include "tsc200x-core.h" @@ -77,9 +78,18 @@ static int tsc2005_remove(struct spi_device *spi) return tsc200x_remove(&spi->dev); } +#ifdef CONFIG_OF +static const struct of_device_id tsc2005_of_match[] = { + { .compatible = "ti,tsc2005" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tsc2005_of_match); +#endif + static struct spi_driver tsc2005_driver = { .driver = { .name = "tsc2005", + .of_match_table = of_match_ptr(tsc2005_of_match), .pm = &tsc200x_pm_ops, }, .probe = tsc2005_probe, diff --git a/drivers/input/touchscreen/tsc200x-core.c b/drivers/input/touchscreen/tsc200x-core.c index b7059ed8872e..88ea5e1b72ae 100644 --- a/drivers/input/touchscreen/tsc200x-core.c +++ b/drivers/input/touchscreen/tsc200x-core.c @@ -27,7 +27,6 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/of.h> -#include <linux/spi/tsc2005.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/gpio/consumer.h> @@ -114,7 +113,6 @@ struct tsc200x { struct regulator *vio; struct gpio_desc *reset_gpio; - void (*set_reset)(bool enable); int (*tsc200x_cmd)(struct device *dev, u8 cmd); int irq; }; @@ -227,12 +225,13 @@ static void tsc200x_stop_scan(struct tsc200x *ts) ts->tsc200x_cmd(ts->dev, TSC200X_CMD_STOP); } -static void tsc200x_set_reset(struct tsc200x *ts, bool enable) +static void tsc200x_reset(struct tsc200x *ts) { - if (ts->reset_gpio) - gpiod_set_value_cansleep(ts->reset_gpio, enable); - else if (ts->set_reset) - ts->set_reset(enable); + if (ts->reset_gpio) { + gpiod_set_value_cansleep(ts->reset_gpio, 1); + usleep_range(100, 500); /* only 10us required */ + gpiod_set_value_cansleep(ts->reset_gpio, 0); + } } /* must be called with ts->mutex held */ @@ -253,7 +252,7 @@ static void __tsc200x_enable(struct tsc200x *ts) { tsc200x_start_scan(ts); - if (ts->esd_timeout && (ts->set_reset || ts->reset_gpio)) { + if (ts->esd_timeout && ts->reset_gpio) { ts->last_valid_interrupt = jiffies; schedule_delayed_work(&ts->esd_work, round_jiffies_relative( @@ -310,9 +309,7 @@ static ssize_t tsc200x_selftest_show(struct device *dev, } /* hardware reset */ - tsc200x_set_reset(ts, false); - usleep_range(100, 500); /* only 10us required */ - tsc200x_set_reset(ts, true); + tsc200x_reset(ts); if (!success) goto out; @@ -354,7 +351,7 @@ static umode_t tsc200x_attr_is_visible(struct kobject *kobj, umode_t mode = attr->mode; if (attr == &dev_attr_selftest.attr) { - if (!ts->set_reset && !ts->reset_gpio) + if (!ts->reset_gpio) mode = 0; } @@ -404,9 +401,7 @@ static void tsc200x_esd_work(struct work_struct *work) tsc200x_update_pen_state(ts, 0, 0, 0); - tsc200x_set_reset(ts, false); - usleep_range(100, 500); /* only 10us required */ - tsc200x_set_reset(ts, true); + tsc200x_reset(ts); enable_irq(ts->irq); tsc200x_start_scan(ts); @@ -454,26 +449,12 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, struct regmap *regmap, int (*tsc200x_cmd)(struct device *dev, u8 cmd)) { - const struct tsc2005_platform_data *pdata = dev_get_platdata(dev); - struct device_node *np = dev->of_node; - struct tsc200x *ts; struct input_dev *input_dev; - unsigned int max_x = MAX_12BIT; - unsigned int max_y = MAX_12BIT; - unsigned int max_p = MAX_12BIT; - unsigned int fudge_x = TSC200X_DEF_X_FUZZ; - unsigned int fudge_y = TSC200X_DEF_Y_FUZZ; - unsigned int fudge_p = TSC200X_DEF_P_FUZZ; - unsigned int x_plate_ohm = TSC200X_DEF_RESISTOR; - unsigned int esd_timeout; + u32 x_plate_ohm; + u32 esd_timeout; int error; - if (!np && !pdata) { - dev_err(dev, "no platform data\n"); - return -ENODEV; - } - if (irq <= 0) { dev_err(dev, "no irq\n"); return -ENODEV; @@ -487,23 +468,6 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, return -ENODEV; } - if (pdata) { - fudge_x = pdata->ts_x_fudge; - fudge_y = pdata->ts_y_fudge; - fudge_p = pdata->ts_pressure_fudge; - max_x = pdata->ts_x_max; - max_y = pdata->ts_y_max; - max_p = pdata->ts_pressure_max; - x_plate_ohm = pdata->ts_x_plate_ohm; - esd_timeout = pdata->esd_timeout_ms; - } else { - x_plate_ohm = TSC200X_DEF_RESISTOR; - of_property_read_u32(np, "ti,x-plate-ohms", &x_plate_ohm); - esd_timeout = 0; - of_property_read_u32(np, "ti,esd-recovery-timeout-ms", - &esd_timeout); - } - ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; @@ -517,8 +481,13 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, ts->idev = input_dev; ts->regmap = regmap; ts->tsc200x_cmd = tsc200x_cmd; - ts->x_plate_ohm = x_plate_ohm; - ts->esd_timeout = esd_timeout; + + error = device_property_read_u32(dev, "ti,x-plate-ohms", &x_plate_ohm); + ts->x_plate_ohm = error ? TSC200X_DEF_RESISTOR : x_plate_ohm; + + error = device_property_read_u32(dev, "ti,esd-recovery-timeout-ms", + &esd_timeout); + ts->esd_timeout = error ? 0 : esd_timeout; ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ts->reset_gpio)) { @@ -527,16 +496,13 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, return error; } - ts->vio = devm_regulator_get_optional(dev, "vio"); + ts->vio = devm_regulator_get(dev, "vio"); if (IS_ERR(ts->vio)) { error = PTR_ERR(ts->vio); - dev_err(dev, "vio regulator missing (%d)", error); + dev_err(dev, "error acquiring vio regulator: %d", error); return error; } - if (!ts->reset_gpio && pdata) - ts->set_reset = pdata->set_reset; - mutex_init(&ts->mutex); spin_lock_init(&ts->lock); @@ -559,22 +525,23 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, input_dev->phys = ts->phys; input_dev->id = *tsc_id; - input_dev->dev.parent = dev; - input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0); - input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); - - if (np) - touchscreen_parse_properties(input_dev, false, NULL); input_dev->open = tsc200x_open; input_dev->close = tsc200x_close; input_set_drvdata(input_dev, ts); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, + 0, MAX_12BIT, TSC200X_DEF_X_FUZZ, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, MAX_12BIT, TSC200X_DEF_Y_FUZZ, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, MAX_12BIT, TSC200X_DEF_P_FUZZ, 0); + + touchscreen_parse_properties(input_dev, false, NULL); + /* Ensure the touchscreen is off */ tsc200x_stop_scan(ts); @@ -587,12 +554,9 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, return error; } - /* enable regulator for DT */ - if (ts->vio) { - error = regulator_enable(ts->vio); - if (error) - return error; - } + error = regulator_enable(ts->vio); + if (error) + return error; dev_set_drvdata(dev, ts); error = sysfs_create_group(&dev->kobj, &tsc200x_attr_group); @@ -615,8 +579,7 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, err_remove_sysfs: sysfs_remove_group(&dev->kobj, &tsc200x_attr_group); disable_regulator: - if (ts->vio) - regulator_disable(ts->vio); + regulator_disable(ts->vio); return error; } EXPORT_SYMBOL_GPL(tsc200x_probe); @@ -627,8 +590,7 @@ int tsc200x_remove(struct device *dev) sysfs_remove_group(&dev->kobj, &tsc200x_attr_group); - if (ts->vio) - regulator_disable(ts->vio); + regulator_disable(ts->vio); return 0; } diff --git a/drivers/input/touchscreen/zet6223.c b/drivers/input/touchscreen/zet6223.c new file mode 100644 index 000000000000..19ffcc7b886c --- /dev/null +++ b/drivers/input/touchscreen/zet6223.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2016, Jelle van der Waa <jelle@vdwaa.nl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <asm/unaligned.h> + +#define ZET6223_MAX_FINGERS 16 +#define ZET6223_MAX_PKT_SIZE (3 + 4 * ZET6223_MAX_FINGERS) + +#define ZET6223_CMD_INFO 0xB2 +#define ZET6223_CMD_INFO_LENGTH 17 +#define ZET6223_VALID_PACKET 0x3c + +#define ZET6223_POWER_ON_DELAY_MSEC 30 + +struct zet6223_ts { + struct i2c_client *client; + struct input_dev *input; + struct regulator *vcc; + struct regulator *vio; + struct touchscreen_properties prop; + struct regulator_bulk_data supplies[2]; + u16 max_x; + u16 max_y; + u8 fingernum; +}; + +static int zet6223_start(struct input_dev *dev) +{ + struct zet6223_ts *ts = input_get_drvdata(dev); + + enable_irq(ts->client->irq); + + return 0; +} + +static void zet6223_stop(struct input_dev *dev) +{ + struct zet6223_ts *ts = input_get_drvdata(dev); + + disable_irq(ts->client->irq); +} + +static irqreturn_t zet6223_irq(int irq, void *dev_id) +{ + struct zet6223_ts *ts = dev_id; + u16 finger_bits; + + /* + * First 3 bytes are an identifier, two bytes of finger data. + * X, Y data per finger is 4 bytes. + */ + u8 bufsize = 3 + 4 * ts->fingernum; + u8 buf[ZET6223_MAX_PKT_SIZE]; + int i; + int ret; + int error; + + ret = i2c_master_recv(ts->client, buf, bufsize); + if (ret != bufsize) { + error = ret < 0 ? ret : -EIO; + dev_err_ratelimited(&ts->client->dev, + "Error reading input data: %d\n", error); + return IRQ_HANDLED; + } + + if (buf[0] != ZET6223_VALID_PACKET) + return IRQ_HANDLED; + + finger_bits = get_unaligned_be16(buf + 1); + for (i = 0; i < ts->fingernum; i++) { + if (!(finger_bits & BIT(15 - i))) + continue; + + input_mt_slot(ts->input, i); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); + input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, + ((buf[i + 3] >> 4) << 8) + buf[i + 4]); + input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, + ((buf[i + 3] & 0xF) << 8) + buf[i + 5]); + } + + input_mt_sync_frame(ts->input); + input_sync(ts->input); + + return IRQ_HANDLED; +} + +static void zet6223_power_off(void *_ts) +{ + struct zet6223_ts *ts = _ts; + + regulator_bulk_disable(ARRAY_SIZE(ts->supplies), ts->supplies); +} + +static int zet6223_power_on(struct zet6223_ts *ts) +{ + struct device *dev = &ts->client->dev; + int error; + + ts->supplies[0].supply = "vio"; + ts->supplies[1].supply = "vcc"; + + error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->supplies), + ts->supplies); + if (error) + return error; + + error = regulator_bulk_enable(ARRAY_SIZE(ts->supplies), ts->supplies); + if (error) + return error; + + msleep(ZET6223_POWER_ON_DELAY_MSEC); + + error = devm_add_action_or_reset(dev, zet6223_power_off, ts); + if (error) { + dev_err(dev, "failed to install poweroff action: %d\n", error); + return error; + } + + return 0; +} + +static int zet6223_query_device(struct zet6223_ts *ts) +{ + u8 buf[ZET6223_CMD_INFO_LENGTH]; + u8 cmd = ZET6223_CMD_INFO; + int ret; + int error; + + ret = i2c_master_send(ts->client, &cmd, sizeof(cmd)); + if (ret != sizeof(cmd)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "touchpanel info cmd failed: %d\n", error); + return error; + } + + ret = i2c_master_recv(ts->client, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "failed to retrieve touchpanel info: %d\n", error); + return error; + } + + ts->fingernum = buf[15] & 0x7F; + if (ts->fingernum > ZET6223_MAX_FINGERS) { + dev_warn(&ts->client->dev, + "touchpanel reports %d fingers, limiting to %d\n", + ts->fingernum, ZET6223_MAX_FINGERS); + ts->fingernum = ZET6223_MAX_FINGERS; + } + + ts->max_x = get_unaligned_le16(&buf[8]); + ts->max_y = get_unaligned_le16(&buf[10]); + + return 0; +} + +static int zet6223_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct zet6223_ts *ts; + struct input_dev *input; + int error; + + if (!client->irq) { + dev_err(dev, "no irq specified\n"); + return -EINVAL; + } + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->client = client; + + error = zet6223_power_on(ts); + if (error) + return error; + + error = zet6223_query_device(ts); + if (error) + return error; + + ts->input = input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input_set_drvdata(input, ts); + + input->name = client->name; + input->id.bustype = BUS_I2C; + input->open = zet6223_start; + input->close = zet6223_stop; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); + + touchscreen_parse_properties(input, true, &ts->prop); + + error = input_mt_init_slots(input, ts->fingernum, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + error = devm_request_threaded_irq(dev, client->irq, NULL, zet6223_irq, + IRQF_ONESHOT, client->name, ts); + if (error) { + dev_err(dev, "failed to request irq %d: %d\n", + client->irq, error); + return error; + } + + zet6223_stop(input); + + error = input_register_device(input); + if (error) + return error; + + return 0; +} + +static const struct of_device_id zet6223_of_match[] = { + { .compatible = "zeitec,zet6223" }, + { } +}; +MODULE_DEVICE_TABLE(of, zet6223_of_match); + +static const struct i2c_device_id zet6223_id[] = { + { "zet6223", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, zet6223_id); + +static struct i2c_driver zet6223_driver = { + .driver = { + .name = "zet6223", + .of_match_table = zet6223_of_match, + }, + .probe = zet6223_probe, + .id_table = zet6223_id +}; +module_i2c_driver(zet6223_driver); + +MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>"); +MODULE_DESCRIPTION("ZEITEC zet622x I2C touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 1b5b8c5361c5..98940d1392cb 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -117,7 +117,7 @@ const struct iommu_ops amd_iommu_ops; static ATOMIC_NOTIFIER_HEAD(ppr_notifier); int amd_iommu_max_glx_val = -1; -static struct dma_map_ops amd_iommu_dma_ops; +static const struct dma_map_ops amd_iommu_dma_ops; /* * This struct contains device specific data for the IOMMU @@ -519,7 +519,7 @@ static void iommu_uninit_device(struct device *dev) iommu_group_remove_device(dev); /* Remove dma-ops */ - dev->archdata.dma_ops = NULL; + dev->dma_ops = NULL; /* * We keep dev_data around for unplugged devices and reuse it when the @@ -2168,7 +2168,7 @@ static int amd_iommu_add_device(struct device *dev) dev_name(dev)); iommu_ignore_device(dev); - dev->archdata.dma_ops = &nommu_dma_ops; + dev->dma_ops = &nommu_dma_ops; goto out; } init_iommu_group(dev); @@ -2185,7 +2185,7 @@ static int amd_iommu_add_device(struct device *dev) if (domain->type == IOMMU_DOMAIN_IDENTITY) dev_data->passthrough = true; else - dev->archdata.dma_ops = &amd_iommu_dma_ops; + dev->dma_ops = &amd_iommu_dma_ops; out: iommu_completion_wait(iommu); @@ -2672,7 +2672,7 @@ static void *alloc_coherent(struct device *dev, size_t size, return NULL; page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT, - get_order(size)); + get_order(size), flag); if (!page) return NULL; } @@ -2732,7 +2732,7 @@ static int amd_iommu_dma_supported(struct device *dev, u64 mask) return check_device(dev); } -static struct dma_map_ops amd_iommu_dma_ops = { +static const struct dma_map_ops amd_iommu_dma_ops = { .alloc = alloc_coherent, .free = free_coherent, .map_page = map_page, diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 04cdac7ab3e3..6130278c5d71 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1507,7 +1507,7 @@ static ssize_t amd_iommu_show_cap(struct device *dev, struct device_attribute *attr, char *buf) { - struct amd_iommu *iommu = dev_get_drvdata(dev); + struct amd_iommu *iommu = dev_to_amd_iommu(dev); return sprintf(buf, "%x\n", iommu->cap); } static DEVICE_ATTR(cap, S_IRUGO, amd_iommu_show_cap, NULL); @@ -1516,7 +1516,7 @@ static ssize_t amd_iommu_show_features(struct device *dev, struct device_attribute *attr, char *buf) { - struct amd_iommu *iommu = dev_get_drvdata(dev); + struct amd_iommu *iommu = dev_to_amd_iommu(dev); return sprintf(buf, "%llx\n", iommu->features); } static DEVICE_ATTR(features, S_IRUGO, amd_iommu_show_features, NULL); diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index af00f381a7b1..003f3ceb2661 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -569,6 +569,11 @@ struct amd_iommu { volatile u64 __aligned(8) cmd_sem; }; +static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev) +{ + return container_of(dev, struct amd_iommu, iommu.dev); +} + #define ACPIHID_UID_LEN 256 #define ACPIHID_HID_LEN 9 diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index f8ed8c95b685..063343909b0d 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -22,6 +22,7 @@ #include <linux/profile.h> #include <linux/module.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/iommu.h> #include <linux/wait.h> #include <linux/pci.h> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f5e02f8e7371..238ad3447712 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3829,7 +3829,7 @@ static void *intel_alloc_coherent(struct device *dev, size_t size, if (gfpflags_allow_blocking(flags)) { unsigned int count = size >> PAGE_SHIFT; - page = dma_alloc_from_contiguous(dev, count, order); + page = dma_alloc_from_contiguous(dev, count, order, flags); if (page && iommu_no_mapping(dev) && page_to_phys(page) + size > dev->coherent_dma_mask) { dma_release_from_contiguous(dev, page, count); @@ -4730,11 +4730,16 @@ static int intel_iommu_cpu_dead(unsigned int cpu) return 0; } +static inline struct intel_iommu *dev_to_intel_iommu(struct device *dev) +{ + return container_of(dev, struct intel_iommu, iommu.dev); +} + static ssize_t intel_iommu_show_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_iommu *iommu = dev_get_drvdata(dev); + struct intel_iommu *iommu = dev_to_intel_iommu(dev); u32 ver = readl(iommu->reg + DMAR_VER_REG); return sprintf(buf, "%d:%d\n", DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver)); @@ -4745,7 +4750,7 @@ static ssize_t intel_iommu_show_address(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_iommu *iommu = dev_get_drvdata(dev); + struct intel_iommu *iommu = dev_to_intel_iommu(dev); return sprintf(buf, "%llx\n", iommu->reg_phys); } static DEVICE_ATTR(address, S_IRUGO, intel_iommu_show_address, NULL); @@ -4754,7 +4759,7 @@ static ssize_t intel_iommu_show_cap(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_iommu *iommu = dev_get_drvdata(dev); + struct intel_iommu *iommu = dev_to_intel_iommu(dev); return sprintf(buf, "%llx\n", iommu->cap); } static DEVICE_ATTR(cap, S_IRUGO, intel_iommu_show_cap, NULL); @@ -4763,7 +4768,7 @@ static ssize_t intel_iommu_show_ecap(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_iommu *iommu = dev_get_drvdata(dev); + struct intel_iommu *iommu = dev_to_intel_iommu(dev); return sprintf(buf, "%llx\n", iommu->ecap); } static DEVICE_ATTR(ecap, S_IRUGO, intel_iommu_show_ecap, NULL); @@ -4772,7 +4777,7 @@ static ssize_t intel_iommu_show_ndoms(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_iommu *iommu = dev_get_drvdata(dev); + struct intel_iommu *iommu = dev_to_intel_iommu(dev); return sprintf(buf, "%ld\n", cap_ndoms(iommu->cap)); } static DEVICE_ATTR(domains_supported, S_IRUGO, intel_iommu_show_ndoms, NULL); @@ -4781,7 +4786,7 @@ static ssize_t intel_iommu_show_ndoms_used(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_iommu *iommu = dev_get_drvdata(dev); + struct intel_iommu *iommu = dev_to_intel_iommu(dev); return sprintf(buf, "%d\n", bitmap_weight(iommu->domain_ids, cap_ndoms(iommu->cap))); } diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index cb72e0011310..23c427602c55 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -16,6 +16,7 @@ #include <linux/intel-iommu.h> #include <linux/mmu_notifier.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/slab.h> #include <linux/intel-svm.h> #include <linux/rculist.h> @@ -579,7 +580,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) if (!svm->mm) goto bad_req; /* If the mm is already defunct, don't handle faults. */ - if (!atomic_inc_not_zero(&svm->mm->mm_users)) + if (!mmget_not_zero(svm->mm)) goto bad_req; down_read(&svm->mm->mmap_sem); vma = find_extend_vma(svm->mm, address); diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index 49d0f70c2bae..1dfd1085a04f 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c @@ -18,7 +18,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/proc_fs.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/seq_file.h> #include <linux/skbuff.h> #include <linux/workqueue.h> diff --git a/drivers/isdn/hardware/eicon/debug.c b/drivers/isdn/hardware/eicon/debug.c index 576b7b4a3278..8bc2791bc39c 100644 --- a/drivers/isdn/hardware/eicon/debug.c +++ b/drivers/isdn/hardware/eicon/debug.c @@ -2049,7 +2049,7 @@ static int diva_dbg_cmp_key(const char *ref, const char *key) { /* In case trace filter starts with "C" character then all following characters are interpreted as command. - Followings commands are available: + Following commands are available: - single, trace single call at time, independent from CPN/CiPN */ static int diva_mnt_cmp_nmbr(const char *nmbr) { diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c index 8d338ba366d0..77dec28ba874 100644 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -1625,7 +1625,7 @@ mISDNipac_init(struct ipac_hw *ipac, void *hw) ipac->hscx[i].bch.hw = hw; ipac->hscx[i].ip = ipac; /* default values for IOM time slots - * can be overwriten by card */ + * can be overwritten by card */ ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03; } diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 63eaa0a9f8a1..1b169559a240 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/mutex.h> +#include <linux/sched/signal.h> #include "isdn_common.h" #include "isdn_tty.h" #ifdef CONFIG_ISDN_AUDIO diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 0222b1a35a2d..9b85295aa657 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -115,7 +115,7 @@ * * The CMX has special functions for conferences with one, two and more * members. It will allow different types of data flow. Receive and transmit - * data to/form upper layer may be swithed on/off individually without losing + * data to/form upper layer may be switched on/off individually without losing * features of CMX, Tones and DTMF. * * Echo Cancellation: Sometimes we like to cancel echo from the interface. diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 67c21876c35f..6ceca7db62ad 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -234,6 +234,8 @@ #include <linux/workqueue.h> #include <linux/kthread.h> #include <linux/slab.h> +#include <linux/sched/signal.h> + #include <net/sock.h> #include "core.h" #include "l1oip.h" diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index b324474c0c12..8b7faea2ddf8 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -19,6 +19,9 @@ #include <linux/mISDNif.h> #include <linux/kthread.h> #include <linux/sched.h> +#include <linux/sched/cputime.h> +#include <linux/signal.h> + #include "core.h" static u_int *debug; diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c index 9438d7ec3308..b1e135fc1fb5 100644 --- a/drivers/isdn/mISDN/timerdev.c +++ b/drivers/isdn/mISDN/timerdev.c @@ -25,6 +25,8 @@ #include <linux/module.h> #include <linux/mISDNif.h> #include <linux/mutex.h> +#include <linux/sched/signal.h> + #include "core.h" static DEFINE_MUTEX(mISDN_mutex); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index d400dcaf4d29..066fc7590729 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -174,12 +174,6 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) const char *state = NULL; struct device_node *np = to_of_node(child); - led.gpiod = devm_get_gpiod_from_child(dev, NULL, child); - if (IS_ERR(led.gpiod)) { - fwnode_handle_put(child); - return ERR_CAST(led.gpiod); - } - ret = fwnode_property_read_string(child, "label", &led.name); if (ret && IS_ENABLED(CONFIG_OF) && np) led.name = np->name; @@ -188,6 +182,14 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) return ERR_PTR(-EINVAL); } + led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child, + GPIOD_ASIS, + led.name); + if (IS_ERR(led.gpiod)) { + fwnode_handle_put(child); + return ERR_CAST(led.gpiod); + } + fwnode_property_read_string(child, "linux,default-trigger", &led.default_trigger); diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index a9145aa7f36a..8d456dc6c5bf 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -29,7 +29,6 @@ struct led_pwm_data { unsigned int active_low; unsigned int period; int duty; - bool can_sleep; }; struct led_pwm_priv { @@ -49,8 +48,8 @@ static void __led_pwm_set(struct led_pwm_data *led_dat) pwm_enable(led_dat->pwm); } -static void led_pwm_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static int led_pwm_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { struct led_pwm_data *led_dat = container_of(led_cdev, struct led_pwm_data, cdev); @@ -66,12 +65,7 @@ static void led_pwm_set(struct led_classdev *led_cdev, led_dat->duty = duty; __led_pwm_set(led_dat); -} -static int led_pwm_set_blocking(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - led_pwm_set(led_cdev, brightness); return 0; } @@ -112,11 +106,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, return ret; } - led_data->can_sleep = pwm_can_sleep(led_data->pwm); - if (!led_data->can_sleep) - led_data->cdev.brightness_set = led_pwm_set; - else - led_data->cdev.brightness_set_blocking = led_pwm_set_blocking; + led_data->cdev.brightness_set_blocking = led_pwm_set; /* * FIXME: pwm_apply_args() should be removed when switching to the diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c index e6f2f8b9f09a..afa3b4099214 100644 --- a/drivers/leds/trigger/ledtrig-heartbeat.c +++ b/drivers/leds/trigger/ledtrig-heartbeat.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/timer.h> #include <linux/sched.h> +#include <linux/sched/loadavg.h> #include <linux/leds.h> #include <linux/reboot.h> #include <linux/suspend.h> diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index ac219045daf7..395ed1961dbf 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -8,6 +8,7 @@ #include <linux/stddef.h> #include <linux/io.h> #include <linux/mm.h> +#include <linux/sched/signal.h> #include <linux/vmalloc.h> #include <linux/cpu.h> #include <linux/freezer.h> diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 30c60687d277..1a6787bc9386 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -8,6 +8,7 @@ #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/file.h> #include <linux/slab.h> #include <linux/export.h> diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 152414e6378a..fee939efc4fc 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -23,7 +23,7 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/mm.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/adb.h> #include <linux/cuda.h> #include <linux/pmu.h> diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 227869159ac0..1ac66421877a 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -39,6 +39,7 @@ #include <linux/of_platform.h> #include <linux/slab.h> #include <linux/memblock.h> +#include <linux/sched/signal.h> #include <asm/byteorder.h> #include <asm/io.h> diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 43b8db2b5445..cce99f72e4ae 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -23,7 +23,7 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/delay.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/miscdevice.h> #include <linux/blkdev.h> #include <linux/pci.h> diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index 9c79f8019d2a..97fb956bb6e0 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -21,6 +21,7 @@ #include <linux/poll.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include <linux/sched/signal.h> #define MBOX_MAX_SIG_LEN 8 #define MBOX_MAX_MSG_LEN 128 diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c index 646fe85261c1..18526d44688d 100644 --- a/drivers/md/bcache/bset.c +++ b/drivers/md/bcache/bset.c @@ -11,6 +11,7 @@ #include "bset.h" #include <linux/console.h> +#include <linux/sched/clock.h> #include <linux/random.h> #include <linux/prefetch.h> diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index a43eedd5804d..450d0e848ae4 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -32,6 +32,9 @@ #include <linux/prefetch.h> #include <linux/random.h> #include <linux/rcupdate.h> +#include <linux/sched/clock.h> +#include <linux/rculist.h> + #include <trace/events/bcache.h> /* diff --git a/drivers/md/bcache/closure.h b/drivers/md/bcache/closure.h index 9b2fe2d3e3a9..1ec84ca81146 100644 --- a/drivers/md/bcache/closure.h +++ b/drivers/md/bcache/closure.h @@ -3,6 +3,7 @@ #include <linux/llist.h> #include <linux/sched.h> +#include <linux/sched/task_stack.h> #include <linux/workqueue.h> /* diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index b3ff57d61dde..f90f13616980 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -13,6 +13,7 @@ #include <linux/blkdev.h> #include <linux/sort.h> +#include <linux/sched/clock.h> static const char * const cache_replacement_policies[] = { "lru", diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c index dde6172f3f10..8c3a938f4bf0 100644 --- a/drivers/md/bcache/util.c +++ b/drivers/md/bcache/util.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/seq_file.h> #include <linux/types.h> +#include <linux/sched/clock.h> #include "util.h" diff --git a/drivers/md/bcache/util.h b/drivers/md/bcache/util.h index cf2cbc211d83..a126919ed102 100644 --- a/drivers/md/bcache/util.h +++ b/drivers/md/bcache/util.h @@ -6,6 +6,7 @@ #include <linux/errno.h> #include <linux/blkdev.h> #include <linux/kernel.h> +#include <linux/sched/clock.h> #include <linux/llist.h> #include <linux/ratelimit.h> #include <linux/vmalloc.h> diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 69e1ae59cab8..6ac2e48b9235 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/kthread.h> +#include <linux/sched/clock.h> #include <trace/events/bcache.h> /* Rate limiting */ diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index d36d427a9efb..df4859f6ac6a 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -11,6 +11,7 @@ #include <linux/device-mapper.h> #include <linux/dm-io.h> #include <linux/slab.h> +#include <linux/sched/mm.h> #include <linux/jiffies.h> #include <linux/vmalloc.h> #include <linux/shrinker.h> diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 1cb2ca9dfae3..389a3637ffcc 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1536,7 +1536,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string down_read(&key->sem); - ukp = user_key_payload(key); + ukp = user_key_payload_locked(key); if (!ukp) { up_read(&key->sem); key_put(key); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a5a9b17f0f7f..4da6fc6b1ffd 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/vmalloc.h> #include <linux/miscdevice.h> +#include <linux/sched/mm.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/slab.h> diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 5c9e95d66f3b..f8564d63982f 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -101,6 +101,8 @@ struct raid_dev { #define CTR_FLAG_RAID10_USE_NEAR_SETS (1 << __CTR_FLAG_RAID10_USE_NEAR_SETS) #define CTR_FLAG_JOURNAL_DEV (1 << __CTR_FLAG_JOURNAL_DEV) +#define RESUME_STAY_FROZEN_FLAGS (CTR_FLAG_DELTA_DISKS | CTR_FLAG_DATA_OFFSET) + /* * Definitions of various constructor flags to * be used in checks of valid / invalid flags @@ -3462,9 +3464,11 @@ static int raid_message(struct dm_target *ti, unsigned int argc, char **argv) else if (!strcasecmp(argv[0], "recover")) set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); else { - if (!strcasecmp(argv[0], "check")) + if (!strcasecmp(argv[0], "check")) { set_bit(MD_RECOVERY_CHECK, &mddev->recovery); - else if (!strcasecmp(argv[0], "repair")) { + set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery); + set_bit(MD_RECOVERY_SYNC, &mddev->recovery); + } else if (!strcasecmp(argv[0], "repair")) { set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery); set_bit(MD_RECOVERY_SYNC, &mddev->recovery); } else @@ -3771,7 +3775,15 @@ static void raid_resume(struct dm_target *ti) mddev->ro = 0; mddev->in_sync = 0; - clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); + /* + * Keep the RAID set frozen if reshape/rebuild flags are set. + * The RAID set is unfrozen once the next table load/resume, + * which clears the reshape/rebuild flags, occurs. + * This ensures that the constructor for the inactive table + * retrieves an up-to-date reshape_position. + */ + if (!(rs->ctr_flags & RESUME_STAY_FROZEN_FLAGS)) + clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); if (mddev->suspended) mddev_resume(mddev); @@ -3779,7 +3791,7 @@ static void raid_resume(struct dm_target *ti) static struct target_type raid_target = { .name = "raid", - .version = {1, 10, 0}, + .version = {1, 10, 1}, .module = THIS_MODULE, .ctr = raid_ctr, .dtr = raid_dtr, diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c index 67d76f21fecd..28955b94d2b2 100644 --- a/drivers/md/dm-rq.c +++ b/drivers/md/dm-rq.c @@ -328,13 +328,15 @@ static void dm_softirq_done(struct request *rq) int rw; if (!clone) { - rq_end_stats(tio->md, rq); + struct mapped_device *md = tio->md; + + rq_end_stats(md, rq); rw = rq_data_dir(rq); if (!rq->q->mq_ops) blk_end_request_all(rq, tio->error); else blk_mq_end_request(rq, tio->error); - rq_completed(tio->md, rw, false); + rq_completed(md, rw, false); return; } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 9f37d7fc2786..f4ffd1eb8f44 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/sched/signal.h> #include <linux/blkpg.h> #include <linux/bio.h> #include <linux/mempool.h> diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index 685aa2d77e25..b0536cfd8e17 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -214,7 +214,7 @@ static void faulty_make_request(struct mddev *mddev, struct bio *bio) } } if (failit) { - struct bio *b = bio_clone_mddev(bio, GFP_NOIO, mddev); + struct bio *b = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); b->bi_bdev = conf->rdev->bdev; b->bi_private = bio; diff --git a/drivers/md/linear.c b/drivers/md/linear.c index f1c7bbac31a5..3e38e0207a3e 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -53,18 +53,26 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) return conf->disks + lo; } +/* + * In linear_congested() conf->raid_disks is used as a copy of + * mddev->raid_disks to iterate conf->disks[], because conf->raid_disks + * and conf->disks[] are created in linear_conf(), they are always + * consitent with each other, but mddev->raid_disks does not. + */ static int linear_congested(struct mddev *mddev, int bits) { struct linear_conf *conf; int i, ret = 0; - conf = mddev->private; + rcu_read_lock(); + conf = rcu_dereference(mddev->private); - for (i = 0; i < mddev->raid_disks && !ret ; i++) { + for (i = 0; i < conf->raid_disks && !ret ; i++) { struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); ret |= bdi_congested(q->backing_dev_info, bits); } + rcu_read_unlock(); return ret; } @@ -144,6 +152,19 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) conf->disks[i-1].end_sector + conf->disks[i].rdev->sectors; + /* + * conf->raid_disks is copy of mddev->raid_disks. The reason to + * keep a copy of mddev->raid_disks in struct linear_conf is, + * mddev->raid_disks may not be consistent with pointers number of + * conf->disks[] when it is updated in linear_add() and used to + * iterate old conf->disks[] earray in linear_congested(). + * Here conf->raid_disks is always consitent with number of + * pointers in conf->disks[] array, and mddev->private is updated + * with rcu_assign_pointer() in linear_addr(), such race can be + * avoided. + */ + conf->raid_disks = raid_disks; + return conf; out: @@ -196,15 +217,24 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev) if (!newconf) return -ENOMEM; + /* newconf->raid_disks already keeps a copy of * the increased + * value of mddev->raid_disks, WARN_ONCE() is just used to make + * sure of this. It is possible that oldconf is still referenced + * in linear_congested(), therefore kfree_rcu() is used to free + * oldconf until no one uses it anymore. + */ mddev_suspend(mddev); - oldconf = mddev->private; + oldconf = rcu_dereference_protected(mddev->private, + lockdep_is_held(&mddev->reconfig_mutex)); mddev->raid_disks++; - mddev->private = newconf; + WARN_ONCE(mddev->raid_disks != newconf->raid_disks, + "copied raid_disks doesn't match mddev->raid_disks"); + rcu_assign_pointer(mddev->private, newconf); md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); set_capacity(mddev->gendisk, mddev->array_sectors); mddev_resume(mddev); revalidate_disk(mddev->gendisk); - kfree(oldconf); + kfree_rcu(oldconf, rcu); return 0; } @@ -262,6 +292,7 @@ static void linear_make_request(struct mddev *mddev, struct bio *bio) trace_block_bio_remap(bdev_get_queue(split->bi_bdev), split, disk_devt(mddev->gendisk), bio_sector); + mddev_check_writesame(mddev, split); generic_make_request(split); } } while (split != bio); diff --git a/drivers/md/linear.h b/drivers/md/linear.h index b685ddd7d7f7..8d392e6098b3 100644 --- a/drivers/md/linear.h +++ b/drivers/md/linear.h @@ -10,6 +10,7 @@ struct linear_conf { struct rcu_head rcu; sector_t array_sectors; + int raid_disks; /* a copy of mddev->raid_disks */ struct dev_info disks[0]; }; #endif diff --git a/drivers/md/md.c b/drivers/md/md.c index ba485dcf1064..548d1b8014f8 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -44,6 +44,7 @@ */ +#include <linux/sched/signal.h> #include <linux/kthread.h> #include <linux/blkdev.h> #include <linux/badblocks.h> @@ -190,16 +191,6 @@ struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, } EXPORT_SYMBOL_GPL(bio_alloc_mddev); -struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask, - struct mddev *mddev) -{ - if (!mddev || !mddev->bio_set) - return bio_clone(bio, gfp_mask); - - return bio_clone_bioset(bio, gfp_mask, mddev->bio_set); -} -EXPORT_SYMBOL_GPL(bio_clone_mddev); - /* * We have a system wide 'event count' that is incremented * on any 'interesting' event, and readers of /proc/mdstat @@ -5228,8 +5219,11 @@ int md_run(struct mddev *mddev) sysfs_notify_dirent_safe(rdev->sysfs_state); } - if (mddev->bio_set == NULL) + if (mddev->bio_set == NULL) { mddev->bio_set = bioset_create(BIO_POOL_SIZE, 0); + if (!mddev->bio_set) + return -ENOMEM; + } spin_lock(&pers_lock); pers = find_pers(mddev->level, mddev->clevel); @@ -8980,7 +8974,14 @@ static __exit void md_exit(void) for_each_mddev(mddev, tmp) { export_array(mddev); + mddev->ctime = 0; mddev->hold_active = 0; + /* + * for_each_mddev() will call mddev_put() at the end of each + * iteration. As the mddev is now fully clear, this will + * schedule the mddev for destruction by a workqueue, and the + * destroy_workqueue() below will wait for that to complete. + */ } destroy_workqueue(md_misc_wq); destroy_workqueue(md_wq); diff --git a/drivers/md/md.h b/drivers/md/md.h index 2a514036a83d..b8859cbf84b6 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -673,8 +673,6 @@ extern void md_rdev_clear(struct md_rdev *rdev); extern void mddev_suspend(struct mddev *mddev); extern void mddev_resume(struct mddev *mddev); -extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask, - struct mddev *mddev); extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, struct mddev *mddev); @@ -710,4 +708,11 @@ static inline void mddev_clear_unsupported_flags(struct mddev *mddev, { mddev->flags &= ~unsupported_flags; } + +static inline void mddev_check_writesame(struct mddev *mddev, struct bio *bio) +{ + if (bio_op(bio) == REQ_OP_WRITE_SAME && + !bdev_get_queue(bio->bi_bdev)->limits.max_write_same_sectors) + mddev->queue->limits.max_write_same_sectors = 0; +} #endif /* _MD_MD_H */ diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index d457afa672d5..79a12b59250b 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -138,6 +138,7 @@ static void multipath_make_request(struct mddev *mddev, struct bio * bio) mp_bh->bio.bi_opf |= REQ_FAILFAST_TRANSPORT; mp_bh->bio.bi_end_io = multipath_end_request; mp_bh->bio.bi_private = mp_bh; + mddev_check_writesame(mddev, &mp_bh->bio); generic_make_request(&mp_bh->bio); return; } diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c index 0863905dee02..8589e0a14068 100644 --- a/drivers/md/persistent-data/dm-block-manager.c +++ b/drivers/md/persistent-data/dm-block-manager.c @@ -13,6 +13,7 @@ #include <linux/rwsem.h> #include <linux/device-mapper.h> #include <linux/stacktrace.h> +#include <linux/sched/task.h> #define DM_MSG_PREFIX "block manager" diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index d6585239bff2..93347ca7c7a6 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -503,6 +503,7 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio) trace_block_bio_remap(bdev_get_queue(split->bi_bdev), split, disk_devt(mddev->gendisk), bio_sector); + mddev_check_writesame(mddev, split); generic_make_request(split); } } while (split != bio); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 830ff2b20346..fbc2d7851b49 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -37,7 +37,10 @@ #include <linux/module.h> #include <linux/seq_file.h> #include <linux/ratelimit.h> +#include <linux/sched/signal.h> + #include <trace/events/block.h> + #include "md.h" #include "raid1.h" #include "bitmap.h" @@ -71,9 +74,8 @@ */ static int max_queued_requests = 1024; -static void allow_barrier(struct r1conf *conf, sector_t start_next_window, - sector_t bi_sector); -static void lower_barrier(struct r1conf *conf); +static void allow_barrier(struct r1conf *conf, sector_t sector_nr); +static void lower_barrier(struct r1conf *conf, sector_t sector_nr); #define raid1_log(md, fmt, args...) \ do { if ((md)->queue) blk_add_trace_msg((md)->queue, "raid1 " fmt, ##args); } while (0) @@ -100,7 +102,6 @@ static void r1bio_pool_free(void *r1_bio, void *data) #define RESYNC_WINDOW_SECTORS (RESYNC_WINDOW >> 9) #define CLUSTER_RESYNC_WINDOW (16 * RESYNC_WINDOW) #define CLUSTER_RESYNC_WINDOW_SECTORS (CLUSTER_RESYNC_WINDOW >> 9) -#define NEXT_NORMALIO_DISTANCE (3 * RESYNC_WINDOW_SECTORS) static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data) { @@ -205,6 +206,7 @@ static void free_r1bio(struct r1bio *r1_bio) static void put_buf(struct r1bio *r1_bio) { struct r1conf *conf = r1_bio->mddev->private; + sector_t sect = r1_bio->sector; int i; for (i = 0; i < conf->raid_disks * 2; i++) { @@ -215,7 +217,7 @@ static void put_buf(struct r1bio *r1_bio) mempool_free(r1_bio, conf->r1buf_pool); - lower_barrier(conf); + lower_barrier(conf, sect); } static void reschedule_retry(struct r1bio *r1_bio) @@ -223,10 +225,12 @@ static void reschedule_retry(struct r1bio *r1_bio) unsigned long flags; struct mddev *mddev = r1_bio->mddev; struct r1conf *conf = mddev->private; + int idx; + idx = sector_to_idx(r1_bio->sector); spin_lock_irqsave(&conf->device_lock, flags); list_add(&r1_bio->retry_list, &conf->retry_list); - conf->nr_queued ++; + atomic_inc(&conf->nr_queued[idx]); spin_unlock_irqrestore(&conf->device_lock, flags); wake_up(&conf->wait_barrier); @@ -243,7 +247,6 @@ static void call_bio_endio(struct r1bio *r1_bio) struct bio *bio = r1_bio->master_bio; int done; struct r1conf *conf = r1_bio->mddev->private; - sector_t start_next_window = r1_bio->start_next_window; sector_t bi_sector = bio->bi_iter.bi_sector; if (bio->bi_phys_segments) { @@ -269,7 +272,7 @@ static void call_bio_endio(struct r1bio *r1_bio) * Wake up any possible resync thread that waits for the device * to go idle. */ - allow_barrier(conf, start_next_window, bi_sector); + allow_barrier(conf, bi_sector); } } @@ -517,6 +520,25 @@ static void raid1_end_write_request(struct bio *bio) bio_put(to_put); } +static sector_t align_to_barrier_unit_end(sector_t start_sector, + sector_t sectors) +{ + sector_t len; + + WARN_ON(sectors == 0); + /* + * len is the number of sectors from start_sector to end of the + * barrier unit which start_sector belongs to. + */ + len = round_up(start_sector + 1, BARRIER_UNIT_SECTOR_SIZE) - + start_sector; + + if (len > sectors) + len = sectors; + + return len; +} + /* * This routine returns the disk from which the requested read should * be done. There is a per-array 'next expected sequential IO' sector @@ -813,168 +835,228 @@ static void flush_pending_writes(struct r1conf *conf) */ static void raise_barrier(struct r1conf *conf, sector_t sector_nr) { + int idx = sector_to_idx(sector_nr); + spin_lock_irq(&conf->resync_lock); /* Wait until no block IO is waiting */ - wait_event_lock_irq(conf->wait_barrier, !conf->nr_waiting, + wait_event_lock_irq(conf->wait_barrier, + !atomic_read(&conf->nr_waiting[idx]), conf->resync_lock); /* block any new IO from starting */ - conf->barrier++; - conf->next_resync = sector_nr; + atomic_inc(&conf->barrier[idx]); + /* + * In raise_barrier() we firstly increase conf->barrier[idx] then + * check conf->nr_pending[idx]. In _wait_barrier() we firstly + * increase conf->nr_pending[idx] then check conf->barrier[idx]. + * A memory barrier here to make sure conf->nr_pending[idx] won't + * be fetched before conf->barrier[idx] is increased. Otherwise + * there will be a race between raise_barrier() and _wait_barrier(). + */ + smp_mb__after_atomic(); /* For these conditions we must wait: * A: while the array is in frozen state - * B: while barrier >= RESYNC_DEPTH, meaning resync reach - * the max count which allowed. - * C: next_resync + RESYNC_SECTORS > start_next_window, meaning - * next resync will reach to the window which normal bios are - * handling. - * D: while there are any active requests in the current window. + * B: while conf->nr_pending[idx] is not 0, meaning regular I/O + * existing in corresponding I/O barrier bucket. + * C: while conf->barrier[idx] >= RESYNC_DEPTH, meaning reaches + * max resync count which allowed on current I/O barrier bucket. */ wait_event_lock_irq(conf->wait_barrier, !conf->array_frozen && - conf->barrier < RESYNC_DEPTH && - conf->current_window_requests == 0 && - (conf->start_next_window >= - conf->next_resync + RESYNC_SECTORS), + !atomic_read(&conf->nr_pending[idx]) && + atomic_read(&conf->barrier[idx]) < RESYNC_DEPTH, conf->resync_lock); - conf->nr_pending++; + atomic_inc(&conf->nr_pending[idx]); spin_unlock_irq(&conf->resync_lock); } -static void lower_barrier(struct r1conf *conf) +static void lower_barrier(struct r1conf *conf, sector_t sector_nr) { - unsigned long flags; - BUG_ON(conf->barrier <= 0); - spin_lock_irqsave(&conf->resync_lock, flags); - conf->barrier--; - conf->nr_pending--; - spin_unlock_irqrestore(&conf->resync_lock, flags); + int idx = sector_to_idx(sector_nr); + + BUG_ON(atomic_read(&conf->barrier[idx]) <= 0); + + atomic_dec(&conf->barrier[idx]); + atomic_dec(&conf->nr_pending[idx]); wake_up(&conf->wait_barrier); } -static bool need_to_wait_for_sync(struct r1conf *conf, struct bio *bio) +static void _wait_barrier(struct r1conf *conf, int idx) { - bool wait = false; + /* + * We need to increase conf->nr_pending[idx] very early here, + * then raise_barrier() can be blocked when it waits for + * conf->nr_pending[idx] to be 0. Then we can avoid holding + * conf->resync_lock when there is no barrier raised in same + * barrier unit bucket. Also if the array is frozen, I/O + * should be blocked until array is unfrozen. + */ + atomic_inc(&conf->nr_pending[idx]); + /* + * In _wait_barrier() we firstly increase conf->nr_pending[idx], then + * check conf->barrier[idx]. In raise_barrier() we firstly increase + * conf->barrier[idx], then check conf->nr_pending[idx]. A memory + * barrier is necessary here to make sure conf->barrier[idx] won't be + * fetched before conf->nr_pending[idx] is increased. Otherwise there + * will be a race between _wait_barrier() and raise_barrier(). + */ + smp_mb__after_atomic(); - if (conf->array_frozen || !bio) - wait = true; - else if (conf->barrier && bio_data_dir(bio) == WRITE) { - if ((conf->mddev->curr_resync_completed - >= bio_end_sector(bio)) || - (conf->start_next_window + NEXT_NORMALIO_DISTANCE - <= bio->bi_iter.bi_sector)) - wait = false; - else - wait = true; - } + /* + * Don't worry about checking two atomic_t variables at same time + * here. If during we check conf->barrier[idx], the array is + * frozen (conf->array_frozen is 1), and chonf->barrier[idx] is + * 0, it is safe to return and make the I/O continue. Because the + * array is frozen, all I/O returned here will eventually complete + * or be queued, no race will happen. See code comment in + * frozen_array(). + */ + if (!READ_ONCE(conf->array_frozen) && + !atomic_read(&conf->barrier[idx])) + return; - return wait; + /* + * After holding conf->resync_lock, conf->nr_pending[idx] + * should be decreased before waiting for barrier to drop. + * Otherwise, we may encounter a race condition because + * raise_barrer() might be waiting for conf->nr_pending[idx] + * to be 0 at same time. + */ + spin_lock_irq(&conf->resync_lock); + atomic_inc(&conf->nr_waiting[idx]); + atomic_dec(&conf->nr_pending[idx]); + /* + * In case freeze_array() is waiting for + * get_unqueued_pending() == extra + */ + wake_up(&conf->wait_barrier); + /* Wait for the barrier in same barrier unit bucket to drop. */ + wait_event_lock_irq(conf->wait_barrier, + !conf->array_frozen && + !atomic_read(&conf->barrier[idx]), + conf->resync_lock); + atomic_inc(&conf->nr_pending[idx]); + atomic_dec(&conf->nr_waiting[idx]); + spin_unlock_irq(&conf->resync_lock); } -static sector_t wait_barrier(struct r1conf *conf, struct bio *bio) +static void wait_read_barrier(struct r1conf *conf, sector_t sector_nr) { - sector_t sector = 0; + int idx = sector_to_idx(sector_nr); - spin_lock_irq(&conf->resync_lock); - if (need_to_wait_for_sync(conf, bio)) { - conf->nr_waiting++; - /* Wait for the barrier to drop. - * However if there are already pending - * requests (preventing the barrier from - * rising completely), and the - * per-process bio queue isn't empty, - * then don't wait, as we need to empty - * that queue to allow conf->start_next_window - * to increase. - */ - raid1_log(conf->mddev, "wait barrier"); - wait_event_lock_irq(conf->wait_barrier, - !conf->array_frozen && - (!conf->barrier || - ((conf->start_next_window < - conf->next_resync + RESYNC_SECTORS) && - current->bio_list && - !bio_list_empty(current->bio_list))), - conf->resync_lock); - conf->nr_waiting--; - } - - if (bio && bio_data_dir(bio) == WRITE) { - if (bio->bi_iter.bi_sector >= conf->next_resync) { - if (conf->start_next_window == MaxSector) - conf->start_next_window = - conf->next_resync + - NEXT_NORMALIO_DISTANCE; - - if ((conf->start_next_window + NEXT_NORMALIO_DISTANCE) - <= bio->bi_iter.bi_sector) - conf->next_window_requests++; - else - conf->current_window_requests++; - sector = conf->start_next_window; - } - } + /* + * Very similar to _wait_barrier(). The difference is, for read + * I/O we don't need wait for sync I/O, but if the whole array + * is frozen, the read I/O still has to wait until the array is + * unfrozen. Since there is no ordering requirement with + * conf->barrier[idx] here, memory barrier is unnecessary as well. + */ + atomic_inc(&conf->nr_pending[idx]); + + if (!READ_ONCE(conf->array_frozen)) + return; - conf->nr_pending++; + spin_lock_irq(&conf->resync_lock); + atomic_inc(&conf->nr_waiting[idx]); + atomic_dec(&conf->nr_pending[idx]); + /* + * In case freeze_array() is waiting for + * get_unqueued_pending() == extra + */ + wake_up(&conf->wait_barrier); + /* Wait for array to be unfrozen */ + wait_event_lock_irq(conf->wait_barrier, + !conf->array_frozen, + conf->resync_lock); + atomic_inc(&conf->nr_pending[idx]); + atomic_dec(&conf->nr_waiting[idx]); spin_unlock_irq(&conf->resync_lock); - return sector; } -static void allow_barrier(struct r1conf *conf, sector_t start_next_window, - sector_t bi_sector) +static void wait_barrier(struct r1conf *conf, sector_t sector_nr) { - unsigned long flags; + int idx = sector_to_idx(sector_nr); - spin_lock_irqsave(&conf->resync_lock, flags); - conf->nr_pending--; - if (start_next_window) { - if (start_next_window == conf->start_next_window) { - if (conf->start_next_window + NEXT_NORMALIO_DISTANCE - <= bi_sector) - conf->next_window_requests--; - else - conf->current_window_requests--; - } else - conf->current_window_requests--; - - if (!conf->current_window_requests) { - if (conf->next_window_requests) { - conf->current_window_requests = - conf->next_window_requests; - conf->next_window_requests = 0; - conf->start_next_window += - NEXT_NORMALIO_DISTANCE; - } else - conf->start_next_window = MaxSector; - } - } - spin_unlock_irqrestore(&conf->resync_lock, flags); + _wait_barrier(conf, idx); +} + +static void wait_all_barriers(struct r1conf *conf) +{ + int idx; + + for (idx = 0; idx < BARRIER_BUCKETS_NR; idx++) + _wait_barrier(conf, idx); +} + +static void _allow_barrier(struct r1conf *conf, int idx) +{ + atomic_dec(&conf->nr_pending[idx]); wake_up(&conf->wait_barrier); } +static void allow_barrier(struct r1conf *conf, sector_t sector_nr) +{ + int idx = sector_to_idx(sector_nr); + + _allow_barrier(conf, idx); +} + +static void allow_all_barriers(struct r1conf *conf) +{ + int idx; + + for (idx = 0; idx < BARRIER_BUCKETS_NR; idx++) + _allow_barrier(conf, idx); +} + +/* conf->resync_lock should be held */ +static int get_unqueued_pending(struct r1conf *conf) +{ + int idx, ret; + + for (ret = 0, idx = 0; idx < BARRIER_BUCKETS_NR; idx++) + ret += atomic_read(&conf->nr_pending[idx]) - + atomic_read(&conf->nr_queued[idx]); + + return ret; +} + static void freeze_array(struct r1conf *conf, int extra) { - /* stop syncio and normal IO and wait for everything to + /* Stop sync I/O and normal I/O and wait for everything to * go quite. - * We wait until nr_pending match nr_queued+extra - * This is called in the context of one normal IO request - * that has failed. Thus any sync request that might be pending - * will be blocked by nr_pending, and we need to wait for - * pending IO requests to complete or be queued for re-try. - * Thus the number queued (nr_queued) plus this request (extra) - * must match the number of pending IOs (nr_pending) before - * we continue. + * This is called in two situations: + * 1) management command handlers (reshape, remove disk, quiesce). + * 2) one normal I/O request failed. + + * After array_frozen is set to 1, new sync IO will be blocked at + * raise_barrier(), and new normal I/O will blocked at _wait_barrier() + * or wait_read_barrier(). The flying I/Os will either complete or be + * queued. When everything goes quite, there are only queued I/Os left. + + * Every flying I/O contributes to a conf->nr_pending[idx], idx is the + * barrier bucket index which this I/O request hits. When all sync and + * normal I/O are queued, sum of all conf->nr_pending[] will match sum + * of all conf->nr_queued[]. But normal I/O failure is an exception, + * in handle_read_error(), we may call freeze_array() before trying to + * fix the read error. In this case, the error read I/O is not queued, + * so get_unqueued_pending() == 1. + * + * Therefore before this function returns, we need to wait until + * get_unqueued_pendings(conf) gets equal to extra. For + * normal I/O context, extra is 1, in rested situations extra is 0. */ spin_lock_irq(&conf->resync_lock); conf->array_frozen = 1; raid1_log(conf->mddev, "wait freeze"); - wait_event_lock_irq_cmd(conf->wait_barrier, - conf->nr_pending == conf->nr_queued+extra, - conf->resync_lock, - flush_pending_writes(conf)); + wait_event_lock_irq_cmd( + conf->wait_barrier, + get_unqueued_pending(conf) == extra, + conf->resync_lock, + flush_pending_writes(conf)); spin_unlock_irq(&conf->resync_lock); } static void unfreeze_array(struct r1conf *conf) @@ -982,8 +1064,8 @@ static void unfreeze_array(struct r1conf *conf) /* reverse the effect of the freeze */ spin_lock_irq(&conf->resync_lock); conf->array_frozen = 0; - wake_up(&conf->wait_barrier); spin_unlock_irq(&conf->resync_lock); + wake_up(&conf->wait_barrier); } /* duplicate the data pages for behind I/O @@ -1070,11 +1152,28 @@ static void raid1_unplug(struct blk_plug_cb *cb, bool from_schedule) kfree(plug); } -static void raid1_read_request(struct mddev *mddev, struct bio *bio, - struct r1bio *r1_bio) +static inline struct r1bio * +alloc_r1bio(struct mddev *mddev, struct bio *bio, sector_t sectors_handled) +{ + struct r1conf *conf = mddev->private; + struct r1bio *r1_bio; + + r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO); + + r1_bio->master_bio = bio; + r1_bio->sectors = bio_sectors(bio) - sectors_handled; + r1_bio->state = 0; + r1_bio->mddev = mddev; + r1_bio->sector = bio->bi_iter.bi_sector + sectors_handled; + + return r1_bio; +} + +static void raid1_read_request(struct mddev *mddev, struct bio *bio) { struct r1conf *conf = mddev->private; struct raid1_info *mirror; + struct r1bio *r1_bio; struct bio *read_bio; struct bitmap *bitmap = mddev->bitmap; const int op = bio_op(bio); @@ -1083,8 +1182,29 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio, int max_sectors; int rdisk; - wait_barrier(conf, bio); + /* + * Still need barrier for READ in case that whole + * array is frozen. + */ + wait_read_barrier(conf, bio->bi_iter.bi_sector); + + r1_bio = alloc_r1bio(mddev, bio, 0); + /* + * We might need to issue multiple reads to different + * devices if there are bad blocks around, so we keep + * track of the number of reads in bio->bi_phys_segments. + * If this is 0, there is only one r1_bio and no locking + * will be needed when requests complete. If it is + * non-zero, then it is the number of not-completed requests. + */ + bio->bi_phys_segments = 0; + bio_clear_flag(bio, BIO_SEG_VALID); + + /* + * make_request() can abort the operation when read-ahead is being + * used and no empty request is available. + */ read_again: rdisk = read_balance(conf, r1_bio, &max_sectors); @@ -1106,9 +1226,8 @@ read_again: atomic_read(&bitmap->behind_writes) == 0); } r1_bio->read_disk = rdisk; - r1_bio->start_next_window = 0; - read_bio = bio_clone_mddev(bio, GFP_NOIO, mddev); + read_bio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); bio_trim(read_bio, r1_bio->sector - bio->bi_iter.bi_sector, max_sectors); @@ -1151,22 +1270,16 @@ read_again: */ reschedule_retry(r1_bio); - r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO); - - r1_bio->master_bio = bio; - r1_bio->sectors = bio_sectors(bio) - sectors_handled; - r1_bio->state = 0; - r1_bio->mddev = mddev; - r1_bio->sector = bio->bi_iter.bi_sector + sectors_handled; + r1_bio = alloc_r1bio(mddev, bio, sectors_handled); goto read_again; } else generic_make_request(read_bio); } -static void raid1_write_request(struct mddev *mddev, struct bio *bio, - struct r1bio *r1_bio) +static void raid1_write_request(struct mddev *mddev, struct bio *bio) { struct r1conf *conf = mddev->private; + struct r1bio *r1_bio; int i, disks; struct bitmap *bitmap = mddev->bitmap; unsigned long flags; @@ -1176,7 +1289,6 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, int first_clone; int sectors_handled; int max_sectors; - sector_t start_next_window; /* * Register the new request and wait if the reconstruction @@ -1212,7 +1324,19 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, } finish_wait(&conf->wait_barrier, &w); } - start_next_window = wait_barrier(conf, bio); + wait_barrier(conf, bio->bi_iter.bi_sector); + + r1_bio = alloc_r1bio(mddev, bio, 0); + + /* We might need to issue multiple writes to different + * devices if there are bad blocks around, so we keep + * track of the number of writes in bio->bi_phys_segments. + * If this is 0, there is only one r1_bio and no locking + * will be needed when requests complete. If it is + * non-zero, then it is the number of not-completed requests. + */ + bio->bi_phys_segments = 0; + bio_clear_flag(bio, BIO_SEG_VALID); if (conf->pending_count >= max_queued_requests) { md_wakeup_thread(mddev->thread); @@ -1233,7 +1357,6 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, disks = conf->raid_disks * 2; retry_write: - r1_bio->start_next_window = start_next_window; blocked_rdev = NULL; rcu_read_lock(); max_sectors = r1_bio->sectors; @@ -1300,25 +1423,15 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, if (unlikely(blocked_rdev)) { /* Wait for this device to become unblocked */ int j; - sector_t old = start_next_window; for (j = 0; j < i; j++) if (r1_bio->bios[j]) rdev_dec_pending(conf->mirrors[j].rdev, mddev); r1_bio->state = 0; - allow_barrier(conf, start_next_window, bio->bi_iter.bi_sector); + allow_barrier(conf, bio->bi_iter.bi_sector); raid1_log(mddev, "wait rdev %d blocked", blocked_rdev->raid_disk); md_wait_for_blocked_rdev(blocked_rdev, mddev); - start_next_window = wait_barrier(conf, bio); - /* - * We must make sure the multi r1bios of bio have - * the same value of bi_phys_segments - */ - if (bio->bi_phys_segments && old && - old != start_next_window) - /* Wait for the former r1bio(s) to complete */ - wait_event(conf->wait_barrier, - bio->bi_phys_segments == 1); + wait_barrier(conf, bio->bi_iter.bi_sector); goto retry_write; } @@ -1341,13 +1454,12 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, first_clone = 1; for (i = 0; i < disks; i++) { - struct bio *mbio; + struct bio *mbio = NULL; + sector_t offset; if (!r1_bio->bios[i]) continue; - mbio = bio_clone_mddev(bio, GFP_NOIO, mddev); - bio_trim(mbio, r1_bio->sector - bio->bi_iter.bi_sector, - max_sectors); + offset = r1_bio->sector - bio->bi_iter.bi_sector; if (first_clone) { /* do behind I/O ? @@ -1357,8 +1469,13 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, if (bitmap && (atomic_read(&bitmap->behind_writes) < mddev->bitmap_info.max_write_behind) && - !waitqueue_active(&bitmap->behind_wait)) + !waitqueue_active(&bitmap->behind_wait)) { + mbio = bio_clone_bioset_partial(bio, GFP_NOIO, + mddev->bio_set, + offset << 9, + max_sectors << 9); alloc_behind_pages(mbio, r1_bio); + } bitmap_startwrite(bitmap, r1_bio->sector, r1_bio->sectors, @@ -1366,6 +1483,19 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, &r1_bio->state)); first_clone = 0; } + + if (!mbio) { + if (r1_bio->behind_bvecs) + mbio = bio_clone_bioset_partial(bio, GFP_NOIO, + mddev->bio_set, + offset << 9, + max_sectors << 9); + else { + mbio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); + bio_trim(mbio, offset, max_sectors); + } + } + if (r1_bio->behind_bvecs) { struct bio_vec *bvec; int j; @@ -1385,8 +1515,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, conf->mirrors[i].rdev->data_offset); mbio->bi_bdev = conf->mirrors[i].rdev->bdev; mbio->bi_end_io = raid1_end_write_request; - mbio->bi_opf = bio_op(bio) | - (bio->bi_opf & (REQ_SYNC | REQ_PREFLUSH | REQ_FUA)); + mbio->bi_opf = bio_op(bio) | (bio->bi_opf & (REQ_SYNC | REQ_FUA)); if (test_bit(FailFast, &conf->mirrors[i].rdev->flags) && !test_bit(WriteMostly, &conf->mirrors[i].rdev->flags) && conf->raid_disks - mddev->degraded > 1) @@ -1427,12 +1556,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, /* We need another r1_bio. It has already been counted * in bio->bi_phys_segments */ - r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO); - r1_bio->master_bio = bio; - r1_bio->sectors = bio_sectors(bio) - sectors_handled; - r1_bio->state = 0; - r1_bio->mddev = mddev; - r1_bio->sector = bio->bi_iter.bi_sector + sectors_handled; + r1_bio = alloc_r1bio(mddev, bio, sectors_handled); goto retry_write; } @@ -1444,36 +1568,30 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, static void raid1_make_request(struct mddev *mddev, struct bio *bio) { - struct r1conf *conf = mddev->private; - struct r1bio *r1_bio; + struct bio *split; + sector_t sectors; - /* - * make_request() can abort the operation when read-ahead is being - * used and no empty request is available. - * - */ - r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO); - - r1_bio->master_bio = bio; - r1_bio->sectors = bio_sectors(bio); - r1_bio->state = 0; - r1_bio->mddev = mddev; - r1_bio->sector = bio->bi_iter.bi_sector; + if (unlikely(bio->bi_opf & REQ_PREFLUSH)) { + md_flush_request(mddev, bio); + return; + } - /* - * We might need to issue multiple reads to different devices if there - * are bad blocks around, so we keep track of the number of reads in - * bio->bi_phys_segments. If this is 0, there is only one r1_bio and - * no locking will be needed when requests complete. If it is - * non-zero, then it is the number of not-completed requests. - */ - bio->bi_phys_segments = 0; - bio_clear_flag(bio, BIO_SEG_VALID); + /* if bio exceeds barrier unit boundary, split it */ + do { + sectors = align_to_barrier_unit_end( + bio->bi_iter.bi_sector, bio_sectors(bio)); + if (sectors < bio_sectors(bio)) { + split = bio_split(bio, sectors, GFP_NOIO, fs_bio_set); + bio_chain(split, bio); + } else { + split = bio; + } - if (bio_data_dir(bio) == READ) - raid1_read_request(mddev, bio, r1_bio); - else - raid1_write_request(mddev, bio, r1_bio); + if (bio_data_dir(split) == READ) + raid1_read_request(mddev, split); + else + raid1_write_request(mddev, split); + } while (split != bio); } static void raid1_status(struct seq_file *seq, struct mddev *mddev) @@ -1564,19 +1682,11 @@ static void print_conf(struct r1conf *conf) static void close_sync(struct r1conf *conf) { - wait_barrier(conf, NULL); - allow_barrier(conf, 0, 0); + wait_all_barriers(conf); + allow_all_barriers(conf); mempool_destroy(conf->r1buf_pool); conf->r1buf_pool = NULL; - - spin_lock_irq(&conf->resync_lock); - conf->next_resync = MaxSector - 2 * NEXT_NORMALIO_DISTANCE; - conf->start_next_window = MaxSector; - conf->current_window_requests += - conf->next_window_requests; - conf->next_window_requests = 0; - spin_unlock_irq(&conf->resync_lock); } static int raid1_spare_active(struct mddev *mddev) @@ -2273,7 +2383,8 @@ static int narrow_write_error(struct r1bio *r1_bio, int i) wbio->bi_vcnt = vcnt; } else { - wbio = bio_clone_mddev(r1_bio->master_bio, GFP_NOIO, mddev); + wbio = bio_clone_fast(r1_bio->master_bio, GFP_NOIO, + mddev->bio_set); } bio_set_op_attrs(wbio, REQ_OP_WRITE, 0); @@ -2323,8 +2434,9 @@ static void handle_sync_write_finished(struct r1conf *conf, struct r1bio *r1_bio static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio) { - int m; + int m, idx; bool fail = false; + for (m = 0; m < conf->raid_disks * 2 ; m++) if (r1_bio->bios[m] == IO_MADE_GOOD) { struct md_rdev *rdev = conf->mirrors[m].rdev; @@ -2350,8 +2462,14 @@ static void handle_write_finished(struct r1conf *conf, struct r1bio *r1_bio) if (fail) { spin_lock_irq(&conf->device_lock); list_add(&r1_bio->retry_list, &conf->bio_end_io_list); - conf->nr_queued++; + idx = sector_to_idx(r1_bio->sector); + atomic_inc(&conf->nr_queued[idx]); spin_unlock_irq(&conf->device_lock); + /* + * In case freeze_array() is waiting for condition + * get_unqueued_pending() == extra to be true. + */ + wake_up(&conf->wait_barrier); md_wakeup_thread(conf->mddev->thread); } else { if (test_bit(R1BIO_WriteError, &r1_bio->state)) @@ -2411,7 +2529,8 @@ read_more: const unsigned long do_sync = r1_bio->master_bio->bi_opf & REQ_SYNC; r1_bio->read_disk = disk; - bio = bio_clone_mddev(r1_bio->master_bio, GFP_NOIO, mddev); + bio = bio_clone_fast(r1_bio->master_bio, GFP_NOIO, + mddev->bio_set); bio_trim(bio, r1_bio->sector - bio->bi_iter.bi_sector, max_sectors); r1_bio->bios[r1_bio->read_disk] = bio; @@ -2445,15 +2564,8 @@ read_more: generic_make_request(bio); bio = NULL; - r1_bio = mempool_alloc(conf->r1bio_pool, GFP_NOIO); - - r1_bio->master_bio = mbio; - r1_bio->sectors = bio_sectors(mbio) - sectors_handled; - r1_bio->state = 0; + r1_bio = alloc_r1bio(mddev, mbio, sectors_handled); set_bit(R1BIO_ReadError, &r1_bio->state); - r1_bio->mddev = mddev; - r1_bio->sector = mbio->bi_iter.bi_sector + - sectors_handled; goto read_more; } else { @@ -2472,6 +2584,7 @@ static void raid1d(struct md_thread *thread) struct r1conf *conf = mddev->private; struct list_head *head = &conf->retry_list; struct blk_plug plug; + int idx; md_check_recovery(mddev); @@ -2479,17 +2592,15 @@ static void raid1d(struct md_thread *thread) !test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags)) { LIST_HEAD(tmp); spin_lock_irqsave(&conf->device_lock, flags); - if (!test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags)) { - while (!list_empty(&conf->bio_end_io_list)) { - list_move(conf->bio_end_io_list.prev, &tmp); - conf->nr_queued--; - } - } + if (!test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags)) + list_splice_init(&conf->bio_end_io_list, &tmp); spin_unlock_irqrestore(&conf->device_lock, flags); while (!list_empty(&tmp)) { r1_bio = list_first_entry(&tmp, struct r1bio, retry_list); list_del(&r1_bio->retry_list); + idx = sector_to_idx(r1_bio->sector); + atomic_dec(&conf->nr_queued[idx]); if (mddev->degraded) set_bit(R1BIO_Degraded, &r1_bio->state); if (test_bit(R1BIO_WriteError, &r1_bio->state)) @@ -2510,7 +2621,8 @@ static void raid1d(struct md_thread *thread) } r1_bio = list_entry(head->prev, struct r1bio, retry_list); list_del(head->prev); - conf->nr_queued--; + idx = sector_to_idx(r1_bio->sector); + atomic_dec(&conf->nr_queued[idx]); spin_unlock_irqrestore(&conf->device_lock, flags); mddev = r1_bio->mddev; @@ -2549,7 +2661,6 @@ static int init_resync(struct r1conf *conf) conf->poolinfo); if (!conf->r1buf_pool) return -ENOMEM; - conf->next_resync = 0; return 0; } @@ -2578,6 +2689,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr, int still_degraded = 0; int good_sectors = RESYNC_SECTORS; int min_bad = 0; /* number of sectors that are bad in all devices */ + int idx = sector_to_idx(sector_nr); if (!conf->r1buf_pool) if (init_resync(conf)) @@ -2627,7 +2739,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr, * If there is non-resync activity waiting for a turn, then let it * though before starting on this new sync request. */ - if (conf->nr_waiting) + if (atomic_read(&conf->nr_waiting[idx])) schedule_timeout_uninterruptible(1); /* we are incrementing sector_nr below. To be safe, we check against @@ -2654,6 +2766,8 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr, r1_bio->sector = sector_nr; r1_bio->state = 0; set_bit(R1BIO_IsSync, &r1_bio->state); + /* make sure good_sectors won't go across barrier unit boundary */ + good_sectors = align_to_barrier_unit_end(sector_nr, good_sectors); for (i = 0; i < conf->raid_disks * 2; i++) { struct md_rdev *rdev; @@ -2884,6 +2998,26 @@ static struct r1conf *setup_conf(struct mddev *mddev) if (!conf) goto abort; + conf->nr_pending = kcalloc(BARRIER_BUCKETS_NR, + sizeof(atomic_t), GFP_KERNEL); + if (!conf->nr_pending) + goto abort; + + conf->nr_waiting = kcalloc(BARRIER_BUCKETS_NR, + sizeof(atomic_t), GFP_KERNEL); + if (!conf->nr_waiting) + goto abort; + + conf->nr_queued = kcalloc(BARRIER_BUCKETS_NR, + sizeof(atomic_t), GFP_KERNEL); + if (!conf->nr_queued) + goto abort; + + conf->barrier = kcalloc(BARRIER_BUCKETS_NR, + sizeof(atomic_t), GFP_KERNEL); + if (!conf->barrier) + goto abort; + conf->mirrors = kzalloc(sizeof(struct raid1_info) * mddev->raid_disks * 2, GFP_KERNEL); @@ -2939,9 +3073,6 @@ static struct r1conf *setup_conf(struct mddev *mddev) conf->pending_count = 0; conf->recovery_disabled = mddev->recovery_disabled - 1; - conf->start_next_window = MaxSector; - conf->current_window_requests = conf->next_window_requests = 0; - err = -EIO; for (i = 0; i < conf->raid_disks * 2; i++) { @@ -2984,6 +3115,10 @@ static struct r1conf *setup_conf(struct mddev *mddev) kfree(conf->mirrors); safe_put_page(conf->tmppage); kfree(conf->poolinfo); + kfree(conf->nr_pending); + kfree(conf->nr_waiting); + kfree(conf->nr_queued); + kfree(conf->barrier); kfree(conf); } return ERR_PTR(err); @@ -3085,6 +3220,10 @@ static void raid1_free(struct mddev *mddev, void *priv) kfree(conf->mirrors); safe_put_page(conf->tmppage); kfree(conf->poolinfo); + kfree(conf->nr_pending); + kfree(conf->nr_waiting); + kfree(conf->nr_queued); + kfree(conf->barrier); kfree(conf); } diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h index c52ef424a24b..dd22a37d0d83 100644 --- a/drivers/md/raid1.h +++ b/drivers/md/raid1.h @@ -1,6 +1,30 @@ #ifndef _RAID1_H #define _RAID1_H +/* + * each barrier unit size is 64MB fow now + * note: it must be larger than RESYNC_DEPTH + */ +#define BARRIER_UNIT_SECTOR_BITS 17 +#define BARRIER_UNIT_SECTOR_SIZE (1<<17) +/* + * In struct r1conf, the following members are related to I/O barrier + * buckets, + * atomic_t *nr_pending; + * atomic_t *nr_waiting; + * atomic_t *nr_queued; + * atomic_t *barrier; + * Each of them points to array of atomic_t variables, each array is + * designed to have BARRIER_BUCKETS_NR elements and occupy a single + * memory page. The data width of atomic_t variables is 4 bytes, equal + * to 1<<(ilog2(sizeof(atomic_t))), BARRIER_BUCKETS_NR_BITS is defined + * as (PAGE_SHIFT - ilog2(sizeof(int))) to make sure an array of + * atomic_t variables with BARRIER_BUCKETS_NR elements just exactly + * occupies a single memory page. + */ +#define BARRIER_BUCKETS_NR_BITS (PAGE_SHIFT - ilog2(sizeof(atomic_t))) +#define BARRIER_BUCKETS_NR (1<<BARRIER_BUCKETS_NR_BITS) + struct raid1_info { struct md_rdev *rdev; sector_t head_position; @@ -35,25 +59,6 @@ struct r1conf { */ int raid_disks; - /* During resync, read_balancing is only allowed on the part - * of the array that has been resynced. 'next_resync' tells us - * where that is. - */ - sector_t next_resync; - - /* When raid1 starts resync, we divide array into four partitions - * |---------|--------------|---------------------|-------------| - * next_resync start_next_window end_window - * start_next_window = next_resync + NEXT_NORMALIO_DISTANCE - * end_window = start_next_window + NEXT_NORMALIO_DISTANCE - * current_window_requests means the count of normalIO between - * start_next_window and end_window. - * next_window_requests means the count of normalIO after end_window. - * */ - sector_t start_next_window; - int current_window_requests; - int next_window_requests; - spinlock_t device_lock; /* list of 'struct r1bio' that need to be processed by raid1d, @@ -79,10 +84,10 @@ struct r1conf { */ wait_queue_head_t wait_barrier; spinlock_t resync_lock; - int nr_pending; - int nr_waiting; - int nr_queued; - int barrier; + atomic_t *nr_pending; + atomic_t *nr_waiting; + atomic_t *nr_queued; + atomic_t *barrier; int array_frozen; /* Set to 1 if a full sync is needed, (fresh device added). @@ -135,7 +140,6 @@ struct r1bio { * in this BehindIO request */ sector_t sector; - sector_t start_next_window; int sectors; unsigned long state; struct mddev *mddev; @@ -185,4 +189,10 @@ enum r1bio_state { R1BIO_WriteError, R1BIO_FailFast, }; + +static inline int sector_to_idx(sector_t sector) +{ + return hash_long(sector >> BARRIER_UNIT_SECTOR_BITS, + BARRIER_BUCKETS_NR_BITS); +} #endif diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 6bc5c2a85160..063c43d83b72 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1132,7 +1132,7 @@ read_again: } slot = r10_bio->read_slot; - read_bio = bio_clone_mddev(bio, GFP_NOIO, mddev); + read_bio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); bio_trim(read_bio, r10_bio->sector - bio->bi_iter.bi_sector, max_sectors); @@ -1406,7 +1406,7 @@ retry_write: int d = r10_bio->devs[i].devnum; if (r10_bio->devs[i].bio) { struct md_rdev *rdev = conf->mirrors[d].rdev; - mbio = bio_clone_mddev(bio, GFP_NOIO, mddev); + mbio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); bio_trim(mbio, r10_bio->sector - bio->bi_iter.bi_sector, max_sectors); r10_bio->devs[i].bio = mbio; @@ -1457,7 +1457,7 @@ retry_write: smp_mb(); rdev = conf->mirrors[d].rdev; } - mbio = bio_clone_mddev(bio, GFP_NOIO, mddev); + mbio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); bio_trim(mbio, r10_bio->sector - bio->bi_iter.bi_sector, max_sectors); r10_bio->devs[i].repl_bio = mbio; @@ -2565,7 +2565,7 @@ static int narrow_write_error(struct r10bio *r10_bio, int i) if (sectors > sect_to_write) sectors = sect_to_write; /* Write at 'sector' for 'sectors' */ - wbio = bio_clone_mddev(bio, GFP_NOIO, mddev); + wbio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); bio_trim(wbio, sector - bio->bi_iter.bi_sector, sectors); wsector = r10_bio->devs[i].addr + (sector - r10_bio->sector); wbio->bi_iter.bi_sector = wsector + @@ -2641,8 +2641,7 @@ read_more: mdname(mddev), bdevname(rdev->bdev, b), (unsigned long long)r10_bio->sector); - bio = bio_clone_mddev(r10_bio->master_bio, - GFP_NOIO, mddev); + bio = bio_clone_fast(r10_bio->master_bio, GFP_NOIO, mddev->bio_set); bio_trim(bio, r10_bio->sector - bio->bi_iter.bi_sector, max_sectors); r10_bio->devs[slot].bio = bio; r10_bio->devs[slot].rdev = rdev; diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c index 302dea3296ba..3f307be01b10 100644 --- a/drivers/md/raid5-cache.c +++ b/drivers/md/raid5-cache.c @@ -20,6 +20,7 @@ #include <linux/crc32c.h> #include <linux/random.h> #include <linux/kthread.h> +#include <linux/types.h> #include "md.h" #include "raid5.h" #include "bitmap.h" @@ -164,9 +165,60 @@ struct r5l_log { struct work_struct deferred_io_work; /* to disable write back during in degraded mode */ struct work_struct disable_writeback_work; + + /* to for chunk_aligned_read in writeback mode, details below */ + spinlock_t tree_lock; + struct radix_tree_root big_stripe_tree; }; /* + * Enable chunk_aligned_read() with write back cache. + * + * Each chunk may contain more than one stripe (for example, a 256kB + * chunk contains 64 4kB-page, so this chunk contain 64 stripes). For + * chunk_aligned_read, these stripes are grouped into one "big_stripe". + * For each big_stripe, we count how many stripes of this big_stripe + * are in the write back cache. These data are tracked in a radix tree + * (big_stripe_tree). We use radix_tree item pointer as the counter. + * r5c_tree_index() is used to calculate keys for the radix tree. + * + * chunk_aligned_read() calls r5c_big_stripe_cached() to look up + * big_stripe of each chunk in the tree. If this big_stripe is in the + * tree, chunk_aligned_read() aborts. This look up is protected by + * rcu_read_lock(). + * + * It is necessary to remember whether a stripe is counted in + * big_stripe_tree. Instead of adding new flag, we reuses existing flags: + * STRIPE_R5C_PARTIAL_STRIPE and STRIPE_R5C_FULL_STRIPE. If either of these + * two flags are set, the stripe is counted in big_stripe_tree. This + * requires moving set_bit(STRIPE_R5C_PARTIAL_STRIPE) to + * r5c_try_caching_write(); and moving clear_bit of + * STRIPE_R5C_PARTIAL_STRIPE and STRIPE_R5C_FULL_STRIPE to + * r5c_finish_stripe_write_out(). + */ + +/* + * radix tree requests lowest 2 bits of data pointer to be 2b'00. + * So it is necessary to left shift the counter by 2 bits before using it + * as data pointer of the tree. + */ +#define R5C_RADIX_COUNT_SHIFT 2 + +/* + * calculate key for big_stripe_tree + * + * sect: align_bi->bi_iter.bi_sector or sh->sector + */ +static inline sector_t r5c_tree_index(struct r5conf *conf, + sector_t sect) +{ + sector_t offset; + + offset = sector_div(sect, conf->chunk_sectors); + return sect; +} + +/* * an IO range starts from a meta data block and end at the next meta data * block. The io unit's the meta data block tracks data/parity followed it. io * unit is written to log disk with normal write, as we always flush log disk @@ -337,17 +389,30 @@ void r5c_check_cached_full_stripe(struct r5conf *conf) /* * Total log space (in sectors) needed to flush all data in cache * - * Currently, writing-out phase automatically includes all pending writes - * to the same sector. So the reclaim of each stripe takes up to - * (conf->raid_disks + 1) pages of log space. + * To avoid deadlock due to log space, it is necessary to reserve log + * space to flush critical stripes (stripes that occupying log space near + * last_checkpoint). This function helps check how much log space is + * required to flush all cached stripes. * - * To totally avoid deadlock due to log space, the code reserves - * (conf->raid_disks + 1) pages for each stripe in cache, which is not - * necessary in most cases. + * To reduce log space requirements, two mechanisms are used to give cache + * flush higher priorities: + * 1. In handle_stripe_dirtying() and schedule_reconstruction(), + * stripes ALREADY in journal can be flushed w/o pending writes; + * 2. In r5l_write_stripe() and r5c_cache_data(), stripes NOT in journal + * can be delayed (r5l_add_no_space_stripe). * - * To improve this, we will need writing-out phase to be able to NOT include - * pending writes, which will reduce the requirement to - * (conf->max_degraded + 1) pages per stripe in cache. + * In cache flush, the stripe goes through 1 and then 2. For a stripe that + * already passed 1, flushing it requires at most (conf->max_degraded + 1) + * pages of journal space. For stripes that has not passed 1, flushing it + * requires (conf->raid_disks + 1) pages of journal space. There are at + * most (conf->group_cnt + 1) stripe that passed 1. So total journal space + * required to flush all cached stripes (in pages) is: + * + * (stripe_in_journal_count - group_cnt - 1) * (max_degraded + 1) + + * (group_cnt + 1) * (raid_disks + 1) + * or + * (stripe_in_journal_count) * (max_degraded + 1) + + * (group_cnt + 1) * (raid_disks - max_degraded) */ static sector_t r5c_log_required_to_flush_cache(struct r5conf *conf) { @@ -356,8 +421,9 @@ static sector_t r5c_log_required_to_flush_cache(struct r5conf *conf) if (!r5c_is_writeback(log)) return 0; - return BLOCK_SECTORS * (conf->raid_disks + 1) * - atomic_read(&log->stripe_in_journal_count); + return BLOCK_SECTORS * + ((conf->max_degraded + 1) * atomic_read(&log->stripe_in_journal_count) + + (conf->raid_disks - conf->max_degraded) * (conf->group_cnt + 1)); } /* @@ -412,16 +478,6 @@ void r5c_make_stripe_write_out(struct stripe_head *sh) if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) atomic_inc(&conf->preread_active_stripes); - - if (test_and_clear_bit(STRIPE_R5C_PARTIAL_STRIPE, &sh->state)) { - BUG_ON(atomic_read(&conf->r5c_cached_partial_stripes) == 0); - atomic_dec(&conf->r5c_cached_partial_stripes); - } - - if (test_and_clear_bit(STRIPE_R5C_FULL_STRIPE, &sh->state)) { - BUG_ON(atomic_read(&conf->r5c_cached_full_stripes) == 0); - atomic_dec(&conf->r5c_cached_full_stripes); - } } static void r5c_handle_data_cached(struct stripe_head *sh) @@ -1271,6 +1327,10 @@ static void r5c_flush_stripe(struct r5conf *conf, struct stripe_head *sh) atomic_inc(&conf->active_stripes); r5c_make_stripe_write_out(sh); + if (test_bit(STRIPE_R5C_PARTIAL_STRIPE, &sh->state)) + atomic_inc(&conf->r5c_flushing_partial_stripes); + else + atomic_inc(&conf->r5c_flushing_full_stripes); raid5_release_stripe(sh); } @@ -1313,12 +1373,16 @@ static void r5c_do_reclaim(struct r5conf *conf) unsigned long flags; int total_cached; int stripes_to_flush; + int flushing_partial, flushing_full; if (!r5c_is_writeback(log)) return; + flushing_partial = atomic_read(&conf->r5c_flushing_partial_stripes); + flushing_full = atomic_read(&conf->r5c_flushing_full_stripes); total_cached = atomic_read(&conf->r5c_cached_partial_stripes) + - atomic_read(&conf->r5c_cached_full_stripes); + atomic_read(&conf->r5c_cached_full_stripes) - + flushing_full - flushing_partial; if (total_cached > conf->min_nr_stripes * 3 / 4 || atomic_read(&conf->empty_inactive_list_nr) > 0) @@ -1328,7 +1392,7 @@ static void r5c_do_reclaim(struct r5conf *conf) */ stripes_to_flush = R5C_RECLAIM_STRIPE_GROUP; else if (total_cached > conf->min_nr_stripes * 1 / 2 || - atomic_read(&conf->r5c_cached_full_stripes) > + atomic_read(&conf->r5c_cached_full_stripes) - flushing_full > R5C_FULL_STRIPE_FLUSH_BATCH) /* * if stripe cache pressure moderate, or if there is many full @@ -1362,9 +1426,9 @@ static void r5c_do_reclaim(struct r5conf *conf) !test_bit(STRIPE_HANDLE, &sh->state) && atomic_read(&sh->count) == 0) { r5c_flush_stripe(conf, sh); + if (count++ >= R5C_RECLAIM_STRIPE_GROUP) + break; } - if (count++ >= R5C_RECLAIM_STRIPE_GROUP) - break; } spin_unlock(&conf->device_lock); spin_unlock_irqrestore(&log->stripe_in_journal_lock, flags); @@ -2320,6 +2384,10 @@ int r5c_try_caching_write(struct r5conf *conf, int i; struct r5dev *dev; int to_cache = 0; + void **pslot; + sector_t tree_index; + int ret; + uintptr_t refcount; BUG_ON(!r5c_is_writeback(log)); @@ -2364,6 +2432,44 @@ int r5c_try_caching_write(struct r5conf *conf, } } + /* if the stripe is not counted in big_stripe_tree, add it now */ + if (!test_bit(STRIPE_R5C_PARTIAL_STRIPE, &sh->state) && + !test_bit(STRIPE_R5C_FULL_STRIPE, &sh->state)) { + tree_index = r5c_tree_index(conf, sh->sector); + spin_lock(&log->tree_lock); + pslot = radix_tree_lookup_slot(&log->big_stripe_tree, + tree_index); + if (pslot) { + refcount = (uintptr_t)radix_tree_deref_slot_protected( + pslot, &log->tree_lock) >> + R5C_RADIX_COUNT_SHIFT; + radix_tree_replace_slot( + &log->big_stripe_tree, pslot, + (void *)((refcount + 1) << R5C_RADIX_COUNT_SHIFT)); + } else { + /* + * this radix_tree_insert can fail safely, so no + * need to call radix_tree_preload() + */ + ret = radix_tree_insert( + &log->big_stripe_tree, tree_index, + (void *)(1 << R5C_RADIX_COUNT_SHIFT)); + if (ret) { + spin_unlock(&log->tree_lock); + r5c_make_stripe_write_out(sh); + return -EAGAIN; + } + } + spin_unlock(&log->tree_lock); + + /* + * set STRIPE_R5C_PARTIAL_STRIPE, this shows the stripe is + * counted in the radix tree + */ + set_bit(STRIPE_R5C_PARTIAL_STRIPE, &sh->state); + atomic_inc(&conf->r5c_cached_partial_stripes); + } + for (i = disks; i--; ) { dev = &sh->dev[i]; if (dev->towrite) { @@ -2438,17 +2544,20 @@ void r5c_finish_stripe_write_out(struct r5conf *conf, struct stripe_head *sh, struct stripe_head_state *s) { + struct r5l_log *log = conf->log; int i; int do_wakeup = 0; + sector_t tree_index; + void **pslot; + uintptr_t refcount; - if (!conf->log || - !test_bit(R5_InJournal, &sh->dev[sh->pd_idx].flags)) + if (!log || !test_bit(R5_InJournal, &sh->dev[sh->pd_idx].flags)) return; WARN_ON(test_bit(STRIPE_R5C_CACHING, &sh->state)); clear_bit(R5_InJournal, &sh->dev[sh->pd_idx].flags); - if (conf->log->r5c_journal_mode == R5C_JOURNAL_MODE_WRITE_THROUGH) + if (log->r5c_journal_mode == R5C_JOURNAL_MODE_WRITE_THROUGH) return; for (i = sh->disks; i--; ) { @@ -2470,12 +2579,45 @@ void r5c_finish_stripe_write_out(struct r5conf *conf, if (do_wakeup) wake_up(&conf->wait_for_overlap); - spin_lock_irq(&conf->log->stripe_in_journal_lock); + spin_lock_irq(&log->stripe_in_journal_lock); list_del_init(&sh->r5c); - spin_unlock_irq(&conf->log->stripe_in_journal_lock); + spin_unlock_irq(&log->stripe_in_journal_lock); sh->log_start = MaxSector; - atomic_dec(&conf->log->stripe_in_journal_count); - r5c_update_log_state(conf->log); + + atomic_dec(&log->stripe_in_journal_count); + r5c_update_log_state(log); + + /* stop counting this stripe in big_stripe_tree */ + if (test_bit(STRIPE_R5C_PARTIAL_STRIPE, &sh->state) || + test_bit(STRIPE_R5C_FULL_STRIPE, &sh->state)) { + tree_index = r5c_tree_index(conf, sh->sector); + spin_lock(&log->tree_lock); + pslot = radix_tree_lookup_slot(&log->big_stripe_tree, + tree_index); + BUG_ON(pslot == NULL); + refcount = (uintptr_t)radix_tree_deref_slot_protected( + pslot, &log->tree_lock) >> + R5C_RADIX_COUNT_SHIFT; + if (refcount == 1) + radix_tree_delete(&log->big_stripe_tree, tree_index); + else + radix_tree_replace_slot( + &log->big_stripe_tree, pslot, + (void *)((refcount - 1) << R5C_RADIX_COUNT_SHIFT)); + spin_unlock(&log->tree_lock); + } + + if (test_and_clear_bit(STRIPE_R5C_PARTIAL_STRIPE, &sh->state)) { + BUG_ON(atomic_read(&conf->r5c_cached_partial_stripes) == 0); + atomic_dec(&conf->r5c_flushing_partial_stripes); + atomic_dec(&conf->r5c_cached_partial_stripes); + } + + if (test_and_clear_bit(STRIPE_R5C_FULL_STRIPE, &sh->state)) { + BUG_ON(atomic_read(&conf->r5c_cached_full_stripes) == 0); + atomic_dec(&conf->r5c_flushing_full_stripes); + atomic_dec(&conf->r5c_cached_full_stripes); + } } int @@ -2535,6 +2677,22 @@ r5c_cache_data(struct r5l_log *log, struct stripe_head *sh, return 0; } +/* check whether this big stripe is in write back cache. */ +bool r5c_big_stripe_cached(struct r5conf *conf, sector_t sect) +{ + struct r5l_log *log = conf->log; + sector_t tree_index; + void *slot; + + if (!log) + return false; + + WARN_ON_ONCE(!rcu_read_lock_held()); + tree_index = r5c_tree_index(conf, sect); + slot = radix_tree_lookup(&log->big_stripe_tree, tree_index); + return slot != NULL; +} + static int r5l_load_log(struct r5l_log *log) { struct md_rdev *rdev = log->rdev; @@ -2681,6 +2839,9 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev) if (!log->meta_pool) goto out_mempool; + spin_lock_init(&log->tree_lock); + INIT_RADIX_TREE(&log->big_stripe_tree, GFP_NOWAIT | __GFP_NOWARN); + log->reclaim_thread = md_register_thread(r5l_reclaim_thread, log->rdev->mddev, "reclaim"); if (!log->reclaim_thread) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 6214e699342c..4fb09b3fcb41 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -55,6 +55,8 @@ #include <linux/ratelimit.h> #include <linux/nodemask.h> #include <linux/flex_array.h> +#include <linux/sched/signal.h> + #include <trace/events/block.h> #include "md.h" @@ -281,13 +283,13 @@ static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh, atomic_dec(&conf->r5c_cached_partial_stripes); list_add_tail(&sh->lru, &conf->r5c_full_stripe_list); r5c_check_cached_full_stripe(conf); - } else { - /* partial stripe */ - if (!test_and_set_bit(STRIPE_R5C_PARTIAL_STRIPE, - &sh->state)) - atomic_inc(&conf->r5c_cached_partial_stripes); + } else + /* + * STRIPE_R5C_PARTIAL_STRIPE is set in + * r5c_try_caching_write(). No need to + * set it again. + */ list_add_tail(&sh->lru, &conf->r5c_partial_stripe_list); - } } } } @@ -353,17 +355,15 @@ static void release_inactive_stripe_list(struct r5conf *conf, static int release_stripe_list(struct r5conf *conf, struct list_head *temp_inactive_list) { - struct stripe_head *sh; + struct stripe_head *sh, *t; int count = 0; struct llist_node *head; head = llist_del_all(&conf->released_stripes); head = llist_reverse_order(head); - while (head) { + llist_for_each_entry_safe(sh, t, head, release_list) { int hash; - sh = llist_entry(head, struct stripe_head, release_list); - head = llist_next(head); /* sh could be readded after STRIPE_ON_RELEASE_LIST is cleard */ smp_mb(); clear_bit(STRIPE_ON_RELEASE_LIST, &sh->state); @@ -863,6 +863,43 @@ static int use_new_offset(struct r5conf *conf, struct stripe_head *sh) return 1; } +static void flush_deferred_bios(struct r5conf *conf) +{ + struct bio_list tmp; + struct bio *bio; + + if (!conf->batch_bio_dispatch || !conf->group_cnt) + return; + + bio_list_init(&tmp); + spin_lock(&conf->pending_bios_lock); + bio_list_merge(&tmp, &conf->pending_bios); + bio_list_init(&conf->pending_bios); + spin_unlock(&conf->pending_bios_lock); + + while ((bio = bio_list_pop(&tmp))) + generic_make_request(bio); +} + +static void defer_bio_issue(struct r5conf *conf, struct bio *bio) +{ + /* + * change group_cnt will drain all bios, so this is safe + * + * A read generally means a read-modify-write, which usually means a + * randwrite, so we don't delay it + */ + if (!conf->batch_bio_dispatch || !conf->group_cnt || + bio_op(bio) == REQ_OP_READ) { + generic_make_request(bio); + return; + } + spin_lock(&conf->pending_bios_lock); + bio_list_add(&conf->pending_bios, bio); + spin_unlock(&conf->pending_bios_lock); + md_wakeup_thread(conf->mddev->thread); +} + static void raid5_end_read_request(struct bio *bi); static void @@ -1043,7 +1080,7 @@ again: trace_block_bio_remap(bdev_get_queue(bi->bi_bdev), bi, disk_devt(conf->mddev->gendisk), sh->dev[i].sector); - generic_make_request(bi); + defer_bio_issue(conf, bi); } if (rrdev) { if (s->syncing || s->expanding || s->expanded @@ -1088,7 +1125,7 @@ again: trace_block_bio_remap(bdev_get_queue(rbi->bi_bdev), rbi, disk_devt(conf->mddev->gendisk), sh->dev[i].sector); - generic_make_request(rbi); + defer_bio_issue(conf, rbi); } if (!rdev && !rrdev) { if (op_is_write(op)) @@ -2914,12 +2951,36 @@ sector_t raid5_compute_blocknr(struct stripe_head *sh, int i, int previous) * like to flush data in journal to RAID disks first, so complex rmw * is handled in the write patch (handle_stripe_dirtying). * + * 2. when journal space is critical (R5C_LOG_CRITICAL=1) + * + * It is important to be able to flush all stripes in raid5-cache. + * Therefore, we need reserve some space on the journal device for + * these flushes. If flush operation includes pending writes to the + * stripe, we need to reserve (conf->raid_disk + 1) pages per stripe + * for the flush out. If we exclude these pending writes from flush + * operation, we only need (conf->max_degraded + 1) pages per stripe. + * Therefore, excluding pending writes in these cases enables more + * efficient use of the journal device. + * + * Note: To make sure the stripe makes progress, we only delay + * towrite for stripes with data already in journal (injournal > 0). + * When LOG_CRITICAL, stripes with injournal == 0 will be sent to + * no_space_stripes list. + * */ -static inline bool delay_towrite(struct r5dev *dev, - struct stripe_head_state *s) +static inline bool delay_towrite(struct r5conf *conf, + struct r5dev *dev, + struct stripe_head_state *s) { - return !test_bit(R5_OVERWRITE, &dev->flags) && - !test_bit(R5_Insync, &dev->flags) && s->injournal; + /* case 1 above */ + if (!test_bit(R5_OVERWRITE, &dev->flags) && + !test_bit(R5_Insync, &dev->flags) && s->injournal) + return true; + /* case 2 above */ + if (test_bit(R5C_LOG_CRITICAL, &conf->cache_state) && + s->injournal > 0) + return true; + return false; } static void @@ -2942,7 +3003,7 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s, for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; - if (dev->towrite && !delay_towrite(dev, s)) { + if (dev->towrite && !delay_towrite(conf, dev, s)) { set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantdrain, &dev->flags); if (!expand) @@ -3694,7 +3755,7 @@ static int handle_stripe_dirtying(struct r5conf *conf, } else for (i = disks; i--; ) { /* would I have to read this buffer for read_modify_write */ struct r5dev *dev = &sh->dev[i]; - if (((dev->towrite && !delay_towrite(dev, s)) || + if (((dev->towrite && !delay_towrite(conf, dev, s)) || i == sh->pd_idx || i == sh->qd_idx || test_bit(R5_InJournal, &dev->flags)) && !test_bit(R5_LOCKED, &dev->flags) && @@ -3718,8 +3779,8 @@ static int handle_stripe_dirtying(struct r5conf *conf, } } - pr_debug("for sector %llu, rmw=%d rcw=%d\n", - (unsigned long long)sh->sector, rmw, rcw); + pr_debug("for sector %llu state 0x%lx, rmw=%d rcw=%d\n", + (unsigned long long)sh->sector, sh->state, rmw, rcw); set_bit(STRIPE_HANDLE, &sh->state); if ((rmw < rcw || (rmw == rcw && conf->rmw_level == PARITY_PREFER_RMW)) && rmw > 0) { /* prefer read-modify-write, but need to get some data */ @@ -3759,7 +3820,7 @@ static int handle_stripe_dirtying(struct r5conf *conf, for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; - if (((dev->towrite && !delay_towrite(dev, s)) || + if (((dev->towrite && !delay_towrite(conf, dev, s)) || i == sh->pd_idx || i == sh->qd_idx || test_bit(R5_InJournal, &dev->flags)) && !test_bit(R5_LOCKED, &dev->flags) && @@ -4995,9 +5056,9 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio) return 0; } /* - * use bio_clone_mddev to make a copy of the bio + * use bio_clone_fast to make a copy of the bio */ - align_bi = bio_clone_mddev(raid_bio, GFP_NOIO, mddev); + align_bi = bio_clone_fast(raid_bio, GFP_NOIO, mddev->bio_set); if (!align_bi) return 0; /* @@ -5025,6 +5086,13 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio) rdev->recovery_offset >= end_sector))) rdev = NULL; } + + if (r5c_big_stripe_cached(conf, align_bi->bi_iter.bi_sector)) { + rcu_read_unlock(); + bio_put(align_bi); + return 0; + } + if (rdev) { sector_t first_bad; int bad_sectors; @@ -5381,7 +5449,6 @@ static void raid5_make_request(struct mddev *mddev, struct bio * bi) * data on failed drives. */ if (rw == READ && mddev->degraded == 0 && - !r5c_is_writeback(conf->log) && mddev->reshape_position == MaxSector) { bi = chunk_aligned_read(mddev, bi); if (!bi) @@ -6126,6 +6193,8 @@ static void raid5d(struct md_thread *thread) mutex_unlock(&conf->cache_size_mutex); } + flush_deferred_bios(conf); + r5l_flush_stripe_to_raid(conf->log); async_tx_issue_pending_all(); @@ -6711,6 +6780,18 @@ static struct r5conf *setup_conf(struct mddev *mddev) atomic_set(&conf->active_stripes, 0); atomic_set(&conf->preread_active_stripes, 0); atomic_set(&conf->active_aligned_reads, 0); + bio_list_init(&conf->pending_bios); + spin_lock_init(&conf->pending_bios_lock); + conf->batch_bio_dispatch = true; + rdev_for_each(rdev, mddev) { + if (test_bit(Journal, &rdev->flags)) + continue; + if (blk_queue_nonrot(bdev_get_queue(rdev->bdev))) { + conf->batch_bio_dispatch = false; + break; + } + } + conf->bypass_threshold = BYPASS_THRESHOLD; conf->recovery_disabled = mddev->recovery_disabled - 1; @@ -6757,6 +6838,8 @@ static struct r5conf *setup_conf(struct mddev *mddev) INIT_LIST_HEAD(&conf->r5c_full_stripe_list); atomic_set(&conf->r5c_cached_partial_stripes, 0); INIT_LIST_HEAD(&conf->r5c_partial_stripe_list); + atomic_set(&conf->r5c_flushing_full_stripes, 0); + atomic_set(&conf->r5c_flushing_partial_stripes, 0); conf->level = mddev->new_level; conf->chunk_sectors = mddev->new_chunk_sectors; diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 1440fa26e296..4bb27b97bf6b 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -663,6 +663,8 @@ struct r5conf { struct list_head r5c_full_stripe_list; atomic_t r5c_cached_partial_stripes; struct list_head r5c_partial_stripe_list; + atomic_t r5c_flushing_full_stripes; + atomic_t r5c_flushing_partial_stripes; atomic_t empty_inactive_list_nr; struct llist_head released_stripes; @@ -684,6 +686,10 @@ struct r5conf { int group_cnt; int worker_cnt_per_group; struct r5l_log *log; + + struct bio_list pending_bios; + spinlock_t pending_bios_lock; + bool batch_bio_dispatch; }; @@ -788,4 +794,5 @@ extern void r5c_check_stripe_cache_usage(struct r5conf *conf); extern void r5c_check_cached_full_stripe(struct r5conf *conf); extern struct md_sysfs_entry r5c_journal_mode; extern void r5c_update_on_rdev_error(struct mddev *mddev); +extern bool r5c_big_stripe_cached(struct r5conf *conf, sector_t sect); #endif diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 000d737ad827..8d65028c7a74 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -34,7 +34,7 @@ #include <linux/vmalloc.h> #include <linux/delay.h> #include <linux/spinlock.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/kthread.h> #include "dvb_ca_en50221.h" diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 4eac71e50c5f..6628f80d184f 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -19,7 +19,7 @@ #define pr_fmt(fmt) "dvb_demux: " fmt -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/vmalloc.h> diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 85ae3669aa66..e3fff8f64d37 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -29,7 +29,7 @@ #include <linux/string.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/slab.h> #include <linux/poll.h> diff --git a/drivers/media/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h index bbe94873d44d..8ed6bcc3a56e 100644 --- a/drivers/media/dvb-core/dvb_ringbuffer.h +++ b/drivers/media/dvb-core/dvb_ringbuffer.h @@ -136,7 +136,7 @@ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf); } /** - * dvb_ringbuffer_read_user - Reads a buffer into an user pointer + * dvb_ringbuffer_read_user - Reads a buffer into a user pointer * * @rbuf: pointer to struct dvb_ringbuffer * @buf: pointer to the buffer where the data will be stored @@ -193,7 +193,7 @@ extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len); /** - * dvb_ringbuffer_write_user - Writes a buffer received via an user pointer + * dvb_ringbuffer_write_user - Writes a buffer received via a user pointer * * @rbuf: pointer to struct dvb_ringbuffer * @buf: pointer to the buffer where the data will be read diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h index 9076bf21cc8a..7a681d8202c7 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h +++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h @@ -1317,9 +1317,9 @@ struct drx_version_list { DRX_MPEG_STR_WIDTH_8 }; -/* CTRL CFG MPEG ouput */ +/* CTRL CFG MPEG output */ /** -* \struct struct drx_cfg_mpeg_output * \brief Configuartion parameters for MPEG output control. +* \struct struct drx_cfg_mpeg_output * \brief Configuration parameters for MPEG output control. * * Used by DRX_CFG_MPEG_OUTPUT, in combination with DRX_CTRL_SET_CFG and * DRX_CTRL_GET_CFG. diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index f1c3e3b09b65..daeaf965dd56 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -601,7 +601,7 @@ static struct drxj_data drxj_data_g = { 0, /* hi_cfg_wake_up_key */ 0, /* hi_cfg_ctrl */ 0, /* HICfgTimeout */ - /* UIO configuartion */ + /* UIO configuration */ DRX_UIO_MODE_DISABLE, /* uio_sma_rx_mode */ DRX_UIO_MODE_DISABLE, /* uio_sma_tx_mode */ DRX_UIO_MODE_DISABLE, /* uioASELMode */ @@ -619,7 +619,7 @@ static struct drxj_data drxj_data_g = { /* false, * flagHDevSet */ /* (u16) 0xFFF, * rdsLastCount */ - /* ATV configuartion */ + /* ATV configuration */ 0UL, /* flags cfg changes */ /* shadow of ATV_TOP_EQU0__A */ {-5, @@ -3352,7 +3352,7 @@ rw_error: /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ -/* miscellaneous configuartions - begin */ +/* miscellaneous configurations - begin */ /*----------------------------------------------------------------------------*/ /** @@ -3515,7 +3515,7 @@ rw_error: } /*----------------------------------------------------------------------------*/ -/* miscellaneous configuartions - end */ +/* miscellaneous configurations - end */ /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ @@ -10952,7 +10952,7 @@ rw_error: static void drxj_reset_mode(struct drxj_data *ext_attr) { - /* Initialize default AFE configuartion for QAM */ + /* Initialize default AFE configuration for QAM */ if (ext_attr->has_lna) { /* IF AGC off, PGA active */ #ifndef DRXJ_VSB_ONLY @@ -10996,7 +10996,7 @@ static void drxj_reset_mode(struct drxj_data *ext_attr) ext_attr->qam_pre_saw_cfg.reference = 0x07; ext_attr->qam_pre_saw_cfg.use_pre_saw = true; #endif - /* Initialize default AFE configuartion for VSB */ + /* Initialize default AFE configuration for VSB */ ext_attr->vsb_rf_agc_cfg.standard = DRX_STANDARD_8VSB; ext_attr->vsb_rf_agc_cfg.ctrl_mode = DRX_AGC_CTRL_AUTO; ext_attr->vsb_rf_agc_cfg.min_output_level = 0; @@ -11072,9 +11072,9 @@ ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode) } if ((*mode == DRX_POWER_UP)) { - /* Restore analog & pin configuartion */ + /* Restore analog & pin configuration */ - /* Initialize default AFE configuartion for VSB */ + /* Initialize default AFE configuration for VSB */ drxj_reset_mode(ext_attr); } else { /* Power down to requested mode */ diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.h b/drivers/media/dvb-frontends/drx39xyj/drxj.h index 55ad535197d2..6c5b8f78f9f6 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.h +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.h @@ -447,7 +447,7 @@ struct drxj_cfg_atv_output { u16 hi_cfg_ctrl; /**< HI Configure() parameter 5 */ u16 hi_cfg_transmit; /**< HI Configure() parameter 6 */ - /* UIO configuartion */ + /* UIO configuration */ enum drxuio_mode uio_sma_rx_mode;/**< current mode of SmaRx pin */ enum drxuio_mode uio_sma_tx_mode;/**< current mode of SmaTx pin */ enum drxuio_mode uio_gpio_mode; /**< current mode of ASEL pin */ @@ -459,7 +459,7 @@ struct drxj_cfg_atv_output { /* IQM RC frequecy shift */ u32 iqm_rc_rate_ofs; /**< frequency shifter setting after setchannel */ - /* ATV configuartion */ + /* ATV configuration */ u32 atv_cfg_changed_flags; /**< flag: flags cfg changes */ s16 atv_top_equ0[DRXJ_COEF_IDX_MAX]; /**< shadow of ATV_TOP_EQU0__A */ s16 atv_top_equ1[DRXJ_COEF_IDX_MAX]; /**< shadow of ATV_TOP_EQU1__A */ diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 15d2cac588b1..7e1bbbaad625 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -1626,7 +1626,7 @@ static int ctrl_power_mode(struct drxk_state *state, enum drx_power_mode *mode) } if (*mode == DRX_POWER_UP) { - /* Restore analog & pin configuartion */ + /* Restore analog & pin configuration */ } else { /* Power down to requested mode */ /* Backup some register settings */ diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index ef35c2b30ea3..4bf5a551ba40 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -309,7 +309,7 @@ static int helene_write_regs(struct helene_priv *priv, if (len + 1 > sizeof(buf)) { dev_warn(&priv->i2c->dev, - "wr reg=%04x: len=%d vs %Zu is too big!\n", + "wr reg=%04x: len=%d vs %zu is too big!\n", reg, len + 1, sizeof(buf)); return -E2BIG; } diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c index 4b67d7e0116d..62aa00767015 100644 --- a/drivers/media/dvb-frontends/or51132.c +++ b/drivers/media/dvb-frontends/or51132.c @@ -133,7 +133,7 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware u32 firmwareAsize, firmwareBsize; int i,ret; - dprintk("Firmware is %Zd bytes\n",fw->size); + dprintk("Firmware is %zd bytes\n",fw->size); /* Get size of firmware A and B */ firmwareAsize = le32_to_cpu(*((__le32*)fw->data)); diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c index 92ab34c3e0be..143b39b5f6c9 100644 --- a/drivers/media/dvb-frontends/tda10048.c +++ b/drivers/media/dvb-frontends/tda10048.c @@ -499,7 +499,7 @@ static int tda10048_firmware_upload(struct dvb_frontend *fe) __func__); return -EIO; } else { - printk(KERN_INFO "%s: firmware read %Zu bytes.\n", + printk(KERN_INFO "%s: firmware read %zu bytes.\n", __func__, fw->size); ret = 0; diff --git a/drivers/media/i2c/adv7183_regs.h b/drivers/media/i2c/adv7183_regs.h index 843d4998435e..4ade89d33d62 100644 --- a/drivers/media/i2c/adv7183_regs.h +++ b/drivers/media/i2c/adv7183_regs.h @@ -83,7 +83,7 @@ #define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ #define ADV7183_CRC_EN 0xB2 /* CRC enable */ #define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ -#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ +#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC switch 2 */ #define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ #define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ #define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h index fef3c736fcba..7be2088c45fe 100644 --- a/drivers/media/pci/cx18/cx18-driver.h +++ b/drivers/media/pci/cx18/cx18-driver.h @@ -24,7 +24,7 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/fs.h> #include <linux/pci.h> #include <linux/interrupt.h> diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index ab2ae53618e8..e73c153285f0 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -59,6 +59,7 @@ #include <media/tveeprom.h> #include <media/i2c/saa7115.h> #include "tuner-xc2028.h" +#include <uapi/linux/sched/types.h> /* If you have already X v4l cards, then set this to X. This way the device numbers stay matched. Example: you have a WinTV card diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h index cde452e30746..d27c5c2c07ea 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.h +++ b/drivers/media/pci/ivtv/ivtv-driver.h @@ -38,37 +38,38 @@ * using information provided by Jiun-Kuei Jung @ AVerMedia. */ -#include <asm/byteorder.h> +#include <linux/module.h> +#include <linux/init.h> #include <linux/delay.h> -#include <linux/device.h> +#include <linux/sched/signal.h> #include <linux/fs.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/ivtv.h> -#include <linux/kernel.h> -#include <linux/kthread.h> #include <linux/list.h> -#include <linux/module.h> -#include <linux/mutex.h> +#include <linux/unistd.h> #include <linux/pagemap.h> -#include <linux/pci.h> #include <linux/scatterlist.h> -#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/mutex.h> #include <linux/slab.h> -#include <linux/spinlock.h> #include <linux/uaccess.h> -#include <linux/unistd.h> +#include <asm/byteorder.h> -#include <media/drv-intf/cx2341x.h> -#include <media/i2c/ir-kbd-i2c.h> -#include <media/tuner.h> +#include <linux/dvb/video.h> +#include <linux/dvb/audio.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fh.h> -#include <media/v4l2-ioctl.h> +#include <media/tuner.h> +#include <media/drv-intf/cx2341x.h> +#include <media/i2c/ir-kbd-i2c.h> + +#include <linux/ivtv.h> /* Memory layout */ #define IVTV_ENCODER_OFFSET 0x00000000 diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c index da1eebd2016f..3219d2f3271e 100644 --- a/drivers/media/pci/pt1/pt1.c +++ b/drivers/media/pci/pt1/pt1.c @@ -18,6 +18,7 @@ */ #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/vmalloc.h> diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c index 77f4d15f322b..e8b5d0992157 100644 --- a/drivers/media/pci/pt3/pt3.c +++ b/drivers/media/pci/pt3/pt3.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/string.h> +#include <linux/sched/signal.h> #include "dmxdev.h" #include "dvbdev.h" diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c index 4ba5eade7ce2..ef4906406ebf 100644 --- a/drivers/media/pci/saa7164/saa7164-fw.c +++ b/drivers/media/pci/saa7164/saa7164-fw.c @@ -422,7 +422,7 @@ int saa7164_downloadfirmware(struct saa7164_dev *dev) return -ENOMEM; } - printk(KERN_INFO "%s() firmware read %Zu bytes.\n", + printk(KERN_INFO "%s() firmware read %zu bytes.\n", __func__, fw->size); if (fw->size != fwlength) { diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c index c908672b2c40..e83bb79f9349 100644 --- a/drivers/media/pci/solo6x10/solo6x10-i2c.c +++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c @@ -27,6 +27,7 @@ * thread context, ACK the interrupt, and move on. -- BenC */ #include <linux/kernel.h> +#include <linux/sched/signal.h> #include "solo6x10.h" diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index 671907a6e6b6..40adceebca7e 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -28,6 +28,7 @@ #include <linux/module.h> #include <linux/vmalloc.h> #include <linux/ktime.h> +#include <linux/sched/signal.h> #include <linux/interrupt.h> #include <linux/proc_fs.h> diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 5615fefbf7af..c0373aede81a 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -358,7 +358,7 @@ struct fimc_pix_limit { * @pix_limit: pixel size constraints for the scaler * @min_inp_pixsize: minimum input pixel size * @min_out_pixsize: minimum output pixel size - * @hor_offs_align: horizontal pixel offset aligment + * @hor_offs_align: horizontal pixel offset alignment * @min_vsize_align: minimum vertical pixel size alignment */ struct fimc_variant { diff --git a/drivers/media/platform/vivid/vivid-radio-rx.c b/drivers/media/platform/vivid/vivid-radio-rx.c index f99092ca8f5c..47c36c26096b 100644 --- a/drivers/media/platform/vivid/vivid-radio-rx.c +++ b/drivers/media/platform/vivid/vivid-radio-rx.c @@ -22,6 +22,8 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include <linux/v4l2-dv-timings.h> +#include <linux/sched/signal.h> + #include <media/v4l2-common.h> #include <media/v4l2-event.h> #include <media/v4l2-dv-timings.h> diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/platform/vivid/vivid-radio-tx.c index 8c59d4f53200..0e8025b7b4dd 100644 --- a/drivers/media/platform/vivid/vivid-radio-tx.c +++ b/drivers/media/platform/vivid/vivid-radio-tx.c @@ -19,6 +19,7 @@ #include <linux/errno.h> #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/delay.h> #include <linux/videodev2.h> #include <linux/v4l2-dv-timings.h> diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index a54ca531d8ef..393dccaabdd0 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -19,7 +19,7 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/fs.h> diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 0345b274eccc..91947cf1950e 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1144,7 +1144,7 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) pr_err("xc5000: Upload failed. rc %d\n", ret); return ret; } - dprintk(1, "firmware read %Zu bytes.\n", fw->size); + dprintk(1, "firmware read %zu bytes.\n", fw->size); if (fw->size != desired_fw->size) { pr_err("xc5000: Firmware file with incorrect size\n"); diff --git a/drivers/media/usb/cpia2/cpia2_core.c b/drivers/media/usb/cpia2/cpia2_core.c index 431dd0b4b332..b1d13444ff30 100644 --- a/drivers/media/usb/cpia2/cpia2_core.c +++ b/drivers/media/usb/cpia2/cpia2_core.c @@ -32,6 +32,7 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/firmware.h> +#include <linux/sched/signal.h> #define FIRMWARE "cpia2/stv0672_vp4.bin" MODULE_FIRMWARE(FIRMWARE); diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 81d7fd4f7776..85ab3fa48f9a 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -2414,7 +2414,7 @@ static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap) deb_info("%s: Upload failed. (file not found?)\n", __func__); return -ENODEV; } else { - deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); + deb_info("%s: firmware read %zu bytes.\n", __func__, state->frontend_firmware->size); } stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size; stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data; @@ -2480,7 +2480,7 @@ static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap) deb_info("%s: Upload failed. (file not found?)\n", __func__); return -EIO; } else { - deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); + deb_info("%s: firmware read %zu bytes.\n", __func__, state->frontend_firmware->size); } nim9090md_config[0].microcode_B_fe_size = state->frontend_firmware->size; nim9090md_config[0].microcode_B_fe_buffer = state->frontend_firmware->data; diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c index 23d3285f182a..e91d00762e94 100644 --- a/drivers/media/usb/gspca/cpia1.c +++ b/drivers/media/usb/gspca/cpia1.c @@ -27,6 +27,8 @@ #define MODULE_NAME "cpia1" #include <linux/input.h> +#include <linux/sched/signal.h> + #include "gspca.h" MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c index 42667710af92..46fb76349000 100644 --- a/drivers/media/usb/gspca/t613.c +++ b/drivers/media/usb/gspca/t613.c @@ -570,9 +570,9 @@ static void setfreq(struct gspca_dev *gspca_dev, s32 val) /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - /* some of this registers are not really neded, because - * they are overriden by setbrigthness, setcontrast, etc, - * but wont hurt anyway, and can help someone with similar webcam + /* some of this registers are not really needed, because + * they are overridden by setbrigthness, setcontrast, etc., + * but won't hurt anyway, and can help someone with similar webcam * to see the initial parameters.*/ struct sd *sd = (struct sd *) gspca_dev; const struct additional_sensor_data *sensor; diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index 4afd4655d562..39c15bb2b20c 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -438,7 +438,7 @@ int tm6000_ir_init(struct tm6000_core *dev) /* input setup */ rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC; - /* Neded, in order to support NEC remotes with 24 or 32 bits */ + /* Needed, in order to support NEC remotes with 24 or 32 bits */ rc->scancode_mask = 0xffff; rc->priv = ir; rc->change_protocol = tm6000_ir_change_protocol; diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 05b5c6652cfa..e48b7c032c95 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -245,7 +245,7 @@ static const struct analog_demod_ops tuner_analog_ops = { * @tuner_callback: an optional function to be called when switching * to analog mode * - * This function applys the tuner config to tuner specified + * This function applies the tuner config to tuner specified * by tun_setup structure. It contains several per-tuner initialization "magic" */ static void set_type(struct i2c_client *c, unsigned int type, @@ -463,7 +463,7 @@ attach_failed: * @sd: subdev descriptor * @tun_setup: type to be associated to a given tuner i2c address * - * This function applys the tuner config to tuner specified + * This function applies the tuner config to tuner specified * by tun_setup structure. * If tuner I2C address is UNSET, then it will only set the device * if the tuner supports the mode specified in the call. diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index ba63ca57ed7e..0b5c43f7e020 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -21,7 +21,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/slab.h> #include <linux/interrupt.h> @@ -434,8 +434,9 @@ static void videobuf_vm_close(struct vm_area_struct *vma) * now ...). Bounce buffers don't work very well for the data rates * video capture has. */ -static int videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int videobuf_vm_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct page *page; dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n", diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 047d6fcdcec2..4e83a8b92665 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -76,9 +76,11 @@ struct at91_ebi_caps { struct at91_ebi { struct clk *clk; - struct regmap *smc; struct regmap *matrix; - + struct { + struct regmap *regmap; + struct clk *clk; + } smc; struct regmap_field *ebi_csa; struct device *dev; @@ -93,7 +95,7 @@ static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid, struct at91_ebi_dev_config *conf) { struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; - unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); + unsigned int clk_period = NSEC_PER_SEC / clk_get_rate(ebid->ebi->clk); struct at91sam9_ebi_dev_config *config = &conf->sam9; struct at91sam9_smc_timings *timings = &config->timings; unsigned int val; @@ -102,43 +104,43 @@ static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid, config->mode = val & ~AT91_SMC_TDF; val = (val & AT91_SMC_TDF) >> 16; - timings->tdf_ns = clk_rate * val; + timings->tdf_ns = clk_period * val; regmap_fields_read(fields->setup, conf->cs, &val); timings->ncs_rd_setup_ns = (val >> 24) & 0x1f; timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128; - timings->ncs_rd_setup_ns *= clk_rate; + timings->ncs_rd_setup_ns *= clk_period; timings->nrd_setup_ns = (val >> 16) & 0x1f; timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128; - timings->nrd_setup_ns *= clk_rate; + timings->nrd_setup_ns *= clk_period; timings->ncs_wr_setup_ns = (val >> 8) & 0x1f; timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128; - timings->ncs_wr_setup_ns *= clk_rate; + timings->ncs_wr_setup_ns *= clk_period; timings->nwe_setup_ns = val & 0x1f; timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128; - timings->nwe_setup_ns *= clk_rate; + timings->nwe_setup_ns *= clk_period; regmap_fields_read(fields->pulse, conf->cs, &val); timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f; timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256; - timings->ncs_rd_pulse_ns *= clk_rate; + timings->ncs_rd_pulse_ns *= clk_period; timings->nrd_pulse_ns = (val >> 16) & 0x3f; timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256; - timings->nrd_pulse_ns *= clk_rate; + timings->nrd_pulse_ns *= clk_period; timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f; timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256; - timings->ncs_wr_pulse_ns *= clk_rate; + timings->ncs_wr_pulse_ns *= clk_period; timings->nwe_pulse_ns = val & 0x3f; timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256; - timings->nwe_pulse_ns *= clk_rate; + timings->nwe_pulse_ns *= clk_period; regmap_fields_read(fields->cycle, conf->cs, &val); timings->nrd_cycle_ns = (val >> 16) & 0x7f; timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256; - timings->nrd_cycle_ns *= clk_rate; + timings->nrd_cycle_ns *= clk_period; timings->nwe_cycle_ns = val & 0x7f; timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256; - timings->nwe_cycle_ns *= clk_rate; + timings->nwe_cycle_ns *= clk_period; } static int at91_xlate_timing(struct device_node *np, const char *prop, @@ -334,6 +336,7 @@ static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid, struct at91_ebi_dev_config *conf) { unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); + unsigned int clk_period = NSEC_PER_SEC / clk_rate; struct at91sam9_ebi_dev_config *config = &conf->sam9; struct at91sam9_smc_timings *timings = &config->timings; struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; @@ -376,7 +379,7 @@ static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid, val |= AT91SAM9_SMC_NWECYCLE(coded_val); regmap_fields_write(fields->cycle, conf->cs, val); - val = DIV_ROUND_UP(timings->tdf_ns, clk_rate); + val = DIV_ROUND_UP(timings->tdf_ns, clk_period); if (val > AT91_SMC_TDF_MAX) val = AT91_SMC_TDF_MAX; regmap_fields_write(fields->mode, conf->cs, @@ -394,22 +397,26 @@ static int at91sam9_ebi_init(struct at91_ebi *ebi) field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ; field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC); - fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); if (IS_ERR(fields->setup)) return PTR_ERR(fields->setup); field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC); - fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); if (IS_ERR(fields->pulse)) return PTR_ERR(fields->pulse); field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC); - fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); if (IS_ERR(fields->cycle)) return PTR_ERR(fields->cycle); field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC); - fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); return PTR_ERR_OR_ZERO(fields->mode); } @@ -422,22 +429,26 @@ static int sama5d3_ebi_init(struct at91_ebi *ebi) field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ; field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC); - fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); if (IS_ERR(fields->setup)) return PTR_ERR(fields->setup); field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC); - fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); if (IS_ERR(fields->pulse)) return PTR_ERR(fields->pulse); field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC); - fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); if (IS_ERR(fields->cycle)) return PTR_ERR(fields->cycle); field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC); - fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc.regmap, + field); return PTR_ERR_OR_ZERO(fields->mode); } @@ -448,12 +459,31 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, struct at91_ebi_dev_config conf = { }; struct device *dev = ebi->dev; struct at91_ebi_dev *ebid; - int ret, numcs = 0, i; + unsigned long cslines = 0; + int ret, numcs = 0, nentries, i; bool apply = false; + u32 cs; - numcs = of_property_count_elems_of_size(np, "reg", - reg_cells * sizeof(u32)); - if (numcs <= 0) { + nentries = of_property_count_elems_of_size(np, "reg", + reg_cells * sizeof(u32)); + for (i = 0; i < nentries; i++) { + ret = of_property_read_u32_index(np, "reg", i * reg_cells, + &cs); + if (ret) + return ret; + + if (cs >= AT91_MATRIX_EBI_NUM_CS || + !(ebi->caps->available_cs & BIT(cs))) { + dev_err(dev, "invalid reg property in %s\n", + np->full_name); + return -EINVAL; + } + + if (!test_and_set_bit(cs, &cslines)) + numcs++; + } + + if (!numcs) { dev_err(dev, "invalid reg property in %s\n", np->full_name); return -EINVAL; } @@ -472,21 +502,8 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, else if (ret) apply = true; - for (i = 0; i < numcs; i++) { - u32 cs; - - ret = of_property_read_u32_index(np, "reg", i * reg_cells, - &cs); - if (ret) - return ret; - - if (cs > AT91_MATRIX_EBI_NUM_CS || - !(ebi->caps->available_cs & BIT(cs))) { - dev_err(dev, "invalid reg property in %s\n", - np->full_name); - return -EINVAL; - } - + i = 0; + for_each_set_bit(cs, &cslines, AT91_MATRIX_EBI_NUM_CS) { ebid->configs[i].cs = cs; if (apply) { @@ -502,9 +519,11 @@ static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, * Attach the EBI device to the generic SMC logic if at least * one "atmel,smc-" property is present. */ - if (ebi->ebi_csa && ret) + if (ebi->ebi_csa && apply) regmap_field_update_bits(ebi->ebi_csa, BIT(cs), 0); + + i++; } list_add_tail(&ebid->node, &ebi->devs); @@ -668,7 +687,7 @@ static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np) static int at91_ebi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *child, *np = dev->of_node; + struct device_node *child, *np = dev->of_node, *smc_np; const struct of_device_id *match; struct at91_ebi *ebi; int ret, reg_cells; @@ -693,9 +712,22 @@ static int at91_ebi_probe(struct platform_device *pdev) ebi->clk = clk; - ebi->smc = syscon_regmap_lookup_by_phandle(np, "atmel,smc"); - if (IS_ERR(ebi->smc)) - return PTR_ERR(ebi->smc); + smc_np = of_parse_phandle(dev->of_node, "atmel,smc", 0); + + ebi->smc.regmap = syscon_node_to_regmap(smc_np); + if (IS_ERR(ebi->smc.regmap)) + return PTR_ERR(ebi->smc.regmap); + + ebi->smc.clk = of_clk_get(smc_np, 0); + if (IS_ERR(ebi->smc.clk)) { + if (PTR_ERR(ebi->smc.clk) != -ENOENT) + return PTR_ERR(ebi->smc.clk); + + ebi->smc.clk = NULL; + } + ret = clk_prepare_enable(ebi->smc.clk); + if (ret) + return ret; /* * The sama5d3 does not provide an EBICSA register and thus does need diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 06cc781ebac1..392dc8dd481f 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -1115,11 +1115,10 @@ static int tegra_emc_probe(struct platform_device *pdev) } mc = of_find_device_by_node(np); + of_node_put(np); if (!mc) return -ENOENT; - of_node_put(np); - emc->mc = platform_get_drvdata(mc); if (!emc->mc) return -EPROBE_DEFER; @@ -1135,9 +1134,7 @@ static int tegra_emc_probe(struct platform_device *pdev) } err = tegra_emc_load_timings_from_dt(emc, np); - of_node_put(np); - if (err) return err; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d0c14b88b24e..55ecdfb74d31 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -46,6 +46,7 @@ config MFD_SUN4I_GPADC select REGMAP_MMIO select REGMAP_IRQ depends on ARCH_SUNXI || COMPILE_TEST + depends on !TOUCHSCREEN_SUN4I help Select this to get support for Allwinner SoCs (A10, A13 and A31) ADC. This driver will only map the hardware interrupt and registers, you @@ -506,17 +507,22 @@ config MFD_KEMPLD device may provide functions like watchdog, GPIO, UART and I2C bus. The following modules are supported: + * COMe-bBD# * COMe-bBL6 * COMe-bHL6 + * COMe-bSL6 * COMe-bIP# + * COMe-bKL6 * COMe-bPC2 (ETXexpress-PC) * COMe-bSC# (ETXexpress-SC T#) + * COMe-cAL6 * COMe-cBL6 * COMe-cBT6 * COMe-cBW6 * COMe-cCT6 * COMe-cDC2 (microETXexpress-DC) * COMe-cHL6 + * COMe-cKL6 * COMe-cPC2 (microETXexpress-PC) * COMe-cSL6 * COMe-mAL10 @@ -714,6 +720,17 @@ config EZX_PCAP This enables the PCAP ASIC present on EZX Phones. This is needed for MMC, TouchScreen, Sound, USB, etc.. +config MFD_CPCAP + tristate "Support for Motorola CPCAP" + depends on SPI + depends on OF || COMPILE_TEST + select REGMAP_SPI + select REGMAP_IRQ + help + Say yes here if you want to include driver for CPCAP. + It is used on many Motorola phones and tablets as a PMIC. + At least Motorola Droid 4 is known to use CPCAP. + config MFD_VIPERBOARD tristate "Nano River Technologies Viperboard" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 876ca8600c51..31ce07611a6f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o obj-$(CONFIG_MFD_CORE) += mfd-core.o obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o +obj-$(CONFIG_MFD_CPCAP) += motorola-cpcap.o obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 6e00124cef01..8511c068a610 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -656,8 +656,8 @@ static const struct mfd_cell ab8500_devs[] = { .of_compatible = "stericsson,ab8500-regulator", }, { - .name = "abx500-clk", - .of_compatible = "stericsson,abx500-clk", + .name = "ab8500-clk", + .of_compatible = "stericsson,ab8500-clk", }, { .name = "ab8500-gpadc", diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 80c0efa66ac1..5b0a0850ef69 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -101,7 +101,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value) u8 bank; if (sysctrl_dev == NULL) - return -EINVAL; + return -EPROBE_DEFER; bank = (reg >> 8); if (!valid_bank(bank)) @@ -117,11 +117,13 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) u8 bank; if (sysctrl_dev == NULL) - return -EINVAL; + return -EPROBE_DEFER; bank = (reg >> 8); - if (!valid_bank(bank)) + if (!valid_bank(bank)) { + pr_err("invalid bank\n"); return -EINVAL; + } return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank, (u8)(reg & 0xFF), mask, value); @@ -148,9 +150,15 @@ static int ab8500_sysctrl_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ab8500_sysctrl_match[] = { + { .compatible = "stericsson,ab8500-sysctrl", }, + {} +}; + static struct platform_driver ab8500_sysctrl_driver = { .driver = { .name = "ab8500-sysctrl", + .of_match_table = ab8500_sysctrl_match, }, .probe = ab8500_sysctrl_probe, .remove = ab8500_sysctrl_remove, diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 2e01975f042d..09cf3699e354 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -26,6 +26,9 @@ #include "arizona.h" +#define ARIZONA_AOD_IRQ_INDEX 0 +#define ARIZONA_MAIN_IRQ_INDEX 1 + static int arizona_map_irq(struct arizona *arizona, int irq) { int ret; @@ -204,9 +207,10 @@ static const struct irq_domain_ops arizona_domain_ops = { int arizona_irq_init(struct arizona *arizona) { int flags = IRQF_ONESHOT; - int ret, i; + int ret; const struct regmap_irq_chip *aod, *irq; struct irq_data *irq_data; + unsigned int virq; arizona->ctrlif_error = true; @@ -318,24 +322,34 @@ int arizona_irq_init(struct arizona *arizona) } if (aod) { - ret = regmap_add_irq_chip(arizona->regmap, - irq_create_mapping(arizona->virq, 0), - IRQF_ONESHOT, 0, aod, - &arizona->aod_irq_chip); + virq = irq_create_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX); + if (!virq) { + dev_err(arizona->dev, "Failed to map AOD IRQs\n"); + ret = -EINVAL; + goto err_domain; + } + + ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT, + 0, aod, &arizona->aod_irq_chip); if (ret != 0) { dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret); - goto err; + goto err_map_aod; } } - ret = regmap_add_irq_chip(arizona->regmap, - irq_create_mapping(arizona->virq, 1), - IRQF_ONESHOT, 0, irq, - &arizona->irq_chip); + virq = irq_create_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX); + if (!virq) { + dev_err(arizona->dev, "Failed to map main IRQs\n"); + ret = -EINVAL; + goto err_aod; + } + + ret = regmap_add_irq_chip(arizona->regmap, virq, IRQF_ONESHOT, + 0, irq, &arizona->irq_chip); if (ret != 0) { dev_err(arizona->dev, "Failed to add main IRQs: %d\n", ret); - goto err_aod; + goto err_map_main_irq; } /* Used to emulate edge trigger and to work around broken pinmux */ @@ -368,9 +382,8 @@ int arizona_irq_init(struct arizona *arizona) } /* Make sure the boot done IRQ is unmasked for resumes */ - i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE); - ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT, - "Boot done", arizona); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_BOOT_DONE, "Boot done", + arizona_boot_done, arizona); if (ret != 0) { dev_err(arizona->dev, "Failed to request boot done %d: %d\n", arizona->irq, ret); @@ -379,10 +392,9 @@ int arizona_irq_init(struct arizona *arizona) /* Handle control interface errors in the core */ if (arizona->ctrlif_error) { - i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR); - ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, - IRQF_ONESHOT, - "Control interface error", arizona); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, + "Control interface error", + arizona_ctrlif_err, arizona); if (ret != 0) { dev_err(arizona->dev, "Failed to request CTRLIF_ERR %d: %d\n", @@ -394,29 +406,47 @@ int arizona_irq_init(struct arizona *arizona) return 0; err_ctrlif: - free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona); err_boot_done: free_irq(arizona->irq, arizona); err_main_irq: - regmap_del_irq_chip(irq_find_mapping(arizona->virq, 1), + regmap_del_irq_chip(irq_find_mapping(arizona->virq, + ARIZONA_MAIN_IRQ_INDEX), arizona->irq_chip); +err_map_main_irq: + irq_dispose_mapping(irq_find_mapping(arizona->virq, + ARIZONA_MAIN_IRQ_INDEX)); err_aod: - regmap_del_irq_chip(irq_find_mapping(arizona->virq, 0), + regmap_del_irq_chip(irq_find_mapping(arizona->virq, + ARIZONA_AOD_IRQ_INDEX), arizona->aod_irq_chip); +err_map_aod: + irq_dispose_mapping(irq_find_mapping(arizona->virq, + ARIZONA_AOD_IRQ_INDEX)); +err_domain: + irq_domain_remove(arizona->virq); err: return ret; } int arizona_irq_exit(struct arizona *arizona) { + unsigned int virq; + if (arizona->ctrlif_error) - free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), - arizona); - free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); - regmap_del_irq_chip(irq_find_mapping(arizona->virq, 1), - arizona->irq_chip); - regmap_del_irq_chip(irq_find_mapping(arizona->virq, 0), - arizona->aod_irq_chip); + arizona_free_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_BOOT_DONE, arizona); + + virq = irq_find_mapping(arizona->virq, ARIZONA_MAIN_IRQ_INDEX); + regmap_del_irq_chip(virq, arizona->irq_chip); + irq_dispose_mapping(virq); + + virq = irq_find_mapping(arizona->virq, ARIZONA_AOD_IRQ_INDEX); + regmap_del_irq_chip(virq, arizona->aod_irq_chip); + irq_dispose_mapping(virq); + + irq_domain_remove(arizona->virq); + free_irq(arizona->irq, arizona); return 0; diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h index 198e9cea77f9..a0bddc5bd043 100644 --- a/drivers/mfd/arizona.h +++ b/drivers/mfd/arizona.h @@ -17,8 +17,6 @@ #include <linux/regmap.h> #include <linux/pm.h> -struct wm_arizona; - extern const struct regmap_config wm5102_i2c_regmap; extern const struct regmap_config wm5102_spi_regmap; diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index ed918de84238..25115fe2acdf 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -31,6 +31,8 @@ #define AXP20X_OFF 0x80 +#define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4) + static const char * const axp20x_model_names[] = { "AXP152", "AXP202", @@ -118,7 +120,14 @@ static const struct regmap_range axp288_writeable_ranges[] = { }; static const struct regmap_range axp288_volatile_ranges[] = { + regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP288_POWER_REASON), + regmap_reg_range(AXP288_BC_GLOBAL, AXP288_BC_GLOBAL), + regmap_reg_range(AXP288_BC_DET_STAT, AXP288_BC_DET_STAT), regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), + regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL), + regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE), + regmap_reg_range(AXP288_RT_BATT_V_H, AXP288_RT_BATT_V_L), + regmap_reg_range(AXP20X_FG_RES, AXP288_FG_CC_CAP_REG), }; static const struct regmap_access_table axp288_writeable_table = { @@ -207,14 +216,14 @@ static struct resource axp22x_pek_resources[] = { static struct resource axp288_power_button_resources[] = { { .name = "PEK_DBR", - .start = AXP288_IRQ_POKN, - .end = AXP288_IRQ_POKN, + .start = AXP288_IRQ_POKP, + .end = AXP288_IRQ_POKP, .flags = IORESOURCE_IRQ, }, { .name = "PEK_DBF", - .start = AXP288_IRQ_POKP, - .end = AXP288_IRQ_POKP, + .start = AXP288_IRQ_POKN, + .end = AXP288_IRQ_POKN, .flags = IORESOURCE_IRQ, }, }; @@ -407,6 +416,9 @@ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2), INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3), INIT_REGMAP_IRQ(AXP288, OV, 0, 4), + INIT_REGMAP_IRQ(AXP288, FALLING_ALT, 0, 5), + INIT_REGMAP_IRQ(AXP288, RISING_ALT, 0, 6), + INIT_REGMAP_IRQ(AXP288, OV_ALT, 0, 7), INIT_REGMAP_IRQ(AXP288, DONE, 1, 2), INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3), @@ -589,7 +601,22 @@ static struct mfd_cell axp20x_cells[] = { }, }; -static struct mfd_cell axp22x_cells[] = { +static struct mfd_cell axp221_cells[] = { + { + .name = "axp20x-pek", + .num_resources = ARRAY_SIZE(axp22x_pek_resources), + .resources = axp22x_pek_resources, + }, { + .name = "axp20x-regulator", + }, { + .name = "axp20x-usb-power-supply", + .of_compatible = "x-powers,axp221-usb-power-supply", + .num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources), + .resources = axp22x_usb_power_supply_resources, + }, +}; + +static struct mfd_cell axp223_cells[] = { { .name = "axp20x-pek", .num_resources = ARRAY_SIZE(axp22x_pek_resources), @@ -598,7 +625,7 @@ static struct mfd_cell axp22x_cells[] = { .name = "axp20x-regulator", }, { .name = "axp20x-usb-power-supply", - .of_compatible = "x-powers,axp221-usb-power-supply", + .of_compatible = "x-powers,axp223-usb-power-supply", .num_resources = ARRAY_SIZE(axp22x_usb_power_supply_resources), .resources = axp22x_usb_power_supply_resources, }, @@ -791,9 +818,14 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip; break; case AXP221_ID: + axp20x->nr_cells = ARRAY_SIZE(axp221_cells); + axp20x->cells = axp221_cells; + axp20x->regmap_cfg = &axp22x_regmap_config; + axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip; + break; case AXP223_ID: - axp20x->nr_cells = ARRAY_SIZE(axp22x_cells); - axp20x->cells = axp22x_cells; + axp20x->nr_cells = ARRAY_SIZE(axp223_cells); + axp20x->cells = axp223_cells; axp20x->regmap_cfg = &axp22x_regmap_config; axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip; break; @@ -802,6 +834,7 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->nr_cells = ARRAY_SIZE(axp288_cells); axp20x->regmap_cfg = &axp288_regmap_config; axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; + axp20x->irq_flags = IRQF_TRIGGER_LOW; break; case AXP806_ID: axp20x->nr_cells = ARRAY_SIZE(axp806_cells); @@ -830,10 +863,33 @@ int axp20x_device_probe(struct axp20x_dev *axp20x) { int ret; + /* + * The AXP806 supports either master/standalone or slave mode. + * Slave mode allows sharing the serial bus, even with multiple + * AXP806 which all have the same hardware address. + * + * This is done with extra "serial interface address extension", + * or AXP806_BUS_ADDR_EXT, and "register address extension", or + * AXP806_REG_ADDR_EXT, registers. The former is read-only, with + * 1 bit customizable at the factory, and 1 bit depending on the + * state of an external pin. The latter is writable. The device + * will only respond to operations to its other registers when + * the these device addressing bits (in the upper 4 bits of the + * registers) match. + * + * Since we only support an AXP806 chained to an AXP809 in slave + * mode, and there isn't any existing hardware which uses AXP806 + * in master mode, or has 2 AXP806s in the same system, we can + * just program the register address extension to the slave mode + * address. + */ + if (axp20x->variant == AXP806_ID) + regmap_write(axp20x->regmap, AXP806_REG_ADDR_EXT, + AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE); + ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq, - IRQF_ONESHOT | IRQF_SHARED, -1, - axp20x->regmap_irq_chip, - &axp20x->regmap_irqc); + IRQF_ONESHOT | IRQF_SHARED | axp20x->irq_flags, + -1, axp20x->regmap_irq_chip, &axp20x->regmap_irqc); if (ret) { dev_err(axp20x->dev, "failed to add irq chip: %d\n", ret); return ret; diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index abd83424b498..9b66a98ba4bf 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -23,6 +23,7 @@ #include <linux/module.h> #include <linux/mfd/core.h> #include <linux/mfd/cros_ec.h> +#include <linux/suspend.h> #include <asm/unaligned.h> #define CROS_EC_DEV_EC_INDEX 0 @@ -65,6 +66,24 @@ static irqreturn_t ec_irq_thread(int irq, void *data) return IRQ_HANDLED; } +static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) +{ + struct { + struct cros_ec_command msg; + struct ec_params_host_sleep_event req; + } __packed buf; + + memset(&buf, 0, sizeof(buf)); + + buf.req.sleep_event = sleep_event; + + buf.msg.command = EC_CMD_HOST_SLEEP_EVENT; + buf.msg.version = 0; + buf.msg.outsize = sizeof(buf.req); + + return cros_ec_cmd_xfer(ec_dev, &buf.msg); +} + int cros_ec_register(struct cros_ec_device *ec_dev) { struct device *dev = ec_dev->dev; @@ -136,6 +155,15 @@ int cros_ec_register(struct cros_ec_device *ec_dev) } } + /* + * Clear sleep event - this will fail harmlessly on platforms that + * don't implement the sleep event host command. + */ + err = cros_ec_sleep_event(ec_dev, 0); + if (err < 0) + dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec", + err); + dev_info(dev, "Chrome EC device registered\n"); return 0; @@ -159,12 +187,24 @@ EXPORT_SYMBOL(cros_ec_remove); int cros_ec_suspend(struct cros_ec_device *ec_dev) { struct device *dev = ec_dev->dev; + int ret; + u8 sleep_event; + + sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ? + HOST_SLEEP_EVENT_S3_RESUME : + HOST_SLEEP_EVENT_S0IX_RESUME; + + ret = cros_ec_sleep_event(ec_dev, sleep_event); + if (ret < 0) + dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec", + ret); if (device_may_wakeup(dev)) ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); disable_irq(ec_dev->irq); ec_dev->was_wake_device = ec_dev->wake_enabled; + ec_dev->suspended = true; return 0; } @@ -179,8 +219,21 @@ static void cros_ec_drain_events(struct cros_ec_device *ec_dev) int cros_ec_resume(struct cros_ec_device *ec_dev) { + int ret; + u8 sleep_event; + + ec_dev->suspended = false; enable_irq(ec_dev->irq); + sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ? + HOST_SLEEP_EVENT_S3_RESUME : + HOST_SLEEP_EVENT_S0IX_RESUME; + + ret = cros_ec_sleep_event(ec_dev, sleep_event); + if (ret < 0) + dev_dbg(ec_dev->dev, "Error %d sending resume event to ec", + ret); + /* * In some cases, we need to distinguish between events that occur * during suspend if the EC is not a wake source. For example, diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 78dbcf8b0bef..16ffeaeb1385 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -157,7 +157,22 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info }, - + /* GLK */ + { PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31ee), (kernel_ulong_t)&bxt_uart_info }, + { PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_info }, /* APL */ { PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info }, { PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info }, diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index da5722d7c540..895f655780a7 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -496,6 +496,14 @@ static struct platform_driver kempld_driver = { static struct dmi_system_id kempld_dmi_table[] __initdata = { { + .ident = "BBD6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-bBD"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, + }, { .ident = "BBL6", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), @@ -512,6 +520,30 @@ static struct dmi_system_id kempld_dmi_table[] __initdata = { .driver_data = (void *)&kempld_platform_data_generic, .callback = kempld_create_platform_device, }, { + .ident = "BKL6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-bKL6"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, + }, { + .ident = "BSL6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-bSL6"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, + }, { + .ident = "CAL6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-cAL"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, + }, { .ident = "CBL6", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), @@ -600,6 +632,14 @@ static struct dmi_system_id kempld_dmi_table[] __initdata = { .driver_data = (void *)&kempld_platform_data_generic, .callback = kempld_create_platform_device, }, { + .ident = "CKL6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-cKL6"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, + }, { .ident = "CNTG", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index be42957a78e1..d98a5d974092 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -20,10 +20,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * * This driver supports the following I/O Controller hubs: * (See the intel documentation on http://developer.intel.com.) * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO) @@ -45,18 +41,6 @@ * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) * document number 320066-003, 320257-008: EP80597 (IICH) * document number 324645-001, 324646-001: Cougar Point (CPT) - * document number TBD : Patsburg (PBG) - * document number TBD : DH89xxCC - * document number TBD : Panther Point - * document number TBD : Lynx Point - * document number TBD : Lynx Point-LP - * document number TBD : Wellsburg - * document number TBD : Avoton SoC - * document number TBD : Coleto Creek - * document number TBD : Wildcat Point-LP - * document number TBD : 9 Series - * document number TBD : Lewisburg - * document number TBD : Apollo Lake SoC */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -567,6 +551,7 @@ static struct lpc_ich_info lpc_chipset_info[] = { }, [LPC_APL] = { .name = "Apollo Lake SoC", + .iTCO_version = 5, .spi_type = INTEL_SPI_BXT, }, }; diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 7b68ed72e9cb..b0e8e13c0049 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -34,6 +34,7 @@ #include <linux/mfd/max77686-private.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/of_device.h> static const struct mfd_cell max77686_devs[] = { { .name = "max77686-pmic", }, @@ -171,11 +172,9 @@ static const struct of_device_id max77686_pmic_dt_match[] = { }; MODULE_DEVICE_TABLE(of, max77686_pmic_dt_match); -static int max77686_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max77686_i2c_probe(struct i2c_client *i2c) { struct max77686_dev *max77686 = NULL; - const struct of_device_id *match; unsigned int data; int ret = 0; const struct regmap_config *config; @@ -188,16 +187,8 @@ static int max77686_i2c_probe(struct i2c_client *i2c, if (!max77686) return -ENOMEM; - if (i2c->dev.of_node) { - match = of_match_node(max77686_pmic_dt_match, i2c->dev.of_node); - if (!match) - return -EINVAL; - - max77686->type = (unsigned long)match->data; - } else - max77686->type = id->driver_data; - i2c_set_clientdata(i2c, max77686); + max77686->type = (unsigned long)of_device_get_match_data(&i2c->dev); max77686->dev = &i2c->dev; max77686->i2c = i2c; @@ -250,13 +241,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, return 0; } -static const struct i2c_device_id max77686_i2c_id[] = { - { "max77686", TYPE_MAX77686 }, - { "max77802", TYPE_MAX77802 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max77686_i2c_id); - #ifdef CONFIG_PM_SLEEP static int max77686_suspend(struct device *dev) { @@ -302,8 +286,7 @@ static struct i2c_driver max77686_i2c_driver = { .pm = &max77686_pm, .of_match_table = of_match_ptr(max77686_pmic_dt_match), }, - .probe = max77686_i2c_probe, - .id_table = max77686_i2c_id, + .probe_new = max77686_i2c_probe, }; module_i2c_driver(max77686_i2c_driver); diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c new file mode 100644 index 000000000000..6aeada7d7ce5 --- /dev/null +++ b/drivers/mfd/motorola-cpcap.c @@ -0,0 +1,259 @@ +/* + * Motorola CPCAP PMIC core driver + * + * Copyright (C) 2016 Tony Lindgren <tony@atomide.com> + * + * 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/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/sysfs.h> + +#include <linux/mfd/motorola-cpcap.h> +#include <linux/spi/spi.h> + +#define CPCAP_NR_IRQ_REG_BANKS 6 +#define CPCAP_NR_IRQ_CHIPS 3 + +struct cpcap_ddata { + struct spi_device *spi; + struct regmap_irq *irqs; + struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS]; + const struct regmap_config *regmap_conf; + struct regmap *regmap; +}; + +static int cpcap_check_revision(struct cpcap_ddata *cpcap) +{ + u16 vendor, rev; + int ret; + + ret = cpcap_get_vendor(&cpcap->spi->dev, cpcap->regmap, &vendor); + if (ret) + return ret; + + ret = cpcap_get_revision(&cpcap->spi->dev, cpcap->regmap, &rev); + if (ret) + return ret; + + dev_info(&cpcap->spi->dev, "CPCAP vendor: %s rev: %i.%i (%x)\n", + vendor == CPCAP_VENDOR_ST ? "ST" : "TI", + CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev), + rev); + + if (rev < CPCAP_REVISION_2_1) { + dev_info(&cpcap->spi->dev, + "Please add old CPCAP revision support as needed\n"); + return -ENODEV; + } + + return 0; +} + +/* + * First two irq chips are the two private macro interrupt chips, the third + * irq chip is for register banks 1 - 4 and is available for drivers to use. + */ +static struct regmap_irq_chip cpcap_irq_chip[CPCAP_NR_IRQ_CHIPS] = { + { + .name = "cpcap-m2", + .num_regs = 1, + .status_base = CPCAP_REG_MI1, + .ack_base = CPCAP_REG_MI1, + .mask_base = CPCAP_REG_MIM1, + .use_ack = true, + }, + { + .name = "cpcap-m2", + .num_regs = 1, + .status_base = CPCAP_REG_MI2, + .ack_base = CPCAP_REG_MI2, + .mask_base = CPCAP_REG_MIM2, + .use_ack = true, + }, + { + .name = "cpcap1-4", + .num_regs = 4, + .status_base = CPCAP_REG_INT1, + .ack_base = CPCAP_REG_INT1, + .mask_base = CPCAP_REG_INTM1, + .type_base = CPCAP_REG_INTS1, + .use_ack = true, + }, +}; + +static void cpcap_init_one_regmap_irq(struct cpcap_ddata *cpcap, + struct regmap_irq *rirq, + int irq_base, int irq) +{ + unsigned int reg_offset; + unsigned int bit, mask; + + reg_offset = irq - irq_base; + reg_offset /= cpcap->regmap_conf->val_bits; + reg_offset *= cpcap->regmap_conf->reg_stride; + + bit = irq % cpcap->regmap_conf->val_bits; + mask = (1 << bit); + + rirq->reg_offset = reg_offset; + rirq->mask = mask; +} + +static int cpcap_init_irq_chip(struct cpcap_ddata *cpcap, int irq_chip, + int irq_start, int nr_irqs) +{ + struct regmap_irq_chip *chip = &cpcap_irq_chip[irq_chip]; + int i, ret; + + for (i = irq_start; i < irq_start + nr_irqs; i++) { + struct regmap_irq *rirq = &cpcap->irqs[i]; + + cpcap_init_one_regmap_irq(cpcap, rirq, irq_start, i); + } + chip->irqs = &cpcap->irqs[irq_start]; + chip->num_irqs = nr_irqs; + chip->irq_drv_data = cpcap; + + ret = devm_regmap_add_irq_chip(&cpcap->spi->dev, cpcap->regmap, + cpcap->spi->irq, + IRQF_TRIGGER_RISING | + IRQF_SHARED, -1, + chip, &cpcap->irqdata[irq_chip]); + if (ret) { + dev_err(&cpcap->spi->dev, "could not add irq chip %i: %i\n", + irq_chip, ret); + return ret; + } + + return 0; +} + +static int cpcap_init_irq(struct cpcap_ddata *cpcap) +{ + int ret; + + cpcap->irqs = devm_kzalloc(&cpcap->spi->dev, + sizeof(*cpcap->irqs) * + CPCAP_NR_IRQ_REG_BANKS * + cpcap->regmap_conf->val_bits, + GFP_KERNEL); + if (!cpcap->irqs) + return -ENOMEM; + + ret = cpcap_init_irq_chip(cpcap, 0, 0, 16); + if (ret) + return ret; + + ret = cpcap_init_irq_chip(cpcap, 1, 16, 16); + if (ret) + return ret; + + ret = cpcap_init_irq_chip(cpcap, 2, 32, 64); + if (ret) + return ret; + + enable_irq_wake(cpcap->spi->irq); + + return 0; +} + +static const struct of_device_id cpcap_of_match[] = { + { .compatible = "motorola,cpcap", }, + { .compatible = "st,6556002", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cpcap_of_match); + +static const struct regmap_config cpcap_regmap_config = { + .reg_bits = 16, + .reg_stride = 4, + .pad_bits = 0, + .val_bits = 16, + .write_flag_mask = 0x8000, + .max_register = CPCAP_REG_ST_TEST2, + .cache_type = REGCACHE_NONE, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int cpcap_probe(struct spi_device *spi) +{ + const struct of_device_id *match; + struct cpcap_ddata *cpcap; + int ret; + + match = of_match_device(of_match_ptr(cpcap_of_match), &spi->dev); + if (!match) + return -ENODEV; + + cpcap = devm_kzalloc(&spi->dev, sizeof(*cpcap), GFP_KERNEL); + if (!cpcap) + return -ENOMEM; + + cpcap->spi = spi; + spi_set_drvdata(spi, cpcap); + + spi->bits_per_word = 16; + spi->mode = SPI_MODE_0 | SPI_CS_HIGH; + + ret = spi_setup(spi); + if (ret) + return ret; + + cpcap->regmap_conf = &cpcap_regmap_config; + cpcap->regmap = devm_regmap_init_spi(spi, &cpcap_regmap_config); + if (IS_ERR(cpcap->regmap)) { + ret = PTR_ERR(cpcap->regmap); + dev_err(&cpcap->spi->dev, "Failed to initialize regmap: %d\n", + ret); + + return ret; + } + + ret = cpcap_check_revision(cpcap); + if (ret) { + dev_err(&cpcap->spi->dev, "Failed to detect CPCAP: %i\n", ret); + return ret; + } + + ret = cpcap_init_irq(cpcap); + if (ret) + return ret; + + return of_platform_populate(spi->dev.of_node, NULL, NULL, + &cpcap->spi->dev); +} + +static int cpcap_remove(struct spi_device *pdev) +{ + struct cpcap_ddata *cpcap = spi_get_drvdata(pdev); + + of_platform_depopulate(&cpcap->spi->dev); + + return 0; +} + +static struct spi_driver cpcap_driver = { + .driver = { + .name = "cpcap-core", + .of_match_table = cpcap_of_match, + }, + .probe = cpcap_probe, + .remove = cpcap_remove, +}; +module_spi_driver(cpcap_driver); + +MODULE_ALIAS("platform:cpcap"); +MODULE_DESCRIPTION("CPCAP driver"); +MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index e14d8b058f0c..8e601c846d08 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -48,6 +48,10 @@ static const struct mfd_cell mt6323_devs[] = { .name = "mt6323-regulator", .of_compatible = "mediatek,mt6323-regulator" }, + { + .name = "mt6323-led", + .of_compatible = "mediatek,mt6323-led" + }, }; static const struct mfd_cell mt6397_devs[] = { diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 2c9acdba7c2d..fd087cbb0bde 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -247,7 +247,7 @@ static const struct regmap_irq rk818_irqs[] = { }, }; -static struct regmap_irq_chip rk808_irq_chip = { +static const struct regmap_irq_chip rk808_irq_chip = { .name = "rk808", .irqs = rk808_irqs, .num_irqs = ARRAY_SIZE(rk808_irqs), @@ -259,7 +259,7 @@ static struct regmap_irq_chip rk808_irq_chip = { .init_ack_masked = true, }; -static struct regmap_irq_chip rk818_irq_chip = { +static const struct regmap_irq_chip rk818_irq_chip = { .name = "rk818", .irqs = rk818_irqs, .num_irqs = ARRAY_SIZE(rk818_irqs), diff --git a/drivers/mfd/sun6i-prcm.c b/drivers/mfd/sun6i-prcm.c index 011fcc555945..2b658bed47db 100644 --- a/drivers/mfd/sun6i-prcm.c +++ b/drivers/mfd/sun6i-prcm.c @@ -12,6 +12,9 @@ #include <linux/init.h> #include <linux/of.h> +#define SUN8I_CODEC_ANALOG_BASE 0x1c0 +#define SUN8I_CODEC_ANALOG_SIZE 0x4 + struct prcm_data { int nsubdevs; const struct mfd_cell *subdevs; @@ -57,6 +60,10 @@ static const struct resource sun6i_a31_apb0_rstc_res[] = { }, }; +static const struct resource sun8i_codec_analog_res[] = { + DEFINE_RES_MEM(SUN8I_CODEC_ANALOG_BASE, SUN8I_CODEC_ANALOG_SIZE), +}; + static const struct mfd_cell sun6i_a31_prcm_subdevs[] = { { .name = "sun6i-a31-ar100-clk", @@ -109,6 +116,12 @@ static const struct mfd_cell sun8i_a23_prcm_subdevs[] = { .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), .resources = sun6i_a31_apb0_rstc_res, }, + { + .name = "sun8i-codec-analog", + .of_compatible = "allwinner,sun8i-a23-codec-analog", + .num_resources = ARRAY_SIZE(sun8i_codec_analog_res), + .resources = sun8i_codec_analog_res, + }, }; static const struct prcm_data sun6i_a31_prcm_data = { diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c index 45871403f995..785d19f6f7c9 100644 --- a/drivers/mfd/tps65912-i2c.c +++ b/drivers/mfd/tps65912-i2c.c @@ -27,6 +27,7 @@ static const struct of_device_id tps65912_i2c_of_match_table[] = { { .compatible = "ti,tps65912", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, tps65912_i2c_of_match_table); static int tps65912_i2c_probe(struct i2c_client *client, const struct i2c_device_id *ids) diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 0516ecda54d3..b2a0340f277e 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -20,6 +20,8 @@ #include <linux/of.h> +#include "../../sound/soc/atmel/atmel_ssc_dai.h" + /* Serialize access to ssc_list and user count */ static DEFINE_SPINLOCK(user_lock); static LIST_HEAD(ssc_list); @@ -145,6 +147,49 @@ static inline const struct atmel_ssc_platform_data * __init platform_get_device_id(pdev)->driver_data; } +#ifdef CONFIG_SND_ATMEL_SOC_SSC +static int ssc_sound_dai_probe(struct ssc_device *ssc) +{ + struct device_node *np = ssc->pdev->dev.of_node; + int ret; + int id; + + ssc->sound_dai = false; + + if (!of_property_read_bool(np, "#sound-dai-cells")) + return 0; + + id = of_alias_get_id(np, "ssc"); + if (id < 0) + return id; + + ret = atmel_ssc_set_audio(id); + ssc->sound_dai = !ret; + + return ret; +} + +static void ssc_sound_dai_remove(struct ssc_device *ssc) +{ + if (!ssc->sound_dai) + return; + + atmel_ssc_put_audio(of_alias_get_id(ssc->pdev->dev.of_node, "ssc")); +} +#else +static inline int ssc_sound_dai_probe(struct ssc_device *ssc) +{ + if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells")) + return -ENOTSUPP; + + return 0; +} + +static inline void ssc_sound_dai_remove(struct ssc_device *ssc) +{ +} +#endif + static int ssc_probe(struct platform_device *pdev) { struct resource *regs; @@ -204,6 +249,9 @@ static int ssc_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n", ssc->regs, ssc->irq); + if (ssc_sound_dai_probe(ssc)) + dev_err(&pdev->dev, "failed to auto-setup ssc for audio\n"); + return 0; } @@ -211,6 +259,8 @@ static int ssc_remove(struct platform_device *pdev) { struct ssc_device *ssc = platform_get_drvdata(pdev); + ssc_sound_dai_remove(ssc); + spin_lock(&user_lock); list_del(&ssc->list); spin_unlock(&user_lock); diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 3907387b6d15..062bf6ca2625 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -121,8 +121,9 @@ void cxl_context_set_mapping(struct cxl_context *ctx, mutex_unlock(&ctx->mapping_lock); } -static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int cxl_mmap_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct cxl_context *ctx = vma->vm_file->private_data; u64 area, offset; diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 6c722d96b775..79e60ec70bd3 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -418,8 +418,9 @@ struct cxl_afu { struct dentry *debugfs; struct mutex contexts_lock; spinlock_t afu_cntl_lock; - /* Used to block access to AFU config space while deconfigured */ - struct rw_semaphore configured_rwsem; + + /* -1: AFU deconfigured/locked, >= 0: number of readers */ + atomic_t configured_state; /* AFU error buffer fields and bin attribute for sysfs */ u64 eb_len, eb_offset; diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c index 377e650a2a1d..2fa015c05561 100644 --- a/drivers/misc/cxl/fault.c +++ b/drivers/misc/cxl/fault.c @@ -8,7 +8,8 @@ */ #include <linux/workqueue.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/mm.h> #include <linux/pid.h> #include <linux/mm.h> #include <linux/moduleparam.h> diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index 859959f19f10..e7139c76f961 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -12,7 +12,7 @@ #include <linux/export.h> #include <linux/kernel.h> #include <linux/bitmap.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/poll.h> #include <linux/pid.h> #include <linux/fs.h> diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c index 2a6bf1d0a3a4..b0b6ed31918e 100644 --- a/drivers/misc/cxl/main.c +++ b/drivers/misc/cxl/main.c @@ -19,6 +19,8 @@ #include <linux/slab.h> #include <linux/idr.h> #include <linux/pci.h> +#include <linux/sched/task.h> + #include <asm/cputable.h> #include <misc/cxl-base.h> @@ -268,8 +270,7 @@ struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int slice) idr_init(&afu->contexts_idr); mutex_init(&afu->contexts_lock); spin_lock_init(&afu->afu_cntl_lock); - init_rwsem(&afu->configured_rwsem); - down_write(&afu->configured_rwsem); + atomic_set(&afu->configured_state, -1); afu->prefault_mode = CXL_PREFAULT_NONE; afu->irqs_max = afu->adapter->user_irqs; diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index 09505f432eda..7ae710585267 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -9,6 +9,7 @@ #include <linux/spinlock.h> #include <linux/sched.h> +#include <linux/sched/clock.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/mm.h> diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index cca938845ffd..91f645992c94 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -1129,7 +1129,7 @@ static int pci_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pc if ((rc = cxl_native_register_psl_irq(afu))) goto err2; - up_write(&afu->configured_rwsem); + atomic_set(&afu->configured_state, 0); return 0; err2: @@ -1142,7 +1142,14 @@ err1: static void pci_deconfigure_afu(struct cxl_afu *afu) { - down_write(&afu->configured_rwsem); + /* + * It's okay to deconfigure when AFU is already locked, otherwise wait + * until there are no readers + */ + if (atomic_read(&afu->configured_state) != -1) { + while (atomic_cmpxchg(&afu->configured_state, 0, -1) != -1) + schedule(); + } cxl_native_release_psl_irq(afu); if (afu->adapter->native->sl_ops->release_serr_irq) afu->adapter->native->sl_ops->release_serr_irq(afu); diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c index 639a343b7836..512a4897dbf6 100644 --- a/drivers/misc/cxl/vphb.c +++ b/drivers/misc/cxl/vphb.c @@ -83,6 +83,16 @@ static inline struct cxl_afu *pci_bus_to_afu(struct pci_bus *bus) return phb ? phb->private_data : NULL; } +static void cxl_afu_configured_put(struct cxl_afu *afu) +{ + atomic_dec_if_positive(&afu->configured_state); +} + +static bool cxl_afu_configured_get(struct cxl_afu *afu) +{ + return atomic_inc_unless_negative(&afu->configured_state); +} + static inline int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn, struct cxl_afu *afu, int *_record) { @@ -107,7 +117,7 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, afu = pci_bus_to_afu(bus); /* Grab a reader lock on afu. */ - if (afu == NULL || !down_read_trylock(&afu->configured_rwsem)) + if (afu == NULL || !cxl_afu_configured_get(afu)) return PCIBIOS_DEVICE_NOT_FOUND; rc = cxl_pcie_config_info(bus, devfn, afu, &record); @@ -132,7 +142,7 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, } out: - up_read(&afu->configured_rwsem); + cxl_afu_configured_put(afu); return rc ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; } @@ -144,7 +154,7 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, afu = pci_bus_to_afu(bus); /* Grab a reader lock on afu. */ - if (afu == NULL || !down_read_trylock(&afu->configured_rwsem)) + if (afu == NULL || !cxl_afu_configured_get(afu)) return PCIBIOS_DEVICE_NOT_FOUND; rc = cxl_pcie_config_info(bus, devfn, afu, &record); @@ -166,7 +176,7 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, } out: - up_read(&afu->configured_rwsem); + cxl_afu_configured_put(afu); return rc ? PCIBIOS_SET_FAILED : PCIBIOS_SUCCESSFUL; } diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 051b14766ef9..764ff5df0dbc 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -19,7 +19,7 @@ #include <linux/log2.h> #include <linux/bitops.h> #include <linux/jiffies.h> -#include <linux/of.h> +#include <linux/property.h> #include <linux/acpi.h> #include <linux/i2c.h> #include <linux/nvmem-provider.h> @@ -562,26 +562,26 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) return 0; } -#ifdef CONFIG_OF -static void at24_get_ofdata(struct i2c_client *client, - struct at24_platform_data *chip) +static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) { - const __be32 *val; - struct device_node *node = client->dev.of_node; - - if (node) { - if (of_get_property(node, "read-only", NULL)) - chip->flags |= AT24_FLAG_READONLY; - val = of_get_property(node, "pagesize", NULL); - if (val) - chip->page_size = be32_to_cpup(val); + int err; + u32 val; + + if (device_property_present(dev, "read-only")) + chip->flags |= AT24_FLAG_READONLY; + + err = device_property_read_u32(dev, "pagesize", &val); + if (!err) { + chip->page_size = val; + } else { + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip->page_size = 1; } } -#else -static void at24_get_ofdata(struct i2c_client *client, - struct at24_platform_data *chip) -{ } -#endif /* CONFIG_OF */ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -613,15 +613,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); magic >>= AT24_SIZE_BYTELEN; chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); - /* - * This is slow, but we can't know all eeproms, so we better - * play safe. Specifying custom eeprom-types via platform_data - * is recommended anyhow. - */ - chip.page_size = 1; - /* update chipdata if OF is present */ - at24_get_ofdata(client, &chip); + at24_get_pdata(&client->dev, &chip); chip.setup = NULL; chip.context = NULL; diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 3d1d55157e5f..2fad790db3bf 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/capability.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/mutex.h> diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index cb290b8ca0c8..dd4617764f14 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -29,7 +29,7 @@ #include <linux/pci.h> #include <linux/string.h> #include <linux/fs.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/atomic.h> diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c index 232034f5da48..5c7dd26db716 100644 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ b/drivers/misc/ibmasm/r_heartbeat.c @@ -20,7 +20,7 @@ * */ -#include <linux/sched.h> +#include <linux/sched/signal.h> #include "ibmasm.h" #include "dot_command.h" diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 99635dd9dbac..fc7efedbc4be 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -103,6 +103,8 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/module.h> +#include <linux/sched/task.h> + #include <asm/sections.h> #define v1printk(a...) do { \ diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index fb8705fc3aca..e389b0b5278d 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -23,6 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/types.h> diff --git a/drivers/misc/lkdtm_heap.c b/drivers/misc/lkdtm_heap.c index 0f1581664c1c..ffb6aeac07b3 100644 --- a/drivers/misc/lkdtm_heap.c +++ b/drivers/misc/lkdtm_heap.c @@ -4,6 +4,7 @@ */ #include "lkdtm.h" #include <linux/slab.h> +#include <linux/sched.h> /* * This tries to stay within the next largest power-of-2 kmalloc cache diff --git a/drivers/misc/lkdtm_usercopy.c b/drivers/misc/lkdtm_usercopy.c index 1dd611423d8b..df6ac985fbb5 100644 --- a/drivers/misc/lkdtm_usercopy.c +++ b/drivers/misc/lkdtm_usercopy.c @@ -5,6 +5,7 @@ #include "lkdtm.h" #include <linux/slab.h> #include <linux/vmalloc.h> +#include <linux/sched/task_stack.h> #include <linux/mman.h> #include <linux/uaccess.h> #include <asm/cacheflush.h> diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index cb3e9e0ca049..df5f78ae3d25 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/slab.h> diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 68fe37b5bc52..d3e3372424d6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -14,7 +14,7 @@ * */ -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/slab.h> diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 9d0b7050c79a..bf816449cd40 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -26,7 +26,7 @@ #include <linux/init.h> #include <linux/ioctl.h> #include <linux/cdev.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/uuid.h> #include <linux/compat.h> #include <linux/jiffies.h> diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c index be37890abb93..77b16ca66846 100644 --- a/drivers/misc/mic/bus/mic_bus.c +++ b/drivers/misc/mic/bus/mic_bus.c @@ -143,7 +143,7 @@ static void mbus_release_dev(struct device *d) } struct mbus_device * -mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, +mbus_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, struct mbus_hw_ops *hw_ops, int index, void __iomem *mmio_va) { @@ -158,7 +158,7 @@ mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, mbdev->dev.parent = pdev; mbdev->id.device = id; mbdev->id.vendor = MBUS_DEV_ANY_ID; - mbdev->dev.archdata.dma_ops = dma_ops; + mbdev->dev.dma_ops = dma_ops; mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask; dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64)); mbdev->dev.release = mbus_release_dev; diff --git a/drivers/misc/mic/bus/scif_bus.c b/drivers/misc/mic/bus/scif_bus.c index ff6e01c25810..a444db5f61fe 100644 --- a/drivers/misc/mic/bus/scif_bus.c +++ b/drivers/misc/mic/bus/scif_bus.c @@ -138,7 +138,7 @@ static void scif_release_dev(struct device *d) } struct scif_hw_dev * -scif_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, +scif_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, struct mic_mw *mmio, struct mic_mw *aper, void *dp, void __iomem *rdp, struct dma_chan **chan, int num_chan, @@ -154,7 +154,7 @@ scif_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, sdev->dev.parent = pdev; sdev->id.device = id; sdev->id.vendor = SCIF_DEV_ANY_ID; - sdev->dev.archdata.dma_ops = dma_ops; + sdev->dev.dma_ops = dma_ops; sdev->dev.release = scif_release_dev; sdev->hw_ops = hw_ops; sdev->dnode = dnode; diff --git a/drivers/misc/mic/bus/scif_bus.h b/drivers/misc/mic/bus/scif_bus.h index 94f29ac608b6..ff59568219ad 100644 --- a/drivers/misc/mic/bus/scif_bus.h +++ b/drivers/misc/mic/bus/scif_bus.h @@ -113,7 +113,7 @@ int scif_register_driver(struct scif_driver *driver); void scif_unregister_driver(struct scif_driver *driver); struct scif_hw_dev * scif_register_device(struct device *pdev, int id, - struct dma_map_ops *dma_ops, + const struct dma_map_ops *dma_ops, struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, struct mic_mw *mmio, struct mic_mw *aper, void *dp, void __iomem *rdp, diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c index 303da222f5b6..fd7f2a6049f8 100644 --- a/drivers/misc/mic/bus/vop_bus.c +++ b/drivers/misc/mic/bus/vop_bus.c @@ -154,7 +154,7 @@ vop_register_device(struct device *pdev, int id, vdev->dev.parent = pdev; vdev->id.device = id; vdev->id.vendor = VOP_DEV_ANY_ID; - vdev->dev.archdata.dma_ops = (struct dma_map_ops *)dma_ops; + vdev->dev.dma_ops = dma_ops; vdev->dev.dma_mask = &vdev->dev.coherent_dma_mask; dma_set_mask(&vdev->dev, DMA_BIT_MASK(64)); vdev->dev.release = vop_release_dev; diff --git a/drivers/misc/mic/cosm/cosm_scif_server.c b/drivers/misc/mic/cosm/cosm_scif_server.c index 5696df4326b5..85f7d09cc65f 100644 --- a/drivers/misc/mic/cosm/cosm_scif_server.c +++ b/drivers/misc/mic/cosm/cosm_scif_server.c @@ -19,6 +19,8 @@ * */ #include <linux/kthread.h> +#include <linux/sched/signal.h> + #include "cosm_main.h" /* diff --git a/drivers/misc/mic/cosm_client/cosm_scif_client.c b/drivers/misc/mic/cosm_client/cosm_scif_client.c index 03e98bf1ac15..aa530fcceaa9 100644 --- a/drivers/misc/mic/cosm_client/cosm_scif_client.c +++ b/drivers/misc/mic/cosm_client/cosm_scif_client.c @@ -22,6 +22,8 @@ #include <linux/delay.h> #include <linux/reboot.h> #include <linux/kthread.h> +#include <linux/sched/signal.h> + #include "../cosm/cosm_main.h" #define COSM_SCIF_MAX_RETRIES 10 diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 9599d732aff3..c327985c9523 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -245,7 +245,7 @@ static void __mic_dma_unmap_sg(struct device *dev, dma_unmap_sg(&mdev->pdev->dev, sg, nents, dir); } -static struct dma_map_ops __mic_dma_ops = { +static const struct dma_map_ops __mic_dma_ops = { .alloc = __mic_dma_alloc, .free = __mic_dma_free, .map_page = __mic_dma_map_page, @@ -344,7 +344,7 @@ mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, mic_unmap_single(mdev, dma_addr, size); } -static struct dma_map_ops mic_dma_ops = { +static const struct dma_map_ops mic_dma_ops = { .map_page = mic_dma_map_page, .unmap_page = mic_dma_unmap_page, }; diff --git a/drivers/misc/mic/scif/scif_main.h b/drivers/misc/mic/scif/scif_main.h index a08f0b600a9e..0e5eff9ad080 100644 --- a/drivers/misc/mic/scif/scif_main.h +++ b/drivers/misc/mic/scif/scif_main.h @@ -18,7 +18,7 @@ #ifndef SCIF_MAIN_H #define SCIF_MAIN_H -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/pci.h> #include <linux/miscdevice.h> #include <linux/dmaengine.h> diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index f806a4471eb9..329727e00e97 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -17,6 +17,9 @@ */ #include <linux/dma_remapping.h> #include <linux/pagemap.h> +#include <linux/sched/mm.h> +#include <linux/sched/signal.h> + #include "scif_main.h" #include "scif_map.h" diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c index 1a2b67f3183d..c2e29d7f0de8 100644 --- a/drivers/misc/mic/vop/vop_main.c +++ b/drivers/misc/mic/vop/vop_main.c @@ -374,7 +374,7 @@ unmap: static int vop_find_vqs(struct virtio_device *dev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], struct irq_affinity *desc) { struct _vop_vdev *vdev = to_vopvdev(dev); struct vop_device *vpdev = vdev->vpdev; diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c index af2e077da4b8..3641f1334cf0 100644 --- a/drivers/misc/sgi-gru/grumain.c +++ b/drivers/misc/sgi-gru/grumain.c @@ -926,8 +926,9 @@ again: * * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries. */ -int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int gru_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct gru_thread_state *gts; unsigned long paddr, vaddr; unsigned long expires; diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 5c3ce2459675..b5e308b50ed1 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h @@ -665,7 +665,7 @@ extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, int cbr_au_count, char *cbmap); extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, int dsr_au_count, char *dsmap); -extern int gru_fault(struct vm_area_struct *, struct vm_fault *vmf); +extern int gru_fault(struct vm_fault *vmf); extern struct gru_mm_struct *gru_register_mmu_notifier(void); extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms); diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c index c344483fa7d6..2cde80c7bb93 100644 --- a/drivers/misc/vexpress-syscfg.c +++ b/drivers/misc/vexpress-syscfg.c @@ -16,7 +16,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/syscore_ops.h> #include <linux/vexpress.h> diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c index f866a4baecb5..21d0fa592145 100644 --- a/drivers/misc/vmw_vmci/vmci_context.c +++ b/drivers/misc/vmw_vmci/vmci_context.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/sched.h> +#include <linux/cred.h> #include <linux/slab.h> #include "vmci_queue_pair.h" @@ -303,7 +304,7 @@ int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg) vmci_dg_size = VMCI_DG_SIZE(dg); if (vmci_dg_size > VMCI_MAX_DG_SIZE) { - pr_devel("Datagram too large (bytes=%Zu)\n", vmci_dg_size); + pr_devel("Datagram too large (bytes=%zu)\n", vmci_dg_size); return VMCI_ERROR_INVALID_ARGS; } diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c index 8449516d6ac6..84258a48029d 100644 --- a/drivers/misc/vmw_vmci/vmci_event.c +++ b/drivers/misc/vmw_vmci/vmci_event.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/rculist.h> #include "vmci_driver.h" #include "vmci_event.h" diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index ec090105eb4b..8a16a26e9658 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/sched.h> +#include <linux/cred.h> #include <linux/slab.h> #include <linux/file.h> #include <linux/init.h> diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index f84a4275ca29..498c0854305f 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -2928,7 +2928,7 @@ int vmci_qpair_get_produce_indexes(const struct vmci_qp *qpair, EXPORT_SYMBOL_GPL(vmci_qpair_get_produce_indexes); /* - * vmci_qpair_get_consume_indexes() - Retrieves the indexes of the comsumer. + * vmci_qpair_get_consume_indexes() - Retrieves the indexes of the consumer. * @qpair: Pointer to the queue pair struct. * @consumer_tail: Reference used for storing consumer tail index. * @producer_head: Reference used for storing the producer head index. diff --git a/drivers/misc/vmw_vmci/vmci_resource.c b/drivers/misc/vmw_vmci/vmci_resource.c index 9a53a30de445..1ab6e8737a5f 100644 --- a/drivers/misc/vmw_vmci/vmci_resource.c +++ b/drivers/misc/vmw_vmci/vmci_resource.c @@ -17,6 +17,7 @@ #include <linux/hash.h> #include <linux/types.h> #include <linux/rculist.h> +#include <linux/completion.h> #include "vmci_resource.h" #include "vmci_driver.h" diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index d29faf2addfe..6d4b72080d51 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/sched.h> +#include <uapi/linux/sched/types.h> #include <linux/kthread.h> #include <linux/export.h> #include <linux/wait.h> diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c index 2b7fc3764803..00750c9d3514 100644 --- a/drivers/mmc/host/mmci_qcom_dml.c +++ b/drivers/mmc/host/mmci_qcom_dml.c @@ -170,7 +170,7 @@ int dml_hw_init(struct mmci_host *host, struct device_node *np) writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT), base + DML_PIPE_ID); - /* Make sure dml intialization is finished */ + /* Make sure dml initialization is finished */ mb(); return 0; diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 82bd00af5cc3..268aae45b514 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -75,18 +75,18 @@ static char module_name[] = "lart"; /* blob */ #define NUM_BLOB_BLOCKS FLASH_NUMBLOCKS_16m_PARAM -#define BLOB_START 0x00000000 -#define BLOB_LEN (NUM_BLOB_BLOCKS * FLASH_BLOCKSIZE_PARAM) +#define PART_BLOB_START 0x00000000 +#define PART_BLOB_LEN (NUM_BLOB_BLOCKS * FLASH_BLOCKSIZE_PARAM) /* kernel */ #define NUM_KERNEL_BLOCKS 7 -#define KERNEL_START (BLOB_START + BLOB_LEN) -#define KERNEL_LEN (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE_MAIN) +#define PART_KERNEL_START (PART_BLOB_START + PART_BLOB_LEN) +#define PART_KERNEL_LEN (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE_MAIN) /* initial ramdisk */ #define NUM_INITRD_BLOCKS 24 -#define INITRD_START (KERNEL_START + KERNEL_LEN) -#define INITRD_LEN (NUM_INITRD_BLOCKS * FLASH_BLOCKSIZE_MAIN) +#define PART_INITRD_START (PART_KERNEL_START + PART_KERNEL_LEN) +#define PART_INITRD_LEN (NUM_INITRD_BLOCKS * FLASH_BLOCKSIZE_MAIN) /* * See section 4.0 in "3 Volt Fast Boot Block Flash Memory" Intel Datasheet @@ -587,20 +587,20 @@ static struct mtd_partition lart_partitions[] = { /* blob */ { .name = "blob", - .offset = BLOB_START, - .size = BLOB_LEN, + .offset = PART_BLOB_START, + .size = PART_BLOB_LEN, }, /* kernel */ { .name = "kernel", - .offset = KERNEL_START, /* MTDPART_OFS_APPEND */ - .size = KERNEL_LEN, + .offset = PART_KERNEL_START, /* MTDPART_OFS_APPEND */ + .size = PART_KERNEL_LEN, }, /* initial ramdisk / file system */ { .name = "file system", - .offset = INITRD_START, /* MTDPART_OFS_APPEND */ - .size = INITRD_LEN, /* MTDPART_SIZ_FULL */ + .offset = PART_INITRD_START, /* MTDPART_OFS_APPEND */ + .size = PART_INITRD_LEN, /* MTDPART_SIZ_FULL */ } }; #define NUM_PARTITIONS ARRAY_SIZE(lart_partitions) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 6c062b8251d2..d52139635b67 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -20,6 +20,7 @@ */ #include <linux/clk.h> #include <linux/slab.h> +#include <linux/sched/task_stack.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1492c12906f6..b0524f8accb6 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -36,6 +36,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/mm.h> +#include <linux/nmi.h> #include <linux/types.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> diff --git a/drivers/mtd/tests/mtd_test.h b/drivers/mtd/tests/mtd_test.h index 4b7bee17c924..04afd0e7074f 100644 --- a/drivers/mtd/tests/mtd_test.h +++ b/drivers/mtd/tests/mtd_test.h @@ -1,5 +1,5 @@ #include <linux/mtd/mtd.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> static inline int mtdtest_relax(void) { diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 85d54f37e28f..77513195f50e 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1159,7 +1159,7 @@ static struct mtd_info * __init open_mtd_by_chdev(const char *mtd_dev) if (err) return ERR_PTR(err); - err = vfs_getattr(&path, &stat); + err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); path_put(&path); if (err) return ERR_PTR(err); diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 88b1897aeb40..d4b2e8744498 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -314,7 +314,7 @@ struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode) if (error) return ERR_PTR(error); - error = vfs_getattr(&path, &stat); + error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); path_put(&path); if (error) return ERR_PTR(error); diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 6ea963e3b89a..62ee439d5882 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -123,7 +123,7 @@ static int __init arcnet_init(void) arc_proto_map[count] = arc_proto_default; if (BUGLVL(D_DURING)) - pr_info("struct sizes: %Zd %Zd %Zd %Zd %Zd\n", + pr_info("struct sizes: %zd %zd %zd %zd %zd\n", sizeof(struct arc_hardware), sizeof(struct arc_rfc1201), sizeof(struct arc_rfc1051), diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 577e57cad1dc..1bcbb8913e17 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -16,6 +16,8 @@ #include <linux/rcupdate.h> #include <linux/ctype.h> #include <linux/inet.h> +#include <linux/sched/signal.h> + #include <net/bonding.h> static int bond_option_active_slave_set(struct bonding *bond, diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index e23c3ed737de..770623a0cc01 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -24,7 +24,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/string.h> diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c index b306210b02b7..bc0eb47eccee 100644 --- a/drivers/net/caif/caif_virtio.c +++ b/drivers/net/caif/caif_virtio.c @@ -679,7 +679,8 @@ static int cfv_probe(struct virtio_device *vdev) goto err; /* Get the TX virtio ring. This is a "guest side vring". */ - err = vdev->config->find_vqs(vdev, 1, &cfv->vq_tx, &vq_cbs, &names); + err = vdev->config->find_vqs(vdev, 1, &cfv->vq_tx, &vq_cbs, &names, + NULL); if (err) goto err; diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c index 4063215c9b54..aac58ce6e371 100644 --- a/drivers/net/can/softing/softing_fw.c +++ b/drivers/net/can/softing/softing_fw.c @@ -17,7 +17,7 @@ */ #include <linux/firmware.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <asm/div64.h> #include <asm/io.h> diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index a81731303730..a9ac58c351a0 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1206,7 +1206,7 @@ static void bfin_mac_rx(struct bfin_mac_local *lp) /* reserve 2 bytes for RXDWA padding */ skb_reserve(new_skb, NET_IP_ALIGN); /* Invalidate the data cache of skb->data range when it is write back - * cache. It will prevent overwritting the new data from DMA + * cache. It will prevent overwriting the new data from DMA */ blackfin_dcache_invalidate_range((unsigned long)new_skb->head, (unsigned long)new_skb->end); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index c2730f15bd8b..38392a520725 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -122,104 +122,40 @@ #include "xgbe.h" #include "xgbe-common.h" -static int xgbe_config_msi(struct xgbe_prv_data *pdata) +static int xgbe_config_multi_msi(struct xgbe_prv_data *pdata) { - unsigned int msi_count; + unsigned int vector_count; unsigned int i, j; int ret; - msi_count = XGBE_MSIX_BASE_COUNT; - msi_count += max(pdata->rx_ring_count, - pdata->tx_ring_count); - msi_count = roundup_pow_of_two(msi_count); + vector_count = XGBE_MSI_BASE_COUNT; + vector_count += max(pdata->rx_ring_count, + pdata->tx_ring_count); - ret = pci_enable_msi_exact(pdata->pcidev, msi_count); + ret = pci_alloc_irq_vectors(pdata->pcidev, XGBE_MSI_MIN_COUNT, + vector_count, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (ret < 0) { - dev_info(pdata->dev, "MSI request for %u interrupts failed\n", - msi_count); - - ret = pci_enable_msi(pdata->pcidev); - if (ret < 0) { - dev_info(pdata->dev, "MSI enablement failed\n"); - return ret; - } - - msi_count = 1; - } - - pdata->irq_count = msi_count; - - pdata->dev_irq = pdata->pcidev->irq; - - if (msi_count > 1) { - pdata->ecc_irq = pdata->pcidev->irq + 1; - pdata->i2c_irq = pdata->pcidev->irq + 2; - pdata->an_irq = pdata->pcidev->irq + 3; - - for (i = XGBE_MSIX_BASE_COUNT, j = 0; - (i < msi_count) && (j < XGBE_MAX_DMA_CHANNELS); - i++, j++) - pdata->channel_irq[j] = pdata->pcidev->irq + i; - pdata->channel_irq_count = j; - - pdata->per_channel_irq = 1; - pdata->channel_irq_mode = XGBE_IRQ_MODE_LEVEL; - } else { - pdata->ecc_irq = pdata->pcidev->irq; - pdata->i2c_irq = pdata->pcidev->irq; - pdata->an_irq = pdata->pcidev->irq; - } - - if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "MSI interrupts enabled\n"); - - return 0; -} - -static int xgbe_config_msix(struct xgbe_prv_data *pdata) -{ - unsigned int msix_count; - unsigned int i, j; - int ret; - - msix_count = XGBE_MSIX_BASE_COUNT; - msix_count += max(pdata->rx_ring_count, - pdata->tx_ring_count); - - pdata->msix_entries = devm_kcalloc(pdata->dev, msix_count, - sizeof(struct msix_entry), - GFP_KERNEL); - if (!pdata->msix_entries) - return -ENOMEM; - - for (i = 0; i < msix_count; i++) - pdata->msix_entries[i].entry = i; - - ret = pci_enable_msix_range(pdata->pcidev, pdata->msix_entries, - XGBE_MSIX_MIN_COUNT, msix_count); - if (ret < 0) { - dev_info(pdata->dev, "MSI-X enablement failed\n"); - devm_kfree(pdata->dev, pdata->msix_entries); - pdata->msix_entries = NULL; + dev_info(pdata->dev, "multi MSI/MSI-X enablement failed\n"); return ret; } pdata->irq_count = ret; - pdata->dev_irq = pdata->msix_entries[0].vector; - pdata->ecc_irq = pdata->msix_entries[1].vector; - pdata->i2c_irq = pdata->msix_entries[2].vector; - pdata->an_irq = pdata->msix_entries[3].vector; + pdata->dev_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->ecc_irq = pci_irq_vector(pdata->pcidev, 1); + pdata->i2c_irq = pci_irq_vector(pdata->pcidev, 2); + pdata->an_irq = pci_irq_vector(pdata->pcidev, 3); - for (i = XGBE_MSIX_BASE_COUNT, j = 0; i < ret; i++, j++) - pdata->channel_irq[j] = pdata->msix_entries[i].vector; + for (i = XGBE_MSI_BASE_COUNT, j = 0; i < ret; i++, j++) + pdata->channel_irq[j] = pci_irq_vector(pdata->pcidev, i); pdata->channel_irq_count = j; pdata->per_channel_irq = 1; pdata->channel_irq_mode = XGBE_IRQ_MODE_LEVEL; if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "MSI-X interrupts enabled\n"); + dev_dbg(pdata->dev, "multi %s interrupts enabled\n", + pdata->pcidev->msix_enabled ? "MSI-X" : "MSI"); return 0; } @@ -228,21 +164,28 @@ static int xgbe_config_irqs(struct xgbe_prv_data *pdata) { int ret; - ret = xgbe_config_msix(pdata); + ret = xgbe_config_multi_msi(pdata); if (!ret) goto out; - ret = xgbe_config_msi(pdata); - if (!ret) - goto out; + ret = pci_alloc_irq_vectors(pdata->pcidev, 1, 1, + PCI_IRQ_LEGACY | PCI_IRQ_MSI); + if (ret < 0) { + dev_info(pdata->dev, "single IRQ enablement failed\n"); + return ret; + } pdata->irq_count = 1; - pdata->irq_shared = 1; + pdata->channel_irq_count = 1; + + pdata->dev_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->ecc_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->i2c_irq = pci_irq_vector(pdata->pcidev, 0); + pdata->an_irq = pci_irq_vector(pdata->pcidev, 0); - pdata->dev_irq = pdata->pcidev->irq; - pdata->ecc_irq = pdata->pcidev->irq; - pdata->i2c_irq = pdata->pcidev->irq; - pdata->an_irq = pdata->pcidev->irq; + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "single %s interrupt enabled\n", + pdata->pcidev->msi_enabled ? "MSI" : "legacy"); out: if (netif_msg_probe(pdata)) { @@ -425,12 +368,15 @@ static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Configure the netdev resource */ ret = xgbe_config_netdev(pdata); if (ret) - goto err_pci_enable; + goto err_irq_vectors; netdev_notice(pdata->netdev, "net device enabled\n"); return 0; +err_irq_vectors: + pci_free_irq_vectors(pdata->pcidev); + err_pci_enable: xgbe_free_pdata(pdata); @@ -446,6 +392,8 @@ static void xgbe_pci_remove(struct pci_dev *pdev) xgbe_deconfig_netdev(pdata); + pci_free_irq_vectors(pdata->pcidev); + xgbe_free_pdata(pdata); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 00108815b55e..f9a24639f574 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -211,9 +211,9 @@ #define XGBE_MAC_PROP_OFFSET 0x1d000 #define XGBE_I2C_CTRL_OFFSET 0x1e000 -/* PCI MSIx support */ -#define XGBE_MSIX_BASE_COUNT 4 -#define XGBE_MSIX_MIN_COUNT (XGBE_MSIX_BASE_COUNT + 1) +/* PCI MSI/MSIx support */ +#define XGBE_MSI_BASE_COUNT 4 +#define XGBE_MSI_MIN_COUNT (XGBE_MSI_BASE_COUNT + 1) /* PCI clock frequencies */ #define XGBE_V2_DMA_CLOCK_FREQ 500000000 /* 500 MHz */ @@ -982,14 +982,12 @@ struct xgbe_prv_data { unsigned int desc_ded_count; unsigned int desc_sec_count; - struct msix_entry *msix_entries; int dev_irq; int ecc_irq; int i2c_irq; int channel_irq[XGBE_MAX_DMA_CHANNELS]; unsigned int per_channel_irq; - unsigned int irq_shared; unsigned int irq_count; unsigned int channel_irq_count; unsigned int channel_irq_mode; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index d0d0d12b531f..e536301acfde 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -293,36 +293,29 @@ static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring, static int xgene_enet_setup_mss(struct net_device *ndev, u32 mss) { struct xgene_enet_pdata *pdata = netdev_priv(ndev); - bool mss_index_found = false; - int mss_index; + int mss_index = -EBUSY; int i; spin_lock(&pdata->mss_lock); /* Reuse the slot if MSS matches */ - for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) { + for (i = 0; mss_index < 0 && i < NUM_MSS_REG; i++) { if (pdata->mss[i] == mss) { pdata->mss_refcnt[i]++; mss_index = i; - mss_index_found = true; } } /* Overwrite the slot with ref_count = 0 */ - for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) { + for (i = 0; mss_index < 0 && i < NUM_MSS_REG; i++) { if (!pdata->mss_refcnt[i]) { pdata->mss_refcnt[i]++; pdata->mac_ops->set_mss(pdata, mss, i); pdata->mss[i] = mss; mss_index = i; - mss_index_found = true; } } - /* No slots with ref_count = 0 available, return busy */ - if (!mss_index_found) - mss_index = -EBUSY; - spin_unlock(&pdata->mss_lock); return mss_index; diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 0ee6e208aa07..50d88d3e03b6 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -817,7 +817,7 @@ static void bcm_enet_adjust_phy_link(struct net_device *dev) rx_pause_en = 1; tx_pause_en = 1; } else if (!priv->pause_auto) { - /* pause setting overrided by user */ + /* pause setting overridden by user */ rx_pause_en = priv->pause_rx; tx_pause_en = priv->pause_tx; } else { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 05356efdbf93..b209b7f6093e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -6957,7 +6957,7 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars) * hence its link is expected to be down * - SECOND_PHY means that first phy should not be able * to link up by itself (using configuration) - * - DEFAULT should be overriden during initialiazation + * - DEFAULT should be overridden during initialization */ DP(NETIF_MSG_LINK, "Invalid link indication" "mpc=0x%x. DISABLING LINK !!!\n", diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index a448177990fe..30d1eb9ebec9 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -20,6 +20,7 @@ #include <linux/moduleparam.h> #include <linux/stringify.h> #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/types.h> #include <linux/compiler.h> #include <linux/slab.h> diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 016d481c6476..30606b11b128 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -1622,7 +1622,7 @@ static void macb_init_rx_buffer_size(struct macb *bp, size_t size) } } - netdev_dbg(bp->dev, "mtu [%u] rx_buffer_size [%Zu]\n", + netdev_dbg(bp->dev, "mtu [%u] rx_buffer_size [%zu]\n", bp->dev->mtu, bp->rx_buffer_size); } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h index 8cd389148166..aa36e9ae7676 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h @@ -23,6 +23,8 @@ #ifndef _OCTEON_MAIN_H_ #define _OCTEON_MAIN_H_ +#include <linux/sched/signal.h> + #if BITS_PER_LONG == 32 #define CVM_CAST64(v) ((long long)(v)) #elif BITS_PER_LONG == 64 diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index acc231293e4d..f6e739da7bb7 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -1416,7 +1416,7 @@ static unsigned int xdigit2int(unsigned char c) * <pattern data>[/<pattern mask>][@<anchor>] * * Up to 2 filter patterns can be specified. If 2 are supplied the first one - * must be anchored at 0. An omited mask is taken as a mask of 1s, an omitted + * must be anchored at 0. An omitted mask is taken as a mask of 1s, an omitted * anchor is taken as 0. */ static ssize_t mps_trc_write(struct file *file, const char __user *buf, diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index 5043b64805f0..8098c93cd16e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -1364,6 +1364,10 @@ struct cpl_tx_data { #define TX_FORCE_S 13 #define TX_FORCE_V(x) ((x) << TX_FORCE_S) +#define T6_TX_FORCE_S 20 +#define T6_TX_FORCE_V(x) ((x) << T6_TX_FORCE_S) +#define T6_TX_FORCE_F T6_TX_FORCE_V(1U) + enum { ULP_TX_MEM_READ = 2, ULP_TX_MEM_WRITE = 3, diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 8d9e4b7a8e84..ccc05f874419 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -3385,6 +3385,14 @@ struct fw_crypto_lookaside_wr { #define FW_CRYPTO_LOOKASIDE_WR_IV_G(x) \ (((x) >> FW_CRYPTO_LOOKASIDE_WR_IV_S) & FW_CRYPTO_LOOKASIDE_WR_IV_M) +#define FW_CRYPTO_LOOKASIDE_WR_FQIDX_S 15 +#define FW_CRYPTO_LOOKASIDE_WR_FQIDX_M 0xff +#define FW_CRYPTO_LOOKASIDE_WR_FQIDX_V(x) \ + ((x) << FW_CRYPTO_LOOKASIDE_WR_FQIDX_S) +#define FW_CRYPTO_LOOKASIDE_WR_FQIDX_G(x) \ + (((x) >> FW_CRYPTO_LOOKASIDE_WR_FQIDX_S) & \ + FW_CRYPTO_LOOKASIDE_WR_FQIDX_M) + #define FW_CRYPTO_LOOKASIDE_WR_TX_CH_S 10 #define FW_CRYPTO_LOOKASIDE_WR_TX_CH_M 0x3 #define FW_CRYPTO_LOOKASIDE_WR_TX_CH_V(x) \ diff --git a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.h b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.h index e995a1a3840a..a91ad766cef0 100644 --- a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.h +++ b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.h @@ -59,7 +59,7 @@ struct cxgbi_pagepod_hdr { #define PPOD_PAGES_MAX 4 struct cxgbi_pagepod { struct cxgbi_pagepod_hdr hdr; - u64 addr[PPOD_PAGES_MAX + 1]; + __be64 addr[PPOD_PAGES_MAX + 1]; }; /* ddp tag format diff --git a/drivers/net/ethernet/ibm/emac/Kconfig b/drivers/net/ethernet/ibm/emac/Kconfig index 3f44a30e0615..90d49191beb3 100644 --- a/drivers/net/ethernet/ibm/emac/Kconfig +++ b/drivers/net/ethernet/ibm/emac/Kconfig @@ -2,6 +2,7 @@ config IBM_EMAC tristate "IBM EMAC Ethernet support" depends on PPC_DCR select CRC32 + select PHYLIB help This driver supports the IBM EMAC family of Ethernet controllers typically found on 4xx embedded PowerPC chips, but also on the diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 6ead2335a169..275c2e2349ad 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -42,6 +42,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_net.h> +#include <linux/of_mdio.h> #include <linux/slab.h> #include <asm/processor.h> @@ -2420,6 +2421,219 @@ static int emac_read_uint_prop(struct device_node *np, const char *name, return 0; } +static void emac_adjust_link(struct net_device *ndev) +{ + struct emac_instance *dev = netdev_priv(ndev); + struct phy_device *phy = dev->phy_dev; + + dev->phy.autoneg = phy->autoneg; + dev->phy.speed = phy->speed; + dev->phy.duplex = phy->duplex; + dev->phy.pause = phy->pause; + dev->phy.asym_pause = phy->asym_pause; + dev->phy.advertising = phy->advertising; +} + +static int emac_mii_bus_read(struct mii_bus *bus, int addr, int regnum) +{ + int ret = emac_mdio_read(bus->priv, addr, regnum); + /* This is a workaround for powered down ports/phys. + * In the wild, this was seen on the Cisco Meraki MX60(W). + * This hardware disables ports as part of the handoff + * procedure. Accessing the ports will lead to errors + * (-ETIMEDOUT, -EREMOTEIO) that do more harm than good. + */ + return ret < 0 ? 0xffff : ret; +} + +static int emac_mii_bus_write(struct mii_bus *bus, int addr, + int regnum, u16 val) +{ + emac_mdio_write(bus->priv, addr, regnum, val); + return 0; +} + +static int emac_mii_bus_reset(struct mii_bus *bus) +{ + struct emac_instance *dev = netdev_priv(bus->priv); + + return emac_reset(dev); +} + +static int emac_mdio_setup_aneg(struct mii_phy *phy, u32 advertise) +{ + struct net_device *ndev = phy->dev; + struct emac_instance *dev = netdev_priv(ndev); + + dev->phy.autoneg = AUTONEG_ENABLE; + dev->phy.speed = SPEED_1000; + dev->phy.duplex = DUPLEX_FULL; + dev->phy.advertising = advertise; + phy->autoneg = AUTONEG_ENABLE; + phy->speed = dev->phy.speed; + phy->duplex = dev->phy.duplex; + phy->advertising = advertise; + return phy_start_aneg(dev->phy_dev); +} + +static int emac_mdio_setup_forced(struct mii_phy *phy, int speed, int fd) +{ + struct net_device *ndev = phy->dev; + struct emac_instance *dev = netdev_priv(ndev); + + dev->phy.autoneg = AUTONEG_DISABLE; + dev->phy.speed = speed; + dev->phy.duplex = fd; + phy->autoneg = AUTONEG_DISABLE; + phy->speed = speed; + phy->duplex = fd; + return phy_start_aneg(dev->phy_dev); +} + +static int emac_mdio_poll_link(struct mii_phy *phy) +{ + struct net_device *ndev = phy->dev; + struct emac_instance *dev = netdev_priv(ndev); + int res; + + res = phy_read_status(dev->phy_dev); + if (res) { + dev_err(&dev->ofdev->dev, "link update failed (%d).", res); + return ethtool_op_get_link(ndev); + } + + return dev->phy_dev->link; +} + +static int emac_mdio_read_link(struct mii_phy *phy) +{ + struct net_device *ndev = phy->dev; + struct emac_instance *dev = netdev_priv(ndev); + int res; + + res = phy_read_status(dev->phy_dev); + if (res) + return res; + + dev->phy.speed = phy->speed; + dev->phy.duplex = phy->duplex; + dev->phy.pause = phy->pause; + dev->phy.asym_pause = phy->asym_pause; + return 0; +} + +static int emac_mdio_init_phy(struct mii_phy *phy) +{ + struct net_device *ndev = phy->dev; + struct emac_instance *dev = netdev_priv(ndev); + + phy_start(dev->phy_dev); + dev->phy.autoneg = phy->autoneg; + dev->phy.speed = phy->speed; + dev->phy.duplex = phy->duplex; + dev->phy.advertising = phy->advertising; + dev->phy.pause = phy->pause; + dev->phy.asym_pause = phy->asym_pause; + + return phy_init_hw(dev->phy_dev); +} + +static const struct mii_phy_ops emac_dt_mdio_phy_ops = { + .init = emac_mdio_init_phy, + .setup_aneg = emac_mdio_setup_aneg, + .setup_forced = emac_mdio_setup_forced, + .poll_link = emac_mdio_poll_link, + .read_link = emac_mdio_read_link, +}; + +static int emac_dt_mdio_probe(struct emac_instance *dev) +{ + struct device_node *mii_np; + int res; + + mii_np = of_get_child_by_name(dev->ofdev->dev.of_node, "mdio"); + if (!mii_np) { + dev_err(&dev->ofdev->dev, "no mdio definition found."); + return -ENODEV; + } + + if (!of_device_is_available(mii_np)) { + res = -ENODEV; + goto put_node; + } + + dev->mii_bus = devm_mdiobus_alloc(&dev->ofdev->dev); + if (!dev->mii_bus) { + res = -ENOMEM; + goto put_node; + } + + dev->mii_bus->priv = dev->ndev; + dev->mii_bus->parent = dev->ndev->dev.parent; + dev->mii_bus->name = "emac_mdio"; + dev->mii_bus->read = &emac_mii_bus_read; + dev->mii_bus->write = &emac_mii_bus_write; + dev->mii_bus->reset = &emac_mii_bus_reset; + snprintf(dev->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev->ofdev->name); + res = of_mdiobus_register(dev->mii_bus, mii_np); + if (res) { + dev_err(&dev->ofdev->dev, "cannot register MDIO bus %s (%d)", + dev->mii_bus->name, res); + } + + put_node: + of_node_put(mii_np); + return res; +} + +static int emac_dt_phy_connect(struct emac_instance *dev, + struct device_node *phy_handle) +{ + int res; + + dev->phy.def = devm_kzalloc(&dev->ofdev->dev, sizeof(*dev->phy.def), + GFP_KERNEL); + if (!dev->phy.def) + return -ENOMEM; + + dev->phy_dev = of_phy_connect(dev->ndev, phy_handle, &emac_adjust_link, + 0, dev->phy_mode); + if (!dev->phy_dev) { + dev_err(&dev->ofdev->dev, "failed to connect to PHY.\n"); + return -ENODEV; + } + + dev->phy.def->phy_id = dev->phy_dev->drv->phy_id; + dev->phy.def->phy_id_mask = dev->phy_dev->drv->phy_id_mask; + dev->phy.def->name = dev->phy_dev->drv->name; + dev->phy.def->ops = &emac_dt_mdio_phy_ops; + dev->phy.features = dev->phy_dev->supported; + dev->phy.address = dev->phy_dev->mdio.addr; + dev->phy.mode = dev->phy_dev->interface; + return 0; +} + +static int emac_dt_phy_probe(struct emac_instance *dev) +{ + struct device_node *np = dev->ofdev->dev.of_node; + struct device_node *phy_handle; + int res = 0; + + phy_handle = of_parse_phandle(np, "phy-handle", 0); + + if (phy_handle) { + res = emac_dt_mdio_probe(dev); + if (!res) { + res = emac_dt_phy_connect(dev, phy_handle); + if (res) + mdiobus_unregister(dev->mii_bus); + } + } + + of_node_put(phy_handle); + return res; +} + static int emac_init_phy(struct emac_instance *dev) { struct device_node *np = dev->ofdev->dev.of_node; @@ -2430,15 +2644,12 @@ static int emac_init_phy(struct emac_instance *dev) dev->phy.dev = ndev; dev->phy.mode = dev->phy_mode; - /* PHY-less configuration. - * XXX I probably should move these settings to the dev tree - */ - if (dev->phy_address == 0xffffffff && dev->phy_map == 0xffffffff) { + /* PHY-less configuration. */ + if ((dev->phy_address == 0xffffffff && dev->phy_map == 0xffffffff) || + of_phy_is_fixed_link(np)) { emac_reset(dev); - /* PHY-less configuration. - * XXX I probably should move these settings to the dev tree - */ + /* PHY-less configuration. */ dev->phy.address = -1; dev->phy.features = SUPPORTED_MII; if (emac_phy_supports_gige(dev->phy_mode)) @@ -2447,6 +2658,16 @@ static int emac_init_phy(struct emac_instance *dev) dev->phy.features |= SUPPORTED_100baseT_Full; dev->phy.pause = 1; + if (of_phy_is_fixed_link(np)) { + int res = emac_dt_mdio_probe(dev); + + if (!res) { + res = of_phy_register_fixed_link(np); + if (res) + mdiobus_unregister(dev->mii_bus); + } + return res; + } return 0; } @@ -2490,6 +2711,18 @@ static int emac_init_phy(struct emac_instance *dev) emac_configure(dev); + if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) { + int res = emac_dt_phy_probe(dev); + + mutex_unlock(&emac_phy_map_lock); + if (!res) + goto init_phy; + + dev_err(&dev->ofdev->dev, "failed to attach dt phy (%d).\n", + res); + return res; + } + if (dev->phy_address != 0xffffffff) phy_map = ~(1 << dev->phy_address); @@ -2517,6 +2750,7 @@ static int emac_init_phy(struct emac_instance *dev) return -ENXIO; } + init_phy: /* Init PHY */ if (dev->phy.def->ops->init) dev->phy.def->ops->init(&dev->phy); @@ -2988,6 +3222,12 @@ static int emac_remove(struct platform_device *ofdev) if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII)) zmii_detach(dev->zmii_dev, dev->zmii_port); + if (dev->phy_dev) + phy_disconnect(dev->phy_dev); + + if (dev->mii_bus) + mdiobus_unregister(dev->mii_bus); + busy_phy_map &= ~(1 << dev->phy.address); DBG(dev, "busy_phy_map now %#x" NL, busy_phy_map); diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index 93ae11494810..0710a6685489 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -199,6 +199,10 @@ struct emac_instance { struct emac_instance *mdio_instance; struct mutex mdio_lock; + /* Device-tree based phy configuration */ + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + /* ZMII infos if any */ u32 zmii_ph; u32 zmii_port; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index cbbf8648307a..78460c52b7c4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -847,9 +847,7 @@ static void i40e_free_vf_res(struct i40e_vf *vf) wr32(hw, reg_idx, reg); i40e_flush(hw); } - /* reset some of the state varibles keeping - * track of the resources - */ + /* reset some of the state variables keeping track of the resources */ vf->num_queue_pairs = 0; vf->vf_states = 0; clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 2788a5409023..68812d783f33 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -294,7 +294,7 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data) u32 i, i2ccmd = 0; u16 phy_data_swapped; - /* Prevent overwritting SFP I2C EEPROM which is at A0 address.*/ + /* Prevent overwriting SFP I2C EEPROM which is at A0 address.*/ if ((hw->phy.addr == 0) || (hw->phy.addr > 7)) { hw_dbg("PHY I2C Address %d is out of range.\n", hw->phy.addr); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index 30535e6b68f0..c8ac46049f34 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -1449,7 +1449,7 @@ do { \ * @atr_input: input bitstream to compute the hash on * @input_mask: mask for the input bitstream * - * This function serves two main purposes. First it applys the input_mask + * This function serves two main purposes. First it applies the input_mask * to the atr_input resulting in a cleaned up atr_input data stream. * Secondly it computes the hash and stores it in the bkt_hash field at * the end of the input byte stream. This way it will be available for diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index a49072b4fa52..e8c105164931 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -43,6 +43,7 @@ #include <linux/semaphore.h> #include <rdma/ib_smi.h> #include <linux/delay.h> +#include <linux/etherdevice.h> #include <asm/io.h> @@ -2955,7 +2956,7 @@ static bool mlx4_valid_vf_state_change(struct mlx4_dev *dev, int port, return false; } -int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac) +int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u8 *mac) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_vport_state *s_info; @@ -2964,13 +2965,22 @@ int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac) if (!mlx4_is_master(dev)) return -EPROTONOSUPPORT; + if (is_multicast_ether_addr(mac)) + return -EINVAL; + slave = mlx4_get_slave_indx(dev, vf); if (slave < 0) return -EINVAL; port = mlx4_slaves_closest_port(dev, slave, port); s_info = &priv->mfunc.master.vf_admin[slave].vport[port]; - s_info->mac = mac; + + if (s_info->spoofchk && is_zero_ether_addr(mac)) { + mlx4_info(dev, "MAC invalidation is not allowed when spoofchk is on\n"); + return -EPERM; + } + + s_info->mac = mlx4_mac_to_u64(mac); mlx4_info(dev, "default mac on vf %d port %d to %llX will take effect only after vf restart\n", vf, port, s_info->mac); return 0; @@ -3143,6 +3153,7 @@ int mlx4_set_vf_spoofchk(struct mlx4_dev *dev, int port, int vf, bool setting) struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_vport_state *s_info; int slave; + u8 mac[ETH_ALEN]; if ((!mlx4_is_master(dev)) || !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_FSM)) @@ -3154,6 +3165,13 @@ int mlx4_set_vf_spoofchk(struct mlx4_dev *dev, int port, int vf, bool setting) port = mlx4_slaves_closest_port(dev, slave, port); s_info = &priv->mfunc.master.vf_admin[slave].vport[port]; + + mlx4_u64_to_mac(mac, s_info->mac); + if (setting && !is_valid_ether_addr(mac)) { + mlx4_info(dev, "Illegal MAC with spoofchk\n"); + return -EPERM; + } + s_info->spoofchk = setting; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index e7b81a305469..024788549c25 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -89,10 +89,17 @@ void mlx4_en_remove_timestamp(struct mlx4_en_dev *mdev) } } +#define MLX4_EN_WRAP_AROUND_SEC 10UL +/* By scheduling the overflow check every 5 seconds, we have a reasonably + * good chance we wont miss a wrap around. + * TOTO: Use a timer instead of a work queue to increase the guarantee. + */ +#define MLX4_EN_OVERFLOW_PERIOD (MLX4_EN_WRAP_AROUND_SEC * HZ / 2) + void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev) { bool timeout = time_is_before_jiffies(mdev->last_overflow_check + - mdev->overflow_period); + MLX4_EN_OVERFLOW_PERIOD); unsigned long flags; if (timeout) { @@ -237,7 +244,6 @@ static const struct ptp_clock_info mlx4_en_ptp_clock_info = { .enable = mlx4_en_phc_enable, }; -#define MLX4_EN_WRAP_AROUND_SEC 10ULL /* This function calculates the max shift that enables the user range * of MLX4_EN_WRAP_AROUND_SEC values in the cycles register. @@ -258,7 +264,6 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) { struct mlx4_dev *dev = mdev->dev; unsigned long flags; - u64 ns, zero = 0; /* mlx4_en_init_timestamp is called for each netdev. * mdev->ptp_clock is common for all ports, skip initialization if @@ -282,13 +287,6 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) ktime_to_ns(ktime_get_real())); write_sequnlock_irqrestore(&mdev->clock_lock, flags); - /* Calculate period in seconds to call the overflow watchdog - to make - * sure counter is checked at least once every wrap around. - */ - ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask, zero, &zero); - do_div(ns, NSEC_PER_SEC / 2 / HZ); - mdev->overflow_period = ns; - /* Configure the PHC */ mdev->ptp_clock_info = mlx4_en_ptp_clock_info; snprintf(mdev->ptp_clock_info.name, 16, "mlx4 ptp"); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c index b04760a5034b..1dae8e40fb25 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c @@ -319,7 +319,7 @@ static int mlx4_en_ets_validate(struct mlx4_en_priv *priv, struct ieee_ets *ets) default: en_err(priv, "TC[%d]: Not supported TSA: %d\n", i, ets->tc_tsa[i]); - return -ENOTSUPP; + return -EOPNOTSUPP; } } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index afe4444e5434..61420473fe5f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2485,12 +2485,8 @@ static int mlx4_en_set_vf_mac(struct net_device *dev, int queue, u8 *mac) { struct mlx4_en_priv *en_priv = netdev_priv(dev); struct mlx4_en_dev *mdev = en_priv->mdev; - u64 mac_u64 = mlx4_mac_to_u64(mac); - if (is_multicast_ether_addr(mac)) - return -EINVAL; - - return mlx4_set_vf_mac(mdev->dev, en_priv->port, queue, mac_u64); + return mlx4_set_vf_mac(mdev->dev, en_priv->port, queue, mac); } static int mlx4_en_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos, diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index d85e6446f9d9..867292880c07 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -604,10 +604,10 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv, dma_sync_single_for_cpu(priv->ddev, dma, frag_info->frag_size, DMA_FROM_DEVICE); - /* Save page reference in skb */ - __skb_frag_set_page(&skb_frags_rx[nr], frags[nr].page); - skb_frag_size_set(&skb_frags_rx[nr], frag_info->frag_size); - skb_frags_rx[nr].page_offset = frags[nr].page_offset; + __skb_fill_page_desc(skb, nr, frags[nr].page, + frags[nr].page_offset, + frag_info->frag_size); + skb->truesize += frag_info->frag_stride; frags[nr].page = NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 39232b6a974f..07406cf2eacd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -1249,9 +1249,8 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) mlx4_warn(dev, "Failed adding irq rmap\n"); } #endif - err = mlx4_create_eq(dev, dev->caps.num_cqs - - dev->caps.reserved_cqs + - MLX4_NUM_SPARE_EQE, + err = mlx4_create_eq(dev, dev->quotas.cq + + MLX4_NUM_SPARE_EQE, (dev->flags & MLX4_FLAG_MSI_X) ? i + 1 - !!(i > MLX4_EQ_ASYNC) : 0, eq); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 3fe885ce1902..37e84a59e751 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -2436,7 +2436,7 @@ int mlx4_config_dev_retrieval(struct mlx4_dev *dev, #define CONFIG_DEV_RX_CSUM_MODE_PORT2_BIT_OFFSET 4 if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_CONFIG_DEV)) - return -ENOTSUPP; + return -EOPNOTSUPP; err = mlx4_CONFIG_DEV_get(dev, &config_dev); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index 8258d08acd8c..e00f627331cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -136,7 +136,7 @@ int mlx4_do_bond(struct mlx4_dev *dev, bool enable) LIST_HEAD(bond_list); if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)) - return -ENOTSUPP; + return -EOPNOTSUPP; ret = mlx4_disable_rx_port_check(dev, enable); if (ret) { diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 15ef787e71ba..21377c315083 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -841,8 +841,6 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) return -EINVAL; } - mlx4_log_num_mgm_entry_size = hca_param.log_mc_entry_sz; - dev->caps.hca_core_clock = hca_param.hca_core_clock; memset(&dev_cap, 0, sizeof(dev_cap)); @@ -1447,7 +1445,7 @@ int mlx4_port_map_set(struct mlx4_dev *dev, struct mlx4_port_map *v2p) int err; if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)) - return -ENOTSUPP; + return -EOPNOTSUPP; mutex_lock(&priv->bond_mutex); @@ -1884,7 +1882,7 @@ int mlx4_get_internal_clock_params(struct mlx4_dev *dev, struct mlx4_priv *priv = mlx4_priv(dev); if (mlx4_is_slave(dev)) - return -ENOTSUPP; + return -EOPNOTSUPP; if (!params) return -EINVAL; @@ -2384,7 +2382,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) /* Query CONFIG_DEV parameters */ err = mlx4_config_dev_retrieval(dev, ¶ms); - if (err && err != -ENOTSUPP) { + if (err && err != -EOPNOTSUPP) { mlx4_err(dev, "Failed to query CONFIG_DEV parameters\n"); } else if (!err) { dev->caps.rx_checksum_flags_port[1] = params.rx_csum_flags_port_1; @@ -3503,6 +3501,8 @@ slave_start: goto err_disable_msix; } + mlx4_init_quotas(dev); + err = mlx4_setup_hca(dev); if (err == -EBUSY && (dev->flags & MLX4_FLAG_MSI_X) && !mlx4_is_mfunc(dev)) { @@ -3515,7 +3515,6 @@ slave_start: if (err) goto err_steer; - mlx4_init_quotas(dev); /* When PF resources are ready arm its comm channel to enable * getting commands */ diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 4941b692e947..3629ce11a68b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -430,7 +430,6 @@ struct mlx4_en_dev { seqlock_t clock_lock; struct timecounter clock; unsigned long last_overflow_check; - unsigned long overflow_period; struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_info; struct notifier_block nb; diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 395b5463cfd9..db65f72879e9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -823,7 +823,7 @@ int mlx4_mw_alloc(struct mlx4_dev *dev, u32 pd, enum mlx4_mw_type type, !(dev->caps.flags & MLX4_DEV_CAP_FLAG_MEM_WINDOW)) || (type == MLX4_MW_TYPE_2 && !(dev->caps.bmme_flags & MLX4_BMME_FLAG_TYPE_2_WIN))) - return -ENOTSUPP; + return -EOPNOTSUPP; index = mlx4_mpt_reserve(dev); if (index == -1) diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index d1cd9c32a9ae..2d6abd4662b1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -447,7 +447,7 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, & MLX4_DEV_CAP_FLAG2_UPDATE_QP_SRC_CHECK_LB)) { mlx4_warn(dev, "Trying to set src check LB, but it isn't supported\n"); - err = -ENOTSUPP; + err = -EOPNOTSUPP; goto out; } pri_addr_path_mask |= diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 6fe9f76ae656..d8d5d161b8c7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -4297,7 +4297,7 @@ int mlx4_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, MLX4_DEV_CAP_FLAG2_UPDATE_QP_SRC_CHECK_LB)) { mlx4_warn(dev, "Src check LB for slave %d isn't supported\n", slave); - return -ENOTSUPP; + return -EOPNOTSUPP; } /* Just change the smac for the QP */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 95ca03c0d9f5..f6a6ded204f6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -816,6 +816,7 @@ int mlx5e_get_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode); +void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type); static inline void mlx5e_tx_notify_hw(struct mlx5e_sq *sq, struct mlx5_wqe_ctrl_seg *ctrl, int bf_sz) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index cc80522b5854..a004a5a1a4c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1487,6 +1487,7 @@ static int set_pflag_rx_cqe_compress(struct net_device *netdev, mlx5e_modify_rx_cqe_compression_locked(priv, enable); priv->params.rx_cqe_compress_def = enable; + mlx5e_set_rq_type_params(priv, priv->params.rq_wq_type); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 3cce6281e075..8ef64c4db2c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -79,9 +79,10 @@ static bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) MLX5_CAP_ETH(mdev, reg_umr_sq); } -static void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type) +void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type) { priv->params.rq_wq_type = rq_type; + priv->params.lro_wqe_sz = MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ; switch (priv->params.rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: priv->params.log_rq_size = is_kdump_kernel() ? @@ -98,6 +99,10 @@ static void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type) priv->params.log_rq_size = is_kdump_kernel() ? MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE : MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE; + + /* Extra room needed for build_skb */ + priv->params.lro_wqe_sz -= MLX5_RX_HEADROOM + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); } priv->params.min_rx_wqes = mlx5_min_rx_wqes(priv->params.rq_wq_type, BIT(priv->params.log_rq_size)); @@ -3521,6 +3526,9 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, cqe_compress_heuristic(link_speed, pci_bw); } + MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, + priv->params.rx_cqe_compress_def); + mlx5e_set_rq_priv_params(priv); if (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) priv->params.lro_en = true; @@ -3547,16 +3555,9 @@ static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, mlx5e_build_default_indir_rqt(mdev, priv->params.indirection_rqt, MLX5E_INDIR_RQT_SIZE, profile->max_nch(mdev)); - priv->params.lro_wqe_sz = - MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ - - /* Extra room needed for build_skb */ - MLX5_RX_HEADROOM - - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - /* Initialize pflags */ MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_BASED_MODER, priv->params.rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); - MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, priv->params.rx_cqe_compress_def); mutex_init(&priv->state_lock); @@ -3970,6 +3971,19 @@ static void mlx5e_register_vport_rep(struct mlx5_core_dev *mdev) } } +static void mlx5e_unregister_vport_rep(struct mlx5_core_dev *mdev) +{ + struct mlx5_eswitch *esw = mdev->priv.eswitch; + int total_vfs = MLX5_TOTAL_VPORTS(mdev); + int vport; + + if (!MLX5_CAP_GEN(mdev, vport_group_manager)) + return; + + for (vport = 1; vport < total_vfs; vport++) + mlx5_eswitch_unregister_vport_rep(esw, vport); +} + void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -4016,6 +4030,7 @@ static int mlx5e_attach(struct mlx5_core_dev *mdev, void *vpriv) return err; } + mlx5e_register_vport_rep(mdev); return 0; } @@ -4027,6 +4042,7 @@ static void mlx5e_detach(struct mlx5_core_dev *mdev, void *vpriv) if (!netif_device_present(netdev)) return; + mlx5e_unregister_vport_rep(mdev); mlx5e_detach_netdev(mdev, netdev); mlx5e_destroy_mdev_resources(mdev); } @@ -4045,8 +4061,6 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) if (err) return NULL; - mlx5e_register_vport_rep(mdev); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) ppriv = &esw->offloads.vport_reps[0]; @@ -4098,13 +4112,7 @@ void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, struct mlx5e_priv *priv) static void mlx5e_remove(struct mlx5_core_dev *mdev, void *vpriv) { - struct mlx5_eswitch *esw = mdev->priv.eswitch; - int total_vfs = MLX5_TOTAL_VPORTS(mdev); struct mlx5e_priv *priv = vpriv; - int vport; - - for (vport = 1; vport < total_vfs; vport++) - mlx5_eswitch_unregister_vport_rep(esw, vport); unregister_netdev(priv->netdev); mlx5e_detach(mdev, vpriv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index b039b87742a6..3d371688fbbb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -30,6 +30,7 @@ * SOFTWARE. */ +#include <linux/prefetch.h> #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/tcp.h> @@ -93,19 +94,18 @@ static inline void mlx5e_cqes_update_owner(struct mlx5e_cq *cq, u32 cqcc, int n) static inline void mlx5e_decompress_cqe(struct mlx5e_rq *rq, struct mlx5e_cq *cq, u32 cqcc) { - u16 wqe_cnt_step; - cq->title.byte_cnt = cq->mini_arr[cq->mini_arr_idx].byte_cnt; cq->title.check_sum = cq->mini_arr[cq->mini_arr_idx].checksum; cq->title.op_own &= 0xf0; cq->title.op_own |= 0x01 & (cqcc >> cq->wq.log_sz); cq->title.wqe_counter = cpu_to_be16(cq->decmprs_wqe_counter); - wqe_cnt_step = - rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ ? - mpwrq_get_cqe_consumed_strides(&cq->title) : 1; - cq->decmprs_wqe_counter = - (cq->decmprs_wqe_counter + wqe_cnt_step) & rq->wq.sz_m1; + if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) + cq->decmprs_wqe_counter += + mpwrq_get_cqe_consumed_strides(&cq->title); + else + cq->decmprs_wqe_counter = + (cq->decmprs_wqe_counter + 1) & rq->wq.sz_m1; } static inline void mlx5e_decompress_cqe_no_hash(struct mlx5e_rq *rq, @@ -171,6 +171,7 @@ void mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool val) mlx5e_close_locked(priv->netdev); MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, val); + mlx5e_set_rq_type_params(priv, priv->params.rq_wq_type); if (was_opened) mlx5e_open_locked(priv->netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c index 65442c36a6e1..31e3cb7ee5fe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c @@ -30,6 +30,7 @@ * SOFTWARE. */ +#include <linux/prefetch.h> #include <linux/ip.h> #include <linux/udp.h> #include <net/udp.h> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index ce3d92106386..2478516a61e2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1232,10 +1232,18 @@ static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, fs_for_each_fte(fte, fg) { nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); if (compare_match_value(&fg->mask, match_value, &fte->val) && - (flow_act->action & fte->action) && - flow_act->flow_tag == fte->flow_tag) { + (flow_act->action & fte->action)) { int old_action = fte->action; + if (fte->flow_tag != flow_act->flow_tag) { + mlx5_core_warn(get_dev(&fte->node), + "FTE flow tag %u already exists with different flow tag %u\n", + fte->flow_tag, + flow_act->flow_tag); + handle = ERR_PTR(-EEXIST); + goto unlock_fte; + } + fte->action |= flow_act->action; handle = add_rule_fte(fte, fg, dest, dest_num, old_action != flow_act->action); diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index ee38c18c2d2d..ee1c78abab0b 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -1251,10 +1251,10 @@ struct ksz_port_info { * @tx_size: Transmit data size. Used for TX optimization. * The maximum is defined by MAX_TX_HELD_SIZE. * @perm_addr: Permanent MAC address. - * @override_addr: Overrided MAC address. + * @override_addr: Overridden MAC address. * @address: Additional MAC address entries. * @addr_list_size: Additional MAC address list size. - * @mac_override: Indication of MAC address overrided. + * @mac_override: Indication of MAC address overridden. * @promiscuous: Counter to keep track of promiscuous mode set. * @all_multi: Counter to keep track of all multicast mode set. * @multi_list: Multicast address entries. @@ -4042,7 +4042,7 @@ static int empty_addr(u8 *addr) * @hw: The hardware instance. * * This routine programs the MAC address of the hardware when the address is - * overrided. + * overridden. */ static void hw_set_addr(struct ksz_hw *hw) { @@ -7043,7 +7043,7 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id) if (macaddr[0] != ':') get_mac_addr(hw_priv, macaddr, MAIN_PORT); - /* Read MAC address and initialize override address if not overrided. */ + /* Read MAC address and initialize override address if not overridden. */ hw_read_addr(hw); /* Multiple device interfaces mode requires a second MAC address. */ diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index c5c1d0e0c16f..118723ea681a 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -5397,7 +5397,7 @@ static void s2io_ethtool_gdrvinfo(struct net_device *dev, * s2io_nic structure. * @regs : pointer to the structure with parameters given by ethtool for * dumping the registers. - * @reg_space: The input argumnet into which all the registers are dumped. + * @reg_space: The input argument into which all the registers are dumped. * Description: * Dumps the entire register space of xFrame NIC into the user given * buffer area. diff --git a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c index db55e6d89cf4..0452848d1316 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c @@ -119,7 +119,7 @@ static void vxge_ethtool_gdrvinfo(struct net_device *dev, * @dev: device pointer. * @regs: pointer to the structure with parameters given by ethtool for * dumping the registers. - * @reg_space: The input argumnet into which all the registers are dumped. + * @reg_space: The input argument into which all the registers are dumped. * * Dumps the vpath register space of Titan NIC into the user given * buffer area. diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 92367a06491a..978d32944c80 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -3272,8 +3272,6 @@ static void nv_force_linkspeed(struct net_device *dev, int speed, int duplex) pci_push(base); writel(np->linkspeed, base + NvRegLinkSpeed); pci_push(base); - - return; } /** diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 3b5d7cfa2321..827de838389f 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -3005,14 +3005,14 @@ static ssize_t netxen_sysfs_write_mem(struct file *filp, struct kobject *kobj, } -static struct bin_attribute bin_attr_crb = { +static const struct bin_attribute bin_attr_crb = { .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = netxen_sysfs_read_crb, .write = netxen_sysfs_write_crb, }; -static struct bin_attribute bin_attr_mem = { +static const struct bin_attribute bin_attr_mem = { .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = netxen_sysfs_read_mem, @@ -3141,7 +3141,7 @@ out: } -static struct bin_attribute bin_attr_dimm = { +static const struct bin_attribute bin_attr_dimm = { .attr = { .name = "dimm", .mode = (S_IRUGO | S_IWUSR) }, .size = sizeof(struct netxen_dimm_cfg), .read = netxen_sysfs_read_dimm, diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 61a9cd5be497..00c17fa6545b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -688,7 +688,9 @@ static inline u8 qed_concrete_to_sw_fid(struct qed_dev *cdev, #define OOO_LB_TC 9 int qed_configure_vport_wfq(struct qed_dev *cdev, u16 vp_id, u32 rate); -void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate); +void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, + struct qed_ptt *p_ptt, + u32 min_pf_rate); void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); #define QED_LEADING_HWFN(dev) (&dev->hwfns[0]) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index d6c5a8165b5f..e2a081ceaf52 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -3198,7 +3198,8 @@ int qed_configure_vport_wfq(struct qed_dev *cdev, u16 vp_id, u32 rate) } /* API to configure WFQ from mcp link change */ -void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate) +void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, + struct qed_ptt *p_ptt, u32 min_pf_rate) { int i; @@ -3212,8 +3213,7 @@ void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate) for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; - __qed_configure_vp_wfq_on_link_change(p_hwfn, - p_hwfn->p_dpc_ptt, + __qed_configure_vp_wfq_on_link_change(p_hwfn, p_ptt, min_pf_rate); } } diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 314022df3469..87fde205149f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -679,7 +679,8 @@ static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn, /* Min bandwidth configuration */ __qed_configure_pf_min_bandwidth(p_hwfn, p_ptt, p_link, min_bw); - qed_configure_vp_wfq_on_link_change(p_hwfn->cdev, p_link->min_pf_rate); + qed_configure_vp_wfq_on_link_change(p_hwfn->cdev, p_ptt, + p_link->min_pf_rate); p_link->an = !!(status & LINK_STATUS_AUTO_NEGOTIATE_ENABLED); p_link->an_complete = !!(status & diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 29ed785f1dc2..253c2bbe1e4e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3014,8 +3014,7 @@ cleanup: ack_vfs[vfid / 32] |= BIT((vfid % 32)); p_hwfn->pf_iov_info->pending_flr[rel_vf_id / 64] &= ~(1ULL << (rel_vf_id % 64)); - p_hwfn->pf_iov_info->pending_events[rel_vf_id / 64] &= - ~(1ULL << (rel_vf_id % 64)); + p_vf->vf_mbx.b_pending_msg = false; } return rc; @@ -3128,11 +3127,20 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, mbx = &p_vf->vf_mbx; /* qed_iov_process_mbx_request */ - DP_VERBOSE(p_hwfn, QED_MSG_IOV, - "VF[%02x]: Processing mailbox message\n", p_vf->abs_vf_id); + if (!mbx->b_pending_msg) { + DP_NOTICE(p_hwfn, + "VF[%02x]: Trying to process mailbox message when none is pending\n", + p_vf->abs_vf_id); + return; + } + mbx->b_pending_msg = false; mbx->first_tlv = mbx->req_virt->first_tlv; + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%02x]: Processing mailbox message [type %04x]\n", + p_vf->abs_vf_id, mbx->first_tlv.tl.type); + /* check if tlv type is known */ if (qed_iov_tlv_supported(mbx->first_tlv.tl.type) && !p_vf->b_malicious) { @@ -3219,20 +3227,19 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, } } -static void qed_iov_pf_add_pending_events(struct qed_hwfn *p_hwfn, u8 vfid) +void qed_iov_pf_get_pending_events(struct qed_hwfn *p_hwfn, u64 *events) { - u64 add_bit = 1ULL << (vfid % 64); + int i; - p_hwfn->pf_iov_info->pending_events[vfid / 64] |= add_bit; -} + memset(events, 0, sizeof(u64) * QED_VF_ARRAY_LENGTH); -static void qed_iov_pf_get_and_clear_pending_events(struct qed_hwfn *p_hwfn, - u64 *events) -{ - u64 *p_pending_events = p_hwfn->pf_iov_info->pending_events; + qed_for_each_vf(p_hwfn, i) { + struct qed_vf_info *p_vf; - memcpy(events, p_pending_events, sizeof(u64) * QED_VF_ARRAY_LENGTH); - memset(p_pending_events, 0, sizeof(u64) * QED_VF_ARRAY_LENGTH); + p_vf = &p_hwfn->pf_iov_info->vfs_array[i]; + if (p_vf->vf_mbx.b_pending_msg) + events[i / 64] |= 1ULL << (i % 64); + } } static struct qed_vf_info *qed_sriov_get_vf_from_absid(struct qed_hwfn *p_hwfn, @@ -3266,7 +3273,7 @@ static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn, p_vf->vf_mbx.pending_req = (((u64)vf_msg->hi) << 32) | vf_msg->lo; /* Mark the event and schedule the workqueue */ - qed_iov_pf_add_pending_events(p_hwfn, p_vf->relative_vf_id); + p_vf->vf_mbx.b_pending_msg = true; qed_schedule_iov(p_hwfn, QED_IOV_WQ_MSG_FLAG); return 0; @@ -4030,7 +4037,7 @@ static void qed_handle_vf_msg(struct qed_hwfn *hwfn) return; } - qed_iov_pf_get_and_clear_pending_events(hwfn, events); + qed_iov_pf_get_pending_events(hwfn, events); DP_VERBOSE(hwfn, QED_MSG_IOV, "Event mask of VF events: 0x%llx 0x%llx 0x%llx\n", diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index fc08cc2da6a7..a89605821522 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -140,6 +140,9 @@ struct qed_iov_vf_mbx { /* Address in VF where a pending message is located */ dma_addr_t pending_req; + /* Message from VF awaits handling */ + bool b_pending_msg; + u8 *offset; /* saved VF request header */ @@ -232,7 +235,6 @@ struct qed_vf_info { */ struct qed_pf_iov { struct qed_vf_info vfs_array[MAX_NUM_VFS]; - u64 pending_events[QED_VF_ARRAY_LENGTH]; u64 pending_flr[QED_VF_ARRAY_LENGTH]; /* Allocate message address continuosuly and split to each VF */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 99b187bfdd55..718bf58a7da6 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -178,7 +178,7 @@ const u32 qlcnic_83xx_reg_tbl[] = { 0x3540, /* Device state, DRV_REG1 */ 0x3544, /* Driver state, DRV_REG2 */ 0x3548, /* Driver scratch, DRV_REG3 */ - 0x354C, /* Device partiton info, DRV_REG4 */ + 0x354C, /* Device partition info, DRV_REG4 */ 0x3524, /* Driver IDC ver, DRV_REG5 */ 0x3550, /* FW_VER_MAJOR */ 0x3554, /* FW_VER_MINOR */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index ccbb04503b27..73027a6c06c7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -1192,56 +1192,56 @@ static struct device_attribute dev_attr_beacon = { .store = qlcnic_store_beacon, }; -static struct bin_attribute bin_attr_crb = { +static const struct bin_attribute bin_attr_crb = { .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_read_crb, .write = qlcnic_sysfs_write_crb, }; -static struct bin_attribute bin_attr_mem = { +static const struct bin_attribute bin_attr_mem = { .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_read_mem, .write = qlcnic_sysfs_write_mem, }; -static struct bin_attribute bin_attr_npar_config = { +static const struct bin_attribute bin_attr_npar_config = { .attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_read_npar_config, .write = qlcnic_sysfs_write_npar_config, }; -static struct bin_attribute bin_attr_pci_config = { +static const struct bin_attribute bin_attr_pci_config = { .attr = {.name = "pci_config", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_read_pci_config, .write = NULL, }; -static struct bin_attribute bin_attr_port_stats = { +static const struct bin_attribute bin_attr_port_stats = { .attr = {.name = "port_stats", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_get_port_stats, .write = qlcnic_sysfs_clear_port_stats, }; -static struct bin_attribute bin_attr_esw_stats = { +static const struct bin_attribute bin_attr_esw_stats = { .attr = {.name = "esw_stats", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_get_esw_stats, .write = qlcnic_sysfs_clear_esw_stats, }; -static struct bin_attribute bin_attr_esw_config = { +static const struct bin_attribute bin_attr_esw_config = { .attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_read_esw_config, .write = qlcnic_sysfs_write_esw_config, }; -static struct bin_attribute bin_attr_pm_config = { +static const struct bin_attribute bin_attr_pm_config = { .attr = {.name = "pm_config", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_sysfs_read_pm_config, diff --git a/drivers/net/ethernet/sfc/falcon/falcon.c b/drivers/net/ethernet/sfc/falcon/falcon.c index c6ff0cc5ef18..93c713c1f627 100644 --- a/drivers/net/ethernet/sfc/falcon/falcon.c +++ b/drivers/net/ethernet/sfc/falcon/falcon.c @@ -16,6 +16,8 @@ #include <linux/i2c.h> #include <linux/mii.h> #include <linux/slab.h> +#include <linux/sched/signal.h> + #include "net_driver.h" #include "bitfield.h" #include "efx.h" diff --git a/drivers/net/ethernet/sfc/mcdi_pcol.h b/drivers/net/ethernet/sfc/mcdi_pcol.h index 47ced8a898ca..91fb54fd03d9 100644 --- a/drivers/net/ethernet/sfc/mcdi_pcol.h +++ b/drivers/net/ethernet/sfc/mcdi_pcol.h @@ -10832,7 +10832,7 @@ /***********************************/ /* MC_CMD_GET_LICENSED_V3_FEATURE_STATES - * Query the state of an one or more licensed features. (Note that the actual + * Query the state of one or more licensed features. (Note that the actual * state may be invalidated by the MC_CMD_LICENSING_V3 OP_UPDATE_LICENSE * operation or a reboot of the MC.) Used for V3 licensing (Medford) */ diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c index 19a458716f1a..1b6f6171d078 100644 --- a/drivers/net/ethernet/sis/sis900.c +++ b/drivers/net/ethernet/sis/sis900.c @@ -176,7 +176,7 @@ struct sis900_private { u32 msg_enable; - unsigned int cur_rx, dirty_rx; /* producer/comsumer pointers for Tx/Rx ring */ + unsigned int cur_rx, dirty_rx; /* producer/consumer pointers for Tx/Rx ring */ unsigned int cur_tx, dirty_tx; /* The saved address of a sent/receive-in-place packet buffer */ diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 144fe84e8a53..04d9245b7149 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -416,7 +416,7 @@ struct stmmac_dma_ops { /* Configure the AXI Bus Mode Register */ void (*axi)(void __iomem *ioaddr, struct stmmac_axi *axi); /* Dump DMA registers */ - void (*dump_regs) (void __iomem *ioaddr); + void (*dump_regs)(void __iomem *ioaddr, u32 *reg_space); /* Set tx/rx threshold in the csr6 register * An invalid value enables the store-and-forward mode */ void (*dma_mode)(void __iomem *ioaddr, int txmode, int rxmode, @@ -456,7 +456,7 @@ struct stmmac_ops { /* Enable RX Queues */ void (*rx_queue_enable)(struct mac_device_info *hw, u32 queue); /* Dump MAC registers */ - void (*dump_regs)(struct mac_device_info *hw); + void (*dump_regs)(struct mac_device_info *hw, u32 *reg_space); /* Handle extra events on specific interrupts hw dependent */ int (*host_irq_status)(struct mac_device_info *hw, struct stmmac_extra_stats *x); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 91c8926b7479..19b9b3087099 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -92,17 +92,13 @@ static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw) return !!(value & GMAC_CONTROL_IPC); } -static void dwmac1000_dump_regs(struct mac_device_info *hw) +static void dwmac1000_dump_regs(struct mac_device_info *hw, u32 *reg_space) { void __iomem *ioaddr = hw->pcsr; int i; - pr_info("\tDWMAC1000 regs (base addr = 0x%p)\n", ioaddr); - for (i = 0; i < 55; i++) { - int offset = i * 4; - pr_info("\tReg No. %d (offset 0x%x): 0x%08x\n", i, - offset, readl(ioaddr + offset)); - } + for (i = 0; i < 55; i++) + reg_space[i] = readl(ioaddr + i * 4); } static void dwmac1000_set_umac_addr(struct mac_device_info *hw, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index fbaec0ffd9ef..d3654a447046 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -201,18 +201,14 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, writel(csr6, ioaddr + DMA_CONTROL); } -static void dwmac1000_dump_dma_regs(void __iomem *ioaddr) +static void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space) { int i; - pr_info(" DMA registers\n"); - for (i = 0; i < 22; i++) { - if ((i < 9) || (i > 17)) { - int offset = i * 4; - pr_err("\t Reg No. %d (offset 0x%x): 0x%08x\n", i, - (DMA_BUS_MODE + offset), - readl(ioaddr + DMA_BUS_MODE + offset)); - } - } + + for (i = 0; i < 22; i++) + if ((i < 9) || (i > 17)) + reg_space[DMA_BUS_MODE / 4 + i] = + readl(ioaddr + DMA_BUS_MODE + i * 4); } static void dwmac1000_get_hw_feature(void __iomem *ioaddr, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 8ab518997b1b..e370ccec6176 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -40,28 +40,18 @@ static void dwmac100_core_init(struct mac_device_info *hw, int mtu) #endif } -static void dwmac100_dump_mac_regs(struct mac_device_info *hw) +static void dwmac100_dump_mac_regs(struct mac_device_info *hw, u32 *reg_space) { void __iomem *ioaddr = hw->pcsr; - pr_info("\t----------------------------------------------\n" - "\t DWMAC 100 CSR (base addr = 0x%p)\n" - "\t----------------------------------------------\n", ioaddr); - pr_info("\tcontrol reg (offset 0x%x): 0x%08x\n", MAC_CONTROL, - readl(ioaddr + MAC_CONTROL)); - pr_info("\taddr HI (offset 0x%x): 0x%08x\n ", MAC_ADDR_HIGH, - readl(ioaddr + MAC_ADDR_HIGH)); - pr_info("\taddr LO (offset 0x%x): 0x%08x\n", MAC_ADDR_LOW, - readl(ioaddr + MAC_ADDR_LOW)); - pr_info("\tmulticast hash HI (offset 0x%x): 0x%08x\n", - MAC_HASH_HIGH, readl(ioaddr + MAC_HASH_HIGH)); - pr_info("\tmulticast hash LO (offset 0x%x): 0x%08x\n", - MAC_HASH_LOW, readl(ioaddr + MAC_HASH_LOW)); - pr_info("\tflow control (offset 0x%x): 0x%08x\n", - MAC_FLOW_CTRL, readl(ioaddr + MAC_FLOW_CTRL)); - pr_info("\tVLAN1 tag (offset 0x%x): 0x%08x\n", MAC_VLAN1, - readl(ioaddr + MAC_VLAN1)); - pr_info("\tVLAN2 tag (offset 0x%x): 0x%08x\n", MAC_VLAN2, - readl(ioaddr + MAC_VLAN2)); + + reg_space[MAC_CONTROL / 4] = readl(ioaddr + MAC_CONTROL); + reg_space[MAC_ADDR_HIGH / 4] = readl(ioaddr + MAC_ADDR_HIGH); + reg_space[MAC_ADDR_LOW / 4] = readl(ioaddr + MAC_ADDR_LOW); + reg_space[MAC_HASH_HIGH / 4] = readl(ioaddr + MAC_HASH_HIGH); + reg_space[MAC_HASH_LOW / 4] = readl(ioaddr + MAC_HASH_LOW); + reg_space[MAC_FLOW_CTRL / 4] = readl(ioaddr + MAC_FLOW_CTRL); + reg_space[MAC_VLAN1 / 4] = readl(ioaddr + MAC_VLAN1); + reg_space[MAC_VLAN2 / 4] = readl(ioaddr + MAC_VLAN2); } static int dwmac100_rx_ipc_enable(struct mac_device_info *hw) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index d40e91e8fc7b..eef2f222ce9a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -66,19 +66,18 @@ static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode, writel(csr6, ioaddr + DMA_CONTROL); } -static void dwmac100_dump_dma_regs(void __iomem *ioaddr) +static void dwmac100_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space) { int i; - pr_debug("DWMAC 100 DMA CSR\n"); for (i = 0; i < 9; i++) - pr_debug("\t CSR%d (offset 0x%x): 0x%08x\n", i, - (DMA_BUS_MODE + i * 4), - readl(ioaddr + DMA_BUS_MODE + i * 4)); + reg_space[DMA_BUS_MODE / 4 + i] = + readl(ioaddr + DMA_BUS_MODE + i * 4); - pr_debug("\tCSR20 (0x%x): 0x%08x, CSR21 (0x%x): 0x%08x\n", - DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR), - DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR)); + reg_space[DMA_CUR_TX_BUF_ADDR / 4] = + readl(ioaddr + DMA_CUR_TX_BUF_ADDR); + reg_space[DMA_CUR_RX_BUF_ADDR / 4] = + readl(ioaddr + DMA_CUR_RX_BUF_ADDR); } /* DMA controller has two counters to track the number of the missed frames. */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 202216cd6789..1e79e6529c4a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -70,19 +70,13 @@ static void dwmac4_rx_queue_enable(struct mac_device_info *hw, u32 queue) writel(value, ioaddr + GMAC_RXQ_CTRL0); } -static void dwmac4_dump_regs(struct mac_device_info *hw) +static void dwmac4_dump_regs(struct mac_device_info *hw, u32 *reg_space) { void __iomem *ioaddr = hw->pcsr; int i; - pr_debug("\tDWMAC4 regs (base addr = 0x%p)\n", ioaddr); - - for (i = 0; i < GMAC_REG_NUM; i++) { - int offset = i * 4; - - pr_debug("\tReg No. %d (offset 0x%x): 0x%08x\n", i, - offset, readl(ioaddr + offset)); - } + for (i = 0; i < GMAC_REG_NUM; i++) + reg_space[i] = readl(ioaddr + i * 4); } static int dwmac4_rx_ipc_enable(struct mac_device_info *hw) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 377d1b44d4f2..f97b0d5d9987 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -127,53 +127,51 @@ static void dwmac4_dma_init(void __iomem *ioaddr, dwmac4_dma_init_channel(ioaddr, dma_cfg, dma_tx, dma_rx, i); } -static void _dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 channel) +static void _dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 channel, + u32 *reg_space) { - pr_debug(" Channel %d\n", channel); - pr_debug("\tDMA_CHAN_CONTROL, offset: 0x%x, val: 0x%x\n", 0, - readl(ioaddr + DMA_CHAN_CONTROL(channel))); - pr_debug("\tDMA_CHAN_TX_CONTROL, offset: 0x%x, val: 0x%x\n", 0x4, - readl(ioaddr + DMA_CHAN_TX_CONTROL(channel))); - pr_debug("\tDMA_CHAN_RX_CONTROL, offset: 0x%x, val: 0x%x\n", 0x8, - readl(ioaddr + DMA_CHAN_RX_CONTROL(channel))); - pr_debug("\tDMA_CHAN_TX_BASE_ADDR, offset: 0x%x, val: 0x%x\n", 0x14, - readl(ioaddr + DMA_CHAN_TX_BASE_ADDR(channel))); - pr_debug("\tDMA_CHAN_RX_BASE_ADDR, offset: 0x%x, val: 0x%x\n", 0x1c, - readl(ioaddr + DMA_CHAN_RX_BASE_ADDR(channel))); - pr_debug("\tDMA_CHAN_TX_END_ADDR, offset: 0x%x, val: 0x%x\n", 0x20, - readl(ioaddr + DMA_CHAN_TX_END_ADDR(channel))); - pr_debug("\tDMA_CHAN_RX_END_ADDR, offset: 0x%x, val: 0x%x\n", 0x28, - readl(ioaddr + DMA_CHAN_RX_END_ADDR(channel))); - pr_debug("\tDMA_CHAN_TX_RING_LEN, offset: 0x%x, val: 0x%x\n", 0x2c, - readl(ioaddr + DMA_CHAN_TX_RING_LEN(channel))); - pr_debug("\tDMA_CHAN_RX_RING_LEN, offset: 0x%x, val: 0x%x\n", 0x30, - readl(ioaddr + DMA_CHAN_RX_RING_LEN(channel))); - pr_debug("\tDMA_CHAN_INTR_ENA, offset: 0x%x, val: 0x%x\n", 0x34, - readl(ioaddr + DMA_CHAN_INTR_ENA(channel))); - pr_debug("\tDMA_CHAN_RX_WATCHDOG, offset: 0x%x, val: 0x%x\n", 0x38, - readl(ioaddr + DMA_CHAN_RX_WATCHDOG(channel))); - pr_debug("\tDMA_CHAN_SLOT_CTRL_STATUS, offset: 0x%x, val: 0x%x\n", 0x3c, - readl(ioaddr + DMA_CHAN_SLOT_CTRL_STATUS(channel))); - pr_debug("\tDMA_CHAN_CUR_TX_DESC, offset: 0x%x, val: 0x%x\n", 0x44, - readl(ioaddr + DMA_CHAN_CUR_TX_DESC(channel))); - pr_debug("\tDMA_CHAN_CUR_RX_DESC, offset: 0x%x, val: 0x%x\n", 0x4c, - readl(ioaddr + DMA_CHAN_CUR_RX_DESC(channel))); - pr_debug("\tDMA_CHAN_CUR_TX_BUF_ADDR, offset: 0x%x, val: 0x%x\n", 0x54, - readl(ioaddr + DMA_CHAN_CUR_TX_BUF_ADDR(channel))); - pr_debug("\tDMA_CHAN_CUR_RX_BUF_ADDR, offset: 0x%x, val: 0x%x\n", 0x5c, - readl(ioaddr + DMA_CHAN_CUR_RX_BUF_ADDR(channel))); - pr_debug("\tDMA_CHAN_STATUS, offset: 0x%x, val: 0x%x\n", 0x60, - readl(ioaddr + DMA_CHAN_STATUS(channel))); + reg_space[DMA_CHAN_CONTROL(channel) / 4] = + readl(ioaddr + DMA_CHAN_CONTROL(channel)); + reg_space[DMA_CHAN_TX_CONTROL(channel) / 4] = + readl(ioaddr + DMA_CHAN_TX_CONTROL(channel)); + reg_space[DMA_CHAN_RX_CONTROL(channel) / 4] = + readl(ioaddr + DMA_CHAN_RX_CONTROL(channel)); + reg_space[DMA_CHAN_TX_BASE_ADDR(channel) / 4] = + readl(ioaddr + DMA_CHAN_TX_BASE_ADDR(channel)); + reg_space[DMA_CHAN_RX_BASE_ADDR(channel) / 4] = + readl(ioaddr + DMA_CHAN_RX_BASE_ADDR(channel)); + reg_space[DMA_CHAN_TX_END_ADDR(channel) / 4] = + readl(ioaddr + DMA_CHAN_TX_END_ADDR(channel)); + reg_space[DMA_CHAN_RX_END_ADDR(channel) / 4] = + readl(ioaddr + DMA_CHAN_RX_END_ADDR(channel)); + reg_space[DMA_CHAN_TX_RING_LEN(channel) / 4] = + readl(ioaddr + DMA_CHAN_TX_RING_LEN(channel)); + reg_space[DMA_CHAN_RX_RING_LEN(channel) / 4] = + readl(ioaddr + DMA_CHAN_RX_RING_LEN(channel)); + reg_space[DMA_CHAN_INTR_ENA(channel) / 4] = + readl(ioaddr + DMA_CHAN_INTR_ENA(channel)); + reg_space[DMA_CHAN_RX_WATCHDOG(channel) / 4] = + readl(ioaddr + DMA_CHAN_RX_WATCHDOG(channel)); + reg_space[DMA_CHAN_SLOT_CTRL_STATUS(channel) / 4] = + readl(ioaddr + DMA_CHAN_SLOT_CTRL_STATUS(channel)); + reg_space[DMA_CHAN_CUR_TX_DESC(channel) / 4] = + readl(ioaddr + DMA_CHAN_CUR_TX_DESC(channel)); + reg_space[DMA_CHAN_CUR_RX_DESC(channel) / 4] = + readl(ioaddr + DMA_CHAN_CUR_RX_DESC(channel)); + reg_space[DMA_CHAN_CUR_TX_BUF_ADDR(channel) / 4] = + readl(ioaddr + DMA_CHAN_CUR_TX_BUF_ADDR(channel)); + reg_space[DMA_CHAN_CUR_RX_BUF_ADDR(channel) / 4] = + readl(ioaddr + DMA_CHAN_CUR_RX_BUF_ADDR(channel)); + reg_space[DMA_CHAN_STATUS(channel) / 4] = + readl(ioaddr + DMA_CHAN_STATUS(channel)); } -static void dwmac4_dump_dma_regs(void __iomem *ioaddr) +static void dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space) { int i; - pr_debug(" GMAC4 DMA registers\n"); - for (i = 0; i < DMA_CHANNEL_NB_MAX; i++) - _dwmac4_dump_dma_regs(ioaddr, i); + _dwmac4_dump_dma_regs(ioaddr, i, reg_space); } static void dwmac4_rx_watchdog(void __iomem *ioaddr, u32 riwt) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 5ff6bc4eb8f1..85d64114e159 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -435,32 +435,14 @@ static int stmmac_ethtool_get_regs_len(struct net_device *dev) static void stmmac_ethtool_gregs(struct net_device *dev, struct ethtool_regs *regs, void *space) { - int i; u32 *reg_space = (u32 *) space; struct stmmac_priv *priv = netdev_priv(dev); memset(reg_space, 0x0, REG_SPACE_SIZE); - if (priv->plat->has_gmac || priv->plat->has_gmac4) { - /* MAC registers */ - for (i = 0; i < 55; i++) - reg_space[i] = readl(priv->ioaddr + (i * 4)); - /* DMA registers */ - for (i = 0; i < 22; i++) - reg_space[i + 55] = - readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4))); - } else { - /* MAC registers */ - for (i = 0; i < 12; i++) - reg_space[i] = readl(priv->ioaddr + (i * 4)); - /* DMA registers */ - for (i = 0; i < 9; i++) - reg_space[i + 12] = - readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4))); - reg_space[22] = readl(priv->ioaddr + DMA_CUR_TX_BUF_ADDR); - reg_space[23] = readl(priv->ioaddr + DMA_CUR_RX_BUF_ADDR); - } + priv->hw->mac->dump_regs(priv->hw, reg_space); + priv->hw->dma->dump_regs(priv->ioaddr, reg_space); } static void diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3cbe09682afe..4498a3861aa3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1729,11 +1729,6 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) priv->hw->dma->start_tx(priv->ioaddr); priv->hw->dma->start_rx(priv->ioaddr); - /* Dump DMA/MAC registers */ - if (netif_msg_hw(priv)) { - priv->hw->mac->dump_regs(priv->hw); - priv->hw->dma->dump_regs(priv->ioaddr); - } priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS; if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) { diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index bda0c6413450..89698741682f 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1330,7 +1330,7 @@ static int __init gtp_init(void) if (err < 0) goto unreg_genl_family; - pr_info("GTP module loaded (pdp ctx size %Zd bytes)\n", + pr_info("GTP module loaded (pdp ctx size %zd bytes)\n", sizeof(struct pdp_ctx)); return 0; diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 6e8f616be48e..1dba16bc7f8d 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -24,6 +24,7 @@ #include <linux/dma/pxa-dma.h> #include <linux/gpio.h> #include <linux/slab.h> +#include <linux/sched/clock.h> #include <net/irda/irda.h> #include <net/irda/irmod.h> diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 42da094b68dd..7ee514879531 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -40,6 +40,7 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/ktime.h> #include <linux/types.h> #include <linux/time.h> diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index a4bfc10b61dd..da85057680d6 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -9,7 +9,7 @@ #include <linux/module.h> #include <linux/skbuff.h> #include <linux/cache.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/wait.h> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d6f7838455dd..1be69d8bc909 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -146,7 +146,7 @@ static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) */ int phy_aneg_done(struct phy_device *phydev) { - if (phydev->drv->aneg_done) + if (phydev->drv && phydev->drv->aneg_done) return phydev->drv->aneg_done(phydev); return genphy_aneg_done(phydev); diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index a411b43a69eb..f9c0e62716ea 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/kmod.h> #include <linux/init.h> #include <linux/list.h> diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 08db4d687533..1da31dc47f86 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -66,7 +66,7 @@ #include <linux/uaccess.h> #include <linux/bitops.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/interrupt.h> diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 35b55a2fa1a1..4d4173d25dd0 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/skbuff.h> #include <linux/cache.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/wait.h> diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 30863e378925..dc1b1dd9157c 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -44,6 +44,7 @@ #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/major.h> #include <linux/slab.h> #include <linux/poll.h> diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index e7b516342678..4f2e8141dbe2 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -52,7 +52,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/delay.h> diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c index 3e37724d30ae..8aefb282c862 100644 --- a/drivers/net/usb/kalmia.c +++ b/drivers/net/usb/kalmia.c @@ -343,7 +343,7 @@ static const struct driver_info kalmia_info = { static const struct usb_device_id products[] = { /* The unswitched USB ID, to get the module auto loaded: */ { USB_DEVICE(0x04e8, 0x689a) }, - /* The stick swithed into modem (by e.g. usb_modeswitch): */ + /* The stick switched into modem (by e.g. usb_modeswitch): */ { USB_DEVICE(0x04e8, 0x6889), .driver_info = (unsigned long) &kalmia_info, }, { /* EMPTY == end of list */} }; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 24d5272cdce5..805674550683 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -11,6 +11,7 @@ */ #include <linux/module.h> +#include <linux/sched/signal.h> #include <linux/netdevice.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index 4f4f71b2966b..c5b21138b7eb 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -383,7 +383,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) /* REVISIT: peripheral "alignment" request is ignored ... */ dev_dbg(&intf->dev, - "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n", + "hard mtu %u (%u from dev), rx buflen %zu, align %d\n", dev->hard_mtu, tmp, dev->rx_urb_size, 1 << le32_to_cpu(u.init_c->packet_alignment)); diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index d9440bc022f2..ac69f28d92d2 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -379,7 +379,7 @@ static int sierra_net_parse_lsi(struct usbnet *dev, char *data, int datalen) u32 expected_length; if (datalen < sizeof(struct lsi_umts_single)) { - netdev_err(dev->net, "%s: Data length %d, exp >= %Zu\n", + netdev_err(dev->net, "%s: Data length %d, exp >= %zu\n", __func__, datalen, sizeof(struct lsi_umts_single)); return -1; } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index bf95016f442a..66fd3139be60 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2080,7 +2080,7 @@ static int virtnet_find_vqs(struct virtnet_info *vi) } ret = vi->vdev->config->find_vqs(vi->vdev, total_vqs, vqs, callbacks, - names); + names, NULL); if (ret) goto err_find; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 556953f53437..b7911994112a 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2035,7 +2035,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, const struct iphdr *old_iph = ip_hdr(skb); union vxlan_addr *dst; union vxlan_addr remote_ip, local_ip; - union vxlan_addr *src; struct vxlan_metadata _md; struct vxlan_metadata *md = &_md; __be16 src_port = 0, dst_port; @@ -2062,7 +2061,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port; vni = (rdst->remote_vni) ? : default_vni; - src = &vxlan->cfg.saddr; + local_ip = vxlan->cfg.saddr; dst_cache = &rdst->dst_cache; md->gbp = skb->mark; ttl = vxlan->cfg.ttl; @@ -2095,7 +2094,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, dst = &remote_ip; dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; vni = tunnel_id_to_key32(info->key.tun_id); - src = &local_ip; dst_cache = &info->dst_cache; if (info->options_len) md = ip_tunnel_info_opts(info); @@ -2115,7 +2113,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, rt = vxlan_get_route(vxlan, dev, sock4, skb, rdst ? rdst->remote_ifindex : 0, tos, dst->sin.sin_addr.s_addr, - &src->sin.sin_addr.s_addr, + &local_ip.sin.sin_addr.s_addr, dst_port, src_port, dst_cache, info); if (IS_ERR(rt)) { @@ -2142,7 +2140,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (err < 0) goto tx_error; - udp_tunnel_xmit_skb(rt, sock4->sock->sk, skb, src->sin.sin_addr.s_addr, + udp_tunnel_xmit_skb(rt, sock4->sock->sk, skb, local_ip.sin.sin_addr.s_addr, dst->sin.sin_addr.s_addr, tos, ttl, df, src_port, dst_port, xnet, !udp_sum); #if IS_ENABLED(CONFIG_IPV6) @@ -2152,7 +2150,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ndst = vxlan6_get_route(vxlan, dev, sock6, skb, rdst ? rdst->remote_ifindex : 0, tos, label, &dst->sin6.sin6_addr, - &src->sin6.sin6_addr, + &local_ip.sin6.sin6_addr, dst_port, src_port, dst_cache, info); if (IS_ERR(ndst)) { @@ -2180,7 +2178,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto tx_error; udp_tunnel6_xmit_skb(ndst, sock6->sock->sk, skb, dev, - &src->sin6.sin6_addr, + &local_ip.sin6.sin6_addr, &dst->sin6.sin6_addr, tos, ttl, label, src_port, dst_port, !udp_sum); #endif @@ -2675,7 +2673,7 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) if (data[IFLA_VXLAN_ID]) { __u32 id = nla_get_u32(data[IFLA_VXLAN_ID]); - if (id >= VXLAN_VID_MASK) + if (id >= VXLAN_N_VID) return -ERANGE; } diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 087eb266601f..4ca71bca39ac 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -78,7 +78,7 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/fs.h> diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c index e74664b84925..502c346aa790 100644 --- a/drivers/net/wimax/i2400m/usb-fw.c +++ b/drivers/net/wimax/i2400m/usb-fw.c @@ -237,7 +237,7 @@ void __i2400mu_bm_notif_cb(struct urb *urb) * * @i2400m: device descriptor * @urb: urb to use - * @completion: completion varible to complete when done + * @completion: completion variable to complete when done * * Data is always read to i2400m->bm_ack_buf */ diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b7fe0af4cb24..363b30a549c2 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -20,6 +20,7 @@ #include <linux/moduleparam.h> #include <linux/inetdevice.h> #include <linux/export.h> +#include <linux/sched/signal.h> #include "core.h" #include "cfg80211.h" diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 815efe9fd208..5214dd7a3936 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -59,13 +59,13 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = { /* * MRC (Maximal Ratio Combining) has always been used with multi-antenna ofdm. * With OFDM for single stream you just add up all antenna inputs, you're - * only interested in what you get after FFT. Signal aligment is also not + * only interested in what you get after FFT. Signal alignment is also not * required for OFDM because any phase difference adds up in the frequency * domain. * * MRC requires extra work for use with CCK. You need to align the antenna * signals from the different antenna before you can add the signals together. - * You need aligment of signals as CCK is in time domain, so addition can cancel + * You need alignment of signals as CCK is in time domain, so addition can cancel * your signal completely if phase is 180 degrees (think of adding sine waves). * You also need to remove noise before the addition and this is where ANI * MRC CCK comes into play. One of the antenna inputs may be stronger but diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index e97ab2b91663..cdafebb9c936 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -36,7 +36,7 @@ #include <linux/etherdevice.h> #include <linux/firmware.h> #include <linux/workqueue.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/skbuff.h> #include <linux/dma-mapping.h> #include <linux/slab.h> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 10098b7586f3..944b83cfc519 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -4874,7 +4874,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, kfree(af_params); } else { brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); - brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len); + brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len); } exit: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index c5744b45ec8f..65689469c5a1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -22,7 +22,7 @@ #include <linux/pci_ids.h> #include <linux/netdevice.h> #include <linux/interrupt.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/mmc/sdio.h> #include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_func.h> diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 356aba9d3d53..f922859acf40 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -1238,7 +1238,7 @@ static int ipw2100_get_hw_features(struct ipw2100_priv *priv) } /* - * Start firmware execution after power on and intialization + * Start firmware execution after power on and initialization * The sequence is: * 1. Release ARC * 2. Wait for f/w initialization completes; @@ -1277,7 +1277,7 @@ static int ipw2100_start_adapter(struct ipw2100_priv *priv) /* Release ARC - clear reset bit */ write_register(priv->net_dev, IPW_REG_RESET_REG, 0); - /* wait for f/w intialization complete */ + /* wait for f/w initialization complete */ IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n"); i = 5000; do { @@ -5652,7 +5652,7 @@ static void shim__set_security(struct net_device *dev, /* As a temporary work around to enable WPA until we figure out why * wpa_supplicant toggles the security capability of the driver, which - * forces a disassocation with force_update... + * forces a disassociation with force_update... * * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index ef9af8a29cad..5ef3c5cc47c5 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -3974,7 +3974,7 @@ static void ipw_send_disassociate(struct ipw_priv *priv, int quiet) return; } - IPW_DEBUG_ASSOC("Disassocation attempt from %pM " + IPW_DEBUG_ASSOC("Disassociation attempt from %pM " "on channel %d.\n", priv->assoc_request.bssid, priv->assoc_request.channel); @@ -5196,7 +5196,7 @@ static void ipw_rx_queue_restock(struct ipw_priv *priv) * Move all used packet from rx_used to rx_free, allocating a new SKB for each. * Also restock the Rx queue via ipw_rx_queue_restock. * - * This is called as a scheduled work item (except for during intialization) + * This is called as a scheduled work item (except for during initialization) */ static void ipw_rx_queue_replenish(void *data) { diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index a91d170a614b..2781f5728d07 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -4855,39 +4855,39 @@ il4965_ucode_callback(const struct firmware *ucode_raw, void *context) */ D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); - D_INFO("f/w package hdr runtime inst size = %Zd\n", pieces.inst_size); - D_INFO("f/w package hdr runtime data size = %Zd\n", pieces.data_size); - D_INFO("f/w package hdr init inst size = %Zd\n", pieces.init_size); - D_INFO("f/w package hdr init data size = %Zd\n", pieces.init_data_size); - D_INFO("f/w package hdr boot inst size = %Zd\n", pieces.boot_size); + D_INFO("f/w package hdr runtime inst size = %zd\n", pieces.inst_size); + D_INFO("f/w package hdr runtime data size = %zd\n", pieces.data_size); + D_INFO("f/w package hdr init inst size = %zd\n", pieces.init_size); + D_INFO("f/w package hdr init data size = %zd\n", pieces.init_data_size); + D_INFO("f/w package hdr boot inst size = %zd\n", pieces.boot_size); /* Verify that uCode images will fit in card's SRAM */ if (pieces.inst_size > il->hw_params.max_inst_size) { - IL_ERR("uCode instr len %Zd too large to fit in\n", + IL_ERR("uCode instr len %zd too large to fit in\n", pieces.inst_size); goto try_again; } if (pieces.data_size > il->hw_params.max_data_size) { - IL_ERR("uCode data len %Zd too large to fit in\n", + IL_ERR("uCode data len %zd too large to fit in\n", pieces.data_size); goto try_again; } if (pieces.init_size > il->hw_params.max_inst_size) { - IL_ERR("uCode init instr len %Zd too large to fit in\n", + IL_ERR("uCode init instr len %zd too large to fit in\n", pieces.init_size); goto try_again; } if (pieces.init_data_size > il->hw_params.max_data_size) { - IL_ERR("uCode init data len %Zd too large to fit in\n", + IL_ERR("uCode init data len %zd too large to fit in\n", pieces.init_data_size); goto try_again; } if (pieces.boot_size > il->hw_params.max_bsm_size) { - IL_ERR("uCode boot instr len %Zd too large to fit in\n", + IL_ERR("uCode boot instr len %zd too large to fit in\n", pieces.boot_size); goto try_again; } @@ -4938,7 +4938,7 @@ il4965_ucode_callback(const struct firmware *ucode_raw, void *context) /* Copy images into buffers for card's bus-master reads ... */ /* Runtime instructions (first block of data in file) */ - D_INFO("Copying (but not loading) uCode instr len %Zd\n", + D_INFO("Copying (but not loading) uCode instr len %zd\n", pieces.inst_size); memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size); @@ -4949,28 +4949,28 @@ il4965_ucode_callback(const struct firmware *ucode_raw, void *context) * Runtime data * NOTE: Copy into backup buffer will be done in il_up() */ - D_INFO("Copying (but not loading) uCode data len %Zd\n", + D_INFO("Copying (but not loading) uCode data len %zd\n", pieces.data_size); memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size); memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size); /* Initialization instructions */ if (pieces.init_size) { - D_INFO("Copying (but not loading) init instr len %Zd\n", + D_INFO("Copying (but not loading) init instr len %zd\n", pieces.init_size); memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size); } /* Initialization data */ if (pieces.init_data_size) { - D_INFO("Copying (but not loading) init data len %Zd\n", + D_INFO("Copying (but not loading) init data len %zd\n", pieces.init_data_size); memcpy(il->ucode_init_data.v_addr, pieces.init_data, pieces.init_data_size); } /* Bootstrap instructions */ - D_INFO("Copying (but not loading) boot instr len %Zd\n", + D_INFO("Copying (but not loading) boot instr len %zd\n", pieces.boot_size); memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 0e0293d42b5d..be466a074c1d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1141,21 +1141,21 @@ static int validate_sec_sizes(struct iwl_drv *drv, struct iwl_firmware_pieces *pieces, const struct iwl_cfg *cfg) { - IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n", + IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %zd\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST)); - IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n", + IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %zd\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA)); - IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n", + IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %zd\n", get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST)); - IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n", + IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %zd\n", get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)); /* Verify that uCode images will fit in card's SRAM. */ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) > cfg->max_inst_size) { - IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n", + IWL_ERR(drv, "uCode instr len %zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST)); return -1; @@ -1163,7 +1163,7 @@ static int validate_sec_sizes(struct iwl_drv *drv, if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) > cfg->max_data_size) { - IWL_ERR(drv, "uCode data len %Zd too large to fit in\n", + IWL_ERR(drv, "uCode data len %zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA)); return -1; @@ -1171,7 +1171,7 @@ static int validate_sec_sizes(struct iwl_drv *drv, if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) > cfg->max_inst_size) { - IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n", + IWL_ERR(drv, "uCode init instr len %zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST)); return -1; @@ -1179,7 +1179,7 @@ static int validate_sec_sizes(struct iwl_drv *drv, if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) > cfg->max_data_size) { - IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n", + IWL_ERR(drv, "uCode init data len %zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA)); return -1; diff --git a/drivers/net/wireless/intersil/hostap/hostap_hw.c b/drivers/net/wireless/intersil/hostap/hostap_hw.c index 544ef7adde7d..04dfd040a650 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_hw.c +++ b/drivers/net/wireless/intersil/hostap/hostap_hw.c @@ -43,7 +43,7 @@ #include <linux/delay.h> #include <linux/random.h> #include <linux/wait.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/rtnetlink.h> #include <linux/wireless.h> #include <net/iw_handler.h> diff --git a/drivers/net/wireless/intersil/hostap/hostap_ioctl.c b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c index a5656bc0e6aa..b2c6b065b542 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c @@ -2,7 +2,7 @@ #include <linux/slab.h> #include <linux/types.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/ethtool.h> #include <linux/if_arp.h> #include <linux/module.h> diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 1620a5d2757d..0889fc81ce9e 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2671,7 +2671,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, tasklet_hrtimer_init(&data->beacon_timer, mac80211_hwsim_beacon, - CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); spin_lock_bh(&hwsim_radio_lock); list_add_tail(&data->list, &hwsim_radios); diff --git a/drivers/net/wireless/marvell/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c index abdd0cf710bf..fac28bd8fbee 100644 --- a/drivers/net/wireless/marvell/mwifiex/txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/txrx.c @@ -346,9 +346,7 @@ void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, return; spin_lock_irqsave(&priv->ack_status_lock, flags); - ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id); - if (ack_skb) - idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); + ack_skb = idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); spin_unlock_irqrestore(&priv->ack_status_lock, flags); if (ack_skb) { diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index 28c2f6fae3e6..e4ff3b973850 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -673,8 +673,8 @@ void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); } -/* This function update non-tdls peer ralist tx_pause while - * tdls channel swithing +/* This function updates non-tdls peer ralist tx_pause while + * tdls channel switching */ void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, u8 *mac, u8 tx_pause) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c index 1922e78ad6bd..89a0a28b8b20 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/fw.c @@ -455,7 +455,7 @@ static u32 _rtl92s_fill_h2c_cmd(struct sk_buff *skb, u32 h2cbufferlen, u8 i = 0; do { - /* 8 - Byte aligment */ + /* 8 - Byte alignment */ len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8); /* Buffer length is not enough */ @@ -504,7 +504,7 @@ static u32 _rtl92s_get_h2c_cmdlen(u32 h2cbufferlen, u32 cmd_num, u32 *pcmd_len) u8 i = 0; do { - /* 8 - Byte aligment */ + /* 8 - Byte alignment */ len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8); /* Buffer length is not enough */ diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index ef5d394f185b..cc8deecea8cb 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -516,7 +516,7 @@ err: /** * rsi_disconnect() - This function performs the reverse of the probe function, - * it deintialize the driver structure. + * it deinitialize the driver structure. * @pfunction: Pointer to the USB interface structure. * * Return: None. diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 5bdf7a03e3dd..d1aa3eee0e81 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -178,7 +178,7 @@ static struct wlcore_conf wl18xx_conf = { .sg = { .params = { [WL18XX_CONF_SG_PARAM_0] = 0, - /* Configuartion Parameters */ + /* Configuration Parameters */ [WL18XX_CONF_SG_ANTENNA_CONFIGURATION] = 0, [WL18XX_CONF_SG_ZIGBEE_COEX] = 0, [WL18XX_CONF_SG_TIME_SYNC] = 0, diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index d0b7734030ef..58898b99d3f7 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -544,7 +544,7 @@ static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) return 0; } -/* vif-specific intialization */ +/* vif-specific initialization */ static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index a2d326760a72..829b26cd4549 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -31,6 +31,7 @@ #include "common.h" #include <linux/kthread.h> +#include <linux/sched/task.h> #include <linux/ethtool.h> #include <linux/rtnetlink.h> #include <linux/if_vlan.h> diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c index d9c55830b2b2..a966c6a85ea8 100644 --- a/drivers/nfc/pn533/pn533.c +++ b/drivers/nfc/pn533/pn533.c @@ -487,7 +487,7 @@ static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code, /* * pn533_send_cmd_direct_async * - * The function sends a piority cmd directly to the chip omiting the cmd + * The function sends a piority cmd directly to the chip omitting the cmd * queue. It's intended to be used by chaining mechanism of received responses * where the host has to request every single chunk of data before scheduling * next cmd from the queue. diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index ce3e8dfa10ad..1b481a5fb966 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -1700,6 +1700,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) struct device *create_namespace_pmem(struct nd_region *nd_region, struct nd_namespace_label *nd_label) { + u64 altcookie = nd_region_interleave_set_altcookie(nd_region); u64 cookie = nd_region_interleave_set_cookie(nd_region); struct nd_label_ent *label_ent; struct nd_namespace_pmem *nspm; @@ -1718,7 +1719,11 @@ struct device *create_namespace_pmem(struct nd_region *nd_region, if (__le64_to_cpu(nd_label->isetcookie) != cookie) { dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", nd_label->uuid); - return ERR_PTR(-EAGAIN); + if (__le64_to_cpu(nd_label->isetcookie) != altcookie) + return ERR_PTR(-EAGAIN); + + dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n", + nd_label->uuid); } nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); @@ -1733,9 +1738,14 @@ struct device *create_namespace_pmem(struct nd_region *nd_region, res->name = dev_name(&nd_region->dev); res->flags = IORESOURCE_MEM; - for (i = 0; i < nd_region->ndr_mappings; i++) - if (!has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) - break; + for (i = 0; i < nd_region->ndr_mappings; i++) { + if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) + continue; + if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i)) + continue; + break; + } + if (i < nd_region->ndr_mappings) { struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 35dd75057e16..2a99c83aa19f 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -328,6 +328,7 @@ struct nd_region *to_nd_region(struct device *dev); int nd_region_to_nstype(struct nd_region *nd_region); int nd_region_register_namespaces(struct nd_region *nd_region, int *err); u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region); void nvdimm_bus_lock(struct device *dev); void nvdimm_bus_unlock(struct device *dev); bool is_nvdimm_bus_locked(struct device *dev); diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 7cd705f3247c..b7cb5066d961 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -505,6 +505,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region) return 0; } +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region) +{ + struct nd_interleave_set *nd_set = nd_region->nd_set; + + if (nd_set) + return nd_set->altcookie; + return 0; +} + void nd_mapping_free_labels(struct nd_mapping *nd_mapping) { struct nd_label_ent *label_ent, *e; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 44a1a257e0b5..9b3b57fef446 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -26,6 +26,7 @@ #include <linux/ptrace.h> #include <linux/nvme_ioctl.h> #include <linux/t10-pi.h> +#include <linux/pm_qos.h> #include <scsi/sg.h> #include <asm/unaligned.h> @@ -56,6 +57,11 @@ EXPORT_SYMBOL_GPL(nvme_max_retries); static int nvme_char_major; module_param(nvme_char_major, int, 0); +static unsigned long default_ps_max_latency_us = 25000; +module_param(default_ps_max_latency_us, ulong, 0644); +MODULE_PARM_DESC(default_ps_max_latency_us, + "max power saving latency for new devices; use PM QOS to change per device"); + static LIST_HEAD(nvme_ctrl_list); static DEFINE_SPINLOCK(dev_list_lock); @@ -560,7 +566,7 @@ int nvme_identify_ctrl(struct nvme_ctrl *dev, struct nvme_id_ctrl **id) /* gcc-4.4.4 (at least) has issues with initializers and anon unions */ c.identify.opcode = nvme_admin_identify; - c.identify.cns = cpu_to_le32(NVME_ID_CNS_CTRL); + c.identify.cns = NVME_ID_CNS_CTRL; *id = kmalloc(sizeof(struct nvme_id_ctrl), GFP_KERNEL); if (!*id) @@ -578,7 +584,7 @@ static int nvme_identify_ns_list(struct nvme_ctrl *dev, unsigned nsid, __le32 *n struct nvme_command c = { }; c.identify.opcode = nvme_admin_identify; - c.identify.cns = cpu_to_le32(NVME_ID_CNS_NS_ACTIVE_LIST); + c.identify.cns = NVME_ID_CNS_NS_ACTIVE_LIST; c.identify.nsid = cpu_to_le32(nsid); return nvme_submit_sync_cmd(dev->admin_q, &c, ns_list, 0x1000); } @@ -590,8 +596,9 @@ int nvme_identify_ns(struct nvme_ctrl *dev, unsigned nsid, int error; /* gcc-4.4.4 (at least) has issues with initializers and anon unions */ - c.identify.opcode = nvme_admin_identify, - c.identify.nsid = cpu_to_le32(nsid), + c.identify.opcode = nvme_admin_identify; + c.identify.nsid = cpu_to_le32(nsid); + c.identify.cns = NVME_ID_CNS_NS; *id = kmalloc(sizeof(struct nvme_id_ns), GFP_KERNEL); if (!*id) @@ -1251,6 +1258,176 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl, blk_queue_write_cache(q, vwc, vwc); } +static void nvme_configure_apst(struct nvme_ctrl *ctrl) +{ + /* + * APST (Autonomous Power State Transition) lets us program a + * table of power state transitions that the controller will + * perform automatically. We configure it with a simple + * heuristic: we are willing to spend at most 2% of the time + * transitioning between power states. Therefore, when running + * in any given state, we will enter the next lower-power + * non-operational state after waiting 100 * (enlat + exlat) + * microseconds, as long as that state's total latency is under + * the requested maximum latency. + * + * We will not autonomously enter any non-operational state for + * which the total latency exceeds ps_max_latency_us. Users + * can set ps_max_latency_us to zero to turn off APST. + */ + + unsigned apste; + struct nvme_feat_auto_pst *table; + int ret; + + /* + * If APST isn't supported or if we haven't been initialized yet, + * then don't do anything. + */ + if (!ctrl->apsta) + return; + + if (ctrl->npss > 31) { + dev_warn(ctrl->device, "NPSS is invalid; not using APST\n"); + return; + } + + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return; + + if (ctrl->ps_max_latency_us == 0) { + /* Turn off APST. */ + apste = 0; + } else { + __le64 target = cpu_to_le64(0); + int state; + + /* + * Walk through all states from lowest- to highest-power. + * According to the spec, lower-numbered states use more + * power. NPSS, despite the name, is the index of the + * lowest-power state, not the number of states. + */ + for (state = (int)ctrl->npss; state >= 0; state--) { + u64 total_latency_us, transition_ms; + + if (target) + table->entries[state] = target; + + /* + * Is this state a useful non-operational state for + * higher-power states to autonomously transition to? + */ + if (!(ctrl->psd[state].flags & + NVME_PS_FLAGS_NON_OP_STATE)) + continue; + + total_latency_us = + (u64)le32_to_cpu(ctrl->psd[state].entry_lat) + + + le32_to_cpu(ctrl->psd[state].exit_lat); + if (total_latency_us > ctrl->ps_max_latency_us) + continue; + + /* + * This state is good. Use it as the APST idle + * target for higher power states. + */ + transition_ms = total_latency_us + 19; + do_div(transition_ms, 20); + if (transition_ms > (1 << 24) - 1) + transition_ms = (1 << 24) - 1; + + target = cpu_to_le64((state << 3) | + (transition_ms << 8)); + } + + apste = 1; + } + + ret = nvme_set_features(ctrl, NVME_FEAT_AUTO_PST, apste, + table, sizeof(*table), NULL); + if (ret) + dev_err(ctrl->device, "failed to set APST feature (%d)\n", ret); + + kfree(table); +} + +static void nvme_set_latency_tolerance(struct device *dev, s32 val) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + u64 latency; + + switch (val) { + case PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT: + case PM_QOS_LATENCY_ANY: + latency = U64_MAX; + break; + + default: + latency = val; + } + + if (ctrl->ps_max_latency_us != latency) { + ctrl->ps_max_latency_us = latency; + nvme_configure_apst(ctrl); + } +} + +struct nvme_core_quirk_entry { + /* + * NVMe model and firmware strings are padded with spaces. For + * simplicity, strings in the quirk table are padded with NULLs + * instead. + */ + u16 vid; + const char *mn; + const char *fr; + unsigned long quirks; +}; + +static const struct nvme_core_quirk_entry core_quirks[] = { + /* + * Seen on a Samsung "SM951 NVMe SAMSUNG 256GB": using APST causes + * the controller to go out to lunch. It dies when the watchdog + * timer reads CSTS and gets 0xffffffff. + */ + { + .vid = 0x144d, + .fr = "BXW75D0Q", + .quirks = NVME_QUIRK_NO_APST, + }, +}; + +/* match is null-terminated but idstr is space-padded. */ +static bool string_matches(const char *idstr, const char *match, size_t len) +{ + size_t matchlen; + + if (!match) + return true; + + matchlen = strlen(match); + WARN_ON_ONCE(matchlen > len); + + if (memcmp(idstr, match, matchlen)) + return false; + + for (; matchlen < len; matchlen++) + if (idstr[matchlen] != ' ') + return false; + + return true; +} + +static bool quirk_matches(const struct nvme_id_ctrl *id, + const struct nvme_core_quirk_entry *q) +{ + return q->vid == le16_to_cpu(id->vid) && + string_matches(id->mn, q->mn, sizeof(id->mn)) && + string_matches(id->fr, q->fr, sizeof(id->fr)); +} + /* * Initialize the cached copies of the Identify data and various controller * register in our nvme_ctrl structure. This should be called as soon as @@ -1262,6 +1439,7 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) u64 cap; int ret, page_shift; u32 max_hw_sectors; + u8 prev_apsta; ret = ctrl->ops->reg_read32(ctrl, NVME_REG_VS, &ctrl->vs); if (ret) { @@ -1285,6 +1463,24 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) return -EIO; } + if (!ctrl->identified) { + /* + * Check for quirks. Quirk can depend on firmware version, + * so, in principle, the set of quirks present can change + * across a reset. As a possible future enhancement, we + * could re-scan for quirks every time we reinitialize + * the device, but we'd have to make sure that the driver + * behaves intelligently if the quirks change. + */ + + int i; + + for (i = 0; i < ARRAY_SIZE(core_quirks); i++) { + if (quirk_matches(id, &core_quirks[i])) + ctrl->quirks |= core_quirks[i].quirks; + } + } + ctrl->oacs = le16_to_cpu(id->oacs); ctrl->vid = le16_to_cpu(id->vid); ctrl->oncs = le16_to_cpup(&id->oncs); @@ -1305,6 +1501,11 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) ctrl->sgls = le32_to_cpu(id->sgls); ctrl->kas = le16_to_cpu(id->kas); + ctrl->npss = id->npss; + prev_apsta = ctrl->apsta; + ctrl->apsta = (ctrl->quirks & NVME_QUIRK_NO_APST) ? 0 : id->apsta; + memcpy(ctrl->psd, id->psd, sizeof(ctrl->psd)); + if (ctrl->ops->is_fabrics) { ctrl->icdoff = le16_to_cpu(id->icdoff); ctrl->ioccsz = le32_to_cpu(id->ioccsz); @@ -1328,6 +1529,16 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) } kfree(id); + + if (ctrl->apsta && !prev_apsta) + dev_pm_qos_expose_latency_tolerance(ctrl->device); + else if (!ctrl->apsta && prev_apsta) + dev_pm_qos_hide_latency_tolerance(ctrl->device); + + nvme_configure_apst(ctrl); + + ctrl->identified = true; + return ret; } EXPORT_SYMBOL_GPL(nvme_init_identify); @@ -1577,6 +1788,29 @@ static ssize_t nvme_sysfs_show_transport(struct device *dev, } static DEVICE_ATTR(transport, S_IRUGO, nvme_sysfs_show_transport, NULL); +static ssize_t nvme_sysfs_show_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + static const char *const state_name[] = { + [NVME_CTRL_NEW] = "new", + [NVME_CTRL_LIVE] = "live", + [NVME_CTRL_RESETTING] = "resetting", + [NVME_CTRL_RECONNECTING]= "reconnecting", + [NVME_CTRL_DELETING] = "deleting", + [NVME_CTRL_DEAD] = "dead", + }; + + if ((unsigned)ctrl->state < ARRAY_SIZE(state_name) && + state_name[ctrl->state]) + return sprintf(buf, "%s\n", state_name[ctrl->state]); + + return sprintf(buf, "unknown state\n"); +} + +static DEVICE_ATTR(state, S_IRUGO, nvme_sysfs_show_state, NULL); + static ssize_t nvme_sysfs_show_subsysnqn(struct device *dev, struct device_attribute *attr, char *buf) @@ -1609,6 +1843,7 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_transport.attr, &dev_attr_subsysnqn.attr, &dev_attr_address.attr, + &dev_attr_state.attr, NULL }; @@ -2065,6 +2300,14 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, list_add_tail(&ctrl->node, &nvme_ctrl_list); spin_unlock(&dev_list_lock); + /* + * Initialize latency tolerance controls. The sysfs files won't + * be visible to userspace unless the device actually supports APST. + */ + ctrl->device->power.set_latency_tolerance = nvme_set_latency_tolerance; + dev_pm_qos_update_user_latency_tolerance(ctrl->device, + min(default_ps_max_latency_us, (unsigned long)S32_MAX)); + return 0; out_release_instance: nvme_release_instance(ctrl); @@ -2090,9 +2333,9 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl) * Revalidating a dead namespace sets capacity to 0. This will * end buffered writers dirtying pages that can't be synced. */ - if (ns->disk && !test_and_set_bit(NVME_NS_DEAD, &ns->flags)) - revalidate_disk(ns->disk); - + if (!ns->disk || test_and_set_bit(NVME_NS_DEAD, &ns->flags)) + continue; + revalidate_disk(ns->disk); blk_set_queue_dying(ns->queue); blk_mq_abort_requeue_list(ns->queue); blk_mq_start_stopped_hw_queues(ns->queue, true); @@ -2101,6 +2344,53 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl) } EXPORT_SYMBOL_GPL(nvme_kill_queues); +void nvme_unfreeze(struct nvme_ctrl *ctrl) +{ + struct nvme_ns *ns; + + mutex_lock(&ctrl->namespaces_mutex); + list_for_each_entry(ns, &ctrl->namespaces, list) + blk_mq_unfreeze_queue(ns->queue); + mutex_unlock(&ctrl->namespaces_mutex); +} +EXPORT_SYMBOL_GPL(nvme_unfreeze); + +void nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl, long timeout) +{ + struct nvme_ns *ns; + + mutex_lock(&ctrl->namespaces_mutex); + list_for_each_entry(ns, &ctrl->namespaces, list) { + timeout = blk_mq_freeze_queue_wait_timeout(ns->queue, timeout); + if (timeout <= 0) + break; + } + mutex_unlock(&ctrl->namespaces_mutex); +} +EXPORT_SYMBOL_GPL(nvme_wait_freeze_timeout); + +void nvme_wait_freeze(struct nvme_ctrl *ctrl) +{ + struct nvme_ns *ns; + + mutex_lock(&ctrl->namespaces_mutex); + list_for_each_entry(ns, &ctrl->namespaces, list) + blk_mq_freeze_queue_wait(ns->queue); + mutex_unlock(&ctrl->namespaces_mutex); +} +EXPORT_SYMBOL_GPL(nvme_wait_freeze); + +void nvme_start_freeze(struct nvme_ctrl *ctrl) +{ + struct nvme_ns *ns; + + mutex_lock(&ctrl->namespaces_mutex); + list_for_each_entry(ns, &ctrl->namespaces, list) + blk_mq_freeze_queue_start(ns->queue); + mutex_unlock(&ctrl->namespaces_mutex); +} +EXPORT_SYMBOL_GPL(nvme_start_freeze); + void nvme_stop_queues(struct nvme_ctrl *ctrl) { struct nvme_ns *ns; diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index 916d13608059..5b7386f69f4d 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -480,11 +480,16 @@ EXPORT_SYMBOL_GPL(nvmf_connect_io_queue); * being implemented to the common NVMe fabrics library. Part of * the overall init sequence of starting up a fabrics driver. */ -void nvmf_register_transport(struct nvmf_transport_ops *ops) +int nvmf_register_transport(struct nvmf_transport_ops *ops) { + if (!ops->create_ctrl) + return -EINVAL; + mutex_lock(&nvmf_transports_mutex); list_add_tail(&ops->entry, &nvmf_transports); mutex_unlock(&nvmf_transports_mutex); + + return 0; } EXPORT_SYMBOL_GPL(nvmf_register_transport); diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h index 924145c979f1..156018182ce4 100644 --- a/drivers/nvme/host/fabrics.h +++ b/drivers/nvme/host/fabrics.h @@ -128,7 +128,7 @@ int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val); int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val); int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl); int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid); -void nvmf_register_transport(struct nvmf_transport_ops *ops); +int nvmf_register_transport(struct nvmf_transport_ops *ops); void nvmf_unregister_transport(struct nvmf_transport_ops *ops); void nvmf_free_options(struct nvmf_ctrl_options *opts); const char *nvmf_get_subsysnqn(struct nvme_ctrl *ctrl); diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index fb51a8de9b29..9690beb15e69 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2353,18 +2353,6 @@ __nvme_fc_create_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, /* sanity checks */ - /* FC-NVME supports 64-byte SQE only */ - if (ctrl->ctrl.ioccsz != 4) { - dev_err(ctrl->ctrl.device, "ioccsz %d is not supported!\n", - ctrl->ctrl.ioccsz); - goto out_remove_admin_queue; - } - /* FC-NVME supports 16-byte CQE only */ - if (ctrl->ctrl.iorcsz != 1) { - dev_err(ctrl->ctrl.device, "iorcsz %d is not supported!\n", - ctrl->ctrl.iorcsz); - goto out_remove_admin_queue; - } /* FC-NVME does not have other data in the capsule */ if (ctrl->ctrl.icdoff) { dev_err(ctrl->ctrl.device, "icdoff %d is not supported!\n", @@ -2562,8 +2550,7 @@ static int __init nvme_fc_init_module(void) if (!nvme_fc_wq) return -ENOMEM; - nvmf_register_transport(&nvme_fc_transport); - return 0; + return nvmf_register_transport(&nvme_fc_transport); } static void __exit nvme_fc_exit_module(void) diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 14cfc6f7facb..2aa20e3e5675 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -78,6 +78,11 @@ enum nvme_quirks { * readiness, which is done by reading the NVME_CSTS_RDY bit. */ NVME_QUIRK_DELAY_BEFORE_CHK_RDY = (1 << 3), + + /* + * APST should not be used. + */ + NVME_QUIRK_NO_APST = (1 << 4), }; /* @@ -112,6 +117,7 @@ enum nvme_ctrl_state { struct nvme_ctrl { enum nvme_ctrl_state state; + bool identified; spinlock_t lock; const struct nvme_ctrl_ops *ops; struct request_queue *admin_q; @@ -147,13 +153,19 @@ struct nvme_ctrl { u32 vs; u32 sgls; u16 kas; + u8 npss; + u8 apsta; unsigned int kato; bool subsystem; unsigned long quirks; + struct nvme_id_power_state psd[32]; struct work_struct scan_work; struct work_struct async_event_work; struct delayed_work ka_work; + /* Power saving configuration */ + u64 ps_max_latency_us; + /* Fabrics only */ u16 sqsize; u32 ioccsz; @@ -282,6 +294,10 @@ void nvme_queue_async_events(struct nvme_ctrl *ctrl); void nvme_stop_queues(struct nvme_ctrl *ctrl); void nvme_start_queues(struct nvme_ctrl *ctrl); void nvme_kill_queues(struct nvme_ctrl *ctrl); +void nvme_unfreeze(struct nvme_ctrl *ctrl); +void nvme_wait_freeze(struct nvme_ctrl *ctrl); +void nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl, long timeout); +void nvme_start_freeze(struct nvme_ctrl *ctrl); #define NVME_QID_ANY -1 struct request *nvme_alloc_request(struct request_queue *q, diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index ddc51adb594d..26a5fd05fe88 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -613,10 +613,7 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx, spin_lock_irq(&nvmeq->q_lock); if (unlikely(nvmeq->cq_vector < 0)) { - if (ns && !test_bit(NVME_NS_DEAD, &ns->flags)) - ret = BLK_MQ_RQ_QUEUE_BUSY; - else - ret = BLK_MQ_RQ_QUEUE_ERROR; + ret = BLK_MQ_RQ_QUEUE_ERROR; spin_unlock_irq(&nvmeq->q_lock); goto out_cleanup_iod; } @@ -1041,9 +1038,10 @@ static int nvme_alloc_sq_cmds(struct nvme_dev *dev, struct nvme_queue *nvmeq, } static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, - int depth) + int depth, int node) { - struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq), GFP_KERNEL); + struct nvme_queue *nvmeq = kzalloc_node(sizeof(*nvmeq), GFP_KERNEL, + node); if (!nvmeq) return NULL; @@ -1220,7 +1218,8 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev) nvmeq = dev->queues[0]; if (!nvmeq) { - nvmeq = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH); + nvmeq = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH, + dev_to_node(dev->dev)); if (!nvmeq) return -ENOMEM; } @@ -1312,7 +1311,9 @@ static int nvme_create_io_queues(struct nvme_dev *dev) int ret = 0; for (i = dev->queue_count; i <= dev->max_qid; i++) { - if (!nvme_alloc_queue(dev, i, dev->q_depth)) { + /* vector == qid - 1, match nvme_create_queue */ + if (!nvme_alloc_queue(dev, i, dev->q_depth, + pci_irq_get_node(to_pci_dev(dev->dev), i - 1))) { ret = -ENOMEM; break; } @@ -1674,21 +1675,34 @@ static void nvme_pci_disable(struct nvme_dev *dev) static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown) { int i, queues; - u32 csts = -1; + bool dead = true; + struct pci_dev *pdev = to_pci_dev(dev->dev); del_timer_sync(&dev->watchdog_timer); mutex_lock(&dev->shutdown_lock); - if (pci_is_enabled(to_pci_dev(dev->dev))) { - nvme_stop_queues(&dev->ctrl); - csts = readl(dev->bar + NVME_REG_CSTS); + if (pci_is_enabled(pdev)) { + u32 csts = readl(dev->bar + NVME_REG_CSTS); + + if (dev->ctrl.state == NVME_CTRL_LIVE) + nvme_start_freeze(&dev->ctrl); + dead = !!((csts & NVME_CSTS_CFS) || !(csts & NVME_CSTS_RDY) || + pdev->error_state != pci_channel_io_normal); } + /* + * Give the controller a chance to complete all entered requests if + * doing a safe shutdown. + */ + if (!dead && shutdown) + nvme_wait_freeze_timeout(&dev->ctrl, NVME_IO_TIMEOUT); + nvme_stop_queues(&dev->ctrl); + queues = dev->online_queues - 1; for (i = dev->queue_count - 1; i > 0; i--) nvme_suspend_queue(dev->queues[i]); - if (csts & NVME_CSTS_CFS || !(csts & NVME_CSTS_RDY)) { + if (dead) { /* A device might become IO incapable very soon during * probe, before the admin queue is configured. Thus, * queue_count can be 0 here. @@ -1703,6 +1717,14 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown) blk_mq_tagset_busy_iter(&dev->tagset, nvme_cancel_request, &dev->ctrl); blk_mq_tagset_busy_iter(&dev->admin_tagset, nvme_cancel_request, &dev->ctrl); + + /* + * The driver will not be starting up queues again if shutting down so + * must flush all entered requests to their failed completion to avoid + * deadlocking blk-mq hot-cpu notifier. + */ + if (shutdown) + nvme_start_queues(&dev->ctrl); mutex_unlock(&dev->shutdown_lock); } @@ -1739,7 +1761,7 @@ static void nvme_pci_free_ctrl(struct nvme_ctrl *ctrl) if (dev->ctrl.admin_q) blk_put_queue(dev->ctrl.admin_q); kfree(dev->queues); - kfree(dev->ctrl.opal_dev); + free_opal_dev(dev->ctrl.opal_dev); kfree(dev); } @@ -1789,14 +1811,17 @@ static void nvme_reset_work(struct work_struct *work) if (result) goto out; - if ((dev->ctrl.oacs & NVME_CTRL_OACS_SEC_SUPP) && !dev->ctrl.opal_dev) { - dev->ctrl.opal_dev = - init_opal_dev(&dev->ctrl, &nvme_sec_submit); + if (dev->ctrl.oacs & NVME_CTRL_OACS_SEC_SUPP) { + if (!dev->ctrl.opal_dev) + dev->ctrl.opal_dev = + init_opal_dev(&dev->ctrl, &nvme_sec_submit); + else if (was_suspend) + opal_unlock_from_suspend(dev->ctrl.opal_dev); + } else { + free_opal_dev(dev->ctrl.opal_dev); + dev->ctrl.opal_dev = NULL; } - if (was_suspend) - opal_unlock_from_suspend(dev->ctrl.opal_dev); - result = nvme_setup_io_queues(dev); if (result) goto out; @@ -1822,7 +1847,9 @@ static void nvme_reset_work(struct work_struct *work) nvme_remove_namespaces(&dev->ctrl); } else { nvme_start_queues(&dev->ctrl); + nvme_wait_freeze(&dev->ctrl); nvme_dev_add(dev); + nvme_unfreeze(&dev->ctrl); } if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_LIVE)) { @@ -2001,8 +2028,10 @@ static void nvme_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); - if (!pci_device_is_present(pdev)) + if (!pci_device_is_present(pdev)) { nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_DEAD); + nvme_dev_disable(dev, false); + } flush_work(&dev->reset_work); nvme_uninit_ctrl(&dev->ctrl); @@ -2121,6 +2150,7 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, + { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, { 0, } }; MODULE_DEVICE_TABLE(pci, nvme_id_table); diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index a75e95d42b3f..779f516e7a4e 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -42,28 +42,6 @@ #define NVME_RDMA_MAX_INLINE_SEGMENTS 1 -static const char *const nvme_rdma_cm_status_strs[] = { - [NVME_RDMA_CM_INVALID_LEN] = "invalid length", - [NVME_RDMA_CM_INVALID_RECFMT] = "invalid record format", - [NVME_RDMA_CM_INVALID_QID] = "invalid queue ID", - [NVME_RDMA_CM_INVALID_HSQSIZE] = "invalid host SQ size", - [NVME_RDMA_CM_INVALID_HRQSIZE] = "invalid host RQ size", - [NVME_RDMA_CM_NO_RSC] = "resource not found", - [NVME_RDMA_CM_INVALID_IRD] = "invalid IRD", - [NVME_RDMA_CM_INVALID_ORD] = "Invalid ORD", -}; - -static const char *nvme_rdma_cm_msg(enum nvme_rdma_cm_status status) -{ - size_t index = status; - - if (index < ARRAY_SIZE(nvme_rdma_cm_status_strs) && - nvme_rdma_cm_status_strs[index]) - return nvme_rdma_cm_status_strs[index]; - else - return "unrecognized reason"; -}; - /* * We handle AEN commands ourselves and don't even let the * block layer know about them. @@ -155,6 +133,10 @@ struct nvme_rdma_ctrl { struct sockaddr addr; struct sockaddr_in addr_in; }; + union { + struct sockaddr src_addr; + struct sockaddr_in src_addr_in; + }; struct nvme_ctrl ctrl; }; @@ -567,6 +549,7 @@ static int nvme_rdma_init_queue(struct nvme_rdma_ctrl *ctrl, int idx, size_t queue_size) { struct nvme_rdma_queue *queue; + struct sockaddr *src_addr = NULL; int ret; queue = &ctrl->queues[idx]; @@ -589,7 +572,10 @@ static int nvme_rdma_init_queue(struct nvme_rdma_ctrl *ctrl, } queue->cm_error = -ETIMEDOUT; - ret = rdma_resolve_addr(queue->cm_id, NULL, &ctrl->addr, + if (ctrl->ctrl.opts->mask & NVMF_OPT_HOST_TRADDR) + src_addr = &ctrl->src_addr; + + ret = rdma_resolve_addr(queue->cm_id, src_addr, &ctrl->addr, NVME_RDMA_CONNECT_TIMEOUT_MS); if (ret) { dev_info(ctrl->ctrl.device, @@ -1065,7 +1051,7 @@ static int nvme_rdma_post_send(struct nvme_rdma_queue *queue, * sequencer is not allocated in our driver's tagset and it's * triggered to be freed by blk_cleanup_queue(). So we need to * always mark it as signaled to ensure that the "wr_cqe", which is - * embeded in request's payload, is not freed when __ib_process_cq() + * embedded in request's payload, is not freed when __ib_process_cq() * calls wr_cqe->done(). */ if ((++queue->sig_count % 32) == 0 || flush) @@ -1265,7 +1251,7 @@ static int nvme_rdma_addr_resolved(struct nvme_rdma_queue *queue) dev = nvme_rdma_find_get_device(queue->cm_id); if (!dev) { - dev_err(queue->cm_id->device->dma_device, + dev_err(queue->cm_id->device->dev.parent, "no client data found!\n"); return -ECONNREFUSED; } @@ -1905,6 +1891,16 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, goto out_free_ctrl; } + if (opts->mask & NVMF_OPT_HOST_TRADDR) { + ret = nvme_rdma_parse_ipaddr(&ctrl->src_addr_in, + opts->host_traddr); + if (ret) { + pr_err("malformed src IP address passed: %s\n", + opts->host_traddr); + goto out_free_ctrl; + } + } + if (opts->mask & NVMF_OPT_TRSVCID) { u16 port; @@ -2016,7 +2012,8 @@ out_free_ctrl: static struct nvmf_transport_ops nvme_rdma_transport = { .name = "rdma", .required_opts = NVMF_OPT_TRADDR, - .allowed_opts = NVMF_OPT_TRSVCID | NVMF_OPT_RECONNECT_DELAY, + .allowed_opts = NVMF_OPT_TRSVCID | NVMF_OPT_RECONNECT_DELAY | + NVMF_OPT_HOST_TRADDR, .create_ctrl = nvme_rdma_create_ctrl, }; @@ -2063,8 +2060,7 @@ static int __init nvme_rdma_init_module(void) return ret; } - nvmf_register_transport(&nvme_rdma_transport); - return 0; + return nvmf_register_transport(&nvme_rdma_transport); } static void __exit nvme_rdma_cleanup_module(void) diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index 95ae52390478..a7bcff45f437 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -13,6 +13,8 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> +#include <linux/rculist.h> + #include <generated/utsrelease.h> #include <asm/unaligned.h> #include "nvmet.h" @@ -41,7 +43,7 @@ static u16 nvmet_get_smart_log_nsid(struct nvmet_req *req, ns = nvmet_find_namespace(req->sq->ctrl, req->cmd->get_log_page.nsid); if (!ns) { status = NVME_SC_INVALID_NS; - pr_err("nvmet : Counld not find namespace id : %d\n", + pr_err("nvmet : Could not find namespace id : %d\n", le32_to_cpu(req->cmd->get_log_page.nsid)); goto out; } @@ -509,7 +511,7 @@ int nvmet_parse_admin_cmd(struct nvmet_req *req) break; case nvme_admin_identify: req->data_len = 4096; - switch (le32_to_cpu(cmd->identify.cns)) { + switch (cmd->identify.cns) { case NVME_ID_CNS_NS: req->execute = nvmet_execute_identify_ns; return 0; diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index fc5ba2f9e15f..11b0a0a5f661 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -14,9 +14,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/random.h> +#include <linux/rculist.h> + #include "nvmet.h" static struct nvmet_fabrics_ops *nvmet_transports[NVMF_TRTYPE_MAX]; +static DEFINE_IDA(cntlid_ida); /* * This read/write semaphore is used to synchronize access to configuration @@ -749,7 +752,7 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn, if (!ctrl->sqs) goto out_free_cqs; - ret = ida_simple_get(&subsys->cntlid_ida, + ret = ida_simple_get(&cntlid_ida, NVME_CNTLID_MIN, NVME_CNTLID_MAX, GFP_KERNEL); if (ret < 0) { @@ -819,7 +822,7 @@ static void nvmet_ctrl_free(struct kref *ref) flush_work(&ctrl->async_event_work); cancel_work_sync(&ctrl->fatal_err_work); - ida_simple_remove(&subsys->cntlid_ida, ctrl->cntlid); + ida_simple_remove(&cntlid_ida, ctrl->cntlid); nvmet_subsys_put(subsys); kfree(ctrl->sqs); @@ -918,9 +921,6 @@ struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn, mutex_init(&subsys->lock); INIT_LIST_HEAD(&subsys->namespaces); INIT_LIST_HEAD(&subsys->ctrls); - - ida_init(&subsys->cntlid_ida); - INIT_LIST_HEAD(&subsys->hosts); return subsys; @@ -933,7 +933,6 @@ static void nvmet_subsys_free(struct kref *ref) WARN_ON_ONCE(!list_empty(&subsys->namespaces)); - ida_destroy(&subsys->cntlid_ida); kfree(subsys->subsysnqn); kfree(subsys); } @@ -976,6 +975,7 @@ static void __exit nvmet_exit(void) { nvmet_exit_configfs(); nvmet_exit_discovery(); + ida_destroy(&cntlid_ida); BUILD_BUG_ON(sizeof(struct nvmf_disc_rsp_page_entry) != 1024); BUILD_BUG_ON(sizeof(struct nvmf_disc_rsp_page_hdr) != 1024); diff --git a/drivers/nvme/target/discovery.c b/drivers/nvme/target/discovery.c index 12f39eea569f..af8aabf05335 100644 --- a/drivers/nvme/target/discovery.c +++ b/drivers/nvme/target/discovery.c @@ -186,14 +186,14 @@ int nvmet_parse_discovery_cmd(struct nvmet_req *req) } case nvme_admin_identify: req->data_len = 4096; - switch (le32_to_cpu(cmd->identify.cns)) { + switch (cmd->identify.cns) { case NVME_ID_CNS_CTRL: req->execute = nvmet_execute_identify_disc_ctrl; return 0; default: pr_err("nvmet: unsupported identify cns %d\n", - le32_to_cpu(cmd->identify.cns)); + cmd->identify.cns); return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; } default: diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c index f4088198cd0d..8bd022af3df6 100644 --- a/drivers/nvme/target/fabrics-cmd.c +++ b/drivers/nvme/target/fabrics-cmd.c @@ -153,8 +153,8 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) goto out; } - pr_info("creating controller %d for NQN %s.\n", - ctrl->cntlid, ctrl->hostnqn); + pr_info("creating controller %d for subsystem %s for NQN %s.\n", + ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn); req->rsp->result.u16 = cpu_to_le16(ctrl->cntlid); out: @@ -220,7 +220,7 @@ int nvmet_parse_connect_cmd(struct nvmet_req *req) req->ns = NULL; - if (req->cmd->common.opcode != nvme_fabrics_command) { + if (cmd->common.opcode != nvme_fabrics_command) { pr_err("invalid command 0x%x on unconnected queue.\n", cmd->fabrics.opcode); return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c index ba57f9852bde..8f483ee7868c 100644 --- a/drivers/nvme/target/fc.c +++ b/drivers/nvme/target/fc.c @@ -1817,16 +1817,14 @@ nvmet_fc_xmt_fcp_op_done(struct nvmefc_tgt_fcp_req *fcpreq) /* data no longer needed */ nvmet_fc_free_tgt_pgs(fod); - if (fcpreq->fcp_error || abort) - nvmet_req_complete(&fod->req, fcpreq->fcp_error); - + nvmet_req_complete(&fod->req, fcpreq->fcp_error); return; } switch (fcpreq->op) { case NVMET_FCOP_WRITEDATA: - if (abort || fcpreq->fcp_error || + if (fcpreq->fcp_error || fcpreq->transferred_length != fcpreq->transfer_length) { nvmet_req_complete(&fod->req, NVME_SC_FC_TRANSPORT_ERROR); @@ -1849,7 +1847,7 @@ nvmet_fc_xmt_fcp_op_done(struct nvmefc_tgt_fcp_req *fcpreq) case NVMET_FCOP_READDATA: case NVMET_FCOP_READDATA_RSP: - if (abort || fcpreq->fcp_error || + if (fcpreq->fcp_error || fcpreq->transferred_length != fcpreq->transfer_length) { /* data no longer needed */ nvmet_fc_free_tgt_pgs(fod); diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index f3862e38f574..d1f06e7768ff 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -724,8 +724,7 @@ static int __init nvme_loop_init_module(void) ret = nvmet_register_transport(&nvme_loop_ops); if (ret) return ret; - nvmf_register_transport(&nvme_loop_transport); - return 0; + return nvmf_register_transport(&nvme_loop_transport); } static void __exit nvme_loop_cleanup_module(void) diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index cc7ad06b43a7..1370eee0a3c0 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -142,7 +142,6 @@ struct nvmet_subsys { unsigned int max_nsid; struct list_head ctrls; - struct ida cntlid_ida; struct list_head hosts; bool allow_any_host; diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index 60990220bd83..9aa1da3778b3 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -1041,6 +1041,9 @@ static int nvmet_rdma_cm_reject(struct rdma_cm_id *cm_id, { struct nvme_rdma_cm_rej rej; + pr_debug("rejecting connect request: status %d (%s)\n", + status, nvme_rdma_cm_msg(status)); + rej.recfmt = cpu_to_le16(NVME_RDMA_CM_FMT_1_0); rej.sts = cpu_to_le16(status); @@ -1091,7 +1094,7 @@ nvmet_rdma_alloc_queue(struct nvmet_rdma_device *ndev, queue->idx = ida_simple_get(&nvmet_rdma_queue_ida, 0, 0, GFP_KERNEL); if (queue->idx < 0) { ret = NVME_RDMA_CM_NO_RSC; - goto out_free_queue; + goto out_destroy_sq; } ret = nvmet_rdma_alloc_rsps(queue); @@ -1135,7 +1138,6 @@ out_destroy_sq: out_free_queue: kfree(queue); out_reject: - pr_debug("rejecting connect request with status code %d\n", ret); nvmet_rdma_cm_reject(cm_id, ret); return NULL; } @@ -1188,7 +1190,6 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, ndev = nvmet_rdma_find_get_device(cm_id); if (!ndev) { - pr_err("no client data!\n"); nvmet_rdma_cm_reject(cm_id, NVME_RDMA_CM_NO_RSC); return -ECONNREFUSED; } diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 642478d35e99..ac27f3d3fbb4 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -31,6 +31,8 @@ #include <linux/fs.h> #include <linux/oprofile.h> #include <linux/sched.h> +#include <linux/sched/mm.h> +#include <linux/sched/task.h> #include <linux/gfp.h> #include "oprofile_stats.h" diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 0581461c3a67..eda2633a393d 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -23,6 +23,8 @@ #include <linux/oprofile.h> #include <linux/errno.h> +#include <asm/ptrace.h> + #include "event_buffer.h" #include "cpu_buffer.h" #include "buffer_sync.h" diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c index 67935fbbbcab..32888f2bd1a9 100644 --- a/drivers/oprofile/event_buffer.c +++ b/drivers/oprofile/event_buffer.c @@ -14,7 +14,7 @@ #include <linux/vmalloc.h> #include <linux/oprofile.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/capability.h> #include <linux/dcookies.h> #include <linux/fs.h> diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index 553ef8a5d588..e32ca2ef9e54 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -1011,7 +1011,7 @@ ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, DBG_RUN_SG("%s() DONE (nents %d)\n", __func__, nents); } -static struct dma_map_ops ccio_ops = { +static const struct dma_map_ops ccio_ops = { .dma_supported = ccio_dma_supported, .alloc = ccio_alloc, .free = ccio_free, @@ -1539,7 +1539,7 @@ static int __init ccio_probe(struct parisc_device *dev) ioc = kzalloc(sizeof(struct ioc), GFP_KERNEL); if (ioc == NULL) { printk(KERN_ERR MODULE_NAME ": memory allocation failure\n"); - return 1; + return -ENOMEM; } ioc->name = dev->id.hversion == U2_IOA_RUNWAY ? "U2" : "UTurn"; @@ -1554,6 +1554,10 @@ static int __init ccio_probe(struct parisc_device *dev) ioc->hw_path = dev->hw_path; ioc->ioc_regs = ioremap_nocache(dev->hpa.start, 4096); + if (!ioc->ioc_regs) { + kfree(ioc); + return -ENOMEM; + } ccio_ioc_init(ioc); ccio_init_resources(ioc); hppa_dma_ops = &ccio_ops; diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c index 103095bbe8c0..7e2f6d5a6aaf 100644 --- a/drivers/parisc/eisa.c +++ b/drivers/parisc/eisa.c @@ -14,16 +14,16 @@ * Wax ASIC also includes a PS/2 and RS-232 controller, but those are * dealt with elsewhere; this file is concerned only with the EISA portions * of Wax. - * - * + * + * * HINT: * ----- * To allow an ISA card to work properly in the EISA slot you need to - * set an edge trigger level. This may be done on the palo command line - * by adding the kernel parameter "eisa_irq_edge=n,n2,[...]]", with + * set an edge trigger level. This may be done on the palo command line + * by adding the kernel parameter "eisa_irq_edge=n,n2,[...]]", with * n and n2 as the irq levels you want to use. - * - * Example: "eisa_irq_edge=10,11" allows ISA cards to operate at + * + * Example: "eisa_irq_edge=10,11" allows ISA cards to operate at * irq levels 10 and 11. */ @@ -46,9 +46,9 @@ #include <asm/eisa_eeprom.h> #if 0 -#define EISA_DBG(msg, arg... ) printk(KERN_DEBUG "eisa: " msg , ## arg ) +#define EISA_DBG(msg, arg...) printk(KERN_DEBUG "eisa: " msg, ## arg) #else -#define EISA_DBG(msg, arg... ) +#define EISA_DBG(msg, arg...) #endif #define SNAKES_EEPROM_BASE_ADDR 0xF0810400 @@ -108,7 +108,7 @@ void eisa_out8(unsigned char data, unsigned short port) void eisa_out16(unsigned short data, unsigned short port) { - if (EISA_bus) + if (EISA_bus) gsc_writew(cpu_to_le16(data), eisa_permute(port)); } @@ -135,9 +135,9 @@ static int master_mask; static int slave_mask; /* the trig level can be set with the - * eisa_irq_edge=n,n,n commandline parameter - * We should really read this from the EEPROM - * in the furure. + * eisa_irq_edge=n,n,n commandline parameter + * We should really read this from the EEPROM + * in the furure. */ /* irq 13,8,2,1,0 must be edge */ static unsigned int eisa_irq_level __read_mostly; /* default to edge triggered */ @@ -170,7 +170,7 @@ static void eisa_unmask_irq(struct irq_data *d) unsigned int irq = d->irq; unsigned long flags; EISA_DBG("enable irq %d\n", irq); - + spin_lock_irqsave(&eisa_irq_lock, flags); if (irq & 8) { slave_mask &= ~(1 << (irq&7)); @@ -194,7 +194,7 @@ static irqreturn_t eisa_irq(int wax_irq, void *intr_dev) { int irq = gsc_readb(0xfc01f000); /* EISA supports 16 irqs */ unsigned long flags; - + spin_lock_irqsave(&eisa_irq_lock, flags); /* read IRR command */ eisa_out8(0x0a, 0x20); @@ -202,31 +202,31 @@ static irqreturn_t eisa_irq(int wax_irq, void *intr_dev) EISA_DBG("irq IAR %02x 8259-1 irr %02x 8259-2 irr %02x\n", irq, eisa_in8(0x20), eisa_in8(0xa0)); - + /* read ISR command */ eisa_out8(0x0a, 0x20); eisa_out8(0x0a, 0xa0); EISA_DBG("irq 8259-1 isr %02x imr %02x 8259-2 isr %02x imr %02x\n", eisa_in8(0x20), eisa_in8(0x21), eisa_in8(0xa0), eisa_in8(0xa1)); - + irq &= 0xf; - + /* mask irq and write eoi */ if (irq & 8) { slave_mask |= (1 << (irq&7)); eisa_out8(slave_mask, 0xa1); eisa_out8(0x60 | (irq&7),0xa0);/* 'Specific EOI' to slave */ - eisa_out8(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */ - + eisa_out8(0x62, 0x20); /* 'Specific EOI' to master-IRQ2 */ + } else { master_mask |= (1 << (irq&7)); eisa_out8(master_mask, 0x21); - eisa_out8(0x60|irq,0x20); /* 'Specific EOI' to master */ + eisa_out8(0x60|irq, 0x20); /* 'Specific EOI' to master */ } spin_unlock_irqrestore(&eisa_irq_lock, flags); generic_handle_irq(irq); - + spin_lock_irqsave(&eisa_irq_lock, flags); /* unmask */ if (irq & 8) { @@ -254,44 +254,44 @@ static struct irqaction irq2_action = { static void init_eisa_pic(void) { unsigned long flags; - + spin_lock_irqsave(&eisa_irq_lock, flags); eisa_out8(0xff, 0x21); /* mask during init */ eisa_out8(0xff, 0xa1); /* mask during init */ - + /* master pic */ - eisa_out8(0x11,0x20); /* ICW1 */ - eisa_out8(0x00,0x21); /* ICW2 */ - eisa_out8(0x04,0x21); /* ICW3 */ - eisa_out8(0x01,0x21); /* ICW4 */ - eisa_out8(0x40,0x20); /* OCW2 */ - + eisa_out8(0x11, 0x20); /* ICW1 */ + eisa_out8(0x00, 0x21); /* ICW2 */ + eisa_out8(0x04, 0x21); /* ICW3 */ + eisa_out8(0x01, 0x21); /* ICW4 */ + eisa_out8(0x40, 0x20); /* OCW2 */ + /* slave pic */ - eisa_out8(0x11,0xa0); /* ICW1 */ - eisa_out8(0x08,0xa1); /* ICW2 */ - eisa_out8(0x02,0xa1); /* ICW3 */ - eisa_out8(0x01,0xa1); /* ICW4 */ - eisa_out8(0x40,0xa0); /* OCW2 */ - + eisa_out8(0x11, 0xa0); /* ICW1 */ + eisa_out8(0x08, 0xa1); /* ICW2 */ + eisa_out8(0x02, 0xa1); /* ICW3 */ + eisa_out8(0x01, 0xa1); /* ICW4 */ + eisa_out8(0x40, 0xa0); /* OCW2 */ + udelay(100); - - slave_mask = 0xff; - master_mask = 0xfb; + + slave_mask = 0xff; + master_mask = 0xfb; eisa_out8(slave_mask, 0xa1); /* OCW1 */ eisa_out8(master_mask, 0x21); /* OCW1 */ - + /* setup trig level */ EISA_DBG("EISA edge/level %04x\n", eisa_irq_level); - + eisa_out8(eisa_irq_level&0xff, 0x4d0); /* Set all irq's to edge */ - eisa_out8((eisa_irq_level >> 8) & 0xff, 0x4d1); - + eisa_out8((eisa_irq_level >> 8) & 0xff, 0x4d1); + EISA_DBG("pic0 mask %02x\n", eisa_in8(0x21)); EISA_DBG("pic1 mask %02x\n", eisa_in8(0xa1)); EISA_DBG("pic0 edge/level %02x\n", eisa_in8(0x4d0)); EISA_DBG("pic1 edge/level %02x\n", eisa_in8(0x4d1)); - + spin_unlock_irqrestore(&eisa_irq_lock, flags); } @@ -305,7 +305,7 @@ static int __init eisa_probe(struct parisc_device *dev) char *name = is_mongoose(dev) ? "Mongoose" : "Wax"; - printk(KERN_INFO "%s EISA Adapter found at 0x%08lx\n", + printk(KERN_INFO "%s EISA Adapter found at 0x%08lx\n", name, (unsigned long)dev->hpa.start); eisa_dev.hba.dev = dev; @@ -334,16 +334,16 @@ static int __init eisa_probe(struct parisc_device *dev) result = request_irq(dev->irq, eisa_irq, IRQF_SHARED, "EISA", &eisa_dev); if (result) { printk(KERN_ERR "EISA: request_irq failed!\n"); - return result; + goto error_release; } - + /* Reserve IRQ2 */ setup_irq(2, &irq2_action); for (i = 0; i < 16; i++) { irq_set_chip_and_handler(i, &eisa_interrupt_type, handle_simple_irq); } - + EISA_bus = 1; if (dev->num_addrs) { @@ -358,6 +358,11 @@ static int __init eisa_probe(struct parisc_device *dev) } } eisa_eeprom_addr = ioremap_nocache(eisa_dev.eeprom_addr, HPEE_MAX_LENGTH); + if (!eisa_eeprom_addr) { + result = -ENOMEM; + printk(KERN_ERR "EISA: ioremap_nocache failed!\n"); + goto error_free_irq; + } result = eisa_enumerator(eisa_dev.eeprom_addr, &eisa_dev.hba.io_space, &eisa_dev.hba.lmmio_space); init_eisa_pic(); @@ -372,11 +377,20 @@ static int __init eisa_probe(struct parisc_device *dev) eisa_dev.root.dma_mask = 0xffffffff; /* wild guess */ if (eisa_root_register (&eisa_dev.root)) { printk(KERN_ERR "EISA: Failed to register EISA root\n"); - return -1; + result = -ENOMEM; + goto error_iounmap; } } - + return 0; + +error_iounmap: + iounmap(eisa_eeprom_addr); +error_free_irq: + free_irq(dev->irq, &eisa_dev); +error_release: + release_resource(&eisa_dev.hba.io_space); + return result; } static const struct parisc_device_id eisa_tbl[] = { @@ -404,7 +418,7 @@ void eisa_make_irq_level(int num) { if (eisa_irq_configured& (1<<num)) { printk(KERN_WARNING - "IRQ %d polarity configured twice (last to level)\n", + "IRQ %d polarity configured twice (last to level)\n", num); } eisa_irq_level |= (1<<num); /* set the corresponding bit */ @@ -414,7 +428,7 @@ void eisa_make_irq_level(int num) void eisa_make_irq_edge(int num) { if (eisa_irq_configured& (1<<num)) { - printk(KERN_WARNING + printk(KERN_WARNING "IRQ %d polarity configured twice (last to edge)\n", num); } @@ -430,18 +444,18 @@ static int __init eisa_irq_setup(char *str) EISA_DBG("IRQ setup\n"); while (cur != NULL) { char *pe; - + val = (int) simple_strtoul(cur, &pe, 0); if (val > 15 || val < 0) { printk(KERN_ERR "eisa: EISA irq value are 0-15\n"); continue; } - if (val == 2) { + if (val == 2) { val = 9; } eisa_make_irq_edge(val); /* clear the corresponding bit */ EISA_DBG("setting IRQ %d to edge-triggered mode\n", val); - + if ((cur = strchr(cur, ','))) { cur++; } else { diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c index ef31b77404ef..e2a3112f1c98 100644 --- a/drivers/parisc/power.c +++ b/drivers/parisc/power.c @@ -39,7 +39,7 @@ #include <linux/kernel.h> #include <linux/notifier.h> #include <linux/reboot.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/kthread.h> #include <linux/pm.h> diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 151b86b6d2e2..33385e574433 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -1069,7 +1069,7 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, } -static struct dma_map_ops sba_ops = { +static const struct dma_map_ops sba_ops = { .dma_supported = sba_dma_supported, .alloc = sba_alloc, .free = sba_free, diff --git a/drivers/parport/daisy.c b/drivers/parport/daisy.c index d998d0ed2bec..46eb15fb57ff 100644 --- a/drivers/parport/daisy.c +++ b/drivers/parport/daisy.c @@ -23,7 +23,7 @@ #include <linux/parport.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <asm/current.h> #include <linux/uaccess.h> diff --git a/drivers/parport/ieee1284.c b/drivers/parport/ieee1284.c index f9fd4b33a546..74cc6dd982d2 100644 --- a/drivers/parport/ieee1284.c +++ b/drivers/parport/ieee1284.c @@ -23,7 +23,7 @@ #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/timer.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #undef DEBUG /* undef me for production */ diff --git a/drivers/parport/ieee1284_ops.c b/drivers/parport/ieee1284_ops.c index c0e7d21c88c2..a959224d011b 100644 --- a/drivers/parport/ieee1284_ops.c +++ b/drivers/parport/ieee1284_ops.c @@ -17,7 +17,7 @@ #include <linux/module.h> #include <linux/parport.h> #include <linux/delay.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/uaccess.h> #undef DEBUG /* undef me for production */ @@ -307,7 +307,7 @@ size_t parport_ieee1284_read_byte (struct parport *port, if (parport_read_status (port) & PARPORT_STATUS_ERROR) { end_of_data: DPRINTK (KERN_DEBUG - "%s: No more byte data (%Zd bytes)\n", + "%s: No more byte data (%zd bytes)\n", port->name, count); /* Go to reverse idle phase. */ diff --git a/drivers/parport/parport_ip32.c b/drivers/parport/parport_ip32.c index 30e981be14c2..dcbeeb220dda 100644 --- a/drivers/parport/parport_ip32.c +++ b/drivers/parport/parport_ip32.c @@ -102,7 +102,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/parport.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/stddef.h> diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 78530d1714dc..9d42dfe65d44 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -44,7 +44,7 @@ #include <linux/module.h> #include <linux/init.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/interrupt.h> @@ -902,7 +902,7 @@ static size_t parport_pc_ecp_write_block_pio(struct parport *port, * ****************************************** */ -/* GCC is not inlining extern inline function later overwriten to non-inline, +/* GCC is not inlining extern inline function later overwritten to non-inline, so we use outlined_ variants here. */ static const struct parport_operations parport_pc_ops = { .write_data = parport_pc_write_data, diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 3308427ed9f7..bc090daa850a 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -27,7 +27,7 @@ #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/slab.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/kmod.h> #include <linux/device.h> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 6555eb78d91c..df141420c902 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -132,4 +132,5 @@ config PCI_HYPERV PCI devices from a PCI backend to support PCI driver domains. source "drivers/pci/hotplug/Kconfig" +source "drivers/pci/dwc/Kconfig" source "drivers/pci/host/Kconfig" diff --git a/drivers/pci/access.c b/drivers/pci/access.c index db239547fefd..8b7382705bf2 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -1,7 +1,7 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/module.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/ioport.h> #include <linux/wait.h> @@ -367,7 +367,7 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) static int pci_vpd_wait(struct pci_dev *dev) { struct pci_vpd *vpd = dev->vpd; - unsigned long timeout = jiffies + msecs_to_jiffies(50); + unsigned long timeout = jiffies + msecs_to_jiffies(125); unsigned long max_sleep = 16; u16 status; int ret; @@ -684,8 +684,9 @@ void pci_cfg_access_unlock(struct pci_dev *dev) WARN_ON(!dev->block_cfg_access); dev->block_cfg_access = 0; - wake_up_all(&pci_cfg_wait); raw_spin_unlock_irqrestore(&pci_lock, flags); + + wake_up_all(&pci_cfg_wait); } EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig new file mode 100644 index 000000000000..dfb8a69afc28 --- /dev/null +++ b/drivers/pci/dwc/Kconfig @@ -0,0 +1,132 @@ +menu "DesignWare PCI Core Support" + +config PCIE_DW + bool + +config PCIE_DW_HOST + bool + depends on PCI + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW + +config PCI_DRA7XX + bool "TI DRA7xx PCIe controller" + depends on PCI + depends on OF && HAS_IOMEM && TI_PIPE3 + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + help + Enables support for the PCIe controller in the DRA7xx SoC. There + are two instances of PCIe controller in DRA7xx. This controller can + act both as EP and RC. This reuses the Designware core. + +config PCIE_DW_PLAT + bool "Platform bus based DesignWare PCIe Controller" + depends on PCI + depends on PCI_MSI_IRQ_DOMAIN + select PCIE_DW_HOST + ---help--- + This selects the DesignWare PCIe controller support. Select this if + you have a PCIe controller on Platform bus. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + +config PCI_EXYNOS + bool "Samsung Exynos PCIe controller" + depends on PCI + depends on SOC_EXYNOS5440 + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + +config PCI_IMX6 + bool "Freescale i.MX6 PCIe controller" + depends on PCI + depends on SOC_IMX6Q + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + +config PCIE_SPEAR13XX + bool "STMicroelectronics SPEAr PCIe controller" + depends on PCI + depends on ARCH_SPEAR13XX + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + help + Say Y here if you want PCIe support on SPEAr13XX SoCs. + +config PCI_KEYSTONE + bool "TI Keystone PCIe controller" + depends on PCI + depends on ARCH_KEYSTONE + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + help + Say Y here if you want to enable PCI controller support on Keystone + SoCs. The PCI controller on Keystone is based on Designware hardware + and therefore the driver re-uses the Designware core functions to + implement the driver. + +config PCI_LAYERSCAPE + bool "Freescale Layerscape PCIe controller" + depends on PCI + depends on OF && (ARM || ARCH_LAYERSCAPE) + depends on PCI_MSI_IRQ_DOMAIN + select MFD_SYSCON + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support on Layerscape SoCs. + +config PCI_HISI + depends on OF && ARM64 + bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers" + depends on PCI + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support on HiSilicon + Hip05 and Hip06 SoCs + +config PCIE_QCOM + bool "Qualcomm PCIe controller" + depends on PCI + depends on ARCH_QCOM && OF + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + help + Say Y here to enable PCIe controller support on Qualcomm SoCs. The + PCIe controller uses the Designware core plus Qualcomm-specific + hardware wrappers. + +config PCIE_ARMADA_8K + bool "Marvell Armada-8K PCIe controller" + depends on PCI + depends on ARCH_MVEBU + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + help + Say Y here if you want to enable PCIe controller support on + Armada-8K SoCs. The PCIe controller on Armada-8K is based on + Designware hardware and therefore the driver re-uses the + Designware core functions to implement the driver. + +config PCIE_ARTPEC6 + bool "Axis ARTPEC-6 PCIe controller" + depends on PCI + depends on MACH_ARTPEC6 + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + help + Say Y here to enable PCIe controller support on Axis ARTPEC-6 + SoCs. This PCIe controller uses the DesignWare core. + +endmenu diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile new file mode 100644 index 000000000000..a2df13c28798 --- /dev/null +++ b/drivers/pci/dwc/Makefile @@ -0,0 +1,24 @@ +obj-$(CONFIG_PCIE_DW) += pcie-designware.o +obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o +obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o +obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o +obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o +obj-$(CONFIG_PCI_IMX6) += pci-imx6.o +obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o +obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o +obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o +obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o +obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o + +# The following drivers are for devices that use the generic ACPI +# pci_root.c driver but don't support standard ECAM config access. +# They contain MCFG quirks to replace the generic ECAM accessors with +# device-specific ones that are shared with the DT driver. + +# The ACPI driver is generic and should not require driver-specific +# config options to be enabled, so we always build these drivers on +# ARM64 and use internal ifdefs to only build the pieces we need +# depending on whether ACPI, the DT driver, or both are enabled. + +obj-$(CONFIG_ARM64) += pcie-hisi.o diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c index 9595fad63f6f..0984baff07e3 100644 --- a/drivers/pci/host/pci-dra7xx.c +++ b/drivers/pci/dwc/pci-dra7xx.c @@ -17,6 +17,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/of_gpio.h> +#include <linux/of_pci.h> #include <linux/pci.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> @@ -63,14 +64,18 @@ #define LINK_UP BIT(16) #define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF +#define EXP_CAP_ID_OFFSET 0x70 + struct dra7xx_pcie { - struct pcie_port pp; + struct dw_pcie *pci; void __iomem *base; /* DT ti_conf */ int phy_count; /* DT phy-names count */ struct phy **phy; + int link_gen; + struct irq_domain *irq_domain; }; -#define to_dra7xx_pcie(x) container_of((x), struct dra7xx_pcie, pp) +#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev) static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset) { @@ -83,9 +88,9 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, writel(value, pcie->base + offset); } -static int dra7xx_pcie_link_up(struct pcie_port *pp) +static int dra7xx_pcie_link_up(struct dw_pcie *pci) { - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); return !!(reg & LINK_UP); @@ -93,20 +98,41 @@ static int dra7xx_pcie_link_up(struct pcie_port *pp) static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx) { - struct pcie_port *pp = &dra7xx->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = dra7xx->pci; + struct device *dev = pci->dev; u32 reg; + u32 exp_cap_off = EXP_CAP_ID_OFFSET; - if (dw_pcie_link_up(pp)) { + if (dw_pcie_link_up(pci)) { dev_err(dev, "link is already up\n"); return 0; } + if (dra7xx->link_gen == 1) { + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP, + 4, ®); + if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { + reg &= ~((u32)PCI_EXP_LNKCAP_SLS); + reg |= PCI_EXP_LNKCAP_SLS_2_5GB; + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCAP, 4, reg); + } + + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2, + 2, ®); + if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { + reg &= ~((u32)PCI_EXP_LNKCAP_SLS); + reg |= PCI_EXP_LNKCAP_SLS_2_5GB; + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCTL2, 2, reg); + } + } + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); reg |= LTSSM_EN; dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); - return dw_pcie_wait_for_link(pp); + return dw_pcie_wait_for_link(pci); } static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) @@ -117,19 +143,14 @@ static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS); dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, ~LEG_EP_INTERRUPTS & ~MSI); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - dra7xx_pcie_writel(dra7xx, - PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, MSI); - else - dra7xx_pcie_writel(dra7xx, - PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, - LEG_EP_INTERRUPTS); + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, + MSI | LEG_EP_INTERRUPTS); } static void dra7xx_pcie_host_init(struct pcie_port *pp) { - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR; pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR; @@ -139,13 +160,11 @@ static void dra7xx_pcie_host_init(struct pcie_port *pp) dw_pcie_setup_rc(pp); dra7xx_pcie_establish_link(dra7xx); - if (IS_ENABLED(CONFIG_PCI_MSI)) - dw_pcie_msi_init(pp); + dw_pcie_msi_init(pp); dra7xx_pcie_enable_interrupts(dra7xx); } -static struct pcie_host_ops dra7xx_pcie_host_ops = { - .link_up = dra7xx_pcie_link_up, +static struct dw_pcie_host_ops dra7xx_pcie_host_ops = { .host_init = dra7xx_pcie_host_init, }; @@ -164,7 +183,9 @@ static const struct irq_domain_ops intx_domain_ops = { static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) { - struct device *dev = pp->dev; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); struct device_node *node = dev->of_node; struct device_node *pcie_intc_node = of_get_next_child(node, NULL); @@ -173,9 +194,9 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) return -ENODEV; } - pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4, - &intx_domain_ops, pp); - if (!pp->irq_domain) { + dra7xx->irq_domain = irq_domain_add_linear(pcie_intc_node, 4, + &intx_domain_ops, pp); + if (!dra7xx->irq_domain) { dev_err(dev, "Failed to get a INTx IRQ domain\n"); return -ENODEV; } @@ -186,7 +207,8 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) { struct dra7xx_pcie *dra7xx = arg; - struct pcie_port *pp = &dra7xx->pp; + struct dw_pcie *pci = dra7xx->pci; + struct pcie_port *pp = &pci->pp; u32 reg; reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); @@ -199,7 +221,8 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) case INTB: case INTC: case INTD: - generic_handle_irq(irq_find_mapping(pp->irq_domain, ffs(reg))); + generic_handle_irq(irq_find_mapping(dra7xx->irq_domain, + ffs(reg))); break; } @@ -212,7 +235,8 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) { struct dra7xx_pcie *dra7xx = arg; - struct device *dev = dra7xx->pp.dev; + struct dw_pcie *pci = dra7xx->pci; + struct device *dev = pci->dev; u32 reg; reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); @@ -267,8 +291,9 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, struct platform_device *pdev) { int ret; - struct pcie_port *pp = &dra7xx->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = dra7xx->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; struct resource *res; pp->irq = platform_get_irq(pdev, 1); @@ -285,15 +310,13 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, return ret; } - if (!IS_ENABLED(CONFIG_PCI_MSI)) { - ret = dra7xx_pcie_init_irq_domain(pp); - if (ret < 0) - return ret; - } + ret = dra7xx_pcie_init_irq_domain(pp); + if (ret < 0) + return ret; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics"); - pp->dbi_base = devm_ioremap(dev, res->start, resource_size(res)); - if (!pp->dbi_base) + pci->dbi_base = devm_ioremap(dev, res->start, resource_size(res)); + if (!pci->dbi_base) return -ENOMEM; ret = dw_pcie_host_init(pp); @@ -305,6 +328,49 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, return 0; } +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = dra7xx_pcie_link_up, +}; + +static void dra7xx_pcie_disable_phy(struct dra7xx_pcie *dra7xx) +{ + int phy_count = dra7xx->phy_count; + + while (phy_count--) { + phy_power_off(dra7xx->phy[phy_count]); + phy_exit(dra7xx->phy[phy_count]); + } +} + +static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx) +{ + int phy_count = dra7xx->phy_count; + int ret; + int i; + + for (i = 0; i < phy_count; i++) { + ret = phy_init(dra7xx->phy[i]); + if (ret < 0) + goto err_phy; + + ret = phy_power_on(dra7xx->phy[i]); + if (ret < 0) { + phy_exit(dra7xx->phy[i]); + goto err_phy; + } + } + + return 0; + +err_phy: + while (--i >= 0) { + phy_power_off(dra7xx->phy[i]); + phy_exit(dra7xx->phy[i]); + } + + return ret; +} + static int __init dra7xx_pcie_probe(struct platform_device *pdev) { u32 reg; @@ -315,21 +381,26 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) struct phy **phy; void __iomem *base; struct resource *res; - struct dra7xx_pcie *dra7xx; + struct dw_pcie *pci; struct pcie_port *pp; + struct dra7xx_pcie *dra7xx; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; char name[10]; - int gpio_sel; - enum of_gpio_flags flags; - unsigned long gpio_flags; + struct gpio_desc *reset; dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); if (!dra7xx) return -ENOMEM; - pp = &dra7xx->pp; - pp->dev = dev; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + pp = &pci->pp; pp->ops = &dra7xx_pcie_host_ops; irq = platform_get_irq(pdev, 0); @@ -365,22 +436,21 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) phy[i] = devm_phy_get(dev, name); if (IS_ERR(phy[i])) return PTR_ERR(phy[i]); - - ret = phy_init(phy[i]); - if (ret < 0) - goto err_phy; - - ret = phy_power_on(phy[i]); - if (ret < 0) { - phy_exit(phy[i]); - goto err_phy; - } } dra7xx->base = base; dra7xx->phy = phy; + dra7xx->pci = pci; dra7xx->phy_count = phy_count; + ret = dra7xx_pcie_enable_phy(dra7xx); + if (ret) { + dev_err(dev, "failed to enable phy\n"); + return ret; + } + + platform_set_drvdata(pdev, dra7xx); + pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { @@ -388,19 +458,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) goto err_get_sync; } - gpio_sel = of_get_gpio_flags(dev->of_node, 0, &flags); - if (gpio_is_valid(gpio_sel)) { - gpio_flags = (flags & OF_GPIO_ACTIVE_LOW) ? - GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; - ret = devm_gpio_request_one(dev, gpio_sel, gpio_flags, - "pcie_reset"); - if (ret) { - dev_err(dev, "gpio%d request failed, ret %d\n", - gpio_sel, ret); - goto err_gpio; - } - } else if (gpio_sel == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); + if (IS_ERR(reset)) { + ret = PTR_ERR(reset); + dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret); goto err_gpio; } @@ -408,11 +469,14 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) reg &= ~LTSSM_EN; dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); + dra7xx->link_gen = of_pci_get_max_link_speed(np); + if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2) + dra7xx->link_gen = 2; + ret = dra7xx_add_pcie_port(dra7xx, pdev); if (ret < 0) goto err_gpio; - platform_set_drvdata(pdev, dra7xx); return 0; err_gpio: @@ -420,12 +484,7 @@ err_gpio: err_get_sync: pm_runtime_disable(dev); - -err_phy: - while (--i >= 0) { - phy_power_off(phy[i]); - phy_exit(phy[i]); - } + dra7xx_pcie_disable_phy(dra7xx); return ret; } @@ -434,13 +493,13 @@ err_phy: static int dra7xx_pcie_suspend(struct device *dev) { struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - struct pcie_port *pp = &dra7xx->pp; + struct dw_pcie *pci = dra7xx->pci; u32 val; /* clear MSE */ - val = dw_pcie_readl_rc(pp, PCI_COMMAND); + val = dw_pcie_readl_dbi(pci, PCI_COMMAND); val &= ~PCI_COMMAND_MEMORY; - dw_pcie_writel_rc(pp, PCI_COMMAND, val); + dw_pcie_writel_dbi(pci, PCI_COMMAND, val); return 0; } @@ -448,13 +507,13 @@ static int dra7xx_pcie_suspend(struct device *dev) static int dra7xx_pcie_resume(struct device *dev) { struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - struct pcie_port *pp = &dra7xx->pp; + struct dw_pcie *pci = dra7xx->pci; u32 val; /* set MSE */ - val = dw_pcie_readl_rc(pp, PCI_COMMAND); + val = dw_pcie_readl_dbi(pci, PCI_COMMAND); val |= PCI_COMMAND_MEMORY; - dw_pcie_writel_rc(pp, PCI_COMMAND, val); + dw_pcie_writel_dbi(pci, PCI_COMMAND, val); return 0; } @@ -462,12 +521,8 @@ static int dra7xx_pcie_resume(struct device *dev) static int dra7xx_pcie_suspend_noirq(struct device *dev) { struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - int count = dra7xx->phy_count; - while (count--) { - phy_power_off(dra7xx->phy[count]); - phy_exit(dra7xx->phy[count]); - } + dra7xx_pcie_disable_phy(dra7xx); return 0; } @@ -475,31 +530,15 @@ static int dra7xx_pcie_suspend_noirq(struct device *dev) static int dra7xx_pcie_resume_noirq(struct device *dev) { struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); - int phy_count = dra7xx->phy_count; int ret; - int i; - - for (i = 0; i < phy_count; i++) { - ret = phy_init(dra7xx->phy[i]); - if (ret < 0) - goto err_phy; - ret = phy_power_on(dra7xx->phy[i]); - if (ret < 0) { - phy_exit(dra7xx->phy[i]); - goto err_phy; - } + ret = dra7xx_pcie_enable_phy(dra7xx); + if (ret) { + dev_err(dev, "failed to enable phy\n"); + return ret; } return 0; - -err_phy: - while (--i >= 0) { - phy_power_off(dra7xx->phy[i]); - phy_exit(dra7xx->phy[i]); - } - - return ret; } #endif diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c new file mode 100644 index 000000000000..993b650ef275 --- /dev/null +++ b/drivers/pci/dwc/pci-exynos.c @@ -0,0 +1,752 @@ +/* + * PCIe host controller driver for Samsung EXYNOS SoCs + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han <jg1.han@samsung.com> + * + * 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/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/resource.h> +#include <linux/signal.h> +#include <linux/types.h> + +#include "pcie-designware.h" + +#define to_exynos_pcie(x) dev_get_drvdata((x)->dev) + +/* PCIe ELBI registers */ +#define PCIE_IRQ_PULSE 0x000 +#define IRQ_INTA_ASSERT BIT(0) +#define IRQ_INTB_ASSERT BIT(2) +#define IRQ_INTC_ASSERT BIT(4) +#define IRQ_INTD_ASSERT BIT(6) +#define PCIE_IRQ_LEVEL 0x004 +#define PCIE_IRQ_SPECIAL 0x008 +#define PCIE_IRQ_EN_PULSE 0x00c +#define PCIE_IRQ_EN_LEVEL 0x010 +#define IRQ_MSI_ENABLE BIT(2) +#define PCIE_IRQ_EN_SPECIAL 0x014 +#define PCIE_PWR_RESET 0x018 +#define PCIE_CORE_RESET 0x01c +#define PCIE_CORE_RESET_ENABLE BIT(0) +#define PCIE_STICKY_RESET 0x020 +#define PCIE_NONSTICKY_RESET 0x024 +#define PCIE_APP_INIT_RESET 0x028 +#define PCIE_APP_LTSSM_ENABLE 0x02c +#define PCIE_ELBI_RDLH_LINKUP 0x064 +#define PCIE_ELBI_LTSSM_ENABLE 0x1 +#define PCIE_ELBI_SLV_AWMISC 0x11c +#define PCIE_ELBI_SLV_ARMISC 0x120 +#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21) + +/* PCIe Purple registers */ +#define PCIE_PHY_GLOBAL_RESET 0x000 +#define PCIE_PHY_COMMON_RESET 0x004 +#define PCIE_PHY_CMN_REG 0x008 +#define PCIE_PHY_MAC_RESET 0x00c +#define PCIE_PHY_PLL_LOCKED 0x010 +#define PCIE_PHY_TRSVREG_RESET 0x020 +#define PCIE_PHY_TRSV_RESET 0x024 + +/* PCIe PHY registers */ +#define PCIE_PHY_IMPEDANCE 0x004 +#define PCIE_PHY_PLL_DIV_0 0x008 +#define PCIE_PHY_PLL_BIAS 0x00c +#define PCIE_PHY_DCC_FEEDBACK 0x014 +#define PCIE_PHY_PLL_DIV_1 0x05c +#define PCIE_PHY_COMMON_POWER 0x064 +#define PCIE_PHY_COMMON_PD_CMN BIT(3) +#define PCIE_PHY_TRSV0_EMP_LVL 0x084 +#define PCIE_PHY_TRSV0_DRV_LVL 0x088 +#define PCIE_PHY_TRSV0_RXCDR 0x0ac +#define PCIE_PHY_TRSV0_POWER 0x0c4 +#define PCIE_PHY_TRSV0_PD_TSV BIT(7) +#define PCIE_PHY_TRSV0_LVCC 0x0dc +#define PCIE_PHY_TRSV1_EMP_LVL 0x144 +#define PCIE_PHY_TRSV1_RXCDR 0x16c +#define PCIE_PHY_TRSV1_POWER 0x184 +#define PCIE_PHY_TRSV1_PD_TSV BIT(7) +#define PCIE_PHY_TRSV1_LVCC 0x19c +#define PCIE_PHY_TRSV2_EMP_LVL 0x204 +#define PCIE_PHY_TRSV2_RXCDR 0x22c +#define PCIE_PHY_TRSV2_POWER 0x244 +#define PCIE_PHY_TRSV2_PD_TSV BIT(7) +#define PCIE_PHY_TRSV2_LVCC 0x25c +#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 +#define PCIE_PHY_TRSV3_RXCDR 0x2ec +#define PCIE_PHY_TRSV3_POWER 0x304 +#define PCIE_PHY_TRSV3_PD_TSV BIT(7) +#define PCIE_PHY_TRSV3_LVCC 0x31c + +struct exynos_pcie_mem_res { + void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */ + void __iomem *phy_base; /* DT 1st resource: PHY CTRL */ + void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */ +}; + +struct exynos_pcie_clk_res { + struct clk *clk; + struct clk *bus_clk; +}; + +struct exynos_pcie { + struct dw_pcie *pci; + struct exynos_pcie_mem_res *mem_res; + struct exynos_pcie_clk_res *clk_res; + const struct exynos_pcie_ops *ops; + int reset_gpio; + + /* For Generic PHY Framework */ + bool using_phy; + struct phy *phy; +}; + +struct exynos_pcie_ops { + int (*get_mem_resources)(struct platform_device *pdev, + struct exynos_pcie *ep); + int (*get_clk_resources)(struct exynos_pcie *ep); + int (*init_clk_resources)(struct exynos_pcie *ep); + void (*deinit_clk_resources)(struct exynos_pcie *ep); +}; + +static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev, + struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + struct resource *res; + + /* If using the PHY framework, doesn't need to get other resource */ + if (ep->using_phy) + return 0; + + ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL); + if (!ep->mem_res) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ep->mem_res->elbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ep->mem_res->elbi_base)) + return PTR_ERR(ep->mem_res->elbi_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ep->mem_res->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ep->mem_res->phy_base)) + return PTR_ERR(ep->mem_res->phy_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + ep->mem_res->block_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ep->mem_res->block_base)) + return PTR_ERR(ep->mem_res->block_base); + + return 0; +} + +static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + + ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL); + if (!ep->clk_res) + return -ENOMEM; + + ep->clk_res->clk = devm_clk_get(dev, "pcie"); + if (IS_ERR(ep->clk_res->clk)) { + dev_err(dev, "Failed to get pcie rc clock\n"); + return PTR_ERR(ep->clk_res->clk); + } + + ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus"); + if (IS_ERR(ep->clk_res->bus_clk)) { + dev_err(dev, "Failed to get pcie bus clock\n"); + return PTR_ERR(ep->clk_res->bus_clk); + } + + return 0; +} + +static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + int ret; + + ret = clk_prepare_enable(ep->clk_res->clk); + if (ret) { + dev_err(dev, "cannot enable pcie rc clock"); + return ret; + } + + ret = clk_prepare_enable(ep->clk_res->bus_clk); + if (ret) { + dev_err(dev, "cannot enable pcie bus clock"); + goto err_bus_clk; + } + + return 0; + +err_bus_clk: + clk_disable_unprepare(ep->clk_res->clk); + + return ret; +} + +static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep) +{ + clk_disable_unprepare(ep->clk_res->bus_clk); + clk_disable_unprepare(ep->clk_res->clk); +} + +static const struct exynos_pcie_ops exynos5440_pcie_ops = { + .get_mem_resources = exynos5440_pcie_get_mem_resources, + .get_clk_resources = exynos5440_pcie_get_clk_resources, + .init_clk_resources = exynos5440_pcie_init_clk_resources, + .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources, +}; + +static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg) +{ + writel(val, base + reg); +} + +static u32 exynos_pcie_readl(void __iomem *base, u32 reg) +{ + return readl(base + reg); +} + +static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC); + if (on) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC); +} + +static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC); + if (on) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC); +} + +static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); + val &= ~PCIE_CORE_RESET_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET); +} + +static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET); + val |= PCIE_CORE_RESET_ENABLE; + + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET); + exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET); +} + +static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep) +{ + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET); + exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET); +} + +static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep) +{ + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET); + exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET); + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET); + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG); + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET); + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET); +} + +static void exynos_pcie_power_on_phy(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER); + val &= ~PCIE_PHY_COMMON_PD_CMN; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER); + val &= ~PCIE_PHY_TRSV0_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER); + val &= ~PCIE_PHY_TRSV1_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER); + val &= ~PCIE_PHY_TRSV2_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER); + val &= ~PCIE_PHY_TRSV3_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER); +} + +static void exynos_pcie_power_off_phy(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER); + val |= PCIE_PHY_COMMON_PD_CMN; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER); + val |= PCIE_PHY_TRSV0_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER); + val |= PCIE_PHY_TRSV1_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER); + val |= PCIE_PHY_TRSV2_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER); + val |= PCIE_PHY_TRSV3_PD_TSV; + exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER); +} + +static void exynos_pcie_init_phy(struct exynos_pcie *ep) +{ + /* DCC feedback control off */ + exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK); + + /* set TX/RX impedance */ + exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE); + + /* set 50Mhz PHY clock */ + exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0); + exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1); + + /* set TX Differential output for lane 0 */ + exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); + + /* set TX Pre-emphasis Level Control for lane 0 to minimum */ + exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL); + + /* set RX clock and data recovery bandwidth */ + exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS); + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR); + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR); + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR); + exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR); + + /* change TX Pre-emphasis Level Control for lanes */ + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL); + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL); + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL); + exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL); + + /* set LVCC */ + exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC); + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC); + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC); + exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC); +} + +static void exynos_pcie_assert_reset(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct device *dev = pci->dev; + + if (ep->reset_gpio >= 0) + devm_gpio_request_one(dev, ep->reset_gpio, + GPIOF_OUT_INIT_HIGH, "RESET"); +} + +static int exynos_pcie_establish_link(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; + u32 val; + + if (dw_pcie_link_up(pci)) { + dev_err(dev, "Link already up\n"); + return 0; + } + + exynos_pcie_assert_core_reset(ep); + + if (ep->using_phy) { + phy_reset(ep->phy); + + exynos_pcie_writel(ep->mem_res->elbi_base, 1, + PCIE_PWR_RESET); + + phy_power_on(ep->phy); + phy_init(ep->phy); + } else { + exynos_pcie_assert_phy_reset(ep); + exynos_pcie_deassert_phy_reset(ep); + exynos_pcie_power_on_phy(ep); + exynos_pcie_init_phy(ep); + + /* pulse for common reset */ + exynos_pcie_writel(ep->mem_res->block_base, 1, + PCIE_PHY_COMMON_RESET); + udelay(500); + exynos_pcie_writel(ep->mem_res->block_base, 0, + PCIE_PHY_COMMON_RESET); + } + + /* pulse for common reset */ + exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_COMMON_RESET); + udelay(500); + exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET); + + exynos_pcie_deassert_core_reset(ep); + dw_pcie_setup_rc(pp); + exynos_pcie_assert_reset(ep); + + /* assert LTSSM enable */ + exynos_pcie_writel(ep->mem_res->elbi_base, PCIE_ELBI_LTSSM_ENABLE, + PCIE_APP_LTSSM_ENABLE); + + /* check if the link is up or not */ + if (!dw_pcie_wait_for_link(pci)) + return 0; + + if (ep->using_phy) { + phy_power_off(ep->phy); + return -ETIMEDOUT; + } + + while (exynos_pcie_readl(ep->mem_res->phy_base, + PCIE_PHY_PLL_LOCKED) == 0) { + val = exynos_pcie_readl(ep->mem_res->block_base, + PCIE_PHY_PLL_LOCKED); + dev_info(dev, "PLL Locked: 0x%x\n", val); + } + exynos_pcie_power_off_phy(ep); + return -ETIMEDOUT; +} + +static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep) +{ + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE); + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE); +} + +static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep) +{ + u32 val; + + /* enable INTX interrupt */ + val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | + IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE); +} + +static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) +{ + struct exynos_pcie *ep = arg; + + exynos_pcie_clear_irq_pulse(ep); + return IRQ_HANDLED; +} + +static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) +{ + struct exynos_pcie *ep = arg; + struct dw_pcie *pci = ep->pci; + struct pcie_port *pp = &pci->pp; + + return dw_handle_msi_irq(pp); +} + +static void exynos_pcie_msi_init(struct exynos_pcie *ep) +{ + struct dw_pcie *pci = ep->pci; + struct pcie_port *pp = &pci->pp; + u32 val; + + dw_pcie_msi_init(pp); + + /* enable MSI interrupt */ + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL); + val |= IRQ_MSI_ENABLE; + exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL); +} + +static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep) +{ + exynos_pcie_enable_irq_pulse(ep); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + exynos_pcie_msi_init(ep); +} + +static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) +{ + struct exynos_pcie *ep = to_exynos_pcie(pci); + u32 val; + + exynos_pcie_sideband_dbi_r_mode(ep, true); + val = readl(pci->dbi_base + reg); + exynos_pcie_sideband_dbi_r_mode(ep, false); + return val; +} + +static void exynos_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) +{ + struct exynos_pcie *ep = to_exynos_pcie(pci); + + exynos_pcie_sideband_dbi_w_mode(ep, true); + writel(val, pci->dbi_base + reg); + exynos_pcie_sideband_dbi_w_mode(ep, false); +} + +static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct exynos_pcie *ep = to_exynos_pcie(pci); + int ret; + + exynos_pcie_sideband_dbi_r_mode(ep, true); + ret = dw_pcie_read(pci->dbi_base + where, size, val); + exynos_pcie_sideband_dbi_r_mode(ep, false); + return ret; +} + +static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct exynos_pcie *ep = to_exynos_pcie(pci); + int ret; + + exynos_pcie_sideband_dbi_w_mode(ep, true); + ret = dw_pcie_write(pci->dbi_base + where, size, val); + exynos_pcie_sideband_dbi_w_mode(ep, false); + return ret; +} + +static int exynos_pcie_link_up(struct dw_pcie *pci) +{ + struct exynos_pcie *ep = to_exynos_pcie(pci); + u32 val; + + val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_RDLH_LINKUP); + if (val == PCIE_ELBI_LTSSM_ENABLE) + return 1; + + return 0; +} + +static void exynos_pcie_host_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct exynos_pcie *ep = to_exynos_pcie(pci); + + exynos_pcie_establish_link(ep); + exynos_pcie_enable_interrupts(ep); +} + +static struct dw_pcie_host_ops exynos_pcie_host_ops = { + .rd_own_conf = exynos_pcie_rd_own_conf, + .wr_own_conf = exynos_pcie_wr_own_conf, + .host_init = exynos_pcie_host_init, +}; + +static int __init exynos_add_pcie_port(struct exynos_pcie *ep, + struct platform_device *pdev) +{ + struct dw_pcie *pci = ep->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; + int ret; + + pp->irq = platform_get_irq(pdev, 1); + if (!pp->irq) { + dev_err(dev, "failed to get irq\n"); + return -ENODEV; + } + ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler, + IRQF_SHARED, "exynos-pcie", ep); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (!pp->msi_irq) { + dev_err(dev, "failed to get msi irq\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, pp->msi_irq, + exynos_pcie_msi_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, + "exynos-pcie", ep); + if (ret) { + dev_err(dev, "failed to request msi irq\n"); + return ret; + } + } + + pp->root_bus_nr = -1; + pp->ops = &exynos_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .readl_dbi = exynos_pcie_readl_dbi, + .writel_dbi = exynos_pcie_writel_dbi, + .link_up = exynos_pcie_link_up, +}; + +static int __init exynos_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie *pci; + struct exynos_pcie *ep; + struct device_node *np = dev->of_node; + int ret; + + ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + ep->pci = pci; + ep->ops = (const struct exynos_pcie_ops *) + of_device_get_match_data(dev); + + ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + + /* Assume that controller doesn't use the PHY framework */ + ep->using_phy = false; + + ep->phy = devm_of_phy_get(dev, np, NULL); + if (IS_ERR(ep->phy)) { + if (PTR_ERR(ep->phy) == -EPROBE_DEFER) + return PTR_ERR(ep->phy); + dev_warn(dev, "Use the 'phy' property. Current DT of pci-exynos was deprecated!!\n"); + } else + ep->using_phy = true; + + if (ep->ops && ep->ops->get_mem_resources) { + ret = ep->ops->get_mem_resources(pdev, ep); + if (ret) + return ret; + } + + if (ep->ops && ep->ops->get_clk_resources) { + ret = ep->ops->get_clk_resources(ep); + if (ret) + return ret; + ret = ep->ops->init_clk_resources(ep); + if (ret) + return ret; + } + + platform_set_drvdata(pdev, ep); + + ret = exynos_add_pcie_port(ep, pdev); + if (ret < 0) + goto fail_probe; + + return 0; + +fail_probe: + if (ep->using_phy) + phy_exit(ep->phy); + + if (ep->ops && ep->ops->deinit_clk_resources) + ep->ops->deinit_clk_resources(ep); + return ret; +} + +static int __exit exynos_pcie_remove(struct platform_device *pdev) +{ + struct exynos_pcie *ep = platform_get_drvdata(pdev); + + if (ep->ops && ep->ops->deinit_clk_resources) + ep->ops->deinit_clk_resources(ep); + + return 0; +} + +static const struct of_device_id exynos_pcie_of_match[] = { + { + .compatible = "samsung,exynos5440-pcie", + .data = &exynos5440_pcie_ops + }, + {}, +}; + +static struct platform_driver exynos_pcie_driver = { + .remove = __exit_p(exynos_pcie_remove), + .driver = { + .name = "exynos-pcie", + .of_match_table = exynos_pcie_of_match, + }, +}; + +/* Exynos PCIe driver does not allow module unload */ + +static int __init exynos_pcie_init(void) +{ + return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe); +} +subsys_initcall(exynos_pcie_init); diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c index c8cefb078218..801e46cd266d 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/dwc/pci-imx6.c @@ -30,7 +30,7 @@ #include "pcie-designware.h" -#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp) +#define to_imx6_pcie(x) dev_get_drvdata((x)->dev) enum imx6_pcie_variants { IMX6Q, @@ -39,7 +39,7 @@ enum imx6_pcie_variants { }; struct imx6_pcie { - struct pcie_port pp; /* pp.dbi_base is DT 0th resource */ + struct dw_pcie *pci; int reset_gpio; bool gpio_active_high; struct clk *pcie_bus; @@ -97,13 +97,13 @@ struct imx6_pcie { static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val) { - struct pcie_port *pp = &imx6_pcie->pp; + struct dw_pcie *pci = imx6_pcie->pci; u32 val; u32 max_iterations = 10; u32 wait_counter = 0; do { - val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT); + val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; wait_counter++; @@ -118,22 +118,22 @@ static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val) static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr) { - struct pcie_port *pp = &imx6_pcie->pp; + struct dw_pcie *pci = imx6_pcie->pci; u32 val; int ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val); return pcie_phy_poll_ack(imx6_pcie, 0); } @@ -141,7 +141,7 @@ static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr) /* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data) { - struct pcie_port *pp = &imx6_pcie->pp; + struct dw_pcie *pci = imx6_pcie->pci; u32 val, phy_ctl; int ret; @@ -151,24 +151,24 @@ static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data) /* assert Read signal */ phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, phy_ctl); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, phy_ctl); ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; - val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT); + val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT); *data = val & 0xffff; /* deassert Read signal */ - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x00); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00); return pcie_phy_poll_ack(imx6_pcie, 0); } static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) { - struct pcie_port *pp = &imx6_pcie->pp; + struct dw_pcie *pci = imx6_pcie->pci; u32 var; int ret; @@ -179,11 +179,11 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) return ret; var = data << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* capture data */ var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) @@ -191,7 +191,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) /* deassert cap data */ var = data << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* wait for ack de-assertion */ ret = pcie_phy_poll_ack(imx6_pcie, 0); @@ -200,7 +200,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) /* assert wr signal */ var = 0x1 << PCIE_PHY_CTRL_WR_LOC; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* wait for ack */ ret = pcie_phy_poll_ack(imx6_pcie, 1); @@ -209,14 +209,14 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) /* deassert wr signal */ var = data << PCIE_PHY_CTRL_DATA_LOC; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var); /* wait for ack de-assertion */ ret = pcie_phy_poll_ack(imx6_pcie, 0); if (ret) return ret; - dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x0); + dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0); return 0; } @@ -247,9 +247,6 @@ static int imx6q_pcie_abort_handler(unsigned long addr, static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { - struct pcie_port *pp = &imx6_pcie->pp; - u32 val, gpr1, gpr12; - switch (imx6_pcie->variant) { case IMX6SX: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, @@ -266,33 +263,6 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) IMX6Q_GPR1_PCIE_SW_RST); break; case IMX6Q: - /* - * If the bootloader already enabled the link we need some - * special handling to get the core back into a state where - * it is safe to touch it for configuration. As there is - * no dedicated reset signal wired up for MX6QDL, we need - * to manually force LTSSM into "detect" state before - * completely disabling LTSSM, which is a prerequisite for - * core configuration. - * - * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we - * have a strong indication that the bootloader activated - * the link. - */ - regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, &gpr1); - regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, &gpr12); - - if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) && - (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) { - val = dw_pcie_readl_rc(pp, PCIE_PL_PFLR); - val &= ~PCIE_PL_PFLR_LINK_STATE_MASK; - val |= PCIE_PL_PFLR_FORCE_LINK; - dw_pcie_writel_rc(pp, PCIE_PL_PFLR, val); - - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); - } - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18); regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, @@ -303,8 +273,8 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { - struct pcie_port *pp = &imx6_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; int ret = 0; switch (imx6_pcie->variant) { @@ -340,8 +310,8 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) { - struct pcie_port *pp = &imx6_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; int ret; ret = clk_prepare_enable(imx6_pcie->pcie_phy); @@ -440,28 +410,28 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) { - struct pcie_port *pp = &imx6_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; /* check if the link is up or not */ - if (!dw_pcie_wait_for_link(pp)) + if (!dw_pcie_wait_for_link(pci)) return 0; dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", - dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0), - dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1)); + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); return -ETIMEDOUT; } static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) { - struct pcie_port *pp = &imx6_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; u32 tmp; unsigned int retries; for (retries = 0; retries < 200; retries++) { - tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); /* Test if the speed change finished. */ if (!(tmp & PORT_LOGIC_SPEED_CHANGE)) return 0; @@ -475,15 +445,16 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg) { struct imx6_pcie *imx6_pcie = arg; - struct pcie_port *pp = &imx6_pcie->pp; + struct dw_pcie *pci = imx6_pcie->pci; + struct pcie_port *pp = &pci->pp; return dw_handle_msi_irq(pp); } static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) { - struct pcie_port *pp = &imx6_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = imx6_pcie->pci; + struct device *dev = pci->dev; u32 tmp; int ret; @@ -492,27 +463,25 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) * started in Gen2 mode, there is a possibility the devices on the * bus will not be detected at all. This happens with PCIe switches. */ - tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR); + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; - dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp); + dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); /* Start LTSSM. */ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); ret = imx6_pcie_wait_for_link(imx6_pcie); - if (ret) { - dev_info(dev, "Link never came up\n"); + if (ret) goto err_reset_phy; - } if (imx6_pcie->link_gen == 2) { /* Allow Gen2 mode after the link is up. */ - tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR); + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR); tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; - dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp); + dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); } else { dev_info(dev, "Link: Gen2 disabled\n"); } @@ -521,9 +490,9 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) * Start Directed Speed Change so the best possible speed both link * partners support can be negotiated. */ - tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); tmp |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); ret = imx6_pcie_wait_for_speed_change(imx6_pcie); if (ret) { @@ -538,21 +507,22 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) goto err_reset_phy; } - tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCSR); + tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR); dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); return 0; err_reset_phy: dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", - dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0), - dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1)); + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); imx6_pcie_reset_phy(imx6_pcie); return ret; } static void imx6_pcie_host_init(struct pcie_port *pp) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); imx6_pcie_assert_core_reset(imx6_pcie); imx6_pcie_init_phy(imx6_pcie); @@ -564,22 +534,22 @@ static void imx6_pcie_host_init(struct pcie_port *pp) dw_pcie_msi_init(pp); } -static int imx6_pcie_link_up(struct pcie_port *pp) +static int imx6_pcie_link_up(struct dw_pcie *pci) { - return dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1) & + return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; } -static struct pcie_host_ops imx6_pcie_host_ops = { - .link_up = imx6_pcie_link_up, +static struct dw_pcie_host_ops imx6_pcie_host_ops = { .host_init = imx6_pcie_host_init, }; static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, struct platform_device *pdev) { - struct pcie_port *pp = &imx6_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = imx6_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; int ret; if (IS_ENABLED(CONFIG_PCI_MSI)) { @@ -611,11 +581,15 @@ static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, return 0; } +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = imx6_pcie_link_up, +}; + static int __init imx6_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct dw_pcie *pci; struct imx6_pcie *imx6_pcie; - struct pcie_port *pp; struct resource *dbi_base; struct device_node *node = dev->of_node; int ret; @@ -624,9 +598,14 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) if (!imx6_pcie) return -ENOMEM; - pp = &imx6_pcie->pp; - pp->dev = dev; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + imx6_pcie->pci = pci; imx6_pcie->variant = (enum imx6_pcie_variants)of_device_get_match_data(dev); @@ -635,9 +614,9 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) "imprecise external abort"); dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pp->dbi_base = devm_ioremap_resource(dev, dbi_base); - if (IS_ERR(pp->dbi_base)) - return PTR_ERR(pp->dbi_base); + pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); /* Fetch GPIOs */ imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0); @@ -678,8 +657,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, "pcie_inbound_axi"); if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { - dev_err(dev, - "pcie_incbound_axi clock missing or invalid\n"); + dev_err(dev, "pcie_inbound_axi clock missing or invalid\n"); return PTR_ERR(imx6_pcie->pcie_inbound_axi); } } @@ -719,11 +697,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) if (ret) imx6_pcie->link_gen = 1; + platform_set_drvdata(pdev, imx6_pcie); + ret = imx6_add_pcie_port(imx6_pcie, pdev); if (ret < 0) return ret; - platform_set_drvdata(pdev, imx6_pcie); return 0; } diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c index 9397c4667106..6b396f6b4615 100644 --- a/drivers/pci/host/pci-keystone-dw.c +++ b/drivers/pci/dwc/pci-keystone-dw.c @@ -72,7 +72,7 @@ /* Config space registers */ #define DEBUG0 0x728 -#define to_keystone_pcie(x) container_of(x, struct keystone_pcie, pp) +#define to_keystone_pcie(x) dev_get_drvdata((x)->dev) static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset, u32 *bit_pos) @@ -83,7 +83,8 @@ static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset, phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp) { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); return ks_pcie->app.start + MSI_IRQ; } @@ -100,8 +101,9 @@ static void ks_dw_app_writel(struct keystone_pcie *ks_pcie, u32 offset, u32 val) void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset) { - struct pcie_port *pp = &ks_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; u32 pending, vector; int src, virq; @@ -128,10 +130,12 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d) struct keystone_pcie *ks_pcie; struct msi_desc *msi; struct pcie_port *pp; + struct dw_pcie *pci; msi = irq_data_get_msi_desc(d); pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); - ks_pcie = to_keystone_pcie(pp); + pci = to_dw_pcie_from_pp(pp); + ks_pcie = to_keystone_pcie(pci); offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); update_reg_offset_bit_pos(offset, ®_offset, &bit_pos); @@ -143,7 +147,8 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d) void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) { u32 reg_offset, bit_pos; - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_SET + (reg_offset << 4), @@ -153,7 +158,8 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) { u32 reg_offset, bit_pos; - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_CLR + (reg_offset << 4), @@ -165,11 +171,13 @@ static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) struct keystone_pcie *ks_pcie; struct msi_desc *msi; struct pcie_port *pp; + struct dw_pcie *pci; u32 offset; msi = irq_data_get_msi_desc(d); pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); - ks_pcie = to_keystone_pcie(pp); + pci = to_dw_pcie_from_pp(pp); + ks_pcie = to_keystone_pcie(pci); offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); /* Mask the end point if PVM implemented */ @@ -186,11 +194,13 @@ static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d) struct keystone_pcie *ks_pcie; struct msi_desc *msi; struct pcie_port *pp; + struct dw_pcie *pci; u32 offset; msi = irq_data_get_msi_desc(d); pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); - ks_pcie = to_keystone_pcie(pp); + pci = to_dw_pcie_from_pp(pp); + ks_pcie = to_keystone_pcie(pci); offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); /* Mask the end point if PVM implemented */ @@ -225,8 +235,9 @@ static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = { int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); - struct device *dev = pp->dev; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); + struct device *dev = pci->dev; int i; pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np, @@ -254,8 +265,8 @@ void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie) void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset) { - struct pcie_port *pp = &ks_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = ks_pcie->pci; + struct device *dev = pci->dev; u32 pending; int virq; @@ -285,7 +296,7 @@ irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie) return IRQ_NONE; if (status & ERR_FATAL_IRQ) - dev_err(ks_pcie->pp.dev, "fatal error (status %#010x)\n", + dev_err(ks_pcie->pci->dev, "fatal error (status %#010x)\n", status); /* Ack the IRQ; status bits are RW1C */ @@ -366,15 +377,16 @@ static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie) void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) { - struct pcie_port *pp = &ks_pcie->pp; + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; u32 start = pp->mem->start, end = pp->mem->end; int i, tr_size; u32 val; /* Disable BARs for inbound access */ ks_dw_pcie_set_dbi_mode(ks_pcie); - dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0); - dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0); ks_dw_pcie_clear_dbi_mode(ks_pcie); /* Set outbound translation size per window division */ @@ -415,11 +427,12 @@ static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus, unsigned int devfn) { u8 device = PCI_SLOT(devfn), function = PCI_FUNC(devfn); - struct pcie_port *pp = &ks_pcie->pp; + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; u32 regval; if (bus == 0) - return pp->dbi_base; + return pci->dbi_base; regval = (bus << 16) | (device << 8) | function; @@ -438,25 +451,27 @@ static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus, int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); u8 bus_num = bus->number; void __iomem *addr; addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - return dw_pcie_cfg_read(addr + where, size, val); + return dw_pcie_read(addr + where, size, val); } int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); u8 bus_num = bus->number; void __iomem *addr; addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn); - return dw_pcie_cfg_write(addr + where, size, val); + return dw_pcie_write(addr + where, size, val); } /** @@ -466,14 +481,15 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, */ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp) { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); /* Configure and set up BAR0 */ ks_dw_pcie_set_dbi_mode(ks_pcie); /* Enable BAR0 */ - dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 1); - dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, SZ_4K - 1); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 1); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, SZ_4K - 1); ks_dw_pcie_clear_dbi_mode(ks_pcie); @@ -481,17 +497,17 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp) * For BAR0, just setting bus address for inbound writes (MSI) should * be sufficient. Use physical address to avoid any conflicts. */ - dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, ks_pcie->app.start); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start); } /** * ks_dw_pcie_link_up() - Check if link up */ -int ks_dw_pcie_link_up(struct pcie_port *pp) +int ks_dw_pcie_link_up(struct dw_pcie *pci) { u32 val; - val = dw_pcie_readl_rc(pp, DEBUG0); + val = dw_pcie_readl_dbi(pci, DEBUG0); return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0; } @@ -519,22 +535,23 @@ void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie) int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, struct device_node *msi_intc_np) { - struct pcie_port *pp = &ks_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *res; /* Index 0 is the config reg. space address */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pp->dbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pp->dbi_base)) - return PTR_ERR(pp->dbi_base); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); /* * We set these same and is used in pcie rd/wr_other_conf * functions */ - pp->va_cfg0_base = pp->dbi_base + SPACE0_REMOTE_CFG_OFFSET; + pp->va_cfg0_base = pci->dbi_base + SPACE0_REMOTE_CFG_OFFSET; pp->va_cfg1_base = pp->va_cfg0_base; /* Index 1 is the application reg. space address */ diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c index 043c19a05da1..fcc9723bad6e 100644 --- a/drivers/pci/host/pci-keystone.c +++ b/drivers/pci/dwc/pci-keystone.c @@ -44,7 +44,7 @@ #define PCIE_RC_K2E 0xb009 #define PCIE_RC_K2L 0xb00a -#define to_keystone_pcie(x) container_of(x, struct keystone_pcie, pp) +#define to_keystone_pcie(x) dev_get_drvdata((x)->dev) static void quirk_limit_mrrs(struct pci_dev *dev) { @@ -88,13 +88,14 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs); static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) { - struct pcie_port *pp = &ks_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; unsigned int retries; dw_pcie_setup_rc(pp); - if (dw_pcie_link_up(pp)) { + if (dw_pcie_link_up(pci)) { dev_err(dev, "Link already up\n"); return 0; } @@ -102,7 +103,7 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) /* check if the link is up or not */ for (retries = 0; retries < 5; retries++) { ks_dw_pcie_initiate_link_train(ks_pcie); - if (!dw_pcie_wait_for_link(pp)) + if (!dw_pcie_wait_for_link(pci)) return 0; } @@ -115,8 +116,8 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc) unsigned int irq = irq_desc_get_irq(desc); struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); u32 offset = irq - ks_pcie->msi_host_irqs[0]; - struct pcie_port *pp = &ks_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = ks_pcie->pci; + struct device *dev = pci->dev; struct irq_chip *chip = irq_desc_get_chip(desc); dev_dbg(dev, "%s, irq %d\n", __func__, irq); @@ -143,8 +144,8 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); - struct pcie_port *pp = &ks_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = ks_pcie->pci; + struct device *dev = pci->dev; u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; struct irq_chip *chip = irq_desc_get_chip(desc); @@ -164,7 +165,7 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie, char *controller, int *num_irqs) { int temp, max_host_irqs, legacy = 1, *host_irqs; - struct device *dev = ks_pcie->pp.dev; + struct device *dev = ks_pcie->pci->dev; struct device_node *np_pcie = dev->of_node, **np_temp; if (!strcmp(controller, "msi-interrupt-controller")) @@ -262,24 +263,25 @@ static int keystone_pcie_fault(unsigned long addr, unsigned int fsr, static void __init ks_pcie_host_init(struct pcie_port *pp) { - struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); u32 val; ks_pcie_establish_link(ks_pcie); ks_dw_pcie_setup_rc_app_regs(ks_pcie); ks_pcie_setup_interrupts(ks_pcie); writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8), - pp->dbi_base + PCI_IO_BASE); + pci->dbi_base + PCI_IO_BASE); /* update the Vendor ID */ - writew(ks_pcie->device_id, pp->dbi_base + PCI_DEVICE_ID); + writew(ks_pcie->device_id, pci->dbi_base + PCI_DEVICE_ID); /* update the DEV_STAT_CTRL to publish right mrrs */ - val = readl(pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); + val = readl(pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); val &= ~PCI_EXP_DEVCTL_READRQ; /* set the mrrs to 256 bytes */ val |= BIT(12); - writel(val, pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); + writel(val, pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL); /* * PCIe access errors that result into OCP errors are caught by ARM as @@ -289,10 +291,9 @@ static void __init ks_pcie_host_init(struct pcie_port *pp) "Asynchronous external abort"); } -static struct pcie_host_ops keystone_pcie_host_ops = { +static struct dw_pcie_host_ops keystone_pcie_host_ops = { .rd_other_conf = ks_dw_pcie_rd_other_conf, .wr_other_conf = ks_dw_pcie_wr_other_conf, - .link_up = ks_dw_pcie_link_up, .host_init = ks_pcie_host_init, .msi_set_irq = ks_dw_pcie_msi_set_irq, .msi_clear_irq = ks_dw_pcie_msi_clear_irq, @@ -311,8 +312,9 @@ static irqreturn_t pcie_err_irq_handler(int irq, void *priv) static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, struct platform_device *pdev) { - struct pcie_port *pp = &ks_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = ks_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; int ret; ret = ks_pcie_get_irq_controller_info(ks_pcie, @@ -365,6 +367,10 @@ static const struct of_device_id ks_pcie_of_match[] = { { }, }; +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = ks_dw_pcie_link_up, +}; + static int __exit ks_pcie_remove(struct platform_device *pdev) { struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); @@ -377,8 +383,8 @@ static int __exit ks_pcie_remove(struct platform_device *pdev) static int __init ks_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct dw_pcie *pci; struct keystone_pcie *ks_pcie; - struct pcie_port *pp; struct resource *res; void __iomem *reg_p; struct phy *phy; @@ -388,8 +394,14 @@ static int __init ks_pcie_probe(struct platform_device *pdev) if (!ks_pcie) return -ENOMEM; - pp = &ks_pcie->pp; - pp->dev = dev; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + ks_pcie->pci = pci; /* initialize SerDes Phy if present */ phy = devm_phy_get(dev, "pcie-phy"); @@ -422,6 +434,8 @@ static int __init ks_pcie_probe(struct platform_device *pdev) if (ret) return ret; + platform_set_drvdata(pdev, ks_pcie); + ret = ks_add_pcie_port(ks_pcie, pdev); if (ret < 0) goto fail_clk; diff --git a/drivers/pci/host/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h index bc54bafda068..74c5825882df 100644 --- a/drivers/pci/host/pci-keystone.h +++ b/drivers/pci/dwc/pci-keystone.h @@ -17,7 +17,7 @@ #define MAX_LEGACY_HOST_IRQS 4 struct keystone_pcie { - struct pcie_port pp; /* pp.dbi_base is DT 0th res */ + struct dw_pcie *pci; struct clk *clk; /* PCI Device ID */ u32 device_id; @@ -54,10 +54,10 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie); -int ks_dw_pcie_link_up(struct pcie_port *pp); void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie); void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip); +int ks_dw_pcie_link_up(struct dw_pcie *pci); diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c index ea789138531b..c32e392a0ae6 100644 --- a/drivers/pci/host/pci-layerscape.c +++ b/drivers/pci/dwc/pci-layerscape.c @@ -39,24 +39,26 @@ struct ls_pcie_drvdata { u32 lut_offset; u32 ltssm_shift; u32 lut_dbg; - struct pcie_host_ops *ops; + struct dw_pcie_host_ops *ops; + const struct dw_pcie_ops *dw_pcie_ops; }; struct ls_pcie { - struct pcie_port pp; /* pp.dbi_base is DT regs */ + struct dw_pcie *pci; void __iomem *lut; struct regmap *scfg; const struct ls_pcie_drvdata *drvdata; int index; }; -#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) +#define to_ls_pcie(x) dev_get_drvdata((x)->dev) static bool ls_pcie_is_bridge(struct ls_pcie *pcie) { + struct dw_pcie *pci = pcie->pci; u32 header_type; - header_type = ioread8(pcie->pp.dbi_base + PCI_HEADER_TYPE); + header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE); header_type &= 0x7f; return header_type == PCI_HEADER_TYPE_BRIDGE; @@ -65,29 +67,34 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie) /* Clear multi-function bit */ static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) { - iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->pp.dbi_base + PCI_HEADER_TYPE); + struct dw_pcie *pci = pcie->pci; + + iowrite8(PCI_HEADER_TYPE_BRIDGE, pci->dbi_base + PCI_HEADER_TYPE); } /* Fix class value */ static void ls_pcie_fix_class(struct ls_pcie *pcie) { - iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->pp.dbi_base + PCI_CLASS_DEVICE); + struct dw_pcie *pci = pcie->pci; + + iowrite16(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE); } /* Drop MSG TLP except for Vendor MSG */ static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) { u32 val; + struct dw_pcie *pci = pcie->pci; - val = ioread32(pcie->pp.dbi_base + PCIE_STRFMR1); + val = ioread32(pci->dbi_base + PCIE_STRFMR1); val &= 0xDFFFFFFF; - iowrite32(val, pcie->pp.dbi_base + PCIE_STRFMR1); + iowrite32(val, pci->dbi_base + PCIE_STRFMR1); } -static int ls1021_pcie_link_up(struct pcie_port *pp) +static int ls1021_pcie_link_up(struct dw_pcie *pci) { u32 state; - struct ls_pcie *pcie = to_ls_pcie(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); if (!pcie->scfg) return 0; @@ -103,8 +110,9 @@ static int ls1021_pcie_link_up(struct pcie_port *pp) static void ls1021_pcie_host_init(struct pcie_port *pp) { - struct device *dev = pp->dev; - struct ls_pcie *pcie = to_ls_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + struct device *dev = pci->dev; u32 index[2]; pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, @@ -127,9 +135,9 @@ static void ls1021_pcie_host_init(struct pcie_port *pp) ls_pcie_drop_msg_tlp(pcie); } -static int ls_pcie_link_up(struct pcie_port *pp) +static int ls_pcie_link_up(struct dw_pcie *pci) { - struct ls_pcie *pcie = to_ls_pcie(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); u32 state; state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >> @@ -144,19 +152,21 @@ static int ls_pcie_link_up(struct pcie_port *pp) static void ls_pcie_host_init(struct pcie_port *pp) { - struct ls_pcie *pcie = to_ls_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); - iowrite32(1, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN); + iowrite32(1, pci->dbi_base + PCIE_DBI_RO_WR_EN); ls_pcie_fix_class(pcie); ls_pcie_clear_multifunction(pcie); ls_pcie_drop_msg_tlp(pcie); - iowrite32(0, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN); + iowrite32(0, pci->dbi_base + PCIE_DBI_RO_WR_EN); } static int ls_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) { - struct device *dev = pp->dev; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; struct device_node *np = dev->of_node; struct device_node *msi_node; @@ -175,20 +185,27 @@ static int ls_pcie_msi_host_init(struct pcie_port *pp, return 0; } -static struct pcie_host_ops ls1021_pcie_host_ops = { - .link_up = ls1021_pcie_link_up, +static struct dw_pcie_host_ops ls1021_pcie_host_ops = { .host_init = ls1021_pcie_host_init, .msi_host_init = ls_pcie_msi_host_init, }; -static struct pcie_host_ops ls_pcie_host_ops = { - .link_up = ls_pcie_link_up, +static struct dw_pcie_host_ops ls_pcie_host_ops = { .host_init = ls_pcie_host_init, .msi_host_init = ls_pcie_msi_host_init, }; +static const struct dw_pcie_ops dw_ls1021_pcie_ops = { + .link_up = ls1021_pcie_link_up, +}; + +static const struct dw_pcie_ops dw_ls_pcie_ops = { + .link_up = ls_pcie_link_up, +}; + static struct ls_pcie_drvdata ls1021_drvdata = { .ops = &ls1021_pcie_host_ops, + .dw_pcie_ops = &dw_ls1021_pcie_ops, }; static struct ls_pcie_drvdata ls1043_drvdata = { @@ -196,6 +213,7 @@ static struct ls_pcie_drvdata ls1043_drvdata = { .ltssm_shift = 24, .lut_dbg = 0x7fc, .ops = &ls_pcie_host_ops, + .dw_pcie_ops = &dw_ls_pcie_ops, }; static struct ls_pcie_drvdata ls1046_drvdata = { @@ -203,6 +221,7 @@ static struct ls_pcie_drvdata ls1046_drvdata = { .ltssm_shift = 24, .lut_dbg = 0x407fc, .ops = &ls_pcie_host_ops, + .dw_pcie_ops = &dw_ls_pcie_ops, }; static struct ls_pcie_drvdata ls2080_drvdata = { @@ -210,6 +229,7 @@ static struct ls_pcie_drvdata ls2080_drvdata = { .ltssm_shift = 0, .lut_dbg = 0x7fc, .ops = &ls_pcie_host_ops, + .dw_pcie_ops = &dw_ls_pcie_ops, }; static const struct of_device_id ls_pcie_of_match[] = { @@ -223,10 +243,13 @@ static const struct of_device_id ls_pcie_of_match[] = { static int __init ls_add_pcie_port(struct ls_pcie *pcie) { - struct pcie_port *pp = &pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; int ret; + pp->ops = pcie->drvdata->ops; + ret = dw_pcie_host_init(pp); if (ret) { dev_err(dev, "failed to initialize host\n"); @@ -239,35 +262,38 @@ static int __init ls_add_pcie_port(struct ls_pcie *pcie) static int __init ls_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *match; + struct dw_pcie *pci; struct ls_pcie *pcie; - struct pcie_port *pp; struct resource *dbi_base; int ret; - match = of_match_device(ls_pcie_of_match, dev); - if (!match) - return -ENODEV; - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; - pp = &pcie->pp; - pp->dev = dev; - pcie->drvdata = match->data; - pp->ops = pcie->drvdata->ops; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pcie->drvdata = of_device_get_match_data(dev); + + pci->dev = dev; + pci->ops = pcie->drvdata->dw_pcie_ops; + + pcie->pci = pci; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - pcie->pp.dbi_base = devm_ioremap_resource(dev, dbi_base); - if (IS_ERR(pcie->pp.dbi_base)) - return PTR_ERR(pcie->pp.dbi_base); + pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); - pcie->lut = pcie->pp.dbi_base + pcie->drvdata->lut_offset; + pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset; if (!ls_pcie_is_bridge(pcie)) return -ENODEV; + platform_set_drvdata(pdev, pcie); + ret = ls_add_pcie_port(pcie); if (ret < 0) return ret; diff --git a/drivers/pci/host/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c index 0ac0f18690f2..f110e3b24a26 100644 --- a/drivers/pci/host/pcie-armada8k.c +++ b/drivers/pci/dwc/pcie-armada8k.c @@ -29,7 +29,7 @@ #include "pcie-designware.h" struct armada8k_pcie { - struct pcie_port pp; /* pp.dbi_base is DT ctrl */ + struct dw_pcie *pci; struct clk *clk; }; @@ -67,76 +67,77 @@ struct armada8k_pcie { #define AX_USER_DOMAIN_MASK 0x3 #define AX_USER_DOMAIN_SHIFT 4 -#define to_armada8k_pcie(x) container_of(x, struct armada8k_pcie, pp) +#define to_armada8k_pcie(x) dev_get_drvdata((x)->dev) -static int armada8k_pcie_link_up(struct pcie_port *pp) +static int armada8k_pcie_link_up(struct dw_pcie *pci) { u32 reg; u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP; - reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_STATUS_REG); + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG); if ((reg & mask) == mask) return 1; - dev_dbg(pp->dev, "No link detected (Global-Status: 0x%08x).\n", reg); + dev_dbg(pci->dev, "No link detected (Global-Status: 0x%08x).\n", reg); return 0; } static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie) { - struct pcie_port *pp = &pcie->pp; + struct dw_pcie *pci = pcie->pci; u32 reg; - if (!dw_pcie_link_up(pp)) { + if (!dw_pcie_link_up(pci)) { /* Disable LTSSM state machine to enable configuration */ - reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG); + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); reg &= ~(PCIE_APP_LTSSM_EN); - dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg); + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); } /* Set the device to root complex mode */ - reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG); + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT); reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT; - dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg); + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); /* Set the PCIe master AxCache attributes */ - dw_pcie_writel_rc(pp, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); - dw_pcie_writel_rc(pp, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); + dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); + dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); /* Set the PCIe master AxDomain attributes */ - reg = dw_pcie_readl_rc(pp, PCIE_ARUSER_REG); + reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG); reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - dw_pcie_writel_rc(pp, PCIE_ARUSER_REG, reg); + dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg); - reg = dw_pcie_readl_rc(pp, PCIE_AWUSER_REG); + reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG); reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - dw_pcie_writel_rc(pp, PCIE_AWUSER_REG, reg); + dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg); /* Enable INT A-D interrupts */ - reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_MASK1_REG); + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG); reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK | PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK; - dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_MASK1_REG, reg); + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg); - if (!dw_pcie_link_up(pp)) { + if (!dw_pcie_link_up(pci)) { /* Configuration done. Start LTSSM */ - reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG); + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); reg |= PCIE_APP_LTSSM_EN; - dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg); + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); } /* Wait until the link becomes active again */ - if (dw_pcie_wait_for_link(pp)) - dev_err(pp->dev, "Link not up after reconfiguration\n"); + if (dw_pcie_wait_for_link(pci)) + dev_err(pci->dev, "Link not up after reconfiguration\n"); } static void armada8k_pcie_host_init(struct pcie_port *pp) { - struct armada8k_pcie *pcie = to_armada8k_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct armada8k_pcie *pcie = to_armada8k_pcie(pci); dw_pcie_setup_rc(pp); armada8k_pcie_establish_link(pcie); @@ -145,7 +146,7 @@ static void armada8k_pcie_host_init(struct pcie_port *pp) static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) { struct armada8k_pcie *pcie = arg; - struct pcie_port *pp = &pcie->pp; + struct dw_pcie *pci = pcie->pci; u32 val; /* @@ -153,21 +154,21 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) * PCI device. However, they are also latched into the PCIe * controller, so we simply discard them. */ - val = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG); - dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG, val); + val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG); + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val); return IRQ_HANDLED; } -static struct pcie_host_ops armada8k_pcie_host_ops = { - .link_up = armada8k_pcie_link_up, +static struct dw_pcie_host_ops armada8k_pcie_host_ops = { .host_init = armada8k_pcie_host_init, }; static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, struct platform_device *pdev) { - struct pcie_port *pp = &pcie->pp; + struct dw_pcie *pci = pcie->pci; + struct pcie_port *pp = &pci->pp; struct device *dev = &pdev->dev; int ret; @@ -196,10 +197,14 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, return 0; } +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = armada8k_pcie_link_up, +}; + static int armada8k_pcie_probe(struct platform_device *pdev) { + struct dw_pcie *pci; struct armada8k_pcie *pcie; - struct pcie_port *pp; struct device *dev = &pdev->dev; struct resource *base; int ret; @@ -208,24 +213,32 @@ static int armada8k_pcie_probe(struct platform_device *pdev) if (!pcie) return -ENOMEM; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + pcie->pci = pci; + pcie->clk = devm_clk_get(dev, NULL); if (IS_ERR(pcie->clk)) return PTR_ERR(pcie->clk); clk_prepare_enable(pcie->clk); - pp = &pcie->pp; - pp->dev = dev; - /* Get the dw-pcie unit configuration/control registers base. */ base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - pp->dbi_base = devm_ioremap_resource(dev, base); - if (IS_ERR(pp->dbi_base)) { + pci->dbi_base = devm_ioremap_resource(dev, base); + if (IS_ERR(pci->dbi_base)) { dev_err(dev, "couldn't remap regs base %p\n", base); - ret = PTR_ERR(pp->dbi_base); + ret = PTR_ERR(pci->dbi_base); goto fail; } + platform_set_drvdata(pdev, pcie); + ret = armada8k_add_pcie_port(pcie, pdev); if (ret) goto fail; diff --git a/drivers/pci/host/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 212786b27f1a..fcd3ef845883 100644 --- a/drivers/pci/host/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -24,10 +24,10 @@ #include "pcie-designware.h" -#define to_artpec6_pcie(x) container_of(x, struct artpec6_pcie, pp) +#define to_artpec6_pcie(x) dev_get_drvdata((x)->dev) struct artpec6_pcie { - struct pcie_port pp; /* pp.dbi_base is DT dbi */ + struct dw_pcie *pci; struct regmap *regmap; /* DT axis,syscon-pcie */ void __iomem *phy_base; /* DT phy */ }; @@ -80,7 +80,8 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie) { - struct pcie_port *pp = &artpec6_pcie->pp; + struct dw_pcie *pci = artpec6_pcie->pci; + struct pcie_port *pp = &pci->pp; u32 val; unsigned int retries; @@ -139,7 +140,7 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie) * Enable writing to config regs. This is required as the Synopsys * driver changes the class code. That register needs DBI write enable. */ - dw_pcie_writel_rc(pp, MISC_CONTROL_1_OFF, DBI_RO_WR_EN); + dw_pcie_writel_dbi(pci, MISC_CONTROL_1_OFF, DBI_RO_WR_EN); pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR; pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR; @@ -155,19 +156,20 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie) artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); /* check if the link is up or not */ - if (!dw_pcie_wait_for_link(pp)) + if (!dw_pcie_wait_for_link(pci)) return 0; - dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", - dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0), - dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1)); + dev_dbg(pci->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1)); return -ETIMEDOUT; } static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie) { - struct pcie_port *pp = &artpec6_pcie->pp; + struct dw_pcie *pci = artpec6_pcie->pci; + struct pcie_port *pp = &pci->pp; if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); @@ -175,20 +177,22 @@ static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie) static void artpec6_pcie_host_init(struct pcie_port *pp) { - struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); artpec6_pcie_establish_link(artpec6_pcie); artpec6_pcie_enable_interrupts(artpec6_pcie); } -static struct pcie_host_ops artpec6_pcie_host_ops = { +static struct dw_pcie_host_ops artpec6_pcie_host_ops = { .host_init = artpec6_pcie_host_init, }; static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg) { struct artpec6_pcie *artpec6_pcie = arg; - struct pcie_port *pp = &artpec6_pcie->pp; + struct dw_pcie *pci = artpec6_pcie->pci; + struct pcie_port *pp = &pci->pp; return dw_handle_msi_irq(pp); } @@ -196,8 +200,9 @@ static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg) static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, struct platform_device *pdev) { - struct pcie_port *pp = &artpec6_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = artpec6_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = pci->dev; int ret; if (IS_ENABLED(CONFIG_PCI_MSI)) { @@ -232,8 +237,8 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, static int artpec6_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct dw_pcie *pci; struct artpec6_pcie *artpec6_pcie; - struct pcie_port *pp; struct resource *dbi_base; struct resource *phy_base; int ret; @@ -242,13 +247,18 @@ static int artpec6_pcie_probe(struct platform_device *pdev) if (!artpec6_pcie) return -ENOMEM; - pp = &artpec6_pcie->pp; - pp->dev = dev; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + + artpec6_pcie->pci = pci; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pp->dbi_base = devm_ioremap_resource(dev, dbi_base); - if (IS_ERR(pp->dbi_base)) - return PTR_ERR(pp->dbi_base); + pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base); @@ -261,6 +271,8 @@ static int artpec6_pcie_probe(struct platform_device *pdev) if (IS_ERR(artpec6_pcie->regmap)) return PTR_ERR(artpec6_pcie->regmap); + platform_set_drvdata(pdev, artpec6_pcie); + ret = artpec6_add_pcie_port(artpec6_pcie, pdev); if (ret < 0) return ret; diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/dwc/pcie-designware-host.c index af8f6e92e885..5ba334938b52 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -11,239 +11,38 @@ * published by the Free Software Foundation. */ -#include <linux/irq.h> #include <linux/irqdomain.h> -#include <linux/kernel.h> -#include <linux/msi.h> #include <linux/of_address.h> #include <linux/of_pci.h> -#include <linux/pci.h> #include <linux/pci_regs.h> #include <linux/platform_device.h> -#include <linux/types.h> -#include <linux/delay.h> #include "pcie-designware.h" -/* Parameters for the waiting for link up routine */ -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_USLEEP_MIN 90000 -#define LINK_WAIT_USLEEP_MAX 100000 - -/* Parameters for the waiting for iATU enabled routine */ -#define LINK_WAIT_MAX_IATU_RETRIES 5 -#define LINK_WAIT_IATU_MIN 9000 -#define LINK_WAIT_IATU_MAX 10000 - -/* Synopsys-specific PCIe configuration registers */ -#define PCIE_PORT_LINK_CONTROL 0x710 -#define PORT_LINK_MODE_MASK (0x3f << 16) -#define PORT_LINK_MODE_1_LANES (0x1 << 16) -#define PORT_LINK_MODE_2_LANES (0x3 << 16) -#define PORT_LINK_MODE_4_LANES (0x7 << 16) -#define PORT_LINK_MODE_8_LANES (0xf << 16) - -#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C -#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) -#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8) -#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) -#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) -#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) -#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8) - -#define PCIE_MSI_ADDR_LO 0x820 -#define PCIE_MSI_ADDR_HI 0x824 -#define PCIE_MSI_INTR0_ENABLE 0x828 -#define PCIE_MSI_INTR0_MASK 0x82C -#define PCIE_MSI_INTR0_STATUS 0x830 - -#define PCIE_ATU_VIEWPORT 0x900 -#define PCIE_ATU_REGION_INBOUND (0x1 << 31) -#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) -#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 - -/* - * iATU Unroll-specific register definitions - * From 4.80 core version the address translation will be made by unroll - */ -#define PCIE_ATU_UNR_REGION_CTRL1 0x00 -#define PCIE_ATU_UNR_REGION_CTRL2 0x04 -#define PCIE_ATU_UNR_LOWER_BASE 0x08 -#define PCIE_ATU_UNR_UPPER_BASE 0x0C -#define PCIE_ATU_UNR_LIMIT 0x10 -#define PCIE_ATU_UNR_LOWER_TARGET 0x14 -#define PCIE_ATU_UNR_UPPER_TARGET 0x18 - -/* Register address builder */ -#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((0x3 << 20) | (region << 9)) - -/* PCIe Port Logic registers */ -#define PLR_OFFSET 0x700 -#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) -#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4) -#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29) - static struct pci_ops dw_pcie_ops; -int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val) -{ - if ((uintptr_t)addr & (size - 1)) { - *val = 0; - return PCIBIOS_BAD_REGISTER_NUMBER; - } - - if (size == 4) - *val = readl(addr); - else if (size == 2) - *val = readw(addr); - else if (size == 1) - *val = readb(addr); - else { - *val = 0; - return PCIBIOS_BAD_REGISTER_NUMBER; - } - - return PCIBIOS_SUCCESSFUL; -} - -int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val) -{ - if ((uintptr_t)addr & (size - 1)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if (size == 4) - writel(val, addr); - else if (size == 2) - writew(val, addr); - else if (size == 1) - writeb(val, addr); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - return PCIBIOS_SUCCESSFUL; -} - -u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg) -{ - if (pp->ops->readl_rc) - return pp->ops->readl_rc(pp, reg); - - return readl(pp->dbi_base + reg); -} - -void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val) -{ - if (pp->ops->writel_rc) - pp->ops->writel_rc(pp, reg, val); - else - writel(val, pp->dbi_base + reg); -} - -static u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - - return dw_pcie_readl_rc(pp, offset + reg); -} - -static void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index, u32 reg, - u32 val) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - - dw_pcie_writel_rc(pp, offset + reg, val); -} - static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) { + struct dw_pcie *pci; + if (pp->ops->rd_own_conf) return pp->ops->rd_own_conf(pp, where, size, val); - return dw_pcie_cfg_read(pp->dbi_base + where, size, val); + pci = to_dw_pcie_from_pp(pp); + return dw_pcie_read(pci->dbi_base + where, size, val); } static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val) { + struct dw_pcie *pci; + if (pp->ops->wr_own_conf) return pp->ops->wr_own_conf(pp, where, size, val); - return dw_pcie_cfg_write(pp->dbi_base + where, size, val); -} - -static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index, - int type, u64 cpu_addr, u64 pci_addr, u32 size) -{ - u32 retries, val; - - if (pp->iatu_unroll_enabled) { - dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_BASE, - lower_32_bits(cpu_addr)); - dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_BASE, - upper_32_bits(cpu_addr)); - dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LIMIT, - lower_32_bits(cpu_addr + size - 1)); - dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_TARGET, - upper_32_bits(pci_addr)); - dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL1, - type); - dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL2, - PCIE_ATU_ENABLE); - } else { - dw_pcie_writel_rc(pp, PCIE_ATU_VIEWPORT, - PCIE_ATU_REGION_OUTBOUND | index); - dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_BASE, - lower_32_bits(cpu_addr)); - dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_BASE, - upper_32_bits(cpu_addr)); - dw_pcie_writel_rc(pp, PCIE_ATU_LIMIT, - lower_32_bits(cpu_addr + size - 1)); - dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_TARGET, - upper_32_bits(pci_addr)); - dw_pcie_writel_rc(pp, PCIE_ATU_CR1, type); - dw_pcie_writel_rc(pp, PCIE_ATU_CR2, PCIE_ATU_ENABLE); - } - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - if (pp->iatu_unroll_enabled) - val = dw_pcie_readl_unroll(pp, index, - PCIE_ATU_UNR_REGION_CTRL2); - else - val = dw_pcie_readl_rc(pp, PCIE_ATU_CR2); - - if (val == PCIE_ATU_ENABLE) - return; - - usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); - } - dev_err(pp->dev, "iATU is not being enabled\n"); + pci = to_dw_pcie_from_pp(pp); + return dw_pcie_write(pci->dbi_base + where, size, val); } static struct irq_chip dw_msi_irq_chip = { @@ -263,16 +62,15 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) for (i = 0; i < MAX_MSI_CTRLS; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, - (u32 *)&val); + (u32 *)&val); if (val) { ret = IRQ_HANDLED; pos = 0; while ((pos = find_next_bit(&val, 32, pos)) != 32) { irq = irq_find_mapping(pp->irq_domain, - i * 32 + pos); - dw_pcie_wr_own_conf(pp, - PCIE_MSI_INTR0_STATUS + i * 12, - 4, 1 << pos); + i * 32 + pos); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + + i * 12, 4, 1 << pos); generic_handle_irq(irq); pos++; } @@ -338,8 +136,9 @@ static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int irq, pos0, i; - struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(desc); + struct pcie_port *pp; + pp = (struct pcie_port *)msi_desc_to_pci_sysdata(desc); pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS, order_base_2(no_irqs)); if (pos0 < 0) @@ -401,7 +200,7 @@ static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos) } static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, - struct msi_desc *desc) + struct msi_desc *desc) { int irq, pos; struct pcie_port *pp = pdev->bus->sysdata; @@ -449,7 +248,7 @@ static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) { struct irq_data *data = irq_get_irq_data(irq); struct msi_desc *msi = irq_data_get_msi_desc(data); - struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); + struct pcie_port *pp = (struct pcie_port *)msi_desc_to_pci_sysdata(msi); clear_irq_range(pp, irq, 1, data->hwirq); } @@ -460,38 +259,8 @@ static struct msi_controller dw_pcie_msi_chip = { .teardown_irq = dw_msi_teardown_irq, }; -int dw_pcie_wait_for_link(struct pcie_port *pp) -{ - int retries; - - /* check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (dw_pcie_link_up(pp)) { - dev_info(pp->dev, "link up\n"); - return 0; - } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - dev_err(pp->dev, "phy link never came up\n"); - - return -ETIMEDOUT; -} - -int dw_pcie_link_up(struct pcie_port *pp) -{ - u32 val; - - if (pp->ops->link_up) - return pp->ops->link_up(pp); - - val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); - return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) && - (!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING))); -} - static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) + irq_hw_number_t hwirq) { irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); @@ -503,21 +272,12 @@ static const struct irq_domain_ops msi_domain_ops = { .map = dw_pcie_msi_map, }; -static u8 dw_pcie_iatu_unroll_enabled(struct pcie_port *pp) -{ - u32 val; - - val = dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT); - if (val == 0xffffffff) - return 1; - - return 0; -} - int dw_pcie_host_init(struct pcie_port *pp) { - struct device_node *np = pp->dev->of_node; - struct platform_device *pdev = to_platform_device(pp->dev); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); struct pci_bus *bus, *child; struct resource *cfg_res; int i, ret; @@ -526,19 +286,19 @@ int dw_pcie_host_init(struct pcie_port *pp) cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { - pp->cfg0_size = resource_size(cfg_res)/2; - pp->cfg1_size = resource_size(cfg_res)/2; + pp->cfg0_size = resource_size(cfg_res) / 2; + pp->cfg1_size = resource_size(cfg_res) / 2; pp->cfg0_base = cfg_res->start; pp->cfg1_base = cfg_res->start + pp->cfg0_size; } else if (!pp->va_cfg0_base) { - dev_err(pp->dev, "missing *config* reg space\n"); + dev_err(dev, "missing *config* reg space\n"); } ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base); if (ret) return ret; - ret = devm_request_pci_bus_resources(&pdev->dev, &res); + ret = devm_request_pci_bus_resources(dev, &res); if (ret) goto error; @@ -548,7 +308,7 @@ int dw_pcie_host_init(struct pcie_port *pp) case IORESOURCE_IO: ret = pci_remap_iospace(win->res, pp->io_base); if (ret) { - dev_warn(pp->dev, "error %d: failed to map resource %pR\n", + dev_warn(dev, "error %d: failed to map resource %pR\n", ret, win->res); resource_list_destroy_entry(win); } else { @@ -566,8 +326,8 @@ int dw_pcie_host_init(struct pcie_port *pp) break; case 0: pp->cfg = win->res; - pp->cfg0_size = resource_size(pp->cfg)/2; - pp->cfg1_size = resource_size(pp->cfg)/2; + pp->cfg0_size = resource_size(pp->cfg) / 2; + pp->cfg1_size = resource_size(pp->cfg) / 2; pp->cfg0_base = pp->cfg->start; pp->cfg1_base = pp->cfg->start + pp->cfg0_size; break; @@ -577,11 +337,11 @@ int dw_pcie_host_init(struct pcie_port *pp) } } - if (!pp->dbi_base) { - pp->dbi_base = devm_ioremap(pp->dev, pp->cfg->start, + if (!pci->dbi_base) { + pci->dbi_base = devm_ioremap(dev, pp->cfg->start, resource_size(pp->cfg)); - if (!pp->dbi_base) { - dev_err(pp->dev, "error with ioremap\n"); + if (!pci->dbi_base) { + dev_err(dev, "error with ioremap\n"); ret = -ENOMEM; goto error; } @@ -590,40 +350,36 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->mem_base = pp->mem->start; if (!pp->va_cfg0_base) { - pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, + pp->va_cfg0_base = devm_ioremap(dev, pp->cfg0_base, pp->cfg0_size); if (!pp->va_cfg0_base) { - dev_err(pp->dev, "error with ioremap in function\n"); + dev_err(dev, "error with ioremap in function\n"); ret = -ENOMEM; goto error; } } if (!pp->va_cfg1_base) { - pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base, + pp->va_cfg1_base = devm_ioremap(dev, pp->cfg1_base, pp->cfg1_size); if (!pp->va_cfg1_base) { - dev_err(pp->dev, "error with ioremap\n"); + dev_err(dev, "error with ioremap\n"); ret = -ENOMEM; goto error; } } - ret = of_property_read_u32(np, "num-lanes", &pp->lanes); - if (ret) - pp->lanes = 0; - - ret = of_property_read_u32(np, "num-viewport", &pp->num_viewport); + ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport); if (ret) - pp->num_viewport = 2; + pci->num_viewport = 2; if (IS_ENABLED(CONFIG_PCI_MSI)) { if (!pp->ops->msi_host_init) { - pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, + pp->irq_domain = irq_domain_add_linear(dev->of_node, MAX_MSI_IRQS, &msi_domain_ops, &dw_pcie_msi_chip); if (!pp->irq_domain) { - dev_err(pp->dev, "irq domain init failed\n"); + dev_err(dev, "irq domain init failed\n"); ret = -ENXIO; goto error; } @@ -642,12 +398,12 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->root_bus_nr = pp->busn->start; if (IS_ENABLED(CONFIG_PCI_MSI)) { - bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr, + bus = pci_scan_root_bus_msi(dev, pp->root_bus_nr, &dw_pcie_ops, pp, &res, &dw_pcie_msi_chip); - dw_pcie_msi_chip.dev = pp->dev; + dw_pcie_msi_chip.dev = dev; } else - bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, + bus = pci_scan_root_bus(dev, pp->root_bus_nr, &dw_pcie_ops, pp, &res); if (!bus) { ret = -ENOMEM; @@ -677,12 +433,13 @@ error: } static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) + u32 devfn, int where, int size, u32 *val) { int ret, type; u32 busdev, cfg_size; u64 cpu_addr; void __iomem *va_cfg_base; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); if (pp->ops->rd_other_conf) return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val); @@ -702,12 +459,12 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, va_cfg_base = pp->va_cfg1_base; } - dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, type, cpu_addr, busdev, cfg_size); - ret = dw_pcie_cfg_read(va_cfg_base + where, size, val); - if (pp->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, + ret = dw_pcie_read(va_cfg_base + where, size, val); + if (pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, PCIE_ATU_TYPE_IO, pp->io_base, pp->io_bus_addr, pp->io_size); @@ -715,12 +472,13 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, } static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) + u32 devfn, int where, int size, u32 val) { int ret, type; u32 busdev, cfg_size; u64 cpu_addr; void __iomem *va_cfg_base; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); if (pp->ops->wr_other_conf) return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val); @@ -740,12 +498,12 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, va_cfg_base = pp->va_cfg1_base; } - dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, type, cpu_addr, busdev, cfg_size); - ret = dw_pcie_cfg_write(va_cfg_base + where, size, val); - if (pp->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, + ret = dw_pcie_write(va_cfg_base + where, size, val); + if (pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, PCIE_ATU_TYPE_IO, pp->io_base, pp->io_bus_addr, pp->io_size); @@ -755,9 +513,11 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, int dev) { + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + /* If there is no link, then there is no device */ if (bus->number != pp->root_bus_nr) { - if (!dw_pcie_link_up(pp)) + if (!dw_pcie_link_up(pci)) return 0; } @@ -769,7 +529,7 @@ static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, } static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) + int size, u32 *val) { struct pcie_port *pp = bus->sysdata; @@ -785,7 +545,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, } static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) + int where, int size, u32 val) { struct pcie_port *pp = bus->sysdata; @@ -803,73 +563,46 @@ static struct pci_ops dw_pcie_ops = { .write = dw_pcie_wr_conf, }; +static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT); + if (val == 0xffffffff) + return 1; + + return 0; +} + void dw_pcie_setup_rc(struct pcie_port *pp) { u32 val; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - /* set the number of lanes */ - val = dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL); - val &= ~PORT_LINK_MODE_MASK; - switch (pp->lanes) { - case 1: - val |= PORT_LINK_MODE_1_LANES; - break; - case 2: - val |= PORT_LINK_MODE_2_LANES; - break; - case 4: - val |= PORT_LINK_MODE_4_LANES; - break; - case 8: - val |= PORT_LINK_MODE_8_LANES; - break; - default: - dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes); - return; - } - dw_pcie_writel_rc(pp, PCIE_PORT_LINK_CONTROL, val); - - /* set link width speed control register */ - val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL); - val &= ~PORT_LOGIC_LINK_WIDTH_MASK; - switch (pp->lanes) { - case 1: - val |= PORT_LOGIC_LINK_WIDTH_1_LANES; - break; - case 2: - val |= PORT_LOGIC_LINK_WIDTH_2_LANES; - break; - case 4: - val |= PORT_LOGIC_LINK_WIDTH_4_LANES; - break; - case 8: - val |= PORT_LOGIC_LINK_WIDTH_8_LANES; - break; - } - dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + dw_pcie_setup(pci); /* setup RC BARs */ - dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0x00000004); - dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0x00000000); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); /* setup interrupt pins */ - val = dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE); + val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE); val &= 0xffff00ff; val |= 0x00000100; - dw_pcie_writel_rc(pp, PCI_INTERRUPT_LINE, val); + dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val); /* setup bus numbers */ - val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS); + val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS); val &= 0xff000000; val |= 0x00010100; - dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val); + dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val); /* setup command register */ - val = dw_pcie_readl_rc(pp, PCI_COMMAND); + val = dw_pcie_readl_dbi(pci, PCI_COMMAND); val &= 0xffff0000; val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR; - dw_pcie_writel_rc(pp, PCI_COMMAND, val); + dw_pcie_writel_dbi(pci, PCI_COMMAND, val); /* * If the platform provides ->rd_other_conf, it means the platform @@ -878,15 +611,15 @@ void dw_pcie_setup_rc(struct pcie_port *pp) */ if (!pp->ops->rd_other_conf) { /* get iATU unroll support */ - pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp); - dev_dbg(pp->dev, "iATU unroll: %s\n", - pp->iatu_unroll_enabled ? "enabled" : "disabled"); + pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci); + dev_dbg(pci->dev, "iATU unroll: %s\n", + pci->iatu_unroll_enabled ? "enabled" : "disabled"); - dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, PCIE_ATU_TYPE_MEM, pp->mem_base, pp->mem_bus_addr, pp->mem_size); - if (pp->num_viewport > 2) - dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX2, + if (pci->num_viewport > 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2, PCIE_ATU_TYPE_IO, pp->io_base, pp->io_bus_addr, pp->io_size); } diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c index 1a02038c4640..b6c832ba39dd 100644 --- a/drivers/pci/host/pcie-designware-plat.c +++ b/drivers/pci/dwc/pcie-designware-plat.c @@ -25,7 +25,7 @@ #include "pcie-designware.h" struct dw_plat_pcie { - struct pcie_port pp; /* pp.dbi_base is DT 0th resource */ + struct dw_pcie *pci; }; static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg) @@ -37,21 +37,23 @@ static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg) static void dw_plat_pcie_host_init(struct pcie_port *pp) { + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + dw_pcie_setup_rc(pp); - dw_pcie_wait_for_link(pp); + dw_pcie_wait_for_link(pci); if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); } -static struct pcie_host_ops dw_plat_pcie_host_ops = { +static struct dw_pcie_host_ops dw_plat_pcie_host_ops = { .host_init = dw_plat_pcie_host_init, }; static int dw_plat_add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) { - struct device *dev = pp->dev; + struct device *dev = &pdev->dev; int ret; pp->irq = platform_get_irq(pdev, 1); @@ -88,7 +90,7 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dw_plat_pcie *dw_plat_pcie; - struct pcie_port *pp; + struct dw_pcie *pci; struct resource *res; /* Resource from DT */ int ret; @@ -96,15 +98,22 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) if (!dw_plat_pcie) return -ENOMEM; - pp = &dw_plat_pcie->pp; - pp->dev = dev; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + + dw_plat_pcie->pci = pci; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pp->dbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pp->dbi_base)) - return PTR_ERR(pp->dbi_base); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + platform_set_drvdata(pdev, dw_plat_pcie); - ret = dw_plat_add_pcie_port(pp, pdev); + ret = dw_plat_add_pcie_port(&pci->pp, pdev); if (ret < 0) return ret; diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c new file mode 100644 index 000000000000..7e1fb7d6643c --- /dev/null +++ b/drivers/pci/dwc/pcie-designware.c @@ -0,0 +1,233 @@ +/* + * Synopsys Designware PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han <jg1.han@samsung.com> + * + * 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/delay.h> +#include <linux/of.h> +#include <linux/types.h> + +#include "pcie-designware.h" + +/* PCIe Port Logic registers */ +#define PLR_OFFSET 0x700 +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4) +#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29) + +int dw_pcie_read(void __iomem *addr, int size, u32 *val) +{ + if ((uintptr_t)addr & (size - 1)) { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (size == 4) { + *val = readl(addr); + } else if (size == 2) { + *val = readw(addr); + } else if (size == 1) { + *val = readb(addr); + } else { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + return PCIBIOS_SUCCESSFUL; +} + +int dw_pcie_write(void __iomem *addr, int size, u32 val) +{ + if ((uintptr_t)addr & (size - 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (size == 4) + writel(val, addr); + else if (size == 2) + writew(val, addr); + else if (size == 1) + writeb(val, addr); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) +{ + if (pci->ops->readl_dbi) + return pci->ops->readl_dbi(pci, reg); + + return readl(pci->dbi_base + reg); +} + +void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) +{ + if (pci->ops->writel_dbi) + pci->ops->writel_dbi(pci, reg, val); + else + writel(val, pci->dbi_base + reg); +} + +static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, u32 index, u32 reg) +{ + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + + return dw_pcie_readl_dbi(pci, offset + reg); +} + +static void dw_pcie_writel_unroll(struct dw_pcie *pci, u32 index, u32 reg, + u32 val) +{ + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + + dw_pcie_writel_dbi(pci, offset + reg, val); +} + +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, + u64 cpu_addr, u64 pci_addr, u32 size) +{ + u32 retries, val; + + if (pci->iatu_unroll_enabled) { + dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, + type); + dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_ENABLE); + } else { + dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, + PCIE_ATU_REGION_OUTBOUND | index); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); + dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); + } + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + if (pci->iatu_unroll_enabled) + val = dw_pcie_readl_unroll(pci, index, + PCIE_ATU_UNR_REGION_CTRL2); + else + val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); + + if (val == PCIE_ATU_ENABLE) + return; + + usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); + } + dev_err(pci->dev, "iATU is not being enabled\n"); +} + +int dw_pcie_wait_for_link(struct dw_pcie *pci) +{ + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (dw_pcie_link_up(pci)) { + dev_info(pci->dev, "link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + dev_err(pci->dev, "phy link never came up\n"); + + return -ETIMEDOUT; +} + +int dw_pcie_link_up(struct dw_pcie *pci) +{ + u32 val; + + if (pci->ops->link_up) + return pci->ops->link_up(pci); + + val = readl(pci->dbi_base + PCIE_PHY_DEBUG_R1); + return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) && + (!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING))); +} + +void dw_pcie_setup(struct dw_pcie *pci) +{ + int ret; + u32 val; + u32 lanes; + struct device *dev = pci->dev; + struct device_node *np = dev->of_node; + + ret = of_property_read_u32(np, "num-lanes", &lanes); + if (ret) + lanes = 0; + + /* set the number of lanes */ + val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); + val &= ~PORT_LINK_MODE_MASK; + switch (lanes) { + case 1: + val |= PORT_LINK_MODE_1_LANES; + break; + case 2: + val |= PORT_LINK_MODE_2_LANES; + break; + case 4: + val |= PORT_LINK_MODE_4_LANES; + break; + case 8: + val |= PORT_LINK_MODE_8_LANES; + break; + default: + dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes); + return; + } + dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); + + /* set link width speed control register */ + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_LINK_WIDTH_MASK; + switch (lanes) { + case 1: + val |= PORT_LOGIC_LINK_WIDTH_1_LANES; + break; + case 2: + val |= PORT_LOGIC_LINK_WIDTH_2_LANES; + break; + case 4: + val |= PORT_LOGIC_LINK_WIDTH_4_LANES; + break; + case 8: + val |= PORT_LOGIC_LINK_WIDTH_8_LANES; + break; + } + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); +} diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h new file mode 100644 index 000000000000..cd3b8713fe50 --- /dev/null +++ b/drivers/pci/dwc/pcie-designware.h @@ -0,0 +1,198 @@ +/* + * Synopsys Designware PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han <jg1.han@samsung.com> + * + * 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_DESIGNWARE_H +#define _PCIE_DESIGNWARE_H + +#include <linux/irq.h> +#include <linux/msi.h> +#include <linux/pci.h> + +/* Parameters for the waiting for link up routine */ +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP_MIN 90000 +#define LINK_WAIT_USLEEP_MAX 100000 + +/* Parameters for the waiting for iATU enabled routine */ +#define LINK_WAIT_MAX_IATU_RETRIES 5 +#define LINK_WAIT_IATU_MIN 9000 +#define LINK_WAIT_IATU_MAX 10000 + +/* Synopsys-specific PCIe configuration registers */ +#define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_MODE_MASK (0x3f << 16) +#define PORT_LINK_MODE_1_LANES (0x1 << 16) +#define PORT_LINK_MODE_2_LANES (0x3 << 16) +#define PORT_LINK_MODE_4_LANES (0x7 << 16) +#define PORT_LINK_MODE_8_LANES (0xf << 16) + +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) +#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8) +#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) +#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) +#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) +#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8) + +#define PCIE_MSI_ADDR_LO 0x820 +#define PCIE_MSI_ADDR_HI 0x824 +#define PCIE_MSI_INTR0_ENABLE 0x828 +#define PCIE_MSI_INTR0_MASK 0x82C +#define PCIE_MSI_INTR0_STATUS 0x830 + +#define PCIE_ATU_VIEWPORT 0x900 +#define PCIE_ATU_REGION_INBOUND (0x1 << 31) +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) +#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 + +/* + * iATU Unroll-specific register definitions + * From 4.80 core version the address translation will be made by unroll + */ +#define PCIE_ATU_UNR_REGION_CTRL1 0x00 +#define PCIE_ATU_UNR_REGION_CTRL2 0x04 +#define PCIE_ATU_UNR_LOWER_BASE 0x08 +#define PCIE_ATU_UNR_UPPER_BASE 0x0C +#define PCIE_ATU_UNR_LIMIT 0x10 +#define PCIE_ATU_UNR_LOWER_TARGET 0x14 +#define PCIE_ATU_UNR_UPPER_TARGET 0x18 + +/* Register address builder */ +#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \ + ((0x3 << 20) | ((region) << 9)) + +/* + * Maximum number of MSI IRQs can be 256 per controller. But keep + * it 32 as of now. Probably we will never need more than 32. If needed, + * then increment it in multiple of 32. + */ +#define MAX_MSI_IRQS 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) + +struct pcie_port; +struct dw_pcie; + +struct dw_pcie_host_ops { + int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); + int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); + void (*host_init)(struct pcie_port *pp); + void (*msi_set_irq)(struct pcie_port *pp, int irq); + void (*msi_clear_irq)(struct pcie_port *pp, int irq); + phys_addr_t (*get_msi_addr)(struct pcie_port *pp); + u32 (*get_msi_data)(struct pcie_port *pp, int pos); + void (*scan_bus)(struct pcie_port *pp); + int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); +}; + +struct pcie_port { + u8 root_bus_nr; + u64 cfg0_base; + void __iomem *va_cfg0_base; + u32 cfg0_size; + u64 cfg1_base; + void __iomem *va_cfg1_base; + u32 cfg1_size; + resource_size_t io_base; + phys_addr_t io_bus_addr; + u32 io_size; + u64 mem_base; + phys_addr_t mem_bus_addr; + u32 mem_size; + struct resource *cfg; + struct resource *io; + struct resource *mem; + struct resource *busn; + int irq; + struct dw_pcie_host_ops *ops; + int msi_irq; + struct irq_domain *irq_domain; + unsigned long msi_data; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); +}; + +struct dw_pcie_ops { + u32 (*readl_dbi)(struct dw_pcie *pcie, u32 reg); + void (*writel_dbi)(struct dw_pcie *pcie, u32 reg, u32 val); + int (*link_up)(struct dw_pcie *pcie); +}; + +struct dw_pcie { + struct device *dev; + void __iomem *dbi_base; + u32 num_viewport; + u8 iatu_unroll_enabled; + struct pcie_port pp; + const struct dw_pcie_ops *ops; +}; + +#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) + +int dw_pcie_read(void __iomem *addr, int size, u32 *val); +int dw_pcie_write(void __iomem *addr, int size, u32 val); + +u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg); +void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val); +int dw_pcie_link_up(struct dw_pcie *pci); +int dw_pcie_wait_for_link(struct dw_pcie *pci); +void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, + int type, u64 cpu_addr, u64 pci_addr, + u32 size); +void dw_pcie_setup(struct dw_pcie *pci); + +#ifdef CONFIG_PCIE_DW_HOST +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); +void dw_pcie_msi_init(struct pcie_port *pp); +void dw_pcie_setup_rc(struct pcie_port *pp); +int dw_pcie_host_init(struct pcie_port *pp); +#else +static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) +{ + return IRQ_NONE; +} + +static inline void dw_pcie_msi_init(struct pcie_port *pp) +{ +} + +static inline void dw_pcie_setup_rc(struct pcie_port *pp) +{ +} + +static inline int dw_pcie_host_init(struct pcie_port *pp) +{ + return 0; +} +#endif +#endif /* _PCIE_DESIGNWARE_H */ diff --git a/drivers/pci/host/pcie-hisi.c b/drivers/pci/dwc/pcie-hisi.c index a301a7187b30..fd66a3199db7 100644 --- a/drivers/pci/host/pcie-hisi.c +++ b/drivers/pci/dwc/pcie-hisi.c @@ -24,10 +24,10 @@ #include <linux/regmap.h> #include "../pci.h" -#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) +#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) -static int hisi_pcie_acpi_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) +static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) { struct pci_config_window *cfg = bus->sysdata; int dev = PCI_SLOT(devfn); @@ -44,8 +44,8 @@ static int hisi_pcie_acpi_rd_conf(struct pci_bus *bus, u32 devfn, int where, return pci_generic_config_read(bus, devfn, where, size, val); } -static int hisi_pcie_acpi_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) +static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) { struct pci_config_window *cfg = bus->sysdata; int dev = PCI_SLOT(devfn); @@ -74,6 +74,8 @@ static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, return pci_ecam_map_bus(bus, devfn, where); } +#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) + static int hisi_pcie_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; @@ -110,8 +112,8 @@ struct pci_ecam_ops hisi_pcie_ops = { .init = hisi_pcie_init, .pci_ops = { .map_bus = hisi_pcie_map_bus, - .read = hisi_pcie_acpi_rd_conf, - .write = hisi_pcie_acpi_wr_conf, + .read = hisi_pcie_rd_conf, + .write = hisi_pcie_wr_conf, } }; @@ -127,7 +129,7 @@ struct pci_ecam_ops hisi_pcie_ops = { #define PCIE_LTSSM_LINKUP_STATE 0x11 #define PCIE_LTSSM_STATE_MASK 0x3F -#define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp) +#define to_hisi_pcie(x) dev_get_drvdata((x)->dev) struct hisi_pcie; @@ -136,10 +138,10 @@ struct pcie_soc_ops { }; struct hisi_pcie { - struct pcie_port pp; /* pp.dbi_base is DT rc_dbi */ + struct dw_pcie *pci; struct regmap *subctrl; u32 port_id; - struct pcie_soc_ops *soc_ops; + const struct pcie_soc_ops *soc_ops; }; /* HipXX PCIe host only supports 32-bit config access */ @@ -149,10 +151,11 @@ static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, u32 reg; u32 reg_val; void *walker = ®_val; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); walker += (where & 0x3); reg = where & ~0x3; - reg_val = dw_pcie_readl_rc(pp, reg); + reg_val = dw_pcie_readl_dbi(pci, reg); if (size == 1) *val = *(u8 __force *) walker; @@ -173,19 +176,20 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, u32 reg_val; u32 reg; void *walker = ®_val; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); walker += (where & 0x3); reg = where & ~0x3; if (size == 4) - dw_pcie_writel_rc(pp, reg, val); + dw_pcie_writel_dbi(pci, reg, val); else if (size == 2) { - reg_val = dw_pcie_readl_rc(pp, reg); + reg_val = dw_pcie_readl_dbi(pci, reg); *(u16 __force *) walker = val; - dw_pcie_writel_rc(pp, reg, reg_val); + dw_pcie_writel_dbi(pci, reg, reg_val); } else if (size == 1) { - reg_val = dw_pcie_readl_rc(pp, reg); + reg_val = dw_pcie_readl_dbi(pci, reg); *(u8 __force *) walker = val; - dw_pcie_writel_rc(pp, reg, reg_val); + dw_pcie_writel_dbi(pci, reg, reg_val); } else return PCIBIOS_BAD_REGISTER_NUMBER; @@ -204,32 +208,32 @@ static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie) static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie) { - struct pcie_port *pp = &hisi_pcie->pp; + struct dw_pcie *pci = hisi_pcie->pci; u32 val; - val = dw_pcie_readl_rc(pp, PCIE_SYS_STATE4); + val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4); return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); } -static int hisi_pcie_link_up(struct pcie_port *pp) +static int hisi_pcie_link_up(struct dw_pcie *pci) { - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); + struct hisi_pcie *hisi_pcie = to_hisi_pcie(pci); return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie); } -static struct pcie_host_ops hisi_pcie_host_ops = { +static struct dw_pcie_host_ops hisi_pcie_host_ops = { .rd_own_conf = hisi_pcie_cfg_read, .wr_own_conf = hisi_pcie_cfg_write, - .link_up = hisi_pcie_link_up, }; static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, struct platform_device *pdev) { - struct pcie_port *pp = &hisi_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = hisi_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; int ret; u32 port_id; @@ -254,12 +258,15 @@ static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, return 0; } +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = hisi_pcie_link_up, +}; + static int hisi_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct dw_pcie *pci; struct hisi_pcie *hisi_pcie; - struct pcie_port *pp; - const struct of_device_id *match; struct resource *reg; struct device_driver *driver; int ret; @@ -268,24 +275,32 @@ static int hisi_pcie_probe(struct platform_device *pdev) if (!hisi_pcie) return -ENOMEM; - pp = &hisi_pcie->pp; - pp->dev = dev; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + driver = dev->driver; - match = of_match_device(driver->of_match_table, dev); - hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data; + hisi_pcie->pci = pci; + + hisi_pcie->soc_ops = of_device_get_match_data(dev); hisi_pcie->subctrl = - syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); + syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); if (IS_ERR(hisi_pcie->subctrl)) { dev_err(dev, "cannot get subctrl base\n"); return PTR_ERR(hisi_pcie->subctrl); } reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); - pp->dbi_base = devm_ioremap_resource(dev, reg); - if (IS_ERR(pp->dbi_base)) - return PTR_ERR(pp->dbi_base); + pci->dbi_base = devm_ioremap_resource(dev, reg); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + platform_set_drvdata(pdev, hisi_pcie); ret = hisi_add_pcie_port(hisi_pcie, pdev); if (ret) @@ -323,4 +338,62 @@ static struct platform_driver hisi_pcie_driver = { }; builtin_platform_driver(hisi_pcie_driver); +static int hisi_pcie_almost_ecam_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pci_ecam_ops *ops; + + ops = (struct pci_ecam_ops *)of_device_get_match_data(dev); + return pci_host_common_probe(pdev, ops); +} + +static int hisi_pcie_platform_init(struct pci_config_window *cfg) +{ + struct device *dev = cfg->parent; + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + void __iomem *reg_base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "missing \"reg[1]\"property\n"); + return -EINVAL; + } + + reg_base = devm_ioremap(dev, res->start, resource_size(res)); + if (!reg_base) + return -ENOMEM; + + cfg->priv = reg_base; + return 0; +} + +struct pci_ecam_ops hisi_pcie_platform_ops = { + .bus_shift = 20, + .init = hisi_pcie_platform_init, + .pci_ops = { + .map_bus = hisi_pcie_map_bus, + .read = hisi_pcie_rd_conf, + .write = hisi_pcie_wr_conf, + } +}; + +static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { + { + .compatible = "hisilicon,pcie-almost-ecam", + .data = (void *) &hisi_pcie_platform_ops, + }, + {}, +}; + +static struct platform_driver hisi_pcie_almost_ecam_driver = { + .probe = hisi_pcie_almost_ecam_probe, + .driver = { + .name = "hisi-pcie-almost-ecam", + .of_match_table = hisi_pcie_almost_ecam_of_match, + }, +}; +builtin_platform_driver(hisi_pcie_almost_ecam_driver); + +#endif #endif diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index 734ba0d4a5c8..67eb7f5926dd 100644 --- a/drivers/pci/host/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -103,7 +103,7 @@ struct qcom_pcie_ops { }; struct qcom_pcie { - struct pcie_port pp; /* pp.dbi_base is DT dbi */ + struct dw_pcie *pci; void __iomem *parf; /* DT parf */ void __iomem *elbi; /* DT elbi */ union qcom_pcie_resources res; @@ -112,7 +112,7 @@ struct qcom_pcie { struct qcom_pcie_ops *ops; }; -#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) +#define to_qcom_pcie(x) dev_get_drvdata((x)->dev) static void qcom_ep_reset_assert(struct qcom_pcie *pcie) { @@ -155,21 +155,23 @@ static void qcom_pcie_v2_ltssm_enable(struct qcom_pcie *pcie) static int qcom_pcie_establish_link(struct qcom_pcie *pcie) { + struct dw_pcie *pci = pcie->pci; - if (dw_pcie_link_up(&pcie->pp)) + if (dw_pcie_link_up(pci)) return 0; /* Enable Link Training state machine */ if (pcie->ops->ltssm_enable) pcie->ops->ltssm_enable(pcie); - return dw_pcie_wait_for_link(&pcie->pp); + return dw_pcie_wait_for_link(pci); } static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v0 *res = &pcie->res.v0; - struct device *dev = pcie->pp.dev; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; res->vdda = devm_regulator_get(dev, "vdda"); if (IS_ERR(res->vdda)) @@ -212,16 +214,14 @@ static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) return PTR_ERR(res->por_reset); res->phy_reset = devm_reset_control_get(dev, "phy"); - if (IS_ERR(res->phy_reset)) - return PTR_ERR(res->phy_reset); - - return 0; + return PTR_ERR_OR_ZERO(res->phy_reset); } static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v1 *res = &pcie->res.v1; - struct device *dev = pcie->pp.dev; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; res->vdda = devm_regulator_get(dev, "vdda"); if (IS_ERR(res->vdda)) @@ -244,10 +244,7 @@ static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) return PTR_ERR(res->slave_bus); res->core = devm_reset_control_get(dev, "core"); - if (IS_ERR(res->core)) - return PTR_ERR(res->core); - - return 0; + return PTR_ERR_OR_ZERO(res->core); } static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie) @@ -270,7 +267,8 @@ static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie) static int qcom_pcie_init_v0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v0 *res = &pcie->res.v0; - struct device *dev = pcie->pp.dev; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; u32 val; int ret; @@ -392,7 +390,8 @@ static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie) static int qcom_pcie_init_v1(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v1 *res = &pcie->res.v1; - struct device *dev = pcie->pp.dev; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; int ret; ret = reset_control_deassert(res->core); @@ -459,7 +458,8 @@ err_res: static int qcom_pcie_get_resources_v2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v2 *res = &pcie->res.v2; - struct device *dev = pcie->pp.dev; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; res->aux_clk = devm_clk_get(dev, "aux"); if (IS_ERR(res->aux_clk)) @@ -478,16 +478,14 @@ static int qcom_pcie_get_resources_v2(struct qcom_pcie *pcie) return PTR_ERR(res->slave_clk); res->pipe_clk = devm_clk_get(dev, "pipe"); - if (IS_ERR(res->pipe_clk)) - return PTR_ERR(res->pipe_clk); - - return 0; + return PTR_ERR_OR_ZERO(res->pipe_clk); } static int qcom_pcie_init_v2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v2 *res = &pcie->res.v2; - struct device *dev = pcie->pp.dev; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; u32 val; int ret; @@ -551,7 +549,8 @@ err_cfg_clk: static int qcom_pcie_post_init_v2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v2 *res = &pcie->res.v2; - struct device *dev = pcie->pp.dev; + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; int ret; ret = clk_prepare_enable(res->pipe_clk); @@ -563,10 +562,9 @@ static int qcom_pcie_post_init_v2(struct qcom_pcie *pcie) return 0; } -static int qcom_pcie_link_up(struct pcie_port *pp) +static int qcom_pcie_link_up(struct dw_pcie *pci) { - struct qcom_pcie *pcie = to_qcom_pcie(pp); - u16 val = readw(pcie->pp.dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); + u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); return !!(val & PCI_EXP_LNKSTA_DLLLA); } @@ -584,7 +582,8 @@ static void qcom_pcie_deinit_v2(struct qcom_pcie *pcie) static void qcom_pcie_host_init(struct pcie_port *pp) { - struct qcom_pcie *pcie = to_qcom_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct qcom_pcie *pcie = to_qcom_pcie(pci); int ret; qcom_ep_reset_assert(pcie); @@ -622,19 +621,20 @@ err_deinit: static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) { + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + /* the device class is not reported correctly from the register */ if (where == PCI_CLASS_REVISION && size == 4) { - *val = readl(pp->dbi_base + PCI_CLASS_REVISION); + *val = readl(pci->dbi_base + PCI_CLASS_REVISION); *val &= 0xff; /* keep revision id */ *val |= PCI_CLASS_BRIDGE_PCI << 16; return PCIBIOS_SUCCESSFUL; } - return dw_pcie_cfg_read(pp->dbi_base + where, size, val); + return dw_pcie_read(pci->dbi_base + where, size, val); } -static struct pcie_host_ops qcom_pcie_dw_ops = { - .link_up = qcom_pcie_link_up, +static struct dw_pcie_host_ops qcom_pcie_dw_ops = { .host_init = qcom_pcie_host_init, .rd_own_conf = qcom_pcie_rd_own_conf, }; @@ -661,19 +661,33 @@ static const struct qcom_pcie_ops ops_v2 = { .ltssm_enable = qcom_pcie_v2_ltssm_enable, }; +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = qcom_pcie_link_up, +}; + static int qcom_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; - struct qcom_pcie *pcie; struct pcie_port *pp; + struct dw_pcie *pci; + struct qcom_pcie *pcie; int ret; pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; - pp = &pcie->pp; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + pp = &pci->pp; + + pcie->pci = pci; + pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev); pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); @@ -686,9 +700,9 @@ static int qcom_pcie_probe(struct platform_device *pdev) return PTR_ERR(pcie->parf); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pp->dbi_base = devm_ioremap_resource(dev, res); - if (IS_ERR(pp->dbi_base)) - return PTR_ERR(pp->dbi_base); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); pcie->elbi = devm_ioremap_resource(dev, res); @@ -699,7 +713,6 @@ static int qcom_pcie_probe(struct platform_device *pdev) if (IS_ERR(pcie->phy)) return PTR_ERR(pcie->phy); - pp->dev = dev; ret = pcie->ops->get_resources(pcie); if (ret) return ret; @@ -725,6 +738,8 @@ static int qcom_pcie_probe(struct platform_device *pdev) if (ret) return ret; + platform_set_drvdata(pdev, pcie); + ret = dw_pcie_host_init(pp); if (ret) { dev_err(dev, "cannot initialize host\n"); diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/dwc/pcie-spear13xx.c index dafe8b88d97d..eaa4ea8e2ea4 100644 --- a/drivers/pci/host/pcie-spear13xx.c +++ b/drivers/pci/dwc/pcie-spear13xx.c @@ -25,7 +25,7 @@ #include "pcie-designware.h" struct spear13xx_pcie { - struct pcie_port pp; /* DT dbi is pp.dbi_base */ + struct dw_pcie *pci; void __iomem *app_base; struct phy *phy; struct clk *clk; @@ -70,17 +70,18 @@ struct pcie_app_reg { #define EXP_CAP_ID_OFFSET 0x70 -#define to_spear13xx_pcie(x) container_of(x, struct spear13xx_pcie, pp) +#define to_spear13xx_pcie(x) dev_get_drvdata((x)->dev) static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie) { - struct pcie_port *pp = &spear13xx_pcie->pp; + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; u32 val; u32 exp_cap_off = EXP_CAP_ID_OFFSET; - if (dw_pcie_link_up(pp)) { - dev_err(pp->dev, "link already up\n"); + if (dw_pcie_link_up(pci)) { + dev_err(pci->dev, "link already up\n"); return 0; } @@ -91,34 +92,34 @@ static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie) * default value in capability register is 512 bytes. So force * it to 128 here. */ - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val); + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val); val &= ~PCI_EXP_DEVCTL_READRQ; - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val); + dw_pcie_write(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val); - dw_pcie_cfg_write(pp->dbi_base + PCI_VENDOR_ID, 2, 0x104A); - dw_pcie_cfg_write(pp->dbi_base + PCI_DEVICE_ID, 2, 0xCD80); + dw_pcie_write(pci->dbi_base + PCI_VENDOR_ID, 2, 0x104A); + dw_pcie_write(pci->dbi_base + PCI_DEVICE_ID, 2, 0xCD80); /* * if is_gen1 is set then handle it, so that some buggy card * also works */ if (spear13xx_pcie->is_gen1) { - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP, - 4, &val); + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP, + 4, &val); if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { val &= ~((u32)PCI_EXP_LNKCAP_SLS); val |= PCI_EXP_LNKCAP_SLS_2_5GB; - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + - PCI_EXP_LNKCAP, 4, val); + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCAP, 4, val); } - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2, - 2, &val); + dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2, + 2, &val); if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) { val &= ~((u32)PCI_EXP_LNKCAP_SLS); val |= PCI_EXP_LNKCAP_SLS_2_5GB; - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + - PCI_EXP_LNKCTL2, 2, val); + dw_pcie_write(pci->dbi_base + exp_cap_off + + PCI_EXP_LNKCTL2, 2, val); } } @@ -128,14 +129,15 @@ static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie) | ((u32)1 << REG_TRANSLATION_ENABLE), &app_reg->app_ctrl_0); - return dw_pcie_wait_for_link(pp); + return dw_pcie_wait_for_link(pci); } static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) { struct spear13xx_pcie *spear13xx_pcie = arg; struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; - struct pcie_port *pp = &spear13xx_pcie->pp; + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; unsigned int status; status = readl(&app_reg->int_sts); @@ -152,7 +154,8 @@ static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pcie) { - struct pcie_port *pp = &spear13xx_pcie->pp; + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; /* Enable MSI interrupt */ @@ -163,9 +166,9 @@ static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pc } } -static int spear13xx_pcie_link_up(struct pcie_port *pp) +static int spear13xx_pcie_link_up(struct dw_pcie *pci) { - struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp); + struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; if (readl(&app_reg->app_status_1) & XMLH_LINK_UP) @@ -176,22 +179,23 @@ static int spear13xx_pcie_link_up(struct pcie_port *pp) static void spear13xx_pcie_host_init(struct pcie_port *pp) { - struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); spear13xx_pcie_establish_link(spear13xx_pcie); spear13xx_pcie_enable_interrupts(spear13xx_pcie); } -static struct pcie_host_ops spear13xx_pcie_host_ops = { - .link_up = spear13xx_pcie_link_up, +static struct dw_pcie_host_ops spear13xx_pcie_host_ops = { .host_init = spear13xx_pcie_host_init, }; static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, struct platform_device *pdev) { - struct pcie_port *pp = &spear13xx_pcie->pp; - struct device *dev = pp->dev; + struct dw_pcie *pci = spear13xx_pcie->pci; + struct pcie_port *pp = &pci->pp; + struct device *dev = &pdev->dev; int ret; pp->irq = platform_get_irq(pdev, 0); @@ -219,11 +223,15 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, return 0; } +static const struct dw_pcie_ops dw_pcie_ops = { + .link_up = spear13xx_pcie_link_up, +}; + static int spear13xx_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct dw_pcie *pci; struct spear13xx_pcie *spear13xx_pcie; - struct pcie_port *pp; struct device_node *np = dev->of_node; struct resource *dbi_base; int ret; @@ -232,6 +240,15 @@ static int spear13xx_pcie_probe(struct platform_device *pdev) if (!spear13xx_pcie) return -ENOMEM; + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + spear13xx_pcie->pci = pci; + spear13xx_pcie->phy = devm_phy_get(dev, "pcie-phy"); if (IS_ERR(spear13xx_pcie->phy)) { ret = PTR_ERR(spear13xx_pcie->phy); @@ -255,26 +272,24 @@ static int spear13xx_pcie_probe(struct platform_device *pdev) return ret; } - pp = &spear13xx_pcie->pp; - pp->dev = dev; - dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pp->dbi_base = devm_ioremap_resource(dev, dbi_base); - if (IS_ERR(pp->dbi_base)) { + pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + if (IS_ERR(pci->dbi_base)) { dev_err(dev, "couldn't remap dbi base %p\n", dbi_base); - ret = PTR_ERR(pp->dbi_base); + ret = PTR_ERR(pci->dbi_base); goto fail_clk; } - spear13xx_pcie->app_base = pp->dbi_base + 0x2000; + spear13xx_pcie->app_base = pci->dbi_base + 0x2000; if (of_property_read_bool(np, "st,pcie-is-gen1")) spear13xx_pcie->is_gen1 = true; + platform_set_drvdata(pdev, spear13xx_pcie); + ret = spear13xx_add_pcie_port(spear13xx_pcie, pdev); if (ret < 0) goto fail_clk; - platform_set_drvdata(pdev, spear13xx_pcie); return 0; fail_clk: diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 898d2c48239c..f7c1d4d5c665 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -1,16 +1,6 @@ menu "PCI host controller drivers" depends on PCI -config PCI_DRA7XX - bool "TI DRA7xx PCIe controller" - depends on OF && HAS_IOMEM && TI_PIPE3 - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - help - Enables support for the PCIe controller in the DRA7xx SoC. There - are two instances of PCIe controller in DRA7xx. This controller can - act both as EP and RC. This reuses the Designware core. - config PCI_MVEBU bool "Marvell EBU PCIe controller" depends on ARCH_MVEBU || ARCH_DOVE @@ -37,36 +27,6 @@ config PCIE_XILINX_NWL or End Point. The current option selection will only support root port enabling. -config PCIE_DW_PLAT - bool "Platform bus based DesignWare PCIe Controller" - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - ---help--- - This selects the DesignWare PCIe controller support. Select this if - you have a PCIe controller on Platform bus. - - If you have a controller with this interface, say Y or M here. - - If unsure, say N. - -config PCIE_DW - bool - depends on PCI_MSI_IRQ_DOMAIN - -config PCI_EXYNOS - bool "Samsung Exynos PCIe controller" - depends on SOC_EXYNOS5440 - depends on PCI_MSI_IRQ_DOMAIN - select PCIEPORTBUS - select PCIE_DW - -config PCI_IMX6 - bool "Freescale i.MX6 PCIe controller" - depends on SOC_IMX6Q - depends on PCI_MSI_IRQ_DOMAIN - select PCIEPORTBUS - select PCIE_DW - config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA @@ -103,27 +63,6 @@ config PCI_HOST_GENERIC Say Y here if you want to support a simple generic PCI host controller, such as the one emulated by kvmtool. -config PCIE_SPEAR13XX - bool "STMicroelectronics SPEAr PCIe controller" - depends on ARCH_SPEAR13XX - depends on PCI_MSI_IRQ_DOMAIN - select PCIEPORTBUS - select PCIE_DW - help - Say Y here if you want PCIe support on SPEAr13XX SoCs. - -config PCI_KEYSTONE - bool "TI Keystone PCIe controller" - depends on ARCH_KEYSTONE - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - select PCIEPORTBUS - help - Say Y here if you want to enable PCI controller support on Keystone - SoCs. The PCI controller on Keystone is based on Designware hardware - and therefore the driver re-uses the Designware core functions to - implement the driver. - config PCIE_XILINX bool "Xilinx AXI PCIe host bridge support" depends on ARCH_ZYNQ || MICROBLAZE @@ -150,15 +89,6 @@ config PCI_XGENE_MSI Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC. This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC. -config PCI_LAYERSCAPE - bool "Freescale Layerscape PCIe controller" - depends on OF && (ARM || ARCH_LAYERSCAPE) - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - select MFD_SYSCON - help - Say Y here if you want PCIe controller support on Layerscape SoCs. - config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE @@ -217,27 +147,6 @@ config PCIE_ALTERA_MSI Say Y here if you want PCIe MSI support for the Altera FPGA. This MSI driver supports Altera MSI to GIC controller IP. -config PCI_HISI - depends on OF && ARM64 - bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers" - depends on PCI_MSI_IRQ_DOMAIN - select PCIEPORTBUS - select PCIE_DW - help - Say Y here if you want PCIe controller support on HiSilicon - Hip05 and Hip06 SoCs - -config PCIE_QCOM - bool "Qualcomm PCIe controller" - depends on ARCH_QCOM && OF - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - select PCIEPORTBUS - help - Say Y here to enable PCIe controller support on Qualcomm SoCs. The - PCIe controller uses the Designware core plus Qualcomm-specific - hardware wrappers. - config PCI_HOST_THUNDER_PEM bool "Cavium Thunder PCIe controller to off-chip devices" depends on ARM64 @@ -254,28 +163,6 @@ config PCI_HOST_THUNDER_ECAM help Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs. -config PCIE_ARMADA_8K - bool "Marvell Armada-8K PCIe controller" - depends on ARCH_MVEBU - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - select PCIEPORTBUS - help - Say Y here if you want to enable PCIe controller support on - Armada-8K SoCs. The PCIe controller on Armada-8K is based on - Designware hardware and therefore the driver re-uses the - Designware core functions to implement the driver. - -config PCIE_ARTPEC6 - bool "Axis ARTPEC-6 PCIe controller" - depends on MACH_ARTPEC6 - depends on PCI_MSI_IRQ_DOMAIN - select PCIE_DW - select PCIEPORTBUS - help - Say Y here to enable PCIe controller support on Axis ARTPEC-6 - SoCs. This PCIe controller uses the DesignWare core. - config PCIE_ROCKCHIP bool "Rockchip PCIe controller" depends on ARCH_ROCKCHIP || COMPILE_TEST diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index bfe3179ae74c..4d3686676cc3 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -1,8 +1,3 @@ -obj-$(CONFIG_PCIE_DW) += pcie-designware.o -obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o -obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o -obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o -obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o @@ -11,12 +6,9 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o -obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o -obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o -obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.o @@ -24,9 +16,6 @@ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o -obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o -obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o -obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o obj-$(CONFIG_VMD) += vmd.o @@ -40,7 +29,6 @@ obj-$(CONFIG_VMD) += vmd.o # ARM64 and use internal ifdefs to only build the pieces we need # depending on whether ACPI, the DT driver, or both are enabled. -obj-$(CONFIG_ARM64) += pcie-hisi.o obj-$(CONFIG_ARM64) += pci-thunder-ecam.o obj-$(CONFIG_ARM64) += pci-thunder-pem.o obj-$(CONFIG_ARM64) += pci-xgene.o diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c deleted file mode 100644 index f1c544bb8b68..000000000000 --- a/drivers/pci/host/pci-exynos.c +++ /dev/null @@ -1,629 +0,0 @@ -/* - * PCIe host controller driver for Samsung EXYNOS SoCs - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jingoo Han <jg1.han@samsung.com> - * - * 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/clk.h> -#include <linux/delay.h> -#include <linux/gpio.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/of_gpio.h> -#include <linux/pci.h> -#include <linux/platform_device.h> -#include <linux/resource.h> -#include <linux/signal.h> -#include <linux/types.h> - -#include "pcie-designware.h" - -#define to_exynos_pcie(x) container_of(x, struct exynos_pcie, pp) - -struct exynos_pcie { - struct pcie_port pp; - void __iomem *elbi_base; /* DT 0th resource */ - void __iomem *phy_base; /* DT 1st resource */ - void __iomem *block_base; /* DT 2nd resource */ - int reset_gpio; - struct clk *clk; - struct clk *bus_clk; -}; - -/* PCIe ELBI registers */ -#define PCIE_IRQ_PULSE 0x000 -#define IRQ_INTA_ASSERT (0x1 << 0) -#define IRQ_INTB_ASSERT (0x1 << 2) -#define IRQ_INTC_ASSERT (0x1 << 4) -#define IRQ_INTD_ASSERT (0x1 << 6) -#define PCIE_IRQ_LEVEL 0x004 -#define PCIE_IRQ_SPECIAL 0x008 -#define PCIE_IRQ_EN_PULSE 0x00c -#define PCIE_IRQ_EN_LEVEL 0x010 -#define IRQ_MSI_ENABLE (0x1 << 2) -#define PCIE_IRQ_EN_SPECIAL 0x014 -#define PCIE_PWR_RESET 0x018 -#define PCIE_CORE_RESET 0x01c -#define PCIE_CORE_RESET_ENABLE (0x1 << 0) -#define PCIE_STICKY_RESET 0x020 -#define PCIE_NONSTICKY_RESET 0x024 -#define PCIE_APP_INIT_RESET 0x028 -#define PCIE_APP_LTSSM_ENABLE 0x02c -#define PCIE_ELBI_RDLH_LINKUP 0x064 -#define PCIE_ELBI_LTSSM_ENABLE 0x1 -#define PCIE_ELBI_SLV_AWMISC 0x11c -#define PCIE_ELBI_SLV_ARMISC 0x120 -#define PCIE_ELBI_SLV_DBI_ENABLE (0x1 << 21) - -/* PCIe Purple registers */ -#define PCIE_PHY_GLOBAL_RESET 0x000 -#define PCIE_PHY_COMMON_RESET 0x004 -#define PCIE_PHY_CMN_REG 0x008 -#define PCIE_PHY_MAC_RESET 0x00c -#define PCIE_PHY_PLL_LOCKED 0x010 -#define PCIE_PHY_TRSVREG_RESET 0x020 -#define PCIE_PHY_TRSV_RESET 0x024 - -/* PCIe PHY registers */ -#define PCIE_PHY_IMPEDANCE 0x004 -#define PCIE_PHY_PLL_DIV_0 0x008 -#define PCIE_PHY_PLL_BIAS 0x00c -#define PCIE_PHY_DCC_FEEDBACK 0x014 -#define PCIE_PHY_PLL_DIV_1 0x05c -#define PCIE_PHY_COMMON_POWER 0x064 -#define PCIE_PHY_COMMON_PD_CMN (0x1 << 3) -#define PCIE_PHY_TRSV0_EMP_LVL 0x084 -#define PCIE_PHY_TRSV0_DRV_LVL 0x088 -#define PCIE_PHY_TRSV0_RXCDR 0x0ac -#define PCIE_PHY_TRSV0_POWER 0x0c4 -#define PCIE_PHY_TRSV0_PD_TSV (0x1 << 7) -#define PCIE_PHY_TRSV0_LVCC 0x0dc -#define PCIE_PHY_TRSV1_EMP_LVL 0x144 -#define PCIE_PHY_TRSV1_RXCDR 0x16c -#define PCIE_PHY_TRSV1_POWER 0x184 -#define PCIE_PHY_TRSV1_PD_TSV (0x1 << 7) -#define PCIE_PHY_TRSV1_LVCC 0x19c -#define PCIE_PHY_TRSV2_EMP_LVL 0x204 -#define PCIE_PHY_TRSV2_RXCDR 0x22c -#define PCIE_PHY_TRSV2_POWER 0x244 -#define PCIE_PHY_TRSV2_PD_TSV (0x1 << 7) -#define PCIE_PHY_TRSV2_LVCC 0x25c -#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 -#define PCIE_PHY_TRSV3_RXCDR 0x2ec -#define PCIE_PHY_TRSV3_POWER 0x304 -#define PCIE_PHY_TRSV3_PD_TSV (0x1 << 7) -#define PCIE_PHY_TRSV3_LVCC 0x31c - -static void exynos_elb_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg) -{ - writel(val, exynos_pcie->elbi_base + reg); -} - -static u32 exynos_elb_readl(struct exynos_pcie *exynos_pcie, u32 reg) -{ - return readl(exynos_pcie->elbi_base + reg); -} - -static void exynos_phy_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg) -{ - writel(val, exynos_pcie->phy_base + reg); -} - -static u32 exynos_phy_readl(struct exynos_pcie *exynos_pcie, u32 reg) -{ - return readl(exynos_pcie->phy_base + reg); -} - -static void exynos_blk_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg) -{ - writel(val, exynos_pcie->block_base + reg); -} - -static u32 exynos_blk_readl(struct exynos_pcie *exynos_pcie, u32 reg) -{ - return readl(exynos_pcie->block_base + reg); -} - -static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *exynos_pcie, - bool on) -{ - u32 val; - - if (on) { - val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC); - val |= PCIE_ELBI_SLV_DBI_ENABLE; - exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_AWMISC); - } else { - val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC); - val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_AWMISC); - } -} - -static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *exynos_pcie, - bool on) -{ - u32 val; - - if (on) { - val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC); - val |= PCIE_ELBI_SLV_DBI_ENABLE; - exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_ARMISC); - } else { - val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC); - val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_ARMISC); - } -} - -static void exynos_pcie_assert_core_reset(struct exynos_pcie *exynos_pcie) -{ - u32 val; - - val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET); - val &= ~PCIE_CORE_RESET_ENABLE; - exynos_elb_writel(exynos_pcie, val, PCIE_CORE_RESET); - exynos_elb_writel(exynos_pcie, 0, PCIE_PWR_RESET); - exynos_elb_writel(exynos_pcie, 0, PCIE_STICKY_RESET); - exynos_elb_writel(exynos_pcie, 0, PCIE_NONSTICKY_RESET); -} - -static void exynos_pcie_deassert_core_reset(struct exynos_pcie *exynos_pcie) -{ - u32 val; - - val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET); - val |= PCIE_CORE_RESET_ENABLE; - - exynos_elb_writel(exynos_pcie, val, PCIE_CORE_RESET); - exynos_elb_writel(exynos_pcie, 1, PCIE_STICKY_RESET); - exynos_elb_writel(exynos_pcie, 1, PCIE_NONSTICKY_RESET); - exynos_elb_writel(exynos_pcie, 1, PCIE_APP_INIT_RESET); - exynos_elb_writel(exynos_pcie, 0, PCIE_APP_INIT_RESET); - exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET); -} - -static void exynos_pcie_assert_phy_reset(struct exynos_pcie *exynos_pcie) -{ - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_MAC_RESET); - exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_GLOBAL_RESET); -} - -static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *exynos_pcie) -{ - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET); - exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET); - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET); - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_CMN_REG); - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSVREG_RESET); - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET); -} - -static void exynos_pcie_power_on_phy(struct exynos_pcie *exynos_pcie) -{ - u32 val; - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); - val &= ~PCIE_PHY_COMMON_PD_CMN; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); - val &= ~PCIE_PHY_TRSV0_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); - val &= ~PCIE_PHY_TRSV1_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); - val &= ~PCIE_PHY_TRSV2_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); - val &= ~PCIE_PHY_TRSV3_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); -} - -static void exynos_pcie_power_off_phy(struct exynos_pcie *exynos_pcie) -{ - u32 val; - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); - val |= PCIE_PHY_COMMON_PD_CMN; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); - val |= PCIE_PHY_TRSV0_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); - val |= PCIE_PHY_TRSV1_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); - val |= PCIE_PHY_TRSV2_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); - - val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); - val |= PCIE_PHY_TRSV3_PD_TSV; - exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); -} - -static void exynos_pcie_init_phy(struct exynos_pcie *exynos_pcie) -{ - /* DCC feedback control off */ - exynos_phy_writel(exynos_pcie, 0x29, PCIE_PHY_DCC_FEEDBACK); - - /* set TX/RX impedance */ - exynos_phy_writel(exynos_pcie, 0xd5, PCIE_PHY_IMPEDANCE); - - /* set 50Mhz PHY clock */ - exynos_phy_writel(exynos_pcie, 0x14, PCIE_PHY_PLL_DIV_0); - exynos_phy_writel(exynos_pcie, 0x12, PCIE_PHY_PLL_DIV_1); - - /* set TX Differential output for lane 0 */ - exynos_phy_writel(exynos_pcie, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); - - /* set TX Pre-emphasis Level Control for lane 0 to minimum */ - exynos_phy_writel(exynos_pcie, 0x0, PCIE_PHY_TRSV0_EMP_LVL); - - /* set RX clock and data recovery bandwidth */ - exynos_phy_writel(exynos_pcie, 0xe7, PCIE_PHY_PLL_BIAS); - exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV0_RXCDR); - exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV1_RXCDR); - exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV2_RXCDR); - exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV3_RXCDR); - - /* change TX Pre-emphasis Level Control for lanes */ - exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV0_EMP_LVL); - exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV1_EMP_LVL); - exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV2_EMP_LVL); - exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV3_EMP_LVL); - - /* set LVCC */ - exynos_phy_writel(exynos_pcie, 0x20, PCIE_PHY_TRSV0_LVCC); - exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV1_LVCC); - exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV2_LVCC); - exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV3_LVCC); -} - -static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie) -{ - struct pcie_port *pp = &exynos_pcie->pp; - struct device *dev = pp->dev; - - if (exynos_pcie->reset_gpio >= 0) - devm_gpio_request_one(dev, exynos_pcie->reset_gpio, - GPIOF_OUT_INIT_HIGH, "RESET"); -} - -static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie) -{ - struct pcie_port *pp = &exynos_pcie->pp; - struct device *dev = pp->dev; - u32 val; - - if (dw_pcie_link_up(pp)) { - dev_err(dev, "Link already up\n"); - return 0; - } - - exynos_pcie_assert_core_reset(exynos_pcie); - exynos_pcie_assert_phy_reset(exynos_pcie); - exynos_pcie_deassert_phy_reset(exynos_pcie); - exynos_pcie_power_on_phy(exynos_pcie); - exynos_pcie_init_phy(exynos_pcie); - - /* pulse for common reset */ - exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET); - udelay(500); - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET); - - exynos_pcie_deassert_core_reset(exynos_pcie); - dw_pcie_setup_rc(pp); - exynos_pcie_assert_reset(exynos_pcie); - - /* assert LTSSM enable */ - exynos_elb_writel(exynos_pcie, PCIE_ELBI_LTSSM_ENABLE, - PCIE_APP_LTSSM_ENABLE); - - /* check if the link is up or not */ - if (!dw_pcie_wait_for_link(pp)) - return 0; - - while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) { - val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED); - dev_info(dev, "PLL Locked: 0x%x\n", val); - } - exynos_pcie_power_off_phy(exynos_pcie); - return -ETIMEDOUT; -} - -static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *exynos_pcie) -{ - u32 val; - - val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_PULSE); - exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_PULSE); -} - -static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *exynos_pcie) -{ - u32 val; - - /* enable INTX interrupt */ - val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | - IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; - exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_PULSE); -} - -static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) -{ - struct exynos_pcie *exynos_pcie = arg; - - exynos_pcie_clear_irq_pulse(exynos_pcie); - return IRQ_HANDLED; -} - -static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) -{ - struct exynos_pcie *exynos_pcie = arg; - struct pcie_port *pp = &exynos_pcie->pp; - - return dw_handle_msi_irq(pp); -} - -static void exynos_pcie_msi_init(struct exynos_pcie *exynos_pcie) -{ - struct pcie_port *pp = &exynos_pcie->pp; - u32 val; - - dw_pcie_msi_init(pp); - - /* enable MSI interrupt */ - val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_EN_LEVEL); - val |= IRQ_MSI_ENABLE; - exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL); -} - -static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie) -{ - exynos_pcie_enable_irq_pulse(exynos_pcie); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - exynos_pcie_msi_init(exynos_pcie); -} - -static u32 exynos_pcie_readl_rc(struct pcie_port *pp, u32 reg) -{ - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - u32 val; - - exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true); - val = readl(pp->dbi_base + reg); - exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false); - return val; -} - -static void exynos_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val) -{ - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - - exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true); - writel(val, pp->dbi_base + reg); - exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false); -} - -static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) -{ - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - int ret; - - exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true); - ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false); - return ret; -} - -static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) -{ - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - int ret; - - exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true); - ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false); - return ret; -} - -static int exynos_pcie_link_up(struct pcie_port *pp) -{ - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - u32 val; - - val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP); - if (val == PCIE_ELBI_LTSSM_ENABLE) - return 1; - - return 0; -} - -static void exynos_pcie_host_init(struct pcie_port *pp) -{ - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - - exynos_pcie_establish_link(exynos_pcie); - exynos_pcie_enable_interrupts(exynos_pcie); -} - -static struct pcie_host_ops exynos_pcie_host_ops = { - .readl_rc = exynos_pcie_readl_rc, - .writel_rc = exynos_pcie_writel_rc, - .rd_own_conf = exynos_pcie_rd_own_conf, - .wr_own_conf = exynos_pcie_wr_own_conf, - .link_up = exynos_pcie_link_up, - .host_init = exynos_pcie_host_init, -}; - -static int __init exynos_add_pcie_port(struct exynos_pcie *exynos_pcie, - struct platform_device *pdev) -{ - struct pcie_port *pp = &exynos_pcie->pp; - struct device *dev = pp->dev; - int ret; - - pp->irq = platform_get_irq(pdev, 1); - if (!pp->irq) { - dev_err(dev, "failed to get irq\n"); - return -ENODEV; - } - ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler, - IRQF_SHARED, "exynos-pcie", exynos_pcie); - if (ret) { - dev_err(dev, "failed to request irq\n"); - return ret; - } - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - pp->msi_irq = platform_get_irq(pdev, 0); - if (!pp->msi_irq) { - dev_err(dev, "failed to get msi irq\n"); - return -ENODEV; - } - - ret = devm_request_irq(dev, pp->msi_irq, - exynos_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "exynos-pcie", exynos_pcie); - if (ret) { - dev_err(dev, "failed to request msi irq\n"); - return ret; - } - } - - pp->root_bus_nr = -1; - pp->ops = &exynos_pcie_host_ops; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - return 0; -} - -static int __init exynos_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct exynos_pcie *exynos_pcie; - struct pcie_port *pp; - struct device_node *np = dev->of_node; - struct resource *elbi_base; - struct resource *phy_base; - struct resource *block_base; - int ret; - - exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL); - if (!exynos_pcie) - return -ENOMEM; - - pp = &exynos_pcie->pp; - pp->dev = dev; - - exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); - - exynos_pcie->clk = devm_clk_get(dev, "pcie"); - if (IS_ERR(exynos_pcie->clk)) { - dev_err(dev, "Failed to get pcie rc clock\n"); - return PTR_ERR(exynos_pcie->clk); - } - ret = clk_prepare_enable(exynos_pcie->clk); - if (ret) - return ret; - - exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus"); - if (IS_ERR(exynos_pcie->bus_clk)) { - dev_err(dev, "Failed to get pcie bus clock\n"); - ret = PTR_ERR(exynos_pcie->bus_clk); - goto fail_clk; - } - ret = clk_prepare_enable(exynos_pcie->bus_clk); - if (ret) - goto fail_clk; - - elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - exynos_pcie->elbi_base = devm_ioremap_resource(dev, elbi_base); - if (IS_ERR(exynos_pcie->elbi_base)) { - ret = PTR_ERR(exynos_pcie->elbi_base); - goto fail_bus_clk; - } - - phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1); - exynos_pcie->phy_base = devm_ioremap_resource(dev, phy_base); - if (IS_ERR(exynos_pcie->phy_base)) { - ret = PTR_ERR(exynos_pcie->phy_base); - goto fail_bus_clk; - } - - block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2); - exynos_pcie->block_base = devm_ioremap_resource(dev, block_base); - if (IS_ERR(exynos_pcie->block_base)) { - ret = PTR_ERR(exynos_pcie->block_base); - goto fail_bus_clk; - } - - ret = exynos_add_pcie_port(exynos_pcie, pdev); - if (ret < 0) - goto fail_bus_clk; - - platform_set_drvdata(pdev, exynos_pcie); - return 0; - -fail_bus_clk: - clk_disable_unprepare(exynos_pcie->bus_clk); -fail_clk: - clk_disable_unprepare(exynos_pcie->clk); - return ret; -} - -static int __exit exynos_pcie_remove(struct platform_device *pdev) -{ - struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev); - - clk_disable_unprepare(exynos_pcie->bus_clk); - clk_disable_unprepare(exynos_pcie->clk); - - return 0; -} - -static const struct of_device_id exynos_pcie_of_match[] = { - { .compatible = "samsung,exynos5440-pcie", }, - {}, -}; - -static struct platform_driver exynos_pcie_driver = { - .remove = __exit_p(exynos_pcie_remove), - .driver = { - .name = "exynos-pcie", - .of_match_table = exynos_pcie_of_match, - }, -}; - -/* Exynos PCIe driver does not allow module unload */ - -static int __init exynos_pcie_init(void) -{ - return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe); -} -subsys_initcall(exynos_pcie_init); diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c index e3c48b5deb93..e9a53bae1c25 100644 --- a/drivers/pci/host/pci-host-common.c +++ b/drivers/pci/host/pci-host-common.c @@ -145,7 +145,9 @@ int pci_host_common_probe(struct platform_device *pdev, return -ENODEV; } +#ifdef CONFIG_ARM pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); +#endif /* * We insert PCI resources into the iomem_resource and diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 3efcc7bdc5fb..ada98569b78e 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -130,7 +130,8 @@ union pci_version { */ union win_slot_encoding { struct { - u32 func:8; + u32 dev:5; + u32 func:3; u32 reserved:24; } bits; u32 slot; @@ -485,7 +486,8 @@ static u32 devfn_to_wslot(int devfn) union win_slot_encoding wslot; wslot.slot = 0; - wslot.bits.func = PCI_SLOT(devfn) | (PCI_FUNC(devfn) << 5); + wslot.bits.dev = PCI_SLOT(devfn); + wslot.bits.func = PCI_FUNC(devfn); return wslot.slot; } @@ -503,7 +505,7 @@ static int wslot_to_devfn(u32 wslot) union win_slot_encoding slot_no; slot_no.slot = wslot; - return PCI_DEVFN(0, slot_no.bits.func); + return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func); } /* @@ -1315,6 +1317,18 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus, get_pcichild(hpdev, hv_pcidev_ref_initial); get_pcichild(hpdev, hv_pcidev_ref_childlist); spin_lock_irqsave(&hbus->device_list_lock, flags); + + /* + * When a device is being added to the bus, we set the PCI domain + * number to be the device serial number, which is non-zero and + * unique on the same VM. The serial numbers start with 1, and + * increase by 1 for each device. So device names including this + * can have shorter names than based on the bus instance UUID. + * Only the first device serial number is used for domain, so the + * domain number will not change after the first device is added. + */ + if (list_empty(&hbus->children)) + hbus->sysdata.domain = desc->ser; list_add_tail(&hpdev->list_entry, &hbus->children); spin_unlock_irqrestore(&hbus->device_list_lock, flags); return hpdev; diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 45a89d969700..cd7d51988738 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -133,6 +133,12 @@ struct mvebu_pcie { int nports; }; +struct mvebu_pcie_window { + phys_addr_t base; + phys_addr_t remap; + size_t size; +}; + /* Structure representing one PCIe interface */ struct mvebu_pcie_port { char *name; @@ -150,10 +156,8 @@ struct mvebu_pcie_port { struct mvebu_sw_pci_bridge bridge; struct device_node *dn; struct mvebu_pcie *pcie; - phys_addr_t memwin_base; - size_t memwin_size; - phys_addr_t iowin_base; - size_t iowin_size; + struct mvebu_pcie_window memwin; + struct mvebu_pcie_window iowin; u32 saved_pcie_stat; }; @@ -379,23 +383,45 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, } } +static void mvebu_pcie_set_window(struct mvebu_pcie_port *port, + unsigned int target, unsigned int attribute, + const struct mvebu_pcie_window *desired, + struct mvebu_pcie_window *cur) +{ + if (desired->base == cur->base && desired->remap == cur->remap && + desired->size == cur->size) + return; + + if (cur->size != 0) { + mvebu_pcie_del_windows(port, cur->base, cur->size); + cur->size = 0; + cur->base = 0; + + /* + * If something tries to change the window while it is enabled + * the change will not be done atomically. That would be + * difficult to do in the general case. + */ + } + + if (desired->size == 0) + return; + + mvebu_pcie_add_windows(port, target, attribute, desired->base, + desired->size, desired->remap); + *cur = *desired; +} + static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) { - phys_addr_t iobase; + struct mvebu_pcie_window desired = {}; /* Are the new iobase/iolimit values invalid? */ if (port->bridge.iolimit < port->bridge.iobase || port->bridge.iolimitupper < port->bridge.iobaseupper || !(port->bridge.command & PCI_COMMAND_IO)) { - - /* If a window was configured, remove it */ - if (port->iowin_base) { - mvebu_pcie_del_windows(port, port->iowin_base, - port->iowin_size); - port->iowin_base = 0; - port->iowin_size = 0; - } - + mvebu_pcie_set_window(port, port->io_target, port->io_attr, + &desired, &port->iowin); return; } @@ -412,32 +438,27 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) * specifications. iobase is the bus address, port->iowin_base * is the CPU address. */ - iobase = ((port->bridge.iobase & 0xF0) << 8) | - (port->bridge.iobaseupper << 16); - port->iowin_base = port->pcie->io.start + iobase; - port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | - (port->bridge.iolimitupper << 16)) - - iobase) + 1; - - mvebu_pcie_add_windows(port, port->io_target, port->io_attr, - port->iowin_base, port->iowin_size, - iobase); + desired.remap = ((port->bridge.iobase & 0xF0) << 8) | + (port->bridge.iobaseupper << 16); + desired.base = port->pcie->io.start + desired.remap; + desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | + (port->bridge.iolimitupper << 16)) - + desired.remap) + + 1; + + mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, + &port->iowin); } static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) { + struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP}; + /* Are the new membase/memlimit values invalid? */ if (port->bridge.memlimit < port->bridge.membase || !(port->bridge.command & PCI_COMMAND_MEMORY)) { - - /* If a window was configured, remove it */ - if (port->memwin_base) { - mvebu_pcie_del_windows(port, port->memwin_base, - port->memwin_size); - port->memwin_base = 0; - port->memwin_size = 0; - } - + mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, + &desired, &port->memwin); return; } @@ -447,14 +468,12 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) * window to setup, according to the PCI-to-PCI bridge * specifications. */ - port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16); - port->memwin_size = - (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - - port->memwin_base + 1; - - mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr, - port->memwin_base, port->memwin_size, - MVEBU_MBUS_NO_REMAP); + desired.base = ((port->bridge.membase & 0xFFF0) << 16); + desired.size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - + desired.base + 1; + + mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, + &port->memwin); } /* @@ -1162,7 +1181,7 @@ static int mvebu_pcie_powerup(struct mvebu_pcie_port *port) return ret; if (port->reset_gpio) { - u32 reset_udelay = 20000; + u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000; of_property_read_u32(port->dn, "reset-delay-us", &reset_udelay); diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c index af722eb0ca75..52b5bdccf5f0 100644 --- a/drivers/pci/host/pci-thunder-pem.c +++ b/drivers/pci/host/pci-thunder-pem.c @@ -36,7 +36,7 @@ struct thunder_pem_pci { static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { - u64 read_val; + u64 read_val, tmp_val; struct pci_config_window *cfg = bus->sysdata; struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv; @@ -65,13 +65,28 @@ static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, read_val |= 0x00007000; /* Skip MSI CAP */ break; case 0x70: /* Express Cap */ - /* PME interrupt on vector 2*/ - read_val |= (2u << 25); + /* + * Change PME interrupt to vector 2 on T88 where it + * reads as 0, else leave it alone. + */ + if (!(read_val & (0x1f << 25))) + read_val |= (2u << 25); break; case 0xb0: /* MSI-X Cap */ - /* TableSize=4, Next Cap is EA */ + /* TableSize=2 or 4, Next Cap is EA */ read_val &= 0xc00000ff; - read_val |= 0x0003bc00; + /* + * If Express Cap(0x70) raw PME vector reads as 0 we are on + * T88 and TableSize is reported as 4, else TableSize + * is 2. + */ + writeq(0x70, pem_pci->pem_reg_base + PEM_CFG_RD); + tmp_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); + tmp_val >>= 32; + if (!(tmp_val & (0x1f << 25))) + read_val |= 0x0003bc00; + else + read_val |= 0x0001bc00; break; case 0xb4: /* Table offset=0, BIR=0 */ diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c index b7dc07002f13..5ebee7d37ff5 100644 --- a/drivers/pci/host/pci-versatile.c +++ b/drivers/pci/host/pci-versatile.c @@ -124,7 +124,7 @@ static int versatile_pci_probe(struct platform_device *pdev) int ret, i, myslot = -1; u32 val; void __iomem *local_pci_cfg_base; - struct pci_bus *bus; + struct pci_bus *bus, *child; LIST_HEAD(pci_res); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -204,6 +204,8 @@ static int versatile_pci_probe(struct platform_device *pdev) pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); pci_assign_unassigned_bus_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); pci_bus_add_devices(bus); return 0; diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index 7c3b54b9eb17..1a6108788f6f 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -246,14 +246,11 @@ static int xgene_pcie_ecam_init(struct pci_config_window *cfg, u32 ipversion) ret = xgene_get_csr_resource(adev, &csr); if (ret) { dev_err(dev, "can't get CSR resource\n"); - kfree(port); return ret; } port->csr_base = devm_ioremap_resource(dev, &csr); - if (IS_ERR(port->csr_base)) { - kfree(port); - return -ENOMEM; - } + if (IS_ERR(port->csr_base)) + return PTR_ERR(port->csr_base); port->cfg_base = cfg->win; port->version = ipversion; @@ -638,7 +635,7 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) struct device_node *dn = dev->of_node; struct xgene_pcie_port *port; resource_size_t iobase = 0; - struct pci_bus *bus; + struct pci_bus *bus, *child; int ret; LIST_HEAD(res); @@ -681,6 +678,8 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) pci_scan_child_bus(bus); pci_assign_unassigned_bus_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); pci_bus_add_devices(bus); return 0; diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index 0c1540225ca3..75ec5cea26f6 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -57,15 +57,19 @@ #define TLP_WRITE_TAG 0x10 #define RP_DEVFN 0 #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) -#define TLP_CFG_DW0(pcie, bus) \ +#define TLP_CFGRD_DW0(pcie, bus) \ ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \ : TLP_FMTTYPE_CFGRD1) << 24) | \ TLP_PAYLOAD_SIZE) +#define TLP_CFGWR_DW0(pcie, bus) \ + ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \ + : TLP_FMTTYPE_CFGWR1) << 24) | \ + TLP_PAYLOAD_SIZE) #define TLP_CFG_DW1(pcie, tag, be) \ (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) #define TLP_CFG_DW2(bus, devfn, offset) \ (((bus) << 24) | ((devfn) << 16) | (offset)) -#define TLP_COMP_STATUS(s) (((s) >> 12) & 7) +#define TLP_COMP_STATUS(s) (((s) >> 13) & 7) #define TLP_HDR_SIZE 3 #define TLP_LOOP 500 @@ -222,7 +226,7 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, { u32 headers[TLP_HDR_SIZE]; - headers[0] = TLP_CFG_DW0(pcie, bus); + headers[0] = TLP_CFGRD_DW0(pcie, bus); headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); headers[2] = TLP_CFG_DW2(bus, devfn, where); @@ -237,7 +241,7 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, u32 headers[TLP_HDR_SIZE]; int ret; - headers[0] = TLP_CFG_DW0(pcie, bus); + headers[0] = TLP_CFGWR_DW0(pcie, bus); headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); headers[2] = TLP_CFG_DW2(bus, devfn, where); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h deleted file mode 100644 index a567ea288ee2..000000000000 --- a/drivers/pci/host/pcie-designware.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Synopsys Designware PCIe host controller driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Jingoo Han <jg1.han@samsung.com> - * - * 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_DESIGNWARE_H -#define _PCIE_DESIGNWARE_H - -/* - * Maximum number of MSI IRQs can be 256 per controller. But keep - * it 32 as of now. Probably we will never need more than 32. If needed, - * then increment it in multiple of 32. - */ -#define MAX_MSI_IRQS 32 -#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) - -struct pcie_port { - struct device *dev; - u8 root_bus_nr; - void __iomem *dbi_base; - u64 cfg0_base; - void __iomem *va_cfg0_base; - u32 cfg0_size; - u64 cfg1_base; - void __iomem *va_cfg1_base; - u32 cfg1_size; - resource_size_t io_base; - phys_addr_t io_bus_addr; - u32 io_size; - u64 mem_base; - phys_addr_t mem_bus_addr; - u32 mem_size; - struct resource *cfg; - struct resource *io; - struct resource *mem; - struct resource *busn; - int irq; - u32 lanes; - u32 num_viewport; - struct pcie_host_ops *ops; - int msi_irq; - struct irq_domain *irq_domain; - unsigned long msi_data; - u8 iatu_unroll_enabled; - DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); -}; - -struct pcie_host_ops { - u32 (*readl_rc)(struct pcie_port *pp, u32 reg); - void (*writel_rc)(struct pcie_port *pp, u32 reg, u32 val); - int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); - int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); - int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 *val); - int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, - unsigned int devfn, int where, int size, u32 val); - int (*link_up)(struct pcie_port *pp); - void (*host_init)(struct pcie_port *pp); - void (*msi_set_irq)(struct pcie_port *pp, int irq); - void (*msi_clear_irq)(struct pcie_port *pp, int irq); - phys_addr_t (*get_msi_addr)(struct pcie_port *pp); - u32 (*get_msi_data)(struct pcie_port *pp, int pos); - void (*scan_bus)(struct pcie_port *pp); - int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); -}; - -u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg); -void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val); -int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val); -int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val); -irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); -void dw_pcie_msi_init(struct pcie_port *pp); -int dw_pcie_wait_for_link(struct pcie_port *pp); -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); - -#endif /* _PCIE_DESIGNWARE_H */ diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c index 22d814a78a78..f4909bb0b2ad 100644 --- a/drivers/pci/host/pcie-iproc-platform.c +++ b/drivers/pci/host/pcie-iproc-platform.c @@ -47,7 +47,6 @@ MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table); static int iproc_pcie_pltfm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id; struct iproc_pcie *pcie; struct device_node *np = dev->of_node; struct resource reg; @@ -55,16 +54,12 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) LIST_HEAD(res); int ret; - of_id = of_match_device(iproc_pcie_of_match_table, dev); - if (!of_id) - return -EINVAL; - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; pcie->dev = dev; - pcie->type = (enum iproc_pcie_type)of_id->data; + pcie->type = (enum iproc_pcie_type) of_device_get_match_data(dev); ret = of_address_to_resource(np, 0, ®); if (ret < 0) { diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index 3ebc025499b9..0f39bd2a04cb 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -1205,7 +1205,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) struct device *dev; int ret; void *sysdata; - struct pci_bus *bus; + struct pci_bus *bus, *child; dev = pcie->dev; @@ -1278,6 +1278,9 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) if (pcie->map_irq) pci_fixup_irqs(pci_common_swizzle, pcie->map_irq); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + pci_bus_add_devices(bus); return 0; diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index aca85be101f8..cb07c45c1858 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -1125,7 +1125,6 @@ static int rcar_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rcar_pcie *pcie; unsigned int data; - const struct of_device_id *of_id; int err; int (*hw_init_fn)(struct rcar_pcie *); @@ -1149,11 +1148,6 @@ static int rcar_pcie_probe(struct platform_device *pdev) if (err) return err; - of_id = of_match_device(rcar_pcie_of_match, dev); - if (!of_id || !of_id->data) - return -EINVAL; - hw_init_fn = of_id->data; - pm_runtime_enable(dev); err = pm_runtime_get_sync(dev); if (err < 0) { @@ -1162,10 +1156,11 @@ static int rcar_pcie_probe(struct platform_device *pdev) } /* Failure to get a link might just be that no cards are inserted */ + hw_init_fn = of_device_get_match_data(dev); err = hw_init_fn(pcie); if (err) { dev_info(dev, "PCIe link down\n"); - err = 0; + err = -ENODEV; goto err_pm_put; } diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c index f2dca7bb0b39..26ddd3535272 100644 --- a/drivers/pci/host/pcie-rockchip.c +++ b/drivers/pci/host/pcie-rockchip.c @@ -20,6 +20,7 @@ #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/iopoll.h> #include <linux/irq.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> @@ -55,6 +56,10 @@ #define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040) #define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0) #define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080) +#define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c) +#define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0) +#define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18 +#define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19 #define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48) #define PCIE_CLIENT_LINK_STATUS_UP 0x00300000 #define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000 @@ -120,6 +125,7 @@ #define PCIE_CORE_INT_CT BIT(11) #define PCIE_CORE_INT_UTC BIT(18) #define PCIE_CORE_INT_MMVC BIT(19) +#define PCIE_CORE_CONFIG_VENDOR (PCIE_CORE_CTRL_MGMT_BASE + 0x44) #define PCIE_CORE_INT_MASK (PCIE_CORE_CTRL_MGMT_BASE + 0x210) #define PCIE_RC_BAR_CONF (PCIE_CORE_CTRL_MGMT_BASE + 0x300) @@ -133,13 +139,14 @@ PCIE_CORE_INT_MMVC) #define PCIE_RC_CONFIG_BASE 0xa00000 -#define PCIE_RC_CONFIG_VENDOR (PCIE_RC_CONFIG_BASE + 0x00) #define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08) #define PCIE_RC_CONFIG_SCC_SHIFT 16 #define PCIE_RC_CONFIG_DCR (PCIE_RC_CONFIG_BASE + 0xc4) #define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18 #define PCIE_RC_CONFIG_DCR_CSPL_LIMIT 0xff #define PCIE_RC_CONFIG_DCR_CPLS_SHIFT 26 +#define PCIE_RC_CONFIG_LINK_CAP (PCIE_RC_CONFIG_BASE + 0xcc) +#define PCIE_RC_CONFIG_LINK_CAP_L0S BIT(10) #define PCIE_RC_CONFIG_LCS (PCIE_RC_CONFIG_BASE + 0xd0) #define PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2 (PCIE_RC_CONFIG_BASE + 0x90c) #define PCIE_RC_CONFIG_THP_CAP (PCIE_RC_CONFIG_BASE + 0x274) @@ -167,9 +174,11 @@ #define IB_ROOT_PORT_REG_SIZE_SHIFT 3 #define AXI_WRAPPER_IO_WRITE 0x6 #define AXI_WRAPPER_MEM_WRITE 0x2 +#define AXI_WRAPPER_NOR_MSG 0xc #define MAX_AXI_IB_ROOTPORT_REGION_NUM 3 #define MIN_AXI_ADDR_BITS_PASSED 8 +#define PCIE_RC_SEND_PME_OFF 0x11960 #define ROCKCHIP_VENDOR_ID 0x1d87 #define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20) #define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15) @@ -178,6 +187,12 @@ #define PCIE_ECAM_ADDR(bus, dev, func, reg) \ (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \ PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg)) +#define PCIE_LINK_IS_L2(x) \ + (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2) +#define PCIE_LINK_UP(x) \ + (((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP) +#define PCIE_LINK_IS_GEN2(x) \ + (((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G) #define RC_REGION_0_ADDR_TRANS_H 0x00000000 #define RC_REGION_0_ADDR_TRANS_L 0x00000000 @@ -211,7 +226,9 @@ struct rockchip_pcie { u32 io_size; int offset; phys_addr_t io_bus_addr; + void __iomem *msg_region; u32 mem_size; + phys_addr_t msg_bus_addr; phys_addr_t mem_bus_addr; }; @@ -449,7 +466,6 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) struct device *dev = rockchip->dev; int err; u32 status; - unsigned long timeout; gpiod_set_value(rockchip->ep_gpio, 0); @@ -590,23 +606,12 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) gpiod_set_value(rockchip->ep_gpio, 1); /* 500ms timeout value should be enough for Gen1/2 training */ - timeout = jiffies + msecs_to_jiffies(500); - - for (;;) { - status = rockchip_pcie_read(rockchip, - PCIE_CLIENT_BASIC_STATUS1); - if ((status & PCIE_CLIENT_LINK_STATUS_MASK) == - PCIE_CLIENT_LINK_STATUS_UP) { - dev_dbg(dev, "PCIe link training gen1 pass!\n"); - break; - } - - if (time_after(jiffies, timeout)) { - dev_err(dev, "PCIe link training gen1 timeout!\n"); - return -ETIMEDOUT; - } - - msleep(20); + err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1, + status, PCIE_LINK_UP(status), 20, + 500 * USEC_PER_MSEC); + if (err) { + dev_err(dev, "PCIe link training gen1 timeout!\n"); + return -ETIMEDOUT; } if (rockchip->link_gen == 2) { @@ -618,22 +623,11 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) status |= PCI_EXP_LNKCTL_RL; rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS); - timeout = jiffies + msecs_to_jiffies(500); - for (;;) { - status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL); - if ((status & PCIE_CORE_PL_CONF_SPEED_MASK) == - PCIE_CORE_PL_CONF_SPEED_5G) { - dev_dbg(dev, "PCIe link training gen2 pass!\n"); - break; - } - - if (time_after(jiffies, timeout)) { - dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n"); - break; - } - - msleep(20); - } + err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL, + status, PCIE_LINK_IS_GEN2(status), 20, + 500 * USEC_PER_MSEC); + if (err) + dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n"); } /* Check the final link width from negotiated lane counter from MGMT */ @@ -643,7 +637,7 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) dev_dbg(dev, "current link width is x%d\n", status); rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID, - PCIE_RC_CONFIG_VENDOR); + PCIE_CORE_CONFIG_VENDOR); rockchip_pcie_write(rockchip, PCI_CLASS_BRIDGE_PCI << PCIE_RC_CONFIG_SCC_SHIFT, PCIE_RC_CONFIG_RID_CCR); @@ -653,6 +647,13 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) status &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK; rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_THP_CAP); + /* Clear L0s from RC's link cap */ + if (of_property_read_bool(dev->of_node, "aspm-no-l0s")) { + status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LINK_CAP); + status &= ~PCIE_RC_CONFIG_LINK_CAP_L0S; + rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LINK_CAP); + } + rockchip_pcie_write(rockchip, 0x0, PCIE_RC_BAR_CONF); rockchip_pcie_write(rockchip, @@ -1186,6 +1187,85 @@ static int rockchip_cfg_atu(struct rockchip_pcie *rockchip) } } + /* assign message regions */ + rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset, + AXI_WRAPPER_NOR_MSG, + 20 - 1, 0, 0); + + rockchip->msg_bus_addr = rockchip->mem_bus_addr + + ((reg_no + offset) << 20); + return err; +} + +static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip) +{ + u32 value; + int err; + + /* send PME_TURN_OFF message */ + writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF); + + /* read LTSSM and wait for falling into L2 link state */ + err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0, + value, PCIE_LINK_IS_L2(value), 20, + jiffies_to_usecs(5 * HZ)); + if (err) { + dev_err(rockchip->dev, "PCIe link enter L2 timeout!\n"); + return err; + } + + return 0; +} + +static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) +{ + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + int ret; + + /* disable core and cli int since we don't need to ack PME_ACK */ + rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) | + PCIE_CLIENT_INT_CLI, PCIE_CLIENT_INT_MASK); + rockchip_pcie_write(rockchip, (u32)PCIE_CORE_INT, PCIE_CORE_INT_MASK); + + ret = rockchip_pcie_wait_l2(rockchip); + if (ret) { + rockchip_pcie_enable_interrupts(rockchip); + return ret; + } + + phy_power_off(rockchip->phy); + phy_exit(rockchip->phy); + + clk_disable_unprepare(rockchip->clk_pcie_pm); + clk_disable_unprepare(rockchip->hclk_pcie); + clk_disable_unprepare(rockchip->aclk_perf_pcie); + clk_disable_unprepare(rockchip->aclk_pcie); + + return ret; +} + +static int __maybe_unused rockchip_pcie_resume_noirq(struct device *dev) +{ + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); + int err; + + clk_prepare_enable(rockchip->clk_pcie_pm); + clk_prepare_enable(rockchip->hclk_pcie); + clk_prepare_enable(rockchip->aclk_perf_pcie); + clk_prepare_enable(rockchip->aclk_pcie); + + err = rockchip_pcie_init_port(rockchip); + if (err) + return err; + + err = rockchip_cfg_atu(rockchip); + if (err) + return err; + + /* Need this to enter L1 again */ + rockchip_pcie_update_txcredit_mui(rockchip); + rockchip_pcie_enable_interrupts(rockchip); + return 0; } @@ -1209,6 +1289,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) if (!rockchip) return -ENOMEM; + platform_set_drvdata(pdev, rockchip); rockchip->dev = dev; err = rockchip_pcie_parse_dt(rockchip); @@ -1262,7 +1343,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) err = devm_request_pci_bus_resources(dev, &res); if (err) - goto err_vpcie; + goto err_free_res; /* Get the I/O and memory ranges from DT */ resource_list_for_each_entry(win, &res) { @@ -1295,11 +1376,19 @@ static int rockchip_pcie_probe(struct platform_device *pdev) err = rockchip_cfg_atu(rockchip); if (err) - goto err_vpcie; + goto err_free_res; + + rockchip->msg_region = devm_ioremap(rockchip->dev, + rockchip->msg_bus_addr, SZ_1M); + if (!rockchip->msg_region) { + err = -ENOMEM; + goto err_free_res; + } + bus = pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res); if (!bus) { err = -ENOMEM; - goto err_vpcie; + goto err_free_res; } pci_bus_size_bridges(bus); @@ -1310,6 +1399,8 @@ static int rockchip_pcie_probe(struct platform_device *pdev) pci_bus_add_devices(bus); return err; +err_free_res: + pci_free_resource_list(&res); err_vpcie: if (!IS_ERR(rockchip->vpcie3v3)) regulator_disable(rockchip->vpcie3v3); @@ -1329,6 +1420,11 @@ err_aclk_pcie: return err; } +static const struct dev_pm_ops rockchip_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq, + rockchip_pcie_resume_noirq) +}; + static const struct of_device_id rockchip_pcie_of_match[] = { { .compatible = "rockchip,rk3399-pcie", }, {} @@ -1338,6 +1434,7 @@ static struct platform_driver rockchip_pcie_driver = { .driver = { .name = "rockchip-pcie", .of_match_table = rockchip_pcie_of_match, + .pm = &rockchip_pcie_pm_ops, }, .probe = rockchip_pcie_probe, diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c index 43eaa4afab94..4c3e0ab35496 100644 --- a/drivers/pci/host/pcie-xilinx-nwl.c +++ b/drivers/pci/host/pcie-xilinx-nwl.c @@ -62,21 +62,9 @@ #define CFG_ENABLE_PM_MSG_FWD BIT(1) #define CFG_ENABLE_INT_MSG_FWD BIT(2) #define CFG_ENABLE_ERR_MSG_FWD BIT(3) -#define CFG_ENABLE_SLT_MSG_FWD BIT(5) -#define CFG_ENABLE_VEN_MSG_FWD BIT(7) -#define CFG_ENABLE_OTH_MSG_FWD BIT(13) -#define CFG_ENABLE_VEN_MSG_EN BIT(14) -#define CFG_ENABLE_VEN_MSG_VEN_INV BIT(15) -#define CFG_ENABLE_VEN_MSG_VEN_ID GENMASK(31, 16) #define CFG_ENABLE_MSG_FILTER_MASK (CFG_ENABLE_PM_MSG_FWD | \ CFG_ENABLE_INT_MSG_FWD | \ - CFG_ENABLE_ERR_MSG_FWD | \ - CFG_ENABLE_SLT_MSG_FWD | \ - CFG_ENABLE_VEN_MSG_FWD | \ - CFG_ENABLE_OTH_MSG_FWD | \ - CFG_ENABLE_VEN_MSG_EN | \ - CFG_ENABLE_VEN_MSG_VEN_INV | \ - CFG_ENABLE_VEN_MSG_VEN_ID) + CFG_ENABLE_ERR_MSG_FWD) /* Misc interrupt status mask bits */ #define MSGF_MISC_SR_RXMSG_AVAIL BIT(0) diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index c8616fadccf1..7f030f5d750b 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -632,7 +632,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct xilinx_pcie_port *port; - struct pci_bus *bus; + struct pci_bus *bus, *child; int err; resource_size_t iobase = 0; LIST_HEAD(res); @@ -686,6 +686,8 @@ static int xilinx_pcie_probe(struct platform_device *pdev) #ifndef CONFIG_MICROBLAZE pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); #endif + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); pci_bus_add_devices(bus); return 0; diff --git a/drivers/pci/host/vmd.c b/drivers/pci/host/vmd.c index 18ef1a93c10a..e27ad2a3bd33 100644 --- a/drivers/pci/host/vmd.c +++ b/drivers/pci/host/vmd.c @@ -282,7 +282,7 @@ static struct device *to_vmd_dev(struct device *dev) return &vmd->dev->dev; } -static struct dma_map_ops *vmd_dma_ops(struct device *dev) +static const struct dma_map_ops *vmd_dma_ops(struct device *dev) { return get_dma_ops(to_vmd_dev(dev)); } diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 68d105aaf4e2..984c7e8cec5a 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -107,7 +107,7 @@ static void __exit ibm_acpiphp_exit(void); static acpi_handle ibm_acpi_handle; static struct notification ibm_note; -static struct bin_attribute ibm_apci_table_attr = { +static struct bin_attribute ibm_apci_table_attr __ro_after_init = { .attr = { .name = "apci_table", .mode = S_IRUGO, diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index 7ec8a8f72c69..95f689f53920 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h index 9103a7b9f3b9..48c8a066a6b7 100644 --- a/drivers/pci/hotplug/cpqphp.h +++ b/drivers/pci/hotplug/cpqphp.h @@ -32,7 +32,7 @@ #include <asm/io.h> /* for read? and write? functions */ #include <linux/delay.h> /* for delays */ #include <linux/mutex.h> -#include <linux/sched.h> /* for signal_pending() */ +#include <linux/sched/signal.h> /* for signal_pending() */ #define MY_NAME "cpqphp" diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 37d70b5ad22f..06109d40c4ac 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -33,7 +33,7 @@ #include <linux/pci.h> #include <linux/pci_hotplug.h> #include <linux/delay.h> -#include <linux/sched.h> /* signal_pending() */ +#include <linux/sched/signal.h> /* signal_pending() */ #include <linux/pcieport_if.h> #include <linux/mutex.h> #include <linux/workqueue.h> diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index d2961ef39a3a..7c203198b582 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -35,9 +35,11 @@ static void pnv_php_register(struct device_node *dn); static void pnv_php_unregister_one(struct device_node *dn); static void pnv_php_unregister(struct device_node *dn); -static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) +static void pnv_php_disable_irq(struct pnv_php_slot *php_slot, + bool disable_device) { struct pci_dev *pdev = php_slot->pdev; + int irq = php_slot->irq; u16 ctrl; if (php_slot->irq > 0) { @@ -56,10 +58,14 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) php_slot->wq = NULL; } - if (pdev->msix_enabled) - pci_disable_msix(pdev); - else if (pdev->msi_enabled) - pci_disable_msi(pdev); + if (disable_device || irq > 0) { + if (pdev->msix_enabled) + pci_disable_msix(pdev); + else if (pdev->msi_enabled) + pci_disable_msi(pdev); + + pci_disable_device(pdev); + } } static void pnv_php_free_slot(struct kref *kref) @@ -68,7 +74,7 @@ static void pnv_php_free_slot(struct kref *kref) struct pnv_php_slot, kref); WARN_ON(!list_empty(&php_slot->children)); - pnv_php_disable_irq(php_slot); + pnv_php_disable_irq(php_slot, false); kfree(php_slot->name); kfree(php_slot); } @@ -76,7 +82,7 @@ static void pnv_php_free_slot(struct kref *kref) static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot) { - if (WARN_ON(!php_slot)) + if (!php_slot) return; kref_put(&php_slot->kref, pnv_php_free_slot); @@ -430,9 +436,21 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan) if (ret) return ret; - /* Proceed if there have nothing behind the slot */ - if (presence == OPAL_PCI_SLOT_EMPTY) + /* + * Proceed if there have nothing behind the slot. However, + * we should leave the slot in registered state at the + * beginning. Otherwise, the PCI devices inserted afterwards + * won't be probed and populated. + */ + if (presence == OPAL_PCI_SLOT_EMPTY) { + if (!php_slot->power_state_check) { + php_slot->power_state_check = true; + + return 0; + } + goto scan; + } /* * If the power supply to the slot is off, we can't detect @@ -705,10 +723,15 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data) if (sts & PCI_EXP_SLTSTA_DLLSC) { pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts); added = !!(lsts & PCI_EXP_LNKSTA_DLLLA); - } else if (sts & PCI_EXP_SLTSTA_PDC) { + } else if (!(php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) && + (sts & PCI_EXP_SLTSTA_PDC)) { ret = pnv_pci_get_presence_state(php_slot->id, &presence); - if (!ret) + if (ret) { + dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n", + php_slot->name, ret, sts); return IRQ_HANDLED; + } + added = !!(presence == OPAL_PCI_SLOT_PRESENT); } else { return IRQ_NONE; @@ -752,6 +775,7 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data) static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) { struct pci_dev *pdev = php_slot->pdev; + u32 broken_pdc = 0; u16 sts, ctrl; int ret; @@ -759,29 +783,44 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); if (!php_slot->wq) { dev_warn(&pdev->dev, "Cannot alloc workqueue\n"); - pnv_php_disable_irq(php_slot); + pnv_php_disable_irq(php_slot, true); return; } + /* Check PDC (Presence Detection Change) is broken or not */ + ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc", + &broken_pdc); + if (!ret && broken_pdc) + php_slot->flags |= PNV_PHP_FLAG_BROKEN_PDC; + /* Clear pending interrupts */ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); - sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); + if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) + sts |= PCI_EXP_SLTSTA_DLLSC; + else + sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); /* Request the interrupt */ ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED, php_slot->name, php_slot); if (ret) { - pnv_php_disable_irq(php_slot); + pnv_php_disable_irq(php_slot, true); dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq); return; } /* Enable the interrupts */ pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); - ctrl |= (PCI_EXP_SLTCTL_HPIE | - PCI_EXP_SLTCTL_PDCE | - PCI_EXP_SLTCTL_DLLSCE); + if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) { + ctrl &= ~PCI_EXP_SLTCTL_PDCE; + ctrl |= (PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_DLLSCE); + } else { + ctrl |= (PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_DLLSCE); + } pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); /* The interrupt is initialized successfully when @irq is valid */ @@ -793,6 +832,14 @@ static void pnv_php_enable_irq(struct pnv_php_slot *php_slot) struct pci_dev *pdev = php_slot->pdev; int irq, ret; + /* + * The MSI/MSIx interrupt might have been occupied by other + * drivers. Don't populate the surprise hotplug capability + * in that case. + */ + if (pci_dev_msi_enabled(pdev)) + return; + ret = pci_enable_device(pdev); if (ret) { dev_warn(&pdev->dev, "Error %d enabling device\n", ret); diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index c614ff7c3bc3..3f93a4e79595 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -463,7 +463,6 @@ static inline int is_dlpar_capable(void) int __init rpadlpar_io_init(void) { - int rc = 0; if (!is_dlpar_capable()) { printk(KERN_WARNING "%s: partition not DLPAR capable\n", @@ -471,8 +470,7 @@ int __init rpadlpar_io_init(void) return -EPERM; } - rc = dlpar_sysfs_init(); - return rc; + return dlpar_sysfs_init(); } void rpadlpar_io_exit(void) diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 4da8fc601467..70c7ea6af034 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -33,7 +33,7 @@ #include <linux/pci.h> #include <linux/pci_hotplug.h> #include <linux/delay.h> -#include <linux/sched.h> /* signal_pending(), struct timer_list */ +#include <linux/sched/signal.h> /* signal_pending(), struct timer_list */ #include <linux/mutex.h> #include <linux/workqueue.h> diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 47227820406d..2479ae876482 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -124,7 +124,6 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) struct pci_sriov *iov = dev->sriov; struct pci_bus *bus; - mutex_lock(&iov->dev->sriov->lock); bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id)); if (!bus) goto failed; @@ -162,7 +161,6 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) __pci_reset_function(virtfn); pci_device_add(virtfn, virtfn->bus); - mutex_unlock(&iov->dev->sriov->lock); pci_bus_add_device(virtfn); sprintf(buf, "virtfn%u", id); @@ -181,12 +179,10 @@ failed2: sysfs_remove_link(&dev->dev.kobj, buf); failed1: pci_dev_put(dev); - mutex_lock(&iov->dev->sriov->lock); pci_stop_and_remove_bus_device(virtfn); failed0: virtfn_remove_bus(dev->bus, bus); failed: - mutex_unlock(&iov->dev->sriov->lock); return rc; } @@ -195,7 +191,6 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset) { char buf[VIRTFN_ID_LEN]; struct pci_dev *virtfn; - struct pci_sriov *iov = dev->sriov; virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), pci_iov_virtfn_bus(dev, id), @@ -218,10 +213,8 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset) if (virtfn->dev.kobj.sd) sysfs_remove_link(&virtfn->dev.kobj, "physfn"); - mutex_lock(&iov->dev->sriov->lock); pci_stop_and_remove_bus_device(virtfn); virtfn_remove_bus(dev->bus, virtfn->bus); - mutex_unlock(&iov->dev->sriov->lock); /* balance pci_get_domain_bus_and_slot() */ pci_dev_put(virtfn); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 7f73bacf13ed..d571bc330686 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -32,32 +32,13 @@ int pci_msi_ignore_mask; #define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN -static struct irq_domain *pci_msi_default_domain; -static DEFINE_MUTEX(pci_msi_domain_lock); - -struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev) -{ - return pci_msi_default_domain; -} - -static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev) -{ - struct irq_domain *domain; - - domain = dev_get_msi_domain(&dev->dev); - if (domain) - return domain; - - return arch_get_pci_msi_domain(dev); -} - static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) { struct irq_domain *domain; - domain = pci_msi_get_domain(dev); + domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) - return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); + return msi_domain_alloc_irqs(domain, &dev->dev, nvec); return arch_setup_msi_irqs(dev, nvec, type); } @@ -66,9 +47,9 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) { struct irq_domain *domain; - domain = pci_msi_get_domain(dev); + domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) - pci_msi_domain_free_irqs(domain, dev); + msi_domain_free_irqs(domain, &dev->dev); else arch_teardown_msi_irqs(dev); } @@ -379,7 +360,7 @@ static void free_msi_irqs(struct pci_dev *dev) } list_del(&entry->list); - kfree(entry); + free_msi_entry(entry); } if (dev->msi_irq_groups) { @@ -610,7 +591,7 @@ static int msi_verify_entries(struct pci_dev *dev) * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * @nvec: number of interrupts to allocate - * @affinity: flag to indicate cpu irq affinity mask should be set + * @affd: description of automatic irq affinity assignments (may be %NULL) * * Setup the MSI capability structure of the device with the requested * number of interrupts. A return value of zero indicates the successful @@ -731,7 +712,7 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, ret = 0; out: kfree(masks); - return 0; + return ret; } static void msix_program_entries(struct pci_dev *dev, @@ -1084,7 +1065,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, if (nvec < 0) return nvec; if (nvec < minvec) - return -EINVAL; + return -ENOSPC; if (nvec > maxvec) nvec = maxvec; @@ -1109,23 +1090,15 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, } } -/** - * pci_enable_msi_range - configure device's MSI capability structure - * @dev: device to configure - * @minvec: minimal number of interrupts to configure - * @maxvec: maximum number of interrupts to configure - * - * This function tries to allocate a maximum possible number of interrupts in a - * range between @minvec and @maxvec. It returns a negative errno if an error - * occurs. If it succeeds, it returns the actual number of interrupts allocated - * and updates the @dev's irq member to the lowest new interrupt number; - * the other interrupt numbers allocated to this device are consecutive. - **/ -int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) +/* deprecated, don't use */ +int pci_enable_msi(struct pci_dev *dev) { - return __pci_enable_msi_range(dev, minvec, maxvec, NULL); + int rc = __pci_enable_msi_range(dev, 1, 1, NULL); + if (rc < 0) + return rc; + return 0; } -EXPORT_SYMBOL(pci_enable_msi_range); +EXPORT_SYMBOL(pci_enable_msi); static int __pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec, @@ -1235,9 +1208,11 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, } /* use legacy irq if allowed */ - if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1) { - pci_intx(dev, 1); - return 1; + if (flags & PCI_IRQ_LEGACY) { + if (min_vecs == 1 && dev->irq) { + pci_intx(dev, 1); + return 1; + } } return vecs; @@ -1323,6 +1298,22 @@ const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr) } EXPORT_SYMBOL(pci_irq_get_affinity); +/** + * pci_irq_get_node - return the numa node of a particular msi vector + * @pdev: PCI device to operate on + * @vec: device-relative interrupt vector index (0-based). + */ +int pci_irq_get_node(struct pci_dev *pdev, int vec) +{ + const struct cpumask *mask; + + mask = pci_irq_get_affinity(pdev, vec); + if (mask) + return local_memory_node(cpu_to_node(cpumask_first(mask))); + return dev_to_node(&pdev->dev); +} +EXPORT_SYMBOL(pci_irq_get_node); + struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc) { return to_pci_dev(desc->dev); @@ -1391,7 +1382,7 @@ int pci_msi_domain_check_cap(struct irq_domain *domain, { struct msi_desc *desc = first_pci_msi_entry(to_pci_dev(dev)); - /* Special handling to support pci_enable_msi_range() */ + /* Special handling to support __pci_enable_msi_range() */ if (pci_msi_desc_is_multi_msi(desc) && !(info->flags & MSI_FLAG_MULTI_PCI_MSI)) return 1; @@ -1404,7 +1395,7 @@ int pci_msi_domain_check_cap(struct irq_domain *domain, static int pci_msi_domain_handle_error(struct irq_domain *domain, struct msi_desc *desc, int error) { - /* Special handling to support pci_enable_msi_range() */ + /* Special handling to support __pci_enable_msi_range() */ if (pci_msi_desc_is_multi_msi(desc) && error == -ENOSPC) return 1; @@ -1491,59 +1482,6 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain); -/** - * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain - * @domain: The interrupt domain to allocate from - * @dev: The device for which to allocate - * @nvec: The number of interrupts to allocate - * @type: Unused to allow simpler migration from the arch_XXX interfaces - * - * Returns: - * A virtual interrupt number or an error code in case of failure - */ -int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev, - int nvec, int type) -{ - return msi_domain_alloc_irqs(domain, &dev->dev, nvec); -} - -/** - * pci_msi_domain_free_irqs - Free interrupts for @dev in @domain - * @domain: The interrupt domain - * @dev: The device for which to free interrupts - */ -void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev) -{ - msi_domain_free_irqs(domain, &dev->dev); -} - -/** - * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain - * @fwnode: Optional fwnode of the interrupt controller - * @info: MSI domain info - * @parent: Parent irq domain - * - * Returns: A domain pointer or NULL in case of failure. If successful - * the default PCI/MSI irqdomain pointer is updated. - */ -struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, struct irq_domain *parent) -{ - struct irq_domain *domain; - - mutex_lock(&pci_msi_domain_lock); - if (pci_msi_default_domain) { - pr_err("PCI: default irq domain for PCI MSI has already been created.\n"); - domain = NULL; - } else { - domain = pci_msi_create_irq_domain(fwnode, info, parent); - pci_msi_default_domain = domain; - } - mutex_unlock(&pci_msi_domain_lock); - - return domain; -} - static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) { u32 *pa = data; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 63d8e18fb6b1..afa72717a979 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -381,8 +381,6 @@ static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) id = pci_match_device(drv, pci_dev); if (id) error = pci_call_probe(drv, pci_dev, id); - if (error >= 0) - error = 0; } return error; } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 066628776e1b..25d010d449a3 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -472,6 +472,7 @@ static ssize_t sriov_numvfs_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); + struct pci_sriov *iov = pdev->sriov; int ret; u16 num_vfs; @@ -482,38 +483,46 @@ static ssize_t sriov_numvfs_store(struct device *dev, if (num_vfs > pci_sriov_get_totalvfs(pdev)) return -ERANGE; + mutex_lock(&iov->dev->sriov->lock); + if (num_vfs == pdev->sriov->num_VFs) - return count; /* no change */ + goto exit; /* is PF driver loaded w/callback */ if (!pdev->driver || !pdev->driver->sriov_configure) { dev_info(&pdev->dev, "Driver doesn't support SRIOV configuration via sysfs\n"); - return -ENOSYS; + ret = -ENOENT; + goto exit; } if (num_vfs == 0) { /* disable VFs */ ret = pdev->driver->sriov_configure(pdev, 0); - if (ret < 0) - return ret; - return count; + goto exit; } /* enable VFs */ if (pdev->sriov->num_VFs) { dev_warn(&pdev->dev, "%d VFs already enabled. Disable before enabling %d VFs\n", pdev->sriov->num_VFs, num_vfs); - return -EBUSY; + ret = -EBUSY; + goto exit; } ret = pdev->driver->sriov_configure(pdev, num_vfs); if (ret < 0) - return ret; + goto exit; if (ret != num_vfs) dev_warn(&pdev->dev, "%d VFs requested; only %d enabled\n", num_vfs, ret); +exit: + mutex_unlock(&iov->dev->sriov->lock); + + if (ret < 0) + return ret; + return count; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index cb17db242f30..8dd38e69d6f2 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -270,7 +270,7 @@ struct pci_sriov { u16 driver_max_VFs; /* max num VFs driver supports */ struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ - struct mutex lock; /* lock for VF bus */ + struct mutex lock; /* lock for setting sriov_numvfs in sysfs */ resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ }; diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 7ce77635e5ad..ac53edbc9613 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -71,6 +71,14 @@ config PCIEASPM_POWERSAVE Enable PCI Express ASPM L0s and L1 where possible, even if the BIOS did not. +config PCIEASPM_POWER_SUPERSAVE + bool "Power Supersave" + depends on PCIEASPM + help + Same as PCIEASPM_POWERSAVE, except it also enables L1 substates where + possible. This would result in higher power savings while staying in L1 + where the components support it. + config PCIEASPM_PERFORMANCE bool "Performance" depends on PCIEASPM diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3dd8bcbb3011..973472c23d89 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -30,8 +30,29 @@ #define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */ #define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */ #define ASPM_STATE_L1 (4) /* L1 state */ +#define ASPM_STATE_L1_1 (8) /* ASPM L1.1 state */ +#define ASPM_STATE_L1_2 (0x10) /* ASPM L1.2 state */ +#define ASPM_STATE_L1_1_PCIPM (0x20) /* PCI PM L1.1 state */ +#define ASPM_STATE_L1_2_PCIPM (0x40) /* PCI PM L1.2 state */ +#define ASPM_STATE_L1_SS_PCIPM (ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1_2_PCIPM) +#define ASPM_STATE_L1_2_MASK (ASPM_STATE_L1_2 | ASPM_STATE_L1_2_PCIPM) +#define ASPM_STATE_L1SS (ASPM_STATE_L1_1 | ASPM_STATE_L1_1_PCIPM |\ + ASPM_STATE_L1_2_MASK) #define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW) -#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1) +#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1 | \ + ASPM_STATE_L1SS) + +/* + * When L1 substates are enabled, the LTR L1.2 threshold is a timing parameter + * that decides whether L1.1 or L1.2 is entered (Refer PCIe spec for details). + * Not sure is there is a way to "calculate" this on the fly, but maybe we + * could turn it into a parameter in future. This value has been taken from + * the following files from Intel's coreboot (which is the only code I found + * to have used this): + * https://www.coreboot.org/pipermail/coreboot-gerrit/2015-March/021134.html + * https://review.coreboot.org/#/c/8832/ + */ +#define LTR_L1_2_THRESHOLD_BITS ((1 << 21) | (1 << 23) | (1 << 30)) struct aspm_latency { u32 l0s; /* L0s latency (nsec) */ @@ -40,6 +61,7 @@ struct aspm_latency { struct pcie_link_state { struct pci_dev *pdev; /* Upstream component of the Link */ + struct pci_dev *downstream; /* Downstream component, function 0 */ struct pcie_link_state *root; /* pointer to the root port link */ struct pcie_link_state *parent; /* pointer to the parent Link state */ struct list_head sibling; /* node in link_list */ @@ -47,11 +69,11 @@ struct pcie_link_state { struct list_head link; /* node in parent's children list */ /* ASPM state */ - u32 aspm_support:3; /* Supported ASPM state */ - u32 aspm_enabled:3; /* Enabled ASPM state */ - u32 aspm_capable:3; /* Capable ASPM state with latency */ - u32 aspm_default:3; /* Default ASPM state by BIOS */ - u32 aspm_disable:3; /* Disabled ASPM state */ + u32 aspm_support:7; /* Supported ASPM state */ + u32 aspm_enabled:7; /* Enabled ASPM state */ + u32 aspm_capable:7; /* Capable ASPM state with latency */ + u32 aspm_default:7; /* Default ASPM state by BIOS */ + u32 aspm_disable:7; /* Disabled ASPM state */ /* Clock PM state */ u32 clkpm_capable:1; /* Clock PM capable? */ @@ -66,6 +88,14 @@ struct pcie_link_state { * has one slot under it, so at most there are 8 functions. */ struct aspm_latency acceptable[8]; + + /* L1 PM Substate info */ + struct { + u32 up_cap_ptr; /* L1SS cap ptr in upstream dev */ + u32 dw_cap_ptr; /* L1SS cap ptr in downstream dev */ + u32 ctl1; /* value to be programmed in ctl1 */ + u32 ctl2; /* value to be programmed in ctl2 */ + } l1ss; }; static int aspm_disabled, aspm_force; @@ -76,11 +106,14 @@ static LIST_HEAD(link_list); #define POLICY_DEFAULT 0 /* BIOS default setting */ #define POLICY_PERFORMANCE 1 /* high performance */ #define POLICY_POWERSAVE 2 /* high power saving */ +#define POLICY_POWER_SUPERSAVE 3 /* possibly even more power saving */ #ifdef CONFIG_PCIEASPM_PERFORMANCE static int aspm_policy = POLICY_PERFORMANCE; #elif defined CONFIG_PCIEASPM_POWERSAVE static int aspm_policy = POLICY_POWERSAVE; +#elif defined CONFIG_PCIEASPM_POWER_SUPERSAVE +static int aspm_policy = POLICY_POWER_SUPERSAVE; #else static int aspm_policy; #endif @@ -88,7 +121,8 @@ static int aspm_policy; static const char *policy_str[] = { [POLICY_DEFAULT] = "default", [POLICY_PERFORMANCE] = "performance", - [POLICY_POWERSAVE] = "powersave" + [POLICY_POWERSAVE] = "powersave", + [POLICY_POWER_SUPERSAVE] = "powersupersave" }; #define LINK_RETRAIN_TIMEOUT HZ @@ -101,6 +135,9 @@ static int policy_to_aspm_state(struct pcie_link_state *link) return 0; case POLICY_POWERSAVE: /* Enable ASPM L0s/L1 */ + return (ASPM_STATE_L0S | ASPM_STATE_L1); + case POLICY_POWER_SUPERSAVE: + /* Enable Everything */ return ASPM_STATE_ALL; case POLICY_DEFAULT: return link->aspm_default; @@ -115,7 +152,8 @@ static int policy_to_clkpm_state(struct pcie_link_state *link) /* Disable ASPM and Clock PM */ return 0; case POLICY_POWERSAVE: - /* Disable Clock PM */ + case POLICY_POWER_SUPERSAVE: + /* Enable Clock PM */ return 1; case POLICY_DEFAULT: return link->clkpm_default; @@ -278,11 +316,33 @@ static u32 calc_l1_acceptable(u32 encoding) return (1000 << encoding); } +/* Convert L1SS T_pwr encoding to usec */ +static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val) +{ + switch (scale) { + case 0: + return val * 2; + case 1: + return val * 10; + case 2: + return val * 100; + } + dev_err(&pdev->dev, "%s: Invalid T_PwrOn scale: %u\n", + __func__, scale); + return 0; +} + struct aspm_register_info { u32 support:2; u32 enabled:2; u32 latency_encoding_l0s; u32 latency_encoding_l1; + + /* L1 substates */ + u32 l1ss_cap_ptr; + u32 l1ss_cap; + u32 l1ss_ctl1; + u32 l1ss_ctl2; }; static void pcie_get_aspm_reg(struct pci_dev *pdev, @@ -297,6 +357,22 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev, info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, ®16); info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC; + + /* Read L1 PM substate capabilities */ + info->l1ss_cap = info->l1ss_ctl1 = info->l1ss_ctl2 = 0; + info->l1ss_cap_ptr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); + if (!info->l1ss_cap_ptr) + return; + pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CAP, + &info->l1ss_cap); + if (!(info->l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) { + info->l1ss_cap = 0; + return; + } + pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL1, + &info->l1ss_ctl1); + pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL2, + &info->l1ss_ctl2); } static void pcie_aspm_check_latency(struct pci_dev *endpoint) @@ -327,6 +403,14 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) * Check L1 latency. * Every switch on the path to root complex need 1 * more microsecond for L1. Spec doesn't mention L0s. + * + * The exit latencies for L1 substates are not advertised + * by a device. Since the spec also doesn't mention a way + * to determine max latencies introduced by enabling L1 + * substates on the components, it is not clear how to do + * a L1 substate exit latency check. We assume that the + * L1 exit latencies advertised by a device include L1 + * substate latencies (and hence do not do any check). */ latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1); if ((link->aspm_capable & ASPM_STATE_L1) && @@ -338,6 +422,60 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) } } +/* + * The L1 PM substate capability is only implemented in function 0 in a + * multi function device. + */ +static struct pci_dev *pci_function_0(struct pci_bus *linkbus) +{ + struct pci_dev *child; + + list_for_each_entry(child, &linkbus->devices, bus_list) + if (PCI_FUNC(child->devfn) == 0) + return child; + return NULL; +} + +/* Calculate L1.2 PM substate timing parameters */ +static void aspm_calc_l1ss_info(struct pcie_link_state *link, + struct aspm_register_info *upreg, + struct aspm_register_info *dwreg) +{ + u32 val1, val2, scale1, scale2; + + link->l1ss.up_cap_ptr = upreg->l1ss_cap_ptr; + link->l1ss.dw_cap_ptr = dwreg->l1ss_cap_ptr; + link->l1ss.ctl1 = link->l1ss.ctl2 = 0; + + if (!(link->aspm_support & ASPM_STATE_L1_2_MASK)) + return; + + /* Choose the greater of the two T_cmn_mode_rstr_time */ + val1 = (upreg->l1ss_cap >> 8) & 0xFF; + val2 = (upreg->l1ss_cap >> 8) & 0xFF; + if (val1 > val2) + link->l1ss.ctl1 |= val1 << 8; + else + link->l1ss.ctl1 |= val2 << 8; + /* + * We currently use LTR L1.2 threshold to be fixed constant picked from + * Intel's coreboot. + */ + link->l1ss.ctl1 |= LTR_L1_2_THRESHOLD_BITS; + + /* Choose the greater of the two T_pwr_on */ + val1 = (upreg->l1ss_cap >> 19) & 0x1F; + scale1 = (upreg->l1ss_cap >> 16) & 0x03; + val2 = (dwreg->l1ss_cap >> 19) & 0x1F; + scale2 = (dwreg->l1ss_cap >> 16) & 0x03; + + if (calc_l1ss_pwron(link->pdev, scale1, val1) > + calc_l1ss_pwron(link->downstream, scale2, val2)) + link->l1ss.ctl2 |= scale1 | (val1 << 3); + else + link->l1ss.ctl2 |= scale2 | (val2 << 3); +} + static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { struct pci_dev *child, *parent = link->pdev; @@ -353,8 +491,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) /* Get upstream/downstream components' register state */ pcie_get_aspm_reg(parent, &upreg); - child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); + child = pci_function_0(linkbus); pcie_get_aspm_reg(child, &dwreg); + link->downstream = child; /* * If ASPM not supported, don't mess with the clocks and link, @@ -397,6 +536,28 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1); link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1); + /* Setup L1 substate */ + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1) + link->aspm_support |= ASPM_STATE_L1_1; + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2) + link->aspm_support |= ASPM_STATE_L1_2; + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1) + link->aspm_support |= ASPM_STATE_L1_1_PCIPM; + if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2) + link->aspm_support |= ASPM_STATE_L1_2_PCIPM; + + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1) + link->aspm_enabled |= ASPM_STATE_L1_1; + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2) + link->aspm_enabled |= ASPM_STATE_L1_2; + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1) + link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM; + if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2) + link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM; + + if (link->aspm_support & ASPM_STATE_L1SS) + aspm_calc_l1ss_info(link, &upreg, &dwreg); + /* Save default state */ link->aspm_default = link->aspm_enabled; @@ -435,6 +596,92 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) } } +static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos, + u32 clear, u32 set) +{ + u32 val; + + pci_read_config_dword(pdev, pos, &val); + val &= ~clear; + val |= set; + pci_write_config_dword(pdev, pos, val); +} + +/* Configure the ASPM L1 substates */ +static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) +{ + u32 val, enable_req; + struct pci_dev *child = link->downstream, *parent = link->pdev; + u32 up_cap_ptr = link->l1ss.up_cap_ptr; + u32 dw_cap_ptr = link->l1ss.dw_cap_ptr; + + enable_req = (link->aspm_enabled ^ state) & state; + + /* + * Here are the rules specified in the PCIe spec for enabling L1SS: + * - When enabling L1.x, enable bit at parent first, then at child + * - When disabling L1.x, disable bit at child first, then at parent + * - When enabling ASPM L1.x, need to disable L1 + * (at child followed by parent). + * - The ASPM/PCIPM L1.2 must be disabled while programming timing + * parameters + * + * To keep it simple, disable all L1SS bits first, and later enable + * what is needed. + */ + + /* Disable all L1 substates */ + pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); + pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); + /* + * If needed, disable L1, and it gets enabled later + * in pcie_config_aspm_link(). + */ + if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) { + pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPM_L1, 0); + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPM_L1, 0); + } + + if (enable_req & ASPM_STATE_L1_2_MASK) { + + /* Program T_pwr_on in both ports */ + pci_write_config_dword(parent, up_cap_ptr + PCI_L1SS_CTL2, + link->l1ss.ctl2); + pci_write_config_dword(child, dw_cap_ptr + PCI_L1SS_CTL2, + link->l1ss.ctl2); + + /* Program T_cmn_mode in parent */ + pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, + 0xFF00, link->l1ss.ctl1); + + /* Program LTR L1.2 threshold in both ports */ + pci_clear_and_set_dword(parent, dw_cap_ptr + PCI_L1SS_CTL1, + 0xE3FF0000, link->l1ss.ctl1); + pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1, + 0xE3FF0000, link->l1ss.ctl1); + } + + val = 0; + if (state & ASPM_STATE_L1_1) + val |= PCI_L1SS_CTL1_ASPM_L1_1; + if (state & ASPM_STATE_L1_2) + val |= PCI_L1SS_CTL1_ASPM_L1_2; + if (state & ASPM_STATE_L1_1_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_1; + if (state & ASPM_STATE_L1_2_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_2; + + /* Enable what we need to enable */ + pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CAP_L1_PM_SS, val); + pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CAP_L1_PM_SS, val); +} + static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) { pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, @@ -444,11 +691,23 @@ static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) { u32 upstream = 0, dwstream = 0; - struct pci_dev *child, *parent = link->pdev; + struct pci_dev *child = link->downstream, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; - /* Nothing to do if the link is already in the requested state */ + /* Enable only the states that were not explicitly disabled */ state &= (link->aspm_capable & ~link->aspm_disable); + + /* Can't enable any substates if L1 is not enabled */ + if (!(state & ASPM_STATE_L1)) + state &= ~ASPM_STATE_L1SS; + + /* Spec says both ports must be in D0 before enabling PCI PM substates*/ + if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) { + state &= ~ASPM_STATE_L1_SS_PCIPM; + state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM); + } + + /* Nothing to do if the link is already in the requested state */ if (link->aspm_enabled == state) return; /* Convert ASPM state to upstream/downstream ASPM register state */ @@ -460,6 +719,10 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) upstream |= PCI_EXP_LNKCTL_ASPM_L1; dwstream |= PCI_EXP_LNKCTL_ASPM_L1; } + + if (link->aspm_capable & ASPM_STATE_L1SS) + pcie_config_aspm_l1ss(link, state); + /* * Spec 2.0 suggests all functions should be configured the * same setting for ASPM. Enabling ASPM L1 should be done in @@ -619,7 +882,8 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) * the BIOS's expectation, we'll do so once pci_enable_device() is * called. */ - if (aspm_policy != POLICY_POWERSAVE) { + if (aspm_policy != POLICY_POWERSAVE && + aspm_policy != POLICY_POWER_SUPERSAVE) { pcie_config_aspm_path(link); pcie_set_clkpm(link, policy_to_clkpm_state(link)); } @@ -719,7 +983,8 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev) if (aspm_disabled || !link) return; - if (aspm_policy != POLICY_POWERSAVE) + if (aspm_policy != POLICY_POWERSAVE && + aspm_policy != POLICY_POWER_SUPERSAVE) return; down_read(&pci_bus_sem); diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index 9811b14d9ad8..d4d70ef4a2d7 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -19,8 +19,28 @@ struct dpc_dev { struct pcie_device *dev; struct work_struct work; int cap_pos; + bool rp; }; +static int dpc_wait_rp_inactive(struct dpc_dev *dpc) +{ + unsigned long timeout = jiffies + HZ; + struct pci_dev *pdev = dpc->dev->port; + u16 status; + + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status); + while (status & PCI_EXP_DPC_RP_BUSY && + !time_after(jiffies, timeout)) { + msleep(10); + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status); + } + if (status & PCI_EXP_DPC_RP_BUSY) { + dev_warn(&pdev->dev, "DPC root port still busy\n"); + return -EBUSY; + } + return 0; +} + static void dpc_wait_link_inactive(struct pci_dev *pdev) { unsigned long timeout = jiffies + HZ; @@ -33,7 +53,7 @@ static void dpc_wait_link_inactive(struct pci_dev *pdev) pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); } if (lnk_status & PCI_EXP_LNKSTA_DLLLA) - dev_warn(&pdev->dev, "Link state not disabled for DPC event"); + dev_warn(&pdev->dev, "Link state not disabled for DPC event\n"); } static void interrupt_event_handler(struct work_struct *work) @@ -52,6 +72,8 @@ static void interrupt_event_handler(struct work_struct *work) pci_unlock_rescan_remove(); dpc_wait_link_inactive(pdev); + if (dpc->rp && dpc_wait_rp_inactive(dpc)) + return; pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT); } @@ -73,11 +95,15 @@ static irqreturn_t dpc_irq(int irq, void *context) if (status & PCI_EXP_DPC_STATUS_TRIGGER) { u16 reason = (status >> 1) & 0x3; + u16 ext_reason = (status >> 5) & 0x3; - dev_warn(&dpc->dev->device, "DPC %s triggered, remove downstream devices\n", + dev_warn(&dpc->dev->device, "DPC %s detected, remove downstream devices\n", (reason == 0) ? "unmasked uncorrectable error" : (reason == 1) ? "ERR_NONFATAL" : - (reason == 2) ? "ERR_FATAL" : "extended error"); + (reason == 2) ? "ERR_FATAL" : + (ext_reason == 0) ? "RP PIO error" : + (ext_reason == 1) ? "software trigger" : + "reserved error"); schedule_work(&dpc->work); } return IRQ_HANDLED; @@ -111,6 +137,8 @@ static int dpc_probe(struct pcie_device *dev) pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap); pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); + dpc->rp = (cap & PCI_EXP_DPC_CAP_RP_EXT); + ctl |= PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN; pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 9698289f105c..cea504f6f478 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -44,52 +44,16 @@ static void release_pcie_device(struct device *dev) } /** - * 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 - * - * 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 j; - - for (j = 0; j < nr_entries; j++) - if (entries[j].entry == new_entry) - return j; - - entries[j].entry = new_entry; - return j; -} - -/** * 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 +61,13 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) * 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 +82,16 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) 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 +106,11 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) 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 +118,39 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) * 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; + + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) + irqs[i] = -1; /* * If MSI cannot be used for PCIe PME or hotplug, we have to use @@ -207,41 +158,25 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) */ 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 +313,7 @@ int pcie_port_device_register(struct pci_dev *dev) * 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 +336,7 @@ int pcie_port_device_register(struct pci_dev *dev) return 0; error_cleanup_irqs: - cleanup_service_irqs(dev); + pci_free_irq_vectors(dev); error_disable: pci_disable_device(dev); return status; @@ -469,7 +404,7 @@ static int remove_iter(struct device *dev, void *data) 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); } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 204960e70333..dfc9a2794141 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1556,8 +1556,16 @@ static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp) static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp) { - if (hpp) - dev_warn(&dev->dev, "PCI-X settings not supported\n"); + int pos; + + if (!hpp) + return; + + pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); + if (!pos) + return; + + dev_warn(&dev->dev, "PCI-X settings not supported\n"); } static bool pcie_root_rcb_set(struct pci_dev *dev) @@ -1583,6 +1591,9 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) if (!hpp) return; + if (!pci_is_pcie(dev)) + return; + if (hpp->revision > 1) { dev_warn(&dev->dev, "PCIe settings rev %d not supported\n", hpp->revision); @@ -1652,12 +1663,30 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) */ } +static void pci_configure_extended_tags(struct pci_dev *dev) +{ + u32 dev_cap; + int ret; + + if (!pci_is_pcie(dev)) + return; + + ret = pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &dev_cap); + if (ret) + return; + + if (dev_cap & PCI_EXP_DEVCAP_EXT_TAG) + pcie_capability_set_word(dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_EXT_TAG); +} + static void pci_configure_device(struct pci_dev *dev) { struct hotplug_params hpp; int ret; pci_configure_mps(dev); + pci_configure_extended_tags(dev); memset(&hpp, 0, sizeof(hpp)); ret = pci_get_hp_params(dev, &hpp); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 1800befa8b8b..f754453fe754 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1634,6 +1634,7 @@ static void quirk_pcie_mch(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_pcie_mch); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quirk_pcie_mch); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quirk_pcie_mch); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_HUAWEI, 0x1610, quirk_pcie_mch); /* @@ -2239,6 +2240,27 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5719, quirk_brcm_5719_limit_mrrs); +#ifdef CONFIG_PCIE_IPROC_PLATFORM +static void quirk_paxc_bridge(struct pci_dev *pdev) +{ + /* The PCI config space is shared with the PAXC root port and the first + * Ethernet device. So, we need to workaround this by telling the PCI + * code that the bridge is not an Ethernet device. + */ + if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + pdev->class = PCI_CLASS_BRIDGE_PCI << 8; + + /* MPSS is not being set properly (as it is currently 0). This is + * because that area of the PCI config space is hard coded to zero, and + * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS) + * so that the MPS can be set to the real max value. + */ + pdev->pcie_mpss = 2; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); +#endif + /* Originally in EDAC sources for i82875P: * Intel tells BIOS developers to hide device 6 which * configures the overflow device access containing @@ -3113,30 +3135,32 @@ static void quirk_remove_d3_delay(struct pci_dev *dev) { dev->d3_delay = 0; } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay); +/* C600 Series devices do not need 10ms d3_delay */ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0412, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c0c, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c31, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3a, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3d, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay); +/* Lynxpoint-H PCH devices do not need 10ms d3_delay */ +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c18, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c1c, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c26, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c31, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3a, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3d, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c4e, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay); /* Intel Cherrytrail devices do not need 10ms d3_delay */ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2280, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2298, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x229c, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b0, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b5, quirk_remove_d3_delay); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b7, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b8, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22d8, quirk_remove_d3_delay); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22dc, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b5, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b7, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2298, quirk_remove_d3_delay); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x229c, quirk_remove_d3_delay); /* * Some devices may pass our check in pci_intx_mask_supported() if @@ -3606,7 +3630,7 @@ static int __init pci_apply_final_quirks(void) fs_initcall_sync(pci_apply_final_quirks); /* - * Followings are device-specific reset methods which can be used to + * Following are device-specific reset methods which can be used to * reset a single function if other methods (e.g. FLR, PM D0->D3) are * not available. */ @@ -4136,6 +4160,26 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags) } /* + * These QCOM root ports do provide ACS-like features to disable peer + * transactions and validate bus numbers in requests, but do not provide an + * actual PCIe ACS capability. Hardware supports source validation but it + * will report the issue as Completer Abort instead of ACS Violation. + * Hardware doesn't support peer-to-peer and each root port is a root + * complex with unique segment numbers. It is not possible for one root + * port to pass traffic to another root port. All PCIe transactions are + * terminated inside the root port. + */ +static int pci_quirk_qcom_rp_acs(struct pci_dev *dev, u16 acs_flags) +{ + u16 flags = (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV); + int ret = acs_flags & ~flags ? 0 : 1; + + dev_info(&dev->dev, "Using QCOM ACS Quirk (%d)\n", ret); + + return ret; +} + +/* * Sunrise Point PCH root ports implement ACS, but unfortunately as shown in * the datasheet (Intel 100 Series Chipset Family PCH Datasheet, Vol. 2, * 12.1.46, 12.1.47)[1] this chipset uses dwords for the ACS capability and @@ -4150,15 +4194,35 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags) * * N.B. This doesn't fix what lspci shows. * + * The 100 series chipset specification update includes this as errata #23[3]. + * + * The 200 series chipset (Union Point) has the same bug according to the + * specification update (Intel 200 Series Chipset Family Platform Controller + * Hub, Specification Update, January 2017, Revision 001, Document# 335194-001, + * Errata 22)[4]. Per the datasheet[5], root port PCI Device IDs for this + * chipset include: + * + * 0xa290-0xa29f PCI Express Root port #{0-16} + * 0xa2e7-0xa2ee PCI Express Root port #{17-24} + * * [1] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-2.html * [2] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-1.html + * [3] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-spec-update.html + * [4] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-spec-update.html + * [5] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-datasheet-vol-1.html */ static bool pci_quirk_intel_spt_pch_acs_match(struct pci_dev *dev) { - return pci_is_pcie(dev) && - pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT && - ((dev->device & ~0xf) == 0xa110 || - (dev->device >= 0xa167 && dev->device <= 0xa16a)); + if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) + return false; + + switch (dev->device) { + case 0xa110 ... 0xa11f: case 0xa167 ... 0xa16a: /* Sunrise Point */ + case 0xa290 ... 0xa29f: case 0xa2e7 ... 0xa2ee: /* Union Point */ + return true; + } + + return false; } #define INTEL_SPT_ACS_CTRL (PCI_ACS_CAP + 4) @@ -4271,6 +4335,9 @@ static const struct pci_dev_acs_enabled { /* I219 */ { PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs }, + /* QCOM QDF2xxx root ports */ + { 0x17cb, 0x400, pci_quirk_qcom_rp_acs }, + { 0x17cb, 0x401, pci_quirk_qcom_rp_acs }, /* Intel PCH root ports */ { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs }, diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f30ca75b5b6c..cb389277df41 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -105,17 +105,8 @@ static struct pci_dev_resource *res_to_dev_res(struct list_head *head, struct pci_dev_resource *dev_res; list_for_each_entry(dev_res, head, list) { - if (dev_res->res == res) { - int idx = res - &dev_res->dev->resource[0]; - - dev_printk(KERN_DEBUG, &dev_res->dev->dev, - "res[%d]=%pR res_to_dev_res add_size %llx min_align %llx\n", - idx, dev_res->res, - (unsigned long long)dev_res->add_size, - (unsigned long long)dev_res->min_align); - + if (dev_res->res == res) return dev_res; - } } return NULL; diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 6d9335865880..9612b84bc3e0 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -20,6 +20,7 @@ #include <linux/perf/arm_pmu.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/sched/clock.h> #include <linux/spinlock.h> #include <linux/irq.h> #include <linux/irqdesc.h> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index bb5cf6f49b06..dc5277ad1b5a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -331,6 +331,14 @@ config PHY_EXYNOS5_USBDRD This driver provides PHY interface for USB 3.0 DRD controller present on Exynos5 SoC series. +config PHY_EXYNOS_PCIE + bool "Exynos PCIe PHY driver" + depends on OF && (ARCH_EXYNOS || COMPILE_TEST) + select GENERIC_PHY + help + Enable PCIe PHY support for Exynos SoC series. + This driver provides PHY interface for Exynos PCIe controller. + config PHY_PISTACHIO_USB tristate "IMG Pistachio USB2.0 PHY driver" depends on MACH_PISTACHIO diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 9f008004f75d..e7b0feb1e125 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -37,6 +37,7 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o +obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o diff --git a/drivers/phy/phy-exynos-pcie.c b/drivers/phy/phy-exynos-pcie.c new file mode 100644 index 000000000000..4f60b83641d5 --- /dev/null +++ b/drivers/phy/phy-exynos-pcie.c @@ -0,0 +1,285 @@ +/* + * Samsung EXYNOS SoC series PCIe PHY driver + * + * Phy provider for PCIe controller on Exynos SoC series + * + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * Jaehoon Chung <jh80.chung@samsung.com> + * + * 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/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> + +/* PCIe Purple registers */ +#define PCIE_PHY_GLOBAL_RESET 0x000 +#define PCIE_PHY_COMMON_RESET 0x004 +#define PCIE_PHY_CMN_REG 0x008 +#define PCIE_PHY_MAC_RESET 0x00c +#define PCIE_PHY_PLL_LOCKED 0x010 +#define PCIE_PHY_TRSVREG_RESET 0x020 +#define PCIE_PHY_TRSV_RESET 0x024 + +/* PCIe PHY registers */ +#define PCIE_PHY_IMPEDANCE 0x004 +#define PCIE_PHY_PLL_DIV_0 0x008 +#define PCIE_PHY_PLL_BIAS 0x00c +#define PCIE_PHY_DCC_FEEDBACK 0x014 +#define PCIE_PHY_PLL_DIV_1 0x05c +#define PCIE_PHY_COMMON_POWER 0x064 +#define PCIE_PHY_COMMON_PD_CMN BIT(3) +#define PCIE_PHY_TRSV0_EMP_LVL 0x084 +#define PCIE_PHY_TRSV0_DRV_LVL 0x088 +#define PCIE_PHY_TRSV0_RXCDR 0x0ac +#define PCIE_PHY_TRSV0_POWER 0x0c4 +#define PCIE_PHY_TRSV0_PD_TSV BIT(7) +#define PCIE_PHY_TRSV0_LVCC 0x0dc +#define PCIE_PHY_TRSV1_EMP_LVL 0x144 +#define PCIE_PHY_TRSV1_RXCDR 0x16c +#define PCIE_PHY_TRSV1_POWER 0x184 +#define PCIE_PHY_TRSV1_PD_TSV BIT(7) +#define PCIE_PHY_TRSV1_LVCC 0x19c +#define PCIE_PHY_TRSV2_EMP_LVL 0x204 +#define PCIE_PHY_TRSV2_RXCDR 0x22c +#define PCIE_PHY_TRSV2_POWER 0x244 +#define PCIE_PHY_TRSV2_PD_TSV BIT(7) +#define PCIE_PHY_TRSV2_LVCC 0x25c +#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 +#define PCIE_PHY_TRSV3_RXCDR 0x2ec +#define PCIE_PHY_TRSV3_POWER 0x304 +#define PCIE_PHY_TRSV3_PD_TSV BIT(7) +#define PCIE_PHY_TRSV3_LVCC 0x31c + +struct exynos_pcie_phy_data { + const struct phy_ops *ops; +}; + +/* For Exynos pcie phy */ +struct exynos_pcie_phy { + const struct exynos_pcie_phy_data *drv_data; + void __iomem *phy_base; + void __iomem *blk_base; /* For exynos5440 */ +}; + +static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset) +{ + writel(val, base + offset); +} + +static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +/* For Exynos5440 specific functions */ +static int exynos5440_pcie_phy_init(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + + /* DCC feedback control off */ + exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK); + + /* set TX/RX impedance */ + exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE); + + /* set 50Mhz PHY clock */ + exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0); + exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1); + + /* set TX Differential output for lane 0 */ + exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); + + /* set TX Pre-emphasis Level Control for lane 0 to minimum */ + exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL); + + /* set RX clock and data recovery bandwidth */ + exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR); + exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR); + + /* change TX Pre-emphasis Level Control for lanes */ + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL); + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL); + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL); + exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL); + + /* set LVCC */ + exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC); + exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC); + exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC); + exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC); + + /* pulse for common reset */ + exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET); + udelay(500); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); + + return 0; +} + +static int exynos5440_pcie_phy_power_on(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + u32 val; + + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); + val &= ~PCIE_PHY_COMMON_PD_CMN; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); + val &= ~PCIE_PHY_TRSV0_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); + val &= ~PCIE_PHY_TRSV1_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); + val &= ~PCIE_PHY_TRSV2_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); + val &= ~PCIE_PHY_TRSV3_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); + + return 0; +} + +static int exynos5440_pcie_phy_power_off(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + u32 val; + + if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val, + (val != 0), 1, 500)) { + dev_err(&phy->dev, "PLL Locked: 0x%x\n", val); + return -ETIMEDOUT; + } + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); + val |= PCIE_PHY_COMMON_PD_CMN; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); + val |= PCIE_PHY_TRSV0_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); + val |= PCIE_PHY_TRSV1_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); + val |= PCIE_PHY_TRSV2_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); + + val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); + val |= PCIE_PHY_TRSV3_PD_TSV; + exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); + + return 0; +} + +static int exynos5440_pcie_phy_reset(struct phy *phy) +{ + struct exynos_pcie_phy *ep = phy_get_drvdata(phy); + + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET); + exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET); + exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET); + + return 0; +} + +static const struct phy_ops exynos5440_phy_ops = { + .init = exynos5440_pcie_phy_init, + .power_on = exynos5440_pcie_phy_power_on, + .power_off = exynos5440_pcie_phy_power_off, + .reset = exynos5440_pcie_phy_reset, + .owner = THIS_MODULE, +}; + +static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = { + .ops = &exynos5440_phy_ops, +}; + +static const struct of_device_id exynos_pcie_phy_match[] = { + { + .compatible = "samsung,exynos5440-pcie-phy", + .data = &exynos5440_pcie_phy_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_pcie_phy_match); + +static int exynos_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct exynos_pcie_phy *exynos_phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct resource *res; + const struct exynos_pcie_phy_data *drv_data; + + drv_data = of_device_get_match_data(dev); + if (!drv_data) + return -ENODEV; + + exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL); + if (!exynos_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + exynos_phy->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(exynos_phy->phy_base)) + return PTR_ERR(exynos_phy->phy_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + exynos_phy->blk_base = devm_ioremap_resource(dev, res); + if (IS_ERR(exynos_phy->phy_base)) + return PTR_ERR(exynos_phy->phy_base); + + exynos_phy->drv_data = drv_data; + + generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, exynos_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver exynos_pcie_phy_driver = { + .probe = exynos_pcie_phy_probe, + .driver = { + .of_match_table = exynos_pcie_phy_match, + .name = "exynos_pcie_phy", + } +}; +module_platform_driver(exynos_pcie_phy_driver); + +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC PCIe PHY driver"); +MODULE_AUTHOR("Jaehoon Chung <jh80.chung@samsung.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig index 8968dd7aebed..e8c4e4f934a6 100644 --- a/drivers/pinctrl/bcm/Kconfig +++ b/drivers/pinctrl/bcm/Kconfig @@ -70,7 +70,7 @@ config PINCTRL_CYGNUS_MUX The Broadcom Cygnus IOMUX driver supports group based IOMUX configuration, with the exception that certain individual pins - can be overrided to GPIO function + can be overridden to GPIO function config PINCTRL_NSP_GPIO bool "Broadcom NSP GPIO (with PINCONF) driver" diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 04053fe1e980..ed5dee744c74 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -447,6 +447,11 @@ static int get_next_event(struct cros_ec_device *ec_dev) struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret; + if (ec_dev->suspended) { + dev_dbg(ec_dev->dev, "Device suspended.\n"); + return -EHOSTDOWN; + } + msg->version = 0; msg->command = EC_CMD_GET_NEXT_EVENT; msg->insize = sizeof(ec_dev->event_data); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 49a594855f98..4bc88eb52712 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -92,9 +92,8 @@ config ASUS_LAPTOP If you have an ACPI-compatible ASUS laptop, say Y or M here. config DELL_SMBIOS - tristate "Dell SMBIOS Support" - depends on DCDBAS - default n + tristate + select DCDBAS ---help--- This module provides common functions for kernel modules using Dell SMBIOS. @@ -103,16 +102,15 @@ config DELL_SMBIOS config DELL_LAPTOP tristate "Dell Laptop Extras" - depends on DELL_SMBIOS depends on DMI depends on BACKLIGHT_CLASS_DEVICE depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL || RFKILL = n depends on SERIO_I8042 + select DELL_SMBIOS select POWER_SUPPLY select LEDS_CLASS select NEW_LEDS - default n ---help--- This driver adds support for rfkill and backlight control to Dell laptops (except for some models covered by the Compal driver). @@ -123,7 +121,7 @@ config DELL_WMI depends on DMI depends on INPUT depends on ACPI_VIDEO || ACPI_VIDEO = n - depends on DELL_SMBIOS + select DELL_SMBIOS select INPUT_SPARSEKMAP ---help--- Say Y here if you want to support WMI-based hotkeys on Dell laptops. @@ -1069,4 +1067,30 @@ config MLX_CPLD_PLATFORM This driver handles hot-plug events for the power suppliers, power cables and fans on the wide range Mellanox IB and Ethernet systems. +config INTEL_TURBO_MAX_3 + bool "Intel Turbo Boost Max Technology 3.0 enumeration driver" + depends on X86_64 && SCHED_MC_PRIO + ---help--- + This driver reads maximum performance ratio of each CPU and set up + the scheduler priority metrics. In this way scheduler can prefer + CPU with higher performance to schedule tasks. + This driver is only required when the system is not using Hardware + P-States (HWP). In HWP mode, priority can be read from ACPI tables. + +config SILEAD_DMI + bool "Tablets with Silead touchscreens" + depends on ACPI && DMI && I2C=y && INPUT + ---help--- + Certain ACPI based tablets with Silead touchscreens do not have + enough data in ACPI tables for the touchscreen driver to handle + the touchscreen properly, as OEMs expected the data to be baked + into the tablet model specific version of the driver shipped + with the OS-image for the device. This option supplies the missing + information. Enable this for x86 tablets with Silead touchscreens. + endif # X86_PLATFORM_DEVICES + +config PMC_ATOM + def_bool y + depends on PCI + select COMMON_CLK diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index b2f52a7690af..299d0f9e40f7 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o +obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o @@ -73,5 +74,7 @@ obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ intel_telemetry_pltdrv.o \ intel_telemetry_debugfs.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o +obj-$(CONFIG_PMC_ATOM) += pmc_atom.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o +obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index a66192f692e3..dac0fbe87460 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -128,6 +128,7 @@ static const struct key_entry acer_wmi_keymap[] __initconst = { {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} }, {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} }, {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} }, + {KE_KEY, 0x86, {KEY_WLAN} }, {KE_END, 0} }; @@ -150,15 +151,30 @@ struct event_return_value { #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ #define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */ -struct lm_input_params { +/* Hotkey Customized Setting and Acer Application Status. + * Set Device Default Value and Report Acer Application Status. + * When Acer Application starts, it will run this method to inform + * BIOS/EC that Acer Application is on. + * App Status + * Bit[0]: Launch Manager Status + * Bit[1]: ePM Status + * Bit[2]: Device Control Status + * Bit[3]: Acer Power Button Utility Status + * Bit[4]: RF Button Status + * Bit[5]: ODD PM Status + * Bit[6]: Device Default Value Control + * Bit[7]: Hall Sensor Application Status + */ +struct func_input_params { u8 function_num; /* Function Number */ u16 commun_devices; /* Communication type devices default status */ u16 devices; /* Other type devices default status */ - u8 lm_status; /* Launch Manager Status */ - u16 reserved; + u8 app_status; /* Acer Device Status. LM, ePM, RF Button... */ + u8 app_mask; /* Bit mask to app_status */ + u8 reserved; } __attribute__((packed)); -struct lm_return_value { +struct func_return_value { u8 error_code; /* Error Code */ u8 ec_return_value; /* EC Return Value */ u16 reserved; @@ -1769,13 +1785,13 @@ static void acer_wmi_notify(u32 value, void *context) } static acpi_status __init -wmid3_set_lm_mode(struct lm_input_params *params, - struct lm_return_value *return_value) +wmid3_set_function_mode(struct func_input_params *params, + struct func_return_value *return_value) { acpi_status status; union acpi_object *obj; - struct acpi_buffer input = { sizeof(struct lm_input_params), params }; + struct acpi_buffer input = { sizeof(struct func_input_params), params }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); @@ -1796,7 +1812,7 @@ wmid3_set_lm_mode(struct lm_input_params *params, return AE_ERROR; } - *return_value = *((struct lm_return_value *)obj->buffer.pointer); + *return_value = *((struct func_return_value *)obj->buffer.pointer); kfree(obj); return status; @@ -1804,16 +1820,17 @@ wmid3_set_lm_mode(struct lm_input_params *params, static int __init acer_wmi_enable_ec_raw(void) { - struct lm_return_value return_value; + struct func_return_value return_value; acpi_status status; - struct lm_input_params params = { + struct func_input_params params = { .function_num = 0x1, .commun_devices = 0xFFFF, .devices = 0xFFFF, - .lm_status = 0x00, /* Launch Manager Deactive */ + .app_status = 0x00, /* Launch Manager Deactive */ + .app_mask = 0x01, }; - status = wmid3_set_lm_mode(¶ms, &return_value); + status = wmid3_set_function_mode(¶ms, &return_value); if (return_value.error_code || return_value.ec_return_value) pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n", @@ -1827,16 +1844,17 @@ static int __init acer_wmi_enable_ec_raw(void) static int __init acer_wmi_enable_lm(void) { - struct lm_return_value return_value; + struct func_return_value return_value; acpi_status status; - struct lm_input_params params = { + struct func_input_params params = { .function_num = 0x1, .commun_devices = 0xFFFF, .devices = 0xFFFF, - .lm_status = 0x01, /* Launch Manager Active */ + .app_status = 0x01, /* Launch Manager Active */ + .app_mask = 0x01, }; - status = wmid3_set_lm_mode(¶ms, &return_value); + status = wmid3_set_function_mode(¶ms, &return_value); if (return_value.error_code || return_value.ec_return_value) pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n", @@ -1846,11 +1864,46 @@ static int __init acer_wmi_enable_lm(void) return status; } +static int __init acer_wmi_enable_rf_button(void) +{ + struct func_return_value return_value; + acpi_status status; + struct func_input_params params = { + .function_num = 0x1, + .commun_devices = 0xFFFF, + .devices = 0xFFFF, + .app_status = 0x10, /* RF Button Active */ + .app_mask = 0x10, + }; + + status = wmid3_set_function_mode(¶ms, &return_value); + + if (return_value.error_code || return_value.ec_return_value) + pr_warn("Enabling RF Button failed: 0x%x - 0x%x\n", + return_value.error_code, + return_value.ec_return_value); + + return status; +} + +#define ACER_WMID_ACCEL_HID "BST0001" + static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, void *ctx, void **retval) { + struct acpi_device *dev; + + if (!strcmp(ctx, "SENR")) { + if (acpi_bus_get_device(ah, &dev)) + return AE_OK; + if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev))) + return AE_OK; + } else + return AE_OK; + *(acpi_handle *)retval = ah; - return AE_OK; + + return AE_CTRL_TERMINATE; } static int __init acer_wmi_get_handle(const char *name, const char *prop, @@ -1877,7 +1930,7 @@ static int __init acer_wmi_accel_setup(void) { int err; - err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle); + err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle); if (err) return err; @@ -2216,6 +2269,9 @@ static int __init acer_wmi_init(void) interface->capability &= ~ACER_CAP_BRIGHTNESS; if (wmi_has_guid(WMID_GUID3)) { + if (ACPI_FAILURE(acer_wmi_enable_rf_button())) + pr_warn("Cannot enable RF Button Driver\n"); + if (ec_raw_mode) { if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { pr_err("Cannot enable EC raw mode\n"); @@ -2233,10 +2289,11 @@ static int __init acer_wmi_init(void) err = acer_wmi_input_setup(); if (err) return err; + err = acer_wmi_accel_setup(); + if (err) + return err; } - acer_wmi_accel_setup(); - err = platform_driver_register(&acer_platform_driver); if (err) { pr_err("Unable to register platform driver\n"); diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 005629447b0c..d6b34923fb4e 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -21,7 +21,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dmi.h> -#include <linux/acpi.h> #include <linux/leds.h> #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index 9f31bc1a47d0..f3796164329e 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -17,19 +17,41 @@ #include <linux/pci_ids.h> #include <linux/leds.h> -#define ASUS_WIRELESS_LED_STATUS 0x2 -#define ASUS_WIRELESS_LED_OFF 0x4 -#define ASUS_WIRELESS_LED_ON 0x5 +struct hswc_params { + u8 on; + u8 off; + u8 status; +}; struct asus_wireless_data { struct input_dev *idev; struct acpi_device *adev; + const struct hswc_params *hswc_params; struct workqueue_struct *wq; struct work_struct led_work; struct led_classdev led; int led_state; }; +static const struct hswc_params atk4001_id_params = { + .on = 0x0, + .off = 0x1, + .status = 0x2, +}; + +static const struct hswc_params atk4002_id_params = { + .on = 0x5, + .off = 0x4, + .status = 0x2, +}; + +static const struct acpi_device_id device_ids[] = { + {"ATK4001", (kernel_ulong_t)&atk4001_id_params}, + {"ATK4002", (kernel_ulong_t)&atk4002_id_params}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, device_ids); + static u64 asus_wireless_method(acpi_handle handle, const char *method, int param) { @@ -61,8 +83,8 @@ static enum led_brightness led_state_get(struct led_classdev *led) data = container_of(led, struct asus_wireless_data, led); s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC", - ASUS_WIRELESS_LED_STATUS); - if (s == ASUS_WIRELESS_LED_ON) + data->hswc_params->status); + if (s == data->hswc_params->on) return LED_FULL; return LED_OFF; } @@ -76,14 +98,13 @@ static void led_state_update(struct work_struct *work) data->led_state); } -static void led_state_set(struct led_classdev *led, - enum led_brightness value) +static void led_state_set(struct led_classdev *led, enum led_brightness value) { struct asus_wireless_data *data; data = container_of(led, struct asus_wireless_data, led); - data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF : - ASUS_WIRELESS_LED_ON; + data->led_state = value == LED_OFF ? data->hswc_params->off : + data->hswc_params->on; queue_work(data->wq, &data->led_work); } @@ -104,12 +125,14 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event) static int asus_wireless_add(struct acpi_device *adev) { struct asus_wireless_data *data; + const struct acpi_device_id *id; int err; data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; adev->driver_data = data; + data->adev = adev; data->idev = devm_input_allocate_device(&adev->dev); if (!data->idev) @@ -124,7 +147,16 @@ static int asus_wireless_add(struct acpi_device *adev) if (err) return err; - data->adev = adev; + for (id = device_ids; id->id[0]; id++) { + if (!strcmp((char *) id->id, acpi_device_hid(adev))) { + data->hswc_params = + (const struct hswc_params *)id->driver_data; + break; + } + } + if (!data->hswc_params) + return 0; + data->wq = create_singlethread_workqueue("asus_wireless_workqueue"); if (!data->wq) return -ENOMEM; @@ -137,6 +169,7 @@ static int asus_wireless_add(struct acpi_device *adev) err = devm_led_classdev_register(&adev->dev, &data->led); if (err) destroy_workqueue(data->wq); + return err; } @@ -149,13 +182,6 @@ static int asus_wireless_remove(struct acpi_device *adev) return 0; } -static const struct acpi_device_id device_ids[] = { - {"ATK4001", 0}, - {"ATK4002", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, device_ids); - static struct acpi_driver asus_wireless_driver = { .name = "Asus Wireless Radio Control Driver", .class = "hotkey", diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 14392a01ab36..f57dd282a002 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -106,6 +106,12 @@ static const struct dmi_system_id dell_device_table[] __initconst = { }, }, { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ + }, + }, + { .ident = "Dell Computer Corporation", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 82d67715ce76..2b218b1d13e5 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -202,6 +202,7 @@ static int radio_led_set(struct led_classdev *cdev, static struct led_classdev radio_led = { .name = "fujitsu::radio_led", + .default_trigger = "rfkill-any", .brightness_get = radio_led_get, .brightness_set_blocking = radio_led_set }; @@ -270,15 +271,20 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2) static int logolamp_set(struct led_classdev *cdev, enum led_brightness brightness) { - if (brightness >= LED_FULL) { - call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); - return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); - } else if (brightness >= LED_HALF) { - call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); - return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); - } else { - return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); - } + int poweron = FUNC_LED_ON, always = FUNC_LED_ON; + int ret; + + if (brightness < LED_HALF) + poweron = FUNC_LED_OFF; + + if (brightness < LED_FULL) + always = FUNC_LED_OFF; + + ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); + if (ret < 0) + return ret; + + return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); } static int kblamps_set(struct led_classdev *cdev, @@ -313,17 +319,17 @@ static int eco_led_set(struct led_classdev *cdev, static enum led_brightness logolamp_get(struct led_classdev *cdev) { - enum led_brightness brightness = LED_OFF; - int poweron, always; - - poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); - if (poweron == FUNC_LED_ON) { - brightness = LED_HALF; - always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); - if (always == FUNC_LED_ON) - brightness = LED_FULL; - } - return brightness; + int ret; + + ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); + if (ret == FUNC_LED_ON) + return LED_FULL; + + ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); + if (ret == FUNC_LED_ON) + return LED_HALF; + + return LED_OFF; } static enum led_brightness kblamps_get(struct led_classdev *cdev) @@ -1029,107 +1035,117 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) return 0; } +static void acpi_fujitsu_hotkey_press(int keycode) +{ + struct input_dev *input = fujitsu_hotkey->input; + int status; + + status = kfifo_in_locked(&fujitsu_hotkey->fifo, + (unsigned char *)&keycode, sizeof(keycode), + &fujitsu_hotkey->fifo_lock); + if (status != sizeof(keycode)) { + vdbg_printk(FUJLAPTOP_DBG_WARN, + "Could not push keycode [0x%x]\n", keycode); + return; + } + input_report_key(input, keycode, 1); + input_sync(input); + vdbg_printk(FUJLAPTOP_DBG_TRACE, + "Push keycode into ringbuffer [%d]\n", keycode); +} + +static void acpi_fujitsu_hotkey_release(void) +{ + struct input_dev *input = fujitsu_hotkey->input; + int keycode, status; + + while (true) { + status = kfifo_out_locked(&fujitsu_hotkey->fifo, + (unsigned char *)&keycode, + sizeof(keycode), + &fujitsu_hotkey->fifo_lock); + if (status != sizeof(keycode)) + return; + input_report_key(input, keycode, 0); + input_sync(input); + vdbg_printk(FUJLAPTOP_DBG_TRACE, + "Pop keycode from ringbuffer [%d]\n", keycode); + } +} + static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) { struct input_dev *input; - int keycode, keycode_r; + int keycode; unsigned int irb = 1; - int i, status; + int i; input = fujitsu_hotkey->input; + if (event != ACPI_FUJITSU_NOTIFY_CODE1) { + keycode = KEY_UNKNOWN; + vdbg_printk(FUJLAPTOP_DBG_WARN, + "Unsupported event [0x%x]\n", event); + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + return; + } + if (fujitsu_hotkey->rfkill_supported) fujitsu_hotkey->rfkill_state = call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); - switch (event) { - case ACPI_FUJITSU_NOTIFY_CODE1: - i = 0; - while ((irb = - call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 - && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { - switch (irb & 0x4ff) { - case KEY1_CODE: - keycode = fujitsu->keycode1; - break; - case KEY2_CODE: - keycode = fujitsu->keycode2; - break; - case KEY3_CODE: - keycode = fujitsu->keycode3; - break; - case KEY4_CODE: - keycode = fujitsu->keycode4; - break; - case KEY5_CODE: - keycode = fujitsu->keycode5; - break; - case 0: - keycode = 0; - break; - default: - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Unknown GIRB result [%x]\n", irb); - keycode = -1; - break; - } - if (keycode > 0) { - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "Push keycode into ringbuffer [%d]\n", - keycode); - status = kfifo_in_locked(&fujitsu_hotkey->fifo, - (unsigned char *)&keycode, - sizeof(keycode), - &fujitsu_hotkey->fifo_lock); - if (status != sizeof(keycode)) { - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Could not push keycode [0x%x]\n", - keycode); - } else { - input_report_key(input, keycode, 1); - input_sync(input); - } - } else if (keycode == 0) { - while ((status = - kfifo_out_locked( - &fujitsu_hotkey->fifo, - (unsigned char *) &keycode_r, - sizeof(keycode_r), - &fujitsu_hotkey->fifo_lock)) - == sizeof(keycode_r)) { - input_report_key(input, keycode_r, 0); - input_sync(input); - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "Pop keycode from ringbuffer [%d]\n", - keycode_r); - } - } + i = 0; + while ((irb = + call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 + && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { + switch (irb & 0x4ff) { + case KEY1_CODE: + keycode = fujitsu->keycode1; + break; + case KEY2_CODE: + keycode = fujitsu->keycode2; + break; + case KEY3_CODE: + keycode = fujitsu->keycode3; + break; + case KEY4_CODE: + keycode = fujitsu->keycode4; + break; + case KEY5_CODE: + keycode = fujitsu->keycode5; + break; + case 0: + keycode = 0; + break; + default: + vdbg_printk(FUJLAPTOP_DBG_WARN, + "Unknown GIRB result [%x]\n", irb); + keycode = -1; + break; } - /* On some models (first seen on the Skylake-based Lifebook - * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is - * handled in software; its state is queried using FUNC_RFKILL - */ - if ((fujitsu_hotkey->rfkill_supported & BIT(26)) && - (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) { - keycode = KEY_TOUCHPAD_TOGGLE; - input_report_key(input, keycode, 1); - input_sync(input); - input_report_key(input, keycode, 0); - input_sync(input); - } + if (keycode > 0) + acpi_fujitsu_hotkey_press(keycode); + else if (keycode == 0) + acpi_fujitsu_hotkey_release(); + } - break; - default: - keycode = KEY_UNKNOWN; - vdbg_printk(FUJLAPTOP_DBG_WARN, - "Unsupported event [0x%x]\n", event); + /* On some models (first seen on the Skylake-based Lifebook + * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is + * handled in software; its state is queried using FUNC_RFKILL + */ + if ((fujitsu_hotkey->rfkill_supported & BIT(26)) && + (call_fext_func(FUNC_RFKILL, 0x1, 0x0, 0x0) & BIT(26))) { + keycode = KEY_TOUCHPAD_TOGGLE; input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); input_sync(input); - break; } + } /* Initialization */ diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 09356684c32f..493d8910a74e 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -251,6 +251,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), + AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index cb3ab2b212b1..bcf438f38781 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -1,5 +1,5 @@ /* - * Intel HID event driver for Windows 8 + * Intel HID event & 5 button array driver * * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> @@ -57,8 +57,24 @@ static const struct key_entry intel_hid_keymap[] = { { KE_END }, }; +/* 5 button array notification value. */ +static const struct key_entry intel_array_keymap[] = { + { KE_KEY, 0xC2, { KEY_LEFTMETA } }, /* Press */ + { KE_IGNORE, 0xC3, { KEY_LEFTMETA } }, /* Release */ + { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* Press */ + { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */ + { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */ + { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */ + { KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */ + { KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */ + { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */ + { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */ + { KE_END }, +}; + struct intel_hid_priv { struct input_dev *input_dev; + struct input_dev *array; }; static int intel_hid_set_enable(struct device *device, int enable) @@ -78,15 +94,43 @@ static int intel_hid_set_enable(struct device *device, int enable) return 0; } +static void intel_button_array_enable(struct device *device, bool enable) +{ + struct intel_hid_priv *priv = dev_get_drvdata(device); + acpi_handle handle = ACPI_HANDLE(device); + unsigned long long button_cap; + acpi_status status; + + if (!priv->array) + return; + + /* Query supported platform features */ + status = acpi_evaluate_integer(handle, "BTNC", NULL, &button_cap); + if (ACPI_FAILURE(status)) { + dev_warn(device, "failed to get button capability\n"); + return; + } + + /* Enable|disable features - power button is always enabled */ + status = acpi_execute_simple_method(handle, "BTNE", + enable ? button_cap : 1); + if (ACPI_FAILURE(status)) + dev_warn(device, "failed to set button capability\n"); +} + static int intel_hid_pl_suspend_handler(struct device *device) { intel_hid_set_enable(device, 0); + intel_button_array_enable(device, false); + return 0; } static int intel_hid_pl_resume_handler(struct device *device) { intel_hid_set_enable(device, 1); + intel_button_array_enable(device, true); + return 0; } @@ -126,6 +170,27 @@ err_free_device: return ret; } +static int intel_button_array_input_setup(struct platform_device *device) +{ + struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); + int ret; + + /* Setup input device for 5 button array */ + priv->array = devm_input_allocate_device(&device->dev); + if (!priv->array) + return -ENOMEM; + + ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL); + if (ret) + return ret; + + priv->array->dev.parent = &device->dev; + priv->array->name = "Intel HID 5 button array"; + priv->array->id.bustype = BUS_HOST; + + return input_register_device(priv->array); +} + static void intel_hid_input_destroy(struct platform_device *device) { struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); @@ -140,10 +205,11 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) unsigned long long ev_index; acpi_status status; - /* The platform spec only defines one event code: 0xC0. */ + /* 0xC0 is for HID events, other values are for 5 button array */ if (event != 0xc0) { - dev_warn(&device->dev, "received unknown event (0x%x)\n", - event); + if (!priv->array || + !sparse_keymap_report_event(priv->array, event, 1, true)) + dev_info(&device->dev, "unknown event 0x%x\n", event); return; } @@ -161,8 +227,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) static int intel_hid_probe(struct platform_device *device) { acpi_handle handle = ACPI_HANDLE(&device->dev); + unsigned long long event_cap, mode; struct intel_hid_priv *priv; - unsigned long long mode; acpi_status status; int err; @@ -193,6 +259,15 @@ static int intel_hid_probe(struct platform_device *device) return err; } + /* Setup 5 button array */ + status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap); + if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) { + dev_info(&device->dev, "platform supports 5 button array\n"); + err = intel_button_array_input_setup(device); + if (err) + pr_err("Failed to setup Intel 5 button array hotkeys\n"); + } + status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler, @@ -206,6 +281,16 @@ static int intel_hid_probe(struct platform_device *device) if (err) goto err_remove_notify; + if (priv->array) { + intel_button_array_enable(&device->dev, true); + + /* Call button load method to enable HID power button */ + status = acpi_evaluate_object(handle, "BTNL", NULL, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&device->dev, + "failed to enable HID power button\n"); + } + return 0; err_remove_notify: @@ -224,6 +309,7 @@ static int intel_hid_remove(struct platform_device *device) acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); intel_hid_input_destroy(device); intel_hid_set_enable(&device->dev, 0); + intel_button_array_enable(&device->dev, false); /* * Even if we failed to shut off the event stream, we can still diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 55663b3d7282..58dcee562d64 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -68,6 +68,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/sched.h> +#include <linux/sched/loadavg.h> #include <linux/seq_file.h> #include <linux/string.h> #include <linux/tick.h> diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 361770568ad0..871cfa682519 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -1,7 +1,10 @@ /* - * Power button driver for Medfield. + * Power button driver for Intel MID platforms. * - * Copyright (C) 2010 Intel Corp + * Copyright (C) 2010,2017 Intel Corp + * + * Author: Hong Liu <hong.liu@intel.com> + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,20 +14,20 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <linux/module.h> #include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/platform_device.h> #include <linux/input.h> +#include <linux/interrupt.h> #include <linux/mfd/intel_msic.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include <linux/pm_wakeirq.h> +#include <linux/slab.h> + +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> +#include <asm/intel_scu_ipc.h> #define DRIVER_NAME "msic_power_btn" @@ -36,37 +39,113 @@ */ #define MSIC_PWRBTNM (1 << 0) -static irqreturn_t mfld_pb_isr(int irq, void *dev_id) +/* Intel Tangier */ +#define BCOVE_PB_LEVEL (1 << 4) /* 1 - release, 0 - press */ + +/* Basin Cove PMIC */ +#define BCOVE_PBIRQ 0x02 +#define BCOVE_IRQLVL1MSK 0x0c +#define BCOVE_PBIRQMASK 0x0d +#define BCOVE_PBSTATUS 0x27 + +struct mid_pb_ddata { + struct device *dev; + int irq; + struct input_dev *input; + unsigned short mirqlvl1_addr; + unsigned short pbstat_addr; + u8 pbstat_mask; + int (*setup)(struct mid_pb_ddata *ddata); +}; + +static int mid_pbstat(struct mid_pb_ddata *ddata, int *value) { - struct input_dev *input = dev_id; + struct input_dev *input = ddata->input; int ret; u8 pbstat; - ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat); + ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat); + if (ret) + return ret; + dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat); + *value = !(pbstat & ddata->pbstat_mask); + return 0; +} + +static int mid_irq_ack(struct mid_pb_ddata *ddata) +{ + return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM); +} + +static int mrfld_setup(struct mid_pb_ddata *ddata) +{ + /* Unmask the PBIRQ and MPBIRQ on Tangier */ + intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM); + intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM); + + return 0; +} + +static irqreturn_t mid_pb_isr(int irq, void *dev_id) +{ + struct mid_pb_ddata *ddata = dev_id; + struct input_dev *input = ddata->input; + int value = 0; + int ret; + + ret = mid_pbstat(ddata, &value); if (ret < 0) { - dev_err(input->dev.parent, "Read error %d while reading" - " MSIC_PB_STATUS\n", ret); + dev_err(input->dev.parent, + "Read error %d while reading MSIC_PB_STATUS\n", ret); } else { - input_event(input, EV_KEY, KEY_POWER, - !(pbstat & MSIC_PB_LEVEL)); + input_event(input, EV_KEY, KEY_POWER, value); input_sync(input); } + mid_irq_ack(ddata); return IRQ_HANDLED; } -static int mfld_pb_probe(struct platform_device *pdev) +static struct mid_pb_ddata mfld_ddata = { + .mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK, + .pbstat_addr = INTEL_MSIC_PBSTATUS, + .pbstat_mask = MSIC_PB_LEVEL, +}; + +static struct mid_pb_ddata mrfld_ddata = { + .mirqlvl1_addr = BCOVE_IRQLVL1MSK, + .pbstat_addr = BCOVE_PBSTATUS, + .pbstat_mask = BCOVE_PB_LEVEL, + .setup = mrfld_setup, +}; + +#define ICPU(model, ddata) \ + { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata } + +static const struct x86_cpu_id mid_pb_cpu_ids[] = { + ICPU(INTEL_FAM6_ATOM_PENWELL, mfld_ddata), + ICPU(INTEL_FAM6_ATOM_MERRIFIELD, mrfld_ddata), + {} +}; + +static int mid_pb_probe(struct platform_device *pdev) { + const struct x86_cpu_id *id; + struct mid_pb_ddata *ddata; struct input_dev *input; int irq = platform_get_irq(pdev, 0); int error; + id = x86_match_cpu(mid_pb_cpu_ids); + if (!id) + return -ENODEV; + if (irq < 0) return -EINVAL; - input = input_allocate_device(); + input = devm_input_allocate_device(&pdev->dev); if (!input) return -ENOMEM; @@ -77,25 +156,36 @@ static int mfld_pb_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT, - DRIVER_NAME, input); - if (error) { - dev_err(&pdev->dev, "Unable to request irq %d for mfld power" - "button\n", irq); - goto err_free_input; + ddata = (struct mid_pb_ddata *)id->driver_data; + if (!ddata) + return -ENODATA; + + ddata->dev = &pdev->dev; + ddata->irq = irq; + ddata->input = input; + + if (ddata->setup) { + error = ddata->setup(ddata); + if (error) + return error; } - device_init_wakeup(&pdev->dev, true); - dev_pm_set_wake_irq(&pdev->dev, irq); + error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr, + IRQF_ONESHOT, DRIVER_NAME, ddata); + if (error) { + dev_err(&pdev->dev, + "Unable to request irq %d for MID power button\n", irq); + return error; + } error = input_register_device(input); if (error) { - dev_err(&pdev->dev, "Unable to register input dev, error " - "%d\n", error); - goto err_free_irq; + dev_err(&pdev->dev, + "Unable to register input dev, error %d\n", error); + return error; } - platform_set_drvdata(pdev, input); + platform_set_drvdata(pdev, ddata); /* * SCU firmware might send power button interrupts to IA core before @@ -107,46 +197,39 @@ static int mfld_pb_probe(struct platform_device *pdev) * initialization. The race happens rarely. So we needn't worry * about it. */ - error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM); + error = mid_irq_ack(ddata); if (error) { - dev_err(&pdev->dev, "Unable to clear power button interrupt, " - "error: %d\n", error); - goto err_free_irq; + dev_err(&pdev->dev, + "Unable to clear power button interrupt, error: %d\n", + error); + return error; } - return 0; + device_init_wakeup(&pdev->dev, true); + dev_pm_set_wake_irq(&pdev->dev, irq); -err_free_irq: - free_irq(irq, input); -err_free_input: - input_free_device(input); - return error; + return 0; } -static int mfld_pb_remove(struct platform_device *pdev) +static int mid_pb_remove(struct platform_device *pdev) { - struct input_dev *input = platform_get_drvdata(pdev); - int irq = platform_get_irq(pdev, 0); - dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); - free_irq(irq, input); - input_unregister_device(input); return 0; } -static struct platform_driver mfld_pb_driver = { +static struct platform_driver mid_pb_driver = { .driver = { .name = DRIVER_NAME, }, - .probe = mfld_pb_probe, - .remove = mfld_pb_remove, + .probe = mid_pb_probe, + .remove = mid_pb_remove, }; -module_platform_driver(mfld_pb_driver); +module_platform_driver(mid_pb_driver); MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); -MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); +MODULE_DESCRIPTION("Intel MID Power Button Driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 0df3c9d37509..008a76903cbf 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -549,9 +549,9 @@ static int mid_thermal_remove(struct platform_device *pdev) static const struct platform_device_id therm_id_table[] = { { DRIVER_NAME, 1 }, - { "msic_thermal", 1 }, { } }; +MODULE_DEVICE_TABLE(platform, therm_id_table); static struct platform_driver mid_thermal_driver = { .driver = { diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index b130b8c9b9d7..914bcd2edbde 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -188,8 +188,7 @@ static int pmc_core_check_read_lock_bit(void) u32 value; value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET); - return test_bit(SPT_PMC_READ_DISABLE_BIT, - (unsigned long *)&value); + return value & BIT(SPT_PMC_READ_DISABLE_BIT); } #if IS_ENABLED(CONFIG_DEBUG_FS) @@ -238,8 +237,7 @@ static int pmc_core_mtpmc_link_status(void) u32 value; value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET); - return test_bit(SPT_PMC_MSG_FULL_STS_BIT, - (unsigned long *)&value); + return value & BIT(SPT_PMC_MSG_FULL_STS_BIT); } static int pmc_core_send_msg(u32 *addr_xram) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 0bf51d574fa9..0651d47b8eeb 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -32,7 +32,10 @@ #include <linux/notifier.h> #include <linux/suspend.h> #include <linux/acpi.h> +#include <linux/io-64-nonatomic-lo-hi.h> + #include <asm/intel_pmc_ipc.h> + #include <linux/platform_data/itco_wdt.h> /* @@ -54,6 +57,18 @@ #define IPC_WRITE_BUFFER 0x80 #define IPC_READ_BUFFER 0x90 +/* PMC Global Control Registers */ +#define GCR_TELEM_DEEP_S0IX_OFFSET 0x1078 +#define GCR_TELEM_SHLW_S0IX_OFFSET 0x1080 + +/* Residency with clock rate at 19.2MHz to usecs */ +#define S0IX_RESIDENCY_IN_USECS(d, s) \ +({ \ + u64 result = 10ull * ((d) + (s)); \ + do_div(result, 192); \ + result; \ +}) + /* * 16-byte buffer for sending data associated with IPC command. */ @@ -68,7 +83,7 @@ #define PLAT_RESOURCE_IPC_INDEX 0 #define PLAT_RESOURCE_IPC_SIZE 0x1000 #define PLAT_RESOURCE_GCR_OFFSET 0x1008 -#define PLAT_RESOURCE_GCR_SIZE 0x4 +#define PLAT_RESOURCE_GCR_SIZE 0x1000 #define PLAT_RESOURCE_BIOS_DATA_INDEX 1 #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 #define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3 @@ -97,8 +112,6 @@ #define TCO_PMC_OFFSET 0x8 #define TCO_PMC_SIZE 0x4 -static const int iTCO_version = 3; - static struct intel_pmc_ipc_dev { struct device *dev; void __iomem *ipc_base; @@ -115,6 +128,7 @@ static struct intel_pmc_ipc_dev { /* gcr */ resource_size_t gcr_base; int gcr_size; + bool has_gcr_regs; /* punit */ struct platform_device *punit_dev; @@ -180,6 +194,11 @@ static inline u32 ipc_data_readl(u32 offset) return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); } +static inline u64 gcr_data_readq(u32 offset) +{ + return readq(ipcdev.ipc_base + offset); +} + static int intel_pmc_ipc_check_status(void) { int status; @@ -389,6 +408,7 @@ static void ipc_pci_remove(struct pci_dev *pdev) static const struct pci_device_id ipc_pci_ids[] = { {PCI_VDEVICE(INTEL, 0x0a94), 0}, {PCI_VDEVICE(INTEL, 0x1a94), 0}, + {PCI_VDEVICE(INTEL, 0x5a94), 0}, { 0,} }; MODULE_DEVICE_TABLE(pci, ipc_pci_ids); @@ -712,7 +732,8 @@ static int ipc_plat_get_res(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to get ipc resource\n"); return -ENXIO; } - size = PLAT_RESOURCE_IPC_SIZE; + size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE; + if (!request_mem_region(res->start, size, pdev->name)) { dev_err(&pdev->dev, "Failed to request ipc resource\n"); return -EBUSY; @@ -748,6 +769,28 @@ static int ipc_plat_get_res(struct platform_device *pdev) return 0; } +/** + * intel_pmc_s0ix_counter_read() - Read S0ix residency. + * @data: Out param that contains current S0ix residency count. + * + * Return: an error code or 0 on success. + */ +int intel_pmc_s0ix_counter_read(u64 *data) +{ + u64 deep, shlw; + + if (!ipcdev.has_gcr_regs) + return -EACCES; + + deep = gcr_data_readq(GCR_TELEM_DEEP_S0IX_OFFSET); + shlw = gcr_data_readq(GCR_TELEM_SHLW_S0IX_OFFSET); + + *data = S0IX_RESIDENCY_IN_USECS(deep, shlw); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read); + #ifdef CONFIG_ACPI static const struct acpi_device_id ipc_acpi_ids[] = { { "INT34D2", 0}, @@ -797,6 +840,8 @@ static int ipc_plat_probe(struct platform_device *pdev) goto err_sys; } + ipcdev.has_gcr_regs = true; + return 0; err_sys: free_irq(ipcdev.irq, &ipcdev); @@ -808,8 +853,11 @@ err_device: iounmap(ipcdev.ipc_base); res = platform_get_resource(pdev, IORESOURCE_MEM, PLAT_RESOURCE_IPC_INDEX); - if (res) - release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE); + if (res) { + release_mem_region(res->start, + PLAT_RESOURCE_IPC_SIZE + + PLAT_RESOURCE_GCR_SIZE); + } return ret; } @@ -825,8 +873,11 @@ static int ipc_plat_remove(struct platform_device *pdev) iounmap(ipcdev.ipc_base); res = platform_get_resource(pdev, IORESOURCE_MEM, PLAT_RESOURCE_IPC_INDEX); - if (res) - release_mem_region(res->start, PLAT_RESOURCE_IPC_SIZE); + if (res) { + release_mem_region(res->start, + PLAT_RESOURCE_IPC_SIZE + + PLAT_RESOURCE_GCR_SIZE); + } ipcdev.dev = NULL; return 0; } diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c new file mode 100644 index 000000000000..4f60d8e32a0a --- /dev/null +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -0,0 +1,151 @@ +/* + * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver + * Copyright (c) 2017, Intel Corporation. + * All rights reserved. + * + * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/topology.h> +#include <linux/workqueue.h> +#include <linux/cpuhotplug.h> +#include <linux/cpufeature.h> +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> + +#define MSR_OC_MAILBOX 0x150 +#define MSR_OC_MAILBOX_CMD_OFFSET 32 +#define MSR_OC_MAILBOX_RSP_OFFSET 32 +#define MSR_OC_MAILBOX_BUSY_BIT 63 +#define OC_MAILBOX_FC_CONTROL_CMD 0x1C + +/* + * Typical latency to get mail box response is ~3us, It takes +3 us to + * process reading mailbox after issuing mailbox write on a Broadwell 3.4 GHz + * system. So for most of the time, the first mailbox read should have the + * response, but to avoid some boundary cases retry twice. + */ +#define OC_MAILBOX_RETRY_COUNT 2 + +static int get_oc_core_priority(unsigned int cpu) +{ + u64 value, cmd = OC_MAILBOX_FC_CONTROL_CMD; + int ret, i; + + /* Issue favored core read command */ + value = cmd << MSR_OC_MAILBOX_CMD_OFFSET; + /* Set the busy bit to indicate OS is trying to issue command */ + value |= BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT); + ret = wrmsrl_safe(MSR_OC_MAILBOX, value); + if (ret) { + pr_debug("cpu %d OC mailbox write failed\n", cpu); + return ret; + } + + for (i = 0; i < OC_MAILBOX_RETRY_COUNT; ++i) { + ret = rdmsrl_safe(MSR_OC_MAILBOX, &value); + if (ret) { + pr_debug("cpu %d OC mailbox read failed\n", cpu); + break; + } + + if (value & BIT_ULL(MSR_OC_MAILBOX_BUSY_BIT)) { + pr_debug("cpu %d OC mailbox still processing\n", cpu); + ret = -EBUSY; + continue; + } + + if ((value >> MSR_OC_MAILBOX_RSP_OFFSET) & 0xff) { + pr_debug("cpu %d OC mailbox cmd failed\n", cpu); + ret = -ENXIO; + break; + } + + ret = value & 0xff; + pr_debug("cpu %d max_ratio %d\n", cpu, ret); + break; + } + + return ret; +} + +/* + * The work item is needed to avoid CPU hotplug locking issues. The function + * itmt_legacy_set_priority() is called from CPU online callback, so can't + * call sched_set_itmt_support() from there as this function will aquire + * hotplug locks in its path. + */ +static void itmt_legacy_work_fn(struct work_struct *work) +{ + sched_set_itmt_support(); +} + +static DECLARE_WORK(sched_itmt_work, itmt_legacy_work_fn); + +static int itmt_legacy_cpu_online(unsigned int cpu) +{ + static u32 max_highest_perf = 0, min_highest_perf = U32_MAX; + int priority; + + priority = get_oc_core_priority(cpu); + if (priority < 0) + return 0; + + sched_set_itmt_core_prio(priority, cpu); + + /* Enable ITMT feature when a core with different priority is found */ + if (max_highest_perf <= min_highest_perf) { + if (priority > max_highest_perf) + max_highest_perf = priority; + + if (priority < min_highest_perf) + min_highest_perf = priority; + + if (max_highest_perf > min_highest_perf) + schedule_work(&sched_itmt_work); + } + + return 0; +} + +#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } + +static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { + ICPU(INTEL_FAM6_BROADWELL_X), + {} +}; + +static int __init itmt_legacy_init(void) +{ + const struct x86_cpu_id *id; + int ret; + + id = x86_match_cpu(itmt_legacy_cpu_ids); + if (!id) + return -ENODEV; + + if (boot_cpu_has(X86_FEATURE_HWP)) + return -ENODEV; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "platform/x86/turbo_max_3:online", + itmt_legacy_cpu_online, NULL); + if (ret < 0) + return ret; + + return 0; +} +late_initcall(itmt_legacy_init) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 25f15df5c2d7..8f98c211b440 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -45,6 +45,10 @@ /* LPC bus IO offsets */ #define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000 #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 +#define MLXPLAT_CPLD_LPC_REG_AGGR_ADRR 0x253a +#define MLXPLAT_CPLD_LPC_REG_PSU_ADRR 0x2558 +#define MLXPLAT_CPLD_LPC_REG_PWR_ADRR 0x2564 +#define MLXPLAT_CPLD_LPC_REG_FAN_ADRR 0x2588 #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda @@ -56,6 +60,17 @@ MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \ MLXPLAT_CPLD_LPC_PIO_OFFSET) +/* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */ +#define MLXPLAT_CPLD_AGGR_PSU_MASK_DEF 0x08 +#define MLXPLAT_CPLD_AGGR_PWR_MASK_DEF 0x08 +#define MLXPLAT_CPLD_AGGR_FAN_MASK_DEF 0x40 +#define MLXPLAT_CPLD_AGGR_MASK_DEF (MLXPLAT_CPLD_AGGR_PSU_MASK_DEF | \ + MLXPLAT_CPLD_AGGR_FAN_MASK_DEF) +#define MLXPLAT_CPLD_AGGR_MASK_MSN21XX 0x04 +#define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) +#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) +#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) + /* Start channel numbers */ #define MLXPLAT_CPLD_CH1 2 #define MLXPLAT_CPLD_CH2 10 @@ -123,7 +138,7 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { }; /* Platform hotplug devices */ -static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = { +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_psu[] = { { .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) }, .bus = 10, @@ -134,7 +149,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = { }, }; -static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = { +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_pwr[] = { { .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) }, .bus = 10, @@ -145,7 +160,7 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = { }, }; -static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = { +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_fan[] = { { .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, .bus = 11, @@ -166,38 +181,38 @@ static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = { /* Platform hotplug default data */ static -struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_default_data = { - .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), - .top_aggr_mask = 0x48, - .top_aggr_psu_mask = 0x08, - .psu_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x58), - .psu_mask = 0x03, - .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_psu), - .psu = mlxplat_mlxcpld_hotplug_psu, - .top_aggr_pwr_mask = 0x08, - .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), - .pwr_mask = 0x03, - .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), - .pwr = mlxplat_mlxcpld_hotplug_pwr, - .top_aggr_fan_mask = 0x40, - .fan_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x88), - .fan_mask = 0x0f, - .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_fan), - .fan = mlxplat_mlxcpld_hotplug_fan, +struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_default_data = { + .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, + .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_DEF, + .top_aggr_psu_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF, + .psu_reg_offset = MLXPLAT_CPLD_LPC_REG_PSU_ADRR, + .psu_mask = MLXPLAT_CPLD_PSU_MASK, + .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_psu), + .psu = mlxplat_mlxcpld_psu, + .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, + .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, + .pwr_mask = MLXPLAT_CPLD_PWR_MASK, + .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), + .pwr = mlxplat_mlxcpld_pwr, + .top_aggr_fan_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF, + .fan_reg_offset = MLXPLAT_CPLD_LPC_REG_FAN_ADRR, + .fan_mask = MLXPLAT_CPLD_FAN_MASK, + .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_fan), + .fan = mlxplat_mlxcpld_fan, }; /* Platform hotplug MSN21xx system family data */ static -struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_msn21xx_data = { - .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), - .top_aggr_mask = 0x04, - .top_aggr_pwr_mask = 0x04, - .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), - .pwr_mask = 0x03, - .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), +struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_msn21xx_data = { + .top_aggr_offset = MLXPLAT_CPLD_LPC_REG_AGGR_ADRR, + .top_aggr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, + .top_aggr_pwr_mask = MLXPLAT_CPLD_AGGR_MASK_MSN21XX, + .pwr_reg_offset = MLXPLAT_CPLD_LPC_REG_PWR_ADRR, + .pwr_mask = MLXPLAT_CPLD_PWR_MASK, + .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), }; -static struct resource mlxplat_mlxcpld_hotplug_resources[] = { +static struct resource mlxplat_mlxcpld_resources[] = { [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), }; @@ -213,7 +228,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) mlxplat_mux_data[i].n_values = ARRAY_SIZE(mlxplat_default_channels[i]); } - mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_default_data; + mlxplat_hotplug = &mlxplat_mlxcpld_default_data; return 1; }; @@ -227,7 +242,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) mlxplat_mux_data[i].n_values = ARRAY_SIZE(mlxplat_msn21xx_channels); } - mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_msn21xx_data; + mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; return 1; }; @@ -314,9 +329,10 @@ static int __init mlxplat_init(void) } priv->pdev_hotplug = platform_device_register_resndata( - &mlxplat_dev->dev, "mlxcpld-hotplug", -1, - mlxplat_mlxcpld_hotplug_resources, - ARRAY_SIZE(mlxplat_mlxcpld_hotplug_resources), + &mlxplat_dev->dev, "mlxcpld-hotplug", + PLATFORM_DEVID_NONE, + mlxplat_mlxcpld_resources, + ARRAY_SIZE(mlxplat_mlxcpld_resources), mlxplat_hotplug, sizeof(*mlxplat_hotplug)); if (IS_ERR(priv->pdev_hotplug)) { err = PTR_ERR(priv->pdev_hotplug); diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c new file mode 100644 index 000000000000..77bac859342d --- /dev/null +++ b/drivers/platform/x86/pmc_atom.c @@ -0,0 +1,532 @@ +/* + * Intel Atom SOC Power Management Controller Driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_data/x86/clk-pmc-atom.h> +#include <linux/platform_data/x86/pmc_atom.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/seq_file.h> + +struct pmc_bit_map { + const char *name; + u32 bit_mask; +}; + +struct pmc_reg_map { + const struct pmc_bit_map *d3_sts_0; + const struct pmc_bit_map *d3_sts_1; + const struct pmc_bit_map *func_dis; + const struct pmc_bit_map *func_dis_2; + const struct pmc_bit_map *pss; +}; + +struct pmc_data { + const struct pmc_reg_map *map; + const struct pmc_clk *clks; +}; + +struct pmc_dev { + u32 base_addr; + void __iomem *regmap; + const struct pmc_reg_map *map; +#ifdef CONFIG_DEBUG_FS + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ + bool init; +}; + +static struct pmc_dev pmc_device; +static u32 acpi_base_addr; + +static const struct pmc_clk byt_clks[] = { + { + .name = "xtal", + .freq = 25000000, + .parent_name = NULL, + }, + { + .name = "pll", + .freq = 19200000, + .parent_name = "xtal", + }, + {}, +}; + +static const struct pmc_clk cht_clks[] = { + { + .name = "xtal", + .freq = 19200000, + .parent_name = NULL, + }, + {}, +}; + +static const struct pmc_bit_map d3_sts_0_map[] = { + {"LPSS1_F0_DMA", BIT_LPSS1_F0_DMA}, + {"LPSS1_F1_PWM1", BIT_LPSS1_F1_PWM1}, + {"LPSS1_F2_PWM2", BIT_LPSS1_F2_PWM2}, + {"LPSS1_F3_HSUART1", BIT_LPSS1_F3_HSUART1}, + {"LPSS1_F4_HSUART2", BIT_LPSS1_F4_HSUART2}, + {"LPSS1_F5_SPI", BIT_LPSS1_F5_SPI}, + {"LPSS1_F6_Reserved", BIT_LPSS1_F6_XXX}, + {"LPSS1_F7_Reserved", BIT_LPSS1_F7_XXX}, + {"SCC_EMMC", BIT_SCC_EMMC}, + {"SCC_SDIO", BIT_SCC_SDIO}, + {"SCC_SDCARD", BIT_SCC_SDCARD}, + {"SCC_MIPI", BIT_SCC_MIPI}, + {"HDA", BIT_HDA}, + {"LPE", BIT_LPE}, + {"OTG", BIT_OTG}, + {"USH", BIT_USH}, + {"GBE", BIT_GBE}, + {"SATA", BIT_SATA}, + {"USB_EHCI", BIT_USB_EHCI}, + {"SEC", BIT_SEC}, + {"PCIE_PORT0", BIT_PCIE_PORT0}, + {"PCIE_PORT1", BIT_PCIE_PORT1}, + {"PCIE_PORT2", BIT_PCIE_PORT2}, + {"PCIE_PORT3", BIT_PCIE_PORT3}, + {"LPSS2_F0_DMA", BIT_LPSS2_F0_DMA}, + {"LPSS2_F1_I2C1", BIT_LPSS2_F1_I2C1}, + {"LPSS2_F2_I2C2", BIT_LPSS2_F2_I2C2}, + {"LPSS2_F3_I2C3", BIT_LPSS2_F3_I2C3}, + {"LPSS2_F3_I2C4", BIT_LPSS2_F4_I2C4}, + {"LPSS2_F5_I2C5", BIT_LPSS2_F5_I2C5}, + {"LPSS2_F6_I2C6", BIT_LPSS2_F6_I2C6}, + {"LPSS2_F7_I2C7", BIT_LPSS2_F7_I2C7}, + {}, +}; + +static struct pmc_bit_map byt_d3_sts_1_map[] = { + {"SMB", BIT_SMB}, + {"OTG_SS_PHY", BIT_OTG_SS_PHY}, + {"USH_SS_PHY", BIT_USH_SS_PHY}, + {"DFX", BIT_DFX}, + {}, +}; + +static struct pmc_bit_map cht_d3_sts_1_map[] = { + {"SMB", BIT_SMB}, + {"GMM", BIT_STS_GMM}, + {"ISH", BIT_STS_ISH}, + {}, +}; + +static struct pmc_bit_map cht_func_dis_2_map[] = { + {"SMB", BIT_SMB}, + {"GMM", BIT_FD_GMM}, + {"ISH", BIT_FD_ISH}, + {}, +}; + +static const struct pmc_bit_map byt_pss_map[] = { + {"GBE", PMC_PSS_BIT_GBE}, + {"SATA", PMC_PSS_BIT_SATA}, + {"HDA", PMC_PSS_BIT_HDA}, + {"SEC", PMC_PSS_BIT_SEC}, + {"PCIE", PMC_PSS_BIT_PCIE}, + {"LPSS", PMC_PSS_BIT_LPSS}, + {"LPE", PMC_PSS_BIT_LPE}, + {"DFX", PMC_PSS_BIT_DFX}, + {"USH_CTRL", PMC_PSS_BIT_USH_CTRL}, + {"USH_SUS", PMC_PSS_BIT_USH_SUS}, + {"USH_VCCS", PMC_PSS_BIT_USH_VCCS}, + {"USH_VCCA", PMC_PSS_BIT_USH_VCCA}, + {"OTG_CTRL", PMC_PSS_BIT_OTG_CTRL}, + {"OTG_VCCS", PMC_PSS_BIT_OTG_VCCS}, + {"OTG_VCCA_CLK", PMC_PSS_BIT_OTG_VCCA_CLK}, + {"OTG_VCCA", PMC_PSS_BIT_OTG_VCCA}, + {"USB", PMC_PSS_BIT_USB}, + {"USB_SUS", PMC_PSS_BIT_USB_SUS}, + {}, +}; + +static const struct pmc_bit_map cht_pss_map[] = { + {"SATA", PMC_PSS_BIT_SATA}, + {"HDA", PMC_PSS_BIT_HDA}, + {"SEC", PMC_PSS_BIT_SEC}, + {"PCIE", PMC_PSS_BIT_PCIE}, + {"LPSS", PMC_PSS_BIT_LPSS}, + {"LPE", PMC_PSS_BIT_LPE}, + {"UFS", PMC_PSS_BIT_CHT_UFS}, + {"UXD", PMC_PSS_BIT_CHT_UXD}, + {"UXD_FD", PMC_PSS_BIT_CHT_UXD_FD}, + {"UX_ENG", PMC_PSS_BIT_CHT_UX_ENG}, + {"USB_SUS", PMC_PSS_BIT_CHT_USB_SUS}, + {"GMM", PMC_PSS_BIT_CHT_GMM}, + {"ISH", PMC_PSS_BIT_CHT_ISH}, + {"DFX_MASTER", PMC_PSS_BIT_CHT_DFX_MASTER}, + {"DFX_CLUSTER1", PMC_PSS_BIT_CHT_DFX_CLUSTER1}, + {"DFX_CLUSTER2", PMC_PSS_BIT_CHT_DFX_CLUSTER2}, + {"DFX_CLUSTER3", PMC_PSS_BIT_CHT_DFX_CLUSTER3}, + {"DFX_CLUSTER4", PMC_PSS_BIT_CHT_DFX_CLUSTER4}, + {"DFX_CLUSTER5", PMC_PSS_BIT_CHT_DFX_CLUSTER5}, + {}, +}; + +static const struct pmc_reg_map byt_reg_map = { + .d3_sts_0 = d3_sts_0_map, + .d3_sts_1 = byt_d3_sts_1_map, + .func_dis = d3_sts_0_map, + .func_dis_2 = byt_d3_sts_1_map, + .pss = byt_pss_map, +}; + +static const struct pmc_reg_map cht_reg_map = { + .d3_sts_0 = d3_sts_0_map, + .d3_sts_1 = cht_d3_sts_1_map, + .func_dis = d3_sts_0_map, + .func_dis_2 = cht_func_dis_2_map, + .pss = cht_pss_map, +}; + +static const struct pmc_data byt_data = { + .map = &byt_reg_map, + .clks = byt_clks, +}; + +static const struct pmc_data cht_data = { + .map = &cht_reg_map, + .clks = cht_clks, +}; + +static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset) +{ + return readl(pmc->regmap + reg_offset); +} + +static inline void pmc_reg_write(struct pmc_dev *pmc, int reg_offset, u32 val) +{ + writel(val, pmc->regmap + reg_offset); +} + +int pmc_atom_read(int offset, u32 *value) +{ + struct pmc_dev *pmc = &pmc_device; + + if (!pmc->init) + return -ENODEV; + + *value = pmc_reg_read(pmc, offset); + return 0; +} +EXPORT_SYMBOL_GPL(pmc_atom_read); + +int pmc_atom_write(int offset, u32 value) +{ + struct pmc_dev *pmc = &pmc_device; + + if (!pmc->init) + return -ENODEV; + + pmc_reg_write(pmc, offset, value); + return 0; +} +EXPORT_SYMBOL_GPL(pmc_atom_write); + +static void pmc_power_off(void) +{ + u16 pm1_cnt_port; + u32 pm1_cnt_value; + + pr_info("Preparing to enter system sleep state S5\n"); + + pm1_cnt_port = acpi_base_addr + PM1_CNT; + + pm1_cnt_value = inl(pm1_cnt_port); + pm1_cnt_value &= SLEEP_TYPE_MASK; + pm1_cnt_value |= SLEEP_TYPE_S5; + pm1_cnt_value |= SLEEP_ENABLE; + + outl(pm1_cnt_value, pm1_cnt_port); +} + +static void pmc_hw_reg_setup(struct pmc_dev *pmc) +{ + /* + * Disable PMC S0IX_WAKE_EN events coming from: + * - LPC clock run + * - GPIO_SUS ored dedicated IRQs + * - GPIO_SCORE ored dedicated IRQs + * - GPIO_SUS shared IRQ + * - GPIO_SCORE shared IRQ + */ + pmc_reg_write(pmc, PMC_S0IX_WAKE_EN, (u32)PMC_WAKE_EN_SETTING); +} + +#ifdef CONFIG_DEBUG_FS +static void pmc_dev_state_print(struct seq_file *s, int reg_index, + u32 sts, const struct pmc_bit_map *sts_map, + u32 fd, const struct pmc_bit_map *fd_map) +{ + int offset = PMC_REG_BIT_WIDTH * reg_index; + int index; + + for (index = 0; sts_map[index].name; index++) { + seq_printf(s, "Dev: %-2d - %-32s\tState: %s [%s]\n", + offset + index, sts_map[index].name, + fd_map[index].bit_mask & fd ? "Disabled" : "Enabled ", + sts_map[index].bit_mask & sts ? "D3" : "D0"); + } +} + +static int pmc_dev_state_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmc = s->private; + const struct pmc_reg_map *m = pmc->map; + u32 func_dis, func_dis_2; + u32 d3_sts_0, d3_sts_1; + + func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS); + func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2); + d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0); + d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1); + + /* Low part */ + pmc_dev_state_print(s, 0, d3_sts_0, m->d3_sts_0, func_dis, m->func_dis); + + /* High part */ + pmc_dev_state_print(s, 1, d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2); + + return 0; +} + +static int pmc_dev_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmc_dev_state_show, inode->i_private); +} + +static const struct file_operations pmc_dev_state_ops = { + .open = pmc_dev_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pmc_pss_state_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmc = s->private; + const struct pmc_bit_map *map = pmc->map->pss; + u32 pss = pmc_reg_read(pmc, PMC_PSS); + int index; + + for (index = 0; map[index].name; index++) { + seq_printf(s, "Island: %-2d - %-32s\tState: %s\n", + index, map[index].name, + map[index].bit_mask & pss ? "Off" : "On"); + } + return 0; +} + +static int pmc_pss_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmc_pss_state_show, inode->i_private); +} + +static const struct file_operations pmc_pss_state_ops = { + .open = pmc_pss_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pmc_sleep_tmr_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmc = s->private; + u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr; + + s0ir_tmr = (u64)pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT; + s0i1_tmr = (u64)pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT; + s0i2_tmr = (u64)pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT; + s0i3_tmr = (u64)pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT; + s0_tmr = (u64)pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT; + + seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr); + seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr); + seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr); + seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr); + seq_printf(s, "S0 Residency:\t%lldus\n", s0_tmr); + return 0; +} + +static int pmc_sleep_tmr_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmc_sleep_tmr_show, inode->i_private); +} + +static const struct file_operations pmc_sleep_tmr_ops = { + .open = pmc_sleep_tmr_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void pmc_dbgfs_unregister(struct pmc_dev *pmc) +{ + debugfs_remove_recursive(pmc->dbgfs_dir); +} + +static int pmc_dbgfs_register(struct pmc_dev *pmc) +{ + struct dentry *dir, *f; + + dir = debugfs_create_dir("pmc_atom", NULL); + if (!dir) + return -ENOMEM; + + pmc->dbgfs_dir = dir; + + f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO, + dir, pmc, &pmc_dev_state_ops); + if (!f) + goto err; + + f = debugfs_create_file("pss_state", S_IFREG | S_IRUGO, + dir, pmc, &pmc_pss_state_ops); + if (!f) + goto err; + + f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO, + dir, pmc, &pmc_sleep_tmr_ops); + if (!f) + goto err; + + return 0; +err: + pmc_dbgfs_unregister(pmc); + return -ENODEV; +} +#else +static int pmc_dbgfs_register(struct pmc_dev *pmc) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap, + const struct pmc_data *pmc_data) +{ + struct platform_device *clkdev; + struct pmc_clk_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->base = pmc_regmap; /* offset is added by client */ + clk_data->clks = pmc_data->clks; + + clkdev = platform_device_register_data(&pdev->dev, "clk-pmc-atom", + PLATFORM_DEVID_NONE, + clk_data, sizeof(*clk_data)); + if (IS_ERR(clkdev)) { + kfree(clk_data); + return PTR_ERR(clkdev); + } + + kfree(clk_data); + + return 0; +} + +static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct pmc_dev *pmc = &pmc_device; + const struct pmc_data *data = (struct pmc_data *)ent->driver_data; + const struct pmc_reg_map *map = data->map; + int ret; + + /* Obtain ACPI base address */ + pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr); + acpi_base_addr &= ACPI_BASE_ADDR_MASK; + + /* Install power off function */ + if (acpi_base_addr != 0 && pm_power_off == NULL) + pm_power_off = pmc_power_off; + + pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr); + pmc->base_addr &= PMC_BASE_ADDR_MASK; + + pmc->regmap = ioremap_nocache(pmc->base_addr, PMC_MMIO_REG_LEN); + if (!pmc->regmap) { + dev_err(&pdev->dev, "error: ioremap failed\n"); + return -ENOMEM; + } + + pmc->map = map; + + /* PMC hardware registers setup */ + pmc_hw_reg_setup(pmc); + + ret = pmc_dbgfs_register(pmc); + if (ret) + dev_warn(&pdev->dev, "debugfs register failed\n"); + + /* Register platform clocks - PMC_PLT_CLK [0..5] */ + ret = pmc_setup_clks(pdev, pmc->regmap, data); + if (ret) + dev_warn(&pdev->dev, "platform clocks register failed: %d\n", + ret); + + pmc->init = true; + return ret; +} + +/* + * Data for PCI driver interface + * + * used by pci_match_id() call below. + */ +static const struct pci_device_id pmc_pci_ids[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_data }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_data }, + { 0, }, +}; + +static int __init pmc_atom_init(void) +{ + struct pci_dev *pdev = NULL; + const struct pci_device_id *ent; + + /* We look for our device - PCU PMC + * we assume that there is max. one device. + * + * We can't use plain pci_driver mechanism, + * as the device is really a multiple function device, + * main driver that binds to the pci_device is lpc_ich + * and have to find & bind to the device this way. + */ + for_each_pci_dev(pdev) { + ent = pci_match_id(pmc_pci_ids, pdev); + if (ent) + return pmc_setup_dev(pdev, ent); + } + /* Device not found. */ + return -ENODEV; +} + +device_initcall(pmc_atom_init); + +/* +MODULE_AUTHOR("Aubrey Li <aubrey.li@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface"); +MODULE_LICENSE("GPL v2"); +*/ diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c new file mode 100644 index 000000000000..02e11fdbf375 --- /dev/null +++ b/drivers/platform/x86/silead_dmi.c @@ -0,0 +1,136 @@ +/* + * Silead touchscreen driver DMI based configuration code + * + * Copyright (c) 2017 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Red Hat authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/notifier.h> +#include <linux/property.h> +#include <linux/string.h> + +struct silead_ts_dmi_data { + const char *acpi_name; + struct property_entry *properties; +}; + +static struct property_entry cube_iwork8_air_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1660), + PROPERTY_ENTRY_U32("touchscreen-size-y", 900), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + { } +}; + +static const struct silead_ts_dmi_data cube_iwork8_air_data = { + .acpi_name = "MSSL1680:00", + .properties = cube_iwork8_air_props, +}; + +static struct property_entry jumper_ezpad_mini3_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1700), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + { } +}; + +static const struct silead_ts_dmi_data jumper_ezpad_mini3_data = { + .acpi_name = "MSSL1680:00", + .properties = jumper_ezpad_mini3_props, +}; + +static const struct dmi_system_id silead_ts_dmi_table[] = { + { + /* CUBE iwork8 Air */ + .driver_data = (void *)&cube_iwork8_air_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "cube"), + DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + }, + }, + { + /* Jumper EZpad mini3 */ + .driver_data = (void *)&jumper_ezpad_mini3_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + /* jumperx.T87.KFBNEEA02 with the version-nr dropped */ + DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"), + }, + }, + { }, +}; + +static void silead_ts_dmi_add_props(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + const struct dmi_system_id *dmi_id; + const struct silead_ts_dmi_data *ts_data; + int error; + + dmi_id = dmi_first_match(silead_ts_dmi_table); + if (!dmi_id) + return; + + ts_data = dmi_id->driver_data; + if (has_acpi_companion(dev) && + !strncmp(ts_data->acpi_name, client->name, I2C_NAME_SIZE)) { + error = device_add_properties(dev, ts_data->properties); + if (error) + dev_err(dev, "failed to add properties: %d\n", error); + } +} + +static int silead_ts_dmi_notifier_call(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + silead_ts_dmi_add_props(dev); + break; + + default: + break; + } + + return 0; +} + +static struct notifier_block silead_ts_dmi_notifier = { + .notifier_call = silead_ts_dmi_notifier_call, +}; + +static int __init silead_ts_dmi_init(void) +{ + int error; + + error = bus_register_notifier(&i2c_bus_type, &silead_ts_dmi_notifier); + if (error) + pr_err("%s: failed to register i2c bus notifier: %d\n", + __func__, error); + + return error; +} + +/* + * We are registering out notifier after i2c core is initialized and i2c bus + * itself is ready (which happens at postcore initcall level), but before + * ACPI starts enumerating devices (at subsys initcall level). + */ +arch_initcall(silead_ts_dmi_init); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index cacb43fb1df7..1d18b32628ec 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -163,6 +163,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */ TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */ TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */ + TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight */ TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ @@ -372,11 +373,9 @@ enum led_status_t { TPACPI_LED_BLINK, }; -/* Special LED class that can defer work */ +/* tpacpi LED class */ struct tpacpi_led_classdev { struct led_classdev led_classdev; - struct work_struct work; - enum led_status_t new_state; int led; }; @@ -1959,7 +1958,7 @@ enum { /* Positions of some of the keys in hotkey masks */ TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12, TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME, TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND, - TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, + TP_ACPI_HKEY_KBD_LIGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP, TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE, TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP, TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, @@ -2344,7 +2343,7 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY); n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE); } - if (m & TP_ACPI_HKEY_THNKLGHT_MASK) { + if (m & TP_ACPI_HKEY_KBD_LIGHT_MASK) { d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT); n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT); } @@ -5084,18 +5083,27 @@ static struct ibm_struct video_driver_data = { * Keyboard backlight subdriver */ +static enum led_brightness kbdlight_brightness; +static DEFINE_MUTEX(kbdlight_mutex); + static int kbdlight_set_level(int level) { + int ret = 0; + if (!hkey_handle) return -ENXIO; + mutex_lock(&kbdlight_mutex); + if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level)) - return -EIO; + ret = -EIO; + else + kbdlight_brightness = level; - return 0; -} + mutex_unlock(&kbdlight_mutex); -static int kbdlight_set_level_and_update(int level); + return ret; +} static int kbdlight_get_level(void) { @@ -5158,24 +5166,10 @@ static bool kbdlight_is_supported(void) return status & BIT(9); } -static void kbdlight_set_worker(struct work_struct *work) -{ - struct tpacpi_led_classdev *data = - container_of(work, struct tpacpi_led_classdev, work); - - if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) - kbdlight_set_level_and_update(data->new_state); -} - -static void kbdlight_sysfs_set(struct led_classdev *led_cdev, +static int kbdlight_sysfs_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct tpacpi_led_classdev *data = - container_of(led_cdev, - struct tpacpi_led_classdev, - led_classdev); - data->new_state = brightness; - queue_work(tpacpi_wq, &data->work); + return kbdlight_set_level(brightness); } static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev) @@ -5193,7 +5187,8 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = { .led_classdev = { .name = "tpacpi::kbd_backlight", .max_brightness = 2, - .brightness_set = &kbdlight_sysfs_set, + .flags = LED_BRIGHT_HW_CHANGED, + .brightness_set_blocking = &kbdlight_sysfs_set, .brightness_get = &kbdlight_sysfs_get, } }; @@ -5205,7 +5200,6 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n"); TPACPI_ACPIHANDLE_INIT(hkey); - INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker); if (!kbdlight_is_supported()) { tp_features.kbdlight = 0; @@ -5213,6 +5207,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) return 1; } + kbdlight_brightness = kbdlight_sysfs_get(NULL); tp_features.kbdlight = 1; rc = led_classdev_register(&tpacpi_pdev->dev, @@ -5222,6 +5217,8 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm) return rc; } + tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | + TP_ACPI_HKEY_KBD_LIGHT_MASK); return 0; } @@ -5229,7 +5226,6 @@ static void kbdlight_exit(void) { if (tp_features.kbdlight) led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev); - flush_workqueue(tpacpi_wq); } static int kbdlight_set_level_and_update(int level) @@ -5358,25 +5354,11 @@ static int light_set_status(int status) return -ENXIO; } -static void light_set_status_worker(struct work_struct *work) -{ - struct tpacpi_led_classdev *data = - container_of(work, struct tpacpi_led_classdev, work); - - if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) - light_set_status((data->new_state != TPACPI_LED_OFF)); -} - -static void light_sysfs_set(struct led_classdev *led_cdev, +static int light_sysfs_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct tpacpi_led_classdev *data = - container_of(led_cdev, - struct tpacpi_led_classdev, - led_classdev); - data->new_state = (brightness != LED_OFF) ? - TPACPI_LED_ON : TPACPI_LED_OFF; - queue_work(tpacpi_wq, &data->work); + return light_set_status((brightness != LED_OFF) ? + TPACPI_LED_ON : TPACPI_LED_OFF); } static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) @@ -5387,7 +5369,7 @@ static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) static struct tpacpi_led_classdev tpacpi_led_thinklight = { .led_classdev = { .name = "tpacpi::thinklight", - .brightness_set = &light_sysfs_set, + .brightness_set_blocking = &light_sysfs_set, .brightness_get = &light_sysfs_get, } }; @@ -5403,7 +5385,6 @@ static int __init light_init(struct ibm_init_struct *iibm) TPACPI_ACPIHANDLE_INIT(lght); } TPACPI_ACPIHANDLE_INIT(cmos); - INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; @@ -5437,7 +5418,6 @@ static int __init light_init(struct ibm_init_struct *iibm) static void light_exit(void) { led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); - flush_workqueue(tpacpi_wq); } static int light_read(struct seq_file *m) @@ -5704,29 +5684,21 @@ static int led_set_status(const unsigned int led, return rc; } -static void led_set_status_worker(struct work_struct *work) -{ - struct tpacpi_led_classdev *data = - container_of(work, struct tpacpi_led_classdev, work); - - if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) - led_set_status(data->led, data->new_state); -} - -static void led_sysfs_set(struct led_classdev *led_cdev, +static int led_sysfs_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct tpacpi_led_classdev *data = container_of(led_cdev, struct tpacpi_led_classdev, led_classdev); + enum led_status_t new_state; if (brightness == LED_OFF) - data->new_state = TPACPI_LED_OFF; + new_state = TPACPI_LED_OFF; else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK) - data->new_state = TPACPI_LED_ON; + new_state = TPACPI_LED_ON; else - data->new_state = TPACPI_LED_BLINK; + new_state = TPACPI_LED_BLINK; - queue_work(tpacpi_wq, &data->work); + return led_set_status(data->led, new_state); } static int led_sysfs_blink_set(struct led_classdev *led_cdev, @@ -5743,10 +5715,7 @@ static int led_sysfs_blink_set(struct led_classdev *led_cdev, } else if ((*delay_on != 500) || (*delay_off != 500)) return -EINVAL; - data->new_state = TPACPI_LED_BLINK; - queue_work(tpacpi_wq, &data->work); - - return 0; + return led_set_status(data->led, TPACPI_LED_BLINK); } static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev) @@ -5775,7 +5744,6 @@ static void led_exit(void) led_classdev_unregister(&tpacpi_leds[i].led_classdev); } - flush_workqueue(tpacpi_wq); kfree(tpacpi_leds); } @@ -5789,7 +5757,7 @@ static int __init tpacpi_init_led(unsigned int led) if (!tpacpi_led_names[led]) return 0; - tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; + tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set; tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; if (led_supported == TPACPI_LED_570) tpacpi_leds[led].led_classdev.brightness_get = @@ -5797,8 +5765,6 @@ static int __init tpacpi_init_led(unsigned int led) tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; - INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker); - rc = led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev); if (rc < 0) @@ -9169,6 +9135,24 @@ static void tpacpi_driver_event(const unsigned int hkey_event) volume_alsa_notify_change(); } } + if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) { + enum led_brightness brightness; + + mutex_lock(&kbdlight_mutex); + + /* + * Check the brightness actually changed, setting the brightness + * through kbdlight_set_level() also triggers this event. + */ + brightness = kbdlight_sysfs_get(NULL); + if (kbdlight_brightness != brightness) { + kbdlight_brightness = brightness; + led_classdev_notify_brightness_hw_changed( + &tpacpi_led_kbdlight.led_classdev, brightness); + } + + mutex_unlock(&kbdlight_mutex); + } } static void hotkey_driver_event(const unsigned int scancode) diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index fa0f19b975a6..974fd684bab2 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -195,7 +195,7 @@ static void sr_stop_vddautocomp(struct omap_sr *sr) } /* - * This function handles the intializations which have to be done + * This function handles the initializations which have to be done * only when both sr device and class driver regiter has * completed. This will be attempted to be called from both sr class * driver register and sr device intializtion API's. Only one call @@ -671,7 +671,7 @@ int sr_register_class(struct omap_sr_class_data *class_data) sr_class = class_data; /* - * Call into late init to do intializations that require + * Call into late init to do initializations that require * both sr driver and sr class driver to be initiallized. */ list_for_each_entry(sr_info, &sr_list, node) @@ -899,7 +899,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) list_add(&sr_info->node, &sr_list); /* - * Call into late init to do intializations that require + * Call into late init to do initializations that require * both sr driver and sr class driver to be initiallized. */ if (sr_class) { diff --git a/drivers/ps3/ps3-sys-manager.c b/drivers/ps3/ps3-sys-manager.c index f2ab435954f6..73e496a72113 100644 --- a/drivers/ps3/ps3-sys-manager.c +++ b/drivers/ps3/ps3-sys-manager.c @@ -22,6 +22,7 @@ #include <linux/module.h> #include <linux/workqueue.h> #include <linux/reboot.h> +#include <linux/sched/signal.h> #include <asm/firmware.h> #include <asm/lv1call.h> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 2d0cfaa6d84c..42e37c20b361 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -76,7 +76,9 @@ config PWM_ATMEL_TCB config PWM_BCM_IPROC tristate "iProc PWM support" - depends on ARCH_BCM_IPROC + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on COMMON_CLK + default ARCH_BCM_IPROC help Generic PWM framework driver for Broadcom iProc PWM block. This block is used in Broadcom iProc SoC's. diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 172ef8245811..a0860b30bd93 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) { struct pwm_device *pwm; + /* check, whether the driver supports a third cell for flags */ if (pc->of_pwm_n_cells < 3) return ERR_PTR(-EINVAL); + /* flags in the third cell are optional */ + if (args->args_count < 2) + return ERR_PTR(-EINVAL); + if (args->args[0] >= pc->npwm) return ERR_PTR(-EINVAL); @@ -148,11 +153,10 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) return pwm; pwm->args.period = args->args[1]; + pwm->args.polarity = PWM_POLARITY_NORMAL; - if (args->args[2] & PWM_POLARITY_INVERTED) + if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) pwm->args.polarity = PWM_POLARITY_INVERSED; - else - pwm->args.polarity = PWM_POLARITY_NORMAL; return pwm; } @@ -163,9 +167,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) { struct pwm_device *pwm; + /* sanity check driver support */ if (pc->of_pwm_n_cells < 2) return ERR_PTR(-EINVAL); + /* all cells are required */ + if (args->args_count != pc->of_pwm_n_cells) + return ERR_PTR(-EINVAL); + if (args->args[0] >= pc->npwm) return ERR_PTR(-EINVAL); @@ -663,24 +672,17 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id) err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index, &args); if (err) { - pr_debug("%s(): can't parse \"pwms\" property\n", __func__); + pr_err("%s(): can't parse \"pwms\" property\n", __func__); return ERR_PTR(err); } pc = of_node_to_pwmchip(args.np); if (IS_ERR(pc)) { - pr_debug("%s(): PWM chip not found\n", __func__); + pr_err("%s(): PWM chip not found\n", __func__); pwm = ERR_CAST(pc); goto put; } - if (args.args_count != pc->of_pwm_n_cells) { - pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name, - args.np->full_name); - pwm = ERR_PTR(-EINVAL); - goto put; - } - pwm = pc->of_xlate(pc, &args); if (IS_ERR(pwm)) goto put; @@ -757,12 +759,13 @@ void pwm_remove_table(struct pwm_lookup *table, size_t num) */ struct pwm_device *pwm_get(struct device *dev, const char *con_id) { - struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); const char *dev_id = dev ? dev_name(dev) : NULL; - struct pwm_chip *chip = NULL; + struct pwm_device *pwm; + struct pwm_chip *chip; unsigned int best = 0; struct pwm_lookup *p, *chosen = NULL; unsigned int match; + int err; /* look up via DT first */ if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) @@ -817,24 +820,35 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) } } - if (!chosen) { - pwm = ERR_PTR(-ENODEV); - goto out; - } + mutex_unlock(&pwm_lookup_lock); + + if (!chosen) + return ERR_PTR(-ENODEV); chip = pwmchip_find_by_name(chosen->provider); + + /* + * If the lookup entry specifies a module, load the module and retry + * the PWM chip lookup. This can be used to work around driver load + * ordering issues if driver's can't be made to properly support the + * deferred probe mechanism. + */ + if (!chip && chosen->module) { + err = request_module(chosen->module); + if (err == 0) + chip = pwmchip_find_by_name(chosen->provider); + } + if (!chip) - goto out; + return ERR_PTR(-EPROBE_DEFER); pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id); if (IS_ERR(pwm)) - goto out; + return pwm; pwm->args.period = chosen->period; pwm->args.polarity = chosen->polarity; -out: - mutex_unlock(&pwm_lookup_lock); return pwm; } EXPORT_SYMBOL_GPL(pwm_get); @@ -960,18 +974,6 @@ void devm_pwm_put(struct device *dev, struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(devm_pwm_put); -/** - * pwm_can_sleep() - report whether PWM access will sleep - * @pwm: PWM device - * - * Returns: True if accessing the PWM can sleep, false otherwise. - */ -bool pwm_can_sleep(struct pwm_device *pwm) -{ - return true; -} -EXPORT_SYMBOL_GPL(pwm_can_sleep); - #ifdef CONFIG_DEBUG_FS static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) { diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index 14fc011faa32..999187277ea5 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -270,7 +270,6 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) chip->chip.npwm = 1; chip->chip.of_xlate = of_pwm_xlate_with_flags; chip->chip.of_pwm_n_cells = 3; - chip->chip.can_sleep = 1; ret = pwmchip_add_with_polarity(&chip->chip, PWM_POLARITY_INVERSED); if (ret) { diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index e6b8b1b7e6ba..67a7023be5c2 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -385,7 +385,6 @@ static int atmel_pwm_probe(struct platform_device *pdev) atmel_pwm->chip.base = -1; atmel_pwm->chip.npwm = 4; - atmel_pwm->chip.can_sleep = true; atmel_pwm->config = data->config; atmel_pwm->updated_pwms = 0; mutex_init(&atmel_pwm->isr_lock); diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index c63418322023..09a95aeb3a70 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -276,7 +276,6 @@ static int kona_pwmc_probe(struct platform_device *pdev) kp->chip.npwm = 6; kp->chip.of_xlate = of_pwm_xlate_with_flags; kp->chip.of_pwm_n_cells = 3; - kp->chip.can_sleep = true; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); kp->base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index 01339c152ab0..771859aca4be 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -206,7 +206,6 @@ static int berlin_pwm_probe(struct platform_device *pdev) pwm->chip.ops = &berlin_pwm_ops; pwm->chip.base = -1; pwm->chip.npwm = 4; - pwm->chip.can_sleep = true; pwm->chip.of_xlate = of_pwm_xlate_with_flags; pwm->chip.of_pwm_n_cells = 3; diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c index 7631ef194de7..d2ed0a2a18e8 100644 --- a/drivers/pwm/pwm-bfin.c +++ b/drivers/pwm/pwm-bfin.c @@ -103,7 +103,7 @@ static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) disable_gptimer(priv->pin); } -static struct pwm_ops bfin_pwm_ops = { +static const struct pwm_ops bfin_pwm_ops = { .request = bfin_pwm_request, .free = bfin_pwm_free, .config = bfin_pwm_config, diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c index 5d5adee16886..8063cffa1c96 100644 --- a/drivers/pwm/pwm-brcmstb.c +++ b/drivers/pwm/pwm-brcmstb.c @@ -270,7 +270,6 @@ static int brcmstb_pwm_probe(struct platform_device *pdev) p->chip.ops = &brcmstb_pwm_ops; p->chip.base = -1; p->chip.npwm = 2; - p->chip.can_sleep = true; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); p->base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index fad968eb75f6..557b4ea16796 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -446,7 +446,6 @@ static int fsl_pwm_probe(struct platform_device *pdev) fpc->chip.of_pwm_n_cells = 3; fpc->chip.base = -1; fpc->chip.npwm = 8; - fpc->chip.can_sleep = true; ret = pwmchip_add(&fpc->chip); if (ret < 0) { diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index d600fd5cd4ba..2ba5c3a398ff 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -38,6 +38,7 @@ #define MX3_PWMCR_DOZEEN (1 << 24) #define MX3_PWMCR_WAITEN (1 << 23) #define MX3_PWMCR_DBGEN (1 << 22) +#define MX3_PWMCR_POUTC (1 << 18) #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) #define MX3_PWMCR_CLKSRC_IPG (1 << 16) #define MX3_PWMCR_SWR (1 << 3) @@ -49,15 +50,10 @@ struct imx_chip { struct clk *clk_per; - struct clk *clk_ipg; void __iomem *mmio_base; struct pwm_chip chip; - - int (*config)(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns); - void (*set_enable)(struct pwm_chip *chip, bool enable); }; #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) @@ -91,176 +87,170 @@ static int imx_pwm_config_v1(struct pwm_chip *chip, return 0; } -static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) +static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm) { struct imx_chip *imx = to_imx_chip(chip); u32 val; + int ret; - val = readl(imx->mmio_base + MX1_PWMC); - - if (enable) - val |= MX1_PWMC_EN; - else - val &= ~MX1_PWMC_EN; + ret = clk_prepare_enable(imx->clk_per); + if (ret < 0) + return ret; + val = readl(imx->mmio_base + MX1_PWMC); + val |= MX1_PWMC_EN; writel(val, imx->mmio_base + MX1_PWMC); -} - -static int imx_pwm_config_v2(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns) -{ - struct imx_chip *imx = to_imx_chip(chip); - struct device *dev = chip->dev; - unsigned long long c; - unsigned long period_cycles, duty_cycles, prescale; - unsigned int period_ms; - bool enable = pwm_is_enabled(pwm); - int wait_count = 0, fifoav; - u32 cr, sr; - - /* - * i.MX PWMv2 has a 4-word sample FIFO. - * In order to avoid FIFO overflow issue, we do software reset - * to clear all sample FIFO if the controller is disabled or - * wait for a full PWM cycle to get a relinquished FIFO slot - * when the controller is enabled and the FIFO is fully loaded. - */ - if (enable) { - sr = readl(imx->mmio_base + MX3_PWMSR); - fifoav = sr & MX3_PWMSR_FIFOAV_MASK; - if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) { - period_ms = DIV_ROUND_UP(pwm_get_period(pwm), - NSEC_PER_MSEC); - msleep(period_ms); - - sr = readl(imx->mmio_base + MX3_PWMSR); - if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK)) - dev_warn(dev, "there is no free FIFO slot\n"); - } - } else { - writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); - do { - usleep_range(200, 1000); - cr = readl(imx->mmio_base + MX3_PWMCR); - } while ((cr & MX3_PWMCR_SWR) && - (wait_count++ < MX3_PWM_SWR_LOOP)); - - if (cr & MX3_PWMCR_SWR) - dev_warn(dev, "software reset timeout\n"); - } - - c = clk_get_rate(imx->clk_per); - c = c * period_ns; - do_div(c, 1000000000); - period_cycles = c; - - prescale = period_cycles / 0x10000 + 1; - - period_cycles /= prescale; - c = (unsigned long long)period_cycles * duty_ns; - do_div(c, period_ns); - duty_cycles = c; - - /* - * according to imx pwm RM, the real period value should be - * PERIOD value in PWMPR plus 2. - */ - if (period_cycles > 2) - period_cycles -= 2; - else - period_cycles = 0; - - writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); - writel(period_cycles, imx->mmio_base + MX3_PWMPR); - - cr = MX3_PWMCR_PRESCALER(prescale) | - MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | - MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; - - if (enable) - cr |= MX3_PWMCR_EN; - - writel(cr, imx->mmio_base + MX3_PWMCR); return 0; } -static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) +static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm) { struct imx_chip *imx = to_imx_chip(chip); u32 val; - val = readl(imx->mmio_base + MX3_PWMCR); - - if (enable) - val |= MX3_PWMCR_EN; - else - val &= ~MX3_PWMCR_EN; + val = readl(imx->mmio_base + MX1_PWMC); + val &= ~MX1_PWMC_EN; + writel(val, imx->mmio_base + MX1_PWMC); - writel(val, imx->mmio_base + MX3_PWMCR); + clk_disable_unprepare(imx->clk_per); } -static int imx_pwm_config(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns) +static void imx_pwm_sw_reset(struct pwm_chip *chip) { struct imx_chip *imx = to_imx_chip(chip); - int ret; - - ret = clk_prepare_enable(imx->clk_ipg); - if (ret) - return ret; + struct device *dev = chip->dev; + int wait_count = 0; + u32 cr; + + writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); + do { + usleep_range(200, 1000); + cr = readl(imx->mmio_base + MX3_PWMCR); + } while ((cr & MX3_PWMCR_SWR) && + (wait_count++ < MX3_PWM_SWR_LOOP)); + + if (cr & MX3_PWMCR_SWR) + dev_warn(dev, "software reset timeout\n"); +} - ret = imx->config(chip, pwm, duty_ns, period_ns); +static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct imx_chip *imx = to_imx_chip(chip); + struct device *dev = chip->dev; + unsigned int period_ms; + int fifoav; + u32 sr; - clk_disable_unprepare(imx->clk_ipg); + sr = readl(imx->mmio_base + MX3_PWMSR); + fifoav = sr & MX3_PWMSR_FIFOAV_MASK; + if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) { + period_ms = DIV_ROUND_UP(pwm_get_period(pwm), + NSEC_PER_MSEC); + msleep(period_ms); - return ret; + sr = readl(imx->mmio_base + MX3_PWMSR); + if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK)) + dev_warn(dev, "there is no free FIFO slot\n"); + } } -static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { + unsigned long period_cycles, duty_cycles, prescale; struct imx_chip *imx = to_imx_chip(chip); + struct pwm_state cstate; + unsigned long long c; int ret; + u32 cr; + + pwm_get_state(pwm, &cstate); + + if (state->enabled) { + c = clk_get_rate(imx->clk_per); + c *= state->period; + + do_div(c, 1000000000); + period_cycles = c; + + prescale = period_cycles / 0x10000 + 1; + + period_cycles /= prescale; + c = (unsigned long long)period_cycles * state->duty_cycle; + do_div(c, state->period); + duty_cycles = c; + + /* + * according to imx pwm RM, the real period value should be + * PERIOD value in PWMPR plus 2. + */ + if (period_cycles > 2) + period_cycles -= 2; + else + period_cycles = 0; + + /* + * Wait for a free FIFO slot if the PWM is already enabled, and + * flush the FIFO if the PWM was disabled and is about to be + * enabled. + */ + if (cstate.enabled) { + imx_pwm_wait_fifo_slot(chip, pwm); + } else { + ret = clk_prepare_enable(imx->clk_per); + if (ret) + return ret; + + imx_pwm_sw_reset(chip); + } - ret = clk_prepare_enable(imx->clk_per); - if (ret) - return ret; + writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); + writel(period_cycles, imx->mmio_base + MX3_PWMPR); - imx->set_enable(chip, true); + cr = MX3_PWMCR_PRESCALER(prescale) | + MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | + MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH | + MX3_PWMCR_EN; - return 0; -} + if (state->polarity == PWM_POLARITY_INVERSED) + cr |= MX3_PWMCR_POUTC; -static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); + writel(cr, imx->mmio_base + MX3_PWMCR); + } else if (cstate.enabled) { + writel(0, imx->mmio_base + MX3_PWMCR); - imx->set_enable(chip, false); + clk_disable_unprepare(imx->clk_per); + } - clk_disable_unprepare(imx->clk_per); + return 0; } -static struct pwm_ops imx_pwm_ops = { - .enable = imx_pwm_enable, - .disable = imx_pwm_disable, - .config = imx_pwm_config, +static const struct pwm_ops imx_pwm_ops_v1 = { + .enable = imx_pwm_enable_v1, + .disable = imx_pwm_disable_v1, + .config = imx_pwm_config_v1, + .owner = THIS_MODULE, +}; + +static const struct pwm_ops imx_pwm_ops_v2 = { + .apply = imx_pwm_apply_v2, .owner = THIS_MODULE, }; struct imx_pwm_data { - int (*config)(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns); - void (*set_enable)(struct pwm_chip *chip, bool enable); + bool polarity_supported; + const struct pwm_ops *ops; }; static struct imx_pwm_data imx_pwm_data_v1 = { - .config = imx_pwm_config_v1, - .set_enable = imx_pwm_set_enable_v1, + .ops = &imx_pwm_ops_v1, }; static struct imx_pwm_data imx_pwm_data_v2 = { - .config = imx_pwm_config_v2, - .set_enable = imx_pwm_set_enable_v2, + .polarity_supported = true, + .ops = &imx_pwm_ops_v2, }; static const struct of_device_id imx_pwm_dt_ids[] = { @@ -282,6 +272,8 @@ static int imx_pwm_probe(struct platform_device *pdev) if (!of_id) return -ENODEV; + data = of_id->data; + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (imx == NULL) return -ENOMEM; @@ -293,28 +285,22 @@ static int imx_pwm_probe(struct platform_device *pdev) return PTR_ERR(imx->clk_per); } - imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(imx->clk_ipg)) { - dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", - PTR_ERR(imx->clk_ipg)); - return PTR_ERR(imx->clk_ipg); - } - - imx->chip.ops = &imx_pwm_ops; + imx->chip.ops = data->ops; imx->chip.dev = &pdev->dev; imx->chip.base = -1; imx->chip.npwm = 1; - imx->chip.can_sleep = true; + + if (data->polarity_supported) { + dev_dbg(&pdev->dev, "PWM supports output inversion\n"); + imx->chip.of_xlate = of_pwm_xlate_with_flags; + imx->chip.of_pwm_n_cells = 3; + } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); - data = of_id->data; - imx->config = data->config; - imx->set_enable = data->set_enable; - ret = pwmchip_add(&imx->chip); if (ret < 0) return ret; diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c index 872ea76a4f19..52584e9962ed 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -278,7 +278,6 @@ static int lp3943_pwm_probe(struct platform_device *pdev) lp3943_pwm->chip.dev = &pdev->dev; lp3943_pwm->chip.ops = &lp3943_pwm_ops; lp3943_pwm->chip.npwm = LP3943_NUM_PWMS; - lp3943_pwm->chip.can_sleep = true; platform_set_drvdata(pdev, lp3943_pwm); diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index 3622f093490e..053088b9b66e 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -17,6 +17,27 @@ #include "pwm-lpss.h" +/* BayTrail */ +static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { + .clk_rate = 25000000, + .npwm = 1, + .base_unit_bits = 16, +}; + +/* Braswell */ +static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { + .clk_rate = 19200000, + .npwm = 1, + .base_unit_bits = 16, +}; + +/* Broxton */ +static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = { + .clk_rate = 19200000, + .npwm = 4, + .base_unit_bits = 22, +}; + static int pwm_lpss_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -80,6 +101,7 @@ static const struct pci_device_id pwm_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bxt_info}, { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info}, { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info}, + { PCI_VDEVICE(INTEL, 0x31c8), (unsigned long)&pwm_lpss_bxt_info}, { PCI_VDEVICE(INTEL, 0x5ac8), (unsigned long)&pwm_lpss_bxt_info}, { }, }; diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c index 54433fc6d1a4..b22b6fdadb9a 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -18,6 +18,27 @@ #include "pwm-lpss.h" +/* BayTrail */ +static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { + .clk_rate = 25000000, + .npwm = 1, + .base_unit_bits = 16, +}; + +/* Braswell */ +static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { + .clk_rate = 19200000, + .npwm = 1, + .base_unit_bits = 16, +}; + +/* Broxton */ +static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = { + .clk_rate = 19200000, + .npwm = 4, + .base_unit_bits = 22, +}; + static int pwm_lpss_probe_platform(struct platform_device *pdev) { const struct pwm_lpss_boardinfo *info; diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 72c0bce5a75c..689d2c1cbead 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -37,30 +38,6 @@ struct pwm_lpss_chip { const struct pwm_lpss_boardinfo *info; }; -/* BayTrail */ -const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { - .clk_rate = 25000000, - .npwm = 1, - .base_unit_bits = 16, -}; -EXPORT_SYMBOL_GPL(pwm_lpss_byt_info); - -/* Braswell */ -const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { - .clk_rate = 19200000, - .npwm = 1, - .base_unit_bits = 16, -}; -EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info); - -/* Broxton */ -const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = { - .clk_rate = 19200000, - .npwm = 4, - .base_unit_bits = 22, -}; -EXPORT_SYMBOL_GPL(pwm_lpss_bxt_info); - static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) { return container_of(chip, struct pwm_lpss_chip, chip); @@ -80,17 +57,42 @@ static inline void pwm_lpss_write(const struct pwm_device *pwm, u32 value) writel(value, lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM); } -static void pwm_lpss_update(struct pwm_device *pwm) +static int pwm_lpss_update(struct pwm_device *pwm) { + struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip); + const void __iomem *addr = lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM; + const unsigned int ms = 500 * USEC_PER_MSEC; + u32 val; + int err; + pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE); - /* Give it some time to propagate */ - usleep_range(10, 50); + + /* + * PWM Configuration register has SW_UPDATE bit that is set when a new + * configuration is written to the register. The bit is automatically + * cleared at the start of the next output cycle by the IP block. + * + * If one writes a new configuration to the register while it still has + * the bit enabled, PWM may freeze. That is, while one can still write + * to the register, it won't have an effect. Thus, we try to sleep long + * enough that the bit gets cleared and make sure the bit is not + * enabled while we update the configuration. + */ + err = readl_poll_timeout(addr, val, !(val & PWM_SW_UPDATE), 40, ms); + if (err) + dev_err(pwm->chip->dev, "PWM_SW_UPDATE was not cleared\n"); + + return err; } -static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +static inline int pwm_lpss_is_updating(struct pwm_device *pwm) +{ + return (pwm_lpss_read(pwm) & PWM_SW_UPDATE) ? -EBUSY : 0; +} + +static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, + int duty_ns, int period_ns) { - struct pwm_lpss_chip *lpwm = to_lpwm(chip); unsigned long long on_time_div; unsigned long c = lpwm->info->clk_rate, base_unit_range; unsigned long long base_unit, freq = NSEC_PER_SEC; @@ -102,62 +104,62 @@ static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm, * The equation is: * base_unit = round(base_unit_range * freq / c) */ - base_unit_range = BIT(lpwm->info->base_unit_bits); + base_unit_range = BIT(lpwm->info->base_unit_bits) - 1; freq *= base_unit_range; base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); - if (duty_ns <= 0) - duty_ns = 1; on_time_div = 255ULL * duty_ns; do_div(on_time_div, period_ns); on_time_div = 255ULL - on_time_div; - pm_runtime_get_sync(chip->dev); - ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; - ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); - base_unit &= (base_unit_range - 1); + ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT); + base_unit &= base_unit_range; ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; pwm_lpss_write(pwm, ctrl); - - /* - * If the PWM is already enabled we need to notify the hardware - * about the change by setting PWM_SW_UPDATE. - */ - if (pwm_is_enabled(pwm)) - pwm_lpss_update(pwm); - - pm_runtime_put(chip->dev); - - return 0; } -static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { - pm_runtime_get_sync(chip->dev); + struct pwm_lpss_chip *lpwm = to_lpwm(chip); + int ret; - /* - * Hardware must first see PWM_SW_UPDATE before the PWM can be - * enabled. - */ - pwm_lpss_update(pwm); - pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE); - return 0; -} + if (state->enabled) { + if (!pwm_is_enabled(pwm)) { + pm_runtime_get_sync(chip->dev); + ret = pwm_lpss_is_updating(pwm); + if (ret) { + pm_runtime_put(chip->dev); + return ret; + } + pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); + ret = pwm_lpss_update(pwm); + if (ret) { + pm_runtime_put(chip->dev); + return ret; + } + pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE); + } else { + ret = pwm_lpss_is_updating(pwm); + if (ret) + return ret; + pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); + return pwm_lpss_update(pwm); + } + } else if (pwm_is_enabled(pwm)) { + pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE); + pm_runtime_put(chip->dev); + } -static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE); - pm_runtime_put(chip->dev); + return 0; } static const struct pwm_ops pwm_lpss_ops = { - .config = pwm_lpss_config, - .enable = pwm_lpss_enable, - .disable = pwm_lpss_disable, + .apply = pwm_lpss_apply, .owner = THIS_MODULE, }; diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h index 04766e0d41aa..c94cd7c2695d 100644 --- a/drivers/pwm/pwm-lpss.h +++ b/drivers/pwm/pwm-lpss.h @@ -24,10 +24,6 @@ struct pwm_lpss_boardinfo { unsigned long base_unit_bits; }; -extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info; -extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info; -extern const struct pwm_lpss_boardinfo pwm_lpss_bxt_info; - struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, const struct pwm_lpss_boardinfo *info); int pwm_lpss_remove(struct pwm_lpss_chip *lpwm); diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 9a596324ebef..a6017ad9926c 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -151,7 +151,7 @@ static int mxs_pwm_probe(struct platform_device *pdev) mxs->chip.dev = &pdev->dev; mxs->chip.ops = &mxs_pwm_ops; mxs->chip.base = -1; - mxs->chip.can_sleep = true; + ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm); if (ret < 0) { dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret); diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 117fccf7934a..0cfb3571a732 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -20,8 +20,10 @@ */ #include <linux/acpi.h> +#include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/pwm.h> @@ -65,7 +67,6 @@ #define PCA9685_MAXCHAN 0x10 #define LED_FULL (1 << 4) -#define MODE1_RESTART (1 << 7) #define MODE1_SLEEP (1 << 4) #define MODE2_INVRT (1 << 4) #define MODE2_OUTDRV (1 << 2) @@ -81,6 +82,10 @@ struct pca9685 { int active_cnt; int duty_ns; int period_ns; +#if IS_ENABLED(CONFIG_GPIOLIB) + struct mutex lock; + struct gpio_chip gpio; +#endif }; static inline struct pca9685 *to_pca(struct pwm_chip *chip) @@ -88,6 +93,151 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) return container_of(chip, struct pca9685, chip); } +#if IS_ENABLED(CONFIG_GPIOLIB) +static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset) +{ + struct pca9685 *pca = gpiochip_get_data(gpio); + struct pwm_device *pwm; + + mutex_lock(&pca->lock); + + pwm = &pca->chip.pwms[offset]; + + if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) { + mutex_unlock(&pca->lock); + return -EBUSY; + } + + pwm_set_chip_data(pwm, (void *)1); + + mutex_unlock(&pca->lock); + return 0; +} + +static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset) +{ + struct pca9685 *pca = gpiochip_get_data(gpio); + struct pwm_device *pwm; + + mutex_lock(&pca->lock); + pwm = &pca->chip.pwms[offset]; + pwm_set_chip_data(pwm, NULL); + mutex_unlock(&pca->lock); +} + +static bool pca9685_pwm_is_gpio(struct pca9685 *pca, struct pwm_device *pwm) +{ + bool is_gpio = false; + + mutex_lock(&pca->lock); + + if (pwm->hwpwm >= PCA9685_MAXCHAN) { + unsigned int i; + + /* + * Check if any of the GPIOs are requested and in that case + * prevent using the "all LEDs" channel. + */ + for (i = 0; i < pca->gpio.ngpio; i++) + if (gpiochip_is_requested(&pca->gpio, i)) { + is_gpio = true; + break; + } + } else if (pwm_get_chip_data(pwm)) { + is_gpio = true; + } + + mutex_unlock(&pca->lock); + return is_gpio; +} + +static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset) +{ + struct pca9685 *pca = gpiochip_get_data(gpio); + struct pwm_device *pwm = &pca->chip.pwms[offset]; + unsigned int value; + + regmap_read(pca->regmap, LED_N_ON_H(pwm->hwpwm), &value); + + return value & LED_FULL; +} + +static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset, + int value) +{ + struct pca9685 *pca = gpiochip_get_data(gpio); + struct pwm_device *pwm = &pca->chip.pwms[offset]; + unsigned int on = value ? LED_FULL : 0; + + /* Clear both OFF registers */ + regmap_write(pca->regmap, LED_N_OFF_L(pwm->hwpwm), 0); + regmap_write(pca->regmap, LED_N_OFF_H(pwm->hwpwm), 0); + + /* Set the full ON bit */ + regmap_write(pca->regmap, LED_N_ON_H(pwm->hwpwm), on); +} + +static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + /* Always out */ + return 0; +} + +static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio, + unsigned int offset) +{ + return -EINVAL; +} + +static int pca9685_pwm_gpio_direction_output(struct gpio_chip *gpio, + unsigned int offset, int value) +{ + pca9685_pwm_gpio_set(gpio, offset, value); + + return 0; +} + +/* + * The PCA9685 has a bit for turning the PWM output full off or on. Some + * boards like Intel Galileo actually uses these as normal GPIOs so we + * expose a GPIO chip here which can exclusively take over the underlying + * PWM channel. + */ +static int pca9685_pwm_gpio_probe(struct pca9685 *pca) +{ + struct device *dev = pca->chip.dev; + + mutex_init(&pca->lock); + + pca->gpio.label = dev_name(dev); + pca->gpio.parent = dev; + pca->gpio.request = pca9685_pwm_gpio_request; + pca->gpio.free = pca9685_pwm_gpio_free; + pca->gpio.get_direction = pca9685_pwm_gpio_get_direction; + pca->gpio.direction_input = pca9685_pwm_gpio_direction_input; + pca->gpio.direction_output = pca9685_pwm_gpio_direction_output; + pca->gpio.get = pca9685_pwm_gpio_get; + pca->gpio.set = pca9685_pwm_gpio_set; + pca->gpio.base = -1; + pca->gpio.ngpio = PCA9685_MAXCHAN; + pca->gpio.can_sleep = true; + + return devm_gpiochip_add_data(dev, &pca->gpio, pca); +} +#else +static inline bool pca9685_pwm_is_gpio(struct pca9685 *pca, + struct pwm_device *pwm) +{ + return false; +} + +static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca) +{ + return 0; +} +#endif + static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { @@ -117,16 +267,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, udelay(500); pca->period_ns = period_ns; - - /* - * If the duty cycle did not change, restart PWM with - * the same duty cycle to period ratio and return. - */ - if (duty_ns == pca->duty_ns) { - regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_RESTART, 0x1); - return 0; - } } else { dev_err(chip->dev, "prescaler not set: period out of bounds!\n"); @@ -264,6 +404,9 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { struct pca9685 *pca = to_pca(chip); + if (pca9685_pwm_is_gpio(pca, pwm)) + return -EBUSY; + if (pca->active_cnt++ == 0) return regmap_update_bits(pca->regmap, PCA9685_MODE1, MODE1_SLEEP, 0x0); @@ -343,9 +486,16 @@ static int pca9685_pwm_probe(struct i2c_client *client, pca->chip.dev = &client->dev; pca->chip.base = -1; - pca->chip.can_sleep = true; - return pwmchip_add(&pca->chip); + ret = pwmchip_add(&pca->chip); + if (ret < 0) + return ret; + + ret = pca9685_pwm_gpio_probe(pca); + if (ret < 0) + pwmchip_remove(&pca->chip); + + return ret; } static int pca9685_pwm_remove(struct i2c_client *client) diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 58b709f29130..4143a46684d2 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -118,7 +118,7 @@ static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable_unprepare(pc->clk); } -static struct pwm_ops pxa_pwm_ops = { +static const struct pwm_ops pxa_pwm_ops = { .config = pxa_pwm_config, .enable = pxa_pwm_enable, .disable = pxa_pwm_disable, diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index dd82dc840af9..2b7c31c9d1ab 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -635,7 +635,6 @@ skip_cpt: pc->chip.ops = &sti_pwm_ops; pc->chip.base = -1; pc->chip.npwm = pc->cdata->pwm_num_devs; - pc->chip.can_sleep = true; ret = pwmchip_add(&pc->chip); if (ret < 0) { diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index b0803f6c64d9..1284ffa05921 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -340,7 +340,6 @@ static int sun4i_pwm_probe(struct platform_device *pdev) pwm->chip.ops = &sun4i_pwm_ops; pwm->chip.base = -1; pwm->chip.npwm = pwm->data->npwm; - pwm->chip.can_sleep = true; pwm->chip.of_xlate = of_pwm_xlate_with_flags; pwm->chip.of_pwm_n_cells = 3; diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index b964470025c5..21eff991d0e3 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -303,7 +303,6 @@ static int twl_pwmled_probe(struct platform_device *pdev) twl->chip.dev = &pdev->dev; twl->chip.base = -1; - twl->chip.can_sleep = true; mutex_init(&twl->mutex); diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index 7a993b056638..9de617b76680 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -323,7 +323,6 @@ static int twl_pwm_probe(struct platform_device *pdev) twl->chip.dev = &pdev->dev; twl->chip.base = -1; twl->chip.npwm = 2; - twl->chip.can_sleep = true; mutex_init(&twl->mutex); diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index cdb58fd4619d..8141a4984126 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -184,7 +184,7 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip, return 0; } -static struct pwm_ops vt8500_pwm_ops = { +static const struct pwm_ops vt8500_pwm_ops = { .enable = vt8500_pwm_enable, .disable = vt8500_pwm_disable, .config = vt8500_pwm_config, diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c index 9013a585507e..50b617af81bd 100644 --- a/drivers/rapidio/devices/rio_mport_cdev.c +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -889,17 +889,16 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode, goto err_req; } - down_read(¤t->mm->mmap_sem); - pinned = get_user_pages( + pinned = get_user_pages_unlocked( (unsigned long)xfer->loc_addr & PAGE_MASK, nr_pages, - dir == DMA_FROM_DEVICE ? FOLL_WRITE : 0, - page_list, NULL); - up_read(¤t->mm->mmap_sem); + page_list, + dir == DMA_FROM_DEVICE ? FOLL_WRITE : 0); if (pinned != nr_pages) { if (pinned < 0) { - rmcd_error("get_user_pages err=%ld", pinned); + rmcd_error("get_user_pages_unlocked err=%ld", + pinned); nr_pages = 0; } else rmcd_error("pinned %ld out of %ld pages", diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 8f9cf0bc571c..65f86bc24c07 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -7,6 +7,9 @@ config REMOTEPROC select FW_LOADER select VIRTIO select VIRTUALIZATION + help + Support for remote processors (such as DSP coprocessors). These + are mainly used on embedded systems. if REMOTEPROC @@ -25,11 +28,11 @@ config OMAP_REMOTEPROC Currently only supported on OMAP4. - Usually you want to say y here, in order to enable multimedia + Usually you want to say Y here, in order to enable multimedia use-cases to run on your platform (multimedia codecs are offloaded to remote DSP processors using this framework). - It's safe to say n here if you're not interested in multimedia + It's safe to say N here if you're not interested in multimedia offloading or just want a bare minimum kernel. config WKUP_M3_RPROC @@ -73,14 +76,16 @@ config QCOM_ADSP_PIL depends on OF && ARCH_QCOM depends on REMOTEPROC depends on QCOM_SMEM + depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n) select MFD_SYSCON select QCOM_MDT_LOADER + select QCOM_RPROC_COMMON select QCOM_SCM help Say y here to support the TrustZone based Peripherial Image Loader for the Qualcomm ADSP remote processors. -config QCOM_MDT_LOADER +config QCOM_RPROC_COMMON tristate config QCOM_Q6V5_PIL @@ -88,8 +93,9 @@ config QCOM_Q6V5_PIL depends on OF && ARCH_QCOM depends on QCOM_SMEM depends on REMOTEPROC + depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n) select MFD_SYSCON - select QCOM_MDT_LOADER + select QCOM_RPROC_COMMON select QCOM_SCM help Say y here to support the Qualcomm Peripherial Image Loader for the @@ -102,6 +108,7 @@ config QCOM_WCNSS_PIL depends on QCOM_SMEM depends on REMOTEPROC select QCOM_MDT_LOADER + select QCOM_RPROC_COMMON select QCOM_SCM help Say y here to support the Peripheral Image Loader for the Qualcomm @@ -111,6 +118,9 @@ config ST_REMOTEPROC tristate "ST remoteproc support" depends on ARCH_STI depends on REMOTEPROC + select MAILBOX + select STI_MBOX + select RPMSG_VIRTIO help Say y here to support ST's adjunct processors via the remote processor framework. diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 0938ea3c41ba..ffc5e430df27 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o -obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o +obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o qcom_wcnss_pil-y += qcom_wcnss.o diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 1afac8f31be0..3814de28599c 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -151,7 +151,7 @@ static void da8xx_rproc_kick(struct rproc *rproc, int vqid) writel(SYSCFG_CHIPSIG2, drproc->chipsig); } -static struct rproc_ops da8xx_rproc_ops = { +static const struct rproc_ops da8xx_rproc_ops = { .start = da8xx_rproc_start, .stop = da8xx_rproc_stop, .kick = da8xx_rproc_kick, diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index fa63bf2eb885..a96ce9083f7f 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -177,7 +177,7 @@ static int omap_rproc_stop(struct rproc *rproc) return 0; } -static struct rproc_ops omap_rproc_ops = { +static const struct rproc_ops omap_rproc_ops = { .start = omap_rproc_start, .stop = omap_rproc_stop, .kick = omap_rproc_kick, diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 43a4ed2f346c..49fe2f807e1d 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -1,5 +1,5 @@ /* - * Qualcomm ADSP Peripheral Image Loader for MSM8974 and MSM8996 + * Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996 * * Copyright (C) 2016 Linaro Ltd * Copyright (C) 2014 Sony Mobile Communications AB @@ -26,15 +26,19 @@ #include <linux/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> +#include <linux/soc/qcom/mdt_loader.h> #include <linux/soc/qcom/smem.h> #include <linux/soc/qcom/smem_state.h> -#include "qcom_mdt_loader.h" +#include "qcom_common.h" #include "remoteproc_internal.h" -#define ADSP_CRASH_REASON_SMEM 423 -#define ADSP_FIRMWARE_NAME "adsp.mdt" -#define ADSP_PAS_ID 1 +struct adsp_data { + int crash_reason_smem; + const char *firmware_name; + int pas_id; + bool has_aggre2_clk; +}; struct qcom_adsp { struct device *dev; @@ -50,8 +54,14 @@ struct qcom_adsp { unsigned stop_bit; struct clk *xo; + struct clk *aggre2_clk; struct regulator *cx_supply; + struct regulator *px_supply; + + int pas_id; + int crash_reason_smem; + bool has_aggre2_clk; struct completion start_done; struct completion stop_done; @@ -60,39 +70,16 @@ struct qcom_adsp { phys_addr_t mem_reloc; void *mem_region; size_t mem_size; + + struct qcom_rproc_subdev smd_subdev; }; static int adsp_load(struct rproc *rproc, const struct firmware *fw) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; - phys_addr_t fw_addr; - size_t fw_size; - bool relocate; - int ret; - - ret = qcom_scm_pas_init_image(ADSP_PAS_ID, fw->data, fw->size); - if (ret) { - dev_err(&rproc->dev, "invalid firmware metadata\n"); - return ret; - } - - ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); - if (ret) { - dev_err(&rproc->dev, "failed to parse mdt header\n"); - return ret; - } - if (relocate) { - adsp->mem_reloc = fw_addr; - - ret = qcom_scm_pas_mem_setup(ADSP_PAS_ID, adsp->mem_phys, fw_size); - if (ret) { - dev_err(&rproc->dev, "unable to setup memory for image\n"); - return ret; - } - } - - return qcom_mdt_load(rproc, fw, rproc->firmware); + return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id, + adsp->mem_region, adsp->mem_phys, adsp->mem_size); } static const struct rproc_fw_ops adsp_fw_ops = { @@ -109,31 +96,43 @@ static int adsp_start(struct rproc *rproc) if (ret) return ret; + ret = clk_prepare_enable(adsp->aggre2_clk); + if (ret) + goto disable_xo_clk; + ret = regulator_enable(adsp->cx_supply); if (ret) - goto disable_clocks; + goto disable_aggre2_clk; + + ret = regulator_enable(adsp->px_supply); + if (ret) + goto disable_cx_supply; - ret = qcom_scm_pas_auth_and_reset(ADSP_PAS_ID); + ret = qcom_scm_pas_auth_and_reset(adsp->pas_id); if (ret) { dev_err(adsp->dev, "failed to authenticate image and release reset\n"); - goto disable_regulators; + goto disable_px_supply; } ret = wait_for_completion_timeout(&adsp->start_done, msecs_to_jiffies(5000)); if (!ret) { dev_err(adsp->dev, "start timed out\n"); - qcom_scm_pas_shutdown(ADSP_PAS_ID); + qcom_scm_pas_shutdown(adsp->pas_id); ret = -ETIMEDOUT; - goto disable_regulators; + goto disable_px_supply; } ret = 0; -disable_regulators: +disable_px_supply: + regulator_disable(adsp->px_supply); +disable_cx_supply: regulator_disable(adsp->cx_supply); -disable_clocks: +disable_aggre2_clk: + clk_disable_unprepare(adsp->aggre2_clk); +disable_xo_clk: clk_disable_unprepare(adsp->xo); return ret; @@ -157,7 +156,7 @@ static int adsp_stop(struct rproc *rproc) BIT(adsp->stop_bit), 0); - ret = qcom_scm_pas_shutdown(ADSP_PAS_ID); + ret = qcom_scm_pas_shutdown(adsp->pas_id); if (ret) dev_err(adsp->dev, "failed to shutdown: %d\n", ret); @@ -197,7 +196,7 @@ static irqreturn_t adsp_fatal_interrupt(int irq, void *dev) size_t len; char *msg; - msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, ADSP_CRASH_REASON_SMEM, &len); + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, adsp->crash_reason_smem, &len); if (!IS_ERR(msg) && len > 0 && msg[0]) dev_err(adsp->dev, "fatal error received: %s\n", msg); @@ -244,6 +243,17 @@ static int adsp_init_clock(struct qcom_adsp *adsp) return ret; } + if (adsp->has_aggre2_clk) { + adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2"); + if (IS_ERR(adsp->aggre2_clk)) { + ret = PTR_ERR(adsp->aggre2_clk); + if (ret != -EPROBE_DEFER) + dev_err(adsp->dev, + "failed to get aggre2 clock"); + return ret; + } + } + return 0; } @@ -255,6 +265,10 @@ static int adsp_init_regulator(struct qcom_adsp *adsp) regulator_set_load(adsp->cx_supply, 100000); + adsp->px_supply = devm_regulator_get(adsp->dev, "px"); + if (IS_ERR(adsp->px_supply)) + return PTR_ERR(adsp->px_supply); + return 0; } @@ -311,20 +325,20 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp) static int adsp_probe(struct platform_device *pdev) { + const struct adsp_data *desc; struct qcom_adsp *adsp; struct rproc *rproc; int ret; + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + if (!qcom_scm_is_available()) return -EPROBE_DEFER; - if (!qcom_scm_pas_supported(ADSP_PAS_ID)) { - dev_err(&pdev->dev, "PAS is not available for ADSP\n"); - return -ENXIO; - } - rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, - ADSP_FIRMWARE_NAME, sizeof(*adsp)); + desc->firmware_name, sizeof(*adsp)); if (!rproc) { dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; @@ -335,6 +349,9 @@ static int adsp_probe(struct platform_device *pdev) adsp = (struct qcom_adsp *)rproc->priv; adsp->dev = &pdev->dev; adsp->rproc = rproc; + adsp->pas_id = desc->pas_id; + adsp->crash_reason_smem = desc->crash_reason_smem; + adsp->has_aggre2_clk = desc->has_aggre2_clk; platform_set_drvdata(pdev, adsp); init_completion(&adsp->start_done); @@ -384,6 +401,8 @@ static int adsp_probe(struct platform_device *pdev) goto free_rproc; } + qcom_add_smd_subdev(rproc, &adsp->smd_subdev); + ret = rproc_add(rproc); if (ret) goto free_rproc; @@ -402,14 +421,31 @@ static int adsp_remove(struct platform_device *pdev) qcom_smem_state_put(adsp->state); rproc_del(adsp->rproc); + + qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); rproc_free(adsp->rproc); return 0; } +static const struct adsp_data adsp_resource_init = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mdt", + .pas_id = 1, + .has_aggre2_clk = false, +}; + +static const struct adsp_data slpi_resource_init = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = true, +}; + static const struct of_device_id adsp_of_match[] = { - { .compatible = "qcom,msm8974-adsp-pil" }, - { .compatible = "qcom,msm8996-adsp-pil" }, + { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init}, + { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init}, + { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init}, { }, }; MODULE_DEVICE_TABLE(of, adsp_of_match); diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c new file mode 100644 index 000000000000..bb90481215c6 --- /dev/null +++ b/drivers/remoteproc/qcom_common.c @@ -0,0 +1,96 @@ +/* + * Qualcomm Peripheral Image Loader helpers + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2015 Sony Mobile Communications Inc + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/firmware.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/rpmsg/qcom_smd.h> + +#include "remoteproc_internal.h" +#include "qcom_common.h" + +#define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) + +/** + * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc + * @rproc: remoteproc handle + * @fw: firmware header + * @tablesz: outgoing size of the table + * + * Returns a dummy table. + */ +struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, + const struct firmware *fw, + int *tablesz) +{ + static struct resource_table table = { .ver = 1, }; + + *tablesz = sizeof(table); + return &table; +} +EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table); + +static int smd_subdev_probe(struct rproc_subdev *subdev) +{ + struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); + + smd->edge = qcom_smd_register_edge(smd->dev, smd->node); + + return IS_ERR(smd->edge) ? PTR_ERR(smd->edge) : 0; +} + +static void smd_subdev_remove(struct rproc_subdev *subdev) +{ + struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); + + qcom_smd_unregister_edge(smd->edge); + smd->edge = NULL; +} + +/** + * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc + * @rproc: rproc handle to parent the subdevice + * @smd: reference to a Qualcomm subdev context + */ +void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) +{ + struct device *dev = &rproc->dev; + + smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge"); + if (!smd->node) + return; + + smd->dev = dev; + rproc_add_subdev(rproc, &smd->subdev, smd_subdev_probe, smd_subdev_remove); +} +EXPORT_SYMBOL_GPL(qcom_add_smd_subdev); + +/** + * qcom_remove_smd_subdev() - remove the smd subdevice from rproc + * @rproc: rproc handle + * @smd: the SMD subdevice to remove + */ +void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) +{ + rproc_remove_subdev(rproc, &smd->subdev); + of_node_put(smd->node); +} +EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev); + +MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h new file mode 100644 index 000000000000..db5c826d5cd4 --- /dev/null +++ b/drivers/remoteproc/qcom_common.h @@ -0,0 +1,22 @@ +#ifndef __RPROC_QCOM_COMMON_H__ +#define __RPROC_QCOM_COMMON_H__ + +#include <linux/remoteproc.h> +#include "remoteproc_internal.h" + +struct qcom_rproc_subdev { + struct rproc_subdev subdev; + + struct device *dev; + struct device_node *node; + struct qcom_smd_edge *edge; +}; + +struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, + const struct firmware *fw, + int *tablesz); + +void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); +void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); + +#endif diff --git a/drivers/remoteproc/qcom_mdt_loader.c b/drivers/remoteproc/qcom_mdt_loader.c deleted file mode 100644 index 2ff18cd6c096..000000000000 --- a/drivers/remoteproc/qcom_mdt_loader.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Qualcomm Peripheral Image Loader - * - * Copyright (C) 2016 Linaro Ltd - * Copyright (C) 2015 Sony Mobile Communications Inc - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/elf.h> -#include <linux/firmware.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/remoteproc.h> -#include <linux/sizes.h> -#include <linux/slab.h> - -#include "remoteproc_internal.h" -#include "qcom_mdt_loader.h" - -/** - * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc - * @rproc: remoteproc handle - * @fw: firmware header - * @tablesz: outgoing size of the table - * - * Returns a dummy table. - */ -struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, - const struct firmware *fw, - int *tablesz) -{ - static struct resource_table table = { .ver = 1, }; - - *tablesz = sizeof(table); - return &table; -} -EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table); - -/** - * qcom_mdt_parse() - extract useful parameters from the mdt header - * @fw: firmware handle - * @fw_addr: optional reference for base of the firmware's memory region - * @fw_size: optional reference for size of the firmware's memory region - * @fw_relocate: optional reference for flagging if the firmware is relocatable - * - * Returns 0 on success, negative errno otherwise. - */ -int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, - size_t *fw_size, bool *fw_relocate) -{ - const struct elf32_phdr *phdrs; - const struct elf32_phdr *phdr; - const struct elf32_hdr *ehdr; - phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; - phys_addr_t max_addr = 0; - bool relocate = false; - int i; - - ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); - - for (i = 0; i < ehdr->e_phnum; i++) { - phdr = &phdrs[i]; - - if (phdr->p_type != PT_LOAD) - continue; - - if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) - continue; - - if (!phdr->p_memsz) - continue; - - if (phdr->p_flags & QCOM_MDT_RELOCATABLE) - relocate = true; - - if (phdr->p_paddr < min_addr) - min_addr = phdr->p_paddr; - - if (phdr->p_paddr + phdr->p_memsz > max_addr) - max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); - } - - if (fw_addr) - *fw_addr = min_addr; - if (fw_size) - *fw_size = max_addr - min_addr; - if (fw_relocate) - *fw_relocate = relocate; - - return 0; -} -EXPORT_SYMBOL_GPL(qcom_mdt_parse); - -/** - * qcom_mdt_load() - load the firmware which header is defined in fw - * @rproc: rproc handle - * @fw: frimware object for the header - * @firmware: filename of the firmware, for building .bXX names - * - * Returns 0 on success, negative errno otherwise. - */ -int qcom_mdt_load(struct rproc *rproc, - const struct firmware *fw, - const char *firmware) -{ - const struct elf32_phdr *phdrs; - const struct elf32_phdr *phdr; - const struct elf32_hdr *ehdr; - size_t fw_name_len; - char *fw_name; - void *ptr; - int ret; - int i; - - ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); - - fw_name_len = strlen(firmware); - if (fw_name_len <= 4) - return -EINVAL; - - fw_name = kstrdup(firmware, GFP_KERNEL); - if (!fw_name) - return -ENOMEM; - - for (i = 0; i < ehdr->e_phnum; i++) { - phdr = &phdrs[i]; - - if (phdr->p_type != PT_LOAD) - continue; - - if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) - continue; - - if (!phdr->p_memsz) - continue; - - ptr = rproc_da_to_va(rproc, phdr->p_paddr, phdr->p_memsz); - if (!ptr) { - dev_err(&rproc->dev, "segment outside memory range\n"); - ret = -EINVAL; - break; - } - - if (phdr->p_filesz) { - sprintf(fw_name + fw_name_len - 3, "b%02d", i); - ret = request_firmware(&fw, fw_name, &rproc->dev); - if (ret) { - dev_err(&rproc->dev, "failed to load %s\n", - fw_name); - break; - } - - memcpy(ptr, fw->data, fw->size); - - release_firmware(fw); - } - - if (phdr->p_memsz > phdr->p_filesz) - memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); - } - - kfree(fw_name); - - return ret; -} -EXPORT_SYMBOL_GPL(qcom_mdt_load); - -MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_mdt_loader.h b/drivers/remoteproc/qcom_mdt_loader.h deleted file mode 100644 index c5d7122755b6..000000000000 --- a/drivers/remoteproc/qcom_mdt_loader.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __QCOM_MDT_LOADER_H__ -#define __QCOM_MDT_LOADER_H__ - -#define QCOM_MDT_TYPE_MASK (7 << 24) -#define QCOM_MDT_TYPE_HASH (2 << 24) -#define QCOM_MDT_RELOCATABLE BIT(27) - -struct resource_table * qcom_mdt_find_rsc_table(struct rproc *rproc, const struct firmware *fw, int *tablesz); -int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name); - -int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate); - -#endif diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index b08989b48df7..8fd697a3cf8f 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -23,22 +23,21 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> #include <linux/reset.h> +#include <linux/soc/qcom/mdt_loader.h> #include <linux/soc/qcom/smem.h> #include <linux/soc/qcom/smem_state.h> #include "remoteproc_internal.h" -#include "qcom_mdt_loader.h" +#include "qcom_common.h" #include <linux/qcom_scm.h> -#define MBA_FIRMWARE_NAME "mba.b00" -#define MPSS_FIRMWARE_NAME "modem.mdt" - #define MPSS_CRASH_REASON_SMEM 421 /* RMB Status Register Values */ @@ -93,6 +92,26 @@ #define QDSS_BHS_ON BIT(21) #define QDSS_LDO_BYP BIT(22) +struct reg_info { + struct regulator *reg; + int uV; + int uA; +}; + +struct qcom_mss_reg_res { + const char *supply; + int uV; + int uA; +}; + +struct rproc_hexagon_res { + const char *hexagon_mba_image; + struct qcom_mss_reg_res *proxy_supply; + struct qcom_mss_reg_res *active_supply; + char **proxy_clk_names; + char **active_clk_names; +}; + struct q6v5 { struct device *dev; struct rproc *rproc; @@ -110,11 +129,15 @@ struct q6v5 { struct qcom_smem_state *state; unsigned stop_bit; - struct regulator_bulk_data supply[4]; + struct clk *active_clks[8]; + struct clk *proxy_clks[4]; + int active_clk_count; + int proxy_clk_count; - struct clk *ahb_clk; - struct clk *axi_clk; - struct clk *rom_clk; + struct reg_info active_regs[1]; + struct reg_info proxy_regs[3]; + int active_reg_count; + int proxy_reg_count; struct completion start_done; struct completion stop_done; @@ -128,65 +151,141 @@ struct q6v5 { phys_addr_t mpss_reloc; void *mpss_region; size_t mpss_size; -}; -enum { - Q6V5_SUPPLY_CX, - Q6V5_SUPPLY_MX, - Q6V5_SUPPLY_MSS, - Q6V5_SUPPLY_PLL, + struct qcom_rproc_subdev smd_subdev; }; -static int q6v5_regulator_init(struct q6v5 *qproc) +static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, + const struct qcom_mss_reg_res *reg_res) { - int ret; + int rc; + int i; - qproc->supply[Q6V5_SUPPLY_CX].supply = "cx"; - qproc->supply[Q6V5_SUPPLY_MX].supply = "mx"; - qproc->supply[Q6V5_SUPPLY_MSS].supply = "mss"; - qproc->supply[Q6V5_SUPPLY_PLL].supply = "pll"; + if (!reg_res) + return 0; + + for (i = 0; reg_res[i].supply; i++) { + regs[i].reg = devm_regulator_get(dev, reg_res[i].supply); + if (IS_ERR(regs[i].reg)) { + rc = PTR_ERR(regs[i].reg); + if (rc != -EPROBE_DEFER) + dev_err(dev, "Failed to get %s\n regulator", + reg_res[i].supply); + return rc; + } - ret = devm_regulator_bulk_get(qproc->dev, - ARRAY_SIZE(qproc->supply), qproc->supply); - if (ret < 0) { - dev_err(qproc->dev, "failed to get supplies\n"); - return ret; + regs[i].uV = reg_res[i].uV; + regs[i].uA = reg_res[i].uA; } - regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 100000); - regulator_set_load(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 100000); - regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 10000); + return i; +} + +static int q6v5_regulator_enable(struct q6v5 *qproc, + struct reg_info *regs, int count) +{ + int ret; + int i; + + for (i = 0; i < count; i++) { + if (regs[i].uV > 0) { + ret = regulator_set_voltage(regs[i].reg, + regs[i].uV, INT_MAX); + if (ret) { + dev_err(qproc->dev, + "Failed to request voltage for %d.\n", + i); + goto err; + } + } + + if (regs[i].uA > 0) { + ret = regulator_set_load(regs[i].reg, + regs[i].uA); + if (ret < 0) { + dev_err(qproc->dev, + "Failed to set regulator mode\n"); + goto err; + } + } + + ret = regulator_enable(regs[i].reg); + if (ret) { + dev_err(qproc->dev, "Regulator enable failed\n"); + goto err; + } + } return 0; +err: + for (; i >= 0; i--) { + if (regs[i].uV > 0) + regulator_set_voltage(regs[i].reg, 0, INT_MAX); + + if (regs[i].uA > 0) + regulator_set_load(regs[i].reg, 0); + + regulator_disable(regs[i].reg); + } + + return ret; } -static int q6v5_regulator_enable(struct q6v5 *qproc) +static void q6v5_regulator_disable(struct q6v5 *qproc, + struct reg_info *regs, int count) { - struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer; - struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer; - int ret; + int i; - /* TODO: Q6V5_SUPPLY_CX is supposed to be set to super-turbo here */ + for (i = 0; i < count; i++) { + if (regs[i].uV > 0) + regulator_set_voltage(regs[i].reg, 0, INT_MAX); - ret = regulator_set_voltage(mx, 1050000, INT_MAX); - if (ret) - return ret; + if (regs[i].uA > 0) + regulator_set_load(regs[i].reg, 0); + + regulator_disable(regs[i].reg); + } +} - regulator_set_voltage(mss, 1000000, 1150000); +static int q6v5_clk_enable(struct device *dev, + struct clk **clks, int count) +{ + int rc; + int i; - return regulator_bulk_enable(ARRAY_SIZE(qproc->supply), qproc->supply); + for (i = 0; i < count; i++) { + rc = clk_prepare_enable(clks[i]); + if (rc) { + dev_err(dev, "Clock enable failed\n"); + goto err; + } + } + + return 0; +err: + for (i--; i >= 0; i--) + clk_disable_unprepare(clks[i]); + + return rc; } -static void q6v5_regulator_disable(struct q6v5 *qproc) +static void q6v5_clk_disable(struct device *dev, + struct clk **clks, int count) { - struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer; - struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer; + int i; + + for (i = 0; i < count; i++) + clk_disable_unprepare(clks[i]); +} - /* TODO: Q6V5_SUPPLY_CX corner votes should be released */ +static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc, + const struct firmware *fw, + int *tablesz) +{ + static struct resource_table table = { .ver = 1, }; - regulator_bulk_disable(ARRAY_SIZE(qproc->supply), qproc->supply); - regulator_set_voltage(mx, 0, INT_MAX); - regulator_set_voltage(mss, 0, 1150000); + *tablesz = sizeof(table); + return &table; } static int q6v5_load(struct rproc *rproc, const struct firmware *fw) @@ -199,7 +298,7 @@ static int q6v5_load(struct rproc *rproc, const struct firmware *fw) } static const struct rproc_fw_ops q6v5_fw_ops = { - .find_rsc_table = qcom_mdt_find_rsc_table, + .find_rsc_table = q6v5_find_rsc_table, .load = q6v5_load, }; @@ -376,45 +475,109 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) return ret < 0 ? ret : 0; } -static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw) +static bool q6v5_phdr_valid(const struct elf32_phdr *phdr) +{ + if (phdr->p_type != PT_LOAD) + return false; + + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) + return false; + + if (!phdr->p_memsz) + return false; + + return true; +} + +static int q6v5_mpss_load(struct q6v5 *qproc) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; + const struct firmware *seg_fw; + const struct firmware *fw; struct elf32_hdr *ehdr; + phys_addr_t mpss_reloc; phys_addr_t boot_addr; - phys_addr_t fw_addr; - bool relocate; + phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; + phys_addr_t max_addr = 0; + bool relocate = false; + char seg_name[10]; + ssize_t offset; size_t size; + void *ptr; int ret; int i; - ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate); - if (ret) { - dev_err(qproc->dev, "failed to parse mdt header\n"); + ret = request_firmware(&fw, "modem.mdt", qproc->dev); + if (ret < 0) { + dev_err(qproc->dev, "unable to load modem.mdt\n"); return ret; } - if (relocate) - boot_addr = qproc->mpss_phys; - else - boot_addr = fw_addr; + /* Initialize the RMB validator */ + writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); + + ret = q6v5_mpss_init_image(qproc, fw); + if (ret) + goto release_firmware; ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); - for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + + for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (phdr->p_type != PT_LOAD) + if (!q6v5_phdr_valid(phdr)) continue; - if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) - continue; + if (phdr->p_flags & QCOM_MDT_RELOCATABLE) + relocate = true; + + if (phdr->p_paddr < min_addr) + min_addr = phdr->p_paddr; + + if (phdr->p_paddr + phdr->p_memsz > max_addr) + max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); + } + + mpss_reloc = relocate ? min_addr : qproc->mpss_phys; - if (!phdr->p_memsz) + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (!q6v5_phdr_valid(phdr)) continue; + offset = phdr->p_paddr - mpss_reloc; + if (offset < 0 || offset + phdr->p_memsz > qproc->mpss_size) { + dev_err(qproc->dev, "segment outside memory range\n"); + ret = -EINVAL; + goto release_firmware; + } + + ptr = qproc->mpss_region + offset; + + if (phdr->p_filesz) { + snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i); + ret = request_firmware(&seg_fw, seg_name, qproc->dev); + if (ret) { + dev_err(qproc->dev, "failed to load %s\n", seg_name); + goto release_firmware; + } + + memcpy(ptr, seg_fw->data, seg_fw->size); + + release_firmware(seg_fw); + } + + if (phdr->p_memsz > phdr->p_filesz) { + memset(ptr + phdr->p_filesz, 0, + phdr->p_memsz - phdr->p_filesz); + } + size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); if (!size) { + boot_addr = relocate ? qproc->mpss_phys : min_addr; writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); } @@ -429,44 +592,6 @@ static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw) else if (ret < 0) dev_err(qproc->dev, "MPSS authentication failed: %d\n", ret); - return ret < 0 ? ret : 0; -} - -static int q6v5_mpss_load(struct q6v5 *qproc) -{ - const struct firmware *fw; - phys_addr_t fw_addr; - bool relocate; - int ret; - - ret = request_firmware(&fw, MPSS_FIRMWARE_NAME, qproc->dev); - if (ret < 0) { - dev_err(qproc->dev, "unable to load " MPSS_FIRMWARE_NAME "\n"); - return ret; - } - - ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate); - if (ret) { - dev_err(qproc->dev, "failed to parse mdt header\n"); - goto release_firmware; - } - - if (relocate) - qproc->mpss_reloc = fw_addr; - - /* Initialize the RMB validator */ - writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); - - ret = q6v5_mpss_init_image(qproc, fw); - if (ret) - goto release_firmware; - - ret = qcom_mdt_load(qproc->rproc, fw, MPSS_FIRMWARE_NAME); - if (ret) - goto release_firmware; - - ret = q6v5_mpss_validate(qproc, fw); - release_firmware: release_firmware(fw); @@ -478,29 +603,38 @@ static int q6v5_start(struct rproc *rproc) struct q6v5 *qproc = (struct q6v5 *)rproc->priv; int ret; - ret = q6v5_regulator_enable(qproc); + ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, + qproc->proxy_reg_count); if (ret) { - dev_err(qproc->dev, "failed to enable supplies\n"); + dev_err(qproc->dev, "failed to enable proxy supplies\n"); return ret; } + ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, + qproc->proxy_clk_count); + if (ret) { + dev_err(qproc->dev, "failed to enable proxy clocks\n"); + goto disable_proxy_reg; + } + + ret = q6v5_regulator_enable(qproc, qproc->active_regs, + qproc->active_reg_count); + if (ret) { + dev_err(qproc->dev, "failed to enable supplies\n"); + goto disable_proxy_clk; + } ret = reset_control_deassert(qproc->mss_restart); if (ret) { dev_err(qproc->dev, "failed to deassert mss restart\n"); goto disable_vdd; } - ret = clk_prepare_enable(qproc->ahb_clk); - if (ret) + ret = q6v5_clk_enable(qproc->dev, qproc->active_clks, + qproc->active_clk_count); + if (ret) { + dev_err(qproc->dev, "failed to enable clocks\n"); goto assert_reset; - - ret = clk_prepare_enable(qproc->axi_clk); - if (ret) - goto disable_ahb_clk; - - ret = clk_prepare_enable(qproc->rom_clk); - if (ret) - goto disable_axi_clk; + } writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); @@ -535,7 +669,10 @@ static int q6v5_start(struct rproc *rproc) qproc->running = true; - /* TODO: All done, release the handover resources */ + q6v5_clk_disable(qproc->dev, qproc->proxy_clks, + qproc->proxy_clk_count); + q6v5_regulator_disable(qproc, qproc->proxy_regs, + qproc->proxy_reg_count); return 0; @@ -543,16 +680,19 @@ halt_axi_ports: q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); - - clk_disable_unprepare(qproc->rom_clk); -disable_axi_clk: - clk_disable_unprepare(qproc->axi_clk); -disable_ahb_clk: - clk_disable_unprepare(qproc->ahb_clk); + q6v5_clk_disable(qproc->dev, qproc->active_clks, + qproc->active_clk_count); assert_reset: reset_control_assert(qproc->mss_restart); disable_vdd: - q6v5_regulator_disable(qproc); + q6v5_regulator_disable(qproc, qproc->active_regs, + qproc->active_reg_count); +disable_proxy_clk: + q6v5_clk_disable(qproc->dev, qproc->proxy_clks, + qproc->proxy_clk_count); +disable_proxy_reg: + q6v5_regulator_disable(qproc, qproc->proxy_regs, + qproc->proxy_reg_count); return ret; } @@ -579,10 +719,10 @@ static int q6v5_stop(struct rproc *rproc) q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); reset_control_assert(qproc->mss_restart); - clk_disable_unprepare(qproc->rom_clk); - clk_disable_unprepare(qproc->axi_clk); - clk_disable_unprepare(qproc->ahb_clk); - q6v5_regulator_disable(qproc); + q6v5_clk_disable(qproc->dev, qproc->active_clks, + qproc->active_clk_count); + q6v5_regulator_disable(qproc, qproc->active_regs, + qproc->active_reg_count); return 0; } @@ -702,27 +842,27 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) return 0; } -static int q6v5_init_clocks(struct q6v5 *qproc) +static int q6v5_init_clocks(struct device *dev, struct clk **clks, + char **clk_names) { - qproc->ahb_clk = devm_clk_get(qproc->dev, "iface"); - if (IS_ERR(qproc->ahb_clk)) { - dev_err(qproc->dev, "failed to get iface clock\n"); - return PTR_ERR(qproc->ahb_clk); - } + int i; - qproc->axi_clk = devm_clk_get(qproc->dev, "bus"); - if (IS_ERR(qproc->axi_clk)) { - dev_err(qproc->dev, "failed to get bus clock\n"); - return PTR_ERR(qproc->axi_clk); - } + if (!clk_names) + return 0; - qproc->rom_clk = devm_clk_get(qproc->dev, "mem"); - if (IS_ERR(qproc->rom_clk)) { - dev_err(qproc->dev, "failed to get mem clock\n"); - return PTR_ERR(qproc->rom_clk); + for (i = 0; clk_names[i]; i++) { + clks[i] = devm_clk_get(dev, clk_names[i]); + if (IS_ERR(clks[i])) { + int rc = PTR_ERR(clks[i]); + + if (rc != -EPROBE_DEFER) + dev_err(dev, "Failed to get %s clock\n", + clk_names[i]); + return rc; + } } - return 0; + return i; } static int q6v5_init_reset(struct q6v5 *qproc) @@ -805,12 +945,17 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) static int q6v5_probe(struct platform_device *pdev) { + const struct rproc_hexagon_res *desc; struct q6v5 *qproc; struct rproc *rproc; int ret; + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, - MBA_FIRMWARE_NAME, sizeof(*qproc)); + desc->hexagon_mba_image, sizeof(*qproc)); if (!rproc) { dev_err(&pdev->dev, "failed to allocate rproc\n"); return -ENOMEM; @@ -834,13 +979,37 @@ static int q6v5_probe(struct platform_device *pdev) if (ret) goto free_rproc; - ret = q6v5_init_clocks(qproc); - if (ret) + ret = q6v5_init_clocks(&pdev->dev, qproc->proxy_clks, + desc->proxy_clk_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get proxy clocks.\n"); goto free_rproc; + } + qproc->proxy_clk_count = ret; - ret = q6v5_regulator_init(qproc); - if (ret) + ret = q6v5_init_clocks(&pdev->dev, qproc->active_clks, + desc->active_clk_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get active clocks.\n"); + goto free_rproc; + } + qproc->active_clk_count = ret; + + ret = q6v5_regulator_init(&pdev->dev, qproc->proxy_regs, + desc->proxy_supply); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get proxy regulators.\n"); + goto free_rproc; + } + qproc->proxy_reg_count = ret; + + ret = q6v5_regulator_init(&pdev->dev, qproc->active_regs, + desc->active_supply); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get active regulators.\n"); goto free_rproc; + } + qproc->active_reg_count = ret; ret = q6v5_init_reset(qproc); if (ret) @@ -868,6 +1037,8 @@ static int q6v5_probe(struct platform_device *pdev) goto free_rproc; } + qcom_add_smd_subdev(rproc, &qproc->smd_subdev); + ret = rproc_add(rproc); if (ret) goto free_rproc; @@ -885,13 +1056,83 @@ static int q6v5_remove(struct platform_device *pdev) struct q6v5 *qproc = platform_get_drvdata(pdev); rproc_del(qproc->rproc); + + qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); rproc_free(qproc->rproc); return 0; } +static const struct rproc_hexagon_res msm8916_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "mx", + .uV = 1050000, + }, + { + .supply = "cx", + .uA = 100000, + }, + { + .supply = "pll", + .uA = 100000, + }, + {} + }, + .proxy_clk_names = (char*[]){ + "xo", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + NULL + }, +}; + +static const struct rproc_hexagon_res msm8974_mss = { + .hexagon_mba_image = "mba.b00", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "mx", + .uV = 1050000, + }, + { + .supply = "cx", + .uA = 100000, + }, + { + .supply = "pll", + .uA = 100000, + }, + {} + }, + .active_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "mss", + .uV = 1050000, + .uA = 100000, + }, + {} + }, + .proxy_clk_names = (char*[]){ + "xo", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + NULL + }, +}; + static const struct of_device_id q6v5_of_match[] = { - { .compatible = "qcom,q6v5-pil", }, + { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss}, + { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss}, + { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss}, { }, }; MODULE_DEVICE_TABLE(of, q6v5_of_match); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index ebd61f5d18bb..c7686393d505 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -28,11 +28,12 @@ #include <linux/qcom_scm.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> +#include <linux/soc/qcom/mdt_loader.h> #include <linux/soc/qcom/smem.h> #include <linux/soc/qcom/smem_state.h> #include <linux/rpmsg/qcom_smd.h> -#include "qcom_mdt_loader.h" +#include "qcom_common.h" #include "remoteproc_internal.h" #include "qcom_wcnss.h" @@ -96,9 +97,7 @@ struct qcom_wcnss { void *mem_region; size_t mem_size; - struct device_node *smd_node; - struct qcom_smd_edge *smd_edge; - struct rproc_subdev smd_subdev; + struct qcom_rproc_subdev smd_subdev; }; static const struct wcnss_data riva_data = { @@ -152,34 +151,9 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, static int wcnss_load(struct rproc *rproc, const struct firmware *fw) { struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; - phys_addr_t fw_addr; - size_t fw_size; - bool relocate; - int ret; - - ret = qcom_scm_pas_init_image(WCNSS_PAS_ID, fw->data, fw->size); - if (ret) { - dev_err(&rproc->dev, "invalid firmware metadata\n"); - return ret; - } - - ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); - if (ret) { - dev_err(&rproc->dev, "failed to parse mdt header\n"); - return ret; - } - if (relocate) { - wcnss->mem_reloc = fw_addr; - - ret = qcom_scm_pas_mem_setup(WCNSS_PAS_ID, wcnss->mem_phys, fw_size); - if (ret) { - dev_err(&rproc->dev, "unable to setup memory for image\n"); - return ret; - } - } - - return qcom_mdt_load(rproc, fw, rproc->firmware); + return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID, + wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size); } static const struct rproc_fw_ops wcnss_fw_ops = { @@ -400,23 +374,6 @@ static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev) return IRQ_HANDLED; } -static int wcnss_smd_probe(struct rproc_subdev *subdev) -{ - struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev); - - wcnss->smd_edge = qcom_smd_register_edge(wcnss->dev, wcnss->smd_node); - - return IS_ERR(wcnss->smd_edge) ? PTR_ERR(wcnss->smd_edge) : 0; -} - -static void wcnss_smd_remove(struct rproc_subdev *subdev) -{ - struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev); - - qcom_smd_unregister_edge(wcnss->smd_edge); - wcnss->smd_edge = NULL; -} - static int wcnss_init_regulators(struct qcom_wcnss *wcnss, const struct wcnss_vreg_info *info, int num_vregs) @@ -599,9 +556,7 @@ static int wcnss_probe(struct platform_device *pdev) } } - wcnss->smd_node = of_get_child_by_name(pdev->dev.of_node, "smd-edge"); - if (wcnss->smd_node) - rproc_add_subdev(rproc, &wcnss->smd_subdev, wcnss_smd_probe, wcnss_smd_remove); + qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); ret = rproc_add(rproc); if (ret) @@ -621,9 +576,10 @@ static int wcnss_remove(struct platform_device *pdev) of_platform_depopulate(&pdev->dev); - of_node_put(wcnss->smd_node); qcom_smem_state_put(wcnss->state); rproc_del(wcnss->rproc); + + qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev); rproc_free(wcnss->rproc); return 0; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 90b05c72186c..3dabb20b8d5d 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -961,48 +961,35 @@ clean_up: } /* - * take a firmware and look for virtio devices to register. + * take a firmware and boot it up. * * Note: this function is called asynchronously upon registration of the * remote processor (so we must wait until it completes before we try * to unregister the device. one other option is just to use kref here, * that might be cleaner). */ -static void rproc_fw_config_virtio(const struct firmware *fw, void *context) +static void rproc_auto_boot_callback(const struct firmware *fw, void *context) { struct rproc *rproc = context; - /* if rproc is marked always-on, request it to boot */ - if (rproc->auto_boot) - rproc_boot(rproc); + rproc_boot(rproc); release_firmware(fw); - /* allow rproc_del() contexts, if any, to proceed */ - complete_all(&rproc->firmware_loading_complete); } -static int rproc_add_virtio_devices(struct rproc *rproc) +static int rproc_trigger_auto_boot(struct rproc *rproc) { int ret; - /* rproc_del() calls must wait until async loader completes */ - init_completion(&rproc->firmware_loading_complete); - /* - * We must retrieve early virtio configuration info from - * the firmware (e.g. whether to register a virtio device, - * what virtio features does it support, ...). - * * We're initiating an asynchronous firmware loading, so we can * be built-in kernel code, without hanging the boot process. */ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, rproc->firmware, &rproc->dev, GFP_KERNEL, - rproc, rproc_fw_config_virtio); - if (ret < 0) { + rproc, rproc_auto_boot_callback); + if (ret < 0) dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret); - complete_all(&rproc->firmware_loading_complete); - } return ret; } @@ -1099,6 +1086,12 @@ static int __rproc_boot(struct rproc *rproc) return ret; } + if (rproc->state == RPROC_DELETED) { + ret = -ENODEV; + dev_err(dev, "can't boot deleted rproc %s\n", rproc->name); + goto unlock_mutex; + } + /* skip the boot process if rproc is already powered up */ if (atomic_inc_return(&rproc->power) > 1) { ret = 0; @@ -1287,9 +1280,13 @@ int rproc_add(struct rproc *rproc) /* create debugfs entries */ rproc_create_debug_dir(rproc); - ret = rproc_add_virtio_devices(rproc); - if (ret < 0) - return ret; + + /* if rproc is marked always-on, request it to boot */ + if (rproc->auto_boot) { + ret = rproc_trigger_auto_boot(rproc); + if (ret < 0) + return ret; + } /* expose to rproc_get_by_phandle users */ mutex_lock(&rproc_list_mutex); @@ -1315,8 +1312,6 @@ static void rproc_type_release(struct device *dev) dev_info(&rproc->dev, "releasing %s\n", rproc->name); - rproc_delete_debug_dir(rproc); - idr_destroy(&rproc->notifyids); if (rproc->index >= 0) @@ -1483,14 +1478,17 @@ int rproc_del(struct rproc *rproc) if (!rproc) return -EINVAL; - /* if rproc is just being registered, wait */ - wait_for_completion(&rproc->firmware_loading_complete); - /* if rproc is marked always-on, rproc_add() booted it */ /* TODO: make sure this works with rproc->power > 1 */ if (rproc->auto_boot) rproc_shutdown(rproc); + mutex_lock(&rproc->lock); + rproc->state = RPROC_DELETED; + mutex_unlock(&rproc->lock); + + rproc_delete_debug_dir(rproc); + /* the rproc is downref'ed as soon as it's removed from the klist */ mutex_lock(&rproc_list_mutex); list_del(&rproc->node); diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c index bc5b0e00efb1..47be411400e5 100644 --- a/drivers/remoteproc/remoteproc_sysfs.c +++ b/drivers/remoteproc/remoteproc_sysfs.c @@ -73,6 +73,7 @@ static const char * const rproc_state_string[] = { [RPROC_SUSPENDED] = "suspended", [RPROC_RUNNING] = "running", [RPROC_CRASHED] = "crashed", + [RPROC_DELETED] = "deleted", [RPROC_LAST] = "invalid", }; diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 364411fb7734..0142cc3f0c91 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -137,7 +137,8 @@ static void rproc_virtio_del_vqs(struct virtio_device *vdev) static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], + struct irq_affinity *desc) { int i, ret; diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index da4e152e9733..d534bf23dc56 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/kernel.h> +#include <linux/mailbox_client.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> @@ -25,6 +26,16 @@ #include <linux/remoteproc.h> #include <linux/reset.h> +#include "remoteproc_internal.h" + +#define ST_RPROC_VQ0 0 +#define ST_RPROC_VQ1 1 +#define ST_RPROC_MAX_VRING 2 + +#define MBOX_RX 0 +#define MBOX_TX 1 +#define MBOX_MAX 2 + struct st_rproc_config { bool sw_reset; bool pwr_reset; @@ -39,8 +50,47 @@ struct st_rproc { u32 clk_rate; struct regmap *boot_base; u32 boot_offset; + struct mbox_chan *mbox_chan[ST_RPROC_MAX_VRING * MBOX_MAX]; + struct mbox_client mbox_client_vq0; + struct mbox_client mbox_client_vq1; }; +static void st_rproc_mbox_callback(struct device *dev, u32 msg) +{ + struct rproc *rproc = dev_get_drvdata(dev); + + if (rproc_vq_interrupt(rproc, msg) == IRQ_NONE) + dev_dbg(dev, "no message was found in vqid %d\n", msg); +} + +static +void st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data) +{ + st_rproc_mbox_callback(mbox_client->dev, 0); +} + +static +void st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data) +{ + st_rproc_mbox_callback(mbox_client->dev, 1); +} + +static void st_rproc_kick(struct rproc *rproc, int vqid) +{ + struct st_rproc *ddata = rproc->priv; + struct device *dev = rproc->dev.parent; + int ret; + + /* send the index of the triggered virtqueue in the mailbox payload */ + if (WARN_ON(vqid >= ST_RPROC_MAX_VRING)) + return; + + ret = mbox_send_message(ddata->mbox_chan[vqid * MBOX_MAX + MBOX_TX], + (void *)&vqid); + if (ret < 0) + dev_err(dev, "failed to send message via mbox: %d\n", ret); +} + static int st_rproc_start(struct rproc *rproc) { struct st_rproc *ddata = rproc->priv; @@ -107,7 +157,8 @@ static int st_rproc_stop(struct rproc *rproc) return sw_err ?: pwr_err; } -static struct rproc_ops st_rproc_ops = { +static const struct rproc_ops st_rproc_ops = { + .kick = st_rproc_kick, .start = st_rproc_start, .stop = st_rproc_stop, }; @@ -221,8 +272,9 @@ static int st_rproc_probe(struct platform_device *pdev) struct st_rproc *ddata; struct device_node *np = dev->of_node; struct rproc *rproc; + struct mbox_chan *chan; int enabled; - int ret; + int ret, i; match = of_match_device(st_rproc_match, dev); if (!match || !match->data) { @@ -247,7 +299,7 @@ static int st_rproc_probe(struct platform_device *pdev) enabled = st_rproc_state(pdev); if (enabled < 0) { ret = enabled; - goto free_rproc; + goto free_clk; } if (enabled) { @@ -257,12 +309,67 @@ static int st_rproc_probe(struct platform_device *pdev) clk_set_rate(ddata->clk, ddata->clk_rate); } + if (of_get_property(np, "mbox-names", NULL)) { + ddata->mbox_client_vq0.dev = dev; + ddata->mbox_client_vq0.tx_done = NULL; + ddata->mbox_client_vq0.tx_block = false; + ddata->mbox_client_vq0.knows_txdone = false; + ddata->mbox_client_vq0.rx_callback = st_rproc_mbox_callback_vq0; + + ddata->mbox_client_vq1.dev = dev; + ddata->mbox_client_vq1.tx_done = NULL; + ddata->mbox_client_vq1.tx_block = false; + ddata->mbox_client_vq1.knows_txdone = false; + ddata->mbox_client_vq1.rx_callback = st_rproc_mbox_callback_vq1; + + /* + * To control a co-processor without IPC mechanism. + * This driver can be used without mbox and rpmsg. + */ + chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_rx"); + if (IS_ERR(chan)) { + dev_err(&rproc->dev, "failed to request mbox chan 0\n"); + ret = PTR_ERR(chan); + goto free_clk; + } + ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_RX] = chan; + + chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_tx"); + if (IS_ERR(chan)) { + dev_err(&rproc->dev, "failed to request mbox chan 0\n"); + ret = PTR_ERR(chan); + goto free_mbox; + } + ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_TX] = chan; + + chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_rx"); + if (IS_ERR(chan)) { + dev_err(&rproc->dev, "failed to request mbox chan 1\n"); + ret = PTR_ERR(chan); + goto free_mbox; + } + ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_RX] = chan; + + chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_tx"); + if (IS_ERR(chan)) { + dev_err(&rproc->dev, "failed to request mbox chan 1\n"); + ret = PTR_ERR(chan); + goto free_mbox; + } + ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_TX] = chan; + } + ret = rproc_add(rproc); if (ret) - goto free_rproc; + goto free_mbox; return 0; +free_mbox: + for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) + mbox_free_channel(ddata->mbox_chan[i]); +free_clk: + clk_unprepare(ddata->clk); free_rproc: rproc_free(rproc); return ret; @@ -272,6 +379,7 @@ static int st_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); struct st_rproc *ddata = rproc->priv; + int i; rproc_del(rproc); @@ -279,6 +387,9 @@ static int st_rproc_remove(struct platform_device *pdev) of_reserved_mem_device_release(&pdev->dev); + for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) + mbox_free_channel(ddata->mbox_chan[i]); + rproc_free(rproc); return 0; diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 507716c8721f..6cfd862f945b 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c @@ -200,7 +200,7 @@ static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len) return va; } -static struct rproc_ops slim_rproc_ops = { +static const struct rproc_ops slim_rproc_ops = { .start = slim_rproc_start, .stop = slim_rproc_stop, .da_to_va = slim_rproc_da_to_va, diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c index 18175d0331fd..1ada0e51fef6 100644 --- a/drivers/remoteproc/wkup_m3_rproc.c +++ b/drivers/remoteproc/wkup_m3_rproc.c @@ -111,7 +111,7 @@ static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len) return va; } -static struct rproc_ops wkup_m3_rproc_ops = { +static const struct rproc_ops wkup_m3_rproc_ops = { .start = wkup_m3_rproc_start, .stop = wkup_m3_rproc_stop, .da_to_va = wkup_m3_rproc_da_to_va, diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 172dc966a01f..f4cdfe94b9ec 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -86,6 +86,12 @@ config RESET_UNIPHIER Say Y if you want to control reset signals provided by System Control block, Media I/O block, Peripheral Block. +config RESET_ZX2967 + bool "ZTE ZX2967 Reset Driver" + depends on ARCH_ZX || COMPILE_TEST + help + This enables the reset controller driver for ZTE's zx2967 family. + config RESET_ZYNQ bool "ZYNQ Reset Driver" if COMPILE_TEST default ARCH_ZYNQ diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 13b346e03d84..2cd3f6c45165 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -13,4 +13,5 @@ obj-$(CONFIG_RESET_STM32) += reset-stm32.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o +obj-$(CONFIG_RESET_ZX2967) += reset-zx2967.o obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c index b6f5f1e1826c..f1e5e65388bb 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -41,7 +41,7 @@ struct reset_control { struct list_head list; unsigned int id; unsigned int refcnt; - int shared; + bool shared; atomic_t deassert_count; atomic_t triggered_count; }; @@ -143,12 +143,18 @@ EXPORT_SYMBOL_GPL(devm_reset_controller_register); * a no-op. * Consumers must not use reset_control_(de)assert on shared reset lines when * reset_control_reset has been used. + * + * If rstc is NULL it is an optional reset and the function will just + * return 0. */ int reset_control_reset(struct reset_control *rstc) { int ret; - if (WARN_ON(IS_ERR_OR_NULL(rstc))) + if (!rstc) + return 0; + + if (WARN_ON(IS_ERR(rstc))) return -EINVAL; if (!rstc->rcdev->ops->reset) @@ -182,10 +188,17 @@ EXPORT_SYMBOL_GPL(reset_control_reset); * internal state to be reset, but must be prepared for this to happen. * Consumers must not use reset_control_reset on shared reset lines when * reset_control_(de)assert has been used. + * return 0. + * + * If rstc is NULL it is an optional reset and the function will just + * return 0. */ int reset_control_assert(struct reset_control *rstc) { - if (WARN_ON(IS_ERR_OR_NULL(rstc))) + if (!rstc) + return 0; + + if (WARN_ON(IS_ERR(rstc))) return -EINVAL; if (!rstc->rcdev->ops->assert) @@ -213,10 +226,17 @@ EXPORT_SYMBOL_GPL(reset_control_assert); * After calling this function, the reset is guaranteed to be deasserted. * Consumers must not use reset_control_reset on shared reset lines when * reset_control_(de)assert has been used. + * return 0. + * + * If rstc is NULL it is an optional reset and the function will just + * return 0. */ int reset_control_deassert(struct reset_control *rstc) { - if (WARN_ON(IS_ERR_OR_NULL(rstc))) + if (!rstc) + return 0; + + if (WARN_ON(IS_ERR(rstc))) return -EINVAL; if (!rstc->rcdev->ops->deassert) @@ -237,12 +257,15 @@ EXPORT_SYMBOL_GPL(reset_control_deassert); /** * reset_control_status - returns a negative errno if not supported, a * positive value if the reset line is asserted, or zero if the reset - * line is not asserted. + * line is not asserted or if the desc is NULL (optional reset). * @rstc: reset controller */ int reset_control_status(struct reset_control *rstc) { - if (WARN_ON(IS_ERR_OR_NULL(rstc))) + if (!rstc) + return 0; + + if (WARN_ON(IS_ERR(rstc))) return -EINVAL; if (rstc->rcdev->ops->status) @@ -254,7 +277,7 @@ EXPORT_SYMBOL_GPL(reset_control_status); static struct reset_control *__reset_control_get( struct reset_controller_dev *rcdev, - unsigned int index, int shared) + unsigned int index, bool shared) { struct reset_control *rstc; @@ -299,7 +322,8 @@ static void __reset_control_put(struct reset_control *rstc) } struct reset_control *__of_reset_control_get(struct device_node *node, - const char *id, int index, int shared) + const char *id, int index, bool shared, + bool optional) { struct reset_control *rstc; struct reset_controller_dev *r, *rcdev; @@ -313,14 +337,18 @@ struct reset_control *__of_reset_control_get(struct device_node *node, if (id) { index = of_property_match_string(node, "reset-names", id); + if (index == -EILSEQ) + return ERR_PTR(index); if (index < 0) - return ERR_PTR(-ENOENT); + return optional ? NULL : ERR_PTR(-ENOENT); } ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", index, &args); - if (ret) + if (ret == -EINVAL) return ERR_PTR(ret); + if (ret) + return optional ? NULL : ERR_PTR(ret); mutex_lock(&reset_list_mutex); rcdev = NULL; @@ -364,7 +392,7 @@ EXPORT_SYMBOL_GPL(__of_reset_control_get); void reset_control_put(struct reset_control *rstc) { - if (IS_ERR(rstc)) + if (IS_ERR_OR_NULL(rstc)) return; mutex_lock(&reset_list_mutex); @@ -379,7 +407,8 @@ static void devm_reset_control_release(struct device *dev, void *res) } struct reset_control *__devm_reset_control_get(struct device *dev, - const char *id, int index, int shared) + const char *id, int index, bool shared, + bool optional) { struct reset_control **ptr, *rstc; @@ -389,7 +418,7 @@ struct reset_control *__devm_reset_control_get(struct device *dev, return ERR_PTR(-ENOMEM); rstc = __of_reset_control_get(dev ? dev->of_node : NULL, - id, index, shared); + id, index, shared, optional); if (!IS_ERR(rstc)) { *ptr = rstc; devres_add(dev, ptr); diff --git a/drivers/reset/hisilicon/Kconfig b/drivers/reset/hisilicon/Kconfig index 1ff8b0c80980..10134dc03fe0 100644 --- a/drivers/reset/hisilicon/Kconfig +++ b/drivers/reset/hisilicon/Kconfig @@ -1,3 +1,10 @@ +config COMMON_RESET_HI3660 + tristate "Hi3660 Reset Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the Hisilicon Hi3660 reset driver. + config COMMON_RESET_HI6220 tristate "Hi6220 Reset Driver" depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/reset/hisilicon/Makefile b/drivers/reset/hisilicon/Makefile index c932f86e2f10..ab8a7bfcbd8d 100644 --- a/drivers/reset/hisilicon/Makefile +++ b/drivers/reset/hisilicon/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_COMMON_RESET_HI6220) += hi6220_reset.o +obj-$(CONFIG_COMMON_RESET_HI3660) += reset-hi3660.o diff --git a/drivers/reset/hisilicon/reset-hi3660.c b/drivers/reset/hisilicon/reset-hi3660.c new file mode 100644 index 000000000000..17d8bb128e6e --- /dev/null +++ b/drivers/reset/hisilicon/reset-hi3660.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +struct hi3660_reset_controller { + struct reset_controller_dev rst; + struct regmap *map; +}; + +#define to_hi3660_reset_controller(_rst) \ + container_of(_rst, struct hi3660_reset_controller, rst) + +static int hi3660_reset_program_hw(struct reset_controller_dev *rcdev, + unsigned long idx, bool assert) +{ + struct hi3660_reset_controller *rc = to_hi3660_reset_controller(rcdev); + unsigned int offset = idx >> 8; + unsigned int mask = BIT(idx & 0x1f); + + if (assert) + return regmap_write(rc->map, offset, mask); + else + return regmap_write(rc->map, offset + 4, mask); +} + +static int hi3660_reset_assert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + return hi3660_reset_program_hw(rcdev, idx, true); +} + +static int hi3660_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + return hi3660_reset_program_hw(rcdev, idx, false); +} + +static int hi3660_reset_dev(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + int err; + + err = hi3660_reset_assert(rcdev, idx); + if (err) + return err; + + return hi3660_reset_deassert(rcdev, idx); +} + +static struct reset_control_ops hi3660_reset_ops = { + .reset = hi3660_reset_dev, + .assert = hi3660_reset_assert, + .deassert = hi3660_reset_deassert, +}; + +static int hi3660_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + unsigned int offset, bit; + + offset = reset_spec->args[0]; + bit = reset_spec->args[1]; + + return (offset << 8) | bit; +} + +static int hi3660_reset_probe(struct platform_device *pdev) +{ + struct hi3660_reset_controller *rc; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + + rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); + if (!rc) + return -ENOMEM; + + rc->map = syscon_regmap_lookup_by_phandle(np, "hisi,rst-syscon"); + if (IS_ERR(rc->map)) { + dev_err(dev, "failed to get hi3660,rst-syscon\n"); + return PTR_ERR(rc->map); + } + + rc->rst.ops = &hi3660_reset_ops, + rc->rst.of_node = np; + rc->rst.of_reset_n_cells = 2; + rc->rst.of_xlate = hi3660_reset_xlate; + + return reset_controller_register(&rc->rst); +} + +static const struct of_device_id hi3660_reset_match[] = { + { .compatible = "hisilicon,hi3660-reset", }, + {}, +}; +MODULE_DEVICE_TABLE(of, hi3660_reset_match); + +static struct platform_driver hi3660_reset_driver = { + .probe = hi3660_reset_probe, + .driver = { + .name = "hi3660-reset", + .of_match_table = hi3660_reset_match, + }, +}; + +static int __init hi3660_reset_init(void) +{ + return platform_driver_register(&hi3660_reset_driver); +} +arch_initcall(hi3660_reset_init); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hi3660-reset"); +MODULE_DESCRIPTION("HiSilicon Hi3660 Reset Driver"); diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c index 47f0ffd3b013..99520b0a1329 100644 --- a/drivers/reset/reset-ti-syscon.c +++ b/drivers/reset/reset-ti-syscon.c @@ -154,11 +154,11 @@ static int ti_syscon_reset_status(struct reset_controller_dev *rcdev, if (ret) return ret; - return (reset_state & BIT(control->status_bit)) && - (control->flags & STATUS_SET); + return !(reset_state & BIT(control->status_bit)) == + !(control->flags & STATUS_SET); } -static struct reset_control_ops ti_syscon_reset_ops = { +static const struct reset_control_ops ti_syscon_reset_ops = { .assert = ti_syscon_reset_assert, .deassert = ti_syscon_reset_deassert, .status = ti_syscon_reset_status, diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c index 968c3ae4535c..9c11be3d3450 100644 --- a/drivers/reset/reset-uniphier.c +++ b/drivers/reset/reset-uniphier.c @@ -390,6 +390,10 @@ static const struct of_device_id uniphier_reset_match[] = { .data = uniphier_sld3_mio_reset_data, }, { + .compatible = "socionext,uniphier-ld11-sd-reset", + .data = uniphier_pro5_sd_reset_data, + }, + { .compatible = "socionext,uniphier-ld20-sd-reset", .data = uniphier_pro5_sd_reset_data, }, diff --git a/drivers/reset/reset-zx2967.c b/drivers/reset/reset-zx2967.c new file mode 100644 index 000000000000..4dabb9ec4841 --- /dev/null +++ b/drivers/reset/reset-zx2967.c @@ -0,0 +1,99 @@ +/* + * ZTE's zx2967 family reset controller driver + * + * Copyright (C) 2017 ZTE Ltd. + * + * Author: Baoyou Xie <baoyou.xie@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> + +struct zx2967_reset { + void __iomem *reg_base; + spinlock_t lock; + struct reset_controller_dev rcdev; +}; + +static int zx2967_reset_act(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct zx2967_reset *reset = NULL; + int bank = id / 32; + int offset = id % 32; + u32 reg; + unsigned long flags; + + reset = container_of(rcdev, struct zx2967_reset, rcdev); + + spin_lock_irqsave(&reset->lock, flags); + + reg = readl_relaxed(reset->reg_base + (bank * 4)); + if (assert) + reg &= ~BIT(offset); + else + reg |= BIT(offset); + writel_relaxed(reg, reset->reg_base + (bank * 4)); + + spin_unlock_irqrestore(&reset->lock, flags); + + return 0; +} + +static int zx2967_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return zx2967_reset_act(rcdev, id, true); +} + +static int zx2967_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return zx2967_reset_act(rcdev, id, false); +} + +static struct reset_control_ops zx2967_reset_ops = { + .assert = zx2967_reset_assert, + .deassert = zx2967_reset_deassert, +}; + +static int zx2967_reset_probe(struct platform_device *pdev) +{ + struct zx2967_reset *reset; + struct resource *res; + + reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); + if (!reset) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reset->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reset->reg_base)) + return PTR_ERR(reset->reg_base); + + spin_lock_init(&reset->lock); + + reset->rcdev.owner = THIS_MODULE; + reset->rcdev.nr_resets = resource_size(res) * 8; + reset->rcdev.ops = &zx2967_reset_ops; + reset->rcdev.of_node = pdev->dev.of_node; + + return devm_reset_controller_register(&pdev->dev, &reset->rcdev); +} + +static const struct of_device_id zx2967_reset_dt_ids[] = { + { .compatible = "zte,zx296718-reset", }, + {}, +}; + +static struct platform_driver zx2967_reset_driver = { + .probe = zx2967_reset_probe, + .driver = { + .name = "zx2967-reset", + .of_match_table = zx2967_reset_dt_ids, + }, +}; +builtin_platform_driver(zx2967_reset_driver); diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index de31c5f14dd9..f12ac0b28263 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -4,6 +4,15 @@ menu "Rpmsg drivers" config RPMSG tristate +config RPMSG_CHAR + tristate "RPMSG device interface" + depends on RPMSG + depends on NET + help + Say Y here to export rpmsg endpoints as device files, usually found + in /dev. They make it possible for user-space programs to send and + receive rpmsg packets. + config RPMSG_QCOM_SMD tristate "Qualcomm Shared Memory Driver (SMD)" depends on QCOM_SMEM diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index ae9c9132cf76..fae9a6d548fb 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_RPMSG) += rpmsg_core.o +obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index 0fae48116a0d..beaef5dd973e 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c @@ -117,6 +117,8 @@ static const struct { struct qcom_smd_edge { struct device dev; + const char *name; + struct device_node *of_node; unsigned edge_id; unsigned remote_pid; @@ -917,6 +919,21 @@ static int qcom_smd_trysend(struct rpmsg_endpoint *ept, void *data, int len) return __qcom_smd_send(qsept->qsch, data, len, false); } +static unsigned int qcom_smd_poll(struct rpmsg_endpoint *ept, + struct file *filp, poll_table *wait) +{ + struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); + struct qcom_smd_channel *channel = qsept->qsch; + unsigned int mask = 0; + + poll_wait(filp, &channel->fblockread_event, wait); + + if (qcom_smd_get_tx_avail(channel) > 20) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + /* * Finds the device_node for the smd child interested in this channel. */ @@ -949,6 +966,7 @@ static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops = { .destroy_ept = qcom_smd_destroy_ept, .send = qcom_smd_send, .trysend = qcom_smd_trysend, + .poll = qcom_smd_poll, }; /* @@ -984,6 +1002,20 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) return rpmsg_register_device(rpdev); } +static int qcom_smd_create_chrdev(struct qcom_smd_edge *edge) +{ + struct qcom_smd_device *qsdev; + + qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL); + if (!qsdev) + return -ENOMEM; + + qsdev->edge = edge; + qsdev->rpdev.ops = &qcom_smd_device_ops; + qsdev->rpdev.dev.parent = &edge->dev; + return rpmsg_chrdev_register_device(&qsdev->rpdev); +} + /* * Allocate the qcom_smd_channel object for a newly found smd channel, * retrieving and validating the smem items involved. @@ -1248,6 +1280,10 @@ static int qcom_smd_parse_edge(struct device *dev, return -EINVAL; } + ret = of_property_read_string(node, "label", &edge->name); + if (ret < 0) + edge->name = node->name; + irq = irq_of_parse_and_map(node, 0); if (irq < 0) { dev_err(dev, "required smd interrupt missing\n"); @@ -1285,6 +1321,21 @@ static void qcom_smd_edge_release(struct device *dev) kfree(edge); } +static ssize_t rpmsg_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qcom_smd_edge *edge = to_smd_edge(dev); + + return sprintf(buf, "%s\n", edge->name); +} +static DEVICE_ATTR_RO(rpmsg_name); + +static struct attribute *qcom_smd_edge_attrs[] = { + &dev_attr_rpmsg_name.attr, + NULL +}; +ATTRIBUTE_GROUPS(qcom_smd_edge); + /** * qcom_smd_register_edge() - register an edge based on an device_node * @parent: parent device for the edge @@ -1306,6 +1357,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, edge->dev.parent = parent; edge->dev.release = qcom_smd_edge_release; + edge->dev.groups = qcom_smd_edge_groups; dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); ret = device_register(&edge->dev); if (ret) { @@ -1319,6 +1371,12 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, goto unregister_dev; } + ret = qcom_smd_create_chrdev(edge); + if (ret) { + dev_err(&edge->dev, "failed to register chrdev for edge\n"); + goto unregister_dev; + } + schedule_work(&edge->scan_work); return edge; diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c new file mode 100644 index 000000000000..0ca2ccc09ca6 --- /dev/null +++ b/drivers/rpmsg/rpmsg_char.c @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2016, Linaro Ltd. + * Copyright (c) 2012, Michal Simek <monstr@monstr.eu> + * Copyright (c) 2012, PetaLogix + * Copyright (c) 2011, Texas Instruments, Inc. + * Copyright (c) 2011, Google, Inc. + * + * Based on rpmsg performance statistics driver by Michal Simek, which in turn + * was based on TI & Google OMX rpmsg driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/poll.h> +#include <linux/rpmsg.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <uapi/linux/rpmsg.h> + +#include "rpmsg_internal.h" + +#define RPMSG_DEV_MAX (MINORMASK + 1) + +static dev_t rpmsg_major; +static struct class *rpmsg_class; + +static DEFINE_IDA(rpmsg_ctrl_ida); +static DEFINE_IDA(rpmsg_ept_ida); +static DEFINE_IDA(rpmsg_minor_ida); + +#define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev) +#define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev) + +#define dev_to_ctrldev(dev) container_of(dev, struct rpmsg_ctrldev, dev) +#define cdev_to_ctrldev(i_cdev) container_of(i_cdev, struct rpmsg_ctrldev, cdev) + +/** + * struct rpmsg_ctrldev - control device for instantiating endpoint devices + * @rpdev: underlaying rpmsg device + * @cdev: cdev for the ctrl device + * @dev: device for the ctrl device + */ +struct rpmsg_ctrldev { + struct rpmsg_device *rpdev; + struct cdev cdev; + struct device dev; +}; + +/** + * struct rpmsg_eptdev - endpoint device context + * @dev: endpoint device + * @cdev: cdev for the endpoint device + * @rpdev: underlaying rpmsg device + * @chinfo: info used to open the endpoint + * @ept_lock: synchronization of @ept modifications + * @ept: rpmsg endpoint reference, when open + * @queue_lock: synchronization of @queue operations + * @queue: incoming message queue + * @readq: wait object for incoming queue + */ +struct rpmsg_eptdev { + struct device dev; + struct cdev cdev; + + struct rpmsg_device *rpdev; + struct rpmsg_channel_info chinfo; + + struct mutex ept_lock; + struct rpmsg_endpoint *ept; + + spinlock_t queue_lock; + struct sk_buff_head queue; + wait_queue_head_t readq; +}; + +static int rpmsg_eptdev_destroy(struct device *dev, void *data) +{ + struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); + + mutex_lock(&eptdev->ept_lock); + if (eptdev->ept) { + rpmsg_destroy_ept(eptdev->ept); + eptdev->ept = NULL; + } + mutex_unlock(&eptdev->ept_lock); + + /* wake up any blocked readers */ + wake_up_interruptible(&eptdev->readq); + + device_del(&eptdev->dev); + put_device(&eptdev->dev); + + return 0; +} + +static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, + void *priv, u32 addr) +{ + struct rpmsg_eptdev *eptdev = priv; + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, len), buf, len); + + spin_lock(&eptdev->queue_lock); + skb_queue_tail(&eptdev->queue, skb); + spin_unlock(&eptdev->queue_lock); + + /* wake up any blocking processes, waiting for new data */ + wake_up_interruptible(&eptdev->readq); + + return 0; +} + +static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) +{ + struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); + struct rpmsg_endpoint *ept; + struct rpmsg_device *rpdev = eptdev->rpdev; + struct device *dev = &eptdev->dev; + + get_device(dev); + + ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo); + if (!ept) { + dev_err(dev, "failed to open %s\n", eptdev->chinfo.name); + put_device(dev); + return -EINVAL; + } + + eptdev->ept = ept; + filp->private_data = eptdev; + + return 0; +} + +static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) +{ + struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); + struct device *dev = &eptdev->dev; + struct sk_buff *skb; + + /* Close the endpoint, if it's not already destroyed by the parent */ + mutex_lock(&eptdev->ept_lock); + if (eptdev->ept) { + rpmsg_destroy_ept(eptdev->ept); + eptdev->ept = NULL; + } + mutex_unlock(&eptdev->ept_lock); + + /* Discard all SKBs */ + while (!skb_queue_empty(&eptdev->queue)) { + skb = skb_dequeue(&eptdev->queue); + kfree_skb(skb); + } + + put_device(dev); + + return 0; +} + +static ssize_t rpmsg_eptdev_read(struct file *filp, char __user *buf, + size_t len, loff_t *f_pos) +{ + struct rpmsg_eptdev *eptdev = filp->private_data; + unsigned long flags; + struct sk_buff *skb; + int use; + + if (!eptdev->ept) + return -EPIPE; + + spin_lock_irqsave(&eptdev->queue_lock, flags); + + /* Wait for data in the queue */ + if (skb_queue_empty(&eptdev->queue)) { + spin_unlock_irqrestore(&eptdev->queue_lock, flags); + + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + /* Wait until we get data or the endpoint goes away */ + if (wait_event_interruptible(eptdev->readq, + !skb_queue_empty(&eptdev->queue) || + !eptdev->ept)) + return -ERESTARTSYS; + + /* We lost the endpoint while waiting */ + if (!eptdev->ept) + return -EPIPE; + + spin_lock_irqsave(&eptdev->queue_lock, flags); + } + + skb = skb_dequeue(&eptdev->queue); + spin_unlock_irqrestore(&eptdev->queue_lock, flags); + if (!skb) + return -EFAULT; + + use = min_t(size_t, len, skb->len); + if (copy_to_user(buf, skb->data, use)) + use = -EFAULT; + + kfree_skb(skb); + + return use; +} + +static ssize_t rpmsg_eptdev_write(struct file *filp, const char __user *buf, + size_t len, loff_t *f_pos) +{ + struct rpmsg_eptdev *eptdev = filp->private_data; + void *kbuf; + int ret; + + kbuf = memdup_user(buf, len); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + if (mutex_lock_interruptible(&eptdev->ept_lock)) { + ret = -ERESTARTSYS; + goto free_kbuf; + } + + if (!eptdev->ept) { + ret = -EPIPE; + goto unlock_eptdev; + } + + if (filp->f_flags & O_NONBLOCK) + ret = rpmsg_trysend(eptdev->ept, kbuf, len); + else + ret = rpmsg_send(eptdev->ept, kbuf, len); + +unlock_eptdev: + mutex_unlock(&eptdev->ept_lock); + +free_kbuf: + kfree(kbuf); + return ret < 0 ? ret : len; +} + +static unsigned int rpmsg_eptdev_poll(struct file *filp, poll_table *wait) +{ + struct rpmsg_eptdev *eptdev = filp->private_data; + unsigned int mask = 0; + + if (!eptdev->ept) + return POLLERR; + + poll_wait(filp, &eptdev->readq, wait); + + if (!skb_queue_empty(&eptdev->queue)) + mask |= POLLIN | POLLRDNORM; + + mask |= rpmsg_poll(eptdev->ept, filp, wait); + + return mask; +} + +static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct rpmsg_eptdev *eptdev = fp->private_data; + + if (cmd != RPMSG_DESTROY_EPT_IOCTL) + return -EINVAL; + + return rpmsg_eptdev_destroy(&eptdev->dev, NULL); +} + +static const struct file_operations rpmsg_eptdev_fops = { + .owner = THIS_MODULE, + .open = rpmsg_eptdev_open, + .release = rpmsg_eptdev_release, + .read = rpmsg_eptdev_read, + .write = rpmsg_eptdev_write, + .poll = rpmsg_eptdev_poll, + .unlocked_ioctl = rpmsg_eptdev_ioctl, +}; + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", eptdev->chinfo.name); +} +static DEVICE_ATTR_RO(name); + +static ssize_t src_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", eptdev->chinfo.src); +} +static DEVICE_ATTR_RO(src); + +static ssize_t dst_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", eptdev->chinfo.dst); +} +static DEVICE_ATTR_RO(dst); + +static struct attribute *rpmsg_eptdev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_src.attr, + &dev_attr_dst.attr, + NULL +}; +ATTRIBUTE_GROUPS(rpmsg_eptdev); + +static void rpmsg_eptdev_release_device(struct device *dev) +{ + struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); + + ida_simple_remove(&rpmsg_ept_ida, dev->id); + ida_simple_remove(&rpmsg_minor_ida, MINOR(eptdev->dev.devt)); + cdev_del(&eptdev->cdev); + kfree(eptdev); +} + +static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev, + struct rpmsg_channel_info chinfo) +{ + struct rpmsg_device *rpdev = ctrldev->rpdev; + struct rpmsg_eptdev *eptdev; + struct device *dev; + int ret; + + eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL); + if (!eptdev) + return -ENOMEM; + + dev = &eptdev->dev; + eptdev->rpdev = rpdev; + eptdev->chinfo = chinfo; + + mutex_init(&eptdev->ept_lock); + spin_lock_init(&eptdev->queue_lock); + skb_queue_head_init(&eptdev->queue); + init_waitqueue_head(&eptdev->readq); + + device_initialize(dev); + dev->class = rpmsg_class; + dev->parent = &ctrldev->dev; + dev->groups = rpmsg_eptdev_groups; + dev_set_drvdata(dev, eptdev); + + cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops); + eptdev->cdev.owner = THIS_MODULE; + + ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); + if (ret < 0) + goto free_eptdev; + dev->devt = MKDEV(MAJOR(rpmsg_major), ret); + + ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto free_minor_ida; + dev->id = ret; + dev_set_name(dev, "rpmsg%d", ret); + + ret = cdev_add(&eptdev->cdev, dev->devt, 1); + if (ret) + goto free_ept_ida; + + /* We can now rely on the release function for cleanup */ + dev->release = rpmsg_eptdev_release_device; + + ret = device_add(dev); + if (ret) { + dev_err(dev, "device_register failed: %d\n", ret); + put_device(dev); + } + + return ret; + +free_ept_ida: + ida_simple_remove(&rpmsg_ept_ida, dev->id); +free_minor_ida: + ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); +free_eptdev: + put_device(dev); + kfree(eptdev); + + return ret; +} + +static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp) +{ + struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); + + get_device(&ctrldev->dev); + filp->private_data = ctrldev; + + return 0; +} + +static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp) +{ + struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); + + put_device(&ctrldev->dev); + + return 0; +} + +static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct rpmsg_ctrldev *ctrldev = fp->private_data; + void __user *argp = (void __user *)arg; + struct rpmsg_endpoint_info eptinfo; + struct rpmsg_channel_info chinfo; + + if (cmd != RPMSG_CREATE_EPT_IOCTL) + return -EINVAL; + + if (copy_from_user(&eptinfo, argp, sizeof(eptinfo))) + return -EFAULT; + + memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE); + chinfo.name[RPMSG_NAME_SIZE-1] = '\0'; + chinfo.src = eptinfo.src; + chinfo.dst = eptinfo.dst; + + return rpmsg_eptdev_create(ctrldev, chinfo); +}; + +static const struct file_operations rpmsg_ctrldev_fops = { + .owner = THIS_MODULE, + .open = rpmsg_ctrldev_open, + .release = rpmsg_ctrldev_release, + .unlocked_ioctl = rpmsg_ctrldev_ioctl, +}; + +static void rpmsg_ctrldev_release_device(struct device *dev) +{ + struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev); + + ida_simple_remove(&rpmsg_ctrl_ida, dev->id); + ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); + cdev_del(&ctrldev->cdev); + kfree(ctrldev); +} + +static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_ctrldev *ctrldev; + struct device *dev; + int ret; + + ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL); + if (!ctrldev) + return -ENOMEM; + + ctrldev->rpdev = rpdev; + + dev = &ctrldev->dev; + device_initialize(dev); + dev->parent = &rpdev->dev; + dev->class = rpmsg_class; + + cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops); + ctrldev->cdev.owner = THIS_MODULE; + + ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); + if (ret < 0) + goto free_ctrldev; + dev->devt = MKDEV(MAJOR(rpmsg_major), ret); + + ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto free_minor_ida; + dev->id = ret; + dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret); + + ret = cdev_add(&ctrldev->cdev, dev->devt, 1); + if (ret) + goto free_ctrl_ida; + + /* We can now rely on the release function for cleanup */ + dev->release = rpmsg_ctrldev_release_device; + + ret = device_add(dev); + if (ret) { + dev_err(&rpdev->dev, "device_register failed: %d\n", ret); + put_device(dev); + } + + dev_set_drvdata(&rpdev->dev, ctrldev); + + return ret; + +free_ctrl_ida: + ida_simple_remove(&rpmsg_ctrl_ida, dev->id); +free_minor_ida: + ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); +free_ctrldev: + put_device(dev); + kfree(ctrldev); + + return ret; +} + +static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev) +{ + struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev); + int ret; + + /* Destroy all endpoints */ + ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_eptdev_destroy); + if (ret) + dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret); + + device_del(&ctrldev->dev); + put_device(&ctrldev->dev); +} + +static struct rpmsg_driver rpmsg_chrdev_driver = { + .probe = rpmsg_chrdev_probe, + .remove = rpmsg_chrdev_remove, + .drv = { + .name = "rpmsg_chrdev", + }, +}; + +static int rpmsg_char_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg"); + if (ret < 0) { + pr_err("rpmsg: failed to allocate char dev region\n"); + return ret; + } + + rpmsg_class = class_create(THIS_MODULE, "rpmsg"); + if (IS_ERR(rpmsg_class)) { + pr_err("failed to create rpmsg class\n"); + unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); + return PTR_ERR(rpmsg_class); + } + + ret = register_rpmsg_driver(&rpmsg_chrdev_driver); + if (ret < 0) { + pr_err("rpmsgchr: failed to register rpmsg driver\n"); + class_destroy(rpmsg_class); + unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); + } + + return ret; +} +postcore_initcall(rpmsg_char_init); + +static void rpmsg_chrdev_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_chrdev_driver); + class_destroy(rpmsg_class); + unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); +} +module_exit(rpmsg_chrdev_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 1cfb775e8e82..600f5f9f7431 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -72,7 +72,7 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, struct rpmsg_channel_info chinfo) { if (WARN_ON(!rpdev)) - return ERR_PTR(-EINVAL); + return NULL; return rpdev->ops->create_ept(rpdev, cb, priv, chinfo); } @@ -240,6 +240,26 @@ int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) EXPORT_SYMBOL(rpmsg_trysendto); /** + * rpmsg_poll() - poll the endpoint's send buffers + * @ept: the rpmsg endpoint + * @filp: file for poll_wait() + * @wait: poll_table for poll_wait() + * + * Returns mask representing the current state of the endpoint's send buffers + */ +unsigned int rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, + poll_table *wait) +{ + if (WARN_ON(!ept)) + return 0; + if (!ept->ops->poll) + return 0; + + return ept->ops->poll(ept, filp, wait); +} +EXPORT_SYMBOL(rpmsg_poll); + +/** * rpmsg_send_offchannel() - send a message using explicit src/dst addresses * @ept: the rpmsg endpoint * @src: source address diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 8075a20f919b..0cf9c7e2ee83 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -21,6 +21,7 @@ #define __RPMSG_INTERNAL_H__ #include <linux/rpmsg.h> +#include <linux/poll.h> #define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev) #define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) @@ -70,6 +71,8 @@ struct rpmsg_endpoint_ops { int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int (*trysend_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len); + unsigned int (*poll)(struct rpmsg_endpoint *ept, struct file *filp, + poll_table *wait); }; int rpmsg_register_device(struct rpmsg_device *rpdev); @@ -79,4 +82,19 @@ int rpmsg_unregister_device(struct device *parent, struct device *rpmsg_find_device(struct device *parent, struct rpmsg_channel_info *chinfo); +/** + * rpmsg_chrdev_register_device() - register chrdev device based on rpdev + * @rpdev: prepared rpdev to be used for creating endpoints + * + * This function wraps rpmsg_register_device() preparing the rpdev for use as + * basis for the rpmsg chrdev. + */ +static inline int rpmsg_chrdev_register_device(struct rpmsg_device *rpdev) +{ + strcpy(rpdev->id.name, "rpmsg_chrdev"); + rpdev->driver_override = "rpmsg_chrdev"; + + return rpmsg_register_device(rpdev); +} + #endif diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 3090b0d3072f..5e66e081027e 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -869,7 +869,7 @@ static int rpmsg_probe(struct virtio_device *vdev) init_waitqueue_head(&vrp->sendq); /* We expect two virtqueues, rx and tx (and in this order) */ - err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names); + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names, NULL); if (err) goto free_vrp; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 5dc673dc9487..ee1b0e9dde79 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1434,9 +1434,10 @@ config RTC_DRV_SUN4V based RTC on SUN4V systems. config RTC_DRV_SUN6I - tristate "Allwinner A31 RTC" - default MACH_SUN6I || MACH_SUN8I || COMPILE_TEST - depends on ARCH_SUNXI + bool "Allwinner A31 RTC" + default MACH_SUN6I || MACH_SUN8I + depends on COMMON_CLK + depends on ARCH_SUNXI || COMPILE_TEST help If you say Y here you will get support for the RTC found in some Allwinner SoCs like the A31 or the A64. @@ -1719,6 +1720,17 @@ config RTC_DRV_R7301 This driver can also be built as a module. If so, the module will be called rtc-r7301. +config RTC_DRV_STM32 + tristate "STM32 RTC" + select REGMAP_MMIO + depends on ARCH_STM32 || COMPILE_TEST + help + If you say yes here you get support for the STM32 On-Chip + Real Time Clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-stm32". + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index f13ab1c5c222..f07297b1460a 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o +obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c index 9a3f2a6f512e..21f355c37eab 100644 --- a/drivers/rtc/rtc-armada38x.c +++ b/drivers/rtc/rtc-armada38x.c @@ -16,6 +16,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/rtc.h> @@ -23,17 +24,48 @@ #define RTC_STATUS_ALARM1 BIT(0) #define RTC_STATUS_ALARM2 BIT(1) #define RTC_IRQ1_CONF 0x4 -#define RTC_IRQ1_AL_EN BIT(0) -#define RTC_IRQ1_FREQ_EN BIT(1) -#define RTC_IRQ1_FREQ_1HZ BIT(2) +#define RTC_IRQ2_CONF 0x8 +#define RTC_IRQ_AL_EN BIT(0) +#define RTC_IRQ_FREQ_EN BIT(1) +#define RTC_IRQ_FREQ_1HZ BIT(2) + #define RTC_TIME 0xC #define RTC_ALARM1 0x10 - -#define SOC_RTC_INTERRUPT 0x8 -#define SOC_RTC_ALARM1 BIT(0) -#define SOC_RTC_ALARM2 BIT(1) -#define SOC_RTC_ALARM1_MASK BIT(2) -#define SOC_RTC_ALARM2_MASK BIT(3) +#define RTC_ALARM2 0x14 + +/* Armada38x SoC registers */ +#define RTC_38X_BRIDGE_TIMING_CTL 0x0 +#define RTC_38X_PERIOD_OFFS 0 +#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) +#define RTC_38X_READ_DELAY_OFFS 26 +#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS) + +/* Armada 7K/8K registers */ +#define RTC_8K_BRIDGE_TIMING_CTL0 0x0 +#define RTC_8K_WRCLK_PERIOD_OFFS 0 +#define RTC_8K_WRCLK_PERIOD_MASK (0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS) +#define RTC_8K_WRCLK_SETUP_OFFS 16 +#define RTC_8K_WRCLK_SETUP_MASK (0xFFFF << RTC_8K_WRCLK_SETUP_OFFS) +#define RTC_8K_BRIDGE_TIMING_CTL1 0x4 +#define RTC_8K_READ_DELAY_OFFS 0 +#define RTC_8K_READ_DELAY_MASK (0xFFFF << RTC_8K_READ_DELAY_OFFS) + +#define RTC_8K_ISR 0x10 +#define RTC_8K_IMR 0x14 +#define RTC_8K_ALARM2 BIT(0) + +#define SOC_RTC_INTERRUPT 0x8 +#define SOC_RTC_ALARM1 BIT(0) +#define SOC_RTC_ALARM2 BIT(1) +#define SOC_RTC_ALARM1_MASK BIT(2) +#define SOC_RTC_ALARM2_MASK BIT(3) + +#define SAMPLE_NR 100 + +struct value_to_freq { + u32 value; + u8 freq; +}; struct armada38x_rtc { struct rtc_device *rtc_dev; @@ -41,38 +73,153 @@ struct armada38x_rtc { void __iomem *regs_soc; spinlock_t lock; int irq; + struct value_to_freq *val_to_freq; + struct armada38x_rtc_data *data; +}; + +#define ALARM1 0 +#define ALARM2 1 + +#define ALARM_REG(base, alarm) ((base) + (alarm) * sizeof(u32)) + +struct armada38x_rtc_data { + /* Initialize the RTC-MBUS bridge timing */ + void (*update_mbus_timing)(struct armada38x_rtc *rtc); + u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg); + void (*clear_isr)(struct armada38x_rtc *rtc); + void (*unmask_interrupt)(struct armada38x_rtc *rtc); + u32 alarm; }; /* * According to the datasheet, the OS should wait 5us after every * register write to the RTC hard macro so that the required update * can occur without holding off the system bus + * According to errata RES-3124064, Write to any RTC register + * may fail. As a workaround, before writing to RTC + * register, issue a dummy write of 0x0 twice to RTC Status + * register. */ + static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset) { + writel(0, rtc->regs + RTC_STATUS); + writel(0, rtc->regs + RTC_STATUS); writel(val, rtc->regs + offset); udelay(5); } +/* Update RTC-MBUS bridge timing parameters */ +static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); + reg &= ~RTC_38X_PERIOD_MASK; + reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */ + reg &= ~RTC_38X_READ_DELAY_MASK; + reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */ + writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); +} + +static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); + reg &= ~RTC_8K_WRCLK_PERIOD_MASK; + reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS; + reg &= ~RTC_8K_WRCLK_SETUP_MASK; + reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS; + writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); + + reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); + reg &= ~RTC_8K_READ_DELAY_MASK; + reg |= 0x3F << RTC_8K_READ_DELAY_OFFS; + writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); +} + +static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + return readl(rtc->regs + rtc_reg); +} + +static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + int i, index_max = 0, max = 0; + + for (i = 0; i < SAMPLE_NR; i++) { + rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg); + rtc->val_to_freq[i].freq = 0; + } + + for (i = 0; i < SAMPLE_NR; i++) { + int j = 0; + u32 value = rtc->val_to_freq[i].value; + + while (rtc->val_to_freq[j].freq) { + if (rtc->val_to_freq[j].value == value) { + rtc->val_to_freq[j].freq++; + break; + } + j++; + } + + if (!rtc->val_to_freq[j].freq) { + rtc->val_to_freq[j].value = value; + rtc->val_to_freq[j].freq = 1; + } + + if (rtc->val_to_freq[j].freq > max) { + index_max = j; + max = rtc->val_to_freq[j].freq; + } + + /* + * If a value already has half of the sample this is the most + * frequent one and we can stop the research right now + */ + if (max > SAMPLE_NR / 2) + break; + } + + return rtc->val_to_freq[index_max].value; +} + +static void armada38x_clear_isr(struct armada38x_rtc *rtc) +{ + u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); + + writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); +} + +static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc) +{ + u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); + + writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT); +} + +static void armada8k_clear_isr(struct armada38x_rtc *rtc) +{ + writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR); +} + +static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc) +{ + writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR); +} + static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct armada38x_rtc *rtc = dev_get_drvdata(dev); - unsigned long time, time_check, flags; + unsigned long time, flags; spin_lock_irqsave(&rtc->lock, flags); - time = readl(rtc->regs + RTC_TIME); - /* - * WA for failing time set attempts. As stated in HW ERRATA if - * more than one second between two time reads is detected - * then read once again. - */ - time_check = readl(rtc->regs + RTC_TIME); - if ((time_check - time) > 1) - time_check = readl(rtc->regs + RTC_TIME); - + time = rtc->data->read_rtc_reg(rtc, RTC_TIME); spin_unlock_irqrestore(&rtc->lock, flags); - rtc_time_to_tm(time_check, tm); + rtc_time_to_tm(time, tm); return 0; } @@ -87,16 +234,9 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm) if (ret) goto out; - /* - * According to errata FE-3124064, Write to RTC TIME register - * may fail. As a workaround, after writing to RTC TIME - * register, issue a dummy write of 0x0 twice to RTC Status - * register. - */ + spin_lock_irqsave(&rtc->lock, flags); rtc_delayed_write(time, rtc, RTC_TIME); - rtc_delayed_write(0, rtc, RTC_STATUS); - rtc_delayed_write(0, rtc, RTC_STATUS); spin_unlock_irqrestore(&rtc->lock, flags); out: @@ -107,12 +247,14 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct armada38x_rtc *rtc = dev_get_drvdata(dev); unsigned long time, flags; + u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); u32 val; spin_lock_irqsave(&rtc->lock, flags); - time = readl(rtc->regs + RTC_ALARM1); - val = readl(rtc->regs + RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN; + time = rtc->data->read_rtc_reg(rtc, reg); + val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN; spin_unlock_irqrestore(&rtc->lock, flags); @@ -125,9 +267,10 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct armada38x_rtc *rtc = dev_get_drvdata(dev); + u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); unsigned long time, flags; int ret = 0; - u32 val; ret = rtc_tm_to_time(&alrm->time, &time); @@ -136,13 +279,11 @@ static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) spin_lock_irqsave(&rtc->lock, flags); - rtc_delayed_write(time, rtc, RTC_ALARM1); + rtc_delayed_write(time, rtc, reg); if (alrm->enabled) { - rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF); - val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); - writel(val | SOC_RTC_ALARM1_MASK, - rtc->regs_soc + SOC_RTC_INTERRUPT); + rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); + rtc->data->unmask_interrupt(rtc); } spin_unlock_irqrestore(&rtc->lock, flags); @@ -155,14 +296,15 @@ static int armada38x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct armada38x_rtc *rtc = dev_get_drvdata(dev); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); unsigned long flags; spin_lock_irqsave(&rtc->lock, flags); if (enabled) - rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF); + rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); else - rtc_delayed_write(0, rtc, RTC_IRQ1_CONF); + rtc_delayed_write(0, rtc, reg_irq); spin_unlock_irqrestore(&rtc->lock, flags); @@ -174,24 +316,23 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) struct armada38x_rtc *rtc = data; u32 val; int event = RTC_IRQF | RTC_AF; + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq); spin_lock(&rtc->lock); - val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); - - writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); - val = readl(rtc->regs + RTC_IRQ1_CONF); - /* disable all the interrupts for alarm 1 */ - rtc_delayed_write(0, rtc, RTC_IRQ1_CONF); + rtc->data->clear_isr(rtc); + val = rtc->data->read_rtc_reg(rtc, reg_irq); + /* disable all the interrupts for alarm*/ + rtc_delayed_write(0, rtc, reg_irq); /* Ack the event */ - rtc_delayed_write(RTC_STATUS_ALARM1, rtc, RTC_STATUS); + rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS); spin_unlock(&rtc->lock); - if (val & RTC_IRQ1_FREQ_EN) { - if (val & RTC_IRQ1_FREQ_1HZ) + if (val & RTC_IRQ_FREQ_EN) { + if (val & RTC_IRQ_FREQ_1HZ) event |= RTC_UF; else event |= RTC_PF; @@ -202,7 +343,7 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) return IRQ_HANDLED; } -static struct rtc_class_ops armada38x_rtc_ops = { +static const struct rtc_class_ops armada38x_rtc_ops = { .read_time = armada38x_rtc_read_time, .set_time = armada38x_rtc_set_time, .read_alarm = armada38x_rtc_read_alarm, @@ -210,17 +351,65 @@ static struct rtc_class_ops armada38x_rtc_ops = { .alarm_irq_enable = armada38x_rtc_alarm_irq_enable, }; +static const struct rtc_class_ops armada38x_rtc_ops_noirq = { + .read_time = armada38x_rtc_read_time, + .set_time = armada38x_rtc_set_time, + .read_alarm = armada38x_rtc_read_alarm, +}; + +static const struct armada38x_rtc_data armada38x_data = { + .update_mbus_timing = rtc_update_38x_mbus_timing_params, + .read_rtc_reg = read_rtc_register_38x_wa, + .clear_isr = armada38x_clear_isr, + .unmask_interrupt = armada38x_unmask_interrupt, + .alarm = ALARM1, +}; + +static const struct armada38x_rtc_data armada8k_data = { + .update_mbus_timing = rtc_update_8k_mbus_timing_params, + .read_rtc_reg = read_rtc_register, + .clear_isr = armada8k_clear_isr, + .unmask_interrupt = armada8k_unmask_interrupt, + .alarm = ALARM2, +}; + +#ifdef CONFIG_OF +static const struct of_device_id armada38x_rtc_of_match_table[] = { + { + .compatible = "marvell,armada-380-rtc", + .data = &armada38x_data, + }, + { + .compatible = "marvell,armada-8k-rtc", + .data = &armada8k_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); +#endif + static __init int armada38x_rtc_probe(struct platform_device *pdev) { + const struct rtc_class_ops *ops; struct resource *res; struct armada38x_rtc *rtc; + const struct of_device_id *match; int ret; + match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev); + if (!match) + return -ENODEV; + rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; + rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR, + sizeof(struct value_to_freq), GFP_KERNEL); + if (!rtc->val_to_freq) + return -ENOMEM; + spin_lock_init(&rtc->lock); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc"); @@ -242,19 +431,27 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev) 0, pdev->name, rtc) < 0) { dev_warn(&pdev->dev, "Interrupt not available.\n"); rtc->irq = -1; + } + platform_set_drvdata(pdev, rtc); + + if (rtc->irq != -1) { + device_init_wakeup(&pdev->dev, 1); + ops = &armada38x_rtc_ops; + } else { /* * If there is no interrupt available then we can't * use the alarm */ - armada38x_rtc_ops.set_alarm = NULL; - armada38x_rtc_ops.alarm_irq_enable = NULL; + ops = &armada38x_rtc_ops_noirq; } - platform_set_drvdata(pdev, rtc); - if (rtc->irq != -1) - device_init_wakeup(&pdev->dev, 1); + rtc->data = (struct armada38x_rtc_data *)match->data; + + + /* Update RTC-MBUS bridge timing parameters */ + rtc->data->update_mbus_timing(rtc); rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, - &armada38x_rtc_ops, THIS_MODULE); + ops, THIS_MODULE); if (IS_ERR(rtc->rtc_dev)) { ret = PTR_ERR(rtc->rtc_dev); dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); @@ -280,6 +477,9 @@ static int armada38x_rtc_resume(struct device *dev) if (device_may_wakeup(dev)) { struct armada38x_rtc *rtc = dev_get_drvdata(dev); + /* Update RTC-MBUS bridge timing parameters */ + rtc->data->update_mbus_timing(rtc); + return disable_irq_wake(rtc->irq); } @@ -290,14 +490,6 @@ static int armada38x_rtc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops, armada38x_rtc_suspend, armada38x_rtc_resume); -#ifdef CONFIG_OF -static const struct of_device_id armada38x_rtc_of_match_table[] = { - { .compatible = "marvell,armada-380-rtc", }, - {} -}; -MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); -#endif - static struct platform_driver armada38x_rtc_driver = { .driver = { .name = "armada38x-rtc", diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c index 84d6e026784d..2ba44ccb9c3a 100644 --- a/drivers/rtc/rtc-au1xxx.c +++ b/drivers/rtc/rtc-au1xxx.c @@ -56,7 +56,7 @@ static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm) return 0; } -static struct rtc_class_ops au1xtoy_rtc_ops = { +static const struct rtc_class_ops au1xtoy_rtc_ops = { .read_time = au1xtoy_rtc_read_time, .set_time = au1xtoy_rtc_set_time, }; diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index 535a5f9338d0..15344b7c07c5 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -333,7 +333,7 @@ static int bfin_rtc_proc(struct device *dev, struct seq_file *seq) #undef yesno } -static struct rtc_class_ops bfin_rtc_ops = { +static const struct rtc_class_ops bfin_rtc_ops = { .read_time = bfin_rtc_read_time, .set_time = bfin_rtc_set_time, .read_alarm = bfin_rtc_read_alarm, diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index 397742446007..2b223935001f 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -34,6 +34,7 @@ #define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */ #define BQ32K_TCH2 0x08 /* Trickle charge enable */ #define BQ32K_CFG2 0x09 /* Trickle charger control */ +#define BQ32K_TCFE BIT(6) /* Trickle charge FET bypass */ struct bq32k_regs { uint8_t seconds; @@ -188,6 +189,65 @@ static int trickle_charger_of_init(struct device *dev, struct device_node *node) return 0; } +static ssize_t bq32k_sysfs_show_tricklecharge_bypass(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int reg, error; + + error = bq32k_read(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + return sprintf(buf, "%d\n", (reg & BQ32K_TCFE) ? 1 : 0); +} + +static ssize_t bq32k_sysfs_store_tricklecharge_bypass(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int reg, enable, error; + + if (kstrtoint(buf, 0, &enable)) + return -EINVAL; + + error = bq32k_read(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + if (enable) { + reg |= BQ32K_TCFE; + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + dev_info(dev, "Enabled trickle charge FET bypass.\n"); + } else { + reg &= ~BQ32K_TCFE; + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + dev_info(dev, "Disabled trickle charge FET bypass.\n"); + } + + return count; +} + +static DEVICE_ATTR(trickle_charge_bypass, 0644, + bq32k_sysfs_show_tricklecharge_bypass, + bq32k_sysfs_store_tricklecharge_bypass); + +static int bq32k_sysfs_register(struct device *dev) +{ + return device_create_file(dev, &dev_attr_trickle_charge_bypass); +} + +static void bq32k_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_trickle_charge_bypass); +} + static int bq32k_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -224,11 +284,26 @@ static int bq32k_probe(struct i2c_client *client, if (IS_ERR(rtc)) return PTR_ERR(rtc); + error = bq32k_sysfs_register(&client->dev); + if (error) { + dev_err(&client->dev, + "Unable to create sysfs entries for rtc bq32000\n"); + return error; + } + + i2c_set_clientdata(client, rtc); return 0; } +static int bq32k_remove(struct i2c_client *client) +{ + bq32k_sysfs_unregister(&client->dev); + + return 0; +} + static const struct i2c_device_id bq32k_id[] = { { "bq32000", 0 }, { } @@ -240,6 +315,7 @@ static struct i2c_driver bq32k_driver = { .name = "bq32k", }, .probe = bq32k_probe, + .remove = bq32k_remove, .id_table = bq32k_id, }; diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index a6d9434addf6..6dc8f29697ab 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -15,7 +15,7 @@ #include <linux/module.h> #include <linux/rtc.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include "rtc-core.h" static dev_t rtc_devt; diff --git a/drivers/rtc/rtc-dm355evm.c b/drivers/rtc/rtc-dm355evm.c index 94067f8eeb10..f225cd873ff6 100644 --- a/drivers/rtc/rtc-dm355evm.c +++ b/drivers/rtc/rtc-dm355evm.c @@ -116,7 +116,7 @@ static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm) return 0; } -static struct rtc_class_ops dm355evm_rtc_ops = { +static const struct rtc_class_ops dm355evm_rtc_ops = { .read_time = dm355evm_rtc_read_time, .set_time = dm355evm_rtc_set_time, }; diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index b1f20d8c358f..9bb39a06b994 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -23,28 +23,28 @@ #include <linux/slab.h> #include <linux/regmap.h> -#define DS3232_REG_SECONDS 0x00 -#define DS3232_REG_MINUTES 0x01 -#define DS3232_REG_HOURS 0x02 -#define DS3232_REG_AMPM 0x02 -#define DS3232_REG_DAY 0x03 -#define DS3232_REG_DATE 0x04 -#define DS3232_REG_MONTH 0x05 -#define DS3232_REG_CENTURY 0x05 -#define DS3232_REG_YEAR 0x06 -#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */ -#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */ -#define DS3232_REG_CR 0x0E /* Control register */ -# define DS3232_REG_CR_nEOSC 0x80 -# define DS3232_REG_CR_INTCN 0x04 -# define DS3232_REG_CR_A2IE 0x02 -# define DS3232_REG_CR_A1IE 0x01 - -#define DS3232_REG_SR 0x0F /* control/status register */ -# define DS3232_REG_SR_OSF 0x80 -# define DS3232_REG_SR_BSY 0x04 -# define DS3232_REG_SR_A2F 0x02 -# define DS3232_REG_SR_A1F 0x01 +#define DS3232_REG_SECONDS 0x00 +#define DS3232_REG_MINUTES 0x01 +#define DS3232_REG_HOURS 0x02 +#define DS3232_REG_AMPM 0x02 +#define DS3232_REG_DAY 0x03 +#define DS3232_REG_DATE 0x04 +#define DS3232_REG_MONTH 0x05 +#define DS3232_REG_CENTURY 0x05 +#define DS3232_REG_YEAR 0x06 +#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */ +#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */ +#define DS3232_REG_CR 0x0E /* Control register */ +# define DS3232_REG_CR_nEOSC 0x80 +# define DS3232_REG_CR_INTCN 0x04 +# define DS3232_REG_CR_A2IE 0x02 +# define DS3232_REG_CR_A1IE 0x01 + +#define DS3232_REG_SR 0x0F /* control/status register */ +# define DS3232_REG_SR_OSF 0x80 +# define DS3232_REG_SR_BSY 0x04 +# define DS3232_REG_SR_A2F 0x02 +# define DS3232_REG_SR_A1F 0x01 struct ds3232 { struct device *dev; @@ -363,6 +363,9 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq, if (ret) return ret; + if (ds3232->irq > 0) + device_init_wakeup(dev, 1); + ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops, THIS_MODULE); if (IS_ERR(ds3232->rtc)) @@ -374,10 +377,10 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq, IRQF_SHARED | IRQF_ONESHOT, name, dev); if (ret) { + device_set_wakeup_capable(dev, 0); ds3232->irq = 0; dev_err(dev, "unable to request IRQ\n"); - } else - device_init_wakeup(dev, 1); + } } return 0; @@ -420,6 +423,7 @@ static int ds3232_i2c_probe(struct i2c_client *client, static const struct regmap_config config = { .reg_bits = 8, .val_bits = 8, + .max_register = 0x13, }; regmap = devm_regmap_init_i2c(client, &config); @@ -479,6 +483,7 @@ static int ds3234_probe(struct spi_device *spi) static const struct regmap_config config = { .reg_bits = 8, .val_bits = 8, + .max_register = 0x13, .write_flag_mask = 0x80, }; struct regmap *regmap; diff --git a/drivers/rtc/rtc-gemini.c b/drivers/rtc/rtc-gemini.c index 688debc14348..ccf0dbadb62d 100644 --- a/drivers/rtc/rtc-gemini.c +++ b/drivers/rtc/rtc-gemini.c @@ -159,9 +159,16 @@ static int gemini_rtc_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id gemini_rtc_dt_match[] = { + { .compatible = "cortina,gemini-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gemini_rtc_dt_match); + static struct platform_driver gemini_rtc_driver = { .driver = { .name = DRV_NAME, + .of_match_table = gemini_rtc_dt_match, }, .probe = gemini_rtc_probe, .remove = gemini_rtc_remove, diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index 67b56b80dc70..6b54f6c24c5f 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c @@ -108,7 +108,6 @@ * @pdev: pionter to platform dev * @rtc: pointer to rtc struct * @ioaddr: IO registers pointer - * @irq: dryice normal interrupt * @clk: input reference clock * @dsr: copy of the DSR register * @irq_lock: interrupt enable register (DIER) lock @@ -120,7 +119,6 @@ struct imxdi_dev { struct platform_device *pdev; struct rtc_device *rtc; void __iomem *ioaddr; - int irq; struct clk *clk; u32 dsr; spinlock_t irq_lock; @@ -668,7 +666,7 @@ static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) return 0; } -static struct rtc_class_ops dryice_rtc_ops = { +static const struct rtc_class_ops dryice_rtc_ops = { .read_time = dryice_rtc_read_time, .set_mmss = dryice_rtc_set_mmss, .alarm_irq_enable = dryice_rtc_alarm_irq_enable, @@ -677,9 +675,9 @@ static struct rtc_class_ops dryice_rtc_ops = { }; /* - * dryice "normal" interrupt handler + * interrupt handler for dryice "normal" and security violation interrupt */ -static irqreturn_t dryice_norm_irq(int irq, void *dev_id) +static irqreturn_t dryice_irq(int irq, void *dev_id) { struct imxdi_dev *imxdi = dev_id; u32 dsr, dier; @@ -765,6 +763,7 @@ static int __init dryice_rtc_probe(struct platform_device *pdev) { struct resource *res; struct imxdi_dev *imxdi; + int norm_irq, sec_irq; int rc; imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL); @@ -780,9 +779,16 @@ static int __init dryice_rtc_probe(struct platform_device *pdev) spin_lock_init(&imxdi->irq_lock); - imxdi->irq = platform_get_irq(pdev, 0); - if (imxdi->irq < 0) - return imxdi->irq; + norm_irq = platform_get_irq(pdev, 0); + if (norm_irq < 0) + return norm_irq; + + /* the 2nd irq is the security violation irq + * make this optional, don't break the device tree ABI + */ + sec_irq = platform_get_irq(pdev, 1); + if (sec_irq <= 0) + sec_irq = IRQ_NOTCONNECTED; init_waitqueue_head(&imxdi->write_wait); @@ -808,13 +814,20 @@ static int __init dryice_rtc_probe(struct platform_device *pdev) if (rc != 0) goto err; - rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq, - IRQF_SHARED, pdev->name, imxdi); + rc = devm_request_irq(&pdev->dev, norm_irq, dryice_irq, + IRQF_SHARED, pdev->name, imxdi); if (rc) { dev_warn(&pdev->dev, "interrupt not available.\n"); goto err; } + rc = devm_request_irq(&pdev->dev, sec_irq, dryice_irq, + IRQF_SHARED, pdev->name, imxdi); + if (rc) { + dev_warn(&pdev->dev, "security violation interrupt not available.\n"); + /* this is not an error, see above */ + } + platform_set_drvdata(pdev, imxdi); imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &dryice_rtc_ops, THIS_MODULE); diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c index 22a9ec4f2b83..e04ca54f21e2 100644 --- a/drivers/rtc/rtc-ls1x.c +++ b/drivers/rtc/rtc-ls1x.c @@ -138,7 +138,7 @@ err: return ret; } -static struct rtc_class_ops ls1x_rtc_ops = { +static const struct rtc_class_ops ls1x_rtc_ops = { .read_time = ls1x_rtc_read_time, .set_time = ls1x_rtc_set_time, }; diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 0eeb5714c00f..02af045305dd 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -16,62 +16,88 @@ #include <linux/module.h> #include <linux/rtc.h> #include <linux/platform_device.h> -#include <linux/platform_data/rtc-m48t86.h> #include <linux/bcd.h> +#include <linux/io.h> -#define M48T86_REG_SEC 0x00 -#define M48T86_REG_SECALRM 0x01 -#define M48T86_REG_MIN 0x02 -#define M48T86_REG_MINALRM 0x03 -#define M48T86_REG_HOUR 0x04 -#define M48T86_REG_HOURALRM 0x05 -#define M48T86_REG_DOW 0x06 /* 1 = sunday */ -#define M48T86_REG_DOM 0x07 -#define M48T86_REG_MONTH 0x08 /* 1 - 12 */ -#define M48T86_REG_YEAR 0x09 /* 0 - 99 */ -#define M48T86_REG_A 0x0A -#define M48T86_REG_B 0x0B -#define M48T86_REG_C 0x0C -#define M48T86_REG_D 0x0D - -#define M48T86_REG_B_H24 (1 << 1) -#define M48T86_REG_B_DM (1 << 2) -#define M48T86_REG_B_SET (1 << 7) -#define M48T86_REG_D_VRT (1 << 7) +#define M48T86_SEC 0x00 +#define M48T86_SECALRM 0x01 +#define M48T86_MIN 0x02 +#define M48T86_MINALRM 0x03 +#define M48T86_HOUR 0x04 +#define M48T86_HOURALRM 0x05 +#define M48T86_DOW 0x06 /* 1 = sunday */ +#define M48T86_DOM 0x07 +#define M48T86_MONTH 0x08 /* 1 - 12 */ +#define M48T86_YEAR 0x09 /* 0 - 99 */ +#define M48T86_A 0x0a +#define M48T86_B 0x0b +#define M48T86_B_SET BIT(7) +#define M48T86_B_DM BIT(2) +#define M48T86_B_H24 BIT(1) +#define M48T86_C 0x0c +#define M48T86_D 0x0d +#define M48T86_D_VRT BIT(7) +#define M48T86_NVRAM(x) (0x0e + (x)) +#define M48T86_NVRAM_LEN 114 + +struct m48t86_rtc_info { + void __iomem *index_reg; + void __iomem *data_reg; + struct rtc_device *rtc; +}; + +static unsigned char m48t86_readb(struct device *dev, unsigned long addr) +{ + struct m48t86_rtc_info *info = dev_get_drvdata(dev); + unsigned char value; + + writeb(addr, info->index_reg); + value = readb(info->data_reg); + + return value; +} + +static void m48t86_writeb(struct device *dev, + unsigned char value, unsigned long addr) +{ + struct m48t86_rtc_info *info = dev_get_drvdata(dev); + + writeb(addr, info->index_reg); + writeb(value, info->data_reg); +} static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) { unsigned char reg; - struct platform_device *pdev = to_platform_device(dev); - struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); - reg = ops->readbyte(M48T86_REG_B); + reg = m48t86_readb(dev, M48T86_B); - if (reg & M48T86_REG_B_DM) { + if (reg & M48T86_B_DM) { /* data (binary) mode */ - tm->tm_sec = ops->readbyte(M48T86_REG_SEC); - tm->tm_min = ops->readbyte(M48T86_REG_MIN); - tm->tm_hour = ops->readbyte(M48T86_REG_HOUR) & 0x3F; - tm->tm_mday = ops->readbyte(M48T86_REG_DOM); + tm->tm_sec = m48t86_readb(dev, M48T86_SEC); + tm->tm_min = m48t86_readb(dev, M48T86_MIN); + tm->tm_hour = m48t86_readb(dev, M48T86_HOUR) & 0x3f; + tm->tm_mday = m48t86_readb(dev, M48T86_DOM); /* tm_mon is 0-11 */ - tm->tm_mon = ops->readbyte(M48T86_REG_MONTH) - 1; - tm->tm_year = ops->readbyte(M48T86_REG_YEAR) + 100; - tm->tm_wday = ops->readbyte(M48T86_REG_DOW); + tm->tm_mon = m48t86_readb(dev, M48T86_MONTH) - 1; + tm->tm_year = m48t86_readb(dev, M48T86_YEAR) + 100; + tm->tm_wday = m48t86_readb(dev, M48T86_DOW); } else { /* bcd mode */ - tm->tm_sec = bcd2bin(ops->readbyte(M48T86_REG_SEC)); - tm->tm_min = bcd2bin(ops->readbyte(M48T86_REG_MIN)); - tm->tm_hour = bcd2bin(ops->readbyte(M48T86_REG_HOUR) & 0x3F); - tm->tm_mday = bcd2bin(ops->readbyte(M48T86_REG_DOM)); + tm->tm_sec = bcd2bin(m48t86_readb(dev, M48T86_SEC)); + tm->tm_min = bcd2bin(m48t86_readb(dev, M48T86_MIN)); + tm->tm_hour = bcd2bin(m48t86_readb(dev, M48T86_HOUR) & + 0x3f); + tm->tm_mday = bcd2bin(m48t86_readb(dev, M48T86_DOM)); /* tm_mon is 0-11 */ - tm->tm_mon = bcd2bin(ops->readbyte(M48T86_REG_MONTH)) - 1; - tm->tm_year = bcd2bin(ops->readbyte(M48T86_REG_YEAR)) + 100; - tm->tm_wday = bcd2bin(ops->readbyte(M48T86_REG_DOW)); + tm->tm_mon = bcd2bin(m48t86_readb(dev, M48T86_MONTH)) - 1; + tm->tm_year = bcd2bin(m48t86_readb(dev, M48T86_YEAR)) + 100; + tm->tm_wday = bcd2bin(m48t86_readb(dev, M48T86_DOW)); } /* correct the hour if the clock is in 12h mode */ - if (!(reg & M48T86_REG_B_H24)) - if (ops->readbyte(M48T86_REG_HOUR) & 0x80) + if (!(reg & M48T86_B_H24)) + if (m48t86_readb(dev, M48T86_HOUR) & 0x80) tm->tm_hour += 12; return rtc_valid_tm(tm); @@ -80,38 +106,36 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) { unsigned char reg; - struct platform_device *pdev = to_platform_device(dev); - struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); - reg = ops->readbyte(M48T86_REG_B); + reg = m48t86_readb(dev, M48T86_B); /* update flag and 24h mode */ - reg |= M48T86_REG_B_SET | M48T86_REG_B_H24; - ops->writebyte(reg, M48T86_REG_B); + reg |= M48T86_B_SET | M48T86_B_H24; + m48t86_writeb(dev, reg, M48T86_B); - if (reg & M48T86_REG_B_DM) { + if (reg & M48T86_B_DM) { /* data (binary) mode */ - ops->writebyte(tm->tm_sec, M48T86_REG_SEC); - ops->writebyte(tm->tm_min, M48T86_REG_MIN); - ops->writebyte(tm->tm_hour, M48T86_REG_HOUR); - ops->writebyte(tm->tm_mday, M48T86_REG_DOM); - ops->writebyte(tm->tm_mon + 1, M48T86_REG_MONTH); - ops->writebyte(tm->tm_year % 100, M48T86_REG_YEAR); - ops->writebyte(tm->tm_wday, M48T86_REG_DOW); + m48t86_writeb(dev, tm->tm_sec, M48T86_SEC); + m48t86_writeb(dev, tm->tm_min, M48T86_MIN); + m48t86_writeb(dev, tm->tm_hour, M48T86_HOUR); + m48t86_writeb(dev, tm->tm_mday, M48T86_DOM); + m48t86_writeb(dev, tm->tm_mon + 1, M48T86_MONTH); + m48t86_writeb(dev, tm->tm_year % 100, M48T86_YEAR); + m48t86_writeb(dev, tm->tm_wday, M48T86_DOW); } else { /* bcd mode */ - ops->writebyte(bin2bcd(tm->tm_sec), M48T86_REG_SEC); - ops->writebyte(bin2bcd(tm->tm_min), M48T86_REG_MIN); - ops->writebyte(bin2bcd(tm->tm_hour), M48T86_REG_HOUR); - ops->writebyte(bin2bcd(tm->tm_mday), M48T86_REG_DOM); - ops->writebyte(bin2bcd(tm->tm_mon + 1), M48T86_REG_MONTH); - ops->writebyte(bin2bcd(tm->tm_year % 100), M48T86_REG_YEAR); - ops->writebyte(bin2bcd(tm->tm_wday), M48T86_REG_DOW); + m48t86_writeb(dev, bin2bcd(tm->tm_sec), M48T86_SEC); + m48t86_writeb(dev, bin2bcd(tm->tm_min), M48T86_MIN); + m48t86_writeb(dev, bin2bcd(tm->tm_hour), M48T86_HOUR); + m48t86_writeb(dev, bin2bcd(tm->tm_mday), M48T86_DOM); + m48t86_writeb(dev, bin2bcd(tm->tm_mon + 1), M48T86_MONTH); + m48t86_writeb(dev, bin2bcd(tm->tm_year % 100), M48T86_YEAR); + m48t86_writeb(dev, bin2bcd(tm->tm_wday), M48T86_DOW); } /* update ended */ - reg &= ~M48T86_REG_B_SET; - ops->writebyte(reg, M48T86_REG_B); + reg &= ~M48T86_B_SET; + m48t86_writeb(dev, reg, M48T86_B); return 0; } @@ -119,18 +143,16 @@ static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) { unsigned char reg; - struct platform_device *pdev = to_platform_device(dev); - struct m48t86_ops *ops = dev_get_platdata(&pdev->dev); - reg = ops->readbyte(M48T86_REG_B); + reg = m48t86_readb(dev, M48T86_B); seq_printf(seq, "mode\t\t: %s\n", - (reg & M48T86_REG_B_DM) ? "binary" : "bcd"); + (reg & M48T86_B_DM) ? "binary" : "bcd"); - reg = ops->readbyte(M48T86_REG_D); + reg = m48t86_readb(dev, M48T86_D); seq_printf(seq, "battery\t\t: %s\n", - (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); + (reg & M48T86_D_VRT) ? "ok" : "exhausted"); return 0; } @@ -141,25 +163,116 @@ static const struct rtc_class_ops m48t86_rtc_ops = { .proc = m48t86_rtc_proc, }; -static int m48t86_rtc_probe(struct platform_device *dev) +static ssize_t m48t86_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + unsigned int i; + + for (i = 0; i < count; i++) + buf[i] = m48t86_readb(dev, M48T86_NVRAM(off + i)); + + return count; +} + +static ssize_t m48t86_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { + struct device *dev = kobj_to_dev(kobj); + unsigned int i; + + for (i = 0; i < count; i++) + m48t86_writeb(dev, buf[i], M48T86_NVRAM(off + i)); + + return count; +} + +static BIN_ATTR(nvram, 0644, m48t86_nvram_read, m48t86_nvram_write, + M48T86_NVRAM_LEN); + +/* + * The RTC is an optional feature at purchase time on some Technologic Systems + * boards. Verify that it actually exists by checking if the last two bytes + * of the NVRAM can be changed. + * + * This is based on the method used in their rtc7800.c example. + */ +static bool m48t86_verify_chip(struct platform_device *pdev) +{ + unsigned int offset0 = M48T86_NVRAM(M48T86_NVRAM_LEN - 2); + unsigned int offset1 = M48T86_NVRAM(M48T86_NVRAM_LEN - 1); + unsigned char tmp0, tmp1; + + tmp0 = m48t86_readb(&pdev->dev, offset0); + tmp1 = m48t86_readb(&pdev->dev, offset1); + + m48t86_writeb(&pdev->dev, 0x00, offset0); + m48t86_writeb(&pdev->dev, 0x55, offset1); + if (m48t86_readb(&pdev->dev, offset1) == 0x55) { + m48t86_writeb(&pdev->dev, 0xaa, offset1); + if (m48t86_readb(&pdev->dev, offset1) == 0xaa && + m48t86_readb(&pdev->dev, offset0) == 0x00) { + m48t86_writeb(&pdev->dev, tmp0, offset0); + m48t86_writeb(&pdev->dev, tmp1, offset1); + + return true; + } + } + return false; +} + +static int m48t86_rtc_probe(struct platform_device *pdev) +{ + struct m48t86_rtc_info *info; + struct resource *res; unsigned char reg; - struct m48t86_ops *ops = dev_get_platdata(&dev->dev); - struct rtc_device *rtc; - rtc = devm_rtc_device_register(&dev->dev, "m48t86", - &m48t86_rtc_ops, THIS_MODULE); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + info->index_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->index_reg)) + return PTR_ERR(info->index_reg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + info->data_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->data_reg)) + return PTR_ERR(info->data_reg); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); + dev_set_drvdata(&pdev->dev, info); + + if (!m48t86_verify_chip(pdev)) { + dev_info(&pdev->dev, "RTC not present\n"); + return -ENODEV; + } - platform_set_drvdata(dev, rtc); + info->rtc = devm_rtc_device_register(&pdev->dev, "m48t86", + &m48t86_rtc_ops, THIS_MODULE); + if (IS_ERR(info->rtc)) + return PTR_ERR(info->rtc); /* read battery status */ - reg = ops->readbyte(M48T86_REG_D); - dev_info(&dev->dev, "battery %s\n", - (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted"); + reg = m48t86_readb(&pdev->dev, M48T86_D); + dev_info(&pdev->dev, "battery %s\n", + (reg & M48T86_D_VRT) ? "ok" : "exhausted"); + if (device_create_bin_file(&pdev->dev, &bin_attr_nvram)) + dev_err(&pdev->dev, "failed to create nvram sysfs entry\n"); + + return 0; +} + +static int m48t86_rtc_remove(struct platform_device *pdev) +{ + device_remove_bin_file(&pdev->dev, &bin_attr_nvram); return 0; } @@ -168,6 +281,7 @@ static struct platform_driver m48t86_rtc_platform_driver = { .name = "rtc-m48t86", }, .probe = m48t86_rtc_probe, + .remove = m48t86_rtc_remove, }; module_platform_driver(m48t86_rtc_platform_driver); diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c index ce75e421ba00..77f21331ae21 100644 --- a/drivers/rtc/rtc-mcp795.c +++ b/drivers/rtc/rtc-mcp795.c @@ -44,12 +44,22 @@ #define MCP795_REG_DAY 0x04 #define MCP795_REG_MONTH 0x06 #define MCP795_REG_CONTROL 0x08 +#define MCP795_REG_ALM0_SECONDS 0x0C +#define MCP795_REG_ALM0_DAY 0x0F #define MCP795_ST_BIT BIT(7) #define MCP795_24_BIT BIT(6) #define MCP795_LP_BIT BIT(5) #define MCP795_EXTOSC_BIT BIT(3) #define MCP795_OSCON_BIT BIT(5) +#define MCP795_ALM0_BIT BIT(4) +#define MCP795_ALM1_BIT BIT(5) +#define MCP795_ALM0IF_BIT BIT(3) +#define MCP795_ALM0C0_BIT BIT(4) +#define MCP795_ALM0C1_BIT BIT(5) +#define MCP795_ALM0C2_BIT BIT(6) + +#define SEC_PER_DAY (24 * 60 * 60) static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count) { @@ -150,6 +160,30 @@ static int mcp795_start_oscillator(struct device *dev, bool *extosc) dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT); } +/* Enable or disable Alarm 0 in RTC */ +static int mcp795_update_alarm(struct device *dev, bool enable) +{ + int ret; + + dev_dbg(dev, "%s alarm\n", enable ? "Enable" : "Disable"); + + if (enable) { + /* clear ALM0IF (Alarm 0 Interrupt Flag) bit */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_ALM0_DAY, + MCP795_ALM0IF_BIT, 0); + if (ret) + return ret; + /* enable alarm 0 */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL, + MCP795_ALM0_BIT, MCP795_ALM0_BIT); + } else { + /* disable alarm 0 and alarm 1 */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL, + MCP795_ALM0_BIT | MCP795_ALM1_BIT, 0); + } + return ret; +} + static int mcp795_set_time(struct device *dev, struct rtc_time *tim) { int ret; @@ -170,6 +204,7 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim) data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec); data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min); data[2] = bin2bcd(tim->tm_hour); + data[3] = (data[3] & 0xF8) | bin2bcd(tim->tm_wday + 1); data[4] = bin2bcd(tim->tm_mday); data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1); @@ -198,9 +233,9 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim) if (ret) return ret; - dev_dbg(dev, "Set mcp795: %04d-%02d-%02d %02d:%02d:%02d\n", + dev_dbg(dev, "Set mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n", tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, - tim->tm_hour, tim->tm_min, tim->tm_sec); + tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec); return 0; } @@ -218,20 +253,139 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim) tim->tm_sec = bcd2bin(data[0] & 0x7F); tim->tm_min = bcd2bin(data[1] & 0x7F); tim->tm_hour = bcd2bin(data[2] & 0x3F); + tim->tm_wday = bcd2bin(data[3] & 0x07) - 1; tim->tm_mday = bcd2bin(data[4] & 0x3F); tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1; tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */ - dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n", - tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, - tim->tm_hour, tim->tm_min, tim->tm_sec); + dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n", + tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, + tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec); return rtc_valid_tm(tim); } +static int mcp795_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_time now_tm; + time64_t now; + time64_t later; + u8 tmp[6]; + int ret; + + /* Read current time from RTC hardware */ + ret = mcp795_read_time(dev, &now_tm); + if (ret) + return ret; + /* Get the number of seconds since 1970 */ + now = rtc_tm_to_time64(&now_tm); + later = rtc_tm_to_time64(&alm->time); + if (later <= now) + return -EINVAL; + /* make sure alarm fires within the next one year */ + if ((later - now) >= + (SEC_PER_DAY * (365 + is_leap_year(alm->time.tm_year)))) + return -EDOM; + /* disable alarm */ + ret = mcp795_update_alarm(dev, false); + if (ret) + return ret; + /* Read registers, so we can leave configuration bits untouched */ + ret = mcp795_rtcc_read(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp)); + if (ret) + return ret; + + alm->time.tm_year = -1; + alm->time.tm_isdst = -1; + alm->time.tm_yday = -1; + + tmp[0] = (tmp[0] & 0x80) | bin2bcd(alm->time.tm_sec); + tmp[1] = (tmp[1] & 0x80) | bin2bcd(alm->time.tm_min); + tmp[2] = (tmp[2] & 0xE0) | bin2bcd(alm->time.tm_hour); + tmp[3] = (tmp[3] & 0x80) | bin2bcd(alm->time.tm_wday + 1); + /* set alarm match: seconds, minutes, hour, day, date and month */ + tmp[3] |= (MCP795_ALM0C2_BIT | MCP795_ALM0C1_BIT | MCP795_ALM0C0_BIT); + tmp[4] = (tmp[4] & 0xC0) | bin2bcd(alm->time.tm_mday); + tmp[5] = (tmp[5] & 0xE0) | bin2bcd(alm->time.tm_mon + 1); + + ret = mcp795_rtcc_write(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp)); + if (ret) + return ret; + + /* enable alarm if requested */ + if (alm->enabled) { + ret = mcp795_update_alarm(dev, true); + if (ret) + return ret; + dev_dbg(dev, "Alarm IRQ armed\n"); + } + dev_dbg(dev, "Set alarm: %02d-%02d(%d) %02d:%02d:%02d\n", + alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday, + alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec); + return 0; +} + +static int mcp795_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u8 data[6]; + int ret; + + ret = mcp795_rtcc_read( + dev, MCP795_REG_ALM0_SECONDS, data, sizeof(data)); + if (ret) + return ret; + + alm->time.tm_sec = bcd2bin(data[0] & 0x7F); + alm->time.tm_min = bcd2bin(data[1] & 0x7F); + alm->time.tm_hour = bcd2bin(data[2] & 0x1F); + alm->time.tm_wday = bcd2bin(data[3] & 0x07) - 1; + alm->time.tm_mday = bcd2bin(data[4] & 0x3F); + alm->time.tm_mon = bcd2bin(data[5] & 0x1F) - 1; + alm->time.tm_year = -1; + alm->time.tm_isdst = -1; + alm->time.tm_yday = -1; + + dev_dbg(dev, "Read alarm: %02d-%02d(%d) %02d:%02d:%02d\n", + alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday, + alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec); + return 0; +} + +static int mcp795_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + return mcp795_update_alarm(dev, !!enabled); +} + +static irqreturn_t mcp795_irq(int irq, void *data) +{ + struct spi_device *spi = data; + struct rtc_device *rtc = spi_get_drvdata(spi); + struct mutex *lock = &rtc->ops_lock; + int ret; + + mutex_lock(lock); + + /* Disable alarm. + * There is no need to clear ALM0IF (Alarm 0 Interrupt Flag) bit, + * because it is done every time when alarm is enabled. + */ + ret = mcp795_update_alarm(&spi->dev, false); + if (ret) + dev_err(&spi->dev, + "Failed to disable alarm in IRQ (ret=%d)\n", ret); + rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF); + + mutex_unlock(lock); + + return IRQ_HANDLED; +} + static const struct rtc_class_ops mcp795_rtc_ops = { .read_time = mcp795_read_time, - .set_time = mcp795_set_time + .set_time = mcp795_set_time, + .read_alarm = mcp795_read_alarm, + .set_alarm = mcp795_set_alarm, + .alarm_irq_enable = mcp795_alarm_irq_enable }; static int mcp795_probe(struct spi_device *spi) @@ -259,6 +413,23 @@ static int mcp795_probe(struct spi_device *spi) spi_set_drvdata(spi, rtc); + if (spi->irq > 0) { + dev_dbg(&spi->dev, "Alarm support enabled\n"); + + /* Clear any pending alarm (ALM0IF bit) before requesting + * the interrupt. + */ + mcp795_rtcc_set_bits(&spi->dev, MCP795_REG_ALM0_DAY, + MCP795_ALM0IF_BIT, 0); + ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + mcp795_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&rtc->dev), spi); + if (ret) + dev_err(&spi->dev, "Failed to request IRQ: %d: %d\n", + spi->irq, ret); + else + device_init_wakeup(&spi->dev, true); + } return 0; } diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 359876a88ac8..77319122642a 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -353,7 +353,7 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) } /* RTC layer */ -static struct rtc_class_ops mxc_rtc_ops = { +static const struct rtc_class_ops mxc_rtc_ops = { .release = mxc_rtc_release, .read_time = mxc_rtc_read_time, .set_mmss64 = mxc_rtc_set_mmss, diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index 2bfdf638b673..f33447c5db85 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -52,9 +52,20 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) struct pcf2127 *pcf2127 = dev_get_drvdata(dev); unsigned char buf[10]; int ret; + int i; - ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, buf, - sizeof(buf)); + for (i = 0; i <= PCF2127_REG_CTRL3; i++) { + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1 + i, + (unsigned int *)(buf + i)); + if (ret) { + dev_err(dev, "%s: read error\n", __func__); + return ret; + } + } + + ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_SC, + (buf + PCF2127_REG_SC), + ARRAY_SIZE(buf) - PCF2127_REG_SC); if (ret) { dev_err(dev, "%s: read error\n", __func__); return ret; diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c index 7163b91bb773..d08da371912c 100644 --- a/drivers/rtc/rtc-rx8010.c +++ b/drivers/rtc/rtc-rx8010.c @@ -63,7 +63,6 @@ struct rx8010_data { struct i2c_client *client; struct rtc_device *rtc; u8 ctrlreg; - spinlock_t flags_lock; }; static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id) @@ -72,12 +71,12 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id) struct rx8010_data *rx8010 = i2c_get_clientdata(client); int flagreg; - spin_lock(&rx8010->flags_lock); + mutex_lock(&rx8010->rtc->ops_lock); flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG); if (flagreg <= 0) { - spin_unlock(&rx8010->flags_lock); + mutex_unlock(&rx8010->rtc->ops_lock); return IRQ_NONE; } @@ -101,7 +100,7 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id) i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg); - spin_unlock(&rx8010->flags_lock); + mutex_unlock(&rx8010->rtc->ops_lock); return IRQ_HANDLED; } @@ -143,7 +142,6 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt) u8 date[7]; int ctrl, flagreg; int ret; - unsigned long irqflags; if ((dt->tm_year < 100) || (dt->tm_year > 199)) return -EINVAL; @@ -181,11 +179,8 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt) if (ret < 0) return ret; - spin_lock_irqsave(&rx8010->flags_lock, irqflags); - flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG); if (flagreg < 0) { - spin_unlock_irqrestore(&rx8010->flags_lock, irqflags); return flagreg; } @@ -193,8 +188,6 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt) ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg & ~RX8010_FLAG_VLF); - spin_unlock_irqrestore(&rx8010->flags_lock, irqflags); - return 0; } @@ -288,12 +281,9 @@ static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t) u8 alarmvals[3]; int extreg, flagreg; int err; - unsigned long irqflags; - spin_lock_irqsave(&rx8010->flags_lock, irqflags); flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG); if (flagreg < 0) { - spin_unlock_irqrestore(&rx8010->flags_lock, irqflags); return flagreg; } @@ -302,14 +292,12 @@ static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t) err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL, rx8010->ctrlreg); if (err < 0) { - spin_unlock_irqrestore(&rx8010->flags_lock, irqflags); return err; } } flagreg &= ~RX8010_FLAG_AF; err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg); - spin_unlock_irqrestore(&rx8010->flags_lock, irqflags); if (err < 0) return err; @@ -404,7 +392,6 @@ static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) struct rx8010_data *rx8010 = dev_get_drvdata(dev); int ret, tmp; int flagreg; - unsigned long irqflags; switch (cmd) { case RTC_VL_READ: @@ -419,16 +406,13 @@ static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) return 0; case RTC_VL_CLR: - spin_lock_irqsave(&rx8010->flags_lock, irqflags); flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG); if (flagreg < 0) { - spin_unlock_irqrestore(&rx8010->flags_lock, irqflags); return flagreg; } flagreg &= ~RX8010_FLAG_VLF; ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg); - spin_unlock_irqrestore(&rx8010->flags_lock, irqflags); if (ret < 0) return ret; @@ -466,8 +450,6 @@ static int rx8010_probe(struct i2c_client *client, rx8010->client = client; i2c_set_clientdata(client, rx8010); - spin_lock_init(&rx8010->flags_lock); - err = rx8010_init_client(client); if (err) return err; diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 17b6235d67a5..c626e43a9cbb 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -535,7 +535,7 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) return 0; } -static struct rtc_class_ops sh_rtc_ops = { +static const struct rtc_class_ops sh_rtc_ops = { .read_time = sh_rtc_read_time, .set_time = sh_rtc_set_time, .read_alarm = sh_rtc_read_alarm, diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index 0f11c2a228e3..d51b07d620f7 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -184,6 +184,7 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) rtc_tm_to_time(alrm_tm, &time); regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0); + rtc_write_sync_lp(data); regmap_write(data->regmap, data->offset + SNVS_LPTAR, time); /* Clear alarm interrupt status bit */ diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c new file mode 100644 index 000000000000..bd57eb1029e1 --- /dev/null +++ b/drivers/rtc/rtc-stm32.c @@ -0,0 +1,725 @@ +/* + * Copyright (C) Amelie Delaunay 2016 + * Author: Amelie Delaunay <amelie.delaunay@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/bcd.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> + +#define DRIVER_NAME "stm32_rtc" + +/* STM32 RTC registers */ +#define STM32_RTC_TR 0x00 +#define STM32_RTC_DR 0x04 +#define STM32_RTC_CR 0x08 +#define STM32_RTC_ISR 0x0C +#define STM32_RTC_PRER 0x10 +#define STM32_RTC_ALRMAR 0x1C +#define STM32_RTC_WPR 0x24 + +/* STM32_RTC_TR bit fields */ +#define STM32_RTC_TR_SEC_SHIFT 0 +#define STM32_RTC_TR_SEC GENMASK(6, 0) +#define STM32_RTC_TR_MIN_SHIFT 8 +#define STM32_RTC_TR_MIN GENMASK(14, 8) +#define STM32_RTC_TR_HOUR_SHIFT 16 +#define STM32_RTC_TR_HOUR GENMASK(21, 16) + +/* STM32_RTC_DR bit fields */ +#define STM32_RTC_DR_DATE_SHIFT 0 +#define STM32_RTC_DR_DATE GENMASK(5, 0) +#define STM32_RTC_DR_MONTH_SHIFT 8 +#define STM32_RTC_DR_MONTH GENMASK(12, 8) +#define STM32_RTC_DR_WDAY_SHIFT 13 +#define STM32_RTC_DR_WDAY GENMASK(15, 13) +#define STM32_RTC_DR_YEAR_SHIFT 16 +#define STM32_RTC_DR_YEAR GENMASK(23, 16) + +/* STM32_RTC_CR bit fields */ +#define STM32_RTC_CR_FMT BIT(6) +#define STM32_RTC_CR_ALRAE BIT(8) +#define STM32_RTC_CR_ALRAIE BIT(12) + +/* STM32_RTC_ISR bit fields */ +#define STM32_RTC_ISR_ALRAWF BIT(0) +#define STM32_RTC_ISR_INITS BIT(4) +#define STM32_RTC_ISR_RSF BIT(5) +#define STM32_RTC_ISR_INITF BIT(6) +#define STM32_RTC_ISR_INIT BIT(7) +#define STM32_RTC_ISR_ALRAF BIT(8) + +/* STM32_RTC_PRER bit fields */ +#define STM32_RTC_PRER_PRED_S_SHIFT 0 +#define STM32_RTC_PRER_PRED_S GENMASK(14, 0) +#define STM32_RTC_PRER_PRED_A_SHIFT 16 +#define STM32_RTC_PRER_PRED_A GENMASK(22, 16) + +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */ +#define STM32_RTC_ALRMXR_SEC_SHIFT 0 +#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0) +#define STM32_RTC_ALRMXR_SEC_MASK BIT(7) +#define STM32_RTC_ALRMXR_MIN_SHIFT 8 +#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8) +#define STM32_RTC_ALRMXR_MIN_MASK BIT(15) +#define STM32_RTC_ALRMXR_HOUR_SHIFT 16 +#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16) +#define STM32_RTC_ALRMXR_PM BIT(22) +#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23) +#define STM32_RTC_ALRMXR_DATE_SHIFT 24 +#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24) +#define STM32_RTC_ALRMXR_WDSEL BIT(30) +#define STM32_RTC_ALRMXR_WDAY_SHIFT 24 +#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24) +#define STM32_RTC_ALRMXR_DATE_MASK BIT(31) + +/* STM32_RTC_WPR key constants */ +#define RTC_WPR_1ST_KEY 0xCA +#define RTC_WPR_2ND_KEY 0x53 +#define RTC_WPR_WRONG_KEY 0xFF + +/* + * RTC registers are protected against parasitic write access. + * PWR_CR_DBP bit must be set to enable write access to RTC registers. + */ +/* STM32_PWR_CR */ +#define PWR_CR 0x00 +/* STM32_PWR_CR bit field */ +#define PWR_CR_DBP BIT(8) + +struct stm32_rtc { + struct rtc_device *rtc_dev; + void __iomem *base; + struct regmap *dbp; + struct clk *ck_rtc; + int irq_alarm; +}; + +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc) +{ + writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + STM32_RTC_WPR); + writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + STM32_RTC_WPR); +} + +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc) +{ + writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + STM32_RTC_WPR); +} + +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc) +{ + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR); + + if (!(isr & STM32_RTC_ISR_INITF)) { + isr |= STM32_RTC_ISR_INIT; + writel_relaxed(isr, rtc->base + STM32_RTC_ISR); + + /* + * It takes around 2 ck_rtc clock cycles to enter in + * initialization phase mode (and have INITF flag set). As + * slowest ck_rtc frequency may be 32kHz and highest should be + * 1MHz, we poll every 10 us with a timeout of 100ms. + */ + return readl_relaxed_poll_timeout_atomic( + rtc->base + STM32_RTC_ISR, + isr, (isr & STM32_RTC_ISR_INITF), + 10, 100000); + } + + return 0; +} + +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc) +{ + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR); + + isr &= ~STM32_RTC_ISR_INIT; + writel_relaxed(isr, rtc->base + STM32_RTC_ISR); +} + +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc) +{ + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR); + + isr &= ~STM32_RTC_ISR_RSF; + writel_relaxed(isr, rtc->base + STM32_RTC_ISR); + + /* + * Wait for RSF to be set to ensure the calendar registers are + * synchronised, it takes around 2 ck_rtc clock cycles + */ + return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR, + isr, + (isr & STM32_RTC_ISR_RSF), + 10, 100000); +} + +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id) +{ + struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id; + unsigned int isr, cr; + + mutex_lock(&rtc->rtc_dev->ops_lock); + + isr = readl_relaxed(rtc->base + STM32_RTC_ISR); + cr = readl_relaxed(rtc->base + STM32_RTC_CR); + + if ((isr & STM32_RTC_ISR_ALRAF) && + (cr & STM32_RTC_CR_ALRAIE)) { + /* Alarm A flag - Alarm interrupt */ + dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n"); + + /* Pass event to the kernel */ + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + /* Clear event flag, otherwise new events won't be received */ + writel_relaxed(isr & ~STM32_RTC_ISR_ALRAF, + rtc->base + STM32_RTC_ISR); + } + + mutex_unlock(&rtc->rtc_dev->ops_lock); + + return IRQ_HANDLED; +} + +/* Convert rtc_time structure from bin to bcd format */ +static void tm2bcd(struct rtc_time *tm) +{ + tm->tm_sec = bin2bcd(tm->tm_sec); + tm->tm_min = bin2bcd(tm->tm_min); + tm->tm_hour = bin2bcd(tm->tm_hour); + + tm->tm_mday = bin2bcd(tm->tm_mday); + tm->tm_mon = bin2bcd(tm->tm_mon + 1); + tm->tm_year = bin2bcd(tm->tm_year - 100); + /* + * Number of days since Sunday + * - on kernel side, 0=Sunday...6=Saturday + * - on rtc side, 0=invalid,1=Monday...7=Sunday + */ + tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday; +} + +/* Convert rtc_time structure from bcd to bin format */ +static void bcd2tm(struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon) - 1; + tm->tm_year = bcd2bin(tm->tm_year) + 100; + /* + * Number of days since Sunday + * - on kernel side, 0=Sunday...6=Saturday + * - on rtc side, 0=invalid,1=Monday...7=Sunday + */ + tm->tm_wday %= 7; +} + +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + unsigned int tr, dr; + + /* Time and Date in BCD format */ + tr = readl_relaxed(rtc->base + STM32_RTC_TR); + dr = readl_relaxed(rtc->base + STM32_RTC_DR); + + tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT; + tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT; + tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT; + + tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT; + tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT; + tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT; + tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT; + + /* We don't report tm_yday and tm_isdst */ + + bcd2tm(tm); + + return 0; +} + +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + unsigned int tr, dr; + int ret = 0; + + tm2bcd(tm); + + /* Time in BCD format */ + tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) | + ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) | + ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR); + + /* Date in BCD format */ + dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) | + ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) | + ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) | + ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY); + + stm32_rtc_wpr_unlock(rtc); + + ret = stm32_rtc_enter_init_mode(rtc); + if (ret) { + dev_err(dev, "Can't enter in init mode. Set time aborted.\n"); + goto end; + } + + writel_relaxed(tr, rtc->base + STM32_RTC_TR); + writel_relaxed(dr, rtc->base + STM32_RTC_DR); + + stm32_rtc_exit_init_mode(rtc); + + ret = stm32_rtc_wait_sync(rtc); +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + unsigned int alrmar, cr, isr; + + alrmar = readl_relaxed(rtc->base + STM32_RTC_ALRMAR); + cr = readl_relaxed(rtc->base + STM32_RTC_CR); + isr = readl_relaxed(rtc->base + STM32_RTC_ISR); + + if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) { + /* + * Date/day doesn't matter in Alarm comparison so alarm + * triggers every day + */ + tm->tm_mday = -1; + tm->tm_wday = -1; + } else { + if (alrmar & STM32_RTC_ALRMXR_WDSEL) { + /* Alarm is set to a day of week */ + tm->tm_mday = -1; + tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >> + STM32_RTC_ALRMXR_WDAY_SHIFT; + tm->tm_wday %= 7; + } else { + /* Alarm is set to a day of month */ + tm->tm_wday = -1; + tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >> + STM32_RTC_ALRMXR_DATE_SHIFT; + } + } + + if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) { + /* Hours don't matter in Alarm comparison */ + tm->tm_hour = -1; + } else { + tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >> + STM32_RTC_ALRMXR_HOUR_SHIFT; + if (alrmar & STM32_RTC_ALRMXR_PM) + tm->tm_hour += 12; + } + + if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) { + /* Minutes don't matter in Alarm comparison */ + tm->tm_min = -1; + } else { + tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >> + STM32_RTC_ALRMXR_MIN_SHIFT; + } + + if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) { + /* Seconds don't matter in Alarm comparison */ + tm->tm_sec = -1; + } else { + tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >> + STM32_RTC_ALRMXR_SEC_SHIFT; + } + + bcd2tm(tm); + + alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0; + alrm->pending = (isr & STM32_RTC_ISR_ALRAF) ? 1 : 0; + + return 0; +} + +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + unsigned int isr, cr; + + cr = readl_relaxed(rtc->base + STM32_RTC_CR); + + stm32_rtc_wpr_unlock(rtc); + + /* We expose Alarm A to the kernel */ + if (enabled) + cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE); + else + cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE); + writel_relaxed(cr, rtc->base + STM32_RTC_CR); + + /* Clear event flag, otherwise new events won't be received */ + isr = readl_relaxed(rtc->base + STM32_RTC_ISR); + isr &= ~STM32_RTC_ISR_ALRAF; + writel_relaxed(isr, rtc->base + STM32_RTC_ISR); + + stm32_rtc_wpr_lock(rtc); + + return 0; +} + +static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm) +{ + int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec; + unsigned int dr = readl_relaxed(rtc->base + STM32_RTC_DR); + unsigned int tr = readl_relaxed(rtc->base + STM32_RTC_TR); + + cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT; + cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT; + cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT; + cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT; + cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT; + cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT; + + /* + * Assuming current date is M-D-Y H:M:S. + * RTC alarm can't be set on a specific month and year. + * So the valid alarm range is: + * M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S + * with a specific case for December... + */ + if ((((tm->tm_year > cur_year) && + (tm->tm_mon == 0x1) && (cur_mon == 0x12)) || + ((tm->tm_year == cur_year) && + (tm->tm_mon <= cur_mon + 1))) && + ((tm->tm_mday > cur_day) || + ((tm->tm_mday == cur_day) && + ((tm->tm_hour > cur_hour) || + ((tm->tm_hour == cur_hour) && (tm->tm_min > cur_min)) || + ((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) && + (tm->tm_sec >= cur_sec)))))) + return 0; + + return -EINVAL; +} + +static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + unsigned int cr, isr, alrmar; + int ret = 0; + + tm2bcd(tm); + + /* + * RTC alarm can't be set on a specific date, unless this date is + * up to the same day of month next month. + */ + if (stm32_rtc_valid_alrm(rtc, tm) < 0) { + dev_err(dev, "Alarm can be set only on upcoming month.\n"); + return -EINVAL; + } + + alrmar = 0; + /* tm_year and tm_mon are not used because not supported by RTC */ + alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) & + STM32_RTC_ALRMXR_DATE; + /* 24-hour format */ + alrmar &= ~STM32_RTC_ALRMXR_PM; + alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) & + STM32_RTC_ALRMXR_HOUR; + alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) & + STM32_RTC_ALRMXR_MIN; + alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) & + STM32_RTC_ALRMXR_SEC; + + stm32_rtc_wpr_unlock(rtc); + + /* Disable Alarm */ + cr = readl_relaxed(rtc->base + STM32_RTC_CR); + cr &= ~STM32_RTC_CR_ALRAE; + writel_relaxed(cr, rtc->base + STM32_RTC_CR); + + /* + * Poll Alarm write flag to be sure that Alarm update is allowed: it + * takes around 2 ck_rtc clock cycles + */ + ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR, + isr, + (isr & STM32_RTC_ISR_ALRAWF), + 10, 100000); + + if (ret) { + dev_err(dev, "Alarm update not allowed\n"); + goto end; + } + + /* Write to Alarm register */ + writel_relaxed(alrmar, rtc->base + STM32_RTC_ALRMAR); + + if (alrm->enabled) + stm32_rtc_alarm_irq_enable(dev, 1); + else + stm32_rtc_alarm_irq_enable(dev, 0); + +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static const struct rtc_class_ops stm32_rtc_ops = { + .read_time = stm32_rtc_read_time, + .set_time = stm32_rtc_set_time, + .read_alarm = stm32_rtc_read_alarm, + .set_alarm = stm32_rtc_set_alarm, + .alarm_irq_enable = stm32_rtc_alarm_irq_enable, +}; + +static const struct of_device_id stm32_rtc_of_match[] = { + { .compatible = "st,stm32-rtc" }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); + +static int stm32_rtc_init(struct platform_device *pdev, + struct stm32_rtc *rtc) +{ + unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr; + unsigned int rate; + int ret = 0; + + rate = clk_get_rate(rtc->ck_rtc); + + /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */ + pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT; + pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT; + + for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) { + pred_s = (rate / (pred_a + 1)) - 1; + + if (((pred_s + 1) * (pred_a + 1)) == rate) + break; + } + + /* + * Can't find a 1Hz, so give priority to RTC power consumption + * by choosing the higher possible value for prediv_a + */ + if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) { + pred_a = pred_a_max; + pred_s = (rate / (pred_a + 1)) - 1; + + dev_warn(&pdev->dev, "ck_rtc is %s\n", + (rate < ((pred_a + 1) * (pred_s + 1))) ? + "fast" : "slow"); + } + + stm32_rtc_wpr_unlock(rtc); + + ret = stm32_rtc_enter_init_mode(rtc); + if (ret) { + dev_err(&pdev->dev, + "Can't enter in init mode. Prescaler config failed.\n"); + goto end; + } + + prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S; + writel_relaxed(prer, rtc->base + STM32_RTC_PRER); + prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A; + writel_relaxed(prer, rtc->base + STM32_RTC_PRER); + + /* Force 24h time format */ + cr = readl_relaxed(rtc->base + STM32_RTC_CR); + cr &= ~STM32_RTC_CR_FMT; + writel_relaxed(cr, rtc->base + STM32_RTC_CR); + + stm32_rtc_exit_init_mode(rtc); + + ret = stm32_rtc_wait_sync(rtc); +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static int stm32_rtc_probe(struct platform_device *pdev) +{ + struct stm32_rtc *rtc; + struct resource *res; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rtc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rtc->base)) + return PTR_ERR(rtc->base); + + rtc->dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "st,syscfg"); + if (IS_ERR(rtc->dbp)) { + dev_err(&pdev->dev, "no st,syscfg\n"); + return PTR_ERR(rtc->dbp); + } + + rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(rtc->ck_rtc)) { + dev_err(&pdev->dev, "no ck_rtc clock"); + return PTR_ERR(rtc->ck_rtc); + } + + ret = clk_prepare_enable(rtc->ck_rtc); + if (ret) + return ret; + + regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP); + + /* + * After a system reset, RTC_ISR.INITS flag can be read to check if + * the calendar has been initalized or not. INITS flag is reset by a + * power-on reset (no vbat, no power-supply). It is not reset if + * ck_rtc parent clock has changed (so RTC prescalers need to be + * changed). That's why we cannot rely on this flag to know if RTC + * init has to be done. + */ + ret = stm32_rtc_init(pdev, rtc); + if (ret) + goto err; + + rtc->irq_alarm = platform_get_irq(pdev, 0); + if (rtc->irq_alarm <= 0) { + dev_err(&pdev->dev, "no alarm irq\n"); + ret = rtc->irq_alarm; + goto err; + } + + platform_set_drvdata(pdev, rtc); + + ret = device_init_wakeup(&pdev->dev, true); + if (ret) + dev_warn(&pdev->dev, + "alarm won't be able to wake up the system"); + + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, + &stm32_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + dev_err(&pdev->dev, "rtc device registration failed, err=%d\n", + ret); + goto err; + } + + /* Handle RTC alarm interrupts */ + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL, + stm32_rtc_alarm_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + pdev->name, rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n", + rtc->irq_alarm); + goto err; + } + + /* + * If INITS flag is reset (calendar year field set to 0x00), calendar + * must be initialized + */ + if (!(readl_relaxed(rtc->base + STM32_RTC_ISR) & STM32_RTC_ISR_INITS)) + dev_warn(&pdev->dev, "Date/Time must be initialized\n"); + + return 0; +err: + clk_disable_unprepare(rtc->ck_rtc); + + regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0); + + device_init_wakeup(&pdev->dev, false); + + return ret; +} + +static int stm32_rtc_remove(struct platform_device *pdev) +{ + struct stm32_rtc *rtc = platform_get_drvdata(pdev); + unsigned int cr; + + /* Disable interrupts */ + stm32_rtc_wpr_unlock(rtc); + cr = readl_relaxed(rtc->base + STM32_RTC_CR); + cr &= ~STM32_RTC_CR_ALRAIE; + writel_relaxed(cr, rtc->base + STM32_RTC_CR); + stm32_rtc_wpr_lock(rtc); + + clk_disable_unprepare(rtc->ck_rtc); + + /* Enable backup domain write protection */ + regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0); + + device_init_wakeup(&pdev->dev, false); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int stm32_rtc_suspend(struct device *dev) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + return enable_irq_wake(rtc->irq_alarm); + + return 0; +} + +static int stm32_rtc_resume(struct device *dev) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + int ret = 0; + + ret = stm32_rtc_wait_sync(rtc); + if (ret < 0) + return ret; + + if (device_may_wakeup(dev)) + return disable_irq_wake(rtc->irq_alarm); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops, + stm32_rtc_suspend, stm32_rtc_resume); + +static struct platform_driver stm32_rtc_driver = { + .probe = stm32_rtc_probe, + .remove = stm32_rtc_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &stm32_rtc_pm_ops, + .of_match_table = stm32_rtc_of_match, + }, +}; + +module_platform_driver(stm32_rtc_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index c169a2cd4727..39cbc1238b92 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -20,6 +20,8 @@ * more details. */ +#include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/fs.h> @@ -33,15 +35,20 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/rtc.h> +#include <linux/slab.h> #include <linux/types.h> /* Control register */ #define SUN6I_LOSC_CTRL 0x0000 +#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16) #define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) #define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) #define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) +#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0) #define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) +#define SUN6I_LOSC_CLK_PRESCAL 0x0008 + /* RTC */ #define SUN6I_RTC_YMD 0x0010 #define SUN6I_RTC_HMS 0x0014 @@ -114,13 +121,142 @@ struct sun6i_rtc_dev { void __iomem *base; int irq; unsigned long alarm; + + struct clk_hw hw; + struct clk_hw *int_osc; + struct clk *losc; + + spinlock_t lock; +}; + +static struct sun6i_rtc_dev *sun6i_rtc; + +static unsigned long sun6i_rtc_osc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + u32 val; + + val = readl(rtc->base + SUN6I_LOSC_CTRL); + if (val & SUN6I_LOSC_CTRL_EXT_OSC) + return parent_rate; + + val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL); + val &= GENMASK(4, 0); + + return parent_rate / (val + 1); +} + +static u8 sun6i_rtc_osc_get_parent(struct clk_hw *hw) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + + return readl(rtc->base + SUN6I_LOSC_CTRL) & SUN6I_LOSC_CTRL_EXT_OSC; +} + +static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + unsigned long flags; + u32 val; + + if (index > 1) + return -EINVAL; + + spin_lock_irqsave(&rtc->lock, flags); + val = readl(rtc->base + SUN6I_LOSC_CTRL); + val &= ~SUN6I_LOSC_CTRL_EXT_OSC; + val |= SUN6I_LOSC_CTRL_KEY; + val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0; + writel(val, rtc->base + SUN6I_LOSC_CTRL); + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static const struct clk_ops sun6i_rtc_osc_ops = { + .recalc_rate = sun6i_rtc_osc_recalc_rate, + + .get_parent = sun6i_rtc_osc_get_parent, + .set_parent = sun6i_rtc_osc_set_parent, }; +static void __init sun6i_rtc_clk_init(struct device_node *node) +{ + struct clk_hw_onecell_data *clk_data; + struct sun6i_rtc_dev *rtc; + struct clk_init_data init = { + .ops = &sun6i_rtc_osc_ops, + }; + const char *parents[2]; + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return; + spin_lock_init(&rtc->lock); + + clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws), + GFP_KERNEL); + if (!clk_data) + return; + spin_lock_init(&rtc->lock); + + rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(rtc->base)) { + pr_crit("Can't map RTC registers"); + return; + } + + /* Switch to the external, more precise, oscillator */ + writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, + rtc->base + SUN6I_LOSC_CTRL); + + /* Yes, I know, this is ugly. */ + sun6i_rtc = rtc; + + /* Deal with old DTs */ + if (!of_get_property(node, "clocks", NULL)) + return; + + rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL, + "rtc-int-osc", + NULL, 0, + 667000, + 300000000); + if (IS_ERR(rtc->int_osc)) { + pr_crit("Couldn't register the internal oscillator\n"); + return; + } + + parents[0] = clk_hw_get_name(rtc->int_osc); + parents[1] = of_clk_get_parent_name(node, 0); + + rtc->hw.init = &init; + + init.parent_names = parents; + init.num_parents = of_clk_get_parent_count(node) + 1; + of_property_read_string(node, "clock-output-names", &init.name); + + rtc->losc = clk_register(NULL, &rtc->hw); + if (IS_ERR(rtc->losc)) { + pr_crit("Couldn't register the LOSC clock\n"); + return; + } + + clk_data->num = 1; + clk_data->hws[0] = &rtc->hw; + of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); +} +CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc", + sun6i_rtc_clk_init); + static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) { struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; + irqreturn_t ret = IRQ_NONE; u32 val; + spin_lock(&chip->lock); val = readl(chip->base + SUN6I_ALRM_IRQ_STA); if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { @@ -129,10 +265,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); - return IRQ_HANDLED; + ret = IRQ_HANDLED; } + spin_unlock(&chip->lock); - return IRQ_NONE; + return ret; } static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) @@ -140,6 +277,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) u32 alrm_val = 0; u32 alrm_irq_val = 0; u32 alrm_wake_val = 0; + unsigned long flags; if (to) { alrm_val = SUN6I_ALRM_EN_CNT_EN; @@ -150,9 +288,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) chip->base + SUN6I_ALRM_IRQ_STA); } + spin_lock_irqsave(&chip->lock, flags); writel(alrm_val, chip->base + SUN6I_ALRM_EN); writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); + spin_unlock_irqrestore(&chip->lock, flags); } static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) @@ -191,11 +331,15 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) { struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + unsigned long flags; u32 alrm_st; u32 alrm_en; + spin_lock_irqsave(&chip->lock, flags); alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); + spin_unlock_irqrestore(&chip->lock, flags); + wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); rtc_time_to_tm(chip->alarm, &wkalrm->time); @@ -349,22 +493,15 @@ static const struct rtc_class_ops sun6i_rtc_ops = { static int sun6i_rtc_probe(struct platform_device *pdev) { - struct sun6i_rtc_dev *chip; - struct resource *res; + struct sun6i_rtc_dev *chip = sun6i_rtc; int ret; - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (!chip) - return -ENOMEM; + return -ENODEV; platform_set_drvdata(pdev, chip); chip->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(chip->base)) - return PTR_ERR(chip->base); - chip->irq = platform_get_irq(pdev, 0); if (chip->irq < 0) { dev_err(&pdev->dev, "No IRQ resource\n"); @@ -404,8 +541,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev) /* disable alarm wakeup */ writel(0, chip->base + SUN6I_ALARM_CONFIG); - chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, - &sun6i_rtc_ops, THIS_MODULE); + clk_prepare_enable(chip->losc); + + chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-sun6i", + &sun6i_rtc_ops, THIS_MODULE); if (IS_ERR(chip->rtc)) { dev_err(&pdev->dev, "unable to register device\n"); return PTR_ERR(chip->rtc); @@ -416,15 +555,6 @@ static int sun6i_rtc_probe(struct platform_device *pdev) return 0; } -static int sun6i_rtc_remove(struct platform_device *pdev) -{ - struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev); - - rtc_device_unregister(chip->rtc); - - return 0; -} - static const struct of_device_id sun6i_rtc_dt_ids[] = { { .compatible = "allwinner,sun6i-a31-rtc" }, { /* sentinel */ }, @@ -433,15 +563,9 @@ MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); static struct platform_driver sun6i_rtc_driver = { .probe = sun6i_rtc_probe, - .remove = sun6i_rtc_remove, .driver = { .name = "sun6i-rtc", .of_match_table = sun6i_rtc_dt_ids, }, }; - -module_platform_driver(sun6i_rtc_driver); - -MODULE_DESCRIPTION("sun6i RTC driver"); -MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(sun6i_rtc_driver); diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c index 3853ba963bb5..d30d57b048d3 100644 --- a/drivers/rtc/rtc-tegra.c +++ b/drivers/rtc/rtc-tegra.c @@ -17,16 +17,18 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include <linux/kernel.h> + +#include <linux/clk.h> +#include <linux/delay.h> #include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/irq.h> #include <linux/io.h> -#include <linux/delay.h> -#include <linux/rtc.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/rtc.h> +#include <linux/slab.h> /* set to 1 = busy every eight 32kHz clocks during copy of sec+msec to AHB */ #define TEGRA_RTC_REG_BUSY 0x004 @@ -59,6 +61,7 @@ struct tegra_rtc_info { struct platform_device *pdev; struct rtc_device *rtc_dev; void __iomem *rtc_base; /* NULL if not initialized. */ + struct clk *clk; int tegra_rtc_irq; /* alarm and periodic irq */ spinlock_t tegra_rtc_lock; }; @@ -326,6 +329,14 @@ static int __init tegra_rtc_probe(struct platform_device *pdev) if (info->tegra_rtc_irq <= 0) return -EBUSY; + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) + return PTR_ERR(info->clk); + + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; + /* set context info. */ info->pdev = pdev; spin_lock_init(&info->tegra_rtc_lock); @@ -346,7 +357,7 @@ static int __init tegra_rtc_probe(struct platform_device *pdev) ret = PTR_ERR(info->rtc_dev); dev_err(&pdev->dev, "Unable to register device (err=%d).\n", ret); - return ret; + goto disable_clk; } ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq, @@ -356,12 +367,25 @@ static int __init tegra_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to request interrupt for device (err=%d).\n", ret); - return ret; + goto disable_clk; } dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n"); return 0; + +disable_clk: + clk_disable_unprepare(info->clk); + return ret; +} + +static int tegra_rtc_remove(struct platform_device *pdev) +{ + struct tegra_rtc_info *info = platform_get_drvdata(pdev); + + clk_disable_unprepare(info->clk); + + return 0; } #ifdef CONFIG_PM_SLEEP @@ -413,6 +437,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev) MODULE_ALIAS("platform:tegra_rtc"); static struct platform_driver tegra_rtc_driver = { + .remove = tegra_rtc_remove, .shutdown = tegra_rtc_shutdown, .driver = { .name = "tegra_rtc", diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c index 5a3d53caa485..d0244d7979fc 100644 --- a/drivers/rtc/rtc-tps65910.c +++ b/drivers/rtc/rtc-tps65910.c @@ -21,6 +21,7 @@ #include <linux/types.h> #include <linux/rtc.h> #include <linux/bcd.h> +#include <linux/math64.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/mfd/tps65910.h> @@ -33,7 +34,21 @@ struct tps65910_rtc { /* Total number of RTC registers needed to set time*/ #define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1) -static int tps65910_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +/* Total number of RTC registers needed to set compensation registers */ +#define NUM_COMP_REGS (TPS65910_RTC_COMP_MSB - TPS65910_RTC_COMP_LSB + 1) + +/* Min and max values supported with 'offset' interface (swapped sign) */ +#define MIN_OFFSET (-277761) +#define MAX_OFFSET (277778) + +/* Number of ticks per hour */ +#define TICKS_PER_HOUR (32768 * 3600) + +/* Multiplier for ppb conversions */ +#define PPB_MULT (1000000000LL) + +static int tps65910_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) { struct tps65910 *tps = dev_get_drvdata(dev->parent); u8 val = 0; @@ -187,6 +202,133 @@ static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) return ret; } +static int tps65910_rtc_set_calibration(struct device *dev, int calibration) +{ + unsigned char comp_data[NUM_COMP_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + s16 value; + int ret; + + /* + * TPS65910 uses two's complement 16 bit value for compensation for RTC + * crystal inaccuracies. One time every hour when seconds counter + * increments from 0 to 1 compensation value will be added to internal + * RTC counter value. + * + * Compensation value 0x7FFF is prohibited value. + * + * Valid range for compensation value: [-32768 .. 32766] + */ + if ((calibration < -32768) || (calibration > 32766)) { + dev_err(dev, "RTC calibration value out of range: %d\n", + calibration); + return -EINVAL; + } + + value = (s16)calibration; + + comp_data[0] = (u16)value & 0xFF; + comp_data[1] = ((u16)value >> 8) & 0xFF; + + /* Update all the compensation registers in one shot */ + ret = regmap_bulk_write(tps->regmap, TPS65910_RTC_COMP_LSB, + comp_data, NUM_COMP_REGS); + if (ret < 0) { + dev_err(dev, "rtc_set_calibration error: %d\n", ret); + return ret; + } + + /* Enable automatic compensation */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_AUTO_COMP, TPS65910_RTC_CTRL_AUTO_COMP); + if (ret < 0) + dev_err(dev, "auto_comp enable failed with error: %d\n", ret); + + return ret; +} + +static int tps65910_rtc_get_calibration(struct device *dev, int *calibration) +{ + unsigned char comp_data[NUM_COMP_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + unsigned int ctrl; + u16 value; + int ret; + + ret = regmap_read(tps->regmap, TPS65910_RTC_CTRL, &ctrl); + if (ret < 0) + return ret; + + /* If automatic compensation is not enabled report back zero */ + if (!(ctrl & TPS65910_RTC_CTRL_AUTO_COMP)) { + *calibration = 0; + return 0; + } + + ret = regmap_bulk_read(tps->regmap, TPS65910_RTC_COMP_LSB, comp_data, + NUM_COMP_REGS); + if (ret < 0) { + dev_err(dev, "rtc_get_calibration error: %d\n", ret); + return ret; + } + + value = (u16)comp_data[0] | ((u16)comp_data[1] << 8); + + *calibration = (s16)value; + + return 0; +} + +static int tps65910_read_offset(struct device *dev, long *offset) +{ + int calibration; + s64 tmp; + int ret; + + ret = tps65910_rtc_get_calibration(dev, &calibration); + if (ret < 0) + return ret; + + /* Convert from RTC calibration register format to ppb format */ + tmp = calibration * (s64)PPB_MULT; + if (tmp < 0) + tmp -= TICKS_PER_HOUR / 2LL; + else + tmp += TICKS_PER_HOUR / 2LL; + tmp = div_s64(tmp, TICKS_PER_HOUR); + + /* Offset value operates in negative way, so swap sign */ + *offset = (long)-tmp; + + return 0; +} + +static int tps65910_set_offset(struct device *dev, long offset) +{ + int calibration; + s64 tmp; + int ret; + + /* Make sure offset value is within supported range */ + if (offset < MIN_OFFSET || offset > MAX_OFFSET) + return -ERANGE; + + /* Convert from ppb format to RTC calibration register format */ + tmp = offset * (s64)TICKS_PER_HOUR; + if (tmp < 0) + tmp -= PPB_MULT / 2LL; + else + tmp += PPB_MULT / 2LL; + tmp = div_s64(tmp, PPB_MULT); + + /* Offset value operates in negative way, so swap sign */ + calibration = (int)-tmp; + + ret = tps65910_rtc_set_calibration(dev, calibration); + + return ret; +} + static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc) { struct device *dev = rtc; @@ -219,6 +361,8 @@ static const struct rtc_class_ops tps65910_rtc_ops = { .read_alarm = tps65910_rtc_read_alarm, .set_alarm = tps65910_rtc_set_alarm, .alarm_irq_enable = tps65910_rtc_alarm_irq_enable, + .read_offset = tps65910_read_offset, + .set_offset = tps65910_set_offset, }; static int tps65910_rtc_probe(struct platform_device *pdev) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 0f1713727d4c..0b38217f8147 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -4864,7 +4864,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, break; case 3: /* tsa_intrg */ len += sprintf(page + len, PRINTK_HEADER - " tsb->tsa.intrg.: not supportet yet\n"); + " tsb->tsa.intrg.: not supported yet\n"); break; } diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 85eca1cef063..c4518168fd02 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/compat.h> +#include <linux/sched/signal.h> #include <linux/module.h> #include <linux/list.h> #include <linux/slab.h> diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index 82c913318b73..ba0e4f93503d 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -7,7 +7,7 @@ */ #include <linux/module.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/sysrq.h> diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index de6fccc13124..1b350665c823 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -29,7 +29,7 @@ #include <asm/chpid.h> #include <asm/airq.h> #include <asm/isc.h> -#include <linux/cputime.h> +#include <linux/sched/cputime.h> #include <asm/fcx.h> #include <asm/nmi.h> #include <asm/crw.h> diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 79823ee9c100..b8006ea9099c 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/timer.h> #include <linux/kernel_stat.h> +#include <linux/sched/signal.h> #include <asm/ccwdev.h> #include <asm/cio.h> diff --git a/drivers/s390/cio/ioasm.c b/drivers/s390/cio/ioasm.c index 8225da619014..4182f60124da 100644 --- a/drivers/s390/cio/ioasm.c +++ b/drivers/s390/cio/ioasm.c @@ -165,13 +165,15 @@ int tpi(struct tpi_info *addr) int chsc(void *chsc_area) { typedef struct { char _[4096]; } addr_type; - int cc; + int cc = -EIO; asm volatile( " .insn rre,0xb25f0000,%2,0\n" - " ipm %0\n" + "0: ipm %0\n" " srl %0,28\n" - : "=d" (cc), "=m" (*(addr_type *) chsc_area) + "1:\n" + EX_TABLE(0b, 1b) + : "+d" (cc), "=m" (*(addr_type *) chsc_area) : "d" (chsc_area), "m" (*(addr_type *) chsc_area) : "cc"); trace_s390_cio_chsc(chsc_area, cc); diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 8ad98a902a91..c61164f4528e 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -8,6 +8,8 @@ #include <linux/slab.h> #include <linux/kernel_stat.h> #include <linux/atomic.h> +#include <linux/rculist.h> + #include <asm/debug.h> #include <asm/qdio.h> #include <asm/airq.h> diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile index 0a7fb83f35e5..be36f1010d75 100644 --- a/drivers/s390/crypto/Makefile +++ b/drivers/s390/crypto/Makefile @@ -10,3 +10,7 @@ zcrypt-objs += zcrypt_msgtype6.o zcrypt_msgtype50.o obj-$(CONFIG_ZCRYPT) += zcrypt.o # adapter drivers depend on ap.o and zcrypt.o obj-$(CONFIG_ZCRYPT) += zcrypt_pcixcc.o zcrypt_cex2a.o zcrypt_cex4.o + +# pkey kernel module +pkey-objs := pkey_api.o +obj-$(CONFIG_PKEY) += pkey.o diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 56db76c05775..9be4596d8a08 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1107,16 +1107,6 @@ static void ap_config_timeout(unsigned long ptr) queue_work(system_long_wq, &ap_scan_work); } -static void ap_reset_domain(void) -{ - int i; - - if (ap_domain_index == -1 || !ap_test_config_domain(ap_domain_index)) - return; - for (i = 0; i < AP_DEVICES; i++) - ap_rapq(AP_MKQID(i, ap_domain_index)); -} - static void ap_reset_all(void) { int i, j; diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c index 1cd9128593e4..cfa161ccc74e 100644 --- a/drivers/s390/crypto/ap_card.c +++ b/drivers/s390/crypto/ap_card.c @@ -58,9 +58,9 @@ static ssize_t ap_functions_show(struct device *dev, static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL); -static ssize_t ap_request_count_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t ap_req_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct ap_card *ac = to_ap_card(dev); unsigned int req_cnt; @@ -72,7 +72,23 @@ static ssize_t ap_request_count_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt); } -static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL); +static ssize_t ap_req_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_card *ac = to_ap_card(dev); + struct ap_queue *aq; + + spin_lock_bh(&ap_list_lock); + for_each_ap_queue(aq, ac) + aq->total_request_count = 0; + spin_unlock_bh(&ap_list_lock); + atomic_set(&ac->total_request_count, 0); + + return count; +} + +static DEVICE_ATTR(request_count, 0644, ap_req_count_show, ap_req_count_store); static ssize_t ap_requestq_count_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index 7be67fa9f224..480c58a63769 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -459,9 +459,9 @@ EXPORT_SYMBOL(ap_queue_resume); /* * AP queue related attributes. */ -static ssize_t ap_request_count_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t ap_req_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct ap_queue *aq = to_ap_queue(dev); unsigned int req_cnt; @@ -472,7 +472,20 @@ static ssize_t ap_request_count_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt); } -static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL); +static ssize_t ap_req_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_queue *aq = to_ap_queue(dev); + + spin_lock_bh(&aq->lock); + aq->total_request_count = 0; + spin_unlock_bh(&aq->lock); + + return count; +} + +static DEVICE_ATTR(request_count, 0644, ap_req_count_show, ap_req_count_store); static ssize_t ap_requestq_count_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c new file mode 100644 index 000000000000..40f1136f5568 --- /dev/null +++ b/drivers/s390/crypto/pkey_api.c @@ -0,0 +1,1148 @@ +/* + * pkey device driver + * + * Copyright IBM Corp. 2017 + * Author(s): Harald Freudenberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + */ + +#define KMSG_COMPONENT "pkey" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/kallsyms.h> +#include <linux/debugfs.h> +#include <asm/zcrypt.h> +#include <asm/cpacf.h> +#include <asm/pkey.h> + +#include "zcrypt_api.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("s390 protected key interface"); + +/* Size of parameter block used for all cca requests/replies */ +#define PARMBSIZE 512 + +/* Size of vardata block used for some of the cca requests/replies */ +#define VARDATASIZE 4096 + +/* + * debug feature data and functions + */ + +static debug_info_t *debug_info; + +#define DEBUG_DBG(...) debug_sprintf_event(debug_info, 6, ##__VA_ARGS__) +#define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__) +#define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__) +#define DEBUG_ERR(...) debug_sprintf_event(debug_info, 3, ##__VA_ARGS__) + +static void __init pkey_debug_init(void) +{ + debug_info = debug_register("pkey", 1, 1, 4 * sizeof(long)); + debug_register_view(debug_info, &debug_sprintf_view); + debug_set_level(debug_info, 3); +} + +static void __exit pkey_debug_exit(void) +{ + debug_unregister(debug_info); +} + +/* inside view of a secure key token (only type 0x01 version 0x04) */ +struct secaeskeytoken { + u8 type; /* 0x01 for internal key token */ + u8 res0[3]; + u8 version; /* should be 0x04 */ + u8 res1[1]; + u8 flag; /* key flags */ + u8 res2[1]; + u64 mkvp; /* master key verification pattern */ + u8 key[32]; /* key value (encrypted) */ + u8 cv[8]; /* control vector */ + u16 bitsize; /* key bit size */ + u16 keysize; /* key byte size */ + u8 tvv[4]; /* token validation value */ +} __packed; + +/* + * Simple check if the token is a valid CCA secure AES key + * token. If keybitsize is given, the bitsize of the key is + * also checked. Returns 0 on success or errno value on failure. + */ +static int check_secaeskeytoken(u8 *token, int keybitsize) +{ + struct secaeskeytoken *t = (struct secaeskeytoken *) token; + + if (t->type != 0x01) { + DEBUG_ERR( + "check_secaeskeytoken secure token check failed, type mismatch 0x%02x != 0x01\n", + (int) t->type); + return -EINVAL; + } + if (t->version != 0x04) { + DEBUG_ERR( + "check_secaeskeytoken secure token check failed, version mismatch 0x%02x != 0x04\n", + (int) t->version); + return -EINVAL; + } + if (keybitsize > 0 && t->bitsize != keybitsize) { + DEBUG_ERR( + "check_secaeskeytoken secure token check failed, bitsize mismatch %d != %d\n", + (int) t->bitsize, keybitsize); + return -EINVAL; + } + + return 0; +} + +/* + * Allocate consecutive memory for request CPRB, request param + * block, reply CPRB and reply param block and fill in values + * for the common fields. Returns 0 on success or errno value + * on failure. + */ +static int alloc_and_prep_cprbmem(size_t paramblen, + u8 **pcprbmem, + struct CPRBX **preqCPRB, + struct CPRBX **prepCPRB) +{ + u8 *cprbmem; + size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen; + struct CPRBX *preqcblk, *prepcblk; + + /* + * allocate consecutive memory for request CPRB, request param + * block, reply CPRB and reply param block + */ + cprbmem = kmalloc(2 * cprbplusparamblen, GFP_KERNEL); + if (!cprbmem) + return -ENOMEM; + memset(cprbmem, 0, 2 * cprbplusparamblen); + + preqcblk = (struct CPRBX *) cprbmem; + prepcblk = (struct CPRBX *) (cprbmem + cprbplusparamblen); + + /* fill request cprb struct */ + preqcblk->cprb_len = sizeof(struct CPRBX); + preqcblk->cprb_ver_id = 0x02; + memcpy(preqcblk->func_id, "T2", 2); + preqcblk->rpl_msgbl = cprbplusparamblen; + if (paramblen) { + preqcblk->req_parmb = + ((u8 *) preqcblk) + sizeof(struct CPRBX); + preqcblk->rpl_parmb = + ((u8 *) prepcblk) + sizeof(struct CPRBX); + } + + *pcprbmem = cprbmem; + *preqCPRB = preqcblk; + *prepCPRB = prepcblk; + + return 0; +} + +/* + * Free the cprb memory allocated with the function above. + * If the scrub value is not zero, the memory is filled + * with zeros before freeing (useful if there was some + * clear key material in there). + */ +static void free_cprbmem(void *mem, size_t paramblen, int scrub) +{ + if (scrub) + memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen)); + kfree(mem); +} + +/* + * Helper function to prepare the xcrb struct + */ +static inline void prep_xcrb(struct ica_xcRB *pxcrb, + u16 cardnr, + struct CPRBX *preqcblk, + struct CPRBX *prepcblk) +{ + memset(pxcrb, 0, sizeof(*pxcrb)); + pxcrb->agent_ID = 0x4341; /* 'CA' */ + pxcrb->user_defined = (cardnr == 0xFFFF ? AUTOSELECT : cardnr); + pxcrb->request_control_blk_length = + preqcblk->cprb_len + preqcblk->req_parml; + pxcrb->request_control_blk_addr = (void *) preqcblk; + pxcrb->reply_control_blk_length = preqcblk->rpl_msgbl; + pxcrb->reply_control_blk_addr = (void *) prepcblk; +} + +/* + * Helper function which calls zcrypt_send_cprb with + * memory management segment adjusted to kernel space + * so that the copy_from_user called within this + * function do in fact copy from kernel space. + */ +static inline int _zcrypt_send_cprb(struct ica_xcRB *xcrb) +{ + int rc; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + rc = zcrypt_send_cprb(xcrb); + set_fs(old_fs); + + return rc; +} + +/* + * Generate (random) AES secure key. + */ +int pkey_genseckey(u16 cardnr, u16 domain, + u32 keytype, struct pkey_seckey *seckey) +{ + int i, rc, keysize; + int seckeysize; + u8 *mem; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct kgreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv1 { + u16 len; + char key_form[8]; + char key_length[8]; + char key_type1[8]; + char key_type2[8]; + } lv1; + struct lv2 { + u16 len; + struct keyid { + u16 len; + u16 attr; + u8 data[SECKEYBLOBSIZE]; + } keyid[6]; + } lv2; + } *preqparm; + struct kgrepparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv3 { + u16 len; + u16 keyblocklen; + struct { + u16 toklen; + u16 tokattr; + u8 tok[0]; + /* ... some more data ... */ + } keyblock; + } lv3; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with KG request */ + preqparm = (struct kgreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "KG", 2); + preqparm->rule_array_len = sizeof(preqparm->rule_array_len); + preqparm->lv1.len = sizeof(struct lv1); + memcpy(preqparm->lv1.key_form, "OP ", 8); + switch (keytype) { + case PKEY_KEYTYPE_AES_128: + keysize = 16; + memcpy(preqparm->lv1.key_length, "KEYLN16 ", 8); + break; + case PKEY_KEYTYPE_AES_192: + keysize = 24; + memcpy(preqparm->lv1.key_length, "KEYLN24 ", 8); + break; + case PKEY_KEYTYPE_AES_256: + keysize = 32; + memcpy(preqparm->lv1.key_length, "KEYLN32 ", 8); + break; + default: + DEBUG_ERR( + "pkey_genseckey unknown/unsupported keytype %d\n", + keytype); + rc = -EINVAL; + goto out; + } + memcpy(preqparm->lv1.key_type1, "AESDATA ", 8); + preqparm->lv2.len = sizeof(struct lv2); + for (i = 0; i < 6; i++) { + preqparm->lv2.keyid[i].len = sizeof(struct keyid); + preqparm->lv2.keyid[i].attr = (i == 2 ? 0x30 : 0x10); + } + preqcblk->req_parml = sizeof(struct kgreqparm); + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "pkey_genseckey zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "pkey_genseckey secure key generate failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct kgrepparm *) prepcblk->rpl_parmb; + + /* check length of the returned secure key token */ + seckeysize = prepparm->lv3.keyblock.toklen + - sizeof(prepparm->lv3.keyblock.toklen) + - sizeof(prepparm->lv3.keyblock.tokattr); + if (seckeysize != SECKEYBLOBSIZE) { + DEBUG_ERR( + "pkey_genseckey secure token size mismatch %d != %d bytes\n", + seckeysize, SECKEYBLOBSIZE); + rc = -EIO; + goto out; + } + + /* check secure key token */ + rc = check_secaeskeytoken(prepparm->lv3.keyblock.tok, 8*keysize); + if (rc) { + rc = -EIO; + goto out; + } + + /* copy the generated secure key token */ + memcpy(seckey->seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE); + +out: + free_cprbmem(mem, PARMBSIZE, 0); + return rc; +} +EXPORT_SYMBOL(pkey_genseckey); + +/* + * Generate an AES secure key with given key value. + */ +int pkey_clr2seckey(u16 cardnr, u16 domain, u32 keytype, + const struct pkey_clrkey *clrkey, + struct pkey_seckey *seckey) +{ + int rc, keysize, seckeysize; + u8 *mem; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct cmreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + char rule_array[8]; + struct lv1 { + u16 len; + u8 clrkey[0]; + } lv1; + struct lv2 { + u16 len; + struct keyid { + u16 len; + u16 attr; + u8 data[SECKEYBLOBSIZE]; + } keyid; + } lv2; + } *preqparm; + struct lv2 *plv2; + struct cmrepparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv3 { + u16 len; + u16 keyblocklen; + struct { + u16 toklen; + u16 tokattr; + u8 tok[0]; + /* ... some more data ... */ + } keyblock; + } lv3; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with CM request */ + preqparm = (struct cmreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "CM", 2); + memcpy(preqparm->rule_array, "AES ", 8); + preqparm->rule_array_len = + sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array); + switch (keytype) { + case PKEY_KEYTYPE_AES_128: + keysize = 16; + break; + case PKEY_KEYTYPE_AES_192: + keysize = 24; + break; + case PKEY_KEYTYPE_AES_256: + keysize = 32; + break; + default: + DEBUG_ERR( + "pkey_clr2seckey unknown/unsupported keytype %d\n", + keytype); + rc = -EINVAL; + goto out; + } + preqparm->lv1.len = sizeof(struct lv1) + keysize; + memcpy(preqparm->lv1.clrkey, clrkey->clrkey, keysize); + plv2 = (struct lv2 *) (((u8 *) &preqparm->lv2) + keysize); + plv2->len = sizeof(struct lv2); + plv2->keyid.len = sizeof(struct keyid); + plv2->keyid.attr = 0x30; + preqcblk->req_parml = sizeof(struct cmreqparm) + keysize; + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "pkey_clr2seckey zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "pkey_clr2seckey clear key import failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct cmrepparm *) prepcblk->rpl_parmb; + + /* check length of the returned secure key token */ + seckeysize = prepparm->lv3.keyblock.toklen + - sizeof(prepparm->lv3.keyblock.toklen) + - sizeof(prepparm->lv3.keyblock.tokattr); + if (seckeysize != SECKEYBLOBSIZE) { + DEBUG_ERR( + "pkey_clr2seckey secure token size mismatch %d != %d bytes\n", + seckeysize, SECKEYBLOBSIZE); + rc = -EIO; + goto out; + } + + /* check secure key token */ + rc = check_secaeskeytoken(prepparm->lv3.keyblock.tok, 8*keysize); + if (rc) { + rc = -EIO; + goto out; + } + + /* copy the generated secure key token */ + memcpy(seckey->seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE); + +out: + free_cprbmem(mem, PARMBSIZE, 1); + return rc; +} +EXPORT_SYMBOL(pkey_clr2seckey); + +/* + * Derive a proteced key from the secure key blob. + */ +int pkey_sec2protkey(u16 cardnr, u16 domain, + const struct pkey_seckey *seckey, + struct pkey_protkey *protkey) +{ + int rc; + u8 *mem; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct uskreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv1 { + u16 len; + u16 attr_len; + u16 attr_flags; + } lv1; + struct lv2 { + u16 len; + u16 attr_len; + u16 attr_flags; + u8 token[0]; /* cca secure key token */ + } lv2 __packed; + } *preqparm; + struct uskrepparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv3 { + u16 len; + u16 attr_len; + u16 attr_flags; + struct cpacfkeyblock { + u8 version; /* version of this struct */ + u8 flags[2]; + u8 algo; + u8 form; + u8 pad1[3]; + u16 keylen; + u8 key[64]; /* the key (keylen bytes) */ + u16 keyattrlen; + u8 keyattr[32]; + u8 pad2[1]; + u8 vptype; + u8 vp[32]; /* verification pattern */ + } keyblock; + } lv3 __packed; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with USK request */ + preqparm = (struct uskreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "US", 2); + preqparm->rule_array_len = sizeof(preqparm->rule_array_len); + preqparm->lv1.len = sizeof(struct lv1); + preqparm->lv1.attr_len = sizeof(struct lv1) - sizeof(preqparm->lv1.len); + preqparm->lv1.attr_flags = 0x0001; + preqparm->lv2.len = sizeof(struct lv2) + SECKEYBLOBSIZE; + preqparm->lv2.attr_len = sizeof(struct lv2) + - sizeof(preqparm->lv2.len) + SECKEYBLOBSIZE; + preqparm->lv2.attr_flags = 0x0000; + memcpy(preqparm->lv2.token, seckey->seckey, SECKEYBLOBSIZE); + preqcblk->req_parml = sizeof(struct uskreqparm) + SECKEYBLOBSIZE; + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "pkey_sec2protkey zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "pkey_sec2protkey unwrap secure key failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct uskrepparm *) prepcblk->rpl_parmb; + + /* check the returned keyblock */ + if (prepparm->lv3.keyblock.version != 0x01) { + DEBUG_ERR( + "pkey_sec2protkey reply param keyblock version mismatch 0x%02x != 0x01\n", + (int) prepparm->lv3.keyblock.version); + rc = -EIO; + goto out; + } + + /* copy the tanslated protected key */ + switch (prepparm->lv3.keyblock.keylen) { + case 16+32: + protkey->type = PKEY_KEYTYPE_AES_128; + break; + case 24+32: + protkey->type = PKEY_KEYTYPE_AES_192; + break; + case 32+32: + protkey->type = PKEY_KEYTYPE_AES_256; + break; + default: + DEBUG_ERR("pkey_sec2protkey unknown/unsupported keytype %d\n", + prepparm->lv3.keyblock.keylen); + rc = -EIO; + goto out; + } + protkey->len = prepparm->lv3.keyblock.keylen; + memcpy(protkey->protkey, prepparm->lv3.keyblock.key, protkey->len); + +out: + free_cprbmem(mem, PARMBSIZE, 0); + return rc; +} +EXPORT_SYMBOL(pkey_sec2protkey); + +/* + * Create a protected key from a clear key value. + */ +int pkey_clr2protkey(u32 keytype, + const struct pkey_clrkey *clrkey, + struct pkey_protkey *protkey) +{ + long fc; + int keysize; + u8 paramblock[64]; + + switch (keytype) { + case PKEY_KEYTYPE_AES_128: + keysize = 16; + fc = CPACF_PCKMO_ENC_AES_128_KEY; + break; + case PKEY_KEYTYPE_AES_192: + keysize = 24; + fc = CPACF_PCKMO_ENC_AES_192_KEY; + break; + case PKEY_KEYTYPE_AES_256: + keysize = 32; + fc = CPACF_PCKMO_ENC_AES_256_KEY; + break; + default: + DEBUG_ERR("pkey_clr2protkey unknown/unsupported keytype %d\n", + keytype); + return -EINVAL; + } + + /* prepare param block */ + memset(paramblock, 0, sizeof(paramblock)); + memcpy(paramblock, clrkey->clrkey, keysize); + + /* call the pckmo instruction */ + cpacf_pckmo(fc, paramblock); + + /* copy created protected key */ + protkey->type = keytype; + protkey->len = keysize + 32; + memcpy(protkey->protkey, paramblock, keysize + 32); + + return 0; +} +EXPORT_SYMBOL(pkey_clr2protkey); + +/* + * query cryptographic facility from adapter + */ +static int query_crypto_facility(u16 cardnr, u16 domain, + const char *keyword, + u8 *rarray, size_t *rarraylen, + u8 *varray, size_t *varraylen) +{ + int rc; + u16 len; + u8 *mem, *ptr; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct fqreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + char rule_array[8]; + struct lv1 { + u16 len; + u8 data[VARDATASIZE]; + } lv1; + u16 dummylen; + } *preqparm; + size_t parmbsize = sizeof(struct fqreqparm); + struct fqrepparm { + u8 subfunc_code[2]; + u8 lvdata[0]; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(parmbsize, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with FQ request */ + preqparm = (struct fqreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "FQ", 2); + strncpy(preqparm->rule_array, keyword, sizeof(preqparm->rule_array)); + preqparm->rule_array_len = + sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array); + preqparm->lv1.len = sizeof(preqparm->lv1); + preqparm->dummylen = sizeof(preqparm->dummylen); + preqcblk->req_parml = parmbsize; + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "query_crypto_facility zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "query_crypto_facility unwrap secure key failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct fqrepparm *) prepcblk->rpl_parmb; + ptr = prepparm->lvdata; + + /* check and possibly copy reply rule array */ + len = *((u16 *) ptr); + if (len > sizeof(u16)) { + ptr += sizeof(u16); + len -= sizeof(u16); + if (rarray && rarraylen && *rarraylen > 0) { + *rarraylen = (len > *rarraylen ? *rarraylen : len); + memcpy(rarray, ptr, *rarraylen); + } + ptr += len; + } + /* check and possible copy reply var array */ + len = *((u16 *) ptr); + if (len > sizeof(u16)) { + ptr += sizeof(u16); + len -= sizeof(u16); + if (varray && varraylen && *varraylen > 0) { + *varraylen = (len > *varraylen ? *varraylen : len); + memcpy(varray, ptr, *varraylen); + } + ptr += len; + } + +out: + free_cprbmem(mem, parmbsize, 0); + return rc; +} + +/* + * Fetch just the mkvp value via query_crypto_facility from adapter. + */ +static int fetch_mkvp(u16 cardnr, u16 domain, u64 *mkvp) +{ + int rc, found = 0; + size_t rlen, vlen; + u8 *rarray, *varray, *pg; + + pg = (u8 *) __get_free_page(GFP_KERNEL); + if (!pg) + return -ENOMEM; + rarray = pg; + varray = pg + PAGE_SIZE/2; + rlen = vlen = PAGE_SIZE/2; + + rc = query_crypto_facility(cardnr, domain, "STATICSA", + rarray, &rlen, varray, &vlen); + if (rc == 0 && rlen > 8*8 && vlen > 184+8) { + if (rarray[64] == '2') { + /* current master key state is valid */ + *mkvp = *((u64 *)(varray + 184)); + found = 1; + } + } + + free_page((unsigned long) pg); + + return found ? 0 : -ENOENT; +} + +/* struct to hold cached mkvp info for each card/domain */ +struct mkvp_info { + struct list_head list; + u16 cardnr; + u16 domain; + u64 mkvp; +}; + +/* a list with mkvp_info entries */ +static LIST_HEAD(mkvp_list); +static DEFINE_SPINLOCK(mkvp_list_lock); + +static int mkvp_cache_fetch(u16 cardnr, u16 domain, u64 *mkvp) +{ + int rc = -ENOENT; + struct mkvp_info *ptr; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry(ptr, &mkvp_list, list) { + if (ptr->cardnr == cardnr && + ptr->domain == domain) { + *mkvp = ptr->mkvp; + rc = 0; + break; + } + } + spin_unlock_bh(&mkvp_list_lock); + + return rc; +} + +static void mkvp_cache_update(u16 cardnr, u16 domain, u64 mkvp) +{ + int found = 0; + struct mkvp_info *ptr; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry(ptr, &mkvp_list, list) { + if (ptr->cardnr == cardnr && + ptr->domain == domain) { + ptr->mkvp = mkvp; + found = 1; + break; + } + } + if (!found) { + ptr = kmalloc(sizeof(*ptr), GFP_ATOMIC); + if (!ptr) { + spin_unlock_bh(&mkvp_list_lock); + return; + } + ptr->cardnr = cardnr; + ptr->domain = domain; + ptr->mkvp = mkvp; + list_add(&ptr->list, &mkvp_list); + } + spin_unlock_bh(&mkvp_list_lock); +} + +static void mkvp_cache_scrub(u16 cardnr, u16 domain) +{ + struct mkvp_info *ptr; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry(ptr, &mkvp_list, list) { + if (ptr->cardnr == cardnr && + ptr->domain == domain) { + list_del(&ptr->list); + kfree(ptr); + break; + } + } + spin_unlock_bh(&mkvp_list_lock); +} + +static void __exit mkvp_cache_free(void) +{ + struct mkvp_info *ptr, *pnext; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry_safe(ptr, pnext, &mkvp_list, list) { + list_del(&ptr->list); + kfree(ptr); + } + spin_unlock_bh(&mkvp_list_lock); +} + +/* + * Search for a matching crypto card based on the Master Key + * Verification Pattern provided inside a secure key. + */ +int pkey_findcard(const struct pkey_seckey *seckey, + u16 *pcardnr, u16 *pdomain, int verify) +{ + struct secaeskeytoken *t = (struct secaeskeytoken *) seckey; + struct zcrypt_device_matrix *device_matrix; + u16 card, dom; + u64 mkvp; + int i, rc; + + /* mkvp must not be zero */ + if (t->mkvp == 0) + return -EINVAL; + + /* fetch status of all crypto cards */ + device_matrix = kmalloc(sizeof(struct zcrypt_device_matrix), + GFP_KERNEL); + if (!device_matrix) + return -ENOMEM; + zcrypt_device_status_mask(device_matrix); + + /* walk through all crypto cards */ + for (i = 0; i < MAX_ZDEV_ENTRIES; i++) { + card = AP_QID_CARD(device_matrix->device[i].qid); + dom = AP_QID_QUEUE(device_matrix->device[i].qid); + if (device_matrix->device[i].online && + device_matrix->device[i].functions & 0x04) { + /* an enabled CCA Coprocessor card */ + /* try cached mkvp */ + if (mkvp_cache_fetch(card, dom, &mkvp) == 0 && + t->mkvp == mkvp) { + if (!verify) + break; + /* verify: fetch mkvp from adapter */ + if (fetch_mkvp(card, dom, &mkvp) == 0) { + mkvp_cache_update(card, dom, mkvp); + if (t->mkvp == mkvp) + break; + } + } + } else { + /* Card is offline and/or not a CCA card. */ + /* del mkvp entry from cache if it exists */ + mkvp_cache_scrub(card, dom); + } + } + if (i >= MAX_ZDEV_ENTRIES) { + /* nothing found, so this time without cache */ + for (i = 0; i < MAX_ZDEV_ENTRIES; i++) { + if (!(device_matrix->device[i].online && + device_matrix->device[i].functions & 0x04)) + continue; + card = AP_QID_CARD(device_matrix->device[i].qid); + dom = AP_QID_QUEUE(device_matrix->device[i].qid); + /* fresh fetch mkvp from adapter */ + if (fetch_mkvp(card, dom, &mkvp) == 0) { + mkvp_cache_update(card, dom, mkvp); + if (t->mkvp == mkvp) + break; + } + } + } + if (i < MAX_ZDEV_ENTRIES) { + if (pcardnr) + *pcardnr = card; + if (pdomain) + *pdomain = dom; + rc = 0; + } else + rc = -ENODEV; + + kfree(device_matrix); + return rc; +} +EXPORT_SYMBOL(pkey_findcard); + +/* + * Find card and transform secure key into protected key. + */ +int pkey_skey2pkey(const struct pkey_seckey *seckey, + struct pkey_protkey *protkey) +{ + u16 cardnr, domain; + int rc, verify; + + /* + * The pkey_sec2protkey call may fail when a card has been + * addressed where the master key was changed after last fetch + * of the mkvp into the cache. So first try without verify then + * with verify enabled (thus refreshing the mkvp for each card). + */ + for (verify = 0; verify < 2; verify++) { + rc = pkey_findcard(seckey, &cardnr, &domain, verify); + if (rc) + continue; + rc = pkey_sec2protkey(cardnr, domain, seckey, protkey); + if (rc == 0) + break; + } + + if (rc) + DEBUG_DBG("pkey_skey2pkey failed rc=%d\n", rc); + + return rc; +} +EXPORT_SYMBOL(pkey_skey2pkey); + +/* + * File io functions + */ + +static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int rc; + + switch (cmd) { + case PKEY_GENSECK: { + struct pkey_genseck __user *ugs = (void __user *) arg; + struct pkey_genseck kgs; + + if (copy_from_user(&kgs, ugs, sizeof(kgs))) + return -EFAULT; + rc = pkey_genseckey(kgs.cardnr, kgs.domain, + kgs.keytype, &kgs.seckey); + DEBUG_DBG("pkey_ioctl pkey_genseckey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ugs, &kgs, sizeof(kgs))) + return -EFAULT; + break; + } + case PKEY_CLR2SECK: { + struct pkey_clr2seck __user *ucs = (void __user *) arg; + struct pkey_clr2seck kcs; + + if (copy_from_user(&kcs, ucs, sizeof(kcs))) + return -EFAULT; + rc = pkey_clr2seckey(kcs.cardnr, kcs.domain, kcs.keytype, + &kcs.clrkey, &kcs.seckey); + DEBUG_DBG("pkey_ioctl pkey_clr2seckey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ucs, &kcs, sizeof(kcs))) + return -EFAULT; + memzero_explicit(&kcs, sizeof(kcs)); + break; + } + case PKEY_SEC2PROTK: { + struct pkey_sec2protk __user *usp = (void __user *) arg; + struct pkey_sec2protk ksp; + + if (copy_from_user(&ksp, usp, sizeof(ksp))) + return -EFAULT; + rc = pkey_sec2protkey(ksp.cardnr, ksp.domain, + &ksp.seckey, &ksp.protkey); + DEBUG_DBG("pkey_ioctl pkey_sec2protkey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(usp, &ksp, sizeof(ksp))) + return -EFAULT; + break; + } + case PKEY_CLR2PROTK: { + struct pkey_clr2protk __user *ucp = (void __user *) arg; + struct pkey_clr2protk kcp; + + if (copy_from_user(&kcp, ucp, sizeof(kcp))) + return -EFAULT; + rc = pkey_clr2protkey(kcp.keytype, + &kcp.clrkey, &kcp.protkey); + DEBUG_DBG("pkey_ioctl pkey_clr2protkey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ucp, &kcp, sizeof(kcp))) + return -EFAULT; + memzero_explicit(&kcp, sizeof(kcp)); + break; + } + case PKEY_FINDCARD: { + struct pkey_findcard __user *ufc = (void __user *) arg; + struct pkey_findcard kfc; + + if (copy_from_user(&kfc, ufc, sizeof(kfc))) + return -EFAULT; + rc = pkey_findcard(&kfc.seckey, + &kfc.cardnr, &kfc.domain, 1); + DEBUG_DBG("pkey_ioctl pkey_findcard()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ufc, &kfc, sizeof(kfc))) + return -EFAULT; + break; + } + case PKEY_SKEY2PKEY: { + struct pkey_skey2pkey __user *usp = (void __user *) arg; + struct pkey_skey2pkey ksp; + + if (copy_from_user(&ksp, usp, sizeof(ksp))) + return -EFAULT; + rc = pkey_skey2pkey(&ksp.seckey, &ksp.protkey); + DEBUG_DBG("pkey_ioctl pkey_skey2pkey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(usp, &ksp, sizeof(ksp))) + return -EFAULT; + break; + } + default: + /* unknown/unsupported ioctl cmd */ + return -ENOTTY; + } + + return rc; +} + +/* + * Sysfs and file io operations + */ +static const struct file_operations pkey_fops = { + .owner = THIS_MODULE, + .open = nonseekable_open, + .llseek = no_llseek, + .unlocked_ioctl = pkey_unlocked_ioctl, +}; + +static struct miscdevice pkey_dev = { + .name = "pkey", + .minor = MISC_DYNAMIC_MINOR, + .mode = 0666, + .fops = &pkey_fops, +}; + +/* + * Module init + */ +int __init pkey_init(void) +{ + cpacf_mask_t pckmo_functions; + + /* check for pckmo instructions available */ + if (!cpacf_query(CPACF_PCKMO, &pckmo_functions)) + return -EOPNOTSUPP; + if (!cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_128_KEY) || + !cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_192_KEY) || + !cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_256_KEY)) + return -EOPNOTSUPP; + + pkey_debug_init(); + + return misc_register(&pkey_dev); +} + +/* + * Module exit + */ +static void __exit pkey_exit(void) +{ + misc_deregister(&pkey_dev); + mkvp_cache_free(); + pkey_debug_exit(); +} + +module_init(pkey_init); +module_exit(pkey_exit); diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 144a17941e6f..93015f85d4a6 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -374,7 +374,7 @@ out: return rc; } -static long zcrypt_send_cprb(struct ica_xcRB *xcRB) +long zcrypt_send_cprb(struct ica_xcRB *xcRB) { struct zcrypt_card *zc, *pref_zc; struct zcrypt_queue *zq, *pref_zq; @@ -444,6 +444,7 @@ out: AP_QID_CARD(qid), AP_QID_QUEUE(qid)); return rc; } +EXPORT_SYMBOL(zcrypt_send_cprb); static bool is_desired_ep11_card(unsigned int dev_id, unsigned short target_num, @@ -619,7 +620,7 @@ out: return rc; } -static void zcrypt_device_status_mask(struct zcrypt_device_matrix *matrix) +void zcrypt_device_status_mask(struct zcrypt_device_matrix *matrix) { struct zcrypt_card *zc; struct zcrypt_queue *zq; diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 274a59051534..6c94efd23eac 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -190,5 +190,7 @@ void zcrypt_msgtype_unregister(struct zcrypt_ops *); struct zcrypt_ops *zcrypt_msgtype(unsigned char *, int); int zcrypt_api_init(void); void zcrypt_api_exit(void); +long zcrypt_send_cprb(struct ica_xcRB *xcRB); +void zcrypt_device_status_mask(struct zcrypt_device_matrix *devstatus); #endif /* _ZCRYPT_API_H_ */ diff --git a/drivers/s390/virtio/kvm_virtio.c b/drivers/s390/virtio/kvm_virtio.c index 5e5c11f37b24..2ce0b3eb2efe 100644 --- a/drivers/s390/virtio/kvm_virtio.c +++ b/drivers/s390/virtio/kvm_virtio.c @@ -255,7 +255,8 @@ static void kvm_del_vqs(struct virtio_device *vdev) static int kvm_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], + struct irq_affinity *desc) { struct kvm_device *kdev = to_kvmdev(vdev); int i; diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index 648373cde4a1..0ed209f3d8b0 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -628,7 +628,8 @@ out: static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], + struct irq_affinity *desc) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); unsigned long *indicatorp = NULL; diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d4023bf1e739..230043c1c90f 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1235,11 +1235,13 @@ config SCSI_QLOGICPTI source "drivers/scsi/qla2xxx/Kconfig" source "drivers/scsi/qla4xxx/Kconfig" source "drivers/scsi/qedi/Kconfig" +source "drivers/scsi/qedf/Kconfig" config SCSI_LPFC tristate "Emulex LightPulse Fibre Channel Support" depends on PCI && SCSI depends on SCSI_FC_ATTRS + depends on NVME_FC && NVME_TARGET_FC select CRC_T10DIF help This lpfc driver supports the Emulex LightPulse @@ -1478,7 +1480,7 @@ config ATARI_SCSI config MAC_SCSI tristate "Macintosh NCR5380 SCSI" - depends on MAC && SCSI=y + depends on MAC && SCSI select SCSI_SPI_ATTRS help This is the NCR 5380 SCSI controller included on most of the 68030 diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 736b77414a4b..fc2855565a51 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_FCOE) += fcoe/ obj-$(CONFIG_FCOE_FNIC) += fnic/ obj-$(CONFIG_SCSI_SNIC) += snic/ obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/ +obj-$(CONFIG_QEDF) += qedf/ obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 907f1e80665b..e3e93def722b 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -294,6 +294,10 @@ MODULE_PARM_DESC(aif_timeout, "The duration of time in seconds to wait for" "deregistering them. This is typically adjusted for heavily burdened" " systems."); +int aac_fib_dump; +module_param(aac_fib_dump, int, 0644); +MODULE_PARM_DESC(aac_fib_dump, "Dump controller fibs prior to IOP_RESET 0=off, 1=on"); + int numacb = -1; module_param(numacb, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(numacb, "Request a limit to the number of adapter control" @@ -311,7 +315,7 @@ module_param(update_interval, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(update_interval, "Interval in seconds between time sync" " updates issued to adapter."); -int check_interval = 24 * 60 * 60; +int check_interval = 60; module_param(check_interval, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(check_interval, "Interval in seconds between adapter health" " checks."); @@ -483,7 +487,7 @@ int aac_get_containers(struct aac_dev *dev) if (status >= 0) { dresp = (struct aac_get_container_count_resp *)fib_data(fibptr); maximum_num_containers = le32_to_cpu(dresp->ContainerSwitchEntries); - if (fibptr->dev->supplement_adapter_info.SupportedOptions2 & + if (fibptr->dev->supplement_adapter_info.supported_options2 & AAC_OPTION_SUPPORTED_240_VOLUMES) { maximum_num_containers = le32_to_cpu(dresp->MaxSimpleVolumes); @@ -639,13 +643,16 @@ static void _aac_probe_container2(void * context, struct fib * fibptr) fsa_dev_ptr = fibptr->dev->fsa_dev; if (fsa_dev_ptr) { struct aac_mount * dresp = (struct aac_mount *) fib_data(fibptr); + __le32 sup_options2; + fsa_dev_ptr += scmd_id(scsicmd); + sup_options2 = + fibptr->dev->supplement_adapter_info.supported_options2; if ((le32_to_cpu(dresp->status) == ST_OK) && (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) && (le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) { - if (!(fibptr->dev->supplement_adapter_info.SupportedOptions2 & - AAC_OPTION_VARIABLE_BLOCK_SIZE)) { + if (!(sup_options2 & AAC_OPTION_VARIABLE_BLOCK_SIZE)) { dresp->mnt[0].fileinfo.bdevinfo.block_size = 0x200; fsa_dev_ptr->block_size = 0x200; } else { @@ -688,7 +695,7 @@ static void _aac_probe_container1(void * context, struct fib * fibptr) int status; dresp = (struct aac_mount *) fib_data(fibptr); - if (!(fibptr->dev->supplement_adapter_info.SupportedOptions2 & + if (!(fibptr->dev->supplement_adapter_info.supported_options2 & AAC_OPTION_VARIABLE_BLOCK_SIZE)) dresp->mnt[0].capacityhigh = 0; if ((le32_to_cpu(dresp->status) != ST_OK) || @@ -705,7 +712,7 @@ static void _aac_probe_container1(void * context, struct fib * fibptr) dinfo = (struct aac_query_mount *)fib_data(fibptr); - if (fibptr->dev->supplement_adapter_info.SupportedOptions2 & + if (fibptr->dev->supplement_adapter_info.supported_options2 & AAC_OPTION_VARIABLE_BLOCK_SIZE) dinfo->command = cpu_to_le32(VM_NameServeAllBlk); else @@ -745,7 +752,7 @@ static int _aac_probe_container(struct scsi_cmnd * scsicmd, int (*callback)(stru dinfo = (struct aac_query_mount *)fib_data(fibptr); - if (fibptr->dev->supplement_adapter_info.SupportedOptions2 & + if (fibptr->dev->supplement_adapter_info.supported_options2 & AAC_OPTION_VARIABLE_BLOCK_SIZE) dinfo->command = cpu_to_le32(VM_NameServeAllBlk); else @@ -896,12 +903,14 @@ char * get_container_type(unsigned tindex) static void setinqstr(struct aac_dev *dev, void *data, int tindex) { struct scsi_inq *str; + struct aac_supplement_adapter_info *sup_adap_info; + sup_adap_info = &dev->supplement_adapter_info; str = (struct scsi_inq *)(data); /* cast data to scsi inq block */ memset(str, ' ', sizeof(*str)); - if (dev->supplement_adapter_info.AdapterTypeText[0]) { - char * cp = dev->supplement_adapter_info.AdapterTypeText; + if (sup_adap_info->adapter_type_text[0]) { + char *cp = sup_adap_info->adapter_type_text; int c; if ((cp[0] == 'A') && (cp[1] == 'O') && (cp[2] == 'C')) inqstrcpy("SMC", str->vid); @@ -911,8 +920,7 @@ static void setinqstr(struct aac_dev *dev, void *data, int tindex) ++cp; c = *cp; *cp = '\0'; - inqstrcpy (dev->supplement_adapter_info.AdapterTypeText, - str->vid); + inqstrcpy(sup_adap_info->adapter_type_text, str->vid); *cp = c; while (*cp && *cp != ' ') ++cp; @@ -1675,8 +1683,8 @@ int aac_issue_bmic_identify(struct aac_dev *dev, u32 bus, u32 target) if (!identify_resp) goto fib_free_ptr; - vbus = (u32)le16_to_cpu(dev->supplement_adapter_info.VirtDeviceBus); - vid = (u32)le16_to_cpu(dev->supplement_adapter_info.VirtDeviceTarget); + vbus = (u32)le16_to_cpu(dev->supplement_adapter_info.virt_device_bus); + vid = (u32)le16_to_cpu(dev->supplement_adapter_info.virt_device_target); aac_fib_init(fibptr); @@ -1815,9 +1823,9 @@ int aac_report_phys_luns(struct aac_dev *dev, struct fib *fibptr, int rescan) } vbus = (u32) le16_to_cpu( - dev->supplement_adapter_info.VirtDeviceBus); + dev->supplement_adapter_info.virt_device_bus); vid = (u32) le16_to_cpu( - dev->supplement_adapter_info.VirtDeviceTarget); + dev->supplement_adapter_info.virt_device_target); aac_fib_init(fibptr); @@ -1893,7 +1901,7 @@ int aac_get_adapter_info(struct aac_dev* dev) } memcpy(&dev->adapter_info, info, sizeof(*info)); - dev->supplement_adapter_info.VirtDeviceBus = 0xffff; + dev->supplement_adapter_info.virt_device_bus = 0xffff; if (dev->adapter_info.options & AAC_OPT_SUPPLEMENT_ADAPTER_INFO) { struct aac_supplement_adapter_info * sinfo; @@ -1961,7 +1969,7 @@ int aac_get_adapter_info(struct aac_dev* dev) } if (!dev->sync_mode && dev->sa_firmware && - dev->supplement_adapter_info.VirtDeviceBus != 0xffff) { + dev->supplement_adapter_info.virt_device_bus != 0xffff) { /* Thor SA Firmware -> CISS_REPORT_PHYSICAL_LUNS */ rcode = aac_report_phys_luns(dev, fibptr, AAC_INIT); } @@ -1976,8 +1984,8 @@ int aac_get_adapter_info(struct aac_dev* dev) (tmp>>16)&0xff, tmp&0xff, le32_to_cpu(dev->adapter_info.kernelbuild), - (int)sizeof(dev->supplement_adapter_info.BuildDate), - dev->supplement_adapter_info.BuildDate); + (int)sizeof(dev->supplement_adapter_info.build_date), + dev->supplement_adapter_info.build_date); tmp = le32_to_cpu(dev->adapter_info.monitorrev); printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n", dev->name, dev->id, @@ -1993,14 +2001,15 @@ int aac_get_adapter_info(struct aac_dev* dev) shost_to_class(dev->scsi_host_ptr), buffer)) printk(KERN_INFO "%s%d: serial %s", dev->name, dev->id, buffer); - if (dev->supplement_adapter_info.VpdInfo.Tsid[0]) { + if (dev->supplement_adapter_info.vpd_info.tsid[0]) { printk(KERN_INFO "%s%d: TSID %.*s\n", dev->name, dev->id, - (int)sizeof(dev->supplement_adapter_info.VpdInfo.Tsid), - dev->supplement_adapter_info.VpdInfo.Tsid); + (int)sizeof(dev->supplement_adapter_info + .vpd_info.tsid), + dev->supplement_adapter_info.vpd_info.tsid); } if (!aac_check_reset || ((aac_check_reset == 1) && - (dev->supplement_adapter_info.SupportedOptions2 & + (dev->supplement_adapter_info.supported_options2 & AAC_OPTION_IGNORE_RESET))) { printk(KERN_INFO "%s%d: Reset Adapter Ignored\n", dev->name, dev->id); @@ -2008,7 +2017,7 @@ int aac_get_adapter_info(struct aac_dev* dev) } dev->cache_protected = 0; - dev->jbod = ((dev->supplement_adapter_info.FeatureBits & + dev->jbod = ((dev->supplement_adapter_info.feature_bits & AAC_FEATURE_JBOD) != 0); dev->nondasd_support = 0; dev->raid_scsi_mode = 0; @@ -2631,7 +2640,7 @@ static int aac_start_stop(struct scsi_cmnd *scsicmd) struct scsi_device *sdev = scsicmd->device; struct aac_dev *aac = (struct aac_dev *)sdev->host->hostdata; - if (!(aac->supplement_adapter_info.SupportedOptions2 & + if (!(aac->supplement_adapter_info.supported_options2 & AAC_OPTION_POWER_MANAGEMENT)) { scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index f2344971e3cb..d036a806f31c 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -97,7 +97,7 @@ enum { #define PMC_GLOBAL_INT_BIT0 0x00000001 #ifndef AAC_DRIVER_BUILD -# define AAC_DRIVER_BUILD 50740 +# define AAC_DRIVER_BUILD 50792 # define AAC_DRIVER_BRANCH "-custom" #endif #define MAXIMUM_NUM_CONTAINERS 32 @@ -1380,57 +1380,57 @@ struct aac_adapter_info struct aac_supplement_adapter_info { - u8 AdapterTypeText[17+1]; - u8 Pad[2]; - __le32 FlashMemoryByteSize; - __le32 FlashImageId; - __le32 MaxNumberPorts; - __le32 Version; - __le32 FeatureBits; - u8 SlotNumber; - u8 ReservedPad0[3]; - u8 BuildDate[12]; - __le32 CurrentNumberPorts; + u8 adapter_type_text[17+1]; + u8 pad[2]; + __le32 flash_memory_byte_size; + __le32 flash_image_id; + __le32 max_number_ports; + __le32 version; + __le32 feature_bits; + u8 slot_number; + u8 reserved_pad0[3]; + u8 build_date[12]; + __le32 current_number_ports; struct { - u8 AssemblyPn[8]; - u8 FruPn[8]; - u8 BatteryFruPn[8]; - u8 EcVersionString[8]; - u8 Tsid[12]; - } VpdInfo; - __le32 FlashFirmwareRevision; - __le32 FlashFirmwareBuild; - __le32 RaidTypeMorphOptions; - __le32 FlashFirmwareBootRevision; - __le32 FlashFirmwareBootBuild; - u8 MfgPcbaSerialNo[12]; - u8 MfgWWNName[8]; - __le32 SupportedOptions2; - __le32 StructExpansion; + u8 assembly_pn[8]; + u8 fru_pn[8]; + u8 battery_fru_pn[8]; + u8 ec_version_string[8]; + u8 tsid[12]; + } vpd_info; + __le32 flash_firmware_revision; + __le32 flash_firmware_build; + __le32 raid_type_morph_options; + __le32 flash_firmware_boot_revision; + __le32 flash_firmware_boot_build; + u8 mfg_pcba_serial_no[12]; + u8 mfg_wwn_name[8]; + __le32 supported_options2; + __le32 struct_expansion; /* StructExpansion == 1 */ - __le32 FeatureBits3; - __le32 SupportedPerformanceModes; - u8 HostBusType; /* uses HOST_BUS_TYPE_xxx defines */ - u8 HostBusWidth; /* actual width in bits or links */ - u16 HostBusSpeed; /* actual bus speed/link rate in MHz */ - u8 MaxRRCDrives; /* max. number of ITP-RRC drives/pool */ - u8 MaxDiskXtasks; /* max. possible num of DiskX Tasks */ - - u8 CpldVerLoaded; - u8 CpldVerInFlash; - - __le64 MaxRRCCapacity; - __le32 CompiledMaxHistLogLevel; - u8 CustomBoardName[12]; - u16 SupportedCntlrMode; /* identify supported controller mode */ - u16 ReservedForFuture16; - __le32 SupportedOptions3; /* reserved for future options */ - - __le16 VirtDeviceBus; /* virt. SCSI device for Thor */ - __le16 VirtDeviceTarget; - __le16 VirtDeviceLUN; - __le16 Unused; - __le32 ReservedForFutureGrowth[68]; + __le32 feature_bits3; + __le32 supported_performance_modes; + u8 host_bus_type; /* uses HOST_BUS_TYPE_xxx defines */ + u8 host_bus_width; /* actual width in bits or links */ + u16 host_bus_speed; /* actual bus speed/link rate in MHz */ + u8 max_rrc_drives; /* max. number of ITP-RRC drives/pool */ + u8 max_disk_xtasks; /* max. possible num of DiskX Tasks */ + + u8 cpld_ver_loaded; + u8 cpld_ver_in_flash; + + __le64 max_rrc_capacity; + __le32 compiled_max_hist_log_level; + u8 custom_board_name[12]; + u16 supported_cntlr_mode; /* identify supported controller mode */ + u16 reserved_for_future16; + __le32 supported_options3; /* reserved for future options */ + + __le16 virt_device_bus; /* virt. SCSI device for Thor */ + __le16 virt_device_target; + __le16 virt_device_lun; + __le16 unused; + __le32 reserved_for_future_growth[68]; }; #define AAC_FEATURE_FALCON cpu_to_le32(0x00000010) @@ -1444,6 +1444,10 @@ struct aac_supplement_adapter_info #define AAC_OPTION_VARIABLE_BLOCK_SIZE cpu_to_le32(0x00040000) /* 240 simple volume support */ #define AAC_OPTION_SUPPORTED_240_VOLUMES cpu_to_le32(0x10000000) +/* + * Supports FIB dump sync command send prior to IOP_RESET + */ +#define AAC_OPTION_SUPPORTED3_IOP_RESET_FIB_DUMP cpu_to_le32(0x00004000) #define AAC_SIS_VERSION_V3 3 #define AAC_SIS_SLOT_UNKNOWN 0xFF @@ -2483,6 +2487,7 @@ struct aac_hba_info { #define GET_DRIVER_BUFFER_PROPERTIES 0x00000023 #define RCV_TEMP_READINGS 0x00000025 #define GET_COMM_PREFERRED_SETTINGS 0x00000026 +#define IOP_RESET_FW_FIB_DUMP 0x00000034 #define IOP_RESET 0x00001000 #define IOP_RESET_ALWAYS 0x00001001 #define RE_INIT_ADAPTER 0x000000ee @@ -2639,6 +2644,7 @@ void aac_hba_callback(void *context, struct fib *fibptr); #define fib_data(fibctx) ((void *)(fibctx)->hw_fib_va->data) struct aac_dev *aac_init_adapter(struct aac_dev *dev); void aac_src_access_devreg(struct aac_dev *dev, int mode); +void aac_set_intx_mode(struct aac_dev *dev); int aac_get_config_status(struct aac_dev *dev, int commit_flag); int aac_get_containers(struct aac_dev *dev); int aac_scsi_cmd(struct scsi_cmnd *cmd); @@ -2685,4 +2691,5 @@ extern int aac_commit; extern int update_interval; extern int check_interval; extern int aac_check_reset; +extern int aac_fib_dump; #endif diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index 614842a9eb07..f6afd50579c0 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -580,7 +580,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) goto cleanup; } - chn = aac_logical_to_phys(user_srbcmd->channel); + chn = user_srbcmd->channel; if (chn < AAC_MAX_BUSES && user_srbcmd->id < AAC_MAX_TARGETS && dev->hba_map[chn][user_srbcmd->id].devtype == AAC_DEVTYPE_NATIVE_RAW) { diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index 40bfc57b6849..35607005f7e1 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -330,7 +330,7 @@ int aac_send_shutdown(struct aac_dev * dev) dev->pdev->device == PMC_DEVICE_S8 || dev->pdev->device == PMC_DEVICE_S9) && dev->msi_enabled) - aac_src_access_devreg(dev, AAC_ENABLE_INTX); + aac_set_intx_mode(dev); return status; } diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 969727b67cdd..a3ad04293487 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -95,12 +95,20 @@ static int fib_map_alloc(struct aac_dev *dev) void aac_fib_map_free(struct aac_dev *dev) { - if (dev->hw_fib_va && dev->max_cmd_size) { - pci_free_consistent(dev->pdev, - (dev->max_cmd_size * - (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)), - dev->hw_fib_va, dev->hw_fib_pa); - } + size_t alloc_size; + size_t fib_size; + int num_fibs; + + if(!dev->hw_fib_va || !dev->max_cmd_size) + return; + + num_fibs = dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB; + fib_size = dev->max_fib_size + sizeof(struct aac_fib_xporthdr); + alloc_size = fib_size * num_fibs + ALIGN32 - 1; + + pci_free_consistent(dev->pdev, alloc_size, dev->hw_fib_va, + dev->hw_fib_pa); + dev->hw_fib_va = NULL; dev->hw_fib_pa = 0; } @@ -153,22 +161,20 @@ int aac_fib_setup(struct aac_dev * dev) if (i<0) return -ENOMEM; - /* 32 byte alignment for PMC */ - hw_fib_pa = (dev->hw_fib_pa + (ALIGN32 - 1)) & ~(ALIGN32 - 1); - dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va + - (hw_fib_pa - dev->hw_fib_pa)); - dev->hw_fib_pa = hw_fib_pa; memset(dev->hw_fib_va, 0, (dev->max_cmd_size + sizeof(struct aac_fib_xporthdr)) * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)); + /* 32 byte alignment for PMC */ + hw_fib_pa = (dev->hw_fib_pa + (ALIGN32 - 1)) & ~(ALIGN32 - 1); + hw_fib = (struct hw_fib *)((unsigned char *)dev->hw_fib_va + + (hw_fib_pa - dev->hw_fib_pa)); + /* add Xport header */ - dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va + + hw_fib = (struct hw_fib *)((unsigned char *)hw_fib + sizeof(struct aac_fib_xporthdr)); - dev->hw_fib_pa += sizeof(struct aac_fib_xporthdr); + hw_fib_pa += sizeof(struct aac_fib_xporthdr); - hw_fib = dev->hw_fib_va; - hw_fib_pa = dev->hw_fib_pa; /* * Initialise the fibs */ @@ -461,6 +467,35 @@ int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw return 0; } +#ifdef CONFIG_EEH +static inline int aac_check_eeh_failure(struct aac_dev *dev) +{ + /* Check for an EEH failure for the given + * device node. Function eeh_dev_check_failure() + * returns 0 if there has not been an EEH error + * otherwise returns a non-zero value. + * + * Need to be called before any PCI operation, + * i.e.,before aac_adapter_check_health() + */ + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev->pdev); + + if (eeh_dev_check_failure(edev)) { + /* The EEH mechanisms will handle this + * error and reset the device if + * necessary. + */ + return 1; + } + return 0; +} +#else +static inline int aac_check_eeh_failure(struct aac_dev *dev) +{ + return 0; +} +#endif + /* * Define the highest level of host to adapter communication routines. * These routines will support host to adapter FS commuication. These @@ -496,9 +531,12 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, unsigned long mflags = 0; unsigned long sflags = 0; - if (!(hw_fib->header.XferState & cpu_to_le32(HostOwned))) return -EBUSY; + + if (hw_fib->header.XferState & cpu_to_le32(AdapterProcessed)) + return -EINVAL; + /* * There are 5 cases with the wait and response requested flags. * The only invalid cases are if the caller requests to wait and @@ -662,6 +700,10 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, } return -ETIMEDOUT; } + + if (aac_check_eeh_failure(dev)) + return -EFAULT; + if ((blink = aac_adapter_check_health(dev)) > 0) { if (wait == -1) { printk(KERN_ERR "aacraid: aac_fib_send: adapter blinkLED 0x%x.\n" @@ -755,7 +797,12 @@ int aac_hba_send(u8 command, struct fib *fibptr, fib_callback callback, FIB_COUNTER_INCREMENT(aac_config.NativeSent); if (wait) { + spin_unlock_irqrestore(&fibptr->event_lock, flags); + + if (aac_check_eeh_failure(dev)) + return -EFAULT; + /* Only set for first known interruptable command */ if (down_interruptible(&fibptr->event_wait)) { fibptr->done = 2; @@ -1590,11 +1637,29 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type) command->SCp.phase = AAC_OWNER_ERROR_HANDLER; command->scsi_done(command); } + /* + * Any Device that was already marked offline needs to be cleaned up + */ + __shost_for_each_device(dev, host) { + if (!scsi_device_online(dev)) { + sdev_printk(KERN_INFO, dev, "Removing offline device\n"); + scsi_remove_device(dev); + scsi_device_put(dev); + } + } retval = 0; out: aac->in_reset = 0; scsi_unblock_requests(host); + /* + * Issue bus rescan to catch any configuration that might have + * occurred + */ + if (!retval) { + dev_info(&aac->pdev->dev, "Issuing bus rescan\n"); + scsi_scan_host(host); + } if (jafo) { spin_lock_irq(host->host_lock); } @@ -1815,7 +1880,7 @@ int aac_check_health(struct aac_dev * aac) printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); if (!aac_check_reset || ((aac_check_reset == 1) && - (aac->supplement_adapter_info.SupportedOptions2 & + (aac->supplement_adapter_info.supported_options2 & AAC_OPTION_IGNORE_RESET))) goto out; host = aac->scsi_host_ptr; @@ -1843,9 +1908,6 @@ static void aac_resolve_luns(struct aac_dev *dev) for (bus = 0; bus < AAC_MAX_BUSES; bus++) { for (target = 0; target < AAC_MAX_TARGETS; target++) { - if (aac_phys_to_logical(bus) == ENCLOSURE_CHANNEL) - continue; - if (bus == CONTAINER_CHANNEL) channel = CONTAINER_CHANNEL; else @@ -1857,7 +1919,7 @@ static void aac_resolve_luns(struct aac_dev *dev) sdev = scsi_device_lookup(dev->scsi_host_ptr, channel, target, 0); - if (!sdev && devtype) + if (!sdev && new_devtype) scsi_add_device(dev->scsi_host_ptr, channel, target, 0); else if (sdev && new_devtype != devtype) @@ -2150,7 +2212,7 @@ static void aac_process_events(struct aac_dev *dev) /* Thor AIF */ aac_handle_sa_aif(dev, fib); aac_fib_adapter_complete(fib, (u16)sizeof(u32)); - continue; + goto free_fib; } /* * We will process the FIB here or pass it to a @@ -2264,8 +2326,8 @@ static int aac_send_wellness_command(struct aac_dev *dev, char *wellness_str, aac_fib_init(fibptr); - vbus = (u32)le16_to_cpu(dev->supplement_adapter_info.VirtDeviceBus); - vid = (u32)le16_to_cpu(dev->supplement_adapter_info.VirtDeviceTarget); + vbus = (u32)le16_to_cpu(dev->supplement_adapter_info.virt_device_bus); + vid = (u32)le16_to_cpu(dev->supplement_adapter_info.virt_device_target); srbcmd = (struct aac_srb *)fib_data(fibptr); @@ -2434,7 +2496,7 @@ int aac_command_thread(void *data) /* Don't even try to talk to adapter if its sick */ ret = aac_check_health(dev); - if (!dev->queues) + if (ret || !dev->queues) break; next_check_jiffies = jiffies + ((long)(unsigned)check_interval) @@ -2446,8 +2508,7 @@ int aac_command_thread(void *data) && (now.tv_usec > (1000000 / HZ))) difference = (((1000000 - now.tv_usec) * HZ) + 500000) / 1000000; - else if (ret == 0) { - + else { if (now.tv_usec > 500000) ++now.tv_sec; @@ -2458,9 +2519,6 @@ int aac_command_thread(void *data) ret = aac_send_hosttime(dev, &now); difference = (long)(unsigned)update_interval*HZ; - } else { - /* retry shortly */ - difference = 10 * HZ; } next_jiffies = jiffies + difference; if (time_before(next_check_jiffies,next_jiffies)) diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 137d22d3a005..520ada8266af 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -891,13 +891,13 @@ static int aac_eh_reset(struct scsi_cmnd* cmd) * Adapters that support a register, instead of a commanded, * reset. */ - if (((aac->supplement_adapter_info.SupportedOptions2 & + if (((aac->supplement_adapter_info.supported_options2 & AAC_OPTION_MU_RESET) || - (aac->supplement_adapter_info.SupportedOptions2 & + (aac->supplement_adapter_info.supported_options2 & AAC_OPTION_DOORBELL_RESET)) && aac_check_reset && ((aac_check_reset != 1) || - !(aac->supplement_adapter_info.SupportedOptions2 & + !(aac->supplement_adapter_info.supported_options2 & AAC_OPTION_IGNORE_RESET))) { /* Bypass wait for command quiesce */ aac_reset_adapter(aac, 2, IOP_HWSOFT_RESET); @@ -1029,8 +1029,8 @@ static ssize_t aac_show_model(struct device *device, struct aac_dev *dev = (struct aac_dev*)class_to_shost(device)->hostdata; int len; - if (dev->supplement_adapter_info.AdapterTypeText[0]) { - char * cp = dev->supplement_adapter_info.AdapterTypeText; + if (dev->supplement_adapter_info.adapter_type_text[0]) { + char *cp = dev->supplement_adapter_info.adapter_type_text; while (*cp && *cp != ' ') ++cp; while (*cp == ' ') @@ -1046,18 +1046,20 @@ static ssize_t aac_show_vendor(struct device *device, struct device_attribute *attr, char *buf) { struct aac_dev *dev = (struct aac_dev*)class_to_shost(device)->hostdata; + struct aac_supplement_adapter_info *sup_adap_info; int len; - if (dev->supplement_adapter_info.AdapterTypeText[0]) { - char * cp = dev->supplement_adapter_info.AdapterTypeText; + sup_adap_info = &dev->supplement_adapter_info; + if (sup_adap_info->adapter_type_text[0]) { + char *cp = sup_adap_info->adapter_type_text; while (*cp && *cp != ' ') ++cp; len = snprintf(buf, PAGE_SIZE, "%.*s\n", - (int)(cp - (char *)dev->supplement_adapter_info.AdapterTypeText), - dev->supplement_adapter_info.AdapterTypeText); + (int)(cp - (char *)sup_adap_info->adapter_type_text), + sup_adap_info->adapter_type_text); } else len = snprintf(buf, PAGE_SIZE, "%s\n", - aac_drivers[dev->cardtype].vname); + aac_drivers[dev->cardtype].vname); return len; } @@ -1078,7 +1080,7 @@ static ssize_t aac_show_flags(struct device *cdev, "SAI_READ_CAPACITY_16\n"); if (dev->jbod) len += snprintf(buf + len, PAGE_SIZE - len, "SUPPORTED_JBOD\n"); - if (dev->supplement_adapter_info.SupportedOptions2 & + if (dev->supplement_adapter_info.supported_options2 & AAC_OPTION_POWER_MANAGEMENT) len += snprintf(buf + len, PAGE_SIZE - len, "SUPPORTED_POWER_MANAGEMENT\n"); @@ -1129,6 +1131,13 @@ static ssize_t aac_show_bios_version(struct device *device, return len; } +static ssize_t aac_show_driver_version(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", aac_driver_version); +} + static ssize_t aac_show_serial_number(struct device *device, struct device_attribute *attr, char *buf) { @@ -1139,12 +1148,12 @@ static ssize_t aac_show_serial_number(struct device *device, len = snprintf(buf, 16, "%06X\n", le32_to_cpu(dev->adapter_info.serial[0])); if (len && - !memcmp(&dev->supplement_adapter_info.MfgPcbaSerialNo[ - sizeof(dev->supplement_adapter_info.MfgPcbaSerialNo)-len], + !memcmp(&dev->supplement_adapter_info.mfg_pcba_serial_no[ + sizeof(dev->supplement_adapter_info.mfg_pcba_serial_no)-len], buf, len-1)) len = snprintf(buf, 16, "%.*s\n", - (int)sizeof(dev->supplement_adapter_info.MfgPcbaSerialNo), - dev->supplement_adapter_info.MfgPcbaSerialNo); + (int)sizeof(dev->supplement_adapter_info.mfg_pcba_serial_no), + dev->supplement_adapter_info.mfg_pcba_serial_no); return min(len, 16); } @@ -1239,6 +1248,13 @@ static struct device_attribute aac_bios_version = { }, .show = aac_show_bios_version, }; +static struct device_attribute aac_lld_version = { + .attr = { + .name = "driver_version", + .mode = 0444, + }, + .show = aac_show_driver_version, +}; static struct device_attribute aac_serial_number = { .attr = { .name = "serial_number", @@ -1276,6 +1292,7 @@ static struct device_attribute *aac_attrs[] = { &aac_kernel_version, &aac_monitor_version, &aac_bios_version, + &aac_lld_version, &aac_serial_number, &aac_max_channel, &aac_max_id, @@ -1630,7 +1647,7 @@ static int aac_acquire_resources(struct aac_dev *dev) if (!dev->sync_mode) { /* After EEH recovery or suspend resume, max_msix count - * may change, therfore updating in init as well. + * may change, therefore updating in init as well. */ dev->init->r7.no_of_msix_vectors = cpu_to_le32(dev->max_msix); aac_adapter_start(dev); diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c index 0e69a80c3275..5d19c31e3bba 100644 --- a/drivers/scsi/aacraid/rx.c +++ b/drivers/scsi/aacraid/rx.c @@ -475,7 +475,7 @@ static int aac_rx_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type) { u32 var = 0; - if (!(dev->supplement_adapter_info.SupportedOptions2 & + if (!(dev->supplement_adapter_info.supported_options2 & AAC_OPTION_MU_RESET) || (bled >= 0) || (bled == -2)) { if (bled) printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n", diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index 8e4e2ddbafd7..2e5338dec621 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -437,16 +437,23 @@ static int aac_src_check_health(struct aac_dev *dev) u32 status = src_readl(dev, MUnit.OMR); /* + * Check to see if the board panic'd. + */ + if (unlikely(status & KERNEL_PANIC)) + goto err_blink; + + /* * Check to see if the board failed any self tests. */ if (unlikely(status & SELF_TEST_FAILED)) - return -1; + goto err_out; /* - * Check to see if the board panic'd. + * Check to see if the board failed any self tests. */ - if (unlikely(status & KERNEL_PANIC)) - return (status >> 16) & 0xFF; + if (unlikely(status & MONITOR_PANIC)) + goto err_out; + /* * Wait for the adapter to be up and running. */ @@ -456,6 +463,12 @@ static int aac_src_check_health(struct aac_dev *dev) * Everything is OK */ return 0; + +err_out: + return -1; + +err_blink: + return (status > 16) & 0xFF; } static inline u32 aac_get_vector(struct aac_dev *dev) @@ -657,7 +670,7 @@ static int aac_srcv_ioremap(struct aac_dev *dev, u32 size) return 0; } -static void aac_set_intx_mode(struct aac_dev *dev) +void aac_set_intx_mode(struct aac_dev *dev) { if (dev->msi_enabled) { aac_src_access_devreg(dev, AAC_ENABLE_INTX); @@ -666,10 +679,27 @@ static void aac_set_intx_mode(struct aac_dev *dev) } } +static void aac_dump_fw_fib_iop_reset(struct aac_dev *dev) +{ + __le32 supported_options3; + + if (!aac_fib_dump) + return; + + supported_options3 = dev->supplement_adapter_info.supported_options3; + if (!(supported_options3 & AAC_OPTION_SUPPORTED3_IOP_RESET_FIB_DUMP)) + return; + + aac_adapter_sync_cmd(dev, IOP_RESET_FW_FIB_DUMP, + 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL); +} + static void aac_send_iop_reset(struct aac_dev *dev, int bled) { u32 var, reset_mask; + aac_dump_fw_fib_iop_reset(dev); + bled = aac_adapter_sync_cmd(dev, IOP_RESET_ALWAYS, 0, 0, 0, 0, 0, 0, &var, &reset_mask, NULL, NULL, NULL); @@ -684,7 +714,7 @@ static void aac_send_iop_reset(struct aac_dev *dev, int bled) aac_set_intx_mode(dev); - if (!bled && (dev->supplement_adapter_info.SupportedOptions2 & + if (!bled && (dev->supplement_adapter_info.supported_options2 & AAC_OPTION_DOORBELL_RESET)) { src_writel(dev, MUnit.IDR, reset_mask); } else { @@ -714,6 +744,12 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled, u8 reset_type) pr_err("%s%d: adapter kernel panic'd %x.\n", dev->name, dev->id, bled); + /* + * When there is a BlinkLED, IOP_RESET has not effect + */ + if (bled >= 2 && dev->sa_firmware && reset_type & HW_IOP_RESET) + reset_type &= ~HW_IOP_RESET; + dev->a_ops.adapter_enable_int = aac_src_disable_interrupt; switch (reset_type) { diff --git a/drivers/scsi/bfa/bfi_ms.h b/drivers/scsi/bfa/bfi_ms.h index ae5bfe039fcc..ccbd9e31a5de 100644 --- a/drivers/scsi/bfa/bfi_ms.h +++ b/drivers/scsi/bfa/bfi_ms.h @@ -680,7 +680,7 @@ struct bfi_ioim_req_s { /* * SG elements array within the IO request must be double word - * aligned. This aligment is required to optimize SGM setup for the IO. + * aligned. This alignment is required to optimize SGM setup for the IO. */ struct bfi_sge_s sges[BFI_SGE_INLINE_MAX]; u8 io_timeout; diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index fdd4eb4e41b2..4fc8ed5fe067 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -39,7 +39,7 @@ #include <linux/bitops.h> #include <linux/log2.h> #include <linux/interrupt.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/io.h> #include <scsi/scsi.h> diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index ed7f3228e234..89ef1a1678d1 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -25,7 +25,7 @@ #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/in.h> #include <linux/kfifo.h> #include <linux/netdevice.h> diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 7069639e92bc..3061d8045382 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -2259,6 +2259,8 @@ static struct dev_dependent_vals dev_corsa_vals = { CXLFLASH_MAX_SECTORS, 0ULL }; static struct dev_dependent_vals dev_flash_gt_vals = { CXLFLASH_MAX_SECTORS, CXLFLASH_NOTIFY_SHUTDOWN }; +static struct dev_dependent_vals dev_briard_vals = { CXLFLASH_MAX_SECTORS, + CXLFLASH_NOTIFY_SHUTDOWN }; /* * PCI device binding table @@ -2268,6 +2270,8 @@ static struct pci_device_id cxlflash_pci_table[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_corsa_vals}, {PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_FLASH_GT, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_flash_gt_vals}, + {PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_BRIARD, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_briard_vals}, {} }; diff --git a/drivers/scsi/cxlflash/main.h b/drivers/scsi/cxlflash/main.h index e43545c86bcf..0be2261e6312 100644 --- a/drivers/scsi/cxlflash/main.h +++ b/drivers/scsi/cxlflash/main.h @@ -25,6 +25,7 @@ #define PCI_DEVICE_ID_IBM_CORSA 0x04F0 #define PCI_DEVICE_ID_IBM_FLASH_GT 0x0600 +#define PCI_DEVICE_ID_IBM_BRIARD 0x0624 /* Since there is only one target, make it 0 */ #define CXLFLASH_TARGET 0 diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index 90869cee2b20..b46fd2f45628 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -305,6 +305,7 @@ static int read_cap16(struct scsi_device *sdev, struct llun_info *lli) struct cxlflash_cfg *cfg = shost_priv(sdev->host); struct device *dev = &cfg->dev->dev; struct glun_info *gli = lli->parent; + struct scsi_sense_hdr sshdr; u8 *cmd_buf = NULL; u8 *scsi_cmd = NULL; u8 *sense_buf = NULL; @@ -332,7 +333,8 @@ retry: /* Drop the ioctl read semahpore across lengthy call */ up_read(&cfg->ioctl_rwsem); result = scsi_execute(sdev, scsi_cmd, DMA_FROM_DEVICE, cmd_buf, - CMD_BUFSIZE, sense_buf, to, CMD_RETRIES, 0, NULL); + CMD_BUFSIZE, sense_buf, &sshdr, to, CMD_RETRIES, + 0, 0, NULL); down_read(&cfg->ioctl_rwsem); rc = check_state(cfg); if (rc) { @@ -345,10 +347,6 @@ retry: if (driver_byte(result) == DRIVER_SENSE) { result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */ if (result & SAM_STAT_CHECK_CONDITION) { - struct scsi_sense_hdr sshdr; - - scsi_normalize_sense(sense_buf, SCSI_SENSE_BUFFERSIZE, - &sshdr); switch (sshdr.sense_key) { case NO_SENSE: case RECOVERED_ERROR: @@ -1053,7 +1051,6 @@ out: /** * cxlflash_mmap_fault() - mmap fault handler for adapter file descriptor - * @vma: VM area associated with mapping. * @vmf: VM fault associated with current fault. * * To support error notification via MMIO, faults are 'caught' by this routine @@ -1067,8 +1064,9 @@ out: * * Return: 0 on success, VM_FAULT_SIGBUS on failure */ -static int cxlflash_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int cxlflash_mmap_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct file *file = vma->vm_file; struct cxl_context *ctx = cxl_fops_get_context(file); struct cxlflash_cfg *cfg = container_of(file->f_op, struct cxlflash_cfg, @@ -1097,7 +1095,7 @@ static int cxlflash_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (likely(!ctxi->err_recovery_active)) { vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - rc = ctxi->cxl_mmap_vmops->fault(vma, vmf); + rc = ctxi->cxl_mmap_vmops->fault(vmf); } else { dev_dbg(dev, "%s: err recovery active, use err_page\n", __func__); diff --git a/drivers/scsi/cxlflash/vlun.c b/drivers/scsi/cxlflash/vlun.c index 8fcc804dbef9..7aa06ef229fd 100644 --- a/drivers/scsi/cxlflash/vlun.c +++ b/drivers/scsi/cxlflash/vlun.c @@ -453,8 +453,8 @@ static int write_same16(struct scsi_device *sdev, /* Drop the ioctl read semahpore across lengthy call */ up_read(&cfg->ioctl_rwsem); result = scsi_execute(sdev, scsi_cmd, DMA_TO_DEVICE, cmd_buf, - CMD_BUFSIZE, sense_buf, to, CMD_RETRIES, - 0, NULL); + CMD_BUFSIZE, sense_buf, NULL, to, + CMD_RETRIES, 0, 0, NULL); down_read(&cfg->ioctl_rwsem); rc = check_state(cfg); if (rc) { diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index d704752b6332..48e200102221 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -151,11 +151,9 @@ static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff, cdb[1] = MI_REPORT_TARGET_PGS; put_unaligned_be32(bufflen, &cdb[6]); - return scsi_execute_req_flags(sdev, cdb, DMA_FROM_DEVICE, - buff, bufflen, sshdr, - ALUA_FAILOVER_TIMEOUT * HZ, - ALUA_FAILOVER_RETRIES, NULL, - req_flags, 0); + return scsi_execute(sdev, cdb, DMA_FROM_DEVICE, buff, bufflen, NULL, + sshdr, ALUA_FAILOVER_TIMEOUT * HZ, + ALUA_FAILOVER_RETRIES, req_flags, 0, NULL); } /* @@ -185,11 +183,9 @@ static int submit_stpg(struct scsi_device *sdev, int group_id, cdb[1] = MO_SET_TARGET_PGS; put_unaligned_be32(stpg_len, &cdb[6]); - return scsi_execute_req_flags(sdev, cdb, DMA_TO_DEVICE, - stpg_data, stpg_len, - sshdr, ALUA_FAILOVER_TIMEOUT * HZ, - ALUA_FAILOVER_RETRIES, NULL, - req_flags, 0); + return scsi_execute(sdev, cdb, DMA_TO_DEVICE, stpg_data, stpg_len, NULL, + sshdr, ALUA_FAILOVER_TIMEOUT * HZ, + ALUA_FAILOVER_RETRIES, req_flags, 0, NULL); } static struct alua_port_group *alua_find_get_pg(char *id_str, size_t id_size, diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 4a7679f6c73d..8654e940e1a8 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -276,10 +276,9 @@ static int send_trespass_cmd(struct scsi_device *sdev, BUG_ON((len > CLARIION_BUFFER_SIZE)); memcpy(csdev->buffer, page22, len); - err = scsi_execute_req_flags(sdev, cdb, DMA_TO_DEVICE, - csdev->buffer, len, &sshdr, - CLARIION_TIMEOUT * HZ, CLARIION_RETRIES, - NULL, req_flags, 0); + err = scsi_execute(sdev, cdb, DMA_TO_DEVICE, csdev->buffer, len, NULL, + &sshdr, CLARIION_TIMEOUT * HZ, CLARIION_RETRIES, + req_flags, 0, NULL); if (err) { if (scsi_sense_valid(&sshdr)) res = trespass_endio(sdev, &sshdr); @@ -358,7 +357,7 @@ static int clariion_prep_fn(struct scsi_device *sdev, struct request *req) static int clariion_std_inquiry(struct scsi_device *sdev, struct clariion_dh_data *csdev) { - int err; + int err = SCSI_DH_OK; char *sp_model; sp_model = parse_sp_model(sdev, sdev->inquiry); diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index be43c940636d..62d314e07d11 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -100,9 +100,8 @@ static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) REQ_FAILFAST_DRIVER; retry: - res = scsi_execute_req_flags(sdev, cmd, DMA_NONE, NULL, 0, &sshdr, - HP_SW_TIMEOUT, HP_SW_RETRIES, - NULL, req_flags, 0); + res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, + HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL); if (res) { if (scsi_sense_valid(&sshdr)) ret = tur_done(sdev, h, &sshdr); @@ -139,9 +138,8 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h) REQ_FAILFAST_DRIVER; retry: - res = scsi_execute_req_flags(sdev, cmd, DMA_NONE, NULL, 0, &sshdr, - HP_SW_TIMEOUT, HP_SW_RETRIES, - NULL, req_flags, 0); + res = scsi_execute(sdev, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, + HP_SW_TIMEOUT, HP_SW_RETRIES, req_flags, 0, NULL); if (res) { if (!scsi_sense_valid(&sshdr)) { sdev_printk(KERN_WARNING, sdev, diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index b64eaae8533d..3cbab8710e58 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -555,10 +555,9 @@ static void send_mode_select(struct work_struct *work) (char *) h->ctlr->array_name, h->ctlr->index, (retry_cnt == RDAC_RETRY_COUNT) ? "queueing" : "retrying"); - if (scsi_execute_req_flags(sdev, cdb, DMA_TO_DEVICE, - &h->ctlr->mode_select, data_size, &sshdr, - RDAC_TIMEOUT * HZ, - RDAC_RETRIES, NULL, req_flags, 0)) { + if (scsi_execute(sdev, cdb, DMA_TO_DEVICE, &h->ctlr->mode_select, + data_size, NULL, &sshdr, RDAC_TIMEOUT * HZ, + RDAC_RETRIES, req_flags, 0, NULL)) { err = mode_select_handle_sense(sdev, &sshdr); if (err == SCSI_DH_RETRY && retry_cnt--) goto retry; diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index cea57e27e713..656463ff9ccb 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1387,7 +1387,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, /* * Actually need to subtract 'sizeof(*mp) - sizeof(*wp)' from 'rlen' * before determining max Vx_Port descriptor but a buggy FCF could have - * omited either or both MAC Address and Name Identifier descriptors + * omitted either or both MAC Address and Name Identifier descriptors */ num_vlink_desc = rlen / sizeof(*vp); if (num_vlink_desc) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 835c59c777f2..b29afafc2885 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -9330,7 +9330,7 @@ static pci_ers_result_t ipr_pci_error_detected(struct pci_dev *pdev, * ipr_probe_ioa_part2 - Initializes IOAs found in ipr_probe_ioa(..) * @ioa_cfg: ioa cfg struct * - * Description: This is the second phase of adapter intialization + * Description: This is the second phase of adapter initialization * This function takes care of initilizing the adapter to the point * where it can accept new commands. diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 6103231104da..fd501f8dbb11 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -36,6 +36,8 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/export.h> +#include <linux/rculist.h> + #include <asm/unaligned.h> #include <scsi/fc/fc_gs.h> diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index c991f3b822f8..b44c3136eb51 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -65,6 +65,8 @@ #include <linux/timer.h> #include <linux/workqueue.h> #include <linux/export.h> +#include <linux/rculist.h> + #include <asm/unaligned.h> #include <scsi/libfc.h> diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 834d1212b6d5..07c08ce68d70 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/slab.h> +#include <linux/sched/signal.h> #include <linux/module.h> #include <asm/unaligned.h> #include <net/tcp.h> diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile index e2516ba8ebfa..cb6aa802c48e 100644 --- a/drivers/scsi/lpfc/Makefile +++ b/drivers/scsi/lpfc/Makefile @@ -1,9 +1,11 @@ #/******************************************************************* # * This file is part of the Emulex Linux Device Driver for * # * Fibre Channel Host Bus Adapters. * +# * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * +# * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * # * Copyright (C) 2004-2012 Emulex. All rights reserved. * # * EMULEX and SLI are trademarks of Emulex. * -# * www.emulex.com * +# * www.broadcom.com * # * * # * This program is free software; you can redistribute it and/or * # * modify it under the terms of version 2 of the GNU General * @@ -28,6 +30,7 @@ endif obj-$(CONFIG_SCSI_LPFC) := lpfc.o -lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o lpfc_hbadisc.o \ - lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsi.o lpfc_attr.o \ - lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o +lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \ + lpfc_hbadisc.o lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o \ + lpfc_scsi.o lpfc_attr.o lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o \ + lpfc_nvme.o lpfc_nvmet.o diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 6593b073c524..0bba2e30b4f0 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -20,6 +22,7 @@ *******************************************************************/ #include <scsi/scsi_host.h> +#include <linux/ktime.h> #if defined(CONFIG_DEBUG_FS) && !defined(CONFIG_SCSI_LPFC_DEBUG_FS) #define CONFIG_SCSI_LPFC_DEBUG_FS @@ -53,6 +56,7 @@ struct lpfc_sli2_slim; #define LPFC_MAX_SG_SEG_CNT 4096 /* sg element count per scsi cmnd */ #define LPFC_MAX_SGL_SEG_CNT 512 /* SGL element count per scsi cmnd */ #define LPFC_MAX_BPL_SEG_CNT 4096 /* BPL element count per scsi cmnd */ +#define LPFC_MIN_NVME_SEG_CNT 254 #define LPFC_MAX_SGE_SIZE 0x80000000 /* Maximum data allowed in a SGE */ #define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */ @@ -114,6 +118,20 @@ enum lpfc_polling_flags { DISABLE_FCP_RING_INT = 0x2 }; +struct perf_prof { + uint16_t cmd_cpu[40]; + uint16_t rsp_cpu[40]; + uint16_t qh_cpu[40]; + uint16_t wqidx[40]; +}; + +/* + * Provide for FC4 TYPE x28 - NVME. The + * bit mask for FCP and NVME is 0x8 identically + * because they are 32 bit positions distance. + */ +#define LPFC_FC4_TYPE_BITMASK 0x00000100 + /* Provide DMA memory definitions the driver uses per port instance. */ struct lpfc_dmabuf { struct list_head list; @@ -131,10 +149,24 @@ struct lpfc_dma_pool { struct hbq_dmabuf { struct lpfc_dmabuf hbuf; struct lpfc_dmabuf dbuf; - uint32_t size; + uint16_t total_size; + uint16_t bytes_recv; uint32_t tag; struct lpfc_cq_event cq_event; unsigned long time_stamp; + void *context; +}; + +struct rqb_dmabuf { + struct lpfc_dmabuf hbuf; + struct lpfc_dmabuf dbuf; + uint16_t total_size; + uint16_t bytes_recv; + void *context; + struct lpfc_iocbq *iocbq; + struct lpfc_sglq *sglq; + struct lpfc_queue *hrq; /* ptr to associated Header RQ */ + struct lpfc_queue *drq; /* ptr to associated Data RQ */ }; /* Priority bit. Set value to exceed low water mark in lpfc_mem. */ @@ -367,7 +399,8 @@ struct lpfc_vport { int32_t stopped; /* HBA has not been restarted since last ERATT */ uint8_t fc_linkspeed; /* Link speed after last READ_LA */ - uint32_t num_disc_nodes; /*in addition to hba_state */ + uint32_t num_disc_nodes; /* in addition to hba_state */ + uint32_t gidft_inp; /* cnt of outstanding GID_FTs */ uint32_t fc_nlp_cnt; /* outstanding NODELIST requests */ uint32_t fc_rscn_id_cnt; /* count of RSCNs payloads in list */ @@ -420,7 +453,6 @@ struct lpfc_vport { uint32_t cfg_max_scsicmpl_time; uint32_t cfg_tgt_queue_depth; uint32_t cfg_first_burst_size; - uint32_t dev_loss_tmo_changed; struct fc_vport *fc_vport; @@ -428,6 +460,9 @@ struct lpfc_vport { #ifdef CONFIG_SCSI_LPFC_DEBUG_FS struct dentry *debug_disc_trc; struct dentry *debug_nodelist; + struct dentry *debug_nvmestat; + struct dentry *debug_nvmektime; + struct dentry *debug_cpucheck; struct dentry *vport_debugfs_root; struct lpfc_debugfs_trc *disc_trc; atomic_t disc_trc_cnt; @@ -442,6 +477,11 @@ struct lpfc_vport { uint16_t fdmi_num_disc; uint32_t fdmi_hba_mask; uint32_t fdmi_port_mask; + + /* There is a single nvme instance per vport. */ + struct nvme_fc_local_port *localport; + uint8_t nvmei_support; /* driver supports NVME Initiator */ + uint32_t last_fcp_wqidx; }; struct hbq_s { @@ -459,10 +499,9 @@ struct hbq_s { struct hbq_dmabuf *); }; -#define LPFC_MAX_HBQS 4 /* this matches the position in the lpfc_hbq_defs array */ #define LPFC_ELS_HBQ 0 -#define LPFC_EXTRA_HBQ 1 +#define LPFC_MAX_HBQS 1 enum hba_temp_state { HBA_NORMAL_TEMP, @@ -652,6 +691,8 @@ struct lpfc_hba { * Firmware supports Forced Link Speed * capability */ +#define HBA_NVME_IOQ_FLUSH 0x80000 /* NVME IO queues flushed. */ + uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/ struct lpfc_dmabuf slim2p; @@ -700,6 +741,9 @@ struct lpfc_hba { uint8_t wwpn[8]; uint32_t RandomData[7]; uint8_t fcp_embed_io; + uint8_t nvme_support; /* Firmware supports NVME */ + uint8_t nvmet_support; /* driver supports NVMET */ +#define LPFC_NVMET_MAX_PORTS 32 uint8_t mds_diags_support; /* HBA Config Parameters */ @@ -725,6 +769,14 @@ struct lpfc_hba { uint32_t cfg_fcp_imax; uint32_t cfg_fcp_cpu_map; uint32_t cfg_fcp_io_channel; + uint32_t cfg_suppress_rsp; + uint32_t cfg_nvme_oas; + uint32_t cfg_nvme_io_channel; + uint32_t cfg_nvmet_mrq; + uint32_t cfg_nvmet_mrq_post; + uint32_t cfg_enable_nvmet; + uint32_t cfg_nvme_enable_fb; + uint32_t cfg_nvmet_fb_size; uint32_t cfg_total_seg_cnt; uint32_t cfg_sg_seg_cnt; uint32_t cfg_sg_dma_buf_size; @@ -770,6 +822,13 @@ struct lpfc_hba { #define LPFC_FDMI_SUPPORT 1 /* FDMI supported? */ uint32_t cfg_enable_SmartSAN; uint32_t cfg_enable_mds_diags; + uint32_t cfg_enable_fc4_type; + uint32_t cfg_xri_split; +#define LPFC_ENABLE_FCP 1 +#define LPFC_ENABLE_NVME 2 +#define LPFC_ENABLE_BOTH 3 + uint32_t io_channel_irqs; /* number of irqs for io channels */ + struct nvmet_fc_target_port *targetport; lpfc_vpd_t vpd; /* vital product data */ struct pci_dev *pcidev; @@ -784,11 +843,11 @@ struct lpfc_hba { unsigned long data_flags; uint32_t hbq_in_use; /* HBQs in use flag */ - struct list_head rb_pend_list; /* Received buffers to be processed */ uint32_t hbq_count; /* Count of configured HBQs */ struct hbq_s hbqs[LPFC_MAX_HBQS]; /* local copy of hbq indicies */ - atomic_t fcp_qidx; /* next work queue to post work to */ + atomic_t fcp_qidx; /* next FCP WQ (RR Policy) */ + atomic_t nvme_qidx; /* next NVME WQ (RR Policy) */ phys_addr_t pci_bar0_map; /* Physical address for PCI BAR0 */ phys_addr_t pci_bar1_map; /* Physical address for PCI BAR1 */ @@ -843,9 +902,17 @@ struct lpfc_hba { /* * stat counters */ - uint64_t fc4InputRequests; - uint64_t fc4OutputRequests; - uint64_t fc4ControlRequests; + uint64_t fc4ScsiInputRequests; + uint64_t fc4ScsiOutputRequests; + uint64_t fc4ScsiControlRequests; + uint64_t fc4ScsiIoCmpls; + uint64_t fc4NvmeInputRequests; + uint64_t fc4NvmeOutputRequests; + uint64_t fc4NvmeControlRequests; + uint64_t fc4NvmeIoCmpls; + uint64_t fc4NvmeLsRequests; + uint64_t fc4NvmeLsCmpls; + uint64_t bg_guard_err_cnt; uint64_t bg_apptag_err_cnt; uint64_t bg_reftag_err_cnt; @@ -856,17 +923,23 @@ struct lpfc_hba { struct list_head lpfc_scsi_buf_list_get; struct list_head lpfc_scsi_buf_list_put; uint32_t total_scsi_bufs; + spinlock_t nvme_buf_list_get_lock; /* NVME buf alloc list lock */ + spinlock_t nvme_buf_list_put_lock; /* NVME buf free list lock */ + struct list_head lpfc_nvme_buf_list_get; + struct list_head lpfc_nvme_buf_list_put; + uint32_t total_nvme_bufs; struct list_head lpfc_iocb_list; uint32_t total_iocbq_bufs; struct list_head active_rrq_list; spinlock_t hbalock; /* pci_mem_pools */ - struct pci_pool *lpfc_scsi_dma_buf_pool; + struct pci_pool *lpfc_sg_dma_buf_pool; struct pci_pool *lpfc_mbuf_pool; struct pci_pool *lpfc_hrb_pool; /* header receive buffer pool */ struct pci_pool *lpfc_drb_pool; /* data receive buffer pool */ struct pci_pool *lpfc_hbq_pool; /* SLI3 hbq buffer pool */ + struct pci_pool *txrdy_payload_pool; struct lpfc_dma_pool lpfc_mbuf_safety_pool; mempool_t *mbox_mem_pool; @@ -878,8 +951,6 @@ struct lpfc_hba { enum intr_type_t intr_type; uint32_t intr_mode; #define LPFC_INTR_ERROR 0xFFFFFFFF - struct msix_entry msix_entries[LPFC_MSIX_VECTORS]; - struct list_head port_list; struct lpfc_vport *pport; /* physical lpfc_vport pointer */ uint16_t max_vpi; /* Maximum virtual nports */ @@ -925,6 +996,12 @@ struct lpfc_hba { struct dentry *debug_readApp; /* inject read app_tag errors */ struct dentry *debug_readRef; /* inject read ref_tag errors */ + struct dentry *debug_nvmeio_trc; + struct lpfc_debugfs_nvmeio_trc *nvmeio_trc; + atomic_t nvmeio_trc_cnt; + uint32_t nvmeio_trc_size; + uint32_t nvmeio_trc_output_idx; + /* T10 DIF error injection */ uint32_t lpfc_injerr_wgrd_cnt; uint32_t lpfc_injerr_wapp_cnt; @@ -950,7 +1027,9 @@ struct lpfc_hba { struct dentry *idiag_ctl_acc; struct dentry *idiag_mbx_acc; struct dentry *idiag_ext_acc; + uint8_t lpfc_idiag_last_eq; #endif + uint16_t nvmeio_trc_on; /* Used for deferred freeing of ELS data buffers */ struct list_head elsbuf; @@ -1023,6 +1102,53 @@ struct lpfc_hba { #define LPFC_TRANSGRESSION_LOW_RXPOWER 0x4000 uint16_t sfp_alarm; uint16_t sfp_warning; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS +#define LPFC_CHECK_CPU_CNT 32 + uint32_t cpucheck_rcv_io[LPFC_CHECK_CPU_CNT]; + uint32_t cpucheck_xmt_io[LPFC_CHECK_CPU_CNT]; + uint32_t cpucheck_cmpl_io[LPFC_CHECK_CPU_CNT]; + uint32_t cpucheck_ccmpl_io[LPFC_CHECK_CPU_CNT]; + uint16_t cpucheck_on; +#define LPFC_CHECK_OFF 0 +#define LPFC_CHECK_NVME_IO 1 +#define LPFC_CHECK_NVMET_RCV 2 +#define LPFC_CHECK_NVMET_IO 4 + uint16_t ktime_on; + uint64_t ktime_data_samples; + uint64_t ktime_status_samples; + uint64_t ktime_last_cmd; + uint64_t ktime_seg1_total; + uint64_t ktime_seg1_min; + uint64_t ktime_seg1_max; + uint64_t ktime_seg2_total; + uint64_t ktime_seg2_min; + uint64_t ktime_seg2_max; + uint64_t ktime_seg3_total; + uint64_t ktime_seg3_min; + uint64_t ktime_seg3_max; + uint64_t ktime_seg4_total; + uint64_t ktime_seg4_min; + uint64_t ktime_seg4_max; + uint64_t ktime_seg5_total; + uint64_t ktime_seg5_min; + uint64_t ktime_seg5_max; + uint64_t ktime_seg6_total; + uint64_t ktime_seg6_min; + uint64_t ktime_seg6_max; + uint64_t ktime_seg7_total; + uint64_t ktime_seg7_min; + uint64_t ktime_seg7_max; + uint64_t ktime_seg8_total; + uint64_t ktime_seg8_min; + uint64_t ktime_seg8_max; + uint64_t ktime_seg9_total; + uint64_t ktime_seg9_min; + uint64_t ktime_seg9_max; + uint64_t ktime_seg10_total; + uint64_t ktime_seg10_min; + uint64_t ktime_seg10_max; +#endif }; static inline struct Scsi_Host * @@ -1093,3 +1219,11 @@ lpfc_sli_read_hs(struct lpfc_hba *phba) return 0; } + +static inline struct lpfc_sli_ring * +lpfc_phba_elsring(struct lpfc_hba *phba) +{ + if (phba->sli_rev == LPFC_SLI_REV4) + return phba->sli4_hba.els_wq->pring; + return &phba->sli.sli3_ring[LPFC_ELS_RING]; +} diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 50cf402dea29..5c783ef7f260 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -35,14 +37,18 @@ #include <scsi/scsi_transport_fc.h> #include <scsi/fc/fc_fs.h> +#include <linux/nvme-fc-driver.h> + #include "lpfc_hw4.h" #include "lpfc_hw.h" #include "lpfc_sli.h" #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" +#include "lpfc_nvmet.h" #include "lpfc_logmsg.h" #include "lpfc_version.h" #include "lpfc_compat.h" @@ -50,9 +56,13 @@ #include "lpfc_vport.h" #include "lpfc_attr.h" -#define LPFC_DEF_DEVLOSS_TMO 30 -#define LPFC_MIN_DEVLOSS_TMO 1 -#define LPFC_MAX_DEVLOSS_TMO 255 +#define LPFC_DEF_DEVLOSS_TMO 30 +#define LPFC_MIN_DEVLOSS_TMO 1 +#define LPFC_MAX_DEVLOSS_TMO 255 + +#define LPFC_DEF_MRQ_POST 256 +#define LPFC_MIN_MRQ_POST 32 +#define LPFC_MAX_MRQ_POST 512 /* * Write key size should be multiple of 4. If write key is changed @@ -130,6 +140,211 @@ lpfc_enable_fip_show(struct device *dev, struct device_attribute *attr, } static ssize_t +lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct lpfc_vport *vport = shost_priv(shost); + struct lpfc_hba *phba = vport->phba; + struct lpfc_nvmet_tgtport *tgtp; + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport; + struct nvme_fc_remote_port *nrport; + char *statep; + int len = 0; + + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { + len += snprintf(buf, PAGE_SIZE, "NVME Disabled\n"); + return len; + } + if (phba->nvmet_support) { + if (!phba->targetport) { + len = snprintf(buf, PAGE_SIZE, + "NVME Target: x%llx is not allocated\n", + wwn_to_u64(vport->fc_portname.u.wwn)); + return len; + } + /* Port state is only one of two values for now. */ + if (phba->targetport->port_id) + statep = "REGISTERED"; + else + statep = "INIT"; + len += snprintf(buf + len, PAGE_SIZE - len, + "NVME Target: Enabled State %s\n", + statep); + len += snprintf(buf + len, PAGE_SIZE - len, + "%s%d WWPN x%llx WWNN x%llx DID x%06x\n", + "NVME Target: lpfc", + phba->brd_no, + wwn_to_u64(vport->fc_portname.u.wwn), + wwn_to_u64(vport->fc_nodename.u.wwn), + phba->targetport->port_id); + + len += snprintf(buf + len, PAGE_SIZE, + "\nNVME Target: Statistics\n"); + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + len += snprintf(buf+len, PAGE_SIZE-len, + "LS: Rcv %08x Drop %08x Abort %08x\n", + atomic_read(&tgtp->rcv_ls_req_in), + atomic_read(&tgtp->rcv_ls_req_drop), + atomic_read(&tgtp->xmt_ls_abort)); + if (atomic_read(&tgtp->rcv_ls_req_in) != + atomic_read(&tgtp->rcv_ls_req_out)) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Rcv LS: in %08x != out %08x\n", + atomic_read(&tgtp->rcv_ls_req_in), + atomic_read(&tgtp->rcv_ls_req_out)); + } + + len += snprintf(buf+len, PAGE_SIZE-len, + "LS: Xmt %08x Drop %08x Cmpl %08x Err %08x\n", + atomic_read(&tgtp->xmt_ls_rsp), + atomic_read(&tgtp->xmt_ls_drop), + atomic_read(&tgtp->xmt_ls_rsp_cmpl), + atomic_read(&tgtp->xmt_ls_rsp_error)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "FCP: Rcv %08x Drop %08x\n", + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_drop)); + + if (atomic_read(&tgtp->rcv_fcp_cmd_in) != + atomic_read(&tgtp->rcv_fcp_cmd_out)) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Rcv FCP: in %08x != out %08x\n", + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_out)); + } + + len += snprintf(buf+len, PAGE_SIZE-len, + "FCP Rsp: RD %08x rsp %08x WR %08x rsp %08x\n", + atomic_read(&tgtp->xmt_fcp_read), + atomic_read(&tgtp->xmt_fcp_read_rsp), + atomic_read(&tgtp->xmt_fcp_write), + atomic_read(&tgtp->xmt_fcp_rsp)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "FCP Rsp: abort %08x drop %08x\n", + atomic_read(&tgtp->xmt_fcp_abort), + atomic_read(&tgtp->xmt_fcp_drop)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "FCP Rsp Cmpl: %08x err %08x drop %08x\n", + atomic_read(&tgtp->xmt_fcp_rsp_cmpl), + atomic_read(&tgtp->xmt_fcp_rsp_error), + atomic_read(&tgtp->xmt_fcp_rsp_drop)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "ABORT: Xmt %08x Err %08x Cmpl %08x", + atomic_read(&tgtp->xmt_abort_rsp), + atomic_read(&tgtp->xmt_abort_rsp_error), + atomic_read(&tgtp->xmt_abort_cmpl)); + + len += snprintf(buf+len, PAGE_SIZE-len, "\n"); + return len; + } + + localport = vport->localport; + if (!localport) { + len = snprintf(buf, PAGE_SIZE, + "NVME Initiator x%llx is not allocated\n", + wwn_to_u64(vport->fc_portname.u.wwn)); + return len; + } + len = snprintf(buf, PAGE_SIZE, "NVME Initiator Enabled\n"); + + spin_lock_irq(shost->host_lock); + lport = (struct lpfc_nvme_lport *)localport->private; + + /* Port state is only one of two values for now. */ + if (localport->port_id) + statep = "ONLINE"; + else + statep = "UNKNOWN "; + + len += snprintf(buf + len, PAGE_SIZE - len, + "%s%d WWPN x%llx WWNN x%llx DID x%06x %s\n", + "NVME LPORT lpfc", + phba->brd_no, + wwn_to_u64(vport->fc_portname.u.wwn), + wwn_to_u64(vport->fc_nodename.u.wwn), + localport->port_id, statep); + + list_for_each_entry(rport, &lport->rport_list, list) { + /* local short-hand pointer. */ + nrport = rport->remoteport; + + /* Port state is only one of two values for now. */ + switch (nrport->port_state) { + case FC_OBJSTATE_ONLINE: + statep = "ONLINE"; + break; + case FC_OBJSTATE_UNKNOWN: + statep = "UNKNOWN "; + break; + default: + statep = "UNSUPPORTED"; + break; + } + + /* Tab in to show lport ownership. */ + len += snprintf(buf + len, PAGE_SIZE - len, + "NVME RPORT "); + if (phba->brd_no >= 10) + len += snprintf(buf + len, PAGE_SIZE - len, " "); + + len += snprintf(buf + len, PAGE_SIZE - len, "WWPN x%llx ", + nrport->port_name); + len += snprintf(buf + len, PAGE_SIZE - len, "WWNN x%llx ", + nrport->node_name); + len += snprintf(buf + len, PAGE_SIZE - len, "DID x%06x ", + nrport->port_id); + + switch (nrport->port_role) { + case FC_PORT_ROLE_NVME_INITIATOR: + len += snprintf(buf + len, PAGE_SIZE - len, + "INITIATOR "); + break; + case FC_PORT_ROLE_NVME_TARGET: + len += snprintf(buf + len, PAGE_SIZE - len, + "TARGET "); + break; + case FC_PORT_ROLE_NVME_DISCOVERY: + len += snprintf(buf + len, PAGE_SIZE - len, + "DISCOVERY "); + break; + default: + len += snprintf(buf + len, PAGE_SIZE - len, + "UNKNOWN_ROLE x%x", + nrport->port_role); + break; + } + len += snprintf(buf + len, PAGE_SIZE - len, "%s ", statep); + /* Terminate the string. */ + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + } + spin_unlock_irq(shost->host_lock); + + len += snprintf(buf + len, PAGE_SIZE, "\nNVME Statistics\n"); + len += snprintf(buf+len, PAGE_SIZE-len, + "LS: Xmt %016llx Cmpl %016llx\n", + phba->fc4NvmeLsRequests, + phba->fc4NvmeLsCmpls); + + len += snprintf(buf+len, PAGE_SIZE-len, + "FCP: Rd %016llx Wr %016llx IO %016llx\n", + phba->fc4NvmeInputRequests, + phba->fc4NvmeOutputRequests, + phba->fc4NvmeControlRequests); + + len += snprintf(buf+len, PAGE_SIZE-len, + " Cmpl %016llx\n", phba->fc4NvmeIoCmpls); + + return len; +} + +static ssize_t lpfc_bg_info_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -675,6 +890,28 @@ lpfc_issue_lip(struct Scsi_Host *shost) return 0; } +int +lpfc_emptyq_wait(struct lpfc_hba *phba, struct list_head *q, spinlock_t *lock) +{ + int cnt = 0; + + spin_lock_irq(lock); + while (!list_empty(q)) { + spin_unlock_irq(lock); + msleep(20); + if (cnt++ > 250) { /* 5 secs */ + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "0466 %s %s\n", + "Outstanding IO when ", + "bringing Adapter offline\n"); + return 0; + } + spin_lock_irq(lock); + } + spin_unlock_irq(lock); + return 1; +} + /** * lpfc_do_offline - Issues a mailbox command to bring the link down * @phba: lpfc_hba pointer. @@ -694,10 +931,10 @@ static int lpfc_do_offline(struct lpfc_hba *phba, uint32_t type) { struct completion online_compl; + struct lpfc_queue *qp = NULL; struct lpfc_sli_ring *pring; struct lpfc_sli *psli; int status = 0; - int cnt = 0; int i; int rc; @@ -717,20 +954,24 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type) /* Wait a little for things to settle down, but not * long enough for dev loss timeout to expire. */ - for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; - while (!list_empty(&pring->txcmplq)) { - msleep(10); - if (cnt++ > 500) { /* 5 secs */ - lpfc_printf_log(phba, - KERN_WARNING, LOG_INIT, - "0466 Outstanding IO when " - "bringing Adapter offline\n"); - break; - } + if (phba->sli_rev != LPFC_SLI_REV4) { + for (i = 0; i < psli->num_rings; i++) { + pring = &psli->sli3_ring[i]; + if (!lpfc_emptyq_wait(phba, &pring->txcmplq, + &phba->hbalock)) + goto out; + } + } else { + list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) { + pring = qp->pring; + if (!pring) + continue; + if (!lpfc_emptyq_wait(phba, &pring->txcmplq, + &pring->ring_lock)) + goto out; } } - +out: init_completion(&online_compl); rc = lpfc_workq_post_event(phba, &status, &online_compl, type); if (rc == 0) @@ -1945,6 +2186,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \ } +static DEVICE_ATTR(nvme_info, 0444, lpfc_nvme_info_show, NULL); static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL); static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL); static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL); @@ -2751,6 +2993,13 @@ lpfc_oas_lun_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(lpfc_xlane_lun, S_IRUGO | S_IWUSR, lpfc_oas_lun_show, lpfc_oas_lun_store); +int lpfc_enable_nvmet_cnt; +unsigned long long lpfc_enable_nvmet[LPFC_NVMET_MAX_PORTS] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +module_param_array(lpfc_enable_nvmet, ullong, &lpfc_enable_nvmet_cnt, 0444); +MODULE_PARM_DESC(lpfc_enable_nvmet, "Enable HBA port(s) WWPN as a NVME Target"); + static int lpfc_poll = 0; module_param(lpfc_poll, int, S_IRUGO); MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:" @@ -2816,9 +3065,9 @@ lpfc_txq_hw_show(struct device *dev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_hba *phba = ((struct lpfc_vport *) shost->hostdata)->phba; + struct lpfc_sli_ring *pring = lpfc_phba_elsring(phba); - return snprintf(buf, PAGE_SIZE, "%d\n", - phba->sli.ring[LPFC_ELS_RING].txq_max); + return snprintf(buf, PAGE_SIZE, "%d\n", pring->txq_max); } static DEVICE_ATTR(txq_hw, S_IRUGO, @@ -2829,9 +3078,9 @@ lpfc_txcmplq_hw_show(struct device *dev, struct device_attribute *attr, { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_hba *phba = ((struct lpfc_vport *) shost->hostdata)->phba; + struct lpfc_sli_ring *pring = lpfc_phba_elsring(phba); - return snprintf(buf, PAGE_SIZE, "%d\n", - phba->sli.ring[LPFC_ELS_RING].txcmplq_max); + return snprintf(buf, PAGE_SIZE, "%d\n", pring->txcmplq_max); } static DEVICE_ATTR(txcmplq_hw, S_IRUGO, @@ -3030,6 +3279,59 @@ static DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR, lpfc_devloss_tmo_show, lpfc_devloss_tmo_store); /* + * lpfc_suppress_rsp: Enable suppress rsp feature is firmware supports it + * lpfc_suppress_rsp = 0 Disable + * lpfc_suppress_rsp = 1 Enable (default) + * + */ +LPFC_ATTR_R(suppress_rsp, 1, 0, 1, + "Enable suppress rsp feature is firmware supports it"); + +/* + * lpfc_nvmet_mrq: Specify number of RQ pairs for processing NVMET cmds + * lpfc_nvmet_mrq = 1 use a single RQ pair + * lpfc_nvmet_mrq >= 2 use specified RQ pairs for MRQ + * + */ +LPFC_ATTR_R(nvmet_mrq, + 1, 1, 16, + "Specify number of RQ pairs for processing NVMET cmds"); + +/* + * lpfc_nvmet_mrq_post: Specify number buffers to post on every MRQ + * + */ +LPFC_ATTR_R(nvmet_mrq_post, LPFC_DEF_MRQ_POST, + LPFC_MIN_MRQ_POST, LPFC_MAX_MRQ_POST, + "Specify number of buffers to post on every MRQ"); + +/* + * lpfc_enable_fc4_type: Defines what FC4 types are supported. + * Supported Values: 1 - register just FCP + * 3 - register both FCP and NVME + * Supported values are [1,3]. Default value is 3 + */ +LPFC_ATTR_R(enable_fc4_type, LPFC_ENABLE_BOTH, + LPFC_ENABLE_FCP, LPFC_ENABLE_BOTH, + "Define fc4 type to register with fabric."); + +/* + * lpfc_xri_split: Defines the division of XRI resources between SCSI and NVME + * This parameter is only used if: + * lpfc_enable_fc4_type is 3 - register both FCP and NVME and + * port is not configured for NVMET. + * + * ELS/CT always get 10% of XRIs, up to a maximum of 250 + * The remaining XRIs get split up based on lpfc_xri_split per port: + * + * Supported Values are in percentages + * the xri_split value is the percentage the SCSI port will get. The remaining + * percentage will go to NVME. + */ +LPFC_ATTR_R(xri_split, 50, 10, 90, + "Division of XRI resources between SCSI and NVME"); + +/* # lpfc_log_verbose: Only turn this flag on if you are willing to risk being # deluged with LOTS of information. # You can set a bit mask to record specific types of verbose messages: @@ -3329,7 +3631,7 @@ static DEVICE_ATTR(lpfc_static_vport, S_IRUGO, * @buf: Data buffer. * @count: Size of the data buffer. * - * This function get called when an user write to the lpfc_stat_data_ctrl + * This function get called when a user write to the lpfc_stat_data_ctrl * sysfs file. This function parse the command written to the sysfs file * and take appropriate action. These commands are used for controlling * driver statistical data collection. @@ -4143,13 +4445,14 @@ lpfc_fcp_imax_store(struct device *dev, struct device_attribute *attr, /* * Value range for the HBA is [5000,5000000] * The value for each EQ depends on how many EQs are configured. + * Allow value == 0 */ - if (val < LPFC_MIN_IMAX || val > LPFC_MAX_IMAX) + if (val && (val < LPFC_MIN_IMAX || val > LPFC_MAX_IMAX)) return -EINVAL; phba->cfg_fcp_imax = (uint32_t)val; - for (i = 0; i < phba->cfg_fcp_io_channel; i += LPFC_MAX_EQ_DELAY) - lpfc_modify_fcp_eq_delay(phba, i); + for (i = 0; i < phba->io_channel_irqs; i++) + lpfc_modify_hba_eq_delay(phba, i); return strlen(buf); } @@ -4187,7 +4490,8 @@ lpfc_fcp_imax_init(struct lpfc_hba *phba, int val) return 0; } - if (val >= LPFC_MIN_IMAX && val <= LPFC_MAX_IMAX) { + if ((val >= LPFC_MIN_IMAX && val <= LPFC_MAX_IMAX) || + (val == 0)) { phba->cfg_fcp_imax = val; return 0; } @@ -4377,6 +4681,32 @@ LPFC_VPORT_ATTR_RW(first_burst_size, 0, 0, 65536, "First burst size for Targets that support first burst"); /* +* lpfc_nvmet_fb_size: NVME Target mode supported first burst size. +* When the driver is configured as an NVME target, this value is +* communicated to the NVME initiator in the PRLI response. It is +* used only when the lpfc_nvme_enable_fb and lpfc_nvmet_support +* parameters are set and the target is sending the PRLI RSP. +* Parameter supported on physical port only - no NPIV support. +* Value range is [0,65536]. Default value is 0. +*/ +LPFC_ATTR_RW(nvmet_fb_size, 0, 0, 65536, + "NVME Target mode first burst size in 512B increments."); + +/* + * lpfc_nvme_enable_fb: Enable NVME first burst on I and T functions. + * For the Initiator (I), enabling this parameter means that an NVMET + * PRLI response with FBA enabled and an FB_SIZE set to a nonzero value will be + * processed by the initiator for subsequent NVME FCP IO. For the target + * function (T), enabling this parameter qualifies the lpfc_nvmet_fb_size + * driver parameter as the target function's first burst size returned to the + * initiator in the target's NVME PRLI response. Parameter supported on physical + * port only - no NPIV support. + * Value range is [0,1]. Default value is 0 (disabled). + */ +LPFC_ATTR_RW(nvme_enable_fb, 0, 0, 1, + "Enable First Burst feature on I and T functions."); + +/* # lpfc_max_scsicmpl_time: Use scsi command completion time to control I/O queue # depth. Default value is 0. When the value of this parameter is zero the # SCSI command completion time is not used for controlling I/O queue depth. When @@ -4423,17 +4753,25 @@ static DEVICE_ATTR(lpfc_max_scsicmpl_time, S_IRUGO | S_IWUSR, LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support"); /* -# lpfc_fcp_io_sched: Determine scheduling algrithmn for issuing FCP cmds -# range is [0,1]. Default value is 0. -# For [0], FCP commands are issued to Work Queues ina round robin fashion. -# For [1], FCP commands are issued to a Work Queue associated with the -# current CPU. -# It would be set to 1 by the driver if it's able to set up cpu affinity -# for FCP I/Os through Work Queue associated with the current CPU. Otherwise, -# roundrobin scheduling of FCP I/Os through WQs will be used. -*/ -LPFC_ATTR_RW(fcp_io_sched, 0, 0, 1, "Determine scheduling algorithm for " - "issuing commands [0] - Round Robin, [1] - Current CPU"); + * lpfc_io_sched: Determine scheduling algrithmn for issuing FCP cmds + * range is [0,1]. Default value is 0. + * For [0], FCP commands are issued to Work Queues ina round robin fashion. + * For [1], FCP commands are issued to a Work Queue associated with the + * current CPU. + * + * LPFC_FCP_SCHED_ROUND_ROBIN == 0 + * LPFC_FCP_SCHED_BY_CPU == 1 + * + * The driver dynamically sets this to 1 (BY_CPU) if it's able to set up cpu + * affinity for FCP/NVME I/Os through Work Queues associated with the current + * CPU. Otherwise, the default 0 (Round Robin) scheduling of FCP/NVME I/Os + * through WQs will be used. + */ +LPFC_ATTR_RW(fcp_io_sched, LPFC_FCP_SCHED_ROUND_ROBIN, + LPFC_FCP_SCHED_ROUND_ROBIN, + LPFC_FCP_SCHED_BY_CPU, + "Determine scheduling algorithm for " + "issuing commands [0] - Round Robin, [1] - Current CPU"); /* # lpfc_fcp2_no_tgt_reset: Determine bus reset behavior @@ -4560,15 +4898,54 @@ LPFC_ATTR_R(use_msi, 2, 0, 2, "Use Message Signaled Interrupts (1) or " "MSI-X (2), if possible"); /* -# lpfc_fcp_io_channel: Set the number of FCP EQ/CQ/WQ IO channels -# -# Value range is [1,7]. Default value is 4. -*/ -LPFC_ATTR_R(fcp_io_channel, LPFC_FCP_IO_CHAN_DEF, LPFC_FCP_IO_CHAN_MIN, - LPFC_FCP_IO_CHAN_MAX, + * lpfc_nvme_oas: Use the oas bit when sending NVME/NVMET IOs + * + * 0 = NVME OAS disabled + * 1 = NVME OAS enabled + * + * Value range is [0,1]. Default value is 0. + */ +LPFC_ATTR_RW(nvme_oas, 0, 0, 1, + "Use OAS bit on NVME IOs"); + +/* + * lpfc_fcp_io_channel: Set the number of FCP IO channels the driver + * will advertise it supports to the SCSI layer. This also will map to + * the number of WQs the driver will create. + * + * 0 = Configure the number of io channels to the number of active CPUs. + * 1,32 = Manually specify how many io channels to use. + * + * Value range is [0,32]. Default value is 4. + */ +LPFC_ATTR_R(fcp_io_channel, + LPFC_FCP_IO_CHAN_DEF, + LPFC_HBA_IO_CHAN_MIN, LPFC_HBA_IO_CHAN_MAX, "Set the number of FCP I/O channels"); /* + * lpfc_nvme_io_channel: Set the number of IO hardware queues the driver + * will advertise it supports to the NVME layer. This also will map to + * the number of WQs the driver will create. + * + * This module parameter is valid when lpfc_enable_fc4_type is set + * to support NVME. + * + * The NVME Layer will try to create this many, plus 1 administrative + * hardware queue. The administrative queue will always map to WQ 0 + * A hardware IO queue maps (qidx) to a specific driver WQ. + * + * 0 = Configure the number of io channels to the number of active CPUs. + * 1,32 = Manually specify how many io channels to use. + * + * Value range is [0,32]. Default value is 0. + */ +LPFC_ATTR_R(nvme_io_channel, + LPFC_NVME_IO_CHAN_DEF, + LPFC_HBA_IO_CHAN_MIN, LPFC_HBA_IO_CHAN_MAX, + "Set the number of NVME I/O channels"); + +/* # lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware. # 0 = HBA resets disabled # 1 = HBA resets enabled (default) @@ -4692,6 +5069,7 @@ LPFC_ATTR_R(sg_seg_cnt, LPFC_DEFAULT_SG_SEG_CNT, LPFC_DEFAULT_SG_SEG_CNT, LPFC_ATTR_R(enable_mds_diags, 0, 0, 1, "Enable MDS Diagnostics"); struct device_attribute *lpfc_hba_attrs[] = { + &dev_attr_nvme_info, &dev_attr_bg_info, &dev_attr_bg_guard_err, &dev_attr_bg_apptag_err, @@ -4718,6 +5096,8 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_peer_port_login, &dev_attr_lpfc_nodev_tmo, &dev_attr_lpfc_devloss_tmo, + &dev_attr_lpfc_enable_fc4_type, + &dev_attr_lpfc_xri_split, &dev_attr_lpfc_fcp_class, &dev_attr_lpfc_use_adisc, &dev_attr_lpfc_first_burst_size, @@ -4752,9 +5132,16 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_poll_tmo, &dev_attr_lpfc_task_mgmt_tmo, &dev_attr_lpfc_use_msi, + &dev_attr_lpfc_nvme_oas, &dev_attr_lpfc_fcp_imax, &dev_attr_lpfc_fcp_cpu_map, &dev_attr_lpfc_fcp_io_channel, + &dev_attr_lpfc_suppress_rsp, + &dev_attr_lpfc_nvme_io_channel, + &dev_attr_lpfc_nvmet_mrq, + &dev_attr_lpfc_nvmet_mrq_post, + &dev_attr_lpfc_nvme_enable_fb, + &dev_attr_lpfc_nvmet_fb_size, &dev_attr_lpfc_enable_bg, &dev_attr_lpfc_soft_wwnn, &dev_attr_lpfc_soft_wwpn, @@ -5764,15 +6151,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_fdmi_on_init(phba, lpfc_fdmi_on); lpfc_enable_SmartSAN_init(phba, lpfc_enable_SmartSAN); lpfc_use_msi_init(phba, lpfc_use_msi); + lpfc_nvme_oas_init(phba, lpfc_nvme_oas); lpfc_fcp_imax_init(phba, lpfc_fcp_imax); lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map); - lpfc_fcp_io_channel_init(phba, lpfc_fcp_io_channel); lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset); lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat); + lpfc_EnableXLane_init(phba, lpfc_EnableXLane); if (phba->sli_rev != LPFC_SLI_REV4) phba->cfg_EnableXLane = 0; lpfc_XLanePriority_init(phba, lpfc_XLanePriority); + memset(phba->cfg_oas_tgt_wwpn, 0, (8 * sizeof(uint8_t))); memset(phba->cfg_oas_vpt_wwpn, 0, (8 * sizeof(uint8_t))); phba->cfg_oas_lun_state = 0; @@ -5786,9 +6175,48 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) phba->cfg_poll = 0; else phba->cfg_poll = lpfc_poll; + lpfc_suppress_rsp_init(phba, lpfc_suppress_rsp); + + lpfc_enable_fc4_type_init(phba, lpfc_enable_fc4_type); + lpfc_nvmet_mrq_init(phba, lpfc_nvmet_mrq); + lpfc_nvmet_mrq_post_init(phba, lpfc_nvmet_mrq_post); + + /* Initialize first burst. Target vs Initiator are different. */ + lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb); + lpfc_nvmet_fb_size_init(phba, lpfc_nvmet_fb_size); + lpfc_fcp_io_channel_init(phba, lpfc_fcp_io_channel); + lpfc_nvme_io_channel_init(phba, lpfc_nvme_io_channel); + + if (phba->sli_rev != LPFC_SLI_REV4) { + /* NVME only supported on SLI4 */ + phba->nvmet_support = 0; + phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP; + } else { + /* We MUST have FCP support */ + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) + phba->cfg_enable_fc4_type |= LPFC_ENABLE_FCP; + } + + /* A value of 0 means use the number of CPUs found in the system */ + if (phba->cfg_fcp_io_channel == 0) + phba->cfg_fcp_io_channel = phba->sli4_hba.num_present_cpu; + if (phba->cfg_nvme_io_channel == 0) + phba->cfg_nvme_io_channel = phba->sli4_hba.num_present_cpu; + + if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME) + phba->cfg_fcp_io_channel = 0; + + if (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP) + phba->cfg_nvme_io_channel = 0; + + if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel) + phba->io_channel_irqs = phba->cfg_fcp_io_channel; + else + phba->io_channel_irqs = phba->cfg_nvme_io_channel; phba->cfg_soft_wwnn = 0L; phba->cfg_soft_wwpn = 0L; + lpfc_xri_split_init(phba, lpfc_xri_split); lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt); lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth); lpfc_hba_log_verbose_init(phba, lpfc_log_verbose); @@ -5805,6 +6233,60 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) } /** + * lpfc_nvme_mod_param_dep - Adjust module parameter value based on + * dependencies between protocols and roles. + * @phba: lpfc_hba pointer. + **/ +void +lpfc_nvme_mod_param_dep(struct lpfc_hba *phba) +{ + if (phba->cfg_nvme_io_channel > phba->sli4_hba.num_present_cpu) + phba->cfg_nvme_io_channel = phba->sli4_hba.num_present_cpu; + + if (phba->cfg_fcp_io_channel > phba->sli4_hba.num_present_cpu) + phba->cfg_fcp_io_channel = phba->sli4_hba.num_present_cpu; + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME && + phba->nvmet_support) { + phba->cfg_enable_fc4_type &= ~LPFC_ENABLE_FCP; + phba->cfg_fcp_io_channel = 0; + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, + "6013 %s x%x fb_size x%x, fb_max x%x\n", + "NVME Target PRLI ACC enable_fb ", + phba->cfg_nvme_enable_fb, + phba->cfg_nvmet_fb_size, + LPFC_NVMET_FB_SZ_MAX); + + if (phba->cfg_nvme_enable_fb == 0) + phba->cfg_nvmet_fb_size = 0; + else { + if (phba->cfg_nvmet_fb_size > LPFC_NVMET_FB_SZ_MAX) + phba->cfg_nvmet_fb_size = LPFC_NVMET_FB_SZ_MAX; + } + + /* Adjust lpfc_nvmet_mrq to avoid running out of WQE slots */ + if (phba->cfg_nvmet_mrq > phba->cfg_nvme_io_channel) { + phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel; + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC, + "6018 Adjust lpfc_nvmet_mrq to %d\n", + phba->cfg_nvmet_mrq); + } + } else { + /* Not NVME Target mode. Turn off Target parameters. */ + phba->nvmet_support = 0; + phba->cfg_nvmet_mrq = 0; + phba->cfg_nvmet_mrq_post = 0; + phba->cfg_nvmet_fb_size = 0; + } + + if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel) + phba->io_channel_irqs = phba->cfg_fcp_io_channel; + else + phba->io_channel_irqs = phba->cfg_nvme_io_channel; +} + +/** * lpfc_get_vport_cfgparam - Used during port create, init the vport structure * @vport: lpfc_vport pointer. **/ diff --git a/drivers/scsi/lpfc/lpfc_attr.h b/drivers/scsi/lpfc/lpfc_attr.h index b2bd28e965fa..d56dafcdd563 100644 --- a/drivers/scsi/lpfc/lpfc_attr.h +++ b/drivers/scsi/lpfc/lpfc_attr.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 7dca4d6a8883..18157d2840a3 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2009-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -1704,6 +1706,7 @@ lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba) struct lpfc_vport **vports; struct Scsi_Host *shost; struct lpfc_sli *psli; + struct lpfc_queue *qp = NULL; struct lpfc_sli_ring *pring; int i = 0; @@ -1711,9 +1714,6 @@ lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba) if (!psli) return -ENODEV; - pring = &psli->ring[LPFC_FCP_RING]; - if (!pring) - return -ENODEV; if ((phba->link_state == LPFC_HBA_ERROR) || (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || @@ -1732,10 +1732,18 @@ lpfc_bsg_diag_mode_enter(struct lpfc_hba *phba) scsi_block_requests(shost); } - while (!list_empty(&pring->txcmplq)) { - if (i++ > 500) /* wait up to 5 seconds */ + if (phba->sli_rev != LPFC_SLI_REV4) { + pring = &psli->sli3_ring[LPFC_FCP_RING]; + lpfc_emptyq_wait(phba, &pring->txcmplq, &phba->hbalock); + return 0; + } + list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) { + pring = qp->pring; + if (!pring || (pring->ringno != LPFC_FCP_RING)) + continue; + if (!lpfc_emptyq_wait(phba, &pring->txcmplq, + &pring->ring_lock)) break; - msleep(10); } return 0; } @@ -2703,7 +2711,7 @@ err_get_xri_exit: * lpfc_bsg_dma_page_alloc - allocate a bsg mbox page sized dma buffers * @phba: Pointer to HBA context object * - * This function allocates BSG_MBOX_SIZE (4KB) page size dma buffer and. + * This function allocates BSG_MBOX_SIZE (4KB) page size dma buffer and * returns the pointer to the buffer. **/ static struct lpfc_dmabuf * @@ -2875,8 +2883,7 @@ out: static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri, size_t len) { - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; struct lpfc_iocbq *cmdiocbq; IOCB_t *cmd = NULL; struct list_head head, *curr, *next; @@ -2890,6 +2897,8 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri, int iocb_stat; int i = 0; + pring = lpfc_phba_elsring(phba); + cmdiocbq = lpfc_sli_get_iocbq(phba); rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); if (rxbmp != NULL) { @@ -5403,13 +5412,15 @@ lpfc_bsg_timeout(struct bsg_job *job) struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job)); struct lpfc_hba *phba = vport->phba; struct lpfc_iocbq *cmdiocb; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; struct bsg_job_data *dd_data; unsigned long flags; int rc = 0; LIST_HEAD(completions); struct lpfc_iocbq *check_iocb, *next_iocb; + pring = lpfc_phba_elsring(phba); + /* if job's driver data is NULL, the command completed or is in the * the process of completing. In this case, return status to request * so the timeout is retried. This avoids double completion issues diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h index f2247aa4fa17..e7d95a4e8042 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.h +++ b/drivers/scsi/lpfc/lpfc_bsg.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2010-2015 Emulex. All rights reserved. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * Copyright (C) 2010-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h index c88e556ea62e..6b32b0ae7506 100644 --- a/drivers/scsi/lpfc/lpfc_compat.h +++ b/drivers/scsi/lpfc/lpfc_compat.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 309643a2c55c..843dd73004da 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -21,6 +23,7 @@ typedef int (*node_filter)(struct lpfc_nodelist *, void *); struct fc_rport; +struct fc_frame_header; void lpfc_down_link(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_sli_read_link_ste(struct lpfc_hba *); void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t, uint16_t); @@ -167,6 +170,8 @@ void lpfc_hb_timeout_handler(struct lpfc_hba *); void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, struct lpfc_iocbq *); int lpfc_ct_handle_unsol_abort(struct lpfc_hba *, struct hbq_dmabuf *); +int lpfc_issue_gidft(struct lpfc_vport *vport); +int lpfc_get_gidft_type(struct lpfc_vport *vport, struct lpfc_iocbq *iocbq); int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t); int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int, uint32_t); void lpfc_fdmi_num_disc_check(struct lpfc_vport *); @@ -186,6 +191,8 @@ void lpfc_unblock_mgmt_io(struct lpfc_hba *); void lpfc_offline_prep(struct lpfc_hba *, int); void lpfc_offline(struct lpfc_hba *); void lpfc_reset_hba(struct lpfc_hba *); +int lpfc_emptyq_wait(struct lpfc_hba *phba, struct list_head *hd, + spinlock_t *slock); int lpfc_fof_queue_create(struct lpfc_hba *); int lpfc_fof_queue_setup(struct lpfc_hba *); @@ -193,7 +200,11 @@ int lpfc_fof_queue_destroy(struct lpfc_hba *); irqreturn_t lpfc_sli4_fof_intr_handler(int, void *); int lpfc_sli_setup(struct lpfc_hba *); -int lpfc_sli_queue_setup(struct lpfc_hba *); +int lpfc_sli4_setup(struct lpfc_hba *phba); +void lpfc_sli_queue_init(struct lpfc_hba *phba); +void lpfc_sli4_queue_init(struct lpfc_hba *phba); +struct lpfc_sli_ring *lpfc_sli4_calc_ring(struct lpfc_hba *phba, + struct lpfc_iocbq *iocbq); void lpfc_handle_eratt(struct lpfc_hba *); void lpfc_handle_latt(struct lpfc_hba *); @@ -220,6 +231,7 @@ void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t); void lpfc_init_vpi(struct lpfc_hba *, struct lpfcMboxq *, uint16_t); void lpfc_unreg_vfi(struct lpfcMboxq *, struct lpfc_vport *); void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *); +void lpfc_reg_fcfi_mrq(struct lpfc_hba *phba, struct lpfcMboxq *mbox, int mode); void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t); void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *); int lpfc_check_pending_fcoe_event(struct lpfc_hba *, uint8_t); @@ -231,8 +243,15 @@ struct hbq_dmabuf *lpfc_els_hbq_alloc(struct lpfc_hba *); void lpfc_els_hbq_free(struct lpfc_hba *, struct hbq_dmabuf *); struct hbq_dmabuf *lpfc_sli4_rb_alloc(struct lpfc_hba *); void lpfc_sli4_rb_free(struct lpfc_hba *, struct hbq_dmabuf *); +struct rqb_dmabuf *lpfc_sli4_nvmet_alloc(struct lpfc_hba *phba); +void lpfc_sli4_nvmet_free(struct lpfc_hba *phba, struct rqb_dmabuf *dmab); void lpfc_sli4_build_dflt_fcf_record(struct lpfc_hba *, struct fcf_record *, uint16_t); +int lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq, + struct lpfc_rqe *hrqe, struct lpfc_rqe *drqe); +int lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hq, + struct lpfc_queue *dq, int count); +int lpfc_free_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hq); void lpfc_unregister_fcf(struct lpfc_hba *); void lpfc_unregister_fcf_rescan(struct lpfc_hba *); void lpfc_unregister_unused_fcf(struct lpfc_hba *); @@ -287,6 +306,11 @@ void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *, LPFC_MBOXQ_t *); int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, uint32_t); +int lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t rnum, + struct lpfc_iocbq *iocbq); +struct lpfc_sglq *__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xri); +struct lpfc_sglq *__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba, + struct lpfc_iocbq *piocbq); void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t); void lpfc_sli_bemem_bcopy(void *, void *, uint32_t); void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *); @@ -336,8 +360,13 @@ void lpfc_sli_free_hbq(struct lpfc_hba *, struct hbq_dmabuf *); void *lpfc_mbuf_alloc(struct lpfc_hba *, int, dma_addr_t *); void __lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t); void lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t); +void *lpfc_nvmet_buf_alloc(struct lpfc_hba *phba, int flags, + dma_addr_t *handle); +void lpfc_nvmet_buf_free(struct lpfc_hba *phba, void *virtp, dma_addr_t dma); void lpfc_in_buf_free(struct lpfc_hba *, struct lpfc_dmabuf *); +void lpfc_rq_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp); + /* Function prototypes. */ const char* lpfc_info(struct Scsi_Host *); int lpfc_scan_finished(struct Scsi_Host *, unsigned long); @@ -356,6 +385,7 @@ extern struct device_attribute *lpfc_hba_attrs[]; extern struct device_attribute *lpfc_vport_attrs[]; extern struct scsi_host_template lpfc_template; extern struct scsi_host_template lpfc_template_s3; +extern struct scsi_host_template lpfc_template_nvme; extern struct scsi_host_template lpfc_vport_template; extern struct fc_function_template lpfc_transport_functions; extern struct fc_function_template lpfc_vport_transport_functions; @@ -375,9 +405,11 @@ void lpfc_host_attrib_init(struct Scsi_Host *); extern void lpfc_debugfs_initialize(struct lpfc_vport *); extern void lpfc_debugfs_terminate(struct lpfc_vport *); extern void lpfc_debugfs_disc_trc(struct lpfc_vport *, int, char *, uint32_t, - uint32_t, uint32_t); + uint32_t, uint32_t); extern void lpfc_debugfs_slow_ring_trc(struct lpfc_hba *, char *, uint32_t, - uint32_t, uint32_t); + uint32_t, uint32_t); +extern void lpfc_debugfs_nvme_trc(struct lpfc_hba *phba, char *fmt, + uint16_t data1, uint16_t data2, uint32_t data3); extern struct lpfc_hbq_init *lpfc_hbq_defs[]; /* SLI4 if_type 2 externs. */ @@ -471,7 +503,10 @@ int lpfc_issue_unreg_vfi(struct lpfc_vport *); int lpfc_selective_reset(struct lpfc_hba *); int lpfc_sli4_read_config(struct lpfc_hba *); void lpfc_sli4_node_prep(struct lpfc_hba *); -int lpfc_sli4_xri_sgl_update(struct lpfc_hba *); +int lpfc_sli4_els_sgl_update(struct lpfc_hba *phba); +int lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba); +int lpfc_sli4_scsi_sgl_update(struct lpfc_hba *phba); +int lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba); void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *); uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *); int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t); @@ -496,3 +531,26 @@ bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *, uint32_t *, uint32_t *); int lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox); void lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb); + +/* NVME interfaces. */ +void lpfc_nvme_unregister_port(struct lpfc_vport *vport, + struct lpfc_nodelist *ndlp); +int lpfc_nvme_register_port(struct lpfc_vport *vport, + struct lpfc_nodelist *ndlp); +int lpfc_nvme_create_localport(struct lpfc_vport *vport); +void lpfc_nvme_destroy_localport(struct lpfc_vport *vport); +void lpfc_nvme_update_localport(struct lpfc_vport *vport); +int lpfc_nvmet_create_targetport(struct lpfc_hba *phba); +int lpfc_nvmet_update_targetport(struct lpfc_hba *phba); +void lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba); +void lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba, + struct lpfc_sli_ring *pring, struct lpfc_iocbq *piocb); +void lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba, + struct lpfc_sli_ring *pring, + struct rqb_dmabuf *nvmebuf, uint64_t isr_ts); +void lpfc_nvme_mod_param_dep(struct lpfc_hba *phba); +void lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba, + struct lpfc_iocbq *cmdiocb, + struct lpfc_wcqe_complete *abts_cmpl); +extern int lpfc_enable_nvmet_cnt; +extern unsigned long long lpfc_enable_nvmet[]; diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 4ac03b16d17f..c22bb3f887e1 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -40,8 +42,9 @@ #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" #include "lpfc_logmsg.h" #include "lpfc_crtn.h" #include "lpfc_version.h" @@ -453,8 +456,90 @@ lpfc_find_vport_by_did(struct lpfc_hba *phba, uint32_t did) { return NULL; } +static void +lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) +{ + struct lpfc_nodelist *ndlp; + + if ((vport->port_type != LPFC_NPIV_PORT) || + !(vport->ct_flags & FC_CT_RFF_ID) || !vport->cfg_restrict_login) { + + ndlp = lpfc_setup_disc_node(vport, Did); + + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) { + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, + "Parse GID_FTrsp: did:x%x flg:x%x x%x", + Did, ndlp->nlp_flag, vport->fc_flag); + + /* By default, the driver expects to support FCP FC4 */ + if (fc4_type == FC_TYPE_FCP) + ndlp->nlp_fc4_type |= NLP_FC4_FCP; + + if (fc4_type == FC_TYPE_NVME) + ndlp->nlp_fc4_type |= NLP_FC4_NVME; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + "0238 Process x%06x NameServer Rsp " + "Data: x%x x%x x%x x%x\n", Did, + ndlp->nlp_flag, ndlp->nlp_fc4_type, + vport->fc_flag, + vport->fc_rscn_id_cnt); + } else { + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, + "Skip1 GID_FTrsp: did:x%x flg:x%x cnt:%d", + Did, vport->fc_flag, vport->fc_rscn_id_cnt); + + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + "0239 Skip x%06x NameServer Rsp " + "Data: x%x x%x\n", Did, + vport->fc_flag, + vport->fc_rscn_id_cnt); + } + } else { + if (!(vport->fc_flag & FC_RSCN_MODE) || + lpfc_rscn_payload_check(vport, Did)) { + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, + "Query GID_FTrsp: did:x%x flg:x%x cnt:%d", + Did, vport->fc_flag, vport->fc_rscn_id_cnt); + + /* + * This NPortID was previously a FCP target, + * Don't even bother to send GFF_ID. + */ + ndlp = lpfc_findnode_did(vport, Did); + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) + ndlp->nlp_fc4_type = fc4_type; + + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) { + ndlp->nlp_fc4_type = fc4_type; + + if (ndlp->nlp_type & NLP_FCP_TARGET) + lpfc_setup_disc_node(vport, Did); + + else if (lpfc_ns_cmd(vport, SLI_CTNS_GFF_ID, + 0, Did) == 0) + vport->num_disc_nodes++; + + else + lpfc_setup_disc_node(vport, Did); + } + } else { + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, + "Skip2 GID_FTrsp: did:x%x flg:x%x cnt:%d", + Did, vport->fc_flag, vport->fc_rscn_id_cnt); + + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + "0245 Skip x%06x NameServer Rsp " + "Data: x%x x%x\n", Did, + vport->fc_flag, + vport->fc_rscn_id_cnt); + } + } +} + static int -lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size) +lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint8_t fc4_type, + uint32_t Size) { struct lpfc_hba *phba = vport->phba; struct lpfc_sli_ct_request *Response = @@ -499,97 +584,12 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size) */ if ((Did != vport->fc_myDID) && ((lpfc_find_vport_by_did(phba, Did) == NULL) || - vport->cfg_peer_port_login)) { - if ((vport->port_type != LPFC_NPIV_PORT) || - (!(vport->ct_flags & FC_CT_RFF_ID)) || - (!vport->cfg_restrict_login)) { - ndlp = lpfc_setup_disc_node(vport, Did); - if (ndlp && NLP_CHK_NODE_ACT(ndlp)) { - lpfc_debugfs_disc_trc(vport, - LPFC_DISC_TRC_CT, - "Parse GID_FTrsp: " - "did:x%x flg:x%x x%x", - Did, ndlp->nlp_flag, - vport->fc_flag); - - lpfc_printf_vlog(vport, - KERN_INFO, - LOG_DISCOVERY, - "0238 Process " - "x%x NameServer Rsp" - "Data: x%x x%x x%x\n", - Did, ndlp->nlp_flag, - vport->fc_flag, - vport->fc_rscn_id_cnt); - } else { - lpfc_debugfs_disc_trc(vport, - LPFC_DISC_TRC_CT, - "Skip1 GID_FTrsp: " - "did:x%x flg:x%x cnt:%d", - Did, vport->fc_flag, - vport->fc_rscn_id_cnt); - - lpfc_printf_vlog(vport, - KERN_INFO, - LOG_DISCOVERY, - "0239 Skip x%x " - "NameServer Rsp Data: " - "x%x x%x\n", - Did, vport->fc_flag, - vport->fc_rscn_id_cnt); - } - - } else { - if (!(vport->fc_flag & FC_RSCN_MODE) || - (lpfc_rscn_payload_check(vport, Did))) { - lpfc_debugfs_disc_trc(vport, - LPFC_DISC_TRC_CT, - "Query GID_FTrsp: " - "did:x%x flg:x%x cnt:%d", - Did, vport->fc_flag, - vport->fc_rscn_id_cnt); - - /* This NPortID was previously - * a FCP target, * Don't even - * bother to send GFF_ID. - */ - ndlp = lpfc_findnode_did(vport, - Did); - if (ndlp && - NLP_CHK_NODE_ACT(ndlp) - && (ndlp->nlp_type & - NLP_FCP_TARGET)) - lpfc_setup_disc_node - (vport, Did); - else if (lpfc_ns_cmd(vport, - SLI_CTNS_GFF_ID, - 0, Did) == 0) - vport->num_disc_nodes++; - else - lpfc_setup_disc_node - (vport, Did); - } - else { - lpfc_debugfs_disc_trc(vport, - LPFC_DISC_TRC_CT, - "Skip2 GID_FTrsp: " - "did:x%x flg:x%x cnt:%d", - Did, vport->fc_flag, - vport->fc_rscn_id_cnt); - - lpfc_printf_vlog(vport, - KERN_INFO, - LOG_DISCOVERY, - "0245 Skip x%x " - "NameServer Rsp Data: " - "x%x x%x\n", - Did, vport->fc_flag, - vport->fc_rscn_id_cnt); - } - } - } + vport->cfg_peer_port_login)) + lpfc_prep_node_fc4type(vport, Did, fc4_type); + if (CTentry & (cpu_to_be32(SLI_CT_LAST_ENTRY))) goto nsout1; + Cnt -= sizeof(uint32_t); } ctptr = NULL; @@ -609,16 +609,18 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct Scsi_Host *shost = lpfc_shost_from_vport(vport); IOCB_t *irsp; struct lpfc_dmabuf *outp; + struct lpfc_dmabuf *inp; struct lpfc_sli_ct_request *CTrsp; + struct lpfc_sli_ct_request *CTreq; struct lpfc_nodelist *ndlp; - int rc; + int rc, type; /* First save ndlp, before we overwrite it */ ndlp = cmdiocb->context_un.ndlp; /* we pass cmdiocb to state machine which needs rspiocb as well */ cmdiocb->context_un.rsp_iocb = rspiocb; - + inp = (struct lpfc_dmabuf *) cmdiocb->context1; outp = (struct lpfc_dmabuf *) cmdiocb->context2; irsp = &rspiocb->iocb; @@ -656,9 +658,14 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, IOERR_NO_RESOURCES) vport->fc_ns_retry++; + type = lpfc_get_gidft_type(vport, cmdiocb); + if (type == 0) + goto out; + /* CT command is being retried */ + vport->gidft_inp--; rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, - vport->fc_ns_retry, 0); + vport->fc_ns_retry, type); if (rc == 0) goto out; } @@ -670,13 +677,18 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, irsp->ulpStatus, vport->fc_ns_retry); } else { /* Good status, continue checking */ + CTreq = (struct lpfc_sli_ct_request *) inp->virt; CTrsp = (struct lpfc_sli_ct_request *) outp->virt; if (CTrsp->CommandResponse.bits.CmdRsp == cpu_to_be16(SLI_CT_RESPONSE_FS_ACC)) { lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, - "0208 NameServer Rsp Data: x%x\n", - vport->fc_flag); - lpfc_ns_rsp(vport, outp, + "0208 NameServer Rsp Data: x%x x%x\n", + vport->fc_flag, + CTreq->un.gid.Fc4Type); + + lpfc_ns_rsp(vport, + outp, + CTreq->un.gid.Fc4Type, (uint32_t) (irsp->un.genreq64.bdl.bdeSize)); } else if (CTrsp->CommandResponse.bits.CmdRsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { @@ -731,9 +743,11 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, (uint32_t) CTrsp->ReasonCode, (uint32_t) CTrsp->Explanation); } + vport->gidft_inp--; } /* Link up / RSCN discovery */ - if (vport->num_disc_nodes == 0) { + if ((vport->num_disc_nodes == 0) && + (vport->gidft_inp == 0)) { /* * The driver has cycled through all Nports in the RSCN payload. * Complete the handling by cleaning up and marking the @@ -881,6 +895,60 @@ out: return; } +static void +lpfc_cmpl_ct_cmd_gft_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, + struct lpfc_iocbq *rspiocb) +{ + struct lpfc_vport *vport = cmdiocb->vport; + IOCB_t *irsp = &rspiocb->iocb; + struct lpfc_dmabuf *inp = (struct lpfc_dmabuf *)cmdiocb->context1; + struct lpfc_dmabuf *outp = (struct lpfc_dmabuf *)cmdiocb->context2; + struct lpfc_sli_ct_request *CTrsp; + int did; + struct lpfc_nodelist *ndlp; + uint32_t fc4_data_0, fc4_data_1; + + did = ((struct lpfc_sli_ct_request *)inp->virt)->un.gft.PortId; + did = be32_to_cpu(did); + + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, + "GFT_ID cmpl: status:x%x/x%x did:x%x", + irsp->ulpStatus, irsp->un.ulpWord[4], did); + + if (irsp->ulpStatus == IOSTAT_SUCCESS) { + /* Good status, continue checking */ + CTrsp = (struct lpfc_sli_ct_request *)outp->virt; + fc4_data_0 = be32_to_cpu(CTrsp->un.gft_acc.fc4_types[0]); + fc4_data_1 = be32_to_cpu(CTrsp->un.gft_acc.fc4_types[1]); + lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, + "3062 DID x%06x GFT Wd0 x%08x Wd1 x%08x\n", + did, fc4_data_0, fc4_data_1); + + ndlp = lpfc_findnode_did(vport, did); + if (ndlp) { + /* The bitmask value for FCP and NVME FCP types is + * the same because they are 32 bits distant from + * each other in word0 and word0. + */ + if (fc4_data_0 & LPFC_FC4_TYPE_BITMASK) + ndlp->nlp_fc4_type |= NLP_FC4_FCP; + if (fc4_data_1 & LPFC_FC4_TYPE_BITMASK) + ndlp->nlp_fc4_type |= NLP_FC4_NVME; + lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, + "3064 Setting ndlp %p, DID x%06x with " + "FC4 x%08x, Data: x%08x x%08x\n", + ndlp, did, ndlp->nlp_fc4_type, + FC_TYPE_FCP, FC_TYPE_NVME); + } + ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE); + lpfc_issue_els_prli(vport, ndlp, 0); + } else + lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, + "3065 GFT_ID failed x%08x\n", irsp->ulpStatus); + + lpfc_ct_free_iocb(phba, cmdiocb); +} static void lpfc_cmpl_ct(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, @@ -1071,31 +1139,27 @@ lpfc_cmpl_ct_cmd_rff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, return; } +/* + * Although the symbolic port name is thought to be an integer + * as of January 18, 2016, leave it as a string until more of + * the record state becomes defined. + */ int lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol, size_t size) { int n; - uint8_t *wwn = vport->phba->wwpn; - n = snprintf(symbol, size, - "Emulex PPN-%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - wwn[0], wwn[1], wwn[2], wwn[3], - wwn[4], wwn[5], wwn[6], wwn[7]); - - if (vport->port_type == LPFC_PHYSICAL_PORT) - return n; - - if (n < size) - n += snprintf(symbol + n, size - n, " VPort-%d", vport->vpi); - - if (n < size && - strlen(vport->fc_vport->symbolic_name)) - n += snprintf(symbol + n, size - n, " VName-%s", - vport->fc_vport->symbolic_name); + /* + * Use the lpfc board number as the Symbolic Port + * Name object. NPIV is not in play so this integer + * value is sufficient and unique per FC-ID. + */ + n = snprintf(symbol, size, "%d", vport->phba->brd_no); return n; } + int lpfc_vport_symbolic_node_name(struct lpfc_vport *vport, char *symbol, size_t size) @@ -1106,24 +1170,26 @@ lpfc_vport_symbolic_node_name(struct lpfc_vport *vport, char *symbol, lpfc_decode_firmware_rev(vport->phba, fwrev, 0); n = snprintf(symbol, size, "Emulex %s", vport->phba->ModelName); - if (size < n) return n; - n += snprintf(symbol + n, size - n, " FV%s", fwrev); + n += snprintf(symbol + n, size - n, " FV%s", fwrev); if (size < n) return n; - n += snprintf(symbol + n, size - n, " DV%s", lpfc_release_version); + n += snprintf(symbol + n, size - n, " DV%s.", + lpfc_release_version); if (size < n) return n; - n += snprintf(symbol + n, size - n, " HN:%s", init_utsname()->nodename); - /* Note :- OS name is "Linux" */ + n += snprintf(symbol + n, size - n, " HN:%s.", + init_utsname()->nodename); if (size < n) return n; - n += snprintf(symbol + n, size - n, " OS:%s", init_utsname()->sysname); + /* Note :- OS name is "Linux" */ + n += snprintf(symbol + n, size - n, " OS:%s\n", + init_utsname()->sysname); return n; } @@ -1148,6 +1214,27 @@ lpfc_find_map_node(struct lpfc_vport *vport) } /* + * This routine will return the FC4 Type associated with the CT + * GID_FT command. + */ +int +lpfc_get_gidft_type(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_sli_ct_request *CtReq; + struct lpfc_dmabuf *mp; + uint32_t type; + + mp = cmdiocb->context1; + if (mp == NULL) + return 0; + CtReq = (struct lpfc_sli_ct_request *)mp->virt; + type = (uint32_t)CtReq->un.gid.Fc4Type; + if ((type != SLI_CTPT_FCP) && (type != SLI_CTPT_NVME)) + return 0; + return type; +} + +/* * lpfc_ns_cmd * Description: * Issue Cmd to NameServer @@ -1207,8 +1294,9 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, /* NameServer Req */ lpfc_printf_vlog(vport, KERN_INFO ,LOG_DISCOVERY, - "0236 NameServer Req Data: x%x x%x x%x\n", - cmdcode, vport->fc_flag, vport->fc_rscn_id_cnt); + "0236 NameServer Req Data: x%x x%x x%x x%x\n", + cmdcode, vport->fc_flag, vport->fc_rscn_id_cnt, + context); bpl = (struct ulp_bde64 *) bmp->virt; memset(bpl, 0, sizeof(struct ulp_bde64)); @@ -1219,6 +1307,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, bpl->tus.f.bdeSize = GID_REQUEST_SZ; else if (cmdcode == SLI_CTNS_GFF_ID) bpl->tus.f.bdeSize = GFF_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_GFT_ID) + bpl->tus.f.bdeSize = GFT_REQUEST_SZ; else if (cmdcode == SLI_CTNS_RFT_ID) bpl->tus.f.bdeSize = RFT_REQUEST_SZ; else if (cmdcode == SLI_CTNS_RNN_ID) @@ -1246,7 +1336,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, case SLI_CTNS_GID_FT: CtReq->CommandResponse.bits.CmdRsp = cpu_to_be16(SLI_CTNS_GID_FT); - CtReq->un.gid.Fc4Type = SLI_CTPT_FCP; + CtReq->un.gid.Fc4Type = context; + if (vport->port_state < LPFC_NS_QRY) vport->port_state = LPFC_NS_QRY; lpfc_set_disctmo(vport); @@ -1261,12 +1352,32 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, cmpl = lpfc_cmpl_ct_cmd_gff_id; break; + case SLI_CTNS_GFT_ID: + CtReq->CommandResponse.bits.CmdRsp = + cpu_to_be16(SLI_CTNS_GFT_ID); + CtReq->un.gft.PortId = cpu_to_be32(context); + cmpl = lpfc_cmpl_ct_cmd_gft_id; + break; + case SLI_CTNS_RFT_ID: vport->ct_flags &= ~FC_CT_RFT_ID; CtReq->CommandResponse.bits.CmdRsp = cpu_to_be16(SLI_CTNS_RFT_ID); CtReq->un.rft.PortId = cpu_to_be32(vport->fc_myDID); - CtReq->un.rft.fcpReg = 1; + + /* Register FC4 FCP type if enabled. */ + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) + CtReq->un.rft.fcpReg = 1; + + /* Register NVME type if enabled. Defined LE and swapped. + * rsvd[0] is used as word1 because of the hard-coded + * word0 usage in the ct_request data structure. + */ + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) + CtReq->un.rft.rsvd[0] = cpu_to_be32(0x00000100); + cmpl = lpfc_cmpl_ct_cmd_rft_id; break; @@ -1316,7 +1427,31 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, cpu_to_be16(SLI_CTNS_RFF_ID); CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID); CtReq->un.rff.fbits = FC4_FEATURE_INIT; - CtReq->un.rff.type_code = FC_TYPE_FCP; + + /* The driver always supports FC_TYPE_FCP. However, the + * caller can specify NVME (type x28) as well. But only + * these that FC4 type is supported. + */ + if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) && + (context == FC_TYPE_NVME)) { + if ((vport == phba->pport) && phba->nvmet_support) { + CtReq->un.rff.fbits = (FC4_FEATURE_TARGET | + FC4_FEATURE_NVME_DISC); + lpfc_nvmet_update_targetport(phba); + } else { + lpfc_nvme_update_localport(vport); + } + CtReq->un.rff.type_code = context; + + } else if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) && + (context == FC_TYPE_FCP)) + CtReq->un.rff.type_code = context; + + else + goto ns_cmd_free_bmpvirt; + cmpl = lpfc_cmpl_ct_cmd_rff_id; break; } @@ -1337,6 +1472,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, */ lpfc_nlp_put(ndlp); +ns_cmd_free_bmpvirt: lpfc_mbuf_free(phba, bmp->virt, bmp->phys); ns_cmd_free_bmp: kfree(bmp); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index caa7a7b0ec53..9f4798e9d938 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2007-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -34,6 +36,9 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> + +#include <linux/nvme-fc-driver.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -41,8 +46,10 @@ #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" +#include "lpfc_nvmet.h" #include "lpfc_logmsg.h" #include "lpfc_crtn.h" #include "lpfc_vport.h" @@ -99,6 +106,12 @@ module_param(lpfc_debugfs_max_slow_ring_trc, int, S_IRUGO); MODULE_PARM_DESC(lpfc_debugfs_max_slow_ring_trc, "Set debugfs slow ring trace depth"); +/* This MUST be a power of 2 */ +static int lpfc_debugfs_max_nvmeio_trc; +module_param(lpfc_debugfs_max_nvmeio_trc, int, 0444); +MODULE_PARM_DESC(lpfc_debugfs_max_nvmeio_trc, + "Set debugfs NVME IO trace depth"); + static int lpfc_debugfs_mask_disc_trc; module_param(lpfc_debugfs_mask_disc_trc, int, S_IRUGO); MODULE_PARM_DESC(lpfc_debugfs_mask_disc_trc, @@ -484,20 +497,23 @@ lpfc_debugfs_dumpHostSlim_data(struct lpfc_hba *phba, char *buf, int size) off += (8 * sizeof(uint32_t)); } - for (i = 0; i < 4; i++) { - pgpp = &phba->port_gp[i]; - pring = &psli->ring[i]; - len += snprintf(buf+len, size-len, - "Ring %d: CMD GetInx:%d (Max:%d Next:%d " - "Local:%d flg:x%x) RSP PutInx:%d Max:%d\n", - i, pgpp->cmdGetInx, pring->sli.sli3.numCiocb, - pring->sli.sli3.next_cmdidx, - pring->sli.sli3.local_getidx, - pring->flag, pgpp->rspPutInx, - pring->sli.sli3.numRiocb); - } - if (phba->sli_rev <= LPFC_SLI_REV3) { + for (i = 0; i < 4; i++) { + pgpp = &phba->port_gp[i]; + pring = &psli->sli3_ring[i]; + len += snprintf(buf+len, size-len, + "Ring %d: CMD GetInx:%d " + "(Max:%d Next:%d " + "Local:%d flg:x%x) " + "RSP PutInx:%d Max:%d\n", + i, pgpp->cmdGetInx, + pring->sli.sli3.numCiocb, + pring->sli.sli3.next_cmdidx, + pring->sli.sli3.local_getidx, + pring->flag, pgpp->rspPutInx, + pring->sli.sli3.numRiocb); + } + word0 = readl(phba->HAregaddr); word1 = readl(phba->CAregaddr); word2 = readl(phba->HSregaddr); @@ -530,11 +546,18 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) int len = 0; int cnt; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *ndlp; - unsigned char *statep, *name; + unsigned char *statep; + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport; + struct lpfc_nvmet_tgtport *tgtp; + struct nvme_fc_remote_port *nrport; cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE); + len += snprintf(buf+len, size-len, "\nFCP Nodelist Entries ...\n"); spin_lock_irq(shost->host_lock); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { if (!cnt) { @@ -574,36 +597,32 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) default: statep = "UNKNOWN"; } - len += snprintf(buf+len, size-len, "%s DID:x%06x ", - statep, ndlp->nlp_DID); - name = (unsigned char *)&ndlp->nlp_portname; - len += snprintf(buf+len, size-len, - "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x ", - *name, *(name+1), *(name+2), *(name+3), - *(name+4), *(name+5), *(name+6), *(name+7)); - name = (unsigned char *)&ndlp->nlp_nodename; - len += snprintf(buf+len, size-len, - "WWNN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x ", - *name, *(name+1), *(name+2), *(name+3), - *(name+4), *(name+5), *(name+6), *(name+7)); + len += snprintf(buf+len, size-len, "%s DID:x%06x ", + statep, ndlp->nlp_DID); + len += snprintf(buf+len, size-len, + "WWPN x%llx ", + wwn_to_u64(ndlp->nlp_portname.u.wwn)); + len += snprintf(buf+len, size-len, + "WWNN x%llx ", + wwn_to_u64(ndlp->nlp_nodename.u.wwn)); if (ndlp->nlp_flag & NLP_RPI_REGISTERED) - len += snprintf(buf+len, size-len, "RPI:%03d ", - ndlp->nlp_rpi); + len += snprintf(buf+len, size-len, "RPI:%03d ", + ndlp->nlp_rpi); else - len += snprintf(buf+len, size-len, "RPI:none "); + len += snprintf(buf+len, size-len, "RPI:none "); len += snprintf(buf+len, size-len, "flag:x%08x ", ndlp->nlp_flag); if (!ndlp->nlp_type) - len += snprintf(buf+len, size-len, "UNKNOWN_TYPE "); + len += snprintf(buf+len, size-len, "UNKNOWN_TYPE "); if (ndlp->nlp_type & NLP_FC_NODE) - len += snprintf(buf+len, size-len, "FC_NODE "); + len += snprintf(buf+len, size-len, "FC_NODE "); if (ndlp->nlp_type & NLP_FABRIC) - len += snprintf(buf+len, size-len, "FABRIC "); + len += snprintf(buf+len, size-len, "FABRIC "); if (ndlp->nlp_type & NLP_FCP_TARGET) - len += snprintf(buf+len, size-len, "FCP_TGT sid:%d ", + len += snprintf(buf+len, size-len, "FCP_TGT sid:%d ", ndlp->nlp_sid); if (ndlp->nlp_type & NLP_FCP_INITIATOR) - len += snprintf(buf+len, size-len, "FCP_INITIATOR "); + len += snprintf(buf+len, size-len, "FCP_INITIATOR "); len += snprintf(buf+len, size-len, "usgmap:%x ", ndlp->nlp_usg_map); len += snprintf(buf+len, size-len, "refcnt:%x", @@ -611,8 +630,592 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf+len, size-len, "\n"); } spin_unlock_irq(shost->host_lock); + + if (phba->nvmet_support && phba->targetport && (vport == phba->pport)) { + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + len += snprintf(buf + len, size - len, + "\nNVME Targetport Entry ...\n"); + + /* Port state is only one of two values for now. */ + if (phba->targetport->port_id) + statep = "REGISTERED"; + else + statep = "INIT"; + len += snprintf(buf + len, size - len, + "TGT WWNN x%llx WWPN x%llx State %s\n", + wwn_to_u64(vport->fc_nodename.u.wwn), + wwn_to_u64(vport->fc_portname.u.wwn), + statep); + len += snprintf(buf + len, size - len, + " Targetport DID x%06x\n", + phba->targetport->port_id); + goto out_exit; + } + + len += snprintf(buf + len, size - len, + "\nNVME Lport/Rport Entries ...\n"); + + localport = vport->localport; + if (!localport) + goto out_exit; + + spin_lock_irq(shost->host_lock); + lport = (struct lpfc_nvme_lport *)localport->private; + + /* Port state is only one of two values for now. */ + if (localport->port_id) + statep = "ONLINE"; + else + statep = "UNKNOWN "; + + len += snprintf(buf + len, size - len, + "Lport DID x%06x PortState %s\n", + localport->port_id, statep); + + len += snprintf(buf + len, size - len, "\tRport List:\n"); + list_for_each_entry(rport, &lport->rport_list, list) { + /* local short-hand pointer. */ + nrport = rport->remoteport; + + /* Port state is only one of two values for now. */ + switch (nrport->port_state) { + case FC_OBJSTATE_ONLINE: + statep = "ONLINE"; + break; + case FC_OBJSTATE_UNKNOWN: + statep = "UNKNOWN "; + break; + default: + statep = "UNSUPPORTED"; + break; + } + + /* Tab in to show lport ownership. */ + len += snprintf(buf + len, size - len, + "\t%s Port ID:x%06x ", + statep, nrport->port_id); + len += snprintf(buf + len, size - len, "WWPN x%llx ", + nrport->port_name); + len += snprintf(buf + len, size - len, "WWNN x%llx ", + nrport->node_name); + switch (nrport->port_role) { + case FC_PORT_ROLE_NVME_INITIATOR: + len += snprintf(buf + len, size - len, + "NVME INITIATOR "); + break; + case FC_PORT_ROLE_NVME_TARGET: + len += snprintf(buf + len, size - len, + "NVME TARGET "); + break; + case FC_PORT_ROLE_NVME_DISCOVERY: + len += snprintf(buf + len, size - len, + "NVME DISCOVERY "); + break; + default: + len += snprintf(buf + len, size - len, + "UNKNOWN ROLE x%x", + nrport->port_role); + break; + } + + /* Terminate the string. */ + len += snprintf(buf + len, size - len, "\n"); + } + + spin_unlock_irq(shost->host_lock); + out_exit: + return len; +} + +/** + * lpfc_debugfs_nvmestat_data - Dump target node list to a buffer + * @vport: The vport to gather target node info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine dumps the NVME statistics associated with @vport + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) +{ + struct lpfc_hba *phba = vport->phba; + struct lpfc_nvmet_tgtport *tgtp; + int len = 0; + + if (phba->nvmet_support) { + if (!phba->targetport) + return len; + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + len += snprintf(buf+len, size-len, + "\nNVME Targetport Statistics\n"); + + len += snprintf(buf+len, size-len, + "LS: Rcv %08x Drop %08x Abort %08x\n", + atomic_read(&tgtp->rcv_ls_req_in), + atomic_read(&tgtp->rcv_ls_req_drop), + atomic_read(&tgtp->xmt_ls_abort)); + if (atomic_read(&tgtp->rcv_ls_req_in) != + atomic_read(&tgtp->rcv_ls_req_out)) { + len += snprintf(buf+len, size-len, + "Rcv LS: in %08x != out %08x\n", + atomic_read(&tgtp->rcv_ls_req_in), + atomic_read(&tgtp->rcv_ls_req_out)); + } + + len += snprintf(buf+len, size-len, + "LS: Xmt %08x Drop %08x Cmpl %08x Err %08x\n", + atomic_read(&tgtp->xmt_ls_rsp), + atomic_read(&tgtp->xmt_ls_drop), + atomic_read(&tgtp->xmt_ls_rsp_cmpl), + atomic_read(&tgtp->xmt_ls_rsp_error)); + + len += snprintf(buf+len, size-len, + "FCP: Rcv %08x Drop %08x\n", + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_drop)); + + if (atomic_read(&tgtp->rcv_fcp_cmd_in) != + atomic_read(&tgtp->rcv_fcp_cmd_out)) { + len += snprintf(buf+len, size-len, + "Rcv FCP: in %08x != out %08x\n", + atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_out)); + } + + len += snprintf(buf+len, size-len, + "FCP Rsp: read %08x readrsp %08x write %08x rsp %08x\n", + atomic_read(&tgtp->xmt_fcp_read), + atomic_read(&tgtp->xmt_fcp_read_rsp), + atomic_read(&tgtp->xmt_fcp_write), + atomic_read(&tgtp->xmt_fcp_rsp)); + + len += snprintf(buf+len, size-len, + "FCP Rsp: abort %08x drop %08x\n", + atomic_read(&tgtp->xmt_fcp_abort), + atomic_read(&tgtp->xmt_fcp_drop)); + + len += snprintf(buf+len, size-len, + "FCP Rsp Cmpl: %08x err %08x drop %08x\n", + atomic_read(&tgtp->xmt_fcp_rsp_cmpl), + atomic_read(&tgtp->xmt_fcp_rsp_error), + atomic_read(&tgtp->xmt_fcp_rsp_drop)); + + len += snprintf(buf+len, size-len, + "ABORT: Xmt %08x Err %08x Cmpl %08x", + atomic_read(&tgtp->xmt_abort_rsp), + atomic_read(&tgtp->xmt_abort_rsp_error), + atomic_read(&tgtp->xmt_abort_cmpl)); + + len += snprintf(buf+len, size-len, "\n"); + } else { + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) + return len; + + len += snprintf(buf + len, size - len, + "\nNVME Lport Statistics\n"); + + len += snprintf(buf + len, size - len, + "LS: Xmt %016llx Cmpl %016llx\n", + phba->fc4NvmeLsRequests, + phba->fc4NvmeLsCmpls); + + len += snprintf(buf + len, size - len, + "FCP: Rd %016llx Wr %016llx IO %016llx\n", + phba->fc4NvmeInputRequests, + phba->fc4NvmeOutputRequests, + phba->fc4NvmeControlRequests); + + len += snprintf(buf + len, size - len, + " Cmpl %016llx\n", phba->fc4NvmeIoCmpls); + } + return len; } + + +/** + * lpfc_debugfs_nvmektime_data - Dump target node list to a buffer + * @vport: The vport to gather target node info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine dumps the NVME statistics associated with @vport + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_nvmektime_data(struct lpfc_vport *vport, char *buf, int size) +{ + struct lpfc_hba *phba = vport->phba; + int len = 0; + + if (phba->nvmet_support == 0) { + /* NVME Initiator */ + len += snprintf(buf + len, PAGE_SIZE - len, + "ktime %s: Total Samples: %lld\n", + (phba->ktime_on ? "Enabled" : "Disabled"), + phba->ktime_data_samples); + if (phba->ktime_data_samples == 0) + return len; + + len += snprintf( + buf + len, PAGE_SIZE - len, + "Segment 1: Last NVME Cmd cmpl " + "done -to- Start of next NVME cnd (in driver)\n"); + len += snprintf( + buf + len, PAGE_SIZE - len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg1_total, + phba->ktime_data_samples), + phba->ktime_seg1_min, + phba->ktime_seg1_max); + len += snprintf( + buf + len, PAGE_SIZE - len, + "Segment 2: Driver start of NVME cmd " + "-to- Firmware WQ doorbell\n"); + len += snprintf( + buf + len, PAGE_SIZE - len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg2_total, + phba->ktime_data_samples), + phba->ktime_seg2_min, + phba->ktime_seg2_max); + len += snprintf( + buf + len, PAGE_SIZE - len, + "Segment 3: Firmware WQ doorbell -to- " + "MSI-X ISR cmpl\n"); + len += snprintf( + buf + len, PAGE_SIZE - len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg3_total, + phba->ktime_data_samples), + phba->ktime_seg3_min, + phba->ktime_seg3_max); + len += snprintf( + buf + len, PAGE_SIZE - len, + "Segment 4: MSI-X ISR cmpl -to- " + "NVME cmpl done\n"); + len += snprintf( + buf + len, PAGE_SIZE - len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg4_total, + phba->ktime_data_samples), + phba->ktime_seg4_min, + phba->ktime_seg4_max); + len += snprintf( + buf + len, PAGE_SIZE - len, + "Total IO avg time: %08lld\n", + div_u64(phba->ktime_seg1_total + + phba->ktime_seg2_total + + phba->ktime_seg3_total + + phba->ktime_seg4_total, + phba->ktime_data_samples)); + return len; + } + + /* NVME Target */ + len += snprintf(buf + len, PAGE_SIZE-len, + "ktime %s: Total Samples: %lld %lld\n", + (phba->ktime_on ? "Enabled" : "Disabled"), + phba->ktime_data_samples, + phba->ktime_status_samples); + if (phba->ktime_data_samples == 0) + return len; + + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 1: MSI-X ISR Rcv cmd -to- " + "cmd pass to NVME Layer\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg1_total, + phba->ktime_data_samples), + phba->ktime_seg1_min, + phba->ktime_seg1_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 2: cmd pass to NVME Layer- " + "-to- Driver rcv cmd OP (action)\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg2_total, + phba->ktime_data_samples), + phba->ktime_seg2_min, + phba->ktime_seg2_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 3: Driver rcv cmd OP -to- " + "Firmware WQ doorbell: cmd\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg3_total, + phba->ktime_data_samples), + phba->ktime_seg3_min, + phba->ktime_seg3_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 4: Firmware WQ doorbell: cmd " + "-to- MSI-X ISR for cmd cmpl\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg4_total, + phba->ktime_data_samples), + phba->ktime_seg4_min, + phba->ktime_seg4_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 5: MSI-X ISR for cmd cmpl " + "-to- NVME layer passed cmd done\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg5_total, + phba->ktime_data_samples), + phba->ktime_seg5_min, + phba->ktime_seg5_max); + + if (phba->ktime_status_samples == 0) { + len += snprintf(buf + len, PAGE_SIZE-len, + "Total: cmd received by MSI-X ISR " + "-to- cmd completed on wire\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld " + "max %08lld\n", + div_u64(phba->ktime_seg10_total, + phba->ktime_data_samples), + phba->ktime_seg10_min, + phba->ktime_seg10_max); + return len; + } + + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 6: NVME layer passed cmd done " + "-to- Driver rcv rsp status OP\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg6_total, + phba->ktime_status_samples), + phba->ktime_seg6_min, + phba->ktime_seg6_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 7: Driver rcv rsp status OP " + "-to- Firmware WQ doorbell: status\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg7_total, + phba->ktime_status_samples), + phba->ktime_seg7_min, + phba->ktime_seg7_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 8: Firmware WQ doorbell: status" + " -to- MSI-X ISR for status cmpl\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg8_total, + phba->ktime_status_samples), + phba->ktime_seg8_min, + phba->ktime_seg8_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Segment 9: MSI-X ISR for status cmpl " + "-to- NVME layer passed status done\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg9_total, + phba->ktime_status_samples), + phba->ktime_seg9_min, + phba->ktime_seg9_max); + len += snprintf(buf + len, PAGE_SIZE-len, + "Total: cmd received by MSI-X ISR -to- " + "cmd completed on wire\n"); + len += snprintf(buf + len, PAGE_SIZE-len, + "avg:%08lld min:%08lld max %08lld\n", + div_u64(phba->ktime_seg10_total, + phba->ktime_status_samples), + phba->ktime_seg10_min, + phba->ktime_seg10_max); + return len; +} + +/** + * lpfc_debugfs_nvmeio_trc_data - Dump NVME IO trace list to a buffer + * @phba: The phba to gather target node info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine dumps the NVME IO trace associated with @phba + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_nvmeio_trc_data(struct lpfc_hba *phba, char *buf, int size) +{ + struct lpfc_debugfs_nvmeio_trc *dtp; + int i, state, index, skip; + int len = 0; + + state = phba->nvmeio_trc_on; + + index = (atomic_read(&phba->nvmeio_trc_cnt) + 1) & + (phba->nvmeio_trc_size - 1); + skip = phba->nvmeio_trc_output_idx; + + len += snprintf(buf + len, size - len, + "%s IO Trace %s: next_idx %d skip %d size %d\n", + (phba->nvmet_support ? "NVME" : "NVMET"), + (state ? "Enabled" : "Disabled"), + index, skip, phba->nvmeio_trc_size); + + if (!phba->nvmeio_trc || state) + return len; + + /* trace MUST bhe off to continue */ + + for (i = index; i < phba->nvmeio_trc_size; i++) { + if (skip) { + skip--; + continue; + } + dtp = phba->nvmeio_trc + i; + phba->nvmeio_trc_output_idx++; + + if (!dtp->fmt) + continue; + + len += snprintf(buf + len, size - len, dtp->fmt, + dtp->data1, dtp->data2, dtp->data3); + + if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) { + phba->nvmeio_trc_output_idx = 0; + len += snprintf(buf + len, size - len, + "Trace Complete\n"); + goto out; + } + + if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) { + len += snprintf(buf + len, size - len, + "Trace Continue (%d of %d)\n", + phba->nvmeio_trc_output_idx, + phba->nvmeio_trc_size); + goto out; + } + } + for (i = 0; i < index; i++) { + if (skip) { + skip--; + continue; + } + dtp = phba->nvmeio_trc + i; + phba->nvmeio_trc_output_idx++; + + if (!dtp->fmt) + continue; + + len += snprintf(buf + len, size - len, dtp->fmt, + dtp->data1, dtp->data2, dtp->data3); + + if (phba->nvmeio_trc_output_idx >= phba->nvmeio_trc_size) { + phba->nvmeio_trc_output_idx = 0; + len += snprintf(buf + len, size - len, + "Trace Complete\n"); + goto out; + } + + if (len >= (size - LPFC_DEBUG_OUT_LINE_SZ)) { + len += snprintf(buf + len, size - len, + "Trace Continue (%d of %d)\n", + phba->nvmeio_trc_output_idx, + phba->nvmeio_trc_size); + goto out; + } + } + + len += snprintf(buf + len, size - len, + "Trace Done\n"); +out: + return len; +} + +/** + * lpfc_debugfs_cpucheck_data - Dump target node list to a buffer + * @vport: The vport to gather target node info from. + * @buf: The buffer to dump log into. + * @size: The maximum amount of data to process. + * + * Description: + * This routine dumps the NVME statistics associated with @vport + * + * Return Value: + * This routine returns the amount of bytes that were dumped into @buf and will + * not exceed @size. + **/ +static int +lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size) +{ + struct lpfc_hba *phba = vport->phba; + int i; + int len = 0; + uint32_t tot_xmt = 0; + uint32_t tot_rcv = 0; + uint32_t tot_cmpl = 0; + uint32_t tot_ccmpl = 0; + + if (phba->nvmet_support == 0) { + /* NVME Initiator */ + len += snprintf(buf + len, PAGE_SIZE - len, + "CPUcheck %s\n", + (phba->cpucheck_on & LPFC_CHECK_NVME_IO ? + "Enabled" : "Disabled")); + for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { + if (i >= LPFC_CHECK_CPU_CNT) + break; + len += snprintf(buf + len, PAGE_SIZE - len, + "%02d: xmit x%08x cmpl x%08x\n", + i, phba->cpucheck_xmt_io[i], + phba->cpucheck_cmpl_io[i]); + tot_xmt += phba->cpucheck_xmt_io[i]; + tot_cmpl += phba->cpucheck_cmpl_io[i]; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "tot:xmit x%08x cmpl x%08x\n", + tot_xmt, tot_cmpl); + return len; + } + + /* NVME Target */ + len += snprintf(buf + len, PAGE_SIZE - len, + "CPUcheck %s ", + (phba->cpucheck_on & LPFC_CHECK_NVMET_IO ? + "IO Enabled - " : "IO Disabled - ")); + len += snprintf(buf + len, PAGE_SIZE - len, + "%s\n", + (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV ? + "Rcv Enabled\n" : "Rcv Disabled\n")); + for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { + if (i >= LPFC_CHECK_CPU_CNT) + break; + len += snprintf(buf + len, PAGE_SIZE - len, + "%02d: xmit x%08x ccmpl x%08x " + "cmpl x%08x rcv x%08x\n", + i, phba->cpucheck_xmt_io[i], + phba->cpucheck_ccmpl_io[i], + phba->cpucheck_cmpl_io[i], + phba->cpucheck_rcv_io[i]); + tot_xmt += phba->cpucheck_xmt_io[i]; + tot_rcv += phba->cpucheck_rcv_io[i]; + tot_cmpl += phba->cpucheck_cmpl_io[i]; + tot_ccmpl += phba->cpucheck_ccmpl_io[i]; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "tot:xmit x%08x ccmpl x%08x cmpl x%08x rcv x%08x\n", + tot_xmt, tot_ccmpl, tot_cmpl, tot_rcv); + return len; +} + #endif /** @@ -697,6 +1300,40 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt, return; } +/** + * lpfc_debugfs_nvme_trc - Store NVME/NVMET trace log + * @phba: The phba to associate this trace string with for retrieval. + * @fmt: Format string to be displayed when dumping the log. + * @data1: 1st data parameter to be applied to @fmt. + * @data2: 2nd data parameter to be applied to @fmt. + * @data3: 3rd data parameter to be applied to @fmt. + * + * Description: + * This routine is used by the driver code to add a debugfs log entry to the + * nvme trace buffer associated with @phba. @fmt, @data1, @data2, and + * @data3 are used like printf when displaying the log. + **/ +inline void +lpfc_debugfs_nvme_trc(struct lpfc_hba *phba, char *fmt, + uint16_t data1, uint16_t data2, uint32_t data3) +{ +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + struct lpfc_debugfs_nvmeio_trc *dtp; + int index; + + if (!phba->nvmeio_trc_on || !phba->nvmeio_trc) + return; + + index = atomic_inc_return(&phba->nvmeio_trc_cnt) & + (phba->nvmeio_trc_size - 1); + dtp = phba->nvmeio_trc + index; + dtp->fmt = fmt; + dtp->data1 = data1; + dtp->data2 = data2; + dtp->data3 = data3; +#endif +} + #ifdef CONFIG_SCSI_LPFC_DEBUG_FS /** * lpfc_debugfs_disc_trc_open - Open the discovery trace log @@ -938,7 +1575,7 @@ lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file) goto out; /* Round to page boundary */ - printk(KERN_ERR "9059 BLKGRD: %s: _dump_buf_data=0x%p\n", + pr_err("9059 BLKGRD: %s: _dump_buf_data=0x%p\n", __func__, _dump_buf_data); debug->buffer = _dump_buf_data; if (!debug->buffer) { @@ -968,8 +1605,8 @@ lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file) goto out; /* Round to page boundary */ - printk(KERN_ERR "9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%pD\n", - __func__, _dump_buf_dif, file); + pr_err("9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%pD\n", + __func__, _dump_buf_dif, file); debug->buffer = _dump_buf_dif; if (!debug->buffer) { kfree(debug); @@ -1229,6 +1866,422 @@ lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file) return 0; } + +static int +lpfc_debugfs_nvmestat_open(struct inode *inode, struct file *file) +{ + struct lpfc_vport *vport = inode->i_private; + struct lpfc_debug *debug; + int rc = -ENOMEM; + + debug = kmalloc(sizeof(*debug), GFP_KERNEL); + if (!debug) + goto out; + + /* Round to page boundary */ + debug->buffer = kmalloc(LPFC_NVMESTAT_SIZE, GFP_KERNEL); + if (!debug->buffer) { + kfree(debug); + goto out; + } + + debug->len = lpfc_debugfs_nvmestat_data(vport, debug->buffer, + LPFC_NVMESTAT_SIZE); + + debug->i_private = inode->i_private; + file->private_data = debug; + + rc = 0; +out: + return rc; +} + +static ssize_t +lpfc_debugfs_nvmestat_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private; + struct lpfc_hba *phba = vport->phba; + struct lpfc_nvmet_tgtport *tgtp; + char mybuf[64]; + char *pbuf; + + if (!phba->targetport) + return -ENXIO; + + if (nbytes > 64) + nbytes = 64; + + /* Protect copy from user */ + if (!access_ok(VERIFY_READ, buf, nbytes)) + return -EFAULT; + + memset(mybuf, 0, sizeof(mybuf)); + + if (copy_from_user(mybuf, buf, nbytes)) + return -EFAULT; + pbuf = &mybuf[0]; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + if ((strncmp(pbuf, "reset", strlen("reset")) == 0) || + (strncmp(pbuf, "zero", strlen("zero")) == 0)) { + atomic_set(&tgtp->rcv_ls_req_in, 0); + atomic_set(&tgtp->rcv_ls_req_out, 0); + atomic_set(&tgtp->rcv_ls_req_drop, 0); + atomic_set(&tgtp->xmt_ls_abort, 0); + atomic_set(&tgtp->xmt_ls_rsp, 0); + atomic_set(&tgtp->xmt_ls_drop, 0); + atomic_set(&tgtp->xmt_ls_rsp_error, 0); + atomic_set(&tgtp->xmt_ls_rsp_cmpl, 0); + + atomic_set(&tgtp->rcv_fcp_cmd_in, 0); + atomic_set(&tgtp->rcv_fcp_cmd_out, 0); + atomic_set(&tgtp->rcv_fcp_cmd_drop, 0); + atomic_set(&tgtp->xmt_fcp_abort, 0); + atomic_set(&tgtp->xmt_fcp_drop, 0); + atomic_set(&tgtp->xmt_fcp_read_rsp, 0); + atomic_set(&tgtp->xmt_fcp_read, 0); + atomic_set(&tgtp->xmt_fcp_write, 0); + atomic_set(&tgtp->xmt_fcp_rsp, 0); + atomic_set(&tgtp->xmt_fcp_rsp_cmpl, 0); + atomic_set(&tgtp->xmt_fcp_rsp_error, 0); + atomic_set(&tgtp->xmt_fcp_rsp_drop, 0); + + atomic_set(&tgtp->xmt_abort_rsp, 0); + atomic_set(&tgtp->xmt_abort_rsp_error, 0); + atomic_set(&tgtp->xmt_abort_cmpl, 0); + } + return nbytes; +} + +static int +lpfc_debugfs_nvmektime_open(struct inode *inode, struct file *file) +{ + struct lpfc_vport *vport = inode->i_private; + struct lpfc_debug *debug; + int rc = -ENOMEM; + + debug = kmalloc(sizeof(*debug), GFP_KERNEL); + if (!debug) + goto out; + + /* Round to page boundary */ + debug->buffer = kmalloc(LPFC_NVMEKTIME_SIZE, GFP_KERNEL); + if (!debug->buffer) { + kfree(debug); + goto out; + } + + debug->len = lpfc_debugfs_nvmektime_data(vport, debug->buffer, + LPFC_NVMEKTIME_SIZE); + + debug->i_private = inode->i_private; + file->private_data = debug; + + rc = 0; +out: + return rc; +} + +static ssize_t +lpfc_debugfs_nvmektime_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private; + struct lpfc_hba *phba = vport->phba; + char mybuf[64]; + char *pbuf; + + if (nbytes > 64) + nbytes = 64; + + /* Protect copy from user */ + if (!access_ok(VERIFY_READ, buf, nbytes)) + return -EFAULT; + + memset(mybuf, 0, sizeof(mybuf)); + + if (copy_from_user(mybuf, buf, nbytes)) + return -EFAULT; + pbuf = &mybuf[0]; + + if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) { + phba->ktime_data_samples = 0; + phba->ktime_status_samples = 0; + phba->ktime_seg1_total = 0; + phba->ktime_seg1_max = 0; + phba->ktime_seg1_min = 0xffffffff; + phba->ktime_seg2_total = 0; + phba->ktime_seg2_max = 0; + phba->ktime_seg2_min = 0xffffffff; + phba->ktime_seg3_total = 0; + phba->ktime_seg3_max = 0; + phba->ktime_seg3_min = 0xffffffff; + phba->ktime_seg4_total = 0; + phba->ktime_seg4_max = 0; + phba->ktime_seg4_min = 0xffffffff; + phba->ktime_seg5_total = 0; + phba->ktime_seg5_max = 0; + phba->ktime_seg5_min = 0xffffffff; + phba->ktime_seg6_total = 0; + phba->ktime_seg6_max = 0; + phba->ktime_seg6_min = 0xffffffff; + phba->ktime_seg7_total = 0; + phba->ktime_seg7_max = 0; + phba->ktime_seg7_min = 0xffffffff; + phba->ktime_seg8_total = 0; + phba->ktime_seg8_max = 0; + phba->ktime_seg8_min = 0xffffffff; + phba->ktime_seg9_total = 0; + phba->ktime_seg9_max = 0; + phba->ktime_seg9_min = 0xffffffff; + phba->ktime_seg10_total = 0; + phba->ktime_seg10_max = 0; + phba->ktime_seg10_min = 0xffffffff; + + phba->ktime_on = 1; + return strlen(pbuf); + } else if ((strncmp(pbuf, "off", + sizeof("off") - 1) == 0)) { + phba->ktime_on = 0; + return strlen(pbuf); + } else if ((strncmp(pbuf, "zero", + sizeof("zero") - 1) == 0)) { + phba->ktime_data_samples = 0; + phba->ktime_status_samples = 0; + phba->ktime_seg1_total = 0; + phba->ktime_seg1_max = 0; + phba->ktime_seg1_min = 0xffffffff; + phba->ktime_seg2_total = 0; + phba->ktime_seg2_max = 0; + phba->ktime_seg2_min = 0xffffffff; + phba->ktime_seg3_total = 0; + phba->ktime_seg3_max = 0; + phba->ktime_seg3_min = 0xffffffff; + phba->ktime_seg4_total = 0; + phba->ktime_seg4_max = 0; + phba->ktime_seg4_min = 0xffffffff; + phba->ktime_seg5_total = 0; + phba->ktime_seg5_max = 0; + phba->ktime_seg5_min = 0xffffffff; + phba->ktime_seg6_total = 0; + phba->ktime_seg6_max = 0; + phba->ktime_seg6_min = 0xffffffff; + phba->ktime_seg7_total = 0; + phba->ktime_seg7_max = 0; + phba->ktime_seg7_min = 0xffffffff; + phba->ktime_seg8_total = 0; + phba->ktime_seg8_max = 0; + phba->ktime_seg8_min = 0xffffffff; + phba->ktime_seg9_total = 0; + phba->ktime_seg9_max = 0; + phba->ktime_seg9_min = 0xffffffff; + phba->ktime_seg10_total = 0; + phba->ktime_seg10_max = 0; + phba->ktime_seg10_min = 0xffffffff; + return strlen(pbuf); + } + return -EINVAL; +} + +static int +lpfc_debugfs_nvmeio_trc_open(struct inode *inode, struct file *file) +{ + struct lpfc_hba *phba = inode->i_private; + struct lpfc_debug *debug; + int rc = -ENOMEM; + + debug = kmalloc(sizeof(*debug), GFP_KERNEL); + if (!debug) + goto out; + + /* Round to page boundary */ + debug->buffer = kmalloc(LPFC_NVMEIO_TRC_SIZE, GFP_KERNEL); + if (!debug->buffer) { + kfree(debug); + goto out; + } + + debug->len = lpfc_debugfs_nvmeio_trc_data(phba, debug->buffer, + LPFC_NVMEIO_TRC_SIZE); + + debug->i_private = inode->i_private; + file->private_data = debug; + + rc = 0; +out: + return rc; +} + +static ssize_t +lpfc_debugfs_nvmeio_trc_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; + int i; + unsigned long sz; + char mybuf[64]; + char *pbuf; + + if (nbytes > 64) + nbytes = 64; + + /* Protect copy from user */ + if (!access_ok(VERIFY_READ, buf, nbytes)) + return -EFAULT; + + memset(mybuf, 0, sizeof(mybuf)); + + if (copy_from_user(mybuf, buf, nbytes)) + return -EFAULT; + pbuf = &mybuf[0]; + + if ((strncmp(pbuf, "off", sizeof("off") - 1) == 0)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0570 nvmeio_trc_off\n"); + phba->nvmeio_trc_output_idx = 0; + phba->nvmeio_trc_on = 0; + return strlen(pbuf); + } else if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0571 nvmeio_trc_on\n"); + phba->nvmeio_trc_output_idx = 0; + phba->nvmeio_trc_on = 1; + return strlen(pbuf); + } + + /* We must be off to allocate the trace buffer */ + if (phba->nvmeio_trc_on != 0) + return -EINVAL; + + /* If not on or off, the parameter is the trace buffer size */ + i = kstrtoul(pbuf, 0, &sz); + if (i) + return -EINVAL; + phba->nvmeio_trc_size = (uint32_t)sz; + + /* It must be a power of 2 - round down */ + i = 0; + while (sz > 1) { + sz = sz >> 1; + i++; + } + sz = (1 << i); + if (phba->nvmeio_trc_size != sz) + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0572 nvmeio_trc_size changed to %ld\n", + sz); + phba->nvmeio_trc_size = (uint32_t)sz; + + /* If one previously exists, free it */ + kfree(phba->nvmeio_trc); + + /* Allocate new trace buffer and initialize */ + phba->nvmeio_trc = kmalloc((sizeof(struct lpfc_debugfs_nvmeio_trc) * + sz), GFP_KERNEL); + if (!phba->nvmeio_trc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0573 Cannot create debugfs " + "nvmeio_trc buffer\n"); + return -ENOMEM; + } + memset(phba->nvmeio_trc, 0, + (sizeof(struct lpfc_debugfs_nvmeio_trc) * sz)); + atomic_set(&phba->nvmeio_trc_cnt, 0); + phba->nvmeio_trc_on = 0; + phba->nvmeio_trc_output_idx = 0; + + return strlen(pbuf); +} + +static int +lpfc_debugfs_cpucheck_open(struct inode *inode, struct file *file) +{ + struct lpfc_vport *vport = inode->i_private; + struct lpfc_debug *debug; + int rc = -ENOMEM; + + debug = kmalloc(sizeof(*debug), GFP_KERNEL); + if (!debug) + goto out; + + /* Round to page boundary */ + debug->buffer = kmalloc(LPFC_CPUCHECK_SIZE, GFP_KERNEL); + if (!debug->buffer) { + kfree(debug); + goto out; + } + + debug->len = lpfc_debugfs_cpucheck_data(vport, debug->buffer, + LPFC_NVMEKTIME_SIZE); + + debug->i_private = inode->i_private; + file->private_data = debug; + + rc = 0; +out: + return rc; +} + +static ssize_t +lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct lpfc_debug *debug = file->private_data; + struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private; + struct lpfc_hba *phba = vport->phba; + char mybuf[64]; + char *pbuf; + int i; + + if (nbytes > 64) + nbytes = 64; + + /* Protect copy from user */ + if (!access_ok(VERIFY_READ, buf, nbytes)) + return -EFAULT; + + memset(mybuf, 0, sizeof(mybuf)); + + if (copy_from_user(mybuf, buf, nbytes)) + return -EFAULT; + pbuf = &mybuf[0]; + + if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) { + if (phba->nvmet_support) + phba->cpucheck_on |= LPFC_CHECK_NVMET_IO; + else + phba->cpucheck_on |= LPFC_CHECK_NVME_IO; + return strlen(pbuf); + } else if ((strncmp(pbuf, "rcv", + sizeof("rcv") - 1) == 0)) { + if (phba->nvmet_support) + phba->cpucheck_on |= LPFC_CHECK_NVMET_RCV; + else + return -EINVAL; + return strlen(pbuf); + } else if ((strncmp(pbuf, "off", + sizeof("off") - 1) == 0)) { + phba->cpucheck_on = LPFC_CHECK_OFF; + return strlen(pbuf); + } else if ((strncmp(pbuf, "zero", + sizeof("zero") - 1) == 0)) { + for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { + if (i >= LPFC_CHECK_CPU_CNT) + break; + phba->cpucheck_rcv_io[i] = 0; + phba->cpucheck_xmt_io[i] = 0; + phba->cpucheck_cmpl_io[i] = 0; + phba->cpucheck_ccmpl_io[i] = 0; + } + return strlen(pbuf); + } + return -EINVAL; +} + /* * --------------------------------- * iDiag debugfs file access methods @@ -1974,6 +3027,203 @@ error_out: return -EINVAL; } +static int +__lpfc_idiag_print_wq(struct lpfc_queue *qp, char *wqtype, + char *pbuffer, int len) +{ + if (!qp) + return len; + + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\t\t%s WQ info: ", wqtype); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "AssocCQID[%04d]: WQ-STAT[oflow:x%x posted:x%llx]\n", + qp->assoc_qid, qp->q_cnt_1, + (unsigned long long)qp->q_cnt_4); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\t\tWQID[%02d], QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]", + qp->queue_id, qp->entry_count, + qp->entry_size, qp->host_index, + qp->hba_index); + len += snprintf(pbuffer + len, + LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); + return len; +} + +static int +lpfc_idiag_wqs_for_cq(struct lpfc_hba *phba, char *wqtype, char *pbuffer, + int *len, int max_cnt, int cq_id) +{ + struct lpfc_queue *qp; + int qidx; + + for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) { + qp = phba->sli4_hba.fcp_wq[qidx]; + if (qp->assoc_qid != cq_id) + continue; + *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len); + if (*len >= max_cnt) + return 1; + } + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { + qp = phba->sli4_hba.nvme_wq[qidx]; + if (qp->assoc_qid != cq_id) + continue; + *len = __lpfc_idiag_print_wq(qp, wqtype, pbuffer, *len); + if (*len >= max_cnt) + return 1; + } + return 0; +} + +static int +__lpfc_idiag_print_cq(struct lpfc_queue *qp, char *cqtype, + char *pbuffer, int len) +{ + if (!qp) + return len; + + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\t%s CQ info: ", cqtype); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "AssocEQID[%02d]: CQ STAT[max:x%x relw:x%x " + "xabt:x%x wq:x%llx]\n", + qp->assoc_qid, qp->q_cnt_1, qp->q_cnt_2, + qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\tCQID[%02d], QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]", + qp->queue_id, qp->entry_count, + qp->entry_size, qp->host_index, + qp->hba_index); + + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); + + return len; +} + +static int +__lpfc_idiag_print_rqpair(struct lpfc_queue *qp, struct lpfc_queue *datqp, + char *rqtype, char *pbuffer, int len) +{ + if (!qp || !datqp) + return len; + + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\t\t%s RQ info: ", rqtype); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "AssocCQID[%02d]: RQ-STAT[nopost:x%x nobuf:x%x " + "trunc:x%x rcv:x%llx]\n", + qp->assoc_qid, qp->q_cnt_1, qp->q_cnt_2, + qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\t\tHQID[%02d], QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]\n", + qp->queue_id, qp->entry_count, qp->entry_size, + qp->host_index, qp->hba_index); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\t\tDQID[%02d], QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]\n", + datqp->queue_id, datqp->entry_count, + datqp->entry_size, datqp->host_index, + datqp->hba_index); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); + + return len; +} + +static int +lpfc_idiag_cqs_for_eq(struct lpfc_hba *phba, char *pbuffer, + int *len, int max_cnt, int eqidx, int eq_id) +{ + struct lpfc_queue *qp; + int qidx, rc; + + for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) { + qp = phba->sli4_hba.fcp_cq[qidx]; + if (qp->assoc_qid != eq_id) + continue; + + *len = __lpfc_idiag_print_cq(qp, "FCP", pbuffer, *len); + + /* Reset max counter */ + qp->CQ_max_cqe = 0; + + if (*len >= max_cnt) + return 1; + + rc = lpfc_idiag_wqs_for_cq(phba, "FCP", pbuffer, len, + max_cnt, qp->queue_id); + if (rc) + return 1; + } + + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { + qp = phba->sli4_hba.nvme_cq[qidx]; + if (qp->assoc_qid != eq_id) + continue; + + *len = __lpfc_idiag_print_cq(qp, "NVME", pbuffer, *len); + + /* Reset max counter */ + qp->CQ_max_cqe = 0; + + if (*len >= max_cnt) + return 1; + + rc = lpfc_idiag_wqs_for_cq(phba, "NVME", pbuffer, len, + max_cnt, qp->queue_id); + if (rc) + return 1; + } + + if (eqidx < phba->cfg_nvmet_mrq) { + /* NVMET CQset */ + qp = phba->sli4_hba.nvmet_cqset[eqidx]; + *len = __lpfc_idiag_print_cq(qp, "NVMET CQset", pbuffer, *len); + + /* Reset max counter */ + qp->CQ_max_cqe = 0; + + if (*len >= max_cnt) + return 1; + + /* RQ header */ + qp = phba->sli4_hba.nvmet_mrq_hdr[eqidx]; + *len = __lpfc_idiag_print_rqpair(qp, + phba->sli4_hba.nvmet_mrq_data[eqidx], + "NVMET MRQ", pbuffer, *len); + + if (*len >= max_cnt) + return 1; + } + + return 0; +} + +static int +__lpfc_idiag_print_eq(struct lpfc_queue *qp, char *eqtype, + char *pbuffer, int len) +{ + if (!qp) + return len; + + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "\n%s EQ info: EQ-STAT[max:x%x noE:x%x " + "bs:x%x proc:x%llx]\n", + eqtype, qp->q_cnt_1, qp->q_cnt_2, qp->q_cnt_3, + (unsigned long long)qp->q_cnt_4); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "EQID[%02d], QE-CNT[%04d], QE-SIZE[%04d], " + "HOST-IDX[%04d], PORT-IDX[%04d]", + qp->queue_id, qp->entry_count, qp->entry_size, + qp->host_index, qp->hba_index); + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, "\n"); + + return len; +} + /** * lpfc_idiag_queinfo_read - idiag debugfs read queue information * @file: The file pointer to read from. @@ -1984,6 +3234,9 @@ error_out: * Description: * This routine reads data from the @phba SLI4 PCI function queue information, * and copies to user @buf. + * This routine only returns 1 EQs worth of information. It remembers the last + * EQ read and jumps to the next EQ. Thus subsequent calls to queInfo will + * retrieve all EQs allocated for the phba. * * Returns: * This function returns the amount of data that was read (this could be less @@ -1995,19 +3248,16 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, { struct lpfc_debug *debug = file->private_data; struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; - int len = 0; char *pbuffer; - int x, cnt; - int max_cnt; + int max_cnt, rc, x, len = 0; struct lpfc_queue *qp = NULL; - if (!debug->buffer) debug->buffer = kmalloc(LPFC_QUE_INFO_GET_BUF_SIZE, GFP_KERNEL); if (!debug->buffer) return 0; pbuffer = debug->buffer; - max_cnt = LPFC_QUE_INFO_GET_BUF_SIZE - 128; + max_cnt = LPFC_QUE_INFO_GET_BUF_SIZE - 256; if (*ppos) return 0; @@ -2015,375 +3265,134 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes, spin_lock_irq(&phba->hbalock); /* Fast-path event queue */ - if (phba->sli4_hba.hba_eq && phba->cfg_fcp_io_channel) { - cnt = phba->cfg_fcp_io_channel; + if (phba->sli4_hba.hba_eq && phba->io_channel_irqs) { - for (x = 0; x < cnt; x++) { + x = phba->lpfc_idiag_last_eq; + if (phba->cfg_fof && (x >= phba->io_channel_irqs)) { + phba->lpfc_idiag_last_eq = 0; + goto fof; + } + phba->lpfc_idiag_last_eq++; + if (phba->lpfc_idiag_last_eq >= phba->io_channel_irqs) + if (phba->cfg_fof == 0) + phba->lpfc_idiag_last_eq = 0; - /* Fast-path EQ */ - qp = phba->sli4_hba.hba_eq[x]; - if (!qp) - goto proc_cq; + len += snprintf(pbuffer + len, LPFC_QUE_INFO_GET_BUF_SIZE - len, + "EQ %d out of %d HBA EQs\n", + x, phba->io_channel_irqs); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\nHBA EQ info: " - "EQ-STAT[max:x%x noE:x%x " - "bs:x%x proc:x%llx]\n", - qp->q_cnt_1, qp->q_cnt_2, - qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); + /* Fast-path EQ */ + qp = phba->sli4_hba.hba_eq[x]; + if (!qp) + goto out; - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "EQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, - qp->entry_count, - qp->entry_size, - qp->host_index, - qp->hba_index); - - - /* Reset max counter */ - qp->EQ_max_eqe = 0; + len = __lpfc_idiag_print_eq(qp, "HBA", pbuffer, len); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; -proc_cq: - /* Fast-path FCP CQ */ - qp = phba->sli4_hba.fcp_cq[x]; - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tFCP CQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocEQID[%02d]: " - "CQ STAT[max:x%x relw:x%x " - "xabt:x%x wq:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, qp->q_cnt_2, - qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tCQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, qp->entry_count, - qp->entry_size, qp->host_index, - qp->hba_index); + /* Reset max counter */ + qp->EQ_max_eqe = 0; + if (len >= max_cnt) + goto too_big; - /* Reset max counter */ - qp->CQ_max_cqe = 0; + /* will dump both fcp and nvme cqs/wqs for the eq */ + rc = lpfc_idiag_cqs_for_eq(phba, pbuffer, &len, + max_cnt, x, qp->queue_id); + if (rc) + goto too_big; - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; + /* Only EQ 0 has slow path CQs configured */ + if (x) + goto out; - /* Fast-path FCP WQ */ - qp = phba->sli4_hba.fcp_wq[x]; + /* Slow-path mailbox CQ */ + qp = phba->sli4_hba.mbx_cq; + len = __lpfc_idiag_print_cq(qp, "MBX", pbuffer, len); + if (len >= max_cnt) + goto too_big; - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tFCP WQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocCQID[%02d]: " - "WQ-STAT[oflow:x%x posted:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tWQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, - qp->entry_count, - qp->entry_size, - qp->host_index, - qp->hba_index); - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; - - if (x) - continue; - - /* Only EQ 0 has slow path CQs configured */ - - /* Slow-path mailbox CQ */ - qp = phba->sli4_hba.mbx_cq; - if (qp) { - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tMBX CQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocEQID[%02d]: " - "CQ-STAT[mbox:x%x relw:x%x " - "xabt:x%x wq:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, qp->q_cnt_2, - qp->q_cnt_3, - (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tCQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, qp->entry_count, - qp->entry_size, qp->host_index, - qp->hba_index); - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; - } + /* Slow-path MBOX MQ */ + qp = phba->sli4_hba.mbx_wq; + len = __lpfc_idiag_print_wq(qp, "MBX", pbuffer, len); + if (len >= max_cnt) + goto too_big; - /* Slow-path MBOX MQ */ - qp = phba->sli4_hba.mbx_wq; - if (qp) { - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tMBX MQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocCQID[%02d]:\n", - phba->sli4_hba.mbx_wq->assoc_qid); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tWQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, qp->entry_count, - qp->entry_size, qp->host_index, - qp->hba_index); - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; - } + /* Slow-path ELS response CQ */ + qp = phba->sli4_hba.els_cq; + len = __lpfc_idiag_print_cq(qp, "ELS", pbuffer, len); + /* Reset max counter */ + if (qp) + qp->CQ_max_cqe = 0; + if (len >= max_cnt) + goto too_big; - /* Slow-path ELS response CQ */ - qp = phba->sli4_hba.els_cq; - if (qp) { - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tELS CQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocEQID[%02d]: " - "CQ-STAT[max:x%x relw:x%x " - "xabt:x%x wq:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, qp->q_cnt_2, - qp->q_cnt_3, - (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tCQID [%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, qp->entry_count, - qp->entry_size, qp->host_index, - qp->hba_index); - - /* Reset max counter */ - qp->CQ_max_cqe = 0; - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; - } + /* Slow-path ELS WQ */ + qp = phba->sli4_hba.els_wq; + len = __lpfc_idiag_print_wq(qp, "ELS", pbuffer, len); + if (len >= max_cnt) + goto too_big; - /* Slow-path ELS WQ */ - qp = phba->sli4_hba.els_wq; - if (qp) { - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tELS WQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocCQID[%02d]: " - " WQ-STAT[oflow:x%x " - "posted:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, - (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tWQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, qp->entry_count, - qp->entry_size, qp->host_index, - qp->hba_index); - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; - } + /* Slow-path NVME LS response CQ */ + qp = phba->sli4_hba.nvmels_cq; + len = __lpfc_idiag_print_cq(qp, "NVME LS", + pbuffer, len); + /* Reset max counter */ + if (qp) + qp->CQ_max_cqe = 0; + if (len >= max_cnt) + goto too_big; - if (phba->sli4_hba.hdr_rq && phba->sli4_hba.dat_rq) { - /* Slow-path RQ header */ - qp = phba->sli4_hba.hdr_rq; + /* Slow-path NVME LS WQ */ + qp = phba->sli4_hba.nvmels_wq; + len = __lpfc_idiag_print_wq(qp, "NVME LS", + pbuffer, len); + if (len >= max_cnt) + goto too_big; - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tRQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocCQID[%02d]: " - "RQ-STAT[nopost:x%x nobuf:x%x " - "trunc:x%x rcv:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, qp->q_cnt_2, - qp->q_cnt_3, - (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tHQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]\n", - qp->queue_id, - qp->entry_count, - qp->entry_size, - qp->host_index, - qp->hba_index); - - /* Slow-path RQ data */ - qp = phba->sli4_hba.dat_rq; - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tDQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]\n", - qp->queue_id, - qp->entry_count, - qp->entry_size, - qp->host_index, - qp->hba_index); - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - } - } + qp = phba->sli4_hba.hdr_rq; + len = __lpfc_idiag_print_rqpair(qp, phba->sli4_hba.dat_rq, + "RQpair", pbuffer, len); + if (len >= max_cnt) + goto too_big; + + goto out; } +fof: if (phba->cfg_fof) { /* FOF EQ */ qp = phba->sli4_hba.fof_eq; - if (!qp) - goto out; - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\nFOF EQ info: " - "EQ-STAT[max:x%x noE:x%x " - "bs:x%x proc:x%llx]\n", - qp->q_cnt_1, qp->q_cnt_2, - qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "EQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, - qp->entry_count, - qp->entry_size, - qp->host_index, - qp->hba_index); + len = __lpfc_idiag_print_eq(qp, "FOF", pbuffer, len); /* Reset max counter */ - qp->EQ_max_eqe = 0; + if (qp) + qp->EQ_max_eqe = 0; - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); if (len >= max_cnt) goto too_big; - } - - if (phba->cfg_fof) { /* OAS CQ */ qp = phba->sli4_hba.oas_cq; - if (qp) { - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tOAS CQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocEQID[%02d]: " - "CQ STAT[max:x%x relw:x%x " - "xabt:x%x wq:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, qp->q_cnt_2, - qp->q_cnt_3, (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\tCQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, qp->entry_count, - qp->entry_size, qp->host_index, - qp->hba_index); - - /* Reset max counter */ + len = __lpfc_idiag_print_cq(qp, "OAS", pbuffer, len); + /* Reset max counter */ + if (qp) qp->CQ_max_cqe = 0; - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; - } + if (len >= max_cnt) + goto too_big; /* OAS WQ */ qp = phba->sli4_hba.oas_wq; - if (qp) { - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tOAS WQ info: "); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "AssocCQID[%02d]: " - "WQ-STAT[oflow:x%x posted:x%llx]\n", - qp->assoc_qid, - qp->q_cnt_1, (unsigned long long)qp->q_cnt_4); - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, - "\t\tWQID[%02d], " - "QE-CNT[%04d], QE-SIZE[%04d], " - "HOST-IDX[%04d], PORT-IDX[%04d]", - qp->queue_id, - qp->entry_count, - qp->entry_size, - qp->host_index, - qp->hba_index); - - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n"); - if (len >= max_cnt) - goto too_big; - } + len = __lpfc_idiag_print_wq(qp, "OAS", pbuffer, len); + if (len >= max_cnt) + goto too_big; } -out: + spin_unlock_irq(&phba->hbalock); return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); too_big: - len += snprintf(pbuffer+len, - LPFC_QUE_INFO_GET_BUF_SIZE-len, "Truncated ...\n"); + len += snprintf(pbuffer + len, + LPFC_QUE_INFO_GET_BUF_SIZE - len, "Truncated ...\n"); +out: spin_unlock_irq(&phba->hbalock); return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len); } @@ -2559,7 +3568,7 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private; uint32_t qidx, quetp, queid, index, count, offset, value; uint32_t *pentry; - struct lpfc_queue *pque; + struct lpfc_queue *pque, *qp; int rc; /* This is a user write operation */ @@ -2595,19 +3604,15 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, case LPFC_IDIAG_EQ: /* HBA event queue */ if (phba->sli4_hba.hba_eq) { - for (qidx = 0; qidx < phba->cfg_fcp_io_channel; - qidx++) { - if (phba->sli4_hba.hba_eq[qidx] && - phba->sli4_hba.hba_eq[qidx]->queue_id == - queid) { + for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) { + qp = phba->sli4_hba.hba_eq[qidx]; + if (qp && qp->queue_id == queid) { /* Sanity check */ - rc = lpfc_idiag_que_param_check( - phba->sli4_hba.hba_eq[qidx], + rc = lpfc_idiag_que_param_check(qp, index, count); if (rc) goto error_out; - idiag.ptr_private = - phba->sli4_hba.hba_eq[qidx]; + idiag.ptr_private = qp; goto pass_check; } } @@ -2637,24 +3642,62 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, idiag.ptr_private = phba->sli4_hba.els_cq; goto pass_check; } + /* NVME LS complete queue */ + if (phba->sli4_hba.nvmels_cq && + phba->sli4_hba.nvmels_cq->queue_id == queid) { + /* Sanity check */ + rc = lpfc_idiag_que_param_check( + phba->sli4_hba.nvmels_cq, index, count); + if (rc) + goto error_out; + idiag.ptr_private = phba->sli4_hba.nvmels_cq; + goto pass_check; + } + /* NVME LS complete queue */ + if (phba->sli4_hba.nvmels_cq && + phba->sli4_hba.nvmels_cq->queue_id == queid) { + /* Sanity check */ + rc = lpfc_idiag_que_param_check( + phba->sli4_hba.nvmels_cq, index, count); + if (rc) + goto error_out; + idiag.ptr_private = phba->sli4_hba.nvmels_cq; + goto pass_check; + } /* FCP complete queue */ if (phba->sli4_hba.fcp_cq) { + for (qidx = 0; qidx < phba->cfg_fcp_io_channel; + qidx++) { + qp = phba->sli4_hba.fcp_cq[qidx]; + if (qp && qp->queue_id == queid) { + /* Sanity check */ + rc = lpfc_idiag_que_param_check( + qp, index, count); + if (rc) + goto error_out; + idiag.ptr_private = qp; + goto pass_check; + } + } + } + /* NVME complete queue */ + if (phba->sli4_hba.nvme_cq) { qidx = 0; do { - if (phba->sli4_hba.fcp_cq[qidx] && - phba->sli4_hba.fcp_cq[qidx]->queue_id == + if (phba->sli4_hba.nvme_cq[qidx] && + phba->sli4_hba.nvme_cq[qidx]->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check( - phba->sli4_hba.fcp_cq[qidx], + phba->sli4_hba.nvme_cq[qidx], index, count); if (rc) goto error_out; idiag.ptr_private = - phba->sli4_hba.fcp_cq[qidx]; + phba->sli4_hba.nvme_cq[qidx]; goto pass_check; } - } while (++qidx < phba->cfg_fcp_io_channel); + } while (++qidx < phba->cfg_nvme_io_channel); } goto error_out; break; @@ -2684,22 +3727,77 @@ lpfc_idiag_queacc_write(struct file *file, const char __user *buf, idiag.ptr_private = phba->sli4_hba.els_wq; goto pass_check; } + /* NVME LS work queue */ + if (phba->sli4_hba.nvmels_wq && + phba->sli4_hba.nvmels_wq->queue_id == queid) { + /* Sanity check */ + rc = lpfc_idiag_que_param_check( + phba->sli4_hba.nvmels_wq, index, count); + if (rc) + goto error_out; + idiag.ptr_private = phba->sli4_hba.nvmels_wq; + goto pass_check; + } + /* NVME LS work queue */ + if (phba->sli4_hba.nvmels_wq && + phba->sli4_hba.nvmels_wq->queue_id == queid) { + /* Sanity check */ + rc = lpfc_idiag_que_param_check( + phba->sli4_hba.nvmels_wq, index, count); + if (rc) + goto error_out; + idiag.ptr_private = phba->sli4_hba.nvmels_wq; + goto pass_check; + } /* FCP work queue */ if (phba->sli4_hba.fcp_wq) { for (qidx = 0; qidx < phba->cfg_fcp_io_channel; + qidx++) { + qp = phba->sli4_hba.fcp_wq[qidx]; + if (qp && qp->queue_id == queid) { + /* Sanity check */ + rc = lpfc_idiag_que_param_check( + qp, index, count); + if (rc) + goto error_out; + idiag.ptr_private = qp; + goto pass_check; + } + } + } + /* NVME work queue */ + if (phba->sli4_hba.nvme_wq) { + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; + qidx++) { + qp = phba->sli4_hba.nvme_wq[qidx]; + if (qp && qp->queue_id == queid) { + /* Sanity check */ + rc = lpfc_idiag_que_param_check( + qp, index, count); + if (rc) + goto error_out; + idiag.ptr_private = qp; + goto pass_check; + } + } + } + + /* NVME work queues */ + if (phba->sli4_hba.nvme_wq) { + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { - if (!phba->sli4_hba.fcp_wq[qidx]) + if (!phba->sli4_hba.nvme_wq[qidx]) continue; - if (phba->sli4_hba.fcp_wq[qidx]->queue_id == + if (phba->sli4_hba.nvme_wq[qidx]->queue_id == queid) { /* Sanity check */ rc = lpfc_idiag_que_param_check( - phba->sli4_hba.fcp_wq[qidx], + phba->sli4_hba.nvme_wq[qidx], index, count); if (rc) goto error_out; idiag.ptr_private = - phba->sli4_hba.fcp_wq[qidx]; + phba->sli4_hba.nvme_wq[qidx]; goto pass_check; } } @@ -3687,6 +4785,46 @@ static const struct file_operations lpfc_debugfs_op_dumpHostSlim = { .release = lpfc_debugfs_release, }; +#undef lpfc_debugfs_op_nvmestat +static const struct file_operations lpfc_debugfs_op_nvmestat = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_nvmestat_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .write = lpfc_debugfs_nvmestat_write, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_nvmektime +static const struct file_operations lpfc_debugfs_op_nvmektime = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_nvmektime_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .write = lpfc_debugfs_nvmektime_write, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_nvmeio_trc +static const struct file_operations lpfc_debugfs_op_nvmeio_trc = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_nvmeio_trc_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .write = lpfc_debugfs_nvmeio_trc_write, + .release = lpfc_debugfs_release, +}; + +#undef lpfc_debugfs_op_cpucheck +static const struct file_operations lpfc_debugfs_op_cpucheck = { + .owner = THIS_MODULE, + .open = lpfc_debugfs_cpucheck_open, + .llseek = lpfc_debugfs_lseek, + .read = lpfc_debugfs_read, + .write = lpfc_debugfs_cpucheck_write, + .release = lpfc_debugfs_release, +}; + #undef lpfc_debugfs_op_dumpData static const struct file_operations lpfc_debugfs_op_dumpData = { .owner = THIS_MODULE, @@ -3853,7 +4991,7 @@ lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp, if ((mbox_tp == mbox_rd) && (dma_tp == dma_mbox)) { if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_MBX) { do_dump |= LPFC_BSG_DMP_MBX_RD_MBX; - printk(KERN_ERR "\nRead mbox command (x%x), " + pr_err("\nRead mbox command (x%x), " "nemb:0x%x, extbuf_cnt:%d:\n", sta_tp, nemb_tp, ext_buf); } @@ -3861,7 +4999,7 @@ lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp, if ((mbox_tp == mbox_rd) && (dma_tp == dma_ebuf)) { if (*mbx_dump_map & LPFC_BSG_DMP_MBX_RD_BUF) { do_dump |= LPFC_BSG_DMP_MBX_RD_BUF; - printk(KERN_ERR "\nRead mbox buffer (x%x), " + pr_err("\nRead mbox buffer (x%x), " "nemb:0x%x, extbuf_seq:%d:\n", sta_tp, nemb_tp, ext_buf); } @@ -3869,7 +5007,7 @@ lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp, if ((mbox_tp == mbox_wr) && (dma_tp == dma_mbox)) { if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_MBX) { do_dump |= LPFC_BSG_DMP_MBX_WR_MBX; - printk(KERN_ERR "\nWrite mbox command (x%x), " + pr_err("\nWrite mbox command (x%x), " "nemb:0x%x, extbuf_cnt:%d:\n", sta_tp, nemb_tp, ext_buf); } @@ -3877,7 +5015,7 @@ lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp, if ((mbox_tp == mbox_wr) && (dma_tp == dma_ebuf)) { if (*mbx_dump_map & LPFC_BSG_DMP_MBX_WR_BUF) { do_dump |= LPFC_BSG_DMP_MBX_WR_BUF; - printk(KERN_ERR "\nWrite mbox buffer (x%x), " + pr_err("\nWrite mbox buffer (x%x), " "nemb:0x%x, extbuf_seq:%d:\n", sta_tp, nemb_tp, ext_buf); } @@ -3889,7 +5027,7 @@ lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp, for (i = 0; i < *mbx_word_cnt; i++) { if (!(i % 8)) { if (i != 0) - printk(KERN_ERR "%s\n", line_buf); + pr_err("%s\n", line_buf); len = 0; len += snprintf(line_buf+len, LPFC_MBX_ACC_LBUF_SZ-len, @@ -3900,7 +5038,7 @@ lpfc_idiag_mbxacc_dump_bsg_mbox(struct lpfc_hba *phba, enum nemb_type nemb_tp, pword++; } if ((i - 1) % 8) - printk(KERN_ERR "%s\n", line_buf); + pr_err("%s\n", line_buf); (*mbx_dump_cnt)--; } @@ -3949,13 +5087,13 @@ lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox) /* dump buffer content */ if (*mbx_dump_map & LPFC_MBX_DMP_MBX_WORD) { - printk(KERN_ERR "Mailbox command:0x%x dump by word:\n", + pr_err("Mailbox command:0x%x dump by word:\n", pmbox->mbxCommand); pword = (uint32_t *)pmbox; for (i = 0; i < *mbx_word_cnt; i++) { if (!(i % 8)) { if (i != 0) - printk(KERN_ERR "%s\n", line_buf); + pr_err("%s\n", line_buf); len = 0; memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ); len += snprintf(line_buf+len, @@ -3968,17 +5106,17 @@ lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox) pword++; } if ((i - 1) % 8) - printk(KERN_ERR "%s\n", line_buf); - printk(KERN_ERR "\n"); + pr_err("%s\n", line_buf); + pr_err("\n"); } if (*mbx_dump_map & LPFC_MBX_DMP_MBX_BYTE) { - printk(KERN_ERR "Mailbox command:0x%x dump by byte:\n", + pr_err("Mailbox command:0x%x dump by byte:\n", pmbox->mbxCommand); pbyte = (uint8_t *)pmbox; for (i = 0; i < *mbx_word_cnt; i++) { if (!(i % 8)) { if (i != 0) - printk(KERN_ERR "%s\n", line_buf); + pr_err("%s\n", line_buf); len = 0; memset(line_buf, 0, LPFC_MBX_ACC_LBUF_SZ); len += snprintf(line_buf+len, @@ -3996,8 +5134,8 @@ lpfc_idiag_mbxacc_dump_issue_mbox(struct lpfc_hba *phba, MAILBOX_t *pmbox) LPFC_MBX_ACC_LBUF_SZ-len, " "); } if ((i - 1) % 8) - printk(KERN_ERR "%s\n", line_buf); - printk(KERN_ERR "\n"); + pr_err("%s\n", line_buf); + pr_err("\n"); } (*mbx_dump_cnt)--; @@ -4240,8 +5378,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) i++; } lpfc_debugfs_max_slow_ring_trc = (1 << i); - printk(KERN_ERR - "lpfc_debugfs_max_disc_trc changed to " + pr_err("lpfc_debugfs_max_disc_trc changed to " "%d\n", lpfc_debugfs_max_disc_trc); } } @@ -4273,6 +5410,61 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) (sizeof(struct lpfc_debugfs_trc) * lpfc_debugfs_max_slow_ring_trc)); } + + snprintf(name, sizeof(name), "nvmeio_trc"); + phba->debug_nvmeio_trc = + debugfs_create_file(name, 0644, + phba->hba_debugfs_root, + phba, &lpfc_debugfs_op_nvmeio_trc); + if (!phba->debug_nvmeio_trc) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "0574 No create debugfs nvmeio_trc\n"); + goto debug_failed; + } + + atomic_set(&phba->nvmeio_trc_cnt, 0); + if (lpfc_debugfs_max_nvmeio_trc) { + num = lpfc_debugfs_max_nvmeio_trc - 1; + if (num & lpfc_debugfs_max_disc_trc) { + /* Change to be a power of 2 */ + num = lpfc_debugfs_max_nvmeio_trc; + i = 0; + while (num > 1) { + num = num >> 1; + i++; + } + lpfc_debugfs_max_nvmeio_trc = (1 << i); + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0575 lpfc_debugfs_max_nvmeio_trc " + "changed to %d\n", + lpfc_debugfs_max_nvmeio_trc); + } + phba->nvmeio_trc_size = lpfc_debugfs_max_nvmeio_trc; + + /* Allocate trace buffer and initialize */ + phba->nvmeio_trc = kmalloc( + (sizeof(struct lpfc_debugfs_nvmeio_trc) * + phba->nvmeio_trc_size), GFP_KERNEL); + + if (!phba->nvmeio_trc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0576 Cannot create debugfs " + "nvmeio_trc buffer\n"); + goto nvmeio_off; + } + memset(phba->nvmeio_trc, 0, + (sizeof(struct lpfc_debugfs_nvmeio_trc) * + phba->nvmeio_trc_size)); + phba->nvmeio_trc_on = 1; + phba->nvmeio_trc_output_idx = 0; + phba->nvmeio_trc = NULL; + } else { +nvmeio_off: + phba->nvmeio_trc_size = 0; + phba->nvmeio_trc_on = 0; + phba->nvmeio_trc_output_idx = 0; + phba->nvmeio_trc = NULL; + } } snprintf(name, sizeof(name), "vport%d", vport->vpi); @@ -4298,8 +5490,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) i++; } lpfc_debugfs_max_disc_trc = (1 << i); - printk(KERN_ERR - "lpfc_debugfs_max_disc_trc changed to %d\n", + pr_err("lpfc_debugfs_max_disc_trc changed to %d\n", lpfc_debugfs_max_disc_trc); } } @@ -4338,6 +5529,39 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) goto debug_failed; } + snprintf(name, sizeof(name), "nvmestat"); + vport->debug_nvmestat = + debugfs_create_file(name, 0644, + vport->vport_debugfs_root, + vport, &lpfc_debugfs_op_nvmestat); + if (!vport->debug_nvmestat) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "0811 Cannot create debugfs nvmestat\n"); + goto debug_failed; + } + + snprintf(name, sizeof(name), "nvmektime"); + vport->debug_nvmektime = + debugfs_create_file(name, 0644, + vport->vport_debugfs_root, + vport, &lpfc_debugfs_op_nvmektime); + if (!vport->debug_nvmektime) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "0815 Cannot create debugfs nvmektime\n"); + goto debug_failed; + } + + snprintf(name, sizeof(name), "cpucheck"); + vport->debug_cpucheck = + debugfs_create_file(name, 0644, + vport->vport_debugfs_root, + vport, &lpfc_debugfs_op_cpucheck); + if (!vport->debug_cpucheck) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "0819 Cannot create debugfs cpucheck\n"); + goto debug_failed; + } + /* * The following section is for additional directories/files for the * physical port. @@ -4502,140 +5726,126 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) kfree(vport->disc_trc); vport->disc_trc = NULL; } - if (vport->debug_disc_trc) { - debugfs_remove(vport->debug_disc_trc); /* discovery_trace */ - vport->debug_disc_trc = NULL; - } - if (vport->debug_nodelist) { - debugfs_remove(vport->debug_nodelist); /* nodelist */ - vport->debug_nodelist = NULL; - } + + debugfs_remove(vport->debug_disc_trc); /* discovery_trace */ + vport->debug_disc_trc = NULL; + + debugfs_remove(vport->debug_nodelist); /* nodelist */ + vport->debug_nodelist = NULL; + + debugfs_remove(vport->debug_nvmestat); /* nvmestat */ + vport->debug_nvmestat = NULL; + + debugfs_remove(vport->debug_nvmektime); /* nvmektime */ + vport->debug_nvmektime = NULL; + + debugfs_remove(vport->debug_cpucheck); /* cpucheck */ + vport->debug_cpucheck = NULL; + if (vport->vport_debugfs_root) { debugfs_remove(vport->vport_debugfs_root); /* vportX */ vport->vport_debugfs_root = NULL; atomic_dec(&phba->debugfs_vport_count); } + if (atomic_read(&phba->debugfs_vport_count) == 0) { - if (phba->debug_hbqinfo) { - debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */ - phba->debug_hbqinfo = NULL; - } - if (phba->debug_dumpHBASlim) { - debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */ - phba->debug_dumpHBASlim = NULL; - } - if (phba->debug_dumpHostSlim) { - debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */ - phba->debug_dumpHostSlim = NULL; - } - if (phba->debug_dumpData) { - debugfs_remove(phba->debug_dumpData); /* dumpData */ - phba->debug_dumpData = NULL; - } + debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */ + phba->debug_hbqinfo = NULL; - if (phba->debug_dumpDif) { - debugfs_remove(phba->debug_dumpDif); /* dumpDif */ - phba->debug_dumpDif = NULL; - } - if (phba->debug_InjErrLBA) { - debugfs_remove(phba->debug_InjErrLBA); /* InjErrLBA */ - phba->debug_InjErrLBA = NULL; - } - if (phba->debug_InjErrNPortID) { /* InjErrNPortID */ - debugfs_remove(phba->debug_InjErrNPortID); - phba->debug_InjErrNPortID = NULL; - } - if (phba->debug_InjErrWWPN) { - debugfs_remove(phba->debug_InjErrWWPN); /* InjErrWWPN */ - phba->debug_InjErrWWPN = NULL; - } - if (phba->debug_writeGuard) { - debugfs_remove(phba->debug_writeGuard); /* writeGuard */ - phba->debug_writeGuard = NULL; - } - if (phba->debug_writeApp) { - debugfs_remove(phba->debug_writeApp); /* writeApp */ - phba->debug_writeApp = NULL; - } - if (phba->debug_writeRef) { - debugfs_remove(phba->debug_writeRef); /* writeRef */ - phba->debug_writeRef = NULL; - } - if (phba->debug_readGuard) { - debugfs_remove(phba->debug_readGuard); /* readGuard */ - phba->debug_readGuard = NULL; - } - if (phba->debug_readApp) { - debugfs_remove(phba->debug_readApp); /* readApp */ - phba->debug_readApp = NULL; - } - if (phba->debug_readRef) { - debugfs_remove(phba->debug_readRef); /* readRef */ - phba->debug_readRef = NULL; - } + debugfs_remove(phba->debug_dumpHBASlim); /* HBASlim */ + phba->debug_dumpHBASlim = NULL; + + debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */ + phba->debug_dumpHostSlim = NULL; + + debugfs_remove(phba->debug_dumpData); /* dumpData */ + phba->debug_dumpData = NULL; + + debugfs_remove(phba->debug_dumpDif); /* dumpDif */ + phba->debug_dumpDif = NULL; + + debugfs_remove(phba->debug_InjErrLBA); /* InjErrLBA */ + phba->debug_InjErrLBA = NULL; + + debugfs_remove(phba->debug_InjErrNPortID); + phba->debug_InjErrNPortID = NULL; + + debugfs_remove(phba->debug_InjErrWWPN); /* InjErrWWPN */ + phba->debug_InjErrWWPN = NULL; + + debugfs_remove(phba->debug_writeGuard); /* writeGuard */ + phba->debug_writeGuard = NULL; + + debugfs_remove(phba->debug_writeApp); /* writeApp */ + phba->debug_writeApp = NULL; + + debugfs_remove(phba->debug_writeRef); /* writeRef */ + phba->debug_writeRef = NULL; + + debugfs_remove(phba->debug_readGuard); /* readGuard */ + phba->debug_readGuard = NULL; + + debugfs_remove(phba->debug_readApp); /* readApp */ + phba->debug_readApp = NULL; + + debugfs_remove(phba->debug_readRef); /* readRef */ + phba->debug_readRef = NULL; if (phba->slow_ring_trc) { kfree(phba->slow_ring_trc); phba->slow_ring_trc = NULL; } - if (phba->debug_slow_ring_trc) { - /* slow_ring_trace */ - debugfs_remove(phba->debug_slow_ring_trc); - phba->debug_slow_ring_trc = NULL; - } + + /* slow_ring_trace */ + debugfs_remove(phba->debug_slow_ring_trc); + phba->debug_slow_ring_trc = NULL; + + debugfs_remove(phba->debug_nvmeio_trc); + phba->debug_nvmeio_trc = NULL; + + kfree(phba->nvmeio_trc); + phba->nvmeio_trc = NULL; /* * iDiag release */ if (phba->sli_rev == LPFC_SLI_REV4) { - if (phba->idiag_ext_acc) { - /* iDiag extAcc */ - debugfs_remove(phba->idiag_ext_acc); - phba->idiag_ext_acc = NULL; - } - if (phba->idiag_mbx_acc) { - /* iDiag mbxAcc */ - debugfs_remove(phba->idiag_mbx_acc); - phba->idiag_mbx_acc = NULL; - } - if (phba->idiag_ctl_acc) { - /* iDiag ctlAcc */ - debugfs_remove(phba->idiag_ctl_acc); - phba->idiag_ctl_acc = NULL; - } - if (phba->idiag_drb_acc) { - /* iDiag drbAcc */ - debugfs_remove(phba->idiag_drb_acc); - phba->idiag_drb_acc = NULL; - } - if (phba->idiag_que_acc) { - /* iDiag queAcc */ - debugfs_remove(phba->idiag_que_acc); - phba->idiag_que_acc = NULL; - } - if (phba->idiag_que_info) { - /* iDiag queInfo */ - debugfs_remove(phba->idiag_que_info); - phba->idiag_que_info = NULL; - } - if (phba->idiag_bar_acc) { - /* iDiag barAcc */ - debugfs_remove(phba->idiag_bar_acc); - phba->idiag_bar_acc = NULL; - } - if (phba->idiag_pci_cfg) { - /* iDiag pciCfg */ - debugfs_remove(phba->idiag_pci_cfg); - phba->idiag_pci_cfg = NULL; - } + /* iDiag extAcc */ + debugfs_remove(phba->idiag_ext_acc); + phba->idiag_ext_acc = NULL; + + /* iDiag mbxAcc */ + debugfs_remove(phba->idiag_mbx_acc); + phba->idiag_mbx_acc = NULL; + + /* iDiag ctlAcc */ + debugfs_remove(phba->idiag_ctl_acc); + phba->idiag_ctl_acc = NULL; + + /* iDiag drbAcc */ + debugfs_remove(phba->idiag_drb_acc); + phba->idiag_drb_acc = NULL; + + /* iDiag queAcc */ + debugfs_remove(phba->idiag_que_acc); + phba->idiag_que_acc = NULL; + + /* iDiag queInfo */ + debugfs_remove(phba->idiag_que_info); + phba->idiag_que_info = NULL; + + /* iDiag barAcc */ + debugfs_remove(phba->idiag_bar_acc); + phba->idiag_bar_acc = NULL; + + /* iDiag pciCfg */ + debugfs_remove(phba->idiag_pci_cfg); + phba->idiag_pci_cfg = NULL; /* Finally remove the iDiag debugfs root */ - if (phba->idiag_root) { - /* iDiag root */ - debugfs_remove(phba->idiag_root); - phba->idiag_root = NULL; - } + debugfs_remove(phba->idiag_root); + phba->idiag_root = NULL; } if (phba->hba_debugfs_root) { @@ -4644,10 +5854,8 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) atomic_dec(&lpfc_debugfs_hba_count); } - if (atomic_read(&lpfc_debugfs_hba_count) == 0) { - debugfs_remove(lpfc_debugfs_root); /* lpfc */ - lpfc_debugfs_root = NULL; - } + debugfs_remove(lpfc_debugfs_root); /* lpfc */ + lpfc_debugfs_root = NULL; } #endif return; @@ -4668,31 +5876,39 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport) void lpfc_debug_dump_all_queues(struct lpfc_hba *phba) { - int fcp_wqidx; + int idx; /* * Dump Work Queues (WQs) */ - lpfc_debug_dump_mbx_wq(phba); - lpfc_debug_dump_els_wq(phba); + lpfc_debug_dump_wq(phba, DUMP_MBX, 0); + lpfc_debug_dump_wq(phba, DUMP_ELS, 0); + lpfc_debug_dump_wq(phba, DUMP_NVMELS, 0); - for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_io_channel; fcp_wqidx++) - lpfc_debug_dump_fcp_wq(phba, fcp_wqidx); + for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) + lpfc_debug_dump_wq(phba, DUMP_FCP, idx); + + for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++) + lpfc_debug_dump_wq(phba, DUMP_NVME, idx); lpfc_debug_dump_hdr_rq(phba); lpfc_debug_dump_dat_rq(phba); /* * Dump Complete Queues (CQs) */ - lpfc_debug_dump_mbx_cq(phba); - lpfc_debug_dump_els_cq(phba); + lpfc_debug_dump_cq(phba, DUMP_MBX, 0); + lpfc_debug_dump_cq(phba, DUMP_ELS, 0); + lpfc_debug_dump_cq(phba, DUMP_NVMELS, 0); + + for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) + lpfc_debug_dump_cq(phba, DUMP_FCP, idx); - for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_io_channel; fcp_wqidx++) - lpfc_debug_dump_fcp_cq(phba, fcp_wqidx); + for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++) + lpfc_debug_dump_cq(phba, DUMP_NVME, idx); /* * Dump Event Queues (EQs) */ - for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_io_channel; fcp_wqidx++) - lpfc_debug_dump_hba_eq(phba, fcp_wqidx); + for (idx = 0; idx < phba->io_channel_irqs; idx++) + lpfc_debug_dump_hba_eq(phba, idx); } diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index 8b2b6a3bfc25..c05f56c3023f 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2007-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -42,6 +44,22 @@ /* hbqinfo output buffer size */ #define LPFC_HBQINFO_SIZE 8192 +enum { + DUMP_FCP, + DUMP_NVME, + DUMP_MBX, + DUMP_ELS, + DUMP_NVMELS, +}; + +/* nvmestat output buffer size */ +#define LPFC_NVMESTAT_SIZE 8192 +#define LPFC_NVMEKTIME_SIZE 8192 +#define LPFC_CPUCHECK_SIZE 8192 +#define LPFC_NVMEIO_TRC_SIZE 8192 + +#define LPFC_DEBUG_OUT_LINE_SZ 80 + /* * For SLI4 iDiag debugfs diagnostics tool */ @@ -188,6 +206,12 @@ #define SIZE_U16 sizeof(uint16_t) #define SIZE_U32 sizeof(uint32_t) +#define lpfc_nvmeio_data(phba, fmt, arg...) \ + { \ + if (phba->nvmeio_trc_on) \ + lpfc_debugfs_nvme_trc(phba, fmt, ##arg); \ + } + struct lpfc_debug { char *i_private; char op; @@ -206,6 +230,13 @@ struct lpfc_debugfs_trc { unsigned long jif; }; +struct lpfc_debugfs_nvmeio_trc { + char *fmt; + uint16_t data1; + uint16_t data2; + uint32_t data3; +}; + struct lpfc_idiag_offset { uint32_t last_rd; }; @@ -358,58 +389,111 @@ lpfc_debug_dump_q(struct lpfc_queue *q) } /** - * lpfc_debug_dump_fcp_wq - dump all entries from a fcp work queue + * lpfc_debug_dump_wq - dump all entries from the fcp or nvme work queue * @phba: Pointer to HBA context object. - * @fcp_wqidx: Index to a FCP work queue. + * @wqidx: Index to a FCP or NVME work queue. * - * This function dumps all entries from a FCP work queue specified by the - * @fcp_wqidx. + * This function dumps all entries from a FCP or NVME work queue specified + * by the wqidx. **/ static inline void -lpfc_debug_dump_fcp_wq(struct lpfc_hba *phba, int fcp_wqidx) +lpfc_debug_dump_wq(struct lpfc_hba *phba, int qtype, int wqidx) { - /* sanity check */ - if (fcp_wqidx >= phba->cfg_fcp_io_channel) + struct lpfc_queue *wq; + char *qtypestr; + + if (qtype == DUMP_FCP) { + wq = phba->sli4_hba.fcp_wq[wqidx]; + qtypestr = "FCP"; + } else if (qtype == DUMP_NVME) { + wq = phba->sli4_hba.nvme_wq[wqidx]; + qtypestr = "NVME"; + } else if (qtype == DUMP_MBX) { + wq = phba->sli4_hba.mbx_wq; + qtypestr = "MBX"; + } else if (qtype == DUMP_ELS) { + wq = phba->sli4_hba.els_wq; + qtypestr = "ELS"; + } else if (qtype == DUMP_NVMELS) { + wq = phba->sli4_hba.nvmels_wq; + qtypestr = "NVMELS"; + } else return; - printk(KERN_ERR "FCP WQ: WQ[Idx:%d|Qid:%d]\n", - fcp_wqidx, phba->sli4_hba.fcp_wq[fcp_wqidx]->queue_id); - lpfc_debug_dump_q(phba->sli4_hba.fcp_wq[fcp_wqidx]); + if (qtype == DUMP_FCP || qtype == DUMP_NVME) + pr_err("%s WQ: WQ[Idx:%d|Qid:%d]\n", + qtypestr, wqidx, wq->queue_id); + else + pr_err("%s WQ: WQ[Qid:%d]\n", + qtypestr, wq->queue_id); + + lpfc_debug_dump_q(wq); } /** - * lpfc_debug_dump_fcp_cq - dump all entries from a fcp work queue's cmpl queue + * lpfc_debug_dump_cq - dump all entries from a fcp or nvme work queue's + * cmpl queue * @phba: Pointer to HBA context object. - * @fcp_wqidx: Index to a FCP work queue. + * @wqidx: Index to a FCP work queue. * - * This function dumps all entries from a FCP complete queue which is - * associated to the FCP work queue specified by the @fcp_wqidx. + * This function dumps all entries from a FCP or NVME completion queue + * which is associated to the work queue specified by the @wqidx. **/ static inline void -lpfc_debug_dump_fcp_cq(struct lpfc_hba *phba, int fcp_wqidx) +lpfc_debug_dump_cq(struct lpfc_hba *phba, int qtype, int wqidx) { - int fcp_cqidx, fcp_cqid; - - /* sanity check */ - if (fcp_wqidx >= phba->cfg_fcp_io_channel) + struct lpfc_queue *wq, *cq, *eq; + char *qtypestr; + int eqidx; + + /* fcp/nvme wq and cq are 1:1, thus same indexes */ + + if (qtype == DUMP_FCP) { + wq = phba->sli4_hba.fcp_wq[wqidx]; + cq = phba->sli4_hba.fcp_cq[wqidx]; + qtypestr = "FCP"; + } else if (qtype == DUMP_NVME) { + wq = phba->sli4_hba.nvme_wq[wqidx]; + cq = phba->sli4_hba.nvme_cq[wqidx]; + qtypestr = "NVME"; + } else if (qtype == DUMP_MBX) { + wq = phba->sli4_hba.mbx_wq; + cq = phba->sli4_hba.mbx_cq; + qtypestr = "MBX"; + } else if (qtype == DUMP_ELS) { + wq = phba->sli4_hba.els_wq; + cq = phba->sli4_hba.els_cq; + qtypestr = "ELS"; + } else if (qtype == DUMP_NVMELS) { + wq = phba->sli4_hba.nvmels_wq; + cq = phba->sli4_hba.nvmels_cq; + qtypestr = "NVMELS"; + } else return; - fcp_cqid = phba->sli4_hba.fcp_wq[fcp_wqidx]->assoc_qid; - for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_io_channel; fcp_cqidx++) - if (phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id == fcp_cqid) + for (eqidx = 0; eqidx < phba->io_channel_irqs; eqidx++) { + eq = phba->sli4_hba.hba_eq[eqidx]; + if (cq->assoc_qid == eq->queue_id) break; - if (phba->intr_type == MSIX) { - if (fcp_cqidx >= phba->cfg_fcp_io_channel) - return; - } else { - if (fcp_cqidx > 0) - return; + } + if (eqidx == phba->io_channel_irqs) { + pr_err("Couldn't find EQ for CQ. Using EQ[0]\n"); + eqidx = 0; + eq = phba->sli4_hba.hba_eq[0]; } - printk(KERN_ERR "FCP CQ: WQ[Idx:%d|Qid%d]->CQ[Idx%d|Qid%d]:\n", - fcp_wqidx, phba->sli4_hba.fcp_wq[fcp_wqidx]->queue_id, - fcp_cqidx, fcp_cqid); - lpfc_debug_dump_q(phba->sli4_hba.fcp_cq[fcp_cqidx]); + if (qtype == DUMP_FCP || qtype == DUMP_NVME) + pr_err("%s CQ: WQ[Idx:%d|Qid%d]->CQ[Idx%d|Qid%d]" + "->EQ[Idx:%d|Qid:%d]:\n", + qtypestr, wqidx, wq->queue_id, wqidx, cq->queue_id, + eqidx, eq->queue_id); + else + pr_err("%s CQ: WQ[Qid:%d]->CQ[Qid:%d]" + "->EQ[Idx:%d|Qid:%d]:\n", + qtypestr, wq->queue_id, cq->queue_id, + eqidx, eq->queue_id); + + lpfc_debug_dump_q(cq); } /** @@ -421,64 +505,15 @@ lpfc_debug_dump_fcp_cq(struct lpfc_hba *phba, int fcp_wqidx) * associated to the FCP work queue specified by the @fcp_wqidx. **/ static inline void -lpfc_debug_dump_hba_eq(struct lpfc_hba *phba, int fcp_wqidx) +lpfc_debug_dump_hba_eq(struct lpfc_hba *phba, int qidx) { - struct lpfc_queue *qdesc; - int fcp_eqidx, fcp_eqid; - int fcp_cqidx, fcp_cqid; + struct lpfc_queue *qp; - /* sanity check */ - if (fcp_wqidx >= phba->cfg_fcp_io_channel) - return; - fcp_cqid = phba->sli4_hba.fcp_wq[fcp_wqidx]->assoc_qid; - for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_io_channel; fcp_cqidx++) - if (phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id == fcp_cqid) - break; - if (phba->intr_type == MSIX) { - if (fcp_cqidx >= phba->cfg_fcp_io_channel) - return; - } else { - if (fcp_cqidx > 0) - return; - } + qp = phba->sli4_hba.hba_eq[qidx]; - fcp_eqidx = fcp_cqidx; - fcp_eqid = phba->sli4_hba.hba_eq[fcp_eqidx]->queue_id; - qdesc = phba->sli4_hba.hba_eq[fcp_eqidx]; + pr_err("EQ[Idx:%d|Qid:%d]\n", qidx, qp->queue_id); - printk(KERN_ERR "FCP EQ: WQ[Idx:%d|Qid:%d]->CQ[Idx:%d|Qid:%d]->" - "EQ[Idx:%d|Qid:%d]\n", - fcp_wqidx, phba->sli4_hba.fcp_wq[fcp_wqidx]->queue_id, - fcp_cqidx, fcp_cqid, fcp_eqidx, fcp_eqid); - lpfc_debug_dump_q(qdesc); -} - -/** - * lpfc_debug_dump_els_wq - dump all entries from the els work queue - * @phba: Pointer to HBA context object. - * - * This function dumps all entries from the ELS work queue. - **/ -static inline void -lpfc_debug_dump_els_wq(struct lpfc_hba *phba) -{ - printk(KERN_ERR "ELS WQ: WQ[Qid:%d]:\n", - phba->sli4_hba.els_wq->queue_id); - lpfc_debug_dump_q(phba->sli4_hba.els_wq); -} - -/** - * lpfc_debug_dump_mbx_wq - dump all entries from the mbox work queue - * @phba: Pointer to HBA context object. - * - * This function dumps all entries from the MBOX work queue. - **/ -static inline void -lpfc_debug_dump_mbx_wq(struct lpfc_hba *phba) -{ - printk(KERN_ERR "MBX WQ: WQ[Qid:%d]\n", - phba->sli4_hba.mbx_wq->queue_id); - lpfc_debug_dump_q(phba->sli4_hba.mbx_wq); + lpfc_debug_dump_q(qp); } /** @@ -510,36 +545,6 @@ lpfc_debug_dump_hdr_rq(struct lpfc_hba *phba) } /** - * lpfc_debug_dump_els_cq - dump all entries from the els complete queue - * @phba: Pointer to HBA context object. - * - * This function dumps all entries from the els complete queue. - **/ -static inline void -lpfc_debug_dump_els_cq(struct lpfc_hba *phba) -{ - printk(KERN_ERR "ELS CQ: WQ[Qid:%d]->CQ[Qid:%d]\n", - phba->sli4_hba.els_wq->queue_id, - phba->sli4_hba.els_cq->queue_id); - lpfc_debug_dump_q(phba->sli4_hba.els_cq); -} - -/** - * lpfc_debug_dump_mbx_cq - dump all entries from the mbox complete queue - * @phba: Pointer to HBA context object. - * - * This function dumps all entries from the mbox complete queue. - **/ -static inline void -lpfc_debug_dump_mbx_cq(struct lpfc_hba *phba) -{ - printk(KERN_ERR "MBX CQ: WQ[Qid:%d]->CQ[Qid:%d]\n", - phba->sli4_hba.mbx_wq->queue_id, - phba->sli4_hba.mbx_cq->queue_id); - lpfc_debug_dump_q(phba->sli4_hba.mbx_cq); -} - -/** * lpfc_debug_dump_wq_by_id - dump all entries from a work queue by queue id * @phba: Pointer to HBA context object. * @qid: Work queue identifier. @@ -556,14 +561,29 @@ lpfc_debug_dump_wq_by_id(struct lpfc_hba *phba, int qid) if (phba->sli4_hba.fcp_wq[wq_idx]->queue_id == qid) break; if (wq_idx < phba->cfg_fcp_io_channel) { - printk(KERN_ERR "FCP WQ[Idx:%d|Qid:%d]\n", wq_idx, qid); + pr_err("FCP WQ[Idx:%d|Qid:%d]\n", wq_idx, qid); lpfc_debug_dump_q(phba->sli4_hba.fcp_wq[wq_idx]); return; } + for (wq_idx = 0; wq_idx < phba->cfg_nvme_io_channel; wq_idx++) + if (phba->sli4_hba.nvme_wq[wq_idx]->queue_id == qid) + break; + if (wq_idx < phba->cfg_nvme_io_channel) { + pr_err("NVME WQ[Idx:%d|Qid:%d]\n", wq_idx, qid); + lpfc_debug_dump_q(phba->sli4_hba.nvme_wq[wq_idx]); + return; + } + if (phba->sli4_hba.els_wq->queue_id == qid) { - printk(KERN_ERR "ELS WQ[Qid:%d]\n", qid); + pr_err("ELS WQ[Qid:%d]\n", qid); lpfc_debug_dump_q(phba->sli4_hba.els_wq); + return; + } + + if (phba->sli4_hba.nvmels_wq->queue_id == qid) { + pr_err("NVME LS WQ[Qid:%d]\n", qid); + lpfc_debug_dump_q(phba->sli4_hba.nvmels_wq); } } @@ -617,27 +637,42 @@ lpfc_debug_dump_rq_by_id(struct lpfc_hba *phba, int qid) static inline void lpfc_debug_dump_cq_by_id(struct lpfc_hba *phba, int qid) { - int cq_idx = 0; + int cq_idx; - do { + for (cq_idx = 0; cq_idx < phba->cfg_fcp_io_channel; cq_idx++) if (phba->sli4_hba.fcp_cq[cq_idx]->queue_id == qid) break; - } while (++cq_idx < phba->cfg_fcp_io_channel); if (cq_idx < phba->cfg_fcp_io_channel) { - printk(KERN_ERR "FCP CQ[Idx:%d|Qid:%d]\n", cq_idx, qid); + pr_err("FCP CQ[Idx:%d|Qid:%d]\n", cq_idx, qid); lpfc_debug_dump_q(phba->sli4_hba.fcp_cq[cq_idx]); return; } + for (cq_idx = 0; cq_idx < phba->cfg_nvme_io_channel; cq_idx++) + if (phba->sli4_hba.nvme_cq[cq_idx]->queue_id == qid) + break; + + if (cq_idx < phba->cfg_nvme_io_channel) { + pr_err("NVME CQ[Idx:%d|Qid:%d]\n", cq_idx, qid); + lpfc_debug_dump_q(phba->sli4_hba.nvme_cq[cq_idx]); + return; + } + if (phba->sli4_hba.els_cq->queue_id == qid) { - printk(KERN_ERR "ELS CQ[Qid:%d]\n", qid); + pr_err("ELS CQ[Qid:%d]\n", qid); lpfc_debug_dump_q(phba->sli4_hba.els_cq); return; } + if (phba->sli4_hba.nvmels_cq->queue_id == qid) { + pr_err("NVME LS CQ[Qid:%d]\n", qid); + lpfc_debug_dump_q(phba->sli4_hba.nvmels_cq); + return; + } + if (phba->sli4_hba.mbx_cq->queue_id == qid) { - printk(KERN_ERR "MBX CQ[Qid:%d]\n", qid); + pr_err("MBX CQ[Qid:%d]\n", qid); lpfc_debug_dump_q(phba->sli4_hba.mbx_cq); } } @@ -655,17 +690,15 @@ lpfc_debug_dump_eq_by_id(struct lpfc_hba *phba, int qid) { int eq_idx; - for (eq_idx = 0; eq_idx < phba->cfg_fcp_io_channel; eq_idx++) { + for (eq_idx = 0; eq_idx < phba->io_channel_irqs; eq_idx++) if (phba->sli4_hba.hba_eq[eq_idx]->queue_id == qid) break; - } - if (eq_idx < phba->cfg_fcp_io_channel) { + if (eq_idx < phba->io_channel_irqs) { printk(KERN_ERR "FCP EQ[Idx:%d|Qid:%d]\n", eq_idx, qid); lpfc_debug_dump_q(phba->sli4_hba.hba_eq[eq_idx]); return; } - } void lpfc_debug_dump_all_queues(struct lpfc_hba *); diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index 361f5b3d9d93..f4ff99d95db3 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2013 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -86,6 +88,17 @@ struct lpfc_nodelist { #define NLP_FABRIC 0x4 /* entry rep a Fabric entity */ #define NLP_FCP_TARGET 0x8 /* entry is an FCP target */ #define NLP_FCP_INITIATOR 0x10 /* entry is an FCP Initiator */ +#define NLP_NVME_TARGET 0x20 /* entry is a NVME Target */ +#define NLP_NVME_INITIATOR 0x40 /* entry is a NVME Initiator */ + + uint16_t nlp_fc4_type; /* FC types node supports. */ + /* Assigned from GID_FF, only + * FCP (0x8) and NVME (0x28) + * supported. + */ +#define NLP_FC4_NONE 0x0 +#define NLP_FC4_FCP 0x1 /* FC4 Type FCP (value x8)) */ +#define NLP_FC4_NVME 0x2 /* FC4 TYPE NVME (value x28) */ uint16_t nlp_rpi; uint16_t nlp_state; /* state transition indicator */ @@ -107,8 +120,8 @@ struct lpfc_nodelist { struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */ struct lpfc_hba *phba; - struct fc_rport *rport; /* Corresponding FC transport - port structure */ + struct fc_rport *rport; /* scsi_transport_fc port structure */ + struct lpfc_nvme_rport *nrport; /* nvme transport rport struct. */ struct lpfc_vport *vport; struct lpfc_work_evt els_retry_evt; struct lpfc_work_evt dev_loss_evt; @@ -118,6 +131,10 @@ struct lpfc_nodelist { unsigned long last_change_time; unsigned long *active_rrqs_xri_bitmap; struct lpfc_scsicmd_bkt *lat_data; /* Latency data */ + uint32_t fc4_prli_sent; + uint32_t upcall_flags; + uint32_t nvme_fb_size; /* NVME target's supported byte cnt */ +#define NVME_FB_BIT_SHIFT 9 /* PRLI Rsp first burst in 512B units. */ }; struct lpfc_node_rrq { struct list_head list; @@ -133,6 +150,7 @@ struct lpfc_node_rrq { /* Defines for nlp_flag (uint32) */ #define NLP_IGNR_REG_CMPL 0x00000001 /* Rcvd rscn before we cmpl reg login */ #define NLP_REG_LOGIN_SEND 0x00000002 /* sent reglogin to adapter */ +#define NLP_SUPPRESS_RSP 0x00000010 /* Remote NPort supports suppress rsp */ #define NLP_PLOGI_SND 0x00000020 /* sent PLOGI request for this entry */ #define NLP_PRLI_SND 0x00000040 /* sent PRLI request for this entry */ #define NLP_ADISC_SND 0x00000080 /* sent ADISC request for this entry */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 3a1f1a2a2b55..2d26440e6f2f 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -29,7 +31,6 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> - #include "lpfc_hw4.h" #include "lpfc_hw.h" #include "lpfc_sli.h" @@ -1323,7 +1324,7 @@ lpfc_els_abort_flogi(struct lpfc_hba *phba) "0201 Abort outstanding I/O on NPort x%x\n", Fabric_DID); - pring = &phba->sli.ring[LPFC_ELS_RING]; + pring = lpfc_phba_elsring(phba); /* * Check the txcmplq for an iocb that matches the nport the driver is @@ -1513,7 +1514,7 @@ static struct lpfc_nodelist * lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, struct lpfc_nodelist *ndlp) { - struct lpfc_vport *vport = ndlp->vport; + struct lpfc_vport *vport = ndlp->vport; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_nodelist *new_ndlp; struct lpfc_rport_data *rdata; @@ -1868,10 +1869,12 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* PLOGI completes to NPort <nlp_DID> */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, - "0102 PLOGI completes to NPort x%x " + "0102 PLOGI completes to NPort x%06x " "Data: x%x x%x x%x x%x x%x\n", - ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4], - irsp->ulpTimeout, disc, vport->num_disc_nodes); + ndlp->nlp_DID, ndlp->nlp_fc4_type, + irsp->ulpStatus, irsp->un.ulpWord[4], + disc, vport->num_disc_nodes); + /* Check to see if link went down during discovery */ if (lpfc_els_chk_latt(vport)) { spin_lock_irq(shost->host_lock); @@ -2000,12 +2003,21 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry) sp->cmn.fcphHigh = FC_PH3; sp->cmn.valid_vendor_ver_level = 0; - memset(sp->vendorVersion, 0, sizeof(sp->vendorVersion)); + memset(sp->un.vendorVersion, 0, sizeof(sp->un.vendorVersion)); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD, "Issue PLOGI: did:x%x", did, 0, 0); + /* If our firmware supports this feature, convey that + * information to the target using the vendor specific field. + */ + if (phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) { + sp->cmn.valid_vendor_ver_level = 1; + sp->un.vv.vid = cpu_to_be32(LPFC_VV_EMLX_ID); + sp->un.vv.flags = cpu_to_be32(LPFC_VV_SUPPRESS_RSP); + } + phba->fc_stat.elsXmitPLOGI++; elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi; ret = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0); @@ -2052,14 +2064,17 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, "PRLI cmpl: status:x%x/x%x did:x%x", irsp->ulpStatus, irsp->un.ulpWord[4], ndlp->nlp_DID); + + /* Ddriver supports multiple FC4 types. Counters matter. */ + vport->fc_prli_sent--; + /* PRLI completes to NPort <nlp_DID> */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, - "0103 PRLI completes to NPort x%x " + "0103 PRLI completes to NPort x%06x " "Data: x%x x%x x%x x%x\n", ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4], - irsp->ulpTimeout, vport->num_disc_nodes); + vport->num_disc_nodes, ndlp->fc4_prli_sent); - vport->fc_prli_sent--; /* Check to see if link went down during discovery */ if (lpfc_els_chk_latt(vport)) goto out; @@ -2068,6 +2083,7 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* Check for retry */ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { /* ELS command is being retried */ + ndlp->fc4_prli_sent--; goto out; } /* PRLI failed */ @@ -2082,9 +2098,14 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); } else - /* Good status, call state machine */ + /* Good status, call state machine. However, if another + * PRLI is outstanding, don't call the state machine + * because final disposition to Mapped or Unmapped is + * completed there. + */ lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); + out: lpfc_els_free_iocb(phba, cmdiocb); return; @@ -2118,42 +2139,100 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; PRLI *npr; + struct lpfc_nvme_prli *npr_nvme; struct lpfc_iocbq *elsiocb; uint8_t *pcmd; uint16_t cmdsize; - - cmdsize = (sizeof(uint32_t) + sizeof(PRLI)); + u32 local_nlp_type, elscmd; + + local_nlp_type = ndlp->nlp_fc4_type; + + send_next_prli: + if (local_nlp_type & NLP_FC4_FCP) { + /* Payload is 4 + 16 = 20 x14 bytes. */ + cmdsize = (sizeof(uint32_t) + sizeof(PRLI)); + elscmd = ELS_CMD_PRLI; + } else if (local_nlp_type & NLP_FC4_NVME) { + /* Payload is 4 + 20 = 24 x18 bytes. */ + cmdsize = (sizeof(uint32_t) + sizeof(struct lpfc_nvme_prli)); + elscmd = ELS_CMD_NVMEPRLI; + } else { + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + "3083 Unknown FC_TYPE x%x ndlp x%06x\n", + ndlp->nlp_fc4_type, ndlp->nlp_DID); + return 1; + } elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, - ndlp->nlp_DID, ELS_CMD_PRLI); + ndlp->nlp_DID, elscmd); if (!elsiocb) return 1; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); /* For PRLI request, remainder of payload is service parameters */ - memset(pcmd, 0, (sizeof(PRLI) + sizeof(uint32_t))); - *((uint32_t *) (pcmd)) = ELS_CMD_PRLI; - pcmd += sizeof(uint32_t); + memset(pcmd, 0, cmdsize); - /* For PRLI, remainder of payload is PRLI parameter page */ - npr = (PRLI *) pcmd; - /* - * If our firmware version is 3.20 or later, - * set the following bits for FC-TAPE support. - */ - if (phba->vpd.rev.feaLevelHigh >= 0x02) { - npr->ConfmComplAllowed = 1; - npr->Retry = 1; - npr->TaskRetryIdReq = 1; - } - npr->estabImagePair = 1; - npr->readXferRdyDis = 1; - if (vport->cfg_first_burst_size) - npr->writeXferRdyDis = 1; + if (local_nlp_type & NLP_FC4_FCP) { + /* Remainder of payload is FCP PRLI parameter page. + * Note: this data structure is defined as + * BE/LE in the structure definition so no + * byte swap call is made. + */ + *((uint32_t *)(pcmd)) = ELS_CMD_PRLI; + pcmd += sizeof(uint32_t); + npr = (PRLI *)pcmd; - /* For FCP support */ - npr->prliType = PRLI_FCP_TYPE; - npr->initiatorFunc = 1; + /* + * If our firmware version is 3.20 or later, + * set the following bits for FC-TAPE support. + */ + if (phba->vpd.rev.feaLevelHigh >= 0x02) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + if (vport->cfg_first_burst_size) + npr->writeXferRdyDis = 1; + + /* For FCP support */ + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + elsiocb->iocb_flag |= LPFC_PRLI_FCP_REQ; + + /* Remove FCP type - processed. */ + local_nlp_type &= ~NLP_FC4_FCP; + } else if (local_nlp_type & NLP_FC4_NVME) { + /* Remainder of payload is NVME PRLI parameter page. + * This data structure is the newer definition that + * uses bf macros so a byte swap is required. + */ + *((uint32_t *)(pcmd)) = ELS_CMD_NVMEPRLI; + pcmd += sizeof(uint32_t); + npr_nvme = (struct lpfc_nvme_prli *)pcmd; + bf_set(prli_type_code, npr_nvme, PRLI_NVME_TYPE); + bf_set(prli_estabImagePair, npr_nvme, 0); /* Should be 0 */ + + /* Only initiators request first burst. */ + if ((phba->cfg_nvme_enable_fb) && + !phba->nvmet_support) + bf_set(prli_fba, npr_nvme, 1); + + if (phba->nvmet_support) { + bf_set(prli_tgt, npr_nvme, 1); + bf_set(prli_disc, npr_nvme, 1); + + } else { + bf_set(prli_init, npr_nvme, 1); + } + npr_nvme->word1 = cpu_to_be32(npr_nvme->word1); + npr_nvme->word4 = cpu_to_be32(npr_nvme->word4); + elsiocb->iocb_flag |= LPFC_PRLI_NVME_REQ; + + /* Remove NVME type - processed. */ + local_nlp_type &= ~NLP_FC4_NVME; + } lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD, "Issue PRLI: did:x%x", @@ -2172,7 +2251,20 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_els_free_iocb(phba, elsiocb); return 1; } + + /* The vport counters are used for lpfc_scan_finished, but + * the ndlp is used to track outstanding PRLIs for different + * FC4 types. + */ vport->fc_prli_sent++; + ndlp->fc4_prli_sent++; + + /* The driver supports 2 FC4 types. Make sure + * a PRLI is issued for all types before exiting. + */ + if (local_nlp_type & (NLP_FC4_FCP | NLP_FC4_NVME)) + goto send_next_prli; + return 0; } @@ -2543,6 +2635,15 @@ out: if ((vport->fc_flag & FC_PT2PT) && !(vport->fc_flag & FC_PT2PT_PLOGI)) { phba->pport->fc_myDID = 0; + + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if (phba->nvmet_support) + lpfc_nvmet_update_targetport(phba); + else + lpfc_nvme_update_localport(phba->pport); + } + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mbox) { lpfc_config_link(phba, mbox); @@ -3055,6 +3156,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp) } break; case ELS_CMD_PRLI: + case ELS_CMD_NVMEPRLI: if (!lpfc_issue_els_prli(vport, ndlp, retry)) { ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE); @@ -3245,7 +3347,8 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, break; } if ((cmd == ELS_CMD_PLOGI) || - (cmd == ELS_CMD_PRLI)) { + (cmd == ELS_CMD_PRLI) || + (cmd == ELS_CMD_NVMEPRLI)) { delay = 1000; maxretry = lpfc_max_els_tries + 1; retry = 1; @@ -3265,7 +3368,8 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, case LSRJT_LOGICAL_BSY: if ((cmd == ELS_CMD_PLOGI) || - (cmd == ELS_CMD_PRLI)) { + (cmd == ELS_CMD_PRLI) || + (cmd == ELS_CMD_NVMEPRLI)) { delay = 1000; maxretry = 48; } else if (cmd == ELS_CMD_FDISC) { @@ -3399,7 +3503,8 @@ out_retry: spin_unlock_irq(shost->host_lock); ndlp->nlp_prev_state = ndlp->nlp_state; - if (cmd == ELS_CMD_PRLI) + if ((cmd == ELS_CMD_PRLI) || + (cmd == ELS_CMD_NVMEPRLI)) lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE); else @@ -3430,6 +3535,7 @@ out_retry: lpfc_issue_els_adisc(vport, ndlp, cmdiocb->retry); return 1; case ELS_CMD_PRLI: + case ELS_CMD_NVMEPRLI: ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE); lpfc_issue_els_prli(vport, ndlp, cmdiocb->retry); @@ -3995,7 +4101,18 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, sizeof(struct serv_parm)); sp->cmn.valid_vendor_ver_level = 0; - memset(sp->vendorVersion, 0, sizeof(sp->vendorVersion)); + memset(sp->un.vendorVersion, 0, + sizeof(sp->un.vendorVersion)); + + /* If our firmware supports this feature, convey that + * info to the target using the vendor specific field. + */ + if (phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) { + sp->cmn.valid_vendor_ver_level = 1; + sp->un.vv.vid = cpu_to_be32(LPFC_VV_EMLX_ID); + sp->un.vv.flags = + cpu_to_be32(LPFC_VV_SUPPRESS_RSP); + } } lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, @@ -4231,17 +4348,43 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, { struct lpfc_hba *phba = vport->phba; PRLI *npr; + struct lpfc_nvme_prli *npr_nvme; lpfc_vpd_t *vpd; IOCB_t *icmd; IOCB_t *oldcmd; struct lpfc_iocbq *elsiocb; uint8_t *pcmd; uint16_t cmdsize; + uint32_t prli_fc4_req, *req_payload; + struct lpfc_dmabuf *req_buf; int rc; + u32 elsrspcmd; + + /* Need the incoming PRLI payload to determine if the ACC is for an + * FC4 or NVME PRLI type. The PRLI type is at word 1. + */ + req_buf = (struct lpfc_dmabuf *)oldiocb->context2; + req_payload = (((uint32_t *)req_buf->virt) + 1); + + /* PRLI type payload is at byte 3 for FCP or NVME. */ + prli_fc4_req = be32_to_cpu(*req_payload); + prli_fc4_req = (prli_fc4_req >> 24) & 0xff; + lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, + "6127 PRLI_ACC: Req Type x%x, Word1 x%08x\n", + prli_fc4_req, *((uint32_t *)req_payload)); + + if (prli_fc4_req == PRLI_FCP_TYPE) { + cmdsize = sizeof(uint32_t) + sizeof(PRLI); + elsrspcmd = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK)); + } else if (prli_fc4_req & PRLI_NVME_TYPE) { + cmdsize = sizeof(uint32_t) + sizeof(struct lpfc_nvme_prli); + elsrspcmd = (ELS_CMD_ACC | (ELS_CMD_NVMEPRLI & ~ELS_RSP_MASK)); + } else { + return 1; + } - cmdsize = sizeof(uint32_t) + sizeof(PRLI); elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp, - ndlp->nlp_DID, (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK))); + ndlp->nlp_DID, elsrspcmd); if (!elsiocb) return 1; @@ -4258,33 +4401,71 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + memset(pcmd, 0, cmdsize); *((uint32_t *) (pcmd)) = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK)); pcmd += sizeof(uint32_t); /* For PRLI, remainder of payload is PRLI parameter page */ - memset(pcmd, 0, sizeof(PRLI)); - - npr = (PRLI *) pcmd; vpd = &phba->vpd; - /* - * If the remote port is a target and our firmware version is 3.20 or - * later, set the following bits for FC-TAPE support. - */ - if ((ndlp->nlp_type & NLP_FCP_TARGET) && - (vpd->rev.feaLevelHigh >= 0x02)) { - npr->ConfmComplAllowed = 1; - npr->Retry = 1; - npr->TaskRetryIdReq = 1; - } - npr->acceptRspCode = PRLI_REQ_EXECUTED; - npr->estabImagePair = 1; - npr->readXferRdyDis = 1; - npr->ConfmComplAllowed = 1; + if (prli_fc4_req == PRLI_FCP_TYPE) { + /* + * If the remote port is a target and our firmware version + * is 3.20 or later, set the following bits for FC-TAPE + * support. + */ + npr = (PRLI *) pcmd; + if ((ndlp->nlp_type & NLP_FCP_TARGET) && + (vpd->rev.feaLevelHigh >= 0x02)) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + npr->acceptRspCode = PRLI_REQ_EXECUTED; + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + npr->ConfmComplAllowed = 1; + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + } else if (prli_fc4_req & PRLI_NVME_TYPE) { + /* Respond with an NVME PRLI Type */ + npr_nvme = (struct lpfc_nvme_prli *) pcmd; + bf_set(prli_type_code, npr_nvme, PRLI_NVME_TYPE); + bf_set(prli_estabImagePair, npr_nvme, 0); /* Should be 0 */ + bf_set(prli_acc_rsp_code, npr_nvme, PRLI_REQ_EXECUTED); + if (phba->nvmet_support) { + bf_set(prli_tgt, npr_nvme, 1); + bf_set(prli_disc, npr_nvme, 1); + if (phba->cfg_nvme_enable_fb) { + bf_set(prli_fba, npr_nvme, 1); + + /* TBD. Target mode needs to post buffers + * that support the configured first burst + * byte size. + */ + bf_set(prli_fb_sz, npr_nvme, + phba->cfg_nvmet_fb_size); + } + } else { + bf_set(prli_init, npr_nvme, 1); + } - npr->prliType = PRLI_FCP_TYPE; - npr->initiatorFunc = 1; + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6015 NVME issue PRLI ACC word1 x%08x " + "word4 x%08x word5 x%08x flag x%x, " + "fcp_info x%x nlp_type x%x\n", + npr_nvme->word1, npr_nvme->word4, + npr_nvme->word5, ndlp->nlp_flag, + ndlp->nlp_fcp_info, ndlp->nlp_type); + npr_nvme->word1 = cpu_to_be32(npr_nvme->word1); + npr_nvme->word4 = cpu_to_be32(npr_nvme->word4); + npr_nvme->word5 = cpu_to_be32(npr_nvme->word5); + } else + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + "6128 Unknown FC_TYPE x%x x%x ndlp x%06x\n", + prli_fc4_req, ndlp->nlp_fc4_type, + ndlp->nlp_DID); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP, "Issue ACC PRLI: did:x%x flg:x%x", @@ -4411,7 +4592,7 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format, **/ static void lpfc_els_clear_rrq(struct lpfc_vport *vport, - struct lpfc_iocbq *iocb, struct lpfc_nodelist *ndlp) + struct lpfc_iocbq *iocb, struct lpfc_nodelist *ndlp) { struct lpfc_hba *phba = vport->phba; uint8_t *pcmd; @@ -4909,7 +5090,7 @@ lpfc_rdp_res_opd_desc(struct fc_rdp_opd_sfp_desc *desc, memcpy(desc->opd_info.vendor_name, &page_a0[SSF_VENDOR_NAME], 16); memcpy(desc->opd_info.model_number, &page_a0[SSF_VENDOR_PN], 16); memcpy(desc->opd_info.serial_number, &page_a0[SSF_VENDOR_SN], 16); - memcpy(desc->opd_info.revision, &page_a0[SSF_VENDOR_REV], 2); + memcpy(desc->opd_info.revision, &page_a0[SSF_VENDOR_REV], 4); memcpy(desc->opd_info.date, &page_a0[SSF_DATE_CODE], 8); desc->length = cpu_to_be32(sizeof(desc->opd_info)); return sizeof(struct fc_rdp_opd_sfp_desc); @@ -5004,7 +5185,7 @@ lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc, memcpy(desc->port_names.wwnn, phba->wwnn, sizeof(desc->port_names.wwnn)); - memcpy(desc->port_names.wwpn, &phba->wwpn, + memcpy(desc->port_names.wwpn, phba->wwpn, sizeof(desc->port_names.wwpn)); desc->length = cpu_to_be32(sizeof(desc->port_names)); @@ -5233,9 +5414,8 @@ lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, struct ls_rjt stat; if (phba->sli_rev < LPFC_SLI_REV4 || - (bf_get(lpfc_sli_intf_if_type, - &phba->sli4_hba.sli_intf) != - LPFC_SLI_INTF_IF_TYPE_2)) { + bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2) { rjt_err = LSRJT_UNABLE_TPC; rjt_expl = LSEXP_REQ_UNSUPPORTED; goto error; @@ -5687,6 +5867,8 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport) (ndlp->nlp_state == NLP_STE_UNUSED_NODE) || !lpfc_rscn_payload_check(vport, ndlp->nlp_DID)) continue; + if (vport->phba->nvmet_support) + continue; lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RECOVERY); lpfc_cancel_retry_delay_tmo(vport, ndlp); @@ -5976,9 +6158,11 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport) if (ndlp && NLP_CHK_NODE_ACT(ndlp) && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { /* Good ndlp, issue CT Request to NameServer */ - if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, 0) == 0) + vport->gidft_inp = 0; + if (lpfc_issue_gidft(vport) == 0) /* Wait for NameServer query cmpl before we can - continue */ + * continue + */ return 1; } else { /* If login to NameServer does not exist, issue one */ @@ -6082,7 +6266,6 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, (void) lpfc_check_sparm(vport, ndlp, sp, CLASS3, 1); - /* * If our portname is greater than the remote portname, * then we initiate Nport login. @@ -7155,7 +7338,8 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport) timeout = (uint32_t)(phba->fc_ratov << 1); - pring = &phba->sli.ring[LPFC_ELS_RING]; + pring = lpfc_phba_elsring(phba); + if ((phba->pport->load_flag & FC_UNLOADING)) return; spin_lock_irq(&phba->hbalock); @@ -7224,7 +7408,7 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport) spin_unlock_irq(&phba->hbalock); } - if (!list_empty(&phba->sli.ring[LPFC_ELS_RING].txcmplq)) + if (!list_empty(&pring->txcmplq)) if (!(phba->pport->load_flag & FC_UNLOADING)) mod_timer(&vport->els_tmofunc, jiffies + msecs_to_jiffies(1000 * timeout)); @@ -7255,7 +7439,7 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) { LIST_HEAD(abort_list); struct lpfc_hba *phba = vport->phba; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; struct lpfc_iocbq *tmp_iocb, *piocb; IOCB_t *cmd = NULL; @@ -7267,6 +7451,7 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) * a working list and release the locks before calling the abort. */ spin_lock_irq(&phba->hbalock); + pring = lpfc_phba_elsring(phba); if (phba->sli_rev == LPFC_SLI_REV4) spin_lock(&pring->ring_lock); @@ -7777,6 +7962,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, lpfc_els_rcv_fan(vport, elsiocb, ndlp); break; case ELS_CMD_PRLI: + case ELS_CMD_NVMEPRLI: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, "RCV PRLI: did:x%x/ste:x%x flg:x%x", did, vport->port_state, ndlp->nlp_flag); @@ -8881,8 +9067,7 @@ lpfc_cmpl_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, break; } - if (atomic_read(&phba->fabric_iocb_count) == 0) - BUG(); + BUG_ON(atomic_read(&phba->fabric_iocb_count) == 0); cmdiocb->iocb_cmpl = cmdiocb->fabric_iocb_cmpl; cmdiocb->fabric_iocb_cmpl = NULL; @@ -8927,8 +9112,7 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb) int ready; int ret; - if (atomic_read(&phba->fabric_iocb_count) > 1) - BUG(); + BUG_ON(atomic_read(&phba->fabric_iocb_count) > 1); spin_lock_irqsave(&phba->hbalock, iflags); ready = atomic_read(&phba->fabric_iocb_count) == 0 && @@ -9013,7 +9197,9 @@ void lpfc_fabric_abort_nport(struct lpfc_nodelist *ndlp) LIST_HEAD(completions); struct lpfc_hba *phba = ndlp->phba; struct lpfc_iocbq *tmp_iocb, *piocb; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; + + pring = lpfc_phba_elsring(phba); spin_lock_irq(&phba->hbalock); list_for_each_entry_safe(piocb, tmp_iocb, &phba->fabric_iocb_list, @@ -9069,13 +9255,13 @@ lpfc_sli4_vport_delete_els_xri_aborted(struct lpfc_vport *vport) unsigned long iflag = 0; spin_lock_irqsave(&phba->hbalock, iflag); - spin_lock(&phba->sli4_hba.abts_sgl_list_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); list_for_each_entry_safe(sglq_entry, sglq_next, &phba->sli4_hba.lpfc_abts_els_sgl_list, list) { if (sglq_entry->ndlp && sglq_entry->ndlp->vport == vport) sglq_entry->ndlp = NULL; } - spin_unlock(&phba->sli4_hba.abts_sgl_list_lock); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); return; } @@ -9099,22 +9285,22 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba, struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL; unsigned long iflag = 0; struct lpfc_nodelist *ndlp; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; + + pring = lpfc_phba_elsring(phba); spin_lock_irqsave(&phba->hbalock, iflag); - spin_lock(&phba->sli4_hba.abts_sgl_list_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); list_for_each_entry_safe(sglq_entry, sglq_next, &phba->sli4_hba.lpfc_abts_els_sgl_list, list) { if (sglq_entry->sli4_xritag == xri) { list_del(&sglq_entry->list); ndlp = sglq_entry->ndlp; sglq_entry->ndlp = NULL; - spin_lock(&pring->ring_lock); list_add_tail(&sglq_entry->list, - &phba->sli4_hba.lpfc_sgl_list); + &phba->sli4_hba.lpfc_els_sgl_list); sglq_entry->state = SGL_FREED; - spin_unlock(&pring->ring_lock); - spin_unlock(&phba->sli4_hba.abts_sgl_list_lock); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); lpfc_set_rrq_active(phba, ndlp, sglq_entry->sli4_lxritag, @@ -9126,21 +9312,21 @@ lpfc_sli4_els_xri_aborted(struct lpfc_hba *phba, return; } } - spin_unlock(&phba->sli4_hba.abts_sgl_list_lock); + spin_unlock(&phba->sli4_hba.sgl_list_lock); lxri = lpfc_sli4_xri_inrange(phba, xri); if (lxri == NO_XRI) { spin_unlock_irqrestore(&phba->hbalock, iflag); return; } - spin_lock(&pring->ring_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); sglq_entry = __lpfc_get_active_sglq(phba, lxri); if (!sglq_entry || (sglq_entry->sli4_xritag != xri)) { - spin_unlock(&pring->ring_lock); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); return; } sglq_entry->state = SGL_XRI_ABORTED; - spin_unlock(&pring->ring_lock); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); return; } diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 82047070cdc9..194a14d5f8a9 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -31,6 +33,9 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> + +#include <linux/nvme-fc-driver.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -38,8 +43,9 @@ #include "lpfc_disc.h" #include "lpfc_sli.h" #include "lpfc_sli4.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" #include "lpfc_logmsg.h" #include "lpfc_crtn.h" #include "lpfc_vport.h" @@ -93,7 +99,7 @@ lpfc_terminate_rport_io(struct fc_rport *rport) if (ndlp->nlp_sid != NLP_NO_SID) { lpfc_sli_abort_iocb(ndlp->vport, - &phba->sli.ring[phba->sli.fcp_ring], + &phba->sli.sli3_ring[LPFC_FCP_RING], ndlp->nlp_sid, 0, LPFC_CTX_TGT); } } @@ -247,8 +253,8 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) if (ndlp->nlp_sid != NLP_NO_SID) { /* flush the target */ lpfc_sli_abort_iocb(vport, - &phba->sli.ring[phba->sli.fcp_ring], - ndlp->nlp_sid, 0, LPFC_CTX_TGT); + &phba->sli.sli3_ring[LPFC_FCP_RING], + ndlp->nlp_sid, 0, LPFC_CTX_TGT); } put_node = rdata->pnode != NULL; rdata->pnode = NULL; @@ -283,7 +289,7 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) if (ndlp->nlp_sid != NLP_NO_SID) { warn_on = 1; - lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], + lpfc_sli_abort_iocb(vport, &phba->sli.sli3_ring[LPFC_FCP_RING], ndlp->nlp_sid, 0, LPFC_CTX_TGT); } @@ -495,11 +501,12 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba, return; } - fc_host_post_vendor_event(shost, - fc_get_event_number(), - evt_data_size, - evt_data, - LPFC_NL_VENDOR_ID); + if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME) + fc_host_post_vendor_event(shost, + fc_get_event_number(), + evt_data_size, + evt_data, + LPFC_NL_VENDOR_ID); lpfc_free_fast_evt(phba, fast_evt_data); return; @@ -682,7 +689,7 @@ lpfc_work_done(struct lpfc_hba *phba) } lpfc_destroy_vport_work_array(phba, vports); - pring = &phba->sli.ring[LPFC_ELS_RING]; + pring = lpfc_phba_elsring(phba); status = (ha_copy & (HA_RXMASK << (4*LPFC_ELS_RING))); status >>= (4*LPFC_ELS_RING); if ((status & HA_RXMASK) || @@ -852,9 +859,12 @@ lpfc_port_link_failure(struct lpfc_vport *vport) void lpfc_linkdown_port(struct lpfc_vport *vport) { + struct lpfc_hba *phba = vport->phba; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); - fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_LINKDOWN, 0); + if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME) + fc_host_post_event(shost, fc_get_event_number(), + FCH_EVT_LINKDOWN, 0); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD, "Link Down: state:x%x rtry:x%x flg:x%x", @@ -894,11 +904,22 @@ lpfc_linkdown(struct lpfc_hba *phba) spin_unlock_irq(shost->host_lock); } vports = lpfc_create_vport_work_array(phba); - if (vports != NULL) + if (vports != NULL) { for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { /* Issue a LINK DOWN event to all nodes */ lpfc_linkdown_port(vports[i]); + + vports[i]->fc_myDID = 0; + + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if (phba->nvmet_support) + lpfc_nvmet_update_targetport(phba); + else + lpfc_nvme_update_localport(vports[i]); + } } + } lpfc_destroy_vport_work_array(phba, vports); /* Clean up any firmware default rpi's */ mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -914,7 +935,6 @@ lpfc_linkdown(struct lpfc_hba *phba) /* Setup myDID for link up if we are in pt2pt mode */ if (phba->pport->fc_flag & FC_PT2PT) { - phba->pport->fc_myDID = 0; mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (mb) { lpfc_config_link(phba, mb); @@ -929,7 +949,6 @@ lpfc_linkdown(struct lpfc_hba *phba) phba->pport->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI); spin_unlock_irq(shost->host_lock); } - return 0; } @@ -977,7 +996,9 @@ lpfc_linkup_port(struct lpfc_vport *vport) (vport != phba->pport)) return; - fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_LINKUP, 0); + if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME) + fc_host_post_event(shost, fc_get_event_number(), + FCH_EVT_LINKUP, 0); spin_lock_irq(shost->host_lock); vport->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY | @@ -1016,7 +1037,7 @@ lpfc_linkup(struct lpfc_hba *phba) * This routine handles processing a CLEAR_LA mailbox * command upon completion. It is setup in the LPFC_MBOXQ * as the completion routine when the command is - * handed off to the SLI layer. + * handed off to the SLI layer. SLI3 only. */ static void lpfc_mbx_cmpl_clear_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) @@ -1028,9 +1049,8 @@ lpfc_mbx_cmpl_clear_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) uint32_t control; /* Since we don't do discovery right now, turn these off here */ - psli->ring[psli->extra_ring].flag &= ~LPFC_STOP_IOCB_EVENT; - psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT; - psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->sli3_ring[LPFC_EXTRA_RING].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->sli3_ring[LPFC_FCP_RING].flag &= ~LPFC_STOP_IOCB_EVENT; /* Check for error */ if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) { @@ -3277,7 +3297,7 @@ lpfc_mbx_issue_link_down(struct lpfc_hba *phba) * This routine handles processing a READ_TOPOLOGY mailbox * command upon completion. It is setup in the LPFC_MBOXQ * as the completion routine when the command is - * handed off to the SLI layer. + * handed off to the SLI layer. SLI4 only. */ void lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) @@ -3285,11 +3305,14 @@ lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) struct lpfc_vport *vport = pmb->vport; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_mbx_read_top *la; + struct lpfc_sli_ring *pring; MAILBOX_t *mb = &pmb->u.mb; struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1); /* Unblock ELS traffic */ - phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT; + pring = lpfc_phba_elsring(phba); + pring->flag &= ~LPFC_STOP_IOCB_EVENT; + /* Check for error */ if (mb->mbxStatus) { lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, @@ -3458,6 +3481,14 @@ lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) spin_lock_irq(shost->host_lock); ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL; spin_unlock_irq(shost->host_lock); + + /* + * We cannot leave the RPI registered because + * if we go thru discovery again for this ndlp + * a subsequent REG_RPI will fail. + */ + ndlp->nlp_flag |= NLP_RPI_REGISTERED; + lpfc_unreg_rpi(vport, ndlp); } /* Call state machine */ @@ -3556,6 +3587,14 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); spin_unlock_irq(shost->host_lock); vport->fc_myDID = 0; + + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if (phba->nvmet_support) + lpfc_nvmet_update_targetport(phba); + else + lpfc_nvme_update_localport(vport); + } goto out; } @@ -3805,6 +3844,52 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) return; } + /* + * This routine will issue a GID_FT for each FC4 Type supported + * by the driver. ALL GID_FTs must complete before discovery is started. + */ +int +lpfc_issue_gidft(struct lpfc_vport *vport) +{ + struct lpfc_hba *phba = vport->phba; + + /* Good status, issue CT Request to NameServer */ + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) { + if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_FCP)) { + /* Cannot issue NameServer FCP Query, so finish up + * discovery + */ + lpfc_printf_vlog(vport, KERN_ERR, LOG_SLI, + "0604 %s FC TYPE %x %s\n", + "Failed to issue GID_FT to ", + FC_TYPE_FCP, + "Finishing discovery."); + return 0; + } + vport->gidft_inp++; + } + + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, SLI_CTPT_NVME)) { + /* Cannot issue NameServer NVME Query, so finish up + * discovery + */ + lpfc_printf_vlog(vport, KERN_ERR, LOG_SLI, + "0605 %s FC_TYPE %x %s %d\n", + "Failed to issue GID_FT to ", + FC_TYPE_NVME, + "Finishing discovery: gidftinp ", + vport->gidft_inp); + if (vport->gidft_inp == 0) + return 0; + } else + vport->gidft_inp++; + } + return vport->gidft_inp; +} + /* * This routine handles processing a NameServer REG_LOGIN mailbox * command upon completion. It is setup in the LPFC_MBOXQ @@ -3821,12 +3906,14 @@ lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) pmb->context1 = NULL; pmb->context2 = NULL; + vport->gidft_inp = 0; if (mb->mbxStatus) { -out: lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, "0260 Register NameServer error: 0x%x\n", mb->mbxStatus); + +out: /* decrement the node reference count held for this * callback function. */ @@ -3870,20 +3957,29 @@ out: lpfc_ns_cmd(vport, SLI_CTNS_RSNN_NN, 0, 0); lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0); lpfc_ns_cmd(vport, SLI_CTNS_RFT_ID, 0, 0); - lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0); + + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP)) + lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, FC_TYPE_FCP); + + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) + lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, + FC_TYPE_NVME); /* Issue SCR just before NameServer GID_FT Query */ lpfc_issue_els_scr(vport, SCR_DID, 0); } vport->fc_ns_retry = 0; - /* Good status, issue CT Request to NameServer */ - if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, 0)) { - /* Cannot issue NameServer Query, so finish up discovery */ + if (lpfc_issue_gidft(vport) == 0) goto out; - } - /* decrement the node reference count held for this + /* + * At this point in time we may need to wait for multiple + * SLI_CTNS_GID_FT CT commands to complete before we start discovery. + * + * decrement the node reference count held for this * callback function. */ lpfc_nlp_put(ndlp); @@ -3903,6 +3999,9 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) struct fc_rport_identifiers rport_ids; struct lpfc_hba *phba = vport->phba; + if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME) + return; + /* Remote port has reappeared. Re-register w/ FC transport */ rport_ids.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn); rport_ids.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn); @@ -3972,12 +4071,17 @@ static void lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp) { struct fc_rport *rport = ndlp->rport; + struct lpfc_vport *vport = ndlp->vport; + struct lpfc_hba *phba = vport->phba; - lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_RPORT, + if (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME) + return; + + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, "rport delete: did:x%x flg:x%x type x%x", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type); - lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, + lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE, "3184 rport unregister x%06x, rport %p\n", ndlp->nlp_DID, rport); @@ -4029,6 +4133,7 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int old_state, int new_state) { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_hba *phba = vport->phba; if (new_state == NLP_STE_UNMAPPED_NODE) { ndlp->nlp_flag &= ~NLP_NODEV_REMOVE; @@ -4039,23 +4144,56 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (new_state == NLP_STE_NPR_NODE) ndlp->nlp_flag &= ~NLP_RCV_PLOGI; - /* Transport interface */ - if (ndlp->rport && (old_state == NLP_STE_MAPPED_NODE || - old_state == NLP_STE_UNMAPPED_NODE)) { - vport->phba->nport_event_cnt++; - lpfc_unregister_remote_port(ndlp); + /* FCP and NVME Transport interface */ + if ((old_state == NLP_STE_MAPPED_NODE || + old_state == NLP_STE_UNMAPPED_NODE)) { + if (ndlp->rport) { + vport->phba->nport_event_cnt++; + lpfc_unregister_remote_port(ndlp); + } + + /* Notify the NVME transport of this rport's loss */ + if (((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) && + (vport->phba->nvmet_support == 0) && + ((ndlp->nlp_fc4_type & NLP_FC4_NVME) || + (ndlp->nlp_DID == Fabric_DID))) { + vport->phba->nport_event_cnt++; + lpfc_nvme_unregister_port(vport, ndlp); + } } + /* FCP and NVME Transport interfaces */ + if (new_state == NLP_STE_MAPPED_NODE || new_state == NLP_STE_UNMAPPED_NODE) { - vport->phba->nport_event_cnt++; - /* - * Tell the fc transport about the port, if we haven't - * already. If we have, and it's a scsi entity, be - * sure to unblock any attached scsi devices - */ - lpfc_register_remote_port(vport, ndlp); + if ((ndlp->nlp_fc4_type & NLP_FC4_FCP) || + (ndlp->nlp_DID == Fabric_DID)) { + vport->phba->nport_event_cnt++; + /* + * Tell the fc transport about the port, if we haven't + * already. If we have, and it's a scsi entity, be + */ + lpfc_register_remote_port(vport, ndlp); + } + /* Notify the NVME transport of this new rport. */ + if (ndlp->nlp_fc4_type & NLP_FC4_NVME) { + if (vport->phba->nvmet_support == 0) { + /* Register this rport with the transport. + * Initiators take the NDLP ref count in + * the register. + */ + vport->phba->nport_event_cnt++; + lpfc_nvme_register_port(vport, ndlp); + } else { + /* Just take an NDLP ref count since the + * target does not register rports. + */ + lpfc_nlp_get(ndlp); + } + } } + if ((new_state == NLP_STE_MAPPED_NODE) && (vport->stat_data_enabled)) { /* @@ -4073,12 +4211,13 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, "0x%x\n", ndlp->nlp_DID); } /* - * if we added to Mapped list, but the remote port - * registration failed or assigned a target id outside - * our presentable range - move the node to the - * Unmapped List + * If the node just added to Mapped list was an FCP target, + * but the remote port registration failed or assigned a target + * id outside the presentable range - move the node to the + * Unmapped List. */ - if (new_state == NLP_STE_MAPPED_NODE && + if ((new_state == NLP_STE_MAPPED_NODE) && + (ndlp->nlp_type & NLP_FCP_TARGET) && (!ndlp->rport || ndlp->rport->scsi_target_id == -1 || ndlp->rport->scsi_target_id >= LPFC_MAX_TARGET)) { @@ -4212,6 +4351,7 @@ lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->vport = vport; ndlp->phba = vport->phba; ndlp->nlp_sid = NLP_NO_SID; + ndlp->nlp_fc4_type = NLP_FC4_NONE; kref_init(&ndlp->kref); NLP_INT_NODE_ACT(ndlp); atomic_set(&ndlp->cmd_pending, 0); @@ -4394,7 +4534,6 @@ lpfc_check_sli_ndlp(struct lpfc_hba *phba, struct lpfc_iocbq *iocb, struct lpfc_nodelist *ndlp) { - struct lpfc_sli *psli = &phba->sli; IOCB_t *icmd = &iocb->iocb; struct lpfc_vport *vport = ndlp->vport; @@ -4413,9 +4552,7 @@ lpfc_check_sli_ndlp(struct lpfc_hba *phba, if (iocb->context1 == (uint8_t *) ndlp) return 1; } - } else if (pring->ringno == psli->extra_ring) { - - } else if (pring->ringno == psli->fcp_ring) { + } else if (pring->ringno == LPFC_FCP_RING) { /* Skip match check if waiting to relogin to FCP target */ if ((ndlp->nlp_type & NLP_FCP_TARGET) && (ndlp->nlp_flag & NLP_DELAY_TMO)) { @@ -4424,12 +4561,58 @@ lpfc_check_sli_ndlp(struct lpfc_hba *phba, if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) { return 1; } - } else if (pring->ringno == psli->next_ring) { - } return 0; } +static void +__lpfc_dequeue_nport_iocbs(struct lpfc_hba *phba, + struct lpfc_nodelist *ndlp, struct lpfc_sli_ring *pring, + struct list_head *dequeue_list) +{ + struct lpfc_iocbq *iocb, *next_iocb; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + /* Check to see if iocb matches the nport */ + if (lpfc_check_sli_ndlp(phba, pring, iocb, ndlp)) + /* match, dequeue */ + list_move_tail(&iocb->list, dequeue_list); + } +} + +static void +lpfc_sli3_dequeue_nport_iocbs(struct lpfc_hba *phba, + struct lpfc_nodelist *ndlp, struct list_head *dequeue_list) +{ + struct lpfc_sli *psli = &phba->sli; + uint32_t i; + + spin_lock_irq(&phba->hbalock); + for (i = 0; i < psli->num_rings; i++) + __lpfc_dequeue_nport_iocbs(phba, ndlp, &psli->sli3_ring[i], + dequeue_list); + spin_unlock_irq(&phba->hbalock); +} + +static void +lpfc_sli4_dequeue_nport_iocbs(struct lpfc_hba *phba, + struct lpfc_nodelist *ndlp, struct list_head *dequeue_list) +{ + struct lpfc_sli_ring *pring; + struct lpfc_queue *qp = NULL; + + spin_lock_irq(&phba->hbalock); + list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) { + pring = qp->pring; + if (!pring) + continue; + spin_lock_irq(&pring->ring_lock); + __lpfc_dequeue_nport_iocbs(phba, ndlp, pring, dequeue_list); + spin_unlock_irq(&pring->ring_lock); + } + spin_unlock_irq(&phba->hbalock); +} + /* * Free resources / clean up outstanding I/Os * associated with nlp_rpi in the LPFC_NODELIST entry. @@ -4438,10 +4621,6 @@ static int lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { LIST_HEAD(completions); - struct lpfc_sli *psli; - struct lpfc_sli_ring *pring; - struct lpfc_iocbq *iocb, *next_iocb; - uint32_t i; lpfc_fabric_abort_nport(ndlp); @@ -4449,29 +4628,11 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) * Everything that matches on txcmplq will be returned * by firmware with a no rpi error. */ - psli = &phba->sli; if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { - /* Now process each ring */ - for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; - - spin_lock_irq(&phba->hbalock); - list_for_each_entry_safe(iocb, next_iocb, &pring->txq, - list) { - /* - * Check to see if iocb matches the nport we are - * looking for - */ - if ((lpfc_check_sli_ndlp(phba, pring, iocb, - ndlp))) { - /* It matches, so deque and call compl - with an error */ - list_move_tail(&iocb->list, - &completions); - } - } - spin_unlock_irq(&phba->hbalock); - } + if (phba->sli_rev != LPFC_SLI_REV4) + lpfc_sli3_dequeue_nport_iocbs(phba, ndlp, &completions); + else + lpfc_sli4_dequeue_nport_iocbs(phba, ndlp, &completions); } /* Cancel all the IOCBs from the completions list */ @@ -4950,6 +5111,8 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) return NULL; lpfc_nlp_init(vport, ndlp, did); lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + if (vport->phba->nvmet_support) + return ndlp; spin_lock_irq(shost->host_lock); ndlp->nlp_flag |= NLP_NPR_2B_DISC; spin_unlock_irq(shost->host_lock); @@ -4958,6 +5121,8 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_NPR_NODE); if (!ndlp) return NULL; + if (vport->phba->nvmet_support) + return ndlp; spin_lock_irq(shost->host_lock); ndlp->nlp_flag |= NLP_NPR_2B_DISC; spin_unlock_irq(shost->host_lock); @@ -4977,6 +5142,8 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) * delay timeout is not needed. */ lpfc_cancel_retry_delay_tmo(vport, ndlp); + if (vport->phba->nvmet_support) + return ndlp; spin_lock_irq(shost->host_lock); ndlp->nlp_flag |= NLP_NPR_2B_DISC; spin_unlock_irq(shost->host_lock); @@ -4992,6 +5159,8 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) ndlp->nlp_flag & NLP_RCV_PLOGI) return NULL; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + if (vport->phba->nvmet_support) + return ndlp; spin_lock_irq(shost->host_lock); ndlp->nlp_flag |= NLP_NPR_2B_DISC; spin_unlock_irq(shost->host_lock); @@ -5040,14 +5209,14 @@ lpfc_disc_list_loopmap(struct lpfc_vport *vport) return; } +/* SLI3 only */ void lpfc_issue_clear_la(struct lpfc_hba *phba, struct lpfc_vport *vport) { LPFC_MBOXQ_t *mbox; struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *extra_ring = &psli->ring[psli->extra_ring]; - struct lpfc_sli_ring *fcp_ring = &psli->ring[psli->fcp_ring]; - struct lpfc_sli_ring *next_ring = &psli->ring[psli->next_ring]; + struct lpfc_sli_ring *extra_ring = &psli->sli3_ring[LPFC_EXTRA_RING]; + struct lpfc_sli_ring *fcp_ring = &psli->sli3_ring[LPFC_FCP_RING]; int rc; /* @@ -5071,7 +5240,6 @@ lpfc_issue_clear_la(struct lpfc_hba *phba, struct lpfc_vport *vport) lpfc_disc_flush_list(vport); extra_ring->flag &= ~LPFC_STOP_IOCB_EVENT; fcp_ring->flag &= ~LPFC_STOP_IOCB_EVENT; - next_ring->flag &= ~LPFC_STOP_IOCB_EVENT; phba->link_state = LPFC_HBA_ERROR; } } @@ -5207,7 +5375,7 @@ lpfc_free_tx(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) struct lpfc_sli_ring *pring; psli = &phba->sli; - pring = &psli->ring[LPFC_ELS_RING]; + pring = lpfc_phba_elsring(phba); /* Error matching iocb on txq or txcmplq * First check the txq. @@ -5331,12 +5499,13 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport) switch (vport->port_state) { case LPFC_LOCAL_CFG_LINK: - /* port_state is identically LPFC_LOCAL_CFG_LINK while waiting for - * FAN - */ - /* FAN timeout */ + /* + * port_state is identically LPFC_LOCAL_CFG_LINK while + * waiting for FAN timeout + */ lpfc_printf_vlog(vport, KERN_WARNING, LOG_DISCOVERY, "0221 FAN timeout\n"); + /* Start discovery by sending FLOGI, clean up old rpis */ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { @@ -5407,8 +5576,8 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport) if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) { /* Try it one more time */ vport->fc_ns_retry++; - rc = lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, - vport->fc_ns_retry, 0); + vport->gidft_inp = 0; + rc = lpfc_issue_gidft(vport); if (rc == 0) break; } @@ -5523,12 +5692,14 @@ restart_disc: if (clrlaerr) { lpfc_disc_flush_list(vport); - psli->ring[(psli->extra_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; - psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; - psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + if (phba->sli_rev != LPFC_SLI_REV4) { + psli->sli3_ring[(LPFC_EXTRA_RING)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->sli3_ring[LPFC_FCP_RING].flag &= + ~LPFC_STOP_IOCB_EVENT; + } vport->port_state = LPFC_VPORT_READY; } - return; } diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 3b970d370600..15ca21484150 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -44,8 +46,6 @@ #define LPFC_FCP_RING 0 /* ring 0 for FCP initiator commands */ #define LPFC_EXTRA_RING 1 /* ring 1 for other protocols */ #define LPFC_ELS_RING 2 /* ring 2 for ELS commands */ -#define LPFC_FCP_NEXT_RING 3 -#define LPFC_FCP_OAS_RING 3 #define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */ #define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */ @@ -92,8 +92,10 @@ union CtCommandResponse { uint32_t word; }; -#define FC4_FEATURE_INIT 0x2 -#define FC4_FEATURE_TARGET 0x1 +/* FC4 Feature bits for RFF_ID */ +#define FC4_FEATURE_TARGET 0x1 +#define FC4_FEATURE_INIT 0x2 +#define FC4_FEATURE_NVME_DISC 0x4 struct lpfc_sli_ct_request { /* Structure is in Big Endian format */ @@ -117,6 +119,16 @@ struct lpfc_sli_ct_request { uint8_t AreaScope; uint8_t Fc4Type; /* for GID_FT requests */ } gid; + struct gid_ff { + uint8_t Flags; + uint8_t DomainScope; + uint8_t AreaScope; + uint8_t rsvd1; + uint8_t rsvd2; + uint8_t rsvd3; + uint8_t Fc4FBits; + uint8_t Fc4Type; + } gid_ff; struct rft { uint32_t PortId; /* For RFT_ID requests */ @@ -161,6 +173,12 @@ struct lpfc_sli_ct_request { struct gff_acc { uint8_t fbits[128]; } gff_acc; + struct gft { + uint32_t PortId; + } gft; + struct gft_acc { + uint32_t fc4_types[8]; + } gft_acc; #define FCP_TYPE_FEATURE_OFFSET 7 struct rff { uint32_t PortId; @@ -176,8 +194,12 @@ struct lpfc_sli_ct_request { #define SLI_CT_REVISION 1 #define GID_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \ sizeof(struct gid)) +#define GIDFF_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \ + sizeof(struct gid_ff)) #define GFF_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \ sizeof(struct gff)) +#define GFT_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \ + sizeof(struct gft)) #define RFT_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \ sizeof(struct rft)) #define RFF_REQUEST_SZ (offsetof(struct lpfc_sli_ct_request, un) + \ @@ -273,6 +295,7 @@ struct lpfc_sli_ct_request { #define SLI_CTNS_GNN_IP 0x0153 #define SLI_CTNS_GIPA_IP 0x0156 #define SLI_CTNS_GID_FT 0x0171 +#define SLI_CTNS_GID_FF 0x01F1 #define SLI_CTNS_GID_PT 0x01A1 #define SLI_CTNS_RPN_ID 0x0212 #define SLI_CTNS_RNN_ID 0x0213 @@ -290,15 +313,16 @@ struct lpfc_sli_ct_request { * Port Types */ -#define SLI_CTPT_N_PORT 0x01 -#define SLI_CTPT_NL_PORT 0x02 -#define SLI_CTPT_FNL_PORT 0x03 -#define SLI_CTPT_IP 0x04 -#define SLI_CTPT_FCP 0x08 -#define SLI_CTPT_NX_PORT 0x7F -#define SLI_CTPT_F_PORT 0x81 -#define SLI_CTPT_FL_PORT 0x82 -#define SLI_CTPT_E_PORT 0x84 +#define SLI_CTPT_N_PORT 0x01 +#define SLI_CTPT_NL_PORT 0x02 +#define SLI_CTPT_FNL_PORT 0x03 +#define SLI_CTPT_IP 0x04 +#define SLI_CTPT_FCP 0x08 +#define SLI_CTPT_NVME 0x28 +#define SLI_CTPT_NX_PORT 0x7F +#define SLI_CTPT_F_PORT 0x81 +#define SLI_CTPT_FL_PORT 0x82 +#define SLI_CTPT_E_PORT 0x84 #define SLI_CT_LAST_ENTRY 0x80000000 @@ -339,6 +363,7 @@ struct lpfc_name { uint8_t IEEE[6]; /* FC IEEE address */ } s; uint8_t wwn[8]; + uint64_t name; } u; }; @@ -492,7 +517,15 @@ struct serv_parm { /* Structure is in Big Endian format */ struct class_parms cls2; struct class_parms cls3; struct class_parms cls4; - uint8_t vendorVersion[16]; + union { + uint8_t vendorVersion[16]; + struct { + uint32_t vid; +#define LPFC_VV_EMLX_ID 0x454d4c58 /* EMLX */ + uint32_t flags; +#define LPFC_VV_SUPPRESS_RSP 1 + } vv; + } un; }; /* @@ -551,6 +584,7 @@ struct fc_vft_header { #define ELS_CMD_REC 0x13000000 #define ELS_CMD_RDP 0x18000000 #define ELS_CMD_PRLI 0x20100014 +#define ELS_CMD_NVMEPRLI 0x20140018 #define ELS_CMD_PRLO 0x21100014 #define ELS_CMD_PRLO_ACC 0x02100014 #define ELS_CMD_PDISC 0x50000000 @@ -590,6 +624,7 @@ struct fc_vft_header { #define ELS_CMD_REC 0x13 #define ELS_CMD_RDP 0x18 #define ELS_CMD_PRLI 0x14001020 +#define ELS_CMD_NVMEPRLI 0x18001420 #define ELS_CMD_PRLO 0x14001021 #define ELS_CMD_PRLO_ACC 0x14001002 #define ELS_CMD_PDISC 0x50 @@ -686,6 +721,7 @@ typedef struct _PRLI { /* Structure is in Big Endian format */ uint8_t prliType; /* FC Parm Word 0, bit 24:31 */ #define PRLI_FCP_TYPE 0x08 +#define PRLI_NVME_TYPE 0x28 uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */ #ifdef __BIG_ENDIAN_BITFIELD @@ -1245,8 +1281,7 @@ struct fc_rdp_opd_sfp_info { uint8_t vendor_name[16]; uint8_t model_number[16]; uint8_t serial_number[16]; - uint8_t revision[2]; - uint8_t reserved[2]; + uint8_t revision[4]; uint8_t date[8]; }; @@ -1265,14 +1300,14 @@ struct fc_rdp_req_frame { struct fc_rdp_res_frame { - uint32_t reply_sequence; /* FC word0 LS_ACC or LS_RJT */ - uint32_t length; /* FC Word 1 */ - struct fc_rdp_link_service_desc link_service_desc; /* Word 2 -4 */ - struct fc_rdp_sfp_desc sfp_desc; /* Word 5 -9 */ - struct fc_rdp_port_speed_desc portspeed_desc; /* Word 10-12 */ - struct fc_rdp_link_error_status_desc link_error_desc; /* Word 13-21 */ - struct fc_rdp_port_name_desc diag_port_names_desc; /* Word 22-27 */ - struct fc_rdp_port_name_desc attached_port_names_desc;/* Word 28-33 */ + uint32_t reply_sequence; /* FC word0 LS_ACC or LS_RJT */ + uint32_t length; /* FC Word 1 */ + struct fc_rdp_link_service_desc link_service_desc; /* Word 2 -4 */ + struct fc_rdp_sfp_desc sfp_desc; /* Word 5 -9 */ + struct fc_rdp_port_speed_desc portspeed_desc; /* Word 10 -12 */ + struct fc_rdp_link_error_status_desc link_error_desc; /* Word 13 -21 */ + struct fc_rdp_port_name_desc diag_port_names_desc; /* Word 22 -27 */ + struct fc_rdp_port_name_desc attached_port_names_desc;/* Word 28 -33 */ struct fc_fec_rdp_desc fec_desc; /* FC word 34-37*/ struct fc_rdp_bbc_desc bbc_desc; /* FC Word 38-42*/ struct fc_rdp_oed_sfp_desc oed_temp_desc; /* FC Word 43-47*/ @@ -1791,6 +1826,7 @@ typedef struct { /* FireFly BIU registers */ #define MBX_INIT_VFI 0xA3 #define MBX_INIT_VPI 0xA4 #define MBX_ACCESS_VDATA 0xA5 +#define MBX_REG_FCFI_MRQ 0xAF #define MBX_AUTH_PORT 0xF8 #define MBX_SECURITY_MGMT 0xF9 diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 5646699b0516..cfdb068a3bfc 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2009-2016 Emulex. All rights reserved. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -108,6 +110,7 @@ struct lpfc_sli_intf { #define LPFC_MAX_MQ_PAGE 8 #define LPFC_MAX_WQ_PAGE_V0 4 #define LPFC_MAX_WQ_PAGE 8 +#define LPFC_MAX_RQ_PAGE 8 #define LPFC_MAX_CQ_PAGE 4 #define LPFC_MAX_EQ_PAGE 8 @@ -198,7 +201,7 @@ struct lpfc_sli_intf { /* Configuration of Interrupts / sec for entire HBA port */ #define LPFC_MIN_IMAX 5000 #define LPFC_MAX_IMAX 5000000 -#define LPFC_DEF_IMAX 50000 +#define LPFC_DEF_IMAX 150000 #define LPFC_MIN_CPU_MAP 0 #define LPFC_MAX_CPU_MAP 2 @@ -348,6 +351,7 @@ struct lpfc_cqe { #define CQE_CODE_RECEIVE 0x4 #define CQE_CODE_XRI_ABORTED 0x5 #define CQE_CODE_RECEIVE_V1 0x9 +#define CQE_CODE_NVME_ERSP 0xd /* * Define mask value for xri_aborted and wcqe completed CQE extended status. @@ -367,6 +371,9 @@ struct lpfc_wcqe_complete { #define lpfc_wcqe_c_hw_status_SHIFT 0 #define lpfc_wcqe_c_hw_status_MASK 0x000000FF #define lpfc_wcqe_c_hw_status_WORD word0 +#define lpfc_wcqe_c_ersp0_SHIFT 0 +#define lpfc_wcqe_c_ersp0_MASK 0x0000FFFF +#define lpfc_wcqe_c_ersp0_WORD word0 uint32_t total_data_placed; uint32_t parameter; #define lpfc_wcqe_c_bg_edir_SHIFT 5 @@ -400,6 +407,9 @@ struct lpfc_wcqe_complete { #define lpfc_wcqe_c_code_SHIFT lpfc_cqe_code_SHIFT #define lpfc_wcqe_c_code_MASK lpfc_cqe_code_MASK #define lpfc_wcqe_c_code_WORD lpfc_cqe_code_WORD +#define lpfc_wcqe_c_sqhead_SHIFT 0 +#define lpfc_wcqe_c_sqhead_MASK 0x0000FFFF +#define lpfc_wcqe_c_sqhead_WORD word3 }; /* completion queue entry for wqe release */ @@ -954,6 +964,7 @@ struct mbox_header { #define LPFC_MBOX_OPCODE_FCOE_DELETE_FCF 0x0A #define LPFC_MBOX_OPCODE_FCOE_POST_HDR_TEMPLATE 0x0B #define LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF 0x10 +#define LPFC_MBOX_OPCODE_FCOE_CQ_CREATE_SET 0x1D #define LPFC_MBOX_OPCODE_FCOE_SET_FCLINK_SETTINGS 0x21 #define LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_STATE 0x22 #define LPFC_MBOX_OPCODE_FCOE_LINK_DIAG_LOOPBACK 0x23 @@ -1135,6 +1146,116 @@ struct lpfc_mbx_cq_create { } u; }; +struct lpfc_mbx_cq_create_set { + union lpfc_sli4_cfg_shdr cfg_shdr; + union { + struct { + uint32_t word0; +#define lpfc_mbx_cq_create_set_page_size_SHIFT 16 /* Version 2 Only */ +#define lpfc_mbx_cq_create_set_page_size_MASK 0x000000FF +#define lpfc_mbx_cq_create_set_page_size_WORD word0 +#define lpfc_mbx_cq_create_set_num_pages_SHIFT 0 +#define lpfc_mbx_cq_create_set_num_pages_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_num_pages_WORD word0 + uint32_t word1; +#define lpfc_mbx_cq_create_set_evt_SHIFT 31 +#define lpfc_mbx_cq_create_set_evt_MASK 0x00000001 +#define lpfc_mbx_cq_create_set_evt_WORD word1 +#define lpfc_mbx_cq_create_set_valid_SHIFT 29 +#define lpfc_mbx_cq_create_set_valid_MASK 0x00000001 +#define lpfc_mbx_cq_create_set_valid_WORD word1 +#define lpfc_mbx_cq_create_set_cqe_cnt_SHIFT 27 +#define lpfc_mbx_cq_create_set_cqe_cnt_MASK 0x00000003 +#define lpfc_mbx_cq_create_set_cqe_cnt_WORD word1 +#define lpfc_mbx_cq_create_set_cqe_size_SHIFT 25 +#define lpfc_mbx_cq_create_set_cqe_size_MASK 0x00000003 +#define lpfc_mbx_cq_create_set_cqe_size_WORD word1 +#define lpfc_mbx_cq_create_set_auto_SHIFT 15 +#define lpfc_mbx_cq_create_set_auto_MASK 0x0000001 +#define lpfc_mbx_cq_create_set_auto_WORD word1 +#define lpfc_mbx_cq_create_set_nodelay_SHIFT 14 +#define lpfc_mbx_cq_create_set_nodelay_MASK 0x00000001 +#define lpfc_mbx_cq_create_set_nodelay_WORD word1 +#define lpfc_mbx_cq_create_set_clswm_SHIFT 12 +#define lpfc_mbx_cq_create_set_clswm_MASK 0x00000003 +#define lpfc_mbx_cq_create_set_clswm_WORD word1 + uint32_t word2; +#define lpfc_mbx_cq_create_set_arm_SHIFT 31 +#define lpfc_mbx_cq_create_set_arm_MASK 0x00000001 +#define lpfc_mbx_cq_create_set_arm_WORD word2 +#define lpfc_mbx_cq_create_set_num_cq_SHIFT 0 +#define lpfc_mbx_cq_create_set_num_cq_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_num_cq_WORD word2 + uint32_t word3; +#define lpfc_mbx_cq_create_set_eq_id1_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id1_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id1_WORD word3 +#define lpfc_mbx_cq_create_set_eq_id0_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id0_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id0_WORD word3 + uint32_t word4; +#define lpfc_mbx_cq_create_set_eq_id3_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id3_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id3_WORD word4 +#define lpfc_mbx_cq_create_set_eq_id2_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id2_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id2_WORD word4 + uint32_t word5; +#define lpfc_mbx_cq_create_set_eq_id5_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id5_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id5_WORD word5 +#define lpfc_mbx_cq_create_set_eq_id4_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id4_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id4_WORD word5 + uint32_t word6; +#define lpfc_mbx_cq_create_set_eq_id7_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id7_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id7_WORD word6 +#define lpfc_mbx_cq_create_set_eq_id6_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id6_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id6_WORD word6 + uint32_t word7; +#define lpfc_mbx_cq_create_set_eq_id9_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id9_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id9_WORD word7 +#define lpfc_mbx_cq_create_set_eq_id8_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id8_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id8_WORD word7 + uint32_t word8; +#define lpfc_mbx_cq_create_set_eq_id11_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id11_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id11_WORD word8 +#define lpfc_mbx_cq_create_set_eq_id10_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id10_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id10_WORD word8 + uint32_t word9; +#define lpfc_mbx_cq_create_set_eq_id13_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id13_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id13_WORD word9 +#define lpfc_mbx_cq_create_set_eq_id12_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id12_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id12_WORD word9 + uint32_t word10; +#define lpfc_mbx_cq_create_set_eq_id15_SHIFT 16 +#define lpfc_mbx_cq_create_set_eq_id15_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id15_WORD word10 +#define lpfc_mbx_cq_create_set_eq_id14_SHIFT 0 +#define lpfc_mbx_cq_create_set_eq_id14_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_eq_id14_WORD word10 + struct dma_address page[1]; + } request; + struct { + uint32_t word0; +#define lpfc_mbx_cq_create_set_num_alloc_SHIFT 16 +#define lpfc_mbx_cq_create_set_num_alloc_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_num_alloc_WORD word0 +#define lpfc_mbx_cq_create_set_base_id_SHIFT 0 +#define lpfc_mbx_cq_create_set_base_id_MASK 0x0000FFFF +#define lpfc_mbx_cq_create_set_base_id_WORD word0 + } response; + } u; +}; + struct lpfc_mbx_cq_destroy { struct mbox_header header; union { @@ -1186,6 +1307,7 @@ struct lpfc_mbx_wq_create { #define lpfc_mbx_wq_create_page_size_SHIFT 0 #define lpfc_mbx_wq_create_page_size_MASK 0x000000FF #define lpfc_mbx_wq_create_page_size_WORD word1 +#define LPFC_WQ_PAGE_SIZE_4096 0x1 #define lpfc_mbx_wq_create_wqe_size_SHIFT 8 #define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F #define lpfc_mbx_wq_create_wqe_size_WORD word1 @@ -1243,10 +1365,10 @@ struct rq_context { #define LPFC_RQ_RING_SIZE_1024 10 /* 1024 entries */ #define LPFC_RQ_RING_SIZE_2048 11 /* 2048 entries */ #define LPFC_RQ_RING_SIZE_4096 12 /* 4096 entries */ -#define lpfc_rq_context_rqe_count_1_SHIFT 16 /* Version 1 Only */ +#define lpfc_rq_context_rqe_count_1_SHIFT 16 /* Version 1-2 Only */ #define lpfc_rq_context_rqe_count_1_MASK 0x0000FFFF #define lpfc_rq_context_rqe_count_1_WORD word0 -#define lpfc_rq_context_rqe_size_SHIFT 8 /* Version 1 Only */ +#define lpfc_rq_context_rqe_size_SHIFT 8 /* Version 1-2 Only */ #define lpfc_rq_context_rqe_size_MASK 0x0000000F #define lpfc_rq_context_rqe_size_WORD word0 #define LPFC_RQE_SIZE_8 2 @@ -1257,7 +1379,14 @@ struct rq_context { #define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */ #define lpfc_rq_context_page_size_MASK 0x000000FF #define lpfc_rq_context_page_size_WORD word0 - uint32_t reserved1; +#define LPFC_RQ_PAGE_SIZE_4096 0x1 + uint32_t word1; +#define lpfc_rq_context_data_size_SHIFT 16 /* Version 2 Only */ +#define lpfc_rq_context_data_size_MASK 0x0000FFFF +#define lpfc_rq_context_data_size_WORD word1 +#define lpfc_rq_context_hdr_size_SHIFT 0 /* Version 2 Only */ +#define lpfc_rq_context_hdr_size_MASK 0x0000FFFF +#define lpfc_rq_context_hdr_size_WORD word1 uint32_t word2; #define lpfc_rq_context_cq_id_SHIFT 16 #define lpfc_rq_context_cq_id_MASK 0x000003FF @@ -1265,6 +1394,9 @@ struct rq_context { #define lpfc_rq_context_buf_size_SHIFT 0 #define lpfc_rq_context_buf_size_MASK 0x0000FFFF #define lpfc_rq_context_buf_size_WORD word2 +#define lpfc_rq_context_base_cq_SHIFT 0 /* Version 2 Only */ +#define lpfc_rq_context_base_cq_MASK 0x0000FFFF +#define lpfc_rq_context_base_cq_WORD word2 uint32_t buffer_size; /* Version 1 Only */ }; @@ -1286,10 +1418,65 @@ struct lpfc_mbx_rq_create { #define lpfc_mbx_rq_create_ulp_num_MASK 0x000000FF #define lpfc_mbx_rq_create_ulp_num_WORD word0 struct rq_context context; - struct dma_address page[LPFC_MAX_WQ_PAGE]; + struct dma_address page[LPFC_MAX_RQ_PAGE]; } request; struct { uint32_t word0; +#define lpfc_mbx_rq_create_q_cnt_v2_SHIFT 16 +#define lpfc_mbx_rq_create_q_cnt_v2_MASK 0x0000FFFF +#define lpfc_mbx_rq_create_q_cnt_v2_WORD word0 +#define lpfc_mbx_rq_create_q_id_SHIFT 0 +#define lpfc_mbx_rq_create_q_id_MASK 0x0000FFFF +#define lpfc_mbx_rq_create_q_id_WORD word0 + uint32_t doorbell_offset; + uint32_t word2; +#define lpfc_mbx_rq_create_bar_set_SHIFT 0 +#define lpfc_mbx_rq_create_bar_set_MASK 0x0000FFFF +#define lpfc_mbx_rq_create_bar_set_WORD word2 +#define lpfc_mbx_rq_create_db_format_SHIFT 16 +#define lpfc_mbx_rq_create_db_format_MASK 0x0000FFFF +#define lpfc_mbx_rq_create_db_format_WORD word2 + } response; + } u; +}; + +struct lpfc_mbx_rq_create_v2 { + union lpfc_sli4_cfg_shdr cfg_shdr; + union { + struct { + uint32_t word0; +#define lpfc_mbx_rq_create_num_pages_SHIFT 0 +#define lpfc_mbx_rq_create_num_pages_MASK 0x0000FFFF +#define lpfc_mbx_rq_create_num_pages_WORD word0 +#define lpfc_mbx_rq_create_rq_cnt_SHIFT 16 +#define lpfc_mbx_rq_create_rq_cnt_MASK 0x000000FF +#define lpfc_mbx_rq_create_rq_cnt_WORD word0 +#define lpfc_mbx_rq_create_dua_SHIFT 16 +#define lpfc_mbx_rq_create_dua_MASK 0x00000001 +#define lpfc_mbx_rq_create_dua_WORD word0 +#define lpfc_mbx_rq_create_bqu_SHIFT 17 +#define lpfc_mbx_rq_create_bqu_MASK 0x00000001 +#define lpfc_mbx_rq_create_bqu_WORD word0 +#define lpfc_mbx_rq_create_ulp_num_SHIFT 24 +#define lpfc_mbx_rq_create_ulp_num_MASK 0x000000FF +#define lpfc_mbx_rq_create_ulp_num_WORD word0 +#define lpfc_mbx_rq_create_dim_SHIFT 29 +#define lpfc_mbx_rq_create_dim_MASK 0x00000001 +#define lpfc_mbx_rq_create_dim_WORD word0 +#define lpfc_mbx_rq_create_dfd_SHIFT 30 +#define lpfc_mbx_rq_create_dfd_MASK 0x00000001 +#define lpfc_mbx_rq_create_dfd_WORD word0 +#define lpfc_mbx_rq_create_dnb_SHIFT 31 +#define lpfc_mbx_rq_create_dnb_MASK 0x00000001 +#define lpfc_mbx_rq_create_dnb_WORD word0 + struct rq_context context; + struct dma_address page[1]; + } request; + struct { + uint32_t word0; +#define lpfc_mbx_rq_create_q_cnt_v2_SHIFT 16 +#define lpfc_mbx_rq_create_q_cnt_v2_MASK 0x0000FFFF +#define lpfc_mbx_rq_create_q_cnt_v2_WORD word0 #define lpfc_mbx_rq_create_q_id_SHIFT 0 #define lpfc_mbx_rq_create_q_id_MASK 0x0000FFFF #define lpfc_mbx_rq_create_q_id_WORD word0 @@ -2203,6 +2390,160 @@ struct lpfc_mbx_reg_fcfi { #define lpfc_reg_fcfi_vlan_tag_WORD word8 }; +struct lpfc_mbx_reg_fcfi_mrq { + uint32_t word1; +#define lpfc_reg_fcfi_mrq_info_index_SHIFT 0 +#define lpfc_reg_fcfi_mrq_info_index_MASK 0x0000FFFF +#define lpfc_reg_fcfi_mrq_info_index_WORD word1 +#define lpfc_reg_fcfi_mrq_fcfi_SHIFT 16 +#define lpfc_reg_fcfi_mrq_fcfi_MASK 0x0000FFFF +#define lpfc_reg_fcfi_mrq_fcfi_WORD word1 + uint32_t word2; +#define lpfc_reg_fcfi_mrq_rq_id1_SHIFT 0 +#define lpfc_reg_fcfi_mrq_rq_id1_MASK 0x0000FFFF +#define lpfc_reg_fcfi_mrq_rq_id1_WORD word2 +#define lpfc_reg_fcfi_mrq_rq_id0_SHIFT 16 +#define lpfc_reg_fcfi_mrq_rq_id0_MASK 0x0000FFFF +#define lpfc_reg_fcfi_mrq_rq_id0_WORD word2 + uint32_t word3; +#define lpfc_reg_fcfi_mrq_rq_id3_SHIFT 0 +#define lpfc_reg_fcfi_mrq_rq_id3_MASK 0x0000FFFF +#define lpfc_reg_fcfi_mrq_rq_id3_WORD word3 +#define lpfc_reg_fcfi_mrq_rq_id2_SHIFT 16 +#define lpfc_reg_fcfi_mrq_rq_id2_MASK 0x0000FFFF +#define lpfc_reg_fcfi_mrq_rq_id2_WORD word3 + uint32_t word4; +#define lpfc_reg_fcfi_mrq_type_match0_SHIFT 24 +#define lpfc_reg_fcfi_mrq_type_match0_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_match0_WORD word4 +#define lpfc_reg_fcfi_mrq_type_mask0_SHIFT 16 +#define lpfc_reg_fcfi_mrq_type_mask0_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_mask0_WORD word4 +#define lpfc_reg_fcfi_mrq_rctl_match0_SHIFT 8 +#define lpfc_reg_fcfi_mrq_rctl_match0_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_match0_WORD word4 +#define lpfc_reg_fcfi_mrq_rctl_mask0_SHIFT 0 +#define lpfc_reg_fcfi_mrq_rctl_mask0_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_mask0_WORD word4 + uint32_t word5; +#define lpfc_reg_fcfi_mrq_type_match1_SHIFT 24 +#define lpfc_reg_fcfi_mrq_type_match1_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_match1_WORD word5 +#define lpfc_reg_fcfi_mrq_type_mask1_SHIFT 16 +#define lpfc_reg_fcfi_mrq_type_mask1_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_mask1_WORD word5 +#define lpfc_reg_fcfi_mrq_rctl_match1_SHIFT 8 +#define lpfc_reg_fcfi_mrq_rctl_match1_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_match1_WORD word5 +#define lpfc_reg_fcfi_mrq_rctl_mask1_SHIFT 0 +#define lpfc_reg_fcfi_mrq_rctl_mask1_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_mask1_WORD word5 + uint32_t word6; +#define lpfc_reg_fcfi_mrq_type_match2_SHIFT 24 +#define lpfc_reg_fcfi_mrq_type_match2_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_match2_WORD word6 +#define lpfc_reg_fcfi_mrq_type_mask2_SHIFT 16 +#define lpfc_reg_fcfi_mrq_type_mask2_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_mask2_WORD word6 +#define lpfc_reg_fcfi_mrq_rctl_match2_SHIFT 8 +#define lpfc_reg_fcfi_mrq_rctl_match2_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_match2_WORD word6 +#define lpfc_reg_fcfi_mrq_rctl_mask2_SHIFT 0 +#define lpfc_reg_fcfi_mrq_rctl_mask2_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_mask2_WORD word6 + uint32_t word7; +#define lpfc_reg_fcfi_mrq_type_match3_SHIFT 24 +#define lpfc_reg_fcfi_mrq_type_match3_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_match3_WORD word7 +#define lpfc_reg_fcfi_mrq_type_mask3_SHIFT 16 +#define lpfc_reg_fcfi_mrq_type_mask3_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_type_mask3_WORD word7 +#define lpfc_reg_fcfi_mrq_rctl_match3_SHIFT 8 +#define lpfc_reg_fcfi_mrq_rctl_match3_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_match3_WORD word7 +#define lpfc_reg_fcfi_mrq_rctl_mask3_SHIFT 0 +#define lpfc_reg_fcfi_mrq_rctl_mask3_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_rctl_mask3_WORD word7 + uint32_t word8; +#define lpfc_reg_fcfi_mrq_ptc7_SHIFT 31 +#define lpfc_reg_fcfi_mrq_ptc7_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc7_WORD word8 +#define lpfc_reg_fcfi_mrq_ptc6_SHIFT 30 +#define lpfc_reg_fcfi_mrq_ptc6_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc6_WORD word8 +#define lpfc_reg_fcfi_mrq_ptc5_SHIFT 29 +#define lpfc_reg_fcfi_mrq_ptc5_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc5_WORD word8 +#define lpfc_reg_fcfi_mrq_ptc4_SHIFT 28 +#define lpfc_reg_fcfi_mrq_ptc4_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc4_WORD word8 +#define lpfc_reg_fcfi_mrq_ptc3_SHIFT 27 +#define lpfc_reg_fcfi_mrq_ptc3_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc3_WORD word8 +#define lpfc_reg_fcfi_mrq_ptc2_SHIFT 26 +#define lpfc_reg_fcfi_mrq_ptc2_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc2_WORD word8 +#define lpfc_reg_fcfi_mrq_ptc1_SHIFT 25 +#define lpfc_reg_fcfi_mrq_ptc1_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc1_WORD word8 +#define lpfc_reg_fcfi_mrq_ptc0_SHIFT 24 +#define lpfc_reg_fcfi_mrq_ptc0_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_ptc0_WORD word8 +#define lpfc_reg_fcfi_mrq_pt7_SHIFT 23 +#define lpfc_reg_fcfi_mrq_pt7_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt7_WORD word8 +#define lpfc_reg_fcfi_mrq_pt6_SHIFT 22 +#define lpfc_reg_fcfi_mrq_pt6_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt6_WORD word8 +#define lpfc_reg_fcfi_mrq_pt5_SHIFT 21 +#define lpfc_reg_fcfi_mrq_pt5_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt5_WORD word8 +#define lpfc_reg_fcfi_mrq_pt4_SHIFT 20 +#define lpfc_reg_fcfi_mrq_pt4_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt4_WORD word8 +#define lpfc_reg_fcfi_mrq_pt3_SHIFT 19 +#define lpfc_reg_fcfi_mrq_pt3_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt3_WORD word8 +#define lpfc_reg_fcfi_mrq_pt2_SHIFT 18 +#define lpfc_reg_fcfi_mrq_pt2_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt2_WORD word8 +#define lpfc_reg_fcfi_mrq_pt1_SHIFT 17 +#define lpfc_reg_fcfi_mrq_pt1_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt1_WORD word8 +#define lpfc_reg_fcfi_mrq_pt0_SHIFT 16 +#define lpfc_reg_fcfi_mrq_pt0_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_pt0_WORD word8 +#define lpfc_reg_fcfi_mrq_xmv_SHIFT 15 +#define lpfc_reg_fcfi_mrq_xmv_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_xmv_WORD word8 +#define lpfc_reg_fcfi_mrq_mode_SHIFT 13 +#define lpfc_reg_fcfi_mrq_mode_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_mode_WORD word8 +#define lpfc_reg_fcfi_mrq_vv_SHIFT 12 +#define lpfc_reg_fcfi_mrq_vv_MASK 0x00000001 +#define lpfc_reg_fcfi_mrq_vv_WORD word8 +#define lpfc_reg_fcfi_mrq_vlan_tag_SHIFT 0 +#define lpfc_reg_fcfi_mrq_vlan_tag_MASK 0x00000FFF +#define lpfc_reg_fcfi_mrq_vlan_tag_WORD word8 + uint32_t word9; +#define lpfc_reg_fcfi_mrq_policy_SHIFT 12 +#define lpfc_reg_fcfi_mrq_policy_MASK 0x0000000F +#define lpfc_reg_fcfi_mrq_policy_WORD word9 +#define lpfc_reg_fcfi_mrq_filter_SHIFT 8 +#define lpfc_reg_fcfi_mrq_filter_MASK 0x0000000F +#define lpfc_reg_fcfi_mrq_filter_WORD word9 +#define lpfc_reg_fcfi_mrq_npairs_SHIFT 0 +#define lpfc_reg_fcfi_mrq_npairs_MASK 0x000000FF +#define lpfc_reg_fcfi_mrq_npairs_WORD word9 + uint32_t word10; + uint32_t word11; + uint32_t word12; + uint32_t word13; + uint32_t word14; + uint32_t word15; + uint32_t word16; +}; + struct lpfc_mbx_unreg_fcfi { uint32_t word1_rsv; uint32_t word2; @@ -2382,6 +2723,9 @@ struct lpfc_mbx_request_features { #define lpfc_mbx_rq_ftr_rq_perfh_SHIFT 11 #define lpfc_mbx_rq_ftr_rq_perfh_MASK 0x00000001 #define lpfc_mbx_rq_ftr_rq_perfh_WORD word2 +#define lpfc_mbx_rq_ftr_rq_mrqp_SHIFT 16 +#define lpfc_mbx_rq_ftr_rq_mrqp_MASK 0x00000001 +#define lpfc_mbx_rq_ftr_rq_mrqp_WORD word2 uint32_t word3; #define lpfc_mbx_rq_ftr_rsp_iaab_SHIFT 0 #define lpfc_mbx_rq_ftr_rsp_iaab_MASK 0x00000001 @@ -2410,6 +2754,9 @@ struct lpfc_mbx_request_features { #define lpfc_mbx_rq_ftr_rsp_perfh_SHIFT 11 #define lpfc_mbx_rq_ftr_rsp_perfh_MASK 0x00000001 #define lpfc_mbx_rq_ftr_rsp_perfh_WORD word3 +#define lpfc_mbx_rq_ftr_rsp_mrqp_SHIFT 16 +#define lpfc_mbx_rq_ftr_rsp_mrqp_MASK 0x00000001 +#define lpfc_mbx_rq_ftr_rsp_mrqp_WORD word3 }; struct lpfc_mbx_supp_pages { @@ -2839,12 +3186,18 @@ struct lpfc_sli4_parameters { #define cfg_mqv_WORD word6 uint32_t word7; uint32_t word8; +#define cfg_wqpcnt_SHIFT 0 +#define cfg_wqpcnt_MASK 0x0000000f +#define cfg_wqpcnt_WORD word8 #define cfg_wqsize_SHIFT 8 #define cfg_wqsize_MASK 0x0000000f #define cfg_wqsize_WORD word8 #define cfg_wqv_SHIFT 14 #define cfg_wqv_MASK 0x00000003 #define cfg_wqv_WORD word8 +#define cfg_wqpsize_SHIFT 16 +#define cfg_wqpsize_MASK 0x000000ff +#define cfg_wqpsize_WORD word8 uint32_t word9; uint32_t word10; #define cfg_rqv_SHIFT 14 @@ -2895,6 +3248,12 @@ struct lpfc_sli4_parameters { #define cfg_mds_diags_SHIFT 1 #define cfg_mds_diags_MASK 0x00000001 #define cfg_mds_diags_WORD word19 +#define cfg_nvme_SHIFT 3 +#define cfg_nvme_MASK 0x00000001 +#define cfg_nvme_WORD word19 +#define cfg_xib_SHIFT 4 +#define cfg_xib_MASK 0x00000001 +#define cfg_xib_WORD word19 }; #define LPFC_SET_UE_RECOVERY 0x10 @@ -3290,14 +3649,17 @@ struct lpfc_mqe { struct lpfc_mbx_del_fcf_tbl_entry del_fcf_entry; struct lpfc_mbx_redisc_fcf_tbl redisc_fcf_tbl; struct lpfc_mbx_reg_fcfi reg_fcfi; + struct lpfc_mbx_reg_fcfi_mrq reg_fcfi_mrq; struct lpfc_mbx_unreg_fcfi unreg_fcfi; struct lpfc_mbx_mq_create mq_create; struct lpfc_mbx_mq_create_ext mq_create_ext; struct lpfc_mbx_eq_create eq_create; struct lpfc_mbx_modify_eq_delay eq_delay; struct lpfc_mbx_cq_create cq_create; + struct lpfc_mbx_cq_create_set cq_create_set; struct lpfc_mbx_wq_create wq_create; struct lpfc_mbx_rq_create rq_create; + struct lpfc_mbx_rq_create_v2 rq_create_v2; struct lpfc_mbx_mq_destroy mq_destroy; struct lpfc_mbx_eq_destroy eq_destroy; struct lpfc_mbx_cq_destroy cq_destroy; @@ -3657,6 +4019,9 @@ struct wqe_common { #define wqe_ebde_cnt_SHIFT 0 #define wqe_ebde_cnt_MASK 0x0000000f #define wqe_ebde_cnt_WORD word10 +#define wqe_nvme_SHIFT 4 +#define wqe_nvme_MASK 0x00000001 +#define wqe_nvme_WORD word10 #define wqe_oas_SHIFT 6 #define wqe_oas_MASK 0x00000001 #define wqe_oas_WORD word10 @@ -3717,9 +4082,18 @@ struct wqe_common { #define LPFC_ELS_ID_FDISC 2 #define LPFC_ELS_ID_LOGO 1 #define LPFC_ELS_ID_DEFAULT 0 +#define wqe_irsp_SHIFT 4 +#define wqe_irsp_MASK 0x00000001 +#define wqe_irsp_WORD word11 +#define wqe_sup_SHIFT 6 +#define wqe_sup_MASK 0x00000001 +#define wqe_sup_WORD word11 #define wqe_wqec_SHIFT 7 #define wqe_wqec_MASK 0x00000001 #define wqe_wqec_WORD word11 +#define wqe_irsplen_SHIFT 8 +#define wqe_irsplen_MASK 0x0000000f +#define wqe_irsplen_WORD word11 #define wqe_cqid_SHIFT 16 #define wqe_cqid_MASK 0x0000ffff #define wqe_cqid_WORD word11 @@ -3897,6 +4271,50 @@ struct gen_req64_wqe { uint32_t max_response_payload_len; }; +/* Define NVME PRLI request to fabric. NVME is a + * fabric-only protocol. + * Updated to red-lined v1.08 on Sept 16, 2016 + */ +struct lpfc_nvme_prli { + uint32_t word1; + /* The Response Code is defined in the FCP PRLI lpfc_hw.h */ +#define prli_acc_rsp_code_SHIFT 8 +#define prli_acc_rsp_code_MASK 0x0000000f +#define prli_acc_rsp_code_WORD word1 +#define prli_estabImagePair_SHIFT 13 +#define prli_estabImagePair_MASK 0x00000001 +#define prli_estabImagePair_WORD word1 +#define prli_type_code_ext_SHIFT 16 +#define prli_type_code_ext_MASK 0x000000ff +#define prli_type_code_ext_WORD word1 +#define prli_type_code_SHIFT 24 +#define prli_type_code_MASK 0x000000ff +#define prli_type_code_WORD word1 + uint32_t word_rsvd2; + uint32_t word_rsvd3; + uint32_t word4; +#define prli_fba_SHIFT 0 +#define prli_fba_MASK 0x00000001 +#define prli_fba_WORD word4 +#define prli_disc_SHIFT 3 +#define prli_disc_MASK 0x00000001 +#define prli_disc_WORD word4 +#define prli_tgt_SHIFT 4 +#define prli_tgt_MASK 0x00000001 +#define prli_tgt_WORD word4 +#define prli_init_SHIFT 5 +#define prli_init_MASK 0x00000001 +#define prli_init_WORD word4 +#define prli_recov_SHIFT 8 +#define prli_recov_MASK 0x00000001 +#define prli_recov_WORD word4 + uint32_t word5; +#define prli_fb_sz_SHIFT 0 +#define prli_fb_sz_MASK 0x0000ffff +#define prli_fb_sz_WORD word5 +#define LPFC_NVMET_FB_SZ_MAX 65536 /* Driver target mode only. */ +}; + struct create_xri_wqe { uint32_t rsrvd[5]; /* words 0-4 */ struct wqe_did wqe_dest; /* word 5 */ @@ -3969,6 +4387,35 @@ struct fcp_icmnd64_wqe { uint32_t rsvd_12_15[4]; /* word 12-15 */ }; +struct fcp_trsp64_wqe { + struct ulp_bde64 bde; + uint32_t response_len; + uint32_t rsvd_4_5[2]; + struct wqe_common wqe_com; /* words 6-11 */ + uint32_t rsvd_12_15[4]; /* word 12-15 */ +}; + +struct fcp_tsend64_wqe { + struct ulp_bde64 bde; + uint32_t payload_offset_len; + uint32_t relative_offset; + uint32_t reserved; + struct wqe_common wqe_com; /* words 6-11 */ + uint32_t fcp_data_len; /* word 12 */ + uint32_t rsvd_13_15[3]; /* word 13-15 */ +}; + +struct fcp_treceive64_wqe { + struct ulp_bde64 bde; + uint32_t payload_offset_len; + uint32_t relative_offset; + uint32_t reserved; + struct wqe_common wqe_com; /* words 6-11 */ + uint32_t fcp_data_len; /* word 12 */ + uint32_t rsvd_13_15[3]; /* word 13-15 */ +}; +#define TXRDY_PAYLOAD_LEN 12 + union lpfc_wqe { uint32_t words[16]; @@ -3984,6 +4431,10 @@ union lpfc_wqe { struct xmit_els_rsp64_wqe xmit_els_rsp; struct els_request64_wqe els_req; struct gen_req64_wqe gen_req; + struct fcp_trsp64_wqe fcp_trsp; + struct fcp_tsend64_wqe fcp_tsend; + struct fcp_treceive64_wqe fcp_treceive; + }; union lpfc_wqe128 { @@ -3992,6 +4443,9 @@ union lpfc_wqe128 { struct fcp_icmnd64_wqe fcp_icmd; struct fcp_iread64_wqe fcp_iread; struct fcp_iwrite64_wqe fcp_iwrite; + struct fcp_trsp64_wqe fcp_trsp; + struct fcp_tsend64_wqe fcp_tsend; + struct fcp_treceive64_wqe fcp_treceive; struct xmit_seq64_wqe xmit_sequence; struct gen_req64_wqe gen_req; }; @@ -4015,11 +4469,39 @@ struct lpfc_grp_hdr { uint8_t revision[32]; }; -#define FCP_COMMAND 0x0 -#define FCP_COMMAND_DATA_OUT 0x1 -#define ELS_COMMAND_NON_FIP 0xC -#define ELS_COMMAND_FIP 0xD -#define OTHER_COMMAND 0x8 +/* Defines for WQE command type */ +#define FCP_COMMAND 0x0 +#define NVME_READ_CMD 0x0 +#define FCP_COMMAND_DATA_OUT 0x1 +#define NVME_WRITE_CMD 0x1 +#define FCP_COMMAND_TRECEIVE 0x2 +#define FCP_COMMAND_TRSP 0x3 +#define FCP_COMMAND_TSEND 0x7 +#define OTHER_COMMAND 0x8 +#define ELS_COMMAND_NON_FIP 0xC +#define ELS_COMMAND_FIP 0xD + +#define LPFC_NVME_EMBED_CMD 0x0 +#define LPFC_NVME_EMBED_WRITE 0x1 +#define LPFC_NVME_EMBED_READ 0x2 + +/* WQE Commands */ +#define CMD_ABORT_XRI_WQE 0x0F +#define CMD_XMIT_SEQUENCE64_WQE 0x82 +#define CMD_XMIT_BCAST64_WQE 0x84 +#define CMD_ELS_REQUEST64_WQE 0x8A +#define CMD_XMIT_ELS_RSP64_WQE 0x95 +#define CMD_XMIT_BLS_RSP64_WQE 0x97 +#define CMD_FCP_IWRITE64_WQE 0x98 +#define CMD_FCP_IREAD64_WQE 0x9A +#define CMD_FCP_ICMND64_WQE 0x9C +#define CMD_FCP_TSEND64_WQE 0x9F +#define CMD_FCP_TRECEIVE64_WQE 0xA1 +#define CMD_FCP_TRSP64_WQE 0xA3 +#define CMD_GEN_REQUEST64_WQE 0xC2 + +#define CMD_WQE_MASK 0xff + #define LPFC_FW_DUMP 1 #define LPFC_FW_RESET 2 diff --git a/drivers/scsi/lpfc/lpfc_ids.h b/drivers/scsi/lpfc/lpfc_ids.h index 5733feafe25f..0ba3733eb36d 100644 --- a/drivers/scsi/lpfc/lpfc_ids.h +++ b/drivers/scsi/lpfc/lpfc_ids.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 64717c171b15..0ee429d773f3 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -34,6 +36,7 @@ #include <linux/firmware.h> #include <linux/miscdevice.h> #include <linux/percpu.h> +#include <linux/msi.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> @@ -46,8 +49,9 @@ #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" #include "lpfc_logmsg.h" #include "lpfc_crtn.h" #include "lpfc_vport.h" @@ -71,6 +75,7 @@ static int lpfc_create_bootstrap_mbox(struct lpfc_hba *); static int lpfc_setup_endian_order(struct lpfc_hba *); static void lpfc_destroy_bootstrap_mbox(struct lpfc_hba *); static void lpfc_free_els_sgl_list(struct lpfc_hba *); +static void lpfc_free_nvmet_sgl_list(struct lpfc_hba *); static void lpfc_init_sgl_list(struct lpfc_hba *); static int lpfc_init_active_sgl_array(struct lpfc_hba *); static void lpfc_free_active_sgl(struct lpfc_hba *); @@ -86,6 +91,7 @@ static void lpfc_sli4_oas_verify(struct lpfc_hba *phba); static struct scsi_transport_template *lpfc_transport_template = NULL; static struct scsi_transport_template *lpfc_vport_transport_template = NULL; static DEFINE_IDR(lpfc_hba_index); +#define LPFC_NVMET_BUF_POST 254 /** * lpfc_config_port_prep - Perform lpfc initialization prior to config port @@ -499,12 +505,10 @@ lpfc_config_port_post(struct lpfc_hba *phba) phba->link_state = LPFC_LINK_DOWN; /* Only process IOCBs on ELS ring till hba_state is READY */ - if (psli->ring[psli->extra_ring].sli.sli3.cmdringaddr) - psli->ring[psli->extra_ring].flag |= LPFC_STOP_IOCB_EVENT; - if (psli->ring[psli->fcp_ring].sli.sli3.cmdringaddr) - psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT; - if (psli->ring[psli->next_ring].sli.sli3.cmdringaddr) - psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->sli3_ring[LPFC_EXTRA_RING].sli.sli3.cmdringaddr) + psli->sli3_ring[LPFC_EXTRA_RING].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->sli3_ring[LPFC_FCP_RING].sli.sli3.cmdringaddr) + psli->sli3_ring[LPFC_FCP_RING].flag |= LPFC_STOP_IOCB_EVENT; /* Post receive buffers for desired rings */ if (phba->sli_rev != 3) @@ -892,7 +896,7 @@ lpfc_hba_free_post_buf(struct lpfc_hba *phba) lpfc_sli_hbqbuf_free_all(phba); else { /* Cleanup preposted buffers on the ELS ring */ - pring = &psli->ring[LPFC_ELS_RING]; + pring = &psli->sli3_ring[LPFC_ELS_RING]; spin_lock_irq(&phba->hbalock); list_splice_init(&pring->postbufq, &buflist); spin_unlock_irq(&phba->hbalock); @@ -925,32 +929,43 @@ static void lpfc_hba_clean_txcmplq(struct lpfc_hba *phba) { struct lpfc_sli *psli = &phba->sli; + struct lpfc_queue *qp = NULL; struct lpfc_sli_ring *pring; LIST_HEAD(completions); int i; - for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; - if (phba->sli_rev >= LPFC_SLI_REV4) - spin_lock_irq(&pring->ring_lock); - else + if (phba->sli_rev != LPFC_SLI_REV4) { + for (i = 0; i < psli->num_rings; i++) { + pring = &psli->sli3_ring[i]; spin_lock_irq(&phba->hbalock); - /* At this point in time the HBA is either reset or DOA. Either - * way, nothing should be on txcmplq as it will NEVER complete. - */ - list_splice_init(&pring->txcmplq, &completions); - pring->txcmplq_cnt = 0; - - if (phba->sli_rev >= LPFC_SLI_REV4) - spin_unlock_irq(&pring->ring_lock); - else + /* At this point in time the HBA is either reset or DOA + * Nothing should be on txcmplq as it will + * NEVER complete. + */ + list_splice_init(&pring->txcmplq, &completions); + pring->txcmplq_cnt = 0; spin_unlock_irq(&phba->hbalock); + lpfc_sli_abort_iocb_ring(phba, pring); + } /* Cancel all the IOCBs from the completions list */ - lpfc_sli_cancel_iocbs(phba, &completions, IOSTAT_LOCAL_REJECT, - IOERR_SLI_ABORTED); + lpfc_sli_cancel_iocbs(phba, &completions, + IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); + return; + } + list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) { + pring = qp->pring; + if (!pring) + continue; + spin_lock_irq(&pring->ring_lock); + list_splice_init(&pring->txcmplq, &completions); + pring->txcmplq_cnt = 0; + spin_unlock_irq(&pring->ring_lock); lpfc_sli_abort_iocb_ring(phba, pring); } + /* Cancel all the IOCBs from the completions list */ + lpfc_sli_cancel_iocbs(phba, &completions, + IOSTAT_LOCAL_REJECT, IOERR_SLI_ABORTED); } /** @@ -989,43 +1004,58 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) { struct lpfc_scsi_buf *psb, *psb_next; LIST_HEAD(aborts); + LIST_HEAD(nvme_aborts); unsigned long iflag = 0; struct lpfc_sglq *sglq_entry = NULL; - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; - lpfc_hba_free_post_buf(phba); + + lpfc_sli_hbqbuf_free_all(phba); lpfc_hba_clean_txcmplq(phba); - pring = &psli->ring[LPFC_ELS_RING]; /* At this point in time the HBA is either reset or DOA. Either * way, nothing should be on lpfc_abts_els_sgl_list, it needs to be - * on the lpfc_sgl_list so that it can either be freed if the + * on the lpfc_els_sgl_list so that it can either be freed if the * driver is unloading or reposted if the driver is restarting * the port. */ - spin_lock_irq(&phba->hbalock); /* required for lpfc_sgl_list and */ + spin_lock_irq(&phba->hbalock); /* required for lpfc_els_sgl_list and */ /* scsl_buf_list */ - /* abts_sgl_list_lock required because worker thread uses this + /* sgl_list_lock required because worker thread uses this * list. */ - spin_lock(&phba->sli4_hba.abts_sgl_list_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); list_for_each_entry(sglq_entry, &phba->sli4_hba.lpfc_abts_els_sgl_list, list) sglq_entry->state = SGL_FREED; + list_for_each_entry(sglq_entry, + &phba->sli4_hba.lpfc_abts_nvmet_sgl_list, list) + sglq_entry->state = SGL_FREED; - spin_lock(&pring->ring_lock); list_splice_init(&phba->sli4_hba.lpfc_abts_els_sgl_list, - &phba->sli4_hba.lpfc_sgl_list); - spin_unlock(&pring->ring_lock); - spin_unlock(&phba->sli4_hba.abts_sgl_list_lock); + &phba->sli4_hba.lpfc_els_sgl_list); + + if (phba->sli4_hba.nvme_wq) + list_splice_init(&phba->sli4_hba.lpfc_abts_nvmet_sgl_list, + &phba->sli4_hba.lpfc_nvmet_sgl_list); + + spin_unlock(&phba->sli4_hba.sgl_list_lock); /* abts_scsi_buf_list_lock required because worker thread uses this * list. */ - spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); - list_splice_init(&phba->sli4_hba.lpfc_abts_scsi_buf_list, - &aborts); - spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { + spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); + list_splice_init(&phba->sli4_hba.lpfc_abts_scsi_buf_list, + &aborts); + spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); + } + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + spin_lock(&phba->sli4_hba.abts_nvme_buf_list_lock); + list_splice_init(&phba->sli4_hba.lpfc_abts_nvme_buf_list, + &nvme_aborts); + spin_unlock(&phba->sli4_hba.abts_nvme_buf_list_lock); + } + spin_unlock_irq(&phba->hbalock); list_for_each_entry_safe(psb, psb_next, &aborts, list) { @@ -1036,6 +1066,14 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba) list_splice(&aborts, &phba->lpfc_scsi_buf_list_put); spin_unlock_irqrestore(&phba->scsi_buf_list_put_lock, iflag); + list_for_each_entry_safe(psb, psb_next, &nvme_aborts, list) { + psb->pCmd = NULL; + psb->status = IOSTAT_SUCCESS; + } + spin_lock_irqsave(&phba->nvme_buf_list_put_lock, iflag); + list_splice(&nvme_aborts, &phba->lpfc_nvme_buf_list_put); + spin_unlock_irqrestore(&phba->nvme_buf_list_put_lock, iflag); + lpfc_sli4_free_sp_events(phba); return 0; } @@ -1829,7 +1867,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba) * @phba: pointer to lpfc hba data structure. * * This routine is invoked from the worker thread to handle a HBA host - * attention link event. + * attention link event. SLI3 only. **/ void lpfc_handle_latt(struct lpfc_hba *phba) @@ -1867,7 +1905,7 @@ lpfc_handle_latt(struct lpfc_hba *phba) pmb->mbox_cmpl = lpfc_mbx_cmpl_read_topology; pmb->vport = vport; /* Block ELS IOCBs until we have processed this mbox command */ - phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT; + phba->sli.sli3_ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT; rc = lpfc_sli_issue_mbox (phba, pmb, MBX_NOWAIT); if (rc == MBX_NOT_FINISHED) { rc = 4; @@ -1883,7 +1921,7 @@ lpfc_handle_latt(struct lpfc_hba *phba) return; lpfc_handle_latt_free_mbuf: - phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT; + phba->sli.sli3_ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT; lpfc_mbuf_free(phba, mp->virt, mp->phys); lpfc_handle_latt_free_mp: kfree(mp); @@ -2441,7 +2479,7 @@ lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt) * * This routine posts initial receive IOCB buffers to the ELS ring. The * current number of initial IOCB buffers specified by LPFC_BUF_RING0 is - * set to 64 IOCBs. + * set to 64 IOCBs. SLI3 only. * * Return codes * 0 - success (currently always success) @@ -2452,7 +2490,7 @@ lpfc_post_rcv_buf(struct lpfc_hba *phba) struct lpfc_sli *psli = &phba->sli; /* Ring 0, ELS / CT buffers */ - lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0); + lpfc_post_buffer(phba, &psli->sli3_ring[LPFC_ELS_RING], LPFC_BUF_RING0); /* Ring 2 - FCP no buffers needed */ return 0; @@ -2640,6 +2678,13 @@ lpfc_cleanup(struct lpfc_vport *vport) lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RECOVERY); + if (ndlp->nlp_fc4_type & NLP_FC4_NVME) { + /* Remove the NVME transport reference now and + * continue to remove the node. + */ + lpfc_nlp_put(ndlp); + } + lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); } @@ -2894,11 +2939,6 @@ lpfc_online(struct lpfc_hba *phba) lpfc_block_mgmt_io(phba, LPFC_MBX_WAIT); - if (!lpfc_sli_queue_setup(phba)) { - lpfc_unblock_mgmt_io(phba); - return 1; - } - if (phba->sli_rev == LPFC_SLI_REV4) { if (lpfc_sli4_hba_setup(phba)) { /* Initialize SLI4 HBA */ lpfc_unblock_mgmt_io(phba); @@ -2909,6 +2949,7 @@ lpfc_online(struct lpfc_hba *phba) vpis_cleared = true; spin_unlock_irq(&phba->hbalock); } else { + lpfc_sli_queue_init(phba); if (lpfc_sli_hba_setup(phba)) { /* Initialize SLI2/SLI3 HBA */ lpfc_unblock_mgmt_io(phba); return 1; @@ -3098,7 +3139,9 @@ static void lpfc_scsi_free(struct lpfc_hba *phba) { struct lpfc_scsi_buf *sb, *sb_next; - struct lpfc_iocbq *io, *io_next; + + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) + return; spin_lock_irq(&phba->hbalock); @@ -3108,7 +3151,7 @@ lpfc_scsi_free(struct lpfc_hba *phba) list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list_put, list) { list_del(&sb->list); - pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data, + pci_pool_free(phba->lpfc_sg_dma_buf_pool, sb->data, sb->dma_handle); kfree(sb); phba->total_scsi_bufs--; @@ -3119,25 +3162,58 @@ lpfc_scsi_free(struct lpfc_hba *phba) list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list_get, list) { list_del(&sb->list); - pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data, + pci_pool_free(phba->lpfc_sg_dma_buf_pool, sb->data, sb->dma_handle); kfree(sb); phba->total_scsi_bufs--; } spin_unlock(&phba->scsi_buf_list_get_lock); + spin_unlock_irq(&phba->hbalock); +} +/** + * lpfc_nvme_free - Free all the NVME buffers and IOCBs from driver lists + * @phba: pointer to lpfc hba data structure. + * + * This routine is to free all the NVME buffers and IOCBs from the driver + * list back to kernel. It is called from lpfc_pci_remove_one to free + * the internal resources before the device is removed from the system. + **/ +static void +lpfc_nvme_free(struct lpfc_hba *phba) +{ + struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; - /* Release all the lpfc_iocbq entries maintained by this host. */ - list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) { - list_del(&io->list); - kfree(io); - phba->total_iocbq_bufs--; - } + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) + return; + + spin_lock_irq(&phba->hbalock); + /* Release all the lpfc_nvme_bufs maintained by this host. */ + spin_lock(&phba->nvme_buf_list_put_lock); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &phba->lpfc_nvme_buf_list_put, list) { + list_del(&lpfc_ncmd->list); + pci_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, + lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + phba->total_nvme_bufs--; + } + spin_unlock(&phba->nvme_buf_list_put_lock); + + spin_lock(&phba->nvme_buf_list_get_lock); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &phba->lpfc_nvme_buf_list_get, list) { + list_del(&lpfc_ncmd->list); + pci_pool_free(phba->lpfc_sg_dma_buf_pool, lpfc_ncmd->data, + lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + phba->total_nvme_bufs--; + } + spin_unlock(&phba->nvme_buf_list_get_lock); spin_unlock_irq(&phba->hbalock); } - /** - * lpfc_sli4_xri_sgl_update - update xri-sgl sizing and mapping + * lpfc_sli4_els_sgl_update - update ELS xri-sgl sizing and mapping * @phba: pointer to lpfc hba data structure. * * This routine first calculates the sizes of the current els and allocated @@ -3149,20 +3225,18 @@ lpfc_scsi_free(struct lpfc_hba *phba) * 0 - successful (for now, it always returns 0) **/ int -lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) +lpfc_sli4_els_sgl_update(struct lpfc_hba *phba) { struct lpfc_sglq *sglq_entry = NULL, *sglq_entry_next = NULL; - struct lpfc_scsi_buf *psb = NULL, *psb_next = NULL; - uint16_t i, lxri, xri_cnt, els_xri_cnt, scsi_xri_cnt; + uint16_t i, lxri, xri_cnt, els_xri_cnt; LIST_HEAD(els_sgl_list); - LIST_HEAD(scsi_sgl_list); int rc; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; /* * update on pci function's els xri-sgl list */ els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); + if (els_xri_cnt > phba->sli4_hba.els_xri_cnt) { /* els xri-sgl expanded */ xri_cnt = els_xri_cnt - phba->sli4_hba.els_xri_cnt; @@ -3198,9 +3272,10 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) list_add_tail(&sglq_entry->list, &els_sgl_list); } spin_lock_irq(&phba->hbalock); - spin_lock(&pring->ring_lock); - list_splice_init(&els_sgl_list, &phba->sli4_hba.lpfc_sgl_list); - spin_unlock(&pring->ring_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(&els_sgl_list, + &phba->sli4_hba.lpfc_els_sgl_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irq(&phba->hbalock); } else if (els_xri_cnt < phba->sli4_hba.els_xri_cnt) { /* els xri-sgl shrinked */ @@ -3210,24 +3285,22 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) "%d to %d\n", phba->sli4_hba.els_xri_cnt, els_xri_cnt); spin_lock_irq(&phba->hbalock); - spin_lock(&pring->ring_lock); - list_splice_init(&phba->sli4_hba.lpfc_sgl_list, &els_sgl_list); - spin_unlock(&pring->ring_lock); - spin_unlock_irq(&phba->hbalock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(&phba->sli4_hba.lpfc_els_sgl_list, + &els_sgl_list); /* release extra els sgls from list */ for (i = 0; i < xri_cnt; i++) { list_remove_head(&els_sgl_list, sglq_entry, struct lpfc_sglq, list); if (sglq_entry) { - lpfc_mbuf_free(phba, sglq_entry->virt, - sglq_entry->phys); + __lpfc_mbuf_free(phba, sglq_entry->virt, + sglq_entry->phys); kfree(sglq_entry); } } - spin_lock_irq(&phba->hbalock); - spin_lock(&pring->ring_lock); - list_splice_init(&els_sgl_list, &phba->sli4_hba.lpfc_sgl_list); - spin_unlock(&pring->ring_lock); + list_splice_init(&els_sgl_list, + &phba->sli4_hba.lpfc_els_sgl_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irq(&phba->hbalock); } else lpfc_printf_log(phba, KERN_INFO, LOG_SLI, @@ -3239,7 +3312,7 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) sglq_entry = NULL; sglq_entry_next = NULL; list_for_each_entry_safe(sglq_entry, sglq_entry_next, - &phba->sli4_hba.lpfc_sgl_list, list) { + &phba->sli4_hba.lpfc_els_sgl_list, list) { lxri = lpfc_sli4_next_xritag(phba); if (lxri == NO_XRI) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -3251,21 +3324,182 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) sglq_entry->sli4_lxritag = lxri; sglq_entry->sli4_xritag = phba->sli4_hba.xri_ids[lxri]; } + return 0; + +out_free_mem: + lpfc_free_els_sgl_list(phba); + return rc; +} + +/** + * lpfc_sli4_nvmet_sgl_update - update xri-sgl sizing and mapping + * @phba: pointer to lpfc hba data structure. + * + * This routine first calculates the sizes of the current els and allocated + * scsi sgl lists, and then goes through all sgls to updates the physical + * XRIs assigned due to port function reset. During port initialization, the + * current els and allocated scsi sgl lists are 0s. + * + * Return codes + * 0 - successful (for now, it always returns 0) + **/ +int +lpfc_sli4_nvmet_sgl_update(struct lpfc_hba *phba) +{ + struct lpfc_sglq *sglq_entry = NULL, *sglq_entry_next = NULL; + uint16_t i, lxri, xri_cnt, els_xri_cnt; + uint16_t nvmet_xri_cnt, tot_cnt; + LIST_HEAD(nvmet_sgl_list); + int rc; /* - * update on pci function's allocated scsi xri-sgl list + * update on pci function's nvmet xri-sgl list + */ + els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); + nvmet_xri_cnt = phba->cfg_nvmet_mrq * phba->cfg_nvmet_mrq_post; + tot_cnt = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; + if (nvmet_xri_cnt > tot_cnt) { + phba->cfg_nvmet_mrq_post = tot_cnt / phba->cfg_nvmet_mrq; + nvmet_xri_cnt = phba->cfg_nvmet_mrq * phba->cfg_nvmet_mrq_post; + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "6301 NVMET post-sgl count changed to %d\n", + phba->cfg_nvmet_mrq_post); + } + + if (nvmet_xri_cnt > phba->sli4_hba.nvmet_xri_cnt) { + /* els xri-sgl expanded */ + xri_cnt = nvmet_xri_cnt - phba->sli4_hba.nvmet_xri_cnt; + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "6302 NVMET xri-sgl cnt grew from %d to %d\n", + phba->sli4_hba.nvmet_xri_cnt, nvmet_xri_cnt); + /* allocate the additional nvmet sgls */ + for (i = 0; i < xri_cnt; i++) { + sglq_entry = kzalloc(sizeof(struct lpfc_sglq), + GFP_KERNEL); + if (sglq_entry == NULL) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "6303 Failure to allocate an " + "NVMET sgl entry:%d\n", i); + rc = -ENOMEM; + goto out_free_mem; + } + sglq_entry->buff_type = NVMET_BUFF_TYPE; + sglq_entry->virt = lpfc_nvmet_buf_alloc(phba, 0, + &sglq_entry->phys); + if (sglq_entry->virt == NULL) { + kfree(sglq_entry); + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "6304 Failure to allocate an " + "NVMET buf:%d\n", i); + rc = -ENOMEM; + goto out_free_mem; + } + sglq_entry->sgl = sglq_entry->virt; + memset(sglq_entry->sgl, 0, + phba->cfg_sg_dma_buf_size); + sglq_entry->state = SGL_FREED; + list_add_tail(&sglq_entry->list, &nvmet_sgl_list); + } + spin_lock_irq(&phba->hbalock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(&nvmet_sgl_list, + &phba->sli4_hba.lpfc_nvmet_sgl_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); + spin_unlock_irq(&phba->hbalock); + } else if (nvmet_xri_cnt < phba->sli4_hba.nvmet_xri_cnt) { + /* nvmet xri-sgl shrunk */ + xri_cnt = phba->sli4_hba.nvmet_xri_cnt - nvmet_xri_cnt; + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "6305 NVMET xri-sgl count decreased from " + "%d to %d\n", phba->sli4_hba.nvmet_xri_cnt, + nvmet_xri_cnt); + spin_lock_irq(&phba->hbalock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(&phba->sli4_hba.lpfc_nvmet_sgl_list, + &nvmet_sgl_list); + /* release extra nvmet sgls from list */ + for (i = 0; i < xri_cnt; i++) { + list_remove_head(&nvmet_sgl_list, + sglq_entry, struct lpfc_sglq, list); + if (sglq_entry) { + lpfc_nvmet_buf_free(phba, sglq_entry->virt, + sglq_entry->phys); + kfree(sglq_entry); + } + } + list_splice_init(&nvmet_sgl_list, + &phba->sli4_hba.lpfc_nvmet_sgl_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); + spin_unlock_irq(&phba->hbalock); + } else + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "6306 NVMET xri-sgl count unchanged: %d\n", + nvmet_xri_cnt); + phba->sli4_hba.nvmet_xri_cnt = nvmet_xri_cnt; + + /* update xris to nvmet sgls on the list */ + sglq_entry = NULL; + sglq_entry_next = NULL; + list_for_each_entry_safe(sglq_entry, sglq_entry_next, + &phba->sli4_hba.lpfc_nvmet_sgl_list, list) { + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "6307 Failed to allocate xri for " + "NVMET sgl\n"); + rc = -ENOMEM; + goto out_free_mem; + } + sglq_entry->sli4_lxritag = lxri; + sglq_entry->sli4_xritag = phba->sli4_hba.xri_ids[lxri]; + } + return 0; + +out_free_mem: + lpfc_free_nvmet_sgl_list(phba); + return rc; +} + +/** + * lpfc_sli4_scsi_sgl_update - update xri-sgl sizing and mapping + * @phba: pointer to lpfc hba data structure. + * + * This routine first calculates the sizes of the current els and allocated + * scsi sgl lists, and then goes through all sgls to updates the physical + * XRIs assigned due to port function reset. During port initialization, the + * current els and allocated scsi sgl lists are 0s. + * + * Return codes + * 0 - successful (for now, it always returns 0) + **/ +int +lpfc_sli4_scsi_sgl_update(struct lpfc_hba *phba) +{ + struct lpfc_scsi_buf *psb, *psb_next; + uint16_t i, lxri, els_xri_cnt, scsi_xri_cnt; + LIST_HEAD(scsi_sgl_list); + int rc; + + /* + * update on pci function's els xri-sgl list */ + els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); phba->total_scsi_bufs = 0; + /* + * update on pci function's allocated scsi xri-sgl list + */ /* maximum number of xris available for scsi buffers */ phba->sli4_hba.scsi_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; - lpfc_printf_log(phba, KERN_INFO, LOG_SLI, - "2401 Current allocated SCSI xri-sgl count:%d, " - "maximum SCSI xri count:%d\n", - phba->sli4_hba.scsi_xri_cnt, - phba->sli4_hba.scsi_xri_max); + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) + return 0; + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) + phba->sli4_hba.scsi_xri_max = /* Split them up */ + (phba->sli4_hba.scsi_xri_max * + phba->cfg_xri_split) / 100; spin_lock_irq(&phba->scsi_buf_list_get_lock); spin_lock(&phba->scsi_buf_list_put_lock); @@ -3283,7 +3517,7 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) list_remove_head(&scsi_sgl_list, psb, struct lpfc_scsi_buf, list); if (psb) { - pci_pool_free(phba->lpfc_scsi_dma_buf_pool, + pci_pool_free(phba->lpfc_sg_dma_buf_pool, psb->data, psb->dma_handle); kfree(psb); } @@ -3314,16 +3548,113 @@ lpfc_sli4_xri_sgl_update(struct lpfc_hba *phba) INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); spin_unlock(&phba->scsi_buf_list_put_lock); spin_unlock_irq(&phba->scsi_buf_list_get_lock); - return 0; out_free_mem: - lpfc_free_els_sgl_list(phba); lpfc_scsi_free(phba); return rc; } /** + * lpfc_sli4_nvme_sgl_update - update xri-sgl sizing and mapping + * @phba: pointer to lpfc hba data structure. + * + * This routine first calculates the sizes of the current els and allocated + * scsi sgl lists, and then goes through all sgls to updates the physical + * XRIs assigned due to port function reset. During port initialization, the + * current els and allocated scsi sgl lists are 0s. + * + * Return codes + * 0 - successful (for now, it always returns 0) + **/ +int +lpfc_sli4_nvme_sgl_update(struct lpfc_hba *phba) +{ + struct lpfc_nvme_buf *lpfc_ncmd = NULL, *lpfc_ncmd_next = NULL; + uint16_t i, lxri, els_xri_cnt; + uint16_t nvme_xri_cnt, nvme_xri_max; + LIST_HEAD(nvme_sgl_list); + int rc; + + phba->total_nvme_bufs = 0; + + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) + return 0; + /* + * update on pci function's allocated nvme xri-sgl list + */ + + /* maximum number of xris available for nvme buffers */ + els_xri_cnt = lpfc_sli4_get_els_iocb_cnt(phba); + nvme_xri_max = phba->sli4_hba.max_cfg_param.max_xri - els_xri_cnt; + phba->sli4_hba.nvme_xri_max = nvme_xri_max; + phba->sli4_hba.nvme_xri_max -= phba->sli4_hba.scsi_xri_max; + + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "6074 Current allocated NVME xri-sgl count:%d, " + "maximum NVME xri count:%d\n", + phba->sli4_hba.nvme_xri_cnt, + phba->sli4_hba.nvme_xri_max); + + spin_lock_irq(&phba->nvme_buf_list_get_lock); + spin_lock(&phba->nvme_buf_list_put_lock); + list_splice_init(&phba->lpfc_nvme_buf_list_get, &nvme_sgl_list); + list_splice(&phba->lpfc_nvme_buf_list_put, &nvme_sgl_list); + spin_unlock(&phba->nvme_buf_list_put_lock); + spin_unlock_irq(&phba->nvme_buf_list_get_lock); + + if (phba->sli4_hba.nvme_xri_cnt > phba->sli4_hba.nvme_xri_max) { + /* max nvme xri shrunk below the allocated nvme buffers */ + spin_lock_irq(&phba->nvme_buf_list_get_lock); + nvme_xri_cnt = phba->sli4_hba.nvme_xri_cnt - + phba->sli4_hba.nvme_xri_max; + spin_unlock_irq(&phba->nvme_buf_list_get_lock); + /* release the extra allocated nvme buffers */ + for (i = 0; i < nvme_xri_cnt; i++) { + list_remove_head(&nvme_sgl_list, lpfc_ncmd, + struct lpfc_nvme_buf, list); + if (lpfc_ncmd) { + pci_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, + lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + } + } + spin_lock_irq(&phba->nvme_buf_list_get_lock); + phba->sli4_hba.nvme_xri_cnt -= nvme_xri_cnt; + spin_unlock_irq(&phba->nvme_buf_list_get_lock); + } + + /* update xris associated to remaining allocated nvme buffers */ + lpfc_ncmd = NULL; + lpfc_ncmd_next = NULL; + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &nvme_sgl_list, list) { + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "6075 Failed to allocate xri for " + "nvme buffer\n"); + rc = -ENOMEM; + goto out_free_mem; + } + lpfc_ncmd->cur_iocbq.sli4_lxritag = lxri; + lpfc_ncmd->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; + } + spin_lock_irq(&phba->nvme_buf_list_get_lock); + spin_lock(&phba->nvme_buf_list_put_lock); + list_splice_init(&nvme_sgl_list, &phba->lpfc_nvme_buf_list_get); + INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put); + spin_unlock(&phba->nvme_buf_list_put_lock); + spin_unlock_irq(&phba->nvme_buf_list_get_lock); + return 0; + +out_free_mem: + lpfc_nvme_free(phba); + return rc; +} + +/** * lpfc_create_port - Create an FC port * @phba: pointer to lpfc hba data structure. * @instance: a unique integer ID to this FC port. @@ -3343,18 +3674,23 @@ struct lpfc_vport * lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) { struct lpfc_vport *vport; - struct Scsi_Host *shost; + struct Scsi_Host *shost = NULL; int error = 0; - if (dev != &phba->pcidev->dev) { - shost = scsi_host_alloc(&lpfc_vport_template, - sizeof(struct lpfc_vport)); - } else { - if (phba->sli_rev == LPFC_SLI_REV4) - shost = scsi_host_alloc(&lpfc_template, - sizeof(struct lpfc_vport)); - else - shost = scsi_host_alloc(&lpfc_template_s3, + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { + if (dev != &phba->pcidev->dev) { + shost = scsi_host_alloc(&lpfc_vport_template, + sizeof(struct lpfc_vport)); + } else { + if (phba->sli_rev == LPFC_SLI_REV4) + shost = scsi_host_alloc(&lpfc_template, + sizeof(struct lpfc_vport)); + else + shost = scsi_host_alloc(&lpfc_template_s3, + sizeof(struct lpfc_vport)); + } + } else if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + shost = scsi_host_alloc(&lpfc_template_nvme, sizeof(struct lpfc_vport)); } if (!shost) @@ -3365,8 +3701,8 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) vport->load_flag |= FC_LOADING; vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; vport->fc_rscn_flush = 0; - lpfc_get_vport_cfgparam(vport); + shost->unique_id = instance; shost->max_id = LPFC_MAX_TARGET; shost->max_lun = vport->cfg_max_luns; @@ -3944,7 +4280,7 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba, lpfc_els_flush_all_cmd(phba); /* Block ELS IOCBs until we have done process link event */ - phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT; + phba->sli4_hba.els_wq->pring->flag |= LPFC_STOP_IOCB_EVENT; /* Update link event statistics */ phba->sli.slistat.link_event++; @@ -4103,7 +4439,7 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc) lpfc_els_flush_all_cmd(phba); /* Block ELS IOCBs until we have done process link event */ - phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT; + phba->sli4_hba.els_wq->pring->flag |= LPFC_STOP_IOCB_EVENT; /* Update link event statistics */ phba->sli.slistat.link_event++; @@ -4272,13 +4608,13 @@ lpfc_sli4_async_sli_evt(struct lpfc_hba *phba, struct lpfc_acqe_sli *acqe_sli) sprintf(message, "Unqualified optics - Replace with " "Avago optics for Warranty and Technical " "Support - Link is%s operational", - (operational) ? "" : " not"); + (operational) ? " not" : ""); break; case LPFC_SLI_EVENT_STATUS_UNCERTIFIED: sprintf(message, "Uncertified optics - Replace with " "Avago-certified optics to enable link " "operation - Link is%s operational", - (operational) ? "" : " not"); + (operational) ? " not" : ""); break; default: /* firmware is reporting a status we don't know about */ @@ -5000,40 +5336,79 @@ lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *phba, int nr_vfn) } /** - * lpfc_sli_driver_resource_setup - Setup driver internal resources for SLI3 dev. + * lpfc_setup_driver_resource_phase1 - Phase1 etup driver internal resources. * @phba: pointer to lpfc hba data structure. * - * This routine is invoked to set up the driver internal resources specific to - * support the SLI-3 HBA device it attached to. + * This routine is invoked to set up the driver internal resources before the + * device specific resource setup to support the HBA device it attached to. * * Return codes - * 0 - successful - * other values - error + * 0 - successful + * other values - error **/ static int -lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) +lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) { - struct lpfc_sli *psli; - int rc; + struct lpfc_sli *psli = &phba->sli; /* - * Initialize timers used by driver + * Driver resources common to all SLI revisions */ + atomic_set(&phba->fast_event_count, 0); + spin_lock_init(&phba->hbalock); - /* Heartbeat timer */ - init_timer(&phba->hb_tmofunc); - phba->hb_tmofunc.function = lpfc_hb_timeout; - phba->hb_tmofunc.data = (unsigned long)phba; + /* Initialize ndlp management spinlock */ + spin_lock_init(&phba->ndlp_lock); + + INIT_LIST_HEAD(&phba->port_list); + INIT_LIST_HEAD(&phba->work_list); + init_waitqueue_head(&phba->wait_4_mlo_m_q); + + /* Initialize the wait queue head for the kernel thread */ + init_waitqueue_head(&phba->work_waitq); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "1403 Protocols supported %s %s %s\n", + ((phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ? + "SCSI" : " "), + ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) ? + "NVME" : " "), + (phba->nvmet_support ? "NVMET" : " ")); + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { + /* Initialize the scsi buffer list used by driver for scsi IO */ + spin_lock_init(&phba->scsi_buf_list_get_lock); + INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_get); + spin_lock_init(&phba->scsi_buf_list_put_lock); + INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); + } + + if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && + (phba->nvmet_support == 0)) { + /* Initialize the NVME buffer list used by driver for NVME IO */ + spin_lock_init(&phba->nvme_buf_list_get_lock); + INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_get); + spin_lock_init(&phba->nvme_buf_list_put_lock); + INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put); + } + + /* Initialize the fabric iocb list */ + INIT_LIST_HEAD(&phba->fabric_iocb_list); + + /* Initialize list to save ELS buffers */ + INIT_LIST_HEAD(&phba->elsbuf); + + /* Initialize FCF connection rec list */ + INIT_LIST_HEAD(&phba->fcf_conn_rec_list); + + /* Initialize OAS configuration list */ + spin_lock_init(&phba->devicelock); + INIT_LIST_HEAD(&phba->luns); - psli = &phba->sli; /* MBOX heartbeat timer */ init_timer(&psli->mbox_tmo); psli->mbox_tmo.function = lpfc_mbox_timeout; psli->mbox_tmo.data = (unsigned long) phba; - /* FCP polling mode timer */ - init_timer(&phba->fcp_poll_timer); - phba->fcp_poll_timer.function = lpfc_poll_timeout; - phba->fcp_poll_timer.data = (unsigned long) phba; /* Fabric block timer */ init_timer(&phba->fabric_block_timer); phba->fabric_block_timer.function = lpfc_fabric_block_timeout; @@ -5042,6 +5417,38 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) init_timer(&phba->eratt_poll); phba->eratt_poll.function = lpfc_poll_eratt; phba->eratt_poll.data = (unsigned long) phba; + /* Heartbeat timer */ + init_timer(&phba->hb_tmofunc); + phba->hb_tmofunc.function = lpfc_hb_timeout; + phba->hb_tmofunc.data = (unsigned long)phba; + + return 0; +} + +/** + * lpfc_sli_driver_resource_setup - Setup driver internal resources for SLI3 dev + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to set up the driver internal resources specific to + * support the SLI-3 HBA device it attached to. + * + * Return codes + * 0 - successful + * other values - error + **/ +static int +lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) +{ + int rc; + + /* + * Initialize timers used by driver + */ + + /* FCP polling mode timer */ + init_timer(&phba->fcp_poll_timer); + phba->fcp_poll_timer.function = lpfc_poll_timeout; + phba->fcp_poll_timer.data = (unsigned long) phba; /* Host attention work mask setup */ phba->work_ha_mask = (HA_ERATT | HA_MBATT | HA_LATT); @@ -5049,6 +5456,12 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) /* Get all the module params for configuring this host */ lpfc_get_cfgparam(phba); + /* Set up phase-1 common device driver resources */ + + rc = lpfc_setup_driver_resource_phase1(phba); + if (rc) + return -ENODEV; + if (phba->pcidev->device == PCI_DEVICE_ID_HORNET) { phba->menlo_flag |= HBA_MENLO_SUPPORT; /* check for menlo minimum sg count */ @@ -5056,10 +5469,10 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) phba->cfg_sg_seg_cnt = LPFC_DEFAULT_MENLO_SG_SEG_CNT; } - if (!phba->sli.ring) - phba->sli.ring = kzalloc(LPFC_SLI3_MAX_RING * + if (!phba->sli.sli3_ring) + phba->sli.sli3_ring = kzalloc(LPFC_SLI3_MAX_RING * sizeof(struct lpfc_sli_ring), GFP_KERNEL); - if (!phba->sli.ring) + if (!phba->sli.sli3_ring) return -ENOMEM; /* @@ -5118,7 +5531,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) * Initialize the SLI Layer to run with lpfc HBAs. */ lpfc_sli_setup(phba); - lpfc_sli_queue_setup(phba); + lpfc_sli_queue_init(phba); /* Allocate device driver memory */ if (lpfc_mem_alloc(phba, BPL_ALIGN_SZ)) @@ -5174,18 +5587,27 @@ lpfc_sli_driver_resource_unset(struct lpfc_hba *phba) static int lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) { - struct lpfc_vector_map_info *cpup; - struct lpfc_sli *psli; LPFC_MBOXQ_t *mboxq; - int rc, i, hbq_count, max_buf_size; + MAILBOX_t *mb; + int rc, i, max_buf_size; uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0}; struct lpfc_mqe *mqe; int longs; int fof_vectors = 0; + uint64_t wwn; + + phba->sli4_hba.num_online_cpu = num_online_cpus(); + phba->sli4_hba.num_present_cpu = lpfc_present_cpu; + phba->sli4_hba.curr_disp_cpu = 0; /* Get all the module params for configuring this host */ lpfc_get_cfgparam(phba); + /* Set up phase-1 common device driver resources */ + rc = lpfc_setup_driver_resource_phase1(phba); + if (rc) + return -ENODEV; + /* Before proceed, wait for POST done and device ready */ rc = lpfc_sli4_post_status_check(phba); if (rc) @@ -5195,27 +5617,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) * Initialize timers used by driver */ - /* Heartbeat timer */ - init_timer(&phba->hb_tmofunc); - phba->hb_tmofunc.function = lpfc_hb_timeout; - phba->hb_tmofunc.data = (unsigned long)phba; init_timer(&phba->rrq_tmr); phba->rrq_tmr.function = lpfc_rrq_timeout; phba->rrq_tmr.data = (unsigned long)phba; - psli = &phba->sli; - /* MBOX heartbeat timer */ - init_timer(&psli->mbox_tmo); - psli->mbox_tmo.function = lpfc_mbox_timeout; - psli->mbox_tmo.data = (unsigned long) phba; - /* Fabric block timer */ - init_timer(&phba->fabric_block_timer); - phba->fabric_block_timer.function = lpfc_fabric_block_timeout; - phba->fabric_block_timer.data = (unsigned long) phba; - /* EA polling mode timer */ - init_timer(&phba->eratt_poll); - phba->eratt_poll.function = lpfc_poll_eratt; - phba->eratt_poll.data = (unsigned long) phba; /* FCF rediscover timer */ init_timer(&phba->fcf.redisc_wait); phba->fcf.redisc_wait.function = lpfc_sli4_fcf_redisc_wait_tmo; @@ -5242,14 +5647,9 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* * For SLI4, instead of using ring 0 (LPFC_FCP_RING) for FCP commands - * we will associate a new ring, for each FCP fastpath EQ/CQ/WQ tuple. + * we will associate a new ring, for each EQ/CQ/WQ tuple. + * The WQ create will allocate the ring. */ - if (!phba->sli.ring) - phba->sli.ring = kzalloc( - (LPFC_SLI3_MAX_RING + phba->cfg_fcp_io_channel) * - sizeof(struct lpfc_sli_ring), GFP_KERNEL); - if (!phba->sli.ring) - return -ENOMEM; /* * It doesn't matter what family our adapter is in, we are @@ -5261,43 +5661,45 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) phba->cfg_sg_seg_cnt = LPFC_MAX_SGL_SEG_CNT - 2; /* - * Since lpfc_sg_seg_cnt is module parameter, the sg_dma_buf_size - * used to create the sg_dma_buf_pool must be dynamically calculated. + * Since lpfc_sg_seg_cnt is module param, the sg_dma_buf_size + * used to create the sg_dma_buf_pool must be calculated. */ - if (phba->cfg_enable_bg) { /* - * The scsi_buf for a T10-DIF I/O will hold the FCP cmnd, - * the FCP rsp, and a SGE for each. Sice we have no control - * over how many protection data segments the SCSI Layer + * The scsi_buf for a T10-DIF I/O holds the FCP cmnd, + * the FCP rsp, and a SGE. Sice we have no control + * over how many protection segments the SCSI Layer * will hand us (ie: there could be one for every block - * in the IO), we just allocate enough SGEs to accomidate - * our max amount and we need to limit lpfc_sg_seg_cnt to - * minimize the risk of running out. + * in the IO), just allocate enough SGEs to accomidate + * our max amount and we need to limit lpfc_sg_seg_cnt + * to minimize the risk of running out. */ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) + - sizeof(struct fcp_rsp) + max_buf_size; + sizeof(struct fcp_rsp) + max_buf_size; /* Total SGEs for scsi_sg_list and scsi_sg_prot_list */ phba->cfg_total_seg_cnt = LPFC_MAX_SGL_SEG_CNT; if (phba->cfg_sg_seg_cnt > LPFC_MAX_SG_SLI4_SEG_CNT_DIF) - phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SLI4_SEG_CNT_DIF; + phba->cfg_sg_seg_cnt = + LPFC_MAX_SG_SLI4_SEG_CNT_DIF; } else { /* - * The scsi_buf for a regular I/O will hold the FCP cmnd, + * The scsi_buf for a regular I/O holds the FCP cmnd, * the FCP rsp, a SGE for each, and a SGE for up to * cfg_sg_seg_cnt data segments. */ phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) + - sizeof(struct fcp_rsp) + - ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge)); + sizeof(struct fcp_rsp) + + ((phba->cfg_sg_seg_cnt + 2) * + sizeof(struct sli4_sge)); /* Total SGEs for scsi_sg_list */ phba->cfg_total_seg_cnt = phba->cfg_sg_seg_cnt + 2; + /* - * NOTE: if (phba->cfg_sg_seg_cnt + 2) <= 256 we only need - * to post 1 page for the SGL. + * NOTE: if (phba->cfg_sg_seg_cnt + 2) <= 256 we only + * need to post 1 page for the SGL. */ } @@ -5317,21 +5719,28 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) phba->cfg_total_seg_cnt); /* Initialize buffer queue management fields */ - hbq_count = lpfc_sli_hbq_count(); - for (i = 0; i < hbq_count; ++i) - INIT_LIST_HEAD(&phba->hbqs[i].hbq_buffer_list); - INIT_LIST_HEAD(&phba->rb_pend_list); + INIT_LIST_HEAD(&phba->hbqs[LPFC_ELS_HBQ].hbq_buffer_list); phba->hbqs[LPFC_ELS_HBQ].hbq_alloc_buffer = lpfc_sli4_rb_alloc; phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer = lpfc_sli4_rb_free; /* * Initialize the SLI Layer to run with lpfc SLI4 HBAs. */ - /* Initialize the Abort scsi buffer list used by driver */ - spin_lock_init(&phba->sli4_hba.abts_scsi_buf_list_lock); - INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_scsi_buf_list); + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { + /* Initialize the Abort scsi buffer list used by driver */ + spin_lock_init(&phba->sli4_hba.abts_scsi_buf_list_lock); + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_scsi_buf_list); + } + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + /* Initialize the Abort nvme buffer list used by driver */ + spin_lock_init(&phba->sli4_hba.abts_nvme_buf_list_lock); + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvme_buf_list); + } + /* This abort list used by worker thread */ - spin_lock_init(&phba->sli4_hba.abts_sgl_list_lock); + spin_lock_init(&phba->sli4_hba.sgl_list_lock); + spin_lock_init(&phba->sli4_hba.nvmet_io_lock); /* * Initialize driver internal slow-path work queues @@ -5359,10 +5768,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* initialize optic_state to 0xFF */ phba->sli4_hba.lnk_info.optic_state = 0xff; - /* Initialize the driver internal SLI layer lists. */ - lpfc_sli_setup(phba); - lpfc_sli_queue_setup(phba); - /* Allocate device driver memory */ rc = lpfc_mem_alloc(phba, SGL_ALIGN_SZ); if (rc) @@ -5372,8 +5777,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) == LPFC_SLI_INTF_IF_TYPE_2) { rc = lpfc_pci_function_reset(phba); - if (unlikely(rc)) - return -ENODEV; + if (unlikely(rc)) { + rc = -ENODEV; + goto out_free_mem; + } phba->temp_sensor_support = 1; } @@ -5410,6 +5817,46 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_bsmbx; } + /* Check for NVMET being configured */ + phba->nvmet_support = 0; + if (lpfc_enable_nvmet_cnt) { + + /* First get WWN of HBA instance */ + lpfc_read_nv(phba, mboxq); + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + if (rc != MBX_SUCCESS) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "6016 Mailbox failed , mbxCmd x%x " + "READ_NV, mbxStatus x%x\n", + bf_get(lpfc_mqe_command, &mboxq->u.mqe), + bf_get(lpfc_mqe_status, &mboxq->u.mqe)); + rc = -EIO; + goto out_free_bsmbx; + } + mb = &mboxq->u.mb; + memcpy(&wwn, (char *)mb->un.varRDnvp.nodename, + sizeof(uint64_t)); + wwn = cpu_to_be64(wwn); + phba->sli4_hba.wwnn.u.name = wwn; + memcpy(&wwn, (char *)mb->un.varRDnvp.portname, + sizeof(uint64_t)); + /* wwn is WWPN of HBA instance */ + wwn = cpu_to_be64(wwn); + phba->sli4_hba.wwpn.u.name = wwn; + + /* Check to see if it matches any module parameter */ + for (i = 0; i < lpfc_enable_nvmet_cnt; i++) { + if (wwn == lpfc_enable_nvmet[i]) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6017 NVME Target %016llx\n", + wwn); + phba->nvmet_support = 1; /* a match */ + } + } + } + + lpfc_nvme_mod_param_dep(phba); + /* Get the Supported Pages if PORT_CAPABILITIES is supported by port. */ lpfc_supported_pages(mboxq); rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); @@ -5448,9 +5895,11 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2999 Unsupported SLI4 Parameters " "Extents and RPI headers enabled.\n"); - goto out_free_bsmbx; } + mempool_free(mboxq, phba->mbox_mem_pool); + goto out_free_bsmbx; } + mempool_free(mboxq, phba->mbox_mem_pool); /* Verify OAS is supported */ @@ -5497,11 +5946,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_remove_rpi_hdrs; } - phba->sli4_hba.fcp_eq_hdl = - kzalloc((sizeof(struct lpfc_fcp_eq_hdl) * - (fof_vectors + phba->cfg_fcp_io_channel)), - GFP_KERNEL); - if (!phba->sli4_hba.fcp_eq_hdl) { + phba->sli4_hba.hba_eq_hdl = kcalloc(fof_vectors + phba->io_channel_irqs, + sizeof(struct lpfc_hba_eq_hdl), + GFP_KERNEL); + if (!phba->sli4_hba.hba_eq_hdl) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2572 Failed allocate memory for " "fast-path per-EQ handle array\n"); @@ -5509,52 +5957,31 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_fcf_rr_bmask; } - phba->sli4_hba.msix_entries = kzalloc((sizeof(struct msix_entry) * - (fof_vectors + - phba->cfg_fcp_io_channel)), GFP_KERNEL); - if (!phba->sli4_hba.msix_entries) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2573 Failed allocate memory for msi-x " - "interrupt vector entries\n"); - rc = -ENOMEM; - goto out_free_fcp_eq_hdl; - } - - phba->sli4_hba.cpu_map = kzalloc((sizeof(struct lpfc_vector_map_info) * - phba->sli4_hba.num_present_cpu), - GFP_KERNEL); + phba->sli4_hba.cpu_map = kcalloc(phba->sli4_hba.num_present_cpu, + sizeof(struct lpfc_vector_map_info), + GFP_KERNEL); if (!phba->sli4_hba.cpu_map) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3327 Failed allocate memory for msi-x " "interrupt vector mapping\n"); rc = -ENOMEM; - goto out_free_msix; + goto out_free_hba_eq_hdl; } if (lpfc_used_cpu == NULL) { - lpfc_used_cpu = kzalloc((sizeof(uint16_t) * lpfc_present_cpu), - GFP_KERNEL); + lpfc_used_cpu = kcalloc(lpfc_present_cpu, sizeof(uint16_t), + GFP_KERNEL); if (!lpfc_used_cpu) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3335 Failed allocate memory for msi-x " "interrupt vector mapping\n"); kfree(phba->sli4_hba.cpu_map); rc = -ENOMEM; - goto out_free_msix; + goto out_free_hba_eq_hdl; } for (i = 0; i < lpfc_present_cpu; i++) lpfc_used_cpu[i] = LPFC_VECTOR_MAP_EMPTY; } - /* Initialize io channels for round robin */ - cpup = phba->sli4_hba.cpu_map; - rc = 0; - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { - cpup->channel_id = rc; - rc++; - if (rc >= phba->cfg_fcp_io_channel) - rc = 0; - } - /* * Enable sr-iov virtual functions if supported and configured * through the module parameter. @@ -5574,10 +6001,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) return 0; -out_free_msix: - kfree(phba->sli4_hba.msix_entries); -out_free_fcp_eq_hdl: - kfree(phba->sli4_hba.fcp_eq_hdl); +out_free_hba_eq_hdl: + kfree(phba->sli4_hba.hba_eq_hdl); out_free_fcf_rr_bmask: kfree(phba->fcf.fcf_rr_bmask); out_remove_rpi_hdrs: @@ -5611,11 +6036,8 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) phba->sli4_hba.num_online_cpu = 0; phba->sli4_hba.curr_disp_cpu = 0; - /* Free memory allocated for msi-x interrupt vector entries */ - kfree(phba->sli4_hba.msix_entries); - /* Free memory allocated for fast-path work queue handles */ - kfree(phba->sli4_hba.fcp_eq_hdl); + kfree(phba->sli4_hba.hba_eq_hdl); /* Free the allocated rpi headers. */ lpfc_sli4_remove_rpi_hdrs(phba); @@ -5627,6 +6049,7 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba) /* Free the ELS sgl list */ lpfc_free_active_sgl(phba); lpfc_free_els_sgl_list(phba); + lpfc_free_nvmet_sgl_list(phba); /* Free the completion queue EQ event pool */ lpfc_sli4_cq_event_release_all(phba); @@ -5689,58 +6112,6 @@ lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) } /** - * lpfc_setup_driver_resource_phase1 - Phase1 etup driver internal resources. - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to set up the driver internal resources before the - * device specific resource setup to support the HBA device it attached to. - * - * Return codes - * 0 - successful - * other values - error - **/ -static int -lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) -{ - /* - * Driver resources common to all SLI revisions - */ - atomic_set(&phba->fast_event_count, 0); - spin_lock_init(&phba->hbalock); - - /* Initialize ndlp management spinlock */ - spin_lock_init(&phba->ndlp_lock); - - INIT_LIST_HEAD(&phba->port_list); - INIT_LIST_HEAD(&phba->work_list); - init_waitqueue_head(&phba->wait_4_mlo_m_q); - - /* Initialize the wait queue head for the kernel thread */ - init_waitqueue_head(&phba->work_waitq); - - /* Initialize the scsi buffer list used by driver for scsi IO */ - spin_lock_init(&phba->scsi_buf_list_get_lock); - INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_get); - spin_lock_init(&phba->scsi_buf_list_put_lock); - INIT_LIST_HEAD(&phba->lpfc_scsi_buf_list_put); - - /* Initialize the fabric iocb list */ - INIT_LIST_HEAD(&phba->fabric_iocb_list); - - /* Initialize list to save ELS buffers */ - INIT_LIST_HEAD(&phba->elsbuf); - - /* Initialize FCF connection rec list */ - INIT_LIST_HEAD(&phba->fcf_conn_rec_list); - - /* Initialize OAS configuration list */ - spin_lock_init(&phba->devicelock); - INIT_LIST_HEAD(&phba->luns); - - return 0; -} - -/** * lpfc_setup_driver_resource_phase2 - Phase2 setup driver internal resources. * @phba: pointer to lpfc hba data structure. * @@ -5887,13 +6258,12 @@ static void lpfc_free_els_sgl_list(struct lpfc_hba *phba) { LIST_HEAD(sglq_list); - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; /* Retrieve all els sgls from driver list */ spin_lock_irq(&phba->hbalock); - spin_lock(&pring->ring_lock); - list_splice_init(&phba->sli4_hba.lpfc_sgl_list, &sglq_list); - spin_unlock(&pring->ring_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(&phba->sli4_hba.lpfc_els_sgl_list, &sglq_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irq(&phba->hbalock); /* Now free the sgl list */ @@ -5901,6 +6271,33 @@ lpfc_free_els_sgl_list(struct lpfc_hba *phba) } /** + * lpfc_free_nvmet_sgl_list - Free nvmet sgl list. + * @phba: pointer to lpfc hba data structure. + * + * This routine is invoked to free the driver's nvmet sgl list and memory. + **/ +static void +lpfc_free_nvmet_sgl_list(struct lpfc_hba *phba) +{ + struct lpfc_sglq *sglq_entry = NULL, *sglq_next = NULL; + LIST_HEAD(sglq_list); + + /* Retrieve all nvmet sgls from driver list */ + spin_lock_irq(&phba->hbalock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(&phba->sli4_hba.lpfc_nvmet_sgl_list, &sglq_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); + spin_unlock_irq(&phba->hbalock); + + /* Now free the sgl list */ + list_for_each_entry_safe(sglq_entry, sglq_next, &sglq_list, list) { + list_del(&sglq_entry->list); + lpfc_nvmet_buf_free(phba, sglq_entry->virt, sglq_entry->phys); + kfree(sglq_entry); + } +} + +/** * lpfc_init_active_sgl_array - Allocate the buf to track active ELS XRIs. * @phba: pointer to lpfc hba data structure. * @@ -5947,14 +6344,19 @@ static void lpfc_init_sgl_list(struct lpfc_hba *phba) { /* Initialize and populate the sglq list per host/VF. */ - INIT_LIST_HEAD(&phba->sli4_hba.lpfc_sgl_list); + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_els_sgl_list); INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_els_sgl_list); + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_nvmet_sgl_list); + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_abts_nvmet_sgl_list); /* els xri-sgl book keeping */ phba->sli4_hba.els_xri_cnt = 0; /* scsi xri-buffer book keeping */ phba->sli4_hba.scsi_xri_cnt = 0; + + /* nvme xri-buffer book keeping */ + phba->sli4_hba.nvme_xri_cnt = 0; } /** @@ -6185,9 +6587,9 @@ lpfc_hba_free(struct lpfc_hba *phba) /* Release the driver assigned board number */ idr_remove(&lpfc_hba_index, phba->brd_no); - /* Free memory allocated with sli rings */ - kfree(phba->sli.ring); - phba->sli.ring = NULL; + /* Free memory allocated with sli3 rings */ + kfree(phba->sli.sli3_ring); + phba->sli.sli3_ring = NULL; kfree(phba); return; @@ -6223,6 +6625,23 @@ lpfc_create_shost(struct lpfc_hba *phba) shost = lpfc_shost_from_vport(vport); phba->pport = vport; + + if (phba->nvmet_support) { + /* Only 1 vport (pport) will support NVME target */ + if (phba->txrdy_payload_pool == NULL) { + phba->txrdy_payload_pool = pci_pool_create( + "txrdy_pool", phba->pcidev, + TXRDY_PAYLOAD_LEN, 16, 0); + if (phba->txrdy_payload_pool) { + phba->targetport = NULL; + phba->cfg_enable_fc4_type = LPFC_ENABLE_NVME; + lpfc_printf_log(phba, KERN_INFO, + LOG_INIT | LOG_NVME_DISC, + "6076 NVME Target Found\n"); + } + } + } + lpfc_debugfs_initialize(vport); /* Put reference to SCSI host to driver's device private data */ pci_set_drvdata(phba->pcidev, shost); @@ -6504,8 +6923,6 @@ lpfc_sli_pci_mem_setup(struct lpfc_hba *phba) memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size()); - INIT_LIST_HEAD(&phba->rb_pend_list); - phba->MBslimaddr = phba->slim_memmap_p; phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET; phba->CAregaddr = phba->ctrl_regs_memmap_p + CA_REG_OFFSET; @@ -7009,7 +7426,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) "VPI(B:%d M:%d) " "VFI(B:%d M:%d) " "RPI(B:%d M:%d) " - "FCFI(Count:%d)\n", + "FCFI:%d EQ:%d CQ:%d WQ:%d RQ:%d\n", phba->sli4_hba.extents_in_use, phba->sli4_hba.max_cfg_param.xri_base, phba->sli4_hba.max_cfg_param.max_xri, @@ -7019,7 +7436,12 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->sli4_hba.max_cfg_param.max_vfi, phba->sli4_hba.max_cfg_param.rpi_base, phba->sli4_hba.max_cfg_param.max_rpi, - phba->sli4_hba.max_cfg_param.max_fcfi); + phba->sli4_hba.max_cfg_param.max_fcfi, + phba->sli4_hba.max_cfg_param.max_eq, + phba->sli4_hba.max_cfg_param.max_cq, + phba->sli4_hba.max_cfg_param.max_wq, + phba->sli4_hba.max_cfg_param.max_rq); + } if (rc) @@ -7210,11 +7632,11 @@ lpfc_setup_endian_order(struct lpfc_hba *phba) } /** - * lpfc_sli4_queue_verify - Verify and update EQ and CQ counts + * lpfc_sli4_queue_verify - Verify and update EQ counts * @phba: pointer to lpfc hba data structure. * - * This routine is invoked to check the user settable queue counts for EQs and - * CQs. after this routine is called the counts will be set to valid values that + * This routine is invoked to check the user settable queue counts for EQs. + * After this routine is called the counts will be set to valid values that * adhere to the constraints of the system's interrupt vectors and the port's * queue resources. * @@ -7225,9 +7647,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba) static int lpfc_sli4_queue_verify(struct lpfc_hba *phba) { - int cfg_fcp_io_channel; - uint32_t cpu; - uint32_t i = 0; + int io_channel; int fof_vectors = phba->cfg_fof ? 1 : 0; /* @@ -7236,49 +7656,40 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) */ /* Sanity check on HBA EQ parameters */ - cfg_fcp_io_channel = phba->cfg_fcp_io_channel; - - /* It doesn't make sense to have more io channels then online CPUs */ - for_each_present_cpu(cpu) { - if (cpu_online(cpu)) - i++; - } - phba->sli4_hba.num_online_cpu = i; - phba->sli4_hba.num_present_cpu = lpfc_present_cpu; - phba->sli4_hba.curr_disp_cpu = 0; + io_channel = phba->io_channel_irqs; - if (i < cfg_fcp_io_channel) { + if (phba->sli4_hba.num_online_cpu < io_channel) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3188 Reducing IO channels to match number of " "online CPUs: from %d to %d\n", - cfg_fcp_io_channel, i); - cfg_fcp_io_channel = i; + io_channel, phba->sli4_hba.num_online_cpu); + io_channel = phba->sli4_hba.num_online_cpu; } - if (cfg_fcp_io_channel + fof_vectors > - phba->sli4_hba.max_cfg_param.max_eq) { - if (phba->sli4_hba.max_cfg_param.max_eq < - LPFC_FCP_IO_CHAN_MIN) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2574 Not enough EQs (%d) from the " - "pci function for supporting FCP " - "EQs (%d)\n", - phba->sli4_hba.max_cfg_param.max_eq, - phba->cfg_fcp_io_channel); - goto out_error; - } + if (io_channel + fof_vectors > phba->sli4_hba.max_cfg_param.max_eq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2575 Reducing IO channels to match number of " "available EQs: from %d to %d\n", - cfg_fcp_io_channel, + io_channel, phba->sli4_hba.max_cfg_param.max_eq); - cfg_fcp_io_channel = phba->sli4_hba.max_cfg_param.max_eq - - fof_vectors; + io_channel = phba->sli4_hba.max_cfg_param.max_eq - fof_vectors; } - /* The actual number of FCP event queues adopted */ - phba->cfg_fcp_io_channel = cfg_fcp_io_channel; + /* The actual number of FCP / NVME event queues adopted */ + if (io_channel != phba->io_channel_irqs) + phba->io_channel_irqs = io_channel; + if (phba->cfg_fcp_io_channel > io_channel) + phba->cfg_fcp_io_channel = io_channel; + if (phba->cfg_nvme_io_channel > io_channel) + phba->cfg_nvme_io_channel = io_channel; + if (phba->cfg_nvme_io_channel < phba->cfg_nvmet_mrq) + phba->cfg_nvmet_mrq = phba->cfg_nvme_io_channel; + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2574 IO channels: irqs %d fcp %d nvme %d MRQ: %d\n", + phba->io_channel_irqs, phba->cfg_fcp_io_channel, + phba->cfg_nvme_io_channel, phba->cfg_nvmet_mrq); /* Get EQ depth from module parameter, fake the default for now */ phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B; @@ -7287,10 +7698,67 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) /* Get CQ depth from module parameter, fake the default for now */ phba->sli4_hba.cq_esize = LPFC_CQE_SIZE; phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT; + return 0; +} +static int +lpfc_alloc_nvme_wq_cq(struct lpfc_hba *phba, int wqidx) +{ + struct lpfc_queue *qdesc; + int cnt; + + qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize, + phba->sli4_hba.cq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0508 Failed allocate fast-path NVME CQ (%d)\n", + wqidx); + return 1; + } + phba->sli4_hba.nvme_cq[wqidx] = qdesc; + + cnt = LPFC_NVME_WQSIZE; + qdesc = lpfc_sli4_queue_alloc(phba, LPFC_WQE128_SIZE, cnt); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0509 Failed allocate fast-path NVME WQ (%d)\n", + wqidx); + return 1; + } + phba->sli4_hba.nvme_wq[wqidx] = qdesc; + list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); + return 0; +} + +static int +lpfc_alloc_fcp_wq_cq(struct lpfc_hba *phba, int wqidx) +{ + struct lpfc_queue *qdesc; + uint32_t wqesize; + + /* Create Fast Path FCP CQs */ + qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize, + phba->sli4_hba.cq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0499 Failed allocate fast-path FCP CQ (%d)\n", wqidx); + return 1; + } + phba->sli4_hba.fcp_cq[wqidx] = qdesc; + + /* Create Fast Path FCP WQs */ + wqesize = (phba->fcp_embed_io) ? + LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize; + qdesc = lpfc_sli4_queue_alloc(phba, wqesize, phba->sli4_hba.wq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0503 Failed allocate fast-path FCP WQ (%d)\n", + wqidx); + return 1; + } + phba->sli4_hba.fcp_wq[wqidx] = qdesc; + list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); return 0; -out_error: - return -ENOMEM; } /** @@ -7311,13 +7779,14 @@ int lpfc_sli4_queue_create(struct lpfc_hba *phba) { struct lpfc_queue *qdesc; - uint32_t wqesize; - int idx; + int idx, io_channel, max; /* * Create HBA Record arrays. + * Both NVME and FCP will share that same vectors / EQs */ - if (!phba->cfg_fcp_io_channel) + io_channel = phba->io_channel_irqs; + if (!io_channel) return -ERANGE; phba->sli4_hba.mq_esize = LPFC_MQE_SIZE; @@ -7326,9 +7795,14 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) phba->sli4_hba.wq_ecount = LPFC_WQE_DEF_COUNT; phba->sli4_hba.rq_esize = LPFC_RQE_SIZE; phba->sli4_hba.rq_ecount = LPFC_RQE_DEF_COUNT; + phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B; + phba->sli4_hba.eq_ecount = LPFC_EQE_DEF_COUNT; + phba->sli4_hba.cq_esize = LPFC_CQE_SIZE; + phba->sli4_hba.cq_ecount = LPFC_CQE_DEF_COUNT; - phba->sli4_hba.hba_eq = kzalloc((sizeof(struct lpfc_queue *) * - phba->cfg_fcp_io_channel), GFP_KERNEL); + phba->sli4_hba.hba_eq = kcalloc(io_channel, + sizeof(struct lpfc_queue *), + GFP_KERNEL); if (!phba->sli4_hba.hba_eq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2576 Failed allocate memory for " @@ -7336,44 +7810,115 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) goto out_error; } - phba->sli4_hba.fcp_cq = kzalloc((sizeof(struct lpfc_queue *) * - phba->cfg_fcp_io_channel), GFP_KERNEL); - if (!phba->sli4_hba.fcp_cq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2577 Failed allocate memory for fast-path " - "CQ record array\n"); - goto out_error; + if (phba->cfg_fcp_io_channel) { + phba->sli4_hba.fcp_cq = kcalloc(phba->cfg_fcp_io_channel, + sizeof(struct lpfc_queue *), + GFP_KERNEL); + if (!phba->sli4_hba.fcp_cq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2577 Failed allocate memory for " + "fast-path CQ record array\n"); + goto out_error; + } + phba->sli4_hba.fcp_wq = kcalloc(phba->cfg_fcp_io_channel, + sizeof(struct lpfc_queue *), + GFP_KERNEL); + if (!phba->sli4_hba.fcp_wq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2578 Failed allocate memory for " + "fast-path FCP WQ record array\n"); + goto out_error; + } + /* + * Since the first EQ can have multiple CQs associated with it, + * this array is used to quickly see if we have a FCP fast-path + * CQ match. + */ + phba->sli4_hba.fcp_cq_map = kcalloc(phba->cfg_fcp_io_channel, + sizeof(uint16_t), + GFP_KERNEL); + if (!phba->sli4_hba.fcp_cq_map) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2545 Failed allocate memory for " + "fast-path CQ map\n"); + goto out_error; + } } - phba->sli4_hba.fcp_wq = kzalloc((sizeof(struct lpfc_queue *) * - phba->cfg_fcp_io_channel), GFP_KERNEL); - if (!phba->sli4_hba.fcp_wq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2578 Failed allocate memory for fast-path " - "WQ record array\n"); - goto out_error; - } + if (phba->cfg_nvme_io_channel) { + phba->sli4_hba.nvme_cq = kcalloc(phba->cfg_nvme_io_channel, + sizeof(struct lpfc_queue *), + GFP_KERNEL); + if (!phba->sli4_hba.nvme_cq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6077 Failed allocate memory for " + "fast-path CQ record array\n"); + goto out_error; + } - /* - * Since the first EQ can have multiple CQs associated with it, - * this array is used to quickly see if we have a FCP fast-path - * CQ match. - */ - phba->sli4_hba.fcp_cq_map = kzalloc((sizeof(uint16_t) * - phba->cfg_fcp_io_channel), GFP_KERNEL); - if (!phba->sli4_hba.fcp_cq_map) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "2545 Failed allocate memory for fast-path " - "CQ map\n"); - goto out_error; + phba->sli4_hba.nvme_wq = kcalloc(phba->cfg_nvme_io_channel, + sizeof(struct lpfc_queue *), + GFP_KERNEL); + if (!phba->sli4_hba.nvme_wq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2581 Failed allocate memory for " + "fast-path NVME WQ record array\n"); + goto out_error; + } + + /* + * Since the first EQ can have multiple CQs associated with it, + * this array is used to quickly see if we have a NVME fast-path + * CQ match. + */ + phba->sli4_hba.nvme_cq_map = kcalloc(phba->cfg_nvme_io_channel, + sizeof(uint16_t), + GFP_KERNEL); + if (!phba->sli4_hba.nvme_cq_map) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6078 Failed allocate memory for " + "fast-path CQ map\n"); + goto out_error; + } + + if (phba->nvmet_support) { + phba->sli4_hba.nvmet_cqset = kcalloc( + phba->cfg_nvmet_mrq, + sizeof(struct lpfc_queue *), + GFP_KERNEL); + if (!phba->sli4_hba.nvmet_cqset) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3121 Fail allocate memory for " + "fast-path CQ set array\n"); + goto out_error; + } + phba->sli4_hba.nvmet_mrq_hdr = kcalloc( + phba->cfg_nvmet_mrq, + sizeof(struct lpfc_queue *), + GFP_KERNEL); + if (!phba->sli4_hba.nvmet_mrq_hdr) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3122 Fail allocate memory for " + "fast-path RQ set hdr array\n"); + goto out_error; + } + phba->sli4_hba.nvmet_mrq_data = kcalloc( + phba->cfg_nvmet_mrq, + sizeof(struct lpfc_queue *), + GFP_KERNEL); + if (!phba->sli4_hba.nvmet_mrq_data) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3124 Fail allocate memory for " + "fast-path RQ set data array\n"); + goto out_error; + } + } } - /* - * Create HBA Event Queues (EQs). The cfg_fcp_io_channel specifies - * how many EQs to create. - */ - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) { + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_wq_list); + /* Create HBA Event Queues (EQs) */ + for (idx = 0; idx < io_channel; idx++) { /* Create EQs */ qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.eq_esize, phba->sli4_hba.eq_ecount); @@ -7383,33 +7928,42 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) goto out_error; } phba->sli4_hba.hba_eq[idx] = qdesc; + } - /* Create Fast Path FCP CQs */ - qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize, - phba->sli4_hba.cq_ecount); - if (!qdesc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0499 Failed allocate fast-path FCP " - "CQ (%d)\n", idx); + /* FCP and NVME io channels are not required to be balanced */ + + for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) + if (lpfc_alloc_fcp_wq_cq(phba, idx)) goto out_error; - } - phba->sli4_hba.fcp_cq[idx] = qdesc; - /* Create Fast Path FCP WQs */ - wqesize = (phba->fcp_embed_io) ? - LPFC_WQE128_SIZE : phba->sli4_hba.wq_esize; - qdesc = lpfc_sli4_queue_alloc(phba, wqesize, - phba->sli4_hba.wq_ecount); - if (!qdesc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0503 Failed allocate fast-path FCP " - "WQ (%d)\n", idx); + for (idx = 0; idx < phba->cfg_nvme_io_channel; idx++) + if (lpfc_alloc_nvme_wq_cq(phba, idx)) + goto out_error; + + /* allocate MRQ CQs */ + max = phba->cfg_nvme_io_channel; + if (max < phba->cfg_nvmet_mrq) + max = phba->cfg_nvmet_mrq; + + for (idx = 0; idx < max; idx++) + if (lpfc_alloc_nvme_wq_cq(phba, idx)) goto out_error; + + if (phba->nvmet_support) { + for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) { + qdesc = lpfc_sli4_queue_alloc(phba, + phba->sli4_hba.cq_esize, + phba->sli4_hba.cq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3142 Failed allocate NVME " + "CQ Set (%d)\n", idx); + goto out_error; + } + phba->sli4_hba.nvmet_cqset[idx] = qdesc; } - phba->sli4_hba.fcp_wq[idx] = qdesc; } - /* * Create Slow Path Completion Queues (CQs) */ @@ -7463,6 +8017,30 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) goto out_error; } phba->sli4_hba.els_wq = qdesc; + list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + /* Create NVME LS Complete Queue */ + qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize, + phba->sli4_hba.cq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6079 Failed allocate NVME LS CQ\n"); + goto out_error; + } + phba->sli4_hba.nvmels_cq = qdesc; + + /* Create NVME LS Work Queue */ + qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.wq_esize, + phba->sli4_hba.wq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6080 Failed allocate NVME LS WQ\n"); + goto out_error; + } + phba->sli4_hba.nvmels_wq = qdesc; + list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); + } /* * Create Receive Queue (RQ) @@ -7488,6 +8066,44 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) } phba->sli4_hba.dat_rq = qdesc; + if (phba->nvmet_support) { + for (idx = 0; idx < phba->cfg_nvmet_mrq; idx++) { + /* Create NVMET Receive Queue for header */ + qdesc = lpfc_sli4_queue_alloc(phba, + phba->sli4_hba.rq_esize, + phba->sli4_hba.rq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3146 Failed allocate " + "receive HRQ\n"); + goto out_error; + } + phba->sli4_hba.nvmet_mrq_hdr[idx] = qdesc; + + /* Only needed for header of RQ pair */ + qdesc->rqbp = kzalloc(sizeof(struct lpfc_rqb), + GFP_KERNEL); + if (qdesc->rqbp == NULL) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6131 Failed allocate " + "Header RQBP\n"); + goto out_error; + } + + /* Create NVMET Receive Queue for data */ + qdesc = lpfc_sli4_queue_alloc(phba, + phba->sli4_hba.rq_esize, + phba->sli4_hba.rq_ecount); + if (!qdesc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3156 Failed allocate " + "receive DRQ\n"); + goto out_error; + } + phba->sli4_hba.nvmet_mrq_data[idx] = qdesc; + } + } + /* Create the Queues needed for Flash Optimized Fabric operations */ if (phba->cfg_fof) lpfc_fof_queue_create(phba); @@ -7498,6 +8114,39 @@ out_error: return -ENOMEM; } +static inline void +__lpfc_sli4_release_queue(struct lpfc_queue **qp) +{ + if (*qp != NULL) { + lpfc_sli4_queue_free(*qp); + *qp = NULL; + } +} + +static inline void +lpfc_sli4_release_queues(struct lpfc_queue ***qs, int max) +{ + int idx; + + if (*qs == NULL) + return; + + for (idx = 0; idx < max; idx++) + __lpfc_sli4_release_queue(&(*qs)[idx]); + + kfree(*qs); + *qs = NULL; +} + +static inline void +lpfc_sli4_release_queue_map(uint16_t **qmap) +{ + if (*qmap != NULL) { + kfree(*qmap); + *qmap = NULL; + } +} + /** * lpfc_sli4_queue_destroy - Destroy all the SLI4 queues * @phba: pointer to lpfc hba data structure. @@ -7513,91 +8162,196 @@ out_error: void lpfc_sli4_queue_destroy(struct lpfc_hba *phba) { - int idx; - if (phba->cfg_fof) lpfc_fof_queue_destroy(phba); - if (phba->sli4_hba.hba_eq != NULL) { - /* Release HBA event queue */ - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) { - if (phba->sli4_hba.hba_eq[idx] != NULL) { - lpfc_sli4_queue_free( - phba->sli4_hba.hba_eq[idx]); - phba->sli4_hba.hba_eq[idx] = NULL; - } - } - kfree(phba->sli4_hba.hba_eq); - phba->sli4_hba.hba_eq = NULL; - } + /* Release HBA eqs */ + lpfc_sli4_release_queues(&phba->sli4_hba.hba_eq, phba->io_channel_irqs); - if (phba->sli4_hba.fcp_cq != NULL) { - /* Release FCP completion queue */ - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) { - if (phba->sli4_hba.fcp_cq[idx] != NULL) { - lpfc_sli4_queue_free( - phba->sli4_hba.fcp_cq[idx]); - phba->sli4_hba.fcp_cq[idx] = NULL; - } - } - kfree(phba->sli4_hba.fcp_cq); - phba->sli4_hba.fcp_cq = NULL; - } + /* Release FCP cqs */ + lpfc_sli4_release_queues(&phba->sli4_hba.fcp_cq, + phba->cfg_fcp_io_channel); - if (phba->sli4_hba.fcp_wq != NULL) { - /* Release FCP work queue */ - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) { - if (phba->sli4_hba.fcp_wq[idx] != NULL) { - lpfc_sli4_queue_free( - phba->sli4_hba.fcp_wq[idx]); - phba->sli4_hba.fcp_wq[idx] = NULL; - } - } - kfree(phba->sli4_hba.fcp_wq); - phba->sli4_hba.fcp_wq = NULL; - } + /* Release FCP wqs */ + lpfc_sli4_release_queues(&phba->sli4_hba.fcp_wq, + phba->cfg_fcp_io_channel); /* Release FCP CQ mapping array */ - if (phba->sli4_hba.fcp_cq_map != NULL) { - kfree(phba->sli4_hba.fcp_cq_map); - phba->sli4_hba.fcp_cq_map = NULL; - } + lpfc_sli4_release_queue_map(&phba->sli4_hba.fcp_cq_map); + + /* Release NVME cqs */ + lpfc_sli4_release_queues(&phba->sli4_hba.nvme_cq, + phba->cfg_nvme_io_channel); + + /* Release NVME wqs */ + lpfc_sli4_release_queues(&phba->sli4_hba.nvme_wq, + phba->cfg_nvme_io_channel); + + /* Release NVME CQ mapping array */ + lpfc_sli4_release_queue_map(&phba->sli4_hba.nvme_cq_map); + + lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_cqset, + phba->cfg_nvmet_mrq); + + lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_mrq_hdr, + phba->cfg_nvmet_mrq); + lpfc_sli4_release_queues(&phba->sli4_hba.nvmet_mrq_data, + phba->cfg_nvmet_mrq); /* Release mailbox command work queue */ - if (phba->sli4_hba.mbx_wq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.mbx_wq); - phba->sli4_hba.mbx_wq = NULL; - } + __lpfc_sli4_release_queue(&phba->sli4_hba.mbx_wq); /* Release ELS work queue */ - if (phba->sli4_hba.els_wq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.els_wq); - phba->sli4_hba.els_wq = NULL; - } + __lpfc_sli4_release_queue(&phba->sli4_hba.els_wq); + + /* Release ELS work queue */ + __lpfc_sli4_release_queue(&phba->sli4_hba.nvmels_wq); /* Release unsolicited receive queue */ - if (phba->sli4_hba.hdr_rq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.hdr_rq); - phba->sli4_hba.hdr_rq = NULL; + __lpfc_sli4_release_queue(&phba->sli4_hba.hdr_rq); + __lpfc_sli4_release_queue(&phba->sli4_hba.dat_rq); + + /* Release ELS complete queue */ + __lpfc_sli4_release_queue(&phba->sli4_hba.els_cq); + + /* Release NVME LS complete queue */ + __lpfc_sli4_release_queue(&phba->sli4_hba.nvmels_cq); + + /* Release mailbox command complete queue */ + __lpfc_sli4_release_queue(&phba->sli4_hba.mbx_cq); + + /* Everything on this list has been freed */ + INIT_LIST_HEAD(&phba->sli4_hba.lpfc_wq_list); +} + +int +lpfc_post_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *hrq, + struct lpfc_queue *drq, int count) +{ + int rc, i; + struct lpfc_rqe hrqe; + struct lpfc_rqe drqe; + struct lpfc_rqb *rqbp; + struct rqb_dmabuf *rqb_buffer; + LIST_HEAD(rqb_buf_list); + + rqbp = hrq->rqbp; + for (i = 0; i < count; i++) { + rqb_buffer = (rqbp->rqb_alloc_buffer)(phba); + if (!rqb_buffer) + break; + rqb_buffer->hrq = hrq; + rqb_buffer->drq = drq; + list_add_tail(&rqb_buffer->hbuf.list, &rqb_buf_list); + } + while (!list_empty(&rqb_buf_list)) { + list_remove_head(&rqb_buf_list, rqb_buffer, struct rqb_dmabuf, + hbuf.list); + + hrqe.address_lo = putPaddrLow(rqb_buffer->hbuf.phys); + hrqe.address_hi = putPaddrHigh(rqb_buffer->hbuf.phys); + drqe.address_lo = putPaddrLow(rqb_buffer->dbuf.phys); + drqe.address_hi = putPaddrHigh(rqb_buffer->dbuf.phys); + rc = lpfc_sli4_rq_put(hrq, drq, &hrqe, &drqe); + if (rc < 0) { + (rqbp->rqb_free_buffer)(phba, rqb_buffer); + } else { + list_add_tail(&rqb_buffer->hbuf.list, + &rqbp->rqb_buffer_list); + rqbp->buffer_count++; + } } - if (phba->sli4_hba.dat_rq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.dat_rq); - phba->sli4_hba.dat_rq = NULL; + return 1; +} + +int +lpfc_free_rq_buffer(struct lpfc_hba *phba, struct lpfc_queue *rq) +{ + struct lpfc_rqb *rqbp; + struct lpfc_dmabuf *h_buf; + struct rqb_dmabuf *rqb_buffer; + + rqbp = rq->rqbp; + while (!list_empty(&rqbp->rqb_buffer_list)) { + list_remove_head(&rqbp->rqb_buffer_list, h_buf, + struct lpfc_dmabuf, list); + + rqb_buffer = container_of(h_buf, struct rqb_dmabuf, hbuf); + (rqbp->rqb_free_buffer)(phba, rqb_buffer); + rqbp->buffer_count--; } + return 1; +} - /* Release ELS complete queue */ - if (phba->sli4_hba.els_cq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.els_cq); - phba->sli4_hba.els_cq = NULL; +static int +lpfc_create_wq_cq(struct lpfc_hba *phba, struct lpfc_queue *eq, + struct lpfc_queue *cq, struct lpfc_queue *wq, uint16_t *cq_map, + int qidx, uint32_t qtype) +{ + struct lpfc_sli_ring *pring; + int rc; + + if (!eq || !cq || !wq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6085 Fast-path %s (%d) not allocated\n", + ((eq) ? ((cq) ? "WQ" : "CQ") : "EQ"), qidx); + return -ENOMEM; } - /* Release mailbox command complete queue */ - if (phba->sli4_hba.mbx_cq != NULL) { - lpfc_sli4_queue_free(phba->sli4_hba.mbx_cq); - phba->sli4_hba.mbx_cq = NULL; + /* create the Cq first */ + rc = lpfc_cq_create(phba, cq, eq, + (qtype == LPFC_MBOX) ? LPFC_MCQ : LPFC_WCQ, qtype); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6086 Failed setup of CQ (%d), rc = 0x%x\n", + qidx, (uint32_t)rc); + return rc; } - return; + if (qtype != LPFC_MBOX) { + /* Setup nvme_cq_map for fast lookup */ + if (cq_map) + *cq_map = cq->queue_id; + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "6087 CQ setup: cq[%d]-id=%d, parent eq[%d]-id=%d\n", + qidx, cq->queue_id, qidx, eq->queue_id); + + /* create the wq */ + rc = lpfc_wq_create(phba, wq, cq, qtype); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6123 Fail setup fastpath WQ (%d), rc = 0x%x\n", + qidx, (uint32_t)rc); + /* no need to tear down cq - caller will do so */ + return rc; + } + + /* Bind this CQ/WQ to the NVME ring */ + pring = wq->pring; + pring->sli.sli4.wqp = (void *)wq; + cq->pring = pring; + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "2593 WQ setup: wq[%d]-id=%d assoc=%d, cq[%d]-id=%d\n", + qidx, wq->queue_id, wq->assoc_qid, qidx, cq->queue_id); + } else { + rc = lpfc_mq_create(phba, wq, cq, LPFC_MBOX); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0539 Failed setup of slow-path MQ: " + "rc = 0x%x\n", rc); + /* no need to tear down cq - caller will do so */ + return rc; + } + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "2589 MBX MQ setup: wq-id=%d, parent cq-id=%d\n", + phba->sli4_hba.mbx_wq->queue_id, + phba->sli4_hba.mbx_cq->queue_id); + } + + return 0; } /** @@ -7615,15 +8369,12 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba) int lpfc_sli4_queue_setup(struct lpfc_hba *phba) { - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; - int rc = -ENOMEM; - int fcp_eqidx, fcp_cqidx, fcp_wqidx; - int fcp_cq_index = 0; uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; LPFC_MBOXQ_t *mboxq; - uint32_t length; + int qidx; + uint32_t length, io_channel; + int rc = -ENOMEM; /* Check for dual-ULP support */ mboxq = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -7673,220 +8424,263 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) /* * Set up HBA Event Queues (EQs) */ + io_channel = phba->io_channel_irqs; /* Set up HBA event queue */ - if (phba->cfg_fcp_io_channel && !phba->sli4_hba.hba_eq) { + if (io_channel && !phba->sli4_hba.hba_eq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3147 Fast-path EQs not allocated\n"); rc = -ENOMEM; goto out_error; } - for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_io_channel; fcp_eqidx++) { - if (!phba->sli4_hba.hba_eq[fcp_eqidx]) { + for (qidx = 0; qidx < io_channel; qidx++) { + if (!phba->sli4_hba.hba_eq[qidx]) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0522 Fast-path EQ (%d) not " - "allocated\n", fcp_eqidx); + "allocated\n", qidx); rc = -ENOMEM; - goto out_destroy_hba_eq; + goto out_destroy; } - rc = lpfc_eq_create(phba, phba->sli4_hba.hba_eq[fcp_eqidx], - (phba->cfg_fcp_imax / phba->cfg_fcp_io_channel)); + rc = lpfc_eq_create(phba, phba->sli4_hba.hba_eq[qidx], + phba->cfg_fcp_imax); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0523 Failed setup of fast-path EQ " - "(%d), rc = 0x%x\n", fcp_eqidx, + "(%d), rc = 0x%x\n", qidx, (uint32_t)rc); - goto out_destroy_hba_eq; + goto out_destroy; } lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2584 HBA EQ setup: " - "queue[%d]-id=%d\n", fcp_eqidx, - phba->sli4_hba.hba_eq[fcp_eqidx]->queue_id); - } - - /* Set up fast-path FCP Response Complete Queue */ - if (!phba->sli4_hba.fcp_cq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3148 Fast-path FCP CQ array not " - "allocated\n"); - rc = -ENOMEM; - goto out_destroy_hba_eq; + "2584 HBA EQ setup: queue[%d]-id=%d\n", + qidx, phba->sli4_hba.hba_eq[qidx]->queue_id); } - for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_io_channel; fcp_cqidx++) { - if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) { + if (phba->cfg_nvme_io_channel) { + if (!phba->sli4_hba.nvme_cq || !phba->sli4_hba.nvme_wq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0526 Fast-path FCP CQ (%d) not " - "allocated\n", fcp_cqidx); + "6084 Fast-path NVME %s array not allocated\n", + (phba->sli4_hba.nvme_cq) ? "CQ" : "WQ"); rc = -ENOMEM; - goto out_destroy_fcp_cq; - } - rc = lpfc_cq_create(phba, phba->sli4_hba.fcp_cq[fcp_cqidx], - phba->sli4_hba.hba_eq[fcp_cqidx], LPFC_WCQ, LPFC_FCP); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0527 Failed setup of fast-path FCP " - "CQ (%d), rc = 0x%x\n", fcp_cqidx, - (uint32_t)rc); - goto out_destroy_fcp_cq; + goto out_destroy; } - /* Setup fcp_cq_map for fast lookup */ - phba->sli4_hba.fcp_cq_map[fcp_cqidx] = - phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id; - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2588 FCP CQ setup: cq[%d]-id=%d, " - "parent seq[%d]-id=%d\n", - fcp_cqidx, - phba->sli4_hba.fcp_cq[fcp_cqidx]->queue_id, - fcp_cqidx, - phba->sli4_hba.hba_eq[fcp_cqidx]->queue_id); - } - - /* Set up fast-path FCP Work Queue */ - if (!phba->sli4_hba.fcp_wq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3149 Fast-path FCP WQ array not " - "allocated\n"); - rc = -ENOMEM; - goto out_destroy_fcp_cq; + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) { + rc = lpfc_create_wq_cq(phba, + phba->sli4_hba.hba_eq[ + qidx % io_channel], + phba->sli4_hba.nvme_cq[qidx], + phba->sli4_hba.nvme_wq[qidx], + &phba->sli4_hba.nvme_cq_map[qidx], + qidx, LPFC_NVME); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6123 Failed to setup fastpath " + "NVME WQ/CQ (%d), rc = 0x%x\n", + qidx, (uint32_t)rc); + goto out_destroy; + } + } } - for (fcp_wqidx = 0; fcp_wqidx < phba->cfg_fcp_io_channel; fcp_wqidx++) { - if (!phba->sli4_hba.fcp_wq[fcp_wqidx]) { + if (phba->cfg_fcp_io_channel) { + /* Set up fast-path FCP Response Complete Queue */ + if (!phba->sli4_hba.fcp_cq || !phba->sli4_hba.fcp_wq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0534 Fast-path FCP WQ (%d) not " - "allocated\n", fcp_wqidx); + "3148 Fast-path FCP %s array not allocated\n", + phba->sli4_hba.fcp_cq ? "WQ" : "CQ"); rc = -ENOMEM; - goto out_destroy_fcp_wq; - } - rc = lpfc_wq_create(phba, phba->sli4_hba.fcp_wq[fcp_wqidx], - phba->sli4_hba.fcp_cq[fcp_wqidx], - LPFC_FCP); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0535 Failed setup of fast-path FCP " - "WQ (%d), rc = 0x%x\n", fcp_wqidx, - (uint32_t)rc); - goto out_destroy_fcp_wq; + goto out_destroy; } - /* Bind this WQ to the next FCP ring */ - pring = &psli->ring[MAX_SLI3_CONFIGURED_RINGS + fcp_wqidx]; - pring->sli.sli4.wqp = (void *)phba->sli4_hba.fcp_wq[fcp_wqidx]; - phba->sli4_hba.fcp_cq[fcp_wqidx]->pring = pring; - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2591 FCP WQ setup: wq[%d]-id=%d, " - "parent cq[%d]-id=%d\n", - fcp_wqidx, - phba->sli4_hba.fcp_wq[fcp_wqidx]->queue_id, - fcp_cq_index, - phba->sli4_hba.fcp_cq[fcp_wqidx]->queue_id); + for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) { + rc = lpfc_create_wq_cq(phba, + phba->sli4_hba.hba_eq[ + qidx % io_channel], + phba->sli4_hba.fcp_cq[qidx], + phba->sli4_hba.fcp_wq[qidx], + &phba->sli4_hba.fcp_cq_map[qidx], + qidx, LPFC_FCP); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0535 Failed to setup fastpath " + "FCP WQ/CQ (%d), rc = 0x%x\n", + qidx, (uint32_t)rc); + goto out_destroy; + } + } } + /* - * Set up Complete Queues (CQs) + * Set up Slow Path Complete Queues (CQs) */ - /* Set up slow-path MBOX Complete Queue as the first CQ */ - if (!phba->sli4_hba.mbx_cq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0528 Mailbox CQ not allocated\n"); - rc = -ENOMEM; - goto out_destroy_fcp_wq; - } - rc = lpfc_cq_create(phba, phba->sli4_hba.mbx_cq, - phba->sli4_hba.hba_eq[0], LPFC_MCQ, LPFC_MBOX); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0529 Failed setup of slow-path mailbox CQ: " - "rc = 0x%x\n", (uint32_t)rc); - goto out_destroy_fcp_wq; - } - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2585 MBX CQ setup: cq-id=%d, parent eq-id=%d\n", - phba->sli4_hba.mbx_cq->queue_id, - phba->sli4_hba.hba_eq[0]->queue_id); + /* Set up slow-path MBOX CQ/MQ */ - /* Set up slow-path ELS Complete Queue */ - if (!phba->sli4_hba.els_cq) { + if (!phba->sli4_hba.mbx_cq || !phba->sli4_hba.mbx_wq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0530 ELS CQ not allocated\n"); + "0528 %s not allocated\n", + phba->sli4_hba.mbx_cq ? + "Mailbox WQ" : "Mailbox CQ"); rc = -ENOMEM; - goto out_destroy_mbx_cq; - } - rc = lpfc_cq_create(phba, phba->sli4_hba.els_cq, - phba->sli4_hba.hba_eq[0], LPFC_WCQ, LPFC_ELS); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0531 Failed setup of slow-path ELS CQ: " - "rc = 0x%x\n", (uint32_t)rc); - goto out_destroy_mbx_cq; + goto out_destroy; } - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2586 ELS CQ setup: cq-id=%d, parent eq-id=%d\n", - phba->sli4_hba.els_cq->queue_id, - phba->sli4_hba.hba_eq[0]->queue_id); - - /* - * Set up all the Work Queues (WQs) - */ - /* Set up Mailbox Command Queue */ - if (!phba->sli4_hba.mbx_wq) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0538 Slow-path MQ not allocated\n"); - rc = -ENOMEM; - goto out_destroy_els_cq; - } - rc = lpfc_mq_create(phba, phba->sli4_hba.mbx_wq, - phba->sli4_hba.mbx_cq, LPFC_MBOX); + rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0], + phba->sli4_hba.mbx_cq, + phba->sli4_hba.mbx_wq, + NULL, 0, LPFC_MBOX); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0539 Failed setup of slow-path MQ: " - "rc = 0x%x\n", rc); - goto out_destroy_els_cq; + "0529 Failed setup of mailbox WQ/CQ: rc = 0x%x\n", + (uint32_t)rc); + goto out_destroy; + } + if (phba->nvmet_support) { + if (!phba->sli4_hba.nvmet_cqset) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3165 Fast-path NVME CQ Set " + "array not allocated\n"); + rc = -ENOMEM; + goto out_destroy; + } + if (phba->cfg_nvmet_mrq > 1) { + rc = lpfc_cq_create_set(phba, + phba->sli4_hba.nvmet_cqset, + phba->sli4_hba.hba_eq, + LPFC_WCQ, LPFC_NVMET); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3164 Failed setup of NVME CQ " + "Set, rc = 0x%x\n", + (uint32_t)rc); + goto out_destroy; + } + } else { + /* Set up NVMET Receive Complete Queue */ + rc = lpfc_cq_create(phba, phba->sli4_hba.nvmet_cqset[0], + phba->sli4_hba.hba_eq[0], + LPFC_WCQ, LPFC_NVMET); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6089 Failed setup NVMET CQ: " + "rc = 0x%x\n", (uint32_t)rc); + goto out_destroy; + } + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "6090 NVMET CQ setup: cq-id=%d, " + "parent eq-id=%d\n", + phba->sli4_hba.nvmet_cqset[0]->queue_id, + phba->sli4_hba.hba_eq[0]->queue_id); + } } - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "2589 MBX MQ setup: wq-id=%d, parent cq-id=%d\n", - phba->sli4_hba.mbx_wq->queue_id, - phba->sli4_hba.mbx_cq->queue_id); - /* Set up slow-path ELS Work Queue */ - if (!phba->sli4_hba.els_wq) { + /* Set up slow-path ELS WQ/CQ */ + if (!phba->sli4_hba.els_cq || !phba->sli4_hba.els_wq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0536 Slow-path ELS WQ not allocated\n"); + "0530 ELS %s not allocated\n", + phba->sli4_hba.els_cq ? "WQ" : "CQ"); rc = -ENOMEM; - goto out_destroy_mbx_wq; + goto out_destroy; } - rc = lpfc_wq_create(phba, phba->sli4_hba.els_wq, - phba->sli4_hba.els_cq, LPFC_ELS); + rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0], + phba->sli4_hba.els_cq, + phba->sli4_hba.els_wq, + NULL, 0, LPFC_ELS); if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0537 Failed setup of slow-path ELS WQ: " - "rc = 0x%x\n", (uint32_t)rc); - goto out_destroy_mbx_wq; + "0529 Failed setup of ELS WQ/CQ: rc = 0x%x\n", + (uint32_t)rc); + goto out_destroy; } - - /* Bind this WQ to the ELS ring */ - pring = &psli->ring[LPFC_ELS_RING]; - pring->sli.sli4.wqp = (void *)phba->sli4_hba.els_wq; - phba->sli4_hba.els_cq->pring = pring; - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "2590 ELS WQ setup: wq-id=%d, parent cq-id=%d\n", phba->sli4_hba.els_wq->queue_id, phba->sli4_hba.els_cq->queue_id); + if (phba->cfg_nvme_io_channel) { + /* Set up NVME LS Complete Queue */ + if (!phba->sli4_hba.nvmels_cq || !phba->sli4_hba.nvmels_wq) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6091 LS %s not allocated\n", + phba->sli4_hba.nvmels_cq ? "WQ" : "CQ"); + rc = -ENOMEM; + goto out_destroy; + } + rc = lpfc_create_wq_cq(phba, phba->sli4_hba.hba_eq[0], + phba->sli4_hba.nvmels_cq, + phba->sli4_hba.nvmels_wq, + NULL, 0, LPFC_NVME_LS); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0529 Failed setup of NVVME LS WQ/CQ: " + "rc = 0x%x\n", (uint32_t)rc); + goto out_destroy; + } + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "6096 ELS WQ setup: wq-id=%d, " + "parent cq-id=%d\n", + phba->sli4_hba.nvmels_wq->queue_id, + phba->sli4_hba.nvmels_cq->queue_id); + } + /* - * Create Receive Queue (RQ) + * Create NVMET Receive Queue (RQ) */ + if (phba->nvmet_support) { + if ((!phba->sli4_hba.nvmet_cqset) || + (!phba->sli4_hba.nvmet_mrq_hdr) || + (!phba->sli4_hba.nvmet_mrq_data)) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6130 MRQ CQ Queues not " + "allocated\n"); + rc = -ENOMEM; + goto out_destroy; + } + if (phba->cfg_nvmet_mrq > 1) { + rc = lpfc_mrq_create(phba, + phba->sli4_hba.nvmet_mrq_hdr, + phba->sli4_hba.nvmet_mrq_data, + phba->sli4_hba.nvmet_cqset, + LPFC_NVMET); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6098 Failed setup of NVMET " + "MRQ: rc = 0x%x\n", + (uint32_t)rc); + goto out_destroy; + } + + } else { + rc = lpfc_rq_create(phba, + phba->sli4_hba.nvmet_mrq_hdr[0], + phba->sli4_hba.nvmet_mrq_data[0], + phba->sli4_hba.nvmet_cqset[0], + LPFC_NVMET); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6057 Failed setup of NVMET " + "Receive Queue: rc = 0x%x\n", + (uint32_t)rc); + goto out_destroy; + } + + lpfc_printf_log( + phba, KERN_INFO, LOG_INIT, + "6099 NVMET RQ setup: hdr-rq-id=%d, " + "dat-rq-id=%d parent cq-id=%d\n", + phba->sli4_hba.nvmet_mrq_hdr[0]->queue_id, + phba->sli4_hba.nvmet_mrq_data[0]->queue_id, + phba->sli4_hba.nvmet_cqset[0]->queue_id); + + } + } + if (!phba->sli4_hba.hdr_rq || !phba->sli4_hba.dat_rq) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0540 Receive Queue not allocated\n"); rc = -ENOMEM; - goto out_destroy_els_wq; + goto out_destroy; } lpfc_rq_adjust_repost(phba, phba->sli4_hba.hdr_rq, LPFC_ELS_HBQ); @@ -7898,7 +8692,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0541 Failed setup of Receive Queue: " "rc = 0x%x\n", (uint32_t)rc); - goto out_destroy_fcp_wq; + goto out_destroy; } lpfc_printf_log(phba, KERN_INFO, LOG_INIT, @@ -7914,7 +8708,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0549 Failed setup of FOF Queues: " "rc = 0x%x\n", rc); - goto out_destroy_els_rq; + goto out_destroy; } } @@ -7922,30 +8716,12 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) * Configure EQ delay multipier for interrupt coalescing using * MODIFY_EQ_DELAY for all EQs created, LPFC_MAX_EQ_DELAY at a time. */ - for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_io_channel; - fcp_eqidx += LPFC_MAX_EQ_DELAY) - lpfc_modify_fcp_eq_delay(phba, fcp_eqidx); + for (qidx = 0; qidx < io_channel; qidx += LPFC_MAX_EQ_DELAY) + lpfc_modify_hba_eq_delay(phba, qidx); return 0; -out_destroy_els_rq: - lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq); -out_destroy_els_wq: - lpfc_wq_destroy(phba, phba->sli4_hba.els_wq); -out_destroy_mbx_wq: - lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq); -out_destroy_els_cq: - lpfc_cq_destroy(phba, phba->sli4_hba.els_cq); -out_destroy_mbx_cq: - lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq); -out_destroy_fcp_wq: - for (--fcp_wqidx; fcp_wqidx >= 0; fcp_wqidx--) - lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_wqidx]); -out_destroy_fcp_cq: - for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--) - lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_cqidx]); -out_destroy_hba_eq: - for (--fcp_eqidx; fcp_eqidx >= 0; fcp_eqidx--) - lpfc_eq_destroy(phba, phba->sli4_hba.hba_eq[fcp_eqidx]); +out_destroy: + lpfc_sli4_queue_unset(phba); out_error: return rc; } @@ -7965,39 +8741,81 @@ out_error: void lpfc_sli4_queue_unset(struct lpfc_hba *phba) { - int fcp_qidx; + int qidx; /* Unset the queues created for Flash Optimized Fabric operations */ if (phba->cfg_fof) lpfc_fof_queue_destroy(phba); + /* Unset mailbox command work queue */ - lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq); + if (phba->sli4_hba.mbx_wq) + lpfc_mq_destroy(phba, phba->sli4_hba.mbx_wq); + + /* Unset NVME LS work queue */ + if (phba->sli4_hba.nvmels_wq) + lpfc_wq_destroy(phba, phba->sli4_hba.nvmels_wq); + /* Unset ELS work queue */ - lpfc_wq_destroy(phba, phba->sli4_hba.els_wq); + if (phba->sli4_hba.els_cq) + lpfc_wq_destroy(phba, phba->sli4_hba.els_wq); + /* Unset unsolicited receive queue */ - lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq); + if (phba->sli4_hba.hdr_rq) + lpfc_rq_destroy(phba, phba->sli4_hba.hdr_rq, + phba->sli4_hba.dat_rq); + /* Unset FCP work queue */ - if (phba->sli4_hba.fcp_wq) { - for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_io_channel; - fcp_qidx++) - lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[fcp_qidx]); + if (phba->sli4_hba.fcp_wq) + for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) + lpfc_wq_destroy(phba, phba->sli4_hba.fcp_wq[qidx]); + + /* Unset NVME work queue */ + if (phba->sli4_hba.nvme_wq) { + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) + lpfc_wq_destroy(phba, phba->sli4_hba.nvme_wq[qidx]); } + /* Unset mailbox command complete queue */ - lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq); + if (phba->sli4_hba.mbx_cq) + lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq); + /* Unset ELS complete queue */ - lpfc_cq_destroy(phba, phba->sli4_hba.els_cq); - /* Unset FCP response complete queue */ - if (phba->sli4_hba.fcp_cq) { - for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_io_channel; - fcp_qidx++) - lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_qidx]); + if (phba->sli4_hba.els_cq) + lpfc_cq_destroy(phba, phba->sli4_hba.els_cq); + + /* Unset NVME LS complete queue */ + if (phba->sli4_hba.nvmels_cq) + lpfc_cq_destroy(phba, phba->sli4_hba.nvmels_cq); + + /* Unset NVME response complete queue */ + if (phba->sli4_hba.nvme_cq) + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) + lpfc_cq_destroy(phba, phba->sli4_hba.nvme_cq[qidx]); + + /* Unset NVMET MRQ queue */ + if (phba->sli4_hba.nvmet_mrq_hdr) { + for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) + lpfc_rq_destroy(phba, + phba->sli4_hba.nvmet_mrq_hdr[qidx], + phba->sli4_hba.nvmet_mrq_data[qidx]); } - /* Unset fast-path event queue */ - if (phba->sli4_hba.hba_eq) { - for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_io_channel; - fcp_qidx++) - lpfc_eq_destroy(phba, phba->sli4_hba.hba_eq[fcp_qidx]); + + /* Unset NVMET CQ Set complete queue */ + if (phba->sli4_hba.nvmet_cqset) { + for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) + lpfc_cq_destroy(phba, + phba->sli4_hba.nvmet_cqset[qidx]); } + + /* Unset FCP response complete queue */ + if (phba->sli4_hba.fcp_cq) + for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) + lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[qidx]); + + /* Unset fast-path event queue */ + if (phba->sli4_hba.hba_eq) + for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) + lpfc_eq_destroy(phba, phba->sli4_hba.hba_eq[qidx]); } /** @@ -8484,16 +9302,7 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba) * @phba: pointer to lpfc hba data structure. * * This routine is invoked to enable the MSI-X interrupt vectors to device - * with SLI-3 interface specs. The kernel function pci_enable_msix_exact() - * is called to enable the MSI-X vectors. Note that pci_enable_msix_exact(), - * once invoked, enables either all or nothing, depending on the current - * availability of PCI vector resources. The device driver is responsible - * for calling the individual request_irq() to register each MSI-X vector - * with a interrupt handler, which is done in this function. Note that - * later when device is unloading, the driver should always call free_irq() - * on all MSI-X vectors it has done request_irq() on before calling - * pci_disable_msix(). Failure to do so results in a BUG_ON() and a device - * will be left with MSI-X enabled and leaks its vectors. + * with SLI-3 interface specs. * * Return codes * 0 - successful @@ -8502,33 +9311,24 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba) static int lpfc_sli_enable_msix(struct lpfc_hba *phba) { - int rc, i; + int rc; LPFC_MBOXQ_t *pmb; /* Set up MSI-X multi-message vectors */ - for (i = 0; i < LPFC_MSIX_VECTORS; i++) - phba->msix_entries[i].entry = i; - - /* Configure MSI-X capability structure */ - rc = pci_enable_msix_exact(phba->pcidev, phba->msix_entries, - LPFC_MSIX_VECTORS); - if (rc) { + rc = pci_alloc_irq_vectors(phba->pcidev, + LPFC_MSIX_VECTORS, LPFC_MSIX_VECTORS, PCI_IRQ_MSIX); + if (rc < 0) { lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "0420 PCI enable MSI-X failed (%d)\n", rc); goto vec_fail_out; } - for (i = 0; i < LPFC_MSIX_VECTORS; i++) - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0477 MSI-X entry[%d]: vector=x%x " - "message=%d\n", i, - phba->msix_entries[i].vector, - phba->msix_entries[i].entry); + /* * Assign MSI-X vectors to interrupt handlers */ /* vector-0 is associated to slow-path handler */ - rc = request_irq(phba->msix_entries[0].vector, + rc = request_irq(pci_irq_vector(phba->pcidev, 0), &lpfc_sli_sp_intr_handler, 0, LPFC_SP_DRIVER_HANDLER_NAME, phba); if (rc) { @@ -8539,7 +9339,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba) } /* vector-1 is associated to fast-path handler */ - rc = request_irq(phba->msix_entries[1].vector, + rc = request_irq(pci_irq_vector(phba->pcidev, 1), &lpfc_sli_fp_intr_handler, 0, LPFC_FP_DRIVER_HANDLER_NAME, phba); @@ -8584,42 +9384,21 @@ mbx_fail_out: mem_fail_out: /* free the irq already requested */ - free_irq(phba->msix_entries[1].vector, phba); + free_irq(pci_irq_vector(phba->pcidev, 1), phba); irq_fail_out: /* free the irq already requested */ - free_irq(phba->msix_entries[0].vector, phba); + free_irq(pci_irq_vector(phba->pcidev, 0), phba); msi_fail_out: /* Unconfigure MSI-X capability structure */ - pci_disable_msix(phba->pcidev); + pci_free_irq_vectors(phba->pcidev); vec_fail_out: return rc; } /** - * lpfc_sli_disable_msix - Disable MSI-X interrupt mode on SLI-3 device. - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to release the MSI-X vectors and then disable the - * MSI-X interrupt mode to device with SLI-3 interface spec. - **/ -static void -lpfc_sli_disable_msix(struct lpfc_hba *phba) -{ - int i; - - /* Free up MSI-X multi-message vectors */ - for (i = 0; i < LPFC_MSIX_VECTORS; i++) - free_irq(phba->msix_entries[i].vector, phba); - /* Disable MSI-X */ - pci_disable_msix(phba->pcidev); - - return; -} - -/** * lpfc_sli_enable_msi - Enable MSI interrupt mode on SLI-3 device. * @phba: pointer to lpfc hba data structure. * @@ -8659,24 +9438,6 @@ lpfc_sli_enable_msi(struct lpfc_hba *phba) } /** - * lpfc_sli_disable_msi - Disable MSI interrupt mode to SLI-3 device. - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to disable the MSI interrupt mode to device with - * SLI-3 interface spec. The driver calls free_irq() on MSI vector it has - * done request_irq() on before calling pci_disable_msi(). Failure to do so - * results in a BUG_ON() and a device will be left with MSI enabled and leaks - * its vector. - */ -static void -lpfc_sli_disable_msi(struct lpfc_hba *phba) -{ - free_irq(phba->pcidev->irq, phba); - pci_disable_msi(phba->pcidev); - return; -} - -/** * lpfc_sli_enable_intr - Enable device interrupt to SLI-3 device. * @phba: pointer to lpfc hba data structure. * @@ -8747,107 +9508,50 @@ lpfc_sli_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) static void lpfc_sli_disable_intr(struct lpfc_hba *phba) { - /* Disable the currently initialized interrupt mode */ + int nr_irqs, i; + if (phba->intr_type == MSIX) - lpfc_sli_disable_msix(phba); - else if (phba->intr_type == MSI) - lpfc_sli_disable_msi(phba); - else if (phba->intr_type == INTx) - free_irq(phba->pcidev->irq, phba); + nr_irqs = LPFC_MSIX_VECTORS; + else + nr_irqs = 1; + + for (i = 0; i < nr_irqs; i++) + free_irq(pci_irq_vector(phba->pcidev, i), phba); + pci_free_irq_vectors(phba->pcidev); /* Reset interrupt management states */ phba->intr_type = NONE; phba->sli.slistat.sli_intr = 0; - - return; } /** - * lpfc_find_next_cpu - Find next available CPU that matches the phys_id + * lpfc_cpu_affinity_check - Check vector CPU affinity mappings * @phba: pointer to lpfc hba data structure. + * @vectors: number of msix vectors allocated. * - * Find next available CPU to use for IRQ to CPU affinity. + * The routine will figure out the CPU affinity assignment for every + * MSI-X vector allocated for the HBA. The hba_eq_hdl will be updated + * with a pointer to the CPU mask that defines ALL the CPUs this vector + * can be associated with. If the vector can be unquely associated with + * a single CPU, that CPU will be recorded in hba_eq_hdl[index].cpu. + * In addition, the CPU to IO channel mapping will be calculated + * and the phba->sli4_hba.cpu_map array will reflect this. */ -static int -lpfc_find_next_cpu(struct lpfc_hba *phba, uint32_t phys_id) +static void +lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) { struct lpfc_vector_map_info *cpup; + int index = 0; + int vec = 0; int cpu; - - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { - /* CPU must be online */ - if (cpu_online(cpu)) { - if ((cpup->irq == LPFC_VECTOR_MAP_EMPTY) && - (lpfc_used_cpu[cpu] == LPFC_VECTOR_MAP_EMPTY) && - (cpup->phys_id == phys_id)) { - return cpu; - } - } - cpup++; - } - - /* - * If we get here, we have used ALL CPUs for the specific - * phys_id. Now we need to clear out lpfc_used_cpu and start - * reusing CPUs. - */ - - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { - if (lpfc_used_cpu[cpu] == phys_id) - lpfc_used_cpu[cpu] = LPFC_VECTOR_MAP_EMPTY; - } - - cpup = phba->sli4_hba.cpu_map; - for (cpu = 0; cpu < phba->sli4_hba.num_present_cpu; cpu++) { - /* CPU must be online */ - if (cpu_online(cpu)) { - if ((cpup->irq == LPFC_VECTOR_MAP_EMPTY) && - (cpup->phys_id == phys_id)) { - return cpu; - } - } - cpup++; - } - return LPFC_VECTOR_MAP_EMPTY; -} - -/** - * lpfc_sli4_set_affinity - Set affinity for HBA IRQ vectors - * @phba: pointer to lpfc hba data structure. - * @vectors: number of HBA vectors - * - * Affinitize MSIX IRQ vectors to CPUs. Try to equally spread vector - * affinization across multple physical CPUs (numa nodes). - * In addition, this routine will assign an IO channel for each CPU - * to use when issuing I/Os. - */ -static int -lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors) -{ - int i, idx, saved_chann, used_chann, cpu, phys_id; - int max_phys_id, min_phys_id; - int num_io_channel, first_cpu, chan; - struct lpfc_vector_map_info *cpup; #ifdef CONFIG_X86 struct cpuinfo_x86 *cpuinfo; #endif - uint8_t chann[LPFC_FCP_IO_CHAN_MAX+1]; - - /* If there is no mapping, just return */ - if (!phba->cfg_fcp_cpu_map) - return 1; /* Init cpu_map array */ memset(phba->sli4_hba.cpu_map, 0xff, (sizeof(struct lpfc_vector_map_info) * - phba->sli4_hba.num_present_cpu)); - - max_phys_id = 0; - min_phys_id = 0xff; - phys_id = 0; - num_io_channel = 0; - first_cpu = LPFC_VECTOR_MAP_EMPTY; + phba->sli4_hba.num_present_cpu)); /* Update CPU map with physical id and core id of each CPU */ cpup = phba->sli4_hba.cpu_map; @@ -8861,184 +9565,16 @@ lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors) cpup->phys_id = 0; cpup->core_id = 0; #endif - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "3328 CPU physid %d coreid %d\n", - cpup->phys_id, cpup->core_id); - - if (cpup->phys_id > max_phys_id) - max_phys_id = cpup->phys_id; - if (cpup->phys_id < min_phys_id) - min_phys_id = cpup->phys_id; + cpup->channel_id = index; /* For now round robin */ + cpup->irq = pci_irq_vector(phba->pcidev, vec); + vec++; + if (vec >= vectors) + vec = 0; + index++; + if (index >= phba->cfg_fcp_io_channel) + index = 0; cpup++; } - - phys_id = min_phys_id; - /* Now associate the HBA vectors with specific CPUs */ - for (idx = 0; idx < vectors; idx++) { - cpup = phba->sli4_hba.cpu_map; - cpu = lpfc_find_next_cpu(phba, phys_id); - if (cpu == LPFC_VECTOR_MAP_EMPTY) { - - /* Try for all phys_id's */ - for (i = 1; i < max_phys_id; i++) { - phys_id++; - if (phys_id > max_phys_id) - phys_id = min_phys_id; - cpu = lpfc_find_next_cpu(phba, phys_id); - if (cpu == LPFC_VECTOR_MAP_EMPTY) - continue; - goto found; - } - - /* Use round robin for scheduling */ - phba->cfg_fcp_io_sched = LPFC_FCP_SCHED_ROUND_ROBIN; - chan = 0; - cpup = phba->sli4_hba.cpu_map; - for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { - cpup->channel_id = chan; - cpup++; - chan++; - if (chan >= phba->cfg_fcp_io_channel) - chan = 0; - } - - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3329 Cannot set affinity:" - "Error mapping vector %d (%d)\n", - idx, vectors); - return 0; - } -found: - cpup += cpu; - if (phba->cfg_fcp_cpu_map == LPFC_DRIVER_CPU_MAP) - lpfc_used_cpu[cpu] = phys_id; - - /* Associate vector with selected CPU */ - cpup->irq = phba->sli4_hba.msix_entries[idx].vector; - - /* Associate IO channel with selected CPU */ - cpup->channel_id = idx; - num_io_channel++; - - if (first_cpu == LPFC_VECTOR_MAP_EMPTY) - first_cpu = cpu; - - /* Now affinitize to the selected CPU */ - i = irq_set_affinity_hint(phba->sli4_hba.msix_entries[idx]. - vector, get_cpu_mask(cpu)); - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "3330 Set Affinity: CPU %d channel %d " - "irq %d (%x)\n", - cpu, cpup->channel_id, - phba->sli4_hba.msix_entries[idx].vector, i); - - /* Spread vector mapping across multple physical CPU nodes */ - phys_id++; - if (phys_id > max_phys_id) - phys_id = min_phys_id; - } - - /* - * Finally fill in the IO channel for any remaining CPUs. - * At this point, all IO channels have been assigned to a specific - * MSIx vector, mapped to a specific CPU. - * Base the remaining IO channel assigned, to IO channels already - * assigned to other CPUs on the same phys_id. - */ - for (i = min_phys_id; i <= max_phys_id; i++) { - /* - * If there are no io channels already mapped to - * this phys_id, just round robin thru the io_channels. - * Setup chann[] for round robin. - */ - for (idx = 0; idx < phba->cfg_fcp_io_channel; idx++) - chann[idx] = idx; - - saved_chann = 0; - used_chann = 0; - - /* - * First build a list of IO channels already assigned - * to this phys_id before reassigning the same IO - * channels to the remaining CPUs. - */ - cpup = phba->sli4_hba.cpu_map; - cpu = first_cpu; - cpup += cpu; - for (idx = 0; idx < phba->sli4_hba.num_present_cpu; - idx++) { - if (cpup->phys_id == i) { - /* - * Save any IO channels that are - * already mapped to this phys_id. - */ - if (cpup->irq != LPFC_VECTOR_MAP_EMPTY) { - if (saved_chann <= - LPFC_FCP_IO_CHAN_MAX) { - chann[saved_chann] = - cpup->channel_id; - saved_chann++; - } - goto out; - } - - /* See if we are using round-robin */ - if (saved_chann == 0) - saved_chann = - phba->cfg_fcp_io_channel; - - /* Associate next IO channel with CPU */ - cpup->channel_id = chann[used_chann]; - num_io_channel++; - used_chann++; - if (used_chann == saved_chann) - used_chann = 0; - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "3331 Set IO_CHANN " - "CPU %d channel %d\n", - idx, cpup->channel_id); - } -out: - cpu++; - if (cpu >= phba->sli4_hba.num_present_cpu) { - cpup = phba->sli4_hba.cpu_map; - cpu = 0; - } else { - cpup++; - } - } - } - - if (phba->sli4_hba.num_online_cpu != phba->sli4_hba.num_present_cpu) { - cpup = phba->sli4_hba.cpu_map; - for (idx = 0; idx < phba->sli4_hba.num_present_cpu; idx++) { - if (cpup->channel_id == LPFC_VECTOR_MAP_EMPTY) { - cpup->channel_id = 0; - num_io_channel++; - - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "3332 Assign IO_CHANN " - "CPU %d channel %d\n", - idx, cpup->channel_id); - } - cpup++; - } - } - - /* Sanity check */ - if (num_io_channel != phba->sli4_hba.num_present_cpu) - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3333 Set affinity mismatch:" - "%d chann != %d cpus: %d vectors\n", - num_io_channel, phba->sli4_hba.num_present_cpu, - vectors); - - /* Enable using cpu affinity for scheduling */ - phba->cfg_fcp_io_sched = LPFC_FCP_SCHED_BY_CPU; - return 1; } @@ -9047,14 +9583,7 @@ out: * @phba: pointer to lpfc hba data structure. * * This routine is invoked to enable the MSI-X interrupt vectors to device - * with SLI-4 interface spec. The kernel function pci_enable_msix_range() - * is called to enable the MSI-X vectors. The device driver is responsible - * for calling the individual request_irq() to register each MSI-X vector - * with a interrupt handler, which is done in this function. Note that - * later when device is unloading, the driver should always call free_irq() - * on all MSI-X vectors it has done request_irq() on before calling - * pci_disable_msix(). Failure to do so results in a BUG_ON() and a device - * will be left with MSI-X enabled and leaks its vectors. + * with SLI-4 interface spec. * * Return codes * 0 - successful @@ -9066,17 +9595,13 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) int vectors, rc, index; /* Set up MSI-X multi-message vectors */ - for (index = 0; index < phba->cfg_fcp_io_channel; index++) - phba->sli4_hba.msix_entries[index].entry = index; - - /* Configure MSI-X capability structure */ - vectors = phba->cfg_fcp_io_channel; - if (phba->cfg_fof) { - phba->sli4_hba.msix_entries[index].entry = index; + vectors = phba->io_channel_irqs; + if (phba->cfg_fof) vectors++; - } - rc = pci_enable_msix_range(phba->pcidev, phba->sli4_hba.msix_entries, - 2, vectors); + + rc = pci_alloc_irq_vectors(phba->pcidev, + (phba->nvmet_support) ? 1 : 2, + vectors, PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); if (rc < 0) { lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "0484 PCI enable MSI-X failed (%d)\n", rc); @@ -9084,14 +9609,6 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) } vectors = rc; - /* Log MSI-X vector assignment */ - for (index = 0; index < vectors; index++) - lpfc_printf_log(phba, KERN_INFO, LOG_INIT, - "0489 MSI-X entry[%d]: vector=x%x " - "message=%d\n", index, - phba->sli4_hba.msix_entries[index].vector, - phba->sli4_hba.msix_entries[index].entry); - /* Assign MSI-X vectors to interrupt handlers */ for (index = 0; index < vectors; index++) { memset(&phba->sli4_hba.handler_name[index], 0, 16); @@ -9099,21 +9616,19 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) LPFC_SLI4_HANDLER_NAME_SZ, LPFC_DRIVER_HANDLER_NAME"%d", index); - phba->sli4_hba.fcp_eq_hdl[index].idx = index; - phba->sli4_hba.fcp_eq_hdl[index].phba = phba; - atomic_set(&phba->sli4_hba.fcp_eq_hdl[index].fcp_eq_in_use, 1); + phba->sli4_hba.hba_eq_hdl[index].idx = index; + phba->sli4_hba.hba_eq_hdl[index].phba = phba; + atomic_set(&phba->sli4_hba.hba_eq_hdl[index].hba_eq_in_use, 1); if (phba->cfg_fof && (index == (vectors - 1))) - rc = request_irq( - phba->sli4_hba.msix_entries[index].vector, + rc = request_irq(pci_irq_vector(phba->pcidev, index), &lpfc_sli4_fof_intr_handler, 0, (char *)&phba->sli4_hba.handler_name[index], - &phba->sli4_hba.fcp_eq_hdl[index]); + &phba->sli4_hba.hba_eq_hdl[index]); else - rc = request_irq( - phba->sli4_hba.msix_entries[index].vector, + rc = request_irq(pci_irq_vector(phba->pcidev, index), &lpfc_sli4_hba_intr_handler, 0, (char *)&phba->sli4_hba.handler_name[index], - &phba->sli4_hba.fcp_eq_hdl[index]); + &phba->sli4_hba.hba_eq_hdl[index]); if (rc) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "0486 MSI-X fast-path (%d) " @@ -9125,64 +9640,38 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) if (phba->cfg_fof) vectors--; - if (vectors != phba->cfg_fcp_io_channel) { + if (vectors != phba->io_channel_irqs) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3238 Reducing IO channels to match number of " "MSI-X vectors, requested %d got %d\n", - phba->cfg_fcp_io_channel, vectors); - phba->cfg_fcp_io_channel = vectors; + phba->io_channel_irqs, vectors); + if (phba->cfg_fcp_io_channel > vectors) + phba->cfg_fcp_io_channel = vectors; + if (phba->cfg_nvme_io_channel > vectors) + phba->cfg_nvme_io_channel = vectors; + if (phba->cfg_fcp_io_channel > phba->cfg_nvme_io_channel) + phba->io_channel_irqs = phba->cfg_fcp_io_channel; + else + phba->io_channel_irqs = phba->cfg_nvme_io_channel; } + lpfc_cpu_affinity_check(phba, vectors); - if (!shost_use_blk_mq(lpfc_shost_from_vport(phba->pport))) - lpfc_sli4_set_affinity(phba, vectors); return rc; cfg_fail_out: /* free the irq already requested */ - for (--index; index >= 0; index--) { - irq_set_affinity_hint(phba->sli4_hba.msix_entries[index]. - vector, NULL); - free_irq(phba->sli4_hba.msix_entries[index].vector, - &phba->sli4_hba.fcp_eq_hdl[index]); - } + for (--index; index >= 0; index--) + free_irq(pci_irq_vector(phba->pcidev, index), + &phba->sli4_hba.hba_eq_hdl[index]); /* Unconfigure MSI-X capability structure */ - pci_disable_msix(phba->pcidev); + pci_free_irq_vectors(phba->pcidev); vec_fail_out: return rc; } /** - * lpfc_sli4_disable_msix - Disable MSI-X interrupt mode to SLI-4 device - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to release the MSI-X vectors and then disable the - * MSI-X interrupt mode to device with SLI-4 interface spec. - **/ -static void -lpfc_sli4_disable_msix(struct lpfc_hba *phba) -{ - int index; - - /* Free up MSI-X multi-message vectors */ - for (index = 0; index < phba->cfg_fcp_io_channel; index++) { - irq_set_affinity_hint(phba->sli4_hba.msix_entries[index]. - vector, NULL); - free_irq(phba->sli4_hba.msix_entries[index].vector, - &phba->sli4_hba.fcp_eq_hdl[index]); - } - if (phba->cfg_fof) { - free_irq(phba->sli4_hba.msix_entries[index].vector, - &phba->sli4_hba.fcp_eq_hdl[index]); - } - /* Disable MSI-X */ - pci_disable_msix(phba->pcidev); - - return; -} - -/** * lpfc_sli4_enable_msi - Enable MSI interrupt mode to SLI-4 device * @phba: pointer to lpfc hba data structure. * @@ -9220,37 +9709,19 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba) return rc; } - for (index = 0; index < phba->cfg_fcp_io_channel; index++) { - phba->sli4_hba.fcp_eq_hdl[index].idx = index; - phba->sli4_hba.fcp_eq_hdl[index].phba = phba; + for (index = 0; index < phba->io_channel_irqs; index++) { + phba->sli4_hba.hba_eq_hdl[index].idx = index; + phba->sli4_hba.hba_eq_hdl[index].phba = phba; } if (phba->cfg_fof) { - phba->sli4_hba.fcp_eq_hdl[index].idx = index; - phba->sli4_hba.fcp_eq_hdl[index].phba = phba; + phba->sli4_hba.hba_eq_hdl[index].idx = index; + phba->sli4_hba.hba_eq_hdl[index].phba = phba; } return 0; } /** - * lpfc_sli4_disable_msi - Disable MSI interrupt mode to SLI-4 device - * @phba: pointer to lpfc hba data structure. - * - * This routine is invoked to disable the MSI interrupt mode to device with - * SLI-4 interface spec. The driver calls free_irq() on MSI vector it has - * done request_irq() on before calling pci_disable_msi(). Failure to do so - * results in a BUG_ON() and a device will be left with MSI enabled and leaks - * its vector. - **/ -static void -lpfc_sli4_disable_msi(struct lpfc_hba *phba) -{ - free_irq(phba->pcidev->irq, phba); - pci_disable_msi(phba->pcidev); - return; -} - -/** * lpfc_sli4_enable_intr - Enable device interrupt to SLI-4 device * @phba: pointer to lpfc hba data structure. * @@ -9270,7 +9741,7 @@ static uint32_t lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) { uint32_t intr_mode = LPFC_INTR_ERROR; - int retval, index; + int retval, idx; if (cfg_mode == 2) { /* Preparation before conf_msi mbox cmd */ @@ -9301,21 +9772,23 @@ lpfc_sli4_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode) retval = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler, IRQF_SHARED, LPFC_DRIVER_NAME, phba); if (!retval) { + struct lpfc_hba_eq_hdl *eqhdl; + /* Indicate initialization to INTx mode */ phba->intr_type = INTx; intr_mode = 0; - for (index = 0; index < phba->cfg_fcp_io_channel; - index++) { - phba->sli4_hba.fcp_eq_hdl[index].idx = index; - phba->sli4_hba.fcp_eq_hdl[index].phba = phba; - atomic_set(&phba->sli4_hba.fcp_eq_hdl[index]. - fcp_eq_in_use, 1); + + for (idx = 0; idx < phba->io_channel_irqs; idx++) { + eqhdl = &phba->sli4_hba.hba_eq_hdl[idx]; + eqhdl->idx = idx; + eqhdl->phba = phba; + atomic_set(&eqhdl->hba_eq_in_use, 1); } if (phba->cfg_fof) { - phba->sli4_hba.fcp_eq_hdl[index].idx = index; - phba->sli4_hba.fcp_eq_hdl[index].phba = phba; - atomic_set(&phba->sli4_hba.fcp_eq_hdl[index]. - fcp_eq_in_use, 1); + eqhdl = &phba->sli4_hba.hba_eq_hdl[idx]; + eqhdl->idx = idx; + eqhdl->phba = phba; + atomic_set(&eqhdl->hba_eq_in_use, 1); } } } @@ -9335,18 +9808,26 @@ static void lpfc_sli4_disable_intr(struct lpfc_hba *phba) { /* Disable the currently initialized interrupt mode */ - if (phba->intr_type == MSIX) - lpfc_sli4_disable_msix(phba); - else if (phba->intr_type == MSI) - lpfc_sli4_disable_msi(phba); - else if (phba->intr_type == INTx) + if (phba->intr_type == MSIX) { + int index; + + /* Free up MSI-X multi-message vectors */ + for (index = 0; index < phba->io_channel_irqs; index++) + free_irq(pci_irq_vector(phba->pcidev, index), + &phba->sli4_hba.hba_eq_hdl[index]); + + if (phba->cfg_fof) + free_irq(pci_irq_vector(phba->pcidev, index), + &phba->sli4_hba.hba_eq_hdl[index]); + } else { free_irq(phba->pcidev->irq, phba); + } + + pci_free_irq_vectors(phba->pcidev); /* Reset interrupt management states */ phba->intr_type = NONE; phba->sli.slistat.sli_intr = 0; - - return; } /** @@ -9399,11 +9880,27 @@ static void lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) { int wait_time = 0; - int fcp_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list); + int nvme_xri_cmpl = 1; + int fcp_xri_cmpl = 1; int els_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list); + int nvmet_xri_cmpl = + list_empty(&phba->sli4_hba.lpfc_abts_nvmet_sgl_list); + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) + fcp_xri_cmpl = + list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list); + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) + nvme_xri_cmpl = + list_empty(&phba->sli4_hba.lpfc_abts_nvme_buf_list); - while (!fcp_xri_cmpl || !els_xri_cmpl) { + while (!fcp_xri_cmpl || !els_xri_cmpl || !nvme_xri_cmpl || + !nvmet_xri_cmpl) { if (wait_time > LPFC_XRI_EXCH_BUSY_WAIT_TMO) { + if (!nvme_xri_cmpl) + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6100 NVME XRI exchange busy " + "wait time: %d seconds.\n", + wait_time/1000); if (!fcp_xri_cmpl) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2877 FCP XRI exchange busy " @@ -9420,10 +9917,19 @@ lpfc_sli4_xri_exchange_busy_wait(struct lpfc_hba *phba) msleep(LPFC_XRI_EXCH_BUSY_WAIT_T1); wait_time += LPFC_XRI_EXCH_BUSY_WAIT_T1; } - fcp_xri_cmpl = - list_empty(&phba->sli4_hba.lpfc_abts_scsi_buf_list); + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) + nvme_xri_cmpl = list_empty( + &phba->sli4_hba.lpfc_abts_nvme_buf_list); + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) + fcp_xri_cmpl = list_empty( + &phba->sli4_hba.lpfc_abts_scsi_buf_list); + els_xri_cmpl = list_empty(&phba->sli4_hba.lpfc_abts_els_sgl_list); + + nvmet_xri_cmpl = + list_empty(&phba->sli4_hba.lpfc_abts_nvmet_sgl_list); } } @@ -9635,10 +10141,35 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) sli4_params->wqsize = bf_get(cfg_wqsize, mbx_sli4_parameters); sli4_params->sgl_pages_max = bf_get(cfg_sgl_page_cnt, mbx_sli4_parameters); + sli4_params->wqpcnt = bf_get(cfg_wqpcnt, mbx_sli4_parameters); sli4_params->sgl_pp_align = bf_get(cfg_sgl_pp_align, mbx_sli4_parameters); phba->sli4_hba.extents_in_use = bf_get(cfg_ext, mbx_sli4_parameters); phba->sli4_hba.rpi_hdrs_in_use = bf_get(cfg_hdrr, mbx_sli4_parameters); + phba->nvme_support = (bf_get(cfg_nvme, mbx_sli4_parameters) && + bf_get(cfg_xib, mbx_sli4_parameters)); + + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_FCP) || + !phba->nvme_support) { + phba->nvme_support = 0; + phba->nvmet_support = 0; + phba->cfg_nvmet_mrq = 0; + phba->cfg_nvme_io_channel = 0; + phba->io_channel_irqs = phba->cfg_fcp_io_channel; + lpfc_printf_log(phba, KERN_ERR, LOG_INIT | LOG_NVME, + "6101 Disabling NVME support: " + "Not supported by firmware: %d %d\n", + bf_get(cfg_nvme, mbx_sli4_parameters), + bf_get(cfg_xib, mbx_sli4_parameters)); + + /* If firmware doesn't support NVME, just use SCSI support */ + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) + return -ENODEV; + phba->cfg_enable_fc4_type = LPFC_ENABLE_FCP; + } + + if (bf_get(cfg_xib, mbx_sli4_parameters) && phba->cfg_suppress_rsp) + phba->sli.sli_flag |= LPFC_SLI_SUPPRESS_RSP; /* Make sure that sge_supp_len can be handled by the driver */ if (sli4_params->sge_supp_len > LPFC_MAX_SGE_SIZE) @@ -9713,14 +10244,6 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid) goto out_disable_pci_dev; } - /* Set up phase-1 common device driver resources */ - error = lpfc_setup_driver_resource_phase1(phba); - if (error) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "1403 Failed to set up driver resource.\n"); - goto out_unset_pci_mem_s3; - } - /* Set up SLI-3 specific device driver resources */ error = lpfc_sli_driver_resource_setup(phba); if (error) { @@ -9876,7 +10399,13 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev) /* Remove FC host and then SCSI host with the physical port */ fc_remove_host(shost); scsi_remove_host(shost); + + /* Perform ndlp cleanup on the physical port. The nvme and nvmet + * localports are destroyed after to cleanup all transport memory. + */ lpfc_cleanup(vport); + lpfc_nvmet_destroy_targetport(phba); + lpfc_nvme_destroy_localport(vport); /* * Bring down the SLI Layer. This step disable all interrupts, @@ -10296,6 +10825,23 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba) } /** + * lpfc_sli4_get_iocb_cnt - Calculate the # of total IOCBs to reserve + * @phba: pointer to lpfc hba data structure. + * + * returns the number of ELS/CT + NVMET IOCBs to reserve + **/ +int +lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba) +{ + int max_xri = lpfc_sli4_get_els_iocb_cnt(phba); + + if (phba->nvmet_support) + max_xri += LPFC_NVMET_BUF_POST; + return max_xri; +} + + +/** * lpfc_write_firmware - attempt to write a firmware image to the port * @fw: pointer to firmware image returned from request_firmware. * @phba: pointer to lpfc hba data structure. @@ -10459,7 +11005,6 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) struct Scsi_Host *shost = NULL; int error; uint32_t cfg_mode, intr_mode; - int adjusted_fcp_io_channel; /* Allocate memory for HBA structure */ phba = lpfc_hba_alloc(pdev); @@ -10484,14 +11029,6 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) goto out_disable_pci_dev; } - /* Set up phase-1 common device driver resources */ - error = lpfc_setup_driver_resource_phase1(phba); - if (error) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "1411 Failed to set up driver resource.\n"); - goto out_unset_pci_mem_s4; - } - /* Set up SLI-4 Specific device driver resources */ error = lpfc_sli4_driver_resource_setup(phba); if (error) { @@ -10550,6 +11087,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* Put device to a known state before enabling interrupt */ lpfc_stop_port(phba); + /* Configure and enable interrupt */ intr_mode = lpfc_sli4_enable_intr(phba, cfg_mode); if (intr_mode == LPFC_INTR_ERROR) { @@ -10559,11 +11097,17 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) goto out_free_sysfs_attr; } /* Default to single EQ for non-MSI-X */ - if (phba->intr_type != MSIX) - adjusted_fcp_io_channel = 1; - else - adjusted_fcp_io_channel = phba->cfg_fcp_io_channel; - phba->cfg_fcp_io_channel = adjusted_fcp_io_channel; + if (phba->intr_type != MSIX) { + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) + phba->cfg_fcp_io_channel = 1; + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + phba->cfg_nvme_io_channel = 1; + if (phba->nvmet_support) + phba->cfg_nvmet_mrq = 1; + } + phba->io_channel_irqs = 1; + } + /* Set up SLI-4 HBA */ if (lpfc_sli4_hba_setup(phba)) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -10579,6 +11123,24 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* Perform post initialization setup */ lpfc_post_init_setup(phba); + /* NVME support in FW earlier in the driver load corrects the + * FC4 type making a check for nvme_support unnecessary. + */ + if ((phba->nvmet_support == 0) && + (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { + /* Create NVME binding with nvme_fc_transport. This + * ensures the vport is initialized. + */ + error = lpfc_nvme_create_localport(vport); + if (error) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6004 NVME registration failed, " + "error x%x\n", + error); + goto out_disable_intr; + } + } + /* check for firmware upgrade or downgrade */ if (phba->cfg_request_firmware_upgrade) lpfc_sli4_request_firmware_update(phba, INT_FW_UPGRADE); @@ -10650,8 +11212,12 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) fc_remove_host(shost); scsi_remove_host(shost); - /* Perform cleanup on the physical port */ + /* Perform ndlp cleanup on the physical port. The nvme and nvmet + * localports are destroyed after to cleanup all transport memory. + */ lpfc_cleanup(vport); + lpfc_nvmet_destroy_targetport(phba); + lpfc_nvme_destroy_localport(vport); /* * Bring down the SLI Layer. This step disables all interrupts, @@ -10669,6 +11235,8 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) * buffers are released to their corresponding pools here. */ lpfc_scsi_free(phba); + lpfc_nvme_free(phba); + lpfc_free_iocb_list(phba); lpfc_sli4_driver_resource_unset(phba); @@ -11314,7 +11882,7 @@ lpfc_sli4_oas_verify(struct lpfc_hba *phba) int lpfc_fof_queue_setup(struct lpfc_hba *phba) { - struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; int rc; rc = lpfc_eq_create(phba, phba->sli4_hba.fof_eq, LPFC_MAX_IMAX); @@ -11333,8 +11901,11 @@ lpfc_fof_queue_setup(struct lpfc_hba *phba) if (rc) goto out_oas_wq; - phba->sli4_hba.oas_cq->pring = &psli->ring[LPFC_FCP_OAS_RING]; - phba->sli4_hba.oas_ring = &psli->ring[LPFC_FCP_OAS_RING]; + /* Bind this CQ/WQ to the NVME ring */ + pring = phba->sli4_hba.oas_wq->pring; + pring->sli.sli4.wqp = + (void *)phba->sli4_hba.oas_wq; + phba->sli4_hba.oas_cq->pring = pring; } return 0; @@ -11391,6 +11962,7 @@ lpfc_fof_queue_create(struct lpfc_hba *phba) goto out_error; phba->sli4_hba.oas_wq = qdesc; + list_add_tail(&qdesc->wq_list, &phba->sli4_hba.lpfc_wq_list); } return 0; @@ -11476,7 +12048,6 @@ static struct miscdevice lpfc_mgmt_dev = { static int __init lpfc_init(void) { - int cpu; int error = 0; printk(LPFC_MODULE_DESC "\n"); @@ -11502,9 +12073,7 @@ lpfc_init(void) /* Initialize in case vector mapping is needed */ lpfc_used_cpu = NULL; - lpfc_present_cpu = 0; - for_each_present_cpu(cpu) - lpfc_present_cpu++; + lpfc_present_cpu = num_present_cpus(); error = pci_register_driver(&lpfc_driver); if (error) { @@ -11550,5 +12119,5 @@ module_init(lpfc_init); module_exit(lpfc_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(LPFC_MODULE_DESC); -MODULE_AUTHOR("Emulex Corporation - tech.support@emulex.com"); +MODULE_AUTHOR("Broadcom"); MODULE_VERSION("0:" LPFC_DRIVER_VERSION); diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h index 2a4e5d21eab2..3b654ad08d1f 100644 --- a/drivers/scsi/lpfc/lpfc_logmsg.h +++ b/drivers/scsi/lpfc/lpfc_logmsg.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2009 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -38,6 +40,10 @@ #define LOG_FIP 0x00020000 /* FIP events */ #define LOG_FCP_UNDER 0x00040000 /* FCP underruns errors */ #define LOG_SCSI_CMD 0x00080000 /* ALL SCSI commands */ +#define LOG_NVME 0x00100000 /* NVME general events. */ +#define LOG_NVME_DISC 0x00200000 /* NVME Discovery/Connect events. */ +#define LOG_NVME_ABTS 0x00400000 /* NVME ABTS events. */ +#define LOG_NVME_IOERR 0x00800000 /* NVME IO Error events. */ #define LOG_ALL_MSG 0xffffffff /* LOG all messages */ #define lpfc_printf_vlog(vport, level, mask, fmt, arg...) \ diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index b234c50c255f..a928f5187fa4 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -954,7 +956,7 @@ lpfc_config_pcb_setup(struct lpfc_hba * phba) pcbp->maxRing = (psli->num_rings - 1); for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; + pring = &psli->sli3_ring[i]; pring->sli.sli3.sizeCiocb = phba->sli_rev == 3 ? SLI3_IOCB_CMD_SIZE : @@ -1217,7 +1219,7 @@ lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb) mb->un.varCfgRing.recvNotify = 1; psli = &phba->sli; - pring = &psli->ring[ring]; + pring = &psli->sli3_ring[ring]; mb->un.varCfgRing.numMask = pring->num_mask; mb->mbxCommand = MBX_CONFIG_RING; mb->mbxOwner = OWN_HOST; @@ -2081,6 +2083,9 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq) if (phba->max_vpi && phba->cfg_enable_npiv) bf_set(lpfc_mbx_rq_ftr_rq_npiv, &mboxq->u.mqe.un.req_ftrs, 1); + if (phba->nvmet_support) + bf_set(lpfc_mbx_rq_ftr_rq_mrqp, &mboxq->u.mqe.un.req_ftrs, 1); + return; } @@ -2434,14 +2439,45 @@ lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox) memset(mbox, 0, sizeof(*mbox)); reg_fcfi = &mbox->u.mqe.un.reg_fcfi; bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_FCFI); - bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi, phba->sli4_hba.hdr_rq->queue_id); - bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID); + if (phba->nvmet_support == 0) { + bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi, + phba->sli4_hba.hdr_rq->queue_id); + /* Match everything - rq_id0 */ + bf_set(lpfc_reg_fcfi_type_match0, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_type_mask0, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_rctl_match0, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_rctl_mask0, reg_fcfi, 0); + + bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, REG_FCF_INVALID_QID); + + /* addr mode is bit wise inverted value of fcf addr_mode */ + bf_set(lpfc_reg_fcfi_mam, reg_fcfi, + (~phba->fcf.addr_mode) & 0x3); + } else { + /* This is ONLY for NVMET MRQ == 1 */ + if (phba->cfg_nvmet_mrq != 1) + return; + + bf_set(lpfc_reg_fcfi_rq_id0, reg_fcfi, + phba->sli4_hba.nvmet_mrq_hdr[0]->queue_id); + /* Match type FCP - rq_id0 */ + bf_set(lpfc_reg_fcfi_type_match0, reg_fcfi, FC_TYPE_FCP); + bf_set(lpfc_reg_fcfi_type_mask0, reg_fcfi, 0xff); + bf_set(lpfc_reg_fcfi_rctl_match0, reg_fcfi, + FC_RCTL_DD_UNSOL_CMD); + + bf_set(lpfc_reg_fcfi_rq_id1, reg_fcfi, + phba->sli4_hba.hdr_rq->queue_id); + /* Match everything else - rq_id1 */ + bf_set(lpfc_reg_fcfi_type_match1, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_type_mask1, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_rctl_match1, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_rctl_mask1, reg_fcfi, 0); + } bf_set(lpfc_reg_fcfi_rq_id2, reg_fcfi, REG_FCF_INVALID_QID); bf_set(lpfc_reg_fcfi_rq_id3, reg_fcfi, REG_FCF_INVALID_QID); bf_set(lpfc_reg_fcfi_info_index, reg_fcfi, phba->fcf.current_rec.fcf_indx); - /* reg_fcf addr mode is bit wise inverted value of fcf addr_mode */ - bf_set(lpfc_reg_fcfi_mam, reg_fcfi, (~phba->fcf.addr_mode) & 0x3); if (phba->fcf.current_rec.vlan_id != LPFC_FCOE_NULL_VID) { bf_set(lpfc_reg_fcfi_vv, reg_fcfi, 1); bf_set(lpfc_reg_fcfi_vlan_tag, reg_fcfi, @@ -2450,6 +2486,70 @@ lpfc_reg_fcfi(struct lpfc_hba *phba, struct lpfcMboxq *mbox) } /** + * lpfc_reg_fcfi_mrq - Initialize the REG_FCFI_MRQ mailbox command + * @phba: pointer to the hba structure containing the FCF index and RQ ID. + * @mbox: pointer to lpfc mbox command to initialize. + * @mode: 0 to register FCFI, 1 to register MRQs + * + * The REG_FCFI_MRQ mailbox command supports Fibre Channel Forwarders (FCFs). + * The SLI Host uses the command to activate an FCF after it has acquired FCF + * information via a READ_FCF mailbox command. This mailbox command also is used + * to indicate where received unsolicited frames from this FCF will be sent. By + * default this routine will set up the FCF to forward all unsolicited frames + * the the RQ ID passed in the @phba. This can be overridden by the caller for + * more complicated setups. + **/ +void +lpfc_reg_fcfi_mrq(struct lpfc_hba *phba, struct lpfcMboxq *mbox, int mode) +{ + struct lpfc_mbx_reg_fcfi_mrq *reg_fcfi; + + /* This is ONLY for MRQ */ + if (phba->cfg_nvmet_mrq <= 1) + return; + + memset(mbox, 0, sizeof(*mbox)); + reg_fcfi = &mbox->u.mqe.un.reg_fcfi_mrq; + bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_REG_FCFI_MRQ); + if (mode == 0) { + bf_set(lpfc_reg_fcfi_mrq_info_index, reg_fcfi, + phba->fcf.current_rec.fcf_indx); + if (phba->fcf.current_rec.vlan_id != LPFC_FCOE_NULL_VID) { + bf_set(lpfc_reg_fcfi_mrq_vv, reg_fcfi, 1); + bf_set(lpfc_reg_fcfi_mrq_vlan_tag, reg_fcfi, + phba->fcf.current_rec.vlan_id); + } + return; + } + + bf_set(lpfc_reg_fcfi_mrq_rq_id0, reg_fcfi, + phba->sli4_hba.nvmet_mrq_hdr[0]->queue_id); + /* Match NVME frames of type FCP (protocol NVME) - rq_id0 */ + bf_set(lpfc_reg_fcfi_mrq_type_match0, reg_fcfi, FC_TYPE_FCP); + bf_set(lpfc_reg_fcfi_mrq_type_mask0, reg_fcfi, 0xff); + bf_set(lpfc_reg_fcfi_mrq_rctl_match0, reg_fcfi, FC_RCTL_DD_UNSOL_CMD); + bf_set(lpfc_reg_fcfi_mrq_rctl_mask0, reg_fcfi, 0xff); + bf_set(lpfc_reg_fcfi_mrq_ptc0, reg_fcfi, 1); + bf_set(lpfc_reg_fcfi_mrq_pt0, reg_fcfi, 1); + + bf_set(lpfc_reg_fcfi_mrq_policy, reg_fcfi, 3); /* NVME connection id */ + bf_set(lpfc_reg_fcfi_mrq_mode, reg_fcfi, 1); + bf_set(lpfc_reg_fcfi_mrq_filter, reg_fcfi, 1); /* rq_id0 */ + bf_set(lpfc_reg_fcfi_mrq_npairs, reg_fcfi, phba->cfg_nvmet_mrq); + + bf_set(lpfc_reg_fcfi_mrq_rq_id1, reg_fcfi, + phba->sli4_hba.hdr_rq->queue_id); + /* Match everything - rq_id1 */ + bf_set(lpfc_reg_fcfi_mrq_type_match1, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_mrq_type_mask1, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_mrq_rctl_match1, reg_fcfi, 0); + bf_set(lpfc_reg_fcfi_mrq_rctl_mask1, reg_fcfi, 0); + + bf_set(lpfc_reg_fcfi_mrq_rq_id2, reg_fcfi, REG_FCF_INVALID_QID); + bf_set(lpfc_reg_fcfi_mrq_rq_id3, reg_fcfi, REG_FCF_INVALID_QID); +} + +/** * lpfc_unreg_fcfi - Initialize the UNREG_FCFI mailbox command * @mbox: pointer to lpfc mbox command to initialize. * @fcfi: FCFI to be unregistered. diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 3fa65338d3f5..c61d8d692ede 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2014 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -24,10 +26,12 @@ #include <linux/pci.h> #include <linux/interrupt.h> +#include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> -#include <scsi/scsi.h> +#include <linux/nvme-fc-driver.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -35,8 +39,10 @@ #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" +#include "lpfc_nvmet.h" #include "lpfc_crtn.h" #include "lpfc_logmsg.h" @@ -66,7 +72,7 @@ lpfc_mem_alloc_active_rrq_pool_s4(struct lpfc_hba *phba) { * lpfc_mem_alloc - create and allocate all PCI and memory pools * @phba: HBA to allocate pools for * - * Description: Creates and allocates PCI pools lpfc_scsi_dma_buf_pool, + * Description: Creates and allocates PCI pools lpfc_sg_dma_buf_pool, * lpfc_mbuf_pool, lpfc_hrb_pool. Creates and allocates kmalloc-backed mempools * for LPFC_MBOXQ_t and lpfc_nodelist. Also allocates the VPI bitmask. * @@ -90,21 +96,23 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align) else i = SLI4_PAGE_SIZE; - phba->lpfc_scsi_dma_buf_pool = - pci_pool_create("lpfc_scsi_dma_buf_pool", - phba->pcidev, - phba->cfg_sg_dma_buf_size, - i, - 0); + phba->lpfc_sg_dma_buf_pool = + pci_pool_create("lpfc_sg_dma_buf_pool", + phba->pcidev, + phba->cfg_sg_dma_buf_size, + i, 0); + if (!phba->lpfc_sg_dma_buf_pool) + goto fail; + } else { - phba->lpfc_scsi_dma_buf_pool = - pci_pool_create("lpfc_scsi_dma_buf_pool", - phba->pcidev, phba->cfg_sg_dma_buf_size, - align, 0); - } + phba->lpfc_sg_dma_buf_pool = + pci_pool_create("lpfc_sg_dma_buf_pool", + phba->pcidev, phba->cfg_sg_dma_buf_size, + align, 0); - if (!phba->lpfc_scsi_dma_buf_pool) - goto fail; + if (!phba->lpfc_sg_dma_buf_pool) + goto fail; + } phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev, LPFC_BPL_SIZE, @@ -170,12 +178,15 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align) LPFC_DEVICE_DATA_POOL_SIZE, sizeof(struct lpfc_device_data)); if (!phba->device_data_mem_pool) - goto fail_free_hrb_pool; + goto fail_free_drb_pool; } else { phba->device_data_mem_pool = NULL; } return 0; +fail_free_drb_pool: + pci_pool_destroy(phba->lpfc_drb_pool); + phba->lpfc_drb_pool = NULL; fail_free_hrb_pool: pci_pool_destroy(phba->lpfc_hrb_pool); phba->lpfc_hrb_pool = NULL; @@ -197,8 +208,8 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align) pci_pool_destroy(phba->lpfc_mbuf_pool); phba->lpfc_mbuf_pool = NULL; fail_free_dma_buf_pool: - pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool); - phba->lpfc_scsi_dma_buf_pool = NULL; + pci_pool_destroy(phba->lpfc_sg_dma_buf_pool); + phba->lpfc_sg_dma_buf_pool = NULL; fail: return -ENOMEM; } @@ -227,6 +238,9 @@ lpfc_mem_free(struct lpfc_hba *phba) if (phba->lpfc_hrb_pool) pci_pool_destroy(phba->lpfc_hrb_pool); phba->lpfc_hrb_pool = NULL; + if (phba->txrdy_payload_pool) + pci_pool_destroy(phba->txrdy_payload_pool); + phba->txrdy_payload_pool = NULL; if (phba->lpfc_hbq_pool) pci_pool_destroy(phba->lpfc_hbq_pool); @@ -258,8 +272,8 @@ lpfc_mem_free(struct lpfc_hba *phba) phba->lpfc_mbuf_pool = NULL; /* Free DMA buffer memory pool */ - pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool); - phba->lpfc_scsi_dma_buf_pool = NULL; + pci_pool_destroy(phba->lpfc_sg_dma_buf_pool); + phba->lpfc_sg_dma_buf_pool = NULL; /* Free Device Data memory pool */ if (phba->device_data_mem_pool) { @@ -282,7 +296,7 @@ lpfc_mem_free(struct lpfc_hba *phba) * @phba: HBA to free memory for * * Description: Free memory from PCI and driver memory pools and also those - * used : lpfc_scsi_dma_buf_pool, lpfc_mbuf_pool, lpfc_hrb_pool. Frees + * used : lpfc_sg_dma_buf_pool, lpfc_mbuf_pool, lpfc_hrb_pool. Frees * kmalloc-backed mempools for LPFC_MBOXQ_t and lpfc_nodelist. Also frees * the VPI bitmask. * @@ -431,6 +445,44 @@ lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma) } /** + * lpfc_nvmet_buf_alloc - Allocate an nvmet_buf from the + * lpfc_sg_dma_buf_pool PCI pool + * @phba: HBA which owns the pool to allocate from + * @mem_flags: indicates if this is a priority (MEM_PRI) allocation + * @handle: used to return the DMA-mapped address of the nvmet_buf + * + * Description: Allocates a DMA-mapped buffer from the lpfc_sg_dma_buf_pool + * PCI pool. Allocates from generic pci_pool_alloc function. + * + * Returns: + * pointer to the allocated nvmet_buf on success + * NULL on failure + **/ +void * +lpfc_nvmet_buf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle) +{ + void *ret; + + ret = pci_pool_alloc(phba->lpfc_sg_dma_buf_pool, GFP_KERNEL, handle); + return ret; +} + +/** + * lpfc_nvmet_buf_free - Free an nvmet_buf from the lpfc_sg_dma_buf_pool + * PCI pool + * @phba: HBA which owns the pool to return to + * @virt: nvmet_buf to free + * @dma: the DMA-mapped address of the lpfc_sg_dma_buf_pool to be freed + * + * Returns: None + **/ +void +lpfc_nvmet_buf_free(struct lpfc_hba *phba, void *virt, dma_addr_t dma) +{ + pci_pool_free(phba->lpfc_sg_dma_buf_pool, virt, dma); +} + +/** * lpfc_els_hbq_alloc - Allocate an HBQ buffer * @phba: HBA to allocate HBQ buffer for * @@ -458,7 +510,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba) kfree(hbqbp); return NULL; } - hbqbp->size = LPFC_BPL_SIZE; + hbqbp->total_size = LPFC_BPL_SIZE; return hbqbp; } @@ -518,7 +570,7 @@ lpfc_sli4_rb_alloc(struct lpfc_hba *phba) kfree(dma_buf); return NULL; } - dma_buf->size = LPFC_BPL_SIZE; + dma_buf->total_size = LPFC_DATA_BUF_SIZE; return dma_buf; } @@ -540,7 +592,134 @@ lpfc_sli4_rb_free(struct lpfc_hba *phba, struct hbq_dmabuf *dmab) pci_pool_free(phba->lpfc_hrb_pool, dmab->hbuf.virt, dmab->hbuf.phys); pci_pool_free(phba->lpfc_drb_pool, dmab->dbuf.virt, dmab->dbuf.phys); kfree(dmab); - return; +} + +/** + * lpfc_sli4_nvmet_alloc - Allocate an SLI4 Receive buffer + * @phba: HBA to allocate a receive buffer for + * + * Description: Allocates a DMA-mapped receive buffer from the lpfc_hrb_pool PCI + * pool along a non-DMA-mapped container for it. + * + * Notes: Not interrupt-safe. Must be called with no locks held. + * + * Returns: + * pointer to HBQ on success + * NULL on failure + **/ +struct rqb_dmabuf * +lpfc_sli4_nvmet_alloc(struct lpfc_hba *phba) +{ + struct rqb_dmabuf *dma_buf; + struct lpfc_iocbq *nvmewqe; + union lpfc_wqe128 *wqe; + + dma_buf = kzalloc(sizeof(struct rqb_dmabuf), GFP_KERNEL); + if (!dma_buf) + return NULL; + + dma_buf->hbuf.virt = pci_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL, + &dma_buf->hbuf.phys); + if (!dma_buf->hbuf.virt) { + kfree(dma_buf); + return NULL; + } + dma_buf->dbuf.virt = pci_pool_alloc(phba->lpfc_drb_pool, GFP_KERNEL, + &dma_buf->dbuf.phys); + if (!dma_buf->dbuf.virt) { + pci_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt, + dma_buf->hbuf.phys); + kfree(dma_buf); + return NULL; + } + dma_buf->total_size = LPFC_DATA_BUF_SIZE; + + dma_buf->context = kzalloc(sizeof(struct lpfc_nvmet_rcv_ctx), + GFP_KERNEL); + if (!dma_buf->context) { + pci_pool_free(phba->lpfc_drb_pool, dma_buf->dbuf.virt, + dma_buf->dbuf.phys); + pci_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt, + dma_buf->hbuf.phys); + kfree(dma_buf); + return NULL; + } + + dma_buf->iocbq = lpfc_sli_get_iocbq(phba); + dma_buf->iocbq->iocb_flag = LPFC_IO_NVMET; + if (!dma_buf->iocbq) { + kfree(dma_buf->context); + pci_pool_free(phba->lpfc_drb_pool, dma_buf->dbuf.virt, + dma_buf->dbuf.phys); + pci_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt, + dma_buf->hbuf.phys); + kfree(dma_buf); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME, + "2621 Ran out of nvmet iocb/WQEs\n"); + return NULL; + } + nvmewqe = dma_buf->iocbq; + wqe = (union lpfc_wqe128 *)&nvmewqe->wqe; + /* Initialize WQE */ + memset(wqe, 0, sizeof(union lpfc_wqe)); + /* Word 7 */ + bf_set(wqe_ct, &wqe->generic.wqe_com, SLI4_CT_RPI); + bf_set(wqe_class, &wqe->generic.wqe_com, CLASS3); + bf_set(wqe_pu, &wqe->generic.wqe_com, 1); + /* Word 10 */ + bf_set(wqe_nvme, &wqe->fcp_tsend.wqe_com, 1); + bf_set(wqe_ebde_cnt, &wqe->generic.wqe_com, 0); + bf_set(wqe_qosd, &wqe->generic.wqe_com, 0); + + dma_buf->iocbq->context1 = NULL; + spin_lock(&phba->sli4_hba.sgl_list_lock); + dma_buf->sglq = __lpfc_sli_get_nvmet_sglq(phba, dma_buf->iocbq); + spin_unlock(&phba->sli4_hba.sgl_list_lock); + if (!dma_buf->sglq) { + lpfc_sli_release_iocbq(phba, dma_buf->iocbq); + kfree(dma_buf->context); + pci_pool_free(phba->lpfc_drb_pool, dma_buf->dbuf.virt, + dma_buf->dbuf.phys); + pci_pool_free(phba->lpfc_hrb_pool, dma_buf->hbuf.virt, + dma_buf->hbuf.phys); + kfree(dma_buf); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME, + "6132 Ran out of nvmet XRIs\n"); + return NULL; + } + return dma_buf; +} + +/** + * lpfc_sli4_nvmet_free - Frees a receive buffer + * @phba: HBA buffer was allocated for + * @dmab: DMA Buffer container returned by lpfc_sli4_rbq_alloc + * + * Description: Frees both the container and the DMA-mapped buffers returned by + * lpfc_sli4_nvmet_alloc. + * + * Notes: Can be called with or without locks held. + * + * Returns: None + **/ +void +lpfc_sli4_nvmet_free(struct lpfc_hba *phba, struct rqb_dmabuf *dmab) +{ + unsigned long flags; + + __lpfc_clear_active_sglq(phba, dmab->sglq->sli4_lxritag); + dmab->sglq->state = SGL_FREED; + dmab->sglq->ndlp = NULL; + + spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock, flags); + list_add_tail(&dmab->sglq->list, &phba->sli4_hba.lpfc_nvmet_sgl_list); + spin_unlock_irqrestore(&phba->sli4_hba.sgl_list_lock, flags); + + lpfc_sli_release_iocbq(phba, dmab->iocbq); + kfree(dmab->context); + pci_pool_free(phba->lpfc_hrb_pool, dmab->hbuf.virt, dmab->hbuf.phys); + pci_pool_free(phba->lpfc_drb_pool, dmab->dbuf.virt, dmab->dbuf.phys); + kfree(dmab); } /** @@ -565,13 +744,13 @@ lpfc_in_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp) return; if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { + hbq_entry = container_of(mp, struct hbq_dmabuf, dbuf); /* Check whether HBQ is still in use */ spin_lock_irqsave(&phba->hbalock, flags); if (!phba->hbq_in_use) { spin_unlock_irqrestore(&phba->hbalock, flags); return; } - hbq_entry = container_of(mp, struct hbq_dmabuf, dbuf); list_del(&hbq_entry->dbuf.list); if (hbq_entry->tag == -1) { (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer) @@ -586,3 +765,48 @@ lpfc_in_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp) } return; } + +/** + * lpfc_rq_buf_free - Free a RQ DMA buffer + * @phba: HBA buffer is associated with + * @mp: Buffer to free + * + * Description: Frees the given DMA buffer in the appropriate way given by + * reposting it to its associated RQ so it can be reused. + * + * Notes: Takes phba->hbalock. Can be called with or without other locks held. + * + * Returns: None + **/ +void +lpfc_rq_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp) +{ + struct lpfc_rqb *rqbp; + struct lpfc_rqe hrqe; + struct lpfc_rqe drqe; + struct rqb_dmabuf *rqb_entry; + unsigned long flags; + int rc; + + if (!mp) + return; + + rqb_entry = container_of(mp, struct rqb_dmabuf, hbuf); + rqbp = rqb_entry->hrq->rqbp; + + spin_lock_irqsave(&phba->hbalock, flags); + list_del(&rqb_entry->hbuf.list); + hrqe.address_lo = putPaddrLow(rqb_entry->hbuf.phys); + hrqe.address_hi = putPaddrHigh(rqb_entry->hbuf.phys); + drqe.address_lo = putPaddrLow(rqb_entry->dbuf.phys); + drqe.address_hi = putPaddrHigh(rqb_entry->dbuf.phys); + rc = lpfc_sli4_rq_put(rqb_entry->hrq, rqb_entry->drq, &hrqe, &drqe); + if (rc < 0) { + (rqbp->rqb_free_buffer)(phba, rqb_entry); + } else { + list_add_tail(&rqb_entry->hbuf.list, &rqbp->rqb_buffer_list); + rqbp->buffer_count++; + } + + spin_unlock_irqrestore(&phba->hbalock, flags); +} diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h index f2b1bbcb196f..b93e78f671fb 100644 --- a/drivers/scsi/lpfc/lpfc_nl.h +++ b/drivers/scsi/lpfc/lpfc_nl.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2010 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 56a3df4fddb0..061626bdf701 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -28,6 +30,9 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> + +#include <linux/nvme-fc-driver.h> #include "lpfc_hw4.h" #include "lpfc_hw.h" @@ -35,8 +40,9 @@ #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" #include "lpfc_logmsg.h" #include "lpfc_crtn.h" #include "lpfc_vport.h" @@ -204,10 +210,11 @@ int lpfc_els_abort(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { LIST_HEAD(abort_list); - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; struct lpfc_iocbq *iocb, *next_iocb; + pring = lpfc_phba_elsring(phba); + /* Abort outstanding I/O on NPort <nlp_DID> */ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_DISCOVERY, "2819 Abort outstanding I/O on NPort x%x " @@ -283,6 +290,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, uint32_t ed_tov; LPFC_MBOXQ_t *mbox; struct ls_rjt stat; + uint32_t vid, flag; int rc; memset(&stat, 0, sizeof (struct ls_rjt)); @@ -418,6 +426,15 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_can_disctmo(vport); } + ndlp->nlp_flag &= ~NLP_SUPPRESS_RSP; + if ((phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) && + sp->cmn.valid_vendor_ver_level) { + vid = be32_to_cpu(sp->un.vv.vid); + flag = be32_to_cpu(sp->un.vv.flags); + if ((vid == LPFC_VV_EMLX_ID) && (flag & LPFC_VV_SUPPRESS_RSP)) + ndlp->nlp_flag |= NLP_SUPPRESS_RSP; + } + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) goto out; @@ -707,6 +724,7 @@ static void lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *cmdiocb) { + struct lpfc_hba *phba = vport->phba; struct lpfc_dmabuf *pcmd; uint32_t *lp; PRLI *npr; @@ -720,16 +738,32 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; ndlp->nlp_flag &= ~NLP_FIRSTBURST; - if (npr->prliType == PRLI_FCP_TYPE) { - if (npr->initiatorFunc) - ndlp->nlp_type |= NLP_FCP_INITIATOR; + if ((npr->prliType == PRLI_FCP_TYPE) || + (npr->prliType == PRLI_NVME_TYPE)) { + if (npr->initiatorFunc) { + if (npr->prliType == PRLI_FCP_TYPE) + ndlp->nlp_type |= NLP_FCP_INITIATOR; + if (npr->prliType == PRLI_NVME_TYPE) + ndlp->nlp_type |= NLP_NVME_INITIATOR; + } if (npr->targetFunc) { - ndlp->nlp_type |= NLP_FCP_TARGET; + if (npr->prliType == PRLI_FCP_TYPE) + ndlp->nlp_type |= NLP_FCP_TARGET; + if (npr->prliType == PRLI_NVME_TYPE) + ndlp->nlp_type |= NLP_NVME_TARGET; if (npr->writeXferRdyDis) ndlp->nlp_flag |= NLP_FIRSTBURST; } if (npr->Retry) ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + + /* If this driver is in nvme target mode, set the ndlp's fc4 + * type to NVME provided the PRLI response claims NVME FC4 + * type. Target mode does not issue gft_id so doesn't get + * the fc4 type set until now. + */ + if ((phba->nvmet_support) && (npr->prliType == PRLI_NVME_TYPE)) + ndlp->nlp_fc4_type |= NLP_FC4_NVME; } if (rport) { /* We need to update the rport role values */ @@ -743,7 +777,8 @@ lpfc_rcv_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, "rport rolechg: role:x%x did:x%x flg:x%x", roles, ndlp->nlp_DID, ndlp->nlp_flag); - fc_remote_port_rolechg(rport, roles); + if (phba->cfg_enable_fc4_type != LPFC_ENABLE_NVME) + fc_remote_port_rolechg(rport, roles); } } @@ -1026,6 +1061,7 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, *rspiocb; struct lpfc_dmabuf *pcmd, *prsp, *mp; uint32_t *lp; + uint32_t vid, flag; IOCB_t *irsp; struct serv_parm *sp; uint32_t ed_tov; @@ -1094,6 +1130,16 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport, ed_tov = (phba->fc_edtov + 999999) / 1000000; } + ndlp->nlp_flag &= ~NLP_SUPPRESS_RSP; + if ((phba->sli.sli_flag & LPFC_SLI_SUPPRESS_RSP) && + sp->cmn.valid_vendor_ver_level) { + vid = be32_to_cpu(sp->un.vv.vid); + flag = be32_to_cpu(sp->un.vv.flags); + if ((vid == LPFC_VV_EMLX_ID) && + (flag & LPFC_VV_SUPPRESS_RSP)) + ndlp->nlp_flag |= NLP_SUPPRESS_RSP; + } + /* * Use the larger EDTOV * RATOV = 2 * EDTOV for pt-to-pt @@ -1489,8 +1535,38 @@ lpfc_rcv_prli_reglogin_issue(struct lpfc_vport *vport, uint32_t evt) { struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; + struct ls_rjt stat; + + if (vport->phba->nvmet_support) { + /* NVME Target mode. Handle and respond to the PRLI and + * transition to UNMAPPED provided the RPI has completed + * registration. + */ + if (ndlp->nlp_flag & NLP_RPI_REGISTERED) { + lpfc_rcv_prli(vport, ndlp, cmdiocb); + lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp); + lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); + } else { + /* RPI registration has not completed. Reject the PRLI + * to prevent an illegal state transition when the + * rpi registration does complete. + */ + lpfc_printf_vlog(vport, KERN_WARNING, LOG_NVME_DISC, + "6115 NVMET ndlp rpi %d state " + "unknown, state x%x flags x%08x\n", + ndlp->nlp_rpi, ndlp->nlp_state, + ndlp->nlp_flag); + memset(&stat, 0, sizeof(struct ls_rjt)); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS; + lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, + ndlp, NULL); + } + } else { + /* Initiator mode. */ + lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp); + } - lpfc_els_rsp_prli_acc(vport, cmdiocb, ndlp); return ndlp->nlp_state; } @@ -1573,9 +1649,11 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, uint32_t evt) { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_hba *phba = vport->phba; LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg; MAILBOX_t *mb = &pmb->u.mb; uint32_t did = mb->un.varWords[1]; + int rc = 0; if (mb->mbxStatus) { /* RegLogin failed */ @@ -1610,19 +1688,55 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_vport *vport, } /* SLI4 ports have preallocated logical rpis. */ - if (vport->phba->sli_rev < LPFC_SLI_REV4) + if (phba->sli_rev < LPFC_SLI_REV4) ndlp->nlp_rpi = mb->un.varWords[0]; ndlp->nlp_flag |= NLP_RPI_REGISTERED; /* Only if we are not a fabric nport do we issue PRLI */ - if (!(ndlp->nlp_type & NLP_FABRIC)) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + "3066 RegLogin Complete on x%x x%x x%x\n", + did, ndlp->nlp_type, ndlp->nlp_fc4_type); + if (!(ndlp->nlp_type & NLP_FABRIC) && + (phba->nvmet_support == 0)) { + /* The driver supports FCP and NVME concurrently. If the + * ndlp's nlp_fc4_type is still zero, the driver doesn't + * know what PRLI to send yet. Figure that out now and + * call PRLI depending on the outcome. + */ + if (vport->fc_flag & FC_PT2PT) { + /* If we are pt2pt, there is no Fabric to determine + * the FC4 type of the remote nport. So if NVME + * is configured try it. + */ + ndlp->nlp_fc4_type |= NLP_FC4_FCP; + if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME)) { + ndlp->nlp_fc4_type |= NLP_FC4_NVME; + /* We need to update the localport also */ + lpfc_nvme_update_localport(vport); + } + + } else if (ndlp->nlp_fc4_type == 0) { + rc = lpfc_ns_cmd(vport, SLI_CTNS_GFT_ID, + 0, ndlp->nlp_DID); + return ndlp->nlp_state; + } + ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PRLI_ISSUE); lpfc_issue_els_prli(vport, ndlp, 0); } else { - ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE; - lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); + if ((vport->fc_flag & FC_PT2PT) && phba->nvmet_support) + phba->targetport->port_id = vport->fc_myDID; + + /* Only Fabric ports should transition. NVME target + * must complete PRLI. + */ + if (ndlp->nlp_type & NLP_FABRIC) { + ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); + } } return ndlp->nlp_state; } @@ -1663,7 +1777,14 @@ lpfc_device_recov_reglogin_issue(struct lpfc_vport *vport, ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); spin_lock_irq(shost->host_lock); - ndlp->nlp_flag |= NLP_IGNR_REG_CMPL; + + /* If we are a target we won't immediately transition into PRLI, + * so if REG_LOGIN already completed we don't need to ignore it. + */ + if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED) || + !vport->phba->nvmet_support) + ndlp->nlp_flag |= NLP_IGNR_REG_CMPL; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); spin_unlock_irq(shost->host_lock); lpfc_disc_set_adisc(vport, ndlp); @@ -1739,10 +1860,23 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_hba *phba = vport->phba; IOCB_t *irsp; PRLI *npr; + struct lpfc_nvme_prli *nvpr; + void *temp_ptr; cmdiocb = (struct lpfc_iocbq *) arg; rspiocb = cmdiocb->context_un.rsp_iocb; - npr = (PRLI *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + + /* A solicited PRLI is either FCP or NVME. The PRLI cmd/rsp + * format is different so NULL the two PRLI types so that the + * driver correctly gets the correct context. + */ + npr = NULL; + nvpr = NULL; + temp_ptr = lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + if (cmdiocb->iocb_flag & LPFC_PRLI_FCP_REQ) + npr = (PRLI *) temp_ptr; + else if (cmdiocb->iocb_flag & LPFC_PRLI_NVME_REQ) + nvpr = (struct lpfc_nvme_prli *) temp_ptr; irsp = &rspiocb->iocb; if (irsp->ulpStatus) { @@ -1750,7 +1884,21 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, vport->cfg_restrict_login) { goto out; } + + /* The LS Req had some error. Don't let this be a + * target. + */ + if ((ndlp->fc4_prli_sent == 1) && + (ndlp->nlp_state == NLP_STE_PRLI_ISSUE) && + (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_FCP_INITIATOR))) + /* The FCP PRLI completed successfully but + * the NVME PRLI failed. Since they are sent in + * succession, allow the FCP to complete. + */ + goto out_err; + ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE; + ndlp->nlp_type |= NLP_FCP_INITIATOR; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); return ndlp->nlp_state; } @@ -1758,9 +1906,16 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, /* Check out PRLI rsp */ ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_fcp_info &= ~NLP_FCP_2_DEVICE; + + /* NVME or FCP first burst must be negotiated for each PRLI. */ ndlp->nlp_flag &= ~NLP_FIRSTBURST; - if ((npr->acceptRspCode == PRLI_REQ_EXECUTED) && + ndlp->nvme_fb_size = 0; + if (npr && (npr->acceptRspCode == PRLI_REQ_EXECUTED) && (npr->prliType == PRLI_FCP_TYPE)) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6028 FCP NPR PRLI Cmpl Init %d Target %d\n", + npr->initiatorFunc, + npr->targetFunc); if (npr->initiatorFunc) ndlp->nlp_type |= NLP_FCP_INITIATOR; if (npr->targetFunc) { @@ -1770,6 +1925,49 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } if (npr->Retry) ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + + /* PRLI completed. Decrement count. */ + ndlp->fc4_prli_sent--; + } else if (nvpr && + (bf_get_be32(prli_acc_rsp_code, nvpr) == + PRLI_REQ_EXECUTED) && + (bf_get_be32(prli_type_code, nvpr) == + PRLI_NVME_TYPE)) { + + /* Complete setting up the remote ndlp personality. */ + if (bf_get_be32(prli_init, nvpr)) + ndlp->nlp_type |= NLP_NVME_INITIATOR; + + /* Target driver cannot solicit NVME FB. */ + if (bf_get_be32(prli_tgt, nvpr)) { + ndlp->nlp_type |= NLP_NVME_TARGET; + if ((bf_get_be32(prli_fba, nvpr) == 1) && + (bf_get_be32(prli_fb_sz, nvpr) > 0) && + (phba->cfg_nvme_enable_fb) && + (!phba->nvmet_support)) { + /* Both sides support FB. The target's first + * burst size is a 512 byte encoded value. + */ + ndlp->nlp_flag |= NLP_FIRSTBURST; + ndlp->nvme_fb_size = bf_get_be32(prli_fb_sz, + nvpr); + } + } + + if (bf_get_be32(prli_recov, nvpr)) + ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6029 NVME PRLI Cmpl w1 x%08x " + "w4 x%08x w5 x%08x flag x%x, " + "fcp_info x%x nlp_type x%x\n", + be32_to_cpu(nvpr->word1), + be32_to_cpu(nvpr->word4), + be32_to_cpu(nvpr->word5), + ndlp->nlp_flag, ndlp->nlp_fcp_info, + ndlp->nlp_type); + /* PRLI completed. Decrement count. */ + ndlp->fc4_prli_sent--; } if (!(ndlp->nlp_type & NLP_FCP_TARGET) && (vport->port_type == LPFC_NPIV_PORT) && @@ -1785,11 +1983,24 @@ out: return ndlp->nlp_state; } - ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE; - if (ndlp->nlp_type & NLP_FCP_TARGET) - lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE); - else - lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); +out_err: + /* The ndlp state cannot move to MAPPED or UNMAPPED before all PRLIs + * are complete. + */ + if (ndlp->fc4_prli_sent == 0) { + ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE; + if (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET)) + lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE); + else + lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); + } else + lpfc_printf_vlog(vport, + KERN_INFO, LOG_ELS, + "3067 PRLI's still outstanding " + "on x%06x - count %d, Pend Node Mode " + "transition...\n", + ndlp->nlp_DID, ndlp->fc4_prli_sent); + return ndlp->nlp_state; } @@ -2104,7 +2315,7 @@ lpfc_rcv_prlo_mapped_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; /* flush the target */ - lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], + lpfc_sli_abort_iocb(vport, &phba->sli.sli3_ring[LPFC_FCP_RING], ndlp->nlp_sid, 0, LPFC_CTX_TGT); /* Treat like rcv logo */ diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c new file mode 100644 index 000000000000..609a908ea9db --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -0,0 +1,2464 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * Copyright (C) 2004-2016 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.broadcom.com * + * Portions Copyright (C) 2004-2005 Christoph Hellwig * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + ********************************************************************/ +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/unaligned.h> +#include <linux/crc-t10dif.h> +#include <net/checksum.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> + +#include <linux/nvme.h> +#include <linux/nvme-fc-driver.h> +#include <linux/nvme-fc.h> +#include "lpfc_version.h" +#include "lpfc_hw4.h" +#include "lpfc_hw.h" +#include "lpfc_sli.h" +#include "lpfc_sli4.h" +#include "lpfc_nl.h" +#include "lpfc_disc.h" +#include "lpfc.h" +#include "lpfc_nvme.h" +#include "lpfc_scsi.h" +#include "lpfc_logmsg.h" +#include "lpfc_crtn.h" +#include "lpfc_vport.h" +#include "lpfc_debugfs.h" + +/* NVME initiator-based functions */ + +static struct lpfc_nvme_buf * +lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp); + +static void +lpfc_release_nvme_buf(struct lpfc_hba *, struct lpfc_nvme_buf *); + + +/** + * lpfc_nvme_create_queue - + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @qidx: An cpu index used to affinitize IO queues and MSIX vectors. + * @handle: An opaque driver handle used in follow-up calls. + * + * Driver registers this routine to preallocate and initialize any + * internal data structures to bind the @qidx to its internal IO queues. + * A hardware queue maps (qidx) to a specific driver MSI-X vector/EQ/CQ/WQ. + * + * Return value : + * 0 - Success + * -EINVAL - Unsupported input value. + * -ENOMEM - Could not alloc necessary memory + **/ +static int +lpfc_nvme_create_queue(struct nvme_fc_local_port *pnvme_lport, + unsigned int qidx, u16 qsize, + void **handle) +{ + struct lpfc_nvme_lport *lport; + struct lpfc_vport *vport; + struct lpfc_nvme_qhandle *qhandle; + char *str; + + lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + vport = lport->vport; + qhandle = kzalloc(sizeof(struct lpfc_nvme_qhandle), GFP_KERNEL); + if (qhandle == NULL) + return -ENOMEM; + + qhandle->cpu_id = smp_processor_id(); + qhandle->qidx = qidx; + /* + * NVME qidx == 0 is the admin queue, so both admin queue + * and first IO queue will use MSI-X vector and associated + * EQ/CQ/WQ at index 0. After that they are sequentially assigned. + */ + if (qidx) { + str = "IO "; /* IO queue */ + qhandle->index = ((qidx - 1) % + vport->phba->cfg_nvme_io_channel); + } else { + str = "ADM"; /* Admin queue */ + qhandle->index = qidx; + } + + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6073 Binding %s HdwQueue %d (cpu %d) to " + "io_channel %d qhandle %p\n", str, + qidx, qhandle->cpu_id, qhandle->index, qhandle); + *handle = (void *)qhandle; + return 0; +} + +/** + * lpfc_nvme_delete_queue - + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @qidx: An cpu index used to affinitize IO queues and MSIX vectors. + * @handle: An opaque driver handle from lpfc_nvme_create_queue + * + * Driver registers this routine to free + * any internal data structures to bind the @qidx to its internal + * IO queues. + * + * Return value : + * 0 - Success + * TODO: What are the failure codes. + **/ +static void +lpfc_nvme_delete_queue(struct nvme_fc_local_port *pnvme_lport, + unsigned int qidx, + void *handle) +{ + struct lpfc_nvme_lport *lport; + struct lpfc_vport *vport; + + lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + vport = lport->vport; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, + "6001 ENTER. lpfc_pnvme %p, qidx x%xi qhandle %p\n", + lport, qidx, handle); + kfree(handle); +} + +static void +lpfc_nvme_localport_delete(struct nvme_fc_local_port *localport) +{ + struct lpfc_nvme_lport *lport = localport->private; + + /* release any threads waiting for the unreg to complete */ + complete(&lport->lport_unreg_done); +} + +/* lpfc_nvme_remoteport_delete + * + * @remoteport: Pointer to an nvme transport remoteport instance. + * + * This is a template downcall. NVME transport calls this function + * when it has completed the unregistration of a previously + * registered remoteport. + * + * Return value : + * None + */ +void +lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) +{ + struct lpfc_nvme_rport *rport = remoteport->private; + struct lpfc_vport *vport; + struct lpfc_nodelist *ndlp; + + ndlp = rport->ndlp; + if (!ndlp) + goto rport_err; + + vport = ndlp->vport; + if (!vport) + goto rport_err; + + /* Remove this rport from the lport's list - memory is owned by the + * transport. Remove the ndlp reference for the NVME transport before + * calling state machine to remove the node, this is devloss = 0 + * semantics. + */ + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6146 remoteport delete complete %p\n", + remoteport); + list_del(&rport->list); + lpfc_nlp_put(ndlp); + + rport_err: + /* This call has to execute as long as the rport is valid. + * Release any threads waiting for the unreg to complete. + */ + complete(&rport->rport_unreg_done); +} + +static void +lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, + struct lpfc_wcqe_complete *wcqe) +{ + struct lpfc_vport *vport = cmdwqe->vport; + uint32_t status; + struct nvmefc_ls_req *pnvme_lsreq; + struct lpfc_dmabuf *buf_ptr; + struct lpfc_nodelist *ndlp; + + vport->phba->fc4NvmeLsCmpls++; + + pnvme_lsreq = (struct nvmefc_ls_req *)cmdwqe->context2; + status = bf_get(lpfc_wcqe_c_status, wcqe) & LPFC_IOCB_STATUS_MASK; + ndlp = (struct lpfc_nodelist *)cmdwqe->context1; + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6047 nvme cmpl Enter " + "Data %p DID %x Xri: %x status %x cmd:%p lsreg:%p " + "bmp:%p ndlp:%p\n", + pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0, + cmdwqe->sli4_xritag, status, + cmdwqe, pnvme_lsreq, cmdwqe->context3, ndlp); + + lpfc_nvmeio_data(phba, "NVME LS CMPL: xri x%x stat x%x parm x%x\n", + cmdwqe->sli4_xritag, status, wcqe->parameter); + + if (cmdwqe->context3) { + buf_ptr = (struct lpfc_dmabuf *)cmdwqe->context3; + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + cmdwqe->context3 = NULL; + } + if (pnvme_lsreq->done) + pnvme_lsreq->done(pnvme_lsreq, status); + else + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6046 nvme cmpl without done call back? " + "Data %p DID %x Xri: %x status %x\n", + pnvme_lsreq, ndlp ? ndlp->nlp_DID : 0, + cmdwqe->sli4_xritag, status); + if (ndlp) { + lpfc_nlp_put(ndlp); + cmdwqe->context1 = NULL; + } + lpfc_sli_release_iocbq(phba, cmdwqe); +} + +static int +lpfc_nvme_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp, + struct lpfc_dmabuf *inp, + struct nvmefc_ls_req *pnvme_lsreq, + void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_wcqe_complete *), + struct lpfc_nodelist *ndlp, uint32_t num_entry, + uint32_t tmo, uint8_t retry) +{ + struct lpfc_hba *phba = vport->phba; + union lpfc_wqe *wqe; + struct lpfc_iocbq *genwqe; + struct ulp_bde64 *bpl; + struct ulp_bde64 bde; + int i, rc, xmit_len, first_len; + + /* Allocate buffer for command WQE */ + genwqe = lpfc_sli_get_iocbq(phba); + if (genwqe == NULL) + return 1; + + wqe = &genwqe->wqe; + memset(wqe, 0, sizeof(union lpfc_wqe)); + + genwqe->context3 = (uint8_t *)bmp; + genwqe->iocb_flag |= LPFC_IO_NVME_LS; + + /* Save for completion so we can release these resources */ + genwqe->context1 = lpfc_nlp_get(ndlp); + genwqe->context2 = (uint8_t *)pnvme_lsreq; + /* Fill in payload, bp points to frame payload */ + + if (!tmo) + /* FC spec states we need 3 * ratov for CT requests */ + tmo = (3 * phba->fc_ratov); + + /* For this command calculate the xmit length of the request bde. */ + xmit_len = 0; + first_len = 0; + bpl = (struct ulp_bde64 *)bmp->virt; + for (i = 0; i < num_entry; i++) { + bde.tus.w = bpl[i].tus.w; + if (bde.tus.f.bdeFlags != BUFF_TYPE_BDE_64) + break; + xmit_len += bde.tus.f.bdeSize; + if (i == 0) + first_len = xmit_len; + } + + genwqe->rsvd2 = num_entry; + genwqe->hba_wqidx = 0; + + /* Words 0 - 2 */ + wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64; + wqe->generic.bde.tus.f.bdeSize = first_len; + wqe->generic.bde.addrLow = bpl[0].addrLow; + wqe->generic.bde.addrHigh = bpl[0].addrHigh; + + /* Word 3 */ + wqe->gen_req.request_payload_len = first_len; + + /* Word 4 */ + + /* Word 5 */ + bf_set(wqe_dfctl, &wqe->gen_req.wge_ctl, 0); + bf_set(wqe_si, &wqe->gen_req.wge_ctl, 1); + bf_set(wqe_la, &wqe->gen_req.wge_ctl, 1); + bf_set(wqe_rctl, &wqe->gen_req.wge_ctl, FC_RCTL_DD_UNSOL_CTL); + bf_set(wqe_type, &wqe->gen_req.wge_ctl, FC_TYPE_NVME); + + /* Word 6 */ + bf_set(wqe_ctxt_tag, &wqe->gen_req.wqe_com, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); + bf_set(wqe_xri_tag, &wqe->gen_req.wqe_com, genwqe->sli4_xritag); + + /* Word 7 */ + bf_set(wqe_tmo, &wqe->gen_req.wqe_com, (vport->phba->fc_ratov-1)); + bf_set(wqe_class, &wqe->gen_req.wqe_com, CLASS3); + bf_set(wqe_cmnd, &wqe->gen_req.wqe_com, CMD_GEN_REQUEST64_WQE); + bf_set(wqe_ct, &wqe->gen_req.wqe_com, SLI4_CT_RPI); + + /* Word 8 */ + wqe->gen_req.wqe_com.abort_tag = genwqe->iotag; + + /* Word 9 */ + bf_set(wqe_reqtag, &wqe->gen_req.wqe_com, genwqe->iotag); + + /* Word 10 */ + bf_set(wqe_dbde, &wqe->gen_req.wqe_com, 1); + bf_set(wqe_iod, &wqe->gen_req.wqe_com, LPFC_WQE_IOD_READ); + bf_set(wqe_qosd, &wqe->gen_req.wqe_com, 1); + bf_set(wqe_lenloc, &wqe->gen_req.wqe_com, LPFC_WQE_LENLOC_NONE); + bf_set(wqe_ebde_cnt, &wqe->gen_req.wqe_com, 0); + + /* Word 11 */ + bf_set(wqe_cqid, &wqe->gen_req.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); + bf_set(wqe_cmd_type, &wqe->gen_req.wqe_com, OTHER_COMMAND); + + + /* Issue GEN REQ WQE for NPORT <did> */ + lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, + "6050 Issue GEN REQ WQE to NPORT x%x " + "Data: x%x x%x wq:%p lsreq:%p bmp:%p xmit:%d 1st:%d\n", + ndlp->nlp_DID, genwqe->iotag, + vport->port_state, + genwqe, pnvme_lsreq, bmp, xmit_len, first_len); + genwqe->wqe_cmpl = cmpl; + genwqe->iocb_cmpl = NULL; + genwqe->drvrTimeout = tmo + LPFC_DRVR_TIMEOUT; + genwqe->vport = vport; + genwqe->retry = retry; + + lpfc_nvmeio_data(phba, "NVME LS XMIT: xri x%x iotag x%x to x%06x\n", + genwqe->sli4_xritag, genwqe->iotag, ndlp->nlp_DID); + + rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, genwqe); + if (rc == WQE_ERROR) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, + "6045 Issue GEN REQ WQE to NPORT x%x " + "Data: x%x x%x\n", + ndlp->nlp_DID, genwqe->iotag, + vport->port_state); + lpfc_sli_release_iocbq(phba, genwqe); + return 1; + } + return 0; +} + +/** + * lpfc_nvme_ls_req - Issue an Link Service request + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @lpfc_nvme_lport: Pointer to the driver's local port data + * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq + * + * Driver registers this routine to handle any link service request + * from the nvme_fc transport to a remote nvme-aware port. + * + * Return value : + * 0 - Success + * TODO: What are the failure codes. + **/ +static int +lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, + struct nvme_fc_remote_port *pnvme_rport, + struct nvmefc_ls_req *pnvme_lsreq) +{ + int ret = 0; + struct lpfc_nvme_lport *lport; + struct lpfc_vport *vport; + struct lpfc_nodelist *ndlp; + struct ulp_bde64 *bpl; + struct lpfc_dmabuf *bmp; + + /* there are two dma buf in the request, actually there is one and + * the second one is just the start address + cmd size. + * Before calling lpfc_nvme_gen_req these buffers need to be wrapped + * in a lpfc_dmabuf struct. When freeing we just free the wrapper + * because the nvem layer owns the data bufs. + * We do not have to break these packets open, we don't care what is in + * them. And we do not have to look at the resonse data, we only care + * that we got a response. All of the caring is going to happen in the + * nvme-fc layer. + */ + + lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + vport = lport->vport; + + ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id); + if (!ndlp) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6043 Could not find node for DID %x\n", + pnvme_rport->port_id); + return 1; + } + bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!bmp) { + + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6044 Could not find node for DID %x\n", + pnvme_rport->port_id); + return 2; + } + INIT_LIST_HEAD(&bmp->list); + bmp->virt = lpfc_mbuf_alloc(vport->phba, MEM_PRI, &(bmp->phys)); + if (!bmp->virt) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6042 Could not find node for DID %x\n", + pnvme_rport->port_id); + kfree(bmp); + return 3; + } + bpl = (struct ulp_bde64 *)bmp->virt; + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pnvme_lsreq->rqstdma)); + bpl->addrLow = le32_to_cpu(putPaddrLow(pnvme_lsreq->rqstdma)); + bpl->tus.f.bdeFlags = 0; + bpl->tus.f.bdeSize = pnvme_lsreq->rqstlen; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pnvme_lsreq->rspdma)); + bpl->addrLow = le32_to_cpu(putPaddrLow(pnvme_lsreq->rspdma)); + bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; + bpl->tus.f.bdeSize = pnvme_lsreq->rsplen; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + /* Expand print to include key fields. */ + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6051 ENTER. lport %p, rport %p lsreq%p rqstlen:%d " + "rsplen:%d %pad %pad\n", + pnvme_lport, pnvme_rport, + pnvme_lsreq, pnvme_lsreq->rqstlen, + pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma, + &pnvme_lsreq->rspdma); + + vport->phba->fc4NvmeLsRequests++; + + /* Hardcode the wait to 30 seconds. Connections are failing otherwise. + * This code allows it all to work. + */ + ret = lpfc_nvme_gen_req(vport, bmp, pnvme_lsreq->rqstaddr, + pnvme_lsreq, lpfc_nvme_cmpl_gen_req, + ndlp, 2, 30, 0); + if (ret != WQE_SUCCESS) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6052 EXIT. issue ls wqe failed lport %p, " + "rport %p lsreq%p Status %x DID %x\n", + pnvme_lport, pnvme_rport, pnvme_lsreq, + ret, ndlp->nlp_DID); + lpfc_mbuf_free(vport->phba, bmp->virt, bmp->phys); + kfree(bmp); + return ret; + } + + /* Stub in routine and return 0 for now. */ + return ret; +} + +/** + * lpfc_nvme_ls_abort - Issue an Link Service request + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @lpfc_nvme_lport: Pointer to the driver's local port data + * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq + * + * Driver registers this routine to handle any link service request + * from the nvme_fc transport to a remote nvme-aware port. + * + * Return value : + * 0 - Success + * TODO: What are the failure codes. + **/ +static void +lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport, + struct nvme_fc_remote_port *pnvme_rport, + struct nvmefc_ls_req *pnvme_lsreq) +{ + struct lpfc_nvme_lport *lport; + struct lpfc_vport *vport; + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + LIST_HEAD(abort_list); + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *wqe, *next_wqe; + + lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + vport = lport->vport; + phba = vport->phba; + + ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id); + if (!ndlp) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, + "6049 Could not find node for DID %x\n", + pnvme_rport->port_id); + return; + } + + /* Expand print to include key fields. */ + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS, + "6040 ENTER. lport %p, rport %p lsreq %p rqstlen:%d " + "rsplen:%d %pad %pad\n", + pnvme_lport, pnvme_rport, + pnvme_lsreq, pnvme_lsreq->rqstlen, + pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma, + &pnvme_lsreq->rspdma); + + /* + * Lock the ELS ring txcmplq and build a local list of all ELS IOs + * that need an ABTS. The IOs need to stay on the txcmplq so that + * the abort operation completes them successfully. + */ + pring = phba->sli4_hba.nvmels_wq->pring; + spin_lock_irq(&phba->hbalock); + spin_lock(&pring->ring_lock); + list_for_each_entry_safe(wqe, next_wqe, &pring->txcmplq, list) { + /* Add to abort_list on on NDLP match. */ + if (lpfc_check_sli_ndlp(phba, pring, wqe, ndlp)) { + wqe->iocb_flag |= LPFC_DRIVER_ABORTED; + list_add_tail(&wqe->dlist, &abort_list); + } + } + spin_unlock(&pring->ring_lock); + spin_unlock_irq(&phba->hbalock); + + /* Abort the targeted IOs and remove them from the abort list. */ + list_for_each_entry_safe(wqe, next_wqe, &abort_list, dlist) { + spin_lock_irq(&phba->hbalock); + list_del_init(&wqe->dlist); + lpfc_sli_issue_abort_iotag(phba, pring, wqe); + spin_unlock_irq(&phba->hbalock); + } +} + +/* Fix up the existing sgls for NVME IO. */ +static void +lpfc_nvme_adj_fcp_sgls(struct lpfc_vport *vport, + struct lpfc_nvme_buf *lpfc_ncmd, + struct nvmefc_fcp_req *nCmd) +{ + struct sli4_sge *sgl; + union lpfc_wqe128 *wqe; + uint32_t *wptr, *dptr; + + /* + * Adjust the FCP_CMD and FCP_RSP DMA data and sge_len to + * match NVME. NVME sends 96 bytes. Also, use the + * nvme commands command and response dma addresses + * rather than the virtual memory to ease the restore + * operation. + */ + sgl = lpfc_ncmd->nvme_sgl; + sgl->sge_len = cpu_to_le32(nCmd->cmdlen); + + sgl++; + + /* Setup the physical region for the FCP RSP */ + sgl->addr_hi = cpu_to_le32(putPaddrHigh(nCmd->rspdma)); + sgl->addr_lo = cpu_to_le32(putPaddrLow(nCmd->rspdma)); + sgl->word2 = le32_to_cpu(sgl->word2); + if (nCmd->sg_cnt) + bf_set(lpfc_sli4_sge_last, sgl, 0); + else + bf_set(lpfc_sli4_sge_last, sgl, 1); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = cpu_to_le32(nCmd->rsplen); + + /* + * Get a local pointer to the built-in wqe and correct + * the cmd size to match NVME's 96 bytes and fix + * the dma address. + */ + + /* 128 byte wqe support here */ + wqe = (union lpfc_wqe128 *)&lpfc_ncmd->cur_iocbq.wqe; + + /* Word 0-2 - NVME CMND IU (embedded payload) */ + wqe->generic.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_IMMED; + wqe->generic.bde.tus.f.bdeSize = 60; + wqe->generic.bde.addrHigh = 0; + wqe->generic.bde.addrLow = 64; /* Word 16 */ + + /* Word 3 */ + bf_set(payload_offset_len, &wqe->fcp_icmd, + (nCmd->rsplen + nCmd->cmdlen)); + + /* Word 10 */ + bf_set(wqe_nvme, &wqe->fcp_icmd.wqe_com, 1); + bf_set(wqe_wqes, &wqe->fcp_icmd.wqe_com, 1); + + /* + * Embed the payload in the last half of the WQE + * WQE words 16-30 get the NVME CMD IU payload + * + * WQE Word 16 is already setup with flags + * WQE words 17-19 get payload Words 2-4 + * WQE words 20-21 get payload Words 6-7 + * WQE words 22-29 get payload Words 16-23 + */ + wptr = &wqe->words[17]; /* WQE ptr */ + dptr = (uint32_t *)nCmd->cmdaddr; /* payload ptr */ + dptr += 2; /* Skip Words 0-1 in payload */ + + *wptr++ = *dptr++; /* Word 2 */ + *wptr++ = *dptr++; /* Word 3 */ + *wptr++ = *dptr++; /* Word 4 */ + dptr++; /* Skip Word 5 in payload */ + *wptr++ = *dptr++; /* Word 6 */ + *wptr++ = *dptr++; /* Word 7 */ + dptr += 8; /* Skip Words 8-15 in payload */ + *wptr++ = *dptr++; /* Word 16 */ + *wptr++ = *dptr++; /* Word 17 */ + *wptr++ = *dptr++; /* Word 18 */ + *wptr++ = *dptr++; /* Word 19 */ + *wptr++ = *dptr++; /* Word 20 */ + *wptr++ = *dptr++; /* Word 21 */ + *wptr++ = *dptr++; /* Word 22 */ + *wptr = *dptr; /* Word 23 */ +} + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS +static void +lpfc_nvme_ktime(struct lpfc_hba *phba, + struct lpfc_nvme_buf *lpfc_ncmd) +{ + uint64_t seg1, seg2, seg3, seg4; + + if (!phba->ktime_on) + return; + if (!lpfc_ncmd->ts_last_cmd || + !lpfc_ncmd->ts_cmd_start || + !lpfc_ncmd->ts_cmd_wqput || + !lpfc_ncmd->ts_isr_cmpl || + !lpfc_ncmd->ts_data_nvme) + return; + if (lpfc_ncmd->ts_cmd_start < lpfc_ncmd->ts_last_cmd) + return; + if (lpfc_ncmd->ts_cmd_wqput < lpfc_ncmd->ts_cmd_start) + return; + if (lpfc_ncmd->ts_isr_cmpl < lpfc_ncmd->ts_cmd_wqput) + return; + if (lpfc_ncmd->ts_data_nvme < lpfc_ncmd->ts_isr_cmpl) + return; + /* + * Segment 1 - Time from Last FCP command cmpl is handed + * off to NVME Layer to start of next command. + * Segment 2 - Time from Driver receives a IO cmd start + * from NVME Layer to WQ put is done on IO cmd. + * Segment 3 - Time from Driver WQ put is done on IO cmd + * to MSI-X ISR for IO cmpl. + * Segment 4 - Time from MSI-X ISR for IO cmpl to when + * cmpl is handled off to the NVME Layer. + */ + seg1 = lpfc_ncmd->ts_cmd_start - lpfc_ncmd->ts_last_cmd; + if (seg1 > 5000000) /* 5 ms - for sequential IOs */ + return; + + /* Calculate times relative to start of IO */ + seg2 = (lpfc_ncmd->ts_cmd_wqput - lpfc_ncmd->ts_cmd_start); + seg3 = (lpfc_ncmd->ts_isr_cmpl - + lpfc_ncmd->ts_cmd_start) - seg2; + seg4 = (lpfc_ncmd->ts_data_nvme - + lpfc_ncmd->ts_cmd_start) - seg2 - seg3; + phba->ktime_data_samples++; + phba->ktime_seg1_total += seg1; + if (seg1 < phba->ktime_seg1_min) + phba->ktime_seg1_min = seg1; + else if (seg1 > phba->ktime_seg1_max) + phba->ktime_seg1_max = seg1; + phba->ktime_seg2_total += seg2; + if (seg2 < phba->ktime_seg2_min) + phba->ktime_seg2_min = seg2; + else if (seg2 > phba->ktime_seg2_max) + phba->ktime_seg2_max = seg2; + phba->ktime_seg3_total += seg3; + if (seg3 < phba->ktime_seg3_min) + phba->ktime_seg3_min = seg3; + else if (seg3 > phba->ktime_seg3_max) + phba->ktime_seg3_max = seg3; + phba->ktime_seg4_total += seg4; + if (seg4 < phba->ktime_seg4_min) + phba->ktime_seg4_min = seg4; + else if (seg4 > phba->ktime_seg4_max) + phba->ktime_seg4_max = seg4; + + lpfc_ncmd->ts_last_cmd = 0; + lpfc_ncmd->ts_cmd_start = 0; + lpfc_ncmd->ts_cmd_wqput = 0; + lpfc_ncmd->ts_isr_cmpl = 0; + lpfc_ncmd->ts_data_nvme = 0; +} +#endif + +/** + * lpfc_nvme_io_cmd_wqe_cmpl - Complete an NVME-over-FCP IO + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @lpfc_nvme_lport: Pointer to the driver's local port data + * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq + * + * Driver registers this routine as it io request handler. This + * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq + * data structure to the rport indicated in @lpfc_nvme_rport. + * + * Return value : + * 0 - Success + * TODO: What are the failure codes. + **/ +static void +lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, + struct lpfc_wcqe_complete *wcqe) +{ + struct lpfc_nvme_buf *lpfc_ncmd = + (struct lpfc_nvme_buf *)pwqeIn->context1; + struct lpfc_vport *vport = pwqeIn->vport; + struct nvmefc_fcp_req *nCmd; + struct nvme_fc_ersp_iu *ep; + struct nvme_fc_cmd_iu *cp; + struct lpfc_nvme_rport *rport; + struct lpfc_nodelist *ndlp; + unsigned long flags; + uint32_t code; + uint16_t cid, sqhd, data; + uint32_t *ptr; + + /* Sanity check on return of outstanding command */ + if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd || !lpfc_ncmd->nrport) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, + "6071 Completion pointers bad on wqe %p.\n", + wcqe); + return; + } + phba->fc4NvmeIoCmpls++; + + nCmd = lpfc_ncmd->nvmeCmd; + rport = lpfc_ncmd->nrport; + + lpfc_nvmeio_data(phba, "NVME FCP CMPL: xri x%x stat x%x parm x%x\n", + lpfc_ncmd->cur_iocbq.sli4_xritag, + bf_get(lpfc_wcqe_c_status, wcqe), wcqe->parameter); + /* + * Catch race where our node has transitioned, but the + * transport is still transitioning. + */ + ndlp = rport->ndlp; + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, + "6061 rport %p, ndlp %p, DID x%06x ndlp " + "not ready.\n", + rport, ndlp, rport->remoteport->port_id); + + ndlp = lpfc_findnode_did(vport, rport->remoteport->port_id); + if (!ndlp) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, + "6062 Ignoring NVME cmpl. No ndlp\n"); + goto out_err; + } + } + + code = bf_get(lpfc_wcqe_c_code, wcqe); + if (code == CQE_CODE_NVME_ERSP) { + /* For this type of CQE, we need to rebuild the rsp */ + ep = (struct nvme_fc_ersp_iu *)nCmd->rspaddr; + + /* + * Get Command Id from cmd to plug into response. This + * code is not needed in the next NVME Transport drop. + */ + cp = (struct nvme_fc_cmd_iu *)nCmd->cmdaddr; + cid = cp->sqe.common.command_id; + + /* + * RSN is in CQE word 2 + * SQHD is in CQE Word 3 bits 15:0 + * Cmd Specific info is in CQE Word 1 + * and in CQE Word 0 bits 15:0 + */ + sqhd = bf_get(lpfc_wcqe_c_sqhead, wcqe); + + /* Now lets build the NVME ERSP IU */ + ep->iu_len = cpu_to_be16(8); + ep->rsn = wcqe->parameter; + ep->xfrd_len = cpu_to_be32(nCmd->payload_length); + ep->rsvd12 = 0; + ptr = (uint32_t *)&ep->cqe.result.u64; + *ptr++ = wcqe->total_data_placed; + data = bf_get(lpfc_wcqe_c_ersp0, wcqe); + *ptr = (uint32_t)data; + ep->cqe.sq_head = sqhd; + ep->cqe.sq_id = nCmd->sqid; + ep->cqe.command_id = cid; + ep->cqe.status = 0; + + lpfc_ncmd->status = IOSTAT_SUCCESS; + lpfc_ncmd->result = 0; + nCmd->rcv_rsplen = LPFC_NVME_ERSP_LEN; + nCmd->transferred_length = nCmd->payload_length; + } else { + lpfc_ncmd->status = (bf_get(lpfc_wcqe_c_status, wcqe) & + LPFC_IOCB_STATUS_MASK); + lpfc_ncmd->result = wcqe->parameter; + + /* For NVME, the only failure path that results in an + * IO error is when the adapter rejects it. All other + * conditions are a success case and resolved by the + * transport. + * IOSTAT_FCP_RSP_ERROR means: + * 1. Length of data received doesn't match total + * transfer length in WQE + * 2. If the RSP payload does NOT match these cases: + * a. RSP length 12/24 bytes and all zeros + * b. NVME ERSP + */ + switch (lpfc_ncmd->status) { + case IOSTAT_SUCCESS: + nCmd->transferred_length = wcqe->total_data_placed; + nCmd->rcv_rsplen = 0; + nCmd->status = 0; + break; + case IOSTAT_FCP_RSP_ERROR: + nCmd->transferred_length = wcqe->total_data_placed; + nCmd->rcv_rsplen = wcqe->parameter; + nCmd->status = 0; + /* Sanity check */ + if (nCmd->rcv_rsplen == LPFC_NVME_ERSP_LEN) + break; + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, + "6081 NVME Completion Protocol Error: " + "status x%x result x%x placed x%x\n", + lpfc_ncmd->status, lpfc_ncmd->result, + wcqe->total_data_placed); + break; + default: +out_err: + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, + "6072 NVME Completion Error: " + "status x%x result x%x placed x%x\n", + lpfc_ncmd->status, lpfc_ncmd->result, + wcqe->total_data_placed); + nCmd->transferred_length = 0; + nCmd->rcv_rsplen = 0; + nCmd->status = NVME_SC_FC_TRANSPORT_ERROR; + } + } + + /* pick up SLI4 exhange busy condition */ + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + lpfc_ncmd->flags |= LPFC_SBUF_XBUSY; + else + lpfc_ncmd->flags &= ~LPFC_SBUF_XBUSY; + + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) + atomic_dec(&ndlp->cmd_pending); + + /* Update stats and complete the IO. There is + * no need for dma unprep because the nvme_transport + * owns the dma address. + */ +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) { + lpfc_ncmd->ts_isr_cmpl = pwqeIn->isr_timestamp; + lpfc_ncmd->ts_data_nvme = ktime_get_ns(); + phba->ktime_last_cmd = lpfc_ncmd->ts_data_nvme; + lpfc_nvme_ktime(phba, lpfc_ncmd); + } + if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) { + if (lpfc_ncmd->cpu != smp_processor_id()) + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, + "6701 CPU Check cmpl: " + "cpu %d expect %d\n", + smp_processor_id(), lpfc_ncmd->cpu); + if (lpfc_ncmd->cpu < LPFC_CHECK_CPU_CNT) + phba->cpucheck_cmpl_io[lpfc_ncmd->cpu]++; + } +#endif + nCmd->done(nCmd); + + spin_lock_irqsave(&phba->hbalock, flags); + lpfc_ncmd->nrport = NULL; + spin_unlock_irqrestore(&phba->hbalock, flags); + + lpfc_release_nvme_buf(phba, lpfc_ncmd); +} + + +/** + * lpfc_nvme_prep_io_cmd - Issue an NVME-over-FCP IO + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @lpfc_nvme_lport: Pointer to the driver's local port data + * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq + * @lpfc_nvme_fcreq: IO request from nvme fc to driver. + * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_queue + * + * Driver registers this routine as it io request handler. This + * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq + * data structure to the rport indicated in @lpfc_nvme_rport. + * + * Return value : + * 0 - Success + * TODO: What are the failure codes. + **/ +static int +lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, + struct lpfc_nvme_buf *lpfc_ncmd, + struct lpfc_nodelist *pnode) +{ + struct lpfc_hba *phba = vport->phba; + struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; + struct lpfc_iocbq *pwqeq = &(lpfc_ncmd->cur_iocbq); + union lpfc_wqe128 *wqe = (union lpfc_wqe128 *)&pwqeq->wqe; + uint32_t req_len; + + if (!pnode || !NLP_CHK_NODE_ACT(pnode)) + return -EINVAL; + + /* + * There are three possibilities here - use scatter-gather segment, use + * the single mapping, or neither. + */ + wqe->fcp_iwrite.initial_xfer_len = 0; + if (nCmd->sg_cnt) { + if (nCmd->io_dir == NVMEFC_FCP_WRITE) { + /* Word 5 */ + if ((phba->cfg_nvme_enable_fb) && + (pnode->nlp_flag & NLP_FIRSTBURST)) { + req_len = lpfc_ncmd->nvmeCmd->payload_length; + if (req_len < pnode->nvme_fb_size) + wqe->fcp_iwrite.initial_xfer_len = + req_len; + else + wqe->fcp_iwrite.initial_xfer_len = + pnode->nvme_fb_size; + } + + /* Word 7 */ + bf_set(wqe_cmnd, &wqe->generic.wqe_com, + CMD_FCP_IWRITE64_WQE); + bf_set(wqe_pu, &wqe->generic.wqe_com, + PARM_READ_CHECK); + + /* Word 10 */ + bf_set(wqe_qosd, &wqe->fcp_iwrite.wqe_com, 0); + bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com, + LPFC_WQE_IOD_WRITE); + bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com, + LPFC_WQE_LENLOC_WORD4); + if (phba->cfg_nvme_oas) + bf_set(wqe_oas, &wqe->fcp_iwrite.wqe_com, 1); + + /* Word 11 */ + bf_set(wqe_cmd_type, &wqe->generic.wqe_com, + NVME_WRITE_CMD); + + /* Word 16 */ + wqe->words[16] = LPFC_NVME_EMBED_WRITE; + + phba->fc4NvmeOutputRequests++; + } else { + /* Word 7 */ + bf_set(wqe_cmnd, &wqe->generic.wqe_com, + CMD_FCP_IREAD64_WQE); + bf_set(wqe_pu, &wqe->generic.wqe_com, + PARM_READ_CHECK); + + /* Word 10 */ + bf_set(wqe_qosd, &wqe->fcp_iread.wqe_com, 0); + bf_set(wqe_iod, &wqe->fcp_iread.wqe_com, + LPFC_WQE_IOD_READ); + bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com, + LPFC_WQE_LENLOC_WORD4); + if (phba->cfg_nvme_oas) + bf_set(wqe_oas, &wqe->fcp_iread.wqe_com, 1); + + /* Word 11 */ + bf_set(wqe_cmd_type, &wqe->generic.wqe_com, + NVME_READ_CMD); + + /* Word 16 */ + wqe->words[16] = LPFC_NVME_EMBED_READ; + + phba->fc4NvmeInputRequests++; + } + } else { + /* Word 4 */ + wqe->fcp_icmd.rsrvd4 = 0; + + /* Word 7 */ + bf_set(wqe_cmnd, &wqe->generic.wqe_com, CMD_FCP_ICMND64_WQE); + bf_set(wqe_pu, &wqe->generic.wqe_com, 0); + + /* Word 10 */ + bf_set(wqe_qosd, &wqe->fcp_icmd.wqe_com, 1); + bf_set(wqe_iod, &wqe->fcp_icmd.wqe_com, LPFC_WQE_IOD_WRITE); + bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com, + LPFC_WQE_LENLOC_NONE); + if (phba->cfg_nvme_oas) + bf_set(wqe_oas, &wqe->fcp_icmd.wqe_com, 1); + + /* Word 11 */ + bf_set(wqe_cmd_type, &wqe->generic.wqe_com, NVME_READ_CMD); + + /* Word 16 */ + wqe->words[16] = LPFC_NVME_EMBED_CMD; + + phba->fc4NvmeControlRequests++; + } + /* + * Finish initializing those WQE fields that are independent + * of the nvme_cmnd request_buffer + */ + + /* Word 6 */ + bf_set(wqe_ctxt_tag, &wqe->generic.wqe_com, + phba->sli4_hba.rpi_ids[pnode->nlp_rpi]); + bf_set(wqe_xri_tag, &wqe->generic.wqe_com, pwqeq->sli4_xritag); + + /* Word 7 */ + /* Preserve Class data in the ndlp. */ + bf_set(wqe_class, &wqe->generic.wqe_com, + (pnode->nlp_fcp_info & 0x0f)); + + /* Word 8 */ + wqe->generic.wqe_com.abort_tag = pwqeq->iotag; + + /* Word 9 */ + bf_set(wqe_reqtag, &wqe->generic.wqe_com, pwqeq->iotag); + + /* Word 11 */ + bf_set(wqe_cqid, &wqe->generic.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); + + pwqeq->vport = vport; + return 0; +} + + +/** + * lpfc_nvme_prep_io_dma - Issue an NVME-over-FCP IO + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @lpfc_nvme_lport: Pointer to the driver's local port data + * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq + * @lpfc_nvme_fcreq: IO request from nvme fc to driver. + * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_queue + * + * Driver registers this routine as it io request handler. This + * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq + * data structure to the rport indicated in @lpfc_nvme_rport. + * + * Return value : + * 0 - Success + * TODO: What are the failure codes. + **/ +static int +lpfc_nvme_prep_io_dma(struct lpfc_vport *vport, + struct lpfc_nvme_buf *lpfc_ncmd) +{ + struct lpfc_hba *phba = vport->phba; + struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; + union lpfc_wqe128 *wqe = (union lpfc_wqe128 *)&lpfc_ncmd->cur_iocbq.wqe; + struct sli4_sge *sgl = lpfc_ncmd->nvme_sgl; + struct scatterlist *data_sg; + struct sli4_sge *first_data_sgl; + dma_addr_t physaddr; + uint32_t num_bde = 0; + uint32_t dma_len; + uint32_t dma_offset = 0; + int nseg, i; + + /* Fix up the command and response DMA stuff. */ + lpfc_nvme_adj_fcp_sgls(vport, lpfc_ncmd, nCmd); + + /* + * There are three possibilities here - use scatter-gather segment, use + * the single mapping, or neither. + */ + if (nCmd->sg_cnt) { + /* + * Jump over the cmd and rsp SGEs. The fix routine + * has already adjusted for this. + */ + sgl += 2; + + first_data_sgl = sgl; + lpfc_ncmd->seg_cnt = nCmd->sg_cnt; + if (lpfc_ncmd->seg_cnt > phba->cfg_sg_seg_cnt) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6058 Too many sg segments from " + "NVME Transport. Max %d, " + "nvmeIO sg_cnt %d\n", + phba->cfg_sg_seg_cnt, + lpfc_ncmd->seg_cnt); + lpfc_ncmd->seg_cnt = 0; + return 1; + } + + /* + * The driver established a maximum scatter-gather segment count + * during probe that limits the number of sg elements in any + * single nvme command. Just run through the seg_cnt and format + * the sge's. + */ + nseg = nCmd->sg_cnt; + data_sg = nCmd->first_sgl; + for (i = 0; i < nseg; i++) { + if (data_sg == NULL) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6059 dptr err %d, nseg %d\n", + i, nseg); + lpfc_ncmd->seg_cnt = 0; + return 1; + } + physaddr = data_sg->dma_address; + dma_len = data_sg->length; + sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr)); + sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr)); + sgl->word2 = le32_to_cpu(sgl->word2); + if ((num_bde + 1) == nseg) + bf_set(lpfc_sli4_sge_last, sgl, 1); + else + bf_set(lpfc_sli4_sge_last, sgl, 0); + bf_set(lpfc_sli4_sge_offset, sgl, dma_offset); + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = cpu_to_le32(dma_len); + + dma_offset += dma_len; + data_sg = sg_next(data_sg); + sgl++; + } + } else { + /* For this clause to be valid, the payload_length + * and sg_cnt must zero. + */ + if (nCmd->payload_length != 0) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6063 NVME DMA Prep Err: sg_cnt %d " + "payload_length x%x\n", + nCmd->sg_cnt, nCmd->payload_length); + return 1; + } + } + + /* + * Due to difference in data length between DIF/non-DIF paths, + * we need to set word 4 of WQE here + */ + wqe->fcp_iread.total_xfer_len = nCmd->payload_length; + return 0; +} + +/** + * lpfc_nvme_fcp_io_submit - Issue an NVME-over-FCP IO + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @lpfc_nvme_lport: Pointer to the driver's local port data + * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq + * @lpfc_nvme_fcreq: IO request from nvme fc to driver. + * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_queue + * + * Driver registers this routine as it io request handler. This + * routine issues an fcp WQE with data from the @lpfc_nvme_fcpreq + * data structure to the rport + indicated in @lpfc_nvme_rport. + * + * Return value : + * 0 - Success + * TODO: What are the failure codes. + **/ +static int +lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, + struct nvme_fc_remote_port *pnvme_rport, + void *hw_queue_handle, + struct nvmefc_fcp_req *pnvme_fcreq) +{ + int ret = 0; + struct lpfc_nvme_lport *lport; + struct lpfc_vport *vport; + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + struct lpfc_nvme_buf *lpfc_ncmd; + struct lpfc_nvme_rport *rport; + struct lpfc_nvme_qhandle *lpfc_queue_info; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint64_t start = 0; +#endif + + lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + vport = lport->vport; + phba = vport->phba; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) + start = ktime_get_ns(); +#endif + rport = (struct lpfc_nvme_rport *)pnvme_rport->private; + lpfc_queue_info = (struct lpfc_nvme_qhandle *)hw_queue_handle; + + /* + * Catch race where our node has transitioned, but the + * transport is still transitioning. + */ + ndlp = rport->ndlp; + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, + "6053 rport %p, ndlp %p, DID x%06x " + "ndlp not ready.\n", + rport, ndlp, pnvme_rport->port_id); + + ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id); + if (!ndlp) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, + "6066 Missing node for DID %x\n", + pnvme_rport->port_id); + ret = -ENODEV; + goto out_fail; + } + } + + /* The remote node has to be a mapped target or it's an error. */ + if ((ndlp->nlp_type & NLP_NVME_TARGET) && + (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, + "6036 rport %p, DID x%06x not ready for " + "IO. State x%x, Type x%x\n", + rport, pnvme_rport->port_id, + ndlp->nlp_state, ndlp->nlp_type); + ret = -ENODEV; + goto out_fail; + + } + + /* The node is shared with FCP IO, make sure the IO pending count does + * not exceed the programmed depth. + */ + if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) { + ret = -EAGAIN; + goto out_fail; + } + + lpfc_ncmd = lpfc_get_nvme_buf(phba, ndlp); + if (lpfc_ncmd == NULL) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6065 driver's buffer pool is empty, " + "IO failed\n"); + ret = -ENOMEM; + goto out_fail; + } +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) { + lpfc_ncmd->ts_cmd_start = start; + lpfc_ncmd->ts_last_cmd = phba->ktime_last_cmd; + } +#endif + + /* + * Store the data needed by the driver to issue, abort, and complete + * an IO. + * Do not let the IO hang out forever. There is no midlayer issuing + * an abort so inform the FW of the maximum IO pending time. + */ + pnvme_fcreq->private = (void *)lpfc_ncmd; + lpfc_ncmd->nvmeCmd = pnvme_fcreq; + lpfc_ncmd->nrport = rport; + lpfc_ncmd->start_time = jiffies; + + lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp); + ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd); + if (ret) { + ret = -ENOMEM; + goto out_free_nvme_buf; + } + + atomic_inc(&ndlp->cmd_pending); + + /* + * Issue the IO on the WQ indicated by index in the hw_queue_handle. + * This identfier was create in our hardware queue create callback + * routine. The driver now is dependent on the IO queue steering from + * the transport. We are trusting the upper NVME layers know which + * index to use and that they have affinitized a CPU to this hardware + * queue. A hardware queue maps to a driver MSI-X vector/EQ/CQ/WQ. + */ + lpfc_ncmd->cur_iocbq.hba_wqidx = lpfc_queue_info->index; + + lpfc_nvmeio_data(phba, "NVME FCP XMIT: xri x%x idx %d to %06x\n", + lpfc_ncmd->cur_iocbq.sli4_xritag, + lpfc_queue_info->index, ndlp->nlp_DID); + + ret = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, &lpfc_ncmd->cur_iocbq); + if (ret) { + atomic_dec(&ndlp->cmd_pending); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, + "6113 FCP could not issue WQE err %x " + "sid: x%x did: x%x oxid: x%x\n", + ret, vport->fc_myDID, ndlp->nlp_DID, + lpfc_ncmd->cur_iocbq.sli4_xritag); + ret = -EINVAL; + goto out_free_nvme_buf; + } + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) + lpfc_ncmd->ts_cmd_wqput = ktime_get_ns(); + + if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) { + lpfc_ncmd->cpu = smp_processor_id(); + if (lpfc_ncmd->cpu != lpfc_queue_info->index) { + /* Check for admin queue */ + if (lpfc_queue_info->qidx) { + lpfc_printf_vlog(vport, + KERN_ERR, LOG_NVME_IOERR, + "6702 CPU Check cmd: " + "cpu %d wq %d\n", + lpfc_ncmd->cpu, + lpfc_queue_info->index); + } + lpfc_ncmd->cpu = lpfc_queue_info->index; + } + if (lpfc_ncmd->cpu < LPFC_CHECK_CPU_CNT) + phba->cpucheck_xmt_io[lpfc_ncmd->cpu]++; + } +#endif + return 0; + + out_free_nvme_buf: + lpfc_release_nvme_buf(phba, lpfc_ncmd); + out_fail: + return ret; +} + +/** + * lpfc_nvme_abort_fcreq_cmpl - Complete an NVME FCP abort request. + * @phba: Pointer to HBA context object + * @cmdiocb: Pointer to command iocb object. + * @rspiocb: Pointer to response iocb object. + * + * This is the callback function for any NVME FCP IO that was aborted. + * + * Return value: + * None + **/ +void +lpfc_nvme_abort_fcreq_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, + struct lpfc_wcqe_complete *abts_cmpl) +{ + lpfc_printf_log(phba, KERN_ERR, LOG_NVME, + "6145 ABORT_XRI_CN completing on rpi x%x " + "original iotag x%x, abort cmd iotag x%x " + "req_tag x%x, status x%x, hwstatus x%x\n", + cmdiocb->iocb.un.acxri.abortContextTag, + cmdiocb->iocb.un.acxri.abortIoTag, + cmdiocb->iotag, + bf_get(lpfc_wcqe_c_request_tag, abts_cmpl), + bf_get(lpfc_wcqe_c_status, abts_cmpl), + bf_get(lpfc_wcqe_c_hw_status, abts_cmpl)); + lpfc_sli_release_iocbq(phba, cmdiocb); +} + +/** + * lpfc_nvme_fcp_abort - Issue an NVME-over-FCP ABTS + * @lpfc_pnvme: Pointer to the driver's nvme instance data + * @lpfc_nvme_lport: Pointer to the driver's local port data + * @lpfc_nvme_rport: Pointer to the rport getting the @lpfc_nvme_ereq + * @lpfc_nvme_fcreq: IO request from nvme fc to driver. + * @hw_queue_handle: Driver-returned handle in lpfc_nvme_create_queue + * + * Driver registers this routine as its nvme request io abort handler. This + * routine issues an fcp Abort WQE with data from the @lpfc_nvme_fcpreq + * data structure to the rport indicated in @lpfc_nvme_rport. This routine + * is executed asynchronously - one the target is validated as "MAPPED" and + * ready for IO, the driver issues the abort request and returns. + * + * Return value: + * None + **/ +static void +lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, + struct nvme_fc_remote_port *pnvme_rport, + void *hw_queue_handle, + struct nvmefc_fcp_req *pnvme_fcreq) +{ + struct lpfc_nvme_lport *lport; + struct lpfc_vport *vport; + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + struct lpfc_nvme_rport *rport; + struct lpfc_nvme_buf *lpfc_nbuf; + struct lpfc_iocbq *abts_buf; + struct lpfc_iocbq *nvmereq_wqe; + union lpfc_wqe *abts_wqe; + unsigned long flags; + int ret_val; + + lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + rport = (struct lpfc_nvme_rport *)pnvme_rport->private; + vport = lport->vport; + phba = vport->phba; + + /* Announce entry to new IO submit field. */ + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, + "6002 Abort Request to rport DID x%06x " + "for nvme_fc_req %p\n", + pnvme_rport->port_id, + pnvme_fcreq); + + /* + * Catch race where our node has transitioned, but the + * transport is still transitioning. + */ + ndlp = rport->ndlp; + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_ABTS, + "6054 rport %p, ndlp %p, DID x%06x ndlp " + " not ready.\n", + rport, ndlp, pnvme_rport->port_id); + + ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id); + if (!ndlp) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_ABTS, + "6055 Could not find node for " + "DID %x\n", + pnvme_rport->port_id); + return; + } + } + + /* The remote node has to be ready to send an abort. */ + if ((ndlp->nlp_state != NLP_STE_MAPPED_NODE) && + !(ndlp->nlp_type & NLP_NVME_TARGET)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_ABTS, + "6048 rport %p, DID x%06x not ready for " + "IO. State x%x, Type x%x\n", + rport, pnvme_rport->port_id, + ndlp->nlp_state, ndlp->nlp_type); + return; + } + + /* If the hba is getting reset, this flag is set. It is + * cleared when the reset is complete and rings reestablished. + */ + spin_lock_irqsave(&phba->hbalock, flags); + /* driver queued commands are in process of being flushed */ + if (phba->hba_flag & HBA_NVME_IOQ_FLUSH) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6139 Driver in reset cleanup - flushing " + "NVME Req now. hba_flag x%x\n", + phba->hba_flag); + return; + } + + lpfc_nbuf = (struct lpfc_nvme_buf *)pnvme_fcreq->private; + if (!lpfc_nbuf) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6140 NVME IO req has no matching lpfc nvme " + "io buffer. Skipping abort req.\n"); + return; + } else if (!lpfc_nbuf->nvmeCmd) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6141 lpfc NVME IO req has no nvme_fcreq " + "io buffer. Skipping abort req.\n"); + return; + } + + /* + * The lpfc_nbuf and the mapped nvme_fcreq in the driver's + * state must match the nvme_fcreq passed by the nvme + * transport. If they don't match, it is likely the driver + * has already completed the NVME IO and the nvme transport + * has not seen it yet. + */ + if (lpfc_nbuf->nvmeCmd != pnvme_fcreq) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6143 NVME req mismatch: " + "lpfc_nbuf %p nvmeCmd %p, " + "pnvme_fcreq %p. Skipping Abort\n", + lpfc_nbuf, lpfc_nbuf->nvmeCmd, + pnvme_fcreq); + return; + } + + /* Don't abort IOs no longer on the pending queue. */ + nvmereq_wqe = &lpfc_nbuf->cur_iocbq; + if (!(nvmereq_wqe->iocb_flag & LPFC_IO_ON_TXCMPLQ)) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6142 NVME IO req %p not queued - skipping " + "abort req\n", + pnvme_fcreq); + return; + } + + lpfc_nvmeio_data(phba, "NVME FCP ABORT: xri x%x idx %d to %06x\n", + nvmereq_wqe->sli4_xritag, + nvmereq_wqe->hba_wqidx, ndlp->nlp_DID); + + /* Outstanding abort is in progress */ + if (nvmereq_wqe->iocb_flag & LPFC_DRIVER_ABORTED) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6144 Outstanding NVME I/O Abort Request " + "still pending on nvme_fcreq %p, " + "lpfc_ncmd %p\n", + pnvme_fcreq, lpfc_nbuf); + return; + } + + abts_buf = __lpfc_sli_get_iocbq(phba); + if (!abts_buf) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6136 No available abort wqes. Skipping " + "Abts req for nvme_fcreq %p.\n", + pnvme_fcreq); + return; + } + + /* Ready - mark outstanding as aborted by driver. */ + nvmereq_wqe->iocb_flag |= LPFC_DRIVER_ABORTED; + + /* Complete prepping the abort wqe and issue to the FW. */ + abts_wqe = &abts_buf->wqe; + + /* WQEs are reused. Clear stale data and set key fields to + * zero like ia, iaab, iaar, xri_tag, and ctxt_tag. + */ + memset(abts_wqe, 0, sizeof(union lpfc_wqe)); + bf_set(abort_cmd_criteria, &abts_wqe->abort_cmd, T_XRI_TAG); + + /* word 7 */ + bf_set(wqe_ct, &abts_wqe->abort_cmd.wqe_com, 0); + bf_set(wqe_cmnd, &abts_wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX); + bf_set(wqe_class, &abts_wqe->abort_cmd.wqe_com, + nvmereq_wqe->iocb.ulpClass); + + /* word 8 - tell the FW to abort the IO associated with this + * outstanding exchange ID. + */ + abts_wqe->abort_cmd.wqe_com.abort_tag = nvmereq_wqe->sli4_xritag; + + /* word 9 - this is the iotag for the abts_wqe completion. */ + bf_set(wqe_reqtag, &abts_wqe->abort_cmd.wqe_com, + abts_buf->iotag); + + /* word 10 */ + bf_set(wqe_wqid, &abts_wqe->abort_cmd.wqe_com, nvmereq_wqe->hba_wqidx); + bf_set(wqe_qosd, &abts_wqe->abort_cmd.wqe_com, 1); + bf_set(wqe_lenloc, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_LENLOC_NONE); + + /* word 11 */ + bf_set(wqe_cmd_type, &abts_wqe->abort_cmd.wqe_com, OTHER_COMMAND); + bf_set(wqe_wqec, &abts_wqe->abort_cmd.wqe_com, 1); + bf_set(wqe_cqid, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); + + /* ABTS WQE must go to the same WQ as the WQE to be aborted */ + abts_buf->iocb_flag |= LPFC_IO_NVME; + abts_buf->hba_wqidx = nvmereq_wqe->hba_wqidx; + abts_buf->vport = vport; + abts_buf->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl; + ret_val = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_buf); + spin_unlock_irqrestore(&phba->hbalock, flags); + if (ret_val == IOCB_ERROR) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6137 Failed abts issue_wqe with status x%x " + "for nvme_fcreq %p.\n", + ret_val, pnvme_fcreq); + lpfc_sli_release_iocbq(phba, abts_buf); + return; + } + + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6138 Transport Abort NVME Request Issued for\n" + "ox_id x%x on reqtag x%x\n", + nvmereq_wqe->sli4_xritag, + abts_buf->iotag); +} + +/* Declare and initialization an instance of the FC NVME template. */ +static struct nvme_fc_port_template lpfc_nvme_template = { + /* initiator-based functions */ + .localport_delete = lpfc_nvme_localport_delete, + .remoteport_delete = lpfc_nvme_remoteport_delete, + .create_queue = lpfc_nvme_create_queue, + .delete_queue = lpfc_nvme_delete_queue, + .ls_req = lpfc_nvme_ls_req, + .fcp_io = lpfc_nvme_fcp_io_submit, + .ls_abort = lpfc_nvme_ls_abort, + .fcp_abort = lpfc_nvme_fcp_abort, + + .max_hw_queues = 1, + .max_sgl_segments = LPFC_NVME_DEFAULT_SEGS, + .max_dif_sgl_segments = LPFC_NVME_DEFAULT_SEGS, + .dma_boundary = 0xFFFFFFFF, + + /* Sizes of additional private data for data structures. + * No use for the last two sizes at this time. + */ + .local_priv_sz = sizeof(struct lpfc_nvme_lport), + .remote_priv_sz = sizeof(struct lpfc_nvme_rport), + .lsrqst_priv_sz = 0, + .fcprqst_priv_sz = 0, +}; + +/** + * lpfc_sli4_post_nvme_sgl_block - post a block of nvme sgl list to firmware + * @phba: pointer to lpfc hba data structure. + * @nblist: pointer to nvme buffer list. + * @count: number of scsi buffers on the list. + * + * This routine is invoked to post a block of @count scsi sgl pages from a + * SCSI buffer list @nblist to the HBA using non-embedded mailbox command. + * No Lock is held. + * + **/ +static int +lpfc_sli4_post_nvme_sgl_block(struct lpfc_hba *phba, + struct list_head *nblist, + int count) +{ + struct lpfc_nvme_buf *lpfc_ncmd; + struct lpfc_mbx_post_uembed_sgl_page1 *sgl; + struct sgl_page_pairs *sgl_pg_pairs; + void *viraddr; + LPFC_MBOXQ_t *mbox; + uint32_t reqlen, alloclen, pg_pairs; + uint32_t mbox_tmo; + uint16_t xritag_start = 0; + int rc = 0; + uint32_t shdr_status, shdr_add_status; + dma_addr_t pdma_phys_bpl1; + union lpfc_sli4_cfg_shdr *shdr; + + /* Calculate the requested length of the dma memory */ + reqlen = count * sizeof(struct sgl_page_pairs) + + sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); + if (reqlen > SLI4_PAGE_SIZE) { + lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + "6118 Block sgl registration required DMA " + "size (%d) great than a page\n", reqlen); + return -ENOMEM; + } + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6119 Failed to allocate mbox cmd memory\n"); + return -ENOMEM; + } + + /* Allocate DMA memory and set up the non-embedded mailbox command */ + alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_POST_SGL_PAGES, reqlen, + LPFC_SLI4_MBX_NEMBED); + + if (alloclen < reqlen) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6120 Allocated DMA memory size (%d) is " + "less than the requested DMA memory " + "size (%d)\n", alloclen, reqlen); + lpfc_sli4_mbox_cmd_free(phba, mbox); + return -ENOMEM; + } + + /* Get the first SGE entry from the non-embedded DMA memory */ + viraddr = mbox->sge_array->addr[0]; + + /* Set up the SGL pages in the non-embedded DMA pages */ + sgl = (struct lpfc_mbx_post_uembed_sgl_page1 *)viraddr; + sgl_pg_pairs = &sgl->sgl_pg_pairs; + + pg_pairs = 0; + list_for_each_entry(lpfc_ncmd, nblist, list) { + /* Set up the sge entry */ + sgl_pg_pairs->sgl_pg0_addr_lo = + cpu_to_le32(putPaddrLow(lpfc_ncmd->dma_phys_sgl)); + sgl_pg_pairs->sgl_pg0_addr_hi = + cpu_to_le32(putPaddrHigh(lpfc_ncmd->dma_phys_sgl)); + if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE) + pdma_phys_bpl1 = lpfc_ncmd->dma_phys_sgl + + SGL_PAGE_SIZE; + else + pdma_phys_bpl1 = 0; + sgl_pg_pairs->sgl_pg1_addr_lo = + cpu_to_le32(putPaddrLow(pdma_phys_bpl1)); + sgl_pg_pairs->sgl_pg1_addr_hi = + cpu_to_le32(putPaddrHigh(pdma_phys_bpl1)); + /* Keep the first xritag on the list */ + if (pg_pairs == 0) + xritag_start = lpfc_ncmd->cur_iocbq.sli4_xritag; + sgl_pg_pairs++; + pg_pairs++; + } + bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start); + bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs); + /* Perform endian conversion if necessary */ + sgl->word0 = cpu_to_le32(sgl->word0); + + if (!phba->sli4_hba.intr_enable) + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + else { + mbox_tmo = lpfc_mbox_tmo_val(phba, mbox); + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); + } + shdr = (union lpfc_sli4_cfg_shdr *)&sgl->cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + if (rc != MBX_TIMEOUT) + lpfc_sli4_mbox_cmd_free(phba, mbox); + if (shdr_status || shdr_add_status || rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "6125 POST_SGL_BLOCK mailbox command failed " + "status x%x add_status x%x mbx status x%x\n", + shdr_status, shdr_add_status, rc); + rc = -ENXIO; + } + return rc; +} + +/** + * lpfc_post_nvme_sgl_list - Post blocks of nvme buffer sgls from a list + * @phba: pointer to lpfc hba data structure. + * @post_nblist: pointer to the nvme buffer list. + * + * This routine walks a list of nvme buffers that was passed in. It attempts + * to construct blocks of nvme buffer sgls which contains contiguous xris and + * uses the non-embedded SGL block post mailbox commands to post to the port. + * For single NVME buffer sgl with non-contiguous xri, if any, it shall use + * embedded SGL post mailbox command for posting. The @post_nblist passed in + * must be local list, thus no lock is needed when manipulate the list. + * + * Returns: 0 = failure, non-zero number of successfully posted buffers. + **/ +static int +lpfc_post_nvme_sgl_list(struct lpfc_hba *phba, + struct list_head *post_nblist, int sb_count) +{ + struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; + int status, sgl_size; + int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0; + dma_addr_t pdma_phys_sgl1; + int last_xritag = NO_XRI; + int cur_xritag; + LIST_HEAD(prep_nblist); + LIST_HEAD(blck_nblist); + LIST_HEAD(nvme_nblist); + + /* sanity check */ + if (sb_count <= 0) + return -EINVAL; + + sgl_size = phba->cfg_sg_dma_buf_size; + + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, post_nblist, list) { + list_del_init(&lpfc_ncmd->list); + block_cnt++; + if ((last_xritag != NO_XRI) && + (lpfc_ncmd->cur_iocbq.sli4_xritag != last_xritag + 1)) { + /* a hole in xri block, form a sgl posting block */ + list_splice_init(&prep_nblist, &blck_nblist); + post_cnt = block_cnt - 1; + /* prepare list for next posting block */ + list_add_tail(&lpfc_ncmd->list, &prep_nblist); + block_cnt = 1; + } else { + /* prepare list for next posting block */ + list_add_tail(&lpfc_ncmd->list, &prep_nblist); + /* enough sgls for non-embed sgl mbox command */ + if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) { + list_splice_init(&prep_nblist, &blck_nblist); + post_cnt = block_cnt; + block_cnt = 0; + } + } + num_posting++; + last_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag; + + /* end of repost sgl list condition for NVME buffers */ + if (num_posting == sb_count) { + if (post_cnt == 0) { + /* last sgl posting block */ + list_splice_init(&prep_nblist, &blck_nblist); + post_cnt = block_cnt; + } else if (block_cnt == 1) { + /* last single sgl with non-contiguous xri */ + if (sgl_size > SGL_PAGE_SIZE) + pdma_phys_sgl1 = + lpfc_ncmd->dma_phys_sgl + + SGL_PAGE_SIZE; + else + pdma_phys_sgl1 = 0; + cur_xritag = lpfc_ncmd->cur_iocbq.sli4_xritag; + status = lpfc_sli4_post_sgl(phba, + lpfc_ncmd->dma_phys_sgl, + pdma_phys_sgl1, cur_xritag); + if (status) { + /* failure, put on abort nvme list */ + lpfc_ncmd->exch_busy = 1; + } else { + /* success, put on NVME buffer list */ + lpfc_ncmd->exch_busy = 0; + lpfc_ncmd->status = IOSTAT_SUCCESS; + num_posted++; + } + /* success, put on NVME buffer sgl list */ + list_add_tail(&lpfc_ncmd->list, &nvme_nblist); + } + } + + /* continue until a nembed page worth of sgls */ + if (post_cnt == 0) + continue; + + /* post block of NVME buffer list sgls */ + status = lpfc_sli4_post_nvme_sgl_block(phba, &blck_nblist, + post_cnt); + + /* don't reset xirtag due to hole in xri block */ + if (block_cnt == 0) + last_xritag = NO_XRI; + + /* reset NVME buffer post count for next round of posting */ + post_cnt = 0; + + /* put posted NVME buffer-sgl posted on NVME buffer sgl list */ + while (!list_empty(&blck_nblist)) { + list_remove_head(&blck_nblist, lpfc_ncmd, + struct lpfc_nvme_buf, list); + if (status) { + /* failure, put on abort nvme list */ + lpfc_ncmd->exch_busy = 1; + } else { + /* success, put on NVME buffer list */ + lpfc_ncmd->exch_busy = 0; + lpfc_ncmd->status = IOSTAT_SUCCESS; + num_posted++; + } + list_add_tail(&lpfc_ncmd->list, &nvme_nblist); + } + } + /* Push NVME buffers with sgl posted to the available list */ + while (!list_empty(&nvme_nblist)) { + list_remove_head(&nvme_nblist, lpfc_ncmd, + struct lpfc_nvme_buf, list); + lpfc_release_nvme_buf(phba, lpfc_ncmd); + } + return num_posted; +} + +/** + * lpfc_repost_nvme_sgl_list - Repost all the allocated nvme buffer sgls + * @phba: pointer to lpfc hba data structure. + * + * This routine walks the list of nvme buffers that have been allocated and + * repost them to the port by using SGL block post. This is needed after a + * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine + * is responsible for moving all nvme buffers on the lpfc_abts_nvme_sgl_list + * to the lpfc_nvme_buf_list. If the repost fails, reject all nvme buffers. + * + * Returns: 0 = success, non-zero failure. + **/ +int +lpfc_repost_nvme_sgl_list(struct lpfc_hba *phba) +{ + LIST_HEAD(post_nblist); + int num_posted, rc = 0; + + /* get all NVME buffers need to repost to a local list */ + spin_lock_irq(&phba->nvme_buf_list_get_lock); + spin_lock(&phba->nvme_buf_list_put_lock); + list_splice_init(&phba->lpfc_nvme_buf_list_get, &post_nblist); + list_splice(&phba->lpfc_nvme_buf_list_put, &post_nblist); + spin_unlock(&phba->nvme_buf_list_put_lock); + spin_unlock_irq(&phba->nvme_buf_list_get_lock); + + /* post the list of nvme buffer sgls to port if available */ + if (!list_empty(&post_nblist)) { + num_posted = lpfc_post_nvme_sgl_list(phba, &post_nblist, + phba->sli4_hba.nvme_xri_cnt); + /* failed to post any nvme buffer, return error */ + if (num_posted == 0) + rc = -EIO; + } + return rc; +} + +/** + * lpfc_new_nvme_buf - Scsi buffer allocator for HBA with SLI4 IF spec + * @vport: The virtual port for which this call being executed. + * @num_to_allocate: The requested number of buffers to allocate. + * + * This routine allocates nvme buffers for device with SLI-4 interface spec, + * the nvme buffer contains all the necessary information needed to initiate + * a NVME I/O. After allocating up to @num_to_allocate NVME buffers and put + * them on a list, it post them to the port by using SGL block post. + * + * Return codes: + * int - number of nvme buffers that were allocated and posted. + * 0 = failure, less than num_to_alloc is a partial failure. + **/ +static int +lpfc_new_nvme_buf(struct lpfc_vport *vport, int num_to_alloc) +{ + struct lpfc_hba *phba = vport->phba; + struct lpfc_nvme_buf *lpfc_ncmd; + struct lpfc_iocbq *pwqeq; + union lpfc_wqe128 *wqe; + struct sli4_sge *sgl; + dma_addr_t pdma_phys_sgl; + uint16_t iotag, lxri = 0; + int bcnt, num_posted, sgl_size; + LIST_HEAD(prep_nblist); + LIST_HEAD(post_nblist); + LIST_HEAD(nvme_nblist); + + sgl_size = phba->cfg_sg_dma_buf_size; + + for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { + lpfc_ncmd = kzalloc(sizeof(struct lpfc_nvme_buf), GFP_KERNEL); + if (!lpfc_ncmd) + break; + /* + * Get memory from the pci pool to map the virt space to + * pci bus space for an I/O. The DMA buffer includes the + * number of SGE's necessary to support the sg_tablesize. + */ + lpfc_ncmd->data = pci_pool_alloc(phba->lpfc_sg_dma_buf_pool, + GFP_KERNEL, + &lpfc_ncmd->dma_handle); + if (!lpfc_ncmd->data) { + kfree(lpfc_ncmd); + break; + } + memset(lpfc_ncmd->data, 0, phba->cfg_sg_dma_buf_size); + + lxri = lpfc_sli4_next_xritag(phba); + if (lxri == NO_XRI) { + pci_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + break; + } + pwqeq = &(lpfc_ncmd->cur_iocbq); + wqe = (union lpfc_wqe128 *)&pwqeq->wqe; + + /* Allocate iotag for lpfc_ncmd->cur_iocbq. */ + iotag = lpfc_sli_next_iotag(phba, pwqeq); + if (iotag == 0) { + pci_pool_free(phba->lpfc_sg_dma_buf_pool, + lpfc_ncmd->data, lpfc_ncmd->dma_handle); + kfree(lpfc_ncmd); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6121 Failed to allocated IOTAG for" + " XRI:0x%x\n", lxri); + lpfc_sli4_free_xri(phba, lxri); + break; + } + pwqeq->sli4_lxritag = lxri; + pwqeq->sli4_xritag = phba->sli4_hba.xri_ids[lxri]; + pwqeq->iocb_flag |= LPFC_IO_NVME; + pwqeq->context1 = lpfc_ncmd; + pwqeq->wqe_cmpl = lpfc_nvme_io_cmd_wqe_cmpl; + + /* Initialize local short-hand pointers. */ + lpfc_ncmd->nvme_sgl = lpfc_ncmd->data; + sgl = lpfc_ncmd->nvme_sgl; + pdma_phys_sgl = lpfc_ncmd->dma_handle; + lpfc_ncmd->dma_phys_sgl = pdma_phys_sgl; + + /* Rsp SGE will be filled in when we rcv an IO + * from the NVME Layer to be sent. + * The cmd is going to be embedded so we need a SKIP SGE. + */ + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP); + bf_set(lpfc_sli4_sge_last, sgl, 0); + sgl->word2 = cpu_to_le32(sgl->word2); + /* Fill in word 3 / sgl_len during cmd submission */ + + lpfc_ncmd->cur_iocbq.context1 = lpfc_ncmd; + + /* Word 7 */ + bf_set(wqe_erp, &wqe->generic.wqe_com, 0); + /* NVME upper layers will time things out, if needed */ + bf_set(wqe_tmo, &wqe->generic.wqe_com, 0); + + /* Word 10 */ + bf_set(wqe_ebde_cnt, &wqe->generic.wqe_com, 0); + bf_set(wqe_dbde, &wqe->generic.wqe_com, 1); + + /* add the nvme buffer to a post list */ + list_add_tail(&lpfc_ncmd->list, &post_nblist); + spin_lock_irq(&phba->nvme_buf_list_get_lock); + phba->sli4_hba.nvme_xri_cnt++; + spin_unlock_irq(&phba->nvme_buf_list_get_lock); + } + lpfc_printf_log(phba, KERN_INFO, LOG_NVME, + "6114 Allocate %d out of %d requested new NVME " + "buffers\n", bcnt, num_to_alloc); + + /* post the list of nvme buffer sgls to port if available */ + if (!list_empty(&post_nblist)) + num_posted = lpfc_post_nvme_sgl_list(phba, + &post_nblist, bcnt); + else + num_posted = 0; + + return num_posted; +} + +/** + * lpfc_get_nvme_buf - Get a nvme buffer from lpfc_nvme_buf_list of the HBA + * @phba: The HBA for which this call is being executed. + * + * This routine removes a nvme buffer from head of @phba lpfc_nvme_buf_list list + * and returns to caller. + * + * Return codes: + * NULL - Error + * Pointer to lpfc_nvme_buf - Success + **/ +static struct lpfc_nvme_buf * +lpfc_get_nvme_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +{ + struct lpfc_nvme_buf *lpfc_ncmd, *lpfc_ncmd_next; + unsigned long iflag = 0; + int found = 0; + + spin_lock_irqsave(&phba->nvme_buf_list_get_lock, iflag); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &phba->lpfc_nvme_buf_list_get, list) { + if (lpfc_test_rrq_active(phba, ndlp, + lpfc_ncmd->cur_iocbq.sli4_lxritag)) + continue; + list_del(&lpfc_ncmd->list); + found = 1; + break; + } + if (!found) { + spin_lock(&phba->nvme_buf_list_put_lock); + list_splice(&phba->lpfc_nvme_buf_list_put, + &phba->lpfc_nvme_buf_list_get); + INIT_LIST_HEAD(&phba->lpfc_nvme_buf_list_put); + spin_unlock(&phba->nvme_buf_list_put_lock); + list_for_each_entry_safe(lpfc_ncmd, lpfc_ncmd_next, + &phba->lpfc_nvme_buf_list_get, list) { + if (lpfc_test_rrq_active( + phba, ndlp, lpfc_ncmd->cur_iocbq.sli4_lxritag)) + continue; + list_del(&lpfc_ncmd->list); + found = 1; + break; + } + } + spin_unlock_irqrestore(&phba->nvme_buf_list_get_lock, iflag); + if (!found) + return NULL; + return lpfc_ncmd; +} + +/** + * lpfc_release_nvme_buf: Return a nvme buffer back to hba nvme buf list. + * @phba: The Hba for which this call is being executed. + * @lpfc_ncmd: The nvme buffer which is being released. + * + * This routine releases @lpfc_ncmd nvme buffer by adding it to tail of @phba + * lpfc_nvme_buf_list list. For SLI4 XRI's are tied to the nvme buffer + * and cannot be reused for at least RA_TOV amount of time if it was + * aborted. + **/ +static void +lpfc_release_nvme_buf(struct lpfc_hba *phba, struct lpfc_nvme_buf *lpfc_ncmd) +{ + unsigned long iflag = 0; + + lpfc_ncmd->nonsg_phys = 0; + if (lpfc_ncmd->exch_busy) { + spin_lock_irqsave(&phba->sli4_hba.abts_nvme_buf_list_lock, + iflag); + lpfc_ncmd->nvmeCmd = NULL; + list_add_tail(&lpfc_ncmd->list, + &phba->sli4_hba.lpfc_abts_nvme_buf_list); + spin_unlock_irqrestore(&phba->sli4_hba.abts_nvme_buf_list_lock, + iflag); + } else { + lpfc_ncmd->nvmeCmd = NULL; + lpfc_ncmd->cur_iocbq.iocb_flag = LPFC_IO_NVME; + spin_lock_irqsave(&phba->nvme_buf_list_put_lock, iflag); + list_add_tail(&lpfc_ncmd->list, &phba->lpfc_nvme_buf_list_put); + spin_unlock_irqrestore(&phba->nvme_buf_list_put_lock, iflag); + } +} + +/** + * lpfc_nvme_create_localport - Create/Bind an nvme localport instance. + * @pvport - the lpfc_vport instance requesting a localport. + * + * This routine is invoked to create an nvme localport instance to bind + * to the nvme_fc_transport. It is called once during driver load + * like lpfc_create_shost after all other services are initialized. + * It requires a vport, vpi, and wwns at call time. Other localport + * parameters are modified as the driver's FCID and the Fabric WWN + * are established. + * + * Return codes + * 0 - successful + * -ENOMEM - no heap memory available + * other values - from nvme registration upcall + **/ +int +lpfc_nvme_create_localport(struct lpfc_vport *vport) +{ + struct lpfc_hba *phba = vport->phba; + struct nvme_fc_port_info nfcp_info; + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + int len, ret = 0; + + /* Initialize this localport instance. The vport wwn usage ensures + * that NPIV is accounted for. + */ + memset(&nfcp_info, 0, sizeof(struct nvme_fc_port_info)); + nfcp_info.port_role = FC_PORT_ROLE_NVME_INITIATOR; + nfcp_info.node_name = wwn_to_u64(vport->fc_nodename.u.wwn); + nfcp_info.port_name = wwn_to_u64(vport->fc_portname.u.wwn); + + /* For now need + 1 to get around NVME transport logic */ + lpfc_nvme_template.max_sgl_segments = phba->cfg_sg_seg_cnt + 1; + lpfc_nvme_template.max_hw_queues = phba->cfg_nvme_io_channel; + + /* localport is allocated from the stack, but the registration + * call allocates heap memory as well as the private area. + */ + ret = nvme_fc_register_localport(&nfcp_info, &lpfc_nvme_template, + &vport->phba->pcidev->dev, &localport); + if (!ret) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME | LOG_NVME_DISC, + "6005 Successfully registered local " + "NVME port num %d, localP %p, private %p, " + "sg_seg %d\n", + localport->port_num, localport, + localport->private, + lpfc_nvme_template.max_sgl_segments); + + /* Private is our lport size declared in the template. */ + lport = (struct lpfc_nvme_lport *)localport->private; + vport->localport = localport; + lport->vport = vport; + INIT_LIST_HEAD(&lport->rport_list); + vport->nvmei_support = 1; + } + + len = lpfc_new_nvme_buf(vport, phba->sli4_hba.nvme_xri_max); + vport->phba->total_nvme_bufs += len; + return ret; +} + +/** + * lpfc_nvme_destroy_localport - Destroy lpfc_nvme bound to nvme transport. + * @pnvme: pointer to lpfc nvme data structure. + * + * This routine is invoked to destroy all lports bound to the phba. + * The lport memory was allocated by the nvme fc transport and is + * released there. This routine ensures all rports bound to the + * lport have been disconnected. + * + **/ +void +lpfc_nvme_destroy_localport(struct lpfc_vport *vport) +{ + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport = NULL, *rport_next = NULL; + int ret; + + if (vport->nvmei_support == 0) + return; + + localport = vport->localport; + vport->localport = NULL; + lport = (struct lpfc_nvme_lport *)localport->private; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, + "6011 Destroying NVME localport %p\n", + localport); + + list_for_each_entry_safe(rport, rport_next, &lport->rport_list, list) { + /* The last node ref has to get released now before the rport + * private memory area is released by the transport. + */ + list_del(&rport->list); + + init_completion(&rport->rport_unreg_done); + ret = nvme_fc_unregister_remoteport(rport->remoteport); + if (ret) + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6008 rport fail destroy %x\n", ret); + wait_for_completion_timeout(&rport->rport_unreg_done, 5); + } + /* lport's rport list is clear. Unregister + * lport and release resources. + */ + init_completion(&lport->lport_unreg_done); + ret = nvme_fc_unregister_localport(localport); + wait_for_completion_timeout(&lport->lport_unreg_done, 5); + + /* Regardless of the unregister upcall response, clear + * nvmei_support. All rports are unregistered and the + * driver will clean up. + */ + vport->nvmei_support = 0; + if (ret == 0) { + lpfc_printf_vlog(vport, + KERN_INFO, LOG_NVME_DISC, + "6009 Unregistered lport Success\n"); + } else { + lpfc_printf_vlog(vport, + KERN_INFO, LOG_NVME_DISC, + "6010 Unregistered lport " + "Failed, status x%x\n", + ret); + } +} + +void +lpfc_nvme_update_localport(struct lpfc_vport *vport) +{ + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + + localport = vport->localport; + lport = (struct lpfc_nvme_lport *)localport->private; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, + "6012 Update NVME lport %p did x%x\n", + localport, vport->fc_myDID); + + localport->port_id = vport->fc_myDID; + if (localport->port_id == 0) + localport->port_role = FC_PORT_ROLE_NVME_DISCOVERY; + else + localport->port_role = FC_PORT_ROLE_NVME_INITIATOR; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6030 bound lport %p to DID x%06x\n", + lport, localport->port_id); + +} + +int +lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) +{ + int ret = 0; + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport; + struct nvme_fc_remote_port *remote_port; + struct nvme_fc_port_info rpinfo; + + lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NVME_DISC, + "6006 Register NVME PORT. DID x%06x nlptype x%x\n", + ndlp->nlp_DID, ndlp->nlp_type); + + localport = vport->localport; + lport = (struct lpfc_nvme_lport *)localport->private; + + if (ndlp->nlp_type & (NLP_NVME_TARGET | NLP_NVME_INITIATOR)) { + + /* The driver isn't expecting the rport wwn to change + * but it might get a different DID on a different + * fabric. + */ + list_for_each_entry(rport, &lport->rport_list, list) { + if (rport->remoteport->port_name != + wwn_to_u64(ndlp->nlp_portname.u.wwn)) + continue; + lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NVME_DISC, + "6035 lport %p, found matching rport " + "at wwpn 0x%llx, Data: x%x x%x x%x " + "x%06x\n", + lport, + rport->remoteport->port_name, + rport->remoteport->port_id, + rport->remoteport->port_role, + ndlp->nlp_type, + ndlp->nlp_DID); + remote_port = rport->remoteport; + if ((remote_port->port_id == 0) && + (remote_port->port_role == + FC_PORT_ROLE_NVME_DISCOVERY)) { + remote_port->port_id = ndlp->nlp_DID; + remote_port->port_role &= + ~FC_PORT_ROLE_NVME_DISCOVERY; + if (ndlp->nlp_type & NLP_NVME_TARGET) + remote_port->port_role |= + FC_PORT_ROLE_NVME_TARGET; + if (ndlp->nlp_type & NLP_NVME_INITIATOR) + remote_port->port_role |= + FC_PORT_ROLE_NVME_INITIATOR; + + lpfc_printf_vlog(ndlp->vport, KERN_INFO, + LOG_NVME_DISC, + "6014 Rebinding lport to " + "rport wwpn 0x%llx, " + "Data: x%x x%x x%x x%06x\n", + remote_port->port_name, + remote_port->port_id, + remote_port->port_role, + ndlp->nlp_type, + ndlp->nlp_DID); + } + return 0; + } + + /* NVME rports are not preserved across devloss. + * Just register this instance. + */ + rpinfo.port_id = ndlp->nlp_DID; + rpinfo.port_role = 0; + if (ndlp->nlp_type & NLP_NVME_TARGET) + rpinfo.port_role |= FC_PORT_ROLE_NVME_TARGET; + if (ndlp->nlp_type & NLP_NVME_INITIATOR) + rpinfo.port_role |= FC_PORT_ROLE_NVME_INITIATOR; + rpinfo.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn); + rpinfo.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn); + + ret = nvme_fc_register_remoteport(localport, &rpinfo, + &remote_port); + if (!ret) { + rport = remote_port->private; + rport->remoteport = remote_port; + rport->lport = lport; + rport->ndlp = lpfc_nlp_get(ndlp); + if (!rport->ndlp) + return -1; + ndlp->nrport = rport; + INIT_LIST_HEAD(&rport->list); + list_add_tail(&rport->list, &lport->rport_list); + lpfc_printf_vlog(vport, KERN_INFO, + LOG_NVME_DISC | LOG_NODE, + "6022 Binding new rport to lport %p " + "Rport WWNN 0x%llx, Rport WWPN 0x%llx " + "DID x%06x Role x%x\n", + lport, + rpinfo.node_name, rpinfo.port_name, + rpinfo.port_id, rpinfo.port_role); + } else { + lpfc_printf_vlog(vport, KERN_ERR, + LOG_NVME_DISC | LOG_NODE, + "6031 RemotePort Registration failed " + "err: %d, DID x%06x\n", + ret, ndlp->nlp_DID); + } + } else { + ret = -EINVAL; + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6027 Unknown nlp_type x%x on DID x%06x " + "ndlp %p. Not Registering nvme rport\n", + ndlp->nlp_type, ndlp->nlp_DID, ndlp); + } + return ret; +} + +/* lpfc_nvme_unregister_port - unbind the DID and port_role from this rport. + * + * There is no notion of Devloss or rport recovery from the current + * nvme_transport perspective. Loss of an rport just means IO cannot + * be sent and recovery is completely up to the initator. + * For now, the driver just unbinds the DID and port_role so that + * no further IO can be issued. Changes are planned for later. + * + * Notes - the ndlp reference count is not decremented here since + * since there is no nvme_transport api for devloss. Node ref count + * is only adjusted in driver unload. + */ +void +lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) +{ + int ret; + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport; + struct nvme_fc_remote_port *remoteport; + + localport = vport->localport; + + /* This is fundamental error. The localport is always + * available until driver unload. Just exit. + */ + if (!localport) + return; + + lport = (struct lpfc_nvme_lport *)localport->private; + if (!lport) + goto input_err; + + rport = ndlp->nrport; + if (!rport) + goto input_err; + + remoteport = rport->remoteport; + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, + "6033 Unreg nvme remoteport %p, portname x%llx, " + "port_id x%06x, portstate x%x port type x%x\n", + remoteport, remoteport->port_name, + remoteport->port_id, remoteport->port_state, + ndlp->nlp_type); + + /* Sanity check ndlp type. Only call for NVME ports. Don't + * clear any rport state until the transport calls back. + */ + if (ndlp->nlp_type & (NLP_NVME_TARGET | NLP_NVME_INITIATOR)) { + init_completion(&rport->rport_unreg_done); + ret = nvme_fc_unregister_remoteport(remoteport); + if (ret != 0) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6167 NVME unregister failed %d " + "port_state x%x\n", + ret, remoteport->port_state); + } + + /* Wait for the driver's delete completion routine to finish + * before proceeding. This guarantees the transport and driver + * have completed the unreg process. + */ + ret = wait_for_completion_timeout(&rport->rport_unreg_done, 5); + if (ret == 0) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6169 Unreg nvme wait failed %d\n", + ret); + } + } + return; + + input_err: + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_DISC, + "6168: State error: lport %p, rport%p FCID x%06x\n", + vport->localport, ndlp->rport, ndlp->nlp_DID); +} diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h new file mode 100644 index 000000000000..b2fae5e813f8 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -0,0 +1,103 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * Copyright (C) 2004-2016 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.broadcom.com * + * Portions Copyright (C) 2004-2005 Christoph Hellwig * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + ********************************************************************/ + +#define LPFC_NVME_MIN_SEGS 16 +#define LPFC_NVME_DEFAULT_SEGS 66 /* 256K IOs - 64 + 2 */ +#define LPFC_NVME_MAX_SEGS 510 +#define LPFC_NVMET_MIN_POSTBUF 16 +#define LPFC_NVMET_DEFAULT_POSTBUF 1024 +#define LPFC_NVMET_MAX_POSTBUF 4096 +#define LPFC_NVME_WQSIZE 256 + +#define LPFC_NVME_ERSP_LEN 0x20 + +struct lpfc_nvme_qhandle { + uint32_t index; /* WQ index to use */ + uint32_t qidx; /* queue index passed to create */ + uint32_t cpu_id; /* current cpu id at time of create */ +}; + +/* Declare nvme-based local and remote port definitions. */ +struct lpfc_nvme_lport { + struct lpfc_vport *vport; + struct list_head rport_list; + struct completion lport_unreg_done; + /* Add sttats counters here */ +}; + +struct lpfc_nvme_rport { + struct list_head list; + struct lpfc_nvme_lport *lport; + struct nvme_fc_remote_port *remoteport; + struct lpfc_nodelist *ndlp; + struct completion rport_unreg_done; +}; + +struct lpfc_nvme_buf { + struct list_head list; + struct nvmefc_fcp_req *nvmeCmd; + struct lpfc_nvme_rport *nrport; + + uint32_t timeout; + + uint16_t flags; /* TBD convert exch_busy to flags */ +#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ + uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ + uint16_t status; /* From IOCB Word 7- ulpStatus */ + uint16_t cpu; + uint16_t qidx; + uint16_t sqid; + uint32_t result; /* From IOCB Word 4. */ + + uint32_t seg_cnt; /* Number of scatter-gather segments returned by + * dma_map_sg. The driver needs this for calls + * to dma_unmap_sg. + */ + dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */ + + /* + * data and dma_handle are the kernel virtual and bus address of the + * dma-able buffer containing the fcp_cmd, fcp_rsp and a scatter + * gather bde list that supports the sg_tablesize value. + */ + void *data; + dma_addr_t dma_handle; + + struct sli4_sge *nvme_sgl; + dma_addr_t dma_phys_sgl; + + /* cur_iocbq has phys of the dma-able buffer. + * Iotag is in here + */ + struct lpfc_iocbq cur_iocbq; + + wait_queue_head_t *waitq; + unsigned long start_time; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint64_t ts_cmd_start; + uint64_t ts_last_cmd; + uint64_t ts_cmd_wqput; + uint64_t ts_isr_cmpl; + uint64_t ts_data_nvme; +#endif +}; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c new file mode 100644 index 000000000000..c421e1738ee9 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -0,0 +1,1986 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channsel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * Copyright (C) 2004-2016 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.broadcom.com * + * Portions Copyright (C) 2004-2005 Christoph Hellwig * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + ********************************************************************/ +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/unaligned.h> +#include <linux/crc-t10dif.h> +#include <net/checksum.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_transport_fc.h> +#include <scsi/fc/fc_fs.h> + +#include <../drivers/nvme/host/nvme.h> +#include <linux/nvme-fc-driver.h> + +#include "lpfc_version.h" +#include "lpfc_hw4.h" +#include "lpfc_hw.h" +#include "lpfc_sli.h" +#include "lpfc_sli4.h" +#include "lpfc_nl.h" +#include "lpfc_disc.h" +#include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" +#include "lpfc_nvmet.h" +#include "lpfc_logmsg.h" +#include "lpfc_crtn.h" +#include "lpfc_vport.h" +#include "lpfc_debugfs.h" + +static struct lpfc_iocbq *lpfc_nvmet_prep_ls_wqe(struct lpfc_hba *, + struct lpfc_nvmet_rcv_ctx *, + dma_addr_t rspbuf, + uint16_t rspsize); +static struct lpfc_iocbq *lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *, + struct lpfc_nvmet_rcv_ctx *); +static int lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *, + struct lpfc_nvmet_rcv_ctx *, + uint32_t, uint16_t); +static int lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *, + struct lpfc_nvmet_rcv_ctx *, + uint32_t, uint16_t); +static int lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *, + struct lpfc_nvmet_rcv_ctx *, + uint32_t, uint16_t); + +/** + * lpfc_nvmet_xmt_ls_rsp_cmp - Completion handler for LS Response + * @phba: Pointer to HBA context object. + * @cmdwqe: Pointer to driver command WQE object. + * @wcqe: Pointer to driver response CQE object. + * + * The function is called from SLI ring event handler with no + * lock held. This function is the completion handler for NVME LS commands + * The function frees memory resources used for the NVME commands. + **/ +static void +lpfc_nvmet_xmt_ls_rsp_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, + struct lpfc_wcqe_complete *wcqe) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct nvmefc_tgt_ls_req *rsp; + struct lpfc_nvmet_rcv_ctx *ctxp; + uint32_t status, result; + + status = bf_get(lpfc_wcqe_c_status, wcqe); + result = wcqe->parameter; + if (!phba->targetport) + goto out; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + + if (status) + atomic_inc(&tgtp->xmt_ls_rsp_error); + else + atomic_inc(&tgtp->xmt_ls_rsp_cmpl); + +out: + ctxp = cmdwqe->context2; + rsp = &ctxp->ctx.ls_req; + + lpfc_nvmeio_data(phba, "NVMET LS CMPL: xri x%x stat x%x result x%x\n", + ctxp->oxid, status, result); + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, + "6038 %s: Entrypoint: ctx %p status %x/%x\n", __func__, + ctxp, status, result); + + lpfc_nlp_put(cmdwqe->context1); + cmdwqe->context2 = NULL; + cmdwqe->context3 = NULL; + lpfc_sli_release_iocbq(phba, cmdwqe); + rsp->done(rsp); + kfree(ctxp); +} + +/** + * lpfc_nvmet_rq_post - Repost a NVMET RQ DMA buffer and clean up context + * @phba: HBA buffer is associated with + * @ctxp: context to clean up + * @mp: Buffer to free + * + * Description: Frees the given DMA buffer in the appropriate way given by + * reposting it to its associated RQ so it can be reused. + * + * Notes: Takes phba->hbalock. Can be called with or without other locks held. + * + * Returns: None + **/ +void +lpfc_nvmet_rq_post(struct lpfc_hba *phba, struct lpfc_nvmet_rcv_ctx *ctxp, + struct lpfc_dmabuf *mp) +{ + if (ctxp) { + if (ctxp->txrdy) { + pci_pool_free(phba->txrdy_payload_pool, ctxp->txrdy, + ctxp->txrdy_phys); + ctxp->txrdy = NULL; + ctxp->txrdy_phys = 0; + } + ctxp->state = LPFC_NVMET_STE_FREE; + } + lpfc_rq_buf_free(phba, mp); +} + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS +static void +lpfc_nvmet_ktime(struct lpfc_hba *phba, + struct lpfc_nvmet_rcv_ctx *ctxp) +{ + uint64_t seg1, seg2, seg3, seg4, seg5; + uint64_t seg6, seg7, seg8, seg9, seg10; + + if (!phba->ktime_on) + return; + + if (!ctxp->ts_isr_cmd || !ctxp->ts_cmd_nvme || + !ctxp->ts_nvme_data || !ctxp->ts_data_wqput || + !ctxp->ts_isr_data || !ctxp->ts_data_nvme || + !ctxp->ts_nvme_status || !ctxp->ts_status_wqput || + !ctxp->ts_isr_status || !ctxp->ts_status_nvme) + return; + + if (ctxp->ts_isr_cmd > ctxp->ts_cmd_nvme) + return; + if (ctxp->ts_cmd_nvme > ctxp->ts_nvme_data) + return; + if (ctxp->ts_nvme_data > ctxp->ts_data_wqput) + return; + if (ctxp->ts_data_wqput > ctxp->ts_isr_data) + return; + if (ctxp->ts_isr_data > ctxp->ts_data_nvme) + return; + if (ctxp->ts_data_nvme > ctxp->ts_nvme_status) + return; + if (ctxp->ts_nvme_status > ctxp->ts_status_wqput) + return; + if (ctxp->ts_status_wqput > ctxp->ts_isr_status) + return; + if (ctxp->ts_isr_status > ctxp->ts_status_nvme) + return; + /* + * Segment 1 - Time from FCP command received by MSI-X ISR + * to FCP command is passed to NVME Layer. + * Segment 2 - Time from FCP command payload handed + * off to NVME Layer to Driver receives a Command op + * from NVME Layer. + * Segment 3 - Time from Driver receives a Command op + * from NVME Layer to Command is put on WQ. + * Segment 4 - Time from Driver WQ put is done + * to MSI-X ISR for Command cmpl. + * Segment 5 - Time from MSI-X ISR for Command cmpl to + * Command cmpl is passed to NVME Layer. + * Segment 6 - Time from Command cmpl is passed to NVME + * Layer to Driver receives a RSP op from NVME Layer. + * Segment 7 - Time from Driver receives a RSP op from + * NVME Layer to WQ put is done on TRSP FCP Status. + * Segment 8 - Time from Driver WQ put is done on TRSP + * FCP Status to MSI-X ISR for TRSP cmpl. + * Segment 9 - Time from MSI-X ISR for TRSP cmpl to + * TRSP cmpl is passed to NVME Layer. + * Segment 10 - Time from FCP command received by + * MSI-X ISR to command is completed on wire. + * (Segments 1 thru 8) for READDATA / WRITEDATA + * (Segments 1 thru 4) for READDATA_RSP + */ + seg1 = ctxp->ts_cmd_nvme - ctxp->ts_isr_cmd; + seg2 = (ctxp->ts_nvme_data - ctxp->ts_isr_cmd) - seg1; + seg3 = (ctxp->ts_data_wqput - ctxp->ts_isr_cmd) - + seg1 - seg2; + seg4 = (ctxp->ts_isr_data - ctxp->ts_isr_cmd) - + seg1 - seg2 - seg3; + seg5 = (ctxp->ts_data_nvme - ctxp->ts_isr_cmd) - + seg1 - seg2 - seg3 - seg4; + + /* For auto rsp commands seg6 thru seg10 will be 0 */ + if (ctxp->ts_nvme_status > ctxp->ts_data_nvme) { + seg6 = (ctxp->ts_nvme_status - + ctxp->ts_isr_cmd) - + seg1 - seg2 - seg3 - seg4 - seg5; + seg7 = (ctxp->ts_status_wqput - + ctxp->ts_isr_cmd) - + seg1 - seg2 - seg3 - + seg4 - seg5 - seg6; + seg8 = (ctxp->ts_isr_status - + ctxp->ts_isr_cmd) - + seg1 - seg2 - seg3 - seg4 - + seg5 - seg6 - seg7; + seg9 = (ctxp->ts_status_nvme - + ctxp->ts_isr_cmd) - + seg1 - seg2 - seg3 - seg4 - + seg5 - seg6 - seg7 - seg8; + seg10 = (ctxp->ts_isr_status - + ctxp->ts_isr_cmd); + } else { + seg6 = 0; + seg7 = 0; + seg8 = 0; + seg9 = 0; + seg10 = (ctxp->ts_isr_data - ctxp->ts_isr_cmd); + } + + phba->ktime_seg1_total += seg1; + if (seg1 < phba->ktime_seg1_min) + phba->ktime_seg1_min = seg1; + else if (seg1 > phba->ktime_seg1_max) + phba->ktime_seg1_max = seg1; + + phba->ktime_seg2_total += seg2; + if (seg2 < phba->ktime_seg2_min) + phba->ktime_seg2_min = seg2; + else if (seg2 > phba->ktime_seg2_max) + phba->ktime_seg2_max = seg2; + + phba->ktime_seg3_total += seg3; + if (seg3 < phba->ktime_seg3_min) + phba->ktime_seg3_min = seg3; + else if (seg3 > phba->ktime_seg3_max) + phba->ktime_seg3_max = seg3; + + phba->ktime_seg4_total += seg4; + if (seg4 < phba->ktime_seg4_min) + phba->ktime_seg4_min = seg4; + else if (seg4 > phba->ktime_seg4_max) + phba->ktime_seg4_max = seg4; + + phba->ktime_seg5_total += seg5; + if (seg5 < phba->ktime_seg5_min) + phba->ktime_seg5_min = seg5; + else if (seg5 > phba->ktime_seg5_max) + phba->ktime_seg5_max = seg5; + + phba->ktime_data_samples++; + if (!seg6) + goto out; + + phba->ktime_seg6_total += seg6; + if (seg6 < phba->ktime_seg6_min) + phba->ktime_seg6_min = seg6; + else if (seg6 > phba->ktime_seg6_max) + phba->ktime_seg6_max = seg6; + + phba->ktime_seg7_total += seg7; + if (seg7 < phba->ktime_seg7_min) + phba->ktime_seg7_min = seg7; + else if (seg7 > phba->ktime_seg7_max) + phba->ktime_seg7_max = seg7; + + phba->ktime_seg8_total += seg8; + if (seg8 < phba->ktime_seg8_min) + phba->ktime_seg8_min = seg8; + else if (seg8 > phba->ktime_seg8_max) + phba->ktime_seg8_max = seg8; + + phba->ktime_seg9_total += seg9; + if (seg9 < phba->ktime_seg9_min) + phba->ktime_seg9_min = seg9; + else if (seg9 > phba->ktime_seg9_max) + phba->ktime_seg9_max = seg9; +out: + phba->ktime_seg10_total += seg10; + if (seg10 < phba->ktime_seg10_min) + phba->ktime_seg10_min = seg10; + else if (seg10 > phba->ktime_seg10_max) + phba->ktime_seg10_max = seg10; + phba->ktime_status_samples++; +} +#endif + +/** + * lpfc_nvmet_xmt_fcp_op_cmp - Completion handler for FCP Response + * @phba: Pointer to HBA context object. + * @cmdwqe: Pointer to driver command WQE object. + * @wcqe: Pointer to driver response CQE object. + * + * The function is called from SLI ring event handler with no + * lock held. This function is the completion handler for NVME FCP commands + * The function frees memory resources used for the NVME commands. + **/ +static void +lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, + struct lpfc_wcqe_complete *wcqe) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct nvmefc_tgt_fcp_req *rsp; + struct lpfc_nvmet_rcv_ctx *ctxp; + uint32_t status, result, op, start_clean; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint32_t id; +#endif + + ctxp = cmdwqe->context2; + rsp = &ctxp->ctx.fcp_req; + op = rsp->op; + ctxp->flag &= ~LPFC_NVMET_IO_INP; + + status = bf_get(lpfc_wcqe_c_status, wcqe); + result = wcqe->parameter; + + if (!phba->targetport) + goto out; + + lpfc_nvmeio_data(phba, "NVMET FCP CMPL: xri x%x op x%x status x%x\n", + ctxp->oxid, op, status); + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + if (status) { + rsp->fcp_error = NVME_SC_DATA_XFER_ERROR; + rsp->transferred_length = 0; + atomic_inc(&tgtp->xmt_fcp_rsp_error); + } else { + rsp->fcp_error = NVME_SC_SUCCESS; + if (op == NVMET_FCOP_RSP) + rsp->transferred_length = rsp->rsplen; + else + rsp->transferred_length = rsp->transfer_length; + atomic_inc(&tgtp->xmt_fcp_rsp_cmpl); + } + +out: + if ((op == NVMET_FCOP_READDATA_RSP) || + (op == NVMET_FCOP_RSP)) { + /* Sanity check */ + ctxp->state = LPFC_NVMET_STE_DONE; + ctxp->entry_cnt++; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) { + if (rsp->op == NVMET_FCOP_READDATA_RSP) { + ctxp->ts_isr_data = + cmdwqe->isr_timestamp; + ctxp->ts_data_nvme = + ktime_get_ns(); + ctxp->ts_nvme_status = + ctxp->ts_data_nvme; + ctxp->ts_status_wqput = + ctxp->ts_data_nvme; + ctxp->ts_isr_status = + ctxp->ts_data_nvme; + ctxp->ts_status_nvme = + ctxp->ts_data_nvme; + } else { + ctxp->ts_isr_status = + cmdwqe->isr_timestamp; + ctxp->ts_status_nvme = + ktime_get_ns(); + } + } + if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { + id = smp_processor_id(); + if (ctxp->cpu != id) + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6703 CPU Check cmpl: " + "cpu %d expect %d\n", + id, ctxp->cpu); + if (ctxp->cpu < LPFC_CHECK_CPU_CNT) + phba->cpucheck_cmpl_io[id]++; + } +#endif + rsp->done(rsp); +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) + lpfc_nvmet_ktime(phba, ctxp); +#endif + /* Let Abort cmpl repost the context */ + if (!(ctxp->flag & LPFC_NVMET_ABORT_OP)) + lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf); + } else { + ctxp->entry_cnt++; + start_clean = offsetof(struct lpfc_iocbq, wqe); + memset(((char *)cmdwqe) + start_clean, 0, + (sizeof(struct lpfc_iocbq) - start_clean)); +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) { + ctxp->ts_isr_data = cmdwqe->isr_timestamp; + ctxp->ts_data_nvme = ktime_get_ns(); + } + if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { + id = smp_processor_id(); + if (ctxp->cpu != id) + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6704 CPU Check cmdcmpl: " + "cpu %d expect %d\n", + id, ctxp->cpu); + if (ctxp->cpu < LPFC_CHECK_CPU_CNT) + phba->cpucheck_ccmpl_io[id]++; + } +#endif + rsp->done(rsp); + } +} + +static int +lpfc_nvmet_xmt_ls_rsp(struct nvmet_fc_target_port *tgtport, + struct nvmefc_tgt_ls_req *rsp) +{ + struct lpfc_nvmet_rcv_ctx *ctxp = + container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.ls_req); + struct lpfc_hba *phba = ctxp->phba; + struct hbq_dmabuf *nvmebuf = + (struct hbq_dmabuf *)ctxp->rqb_buffer; + struct lpfc_iocbq *nvmewqeq; + struct lpfc_nvmet_tgtport *nvmep = tgtport->private; + struct lpfc_dmabuf dmabuf; + struct ulp_bde64 bpl; + int rc; + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, + "6023 %s: Entrypoint ctx %p %p\n", __func__, + ctxp, tgtport); + + nvmewqeq = lpfc_nvmet_prep_ls_wqe(phba, ctxp, rsp->rspdma, + rsp->rsplen); + if (nvmewqeq == NULL) { + atomic_inc(&nvmep->xmt_ls_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6150 LS Drop IO x%x: Prep\n", + ctxp->oxid); + lpfc_in_buf_free(phba, &nvmebuf->dbuf); + lpfc_nvmet_unsol_ls_issue_abort(phba, ctxp, + ctxp->sid, ctxp->oxid); + return -ENOMEM; + } + + /* Save numBdes for bpl2sgl */ + nvmewqeq->rsvd2 = 1; + nvmewqeq->hba_wqidx = 0; + nvmewqeq->context3 = &dmabuf; + dmabuf.virt = &bpl; + bpl.addrLow = nvmewqeq->wqe.xmit_sequence.bde.addrLow; + bpl.addrHigh = nvmewqeq->wqe.xmit_sequence.bde.addrHigh; + bpl.tus.f.bdeSize = rsp->rsplen; + bpl.tus.f.bdeFlags = 0; + bpl.tus.w = le32_to_cpu(bpl.tus.w); + + nvmewqeq->wqe_cmpl = lpfc_nvmet_xmt_ls_rsp_cmp; + nvmewqeq->iocb_cmpl = NULL; + nvmewqeq->context2 = ctxp; + + lpfc_nvmeio_data(phba, "NVMET LS RESP: xri x%x wqidx x%x len x%x\n", + ctxp->oxid, nvmewqeq->hba_wqidx, rsp->rsplen); + + rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, nvmewqeq); + if (rc == WQE_SUCCESS) { + /* + * Okay to repost buffer here, but wait till cmpl + * before freeing ctxp and iocbq. + */ + lpfc_in_buf_free(phba, &nvmebuf->dbuf); + ctxp->rqb_buffer = 0; + atomic_inc(&nvmep->xmt_ls_rsp); + return 0; + } + /* Give back resources */ + atomic_inc(&nvmep->xmt_ls_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6151 LS Drop IO x%x: Issue %d\n", + ctxp->oxid, rc); + + lpfc_nlp_put(nvmewqeq->context1); + + lpfc_in_buf_free(phba, &nvmebuf->dbuf); + lpfc_nvmet_unsol_ls_issue_abort(phba, ctxp, ctxp->sid, ctxp->oxid); + return -ENXIO; +} + +static int +lpfc_nvmet_xmt_fcp_op(struct nvmet_fc_target_port *tgtport, + struct nvmefc_tgt_fcp_req *rsp) +{ + struct lpfc_nvmet_tgtport *lpfc_nvmep = tgtport->private; + struct lpfc_nvmet_rcv_ctx *ctxp = + container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req); + struct lpfc_hba *phba = ctxp->phba; + struct lpfc_iocbq *nvmewqeq; + unsigned long iflags; + int rc, id; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) { + if (rsp->op == NVMET_FCOP_RSP) + ctxp->ts_nvme_status = ktime_get_ns(); + else + ctxp->ts_nvme_data = ktime_get_ns(); + } + if (phba->cpucheck_on & LPFC_CHECK_NVMET_IO) { + id = smp_processor_id(); + ctxp->cpu = id; + if (id < LPFC_CHECK_CPU_CNT) + phba->cpucheck_xmt_io[id]++; + if (rsp->hwqid != id) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6705 CPU Check OP: " + "cpu %d expect %d\n", + id, rsp->hwqid); + ctxp->cpu = rsp->hwqid; + } + } +#endif + + if (rsp->op == NVMET_FCOP_ABORT) { + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, + "6103 Abort op: oxri x%x %d cnt %d\n", + ctxp->oxid, ctxp->state, ctxp->entry_cnt); + + lpfc_nvmeio_data(phba, "NVMET FCP ABRT: " + "xri x%x state x%x cnt x%x\n", + ctxp->oxid, ctxp->state, ctxp->entry_cnt); + + atomic_inc(&lpfc_nvmep->xmt_fcp_abort); + ctxp->entry_cnt++; + ctxp->flag |= LPFC_NVMET_ABORT_OP; + if (ctxp->flag & LPFC_NVMET_IO_INP) + lpfc_nvmet_sol_fcp_issue_abort(phba, ctxp, ctxp->sid, + ctxp->oxid); + else + lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, ctxp->sid, + ctxp->oxid); + return 0; + } + + /* Sanity check */ + if (ctxp->state == LPFC_NVMET_STE_ABORT) { + atomic_inc(&lpfc_nvmep->xmt_fcp_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6102 Bad state IO x%x aborted\n", + ctxp->oxid); + goto aerr; + } + + nvmewqeq = lpfc_nvmet_prep_fcp_wqe(phba, ctxp); + if (nvmewqeq == NULL) { + atomic_inc(&lpfc_nvmep->xmt_fcp_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6152 FCP Drop IO x%x: Prep\n", + ctxp->oxid); + goto aerr; + } + + nvmewqeq->wqe_cmpl = lpfc_nvmet_xmt_fcp_op_cmp; + nvmewqeq->iocb_cmpl = NULL; + nvmewqeq->context2 = ctxp; + nvmewqeq->iocb_flag |= LPFC_IO_NVMET; + ctxp->wqeq->hba_wqidx = rsp->hwqid; + + lpfc_nvmeio_data(phba, "NVMET FCP CMND: xri x%x op x%x len x%x\n", + ctxp->oxid, rsp->op, rsp->rsplen); + + /* For now we take hbalock */ + spin_lock_irqsave(&phba->hbalock, iflags); + rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, nvmewqeq); + spin_unlock_irqrestore(&phba->hbalock, iflags); + if (rc == WQE_SUCCESS) { + ctxp->flag |= LPFC_NVMET_IO_INP; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (!phba->ktime_on) + return 0; + if (rsp->op == NVMET_FCOP_RSP) + ctxp->ts_status_wqput = ktime_get_ns(); + else + ctxp->ts_data_wqput = ktime_get_ns(); +#endif + return 0; + } + + /* Give back resources */ + atomic_inc(&lpfc_nvmep->xmt_fcp_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6153 FCP Drop IO x%x: Issue: %d\n", + ctxp->oxid, rc); + + ctxp->wqeq->hba_wqidx = 0; + nvmewqeq->context2 = NULL; + nvmewqeq->context3 = NULL; +aerr: + return -ENXIO; +} + +static void +lpfc_nvmet_targetport_delete(struct nvmet_fc_target_port *targetport) +{ + struct lpfc_nvmet_tgtport *tport = targetport->private; + + /* release any threads waiting for the unreg to complete */ + complete(&tport->tport_unreg_done); +} + +static struct nvmet_fc_target_template lpfc_tgttemplate = { + .targetport_delete = lpfc_nvmet_targetport_delete, + .xmt_ls_rsp = lpfc_nvmet_xmt_ls_rsp, + .fcp_op = lpfc_nvmet_xmt_fcp_op, + + .max_hw_queues = 1, + .max_sgl_segments = LPFC_NVMET_DEFAULT_SEGS, + .max_dif_sgl_segments = LPFC_NVMET_DEFAULT_SEGS, + .dma_boundary = 0xFFFFFFFF, + + /* optional features */ + .target_features = 0, + /* sizes of additional private data for data structures */ + .target_priv_sz = sizeof(struct lpfc_nvmet_tgtport), +}; + +int +lpfc_nvmet_create_targetport(struct lpfc_hba *phba) +{ + struct lpfc_vport *vport = phba->pport; + struct lpfc_nvmet_tgtport *tgtp; + struct nvmet_fc_port_info pinfo; + int error = 0; + + if (phba->targetport) + return 0; + + memset(&pinfo, 0, sizeof(struct nvmet_fc_port_info)); + pinfo.node_name = wwn_to_u64(vport->fc_nodename.u.wwn); + pinfo.port_name = wwn_to_u64(vport->fc_portname.u.wwn); + pinfo.port_id = vport->fc_myDID; + + lpfc_tgttemplate.max_hw_queues = phba->cfg_nvme_io_channel; + lpfc_tgttemplate.max_sgl_segments = phba->cfg_sg_seg_cnt; + lpfc_tgttemplate.target_features = NVMET_FCTGTFEAT_READDATA_RSP | + NVMET_FCTGTFEAT_NEEDS_CMD_CPUSCHED; + + error = nvmet_fc_register_targetport(&pinfo, &lpfc_tgttemplate, + &phba->pcidev->dev, + &phba->targetport); + if (error) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC, + "6025 Cannot register NVME targetport " + "x%x\n", error); + phba->targetport = NULL; + } else { + tgtp = (struct lpfc_nvmet_tgtport *) + phba->targetport->private; + tgtp->phba = phba; + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, + "6026 Registered NVME " + "targetport: %p, private %p " + "portnm %llx nodenm %llx\n", + phba->targetport, tgtp, + pinfo.port_name, pinfo.node_name); + + atomic_set(&tgtp->rcv_ls_req_in, 0); + atomic_set(&tgtp->rcv_ls_req_out, 0); + atomic_set(&tgtp->rcv_ls_req_drop, 0); + atomic_set(&tgtp->xmt_ls_abort, 0); + atomic_set(&tgtp->xmt_ls_rsp, 0); + atomic_set(&tgtp->xmt_ls_drop, 0); + atomic_set(&tgtp->xmt_ls_rsp_error, 0); + atomic_set(&tgtp->xmt_ls_rsp_cmpl, 0); + atomic_set(&tgtp->rcv_fcp_cmd_in, 0); + atomic_set(&tgtp->rcv_fcp_cmd_out, 0); + atomic_set(&tgtp->rcv_fcp_cmd_drop, 0); + atomic_set(&tgtp->xmt_fcp_abort, 0); + atomic_set(&tgtp->xmt_fcp_drop, 0); + atomic_set(&tgtp->xmt_fcp_read_rsp, 0); + atomic_set(&tgtp->xmt_fcp_read, 0); + atomic_set(&tgtp->xmt_fcp_write, 0); + atomic_set(&tgtp->xmt_fcp_rsp, 0); + atomic_set(&tgtp->xmt_fcp_rsp_cmpl, 0); + atomic_set(&tgtp->xmt_fcp_rsp_error, 0); + atomic_set(&tgtp->xmt_fcp_rsp_drop, 0); + atomic_set(&tgtp->xmt_abort_rsp, 0); + atomic_set(&tgtp->xmt_abort_rsp_error, 0); + atomic_set(&tgtp->xmt_abort_cmpl, 0); + } + return error; +} + +int +lpfc_nvmet_update_targetport(struct lpfc_hba *phba) +{ + struct lpfc_vport *vport = phba->pport; + + if (!phba->targetport) + return 0; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, + "6007 Update NVMET port %p did x%x\n", + phba->targetport, vport->fc_myDID); + + phba->targetport->port_id = vport->fc_myDID; + return 0; +} + +void +lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba) +{ + struct lpfc_nvmet_tgtport *tgtp; + + if (phba->nvmet_support == 0) + return; + if (phba->targetport) { + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + init_completion(&tgtp->tport_unreg_done); + nvmet_fc_unregister_targetport(phba->targetport); + wait_for_completion_timeout(&tgtp->tport_unreg_done, 5); + } + phba->targetport = NULL; +} + +/** + * lpfc_nvmet_unsol_ls_buffer - Process an unsolicited event data buffer + * @phba: pointer to lpfc hba data structure. + * @pring: pointer to a SLI ring. + * @nvmebuf: pointer to lpfc nvme command HBQ data structure. + * + * This routine is used for processing the WQE associated with a unsolicited + * event. It first determines whether there is an existing ndlp that matches + * the DID from the unsolicited WQE. If not, it will create a new one with + * the DID from the unsolicited WQE. The ELS command from the unsolicited + * WQE is then used to invoke the proper routine and to set up proper state + * of the discovery state machine. + **/ +static void +lpfc_nvmet_unsol_ls_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct hbq_dmabuf *nvmebuf) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct fc_frame_header *fc_hdr; + struct lpfc_nvmet_rcv_ctx *ctxp; + uint32_t *payload; + uint32_t size, oxid, sid, rc; + + if (!nvmebuf || !phba->targetport) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6154 LS Drop IO\n"); + oxid = 0; + size = 0; + sid = 0; + goto dropit; + } + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + payload = (uint32_t *)(nvmebuf->dbuf.virt); + fc_hdr = (struct fc_frame_header *)(nvmebuf->hbuf.virt); + size = bf_get(lpfc_rcqe_length, &nvmebuf->cq_event.cqe.rcqe_cmpl); + oxid = be16_to_cpu(fc_hdr->fh_ox_id); + sid = sli4_sid_from_fc_hdr(fc_hdr); + + ctxp = kzalloc(sizeof(struct lpfc_nvmet_rcv_ctx), GFP_ATOMIC); + if (ctxp == NULL) { + atomic_inc(&tgtp->rcv_ls_req_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6155 LS Drop IO x%x: Alloc\n", + oxid); +dropit: + lpfc_nvmeio_data(phba, "NVMET LS DROP: " + "xri x%x sz %d from %06x\n", + oxid, size, sid); + if (nvmebuf) + lpfc_in_buf_free(phba, &nvmebuf->dbuf); + return; + } + ctxp->phba = phba; + ctxp->size = size; + ctxp->oxid = oxid; + ctxp->sid = sid; + ctxp->wqeq = NULL; + ctxp->state = LPFC_NVMET_STE_RCV; + ctxp->rqb_buffer = (void *)nvmebuf; + + lpfc_nvmeio_data(phba, "NVMET LS RCV: xri x%x sz %d from %06x\n", + oxid, size, sid); + /* + * The calling sequence should be: + * nvmet_fc_rcv_ls_req -> lpfc_nvmet_xmt_ls_rsp/cmp ->_req->done + * lpfc_nvmet_xmt_ls_rsp_cmp should free the allocated ctxp. + */ + atomic_inc(&tgtp->rcv_ls_req_in); + rc = nvmet_fc_rcv_ls_req(phba->targetport, &ctxp->ctx.ls_req, + payload, size); + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, + "6037 %s: ctx %p sz %d rc %d: %08x %08x %08x " + "%08x %08x %08x\n", __func__, ctxp, size, rc, + *payload, *(payload+1), *(payload+2), + *(payload+3), *(payload+4), *(payload+5)); + + if (rc == 0) { + atomic_inc(&tgtp->rcv_ls_req_out); + return; + } + + lpfc_nvmeio_data(phba, "NVMET LS DROP: xri x%x sz %d from %06x\n", + oxid, size, sid); + + atomic_inc(&tgtp->rcv_ls_req_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6156 LS Drop IO x%x: nvmet_fc_rcv_ls_req %d\n", + ctxp->oxid, rc); + + /* We assume a rcv'ed cmd ALWAYs fits into 1 buffer */ + if (nvmebuf) + lpfc_in_buf_free(phba, &nvmebuf->dbuf); + + atomic_inc(&tgtp->xmt_ls_abort); + lpfc_nvmet_unsol_ls_issue_abort(phba, ctxp, sid, oxid); +} + +/** + * lpfc_nvmet_unsol_fcp_buffer - Process an unsolicited event data buffer + * @phba: pointer to lpfc hba data structure. + * @pring: pointer to a SLI ring. + * @nvmebuf: pointer to lpfc nvme command HBQ data structure. + * + * This routine is used for processing the WQE associated with a unsolicited + * event. It first determines whether there is an existing ndlp that matches + * the DID from the unsolicited WQE. If not, it will create a new one with + * the DID from the unsolicited WQE. The ELS command from the unsolicited + * WQE is then used to invoke the proper routine and to set up proper state + * of the discovery state machine. + **/ +static void +lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, + struct lpfc_sli_ring *pring, + struct rqb_dmabuf *nvmebuf, + uint64_t isr_timestamp) +{ + struct lpfc_nvmet_rcv_ctx *ctxp; + struct lpfc_nvmet_tgtport *tgtp; + struct fc_frame_header *fc_hdr; + uint32_t *payload; + uint32_t size, oxid, sid, rc; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint32_t id; +#endif + + if (!nvmebuf || !phba->targetport) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6157 FCP Drop IO\n"); + oxid = 0; + size = 0; + sid = 0; + goto dropit; + } + + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + payload = (uint32_t *)(nvmebuf->dbuf.virt); + fc_hdr = (struct fc_frame_header *)(nvmebuf->hbuf.virt); + size = nvmebuf->bytes_recv; + oxid = be16_to_cpu(fc_hdr->fh_ox_id); + sid = sli4_sid_from_fc_hdr(fc_hdr); + + ctxp = (struct lpfc_nvmet_rcv_ctx *)nvmebuf->context; + if (ctxp == NULL) { + atomic_inc(&tgtp->rcv_fcp_cmd_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6158 FCP Drop IO x%x: Alloc\n", + oxid); + lpfc_nvmet_rq_post(phba, NULL, &nvmebuf->hbuf); + /* Cannot send ABTS without context */ + return; + } + memset(ctxp, 0, sizeof(ctxp->ctx)); + ctxp->wqeq = NULL; + ctxp->txrdy = NULL; + ctxp->offset = 0; + ctxp->phba = phba; + ctxp->size = size; + ctxp->oxid = oxid; + ctxp->sid = sid; + ctxp->state = LPFC_NVMET_STE_RCV; + ctxp->rqb_buffer = nvmebuf; + ctxp->entry_cnt = 1; + ctxp->flag = 0; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) { + ctxp->ts_isr_cmd = isr_timestamp; + ctxp->ts_cmd_nvme = ktime_get_ns(); + ctxp->ts_nvme_data = 0; + ctxp->ts_data_wqput = 0; + ctxp->ts_isr_data = 0; + ctxp->ts_data_nvme = 0; + ctxp->ts_nvme_status = 0; + ctxp->ts_status_wqput = 0; + ctxp->ts_isr_status = 0; + ctxp->ts_status_nvme = 0; + } + + if (phba->cpucheck_on & LPFC_CHECK_NVMET_RCV) { + id = smp_processor_id(); + if (id < LPFC_CHECK_CPU_CNT) + phba->cpucheck_rcv_io[id]++; + } +#endif + + lpfc_nvmeio_data(phba, "NVMET FCP RCV: xri x%x sz %d from %06x\n", + oxid, size, sid); + + atomic_inc(&tgtp->rcv_fcp_cmd_in); + /* + * The calling sequence should be: + * nvmet_fc_rcv_fcp_req -> lpfc_nvmet_xmt_fcp_op/cmp -> req->done + * lpfc_nvmet_xmt_fcp_op_cmp should free the allocated ctxp. + */ + rc = nvmet_fc_rcv_fcp_req(phba->targetport, &ctxp->ctx.fcp_req, + payload, size); + + /* Process FCP command */ + if (rc == 0) { + atomic_inc(&tgtp->rcv_fcp_cmd_out); + return; + } + + atomic_inc(&tgtp->rcv_fcp_cmd_drop); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6159 FCP Drop IO x%x: nvmet_fc_rcv_fcp_req x%x\n", + ctxp->oxid, rc); +dropit: + lpfc_nvmeio_data(phba, "NVMET FCP DROP: xri x%x sz %d from %06x\n", + oxid, size, sid); + if (oxid) { + lpfc_nvmet_unsol_fcp_issue_abort(phba, ctxp, sid, oxid); + return; + } + + if (nvmebuf) { + nvmebuf->iocbq->hba_wqidx = 0; + /* We assume a rcv'ed cmd ALWAYs fits into 1 buffer */ + lpfc_nvmet_rq_post(phba, NULL, &nvmebuf->hbuf); + } +} + +/** + * lpfc_nvmet_unsol_ls_event - Process an unsolicited event from an nvme nport + * @phba: pointer to lpfc hba data structure. + * @pring: pointer to a SLI ring. + * @nvmebuf: pointer to received nvme data structure. + * + * This routine is used to process an unsolicited event received from a SLI + * (Service Level Interface) ring. The actual processing of the data buffer + * associated with the unsolicited event is done by invoking the routine + * lpfc_nvmet_unsol_ls_buffer() after properly set up the buffer from the + * SLI RQ on which the unsolicited event was received. + **/ +void +lpfc_nvmet_unsol_ls_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *piocb) +{ + struct lpfc_dmabuf *d_buf; + struct hbq_dmabuf *nvmebuf; + + d_buf = piocb->context2; + nvmebuf = container_of(d_buf, struct hbq_dmabuf, dbuf); + + if (phba->nvmet_support == 0) { + lpfc_in_buf_free(phba, &nvmebuf->dbuf); + return; + } + lpfc_nvmet_unsol_ls_buffer(phba, pring, nvmebuf); +} + +/** + * lpfc_nvmet_unsol_fcp_event - Process an unsolicited event from an nvme nport + * @phba: pointer to lpfc hba data structure. + * @pring: pointer to a SLI ring. + * @nvmebuf: pointer to received nvme data structure. + * + * This routine is used to process an unsolicited event received from a SLI + * (Service Level Interface) ring. The actual processing of the data buffer + * associated with the unsolicited event is done by invoking the routine + * lpfc_nvmet_unsol_fcp_buffer() after properly set up the buffer from the + * SLI RQ on which the unsolicited event was received. + **/ +void +lpfc_nvmet_unsol_fcp_event(struct lpfc_hba *phba, + struct lpfc_sli_ring *pring, + struct rqb_dmabuf *nvmebuf, + uint64_t isr_timestamp) +{ + if (phba->nvmet_support == 0) { + lpfc_nvmet_rq_post(phba, NULL, &nvmebuf->hbuf); + return; + } + lpfc_nvmet_unsol_fcp_buffer(phba, pring, nvmebuf, + isr_timestamp); +} + +/** + * lpfc_nvmet_prep_ls_wqe - Allocate and prepare a lpfc wqe data structure + * @phba: pointer to a host N_Port data structure. + * @ctxp: Context info for NVME LS Request + * @rspbuf: DMA buffer of NVME command. + * @rspsize: size of the NVME command. + * + * This routine is used for allocating a lpfc-WQE data structure from + * the driver lpfc-WQE free-list and prepare the WQE with the parameters + * passed into the routine for discovery state machine to issue an Extended + * Link Service (NVME) commands. It is a generic lpfc-WQE allocation + * and preparation routine that is used by all the discovery state machine + * routines and the NVME command-specific fields will be later set up by + * the individual discovery machine routines after calling this routine + * allocating and preparing a generic WQE data structure. It fills in the + * Buffer Descriptor Entries (BDEs), allocates buffers for both command + * payload and response payload (if expected). The reference count on the + * ndlp is incremented by 1 and the reference to the ndlp is put into + * context1 of the WQE data structure for this WQE to hold the ndlp + * reference for the command's callback function to access later. + * + * Return code + * Pointer to the newly allocated/prepared nvme wqe data structure + * NULL - when nvme wqe data structure allocation/preparation failed + **/ +static struct lpfc_iocbq * +lpfc_nvmet_prep_ls_wqe(struct lpfc_hba *phba, + struct lpfc_nvmet_rcv_ctx *ctxp, + dma_addr_t rspbuf, uint16_t rspsize) +{ + struct lpfc_nodelist *ndlp; + struct lpfc_iocbq *nvmewqe; + union lpfc_wqe *wqe; + + if (!lpfc_is_link_up(phba)) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC, + "6104 lpfc_nvmet_prep_ls_wqe: link err: " + "NPORT x%x oxid:x%x\n", + ctxp->sid, ctxp->oxid); + return NULL; + } + + /* Allocate buffer for command wqe */ + nvmewqe = lpfc_sli_get_iocbq(phba); + if (nvmewqe == NULL) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC, + "6105 lpfc_nvmet_prep_ls_wqe: No WQE: " + "NPORT x%x oxid:x%x\n", + ctxp->sid, ctxp->oxid); + return NULL; + } + + ndlp = lpfc_findnode_did(phba->pport, ctxp->sid); + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) || + ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) && + (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_DISC, + "6106 lpfc_nvmet_prep_ls_wqe: No ndlp: " + "NPORT x%x oxid:x%x\n", + ctxp->sid, ctxp->oxid); + goto nvme_wqe_free_wqeq_exit; + } + ctxp->wqeq = nvmewqe; + + /* prevent preparing wqe with NULL ndlp reference */ + nvmewqe->context1 = lpfc_nlp_get(ndlp); + if (nvmewqe->context1 == NULL) + goto nvme_wqe_free_wqeq_exit; + nvmewqe->context2 = ctxp; + + wqe = &nvmewqe->wqe; + memset(wqe, 0, sizeof(union lpfc_wqe)); + + /* Words 0 - 2 */ + wqe->xmit_sequence.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64; + wqe->xmit_sequence.bde.tus.f.bdeSize = rspsize; + wqe->xmit_sequence.bde.addrLow = le32_to_cpu(putPaddrLow(rspbuf)); + wqe->xmit_sequence.bde.addrHigh = le32_to_cpu(putPaddrHigh(rspbuf)); + + /* Word 3 */ + + /* Word 4 */ + + /* Word 5 */ + bf_set(wqe_dfctl, &wqe->xmit_sequence.wge_ctl, 0); + bf_set(wqe_ls, &wqe->xmit_sequence.wge_ctl, 1); + bf_set(wqe_la, &wqe->xmit_sequence.wge_ctl, 0); + bf_set(wqe_rctl, &wqe->xmit_sequence.wge_ctl, FC_RCTL_DD_SOL_CTL); + bf_set(wqe_type, &wqe->xmit_sequence.wge_ctl, FC_TYPE_NVME); + + /* Word 6 */ + bf_set(wqe_ctxt_tag, &wqe->xmit_sequence.wqe_com, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); + bf_set(wqe_xri_tag, &wqe->xmit_sequence.wqe_com, nvmewqe->sli4_xritag); + + /* Word 7 */ + bf_set(wqe_cmnd, &wqe->xmit_sequence.wqe_com, + CMD_XMIT_SEQUENCE64_WQE); + bf_set(wqe_ct, &wqe->xmit_sequence.wqe_com, SLI4_CT_RPI); + bf_set(wqe_class, &wqe->xmit_sequence.wqe_com, CLASS3); + bf_set(wqe_pu, &wqe->xmit_sequence.wqe_com, 0); + + /* Word 8 */ + wqe->xmit_sequence.wqe_com.abort_tag = nvmewqe->iotag; + + /* Word 9 */ + bf_set(wqe_reqtag, &wqe->xmit_sequence.wqe_com, nvmewqe->iotag); + /* Needs to be set by caller */ + bf_set(wqe_rcvoxid, &wqe->xmit_sequence.wqe_com, ctxp->oxid); + + /* Word 10 */ + bf_set(wqe_dbde, &wqe->xmit_sequence.wqe_com, 1); + bf_set(wqe_iod, &wqe->xmit_sequence.wqe_com, LPFC_WQE_IOD_WRITE); + bf_set(wqe_lenloc, &wqe->xmit_sequence.wqe_com, + LPFC_WQE_LENLOC_WORD12); + bf_set(wqe_ebde_cnt, &wqe->xmit_sequence.wqe_com, 0); + + /* Word 11 */ + bf_set(wqe_cqid, &wqe->xmit_sequence.wqe_com, + LPFC_WQE_CQ_ID_DEFAULT); + bf_set(wqe_cmd_type, &wqe->xmit_sequence.wqe_com, + OTHER_COMMAND); + + /* Word 12 */ + wqe->xmit_sequence.xmit_len = rspsize; + + nvmewqe->retry = 1; + nvmewqe->vport = phba->pport; + nvmewqe->drvrTimeout = (phba->fc_ratov * 3) + LPFC_DRVR_TIMEOUT; + nvmewqe->iocb_flag |= LPFC_IO_NVME_LS; + + /* Xmit NVME response to remote NPORT <did> */ + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_DISC, + "6039 Xmit NVME LS response to remote " + "NPORT x%x iotag:x%x oxid:x%x size:x%x\n", + ndlp->nlp_DID, nvmewqe->iotag, ctxp->oxid, + rspsize); + return nvmewqe; + +nvme_wqe_free_wqeq_exit: + nvmewqe->context2 = NULL; + nvmewqe->context3 = NULL; + lpfc_sli_release_iocbq(phba, nvmewqe); + return NULL; +} + + +static struct lpfc_iocbq * +lpfc_nvmet_prep_fcp_wqe(struct lpfc_hba *phba, + struct lpfc_nvmet_rcv_ctx *ctxp) +{ + struct nvmefc_tgt_fcp_req *rsp = &ctxp->ctx.fcp_req; + struct lpfc_nvmet_tgtport *tgtp; + struct sli4_sge *sgl; + struct lpfc_nodelist *ndlp; + struct lpfc_iocbq *nvmewqe; + struct scatterlist *sgel; + union lpfc_wqe128 *wqe; + uint32_t *txrdy; + dma_addr_t physaddr; + int i, cnt; + int xc = 1; + + if (!lpfc_is_link_up(phba)) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6107 lpfc_nvmet_prep_fcp_wqe: link err:" + "NPORT x%x oxid:x%x\n", ctxp->sid, + ctxp->oxid); + return NULL; + } + + ndlp = lpfc_findnode_did(phba->pport, ctxp->sid); + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) || + ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) && + (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6108 lpfc_nvmet_prep_fcp_wqe: no ndlp: " + "NPORT x%x oxid:x%x\n", + ctxp->sid, ctxp->oxid); + return NULL; + } + + if (rsp->sg_cnt > phba->cfg_sg_seg_cnt) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6109 lpfc_nvmet_prep_fcp_wqe: seg cnt err: " + "NPORT x%x oxid:x%x\n", + ctxp->sid, ctxp->oxid); + return NULL; + } + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + nvmewqe = ctxp->wqeq; + if (nvmewqe == NULL) { + /* Allocate buffer for command wqe */ + nvmewqe = ctxp->rqb_buffer->iocbq; + if (nvmewqe == NULL) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6110 lpfc_nvmet_prep_fcp_wqe: No " + "WQE: NPORT x%x oxid:x%x\n", + ctxp->sid, ctxp->oxid); + return NULL; + } + ctxp->wqeq = nvmewqe; + xc = 0; /* create new XRI */ + nvmewqe->sli4_lxritag = NO_XRI; + nvmewqe->sli4_xritag = NO_XRI; + } + + /* Sanity check */ + if (((ctxp->state == LPFC_NVMET_STE_RCV) && + (ctxp->entry_cnt == 1)) || + ((ctxp->state == LPFC_NVMET_STE_DATA) && + (ctxp->entry_cnt > 1))) { + wqe = (union lpfc_wqe128 *)&nvmewqe->wqe; + } else { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6111 Wrong state %s: %d cnt %d\n", + __func__, ctxp->state, ctxp->entry_cnt); + return NULL; + } + + sgl = (struct sli4_sge *)ctxp->rqb_buffer->sglq->sgl; + switch (rsp->op) { + case NVMET_FCOP_READDATA: + case NVMET_FCOP_READDATA_RSP: + /* Words 0 - 2 : The first sg segment */ + sgel = &rsp->sg[0]; + physaddr = sg_dma_address(sgel); + wqe->fcp_tsend.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64; + wqe->fcp_tsend.bde.tus.f.bdeSize = sg_dma_len(sgel); + wqe->fcp_tsend.bde.addrLow = cpu_to_le32(putPaddrLow(physaddr)); + wqe->fcp_tsend.bde.addrHigh = + cpu_to_le32(putPaddrHigh(physaddr)); + + /* Word 3 */ + wqe->fcp_tsend.payload_offset_len = 0; + + /* Word 4 */ + wqe->fcp_tsend.relative_offset = ctxp->offset; + + /* Word 5 */ + + /* Word 6 */ + bf_set(wqe_ctxt_tag, &wqe->fcp_tsend.wqe_com, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); + bf_set(wqe_xri_tag, &wqe->fcp_tsend.wqe_com, + nvmewqe->sli4_xritag); + + /* Word 7 */ + bf_set(wqe_cmnd, &wqe->fcp_tsend.wqe_com, CMD_FCP_TSEND64_WQE); + + /* Word 8 */ + wqe->fcp_tsend.wqe_com.abort_tag = nvmewqe->iotag; + + /* Word 9 */ + bf_set(wqe_reqtag, &wqe->fcp_tsend.wqe_com, nvmewqe->iotag); + bf_set(wqe_rcvoxid, &wqe->fcp_tsend.wqe_com, ctxp->oxid); + + /* Word 10 */ + bf_set(wqe_nvme, &wqe->fcp_tsend.wqe_com, 1); + bf_set(wqe_dbde, &wqe->fcp_tsend.wqe_com, 1); + bf_set(wqe_iod, &wqe->fcp_tsend.wqe_com, LPFC_WQE_IOD_WRITE); + bf_set(wqe_lenloc, &wqe->fcp_tsend.wqe_com, + LPFC_WQE_LENLOC_WORD12); + bf_set(wqe_ebde_cnt, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_xc, &wqe->fcp_tsend.wqe_com, xc); + bf_set(wqe_nvme, &wqe->fcp_tsend.wqe_com, 1); + if (phba->cfg_nvme_oas) + bf_set(wqe_oas, &wqe->fcp_tsend.wqe_com, 1); + + /* Word 11 */ + bf_set(wqe_cqid, &wqe->fcp_tsend.wqe_com, + LPFC_WQE_CQ_ID_DEFAULT); + bf_set(wqe_cmd_type, &wqe->fcp_tsend.wqe_com, + FCP_COMMAND_TSEND); + + /* Word 12 */ + wqe->fcp_tsend.fcp_data_len = rsp->transfer_length; + + /* Setup 2 SKIP SGEs */ + sgl->addr_hi = 0; + sgl->addr_lo = 0; + sgl->word2 = 0; + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = 0; + sgl++; + sgl->addr_hi = 0; + sgl->addr_lo = 0; + sgl->word2 = 0; + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = 0; + sgl++; + if (rsp->op == NVMET_FCOP_READDATA_RSP) { + atomic_inc(&tgtp->xmt_fcp_read_rsp); + bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 1); + if ((ndlp->nlp_flag & NLP_SUPPRESS_RSP) && + (rsp->rsplen == 12)) { + bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 1); + bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0); + } else { + bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 1); + bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 1); + bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, + ((rsp->rsplen >> 2) - 1)); + memcpy(&wqe->words[16], rsp->rspaddr, + rsp->rsplen); + } + } else { + atomic_inc(&tgtp->xmt_fcp_read); + + bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_wqes, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_irsp, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_ar, &wqe->fcp_tsend.wqe_com, 0); + bf_set(wqe_irsplen, &wqe->fcp_tsend.wqe_com, 0); + } + ctxp->state = LPFC_NVMET_STE_DATA; + break; + + case NVMET_FCOP_WRITEDATA: + /* Words 0 - 2 : The first sg segment */ + txrdy = pci_pool_alloc(phba->txrdy_payload_pool, + GFP_KERNEL, &physaddr); + if (!txrdy) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, + "6041 Bad txrdy buffer: oxid x%x\n", + ctxp->oxid); + return NULL; + } + ctxp->txrdy = txrdy; + ctxp->txrdy_phys = physaddr; + wqe->fcp_treceive.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64; + wqe->fcp_treceive.bde.tus.f.bdeSize = TXRDY_PAYLOAD_LEN; + wqe->fcp_treceive.bde.addrLow = + cpu_to_le32(putPaddrLow(physaddr)); + wqe->fcp_treceive.bde.addrHigh = + cpu_to_le32(putPaddrHigh(physaddr)); + + /* Word 3 */ + wqe->fcp_treceive.payload_offset_len = TXRDY_PAYLOAD_LEN; + + /* Word 4 */ + wqe->fcp_treceive.relative_offset = ctxp->offset; + + /* Word 5 */ + + /* Word 6 */ + bf_set(wqe_ctxt_tag, &wqe->fcp_treceive.wqe_com, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); + bf_set(wqe_xri_tag, &wqe->fcp_treceive.wqe_com, + nvmewqe->sli4_xritag); + + /* Word 7 */ + bf_set(wqe_ar, &wqe->fcp_treceive.wqe_com, 0); + bf_set(wqe_cmnd, &wqe->fcp_treceive.wqe_com, + CMD_FCP_TRECEIVE64_WQE); + + /* Word 8 */ + wqe->fcp_treceive.wqe_com.abort_tag = nvmewqe->iotag; + + /* Word 9 */ + bf_set(wqe_reqtag, &wqe->fcp_treceive.wqe_com, nvmewqe->iotag); + bf_set(wqe_rcvoxid, &wqe->fcp_treceive.wqe_com, ctxp->oxid); + + /* Word 10 */ + bf_set(wqe_nvme, &wqe->fcp_treceive.wqe_com, 1); + bf_set(wqe_dbde, &wqe->fcp_treceive.wqe_com, 1); + bf_set(wqe_iod, &wqe->fcp_treceive.wqe_com, LPFC_WQE_IOD_READ); + bf_set(wqe_lenloc, &wqe->fcp_treceive.wqe_com, + LPFC_WQE_LENLOC_WORD12); + bf_set(wqe_xc, &wqe->fcp_treceive.wqe_com, xc); + bf_set(wqe_wqes, &wqe->fcp_treceive.wqe_com, 0); + bf_set(wqe_irsp, &wqe->fcp_treceive.wqe_com, 0); + bf_set(wqe_irsplen, &wqe->fcp_treceive.wqe_com, 0); + bf_set(wqe_nvme, &wqe->fcp_treceive.wqe_com, 1); + if (phba->cfg_nvme_oas) + bf_set(wqe_oas, &wqe->fcp_treceive.wqe_com, 1); + + /* Word 11 */ + bf_set(wqe_cqid, &wqe->fcp_treceive.wqe_com, + LPFC_WQE_CQ_ID_DEFAULT); + bf_set(wqe_cmd_type, &wqe->fcp_treceive.wqe_com, + FCP_COMMAND_TRECEIVE); + bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0); + + /* Word 12 */ + wqe->fcp_tsend.fcp_data_len = rsp->transfer_length; + + /* Setup 1 TXRDY and 1 SKIP SGE */ + txrdy[0] = 0; + txrdy[1] = cpu_to_be32(rsp->transfer_length); + txrdy[2] = 0; + + sgl->addr_hi = putPaddrHigh(physaddr); + sgl->addr_lo = putPaddrLow(physaddr); + sgl->word2 = 0; + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = cpu_to_le32(TXRDY_PAYLOAD_LEN); + sgl++; + sgl->addr_hi = 0; + sgl->addr_lo = 0; + sgl->word2 = 0; + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_SKIP); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = 0; + sgl++; + ctxp->state = LPFC_NVMET_STE_DATA; + atomic_inc(&tgtp->xmt_fcp_write); + break; + + case NVMET_FCOP_RSP: + /* Words 0 - 2 */ + sgel = &rsp->sg[0]; + physaddr = rsp->rspdma; + wqe->fcp_trsp.bde.tus.f.bdeFlags = BUFF_TYPE_BDE_64; + wqe->fcp_trsp.bde.tus.f.bdeSize = rsp->rsplen; + wqe->fcp_trsp.bde.addrLow = + cpu_to_le32(putPaddrLow(physaddr)); + wqe->fcp_trsp.bde.addrHigh = + cpu_to_le32(putPaddrHigh(physaddr)); + + /* Word 3 */ + wqe->fcp_trsp.response_len = rsp->rsplen; + + /* Word 4 */ + wqe->fcp_trsp.rsvd_4_5[0] = 0; + + + /* Word 5 */ + + /* Word 6 */ + bf_set(wqe_ctxt_tag, &wqe->fcp_trsp.wqe_com, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); + bf_set(wqe_xri_tag, &wqe->fcp_trsp.wqe_com, + nvmewqe->sli4_xritag); + + /* Word 7 */ + bf_set(wqe_ag, &wqe->fcp_trsp.wqe_com, 1); + bf_set(wqe_cmnd, &wqe->fcp_trsp.wqe_com, CMD_FCP_TRSP64_WQE); + + /* Word 8 */ + wqe->fcp_trsp.wqe_com.abort_tag = nvmewqe->iotag; + + /* Word 9 */ + bf_set(wqe_reqtag, &wqe->fcp_trsp.wqe_com, nvmewqe->iotag); + bf_set(wqe_rcvoxid, &wqe->fcp_trsp.wqe_com, ctxp->oxid); + + /* Word 10 */ + bf_set(wqe_nvme, &wqe->fcp_trsp.wqe_com, 1); + bf_set(wqe_dbde, &wqe->fcp_trsp.wqe_com, 0); + bf_set(wqe_iod, &wqe->fcp_trsp.wqe_com, LPFC_WQE_IOD_WRITE); + bf_set(wqe_lenloc, &wqe->fcp_trsp.wqe_com, + LPFC_WQE_LENLOC_WORD3); + bf_set(wqe_xc, &wqe->fcp_trsp.wqe_com, xc); + bf_set(wqe_nvme, &wqe->fcp_trsp.wqe_com, 1); + if (phba->cfg_nvme_oas) + bf_set(wqe_oas, &wqe->fcp_trsp.wqe_com, 1); + + /* Word 11 */ + bf_set(wqe_cqid, &wqe->fcp_trsp.wqe_com, + LPFC_WQE_CQ_ID_DEFAULT); + bf_set(wqe_cmd_type, &wqe->fcp_trsp.wqe_com, + FCP_COMMAND_TRSP); + bf_set(wqe_sup, &wqe->fcp_tsend.wqe_com, 0); + ctxp->state = LPFC_NVMET_STE_RSP; + + if (rsp->rsplen == LPFC_NVMET_SUCCESS_LEN) { + /* Good response - all zero's on wire */ + bf_set(wqe_wqes, &wqe->fcp_trsp.wqe_com, 0); + bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 0); + bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com, 0); + } else { + bf_set(wqe_wqes, &wqe->fcp_trsp.wqe_com, 1); + bf_set(wqe_irsp, &wqe->fcp_trsp.wqe_com, 1); + bf_set(wqe_irsplen, &wqe->fcp_trsp.wqe_com, + ((rsp->rsplen >> 2) - 1)); + memcpy(&wqe->words[16], rsp->rspaddr, rsp->rsplen); + } + + /* Use rspbuf, NOT sg list */ + rsp->sg_cnt = 0; + sgl->word2 = 0; + atomic_inc(&tgtp->xmt_fcp_rsp); + break; + + default: + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_IOERR, + "6064 Unknown Rsp Op %d\n", + rsp->op); + return NULL; + } + + nvmewqe->retry = 1; + nvmewqe->vport = phba->pport; + nvmewqe->drvrTimeout = (phba->fc_ratov * 3) + LPFC_DRVR_TIMEOUT; + nvmewqe->context1 = ndlp; + + for (i = 0; i < rsp->sg_cnt; i++) { + sgel = &rsp->sg[i]; + physaddr = sg_dma_address(sgel); + cnt = sg_dma_len(sgel); + sgl->addr_hi = putPaddrHigh(physaddr); + sgl->addr_lo = putPaddrLow(physaddr); + sgl->word2 = 0; + bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA); + bf_set(lpfc_sli4_sge_offset, sgl, ctxp->offset); + if ((i+1) == rsp->sg_cnt) + bf_set(lpfc_sli4_sge_last, sgl, 1); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = cpu_to_le32(cnt); + sgl++; + ctxp->offset += cnt; + } + return nvmewqe; +} + +/** + * lpfc_nvmet_sol_fcp_abort_cmp - Completion handler for ABTS + * @phba: Pointer to HBA context object. + * @cmdwqe: Pointer to driver command WQE object. + * @wcqe: Pointer to driver response CQE object. + * + * The function is called from SLI ring event handler with no + * lock held. This function is the completion handler for NVME ABTS for FCP cmds + * The function frees memory resources used for the NVME commands. + **/ +static void +lpfc_nvmet_sol_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, + struct lpfc_wcqe_complete *wcqe) +{ + struct lpfc_nvmet_rcv_ctx *ctxp; + struct lpfc_nvmet_tgtport *tgtp; + uint32_t status, result; + + ctxp = cmdwqe->context2; + status = bf_get(lpfc_wcqe_c_status, wcqe); + result = wcqe->parameter; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + atomic_inc(&tgtp->xmt_abort_cmpl); + + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS, + "6165 Abort cmpl: xri x%x WCQE: %08x %08x %08x %08x\n", + ctxp->oxid, wcqe->word0, wcqe->total_data_placed, + result, wcqe->word3); + + ctxp->state = LPFC_NVMET_STE_DONE; + lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf); + + cmdwqe->context2 = NULL; + cmdwqe->context3 = NULL; + lpfc_sli_release_iocbq(phba, cmdwqe); +} + +/** + * lpfc_nvmet_xmt_fcp_abort_cmp - Completion handler for ABTS + * @phba: Pointer to HBA context object. + * @cmdwqe: Pointer to driver command WQE object. + * @wcqe: Pointer to driver response CQE object. + * + * The function is called from SLI ring event handler with no + * lock held. This function is the completion handler for NVME ABTS for FCP cmds + * The function frees memory resources used for the NVME commands. + **/ +static void +lpfc_nvmet_xmt_fcp_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, + struct lpfc_wcqe_complete *wcqe) +{ + struct lpfc_nvmet_rcv_ctx *ctxp; + struct lpfc_nvmet_tgtport *tgtp; + uint32_t status, result; + + ctxp = cmdwqe->context2; + status = bf_get(lpfc_wcqe_c_status, wcqe); + result = wcqe->parameter; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + atomic_inc(&tgtp->xmt_abort_cmpl); + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, + "6070 Abort cmpl: ctx %p WCQE: %08x %08x %08x %08x\n", + ctxp, wcqe->word0, wcqe->total_data_placed, + result, wcqe->word3); + + if (ctxp) { + /* Sanity check */ + if (ctxp->state != LPFC_NVMET_STE_ABORT) { + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS, + "6112 ABORT Wrong state:%d oxid x%x\n", + ctxp->state, ctxp->oxid); + } + ctxp->state = LPFC_NVMET_STE_DONE; + lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf); + cmdwqe->context2 = NULL; + cmdwqe->context3 = NULL; + } +} + +/** + * lpfc_nvmet_xmt_ls_abort_cmp - Completion handler for ABTS + * @phba: Pointer to HBA context object. + * @cmdwqe: Pointer to driver command WQE object. + * @wcqe: Pointer to driver response CQE object. + * + * The function is called from SLI ring event handler with no + * lock held. This function is the completion handler for NVME ABTS for LS cmds + * The function frees memory resources used for the NVME commands. + **/ +static void +lpfc_nvmet_xmt_ls_abort_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, + struct lpfc_wcqe_complete *wcqe) +{ + struct lpfc_nvmet_rcv_ctx *ctxp; + struct lpfc_nvmet_tgtport *tgtp; + uint32_t status, result; + + ctxp = cmdwqe->context2; + status = bf_get(lpfc_wcqe_c_status, wcqe); + result = wcqe->parameter; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + atomic_inc(&tgtp->xmt_abort_cmpl); + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, + "6083 Abort cmpl: ctx %p WCQE: %08x %08x %08x %08x\n", + ctxp, wcqe->word0, wcqe->total_data_placed, + result, wcqe->word3); + + if (ctxp) { + cmdwqe->context2 = NULL; + cmdwqe->context3 = NULL; + lpfc_sli_release_iocbq(phba, cmdwqe); + kfree(ctxp); + } else + lpfc_sli_release_iocbq(phba, cmdwqe); +} + +static int +lpfc_nvmet_unsol_issue_abort(struct lpfc_hba *phba, + struct lpfc_nvmet_rcv_ctx *ctxp, + uint32_t sid, uint16_t xri) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct lpfc_iocbq *abts_wqeq; + union lpfc_wqe *wqe_abts; + struct lpfc_nodelist *ndlp; + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, + "6067 %s: Entrypoint: sid %x xri %x\n", __func__, + sid, xri); + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + + ndlp = lpfc_findnode_did(phba->pport, sid); + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) || + ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) && + (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) { + atomic_inc(&tgtp->xmt_abort_rsp_error); + lpfc_printf_log(phba, KERN_WARNING, LOG_NVME_ABTS, + "6134 Drop ABTS - wrong NDLP state x%x.\n", + ndlp->nlp_state); + + /* No failure to an ABTS request. */ + return 0; + } + + abts_wqeq = ctxp->wqeq; + wqe_abts = &abts_wqeq->wqe; + ctxp->state = LPFC_NVMET_STE_ABORT; + + /* + * Since we zero the whole WQE, we need to ensure we set the WQE fields + * that were initialized in lpfc_sli4_nvmet_alloc. + */ + memset(wqe_abts, 0, sizeof(union lpfc_wqe)); + + /* Word 5 */ + bf_set(wqe_dfctl, &wqe_abts->xmit_sequence.wge_ctl, 0); + bf_set(wqe_ls, &wqe_abts->xmit_sequence.wge_ctl, 1); + bf_set(wqe_la, &wqe_abts->xmit_sequence.wge_ctl, 0); + bf_set(wqe_rctl, &wqe_abts->xmit_sequence.wge_ctl, FC_RCTL_BA_ABTS); + bf_set(wqe_type, &wqe_abts->xmit_sequence.wge_ctl, FC_TYPE_BLS); + + /* Word 6 */ + bf_set(wqe_ctxt_tag, &wqe_abts->xmit_sequence.wqe_com, + phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]); + bf_set(wqe_xri_tag, &wqe_abts->xmit_sequence.wqe_com, + abts_wqeq->sli4_xritag); + + /* Word 7 */ + bf_set(wqe_cmnd, &wqe_abts->xmit_sequence.wqe_com, + CMD_XMIT_SEQUENCE64_WQE); + bf_set(wqe_ct, &wqe_abts->xmit_sequence.wqe_com, SLI4_CT_RPI); + bf_set(wqe_class, &wqe_abts->xmit_sequence.wqe_com, CLASS3); + bf_set(wqe_pu, &wqe_abts->xmit_sequence.wqe_com, 0); + + /* Word 8 */ + wqe_abts->xmit_sequence.wqe_com.abort_tag = abts_wqeq->iotag; + + /* Word 9 */ + bf_set(wqe_reqtag, &wqe_abts->xmit_sequence.wqe_com, abts_wqeq->iotag); + /* Needs to be set by caller */ + bf_set(wqe_rcvoxid, &wqe_abts->xmit_sequence.wqe_com, xri); + + /* Word 10 */ + bf_set(wqe_dbde, &wqe_abts->xmit_sequence.wqe_com, 1); + bf_set(wqe_iod, &wqe_abts->xmit_sequence.wqe_com, LPFC_WQE_IOD_WRITE); + bf_set(wqe_lenloc, &wqe_abts->xmit_sequence.wqe_com, + LPFC_WQE_LENLOC_WORD12); + bf_set(wqe_ebde_cnt, &wqe_abts->xmit_sequence.wqe_com, 0); + bf_set(wqe_qosd, &wqe_abts->xmit_sequence.wqe_com, 0); + + /* Word 11 */ + bf_set(wqe_cqid, &wqe_abts->xmit_sequence.wqe_com, + LPFC_WQE_CQ_ID_DEFAULT); + bf_set(wqe_cmd_type, &wqe_abts->xmit_sequence.wqe_com, + OTHER_COMMAND); + + abts_wqeq->vport = phba->pport; + abts_wqeq->context1 = ndlp; + abts_wqeq->context2 = ctxp; + abts_wqeq->context3 = NULL; + abts_wqeq->rsvd2 = 0; + /* hba_wqidx should already be setup from command we are aborting */ + abts_wqeq->iocb.ulpCommand = CMD_XMIT_SEQUENCE64_CR; + abts_wqeq->iocb.ulpLe = 1; + + lpfc_printf_log(phba, KERN_INFO, LOG_NVME_ABTS, + "6069 Issue ABTS to xri x%x reqtag x%x\n", + xri, abts_wqeq->iotag); + return 1; +} + +static int +lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, + struct lpfc_nvmet_rcv_ctx *ctxp, + uint32_t sid, uint16_t xri) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct lpfc_iocbq *abts_wqeq; + union lpfc_wqe *abts_wqe; + struct lpfc_nodelist *ndlp; + unsigned long flags; + int rc; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + if (!ctxp->wqeq) { + ctxp->wqeq = ctxp->rqb_buffer->iocbq; + ctxp->wqeq->hba_wqidx = 0; + } + + ndlp = lpfc_findnode_did(phba->pport, sid); + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) || + ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) && + (ndlp->nlp_state != NLP_STE_MAPPED_NODE))) { + atomic_inc(&tgtp->xmt_abort_rsp_error); + lpfc_printf_log(phba, KERN_WARNING, LOG_NVME_ABTS, + "6160 Drop ABTS - wrong NDLP state x%x.\n", + ndlp->nlp_state); + + /* No failure to an ABTS request. */ + return 0; + } + + /* Issue ABTS for this WQE based on iotag */ + ctxp->abort_wqeq = lpfc_sli_get_iocbq(phba); + if (!ctxp->abort_wqeq) { + lpfc_printf_log(phba, KERN_WARNING, LOG_NVME_ABTS, + "6161 Abort failed: No wqeqs: " + "xri: x%x\n", ctxp->oxid); + /* No failure to an ABTS request. */ + return 0; + } + abts_wqeq = ctxp->abort_wqeq; + abts_wqe = &abts_wqeq->wqe; + ctxp->state = LPFC_NVMET_STE_ABORT; + + /* Announce entry to new IO submit field. */ + lpfc_printf_log(phba, KERN_ERR, LOG_NVME_ABTS, + "6162 Abort Request to rport DID x%06x " + "for xri x%x x%x\n", + ctxp->sid, ctxp->oxid, ctxp->wqeq->sli4_xritag); + + /* If the hba is getting reset, this flag is set. It is + * cleared when the reset is complete and rings reestablished. + */ + spin_lock_irqsave(&phba->hbalock, flags); + /* driver queued commands are in process of being flushed */ + if (phba->hba_flag & HBA_NVME_IOQ_FLUSH) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME, + "6163 Driver in reset cleanup - flushing " + "NVME Req now. hba_flag x%x oxid x%x\n", + phba->hba_flag, ctxp->oxid); + lpfc_sli_release_iocbq(phba, abts_wqeq); + return 0; + } + + /* Outstanding abort is in progress */ + if (abts_wqeq->iocb_flag & LPFC_DRIVER_ABORTED) { + spin_unlock_irqrestore(&phba->hbalock, flags); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME, + "6164 Outstanding NVME I/O Abort Request " + "still pending on oxid x%x\n", + ctxp->oxid); + lpfc_sli_release_iocbq(phba, abts_wqeq); + return 0; + } + + /* Ready - mark outstanding as aborted by driver. */ + abts_wqeq->iocb_flag |= LPFC_DRIVER_ABORTED; + + /* WQEs are reused. Clear stale data and set key fields to + * zero like ia, iaab, iaar, xri_tag, and ctxt_tag. + */ + memset(abts_wqe, 0, sizeof(union lpfc_wqe)); + + /* word 3 */ + bf_set(abort_cmd_criteria, &abts_wqe->abort_cmd, T_XRI_TAG); + + /* word 7 */ + bf_set(wqe_ct, &abts_wqe->abort_cmd.wqe_com, 0); + bf_set(wqe_cmnd, &abts_wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX); + + /* word 8 - tell the FW to abort the IO associated with this + * outstanding exchange ID. + */ + abts_wqe->abort_cmd.wqe_com.abort_tag = ctxp->wqeq->sli4_xritag; + + /* word 9 - this is the iotag for the abts_wqe completion. */ + bf_set(wqe_reqtag, &abts_wqe->abort_cmd.wqe_com, + abts_wqeq->iotag); + + /* word 10 */ + bf_set(wqe_qosd, &abts_wqe->abort_cmd.wqe_com, 1); + bf_set(wqe_lenloc, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_LENLOC_NONE); + + /* word 11 */ + bf_set(wqe_cmd_type, &abts_wqe->abort_cmd.wqe_com, OTHER_COMMAND); + bf_set(wqe_wqec, &abts_wqe->abort_cmd.wqe_com, 1); + bf_set(wqe_cqid, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); + + /* ABTS WQE must go to the same WQ as the WQE to be aborted */ + abts_wqeq->hba_wqidx = ctxp->wqeq->hba_wqidx; + abts_wqeq->wqe_cmpl = lpfc_nvmet_sol_fcp_abort_cmp; + abts_wqeq->iocb_cmpl = 0; + abts_wqeq->iocb_flag |= LPFC_IO_NVME; + abts_wqeq->context2 = ctxp; + rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_wqeq); + spin_unlock_irqrestore(&phba->hbalock, flags); + if (rc == WQE_SUCCESS) + return 0; + + lpfc_sli_release_iocbq(phba, abts_wqeq); + lpfc_printf_log(phba, KERN_ERR, LOG_NVME, + "6166 Failed abts issue_wqe with status x%x " + "for oxid x%x.\n", + rc, ctxp->oxid); + return 1; +} + + +static int +lpfc_nvmet_unsol_fcp_issue_abort(struct lpfc_hba *phba, + struct lpfc_nvmet_rcv_ctx *ctxp, + uint32_t sid, uint16_t xri) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct lpfc_iocbq *abts_wqeq; + unsigned long flags; + int rc; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + if (!ctxp->wqeq) { + ctxp->wqeq = ctxp->rqb_buffer->iocbq; + ctxp->wqeq->hba_wqidx = 0; + } + + rc = lpfc_nvmet_unsol_issue_abort(phba, ctxp, sid, xri); + if (rc == 0) + goto aerr; + + spin_lock_irqsave(&phba->hbalock, flags); + abts_wqeq = ctxp->wqeq; + abts_wqeq->wqe_cmpl = lpfc_nvmet_xmt_fcp_abort_cmp; + abts_wqeq->iocb_cmpl = 0; + abts_wqeq->iocb_flag |= LPFC_IO_NVMET; + rc = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abts_wqeq); + spin_unlock_irqrestore(&phba->hbalock, flags); + if (rc == WQE_SUCCESS) { + atomic_inc(&tgtp->xmt_abort_rsp); + return 0; + } + +aerr: + lpfc_nvmet_rq_post(phba, ctxp, &ctxp->rqb_buffer->hbuf); + atomic_inc(&tgtp->xmt_abort_rsp_error); + lpfc_printf_log(phba, KERN_WARNING, LOG_NVME_ABTS, + "6135 Failed to Issue ABTS for oxid x%x. Status x%x\n", + ctxp->oxid, rc); + return 1; +} + +static int +lpfc_nvmet_unsol_ls_issue_abort(struct lpfc_hba *phba, + struct lpfc_nvmet_rcv_ctx *ctxp, + uint32_t sid, uint16_t xri) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct lpfc_iocbq *abts_wqeq; + union lpfc_wqe *wqe_abts; + unsigned long flags; + int rc; + + tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; + if (!ctxp->wqeq) { + /* Issue ABTS for this WQE based on iotag */ + ctxp->wqeq = lpfc_sli_get_iocbq(phba); + if (!ctxp->wqeq) { + lpfc_printf_log(phba, KERN_WARNING, LOG_NVME_ABTS, + "6068 Abort failed: No wqeqs: " + "xri: x%x\n", xri); + /* No failure to an ABTS request. */ + kfree(ctxp); + return 0; + } + } + abts_wqeq = ctxp->wqeq; + wqe_abts = &abts_wqeq->wqe; + lpfc_nvmet_unsol_issue_abort(phba, ctxp, sid, xri); + + spin_lock_irqsave(&phba->hbalock, flags); + abts_wqeq->wqe_cmpl = lpfc_nvmet_xmt_ls_abort_cmp; + abts_wqeq->iocb_cmpl = 0; + abts_wqeq->iocb_flag |= LPFC_IO_NVME_LS; + rc = lpfc_sli4_issue_wqe(phba, LPFC_ELS_RING, abts_wqeq); + spin_unlock_irqrestore(&phba->hbalock, flags); + if (rc == WQE_SUCCESS) { + atomic_inc(&tgtp->xmt_abort_rsp); + return 0; + } + + atomic_inc(&tgtp->xmt_abort_rsp_error); + abts_wqeq->context2 = NULL; + abts_wqeq->context3 = NULL; + lpfc_sli_release_iocbq(phba, abts_wqeq); + kfree(ctxp); + lpfc_printf_log(phba, KERN_WARNING, LOG_NVME_ABTS, + "6056 Failed to Issue ABTS. Status x%x\n", rc); + return 0; +} diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h new file mode 100644 index 000000000000..ca96f05c1604 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -0,0 +1,116 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * Copyright (C) 2004-2016 Emulex. All rights reserved. * + * EMULEX and SLI are trademarks of Emulex. * + * www.broadcom.com * + * Portions Copyright (C) 2004-2005 Christoph Hellwig * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of version 2 of the GNU General * + * Public License as published by the Free Software Foundation. * + * This program is distributed in the hope that it will be useful. * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * + * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * + * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * + * TO BE LEGALLY INVALID. See the GNU General Public License for * + * more details, a copy of which can be found in the file COPYING * + * included with this package. * + ********************************************************************/ + +#define LPFC_NVMET_MIN_SEGS 16 +#define LPFC_NVMET_DEFAULT_SEGS 64 /* 256K IOs */ +#define LPFC_NVMET_MAX_SEGS 510 +#define LPFC_NVMET_SUCCESS_LEN 12 + +/* Used for NVME Target */ +struct lpfc_nvmet_tgtport { + struct lpfc_hba *phba; + struct completion tport_unreg_done; + + /* Stats counters - lpfc_nvmet_unsol_ls_buffer */ + atomic_t rcv_ls_req_in; + atomic_t rcv_ls_req_out; + atomic_t rcv_ls_req_drop; + atomic_t xmt_ls_abort; + + /* Stats counters - lpfc_nvmet_xmt_ls_rsp */ + atomic_t xmt_ls_rsp; + atomic_t xmt_ls_drop; + + /* Stats counters - lpfc_nvmet_xmt_ls_rsp_cmp */ + atomic_t xmt_ls_rsp_error; + atomic_t xmt_ls_rsp_cmpl; + + /* Stats counters - lpfc_nvmet_unsol_fcp_buffer */ + atomic_t rcv_fcp_cmd_in; + atomic_t rcv_fcp_cmd_out; + atomic_t rcv_fcp_cmd_drop; + + /* Stats counters - lpfc_nvmet_xmt_fcp_op */ + atomic_t xmt_fcp_abort; + atomic_t xmt_fcp_drop; + atomic_t xmt_fcp_read_rsp; + atomic_t xmt_fcp_read; + atomic_t xmt_fcp_write; + atomic_t xmt_fcp_rsp; + + /* Stats counters - lpfc_nvmet_xmt_fcp_op_cmp */ + atomic_t xmt_fcp_rsp_cmpl; + atomic_t xmt_fcp_rsp_error; + atomic_t xmt_fcp_rsp_drop; + + + /* Stats counters - lpfc_nvmet_unsol_issue_abort */ + atomic_t xmt_abort_rsp; + atomic_t xmt_abort_rsp_error; + + /* Stats counters - lpfc_nvmet_xmt_abort_cmp */ + atomic_t xmt_abort_cmpl; +}; + +struct lpfc_nvmet_rcv_ctx { + union { + struct nvmefc_tgt_ls_req ls_req; + struct nvmefc_tgt_fcp_req fcp_req; + } ctx; + struct lpfc_hba *phba; + struct lpfc_iocbq *wqeq; + struct lpfc_iocbq *abort_wqeq; + dma_addr_t txrdy_phys; + uint32_t *txrdy; + uint32_t sid; + uint32_t offset; + uint16_t oxid; + uint16_t size; + uint16_t entry_cnt; + uint16_t cpu; + uint16_t state; + /* States */ +#define LPFC_NVMET_STE_FREE 0 +#define LPFC_NVMET_STE_RCV 1 +#define LPFC_NVMET_STE_DATA 2 +#define LPFC_NVMET_STE_ABORT 3 +#define LPFC_NVMET_STE_RSP 4 +#define LPFC_NVMET_STE_DONE 5 + uint16_t flag; +#define LPFC_NVMET_IO_INP 1 +#define LPFC_NVMET_ABORT_OP 2 + struct rqb_dmabuf *rqb_buffer; + +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + uint64_t ts_isr_cmd; + uint64_t ts_cmd_nvme; + uint64_t ts_nvme_data; + uint64_t ts_data_wqput; + uint64_t ts_isr_data; + uint64_t ts_data_nvme; + uint64_t ts_nvme_status; + uint64_t ts_status_wqput; + uint64_t ts_isr_status; + uint64_t ts_status_nvme; +#endif +}; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 1180a22beb43..9d6384af9fce 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -413,7 +415,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) * struct fcp_cmnd, struct fcp_rsp and the number of bde's * necessary to support the sg_tablesize. */ - psb->data = pci_pool_zalloc(phba->lpfc_scsi_dma_buf_pool, + psb->data = pci_pool_zalloc(phba->lpfc_sg_dma_buf_pool, GFP_KERNEL, &psb->dma_handle); if (!psb->data) { kfree(psb); @@ -424,8 +426,8 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) /* Allocate iotag for psb->cur_iocbq. */ iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq); if (iotag == 0) { - pci_pool_free(phba->lpfc_scsi_dma_buf_pool, - psb->data, psb->dma_handle); + pci_pool_free(phba->lpfc_sg_dma_buf_pool, + psb->data, psb->dma_handle); kfree(psb); break; } @@ -522,6 +524,8 @@ lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport) struct lpfc_scsi_buf *psb, *next_psb; unsigned long iflag = 0; + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) + return; spin_lock_irqsave(&phba->hbalock, iflag); spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); list_for_each_entry_safe(psb, next_psb, @@ -554,8 +558,10 @@ lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, int i; struct lpfc_nodelist *ndlp; int rrq_empty = 0; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring = phba->sli4_hba.els_wq->pring; + if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP)) + return; spin_lock_irqsave(&phba->hbalock, iflag); spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); list_for_each_entry_safe(psb, next_psb, @@ -819,7 +825,7 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) * for the struct fcp_cmnd, struct fcp_rsp and the number * of bde's necessary to support the sg_tablesize. */ - psb->data = pci_pool_zalloc(phba->lpfc_scsi_dma_buf_pool, + psb->data = pci_pool_zalloc(phba->lpfc_sg_dma_buf_pool, GFP_KERNEL, &psb->dma_handle); if (!psb->data) { kfree(psb); @@ -832,7 +838,7 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) */ if (phba->cfg_enable_bg && (((unsigned long)(psb->data) & (unsigned long)(SLI4_PAGE_SIZE - 1)) != 0)) { - pci_pool_free(phba->lpfc_scsi_dma_buf_pool, + pci_pool_free(phba->lpfc_sg_dma_buf_pool, psb->data, psb->dma_handle); kfree(psb); break; @@ -841,8 +847,8 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) lxri = lpfc_sli4_next_xritag(phba); if (lxri == NO_XRI) { - pci_pool_free(phba->lpfc_scsi_dma_buf_pool, - psb->data, psb->dma_handle); + pci_pool_free(phba->lpfc_sg_dma_buf_pool, + psb->data, psb->dma_handle); kfree(psb); break; } @@ -850,8 +856,8 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) /* Allocate iotag for psb->cur_iocbq. */ iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq); if (iotag == 0) { - pci_pool_free(phba->lpfc_scsi_dma_buf_pool, - psb->data, psb->dma_handle); + pci_pool_free(phba->lpfc_sg_dma_buf_pool, + psb->data, psb->dma_handle); kfree(psb); lpfc_printf_log(phba, KERN_ERR, LOG_FCP, "3368 Failed to allocate IOTAG for" @@ -920,7 +926,7 @@ lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) phba->sli4_hba.scsi_xri_cnt++; spin_unlock_irq(&phba->scsi_buf_list_get_lock); } - lpfc_printf_log(phba, KERN_INFO, LOG_BG, + lpfc_printf_log(phba, KERN_INFO, LOG_BG | LOG_FCP, "3021 Allocate %d out of %d requested new SCSI " "buffers\n", bcnt, num_to_alloc); @@ -3894,7 +3900,7 @@ int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, } } chann = atomic_add_return(1, &phba->fcp_qidx); - chann = (chann % phba->cfg_fcp_io_channel); + chann = chann % phba->cfg_fcp_io_channel; return chann; } @@ -3925,6 +3931,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct Scsi_Host *shost; uint32_t logit = LOG_FCP; + phba->fc4ScsiIoCmpls++; + /* Sanity check on return of outstanding command */ cmd = lpfc_cmd->pCmd; if (!cmd) @@ -3967,6 +3975,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, lpfc_cmd->prot_data_segment = NULL; } #endif + if (pnode && NLP_CHK_NODE_ACT(pnode)) atomic_dec(&pnode->cmd_pending); @@ -4241,19 +4250,19 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, vport->cfg_first_burst_size; } fcp_cmnd->fcpCntl3 = WRITE_DATA; - phba->fc4OutputRequests++; + phba->fc4ScsiOutputRequests++; } else { iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR; iocb_cmd->ulpPU = PARM_READ_CHECK; fcp_cmnd->fcpCntl3 = READ_DATA; - phba->fc4InputRequests++; + phba->fc4ScsiInputRequests++; } } else { iocb_cmd->ulpCommand = CMD_FCP_ICMND64_CR; iocb_cmd->un.fcpi.fcpi_parm = 0; iocb_cmd->ulpPU = 0; fcp_cmnd->fcpCntl3 = 0; - phba->fc4ControlRequests++; + phba->fc4ScsiControlRequests++; } if (phba->sli_rev == 3 && !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) @@ -4467,7 +4476,7 @@ static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba) unsigned long poll_tmo_expires = (jiffies + msecs_to_jiffies(phba->cfg_poll_tmo)); - if (!list_empty(&phba->sli.ring[LPFC_FCP_RING].txcmplq)) + if (!list_empty(&phba->sli.sli3_ring[LPFC_FCP_RING].txcmplq)) mod_timer(&phba->fcp_poll_timer, poll_tmo_expires); } @@ -4497,7 +4506,7 @@ void lpfc_poll_timeout(unsigned long ptr) if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { lpfc_sli_handle_fast_ring_event(phba, - &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); + &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ); if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_poll_rearm_timer(phba); @@ -4561,7 +4570,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) if (lpfc_cmd == NULL) { lpfc_rampdown_queue_depth(phba); - lpfc_printf_vlog(vport, KERN_INFO, LOG_MISC, + lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_ERROR, "0707 driver's buffer pool is empty, " "IO busied\n"); goto out_host_busy; @@ -4636,7 +4645,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) } if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { lpfc_sli_handle_fast_ring_event(phba, - &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); + &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ); if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_poll_rearm_timer(phba); @@ -4681,7 +4690,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) IOCB_t *cmd, *icmd; int ret = SUCCESS, status = 0; struct lpfc_sli_ring *pring_s4; - int ring_number, ret_val; + int ret_val; unsigned long flags, iflags; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq); @@ -4769,7 +4778,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) icmd->ulpClass = cmd->ulpClass; /* ABTS WQE must go to the same WQ as the WQE to be aborted */ - abtsiocb->fcp_wqidx = iocb->fcp_wqidx; + abtsiocb->hba_wqidx = iocb->hba_wqidx; abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX; if (iocb->iocb_flag & LPFC_IO_FOF) abtsiocb->iocb_flag |= LPFC_IO_FOF; @@ -4782,8 +4791,11 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; abtsiocb->vport = vport; if (phba->sli_rev == LPFC_SLI_REV4) { - ring_number = MAX_SLI3_CONFIGURED_RINGS + iocb->fcp_wqidx; - pring_s4 = &phba->sli.ring[ring_number]; + pring_s4 = lpfc_sli4_calc_ring(phba, iocb); + if (pring_s4 == NULL) { + ret = FAILED; + goto out_unlock; + } /* Note: both hbalock and ring_lock must be set here */ spin_lock_irqsave(&pring_s4->ring_lock, iflags); ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, @@ -4805,7 +4817,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_sli_handle_fast_ring_event(phba, - &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); + &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ); wait_for_cmpl: lpfc_cmd->waitq = &waitq; @@ -5105,7 +5117,7 @@ lpfc_reset_flush_io_context(struct lpfc_vport *vport, uint16_t tgt_id, cnt = lpfc_sli_sum_iocb(vport, tgt_id, lun_id, context); if (cnt) lpfc_sli_abort_taskmgmt(vport, - &phba->sli.ring[phba->sli.fcp_ring], + &phba->sli.sli3_ring[LPFC_FCP_RING], tgt_id, lun_id, context); later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies; while (time_after(later, jiffies) && cnt) { @@ -5323,7 +5335,8 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) continue; if (ndlp->nlp_state == NLP_STE_MAPPED_NODE && ndlp->nlp_sid == i && - ndlp->rport) { + ndlp->rport && + ndlp->nlp_type & NLP_FCP_TARGET) { match = 1; break; } @@ -5534,7 +5547,7 @@ lpfc_slave_configure(struct scsi_device *sdev) if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { lpfc_sli_handle_fast_ring_event(phba, - &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ); + &phba->sli.sli3_ring[LPFC_FCP_RING], HA_R0RE_REQ); if (phba->cfg_poll & DISABLE_FCP_RING_INT) lpfc_poll_rearm_timer(phba); } @@ -5898,6 +5911,48 @@ lpfc_disable_oas_lun(struct lpfc_hba *phba, struct lpfc_name *vport_wwpn, return false; } +static int +lpfc_no_command(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) +{ + return SCSI_MLQUEUE_HOST_BUSY; +} + +static int +lpfc_no_handler(struct scsi_cmnd *cmnd) +{ + return FAILED; +} + +static int +lpfc_no_slave(struct scsi_device *sdev) +{ + return -ENODEV; +} + +struct scsi_host_template lpfc_template_nvme = { + .module = THIS_MODULE, + .name = LPFC_DRIVER_NAME, + .proc_name = LPFC_DRIVER_NAME, + .info = lpfc_info, + .queuecommand = lpfc_no_command, + .eh_abort_handler = lpfc_no_handler, + .eh_device_reset_handler = lpfc_no_handler, + .eh_target_reset_handler = lpfc_no_handler, + .eh_bus_reset_handler = lpfc_no_handler, + .eh_host_reset_handler = lpfc_no_handler, + .slave_alloc = lpfc_no_slave, + .slave_configure = lpfc_no_slave, + .scan_finished = lpfc_scan_finished, + .this_id = -1, + .sg_tablesize = 1, + .cmd_per_lun = 1, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = lpfc_hba_attrs, + .max_sectors = 0xFFFF, + .vendor_id = LPFC_NL_VENDOR_ID, + .track_queue_depth = 0, +}; + struct scsi_host_template lpfc_template_s3 = { .module = THIS_MODULE, .name = LPFC_DRIVER_NAME, diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 8cb80dabada8..5da7e15400cb 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -135,6 +137,8 @@ struct lpfc_scsi_buf { uint32_t timeout; + uint16_t flags; /* TBD convert exch_busy to flags */ +#define LPFC_SBUF_XBUSY 0x1 /* SLI4 hba reported XB on WCQE cmpl */ uint16_t exch_busy; /* SLI4 hba reported XB on complete WCQE */ uint16_t status; /* From IOCB Word 7- ulpStatus */ uint32_t result; /* From IOCB Word 4. */ @@ -164,6 +168,8 @@ struct lpfc_scsi_buf { * Iotag is in here */ struct lpfc_iocbq cur_iocbq; + uint16_t cpu; + wait_queue_head_t *waitq; unsigned long start_time; @@ -178,13 +184,15 @@ struct lpfc_scsi_buf { #endif }; -#define LPFC_SCSI_DMA_EXT_SIZE 264 -#define LPFC_BPL_SIZE 1024 -#define MDAC_DIRECT_CMD 0x22 +#define LPFC_SCSI_DMA_EXT_SIZE 264 +#define LPFC_BPL_SIZE 1024 +#define MDAC_DIRECT_CMD 0x22 + +#define FIND_FIRST_OAS_LUN 0 +#define NO_MORE_OAS_LUN -1 +#define NOT_OAS_ENABLED_LUN NO_MORE_OAS_LUN -#define FIND_FIRST_OAS_LUN 0 -#define NO_MORE_OAS_LUN -1 -#define NOT_OAS_ENABLED_LUN NO_MORE_OAS_LUN +#define TXRDY_PAYLOAD_LEN 12 int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index d977a472f89f..e43e5e23c24b 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -34,14 +36,18 @@ #include <scsi/fc/fc_fs.h> #include <linux/aer.h> +#include <linux/nvme-fc-driver.h> + #include "lpfc_hw4.h" #include "lpfc_hw.h" #include "lpfc_sli.h" #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" -#include "lpfc_scsi.h" #include "lpfc.h" +#include "lpfc_scsi.h" +#include "lpfc_nvme.h" +#include "lpfc_nvmet.h" #include "lpfc_crtn.h" #include "lpfc_logmsg.h" #include "lpfc_compat.h" @@ -67,14 +73,17 @@ static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *, struct lpfc_iocbq *); static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *, struct hbq_dmabuf *); -static int lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *, struct lpfc_queue *, +static int lpfc_sli4_fp_handle_cqe(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_cqe *); -static int lpfc_sli4_post_els_sgl_list(struct lpfc_hba *, struct list_head *, +static int lpfc_sli4_post_sgl_list(struct lpfc_hba *, struct list_head *, int); static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *, struct lpfc_eqe *, uint32_t); static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba); static bool lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba); +static int lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, + struct lpfc_sli_ring *pring, + struct lpfc_iocbq *cmdiocb); static IOCB_t * lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq) @@ -271,10 +280,11 @@ lpfc_sli4_eq_get(struct lpfc_queue *q) /* * insert barrier for instruction interlock : data from the hardware * must have the valid bit checked before it can be copied and acted - * upon. Given what was seen in lpfc_sli4_cq_get() of speculative - * instructions allowing action on content before valid bit checked, - * add barrier here as well. May not be needed as "content" is a - * single 32-bit entity here (vs multi word structure for cq's). + * upon. Speculative instructions were allowing a bcopy at the start + * of lpfc_sli4_fp_handle_wcqe(), which is called immediately + * after our return, to copy data before the valid bit check above + * was done. As such, some of the copied data was stale. The barrier + * ensures the check is before any data is copied. */ mb(); return eqe; @@ -386,11 +396,10 @@ lpfc_sli4_cq_get(struct lpfc_queue *q) /* * insert barrier for instruction interlock : data from the hardware * must have the valid bit checked before it can be copied and acted - * upon. Speculative instructions were allowing a bcopy at the start - * of lpfc_sli4_fp_handle_wcqe(), which is called immediately - * after our return, to copy data before the valid bit check above - * was done. As such, some of the copied data was stale. The barrier - * ensures the check is before any data is copied. + * upon. Given what was seen in lpfc_sli4_cq_get() of speculative + * instructions allowing action on content before valid bit checked, + * add barrier here as well. May not be needed as "content" is a + * single 32-bit entity here (vs multi word structure for cq's). */ mb(); return cqe; @@ -456,7 +465,7 @@ lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm) * on @q then this function will return -ENOMEM. * The caller is expected to hold the hbalock when calling this routine. **/ -static int +int lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq, struct lpfc_rqe *hrqe, struct lpfc_rqe *drqe) { @@ -602,7 +611,7 @@ __lpfc_sli_get_iocbq(struct lpfc_hba *phba) * * Returns sglq ponter = success, NULL = Failure. **/ -static struct lpfc_sglq * +struct lpfc_sglq * __lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xritag) { struct lpfc_sglq *sglq; @@ -902,7 +911,7 @@ out: } /** - * __lpfc_sli_get_sglq - Allocates an iocb object from sgl pool + * __lpfc_sli_get_els_sglq - Allocates an iocb object from sgl pool * @phba: Pointer to HBA context object. * @piocb: Pointer to the iocbq. * @@ -912,9 +921,9 @@ out: * allocated sglq object else it returns NULL. **/ static struct lpfc_sglq * -__lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) +__lpfc_sli_get_els_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) { - struct list_head *lpfc_sgl_list = &phba->sli4_hba.lpfc_sgl_list; + struct list_head *lpfc_els_sgl_list = &phba->sli4_hba.lpfc_els_sgl_list; struct lpfc_sglq *sglq = NULL; struct lpfc_sglq *start_sglq = NULL; struct lpfc_scsi_buf *lpfc_cmd; @@ -938,18 +947,21 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) ndlp = piocbq->context1; } - list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_remove_head(lpfc_els_sgl_list, sglq, struct lpfc_sglq, list); start_sglq = sglq; while (!found) { if (!sglq) return NULL; - if (lpfc_test_rrq_active(phba, ndlp, sglq->sli4_lxritag)) { + if (ndlp && ndlp->active_rrqs_xri_bitmap && + test_bit(sglq->sli4_lxritag, + ndlp->active_rrqs_xri_bitmap)) { /* This xri has an rrq outstanding for this DID. * put it back in the list and get another xri. */ - list_add_tail(&sglq->list, lpfc_sgl_list); + list_add_tail(&sglq->list, lpfc_els_sgl_list); sglq = NULL; - list_remove_head(lpfc_sgl_list, sglq, + list_remove_head(lpfc_els_sgl_list, sglq, struct lpfc_sglq, list); if (sglq == start_sglq) { sglq = NULL; @@ -962,6 +974,35 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) phba->sli4_hba.lpfc_sglq_active_list[sglq->sli4_lxritag] = sglq; sglq->state = SGL_ALLOCATED; } + spin_unlock(&phba->sli4_hba.sgl_list_lock); + return sglq; +} + +/** + * __lpfc_sli_get_nvmet_sglq - Allocates an iocb object from sgl pool + * @phba: Pointer to HBA context object. + * @piocb: Pointer to the iocbq. + * + * This function is called with the sgl_list lock held. This function + * gets a new driver sglq object from the sglq list. If the + * list is not empty then it is successful, it returns pointer to the newly + * allocated sglq object else it returns NULL. + **/ +struct lpfc_sglq * +__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq) +{ + struct list_head *lpfc_nvmet_sgl_list; + struct lpfc_sglq *sglq = NULL; + + lpfc_nvmet_sgl_list = &phba->sli4_hba.lpfc_nvmet_sgl_list; + + lockdep_assert_held(&phba->sli4_hba.sgl_list_lock); + + list_remove_head(lpfc_nvmet_sgl_list, sglq, struct lpfc_sglq, list); + if (!sglq) + return NULL; + phba->sli4_hba.lpfc_sglq_active_list[sglq->sli4_lxritag] = sglq; + sglq->state = SGL_ALLOCATED; return sglq; } @@ -1002,7 +1043,7 @@ lpfc_sli_get_iocbq(struct lpfc_hba *phba) * this IO was aborted then the sglq entry it put on the * lpfc_abts_els_sgl_list until the CQ_ABORTED_XRI is received. If the * IO has good status or fails for any other reason then the sglq - * entry is added to the free list (lpfc_sgl_list). + * entry is added to the free list (lpfc_els_sgl_list). **/ static void __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) @@ -1010,7 +1051,7 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) struct lpfc_sglq *sglq; size_t start_clean = offsetof(struct lpfc_iocbq, iocb); unsigned long iflag = 0; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; lockdep_assert_held(&phba->hbalock); @@ -1021,21 +1062,36 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) if (sglq) { + if (iocbq->iocb_flag & LPFC_IO_NVMET) { + spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock, + iflag); + sglq->state = SGL_FREED; + sglq->ndlp = NULL; + list_add_tail(&sglq->list, + &phba->sli4_hba.lpfc_nvmet_sgl_list); + spin_unlock_irqrestore( + &phba->sli4_hba.sgl_list_lock, iflag); + goto out; + } + + pring = phba->sli4_hba.els_wq->pring; if ((iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) && (sglq->state != SGL_XRI_ABORTED)) { - spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock, - iflag); + spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock, + iflag); list_add(&sglq->list, - &phba->sli4_hba.lpfc_abts_els_sgl_list); + &phba->sli4_hba.lpfc_abts_els_sgl_list); spin_unlock_irqrestore( - &phba->sli4_hba.abts_sgl_list_lock, iflag); + &phba->sli4_hba.sgl_list_lock, iflag); } else { - spin_lock_irqsave(&pring->ring_lock, iflag); + spin_lock_irqsave(&phba->sli4_hba.sgl_list_lock, + iflag); sglq->state = SGL_FREED; sglq->ndlp = NULL; list_add_tail(&sglq->list, - &phba->sli4_hba.lpfc_sgl_list); - spin_unlock_irqrestore(&pring->ring_lock, iflag); + &phba->sli4_hba.lpfc_els_sgl_list); + spin_unlock_irqrestore( + &phba->sli4_hba.sgl_list_lock, iflag); /* Check if TXQ queue needs to be serviced */ if (!list_empty(&pring->txq)) @@ -1043,13 +1099,15 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq) } } - +out: /* * Clean all volatile data fields, preserve iotag and node struct. */ memset((char *)iocbq + start_clean, 0, sizeof(*iocbq) - start_clean); iocbq->sli4_lxritag = NO_XRI; iocbq->sli4_xritag = NO_XRI; + iocbq->iocb_flag &= ~(LPFC_IO_NVME | LPFC_IO_NVMET | + LPFC_IO_NVME_LS); list_add_tail(&iocbq->list, &phba->lpfc_iocb_list); } @@ -1639,7 +1697,7 @@ lpfc_sli_resume_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) if (lpfc_is_link_up(phba) && (!list_empty(&pring->txq)) && - (pring->ringno != phba->sli.fcp_ring || + (pring->ringno != LPFC_FCP_RING || phba->sli.sli_flag & LPFC_PROCESS_LA)) { while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && @@ -1718,7 +1776,6 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba) struct hbq_dmabuf *hbq_buf; unsigned long flags; int i, hbq_count; - uint32_t hbqno; hbq_count = lpfc_sli_hbq_count(); /* Return all memory used by all HBQs */ @@ -1732,24 +1789,6 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba) } phba->hbqs[i].buffer_count = 0; } - /* Return all HBQ buffer that are in-fly */ - list_for_each_entry_safe(dmabuf, next_dmabuf, &phba->rb_pend_list, - list) { - hbq_buf = container_of(dmabuf, struct hbq_dmabuf, dbuf); - list_del(&hbq_buf->dbuf.list); - if (hbq_buf->tag == -1) { - (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer) - (phba, hbq_buf); - } else { - hbqno = hbq_buf->tag >> 16; - if (hbqno >= LPFC_MAX_HBQS) - (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer) - (phba, hbq_buf); - else - (phba->hbqs[hbqno].hbq_free_buffer)(phba, - hbq_buf); - } - } /* Mark the HBQs not in use */ phba->hbq_in_use = 0; @@ -1802,7 +1841,7 @@ lpfc_sli_hbq_to_firmware_s3(struct lpfc_hba *phba, uint32_t hbqno, hbqe->bde.addrHigh = le32_to_cpu(putPaddrHigh(physaddr)); hbqe->bde.addrLow = le32_to_cpu(putPaddrLow(physaddr)); - hbqe->bde.tus.f.bdeSize = hbq_buf->size; + hbqe->bde.tus.f.bdeSize = hbq_buf->total_size; hbqe->bde.tus.f.bdeFlags = 0; hbqe->bde.tus.w = le32_to_cpu(hbqe->bde.tus.w); hbqe->buffer_tag = le32_to_cpu(hbq_buf->tag); @@ -1834,17 +1873,23 @@ lpfc_sli_hbq_to_firmware_s4(struct lpfc_hba *phba, uint32_t hbqno, int rc; struct lpfc_rqe hrqe; struct lpfc_rqe drqe; + struct lpfc_queue *hrq; + struct lpfc_queue *drq; + + if (hbqno != LPFC_ELS_HBQ) + return 1; + hrq = phba->sli4_hba.hdr_rq; + drq = phba->sli4_hba.dat_rq; lockdep_assert_held(&phba->hbalock); hrqe.address_lo = putPaddrLow(hbq_buf->hbuf.phys); hrqe.address_hi = putPaddrHigh(hbq_buf->hbuf.phys); drqe.address_lo = putPaddrLow(hbq_buf->dbuf.phys); drqe.address_hi = putPaddrHigh(hbq_buf->dbuf.phys); - rc = lpfc_sli4_rq_put(phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq, - &hrqe, &drqe); + rc = lpfc_sli4_rq_put(hrq, drq, &hrqe, &drqe); if (rc < 0) return rc; - hbq_buf->tag = rc; + hbq_buf->tag = (rc | (hbqno << 16)); list_add_tail(&hbq_buf->dbuf.list, &phba->hbqs[hbqno].hbq_buffer_list); return 0; } @@ -1861,22 +1906,9 @@ static struct lpfc_hbq_init lpfc_els_hbq = { .add_count = 40, }; -/* HBQ for the extra ring if needed */ -static struct lpfc_hbq_init lpfc_extra_hbq = { - .rn = 1, - .entry_count = 200, - .mask_count = 0, - .profile = 0, - .ring_mask = (1 << LPFC_EXTRA_RING), - .buffer_count = 0, - .init_count = 0, - .add_count = 5, -}; - /* Array of HBQs */ struct lpfc_hbq_init *lpfc_hbq_defs[] = { &lpfc_els_hbq, - &lpfc_extra_hbq, }; /** @@ -1998,6 +2030,29 @@ lpfc_sli_hbqbuf_get(struct list_head *rb_list) } /** + * lpfc_sli_rqbuf_get - Remove the first dma buffer off of an RQ list + * @phba: Pointer to HBA context object. + * @hbqno: HBQ number. + * + * This function removes the first RQ buffer on an RQ buffer list and returns a + * pointer to that buffer. If it finds no buffers on the list it returns NULL. + **/ +static struct rqb_dmabuf * +lpfc_sli_rqbuf_get(struct lpfc_hba *phba, struct lpfc_queue *hrq) +{ + struct lpfc_dmabuf *h_buf; + struct lpfc_rqb *rqbp; + + rqbp = hrq->rqbp; + list_remove_head(&rqbp->rqb_buffer_list, h_buf, + struct lpfc_dmabuf, list); + if (!h_buf) + return NULL; + rqbp->buffer_count--; + return container_of(h_buf, struct rqb_dmabuf, hbuf); +} + +/** * lpfc_sli_hbqbuf_find - Find the hbq buffer associated with a tag * @phba: Pointer to HBA context object. * @tag: Tag of the hbq buffer. @@ -2463,6 +2518,14 @@ lpfc_complete_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, { int i; + switch (fch_type) { + case FC_TYPE_NVME: + lpfc_nvmet_unsol_ls_event(phba, pring, saveq); + return 1; + default: + break; + } + /* unSolicited Responses */ if (pring->prt[0].profile) { if (pring->prt[0].lpfc_sli_rcv_unsol_event) @@ -2713,7 +2776,7 @@ static struct lpfc_iocbq * lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, uint16_t iotag) { - struct lpfc_iocbq *cmd_iocb; + struct lpfc_iocbq *cmd_iocb = NULL; lockdep_assert_held(&phba->hbalock); if (iotag != 0 && iotag <= phba->sli.last_iotag) { @@ -2727,8 +2790,10 @@ lpfc_sli_iocbq_lookup_by_tag(struct lpfc_hba *phba, } lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0372 iotag x%x is out of range: max iotag (x%x)\n", - iotag, phba->sli.last_iotag); + "0372 iotag x%x lookup error: max iotag (x%x) " + "iocb_flag x%x\n", + iotag, phba->sli.last_iotag, + cmd_iocb ? cmd_iocb->iocb_flag : 0xffff); return NULL; } @@ -3598,6 +3663,33 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) } /** + * lpfc_sli_abort_wqe_ring - Abort all iocbs in the ring + * @phba: Pointer to HBA context object. + * @pring: Pointer to driver SLI ring object. + * + * This function aborts all iocbs in the given ring and frees all the iocb + * objects in txq. This function issues an abort iocb for all the iocb commands + * in txcmplq. The iocbs in the txcmplq is not guaranteed to complete before + * the return of this function. The caller is not required to hold any locks. + **/ +void +lpfc_sli_abort_wqe_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) +{ + LIST_HEAD(completions); + struct lpfc_iocbq *iocb, *next_iocb; + + if (pring->ringno == LPFC_ELS_RING) + lpfc_fabric_abort_hba(phba); + + spin_lock_irq(&phba->hbalock); + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) + lpfc_sli4_abort_nvme_io(phba, pring, iocb); + spin_unlock_irq(&phba->hbalock); +} + + +/** * lpfc_sli_abort_fcp_rings - Abort all iocbs in all FCP rings * @phba: Pointer to HBA context object. * @pring: Pointer to driver SLI ring object. @@ -3617,15 +3709,40 @@ lpfc_sli_abort_fcp_rings(struct lpfc_hba *phba) /* Look on all the FCP Rings for the iotag */ if (phba->sli_rev >= LPFC_SLI_REV4) { for (i = 0; i < phba->cfg_fcp_io_channel; i++) { - pring = &psli->ring[i + MAX_SLI3_CONFIGURED_RINGS]; + pring = phba->sli4_hba.fcp_wq[i]->pring; lpfc_sli_abort_iocb_ring(phba, pring); } } else { - pring = &psli->ring[psli->fcp_ring]; + pring = &psli->sli3_ring[LPFC_FCP_RING]; lpfc_sli_abort_iocb_ring(phba, pring); } } +/** + * lpfc_sli_abort_nvme_rings - Abort all wqes in all NVME rings + * @phba: Pointer to HBA context object. + * + * This function aborts all wqes in NVME rings. This function issues an + * abort wqe for all the outstanding IO commands in txcmplq. The iocbs in + * the txcmplq is not guaranteed to complete before the return of this + * function. The caller is not required to hold any locks. + **/ +void +lpfc_sli_abort_nvme_rings(struct lpfc_hba *phba) +{ + struct lpfc_sli_ring *pring; + uint32_t i; + + if (phba->sli_rev < LPFC_SLI_REV4) + return; + + /* Abort all IO on each NVME ring. */ + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + pring = phba->sli4_hba.nvme_wq[i]->pring; + lpfc_sli_abort_wqe_ring(phba, pring); + } +} + /** * lpfc_sli_flush_fcp_rings - flush all iocbs in the fcp ring @@ -3654,7 +3771,7 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) /* Look on all the FCP Rings for the iotag */ if (phba->sli_rev >= LPFC_SLI_REV4) { for (i = 0; i < phba->cfg_fcp_io_channel; i++) { - pring = &psli->ring[i + MAX_SLI3_CONFIGURED_RINGS]; + pring = phba->sli4_hba.fcp_wq[i]->pring; spin_lock_irq(&pring->ring_lock); /* Retrieve everything on txq */ @@ -3675,7 +3792,7 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) IOERR_SLI_DOWN); } } else { - pring = &psli->ring[psli->fcp_ring]; + pring = &psli->sli3_ring[LPFC_FCP_RING]; spin_lock_irq(&phba->hbalock); /* Retrieve everything on txq */ @@ -3696,6 +3813,51 @@ lpfc_sli_flush_fcp_rings(struct lpfc_hba *phba) } /** + * lpfc_sli_flush_nvme_rings - flush all wqes in the nvme rings + * @phba: Pointer to HBA context object. + * + * This function flushes all wqes in the nvme rings and frees all resources + * in the txcmplq. This function does not issue abort wqes for the IO + * commands in txcmplq, they will just be returned with + * IOERR_SLI_DOWN. This function is invoked with EEH when device's PCI + * slot has been permanently disabled. + **/ +void +lpfc_sli_flush_nvme_rings(struct lpfc_hba *phba) +{ + LIST_HEAD(txcmplq); + struct lpfc_sli_ring *pring; + uint32_t i; + + if (phba->sli_rev < LPFC_SLI_REV4) + return; + + /* Hint to other driver operations that a flush is in progress. */ + spin_lock_irq(&phba->hbalock); + phba->hba_flag |= HBA_NVME_IOQ_FLUSH; + spin_unlock_irq(&phba->hbalock); + + /* Cycle through all NVME rings and complete each IO with + * a local driver reason code. This is a flush so no + * abort exchange to FW. + */ + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + pring = phba->sli4_hba.nvme_wq[i]->pring; + + /* Retrieve everything on the txcmplq */ + spin_lock_irq(&pring->ring_lock); + list_splice_init(&pring->txcmplq, &txcmplq); + pring->txcmplq_cnt = 0; + spin_unlock_irq(&pring->ring_lock); + + /* Flush the txcmpq &&&PAE */ + lpfc_sli_cancel_iocbs(phba, &txcmplq, + IOSTAT_LOCAL_REJECT, + IOERR_SLI_DOWN); + } +} + +/** * lpfc_sli_brdready_s3 - Check for sli3 host ready status * @phba: Pointer to HBA context object. * @mask: Bit mask to be checked. @@ -4069,7 +4231,7 @@ lpfc_sli_brdreset(struct lpfc_hba *phba) /* Initialize relevant SLI info */ for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; + pring = &psli->sli3_ring[i]; pring->flag = 0; pring->sli.sli3.rspidx = 0; pring->sli.sli3.next_cmdidx = 0; @@ -4498,10 +4660,11 @@ static int lpfc_sli4_rb_setup(struct lpfc_hba *phba) { phba->hbq_in_use = 1; - phba->hbqs[0].entry_count = lpfc_hbq_defs[0]->entry_count; + phba->hbqs[LPFC_ELS_HBQ].entry_count = + lpfc_hbq_defs[LPFC_ELS_HBQ]->entry_count; phba->hbq_count = 1; + lpfc_sli_hbqbuf_init_hbqs(phba, LPFC_ELS_HBQ); /* Initially populate or replenish the HBQs */ - lpfc_sli_hbqbuf_init_hbqs(phba, 0); return 0; } @@ -4510,7 +4673,7 @@ lpfc_sli4_rb_setup(struct lpfc_hba *phba) * @phba: Pointer to HBA context object. * @sli_mode: sli mode - 2/3 * - * This function is called by the sli intialization code path + * This function is called by the sli initialization code path * to issue config_port mailbox command. This function restarts the * HBA firmware and issues a config_port mailbox command to configure * the SLI interface in the sli mode specified by sli_mode @@ -4650,11 +4813,11 @@ do_prep_failed: /** - * lpfc_sli_hba_setup - SLI intialization function + * lpfc_sli_hba_setup - SLI initialization function * @phba: Pointer to HBA context object. * - * This function is the main SLI intialization function. This function - * is called by the HBA intialization code, HBA reset code and HBA + * This function is the main SLI initialization function. This function + * is called by the HBA initialization code, HBA reset code and HBA * error attention handler code. Caller is not required to hold any * locks. This function issues config_port mailbox command to configure * the SLI, setup iocb rings and HBQ rings. In the end the function @@ -5107,26 +5270,38 @@ out_free_mboxq: static void lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba) { - int fcp_eqidx; + int qidx; lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM); lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM); - fcp_eqidx = 0; - if (phba->sli4_hba.fcp_cq) { - do { - lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx], - LPFC_QUEUE_REARM); - } while (++fcp_eqidx < phba->cfg_fcp_io_channel); - } + if (phba->sli4_hba.nvmels_cq) + lpfc_sli4_cq_release(phba->sli4_hba.nvmels_cq, + LPFC_QUEUE_REARM); + + if (phba->sli4_hba.fcp_cq) + for (qidx = 0; qidx < phba->cfg_fcp_io_channel; qidx++) + lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[qidx], + LPFC_QUEUE_REARM); + + if (phba->sli4_hba.nvme_cq) + for (qidx = 0; qidx < phba->cfg_nvme_io_channel; qidx++) + lpfc_sli4_cq_release(phba->sli4_hba.nvme_cq[qidx], + LPFC_QUEUE_REARM); if (phba->cfg_fof) lpfc_sli4_cq_release(phba->sli4_hba.oas_cq, LPFC_QUEUE_REARM); - if (phba->sli4_hba.hba_eq) { - for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_io_channel; - fcp_eqidx++) - lpfc_sli4_eq_release(phba->sli4_hba.hba_eq[fcp_eqidx], - LPFC_QUEUE_REARM); + if (phba->sli4_hba.hba_eq) + for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) + lpfc_sli4_eq_release(phba->sli4_hba.hba_eq[qidx], + LPFC_QUEUE_REARM); + + if (phba->nvmet_support) { + for (qidx = 0; qidx < phba->cfg_nvmet_mrq; qidx++) { + lpfc_sli4_cq_release( + phba->sli4_hba.nvmet_cqset[qidx], + LPFC_QUEUE_REARM); + } } if (phba->cfg_fof) @@ -5560,9 +5735,13 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) rsrc_blks->rsrc_size = rsrc_size; list_add_tail(&rsrc_blks->list, ext_blk_list); rsrc_start = rsrc_id; - if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0)) + if ((type == LPFC_RSC_TYPE_FCOE_XRI) && (j == 0)) { phba->sli4_hba.scsi_xri_start = rsrc_start + - lpfc_sli4_get_els_iocb_cnt(phba); + lpfc_sli4_get_iocb_cnt(phba); + phba->sli4_hba.nvme_xri_start = + phba->sli4_hba.scsi_xri_start + + phba->sli4_hba.scsi_xri_max; + } while (rsrc_id < (rsrc_start + rsrc_size)) { ids[j] = rsrc_id; @@ -5578,6 +5757,8 @@ lpfc_sli4_alloc_extent(struct lpfc_hba *phba, uint16_t type) return rc; } + + /** * lpfc_sli4_dealloc_extent - Deallocate an SLI4 resource extent. * @phba: Pointer to HBA context object. @@ -6156,42 +6337,45 @@ lpfc_sli4_get_allocated_extnts(struct lpfc_hba *phba, uint16_t type, } /** - * lpfc_sli4_repost_els_sgl_list - Repsot the els buffers sgl pages as block + * lpfc_sli4_repost_sgl_list - Repsot the buffers sgl pages as block * @phba: pointer to lpfc hba data structure. + * @pring: Pointer to driver SLI ring object. + * @sgl_list: linked link of sgl buffers to post + * @cnt: number of linked list buffers * - * This routine walks the list of els buffers that have been allocated and + * This routine walks the list of buffers that have been allocated and * repost them to the port by using SGL block post. This is needed after a * pci_function_reset/warm_start or start. It attempts to construct blocks - * of els buffer sgls which contains contiguous xris and uses the non-embedded - * SGL block post mailbox commands to post them to the port. For single els + * of buffer sgls which contains contiguous xris and uses the non-embedded + * SGL block post mailbox commands to post them to the port. For single * buffer sgl with non-contiguous xri, if any, it shall use embedded SGL post * mailbox command for posting. * * Returns: 0 = success, non-zero failure. **/ static int -lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba) +lpfc_sli4_repost_sgl_list(struct lpfc_hba *phba, + struct list_head *sgl_list, int cnt) { struct lpfc_sglq *sglq_entry = NULL; struct lpfc_sglq *sglq_entry_next = NULL; struct lpfc_sglq *sglq_entry_first = NULL; - int status, total_cnt, post_cnt = 0, num_posted = 0, block_cnt = 0; + int status, total_cnt; + int post_cnt = 0, num_posted = 0, block_cnt = 0; int last_xritag = NO_XRI; - struct lpfc_sli_ring *pring; LIST_HEAD(prep_sgl_list); LIST_HEAD(blck_sgl_list); LIST_HEAD(allc_sgl_list); LIST_HEAD(post_sgl_list); LIST_HEAD(free_sgl_list); - pring = &phba->sli.ring[LPFC_ELS_RING]; spin_lock_irq(&phba->hbalock); - spin_lock(&pring->ring_lock); - list_splice_init(&phba->sli4_hba.lpfc_sgl_list, &allc_sgl_list); - spin_unlock(&pring->ring_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(sgl_list, &allc_sgl_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irq(&phba->hbalock); - total_cnt = phba->sli4_hba.els_xri_cnt; + total_cnt = cnt; list_for_each_entry_safe(sglq_entry, sglq_entry_next, &allc_sgl_list, list) { list_del_init(&sglq_entry->list); @@ -6220,8 +6404,8 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba) /* keep track of last sgl's xritag */ last_xritag = sglq_entry->sli4_xritag; - /* end of repost sgl list condition for els buffers */ - if (num_posted == phba->sli4_hba.els_xri_cnt) { + /* end of repost sgl list condition for buffers */ + if (num_posted == total_cnt) { if (post_cnt == 0) { list_splice_init(&prep_sgl_list, &blck_sgl_list); @@ -6238,7 +6422,7 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba) /* Failure, put sgl to free list */ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "3159 Failed to post els " + "3159 Failed to post " "sgl, xritag:x%x\n", sglq_entry->sli4_xritag); list_add_tail(&sglq_entry->list, @@ -6252,9 +6436,9 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba) if (post_cnt == 0) continue; - /* post the els buffer list sgls as a block */ - status = lpfc_sli4_post_els_sgl_list(phba, &blck_sgl_list, - post_cnt); + /* post the buffer list sgls as a block */ + status = lpfc_sli4_post_sgl_list(phba, &blck_sgl_list, + post_cnt); if (!status) { /* success, put sgl list to posted sgl list */ @@ -6265,7 +6449,7 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba) struct lpfc_sglq, list); lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "3160 Failed to post els sgl-list, " + "3160 Failed to post sgl-list, " "xritag:x%x-x%x\n", sglq_entry_first->sli4_xritag, (sglq_entry_first->sli4_xritag + @@ -6278,29 +6462,28 @@ lpfc_sli4_repost_els_sgl_list(struct lpfc_hba *phba) if (block_cnt == 0) last_xritag = NO_XRI; - /* reset els sgl post count for next round of posting */ + /* reset sgl post count for next round of posting */ post_cnt = 0; } - /* update the number of XRIs posted for ELS */ - phba->sli4_hba.els_xri_cnt = total_cnt; - /* free the els sgls failed to post */ + /* free the sgls failed to post */ lpfc_free_sgl_list(phba, &free_sgl_list); - /* push els sgls posted to the availble list */ + /* push sgls posted to the available list */ if (!list_empty(&post_sgl_list)) { spin_lock_irq(&phba->hbalock); - spin_lock(&pring->ring_lock); - list_splice_init(&post_sgl_list, - &phba->sli4_hba.lpfc_sgl_list); - spin_unlock(&pring->ring_lock); + spin_lock(&phba->sli4_hba.sgl_list_lock); + list_splice_init(&post_sgl_list, sgl_list); + spin_unlock(&phba->sli4_hba.sgl_list_lock); spin_unlock_irq(&phba->hbalock); } else { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "3161 Failure to post els sgl to port.\n"); + "3161 Failure to post sgl to port.\n"); return -EIO; } - return 0; + + /* return the number of XRIs actually posted */ + return total_cnt; } void @@ -6324,18 +6507,18 @@ lpfc_set_host_data(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) } /** - * lpfc_sli4_hba_setup - SLI4 device intialization PCI function + * lpfc_sli4_hba_setup - SLI4 device initialization PCI function * @phba: Pointer to HBA context object. * - * This function is the main SLI4 device intialization PCI function. This - * function is called by the HBA intialization code, HBA reset code and + * This function is the main SLI4 device initialization PCI function. This + * function is called by the HBA initialization code, HBA reset code and * HBA error attention handler code. Caller is not required to hold any * locks. **/ int lpfc_sli4_hba_setup(struct lpfc_hba *phba) { - int rc; + int rc, i; LPFC_MBOXQ_t *mboxq; struct lpfc_mqe *mqe; uint8_t *vpd; @@ -6344,6 +6527,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) struct Scsi_Host *shost = lpfc_shost_from_vport(phba->pport); struct lpfc_vport *vport = phba->pport; struct lpfc_dmabuf *mp; + struct lpfc_rqb *rqbp; /* Perform a PCI function reset to start from clean */ rc = lpfc_pci_function_reset(phba); @@ -6622,35 +6806,141 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) fc_host_node_name(shost) = wwn_to_u64(vport->fc_nodename.u.wwn); fc_host_port_name(shost) = wwn_to_u64(vport->fc_portname.u.wwn); - /* update host els and scsi xri-sgl sizes and mappings */ - rc = lpfc_sli4_xri_sgl_update(phba); + /* Create all the SLI4 queues */ + rc = lpfc_sli4_queue_create(phba); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3089 Failed to allocate queues\n"); + rc = -ENODEV; + goto out_free_mbox; + } + /* Set up all the queues to the device */ + rc = lpfc_sli4_queue_setup(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "0381 Error %d during queue setup.\n ", rc); + goto out_stop_timers; + } + /* Initialize the driver internal SLI layer lists. */ + lpfc_sli4_setup(phba); + lpfc_sli4_queue_init(phba); + + /* update host els xri-sgl sizes and mappings */ + rc = lpfc_sli4_els_sgl_update(phba); if (unlikely(rc)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "1400 Failed to update xri-sgl size and " "mapping: %d\n", rc); - goto out_free_mbox; + goto out_destroy_queue; } /* register the els sgl pool to the port */ - rc = lpfc_sli4_repost_els_sgl_list(phba); - if (unlikely(rc)) { + rc = lpfc_sli4_repost_sgl_list(phba, &phba->sli4_hba.lpfc_els_sgl_list, + phba->sli4_hba.els_xri_cnt); + if (unlikely(rc < 0)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "0582 Error %d during els sgl post " "operation\n", rc); rc = -ENODEV; - goto out_free_mbox; + goto out_destroy_queue; } + phba->sli4_hba.els_xri_cnt = rc; - /* register the allocated scsi sgl pool to the port */ - rc = lpfc_sli4_repost_scsi_sgl_list(phba); - if (unlikely(rc)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, - "0383 Error %d during scsi sgl post " - "operation\n", rc); - /* Some Scsi buffers were moved to the abort scsi list */ - /* A pci function reset will repost them */ - rc = -ENODEV; - goto out_free_mbox; + if (phba->nvmet_support) { + /* update host nvmet xri-sgl sizes and mappings */ + rc = lpfc_sli4_nvmet_sgl_update(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "6308 Failed to update nvmet-sgl size " + "and mapping: %d\n", rc); + goto out_destroy_queue; + } + + /* register the nvmet sgl pool to the port */ + rc = lpfc_sli4_repost_sgl_list( + phba, + &phba->sli4_hba.lpfc_nvmet_sgl_list, + phba->sli4_hba.nvmet_xri_cnt); + if (unlikely(rc < 0)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "3117 Error %d during nvmet " + "sgl post\n", rc); + rc = -ENODEV; + goto out_destroy_queue; + } + phba->sli4_hba.nvmet_xri_cnt = rc; + lpfc_nvmet_create_targetport(phba); + } else { + /* update host scsi xri-sgl sizes and mappings */ + rc = lpfc_sli4_scsi_sgl_update(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "6309 Failed to update scsi-sgl size " + "and mapping: %d\n", rc); + goto out_destroy_queue; + } + + /* update host nvme xri-sgl sizes and mappings */ + rc = lpfc_sli4_nvme_sgl_update(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "6082 Failed to update nvme-sgl size " + "and mapping: %d\n", rc); + goto out_destroy_queue; + } + } + + if (phba->nvmet_support && phba->cfg_nvmet_mrq) { + + /* Post initial buffers to all RQs created */ + for (i = 0; i < phba->cfg_nvmet_mrq; i++) { + rqbp = phba->sli4_hba.nvmet_mrq_hdr[i]->rqbp; + INIT_LIST_HEAD(&rqbp->rqb_buffer_list); + rqbp->rqb_alloc_buffer = lpfc_sli4_nvmet_alloc; + rqbp->rqb_free_buffer = lpfc_sli4_nvmet_free; + rqbp->entry_count = 256; + rqbp->buffer_count = 0; + + /* Divide by 4 and round down to multiple of 16 */ + rc = (phba->cfg_nvmet_mrq_post >> 2) & 0xfff8; + phba->sli4_hba.nvmet_mrq_hdr[i]->entry_repost = rc; + phba->sli4_hba.nvmet_mrq_data[i]->entry_repost = rc; + + lpfc_post_rq_buffer( + phba, phba->sli4_hba.nvmet_mrq_hdr[i], + phba->sli4_hba.nvmet_mrq_data[i], + phba->cfg_nvmet_mrq_post); + } + } + + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) { + /* register the allocated scsi sgl pool to the port */ + rc = lpfc_sli4_repost_scsi_sgl_list(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "0383 Error %d during scsi sgl post " + "operation\n", rc); + /* Some Scsi buffers were moved to abort scsi list */ + /* A pci function reset will repost them */ + rc = -ENODEV; + goto out_destroy_queue; + } + } + + if ((phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) && + (phba->nvmet_support == 0)) { + + /* register the allocated nvme sgl pool to the port */ + rc = lpfc_repost_nvme_sgl_list(phba); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + "6116 Error %d during nvme sgl post " + "operation\n", rc); + /* Some NVME buffers were moved to abort nvme list */ + /* A pci function reset will repost them */ + rc = -ENODEV; + goto out_destroy_queue; + } } /* Post the rpi header region to the device. */ @@ -6660,24 +6950,46 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) "0393 Error %d during rpi post operation\n", rc); rc = -ENODEV; - goto out_free_mbox; + goto out_destroy_queue; } lpfc_sli4_node_prep(phba); - /* Create all the SLI4 queues */ - rc = lpfc_sli4_queue_create(phba); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3089 Failed to allocate queues\n"); - rc = -ENODEV; - goto out_stop_timers; - } - /* Set up all the queues to the device */ - rc = lpfc_sli4_queue_setup(phba); - if (unlikely(rc)) { - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, - "0381 Error %d during queue setup.\n ", rc); - goto out_destroy_queue; + if (!(phba->hba_flag & HBA_FCOE_MODE)) { + if ((phba->nvmet_support == 0) || (phba->cfg_nvmet_mrq == 1)) { + /* + * The FC Port needs to register FCFI (index 0) + */ + lpfc_reg_fcfi(phba, mboxq); + mboxq->vport = phba->pport; + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + if (rc != MBX_SUCCESS) + goto out_unset_queue; + rc = 0; + phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_fcfi, + &mboxq->u.mqe.un.reg_fcfi); + } else { + /* We are a NVME Target mode with MRQ > 1 */ + + /* First register the FCFI */ + lpfc_reg_fcfi_mrq(phba, mboxq, 0); + mboxq->vport = phba->pport; + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + if (rc != MBX_SUCCESS) + goto out_unset_queue; + rc = 0; + phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_mrq_fcfi, + &mboxq->u.mqe.un.reg_fcfi_mrq); + + /* Next register the MRQs */ + lpfc_reg_fcfi_mrq(phba, mboxq, 1); + mboxq->vport = phba->pport; + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + if (rc != MBX_SUCCESS) + goto out_unset_queue; + rc = 0; + } + /* Check if the port is configured to be disabled */ + lpfc_sli_read_link_ste(phba); } /* Arm the CQs and then EQs on device */ @@ -6731,23 +7043,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) rc = 0; } - if (!(phba->hba_flag & HBA_FCOE_MODE)) { - /* - * The FC Port needs to register FCFI (index 0) - */ - lpfc_reg_fcfi(phba, mboxq); - mboxq->vport = phba->pport; - rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); - if (rc != MBX_SUCCESS) - goto out_unset_queue; - rc = 0; - phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_fcfi, - &mboxq->u.mqe.un.reg_fcfi); - - /* Check if the port is configured to be disabled */ - lpfc_sli_read_link_ste(phba); - } - /* * The port is ready, set the host's link state to LINK_DOWN * in preparation for link interrupts. @@ -6884,7 +7179,7 @@ lpfc_sli4_process_missed_mbox_completions(struct lpfc_hba *phba) /* Find the eq associated with the mcq */ if (phba->sli4_hba.hba_eq) - for (eqidx = 0; eqidx < phba->cfg_fcp_io_channel; eqidx++) + for (eqidx = 0; eqidx < phba->io_channel_irqs; eqidx++) if (phba->sli4_hba.hba_eq[eqidx]->queue_id == phba->sli4_hba.mbx_cq->assoc_qid) { fpeq = phba->sli4_hba.hba_eq[eqidx]; @@ -7243,16 +7538,15 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, = MAILBOX_HBA_EXT_OFFSET; /* Copy the mailbox extension data */ - if (pmbox->in_ext_byte_len && pmbox->context2) { + if (pmbox->in_ext_byte_len && pmbox->context2) lpfc_memcpy_to_slim(phba->MBslimaddr + MAILBOX_HBA_EXT_OFFSET, pmbox->context2, pmbox->in_ext_byte_len); - } - if (mbx->mbxCommand == MBX_CONFIG_PORT) { + if (mbx->mbxCommand == MBX_CONFIG_PORT) /* copy command data into host mbox for cmpl */ - lpfc_sli_pcimem_bcopy(mbx, phba->mbox, MAILBOX_CMD_SIZE); - } + lpfc_sli_pcimem_bcopy(mbx, phba->mbox, + MAILBOX_CMD_SIZE); /* First copy mbox command data to HBA SLIM, skip past first word */ @@ -7266,10 +7560,9 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, writel(ldata, to_slim); readl(to_slim); /* flush */ - if (mbx->mbxCommand == MBX_CONFIG_PORT) { + if (mbx->mbxCommand == MBX_CONFIG_PORT) /* switch over to host mailbox */ psli->sli_flag |= LPFC_SLI_ACTIVE; - } } wmb(); @@ -7368,7 +7661,8 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, if (psli->sli_flag & LPFC_SLI_ACTIVE) { /* copy results back to user */ - lpfc_sli_pcimem_bcopy(phba->mbox, mbx, MAILBOX_CMD_SIZE); + lpfc_sli_pcimem_bcopy(phba->mbox, mbx, + MAILBOX_CMD_SIZE); /* Copy the mailbox extension data */ if (pmbox->out_ext_byte_len && pmbox->context2) { lpfc_sli_pcimem_bcopy(phba->mbox_ext, @@ -7378,7 +7672,7 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, } else { /* First copy command data */ lpfc_memcpy_from_slim(mbx, phba->MBslimaddr, - MAILBOX_CMD_SIZE); + MAILBOX_CMD_SIZE); /* Copy the mailbox extension data */ if (pmbox->out_ext_byte_len && pmbox->context2) { lpfc_memcpy_from_slim(pmbox->context2, @@ -8059,7 +8353,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number, { struct lpfc_iocbq *nextiocb; IOCB_t *iocb; - struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number]; + struct lpfc_sli_ring *pring = &phba->sli.sli3_ring[ring_number]; lockdep_assert_held(&phba->hbalock); @@ -8133,7 +8427,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number, * For FCP commands, we must be in a state where we can process link * attention events. */ - } else if (unlikely(pring->ringno == phba->sli.fcp_ring && + } else if (unlikely(pring->ringno == LPFC_FCP_RING && !(phba->sli.sli_flag & LPFC_PROCESS_LA))) { goto iocb_busy; } @@ -8870,9 +9164,21 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, union lpfc_wqe *wqe; union lpfc_wqe128 wqe128; struct lpfc_queue *wq; - struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number]; + struct lpfc_sli_ring *pring; - lockdep_assert_held(&phba->hbalock); + /* Get the WQ */ + if ((piocb->iocb_flag & LPFC_IO_FCP) || + (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { + if (!phba->cfg_fof || (!(piocb->iocb_flag & LPFC_IO_OAS))) + wq = phba->sli4_hba.fcp_wq[piocb->hba_wqidx]; + else + wq = phba->sli4_hba.oas_wq; + } else { + wq = phba->sli4_hba.els_wq; + } + + /* Get corresponding ring */ + pring = wq->pring; /* * The WQE can be either 64 or 128 bytes, @@ -8880,6 +9186,8 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, */ wqe = (union lpfc_wqe *)&wqe128; + lockdep_assert_held(&phba->hbalock); + if (piocb->sli4_xritag == NO_XRI) { if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN || piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN) @@ -8894,7 +9202,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, return IOCB_BUSY; } } else { - sglq = __lpfc_sli_get_sglq(phba, piocb); + sglq = __lpfc_sli_get_els_sglq(phba, piocb); if (!sglq) { if (!(flag & SLI_IOCB_RET_IOCB)) { __lpfc_sli_ringtx_put(phba, @@ -8906,10 +9214,10 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, } } } - } else if (piocb->iocb_flag & LPFC_IO_FCP) { + } else if (piocb->iocb_flag & LPFC_IO_FCP) /* These IO's already have an XRI and a mapped sgl. */ sglq = NULL; - } else { + else { /* * This is a continuation of a commandi,(CX) so this * sglq is on the active list @@ -8929,21 +9237,8 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, if (lpfc_sli4_iocb2wqe(phba, piocb, wqe)) return IOCB_ERROR; - if ((piocb->iocb_flag & LPFC_IO_FCP) || - (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) { - if (!phba->cfg_fof || (!(piocb->iocb_flag & LPFC_IO_OAS))) { - wq = phba->sli4_hba.fcp_wq[piocb->fcp_wqidx]; - } else { - wq = phba->sli4_hba.oas_wq; - } - if (lpfc_sli4_wq_put(wq, wqe)) - return IOCB_ERROR; - } else { - if (unlikely(!phba->sli4_hba.els_wq)) - return IOCB_ERROR; - if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, wqe)) - return IOCB_ERROR; - } + if (lpfc_sli4_wq_put(wq, wqe)) + return IOCB_ERROR; lpfc_sli_ringtxcmpl_put(phba, pring, piocb); return 0; @@ -9001,46 +9296,44 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) } /** - * lpfc_sli_calc_ring - Calculates which ring to use + * lpfc_sli4_calc_ring - Calculates which ring to use * @phba: Pointer to HBA context object. - * @ring_number: Initial ring * @piocb: Pointer to command iocb. * - * For SLI4, FCP IO can deferred to one fo many WQs, based on - * fcp_wqidx, thus we need to calculate the corresponding ring. + * For SLI4 only, FCP IO can deferred to one fo many WQs, based on + * hba_wqidx, thus we need to calculate the corresponding ring. * Since ABORTS must go on the same WQ of the command they are - * aborting, we use command's fcp_wqidx. + * aborting, we use command's hba_wqidx. */ -static int -lpfc_sli_calc_ring(struct lpfc_hba *phba, uint32_t ring_number, - struct lpfc_iocbq *piocb) +struct lpfc_sli_ring * +lpfc_sli4_calc_ring(struct lpfc_hba *phba, struct lpfc_iocbq *piocb) { - if (phba->sli_rev < LPFC_SLI_REV4) - return ring_number; - - if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { + if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { if (!(phba->cfg_fof) || - (!(piocb->iocb_flag & LPFC_IO_FOF))) { + (!(piocb->iocb_flag & LPFC_IO_FOF))) { if (unlikely(!phba->sli4_hba.fcp_wq)) - return LPFC_HBA_ERROR; + return NULL; /* - * for abort iocb fcp_wqidx should already + * for abort iocb hba_wqidx should already * be setup based on what work queue we used. */ if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) - piocb->fcp_wqidx = + piocb->hba_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba, piocb->context1); - ring_number = MAX_SLI3_CONFIGURED_RINGS + - piocb->fcp_wqidx; + return phba->sli4_hba.fcp_wq[piocb->hba_wqidx]->pring; } else { if (unlikely(!phba->sli4_hba.oas_wq)) - return LPFC_HBA_ERROR; - piocb->fcp_wqidx = 0; - ring_number = LPFC_FCP_OAS_RING; + return NULL; + piocb->hba_wqidx = 0; + return phba->sli4_hba.oas_wq->pring; } + } else { + if (unlikely(!phba->sli4_hba.els_wq)) + return NULL; + piocb->hba_wqidx = 0; + return phba->sli4_hba.els_wq->pring; } - return ring_number; } /** @@ -9060,7 +9353,7 @@ int lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, struct lpfc_iocbq *piocb, uint32_t flag) { - struct lpfc_fcp_eq_hdl *fcp_eq_hdl; + struct lpfc_hba_eq_hdl *hba_eq_hdl; struct lpfc_sli_ring *pring; struct lpfc_queue *fpeq; struct lpfc_eqe *eqe; @@ -9068,21 +9361,19 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, int rc, idx; if (phba->sli_rev == LPFC_SLI_REV4) { - ring_number = lpfc_sli_calc_ring(phba, ring_number, piocb); - if (unlikely(ring_number == LPFC_HBA_ERROR)) + pring = lpfc_sli4_calc_ring(phba, piocb); + if (unlikely(pring == NULL)) return IOCB_ERROR; - idx = piocb->fcp_wqidx; - pring = &phba->sli.ring[ring_number]; spin_lock_irqsave(&pring->ring_lock, iflags); rc = __lpfc_sli_issue_iocb(phba, ring_number, piocb, flag); spin_unlock_irqrestore(&pring->ring_lock, iflags); if (lpfc_fcp_look_ahead && (piocb->iocb_flag & LPFC_IO_FCP)) { - fcp_eq_hdl = &phba->sli4_hba.fcp_eq_hdl[idx]; + idx = piocb->hba_wqidx; + hba_eq_hdl = &phba->sli4_hba.hba_eq_hdl[idx]; - if (atomic_dec_and_test(&fcp_eq_hdl-> - fcp_eq_in_use)) { + if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use)) { /* Get associated EQ with this index */ fpeq = phba->sli4_hba.hba_eq[idx]; @@ -9103,7 +9394,7 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_REARM); } - atomic_inc(&fcp_eq_hdl->fcp_eq_in_use); + atomic_inc(&hba_eq_hdl->hba_eq_in_use); } } else { /* For now, SLI2/3 will still use hbalock */ @@ -9123,7 +9414,7 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, uint32_t ring_number, * only when driver needs to support target mode functionality * or IP over FC functionalities. * - * This function is called with no lock held. + * This function is called with no lock held. SLI3 only. **/ static int lpfc_extra_ring_setup( struct lpfc_hba *phba) @@ -9136,14 +9427,14 @@ lpfc_extra_ring_setup( struct lpfc_hba *phba) /* Adjust cmd/rsp ring iocb entries more evenly */ /* Take some away from the FCP ring */ - pring = &psli->ring[psli->fcp_ring]; + pring = &psli->sli3_ring[LPFC_FCP_RING]; pring->sli.sli3.numCiocb -= SLI2_IOCB_CMD_R1XTRA_ENTRIES; pring->sli.sli3.numRiocb -= SLI2_IOCB_RSP_R1XTRA_ENTRIES; pring->sli.sli3.numCiocb -= SLI2_IOCB_CMD_R3XTRA_ENTRIES; pring->sli.sli3.numRiocb -= SLI2_IOCB_RSP_R3XTRA_ENTRIES; /* and give them to the extra ring */ - pring = &psli->ring[psli->extra_ring]; + pring = &psli->sli3_ring[LPFC_EXTRA_RING]; pring->sli.sli3.numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES; pring->sli.sli3.numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES; @@ -9328,7 +9619,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba, /** - * lpfc_sli_setup - SLI ring setup function + * lpfc_sli4_setup - SLI ring setup function * @phba: Pointer to HBA context object. * * lpfc_sli_setup sets up rings of the SLI interface with @@ -9339,6 +9630,51 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba, * This function always returns 0. **/ int +lpfc_sli4_setup(struct lpfc_hba *phba) +{ + struct lpfc_sli_ring *pring; + + pring = phba->sli4_hba.els_wq->pring; + pring->num_mask = LPFC_MAX_RING_MASK; + pring->prt[0].profile = 0; /* Mask 0 */ + pring->prt[0].rctl = FC_RCTL_ELS_REQ; + pring->prt[0].type = FC_TYPE_ELS; + pring->prt[0].lpfc_sli_rcv_unsol_event = + lpfc_els_unsol_event; + pring->prt[1].profile = 0; /* Mask 1 */ + pring->prt[1].rctl = FC_RCTL_ELS_REP; + pring->prt[1].type = FC_TYPE_ELS; + pring->prt[1].lpfc_sli_rcv_unsol_event = + lpfc_els_unsol_event; + pring->prt[2].profile = 0; /* Mask 2 */ + /* NameServer Inquiry */ + pring->prt[2].rctl = FC_RCTL_DD_UNSOL_CTL; + /* NameServer */ + pring->prt[2].type = FC_TYPE_CT; + pring->prt[2].lpfc_sli_rcv_unsol_event = + lpfc_ct_unsol_event; + pring->prt[3].profile = 0; /* Mask 3 */ + /* NameServer response */ + pring->prt[3].rctl = FC_RCTL_DD_SOL_CTL; + /* NameServer */ + pring->prt[3].type = FC_TYPE_CT; + pring->prt[3].lpfc_sli_rcv_unsol_event = + lpfc_ct_unsol_event; + return 0; +} + +/** + * lpfc_sli_setup - SLI ring setup function + * @phba: Pointer to HBA context object. + * + * lpfc_sli_setup sets up rings of the SLI interface with + * number of iocbs per ring and iotags. This function is + * called while driver attach to the HBA and before the + * interrupts are enabled. So there is no need for locking. + * + * This function always returns 0. SLI3 only. + **/ +int lpfc_sli_setup(struct lpfc_hba *phba) { int i, totiocbsize = 0; @@ -9346,19 +9682,14 @@ lpfc_sli_setup(struct lpfc_hba *phba) struct lpfc_sli_ring *pring; psli->num_rings = MAX_SLI3_CONFIGURED_RINGS; - if (phba->sli_rev == LPFC_SLI_REV4) - psli->num_rings += phba->cfg_fcp_io_channel; psli->sli_flag = 0; - psli->fcp_ring = LPFC_FCP_RING; - psli->next_ring = LPFC_FCP_NEXT_RING; - psli->extra_ring = LPFC_EXTRA_RING; psli->iocbq_lookup = NULL; psli->iocbq_lookup_len = 0; psli->last_iotag = 0; for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; + pring = &psli->sli3_ring[i]; switch (i) { case LPFC_FCP_RING: /* ring 0 - FCP */ /* numCiocb and numRiocb are used in config_port */ @@ -9457,18 +9788,90 @@ lpfc_sli_setup(struct lpfc_hba *phba) } /** - * lpfc_sli_queue_setup - Queue initialization function + * lpfc_sli4_queue_init - Queue initialization function * @phba: Pointer to HBA context object. * - * lpfc_sli_queue_setup sets up mailbox queues and iocb queues for each + * lpfc_sli4_queue_init sets up mailbox queues and iocb queues for each * ring. This function also initializes ring indices of each ring. * This function is called during the initialization of the SLI * interface of an HBA. * This function is called with no lock held and always returns * 1. **/ -int -lpfc_sli_queue_setup(struct lpfc_hba *phba) +void +lpfc_sli4_queue_init(struct lpfc_hba *phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + int i; + + psli = &phba->sli; + spin_lock_irq(&phba->hbalock); + INIT_LIST_HEAD(&psli->mboxq); + INIT_LIST_HEAD(&psli->mboxq_cmpl); + /* Initialize list headers for txq and txcmplq as double linked lists */ + for (i = 0; i < phba->cfg_fcp_io_channel; i++) { + pring = phba->sli4_hba.fcp_wq[i]->pring; + pring->flag = 0; + pring->ringno = LPFC_FCP_RING; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + spin_lock_init(&pring->ring_lock); + } + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + pring = phba->sli4_hba.nvme_wq[i]->pring; + pring->flag = 0; + pring->ringno = LPFC_FCP_RING; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + spin_lock_init(&pring->ring_lock); + } + pring = phba->sli4_hba.els_wq->pring; + pring->flag = 0; + pring->ringno = LPFC_ELS_RING; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + spin_lock_init(&pring->ring_lock); + + if (phba->cfg_nvme_io_channel) { + pring = phba->sli4_hba.nvmels_wq->pring; + pring->flag = 0; + pring->ringno = LPFC_ELS_RING; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + spin_lock_init(&pring->ring_lock); + } + + if (phba->cfg_fof) { + pring = phba->sli4_hba.oas_wq->pring; + pring->flag = 0; + pring->ringno = LPFC_FCP_RING; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + spin_lock_init(&pring->ring_lock); + } + + spin_unlock_irq(&phba->hbalock); +} + +/** + * lpfc_sli_queue_init - Queue initialization function + * @phba: Pointer to HBA context object. + * + * lpfc_sli_queue_init sets up mailbox queues and iocb queues for each + * ring. This function also initializes ring indices of each ring. + * This function is called during the initialization of the SLI + * interface of an HBA. + * This function is called with no lock held and always returns + * 1. + **/ +void +lpfc_sli_queue_init(struct lpfc_hba *phba) { struct lpfc_sli *psli; struct lpfc_sli_ring *pring; @@ -9480,21 +9883,20 @@ lpfc_sli_queue_setup(struct lpfc_hba *phba) INIT_LIST_HEAD(&psli->mboxq_cmpl); /* Initialize list headers for txq and txcmplq as double linked lists */ for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; + pring = &psli->sli3_ring[i]; pring->ringno = i; pring->sli.sli3.next_cmdidx = 0; pring->sli.sli3.local_getidx = 0; pring->sli.sli3.cmdidx = 0; - pring->flag = 0; - INIT_LIST_HEAD(&pring->txq); - INIT_LIST_HEAD(&pring->txcmplq); INIT_LIST_HEAD(&pring->iocb_continueq); INIT_LIST_HEAD(&pring->iocb_continue_saveq); INIT_LIST_HEAD(&pring->postbufq); + pring->flag = 0; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); spin_lock_init(&pring->ring_lock); } spin_unlock_irq(&phba->hbalock); - return 1; } /** @@ -9566,6 +9968,7 @@ lpfc_sli_host_down(struct lpfc_vport *vport) LIST_HEAD(completions); struct lpfc_hba *phba = vport->phba; struct lpfc_sli *psli = &phba->sli; + struct lpfc_queue *qp = NULL; struct lpfc_sli_ring *pring; struct lpfc_iocbq *iocb, *next_iocb; int i; @@ -9575,36 +9978,64 @@ lpfc_sli_host_down(struct lpfc_vport *vport) lpfc_cleanup_discovery_resources(vport); spin_lock_irqsave(&phba->hbalock, flags); - for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; - prev_pring_flag = pring->flag; - /* Only slow rings */ - if (pring->ringno == LPFC_ELS_RING) { - pring->flag |= LPFC_DEFERRED_RING_EVENT; - /* Set the lpfc data pending flag */ - set_bit(LPFC_DATA_READY, &phba->data_flags); - } - /* - * Error everything on the txq since these iocbs have not been - * given to the FW yet. - */ - list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { - if (iocb->vport != vport) - continue; - list_move_tail(&iocb->list, &completions); - } - /* Next issue ABTS for everything on the txcmplq */ - list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, - list) { - if (iocb->vport != vport) + /* + * Error everything on the txq since these iocbs + * have not been given to the FW yet. + * Also issue ABTS for everything on the txcmplq + */ + if (phba->sli_rev != LPFC_SLI_REV4) { + for (i = 0; i < psli->num_rings; i++) { + pring = &psli->sli3_ring[i]; + prev_pring_flag = pring->flag; + /* Only slow rings */ + if (pring->ringno == LPFC_ELS_RING) { + pring->flag |= LPFC_DEFERRED_RING_EVENT; + /* Set the lpfc data pending flag */ + set_bit(LPFC_DATA_READY, &phba->data_flags); + } + list_for_each_entry_safe(iocb, next_iocb, + &pring->txq, list) { + if (iocb->vport != vport) + continue; + list_move_tail(&iocb->list, &completions); + } + list_for_each_entry_safe(iocb, next_iocb, + &pring->txcmplq, list) { + if (iocb->vport != vport) + continue; + lpfc_sli_issue_abort_iotag(phba, pring, iocb); + } + pring->flag = prev_pring_flag; + } + } else { + list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) { + pring = qp->pring; + if (!pring) continue; - lpfc_sli_issue_abort_iotag(phba, pring, iocb); + if (pring == phba->sli4_hba.els_wq->pring) { + pring->flag |= LPFC_DEFERRED_RING_EVENT; + /* Set the lpfc data pending flag */ + set_bit(LPFC_DATA_READY, &phba->data_flags); + } + prev_pring_flag = pring->flag; + spin_lock_irq(&pring->ring_lock); + list_for_each_entry_safe(iocb, next_iocb, + &pring->txq, list) { + if (iocb->vport != vport) + continue; + list_move_tail(&iocb->list, &completions); + } + spin_unlock_irq(&pring->ring_lock); + list_for_each_entry_safe(iocb, next_iocb, + &pring->txcmplq, list) { + if (iocb->vport != vport) + continue; + lpfc_sli_issue_abort_iotag(phba, pring, iocb); + } + pring->flag = prev_pring_flag; } - - pring->flag = prev_pring_flag; } - spin_unlock_irqrestore(&phba->hbalock, flags); /* Cancel all the IOCBs from the completions list */ @@ -9633,6 +10064,7 @@ lpfc_sli_hba_down(struct lpfc_hba *phba) { LIST_HEAD(completions); struct lpfc_sli *psli = &phba->sli; + struct lpfc_queue *qp = NULL; struct lpfc_sli_ring *pring; struct lpfc_dmabuf *buf_ptr; unsigned long flags = 0; @@ -9646,20 +10078,36 @@ lpfc_sli_hba_down(struct lpfc_hba *phba) lpfc_fabric_abort_hba(phba); spin_lock_irqsave(&phba->hbalock, flags); - for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; - /* Only slow rings */ - if (pring->ringno == LPFC_ELS_RING) { - pring->flag |= LPFC_DEFERRED_RING_EVENT; - /* Set the lpfc data pending flag */ - set_bit(LPFC_DATA_READY, &phba->data_flags); - } - /* - * Error everything on the txq since these iocbs have not been - * given to the FW yet. - */ - list_splice_init(&pring->txq, &completions); + /* + * Error everything on the txq since these iocbs + * have not been given to the FW yet. + */ + if (phba->sli_rev != LPFC_SLI_REV4) { + for (i = 0; i < psli->num_rings; i++) { + pring = &psli->sli3_ring[i]; + /* Only slow rings */ + if (pring->ringno == LPFC_ELS_RING) { + pring->flag |= LPFC_DEFERRED_RING_EVENT; + /* Set the lpfc data pending flag */ + set_bit(LPFC_DATA_READY, &phba->data_flags); + } + list_splice_init(&pring->txq, &completions); + } + } else { + list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) { + pring = qp->pring; + if (!pring) + continue; + spin_lock_irq(&pring->ring_lock); + list_splice_init(&pring->txq, &completions); + spin_unlock_irq(&pring->ring_lock); + if (pring == phba->sli4_hba.els_wq->pring) { + pring->flag |= LPFC_DEFERRED_RING_EVENT; + /* Set the lpfc data pending flag */ + set_bit(LPFC_DATA_READY, &phba->data_flags); + } + } } spin_unlock_irqrestore(&phba->hbalock, flags); @@ -9986,7 +10434,6 @@ lpfc_sli_abort_iotag_issue(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_iocbq *abtsiocbp; IOCB_t *icmd = NULL; IOCB_t *iabt = NULL; - int ring_number; int retval; unsigned long iflags; @@ -10026,7 +10473,7 @@ lpfc_sli_abort_iotag_issue(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, iabt->ulpClass = icmd->ulpClass; /* ABTS WQE must go to the same WQ as the WQE to be aborted */ - abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx; + abtsiocbp->hba_wqidx = cmdiocb->hba_wqidx; if (cmdiocb->iocb_flag & LPFC_IO_FCP) abtsiocbp->iocb_flag |= LPFC_USE_FCPWQIDX; if (cmdiocb->iocb_flag & LPFC_IO_FOF) @@ -10048,11 +10495,9 @@ lpfc_sli_abort_iotag_issue(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, abtsiocbp->iotag); if (phba->sli_rev == LPFC_SLI_REV4) { - ring_number = - lpfc_sli_calc_ring(phba, pring->ringno, abtsiocbp); - if (unlikely(ring_number == LPFC_HBA_ERROR)) + pring = lpfc_sli4_calc_ring(phba, abtsiocbp); + if (unlikely(pring == NULL)) return 0; - pring = &phba->sli.ring[ring_number]; /* Note: both hbalock and ring_lock need to be set here */ spin_lock_irqsave(&pring->ring_lock, iflags); retval = __lpfc_sli_issue_iocb(phba, pring->ringno, @@ -10134,6 +10579,108 @@ abort_iotag_exit: } /** + * lpfc_sli4_abort_nvme_io - Issue abort for a command iocb + * @phba: Pointer to HBA context object. + * @pring: Pointer to driver SLI ring object. + * @cmdiocb: Pointer to driver command iocb object. + * + * This function issues an abort iocb for the provided command iocb down to + * the port. Other than the case the outstanding command iocb is an abort + * request, this function issues abort out unconditionally. This function is + * called with hbalock held. The function returns 0 when it fails due to + * memory allocation failure or when the command iocb is an abort request. + **/ +static int +lpfc_sli4_abort_nvme_io(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_vport *vport = cmdiocb->vport; + struct lpfc_iocbq *abtsiocbp; + union lpfc_wqe *abts_wqe; + int retval; + + /* + * There are certain command types we don't want to abort. And we + * don't want to abort commands that are already in the process of + * being aborted. + */ + if (cmdiocb->iocb.ulpCommand == CMD_ABORT_XRI_CN || + cmdiocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN || + (cmdiocb->iocb_flag & LPFC_DRIVER_ABORTED) != 0) + return 0; + + /* issue ABTS for this io based on iotag */ + abtsiocbp = __lpfc_sli_get_iocbq(phba); + if (abtsiocbp == NULL) + return 0; + + /* This signals the response to set the correct status + * before calling the completion handler + */ + cmdiocb->iocb_flag |= LPFC_DRIVER_ABORTED; + + /* Complete prepping the abort wqe and issue to the FW. */ + abts_wqe = &abtsiocbp->wqe; + bf_set(abort_cmd_ia, &abts_wqe->abort_cmd, 0); + bf_set(abort_cmd_criteria, &abts_wqe->abort_cmd, T_XRI_TAG); + + /* Explicitly set reserved fields to zero.*/ + abts_wqe->abort_cmd.rsrvd4 = 0; + abts_wqe->abort_cmd.rsrvd5 = 0; + + /* WQE Common - word 6. Context is XRI tag. Set 0. */ + bf_set(wqe_xri_tag, &abts_wqe->abort_cmd.wqe_com, 0); + bf_set(wqe_ctxt_tag, &abts_wqe->abort_cmd.wqe_com, 0); + + /* word 7 */ + bf_set(wqe_ct, &abts_wqe->abort_cmd.wqe_com, 0); + bf_set(wqe_cmnd, &abts_wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX); + bf_set(wqe_class, &abts_wqe->abort_cmd.wqe_com, + cmdiocb->iocb.ulpClass); + + /* word 8 - tell the FW to abort the IO associated with this + * outstanding exchange ID. + */ + abts_wqe->abort_cmd.wqe_com.abort_tag = cmdiocb->sli4_xritag; + + /* word 9 - this is the iotag for the abts_wqe completion. */ + bf_set(wqe_reqtag, &abts_wqe->abort_cmd.wqe_com, + abtsiocbp->iotag); + + /* word 10 */ + bf_set(wqe_wqid, &abts_wqe->abort_cmd.wqe_com, cmdiocb->hba_wqidx); + bf_set(wqe_qosd, &abts_wqe->abort_cmd.wqe_com, 1); + bf_set(wqe_lenloc, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_LENLOC_NONE); + + /* word 11 */ + bf_set(wqe_cmd_type, &abts_wqe->abort_cmd.wqe_com, OTHER_COMMAND); + bf_set(wqe_wqec, &abts_wqe->abort_cmd.wqe_com, 1); + bf_set(wqe_cqid, &abts_wqe->abort_cmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); + + /* ABTS WQE must go to the same WQ as the WQE to be aborted */ + abtsiocbp->iocb_flag |= LPFC_IO_NVME; + abtsiocbp->vport = vport; + abtsiocbp->wqe_cmpl = lpfc_nvme_abort_fcreq_cmpl; + retval = lpfc_sli4_issue_wqe(phba, LPFC_FCP_RING, abtsiocbp); + if (retval == IOCB_ERROR) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6147 Failed abts issue_wqe with status x%x " + "for oxid x%x\n", + retval, cmdiocb->sli4_xritag); + lpfc_sli_release_iocbq(phba, abtsiocbp); + return retval; + } + + lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME, + "6148 Drv Abort NVME Request Issued for " + "ox_id x%x on reqtag x%x\n", + cmdiocb->sli4_xritag, + abtsiocbp->iotag); + + return retval; +} + +/** * lpfc_sli_hba_iocb_abort - Abort all iocbs to an hba. * @phba: pointer to lpfc HBA data structure. * @@ -10144,10 +10691,20 @@ lpfc_sli_hba_iocb_abort(struct lpfc_hba *phba) { struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring; + struct lpfc_queue *qp = NULL; int i; - for (i = 0; i < psli->num_rings; i++) { - pring = &psli->ring[i]; + if (phba->sli_rev != LPFC_SLI_REV4) { + for (i = 0; i < psli->num_rings; i++) { + pring = &psli->sli3_ring[i]; + lpfc_sli_abort_iocb_ring(phba, pring); + } + return; + } + list_for_each_entry(qp, &phba->sli4_hba.lpfc_wq_list, wq_list) { + pring = qp->pring; + if (!pring) + continue; lpfc_sli_abort_iocb_ring(phba, pring); } } @@ -10351,7 +10908,7 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, abtsiocb->vport = vport; /* ABTS WQE must go to the same WQ as the WQE to be aborted */ - abtsiocb->fcp_wqidx = iocbq->fcp_wqidx; + abtsiocb->hba_wqidx = iocbq->hba_wqidx; if (iocbq->iocb_flag & LPFC_IO_FCP) abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX; if (iocbq->iocb_flag & LPFC_IO_FOF) @@ -10411,7 +10968,6 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, int sum, i, ret_val; unsigned long iflags; struct lpfc_sli_ring *pring_s4; - uint32_t ring_number; spin_lock_irq(&phba->hbalock); @@ -10454,7 +11010,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, abtsiocbq->vport = vport; /* ABTS WQE must go to the same WQ as the WQE to be aborted */ - abtsiocbq->fcp_wqidx = iocbq->fcp_wqidx; + abtsiocbq->hba_wqidx = iocbq->hba_wqidx; if (iocbq->iocb_flag & LPFC_IO_FCP) abtsiocbq->iocb_flag |= LPFC_USE_FCPWQIDX; if (iocbq->iocb_flag & LPFC_IO_FOF) @@ -10479,9 +11035,9 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, iocbq->iocb_flag |= LPFC_DRIVER_ABORTED; if (phba->sli_rev == LPFC_SLI_REV4) { - ring_number = MAX_SLI3_CONFIGURED_RINGS + - iocbq->fcp_wqidx; - pring_s4 = &phba->sli.ring[ring_number]; + pring_s4 = lpfc_sli4_calc_ring(phba, iocbq); + if (pring_s4 == NULL) + continue; /* Note: both hbalock and ring_lock must be set here */ spin_lock_irqsave(&pring_s4->ring_lock, iflags); ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, @@ -10643,10 +11199,14 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba, struct lpfc_iocbq *iocb; int txq_cnt = 0; int txcmplq_cnt = 0; - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; unsigned long iflags; bool iocb_completed = true; + if (phba->sli_rev >= LPFC_SLI_REV4) + pring = lpfc_sli4_calc_ring(phba, piocb); + else + pring = &phba->sli.sli3_ring[ring_number]; /* * If the caller has provided a response iocbq buffer, then context2 * is NULL or its an error. @@ -11441,6 +12001,7 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id) uint32_t ha_copy; unsigned long status; unsigned long iflag; + struct lpfc_sli_ring *pring; /* Get the driver's phba structure from the dev_id and * assume the HBA is not interrupting. @@ -11485,10 +12046,9 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id) status = (ha_copy & (HA_RXMASK << (4*LPFC_FCP_RING))); status >>= (4*LPFC_FCP_RING); + pring = &phba->sli.sli3_ring[LPFC_FCP_RING]; if (status & HA_RXMASK) - lpfc_sli_handle_fast_ring_event(phba, - &phba->sli.ring[LPFC_FCP_RING], - status); + lpfc_sli_handle_fast_ring_event(phba, pring, status); if (phba->cfg_multi_ring_support == 2) { /* @@ -11499,7 +12059,7 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id) status >>= (4*LPFC_EXTRA_RING); if (status & HA_RXMASK) { lpfc_sli_handle_fast_ring_event(phba, - &phba->sli.ring[LPFC_EXTRA_RING], + &phba->sli.sli3_ring[LPFC_EXTRA_RING], status); } } @@ -11812,11 +12372,13 @@ static struct lpfc_iocbq * lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba, struct lpfc_iocbq *irspiocbq) { - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; struct lpfc_iocbq *cmdiocbq; struct lpfc_wcqe_complete *wcqe; unsigned long iflags; + pring = lpfc_phba_elsring(phba); + wcqe = &irspiocbq->cq_event.cqe.wcqe_cmpl; spin_lock_irqsave(&pring->ring_lock, iflags); pring->stats.iocb_event++; @@ -12052,8 +12614,6 @@ lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, txq_cnt++; if (!list_empty(&pring->txcmplq)) txcmplq_cnt++; - if (!list_empty(&phba->sli.ring[LPFC_FCP_RING].txcmplq)) - fcp_txcmplq_cnt++; lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0387 NO IOCBQ data: txq_cnt=%d iocb_cnt=%d " "fcp_txcmplq_cnt=%d, els_txcmplq_cnt=%d\n", @@ -12079,7 +12639,7 @@ lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, * @phba: Pointer to HBA context object. * @wcqe: Pointer to work-queue completion queue entry. * - * This routine handles slow-path WQ entry comsumed event by invoking the + * This routine handles slow-path WQ entry consumed event by invoking the * proper WQ release routine to the slow-path WQ. **/ static void @@ -12172,6 +12732,7 @@ static bool lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe) { bool workposted = false; + struct fc_frame_header *fc_hdr; struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq; struct lpfc_queue *drq = phba->sli4_hba.dat_rq; struct hbq_dmabuf *dma_buf; @@ -12206,6 +12767,10 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe) } hrq->RQ_rcv_buf++; memcpy(&dma_buf->cq_event.cqe.rcqe_cmpl, rcqe, sizeof(*rcqe)); + + /* If a NVME LS event (type 0x28), treat it as Fast path */ + fc_hdr = (struct fc_frame_header *)dma_buf->hbuf.virt; + /* save off the frame for the word thread to process */ list_add_tail(&dma_buf->cq_event.list, &phba->sli4_hba.sp_queue_event); @@ -12324,6 +12889,9 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, return; } + /* Save EQ associated with this CQ */ + cq->assoc_qp = speq; + /* Process all the entries to the CQ */ switch (cq->type) { case LPFC_MCQ: @@ -12336,8 +12904,9 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, break; case LPFC_WCQ: while ((cqe = lpfc_sli4_cq_get(cq))) { - if (cq->subtype == LPFC_FCP) - workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, + if ((cq->subtype == LPFC_FCP) || + (cq->subtype == LPFC_NVME)) + workposted |= lpfc_sli4_fp_handle_cqe(phba, cq, cqe); else workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, @@ -12424,7 +12993,23 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, bf_get(lpfc_wcqe_c_request_tag, wcqe)); return; } - if (unlikely(!cmdiocbq->iocb_cmpl)) { + + if (cq->assoc_qp) + cmdiocbq->isr_timestamp = + cq->assoc_qp->isr_timestamp; + + if (cmdiocbq->iocb_cmpl == NULL) { + if (cmdiocbq->wqe_cmpl) { + if (cmdiocbq->iocb_flag & LPFC_DRIVER_ABORTED) { + spin_lock_irqsave(&phba->hbalock, iflags); + cmdiocbq->iocb_flag &= ~LPFC_DRIVER_ABORTED; + spin_unlock_irqrestore(&phba->hbalock, iflags); + } + + /* Pass the cmd_iocb and the wcqe to the upper layer */ + (cmdiocbq->wqe_cmpl)(phba, cmdiocbq, wcqe); + return; + } lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, "0375 FCP cmdiocb not callback function " "iotag: (%d)\n", @@ -12451,7 +13036,7 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, * @cq: Pointer to completion queue. * @wcqe: Pointer to work-queue completion queue entry. * - * This routine handles an fast-path WQ entry comsumed event by invoking the + * This routine handles an fast-path WQ entry consumed event by invoking the * proper WQ release routine to the slow-path WQ. **/ static void @@ -12460,12 +13045,12 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, { struct lpfc_queue *childwq; bool wqid_matched = false; - uint16_t fcp_wqid; + uint16_t hba_wqid; /* Check for fast-path FCP work queue release */ - fcp_wqid = bf_get(lpfc_wcqe_r_wq_id, wcqe); + hba_wqid = bf_get(lpfc_wcqe_r_wq_id, wcqe); list_for_each_entry(childwq, &cq->child_list, list) { - if (childwq->queue_id == fcp_wqid) { + if (childwq->queue_id == hba_wqid) { lpfc_sli4_wq_release(childwq, bf_get(lpfc_wcqe_r_wqe_index, wcqe)); wqid_matched = true; @@ -12476,11 +13061,108 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, if (wqid_matched != true) lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, "2580 Fast-path wqe consume event carries " - "miss-matched qid: wcqe-qid=x%x\n", fcp_wqid); + "miss-matched qid: wcqe-qid=x%x\n", hba_wqid); +} + +/** + * lpfc_sli4_nvmet_handle_rcqe - Process a receive-queue completion queue entry + * @phba: Pointer to HBA context object. + * @rcqe: Pointer to receive-queue completion queue entry. + * + * This routine process a receive-queue completion queue entry. + * + * Return: true if work posted to worker thread, otherwise false. + **/ +static bool +lpfc_sli4_nvmet_handle_rcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, + struct lpfc_rcqe *rcqe) +{ + bool workposted = false; + struct lpfc_queue *hrq; + struct lpfc_queue *drq; + struct rqb_dmabuf *dma_buf; + struct fc_frame_header *fc_hdr; + uint32_t status, rq_id; + unsigned long iflags; + uint32_t fctl, idx; + + if ((phba->nvmet_support == 0) || + (phba->sli4_hba.nvmet_cqset == NULL)) + return workposted; + + idx = cq->queue_id - phba->sli4_hba.nvmet_cqset[0]->queue_id; + hrq = phba->sli4_hba.nvmet_mrq_hdr[idx]; + drq = phba->sli4_hba.nvmet_mrq_data[idx]; + + /* sanity check on queue memory */ + if (unlikely(!hrq) || unlikely(!drq)) + return workposted; + + if (bf_get(lpfc_cqe_code, rcqe) == CQE_CODE_RECEIVE_V1) + rq_id = bf_get(lpfc_rcqe_rq_id_v1, rcqe); + else + rq_id = bf_get(lpfc_rcqe_rq_id, rcqe); + + if ((phba->nvmet_support == 0) || + (rq_id != hrq->queue_id)) + return workposted; + + status = bf_get(lpfc_rcqe_status, rcqe); + switch (status) { + case FC_STATUS_RQ_BUF_LEN_EXCEEDED: + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "6126 Receive Frame Truncated!!\n"); + hrq->RQ_buf_trunc++; + break; + case FC_STATUS_RQ_SUCCESS: + lpfc_sli4_rq_release(hrq, drq); + spin_lock_irqsave(&phba->hbalock, iflags); + dma_buf = lpfc_sli_rqbuf_get(phba, hrq); + if (!dma_buf) { + hrq->RQ_no_buf_found++; + spin_unlock_irqrestore(&phba->hbalock, iflags); + goto out; + } + spin_unlock_irqrestore(&phba->hbalock, iflags); + hrq->RQ_rcv_buf++; + fc_hdr = (struct fc_frame_header *)dma_buf->hbuf.virt; + + /* Just some basic sanity checks on FCP Command frame */ + fctl = (fc_hdr->fh_f_ctl[0] << 16 | + fc_hdr->fh_f_ctl[1] << 8 | + fc_hdr->fh_f_ctl[2]); + if (((fctl & + (FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT)) != + (FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT)) || + (fc_hdr->fh_seq_cnt != 0)) /* 0 byte swapped is still 0 */ + goto drop; + + if (fc_hdr->fh_type == FC_TYPE_FCP) { + dma_buf->bytes_recv = bf_get(lpfc_rcqe_length, rcqe); + lpfc_nvmet_unsol_fcp_event( + phba, phba->sli4_hba.els_wq->pring, dma_buf, + cq->assoc_qp->isr_timestamp); + return false; + } +drop: + lpfc_in_buf_free(phba, &dma_buf->dbuf); + break; + case FC_STATUS_INSUFF_BUF_NEED_BUF: + case FC_STATUS_INSUFF_BUF_FRM_DISC: + hrq->RQ_no_posted_buf++; + /* Post more buffers if possible */ + spin_lock_irqsave(&phba->hbalock, iflags); + phba->hba_flag |= HBA_POST_RECEIVE_BUFFER; + spin_unlock_irqrestore(&phba->hbalock, iflags); + workposted = true; + break; + } +out: + return workposted; } /** - * lpfc_sli4_fp_handle_wcqe - Process fast-path work queue completion entry + * lpfc_sli4_fp_handle_cqe - Process fast-path work queue completion entry * @cq: Pointer to the completion queue. * @eqe: Pointer to fast-path completion queue entry. * @@ -12488,7 +13170,7 @@ lpfc_sli4_fp_handle_rel_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, * event queue for FCP command response completion. **/ static int -lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, +lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, struct lpfc_cqe *cqe) { struct lpfc_wcqe_release wcqe; @@ -12500,10 +13182,15 @@ lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, /* Check and process for different type of WCQE and dispatch */ switch (bf_get(lpfc_wcqe_c_code, &wcqe)) { case CQE_CODE_COMPL_WQE: + case CQE_CODE_NVME_ERSP: cq->CQ_wq++; /* Process the WQ complete event */ phba->last_completion_time = jiffies; - lpfc_sli4_fp_handle_fcp_wcqe(phba, cq, + if ((cq->subtype == LPFC_FCP) || (cq->subtype == LPFC_NVME)) + lpfc_sli4_fp_handle_fcp_wcqe(phba, cq, + (struct lpfc_wcqe_complete *)&wcqe); + if (cq->subtype == LPFC_NVME_LS) + lpfc_sli4_fp_handle_fcp_wcqe(phba, cq, (struct lpfc_wcqe_complete *)&wcqe); break; case CQE_CODE_RELEASE_WQE: @@ -12519,9 +13206,17 @@ lpfc_sli4_fp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq, (struct sli4_wcqe_xri_aborted *)&wcqe); break; + case CQE_CODE_RECEIVE_V1: + case CQE_CODE_RECEIVE: + phba->last_completion_time = jiffies; + if (cq->subtype == LPFC_NVMET) { + workposted = lpfc_sli4_nvmet_handle_rcqe( + phba, cq, (struct lpfc_rcqe *)&wcqe); + } + break; default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0144 Not a valid WCQE code: x%x\n", + "0144 Not a valid CQE code: x%x\n", bf_get(lpfc_wcqe_c_code, &wcqe)); break; } @@ -12544,10 +13239,10 @@ static void lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, uint32_t qidx) { - struct lpfc_queue *cq; + struct lpfc_queue *cq = NULL; struct lpfc_cqe *cqe; bool workposted = false; - uint16_t cqid; + uint16_t cqid, id; int ecount = 0; if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) { @@ -12562,28 +13257,42 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, /* Get the reference to the corresponding CQ */ cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); - /* Check if this is a Slow path event */ - if (unlikely(cqid != phba->sli4_hba.fcp_cq_map[qidx])) { - lpfc_sli4_sp_handle_eqe(phba, eqe, - phba->sli4_hba.hba_eq[qidx]); - return; + if (phba->cfg_nvmet_mrq && phba->sli4_hba.nvmet_cqset) { + id = phba->sli4_hba.nvmet_cqset[0]->queue_id; + if ((cqid >= id) && (cqid < (id + phba->cfg_nvmet_mrq))) { + /* Process NVMET unsol rcv */ + cq = phba->sli4_hba.nvmet_cqset[cqid - id]; + goto process_cq; + } } - if (unlikely(!phba->sli4_hba.fcp_cq)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "3146 Fast-path completion queues " - "does not exist\n"); - return; + if (phba->sli4_hba.nvme_cq_map && + (cqid == phba->sli4_hba.nvme_cq_map[qidx])) { + /* Process NVME / NVMET command completion */ + cq = phba->sli4_hba.nvme_cq[qidx]; + goto process_cq; } - cq = phba->sli4_hba.fcp_cq[qidx]; - if (unlikely(!cq)) { - if (phba->sli.sli_flag & LPFC_SLI_ACTIVE) - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0367 Fast-path completion queue " - "(%d) does not exist\n", qidx); + + if (phba->sli4_hba.fcp_cq_map && + (cqid == phba->sli4_hba.fcp_cq_map[qidx])) { + /* Process FCP command completion */ + cq = phba->sli4_hba.fcp_cq[qidx]; + goto process_cq; + } + + if (phba->sli4_hba.nvmels_cq && + (cqid == phba->sli4_hba.nvmels_cq->queue_id)) { + /* Process NVME unsol rcv */ + cq = phba->sli4_hba.nvmels_cq; + } + + /* Otherwise this is a Slow path event */ + if (cq == NULL) { + lpfc_sli4_sp_handle_eqe(phba, eqe, phba->sli4_hba.hba_eq[qidx]); return; } +process_cq: if (unlikely(cqid != cq->queue_id)) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0368 Miss-matched fast-path completion " @@ -12592,9 +13301,12 @@ lpfc_sli4_hba_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, return; } + /* Save EQ associated with this CQ */ + cq->assoc_qp = phba->sli4_hba.hba_eq[qidx]; + /* Process all the entries to the CQ */ while ((cqe = lpfc_sli4_cq_get(cq))) { - workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, cqe); + workposted |= lpfc_sli4_fp_handle_cqe(phba, cq, cqe); if (!(++ecount % cq->entry_repost)) lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM); } @@ -12685,7 +13397,7 @@ lpfc_sli4_fof_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe) /* Process all the entries to the OAS CQ */ while ((cqe = lpfc_sli4_cq_get(cq))) { - workposted |= lpfc_sli4_fp_handle_wcqe(phba, cq, cqe); + workposted |= lpfc_sli4_fp_handle_cqe(phba, cq, cqe); if (!(++ecount % cq->entry_repost)) lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM); } @@ -12733,15 +13445,15 @@ irqreturn_t lpfc_sli4_fof_intr_handler(int irq, void *dev_id) { struct lpfc_hba *phba; - struct lpfc_fcp_eq_hdl *fcp_eq_hdl; + struct lpfc_hba_eq_hdl *hba_eq_hdl; struct lpfc_queue *eq; struct lpfc_eqe *eqe; unsigned long iflag; int ecount = 0; /* Get the driver's phba structure from the dev_id */ - fcp_eq_hdl = (struct lpfc_fcp_eq_hdl *)dev_id; - phba = fcp_eq_hdl->phba; + hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id; + phba = hba_eq_hdl->phba; if (unlikely(!phba)) return IRQ_NONE; @@ -12827,17 +13539,17 @@ irqreturn_t lpfc_sli4_hba_intr_handler(int irq, void *dev_id) { struct lpfc_hba *phba; - struct lpfc_fcp_eq_hdl *fcp_eq_hdl; + struct lpfc_hba_eq_hdl *hba_eq_hdl; struct lpfc_queue *fpeq; struct lpfc_eqe *eqe; unsigned long iflag; int ecount = 0; - int fcp_eqidx; + int hba_eqidx; /* Get the driver's phba structure from the dev_id */ - fcp_eq_hdl = (struct lpfc_fcp_eq_hdl *)dev_id; - phba = fcp_eq_hdl->phba; - fcp_eqidx = fcp_eq_hdl->idx; + hba_eq_hdl = (struct lpfc_hba_eq_hdl *)dev_id; + phba = hba_eq_hdl->phba; + hba_eqidx = hba_eq_hdl->idx; if (unlikely(!phba)) return IRQ_NONE; @@ -12845,15 +13557,20 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) return IRQ_NONE; /* Get to the EQ struct associated with this vector */ - fpeq = phba->sli4_hba.hba_eq[fcp_eqidx]; + fpeq = phba->sli4_hba.hba_eq[hba_eqidx]; if (unlikely(!fpeq)) return IRQ_NONE; +#ifdef CONFIG_SCSI_LPFC_DEBUG_FS + if (phba->ktime_on) + fpeq->isr_timestamp = ktime_get_ns(); +#endif + if (lpfc_fcp_look_ahead) { - if (atomic_dec_and_test(&fcp_eq_hdl->fcp_eq_in_use)) + if (atomic_dec_and_test(&hba_eq_hdl->hba_eq_in_use)) lpfc_sli4_eq_clr_intr(fpeq); else { - atomic_inc(&fcp_eq_hdl->fcp_eq_in_use); + atomic_inc(&hba_eq_hdl->hba_eq_in_use); return IRQ_NONE; } } @@ -12868,7 +13585,7 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) lpfc_sli4_eq_flush(phba, fpeq); spin_unlock_irqrestore(&phba->hbalock, iflag); if (lpfc_fcp_look_ahead) - atomic_inc(&fcp_eq_hdl->fcp_eq_in_use); + atomic_inc(&hba_eq_hdl->hba_eq_in_use); return IRQ_NONE; } @@ -12879,7 +13596,7 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) if (eqe == NULL) break; - lpfc_sli4_hba_handle_eqe(phba, eqe, fcp_eqidx); + lpfc_sli4_hba_handle_eqe(phba, eqe, hba_eqidx); if (!(++ecount % fpeq->entry_repost)) lpfc_sli4_eq_release(fpeq, LPFC_QUEUE_NOARM); fpeq->EQ_processed++; @@ -12896,7 +13613,7 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) fpeq->EQ_no_entry++; if (lpfc_fcp_look_ahead) { - atomic_inc(&fcp_eq_hdl->fcp_eq_in_use); + atomic_inc(&hba_eq_hdl->hba_eq_in_use); return IRQ_NONE; } @@ -12910,7 +13627,8 @@ lpfc_sli4_hba_intr_handler(int irq, void *dev_id) } if (lpfc_fcp_look_ahead) - atomic_inc(&fcp_eq_hdl->fcp_eq_in_use); + atomic_inc(&hba_eq_hdl->hba_eq_in_use); + return IRQ_HANDLED; } /* lpfc_sli4_fp_intr_handler */ @@ -12937,7 +13655,7 @@ lpfc_sli4_intr_handler(int irq, void *dev_id) struct lpfc_hba *phba; irqreturn_t hba_irq_rc; bool hba_handled = false; - int fcp_eqidx; + int qidx; /* Get the driver's phba structure from the dev_id */ phba = (struct lpfc_hba *)dev_id; @@ -12948,16 +13666,16 @@ lpfc_sli4_intr_handler(int irq, void *dev_id) /* * Invoke fast-path host attention interrupt handling as appropriate. */ - for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_io_channel; fcp_eqidx++) { + for (qidx = 0; qidx < phba->io_channel_irqs; qidx++) { hba_irq_rc = lpfc_sli4_hba_intr_handler(irq, - &phba->sli4_hba.fcp_eq_hdl[fcp_eqidx]); + &phba->sli4_hba.hba_eq_hdl[qidx]); if (hba_irq_rc == IRQ_HANDLED) hba_handled |= true; } if (phba->cfg_fof) { hba_irq_rc = lpfc_sli4_fof_intr_handler(irq, - &phba->sli4_hba.fcp_eq_hdl[0]); + &phba->sli4_hba.hba_eq_hdl[qidx]); if (hba_irq_rc == IRQ_HANDLED) hba_handled |= true; } @@ -12988,6 +13706,11 @@ lpfc_sli4_queue_free(struct lpfc_queue *queue) dmabuf->virt, dmabuf->phys); kfree(dmabuf); } + if (queue->rqbp) { + lpfc_free_rq_buffer(queue->phba, queue); + kfree(queue->rqbp); + } + kfree(queue->pring); kfree(queue); return; } @@ -13021,7 +13744,13 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t entry_size, return NULL; queue->page_count = (ALIGN(entry_size * entry_count, hw_page_size))/hw_page_size; + + /* If needed, Adjust page count to match the max the adapter supports */ + if (queue->page_count > phba->sli4_hba.pc_sli4_params.wqpcnt) + queue->page_count = phba->sli4_hba.pc_sli4_params.wqpcnt; + INIT_LIST_HEAD(&queue->list); + INIT_LIST_HEAD(&queue->wq_list); INIT_LIST_HEAD(&queue->page_list); INIT_LIST_HEAD(&queue->child_list); for (x = 0, total_qe_count = 0; x < queue->page_count; x++) { @@ -13093,7 +13822,7 @@ lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset) } /** - * lpfc_modify_fcp_eq_delay - Modify Delay Multiplier on FCP EQs + * lpfc_modify_hba_eq_delay - Modify Delay Multiplier on FCP EQs * @phba: HBA structure that indicates port to create a queue on. * @startq: The starting FCP EQ to modify * @@ -13109,7 +13838,7 @@ lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset) * fails this function will return -ENXIO. **/ int -lpfc_modify_fcp_eq_delay(struct lpfc_hba *phba, uint32_t startq) +lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq) { struct lpfc_mbx_modify_eq_delay *eq_delay; LPFC_MBOXQ_t *mbox; @@ -13117,11 +13846,11 @@ lpfc_modify_fcp_eq_delay(struct lpfc_hba *phba, uint32_t startq) int cnt, rc, length, status = 0; uint32_t shdr_status, shdr_add_status; uint32_t result; - int fcp_eqidx; + int qidx; union lpfc_sli4_cfg_shdr *shdr; uint16_t dmult; - if (startq >= phba->cfg_fcp_io_channel) + if (startq >= phba->io_channel_irqs) return 0; mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); @@ -13135,16 +13864,15 @@ lpfc_modify_fcp_eq_delay(struct lpfc_hba *phba, uint32_t startq) eq_delay = &mbox->u.mqe.un.eq_delay; /* Calculate delay multiper from maximum interrupt per second */ - result = phba->cfg_fcp_imax / phba->cfg_fcp_io_channel; - if (result > LPFC_DMULT_CONST) + result = phba->cfg_fcp_imax / phba->io_channel_irqs; + if (result > LPFC_DMULT_CONST || result == 0) dmult = 0; else dmult = LPFC_DMULT_CONST/result - 1; cnt = 0; - for (fcp_eqidx = startq; fcp_eqidx < phba->cfg_fcp_io_channel; - fcp_eqidx++) { - eq = phba->sli4_hba.hba_eq[fcp_eqidx]; + for (qidx = startq; qidx < phba->io_channel_irqs; qidx++) { + eq = phba->sli4_hba.hba_eq[qidx]; if (!eq) continue; eq_delay->u.request.eq[cnt].eq_id = eq->queue_id; @@ -13359,8 +14087,10 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, switch (cq->entry_count) { default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "0361 Unsupported CQ count. (%d)\n", - cq->entry_count); + "0361 Unsupported CQ count: " + "entry cnt %d sz %d pg cnt %d repost %d\n", + cq->entry_count, cq->entry_size, + cq->page_count, cq->entry_repost); if (cq->entry_count < 256) { status = -EINVAL; goto out; @@ -13420,6 +14150,234 @@ out: } /** + * lpfc_cq_create_set - Create a set of Completion Queues on the HBA for MRQ + * @phba: HBA structure that indicates port to create a queue on. + * @cqp: The queue structure array to use to create the completion queues. + * @eqp: The event queue array to bind these completion queues to. + * + * This function creates a set of completion queue, s to support MRQ + * as detailed in @cqp, on a port, + * described by @phba by sending a CREATE_CQ_SET mailbox command to the HBA. + * + * The @phba struct is used to send mailbox command to HBA. The @cq struct + * is used to get the entry count and entry size that are necessary to + * determine the number of pages to allocate and use for this queue. The @eq + * is used to indicate which event queue to bind this completion queue to. This + * function will send the CREATE_CQ_SET mailbox command to the HBA to setup the + * completion queue. This function is asynchronous and will wait for the mailbox + * command to finish before continuing. + * + * On success this function will return a zero. If unable to allocate enough + * memory this function will return -ENOMEM. If the queue create mailbox command + * fails this function will return -ENXIO. + **/ +int +lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, + struct lpfc_queue **eqp, uint32_t type, uint32_t subtype) +{ + struct lpfc_queue *cq; + struct lpfc_queue *eq; + struct lpfc_mbx_cq_create_set *cq_set; + struct lpfc_dmabuf *dmabuf; + LPFC_MBOXQ_t *mbox; + int rc, length, alloclen, status = 0; + int cnt, idx, numcq, page_idx = 0; + uint32_t shdr_status, shdr_add_status; + union lpfc_sli4_cfg_shdr *shdr; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + + /* sanity check on queue memory */ + numcq = phba->cfg_nvmet_mrq; + if (!cqp || !eqp || !numcq) + return -ENODEV; + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + length = sizeof(struct lpfc_mbx_cq_create_set); + length += ((numcq * cqp[0]->page_count) * + sizeof(struct dma_address)); + alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_CQ_CREATE_SET, length, + LPFC_SLI4_MBX_NEMBED); + if (alloclen < length) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "3098 Allocated DMA memory size (%d) is " + "less than the requested DMA memory size " + "(%d)\n", alloclen, length); + status = -ENOMEM; + goto out; + } + cq_set = mbox->sge_array->addr[0]; + shdr = (union lpfc_sli4_cfg_shdr *)&cq_set->cfg_shdr; + bf_set(lpfc_mbox_hdr_version, &shdr->request, 0); + + for (idx = 0; idx < numcq; idx++) { + cq = cqp[idx]; + eq = eqp[idx]; + if (!cq || !eq) { + status = -ENOMEM; + goto out; + } + + switch (idx) { + case 0: + bf_set(lpfc_mbx_cq_create_set_page_size, + &cq_set->u.request, + (hw_page_size / SLI4_PAGE_SIZE)); + bf_set(lpfc_mbx_cq_create_set_num_pages, + &cq_set->u.request, cq->page_count); + bf_set(lpfc_mbx_cq_create_set_evt, + &cq_set->u.request, 1); + bf_set(lpfc_mbx_cq_create_set_valid, + &cq_set->u.request, 1); + bf_set(lpfc_mbx_cq_create_set_cqe_size, + &cq_set->u.request, 0); + bf_set(lpfc_mbx_cq_create_set_num_cq, + &cq_set->u.request, numcq); + switch (cq->entry_count) { + default: + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "3118 Bad CQ count. (%d)\n", + cq->entry_count); + if (cq->entry_count < 256) { + status = -EINVAL; + goto out; + } + /* otherwise default to smallest (drop thru) */ + case 256: + bf_set(lpfc_mbx_cq_create_set_cqe_cnt, + &cq_set->u.request, LPFC_CQ_CNT_256); + break; + case 512: + bf_set(lpfc_mbx_cq_create_set_cqe_cnt, + &cq_set->u.request, LPFC_CQ_CNT_512); + break; + case 1024: + bf_set(lpfc_mbx_cq_create_set_cqe_cnt, + &cq_set->u.request, LPFC_CQ_CNT_1024); + break; + } + bf_set(lpfc_mbx_cq_create_set_eq_id0, + &cq_set->u.request, eq->queue_id); + break; + case 1: + bf_set(lpfc_mbx_cq_create_set_eq_id1, + &cq_set->u.request, eq->queue_id); + break; + case 2: + bf_set(lpfc_mbx_cq_create_set_eq_id2, + &cq_set->u.request, eq->queue_id); + break; + case 3: + bf_set(lpfc_mbx_cq_create_set_eq_id3, + &cq_set->u.request, eq->queue_id); + break; + case 4: + bf_set(lpfc_mbx_cq_create_set_eq_id4, + &cq_set->u.request, eq->queue_id); + break; + case 5: + bf_set(lpfc_mbx_cq_create_set_eq_id5, + &cq_set->u.request, eq->queue_id); + break; + case 6: + bf_set(lpfc_mbx_cq_create_set_eq_id6, + &cq_set->u.request, eq->queue_id); + break; + case 7: + bf_set(lpfc_mbx_cq_create_set_eq_id7, + &cq_set->u.request, eq->queue_id); + break; + case 8: + bf_set(lpfc_mbx_cq_create_set_eq_id8, + &cq_set->u.request, eq->queue_id); + break; + case 9: + bf_set(lpfc_mbx_cq_create_set_eq_id9, + &cq_set->u.request, eq->queue_id); + break; + case 10: + bf_set(lpfc_mbx_cq_create_set_eq_id10, + &cq_set->u.request, eq->queue_id); + break; + case 11: + bf_set(lpfc_mbx_cq_create_set_eq_id11, + &cq_set->u.request, eq->queue_id); + break; + case 12: + bf_set(lpfc_mbx_cq_create_set_eq_id12, + &cq_set->u.request, eq->queue_id); + break; + case 13: + bf_set(lpfc_mbx_cq_create_set_eq_id13, + &cq_set->u.request, eq->queue_id); + break; + case 14: + bf_set(lpfc_mbx_cq_create_set_eq_id14, + &cq_set->u.request, eq->queue_id); + break; + case 15: + bf_set(lpfc_mbx_cq_create_set_eq_id15, + &cq_set->u.request, eq->queue_id); + break; + } + + /* link the cq onto the parent eq child list */ + list_add_tail(&cq->list, &eq->child_list); + /* Set up completion queue's type and subtype */ + cq->type = type; + cq->subtype = subtype; + cq->assoc_qid = eq->queue_id; + cq->host_index = 0; + cq->hba_index = 0; + + rc = 0; + list_for_each_entry(dmabuf, &cq->page_list, list) { + memset(dmabuf->virt, 0, hw_page_size); + cnt = page_idx + dmabuf->buffer_tag; + cq_set->u.request.page[cnt].addr_lo = + putPaddrLow(dmabuf->phys); + cq_set->u.request.page[cnt].addr_hi = + putPaddrHigh(dmabuf->phys); + rc++; + } + page_idx += rc; + } + + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + + /* The IOCTL status is embedded in the mailbox subheader. */ + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + if (shdr_status || shdr_add_status || rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3119 CQ_CREATE_SET mailbox failed with " + "status x%x add_status x%x, mbx status x%x\n", + shdr_status, shdr_add_status, rc); + status = -ENXIO; + goto out; + } + rc = bf_get(lpfc_mbx_cq_create_set_base_id, &cq_set->u.response); + if (rc == 0xFFFF) { + status = -ENXIO; + goto out; + } + + for (idx = 0; idx < numcq; idx++) { + cq = cqp[idx]; + cq->queue_id = rc + idx; + } + +out: + lpfc_sli4_mbox_cmd_free(phba, mbox); + return status; +} + +/** * lpfc_mq_create_fb_init - Send MCC_CREATE without async events registration * @phba: HBA structure that indicates port to create a queue on. * @mq: The queue structure to use to create the mailbox queue. @@ -13722,7 +14680,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_WQ_WQE_SIZE_128); bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + LPFC_WQ_PAGE_SIZE_4096); page = wq_create->u.request_1.page; break; } @@ -13748,8 +14706,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_WQ_WQE_SIZE_128); break; } - bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + bf_set(lpfc_mbx_wq_create_page_size, + &wq_create->u.request_1, + LPFC_WQ_PAGE_SIZE_4096); page = wq_create->u.request_1.page; break; default: @@ -13825,6 +14784,11 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, wq->db_format = LPFC_DB_LIST_FORMAT; wq->db_regaddr = phba->sli4_hba.WQDBregaddr; } + wq->pring = kzalloc(sizeof(struct lpfc_sli_ring), GFP_KERNEL); + if (wq->pring == NULL) { + status = -ENOMEM; + goto out; + } wq->type = LPFC_WQ; wq->assoc_qid = cq->queue_id; wq->subtype = subtype; @@ -13935,7 +14899,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, LPFC_RQE_SIZE_8); bf_set(lpfc_rq_context_page_size, &rq_create->u.request.context, - (PAGE_SIZE/SLI4_PAGE_SIZE)); + LPFC_RQ_PAGE_SIZE_4096); } else { switch (hrq->entry_count) { default: @@ -14144,6 +15108,197 @@ out: } /** + * lpfc_mrq_create - Create MRQ Receive Queues on the HBA + * @phba: HBA structure that indicates port to create a queue on. + * @hrqp: The queue structure array to use to create the header receive queues. + * @drqp: The queue structure array to use to create the data receive queues. + * @cqp: The completion queue array to bind these receive queues to. + * + * This function creates a receive buffer queue pair , as detailed in @hrq and + * @drq, on a port, described by @phba by sending a RQ_CREATE mailbox command + * to the HBA. + * + * The @phba struct is used to send mailbox command to HBA. The @drq and @hrq + * struct is used to get the entry count that is necessary to determine the + * number of pages to use for this queue. The @cq is used to indicate which + * completion queue to bind received buffers that are posted to these queues to. + * This function will send the RQ_CREATE mailbox command to the HBA to setup the + * receive queue pair. This function is asynchronous and will wait for the + * mailbox command to finish before continuing. + * + * On success this function will return a zero. If unable to allocate enough + * memory this function will return -ENOMEM. If the queue create mailbox command + * fails this function will return -ENXIO. + **/ +int +lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp, + struct lpfc_queue **drqp, struct lpfc_queue **cqp, + uint32_t subtype) +{ + struct lpfc_queue *hrq, *drq, *cq; + struct lpfc_mbx_rq_create_v2 *rq_create; + struct lpfc_dmabuf *dmabuf; + LPFC_MBOXQ_t *mbox; + int rc, length, alloclen, status = 0; + int cnt, idx, numrq, page_idx = 0; + uint32_t shdr_status, shdr_add_status; + union lpfc_sli4_cfg_shdr *shdr; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + + numrq = phba->cfg_nvmet_mrq; + /* sanity check on array memory */ + if (!hrqp || !drqp || !cqp || !numrq) + return -ENODEV; + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + length = sizeof(struct lpfc_mbx_rq_create_v2); + length += ((2 * numrq * hrqp[0]->page_count) * + sizeof(struct dma_address)); + + alloclen = lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, + LPFC_MBOX_OPCODE_FCOE_RQ_CREATE, length, + LPFC_SLI4_MBX_NEMBED); + if (alloclen < length) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "3099 Allocated DMA memory size (%d) is " + "less than the requested DMA memory size " + "(%d)\n", alloclen, length); + status = -ENOMEM; + goto out; + } + + + + rq_create = mbox->sge_array->addr[0]; + shdr = (union lpfc_sli4_cfg_shdr *)&rq_create->cfg_shdr; + + bf_set(lpfc_mbox_hdr_version, &shdr->request, LPFC_Q_CREATE_VERSION_2); + cnt = 0; + + for (idx = 0; idx < numrq; idx++) { + hrq = hrqp[idx]; + drq = drqp[idx]; + cq = cqp[idx]; + + if (hrq->entry_count != drq->entry_count) { + status = -EINVAL; + goto out; + } + + /* sanity check on queue memory */ + if (!hrq || !drq || !cq) { + status = -ENODEV; + goto out; + } + + if (idx == 0) { + bf_set(lpfc_mbx_rq_create_num_pages, + &rq_create->u.request, + hrq->page_count); + bf_set(lpfc_mbx_rq_create_rq_cnt, + &rq_create->u.request, (numrq * 2)); + bf_set(lpfc_mbx_rq_create_dnb, &rq_create->u.request, + 1); + bf_set(lpfc_rq_context_base_cq, + &rq_create->u.request.context, + cq->queue_id); + bf_set(lpfc_rq_context_data_size, + &rq_create->u.request.context, + LPFC_DATA_BUF_SIZE); + bf_set(lpfc_rq_context_hdr_size, + &rq_create->u.request.context, + LPFC_HDR_BUF_SIZE); + bf_set(lpfc_rq_context_rqe_count_1, + &rq_create->u.request.context, + hrq->entry_count); + bf_set(lpfc_rq_context_rqe_size, + &rq_create->u.request.context, + LPFC_RQE_SIZE_8); + bf_set(lpfc_rq_context_page_size, + &rq_create->u.request.context, + (PAGE_SIZE/SLI4_PAGE_SIZE)); + } + rc = 0; + list_for_each_entry(dmabuf, &hrq->page_list, list) { + memset(dmabuf->virt, 0, hw_page_size); + cnt = page_idx + dmabuf->buffer_tag; + rq_create->u.request.page[cnt].addr_lo = + putPaddrLow(dmabuf->phys); + rq_create->u.request.page[cnt].addr_hi = + putPaddrHigh(dmabuf->phys); + rc++; + } + page_idx += rc; + + rc = 0; + list_for_each_entry(dmabuf, &drq->page_list, list) { + memset(dmabuf->virt, 0, hw_page_size); + cnt = page_idx + dmabuf->buffer_tag; + rq_create->u.request.page[cnt].addr_lo = + putPaddrLow(dmabuf->phys); + rq_create->u.request.page[cnt].addr_hi = + putPaddrHigh(dmabuf->phys); + rc++; + } + page_idx += rc; + + hrq->db_format = LPFC_DB_RING_FORMAT; + hrq->db_regaddr = phba->sli4_hba.RQDBregaddr; + hrq->type = LPFC_HRQ; + hrq->assoc_qid = cq->queue_id; + hrq->subtype = subtype; + hrq->host_index = 0; + hrq->hba_index = 0; + + drq->db_format = LPFC_DB_RING_FORMAT; + drq->db_regaddr = phba->sli4_hba.RQDBregaddr; + drq->type = LPFC_DRQ; + drq->assoc_qid = cq->queue_id; + drq->subtype = subtype; + drq->host_index = 0; + drq->hba_index = 0; + + list_add_tail(&hrq->list, &cq->child_list); + list_add_tail(&drq->list, &cq->child_list); + } + + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + /* The IOCTL status is embedded in the mailbox subheader. */ + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + if (shdr_status || shdr_add_status || rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3120 RQ_CREATE mailbox failed with " + "status x%x add_status x%x, mbx status x%x\n", + shdr_status, shdr_add_status, rc); + status = -ENXIO; + goto out; + } + rc = bf_get(lpfc_mbx_rq_create_q_id, &rq_create->u.response); + if (rc == 0xFFFF) { + status = -ENXIO; + goto out; + } + + /* Initialize all RQs with associated queue id */ + for (idx = 0; idx < numrq; idx++) { + hrq = hrqp[idx]; + hrq->queue_id = rc + (2 * idx); + drq = drqp[idx]; + drq->queue_id = rc + (2 * idx) + 1; + } + +out: + lpfc_sli4_mbox_cmd_free(phba, mbox); + return status; +} + +/** * lpfc_eq_destroy - Destroy an event Queue on the HBA * @eq: The queue structure associated with the queue to destroy. * @@ -14609,7 +15764,7 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba) } /** - * lpfc_sli4_post_els_sgl_list - post a block of ELS sgls to the port. + * lpfc_sli4_post_sgl_list - post a block of ELS sgls to the port. * @phba: pointer to lpfc hba data structure. * @post_sgl_list: pointer to els sgl entry list. * @count: number of els sgl entries on the list. @@ -14620,7 +15775,7 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba) * stopped. **/ static int -lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba, +lpfc_sli4_post_sgl_list(struct lpfc_hba *phba, struct list_head *post_sgl_list, int post_cnt) { @@ -14636,14 +15791,15 @@ lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba, uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; - reqlen = phba->sli4_hba.els_xri_cnt * sizeof(struct sgl_page_pairs) + + reqlen = post_cnt * sizeof(struct sgl_page_pairs) + sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); if (reqlen > SLI4_PAGE_SIZE) { - lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2559 Block sgl registration required DMA " "size (%d) great than a page\n", reqlen); return -ENOMEM; } + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; @@ -14687,8 +15843,9 @@ lpfc_sli4_post_els_sgl_list(struct lpfc_hba *phba, /* Complete initialization and perform endian conversion. */ bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start); - bf_set(lpfc_post_sgl_pages_xricnt, sgl, phba->sli4_hba.els_xri_cnt); + bf_set(lpfc_post_sgl_pages_xricnt, sgl, post_cnt); sgl->word0 = cpu_to_le32(sgl->word0); + if (!phba->sli4_hba.intr_enable) rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); else { @@ -14823,6 +15980,9 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, return rc; } +static char *lpfc_rctl_names[] = FC_RCTL_NAMES_INIT; +static char *lpfc_type_names[] = FC_TYPE_NAMES_INIT; + /** * lpfc_fc_frame_check - Check that this frame is a valid frame to handle * @phba: pointer to lpfc_hba struct that the frame was received on @@ -14837,8 +15997,6 @@ static int lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr) { /* make rctl_names static to save stack space */ - static char *rctl_names[] = FC_RCTL_NAMES_INIT; - char *type_names[] = FC_TYPE_NAMES_INIT; struct fc_vft_header *fc_vft_hdr; uint32_t *header = (uint32_t *) fc_hdr; @@ -14883,6 +16041,7 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr) case FC_TYPE_ELS: case FC_TYPE_FCP: case FC_TYPE_CT: + case FC_TYPE_NVME: break; case FC_TYPE_IP: case FC_TYPE_ILS: @@ -14893,8 +16052,8 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr) lpfc_printf_log(phba, KERN_INFO, LOG_ELS, "2538 Received frame rctl:%s (x%x), type:%s (x%x), " "frame Data:%08x %08x %08x %08x %08x %08x %08x\n", - rctl_names[fc_hdr->fh_r_ctl], fc_hdr->fh_r_ctl, - type_names[fc_hdr->fh_type], fc_hdr->fh_type, + lpfc_rctl_names[fc_hdr->fh_r_ctl], fc_hdr->fh_r_ctl, + lpfc_type_names[fc_hdr->fh_type], fc_hdr->fh_type, be32_to_cpu(header[0]), be32_to_cpu(header[1]), be32_to_cpu(header[2]), be32_to_cpu(header[3]), be32_to_cpu(header[4]), be32_to_cpu(header[5]), @@ -14903,8 +16062,8 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr) drop: lpfc_printf_log(phba, KERN_WARNING, LOG_ELS, "2539 Dropped frame rctl:%s type:%s\n", - rctl_names[fc_hdr->fh_r_ctl], - type_names[fc_hdr->fh_type]); + lpfc_rctl_names[fc_hdr->fh_r_ctl], + lpfc_type_names[fc_hdr->fh_type]); return 1; } @@ -14940,14 +16099,11 @@ lpfc_fc_hdr_get_vfi(struct fc_frame_header *fc_hdr) **/ static struct lpfc_vport * lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr, - uint16_t fcfi) + uint16_t fcfi, uint32_t did) { struct lpfc_vport **vports; struct lpfc_vport *vport = NULL; int i; - uint32_t did = (fc_hdr->fh_d_id[0] << 16 | - fc_hdr->fh_d_id[1] << 8 | - fc_hdr->fh_d_id[2]); if (did == Fabric_DID) return phba->pport; @@ -14956,7 +16112,7 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr, return phba->pport; vports = lpfc_create_vport_work_array(phba); - if (vports != NULL) + if (vports != NULL) { for (i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) { if (phba->fcf.fcfi == fcfi && vports[i]->vfi == lpfc_fc_hdr_get_vfi(fc_hdr) && @@ -14965,6 +16121,7 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr, break; } } + } lpfc_destroy_vport_work_array(phba, vports); return vport; } @@ -15394,7 +16551,7 @@ lpfc_sli4_seq_abort_rsp(struct lpfc_vport *vport, * a BA_RJT. */ if ((fctl & FC_FC_EX_CTX) && - (lxri > lpfc_sli4_get_els_iocb_cnt(phba))) { + (lxri > lpfc_sli4_get_iocb_cnt(phba))) { icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_RJT; bf_set(lpfc_vndr_code, &icmd->un.bls_rsp, 0); bf_set(lpfc_rsn_expln, &icmd->un.bls_rsp, FC_BA_RJT_INV_XID); @@ -15571,6 +16728,7 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf) /* Initialize the first IOCB. */ first_iocbq->iocb.unsli3.rcvsli3.acc_len = 0; first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS; + first_iocbq->vport = vport; /* Check FC Header to see what TYPE of frame we are rcv'ing */ if (sli4_type_from_fc_hdr(fc_hdr) == FC_TYPE_ELS) { @@ -15683,7 +16841,7 @@ lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport, return; } if (!lpfc_complete_unsol_iocb(phba, - &phba->sli.ring[LPFC_ELS_RING], + phba->sli4_hba.els_wq->pring, iocbq, fc_hdr->fh_r_ctl, fc_hdr->fh_type)) lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -15708,8 +16866,7 @@ lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport, * This function is called with no lock held. This function processes all * the received buffers and gives it to upper layers when a received buffer * indicates that it is the final frame in the sequence. The interrupt - * service routine processes received buffers at interrupt contexts and adds - * received dma buffers to the rb_pend_list queue and signals the worker thread. + * service routine processes received buffers at interrupt contexts. * Worker thread calls lpfc_sli4_handle_received_buffer, which will call the * appropriate receive function when the final frame in a sequence is received. **/ @@ -15725,11 +16882,13 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba, /* Process each received buffer */ fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt; + /* check to see if this a valid type of frame */ if (lpfc_fc_frame_check(phba, fc_hdr)) { lpfc_in_buf_free(phba, &dmabuf->dbuf); return; } + if ((bf_get(lpfc_cqe_code, &dmabuf->cq_event.cqe.rcqe_cmpl) == CQE_CODE_RECEIVE_V1)) fcfi = bf_get(lpfc_rcqe_fcf_id_v1, @@ -15738,16 +16897,16 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba, fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl); - vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi); + /* d_id this frame is directed to */ + did = sli4_did_from_fc_hdr(fc_hdr); + + vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi, did); if (!vport) { /* throw out the frame */ lpfc_in_buf_free(phba, &dmabuf->dbuf); return; } - /* d_id this frame is directed to */ - did = sli4_did_from_fc_hdr(fc_hdr); - /* vport is registered unless we rcv a FLOGI directed to Fabric_DID */ if (!(vport->vpi_state & LPFC_VPI_REGISTERED) && (did != Fabric_DID)) { @@ -17225,7 +18384,7 @@ uint32_t lpfc_drain_txq(struct lpfc_hba *phba) { LIST_HEAD(completions); - struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; + struct lpfc_sli_ring *pring; struct lpfc_iocbq *piocbq = NULL; unsigned long iflags = 0; char *fail_msg = NULL; @@ -17234,6 +18393,8 @@ lpfc_drain_txq(struct lpfc_hba *phba) union lpfc_wqe *wqe = (union lpfc_wqe *) &wqe128; uint32_t txq_cnt = 0; + pring = lpfc_phba_elsring(phba); + spin_lock_irqsave(&pring->ring_lock, iflags); list_for_each_entry(piocbq, &pring->txq, list) { txq_cnt++; @@ -17255,7 +18416,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) txq_cnt); break; } - sglq = __lpfc_sli_get_sglq(phba, piocbq); + sglq = __lpfc_sli_get_els_sglq(phba, piocbq); if (!sglq) { __lpfc_sli_ringtx_put(phba, pring, piocbq); spin_unlock_irqrestore(&pring->ring_lock, iflags); @@ -17295,3 +18456,217 @@ lpfc_drain_txq(struct lpfc_hba *phba) return txq_cnt; } + +/** + * lpfc_wqe_bpl2sgl - Convert the bpl/bde to a sgl. + * @phba: Pointer to HBA context object. + * @pwqe: Pointer to command WQE. + * @sglq: Pointer to the scatter gather queue object. + * + * This routine converts the bpl or bde that is in the WQE + * to a sgl list for the sli4 hardware. The physical address + * of the bpl/bde is converted back to a virtual address. + * If the WQE contains a BPL then the list of BDE's is + * converted to sli4_sge's. If the WQE contains a single + * BDE then it is converted to a single sli_sge. + * The WQE is still in cpu endianness so the contents of + * the bpl can be used without byte swapping. + * + * Returns valid XRI = Success, NO_XRI = Failure. + */ +static uint16_t +lpfc_wqe_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeq, + struct lpfc_sglq *sglq) +{ + uint16_t xritag = NO_XRI; + struct ulp_bde64 *bpl = NULL; + struct ulp_bde64 bde; + struct sli4_sge *sgl = NULL; + struct lpfc_dmabuf *dmabuf; + union lpfc_wqe *wqe; + int numBdes = 0; + int i = 0; + uint32_t offset = 0; /* accumulated offset in the sg request list */ + int inbound = 0; /* number of sg reply entries inbound from firmware */ + uint32_t cmd; + + if (!pwqeq || !sglq) + return xritag; + + sgl = (struct sli4_sge *)sglq->sgl; + wqe = &pwqeq->wqe; + pwqeq->iocb.ulpIoTag = pwqeq->iotag; + + cmd = bf_get(wqe_cmnd, &wqe->generic.wqe_com); + if (cmd == CMD_XMIT_BLS_RSP64_WQE) + return sglq->sli4_xritag; + numBdes = pwqeq->rsvd2; + if (numBdes) { + /* The addrHigh and addrLow fields within the WQE + * have not been byteswapped yet so there is no + * need to swap them back. + */ + if (pwqeq->context3) + dmabuf = (struct lpfc_dmabuf *)pwqeq->context3; + else + return xritag; + + bpl = (struct ulp_bde64 *)dmabuf->virt; + if (!bpl) + return xritag; + + for (i = 0; i < numBdes; i++) { + /* Should already be byte swapped. */ + sgl->addr_hi = bpl->addrHigh; + sgl->addr_lo = bpl->addrLow; + + sgl->word2 = le32_to_cpu(sgl->word2); + if ((i+1) == numBdes) + bf_set(lpfc_sli4_sge_last, sgl, 1); + else + bf_set(lpfc_sli4_sge_last, sgl, 0); + /* swap the size field back to the cpu so we + * can assign it to the sgl. + */ + bde.tus.w = le32_to_cpu(bpl->tus.w); + sgl->sge_len = cpu_to_le32(bde.tus.f.bdeSize); + /* The offsets in the sgl need to be accumulated + * separately for the request and reply lists. + * The request is always first, the reply follows. + */ + switch (cmd) { + case CMD_GEN_REQUEST64_WQE: + /* add up the reply sg entries */ + if (bpl->tus.f.bdeFlags == BUFF_TYPE_BDE_64I) + inbound++; + /* first inbound? reset the offset */ + if (inbound == 1) + offset = 0; + bf_set(lpfc_sli4_sge_offset, sgl, offset); + bf_set(lpfc_sli4_sge_type, sgl, + LPFC_SGE_TYPE_DATA); + offset += bde.tus.f.bdeSize; + break; + case CMD_FCP_TRSP64_WQE: + bf_set(lpfc_sli4_sge_offset, sgl, 0); + bf_set(lpfc_sli4_sge_type, sgl, + LPFC_SGE_TYPE_DATA); + break; + case CMD_FCP_TSEND64_WQE: + case CMD_FCP_TRECEIVE64_WQE: + bf_set(lpfc_sli4_sge_type, sgl, + bpl->tus.f.bdeFlags); + if (i < 3) + offset = 0; + else + offset += bde.tus.f.bdeSize; + bf_set(lpfc_sli4_sge_offset, sgl, offset); + break; + } + sgl->word2 = cpu_to_le32(sgl->word2); + bpl++; + sgl++; + } + } else if (wqe->gen_req.bde.tus.f.bdeFlags == BUFF_TYPE_BDE_64) { + /* The addrHigh and addrLow fields of the BDE have not + * been byteswapped yet so they need to be swapped + * before putting them in the sgl. + */ + sgl->addr_hi = cpu_to_le32(wqe->gen_req.bde.addrHigh); + sgl->addr_lo = cpu_to_le32(wqe->gen_req.bde.addrLow); + sgl->word2 = le32_to_cpu(sgl->word2); + bf_set(lpfc_sli4_sge_last, sgl, 1); + sgl->word2 = cpu_to_le32(sgl->word2); + sgl->sge_len = cpu_to_le32(wqe->gen_req.bde.tus.f.bdeSize); + } + return sglq->sli4_xritag; +} + +/** + * lpfc_sli4_issue_wqe - Issue an SLI4 Work Queue Entry (WQE) + * @phba: Pointer to HBA context object. + * @ring_number: Base sli ring number + * @pwqe: Pointer to command WQE. + **/ +int +lpfc_sli4_issue_wqe(struct lpfc_hba *phba, uint32_t ring_number, + struct lpfc_iocbq *pwqe) +{ + union lpfc_wqe *wqe = &pwqe->wqe; + struct lpfc_nvmet_rcv_ctx *ctxp; + struct lpfc_queue *wq; + struct lpfc_sglq *sglq; + struct lpfc_sli_ring *pring; + unsigned long iflags; + + /* NVME_LS and NVME_LS ABTS requests. */ + if (pwqe->iocb_flag & LPFC_IO_NVME_LS) { + pring = phba->sli4_hba.nvmels_wq->pring; + spin_lock_irqsave(&pring->ring_lock, iflags); + sglq = __lpfc_sli_get_els_sglq(phba, pwqe); + if (!sglq) { + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return WQE_BUSY; + } + pwqe->sli4_lxritag = sglq->sli4_lxritag; + pwqe->sli4_xritag = sglq->sli4_xritag; + if (lpfc_wqe_bpl2sgl(phba, pwqe, sglq) == NO_XRI) { + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return WQE_ERROR; + } + bf_set(wqe_xri_tag, &pwqe->wqe.xmit_bls_rsp.wqe_com, + pwqe->sli4_xritag); + if (lpfc_sli4_wq_put(phba->sli4_hba.nvmels_wq, wqe)) { + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return WQE_ERROR; + } + lpfc_sli_ringtxcmpl_put(phba, pring, pwqe); + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return 0; + } + + /* NVME_FCREQ and NVME_ABTS requests */ + if (pwqe->iocb_flag & LPFC_IO_NVME) { + /* Get the IO distribution (hba_wqidx) for WQ assignment. */ + pring = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]->pring; + + spin_lock_irqsave(&pring->ring_lock, iflags); + wq = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]; + bf_set(wqe_cqid, &wqe->generic.wqe_com, + phba->sli4_hba.nvme_cq[pwqe->hba_wqidx]->queue_id); + if (lpfc_sli4_wq_put(wq, wqe)) { + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return WQE_ERROR; + } + lpfc_sli_ringtxcmpl_put(phba, pring, pwqe); + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return 0; + } + + /* NVMET requests */ + if (pwqe->iocb_flag & LPFC_IO_NVMET) { + /* Get the IO distribution (hba_wqidx) for WQ assignment. */ + pring = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]->pring; + + spin_lock_irqsave(&pring->ring_lock, iflags); + ctxp = pwqe->context2; + sglq = ctxp->rqb_buffer->sglq; + if (pwqe->sli4_xritag == NO_XRI) { + pwqe->sli4_lxritag = sglq->sli4_lxritag; + pwqe->sli4_xritag = sglq->sli4_xritag; + } + bf_set(wqe_xri_tag, &pwqe->wqe.xmit_bls_rsp.wqe_com, + pwqe->sli4_xritag); + wq = phba->sli4_hba.nvme_wq[pwqe->hba_wqidx]; + bf_set(wqe_cqid, &wqe->generic.wqe_com, + phba->sli4_hba.nvme_cq[pwqe->hba_wqidx]->queue_id); + if (lpfc_sli4_wq_put(wq, wqe)) { + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return WQE_ERROR; + } + lpfc_sli_ringtxcmpl_put(phba, pring, pwqe); + spin_unlock_irqrestore(&pring->ring_lock, iflags); + return 0; + } + return WQE_ERROR; +} diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 74227a28bd56..9085306ddd78 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -54,9 +56,16 @@ struct lpfc_iocbq { uint16_t iotag; /* pre-assigned IO tag */ uint16_t sli4_lxritag; /* logical pre-assigned XRI. */ uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */ + uint16_t hba_wqidx; /* index to HBA work queue */ struct lpfc_cq_event cq_event; + struct lpfc_wcqe_complete wcqe_cmpl; /* WQE cmpl */ + uint64_t isr_timestamp; - IOCB_t iocb; /* IOCB cmd */ + /* Be careful here */ + union lpfc_wqe wqe; /* WQE cmd */ + IOCB_t iocb; /* For IOCB cmd or if we want 128 byte WQE */ + + uint8_t rsvd2; uint8_t priority; /* OAS priority */ uint8_t retry; /* retry counter for IOCB cmd - if needed */ uint32_t iocb_flag; @@ -82,9 +91,13 @@ struct lpfc_iocbq { #define LPFC_IO_OAS 0x10000 /* OAS FCP IO */ #define LPFC_IO_FOF 0x20000 /* FOF FCP IO */ #define LPFC_IO_LOOPBACK 0x40000 /* Loopback IO */ +#define LPFC_PRLI_NVME_REQ 0x80000 /* This is an NVME PRLI. */ +#define LPFC_PRLI_FCP_REQ 0x100000 /* This is an NVME PRLI. */ +#define LPFC_IO_NVME 0x200000 /* NVME FCP command */ +#define LPFC_IO_NVME_LS 0x400000 /* NVME LS command */ +#define LPFC_IO_NVMET 0x800000 /* NVMET command */ uint32_t drvrTimeout; /* driver timeout in seconds */ - uint32_t fcp_wqidx; /* index to FCP work queue */ struct lpfc_vport *vport;/* virtual port pointer */ void *context1; /* caller context information */ void *context2; /* caller context information */ @@ -97,12 +110,14 @@ struct lpfc_iocbq { struct lpfc_node_rrq *rrq; } context_un; - void (*fabric_iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + void (*fabric_iocb_cmpl)(struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *); - void (*wait_iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + void (*wait_iocb_cmpl)(struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *); - void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + void (*iocb_cmpl)(struct lpfc_hba *, struct lpfc_iocbq *, struct lpfc_iocbq *); + void (*wqe_cmpl)(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_wcqe_complete *); }; #define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */ @@ -112,6 +127,14 @@ struct lpfc_iocbq { #define IOCB_ERROR 2 #define IOCB_TIMEDOUT 3 +#define SLI_WQE_RET_WQE 1 /* Return WQE if cmd ring full */ + +#define WQE_SUCCESS 0 +#define WQE_BUSY 1 +#define WQE_ERROR 2 +#define WQE_TIMEDOUT 3 +#define WQE_ABORTED 4 + #define LPFC_MBX_WAKE 1 #define LPFC_MBX_IMED_UNREG 2 @@ -297,12 +320,9 @@ struct lpfc_sli { #define LPFC_BLOCK_MGMT_IO 0x800 /* Don't allow mgmt mbx or iocb cmds */ #define LPFC_MENLO_MAINT 0x1000 /* need for menl fw download */ #define LPFC_SLI_ASYNC_MBX_BLK 0x2000 /* Async mailbox is blocked */ +#define LPFC_SLI_SUPPRESS_RSP 0x4000 /* Suppress RSP feature is supported */ - struct lpfc_sli_ring *ring; - int fcp_ring; /* ring used for FCP initiator commands */ - int next_ring; - - int extra_ring; /* extra ring used for other protocols */ + struct lpfc_sli_ring *sli3_ring; struct lpfc_sli_stat slistat; /* SLI statistical info */ struct list_head mboxq; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 0b88b5703e0f..91153c9f6d18 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -35,9 +37,10 @@ #define LPFC_NEMBED_MBOX_SGL_CNT 254 /* Multi-queue arrangement for FCP EQ/CQ/WQ tuples */ -#define LPFC_FCP_IO_CHAN_DEF 4 -#define LPFC_FCP_IO_CHAN_MIN 1 -#define LPFC_FCP_IO_CHAN_MAX 16 +#define LPFC_HBA_IO_CHAN_MIN 0 +#define LPFC_HBA_IO_CHAN_MAX 32 +#define LPFC_FCP_IO_CHAN_DEF 4 +#define LPFC_NVME_IO_CHAN_DEF 0 /* Number of channels used for Flash Optimized Fabric (FOF) operations */ @@ -107,6 +110,9 @@ enum lpfc_sli4_queue_subtype { LPFC_MBOX, LPFC_FCP, LPFC_ELS, + LPFC_NVME, + LPFC_NVMET, + LPFC_NVME_LS, LPFC_USOL }; @@ -125,25 +131,41 @@ union sli4_qe { struct lpfc_rqe *rqe; }; +/* RQ buffer list */ +struct lpfc_rqb { + uint16_t entry_count; /* Current number of RQ slots */ + uint16_t buffer_count; /* Current number of buffers posted */ + struct list_head rqb_buffer_list; /* buffers assigned to this HBQ */ + /* Callback for HBQ buffer allocation */ + struct rqb_dmabuf *(*rqb_alloc_buffer)(struct lpfc_hba *); + /* Callback for HBQ buffer free */ + void (*rqb_free_buffer)(struct lpfc_hba *, + struct rqb_dmabuf *); +}; + struct lpfc_queue { struct list_head list; + struct list_head wq_list; enum lpfc_sli4_queue_type type; enum lpfc_sli4_queue_subtype subtype; struct lpfc_hba *phba; struct list_head child_list; + struct list_head page_list; + struct list_head sgl_list; uint32_t entry_count; /* Number of entries to support on the queue */ uint32_t entry_size; /* Size of each queue entry. */ uint32_t entry_repost; /* Count of entries before doorbell is rung */ #define LPFC_QUEUE_MIN_REPOST 8 uint32_t queue_id; /* Queue ID assigned by the hardware */ uint32_t assoc_qid; /* Queue ID associated with, for CQ/WQ/MQ */ - struct list_head page_list; uint32_t page_count; /* Number of pages allocated for this queue */ uint32_t host_index; /* The host's index for putting or getting */ uint32_t hba_index; /* The last known hba index for get or put */ struct lpfc_sli_ring *pring; /* ptr to io ring associated with q */ + struct lpfc_rqb *rqbp; /* ptr to RQ buffers */ + uint16_t sgl_list_cnt; uint16_t db_format; #define LPFC_DB_RING_FORMAT 0x01 #define LPFC_DB_LIST_FORMAT 0x02 @@ -176,6 +198,8 @@ struct lpfc_queue { #define RQ_buf_trunc q_cnt_3 #define RQ_rcv_buf q_cnt_4 + uint64_t isr_timestamp; + struct lpfc_queue *assoc_qp; union sli4_qe qe[1]; /* array to index entries (must be last) */ }; @@ -338,6 +362,7 @@ struct lpfc_bmbx { #define LPFC_CQE_DEF_COUNT 1024 #define LPFC_WQE_DEF_COUNT 256 #define LPFC_WQE128_DEF_COUNT 128 +#define LPFC_WQE128_MAX_COUNT 256 #define LPFC_MQE_DEF_COUNT 16 #define LPFC_RQE_DEF_COUNT 512 @@ -379,10 +404,14 @@ struct lpfc_max_cfg_param { struct lpfc_hba; /* SLI4 HBA multi-fcp queue handler struct */ -struct lpfc_fcp_eq_hdl { +struct lpfc_hba_eq_hdl { uint32_t idx; struct lpfc_hba *phba; - atomic_t fcp_eq_in_use; + atomic_t hba_eq_in_use; + struct cpumask *cpumask; + /* CPU affinitsed to or 0xffffffff if multiple */ + uint32_t cpu; +#define LPFC_MULTI_CPU_AFFINITY 0xffffffff }; /* Port Capabilities for SLI4 Parameters */ @@ -427,6 +456,7 @@ struct lpfc_pc_sli4_params { uint8_t wqsize; #define LPFC_WQ_SZ64_SUPPORT 1 #define LPFC_WQ_SZ128_SUPPORT 2 + uint8_t wqpcnt; }; struct lpfc_iov { @@ -445,7 +475,7 @@ struct lpfc_sli4_lnk_info { uint8_t optic_state; }; -#define LPFC_SLI4_HANDLER_CNT (LPFC_FCP_IO_CHAN_MAX+ \ +#define LPFC_SLI4_HANDLER_CNT (LPFC_HBA_IO_CHAN_MAX+ \ LPFC_FOF_IO_CHAN_NUM) #define LPFC_SLI4_HANDLER_NAME_SZ 16 @@ -515,23 +545,34 @@ struct lpfc_sli4_hba { uint32_t ue_to_rp; struct lpfc_register sli_intf; struct lpfc_pc_sli4_params pc_sli4_params; - struct msix_entry *msix_entries; uint8_t handler_name[LPFC_SLI4_HANDLER_CNT][LPFC_SLI4_HANDLER_NAME_SZ]; - struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */ + struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */ /* Pointers to the constructed SLI4 queues */ - struct lpfc_queue **hba_eq;/* Event queues for HBA */ - struct lpfc_queue **fcp_cq;/* Fast-path FCP compl queue */ - struct lpfc_queue **fcp_wq;/* Fast-path FCP work queue */ + struct lpfc_queue **hba_eq; /* Event queues for HBA */ + struct lpfc_queue **fcp_cq; /* Fast-path FCP compl queue */ + struct lpfc_queue **nvme_cq; /* Fast-path NVME compl queue */ + struct lpfc_queue **nvmet_cqset; /* Fast-path NVMET CQ Set queues */ + struct lpfc_queue **nvmet_mrq_hdr; /* Fast-path NVMET hdr MRQs */ + struct lpfc_queue **nvmet_mrq_data; /* Fast-path NVMET data MRQs */ + struct lpfc_queue **fcp_wq; /* Fast-path FCP work queue */ + struct lpfc_queue **nvme_wq; /* Fast-path NVME work queue */ uint16_t *fcp_cq_map; + uint16_t *nvme_cq_map; + struct list_head lpfc_wq_list; struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */ struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */ + struct lpfc_queue *nvmels_cq; /* NVME LS complete queue */ struct lpfc_queue *mbx_wq; /* Slow-path MBOX work queue */ struct lpfc_queue *els_wq; /* Slow-path ELS work queue */ + struct lpfc_queue *nvmels_wq; /* NVME LS work queue */ struct lpfc_queue *hdr_rq; /* Slow-path Header Receive queue */ struct lpfc_queue *dat_rq; /* Slow-path Data Receive queue */ + struct lpfc_name wwnn; + struct lpfc_name wwpn; + uint32_t fw_func_mode; /* FW function protocol mode */ uint32_t ulp0_mode; /* ULP0 protocol mode */ uint32_t ulp1_mode; /* ULP1 protocol mode */ @@ -568,14 +609,20 @@ struct lpfc_sli4_hba { uint16_t rpi_hdrs_in_use; /* must post rpi hdrs if set. */ uint16_t next_xri; /* last_xri - max_cfg_param.xri_base = used */ uint16_t next_rpi; + uint16_t nvme_xri_max; + uint16_t nvme_xri_cnt; + uint16_t nvme_xri_start; uint16_t scsi_xri_max; uint16_t scsi_xri_cnt; - uint16_t els_xri_cnt; uint16_t scsi_xri_start; - struct list_head lpfc_free_sgl_list; - struct list_head lpfc_sgl_list; + uint16_t els_xri_cnt; + uint16_t nvmet_xri_cnt; + struct list_head lpfc_els_sgl_list; struct list_head lpfc_abts_els_sgl_list; + struct list_head lpfc_nvmet_sgl_list; + struct list_head lpfc_abts_nvmet_sgl_list; struct list_head lpfc_abts_scsi_buf_list; + struct list_head lpfc_abts_nvme_buf_list; struct lpfc_sglq **lpfc_sglq_active_list; struct list_head lpfc_rpi_hdr_list; unsigned long *rpi_bmask; @@ -602,8 +649,10 @@ struct lpfc_sli4_hba { #define LPFC_SLI4_PPNAME_NON 0 #define LPFC_SLI4_PPNAME_GET 1 struct lpfc_iov iov; + spinlock_t abts_nvme_buf_list_lock; /* list of aborted SCSI IOs */ spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ - spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */ + spinlock_t sgl_list_lock; /* list of aborted els IOs */ + spinlock_t nvmet_io_lock; uint32_t physical_port; /* CPU to vector mapping information */ @@ -611,11 +660,14 @@ struct lpfc_sli4_hba { uint16_t num_online_cpu; uint16_t num_present_cpu; uint16_t curr_disp_cpu; + + uint16_t nvmet_mrq_post_idx; }; enum lpfc_sge_type { GEN_BUFF_TYPE, - SCSI_BUFF_TYPE + SCSI_BUFF_TYPE, + NVMET_BUFF_TYPE }; enum lpfc_sgl_state { @@ -694,15 +746,21 @@ struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t, uint32_t); void lpfc_sli4_queue_free(struct lpfc_queue *); int lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint32_t); -int lpfc_modify_fcp_eq_delay(struct lpfc_hba *, uint32_t); +int lpfc_modify_hba_eq_delay(struct lpfc_hba *phba, uint32_t startq); int lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t, uint32_t); +int lpfc_cq_create_set(struct lpfc_hba *phba, struct lpfc_queue **cqp, + struct lpfc_queue **eqp, uint32_t type, + uint32_t subtype); int32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t); int lpfc_wq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t); int lpfc_rq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, struct lpfc_queue *, uint32_t); +int lpfc_mrq_create(struct lpfc_hba *phba, struct lpfc_queue **hrqp, + struct lpfc_queue **drqp, struct lpfc_queue **cqp, + uint32_t subtype); void lpfc_rq_adjust_repost(struct lpfc_hba *, struct lpfc_queue *, int); int lpfc_eq_destroy(struct lpfc_hba *, struct lpfc_queue *); int lpfc_cq_destroy(struct lpfc_hba *, struct lpfc_queue *); @@ -714,6 +772,7 @@ int lpfc_sli4_queue_setup(struct lpfc_hba *); void lpfc_sli4_queue_unset(struct lpfc_hba *); int lpfc_sli4_post_sgl(struct lpfc_hba *, dma_addr_t, dma_addr_t, uint16_t); int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *); +int lpfc_repost_nvme_sgl_list(struct lpfc_hba *phba); uint16_t lpfc_sli4_next_xritag(struct lpfc_hba *); void lpfc_sli4_free_xri(struct lpfc_hba *, int); int lpfc_sli4_post_async_mbox(struct lpfc_hba *); @@ -746,6 +805,7 @@ int lpfc_sli4_brdreset(struct lpfc_hba *); int lpfc_sli4_add_fcf_record(struct lpfc_hba *, struct fcf_record *); void lpfc_sli_remove_dflt_fcf(struct lpfc_hba *); int lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *); +int lpfc_sli4_get_iocb_cnt(struct lpfc_hba *phba); int lpfc_sli4_init_vpi(struct lpfc_vport *); uint32_t lpfc_sli4_cq_release(struct lpfc_queue *, bool); uint32_t lpfc_sli4_eq_release(struct lpfc_queue *, bool); diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 0ee0623a354c..86c6c9b26b82 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * @@ -18,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "11.2.0.4" +#define LPFC_DRIVER_VERSION "11.2.0.7" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ @@ -30,4 +32,6 @@ #define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \ LPFC_DRIVER_VERSION -#define LPFC_COPYRIGHT "Copyright(c) 2004-2016 Emulex. All rights reserved." +#define LPFC_COPYRIGHT "Copyright (C) 2017 Broadcom. All Rights Reserved. " \ + "The term \"Broadcom\" refers to Broadcom Limited " \ + "and/or its subsidiaries." diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index e18bbc66e83b..9a0339dbc024 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * @@ -28,11 +30,13 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/sched/signal.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> + #include "lpfc_hw4.h" #include "lpfc_hw.h" #include "lpfc_sli.h" @@ -402,6 +406,22 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) vport->fdmi_port_mask = phba->pport->fdmi_port_mask; } + if ((phba->nvmet_support == 0) && + ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || + (phba->cfg_enable_fc4_type == LPFC_ENABLE_NVME))) { + /* Create NVME binding with nvme_fc_transport. This + * ensures the vport is initialized. + */ + rc = lpfc_nvme_create_localport(vport); + if (rc) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "6003 %s status x%x\n", + "NVME registration failed, ", + rc); + goto error_out; + } + } + /* * In SLI4, the vpi must be activated before it can be used * by the port. diff --git a/drivers/scsi/lpfc/lpfc_vport.h b/drivers/scsi/lpfc/lpfc_vport.h index 6b2c94eb8134..62295971f66c 100644 --- a/drivers/scsi/lpfc/lpfc_vport.h +++ b/drivers/scsi/lpfc/lpfc_vport.h @@ -1,9 +1,11 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * + * Copyright (C) 2017 Broadcom. All Rights Reserved. The term * + * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * * Copyright (C) 2004-2006 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * - * www.emulex.com * + * www.broadcom.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index a3fe1fb55c17..5b7aec5d575a 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -1148,7 +1148,7 @@ mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc) /* TMs are on msix_index == 0 */ if (reply_q->msix_index == 0) continue; - synchronize_irq(reply_q->vector); + synchronize_irq(pci_irq_vector(ioc->pdev, reply_q->msix_index)); } } @@ -1837,11 +1837,8 @@ _base_free_irq(struct MPT3SAS_ADAPTER *ioc) list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) { list_del(&reply_q->list); - if (smp_affinity_enable) { - irq_set_affinity_hint(reply_q->vector, NULL); - free_cpumask_var(reply_q->affinity_hint); - } - free_irq(reply_q->vector, reply_q); + free_irq(pci_irq_vector(ioc->pdev, reply_q->msix_index), + reply_q); kfree(reply_q); } } @@ -1850,13 +1847,13 @@ _base_free_irq(struct MPT3SAS_ADAPTER *ioc) * _base_request_irq - request irq * @ioc: per adapter object * @index: msix index into vector table - * @vector: irq vector * * Inserting respective reply_queue into the list. */ static int -_base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index, u32 vector) +_base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index) { + struct pci_dev *pdev = ioc->pdev; struct adapter_reply_queue *reply_q; int r; @@ -1868,14 +1865,6 @@ _base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index, u32 vector) } reply_q->ioc = ioc; reply_q->msix_index = index; - reply_q->vector = vector; - - if (smp_affinity_enable) { - if (!zalloc_cpumask_var(&reply_q->affinity_hint, GFP_KERNEL)) { - kfree(reply_q); - return -ENOMEM; - } - } atomic_set(&reply_q->busy, 0); if (ioc->msix_enable) @@ -1884,12 +1873,11 @@ _base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index, u32 vector) else snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d", ioc->driver_name, ioc->id); - r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name, - reply_q); + r = request_irq(pci_irq_vector(pdev, index), _base_interrupt, + IRQF_SHARED, reply_q->name, reply_q); if (r) { pr_err(MPT3SAS_FMT "unable to allocate interrupt %d!\n", - reply_q->name, vector); - free_cpumask_var(reply_q->affinity_hint); + reply_q->name, pci_irq_vector(pdev, index)); kfree(reply_q); return -EBUSY; } @@ -1925,6 +1913,21 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) if (!nr_msix) return; + if (smp_affinity_enable) { + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { + const cpumask_t *mask = pci_irq_get_affinity(ioc->pdev, + reply_q->msix_index); + if (!mask) { + pr_warn(MPT3SAS_FMT "no affinity for msi %x\n", + ioc->name, reply_q->msix_index); + continue; + } + + for_each_cpu(cpu, mask) + ioc->cpu_msix_table[cpu] = reply_q->msix_index; + } + return; + } cpu = cpumask_first(cpu_online_mask); list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { @@ -1938,18 +1941,9 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) group++; for (i = 0 ; i < group ; i++) { - ioc->cpu_msix_table[cpu] = index; - if (smp_affinity_enable) - cpumask_or(reply_q->affinity_hint, - reply_q->affinity_hint, get_cpu_mask(cpu)); + ioc->cpu_msix_table[cpu] = reply_q->msix_index; cpu = cpumask_next(cpu, cpu_online_mask); } - if (smp_affinity_enable) - if (irq_set_affinity_hint(reply_q->vector, - reply_q->affinity_hint)) - dinitprintk(ioc, pr_info(MPT3SAS_FMT - "Err setting affinity hint to irq vector %d\n", - ioc->name, reply_q->vector)); index++; } } @@ -1976,10 +1970,10 @@ _base_disable_msix(struct MPT3SAS_ADAPTER *ioc) static int _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) { - struct msix_entry *entries, *a; int r; int i, local_max_msix_vectors; u8 try_msix = 0; + unsigned int irq_flags = PCI_IRQ_MSIX; if (msix_disable == -1 || msix_disable == 0) try_msix = 1; @@ -1991,7 +1985,7 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) goto try_ioapic; ioc->reply_queue_count = min_t(int, ioc->cpu_count, - ioc->msix_vector_count); + ioc->msix_vector_count); printk(MPT3SAS_FMT "MSI-X vectors supported: %d, no of cores" ": %d, max_msix_vectors: %d\n", ioc->name, ioc->msix_vector_count, @@ -2002,56 +1996,51 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) else local_max_msix_vectors = max_msix_vectors; - if (local_max_msix_vectors > 0) { + if (local_max_msix_vectors > 0) ioc->reply_queue_count = min_t(int, local_max_msix_vectors, ioc->reply_queue_count); - ioc->msix_vector_count = ioc->reply_queue_count; - } else if (local_max_msix_vectors == 0) + else if (local_max_msix_vectors == 0) goto try_ioapic; if (ioc->msix_vector_count < ioc->cpu_count) smp_affinity_enable = 0; - entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry), - GFP_KERNEL); - if (!entries) { - dfailprintk(ioc, pr_info(MPT3SAS_FMT - "kcalloc failed @ at %s:%d/%s() !!!\n", - ioc->name, __FILE__, __LINE__, __func__)); - goto try_ioapic; - } + if (smp_affinity_enable) + irq_flags |= PCI_IRQ_AFFINITY; - for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) - a->entry = i; - - r = pci_enable_msix_exact(ioc->pdev, entries, ioc->reply_queue_count); - if (r) { + r = pci_alloc_irq_vectors(ioc->pdev, 1, ioc->reply_queue_count, + irq_flags); + if (r < 0) { dfailprintk(ioc, pr_info(MPT3SAS_FMT - "pci_enable_msix_exact failed (r=%d) !!!\n", + "pci_alloc_irq_vectors failed (r=%d) !!!\n", ioc->name, r)); - kfree(entries); goto try_ioapic; } ioc->msix_enable = 1; - for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) { - r = _base_request_irq(ioc, i, a->vector); + ioc->reply_queue_count = r; + for (i = 0; i < ioc->reply_queue_count; i++) { + r = _base_request_irq(ioc, i); if (r) { _base_free_irq(ioc); _base_disable_msix(ioc); - kfree(entries); goto try_ioapic; } } - kfree(entries); return 0; /* failback to io_apic interrupt routing */ try_ioapic: ioc->reply_queue_count = 1; - r = _base_request_irq(ioc, 0, ioc->pdev->irq); + r = pci_alloc_irq_vectors(ioc->pdev, 1, 1, PCI_IRQ_LEGACY); + if (r < 0) { + dfailprintk(ioc, pr_info(MPT3SAS_FMT + "pci_alloc_irq_vector(legacy) failed (r=%d) !!!\n", + ioc->name, r)); + } else + r = _base_request_irq(ioc, 0); return r; } @@ -2222,7 +2211,8 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) list_for_each_entry(reply_q, &ioc->reply_queue_list, list) pr_info(MPT3SAS_FMT "%s: IRQ %d\n", reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : - "IO-APIC enabled"), reply_q->vector); + "IO-APIC enabled"), + pci_irq_vector(ioc->pdev, reply_q->msix_index)); pr_info(MPT3SAS_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n", ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz); @@ -5357,7 +5347,8 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) sizeof(resource_size_t *), GFP_KERNEL); if (!ioc->reply_post_host_index) { dfailprintk(ioc, pr_info(MPT3SAS_FMT "allocation " - "for cpu_msix_table failed!!!\n", ioc->name)); + "for reply_post_host_index failed!!!\n", + ioc->name)); r = -ENOMEM; goto out_free_resources; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 4ab634fc27df..7fe7e6ed595b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -731,12 +731,10 @@ struct _event_ack_list { struct adapter_reply_queue { struct MPT3SAS_ADAPTER *ioc; u8 msix_index; - unsigned int vector; u32 reply_post_host_index; Mpi2ReplyDescriptorsUnion_t *reply_post_free; char name[MPT_NAME_LENGTH]; atomic_t busy; - cpumask_var_t affinity_hint; struct list_head list; }; diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index 02fe1c4aae2f..bdffb692bded 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -1925,7 +1925,7 @@ mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, * * This allows ownership of the specified buffer to returned to the driver, * allowing an application to read the buffer without fear that firmware is - * overwritting information in the buffer. + * overwriting information in the buffer. */ static long _ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg) diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.h b/drivers/scsi/mpt3sas/mpt3sas_ctl.h index f3e17a8c1b07..a44046cff0f3 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.h +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.h @@ -390,7 +390,7 @@ struct mpt3_diag_query { * * This allows ownership of the specified buffer to returned to the driver, * allowing an application to read the buffer without fear that firmware is - * overwritting information in the buffer. + * overwriting information in the buffer. */ struct mpt3_diag_release { struct mpt3_ioctl_header hdr; diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index 30b905080c61..6903f03c88af 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c @@ -1290,7 +1290,7 @@ int osd_req_add_get_attr_list(struct osd_request *or, or->enc_get_attr.total_bytes = total_bytes; OSD_DEBUG( - "get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%Zu)\n", + "get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%zu)\n", or->get_attr.total_bytes, or->get_attr.total_bytes - _osd_req_sizeof_alist_header(or), or->enc_get_attr.total_bytes, @@ -1677,7 +1677,7 @@ int osd_finalize_request(struct osd_request *or, } } else { /* TODO: I think that for the GET_ATTR command these 2 should - * be reversed to keep them in execution order (for embeded + * be reversed to keep them in execution order (for embedded * targets with low memory footprint) */ ret = _osd_req_finalize_set_attr_list(or); diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index 243eab3d10d0..e0ce5d2fd14d 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c @@ -372,6 +372,7 @@ EXPORT_SYMBOL(osduld_device_same); static int __detect_osd(struct osd_uld_device *oud) { struct scsi_device *scsi_device = oud->od.scsi_device; + struct scsi_sense_hdr sense_hdr; char caps[OSD_CAP_LEN]; int error; @@ -380,7 +381,7 @@ static int __detect_osd(struct osd_uld_device *oud) */ OSD_DEBUG("start scsi_test_unit_ready %p %p %p\n", oud, scsi_device, scsi_device->request_queue); - error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, NULL); + error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, &sense_hdr); if (error) OSD_ERR("warning: scsi_test_unit_ready failed\n"); diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 451de6c5e3c9..c47f4b349bac 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -35,7 +35,7 @@ static const char * osst_version = "0.99.4"; #include <linux/fs.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/proc_fs.h> #include <linux/mm.h> #include <linux/slab.h> @@ -3435,7 +3435,7 @@ static ssize_t osst_write(struct file * filp, const char __user * buf, size_t co /* Write must be integral number of blocks */ if (STp->block_size != 0 && (count % STp->block_size) != 0) { - printk(KERN_ERR "%s:E: Write (%Zd bytes) not multiple of tape block size (%d%c).\n", + printk(KERN_ERR "%s:E: Write (%zd bytes) not multiple of tape block size (%d%c).\n", name, count, STp->block_size<1024? STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k'); retval = (-EINVAL); @@ -3756,7 +3756,7 @@ static ssize_t osst_read(struct file * filp, char __user * buf, size_t count, lo if ((count % STp->block_size) != 0) { printk(KERN_WARNING - "%s:W: Read (%Zd bytes) not multiple of tape block size (%d%c).\n", name, count, + "%s:W: Read (%zd bytes) not multiple of tape block size (%d%c).\n", name, count, STp->block_size<1024?STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k'); } @@ -3815,7 +3815,7 @@ static ssize_t osst_read(struct file * filp, char __user * buf, size_t count, lo if (transfer == 0) { printk(KERN_WARNING - "%s:W: Nothing can be transferred, requested %Zd, tape block size (%d%c).\n", + "%s:W: Nothing can be transferred, requested %zd, tape block size (%d%c).\n", name, count, STp->block_size < 1024? STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k'); diff --git a/drivers/scsi/qedf/Kconfig b/drivers/scsi/qedf/Kconfig new file mode 100644 index 000000000000..943f5ee45807 --- /dev/null +++ b/drivers/scsi/qedf/Kconfig @@ -0,0 +1,11 @@ +config QEDF + tristate "QLogic QEDF 25/40/100Gb FCoE Initiator Driver Support" + depends on PCI && SCSI + depends on QED + depends on LIBFC + depends on LIBFCOE + select QED_LL2 + select QED_FCOE + ---help--- + This driver supports FCoE offload for the QLogic FastLinQ + 41000 Series Converged Network Adapters. diff --git a/drivers/scsi/qedf/Makefile b/drivers/scsi/qedf/Makefile new file mode 100644 index 000000000000..64e9f507ce32 --- /dev/null +++ b/drivers/scsi/qedf/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_QEDF) := qedf.o +qedf-y = qedf_dbg.o qedf_main.o qedf_io.o qedf_fip.o \ + qedf_attr.o qedf_els.o + +qedf-$(CONFIG_DEBUG_FS) += qedf_debugfs.o diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h new file mode 100644 index 000000000000..96346a1b1515 --- /dev/null +++ b/drivers/scsi/qedf/qedf.h @@ -0,0 +1,545 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#ifndef _QEDFC_H_ +#define _QEDFC_H_ + +#include <scsi/libfcoe.h> +#include <scsi/libfc.h> +#include <scsi/fc/fc_fip.h> +#include <scsi/fc/fc_fc2.h> +#include <scsi/scsi_tcq.h> +#include <scsi/fc_encode.h> +#include <linux/version.h> + + +/* qedf_hsi.h needs to before included any qed includes */ +#include "qedf_hsi.h" + +#include <linux/qed/qed_if.h> +#include <linux/qed/qed_fcoe_if.h> +#include <linux/qed/qed_ll2_if.h> +#include "qedf_version.h" +#include "qedf_dbg.h" + +/* Helpers to extract upper and lower 32-bits of pointer */ +#define U64_HI(val) ((u32)(((u64)(val)) >> 32)) +#define U64_LO(val) ((u32)(((u64)(val)) & 0xffffffff)) + +#define QEDF_DESCR "QLogic FCoE Offload Driver" +#define QEDF_MODULE_NAME "qedf" + +#define QEDF_MIN_XID 0 +#define QEDF_MAX_SCSI_XID (NUM_TASKS_PER_CONNECTION - 1) +#define QEDF_MAX_ELS_XID 4095 +#define QEDF_FLOGI_RETRY_CNT 3 +#define QEDF_RPORT_RETRY_CNT 255 +#define QEDF_MAX_SESSIONS 1024 +#define QEDF_MAX_PAYLOAD 2048 +#define QEDF_MAX_BDS_PER_CMD 256 +#define QEDF_MAX_BD_LEN 0xffff +#define QEDF_BD_SPLIT_SZ 0x1000 +#define QEDF_PAGE_SIZE 4096 +#define QED_HW_DMA_BOUNDARY 0xfff +#define QEDF_MAX_SGLEN_FOR_CACHESGL ((1U << 16) - 1) +#define QEDF_MFS (QEDF_MAX_PAYLOAD + \ + sizeof(struct fc_frame_header)) +#define QEDF_MAX_NPIV 64 +#define QEDF_TM_TIMEOUT 10 +#define QEDF_ABORT_TIMEOUT 10 +#define QEDF_CLEANUP_TIMEOUT 10 +#define QEDF_MAX_CDB_LEN 16 + +#define UPSTREAM_REMOVE 1 +#define UPSTREAM_KEEP 1 + +struct qedf_mp_req { + uint8_t tm_flags; + + uint32_t req_len; + void *req_buf; + dma_addr_t req_buf_dma; + struct fcoe_sge *mp_req_bd; + dma_addr_t mp_req_bd_dma; + struct fc_frame_header req_fc_hdr; + + uint32_t resp_len; + void *resp_buf; + dma_addr_t resp_buf_dma; + struct fcoe_sge *mp_resp_bd; + dma_addr_t mp_resp_bd_dma; + struct fc_frame_header resp_fc_hdr; +}; + +struct qedf_els_cb_arg { + struct qedf_ioreq *aborted_io_req; + struct qedf_ioreq *io_req; + u8 op; /* Used to keep track of ELS op */ + uint16_t l2_oxid; + u32 offset; /* Used for sequence cleanup */ + u8 r_ctl; /* Used for sequence cleanup */ +}; + +enum qedf_ioreq_event { + QEDF_IOREQ_EV_ABORT_SUCCESS, + QEDF_IOREQ_EV_ABORT_FAILED, + QEDF_IOREQ_EV_SEND_RRQ, + QEDF_IOREQ_EV_ELS_TMO, + QEDF_IOREQ_EV_ELS_ERR_DETECT, + QEDF_IOREQ_EV_ELS_FLUSH, + QEDF_IOREQ_EV_CLEANUP_SUCCESS, + QEDF_IOREQ_EV_CLEANUP_FAILED, +}; + +#define FC_GOOD 0 +#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER (0x1<<2) +#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER (0x1<<3) +#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status) +#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID (0x1<<0) +#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID (0x1<<1) +struct qedf_ioreq { + struct list_head link; + uint16_t xid; + struct scsi_cmnd *sc_cmd; + bool use_slowpath; /* Use slow SGL for this I/O */ +#define QEDF_SCSI_CMD 1 +#define QEDF_TASK_MGMT_CMD 2 +#define QEDF_ABTS 3 +#define QEDF_ELS 4 +#define QEDF_CLEANUP 5 +#define QEDF_SEQ_CLEANUP 6 + u8 cmd_type; +#define QEDF_CMD_OUTSTANDING 0x0 +#define QEDF_CMD_IN_ABORT 0x1 +#define QEDF_CMD_IN_CLEANUP 0x2 +#define QEDF_CMD_SRR_SENT 0x3 + u8 io_req_flags; + struct qedf_rport *fcport; + unsigned long flags; + enum qedf_ioreq_event event; + size_t data_xfer_len; + struct kref refcount; + struct qedf_cmd_mgr *cmd_mgr; + struct io_bdt *bd_tbl; + struct delayed_work timeout_work; + struct completion tm_done; + struct completion abts_done; + struct fcoe_task_context *task; + int idx; +/* + * Need to allocate enough room for both sense data and FCP response data + * which has a max length of 8 bytes according to spec. + */ +#define QEDF_SCSI_SENSE_BUFFERSIZE (SCSI_SENSE_BUFFERSIZE + 8) + uint8_t *sense_buffer; + dma_addr_t sense_buffer_dma; + u32 fcp_resid; + u32 fcp_rsp_len; + u32 fcp_sns_len; + u8 cdb_status; + u8 fcp_status; + u8 fcp_rsp_code; + u8 scsi_comp_flags; +#define QEDF_MAX_REUSE 0xfff + u16 reuse_count; + struct qedf_mp_req mp_req; + void (*cb_func)(struct qedf_els_cb_arg *cb_arg); + struct qedf_els_cb_arg *cb_arg; + int fp_idx; + unsigned int cpu; + unsigned int int_cpu; +#define QEDF_IOREQ_SLOW_SGE 0 +#define QEDF_IOREQ_SINGLE_SGE 1 +#define QEDF_IOREQ_FAST_SGE 2 + u8 sge_type; + struct delayed_work rrq_work; + + /* Used for sequence level recovery; i.e. REC/SRR */ + uint32_t rx_buf_off; + uint32_t tx_buf_off; + uint32_t rx_id; + uint32_t task_retry_identifier; + + /* + * Used to tell if we need to return a SCSI command + * during some form of error processing. + */ + bool return_scsi_cmd_on_abts; +}; + +extern struct workqueue_struct *qedf_io_wq; + +struct qedf_rport { + spinlock_t rport_lock; +#define QEDF_RPORT_SESSION_READY 1 +#define QEDF_RPORT_UPLOADING_CONNECTION 2 + unsigned long flags; + unsigned long retry_delay_timestamp; + struct fc_rport *rport; + struct fc_rport_priv *rdata; + struct qedf_ctx *qedf; + u32 handle; /* Handle from qed */ + u32 fw_cid; /* fw_cid from qed */ + void __iomem *p_doorbell; + /* Send queue management */ + atomic_t free_sqes; + atomic_t num_active_ios; + struct fcoe_wqe *sq; + dma_addr_t sq_dma; + u16 sq_prod_idx; + u16 fw_sq_prod_idx; + u16 sq_con_idx; + u32 sq_mem_size; + void *sq_pbl; + dma_addr_t sq_pbl_dma; + u32 sq_pbl_size; + u32 sid; +#define QEDF_RPORT_TYPE_DISK 1 +#define QEDF_RPORT_TYPE_TAPE 2 + uint dev_type; /* Disk or tape */ + struct list_head peers; +}; + +/* Used to contain LL2 skb's in ll2_skb_list */ +struct qedf_skb_work { + struct work_struct work; + struct sk_buff *skb; + struct qedf_ctx *qedf; +}; + +struct qedf_fastpath { +#define QEDF_SB_ID_NULL 0xffff + u16 sb_id; + struct qed_sb_info *sb_info; + struct qedf_ctx *qedf; + /* Keep track of number of completions on this fastpath */ + unsigned long completions; + uint32_t cq_num_entries; +}; + +/* Used to pass fastpath information needed to process CQEs */ +struct qedf_io_work { + struct work_struct work; + struct fcoe_cqe cqe; + struct qedf_ctx *qedf; + struct fc_frame *fp; +}; + +struct qedf_glbl_q_params { + u64 hw_p_cq; /* Completion queue PBL */ + u64 hw_p_rq; /* Request queue PBL */ + u64 hw_p_cmdq; /* Command queue PBL */ +}; + +struct global_queue { + struct fcoe_cqe *cq; + dma_addr_t cq_dma; + u32 cq_mem_size; + u32 cq_cons_idx; /* Completion queue consumer index */ + u32 cq_prod_idx; + + void *cq_pbl; + dma_addr_t cq_pbl_dma; + u32 cq_pbl_size; +}; + +/* I/O tracing entry */ +#define QEDF_IO_TRACE_SIZE 2048 +struct qedf_io_log { +#define QEDF_IO_TRACE_REQ 0 +#define QEDF_IO_TRACE_RSP 1 + uint8_t direction; + uint16_t task_id; + uint32_t port_id; /* Remote port fabric ID */ + int lun; + char op; /* SCSI CDB */ + uint8_t lba[4]; + unsigned int bufflen; /* SCSI buffer length */ + unsigned int sg_count; /* Number of SG elements */ + int result; /* Result passed back to mid-layer */ + unsigned long jiffies; /* Time stamp when I/O logged */ + int refcount; /* Reference count for task id */ + unsigned int req_cpu; /* CPU that the task is queued on */ + unsigned int int_cpu; /* Interrupt CPU that the task is received on */ + unsigned int rsp_cpu; /* CPU that task is returned on */ + u8 sge_type; /* Did we take the slow, single or fast SGE path */ +}; + +/* Number of entries in BDQ */ +#define QEDF_BDQ_SIZE 256 +#define QEDF_BDQ_BUF_SIZE 2072 + +/* DMA coherent buffers for BDQ */ +struct qedf_bdq_buf { + void *buf_addr; + dma_addr_t buf_dma; +}; + +/* Main adapter struct */ +struct qedf_ctx { + struct qedf_dbg_ctx dbg_ctx; + struct fcoe_ctlr ctlr; + struct fc_lport *lport; + u8 data_src_addr[ETH_ALEN]; +#define QEDF_LINK_DOWN 0 +#define QEDF_LINK_UP 1 + atomic_t link_state; +#define QEDF_DCBX_PENDING 0 +#define QEDF_DCBX_DONE 1 + atomic_t dcbx; + uint16_t max_scsi_xid; + uint16_t max_els_xid; +#define QEDF_NULL_VLAN_ID -1 +#define QEDF_FALLBACK_VLAN 1002 +#define QEDF_DEFAULT_PRIO 3 + int vlan_id; + uint vlan_hw_insert:1; + struct qed_dev *cdev; + struct qed_dev_fcoe_info dev_info; + struct qed_int_info int_info; + uint16_t last_command; + spinlock_t hba_lock; + struct pci_dev *pdev; + u64 wwnn; + u64 wwpn; + u8 __aligned(16) mac[ETH_ALEN]; + struct list_head fcports; + atomic_t num_offloads; + unsigned int curr_conn_id; + struct workqueue_struct *ll2_recv_wq; + struct workqueue_struct *link_update_wq; + struct delayed_work link_update; + struct delayed_work link_recovery; + struct completion flogi_compl; + struct completion fipvlan_compl; + + /* + * Used to tell if we're in the window where we are waiting for + * the link to come back up before informting fcoe that the link is + * done. + */ + atomic_t link_down_tmo_valid; +#define QEDF_TIMER_INTERVAL (1 * HZ) + struct timer_list timer; /* One second book keeping timer */ +#define QEDF_DRAIN_ACTIVE 1 +#define QEDF_LL2_STARTED 2 +#define QEDF_UNLOADING 3 +#define QEDF_GRCDUMP_CAPTURE 4 +#define QEDF_IN_RECOVERY 5 +#define QEDF_DBG_STOP_IO 6 + unsigned long flags; /* Miscellaneous state flags */ + int fipvlan_retries; + u8 num_queues; + struct global_queue **global_queues; + /* Pointer to array of queue structures */ + struct qedf_glbl_q_params *p_cpuq; + /* Physical address of array of queue structures */ + dma_addr_t hw_p_cpuq; + + struct qedf_bdq_buf bdq[QEDF_BDQ_SIZE]; + void *bdq_pbl; + dma_addr_t bdq_pbl_dma; + size_t bdq_pbl_mem_size; + void *bdq_pbl_list; + dma_addr_t bdq_pbl_list_dma; + u8 bdq_pbl_list_num_entries; + void __iomem *bdq_primary_prod; + void __iomem *bdq_secondary_prod; + uint16_t bdq_prod_idx; + + /* Structure for holding all the fastpath for this qedf_ctx */ + struct qedf_fastpath *fp_array; + struct qed_fcoe_tid tasks; + struct qedf_cmd_mgr *cmd_mgr; + /* Holds the PF parameters we pass to qed to start he FCoE function */ + struct qed_pf_params pf_params; + /* Used to time middle path ELS and TM commands */ + struct workqueue_struct *timer_work_queue; + +#define QEDF_IO_WORK_MIN 64 + mempool_t *io_mempool; + struct workqueue_struct *dpc_wq; + + u32 slow_sge_ios; + u32 fast_sge_ios; + u32 single_sge_ios; + + uint8_t *grcdump; + uint32_t grcdump_size; + + struct qedf_io_log io_trace_buf[QEDF_IO_TRACE_SIZE]; + spinlock_t io_trace_lock; + uint16_t io_trace_idx; + + bool stop_io_on_error; + + u32 flogi_cnt; + u32 flogi_failed; + + /* Used for fc statistics */ + u64 input_requests; + u64 output_requests; + u64 control_requests; + u64 packet_aborts; + u64 alloc_failures; +}; + +struct io_bdt { + struct qedf_ioreq *io_req; + struct fcoe_sge *bd_tbl; + dma_addr_t bd_tbl_dma; + u16 bd_valid; +}; + +struct qedf_cmd_mgr { + struct qedf_ctx *qedf; + u16 idx; + struct io_bdt **io_bdt_pool; +#define FCOE_PARAMS_NUM_TASKS 4096 + struct qedf_ioreq cmds[FCOE_PARAMS_NUM_TASKS]; + spinlock_t lock; + atomic_t free_list_cnt; +}; + +/* Stolen from qed_cxt_api.h and adapted for qed_fcoe_info + * Usage: + * + * void *ptr; + * ptr = qedf_get_task_mem(&qedf->tasks, 128); + */ +static inline void *qedf_get_task_mem(struct qed_fcoe_tid *info, u32 tid) +{ + return (void *)(info->blocks[tid / info->num_tids_per_block] + + (tid % info->num_tids_per_block) * info->size); +} + +static inline void qedf_stop_all_io(struct qedf_ctx *qedf) +{ + set_bit(QEDF_DBG_STOP_IO, &qedf->flags); +} + +/* + * Externs + */ +#define QEDF_DEFAULT_LOG_MASK 0x3CFB6 +extern const struct qed_fcoe_ops *qed_ops; +extern uint qedf_dump_frames; +extern uint qedf_io_tracing; +extern uint qedf_stop_io_on_error; +extern uint qedf_link_down_tmo; +#define QEDF_RETRY_DELAY_MAX 20 /* 2 seconds */ +extern bool qedf_retry_delay; +extern uint qedf_debug; + +extern struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf); +extern void qedf_cmd_mgr_free(struct qedf_cmd_mgr *cmgr); +extern int qedf_queuecommand(struct Scsi_Host *host, + struct scsi_cmnd *sc_cmd); +extern void qedf_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb); +extern void qedf_update_src_mac(struct fc_lport *lport, u8 *addr); +extern u8 *qedf_get_src_mac(struct fc_lport *lport); +extern void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb); +extern void qedf_fcoe_send_vlan_req(struct qedf_ctx *qedf); +extern void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req); +extern void qedf_process_warning_compl(struct qedf_ctx *qedf, + struct fcoe_cqe *cqe, struct qedf_ioreq *io_req); +extern void qedf_process_error_detect(struct qedf_ctx *qedf, + struct fcoe_cqe *cqe, struct qedf_ioreq *io_req); +extern void qedf_flush_active_ios(struct qedf_rport *fcport, int lun); +extern void qedf_release_cmd(struct kref *ref); +extern int qedf_initiate_abts(struct qedf_ioreq *io_req, + bool return_scsi_cmd_on_abts); +extern void qedf_process_abts_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req); +extern struct qedf_ioreq *qedf_alloc_cmd(struct qedf_rport *fcport, + u8 cmd_type); + +extern struct device_attribute *qedf_host_attrs[]; +extern void qedf_cmd_timer_set(struct qedf_ctx *qedf, struct qedf_ioreq *io_req, + unsigned int timer_msec); +extern int qedf_init_mp_req(struct qedf_ioreq *io_req); +extern void qedf_init_mp_task(struct qedf_ioreq *io_req, + struct fcoe_task_context *task_ctx); +extern void qedf_add_to_sq(struct qedf_rport *fcport, u16 xid, + u32 ptu_invalidate, enum fcoe_task_type req_type, u32 offset); +extern void qedf_ring_doorbell(struct qedf_rport *fcport); +extern void qedf_process_els_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *els_req); +extern int qedf_send_rrq(struct qedf_ioreq *aborted_io_req); +extern int qedf_send_adisc(struct qedf_rport *fcport, struct fc_frame *fp); +extern int qedf_initiate_cleanup(struct qedf_ioreq *io_req, + bool return_scsi_cmd_on_abts); +extern void qedf_process_cleanup_compl(struct qedf_ctx *qedf, + struct fcoe_cqe *cqe, struct qedf_ioreq *io_req); +extern int qedf_initiate_tmf(struct scsi_cmnd *sc_cmd, u8 tm_flags); +extern void qedf_process_tmf_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req); +extern void qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe); +extern void qedf_scsi_done(struct qedf_ctx *qedf, struct qedf_ioreq *io_req, + int result); +extern void qedf_set_vlan_id(struct qedf_ctx *qedf, int vlan_id); +extern void qedf_create_sysfs_ctx_attr(struct qedf_ctx *qedf); +extern void qedf_remove_sysfs_ctx_attr(struct qedf_ctx *qedf); +extern void qedf_capture_grc_dump(struct qedf_ctx *qedf); +extern void qedf_wait_for_upload(struct qedf_ctx *qedf); +extern void qedf_process_unsol_compl(struct qedf_ctx *qedf, uint16_t que_idx, + struct fcoe_cqe *cqe); +extern void qedf_restart_rport(struct qedf_rport *fcport); +extern int qedf_send_rec(struct qedf_ioreq *orig_io_req); +extern int qedf_post_io_req(struct qedf_rport *fcport, + struct qedf_ioreq *io_req); +extern void qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf, + struct fcoe_cqe *cqe, struct qedf_ioreq *io_req); +extern int qedf_send_flogi(struct qedf_ctx *qedf); +extern void qedf_fp_io_handler(struct work_struct *work); + +#define FCOE_WORD_TO_BYTE 4 +#define QEDF_MAX_TASK_NUM 0xFFFF + +struct fip_vlan { + struct ethhdr eth; + struct fip_header fip; + struct { + struct fip_mac_desc mac; + struct fip_wwn_desc wwnn; + } desc; +}; + +/* SQ/CQ Sizes */ +#define GBL_RSVD_TASKS 16 +#define NUM_TASKS_PER_CONNECTION 1024 +#define NUM_RW_TASKS_PER_CONNECTION 512 +#define FCOE_PARAMS_CQ_NUM_ENTRIES FCOE_PARAMS_NUM_TASKS + +#define FCOE_PARAMS_CMDQ_NUM_ENTRIES FCOE_PARAMS_NUM_TASKS +#define SQ_NUM_ENTRIES NUM_TASKS_PER_CONNECTION + +#define QEDF_FCOE_PARAMS_GL_RQ_PI 0 +#define QEDF_FCOE_PARAMS_GL_CMD_PI 1 + +#define QEDF_READ (1 << 1) +#define QEDF_WRITE (1 << 0) +#define MAX_FIBRE_LUNS 0xffffffff + +#define QEDF_MAX_NUM_CQS 8 + +/* + * PCI function probe defines + */ +/* Probe/remove called during normal PCI probe */ +#define QEDF_MODE_NORMAL 0 +/* Probe/remove called from qed error recovery */ +#define QEDF_MODE_RECOVERY 1 + +#define SUPPORTED_25000baseKR_Full (1<<27) +#define SUPPORTED_50000baseKR2_Full (1<<28) +#define SUPPORTED_100000baseKR4_Full (1<<29) +#define SUPPORTED_100000baseCR4_Full (1<<30) + +#endif diff --git a/drivers/scsi/qedf/qedf_attr.c b/drivers/scsi/qedf/qedf_attr.c new file mode 100644 index 000000000000..47720611ad2c --- /dev/null +++ b/drivers/scsi/qedf/qedf_attr.c @@ -0,0 +1,165 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include "qedf.h" + +static ssize_t +qedf_fcoe_mac_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fc_lport *lport = shost_priv(class_to_shost(dev)); + u32 port_id; + u8 lport_src_id[3]; + u8 fcoe_mac[6]; + + port_id = fc_host_port_id(lport->host); + lport_src_id[2] = (port_id & 0x000000FF); + lport_src_id[1] = (port_id & 0x0000FF00) >> 8; + lport_src_id[0] = (port_id & 0x00FF0000) >> 16; + fc_fcoe_set_mac(fcoe_mac, lport_src_id); + + return scnprintf(buf, PAGE_SIZE, "%pM\n", fcoe_mac); +} + +static DEVICE_ATTR(fcoe_mac, S_IRUGO, qedf_fcoe_mac_show, NULL); + +struct device_attribute *qedf_host_attrs[] = { + &dev_attr_fcoe_mac, + NULL, +}; + +extern const struct qed_fcoe_ops *qed_ops; + +inline bool qedf_is_vport(struct qedf_ctx *qedf) +{ + return (!(qedf->lport->vport == NULL)); +} + +/* Get base qedf for physical port from vport */ +static struct qedf_ctx *qedf_get_base_qedf(struct qedf_ctx *qedf) +{ + struct fc_lport *lport; + struct fc_lport *base_lport; + + if (!(qedf_is_vport(qedf))) + return NULL; + + lport = qedf->lport; + base_lport = shost_priv(vport_to_shost(lport->vport)); + return (struct qedf_ctx *)(lport_priv(base_lport)); +} + +void qedf_capture_grc_dump(struct qedf_ctx *qedf) +{ + struct qedf_ctx *base_qedf; + + /* Make sure we use the base qedf to take the GRC dump */ + if (qedf_is_vport(qedf)) + base_qedf = qedf_get_base_qedf(qedf); + else + base_qedf = qedf; + + if (test_bit(QEDF_GRCDUMP_CAPTURE, &base_qedf->flags)) { + QEDF_INFO(&(base_qedf->dbg_ctx), QEDF_LOG_INFO, + "GRC Dump already captured.\n"); + return; + } + + + qedf_get_grc_dump(base_qedf->cdev, qed_ops->common, + &base_qedf->grcdump, &base_qedf->grcdump_size); + QEDF_ERR(&(base_qedf->dbg_ctx), "GRC Dump captured.\n"); + set_bit(QEDF_GRCDUMP_CAPTURE, &base_qedf->flags); + qedf_uevent_emit(base_qedf->lport->host, QEDF_UEVENT_CODE_GRCDUMP, + NULL); +} + +static ssize_t +qedf_sysfs_read_grcdump(struct file *filep, struct kobject *kobj, + struct bin_attribute *ba, char *buf, loff_t off, + size_t count) +{ + ssize_t ret = 0; + struct fc_lport *lport = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + struct qedf_ctx *qedf = lport_priv(lport); + + if (test_bit(QEDF_GRCDUMP_CAPTURE, &qedf->flags)) { + ret = memory_read_from_buffer(buf, count, &off, + qedf->grcdump, qedf->grcdump_size); + } else { + QEDF_ERR(&(qedf->dbg_ctx), "GRC Dump not captured!\n"); + } + + return ret; +} + +static ssize_t +qedf_sysfs_write_grcdump(struct file *filep, struct kobject *kobj, + struct bin_attribute *ba, char *buf, loff_t off, + size_t count) +{ + struct fc_lport *lport = NULL; + struct qedf_ctx *qedf = NULL; + long reading; + int ret = 0; + char msg[40]; + + if (off != 0) + return ret; + + + lport = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + qedf = lport_priv(lport); + + buf[1] = 0; + ret = kstrtol(buf, 10, &reading); + if (ret) { + QEDF_ERR(&(qedf->dbg_ctx), "Invalid input, err(%d)\n", ret); + return ret; + } + + memset(msg, 0, sizeof(msg)); + switch (reading) { + case 0: + memset(qedf->grcdump, 0, qedf->grcdump_size); + clear_bit(QEDF_GRCDUMP_CAPTURE, &qedf->flags); + break; + case 1: + qedf_capture_grc_dump(qedf); + break; + } + + return count; +} + +static struct bin_attribute sysfs_grcdump_attr = { + .attr = { + .name = "grcdump", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = qedf_sysfs_read_grcdump, + .write = qedf_sysfs_write_grcdump, +}; + +static struct sysfs_bin_attrs bin_file_entries[] = { + {"grcdump", &sysfs_grcdump_attr}, + {NULL}, +}; + +void qedf_create_sysfs_ctx_attr(struct qedf_ctx *qedf) +{ + qedf_create_sysfs_attr(qedf->lport->host, bin_file_entries); +} + +void qedf_remove_sysfs_ctx_attr(struct qedf_ctx *qedf) +{ + qedf_remove_sysfs_attr(qedf->lport->host, bin_file_entries); +} diff --git a/drivers/scsi/qedf/qedf_dbg.c b/drivers/scsi/qedf/qedf_dbg.c new file mode 100644 index 000000000000..e023f5d0dc12 --- /dev/null +++ b/drivers/scsi/qedf/qedf_dbg.c @@ -0,0 +1,195 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include "qedf_dbg.h" +#include <linux/vmalloc.h> + +void +qedf_dbg_err(struct qedf_dbg_ctx *qedf, const char *func, u32 line, + const char *fmt, ...) +{ + va_list va; + struct va_format vaf; + char nfunc[32]; + + memset(nfunc, 0, sizeof(nfunc)); + memcpy(nfunc, func, sizeof(nfunc) - 1); + + va_start(va, fmt); + + vaf.fmt = fmt; + vaf.va = &va; + + if (likely(qedf) && likely(qedf->pdev)) + pr_err("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)), + nfunc, line, qedf->host_no, &vaf); + else + pr_err("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf); + + va_end(va); +} + +void +qedf_dbg_warn(struct qedf_dbg_ctx *qedf, const char *func, u32 line, + const char *fmt, ...) +{ + va_list va; + struct va_format vaf; + char nfunc[32]; + + memset(nfunc, 0, sizeof(nfunc)); + memcpy(nfunc, func, sizeof(nfunc) - 1); + + va_start(va, fmt); + + vaf.fmt = fmt; + vaf.va = &va; + + if (!(qedf_debug & QEDF_LOG_WARN)) + goto ret; + + if (likely(qedf) && likely(qedf->pdev)) + pr_warn("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)), + nfunc, line, qedf->host_no, &vaf); + else + pr_warn("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf); + +ret: + va_end(va); +} + +void +qedf_dbg_notice(struct qedf_dbg_ctx *qedf, const char *func, u32 line, + const char *fmt, ...) +{ + va_list va; + struct va_format vaf; + char nfunc[32]; + + memset(nfunc, 0, sizeof(nfunc)); + memcpy(nfunc, func, sizeof(nfunc) - 1); + + va_start(va, fmt); + + vaf.fmt = fmt; + vaf.va = &va; + + if (!(qedf_debug & QEDF_LOG_NOTICE)) + goto ret; + + if (likely(qedf) && likely(qedf->pdev)) + pr_notice("[%s]:[%s:%d]:%d: %pV", + dev_name(&(qedf->pdev->dev)), nfunc, line, + qedf->host_no, &vaf); + else + pr_notice("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf); + +ret: + va_end(va); +} + +void +qedf_dbg_info(struct qedf_dbg_ctx *qedf, const char *func, u32 line, + u32 level, const char *fmt, ...) +{ + va_list va; + struct va_format vaf; + char nfunc[32]; + + memset(nfunc, 0, sizeof(nfunc)); + memcpy(nfunc, func, sizeof(nfunc) - 1); + + va_start(va, fmt); + + vaf.fmt = fmt; + vaf.va = &va; + + if (!(qedf_debug & level)) + goto ret; + + if (likely(qedf) && likely(qedf->pdev)) + pr_info("[%s]:[%s:%d]:%d: %pV", dev_name(&(qedf->pdev->dev)), + nfunc, line, qedf->host_no, &vaf); + else + pr_info("[0000:00:00.0]:[%s:%d]: %pV", nfunc, line, &vaf); + +ret: + va_end(va); +} + +int +qedf_alloc_grc_dump_buf(u8 **buf, uint32_t len) +{ + *buf = vmalloc(len); + if (!(*buf)) + return -ENOMEM; + + memset(*buf, 0, len); + return 0; +} + +void +qedf_free_grc_dump_buf(uint8_t **buf) +{ + vfree(*buf); + *buf = NULL; +} + +int +qedf_get_grc_dump(struct qed_dev *cdev, const struct qed_common_ops *common, + u8 **buf, uint32_t *grcsize) +{ + if (!*buf) + return -EINVAL; + + return common->dbg_grc(cdev, *buf, grcsize); +} + +void +qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg) +{ + char event_string[40]; + char *envp[] = {event_string, NULL}; + + memset(event_string, 0, sizeof(event_string)); + switch (code) { + case QEDF_UEVENT_CODE_GRCDUMP: + if (msg) + strncpy(event_string, msg, strlen(msg)); + else + sprintf(event_string, "GRCDUMP=%u", shost->host_no); + break; + default: + /* do nothing */ + break; + } + + kobject_uevent_env(&shost->shost_gendev.kobj, KOBJ_CHANGE, envp); +} + +int +qedf_create_sysfs_attr(struct Scsi_Host *shost, struct sysfs_bin_attrs *iter) +{ + int ret = 0; + + for (; iter->name; iter++) { + ret = sysfs_create_bin_file(&shost->shost_gendev.kobj, + iter->attr); + if (ret) + pr_err("Unable to create sysfs %s attr, err(%d).\n", + iter->name, ret); + } + return ret; +} + +void +qedf_remove_sysfs_attr(struct Scsi_Host *shost, struct sysfs_bin_attrs *iter) +{ + for (; iter->name; iter++) + sysfs_remove_bin_file(&shost->shost_gendev.kobj, iter->attr); +} diff --git a/drivers/scsi/qedf/qedf_dbg.h b/drivers/scsi/qedf/qedf_dbg.h new file mode 100644 index 000000000000..23bd70628a2f --- /dev/null +++ b/drivers/scsi/qedf/qedf_dbg.h @@ -0,0 +1,154 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#ifndef _QEDF_DBG_H_ +#define _QEDF_DBG_H_ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/string.h> +#include <linux/version.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <scsi/scsi_transport.h> +#include <linux/fs.h> + +#include <linux/qed/common_hsi.h> +#include <linux/qed/qed_if.h> + +extern uint qedf_debug; + +/* Debug print level definitions */ +#define QEDF_LOG_DEFAULT 0x1 /* Set default logging mask */ +#define QEDF_LOG_INFO 0x2 /* + * Informational logs, + * MAC address, WWPN, WWNN + */ +#define QEDF_LOG_DISC 0x4 /* Init, discovery, rport */ +#define QEDF_LOG_LL2 0x8 /* LL2, VLAN logs */ +#define QEDF_LOG_CONN 0x10 /* Connection setup, cleanup */ +#define QEDF_LOG_EVT 0x20 /* Events, link, mtu */ +#define QEDF_LOG_TIMER 0x40 /* Timer events */ +#define QEDF_LOG_MP_REQ 0x80 /* Middle Path (MP) logs */ +#define QEDF_LOG_SCSI_TM 0x100 /* SCSI Aborts, Task Mgmt */ +#define QEDF_LOG_UNSOL 0x200 /* unsolicited event logs */ +#define QEDF_LOG_IO 0x400 /* scsi cmd, completion */ +#define QEDF_LOG_MQ 0x800 /* Multi Queue logs */ +#define QEDF_LOG_BSG 0x1000 /* BSG logs */ +#define QEDF_LOG_DEBUGFS 0x2000 /* debugFS logs */ +#define QEDF_LOG_LPORT 0x4000 /* lport logs */ +#define QEDF_LOG_ELS 0x8000 /* ELS logs */ +#define QEDF_LOG_NPIV 0x10000 /* NPIV logs */ +#define QEDF_LOG_SESS 0x20000 /* Conection setup, cleanup */ +#define QEDF_LOG_TID 0x80000 /* + * FW TID context acquire + * free + */ +#define QEDF_TRACK_TID 0x100000 /* + * Track TID state. To be + * enabled only at module load + * and not run-time. + */ +#define QEDF_TRACK_CMD_LIST 0x300000 /* + * Track active cmd list nodes, + * done with reference to TID, + * hence TRACK_TID also enabled. + */ +#define QEDF_LOG_NOTICE 0x40000000 /* Notice logs */ +#define QEDF_LOG_WARN 0x80000000 /* Warning logs */ + +/* Debug context structure */ +struct qedf_dbg_ctx { + unsigned int host_no; + struct pci_dev *pdev; +#ifdef CONFIG_DEBUG_FS + struct dentry *bdf_dentry; +#endif +}; + +#define QEDF_ERR(pdev, fmt, ...) \ + qedf_dbg_err(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__) +#define QEDF_WARN(pdev, fmt, ...) \ + qedf_dbg_warn(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__) +#define QEDF_NOTICE(pdev, fmt, ...) \ + qedf_dbg_notice(pdev, __func__, __LINE__, fmt, ## __VA_ARGS__) +#define QEDF_INFO(pdev, level, fmt, ...) \ + qedf_dbg_info(pdev, __func__, __LINE__, level, fmt, \ + ## __VA_ARGS__) + +extern void qedf_dbg_err(struct qedf_dbg_ctx *qedf, const char *func, u32 line, + const char *fmt, ...); +extern void qedf_dbg_warn(struct qedf_dbg_ctx *qedf, const char *func, u32 line, + const char *, ...); +extern void qedf_dbg_notice(struct qedf_dbg_ctx *qedf, const char *func, + u32 line, const char *, ...); +extern void qedf_dbg_info(struct qedf_dbg_ctx *qedf, const char *func, u32 line, + u32 info, const char *fmt, ...); + +/* GRC Dump related defines */ + +struct Scsi_Host; + +#define QEDF_UEVENT_CODE_GRCDUMP 0 + +struct sysfs_bin_attrs { + char *name; + struct bin_attribute *attr; +}; + +extern int qedf_alloc_grc_dump_buf(uint8_t **buf, uint32_t len); +extern void qedf_free_grc_dump_buf(uint8_t **buf); +extern int qedf_get_grc_dump(struct qed_dev *cdev, + const struct qed_common_ops *common, uint8_t **buf, + uint32_t *grcsize); +extern void qedf_uevent_emit(struct Scsi_Host *shost, u32 code, char *msg); +extern int qedf_create_sysfs_attr(struct Scsi_Host *shost, + struct sysfs_bin_attrs *iter); +extern void qedf_remove_sysfs_attr(struct Scsi_Host *shost, + struct sysfs_bin_attrs *iter); + +#ifdef CONFIG_DEBUG_FS +/* DebugFS related code */ +struct qedf_list_of_funcs { + char *oper_str; + ssize_t (*oper_func)(struct qedf_dbg_ctx *qedf); +}; + +struct qedf_debugfs_ops { + char *name; + struct qedf_list_of_funcs *qedf_funcs; +}; + +#define qedf_dbg_fileops(drv, ops) \ +{ \ + .owner = THIS_MODULE, \ + .open = simple_open, \ + .read = drv##_dbg_##ops##_cmd_read, \ + .write = drv##_dbg_##ops##_cmd_write \ +} + +/* Used for debugfs sequential files */ +#define qedf_dbg_fileops_seq(drv, ops) \ +{ \ + .owner = THIS_MODULE, \ + .open = drv##_dbg_##ops##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +extern void qedf_dbg_host_init(struct qedf_dbg_ctx *qedf, + struct qedf_debugfs_ops *dops, + struct file_operations *fops); +extern void qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf); +extern void qedf_dbg_init(char *drv_name); +extern void qedf_dbg_exit(void); +#endif /* CONFIG_DEBUG_FS */ + +#endif /* _QEDF_DBG_H_ */ diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c new file mode 100644 index 000000000000..cb08b625c594 --- /dev/null +++ b/drivers/scsi/qedf/qedf_debugfs.c @@ -0,0 +1,460 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#ifdef CONFIG_DEBUG_FS + +#include <linux/uaccess.h> +#include <linux/debugfs.h> +#include <linux/module.h> + +#include "qedf.h" +#include "qedf_dbg.h" + +static struct dentry *qedf_dbg_root; + +/** + * qedf_dbg_host_init - setup the debugfs file for the pf + * @pf: the pf that is starting up + **/ +void +qedf_dbg_host_init(struct qedf_dbg_ctx *qedf, + struct qedf_debugfs_ops *dops, + struct file_operations *fops) +{ + char host_dirname[32]; + struct dentry *file_dentry = NULL; + + QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "Creating debugfs host node\n"); + /* create pf dir */ + sprintf(host_dirname, "host%u", qedf->host_no); + qedf->bdf_dentry = debugfs_create_dir(host_dirname, qedf_dbg_root); + if (!qedf->bdf_dentry) + return; + + /* create debugfs files */ + while (dops) { + if (!(dops->name)) + break; + + file_dentry = debugfs_create_file(dops->name, 0600, + qedf->bdf_dentry, qedf, + fops); + if (!file_dentry) { + QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, + "Debugfs entry %s creation failed\n", + dops->name); + debugfs_remove_recursive(qedf->bdf_dentry); + return; + } + dops++; + fops++; + } +} + +/** + * qedf_dbg_host_exit - clear out the pf's debugfs entries + * @pf: the pf that is stopping + **/ +void +qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf) +{ + QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "Destroying debugfs host " + "entry\n"); + /* remove debugfs entries of this PF */ + debugfs_remove_recursive(qedf->bdf_dentry); + qedf->bdf_dentry = NULL; +} + +/** + * qedf_dbg_init - start up debugfs for the driver + **/ +void +qedf_dbg_init(char *drv_name) +{ + QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Creating debugfs root node\n"); + + /* create qed dir in root of debugfs. NULL means debugfs root */ + qedf_dbg_root = debugfs_create_dir(drv_name, NULL); + if (!qedf_dbg_root) + QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Init of debugfs " + "failed\n"); +} + +/** + * qedf_dbg_exit - clean out the driver's debugfs entries + **/ +void +qedf_dbg_exit(void) +{ + QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Destroying debugfs root " + "entry\n"); + + /* remove qed dir in root of debugfs */ + debugfs_remove_recursive(qedf_dbg_root); + qedf_dbg_root = NULL; +} + +struct qedf_debugfs_ops qedf_debugfs_ops[] = { + { "fp_int", NULL }, + { "io_trace", NULL }, + { "debug", NULL }, + { "stop_io_on_error", NULL}, + { "driver_stats", NULL}, + { "clear_stats", NULL}, + { "offload_stats", NULL}, + /* This must be last */ + { NULL, NULL } +}; + +DECLARE_PER_CPU(struct qedf_percpu_iothread_s, qedf_percpu_iothreads); + +static ssize_t +qedf_dbg_fp_int_cmd_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + size_t cnt = 0; + int id; + struct qedf_fastpath *fp = NULL; + struct qedf_dbg_ctx *qedf_dbg = + (struct qedf_dbg_ctx *)filp->private_data; + struct qedf_ctx *qedf = container_of(qedf_dbg, + struct qedf_ctx, dbg_ctx); + + QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n"); + + cnt = sprintf(buffer, "\nFastpath I/O completions\n\n"); + + for (id = 0; id < qedf->num_queues; id++) { + fp = &(qedf->fp_array[id]); + if (fp->sb_id == QEDF_SB_ID_NULL) + continue; + cnt += sprintf((buffer + cnt), "#%d: %lu\n", id, + fp->completions); + } + + cnt = min_t(int, count, cnt - *ppos); + *ppos += cnt; + return cnt; +} + +static ssize_t +qedf_dbg_fp_int_cmd_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + if (!count || *ppos) + return 0; + + return count; +} + +static ssize_t +qedf_dbg_debug_cmd_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + int cnt; + struct qedf_dbg_ctx *qedf = + (struct qedf_dbg_ctx *)filp->private_data; + + QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "entered\n"); + cnt = sprintf(buffer, "debug mask = 0x%x\n", qedf_debug); + + cnt = min_t(int, count, cnt - *ppos); + *ppos += cnt; + return cnt; +} + +static ssize_t +qedf_dbg_debug_cmd_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + uint32_t val; + void *kern_buf; + int rval; + struct qedf_dbg_ctx *qedf = + (struct qedf_dbg_ctx *)filp->private_data; + + if (!count || *ppos) + return 0; + + kern_buf = memdup_user(buffer, count); + if (IS_ERR(kern_buf)) + return PTR_ERR(kern_buf); + + rval = kstrtouint(kern_buf, 10, &val); + kfree(kern_buf); + if (rval) + return rval; + + if (val == 1) + qedf_debug = QEDF_DEFAULT_LOG_MASK; + else + qedf_debug = val; + + QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "Setting debug=0x%x.\n", val); + return count; +} + +static ssize_t +qedf_dbg_stop_io_on_error_cmd_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + int cnt; + struct qedf_dbg_ctx *qedf_dbg = + (struct qedf_dbg_ctx *)filp->private_data; + struct qedf_ctx *qedf = container_of(qedf_dbg, + struct qedf_ctx, dbg_ctx); + + QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n"); + cnt = sprintf(buffer, "%s\n", + qedf->stop_io_on_error ? "true" : "false"); + + cnt = min_t(int, count, cnt - *ppos); + *ppos += cnt; + return cnt; +} + +static ssize_t +qedf_dbg_stop_io_on_error_cmd_write(struct file *filp, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + void *kern_buf; + struct qedf_dbg_ctx *qedf_dbg = + (struct qedf_dbg_ctx *)filp->private_data; + struct qedf_ctx *qedf = container_of(qedf_dbg, struct qedf_ctx, + dbg_ctx); + + QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n"); + + if (!count || *ppos) + return 0; + + kern_buf = memdup_user(buffer, 6); + if (IS_ERR(kern_buf)) + return PTR_ERR(kern_buf); + + if (strncmp(kern_buf, "false", 5) == 0) + qedf->stop_io_on_error = false; + else if (strncmp(kern_buf, "true", 4) == 0) + qedf->stop_io_on_error = true; + else if (strncmp(kern_buf, "now", 3) == 0) + /* Trigger from user to stop all I/O on this host */ + set_bit(QEDF_DBG_STOP_IO, &qedf->flags); + + kfree(kern_buf); + return count; +} + +static int +qedf_io_trace_show(struct seq_file *s, void *unused) +{ + int i, idx = 0; + struct qedf_ctx *qedf = s->private; + struct qedf_dbg_ctx *qedf_dbg = &qedf->dbg_ctx; + struct qedf_io_log *io_log; + unsigned long flags; + + if (!qedf_io_tracing) { + seq_puts(s, "I/O tracing not enabled.\n"); + goto out; + } + + QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n"); + + spin_lock_irqsave(&qedf->io_trace_lock, flags); + idx = qedf->io_trace_idx; + for (i = 0; i < QEDF_IO_TRACE_SIZE; i++) { + io_log = &qedf->io_trace_buf[idx]; + seq_printf(s, "%d:", io_log->direction); + seq_printf(s, "0x%x:", io_log->task_id); + seq_printf(s, "0x%06x:", io_log->port_id); + seq_printf(s, "%d:", io_log->lun); + seq_printf(s, "0x%02x:", io_log->op); + seq_printf(s, "0x%02x%02x%02x%02x:", io_log->lba[0], + io_log->lba[1], io_log->lba[2], io_log->lba[3]); + seq_printf(s, "%d:", io_log->bufflen); + seq_printf(s, "%d:", io_log->sg_count); + seq_printf(s, "0x%08x:", io_log->result); + seq_printf(s, "%lu:", io_log->jiffies); + seq_printf(s, "%d:", io_log->refcount); + seq_printf(s, "%d:", io_log->req_cpu); + seq_printf(s, "%d:", io_log->int_cpu); + seq_printf(s, "%d:", io_log->rsp_cpu); + seq_printf(s, "%d\n", io_log->sge_type); + + idx++; + if (idx == QEDF_IO_TRACE_SIZE) + idx = 0; + } + spin_unlock_irqrestore(&qedf->io_trace_lock, flags); + +out: + return 0; +} + +static int +qedf_dbg_io_trace_open(struct inode *inode, struct file *file) +{ + struct qedf_dbg_ctx *qedf_dbg = inode->i_private; + struct qedf_ctx *qedf = container_of(qedf_dbg, + struct qedf_ctx, dbg_ctx); + + return single_open(file, qedf_io_trace_show, qedf); +} + +static int +qedf_driver_stats_show(struct seq_file *s, void *unused) +{ + struct qedf_ctx *qedf = s->private; + struct qedf_rport *fcport; + struct fc_rport_priv *rdata; + + seq_printf(s, "cmg_mgr free io_reqs: %d\n", + atomic_read(&qedf->cmd_mgr->free_list_cnt)); + seq_printf(s, "slow SGEs: %d\n", qedf->slow_sge_ios); + seq_printf(s, "single SGEs: %d\n", qedf->single_sge_ios); + seq_printf(s, "fast SGEs: %d\n\n", qedf->fast_sge_ios); + + seq_puts(s, "Offloaded ports:\n\n"); + + rcu_read_lock(); + list_for_each_entry_rcu(fcport, &qedf->fcports, peers) { + rdata = fcport->rdata; + if (rdata == NULL) + continue; + seq_printf(s, "%06x: free_sqes: %d, num_active_ios: %d\n", + rdata->ids.port_id, atomic_read(&fcport->free_sqes), + atomic_read(&fcport->num_active_ios)); + } + rcu_read_unlock(); + + return 0; +} + +static int +qedf_dbg_driver_stats_open(struct inode *inode, struct file *file) +{ + struct qedf_dbg_ctx *qedf_dbg = inode->i_private; + struct qedf_ctx *qedf = container_of(qedf_dbg, + struct qedf_ctx, dbg_ctx); + + return single_open(file, qedf_driver_stats_show, qedf); +} + +static ssize_t +qedf_dbg_clear_stats_cmd_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + int cnt = 0; + + /* Essentially a read stub */ + cnt = min_t(int, count, cnt - *ppos); + *ppos += cnt; + return cnt; +} + +static ssize_t +qedf_dbg_clear_stats_cmd_write(struct file *filp, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct qedf_dbg_ctx *qedf_dbg = + (struct qedf_dbg_ctx *)filp->private_data; + struct qedf_ctx *qedf = container_of(qedf_dbg, struct qedf_ctx, + dbg_ctx); + + QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "Clearing stat counters.\n"); + + if (!count || *ppos) + return 0; + + /* Clear stat counters exposed by 'stats' node */ + qedf->slow_sge_ios = 0; + qedf->single_sge_ios = 0; + qedf->fast_sge_ios = 0; + + return count; +} + +static int +qedf_offload_stats_show(struct seq_file *s, void *unused) +{ + struct qedf_ctx *qedf = s->private; + struct qed_fcoe_stats *fw_fcoe_stats; + + fw_fcoe_stats = kmalloc(sizeof(struct qed_fcoe_stats), GFP_KERNEL); + if (!fw_fcoe_stats) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate memory for " + "fw_fcoe_stats.\n"); + goto out; + } + + /* Query firmware for offload stats */ + qed_ops->get_stats(qedf->cdev, fw_fcoe_stats); + + seq_printf(s, "fcoe_rx_byte_cnt=%llu\n" + "fcoe_rx_data_pkt_cnt=%llu\n" + "fcoe_rx_xfer_pkt_cnt=%llu\n" + "fcoe_rx_other_pkt_cnt=%llu\n" + "fcoe_silent_drop_pkt_cmdq_full_cnt=%u\n" + "fcoe_silent_drop_pkt_crc_error_cnt=%u\n" + "fcoe_silent_drop_pkt_task_invalid_cnt=%u\n" + "fcoe_silent_drop_total_pkt_cnt=%u\n" + "fcoe_silent_drop_pkt_rq_full_cnt=%u\n" + "fcoe_tx_byte_cnt=%llu\n" + "fcoe_tx_data_pkt_cnt=%llu\n" + "fcoe_tx_xfer_pkt_cnt=%llu\n" + "fcoe_tx_other_pkt_cnt=%llu\n", + fw_fcoe_stats->fcoe_rx_byte_cnt, + fw_fcoe_stats->fcoe_rx_data_pkt_cnt, + fw_fcoe_stats->fcoe_rx_xfer_pkt_cnt, + fw_fcoe_stats->fcoe_rx_other_pkt_cnt, + fw_fcoe_stats->fcoe_silent_drop_pkt_cmdq_full_cnt, + fw_fcoe_stats->fcoe_silent_drop_pkt_crc_error_cnt, + fw_fcoe_stats->fcoe_silent_drop_pkt_task_invalid_cnt, + fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt, + fw_fcoe_stats->fcoe_silent_drop_pkt_rq_full_cnt, + fw_fcoe_stats->fcoe_tx_byte_cnt, + fw_fcoe_stats->fcoe_tx_data_pkt_cnt, + fw_fcoe_stats->fcoe_tx_xfer_pkt_cnt, + fw_fcoe_stats->fcoe_tx_other_pkt_cnt); + + kfree(fw_fcoe_stats); +out: + return 0; +} + +static int +qedf_dbg_offload_stats_open(struct inode *inode, struct file *file) +{ + struct qedf_dbg_ctx *qedf_dbg = inode->i_private; + struct qedf_ctx *qedf = container_of(qedf_dbg, + struct qedf_ctx, dbg_ctx); + + return single_open(file, qedf_offload_stats_show, qedf); +} + + +const struct file_operations qedf_dbg_fops[] = { + qedf_dbg_fileops(qedf, fp_int), + qedf_dbg_fileops_seq(qedf, io_trace), + qedf_dbg_fileops(qedf, debug), + qedf_dbg_fileops(qedf, stop_io_on_error), + qedf_dbg_fileops_seq(qedf, driver_stats), + qedf_dbg_fileops(qedf, clear_stats), + qedf_dbg_fileops_seq(qedf, offload_stats), + /* This must be last */ + { NULL, NULL }, +}; + +#else /* CONFIG_DEBUG_FS */ +void qedf_dbg_host_init(struct qedf_dbg_ctx *); +void qedf_dbg_host_exit(struct qedf_dbg_ctx *); +void qedf_dbg_init(char *); +void qedf_dbg_exit(void); +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c new file mode 100644 index 000000000000..59f3e5c73a13 --- /dev/null +++ b/drivers/scsi/qedf/qedf_els.c @@ -0,0 +1,949 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include "qedf.h" + +/* It's assumed that the lock is held when calling this function. */ +static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op, + void *data, uint32_t data_len, + void (*cb_func)(struct qedf_els_cb_arg *cb_arg), + struct qedf_els_cb_arg *cb_arg, uint32_t timer_msec) +{ + struct qedf_ctx *qedf = fcport->qedf; + struct fc_lport *lport = qedf->lport; + struct qedf_ioreq *els_req; + struct qedf_mp_req *mp_req; + struct fc_frame_header *fc_hdr; + struct fcoe_task_context *task; + int rc = 0; + uint32_t did, sid; + uint16_t xid; + uint32_t start_time = jiffies / HZ; + uint32_t current_time; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending ELS\n"); + + rc = fc_remote_port_chkready(fcport->rport); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: rport not ready\n", op); + rc = -EAGAIN; + goto els_err; + } + if (lport->state != LPORT_ST_READY || !(lport->link_up)) { + QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: link is not ready\n", + op); + rc = -EAGAIN; + goto els_err; + } + + if (!(test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags))) { + QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: fcport not ready\n", op); + rc = -EINVAL; + goto els_err; + } + +retry_els: + els_req = qedf_alloc_cmd(fcport, QEDF_ELS); + if (!els_req) { + current_time = jiffies / HZ; + if ((current_time - start_time) > 10) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "els: Failed els 0x%x\n", op); + rc = -ENOMEM; + goto els_err; + } + mdelay(20 * USEC_PER_MSEC); + goto retry_els; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "initiate_els els_req = " + "0x%p cb_arg = %p xid = %x\n", els_req, cb_arg, + els_req->xid); + els_req->sc_cmd = NULL; + els_req->cmd_type = QEDF_ELS; + els_req->fcport = fcport; + els_req->cb_func = cb_func; + cb_arg->io_req = els_req; + cb_arg->op = op; + els_req->cb_arg = cb_arg; + els_req->data_xfer_len = data_len; + + /* Record which cpu this request is associated with */ + els_req->cpu = smp_processor_id(); + + mp_req = (struct qedf_mp_req *)&(els_req->mp_req); + rc = qedf_init_mp_req(els_req); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "ELS MP request init failed\n"); + kref_put(&els_req->refcount, qedf_release_cmd); + goto els_err; + } else { + rc = 0; + } + + /* Fill ELS Payload */ + if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) { + memcpy(mp_req->req_buf, data, data_len); + } else { + QEDF_ERR(&(qedf->dbg_ctx), "Invalid ELS op 0x%x\n", op); + els_req->cb_func = NULL; + els_req->cb_arg = NULL; + kref_put(&els_req->refcount, qedf_release_cmd); + rc = -EINVAL; + } + + if (rc) + goto els_err; + + /* Fill FC header */ + fc_hdr = &(mp_req->req_fc_hdr); + + did = fcport->rdata->ids.port_id; + sid = fcport->sid; + + __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, sid, did, + FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | + FC_FC_SEQ_INIT, 0); + + /* Obtain exchange id */ + xid = els_req->xid; + + /* Initialize task context for this IO request */ + task = qedf_get_task_mem(&qedf->tasks, xid); + qedf_init_mp_task(els_req, task); + + /* Put timer on original I/O request */ + if (timer_msec) + qedf_cmd_timer_set(qedf, els_req, timer_msec); + + qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_MIDPATH, 0); + + /* Ring doorbell */ + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Ringing doorbell for ELS " + "req\n"); + qedf_ring_doorbell(fcport); +els_err: + return rc; +} + +void qedf_process_els_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *els_req) +{ + struct fcoe_task_context *task_ctx; + struct scsi_cmnd *sc_cmd; + uint16_t xid; + struct fcoe_cqe_midpath_info *mp_info; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered with xid = 0x%x" + " cmd_type = %d.\n", els_req->xid, els_req->cmd_type); + + /* Kill the ELS timer */ + cancel_delayed_work(&els_req->timeout_work); + + xid = els_req->xid; + task_ctx = qedf_get_task_mem(&qedf->tasks, xid); + sc_cmd = els_req->sc_cmd; + + /* Get ELS response length from CQE */ + mp_info = &cqe->cqe_info.midpath_info; + els_req->mp_req.resp_len = mp_info->data_placement_size; + + /* Parse ELS response */ + if ((els_req->cb_func) && (els_req->cb_arg)) { + els_req->cb_func(els_req->cb_arg); + els_req->cb_arg = NULL; + } + + kref_put(&els_req->refcount, qedf_release_cmd); +} + +static void qedf_rrq_compl(struct qedf_els_cb_arg *cb_arg) +{ + struct qedf_ioreq *orig_io_req; + struct qedf_ioreq *rrq_req; + struct qedf_ctx *qedf; + int refcount; + + rrq_req = cb_arg->io_req; + qedf = rrq_req->fcport->qedf; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered.\n"); + + orig_io_req = cb_arg->aborted_io_req; + + if (!orig_io_req) + goto out_free; + + if (rrq_req->event != QEDF_IOREQ_EV_ELS_TMO && + rrq_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) + cancel_delayed_work_sync(&orig_io_req->timeout_work); + + refcount = kref_read(&orig_io_req->refcount); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "rrq_compl: orig io = %p," + " orig xid = 0x%x, rrq_xid = 0x%x, refcount=%d\n", + orig_io_req, orig_io_req->xid, rrq_req->xid, refcount); + + /* This should return the aborted io_req to the command pool */ + if (orig_io_req) + kref_put(&orig_io_req->refcount, qedf_release_cmd); + +out_free: + kfree(cb_arg); +} + +/* Assumes kref is already held by caller */ +int qedf_send_rrq(struct qedf_ioreq *aborted_io_req) +{ + + struct fc_els_rrq rrq; + struct qedf_rport *fcport; + struct fc_lport *lport; + struct qedf_els_cb_arg *cb_arg = NULL; + struct qedf_ctx *qedf; + uint32_t sid; + uint32_t r_a_tov; + int rc; + + if (!aborted_io_req) { + QEDF_ERR(NULL, "abort_io_req is NULL.\n"); + return -EINVAL; + } + + fcport = aborted_io_req->fcport; + + /* Check that fcport is still offloaded */ + if (!(test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags))) { + QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); + return -EINVAL; + } + + if (!fcport->qedf) { + QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); + return -EINVAL; + } + + qedf = fcport->qedf; + lport = qedf->lport; + sid = fcport->sid; + r_a_tov = lport->r_a_tov; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending RRQ orig " + "io = %p, orig_xid = 0x%x\n", aborted_io_req, + aborted_io_req->xid); + memset(&rrq, 0, sizeof(rrq)); + + cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); + if (!cb_arg) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " + "RRQ\n"); + rc = -ENOMEM; + goto rrq_err; + } + + cb_arg->aborted_io_req = aborted_io_req; + + rrq.rrq_cmd = ELS_RRQ; + hton24(rrq.rrq_s_id, sid); + rrq.rrq_ox_id = htons(aborted_io_req->xid); + rrq.rrq_rx_id = + htons(aborted_io_req->task->tstorm_st_context.read_write.rx_id); + + rc = qedf_initiate_els(fcport, ELS_RRQ, &rrq, sizeof(rrq), + qedf_rrq_compl, cb_arg, r_a_tov); + +rrq_err: + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "RRQ failed - release orig io " + "req 0x%x\n", aborted_io_req->xid); + kfree(cb_arg); + kref_put(&aborted_io_req->refcount, qedf_release_cmd); + } + return rc; +} + +static void qedf_process_l2_frame_compl(struct qedf_rport *fcport, + struct fc_frame *fp, + u16 l2_oxid) +{ + struct fc_lport *lport = fcport->qedf->lport; + struct fc_frame_header *fh; + u32 crc; + + fh = (struct fc_frame_header *)fc_frame_header_get(fp); + + /* Set the OXID we return to what libfc used */ + if (l2_oxid != FC_XID_UNKNOWN) + fh->fh_ox_id = htons(l2_oxid); + + /* Setup header fields */ + fh->fh_r_ctl = FC_RCTL_ELS_REP; + fh->fh_type = FC_TYPE_ELS; + /* Last sequence, end sequence */ + fh->fh_f_ctl[0] = 0x98; + hton24(fh->fh_d_id, lport->port_id); + hton24(fh->fh_s_id, fcport->rdata->ids.port_id); + fh->fh_rx_id = 0xffff; + + /* Set frame attributes */ + crc = fcoe_fc_crc(fp); + fc_frame_init(fp); + fr_dev(fp) = lport; + fr_sof(fp) = FC_SOF_I3; + fr_eof(fp) = FC_EOF_T; + fr_crc(fp) = cpu_to_le32(~crc); + + /* Send completed request to libfc */ + fc_exch_recv(lport, fp); +} + +/* + * In instances where an ELS command times out we may need to restart the + * rport by logging out and then logging back in. + */ +void qedf_restart_rport(struct qedf_rport *fcport) +{ + struct fc_lport *lport; + struct fc_rport_priv *rdata; + u32 port_id; + + if (!fcport) + return; + + rdata = fcport->rdata; + if (rdata) { + lport = fcport->qedf->lport; + port_id = rdata->ids.port_id; + QEDF_ERR(&(fcport->qedf->dbg_ctx), + "LOGO port_id=%x.\n", port_id); + fc_rport_logoff(rdata); + /* Recreate the rport and log back in */ + rdata = fc_rport_create(lport, port_id); + if (rdata) + fc_rport_login(rdata); + } +} + +static void qedf_l2_els_compl(struct qedf_els_cb_arg *cb_arg) +{ + struct qedf_ioreq *els_req; + struct qedf_rport *fcport; + struct qedf_mp_req *mp_req; + struct fc_frame *fp; + struct fc_frame_header *fh, *mp_fc_hdr; + void *resp_buf, *fc_payload; + u32 resp_len; + u16 l2_oxid; + + l2_oxid = cb_arg->l2_oxid; + els_req = cb_arg->io_req; + + if (!els_req) { + QEDF_ERR(NULL, "els_req is NULL.\n"); + goto free_arg; + } + + /* + * If we are flushing the command just free the cb_arg as none of the + * response data will be valid. + */ + if (els_req->event == QEDF_IOREQ_EV_ELS_FLUSH) + goto free_arg; + + fcport = els_req->fcport; + mp_req = &(els_req->mp_req); + mp_fc_hdr = &(mp_req->resp_fc_hdr); + resp_len = mp_req->resp_len; + resp_buf = mp_req->resp_buf; + + /* + * If a middle path ELS command times out, don't try to return + * the command but rather do any internal cleanup and then libfc + * timeout the command and clean up its internal resources. + */ + if (els_req->event == QEDF_IOREQ_EV_ELS_TMO) { + /* + * If ADISC times out, libfc will timeout the exchange and then + * try to send a PLOGI which will timeout since the session is + * still offloaded. Force libfc to logout the session which + * will offload the connection and allow the PLOGI response to + * flow over the LL2 path. + */ + if (cb_arg->op == ELS_ADISC) + qedf_restart_rport(fcport); + return; + } + + if (sizeof(struct fc_frame_header) + resp_len > QEDF_PAGE_SIZE) { + QEDF_ERR(&(fcport->qedf->dbg_ctx), "resp_len is " + "beyond page size.\n"); + goto free_arg; + } + + fp = fc_frame_alloc(fcport->qedf->lport, resp_len); + if (!fp) { + QEDF_ERR(&(fcport->qedf->dbg_ctx), + "fc_frame_alloc failure.\n"); + return; + } + + /* Copy frame header from firmware into fp */ + fh = (struct fc_frame_header *)fc_frame_header_get(fp); + memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); + + /* Copy payload from firmware into fp */ + fc_payload = fc_frame_payload_get(fp, resp_len); + memcpy(fc_payload, resp_buf, resp_len); + + QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, + "Completing OX_ID 0x%x back to libfc.\n", l2_oxid); + qedf_process_l2_frame_compl(fcport, fp, l2_oxid); + +free_arg: + kfree(cb_arg); +} + +int qedf_send_adisc(struct qedf_rport *fcport, struct fc_frame *fp) +{ + struct fc_els_adisc *adisc; + struct fc_frame_header *fh; + struct fc_lport *lport = fcport->qedf->lport; + struct qedf_els_cb_arg *cb_arg = NULL; + struct qedf_ctx *qedf; + uint32_t r_a_tov = lport->r_a_tov; + int rc; + + qedf = fcport->qedf; + fh = fc_frame_header_get(fp); + + cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); + if (!cb_arg) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " + "ADISC\n"); + rc = -ENOMEM; + goto adisc_err; + } + cb_arg->l2_oxid = ntohs(fh->fh_ox_id); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "Sending ADISC ox_id=0x%x.\n", cb_arg->l2_oxid); + + adisc = fc_frame_payload_get(fp, sizeof(*adisc)); + + rc = qedf_initiate_els(fcport, ELS_ADISC, adisc, sizeof(*adisc), + qedf_l2_els_compl, cb_arg, r_a_tov); + +adisc_err: + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "ADISC failed.\n"); + kfree(cb_arg); + } + return rc; +} + +static void qedf_srr_compl(struct qedf_els_cb_arg *cb_arg) +{ + struct qedf_ioreq *orig_io_req; + struct qedf_ioreq *srr_req; + struct qedf_mp_req *mp_req; + struct fc_frame_header *mp_fc_hdr, *fh; + struct fc_frame *fp; + void *resp_buf, *fc_payload; + u32 resp_len; + struct fc_lport *lport; + struct qedf_ctx *qedf; + int refcount; + u8 opcode; + + srr_req = cb_arg->io_req; + qedf = srr_req->fcport->qedf; + lport = qedf->lport; + + orig_io_req = cb_arg->aborted_io_req; + + if (!orig_io_req) + goto out_free; + + clear_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags); + + if (srr_req->event != QEDF_IOREQ_EV_ELS_TMO && + srr_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) + cancel_delayed_work_sync(&orig_io_req->timeout_work); + + refcount = kref_read(&orig_io_req->refcount); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," + " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", + orig_io_req, orig_io_req->xid, srr_req->xid, refcount); + + /* If a SRR times out, simply free resources */ + if (srr_req->event == QEDF_IOREQ_EV_ELS_TMO) + goto out_free; + + /* Normalize response data into struct fc_frame */ + mp_req = &(srr_req->mp_req); + mp_fc_hdr = &(mp_req->resp_fc_hdr); + resp_len = mp_req->resp_len; + resp_buf = mp_req->resp_buf; + + fp = fc_frame_alloc(lport, resp_len); + if (!fp) { + QEDF_ERR(&(qedf->dbg_ctx), + "fc_frame_alloc failure.\n"); + goto out_free; + } + + /* Copy frame header from firmware into fp */ + fh = (struct fc_frame_header *)fc_frame_header_get(fp); + memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); + + /* Copy payload from firmware into fp */ + fc_payload = fc_frame_payload_get(fp, resp_len); + memcpy(fc_payload, resp_buf, resp_len); + + opcode = fc_frame_payload_op(fp); + switch (opcode) { + case ELS_LS_ACC: + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "SRR success.\n"); + break; + case ELS_LS_RJT: + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, + "SRR rejected.\n"); + qedf_initiate_abts(orig_io_req, true); + break; + } + + fc_frame_free(fp); +out_free: + /* Put reference for original command since SRR completed */ + kref_put(&orig_io_req->refcount, qedf_release_cmd); + kfree(cb_arg); +} + +static int qedf_send_srr(struct qedf_ioreq *orig_io_req, u32 offset, u8 r_ctl) +{ + struct fcp_srr srr; + struct qedf_ctx *qedf; + struct qedf_rport *fcport; + struct fc_lport *lport; + struct qedf_els_cb_arg *cb_arg = NULL; + u32 sid, r_a_tov; + int rc; + + if (!orig_io_req) { + QEDF_ERR(NULL, "orig_io_req is NULL.\n"); + return -EINVAL; + } + + fcport = orig_io_req->fcport; + + /* Check that fcport is still offloaded */ + if (!(test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags))) { + QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); + return -EINVAL; + } + + if (!fcport->qedf) { + QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); + return -EINVAL; + } + + /* Take reference until SRR command completion */ + kref_get(&orig_io_req->refcount); + + qedf = fcport->qedf; + lport = qedf->lport; + sid = fcport->sid; + r_a_tov = lport->r_a_tov; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending SRR orig_io=%p, " + "orig_xid=0x%x\n", orig_io_req, orig_io_req->xid); + memset(&srr, 0, sizeof(srr)); + + cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); + if (!cb_arg) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " + "SRR\n"); + rc = -ENOMEM; + goto srr_err; + } + + cb_arg->aborted_io_req = orig_io_req; + + srr.srr_op = ELS_SRR; + srr.srr_ox_id = htons(orig_io_req->xid); + srr.srr_rx_id = htons(orig_io_req->rx_id); + srr.srr_rel_off = htonl(offset); + srr.srr_r_ctl = r_ctl; + + rc = qedf_initiate_els(fcport, ELS_SRR, &srr, sizeof(srr), + qedf_srr_compl, cb_arg, r_a_tov); + +srr_err: + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "SRR failed - release orig_io_req" + "=0x%x\n", orig_io_req->xid); + kfree(cb_arg); + /* If we fail to queue SRR, send ABTS to orig_io */ + qedf_initiate_abts(orig_io_req, true); + kref_put(&orig_io_req->refcount, qedf_release_cmd); + } else + /* Tell other threads that SRR is in progress */ + set_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags); + + return rc; +} + +static void qedf_initiate_seq_cleanup(struct qedf_ioreq *orig_io_req, + u32 offset, u8 r_ctl) +{ + struct qedf_rport *fcport; + unsigned long flags; + struct qedf_els_cb_arg *cb_arg; + + fcport = orig_io_req->fcport; + + QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, + "Doing sequence cleanup for xid=0x%x offset=%u.\n", + orig_io_req->xid, offset); + + cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); + if (!cb_arg) { + QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to allocate cb_arg " + "for sequence cleanup\n"); + return; + } + + /* Get reference for cleanup request */ + kref_get(&orig_io_req->refcount); + + orig_io_req->cmd_type = QEDF_SEQ_CLEANUP; + cb_arg->offset = offset; + cb_arg->r_ctl = r_ctl; + orig_io_req->cb_arg = cb_arg; + + qedf_cmd_timer_set(fcport->qedf, orig_io_req, + QEDF_CLEANUP_TIMEOUT * HZ); + + spin_lock_irqsave(&fcport->rport_lock, flags); + + qedf_add_to_sq(fcport, orig_io_req->xid, 0, + FCOE_TASK_TYPE_SEQUENCE_CLEANUP, offset); + qedf_ring_doorbell(fcport); + + spin_unlock_irqrestore(&fcport->rport_lock, flags); +} + +void qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf, + struct fcoe_cqe *cqe, struct qedf_ioreq *io_req) +{ + int rc; + struct qedf_els_cb_arg *cb_arg; + + cb_arg = io_req->cb_arg; + + /* If we timed out just free resources */ + if (io_req->event == QEDF_IOREQ_EV_ELS_TMO || !cqe) + goto free; + + /* Kill the timer we put on the request */ + cancel_delayed_work_sync(&io_req->timeout_work); + + rc = qedf_send_srr(io_req, cb_arg->offset, cb_arg->r_ctl); + if (rc) + QEDF_ERR(&(qedf->dbg_ctx), "Unable to send SRR, I/O will " + "abort, xid=0x%x.\n", io_req->xid); +free: + kfree(cb_arg); + kref_put(&io_req->refcount, qedf_release_cmd); +} + +static bool qedf_requeue_io_req(struct qedf_ioreq *orig_io_req) +{ + struct qedf_rport *fcport; + struct qedf_ioreq *new_io_req; + unsigned long flags; + bool rc = false; + + fcport = orig_io_req->fcport; + if (!fcport) { + QEDF_ERR(NULL, "fcport is NULL.\n"); + goto out; + } + + if (!orig_io_req->sc_cmd) { + QEDF_ERR(&(fcport->qedf->dbg_ctx), "sc_cmd is NULL for " + "xid=0x%x.\n", orig_io_req->xid); + goto out; + } + + new_io_req = qedf_alloc_cmd(fcport, QEDF_SCSI_CMD); + if (!new_io_req) { + QEDF_ERR(&(fcport->qedf->dbg_ctx), "Could not allocate new " + "io_req.\n"); + goto out; + } + + new_io_req->sc_cmd = orig_io_req->sc_cmd; + + /* + * This keeps the sc_cmd struct from being returned to the tape + * driver and being requeued twice. We do need to put a reference + * for the original I/O request since we will not do a SCSI completion + * for it. + */ + orig_io_req->sc_cmd = NULL; + kref_put(&orig_io_req->refcount, qedf_release_cmd); + + spin_lock_irqsave(&fcport->rport_lock, flags); + + /* kref for new command released in qedf_post_io_req on error */ + if (qedf_post_io_req(fcport, new_io_req)) { + QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to post io_req\n"); + /* Return SQE to pool */ + atomic_inc(&fcport->free_sqes); + } else { + QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, + "Reissued SCSI command from orig_xid=0x%x on " + "new_xid=0x%x.\n", orig_io_req->xid, new_io_req->xid); + /* + * Abort the original I/O but do not return SCSI command as + * it has been reissued on another OX_ID. + */ + spin_unlock_irqrestore(&fcport->rport_lock, flags); + qedf_initiate_abts(orig_io_req, false); + goto out; + } + + spin_unlock_irqrestore(&fcport->rport_lock, flags); +out: + return rc; +} + + +static void qedf_rec_compl(struct qedf_els_cb_arg *cb_arg) +{ + struct qedf_ioreq *orig_io_req; + struct qedf_ioreq *rec_req; + struct qedf_mp_req *mp_req; + struct fc_frame_header *mp_fc_hdr, *fh; + struct fc_frame *fp; + void *resp_buf, *fc_payload; + u32 resp_len; + struct fc_lport *lport; + struct qedf_ctx *qedf; + int refcount; + enum fc_rctl r_ctl; + struct fc_els_ls_rjt *rjt; + struct fc_els_rec_acc *acc; + u8 opcode; + u32 offset, e_stat; + struct scsi_cmnd *sc_cmd; + bool srr_needed = false; + + rec_req = cb_arg->io_req; + qedf = rec_req->fcport->qedf; + lport = qedf->lport; + + orig_io_req = cb_arg->aborted_io_req; + + if (!orig_io_req) + goto out_free; + + if (rec_req->event != QEDF_IOREQ_EV_ELS_TMO && + rec_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) + cancel_delayed_work_sync(&orig_io_req->timeout_work); + + refcount = kref_read(&orig_io_req->refcount); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," + " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", + orig_io_req, orig_io_req->xid, rec_req->xid, refcount); + + /* If a REC times out, free resources */ + if (rec_req->event == QEDF_IOREQ_EV_ELS_TMO) + goto out_free; + + /* Normalize response data into struct fc_frame */ + mp_req = &(rec_req->mp_req); + mp_fc_hdr = &(mp_req->resp_fc_hdr); + resp_len = mp_req->resp_len; + acc = resp_buf = mp_req->resp_buf; + + fp = fc_frame_alloc(lport, resp_len); + if (!fp) { + QEDF_ERR(&(qedf->dbg_ctx), + "fc_frame_alloc failure.\n"); + goto out_free; + } + + /* Copy frame header from firmware into fp */ + fh = (struct fc_frame_header *)fc_frame_header_get(fp); + memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); + + /* Copy payload from firmware into fp */ + fc_payload = fc_frame_payload_get(fp, resp_len); + memcpy(fc_payload, resp_buf, resp_len); + + opcode = fc_frame_payload_op(fp); + if (opcode == ELS_LS_RJT) { + rjt = fc_frame_payload_get(fp, sizeof(*rjt)); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "Received LS_RJT for REC: er_reason=0x%x, " + "er_explan=0x%x.\n", rjt->er_reason, rjt->er_explan); + /* + * The following response(s) mean that we need to reissue the + * request on another exchange. We need to do this without + * informing the upper layers lest it cause an application + * error. + */ + if ((rjt->er_reason == ELS_RJT_LOGIC || + rjt->er_reason == ELS_RJT_UNAB) && + rjt->er_explan == ELS_EXPL_OXID_RXID) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "Handle CMD LOST case.\n"); + qedf_requeue_io_req(orig_io_req); + } + } else if (opcode == ELS_LS_ACC) { + offset = ntohl(acc->reca_fc4value); + e_stat = ntohl(acc->reca_e_stat); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "Received LS_ACC for REC: offset=0x%x, e_stat=0x%x.\n", + offset, e_stat); + if (e_stat & ESB_ST_SEQ_INIT) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "Target has the seq init\n"); + goto out_free_frame; + } + sc_cmd = orig_io_req->sc_cmd; + if (!sc_cmd) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "sc_cmd is NULL for xid=0x%x.\n", + orig_io_req->xid); + goto out_free_frame; + } + /* SCSI write case */ + if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { + if (offset == orig_io_req->data_xfer_len) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "WRITE - response lost.\n"); + r_ctl = FC_RCTL_DD_CMD_STATUS; + srr_needed = true; + offset = 0; + } else { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "WRITE - XFER_RDY/DATA lost.\n"); + r_ctl = FC_RCTL_DD_DATA_DESC; + /* Use data from warning CQE instead of REC */ + offset = orig_io_req->tx_buf_off; + } + /* SCSI read case */ + } else { + if (orig_io_req->rx_buf_off == + orig_io_req->data_xfer_len) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "READ - response lost.\n"); + srr_needed = true; + r_ctl = FC_RCTL_DD_CMD_STATUS; + offset = 0; + } else { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "READ - DATA lost.\n"); + /* + * For read case we always set the offset to 0 + * for sequence recovery task. + */ + offset = 0; + r_ctl = FC_RCTL_DD_SOL_DATA; + } + } + + if (srr_needed) + qedf_send_srr(orig_io_req, offset, r_ctl); + else + qedf_initiate_seq_cleanup(orig_io_req, offset, r_ctl); + } + +out_free_frame: + fc_frame_free(fp); +out_free: + /* Put reference for original command since REC completed */ + kref_put(&orig_io_req->refcount, qedf_release_cmd); + kfree(cb_arg); +} + +/* Assumes kref is already held by caller */ +int qedf_send_rec(struct qedf_ioreq *orig_io_req) +{ + + struct fc_els_rec rec; + struct qedf_rport *fcport; + struct fc_lport *lport; + struct qedf_els_cb_arg *cb_arg = NULL; + struct qedf_ctx *qedf; + uint32_t sid; + uint32_t r_a_tov; + int rc; + + if (!orig_io_req) { + QEDF_ERR(NULL, "orig_io_req is NULL.\n"); + return -EINVAL; + } + + fcport = orig_io_req->fcport; + + /* Check that fcport is still offloaded */ + if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); + return -EINVAL; + } + + if (!fcport->qedf) { + QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); + return -EINVAL; + } + + /* Take reference until REC command completion */ + kref_get(&orig_io_req->refcount); + + qedf = fcport->qedf; + lport = qedf->lport; + sid = fcport->sid; + r_a_tov = lport->r_a_tov; + + memset(&rec, 0, sizeof(rec)); + + cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); + if (!cb_arg) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " + "REC\n"); + rc = -ENOMEM; + goto rec_err; + } + + cb_arg->aborted_io_req = orig_io_req; + + rec.rec_cmd = ELS_REC; + hton24(rec.rec_s_id, sid); + rec.rec_ox_id = htons(orig_io_req->xid); + rec.rec_rx_id = + htons(orig_io_req->task->tstorm_st_context.read_write.rx_id); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending REC orig_io=%p, " + "orig_xid=0x%x rx_id=0x%x\n", orig_io_req, + orig_io_req->xid, rec.rec_rx_id); + rc = qedf_initiate_els(fcport, ELS_REC, &rec, sizeof(rec), + qedf_rec_compl, cb_arg, r_a_tov); + +rec_err: + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "REC failed - release orig_io_req" + "=0x%x\n", orig_io_req->xid); + kfree(cb_arg); + kref_put(&orig_io_req->refcount, qedf_release_cmd); + } + return rc; +} diff --git a/drivers/scsi/qedf/qedf_fip.c b/drivers/scsi/qedf/qedf_fip.c new file mode 100644 index 000000000000..868d423380d1 --- /dev/null +++ b/drivers/scsi/qedf/qedf_fip.c @@ -0,0 +1,269 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include <linux/if_ether.h> +#include <linux/if_vlan.h> +#include "qedf.h" + +extern const struct qed_fcoe_ops *qed_ops; +/* + * FIP VLAN functions that will eventually move to libfcoe. + */ + +void qedf_fcoe_send_vlan_req(struct qedf_ctx *qedf) +{ + struct sk_buff *skb; + char *eth_fr; + int fr_len; + struct fip_vlan *vlan; +#define MY_FIP_ALL_FCF_MACS ((__u8[6]) { 1, 0x10, 0x18, 1, 0, 2 }) + static u8 my_fcoe_all_fcfs[ETH_ALEN] = MY_FIP_ALL_FCF_MACS; + + skb = dev_alloc_skb(sizeof(struct fip_vlan)); + if (!skb) + return; + + fr_len = sizeof(*vlan); + eth_fr = (char *)skb->data; + vlan = (struct fip_vlan *)eth_fr; + + memset(vlan, 0, sizeof(*vlan)); + ether_addr_copy(vlan->eth.h_source, qedf->mac); + ether_addr_copy(vlan->eth.h_dest, my_fcoe_all_fcfs); + vlan->eth.h_proto = htons(ETH_P_FIP); + + vlan->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); + vlan->fip.fip_op = htons(FIP_OP_VLAN); + vlan->fip.fip_subcode = FIP_SC_VL_REQ; + vlan->fip.fip_dl_len = htons(sizeof(vlan->desc) / FIP_BPW); + + vlan->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC; + vlan->desc.mac.fd_desc.fip_dlen = sizeof(vlan->desc.mac) / FIP_BPW; + ether_addr_copy(vlan->desc.mac.fd_mac, qedf->mac); + + vlan->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME; + vlan->desc.wwnn.fd_desc.fip_dlen = sizeof(vlan->desc.wwnn) / FIP_BPW; + put_unaligned_be64(qedf->lport->wwnn, &vlan->desc.wwnn.fd_wwn); + + skb_put(skb, sizeof(*vlan)); + skb->protocol = htons(ETH_P_FIP); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Sending FIP VLAN " + "request."); + + if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) { + QEDF_WARN(&(qedf->dbg_ctx), "Cannot send vlan request " + "because link is not up.\n"); + + kfree_skb(skb); + return; + } + qed_ops->ll2->start_xmit(qedf->cdev, skb); +} + +static void qedf_fcoe_process_vlan_resp(struct qedf_ctx *qedf, + struct sk_buff *skb) +{ + struct fip_header *fiph; + struct fip_desc *desc; + u16 vid = 0; + ssize_t rlen; + size_t dlen; + + fiph = (struct fip_header *)(((void *)skb->data) + 2 * ETH_ALEN + 2); + + rlen = ntohs(fiph->fip_dl_len) * 4; + desc = (struct fip_desc *)(fiph + 1); + while (rlen > 0) { + dlen = desc->fip_dlen * FIP_BPW; + switch (desc->fip_dtype) { + case FIP_DT_VLAN: + vid = ntohs(((struct fip_vlan_desc *)desc)->fd_vlan); + break; + } + desc = (struct fip_desc *)((char *)desc + dlen); + rlen -= dlen; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "VLAN response, " + "vid=0x%x.\n", vid); + + if (vid > 0 && qedf->vlan_id != vid) { + qedf_set_vlan_id(qedf, vid); + + /* Inform waiter that it's ok to call fcoe_ctlr_link up() */ + complete(&qedf->fipvlan_compl); + } +} + +void qedf_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) +{ + struct qedf_ctx *qedf = container_of(fip, struct qedf_ctx, ctlr); + struct ethhdr *eth_hdr; + struct vlan_ethhdr *vlan_hdr; + struct fip_header *fiph; + u16 op, vlan_tci = 0; + u8 sub; + + if (!test_bit(QEDF_LL2_STARTED, &qedf->flags)) { + QEDF_WARN(&(qedf->dbg_ctx), "LL2 not started\n"); + kfree_skb(skb); + return; + } + + fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2); + eth_hdr = (struct ethhdr *)skb_mac_header(skb); + op = ntohs(fiph->fip_op); + sub = fiph->fip_subcode; + + if (!qedf->vlan_hw_insert) { + vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, sizeof(*vlan_hdr) + - sizeof(*eth_hdr)); + memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN); + vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q); + vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto; + vlan_hdr->h_vlan_TCI = vlan_tci = htons(qedf->vlan_id); + } + + /* Update eth_hdr since we added a VLAN tag */ + eth_hdr = (struct ethhdr *)skb_mac_header(skb); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame send: " + "dest=%pM op=%x sub=%x vlan=%04x.", eth_hdr->h_dest, op, sub, + ntohs(vlan_tci)); + if (qedf_dump_frames) + print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb->len, false); + + qed_ops->ll2->start_xmit(qedf->cdev, skb); +} + +/* Process incoming FIP frames. */ +void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb) +{ + struct ethhdr *eth_hdr; + struct fip_header *fiph; + struct fip_desc *desc; + struct fip_mac_desc *mp; + struct fip_wwn_desc *wp; + struct fip_vn_desc *vp; + size_t rlen, dlen; + uint32_t cvl_port_id; + __u8 cvl_mac[ETH_ALEN]; + u16 op; + u8 sub; + + eth_hdr = (struct ethhdr *)skb_mac_header(skb); + fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2); + op = ntohs(fiph->fip_op); + sub = fiph->fip_subcode; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame received: " + "skb=%p fiph=%p source=%pM op=%x sub=%x", skb, fiph, + eth_hdr->h_source, op, sub); + if (qedf_dump_frames) + print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb->len, false); + + /* Handle FIP VLAN resp in the driver */ + if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) { + qedf_fcoe_process_vlan_resp(qedf, skb); + qedf->vlan_hw_insert = 0; + kfree_skb(skb); + } else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Clear virtual " + "link received.\n"); + + /* Check that an FCF has been selected by fcoe */ + if (qedf->ctlr.sel_fcf == NULL) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Dropping CVL since FCF has not been selected " + "yet."); + return; + } + + cvl_port_id = 0; + memset(cvl_mac, 0, ETH_ALEN); + /* + * We need to loop through the CVL descriptors to determine + * if we want to reset the fcoe link + */ + rlen = ntohs(fiph->fip_dl_len) * FIP_BPW; + desc = (struct fip_desc *)(fiph + 1); + while (rlen >= sizeof(*desc)) { + dlen = desc->fip_dlen * FIP_BPW; + switch (desc->fip_dtype) { + case FIP_DT_MAC: + mp = (struct fip_mac_desc *)desc; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, + "fd_mac=%pM.\n", __func__, mp->fd_mac); + ether_addr_copy(cvl_mac, mp->fd_mac); + break; + case FIP_DT_NAME: + wp = (struct fip_wwn_desc *)desc; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, + "fc_wwpn=%016llx.\n", + get_unaligned_be64(&wp->fd_wwn)); + break; + case FIP_DT_VN_ID: + vp = (struct fip_vn_desc *)desc; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, + "fd_fc_id=%x.\n", ntoh24(vp->fd_fc_id)); + cvl_port_id = ntoh24(vp->fd_fc_id); + break; + default: + /* Ignore anything else */ + break; + } + desc = (struct fip_desc *)((char *)desc + dlen); + rlen -= dlen; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, + "cvl_port_id=%06x cvl_mac=%pM.\n", cvl_port_id, + cvl_mac); + if (cvl_port_id == qedf->lport->port_id && + ether_addr_equal(cvl_mac, + qedf->ctlr.sel_fcf->fcf_mac)) { + fcoe_ctlr_link_down(&qedf->ctlr); + qedf_wait_for_upload(qedf); + fcoe_ctlr_link_up(&qedf->ctlr); + } + kfree_skb(skb); + } else { + /* Everything else is handled by libfcoe */ + __skb_pull(skb, ETH_HLEN); + fcoe_ctlr_recv(&qedf->ctlr, skb); + } +} + +void qedf_update_src_mac(struct fc_lport *lport, u8 *addr) +{ + struct qedf_ctx *qedf = lport_priv(lport); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Setting data_src_addr=%pM.\n", addr); + ether_addr_copy(qedf->data_src_addr, addr); +} + +u8 *qedf_get_src_mac(struct fc_lport *lport) +{ + u8 mac[ETH_ALEN]; + u8 port_id[3]; + struct qedf_ctx *qedf = lport_priv(lport); + + /* We need to use the lport port_id to create the data_src_addr */ + if (is_zero_ether_addr(qedf->data_src_addr)) { + hton24(port_id, lport->port_id); + fc_fcoe_set_mac(mac, port_id); + qedf->ctlr.update_mac(lport, mac); + } + return qedf->data_src_addr; +} diff --git a/drivers/scsi/qedf/qedf_hsi.h b/drivers/scsi/qedf/qedf_hsi.h new file mode 100644 index 000000000000..dfd65dec2874 --- /dev/null +++ b/drivers/scsi/qedf/qedf_hsi.h @@ -0,0 +1,422 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#ifndef __QEDF_HSI__ +#define __QEDF_HSI__ +/* + * Add include to common target + */ +#include <linux/qed/common_hsi.h> + +/* + * Add include to common storage target + */ +#include <linux/qed/storage_common.h> + +/* + * Add include to common fcoe target for both eCore and protocol driver + */ +#include <linux/qed/fcoe_common.h> + + +/* + * FCoE CQ element ABTS information + */ +struct fcoe_abts_info { + u8 r_ctl /* R_CTL in the ABTS response frame */; + u8 reserved0; + __le16 rx_id; + __le32 reserved2[2]; + __le32 fc_payload[3] /* ABTS FC payload response frame */; +}; + + +/* + * FCoE class type + */ +enum fcoe_class_type { + FCOE_TASK_CLASS_TYPE_3, + FCOE_TASK_CLASS_TYPE_2, + MAX_FCOE_CLASS_TYPE +}; + + +/* + * FCoE CMDQ element control information + */ +struct fcoe_cmdqe_control { + __le16 conn_id; + u8 num_additional_cmdqes; + u8 cmdType; + /* true for ABTS request cmdqe. used in Target mode */ +#define FCOE_CMDQE_CONTROL_ABTSREQCMD_MASK 0x1 +#define FCOE_CMDQE_CONTROL_ABTSREQCMD_SHIFT 0 +#define FCOE_CMDQE_CONTROL_RESERVED1_MASK 0x7F +#define FCOE_CMDQE_CONTROL_RESERVED1_SHIFT 1 + u8 reserved2[4]; +}; + +/* + * FCoE control + payload CMDQ element + */ +struct fcoe_cmdqe { + struct fcoe_cmdqe_control hdr; + u8 fc_header[24]; + __le32 fcp_cmd_payload[8]; +}; + + + +/* + * FCP RSP flags + */ +struct fcoe_fcp_rsp_flags { + u8 flags; +#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID_MASK 0x1 +#define FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID_SHIFT 0 +#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID_MASK 0x1 +#define FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID_SHIFT 1 +#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER_MASK 0x1 +#define FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER_SHIFT 2 +#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER_MASK 0x1 +#define FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER_SHIFT 3 +#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ_MASK 0x1 +#define FCOE_FCP_RSP_FLAGS_FCP_CONF_REQ_SHIFT 4 +#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS_MASK 0x7 +#define FCOE_FCP_RSP_FLAGS_FCP_BIDI_FLAGS_SHIFT 5 +}; + +/* + * FCoE CQ element response information + */ +struct fcoe_cqe_rsp_info { + struct fcoe_fcp_rsp_flags rsp_flags; + u8 scsi_status_code; + __le16 retry_delay_timer; + __le32 fcp_resid; + __le32 fcp_sns_len; + __le32 fcp_rsp_len; + __le16 rx_id; + u8 fw_error_flags; +#define FCOE_CQE_RSP_INFO_FW_UNDERRUN_MASK 0x1 /* FW detected underrun */ +#define FCOE_CQE_RSP_INFO_FW_UNDERRUN_SHIFT 0 +#define FCOE_CQE_RSP_INFO_RESREVED_MASK 0x7F +#define FCOE_CQE_RSP_INFO_RESREVED_SHIFT 1 + u8 reserved; + __le32 fw_residual /* Residual bytes calculated by FW */; +}; + +/* + * FCoE CQ element Target completion information + */ +struct fcoe_cqe_target_info { + __le16 rx_id; + __le16 reserved0; + __le32 reserved1[5]; +}; + +/* + * FCoE error/warning reporting entry + */ +struct fcoe_err_report_entry { + __le32 err_warn_bitmap_lo /* Error bitmap lower 32 bits */; + __le32 err_warn_bitmap_hi /* Error bitmap higher 32 bits */; + /* Buffer offset the beginning of the Sequence last transmitted */ + __le32 tx_buf_off; + /* Buffer offset from the beginning of the Sequence last received */ + __le32 rx_buf_off; + __le16 rx_id /* RX_ID of the associated task */; + __le16 reserved1; + __le32 reserved2; +}; + +/* + * FCoE CQ element middle path information + */ +struct fcoe_cqe_midpath_info { + __le32 data_placement_size; + __le16 rx_id; + __le16 reserved0; + __le32 reserved1[4]; +}; + +/* + * FCoE CQ element unsolicited information + */ +struct fcoe_unsolic_info { + /* BD information: Physical address and opaque data */ + struct scsi_bd bd_info; + __le16 conn_id /* Connection ID the frame is associated to */; + __le16 pkt_len /* Packet length */; + u8 reserved1[4]; +}; + +/* + * FCoE warning reporting entry + */ +struct fcoe_warning_report_entry { + /* BD information: Physical address and opaque data */ + struct scsi_bd bd_info; + /* Buffer offset the beginning of the Sequence last transmitted */ + __le32 buf_off; + __le16 rx_id /* RX_ID of the associated task */; + __le16 reserved1; +}; + +/* + * FCoE CQ element information + */ +union fcoe_cqe_info { + struct fcoe_cqe_rsp_info rsp_info /* Response completion information */; + /* Target completion information */ + struct fcoe_cqe_target_info target_info; + /* Error completion information */ + struct fcoe_err_report_entry err_info; + struct fcoe_abts_info abts_info /* ABTS completion information */; + /* Middle path completion information */ + struct fcoe_cqe_midpath_info midpath_info; + /* Unsolicited packet completion information */ + struct fcoe_unsolic_info unsolic_info; + /* Warning completion information (Rec Tov expiration) */ + struct fcoe_warning_report_entry warn_info; +}; + +/* + * FCoE CQ element + */ +struct fcoe_cqe { + __le32 cqe_data; + /* The task identifier (OX_ID) to be completed */ +#define FCOE_CQE_TASK_ID_MASK 0xFFFF +#define FCOE_CQE_TASK_ID_SHIFT 0 + /* + * The CQE type: 0x0 Indicating on a pending work request completion. + * 0x1 - Indicating on an unsolicited event notification. use enum + * fcoe_cqe_type (use enum fcoe_cqe_type) + */ +#define FCOE_CQE_CQE_TYPE_MASK 0xF +#define FCOE_CQE_CQE_TYPE_SHIFT 16 +#define FCOE_CQE_RESERVED0_MASK 0xFFF +#define FCOE_CQE_RESERVED0_SHIFT 20 + __le16 reserved1; + __le16 fw_cq_prod; + union fcoe_cqe_info cqe_info; +}; + +/* + * FCoE CQE type + */ +enum fcoe_cqe_type { + /* solicited response on a R/W or middle-path SQE */ + FCOE_GOOD_COMPLETION_CQE_TYPE, + FCOE_UNSOLIC_CQE_TYPE /* unsolicited packet, RQ consumed */, + FCOE_ERROR_DETECTION_CQE_TYPE /* timer expiration, validation error */, + FCOE_WARNING_CQE_TYPE /* rec_tov or rr_tov timer expiration */, + FCOE_EXCH_CLEANUP_CQE_TYPE /* task cleanup completed */, + FCOE_ABTS_CQE_TYPE /* ABTS received and task cleaned */, + FCOE_DUMMY_CQE_TYPE /* just increment SQ CONS */, + /* Task was completed wight after sending a pkt to the target */ + FCOE_LOCAL_COMP_CQE_TYPE, + MAX_FCOE_CQE_TYPE +}; + + +/* + * FCoE device type + */ +enum fcoe_device_type { + FCOE_TASK_DEV_TYPE_DISK, + FCOE_TASK_DEV_TYPE_TAPE, + MAX_FCOE_DEVICE_TYPE +}; + + + + +/* + * FCoE fast path error codes + */ +enum fcoe_fp_error_warning_code { + FCOE_ERROR_CODE_XFER_OOO_RO /* XFER error codes */, + FCOE_ERROR_CODE_XFER_RO_NOT_ALIGNED, + FCOE_ERROR_CODE_XFER_NULL_BURST_LEN, + FCOE_ERROR_CODE_XFER_RO_GREATER_THAN_DATA2TRNS, + FCOE_ERROR_CODE_XFER_INVALID_PAYLOAD_SIZE, + FCOE_ERROR_CODE_XFER_TASK_TYPE_NOT_WRITE, + FCOE_ERROR_CODE_XFER_PEND_XFER_SET, + FCOE_ERROR_CODE_XFER_OPENED_SEQ, + FCOE_ERROR_CODE_XFER_FCTL, + FCOE_ERROR_CODE_FCP_RSP_BIDI_FLAGS_SET /* FCP RSP error codes */, + FCOE_ERROR_CODE_FCP_RSP_INVALID_LENGTH_FIELD, + FCOE_ERROR_CODE_FCP_RSP_INVALID_SNS_FIELD, + FCOE_ERROR_CODE_FCP_RSP_INVALID_PAYLOAD_SIZE, + FCOE_ERROR_CODE_FCP_RSP_PEND_XFER_SET, + FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ, + FCOE_ERROR_CODE_FCP_RSP_FCTL, + FCOE_ERROR_CODE_FCP_RSP_LAST_SEQ_RESET, + FCOE_ERROR_CODE_FCP_RSP_CONF_REQ_NOT_SUPPORTED_YET, + FCOE_ERROR_CODE_DATA_OOO_RO /* FCP DATA error codes */, + FCOE_ERROR_CODE_DATA_EXCEEDS_DEFINED_MAX_FRAME_SIZE, + FCOE_ERROR_CODE_DATA_EXCEEDS_DATA2TRNS, + FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET, + FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET, + FCOE_ERROR_CODE_DATA_EOFN_END_SEQ_SET, + FCOE_ERROR_CODE_DATA_EOFT_END_SEQ_RESET, + FCOE_ERROR_CODE_DATA_TASK_TYPE_NOT_READ, + FCOE_ERROR_CODE_DATA_FCTL_INITIATIR, + FCOE_ERROR_CODE_MIDPATH_INVALID_TYPE /* Middle path error codes */, + FCOE_ERROR_CODE_MIDPATH_SOFI3_SEQ_ACTIVE_SET, + FCOE_ERROR_CODE_MIDPATH_SOFN_SEQ_ACTIVE_RESET, + FCOE_ERROR_CODE_MIDPATH_EOFN_END_SEQ_SET, + FCOE_ERROR_CODE_MIDPATH_EOFT_END_SEQ_RESET, + FCOE_ERROR_CODE_MIDPATH_REPLY_FCTL, + FCOE_ERROR_CODE_MIDPATH_INVALID_REPLY, + FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_RCTL, + FCOE_ERROR_CODE_COMMON_MIDDLE_FRAME_WITH_PAD /* Common error codes */, + FCOE_ERROR_CODE_COMMON_SEQ_INIT_IN_TCE, + FCOE_ERROR_CODE_COMMON_FC_HDR_RX_ID_MISMATCH, + FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT, + FCOE_ERROR_CODE_COMMON_DATA_FC_HDR_FCP_TYPE_MISMATCH, + FCOE_ERROR_CODE_COMMON_DATA_NO_MORE_SGES, + FCOE_ERROR_CODE_COMMON_OPTIONAL_FC_HDR, + FCOE_ERROR_CODE_COMMON_READ_TCE_OX_ID_TOO_BIG, + FCOE_ERROR_CODE_COMMON_DATA_WAS_NOT_TRANSMITTED, + FCOE_ERROR_CODE_COMMON_TASK_DDF_RCTL_INFO_FIELD, + FCOE_ERROR_CODE_COMMON_TASK_INVALID_RCTL, + FCOE_ERROR_CODE_COMMON_TASK_RCTL_GENERAL_MISMATCH, + FCOE_ERROR_CODE_E_D_TOV_TIMER_EXPIRATION /* Timer error codes */, + FCOE_WARNING_CODE_REC_TOV_TIMER_EXPIRATION /* Timer error codes */, + FCOE_ERROR_CODE_RR_TOV_TIMER_EXPIRATION /* Timer error codes */, + /* ABTSrsp pckt arrived unexpected */ + FCOE_ERROR_CODE_ABTS_REPLY_UNEXPECTED, + FCOE_ERROR_CODE_TARGET_MODE_FCP_RSP, + FCOE_ERROR_CODE_TARGET_MODE_FCP_XFER, + FCOE_ERROR_CODE_TARGET_MODE_DATA_TASK_TYPE_NOT_WRITE, + FCOE_ERROR_CODE_DATA_FCTL_TARGET, + FCOE_ERROR_CODE_TARGET_DATA_SIZE_NO_MATCH_XFER, + FCOE_ERROR_CODE_TARGET_DIF_CRC_CHECKSUM_ERROR, + FCOE_ERROR_CODE_TARGET_DIF_REF_TAG_ERROR, + FCOE_ERROR_CODE_TARGET_DIF_APP_TAG_ERROR, + MAX_FCOE_FP_ERROR_WARNING_CODE +}; + + +/* + * FCoE RESPQ element + */ +struct fcoe_respqe { + __le16 ox_id /* OX_ID that is located in the FCP_RSP FC header */; + __le16 rx_id /* RX_ID that is located in the FCP_RSP FC header */; + __le32 additional_info; +/* PARAM that is located in the FCP_RSP FC header */ +#define FCOE_RESPQE_PARAM_MASK 0xFFFFFF +#define FCOE_RESPQE_PARAM_SHIFT 0 +/* Indication whther its Target-auto-rsp mode or not */ +#define FCOE_RESPQE_TARGET_AUTO_RSP_MASK 0xFF +#define FCOE_RESPQE_TARGET_AUTO_RSP_SHIFT 24 +}; + + +/* + * FCoE slow path error codes + */ +enum fcoe_sp_error_code { + /* Error codes for Error Reporting in slow path flows */ + FCOE_ERROR_CODE_SLOW_PATH_TOO_MANY_FUNCS, + FCOE_ERROR_SLOW_PATH_CODE_NO_LICENSE, + MAX_FCOE_SP_ERROR_CODE +}; + + +/* + * FCoE SQE request type + */ +enum fcoe_sqe_request_type { + SEND_FCOE_CMD, + SEND_FCOE_MIDPATH, + SEND_FCOE_ABTS_REQUEST, + FCOE_EXCHANGE_CLEANUP, + FCOE_SEQUENCE_RECOVERY, + SEND_FCOE_XFER_RDY, + SEND_FCOE_RSP, + SEND_FCOE_RSP_WITH_SENSE_DATA, + SEND_FCOE_TARGET_DATA, + SEND_FCOE_INITIATOR_DATA, + /* + * Xfer Continuation (==1) ready to be sent. Previous XFERs data + * received successfully. + */ + SEND_FCOE_XFER_CONTINUATION_RDY, + SEND_FCOE_TARGET_ABTS_RSP, + MAX_FCOE_SQE_REQUEST_TYPE +}; + + +/* + * FCoE task TX state + */ +enum fcoe_task_tx_state { + /* Initiate state after driver has initialized the task */ + FCOE_TASK_TX_STATE_NORMAL, + /* Updated by TX path after complete transmitting unsolicited packet */ + FCOE_TASK_TX_STATE_UNSOLICITED_COMPLETED, + /* + * Updated by TX path after start processing the task requesting the + * cleanup/abort operation + */ + FCOE_TASK_TX_STATE_CLEAN_REQ, + FCOE_TASK_TX_STATE_ABTS /* Updated by TX path during abort procedure */, + /* Updated by TX path during exchange cleanup procedure */ + FCOE_TASK_TX_STATE_EXCLEANUP, + /* + * Updated by TX path during exchange cleanup continuation task + * procedure + */ + FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_WRITE_CONT, + /* Updated by TX path during exchange cleanup first xfer procedure */ + FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_WRITE, + /* Updated by TX path during exchange cleanup read task in Target */ + FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_READ_OR_RSP, + /* Updated by TX path during target exchange cleanup procedure */ + FCOE_TASK_TX_STATE_EXCLEANUP_TARGET_WRITE_LAST_CYCLE, + /* Updated by TX path during sequence recovery procedure */ + FCOE_TASK_TX_STATE_SEQRECOVERY, + MAX_FCOE_TASK_TX_STATE +}; + + +/* + * FCoE task type + */ +enum fcoe_task_type { + FCOE_TASK_TYPE_WRITE_INITIATOR, + FCOE_TASK_TYPE_READ_INITIATOR, + FCOE_TASK_TYPE_MIDPATH, + FCOE_TASK_TYPE_UNSOLICITED, + FCOE_TASK_TYPE_ABTS, + FCOE_TASK_TYPE_EXCHANGE_CLEANUP, + FCOE_TASK_TYPE_SEQUENCE_CLEANUP, + FCOE_TASK_TYPE_WRITE_TARGET, + FCOE_TASK_TYPE_READ_TARGET, + FCOE_TASK_TYPE_RSP, + FCOE_TASK_TYPE_RSP_SENSE_DATA, + FCOE_TASK_TYPE_ABTS_TARGET, + FCOE_TASK_TYPE_ENUM_SIZE, + MAX_FCOE_TASK_TYPE +}; + +struct scsi_glbl_queue_entry { + /* Start physical address for the RQ (receive queue) PBL. */ + struct regpair rq_pbl_addr; + /* Start physical address for the CQ (completion queue) PBL. */ + struct regpair cq_pbl_addr; + /* Start physical address for the CMDQ (command queue) PBL. */ + struct regpair cmdq_pbl_addr; +}; + +#endif /* __QEDF_HSI__ */ diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c new file mode 100644 index 000000000000..ee0dcf9d3aba --- /dev/null +++ b/drivers/scsi/qedf/qedf_io.c @@ -0,0 +1,2282 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include <linux/spinlock.h> +#include <linux/vmalloc.h> +#include "qedf.h" +#include <scsi/scsi_tcq.h> + +void qedf_cmd_timer_set(struct qedf_ctx *qedf, struct qedf_ioreq *io_req, + unsigned int timer_msec) +{ + queue_delayed_work(qedf->timer_work_queue, &io_req->timeout_work, + msecs_to_jiffies(timer_msec)); +} + +static void qedf_cmd_timeout(struct work_struct *work) +{ + + struct qedf_ioreq *io_req = + container_of(work, struct qedf_ioreq, timeout_work.work); + struct qedf_ctx *qedf = io_req->fcport->qedf; + struct qedf_rport *fcport = io_req->fcport; + u8 op = 0; + + switch (io_req->cmd_type) { + case QEDF_ABTS: + QEDF_ERR((&qedf->dbg_ctx), "ABTS timeout, xid=0x%x.\n", + io_req->xid); + /* Cleanup timed out ABTS */ + qedf_initiate_cleanup(io_req, true); + complete(&io_req->abts_done); + + /* + * Need to call kref_put for reference taken when initiate_abts + * was called since abts_compl won't be called now that we've + * cleaned up the task. + */ + kref_put(&io_req->refcount, qedf_release_cmd); + + /* + * Now that the original I/O and the ABTS are complete see + * if we need to reconnect to the target. + */ + qedf_restart_rport(fcport); + break; + case QEDF_ELS: + kref_get(&io_req->refcount); + /* + * Don't attempt to clean an ELS timeout as any subseqeunt + * ABTS or cleanup requests just hang. For now just free + * the resources of the original I/O and the RRQ + */ + QEDF_ERR(&(qedf->dbg_ctx), "ELS timeout, xid=0x%x.\n", + io_req->xid); + io_req->event = QEDF_IOREQ_EV_ELS_TMO; + /* Call callback function to complete command */ + if (io_req->cb_func && io_req->cb_arg) { + op = io_req->cb_arg->op; + io_req->cb_func(io_req->cb_arg); + io_req->cb_arg = NULL; + } + qedf_initiate_cleanup(io_req, true); + kref_put(&io_req->refcount, qedf_release_cmd); + break; + case QEDF_SEQ_CLEANUP: + QEDF_ERR(&(qedf->dbg_ctx), "Sequence cleanup timeout, " + "xid=0x%x.\n", io_req->xid); + qedf_initiate_cleanup(io_req, true); + io_req->event = QEDF_IOREQ_EV_ELS_TMO; + qedf_process_seq_cleanup_compl(qedf, NULL, io_req); + break; + default: + break; + } +} + +void qedf_cmd_mgr_free(struct qedf_cmd_mgr *cmgr) +{ + struct io_bdt *bdt_info; + struct qedf_ctx *qedf = cmgr->qedf; + size_t bd_tbl_sz; + u16 min_xid = QEDF_MIN_XID; + u16 max_xid = (FCOE_PARAMS_NUM_TASKS - 1); + int num_ios; + int i; + struct qedf_ioreq *io_req; + + num_ios = max_xid - min_xid + 1; + + /* Free fcoe_bdt_ctx structures */ + if (!cmgr->io_bdt_pool) + goto free_cmd_pool; + + bd_tbl_sz = QEDF_MAX_BDS_PER_CMD * sizeof(struct fcoe_sge); + for (i = 0; i < num_ios; i++) { + bdt_info = cmgr->io_bdt_pool[i]; + if (bdt_info->bd_tbl) { + dma_free_coherent(&qedf->pdev->dev, bd_tbl_sz, + bdt_info->bd_tbl, bdt_info->bd_tbl_dma); + bdt_info->bd_tbl = NULL; + } + } + + /* Destroy io_bdt pool */ + for (i = 0; i < num_ios; i++) { + kfree(cmgr->io_bdt_pool[i]); + cmgr->io_bdt_pool[i] = NULL; + } + + kfree(cmgr->io_bdt_pool); + cmgr->io_bdt_pool = NULL; + +free_cmd_pool: + + for (i = 0; i < num_ios; i++) { + io_req = &cmgr->cmds[i]; + /* Make sure we free per command sense buffer */ + if (io_req->sense_buffer) + dma_free_coherent(&qedf->pdev->dev, + QEDF_SCSI_SENSE_BUFFERSIZE, io_req->sense_buffer, + io_req->sense_buffer_dma); + cancel_delayed_work_sync(&io_req->rrq_work); + } + + /* Free command manager itself */ + vfree(cmgr); +} + +static void qedf_handle_rrq(struct work_struct *work) +{ + struct qedf_ioreq *io_req = + container_of(work, struct qedf_ioreq, rrq_work.work); + + qedf_send_rrq(io_req); + +} + +struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf) +{ + struct qedf_cmd_mgr *cmgr; + struct io_bdt *bdt_info; + struct qedf_ioreq *io_req; + u16 xid; + int i; + int num_ios; + u16 min_xid = QEDF_MIN_XID; + u16 max_xid = (FCOE_PARAMS_NUM_TASKS - 1); + + /* Make sure num_queues is already set before calling this function */ + if (!qedf->num_queues) { + QEDF_ERR(&(qedf->dbg_ctx), "num_queues is not set.\n"); + return NULL; + } + + if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN) { + QEDF_WARN(&(qedf->dbg_ctx), "Invalid min_xid 0x%x and " + "max_xid 0x%x.\n", min_xid, max_xid); + return NULL; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "min xid 0x%x, max xid " + "0x%x.\n", min_xid, max_xid); + + num_ios = max_xid - min_xid + 1; + + cmgr = vzalloc(sizeof(struct qedf_cmd_mgr)); + if (!cmgr) { + QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc cmd mgr.\n"); + return NULL; + } + + cmgr->qedf = qedf; + spin_lock_init(&cmgr->lock); + + /* + * Initialize list of qedf_ioreq. + */ + xid = QEDF_MIN_XID; + + for (i = 0; i < num_ios; i++) { + io_req = &cmgr->cmds[i]; + INIT_DELAYED_WORK(&io_req->timeout_work, qedf_cmd_timeout); + + io_req->xid = xid++; + + INIT_DELAYED_WORK(&io_req->rrq_work, qedf_handle_rrq); + + /* Allocate DMA memory to hold sense buffer */ + io_req->sense_buffer = dma_alloc_coherent(&qedf->pdev->dev, + QEDF_SCSI_SENSE_BUFFERSIZE, &io_req->sense_buffer_dma, + GFP_KERNEL); + if (!io_req->sense_buffer) + goto mem_err; + } + + /* Allocate pool of io_bdts - one for each qedf_ioreq */ + cmgr->io_bdt_pool = kmalloc_array(num_ios, sizeof(struct io_bdt *), + GFP_KERNEL); + + if (!cmgr->io_bdt_pool) { + QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc io_bdt_pool.\n"); + goto mem_err; + } + + for (i = 0; i < num_ios; i++) { + cmgr->io_bdt_pool[i] = kmalloc(sizeof(struct io_bdt), + GFP_KERNEL); + if (!cmgr->io_bdt_pool[i]) { + QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc " + "io_bdt_pool[%d].\n", i); + goto mem_err; + } + } + + for (i = 0; i < num_ios; i++) { + bdt_info = cmgr->io_bdt_pool[i]; + bdt_info->bd_tbl = dma_alloc_coherent(&qedf->pdev->dev, + QEDF_MAX_BDS_PER_CMD * sizeof(struct fcoe_sge), + &bdt_info->bd_tbl_dma, GFP_KERNEL); + if (!bdt_info->bd_tbl) { + QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc " + "bdt_tbl[%d].\n", i); + goto mem_err; + } + } + atomic_set(&cmgr->free_list_cnt, num_ios); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "cmgr->free_list_cnt=%d.\n", + atomic_read(&cmgr->free_list_cnt)); + + return cmgr; + +mem_err: + qedf_cmd_mgr_free(cmgr); + return NULL; +} + +struct qedf_ioreq *qedf_alloc_cmd(struct qedf_rport *fcport, u8 cmd_type) +{ + struct qedf_ctx *qedf = fcport->qedf; + struct qedf_cmd_mgr *cmd_mgr = qedf->cmd_mgr; + struct qedf_ioreq *io_req = NULL; + struct io_bdt *bd_tbl; + u16 xid; + uint32_t free_sqes; + int i; + unsigned long flags; + + free_sqes = atomic_read(&fcport->free_sqes); + + if (!free_sqes) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Returning NULL, free_sqes=%d.\n ", + free_sqes); + goto out_failed; + } + + /* Limit the number of outstanding R/W tasks */ + if ((atomic_read(&fcport->num_active_ios) >= + NUM_RW_TASKS_PER_CONNECTION)) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Returning NULL, num_active_ios=%d.\n", + atomic_read(&fcport->num_active_ios)); + goto out_failed; + } + + /* Limit global TIDs certain tasks */ + if (atomic_read(&cmd_mgr->free_list_cnt) <= GBL_RSVD_TASKS) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Returning NULL, free_list_cnt=%d.\n", + atomic_read(&cmd_mgr->free_list_cnt)); + goto out_failed; + } + + spin_lock_irqsave(&cmd_mgr->lock, flags); + for (i = 0; i < FCOE_PARAMS_NUM_TASKS; i++) { + io_req = &cmd_mgr->cmds[cmd_mgr->idx]; + cmd_mgr->idx++; + if (cmd_mgr->idx == FCOE_PARAMS_NUM_TASKS) + cmd_mgr->idx = 0; + + /* Check to make sure command was previously freed */ + if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags)) + break; + } + + if (i == FCOE_PARAMS_NUM_TASKS) { + spin_unlock_irqrestore(&cmd_mgr->lock, flags); + goto out_failed; + } + + set_bit(QEDF_CMD_OUTSTANDING, &io_req->flags); + spin_unlock_irqrestore(&cmd_mgr->lock, flags); + + atomic_inc(&fcport->num_active_ios); + atomic_dec(&fcport->free_sqes); + xid = io_req->xid; + atomic_dec(&cmd_mgr->free_list_cnt); + + io_req->cmd_mgr = cmd_mgr; + io_req->fcport = fcport; + + /* Hold the io_req against deletion */ + kref_init(&io_req->refcount); + + /* Bind io_bdt for this io_req */ + /* Have a static link between io_req and io_bdt_pool */ + bd_tbl = io_req->bd_tbl = cmd_mgr->io_bdt_pool[xid]; + if (bd_tbl == NULL) { + QEDF_ERR(&(qedf->dbg_ctx), "bd_tbl is NULL, xid=%x.\n", xid); + kref_put(&io_req->refcount, qedf_release_cmd); + goto out_failed; + } + bd_tbl->io_req = io_req; + io_req->cmd_type = cmd_type; + + /* Reset sequence offset data */ + io_req->rx_buf_off = 0; + io_req->tx_buf_off = 0; + io_req->rx_id = 0xffff; /* No OX_ID */ + + return io_req; + +out_failed: + /* Record failure for stats and return NULL to caller */ + qedf->alloc_failures++; + return NULL; +} + +static void qedf_free_mp_resc(struct qedf_ioreq *io_req) +{ + struct qedf_mp_req *mp_req = &(io_req->mp_req); + struct qedf_ctx *qedf = io_req->fcport->qedf; + uint64_t sz = sizeof(struct fcoe_sge); + + /* clear tm flags */ + mp_req->tm_flags = 0; + if (mp_req->mp_req_bd) { + dma_free_coherent(&qedf->pdev->dev, sz, + mp_req->mp_req_bd, mp_req->mp_req_bd_dma); + mp_req->mp_req_bd = NULL; + } + if (mp_req->mp_resp_bd) { + dma_free_coherent(&qedf->pdev->dev, sz, + mp_req->mp_resp_bd, mp_req->mp_resp_bd_dma); + mp_req->mp_resp_bd = NULL; + } + if (mp_req->req_buf) { + dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE, + mp_req->req_buf, mp_req->req_buf_dma); + mp_req->req_buf = NULL; + } + if (mp_req->resp_buf) { + dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE, + mp_req->resp_buf, mp_req->resp_buf_dma); + mp_req->resp_buf = NULL; + } +} + +void qedf_release_cmd(struct kref *ref) +{ + struct qedf_ioreq *io_req = + container_of(ref, struct qedf_ioreq, refcount); + struct qedf_cmd_mgr *cmd_mgr = io_req->cmd_mgr; + struct qedf_rport *fcport = io_req->fcport; + + if (io_req->cmd_type == QEDF_ELS || + io_req->cmd_type == QEDF_TASK_MGMT_CMD) + qedf_free_mp_resc(io_req); + + atomic_inc(&cmd_mgr->free_list_cnt); + atomic_dec(&fcport->num_active_ios); + if (atomic_read(&fcport->num_active_ios) < 0) + QEDF_WARN(&(fcport->qedf->dbg_ctx), "active_ios < 0.\n"); + + /* Increment task retry identifier now that the request is released */ + io_req->task_retry_identifier++; + + clear_bit(QEDF_CMD_OUTSTANDING, &io_req->flags); +} + +static int qedf_split_bd(struct qedf_ioreq *io_req, u64 addr, int sg_len, + int bd_index) +{ + struct fcoe_sge *bd = io_req->bd_tbl->bd_tbl; + int frag_size, sg_frags; + + sg_frags = 0; + while (sg_len) { + if (sg_len > QEDF_BD_SPLIT_SZ) + frag_size = QEDF_BD_SPLIT_SZ; + else + frag_size = sg_len; + bd[bd_index + sg_frags].sge_addr.lo = U64_LO(addr); + bd[bd_index + sg_frags].sge_addr.hi = U64_HI(addr); + bd[bd_index + sg_frags].size = (uint16_t)frag_size; + + addr += (u64)frag_size; + sg_frags++; + sg_len -= frag_size; + } + return sg_frags; +} + +static int qedf_map_sg(struct qedf_ioreq *io_req) +{ + struct scsi_cmnd *sc = io_req->sc_cmd; + struct Scsi_Host *host = sc->device->host; + struct fc_lport *lport = shost_priv(host); + struct qedf_ctx *qedf = lport_priv(lport); + struct fcoe_sge *bd = io_req->bd_tbl->bd_tbl; + struct scatterlist *sg; + int byte_count = 0; + int sg_count = 0; + int bd_count = 0; + int sg_frags; + unsigned int sg_len; + u64 addr, end_addr; + int i; + + sg_count = dma_map_sg(&qedf->pdev->dev, scsi_sglist(sc), + scsi_sg_count(sc), sc->sc_data_direction); + + sg = scsi_sglist(sc); + + /* + * New condition to send single SGE as cached-SGL with length less + * than 64k. + */ + if ((sg_count == 1) && (sg_dma_len(sg) <= + QEDF_MAX_SGLEN_FOR_CACHESGL)) { + sg_len = sg_dma_len(sg); + addr = (u64)sg_dma_address(sg); + + bd[bd_count].sge_addr.lo = (addr & 0xffffffff); + bd[bd_count].sge_addr.hi = (addr >> 32); + bd[bd_count].size = (u16)sg_len; + + return ++bd_count; + } + + scsi_for_each_sg(sc, sg, sg_count, i) { + sg_len = sg_dma_len(sg); + addr = (u64)sg_dma_address(sg); + end_addr = (u64)(addr + sg_len); + + /* + * First s/g element in the list so check if the end_addr + * is paged aligned. Also check to make sure the length is + * at least page size. + */ + if ((i == 0) && (sg_count > 1) && + ((end_addr % QEDF_PAGE_SIZE) || + sg_len < QEDF_PAGE_SIZE)) + io_req->use_slowpath = true; + /* + * Last s/g element so check if the start address is paged + * aligned. + */ + else if ((i == (sg_count - 1)) && (sg_count > 1) && + (addr % QEDF_PAGE_SIZE)) + io_req->use_slowpath = true; + /* + * Intermediate s/g element so check if start and end address + * is page aligned. + */ + else if ((i != 0) && (i != (sg_count - 1)) && + ((addr % QEDF_PAGE_SIZE) || (end_addr % QEDF_PAGE_SIZE))) + io_req->use_slowpath = true; + + if (sg_len > QEDF_MAX_BD_LEN) { + sg_frags = qedf_split_bd(io_req, addr, sg_len, + bd_count); + } else { + sg_frags = 1; + bd[bd_count].sge_addr.lo = U64_LO(addr); + bd[bd_count].sge_addr.hi = U64_HI(addr); + bd[bd_count].size = (uint16_t)sg_len; + } + + bd_count += sg_frags; + byte_count += sg_len; + } + + if (byte_count != scsi_bufflen(sc)) + QEDF_ERR(&(qedf->dbg_ctx), "byte_count = %d != " + "scsi_bufflen = %d, task_id = 0x%x.\n", byte_count, + scsi_bufflen(sc), io_req->xid); + + return bd_count; +} + +static int qedf_build_bd_list_from_sg(struct qedf_ioreq *io_req) +{ + struct scsi_cmnd *sc = io_req->sc_cmd; + struct fcoe_sge *bd = io_req->bd_tbl->bd_tbl; + int bd_count; + + if (scsi_sg_count(sc)) { + bd_count = qedf_map_sg(io_req); + if (bd_count == 0) + return -ENOMEM; + } else { + bd_count = 0; + bd[0].sge_addr.lo = bd[0].sge_addr.hi = 0; + bd[0].size = 0; + } + io_req->bd_tbl->bd_valid = bd_count; + + return 0; +} + +static void qedf_build_fcp_cmnd(struct qedf_ioreq *io_req, + struct fcp_cmnd *fcp_cmnd) +{ + struct scsi_cmnd *sc_cmd = io_req->sc_cmd; + + /* fcp_cmnd is 32 bytes */ + memset(fcp_cmnd, 0, FCP_CMND_LEN); + + /* 8 bytes: SCSI LUN info */ + int_to_scsilun(sc_cmd->device->lun, + (struct scsi_lun *)&fcp_cmnd->fc_lun); + + /* 4 bytes: flag info */ + fcp_cmnd->fc_pri_ta = 0; + fcp_cmnd->fc_tm_flags = io_req->mp_req.tm_flags; + fcp_cmnd->fc_flags = io_req->io_req_flags; + fcp_cmnd->fc_cmdref = 0; + + /* Populate data direction */ + if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) + fcp_cmnd->fc_flags |= FCP_CFL_WRDATA; + else if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) + fcp_cmnd->fc_flags |= FCP_CFL_RDDATA; + + fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE; + + /* 16 bytes: CDB information */ + memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len); + + /* 4 bytes: FCP data length */ + fcp_cmnd->fc_dl = htonl(io_req->data_xfer_len); + +} + +static void qedf_init_task(struct qedf_rport *fcport, struct fc_lport *lport, + struct qedf_ioreq *io_req, u32 *ptu_invalidate, + struct fcoe_task_context *task_ctx) +{ + enum fcoe_task_type task_type; + struct scsi_cmnd *sc_cmd = io_req->sc_cmd; + struct io_bdt *bd_tbl = io_req->bd_tbl; + union fcoe_data_desc_ctx *data_desc; + u32 *fcp_cmnd; + u32 tmp_fcp_cmnd[8]; + int cnt, i; + int bd_count; + struct qedf_ctx *qedf = fcport->qedf; + uint16_t cq_idx = smp_processor_id() % qedf->num_queues; + u8 tmp_sgl_mode = 0; + u8 mst_sgl_mode = 0; + + memset(task_ctx, 0, sizeof(struct fcoe_task_context)); + io_req->task = task_ctx; + + if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) + task_type = FCOE_TASK_TYPE_WRITE_INITIATOR; + else + task_type = FCOE_TASK_TYPE_READ_INITIATOR; + + /* Y Storm context */ + task_ctx->ystorm_st_context.expect_first_xfer = 1; + task_ctx->ystorm_st_context.data_2_trns_rem = io_req->data_xfer_len; + /* Check if this is required */ + task_ctx->ystorm_st_context.ox_id = io_req->xid; + task_ctx->ystorm_st_context.task_rety_identifier = + io_req->task_retry_identifier; + + /* T Storm ag context */ + SET_FIELD(task_ctx->tstorm_ag_context.flags0, + TSTORM_FCOE_TASK_AG_CTX_CONNECTION_TYPE, PROTOCOLID_FCOE); + task_ctx->tstorm_ag_context.icid = (u16)fcport->fw_cid; + + /* T Storm st context */ + SET_FIELD(task_ctx->tstorm_st_context.read_write.flags, + FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, + 1); + task_ctx->tstorm_st_context.read_write.rx_id = 0xffff; + + task_ctx->tstorm_st_context.read_only.dev_type = + FCOE_TASK_DEV_TYPE_DISK; + task_ctx->tstorm_st_context.read_only.conf_supported = 0; + task_ctx->tstorm_st_context.read_only.cid = fcport->fw_cid; + + /* Completion queue for response. */ + task_ctx->tstorm_st_context.read_only.glbl_q_num = cq_idx; + task_ctx->tstorm_st_context.read_only.fcp_cmd_trns_size = + io_req->data_xfer_len; + task_ctx->tstorm_st_context.read_write.e_d_tov_exp_timeout_val = + lport->e_d_tov; + + task_ctx->ustorm_ag_context.global_cq_num = cq_idx; + io_req->fp_idx = cq_idx; + + bd_count = bd_tbl->bd_valid; + if (task_type == FCOE_TASK_TYPE_WRITE_INITIATOR) { + /* Setup WRITE task */ + struct fcoe_sge *fcoe_bd_tbl = bd_tbl->bd_tbl; + + task_ctx->ystorm_st_context.task_type = + FCOE_TASK_TYPE_WRITE_INITIATOR; + data_desc = &task_ctx->ystorm_st_context.data_desc; + + if (io_req->use_slowpath) { + SET_FIELD(task_ctx->ystorm_st_context.sgl_mode, + YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, + FCOE_SLOW_SGL); + data_desc->slow.base_sgl_addr.lo = + U64_LO(bd_tbl->bd_tbl_dma); + data_desc->slow.base_sgl_addr.hi = + U64_HI(bd_tbl->bd_tbl_dma); + data_desc->slow.remainder_num_sges = bd_count; + data_desc->slow.curr_sge_off = 0; + data_desc->slow.curr_sgl_index = 0; + qedf->slow_sge_ios++; + io_req->sge_type = QEDF_IOREQ_SLOW_SGE; + } else { + SET_FIELD(task_ctx->ystorm_st_context.sgl_mode, + YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, + (bd_count <= 4) ? (enum fcoe_sgl_mode)bd_count : + FCOE_MUL_FAST_SGES); + + if (bd_count == 1) { + data_desc->single_sge.sge_addr.lo = + fcoe_bd_tbl->sge_addr.lo; + data_desc->single_sge.sge_addr.hi = + fcoe_bd_tbl->sge_addr.hi; + data_desc->single_sge.size = + fcoe_bd_tbl->size; + data_desc->single_sge.is_valid_sge = 0; + qedf->single_sge_ios++; + io_req->sge_type = QEDF_IOREQ_SINGLE_SGE; + } else { + data_desc->fast.sgl_start_addr.lo = + U64_LO(bd_tbl->bd_tbl_dma); + data_desc->fast.sgl_start_addr.hi = + U64_HI(bd_tbl->bd_tbl_dma); + data_desc->fast.sgl_byte_offset = + data_desc->fast.sgl_start_addr.lo & + (QEDF_PAGE_SIZE - 1); + if (data_desc->fast.sgl_byte_offset > 0) + QEDF_ERR(&(qedf->dbg_ctx), + "byte_offset=%u for xid=0x%x.\n", + io_req->xid, + data_desc->fast.sgl_byte_offset); + data_desc->fast.task_reuse_cnt = + io_req->reuse_count; + io_req->reuse_count++; + if (io_req->reuse_count == QEDF_MAX_REUSE) { + *ptu_invalidate = 1; + io_req->reuse_count = 0; + } + qedf->fast_sge_ios++; + io_req->sge_type = QEDF_IOREQ_FAST_SGE; + } + } + + /* T Storm context */ + task_ctx->tstorm_st_context.read_only.task_type = + FCOE_TASK_TYPE_WRITE_INITIATOR; + + /* M Storm context */ + tmp_sgl_mode = GET_FIELD(task_ctx->ystorm_st_context.sgl_mode, + YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE); + SET_FIELD(task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, + FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_TX_SGL_MODE, + tmp_sgl_mode); + + } else { + /* Setup READ task */ + + /* M Storm context */ + struct fcoe_sge *fcoe_bd_tbl = bd_tbl->bd_tbl; + + data_desc = &task_ctx->mstorm_st_context.fp.data_desc; + task_ctx->mstorm_st_context.fp.data_2_trns_rem = + io_req->data_xfer_len; + + if (io_req->use_slowpath) { + SET_FIELD( + task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, + FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE, + FCOE_SLOW_SGL); + data_desc->slow.base_sgl_addr.lo = + U64_LO(bd_tbl->bd_tbl_dma); + data_desc->slow.base_sgl_addr.hi = + U64_HI(bd_tbl->bd_tbl_dma); + data_desc->slow.remainder_num_sges = + bd_count; + data_desc->slow.curr_sge_off = 0; + data_desc->slow.curr_sgl_index = 0; + qedf->slow_sge_ios++; + io_req->sge_type = QEDF_IOREQ_SLOW_SGE; + } else { + SET_FIELD( + task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, + FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE, + (bd_count <= 4) ? (enum fcoe_sgl_mode)bd_count : + FCOE_MUL_FAST_SGES); + + if (bd_count == 1) { + data_desc->single_sge.sge_addr.lo = + fcoe_bd_tbl->sge_addr.lo; + data_desc->single_sge.sge_addr.hi = + fcoe_bd_tbl->sge_addr.hi; + data_desc->single_sge.size = + fcoe_bd_tbl->size; + data_desc->single_sge.is_valid_sge = 0; + qedf->single_sge_ios++; + io_req->sge_type = QEDF_IOREQ_SINGLE_SGE; + } else { + data_desc->fast.sgl_start_addr.lo = + U64_LO(bd_tbl->bd_tbl_dma); + data_desc->fast.sgl_start_addr.hi = + U64_HI(bd_tbl->bd_tbl_dma); + data_desc->fast.sgl_byte_offset = 0; + data_desc->fast.task_reuse_cnt = + io_req->reuse_count; + io_req->reuse_count++; + if (io_req->reuse_count == QEDF_MAX_REUSE) { + *ptu_invalidate = 1; + io_req->reuse_count = 0; + } + qedf->fast_sge_ios++; + io_req->sge_type = QEDF_IOREQ_FAST_SGE; + } + } + + /* Y Storm context */ + task_ctx->ystorm_st_context.expect_first_xfer = 0; + task_ctx->ystorm_st_context.task_type = + FCOE_TASK_TYPE_READ_INITIATOR; + + /* T Storm context */ + task_ctx->tstorm_st_context.read_only.task_type = + FCOE_TASK_TYPE_READ_INITIATOR; + mst_sgl_mode = GET_FIELD( + task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, + FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE); + SET_FIELD(task_ctx->tstorm_st_context.read_write.flags, + FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RX_SGL_MODE, + mst_sgl_mode); + } + + /* fill FCP_CMND IU */ + fcp_cmnd = (u32 *)task_ctx->ystorm_st_context.tx_info_union.fcp_cmd_payload.opaque; + qedf_build_fcp_cmnd(io_req, (struct fcp_cmnd *)&tmp_fcp_cmnd); + + /* Swap fcp_cmnd since FC is big endian */ + cnt = sizeof(struct fcp_cmnd) / sizeof(u32); + + for (i = 0; i < cnt; i++) { + *fcp_cmnd = cpu_to_be32(tmp_fcp_cmnd[i]); + fcp_cmnd++; + } + + /* M Storm context - Sense buffer */ + task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.lo = + U64_LO(io_req->sense_buffer_dma); + task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.hi = + U64_HI(io_req->sense_buffer_dma); +} + +void qedf_init_mp_task(struct qedf_ioreq *io_req, + struct fcoe_task_context *task_ctx) +{ + struct qedf_mp_req *mp_req = &(io_req->mp_req); + struct qedf_rport *fcport = io_req->fcport; + struct qedf_ctx *qedf = io_req->fcport->qedf; + struct fc_frame_header *fc_hdr; + enum fcoe_task_type task_type = 0; + union fcoe_data_desc_ctx *data_desc; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Initializing MP task " + "for cmd_type = %d\n", io_req->cmd_type); + + qedf->control_requests++; + + /* Obtain task_type */ + if ((io_req->cmd_type == QEDF_TASK_MGMT_CMD) || + (io_req->cmd_type == QEDF_ELS)) { + task_type = FCOE_TASK_TYPE_MIDPATH; + } else if (io_req->cmd_type == QEDF_ABTS) { + task_type = FCOE_TASK_TYPE_ABTS; + } + + memset(task_ctx, 0, sizeof(struct fcoe_task_context)); + + /* Setup the task from io_req for easy reference */ + io_req->task = task_ctx; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "task type = %d\n", + task_type); + + /* YSTORM only */ + { + /* Initialize YSTORM task context */ + struct fcoe_tx_mid_path_params *task_fc_hdr = + &task_ctx->ystorm_st_context.tx_info_union.tx_params.mid_path; + memset(task_fc_hdr, 0, sizeof(struct fcoe_tx_mid_path_params)); + task_ctx->ystorm_st_context.task_rety_identifier = + io_req->task_retry_identifier; + + /* Init SGL parameters */ + if ((task_type == FCOE_TASK_TYPE_MIDPATH) || + (task_type == FCOE_TASK_TYPE_UNSOLICITED)) { + data_desc = &task_ctx->ystorm_st_context.data_desc; + data_desc->slow.base_sgl_addr.lo = + U64_LO(mp_req->mp_req_bd_dma); + data_desc->slow.base_sgl_addr.hi = + U64_HI(mp_req->mp_req_bd_dma); + data_desc->slow.remainder_num_sges = 1; + data_desc->slow.curr_sge_off = 0; + data_desc->slow.curr_sgl_index = 0; + } + + fc_hdr = &(mp_req->req_fc_hdr); + if (task_type == FCOE_TASK_TYPE_MIDPATH) { + fc_hdr->fh_ox_id = io_req->xid; + fc_hdr->fh_rx_id = htons(0xffff); + } else if (task_type == FCOE_TASK_TYPE_UNSOLICITED) { + fc_hdr->fh_rx_id = io_req->xid; + } + + /* Fill FC Header into middle path buffer */ + task_fc_hdr->parameter = fc_hdr->fh_parm_offset; + task_fc_hdr->r_ctl = fc_hdr->fh_r_ctl; + task_fc_hdr->type = fc_hdr->fh_type; + task_fc_hdr->cs_ctl = fc_hdr->fh_cs_ctl; + task_fc_hdr->df_ctl = fc_hdr->fh_df_ctl; + task_fc_hdr->rx_id = fc_hdr->fh_rx_id; + task_fc_hdr->ox_id = fc_hdr->fh_ox_id; + + task_ctx->ystorm_st_context.data_2_trns_rem = + io_req->data_xfer_len; + task_ctx->ystorm_st_context.task_type = task_type; + } + + /* TSTORM ONLY */ + { + task_ctx->tstorm_ag_context.icid = (u16)fcport->fw_cid; + task_ctx->tstorm_st_context.read_only.cid = fcport->fw_cid; + /* Always send middle-path repsonses on CQ #0 */ + task_ctx->tstorm_st_context.read_only.glbl_q_num = 0; + io_req->fp_idx = 0; + SET_FIELD(task_ctx->tstorm_ag_context.flags0, + TSTORM_FCOE_TASK_AG_CTX_CONNECTION_TYPE, + PROTOCOLID_FCOE); + task_ctx->tstorm_st_context.read_only.task_type = task_type; + SET_FIELD(task_ctx->tstorm_st_context.read_write.flags, + FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, + 1); + task_ctx->tstorm_st_context.read_write.rx_id = 0xffff; + } + + /* MSTORM only */ + { + if (task_type == FCOE_TASK_TYPE_MIDPATH) { + /* Initialize task context */ + data_desc = &task_ctx->mstorm_st_context.fp.data_desc; + + /* Set cache sges address and length */ + data_desc->slow.base_sgl_addr.lo = + U64_LO(mp_req->mp_resp_bd_dma); + data_desc->slow.base_sgl_addr.hi = + U64_HI(mp_req->mp_resp_bd_dma); + data_desc->slow.remainder_num_sges = 1; + data_desc->slow.curr_sge_off = 0; + data_desc->slow.curr_sgl_index = 0; + + /* + * Also need to fil in non-fastpath response address + * for middle path commands. + */ + task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.lo = + U64_LO(mp_req->mp_resp_bd_dma); + task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.hi = + U64_HI(mp_req->mp_resp_bd_dma); + } + } + + /* USTORM ONLY */ + { + task_ctx->ustorm_ag_context.global_cq_num = 0; + } + + /* I/O stats. Middle path commands always use slow SGEs */ + qedf->slow_sge_ios++; + io_req->sge_type = QEDF_IOREQ_SLOW_SGE; +} + +void qedf_add_to_sq(struct qedf_rport *fcport, u16 xid, u32 ptu_invalidate, + enum fcoe_task_type req_type, u32 offset) +{ + struct fcoe_wqe *sqe; + uint16_t total_sqe = (fcport->sq_mem_size)/(sizeof(struct fcoe_wqe)); + + sqe = &fcport->sq[fcport->sq_prod_idx]; + + fcport->sq_prod_idx++; + fcport->fw_sq_prod_idx++; + if (fcport->sq_prod_idx == total_sqe) + fcport->sq_prod_idx = 0; + + switch (req_type) { + case FCOE_TASK_TYPE_WRITE_INITIATOR: + case FCOE_TASK_TYPE_READ_INITIATOR: + SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, SEND_FCOE_CMD); + if (ptu_invalidate) + SET_FIELD(sqe->flags, FCOE_WQE_INVALIDATE_PTU, 1); + break; + case FCOE_TASK_TYPE_MIDPATH: + SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, SEND_FCOE_MIDPATH); + break; + case FCOE_TASK_TYPE_ABTS: + SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, + SEND_FCOE_ABTS_REQUEST); + break; + case FCOE_TASK_TYPE_EXCHANGE_CLEANUP: + SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, + FCOE_EXCHANGE_CLEANUP); + break; + case FCOE_TASK_TYPE_SEQUENCE_CLEANUP: + SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, + FCOE_SEQUENCE_RECOVERY); + /* NOTE: offset param only used for sequence recovery */ + sqe->additional_info_union.seq_rec_updated_offset = offset; + break; + case FCOE_TASK_TYPE_UNSOLICITED: + break; + default: + break; + } + + sqe->task_id = xid; + + /* Make sure SQ data is coherent */ + wmb(); + +} + +void qedf_ring_doorbell(struct qedf_rport *fcport) +{ + struct fcoe_db_data dbell = { 0 }; + + dbell.agg_flags = 0; + + dbell.params |= DB_DEST_XCM << FCOE_DB_DATA_DEST_SHIFT; + dbell.params |= DB_AGG_CMD_SET << FCOE_DB_DATA_AGG_CMD_SHIFT; + dbell.params |= DQ_XCM_FCOE_SQ_PROD_CMD << + FCOE_DB_DATA_AGG_VAL_SEL_SHIFT; + + dbell.sq_prod = fcport->fw_sq_prod_idx; + writel(*(u32 *)&dbell, fcport->p_doorbell); + /* Make sure SQ index is updated so f/w prcesses requests in order */ + wmb(); + mmiowb(); +} + +static void qedf_trace_io(struct qedf_rport *fcport, struct qedf_ioreq *io_req, + int8_t direction) +{ + struct qedf_ctx *qedf = fcport->qedf; + struct qedf_io_log *io_log; + struct scsi_cmnd *sc_cmd = io_req->sc_cmd; + unsigned long flags; + uint8_t op; + + spin_lock_irqsave(&qedf->io_trace_lock, flags); + + io_log = &qedf->io_trace_buf[qedf->io_trace_idx]; + io_log->direction = direction; + io_log->task_id = io_req->xid; + io_log->port_id = fcport->rdata->ids.port_id; + io_log->lun = sc_cmd->device->lun; + io_log->op = op = sc_cmd->cmnd[0]; + io_log->lba[0] = sc_cmd->cmnd[2]; + io_log->lba[1] = sc_cmd->cmnd[3]; + io_log->lba[2] = sc_cmd->cmnd[4]; + io_log->lba[3] = sc_cmd->cmnd[5]; + io_log->bufflen = scsi_bufflen(sc_cmd); + io_log->sg_count = scsi_sg_count(sc_cmd); + io_log->result = sc_cmd->result; + io_log->jiffies = jiffies; + io_log->refcount = kref_read(&io_req->refcount); + + if (direction == QEDF_IO_TRACE_REQ) { + /* For requests we only care abot the submission CPU */ + io_log->req_cpu = io_req->cpu; + io_log->int_cpu = 0; + io_log->rsp_cpu = 0; + } else if (direction == QEDF_IO_TRACE_RSP) { + io_log->req_cpu = io_req->cpu; + io_log->int_cpu = io_req->int_cpu; + io_log->rsp_cpu = smp_processor_id(); + } + + io_log->sge_type = io_req->sge_type; + + qedf->io_trace_idx++; + if (qedf->io_trace_idx == QEDF_IO_TRACE_SIZE) + qedf->io_trace_idx = 0; + + spin_unlock_irqrestore(&qedf->io_trace_lock, flags); +} + +int qedf_post_io_req(struct qedf_rport *fcport, struct qedf_ioreq *io_req) +{ + struct scsi_cmnd *sc_cmd = io_req->sc_cmd; + struct Scsi_Host *host = sc_cmd->device->host; + struct fc_lport *lport = shost_priv(host); + struct qedf_ctx *qedf = lport_priv(lport); + struct fcoe_task_context *task_ctx; + u16 xid; + enum fcoe_task_type req_type = 0; + u32 ptu_invalidate = 0; + + /* Initialize rest of io_req fileds */ + io_req->data_xfer_len = scsi_bufflen(sc_cmd); + sc_cmd->SCp.ptr = (char *)io_req; + io_req->use_slowpath = false; /* Assume fast SGL by default */ + + /* Record which cpu this request is associated with */ + io_req->cpu = smp_processor_id(); + + if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) { + req_type = FCOE_TASK_TYPE_READ_INITIATOR; + io_req->io_req_flags = QEDF_READ; + qedf->input_requests++; + } else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { + req_type = FCOE_TASK_TYPE_WRITE_INITIATOR; + io_req->io_req_flags = QEDF_WRITE; + qedf->output_requests++; + } else { + io_req->io_req_flags = 0; + qedf->control_requests++; + } + + xid = io_req->xid; + + /* Build buffer descriptor list for firmware from sg list */ + if (qedf_build_bd_list_from_sg(io_req)) { + QEDF_ERR(&(qedf->dbg_ctx), "BD list creation failed.\n"); + kref_put(&io_req->refcount, qedf_release_cmd); + return -EAGAIN; + } + + /* Get the task context */ + task_ctx = qedf_get_task_mem(&qedf->tasks, xid); + if (!task_ctx) { + QEDF_WARN(&(qedf->dbg_ctx), "task_ctx is NULL, xid=%d.\n", + xid); + kref_put(&io_req->refcount, qedf_release_cmd); + return -EINVAL; + } + + qedf_init_task(fcport, lport, io_req, &ptu_invalidate, task_ctx); + + if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "Session not offloaded yet.\n"); + kref_put(&io_req->refcount, qedf_release_cmd); + } + + /* Obtain free SQ entry */ + qedf_add_to_sq(fcport, xid, ptu_invalidate, req_type, 0); + + /* Ring doorbell */ + qedf_ring_doorbell(fcport); + + if (qedf_io_tracing && io_req->sc_cmd) + qedf_trace_io(fcport, io_req, QEDF_IO_TRACE_REQ); + + return false; +} + +int +qedf_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd) +{ + struct fc_lport *lport = shost_priv(host); + struct qedf_ctx *qedf = lport_priv(lport); + struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); + struct fc_rport_libfc_priv *rp = rport->dd_data; + struct qedf_rport *fcport = rport->dd_data; + struct qedf_ioreq *io_req; + int rc = 0; + int rval; + unsigned long flags = 0; + + + if (test_bit(QEDF_UNLOADING, &qedf->flags) || + test_bit(QEDF_DBG_STOP_IO, &qedf->flags)) { + sc_cmd->result = DID_NO_CONNECT << 16; + sc_cmd->scsi_done(sc_cmd); + return 0; + } + + rval = fc_remote_port_chkready(rport); + if (rval) { + sc_cmd->result = rval; + sc_cmd->scsi_done(sc_cmd); + return 0; + } + + /* Retry command if we are doing a qed drain operation */ + if (test_bit(QEDF_DRAIN_ACTIVE, &qedf->flags)) { + rc = SCSI_MLQUEUE_HOST_BUSY; + goto exit_qcmd; + } + + if (lport->state != LPORT_ST_READY || + atomic_read(&qedf->link_state) != QEDF_LINK_UP) { + rc = SCSI_MLQUEUE_HOST_BUSY; + goto exit_qcmd; + } + + /* rport and tgt are allocated together, so tgt should be non-NULL */ + fcport = (struct qedf_rport *)&rp[1]; + + if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + /* + * Session is not offloaded yet. Let SCSI-ml retry + * the command. + */ + rc = SCSI_MLQUEUE_TARGET_BUSY; + goto exit_qcmd; + } + if (fcport->retry_delay_timestamp) { + if (time_after(jiffies, fcport->retry_delay_timestamp)) { + fcport->retry_delay_timestamp = 0; + } else { + /* If retry_delay timer is active, flow off the ML */ + rc = SCSI_MLQUEUE_TARGET_BUSY; + goto exit_qcmd; + } + } + + io_req = qedf_alloc_cmd(fcport, QEDF_SCSI_CMD); + if (!io_req) { + rc = SCSI_MLQUEUE_HOST_BUSY; + goto exit_qcmd; + } + + io_req->sc_cmd = sc_cmd; + + /* Take fcport->rport_lock for posting to fcport send queue */ + spin_lock_irqsave(&fcport->rport_lock, flags); + if (qedf_post_io_req(fcport, io_req)) { + QEDF_WARN(&(qedf->dbg_ctx), "Unable to post io_req\n"); + /* Return SQE to pool */ + atomic_inc(&fcport->free_sqes); + rc = SCSI_MLQUEUE_HOST_BUSY; + } + spin_unlock_irqrestore(&fcport->rport_lock, flags); + +exit_qcmd: + return rc; +} + +static void qedf_parse_fcp_rsp(struct qedf_ioreq *io_req, + struct fcoe_cqe_rsp_info *fcp_rsp) +{ + struct scsi_cmnd *sc_cmd = io_req->sc_cmd; + struct qedf_ctx *qedf = io_req->fcport->qedf; + u8 rsp_flags = fcp_rsp->rsp_flags.flags; + int fcp_sns_len = 0; + int fcp_rsp_len = 0; + uint8_t *rsp_info, *sense_data; + + io_req->fcp_status = FC_GOOD; + io_req->fcp_resid = 0; + if (rsp_flags & (FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER | + FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER)) + io_req->fcp_resid = fcp_rsp->fcp_resid; + + io_req->scsi_comp_flags = rsp_flags; + CMD_SCSI_STATUS(sc_cmd) = io_req->cdb_status = + fcp_rsp->scsi_status_code; + + if (rsp_flags & + FCOE_FCP_RSP_FLAGS_FCP_RSP_LEN_VALID) + fcp_rsp_len = fcp_rsp->fcp_rsp_len; + + if (rsp_flags & + FCOE_FCP_RSP_FLAGS_FCP_SNS_LEN_VALID) + fcp_sns_len = fcp_rsp->fcp_sns_len; + + io_req->fcp_rsp_len = fcp_rsp_len; + io_req->fcp_sns_len = fcp_sns_len; + rsp_info = sense_data = io_req->sense_buffer; + + /* fetch fcp_rsp_code */ + if ((fcp_rsp_len == 4) || (fcp_rsp_len == 8)) { + /* Only for task management function */ + io_req->fcp_rsp_code = rsp_info[3]; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "fcp_rsp_code = %d\n", io_req->fcp_rsp_code); + /* Adjust sense-data location. */ + sense_data += fcp_rsp_len; + } + + if (fcp_sns_len > SCSI_SENSE_BUFFERSIZE) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Truncating sense buffer\n"); + fcp_sns_len = SCSI_SENSE_BUFFERSIZE; + } + + memset(sc_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + if (fcp_sns_len) + memcpy(sc_cmd->sense_buffer, sense_data, + fcp_sns_len); +} + +static void qedf_unmap_sg_list(struct qedf_ctx *qedf, struct qedf_ioreq *io_req) +{ + struct scsi_cmnd *sc = io_req->sc_cmd; + + if (io_req->bd_tbl->bd_valid && sc && scsi_sg_count(sc)) { + dma_unmap_sg(&qedf->pdev->dev, scsi_sglist(sc), + scsi_sg_count(sc), sc->sc_data_direction); + io_req->bd_tbl->bd_valid = 0; + } +} + +void qedf_scsi_completion(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req) +{ + u16 xid, rval; + struct fcoe_task_context *task_ctx; + struct scsi_cmnd *sc_cmd; + struct fcoe_cqe_rsp_info *fcp_rsp; + struct qedf_rport *fcport; + int refcount; + u16 scope, qualifier = 0; + u8 fw_residual_flag = 0; + + if (!io_req) + return; + if (!cqe) + return; + + xid = io_req->xid; + task_ctx = qedf_get_task_mem(&qedf->tasks, xid); + sc_cmd = io_req->sc_cmd; + fcp_rsp = &cqe->cqe_info.rsp_info; + + if (!sc_cmd) { + QEDF_WARN(&(qedf->dbg_ctx), "sc_cmd is NULL!\n"); + return; + } + + if (!sc_cmd->SCp.ptr) { + QEDF_WARN(&(qedf->dbg_ctx), "SCp.ptr is NULL, returned in " + "another context.\n"); + return; + } + + if (!sc_cmd->request) { + QEDF_WARN(&(qedf->dbg_ctx), "sc_cmd->request is NULL, " + "sc_cmd=%p.\n", sc_cmd); + return; + } + + if (!sc_cmd->request->special) { + QEDF_WARN(&(qedf->dbg_ctx), "request->special is NULL so " + "request not valid, sc_cmd=%p.\n", sc_cmd); + return; + } + + if (!sc_cmd->request->q) { + QEDF_WARN(&(qedf->dbg_ctx), "request->q is NULL so request " + "is not valid, sc_cmd=%p.\n", sc_cmd); + return; + } + + fcport = io_req->fcport; + + qedf_parse_fcp_rsp(io_req, fcp_rsp); + + qedf_unmap_sg_list(qedf, io_req); + + /* Check for FCP transport error */ + if (io_req->fcp_rsp_len > 3 && io_req->fcp_rsp_code) { + QEDF_ERR(&(qedf->dbg_ctx), + "FCP I/O protocol failure xid=0x%x fcp_rsp_len=%d " + "fcp_rsp_code=%d.\n", io_req->xid, io_req->fcp_rsp_len, + io_req->fcp_rsp_code); + sc_cmd->result = DID_BUS_BUSY << 16; + goto out; + } + + fw_residual_flag = GET_FIELD(cqe->cqe_info.rsp_info.fw_error_flags, + FCOE_CQE_RSP_INFO_FW_UNDERRUN); + if (fw_residual_flag) { + QEDF_ERR(&(qedf->dbg_ctx), + "Firmware detected underrun: xid=0x%x fcp_rsp.flags=0x%02x " + "fcp_resid=%d fw_residual=0x%x.\n", io_req->xid, + fcp_rsp->rsp_flags.flags, io_req->fcp_resid, + cqe->cqe_info.rsp_info.fw_residual); + + if (io_req->cdb_status == 0) + sc_cmd->result = (DID_ERROR << 16) | io_req->cdb_status; + else + sc_cmd->result = (DID_OK << 16) | io_req->cdb_status; + + /* Abort the command since we did not get all the data */ + init_completion(&io_req->abts_done); + rval = qedf_initiate_abts(io_req, true); + if (rval) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n"); + sc_cmd->result = (DID_ERROR << 16) | io_req->cdb_status; + } + + /* + * Set resid to the whole buffer length so we won't try to resue + * any previously data. + */ + scsi_set_resid(sc_cmd, scsi_bufflen(sc_cmd)); + goto out; + } + + switch (io_req->fcp_status) { + case FC_GOOD: + if (io_req->cdb_status == 0) { + /* Good I/O completion */ + sc_cmd->result = DID_OK << 16; + } else { + refcount = kref_read(&io_req->refcount); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "%d:0:%d:%d xid=0x%0x op=0x%02x " + "lba=%02x%02x%02x%02x cdb_status=%d " + "fcp_resid=0x%x refcount=%d.\n", + qedf->lport->host->host_no, sc_cmd->device->id, + sc_cmd->device->lun, io_req->xid, + sc_cmd->cmnd[0], sc_cmd->cmnd[2], sc_cmd->cmnd[3], + sc_cmd->cmnd[4], sc_cmd->cmnd[5], + io_req->cdb_status, io_req->fcp_resid, + refcount); + sc_cmd->result = (DID_OK << 16) | io_req->cdb_status; + + if (io_req->cdb_status == SAM_STAT_TASK_SET_FULL || + io_req->cdb_status == SAM_STAT_BUSY) { + /* + * Check whether we need to set retry_delay at + * all based on retry_delay module parameter + * and the status qualifier. + */ + + /* Upper 2 bits */ + scope = fcp_rsp->retry_delay_timer & 0xC000; + /* Lower 14 bits */ + qualifier = fcp_rsp->retry_delay_timer & 0x3FFF; + + if (qedf_retry_delay && + scope > 0 && qualifier > 0 && + qualifier <= 0x3FEF) { + /* Check we don't go over the max */ + if (qualifier > QEDF_RETRY_DELAY_MAX) + qualifier = + QEDF_RETRY_DELAY_MAX; + fcport->retry_delay_timestamp = + jiffies + (qualifier * HZ / 10); + } + } + } + if (io_req->fcp_resid) + scsi_set_resid(sc_cmd, io_req->fcp_resid); + break; + default: + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "fcp_status=%d.\n", + io_req->fcp_status); + break; + } + +out: + if (qedf_io_tracing) + qedf_trace_io(fcport, io_req, QEDF_IO_TRACE_RSP); + + io_req->sc_cmd = NULL; + sc_cmd->SCp.ptr = NULL; + sc_cmd->scsi_done(sc_cmd); + kref_put(&io_req->refcount, qedf_release_cmd); +} + +/* Return a SCSI command in some other context besides a normal completion */ +void qedf_scsi_done(struct qedf_ctx *qedf, struct qedf_ioreq *io_req, + int result) +{ + u16 xid; + struct scsi_cmnd *sc_cmd; + int refcount; + + if (!io_req) + return; + + xid = io_req->xid; + sc_cmd = io_req->sc_cmd; + + if (!sc_cmd) { + QEDF_WARN(&(qedf->dbg_ctx), "sc_cmd is NULL!\n"); + return; + } + + if (!sc_cmd->SCp.ptr) { + QEDF_WARN(&(qedf->dbg_ctx), "SCp.ptr is NULL, returned in " + "another context.\n"); + return; + } + + qedf_unmap_sg_list(qedf, io_req); + + sc_cmd->result = result << 16; + refcount = kref_read(&io_req->refcount); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "%d:0:%d:%d: Completing " + "sc_cmd=%p result=0x%08x op=0x%02x lba=0x%02x%02x%02x%02x, " + "allowed=%d retries=%d refcount=%d.\n", + qedf->lport->host->host_no, sc_cmd->device->id, + sc_cmd->device->lun, sc_cmd, sc_cmd->result, sc_cmd->cmnd[0], + sc_cmd->cmnd[2], sc_cmd->cmnd[3], sc_cmd->cmnd[4], + sc_cmd->cmnd[5], sc_cmd->allowed, sc_cmd->retries, + refcount); + + /* + * Set resid to the whole buffer length so we won't try to resue any + * previously read data + */ + scsi_set_resid(sc_cmd, scsi_bufflen(sc_cmd)); + + if (qedf_io_tracing) + qedf_trace_io(io_req->fcport, io_req, QEDF_IO_TRACE_RSP); + + io_req->sc_cmd = NULL; + sc_cmd->SCp.ptr = NULL; + sc_cmd->scsi_done(sc_cmd); + kref_put(&io_req->refcount, qedf_release_cmd); +} + +/* + * Handle warning type CQE completions. This is mainly used for REC timer + * popping. + */ +void qedf_process_warning_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req) +{ + int rval, i; + struct qedf_rport *fcport = io_req->fcport; + u64 err_warn_bit_map; + u8 err_warn = 0xff; + + if (!cqe) + return; + + QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "Warning CQE, " + "xid=0x%x\n", io_req->xid); + QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), + "err_warn_bitmap=%08x:%08x\n", + le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_hi), + le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_lo)); + QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "tx_buff_off=%08x, " + "rx_buff_off=%08x, rx_id=%04x\n", + le32_to_cpu(cqe->cqe_info.err_info.tx_buf_off), + le32_to_cpu(cqe->cqe_info.err_info.rx_buf_off), + le32_to_cpu(cqe->cqe_info.err_info.rx_id)); + + /* Normalize the error bitmap value to an just an unsigned int */ + err_warn_bit_map = (u64) + ((u64)cqe->cqe_info.err_info.err_warn_bitmap_hi << 32) | + (u64)cqe->cqe_info.err_info.err_warn_bitmap_lo; + for (i = 0; i < 64; i++) { + if (err_warn_bit_map & (u64)((u64)1 << i)) { + err_warn = i; + break; + } + } + + /* Check if REC TOV expired if this is a tape device */ + if (fcport->dev_type == QEDF_RPORT_TYPE_TAPE) { + if (err_warn == + FCOE_WARNING_CODE_REC_TOV_TIMER_EXPIRATION) { + QEDF_ERR(&(qedf->dbg_ctx), "REC timer expired.\n"); + if (!test_bit(QEDF_CMD_SRR_SENT, &io_req->flags)) { + io_req->rx_buf_off = + cqe->cqe_info.err_info.rx_buf_off; + io_req->tx_buf_off = + cqe->cqe_info.err_info.tx_buf_off; + io_req->rx_id = cqe->cqe_info.err_info.rx_id; + rval = qedf_send_rec(io_req); + /* + * We only want to abort the io_req if we + * can't queue the REC command as we want to + * keep the exchange open for recovery. + */ + if (rval) + goto send_abort; + } + return; + } + } + +send_abort: + init_completion(&io_req->abts_done); + rval = qedf_initiate_abts(io_req, true); + if (rval) + QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n"); +} + +/* Cleanup a command when we receive an error detection completion */ +void qedf_process_error_detect(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req) +{ + int rval; + + if (!cqe) + return; + + QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "Error detection CQE, " + "xid=0x%x\n", io_req->xid); + QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), + "err_warn_bitmap=%08x:%08x\n", + le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_hi), + le32_to_cpu(cqe->cqe_info.err_info.err_warn_bitmap_lo)); + QEDF_ERR(&(io_req->fcport->qedf->dbg_ctx), "tx_buff_off=%08x, " + "rx_buff_off=%08x, rx_id=%04x\n", + le32_to_cpu(cqe->cqe_info.err_info.tx_buf_off), + le32_to_cpu(cqe->cqe_info.err_info.rx_buf_off), + le32_to_cpu(cqe->cqe_info.err_info.rx_id)); + + if (qedf->stop_io_on_error) { + qedf_stop_all_io(qedf); + return; + } + + init_completion(&io_req->abts_done); + rval = qedf_initiate_abts(io_req, true); + if (rval) + QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n"); +} + +static void qedf_flush_els_req(struct qedf_ctx *qedf, + struct qedf_ioreq *els_req) +{ + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Flushing ELS request xid=0x%x refcount=%d.\n", els_req->xid, + kref_read(&els_req->refcount)); + + /* + * Need to distinguish this from a timeout when calling the + * els_req->cb_func. + */ + els_req->event = QEDF_IOREQ_EV_ELS_FLUSH; + + /* Cancel the timer */ + cancel_delayed_work_sync(&els_req->timeout_work); + + /* Call callback function to complete command */ + if (els_req->cb_func && els_req->cb_arg) { + els_req->cb_func(els_req->cb_arg); + els_req->cb_arg = NULL; + } + + /* Release kref for original initiate_els */ + kref_put(&els_req->refcount, qedf_release_cmd); +} + +/* A value of -1 for lun is a wild card that means flush all + * active SCSI I/Os for the target. + */ +void qedf_flush_active_ios(struct qedf_rport *fcport, int lun) +{ + struct qedf_ioreq *io_req; + struct qedf_ctx *qedf; + struct qedf_cmd_mgr *cmd_mgr; + int i, rc; + + if (!fcport) + return; + + qedf = fcport->qedf; + cmd_mgr = qedf->cmd_mgr; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "Flush active i/o's.\n"); + + for (i = 0; i < FCOE_PARAMS_NUM_TASKS; i++) { + io_req = &cmd_mgr->cmds[i]; + + if (!io_req) + continue; + if (io_req->fcport != fcport) + continue; + if (io_req->cmd_type == QEDF_ELS) { + rc = kref_get_unless_zero(&io_req->refcount); + if (!rc) { + QEDF_ERR(&(qedf->dbg_ctx), + "Could not get kref for io_req=0x%p.\n", + io_req); + continue; + } + qedf_flush_els_req(qedf, io_req); + /* + * Release the kref and go back to the top of the + * loop. + */ + goto free_cmd; + } + + if (!io_req->sc_cmd) + continue; + if (lun > 0) { + if (io_req->sc_cmd->device->lun != + (u64)lun) + continue; + } + + /* + * Use kref_get_unless_zero in the unlikely case the command + * we're about to flush was completed in the normal SCSI path + */ + rc = kref_get_unless_zero(&io_req->refcount); + if (!rc) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not get kref for " + "io_req=0x%p\n", io_req); + continue; + } + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Cleanup xid=0x%x.\n", io_req->xid); + + /* Cleanup task and return I/O mid-layer */ + qedf_initiate_cleanup(io_req, true); + +free_cmd: + kref_put(&io_req->refcount, qedf_release_cmd); + } +} + +/* + * Initiate a ABTS middle path command. Note that we don't have to initialize + * the task context for an ABTS task. + */ +int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) +{ + struct fc_lport *lport; + struct qedf_rport *fcport = io_req->fcport; + struct fc_rport_priv *rdata = fcport->rdata; + struct qedf_ctx *qedf = fcport->qedf; + u16 xid; + u32 r_a_tov = 0; + int rc = 0; + unsigned long flags; + + r_a_tov = rdata->r_a_tov; + lport = qedf->lport; + + if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "tgt not offloaded\n"); + rc = 1; + goto abts_err; + } + + if (lport->state != LPORT_ST_READY || !(lport->link_up)) { + QEDF_ERR(&(qedf->dbg_ctx), "link is not ready\n"); + rc = 1; + goto abts_err; + } + + if (atomic_read(&qedf->link_down_tmo_valid) > 0) { + QEDF_ERR(&(qedf->dbg_ctx), "link_down_tmo active.\n"); + rc = 1; + goto abts_err; + } + + /* Ensure room on SQ */ + if (!atomic_read(&fcport->free_sqes)) { + QEDF_ERR(&(qedf->dbg_ctx), "No SQ entries available\n"); + rc = 1; + goto abts_err; + } + + + kref_get(&io_req->refcount); + + xid = io_req->xid; + qedf->control_requests++; + qedf->packet_aborts++; + + /* Set the return CPU to be the same as the request one */ + io_req->cpu = smp_processor_id(); + + /* Set the command type to abort */ + io_req->cmd_type = QEDF_ABTS; + io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts; + + set_bit(QEDF_CMD_IN_ABORT, &io_req->flags); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "ABTS io_req xid = " + "0x%x\n", xid); + + qedf_cmd_timer_set(qedf, io_req, QEDF_ABORT_TIMEOUT * HZ); + + spin_lock_irqsave(&fcport->rport_lock, flags); + + /* Add ABTS to send queue */ + qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_ABTS, 0); + + /* Ring doorbell */ + qedf_ring_doorbell(fcport); + + spin_unlock_irqrestore(&fcport->rport_lock, flags); + + return rc; +abts_err: + /* + * If the ABTS task fails to queue then we need to cleanup the + * task at the firmware. + */ + qedf_initiate_cleanup(io_req, return_scsi_cmd_on_abts); + return rc; +} + +void qedf_process_abts_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req) +{ + uint32_t r_ctl; + uint16_t xid; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "Entered with xid = " + "0x%x cmd_type = %d\n", io_req->xid, io_req->cmd_type); + + cancel_delayed_work(&io_req->timeout_work); + + xid = io_req->xid; + r_ctl = cqe->cqe_info.abts_info.r_ctl; + + switch (r_ctl) { + case FC_RCTL_BA_ACC: + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, + "ABTS response - ACC Send RRQ after R_A_TOV\n"); + io_req->event = QEDF_IOREQ_EV_ABORT_SUCCESS; + /* + * Dont release this cmd yet. It will be relesed + * after we get RRQ response + */ + kref_get(&io_req->refcount); + queue_delayed_work(qedf->dpc_wq, &io_req->rrq_work, + msecs_to_jiffies(qedf->lport->r_a_tov)); + break; + /* For error cases let the cleanup return the command */ + case FC_RCTL_BA_RJT: + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, + "ABTS response - RJT\n"); + io_req->event = QEDF_IOREQ_EV_ABORT_FAILED; + break; + default: + QEDF_ERR(&(qedf->dbg_ctx), "Unknown ABTS response\n"); + break; + } + + clear_bit(QEDF_CMD_IN_ABORT, &io_req->flags); + + if (io_req->sc_cmd) { + if (io_req->return_scsi_cmd_on_abts) + qedf_scsi_done(qedf, io_req, DID_ERROR); + } + + /* Notify eh_abort handler that ABTS is complete */ + complete(&io_req->abts_done); + + kref_put(&io_req->refcount, qedf_release_cmd); +} + +int qedf_init_mp_req(struct qedf_ioreq *io_req) +{ + struct qedf_mp_req *mp_req; + struct fcoe_sge *mp_req_bd; + struct fcoe_sge *mp_resp_bd; + struct qedf_ctx *qedf = io_req->fcport->qedf; + dma_addr_t addr; + uint64_t sz; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_MP_REQ, "Entered.\n"); + + mp_req = (struct qedf_mp_req *)&(io_req->mp_req); + memset(mp_req, 0, sizeof(struct qedf_mp_req)); + + if (io_req->cmd_type != QEDF_ELS) { + mp_req->req_len = sizeof(struct fcp_cmnd); + io_req->data_xfer_len = mp_req->req_len; + } else + mp_req->req_len = io_req->data_xfer_len; + + mp_req->req_buf = dma_alloc_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE, + &mp_req->req_buf_dma, GFP_KERNEL); + if (!mp_req->req_buf) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc MP req buffer\n"); + qedf_free_mp_resc(io_req); + return -ENOMEM; + } + + mp_req->resp_buf = dma_alloc_coherent(&qedf->pdev->dev, + QEDF_PAGE_SIZE, &mp_req->resp_buf_dma, GFP_KERNEL); + if (!mp_req->resp_buf) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc TM resp " + "buffer\n"); + qedf_free_mp_resc(io_req); + return -ENOMEM; + } + + /* Allocate and map mp_req_bd and mp_resp_bd */ + sz = sizeof(struct fcoe_sge); + mp_req->mp_req_bd = dma_alloc_coherent(&qedf->pdev->dev, sz, + &mp_req->mp_req_bd_dma, GFP_KERNEL); + if (!mp_req->mp_req_bd) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc MP req bd\n"); + qedf_free_mp_resc(io_req); + return -ENOMEM; + } + + mp_req->mp_resp_bd = dma_alloc_coherent(&qedf->pdev->dev, sz, + &mp_req->mp_resp_bd_dma, GFP_KERNEL); + if (!mp_req->mp_resp_bd) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to alloc MP resp bd\n"); + qedf_free_mp_resc(io_req); + return -ENOMEM; + } + + /* Fill bd table */ + addr = mp_req->req_buf_dma; + mp_req_bd = mp_req->mp_req_bd; + mp_req_bd->sge_addr.lo = U64_LO(addr); + mp_req_bd->sge_addr.hi = U64_HI(addr); + mp_req_bd->size = QEDF_PAGE_SIZE; + + /* + * MP buffer is either a task mgmt command or an ELS. + * So the assumption is that it consumes a single bd + * entry in the bd table + */ + mp_resp_bd = mp_req->mp_resp_bd; + addr = mp_req->resp_buf_dma; + mp_resp_bd->sge_addr.lo = U64_LO(addr); + mp_resp_bd->sge_addr.hi = U64_HI(addr); + mp_resp_bd->size = QEDF_PAGE_SIZE; + + return 0; +} + +/* + * Last ditch effort to clear the port if it's stuck. Used only after a + * cleanup task times out. + */ +static void qedf_drain_request(struct qedf_ctx *qedf) +{ + if (test_bit(QEDF_DRAIN_ACTIVE, &qedf->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "MCP drain already active.\n"); + return; + } + + /* Set bit to return all queuecommand requests as busy */ + set_bit(QEDF_DRAIN_ACTIVE, &qedf->flags); + + /* Call qed drain request for function. Should be synchronous */ + qed_ops->common->drain(qedf->cdev); + + /* Settle time for CQEs to be returned */ + msleep(100); + + /* Unplug and continue */ + clear_bit(QEDF_DRAIN_ACTIVE, &qedf->flags); +} + +/* + * Returns SUCCESS if the cleanup task does not timeout, otherwise return + * FAILURE. + */ +int qedf_initiate_cleanup(struct qedf_ioreq *io_req, + bool return_scsi_cmd_on_abts) +{ + struct qedf_rport *fcport; + struct qedf_ctx *qedf; + uint16_t xid; + struct fcoe_task_context *task; + int tmo = 0; + int rc = SUCCESS; + unsigned long flags; + + fcport = io_req->fcport; + if (!fcport) { + QEDF_ERR(NULL, "fcport is NULL.\n"); + return SUCCESS; + } + + qedf = fcport->qedf; + if (!qedf) { + QEDF_ERR(NULL, "qedf is NULL.\n"); + return SUCCESS; + } + + if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) || + test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "io_req xid=0x%x already in " + "cleanup processing or already completed.\n", + io_req->xid); + return SUCCESS; + } + + /* Ensure room on SQ */ + if (!atomic_read(&fcport->free_sqes)) { + QEDF_ERR(&(qedf->dbg_ctx), "No SQ entries available\n"); + return FAILED; + } + + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "Entered xid=0x%x\n", + io_req->xid); + + /* Cleanup cmds re-use the same TID as the original I/O */ + xid = io_req->xid; + io_req->cmd_type = QEDF_CLEANUP; + io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts; + + /* Set the return CPU to be the same as the request one */ + io_req->cpu = smp_processor_id(); + + set_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags); + + task = qedf_get_task_mem(&qedf->tasks, xid); + + init_completion(&io_req->tm_done); + + /* Obtain free SQ entry */ + spin_lock_irqsave(&fcport->rport_lock, flags); + qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_EXCHANGE_CLEANUP, 0); + + /* Ring doorbell */ + qedf_ring_doorbell(fcport); + spin_unlock_irqrestore(&fcport->rport_lock, flags); + + tmo = wait_for_completion_timeout(&io_req->tm_done, + QEDF_CLEANUP_TIMEOUT * HZ); + + if (!tmo) { + rc = FAILED; + /* Timeout case */ + QEDF_ERR(&(qedf->dbg_ctx), "Cleanup command timeout, " + "xid=%x.\n", io_req->xid); + clear_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags); + /* Issue a drain request if cleanup task times out */ + QEDF_ERR(&(qedf->dbg_ctx), "Issuing MCP drain request.\n"); + qedf_drain_request(qedf); + } + + if (io_req->sc_cmd) { + if (io_req->return_scsi_cmd_on_abts) + qedf_scsi_done(qedf, io_req, DID_ERROR); + } + + if (rc == SUCCESS) + io_req->event = QEDF_IOREQ_EV_CLEANUP_SUCCESS; + else + io_req->event = QEDF_IOREQ_EV_CLEANUP_FAILED; + + return rc; +} + +void qedf_process_cleanup_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req) +{ + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, "Entered xid = 0x%x\n", + io_req->xid); + + clear_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags); + + /* Complete so we can finish cleaning up the I/O */ + complete(&io_req->tm_done); +} + +static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd, + uint8_t tm_flags) +{ + struct qedf_ioreq *io_req; + struct qedf_mp_req *tm_req; + struct fcoe_task_context *task; + struct fc_frame_header *fc_hdr; + struct fcp_cmnd *fcp_cmnd; + struct qedf_ctx *qedf = fcport->qedf; + int rc = 0; + uint16_t xid; + uint32_t sid, did; + int tmo = 0; + unsigned long flags; + + if (!sc_cmd) { + QEDF_ERR(&(qedf->dbg_ctx), "invalid arg\n"); + return FAILED; + } + + if (!(test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags))) { + QEDF_ERR(&(qedf->dbg_ctx), "fcport not offloaded\n"); + rc = FAILED; + return FAILED; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "portid = 0x%x " + "tm_flags = %d\n", fcport->rdata->ids.port_id, tm_flags); + + io_req = qedf_alloc_cmd(fcport, QEDF_TASK_MGMT_CMD); + if (!io_req) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed TMF"); + rc = -EAGAIN; + goto reset_tmf_err; + } + + /* Initialize rest of io_req fields */ + io_req->sc_cmd = sc_cmd; + io_req->fcport = fcport; + io_req->cmd_type = QEDF_TASK_MGMT_CMD; + + /* Set the return CPU to be the same as the request one */ + io_req->cpu = smp_processor_id(); + + tm_req = (struct qedf_mp_req *)&(io_req->mp_req); + + rc = qedf_init_mp_req(io_req); + if (rc == FAILED) { + QEDF_ERR(&(qedf->dbg_ctx), "Task mgmt MP request init " + "failed\n"); + kref_put(&io_req->refcount, qedf_release_cmd); + goto reset_tmf_err; + } + + /* Set TM flags */ + io_req->io_req_flags = 0; + tm_req->tm_flags = tm_flags; + + /* Default is to return a SCSI command when an error occurs */ + io_req->return_scsi_cmd_on_abts = true; + + /* Fill FCP_CMND */ + qedf_build_fcp_cmnd(io_req, (struct fcp_cmnd *)tm_req->req_buf); + fcp_cmnd = (struct fcp_cmnd *)tm_req->req_buf; + memset(fcp_cmnd->fc_cdb, 0, FCP_CMND_LEN); + fcp_cmnd->fc_dl = 0; + + /* Fill FC header */ + fc_hdr = &(tm_req->req_fc_hdr); + sid = fcport->sid; + did = fcport->rdata->ids.port_id; + __fc_fill_fc_hdr(fc_hdr, FC_RCTL_DD_UNSOL_CMD, sid, did, + FC_TYPE_FCP, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | + FC_FC_SEQ_INIT, 0); + /* Obtain exchange id */ + xid = io_req->xid; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, "TMF io_req xid = " + "0x%x\n", xid); + + /* Initialize task context for this IO request */ + task = qedf_get_task_mem(&qedf->tasks, xid); + qedf_init_mp_task(io_req, task); + + init_completion(&io_req->tm_done); + + /* Obtain free SQ entry */ + spin_lock_irqsave(&fcport->rport_lock, flags); + qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_MIDPATH, 0); + + /* Ring doorbell */ + qedf_ring_doorbell(fcport); + spin_unlock_irqrestore(&fcport->rport_lock, flags); + + tmo = wait_for_completion_timeout(&io_req->tm_done, + QEDF_TM_TIMEOUT * HZ); + + if (!tmo) { + rc = FAILED; + QEDF_ERR(&(qedf->dbg_ctx), "wait for tm_cmpl timeout!\n"); + } else { + /* Check TMF response code */ + if (io_req->fcp_rsp_code == 0) + rc = SUCCESS; + else + rc = FAILED; + } + + if (tm_flags == FCP_TMF_LUN_RESET) + qedf_flush_active_ios(fcport, (int)sc_cmd->device->lun); + else + qedf_flush_active_ios(fcport, -1); + + kref_put(&io_req->refcount, qedf_release_cmd); + + if (rc != SUCCESS) { + QEDF_ERR(&(qedf->dbg_ctx), "task mgmt command failed...\n"); + rc = FAILED; + } else { + QEDF_ERR(&(qedf->dbg_ctx), "task mgmt command success...\n"); + rc = SUCCESS; + } +reset_tmf_err: + return rc; +} + +int qedf_initiate_tmf(struct scsi_cmnd *sc_cmd, u8 tm_flags) +{ + struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); + struct fc_rport_libfc_priv *rp = rport->dd_data; + struct qedf_rport *fcport = (struct qedf_rport *)&rp[1]; + struct qedf_ctx *qedf; + struct fc_lport *lport; + int rc = SUCCESS; + int rval; + + rval = fc_remote_port_chkready(rport); + + if (rval) { + QEDF_ERR(NULL, "device_reset rport not ready\n"); + rc = FAILED; + goto tmf_err; + } + + if (fcport == NULL) { + QEDF_ERR(NULL, "device_reset: rport is NULL\n"); + rc = FAILED; + goto tmf_err; + } + + qedf = fcport->qedf; + lport = qedf->lport; + + if (test_bit(QEDF_UNLOADING, &qedf->flags) || + test_bit(QEDF_DBG_STOP_IO, &qedf->flags)) { + rc = SUCCESS; + goto tmf_err; + } + + if (lport->state != LPORT_ST_READY || !(lport->link_up)) { + QEDF_ERR(&(qedf->dbg_ctx), "link is not ready\n"); + rc = FAILED; + goto tmf_err; + } + + rc = qedf_execute_tmf(fcport, sc_cmd, tm_flags); + +tmf_err: + return rc; +} + +void qedf_process_tmf_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, + struct qedf_ioreq *io_req) +{ + struct fcoe_cqe_rsp_info *fcp_rsp; + struct fcoe_cqe_midpath_info *mp_info; + + + /* Get TMF response length from CQE */ + mp_info = &cqe->cqe_info.midpath_info; + io_req->mp_req.resp_len = mp_info->data_placement_size; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, + "Response len is %d.\n", io_req->mp_req.resp_len); + + fcp_rsp = &cqe->cqe_info.rsp_info; + qedf_parse_fcp_rsp(io_req, fcp_rsp); + + io_req->sc_cmd = NULL; + complete(&io_req->tm_done); +} + +void qedf_process_unsol_compl(struct qedf_ctx *qedf, uint16_t que_idx, + struct fcoe_cqe *cqe) +{ + unsigned long flags; + uint16_t tmp; + uint16_t pktlen = cqe->cqe_info.unsolic_info.pkt_len; + u32 payload_len, crc; + struct fc_frame_header *fh; + struct fc_frame *fp; + struct qedf_io_work *io_work; + u32 bdq_idx; + void *bdq_addr; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL, + "address.hi=%x address.lo=%x opaque_data.hi=%x " + "opaque_data.lo=%x bdq_prod_idx=%u len=%u.\n", + le32_to_cpu(cqe->cqe_info.unsolic_info.bd_info.address.hi), + le32_to_cpu(cqe->cqe_info.unsolic_info.bd_info.address.lo), + le32_to_cpu(cqe->cqe_info.unsolic_info.bd_info.opaque.hi), + le32_to_cpu(cqe->cqe_info.unsolic_info.bd_info.opaque.lo), + qedf->bdq_prod_idx, pktlen); + + bdq_idx = le32_to_cpu(cqe->cqe_info.unsolic_info.bd_info.opaque.lo); + if (bdq_idx >= QEDF_BDQ_SIZE) { + QEDF_ERR(&(qedf->dbg_ctx), "bdq_idx is out of range %d.\n", + bdq_idx); + goto increment_prod; + } + + bdq_addr = qedf->bdq[bdq_idx].buf_addr; + if (!bdq_addr) { + QEDF_ERR(&(qedf->dbg_ctx), "bdq_addr is NULL, dropping " + "unsolicited packet.\n"); + goto increment_prod; + } + + if (qedf_dump_frames) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL, + "BDQ frame is at addr=%p.\n", bdq_addr); + print_hex_dump(KERN_WARNING, "bdq ", DUMP_PREFIX_OFFSET, 16, 1, + (void *)bdq_addr, pktlen, false); + } + + /* Allocate frame */ + payload_len = pktlen - sizeof(struct fc_frame_header); + fp = fc_frame_alloc(qedf->lport, payload_len); + if (!fp) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate fp.\n"); + goto increment_prod; + } + + /* Copy data from BDQ buffer into fc_frame struct */ + fh = (struct fc_frame_header *)fc_frame_header_get(fp); + memcpy(fh, (void *)bdq_addr, pktlen); + + /* Initialize the frame so libfc sees it as a valid frame */ + crc = fcoe_fc_crc(fp); + fc_frame_init(fp); + fr_dev(fp) = qedf->lport; + fr_sof(fp) = FC_SOF_I3; + fr_eof(fp) = FC_EOF_T; + fr_crc(fp) = cpu_to_le32(~crc); + + /* + * We need to return the frame back up to libfc in a non-atomic + * context + */ + io_work = mempool_alloc(qedf->io_mempool, GFP_ATOMIC); + if (!io_work) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate " + "work for I/O completion.\n"); + fc_frame_free(fp); + goto increment_prod; + } + memset(io_work, 0, sizeof(struct qedf_io_work)); + + INIT_WORK(&io_work->work, qedf_fp_io_handler); + + /* Copy contents of CQE for deferred processing */ + memcpy(&io_work->cqe, cqe, sizeof(struct fcoe_cqe)); + + io_work->qedf = qedf; + io_work->fp = fp; + + queue_work_on(smp_processor_id(), qedf_io_wq, &io_work->work); +increment_prod: + spin_lock_irqsave(&qedf->hba_lock, flags); + + /* Increment producer to let f/w know we've handled the frame */ + qedf->bdq_prod_idx++; + + /* Producer index wraps at uint16_t boundary */ + if (qedf->bdq_prod_idx == 0xffff) + qedf->bdq_prod_idx = 0; + + writew(qedf->bdq_prod_idx, qedf->bdq_primary_prod); + tmp = readw(qedf->bdq_primary_prod); + writew(qedf->bdq_prod_idx, qedf->bdq_secondary_prod); + tmp = readw(qedf->bdq_secondary_prod); + + spin_unlock_irqrestore(&qedf->hba_lock, flags); +} diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c new file mode 100644 index 000000000000..d9d7a86b5f8b --- /dev/null +++ b/drivers/scsi/qedf/qedf_main.c @@ -0,0 +1,3336 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/highmem.h> +#include <linux/crc32.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/kthread.h> +#include <scsi/libfc.h> +#include <scsi/scsi_host.h> +#include <linux/if_ether.h> +#include <linux/if_vlan.h> +#include <linux/cpu.h> +#include "qedf.h" + +const struct qed_fcoe_ops *qed_ops; + +static int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id); +static void qedf_remove(struct pci_dev *pdev); + +extern struct qedf_debugfs_ops qedf_debugfs_ops; +extern struct file_operations qedf_dbg_fops; + +/* + * Driver module parameters. + */ +static unsigned int qedf_dev_loss_tmo = 60; +module_param_named(dev_loss_tmo, qedf_dev_loss_tmo, int, S_IRUGO); +MODULE_PARM_DESC(dev_loss_tmo, " dev_loss_tmo setting for attached " + "remote ports (default 60)"); + +uint qedf_debug = QEDF_LOG_INFO; +module_param_named(debug, qedf_debug, uint, S_IRUGO); +MODULE_PARM_DESC(qedf_debug, " Debug mask. Pass '1' to enable default debugging" + " mask"); + +static uint qedf_fipvlan_retries = 30; +module_param_named(fipvlan_retries, qedf_fipvlan_retries, int, S_IRUGO); +MODULE_PARM_DESC(fipvlan_retries, " Number of FIP VLAN requests to attempt " + "before giving up (default 30)"); + +static uint qedf_fallback_vlan = QEDF_FALLBACK_VLAN; +module_param_named(fallback_vlan, qedf_fallback_vlan, int, S_IRUGO); +MODULE_PARM_DESC(fallback_vlan, " VLAN ID to try if fip vlan request fails " + "(default 1002)."); + +static uint qedf_default_prio = QEDF_DEFAULT_PRIO; +module_param_named(default_prio, qedf_default_prio, int, S_IRUGO); +MODULE_PARM_DESC(default_prio, " Default 802.1q priority for FIP and FCoE" + " traffic (default 3)."); + +uint qedf_dump_frames; +module_param_named(dump_frames, qedf_dump_frames, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_frames, " Print the skb data of FIP and FCoE frames " + "(default off)"); + +static uint qedf_queue_depth; +module_param_named(queue_depth, qedf_queue_depth, int, S_IRUGO); +MODULE_PARM_DESC(queue_depth, " Sets the queue depth for all LUNs discovered " + "by the qedf driver. Default is 0 (use OS default)."); + +uint qedf_io_tracing; +module_param_named(io_tracing, qedf_io_tracing, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(io_tracing, " Enable logging of SCSI requests/completions " + "into trace buffer. (default off)."); + +static uint qedf_max_lun = MAX_FIBRE_LUNS; +module_param_named(max_lun, qedf_max_lun, int, S_IRUGO); +MODULE_PARM_DESC(max_lun, " Sets the maximum luns per target that the driver " + "supports. (default 0xffffffff)"); + +uint qedf_link_down_tmo; +module_param_named(link_down_tmo, qedf_link_down_tmo, int, S_IRUGO); +MODULE_PARM_DESC(link_down_tmo, " Delays informing the fcoe transport that the " + "link is down by N seconds."); + +bool qedf_retry_delay; +module_param_named(retry_delay, qedf_retry_delay, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(retry_delay, " Enable/disable handling of FCP_RSP IU retry " + "delay handling (default off)."); + +static uint qedf_dp_module; +module_param_named(dp_module, qedf_dp_module, uint, S_IRUGO); +MODULE_PARM_DESC(dp_module, " bit flags control for verbose printk passed " + "qed module during probe."); + +static uint qedf_dp_level; +module_param_named(dp_level, qedf_dp_level, uint, S_IRUGO); +MODULE_PARM_DESC(dp_level, " printk verbosity control passed to qed module " + "during probe (0-3: 0 more verbose)."); + +struct workqueue_struct *qedf_io_wq; + +static struct fcoe_percpu_s qedf_global; +static DEFINE_SPINLOCK(qedf_global_lock); + +static struct kmem_cache *qedf_io_work_cache; + +void qedf_set_vlan_id(struct qedf_ctx *qedf, int vlan_id) +{ + qedf->vlan_id = vlan_id; + qedf->vlan_id |= qedf_default_prio << VLAN_PRIO_SHIFT; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Setting vlan_id=%04x " + "prio=%d.\n", vlan_id, qedf_default_prio); +} + +/* Returns true if we have a valid vlan, false otherwise */ +static bool qedf_initiate_fipvlan_req(struct qedf_ctx *qedf) +{ + int rc; + + if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) { + QEDF_ERR(&(qedf->dbg_ctx), "Link not up.\n"); + return false; + } + + while (qedf->fipvlan_retries--) { + if (qedf->vlan_id > 0) + return true; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Retry %d.\n", qedf->fipvlan_retries); + init_completion(&qedf->fipvlan_compl); + qedf_fcoe_send_vlan_req(qedf); + rc = wait_for_completion_timeout(&qedf->fipvlan_compl, + 1 * HZ); + if (rc > 0) { + fcoe_ctlr_link_up(&qedf->ctlr); + return true; + } + } + + return false; +} + +static void qedf_handle_link_update(struct work_struct *work) +{ + struct qedf_ctx *qedf = + container_of(work, struct qedf_ctx, link_update.work); + int rc; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Entered.\n"); + + if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) { + rc = qedf_initiate_fipvlan_req(qedf); + if (rc) + return; + /* + * If we get here then we never received a repsonse to our + * fip vlan request so set the vlan_id to the default and + * tell FCoE that the link is up + */ + QEDF_WARN(&(qedf->dbg_ctx), "Did not receive FIP VLAN " + "response, falling back to default VLAN %d.\n", + qedf_fallback_vlan); + qedf_set_vlan_id(qedf, QEDF_FALLBACK_VLAN); + + /* + * Zero out data_src_addr so we'll update it with the new + * lport port_id + */ + eth_zero_addr(qedf->data_src_addr); + fcoe_ctlr_link_up(&qedf->ctlr); + } else if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) { + /* + * If we hit here and link_down_tmo_valid is still 1 it means + * that link_down_tmo timed out so set it to 0 to make sure any + * other readers have accurate state. + */ + atomic_set(&qedf->link_down_tmo_valid, 0); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Calling fcoe_ctlr_link_down().\n"); + fcoe_ctlr_link_down(&qedf->ctlr); + qedf_wait_for_upload(qedf); + /* Reset the number of FIP VLAN retries */ + qedf->fipvlan_retries = qedf_fipvlan_retries; + } +} + +static void qedf_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, + void *arg) +{ + struct fc_exch *exch = fc_seq_exch(seq); + struct fc_lport *lport = exch->lp; + struct qedf_ctx *qedf = lport_priv(lport); + + if (!qedf) { + QEDF_ERR(NULL, "qedf is NULL.\n"); + return; + } + + /* + * If ERR_PTR is set then don't try to stat anything as it will cause + * a crash when we access fp. + */ + if (IS_ERR(fp)) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "fp has IS_ERR() set.\n"); + goto skip_stat; + } + + /* Log stats for FLOGI reject */ + if (fc_frame_payload_op(fp) == ELS_LS_RJT) + qedf->flogi_failed++; + + /* Complete flogi_compl so we can proceed to sending ADISCs */ + complete(&qedf->flogi_compl); + +skip_stat: + /* Report response to libfc */ + fc_lport_flogi_resp(seq, fp, lport); +} + +static struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did, + struct fc_frame *fp, unsigned int op, + void (*resp)(struct fc_seq *, + struct fc_frame *, + void *), + void *arg, u32 timeout) +{ + struct qedf_ctx *qedf = lport_priv(lport); + + /* + * Intercept FLOGI for statistic purposes. Note we use the resp + * callback to tell if this is really a flogi. + */ + if (resp == fc_lport_flogi_resp) { + qedf->flogi_cnt++; + return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp, + arg, timeout); + } + + return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); +} + +int qedf_send_flogi(struct qedf_ctx *qedf) +{ + struct fc_lport *lport; + struct fc_frame *fp; + + lport = qedf->lport; + + if (!lport->tt.elsct_send) + return -EINVAL; + + fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); + if (!fp) { + QEDF_ERR(&(qedf->dbg_ctx), "fc_frame_alloc failed.\n"); + return -ENOMEM; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, + "Sending FLOGI to reestablish session with switch.\n"); + lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, + ELS_FLOGI, qedf_flogi_resp, lport, lport->r_a_tov); + + init_completion(&qedf->flogi_compl); + + return 0; +} + +struct qedf_tmp_rdata_item { + struct fc_rport_priv *rdata; + struct list_head list; +}; + +/* + * This function is called if link_down_tmo is in use. If we get a link up and + * link_down_tmo has not expired then use just FLOGI/ADISC to recover our + * sessions with targets. Otherwise, just call fcoe_ctlr_link_up(). + */ +static void qedf_link_recovery(struct work_struct *work) +{ + struct qedf_ctx *qedf = + container_of(work, struct qedf_ctx, link_recovery.work); + struct qedf_rport *fcport; + struct fc_rport_priv *rdata; + struct qedf_tmp_rdata_item *rdata_item, *tmp_rdata_item; + bool rc; + int retries = 30; + int rval, i; + struct list_head rdata_login_list; + + INIT_LIST_HEAD(&rdata_login_list); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Link down tmo did not expire.\n"); + + /* + * Essentially reset the fcoe_ctlr here without affecting the state + * of the libfc structs. + */ + qedf->ctlr.state = FIP_ST_LINK_WAIT; + fcoe_ctlr_link_down(&qedf->ctlr); + + /* + * Bring the link up before we send the fipvlan request so libfcoe + * can select a new fcf in parallel + */ + fcoe_ctlr_link_up(&qedf->ctlr); + + /* Since the link when down and up to verify which vlan we're on */ + qedf->fipvlan_retries = qedf_fipvlan_retries; + rc = qedf_initiate_fipvlan_req(qedf); + if (!rc) + return; + + /* + * We need to wait for an FCF to be selected due to the + * fcoe_ctlr_link_up other the FLOGI will be rejected. + */ + while (retries > 0) { + if (qedf->ctlr.sel_fcf) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "FCF reselected, proceeding with FLOGI.\n"); + break; + } + msleep(500); + retries--; + } + + if (retries < 1) { + QEDF_ERR(&(qedf->dbg_ctx), "Exhausted retries waiting for " + "FCF selection.\n"); + return; + } + + rval = qedf_send_flogi(qedf); + if (rval) + return; + + /* Wait for FLOGI completion before proceeding with sending ADISCs */ + i = wait_for_completion_timeout(&qedf->flogi_compl, + qedf->lport->r_a_tov); + if (i == 0) { + QEDF_ERR(&(qedf->dbg_ctx), "FLOGI timed out.\n"); + return; + } + + /* + * Call lport->tt.rport_login which will cause libfc to send an + * ADISC since the rport is in state ready. + */ + rcu_read_lock(); + list_for_each_entry_rcu(fcport, &qedf->fcports, peers) { + rdata = fcport->rdata; + if (rdata == NULL) + continue; + rdata_item = kzalloc(sizeof(struct qedf_tmp_rdata_item), + GFP_ATOMIC); + if (!rdata_item) + continue; + if (kref_get_unless_zero(&rdata->kref)) { + rdata_item->rdata = rdata; + list_add(&rdata_item->list, &rdata_login_list); + } else + kfree(rdata_item); + } + rcu_read_unlock(); + /* + * Do the fc_rport_login outside of the rcu lock so we don't take a + * mutex in an atomic context. + */ + list_for_each_entry_safe(rdata_item, tmp_rdata_item, &rdata_login_list, + list) { + list_del(&rdata_item->list); + fc_rport_login(rdata_item->rdata); + kref_put(&rdata_item->rdata->kref, fc_rport_destroy); + kfree(rdata_item); + } +} + +static void qedf_update_link_speed(struct qedf_ctx *qedf, + struct qed_link_output *link) +{ + struct fc_lport *lport = qedf->lport; + + lport->link_speed = FC_PORTSPEED_UNKNOWN; + lport->link_supported_speeds = FC_PORTSPEED_UNKNOWN; + + /* Set fc_host link speed */ + switch (link->speed) { + case 10000: + lport->link_speed = FC_PORTSPEED_10GBIT; + break; + case 25000: + lport->link_speed = FC_PORTSPEED_25GBIT; + break; + case 40000: + lport->link_speed = FC_PORTSPEED_40GBIT; + break; + case 50000: + lport->link_speed = FC_PORTSPEED_50GBIT; + break; + case 100000: + lport->link_speed = FC_PORTSPEED_100GBIT; + break; + default: + lport->link_speed = FC_PORTSPEED_UNKNOWN; + break; + } + + /* + * Set supported link speed by querying the supported + * capabilities of the link. + */ + if (link->supported_caps & SUPPORTED_10000baseKR_Full) + lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; + if (link->supported_caps & SUPPORTED_25000baseKR_Full) + lport->link_supported_speeds |= FC_PORTSPEED_25GBIT; + if (link->supported_caps & SUPPORTED_40000baseLR4_Full) + lport->link_supported_speeds |= FC_PORTSPEED_40GBIT; + if (link->supported_caps & SUPPORTED_50000baseKR2_Full) + lport->link_supported_speeds |= FC_PORTSPEED_50GBIT; + if (link->supported_caps & SUPPORTED_100000baseKR4_Full) + lport->link_supported_speeds |= FC_PORTSPEED_100GBIT; + fc_host_supported_speeds(lport->host) = lport->link_supported_speeds; +} + +static void qedf_link_update(void *dev, struct qed_link_output *link) +{ + struct qedf_ctx *qedf = (struct qedf_ctx *)dev; + + if (link->link_up) { + QEDF_ERR(&(qedf->dbg_ctx), "LINK UP (%d GB/s).\n", + link->speed / 1000); + + /* Cancel any pending link down work */ + cancel_delayed_work(&qedf->link_update); + + atomic_set(&qedf->link_state, QEDF_LINK_UP); + qedf_update_link_speed(qedf, link); + + if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE) { + QEDF_ERR(&(qedf->dbg_ctx), "DCBx done.\n"); + if (atomic_read(&qedf->link_down_tmo_valid) > 0) + queue_delayed_work(qedf->link_update_wq, + &qedf->link_recovery, 0); + else + queue_delayed_work(qedf->link_update_wq, + &qedf->link_update, 0); + atomic_set(&qedf->link_down_tmo_valid, 0); + } + + } else { + QEDF_ERR(&(qedf->dbg_ctx), "LINK DOWN.\n"); + + atomic_set(&qedf->link_state, QEDF_LINK_DOWN); + atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING); + /* + * Flag that we're waiting for the link to come back up before + * informing the fcoe layer of the event. + */ + if (qedf_link_down_tmo > 0) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Starting link down tmo.\n"); + atomic_set(&qedf->link_down_tmo_valid, 1); + } + qedf->vlan_id = 0; + qedf_update_link_speed(qedf, link); + queue_delayed_work(qedf->link_update_wq, &qedf->link_update, + qedf_link_down_tmo * HZ); + } +} + + +static void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type) +{ + struct qedf_ctx *qedf = (struct qedf_ctx *)dev; + + QEDF_ERR(&(qedf->dbg_ctx), "DCBx event valid=%d enabled=%d fcoe " + "prio=%d.\n", get->operational.valid, get->operational.enabled, + get->operational.app_prio.fcoe); + + if (get->operational.enabled && get->operational.valid) { + /* If DCBX was already negotiated on link up then just exit */ + if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "DCBX already set on link up.\n"); + return; + } + + atomic_set(&qedf->dcbx, QEDF_DCBX_DONE); + + if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) { + if (atomic_read(&qedf->link_down_tmo_valid) > 0) + queue_delayed_work(qedf->link_update_wq, + &qedf->link_recovery, 0); + else + queue_delayed_work(qedf->link_update_wq, + &qedf->link_update, 0); + atomic_set(&qedf->link_down_tmo_valid, 0); + } + } + +} + +static u32 qedf_get_login_failures(void *cookie) +{ + struct qedf_ctx *qedf; + + qedf = (struct qedf_ctx *)cookie; + return qedf->flogi_failed; +} + +static struct qed_fcoe_cb_ops qedf_cb_ops = { + { + .link_update = qedf_link_update, + .dcbx_aen = qedf_dcbx_handler, + } +}; + +/* + * Various transport templates. + */ + +static struct scsi_transport_template *qedf_fc_transport_template; +static struct scsi_transport_template *qedf_fc_vport_transport_template; + +/* + * SCSI EH handlers + */ +static int qedf_eh_abort(struct scsi_cmnd *sc_cmd) +{ + struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); + struct fc_rport_libfc_priv *rp = rport->dd_data; + struct qedf_rport *fcport; + struct fc_lport *lport; + struct qedf_ctx *qedf; + struct qedf_ioreq *io_req; + int rc = FAILED; + int rval; + + if (fc_remote_port_chkready(rport)) { + QEDF_ERR(NULL, "rport not ready\n"); + goto out; + } + + lport = shost_priv(sc_cmd->device->host); + qedf = (struct qedf_ctx *)lport_priv(lport); + + if ((lport->state != LPORT_ST_READY) || !(lport->link_up)) { + QEDF_ERR(&(qedf->dbg_ctx), "link not ready.\n"); + goto out; + } + + fcport = (struct qedf_rport *)&rp[1]; + + io_req = (struct qedf_ioreq *)sc_cmd->SCp.ptr; + if (!io_req) { + QEDF_ERR(&(qedf->dbg_ctx), "io_req is NULL.\n"); + rc = SUCCESS; + goto out; + } + + if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) || + test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags) || + test_bit(QEDF_CMD_IN_ABORT, &io_req->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "io_req xid=0x%x already in " + "cleanup or abort processing or already " + "completed.\n", io_req->xid); + rc = SUCCESS; + goto out; + } + + QEDF_ERR(&(qedf->dbg_ctx), "Aborting io_req sc_cmd=%p xid=0x%x " + "fp_idx=%d.\n", sc_cmd, io_req->xid, io_req->fp_idx); + + if (qedf->stop_io_on_error) { + qedf_stop_all_io(qedf); + rc = SUCCESS; + goto out; + } + + init_completion(&io_req->abts_done); + rval = qedf_initiate_abts(io_req, true); + if (rval) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n"); + goto out; + } + + wait_for_completion(&io_req->abts_done); + + if (io_req->event == QEDF_IOREQ_EV_ABORT_SUCCESS || + io_req->event == QEDF_IOREQ_EV_ABORT_FAILED || + io_req->event == QEDF_IOREQ_EV_CLEANUP_SUCCESS) { + /* + * If we get a reponse to the abort this is success from + * the perspective that all references to the command have + * been removed from the driver and firmware + */ + rc = SUCCESS; + } else { + /* If the abort and cleanup failed then return a failure */ + rc = FAILED; + } + + if (rc == SUCCESS) + QEDF_ERR(&(qedf->dbg_ctx), "ABTS succeeded, xid=0x%x.\n", + io_req->xid); + else + QEDF_ERR(&(qedf->dbg_ctx), "ABTS failed, xid=0x%x.\n", + io_req->xid); + +out: + return rc; +} + +static int qedf_eh_target_reset(struct scsi_cmnd *sc_cmd) +{ + QEDF_ERR(NULL, "TARGET RESET Issued..."); + return qedf_initiate_tmf(sc_cmd, FCP_TMF_TGT_RESET); +} + +static int qedf_eh_device_reset(struct scsi_cmnd *sc_cmd) +{ + QEDF_ERR(NULL, "LUN RESET Issued...\n"); + return qedf_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET); +} + +void qedf_wait_for_upload(struct qedf_ctx *qedf) +{ + while (1) { + if (atomic_read(&qedf->num_offloads)) + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Waiting for all uploads to complete.\n"); + else + break; + msleep(500); + } +} + +/* Reset the host by gracefully logging out and then logging back in */ +static int qedf_eh_host_reset(struct scsi_cmnd *sc_cmd) +{ + struct fc_lport *lport; + struct qedf_ctx *qedf; + + lport = shost_priv(sc_cmd->device->host); + + if (lport->vport) { + QEDF_ERR(NULL, "Cannot issue host reset on NPIV port.\n"); + return SUCCESS; + } + + qedf = (struct qedf_ctx *)lport_priv(lport); + + if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN || + test_bit(QEDF_UNLOADING, &qedf->flags) || + test_bit(QEDF_DBG_STOP_IO, &qedf->flags)) + return FAILED; + + QEDF_ERR(&(qedf->dbg_ctx), "HOST RESET Issued..."); + + /* For host reset, essentially do a soft link up/down */ + atomic_set(&qedf->link_state, QEDF_LINK_DOWN); + atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING); + queue_delayed_work(qedf->link_update_wq, &qedf->link_update, + 0); + qedf_wait_for_upload(qedf); + atomic_set(&qedf->link_state, QEDF_LINK_UP); + qedf->vlan_id = 0; + queue_delayed_work(qedf->link_update_wq, &qedf->link_update, + 0); + + return SUCCESS; +} + +static int qedf_slave_configure(struct scsi_device *sdev) +{ + if (qedf_queue_depth) { + scsi_change_queue_depth(sdev, qedf_queue_depth); + } + + return 0; +} + +static struct scsi_host_template qedf_host_template = { + .module = THIS_MODULE, + .name = QEDF_MODULE_NAME, + .this_id = -1, + .cmd_per_lun = 3, + .use_clustering = ENABLE_CLUSTERING, + .max_sectors = 0xffff, + .queuecommand = qedf_queuecommand, + .shost_attrs = qedf_host_attrs, + .eh_abort_handler = qedf_eh_abort, + .eh_device_reset_handler = qedf_eh_device_reset, /* lun reset */ + .eh_target_reset_handler = qedf_eh_target_reset, /* target reset */ + .eh_host_reset_handler = qedf_eh_host_reset, + .slave_configure = qedf_slave_configure, + .dma_boundary = QED_HW_DMA_BOUNDARY, + .sg_tablesize = QEDF_MAX_BDS_PER_CMD, + .can_queue = FCOE_PARAMS_NUM_TASKS, +}; + +static int qedf_get_paged_crc_eof(struct sk_buff *skb, int tlen) +{ + int rc; + + spin_lock(&qedf_global_lock); + rc = fcoe_get_paged_crc_eof(skb, tlen, &qedf_global); + spin_unlock(&qedf_global_lock); + + return rc; +} + +static struct qedf_rport *qedf_fcport_lookup(struct qedf_ctx *qedf, u32 port_id) +{ + struct qedf_rport *fcport; + struct fc_rport_priv *rdata; + + rcu_read_lock(); + list_for_each_entry_rcu(fcport, &qedf->fcports, peers) { + rdata = fcport->rdata; + if (rdata == NULL) + continue; + if (rdata->ids.port_id == port_id) { + rcu_read_unlock(); + return fcport; + } + } + rcu_read_unlock(); + + /* Return NULL to caller to let them know fcport was not found */ + return NULL; +} + +/* Transmits an ELS frame over an offloaded session */ +static int qedf_xmit_l2_frame(struct qedf_rport *fcport, struct fc_frame *fp) +{ + struct fc_frame_header *fh; + int rc = 0; + + fh = fc_frame_header_get(fp); + if ((fh->fh_type == FC_TYPE_ELS) && + (fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { + switch (fc_frame_payload_op(fp)) { + case ELS_ADISC: + qedf_send_adisc(fcport, fp); + rc = 1; + break; + } + } + + return rc; +} + +/** + * qedf_xmit - qedf FCoE frame transmit function + * + */ +static int qedf_xmit(struct fc_lport *lport, struct fc_frame *fp) +{ + struct fc_lport *base_lport; + struct qedf_ctx *qedf; + struct ethhdr *eh; + struct fcoe_crc_eof *cp; + struct sk_buff *skb; + struct fc_frame_header *fh; + struct fcoe_hdr *hp; + u8 sof, eof; + u32 crc; + unsigned int hlen, tlen, elen; + int wlen; + struct fc_stats *stats; + struct fc_lport *tmp_lport; + struct fc_lport *vn_port = NULL; + struct qedf_rport *fcport; + int rc; + u16 vlan_tci = 0; + + qedf = (struct qedf_ctx *)lport_priv(lport); + + fh = fc_frame_header_get(fp); + skb = fp_skb(fp); + + /* Filter out traffic to other NPIV ports on the same host */ + if (lport->vport) + base_lport = shost_priv(vport_to_shost(lport->vport)); + else + base_lport = lport; + + /* Flag if the destination is the base port */ + if (base_lport->port_id == ntoh24(fh->fh_d_id)) { + vn_port = base_lport; + } else { + /* Got through the list of vports attached to the base_lport + * and see if we have a match with the destination address. + */ + list_for_each_entry(tmp_lport, &base_lport->vports, list) { + if (tmp_lport->port_id == ntoh24(fh->fh_d_id)) { + vn_port = tmp_lport; + break; + } + } + } + if (vn_port && ntoh24(fh->fh_d_id) != FC_FID_FLOGI) { + struct fc_rport_priv *rdata = NULL; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, + "Dropping FCoE frame to %06x.\n", ntoh24(fh->fh_d_id)); + kfree_skb(skb); + rdata = fc_rport_lookup(lport, ntoh24(fh->fh_d_id)); + if (rdata) + rdata->retries = lport->max_rport_retry_count; + return -EINVAL; + } + /* End NPIV filtering */ + + if (!qedf->ctlr.sel_fcf) { + kfree_skb(skb); + return 0; + } + + if (!test_bit(QEDF_LL2_STARTED, &qedf->flags)) { + QEDF_WARN(&(qedf->dbg_ctx), "LL2 not started\n"); + kfree_skb(skb); + return 0; + } + + if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) { + QEDF_WARN(&(qedf->dbg_ctx), "qedf link down\n"); + kfree_skb(skb); + return 0; + } + + if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { + if (fcoe_ctlr_els_send(&qedf->ctlr, lport, skb)) + return 0; + } + + /* Check to see if this needs to be sent on an offloaded session */ + fcport = qedf_fcport_lookup(qedf, ntoh24(fh->fh_d_id)); + + if (fcport && test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + rc = qedf_xmit_l2_frame(fcport, fp); + /* + * If the frame was successfully sent over the middle path + * then do not try to also send it over the LL2 path + */ + if (rc) + return 0; + } + + sof = fr_sof(fp); + eof = fr_eof(fp); + + elen = sizeof(struct ethhdr); + hlen = sizeof(struct fcoe_hdr); + tlen = sizeof(struct fcoe_crc_eof); + wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; + + skb->ip_summed = CHECKSUM_NONE; + crc = fcoe_fc_crc(fp); + + /* copy port crc and eof to the skb buff */ + if (skb_is_nonlinear(skb)) { + skb_frag_t *frag; + + if (qedf_get_paged_crc_eof(skb, tlen)) { + kfree_skb(skb); + return -ENOMEM; + } + frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; + cp = kmap_atomic(skb_frag_page(frag)) + frag->page_offset; + } else { + cp = (struct fcoe_crc_eof *)skb_put(skb, tlen); + } + + memset(cp, 0, sizeof(*cp)); + cp->fcoe_eof = eof; + cp->fcoe_crc32 = cpu_to_le32(~crc); + if (skb_is_nonlinear(skb)) { + kunmap_atomic(cp); + cp = NULL; + } + + + /* adjust skb network/transport offsets to match mac/fcoe/port */ + skb_push(skb, elen + hlen); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb->mac_len = elen; + skb->protocol = htons(ETH_P_FCOE); + + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), qedf->vlan_id); + + /* fill up mac and fcoe headers */ + eh = eth_hdr(skb); + eh->h_proto = htons(ETH_P_FCOE); + if (qedf->ctlr.map_dest) + fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); + else + /* insert GW address */ + ether_addr_copy(eh->h_dest, qedf->ctlr.dest_addr); + + /* Set the source MAC address */ + fc_fcoe_set_mac(eh->h_source, fh->fh_s_id); + + hp = (struct fcoe_hdr *)(eh + 1); + memset(hp, 0, sizeof(*hp)); + if (FC_FCOE_VER) + FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); + hp->fcoe_sof = sof; + + /*update tx stats */ + stats = per_cpu_ptr(lport->stats, get_cpu()); + stats->TxFrames++; + stats->TxWords += wlen; + put_cpu(); + + /* Get VLAN ID from skb for printing purposes */ + __vlan_hwaccel_get_tag(skb, &vlan_tci); + + /* send down to lld */ + fr_dev(fp) = lport; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FCoE frame send: " + "src=%06x dest=%06x r_ctl=%x type=%x vlan=%04x.\n", + ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl, fh->fh_type, + vlan_tci); + if (qedf_dump_frames) + print_hex_dump(KERN_WARNING, "fcoe: ", DUMP_PREFIX_OFFSET, 16, + 1, skb->data, skb->len, false); + qed_ops->ll2->start_xmit(qedf->cdev, skb); + + return 0; +} + +static int qedf_alloc_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport) +{ + int rval = 0; + u32 *pbl; + dma_addr_t page; + int num_pages; + + /* Calculate appropriate queue and PBL sizes */ + fcport->sq_mem_size = SQ_NUM_ENTRIES * sizeof(struct fcoe_wqe); + fcport->sq_mem_size = ALIGN(fcport->sq_mem_size, QEDF_PAGE_SIZE); + fcport->sq_pbl_size = (fcport->sq_mem_size / QEDF_PAGE_SIZE) * + sizeof(void *); + fcport->sq_pbl_size = fcport->sq_pbl_size + QEDF_PAGE_SIZE; + + fcport->sq = dma_alloc_coherent(&qedf->pdev->dev, fcport->sq_mem_size, + &fcport->sq_dma, GFP_KERNEL); + if (!fcport->sq) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send " + "queue.\n"); + rval = 1; + goto out; + } + memset(fcport->sq, 0, fcport->sq_mem_size); + + fcport->sq_pbl = dma_alloc_coherent(&qedf->pdev->dev, + fcport->sq_pbl_size, &fcport->sq_pbl_dma, GFP_KERNEL); + if (!fcport->sq_pbl) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send " + "queue PBL.\n"); + rval = 1; + goto out_free_sq; + } + memset(fcport->sq_pbl, 0, fcport->sq_pbl_size); + + /* Create PBL */ + num_pages = fcport->sq_mem_size / QEDF_PAGE_SIZE; + page = fcport->sq_dma; + pbl = (u32 *)fcport->sq_pbl; + + while (num_pages--) { + *pbl = U64_LO(page); + pbl++; + *pbl = U64_HI(page); + pbl++; + page += QEDF_PAGE_SIZE; + } + + return rval; + +out_free_sq: + dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size, fcport->sq, + fcport->sq_dma); +out: + return rval; +} + +static void qedf_free_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport) +{ + if (fcport->sq_pbl) + dma_free_coherent(&qedf->pdev->dev, fcport->sq_pbl_size, + fcport->sq_pbl, fcport->sq_pbl_dma); + if (fcport->sq) + dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size, + fcport->sq, fcport->sq_dma); +} + +static int qedf_offload_connection(struct qedf_ctx *qedf, + struct qedf_rport *fcport) +{ + struct qed_fcoe_params_offload conn_info; + u32 port_id; + u8 lport_src_id[3]; + int rval; + uint16_t total_sqe = (fcport->sq_mem_size / sizeof(struct fcoe_wqe)); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offloading connection " + "portid=%06x.\n", fcport->rdata->ids.port_id); + rval = qed_ops->acquire_conn(qedf->cdev, &fcport->handle, + &fcport->fw_cid, &fcport->p_doorbell); + if (rval) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not acquire connection " + "for portid=%06x.\n", fcport->rdata->ids.port_id); + rval = 1; /* For some reason qed returns 0 on failure here */ + goto out; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "portid=%06x " + "fw_cid=%08x handle=%d.\n", fcport->rdata->ids.port_id, + fcport->fw_cid, fcport->handle); + + memset(&conn_info, 0, sizeof(struct qed_fcoe_params_offload)); + + /* Fill in the offload connection info */ + conn_info.sq_pbl_addr = fcport->sq_pbl_dma; + + conn_info.sq_curr_page_addr = (dma_addr_t)(*(u64 *)fcport->sq_pbl); + conn_info.sq_next_page_addr = + (dma_addr_t)(*(u64 *)(fcport->sq_pbl + 8)); + + /* Need to use our FCoE MAC for the offload session */ + port_id = fc_host_port_id(qedf->lport->host); + lport_src_id[2] = (port_id & 0x000000FF); + lport_src_id[1] = (port_id & 0x0000FF00) >> 8; + lport_src_id[0] = (port_id & 0x00FF0000) >> 16; + fc_fcoe_set_mac(conn_info.src_mac, lport_src_id); + + ether_addr_copy(conn_info.dst_mac, qedf->ctlr.dest_addr); + + conn_info.tx_max_fc_pay_len = fcport->rdata->maxframe_size; + conn_info.e_d_tov_timer_val = qedf->lport->e_d_tov / 20; + conn_info.rec_tov_timer_val = 3; /* I think this is what E3 was */ + conn_info.rx_max_fc_pay_len = fcport->rdata->maxframe_size; + + /* Set VLAN data */ + conn_info.vlan_tag = qedf->vlan_id << + FCOE_CONN_OFFLOAD_RAMROD_DATA_VLAN_ID_SHIFT; + conn_info.vlan_tag |= + qedf_default_prio << FCOE_CONN_OFFLOAD_RAMROD_DATA_PRIORITY_SHIFT; + conn_info.flags |= (FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_MASK << + FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_SHIFT); + + /* Set host port source id */ + port_id = fc_host_port_id(qedf->lport->host); + fcport->sid = port_id; + conn_info.s_id.addr_hi = (port_id & 0x000000FF); + conn_info.s_id.addr_mid = (port_id & 0x0000FF00) >> 8; + conn_info.s_id.addr_lo = (port_id & 0x00FF0000) >> 16; + + conn_info.max_conc_seqs_c3 = fcport->rdata->max_seq; + + /* Set remote port destination id */ + port_id = fcport->rdata->rport->port_id; + conn_info.d_id.addr_hi = (port_id & 0x000000FF); + conn_info.d_id.addr_mid = (port_id & 0x0000FF00) >> 8; + conn_info.d_id.addr_lo = (port_id & 0x00FF0000) >> 16; + + conn_info.def_q_idx = 0; /* Default index for send queue? */ + + /* Set FC-TAPE specific flags if needed */ + if (fcport->dev_type == QEDF_RPORT_TYPE_TAPE) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, + "Enable CONF, REC for portid=%06x.\n", + fcport->rdata->ids.port_id); + conn_info.flags |= 1 << + FCOE_CONN_OFFLOAD_RAMROD_DATA_B_CONF_REQ_SHIFT; + conn_info.flags |= + ((fcport->rdata->sp_features & FC_SP_FT_SEQC) ? 1 : 0) << + FCOE_CONN_OFFLOAD_RAMROD_DATA_B_REC_VALID_SHIFT; + } + + rval = qed_ops->offload_conn(qedf->cdev, fcport->handle, &conn_info); + if (rval) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not offload connection " + "for portid=%06x.\n", fcport->rdata->ids.port_id); + goto out_free_conn; + } else + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offload " + "succeeded portid=%06x total_sqe=%d.\n", + fcport->rdata->ids.port_id, total_sqe); + + spin_lock_init(&fcport->rport_lock); + atomic_set(&fcport->free_sqes, total_sqe); + return 0; +out_free_conn: + qed_ops->release_conn(qedf->cdev, fcport->handle); +out: + return rval; +} + +#define QEDF_TERM_BUFF_SIZE 10 +static void qedf_upload_connection(struct qedf_ctx *qedf, + struct qedf_rport *fcport) +{ + void *term_params; + dma_addr_t term_params_dma; + + /* Term params needs to be a DMA coherent buffer as qed shared the + * physical DMA address with the firmware. The buffer may be used in + * the receive path so we may eventually have to move this. + */ + term_params = dma_alloc_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE, + &term_params_dma, GFP_KERNEL); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Uploading connection " + "port_id=%06x.\n", fcport->rdata->ids.port_id); + + qed_ops->destroy_conn(qedf->cdev, fcport->handle, term_params_dma); + qed_ops->release_conn(qedf->cdev, fcport->handle); + + dma_free_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE, term_params, + term_params_dma); +} + +static void qedf_cleanup_fcport(struct qedf_ctx *qedf, + struct qedf_rport *fcport) +{ + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Cleaning up portid=%06x.\n", + fcport->rdata->ids.port_id); + + /* Flush any remaining i/o's before we upload the connection */ + qedf_flush_active_ios(fcport, -1); + + if (test_and_clear_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) + qedf_upload_connection(qedf, fcport); + qedf_free_sq(qedf, fcport); + fcport->rdata = NULL; + fcport->qedf = NULL; +} + +/** + * This event_callback is called after successful completion of libfc + * initiated target login. qedf can proceed with initiating the session + * establishment. + */ +static void qedf_rport_event_handler(struct fc_lport *lport, + struct fc_rport_priv *rdata, + enum fc_rport_event event) +{ + struct qedf_ctx *qedf = lport_priv(lport); + struct fc_rport *rport = rdata->rport; + struct fc_rport_libfc_priv *rp; + struct qedf_rport *fcport; + u32 port_id; + int rval; + unsigned long flags; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "event = %d, " + "port_id = 0x%x\n", event, rdata->ids.port_id); + + switch (event) { + case RPORT_EV_READY: + if (!rport) { + QEDF_WARN(&(qedf->dbg_ctx), "rport is NULL.\n"); + break; + } + + rp = rport->dd_data; + fcport = (struct qedf_rport *)&rp[1]; + fcport->qedf = qedf; + + if (atomic_read(&qedf->num_offloads) >= QEDF_MAX_SESSIONS) { + QEDF_ERR(&(qedf->dbg_ctx), "Not offloading " + "portid=0x%x as max number of offloaded sessions " + "reached.\n", rdata->ids.port_id); + return; + } + + /* + * Don't try to offload the session again. Can happen when we + * get an ADISC + */ + if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + QEDF_WARN(&(qedf->dbg_ctx), "Session already " + "offloaded, portid=0x%x.\n", + rdata->ids.port_id); + return; + } + + if (rport->port_id == FC_FID_DIR_SERV) { + /* + * qedf_rport structure doesn't exist for + * directory server. + * We should not come here, as lport will + * take care of fabric login + */ + QEDF_WARN(&(qedf->dbg_ctx), "rport struct does not " + "exist for dir server port_id=%x\n", + rdata->ids.port_id); + break; + } + + if (rdata->spp_type != FC_TYPE_FCP) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Not offlading since since spp type isn't FCP\n"); + break; + } + if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Not FCP target so not offloading\n"); + break; + } + + fcport->rdata = rdata; + fcport->rport = rport; + + rval = qedf_alloc_sq(qedf, fcport); + if (rval) { + qedf_cleanup_fcport(qedf, fcport); + break; + } + + /* Set device type */ + if (rdata->flags & FC_RP_FLAGS_RETRY && + rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET && + !(rdata->ids.roles & FC_RPORT_ROLE_FCP_INITIATOR)) { + fcport->dev_type = QEDF_RPORT_TYPE_TAPE; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "portid=%06x is a TAPE device.\n", + rdata->ids.port_id); + } else { + fcport->dev_type = QEDF_RPORT_TYPE_DISK; + } + + rval = qedf_offload_connection(qedf, fcport); + if (rval) { + qedf_cleanup_fcport(qedf, fcport); + break; + } + + /* Add fcport to list of qedf_ctx list of offloaded ports */ + spin_lock_irqsave(&qedf->hba_lock, flags); + list_add_rcu(&fcport->peers, &qedf->fcports); + spin_unlock_irqrestore(&qedf->hba_lock, flags); + + /* + * Set the session ready bit to let everyone know that this + * connection is ready for I/O + */ + set_bit(QEDF_RPORT_SESSION_READY, &fcport->flags); + atomic_inc(&qedf->num_offloads); + + break; + case RPORT_EV_LOGO: + case RPORT_EV_FAILED: + case RPORT_EV_STOP: + port_id = rdata->ids.port_id; + if (port_id == FC_FID_DIR_SERV) + break; + + if (!rport) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "port_id=%x - rport notcreated Yet!!\n", port_id); + break; + } + rp = rport->dd_data; + /* + * Perform session upload. Note that rdata->peers is already + * removed from disc->rports list before we get this event. + */ + fcport = (struct qedf_rport *)&rp[1]; + + /* Only free this fcport if it is offloaded already */ + if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + set_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags); + qedf_cleanup_fcport(qedf, fcport); + + /* + * Remove fcport to list of qedf_ctx list of offloaded + * ports + */ + spin_lock_irqsave(&qedf->hba_lock, flags); + list_del_rcu(&fcport->peers); + spin_unlock_irqrestore(&qedf->hba_lock, flags); + + clear_bit(QEDF_RPORT_UPLOADING_CONNECTION, + &fcport->flags); + atomic_dec(&qedf->num_offloads); + } + + break; + + case RPORT_EV_NONE: + break; + } +} + +static void qedf_abort_io(struct fc_lport *lport) +{ + /* NO-OP but need to fill in the template */ +} + +static void qedf_fcp_cleanup(struct fc_lport *lport) +{ + /* + * NO-OP but need to fill in template to prevent a NULL + * function pointer dereference during link down. I/Os + * will be flushed when port is uploaded. + */ +} + +static struct libfc_function_template qedf_lport_template = { + .frame_send = qedf_xmit, + .fcp_abort_io = qedf_abort_io, + .fcp_cleanup = qedf_fcp_cleanup, + .rport_event_callback = qedf_rport_event_handler, + .elsct_send = qedf_elsct_send, +}; + +static void qedf_fcoe_ctlr_setup(struct qedf_ctx *qedf) +{ + fcoe_ctlr_init(&qedf->ctlr, FIP_ST_AUTO); + + qedf->ctlr.send = qedf_fip_send; + qedf->ctlr.update_mac = qedf_update_src_mac; + qedf->ctlr.get_src_addr = qedf_get_src_mac; + ether_addr_copy(qedf->ctlr.ctl_src_addr, qedf->mac); +} + +static int qedf_lport_setup(struct qedf_ctx *qedf) +{ + struct fc_lport *lport = qedf->lport; + + lport->link_up = 0; + lport->max_retry_count = QEDF_FLOGI_RETRY_CNT; + lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT; + lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | + FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); + lport->boot_time = jiffies; + lport->e_d_tov = 2 * 1000; + lport->r_a_tov = 10 * 1000; + + /* Set NPIV support */ + lport->does_npiv = 1; + fc_host_max_npiv_vports(lport->host) = QEDF_MAX_NPIV; + + fc_set_wwnn(lport, qedf->wwnn); + fc_set_wwpn(lport, qedf->wwpn); + + fcoe_libfc_config(lport, &qedf->ctlr, &qedf_lport_template, 0); + + /* Allocate the exchange manager */ + fc_exch_mgr_alloc(lport, FC_CLASS_3, qedf->max_scsi_xid + 1, + qedf->max_els_xid, NULL); + + if (fc_lport_init_stats(lport)) + return -ENOMEM; + + /* Finish lport config */ + fc_lport_config(lport); + + /* Set max frame size */ + fc_set_mfs(lport, QEDF_MFS); + fc_host_maxframe_size(lport->host) = lport->mfs; + + /* Set default dev_loss_tmo based on module parameter */ + fc_host_dev_loss_tmo(lport->host) = qedf_dev_loss_tmo; + + /* Set symbolic node name */ + snprintf(fc_host_symbolic_name(lport->host), 256, + "QLogic %s v%s", QEDF_MODULE_NAME, QEDF_VERSION); + + return 0; +} + +/* + * NPIV functions + */ + +static int qedf_vport_libfc_config(struct fc_vport *vport, + struct fc_lport *lport) +{ + lport->link_up = 0; + lport->qfull = 0; + lport->max_retry_count = QEDF_FLOGI_RETRY_CNT; + lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT; + lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | + FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); + lport->boot_time = jiffies; + lport->e_d_tov = 2 * 1000; + lport->r_a_tov = 10 * 1000; + lport->does_npiv = 1; /* Temporary until we add NPIV support */ + + /* Allocate stats for vport */ + if (fc_lport_init_stats(lport)) + return -ENOMEM; + + /* Finish lport config */ + fc_lport_config(lport); + + /* offload related configuration */ + lport->crc_offload = 0; + lport->seq_offload = 0; + lport->lro_enabled = 0; + lport->lro_xid = 0; + lport->lso_max = 0; + + return 0; +} + +static int qedf_vport_create(struct fc_vport *vport, bool disabled) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fc_lport *vn_port; + struct qedf_ctx *base_qedf = lport_priv(n_port); + struct qedf_ctx *vport_qedf; + + char buf[32]; + int rc = 0; + + rc = fcoe_validate_vport_create(vport); + if (rc) { + fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); + QEDF_WARN(&(base_qedf->dbg_ctx), "Failed to create vport, " + "WWPN (0x%s) already exists.\n", buf); + goto err1; + } + + if (atomic_read(&base_qedf->link_state) != QEDF_LINK_UP) { + QEDF_WARN(&(base_qedf->dbg_ctx), "Cannot create vport " + "because link is not up.\n"); + rc = -EIO; + goto err1; + } + + vn_port = libfc_vport_create(vport, sizeof(struct qedf_ctx)); + if (!vn_port) { + QEDF_WARN(&(base_qedf->dbg_ctx), "Could not create lport " + "for vport.\n"); + rc = -ENOMEM; + goto err1; + } + + fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); + QEDF_ERR(&(base_qedf->dbg_ctx), "Creating NPIV port, WWPN=%s.\n", + buf); + + /* Copy some fields from base_qedf */ + vport_qedf = lport_priv(vn_port); + memcpy(vport_qedf, base_qedf, sizeof(struct qedf_ctx)); + + /* Set qedf data specific to this vport */ + vport_qedf->lport = vn_port; + /* Use same hba_lock as base_qedf */ + vport_qedf->hba_lock = base_qedf->hba_lock; + vport_qedf->pdev = base_qedf->pdev; + vport_qedf->cmd_mgr = base_qedf->cmd_mgr; + init_completion(&vport_qedf->flogi_compl); + INIT_LIST_HEAD(&vport_qedf->fcports); + + rc = qedf_vport_libfc_config(vport, vn_port); + if (rc) { + QEDF_ERR(&(base_qedf->dbg_ctx), "Could not allocate memory " + "for lport stats.\n"); + goto err2; + } + + fc_set_wwnn(vn_port, vport->node_name); + fc_set_wwpn(vn_port, vport->port_name); + vport_qedf->wwnn = vn_port->wwnn; + vport_qedf->wwpn = vn_port->wwpn; + + vn_port->host->transportt = qedf_fc_vport_transport_template; + vn_port->host->can_queue = QEDF_MAX_ELS_XID; + vn_port->host->max_lun = qedf_max_lun; + vn_port->host->sg_tablesize = QEDF_MAX_BDS_PER_CMD; + vn_port->host->max_cmd_len = QEDF_MAX_CDB_LEN; + + rc = scsi_add_host(vn_port->host, &vport->dev); + if (rc) { + QEDF_WARN(&(base_qedf->dbg_ctx), "Error adding Scsi_Host.\n"); + goto err2; + } + + /* Set default dev_loss_tmo based on module parameter */ + fc_host_dev_loss_tmo(vn_port->host) = qedf_dev_loss_tmo; + + /* Init libfc stuffs */ + memcpy(&vn_port->tt, &qedf_lport_template, + sizeof(qedf_lport_template)); + fc_exch_init(vn_port); + fc_elsct_init(vn_port); + fc_lport_init(vn_port); + fc_disc_init(vn_port); + fc_disc_config(vn_port, vn_port); + + + /* Allocate the exchange manager */ + shost = vport_to_shost(vport); + n_port = shost_priv(shost); + fc_exch_mgr_list_clone(n_port, vn_port); + + /* Set max frame size */ + fc_set_mfs(vn_port, QEDF_MFS); + + fc_host_port_type(vn_port->host) = FC_PORTTYPE_UNKNOWN; + + if (disabled) { + fc_vport_set_state(vport, FC_VPORT_DISABLED); + } else { + vn_port->boot_time = jiffies; + fc_fabric_login(vn_port); + fc_vport_setlink(vn_port); + } + + QEDF_INFO(&(base_qedf->dbg_ctx), QEDF_LOG_NPIV, "vn_port=%p.\n", + vn_port); + + /* Set up debug context for vport */ + vport_qedf->dbg_ctx.host_no = vn_port->host->host_no; + vport_qedf->dbg_ctx.pdev = base_qedf->pdev; + +err2: + scsi_host_put(vn_port->host); +err1: + return rc; +} + +static int qedf_vport_destroy(struct fc_vport *vport) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fc_lport *vn_port = vport->dd_data; + + mutex_lock(&n_port->lp_mutex); + list_del(&vn_port->list); + mutex_unlock(&n_port->lp_mutex); + + fc_fabric_logoff(vn_port); + fc_lport_destroy(vn_port); + + /* Detach from scsi-ml */ + fc_remove_host(vn_port->host); + scsi_remove_host(vn_port->host); + + /* + * Only try to release the exchange manager if the vn_port + * configuration is complete. + */ + if (vn_port->state == LPORT_ST_READY) + fc_exch_mgr_free(vn_port); + + /* Free memory used by statistical counters */ + fc_lport_free_stats(vn_port); + + /* Release Scsi_Host */ + if (vn_port->host) + scsi_host_put(vn_port->host); + + return 0; +} + +static int qedf_vport_disable(struct fc_vport *vport, bool disable) +{ + struct fc_lport *lport = vport->dd_data; + + if (disable) { + fc_vport_set_state(vport, FC_VPORT_DISABLED); + fc_fabric_logoff(lport); + } else { + lport->boot_time = jiffies; + fc_fabric_login(lport); + fc_vport_setlink(lport); + } + return 0; +} + +/* + * During removal we need to wait for all the vports associated with a port + * to be destroyed so we avoid a race condition where libfc is still trying + * to reap vports while the driver remove function has already reaped the + * driver contexts associated with the physical port. + */ +static void qedf_wait_for_vport_destroy(struct qedf_ctx *qedf) +{ + struct fc_host_attrs *fc_host = shost_to_fc_host(qedf->lport->host); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV, + "Entered.\n"); + while (fc_host->npiv_vports_inuse > 0) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV, + "Waiting for all vports to be reaped.\n"); + msleep(1000); + } +} + +/** + * qedf_fcoe_reset - Resets the fcoe + * + * @shost: shost the reset is from + * + * Returns: always 0 + */ +static int qedf_fcoe_reset(struct Scsi_Host *shost) +{ + struct fc_lport *lport = shost_priv(shost); + + fc_fabric_logoff(lport); + fc_fabric_login(lport); + return 0; +} + +static struct fc_host_statistics *qedf_fc_get_host_stats(struct Scsi_Host + *shost) +{ + struct fc_host_statistics *qedf_stats; + struct fc_lport *lport = shost_priv(shost); + struct qedf_ctx *qedf = lport_priv(lport); + struct qed_fcoe_stats *fw_fcoe_stats; + + qedf_stats = fc_get_host_stats(shost); + + /* We don't collect offload stats for specific NPIV ports */ + if (lport->vport) + goto out; + + fw_fcoe_stats = kmalloc(sizeof(struct qed_fcoe_stats), GFP_KERNEL); + if (!fw_fcoe_stats) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate memory for " + "fw_fcoe_stats.\n"); + goto out; + } + + /* Query firmware for offload stats */ + qed_ops->get_stats(qedf->cdev, fw_fcoe_stats); + + /* + * The expectation is that we add our offload stats to the stats + * being maintained by libfc each time the fc_get_host_status callback + * is invoked. The additions are not carried over for each call to + * the fc_get_host_stats callback. + */ + qedf_stats->tx_frames += fw_fcoe_stats->fcoe_tx_data_pkt_cnt + + fw_fcoe_stats->fcoe_tx_xfer_pkt_cnt + + fw_fcoe_stats->fcoe_tx_other_pkt_cnt; + qedf_stats->rx_frames += fw_fcoe_stats->fcoe_rx_data_pkt_cnt + + fw_fcoe_stats->fcoe_rx_xfer_pkt_cnt + + fw_fcoe_stats->fcoe_rx_other_pkt_cnt; + qedf_stats->fcp_input_megabytes += + do_div(fw_fcoe_stats->fcoe_rx_byte_cnt, 1000000); + qedf_stats->fcp_output_megabytes += + do_div(fw_fcoe_stats->fcoe_tx_byte_cnt, 1000000); + qedf_stats->rx_words += fw_fcoe_stats->fcoe_rx_byte_cnt / 4; + qedf_stats->tx_words += fw_fcoe_stats->fcoe_tx_byte_cnt / 4; + qedf_stats->invalid_crc_count += + fw_fcoe_stats->fcoe_silent_drop_pkt_crc_error_cnt; + qedf_stats->dumped_frames = + fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt; + qedf_stats->error_frames += + fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt; + qedf_stats->fcp_input_requests += qedf->input_requests; + qedf_stats->fcp_output_requests += qedf->output_requests; + qedf_stats->fcp_control_requests += qedf->control_requests; + qedf_stats->fcp_packet_aborts += qedf->packet_aborts; + qedf_stats->fcp_frame_alloc_failures += qedf->alloc_failures; + + kfree(fw_fcoe_stats); +out: + return qedf_stats; +} + +static struct fc_function_template qedf_fc_transport_fn = { + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_active_fc4s = 1, + .show_host_maxframe_size = 1, + + .show_host_port_id = 1, + .show_host_supported_speeds = 1, + .get_host_speed = fc_get_host_speed, + .show_host_speed = 1, + .show_host_port_type = 1, + .get_host_port_state = fc_get_host_port_state, + .show_host_port_state = 1, + .show_host_symbolic_name = 1, + + /* + * Tell FC transport to allocate enough space to store the backpointer + * for the associate qedf_rport struct. + */ + .dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) + + sizeof(struct qedf_rport)), + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_host_fabric_name = 1, + .show_starget_node_name = 1, + .show_starget_port_name = 1, + .show_starget_port_id = 1, + .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, + .show_rport_dev_loss_tmo = 1, + .get_fc_host_stats = qedf_fc_get_host_stats, + .issue_fc_host_lip = qedf_fcoe_reset, + .vport_create = qedf_vport_create, + .vport_delete = qedf_vport_destroy, + .vport_disable = qedf_vport_disable, + .bsg_request = fc_lport_bsg_request, +}; + +static struct fc_function_template qedf_fc_vport_transport_fn = { + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_active_fc4s = 1, + .show_host_maxframe_size = 1, + .show_host_port_id = 1, + .show_host_supported_speeds = 1, + .get_host_speed = fc_get_host_speed, + .show_host_speed = 1, + .show_host_port_type = 1, + .get_host_port_state = fc_get_host_port_state, + .show_host_port_state = 1, + .show_host_symbolic_name = 1, + .dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) + + sizeof(struct qedf_rport)), + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_host_fabric_name = 1, + .show_starget_node_name = 1, + .show_starget_port_name = 1, + .show_starget_port_id = 1, + .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, + .show_rport_dev_loss_tmo = 1, + .get_fc_host_stats = fc_get_host_stats, + .issue_fc_host_lip = qedf_fcoe_reset, + .bsg_request = fc_lport_bsg_request, +}; + +static bool qedf_fp_has_work(struct qedf_fastpath *fp) +{ + struct qedf_ctx *qedf = fp->qedf; + struct global_queue *que; + struct qed_sb_info *sb_info = fp->sb_info; + struct status_block *sb = sb_info->sb_virt; + u16 prod_idx; + + /* Get the pointer to the global CQ this completion is on */ + que = qedf->global_queues[fp->sb_id]; + + /* Be sure all responses have been written to PI */ + rmb(); + + /* Get the current firmware producer index */ + prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI]; + + return (que->cq_prod_idx != prod_idx); +} + +/* + * Interrupt handler code. + */ + +/* Process completion queue and copy CQE contents for deferred processesing + * + * Return true if we should wake the I/O thread, false if not. + */ +static bool qedf_process_completions(struct qedf_fastpath *fp) +{ + struct qedf_ctx *qedf = fp->qedf; + struct qed_sb_info *sb_info = fp->sb_info; + struct status_block *sb = sb_info->sb_virt; + struct global_queue *que; + u16 prod_idx; + struct fcoe_cqe *cqe; + struct qedf_io_work *io_work; + int num_handled = 0; + unsigned int cpu; + struct qedf_ioreq *io_req = NULL; + u16 xid; + u16 new_cqes; + u32 comp_type; + + /* Get the current firmware producer index */ + prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI]; + + /* Get the pointer to the global CQ this completion is on */ + que = qedf->global_queues[fp->sb_id]; + + /* Calculate the amount of new elements since last processing */ + new_cqes = (prod_idx >= que->cq_prod_idx) ? + (prod_idx - que->cq_prod_idx) : + 0x10000 - que->cq_prod_idx + prod_idx; + + /* Save producer index */ + que->cq_prod_idx = prod_idx; + + while (new_cqes) { + fp->completions++; + num_handled++; + cqe = &que->cq[que->cq_cons_idx]; + + comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) & + FCOE_CQE_CQE_TYPE_MASK; + + /* + * Process unsolicited CQEs directly in the interrupt handler + * sine we need the fastpath ID + */ + if (comp_type == FCOE_UNSOLIC_CQE_TYPE) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL, + "Unsolicated CQE.\n"); + qedf_process_unsol_compl(qedf, fp->sb_id, cqe); + /* + * Don't add a work list item. Increment consumer + * consumer index and move on. + */ + goto inc_idx; + } + + xid = cqe->cqe_data & FCOE_CQE_TASK_ID_MASK; + io_req = &qedf->cmd_mgr->cmds[xid]; + + /* + * Figure out which percpu thread we should queue this I/O + * on. + */ + if (!io_req) + /* If there is not io_req assocated with this CQE + * just queue it on CPU 0 + */ + cpu = 0; + else { + cpu = io_req->cpu; + io_req->int_cpu = smp_processor_id(); + } + + io_work = mempool_alloc(qedf->io_mempool, GFP_ATOMIC); + if (!io_work) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate " + "work for I/O completion.\n"); + continue; + } + memset(io_work, 0, sizeof(struct qedf_io_work)); + + INIT_WORK(&io_work->work, qedf_fp_io_handler); + + /* Copy contents of CQE for deferred processing */ + memcpy(&io_work->cqe, cqe, sizeof(struct fcoe_cqe)); + + io_work->qedf = fp->qedf; + io_work->fp = NULL; /* Only used for unsolicited frames */ + + queue_work_on(cpu, qedf_io_wq, &io_work->work); + +inc_idx: + que->cq_cons_idx++; + if (que->cq_cons_idx == fp->cq_num_entries) + que->cq_cons_idx = 0; + new_cqes--; + } + + return true; +} + + +/* MSI-X fastpath handler code */ +static irqreturn_t qedf_msix_handler(int irq, void *dev_id) +{ + struct qedf_fastpath *fp = dev_id; + + if (!fp) { + QEDF_ERR(NULL, "fp is null.\n"); + return IRQ_HANDLED; + } + if (!fp->sb_info) { + QEDF_ERR(NULL, "fp->sb_info in null."); + return IRQ_HANDLED; + } + + /* + * Disable interrupts for this status block while we process new + * completions + */ + qed_sb_ack(fp->sb_info, IGU_INT_DISABLE, 0 /*do not update*/); + + while (1) { + qedf_process_completions(fp); + + if (qedf_fp_has_work(fp) == 0) { + /* Update the sb information */ + qed_sb_update_sb_idx(fp->sb_info); + + /* Check for more work */ + rmb(); + + if (qedf_fp_has_work(fp) == 0) { + /* Re-enable interrupts */ + qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, 1); + return IRQ_HANDLED; + } + } + } + + /* Do we ever want to break out of above loop? */ + return IRQ_HANDLED; +} + +/* simd handler for MSI/INTa */ +static void qedf_simd_int_handler(void *cookie) +{ + /* Cookie is qedf_ctx struct */ + struct qedf_ctx *qedf = (struct qedf_ctx *)cookie; + + QEDF_WARN(&(qedf->dbg_ctx), "qedf=%p.\n", qedf); +} + +#define QEDF_SIMD_HANDLER_NUM 0 +static void qedf_sync_free_irqs(struct qedf_ctx *qedf) +{ + int i; + + if (qedf->int_info.msix_cnt) { + for (i = 0; i < qedf->int_info.used_cnt; i++) { + synchronize_irq(qedf->int_info.msix[i].vector); + irq_set_affinity_hint(qedf->int_info.msix[i].vector, + NULL); + irq_set_affinity_notifier(qedf->int_info.msix[i].vector, + NULL); + free_irq(qedf->int_info.msix[i].vector, + &qedf->fp_array[i]); + } + } else + qed_ops->common->simd_handler_clean(qedf->cdev, + QEDF_SIMD_HANDLER_NUM); + + qedf->int_info.used_cnt = 0; + qed_ops->common->set_fp_int(qedf->cdev, 0); +} + +static int qedf_request_msix_irq(struct qedf_ctx *qedf) +{ + int i, rc, cpu; + + cpu = cpumask_first(cpu_online_mask); + for (i = 0; i < qedf->num_queues; i++) { + rc = request_irq(qedf->int_info.msix[i].vector, + qedf_msix_handler, 0, "qedf", &qedf->fp_array[i]); + + if (rc) { + QEDF_WARN(&(qedf->dbg_ctx), "request_irq failed.\n"); + qedf_sync_free_irqs(qedf); + return rc; + } + + qedf->int_info.used_cnt++; + rc = irq_set_affinity_hint(qedf->int_info.msix[i].vector, + get_cpu_mask(cpu)); + cpu = cpumask_next(cpu, cpu_online_mask); + } + + return 0; +} + +static int qedf_setup_int(struct qedf_ctx *qedf) +{ + int rc = 0; + + /* + * Learn interrupt configuration + */ + rc = qed_ops->common->set_fp_int(qedf->cdev, num_online_cpus()); + + rc = qed_ops->common->get_fp_int(qedf->cdev, &qedf->int_info); + if (rc) + return 0; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of msix_cnt = " + "0x%x num of cpus = 0x%x\n", qedf->int_info.msix_cnt, + num_online_cpus()); + + if (qedf->int_info.msix_cnt) + return qedf_request_msix_irq(qedf); + + qed_ops->common->simd_handler_config(qedf->cdev, &qedf, + QEDF_SIMD_HANDLER_NUM, qedf_simd_int_handler); + qedf->int_info.used_cnt = 1; + + return 0; +} + +/* Main function for libfc frame reception */ +static void qedf_recv_frame(struct qedf_ctx *qedf, + struct sk_buff *skb) +{ + u32 fr_len; + struct fc_lport *lport; + struct fc_frame_header *fh; + struct fcoe_crc_eof crc_eof; + struct fc_frame *fp; + u8 *mac = NULL; + u8 *dest_mac = NULL; + struct fcoe_hdr *hp; + struct qedf_rport *fcport; + + lport = qedf->lport; + if (lport == NULL || lport->state == LPORT_ST_DISABLED) { + QEDF_WARN(NULL, "Invalid lport struct or lport disabled.\n"); + kfree_skb(skb); + return; + } + + if (skb_is_nonlinear(skb)) + skb_linearize(skb); + mac = eth_hdr(skb)->h_source; + dest_mac = eth_hdr(skb)->h_dest; + + /* Pull the header */ + hp = (struct fcoe_hdr *)skb->data; + fh = (struct fc_frame_header *) skb_transport_header(skb); + skb_pull(skb, sizeof(struct fcoe_hdr)); + fr_len = skb->len - sizeof(struct fcoe_crc_eof); + + fp = (struct fc_frame *)skb; + fc_frame_init(fp); + fr_dev(fp) = lport; + fr_sof(fp) = hp->fcoe_sof; + if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { + kfree_skb(skb); + return; + } + fr_eof(fp) = crc_eof.fcoe_eof; + fr_crc(fp) = crc_eof.fcoe_crc32; + if (pskb_trim(skb, fr_len)) { + kfree_skb(skb); + return; + } + + fh = fc_frame_header_get(fp); + + if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && + fh->fh_type == FC_TYPE_FCP) { + /* Drop FCP data. We dont this in L2 path */ + kfree_skb(skb); + return; + } + if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && + fh->fh_type == FC_TYPE_ELS) { + switch (fc_frame_payload_op(fp)) { + case ELS_LOGO: + if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) { + /* drop non-FIP LOGO */ + kfree_skb(skb); + return; + } + break; + } + } + + if (fh->fh_r_ctl == FC_RCTL_BA_ABTS) { + /* Drop incoming ABTS */ + kfree_skb(skb); + return; + } + + /* + * If a connection is uploading, drop incoming FCoE frames as there + * is a small window where we could try to return a frame while libfc + * is trying to clean things up. + */ + + /* Get fcport associated with d_id if it exists */ + fcport = qedf_fcport_lookup(qedf, ntoh24(fh->fh_d_id)); + + if (fcport && test_bit(QEDF_RPORT_UPLOADING_CONNECTION, + &fcport->flags)) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, + "Connection uploading, dropping fp=%p.\n", fp); + kfree_skb(skb); + return; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FCoE frame receive: " + "skb=%p fp=%p src=%06x dest=%06x r_ctl=%x fh_type=%x.\n", skb, fp, + ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl, + fh->fh_type); + if (qedf_dump_frames) + print_hex_dump(KERN_WARNING, "fcoe: ", DUMP_PREFIX_OFFSET, 16, + 1, skb->data, skb->len, false); + fc_exch_recv(lport, fp); +} + +static void qedf_ll2_process_skb(struct work_struct *work) +{ + struct qedf_skb_work *skb_work = + container_of(work, struct qedf_skb_work, work); + struct qedf_ctx *qedf = skb_work->qedf; + struct sk_buff *skb = skb_work->skb; + struct ethhdr *eh; + + if (!qedf) { + QEDF_ERR(NULL, "qedf is NULL\n"); + goto err_out; + } + + eh = (struct ethhdr *)skb->data; + + /* Undo VLAN encapsulation */ + if (eh->h_proto == htons(ETH_P_8021Q)) { + memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2); + eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN); + skb_reset_mac_header(skb); + } + + /* + * Process either a FIP frame or FCoE frame based on the + * protocol value. If it's not either just drop the + * frame. + */ + if (eh->h_proto == htons(ETH_P_FIP)) { + qedf_fip_recv(qedf, skb); + goto out; + } else if (eh->h_proto == htons(ETH_P_FCOE)) { + __skb_pull(skb, ETH_HLEN); + qedf_recv_frame(qedf, skb); + goto out; + } else + goto err_out; + +err_out: + kfree_skb(skb); +out: + kfree(skb_work); + return; +} + +static int qedf_ll2_rx(void *cookie, struct sk_buff *skb, + u32 arg1, u32 arg2) +{ + struct qedf_ctx *qedf = (struct qedf_ctx *)cookie; + struct qedf_skb_work *skb_work; + + skb_work = kzalloc(sizeof(struct qedf_skb_work), GFP_ATOMIC); + if (!skb_work) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate skb_work so " + "dropping frame.\n"); + kfree_skb(skb); + return 0; + } + + INIT_WORK(&skb_work->work, qedf_ll2_process_skb); + skb_work->skb = skb; + skb_work->qedf = qedf; + queue_work(qedf->ll2_recv_wq, &skb_work->work); + + return 0; +} + +static struct qed_ll2_cb_ops qedf_ll2_cb_ops = { + .rx_cb = qedf_ll2_rx, + .tx_cb = NULL, +}; + +/* Main thread to process I/O completions */ +void qedf_fp_io_handler(struct work_struct *work) +{ + struct qedf_io_work *io_work = + container_of(work, struct qedf_io_work, work); + u32 comp_type; + + /* + * Deferred part of unsolicited CQE sends + * frame to libfc. + */ + comp_type = (io_work->cqe.cqe_data >> + FCOE_CQE_CQE_TYPE_SHIFT) & + FCOE_CQE_CQE_TYPE_MASK; + if (comp_type == FCOE_UNSOLIC_CQE_TYPE && + io_work->fp) + fc_exch_recv(io_work->qedf->lport, io_work->fp); + else + qedf_process_cqe(io_work->qedf, &io_work->cqe); + + kfree(io_work); +} + +static int qedf_alloc_and_init_sb(struct qedf_ctx *qedf, + struct qed_sb_info *sb_info, u16 sb_id) +{ + struct status_block *sb_virt; + dma_addr_t sb_phys; + int ret; + + sb_virt = dma_alloc_coherent(&qedf->pdev->dev, + sizeof(struct status_block), &sb_phys, GFP_KERNEL); + + if (!sb_virt) { + QEDF_ERR(&(qedf->dbg_ctx), "Status block allocation failed " + "for id = %d.\n", sb_id); + return -ENOMEM; + } + + ret = qed_ops->common->sb_init(qedf->cdev, sb_info, sb_virt, sb_phys, + sb_id, QED_SB_TYPE_STORAGE); + + if (ret) { + QEDF_ERR(&(qedf->dbg_ctx), "Status block initialization " + "failed for id = %d.\n", sb_id); + return ret; + } + + return 0; +} + +static void qedf_free_sb(struct qedf_ctx *qedf, struct qed_sb_info *sb_info) +{ + if (sb_info->sb_virt) + dma_free_coherent(&qedf->pdev->dev, sizeof(*sb_info->sb_virt), + (void *)sb_info->sb_virt, sb_info->sb_phys); +} + +static void qedf_destroy_sb(struct qedf_ctx *qedf) +{ + int id; + struct qedf_fastpath *fp = NULL; + + for (id = 0; id < qedf->num_queues; id++) { + fp = &(qedf->fp_array[id]); + if (fp->sb_id == QEDF_SB_ID_NULL) + break; + qedf_free_sb(qedf, fp->sb_info); + kfree(fp->sb_info); + } + kfree(qedf->fp_array); +} + +static int qedf_prepare_sb(struct qedf_ctx *qedf) +{ + int id; + struct qedf_fastpath *fp; + int ret; + + qedf->fp_array = + kcalloc(qedf->num_queues, sizeof(struct qedf_fastpath), + GFP_KERNEL); + + if (!qedf->fp_array) { + QEDF_ERR(&(qedf->dbg_ctx), "fastpath array allocation " + "failed.\n"); + return -ENOMEM; + } + + for (id = 0; id < qedf->num_queues; id++) { + fp = &(qedf->fp_array[id]); + fp->sb_id = QEDF_SB_ID_NULL; + fp->sb_info = kcalloc(1, sizeof(*fp->sb_info), GFP_KERNEL); + if (!fp->sb_info) { + QEDF_ERR(&(qedf->dbg_ctx), "SB info struct " + "allocation failed.\n"); + goto err; + } + ret = qedf_alloc_and_init_sb(qedf, fp->sb_info, id); + if (ret) { + QEDF_ERR(&(qedf->dbg_ctx), "SB allocation and " + "initialization failed.\n"); + goto err; + } + fp->sb_id = id; + fp->qedf = qedf; + fp->cq_num_entries = + qedf->global_queues[id]->cq_mem_size / + sizeof(struct fcoe_cqe); + } +err: + return 0; +} + +void qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe) +{ + u16 xid; + struct qedf_ioreq *io_req; + struct qedf_rport *fcport; + u32 comp_type; + + comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) & + FCOE_CQE_CQE_TYPE_MASK; + + xid = cqe->cqe_data & FCOE_CQE_TASK_ID_MASK; + io_req = &qedf->cmd_mgr->cmds[xid]; + + /* Completion not for a valid I/O anymore so just return */ + if (!io_req) + return; + + fcport = io_req->fcport; + + if (fcport == NULL) { + QEDF_ERR(&(qedf->dbg_ctx), "fcport is NULL.\n"); + return; + } + + /* + * Check that fcport is offloaded. If it isn't then the spinlock + * isn't valid and shouldn't be taken. We should just return. + */ + if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "Session not offloaded yet.\n"); + return; + } + + + switch (comp_type) { + case FCOE_GOOD_COMPLETION_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + switch (io_req->cmd_type) { + case QEDF_SCSI_CMD: + qedf_scsi_completion(qedf, cqe, io_req); + break; + case QEDF_ELS: + qedf_process_els_compl(qedf, cqe, io_req); + break; + case QEDF_TASK_MGMT_CMD: + qedf_process_tmf_compl(qedf, cqe, io_req); + break; + case QEDF_SEQ_CLEANUP: + qedf_process_seq_cleanup_compl(qedf, cqe, io_req); + break; + } + break; + case FCOE_ERROR_DETECTION_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Error detect CQE.\n"); + qedf_process_error_detect(qedf, cqe, io_req); + break; + case FCOE_EXCH_CLEANUP_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Cleanup CQE.\n"); + qedf_process_cleanup_compl(qedf, cqe, io_req); + break; + case FCOE_ABTS_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Abort CQE.\n"); + qedf_process_abts_compl(qedf, cqe, io_req); + break; + case FCOE_DUMMY_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Dummy CQE.\n"); + break; + case FCOE_LOCAL_COMP_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Local completion CQE.\n"); + break; + case FCOE_WARNING_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Warning CQE.\n"); + qedf_process_warning_compl(qedf, cqe, io_req); + break; + case MAX_FCOE_CQE_TYPE: + atomic_inc(&fcport->free_sqes); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Max FCoE CQE.\n"); + break; + default: + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Default CQE.\n"); + break; + } +} + +static void qedf_free_bdq(struct qedf_ctx *qedf) +{ + int i; + + if (qedf->bdq_pbl_list) + dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE, + qedf->bdq_pbl_list, qedf->bdq_pbl_list_dma); + + if (qedf->bdq_pbl) + dma_free_coherent(&qedf->pdev->dev, qedf->bdq_pbl_mem_size, + qedf->bdq_pbl, qedf->bdq_pbl_dma); + + for (i = 0; i < QEDF_BDQ_SIZE; i++) { + if (qedf->bdq[i].buf_addr) { + dma_free_coherent(&qedf->pdev->dev, QEDF_BDQ_BUF_SIZE, + qedf->bdq[i].buf_addr, qedf->bdq[i].buf_dma); + } + } +} + +static void qedf_free_global_queues(struct qedf_ctx *qedf) +{ + int i; + struct global_queue **gl = qedf->global_queues; + + for (i = 0; i < qedf->num_queues; i++) { + if (!gl[i]) + continue; + + if (gl[i]->cq) + dma_free_coherent(&qedf->pdev->dev, + gl[i]->cq_mem_size, gl[i]->cq, gl[i]->cq_dma); + if (gl[i]->cq_pbl) + dma_free_coherent(&qedf->pdev->dev, gl[i]->cq_pbl_size, + gl[i]->cq_pbl, gl[i]->cq_pbl_dma); + + kfree(gl[i]); + } + + qedf_free_bdq(qedf); +} + +static int qedf_alloc_bdq(struct qedf_ctx *qedf) +{ + int i; + struct scsi_bd *pbl; + u64 *list; + dma_addr_t page; + + /* Alloc dma memory for BDQ buffers */ + for (i = 0; i < QEDF_BDQ_SIZE; i++) { + qedf->bdq[i].buf_addr = dma_alloc_coherent(&qedf->pdev->dev, + QEDF_BDQ_BUF_SIZE, &qedf->bdq[i].buf_dma, GFP_KERNEL); + if (!qedf->bdq[i].buf_addr) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate BDQ " + "buffer %d.\n", i); + return -ENOMEM; + } + } + + /* Alloc dma memory for BDQ page buffer list */ + qedf->bdq_pbl_mem_size = + QEDF_BDQ_SIZE * sizeof(struct scsi_bd); + qedf->bdq_pbl_mem_size = + ALIGN(qedf->bdq_pbl_mem_size, QEDF_PAGE_SIZE); + + qedf->bdq_pbl = dma_alloc_coherent(&qedf->pdev->dev, + qedf->bdq_pbl_mem_size, &qedf->bdq_pbl_dma, GFP_KERNEL); + if (!qedf->bdq_pbl) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate BDQ PBL.\n"); + return -ENOMEM; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "BDQ PBL addr=0x%p dma=0x%llx.\n", qedf->bdq_pbl, + qedf->bdq_pbl_dma); + + /* + * Populate BDQ PBL with physical and virtual address of individual + * BDQ buffers + */ + pbl = (struct scsi_bd *)qedf->bdq_pbl; + for (i = 0; i < QEDF_BDQ_SIZE; i++) { + pbl->address.hi = cpu_to_le32(U64_HI(qedf->bdq[i].buf_dma)); + pbl->address.lo = cpu_to_le32(U64_LO(qedf->bdq[i].buf_dma)); + pbl->opaque.hi = 0; + /* Opaque lo data is an index into the BDQ array */ + pbl->opaque.lo = cpu_to_le32(i); + pbl++; + } + + /* Allocate list of PBL pages */ + qedf->bdq_pbl_list = dma_alloc_coherent(&qedf->pdev->dev, + QEDF_PAGE_SIZE, &qedf->bdq_pbl_list_dma, GFP_KERNEL); + if (!qedf->bdq_pbl_list) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate list of PBL " + "pages.\n"); + return -ENOMEM; + } + memset(qedf->bdq_pbl_list, 0, QEDF_PAGE_SIZE); + + /* + * Now populate PBL list with pages that contain pointers to the + * individual buffers. + */ + qedf->bdq_pbl_list_num_entries = qedf->bdq_pbl_mem_size / + QEDF_PAGE_SIZE; + list = (u64 *)qedf->bdq_pbl_list; + page = qedf->bdq_pbl_list_dma; + for (i = 0; i < qedf->bdq_pbl_list_num_entries; i++) { + *list = qedf->bdq_pbl_dma; + list++; + page += QEDF_PAGE_SIZE; + } + + return 0; +} + +static int qedf_alloc_global_queues(struct qedf_ctx *qedf) +{ + u32 *list; + int i; + int status = 0, rc; + u32 *pbl; + dma_addr_t page; + int num_pages; + + /* Allocate and map CQs, RQs */ + /* + * Number of global queues (CQ / RQ). This should + * be <= number of available MSIX vectors for the PF + */ + if (!qedf->num_queues) { + QEDF_ERR(&(qedf->dbg_ctx), "No MSI-X vectors available!\n"); + return 1; + } + + /* + * Make sure we allocated the PBL that will contain the physical + * addresses of our queues + */ + if (!qedf->p_cpuq) { + status = 1; + goto mem_alloc_failure; + } + + qedf->global_queues = kzalloc((sizeof(struct global_queue *) + * qedf->num_queues), GFP_KERNEL); + if (!qedf->global_queues) { + QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate global " + "queues array ptr memory\n"); + return -ENOMEM; + } + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "qedf->global_queues=%p.\n", qedf->global_queues); + + /* Allocate DMA coherent buffers for BDQ */ + rc = qedf_alloc_bdq(qedf); + if (rc) + goto mem_alloc_failure; + + /* Allocate a CQ and an associated PBL for each MSI-X vector */ + for (i = 0; i < qedf->num_queues; i++) { + qedf->global_queues[i] = kzalloc(sizeof(struct global_queue), + GFP_KERNEL); + if (!qedf->global_queues[i]) { + QEDF_WARN(&(qedf->dbg_ctx), "Unable to allocation " + "global queue %d.\n", i); + goto mem_alloc_failure; + } + + qedf->global_queues[i]->cq_mem_size = + FCOE_PARAMS_CQ_NUM_ENTRIES * sizeof(struct fcoe_cqe); + qedf->global_queues[i]->cq_mem_size = + ALIGN(qedf->global_queues[i]->cq_mem_size, QEDF_PAGE_SIZE); + + qedf->global_queues[i]->cq_pbl_size = + (qedf->global_queues[i]->cq_mem_size / + PAGE_SIZE) * sizeof(void *); + qedf->global_queues[i]->cq_pbl_size = + ALIGN(qedf->global_queues[i]->cq_pbl_size, QEDF_PAGE_SIZE); + + qedf->global_queues[i]->cq = + dma_alloc_coherent(&qedf->pdev->dev, + qedf->global_queues[i]->cq_mem_size, + &qedf->global_queues[i]->cq_dma, GFP_KERNEL); + + if (!qedf->global_queues[i]->cq) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate " + "cq.\n"); + status = -ENOMEM; + goto mem_alloc_failure; + } + memset(qedf->global_queues[i]->cq, 0, + qedf->global_queues[i]->cq_mem_size); + + qedf->global_queues[i]->cq_pbl = + dma_alloc_coherent(&qedf->pdev->dev, + qedf->global_queues[i]->cq_pbl_size, + &qedf->global_queues[i]->cq_pbl_dma, GFP_KERNEL); + + if (!qedf->global_queues[i]->cq_pbl) { + QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate " + "cq PBL.\n"); + status = -ENOMEM; + goto mem_alloc_failure; + } + memset(qedf->global_queues[i]->cq_pbl, 0, + qedf->global_queues[i]->cq_pbl_size); + + /* Create PBL */ + num_pages = qedf->global_queues[i]->cq_mem_size / + QEDF_PAGE_SIZE; + page = qedf->global_queues[i]->cq_dma; + pbl = (u32 *)qedf->global_queues[i]->cq_pbl; + + while (num_pages--) { + *pbl = U64_LO(page); + pbl++; + *pbl = U64_HI(page); + pbl++; + page += QEDF_PAGE_SIZE; + } + /* Set the initial consumer index for cq */ + qedf->global_queues[i]->cq_cons_idx = 0; + } + + list = (u32 *)qedf->p_cpuq; + + /* + * The list is built as follows: CQ#0 PBL pointer, RQ#0 PBL pointer, + * CQ#1 PBL pointer, RQ#1 PBL pointer, etc. Each PBL pointer points + * to the physical address which contains an array of pointers to + * the physical addresses of the specific queue pages. + */ + for (i = 0; i < qedf->num_queues; i++) { + *list = U64_LO(qedf->global_queues[i]->cq_pbl_dma); + list++; + *list = U64_HI(qedf->global_queues[i]->cq_pbl_dma); + list++; + *list = U64_LO(0); + list++; + *list = U64_HI(0); + list++; + } + + return 0; + +mem_alloc_failure: + qedf_free_global_queues(qedf); + return status; +} + +static int qedf_set_fcoe_pf_param(struct qedf_ctx *qedf) +{ + u8 sq_num_pbl_pages; + u32 sq_mem_size; + u32 cq_mem_size; + u32 cq_num_entries; + int rval; + + /* + * The number of completion queues/fastpath interrupts/status blocks + * we allocation is the minimum off: + * + * Number of CPUs + * Number of MSI-X vectors + * Max number allocated in hardware (QEDF_MAX_NUM_CQS) + */ + qedf->num_queues = min((unsigned int)QEDF_MAX_NUM_CQS, + num_online_cpus()); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of CQs is %d.\n", + qedf->num_queues); + + qedf->p_cpuq = pci_alloc_consistent(qedf->pdev, + qedf->num_queues * sizeof(struct qedf_glbl_q_params), + &qedf->hw_p_cpuq); + + if (!qedf->p_cpuq) { + QEDF_ERR(&(qedf->dbg_ctx), "pci_alloc_consistent failed.\n"); + return 1; + } + + rval = qedf_alloc_global_queues(qedf); + if (rval) { + QEDF_ERR(&(qedf->dbg_ctx), "Global queue allocation " + "failed.\n"); + return 1; + } + + /* Calculate SQ PBL size in the same manner as in qedf_sq_alloc() */ + sq_mem_size = SQ_NUM_ENTRIES * sizeof(struct fcoe_wqe); + sq_mem_size = ALIGN(sq_mem_size, QEDF_PAGE_SIZE); + sq_num_pbl_pages = (sq_mem_size / QEDF_PAGE_SIZE); + + /* Calculate CQ num entries */ + cq_mem_size = FCOE_PARAMS_CQ_NUM_ENTRIES * sizeof(struct fcoe_cqe); + cq_mem_size = ALIGN(cq_mem_size, QEDF_PAGE_SIZE); + cq_num_entries = cq_mem_size / sizeof(struct fcoe_cqe); + + memset(&(qedf->pf_params), 0, + sizeof(qedf->pf_params)); + + /* Setup the value for fcoe PF */ + qedf->pf_params.fcoe_pf_params.num_cons = QEDF_MAX_SESSIONS; + qedf->pf_params.fcoe_pf_params.num_tasks = FCOE_PARAMS_NUM_TASKS; + qedf->pf_params.fcoe_pf_params.glbl_q_params_addr = + (u64)qedf->hw_p_cpuq; + qedf->pf_params.fcoe_pf_params.sq_num_pbl_pages = sq_num_pbl_pages; + + qedf->pf_params.fcoe_pf_params.rq_buffer_log_size = 0; + + qedf->pf_params.fcoe_pf_params.cq_num_entries = cq_num_entries; + qedf->pf_params.fcoe_pf_params.num_cqs = qedf->num_queues; + + /* log_page_size: 12 for 4KB pages */ + qedf->pf_params.fcoe_pf_params.log_page_size = ilog2(QEDF_PAGE_SIZE); + + qedf->pf_params.fcoe_pf_params.mtu = 9000; + qedf->pf_params.fcoe_pf_params.gl_rq_pi = QEDF_FCOE_PARAMS_GL_RQ_PI; + qedf->pf_params.fcoe_pf_params.gl_cmd_pi = QEDF_FCOE_PARAMS_GL_CMD_PI; + + /* BDQ address and size */ + qedf->pf_params.fcoe_pf_params.bdq_pbl_base_addr[0] = + qedf->bdq_pbl_list_dma; + qedf->pf_params.fcoe_pf_params.bdq_pbl_num_entries[0] = + qedf->bdq_pbl_list_num_entries; + qedf->pf_params.fcoe_pf_params.rq_buffer_size = QEDF_BDQ_BUF_SIZE; + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "bdq_list=%p bdq_pbl_list_dma=%llx bdq_pbl_list_entries=%d.\n", + qedf->bdq_pbl_list, + qedf->pf_params.fcoe_pf_params.bdq_pbl_base_addr[0], + qedf->pf_params.fcoe_pf_params.bdq_pbl_num_entries[0]); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "cq_num_entries=%d.\n", + qedf->pf_params.fcoe_pf_params.cq_num_entries); + + return 0; +} + +/* Free DMA coherent memory for array of queue pointers we pass to qed */ +static void qedf_free_fcoe_pf_param(struct qedf_ctx *qedf) +{ + size_t size = 0; + + if (qedf->p_cpuq) { + size = qedf->num_queues * sizeof(struct qedf_glbl_q_params); + pci_free_consistent(qedf->pdev, size, qedf->p_cpuq, + qedf->hw_p_cpuq); + } + + qedf_free_global_queues(qedf); + + if (qedf->global_queues) + kfree(qedf->global_queues); +} + +/* + * PCI driver functions + */ + +static const struct pci_device_id qedf_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x165c) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x8080) }, + {0} +}; +MODULE_DEVICE_TABLE(pci, qedf_pci_tbl); + +static struct pci_driver qedf_pci_driver = { + .name = QEDF_MODULE_NAME, + .id_table = qedf_pci_tbl, + .probe = qedf_probe, + .remove = qedf_remove, +}; + +static int __qedf_probe(struct pci_dev *pdev, int mode) +{ + int rc = -EINVAL; + struct fc_lport *lport; + struct qedf_ctx *qedf; + struct Scsi_Host *host; + bool is_vf = false; + struct qed_ll2_params params; + char host_buf[20]; + struct qed_link_params link_params; + int status; + void *task_start, *task_end; + struct qed_slowpath_params slowpath_params; + struct qed_probe_params qed_params; + u16 tmp; + + /* + * When doing error recovery we didn't reap the lport so don't try + * to reallocate it. + */ + if (mode != QEDF_MODE_RECOVERY) { + lport = libfc_host_alloc(&qedf_host_template, + sizeof(struct qedf_ctx)); + + if (!lport) { + QEDF_ERR(NULL, "Could not allocate lport.\n"); + rc = -ENOMEM; + goto err0; + } + + /* Initialize qedf_ctx */ + qedf = lport_priv(lport); + qedf->lport = lport; + qedf->ctlr.lp = lport; + qedf->pdev = pdev; + qedf->dbg_ctx.pdev = pdev; + qedf->dbg_ctx.host_no = lport->host->host_no; + spin_lock_init(&qedf->hba_lock); + INIT_LIST_HEAD(&qedf->fcports); + qedf->curr_conn_id = QEDF_MAX_SESSIONS - 1; + atomic_set(&qedf->num_offloads, 0); + qedf->stop_io_on_error = false; + pci_set_drvdata(pdev, qedf); + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, + "QLogic FastLinQ FCoE Module qedf %s, " + "FW %d.%d.%d.%d\n", QEDF_VERSION, + FW_MAJOR_VERSION, FW_MINOR_VERSION, FW_REVISION_VERSION, + FW_ENGINEERING_VERSION); + } else { + /* Init pointers during recovery */ + qedf = pci_get_drvdata(pdev); + lport = qedf->lport; + } + + host = lport->host; + + /* Allocate mempool for qedf_io_work structs */ + qedf->io_mempool = mempool_create_slab_pool(QEDF_IO_WORK_MIN, + qedf_io_work_cache); + if (qedf->io_mempool == NULL) { + QEDF_ERR(&(qedf->dbg_ctx), "qedf->io_mempool is NULL.\n"); + goto err1; + } + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, "qedf->io_mempool=%p.\n", + qedf->io_mempool); + + sprintf(host_buf, "qedf_%u_link", + qedf->lport->host->host_no); + qedf->link_update_wq = create_singlethread_workqueue(host_buf); + INIT_DELAYED_WORK(&qedf->link_update, qedf_handle_link_update); + INIT_DELAYED_WORK(&qedf->link_recovery, qedf_link_recovery); + + qedf->fipvlan_retries = qedf_fipvlan_retries; + + /* + * Common probe. Takes care of basic hardware init and pci_* + * functions. + */ + memset(&qed_params, 0, sizeof(qed_params)); + qed_params.protocol = QED_PROTOCOL_FCOE; + qed_params.dp_module = qedf_dp_module; + qed_params.dp_level = qedf_dp_level; + qed_params.is_vf = is_vf; + qedf->cdev = qed_ops->common->probe(pdev, &qed_params); + if (!qedf->cdev) { + rc = -ENODEV; + goto err1; + } + + /* queue allocation code should come here + * order should be + * slowpath_start + * status block allocation + * interrupt registration (to get min number of queues) + * set_fcoe_pf_param + * qed_sp_fcoe_func_start + */ + rc = qedf_set_fcoe_pf_param(qedf); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "Cannot set fcoe pf param.\n"); + goto err2; + } + qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params); + + /* Learn information crucial for qedf to progress */ + rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed to dev info.\n"); + goto err1; + } + + /* Record BDQ producer doorbell addresses */ + qedf->bdq_primary_prod = qedf->dev_info.primary_dbq_rq_addr; + qedf->bdq_secondary_prod = qedf->dev_info.secondary_bdq_rq_addr; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "BDQ primary_prod=%p secondary_prod=%p.\n", qedf->bdq_primary_prod, + qedf->bdq_secondary_prod); + + qed_ops->register_ops(qedf->cdev, &qedf_cb_ops, qedf); + + rc = qedf_prepare_sb(qedf); + if (rc) { + + QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n"); + goto err2; + } + + /* Start the Slowpath-process */ + slowpath_params.int_mode = QED_INT_MODE_MSIX; + slowpath_params.drv_major = QEDF_DRIVER_MAJOR_VER; + slowpath_params.drv_minor = QEDF_DRIVER_MINOR_VER; + slowpath_params.drv_rev = QEDF_DRIVER_REV_VER; + slowpath_params.drv_eng = QEDF_DRIVER_ENG_VER; + memcpy(slowpath_params.name, "qedf", QED_DRV_VER_STR_SIZE); + rc = qed_ops->common->slowpath_start(qedf->cdev, &slowpath_params); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n"); + goto err2; + } + + /* + * update_pf_params needs to be called before and after slowpath + * start + */ + qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params); + + /* Setup interrupts */ + rc = qedf_setup_int(qedf); + if (rc) + goto err3; + + rc = qed_ops->start(qedf->cdev, &qedf->tasks); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "Cannot start FCoE function.\n"); + goto err4; + } + task_start = qedf_get_task_mem(&qedf->tasks, 0); + task_end = qedf_get_task_mem(&qedf->tasks, MAX_TID_BLOCKS_FCOE - 1); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Task context start=%p, " + "end=%p block_size=%u.\n", task_start, task_end, + qedf->tasks.size); + + /* + * We need to write the number of BDs in the BDQ we've preallocated so + * the f/w will do a prefetch and we'll get an unsolicited CQE when a + * packet arrives. + */ + qedf->bdq_prod_idx = QEDF_BDQ_SIZE; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Writing %d to primary and secondary BDQ doorbell registers.\n", + qedf->bdq_prod_idx); + writew(qedf->bdq_prod_idx, qedf->bdq_primary_prod); + tmp = readw(qedf->bdq_primary_prod); + writew(qedf->bdq_prod_idx, qedf->bdq_secondary_prod); + tmp = readw(qedf->bdq_secondary_prod); + + qed_ops->common->set_power_state(qedf->cdev, PCI_D0); + + /* Now that the dev_info struct has been filled in set the MAC + * address + */ + ether_addr_copy(qedf->mac, qedf->dev_info.common.hw_mac); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "MAC address is %pM.\n", + qedf->mac); + + /* Set the WWNN and WWPN based on the MAC address */ + qedf->wwnn = fcoe_wwn_from_mac(qedf->mac, 1, 0); + qedf->wwpn = fcoe_wwn_from_mac(qedf->mac, 2, 0); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "WWNN=%016llx " + "WWPN=%016llx.\n", qedf->wwnn, qedf->wwpn); + + sprintf(host_buf, "host_%d", host->host_no); + qed_ops->common->set_id(qedf->cdev, host_buf, QEDF_VERSION); + + + /* Set xid max values */ + qedf->max_scsi_xid = QEDF_MAX_SCSI_XID; + qedf->max_els_xid = QEDF_MAX_ELS_XID; + + /* Allocate cmd mgr */ + qedf->cmd_mgr = qedf_cmd_mgr_alloc(qedf); + if (!qedf->cmd_mgr) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed to allocate cmd mgr.\n"); + goto err5; + } + + if (mode != QEDF_MODE_RECOVERY) { + host->transportt = qedf_fc_transport_template; + host->can_queue = QEDF_MAX_ELS_XID; + host->max_lun = qedf_max_lun; + host->max_cmd_len = QEDF_MAX_CDB_LEN; + rc = scsi_add_host(host, &pdev->dev); + if (rc) + goto err6; + } + + memset(¶ms, 0, sizeof(params)); + params.mtu = 9000; + ether_addr_copy(params.ll2_mac_address, qedf->mac); + + /* Start LL2 processing thread */ + snprintf(host_buf, 20, "qedf_%d_ll2", host->host_no); + qedf->ll2_recv_wq = + create_singlethread_workqueue(host_buf); + if (!qedf->ll2_recv_wq) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed to LL2 workqueue.\n"); + goto err7; + } + +#ifdef CONFIG_DEBUG_FS + qedf_dbg_host_init(&(qedf->dbg_ctx), &qedf_debugfs_ops, + &qedf_dbg_fops); +#endif + + /* Start LL2 */ + qed_ops->ll2->register_cb_ops(qedf->cdev, &qedf_ll2_cb_ops, qedf); + rc = qed_ops->ll2->start(qedf->cdev, ¶ms); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "Could not start Light L2.\n"); + goto err7; + } + set_bit(QEDF_LL2_STARTED, &qedf->flags); + + /* hw will be insterting vlan tag*/ + qedf->vlan_hw_insert = 1; + qedf->vlan_id = 0; + + /* + * No need to setup fcoe_ctlr or fc_lport objects during recovery since + * they were not reaped during the unload process. + */ + if (mode != QEDF_MODE_RECOVERY) { + /* Setup imbedded fcoe controller */ + qedf_fcoe_ctlr_setup(qedf); + + /* Setup lport */ + rc = qedf_lport_setup(qedf); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), + "qedf_lport_setup failed.\n"); + goto err7; + } + } + + sprintf(host_buf, "qedf_%u_timer", qedf->lport->host->host_no); + qedf->timer_work_queue = + create_singlethread_workqueue(host_buf); + if (!qedf->timer_work_queue) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed to start timer " + "workqueue.\n"); + goto err7; + } + + /* DPC workqueue is not reaped during recovery unload */ + if (mode != QEDF_MODE_RECOVERY) { + sprintf(host_buf, "qedf_%u_dpc", + qedf->lport->host->host_no); + qedf->dpc_wq = create_singlethread_workqueue(host_buf); + } + + /* + * GRC dump and sysfs parameters are not reaped during the recovery + * unload process. + */ + if (mode != QEDF_MODE_RECOVERY) { + qedf->grcdump_size = qed_ops->common->dbg_grc_size(qedf->cdev); + if (qedf->grcdump_size) { + rc = qedf_alloc_grc_dump_buf(&qedf->grcdump, + qedf->grcdump_size); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), + "GRC Dump buffer alloc failed.\n"); + qedf->grcdump = NULL; + } + + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "grcdump: addr=%p, size=%u.\n", + qedf->grcdump, qedf->grcdump_size); + } + qedf_create_sysfs_ctx_attr(qedf); + + /* Initialize I/O tracing for this adapter */ + spin_lock_init(&qedf->io_trace_lock); + qedf->io_trace_idx = 0; + } + + init_completion(&qedf->flogi_compl); + + memset(&link_params, 0, sizeof(struct qed_link_params)); + link_params.link_up = true; + status = qed_ops->common->set_link(qedf->cdev, &link_params); + if (status) + QEDF_WARN(&(qedf->dbg_ctx), "set_link failed.\n"); + + /* Start/restart discovery */ + if (mode == QEDF_MODE_RECOVERY) + fcoe_ctlr_link_up(&qedf->ctlr); + else + fc_fabric_login(lport); + + /* All good */ + return 0; + +err7: + if (qedf->ll2_recv_wq) + destroy_workqueue(qedf->ll2_recv_wq); + fc_remove_host(qedf->lport->host); + scsi_remove_host(qedf->lport->host); +#ifdef CONFIG_DEBUG_FS + qedf_dbg_host_exit(&(qedf->dbg_ctx)); +#endif +err6: + qedf_cmd_mgr_free(qedf->cmd_mgr); +err5: + qed_ops->stop(qedf->cdev); +err4: + qedf_free_fcoe_pf_param(qedf); + qedf_sync_free_irqs(qedf); +err3: + qed_ops->common->slowpath_stop(qedf->cdev); +err2: + qed_ops->common->remove(qedf->cdev); +err1: + scsi_host_put(lport->host); +err0: + return rc; +} + +static int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + return __qedf_probe(pdev, QEDF_MODE_NORMAL); +} + +static void __qedf_remove(struct pci_dev *pdev, int mode) +{ + struct qedf_ctx *qedf; + + if (!pdev) { + QEDF_ERR(NULL, "pdev is NULL.\n"); + return; + } + + qedf = pci_get_drvdata(pdev); + + /* + * Prevent race where we're in board disable work and then try to + * rmmod the module. + */ + if (test_bit(QEDF_UNLOADING, &qedf->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "Already removing PCI function.\n"); + return; + } + + if (mode != QEDF_MODE_RECOVERY) + set_bit(QEDF_UNLOADING, &qedf->flags); + + /* Logoff the fabric to upload all connections */ + if (mode == QEDF_MODE_RECOVERY) + fcoe_ctlr_link_down(&qedf->ctlr); + else + fc_fabric_logoff(qedf->lport); + qedf_wait_for_upload(qedf); + +#ifdef CONFIG_DEBUG_FS + qedf_dbg_host_exit(&(qedf->dbg_ctx)); +#endif + + /* Stop any link update handling */ + cancel_delayed_work_sync(&qedf->link_update); + destroy_workqueue(qedf->link_update_wq); + qedf->link_update_wq = NULL; + + if (qedf->timer_work_queue) + destroy_workqueue(qedf->timer_work_queue); + + /* Stop Light L2 */ + clear_bit(QEDF_LL2_STARTED, &qedf->flags); + qed_ops->ll2->stop(qedf->cdev); + if (qedf->ll2_recv_wq) + destroy_workqueue(qedf->ll2_recv_wq); + + /* Stop fastpath */ + qedf_sync_free_irqs(qedf); + qedf_destroy_sb(qedf); + + /* + * During recovery don't destroy OS constructs that represent the + * physical port. + */ + if (mode != QEDF_MODE_RECOVERY) { + qedf_free_grc_dump_buf(&qedf->grcdump); + qedf_remove_sysfs_ctx_attr(qedf); + + /* Remove all SCSI/libfc/libfcoe structures */ + fcoe_ctlr_destroy(&qedf->ctlr); + fc_lport_destroy(qedf->lport); + fc_remove_host(qedf->lport->host); + scsi_remove_host(qedf->lport->host); + } + + qedf_cmd_mgr_free(qedf->cmd_mgr); + + if (mode != QEDF_MODE_RECOVERY) { + fc_exch_mgr_free(qedf->lport); + fc_lport_free_stats(qedf->lport); + + /* Wait for all vports to be reaped */ + qedf_wait_for_vport_destroy(qedf); + } + + /* + * Now that all connections have been uploaded we can stop the + * rest of the qed operations + */ + qed_ops->stop(qedf->cdev); + + if (mode != QEDF_MODE_RECOVERY) { + if (qedf->dpc_wq) { + /* Stop general DPC handling */ + destroy_workqueue(qedf->dpc_wq); + qedf->dpc_wq = NULL; + } + } + + /* Final shutdown for the board */ + qedf_free_fcoe_pf_param(qedf); + if (mode != QEDF_MODE_RECOVERY) { + qed_ops->common->set_power_state(qedf->cdev, PCI_D0); + pci_set_drvdata(pdev, NULL); + } + qed_ops->common->slowpath_stop(qedf->cdev); + qed_ops->common->remove(qedf->cdev); + + mempool_destroy(qedf->io_mempool); + + /* Only reap the Scsi_host on a real removal */ + if (mode != QEDF_MODE_RECOVERY) + scsi_host_put(qedf->lport->host); +} + +static void qedf_remove(struct pci_dev *pdev) +{ + /* Check to make sure this function wasn't already disabled */ + if (!atomic_read(&pdev->enable_cnt)) + return; + + __qedf_remove(pdev, QEDF_MODE_NORMAL); +} + +/* + * Module Init/Remove + */ + +static int __init qedf_init(void) +{ + int ret; + + /* If debug=1 passed, set the default log mask */ + if (qedf_debug == QEDF_LOG_DEFAULT) + qedf_debug = QEDF_DEFAULT_LOG_MASK; + + /* Print driver banner */ + QEDF_INFO(NULL, QEDF_LOG_INFO, "%s v%s.\n", QEDF_DESCR, + QEDF_VERSION); + + /* Create kmem_cache for qedf_io_work structs */ + qedf_io_work_cache = kmem_cache_create("qedf_io_work_cache", + sizeof(struct qedf_io_work), 0, SLAB_HWCACHE_ALIGN, NULL); + if (qedf_io_work_cache == NULL) { + QEDF_ERR(NULL, "qedf_io_work_cache is NULL.\n"); + goto err1; + } + QEDF_INFO(NULL, QEDF_LOG_DISC, "qedf_io_work_cache=%p.\n", + qedf_io_work_cache); + + qed_ops = qed_get_fcoe_ops(); + if (!qed_ops) { + QEDF_ERR(NULL, "Failed to get qed fcoe operations\n"); + goto err1; + } + +#ifdef CONFIG_DEBUG_FS + qedf_dbg_init("qedf"); +#endif + + qedf_fc_transport_template = + fc_attach_transport(&qedf_fc_transport_fn); + if (!qedf_fc_transport_template) { + QEDF_ERR(NULL, "Could not register with FC transport\n"); + goto err2; + } + + qedf_fc_vport_transport_template = + fc_attach_transport(&qedf_fc_vport_transport_fn); + if (!qedf_fc_vport_transport_template) { + QEDF_ERR(NULL, "Could not register vport template with FC " + "transport\n"); + goto err3; + } + + qedf_io_wq = create_workqueue("qedf_io_wq"); + if (!qedf_io_wq) { + QEDF_ERR(NULL, "Could not create qedf_io_wq.\n"); + goto err4; + } + + qedf_cb_ops.get_login_failures = qedf_get_login_failures; + + ret = pci_register_driver(&qedf_pci_driver); + if (ret) { + QEDF_ERR(NULL, "Failed to register driver\n"); + goto err5; + } + + return 0; + +err5: + destroy_workqueue(qedf_io_wq); +err4: + fc_release_transport(qedf_fc_vport_transport_template); +err3: + fc_release_transport(qedf_fc_transport_template); +err2: +#ifdef CONFIG_DEBUG_FS + qedf_dbg_exit(); +#endif + qed_put_fcoe_ops(); +err1: + return -EINVAL; +} + +static void __exit qedf_cleanup(void) +{ + pci_unregister_driver(&qedf_pci_driver); + + destroy_workqueue(qedf_io_wq); + + fc_release_transport(qedf_fc_vport_transport_template); + fc_release_transport(qedf_fc_transport_template); +#ifdef CONFIG_DEBUG_FS + qedf_dbg_exit(); +#endif + qed_put_fcoe_ops(); + + kmem_cache_destroy(qedf_io_work_cache); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("QLogic QEDF 25/40/50/100Gb FCoE Driver"); +MODULE_AUTHOR("QLogic Corporation"); +MODULE_VERSION(QEDF_VERSION); +module_init(qedf_init); +module_exit(qedf_cleanup); diff --git a/drivers/scsi/qedf/qedf_version.h b/drivers/scsi/qedf/qedf_version.h new file mode 100644 index 000000000000..4ae5f537a440 --- /dev/null +++ b/drivers/scsi/qedf/qedf_version.h @@ -0,0 +1,15 @@ +/* + * QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#define QEDF_VERSION "8.10.7.0" +#define QEDF_DRIVER_MAJOR_VER 8 +#define QEDF_DRIVER_MINOR_VER 10 +#define QEDF_DRIVER_REV_VER 7 +#define QEDF_DRIVER_ENG_VER 0 + diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c index b1d3904ae8fd..c9f0ef4e11b3 100644 --- a/drivers/scsi/qedi/qedi_fw.c +++ b/drivers/scsi/qedi/qedi_fw.c @@ -165,10 +165,9 @@ static void qedi_tmf_resp_work(struct work_struct *work) iscsi_block_session(session->cls_session); rval = qedi_cleanup_all_io(qedi, qedi_conn, qedi_cmd->task, true); if (rval) { - clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); qedi_clear_task_idx(qedi, qedi_cmd->task_id); iscsi_unblock_session(session->cls_session); - return; + goto exit_tmf_resp; } iscsi_unblock_session(session->cls_session); @@ -177,6 +176,8 @@ static void qedi_tmf_resp_work(struct work_struct *work) spin_lock(&session->back_lock); __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, NULL, 0); spin_unlock(&session->back_lock); + +exit_tmf_resp: kfree(resp_hdr_ptr); clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); } diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index f201f4099620..f610103994af 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -2163,6 +2163,9 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) clear_bit(vha->vp_idx, ha->vp_idx_map); mutex_unlock(&ha->vport_lock); + dma_free_coherent(&ha->pdev->dev, vha->gnl.size, vha->gnl.l, + vha->gnl.ldma); + if (vha->qpair->vp_idx == vha->vp_idx) { if (qla2xxx_delete_qpair(vha, vha->qpair) != QLA_SUCCESS) ql_log(ql_log_warn, vha, 0x7087, diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index 40ca75bbcb9d..84c9098cc089 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -13,28 +13,25 @@ /* BSG support for ELS/CT pass through */ void -qla2x00_bsg_job_done(void *data, void *ptr, int res) +qla2x00_bsg_job_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; - struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + srb_t *sp = ptr; struct bsg_job *bsg_job = sp->u.bsg_job; struct fc_bsg_reply *bsg_reply = bsg_job->reply; bsg_reply->result = res; bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len); - sp->free(vha, sp); + sp->free(sp); } void -qla2x00_bsg_sp_free(void *data, void *ptr) +qla2x00_bsg_sp_free(void *ptr) { - srb_t *sp = (srb_t *)ptr; - struct scsi_qla_host *vha = sp->fcport->vha; + srb_t *sp = ptr; + struct qla_hw_data *ha = sp->vha->hw; struct bsg_job *bsg_job = sp->u.bsg_job; struct fc_bsg_request *bsg_request = bsg_job->request; - - struct qla_hw_data *ha = vha->hw; struct qla_mt_iocb_rqst_fx00 *piocb_rqst; if (sp->type == SRB_FXIOCB_BCMD) { @@ -62,7 +59,7 @@ qla2x00_bsg_sp_free(void *data, void *ptr) sp->type == SRB_FXIOCB_BCMD || sp->type == SRB_ELS_CMD_HST) kfree(sp->fcport); - qla2x00_rel_sp(vha, sp); + qla2x00_rel_sp(sp); } int @@ -394,7 +391,7 @@ qla2x00_process_els(struct bsg_job *bsg_job) if (rval != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x700e, "qla2x00_start_sp failed = %d\n", rval); - qla2x00_rel_sp(vha, sp); + qla2x00_rel_sp(sp); rval = -EIO; goto done_unmap_sg; } @@ -542,7 +539,7 @@ qla2x00_process_ct(struct bsg_job *bsg_job) if (rval != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x7017, "qla2x00_start_sp failed=%d.\n", rval); - qla2x00_rel_sp(vha, sp); + qla2x00_rel_sp(sp); rval = -EIO; goto done_free_fcport; } @@ -2578,6 +2575,6 @@ qla24xx_bsg_timeout(struct bsg_job *bsg_job) done: spin_unlock_irqrestore(&ha->hardware_lock, flags); - sp->free(vha, sp); + sp->free(sp); return 0; } diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 2f14adfab018..625d438e3cce 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -55,6 +55,8 @@ #include "qla_settings.h" +#define MODE_DUAL (MODE_TARGET | MODE_INITIATOR) + /* * Data bit definitions */ @@ -251,6 +253,14 @@ #define MAX_CMDSZ 16 /* SCSI maximum CDB size. */ #include "qla_fw.h" + +struct name_list_extended { + struct get_name_list_extended *l; + dma_addr_t ldma; + struct list_head fcports; /* protect by sess_list */ + u32 size; + u8 sent; +}; /* * Timeout timer counts in seconds */ @@ -309,6 +319,17 @@ struct els_logo_payload { uint8_t wwpn[WWN_SIZE]; }; +struct ct_arg { + void *iocb; + u16 nport_handle; + dma_addr_t req_dma; + dma_addr_t rsp_dma; + u32 req_size; + u32 rsp_size; + void *req; + void *rsp; +}; + /* * SRB extensions. */ @@ -320,6 +341,7 @@ struct srb_iocb { #define SRB_LOGIN_COND_PLOGI BIT_1 #define SRB_LOGIN_SKIP_PRLI BIT_2 uint16_t data[2]; + u32 iop[2]; } logio; struct { #define ELS_DCMD_TIMEOUT 20 @@ -372,6 +394,16 @@ struct srb_iocb { __le16 comp_status; struct completion comp; } abt; + struct ct_arg ctarg; + struct { + __le16 in_mb[28]; /* fr fw */ + __le16 out_mb[28]; /* to fw */ + void *out, *in; + dma_addr_t out_dma, in_dma; + } mbx; + struct { + struct imm_ntfy_from_isp *ntfy; + } nack; } u; struct timer_list timer; @@ -392,23 +424,31 @@ struct srb_iocb { #define SRB_FXIOCB_BCMD 11 #define SRB_ABT_CMD 12 #define SRB_ELS_DCMD 13 +#define SRB_MB_IOCB 14 +#define SRB_CT_PTHRU_CMD 15 +#define SRB_NACK_PLOGI 16 +#define SRB_NACK_PRLI 17 +#define SRB_NACK_LOGO 18 typedef struct srb { atomic_t ref_count; struct fc_port *fcport; + struct scsi_qla_host *vha; uint32_t handle; uint16_t flags; uint16_t type; char *name; int iocbs; struct qla_qpair *qpair; + u32 gen1; /* scratch */ + u32 gen2; /* scratch */ union { struct srb_iocb iocb_cmd; struct bsg_job *bsg_job; struct srb_cmd scmd; } u; - void (*done)(void *, void *, int); - void (*free)(void *, void *); + void (*done)(void *, int); + void (*free)(void *); } srb_t; #define GET_CMD_SP(sp) (sp->u.scmd.cmd) @@ -1794,6 +1834,7 @@ typedef struct { #define SS_RESIDUAL_OVER BIT_10 #define SS_SENSE_LEN_VALID BIT_9 #define SS_RESPONSE_INFO_LEN_VALID BIT_8 +#define SS_SCSI_STATUS_BYTE 0xff #define SS_RESERVE_CONFLICT (BIT_4 | BIT_3) #define SS_BUSY_CONDITION BIT_3 @@ -1975,6 +2016,84 @@ struct mbx_entry { uint8_t port_name[WWN_SIZE]; }; +#ifndef IMMED_NOTIFY_TYPE +#define IMMED_NOTIFY_TYPE 0x0D /* Immediate notify entry. */ +/* + * ISP queue - immediate notify entry structure definition. + * This is sent by the ISP to the Target driver. + * This IOCB would have report of events sent by the + * initiator, that needs to be handled by the target + * driver immediately. + */ +struct imm_ntfy_from_isp { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + union { + struct { + uint32_t sys_define_2; /* System defined. */ + target_id_t target; + uint16_t lun; + uint8_t target_id; + uint8_t reserved_1; + uint16_t status_modifier; + uint16_t status; + uint16_t task_flags; + uint16_t seq_id; + uint16_t srr_rx_id; + uint32_t srr_rel_offs; + uint16_t srr_ui; +#define SRR_IU_DATA_IN 0x1 +#define SRR_IU_DATA_OUT 0x5 +#define SRR_IU_STATUS 0x7 + uint16_t srr_ox_id; + uint8_t reserved_2[28]; + } isp2x; + struct { + uint32_t reserved; + uint16_t nport_handle; + uint16_t reserved_2; + uint16_t flags; +#define NOTIFY24XX_FLAGS_GLOBAL_TPRLO BIT_1 +#define NOTIFY24XX_FLAGS_PUREX_IOCB BIT_0 + uint16_t srr_rx_id; + uint16_t status; + uint8_t status_subcode; + uint8_t fw_handle; + uint32_t exchange_address; + uint32_t srr_rel_offs; + uint16_t srr_ui; + uint16_t srr_ox_id; + union { + struct { + uint8_t node_name[8]; + } plogi; /* PLOGI/ADISC/PDISC */ + struct { + /* PRLI word 3 bit 0-15 */ + uint16_t wd3_lo; + uint8_t resv0[6]; + } prli; + struct { + uint8_t port_id[3]; + uint8_t resv1; + uint16_t nport_handle; + uint16_t resv2; + } req_els; + } u; + uint8_t port_name[8]; + uint8_t resv3[3]; + uint8_t vp_index; + uint32_t reserved_5; + uint8_t port_id[3]; + uint8_t reserved_6; + } isp24; + } u; + uint16_t reserved_7; + uint16_t ox_id; +} __packed; +#endif + /* * ISP request and response queue entry sizes */ @@ -2022,10 +2141,22 @@ typedef struct { #define FC4_TYPE_OTHER 0x0 #define FC4_TYPE_UNKNOWN 0xff +/* mailbox command 4G & above */ +struct mbx_24xx_entry { + uint8_t entry_type; + uint8_t entry_count; + uint8_t sys_define1; + uint8_t entry_status; + uint32_t handle; + uint16_t mb[28]; +}; + +#define IOCB_SIZE 64 + /* * Fibre channel port type. */ - typedef enum { +typedef enum { FCT_UNKNOWN, FCT_RSCN, FCT_SWITCH, @@ -2034,6 +2165,74 @@ typedef struct { FCT_TARGET } fc_port_type_t; +enum qla_sess_deletion { + QLA_SESS_DELETION_NONE = 0, + QLA_SESS_DELETION_IN_PROGRESS, + QLA_SESS_DELETED, +}; + +enum qlt_plogi_link_t { + QLT_PLOGI_LINK_SAME_WWN, + QLT_PLOGI_LINK_CONFLICT, + QLT_PLOGI_LINK_MAX +}; + +struct qlt_plogi_ack_t { + struct list_head list; + struct imm_ntfy_from_isp iocb; + port_id_t id; + int ref_count; + void *fcport; +}; + +struct ct_sns_desc { + struct ct_sns_pkt *ct_sns; + dma_addr_t ct_sns_dma; +}; + +enum discovery_state { + DSC_DELETED, + DSC_GID_PN, + DSC_GNL, + DSC_LOGIN_PEND, + DSC_LOGIN_FAILED, + DSC_GPDB, + DSC_GPSC, + DSC_UPD_FCPORT, + DSC_LOGIN_COMPLETE, + DSC_DELETE_PEND, +}; + +enum login_state { /* FW control Target side */ + DSC_LS_LLIOCB_SENT = 2, + DSC_LS_PLOGI_PEND, + DSC_LS_PLOGI_COMP, + DSC_LS_PRLI_PEND, + DSC_LS_PRLI_COMP, + DSC_LS_PORT_UNAVAIL, + DSC_LS_PRLO_PEND = 9, + DSC_LS_LOGO_PEND, +}; + +enum fcport_mgt_event { + FCME_RELOGIN = 1, + FCME_RSCN, + FCME_GIDPN_DONE, + FCME_PLOGI_DONE, /* Initiator side sent LLIOCB */ + FCME_GNL_DONE, + FCME_GPSC_DONE, + FCME_GPDB_DONE, + FCME_GPNID_DONE, + FCME_DELETE_DONE, +}; + +enum rscn_addr_format { + RSCN_PORT_ADDR, + RSCN_AREA_ADDR, + RSCN_DOM_ADDR, + RSCN_FAB_ADDR, +}; + /* * Fibre channel port structure. */ @@ -2047,6 +2246,29 @@ typedef struct fc_port { uint16_t loop_id; uint16_t old_loop_id; + unsigned int conf_compl_supported:1; + unsigned int deleted:2; + unsigned int local:1; + unsigned int logout_on_delete:1; + unsigned int logo_ack_needed:1; + unsigned int keep_nport_handle:1; + unsigned int send_els_logo:1; + unsigned int login_pause:1; + unsigned int login_succ:1; + + struct fc_port *conflict; + unsigned char logout_completed; + int generation; + + struct se_session *se_sess; + struct kref sess_kref; + struct qla_tgt *tgt; + unsigned long expires; + struct list_head del_list_entry; + struct work_struct free_work; + + struct qlt_plogi_ack_t *plogi_link[QLT_PLOGI_LINK_MAX]; + uint16_t tgt_id; uint16_t old_tgt_id; @@ -2075,8 +2297,30 @@ typedef struct fc_port { unsigned long retry_delay_timestamp; struct qla_tgt_sess *tgt_session; + struct ct_sns_desc ct_desc; + enum discovery_state disc_state; + enum login_state fw_login_state; + u32 login_gen, last_login_gen; + u32 rscn_gen, last_rscn_gen; + u32 chip_reset; + struct list_head gnl_entry; + struct work_struct del_work; + u8 iocb[IOCB_SIZE]; } fc_port_t; +#define QLA_FCPORT_SCAN 1 +#define QLA_FCPORT_FOUND 2 + +struct event_arg { + enum fcport_mgt_event event; + fc_port_t *fcport; + srb_t *sp; + port_id_t id; + u16 data[2], rc; + u8 port_name[WWN_SIZE]; + u32 iop[2]; +}; + #include "qla_mr.h" /* @@ -2154,6 +2398,10 @@ static const char * const port_state_str[] = { #define GFT_ID_REQ_SIZE (16 + 4) #define GFT_ID_RSP_SIZE (16 + 32) +#define GID_PN_CMD 0x121 +#define GID_PN_REQ_SIZE (16 + 8) +#define GID_PN_RSP_SIZE (16 + 4) + #define RFT_ID_CMD 0x217 #define RFT_ID_REQ_SIZE (16 + 4 + 32) #define RFT_ID_RSP_SIZE 16 @@ -2479,6 +2727,10 @@ struct ct_sns_req { uint8_t reserved; uint8_t port_name[3]; } gff_id; + + struct { + uint8_t port_name[8]; + } gid_pn; } req; }; @@ -2558,6 +2810,10 @@ struct ct_sns_rsp { struct { uint8_t fc4_features[128]; } gff_id; + struct { + uint8_t reserved; + uint8_t port_id[3]; + } gid_pn; } rsp; }; @@ -2699,11 +2955,11 @@ struct isp_operations { uint16_t (*calc_req_entries) (uint16_t); void (*build_iocbs) (srb_t *, cmd_entry_t *, uint16_t); - void * (*prep_ms_iocb) (struct scsi_qla_host *, uint32_t, uint32_t); - void * (*prep_ms_fdmi_iocb) (struct scsi_qla_host *, uint32_t, + void *(*prep_ms_iocb) (struct scsi_qla_host *, struct ct_arg *); + void *(*prep_ms_fdmi_iocb) (struct scsi_qla_host *, uint32_t, uint32_t); - uint8_t * (*read_nvram) (struct scsi_qla_host *, uint8_t *, + uint8_t *(*read_nvram) (struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); int (*write_nvram) (struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); @@ -2765,13 +3021,21 @@ enum qla_work_type { QLA_EVT_AEN, QLA_EVT_IDC_ACK, QLA_EVT_ASYNC_LOGIN, - QLA_EVT_ASYNC_LOGIN_DONE, QLA_EVT_ASYNC_LOGOUT, QLA_EVT_ASYNC_LOGOUT_DONE, QLA_EVT_ASYNC_ADISC, QLA_EVT_ASYNC_ADISC_DONE, QLA_EVT_UEVENT, QLA_EVT_AENFX, + QLA_EVT_GIDPN, + QLA_EVT_GPNID, + QLA_EVT_GPNID_DONE, + QLA_EVT_NEW_SESS, + QLA_EVT_GPDB, + QLA_EVT_GPSC, + QLA_EVT_UPD_FCPORT, + QLA_EVT_GNL, + QLA_EVT_NACK, }; @@ -2807,6 +3071,23 @@ struct qla_work_evt { struct { srb_t *sp; } iosb; + struct { + port_id_t id; + } gpnid; + struct { + port_id_t id; + u8 port_name[8]; + void *pla; + } new_sess; + struct { /*Get PDB, Get Speed, update fcport, gnl, gidpn */ + fc_port_t *fcport; + u8 opt; + } fcport; + struct { + fc_port_t *fcport; + u8 iocb[IOCB_SIZE]; + int type; + } nack; } u; }; @@ -2943,6 +3224,7 @@ struct qla_qpair { struct qla_hw_data *hw; struct work_struct q_work; struct list_head qp_list_elem; /* vha->qp_list */ + struct scsi_qla_host *vha; }; /* Place holder for FW buffer parameters */ @@ -2963,7 +3245,6 @@ struct qlt_hw_data { /* Protected by hw lock */ uint32_t enable_class_2:1; uint32_t enable_explicit_conf:1; - uint32_t ini_mode_force_reverse:1; uint32_t node_name_set:1; dma_addr_t atio_dma; /* Physical address. */ @@ -3115,6 +3396,7 @@ struct qla_hw_data { #define FLOGI_SP_SUPPORT BIT_13 uint8_t port_no; /* Physical port of adapter */ + uint8_t exch_starvation; /* Timeout timers. */ uint8_t loop_down_abort_time; /* port down timer */ @@ -3682,7 +3964,7 @@ typedef struct scsi_qla_host { #define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */ #define MPI_RESET_NEEDED 19 /* Initiate MPI FW reset */ #define ISP_QUIESCE_NEEDED 20 /* Driver need some quiescence */ -#define SCR_PENDING 21 /* SCR in target mode */ +#define FREE_BIT 21 #define PORT_UPDATE_NEEDED 22 #define FX00_RESET_RECOVERY 23 #define FX00_TARGET_SCAN 24 @@ -3736,7 +4018,9 @@ typedef struct scsi_qla_host { /* list of commands waiting on workqueue */ struct list_head qla_cmd_list; struct list_head qla_sess_op_cmd_list; + struct list_head unknown_atio_list; spinlock_t cmd_list_lock; + struct delayed_work unknown_atio_work; /* Counter to detect races between ELS and RSCN events */ atomic_t generation_tick; @@ -3788,6 +4072,10 @@ typedef struct scsi_qla_host { struct qla8044_reset_template reset_tmplt; struct qla_tgt_counters tgt_counters; uint16_t bbcr; + struct name_list_extended gnl; + /* Count of active session/fcport */ + int fcport_count; + wait_queue_head_t fcport_waitQ; } scsi_qla_host_t; struct qla27xx_image_status { diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 34272fde8a5b..b48cce696bac 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -18,7 +18,7 @@ qla2x00_dfs_tgt_sess_show(struct seq_file *s, void *unused) scsi_qla_host_t *vha = s->private; struct qla_hw_data *ha = vha->hw; unsigned long flags; - struct qla_tgt_sess *sess = NULL; + struct fc_port *sess = NULL; struct qla_tgt *tgt= vha->vha_tgt.qla_tgt; seq_printf(s, "%s\n",vha->host_str); @@ -26,12 +26,11 @@ qla2x00_dfs_tgt_sess_show(struct seq_file *s, void *unused) seq_printf(s, "Port ID Port Name Handle\n"); spin_lock_irqsave(&ha->tgt.sess_lock, flags); - list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + list_for_each_entry(sess, &vha->vp_fcports, list) seq_printf(s, "%02x:%02x:%02x %8phC %d\n", - sess->s_id.b.domain,sess->s_id.b.area, - sess->s_id.b.al_pa, sess->port_name, - sess->loop_id); - } + sess->d_id.b.domain, sess->d_id.b.area, + sess->d_id.b.al_pa, sess->port_name, + sess->loop_id); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); } diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 8a2368b32dec..1f808928763b 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -72,6 +72,37 @@ struct port_database_24xx { uint8_t reserved_3[24]; }; +/* + * MB 75h returns a list of DB entries similar to port_database_24xx(64B). + * However, in this case it returns 1st 40 bytes. + */ +struct get_name_list_extended { + __le16 flags; + u8 current_login_state; + u8 last_login_state; + u8 hard_address[3]; + u8 reserved_1; + u8 port_id[3]; + u8 sequence_id; + __le16 port_timer; + __le16 nport_handle; /* N_PORT handle. */ + __le16 receive_data_size; + __le16 reserved_2; + + /* PRLI SVC Param are Big endian */ + u8 prli_svc_param_word_0[2]; /* Bits 15-0 of word 0 */ + u8 prli_svc_param_word_3[2]; /* Bits 15-0 of word 3 */ + u8 port_name[WWN_SIZE]; + u8 node_name[WWN_SIZE]; +}; + +/* MB 75h: This is the short version of the database */ +struct get_name_list { + u8 port_node_name[WWN_SIZE]; /* B7 most sig, B0 least sig */ + __le16 nport_handle; + u8 reserved; +}; + struct vp_database_24xx { uint16_t vp_status; uint8_t options; @@ -1270,27 +1301,76 @@ struct vp_config_entry_24xx { }; #define VP_RPT_ID_IOCB_TYPE 0x32 /* Report ID Acquisition entry. */ +enum VP_STATUS { + VP_STAT_COMPL, + VP_STAT_FAIL, + VP_STAT_ID_CHG, + VP_STAT_SNS_TO, /* timeout */ + VP_STAT_SNS_RJT, + VP_STAT_SCR_TO, /* timeout */ + VP_STAT_SCR_RJT, +}; + +enum VP_FLAGS { + VP_FLAGS_CON_FLOOP = 1, + VP_FLAGS_CON_P2P = 2, + VP_FLAGS_CON_FABRIC = 3, + VP_FLAGS_NAME_VALID = BIT_5, +}; + struct vp_rpt_id_entry_24xx { uint8_t entry_type; /* Entry type. */ uint8_t entry_count; /* Entry count. */ uint8_t sys_define; /* System defined. */ uint8_t entry_status; /* Entry Status. */ - - uint32_t handle; /* System handle. */ - - uint16_t vp_count; /* Format 0 -- | VP setup | VP acq |. */ - /* Format 1 -- | VP count |. */ - uint16_t vp_idx; /* Format 0 -- Reserved. */ - /* Format 1 -- VP status and index. */ + uint32_t resv1; + uint8_t vp_acquired; + uint8_t vp_setup; + uint8_t vp_idx; /* Format 0=reserved */ + uint8_t vp_status; /* Format 0=reserved */ uint8_t port_id[3]; uint8_t format; - - uint8_t vp_idx_map[16]; - - uint8_t reserved_4[24]; - uint16_t bbcr; - uint8_t reserved_5[6]; + union { + struct { + /* format 0 loop */ + uint8_t vp_idx_map[16]; + uint8_t reserved_4[32]; + } f0; + struct { + /* format 1 fabric */ + uint8_t vpstat1_subcode; /* vp_status=1 subcode */ + uint8_t flags; + uint16_t fip_flags; + uint8_t rsv2[12]; + + uint8_t ls_rjt_vendor; + uint8_t ls_rjt_explanation; + uint8_t ls_rjt_reason; + uint8_t rsv3[5]; + + uint8_t port_name[8]; + uint8_t node_name[8]; + uint16_t bbcr; + uint8_t reserved_5[6]; + } f1; + struct { /* format 2: N2N direct connect */ + uint8_t vpstat1_subcode; + uint8_t flags; + uint16_t rsv6; + uint8_t rsv2[12]; + + uint8_t ls_rjt_vendor; + uint8_t ls_rjt_explanation; + uint8_t ls_rjt_reason; + uint8_t rsv3[5]; + + uint8_t port_name[8]; + uint8_t node_name[8]; + uint32_t remote_nport_id; + uint32_t reserved_5; + } f2; + } u; }; #define VF_EVFP_IOCB_TYPE 0x26 /* Exchange Virtual Fabric Parameters entry. */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index afa0116a163b..b3d6441d1d90 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -73,6 +73,10 @@ extern void qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern void qla2x00_async_adisc_done(struct scsi_qla_host *, fc_port_t *, uint16_t *); +struct qla_work_evt *qla2x00_alloc_work(struct scsi_qla_host *, + enum qla_work_type); +extern int qla24xx_async_gnl(struct scsi_qla_host *, fc_port_t *); +int qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e); extern void *qla2x00_alloc_iocbs(struct scsi_qla_host *, srb_t *); extern void *qla2x00_alloc_iocbs_ready(struct scsi_qla_host *, srb_t *); extern int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *); @@ -94,6 +98,13 @@ extern uint8_t qla27xx_find_valid_image(struct scsi_qla_host *); extern struct qla_qpair *qla2xxx_create_qpair(struct scsi_qla_host *, int, int); extern int qla2xxx_delete_qpair(struct scsi_qla_host *, struct qla_qpair *); +void qla2x00_fcport_event_handler(scsi_qla_host_t *, struct event_arg *); +int qla24xx_async_gpdb(struct scsi_qla_host *, fc_port_t *, u8); +int qla24xx_async_notify_ack(scsi_qla_host_t *, fc_port_t *, + struct imm_ntfy_from_isp *, int); +int qla24xx_post_newsess_work(struct scsi_qla_host *, port_id_t *, u8 *, + void *); +int qla24xx_fcport_handle_login(struct scsi_qla_host *, fc_port_t *); /* * Global Data in qla_os.c source file. @@ -127,6 +138,7 @@ extern int ql2xmdenable; extern int ql2xexlogins; extern int ql2xexchoffld; extern int ql2xfwholdabts; +extern int ql2xmvasynctoatio; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -135,8 +147,6 @@ extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *); extern int qla2x00_post_async_login_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); -extern int qla2x00_post_async_login_done_work(struct scsi_qla_host *, - fc_port_t *, uint16_t *); extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *, @@ -176,9 +186,13 @@ extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); extern void qla2x00_disable_board_on_pci_error(struct work_struct *); -extern void qla2x00_sp_compl(void *, void *, int); -extern void qla2xxx_qpair_sp_free_dma(void *, void *); -extern void qla2xxx_qpair_sp_compl(void *, void *, int); +extern void qla2x00_sp_compl(void *, int); +extern void qla2xxx_qpair_sp_free_dma(void *); +extern void qla2xxx_qpair_sp_compl(void *, int); +extern int qla24xx_post_upd_fcport_work(struct scsi_qla_host *, fc_port_t *); +void qla2x00_handle_login_done_event(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +int qla24xx_post_gnl_work(struct scsi_qla_host *, fc_port_t *); /* * Global Functions in qla_mid.c source file. @@ -201,7 +215,7 @@ extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *); extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *); extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *); -extern void qla2x00_sp_free_dma(void *, void *); +extern void qla2x00_sp_free_dma(void *); extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *); extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int, int); @@ -302,9 +316,6 @@ extern int qla2x00_init_firmware(scsi_qla_host_t *, uint16_t); extern int -qla2x00_get_node_name_list(scsi_qla_host_t *, void **, int *); - -extern int qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t); extern int @@ -483,6 +494,9 @@ qla2x00_process_completed_request(struct scsi_qla_host *, struct req_que *, uint32_t); extern irqreturn_t qla2xxx_msix_rsp_q(int irq, void *dev_id); +fc_port_t *qla2x00_find_fcport_by_loopid(scsi_qla_host_t *, uint16_t); +fc_port_t *qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *, u8 *, u8); +fc_port_t *qla2x00_find_fcport_by_nportid(scsi_qla_host_t *, port_id_t *, u8); /* * Global Function Prototypes in qla_sup.c source file. @@ -574,8 +588,8 @@ extern void qla2xxx_dump_post_process(scsi_qla_host_t *, int); /* * Global Function Prototypes in qla_gs.c source file. */ -extern void *qla2x00_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t); -extern void *qla24xx_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t); +extern void *qla2x00_prep_ms_iocb(scsi_qla_host_t *, struct ct_arg *); +extern void *qla24xx_prep_ms_iocb(scsi_qla_host_t *, struct ct_arg *); extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *); extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *); @@ -591,6 +605,23 @@ extern int qla2x00_fdmi_register(scsi_qla_host_t *); extern int qla2x00_gfpn_id(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gpsc(scsi_qla_host_t *, sw_info_t *); extern void qla2x00_get_sym_node_name(scsi_qla_host_t *, uint8_t *, size_t); +extern int qla2x00_chk_ms_status(scsi_qla_host_t *, ms_iocb_entry_t *, + struct ct_sns_rsp *, const char *); +extern void qla2x00_async_iocb_timeout(void *data); +extern int qla24xx_async_gidpn(scsi_qla_host_t *, fc_port_t *); +int qla24xx_post_gidpn_work(struct scsi_qla_host *, fc_port_t *); +void qla24xx_handle_gidpn_event(scsi_qla_host_t *, struct event_arg *); + +extern void qla2x00_free_fcport(fc_port_t *); + +extern int qla24xx_post_gpnid_work(struct scsi_qla_host *, port_id_t *); +extern int qla24xx_async_gpnid(scsi_qla_host_t *, port_id_t *); +void qla24xx_async_gpnid_done(scsi_qla_host_t *, srb_t*); +void qla24xx_handle_gpnid_event(scsi_qla_host_t *, struct event_arg *); + +int qla24xx_post_gpsc_work(struct scsi_qla_host *, fc_port_t *); +int qla24xx_async_gpsc(scsi_qla_host_t *, fc_port_t *); +int qla2x00_mgmt_svr_login(scsi_qla_host_t *); /* * Global Function Prototypes in qla_attr.c source file. @@ -702,10 +733,10 @@ extern int qla82xx_restart_isp(scsi_qla_host_t *); /* IOCB related functions */ extern int qla82xx_start_scsi(srb_t *); -extern void qla2x00_sp_free(void *, void *); +extern void qla2x00_sp_free(void *); extern void qla2x00_sp_timeout(unsigned long); -extern void qla2x00_bsg_job_done(void *, void *, int); -extern void qla2x00_bsg_sp_free(void *, void *); +extern void qla2x00_bsg_job_done(void *, int); +extern void qla2x00_bsg_sp_free(void *); extern void qla2x00_start_iocbs(struct scsi_qla_host *, struct req_que *); /* Interrupt related */ @@ -803,4 +834,17 @@ extern int qla_get_exchoffld_status(scsi_qla_host_t *, uint16_t *, uint16_t *); extern int qla_set_exchoffld_mem_cfg(scsi_qla_host_t *, dma_addr_t); extern void qlt_handle_abts_recv(struct scsi_qla_host *, response_t *); +int qla24xx_async_notify_ack(scsi_qla_host_t *, fc_port_t *, + struct imm_ntfy_from_isp *, int); +void qla24xx_do_nack_work(struct scsi_qla_host *, struct qla_work_evt *); +void qlt_plogi_ack_link(struct scsi_qla_host *, struct qlt_plogi_ack_t *, + struct fc_port *, enum qlt_plogi_link_t); +void qlt_plogi_ack_unref(struct scsi_qla_host *, struct qlt_plogi_ack_t *); +extern void qlt_schedule_sess_for_deletion(struct fc_port *, bool); +extern void qlt_schedule_sess_for_deletion_lock(struct fc_port *); +extern struct fc_port *qlt_find_sess_invalidate_other(scsi_qla_host_t *, + uint64_t wwn, port_id_t port_id, uint16_t loop_id, struct fc_port **); +void qla24xx_delete_sess_fn(struct work_struct *); +void qlt_unknown_atio_work_fn(struct work_struct *); + #endif /* _QLA_GBL_H */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index ee3df8794806..ab0f873fd6a1 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -24,12 +24,12 @@ static int qla2x00_sns_rnn_id(scsi_qla_host_t *); * Returns a pointer to the @ha's ms_iocb. */ void * -qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) +qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg) { struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; - ms_pkt = ha->ms_iocb; + ms_pkt = (ms_iocb_entry_t *)arg->iocb; memset(ms_pkt, 0, sizeof(ms_iocb_entry_t)); ms_pkt->entry_type = MS_IOCB_TYPE; @@ -39,15 +39,15 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ms_pkt->cmd_dsd_count = cpu_to_le16(1); ms_pkt->total_dsd_count = cpu_to_le16(2); - ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size); - ms_pkt->req_bytecount = cpu_to_le32(req_size); + ms_pkt->rsp_bytecount = cpu_to_le32(arg->rsp_size); + ms_pkt->req_bytecount = cpu_to_le32(arg->req_size); - ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); - ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(arg->req_dma)); + ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(arg->req_dma)); ms_pkt->dseg_req_length = ms_pkt->req_bytecount; - ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); - ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(arg->rsp_dma)); + ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(arg->rsp_dma)); ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount; vha->qla_stats.control_requests++; @@ -64,29 +64,29 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) * Returns a pointer to the @ha's ms_iocb. */ void * -qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) +qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg) { struct qla_hw_data *ha = vha->hw; struct ct_entry_24xx *ct_pkt; - ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; + ct_pkt = (struct ct_entry_24xx *)arg->iocb; memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_count = 1; - ct_pkt->nport_handle = cpu_to_le16(NPH_SNS); + ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle); ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); ct_pkt->cmd_dsd_count = cpu_to_le16(1); ct_pkt->rsp_dsd_count = cpu_to_le16(1); - ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); - ct_pkt->cmd_byte_count = cpu_to_le32(req_size); + ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size); + ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size); - ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); - ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(arg->req_dma)); + ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(arg->req_dma)); ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count; - ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); - ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(arg->rsp_dma)); + ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(arg->rsp_dma)); ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; ct_pkt->vp_index = vha->vp_idx; @@ -117,7 +117,7 @@ qla2x00_prep_ct_req(struct ct_sns_pkt *p, uint16_t cmd, uint16_t rsp_size) return &p->p.req; } -static int +int qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, struct ct_sns_rsp *ct_rsp, const char *routine) { @@ -183,14 +183,21 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; struct qla_hw_data *ha = vha->hw; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_ga_nxt(vha, fcport); + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = GA_NXT_REQ_SIZE; + arg.rsp_size = GA_NXT_RSP_SIZE; + arg.nport_handle = NPH_SNS; + /* Issue GA_NXT */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GA_NXT_REQ_SIZE, - GA_NXT_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GA_NXT_CMD, @@ -269,16 +276,24 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) struct ct_sns_gid_pt_data *gid_data; struct qla_hw_data *ha = vha->hw; uint16_t gid_pt_rsp_size; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_gid_pt(vha, list); gid_data = NULL; gid_pt_rsp_size = qla2x00_gid_pt_rsp_size(vha); + + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = GID_PT_REQ_SIZE; + arg.rsp_size = gid_pt_rsp_size; + arg.nport_handle = NPH_SNS; + /* Issue GID_PT */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GID_PT_REQ_SIZE, - gid_pt_rsp_size); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GID_PT_CMD, gid_pt_rsp_size); @@ -344,15 +359,22 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; struct qla_hw_data *ha = vha->hw; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_gpn_id(vha, list); + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = GPN_ID_REQ_SIZE; + arg.rsp_size = GPN_ID_RSP_SIZE; + arg.nport_handle = NPH_SNS; + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GPN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GPN_ID_REQ_SIZE, - GPN_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GPN_ID_CMD, @@ -406,15 +428,22 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_gnn_id(vha, list); + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = GNN_ID_REQ_SIZE; + arg.rsp_size = GNN_ID_RSP_SIZE; + arg.nport_handle = NPH_SNS; + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GNN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GNN_ID_REQ_SIZE, - GNN_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GNN_ID_CMD, @@ -473,14 +502,21 @@ qla2x00_rft_id(scsi_qla_host_t *vha) ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_rft_id(vha); + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = RFT_ID_REQ_SIZE; + arg.rsp_size = RFT_ID_RSP_SIZE; + arg.nport_handle = NPH_SNS; + /* Issue RFT_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFT_ID_REQ_SIZE, - RFT_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFT_ID_CMD, @@ -526,6 +562,7 @@ qla2x00_rff_id(scsi_qla_host_t *vha) ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { ql_dbg(ql_dbg_disc, vha, 0x2046, @@ -533,10 +570,16 @@ qla2x00_rff_id(scsi_qla_host_t *vha) return (QLA_SUCCESS); } + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = RFF_ID_REQ_SIZE; + arg.rsp_size = RFF_ID_RSP_SIZE; + arg.nport_handle = NPH_SNS; + /* Issue RFF_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFF_ID_REQ_SIZE, - RFF_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFF_ID_CMD, @@ -584,14 +627,21 @@ qla2x00_rnn_id(scsi_qla_host_t *vha) ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_rnn_id(vha); + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = RNN_ID_REQ_SIZE; + arg.rsp_size = RNN_ID_RSP_SIZE; + arg.nport_handle = NPH_SNS; + /* Issue RNN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RNN_ID_REQ_SIZE, - RNN_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE); @@ -651,6 +701,7 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha) ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct ct_arg arg; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { ql_dbg(ql_dbg_disc, vha, 0x2050, @@ -658,10 +709,17 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha) return (QLA_SUCCESS); } + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = 0; + arg.rsp_size = RSNN_NN_RSP_SIZE; + arg.nport_handle = NPH_SNS; + /* Issue RSNN_NN */ /* Prepare common MS IOCB */ /* Request size adjusted after CT preparation */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, 0, RSNN_NN_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RSNN_NN_CMD, @@ -1103,7 +1161,7 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha) * * Returns 0 on success. */ -static int +int qla2x00_mgmt_svr_login(scsi_qla_host_t *vha) { int ret, rval; @@ -2425,15 +2483,22 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct ct_arg arg; if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = GFPN_ID_REQ_SIZE; + arg.rsp_size = GFPN_ID_RSP_SIZE; + arg.nport_handle = NPH_SNS; + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GFPN_ID */ /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFPN_ID_REQ_SIZE, - GFPN_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFPN_ID_CMD, @@ -2471,36 +2536,6 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) return (rval); } -static inline void * -qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size, - uint32_t rsp_size) -{ - struct ct_entry_24xx *ct_pkt; - struct qla_hw_data *ha = vha->hw; - ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; - memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); - - ct_pkt->entry_type = CT_IOCB_TYPE; - ct_pkt->entry_count = 1; - ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id); - ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ct_pkt->cmd_dsd_count = cpu_to_le16(1); - ct_pkt->rsp_dsd_count = cpu_to_le16(1); - ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); - ct_pkt->cmd_byte_count = cpu_to_le32(req_size); - - ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); - ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); - ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count; - - ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); - ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); - ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; - ct_pkt->vp_index = vha->vp_idx; - - return ct_pkt; -} - static inline struct ct_sns_req * qla24xx_prep_ct_fm_req(struct ct_sns_pkt *p, uint16_t cmd, @@ -2530,9 +2565,10 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) int rval; uint16_t i; struct qla_hw_data *ha = vha->hw; - ms_iocb_entry_t *ms_pkt; + ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; + struct ct_arg arg; if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; @@ -2543,11 +2579,17 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) if (rval) return rval; + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = GPSC_REQ_SIZE; + arg.rsp_size = GPSC_RSP_SIZE; + arg.nport_handle = vha->mgmt_svr_loop_id; + for (i = 0; i < ha->max_fibre_devices; i++) { /* Issue GFPN_ID */ /* Prepare common MS IOCB */ - ms_pkt = qla24xx_prep_ms_fm_iocb(vha, GPSC_REQ_SIZE, - GPSC_RSP_SIZE); + ms_pkt = qla24xx_prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla24xx_prep_ct_fm_req(ha->ct_sns, GPSC_CMD, @@ -2641,6 +2683,7 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) struct ct_sns_rsp *ct_rsp; struct qla_hw_data *ha = vha->hw; uint8_t fcp_scsi_features = 0; + struct ct_arg arg; for (i = 0; i < ha->max_fibre_devices; i++) { /* Set default FC4 Type as UNKNOWN so the default is to @@ -2651,9 +2694,15 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) if (!IS_FWI2_CAPABLE(ha)) continue; + arg.iocb = ha->ms_iocb; + arg.req_dma = ha->ct_sns_dma; + arg.rsp_dma = ha->ct_sns_dma; + arg.req_size = GFF_ID_REQ_SIZE; + arg.rsp_size = GFF_ID_RSP_SIZE; + arg.nport_handle = NPH_SNS; + /* Prepare common MS IOCB */ - ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFF_ID_REQ_SIZE, - GFF_ID_RSP_SIZE); + ms_pkt = ha->isp_ops->prep_ms_iocb(vha, &arg); /* Prepare CT request */ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFF_ID_CMD, @@ -2692,3 +2741,538 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) break; } } + +/* GID_PN completion processing. */ +void qla24xx_handle_gidpn_event(scsi_qla_host_t *vha, struct event_arg *ea) +{ + fc_port_t *fcport = ea->fcport; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC login state %d \n", + __func__, fcport->port_name, fcport->fw_login_state); + + if (ea->sp->gen2 != fcport->login_gen) { + /* PLOGI/PRLI/LOGO came in while cmd was out.*/ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC generation changed rscn %d|%d login %d|%d \n", + __func__, fcport->port_name, fcport->last_rscn_gen, + fcport->rscn_gen, fcport->last_login_gen, fcport->login_gen); + return; + } + + if (!ea->rc) { + if (ea->sp->gen1 == fcport->rscn_gen) { + fcport->scan_state = QLA_FCPORT_FOUND; + fcport->flags |= FCF_FABRIC_DEVICE; + + if (fcport->d_id.b24 == ea->id.b24) { + /* cable plugged into the same place */ + switch (vha->host->active_mode) { + case MODE_TARGET: + /* NOOP. let the other guy login to us.*/ + break; + case MODE_INITIATOR: + case MODE_DUAL: + default: + if (atomic_read(&fcport->state) == + FCS_ONLINE) + break; + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gnl\n", + __func__, __LINE__, fcport->port_name); + qla24xx_post_gnl_work(vha, fcport); + break; + } + } else { /* fcport->d_id.b24 != ea->id.b24 */ + fcport->d_id.b24 = ea->id.b24; + if (fcport->deleted == QLA_SESS_DELETED) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", + __func__, __LINE__, fcport->port_name); + qlt_schedule_sess_for_deletion_lock(fcport); + } + } + } else { /* ea->sp->gen1 != fcport->rscn_gen */ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gidpn\n", + __func__, __LINE__, fcport->port_name); + /* rscn came in while cmd was out */ + qla24xx_post_gidpn_work(vha, fcport); + } + } else { /* ea->rc */ + /* cable pulled */ + if (ea->sp->gen1 == fcport->rscn_gen) { + if (ea->sp->gen2 == fcport->login_gen) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", __func__, + __LINE__, fcport->port_name); + qlt_schedule_sess_for_deletion_lock(fcport); + } else { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC login\n", __func__, __LINE__, + fcport->port_name); + qla24xx_fcport_handle_login(vha, fcport); + } + } else { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gidpn\n", __func__, __LINE__, + fcport->port_name); + qla24xx_post_gidpn_work(vha, fcport); + } + } +} /* gidpn_event */ + +static void qla2x00_async_gidpn_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + fc_port_t *fcport = sp->fcport; + u8 *id = fcport->ct_desc.ct_sns->p.rsp.rsp.gid_pn.port_id; + struct event_arg ea; + + fcport->flags &= ~FCF_ASYNC_SENT; + + memset(&ea, 0, sizeof(ea)); + ea.fcport = fcport; + ea.id.b.domain = id[0]; + ea.id.b.area = id[1]; + ea.id.b.al_pa = id[2]; + ea.sp = sp; + ea.rc = res; + ea.event = FCME_GIDPN_DONE; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s res %x, WWPN %8phC ID %3phC \n", + sp->name, res, fcport->port_name, id); + + qla2x00_fcport_event_handler(vha, &ea); + + sp->free(sp); +} + +int qla24xx_async_gidpn(scsi_qla_host_t *vha, fc_port_t *fcport) +{ + int rval = QLA_FUNCTION_FAILED; + struct ct_sns_req *ct_req; + srb_t *sp; + + if (!vha->flags.online) + goto done; + + fcport->flags |= FCF_ASYNC_SENT; + fcport->disc_state = DSC_GID_PN; + fcport->scan_state = QLA_FCPORT_SCAN; + sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); + if (!sp) + goto done; + + sp->type = SRB_CT_PTHRU_CMD; + sp->name = "gidpn"; + sp->gen1 = fcport->rscn_gen; + sp->gen2 = fcport->login_gen; + + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + /* CT_IU preamble */ + ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GID_PN_CMD, + GID_PN_RSP_SIZE); + + /* GIDPN req */ + memcpy(ct_req->req.gid_pn.port_name, fcport->port_name, + WWN_SIZE); + + /* req & rsp use the same buffer */ + sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns; + sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma; + sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns; + sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma; + sp->u.iocb_cmd.u.ctarg.req_size = GID_PN_REQ_SIZE; + sp->u.iocb_cmd.u.ctarg.rsp_size = GID_PN_RSP_SIZE; + sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS; + + sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_gidpn_sp_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0x206f, + "Async-%s - %8phC hdl=%x loopid=%x portid %02x%02x%02x.\n", + sp->name, fcport->port_name, + sp->handle, fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); + return rval; + +done_free_sp: + sp->free(sp); +done: + fcport->flags &= ~FCF_ASYNC_SENT; + return rval; +} + +int qla24xx_post_gidpn_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_work_evt *e; + int ls; + + ls = atomic_read(&vha->loop_state); + if (((ls != LOOP_READY) && (ls != LOOP_UP)) || + test_bit(UNLOADING, &vha->dpc_flags)) + return 0; + + e = qla2x00_alloc_work(vha, QLA_EVT_GIDPN); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + return qla2x00_post_work(vha, e); +} + +int qla24xx_post_gpsc_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_GPSC); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + return qla2x00_post_work(vha, e); +} + +static void qla24xx_async_gpsc_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + struct qla_hw_data *ha = vha->hw; + fc_port_t *fcport = sp->fcport; + struct ct_sns_rsp *ct_rsp; + struct event_arg ea; + + ct_rsp = &fcport->ct_desc.ct_sns->p.rsp; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s res %x, WWPN %8phC \n", + sp->name, res, fcport->port_name); + + fcport->flags &= ~FCF_ASYNC_SENT; + + if (res == (DID_ERROR << 16)) { + /* entry status error */ + goto done; + } else if (res) { + if ((ct_rsp->header.reason_code == + CT_REASON_INVALID_COMMAND_CODE) || + (ct_rsp->header.reason_code == + CT_REASON_COMMAND_UNSUPPORTED)) { + ql_dbg(ql_dbg_disc, vha, 0x205a, + "GPSC command unsupported, disabling " + "query.\n"); + ha->flags.gpsc_supported = 0; + res = QLA_SUCCESS; + } + } else { + switch (be16_to_cpu(ct_rsp->rsp.gpsc.speed)) { + case BIT_15: + fcport->fp_speed = PORT_SPEED_1GB; + break; + case BIT_14: + fcport->fp_speed = PORT_SPEED_2GB; + break; + case BIT_13: + fcport->fp_speed = PORT_SPEED_4GB; + break; + case BIT_12: + fcport->fp_speed = PORT_SPEED_10GB; + break; + case BIT_11: + fcport->fp_speed = PORT_SPEED_8GB; + break; + case BIT_10: + fcport->fp_speed = PORT_SPEED_16GB; + break; + case BIT_8: + fcport->fp_speed = PORT_SPEED_32GB; + break; + } + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async-%s OUT WWPN %8phC speeds=%04x speed=%04x.\n", + sp->name, + fcport->fabric_port_name, + be16_to_cpu(ct_rsp->rsp.gpsc.speeds), + be16_to_cpu(ct_rsp->rsp.gpsc.speed)); + } +done: + memset(&ea, 0, sizeof(ea)); + ea.event = FCME_GPSC_DONE; + ea.rc = res; + ea.fcport = fcport; + qla2x00_fcport_event_handler(vha, &ea); + + sp->free(sp); +} + +int qla24xx_async_gpsc(scsi_qla_host_t *vha, fc_port_t *fcport) +{ + int rval = QLA_FUNCTION_FAILED; + struct ct_sns_req *ct_req; + srb_t *sp; + + if (!vha->flags.online) + goto done; + + fcport->flags |= FCF_ASYNC_SENT; + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + sp->type = SRB_CT_PTHRU_CMD; + sp->name = "gpsc"; + sp->gen1 = fcport->rscn_gen; + sp->gen2 = fcport->login_gen; + + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + /* CT_IU preamble */ + ct_req = qla24xx_prep_ct_fm_req(fcport->ct_desc.ct_sns, GPSC_CMD, + GPSC_RSP_SIZE); + + /* GPSC req */ + memcpy(ct_req->req.gpsc.port_name, fcport->port_name, + WWN_SIZE); + + sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns; + sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma; + sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns; + sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma; + sp->u.iocb_cmd.u.ctarg.req_size = GPSC_REQ_SIZE; + sp->u.iocb_cmd.u.ctarg.rsp_size = GPSC_RSP_SIZE; + sp->u.iocb_cmd.u.ctarg.nport_handle = vha->mgmt_svr_loop_id; + + sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout; + sp->done = qla24xx_async_gpsc_sp_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async-%s %8phC hdl=%x loopid=%x portid=%02x%02x%02x.\n", + sp->name, fcport->port_name, sp->handle, + fcport->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa); + return rval; + +done_free_sp: + sp->free(sp); +done: + fcport->flags &= ~FCF_ASYNC_SENT; + return rval; +} + +int qla24xx_post_gpnid_work(struct scsi_qla_host *vha, port_id_t *id) +{ + struct qla_work_evt *e; + + if (test_bit(UNLOADING, &vha->dpc_flags)) + return 0; + + e = qla2x00_alloc_work(vha, QLA_EVT_GPNID); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.gpnid.id = *id; + return qla2x00_post_work(vha, e); +} + +void qla24xx_async_gpnid_done(scsi_qla_host_t *vha, srb_t *sp) +{ + if (sp->u.iocb_cmd.u.ctarg.req) { + dma_free_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), + sp->u.iocb_cmd.u.ctarg.req, + sp->u.iocb_cmd.u.ctarg.req_dma); + sp->u.iocb_cmd.u.ctarg.req = NULL; + } + if (sp->u.iocb_cmd.u.ctarg.rsp) { + dma_free_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), + sp->u.iocb_cmd.u.ctarg.rsp, + sp->u.iocb_cmd.u.ctarg.rsp_dma); + sp->u.iocb_cmd.u.ctarg.rsp = NULL; + } + + sp->free(sp); +} + +void qla24xx_handle_gpnid_event(scsi_qla_host_t *vha, struct event_arg *ea) +{ + fc_port_t *fcport; + unsigned long flags; + + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + fcport = qla2x00_find_fcport_by_wwpn(vha, ea->port_name, 1); + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + + if (fcport) { + /* cable moved. just plugged in */ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", + __func__, __LINE__, fcport->port_name); + + fcport->rscn_gen++; + fcport->d_id = ea->id; + fcport->scan_state = QLA_FCPORT_FOUND; + fcport->flags |= FCF_FABRIC_DEVICE; + + qlt_schedule_sess_for_deletion_lock(fcport); + } else { + /* create new fcport */ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post new sess\n", + __func__, __LINE__, ea->port_name); + + qla24xx_post_newsess_work(vha, &ea->id, ea->port_name, NULL); + } +} + +static void qla2x00_async_gpnid_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + struct ct_sns_req *ct_req = + (struct ct_sns_req *)sp->u.iocb_cmd.u.ctarg.req; + struct ct_sns_rsp *ct_rsp = + (struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp; + struct event_arg ea; + struct qla_work_evt *e; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s res %x ID %3phC. %8phC\n", + sp->name, res, ct_req->req.port_id.port_id, + ct_rsp->rsp.gpn_id.port_name); + + memset(&ea, 0, sizeof(ea)); + memcpy(ea.port_name, ct_rsp->rsp.gpn_id.port_name, WWN_SIZE); + ea.sp = sp; + ea.id.b.domain = ct_req->req.port_id.port_id[0]; + ea.id.b.area = ct_req->req.port_id.port_id[1]; + ea.id.b.al_pa = ct_req->req.port_id.port_id[2]; + ea.rc = res; + ea.event = FCME_GPNID_DONE; + + qla2x00_fcport_event_handler(vha, &ea); + + e = qla2x00_alloc_work(vha, QLA_EVT_GPNID_DONE); + if (!e) { + /* please ignore kernel warning. otherwise, we have mem leak. */ + if (sp->u.iocb_cmd.u.ctarg.req) { + dma_free_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), + sp->u.iocb_cmd.u.ctarg.req, + sp->u.iocb_cmd.u.ctarg.req_dma); + sp->u.iocb_cmd.u.ctarg.req = NULL; + } + if (sp->u.iocb_cmd.u.ctarg.rsp) { + dma_free_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), + sp->u.iocb_cmd.u.ctarg.rsp, + sp->u.iocb_cmd.u.ctarg.rsp_dma); + sp->u.iocb_cmd.u.ctarg.rsp = NULL; + } + + sp->free(sp); + return; + } + + e->u.iosb.sp = sp; + qla2x00_post_work(vha, e); +} + +/* Get WWPN with Nport ID. */ +int qla24xx_async_gpnid(scsi_qla_host_t *vha, port_id_t *id) +{ + int rval = QLA_FUNCTION_FAILED; + struct ct_sns_req *ct_req; + srb_t *sp; + struct ct_sns_pkt *ct_sns; + + if (!vha->flags.online) + goto done; + + sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL); + if (!sp) + goto done; + + sp->type = SRB_CT_PTHRU_CMD; + sp->name = "gpnid"; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma, + GFP_KERNEL); + if (!sp->u.iocb_cmd.u.ctarg.req) { + ql_log(ql_log_warn, vha, 0xffff, + "Failed to allocate ct_sns request.\n"); + goto done_free_sp; + } + + sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.rsp_dma, + GFP_KERNEL); + if (!sp->u.iocb_cmd.u.ctarg.rsp) { + ql_log(ql_log_warn, vha, 0xffff, + "Failed to allocate ct_sns request.\n"); + goto done_free_sp; + } + + ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.rsp; + memset(ct_sns, 0, sizeof(*ct_sns)); + + ct_sns = (struct ct_sns_pkt *)sp->u.iocb_cmd.u.ctarg.req; + /* CT_IU preamble */ + ct_req = qla2x00_prep_ct_req(ct_sns, GPN_ID_CMD, GPN_ID_RSP_SIZE); + + /* GPN_ID req */ + ct_req->req.port_id.port_id[0] = id->b.domain; + ct_req->req.port_id.port_id[1] = id->b.area; + ct_req->req.port_id.port_id[2] = id->b.al_pa; + + sp->u.iocb_cmd.u.ctarg.req_size = GPN_ID_REQ_SIZE; + sp->u.iocb_cmd.u.ctarg.rsp_size = GPN_ID_RSP_SIZE; + sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS; + + sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout; + sp->done = qla2x00_async_gpnid_sp_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async-%s hdl=%x ID %3phC.\n", sp->name, + sp->handle, ct_req->req.port_id.port_id); + return rval; + +done_free_sp: + if (sp->u.iocb_cmd.u.ctarg.req) { + dma_free_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), + sp->u.iocb_cmd.u.ctarg.req, + sp->u.iocb_cmd.u.ctarg.req_dma); + sp->u.iocb_cmd.u.ctarg.req = NULL; + } + if (sp->u.iocb_cmd.u.ctarg.rsp) { + dma_free_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), + sp->u.iocb_cmd.u.ctarg.rsp, + sp->u.iocb_cmd.u.ctarg.rsp_dma); + sp->u.iocb_cmd.u.ctarg.rsp = NULL; + } + + sp->free(sp); +done: + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 7b6317c8c2e9..32fb9007f137 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -30,15 +30,15 @@ static int qla2x00_configure_hba(scsi_qla_host_t *); static int qla2x00_configure_loop(scsi_qla_host_t *); static int qla2x00_configure_local_loop(scsi_qla_host_t *); static int qla2x00_configure_fabric(scsi_qla_host_t *); -static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *, struct list_head *); -static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *, - uint16_t *); - +static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *); static int qla2x00_restart_isp(scsi_qla_host_t *); static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *); static int qla84xx_init_chip(scsi_qla_host_t *); static int qla25xx_init_queues(struct qla_hw_data *); +static int qla24xx_post_gpdb_work(struct scsi_qla_host *, fc_port_t *, u8); +static void qla24xx_handle_plogi_done_event(struct scsi_qla_host *, + struct event_arg *); /* SRB Extensions ---------------------------------------------------------- */ @@ -47,29 +47,27 @@ qla2x00_sp_timeout(unsigned long __data) { srb_t *sp = (srb_t *)__data; struct srb_iocb *iocb; - fc_port_t *fcport = sp->fcport; - struct qla_hw_data *ha = fcport->vha->hw; + scsi_qla_host_t *vha = sp->vha; struct req_que *req; unsigned long flags; - spin_lock_irqsave(&ha->hardware_lock, flags); - req = ha->req_q_map[0]; + spin_lock_irqsave(&vha->hw->hardware_lock, flags); + req = vha->hw->req_q_map[0]; req->outstanding_cmds[sp->handle] = NULL; iocb = &sp->u.iocb_cmd; iocb->timeout(sp); - sp->free(fcport->vha, sp); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + sp->free(sp); + spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); } void -qla2x00_sp_free(void *data, void *ptr) +qla2x00_sp_free(void *ptr) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; struct srb_iocb *iocb = &sp->u.iocb_cmd; - struct scsi_qla_host *vha = (scsi_qla_host_t *)data; del_timer(&iocb->timer); - qla2x00_rel_sp(vha, sp); + qla2x00_rel_sp(sp); } /* Asynchronous Login/Logout Routines -------------------------------------- */ @@ -94,43 +92,72 @@ qla2x00_get_async_timeout(struct scsi_qla_host *vha) return tmo; } -static void +void qla2x00_async_iocb_timeout(void *data) { - srb_t *sp = (srb_t *)data; + srb_t *sp = data; fc_port_t *fcport = sp->fcport; + struct srb_iocb *lio = &sp->u.iocb_cmd; + struct event_arg ea; ql_dbg(ql_dbg_disc, fcport->vha, 0x2071, - "Async-%s timeout - hdl=%x portid=%02x%02x%02x.\n", - sp->name, sp->handle, fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa); + "Async-%s timeout - hdl=%x portid=%06x %8phC.\n", + sp->name, sp->handle, fcport->d_id.b24, fcport->port_name); fcport->flags &= ~FCF_ASYNC_SENT; - if (sp->type == SRB_LOGIN_CMD) { - struct srb_iocb *lio = &sp->u.iocb_cmd; - qla2x00_post_async_logout_work(fcport->vha, fcport, NULL); + + switch (sp->type) { + case SRB_LOGIN_CMD: /* Retry as needed. */ lio->u.logio.data[0] = MBS_COMMAND_ERROR; lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? QLA_LOGIO_LOGIN_RETRIED : 0; - qla2x00_post_async_login_done_work(fcport->vha, fcport, - lio->u.logio.data); - } else if (sp->type == SRB_LOGOUT_CMD) { + memset(&ea, 0, sizeof(ea)); + ea.event = FCME_PLOGI_DONE; + ea.fcport = sp->fcport; + ea.data[0] = lio->u.logio.data[0]; + ea.data[1] = lio->u.logio.data[1]; + ea.sp = sp; + qla24xx_handle_plogi_done_event(fcport->vha, &ea); + break; + case SRB_LOGOUT_CMD: qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT); + break; + case SRB_CT_PTHRU_CMD: + case SRB_MB_IOCB: + case SRB_NACK_PLOGI: + case SRB_NACK_PRLI: + case SRB_NACK_LOGO: + sp->done(sp, QLA_FUNCTION_TIMEOUT); + break; } } static void -qla2x00_async_login_sp_done(void *data, void *ptr, int res) +qla2x00_async_login_sp_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; + struct scsi_qla_host *vha = sp->vha; struct srb_iocb *lio = &sp->u.iocb_cmd; - struct scsi_qla_host *vha = (scsi_qla_host_t *)data; + struct event_arg ea; - if (!test_bit(UNLOADING, &vha->dpc_flags)) - qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport, - lio->u.logio.data); - sp->free(sp->fcport->vha, sp); + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC res %d \n", __func__, sp->fcport->port_name, res); + + sp->fcport->flags &= ~FCF_ASYNC_SENT; + if (!test_bit(UNLOADING, &vha->dpc_flags)) { + memset(&ea, 0, sizeof(ea)); + ea.event = FCME_PLOGI_DONE; + ea.fcport = sp->fcport; + ea.data[0] = lio->u.logio.data[0]; + ea.data[1] = lio->u.logio.data[1]; + ea.iop[0] = lio->u.logio.iop[0]; + ea.iop[1] = lio->u.logio.iop[1]; + ea.sp = sp; + qla2x00_fcport_event_handler(vha, &ea); + } + + sp->free(sp); } int @@ -139,13 +166,23 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, { srb_t *sp; struct srb_iocb *lio; - int rval; + int rval = QLA_FUNCTION_FAILED; + + if (!vha->flags.online) + goto done; + + if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || + (fcport->fw_login_state == DSC_LS_PLOGI_COMP) || + (fcport->fw_login_state == DSC_LS_PRLI_PEND)) + goto done; - rval = QLA_FUNCTION_FAILED; sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; + fcport->flags |= FCF_ASYNC_SENT; + fcport->logout_completed = 0; + sp->type = SRB_LOGIN_CMD; sp->name = "login"; qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); @@ -165,29 +202,30 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, } ql_dbg(ql_dbg_disc, vha, 0x2072, - "Async-login - hdl=%x, loopid=%x portid=%02x%02x%02x " - "retries=%d.\n", sp->handle, fcport->loop_id, + "Async-login - %8phC hdl=%x, loopid=%x portid=%02x%02x%02x " + "retries=%d.\n", fcport->port_name, sp->handle, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, fcport->login_retry); return rval; done_free_sp: - sp->free(fcport->vha, sp); + sp->free(sp); done: + fcport->flags &= ~FCF_ASYNC_SENT; return rval; } static void -qla2x00_async_logout_sp_done(void *data, void *ptr, int res) +qla2x00_async_logout_sp_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; struct srb_iocb *lio = &sp->u.iocb_cmd; - struct scsi_qla_host *vha = (scsi_qla_host_t *)data; - if (!test_bit(UNLOADING, &vha->dpc_flags)) - qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport, + sp->fcport->flags &= ~FCF_ASYNC_SENT; + if (!test_bit(UNLOADING, &sp->vha->dpc_flags)) + qla2x00_post_async_logout_done_work(sp->vha, sp->fcport, lio->u.logio.data); - sp->free(sp->fcport->vha, sp); + sp->free(sp); } int @@ -198,6 +236,7 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) int rval; rval = QLA_FUNCTION_FAILED; + fcport->flags |= FCF_ASYNC_SENT; sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; @@ -214,28 +253,30 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) goto done_free_sp; ql_dbg(ql_dbg_disc, vha, 0x2070, - "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x.\n", + "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x %8phC.\n", sp->handle, fcport->loop_id, fcport->d_id.b.domain, - fcport->d_id.b.area, fcport->d_id.b.al_pa); + fcport->d_id.b.area, fcport->d_id.b.al_pa, + fcport->port_name); return rval; done_free_sp: - sp->free(fcport->vha, sp); + sp->free(sp); done: + fcport->flags &= ~FCF_ASYNC_SENT; return rval; } static void -qla2x00_async_adisc_sp_done(void *data, void *ptr, int res) +qla2x00_async_adisc_sp_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; + struct scsi_qla_host *vha = sp->vha; struct srb_iocb *lio = &sp->u.iocb_cmd; - struct scsi_qla_host *vha = (scsi_qla_host_t *)data; if (!test_bit(UNLOADING, &vha->dpc_flags)) - qla2x00_post_async_adisc_done_work(sp->fcport->vha, sp->fcport, + qla2x00_post_async_adisc_done_work(sp->vha, sp->fcport, lio->u.logio.data); - sp->free(sp->fcport->vha, sp); + sp->free(sp); } int @@ -247,6 +288,7 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, int rval; rval = QLA_FUNCTION_FAILED; + fcport->flags |= FCF_ASYNC_SENT; sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; @@ -271,15 +313,858 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, return rval; done_free_sp: - sp->free(fcport->vha, sp); + sp->free(sp); done: + fcport->flags &= ~FCF_ASYNC_SENT; return rval; } +static void qla24xx_handle_gnl_done_event(scsi_qla_host_t *vha, + struct event_arg *ea) +{ + fc_port_t *fcport, *conflict_fcport; + struct get_name_list_extended *e; + u16 i, n, found = 0, loop_id; + port_id_t id; + u64 wwn; + u8 opt = 0; + + fcport = ea->fcport; + + if (ea->rc) { /* rval */ + if (fcport->login_retry == 0) { + fcport->login_retry = vha->hw->login_retry_count; + ql_dbg(ql_dbg_disc, vha, 0xffff, + "GNL failed Port login retry %8phN, retry cnt=%d.\n", + fcport->port_name, fcport->login_retry); + } + return; + } + + if (fcport->last_rscn_gen != fcport->rscn_gen) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC rscn gen changed rscn %d|%d \n", + __func__, fcport->port_name, + fcport->last_rscn_gen, fcport->rscn_gen); + qla24xx_post_gidpn_work(vha, fcport); + return; + } else if (fcport->last_login_gen != fcport->login_gen) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC login gen changed login %d|%d \n", + __func__, fcport->port_name, + fcport->last_login_gen, fcport->login_gen); + return; + } + + n = ea->data[0] / sizeof(struct get_name_list_extended); + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC n %d %02x%02x%02x lid %d \n", + __func__, __LINE__, fcport->port_name, n, + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa, fcport->loop_id); + + for (i = 0; i < n; i++) { + e = &vha->gnl.l[i]; + wwn = wwn_to_u64(e->port_name); + + if (memcmp((u8 *)&wwn, fcport->port_name, WWN_SIZE)) + continue; + + found = 1; + id.b.domain = e->port_id[2]; + id.b.area = e->port_id[1]; + id.b.al_pa = e->port_id[0]; + id.b.rsvd_1 = 0; + + loop_id = le16_to_cpu(e->nport_handle); + loop_id = (loop_id & 0x7fff); + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s found %8phC CLS [%d|%d] ID[%02x%02x%02x|%02x%02x%02x] lid[%d|%d]\n", + __func__, fcport->port_name, + e->current_login_state, fcport->fw_login_state, + id.b.domain, id.b.area, id.b.al_pa, + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa, loop_id, fcport->loop_id); + + if ((id.b24 != fcport->d_id.b24) || + ((fcport->loop_id != FC_NO_LOOP_ID) && + (fcport->loop_id != loop_id))) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", + __func__, __LINE__, fcport->port_name); + qlt_schedule_sess_for_deletion(fcport, 1); + return; + } + + fcport->loop_id = loop_id; + + wwn = wwn_to_u64(fcport->port_name); + qlt_find_sess_invalidate_other(vha, wwn, + id, loop_id, &conflict_fcport); + + if (conflict_fcport) { + /* + * Another share fcport share the same loop_id & + * nport id. Conflict fcport needs to finish + * cleanup before this fcport can proceed to login. + */ + conflict_fcport->conflict = fcport; + fcport->login_pause = 1; + } + + switch (e->current_login_state) { + case DSC_LS_PRLI_COMP: + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpdb\n", + __func__, __LINE__, fcport->port_name); + opt = PDO_FORCE_ADISC; + qla24xx_post_gpdb_work(vha, fcport, opt); + break; + + case DSC_LS_PORT_UNAVAIL: + default: + if (fcport->loop_id == FC_NO_LOOP_ID) { + qla2x00_find_new_loop_id(vha, fcport); + fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; + } + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC \n", + __func__, __LINE__, fcport->port_name); + qla24xx_fcport_handle_login(vha, fcport); + break; + } + } + + if (!found) { + /* fw has no record of this port */ + if (fcport->loop_id == FC_NO_LOOP_ID) { + qla2x00_find_new_loop_id(vha, fcport); + fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; + } else { + for (i = 0; i < n; i++) { + e = &vha->gnl.l[i]; + id.b.domain = e->port_id[0]; + id.b.area = e->port_id[1]; + id.b.al_pa = e->port_id[2]; + id.b.rsvd_1 = 0; + loop_id = le16_to_cpu(e->nport_handle); + + if (fcport->d_id.b24 == id.b24) { + conflict_fcport = + qla2x00_find_fcport_by_wwpn(vha, + e->port_name, 0); + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", + __func__, __LINE__, + conflict_fcport->port_name); + qlt_schedule_sess_for_deletion + (conflict_fcport, 1); + } + + if (fcport->loop_id == loop_id) { + /* FW already picked this loop id for another fcport */ + qla2x00_find_new_loop_id(vha, fcport); + } + } + } + qla24xx_fcport_handle_login(vha, fcport); + } +} /* gnl_event */ + +static void +qla24xx_async_gnl_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + unsigned long flags; + struct fc_port *fcport = NULL, *tf; + u16 i, n = 0, loop_id; + struct event_arg ea; + struct get_name_list_extended *e; + u64 wwn; + struct list_head h; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s res %x mb[1]=%x mb[2]=%x \n", + sp->name, res, sp->u.iocb_cmd.u.mbx.in_mb[1], + sp->u.iocb_cmd.u.mbx.in_mb[2]); + + memset(&ea, 0, sizeof(ea)); + ea.sp = sp; + ea.rc = res; + ea.event = FCME_GNL_DONE; + + if (sp->u.iocb_cmd.u.mbx.in_mb[1] >= + sizeof(struct get_name_list_extended)) { + n = sp->u.iocb_cmd.u.mbx.in_mb[1] / + sizeof(struct get_name_list_extended); + ea.data[0] = sp->u.iocb_cmd.u.mbx.in_mb[1]; /* amnt xfered */ + } + + for (i = 0; i < n; i++) { + e = &vha->gnl.l[i]; + loop_id = le16_to_cpu(e->nport_handle); + /* mask out reserve bit */ + loop_id = (loop_id & 0x7fff); + set_bit(loop_id, vha->hw->loop_id_map); + wwn = wwn_to_u64(e->port_name); + + ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0xffff, + "%s %8phC %02x:%02x:%02x state %d/%d lid %x \n", + __func__, (void *)&wwn, e->port_id[2], e->port_id[1], + e->port_id[0], e->current_login_state, e->last_login_state, + (loop_id & 0x7fff)); + } + + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + vha->gnl.sent = 0; + + INIT_LIST_HEAD(&h); + fcport = tf = NULL; + if (!list_empty(&vha->gnl.fcports)) + list_splice_init(&vha->gnl.fcports, &h); + + list_for_each_entry_safe(fcport, tf, &h, gnl_entry) { + list_del_init(&fcport->gnl_entry); + fcport->flags &= ~FCF_ASYNC_SENT; + ea.fcport = fcport; + + qla2x00_fcport_event_handler(vha, &ea); + } + + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + + sp->free(sp); +} + +int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + srb_t *sp; + struct srb_iocb *mbx; + int rval = QLA_FUNCTION_FAILED; + unsigned long flags; + u16 *mb; + + if (!vha->flags.online) + goto done; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async-gnlist WWPN %8phC \n", fcport->port_name); + + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + fcport->flags |= FCF_ASYNC_SENT; + fcport->disc_state = DSC_GNL; + fcport->last_rscn_gen = fcport->rscn_gen; + fcport->last_login_gen = fcport->login_gen; + + list_add_tail(&fcport->gnl_entry, &vha->gnl.fcports); + if (vha->gnl.sent) { + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + rval = QLA_SUCCESS; + goto done; + } + vha->gnl.sent = 1; + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + sp->type = SRB_MB_IOCB; + sp->name = "gnlist"; + sp->gen1 = fcport->rscn_gen; + sp->gen2 = fcport->login_gen; + + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2); + + mb = sp->u.iocb_cmd.u.mbx.out_mb; + mb[0] = MBC_PORT_NODE_NAME_LIST; + mb[1] = BIT_2 | BIT_3; + mb[2] = MSW(vha->gnl.ldma); + mb[3] = LSW(vha->gnl.ldma); + mb[6] = MSW(MSD(vha->gnl.ldma)); + mb[7] = LSW(MSD(vha->gnl.ldma)); + mb[8] = vha->gnl.size; + mb[9] = vha->vp_idx; + + mbx = &sp->u.iocb_cmd; + mbx->timeout = qla2x00_async_iocb_timeout; + + sp->done = qla24xx_async_gnl_sp_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async-%s - OUT WWPN %8phC hndl %x\n", + sp->name, fcport->port_name, sp->handle); + + return rval; + +done_free_sp: + sp->free(sp); +done: + fcport->flags &= ~FCF_ASYNC_SENT; + return rval; +} + +int qla24xx_post_gnl_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_GNL); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + return qla2x00_post_work(vha, e); +} + +static +void qla24xx_async_gpdb_sp_done(void *s, int res) +{ + struct srb *sp = s; + struct scsi_qla_host *vha = sp->vha; + struct qla_hw_data *ha = vha->hw; + uint64_t zero = 0; + struct port_database_24xx *pd; + fc_port_t *fcport = sp->fcport; + u16 *mb = sp->u.iocb_cmd.u.mbx.in_mb; + int rval = QLA_SUCCESS; + struct event_arg ea; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s res %x, WWPN %8phC mb[1]=%x mb[2]=%x \n", + sp->name, res, fcport->port_name, mb[1], mb[2]); + + fcport->flags &= ~FCF_ASYNC_SENT; + + if (res) { + rval = res; + goto gpd_error_out; + } + + pd = (struct port_database_24xx *)sp->u.iocb_cmd.u.mbx.in; + + /* Check for logged in state. */ + if (pd->current_login_state != PDS_PRLI_COMPLETE && + pd->last_login_state != PDS_PRLI_COMPLETE) { + ql_dbg(ql_dbg_mbx, vha, 0xffff, + "Unable to verify login-state (%x/%x) for " + "loop_id %x.\n", pd->current_login_state, + pd->last_login_state, fcport->loop_id); + rval = QLA_FUNCTION_FAILED; + goto gpd_error_out; + } + + if (fcport->loop_id == FC_NO_LOOP_ID || + (memcmp(fcport->port_name, (uint8_t *)&zero, 8) && + memcmp(fcport->port_name, pd->port_name, 8))) { + /* We lost the device mid way. */ + rval = QLA_NOT_LOGGED_IN; + goto gpd_error_out; + } + + /* Names are little-endian. */ + memcpy(fcport->node_name, pd->node_name, WWN_SIZE); + + /* Get port_id of device. */ + fcport->d_id.b.domain = pd->port_id[0]; + fcport->d_id.b.area = pd->port_id[1]; + fcport->d_id.b.al_pa = pd->port_id[2]; + fcport->d_id.b.rsvd_1 = 0; + + /* If not target must be initiator or unknown type. */ + if ((pd->prli_svc_param_word_3[0] & BIT_4) == 0) + fcport->port_type = FCT_INITIATOR; + else + fcport->port_type = FCT_TARGET; + + /* Passback COS information. */ + fcport->supported_classes = (pd->flags & PDF_CLASS_2) ? + FC_COS_CLASS2 : FC_COS_CLASS3; + + if (pd->prli_svc_param_word_3[0] & BIT_7) { + fcport->flags |= FCF_CONF_COMP_SUPPORTED; + fcport->conf_compl_supported = 1; + } + +gpd_error_out: + memset(&ea, 0, sizeof(ea)); + ea.event = FCME_GPDB_DONE; + ea.rc = rval; + ea.fcport = fcport; + ea.sp = sp; + + qla2x00_fcport_event_handler(vha, &ea); + + dma_pool_free(ha->s_dma_pool, sp->u.iocb_cmd.u.mbx.in, + sp->u.iocb_cmd.u.mbx.in_dma); + + sp->free(sp); +} + +static int qla24xx_post_gpdb_work(struct scsi_qla_host *vha, fc_port_t *fcport, + u8 opt) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_GPDB); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + e->u.fcport.opt = opt; + return qla2x00_post_work(vha, e); +} + +int qla24xx_async_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt) +{ + srb_t *sp; + struct srb_iocb *mbx; + int rval = QLA_FUNCTION_FAILED; + u16 *mb; + dma_addr_t pd_dma; + struct port_database_24xx *pd; + struct qla_hw_data *ha = vha->hw; + + if (!vha->flags.online) + goto done; + + fcport->flags |= FCF_ASYNC_SENT; + fcport->disc_state = DSC_GPDB; + + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma); + if (pd == NULL) { + ql_log(ql_log_warn, vha, 0xffff, + "Failed to allocate port database structure.\n"); + goto done_free_sp; + } + memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE)); + + sp->type = SRB_MB_IOCB; + sp->name = "gpdb"; + sp->gen1 = fcport->rscn_gen; + sp->gen2 = fcport->login_gen; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); + + mb = sp->u.iocb_cmd.u.mbx.out_mb; + mb[0] = MBC_GET_PORT_DATABASE; + mb[1] = fcport->loop_id; + mb[2] = MSW(pd_dma); + mb[3] = LSW(pd_dma); + mb[6] = MSW(MSD(pd_dma)); + mb[7] = LSW(MSD(pd_dma)); + mb[9] = vha->vp_idx; + mb[10] = opt; + + mbx = &sp->u.iocb_cmd; + mbx->timeout = qla2x00_async_iocb_timeout; + mbx->u.mbx.in = (void *)pd; + mbx->u.mbx.in_dma = pd_dma; + + sp->done = qla24xx_async_gpdb_sp_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async-%s %8phC hndl %x opt %x\n", + sp->name, fcport->port_name, sp->handle, opt); + + return rval; + +done_free_sp: + if (pd) + dma_pool_free(ha->s_dma_pool, pd, pd_dma); + + sp->free(sp); +done: + fcport->flags &= ~FCF_ASYNC_SENT; + qla24xx_post_gpdb_work(vha, fcport, opt); + return rval; +} + +static +void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) +{ + int rval = ea->rc; + fc_port_t *fcport = ea->fcport; + unsigned long flags; + + fcport->flags &= ~FCF_ASYNC_SENT; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC DS %d LS %d rval %d\n", __func__, fcport->port_name, + fcport->disc_state, fcport->fw_login_state, rval); + + if (ea->sp->gen2 != fcport->login_gen) { + /* target side must have changed it. */ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC generation changed rscn %d|%d login %d|%d \n", + __func__, fcport->port_name, fcport->last_rscn_gen, + fcport->rscn_gen, fcport->last_login_gen, + fcport->login_gen); + return; + } else if (ea->sp->gen1 != fcport->rscn_gen) { + ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC post gidpn\n", + __func__, __LINE__, fcport->port_name); + qla24xx_post_gidpn_work(vha, fcport); + return; + } + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC post del sess\n", + __func__, __LINE__, fcport->port_name); + qlt_schedule_sess_for_deletion_lock(fcport); + return; + } + + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + ea->fcport->login_gen++; + ea->fcport->deleted = 0; + ea->fcport->logout_on_delete = 1; + + if (!ea->fcport->login_succ && !IS_SW_RESV_ADDR(ea->fcport->d_id)) { + vha->fcport_count++; + ea->fcport->login_succ = 1; + + if (!IS_IIDMA_CAPABLE(vha->hw) || + !vha->hw->flags.gpsc_supported) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post upd_fcport fcp_cnt %d\n", + __func__, __LINE__, fcport->port_name, + vha->fcport_count); + + qla24xx_post_upd_fcport_work(vha, fcport); + } else { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpsc fcp_cnt %d\n", + __func__, __LINE__, fcport->port_name, + vha->fcport_count); + + qla24xx_post_gpsc_work(vha, fcport); + } + } + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); +} /* gpdb event */ + +int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + if (fcport->login_retry == 0) + return 0; + + if (fcport->scan_state != QLA_FCPORT_FOUND) + return 0; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d|%d retry %d lid %d\n", + __func__, fcport->port_name, fcport->disc_state, + fcport->fw_login_state, fcport->login_pause, fcport->flags, + fcport->conflict, fcport->last_rscn_gen, fcport->rscn_gen, + fcport->last_login_gen, fcport->login_gen, fcport->login_retry, + fcport->loop_id); + + fcport->login_retry--; + + if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || + (fcport->fw_login_state == DSC_LS_PLOGI_COMP) || + (fcport->fw_login_state == DSC_LS_PRLI_PEND)) + return 0; + + /* for pure Target Mode. Login will not be initiated */ + if (vha->host->active_mode == MODE_TARGET) + return 0; + + if (fcport->flags & FCF_ASYNC_SENT) { + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + return 0; + } + + switch (fcport->disc_state) { + case DSC_DELETED: + if (fcport->loop_id == FC_NO_LOOP_ID) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gnl\n", + __func__, __LINE__, fcport->port_name); + qla24xx_async_gnl(vha, fcport); + } else { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post login\n", + __func__, __LINE__, fcport->port_name); + fcport->disc_state = DSC_LOGIN_PEND; + qla2x00_post_async_login_work(vha, fcport, NULL); + } + break; + + case DSC_GNL: + if (fcport->login_pause) { + fcport->last_rscn_gen = fcport->rscn_gen; + fcport->last_login_gen = fcport->login_gen; + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + break; + } + + if (fcport->flags & FCF_FCP2_DEVICE) { + u8 opt = PDO_FORCE_ADISC; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpdb\n", + __func__, __LINE__, fcport->port_name); + + fcport->disc_state = DSC_GPDB; + qla24xx_post_gpdb_work(vha, fcport, opt); + } else { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post login \n", + __func__, __LINE__, fcport->port_name); + fcport->disc_state = DSC_LOGIN_PEND; + qla2x00_post_async_login_work(vha, fcport, NULL); + } + + break; + + case DSC_LOGIN_FAILED: + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gidpn \n", + __func__, __LINE__, fcport->port_name); + + qla24xx_post_gidpn_work(vha, fcport); + break; + + case DSC_LOGIN_COMPLETE: + /* recheck login state */ + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpdb \n", + __func__, __LINE__, fcport->port_name); + + qla24xx_post_gpdb_work(vha, fcport, PDO_FORCE_ADISC); + break; + + default: + break; + } + + return 0; +} + +static +void qla24xx_handle_rscn_event(fc_port_t *fcport, struct event_arg *ea) +{ + fcport->rscn_gen++; + + ql_dbg(ql_dbg_disc, fcport->vha, 0xffff, + "%s %8phC DS %d LS %d\n", + __func__, fcport->port_name, fcport->disc_state, + fcport->fw_login_state); + + if (fcport->flags & FCF_ASYNC_SENT) + return; + + switch (fcport->disc_state) { + case DSC_DELETED: + case DSC_LOGIN_COMPLETE: + qla24xx_post_gidpn_work(fcport->vha, fcport); + break; + + default: + break; + } +} + +int qla24xx_post_newsess_work(struct scsi_qla_host *vha, port_id_t *id, + u8 *port_name, void *pla) +{ + struct qla_work_evt *e; + e = qla2x00_alloc_work(vha, QLA_EVT_NEW_SESS); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.new_sess.id = *id; + e->u.new_sess.pla = pla; + memcpy(e->u.new_sess.port_name, port_name, WWN_SIZE); + + return qla2x00_post_work(vha, e); +} + +static +int qla24xx_handle_delete_done_event(scsi_qla_host_t *vha, + struct event_arg *ea) +{ + fc_port_t *fcport = ea->fcport; + + if (test_bit(UNLOADING, &vha->dpc_flags)) + return 0; + + switch (vha->host->active_mode) { + case MODE_INITIATOR: + case MODE_DUAL: + if (fcport->scan_state == QLA_FCPORT_FOUND) + qla24xx_fcport_handle_login(vha, fcport); + break; + + case MODE_TARGET: + default: + /* no-op */ + break; + } + + return 0; +} + +static +void qla24xx_handle_relogin_event(scsi_qla_host_t *vha, + struct event_arg *ea) +{ + fc_port_t *fcport = ea->fcport; + + if (fcport->scan_state != QLA_FCPORT_FOUND) { + fcport->login_retry++; + return; + } + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phC DS %d LS %d P %d del %d cnfl %p rscn %d|%d login %d|%d fl %x\n", + __func__, fcport->port_name, fcport->disc_state, + fcport->fw_login_state, fcport->login_pause, + fcport->deleted, fcport->conflict, + fcport->last_rscn_gen, fcport->rscn_gen, + fcport->last_login_gen, fcport->login_gen, + fcport->flags); + + if ((fcport->fw_login_state == DSC_LS_PLOGI_PEND) || + (fcport->fw_login_state == DSC_LS_PLOGI_COMP) || + (fcport->fw_login_state == DSC_LS_PRLI_PEND)) + return; + + if (fcport->flags & FCF_ASYNC_SENT) { + fcport->login_retry++; + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + return; + } + + if (fcport->disc_state == DSC_DELETE_PEND) { + fcport->login_retry++; + return; + } + + if (fcport->last_rscn_gen != fcport->rscn_gen) { + ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC post gidpn\n", + __func__, __LINE__, fcport->port_name); + + qla24xx_async_gidpn(vha, fcport); + return; + } + + qla24xx_fcport_handle_login(vha, fcport); +} + +void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) +{ + fc_port_t *fcport, *f, *tf; + uint32_t id = 0, mask, rid; + int rc; + + switch (ea->event) { + case FCME_RELOGIN: + if (test_bit(UNLOADING, &vha->dpc_flags)) + return; + + qla24xx_handle_relogin_event(vha, ea); + break; + case FCME_RSCN: + if (test_bit(UNLOADING, &vha->dpc_flags)) + return; + switch (ea->id.b.rsvd_1) { + case RSCN_PORT_ADDR: + fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1); + if (!fcport) { + /* cable moved */ + rc = qla24xx_post_gpnid_work(vha, &ea->id); + if (rc) { + ql_log(ql_log_warn, vha, 0xffff, + "RSCN GPNID work failed %02x%02x%02x\n", + ea->id.b.domain, ea->id.b.area, + ea->id.b.al_pa); + } + } else { + ea->fcport = fcport; + qla24xx_handle_rscn_event(fcport, ea); + } + break; + case RSCN_AREA_ADDR: + case RSCN_DOM_ADDR: + if (ea->id.b.rsvd_1 == RSCN_AREA_ADDR) { + mask = 0xffff00; + ql_log(ql_dbg_async, vha, 0xffff, + "RSCN: Area 0x%06x was affected\n", + ea->id.b24); + } else { + mask = 0xff0000; + ql_log(ql_dbg_async, vha, 0xffff, + "RSCN: Domain 0x%06x was affected\n", + ea->id.b24); + } + + rid = ea->id.b24 & mask; + list_for_each_entry_safe(f, tf, &vha->vp_fcports, + list) { + id = f->d_id.b24 & mask; + if (rid == id) { + ea->fcport = f; + qla24xx_handle_rscn_event(f, ea); + } + } + break; + case RSCN_FAB_ADDR: + default: + ql_log(ql_log_warn, vha, 0xffff, + "RSCN: Fabric was affected. Addr format %d\n", + ea->id.b.rsvd_1); + qla2x00_mark_all_devices_lost(vha, 1); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + } + break; + case FCME_GIDPN_DONE: + qla24xx_handle_gidpn_event(vha, ea); + break; + case FCME_GNL_DONE: + qla24xx_handle_gnl_done_event(vha, ea); + break; + case FCME_GPSC_DONE: + qla24xx_post_upd_fcport_work(vha, ea->fcport); + break; + case FCME_PLOGI_DONE: /* Initiator side sent LLIOCB */ + qla24xx_handle_plogi_done_event(vha, ea); + break; + case FCME_GPDB_DONE: + qla24xx_handle_gpdb_event(vha, ea); + break; + case FCME_GPNID_DONE: + qla24xx_handle_gpnid_event(vha, ea); + break; + case FCME_DELETE_DONE: + qla24xx_handle_delete_done_event(vha, ea); + break; + default: + BUG_ON(1); + break; + } +} + static void qla2x00_tmf_iocb_timeout(void *data) { - srb_t *sp = (srb_t *)data; + srb_t *sp = data; struct srb_iocb *tmf = &sp->u.iocb_cmd; tmf->u.tmf.comp_status = CS_TIMEOUT; @@ -287,10 +1172,11 @@ qla2x00_tmf_iocb_timeout(void *data) } static void -qla2x00_tmf_sp_done(void *data, void *ptr, int res) +qla2x00_tmf_sp_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; struct srb_iocb *tmf = &sp->u.iocb_cmd; + complete(&tmf->u.tmf.comp); } @@ -348,7 +1234,7 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun, } done_free_sp: - sp->free(vha, sp); + sp->free(sp); done: return rval; } @@ -356,7 +1242,7 @@ done: static void qla24xx_abort_iocb_timeout(void *data) { - srb_t *sp = (srb_t *)data; + srb_t *sp = data; struct srb_iocb *abt = &sp->u.iocb_cmd; abt->u.abt.comp_status = CS_TIMEOUT; @@ -364,9 +1250,9 @@ qla24xx_abort_iocb_timeout(void *data) } static void -qla24xx_abort_sp_done(void *data, void *ptr, int res) +qla24xx_abort_sp_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; struct srb_iocb *abt = &sp->u.iocb_cmd; complete(&abt->u.abt.comp); @@ -375,7 +1261,7 @@ qla24xx_abort_sp_done(void *data, void *ptr, int res) static int qla24xx_async_abort_cmd(srb_t *cmd_sp) { - scsi_qla_host_t *vha = cmd_sp->fcport->vha; + scsi_qla_host_t *vha = cmd_sp->vha; fc_port_t *fcport = cmd_sp->fcport; struct srb_iocb *abt_iocb; srb_t *sp; @@ -408,7 +1294,7 @@ qla24xx_async_abort_cmd(srb_t *cmd_sp) QLA_SUCCESS : QLA_FUNCTION_FAILED; done_free_sp: - sp->free(vha, sp); + sp->free(sp); done: return rval; } @@ -441,59 +1327,65 @@ qla24xx_async_abort_command(srb_t *sp) return qla24xx_async_abort_cmd(sp); } -void -qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport, - uint16_t *data) +static void +qla24xx_handle_plogi_done_event(struct scsi_qla_host *vha, struct event_arg *ea) { - int rval; + port_id_t cid; /* conflict Nport id */ - switch (data[0]) { + switch (ea->data[0]) { case MBS_COMMAND_COMPLETE: /* * Driver must validate login state - If PRLI not complete, * force a relogin attempt via implicit LOGO, PLOGI, and PRLI * requests. */ - rval = qla2x00_get_port_database(vha, fcport, 0); - if (rval == QLA_NOT_LOGGED_IN) { - fcport->flags &= ~FCF_ASYNC_SENT; - fcport->flags |= FCF_LOGIN_NEEDED; - set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - break; - } - - if (rval != QLA_SUCCESS) { - qla2x00_post_async_logout_work(vha, fcport, NULL); - qla2x00_post_async_login_work(vha, fcport, NULL); - break; - } - if (fcport->flags & FCF_FCP2_DEVICE) { - qla2x00_post_async_adisc_work(vha, fcport, data); - break; - } - qla2x00_update_fcport(vha, fcport); + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpdb\n", + __func__, __LINE__, ea->fcport->port_name); + ea->fcport->chip_reset = vha->hw->chip_reset; + ea->fcport->logout_on_delete = 1; + qla24xx_post_gpdb_work(vha, ea->fcport, 0); break; case MBS_COMMAND_ERROR: - fcport->flags &= ~FCF_ASYNC_SENT; - if (data[1] & QLA_LOGIO_LOGIN_RETRIED) + ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %d %8phC cmd error %x\n", + __func__, __LINE__, ea->fcport->port_name, ea->data[1]); + + ea->fcport->flags &= ~FCF_ASYNC_SENT; + ea->fcport->disc_state = DSC_LOGIN_FAILED; + if (ea->data[1] & QLA_LOGIO_LOGIN_RETRIED) set_bit(RELOGIN_NEEDED, &vha->dpc_flags); else - qla2x00_mark_device_lost(vha, fcport, 1, 0); - break; - case MBS_PORT_ID_USED: - fcport->loop_id = data[1]; - qla2x00_post_async_logout_work(vha, fcport, NULL); - qla2x00_post_async_login_work(vha, fcport, NULL); + qla2x00_mark_device_lost(vha, ea->fcport, 1, 0); break; case MBS_LOOP_ID_USED: - fcport->loop_id++; - rval = qla2x00_find_new_loop_id(vha, fcport); - if (rval != QLA_SUCCESS) { - fcport->flags &= ~FCF_ASYNC_SENT; - qla2x00_mark_device_lost(vha, fcport, 1, 0); - break; + /* data[1] = IO PARAM 1 = nport ID */ + cid.b.domain = (ea->iop[1] >> 16) & 0xff; + cid.b.area = (ea->iop[1] >> 8) & 0xff; + cid.b.al_pa = ea->iop[1] & 0xff; + cid.b.rsvd_1 = 0; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC LoopID 0x%x in use post gnl\n", + __func__, __LINE__, ea->fcport->port_name, + ea->fcport->loop_id); + + if (IS_SW_RESV_ADDR(cid)) { + set_bit(ea->fcport->loop_id, vha->hw->loop_id_map); + ea->fcport->loop_id = FC_NO_LOOP_ID; + } else { + qla2x00_clear_loop_id(ea->fcport); } - qla2x00_post_async_login_work(vha, fcport, NULL); + qla24xx_post_gnl_work(vha, ea->fcport); + break; + case MBS_PORT_ID_USED: + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC NPortId %02x%02x%02x inuse post gidpn\n", + __func__, __LINE__, ea->fcport->port_name, + ea->fcport->d_id.b.domain, ea->fcport->d_id.b.area, + ea->fcport->d_id.b.al_pa); + + qla2x00_clear_loop_id(ea->fcport); + qla24xx_post_gidpn_work(vha, ea->fcport); break; } return; @@ -503,10 +1395,9 @@ void qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) { - /* Don't re-login in target mode */ - if (!fcport->tgt_session) - qla2x00_mark_device_lost(vha, fcport, 1, 0); + qla2x00_mark_device_lost(vha, fcport, 1, 0); qlt_logo_completion_handler(fcport, data[0]); + fcport->login_gen++; return; } @@ -709,7 +1600,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) } } - if (qla_ini_mode_enabled(vha)) + if (qla_ini_mode_enabled(vha) || qla_dual_mode_enabled(vha)) rval = qla2x00_init_rings(vha); ha->flags.chip_reset_done = 1; @@ -2088,6 +2979,21 @@ qla24xx_update_fw_options(scsi_qla_host_t *vha) __func__, ha->fw_options[2]); } + /* Move PUREX, ABTS RX & RIDA to ATIOQ */ + if (ql2xmvasynctoatio) { + if (qla_tgt_mode_enabled(vha) || + qla_dual_mode_enabled(vha)) + ha->fw_options[2] |= BIT_11; + else + ha->fw_options[2] &= ~BIT_11; + } + + ql_dbg(ql_dbg_init, vha, 0xffff, + "%s, add FW options 1-3 = 0x%04x 0x%04x 0x%04x mode %x\n", + __func__, ha->fw_options[1], ha->fw_options[2], + ha->fw_options[3], vha->host->active_mode); + qla2x00_set_fw_options(vha, ha->fw_options); + /* Update Serial Link options. */ if ((le16_to_cpu(ha->fw_seriallink_options24[0]) & BIT_0) == 0) return; @@ -2968,8 +3874,14 @@ qla2x00_rport_del(void *data) rport = fcport->drport ? fcport->drport: fcport->rport; fcport->drport = NULL; spin_unlock_irqrestore(fcport->vha->host->host_lock, flags); - if (rport) + if (rport) { + ql_dbg(ql_dbg_disc, fcport->vha, 0xffff, + "%s %8phN. rport %p roles %x \n", + __func__, fcport->port_name, rport, + rport->roles); + fc_remote_port_delete(rport); + } } /** @@ -2995,9 +3907,42 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED); fcport->supported_classes = FC_COS_UNSPECIFIED; + fcport->ct_desc.ct_sns = dma_alloc_coherent(&vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), &fcport->ct_desc.ct_sns_dma, + flags); + fcport->disc_state = DSC_DELETED; + fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; + fcport->deleted = QLA_SESS_DELETED; + fcport->login_retry = vha->hw->login_retry_count; + fcport->login_retry = 5; + fcport->logout_on_delete = 1; + + if (!fcport->ct_desc.ct_sns) { + ql_log(ql_log_warn, vha, 0xffff, + "Failed to allocate ct_sns request.\n"); + kfree(fcport); + fcport = NULL; + } + INIT_WORK(&fcport->del_work, qla24xx_delete_sess_fn); + INIT_LIST_HEAD(&fcport->gnl_entry); + INIT_LIST_HEAD(&fcport->list); + return fcport; } +void +qla2x00_free_fcport(fc_port_t *fcport) +{ + if (fcport->ct_desc.ct_sns) { + dma_free_coherent(&fcport->vha->hw->pdev->dev, + sizeof(struct ct_sns_pkt), fcport->ct_desc.ct_sns, + fcport->ct_desc.ct_sns_dma); + + fcport->ct_desc.ct_sns = NULL; + } + kfree(fcport); +} + /* * qla2x00_configure_loop * Updates Fibre Channel Device Database with what is actually on loop. @@ -3055,10 +4000,11 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) } else if (ha->current_topology == ISP_CFG_N) { clear_bit(RSCN_UPDATE, &flags); - + } else if (ha->current_topology == ISP_CFG_NL) { + clear_bit(RSCN_UPDATE, &flags); + set_bit(LOCAL_LOOP_UPDATE, &flags); } else if (!vha->flags.online || (test_bit(ABORT_ISP_ACTIVE, &flags))) { - set_bit(RSCN_UPDATE, &flags); set_bit(LOCAL_LOOP_UPDATE, &flags); } @@ -3095,7 +4041,8 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) * Process any ATIO queue entries that came in * while we weren't online. */ - if (qla_tgt_mode_enabled(vha)) { + if (qla_tgt_mode_enabled(vha) || + qla_dual_mode_enabled(vha)) { if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) { spin_lock_irqsave(&ha->tgt.atio_lock, flags); @@ -3159,6 +4106,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) uint16_t loop_id; uint8_t domain, area, al_pa; struct qla_hw_data *ha = vha->hw; + unsigned long flags; found_devs = 0; new_fcport = NULL; @@ -3199,7 +4147,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) "Marking port lost loop_id=0x%04x.\n", fcport->loop_id); - qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); + qla2x00_mark_device_lost(vha, fcport, 0, 0); } } @@ -3230,13 +4178,14 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) if (loop_id > LAST_LOCAL_LOOP_ID) continue; - memset(new_fcport, 0, sizeof(fc_port_t)); + memset(new_fcport->port_name, 0, WWN_SIZE); /* Fill in member data. */ new_fcport->d_id.b.domain = domain; new_fcport->d_id.b.area = area; new_fcport->d_id.b.al_pa = al_pa; new_fcport->loop_id = loop_id; + rval2 = qla2x00_get_port_database(vha, new_fcport, 0); if (rval2 != QLA_SUCCESS) { ql_dbg(ql_dbg_disc, vha, 0x201a, @@ -3249,6 +4198,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) continue; } + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); /* Check for matching device in port list. */ found = 0; fcport = NULL; @@ -3264,6 +4214,12 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) memcpy(fcport->node_name, new_fcport->node_name, WWN_SIZE); + if (!fcport->login_succ) { + vha->fcport_count++; + fcport->login_succ = 1; + fcport->disc_state = DSC_LOGIN_COMPLETE; + } + found++; break; } @@ -3274,16 +4230,28 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) /* Allocate a new replacement fcport. */ fcport = new_fcport; + if (!fcport->login_succ) { + vha->fcport_count++; + fcport->login_succ = 1; + fcport->disc_state = DSC_LOGIN_COMPLETE; + } + + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (new_fcport == NULL) { ql_log(ql_log_warn, vha, 0x201c, "Failed to allocate memory for fcport.\n"); rval = QLA_MEMORY_ALLOC_FAILED; goto cleanup_allocation; } + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); new_fcport->flags &= ~FCF_FABRIC_DEVICE; } + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + /* Base iIDMA settings on HBA port speed. */ fcport->fp_speed = ha->link_data_rate; @@ -3334,6 +4302,7 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) } } +/* qla2x00_reg_remote_port is reserved for Initiator Mode only.*/ static void qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) { @@ -3352,12 +4321,6 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) "Unable to allocate fc remote port.\n"); return; } - /* - * Create target mode FC NEXUS in qla_target.c if target mode is - * enabled.. - */ - - qlt_fc_port_added(vha, fcport); spin_lock_irqsave(fcport->vha->host->host_lock, flags); *((fc_port_t **)rport->dd_data) = fcport; @@ -3370,6 +4333,12 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; if (fcport->port_type == FCT_TARGET) rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %8phN. rport %p is %s mode \n", + __func__, fcport->port_name, rport, + (fcport->port_type == FCT_TARGET) ? "tgt" : "ini"); + fc_remote_port_rolechg(rport, rport_ids.roles); } @@ -3393,25 +4362,44 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) { fcport->vha = vha; + if (IS_SW_RESV_ADDR(fcport->d_id)) + return; + + ql_dbg(ql_dbg_disc, vha, 0xffff, "%s %8phC \n", + __func__, fcport->port_name); + if (IS_QLAFX00(vha->hw)) { qla2x00_set_fcport_state(fcport, FCS_ONLINE); goto reg_port; } fcport->login_retry = 0; fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); + fcport->disc_state = DSC_LOGIN_COMPLETE; + fcport->deleted = 0; + fcport->logout_on_delete = 1; qla2x00_set_fcport_state(fcport, FCS_ONLINE); qla2x00_iidma_fcport(vha, fcport); qla24xx_update_fcport_fcp_prio(vha, fcport); reg_port: - if (qla_ini_mode_enabled(vha)) + switch (vha->host->active_mode) { + case MODE_INITIATOR: qla2x00_reg_remote_port(vha, fcport); - else { - /* - * Create target mode FC NEXUS in qla_target.c - */ - qlt_fc_port_added(vha, fcport); + break; + case MODE_TARGET: + if (!vha->vha_tgt.qla_tgt->tgt_stop && + !vha->vha_tgt.qla_tgt->tgt_stopped) + qlt_fc_port_added(vha, fcport); + break; + case MODE_DUAL: + qla2x00_reg_remote_port(vha, fcport); + if (!vha->vha_tgt.qla_tgt->tgt_stop && + !vha->vha_tgt.qla_tgt->tgt_stopped) + qlt_fc_port_added(vha, fcport); + break; + default: + break; } } @@ -3430,13 +4418,11 @@ static int qla2x00_configure_fabric(scsi_qla_host_t *vha) { int rval; - fc_port_t *fcport, *fcptemp; - uint16_t next_loopid; + fc_port_t *fcport; uint16_t mb[MAILBOX_REGISTER_COUNT]; uint16_t loop_id; LIST_HEAD(new_fcports); struct qla_hw_data *ha = vha->hw; - struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); int discovery_gen; /* If FL port exists, then SNS is present */ @@ -3454,7 +4440,19 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) } vha->device_flags |= SWITCH_FOUND; + + if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) { + rval = qla2x00_send_change_request(vha, 0x3, 0); + if (rval != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0x121, + "Failed to enable receiving of RSCN requests: 0x%x.\n", + rval); + } + + do { + qla2x00_mgmt_svr_login(vha); + /* FDMI support. */ if (ql2xfdmienable && test_and_clear_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags)) @@ -3501,9 +4499,6 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) } } -#define QLA_FCPORT_SCAN 1 -#define QLA_FCPORT_FOUND 2 - list_for_each_entry(fcport, &vha->vp_fcports, list) { fcport->scan_state = QLA_FCPORT_SCAN; } @@ -3516,174 +4511,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) * will be newer than discovery_gen. */ qlt_do_generation_tick(vha, &discovery_gen); - rval = qla2x00_find_all_fabric_devs(vha, &new_fcports); + rval = qla2x00_find_all_fabric_devs(vha); if (rval != QLA_SUCCESS) break; - - /* - * Logout all previous fabric devices marked lost, except - * FCP2 devices. - */ - list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) - break; - - if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) - continue; - - if (fcport->scan_state == QLA_FCPORT_SCAN) { - if (qla_ini_mode_enabled(base_vha) && - atomic_read(&fcport->state) == FCS_ONLINE) { - qla2x00_mark_device_lost(vha, fcport, - ql2xplogiabsentdevice, 0); - if (fcport->loop_id != FC_NO_LOOP_ID && - (fcport->flags & FCF_FCP2_DEVICE) == 0 && - fcport->port_type != FCT_INITIATOR && - fcport->port_type != FCT_BROADCAST) { - ha->isp_ops->fabric_logout(vha, - fcport->loop_id, - fcport->d_id.b.domain, - fcport->d_id.b.area, - fcport->d_id.b.al_pa); - qla2x00_clear_loop_id(fcport); - } - } else if (!qla_ini_mode_enabled(base_vha)) { - /* - * In target mode, explicitly kill - * sessions and log out of devices - * that are gone, so that we don't - * end up with an initiator using the - * wrong ACL (if the fabric recycles - * an FC address and we have a stale - * session around) and so that we don't - * report initiators that are no longer - * on the fabric. - */ - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf077, - "port gone, logging out/killing session: " - "%8phC state 0x%x flags 0x%x fc4_type 0x%x " - "scan_state %d\n", - fcport->port_name, - atomic_read(&fcport->state), - fcport->flags, fcport->fc4_type, - fcport->scan_state); - qlt_fc_port_deleted(vha, fcport, - discovery_gen); - } - } - } - - /* Starting free loop ID. */ - next_loopid = ha->min_external_loopid; - - /* - * Scan through our port list and login entries that need to be - * logged in. - */ - list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (atomic_read(&vha->loop_down_timer) || - test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) - break; - - if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 || - (fcport->flags & FCF_LOGIN_NEEDED) == 0) - continue; - - /* - * If we're not an initiator, skip looking for devices - * and logging in. There's no reason for us to do it, - * and it seems to actively cause problems in target - * mode if we race with the initiator logging into us - * (we might get the "port ID used" status back from - * our login command and log out the initiator, which - * seems to cause havoc). - */ - if (!qla_ini_mode_enabled(base_vha)) { - if (fcport->scan_state == QLA_FCPORT_FOUND) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf078, - "port %8phC state 0x%x flags 0x%x fc4_type 0x%x " - "scan_state %d (initiator mode disabled; skipping " - "login)\n", fcport->port_name, - atomic_read(&fcport->state), - fcport->flags, fcport->fc4_type, - fcport->scan_state); - } - continue; - } - - if (fcport->loop_id == FC_NO_LOOP_ID) { - fcport->loop_id = next_loopid; - rval = qla2x00_find_new_loop_id( - base_vha, fcport); - if (rval != QLA_SUCCESS) { - /* Ran out of IDs to use */ - break; - } - } - /* Login and update database */ - qla2x00_fabric_dev_login(vha, fcport, &next_loopid); - } - - /* Exit if out of loop IDs. */ - if (rval != QLA_SUCCESS) { - break; - } - - /* - * Login and add the new devices to our port list. - */ - list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) { - if (atomic_read(&vha->loop_down_timer) || - test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) - break; - - /* - * If we're not an initiator, skip looking for devices - * and logging in. There's no reason for us to do it, - * and it seems to actively cause problems in target - * mode if we race with the initiator logging into us - * (we might get the "port ID used" status back from - * our login command and log out the initiator, which - * seems to cause havoc). - */ - if (qla_ini_mode_enabled(base_vha)) { - /* Find a new loop ID to use. */ - fcport->loop_id = next_loopid; - rval = qla2x00_find_new_loop_id(base_vha, - fcport); - if (rval != QLA_SUCCESS) { - /* Ran out of IDs to use */ - break; - } - - /* Login and update database */ - qla2x00_fabric_dev_login(vha, fcport, - &next_loopid); - } else { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf079, - "new port %8phC state 0x%x flags 0x%x fc4_type " - "0x%x scan_state %d (initiator mode disabled; " - "skipping login)\n", - fcport->port_name, - atomic_read(&fcport->state), - fcport->flags, fcport->fc4_type, - fcport->scan_state); - } - - list_move_tail(&fcport->list, &vha->vp_fcports); - } } while (0); - /* Free all new device structures not processed. */ - list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) { - list_del(&fcport->list); - kfree(fcport); - } - - if (rval) { + if (rval) ql_dbg(ql_dbg_disc, vha, 0x2068, "Configure fabric error exit rval=%d.\n", rval); - } return (rval); } @@ -3702,12 +4537,11 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) * Kernel context. */ static int -qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, - struct list_head *new_fcports) +qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha) { int rval; uint16_t loop_id; - fc_port_t *fcport, *new_fcport, *fcptemp; + fc_port_t *fcport, *new_fcport; int found; sw_info_t *swl; @@ -3716,6 +4550,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, port_id_t wrap = {}, nxt_d_id; struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + unsigned long flags; rval = QLA_SUCCESS; @@ -3736,9 +4571,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, swl = NULL; } else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) { swl = NULL; - } else if (ql2xiidmaenable && - qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) { - qla2x00_gpsc(vha, swl); + } else if (qla2x00_gfpn_id(vha, swl) != QLA_SUCCESS) { + swl = NULL; } /* If other queries succeeded probe for FC-4 type */ @@ -3800,11 +4634,6 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ql_log(ql_log_warn, vha, 0x2064, "SNS scan failed -- assuming " "zero-entry result.\n"); - list_for_each_entry_safe(fcport, fcptemp, - new_fcports, list) { - list_del(&fcport->list); - kfree(fcport); - } rval = QLA_SUCCESS; break; } @@ -3847,6 +4676,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, new_fcport->fc4_type != FC4_TYPE_UNKNOWN)) continue; + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + /* Locate matching device in database. */ found = 0; list_for_each_entry(fcport, &vha->vp_fcports, list) { @@ -3869,7 +4700,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, */ if (fcport->d_id.b24 == new_fcport->d_id.b24 && (atomic_read(&fcport->state) == FCS_ONLINE || - !qla_ini_mode_enabled(base_vha))) { + (vha->host->active_mode == MODE_TARGET))) { break; } @@ -3889,7 +4720,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, * Log it out if still logged in and mark it for * relogin later. */ - if (!qla_ini_mode_enabled(base_vha)) { + if (qla_tgt_mode_enabled(base_vha)) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf080, "port changed FC ID, %8phC" " old %x:%x:%x (loop_id 0x%04x)-> new %x:%x:%x\n", @@ -3907,25 +4738,19 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, fcport->d_id.b24 = new_fcport->d_id.b24; fcport->flags |= FCF_LOGIN_NEEDED; - if (fcport->loop_id != FC_NO_LOOP_ID && - (fcport->flags & FCF_FCP2_DEVICE) == 0 && - (fcport->flags & FCF_ASYNC_SENT) == 0 && - fcport->port_type != FCT_INITIATOR && - fcport->port_type != FCT_BROADCAST) { - ha->isp_ops->fabric_logout(vha, fcport->loop_id, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa); - qla2x00_clear_loop_id(fcport); - } - break; } - if (found) + if (found) { + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); continue; + } /* If device was not in our fcports list, then add it. */ new_fcport->scan_state = QLA_FCPORT_FOUND; - list_add_tail(&new_fcport->list, new_fcports); + list_add_tail(&new_fcport->list, &vha->vp_fcports); + + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + /* Allocate a new replacement fcport. */ nxt_d_id.b24 = new_fcport->d_id.b24; @@ -3939,8 +4764,44 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, new_fcport->d_id.b24 = nxt_d_id.b24; } - kfree(new_fcport); + qla2x00_free_fcport(new_fcport); + /* + * Logout all previous fabric dev marked lost, except FCP2 devices. + */ + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) + break; + + if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 || + (fcport->flags & FCF_LOGIN_NEEDED) == 0) + continue; + + if (fcport->scan_state == QLA_FCPORT_SCAN) { + if ((qla_dual_mode_enabled(vha) || + qla_ini_mode_enabled(vha)) && + atomic_read(&fcport->state) == FCS_ONLINE) { + qla2x00_mark_device_lost(vha, fcport, + ql2xplogiabsentdevice, 0); + if (fcport->loop_id != FC_NO_LOOP_ID && + (fcport->flags & FCF_FCP2_DEVICE) == 0 && + fcport->port_type != FCT_INITIATOR && + fcport->port_type != FCT_BROADCAST) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", + __func__, __LINE__, + fcport->port_name); + + qlt_schedule_sess_for_deletion_lock + (fcport); + continue; + } + } + } + + if (fcport->scan_state == QLA_FCPORT_FOUND) + qla24xx_fcport_handle_login(vha, fcport); + } return (rval); } @@ -3992,64 +4853,6 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev) return (rval); } -/* - * qla2x00_fabric_dev_login - * Login fabric target device and update FC port database. - * - * Input: - * ha: adapter state pointer. - * fcport: port structure list pointer. - * next_loopid: contains value of a new loop ID that can be used - * by the next login attempt. - * - * Returns: - * qla2x00 local function return status code. - * - * Context: - * Kernel context. - */ -static int -qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport, - uint16_t *next_loopid) -{ - int rval; - uint8_t opts; - struct qla_hw_data *ha = vha->hw; - - rval = QLA_SUCCESS; - - if (IS_ALOGIO_CAPABLE(ha)) { - if (fcport->flags & FCF_ASYNC_SENT) - return rval; - fcport->flags |= FCF_ASYNC_SENT; - rval = qla2x00_post_async_login_work(vha, fcport, NULL); - if (!rval) - return rval; - } - - fcport->flags &= ~FCF_ASYNC_SENT; - rval = qla2x00_fabric_login(vha, fcport, next_loopid); - if (rval == QLA_SUCCESS) { - /* Send an ADISC to FCP2 devices.*/ - opts = 0; - if (fcport->flags & FCF_FCP2_DEVICE) - opts |= BIT_1; - rval = qla2x00_get_port_database(vha, fcport, opts); - if (rval != QLA_SUCCESS) { - ha->isp_ops->fabric_logout(vha, fcport->loop_id, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa); - qla2x00_mark_device_lost(vha, fcport, 1, 0); - } else { - qla2x00_update_fcport(vha, fcport); - } - } else { - /* Retry Login. */ - qla2x00_mark_device_lost(vha, fcport, 1, 0); - } - - return (rval); -} /* * qla2x00_fabric_login @@ -4341,13 +5144,6 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) spin_unlock_irqrestore(&ha->vport_slock, flags); qla2x00_rport_del(fcport); - /* - * Release the target mode FC NEXUS in - * qla_target.c, if target mod is enabled. - */ - qlt_fc_port_deleted(vha, fcport, - base_vha->total_fcport_update_gen); - spin_lock_irqsave(&ha->vport_slock, flags); } } @@ -4730,6 +5526,8 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) if (!(IS_P3P_TYPE(ha))) ha->isp_ops->reset_chip(vha); + ha->chip_reset++; + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); if (atomic_read(&vha->loop_state) != LOOP_DOWN) { atomic_set(&vha->loop_state, LOOP_DOWN); @@ -4784,8 +5582,6 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) /* Requeue all commands in outstanding command list. */ qla2x00_abort_all_cmds(vha, DID_RESET << 16); } - - ha->chip_reset++; /* memory barrier */ wmb(); } @@ -4981,7 +5777,6 @@ qla2x00_restart_isp(scsi_qla_host_t *vha) if (!status) { /* Issue a marker after FW becomes ready. */ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); - set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); } @@ -5209,7 +6004,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) rval = 1; } - if (!qla_ini_mode_enabled(vha)) { + if (qla_tgt_mode_enabled(vha)) { /* Don't enable full login after initial LIP */ nv->firmware_options_1 &= cpu_to_le32(~BIT_13); /* Don't enable LIP full login for initiator */ @@ -5400,6 +6195,7 @@ uint8_t qla27xx_find_valid_image(struct scsi_qla_host *vha) for (chksum = 0; cnt--; wptr++) chksum += le32_to_cpu(*wptr); + if (chksum) { ql_dbg(ql_dbg_init, vha, 0x018c, "Checksum validation failed for primary image (0x%x)\n", @@ -5669,7 +6465,7 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Validate firmware image by checking version. */ if (blob->fw->size < 8 * sizeof(uint16_t)) { ql_log(ql_log_fatal, vha, 0x0085, - "Unable to verify integrity of firmware image (%Zd).\n", + "Unable to verify integrity of firmware image (%zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -5697,7 +6493,7 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) if (blob->fw->size < fwclen) { ql_log(ql_log_fatal, vha, 0x0088, "Unable to verify integrity of firmware image " - "(%Zd).\n", blob->fw->size); + "(%zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -5778,7 +6574,7 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Validate firmware image by checking version. */ if (blob->fw->size < 8 * sizeof(uint32_t)) { ql_log(ql_log_fatal, vha, 0x0093, - "Unable to verify integrity of firmware image (%Zd).\n", + "Unable to verify integrity of firmware image (%zd).\n", blob->fw->size); return QLA_FUNCTION_FAILED; } @@ -5789,7 +6585,7 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { ql_log(ql_log_fatal, vha, 0x0094, - "Unable to verify integrity of firmware image (%Zd).\n", + "Unable to verify integrity of firmware image (%zd).\n", blob->fw->size); ql_log(ql_log_fatal, vha, 0x0095, "Firmware data: %08x %08x %08x %08x.\n", @@ -5807,7 +6603,7 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) if (blob->fw->size < fwclen) { ql_log(ql_log_fatal, vha, 0x0096, "Unable to verify integrity of firmware image " - "(%Zd).\n", blob->fw->size); + "(%zd).\n", blob->fw->size); return QLA_FUNCTION_FAILED; } @@ -6412,6 +7208,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) vha->flags.process_response_queue = 1; } + /* enable RIDA Format2 */ + if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) + icb->firmware_options_3 |= BIT_0; + if (rval) { ql_log(ql_log_warn, vha, 0x0076, "NVRAM configuration failed.\n"); @@ -6536,13 +7336,26 @@ qla81xx_update_fw_options(scsi_qla_host_t *vha) __func__, ha->fw_options[2]); } - if (!ql2xetsenable) - goto out; + /* Move PUREX, ABTS RX & RIDA to ATIOQ */ + if (ql2xmvasynctoatio) { + if (qla_tgt_mode_enabled(vha) || + qla_dual_mode_enabled(vha)) + ha->fw_options[2] |= BIT_11; + else + ha->fw_options[2] &= ~BIT_11; + } + + if (ql2xetsenable) { + /* Enable ETS Burst. */ + memset(ha->fw_options, 0, sizeof(ha->fw_options)); + ha->fw_options[2] |= BIT_9; + } + + ql_dbg(ql_dbg_init, vha, 0xffff, + "%s, add FW options 1-3 = 0x%04x 0x%04x 0x%04x mode %x\n", + __func__, ha->fw_options[1], ha->fw_options[2], + ha->fw_options[3], vha->host->active_mode); - /* Enable ETS Burst. */ - memset(ha->fw_options, 0, sizeof(ha->fw_options)); - ha->fw_options[2] |= BIT_9; -out: qla2x00_set_fw_options(vha, ha->fw_options); } @@ -6748,6 +7561,7 @@ struct qla_qpair *qla2xxx_create_qpair(struct scsi_qla_host *vha, int qos, int v memset(qpair, 0, sizeof(struct qla_qpair)); qpair->hw = vha->hw; + qpair->vha = vha; /* Assign available que pair id */ mutex_lock(&ha->mq_lock); diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 44e404583c86..66df6cec59da 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -166,8 +166,8 @@ qla2x00_set_fcport_state(fc_port_t *fcport, int state) /* Don't print state transitions during initial allocation of fcport */ if (old_state && old_state != state) { ql_dbg(ql_dbg_disc, fcport->vha, 0x207d, - "FCPort state transitioned from %s to %s - " - "portid=%02x%02x%02x.\n", + "FCPort %8phC state transitioned from %s to %s - " + "portid=%02x%02x%02x.\n", fcport->port_name, port_state_str[old_state], port_state_str[state], fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); @@ -232,6 +232,7 @@ qla2xxx_get_qpair_sp(struct qla_qpair *qpair, fc_port_t *fcport, gfp_t flag) memset(sp, 0, sizeof(*sp)); sp->fcport = fcport; sp->iocbs = 1; + sp->vha = qpair->vha; done: if (!sp) QLA_QPAIR_MARK_NOT_BUSY(qpair); @@ -249,20 +250,20 @@ static inline srb_t * qla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag) { srb_t *sp = NULL; - struct qla_hw_data *ha = vha->hw; uint8_t bail; QLA_VHA_MARK_BUSY(vha, bail); if (unlikely(bail)) return NULL; - sp = mempool_alloc(ha->srb_mempool, flag); + sp = mempool_alloc(vha->hw->srb_mempool, flag); if (!sp) goto done; memset(sp, 0, sizeof(*sp)); sp->fcport = fcport; sp->iocbs = 1; + sp->vha = vha; done: if (!sp) QLA_VHA_MARK_NOT_BUSY(vha); @@ -270,10 +271,10 @@ done: } static inline void -qla2x00_rel_sp(scsi_qla_host_t *vha, srb_t *sp) +qla2x00_rel_sp(srb_t *sp) { - mempool_free(sp, vha->hw->srb_mempool); - QLA_VHA_MARK_NOT_BUSY(vha); + QLA_VHA_MARK_NOT_BUSY(sp->vha); + mempool_free(sp, sp->vha->hw->srb_mempool); } static inline void @@ -285,8 +286,7 @@ qla2x00_init_timer(srb_t *sp, unsigned long tmo) sp->u.iocb_cmd.timer.function = qla2x00_sp_timeout; add_timer(&sp->u.iocb_cmd.timer); sp->free = qla2x00_sp_free; - if ((IS_QLAFX00(sp->fcport->vha->hw)) && - (sp->type == SRB_FXIOCB_DCMD)) + if (IS_QLAFX00(sp->vha->hw) && (sp->type == SRB_FXIOCB_DCMD)) init_completion(&sp->u.iocb_cmd.u.fxiocb.fxiocb_comp); if (sp->type == SRB_ELS_DCMD) init_completion(&sp->u.iocb_cmd.u.els_logo.comp); diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 58e49a3e1de8..535079280288 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -23,7 +23,7 @@ qla2x00_get_cmd_direction(srb_t *sp) { uint16_t cflags; struct scsi_cmnd *cmd = GET_CMD_SP(sp); - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; cflags = 0; @@ -210,7 +210,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, return; } - vha = sp->fcport->vha; + vha = sp->vha; cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); /* Three DSDs are available in the Command Type 2 IOCB */ @@ -267,7 +267,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, return; } - vha = sp->fcport->vha; + vha = sp->vha; cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); /* Two DSDs are available in the Command Type 3 IOCB */ @@ -324,7 +324,7 @@ qla2x00_start_scsi(srb_t *sp) struct rsp_que *rsp; /* Setup device pointers. */ - vha = sp->fcport->vha; + vha = sp->vha; ha = vha->hw; reg = &ha->iobase->isp; cmd = GET_CMD_SP(sp); @@ -601,7 +601,7 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, return 0; } - vha = sp->fcport->vha; + vha = sp->vha; ha = vha->hw; /* Set transfer direction */ @@ -716,7 +716,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, return; } - vha = sp->fcport->vha; + vha = sp->vha; /* Set transfer direction */ if (cmd->sc_data_direction == DMA_TO_DEVICE) { @@ -1108,7 +1108,7 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, if (sp) { cmd = GET_CMD_SP(sp); sgl = scsi_prot_sglist(cmd); - vha = sp->fcport->vha; + vha = sp->vha; } else if (tc) { vha = tc->vha; sgl = tc->prot_sg; @@ -1215,7 +1215,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, /* Update entry type to indicate Command Type CRC_2 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = cpu_to_le32(COMMAND_TYPE_CRC_2); - vha = sp->fcport->vha; + vha = sp->vha; ha = vha->hw; /* No data transfer */ @@ -1225,7 +1225,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, return QLA_SUCCESS; } - cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + cmd_pkt->vp_index = sp->vha->vp_idx; /* Set transfer direction */ if (cmd->sc_data_direction == DMA_TO_DEVICE) { @@ -1415,7 +1415,7 @@ qla24xx_start_scsi(srb_t *sp) struct req_que *req = NULL; struct rsp_que *rsp = NULL; struct scsi_cmnd *cmd = GET_CMD_SP(sp); - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct qla_hw_data *ha = vha->hw; /* Setup device pointers. */ @@ -1492,7 +1492,7 @@ qla24xx_start_scsi(srb_t *sp) cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + cmd_pkt->vp_index = sp->vha->vp_idx; int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); @@ -1564,7 +1564,7 @@ qla24xx_dif_start_scsi(srb_t *sp) struct req_que *req = NULL; struct rsp_que *rsp = NULL; struct scsi_cmnd *cmd = GET_CMD_SP(sp); - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct qla_hw_data *ha = vha->hw; struct cmd_type_crc_2 *cmd_pkt; uint32_t status = 0; @@ -2214,13 +2214,13 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[2] = sp->fcport->d_id.b.domain; - logio->vp_index = sp->fcport->vha->vp_idx; + logio->vp_index = sp->vha->vp_idx; } static void qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx) { - struct qla_hw_data *ha = sp->fcport->vha->hw; + struct qla_hw_data *ha = sp->vha->hw; struct srb_iocb *lio = &sp->u.iocb_cmd; uint16_t opts; @@ -2238,7 +2238,7 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx) mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain); mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 | sp->fcport->d_id.b.al_pa); - mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); + mbx->mb9 = cpu_to_le16(sp->vha->vp_idx); } static void @@ -2247,20 +2247,20 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->control_flags = cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); - if (!sp->fcport->tgt_session || - !sp->fcport->tgt_session->keep_nport_handle) + if (!sp->fcport->se_sess || + !sp->fcport->keep_nport_handle) logio->control_flags |= cpu_to_le16(LCF_FREE_NPORT); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[2] = sp->fcport->d_id.b.domain; - logio->vp_index = sp->fcport->vha->vp_idx; + logio->vp_index = sp->vha->vp_idx; } static void qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx) { - struct qla_hw_data *ha = sp->fcport->vha->hw; + struct qla_hw_data *ha = sp->vha->hw; mbx->entry_type = MBX_IOCB_TYPE; SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id); @@ -2271,7 +2271,7 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx) mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain); mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 | sp->fcport->d_id.b.al_pa); - mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); + mbx->mb9 = cpu_to_le16(sp->vha->vp_idx); /* Implicit: mbx->mbx10 = 0. */ } @@ -2281,13 +2281,13 @@ qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); - logio->vp_index = sp->fcport->vha->vp_idx; + logio->vp_index = sp->vha->vp_idx; } static void qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx) { - struct qla_hw_data *ha = sp->fcport->vha->hw; + struct qla_hw_data *ha = sp->vha->hw; mbx->entry_type = MBX_IOCB_TYPE; SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id); @@ -2302,7 +2302,7 @@ qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx) mbx->mb3 = cpu_to_le16(LSW(ha->async_pd_dma)); mbx->mb6 = cpu_to_le16(MSW(MSD(ha->async_pd_dma))); mbx->mb7 = cpu_to_le16(LSW(MSD(ha->async_pd_dma))); - mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); + mbx->mb9 = cpu_to_le16(sp->vha->vp_idx); } static void @@ -2338,32 +2338,30 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) } static void -qla2x00_els_dcmd_sp_free(void *ptr, void *data) +qla2x00_els_dcmd_sp_free(void *data) { - struct scsi_qla_host *vha = (scsi_qla_host_t *)ptr; - struct qla_hw_data *ha = vha->hw; - srb_t *sp = (srb_t *)data; + srb_t *sp = data; struct srb_iocb *elsio = &sp->u.iocb_cmd; kfree(sp->fcport); if (elsio->u.els_logo.els_logo_pyld) - dma_free_coherent(&ha->pdev->dev, DMA_POOL_SIZE, + dma_free_coherent(&sp->vha->hw->pdev->dev, DMA_POOL_SIZE, elsio->u.els_logo.els_logo_pyld, elsio->u.els_logo.els_logo_pyld_dma); del_timer(&elsio->timer); - qla2x00_rel_sp(vha, sp); + qla2x00_rel_sp(sp); } static void qla2x00_els_dcmd_iocb_timeout(void *data) { - srb_t *sp = (srb_t *)data; - struct srb_iocb *lio = &sp->u.iocb_cmd; + srb_t *sp = data; fc_port_t *fcport = sp->fcport; - struct scsi_qla_host *vha = fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct qla_hw_data *ha = vha->hw; + struct srb_iocb *lio = &sp->u.iocb_cmd; unsigned long flags = 0; ql_dbg(ql_dbg_io, vha, 0x3069, @@ -2386,12 +2384,12 @@ qla2x00_els_dcmd_iocb_timeout(void *data) } static void -qla2x00_els_dcmd_sp_done(void *data, void *ptr, int res) +qla2x00_els_dcmd_sp_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; fc_port_t *fcport = sp->fcport; struct srb_iocb *lio = &sp->u.iocb_cmd; - struct scsi_qla_host *vha = fcport->vha; + struct scsi_qla_host *vha = sp->vha; ql_dbg(ql_dbg_io, vha, 0x3072, "%s hdl=%x, portid=%02x%02x%02x done\n", @@ -2449,7 +2447,7 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode, GFP_KERNEL); if (!elsio->u.els_logo.els_logo_pyld) { - sp->free(vha, sp); + sp->free(sp); return QLA_FUNCTION_FAILED; } @@ -2468,7 +2466,7 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode, rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) { - sp->free(vha, sp); + sp->free(sp); return QLA_FUNCTION_FAILED; } @@ -2479,14 +2477,14 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode, wait_for_completion(&elsio->u.els_logo.comp); - sp->free(vha, sp); + sp->free(sp); return rval; } static void qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) { - scsi_qla_host_t *vha = sp->fcport->vha; + scsi_qla_host_t *vha = sp->vha; struct srb_iocb *elsio = &sp->u.iocb_cmd; els_iocb->entry_type = ELS_IOCB_TYPE; @@ -2518,7 +2516,7 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->rx_address[1] = 0; els_iocb->rx_len = 0; - sp->fcport->vha->qla_stats.control_requests++; + sp->vha->qla_stats.control_requests++; } static void @@ -2534,7 +2532,7 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->handle = sp->handle; els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); els_iocb->tx_dsd_count = cpu_to_le16(bsg_job->request_payload.sg_cnt); - els_iocb->vp_index = sp->fcport->vha->vp_idx; + els_iocb->vp_index = sp->vha->vp_idx; els_iocb->sof_type = EST_SOFI3; els_iocb->rx_dsd_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt); @@ -2565,7 +2563,7 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->rx_len = cpu_to_le32(sg_dma_len (bsg_job->reply_payload.sg_list)); - sp->fcport->vha->qla_stats.control_requests++; + sp->vha->qla_stats.control_requests++; } static void @@ -2576,7 +2574,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) struct scatterlist *sg; int index; uint16_t tot_dsds; - scsi_qla_host_t *vha = sp->fcport->vha; + scsi_qla_host_t *vha = sp->vha; struct qla_hw_data *ha = vha->hw; struct bsg_job *bsg_job = sp->u.bsg_job; int loop_iterartion = 0; @@ -2642,7 +2640,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) } ct_iocb->entry_count = entry_count; - sp->fcport->vha->qla_stats.control_requests++; + sp->vha->qla_stats.control_requests++; } static void @@ -2653,7 +2651,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) struct scatterlist *sg; int index; uint16_t tot_dsds; - scsi_qla_host_t *vha = sp->fcport->vha; + scsi_qla_host_t *vha = sp->vha; struct qla_hw_data *ha = vha->hw; struct bsg_job *bsg_job = sp->u.bsg_job; int loop_iterartion = 0; @@ -2665,7 +2663,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) ct_iocb->handle = sp->handle; ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); - ct_iocb->vp_index = sp->fcport->vha->vp_idx; + ct_iocb->vp_index = sp->vha->vp_idx; ct_iocb->comp_status = cpu_to_le16(0); ct_iocb->cmd_dsd_count = @@ -2739,7 +2737,7 @@ qla82xx_start_scsi(srb_t *sp) uint32_t *fcp_dl; uint8_t additional_cdb_len; struct ct6_dsd *ctx; - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct qla_hw_data *ha = vha->hw; struct req_que *req = NULL; struct rsp_que *rsp = NULL; @@ -2901,7 +2899,7 @@ sufficient_dsds: cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + cmd_pkt->vp_index = sp->vha->vp_idx; /* Build IOCB segments */ if (qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds)) @@ -2974,7 +2972,7 @@ sufficient_dsds: cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + cmd_pkt->vp_index = sp->vha->vp_idx; int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, @@ -3060,7 +3058,7 @@ static void qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb) { struct srb_iocb *aio = &sp->u.iocb_cmd; - scsi_qla_host_t *vha = sp->fcport->vha; + scsi_qla_host_t *vha = sp->vha; struct req_que *req = vha->req; memset(abt_iocb, 0, sizeof(struct abort_entry_24xx)); @@ -3079,19 +3077,69 @@ qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb) wmb(); } +static void +qla2x00_mb_iocb(srb_t *sp, struct mbx_24xx_entry *mbx) +{ + int i, sz; + + mbx->entry_type = MBX_IOCB_TYPE; + mbx->handle = sp->handle; + sz = min(ARRAY_SIZE(mbx->mb), ARRAY_SIZE(sp->u.iocb_cmd.u.mbx.out_mb)); + + for (i = 0; i < sz; i++) + mbx->mb[i] = cpu_to_le16(sp->u.iocb_cmd.u.mbx.out_mb[i]); +} + +static void +qla2x00_ctpthru_cmd_iocb(srb_t *sp, struct ct_entry_24xx *ct_pkt) +{ + sp->u.iocb_cmd.u.ctarg.iocb = ct_pkt; + qla24xx_prep_ms_iocb(sp->vha, &sp->u.iocb_cmd.u.ctarg); + ct_pkt->handle = sp->handle; +} + +static void qla2x00_send_notify_ack_iocb(srb_t *sp, + struct nack_to_isp *nack) +{ + struct imm_ntfy_from_isp *ntfy = sp->u.iocb_cmd.u.nack.ntfy; + + nack->entry_type = NOTIFY_ACK_TYPE; + nack->entry_count = 1; + nack->ox_id = ntfy->ox_id; + + nack->u.isp24.handle = sp->handle; + nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle; + if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) { + nack->u.isp24.flags = ntfy->u.isp24.flags & + cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); + } + nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id; + nack->u.isp24.status = ntfy->u.isp24.status; + nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode; + nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle; + nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address; + nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs; + nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui; + nack->u.isp24.srr_flags = 0; + nack->u.isp24.srr_reject_code = 0; + nack->u.isp24.srr_reject_code_expl = 0; + nack->u.isp24.vp_index = ntfy->u.isp24.vp_index; +} + int qla2x00_start_sp(srb_t *sp) { int rval; - struct qla_hw_data *ha = sp->fcport->vha->hw; + scsi_qla_host_t *vha = sp->vha; + struct qla_hw_data *ha = vha->hw; void *pkt; unsigned long flags; rval = QLA_FUNCTION_FAILED; spin_lock_irqsave(&ha->hardware_lock, flags); - pkt = qla2x00_alloc_iocbs(sp->fcport->vha, sp); + pkt = qla2x00_alloc_iocbs(vha, sp); if (!pkt) { - ql_log(ql_log_warn, sp->fcport->vha, 0x700c, + ql_log(ql_log_warn, vha, 0x700c, "qla2x00_alloc_iocbs failed.\n"); goto done; } @@ -3139,12 +3187,23 @@ qla2x00_start_sp(srb_t *sp) case SRB_ELS_DCMD: qla24xx_els_logo_iocb(sp, pkt); break; + case SRB_CT_PTHRU_CMD: + qla2x00_ctpthru_cmd_iocb(sp, pkt); + break; + case SRB_MB_IOCB: + qla2x00_mb_iocb(sp, pkt); + break; + case SRB_NACK_PLOGI: + case SRB_NACK_PRLI: + case SRB_NACK_LOGO: + qla2x00_send_notify_ack_iocb(sp, pkt); + break; default: break; } wmb(); - qla2x00_start_iocbs(sp->fcport->vha, ha->req_q_map[0]); + qla2x00_start_iocbs(vha, ha->req_q_map[0]); done: spin_unlock_irqrestore(&ha->hardware_lock, flags); return rval; diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index edc2264db45b..3c66ea29de27 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -561,14 +561,50 @@ qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry) return ret; } -static inline fc_port_t * +fc_port_t * qla2x00_find_fcport_by_loopid(scsi_qla_host_t *vha, uint16_t loop_id) { - fc_port_t *fcport; + fc_port_t *f, *tf; + + f = tf = NULL; + list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) + if (f->loop_id == loop_id) + return f; + return NULL; +} + +fc_port_t * +qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *vha, u8 *wwpn, u8 incl_deleted) +{ + fc_port_t *f, *tf; + + f = tf = NULL; + list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) { + if (memcmp(f->port_name, wwpn, WWN_SIZE) == 0) { + if (incl_deleted) + return f; + else if (f->deleted == 0) + return f; + } + } + return NULL; +} - list_for_each_entry(fcport, &vha->vp_fcports, list) - if (fcport->loop_id == loop_id) - return fcport; +fc_port_t * +qla2x00_find_fcport_by_nportid(scsi_qla_host_t *vha, port_id_t *id, + u8 incl_deleted) +{ + fc_port_t *f, *tf; + + f = tf = NULL; + list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) { + if (f->d_id.b24 == id->b24) { + if (incl_deleted) + return f; + else if (f->deleted == 0) + return f; + } + } return NULL; } @@ -934,7 +970,11 @@ skip_rio: ql_dbg(ql_dbg_async, vha, 0x508a, "Marking port lost loopid=%04x portid=%06x.\n", fcport->loop_id, fcport->d_id.b24); - qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); + if (qla_ini_mode_enabled(vha)) { + qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); + fcport->logout_on_delete = 0; + qlt_schedule_sess_for_deletion_lock(fcport); + } break; global_port_update: @@ -985,9 +1025,6 @@ global_port_update: qla2x00_mark_all_devices_lost(vha, 1); - if (vha->vp_idx == 0 && !qla_ini_mode_enabled(vha)) - set_bit(SCR_PENDING, &vha->dpc_flags); - set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); set_bit(VP_CONFIG_OK, &vha->vp_flags); @@ -1024,27 +1061,19 @@ global_port_update: if (qla2x00_is_a_vp_did(vha, rscn_entry)) break; - /* - * Search for the rport related to this RSCN entry and mark it - * as lost. - */ - list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (atomic_read(&fcport->state) != FCS_ONLINE) - continue; - if (fcport->d_id.b24 == rscn_entry) { - qla2x00_mark_device_lost(vha, fcport, 0, 0); - break; - } - } - atomic_set(&vha->loop_down_timer, 0); vha->flags.management_server_logged_in = 0; - - set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); - set_bit(RSCN_UPDATE, &vha->dpc_flags); - qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry); + { + struct event_arg ea; + + memset(&ea, 0, sizeof(ea)); + ea.event = FCME_RSCN; + ea.id.b24 = rscn_entry; + ea.id.b.rsvd_1 = rscn_entry >> 24; + qla2x00_fcport_event_handler(vha, &ea); + qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry); + } break; - /* case MBA_RIO_RESPONSE: */ case MBA_ZIO_RESPONSE: ql_dbg(ql_dbg_async, vha, 0x5015, @@ -1212,7 +1241,7 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha, req->outstanding_cmds[index] = NULL; /* Save ISP completion status */ - sp->done(ha, sp, DID_OK << 16); + sp->done(sp, DID_OK << 16); } else { ql_log(ql_log_warn, vha, 0x3016, "Invalid SCSI SRB.\n"); @@ -1235,7 +1264,8 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func, index = LSW(pkt->handle); if (index >= req->num_outstanding_cmds) { ql_log(ql_log_warn, vha, 0x5031, - "Invalid command index (%x).\n", index); + "Invalid command index (%x) type %8ph.\n", + index, iocb); if (IS_P3P_TYPE(ha)) set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); else @@ -1343,66 +1373,122 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, le16_to_cpu(mbx->mb7)); logio_done: - sp->done(vha, sp, 0); + sp->done(sp, 0); } static void -qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, - sts_entry_t *pkt, int iocb_type) +qla24xx_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct mbx_24xx_entry *pkt) { - const char func[] = "CT_IOCB"; - const char *type; + const char func[] = "MBX-IOCB2"; srb_t *sp; - struct bsg_job *bsg_job; - struct fc_bsg_reply *bsg_reply; - uint16_t comp_status; + struct srb_iocb *si; + u16 sz, i; int res; sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (!sp) return; - bsg_job = sp->u.bsg_job; - bsg_reply = bsg_job->reply; + si = &sp->u.iocb_cmd; + sz = min(ARRAY_SIZE(pkt->mb), ARRAY_SIZE(sp->u.iocb_cmd.u.mbx.in_mb)); - type = "ct pass-through"; + for (i = 0; i < sz; i++) + si->u.mbx.in_mb[i] = le16_to_cpu(pkt->mb[i]); - comp_status = le16_to_cpu(pkt->comp_status); + res = (si->u.mbx.in_mb[0] & MBS_MASK); - /* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT - * fc payload to the caller - */ - bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; - bsg_job->reply_len = sizeof(struct fc_bsg_reply); + sp->done(sp, res); +} - if (comp_status != CS_COMPLETE) { - if (comp_status == CS_DATA_UNDERRUN) { - res = DID_OK << 16; - bsg_reply->reply_payload_rcv_len = - le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len); +static void +qla24xxx_nack_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct nack_to_isp *pkt) +{ + const char func[] = "nack"; + srb_t *sp; + int res = 0; - ql_log(ql_log_warn, vha, 0x5048, - "CT pass-through-%s error " - "comp_status-status=0x%x total_byte = 0x%x.\n", - type, comp_status, - bsg_reply->reply_payload_rcv_len); - } else { - ql_log(ql_log_warn, vha, 0x5049, - "CT pass-through-%s error " - "comp_status-status=0x%x.\n", type, comp_status); - res = DID_ERROR << 16; - bsg_reply->reply_payload_rcv_len = 0; - } - ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035, - (uint8_t *)pkt, sizeof(*pkt)); - } else { - res = DID_OK << 16; - bsg_reply->reply_payload_rcv_len = - bsg_job->reply_payload.payload_len; - bsg_job->reply_len = 0; - } + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + + if (pkt->u.isp2x.status != cpu_to_le16(NOTIFY_ACK_SUCCESS)) + res = QLA_FUNCTION_FAILED; + + sp->done(sp, res); +} + +static void +qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, + sts_entry_t *pkt, int iocb_type) +{ + const char func[] = "CT_IOCB"; + const char *type; + srb_t *sp; + struct bsg_job *bsg_job; + struct fc_bsg_reply *bsg_reply; + uint16_t comp_status; + int res = 0; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; - sp->done(vha, sp, res); + switch (sp->type) { + case SRB_CT_CMD: + bsg_job = sp->u.bsg_job; + bsg_reply = bsg_job->reply; + + type = "ct pass-through"; + + comp_status = le16_to_cpu(pkt->comp_status); + + /* + * return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT + * fc payload to the caller + */ + bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + + if (comp_status != CS_COMPLETE) { + if (comp_status == CS_DATA_UNDERRUN) { + res = DID_OK << 16; + bsg_reply->reply_payload_rcv_len = + le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len); + + ql_log(ql_log_warn, vha, 0x5048, + "CT pass-through-%s error comp_status=0x%x total_byte=0x%x.\n", + type, comp_status, + bsg_reply->reply_payload_rcv_len); + } else { + ql_log(ql_log_warn, vha, 0x5049, + "CT pass-through-%s error comp_status=0x%x.\n", + type, comp_status); + res = DID_ERROR << 16; + bsg_reply->reply_payload_rcv_len = 0; + } + ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035, + (uint8_t *)pkt, sizeof(*pkt)); + } else { + res = DID_OK << 16; + bsg_reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + bsg_job->reply_len = 0; + } + break; + case SRB_CT_PTHRU_CMD: + /* + * borrowing sts_entry_24xx.comp_status. + * same location as ct_entry_24xx.comp_status + */ + res = qla2x00_chk_ms_status(vha, (ms_iocb_entry_t *)pkt, + (struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp, + sp->name); + break; + } + + sp->done(sp, res); } static void @@ -1438,7 +1524,16 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, type = "Driver ELS logo"; ql_dbg(ql_dbg_user, vha, 0x5047, "Completing %s: (%p) type=%d.\n", type, sp, sp->type); - sp->done(vha, sp, 0); + sp->done(sp, 0); + return; + case SRB_CT_PTHRU_CMD: + /* borrowing sts_entry_24xx.comp_status. + same location as ct_entry_24xx.comp_status + */ + res = qla2x00_chk_ms_status(vha, (ms_iocb_entry_t *)pkt, + (struct ct_sns_rsp *)sp->u.iocb_cmd.u.ctarg.rsp, + sp->name); + sp->done(sp, res); return; default: ql_dbg(ql_dbg_user, vha, 0x503e, @@ -1496,7 +1591,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, bsg_job->reply_len = 0; } - sp->done(vha, sp, res); + sp->done(sp, res); } static void @@ -1543,6 +1638,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, fcport->d_id.b.area, fcport->d_id.b.al_pa, le32_to_cpu(logio->io_parameter[0])); + vha->hw->exch_starvation = 0; data[0] = MBS_COMMAND_COMPLETE; if (sp->type != SRB_LOGIN_CMD) goto logio_done; @@ -1568,6 +1664,8 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, iop[0] = le32_to_cpu(logio->io_parameter[0]); iop[1] = le32_to_cpu(logio->io_parameter[1]); + lio->u.logio.iop[0] = iop[0]; + lio->u.logio.iop[1] = iop[1]; switch (iop[0]) { case LSC_SCODE_PORTID_USED: data[0] = MBS_PORT_ID_USED; @@ -1576,6 +1674,21 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, case LSC_SCODE_NPORT_USED: data[0] = MBS_LOOP_ID_USED; break; + case LSC_SCODE_NOXCB: + vha->hw->exch_starvation++; + if (vha->hw->exch_starvation > 5) { + ql_log(ql_log_warn, vha, 0xffff, + "Exchange starvation. Resetting RISC\n"); + + vha->hw->exch_starvation = 0; + + if (IS_P3P_TYPE(vha->hw)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } + /* drop through */ default: data[0] = MBS_COMMAND_ERROR; break; @@ -1590,7 +1703,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, le32_to_cpu(logio->io_parameter[1])); logio_done: - sp->done(vha, sp, 0); + sp->done(sp, 0); } static void @@ -1640,7 +1753,7 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5055, (uint8_t *)sts, sizeof(*sts)); - sp->done(vha, sp, 0); + sp->done(sp, 0); } /** @@ -1728,7 +1841,7 @@ static inline void qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, uint32_t sense_len, struct rsp_que *rsp, int res) { - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct scsi_cmnd *cp = GET_CMD_SP(sp); uint32_t track_sense_len; @@ -1756,7 +1869,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, if (sense_len) { ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x301c, "Check condition Sense data, nexus%ld:%d:%llu cmd=%p.\n", - sp->fcport->vha->host_no, cp->device->id, cp->device->lun, + sp->vha->host_no, cp->device->id, cp->device->lun, cp); ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302b, cp->sense_buffer, sense_len); @@ -1778,7 +1891,7 @@ struct scsi_dif_tuple { static inline int qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) { - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct scsi_cmnd *cmd = GET_CMD_SP(sp); uint8_t *ap = &sts24->data[12]; uint8_t *ep = &sts24->data[20]; @@ -2043,7 +2156,7 @@ done: bsg_job->reply_len = sizeof(struct fc_bsg_reply); /* Always return DID_OK, bsg will send the vendor specific response * in this case only */ - sp->done(vha, sp, (DID_OK << 6)); + sp->done(sp, DID_OK << 6); } @@ -2076,6 +2189,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) int res = 0; uint16_t state_flags = 0; uint16_t retry_delay = 0; + uint8_t no_logout = 0; sts = (sts_entry_t *) pkt; sts24 = (struct sts_entry_24xx *) pkt; @@ -2336,6 +2450,7 @@ check_scsi_status: break; case CS_PORT_LOGGED_OUT: + no_logout = 1; case CS_PORT_CONFIG_CHG: case CS_PORT_BUSY: case CS_INCOMPLETE: @@ -2358,14 +2473,21 @@ check_scsi_status: break; } - ql_dbg(ql_dbg_io, fcport->vha, 0x3021, - "Port to be marked lost on fcport=%02x%02x%02x, current " - "port state= %s.\n", fcport->d_id.b.domain, - fcport->d_id.b.area, fcport->d_id.b.al_pa, - port_state_str[atomic_read(&fcport->state)]); + if (atomic_read(&fcport->state) == FCS_ONLINE) { + ql_dbg(ql_dbg_disc, fcport->vha, 0x3021, + "Port to be marked lost on fcport=%02x%02x%02x, current " + "port state= %s comp_status %x.\n", fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + port_state_str[atomic_read(&fcport->state)], + comp_status); + + if (no_logout) + fcport->logout_on_delete = 0; - if (atomic_read(&fcport->state) == FCS_ONLINE) qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); + qlt_schedule_sess_for_deletion_lock(fcport); + } + break; case CS_ABORTED: @@ -2407,7 +2529,7 @@ out: resid_len, fw_resid_len, sp, cp); if (rsp->status_srb == NULL) - sp->done(ha, sp, res); + sp->done(sp, res); } /** @@ -2464,7 +2586,7 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) /* Place command on done queue. */ if (sense_len == 0) { rsp->status_srb = NULL; - sp->done(ha, sp, cp->result); + sp->done(sp, cp->result); } } @@ -2500,7 +2622,7 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (sp) { - sp->done(ha, sp, res); + sp->done(sp, res); return; } fatal: @@ -2558,7 +2680,7 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, abt = &sp->u.iocb_cmd; abt->u.abt.comp_status = le32_to_cpu(pkt->nport_handle); - sp->done(vha, sp, 0); + sp->done(sp, 0); } /** @@ -2629,10 +2751,16 @@ process_err: } case ABTS_RESP_24XX: case CTIO_TYPE7: - case NOTIFY_ACK_TYPE: case CTIO_CRC2: qlt_response_pkt_all_vps(vha, (response_t *)pkt); break; + case NOTIFY_ACK_TYPE: + if (pkt->handle == QLA_TGT_SKIP_HANDLE) + qlt_response_pkt_all_vps(vha, (response_t *)pkt); + else + qla24xxx_nack_iocb_entry(vha, rsp->req, + (struct nack_to_isp *)pkt); + break; case MARKER_TYPE: /* Do nothing in this case, this check is to prevent it * from falling into default case @@ -2642,6 +2770,10 @@ process_err: qla24xx_abort_iocb_entry(vha, rsp->req, (struct abort_entry_24xx *)pkt); break; + case MBX_IOCB_TYPE: + qla24xx_mbx_iocb_entry(vha, rsp->req, + (struct mbx_24xx_entry *)pkt); + break; default: /* Type Not Supported. */ ql_dbg(ql_dbg_async, vha, 0x5042, @@ -2658,8 +2790,9 @@ process_err: if (IS_P3P_TYPE(ha)) { struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; WRT_REG_DWORD(®->rsp_q_out[0], rsp->ring_index); - } else + } else { WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index); + } } static void @@ -3015,14 +3148,17 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) int i, ret; struct qla_msix_entry *qentry; scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + int min_vecs = QLA_BASE_VECTORS; struct irq_affinity desc = { .pre_vectors = QLA_BASE_VECTORS, }; - if (QLA_TGT_MODE_ENABLED() && IS_ATIO_MSIX_CAPABLE(ha)) + if (QLA_TGT_MODE_ENABLED() && IS_ATIO_MSIX_CAPABLE(ha)) { desc.pre_vectors++; + min_vecs++; + } - ret = pci_alloc_irq_vectors_affinity(ha->pdev, QLA_BASE_VECTORS, + ret = pci_alloc_irq_vectors_affinity(ha->pdev, min_vecs, ha->msix_count, PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, &desc); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 67f64db390b0..35079f417417 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1637,94 +1637,6 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) return rval; } -/* - * qla2x00_get_node_name_list - * Issue get node name list mailbox command, kmalloc() - * and return the resulting list. Caller must kfree() it! - * - * Input: - * ha = adapter state pointer. - * out_data = resulting list - * out_len = length of the resulting list - * - * Returns: - * qla2x00 local function return status code. - * - * Context: - * Kernel context. - */ -int -qla2x00_get_node_name_list(scsi_qla_host_t *vha, void **out_data, int *out_len) -{ - struct qla_hw_data *ha = vha->hw; - struct qla_port_24xx_data *list = NULL; - void *pmap; - mbx_cmd_t mc; - dma_addr_t pmap_dma; - ulong dma_size; - int rval, left; - - left = 1; - while (left > 0) { - dma_size = left * sizeof(*list); - pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size, - &pmap_dma, GFP_KERNEL); - if (!pmap) { - ql_log(ql_log_warn, vha, 0x113f, - "%s(%ld): DMA Alloc failed of %ld\n", - __func__, vha->host_no, dma_size); - rval = QLA_MEMORY_ALLOC_FAILED; - goto out; - } - - mc.mb[0] = MBC_PORT_NODE_NAME_LIST; - mc.mb[1] = BIT_1 | BIT_3; - mc.mb[2] = MSW(pmap_dma); - mc.mb[3] = LSW(pmap_dma); - mc.mb[6] = MSW(MSD(pmap_dma)); - mc.mb[7] = LSW(MSD(pmap_dma)); - mc.mb[8] = dma_size; - mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7|MBX_8; - mc.in_mb = MBX_0|MBX_1; - mc.tov = 30; - mc.flags = MBX_DMA_IN; - - rval = qla2x00_mailbox_command(vha, &mc); - if (rval != QLA_SUCCESS) { - if ((mc.mb[0] == MBS_COMMAND_ERROR) && - (mc.mb[1] == 0xA)) { - left += le16_to_cpu(mc.mb[2]) / - sizeof(struct qla_port_24xx_data); - goto restart; - } - goto out_free; - } - - left = 0; - - list = kmemdup(pmap, dma_size, GFP_KERNEL); - if (!list) { - ql_log(ql_log_warn, vha, 0x1140, - "%s(%ld): failed to allocate node names list " - "structure.\n", __func__, vha->host_no); - rval = QLA_MEMORY_ALLOC_FAILED; - goto out_free; - } - -restart: - dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); - } - - *out_data = list; - *out_len = dma_size; - -out: - return rval; - -out_free: - dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); - return rval; -} /* * qla2x00_get_port_database @@ -3687,10 +3599,8 @@ void qla24xx_report_id_acquisition(scsi_qla_host_t *vha, struct vp_rpt_id_entry_24xx *rptid_entry) { - uint8_t vp_idx; - uint16_t stat = le16_to_cpu(rptid_entry->vp_idx); struct qla_hw_data *ha = vha->hw; - scsi_qla_host_t *vp; + scsi_qla_host_t *vp = NULL; unsigned long flags; int found; @@ -3701,80 +3611,124 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, return; if (rptid_entry->format == 0) { + /* loop */ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b7, "Format 0 : Number of VPs setup %d, number of " - "VPs acquired %d.\n", - MSB(le16_to_cpu(rptid_entry->vp_count)), - LSB(le16_to_cpu(rptid_entry->vp_count))); + "VPs acquired %d.\n", rptid_entry->vp_setup, + rptid_entry->vp_acquired); ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b8, "Primary port id %02x%02x%02x.\n", rptid_entry->port_id[2], rptid_entry->port_id[1], rptid_entry->port_id[0]); + + vha->d_id.b.domain = rptid_entry->port_id[2]; + vha->d_id.b.area = rptid_entry->port_id[1]; + vha->d_id.b.al_pa = rptid_entry->port_id[0]; + + spin_lock_irqsave(&ha->vport_slock, flags); + qlt_update_vp_map(vha, SET_AL_PA); + spin_unlock_irqrestore(&ha->vport_slock, flags); + } else if (rptid_entry->format == 1) { - vp_idx = LSB(stat); + /* fabric */ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b9, "Format 1: VP[%d] enabled - status %d - with " - "port id %02x%02x%02x.\n", vp_idx, MSB(stat), + "port id %02x%02x%02x.\n", rptid_entry->vp_idx, + rptid_entry->vp_status, rptid_entry->port_id[2], rptid_entry->port_id[1], rptid_entry->port_id[0]); /* buffer to buffer credit flag */ - vha->flags.bbcr_enable = (rptid_entry->bbcr & 0xf) != 0; - - /* FA-WWN is only for physical port */ - if (!vp_idx) { - void *wwpn = ha->init_cb->port_name; + vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0; + + if (rptid_entry->vp_idx == 0) { + if (rptid_entry->vp_status == VP_STAT_COMPL) { + /* FA-WWN is only for physical port */ + if (qla_ini_mode_enabled(vha) && + ha->flags.fawwpn_enabled && + (rptid_entry->u.f1.flags & + VP_FLAGS_NAME_VALID)) { + memcpy(vha->port_name, + rptid_entry->u.f1.port_name, + WWN_SIZE); + } - if (!MSB(stat)) { - if (rptid_entry->vp_idx_map[1] & BIT_6) - wwpn = rptid_entry->reserved_4 + 8; + vha->d_id.b.domain = rptid_entry->port_id[2]; + vha->d_id.b.area = rptid_entry->port_id[1]; + vha->d_id.b.al_pa = rptid_entry->port_id[0]; + spin_lock_irqsave(&ha->vport_slock, flags); + qlt_update_vp_map(vha, SET_AL_PA); + spin_unlock_irqrestore(&ha->vport_slock, flags); } - memcpy(vha->port_name, wwpn, WWN_SIZE); + fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name); - ql_dbg(ql_dbg_mbx, vha, 0x1018, - "FA-WWN portname %016llx (%x)\n", - fc_host_port_name(vha->host), MSB(stat)); - } - - vp = vha; - if (vp_idx == 0) - goto reg_needed; - if (MSB(stat) != 0 && MSB(stat) != 2) { - ql_dbg(ql_dbg_mbx, vha, 0x10ba, - "Could not acquire ID for VP[%d].\n", vp_idx); - return; - } + if (qla_ini_mode_enabled(vha)) + ql_dbg(ql_dbg_mbx, vha, 0x1018, + "FA-WWN portname %016llx (%x)\n", + fc_host_port_name(vha->host), + rptid_entry->vp_status); - found = 0; - spin_lock_irqsave(&ha->vport_slock, flags); - list_for_each_entry(vp, &ha->vp_list, list) { - if (vp_idx == vp->vp_idx) { - found = 1; - break; + set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); + set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); + } else { + if (rptid_entry->vp_status != VP_STAT_COMPL && + rptid_entry->vp_status != VP_STAT_ID_CHG) { + ql_dbg(ql_dbg_mbx, vha, 0x10ba, + "Could not acquire ID for VP[%d].\n", + rptid_entry->vp_idx); + return; } - } - spin_unlock_irqrestore(&ha->vport_slock, flags); - if (!found) - return; + found = 0; + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + if (rptid_entry->vp_idx == vp->vp_idx) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&ha->vport_slock, flags); - vp->d_id.b.domain = rptid_entry->port_id[2]; - vp->d_id.b.area = rptid_entry->port_id[1]; - vp->d_id.b.al_pa = rptid_entry->port_id[0]; + if (!found) + return; - /* - * Cannot configure here as we are still sitting on the - * response queue. Handle it in dpc context. - */ - set_bit(VP_IDX_ACQUIRED, &vp->vp_flags); + vp->d_id.b.domain = rptid_entry->port_id[2]; + vp->d_id.b.area = rptid_entry->port_id[1]; + vp->d_id.b.al_pa = rptid_entry->port_id[0]; + spin_lock_irqsave(&ha->vport_slock, flags); + qlt_update_vp_map(vp, SET_AL_PA); + spin_unlock_irqrestore(&ha->vport_slock, flags); -reg_needed: - set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags); - set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags); + /* + * Cannot configure here as we are still sitting on the + * response queue. Handle it in dpc context. + */ + set_bit(VP_IDX_ACQUIRED, &vp->vp_flags); + set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags); + set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags); + } set_bit(VP_DPC_NEEDED, &vha->dpc_flags); qla2xxx_wake_dpc(vha); + } else if (rptid_entry->format == 2) { + ql_dbg(ql_dbg_async, vha, 0xffff, + "RIDA: format 2/N2N Primary port id %02x%02x%02x.\n", + rptid_entry->port_id[2], rptid_entry->port_id[1], + rptid_entry->port_id[0]); + + ql_dbg(ql_dbg_async, vha, 0xffff, + "N2N: Remote WWPN %8phC.\n", + rptid_entry->u.f2.port_name); + + /* N2N. direct connect */ + vha->d_id.b.domain = rptid_entry->port_id[2]; + vha->d_id.b.area = rptid_entry->port_id[1]; + vha->d_id.b.al_pa = rptid_entry->port_id[0]; + + spin_lock_irqsave(&ha->vport_slock, flags); + qlt_update_vp_map(vha, SET_AL_PA); + spin_unlock_irqrestore(&ha->vport_slock, flags); } } diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index 96c33e292eba..10b742d27e16 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -1789,16 +1789,16 @@ qlafx00_update_host_attr(scsi_qla_host_t *vha, struct port_info_data *pinfo) static void qla2x00_fxdisc_iocb_timeout(void *data) { - srb_t *sp = (srb_t *)data; + srb_t *sp = data; struct srb_iocb *lio = &sp->u.iocb_cmd; complete(&lio->u.fxiocb.fxiocb_comp); } static void -qla2x00_fxdisc_sp_done(void *data, void *ptr, int res) +qla2x00_fxdisc_sp_done(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; struct srb_iocb *lio = &sp->u.iocb_cmd; complete(&lio->u.fxiocb.fxiocb_comp); @@ -1999,7 +1999,7 @@ done_unmap_req: dma_free_coherent(&ha->pdev->dev, fdisc->u.fxiocb.req_len, fdisc->u.fxiocb.req_addr, fdisc->u.fxiocb.req_dma_handle); done_free_sp: - sp->free(vha, sp); + sp->free(sp); done: return rval; } @@ -2127,7 +2127,7 @@ static inline void qlafx00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, uint32_t sense_len, struct rsp_que *rsp, int res) { - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct scsi_cmnd *cp = GET_CMD_SP(sp); uint32_t track_sense_len; @@ -2162,7 +2162,7 @@ qlafx00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, if (sense_len) { ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3039, "Check condition Sense data, nexus%ld:%d:%llu cmd=%p.\n", - sp->fcport->vha->host_no, cp->device->id, cp->device->lun, + sp->vha->host_no, cp->device->id, cp->device->lun, cp); ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x3049, cp->sense_buffer, sense_len); @@ -2181,7 +2181,7 @@ qlafx00_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, (sstatus & cpu_to_le16((uint16_t)SS_RESPONSE_INFO_LEN_VALID))) cpstatus = cpu_to_le16((uint16_t)CS_INCOMPLETE); tmf->u.tmf.comp_status = cpstatus; - sp->done(vha, sp, 0); + sp->done(sp, 0); } static void @@ -2198,7 +2198,7 @@ qlafx00_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, abt = &sp->u.iocb_cmd; abt->u.abt.comp_status = pkt->tgt_id_sts; - sp->done(vha, sp, 0); + sp->done(sp, 0); } static void @@ -2264,7 +2264,7 @@ qlafx00_ioctl_iosb_entry(scsi_qla_host_t *vha, struct req_que *req, bsg_reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len; } - sp->done(vha, sp, res); + sp->done(sp, res); } /** @@ -2537,7 +2537,7 @@ check_scsi_status: par_sense_len, rsp_info_len); if (rsp->status_srb == NULL) - sp->done(ha, sp, res); + sp->done(sp, res); } /** @@ -2614,7 +2614,7 @@ qlafx00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) /* Place command on done queue. */ if (sense_len == 0) { rsp->status_srb = NULL; - sp->done(ha, sp, cp->result); + sp->done(sp, cp->result); } } @@ -2695,7 +2695,7 @@ qlafx00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (sp) { - sp->done(ha, sp, res); + sp->done(sp, res); return; } @@ -2997,7 +2997,7 @@ qlafx00_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_fx00 *cmd_pkt, cont_a64_entry_t lcont_pkt; cont_a64_entry_t *cont_pkt; - vha = sp->fcport->vha; + vha = sp->vha; req = vha->req; cmd = GET_CMD_SP(sp); @@ -3081,7 +3081,7 @@ qlafx00_start_scsi(srb_t *sp) struct req_que *req = NULL; struct rsp_que *rsp = NULL; struct scsi_cmnd *cmd = GET_CMD_SP(sp); - struct scsi_qla_host *vha = sp->fcport->vha; + struct scsi_qla_host *vha = sp->vha; struct qla_hw_data *ha = vha->hw; struct cmd_type_7_fx00 *cmd_pkt; struct cmd_type_7_fx00 lcmd_pkt; @@ -3205,7 +3205,7 @@ void qlafx00_tm_iocb(srb_t *sp, struct tsk_mgmt_entry_fx00 *ptm_iocb) { struct srb_iocb *fxio = &sp->u.iocb_cmd; - scsi_qla_host_t *vha = sp->fcport->vha; + scsi_qla_host_t *vha = sp->vha; struct req_que *req = vha->req; struct tsk_mgmt_entry_fx00 tm_iocb; struct scsi_lun llun; @@ -3232,7 +3232,7 @@ void qlafx00_abort_iocb(srb_t *sp, struct abort_iocb_entry_fx00 *pabt_iocb) { struct srb_iocb *fxio = &sp->u.iocb_cmd; - scsi_qla_host_t *vha = sp->fcport->vha; + scsi_qla_host_t *vha = sp->vha; struct req_que *req = vha->req; struct abort_iocb_entry_fx00 abt_iocb; @@ -3346,8 +3346,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) REQUEST_ENTRY_SIZE); cont_pkt = qlafx00_prep_cont_type1_iocb( - sp->fcport->vha->req, - &lcont_pkt); + sp->vha->req, &lcont_pkt); cur_dsd = (__le32 *) lcont_pkt.dseg_0_address; avail_dsds = 5; @@ -3368,7 +3367,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) &lcont_pkt, REQUEST_ENTRY_SIZE); ql_dump_buffer( ql_dbg_user + ql_dbg_verbose, - sp->fcport->vha, 0x3042, + sp->vha, 0x3042, (uint8_t *)&lcont_pkt, REQUEST_ENTRY_SIZE); } @@ -3377,7 +3376,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) memcpy_toio((void __iomem *)cont_pkt, &lcont_pkt, REQUEST_ENTRY_SIZE); ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, - sp->fcport->vha, 0x3043, + sp->vha, 0x3043, (uint8_t *)&lcont_pkt, REQUEST_ENTRY_SIZE); } } @@ -3409,8 +3408,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) REQUEST_ENTRY_SIZE); cont_pkt = qlafx00_prep_cont_type1_iocb( - sp->fcport->vha->req, - &lcont_pkt); + sp->vha->req, &lcont_pkt); cur_dsd = (__le32 *) lcont_pkt.dseg_0_address; avail_dsds = 5; @@ -3431,7 +3429,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) REQUEST_ENTRY_SIZE); ql_dump_buffer( ql_dbg_user + ql_dbg_verbose, - sp->fcport->vha, 0x3045, + sp->vha, 0x3045, (uint8_t *)&lcont_pkt, REQUEST_ENTRY_SIZE); } @@ -3440,7 +3438,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) memcpy_toio((void __iomem *)cont_pkt, &lcont_pkt, REQUEST_ENTRY_SIZE); ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, - sp->fcport->vha, 0x3046, + sp->vha, 0x3046, (uint8_t *)&lcont_pkt, REQUEST_ENTRY_SIZE); } } @@ -3452,7 +3450,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) } ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, - sp->fcport->vha, 0x3047, + sp->vha, 0x3047, (uint8_t *)&fx_iocb, sizeof(struct fxdisc_entry_fx00)); memcpy_toio((void __iomem *)pfxiocb, &fx_iocb, diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d01c90c7dd04..1fed235a1b4a 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -237,6 +237,13 @@ MODULE_PARM_DESC(ql2xfwholdabts, "0 (Default) Do not set fw option. " "1 - Set fw option to hold ABTS."); +int ql2xmvasynctoatio = 1; +module_param(ql2xmvasynctoatio, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xmvasynctoatio, + "Move PUREX, ABTS RX and RIDA IOCBs to ATIOQ" + "0 (Default). Do not move IOCBs" + "1 - Move IOCBs."); + /* * SCSI host template entry points */ @@ -607,11 +614,11 @@ qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str, size_t size) } void -qla2x00_sp_free_dma(void *vha, void *ptr) +qla2x00_sp_free_dma(void *ptr) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; + struct qla_hw_data *ha = sp->vha->hw; struct scsi_cmnd *cmd = GET_CMD_SP(sp); - struct qla_hw_data *ha = sp->fcport->vha->hw; void *ctx = GET_CMD_CTX_SP(sp); if (sp->flags & SRB_DMA_VALID) { @@ -650,20 +657,19 @@ qla2x00_sp_free_dma(void *vha, void *ptr) } CMD_SP(cmd) = NULL; - qla2x00_rel_sp(sp->fcport->vha, sp); + qla2x00_rel_sp(sp); } void -qla2x00_sp_compl(void *data, void *ptr, int res) +qla2x00_sp_compl(void *ptr, int res) { - struct qla_hw_data *ha = (struct qla_hw_data *)data; - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; struct scsi_cmnd *cmd = GET_CMD_SP(sp); cmd->result = res; if (atomic_read(&sp->ref_count) == 0) { - ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3015, + ql_dbg(ql_dbg_io, sp->vha, 0x3015, "SP reference-count to ZERO -- sp=%p cmd=%p.\n", sp, GET_CMD_SP(sp)); if (ql2xextended_error_logging & ql_dbg_io) @@ -673,12 +679,12 @@ qla2x00_sp_compl(void *data, void *ptr, int res) if (!atomic_dec_and_test(&sp->ref_count)) return; - qla2x00_sp_free_dma(ha, sp); + qla2x00_sp_free_dma(sp); cmd->scsi_done(cmd); } void -qla2xxx_qpair_sp_free_dma(void *vha, void *ptr) +qla2xxx_qpair_sp_free_dma(void *ptr) { srb_t *sp = (srb_t *)ptr; struct scsi_cmnd *cmd = GET_CMD_SP(sp); @@ -724,9 +730,9 @@ qla2xxx_qpair_sp_free_dma(void *vha, void *ptr) } void -qla2xxx_qpair_sp_compl(void *data, void *ptr, int res) +qla2xxx_qpair_sp_compl(void *ptr, int res) { - srb_t *sp = (srb_t *)ptr; + srb_t *sp = ptr; struct scsi_cmnd *cmd = GET_CMD_SP(sp); cmd->result = res; @@ -742,7 +748,7 @@ qla2xxx_qpair_sp_compl(void *data, void *ptr, int res) if (!atomic_dec_and_test(&sp->ref_count)) return; - qla2xxx_qpair_sp_free_dma(sp->fcport->vha, sp); + qla2xxx_qpair_sp_free_dma(sp); cmd->scsi_done(cmd); } @@ -863,7 +869,7 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) return 0; qc24_host_busy_free_sp: - qla2x00_sp_free_dma(ha, sp); + qla2x00_sp_free_dma(sp); qc24_host_busy: return SCSI_MLQUEUE_HOST_BUSY; @@ -952,7 +958,7 @@ qla2xxx_mqueuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd, return 0; qc24_host_busy_free_sp: - qla2xxx_qpair_sp_free_dma(vha, sp); + qla2xxx_qpair_sp_free_dma(sp); qc24_host_busy: return SCSI_MLQUEUE_HOST_BUSY; @@ -1044,6 +1050,34 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) return (return_status); } +static inline int test_fcport_count(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + unsigned long flags; + int res; + + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + ql_dbg(ql_dbg_init, vha, 0xffff, + "tgt %p, fcport_count=%d\n", + vha, vha->fcport_count); + res = (vha->fcport_count == 0); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + + return res; +} + +/* + * qla2x00_wait_for_sess_deletion can only be called from remove_one. + * it has dependency on UNLOADING flag to stop device discovery + */ +static void +qla2x00_wait_for_sess_deletion(scsi_qla_host_t *vha) +{ + qla2x00_mark_all_devices_lost(vha, 0); + + wait_event(vha->fcport_waitQ, test_fcport_count(vha)); +} + /* * qla2x00_wait_for_hba_ready * Wait till the HBA is ready before doing driver unload @@ -1204,7 +1238,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) } spin_lock_irqsave(&ha->hardware_lock, flags); - sp->done(ha, sp, 0); + sp->done(sp, 0); spin_unlock_irqrestore(&ha->hardware_lock, flags); /* Did the command return during mailbox execution? */ @@ -1249,7 +1283,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, continue; if (sp->type != SRB_SCSI_CMD) continue; - if (vha->vp_idx != sp->fcport->vha->vp_idx) + if (vha->vp_idx != sp->vha->vp_idx) continue; match = 0; cmd = GET_CMD_SP(sp); @@ -1629,7 +1663,7 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) spin_lock_irqsave(&ha->hardware_lock, flags); } req->outstanding_cmds[cnt] = NULL; - sp->done(vha, sp, res); + sp->done(sp, res); } } } @@ -1815,6 +1849,7 @@ skip_pio: /* Determine queue resources */ ha->max_req_queues = ha->max_rsp_queues = 1; + ha->msix_count = QLA_BASE_VECTORS; if (!ql2xmqsupport || (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))) goto mqiobase_exit; @@ -1842,9 +1877,8 @@ skip_pio: "BAR 3 not enabled.\n"); mqiobase_exit: - ha->msix_count = ha->max_rsp_queues + 1; ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001c, - "MSIX Count:%d.\n", ha->msix_count); + "MSIX Count: %d.\n", ha->msix_count); return (0); iospace_error_exit: @@ -1892,6 +1926,7 @@ qla83xx_iospace_config(struct qla_hw_data *ha) /* 83XX 26XX always use MQ type access for queues * - mbar 2, a.k.a region 4 */ ha->max_req_queues = ha->max_rsp_queues = 1; + ha->msix_count = QLA_BASE_VECTORS; ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 4), pci_resource_len(ha->pdev, 4)); @@ -1915,12 +1950,13 @@ qla83xx_iospace_config(struct qla_hw_data *ha) if (ql2xmqsupport) { /* MB interrupt uses 1 vector */ ha->max_req_queues = ha->msix_count - 1; - ha->max_rsp_queues = ha->max_req_queues; /* ATIOQ needs 1 vector. That's 1 less QPair */ if (QLA_TGT_MODE_ENABLED()) ha->max_req_queues--; + ha->max_rsp_queues = ha->max_req_queues; + /* Queue pairs is the max value minus * the base queue pair */ ha->max_qpairs = ha->max_req_queues - 1; @@ -1934,14 +1970,8 @@ qla83xx_iospace_config(struct qla_hw_data *ha) "BAR 1 not enabled.\n"); mqiobase_exit: - ha->msix_count = ha->max_rsp_queues + 1; - if (QLA_TGT_MODE_ENABLED()) - ha->msix_count++; - - qlt_83xx_iospace_config(ha); - ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011f, - "MSIX Count:%d.\n", ha->msix_count); + "MSIX Count: %d.\n", ha->msix_count); return 0; iospace_error_exit: @@ -3124,7 +3154,8 @@ skip_dpc: ql_dbg(ql_dbg_init, base_vha, 0x00f2, "Init done and hba is online.\n"); - if (qla_ini_mode_enabled(base_vha)) + if (qla_ini_mode_enabled(base_vha) || + qla_dual_mode_enabled(base_vha)) scsi_scan_host(host); else ql_dbg(ql_dbg_init, base_vha, 0x0122, @@ -3373,21 +3404,26 @@ qla2x00_remove_one(struct pci_dev *pdev) * resources. */ if (!atomic_read(&pdev->enable_cnt)) { + dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size, + base_vha->gnl.l, base_vha->gnl.ldma); + scsi_host_put(base_vha->host); kfree(ha); pci_set_drvdata(pdev, NULL); return; } - qla2x00_wait_for_hba_ready(base_vha); - /* if UNLOAD flag is already set, then continue unload, + /* + * if UNLOAD flag is already set, then continue unload, * where it was set first. */ if (test_bit(UNLOADING, &base_vha->dpc_flags)) return; set_bit(UNLOADING, &base_vha->dpc_flags); + dma_free_coherent(&ha->pdev->dev, + base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma); if (IS_QLAFX00(ha)) qlafx00_driver_shutdown(base_vha, 20); @@ -3536,10 +3572,14 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, qla2xxx_wake_dpc(base_vha); } else { int now; - if (rport) + if (rport) { + ql_dbg(ql_dbg_disc, fcport->vha, 0xffff, + "%s %8phN. rport %p roles %x \n", + __func__, fcport->port_name, rport, + rport->roles); fc_remote_port_delete(rport); + } qlt_do_generation_tick(vha, &now); - qlt_fc_port_deleted(vha, fcport, now); } } @@ -3582,7 +3622,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, fcport->login_retry = vha->hw->login_retry_count; ql_dbg(ql_dbg_disc, vha, 0x2067, - "Port login retry %8phN, id = 0x%04x retry cnt=%d.\n", + "Port login retry %8phN, lid 0x%04x retry cnt=%d.\n", fcport->port_name, fcport->loop_id, fcport->login_retry); } } @@ -3605,7 +3645,13 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer) { fc_port_t *fcport; + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Mark all dev lost\n"); + list_for_each_entry(fcport, &vha->vp_fcports, list) { + fcport->scan_state = 0; + qlt_schedule_sess_for_deletion_lock(fcport); + if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx) continue; @@ -4195,10 +4241,10 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, struct scsi_qla_host *vha = NULL; host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t)); - if (host == NULL) { + if (!host) { ql_log_pci(ql_log_fatal, ha->pdev, 0x0107, "Failed to allocate host from the scsi layer, aborting.\n"); - goto fail; + return NULL; } /* Clear our data area */ @@ -4217,9 +4263,22 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, INIT_LIST_HEAD(&vha->logo_list); INIT_LIST_HEAD(&vha->plogi_ack_list); INIT_LIST_HEAD(&vha->qp_list); + INIT_LIST_HEAD(&vha->gnl.fcports); spin_lock_init(&vha->work_lock); spin_lock_init(&vha->cmd_list_lock); + init_waitqueue_head(&vha->fcport_waitQ); + + vha->gnl.size = sizeof(struct get_name_list_extended) * + (ha->max_loop_id + 1); + vha->gnl.l = dma_alloc_coherent(&ha->pdev->dev, + vha->gnl.size, &vha->gnl.ldma, GFP_KERNEL); + if (!vha->gnl.l) { + ql_log(ql_log_fatal, vha, 0xffff, + "Alloc failed for name list.\n"); + scsi_remove_host(vha->host); + return NULL; + } sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no); ql_dbg(ql_dbg_init, vha, 0x0041, @@ -4228,12 +4287,9 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, dev_name(&(ha->pdev->dev))); return vha; - -fail: - return vha; } -static struct qla_work_evt * +struct qla_work_evt * qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type) { struct qla_work_evt *e; @@ -4255,7 +4311,7 @@ qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type) return e; } -static int +int qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e) { unsigned long flags; @@ -4316,7 +4372,6 @@ int qla2x00_post_async_##name##_work( \ } qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN); -qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE); qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT); qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE); qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC); @@ -4369,6 +4424,67 @@ qlafx00_post_aenfx_work(struct scsi_qla_host *vha, uint32_t evtcode, return qla2x00_post_work(vha, e); } +int qla24xx_post_upd_fcport_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_UPD_FCPORT); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + return qla2x00_post_work(vha, e); +} + +static +void qla24xx_create_new_sess(struct scsi_qla_host *vha, struct qla_work_evt *e) +{ + unsigned long flags; + fc_port_t *fcport = NULL; + struct qlt_plogi_ack_t *pla = + (struct qlt_plogi_ack_t *)e->u.new_sess.pla; + + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + fcport = qla2x00_find_fcport_by_wwpn(vha, e->u.new_sess.port_name, 1); + if (fcport) { + fcport->d_id = e->u.new_sess.id; + if (pla) { + fcport->fw_login_state = DSC_LS_PLOGI_PEND; + qlt_plogi_ack_link(vha, pla, fcport, QLT_PLOGI_LINK_SAME_WWN); + /* we took an extra ref_count to prevent PLOGI ACK when + * fcport/sess has not been created. + */ + pla->ref_count--; + } + } else { + fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (fcport) { + fcport->d_id = e->u.new_sess.id; + fcport->scan_state = QLA_FCPORT_FOUND; + fcport->flags |= FCF_FABRIC_DEVICE; + fcport->fw_login_state = DSC_LS_PLOGI_PEND; + + memcpy(fcport->port_name, e->u.new_sess.port_name, + WWN_SIZE); + list_add_tail(&fcport->list, &vha->vp_fcports); + + if (pla) { + qlt_plogi_ack_link(vha, pla, fcport, + QLT_PLOGI_LINK_SAME_WWN); + pla->ref_count--; + } + } + } + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + + if (fcport) { + if (pla) + qlt_plogi_ack_unref(vha, pla); + else + qla24xx_async_gnl(vha, fcport); + } +} + void qla2x00_do_work(struct scsi_qla_host *vha) { @@ -4395,10 +4511,6 @@ qla2x00_do_work(struct scsi_qla_host *vha) qla2x00_async_login(vha, e->u.logio.fcport, e->u.logio.data); break; - case QLA_EVT_ASYNC_LOGIN_DONE: - qla2x00_async_login_done(vha, e->u.logio.fcport, - e->u.logio.data); - break; case QLA_EVT_ASYNC_LOGOUT: qla2x00_async_logout(vha, e->u.logio.fcport); break; @@ -4420,6 +4532,34 @@ qla2x00_do_work(struct scsi_qla_host *vha) case QLA_EVT_AENFX: qlafx00_process_aen(vha, e); break; + case QLA_EVT_GIDPN: + qla24xx_async_gidpn(vha, e->u.fcport.fcport); + break; + case QLA_EVT_GPNID: + qla24xx_async_gpnid(vha, &e->u.gpnid.id); + break; + case QLA_EVT_GPNID_DONE: + qla24xx_async_gpnid_done(vha, e->u.iosb.sp); + break; + case QLA_EVT_NEW_SESS: + qla24xx_create_new_sess(vha, e); + break; + case QLA_EVT_GPDB: + qla24xx_async_gpdb(vha, e->u.fcport.fcport, + e->u.fcport.opt); + break; + case QLA_EVT_GPSC: + qla24xx_async_gpsc(vha, e->u.fcport.fcport); + break; + case QLA_EVT_UPD_FCPORT: + qla2x00_update_fcport(vha, e->u.fcport.fcport); + break; + case QLA_EVT_GNL: + qla24xx_async_gnl(vha, e->u.fcport.fcport); + break; + case QLA_EVT_NACK: + qla24xx_do_nack_work(vha, e); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); @@ -4436,9 +4576,7 @@ void qla2x00_relogin(struct scsi_qla_host *vha) { fc_port_t *fcport; int status; - uint16_t next_loopid = 0; - struct qla_hw_data *ha = vha->hw; - uint16_t data[2]; + struct event_arg ea; list_for_each_entry(fcport, &vha->vp_fcports, list) { /* @@ -4449,77 +4587,38 @@ void qla2x00_relogin(struct scsi_qla_host *vha) fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) { fcport->login_retry--; if (fcport->flags & FCF_FABRIC_DEVICE) { - if (fcport->flags & FCF_FCP2_DEVICE) - ha->isp_ops->fabric_logout(vha, - fcport->loop_id, - fcport->d_id.b.domain, - fcport->d_id.b.area, - fcport->d_id.b.al_pa); - - if (fcport->loop_id == FC_NO_LOOP_ID) { - fcport->loop_id = next_loopid = - ha->min_external_loopid; - status = qla2x00_find_new_loop_id( - vha, fcport); - if (status != QLA_SUCCESS) { - /* Ran out of IDs to use */ - break; - } - } - - if (IS_ALOGIO_CAPABLE(ha)) { - fcport->flags |= FCF_ASYNC_SENT; - data[0] = 0; - data[1] = QLA_LOGIO_LOGIN_RETRIED; - status = qla2x00_post_async_login_work( - vha, fcport, data); - if (status == QLA_SUCCESS) - continue; - /* Attempt a retry. */ - status = 1; - } else { - status = qla2x00_fabric_login(vha, - fcport, &next_loopid); - if (status == QLA_SUCCESS) { - int status2; - uint8_t opts; - - opts = 0; - if (fcport->flags & - FCF_FCP2_DEVICE) - opts |= BIT_1; - status2 = - qla2x00_get_port_database( - vha, fcport, opts); - if (status2 != QLA_SUCCESS) - status = 1; - } - } - } else + ql_dbg(ql_dbg_disc, fcport->vha, 0xffff, + "%s %8phC DS %d LS %d\n", __func__, + fcport->port_name, fcport->disc_state, + fcport->fw_login_state); + memset(&ea, 0, sizeof(ea)); + ea.event = FCME_RELOGIN; + ea.fcport = fcport; + qla2x00_fcport_event_handler(vha, &ea); + } else { status = qla2x00_local_device_login(vha, fcport); + if (status == QLA_SUCCESS) { + fcport->old_loop_id = fcport->loop_id; + ql_dbg(ql_dbg_disc, vha, 0x2003, + "Port login OK: logged in ID 0x%x.\n", + fcport->loop_id); + qla2x00_update_fcport(vha, fcport); + } else if (status == 1) { + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + /* retry the login again */ + ql_dbg(ql_dbg_disc, vha, 0x2007, + "Retrying %d login again loop_id 0x%x.\n", + fcport->login_retry, + fcport->loop_id); + } else { + fcport->login_retry = 0; + } - if (status == QLA_SUCCESS) { - fcport->old_loop_id = fcport->loop_id; - - ql_dbg(ql_dbg_disc, vha, 0x2003, - "Port login OK: logged in ID 0x%x.\n", - fcport->loop_id); - - qla2x00_update_fcport(vha, fcport); - - } else if (status == 1) { - set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - /* retry the login again */ - ql_dbg(ql_dbg_disc, vha, 0x2007, - "Retrying %d login again loop_id 0x%x.\n", - fcport->login_retry, fcport->loop_id); - } else { - fcport->login_retry = 0; + if (fcport->login_retry == 0 && + status != QLA_SUCCESS) + qla2x00_clear_loop_id(fcport); } - - if (fcport->login_retry == 0 && status != QLA_SUCCESS) - qla2x00_clear_loop_id(fcport); } if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) break; @@ -5183,7 +5282,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) struct pci_dev *pdev = ha->pdev; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - /* if UNLOAD flag is already set, then continue unload, + /* + * if UNLOAD flag is already set, then continue unload, * where it was set first. */ if (test_bit(UNLOADING, &base_vha->dpc_flags)) @@ -5192,6 +5292,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) ql_log(ql_log_warn, base_vha, 0x015b, "Disabling adapter.\n"); + qla2x00_wait_for_sess_deletion(base_vha); + set_bit(UNLOADING, &base_vha->dpc_flags); qla2x00_delete_all_vps(ha, base_vha); @@ -5410,16 +5512,6 @@ qla2x00_do_dpc(void *data) qla2x00_update_fcports(base_vha); } - if (test_bit(SCR_PENDING, &base_vha->dpc_flags)) { - int ret; - ret = qla2x00_send_change_request(base_vha, 0x3, 0); - if (ret != QLA_SUCCESS) - ql_log(ql_log_warn, base_vha, 0x121, - "Failed to enable receiving of RSCN " - "requests: 0x%x.\n", ret); - clear_bit(SCR_PENDING, &base_vha->dpc_flags); - } - if (IS_QLAFX00(ha)) goto loop_resync_check; diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index e4fda84b959e..45f5077684f0 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -55,8 +55,17 @@ MODULE_PARM_DESC(qlini_mode, "disabled on enabling target mode and then on disabling target mode " "enabled back; " "\"disabled\" - initiator mode will never be enabled; " + "\"dual\" - Initiator Modes will be enabled. Target Mode can be activated " + "when ready " "\"enabled\" (default) - initiator mode will always stay enabled."); +static int ql_dm_tgt_ex_pct = 50; +module_param(ql_dm_tgt_ex_pct, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql_dm_tgt_ex_pct, + "For Dual Mode (qlini_mode=dual), this parameter determines " + "the percentage of exchanges/cmds FW will allocate resources " + "for Target mode."); + int ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE; static int temp_sam_status = SAM_STAT_BUSY; @@ -102,12 +111,10 @@ enum fcp_resp_rsp_codes { static void qlt_24xx_atio_pkt(struct scsi_qla_host *ha, struct atio_from_isp *pkt, uint8_t); static void qlt_response_pkt(struct scsi_qla_host *ha, response_t *pkt); -static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, +static int qlt_issue_task_mgmt(struct fc_port *sess, u64 lun, int fn, void *iocb, int flags); static void qlt_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked, int ul_abort); -static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha, - struct qla_tgt_srr_imm *imm, int ha_lock); static void qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd); static void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, @@ -120,6 +127,9 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha, uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan); static void qlt_send_term_imm_notif(struct scsi_qla_host *vha, struct imm_ntfy_from_isp *imm, int ha_locked); +static struct fc_port *qlt_create_sess(struct scsi_qla_host *vha, + fc_port_t *fcport, bool local); +void qlt_unreg_sess(struct fc_port *sess); /* * Global Variables */ @@ -140,21 +150,6 @@ void qlt_do_generation_tick(struct scsi_qla_host *vha, int *dest) wmb(); } -/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ -static struct qla_tgt_sess *qlt_find_sess_by_port_name( - struct qla_tgt *tgt, - const uint8_t *port_name) -{ - struct qla_tgt_sess *sess; - - list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { - if (!memcmp(sess->port_name, port_name, WWN_SIZE)) - return sess; - } - - return NULL; -} - /* Might release hw lock, then reaquire!! */ static inline int qlt_issue_marker(struct scsi_qla_host *vha, int vha_locked) { @@ -229,6 +224,105 @@ static inline void qlt_decr_num_pend_cmds(struct scsi_qla_host *vha) spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags); } + +static void qlt_queue_unknown_atio(scsi_qla_host_t *vha, + struct atio_from_isp *atio, uint8_t ha_locked) +{ + struct qla_tgt_sess_op *u; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + unsigned long flags; + + if (tgt->tgt_stop) { + ql_dbg(ql_dbg_async, vha, 0xffff, + "qla_target(%d): dropping unknown ATIO_TYPE7, " + "because tgt is being stopped", vha->vp_idx); + goto out_term; + } + + u = kzalloc(sizeof(*u), GFP_ATOMIC); + if (u == NULL) { + ql_dbg(ql_dbg_async, vha, 0xffff, + "Alloc of struct unknown_atio (size %zd) failed", sizeof(*u)); + /* It should be harmless and on the next retry should work well */ + goto out_term; + } + + u->vha = vha; + memcpy(&u->atio, atio, sizeof(*atio)); + INIT_LIST_HEAD(&u->cmd_list); + + spin_lock_irqsave(&vha->cmd_list_lock, flags); + list_add_tail(&u->cmd_list, &vha->unknown_atio_list); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); + + schedule_delayed_work(&vha->unknown_atio_work, 1); + +out: + return; + +out_term: + qlt_send_term_exchange(vha, NULL, atio, ha_locked, 0); + goto out; +} + +static void qlt_try_to_dequeue_unknown_atios(struct scsi_qla_host *vha, + uint8_t ha_locked) +{ + struct qla_tgt_sess_op *u, *t; + scsi_qla_host_t *host; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + unsigned long flags; + uint8_t queued = 0; + + list_for_each_entry_safe(u, t, &vha->unknown_atio_list, cmd_list) { + if (u->aborted) { + ql_dbg(ql_dbg_async, vha, 0xffff, + "Freeing unknown %s %p, because of Abort", + "ATIO_TYPE7", u); + qlt_send_term_exchange(vha, NULL, &u->atio, + ha_locked, 0); + goto abort; + } + + host = qlt_find_host_by_d_id(vha, u->atio.u.isp24.fcp_hdr.d_id); + if (host != NULL) { + ql_dbg(ql_dbg_async, vha, 0xffff, + "Requeuing unknown ATIO_TYPE7 %p", u); + qlt_24xx_atio_pkt(host, &u->atio, ha_locked); + } else if (tgt->tgt_stop) { + ql_dbg(ql_dbg_async, vha, 0xffff, + "Freeing unknown %s %p, because tgt is being stopped", + "ATIO_TYPE7", u); + qlt_send_term_exchange(vha, NULL, &u->atio, + ha_locked, 0); + } else { + ql_dbg(ql_dbg_async, vha, 0xffff, + "u %p, vha %p, host %p, sched again..", u, + vha, host); + if (!queued) { + queued = 1; + schedule_delayed_work(&vha->unknown_atio_work, + 1); + } + continue; + } + +abort: + spin_lock_irqsave(&vha->cmd_list_lock, flags); + list_del(&u->cmd_list); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); + kfree(u); + } +} + +void qlt_unknown_atio_work_fn(struct work_struct *work) +{ + struct scsi_qla_host *vha = container_of(to_delayed_work(work), + struct scsi_qla_host, unknown_atio_work); + + qlt_try_to_dequeue_unknown_atios(vha, 0); +} + static bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, struct atio_from_isp *atio, uint8_t ha_locked) { @@ -249,8 +343,14 @@ static bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, atio->u.isp24.fcp_hdr.d_id[0], atio->u.isp24.fcp_hdr.d_id[1], atio->u.isp24.fcp_hdr.d_id[2]); + + + qlt_queue_unknown_atio(vha, atio, ha_locked); break; } + if (unlikely(!list_empty(&vha->unknown_atio_list))) + qlt_try_to_dequeue_unknown_atios(vha, ha_locked); + qlt_24xx_atio_pkt(host, atio, ha_locked); break; } @@ -278,6 +378,31 @@ static bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, break; } + case VP_RPT_ID_IOCB_TYPE: + qla24xx_report_id_acquisition(vha, + (struct vp_rpt_id_entry_24xx *)atio); + break; + + case ABTS_RECV_24XX: + { + struct abts_recv_from_24xx *entry = + (struct abts_recv_from_24xx *)atio; + struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha, + entry->vp_index); + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xffff, + "qla_target(%d): Response pkt (ABTS_RECV_24XX) " + "received, with unknown vp_index %d\n", + vha->vp_idx, entry->vp_index); + break; + } + qlt_response_pkt(host, (response_t *)atio); + break; + + } + + /* case PUREX_IOCB_TYPE: ql2xmvasynctoatio */ + default: ql_dbg(ql_dbg_tgt, vha, 0xe040, "qla_target(%d): Received unknown ATIO atio " @@ -395,22 +520,263 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt) /* * All qlt_plogi_ack_t operations are protected by hardware_lock */ +static int qla24xx_post_nack_work(struct scsi_qla_host *vha, fc_port_t *fcport, + struct imm_ntfy_from_isp *ntfy, int type) +{ + struct qla_work_evt *e; + e = qla2x00_alloc_work(vha, QLA_EVT_NACK); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.nack.fcport = fcport; + e->u.nack.type = type; + memcpy(e->u.nack.iocb, ntfy, sizeof(struct imm_ntfy_from_isp)); + return qla2x00_post_work(vha, e); +} + +static +void qla2x00_async_nack_sp_done(void *s, int res) +{ + struct srb *sp = (struct srb *)s; + struct scsi_qla_host *vha = sp->vha; + unsigned long flags; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async done-%s res %x %8phC type %d\n", + sp->name, res, sp->fcport->port_name, sp->type); + + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + sp->fcport->flags &= ~FCF_ASYNC_SENT; + sp->fcport->chip_reset = vha->hw->chip_reset; + + switch (sp->type) { + case SRB_NACK_PLOGI: + sp->fcport->login_gen++; + sp->fcport->fw_login_state = DSC_LS_PLOGI_COMP; + sp->fcport->logout_on_delete = 1; + break; + + case SRB_NACK_PRLI: + sp->fcport->fw_login_state = DSC_LS_PRLI_COMP; + sp->fcport->deleted = 0; + + if (!sp->fcport->login_succ && + !IS_SW_RESV_ADDR(sp->fcport->d_id)) { + sp->fcport->login_succ = 1; + + vha->fcport_count++; + + if (!IS_IIDMA_CAPABLE(vha->hw) || + !vha->hw->flags.gpsc_supported) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post upd_fcport fcp_cnt %d\n", + __func__, __LINE__, + sp->fcport->port_name, + vha->fcport_count); + + qla24xx_post_upd_fcport_work(vha, sp->fcport); + } else { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpsc fcp_cnt %d\n", + __func__, __LINE__, + sp->fcport->port_name, + vha->fcport_count); + + qla24xx_post_gpsc_work(vha, sp->fcport); + } + } + break; + + case SRB_NACK_LOGO: + sp->fcport->login_gen++; + sp->fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; + qlt_logo_completion_handler(sp->fcport, MBS_COMMAND_COMPLETE); + break; + } + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + + sp->free(sp); +} + +int qla24xx_async_notify_ack(scsi_qla_host_t *vha, fc_port_t *fcport, + struct imm_ntfy_from_isp *ntfy, int type) +{ + int rval = QLA_FUNCTION_FAILED; + srb_t *sp; + char *c = NULL; + + fcport->flags |= FCF_ASYNC_SENT; + switch (type) { + case SRB_NACK_PLOGI: + fcport->fw_login_state = DSC_LS_PLOGI_PEND; + c = "PLOGI"; + break; + case SRB_NACK_PRLI: + fcport->fw_login_state = DSC_LS_PRLI_PEND; + c = "PRLI"; + break; + case SRB_NACK_LOGO: + fcport->fw_login_state = DSC_LS_LOGO_PEND; + c = "LOGO"; + break; + } + + sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); + if (!sp) + goto done; + + sp->type = type; + sp->name = "nack"; + + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2); + + sp->u.iocb_cmd.u.nack.ntfy = ntfy; + + sp->done = qla2x00_async_nack_sp_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "Async-%s %8phC hndl %x %s\n", + sp->name, fcport->port_name, sp->handle, c); + + return rval; + +done_free_sp: + sp->free(sp); +done: + fcport->flags &= ~FCF_ASYNC_SENT; + return rval; +} + +void qla24xx_do_nack_work(struct scsi_qla_host *vha, struct qla_work_evt *e) +{ + fc_port_t *t; + unsigned long flags; + + switch (e->u.nack.type) { + case SRB_NACK_PRLI: + mutex_lock(&vha->vha_tgt.tgt_mutex); + t = qlt_create_sess(vha, e->u.nack.fcport, 0); + mutex_unlock(&vha->vha_tgt.tgt_mutex); + if (t) { + ql_log(ql_log_info, vha, 0xffff, + "%s create sess success %p", __func__, t); + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + /* create sess has an extra kref */ + vha->hw->tgt.tgt_ops->put_sess(e->u.nack.fcport); + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + } + break; + } + qla24xx_async_notify_ack(vha, e->u.nack.fcport, + (struct imm_ntfy_from_isp*)e->u.nack.iocb, e->u.nack.type); +} + +void qla24xx_delete_sess_fn(struct work_struct *work) +{ + fc_port_t *fcport = container_of(work, struct fc_port, del_work); + struct qla_hw_data *ha = fcport->vha->hw; + unsigned long flags; + + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + + if (fcport->se_sess) { + ha->tgt.tgt_ops->shutdown_sess(fcport); + ha->tgt.tgt_ops->put_sess(fcport); + } else { + qlt_unreg_sess(fcport); + } + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); +} + +/* + * Called from qla2x00_reg_remote_port() + */ +void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct fc_port *sess = fcport; + unsigned long flags; + + if (!vha->hw->tgt.tgt_ops) + return; + + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + if (tgt->tgt_stop) { + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + return; + } + + if (fcport->disc_state == DSC_DELETE_PEND) { + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + return; + } + + if (!sess->se_sess) { + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + + mutex_lock(&vha->vha_tgt.tgt_mutex); + sess = qlt_create_sess(vha, fcport, false); + mutex_unlock(&vha->vha_tgt.tgt_mutex); + + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + } else { + if (fcport->fw_login_state == DSC_LS_PRLI_COMP) { + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + return; + } + + if (!kref_get_unless_zero(&sess->sess_kref)) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s: kref_get fail sess %8phC \n", + __func__, sess->port_name); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + return; + } + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c, + "qla_target(%u): %ssession for port %8phC " + "(loop ID %d) reappeared\n", vha->vp_idx, + sess->local ? "local " : "", sess->port_name, sess->loop_id); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007, + "Reappeared sess %p\n", sess); + + ha->tgt.tgt_ops->update_sess(sess, fcport->d_id, + fcport->loop_id, + (fcport->flags & FCF_CONF_COMP_SUPPORTED)); + } + + if (sess && sess->local) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d, + "qla_target(%u): local session for " + "port %8phC (loop ID %d) became global\n", vha->vp_idx, + fcport->port_name, sess->loop_id); + sess->local = 0; + } + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); +} /* * This is a zero-base ref-counting solution, since hardware_lock * guarantees that ref_count is not modified concurrently. * Upon successful return content of iocb is undefined */ -static qlt_plogi_ack_t * +static struct qlt_plogi_ack_t * qlt_plogi_ack_find_add(struct scsi_qla_host *vha, port_id_t *id, struct imm_ntfy_from_isp *iocb) { - qlt_plogi_ack_t *pla; + struct qlt_plogi_ack_t *pla; list_for_each_entry(pla, &vha->plogi_ack_list, list) { if (pla->id.b24 == id->b24) { qlt_send_term_imm_notif(vha, &pla->iocb, 1); - pla->iocb = *iocb; + memcpy(&pla->iocb, iocb, sizeof(pla->iocb)); return pla; } } @@ -423,50 +789,78 @@ qlt_plogi_ack_find_add(struct scsi_qla_host *vha, port_id_t *id, return NULL; } - pla->iocb = *iocb; + memcpy(&pla->iocb, iocb, sizeof(pla->iocb)); pla->id = *id; list_add_tail(&pla->list, &vha->plogi_ack_list); return pla; } -static void qlt_plogi_ack_unref(struct scsi_qla_host *vha, qlt_plogi_ack_t *pla) +void qlt_plogi_ack_unref(struct scsi_qla_host *vha, + struct qlt_plogi_ack_t *pla) { + struct imm_ntfy_from_isp *iocb = &pla->iocb; + port_id_t port_id; + uint16_t loop_id; + fc_port_t *fcport = pla->fcport; + BUG_ON(!pla->ref_count); pla->ref_count--; if (pla->ref_count) return; - ql_dbg(ql_dbg_async, vha, 0x5089, + ql_dbg(ql_dbg_disc, vha, 0x5089, "Sending PLOGI ACK to wwn %8phC s_id %02x:%02x:%02x loop_id %#04x" - " exch %#x ox_id %#x\n", pla->iocb.u.isp24.port_name, - pla->iocb.u.isp24.port_id[2], pla->iocb.u.isp24.port_id[1], - pla->iocb.u.isp24.port_id[0], - le16_to_cpu(pla->iocb.u.isp24.nport_handle), - pla->iocb.u.isp24.exchange_address, pla->iocb.ox_id); - qlt_send_notify_ack(vha, &pla->iocb, 0, 0, 0, 0, 0, 0); + " exch %#x ox_id %#x\n", iocb->u.isp24.port_name, + iocb->u.isp24.port_id[2], iocb->u.isp24.port_id[1], + iocb->u.isp24.port_id[0], + le16_to_cpu(iocb->u.isp24.nport_handle), + iocb->u.isp24.exchange_address, iocb->ox_id); + + port_id.b.domain = iocb->u.isp24.port_id[2]; + port_id.b.area = iocb->u.isp24.port_id[1]; + port_id.b.al_pa = iocb->u.isp24.port_id[0]; + port_id.b.rsvd_1 = 0; + + loop_id = le16_to_cpu(iocb->u.isp24.nport_handle); + + fcport->loop_id = loop_id; + fcport->d_id = port_id; + qla24xx_post_nack_work(vha, fcport, iocb, SRB_NACK_PLOGI); + + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->plogi_link[QLT_PLOGI_LINK_SAME_WWN] == pla) + fcport->plogi_link[QLT_PLOGI_LINK_SAME_WWN] = NULL; + if (fcport->plogi_link[QLT_PLOGI_LINK_CONFLICT] == pla) + fcport->plogi_link[QLT_PLOGI_LINK_CONFLICT] = NULL; + } list_del(&pla->list); kmem_cache_free(qla_tgt_plogi_cachep, pla); } -static void -qlt_plogi_ack_link(struct scsi_qla_host *vha, qlt_plogi_ack_t *pla, - struct qla_tgt_sess *sess, qlt_plogi_link_t link) +void +qlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla, + struct fc_port *sess, enum qlt_plogi_link_t link) { + struct imm_ntfy_from_isp *iocb = &pla->iocb; /* Inc ref_count first because link might already be pointing at pla */ pla->ref_count++; + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf097, + "Linking sess %p [%d] wwn %8phC with PLOGI ACK to wwn %8phC" + " s_id %02x:%02x:%02x, ref=%d pla %p link %d\n", + sess, link, sess->port_name, + iocb->u.isp24.port_name, iocb->u.isp24.port_id[2], + iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0], + pla->ref_count, pla, link); + if (sess->plogi_link[link]) qlt_plogi_ack_unref(vha, sess->plogi_link[link]); - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf097, - "Linking sess %p [%d] wwn %8phC with PLOGI ACK to wwn %8phC" - " s_id %02x:%02x:%02x, ref=%d\n", sess, link, sess->port_name, - pla->iocb.u.isp24.port_name, pla->iocb.u.isp24.port_id[2], - pla->iocb.u.isp24.port_id[1], pla->iocb.u.isp24.port_id[0], - pla->ref_count); + if (link == QLT_PLOGI_LINK_SAME_WWN) + pla->fcport = sess; sess->plogi_link[link] = pla; } @@ -519,49 +913,45 @@ qlt_send_first_logo(struct scsi_qla_host *vha, qlt_port_logo_t *logo) static void qlt_free_session_done(struct work_struct *work) { - struct qla_tgt_sess *sess = container_of(work, struct qla_tgt_sess, + struct fc_port *sess = container_of(work, struct fc_port, free_work); struct qla_tgt *tgt = sess->tgt; struct scsi_qla_host *vha = sess->vha; struct qla_hw_data *ha = vha->hw; unsigned long flags; bool logout_started = false; - fc_port_t fcport; + struct event_arg ea; + scsi_qla_host_t *base_vha; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084, "%s: se_sess %p / sess %p from port %8phC loop_id %#04x" " s_id %02x:%02x:%02x logout %d keep %d els_logo %d\n", __func__, sess->se_sess, sess, sess->port_name, sess->loop_id, - sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa, + sess->d_id.b.domain, sess->d_id.b.area, sess->d_id.b.al_pa, sess->logout_on_delete, sess->keep_nport_handle, sess->send_els_logo); - BUG_ON(!tgt); - if (sess->send_els_logo) { - qlt_port_logo_t logo; - logo.id = sess->s_id; - logo.cmd_count = 0; - qlt_send_first_logo(vha, &logo); - } + if (!IS_SW_RESV_ADDR(sess->d_id)) { + if (sess->send_els_logo) { + qlt_port_logo_t logo; - if (sess->logout_on_delete) { - int rc; + logo.id = sess->d_id; + logo.cmd_count = 0; + qlt_send_first_logo(vha, &logo); + } - memset(&fcport, 0, sizeof(fcport)); - fcport.loop_id = sess->loop_id; - fcport.d_id = sess->s_id; - memcpy(fcport.port_name, sess->port_name, WWN_SIZE); - fcport.vha = vha; - fcport.tgt_session = sess; - - rc = qla2x00_post_async_logout_work(vha, &fcport, NULL); - if (rc != QLA_SUCCESS) - ql_log(ql_log_warn, vha, 0xf085, - "Schedule logo failed sess %p rc %d\n", - sess, rc); - else - logout_started = true; + if (sess->logout_on_delete) { + int rc; + + rc = qla2x00_post_async_logout_work(vha, sess, NULL); + if (rc != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0xf085, + "Schedule logo failed sess %p rc %d\n", + sess, rc); + else + logout_started = true; + } } /* @@ -583,29 +973,61 @@ static void qlt_free_session_done(struct work_struct *work) msleep(100); } - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf087, - "%s: sess %p logout completed\n", - __func__, sess); + ql_dbg(ql_dbg_disc, vha, 0xf087, + "%s: sess %p logout completed\n",__func__, sess); } - spin_lock_irqsave(&ha->hardware_lock, flags); + if (sess->logo_ack_needed) { + sess->logo_ack_needed = 0; + qla24xx_async_notify_ack(vha, sess, + (struct imm_ntfy_from_isp *)sess->iocb, SRB_NACK_LOGO); + } + + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + if (sess->se_sess) { + sess->se_sess = NULL; + if (tgt && !IS_SW_RESV_ADDR(sess->d_id)) + tgt->sess_count--; + } + + sess->disc_state = DSC_DELETED; + sess->fw_login_state = DSC_LS_PORT_UNAVAIL; + sess->deleted = QLA_SESS_DELETED; + sess->login_retry = vha->hw->login_retry_count; + + if (sess->login_succ && !IS_SW_RESV_ADDR(sess->d_id)) { + vha->fcport_count--; + sess->login_succ = 0; + } + + if (sess->chip_reset != sess->vha->hw->chip_reset) + qla2x00_clear_loop_id(sess); + + if (sess->conflict) { + sess->conflict->login_pause = 0; + sess->conflict = NULL; + if (!test_bit(UNLOADING, &vha->dpc_flags)) + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + } { - qlt_plogi_ack_t *own = + struct qlt_plogi_ack_t *own = sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]; - qlt_plogi_ack_t *con = + struct qlt_plogi_ack_t *con = sess->plogi_link[QLT_PLOGI_LINK_CONFLICT]; + struct imm_ntfy_from_isp *iocb; if (con) { + iocb = &con->iocb; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf099, - "se_sess %p / sess %p port %8phC is gone," - " %s (ref=%d), releasing PLOGI for %8phC (ref=%d)\n", - sess->se_sess, sess, sess->port_name, - own ? "releasing own PLOGI" : - "no own PLOGI pending", - own ? own->ref_count : -1, - con->iocb.u.isp24.port_name, con->ref_count); + "se_sess %p / sess %p port %8phC is gone," + " %s (ref=%d), releasing PLOGI for %8phC (ref=%d)\n", + sess->se_sess, sess, sess->port_name, + own ? "releasing own PLOGI" : "no own PLOGI pending", + own ? own->ref_count : -1, + iocb->u.isp24.port_name, con->ref_count); qlt_plogi_ack_unref(vha, con); + sess->plogi_link[QLT_PLOGI_LINK_CONFLICT] = NULL; } else { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09a, "se_sess %p / sess %p port %8phC is gone, %s (ref=%d)\n", @@ -615,59 +1037,64 @@ static void qlt_free_session_done(struct work_struct *work) own ? own->ref_count : -1); } - if (own) + if (own) { + sess->fw_login_state = DSC_LS_PLOGI_PEND; qlt_plogi_ack_unref(vha, own); + sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] = NULL; + } } - - list_del(&sess->sess_list_entry); - - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, - "Unregistration of sess %p finished\n", sess); + "Unregistration of sess %p %8phC finished fcp_cnt %d\n", + sess, sess->port_name, vha->fcport_count); - kfree(sess); - /* - * We need to protect against race, when tgt is freed before or - * inside wake_up() - */ - tgt->sess_count--; - if (tgt->sess_count == 0) + if (tgt && (tgt->sess_count == 0)) wake_up_all(&tgt->waitQ); + + if (vha->fcport_count == 0) + wake_up_all(&vha->fcport_waitQ); + + base_vha = pci_get_drvdata(ha->pdev); + if (test_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags)) + return; + + if (!tgt || !tgt->tgt_stop) { + memset(&ea, 0, sizeof(ea)); + ea.event = FCME_DELETE_DONE; + ea.fcport = sess; + qla2x00_fcport_event_handler(vha, &ea); + } } /* ha->tgt.sess_lock supposed to be held on entry */ -static void qlt_release_session(struct kref *kref) +void qlt_unreg_sess(struct fc_port *sess) { - struct qla_tgt_sess *sess = - container_of(kref, struct qla_tgt_sess, sess_kref); struct scsi_qla_host *vha = sess->vha; + ql_dbg(ql_dbg_disc, sess->vha, 0xffff, + "%s sess %p for deletion %8phC\n", + __func__, sess, sess->port_name); + if (sess->se_sess) vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess); - if (!list_empty(&sess->del_list_entry)) - list_del_init(&sess->del_list_entry); + qla2x00_mark_device_lost(vha, sess, 1, 1); + sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; + sess->disc_state = DSC_DELETE_PEND; + sess->last_rscn_gen = sess->rscn_gen; + sess->last_login_gen = sess->login_gen; INIT_WORK(&sess->free_work, qlt_free_session_done); schedule_work(&sess->free_work); } - -void qlt_put_sess(struct qla_tgt_sess *sess) -{ - if (!sess) - return; - - assert_spin_locked(&sess->vha->hw->tgt.sess_lock); - kref_put(&sess->sess_kref, qlt_release_session); -} -EXPORT_SYMBOL(qlt_put_sess); +EXPORT_SYMBOL(qlt_unreg_sess); static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) { struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess = NULL; + struct fc_port *sess = NULL; uint16_t loop_id; int res = 0; struct imm_ntfy_from_isp *n = (struct imm_ntfy_from_isp *)iocb; @@ -680,31 +1107,6 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) spin_lock_irqsave(&ha->tgt.sess_lock, flags); qlt_clear_tgt_db(vha->vha_tgt.qla_tgt); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); -#if 0 /* FIXME: do we need to choose a session here? */ - if (!list_empty(&ha->tgt.qla_tgt->sess_list)) { - sess = list_entry(ha->tgt.qla_tgt->sess_list.next, - typeof(*sess), sess_list_entry); - switch (mcmd) { - case QLA_TGT_NEXUS_LOSS_SESS: - mcmd = QLA_TGT_NEXUS_LOSS; - break; - case QLA_TGT_ABORT_ALL_SESS: - mcmd = QLA_TGT_ABORT_ALL; - break; - case QLA_TGT_NEXUS_LOSS: - case QLA_TGT_ABORT_ALL: - break; - default: - ql_dbg(ql_dbg_tgt, vha, 0xe046, - "qla_target(%d): Not allowed " - "command %x in %s", vha->vp_idx, - mcmd, __func__); - sess = NULL; - break; - } - } else - sess = NULL; -#endif } else { spin_lock_irqsave(&ha->tgt.sess_lock, flags); sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id); @@ -726,57 +1128,69 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) return qlt_issue_task_mgmt(sess, 0, mcmd, iocb, QLA24XX_MGMT_SEND_NACK); } +static void qla24xx_chk_fcp_state(struct fc_port *sess) +{ + if (sess->chip_reset != sess->vha->hw->chip_reset) { + sess->logout_on_delete = 0; + sess->logo_ack_needed = 0; + sess->fw_login_state = DSC_LS_PORT_UNAVAIL; + sess->scan_state = 0; + } +} + /* ha->tgt.sess_lock supposed to be held on entry */ -static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, +void qlt_schedule_sess_for_deletion(struct fc_port *sess, bool immediate) { struct qla_tgt *tgt = sess->tgt; - uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5; - if (sess->deleted) { - /* Upgrade to unconditional deletion in case it was temporary */ - if (immediate && sess->deleted == QLA_SESS_DELETION_PENDING) - list_del(&sess->del_list_entry); - else + if (sess->disc_state == DSC_DELETE_PEND) + return; + + if (sess->disc_state == DSC_DELETED) { + if (tgt && tgt->tgt_stop && (tgt->sess_count == 0)) + wake_up_all(&tgt->waitQ); + if (sess->vha->fcport_count == 0) + wake_up_all(&sess->vha->fcport_waitQ); + + if (!sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] && + !sess->plogi_link[QLT_PLOGI_LINK_CONFLICT]) return; } - ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, - "Scheduling sess %p for deletion\n", sess); + sess->disc_state = DSC_DELETE_PEND; - if (immediate) { - dev_loss_tmo = 0; - sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; - list_add(&sess->del_list_entry, &tgt->del_sess_list); - } else { - sess->deleted = QLA_SESS_DELETION_PENDING; - list_add_tail(&sess->del_list_entry, &tgt->del_sess_list); - } + if (sess->deleted == QLA_SESS_DELETED) + sess->logout_on_delete = 0; - sess->expires = jiffies + dev_loss_tmo * HZ; + sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; + qla24xx_chk_fcp_state(sess); - ql_dbg(ql_dbg_tgt, sess->vha, 0xe048, - "qla_target(%d): session for port %8phC (loop ID %d s_id %02x:%02x:%02x)" - " scheduled for deletion in %u secs (expires: %lu) immed: %d, logout: %d, gen: %#x\n", - sess->vha->vp_idx, sess->port_name, sess->loop_id, - sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa, - dev_loss_tmo, sess->expires, immediate, sess->logout_on_delete, - sess->generation); + ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, + "Scheduling sess %p for deletion\n", sess); - if (immediate) - mod_delayed_work(system_wq, &tgt->sess_del_work, 0); - else - schedule_delayed_work(&tgt->sess_del_work, - sess->expires - jiffies); + schedule_work(&sess->del_work); +} + +void qlt_schedule_sess_for_deletion_lock(struct fc_port *sess) +{ + unsigned long flags; + struct qla_hw_data *ha = sess->vha->hw; + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + qlt_schedule_sess_for_deletion(sess, 1); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); } /* ha->tgt.sess_lock supposed to be held on entry */ static void qlt_clear_tgt_db(struct qla_tgt *tgt) { - struct qla_tgt_sess *sess; + struct fc_port *sess; + scsi_qla_host_t *vha = tgt->vha; - list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) - qlt_schedule_sess_for_deletion(sess, true); + list_for_each_entry(sess, &vha->vp_fcports, list) { + if (sess->se_sess) + qlt_schedule_sess_for_deletion(sess, 1); + } /* At this point tgt could be already dead */ } @@ -830,240 +1244,84 @@ out_free_id_list: return res; } -/* ha->tgt.sess_lock supposed to be held on entry */ -static void qlt_undelete_sess(struct qla_tgt_sess *sess) -{ - BUG_ON(sess->deleted != QLA_SESS_DELETION_PENDING); - - list_del_init(&sess->del_list_entry); - sess->deleted = 0; -} - -static void qlt_del_sess_work_fn(struct delayed_work *work) -{ - struct qla_tgt *tgt = container_of(work, struct qla_tgt, - sess_del_work); - struct scsi_qla_host *vha = tgt->vha; - struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess; - unsigned long flags, elapsed; - - spin_lock_irqsave(&ha->tgt.sess_lock, flags); - while (!list_empty(&tgt->del_sess_list)) { - sess = list_entry(tgt->del_sess_list.next, typeof(*sess), - del_list_entry); - elapsed = jiffies; - if (time_after_eq(elapsed, sess->expires)) { - /* No turning back */ - list_del_init(&sess->del_list_entry); - sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004, - "Timeout: sess %p about to be deleted\n", - sess); - if (sess->se_sess) - ha->tgt.tgt_ops->shutdown_sess(sess); - qlt_put_sess(sess); - } else { - schedule_delayed_work(&tgt->sess_del_work, - sess->expires - elapsed); - break; - } - } - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); -} - /* * Adds an extra ref to allow to drop hw lock after adding sess to the list. * Caller must put it. */ -static struct qla_tgt_sess *qlt_create_sess( +static struct fc_port *qlt_create_sess( struct scsi_qla_host *vha, fc_port_t *fcport, bool local) { struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess; + struct fc_port *sess = fcport; unsigned long flags; - /* Check to avoid double sessions */ - spin_lock_irqsave(&ha->tgt.sess_lock, flags); - list_for_each_entry(sess, &vha->vha_tgt.qla_tgt->sess_list, - sess_list_entry) { - if (!memcmp(sess->port_name, fcport->port_name, WWN_SIZE)) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf005, - "Double sess %p found (s_id %x:%x:%x, " - "loop_id %d), updating to d_id %x:%x:%x, " - "loop_id %d", sess, sess->s_id.b.domain, - sess->s_id.b.al_pa, sess->s_id.b.area, - sess->loop_id, fcport->d_id.b.domain, - fcport->d_id.b.al_pa, fcport->d_id.b.area, - fcport->loop_id); - - /* Cannot undelete at this point */ - if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { - spin_unlock_irqrestore(&ha->tgt.sess_lock, - flags); - return NULL; - } - - if (sess->deleted) - qlt_undelete_sess(sess); - - if (!sess->se_sess) { - if (ha->tgt.tgt_ops->check_initiator_node_acl(vha, - &sess->port_name[0], sess) < 0) { - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - return NULL; - } - } - - kref_get(&sess->sess_kref); - ha->tgt.tgt_ops->update_sess(sess, fcport->d_id, fcport->loop_id, - (fcport->flags & FCF_CONF_COMP_SUPPORTED)); - - if (sess->local && !local) - sess->local = 0; - - qlt_do_generation_tick(vha, &sess->generation); - - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + if (vha->vha_tgt.qla_tgt->tgt_stop) + return NULL; - return sess; + if (fcport->se_sess) { + if (!kref_get_unless_zero(&sess->sess_kref)) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s: kref_get_unless_zero failed for %8phC\n", + __func__, sess->port_name); + return NULL; } - } - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - - sess = kzalloc(sizeof(*sess), GFP_KERNEL); - if (!sess) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04a, - "qla_target(%u): session allocation failed, all commands " - "from port %8phC will be refused", vha->vp_idx, - fcport->port_name); - - return NULL; + return fcport; } sess->tgt = vha->vha_tgt.qla_tgt; - sess->vha = vha; - sess->s_id = fcport->d_id; - sess->loop_id = fcport->loop_id; sess->local = local; - kref_init(&sess->sess_kref); - INIT_LIST_HEAD(&sess->del_list_entry); - /* Under normal circumstances we want to logout from firmware when + /* + * Under normal circumstances we want to logout from firmware when * session eventually ends and release corresponding nport handle. * In the exception cases (e.g. when new PLOGI is waiting) corresponding - * code will adjust these flags as necessary. */ + * code will adjust these flags as necessary. + */ sess->logout_on_delete = 1; sess->keep_nport_handle = 0; + sess->logout_completed = 0; - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, - "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n", - sess, vha->vha_tgt.qla_tgt); - - sess->conf_compl_supported = (fcport->flags & FCF_CONF_COMP_SUPPORTED); - BUILD_BUG_ON(sizeof(sess->port_name) != sizeof(fcport->port_name)); - memcpy(sess->port_name, fcport->port_name, sizeof(sess->port_name)); - - spin_lock_irqsave(&ha->tgt.sess_lock, flags); - list_add_tail(&sess->sess_list_entry, &vha->vha_tgt.qla_tgt->sess_list); - vha->vha_tgt.qla_tgt->sess_count++; - qlt_do_generation_tick(vha, &sess->generation); - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b, - "qla_target(%d): %ssession for wwn %8phC (loop_id %d, " - "s_id %x:%x:%x, confirmed completion %ssupported) added\n", - vha->vp_idx, local ? "local " : "", fcport->port_name, - fcport->loop_id, sess->s_id.b.domain, sess->s_id.b.area, - sess->s_id.b.al_pa, sess->conf_compl_supported ? "" : "not "); - - /* - * Determine if this fc_port->port_name is allowed to access - * target mode using explict NodeACLs+MappedLUNs, or using - * TPG demo mode. If this is successful a target mode FC nexus - * is created. - */ if (ha->tgt.tgt_ops->check_initiator_node_acl(vha, &fcport->port_name[0], sess) < 0) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xffff, + "(%d) %8phC check_initiator_node_acl failed\n", + vha->vp_idx, fcport->port_name); return NULL; } else { + kref_init(&fcport->sess_kref); /* - * Take an extra reference to ->sess_kref here to handle qla_tgt_sess - * access across ->tgt.sess_lock reaquire. + * Take an extra reference to ->sess_kref here to handle + * fc_port access across ->tgt.sess_lock reaquire. */ - kref_get(&sess->sess_kref); - } - - return sess; -} - -/* - * Called from qla2x00_reg_remote_port() - */ -void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) -{ - struct qla_hw_data *ha = vha->hw; - struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; - struct qla_tgt_sess *sess; - unsigned long flags; - - if (!vha->hw->tgt.tgt_ops) - return; - - if (!tgt || (fcport->port_type != FCT_INITIATOR)) - return; + if (!kref_get_unless_zero(&sess->sess_kref)) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s: kref_get_unless_zero failed for %8phC\n", + __func__, sess->port_name); + return NULL; + } - if (qla_ini_mode_enabled(vha)) - return; + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + if (!IS_SW_RESV_ADDR(sess->d_id)) + vha->vha_tgt.qla_tgt->sess_count++; - spin_lock_irqsave(&ha->tgt.sess_lock, flags); - if (tgt->tgt_stop) { + qlt_do_generation_tick(vha, &sess->generation); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - return; } - sess = qlt_find_sess_by_port_name(tgt, fcport->port_name); - if (!sess) { - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - - mutex_lock(&vha->vha_tgt.tgt_mutex); - sess = qlt_create_sess(vha, fcport, false); - mutex_unlock(&vha->vha_tgt.tgt_mutex); - spin_lock_irqsave(&ha->tgt.sess_lock, flags); - } else if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { - /* Point of no return */ - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - return; - } else { - kref_get(&sess->sess_kref); - - if (sess->deleted) { - qlt_undelete_sess(sess); - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c, - "qla_target(%u): %ssession for port %8phC " - "(loop ID %d) reappeared\n", vha->vp_idx, - sess->local ? "local " : "", sess->port_name, - sess->loop_id); + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, + "Adding sess %p se_sess %p to tgt %p sess_count %d\n", + sess, sess->se_sess, vha->vha_tgt.qla_tgt, + vha->vha_tgt.qla_tgt->sess_count); - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007, - "Reappeared sess %p\n", sess); - } - ha->tgt.tgt_ops->update_sess(sess, fcport->d_id, fcport->loop_id, - (fcport->flags & FCF_CONF_COMP_SUPPORTED)); - } + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b, + "qla_target(%d): %ssession for wwn %8phC (loop_id %d, " + "s_id %x:%x:%x, confirmed completion %ssupported) added\n", + vha->vp_idx, local ? "local " : "", fcport->port_name, + fcport->loop_id, sess->d_id.b.domain, sess->d_id.b.area, + sess->d_id.b.al_pa, sess->conf_compl_supported ? "" : "not "); - if (sess && sess->local) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d, - "qla_target(%u): local session for " - "port %8phC (loop ID %d) became global\n", vha->vp_idx, - fcport->port_name, sess->loop_id); - sess->local = 0; - } - qlt_put_sess(sess); - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + return sess; } /* @@ -1074,7 +1332,7 @@ void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport, int max_gen) { struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; - struct qla_tgt_sess *sess; + struct fc_port *sess = fcport; unsigned long flags; if (!vha->hw->tgt.tgt_ops) @@ -1088,8 +1346,7 @@ qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport, int max_gen) spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); return; } - sess = qlt_find_sess_by_port_name(tgt, fcport->port_name); - if (!sess) { + if (!sess->se_sess) { spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); return; } @@ -1120,12 +1377,12 @@ static inline int test_tgt_sess_count(struct qla_tgt *tgt) * We need to protect against race, when tgt is freed before or * inside wake_up() */ - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(&ha->tgt.sess_lock, flags); ql_dbg(ql_dbg_tgt, tgt->vha, 0xe002, - "tgt %p, empty(sess_list)=%d sess_count=%d\n", - tgt, list_empty(&tgt->sess_list), tgt->sess_count); + "tgt %p, sess_count=%d\n", + tgt, tgt->sess_count); res = (tgt->sess_count == 0); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); return res; } @@ -1173,8 +1430,6 @@ int qlt_stop_phase1(struct qla_tgt *tgt) mutex_unlock(&vha->vha_tgt.tgt_mutex); mutex_unlock(&qla_tgt_mutex); - flush_delayed_work(&tgt->sess_del_work); - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009, "Waiting for sess works (tgt %p)", tgt); spin_lock_irqsave(&tgt->sess_work_lock, flags); @@ -1186,14 +1441,13 @@ int qlt_stop_phase1(struct qla_tgt *tgt) spin_unlock_irqrestore(&tgt->sess_work_lock, flags); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00a, - "Waiting for tgt %p: list_empty(sess_list)=%d " - "sess_count=%d\n", tgt, list_empty(&tgt->sess_list), - tgt->sess_count); + "Waiting for tgt %p: sess_count=%d\n", tgt, tgt->sess_count); wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); /* Big hammer */ - if (!ha->flags.host_shutting_down && qla_tgt_mode_enabled(vha)) + if (!ha->flags.host_shutting_down && + (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha))) qlt_disable_vha(vha); /* Wait for sessions to clear out (just in case) */ @@ -1320,6 +1574,7 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha, nack = (struct nack_to_isp *)pkt; nack->ox_id = ntfy->ox_id; + nack->u.isp24.handle = QLA_TGT_SKIP_HANDLE; nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle; if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) { nack->u.isp24.flags = ntfy->u.isp24.flags & @@ -1489,6 +1744,14 @@ static int abort_cmd_for_tag(struct scsi_qla_host *vha, uint32_t tag) } } + list_for_each_entry(op, &vha->unknown_atio_list, cmd_list) { + if (tag == op->atio.u.isp24.exchange_addr) { + op->aborted = true; + spin_unlock(&vha->cmd_list_lock); + return 1; + } + } + list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) { if (tag == cmd->atio.u.isp24.exchange_addr) { cmd->aborted = 1; @@ -1525,6 +1788,18 @@ static void abort_cmds_for_lun(struct scsi_qla_host *vha, if (op_key == key && op_lun == lun) op->aborted = true; } + + list_for_each_entry(op, &vha->unknown_atio_list, cmd_list) { + uint32_t op_key; + u64 op_lun; + + op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id); + op_lun = scsilun_to_int( + (struct scsi_lun *)&op->atio.u.isp24.fcp_cmnd.lun); + if (op_key == key && op_lun == lun) + op->aborted = true; + } + list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) { uint32_t cmd_key; uint32_t cmd_lun; @@ -1540,7 +1815,7 @@ static void abort_cmds_for_lun(struct scsi_qla_host *vha, /* ha->hardware_lock supposed to be held on entry */ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, - struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess) + struct abts_recv_from_24xx *abts, struct fc_port *sess) { struct qla_hw_data *ha = vha->hw; struct se_session *se_sess = sess->se_sess; @@ -1549,8 +1824,9 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, u32 lun = 0; int rc; bool found_lun = false; + unsigned long flags; - spin_lock(&se_sess->sess_cmd_lock); + spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); list_for_each_entry(se_cmd, &se_sess->sess_cmd_list, se_cmd_list) { struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); @@ -1560,7 +1836,7 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, break; } } - spin_unlock(&se_sess->sess_cmd_lock); + spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); /* cmd not in LIO lists, look in qla list */ if (!found_lun) { @@ -1592,8 +1868,9 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, mcmd->sess = sess; memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts)); mcmd->reset_count = vha->hw->chip_reset; + mcmd->tmr_func = QLA_TGT_ABTS; - rc = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, TMR_ABORT_TASK, + rc = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, mcmd->tmr_func, abts->exchange_addr_to_abort); if (rc != 0) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf052, @@ -1613,7 +1890,7 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, struct abts_recv_from_24xx *abts) { struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess; + struct fc_port *sess; uint32_t tag = abts->exchange_addr_to_abort; uint8_t s_id[3]; int rc; @@ -1665,7 +1942,7 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + if (sess->deleted) { qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false); return; } @@ -1763,10 +2040,23 @@ void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd) return; } - if (mcmd->flags == QLA24XX_MGMT_SEND_NACK) - qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy, - 0, 0, 0, 0, 0, 0); - else { + if (mcmd->flags == QLA24XX_MGMT_SEND_NACK) { + if (mcmd->orig_iocb.imm_ntfy.u.isp24.status_subcode == + ELS_LOGO || + mcmd->orig_iocb.imm_ntfy.u.isp24.status_subcode == + ELS_PRLO || + mcmd->orig_iocb.imm_ntfy.u.isp24.status_subcode == + ELS_TPRLO) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "TM response logo %phC status %#x state %#x", + mcmd->sess->port_name, mcmd->fc_tm_rsp, + mcmd->flags); + qlt_schedule_sess_for_deletion_lock(mcmd->sess); + } else { + qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy, + 0, 0, 0, 0, 0, 0); + } + } else { if (mcmd->orig_iocb.atio.u.raw.entry_type == ABTS_RECV_24XX) qlt_24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts, mcmd->fc_tm_rsp, false); @@ -2182,95 +2472,6 @@ static inline int qlt_need_explicit_conf(struct qla_hw_data *ha, cmd->conf_compl_supported; } -#ifdef CONFIG_QLA_TGT_DEBUG_SRR -/* - * Original taken from the XFS code - */ -static unsigned long qlt_srr_random(void) -{ - static int Inited; - static unsigned long RandomValue; - static DEFINE_SPINLOCK(lock); - /* cycles pseudo-randomly through all values between 1 and 2^31 - 2 */ - register long rv; - register long lo; - register long hi; - unsigned long flags; - - spin_lock_irqsave(&lock, flags); - if (!Inited) { - RandomValue = jiffies; - Inited = 1; - } - rv = RandomValue; - hi = rv / 127773; - lo = rv % 127773; - rv = 16807 * lo - 2836 * hi; - if (rv <= 0) - rv += 2147483647; - RandomValue = rv; - spin_unlock_irqrestore(&lock, flags); - return rv; -} - -static void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) -{ -#if 0 /* This is not a real status packets lost, so it won't lead to SRR */ - if ((*xmit_type & QLA_TGT_XMIT_STATUS) && (qlt_srr_random() % 200) - == 50) { - *xmit_type &= ~QLA_TGT_XMIT_STATUS; - ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf015, - "Dropping cmd %p (tag %d) status", cmd, se_cmd->tag); - } -#endif - /* - * It's currently not possible to simulate SRRs for FCP_WRITE without - * a physical link layer failure, so don't even try here.. - */ - if (cmd->dma_data_direction != DMA_FROM_DEVICE) - return; - - if (qlt_has_data(cmd) && (cmd->sg_cnt > 1) && - ((qlt_srr_random() % 100) == 20)) { - int i, leave = 0; - unsigned int tot_len = 0; - - while (leave == 0) - leave = qlt_srr_random() % cmd->sg_cnt; - - for (i = 0; i < leave; i++) - tot_len += cmd->sg[i].length; - - ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf016, - "Cutting cmd %p (tag %d) buffer" - " tail to len %d, sg_cnt %d (cmd->bufflen %d," - " cmd->sg_cnt %d)", cmd, se_cmd->tag, tot_len, leave, - cmd->bufflen, cmd->sg_cnt); - - cmd->bufflen = tot_len; - cmd->sg_cnt = leave; - } - - if (qlt_has_data(cmd) && ((qlt_srr_random() % 100) == 70)) { - unsigned int offset = qlt_srr_random() % cmd->bufflen; - - ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf017, - "Cutting cmd %p (tag %d) buffer head " - "to offset %d (cmd->bufflen %d)", cmd, se_cmd->tag, offset, - cmd->bufflen); - if (offset == 0) - *xmit_type &= ~QLA_TGT_XMIT_DATA; - else if (qlt_set_data_offset(cmd, offset)) { - ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf018, - "qlt_set_data_offset() failed (tag %d)", se_cmd->tag); - } - } -} -#else -static inline void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) -{} -#endif - static void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio, struct qla_tgt_prm *prm) { @@ -2288,7 +2489,7 @@ static void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio, int i; if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) { - if (prm->cmd->se_cmd.scsi_status != 0) { + if ((prm->rq_result & SS_SCSI_STATUS_BYTE) != 0) { ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe017, "Skipping EXPLICIT_CONFORM and " "CTIO7_FLAGS_CONFORM_REQ for FCP READ w/ " @@ -2672,7 +2873,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, int res; spin_lock_irqsave(&ha->hardware_lock, flags); - if (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + if (cmd->sess && cmd->sess->deleted) { cmd->state = QLA_TGT_STATE_PROCESSED; if (cmd->sess->logout_completed) /* no need to terminate. FW already freed exchange. */ @@ -2685,7 +2886,6 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, spin_unlock_irqrestore(&ha->hardware_lock, flags); memset(&prm, 0, sizeof(prm)); - qlt_check_srr_debug(cmd, &xmit_type); ql_dbg(ql_dbg_tgt, cmd->vha, 0xe018, "is_send_status=%d, cmd->bufflen=%d, cmd->sg_cnt=%d, cmd->dma_data_direction=%d se_cmd[%p]\n", @@ -2848,7 +3048,7 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) spin_lock_irqsave(&ha->hardware_lock, flags); if (!vha->flags.online || (cmd->reset_count != ha->chip_reset) || - (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS)) { + (cmd->sess && cmd->sess->deleted)) { /* * Either the port is not online or this request was from * previous life, just abort the processing. @@ -3296,7 +3496,7 @@ int qlt_abort_cmd(struct qla_tgt_cmd *cmd) return EIO; } cmd->aborted = 1; - cmd->cmd_flags |= BIT_6; + cmd->trc_flags |= TRC_ABORT; spin_unlock_irqrestore(&cmd->cmd_lock, flags); qlt_send_term_exchange(vha, cmd, &cmd->atio, 0, 1); @@ -3306,7 +3506,7 @@ EXPORT_SYMBOL(qlt_abort_cmd); void qlt_free_cmd(struct qla_tgt_cmd *cmd) { - struct qla_tgt_sess *sess = cmd->sess; + struct fc_port *sess = cmd->sess; ql_dbg(ql_dbg_tgt, cmd->vha, 0xe074, "%s: se_cmd[%p] ox_id %04x\n", @@ -3335,90 +3535,6 @@ void qlt_free_cmd(struct qla_tgt_cmd *cmd) } EXPORT_SYMBOL(qlt_free_cmd); -/* ha->hardware_lock supposed to be held on entry */ -static int qlt_prepare_srr_ctio(struct scsi_qla_host *vha, - struct qla_tgt_cmd *cmd, void *ctio) -{ - struct qla_tgt_srr_ctio *sc; - struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; - struct qla_tgt_srr_imm *imm; - - tgt->ctio_srr_id++; - cmd->cmd_flags |= BIT_15; - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf019, - "qla_target(%d): CTIO with SRR status received\n", vha->vp_idx); - - if (!ctio) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf055, - "qla_target(%d): SRR CTIO, but ctio is NULL\n", - vha->vp_idx); - return -EINVAL; - } - - sc = kzalloc(sizeof(*sc), GFP_ATOMIC); - if (sc != NULL) { - sc->cmd = cmd; - /* IRQ is already OFF */ - spin_lock(&tgt->srr_lock); - sc->srr_id = tgt->ctio_srr_id; - list_add_tail(&sc->srr_list_entry, - &tgt->srr_ctio_list); - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01a, - "CTIO SRR %p added (id %d)\n", sc, sc->srr_id); - if (tgt->imm_srr_id == tgt->ctio_srr_id) { - int found = 0; - list_for_each_entry(imm, &tgt->srr_imm_list, - srr_list_entry) { - if (imm->srr_id == sc->srr_id) { - found = 1; - break; - } - } - if (found) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01b, - "Scheduling srr work\n"); - schedule_work(&tgt->srr_work); - } else { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf056, - "qla_target(%d): imm_srr_id " - "== ctio_srr_id (%d), but there is no " - "corresponding SRR IMM, deleting CTIO " - "SRR %p\n", vha->vp_idx, - tgt->ctio_srr_id, sc); - list_del(&sc->srr_list_entry); - spin_unlock(&tgt->srr_lock); - - kfree(sc); - return -EINVAL; - } - } - spin_unlock(&tgt->srr_lock); - } else { - struct qla_tgt_srr_imm *ti; - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf057, - "qla_target(%d): Unable to allocate SRR CTIO entry\n", - vha->vp_idx); - spin_lock(&tgt->srr_lock); - list_for_each_entry_safe(imm, ti, &tgt->srr_imm_list, - srr_list_entry) { - if (imm->srr_id == tgt->ctio_srr_id) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01c, - "IMM SRR %p deleted (id %d)\n", - imm, imm->srr_id); - list_del(&imm->srr_list_entry); - qlt_reject_free_srr_imm(vha, imm, 1); - } - } - spin_unlock(&tgt->srr_lock); - - return -ENOMEM; - } - - return 0; -} - /* * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ @@ -3527,7 +3643,7 @@ qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) dump_stack(); } - cmd->cmd_flags |= BIT_17; + cmd->trc_flags |= TRC_FLUSH; ha->tgt.tgt_ops->free_cmd(cmd); } @@ -3632,20 +3748,14 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, */ cmd->sess->logout_on_delete = 0; cmd->sess->send_els_logo = 1; - qlt_schedule_sess_for_deletion(cmd->sess, true); + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", + __func__, __LINE__, cmd->sess->port_name); + + qlt_schedule_sess_for_deletion_lock(cmd->sess); } break; } - case CTIO_SRR_RECEIVED: - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05a, - "qla_target(%d): CTIO with SRR_RECEIVED" - " status %x received (state %x, se_cmd %p)\n", - vha->vp_idx, status, cmd->state, se_cmd); - if (qlt_prepare_srr_ctio(vha, cmd, ctio) != 0) - break; - else - return; - case CTIO_DIF_ERROR: { struct ctio_crc_from_fw *crc = (struct ctio_crc_from_fw *)ctio; @@ -3693,7 +3803,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, */ if ((cmd->state != QLA_TGT_STATE_NEED_DATA) && (!cmd->aborted)) { - cmd->cmd_flags |= BIT_13; + cmd->trc_flags |= TRC_CTIO_ERR; if (qlt_term_ctio_exchange(vha, ctio, cmd, status)) return; } @@ -3701,7 +3811,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, skip_term: if (cmd->state == QLA_TGT_STATE_PROCESSED) { - cmd->cmd_flags |= BIT_12; + cmd->trc_flags |= TRC_CTIO_DONE; } else if (cmd->state == QLA_TGT_STATE_NEED_DATA) { cmd->state = QLA_TGT_STATE_DATA_IN; @@ -3711,11 +3821,11 @@ skip_term: ha->tgt.tgt_ops->handle_data(cmd); return; } else if (cmd->aborted) { - cmd->cmd_flags |= BIT_18; + cmd->trc_flags |= TRC_CTIO_ABORTED; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e, "Aborted command %p (tag %lld) finished\n", cmd, se_cmd->tag); } else { - cmd->cmd_flags |= BIT_19; + cmd->trc_flags |= TRC_CTIO_STRANGE; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c, "qla_target(%d): A command in state (%d) should " "not return a CTIO complete\n", vha->vp_idx, cmd->state); @@ -3762,7 +3872,7 @@ static inline int qlt_get_fcp_task_attr(struct scsi_qla_host *vha, return fcp_task_attr; } -static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *, +static struct fc_port *qlt_make_local_sess(struct scsi_qla_host *, uint8_t *); /* * Process context for I/O path into tcm_qla2xxx code @@ -3772,7 +3882,7 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) scsi_qla_host_t *vha = cmd->vha; struct qla_hw_data *ha = vha->hw; struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; - struct qla_tgt_sess *sess = cmd->sess; + struct fc_port *sess = cmd->sess; struct atio_from_isp *atio = &cmd->atio; unsigned char *cdb; unsigned long flags; @@ -3780,7 +3890,7 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) int ret, fcp_task_attr, data_dir, bidi = 0; cmd->cmd_in_wq = 0; - cmd->cmd_flags |= BIT_1; + cmd->trc_flags |= TRC_DO_WORK; if (tgt->tgt_stop) goto out_term; @@ -3822,7 +3932,7 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) * Drop extra session reference from qla_tgt_handle_cmd_for_atio*( */ spin_lock_irqsave(&ha->tgt.sess_lock, flags); - qlt_put_sess(sess); + ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); return; @@ -3832,7 +3942,7 @@ out_term: * cmd has not sent to target yet, so pass NULL as the second * argument to qlt_send_term_exchange() and free the memory here. */ - cmd->cmd_flags |= BIT_2; + cmd->trc_flags |= TRC_DO_WORK_ERR; spin_lock_irqsave(&ha->hardware_lock, flags); qlt_send_term_exchange(vha, NULL, &cmd->atio, 1, 0); @@ -3841,7 +3951,7 @@ out_term: spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->tgt.sess_lock, flags); - qlt_put_sess(sess); + ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); } @@ -3859,7 +3969,7 @@ static void qlt_do_work(struct work_struct *work) } static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha, - struct qla_tgt_sess *sess, + struct fc_port *sess, struct atio_from_isp *atio) { struct se_session *se_sess = sess->se_sess; @@ -3883,7 +3993,7 @@ static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha, cmd->loop_id = sess->loop_id; cmd->conf_compl_supported = sess->conf_compl_supported; - cmd->cmd_flags = 0; + cmd->trc_flags = 0; cmd->jiffies_at_alloc = get_jiffies_64(); cmd->reset_count = vha->hw->chip_reset; @@ -3900,7 +4010,7 @@ static void qlt_create_sess_from_atio(struct work_struct *work) struct qla_tgt_sess_op, work); scsi_qla_host_t *vha = op->vha; struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess; + struct fc_port *sess; struct qla_tgt_cmd *cmd; unsigned long flags; uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id; @@ -3941,11 +4051,12 @@ static void qlt_create_sess_from_atio(struct work_struct *work) if (!cmd) { spin_lock_irqsave(&ha->hardware_lock, flags); qlt_send_busy(vha, &op->atio, SAM_STAT_BUSY); - qlt_put_sess(sess); + ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->hardware_lock, flags); kfree(op); return; } + /* * __qlt_do_work() will call qlt_put_sess() to release * the extra reference taken above by qlt_make_local_sess() @@ -3953,13 +4064,11 @@ static void qlt_create_sess_from_atio(struct work_struct *work) __qlt_do_work(cmd); kfree(op); return; - out_term: spin_lock_irqsave(&ha->hardware_lock, flags); qlt_send_term_exchange(vha, NULL, &op->atio, 1, 0); spin_unlock_irqrestore(&ha->hardware_lock, flags); kfree(op); - } /* ha->hardware_lock supposed to be held on entry */ @@ -3968,8 +4077,9 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, { struct qla_hw_data *ha = vha->hw; struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; - struct qla_tgt_sess *sess; + struct fc_port *sess; struct qla_tgt_cmd *cmd; + unsigned long flags; if (unlikely(tgt->tgt_stop)) { ql_dbg(ql_dbg_io, vha, 0x3061, @@ -3998,7 +4108,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, /* Another WWN used to have our s_id. Our PLOGI scheduled its * session deletion, but it's still in sess_del_work wq */ - if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + if (sess->deleted) { ql_dbg(ql_dbg_io, vha, 0x3061, "New command while old session %p is being deleted\n", sess); @@ -4008,24 +4118,32 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, /* * Do kref_get() before returning + dropping qla_hw_data->hardware_lock. */ - kref_get(&sess->sess_kref); + if (!kref_get_unless_zero(&sess->sess_kref)) { + ql_dbg(ql_dbg_tgt, vha, 0xffff, + "%s: kref_get fail, %8phC oxid %x \n", + __func__, sess->port_name, + be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id)); + return -EFAULT; + } cmd = qlt_get_tag(vha, sess, atio); if (!cmd) { ql_dbg(ql_dbg_io, vha, 0x3062, "qla_target(%d): Allocation of cmd failed\n", vha->vp_idx); - qlt_put_sess(sess); + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); return -ENOMEM; } cmd->cmd_in_wq = 1; - cmd->cmd_flags |= BIT_0; + cmd->trc_flags |= TRC_NEW_CMD; cmd->se_cmd.cpuid = ha->msix_count ? ha->tgt.rspq_vector_cpuid : WORK_CPU_UNBOUND; - spin_lock(&vha->cmd_list_lock); + spin_lock_irqsave(&vha->cmd_list_lock, flags); list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list); - spin_unlock(&vha->cmd_list_lock); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); INIT_WORK(&cmd->work, qlt_do_work); if (ha->msix_count) { @@ -4043,7 +4161,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, } /* ha->hardware_lock supposed to be held on entry */ -static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, +static int qlt_issue_task_mgmt(struct fc_port *sess, u64 lun, int fn, void *iocb, int flags) { struct scsi_qla_host *vha = sess->vha; @@ -4051,7 +4169,6 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, struct qla_tgt_mgmt_cmd *mcmd; struct atio_from_isp *a = (struct atio_from_isp *)iocb; int res; - uint8_t tmr_func; mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC); if (!mcmd) { @@ -4073,74 +4190,12 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, mcmd->reset_count = vha->hw->chip_reset; switch (fn) { - case QLA_TGT_CLEAR_ACA: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10000, - "qla_target(%d): CLEAR_ACA received\n", sess->vha->vp_idx); - tmr_func = TMR_CLEAR_ACA; - break; - - case QLA_TGT_TARGET_RESET: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10001, - "qla_target(%d): TARGET_RESET received\n", - sess->vha->vp_idx); - tmr_func = TMR_TARGET_WARM_RESET; - break; - case QLA_TGT_LUN_RESET: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002, - "qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx); - tmr_func = TMR_LUN_RESET; - abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id); - break; - - case QLA_TGT_CLEAR_TS: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10003, - "qla_target(%d): CLEAR_TS received\n", sess->vha->vp_idx); - tmr_func = TMR_CLEAR_TASK_SET; - break; - - case QLA_TGT_ABORT_TS: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10004, - "qla_target(%d): ABORT_TS received\n", sess->vha->vp_idx); - tmr_func = TMR_ABORT_TASK_SET; - break; -#if 0 - case QLA_TGT_ABORT_ALL: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10005, - "qla_target(%d): Doing ABORT_ALL_TASKS\n", - sess->vha->vp_idx); - tmr_func = 0; - break; - - case QLA_TGT_ABORT_ALL_SESS: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10006, - "qla_target(%d): Doing ABORT_ALL_TASKS_SESS\n", - sess->vha->vp_idx); - tmr_func = 0; - break; - - case QLA_TGT_NEXUS_LOSS_SESS: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10007, - "qla_target(%d): Doing NEXUS_LOSS_SESS\n", - sess->vha->vp_idx); - tmr_func = 0; - break; - - case QLA_TGT_NEXUS_LOSS: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x10008, - "qla_target(%d): Doing NEXUS_LOSS\n", sess->vha->vp_idx); - tmr_func = 0; - break; -#endif - default: - ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000a, - "qla_target(%d): Unknown task mgmt fn 0x%x\n", - sess->vha->vp_idx, fn); - mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); - return -ENOSYS; + abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id); + break; } - res = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, tmr_func, 0); + res = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, mcmd->tmr_func, 0); if (res != 0) { ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000b, "qla_target(%d): tgt.tgt_ops->handle_tmr() failed: %d\n", @@ -4158,7 +4213,7 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) struct atio_from_isp *a = (struct atio_from_isp *)iocb; struct qla_hw_data *ha = vha->hw; struct qla_tgt *tgt; - struct qla_tgt_sess *sess; + struct fc_port *sess; uint32_t lun, unpacked_lun; int fn; unsigned long flags; @@ -4183,7 +4238,7 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) sizeof(struct atio_from_isp)); } - if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) + if (sess->deleted) return -EFAULT; return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0); @@ -4191,7 +4246,7 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) /* ha->hardware_lock supposed to be held on entry */ static int __qlt_abort_task(struct scsi_qla_host *vha, - struct imm_ntfy_from_isp *iocb, struct qla_tgt_sess *sess) + struct imm_ntfy_from_isp *iocb, struct fc_port *sess) { struct atio_from_isp *a = (struct atio_from_isp *)iocb; struct qla_hw_data *ha = vha->hw; @@ -4215,8 +4270,9 @@ static int __qlt_abort_task(struct scsi_qla_host *vha, lun = a->u.isp24.fcp_cmnd.lun; unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); mcmd->reset_count = vha->hw->chip_reset; + mcmd->tmr_func = QLA_TGT_2G_ABORT_TASK; - rc = ha->tgt.tgt_ops->handle_tmr(mcmd, unpacked_lun, TMR_ABORT_TASK, + rc = ha->tgt.tgt_ops->handle_tmr(mcmd, unpacked_lun, mcmd->tmr_func, le16_to_cpu(iocb->u.isp2x.seq_id)); if (rc != 0) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf060, @@ -4234,7 +4290,7 @@ static int qlt_abort_task(struct scsi_qla_host *vha, struct imm_ntfy_from_isp *iocb) { struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess; + struct fc_port *sess; int loop_id; unsigned long flags; @@ -4257,22 +4313,20 @@ static int qlt_abort_task(struct scsi_qla_host *vha, void qlt_logo_completion_handler(fc_port_t *fcport, int rc) { - if (fcport->tgt_session) { - if (rc != MBS_COMMAND_COMPLETE) { - ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093, - "%s: se_sess %p / sess %p from" - " port %8phC loop_id %#04x s_id %02x:%02x:%02x" - " LOGO failed: %#x\n", - __func__, - fcport->tgt_session->se_sess, - fcport->tgt_session, - fcport->port_name, fcport->loop_id, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa, rc); - } - - fcport->tgt_session->logout_completed = 1; + if (rc != MBS_COMMAND_COMPLETE) { + ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093, + "%s: se_sess %p / sess %p from" + " port %8phC loop_id %#04x s_id %02x:%02x:%02x" + " LOGO failed: %#x\n", + __func__, + fcport->se_sess, + fcport, + fcport->port_name, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa, rc); } + + fcport->logout_completed = 1; } /* @@ -4282,16 +4336,16 @@ void qlt_logo_completion_handler(fc_port_t *fcport, int rc) * deletion. Returns existing session with matching wwn if present. * Null otherwise. */ -static struct qla_tgt_sess * -qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, - port_id_t port_id, uint16_t loop_id, struct qla_tgt_sess **conflict_sess) +struct fc_port * +qlt_find_sess_invalidate_other(scsi_qla_host_t *vha, uint64_t wwn, + port_id_t port_id, uint16_t loop_id, struct fc_port **conflict_sess) { - struct qla_tgt_sess *sess = NULL, *other_sess; + struct fc_port *sess = NULL, *other_sess; uint64_t other_wwn; *conflict_sess = NULL; - list_for_each_entry(other_sess, &tgt->sess_list, sess_list_entry) { + list_for_each_entry(other_sess, &vha->vp_fcports, list) { other_wwn = wwn_to_u64(other_sess->port_name); @@ -4302,9 +4356,9 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, } /* find other sess with nport_id collision */ - if (port_id.b24 == other_sess->s_id.b24) { + if (port_id.b24 == other_sess->d_id.b24) { if (loop_id != other_sess->loop_id) { - ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000c, + ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000c, "Invalidating sess %p loop_id %d wwn %llx.\n", other_sess, other_sess->loop_id, other_wwn); @@ -4320,6 +4374,11 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, * Another wwn used to have our s_id/loop_id * kill the session, but don't free the loop_id */ + ql_dbg(ql_dbg_tgt_tmr, vha, 0xffff, + "Invalidating sess %p loop_id %d wwn %llx.\n", + other_sess, other_sess->loop_id, other_wwn); + + other_sess->keep_nport_handle = 1; *conflict_sess = other_sess; qlt_schedule_sess_for_deletion(other_sess, @@ -4329,8 +4388,9 @@ qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, } /* find other sess with nport handle collision */ - if (loop_id == other_sess->loop_id) { - ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000d, + if ((loop_id == other_sess->loop_id) && + (loop_id != FC_NO_LOOP_ID)) { + ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000d, "Invalidating sess %p loop_id %d wwn %llx.\n", other_sess, other_sess->loop_id, other_wwn); @@ -4358,11 +4418,21 @@ static int abort_cmds_for_s_id(struct scsi_qla_host *vha, port_id_t *s_id) spin_lock(&vha->cmd_list_lock); list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) { uint32_t op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id); + if (op_key == key) { op->aborted = true; count++; } } + + list_for_each_entry(op, &vha->unknown_atio_list, cmd_list) { + uint32_t op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id); + if (op_key == key) { + op->aborted = true; + count++; + } + } + list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) { uint32_t cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id); if (cmd_key == key) { @@ -4383,13 +4453,13 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, { struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess = NULL, *conflict_sess = NULL; + struct fc_port *sess = NULL, *conflict_sess = NULL; uint64_t wwn; port_id_t port_id; uint16_t loop_id; uint16_t wd3_lo; int res = 0; - qlt_plogi_ack_t *pla; + struct qlt_plogi_ack_t *pla; unsigned long flags; wwn = wwn_to_u64(iocb->u.isp24.port_name); @@ -4401,9 +4471,12 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, loop_id = le16_to_cpu(iocb->u.isp24.nport_handle); - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026, - "qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n", - vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode); + ql_dbg(ql_dbg_disc, vha, 0xf026, + "qla_target(%d): Port ID: %02x:%02x:%02x ELS opcode: 0x%02x lid %d %8phC\n", + vha->vp_idx, iocb->u.isp24.port_id[2], + iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0], + iocb->u.isp24.status_subcode, loop_id, + iocb->u.isp24.port_name); /* res = 1 means ack at the end of thread * res = 0 means ack async/later. @@ -4416,12 +4489,12 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, if (wwn) { spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags); - sess = qlt_find_sess_invalidate_other(tgt, wwn, - port_id, loop_id, &conflict_sess); + sess = qlt_find_sess_invalidate_other(vha, wwn, + port_id, loop_id, &conflict_sess); spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); } - if (IS_SW_RESV_ADDR(port_id) || (!sess && !conflict_sess)) { + if (IS_SW_RESV_ADDR(port_id)) { res = 1; break; } @@ -4429,42 +4502,66 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, pla = qlt_plogi_ack_find_add(vha, &port_id, iocb); if (!pla) { qlt_send_term_imm_notif(vha, iocb, 1); - - res = 0; break; } res = 0; - if (conflict_sess) + if (conflict_sess) { + conflict_sess->login_gen++; qlt_plogi_ack_link(vha, pla, conflict_sess, - QLT_PLOGI_LINK_CONFLICT); + QLT_PLOGI_LINK_CONFLICT); + } - if (!sess) + if (!sess) { + pla->ref_count++; + qla24xx_post_newsess_work(vha, &port_id, + iocb->u.isp24.port_name, pla); + res = 0; break; + } qlt_plogi_ack_link(vha, pla, sess, QLT_PLOGI_LINK_SAME_WWN); - /* - * Under normal circumstances we want to release nport handle - * during LOGO process to avoid nport handle leaks inside FW. - * The exception is when LOGO is done while another PLOGI with - * the same nport handle is waiting as might be the case here. - * Note: there is always a possibily of a race where session - * deletion has already started for other reasons (e.g. ACL - * removal) and now PLOGI arrives: - * 1. if PLOGI arrived in FW after nport handle has been freed, - * FW must have assigned this PLOGI a new/same handle and we - * can proceed ACK'ing it as usual when session deletion - * completes. - * 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT - * bit reached it, the handle has now been released. We'll - * get an error when we ACK this PLOGI. Nothing will be sent - * back to initiator. Initiator should eventually retry - * PLOGI and situation will correct itself. - */ - sess->keep_nport_handle = ((sess->loop_id == loop_id) && - (sess->s_id.b24 == port_id.b24)); - qlt_schedule_sess_for_deletion(sess, true); + sess->fw_login_state = DSC_LS_PLOGI_PEND; + sess->d_id = port_id; + sess->login_gen++; + + switch (sess->disc_state) { + case DSC_DELETED: + qlt_plogi_ack_unref(vha, pla); + break; + + default: + /* + * Under normal circumstances we want to release nport handle + * during LOGO process to avoid nport handle leaks inside FW. + * The exception is when LOGO is done while another PLOGI with + * the same nport handle is waiting as might be the case here. + * Note: there is always a possibily of a race where session + * deletion has already started for other reasons (e.g. ACL + * removal) and now PLOGI arrives: + * 1. if PLOGI arrived in FW after nport handle has been freed, + * FW must have assigned this PLOGI a new/same handle and we + * can proceed ACK'ing it as usual when session deletion + * completes. + * 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT + * bit reached it, the handle has now been released. We'll + * get an error when we ACK this PLOGI. Nothing will be sent + * back to initiator. Initiator should eventually retry + * PLOGI and situation will correct itself. + */ + sess->keep_nport_handle = ((sess->loop_id == loop_id) && + (sess->d_id.b24 == port_id.b24)); + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post del sess\n", + __func__, __LINE__, sess->port_name); + + + qlt_schedule_sess_for_deletion_lock(sess); + break; + } + break; case ELS_PRLI: @@ -4472,8 +4569,8 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, if (wwn) { spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags); - sess = qlt_find_sess_invalidate_other(tgt, wwn, port_id, - loop_id, &conflict_sess); + sess = qlt_find_sess_invalidate_other(vha, wwn, port_id, + loop_id, &conflict_sess); spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); } @@ -4487,7 +4584,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, } if (sess != NULL) { - if (sess->deleted) { + if (sess->fw_login_state == DSC_LS_PLOGI_PEND) { /* * Impatient initiator sent PRLI before last * PLOGI could finish. Will force him to re-try, @@ -4511,11 +4608,16 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, sess->local = 0; sess->loop_id = loop_id; - sess->s_id = port_id; + sess->d_id = port_id; + sess->fw_login_state = DSC_LS_PRLI_PEND; if (wd3_lo & BIT_7) sess->conf_compl_supported = 1; + if ((wd3_lo & BIT_4) == 0) + sess->port_type = FCT_INITIATOR; + else + sess->port_type = FCT_TARGET; } res = 1; /* send notify ack */ @@ -4525,15 +4627,61 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); qla2xxx_wake_dpc(vha); } else { - /* todo: else - create sess here. */ - res = 1; /* send notify ack */ - } + if (sess) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post nack\n", + __func__, __LINE__, sess->port_name); + qla24xx_post_nack_work(vha, sess, iocb, + SRB_NACK_PRLI); + res = 0; + } + } break; + + case ELS_TPRLO: + if (le16_to_cpu(iocb->u.isp24.flags) & + NOTIFY24XX_FLAGS_GLOBAL_TPRLO) { + loop_id = 0xFFFF; + qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS); + res = 1; + break; + } + /* drop through */ case ELS_LOGO: case ELS_PRLO: + spin_lock_irqsave(&ha->tgt.sess_lock, flags); + sess = qla2x00_find_fcport_by_loopid(vha, loop_id); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + + if (sess) { + sess->login_gen++; + sess->fw_login_state = DSC_LS_LOGO_PEND; + sess->logo_ack_needed = 1; + memcpy(sess->iocb, iocb, IOCB_SIZE); + } + res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS); + + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s: logo %llx res %d sess %p ", + __func__, wwn, res, sess); + if (res == 0) { + /* + * cmd went upper layer, look for qlt_xmit_tm_rsp() + * for LOGO_ACK & sess delete + */ + BUG_ON(!sess); + res = 0; + } else { + /* cmd did not go to upper layer. */ + if (sess) { + qlt_schedule_sess_for_deletion_lock(sess); + res = 0; + } + /* else logo will be ack */ + } break; case ELS_PDISC: case ELS_ADISC: @@ -4544,6 +4692,16 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, 0, 0, 0, 0, 0, 0); tgt->link_reinit_iocb_pending = 0; } + + sess = qla2x00_find_fcport_by_wwpn(vha, + iocb->u.isp24.port_name, 1); + if (sess) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "sess %p lid %d|%d DS %d LS %d\n", + sess, sess->loop_id, loop_id, + sess->disc_state, sess->fw_login_state); + } + res = 1; /* send notify ack */ break; } @@ -4560,451 +4718,6 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, return res; } -static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset) -{ -#if 1 - /* - * FIXME: Reject non zero SRR relative offset until we can test - * this code properly. - */ - pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset); - return -1; -#else - struct scatterlist *sg, *sgp, *sg_srr, *sg_srr_start = NULL; - size_t first_offset = 0, rem_offset = offset, tmp = 0; - int i, sg_srr_cnt, bufflen = 0; - - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe023, - "Entering qla_tgt_set_data_offset: cmd: %p, cmd->sg: %p, " - "cmd->sg_cnt: %u, direction: %d\n", - cmd, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); - - if (!cmd->sg || !cmd->sg_cnt) { - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe055, - "Missing cmd->sg or zero cmd->sg_cnt in" - " qla_tgt_set_data_offset\n"); - return -EINVAL; - } - /* - * Walk the current cmd->sg list until we locate the new sg_srr_start - */ - for_each_sg(cmd->sg, sg, cmd->sg_cnt, i) { - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe024, - "sg[%d]: %p page: %p, length: %d, offset: %d\n", - i, sg, sg_page(sg), sg->length, sg->offset); - - if ((sg->length + tmp) > offset) { - first_offset = rem_offset; - sg_srr_start = sg; - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe025, - "Found matching sg[%d], using %p as sg_srr_start, " - "and using first_offset: %zu\n", i, sg, - first_offset); - break; - } - tmp += sg->length; - rem_offset -= sg->length; - } - - if (!sg_srr_start) { - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe056, - "Unable to locate sg_srr_start for offset: %u\n", offset); - return -EINVAL; - } - sg_srr_cnt = (cmd->sg_cnt - i); - - sg_srr = kzalloc(sizeof(struct scatterlist) * sg_srr_cnt, GFP_KERNEL); - if (!sg_srr) { - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe057, - "Unable to allocate sgp\n"); - return -ENOMEM; - } - sg_init_table(sg_srr, sg_srr_cnt); - sgp = &sg_srr[0]; - /* - * Walk the remaining list for sg_srr_start, mapping to the newly - * allocated sg_srr taking first_offset into account. - */ - for_each_sg(sg_srr_start, sg, sg_srr_cnt, i) { - if (first_offset) { - sg_set_page(sgp, sg_page(sg), - (sg->length - first_offset), first_offset); - first_offset = 0; - } else { - sg_set_page(sgp, sg_page(sg), sg->length, 0); - } - bufflen += sgp->length; - - sgp = sg_next(sgp); - if (!sgp) - break; - } - - cmd->sg = sg_srr; - cmd->sg_cnt = sg_srr_cnt; - cmd->bufflen = bufflen; - cmd->offset += offset; - cmd->free_sg = 1; - - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe026, "New cmd->sg: %p\n", cmd->sg); - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe027, "New cmd->sg_cnt: %u\n", - cmd->sg_cnt); - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe028, "New cmd->bufflen: %u\n", - cmd->bufflen); - ql_dbg(ql_dbg_tgt, cmd->vha, 0xe029, "New cmd->offset: %u\n", - cmd->offset); - - if (cmd->sg_cnt < 0) - BUG(); - - if (cmd->bufflen < 0) - BUG(); - - return 0; -#endif -} - -static inline int qlt_srr_adjust_data(struct qla_tgt_cmd *cmd, - uint32_t srr_rel_offs, int *xmit_type) -{ - int res = 0, rel_offs; - - rel_offs = srr_rel_offs - cmd->offset; - ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf027, "srr_rel_offs=%d, rel_offs=%d", - srr_rel_offs, rel_offs); - - *xmit_type = QLA_TGT_XMIT_ALL; - - if (rel_offs < 0) { - ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf062, - "qla_target(%d): SRR rel_offs (%d) < 0", - cmd->vha->vp_idx, rel_offs); - res = -1; - } else if (rel_offs == cmd->bufflen) - *xmit_type = QLA_TGT_XMIT_STATUS; - else if (rel_offs > 0) - res = qlt_set_data_offset(cmd, rel_offs); - - return res; -} - -/* No locks, thread context */ -static void qlt_handle_srr(struct scsi_qla_host *vha, - struct qla_tgt_srr_ctio *sctio, struct qla_tgt_srr_imm *imm) -{ - struct imm_ntfy_from_isp *ntfy = - (struct imm_ntfy_from_isp *)&imm->imm_ntfy; - struct qla_hw_data *ha = vha->hw; - struct qla_tgt_cmd *cmd = sctio->cmd; - struct se_cmd *se_cmd = &cmd->se_cmd; - unsigned long flags; - int xmit_type = 0, resp = 0; - uint32_t offset; - uint16_t srr_ui; - - offset = le32_to_cpu(ntfy->u.isp24.srr_rel_offs); - srr_ui = ntfy->u.isp24.srr_ui; - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf028, "SRR cmd %p, srr_ui %x\n", - cmd, srr_ui); - - switch (srr_ui) { - case SRR_IU_STATUS: - spin_lock_irqsave(&ha->hardware_lock, flags); - qlt_send_notify_ack(vha, ntfy, - 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - xmit_type = QLA_TGT_XMIT_STATUS; - resp = 1; - break; - case SRR_IU_DATA_IN: - if (!cmd->sg || !cmd->sg_cnt) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf063, - "Unable to process SRR_IU_DATA_IN due to" - " missing cmd->sg, state: %d\n", cmd->state); - dump_stack(); - goto out_reject; - } - if (se_cmd->scsi_status != 0) { - ql_dbg(ql_dbg_tgt, vha, 0xe02a, - "Rejecting SRR_IU_DATA_IN with non GOOD " - "scsi_status\n"); - goto out_reject; - } - cmd->bufflen = se_cmd->data_length; - - if (qlt_has_data(cmd)) { - if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0) - goto out_reject; - spin_lock_irqsave(&ha->hardware_lock, flags); - qlt_send_notify_ack(vha, ntfy, - 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - resp = 1; - } else { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf064, - "qla_target(%d): SRR for in data for cmd without them (tag %lld, SCSI status %d), reject", - vha->vp_idx, se_cmd->tag, - cmd->se_cmd.scsi_status); - goto out_reject; - } - break; - case SRR_IU_DATA_OUT: - if (!cmd->sg || !cmd->sg_cnt) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf065, - "Unable to process SRR_IU_DATA_OUT due to" - " missing cmd->sg\n"); - dump_stack(); - goto out_reject; - } - if (se_cmd->scsi_status != 0) { - ql_dbg(ql_dbg_tgt, vha, 0xe02b, - "Rejecting SRR_IU_DATA_OUT" - " with non GOOD scsi_status\n"); - goto out_reject; - } - cmd->bufflen = se_cmd->data_length; - - if (qlt_has_data(cmd)) { - if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0) - goto out_reject; - spin_lock_irqsave(&ha->hardware_lock, flags); - qlt_send_notify_ack(vha, ntfy, - 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (xmit_type & QLA_TGT_XMIT_DATA) { - cmd->cmd_flags |= BIT_8; - qlt_rdy_to_xfer(cmd); - } - } else { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf066, - "qla_target(%d): SRR for out data for cmd without them (tag %lld, SCSI status %d), reject", - vha->vp_idx, se_cmd->tag, cmd->se_cmd.scsi_status); - goto out_reject; - } - break; - default: - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf067, - "qla_target(%d): Unknown srr_ui value %x", - vha->vp_idx, srr_ui); - goto out_reject; - } - - /* Transmit response in case of status and data-in cases */ - if (resp) { - cmd->cmd_flags |= BIT_7; - qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status); - } - - return; - -out_reject: - spin_lock_irqsave(&ha->hardware_lock, flags); - qlt_send_notify_ack(vha, ntfy, 0, 0, 0, - NOTIFY_ACK_SRR_FLAGS_REJECT, - NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, - NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); - if (cmd->state == QLA_TGT_STATE_NEED_DATA) { - cmd->state = QLA_TGT_STATE_DATA_IN; - dump_stack(); - } else { - cmd->cmd_flags |= BIT_9; - qlt_send_term_exchange(vha, cmd, &cmd->atio, 1, 0); - } - spin_unlock_irqrestore(&ha->hardware_lock, flags); -} - -static void qlt_reject_free_srr_imm(struct scsi_qla_host *vha, - struct qla_tgt_srr_imm *imm, int ha_locked) -{ - struct qla_hw_data *ha = vha->hw; - unsigned long flags = 0; - -#ifndef __CHECKER__ - if (!ha_locked) - spin_lock_irqsave(&ha->hardware_lock, flags); -#endif - - qlt_send_notify_ack(vha, (void *)&imm->imm_ntfy, 0, 0, 0, - NOTIFY_ACK_SRR_FLAGS_REJECT, - NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, - NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); - -#ifndef __CHECKER__ - if (!ha_locked) - spin_unlock_irqrestore(&ha->hardware_lock, flags); -#endif - - kfree(imm); -} - -static void qlt_handle_srr_work(struct work_struct *work) -{ - struct qla_tgt *tgt = container_of(work, struct qla_tgt, srr_work); - struct scsi_qla_host *vha = tgt->vha; - struct qla_tgt_srr_ctio *sctio; - unsigned long flags; - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf029, "Entering SRR work (tgt %p)\n", - tgt); - -restart: - spin_lock_irqsave(&tgt->srr_lock, flags); - list_for_each_entry(sctio, &tgt->srr_ctio_list, srr_list_entry) { - struct qla_tgt_srr_imm *imm, *i, *ti; - struct qla_tgt_cmd *cmd; - struct se_cmd *se_cmd; - - imm = NULL; - list_for_each_entry_safe(i, ti, &tgt->srr_imm_list, - srr_list_entry) { - if (i->srr_id == sctio->srr_id) { - list_del(&i->srr_list_entry); - if (imm) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf068, - "qla_target(%d): There must be " - "only one IMM SRR per CTIO SRR " - "(IMM SRR %p, id %d, CTIO %p\n", - vha->vp_idx, i, i->srr_id, sctio); - qlt_reject_free_srr_imm(tgt->vha, i, 0); - } else - imm = i; - } - } - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02a, - "IMM SRR %p, CTIO SRR %p (id %d)\n", imm, sctio, - sctio->srr_id); - - if (imm == NULL) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02b, - "Not found matching IMM for SRR CTIO (id %d)\n", - sctio->srr_id); - continue; - } else - list_del(&sctio->srr_list_entry); - - spin_unlock_irqrestore(&tgt->srr_lock, flags); - - cmd = sctio->cmd; - /* - * Reset qla_tgt_cmd SRR values and SGL pointer+count to follow - * tcm_qla2xxx_write_pending() and tcm_qla2xxx_queue_data_in() - * logic.. - */ - cmd->offset = 0; - if (cmd->free_sg) { - kfree(cmd->sg); - cmd->sg = NULL; - cmd->free_sg = 0; - } - se_cmd = &cmd->se_cmd; - - cmd->sg_cnt = se_cmd->t_data_nents; - cmd->sg = se_cmd->t_data_sg; - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02c, - "SRR cmd %p (se_cmd %p, tag %lld, op %x), sg_cnt=%d, offset=%d", - cmd, &cmd->se_cmd, se_cmd->tag, se_cmd->t_task_cdb ? - se_cmd->t_task_cdb[0] : 0, cmd->sg_cnt, cmd->offset); - - qlt_handle_srr(vha, sctio, imm); - - kfree(imm); - kfree(sctio); - goto restart; - } - spin_unlock_irqrestore(&tgt->srr_lock, flags); -} - -/* ha->hardware_lock supposed to be held on entry */ -static void qlt_prepare_srr_imm(struct scsi_qla_host *vha, - struct imm_ntfy_from_isp *iocb) -{ - struct qla_tgt_srr_imm *imm; - struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; - struct qla_tgt_srr_ctio *sctio; - - tgt->imm_srr_id++; - - ql_log(ql_log_warn, vha, 0xf02d, "qla_target(%d): SRR received\n", - vha->vp_idx); - - imm = kzalloc(sizeof(*imm), GFP_ATOMIC); - if (imm != NULL) { - memcpy(&imm->imm_ntfy, iocb, sizeof(imm->imm_ntfy)); - - /* IRQ is already OFF */ - spin_lock(&tgt->srr_lock); - imm->srr_id = tgt->imm_srr_id; - list_add_tail(&imm->srr_list_entry, - &tgt->srr_imm_list); - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02e, - "IMM NTFY SRR %p added (id %d, ui %x)\n", - imm, imm->srr_id, iocb->u.isp24.srr_ui); - if (tgt->imm_srr_id == tgt->ctio_srr_id) { - int found = 0; - list_for_each_entry(sctio, &tgt->srr_ctio_list, - srr_list_entry) { - if (sctio->srr_id == imm->srr_id) { - found = 1; - break; - } - } - if (found) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02f, "%s", - "Scheduling srr work\n"); - schedule_work(&tgt->srr_work); - } else { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf030, - "qla_target(%d): imm_srr_id " - "== ctio_srr_id (%d), but there is no " - "corresponding SRR CTIO, deleting IMM " - "SRR %p\n", vha->vp_idx, tgt->ctio_srr_id, - imm); - list_del(&imm->srr_list_entry); - - kfree(imm); - - spin_unlock(&tgt->srr_lock); - goto out_reject; - } - } - spin_unlock(&tgt->srr_lock); - } else { - struct qla_tgt_srr_ctio *ts; - - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf069, - "qla_target(%d): Unable to allocate SRR IMM " - "entry, SRR request will be rejected\n", vha->vp_idx); - - /* IRQ is already OFF */ - spin_lock(&tgt->srr_lock); - list_for_each_entry_safe(sctio, ts, &tgt->srr_ctio_list, - srr_list_entry) { - if (sctio->srr_id == tgt->imm_srr_id) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf031, - "CTIO SRR %p deleted (id %d)\n", - sctio, sctio->srr_id); - list_del(&sctio->srr_list_entry); - qlt_send_term_exchange(vha, sctio->cmd, - &sctio->cmd->atio, 1, 0); - kfree(sctio); - } - } - spin_unlock(&tgt->srr_lock); - goto out_reject; - } - - return; - -out_reject: - qlt_send_notify_ack(vha, iocb, 0, 0, 0, - NOTIFY_ACK_SRR_FLAGS_REJECT, - NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, - NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); -} - /* * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ @@ -5126,12 +4839,6 @@ static void qlt_handle_imm_notify(struct scsi_qla_host *vha, if (qlt_24xx_handle_els(vha, iocb) == 0) send_notify_ack = 0; break; - - case IMM_NTFY_SRR: - qlt_prepare_srr_imm(vha, iocb); - send_notify_ack = 0; - break; - default: ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06d, "qla_target(%d): Received unknown immediate " @@ -5153,7 +4860,7 @@ static int __qlt_send_busy(struct scsi_qla_host *vha, struct ctio7_to_24xx *ctio24; struct qla_hw_data *ha = vha->hw; request_t *pkt; - struct qla_tgt_sess *sess = NULL; + struct fc_port *sess = NULL; unsigned long flags; spin_lock_irqsave(&ha->tgt.sess_lock, flags); @@ -5214,7 +4921,7 @@ qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, { struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess; + struct fc_port *sess; struct se_session *se_sess; struct qla_tgt_cmd *cmd; int tag; @@ -5756,6 +5463,32 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); break; + case MBA_REJECTED_FCP_CMD: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xffff, + "qla_target(%d): Async event LS_REJECT occurred " + "(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, + le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); + + if (le16_to_cpu(mailbox[3]) == 1) { + /* exchange starvation. */ + vha->hw->exch_starvation++; + if (vha->hw->exch_starvation > 5) { + ql_log(ql_log_warn, vha, 0xffff, + "Exchange starvation-. Resetting RISC\n"); + + vha->hw->exch_starvation = 0; + if (IS_P3P_TYPE(vha->hw)) + set_bit(FCOE_CTX_RESET_NEEDED, + &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } + } + break; + case MBA_PORT_UPDATE: ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03d, "qla_target(%d): Port update async event %#x " @@ -5765,14 +5498,14 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); login_code = le16_to_cpu(mailbox[2]); - if (login_code == 0x4) + if (login_code == 0x4) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03e, "Async MB 2: Got PLOGI Complete\n"); - else if (login_code == 0x7) + vha->hw->exch_starvation = 0; + } else if (login_code == 0x7) ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f, "Async MB 2: Port Logged Out\n"); break; - default: break; } @@ -5783,8 +5516,10 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, uint16_t loop_id) { - fc_port_t *fcport; + fc_port_t *fcport, *tfcp, *del; int rc; + unsigned long flags; + u8 newfcport = 0; fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); if (!fcport) { @@ -5806,18 +5541,82 @@ static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, return NULL; } + del = NULL; + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + tfcp = qla2x00_find_fcport_by_wwpn(vha, fcport->port_name, 1); + + if (tfcp) { + tfcp->d_id = fcport->d_id; + tfcp->port_type = fcport->port_type; + tfcp->supported_classes = fcport->supported_classes; + tfcp->flags |= fcport->flags; + + del = fcport; + fcport = tfcp; + } else { + if (vha->hw->current_topology == ISP_CFG_F) + fcport->flags |= FCF_FABRIC_DEVICE; + + list_add_tail(&fcport->list, &vha->vp_fcports); + if (!IS_SW_RESV_ADDR(fcport->d_id)) + vha->fcport_count++; + fcport->login_gen++; + fcport->disc_state = DSC_LOGIN_COMPLETE; + fcport->login_succ = 1; + newfcport = 1; + } + + fcport->deleted = 0; + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + + switch (vha->host->active_mode) { + case MODE_INITIATOR: + case MODE_DUAL: + if (newfcport) { + if (!IS_IIDMA_CAPABLE(vha->hw) || !vha->hw->flags.gpsc_supported) { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post upd_fcport fcp_cnt %d\n", + __func__, __LINE__, fcport->port_name, vha->fcport_count); + qla24xx_post_upd_fcport_work(vha, fcport); + } else { + ql_dbg(ql_dbg_disc, vha, 0xffff, + "%s %d %8phC post gpsc fcp_cnt %d\n", + __func__, __LINE__, fcport->port_name, vha->fcport_count); + qla24xx_post_gpsc_work(vha, fcport); + } + } + break; + + case MODE_TARGET: + default: + break; + } + if (del) + qla2x00_free_fcport(del); + return fcport; } /* Must be called under tgt_mutex */ -static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha, +static struct fc_port *qlt_make_local_sess(struct scsi_qla_host *vha, uint8_t *s_id) { - struct qla_tgt_sess *sess = NULL; + struct fc_port *sess = NULL; fc_port_t *fcport = NULL; int rc, global_resets; uint16_t loop_id = 0; + if ((s_id[0] == 0xFF) && (s_id[1] == 0xFC)) { + /* + * This is Domain Controller, so it should be + * OK to drop SCSI commands from it. + */ + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042, + "Unable to find initiator with S_ID %x:%x:%x", + s_id[0], s_id[1], s_id[2]); + return NULL; + } + mutex_lock(&vha->vha_tgt.tgt_mutex); retry: @@ -5828,21 +5627,11 @@ retry: if (rc != 0) { mutex_unlock(&vha->vha_tgt.tgt_mutex); - if ((s_id[0] == 0xFF) && - (s_id[1] == 0xFC)) { - /* - * This is Domain Controller, so it should be - * OK to drop SCSI commands from it. - */ - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042, - "Unable to find initiator with S_ID %x:%x:%x", - s_id[0], s_id[1], s_id[2]); - } else - ql_log(ql_log_info, vha, 0xf071, - "qla_target(%d): Unable to find " - "initiator with S_ID %x:%x:%x", - vha->vp_idx, s_id[0], s_id[1], - s_id[2]); + ql_log(ql_log_info, vha, 0xf071, + "qla_target(%d): Unable to find " + "initiator with S_ID %x:%x:%x", + vha->vp_idx, s_id[0], s_id[1], + s_id[2]); if (rc == -ENOENT) { qlt_port_logo_t logo; @@ -5875,7 +5664,6 @@ retry: mutex_unlock(&vha->vha_tgt.tgt_mutex); - kfree(fcport); return sess; } @@ -5884,7 +5672,7 @@ static void qlt_abort_work(struct qla_tgt *tgt, { struct scsi_qla_host *vha = tgt->vha; struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess = NULL; + struct fc_port *sess = NULL; unsigned long flags = 0, flags2 = 0; uint32_t be_s_id; uint8_t s_id[3]; @@ -5911,12 +5699,18 @@ static void qlt_abort_work(struct qla_tgt *tgt, if (!sess) goto out_term2; } else { - if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + if (sess->deleted) { sess = NULL; goto out_term2; } - kref_get(&sess->sess_kref); + if (!kref_get_unless_zero(&sess->sess_kref)) { + ql_dbg(ql_dbg_tgt_tmr, vha, 0xffff, + "%s: kref_get fail %8phC \n", + __func__, sess->port_name); + sess = NULL; + goto out_term2; + } } spin_lock_irqsave(&ha->hardware_lock, flags); @@ -5928,8 +5722,8 @@ static void qlt_abort_work(struct qla_tgt *tgt, if (rc != 0) goto out_term; spin_unlock_irqrestore(&ha->hardware_lock, flags); - - qlt_put_sess(sess); + if (sess) + ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2); return; @@ -5940,7 +5734,8 @@ out_term: qlt_24xx_send_abts_resp(vha, &prm->abts, FCP_TMF_REJECTED, false); spin_unlock_irqrestore(&ha->hardware_lock, flags); - qlt_put_sess(sess); + if (sess) + ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2); } @@ -5950,7 +5745,7 @@ static void qlt_tmr_work(struct qla_tgt *tgt, struct atio_from_isp *a = &prm->tm_iocb2; struct scsi_qla_host *vha = tgt->vha; struct qla_hw_data *ha = vha->hw; - struct qla_tgt_sess *sess = NULL; + struct fc_port *sess = NULL; unsigned long flags; uint8_t *s_id = NULL; /* to hide compiler warnings */ int rc; @@ -5975,12 +5770,18 @@ static void qlt_tmr_work(struct qla_tgt *tgt, if (!sess) goto out_term; } else { - if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + if (sess->deleted) { sess = NULL; goto out_term; } - kref_get(&sess->sess_kref); + if (!kref_get_unless_zero(&sess->sess_kref)) { + ql_dbg(ql_dbg_tgt_tmr, vha, 0xffff, + "%s: kref_get fail %8phC\n", + __func__, sess->port_name); + sess = NULL; + goto out_term; + } } iocb = a; @@ -5992,13 +5793,13 @@ static void qlt_tmr_work(struct qla_tgt *tgt, if (rc != 0) goto out_term; - qlt_put_sess(sess); + ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); return; out_term: qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1, 0); - qlt_put_sess(sess); + ha->tgt.tgt_ops->put_sess(sess); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); } @@ -6075,17 +5876,10 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) tgt->ha = ha; tgt->vha = base_vha; init_waitqueue_head(&tgt->waitQ); - INIT_LIST_HEAD(&tgt->sess_list); INIT_LIST_HEAD(&tgt->del_sess_list); - INIT_DELAYED_WORK(&tgt->sess_del_work, - (void (*)(struct work_struct *))qlt_del_sess_work_fn); spin_lock_init(&tgt->sess_work_lock); INIT_WORK(&tgt->sess_work, qlt_sess_work_fn); INIT_LIST_HEAD(&tgt->sess_works_list); - spin_lock_init(&tgt->srr_lock); - INIT_LIST_HEAD(&tgt->srr_ctio_list); - INIT_LIST_HEAD(&tgt->srr_imm_list); - INIT_WORK(&tgt->srr_work, qlt_handle_srr_work); atomic_set(&tgt->tgt_global_resets_count, 0); base_vha->vha_tgt.qla_tgt = tgt; @@ -6251,29 +6045,25 @@ EXPORT_SYMBOL(qlt_lport_deregister); /* Must be called under HW lock */ static void qlt_set_mode(struct scsi_qla_host *vha) { - struct qla_hw_data *ha = vha->hw; - switch (ql2x_ini_mode) { case QLA2XXX_INI_MODE_DISABLED: case QLA2XXX_INI_MODE_EXCLUSIVE: vha->host->active_mode = MODE_TARGET; break; case QLA2XXX_INI_MODE_ENABLED: - vha->host->active_mode |= MODE_TARGET; + vha->host->active_mode = MODE_UNKNOWN; + break; + case QLA2XXX_INI_MODE_DUAL: + vha->host->active_mode = MODE_DUAL; break; default: break; } - - if (ha->tgt.ini_mode_force_reverse) - qla_reverse_ini_mode(vha); } /* Must be called under HW lock */ static void qlt_clear_mode(struct scsi_qla_host *vha) { - struct qla_hw_data *ha = vha->hw; - switch (ql2x_ini_mode) { case QLA2XXX_INI_MODE_DISABLED: vha->host->active_mode = MODE_UNKNOWN; @@ -6282,14 +6072,12 @@ static void qlt_clear_mode(struct scsi_qla_host *vha) vha->host->active_mode = MODE_INITIATOR; break; case QLA2XXX_INI_MODE_ENABLED: - vha->host->active_mode &= ~MODE_TARGET; + case QLA2XXX_INI_MODE_DUAL: + vha->host->active_mode = MODE_INITIATOR; break; default: break; } - - if (ha->tgt.ini_mode_force_reverse) - qla_reverse_ini_mode(vha); } /* @@ -6377,9 +6165,6 @@ static void qlt_disable_vha(struct scsi_qla_host *vha) void qlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha) { - if (!qla_tgt_mode_enabled(vha)) - return; - vha->vha_tgt.qla_tgt = NULL; mutex_init(&vha->vha_tgt.tgt_mutex); @@ -6405,13 +6190,11 @@ qlt_rff_id(struct scsi_qla_host *vha, struct ct_sns_req *ct_req) * FC-4 Feature bit 0 indicates target functionality to the name server. */ if (qla_tgt_mode_enabled(vha)) { - if (qla_ini_mode_enabled(vha)) - ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1; - else - ct_req->req.rff_id.fc4_feature = BIT_0; + ct_req->req.rff_id.fc4_feature = BIT_0; } else if (qla_ini_mode_enabled(vha)) { ct_req->req.rff_id.fc4_feature = BIT_1; - } + } else if (qla_dual_mode_enabled(vha)) + ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1; } /* @@ -6430,7 +6213,7 @@ qlt_init_atio_q_entries(struct scsi_qla_host *vha) uint16_t cnt; struct atio_from_isp *pkt = (struct atio_from_isp *)ha->tgt.atio_ring; - if (!qla_tgt_mode_enabled(vha)) + if (qla_ini_mode_enabled(vha)) return; for (cnt = 0; cnt < ha->tgt.atio_q_length; cnt++) { @@ -6523,8 +6306,10 @@ void qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) { struct qla_hw_data *ha = vha->hw; + u32 tmp; + u16 t; - if (qla_tgt_mode_enabled(vha)) { + if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) { if (!ha->tgt.saved_set) { /* We save only once */ ha->tgt.saved_exchange_count = nv->exchange_count; @@ -6537,13 +6322,30 @@ qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) ha->tgt.saved_set = 1; } - nv->exchange_count = cpu_to_le16(0xFFFF); + if (qla_tgt_mode_enabled(vha)) { + nv->exchange_count = cpu_to_le16(0xFFFF); + } else { /* dual */ + if (ql_dm_tgt_ex_pct > 100) { + ql_dm_tgt_ex_pct = 50; + } else if (ql_dm_tgt_ex_pct == 100) { + /* leave some for FW */ + ql_dm_tgt_ex_pct = 95; + } + + tmp = ha->orig_fw_xcb_count * ql_dm_tgt_ex_pct; + tmp = tmp/100; + if (tmp > 0xffff) + tmp = 0xffff; + + t = tmp & 0xffff; + nv->exchange_count = cpu_to_le16(t); + } /* Enable target mode */ nv->firmware_options_1 |= cpu_to_le32(BIT_4); /* Disable ini mode, if requested */ - if (!qla_ini_mode_enabled(vha)) + if (qla_tgt_mode_enabled(vha)) nv->firmware_options_1 |= cpu_to_le32(BIT_5); /* Disable Full Login after LIP */ @@ -6622,11 +6424,13 @@ void qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) { struct qla_hw_data *ha = vha->hw; + u32 tmp; + u16 t; if (!QLA_TGT_MODE_ENABLED()) return; - if (qla_tgt_mode_enabled(vha)) { + if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) { if (!ha->tgt.saved_set) { /* We save only once */ ha->tgt.saved_exchange_count = nv->exchange_count; @@ -6639,13 +6443,29 @@ qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) ha->tgt.saved_set = 1; } - nv->exchange_count = cpu_to_le16(0xFFFF); + if (qla_tgt_mode_enabled(vha)) { + nv->exchange_count = cpu_to_le16(0xFFFF); + } else { /* dual */ + if (ql_dm_tgt_ex_pct > 100) { + ql_dm_tgt_ex_pct = 50; + } else if (ql_dm_tgt_ex_pct == 100) { + /* leave some for FW */ + ql_dm_tgt_ex_pct = 95; + } + + tmp = ha->orig_fw_xcb_count * ql_dm_tgt_ex_pct; + tmp = tmp/100; + if (tmp > 0xffff) + tmp = 0xffff; + t = tmp & 0xffff; + nv->exchange_count = cpu_to_le16(t); + } /* Enable target mode */ nv->firmware_options_1 |= cpu_to_le32(BIT_4); /* Disable ini mode, if requested */ - if (!qla_ini_mode_enabled(vha)) + if (qla_tgt_mode_enabled(vha)) nv->firmware_options_1 |= cpu_to_le32(BIT_5); /* Disable Full Login after LIP */ nv->firmware_options_1 &= cpu_to_le32(~BIT_13); @@ -6749,10 +6569,12 @@ void qlt_modify_vp_config(struct scsi_qla_host *vha, struct vp_config_entry_24xx *vpmod) { - if (qla_tgt_mode_enabled(vha)) + /* enable target mode. Bit5 = 1 => disable */ + if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) vpmod->options_idx1 &= ~BIT_5; - /* Disable ini mode, if requested */ - if (!qla_ini_mode_enabled(vha)) + + /* Disable ini mode, if requested. bit4 = 1 => disable */ + if (qla_tgt_mode_enabled(vha)) vpmod->options_idx1 &= ~BIT_4; } @@ -6772,6 +6594,11 @@ qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha) mutex_init(&base_vha->vha_tgt.tgt_mutex); mutex_init(&base_vha->vha_tgt.tgt_host_action_mutex); + + INIT_LIST_HEAD(&base_vha->unknown_atio_list); + INIT_DELAYED_WORK(&base_vha->unknown_atio_work, + qlt_unknown_atio_work_fn); + qlt_clear_mode(base_vha); } @@ -6906,6 +6733,8 @@ static int __init qlt_parse_ini_mode(void) ql2x_ini_mode = QLA2XXX_INI_MODE_DISABLED; else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_ENABLED) == 0) ql2x_ini_mode = QLA2XXX_INI_MODE_ENABLED; + else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_DUAL) == 0) + ql2x_ini_mode = QLA2XXX_INI_MODE_DUAL; else return false; @@ -6935,9 +6764,8 @@ int __init qlt_init(void) } qla_tgt_plogi_cachep = kmem_cache_create("qla_tgt_plogi_cachep", - sizeof(qlt_plogi_ack_t), - __alignof__(qlt_plogi_ack_t), - 0, NULL); + sizeof(struct qlt_plogi_ack_t), __alignof__(struct qlt_plogi_ack_t), + 0, NULL); if (!qla_tgt_plogi_cachep) { ql_log(ql_log_fatal, NULL, 0xe06d, diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index 0824a8164a24..a7f90dcaae37 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -45,10 +45,12 @@ #define QLA2XXX_INI_MODE_STR_EXCLUSIVE "exclusive" #define QLA2XXX_INI_MODE_STR_DISABLED "disabled" #define QLA2XXX_INI_MODE_STR_ENABLED "enabled" +#define QLA2XXX_INI_MODE_STR_DUAL "dual" #define QLA2XXX_INI_MODE_EXCLUSIVE 0 #define QLA2XXX_INI_MODE_DISABLED 1 #define QLA2XXX_INI_MODE_ENABLED 2 +#define QLA2XXX_INI_MODE_DUAL 3 #define QLA2XXX_COMMAND_COUNT_INIT 250 #define QLA2XXX_IMMED_NOTIFY_COUNT_INIT 250 @@ -118,84 +120,6 @@ ? le16_to_cpu((iocb)->u.isp2x.target.extended) \ : (uint16_t)(iocb)->u.isp2x.target.id.standard) -#ifndef IMMED_NOTIFY_TYPE -#define IMMED_NOTIFY_TYPE 0x0D /* Immediate notify entry. */ -/* - * ISP queue - immediate notify entry structure definition. - * This is sent by the ISP to the Target driver. - * This IOCB would have report of events sent by the - * initiator, that needs to be handled by the target - * driver immediately. - */ -struct imm_ntfy_from_isp { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ - uint8_t entry_status; /* Entry Status. */ - union { - struct { - uint32_t sys_define_2; /* System defined. */ - target_id_t target; - uint16_t lun; - uint8_t target_id; - uint8_t reserved_1; - uint16_t status_modifier; - uint16_t status; - uint16_t task_flags; - uint16_t seq_id; - uint16_t srr_rx_id; - uint32_t srr_rel_offs; - uint16_t srr_ui; -#define SRR_IU_DATA_IN 0x1 -#define SRR_IU_DATA_OUT 0x5 -#define SRR_IU_STATUS 0x7 - uint16_t srr_ox_id; - uint8_t reserved_2[28]; - } isp2x; - struct { - uint32_t reserved; - uint16_t nport_handle; - uint16_t reserved_2; - uint16_t flags; -#define NOTIFY24XX_FLAGS_GLOBAL_TPRLO BIT_1 -#define NOTIFY24XX_FLAGS_PUREX_IOCB BIT_0 - uint16_t srr_rx_id; - uint16_t status; - uint8_t status_subcode; - uint8_t fw_handle; - uint32_t exchange_address; - uint32_t srr_rel_offs; - uint16_t srr_ui; - uint16_t srr_ox_id; - union { - struct { - uint8_t node_name[8]; - } plogi; /* PLOGI/ADISC/PDISC */ - struct { - /* PRLI word 3 bit 0-15 */ - uint16_t wd3_lo; - uint8_t resv0[6]; - } prli; - struct { - uint8_t port_id[3]; - uint8_t resv1; - uint16_t nport_handle; - uint16_t resv2; - } req_els; - } u; - uint8_t port_name[8]; - uint8_t resv3[3]; - uint8_t vp_index; - uint32_t reserved_5; - uint8_t port_id[3]; - uint8_t reserved_6; - } isp24; - } u; - uint16_t reserved_7; - uint16_t ox_id; -} __packed; -#endif - #ifndef NOTIFY_ACK_TYPE #define NOTIFY_ACK_TYPE 0x0E /* Notify acknowledge entry. */ /* @@ -731,7 +655,7 @@ struct abts_resp_from_24xx_fw { \********************************************************************/ struct qla_tgt_mgmt_cmd; -struct qla_tgt_sess; +struct fc_port; /* * This structure provides a template of function calls that the @@ -744,21 +668,22 @@ struct qla_tgt_func_tmpl { unsigned char *, uint32_t, int, int, int); void (*handle_data)(struct qla_tgt_cmd *); void (*handle_dif_err)(struct qla_tgt_cmd *); - int (*handle_tmr)(struct qla_tgt_mgmt_cmd *, uint32_t, uint8_t, + int (*handle_tmr)(struct qla_tgt_mgmt_cmd *, uint32_t, uint16_t, uint32_t); void (*free_cmd)(struct qla_tgt_cmd *); void (*free_mcmd)(struct qla_tgt_mgmt_cmd *); - void (*free_session)(struct qla_tgt_sess *); + void (*free_session)(struct fc_port *); int (*check_initiator_node_acl)(struct scsi_qla_host *, unsigned char *, - struct qla_tgt_sess *); - void (*update_sess)(struct qla_tgt_sess *, port_id_t, uint16_t, bool); - struct qla_tgt_sess *(*find_sess_by_loop_id)(struct scsi_qla_host *, + struct fc_port *); + void (*update_sess)(struct fc_port *, port_id_t, uint16_t, bool); + struct fc_port *(*find_sess_by_loop_id)(struct scsi_qla_host *, const uint16_t); - struct qla_tgt_sess *(*find_sess_by_s_id)(struct scsi_qla_host *, + struct fc_port *(*find_sess_by_s_id)(struct scsi_qla_host *, const uint8_t *); - void (*clear_nacl_from_fcport_map)(struct qla_tgt_sess *); - void (*shutdown_sess)(struct qla_tgt_sess *); + void (*clear_nacl_from_fcport_map)(struct fc_port *); + void (*put_sess)(struct fc_port *); + void (*shutdown_sess)(struct fc_port *); }; int qla2x00_wait_for_hba_online(struct scsi_qla_host *); @@ -795,6 +720,8 @@ int qla2x00_wait_for_hba_online(struct scsi_qla_host *); #define QLA_TGT_ABORT_ALL 0xFFFE #define QLA_TGT_NEXUS_LOSS_SESS 0xFFFD #define QLA_TGT_NEXUS_LOSS 0xFFFC +#define QLA_TGT_ABTS 0xFFFB +#define QLA_TGT_2G_ABORT_TASK 0xFFFA /* Notify Acknowledge flags */ #define NOTIFY_ACK_RES_COUNT BIT_8 @@ -872,12 +799,8 @@ struct qla_tgt { /* Count of sessions refering qla_tgt. Protected by hardware_lock. */ int sess_count; - /* Protected by hardware_lock. Addition also protected by tgt_mutex. */ - struct list_head sess_list; - /* Protected by hardware_lock */ struct list_head del_sess_list; - struct delayed_work sess_del_work; spinlock_t sess_work_lock; struct list_head sess_works_list; @@ -888,16 +811,7 @@ struct qla_tgt { int notify_ack_expected; int abts_resp_expected; int modify_lun_expected; - - int ctio_srr_id; - int imm_srr_id; - spinlock_t srr_lock; - struct list_head srr_ctio_list; - struct list_head srr_imm_list; - struct work_struct srr_work; - atomic_t tgt_global_resets_count; - struct list_head tgt_list_entry; }; @@ -910,92 +824,32 @@ struct qla_tgt_sess_op { bool aborted; }; -enum qla_sess_deletion { - QLA_SESS_DELETION_NONE = 0, - QLA_SESS_DELETION_PENDING = 1, /* hopefully we can get rid of - * this one */ - QLA_SESS_DELETION_IN_PROGRESS = 2, -}; - -typedef enum { - QLT_PLOGI_LINK_SAME_WWN, - QLT_PLOGI_LINK_CONFLICT, - QLT_PLOGI_LINK_MAX -} qlt_plogi_link_t; - -typedef struct { - struct list_head list; - struct imm_ntfy_from_isp iocb; - port_id_t id; - int ref_count; -} qlt_plogi_ack_t; - -/* - * Equivilant to IT Nexus (Initiator-Target) - */ -struct qla_tgt_sess { - uint16_t loop_id; - port_id_t s_id; - - unsigned int conf_compl_supported:1; - unsigned int deleted:2; - unsigned int local:1; - unsigned int logout_on_delete:1; - unsigned int keep_nport_handle:1; - unsigned int send_els_logo:1; - - unsigned char logout_completed; - - int generation; - - struct se_session *se_sess; - struct kref sess_kref; - struct scsi_qla_host *vha; - struct qla_tgt *tgt; - - struct list_head sess_list_entry; - unsigned long expires; - struct list_head del_list_entry; - - uint8_t port_name[WWN_SIZE]; - struct work_struct free_work; - - qlt_plogi_ack_t *plogi_link[QLT_PLOGI_LINK_MAX]; +enum trace_flags { + TRC_NEW_CMD = BIT_0, + TRC_DO_WORK = BIT_1, + TRC_DO_WORK_ERR = BIT_2, + TRC_XFR_RDY = BIT_3, + TRC_XMIT_DATA = BIT_4, + TRC_XMIT_STATUS = BIT_5, + TRC_SRR_RSP = BIT_6, + TRC_SRR_XRDY = BIT_7, + TRC_SRR_TERM = BIT_8, + TRC_SRR_CTIO = BIT_9, + TRC_FLUSH = BIT_10, + TRC_CTIO_ERR = BIT_11, + TRC_CTIO_DONE = BIT_12, + TRC_CTIO_ABORTED = BIT_13, + TRC_CTIO_STRANGE= BIT_14, + TRC_CMD_DONE = BIT_15, + TRC_CMD_CHK_STOP = BIT_16, + TRC_CMD_FREE = BIT_17, + TRC_DATA_IN = BIT_18, + TRC_ABORT = BIT_19, }; -typedef enum { - /* - * BIT_0 - Atio Arrival / schedule to work - * BIT_1 - qlt_do_work - * BIT_2 - qlt_do work failed - * BIT_3 - xfer rdy/tcm_qla2xxx_write_pending - * BIT_4 - read respond/tcm_qla2xx_queue_data_in - * BIT_5 - status respond / tcm_qla2xx_queue_status - * BIT_6 - tcm request to abort/Term exchange. - * pre_xmit_response->qlt_send_term_exchange - * BIT_7 - SRR received (qlt_handle_srr->qlt_xmit_response) - * BIT_8 - SRR received (qlt_handle_srr->qlt_rdy_to_xfer) - * BIT_9 - SRR received (qla_handle_srr->qlt_send_term_exchange) - * BIT_10 - Data in - hanlde_data->tcm_qla2xxx_handle_data - - * BIT_12 - good completion - qlt_ctio_do_completion -->free_cmd - * BIT_13 - Bad completion - - * qlt_ctio_do_completion --> qlt_term_ctio_exchange - * BIT_14 - Back end data received/sent. - * BIT_15 - SRR prepare ctio - * BIT_16 - complete free - * BIT_17 - flush - qlt_abort_cmd_on_host_reset - * BIT_18 - completion w/abort status - * BIT_19 - completion w/unknown status - * BIT_20 - tcm_qla2xxx_free_cmd - */ - CMD_FLAG_DATA_WORK = BIT_11, - CMD_FLAG_DATA_WORK_FREE = BIT_21, -} cmd_flags_t; - struct qla_tgt_cmd { struct se_cmd se_cmd; - struct qla_tgt_sess *sess; + struct fc_port *sess; int state; struct work_struct free_work; struct work_struct work; @@ -1014,6 +868,8 @@ struct qla_tgt_cmd { unsigned int cmd_sent_to_fw:1; unsigned int cmd_in_wq:1; unsigned int aborted:1; + unsigned int data_work:1; + unsigned int data_work_free:1; struct scatterlist *sg; /* cmd data buffer SG vector */ int sg_cnt; /* SG segments count */ @@ -1038,7 +894,7 @@ struct qla_tgt_cmd { uint64_t jiffies_at_alloc; uint64_t jiffies_at_free; - cmd_flags_t cmd_flags; + enum trace_flags trc_flags; }; struct qla_tgt_sess_work_param { @@ -1056,9 +912,9 @@ struct qla_tgt_sess_work_param { }; struct qla_tgt_mgmt_cmd { - uint8_t tmr_func; + uint16_t tmr_func; uint8_t fc_tm_rsp; - struct qla_tgt_sess *sess; + struct fc_port *sess; struct se_cmd se_cmd; struct work_struct free_work; unsigned int flags; @@ -1090,18 +946,6 @@ struct qla_tgt_prm { uint16_t tot_dsds; }; -struct qla_tgt_srr_imm { - struct list_head srr_list_entry; - int srr_id; - struct imm_ntfy_from_isp imm_ntfy; -}; - -struct qla_tgt_srr_ctio { - struct list_head srr_list_entry; - int srr_id; - struct qla_tgt_cmd *cmd; -}; - /* Check for Switch reserved address */ #define IS_SW_RESV_ADDR(_s_id) \ ((_s_id.b.domain == 0xff) && (_s_id.b.area == 0xfc)) @@ -1121,7 +965,7 @@ extern int qlt_remove_target(struct qla_hw_data *, struct scsi_qla_host *); extern int qlt_lport_register(void *, u64, u64, u64, int (*callback)(struct scsi_qla_host *, void *, u64, u64)); extern void qlt_lport_deregister(struct scsi_qla_host *); -void qlt_put_sess(struct qla_tgt_sess *sess); +extern void qlt_unreg_sess(struct fc_port *); extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *); extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *, int); extern int __init qlt_init(void); @@ -1133,24 +977,22 @@ extern void qlt_update_vp_map(struct scsi_qla_host *, int); * is not set. Right now, ha value is ignored. */ #define QLA_TGT_MODE_ENABLED() (ql2x_ini_mode != QLA2XXX_INI_MODE_ENABLED) + extern int ql2x_ini_mode; static inline bool qla_tgt_mode_enabled(struct scsi_qla_host *ha) { - return ha->host->active_mode & MODE_TARGET; + return ha->host->active_mode == MODE_TARGET; } static inline bool qla_ini_mode_enabled(struct scsi_qla_host *ha) { - return ha->host->active_mode & MODE_INITIATOR; + return ha->host->active_mode == MODE_INITIATOR; } -static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha) +static inline bool qla_dual_mode_enabled(struct scsi_qla_host *ha) { - if (ha->host->active_mode & MODE_INITIATOR) - ha->host->active_mode &= ~MODE_INITIATOR; - else - ha->host->active_mode |= MODE_INITIATOR; + return (ha->host->active_mode == MODE_DUAL); } static inline uint32_t sid_to_key(const uint8_t *s_id) diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 3084983c1287..8e8ab0fa9672 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -282,10 +282,10 @@ static void tcm_qla2xxx_complete_free(struct work_struct *work) cmd->cmd_in_wq = 0; - WARN_ON(cmd->cmd_flags & BIT_16); + WARN_ON(cmd->trc_flags & TRC_CMD_FREE); cmd->vha->tgt_counters.qla_core_ret_sta_ctio++; - cmd->cmd_flags |= BIT_16; + cmd->trc_flags |= TRC_CMD_FREE; transport_generic_free_cmd(&cmd->se_cmd, 0); } @@ -299,8 +299,8 @@ static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd) cmd->vha->tgt_counters.core_qla_free_cmd++; cmd->cmd_in_wq = 1; - BUG_ON(cmd->cmd_flags & BIT_20); - cmd->cmd_flags |= BIT_20; + WARN_ON(cmd->trc_flags & TRC_CMD_DONE); + cmd->trc_flags |= TRC_CMD_DONE; INIT_WORK(&cmd->work, tcm_qla2xxx_complete_free); queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, &cmd->work); @@ -315,7 +315,7 @@ static int tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd) if ((se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) == 0) { cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); - cmd->cmd_flags |= BIT_14; + cmd->trc_flags |= TRC_CMD_CHK_STOP; } return target_put_sess_cmd(se_cmd); @@ -339,9 +339,26 @@ static void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd) qlt_free_cmd(cmd); } +static void tcm_qla2xxx_release_session(struct kref *kref) +{ + struct fc_port *sess = container_of(kref, + struct fc_port, sess_kref); + + qlt_unreg_sess(sess); +} + +static void tcm_qla2xxx_put_sess(struct fc_port *sess) +{ + if (!sess) + return; + + assert_spin_locked(&sess->vha->hw->tgt.sess_lock); + kref_put(&sess->sess_kref, tcm_qla2xxx_release_session); +} + static void tcm_qla2xxx_close_session(struct se_session *se_sess) { - struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr; + struct fc_port *sess = se_sess->fabric_sess_ptr; struct scsi_qla_host *vha; unsigned long flags; @@ -350,7 +367,7 @@ static void tcm_qla2xxx_close_session(struct se_session *se_sess) spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); target_sess_cmd_list_set_waiting(se_sess); - qlt_put_sess(sess); + tcm_qla2xxx_put_sess(sess); spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); } @@ -377,7 +394,7 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd) cmd->se_cmd.se_cmd_flags); return 0; } - cmd->cmd_flags |= BIT_3; + cmd->trc_flags |= TRC_XFR_RDY; cmd->bufflen = se_cmd->data_length; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); @@ -441,7 +458,7 @@ static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd, { struct se_cmd *se_cmd = &cmd->se_cmd; struct se_session *se_sess; - struct qla_tgt_sess *sess; + struct fc_port *sess; #ifdef CONFIG_TCM_QLA2XXX_DEBUG struct se_portal_group *se_tpg; struct tcm_qla2xxx_tpg *tpg; @@ -456,7 +473,7 @@ static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd, sess = cmd->sess; if (!sess) { - pr_err("Unable to locate struct qla_tgt_sess from qla_tgt_cmd\n"); + pr_err("Unable to locate struct fc_port from qla_tgt_cmd\n"); return -EINVAL; } @@ -493,9 +510,9 @@ static void tcm_qla2xxx_handle_data_work(struct work_struct *work) cmd->cmd_in_wq = 0; spin_lock_irqsave(&cmd->cmd_lock, flags); - cmd->cmd_flags |= CMD_FLAG_DATA_WORK; + cmd->data_work = 1; if (cmd->aborted) { - cmd->cmd_flags |= CMD_FLAG_DATA_WORK_FREE; + cmd->data_work_free = 1; spin_unlock_irqrestore(&cmd->cmd_lock, flags); tcm_qla2xxx_free_cmd(cmd); @@ -532,7 +549,7 @@ static void tcm_qla2xxx_handle_data_work(struct work_struct *work) */ static void tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd) { - cmd->cmd_flags |= BIT_10; + cmd->trc_flags |= TRC_DATA_IN; cmd->cmd_in_wq = 1; INIT_WORK(&cmd->work, tcm_qla2xxx_handle_data_work); queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, &cmd->work); @@ -563,13 +580,49 @@ static void tcm_qla2xxx_handle_dif_err(struct qla_tgt_cmd *cmd) * Called from qla_target.c:qlt_issue_task_mgmt() */ static int tcm_qla2xxx_handle_tmr(struct qla_tgt_mgmt_cmd *mcmd, uint32_t lun, - uint8_t tmr_func, uint32_t tag) + uint16_t tmr_func, uint32_t tag) { - struct qla_tgt_sess *sess = mcmd->sess; + struct fc_port *sess = mcmd->sess; struct se_cmd *se_cmd = &mcmd->se_cmd; + int transl_tmr_func = 0; + + switch (tmr_func) { + case QLA_TGT_ABTS: + pr_debug("%ld: ABTS received\n", sess->vha->host_no); + transl_tmr_func = TMR_ABORT_TASK; + break; + case QLA_TGT_2G_ABORT_TASK: + pr_debug("%ld: 2G Abort Task received\n", sess->vha->host_no); + transl_tmr_func = TMR_ABORT_TASK; + break; + case QLA_TGT_CLEAR_ACA: + pr_debug("%ld: CLEAR_ACA received\n", sess->vha->host_no); + transl_tmr_func = TMR_CLEAR_ACA; + break; + case QLA_TGT_TARGET_RESET: + pr_debug("%ld: TARGET_RESET received\n", sess->vha->host_no); + transl_tmr_func = TMR_TARGET_WARM_RESET; + break; + case QLA_TGT_LUN_RESET: + pr_debug("%ld: LUN_RESET received\n", sess->vha->host_no); + transl_tmr_func = TMR_LUN_RESET; + break; + case QLA_TGT_CLEAR_TS: + pr_debug("%ld: CLEAR_TS received\n", sess->vha->host_no); + transl_tmr_func = TMR_CLEAR_TASK_SET; + break; + case QLA_TGT_ABORT_TS: + pr_debug("%ld: ABORT_TS received\n", sess->vha->host_no); + transl_tmr_func = TMR_ABORT_TASK_SET; + break; + default: + pr_debug("%ld: Unknown task mgmt fn 0x%x\n", + sess->vha->host_no, tmr_func); + return -ENOSYS; + } return target_submit_tmr(se_cmd, sess->se_sess, NULL, lun, mcmd, - tmr_func, GFP_ATOMIC, tag, TARGET_SCF_ACK_KREF); + transl_tmr_func, GFP_ATOMIC, tag, TARGET_SCF_ACK_KREF); } static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) @@ -591,7 +644,7 @@ static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) return 0; } - cmd->cmd_flags |= BIT_4; + cmd->trc_flags |= TRC_XMIT_DATA; cmd->bufflen = se_cmd->data_length; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); @@ -622,11 +675,11 @@ static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd) cmd->sg_cnt = 0; cmd->offset = 0; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); - if (cmd->cmd_flags & BIT_5) { - pr_crit("Bit_5 already set for cmd = %p.\n", cmd); + if (cmd->trc_flags & TRC_XMIT_STATUS) { + pr_crit("Multiple calls for status = %p.\n", cmd); dump_stack(); } - cmd->cmd_flags |= BIT_5; + cmd->trc_flags |= TRC_XMIT_STATUS; if (se_cmd->data_direction == DMA_FROM_DEVICE) { /* @@ -682,10 +735,7 @@ static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) qlt_xmit_tm_rsp(mcmd); } - -#define DATA_WORK_NOT_FREE(_flags) \ - (( _flags & (CMD_FLAG_DATA_WORK|CMD_FLAG_DATA_WORK_FREE)) == \ - CMD_FLAG_DATA_WORK) +#define DATA_WORK_NOT_FREE(_cmd) (_cmd->data_work && !_cmd->data_work_free) static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) { struct qla_tgt_cmd *cmd = container_of(se_cmd, @@ -697,13 +747,13 @@ static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) spin_lock_irqsave(&cmd->cmd_lock, flags); if ((cmd->state == QLA_TGT_STATE_NEW)|| - ((cmd->state == QLA_TGT_STATE_DATA_IN) && - DATA_WORK_NOT_FREE(cmd->cmd_flags)) ) { - - cmd->cmd_flags |= CMD_FLAG_DATA_WORK_FREE; + ((cmd->state == QLA_TGT_STATE_DATA_IN) && + DATA_WORK_NOT_FREE(cmd))) { + cmd->data_work_free = 1; spin_unlock_irqrestore(&cmd->cmd_lock, flags); - /* Cmd have not reached firmware. - * Use this trigger to free it. */ + /* + * cmd has not reached fw, Use this trigger to free it. + */ tcm_qla2xxx_free_cmd(cmd); return; } @@ -713,11 +763,11 @@ static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) } static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *, - struct tcm_qla2xxx_nacl *, struct qla_tgt_sess *); + struct tcm_qla2xxx_nacl *, struct fc_port *); /* * Expected to be called with struct qla_hw_data->tgt.sess_lock held */ -static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess) +static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct fc_port *sess) { struct se_node_acl *se_nacl = sess->se_sess->se_node_acl; struct se_portal_group *se_tpg = se_nacl->se_tpg; @@ -756,7 +806,7 @@ static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess) tcm_qla2xxx_clear_sess_lookup(lport, nacl, sess); } -static void tcm_qla2xxx_shutdown_sess(struct qla_tgt_sess *sess) +static void tcm_qla2xxx_shutdown_sess(struct fc_port *sess) { assert_spin_locked(&sess->vha->hw->tgt.sess_lock); target_sess_cmd_list_set_waiting(sess->se_sess); @@ -1141,7 +1191,7 @@ static struct se_portal_group *tcm_qla2xxx_npiv_make_tpg( /* * Expected to be called with struct qla_hw_data->tgt.sess_lock held */ -static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id( +static struct fc_port *tcm_qla2xxx_find_sess_by_s_id( scsi_qla_host_t *vha, const uint8_t *s_id) { @@ -1169,12 +1219,12 @@ static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id( se_nacl, se_nacl->initiatorname); nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); - if (!nacl->qla_tgt_sess) { - pr_err("Unable to locate struct qla_tgt_sess\n"); + if (!nacl->fc_port) { + pr_err("Unable to locate struct fc_port\n"); return NULL; } - return nacl->qla_tgt_sess; + return nacl->fc_port; } /* @@ -1185,7 +1235,7 @@ static void tcm_qla2xxx_set_sess_by_s_id( struct se_node_acl *new_se_nacl, struct tcm_qla2xxx_nacl *nacl, struct se_session *se_sess, - struct qla_tgt_sess *qla_tgt_sess, + struct fc_port *fc_port, uint8_t *s_id) { u32 key; @@ -1209,22 +1259,22 @@ static void tcm_qla2xxx_set_sess_by_s_id( pr_debug("Wiping nonexisting fc_port entry\n"); } - qla_tgt_sess->se_sess = se_sess; - nacl->qla_tgt_sess = qla_tgt_sess; + fc_port->se_sess = se_sess; + nacl->fc_port = fc_port; return; } - if (nacl->qla_tgt_sess) { + if (nacl->fc_port) { if (new_se_nacl == NULL) { - pr_debug("Clearing existing nacl->qla_tgt_sess and fc_port entry\n"); + pr_debug("Clearing existing nacl->fc_port and fc_port entry\n"); btree_remove32(&lport->lport_fcport_map, key); - nacl->qla_tgt_sess = NULL; + nacl->fc_port = NULL; return; } - pr_debug("Replacing existing nacl->qla_tgt_sess and fc_port entry\n"); + pr_debug("Replacing existing nacl->fc_port and fc_port entry\n"); btree_update32(&lport->lport_fcport_map, key, new_se_nacl); - qla_tgt_sess->se_sess = se_sess; - nacl->qla_tgt_sess = qla_tgt_sess; + fc_port->se_sess = se_sess; + nacl->fc_port = fc_port; return; } @@ -1234,19 +1284,19 @@ static void tcm_qla2xxx_set_sess_by_s_id( return; } - pr_debug("Replacing existing fc_port entry w/o active nacl->qla_tgt_sess\n"); + pr_debug("Replacing existing fc_port entry w/o active nacl->fc_port\n"); btree_update32(&lport->lport_fcport_map, key, new_se_nacl); - qla_tgt_sess->se_sess = se_sess; - nacl->qla_tgt_sess = qla_tgt_sess; + fc_port->se_sess = se_sess; + nacl->fc_port = fc_port; - pr_debug("Setup nacl->qla_tgt_sess %p by s_id for se_nacl: %p, initiatorname: %s\n", - nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname); + pr_debug("Setup nacl->fc_port %p by s_id for se_nacl: %p, initiatorname: %s\n", + nacl->fc_port, new_se_nacl, new_se_nacl->initiatorname); } /* * Expected to be called with struct qla_hw_data->tgt.sess_lock held */ -static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_loop_id( +static struct fc_port *tcm_qla2xxx_find_sess_by_loop_id( scsi_qla_host_t *vha, const uint16_t loop_id) { @@ -1274,12 +1324,12 @@ static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_loop_id( nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); - if (!nacl->qla_tgt_sess) { - pr_err("Unable to locate struct qla_tgt_sess\n"); + if (!nacl->fc_port) { + pr_err("Unable to locate struct fc_port\n"); return NULL; } - return nacl->qla_tgt_sess; + return nacl->fc_port; } /* @@ -1290,7 +1340,7 @@ static void tcm_qla2xxx_set_sess_by_loop_id( struct se_node_acl *new_se_nacl, struct tcm_qla2xxx_nacl *nacl, struct se_session *se_sess, - struct qla_tgt_sess *qla_tgt_sess, + struct fc_port *fc_port, uint16_t loop_id) { struct se_node_acl *saved_nacl; @@ -1305,27 +1355,27 @@ static void tcm_qla2xxx_set_sess_by_loop_id( if (!saved_nacl) { pr_debug("Setting up new fc_loopid->se_nacl to new_se_nacl\n"); fc_loopid->se_nacl = new_se_nacl; - if (qla_tgt_sess->se_sess != se_sess) - qla_tgt_sess->se_sess = se_sess; - if (nacl->qla_tgt_sess != qla_tgt_sess) - nacl->qla_tgt_sess = qla_tgt_sess; + if (fc_port->se_sess != se_sess) + fc_port->se_sess = se_sess; + if (nacl->fc_port != fc_port) + nacl->fc_port = fc_port; return; } - if (nacl->qla_tgt_sess) { + if (nacl->fc_port) { if (new_se_nacl == NULL) { - pr_debug("Clearing nacl->qla_tgt_sess and fc_loopid->se_nacl\n"); + pr_debug("Clearing nacl->fc_port and fc_loopid->se_nacl\n"); fc_loopid->se_nacl = NULL; - nacl->qla_tgt_sess = NULL; + nacl->fc_port = NULL; return; } - pr_debug("Replacing existing nacl->qla_tgt_sess and fc_loopid->se_nacl\n"); + pr_debug("Replacing existing nacl->fc_port and fc_loopid->se_nacl\n"); fc_loopid->se_nacl = new_se_nacl; - if (qla_tgt_sess->se_sess != se_sess) - qla_tgt_sess->se_sess = se_sess; - if (nacl->qla_tgt_sess != qla_tgt_sess) - nacl->qla_tgt_sess = qla_tgt_sess; + if (fc_port->se_sess != se_sess) + fc_port->se_sess = se_sess; + if (nacl->fc_port != fc_port) + nacl->fc_port = fc_port; return; } @@ -1335,29 +1385,29 @@ static void tcm_qla2xxx_set_sess_by_loop_id( return; } - pr_debug("Replacing existing fc_loopid->se_nacl w/o active nacl->qla_tgt_sess\n"); + pr_debug("Replacing existing fc_loopid->se_nacl w/o active nacl->fc_port\n"); fc_loopid->se_nacl = new_se_nacl; - if (qla_tgt_sess->se_sess != se_sess) - qla_tgt_sess->se_sess = se_sess; - if (nacl->qla_tgt_sess != qla_tgt_sess) - nacl->qla_tgt_sess = qla_tgt_sess; + if (fc_port->se_sess != se_sess) + fc_port->se_sess = se_sess; + if (nacl->fc_port != fc_port) + nacl->fc_port = fc_port; - pr_debug("Setup nacl->qla_tgt_sess %p by loop_id for se_nacl: %p, initiatorname: %s\n", - nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname); + pr_debug("Setup nacl->fc_port %p by loop_id for se_nacl: %p, initiatorname: %s\n", + nacl->fc_port, new_se_nacl, new_se_nacl->initiatorname); } /* * Should always be called with qla_hw_data->tgt.sess_lock held. */ static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *lport, - struct tcm_qla2xxx_nacl *nacl, struct qla_tgt_sess *sess) + struct tcm_qla2xxx_nacl *nacl, struct fc_port *sess) { struct se_session *se_sess = sess->se_sess; unsigned char be_sid[3]; - be_sid[0] = sess->s_id.b.domain; - be_sid[1] = sess->s_id.b.area; - be_sid[2] = sess->s_id.b.al_pa; + be_sid[0] = sess->d_id.b.domain; + be_sid[1] = sess->d_id.b.area; + be_sid[2] = sess->d_id.b.al_pa; tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess, sess, be_sid); @@ -1365,7 +1415,7 @@ static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *lport, sess, sess->loop_id); } -static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) +static void tcm_qla2xxx_free_session(struct fc_port *sess) { struct qla_tgt *tgt = sess->tgt; struct qla_hw_data *ha = tgt->ha; @@ -1377,7 +1427,7 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) se_sess = sess->se_sess; if (!se_sess) { - pr_err("struct qla_tgt_sess->se_sess is NULL\n"); + pr_err("struct fc_port->se_sess is NULL\n"); dump_stack(); return; } @@ -1404,14 +1454,14 @@ static int tcm_qla2xxx_session_cb(struct se_portal_group *se_tpg, struct se_node_acl *se_nacl = se_sess->se_node_acl; struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); - struct qla_tgt_sess *qlat_sess = p; + struct fc_port *qlat_sess = p; uint16_t loop_id = qlat_sess->loop_id; unsigned long flags; unsigned char be_sid[3]; - be_sid[0] = qlat_sess->s_id.b.domain; - be_sid[1] = qlat_sess->s_id.b.area; - be_sid[2] = qlat_sess->s_id.b.al_pa; + be_sid[0] = qlat_sess->d_id.b.domain; + be_sid[1] = qlat_sess->d_id.b.area; + be_sid[2] = qlat_sess->d_id.b.al_pa; /* * And now setup se_nacl and session pointers into HW lport internal @@ -1434,7 +1484,7 @@ static int tcm_qla2xxx_session_cb(struct se_portal_group *se_tpg, static int tcm_qla2xxx_check_initiator_node_acl( scsi_qla_host_t *vha, unsigned char *fc_wwpn, - struct qla_tgt_sess *qlat_sess) + struct fc_port *qlat_sess) { struct qla_hw_data *ha = vha->hw; struct tcm_qla2xxx_lport *lport; @@ -1478,7 +1528,7 @@ static int tcm_qla2xxx_check_initiator_node_acl( return 0; } -static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, +static void tcm_qla2xxx_update_sess(struct fc_port *sess, port_id_t s_id, uint16_t loop_id, bool conf_compl_supported) { struct qla_tgt *tgt = sess->tgt; @@ -1491,11 +1541,11 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, u32 key; - if (sess->loop_id != loop_id || sess->s_id.b24 != s_id.b24) + if (sess->loop_id != loop_id || sess->d_id.b24 != s_id.b24) pr_info("Updating session %p from port %8phC loop_id %d -> %d s_id %x:%x:%x -> %x:%x:%x\n", sess, sess->port_name, - sess->loop_id, loop_id, sess->s_id.b.domain, - sess->s_id.b.area, sess->s_id.b.al_pa, s_id.b.domain, + sess->loop_id, loop_id, sess->d_id.b.domain, + sess->d_id.b.area, sess->d_id.b.al_pa, s_id.b.domain, s_id.b.area, s_id.b.al_pa); if (sess->loop_id != loop_id) { @@ -1515,18 +1565,20 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, sess->loop_id = loop_id; } - if (sess->s_id.b24 != s_id.b24) { - key = (((u32) sess->s_id.b.domain << 16) | - ((u32) sess->s_id.b.area << 8) | - ((u32) sess->s_id.b.al_pa)); + if (sess->d_id.b24 != s_id.b24) { + key = (((u32) sess->d_id.b.domain << 16) | + ((u32) sess->d_id.b.area << 8) | + ((u32) sess->d_id.b.al_pa)); if (btree_lookup32(&lport->lport_fcport_map, key)) - WARN(btree_remove32(&lport->lport_fcport_map, key) != se_nacl, - "Found wrong se_nacl when updating s_id %x:%x:%x\n", - sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa); + WARN(btree_remove32(&lport->lport_fcport_map, key) != + se_nacl, "Found wrong se_nacl when updating s_id %x:%x:%x\n", + sess->d_id.b.domain, sess->d_id.b.area, + sess->d_id.b.al_pa); else WARN(1, "No lport_fcport_map entry for s_id %x:%x:%x\n", - sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa); + sess->d_id.b.domain, sess->d_id.b.area, + sess->d_id.b.al_pa); key = (((u32) s_id.b.domain << 16) | ((u32) s_id.b.area << 8) | @@ -1537,10 +1589,11 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, s_id.b.domain, s_id.b.area, s_id.b.al_pa); btree_update32(&lport->lport_fcport_map, key, se_nacl); } else { - btree_insert32(&lport->lport_fcport_map, key, se_nacl, GFP_ATOMIC); + btree_insert32(&lport->lport_fcport_map, key, se_nacl, + GFP_ATOMIC); } - sess->s_id = s_id; + sess->d_id = s_id; nacl->nport_id = key; } @@ -1567,6 +1620,7 @@ static struct qla_tgt_func_tmpl tcm_qla2xxx_template = { .find_sess_by_s_id = tcm_qla2xxx_find_sess_by_s_id, .find_sess_by_loop_id = tcm_qla2xxx_find_sess_by_loop_id, .clear_nacl_from_fcport_map = tcm_qla2xxx_clear_nacl_from_fcport_map, + .put_sess = tcm_qla2xxx_put_sess, .shutdown_sess = tcm_qla2xxx_shutdown_sess, }; @@ -1690,7 +1744,7 @@ static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha, (struct tcm_qla2xxx_lport *)base_vha->vha_tgt.target_lport_ptr; struct fc_vport_identifiers vport_id; - if (!qla_tgt_mode_enabled(base_vha)) { + if (qla_ini_mode_enabled(base_vha)) { pr_err("qla2xxx base_vha not enabled for target mode\n"); return -EPERM; } @@ -1738,7 +1792,7 @@ static struct se_wwn *tcm_qla2xxx_npiv_make_lport( p = strchr(tmp, '@'); if (!p) { - pr_err("Unable to locate NPIV '@' seperator\n"); + pr_err("Unable to locate NPIV '@' separator\n"); return ERR_PTR(-EINVAL); } *p++ = '\0'; diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.h b/drivers/scsi/qla2xxx/tcm_qla2xxx.h index cf8430be183b..071035dfa99a 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.h +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.h @@ -20,8 +20,8 @@ struct tcm_qla2xxx_nacl { u64 nport_wwnn; /* ASCII formatted WWPN for FC Initiator Nport */ char nport_name[TCM_QLA2XXX_NAMELEN]; - /* Pointer to qla_tgt_sess */ - struct qla_tgt_sess *qla_tgt_sess; + /* Pointer to fc_port */ + struct fc_port *fc_port; /* Pointer to TCM FC nexus */ struct se_session *nport_nexus; }; diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c index b1383a71400e..a75673bb82b3 100644 --- a/drivers/scsi/scsi_common.c +++ b/drivers/scsi/scsi_common.c @@ -137,11 +137,11 @@ EXPORT_SYMBOL(int_to_scsilun); bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, struct scsi_sense_hdr *sshdr) { + memset(sshdr, 0, sizeof(struct scsi_sense_hdr)); + if (!sense_buffer || !sb_len) return false; - memset(sshdr, 0, sizeof(struct scsi_sense_hdr)); - sshdr->response_code = (sense_buffer[0] & 0x7f); if (!scsi_sense_valid(sshdr)) diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c index b8d3b97b217a..84addee05be6 100644 --- a/drivers/scsi/scsi_dh.c +++ b/drivers/scsi/scsi_dh.c @@ -219,20 +219,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) } EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); -static struct scsi_device *get_sdev_from_queue(struct request_queue *q) -{ - struct scsi_device *sdev; - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - sdev = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - return sdev; -} - /* * scsi_dh_activate - activate the path associated with the scsi_device * corresponding to the given request queue. @@ -251,7 +237,7 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) struct scsi_device *sdev; int err = SCSI_DH_NOSYS; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) { if (fn) fn(data, err); @@ -298,7 +284,7 @@ int scsi_dh_set_params(struct request_queue *q, const char *params) struct scsi_device *sdev; int err = -SCSI_DH_NOSYS; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return err; @@ -321,7 +307,7 @@ int scsi_dh_attach(struct request_queue *q, const char *name) struct scsi_device_handler *scsi_dh; int err = 0; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return -ENODEV; @@ -359,7 +345,7 @@ const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) struct scsi_device *sdev; const char *handler_name = NULL; - sdev = get_sdev_from_queue(q); + sdev = scsi_device_from_queue(q); if (!sdev) return NULL; diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 8b8c814df5c7..b6bf3f29a12a 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -199,6 +199,7 @@ static int scsi_ioctl_get_pci(struct scsi_device *sdev, void __user *arg) int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) { char scsi_cmd[MAX_COMMAND_SIZE]; + struct scsi_sense_hdr sense_hdr; /* Check for deprecated ioctls ... all the ioctls which don't * follow the new unique numbering scheme are deprecated */ @@ -243,7 +244,7 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); case SCSI_IOCTL_TEST_UNIT_READY: return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT, - NORMAL_RETRIES, NULL); + NORMAL_RETRIES, &sense_hdr); case SCSI_IOCTL_START_UNIT: scsi_cmd[0] = START_STOP; scsi_cmd[1] = 0; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 912fbc3b4543..ba2286652ff6 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -213,10 +213,30 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason) __scsi_queue_insert(cmd, reason, 1); } -static int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, + +/** + * scsi_execute - insert request and wait for the result + * @sdev: scsi device + * @cmd: scsi command + * @data_direction: data direction + * @buffer: data buffer + * @bufflen: len of buffer + * @sense: optional sense buffer + * @sshdr: optional decoded sense header + * @timeout: request timeout in seconds + * @retries: number of times to retry request + * @flags: flags for ->cmd_flags + * @rq_flags: flags for ->rq_flags + * @resid: optional residual length + * + * returns the req->errors value which is the scsi_cmnd result + * field. + */ +int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, int data_direction, void *buffer, unsigned bufflen, - unsigned char *sense, int timeout, int retries, u64 flags, - req_flags_t rq_flags, int *resid) + unsigned char *sense, struct scsi_sense_hdr *sshdr, + int timeout, int retries, u64 flags, req_flags_t rq_flags, + int *resid) { struct request *req; struct scsi_request *rq; @@ -259,62 +279,16 @@ static int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, *resid = rq->resid_len; if (sense && rq->sense_len) memcpy(sense, rq->sense, SCSI_SENSE_BUFFERSIZE); + if (sshdr) + scsi_normalize_sense(rq->sense, rq->sense_len, sshdr); ret = req->errors; out: blk_put_request(req); return ret; } - -/** - * scsi_execute - insert request and wait for the result - * @sdev: scsi device - * @cmd: scsi command - * @data_direction: data direction - * @buffer: data buffer - * @bufflen: len of buffer - * @sense: optional sense buffer - * @timeout: request timeout in seconds - * @retries: number of times to retry request - * @flags: or into request flags; - * @resid: optional residual length - * - * returns the req->errors value which is the scsi_cmnd result - * field. - */ -int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, - int data_direction, void *buffer, unsigned bufflen, - unsigned char *sense, int timeout, int retries, u64 flags, - int *resid) -{ - return __scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense, - timeout, retries, flags, 0, resid); -} EXPORT_SYMBOL(scsi_execute); -int scsi_execute_req_flags(struct scsi_device *sdev, const unsigned char *cmd, - int data_direction, void *buffer, unsigned bufflen, - struct scsi_sense_hdr *sshdr, int timeout, int retries, - int *resid, u64 flags, req_flags_t rq_flags) -{ - char *sense = NULL; - int result; - - if (sshdr) { - sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); - if (!sense) - return DRIVER_ERROR << 24; - } - result = __scsi_execute(sdev, cmd, data_direction, buffer, bufflen, - sense, timeout, retries, flags, rq_flags, resid); - if (sshdr) - scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr); - - kfree(sense); - return result; -} -EXPORT_SYMBOL(scsi_execute_req_flags); - /* * Function: scsi_init_cmd_errh() * @@ -1167,7 +1141,7 @@ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd) /* zero out the cmd, except for the embedded scsi_request */ memset((char *)cmd + sizeof(cmd->req), 0, - sizeof(*cmd) - sizeof(cmd->req)); + sizeof(*cmd) - sizeof(cmd->req) + dev->host->hostt->cmd_size); cmd->device = dev; cmd->sense_buffer = buf; @@ -2231,6 +2205,29 @@ void scsi_mq_destroy_tags(struct Scsi_Host *shost) blk_mq_free_tag_set(&shost->tag_set); } +/** + * scsi_device_from_queue - return sdev associated with a request_queue + * @q: The request queue to return the sdev from + * + * Return the sdev associated with a request queue or NULL if the + * request_queue does not reference a SCSI device. + */ +struct scsi_device *scsi_device_from_queue(struct request_queue *q) +{ + struct scsi_device *sdev = NULL; + + if (q->mq_ops) { + if (q->mq_ops == &scsi_mq_ops) + sdev = q->queuedata; + } else if (q->request_fn == scsi_request_fn) + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + + return sdev; +} +EXPORT_SYMBOL_GPL(scsi_device_from_queue); + /* * Function: scsi_block_requests() * @@ -2497,28 +2494,20 @@ EXPORT_SYMBOL(scsi_mode_sense); * @sdev: scsi device to change the state of. * @timeout: command timeout * @retries: number of retries before failing - * @sshdr_external: Optional pointer to struct scsi_sense_hdr for - * returning sense. Make sure that this is cleared before passing - * in. + * @sshdr: outpout pointer for decoded sense information. * * Returns zero if unsuccessful or an error if TUR failed. For * removable media, UNIT_ATTENTION sets ->changed flag. **/ int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, - struct scsi_sense_hdr *sshdr_external) + struct scsi_sense_hdr *sshdr) { char cmd[] = { TEST_UNIT_READY, 0, 0, 0, 0, 0, }; - struct scsi_sense_hdr *sshdr; int result; - if (!sshdr_external) - sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL); - else - sshdr = sshdr_external; - /* try to eat the UNIT_ATTENTION if there are enough retries */ do { result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr, @@ -2529,8 +2518,6 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, } while (scsi_sense_valid(sshdr) && sshdr->sense_key == UNIT_ATTENTION && --retries); - if (!sshdr_external) - kfree(sshdr); return result; } EXPORT_SYMBOL(scsi_test_unit_ready); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 126a5ee00987..cdbb293aca08 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -227,27 +227,31 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) return 0; } + q = blk_alloc_queue(GFP_KERNEL); + if (!q) + return -ENOMEM; + q->cmd_size = sizeof(struct scsi_request); + if (rphy) { - q = blk_init_queue(sas_non_host_smp_request, NULL); + q->request_fn = sas_non_host_smp_request; dev = &rphy->dev; name = dev_name(dev); release = NULL; } else { - q = blk_init_queue(sas_host_smp_request, NULL); + q->request_fn = sas_host_smp_request; dev = &shost->shost_gendev; snprintf(namebuf, sizeof(namebuf), "sas_host%d", shost->host_no); name = namebuf; release = sas_host_release; } - if (!q) - return -ENOMEM; + error = blk_init_allocated_queue(q); + if (error) + goto out_cleanup_queue; error = bsg_register_queue(q, dev, name, release); - if (error) { - blk_cleanup_queue(q); - return -ENOMEM; - } + if (error) + goto out_cleanup_queue; if (rphy) rphy->q = q; @@ -261,6 +265,10 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); return 0; + +out_cleanup_queue: + blk_cleanup_queue(q); + return error; } static void sas_bsg_remove(struct Scsi_Host *shost, struct sas_rphy *rphy) @@ -1467,7 +1475,7 @@ static void sas_end_device_release(struct device *dev) } /** - * sas_rphy_initialize - common rphy intialization + * sas_rphy_initialize - common rphy initialization * @rphy: rphy to initialise * * Used by both sas_end_device_alloc() and sas_expander_alloc() to diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index 319868f3f674..d0219e36080c 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -123,25 +123,21 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd, { int i, result; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; + struct scsi_sense_hdr sshdr_tmp; + + if (!sshdr) + sshdr = &sshdr_tmp; for(i = 0; i < DV_RETRIES; i++) { - result = scsi_execute(sdev, cmd, dir, buffer, bufflen, - sense, DV_TIMEOUT, /* retries */ 1, + result = scsi_execute(sdev, cmd, dir, buffer, bufflen, sense, + sshdr, DV_TIMEOUT, /* retries */ 1, REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER, - NULL); - if (driver_byte(result) & DRIVER_SENSE) { - struct scsi_sense_hdr sshdr_tmp; - if (!sshdr) - sshdr = &sshdr_tmp; - - if (scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, - sshdr) - && sshdr->sense_key == UNIT_ATTENTION) - continue; - } - break; + 0, NULL); + if (!(driver_byte(result) & DRIVER_SENSE) || + sshdr->sense_key != UNIT_ATTENTION) + break; } return result; } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index cb6e68dd6df0..c7839f6c35cc 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1425,7 +1425,6 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) { struct scsi_disk *sdkp = scsi_disk_get(disk); struct scsi_device *sdp; - struct scsi_sense_hdr *sshdr = NULL; int retval; if (!sdkp) @@ -1454,22 +1453,21 @@ static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) * by sd_spinup_disk() from sd_revalidate_disk(), which happens whenever * sd_revalidate() is called. */ - retval = -ENODEV; - if (scsi_block_when_processing_errors(sdp)) { - sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL); + struct scsi_sense_hdr sshdr = { 0, }; + retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES, - sshdr); - } + &sshdr); - /* failed to execute TUR, assume media not present */ - if (host_byte(retval)) { - set_media_not_present(sdkp); - goto out; - } + /* failed to execute TUR, assume media not present */ + if (host_byte(retval)) { + set_media_not_present(sdkp); + goto out; + } - if (media_not_present(sdkp, sshdr)) - goto out; + if (media_not_present(sdkp, &sshdr)) + goto out; + } /* * For removable scsi disk we have to recognise the presence @@ -1485,7 +1483,6 @@ out: * Medium present state has changed in either direction. * Device has indicated UNIT_ATTENTION. */ - kfree(sshdr); retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0; sdp->changed = 0; scsi_disk_put(sdkp); @@ -1511,9 +1508,8 @@ static int sd_sync_cache(struct scsi_disk *sdkp) * Leave the rest of the command zero to indicate * flush everything. */ - res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, - &sshdr, timeout, SD_MAX_RETRIES, - NULL, 0, RQF_PM); + res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, + timeout, SD_MAX_RETRIES, 0, RQF_PM, NULL); if (res == 0) break; } @@ -3084,7 +3080,7 @@ struct sd_devt { struct disk_devt disk_devt; }; -void sd_devt_release(struct disk_devt *disk_devt) +static void sd_devt_release(struct disk_devt *disk_devt) { struct sd_devt *sd_devt = container_of(disk_devt, struct sd_devt, disk_devt); @@ -3213,10 +3209,10 @@ static int sd_probe(struct device *dev) sd_devt = NULL; out_put: put_disk(gd); - out_free: - kfree(sdkp); out_free_devt: kfree(sd_devt); + out_free: + kfree(sdkp); out: scsi_autopm_put_device(sdp); return error; @@ -3299,8 +3295,8 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start) if (!scsi_device_online(sdp)) return -ENODEV; - res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr, - SD_TIMEOUT, SD_MAX_RETRIES, NULL, 0, RQF_PM); + res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, + SD_TIMEOUT, SD_MAX_RETRIES, 0, RQF_PM, NULL); if (res) { sd_print_result(sdkp, "Start/Stop Unit failed", res); if (driver_byte(res) & DRIVER_SENSE) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index e831e01f9fa6..29b86505f796 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1185,8 +1185,9 @@ sg_fasync(int fd, struct file *filp, int mode) } static int -sg_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +sg_vma_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; Sg_fd *sfp; unsigned long offset, len, sa; Sg_scatter_hold *rsv_schp; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 11c0dfb3dfa3..657ad15682a3 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -534,8 +534,7 @@ static int pqi_write_current_time_to_host_wellness( size_t buffer_length; time64_t local_time; unsigned int year; - struct timeval time; - struct rtc_time tm; + struct tm tm; buffer_length = sizeof(*buffer); @@ -552,9 +551,8 @@ static int pqi_write_current_time_to_host_wellness( put_unaligned_le16(sizeof(buffer->time), &buffer->time_length); - do_gettimeofday(&time); - local_time = time.tv_sec - (sys_tz.tz_minuteswest * 60); - rtc_time64_to_tm(local_time, &tm); + local_time = ktime_get_real_seconds(); + time64_to_tm(local_time, -sys_tz.tz_minuteswest * 60, &tm); year = tm.tm_year + 1900; buffer->time[0] = bin2bcd(tm.tm_hour); diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index dfffdf63e44c..4610c8c5693f 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -187,30 +187,19 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) struct scsi_device *SDev; struct scsi_sense_hdr sshdr; int result, err = 0, retries = 0; - struct request_sense *sense = cgc->sense; SDev = cd->device; - if (!sense) { - sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); - if (!sense) { - err = -ENOMEM; - goto out; - } - } - retry: if (!scsi_block_when_processing_errors(SDev)) { err = -ENODEV; goto out; } - memset(sense, 0, sizeof(*sense)); result = scsi_execute(SDev, cgc->cmd, cgc->data_direction, - cgc->buffer, cgc->buflen, (char *)sense, - cgc->timeout, IOCTL_RETRIES, 0, NULL); - - scsi_normalize_sense((char *)sense, sizeof(*sense), &sshdr); + cgc->buffer, cgc->buflen, + (unsigned char *)cgc->sense, &sshdr, + cgc->timeout, IOCTL_RETRIES, 0, 0, NULL); /* Minimal error checking. Ignore cases we know about, and report the rest. */ if (driver_byte(result) != 0) { @@ -261,8 +250,6 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) /* Wake up a process waiting for device */ out: - if (!cgc->sense) - kfree(sense); cgc->stat = err; return err; } diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 81212d4bd9bf..e5ef78a6848e 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -23,7 +23,7 @@ static const char *verstr = "20160209"; #include <linux/fs.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/string.h> diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 585e54f6512c..638e5f427c90 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -280,7 +280,7 @@ static const struct vmstor_protocol vmstor_protocols[] = { /* - * This structure is sent during the intialization phase to get the different + * This structure is sent during the initialization phase to get the different * properties of the channel. */ diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index ce5d023c1c91..c87d770b519a 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1523,18 +1523,6 @@ static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) return false; } - /* - * Not performing check for each individual select_major - * mappings of select_minor, since there is no harm in - * configuring a non-existent select_minor - */ - if (host->testbus.select_minor > 0xFF) { - dev_err(host->hba->dev, - "%s: 0x%05X is not a legal testbus option\n", - __func__, host->testbus.select_minor); - return false; - } - return true; } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8b721f431dd0..dc6efbd1be8e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6915,9 +6915,9 @@ ufshcd_send_request_sense(struct ufs_hba *hba, struct scsi_device *sdp) goto out; } - ret = scsi_execute_req_flags(sdp, cmd, DMA_FROM_DEVICE, buffer, - UFSHCD_REQ_SENSE_SIZE, NULL, - msecs_to_jiffies(1000), 3, NULL, 0, RQF_PM); + ret = scsi_execute(sdp, cmd, DMA_FROM_DEVICE, buffer, + UFSHCD_REQ_SENSE_SIZE, NULL, NULL, + msecs_to_jiffies(1000), 3, 0, RQF_PM, NULL); if (ret) pr_err("%s: failed with err %d\n", __func__, ret); @@ -6982,8 +6982,8 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, * callbacks hence set the RQF_PM flag so that it doesn't resume the * already suspended childs. */ - ret = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr, - START_STOP_TIMEOUT, 0, NULL, 0, RQF_PM); + ret = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, + START_STOP_TIMEOUT, 0, 0, RQF_PM, NULL); if (ret) { sdev_printk(KERN_WARNING, sdp, "START_STOP failed for power mode: %d, result %x\n", diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index c680d7641311..939c47df73fa 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/mempool.h> +#include <linux/interrupt.h> #include <linux/virtio.h> #include <linux/virtio_ids.h> #include <linux/virtio_config.h> @@ -29,6 +30,7 @@ #include <scsi/scsi_cmnd.h> #include <scsi/scsi_tcq.h> #include <linux/seqlock.h> +#include <linux/blk-mq-virtio.h> #define VIRTIO_SCSI_MEMPOOL_SZ 64 #define VIRTIO_SCSI_EVENT_LEN 8 @@ -108,7 +110,6 @@ struct virtio_scsi { bool affinity_hint_set; struct hlist_node node; - struct hlist_node node_dead; /* Protected by event_vq lock */ bool stop_events; @@ -118,7 +119,6 @@ struct virtio_scsi { struct virtio_scsi_vq req_vqs[]; }; -static enum cpuhp_state virtioscsi_online; static struct kmem_cache *virtscsi_cmd_cache; static mempool_t *virtscsi_cmd_pool; @@ -766,6 +766,13 @@ static void virtscsi_target_destroy(struct scsi_target *starget) kfree(tgt); } +static int virtscsi_map_queues(struct Scsi_Host *shost) +{ + struct virtio_scsi *vscsi = shost_priv(shost); + + return blk_mq_virtio_map_queues(&shost->tag_set, vscsi->vdev, 2); +} + static struct scsi_host_template virtscsi_host_template_single = { .module = THIS_MODULE, .name = "Virtio SCSI HBA", @@ -801,6 +808,7 @@ static struct scsi_host_template virtscsi_host_template_multi = { .use_clustering = ENABLE_CLUSTERING, .target_alloc = virtscsi_target_alloc, .target_destroy = virtscsi_target_destroy, + .map_queues = virtscsi_map_queues, .track_queue_depth = 1, }; @@ -817,80 +825,6 @@ static struct scsi_host_template virtscsi_host_template_multi = { virtio_cwrite(vdev, struct virtio_scsi_config, fld, &__val); \ } while(0) -static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity) -{ - int i; - int cpu; - - /* In multiqueue mode, when the number of cpu is equal - * to the number of request queues, we let the qeueues - * to be private to one cpu by setting the affinity hint - * to eliminate the contention. - */ - if ((vscsi->num_queues == 1 || - vscsi->num_queues != num_online_cpus()) && affinity) { - if (vscsi->affinity_hint_set) - affinity = false; - else - return; - } - - if (affinity) { - i = 0; - for_each_online_cpu(cpu) { - virtqueue_set_affinity(vscsi->req_vqs[i].vq, cpu); - i++; - } - - vscsi->affinity_hint_set = true; - } else { - for (i = 0; i < vscsi->num_queues; i++) { - if (!vscsi->req_vqs[i].vq) - continue; - - virtqueue_set_affinity(vscsi->req_vqs[i].vq, -1); - } - - vscsi->affinity_hint_set = false; - } -} - -static void virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity) -{ - get_online_cpus(); - __virtscsi_set_affinity(vscsi, affinity); - put_online_cpus(); -} - -static int virtscsi_cpu_online(unsigned int cpu, struct hlist_node *node) -{ - struct virtio_scsi *vscsi = hlist_entry_safe(node, struct virtio_scsi, - node); - __virtscsi_set_affinity(vscsi, true); - return 0; -} - -static int virtscsi_cpu_notif_add(struct virtio_scsi *vi) -{ - int ret; - - ret = cpuhp_state_add_instance(virtioscsi_online, &vi->node); - if (ret) - return ret; - - ret = cpuhp_state_add_instance(CPUHP_VIRT_SCSI_DEAD, &vi->node_dead); - if (ret) - cpuhp_state_remove_instance(virtioscsi_online, &vi->node); - return ret; -} - -static void virtscsi_cpu_notif_remove(struct virtio_scsi *vi) -{ - cpuhp_state_remove_instance_nocalls(virtioscsi_online, &vi->node); - cpuhp_state_remove_instance_nocalls(CPUHP_VIRT_SCSI_DEAD, - &vi->node_dead); -} - static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, struct virtqueue *vq) { @@ -900,14 +834,8 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, static void virtscsi_remove_vqs(struct virtio_device *vdev) { - struct Scsi_Host *sh = virtio_scsi_host(vdev); - struct virtio_scsi *vscsi = shost_priv(sh); - - virtscsi_set_affinity(vscsi, false); - /* Stop all the virtqueues. */ vdev->config->reset(vdev); - vdev->config->del_vqs(vdev); } @@ -920,6 +848,7 @@ static int virtscsi_init(struct virtio_device *vdev, vq_callback_t **callbacks; const char **names; struct virtqueue **vqs; + struct irq_affinity desc = { .pre_vectors = 2 }; num_vqs = vscsi->num_queues + VIRTIO_SCSI_VQ_BASE; vqs = kmalloc(num_vqs * sizeof(struct virtqueue *), GFP_KERNEL); @@ -941,7 +870,8 @@ static int virtscsi_init(struct virtio_device *vdev, } /* Discover virtqueues and write information to configuration. */ - err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names); + err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names, + &desc); if (err) goto out; @@ -1007,10 +937,6 @@ static int virtscsi_probe(struct virtio_device *vdev) if (err) goto virtscsi_init_failed; - err = virtscsi_cpu_notif_add(vscsi); - if (err) - goto scsi_add_host_failed; - cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1; shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; @@ -1065,9 +991,6 @@ static void virtscsi_remove(struct virtio_device *vdev) virtscsi_cancel_event_work(vscsi); scsi_remove_host(shost); - - virtscsi_cpu_notif_remove(vscsi); - virtscsi_remove_vqs(vdev); scsi_host_put(shost); } @@ -1075,10 +998,6 @@ static void virtscsi_remove(struct virtio_device *vdev) #ifdef CONFIG_PM_SLEEP static int virtscsi_freeze(struct virtio_device *vdev) { - struct Scsi_Host *sh = virtio_scsi_host(vdev); - struct virtio_scsi *vscsi = shost_priv(sh); - - virtscsi_cpu_notif_remove(vscsi); virtscsi_remove_vqs(vdev); return 0; } @@ -1093,11 +1012,6 @@ static int virtscsi_restore(struct virtio_device *vdev) if (err) return err; - err = virtscsi_cpu_notif_add(vscsi); - if (err) { - vdev->config->del_vqs(vdev); - return err; - } virtio_device_ready(vdev); if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) @@ -1152,16 +1066,6 @@ static int __init init(void) pr_err("mempool_create() for virtscsi_cmd_pool failed\n"); goto error; } - ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "scsi/virtio:online", - virtscsi_cpu_online, NULL); - if (ret < 0) - goto error; - virtioscsi_online = ret; - ret = cpuhp_setup_state_multi(CPUHP_VIRT_SCSI_DEAD, "scsi/virtio:dead", - NULL, virtscsi_cpu_online); - if (ret) - goto error; ret = register_virtio_driver(&virtio_scsi_driver); if (ret < 0) goto error; @@ -1177,17 +1081,12 @@ error: kmem_cache_destroy(virtscsi_cmd_cache); virtscsi_cmd_cache = NULL; } - if (virtioscsi_online) - cpuhp_remove_multi_state(virtioscsi_online); - cpuhp_remove_multi_state(CPUHP_VIRT_SCSI_DEAD); return ret; } static void __exit fini(void) { unregister_virtio_driver(&virtio_scsi_driver); - cpuhp_remove_multi_state(virtioscsi_online); - cpuhp_remove_multi_state(CPUHP_VIRT_SCSI_DEAD); mempool_destroy(virtscsi_cmd_pool); kmem_cache_destroy(virtscsi_cmd_cache); } diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index f31bceb69c0d..f09023f7ab11 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -11,5 +11,6 @@ source "drivers/soc/tegra/Kconfig" source "drivers/soc/ti/Kconfig" source "drivers/soc/ux500/Kconfig" source "drivers/soc/versatile/Kconfig" +source "drivers/soc/zte/Kconfig" endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 50c23d0bd457..05eae52a30b4 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_SOC_TI) += ti/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_PLAT_VERSATILE) += versatile/ +obj-$(CONFIG_ARCH_ZX) += zte/ diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c index 039374e9fdc0..95d77ec5c5d7 100644 --- a/drivers/soc/dove/pmu.c +++ b/drivers/soc/dove/pmu.c @@ -87,7 +87,7 @@ static int pmu_reset_deassert(struct reset_controller_dev *rc, unsigned long id) return 0; } -static struct reset_control_ops pmu_reset_ops = { +static const struct reset_control_ops pmu_reset_ops = { .reset = pmu_reset_reset, .assert = pmu_reset_assert, .deassert = pmu_reset_deassert, diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h index 2eaf3184f61d..2ce394aa4c95 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.h +++ b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -36,6 +36,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/kthread.h> +#include <linux/sched/signal.h> #include <linux/vmalloc.h> #include <linux/platform_device.h> #include <linux/of.h> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 461b387d03cc..78b1bb7bcf20 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -10,6 +10,10 @@ config QCOM_GSBI functions for connecting the underlying serial UART, SPI, and I2C devices to the output pins. +config QCOM_MDT_LOADER + tristate + select QCOM_SCM + config QCOM_PM bool "Qualcomm Power Management" depends on ARCH_QCOM && !ARM64 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index fdd664edf0bd..1f30260b06b8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o +obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_SMD) += smd.o obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c new file mode 100644 index 000000000000..bd63df0d14e0 --- /dev/null +++ b/drivers/soc/qcom/mdt_loader.c @@ -0,0 +1,204 @@ +/* + * Qualcomm Peripheral Image Loader + * + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2015 Sony Mobile Communications Inc + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/elf.h> +#include <linux/firmware.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/qcom_scm.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/soc/qcom/mdt_loader.h> + +static bool mdt_phdr_valid(const struct elf32_phdr *phdr) +{ + if (phdr->p_type != PT_LOAD) + return false; + + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) + return false; + + if (!phdr->p_memsz) + return false; + + return true; +} + +/** + * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt + * @fw: firmware object for the mdt file + * + * Returns size of the loaded firmware blob, or -EINVAL on failure. + */ +ssize_t qcom_mdt_get_size(const struct firmware *fw) +{ + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; + const struct elf32_hdr *ehdr; + phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; + phys_addr_t max_addr = 0; + int i; + + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (!mdt_phdr_valid(phdr)) + continue; + + if (phdr->p_paddr < min_addr) + min_addr = phdr->p_paddr; + + if (phdr->p_paddr + phdr->p_memsz > max_addr) + max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); + } + + return min_addr < max_addr ? max_addr - min_addr : -EINVAL; +} +EXPORT_SYMBOL_GPL(qcom_mdt_get_size); + +/** + * qcom_mdt_load() - load the firmware which header is loaded as fw + * @dev: device handle to associate resources with + * @fw: firmware object for the mdt file + * @firmware: name of the firmware, for construction of segment file names + * @pas_id: PAS identifier + * @mem_region: allocated memory region to load firmware into + * @mem_phys: physical address of allocated memory region + * @mem_size: size of the allocated memory region + * + * Returns 0 on success, negative errno otherwise. + */ +int qcom_mdt_load(struct device *dev, const struct firmware *fw, + const char *firmware, int pas_id, void *mem_region, + phys_addr_t mem_phys, size_t mem_size) +{ + const struct elf32_phdr *phdrs; + const struct elf32_phdr *phdr; + const struct elf32_hdr *ehdr; + const struct firmware *seg_fw; + phys_addr_t mem_reloc; + phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; + phys_addr_t max_addr = 0; + size_t fw_name_len; + ssize_t offset; + char *fw_name; + bool relocate = false; + void *ptr; + int ret; + int i; + + if (!fw || !mem_region || !mem_phys || !mem_size) + return -EINVAL; + + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); + + fw_name_len = strlen(firmware); + if (fw_name_len <= 4) + return -EINVAL; + + fw_name = kstrdup(firmware, GFP_KERNEL); + if (!fw_name) + return -ENOMEM; + + ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); + if (ret) { + dev_err(dev, "invalid firmware metadata\n"); + goto out; + } + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (!mdt_phdr_valid(phdr)) + continue; + + if (phdr->p_flags & QCOM_MDT_RELOCATABLE) + relocate = true; + + if (phdr->p_paddr < min_addr) + min_addr = phdr->p_paddr; + + if (phdr->p_paddr + phdr->p_memsz > max_addr) + max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); + } + + if (relocate) { + ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); + if (ret) { + dev_err(dev, "unable to setup relocation\n"); + goto out; + } + + /* + * The image is relocatable, so offset each segment based on + * the lowest segment address. + */ + mem_reloc = min_addr; + } else { + /* + * Image is not relocatable, so offset each segment based on + * the allocated physical chunk of memory. + */ + mem_reloc = mem_phys; + } + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (!mdt_phdr_valid(phdr)) + continue; + + offset = phdr->p_paddr - mem_reloc; + if (offset < 0 || offset + phdr->p_memsz > mem_size) { + dev_err(dev, "segment outside memory range\n"); + ret = -EINVAL; + break; + } + + ptr = mem_region + offset; + + if (phdr->p_filesz) { + sprintf(fw_name + fw_name_len - 3, "b%02d", i); + ret = request_firmware(&seg_fw, fw_name, dev); + if (ret) { + dev_err(dev, "failed to load %s\n", fw_name); + break; + } + + memcpy(ptr, seg_fw->data, seg_fw->size); + + release_firmware(seg_fw); + } + + if (phdr->p_memsz > phdr->p_filesz) + memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); + } + +out: + kfree(fw_name); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_mdt_load); + +MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index 7140ff825598..20da55d9cbb1 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -3,6 +3,16 @@ if ARCH_ROCKCHIP || COMPILE_TEST # # Rockchip Soc drivers # + +config ROCKCHIP_GRF + bool + default y + help + The General Register Files are a central component providing + special additional settings registers for a lot of soc-components. + In a lot of cases there also need to be default settings initialized + to make some of them conform to expectations of the kernel. + config ROCKCHIP_PM_DOMAINS bool "Rockchip generic power domain" depends on PM diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index 3d73d0672d22..c851fa0056d0 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -1,4 +1,5 @@ # # Rockchip Soc drivers # +obj-$(CONFIG_ROCKCHIP_GRF) += grf.o obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c new file mode 100644 index 000000000000..d61db34ad6dd --- /dev/null +++ b/drivers/soc/rockchip/grf.c @@ -0,0 +1,134 @@ +/* + * Rockchip Generic Register Files setup + * + * Copyright (c) 2016 Heiko Stuebner <heiko@sntech.de> + * + * 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/err.h> +#include <linux/mfd/syscon.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +struct rockchip_grf_value { + const char *desc; + u32 reg; + u32 val; +}; + +struct rockchip_grf_info { + const struct rockchip_grf_value *values; + int num_values; +}; + +#define RK3036_GRF_SOC_CON0 0x140 + +static const struct rockchip_grf_value rk3036_defaults[] __initconst = { + /* + * Disable auto jtag/sdmmc switching that causes issues with the + * clock-framework and the mmc controllers making them unreliable. + */ + { "jtag switching", RK3036_GRF_SOC_CON0, HIWORD_UPDATE(0, 1, 11) }, +}; + +static const struct rockchip_grf_info rk3036_grf __initconst = { + .values = rk3036_defaults, + .num_values = ARRAY_SIZE(rk3036_defaults), +}; + +#define RK3288_GRF_SOC_CON0 0x244 + +static const struct rockchip_grf_value rk3288_defaults[] __initconst = { + { "jtag switching", RK3288_GRF_SOC_CON0, HIWORD_UPDATE(0, 1, 12) }, +}; + +static const struct rockchip_grf_info rk3288_grf __initconst = { + .values = rk3288_defaults, + .num_values = ARRAY_SIZE(rk3288_defaults), +}; + +#define RK3368_GRF_SOC_CON15 0x43c + +static const struct rockchip_grf_value rk3368_defaults[] __initconst = { + { "jtag switching", RK3368_GRF_SOC_CON15, HIWORD_UPDATE(0, 1, 13) }, +}; + +static const struct rockchip_grf_info rk3368_grf __initconst = { + .values = rk3368_defaults, + .num_values = ARRAY_SIZE(rk3368_defaults), +}; + +#define RK3399_GRF_SOC_CON7 0xe21c + +static const struct rockchip_grf_value rk3399_defaults[] __initconst = { + { "jtag switching", RK3399_GRF_SOC_CON7, HIWORD_UPDATE(0, 1, 12) }, +}; + +static const struct rockchip_grf_info rk3399_grf __initconst = { + .values = rk3399_defaults, + .num_values = ARRAY_SIZE(rk3399_defaults), +}; + +static const struct of_device_id rockchip_grf_dt_match[] __initconst = { + { + .compatible = "rockchip,rk3036-grf", + .data = (void *)&rk3036_grf, + }, { + .compatible = "rockchip,rk3288-grf", + .data = (void *)&rk3288_grf, + }, { + .compatible = "rockchip,rk3368-grf", + .data = (void *)&rk3368_grf, + }, { + .compatible = "rockchip,rk3399-grf", + .data = (void *)&rk3399_grf, + }, + { /* sentinel */ }, +}; + +static int __init rockchip_grf_init(void) +{ + const struct rockchip_grf_info *grf_info; + const struct of_device_id *match; + struct device_node *np; + struct regmap *grf; + int ret, i; + + np = of_find_matching_node_and_match(NULL, rockchip_grf_dt_match, + &match); + if (!np) + return -ENODEV; + if (!match || !match->data) { + pr_err("%s: missing grf data\n", __func__); + return -EINVAL; + } + + grf_info = match->data; + + grf = syscon_node_to_regmap(np); + if (IS_ERR(grf)) { + pr_err("%s: could not get grf syscon\n", __func__); + return PTR_ERR(grf); + } + + for (i = 0; i < grf_info->num_values; i++) { + const struct rockchip_grf_value *val = &grf_info->values[i]; + + pr_debug("%s: adjusting %s in %#6x to %#10x\n", __func__, + val->desc, val->reg, val->val); + ret = regmap_write(grf, val->reg, val->val); + if (ret < 0) + pr_err("%s: write to %#6x failed with %d\n", + __func__, val->reg, ret); + } + + return 0; +} +postcore_initcall(rockchip_grf_init); diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 1c78c42416c6..796c46a6cbe7 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -19,6 +19,7 @@ #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <dt-bindings/power/rk3288-power.h> +#include <dt-bindings/power/rk3328-power.h> #include <dt-bindings/power/rk3368-power.h> #include <dt-bindings/power/rk3399-power.h> @@ -29,6 +30,8 @@ struct rockchip_domain_info { int idle_mask; int ack_mask; bool active_wakeup; + int pwr_w_mask; + int req_w_mask; }; struct rockchip_pmu_info { @@ -87,9 +90,24 @@ struct rockchip_pmu { .active_wakeup = wakeup, \ } +#define DOMAIN_M(pwr, status, req, idle, ack, wakeup) \ +{ \ + .pwr_w_mask = (pwr >= 0) ? BIT(pwr + 16) : 0, \ + .pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \ + .status_mask = (status >= 0) ? BIT(status) : 0, \ + .req_w_mask = (req >= 0) ? BIT(req + 16) : 0, \ + .req_mask = (req >= 0) ? BIT(req) : 0, \ + .idle_mask = (idle >= 0) ? BIT(idle) : 0, \ + .ack_mask = (ack >= 0) ? BIT(ack) : 0, \ + .active_wakeup = wakeup, \ +} + #define DOMAIN_RK3288(pwr, status, req, wakeup) \ DOMAIN(pwr, status, req, req, (req) + 16, wakeup) +#define DOMAIN_RK3328(pwr, status, req, wakeup) \ + DOMAIN_M(pwr, pwr, req, (req) + 10, req, wakeup) + #define DOMAIN_RK3368(pwr, status, req, wakeup) \ DOMAIN(pwr, status, req, (req) + 16, req, wakeup) @@ -127,9 +145,13 @@ static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, if (pd_info->req_mask == 0) return 0; - - regmap_update_bits(pmu->regmap, pmu->info->req_offset, - pd_info->req_mask, idle ? -1U : 0); + else if (pd_info->req_w_mask) + regmap_write(pmu->regmap, pmu->info->req_offset, + idle ? (pd_info->req_mask | pd_info->req_w_mask) : + pd_info->req_w_mask); + else + regmap_update_bits(pmu->regmap, pmu->info->req_offset, + pd_info->req_mask, idle ? -1U : 0); dsb(sy); @@ -230,9 +252,13 @@ static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, if (pd->info->pwr_mask == 0) return; - - regmap_update_bits(pmu->regmap, pmu->info->pwr_offset, - pd->info->pwr_mask, on ? 0 : -1U); + else if (pd->info->pwr_w_mask) + regmap_write(pmu->regmap, pmu->info->pwr_offset, + on ? pd->info->pwr_mask : + (pd->info->pwr_mask | pd->info->pwr_w_mask)); + else + regmap_update_bits(pmu->regmap, pmu->info->pwr_offset, + pd->info->pwr_mask, on ? 0 : -1U); dsb(sy); @@ -692,6 +718,18 @@ static const struct rockchip_domain_info rk3288_pm_domains[] = { [RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2, false), }; +static const struct rockchip_domain_info rk3328_pm_domains[] = { + [RK3328_PD_CORE] = DOMAIN_RK3328(-1, 0, 0, false), + [RK3328_PD_GPU] = DOMAIN_RK3328(-1, 1, 1, false), + [RK3328_PD_BUS] = DOMAIN_RK3328(-1, 2, 2, true), + [RK3328_PD_MSCH] = DOMAIN_RK3328(-1, 3, 3, true), + [RK3328_PD_PERI] = DOMAIN_RK3328(-1, 4, 4, true), + [RK3328_PD_VIDEO] = DOMAIN_RK3328(-1, 5, 5, false), + [RK3328_PD_HEVC] = DOMAIN_RK3328(-1, 6, 6, false), + [RK3328_PD_VIO] = DOMAIN_RK3328(-1, 8, 8, false), + [RK3328_PD_VPU] = DOMAIN_RK3328(-1, 9, 9, false), +}; + static const struct rockchip_domain_info rk3368_pm_domains[] = { [RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6, true), [RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8, false), @@ -747,6 +785,15 @@ static const struct rockchip_pmu_info rk3288_pmu = { .domain_info = rk3288_pm_domains, }; +static const struct rockchip_pmu_info rk3328_pmu = { + .req_offset = 0x414, + .idle_offset = 0x484, + .ack_offset = 0x484, + + .num_domains = ARRAY_SIZE(rk3328_pm_domains), + .domain_info = rk3328_pm_domains, +}; + static const struct rockchip_pmu_info rk3368_pmu = { .pwr_offset = 0x0c, .status_offset = 0x10, @@ -783,6 +830,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .data = (void *)&rk3288_pmu, }, { + .compatible = "rockchip,rk3328-power-controller", + .data = (void *)&rk3328_pmu, + }, + { .compatible = "rockchip,rk3368-power-controller", .data = (void *)&rk3368_pmu, }, diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index 813df6e7292d..56d9244ff981 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -44,7 +44,7 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode) unsigned int i; const struct exynos_pmu_data *pmu_data; - if (!pmu_context) + if (!pmu_context || !pmu_context->pmu_data) return; pmu_data = pmu_context->pmu_data; @@ -90,6 +90,8 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = { }, { .compatible = "samsung,exynos5420-pmu", .data = &exynos5420_pmu_data, + }, { + .compatible = "samsung,exynos5433-pmu", }, { /*sentinel*/ }, }; @@ -122,7 +124,7 @@ static int exynos_pmu_probe(struct platform_device *pdev) pmu_context->dev = dev; pmu_context->pmu_data = of_device_get_match_data(dev); - if (pmu_context->pmu_data->pmu_init) + if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init) pmu_context->pmu_data->pmu_init(); platform_set_drvdata(pdev, pmu_context); diff --git a/drivers/soc/samsung/exynos5250-pmu.c b/drivers/soc/samsung/exynos5250-pmu.c index 3fac42561964..8d94f0819f32 100644 --- a/drivers/soc/samsung/exynos5250-pmu.c +++ b/drivers/soc/samsung/exynos5250-pmu.c @@ -29,7 +29,7 @@ static const struct exynos_pmu_conf exynos5250_pmu_config[] = { { EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, { EXYNOS5_ARM_COMMON_SYS_PWR_REG, { 0x0, 0x0, 0x2} }, { EXYNOS5_ARM_L2_SYS_PWR_REG, { 0x3, 0x3, 0x3} }, - { EXYNOS5_ARM_L2_OPTION, { 0x10, 0x10, 0x0 } }, + { EXYNOS_L2_OPTION(0), { 0x10, 0x10, 0x0 } }, { EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, { EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, { EXYNOS5_CMU_RESET_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, diff --git a/drivers/soc/samsung/exynos5420-pmu.c b/drivers/soc/samsung/exynos5420-pmu.c index 3f2c64180ef8..0a89fa79c678 100644 --- a/drivers/soc/samsung/exynos5420-pmu.c +++ b/drivers/soc/samsung/exynos5420-pmu.c @@ -230,11 +230,11 @@ static void exynos5420_pmu_init(void) pmu_raw_writel(EXYNOS5420_USE_STANDBY_WFI_ALL, S5P_CENTRAL_SEQ_OPTION); value = pmu_raw_readl(EXYNOS_L2_OPTION(0)); - value &= ~EXYNOS5_USE_RETENTION; + value &= ~EXYNOS_L2_USE_RETENTION; pmu_raw_writel(value, EXYNOS_L2_OPTION(0)); value = pmu_raw_readl(EXYNOS_L2_OPTION(1)); - value &= ~EXYNOS5_USE_RETENTION; + value &= ~EXYNOS_L2_USE_RETENTION; pmu_raw_writel(value, EXYNOS_L2_OPTION(1)); /* diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c index 7112004b8032..a6a5d807cc2b 100644 --- a/drivers/soc/samsung/pm_domains.c +++ b/drivers/soc/samsung/pm_domains.c @@ -35,7 +35,6 @@ struct exynos_pm_domain_config { */ struct exynos_pm_domain { void __iomem *base; - char const *name; bool is_off; struct generic_pm_domain pd; struct clk *oscclk; @@ -70,7 +69,7 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) pd->pclk[i] = clk_get_parent(pd->clk[i]); if (clk_set_parent(pd->clk[i], pd->oscclk)) pr_err("%s: error setting oscclk as parent to clock %d\n", - pd->name, i); + domain->name, i); } } @@ -101,7 +100,7 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) continue; /* Skip on first power up */ if (clk_set_parent(pd->clk[i], pd->pclk[i])) pr_err("%s: error setting parent to clock%d\n", - pd->name, i); + domain->name, i); } } @@ -128,14 +127,30 @@ static const struct exynos_pm_domain_config exynos4210_cfg __initconst = { .local_pwr_cfg = 0x7, }; +static const struct exynos_pm_domain_config exynos5433_cfg __initconst = { + .local_pwr_cfg = 0xf, +}; + static const struct of_device_id exynos_pm_domain_of_match[] __initconst = { { .compatible = "samsung,exynos4210-pd", .data = &exynos4210_cfg, + }, { + .compatible = "samsung,exynos5433-pd", + .data = &exynos5433_cfg, }, { }, }; +static __init const char *exynos_get_domain_name(struct device_node *node) +{ + const char *name; + + if (of_property_read_string(node, "label", &name) < 0) + name = strrchr(node->full_name, '/') + 1; + return kstrdup_const(name, GFP_KERNEL); +} + static __init int exynos4_pm_init_power_domain(void) { struct device_node *np; @@ -150,20 +165,16 @@ static __init int exynos4_pm_init_power_domain(void) pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { - pr_err("%s: failed to allocate memory for domain\n", - __func__); of_node_put(np); return -ENOMEM; } - pd->pd.name = kstrdup_const(strrchr(np->full_name, '/') + 1, - GFP_KERNEL); + pd->pd.name = exynos_get_domain_name(np); if (!pd->pd.name) { kfree(pd); of_node_put(np); return -ENOMEM; } - pd->name = pd->pd.name; pd->base = of_iomap(np, 0); if (!pd->base) { pr_warn("%s: failed to map memory\n", __func__); @@ -227,10 +238,10 @@ no_clk: if (of_genpd_add_subdomain(&parent, &child)) pr_warn("%s failed to add subdomain: %s\n", - parent.np->name, child.np->name); + parent.np->full_name, child.np->full_name); else pr_info("%s has as child subdomain: %s.\n", - parent.np->name, child.np->name); + parent.np->full_name, child.np->full_name); } return 0; diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index 1a7b5caa127b..ecebe2eecc3a 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -395,7 +395,7 @@ static int of_channel_match_helper(struct device_node *np, const char *name, if (of_parse_phandle_with_fixed_args(np, "ti,navigator-dmas", 1, index, &args)) { - dev_err(kdev->dev, "Missing the pahndle args name %s\n", name); + dev_err(kdev->dev, "Missing the phandle args name %s\n", name); return -ENODEV; } @@ -436,7 +436,7 @@ void *knav_dma_open_channel(struct device *dev, const char *name, } dev_dbg(kdev->dev, "initializing %s channel %d from DMA %s\n", - config->direction == DMA_MEM_TO_DEV ? "transmit" : + config->direction == DMA_MEM_TO_DEV ? "transmit" : config->direction == DMA_DEV_TO_MEM ? "receive" : "unknown", chan_num, instance); diff --git a/drivers/soc/ti/knav_qmss_acc.c b/drivers/soc/ti/knav_qmss_acc.c index 0612ebae0a09..3d7225f4e77f 100644 --- a/drivers/soc/ti/knav_qmss_acc.c +++ b/drivers/soc/ti/knav_qmss_acc.c @@ -16,21 +16,12 @@ * General Public License for more details. */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/device.h> +#include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/interrupt.h> -#include <linux/bitops.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/soc/ti/knav_qmss.h> -#include <linux/platform_device.h> -#include <linux/dma-mapping.h> -#include <linux/of.h> -#include <linux/of_device.h> +#include <linux/module.h> #include <linux/of_address.h> -#include <linux/firmware.h> +#include <linux/soc/ti/knav_qmss.h> #include "knav_qmss.h" diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index eacad57f2977..279e7c5551dd 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -16,26 +16,17 @@ * General Public License for more details. */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/bitops.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/platform_device.h> +#include <linux/debugfs.h> #include <linux/dma-mapping.h> -#include <linux/of.h> -#include <linux/of_irq.h> -#include <linux/of_device.h> +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> #include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/pm_runtime.h> -#include <linux/firmware.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/string.h> +#include <linux/slab.h> #include <linux/soc/ti/knav_qmss.h> #include "knav_qmss.h" diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 5bb376009d98..369aef5e7228 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -370,8 +370,6 @@ static void wkup_m3_rproc_boot_thread(struct wkup_m3_ipc *m3_ipc) struct device *dev = m3_ipc->dev; int ret; - wait_for_completion(&m3_ipc->rproc->firmware_loading_complete); - init_completion(&m3_ipc->sync_complete); ret = rproc_boot(m3_ipc->rproc); diff --git a/drivers/soc/zte/Kconfig b/drivers/soc/zte/Kconfig new file mode 100644 index 000000000000..20bde38ce2f9 --- /dev/null +++ b/drivers/soc/zte/Kconfig @@ -0,0 +1,13 @@ +# +# ZTE SoC drivers +# +menuconfig SOC_ZTE + bool "ZTE SoC driver support" + +if SOC_ZTE + +config ZX2967_PM_DOMAINS + bool "ZX2967 PM domains" + depends on PM_GENERIC_DOMAINS + +endif diff --git a/drivers/soc/zte/Makefile b/drivers/soc/zte/Makefile new file mode 100644 index 000000000000..96b7cd4c9629 --- /dev/null +++ b/drivers/soc/zte/Makefile @@ -0,0 +1,5 @@ +# +# ZTE SOC drivers +# +obj-$(CONFIG_ZX2967_PM_DOMAINS) += zx2967_pm_domains.o +obj-$(CONFIG_ZX2967_PM_DOMAINS) += zx296718_pm_domains.o diff --git a/drivers/soc/zte/zx296718_pm_domains.c b/drivers/soc/zte/zx296718_pm_domains.c new file mode 100644 index 000000000000..5ed924fee855 --- /dev/null +++ b/drivers/soc/zte/zx296718_pm_domains.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 ZTE Ltd. + * + * Author: Baoyou Xie <baoyou.xie@linaro.org> + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <dt-bindings/soc/zte,pm_domains.h> +#include "zx2967_pm_domains.h" + +static u16 zx296718_offsets[REG_ARRAY_SIZE] = { + [REG_CLKEN] = 0x18, + [REG_ISOEN] = 0x1c, + [REG_RSTEN] = 0x20, + [REG_PWREN] = 0x24, + [REG_ACK_SYNC] = 0x28, +}; + +enum { + PCU_DM_VOU = 0, + PCU_DM_SAPPU, + PCU_DM_VDE, + PCU_DM_VCE, + PCU_DM_HDE, + PCU_DM_VIU, + PCU_DM_USB20, + PCU_DM_USB21, + PCU_DM_USB30, + PCU_DM_HSIC, + PCU_DM_GMAC, + PCU_DM_TS, +}; + +static struct zx2967_pm_domain vou_domain = { + .dm = { + .name = "vou_domain", + }, + .bit = PCU_DM_VOU, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain sappu_domain = { + .dm = { + .name = "sappu_domain", + }, + .bit = PCU_DM_SAPPU, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain vde_domain = { + .dm = { + .name = "vde_domain", + }, + .bit = PCU_DM_VDE, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain vce_domain = { + .dm = { + .name = "vce_domain", + }, + .bit = PCU_DM_VCE, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain hde_domain = { + .dm = { + .name = "hde_domain", + }, + .bit = PCU_DM_HDE, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain viu_domain = { + .dm = { + .name = "viu_domain", + }, + .bit = PCU_DM_VIU, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain usb20_domain = { + .dm = { + .name = "usb20_domain", + }, + .bit = PCU_DM_USB20, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain usb21_domain = { + .dm = { + .name = "usb21_domain", + }, + .bit = PCU_DM_USB21, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain usb30_domain = { + .dm = { + .name = "usb30_domain", + }, + .bit = PCU_DM_USB30, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain hsic_domain = { + .dm = { + .name = "hsic_domain", + }, + .bit = PCU_DM_HSIC, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain gmac_domain = { + .dm = { + .name = "gmac_domain", + }, + .bit = PCU_DM_GMAC, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct zx2967_pm_domain ts_domain = { + .dm = { + .name = "ts_domain", + }, + .bit = PCU_DM_TS, + .polarity = PWREN, + .reg_offset = zx296718_offsets, +}; + +static struct generic_pm_domain *zx296718_pm_domains[] = { + [DM_ZX296718_VOU] = &vou_domain.dm, + [DM_ZX296718_SAPPU] = &sappu_domain.dm, + [DM_ZX296718_VDE] = &vde_domain.dm, + [DM_ZX296718_VCE] = &vce_domain.dm, + [DM_ZX296718_HDE] = &hde_domain.dm, + [DM_ZX296718_VIU] = &viu_domain.dm, + [DM_ZX296718_USB20] = &usb20_domain.dm, + [DM_ZX296718_USB21] = &usb21_domain.dm, + [DM_ZX296718_USB30] = &usb30_domain.dm, + [DM_ZX296718_HSIC] = &hsic_domain.dm, + [DM_ZX296718_GMAC] = &gmac_domain.dm, + [DM_ZX296718_TS] = &ts_domain.dm, +}; + +static int zx296718_pd_probe(struct platform_device *pdev) +{ + return zx2967_pd_probe(pdev, + zx296718_pm_domains, + ARRAY_SIZE(zx296718_pm_domains)); +} + +static const struct of_device_id zx296718_pm_domain_matches[] = { + { .compatible = "zte,zx296718-pcu", }, + { }, +}; + +static struct platform_driver zx296718_pd_driver = { + .driver = { + .name = "zx296718-powerdomain", + .owner = THIS_MODULE, + .of_match_table = zx296718_pm_domain_matches, + }, + .probe = zx296718_pd_probe, +}; + +static int __init zx296718_pd_init(void) +{ + return platform_driver_register(&zx296718_pd_driver); +} +subsys_initcall(zx296718_pd_init); diff --git a/drivers/soc/zte/zx2967_pm_domains.c b/drivers/soc/zte/zx2967_pm_domains.c new file mode 100644 index 000000000000..61c8d84bf315 --- /dev/null +++ b/drivers/soc/zte/zx2967_pm_domains.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2017 ZTE Ltd. + * + * Author: Baoyou Xie <baoyou.xie@linaro.org> + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "zx2967_pm_domains.h" + +#define PCU_DM_CLKEN(zpd) ((zpd)->reg_offset[REG_CLKEN]) +#define PCU_DM_ISOEN(zpd) ((zpd)->reg_offset[REG_ISOEN]) +#define PCU_DM_RSTEN(zpd) ((zpd)->reg_offset[REG_RSTEN]) +#define PCU_DM_PWREN(zpd) ((zpd)->reg_offset[REG_PWREN]) +#define PCU_DM_ACK_SYNC(zpd) ((zpd)->reg_offset[REG_ACK_SYNC]) + +static void __iomem *pcubase; + +static int zx2967_power_on(struct generic_pm_domain *domain) +{ + struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain; + unsigned long loop = 1000; + u32 val; + + val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd)); + if (zpd->polarity == PWREN) + val |= BIT(zpd->bit); + else + val &= ~BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd)); + + do { + udelay(1); + val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd)) + & BIT(zpd->bit); + } while (--loop && !val); + + if (!loop) { + pr_err("Error: %s %s fail\n", __func__, domain->name); + return -EIO; + } + + val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd)); + val |= BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd)); + udelay(5); + + val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd)); + val &= ~BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd)); + udelay(5); + + val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd)); + val |= BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd)); + udelay(5); + + pr_debug("poweron %s\n", domain->name); + + return 0; +} + +static int zx2967_power_off(struct generic_pm_domain *domain) +{ + struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain; + unsigned long loop = 1000; + u32 val; + + val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd)); + val &= ~BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd)); + udelay(5); + + val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd)); + val |= BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd)); + udelay(5); + + val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd)); + val &= ~BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd)); + udelay(5); + + val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd)); + if (zpd->polarity == PWREN) + val &= ~BIT(zpd->bit); + else + val |= BIT(zpd->bit); + writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd)); + + do { + udelay(1); + val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd)) + & BIT(zpd->bit); + } while (--loop && val); + + if (!loop) { + pr_err("Error: %s %s fail\n", __func__, domain->name); + return -EIO; + } + + pr_debug("poweroff %s\n", domain->name); + + return 0; +} + +int zx2967_pd_probe(struct platform_device *pdev, + struct generic_pm_domain **zx_pm_domains, + int domain_num) +{ + struct genpd_onecell_data *genpd_data; + struct resource *res; + int i; + + genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL); + if (!genpd_data) + return -ENOMEM; + + genpd_data->domains = zx_pm_domains; + genpd_data->num_domains = domain_num; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcubase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcubase)) { + dev_err(&pdev->dev, "ioremap fail.\n"); + return PTR_ERR(pcubase); + } + + for (i = 0; i < domain_num; ++i) { + zx_pm_domains[i]->power_on = zx2967_power_on; + zx_pm_domains[i]->power_off = zx2967_power_off; + + pm_genpd_init(zx_pm_domains[i], NULL, false); + } + + of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data); + dev_info(&pdev->dev, "powerdomain init ok\n"); + return 0; +} diff --git a/drivers/soc/zte/zx2967_pm_domains.h b/drivers/soc/zte/zx2967_pm_domains.h new file mode 100644 index 000000000000..cb46595a7ff3 --- /dev/null +++ b/drivers/soc/zte/zx2967_pm_domains.h @@ -0,0 +1,44 @@ +/* + * Header for ZTE's Power Domain Driver support + * + * Copyright (C) 2017 ZTE Ltd. + * + * Author: Baoyou Xie <baoyou.xie@linaro.org> + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __ZTE_ZX2967_PM_DOMAIN_H +#define __ZTE_ZX2967_PM_DOMAIN_H + +#include <linux/platform_device.h> +#include <linux/pm_domain.h> + +enum { + REG_CLKEN, + REG_ISOEN, + REG_RSTEN, + REG_PWREN, + REG_PWRDN, + REG_ACK_SYNC, + + /* The size of the array - must be last */ + REG_ARRAY_SIZE, +}; + +enum zx2967_power_polarity { + PWREN, + PWRDN, +}; + +struct zx2967_pm_domain { + struct generic_pm_domain dm; + const u16 bit; + const enum zx2967_power_polarity polarity; + const u16 *reg_offset; +}; + +int zx2967_pd_probe(struct platform_device *pdev, + struct generic_pm_domain **zx_pm_domains, + int domain_num); + +#endif /* __ZTE_ZX2967_PM_DOMAIN_H */ diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 44222ef9471e..90b5b2efafbf 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -33,6 +33,7 @@ #include <linux/pm_domain.h> #include <linux/export.h> #include <linux/sched/rt.h> +#include <uapi/linux/sched/types.h> #include <linux/delay.h> #include <linux/kthread.h> #include <linux/ioport.h> diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 969600779e44..f45115fce4eb 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -36,6 +36,7 @@ #include <linux/debugfs.h> #include <linux/dma-buf.h> #include <linux/idr.h> +#include <linux/sched/task.h> #include "ion.h" #include "ion_priv.h" @@ -870,9 +871,9 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer, mutex_unlock(&buffer->lock); } -static int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int ion_vm_fault(struct vm_fault *vmf) { - struct ion_buffer *buffer = vma->vm_private_data; + struct ion_buffer *buffer = vmf->vma->vm_private_data; unsigned long pfn; int ret; @@ -881,7 +882,7 @@ static int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) BUG_ON(!buffer->pages || !buffer->pages[vmf->pgoff]); pfn = page_to_pfn(ion_buffer_page(buffer->pages[vmf->pgoff])); - ret = vm_insert_pfn(vma, vmf->address, pfn); + ret = vm_insert_pfn(vmf->vma, vmf->address, pfn); mutex_unlock(&buffer->lock); if (ret) return VM_FAULT_ERROR; diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c index 4e5c0f17f579..c69d0bd53693 100644 --- a/drivers/staging/android/ion/ion_heap.c +++ b/drivers/staging/android/ion/ion_heap.c @@ -20,6 +20,7 @@ #include <linux/mm.h> #include <linux/rtmutex.h> #include <linux/sched.h> +#include <uapi/linux/sched/types.h> #include <linux/scatterlist.h> #include <linux/vmalloc.h> #include "ion.h" diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index ec3b66561412..054660049395 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -37,7 +37,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/oom.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/swap.h> #include <linux/rcupdate.h> #include <linux/profile.h> diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 57e8599b54e6..8deac8d9225d 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -23,7 +23,7 @@ #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/fcntl.h> #include <linux/delay.h> #include <linux/mm.h> diff --git a/drivers/staging/dgnc/dgnc_tty.c b/drivers/staging/dgnc/dgnc_tty.c index c63e591631f6..c3b8fc54883d 100644 --- a/drivers/staging/dgnc/dgnc_tty.c +++ b/drivers/staging/dgnc/dgnc_tty.c @@ -19,7 +19,7 @@ */ #include <linux/kernel.h> -#include <linux/sched.h> /* For jiffies, task states */ +#include <linux/sched/signal.h> /* For jiffies, task states, etc. */ #include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */ #include <linux/module.h> #include <linux/ctype.h> diff --git a/drivers/staging/dgnc/dgnc_utils.c b/drivers/staging/dgnc/dgnc_utils.c index 95272f4765fc..6f59240024d1 100644 --- a/drivers/staging/dgnc/dgnc_utils.c +++ b/drivers/staging/dgnc/dgnc_utils.c @@ -1,5 +1,5 @@ #include <linux/tty.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include "dgnc_utils.h" /* diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index c4bf3298ba07..f0404bc37123 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -284,7 +284,6 @@ static int gb_pwm_probe(struct gbphy_device *gbphy_dev, pwm->ops = &gb_pwm_ops; pwm->base = -1; /* Allocate base dynamically */ pwm->npwm = pwmc->pwm_max + 1; - pwm->can_sleep = true; /* FIXME */ ret = pwmchip_add(pwm); if (ret) { diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index ab0dbf5cab5a..43255e2e9276 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -14,7 +14,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/module.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/slab.h> #include <linux/uaccess.h> diff --git a/drivers/staging/gs_fpgaboot/gs_fpgaboot.h b/drivers/staging/gs_fpgaboot/gs_fpgaboot.h index 7b8cc3a25214..cd1eb2c4c940 100644 --- a/drivers/staging/gs_fpgaboot/gs_fpgaboot.h +++ b/drivers/staging/gs_fpgaboot/gs_fpgaboot.h @@ -39,7 +39,7 @@ struct fpgaimage { const struct firmware *fw_entry; /* - * the followings can be read from bitstream, + * the following can be read from bitstream, * but other image format should have as well */ char filename[MAX_STR]; diff --git a/drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c b/drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c index cf902154f0aa..bcf9f3dd0310 100644 --- a/drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c +++ b/drivers/staging/lustre/lnet/libcfs/linux/linux-prim.c @@ -34,7 +34,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs_struct.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include "../../../include/linux/libcfs/libcfs.h" diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h b/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h index b0eb80d70c23..60b827eeefe2 100644 --- a/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h +++ b/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h @@ -1704,7 +1704,7 @@ struct ost_lvb { * lquota data structures */ -/* The lquota_id structure is an union of all the possible identifier types that +/* The lquota_id structure is a union of all the possible identifier types that * can be used with quota, this includes: * - 64-bit user ID * - 64-bit group ID diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h index 21aec0ca9ad3..7d8628ce0d3b 100644 --- a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h +++ b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h @@ -44,6 +44,7 @@ #ifdef __KERNEL__ # include <linux/quota.h> +# include <linux/sched/signal.h> # include <linux/string.h> /* snprintf() */ # include <linux/version.h> #else /* !__KERNEL__ */ diff --git a/drivers/staging/lustre/lustre/include/lustre_compat.h b/drivers/staging/lustre/lustre/include/lustre_compat.h index 300e96fb032a..da9ce195c52e 100644 --- a/drivers/staging/lustre/lustre/include/lustre_compat.h +++ b/drivers/staging/lustre/lustre/include/lustre_compat.h @@ -35,6 +35,7 @@ #include <linux/fs_struct.h> #include <linux/namei.h> +#include <linux/cred.h> #include "lustre_patchless_compat.h" diff --git a/drivers/staging/lustre/lustre/include/lustre_lib.h b/drivers/staging/lustre/lustre/include/lustre_lib.h index 27f3148c4344..b04d613846ee 100644 --- a/drivers/staging/lustre/lustre/include/lustre_lib.h +++ b/drivers/staging/lustre/lustre/include/lustre_lib.h @@ -42,7 +42,7 @@ * @{ */ -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/signal.h> #include <linux/types.h> #include "../../include/linux/libcfs/libcfs.h" diff --git a/drivers/staging/lustre/lustre/include/obd_support.h b/drivers/staging/lustre/lustre/include/obd_support.h index aaedec7d793c..dace6591a0a4 100644 --- a/drivers/staging/lustre/lustre/include/obd_support.h +++ b/drivers/staging/lustre/lustre/include/obd_support.h @@ -34,6 +34,8 @@ #define _OBD_SUPPORT #include <linux/slab.h> +#include <linux/sched/signal.h> + #include "../../include/linux/libcfs/libcfs.h" #include "lustre_compat.h" #include "lprocfs_status.h" diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c index 10adfcdd7035..481c0d01d4c6 100644 --- a/drivers/staging/lustre/lustre/llite/file.c +++ b/drivers/staging/lustre/lustre/llite/file.c @@ -2952,15 +2952,16 @@ static int ll_inode_revalidate(struct dentry *dentry, __u64 ibits) return rc; } -int ll_getattr(struct vfsmount *mnt, struct dentry *de, struct kstat *stat) +int ll_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) { - struct inode *inode = d_inode(de); + struct inode *inode = d_inode(path->dentry); struct ll_sb_info *sbi = ll_i2sbi(inode); struct ll_inode_info *lli = ll_i2info(inode); int res; - res = ll_inode_revalidate(de, MDS_INODELOCK_UPDATE | - MDS_INODELOCK_LOOKUP); + res = ll_inode_revalidate(path->dentry, + MDS_INODELOCK_UPDATE | MDS_INODELOCK_LOOKUP); ll_stats_ops_tally(sbi, LPROC_LL_GETATTR, 1); if (res) diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h index ecdfd0c29b7f..55f68acd85d1 100644 --- a/drivers/staging/lustre/lustre/llite/llite_internal.h +++ b/drivers/staging/lustre/lustre/llite/llite_internal.h @@ -750,7 +750,8 @@ int ll_file_open(struct inode *inode, struct file *file); int ll_file_release(struct inode *inode, struct file *file); int ll_release_openhandle(struct inode *, struct lookup_intent *); int ll_md_real_close(struct inode *inode, fmode_t fmode); -int ll_getattr(struct vfsmount *mnt, struct dentry *de, struct kstat *stat); +int ll_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags); struct posix_acl *ll_get_acl(struct inode *inode, int type); int ll_migrate(struct inode *parent, struct file *file, int mdtidx, const char *name, int namelen); diff --git a/drivers/staging/lustre/lustre/llite/llite_mmap.c b/drivers/staging/lustre/lustre/llite/llite_mmap.c index 9afa6bec3e6f..896196c74cd2 100644 --- a/drivers/staging/lustre/lustre/llite/llite_mmap.c +++ b/drivers/staging/lustre/lustre/llite/llite_mmap.c @@ -321,7 +321,7 @@ out: return fault_ret; } -static int ll_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int ll_fault(struct vm_fault *vmf) { int count = 0; bool printed = false; @@ -335,7 +335,7 @@ static int ll_fault(struct vm_area_struct *vma, struct vm_fault *vmf) set = cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM)); restart: - result = ll_fault0(vma, vmf); + result = ll_fault0(vmf->vma, vmf); LASSERT(!(result & VM_FAULT_LOCKED)); if (result == 0) { struct page *vmpage = vmf->page; @@ -362,8 +362,9 @@ restart: return result; } -static int ll_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) +static int ll_page_mkwrite(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; int count = 0; bool printed = false; bool retry; diff --git a/drivers/staging/lustre/lustre/llite/vvp_io.c b/drivers/staging/lustre/lustre/llite/vvp_io.c index 3e9cf710501b..4c57755e06e7 100644 --- a/drivers/staging/lustre/lustre/llite/vvp_io.c +++ b/drivers/staging/lustre/lustre/llite/vvp_io.c @@ -1014,7 +1014,7 @@ static int vvp_io_kernel_fault(struct vvp_fault_io *cfio) { struct vm_fault *vmf = cfio->ft_vmf; - cfio->ft_flags = filemap_fault(cfio->ft_vma, vmf); + cfio->ft_flags = filemap_fault(vmf); cfio->ft_flags_valid = 1; if (vmf->page) { diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec.c b/drivers/staging/lustre/lustre/ptlrpc/sec.c index e860df7c45a2..366f2ce20f5e 100644 --- a/drivers/staging/lustre/lustre/ptlrpc/sec.c +++ b/drivers/staging/lustre/lustre/ptlrpc/sec.c @@ -38,7 +38,9 @@ #include "../../include/linux/libcfs/libcfs.h" #include <linux/crypto.h> +#include <linux/cred.h> #include <linux/key.h> +#include <linux/sched/task.h> #include "../include/obd.h" #include "../include/obd_class.h" diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index c75ae43095ba..c6c3de94adaa 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -36,7 +36,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/fs.h> diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 34aac3e2eb87..e4a533b6beb3 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -42,7 +42,7 @@ #include <linux/module.h> #include <linux/kmod.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/fs.h> #include <linux/poll.h> #include <linux/string.h> diff --git a/drivers/staging/rtl8188eu/include/osdep_service.h b/drivers/staging/rtl8188eu/include/osdep_service.h index ee3f5ee06529..9e390648d93e 100644 --- a/drivers/staging/rtl8188eu/include/osdep_service.h +++ b/drivers/staging/rtl8188eu/include/osdep_service.h @@ -37,7 +37,7 @@ #include <linux/io.h> #include <linux/mutex.h> #include <linux/sem.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/etherdevice.h> #include <linux/wireless.h> #include <net/iw_handler.h> diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211.h b/drivers/staging/rtl8192u/ieee80211/ieee80211.h index 0d247058bce4..097147071df0 100644 --- a/drivers/staging/rtl8192u/ieee80211/ieee80211.h +++ b/drivers/staging/rtl8192u/ieee80211/ieee80211.h @@ -1953,7 +1953,7 @@ struct ieee80211_device { /* ask to the driver to retune the radio . * This function can sleep. the driver should ensure - * the radio has been swithced before return. + * the radio has been switched before return. */ void (*set_chan)(struct net_device *dev, short ch); @@ -1964,7 +1964,7 @@ struct ieee80211_device { * The syncro version is similar to the start_scan but * does not return until all channels has been scanned. * this is called in user context and should sleep, - * it is called in a work_queue when swithcing to ad-hoc mode + * it is called in a work_queue when switching to ad-hoc mode * or in behalf of iwlist scan when the card is associated * and root user ask for a scan. * the function stop_scan should stop both the syncro and diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c index 1bff0e91cc0c..0ea90aae4283 100644 --- a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c +++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c @@ -2364,7 +2364,7 @@ static void ieee80211_start_ibss_wq(struct work_struct *work) // if((IS_DOT11D_ENABLE(ieee)) && (ieee->state == IEEE80211_NOLINK)) if (ieee->state == IEEE80211_NOLINK) ieee->current_network.channel = 6; - /* if not then the state is not linked. Maybe the user swithced to + /* if not then the state is not linked. Maybe the user switched to * ad-hoc mode just after being in monitor mode, or just after * being very few time in managed mode (so the card have had no * time to scan all the chans..) or we have just run up the iface diff --git a/drivers/staging/rtl8712/osdep_service.h b/drivers/staging/rtl8712/osdep_service.h index b8a170978434..5d33020554cd 100644 --- a/drivers/staging/rtl8712/osdep_service.h +++ b/drivers/staging/rtl8712/osdep_service.h @@ -33,7 +33,7 @@ #include <linux/interrupt.h> #include <linux/semaphore.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/sem.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> diff --git a/drivers/staging/rtl8712/rtl8712_cmd.c b/drivers/staging/rtl8712/rtl8712_cmd.c index f19b6b27aa71..5346c657485d 100644 --- a/drivers/staging/rtl8712/rtl8712_cmd.c +++ b/drivers/staging/rtl8712/rtl8712_cmd.c @@ -32,6 +32,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> +#include <linux/sched/signal.h> #include <linux/module.h> #include <linux/kref.h> #include <linux/netdevice.h> diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c index ff68a384f9c2..d2ff0afd685a 100644 --- a/drivers/staging/speakup/speakup_soft.c +++ b/drivers/staging/speakup/speakup_soft.c @@ -22,7 +22,7 @@ #include <linux/unistd.h> #include <linux/miscdevice.h> /* for misc_register, and SYNTH_MINOR */ #include <linux/poll.h> /* for poll_wait() */ -#include <linux/sched.h> /* schedule(), signal_pending(), TASK_INTERRUPTIBLE */ +#include <linux/sched/signal.h> /* schedule(), signal_pending(), TASK_INTERRUPTIBLE */ #include "spk_priv.h" #include "speakup.h" diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 1dc8627e65b0..8a0d214f6e9b 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -34,6 +34,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/sched/signal.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/cdev.h> @@ -1875,8 +1876,8 @@ vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) ** ** VC_RESUME_IDLE - Initialise the resume completion at the same time. The ** resume completion is in it's 'done' state whenever -** videcore is running. Therfore, the VC_RESUME_IDLE state -** implies that videocore is suspended. +** videcore is running. Therefore, the VC_RESUME_IDLE +** state implies that videocore is suspended. ** Hence, any thread which needs to wait until videocore is ** running can wait on this completion - it will only block ** if videocore is suspended. diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h index 4055d4bf9f74..e63964f5a18a 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h @@ -47,7 +47,7 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/random.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/ctype.h> #include <linux/uaccess.h> #include <linux/time.h> /* for time_t */ diff --git a/drivers/staging/wilc1000/linux_wlan.c b/drivers/staging/wilc1000/linux_wlan.c index 9ab43935869e..2eebc6215cac 100644 --- a/drivers/staging/wilc1000/linux_wlan.c +++ b/drivers/staging/wilc1000/linux_wlan.c @@ -213,7 +213,7 @@ static void deinit_irq(struct net_device *dev) vif = netdev_priv(dev); wilc = vif->wilc; - /* Deintialize IRQ */ + /* Deinitialize IRQ */ if (wilc->dev_irq_num) { free_irq(wilc->dev_irq_num, wilc); gpio_free(wilc->gpio); diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c index f7ce47cac2aa..7961d1c56847 100644 --- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c +++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c @@ -2357,7 +2357,7 @@ int wilc_deinit_host_int(struct net_device *net) del_timer_sync(&wilc_during_ip_timer); if (s32Error) - netdev_err(net, "Error while deintializing host interface\n"); + netdev_err(net, "Error while deinitializing host interface\n"); return s32Error; } diff --git a/drivers/target/iscsi/cxgbit/cxgbit_cm.c b/drivers/target/iscsi/cxgbit/cxgbit_cm.c index 2fb1bf1a26c5..37a05185dcbe 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_cm.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_cm.c @@ -872,7 +872,8 @@ cxgbit_offload_init(struct cxgbit_sock *csk, int iptype, __u8 *peer_ip, goto out; csk->mtu = ndev->mtu; csk->tx_chan = cxgb4_port_chan(ndev); - csk->smac_idx = (cxgb4_port_viid(ndev) & 0x7F) << 1; + csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi.adapter_type, + cxgb4_port_viid(ndev)); step = cdev->lldi.ntxq / cdev->lldi.nchan; csk->txq_idx = cxgb4_port_idx(ndev) * step; @@ -907,7 +908,8 @@ cxgbit_offload_init(struct cxgbit_sock *csk, int iptype, __u8 *peer_ip, port_id = cxgb4_port_idx(ndev); csk->mtu = dst_mtu(dst); csk->tx_chan = cxgb4_port_chan(ndev); - csk->smac_idx = (cxgb4_port_viid(ndev) & 0x7F) << 1; + csk->smac_idx = cxgb4_tp_smt_idx(cdev->lldi.adapter_type, + cxgb4_port_viid(ndev)); step = cdev->lldi.ntxq / cdev->lldi.nports; csk->txq_idx = (port_id * step) + @@ -1066,6 +1068,7 @@ cxgbit_pass_accept_rpl(struct cxgbit_sock *csk, struct cpl_pass_accept_req *req) struct sk_buff *skb; const struct tcphdr *tcph; struct cpl_t5_pass_accept_rpl *rpl5; + struct cxgb4_lld_info *lldi = &csk->com.cdev->lldi; unsigned int len = roundup(sizeof(*rpl5), 16); unsigned int mtu_idx; u64 opt0; @@ -1111,6 +1114,9 @@ cxgbit_pass_accept_rpl(struct cxgbit_sock *csk, struct cpl_pass_accept_req *req) opt2 = RX_CHANNEL_V(0) | RSS_QUEUE_VALID_F | RSS_QUEUE_V(csk->rss_qid); + if (!is_t5(lldi->adapter_type)) + opt2 |= RX_FC_DISABLE_F; + if (req->tcpopt.tstamp) opt2 |= TSTAMPS_EN_F; if (req->tcpopt.sack) @@ -1119,8 +1125,13 @@ cxgbit_pass_accept_rpl(struct cxgbit_sock *csk, struct cpl_pass_accept_req *req) opt2 |= WND_SCALE_EN_F; hlen = ntohl(req->hdr_len); - tcph = (const void *)(req + 1) + ETH_HDR_LEN_G(hlen) + - IP_HDR_LEN_G(hlen); + + if (is_t5(lldi->adapter_type)) + tcph = (struct tcphdr *)((u8 *)(req + 1) + + ETH_HDR_LEN_G(hlen) + IP_HDR_LEN_G(hlen)); + else + tcph = (struct tcphdr *)((u8 *)(req + 1) + + T6_ETH_HDR_LEN_G(hlen) + T6_IP_HDR_LEN_G(hlen)); if (tcph->ece && tcph->cwr) opt2 |= CCTRL_ECN_V(1); @@ -1726,7 +1737,7 @@ static bool cxgbit_credit_err(const struct cxgbit_sock *csk) } while (skb) { - credit += skb->csum; + credit += (__force u32)skb->csum; skb = cxgbit_skcb_tx_wr_next(skb); } @@ -1753,6 +1764,7 @@ static void cxgbit_fw4_ack(struct cxgbit_sock *csk, struct sk_buff *skb) while (credits) { struct sk_buff *p = cxgbit_sock_peek_wr(csk); + const u32 csum = (__force u32)p->csum; if (unlikely(!p)) { pr_err("csk 0x%p,%u, cr %u,%u+%u, empty.\n", @@ -1761,17 +1773,17 @@ static void cxgbit_fw4_ack(struct cxgbit_sock *csk, struct sk_buff *skb) break; } - if (unlikely(credits < p->csum)) { + if (unlikely(credits < csum)) { pr_warn("csk 0x%p,%u, cr %u,%u+%u, < %u.\n", csk, csk->tid, credits, csk->wr_cred, csk->wr_una_cred, - p->csum); - p->csum -= credits; + csum); + p->csum = (__force __wsum)(csum - credits); break; } cxgbit_sock_dequeue_wr(csk); - credits -= p->csum; + credits -= csum; kfree_skb(p); } diff --git a/drivers/target/iscsi/cxgbit/cxgbit_lro.h b/drivers/target/iscsi/cxgbit/cxgbit_lro.h index 28c11bd1b930..dcaed3a1d23f 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_lro.h +++ b/drivers/target/iscsi/cxgbit/cxgbit_lro.h @@ -31,8 +31,9 @@ enum cxgbit_pducb_flags { PDUCBF_RX_DATA = (1 << 1), /* received pdu payload */ PDUCBF_RX_STATUS = (1 << 2), /* received ddp status */ PDUCBF_RX_DATA_DDPD = (1 << 3), /* pdu payload ddp'd */ - PDUCBF_RX_HCRC_ERR = (1 << 4), /* header digest error */ - PDUCBF_RX_DCRC_ERR = (1 << 5), /* data digest error */ + PDUCBF_RX_DDP_CMP = (1 << 4), /* ddp completion */ + PDUCBF_RX_HCRC_ERR = (1 << 5), /* header digest error */ + PDUCBF_RX_DCRC_ERR = (1 << 6), /* data digest error */ }; struct cxgbit_lro_pdu_cb { diff --git a/drivers/target/iscsi/cxgbit/cxgbit_main.c b/drivers/target/iscsi/cxgbit/cxgbit_main.c index 96eedfc49c94..4fd775ace541 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_main.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_main.c @@ -165,29 +165,24 @@ static int cxgbit_uld_state_change(void *handle, enum cxgb4_state state) } static void -cxgbit_proc_ddp_status(unsigned int tid, struct cpl_rx_data_ddp *cpl, - struct cxgbit_lro_pdu_cb *pdu_cb) +cxgbit_process_ddpvld(struct cxgbit_sock *csk, struct cxgbit_lro_pdu_cb *pdu_cb, + u32 ddpvld) { - unsigned int status = ntohl(cpl->ddpvld); - pdu_cb->flags |= PDUCBF_RX_STATUS; - pdu_cb->ddigest = ntohl(cpl->ulp_crc); - pdu_cb->pdulen = ntohs(cpl->len); - - if (status & (1 << CPL_RX_ISCSI_DDP_STATUS_HCRC_SHIFT)) { - pr_info("tid 0x%x, status 0x%x, hcrc bad.\n", tid, status); + if (ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_HCRC_SHIFT)) { + pr_info("tid 0x%x, status 0x%x, hcrc bad.\n", csk->tid, ddpvld); pdu_cb->flags |= PDUCBF_RX_HCRC_ERR; } - if (status & (1 << CPL_RX_ISCSI_DDP_STATUS_DCRC_SHIFT)) { - pr_info("tid 0x%x, status 0x%x, dcrc bad.\n", tid, status); + if (ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_DCRC_SHIFT)) { + pr_info("tid 0x%x, status 0x%x, dcrc bad.\n", csk->tid, ddpvld); pdu_cb->flags |= PDUCBF_RX_DCRC_ERR; } - if (status & (1 << CPL_RX_ISCSI_DDP_STATUS_PAD_SHIFT)) - pr_info("tid 0x%x, status 0x%x, pad bad.\n", tid, status); + if (ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_PAD_SHIFT)) + pr_info("tid 0x%x, status 0x%x, pad bad.\n", csk->tid, ddpvld); - if ((status & (1 << CPL_RX_ISCSI_DDP_STATUS_DDP_SHIFT)) && + if ((ddpvld & (1 << CPL_RX_ISCSI_DDP_STATUS_DDP_SHIFT)) && (!(pdu_cb->flags & PDUCBF_RX_DATA))) { pdu_cb->flags |= PDUCBF_RX_DATA_DDPD; } @@ -201,13 +196,17 @@ cxgbit_lro_add_packet_rsp(struct sk_buff *skb, u8 op, const __be64 *rsp) lro_cb->pdu_idx); struct cpl_rx_iscsi_ddp *cpl = (struct cpl_rx_iscsi_ddp *)(rsp + 1); - cxgbit_proc_ddp_status(lro_cb->csk->tid, cpl, pdu_cb); + cxgbit_process_ddpvld(lro_cb->csk, pdu_cb, be32_to_cpu(cpl->ddpvld)); + + pdu_cb->flags |= PDUCBF_RX_STATUS; + pdu_cb->ddigest = ntohl(cpl->ulp_crc); + pdu_cb->pdulen = ntohs(cpl->len); if (pdu_cb->flags & PDUCBF_RX_HDR) pdu_cb->complete = true; - lro_cb->complete = true; lro_cb->pdu_totallen += pdu_cb->pdulen; + lro_cb->complete = true; lro_cb->pdu_idx++; } @@ -257,7 +256,7 @@ cxgbit_lro_add_packet_gl(struct sk_buff *skb, u8 op, const struct pkt_gl *gl) cxgbit_skcb_flags(skb) = 0; lro_cb->complete = false; - } else { + } else if (op == CPL_ISCSI_DATA) { struct cpl_iscsi_data *cpl = (struct cpl_iscsi_data *)gl->va; offset = sizeof(struct cpl_iscsi_data); @@ -267,6 +266,36 @@ cxgbit_lro_add_packet_gl(struct sk_buff *skb, u8 op, const struct pkt_gl *gl) pdu_cb->doffset = lro_cb->offset; pdu_cb->nr_dfrags = gl->nfrags; pdu_cb->dfrag_idx = skb_shinfo(skb)->nr_frags; + lro_cb->complete = false; + } else { + struct cpl_rx_iscsi_cmp *cpl; + + cpl = (struct cpl_rx_iscsi_cmp *)gl->va; + offset = sizeof(struct cpl_rx_iscsi_cmp); + pdu_cb->flags |= (PDUCBF_RX_HDR | PDUCBF_RX_STATUS); + len = be16_to_cpu(cpl->len); + pdu_cb->hdr = gl->va + offset; + pdu_cb->hlen = len; + pdu_cb->hfrag_idx = skb_shinfo(skb)->nr_frags; + pdu_cb->ddigest = be32_to_cpu(cpl->ulp_crc); + pdu_cb->pdulen = ntohs(cpl->len); + + if (unlikely(gl->nfrags > 1)) + cxgbit_skcb_flags(skb) = 0; + + cxgbit_process_ddpvld(lro_cb->csk, pdu_cb, + be32_to_cpu(cpl->ddpvld)); + + if (pdu_cb->flags & PDUCBF_RX_DATA_DDPD) { + pdu_cb->flags |= PDUCBF_RX_DDP_CMP; + pdu_cb->complete = true; + } else if (pdu_cb->flags & PDUCBF_RX_DATA) { + pdu_cb->complete = true; + } + + lro_cb->pdu_totallen += pdu_cb->hlen + pdu_cb->dlen; + lro_cb->complete = true; + lro_cb->pdu_idx++; } cxgbit_copy_frags(skb, gl, offset); @@ -413,6 +442,7 @@ cxgbit_uld_lro_rx_handler(void *hndl, const __be64 *rsp, switch (op) { case CPL_ISCSI_HDR: case CPL_ISCSI_DATA: + case CPL_RX_ISCSI_CMP: case CPL_RX_ISCSI_DDP: case CPL_FW4_ACK: lro_flush = false; @@ -454,12 +484,13 @@ cxgbit_uld_lro_rx_handler(void *hndl, const __be64 *rsp, if (unlikely(op != *(u8 *)gl->va)) { pr_info("? FL 0x%p,RSS%#llx,FL %#llx,len %u.\n", gl->va, be64_to_cpu(*rsp), - be64_to_cpu(*(u64 *)gl->va), + get_unaligned_be64(gl->va), gl->tot_len); return 0; } - if (op == CPL_ISCSI_HDR || op == CPL_ISCSI_DATA) { + if ((op == CPL_ISCSI_HDR) || (op == CPL_ISCSI_DATA) || + (op == CPL_RX_ISCSI_CMP)) { if (!cxgbit_lro_receive(csk, op, rsp, gl, lro_mgr, napi)) return 0; diff --git a/drivers/target/iscsi/cxgbit/cxgbit_target.c b/drivers/target/iscsi/cxgbit/cxgbit_target.c index 8bcb9b71f764..bdcc8b4c522a 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_target.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_target.c @@ -8,6 +8,8 @@ #include <linux/workqueue.h> #include <linux/kthread.h> +#include <linux/sched/signal.h> + #include <asm/unaligned.h> #include <net/tcp.h> #include <target/target_core_base.h> @@ -162,12 +164,14 @@ cxgbit_tx_data_wr(struct cxgbit_sock *csk, struct sk_buff *skb, u32 dlen, u32 len, u32 credits, u32 compl) { struct fw_ofld_tx_data_wr *req; + const struct cxgb4_lld_info *lldi = &csk->com.cdev->lldi; u32 submode = cxgbit_skcb_submode(skb); u32 wr_ulp_mode = 0; u32 hdr_size = sizeof(*req); u32 opcode = FW_OFLD_TX_DATA_WR; u32 immlen = 0; - u32 force = TX_FORCE_V(!submode); + u32 force = is_t5(lldi->adapter_type) ? TX_FORCE_V(!submode) : + T6_TX_FORCE_F; if (cxgbit_skcb_flags(skb) & SKCBF_TX_ISO) { opcode = FW_ISCSI_TX_DATA_WR; @@ -243,7 +247,7 @@ void cxgbit_push_tx_frames(struct cxgbit_sock *csk) } __skb_unlink(skb, &csk->txq); set_wr_txq(skb, CPL_PRIORITY_DATA, csk->txq_idx); - skb->csum = credits_needed + flowclen16; + skb->csum = (__force __wsum)(credits_needed + flowclen16); csk->wr_cred -= credits_needed; csk->wr_una_cred += credits_needed; @@ -651,26 +655,6 @@ static int cxgbit_set_iso_npdu(struct cxgbit_sock *csk) u32 max_npdu, max_iso_npdu; if (conn->login->leading_connection) { - param = iscsi_find_param_from_key(DATASEQUENCEINORDER, - conn->param_list); - if (!param) { - pr_err("param not found key %s\n", DATASEQUENCEINORDER); - return -1; - } - - if (strcmp(param->value, YES)) - return 0; - - param = iscsi_find_param_from_key(DATAPDUINORDER, - conn->param_list); - if (!param) { - pr_err("param not found key %s\n", DATAPDUINORDER); - return -1; - } - - if (strcmp(param->value, YES)) - return 0; - param = iscsi_find_param_from_key(MAXBURSTLENGTH, conn->param_list); if (!param) { @@ -681,11 +665,6 @@ static int cxgbit_set_iso_npdu(struct cxgbit_sock *csk) if (kstrtou32(param->value, 0, &mbl) < 0) return -1; } else { - if (!conn->sess->sess_ops->DataSequenceInOrder) - return 0; - if (!conn->sess->sess_ops->DataPDUInOrder) - return 0; - mbl = conn->sess->sess_ops->MaxBurstLength; } @@ -704,6 +683,53 @@ static int cxgbit_set_iso_npdu(struct cxgbit_sock *csk) return 0; } +/* + * cxgbit_seq_pdu_inorder() + * @csk: pointer to cxgbit socket structure + * + * This function checks whether data sequence and data + * pdu are in order. + * + * Return: returns -1 on error, 0 if data sequence and + * data pdu are in order, 1 if data sequence or data pdu + * is not in order. + */ +static int cxgbit_seq_pdu_inorder(struct cxgbit_sock *csk) +{ + struct iscsi_conn *conn = csk->conn; + struct iscsi_param *param; + + if (conn->login->leading_connection) { + param = iscsi_find_param_from_key(DATASEQUENCEINORDER, + conn->param_list); + if (!param) { + pr_err("param not found key %s\n", DATASEQUENCEINORDER); + return -1; + } + + if (strcmp(param->value, YES)) + return 1; + + param = iscsi_find_param_from_key(DATAPDUINORDER, + conn->param_list); + if (!param) { + pr_err("param not found key %s\n", DATAPDUINORDER); + return -1; + } + + if (strcmp(param->value, YES)) + return 1; + + } else { + if (!conn->sess->sess_ops->DataSequenceInOrder) + return 1; + if (!conn->sess->sess_ops->DataPDUInOrder) + return 1; + } + + return 0; +} + static int cxgbit_set_params(struct iscsi_conn *conn) { struct cxgbit_sock *csk = conn->context; @@ -730,11 +756,24 @@ static int cxgbit_set_params(struct iscsi_conn *conn) } if (!erl) { + int ret; + + ret = cxgbit_seq_pdu_inorder(csk); + if (ret < 0) { + return -1; + } else if (ret > 0) { + if (is_t5(cdev->lldi.adapter_type)) + goto enable_ddp; + else + goto enable_digest; + } + if (test_bit(CDEV_ISO_ENABLE, &cdev->flags)) { if (cxgbit_set_iso_npdu(csk)) return -1; } +enable_ddp: if (test_bit(CDEV_DDP_ENABLE, &cdev->flags)) { if (cxgbit_setup_conn_pgidx(csk, ppm->tformat.pgsz_idx_dflt)) @@ -743,6 +782,7 @@ static int cxgbit_set_params(struct iscsi_conn *conn) } } +enable_digest: if (cxgbit_set_digest(csk)) return -1; @@ -983,11 +1023,36 @@ static int cxgbit_handle_iscsi_dataout(struct cxgbit_sock *csk) int rc, sg_nents, sg_off; bool dcrc_err = false; - rc = iscsit_check_dataout_hdr(conn, (unsigned char *)hdr, &cmd); - if (rc < 0) - return rc; - else if (!cmd) - return 0; + if (pdu_cb->flags & PDUCBF_RX_DDP_CMP) { + u32 offset = be32_to_cpu(hdr->offset); + u32 ddp_data_len; + u32 payload_length = ntoh24(hdr->dlength); + bool success = false; + + cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt, 0); + if (!cmd) + return 0; + + ddp_data_len = offset - cmd->write_data_done; + atomic_long_add(ddp_data_len, &conn->sess->rx_data_octets); + + cmd->write_data_done = offset; + cmd->next_burst_len = ddp_data_len; + cmd->data_sn = be32_to_cpu(hdr->datasn); + + rc = __iscsit_check_dataout_hdr(conn, (unsigned char *)hdr, + cmd, payload_length, &success); + if (rc < 0) + return rc; + else if (!success) + return 0; + } else { + rc = iscsit_check_dataout_hdr(conn, (unsigned char *)hdr, &cmd); + if (rc < 0) + return rc; + else if (!cmd) + return 0; + } if (pdu_cb->flags & PDUCBF_RX_DCRC_ERR) { pr_err("ITT: 0x%08x, Offset: %u, Length: %u," @@ -1351,6 +1416,9 @@ static void cxgbit_lro_hskb_reset(struct cxgbit_sock *csk) for (i = 0; i < ssi->nr_frags; i++) put_page(skb_frag_page(&ssi->frags[i])); ssi->nr_frags = 0; + skb->data_len = 0; + skb->truesize -= skb->len; + skb->len = 0; } static void @@ -1364,39 +1432,42 @@ cxgbit_lro_skb_merge(struct cxgbit_sock *csk, struct sk_buff *skb, u8 pdu_idx) unsigned int len = 0; if (pdu_cb->flags & PDUCBF_RX_HDR) { - hpdu_cb->flags = pdu_cb->flags; + u8 hfrag_idx = hssi->nr_frags; + + hpdu_cb->flags |= pdu_cb->flags; hpdu_cb->seq = pdu_cb->seq; hpdu_cb->hdr = pdu_cb->hdr; hpdu_cb->hlen = pdu_cb->hlen; - memcpy(&hssi->frags[0], &ssi->frags[pdu_cb->hfrag_idx], + memcpy(&hssi->frags[hfrag_idx], &ssi->frags[pdu_cb->hfrag_idx], sizeof(skb_frag_t)); - get_page(skb_frag_page(&hssi->frags[0])); - hssi->nr_frags = 1; - hpdu_cb->frags = 1; - hpdu_cb->hfrag_idx = 0; + get_page(skb_frag_page(&hssi->frags[hfrag_idx])); + hssi->nr_frags++; + hpdu_cb->frags++; + hpdu_cb->hfrag_idx = hfrag_idx; - len = hssi->frags[0].size; - hskb->len = len; - hskb->data_len = len; - hskb->truesize = len; + len = hssi->frags[hfrag_idx].size; + hskb->len += len; + hskb->data_len += len; + hskb->truesize += len; } if (pdu_cb->flags & PDUCBF_RX_DATA) { - u8 hfrag_idx = 1, i; + u8 dfrag_idx = hssi->nr_frags, i; hpdu_cb->flags |= pdu_cb->flags; + hpdu_cb->dfrag_idx = dfrag_idx; len = 0; - for (i = 0; i < pdu_cb->nr_dfrags; hfrag_idx++, i++) { - memcpy(&hssi->frags[hfrag_idx], + for (i = 0; i < pdu_cb->nr_dfrags; dfrag_idx++, i++) { + memcpy(&hssi->frags[dfrag_idx], &ssi->frags[pdu_cb->dfrag_idx + i], sizeof(skb_frag_t)); - get_page(skb_frag_page(&hssi->frags[hfrag_idx])); + get_page(skb_frag_page(&hssi->frags[dfrag_idx])); - len += hssi->frags[hfrag_idx].size; + len += hssi->frags[dfrag_idx].size; hssi->nr_frags++; hpdu_cb->frags++; @@ -1405,7 +1476,6 @@ cxgbit_lro_skb_merge(struct cxgbit_sock *csk, struct sk_buff *skb, u8 pdu_idx) hpdu_cb->dlen = pdu_cb->dlen; hpdu_cb->doffset = hpdu_cb->hlen; hpdu_cb->nr_dfrags = pdu_cb->nr_dfrags; - hpdu_cb->dfrag_idx = 1; hskb->len += len; hskb->data_len += len; hskb->truesize += len; @@ -1490,10 +1560,15 @@ static int cxgbit_rx_lro_skb(struct cxgbit_sock *csk, struct sk_buff *skb) static int cxgbit_rx_skb(struct cxgbit_sock *csk, struct sk_buff *skb) { + struct cxgb4_lld_info *lldi = &csk->com.cdev->lldi; int ret = -1; - if (likely(cxgbit_skcb_flags(skb) & SKCBF_RX_LRO)) - ret = cxgbit_rx_lro_skb(csk, skb); + if (likely(cxgbit_skcb_flags(skb) & SKCBF_RX_LRO)) { + if (is_t5(lldi->adapter_type)) + ret = cxgbit_rx_lro_skb(csk, skb); + else + ret = cxgbit_process_lro_skb(csk, skb); + } __kfree_skb(skb); return ret; diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index da2c73a255de..a91802432f2f 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -24,6 +24,7 @@ #include <linux/vmalloc.h> #include <linux/idr.h> #include <linux/delay.h> +#include <linux/sched/signal.h> #include <asm/unaligned.h> #include <net/ipv6.h> #include <scsi/scsi_proto.h> @@ -1431,36 +1432,17 @@ static void iscsit_do_crypto_hash_buf( } int -iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, - struct iscsi_cmd **out_cmd) +__iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf, + struct iscsi_cmd *cmd, u32 payload_length, + bool *success) { - struct iscsi_data *hdr = (struct iscsi_data *)buf; - struct iscsi_cmd *cmd = NULL; + struct iscsi_data *hdr = buf; struct se_cmd *se_cmd; - u32 payload_length = ntoh24(hdr->dlength); int rc; - if (!payload_length) { - pr_warn("DataOUT payload is ZERO, ignoring.\n"); - return 0; - } - /* iSCSI write */ atomic_long_add(payload_length, &conn->sess->rx_data_octets); - if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) { - pr_err("DataSegmentLength: %u is greater than" - " MaxXmitDataSegmentLength: %u\n", payload_length, - conn->conn_ops->MaxXmitDataSegmentLength); - return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, - buf); - } - - cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt, - payload_length); - if (!cmd) - return 0; - pr_debug("Got DataOut ITT: 0x%08x, TTT: 0x%08x," " DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n", hdr->itt, hdr->ttt, hdr->datasn, ntohl(hdr->offset), @@ -1545,7 +1527,7 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, } } /* - * Preform DataSN, DataSequenceInOrder, DataPDUInOrder, and + * Perform DataSN, DataSequenceInOrder, DataPDUInOrder, and * within-command recovery checks before receiving the payload. */ rc = iscsit_check_pre_dataout(cmd, buf); @@ -1553,10 +1535,44 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, return 0; else if (rc == DATAOUT_CANNOT_RECOVER) return -1; - - *out_cmd = cmd; + *success = true; return 0; } +EXPORT_SYMBOL(__iscsit_check_dataout_hdr); + +int +iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf, + struct iscsi_cmd **out_cmd) +{ + struct iscsi_data *hdr = buf; + struct iscsi_cmd *cmd; + u32 payload_length = ntoh24(hdr->dlength); + int rc; + bool success = false; + + if (!payload_length) { + pr_warn_ratelimited("DataOUT payload is ZERO, ignoring.\n"); + return 0; + } + + if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) { + pr_err_ratelimited("DataSegmentLength: %u is greater than" + " MaxXmitDataSegmentLength: %u\n", payload_length, + conn->conn_ops->MaxXmitDataSegmentLength); + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, buf); + } + + cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt, payload_length); + if (!cmd) + return 0; + + rc = __iscsit_check_dataout_hdr(conn, buf, cmd, payload_length, &success); + + if (success) + *out_cmd = cmd; + + return rc; +} EXPORT_SYMBOL(iscsit_check_dataout_hdr); static int @@ -1920,6 +1936,28 @@ out: return ret; } +static enum tcm_tmreq_table iscsit_convert_tmf(u8 iscsi_tmf) +{ + switch (iscsi_tmf) { + case ISCSI_TM_FUNC_ABORT_TASK: + return TMR_ABORT_TASK; + case ISCSI_TM_FUNC_ABORT_TASK_SET: + return TMR_ABORT_TASK_SET; + case ISCSI_TM_FUNC_CLEAR_ACA: + return TMR_CLEAR_ACA; + case ISCSI_TM_FUNC_CLEAR_TASK_SET: + return TMR_CLEAR_TASK_SET; + case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: + return TMR_LUN_RESET; + case ISCSI_TM_FUNC_TARGET_WARM_RESET: + return TMR_TARGET_WARM_RESET; + case ISCSI_TM_FUNC_TARGET_COLD_RESET: + return TMR_TARGET_COLD_RESET; + default: + return TMR_UNKNOWN; + } +} + int iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, unsigned char *buf) @@ -1929,7 +1967,7 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct iscsi_tm *hdr; int out_of_order_cmdsn = 0, ret; bool sess_ref = false; - u8 function; + u8 function, tcm_function = TMR_UNKNOWN; hdr = (struct iscsi_tm *) buf; hdr->flags &= ~ISCSI_FLAG_CMD_FINAL; @@ -1975,54 +2013,27 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * LIO-Target $FABRIC_MOD */ if (function != ISCSI_TM_FUNC_TASK_REASSIGN) { - - u8 tcm_function; - int ret; - transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops, conn->sess->se_sess, 0, DMA_NONE, TCM_SIMPLE_TAG, cmd->sense_buffer + 2); target_get_sess_cmd(&cmd->se_cmd, true); sess_ref = true; - - switch (function) { - case ISCSI_TM_FUNC_ABORT_TASK: - tcm_function = TMR_ABORT_TASK; - break; - case ISCSI_TM_FUNC_ABORT_TASK_SET: - tcm_function = TMR_ABORT_TASK_SET; - break; - case ISCSI_TM_FUNC_CLEAR_ACA: - tcm_function = TMR_CLEAR_ACA; - break; - case ISCSI_TM_FUNC_CLEAR_TASK_SET: - tcm_function = TMR_CLEAR_TASK_SET; - break; - case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: - tcm_function = TMR_LUN_RESET; - break; - case ISCSI_TM_FUNC_TARGET_WARM_RESET: - tcm_function = TMR_TARGET_WARM_RESET; - break; - case ISCSI_TM_FUNC_TARGET_COLD_RESET: - tcm_function = TMR_TARGET_COLD_RESET; - break; - default: + tcm_function = iscsit_convert_tmf(function); + if (tcm_function == TMR_UNKNOWN) { pr_err("Unknown iSCSI TMR Function:" " 0x%02x\n", function); return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } - - ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req, - tcm_function, GFP_KERNEL); - if (ret < 0) - return iscsit_add_reject_cmd(cmd, + } + ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req, tcm_function, + GFP_KERNEL); + if (ret < 0) + return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); - cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req; - } + cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req; cmd->iscsi_opcode = ISCSI_OP_SCSI_TMFUNC; cmd->i_state = ISTATE_SEND_TASKMGTRSP; @@ -4136,7 +4147,7 @@ int iscsit_close_connection( /* * During Connection recovery drop unacknowledged out of order * commands for this connection, and prepare the other commands - * for realligence. + * for reallegiance. * * During normal operation clear the out of order commands (but * do not free the struct iscsi_ooo_cmdsn's) and release all @@ -4144,7 +4155,7 @@ int iscsit_close_connection( */ if (atomic_read(&conn->connection_recovery)) { iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(conn); - iscsit_prepare_cmds_for_realligance(conn); + iscsit_prepare_cmds_for_reallegiance(conn); } else { iscsit_clear_ooo_cmdsns_for_conn(conn); iscsit_release_commands_from_conn(conn); diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index b54e72c7ab0f..9a96e17bf7cd 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -17,6 +17,8 @@ * GNU General Public License for more details. ******************************************************************************/ +#include <linux/sched/signal.h> + #include <scsi/iscsi_proto.h> #include <target/target_core_base.h> #include <target/target_core_fabric.h> @@ -44,10 +46,8 @@ void iscsit_set_dataout_sequence_values( */ if (cmd->unsolicited_data) { cmd->seq_start_offset = cmd->write_data_done; - cmd->seq_end_offset = (cmd->write_data_done + - ((cmd->se_cmd.data_length > - conn->sess->sess_ops->FirstBurstLength) ? - conn->sess->sess_ops->FirstBurstLength : cmd->se_cmd.data_length)); + cmd->seq_end_offset = min(cmd->se_cmd.data_length, + conn->sess->sess_ops->FirstBurstLength); return; } diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c index faf9ae014b30..8df9c90f3db3 100644 --- a/drivers/target/iscsi/iscsi_target_erl2.c +++ b/drivers/target/iscsi/iscsi_target_erl2.c @@ -312,7 +312,7 @@ int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn) return 0; } -int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) +int iscsit_prepare_cmds_for_reallegiance(struct iscsi_conn *conn) { u32 cmd_count = 0; struct iscsi_cmd *cmd, *cmd_tmp; @@ -347,7 +347,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) && (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) { - pr_debug("Not performing realligence on" + pr_debug("Not performing reallegiance on" " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x," " CID: %hu\n", cmd->iscsi_opcode, cmd->init_task_tag, cmd->cmd_sn, conn->cid); @@ -382,7 +382,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) cmd_count++; pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x," " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for" - " realligence.\n", cmd->iscsi_opcode, + " reallegiance.\n", cmd->iscsi_opcode, cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn, conn->cid); diff --git a/drivers/target/iscsi/iscsi_target_erl2.h b/drivers/target/iscsi/iscsi_target_erl2.h index 7965f1e86506..634d01e13652 100644 --- a/drivers/target/iscsi/iscsi_target_erl2.h +++ b/drivers/target/iscsi/iscsi_target_erl2.h @@ -19,7 +19,7 @@ extern int iscsit_remove_cmd_from_connection_recovery(struct iscsi_cmd *, struct iscsi_session *); extern void iscsit_discard_cr_cmds_by_expstatsn(struct iscsi_conn_recovery *, u32); extern int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *); -extern int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *); +extern int iscsit_prepare_cmds_for_reallegiance(struct iscsi_conn *); extern int iscsit_connection_recovery_transport_reset(struct iscsi_conn *); #endif /*** ISCSI_TARGET_ERL2_H ***/ diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index eab274d17b5c..ad8f3011bdc2 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/kthread.h> +#include <linux/sched/signal.h> #include <linux/idr.h> #include <linux/tcp.h> /* TCP_NODELAY */ #include <net/ipv6.h> /* ipv6_addr_v4mapped() */ @@ -223,7 +224,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn) return 0; pr_debug("%s iSCSI Session SID %u is still active for %s," - " preforming session reinstatement.\n", (sessiontype) ? + " performing session reinstatement.\n", (sessiontype) ? "Discovery" : "Normal", sess->sid, sess->sess_ops->InitiatorName); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 46388c9e08da..7ccc9c1cbfd1 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -19,6 +19,7 @@ #include <linux/ctype.h> #include <linux/kthread.h> #include <linux/slab.h> +#include <linux/sched/signal.h> #include <net/sock.h> #include <scsi/iscsi_proto.h> #include <target/target_core_base.h> @@ -1249,16 +1250,16 @@ int iscsi_target_start_negotiation( { int ret; - if (conn->sock) { - struct sock *sk = conn->sock->sk; + if (conn->sock) { + struct sock *sk = conn->sock->sk; - write_lock_bh(&sk->sk_callback_lock); - set_bit(LOGIN_FLAGS_READY, &conn->login_flags); - write_unlock_bh(&sk->sk_callback_lock); - } + write_lock_bh(&sk->sk_callback_lock); + set_bit(LOGIN_FLAGS_READY, &conn->login_flags); + write_unlock_bh(&sk->sk_callback_lock); + } - ret = iscsi_target_do_login(conn, login); - if (ret < 0) { + ret = iscsi_target_do_login(conn, login); + if (ret < 0) { cancel_delayed_work_sync(&conn->login_work); cancel_delayed_work_sync(&conn->login_cleanup_work); iscsi_target_restore_sock_callbacks(conn); diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c index 3d637055c36f..cb231c907d51 100644 --- a/drivers/target/iscsi/iscsi_target_tmr.c +++ b/drivers/target/iscsi/iscsi_target_tmr.c @@ -440,14 +440,14 @@ static int iscsit_task_reassign_complete( break; default: pr_err("Illegal iSCSI Opcode 0x%02x during" - " command realligence\n", cmd->iscsi_opcode); + " command reallegiance\n", cmd->iscsi_opcode); return -1; } if (ret != 0) return ret; - pr_debug("Completed connection realligence for Opcode: 0x%02x," + pr_debug("Completed connection reallegiance for Opcode: 0x%02x," " ITT: 0x%08x to CID: %hu.\n", cmd->iscsi_opcode, cmd->init_task_tag, conn->cid); diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index b5a1b4ccba12..5041a9c8bdcb 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -417,6 +417,7 @@ struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump( return NULL; } +EXPORT_SYMBOL(iscsit_find_cmd_from_itt_or_dump); struct iscsi_cmd *iscsit_find_cmd_from_ttt( struct iscsi_conn *conn, @@ -1304,39 +1305,6 @@ static int iscsit_do_rx_data( return total_rx; } -static int iscsit_do_tx_data( - struct iscsi_conn *conn, - struct iscsi_data_count *count) -{ - int ret, iov_len; - struct kvec *iov_p; - struct msghdr msg; - - if (!conn || !conn->sock || !conn->conn_ops) - return -1; - - if (count->data_length <= 0) { - pr_err("Data length is: %d\n", count->data_length); - return -1; - } - - memset(&msg, 0, sizeof(struct msghdr)); - - iov_p = count->iov; - iov_len = count->iov_count; - - ret = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len, - count->data_length); - if (ret != count->data_length) { - pr_err("Unexpected ret: %d send data %d\n", - ret, count->data_length); - return -EPIPE; - } - pr_debug("ret: %d, sent data: %d\n", ret, count->data_length); - - return ret; -} - int rx_data( struct iscsi_conn *conn, struct kvec *iov, @@ -1363,45 +1331,35 @@ int tx_data( int iov_count, int data) { - struct iscsi_data_count c; + struct msghdr msg; + int total_tx = 0; if (!conn || !conn->sock || !conn->conn_ops) return -1; - memset(&c, 0, sizeof(struct iscsi_data_count)); - c.iov = iov; - c.iov_count = iov_count; - c.data_length = data; - c.type = ISCSI_TX_DATA; + if (data <= 0) { + pr_err("Data length is: %d\n", data); + return -1; + } - return iscsit_do_tx_data(conn, &c); -} + memset(&msg, 0, sizeof(struct msghdr)); -static bool sockaddr_equal(struct sockaddr_storage *x, struct sockaddr_storage *y) -{ - switch (x->ss_family) { - case AF_INET: { - struct sockaddr_in *sinx = (struct sockaddr_in *)x; - struct sockaddr_in *siny = (struct sockaddr_in *)y; - if (sinx->sin_addr.s_addr != siny->sin_addr.s_addr) - return false; - if (sinx->sin_port != siny->sin_port) - return false; - break; - } - case AF_INET6: { - struct sockaddr_in6 *sinx = (struct sockaddr_in6 *)x; - struct sockaddr_in6 *siny = (struct sockaddr_in6 *)y; - if (!ipv6_addr_equal(&sinx->sin6_addr, &siny->sin6_addr)) - return false; - if (sinx->sin6_port != siny->sin6_port) - return false; - break; - } - default: - return false; + iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, + iov, iov_count, data); + + while (msg_data_left(&msg)) { + int tx_loop = sock_sendmsg(conn->sock, &msg); + if (tx_loop <= 0) { + pr_debug("tx_loop: %d total_tx %d\n", + tx_loop, total_tx); + return tx_loop; + } + total_tx += tx_loop; + pr_debug("tx_loop: %d, total_tx: %d, data: %d\n", + tx_loop, total_tx, data); } - return true; + + return total_tx; } void iscsit_collect_login_stats( @@ -1420,13 +1378,6 @@ void iscsit_collect_login_stats( ls = &tiqn->login_stats; spin_lock(&ls->lock); - if (sockaddr_equal(&conn->login_sockaddr, &ls->last_intr_fail_sockaddr) && - ((get_jiffies_64() - ls->last_fail_time) < 10)) { - /* We already have the failure info for this login */ - spin_unlock(&ls->lock); - return; - } - if (status_class == ISCSI_STATUS_CLS_SUCCESS) ls->accepts++; else if (status_class == ISCSI_STATUS_CLS_REDIRECT) { @@ -1471,10 +1422,10 @@ struct iscsi_tiqn *iscsit_snmp_get_tiqn(struct iscsi_conn *conn) { struct iscsi_portal_group *tpg; - if (!conn || !conn->sess) + if (!conn) return NULL; - tpg = conn->sess->tpg; + tpg = conn->tpg; if (!tpg) return NULL; diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 26929c44d703..c754ae33bf7b 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -78,12 +78,16 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) &deve->read_bytes); se_lun = rcu_dereference(deve->se_lun); + + if (!percpu_ref_tryget_live(&se_lun->lun_ref)) { + se_lun = NULL; + goto out_unlock; + } + se_cmd->se_lun = rcu_dereference(deve->se_lun); se_cmd->pr_res_key = deve->pr_res_key; se_cmd->orig_fe_lun = unpacked_lun; se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; - - percpu_ref_get(&se_lun->lun_ref); se_cmd->lun_ref_active = true; if ((se_cmd->data_direction == DMA_TO_DEVICE) && @@ -97,6 +101,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun) goto ref_dev; } } +out_unlock: rcu_read_unlock(); if (!se_lun) { @@ -163,7 +168,6 @@ int transport_lookup_tmr_lun(struct se_cmd *se_cmd, u64 unpacked_lun) rcu_read_lock(); deve = target_nacl_find_deve(nacl, unpacked_lun); if (deve) { - se_tmr->tmr_lun = rcu_dereference(deve->se_lun); se_cmd->se_lun = rcu_dereference(deve->se_lun); se_lun = rcu_dereference(deve->se_lun); se_cmd->pr_res_key = deve->pr_res_key; @@ -816,6 +820,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) xcopy_lun = &dev->xcopy_lun; rcu_assign_pointer(xcopy_lun->lun_se_dev, dev); init_completion(&xcopy_lun->lun_ref_comp); + init_completion(&xcopy_lun->lun_shutdown_comp); INIT_LIST_HEAD(&xcopy_lun->lun_deve_list); INIT_LIST_HEAD(&xcopy_lun->lun_dev_link); mutex_init(&xcopy_lun->lun_tg_pt_md_mutex); diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index df7b6e95c019..68d8aef7ab78 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -604,7 +604,7 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes spin_lock_irq(&cmd->t_state_lock); cmd->t_state = TRANSPORT_PROCESSING; - cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT; + cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); __target_execute_cmd(cmd, false); diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index 1a39033d2bff..8038255b21e8 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -158,12 +158,28 @@ static ssize_t target_stat_tgt_resets_show(struct config_item *item, atomic_long_read(&to_stat_tgt_dev(item)->num_resets)); } +static ssize_t target_stat_tgt_aborts_complete_show(struct config_item *item, + char *page) +{ + return snprintf(page, PAGE_SIZE, "%lu\n", + atomic_long_read(&to_stat_tgt_dev(item)->aborts_complete)); +} + +static ssize_t target_stat_tgt_aborts_no_task_show(struct config_item *item, + char *page) +{ + return snprintf(page, PAGE_SIZE, "%lu\n", + atomic_long_read(&to_stat_tgt_dev(item)->aborts_no_task)); +} + CONFIGFS_ATTR_RO(target_stat_tgt_, inst); CONFIGFS_ATTR_RO(target_stat_tgt_, indx); CONFIGFS_ATTR_RO(target_stat_tgt_, num_lus); CONFIGFS_ATTR_RO(target_stat_tgt_, status); CONFIGFS_ATTR_RO(target_stat_tgt_, non_access_lus); CONFIGFS_ATTR_RO(target_stat_tgt_, resets); +CONFIGFS_ATTR_RO(target_stat_tgt_, aborts_complete); +CONFIGFS_ATTR_RO(target_stat_tgt_, aborts_no_task); static struct configfs_attribute *target_stat_scsi_tgt_dev_attrs[] = { &target_stat_tgt_attr_inst, @@ -172,6 +188,8 @@ static struct configfs_attribute *target_stat_scsi_tgt_dev_attrs[] = { &target_stat_tgt_attr_status, &target_stat_tgt_attr_non_access_lus, &target_stat_tgt_attr_resets, + &target_stat_tgt_attr_aborts_complete, + &target_stat_tgt_attr_aborts_no_task, NULL, }; @@ -795,16 +813,34 @@ static ssize_t target_stat_transport_dev_name_show(struct config_item *item, return ret; } +static ssize_t target_stat_transport_proto_id_show(struct config_item *item, + char *page) +{ + struct se_lun *lun = to_transport_stat(item); + struct se_device *dev; + struct se_portal_group *tpg = lun->lun_tpg; + ssize_t ret = -ENODEV; + + rcu_read_lock(); + dev = rcu_dereference(lun->lun_se_dev); + if (dev) + ret = snprintf(page, PAGE_SIZE, "%u\n", tpg->proto_id); + rcu_read_unlock(); + return ret; +} + CONFIGFS_ATTR_RO(target_stat_transport_, inst); CONFIGFS_ATTR_RO(target_stat_transport_, device); CONFIGFS_ATTR_RO(target_stat_transport_, indx); CONFIGFS_ATTR_RO(target_stat_transport_, dev_name); +CONFIGFS_ATTR_RO(target_stat_transport_, proto_id); static struct configfs_attribute *target_stat_scsi_transport_attrs[] = { &target_stat_transport_attr_inst, &target_stat_transport_attr_device, &target_stat_transport_attr_indx, &target_stat_transport_attr_dev_name, + &target_stat_transport_attr_proto_id, NULL, }; diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 4f229e711e1c..dce1e1b47316 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -175,10 +175,9 @@ void core_tmr_abort_task( printk("ABORT_TASK: Found referenced %s task_tag: %llu\n", se_cmd->se_tfo->get_fabric_name(), ref_tag); - if (!__target_check_io_state(se_cmd, se_sess, 0)) { - spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); - goto out; - } + if (!__target_check_io_state(se_cmd, se_sess, 0)) + continue; + list_del_init(&se_cmd->se_cmd_list); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); @@ -191,14 +190,15 @@ void core_tmr_abort_task( printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for" " ref_tag: %llu\n", ref_tag); tmr->response = TMR_FUNCTION_COMPLETE; + atomic_long_inc(&dev->aborts_complete); return; } spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); -out: printk("ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST for ref_tag: %lld\n", tmr->ref_task_tag); tmr->response = TMR_TASK_DOES_NOT_EXIST; + atomic_long_inc(&dev->aborts_no_task); } static void core_tmr_drain_tmr_list( @@ -217,13 +217,8 @@ static void core_tmr_drain_tmr_list( * LUN_RESET tmr.. */ spin_lock_irqsave(&dev->se_tmr_lock, flags); + list_del_init(&tmr->tmr_list); list_for_each_entry_safe(tmr_p, tmr_pp, &dev->dev_tmr_list, tmr_list) { - /* - * Allow the received TMR to return with FUNCTION_COMPLETE. - */ - if (tmr_p == tmr) - continue; - cmd = tmr_p->task_cmd; if (!cmd) { pr_err("Unable to locate struct se_cmd for TMR\n"); diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index d99752c6cd60..c0dbfa016575 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -445,9 +445,10 @@ static void core_tpg_lun_ref_release(struct percpu_ref *ref) { struct se_lun *lun = container_of(ref, struct se_lun, lun_ref); - complete(&lun->lun_ref_comp); + complete(&lun->lun_shutdown_comp); } +/* Does not change se_wwn->priv. */ int core_tpg_register( struct se_wwn *se_wwn, struct se_portal_group *se_tpg, @@ -571,6 +572,7 @@ struct se_lun *core_tpg_alloc_lun( lun->lun_link_magic = SE_LUN_LINK_MAGIC; atomic_set(&lun->lun_acl_count, 0); init_completion(&lun->lun_ref_comp); + init_completion(&lun->lun_shutdown_comp); INIT_LIST_HEAD(&lun->lun_deve_list); INIT_LIST_HEAD(&lun->lun_dev_link); atomic_set(&lun->lun_tg_pt_secondary_offline, 0); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 437591bc7c08..434d9d693989 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -593,9 +593,6 @@ static void target_remove_from_state_list(struct se_cmd *cmd) if (!dev) return; - if (cmd->transport_state & CMD_T_BUSY) - return; - spin_lock_irqsave(&dev->execute_task_lock, flags); if (cmd->state_active) { list_del(&cmd->state_list); @@ -604,24 +601,18 @@ static void target_remove_from_state_list(struct se_cmd *cmd) spin_unlock_irqrestore(&dev->execute_task_lock, flags); } -static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists, - bool write_pending) +static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd) { unsigned long flags; - if (remove_from_lists) { - target_remove_from_state_list(cmd); + target_remove_from_state_list(cmd); - /* - * Clear struct se_cmd->se_lun before the handoff to FE. - */ - cmd->se_lun = NULL; - } + /* + * Clear struct se_cmd->se_lun before the handoff to FE. + */ + cmd->se_lun = NULL; spin_lock_irqsave(&cmd->t_state_lock, flags); - if (write_pending) - cmd->t_state = TRANSPORT_WRITE_PENDING; - /* * Determine if frontend context caller is requesting the stopping of * this command for frontend exceptions. @@ -635,31 +626,18 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists, complete_all(&cmd->t_transport_stop_comp); return 1; } - cmd->transport_state &= ~CMD_T_ACTIVE; - if (remove_from_lists) { - /* - * Some fabric modules like tcm_loop can release - * their internally allocated I/O reference now and - * struct se_cmd now. - * - * Fabric modules are expected to return '1' here if the - * se_cmd being passed is released at this point, - * or zero if not being released. - */ - if (cmd->se_tfo->check_stop_free != NULL) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return cmd->se_tfo->check_stop_free(cmd); - } - } - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return 0; -} -static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd) -{ - return transport_cmd_check_stop(cmd, true, false); + /* + * Some fabric modules like tcm_loop can release their internally + * allocated I/O reference and struct se_cmd now. + * + * Fabric modules are expected to return '1' here if the se_cmd being + * passed is released at this point, or zero if not being released. + */ + return cmd->se_tfo->check_stop_free ? cmd->se_tfo->check_stop_free(cmd) + : 0; } static void transport_lun_remove_cmd(struct se_cmd *cmd) @@ -733,7 +711,6 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) spin_lock_irqsave(&cmd->t_state_lock, flags); - cmd->transport_state &= ~CMD_T_BUSY; if (dev && dev->transport->transport_complete) { dev->transport->transport_complete(cmd, @@ -1246,7 +1223,6 @@ void transport_init_se_cmd( init_completion(&cmd->cmd_wait_comp); spin_lock_init(&cmd->t_state_lock); kref_init(&cmd->cmd_kref); - cmd->transport_state = CMD_T_DEV_ACTIVE; cmd->se_tfo = tfo; cmd->se_sess = se_sess; @@ -1671,6 +1647,9 @@ void transport_generic_request_failure(struct se_cmd *cmd, { int ret = 0, post_ret = 0; + if (transport_check_aborted_status(cmd, 1)) + return; + pr_debug("-----[ Storage Engine Exception for cmd: %p ITT: 0x%08llx" " CDB: 0x%02x\n", cmd, cmd->tag, cmd->t_task_cdb[0]); pr_debug("-----[ i_state: %d t_state: %d sense_reason: %d\n", @@ -1801,7 +1780,7 @@ void __target_execute_cmd(struct se_cmd *cmd, bool do_checks) return; err: spin_lock_irq(&cmd->t_state_lock); - cmd->transport_state &= ~(CMD_T_BUSY|CMD_T_SENT); + cmd->transport_state &= ~CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); transport_generic_request_failure(cmd, ret); @@ -1829,7 +1808,7 @@ static int target_write_prot_action(struct se_cmd *cmd) sectors, 0, cmd->t_prot_sg, 0); if (unlikely(cmd->pi_err)) { spin_lock_irq(&cmd->t_state_lock); - cmd->transport_state &= ~(CMD_T_BUSY|CMD_T_SENT); + cmd->transport_state &= ~CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); transport_generic_request_failure(cmd, cmd->pi_err); return -1; @@ -1918,7 +1897,7 @@ void target_execute_cmd(struct se_cmd *cmd) } cmd->t_state = TRANSPORT_PROCESSING; - cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT; + cmd->transport_state |= CMD_T_ACTIVE | CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); if (target_write_prot_action(cmd)) @@ -1926,7 +1905,7 @@ void target_execute_cmd(struct se_cmd *cmd) if (target_handle_task_attr(cmd)) { spin_lock_irq(&cmd->t_state_lock); - cmd->transport_state &= ~(CMD_T_BUSY | CMD_T_SENT); + cmd->transport_state &= ~CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); return; } @@ -1979,8 +1958,6 @@ static void transport_complete_task_attr(struct se_cmd *cmd) if (cmd->sam_task_attr == TCM_SIMPLE_TAG) { atomic_dec_mb(&dev->simple_cmds); dev->dev_cur_ordered_id++; - pr_debug("Incremented dev->dev_cur_ordered_id: %u for SIMPLE\n", - dev->dev_cur_ordered_id); } else if (cmd->sam_task_attr == TCM_HEAD_TAG) { dev->dev_cur_ordered_id++; pr_debug("Incremented dev_cur_ordered_id: %u for HEAD_OF_QUEUE\n", @@ -2387,6 +2364,7 @@ EXPORT_SYMBOL(target_alloc_sgl); sense_reason_t transport_generic_new_cmd(struct se_cmd *cmd) { + unsigned long flags; int ret = 0; bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB); @@ -2452,7 +2430,24 @@ transport_generic_new_cmd(struct se_cmd *cmd) target_execute_cmd(cmd); return 0; } - transport_cmd_check_stop(cmd, false, true); + + spin_lock_irqsave(&cmd->t_state_lock, flags); + cmd->t_state = TRANSPORT_WRITE_PENDING; + /* + * Determine if frontend context caller is requesting the stopping of + * this command for frontend exceptions. + */ + if (cmd->transport_state & CMD_T_STOP) { + pr_debug("%s:%d CMD_T_STOP for ITT: 0x%08llx\n", + __func__, __LINE__, cmd->tag); + + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + + complete_all(&cmd->t_transport_stop_comp); + return 0; + } + cmd->transport_state &= ~CMD_T_ACTIVE; + spin_unlock_irqrestore(&cmd->t_state_lock, flags); ret = cmd->se_tfo->write_pending(cmd); if (ret == -EAGAIN || ret == -ENOMEM) @@ -2595,39 +2590,38 @@ static void target_release_cmd_kref(struct kref *kref) unsigned long flags; bool fabric_stop; - spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); + if (se_sess) { + spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); - spin_lock(&se_cmd->t_state_lock); - fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP) && - (se_cmd->transport_state & CMD_T_ABORTED); - spin_unlock(&se_cmd->t_state_lock); + spin_lock(&se_cmd->t_state_lock); + fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP) && + (se_cmd->transport_state & CMD_T_ABORTED); + spin_unlock(&se_cmd->t_state_lock); - if (se_cmd->cmd_wait_set || fabric_stop) { + if (se_cmd->cmd_wait_set || fabric_stop) { + list_del_init(&se_cmd->se_cmd_list); + spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); + target_free_cmd_mem(se_cmd); + complete(&se_cmd->cmd_wait_comp); + return; + } list_del_init(&se_cmd->se_cmd_list); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); - target_free_cmd_mem(se_cmd); - complete(&se_cmd->cmd_wait_comp); - return; } - list_del_init(&se_cmd->se_cmd_list); - spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); target_free_cmd_mem(se_cmd); se_cmd->se_tfo->release_cmd(se_cmd); } -/* target_put_sess_cmd - Check for active I/O shutdown via kref_put - * @se_cmd: command descriptor to drop +/** + * target_put_sess_cmd - decrease the command reference count + * @se_cmd: command to drop a reference from + * + * Returns 1 if and only if this target_put_sess_cmd() call caused the + * refcount to drop to zero. Returns zero otherwise. */ int target_put_sess_cmd(struct se_cmd *se_cmd) { - struct se_session *se_sess = se_cmd->se_sess; - - if (!se_sess) { - target_free_cmd_mem(se_cmd); - se_cmd->se_tfo->release_cmd(se_cmd); - return 1; - } return kref_put(&se_cmd->cmd_kref, target_release_cmd_kref); } EXPORT_SYMBOL(target_put_sess_cmd); @@ -2706,10 +2700,39 @@ void target_wait_for_sess_cmds(struct se_session *se_sess) } EXPORT_SYMBOL(target_wait_for_sess_cmds); +static void target_lun_confirm(struct percpu_ref *ref) +{ + struct se_lun *lun = container_of(ref, struct se_lun, lun_ref); + + complete(&lun->lun_ref_comp); +} + void transport_clear_lun_ref(struct se_lun *lun) { - percpu_ref_kill(&lun->lun_ref); + /* + * Mark the percpu-ref as DEAD, switch to atomic_t mode, drop + * the initial reference and schedule confirm kill to be + * executed after one full RCU grace period has completed. + */ + percpu_ref_kill_and_confirm(&lun->lun_ref, target_lun_confirm); + /* + * The first completion waits for percpu_ref_switch_to_atomic_rcu() + * to call target_lun_confirm after lun->lun_ref has been marked + * as __PERCPU_REF_DEAD on all CPUs, and switches to atomic_t + * mode so that percpu_ref_tryget_live() lookup of lun->lun_ref + * fails for all new incoming I/O. + */ wait_for_completion(&lun->lun_ref_comp); + /* + * The second completion waits for percpu_ref_put_many() to + * invoke ->release() after lun->lun_ref has switched to + * atomic_t mode, and lun->lun_ref.count has reached zero. + * + * At this point all target-core lun->lun_ref references have + * been dropped via transport_lun_remove_cmd(), and it's safe + * to proceed with the remaining LUN shutdown. + */ + wait_for_completion(&lun->lun_shutdown_comp); } static bool @@ -2765,11 +2788,8 @@ __transport_wait_for_tasks(struct se_cmd *cmd, bool fabric_stop, } /** - * transport_wait_for_tasks - wait for completion to occur - * @cmd: command to wait - * - * Called from frontend fabric context to wait for storage engine - * to pause and/or release frontend generated struct se_cmd. + * transport_wait_for_tasks - set CMD_T_STOP and wait for t_transport_stop_comp + * @cmd: command to wait on */ bool transport_wait_for_tasks(struct se_cmd *cmd) { diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 8041710b6972..c3adefe95e50 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -642,9 +642,7 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) WARN_ON(tcmu_hdr_get_op(entry->hdr.len_op) != TCMU_OP_CMD); spin_lock(&udev->commands_lock); - cmd = idr_find(&udev->commands, entry->hdr.cmd_id); - if (cmd) - idr_remove(&udev->commands, cmd->cmd_id); + cmd = idr_remove(&udev->commands, entry->hdr.cmd_id); spin_unlock(&udev->commands_lock); if (!cmd) { @@ -783,15 +781,15 @@ static int tcmu_find_mem_index(struct vm_area_struct *vma) return -1; } -static int tcmu_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int tcmu_vma_fault(struct vm_fault *vmf) { - struct tcmu_dev *udev = vma->vm_private_data; + struct tcmu_dev *udev = vmf->vma->vm_private_data; struct uio_info *info = &udev->uio_info; struct page *page; unsigned long offset; void *addr; - int mi = tcmu_find_mem_index(vma); + int mi = tcmu_find_mem_index(vmf->vma); if (mi < 0) return VM_FAULT_SIGBUS; diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 9af7842b8178..ec372860106f 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -83,14 +83,12 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller) static void ft_free_cmd(struct ft_cmd *cmd) { struct fc_frame *fp; - struct fc_lport *lport; struct ft_sess *sess; if (!cmd) return; sess = cmd->sess; fp = cmd->req_frame; - lport = fr_dev(fp); if (fr_seq(fp)) fc_seq_release(fr_seq(fp)); fc_frame_free(fp); diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index c2c056cc7ea5..776b34396144 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -245,6 +245,15 @@ config RCAR_THERMAL Enable this to plug the R-Car thermal sensor driver into the Linux thermal framework. +config RCAR_GEN3_THERMAL + tristate "Renesas R-Car Gen3 thermal driver" + depends on ARCH_RENESAS || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + Enable this to plug the R-Car Gen3 thermal sensor driver into the Linux + thermal framework. + config KIRKWOOD_THERMAL tristate "Temperature sensor on Marvell Kirkwood SoCs" depends on MACH_KIRKWOOD || COMPILE_TEST @@ -436,4 +445,12 @@ depends on (ARCH_QCOM && OF) || COMPILE_TEST source "drivers/thermal/qcom/Kconfig" endmenu +config ZX2967_THERMAL + tristate "Thermal sensors on zx2967 SoC" + depends on ARCH_ZX || COMPILE_TEST + help + Enable the zx2967 thermal sensors driver, which supports + the primitive temperature sensor embedded in zx2967 SoCs. + This sensor generates the real time die temperature. + endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 6a3d7b573036..7adae2029355 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o +obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-y += samsung/ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o @@ -56,3 +57,4 @@ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/ obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o +obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o diff --git a/drivers/thermal/clock_cooling.c b/drivers/thermal/clock_cooling.c index ed5dd0e88657..56711c25584d 100644 --- a/drivers/thermal/clock_cooling.c +++ b/drivers/thermal/clock_cooling.c @@ -65,42 +65,7 @@ struct clock_cooling_device { }; #define to_clock_cooling_device(x) \ container_of(x, struct clock_cooling_device, clk_rate_change_nb) -static DEFINE_IDR(clock_idr); -static DEFINE_MUTEX(cooling_clock_lock); - -/** - * clock_cooling_get_idr - function to get an unique id. - * @id: int * value generated by this function. - * - * This function will populate @id with an unique - * id, using the idr API. - * - * Return: 0 on success, an error code on failure. - */ -static int clock_cooling_get_idr(int *id) -{ - int ret; - - mutex_lock(&cooling_clock_lock); - ret = idr_alloc(&clock_idr, NULL, 0, 0, GFP_KERNEL); - mutex_unlock(&cooling_clock_lock); - if (unlikely(ret < 0)) - return ret; - *id = ret; - - return 0; -} - -/** - * release_idr - function to free the unique id. - * @id: int value representing the unique id. - */ -static void release_idr(int id) -{ - mutex_lock(&cooling_clock_lock); - idr_remove(&clock_idr, id); - mutex_unlock(&cooling_clock_lock); -} +static DEFINE_IDA(clock_ida); /* Below code defines functions to be used for clock as cooling device */ @@ -432,16 +397,17 @@ clock_cooling_register(struct device *dev, const char *clock_name) if (IS_ERR(ccdev->clk)) return ERR_CAST(ccdev->clk); - ret = clock_cooling_get_idr(&ccdev->id); - if (ret) - return ERR_PTR(-EINVAL); + ret = ida_simple_get(&clock_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + return ERR_PTR(ret); + ccdev->id = ret; snprintf(dev_name, sizeof(dev_name), "thermal-clock-%d", ccdev->id); cdev = thermal_cooling_device_register(dev_name, ccdev, &clock_cooling_ops); if (IS_ERR(cdev)) { - release_idr(ccdev->id); + ida_simple_remove(&clock_ida, ccdev->id); return ERR_PTR(-EINVAL); } ccdev->cdev = cdev; @@ -450,7 +416,7 @@ clock_cooling_register(struct device *dev, const char *clock_name) /* Assuming someone has already filled the opp table for this device */ ret = dev_pm_opp_init_cpufreq_table(dev, &ccdev->freq_table); if (ret) { - release_idr(ccdev->id); + ida_simple_remove(&clock_ida, ccdev->id); return ERR_PTR(ret); } ccdev->clock_state = 0; @@ -481,6 +447,6 @@ void clock_cooling_unregister(struct thermal_cooling_device *cdev) dev_pm_opp_free_cpufreq_table(ccdev->dev, &ccdev->freq_table); thermal_cooling_device_unregister(ccdev->cdev); - release_idr(ccdev->id); + ida_simple_remove(&clock_ida, ccdev->id); } EXPORT_SYMBOL_GPL(clock_cooling_unregister); diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 85fdbf762fa0..91048eeca28b 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -26,6 +26,7 @@ #include <linux/thermal.h> #include <linux/cpufreq.h> #include <linux/err.h> +#include <linux/idr.h> #include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/cpu.h> @@ -104,50 +105,13 @@ struct cpufreq_cooling_device { struct device *cpu_dev; get_static_t plat_get_static_power; }; -static DEFINE_IDR(cpufreq_idr); -static DEFINE_MUTEX(cooling_cpufreq_lock); +static DEFINE_IDA(cpufreq_ida); static unsigned int cpufreq_dev_count; static DEFINE_MUTEX(cooling_list_lock); static LIST_HEAD(cpufreq_dev_list); -/** - * get_idr - function to get a unique id. - * @idr: struct idr * handle used to create a id. - * @id: int * value generated by this function. - * - * This function will populate @id with an unique - * id, using the idr API. - * - * Return: 0 on success, an error code on failure. - */ -static int get_idr(struct idr *idr, int *id) -{ - int ret; - - mutex_lock(&cooling_cpufreq_lock); - ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); - mutex_unlock(&cooling_cpufreq_lock); - if (unlikely(ret < 0)) - return ret; - *id = ret; - - return 0; -} - -/** - * release_idr - function to free the unique id. - * @idr: struct idr * handle used for creating the id. - * @id: int value representing the unique id. - */ -static void release_idr(struct idr *idr, int id) -{ - mutex_lock(&cooling_cpufreq_lock); - idr_remove(idr, id); - mutex_unlock(&cooling_cpufreq_lock); -} - /* Below code defines functions to be used for cpufreq as cooling device */ /** @@ -645,31 +609,39 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev, unsigned long state, u32 *power) { unsigned int freq, num_cpus; - cpumask_t cpumask; + cpumask_var_t cpumask; u32 static_power, dynamic_power; int ret; struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; - cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask); - num_cpus = cpumask_weight(&cpumask); + if (!alloc_cpumask_var(&cpumask, GFP_KERNEL)) + return -ENOMEM; + + cpumask_and(cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask); + num_cpus = cpumask_weight(cpumask); /* None of our cpus are online, so no power */ if (num_cpus == 0) { *power = 0; - return 0; + ret = 0; + goto out; } freq = cpufreq_device->freq_table[state]; - if (!freq) - return -EINVAL; + if (!freq) { + ret = -EINVAL; + goto out; + } dynamic_power = cpu_freq_to_power(cpufreq_device, freq) * num_cpus; ret = get_static_power(cpufreq_device, tz, freq, &static_power); if (ret) - return ret; + goto out; *power = static_power + dynamic_power; - return 0; +out: + free_cpumask_var(cpumask); + return ret; } /** @@ -795,16 +767,20 @@ __cpufreq_cooling_register(struct device_node *np, struct cpufreq_cooling_device *cpufreq_dev; char dev_name[THERMAL_NAME_LENGTH]; struct cpufreq_frequency_table *pos, *table; - struct cpumask temp_mask; + cpumask_var_t temp_mask; unsigned int freq, i, num_cpus; int ret; struct thermal_cooling_device_ops *cooling_ops; - cpumask_and(&temp_mask, clip_cpus, cpu_online_mask); - policy = cpufreq_cpu_get(cpumask_first(&temp_mask)); + if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); + + cpumask_and(temp_mask, clip_cpus, cpu_online_mask); + policy = cpufreq_cpu_get(cpumask_first(temp_mask)); if (!policy) { pr_debug("%s: CPUFreq policy not found\n", __func__); - return ERR_PTR(-EPROBE_DEFER); + cool_dev = ERR_PTR(-EPROBE_DEFER); + goto free_cpumask; } table = policy->freq_table; @@ -867,11 +843,12 @@ __cpufreq_cooling_register(struct device_node *np, cooling_ops = &cpufreq_cooling_ops; } - ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); - if (ret) { + ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); + if (ret < 0) { cool_dev = ERR_PTR(ret); goto free_power_table; } + cpufreq_dev->id = ret; /* Fill freq-table in descending order of frequencies */ for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) { @@ -891,27 +868,24 @@ __cpufreq_cooling_register(struct device_node *np, cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev, cooling_ops); if (IS_ERR(cool_dev)) - goto remove_idr; + goto remove_ida; cpufreq_dev->clipped_freq = cpufreq_dev->freq_table[0]; cpufreq_dev->cool_dev = cool_dev; - mutex_lock(&cooling_cpufreq_lock); - mutex_lock(&cooling_list_lock); list_add(&cpufreq_dev->node, &cpufreq_dev_list); - mutex_unlock(&cooling_list_lock); /* Register the notifier for first cpufreq cooling device */ if (!cpufreq_dev_count++) cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); - mutex_unlock(&cooling_cpufreq_lock); + mutex_unlock(&cooling_list_lock); goto put_policy; -remove_idr: - release_idr(&cpufreq_idr, cpufreq_dev->id); +remove_ida: + ida_simple_remove(&cpufreq_ida, cpufreq_dev->id); free_power_table: kfree(cpufreq_dev->dyn_power_table); free_table: @@ -924,7 +898,8 @@ free_cdev: kfree(cpufreq_dev); put_policy: cpufreq_cpu_put(policy); - +free_cpumask: + free_cpumask_var(temp_mask); return cool_dev; } @@ -1052,20 +1027,17 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) cpufreq_dev = cdev->devdata; + mutex_lock(&cooling_list_lock); /* Unregister the notifier for the last cpufreq cooling device */ - mutex_lock(&cooling_cpufreq_lock); if (!--cpufreq_dev_count) cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); - mutex_lock(&cooling_list_lock); list_del(&cpufreq_dev->node); mutex_unlock(&cooling_list_lock); - mutex_unlock(&cooling_cpufreq_lock); - thermal_cooling_device_unregister(cpufreq_dev->cool_dev); - release_idr(&cpufreq_idr, cpufreq_dev->id); + ida_simple_remove(&cpufreq_ida, cpufreq_dev->id); kfree(cpufreq_dev->dyn_power_table); kfree(cpufreq_dev->time_in_idle_timestamp); kfree(cpufreq_dev->time_in_idle); diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index ba7a5cd994dc..7743a78d4723 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -21,14 +21,14 @@ #include <linux/devfreq.h> #include <linux/devfreq_cooling.h> #include <linux/export.h> +#include <linux/idr.h> #include <linux/slab.h> #include <linux/pm_opp.h> #include <linux/thermal.h> #include <trace/events/thermal.h> -static DEFINE_MUTEX(devfreq_lock); -static DEFINE_IDR(devfreq_idr); +static DEFINE_IDA(devfreq_ida); /** * struct devfreq_cooling_device - Devfreq cooling device @@ -58,42 +58,6 @@ struct devfreq_cooling_device { }; /** - * get_idr - function to get a unique id. - * @idr: struct idr * handle used to create a id. - * @id: int * value generated by this function. - * - * This function will populate @id with an unique - * id, using the idr API. - * - * Return: 0 on success, an error code on failure. - */ -static int get_idr(struct idr *idr, int *id) -{ - int ret; - - mutex_lock(&devfreq_lock); - ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); - mutex_unlock(&devfreq_lock); - if (unlikely(ret < 0)) - return ret; - *id = ret; - - return 0; -} - -/** - * release_idr - function to free the unique id. - * @idr: struct idr * handle used for creating the id. - * @id: int value representing the unique id. - */ -static void release_idr(struct idr *idr, int id) -{ - mutex_lock(&devfreq_lock); - idr_remove(idr, id); - mutex_unlock(&devfreq_lock); -} - -/** * partition_enable_opps() - disable all opps above a given state * @dfc: Pointer to devfreq we are operating on * @cdev_state: cooling device state we're setting @@ -489,9 +453,10 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, if (err) goto free_dfc; - err = get_idr(&devfreq_idr, &dfc->id); - if (err) + err = ida_simple_get(&devfreq_ida, 0, 0, GFP_KERNEL); + if (err < 0) goto free_tables; + dfc->id = err; snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d", dfc->id); @@ -502,15 +467,15 @@ of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, dev_err(df->dev.parent, "Failed to register devfreq cooling device (%d)\n", err); - goto release_idr; + goto release_ida; } dfc->cdev = cdev; return cdev; -release_idr: - release_idr(&devfreq_idr, dfc->id); +release_ida: + ida_simple_remove(&devfreq_ida, dfc->id); free_tables: kfree(dfc->power_table); kfree(dfc->freq_table); @@ -558,7 +523,7 @@ void devfreq_cooling_unregister(struct thermal_cooling_device *cdev) dfc = cdev->devdata; thermal_cooling_device_unregister(dfc->cdev); - release_idr(&devfreq_idr, dfc->id); + ida_simple_remove(&devfreq_ida, dfc->id); kfree(dfc->power_table); kfree(dfc->freq_table); diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 06912f0602b7..fb648a45754e 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -489,6 +489,10 @@ static int imx_thermal_probe(struct platform_device *pdev) data->tempmon = map; data->socdata = of_device_get_match_data(&pdev->dev); + if (!data->socdata) { + dev_err(&pdev->dev, "no device match found\n"); + return -ENODEV; + } /* make sure the IRQ flag is clear before enabling irq on i.MX6SX */ if (data->socdata->version == TEMPMON_IMX6SX) { diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index df64692e9e64..d718cd179ddb 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -50,6 +50,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/sched/rt.h> +#include <uapi/linux/sched/types.h> #include <asm/nmi.h> #include <asm/msr.h> @@ -461,16 +462,13 @@ static void poll_pkg_cstate(struct work_struct *dummy) { static u64 msr_last; static u64 tsc_last; - static unsigned long jiffies_last; u64 msr_now; - unsigned long jiffies_now; u64 tsc_now; u64 val64; msr_now = pkg_state_counter(); tsc_now = rdtsc(); - jiffies_now = jiffies; /* calculate pkg cstate vs tsc ratio */ if (!msr_last || !tsc_last) @@ -485,7 +483,6 @@ static void poll_pkg_cstate(struct work_struct *dummy) /* update record */ msr_last = msr_now; - jiffies_last = jiffies_now; tsc_last = tsc_now; if (true == clamping) diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 34169c32d495..1aff7fde54b1 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -183,37 +183,37 @@ struct mtk_thermal { }; /* MT8173 thermal sensor data */ -const int mt8173_bank_data[MT8173_NUM_ZONES][3] = { +static const int mt8173_bank_data[MT8173_NUM_ZONES][3] = { { MT8173_TS2, MT8173_TS3 }, { MT8173_TS2, MT8173_TS4 }, { MT8173_TS1, MT8173_TS2, MT8173_TSABB }, { MT8173_TS2 }, }; -const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = { +static const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR2 }; -const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { +static const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3 }; -const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; +static const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; /* MT2701 thermal sensor data */ -const int mt2701_bank_data[MT2701_NUM_SENSORS] = { +static const int mt2701_bank_data[MT2701_NUM_SENSORS] = { MT2701_TS1, MT2701_TS2, MT2701_TSABB }; -const int mt2701_msr[MT2701_NUM_SENSORS_PER_ZONE] = { +static const int mt2701_msr[MT2701_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, TEMP_MSR1, TEMP_MSR2 }; -const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { +static const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2 }; -const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; +static const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; /** * The MT8173 thermal controller has four banks. Each bank can read up to diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c new file mode 100644 index 000000000000..d33c845244b1 --- /dev/null +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -0,0 +1,335 @@ +/* + * R-Car Gen3 THS thermal sensor driver + * Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen. + * + * Copyright (C) 2016 Renesas Electronics Corporation. + * Copyright (C) 2016 Sang Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/thermal.h> + +/* Register offsets */ +#define REG_GEN3_IRQSTR 0x04 +#define REG_GEN3_IRQMSK 0x08 +#define REG_GEN3_IRQCTL 0x0C +#define REG_GEN3_IRQEN 0x10 +#define REG_GEN3_IRQTEMP1 0x14 +#define REG_GEN3_IRQTEMP2 0x18 +#define REG_GEN3_IRQTEMP3 0x1C +#define REG_GEN3_CTSR 0x20 +#define REG_GEN3_THCTR 0x20 +#define REG_GEN3_TEMP 0x28 +#define REG_GEN3_THCODE1 0x50 +#define REG_GEN3_THCODE2 0x54 +#define REG_GEN3_THCODE3 0x58 + +/* CTSR bits */ +#define CTSR_PONM BIT(8) +#define CTSR_AOUT BIT(7) +#define CTSR_THBGR BIT(5) +#define CTSR_VMEN BIT(4) +#define CTSR_VMST BIT(1) +#define CTSR_THSST BIT(0) + +/* THCTR bits */ +#define THCTR_PONM BIT(6) +#define THCTR_THSST BIT(0) + +#define CTEMP_MASK 0xFFF + +#define MCELSIUS(temp) ((temp) * 1000) +#define GEN3_FUSE_MASK 0xFFF + +#define TSC_MAX_NUM 3 + +/* Structure for thermal temperature calculation */ +struct equation_coefs { + int a1; + int b1; + int a2; + int b2; +}; + +struct rcar_gen3_thermal_tsc { + void __iomem *base; + struct thermal_zone_device *zone; + struct equation_coefs coef; + struct mutex lock; +}; + +struct rcar_gen3_thermal_priv { + struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; +}; + +struct rcar_gen3_thermal_data { + void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc); +}; + +static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc, + u32 reg) +{ + return ioread32(tsc->base + reg); +} + +static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, + u32 reg, u32 data) +{ + iowrite32(data, tsc->base + reg); +} + +/* + * Linear approximation for temperature + * + * [reg] = [temp] * a + b => [temp] = ([reg] - b) / a + * + * The constants a and b are calculated using two triplets of int values PTAT + * and THCODE. PTAT and THCODE can either be read from hardware or use hard + * coded values from driver. The formula to calculate a and b are taken from + * BSP and sparsely documented and understood. + * + * Examining the linear formula and the formula used to calculate constants a + * and b while knowing that the span for PTAT and THCODE values are between + * 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff == 0xffe001. + * Integer also needs to be signed so that leaves 7 bits for binary + * fixed point scaling. + */ + +#define FIXPT_SHIFT 7 +#define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) +#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) +#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) + +#define RCAR3_THERMAL_GRAN 500 /* mili Celsius */ + +/* no idea where these constants come from */ +#define TJ_1 96 +#define TJ_3 -41 + +static void rcar_gen3_thermal_calc_coefs(struct equation_coefs *coef, + int *ptat, int *thcode) +{ + int tj_2; + + /* TODO: Find documentation and document constant calculation formula */ + + /* + * Division is not scaled in BSP and if scaled it might overflow + * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled + */ + tj_2 = (FIXPT_INT((ptat[1] - ptat[2]) * 137) + / (ptat[0] - ptat[2])) - FIXPT_INT(41); + + coef->a1 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[2]), + tj_2 - FIXPT_INT(TJ_3)); + coef->b1 = FIXPT_INT(thcode[2]) - coef->a1 * TJ_3; + + coef->a2 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[0]), + tj_2 - FIXPT_INT(TJ_1)); + coef->b2 = FIXPT_INT(thcode[0]) - coef->a2 * TJ_1; +} + +static int rcar_gen3_thermal_round(int temp) +{ + int result, round_offs; + + round_offs = temp >= 0 ? RCAR3_THERMAL_GRAN / 2 : + -RCAR3_THERMAL_GRAN / 2; + result = (temp + round_offs) / RCAR3_THERMAL_GRAN; + return result * RCAR3_THERMAL_GRAN; +} + +static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) +{ + struct rcar_gen3_thermal_tsc *tsc = devdata; + int mcelsius, val1, val2; + u32 reg; + + /* Read register and convert to mili Celsius */ + mutex_lock(&tsc->lock); + + reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; + + val1 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, tsc->coef.a1); + val2 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, tsc->coef.a2); + mcelsius = FIXPT_TO_MCELSIUS((val1 + val2) / 2); + + mutex_unlock(&tsc->lock); + + /* Make sure we are inside specifications */ + if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125))) + return -EIO; + + /* Round value to device granularity setting */ + *temp = rcar_gen3_thermal_round(mcelsius); + + return 0; +} + +static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { + .get_temp = rcar_gen3_thermal_get_temp, +}; + +static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) +{ + rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); + rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 0x0); + + usleep_range(1000, 2000); + + rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); + rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, + CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN); + + usleep_range(100, 200); + + rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, + CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN | + CTSR_VMST | CTSR_THSST); + + usleep_range(1000, 2000); +} + +static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) +{ + u32 reg_val; + + reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); + reg_val &= ~THCTR_PONM; + rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); + + usleep_range(1000, 2000); + + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); + reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); + reg_val |= THCTR_THSST; + rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); +} + +static const struct rcar_gen3_thermal_data r8a7795_data = { + .thermal_init = r8a7795_thermal_init, +}; + +static const struct rcar_gen3_thermal_data r8a7796_data = { + .thermal_init = r8a7796_thermal_init, +}; + +static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { + { .compatible = "renesas,r8a7795-thermal", .data = &r8a7795_data}, + { .compatible = "renesas,r8a7796-thermal", .data = &r8a7796_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); + +static int rcar_gen3_thermal_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + pm_runtime_put(dev); + pm_runtime_disable(dev); + + return 0; +} + +static int rcar_gen3_thermal_probe(struct platform_device *pdev) +{ + struct rcar_gen3_thermal_priv *priv; + struct device *dev = &pdev->dev; + struct resource *res; + struct thermal_zone_device *zone; + int ret, i; + const struct rcar_gen3_thermal_data *match_data = + of_device_get_match_data(dev); + + /* default values if FUSEs are missing */ + /* TODO: Read values from hardware on supported platforms */ + int ptat[3] = { 2351, 1509, 435 }; + int thcode[TSC_MAX_NUM][3] = { + { 3248, 2800, 2221 }, + { 3245, 2795, 2216 }, + { 3250, 2805, 2237 }, + }; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + for (i = 0; i < TSC_MAX_NUM; i++) { + struct rcar_gen3_thermal_tsc *tsc; + + tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); + if (!tsc) { + ret = -ENOMEM; + goto error_unregister; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + break; + + tsc->base = devm_ioremap_resource(dev, res); + if (IS_ERR(tsc->base)) { + ret = PTR_ERR(tsc->base); + goto error_unregister; + } + + priv->tscs[i] = tsc; + mutex_init(&tsc->lock); + + match_data->thermal_init(tsc); + rcar_gen3_thermal_calc_coefs(&tsc->coef, ptat, thcode[i]); + + zone = devm_thermal_zone_of_sensor_register(dev, i, tsc, + &rcar_gen3_tz_of_ops); + if (IS_ERR(zone)) { + dev_err(dev, "Can't register thermal zone\n"); + ret = PTR_ERR(zone); + goto error_unregister; + } + tsc->zone = zone; + } + + return 0; + +error_unregister: + rcar_gen3_thermal_remove(pdev); + + return ret; +} + +static struct platform_driver rcar_gen3_thermal_driver = { + .driver = { + .name = "rcar_gen3_thermal", + .of_match_table = rcar_gen3_thermal_dt_ids, + }, + .probe = rcar_gen3_thermal_probe, + .remove = rcar_gen3_thermal_remove, +}; +module_platform_driver(rcar_gen3_thermal_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver"); +MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>"); diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index ad1186dd6132..7b8ef09d2b3c 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1168,7 +1168,6 @@ static int exynos_of_sensor_conf(struct device_node *np, pdata->default_temp_offset = (u8)value; of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type); - of_property_read_u32(np, "samsung,tmu_cal_mode", &pdata->cal_mode); of_node_put(np); return 0; diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h index 440c7140b660..5149c2a3030c 100644 --- a/drivers/thermal/samsung/exynos_tmu.h +++ b/drivers/thermal/samsung/exynos_tmu.h @@ -70,7 +70,6 @@ struct exynos_tmu_platform_data { enum soc_type type; u32 cal_type; - u32 cal_mode; }; #endif /* _EXYNOS_TMU_H */ diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 655591316a88..11f0675cb7e5 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -36,9 +36,8 @@ MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Generic thermal management sysfs support"); MODULE_LICENSE("GPL v2"); -static DEFINE_IDR(thermal_tz_idr); -static DEFINE_IDR(thermal_cdev_idr); -static DEFINE_MUTEX(thermal_idr_lock); +static DEFINE_IDA(thermal_tz_ida); +static DEFINE_IDA(thermal_cdev_ida); static LIST_HEAD(thermal_tz_list); static LIST_HEAD(thermal_cdev_list); @@ -589,29 +588,6 @@ void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, * - thermal zone devices lifecycle: registration, unregistration, * binding, and unbinding. */ -static int get_idr(struct idr *idr, struct mutex *lock, int *id) -{ - int ret; - - if (lock) - mutex_lock(lock); - ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); - if (lock) - mutex_unlock(lock); - if (unlikely(ret < 0)) - return ret; - *id = ret; - return 0; -} - -static void release_idr(struct idr *idr, struct mutex *lock, int id) -{ - if (lock) - mutex_lock(lock); - idr_remove(idr, id); - if (lock) - mutex_unlock(lock); -} /** * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone @@ -685,15 +661,16 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, dev->target = THERMAL_NO_TARGET; dev->weight = weight; - result = get_idr(&tz->idr, &tz->lock, &dev->id); - if (result) + result = ida_simple_get(&tz->ida, 0, 0, GFP_KERNEL); + if (result < 0) goto free_mem; + dev->id = result; sprintf(dev->name, "cdev%d", dev->id); result = sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name); if (result) - goto release_idr; + goto release_ida; sprintf(dev->attr_name, "cdev%d_trip_point", dev->id); sysfs_attr_init(&dev->attr.attr); @@ -737,8 +714,8 @@ remove_trip_file: device_remove_file(&tz->device, &dev->attr); remove_symbol_link: sysfs_remove_link(&tz->device.kobj, dev->name); -release_idr: - release_idr(&tz->idr, &tz->lock, dev->id); +release_ida: + ida_simple_remove(&tz->ida, dev->id); free_mem: kfree(dev); return result; @@ -785,7 +762,7 @@ unbind: device_remove_file(&tz->device, &pos->weight_attr); device_remove_file(&tz->device, &pos->attr); sysfs_remove_link(&tz->device.kobj, pos->name); - release_idr(&tz->idr, &tz->lock, pos->id); + ida_simple_remove(&tz->ida, pos->id); kfree(pos); return 0; } @@ -925,12 +902,13 @@ __thermal_cooling_device_register(struct device_node *np, if (!cdev) return ERR_PTR(-ENOMEM); - result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id); - if (result) { + result = ida_simple_get(&thermal_cdev_ida, 0, 0, GFP_KERNEL); + if (result < 0) { kfree(cdev); return ERR_PTR(result); } + cdev->id = result; strlcpy(cdev->type, type ? : "", sizeof(cdev->type)); mutex_init(&cdev->lock); INIT_LIST_HEAD(&cdev->thermal_instances); @@ -943,7 +921,7 @@ __thermal_cooling_device_register(struct device_node *np, dev_set_name(&cdev->device, "cooling_device%d", cdev->id); result = device_register(&cdev->device); if (result) { - release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); + ida_simple_remove(&thermal_cdev_ida, cdev->id); kfree(cdev); return ERR_PTR(result); } @@ -1070,7 +1048,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) mutex_unlock(&thermal_list_lock); - release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); + ida_simple_remove(&thermal_cdev_ida, cdev->id); device_unregister(&cdev->device); } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); @@ -1172,14 +1150,15 @@ thermal_zone_device_register(const char *type, int trips, int mask, return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&tz->thermal_instances); - idr_init(&tz->idr); + ida_init(&tz->ida); mutex_init(&tz->lock); - result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id); - if (result) { + result = ida_simple_get(&thermal_tz_ida, 0, 0, GFP_KERNEL); + if (result < 0) { kfree(tz); return ERR_PTR(result); } + tz->id = result; strlcpy(tz->type, type, sizeof(tz->type)); tz->ops = ops; tz->tzp = tzp; @@ -1201,7 +1180,7 @@ thermal_zone_device_register(const char *type, int trips, int mask, dev_set_name(&tz->device, "thermal_zone%d", tz->id); result = device_register(&tz->device); if (result) { - release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); + ida_simple_remove(&thermal_tz_ida, tz->id); kfree(tz); return ERR_PTR(result); } @@ -1255,7 +1234,7 @@ thermal_zone_device_register(const char *type, int trips, int mask, return tz; unregister: - release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); + ida_simple_remove(&thermal_tz_ida, tz->id); device_unregister(&tz->device); return ERR_PTR(result); } @@ -1313,8 +1292,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_set_governor(tz, NULL); thermal_remove_hwmon_sysfs(tz); - release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); - idr_destroy(&tz->idr); + ida_simple_remove(&thermal_tz_ida, tz->id); + ida_destroy(&tz->ida); mutex_destroy(&tz->lock); device_unregister(&tz->device); } @@ -1514,9 +1493,8 @@ unregister_class: unregister_governors: thermal_unregister_governors(); error: - idr_destroy(&thermal_tz_idr); - idr_destroy(&thermal_cdev_idr); - mutex_destroy(&thermal_idr_lock); + ida_destroy(&thermal_tz_ida); + ida_destroy(&thermal_cdev_ida); mutex_destroy(&thermal_list_lock); mutex_destroy(&thermal_governor_lock); return result; @@ -1529,9 +1507,8 @@ static void __exit thermal_exit(void) genetlink_exit(); class_unregister(&thermal_class); thermal_unregister_governors(); - idr_destroy(&thermal_tz_idr); - idr_destroy(&thermal_cdev_idr); - mutex_destroy(&thermal_idr_lock); + ida_destroy(&thermal_tz_ida); + ida_destroy(&thermal_cdev_ida); mutex_destroy(&thermal_list_lock); mutex_destroy(&thermal_governor_lock); } diff --git a/drivers/thermal/ti-soc-thermal/Kconfig b/drivers/thermal/ti-soc-thermal/Kconfig index ea8283f08aa6..fe0e877f84d0 100644 --- a/drivers/thermal/ti-soc-thermal/Kconfig +++ b/drivers/thermal/ti-soc-thermal/Kconfig @@ -11,7 +11,6 @@ config TI_SOC_THERMAL config TI_THERMAL bool "Texas Instruments SoCs thermal framework support" depends on TI_SOC_THERMAL - depends on CPU_THERMAL help If you say yes here you want to get support for generic thermal framework for the Texas Instruments on die bandgap temperature sensor. diff --git a/drivers/thermal/ti-soc-thermal/dra752-bandgap.h b/drivers/thermal/ti-soc-thermal/dra752-bandgap.h index 6b0f2b1160f7..a31e4b5e82cd 100644 --- a/drivers/thermal/ti-soc-thermal/dra752-bandgap.h +++ b/drivers/thermal/ti-soc-thermal/dra752-bandgap.h @@ -54,7 +54,6 @@ #define DRA752_STD_FUSE_OPP_BGAP_CORE_OFFSET 0x8 #define DRA752_TEMP_SENSOR_CORE_OFFSET 0x154 #define DRA752_BANDGAP_THRESHOLD_CORE_OFFSET 0x1ac -#define DRA752_BANDGAP_TSHUT_CORE_OFFSET 0x1b8 #define DRA752_BANDGAP_CUMUL_DTEMP_CORE_OFFSET 0x1c4 #define DRA752_DTEMP_CORE_0_OFFSET 0x208 #define DRA752_DTEMP_CORE_1_OFFSET 0x20c @@ -66,7 +65,6 @@ #define DRA752_STD_FUSE_OPP_BGAP_IVA_OFFSET 0x388 #define DRA752_TEMP_SENSOR_IVA_OFFSET 0x398 #define DRA752_BANDGAP_THRESHOLD_IVA_OFFSET 0x3a4 -#define DRA752_BANDGAP_TSHUT_IVA_OFFSET 0x3ac #define DRA752_BANDGAP_CUMUL_DTEMP_IVA_OFFSET 0x3b4 #define DRA752_DTEMP_IVA_0_OFFSET 0x3d0 #define DRA752_DTEMP_IVA_1_OFFSET 0x3d4 @@ -78,7 +76,6 @@ #define DRA752_STD_FUSE_OPP_BGAP_MPU_OFFSET 0x4 #define DRA752_TEMP_SENSOR_MPU_OFFSET 0x14c #define DRA752_BANDGAP_THRESHOLD_MPU_OFFSET 0x1a4 -#define DRA752_BANDGAP_TSHUT_MPU_OFFSET 0x1b0 #define DRA752_BANDGAP_CUMUL_DTEMP_MPU_OFFSET 0x1bc #define DRA752_DTEMP_MPU_0_OFFSET 0x1e0 #define DRA752_DTEMP_MPU_1_OFFSET 0x1e4 @@ -90,7 +87,6 @@ #define DRA752_STD_FUSE_OPP_BGAP_DSPEVE_OFFSET 0x384 #define DRA752_TEMP_SENSOR_DSPEVE_OFFSET 0x394 #define DRA752_BANDGAP_THRESHOLD_DSPEVE_OFFSET 0x3a0 -#define DRA752_BANDGAP_TSHUT_DSPEVE_OFFSET 0x3a8 #define DRA752_BANDGAP_CUMUL_DTEMP_DSPEVE_OFFSET 0x3b0 #define DRA752_DTEMP_DSPEVE_0_OFFSET 0x3bc #define DRA752_DTEMP_DSPEVE_1_OFFSET 0x3c0 @@ -102,7 +98,6 @@ #define DRA752_STD_FUSE_OPP_BGAP_GPU_OFFSET 0x0 #define DRA752_TEMP_SENSOR_GPU_OFFSET 0x150 #define DRA752_BANDGAP_THRESHOLD_GPU_OFFSET 0x1a8 -#define DRA752_BANDGAP_TSHUT_GPU_OFFSET 0x1b4 #define DRA752_BANDGAP_CUMUL_DTEMP_GPU_OFFSET 0x1c0 #define DRA752_DTEMP_GPU_0_OFFSET 0x1f4 #define DRA752_DTEMP_GPU_1_OFFSET 0x1f8 @@ -173,10 +168,6 @@ #define DRA752_BANDGAP_THRESHOLD_HOT_MASK (0x3ff << 16) #define DRA752_BANDGAP_THRESHOLD_COLD_MASK (0x3ff << 0) -/* DRA752.TSHUT_THRESHOLD */ -#define DRA752_TSHUT_THRESHOLD_MUXCTRL_MASK BIT(31) -#define DRA752_TSHUT_THRESHOLD_HOT_MASK (0x3ff << 16) -#define DRA752_TSHUT_THRESHOLD_COLD_MASK (0x3ff << 0) /* DRA752.BANDGAP_CUMUL_DTEMP_CORE */ #define DRA752_BANDGAP_CUMUL_DTEMP_CORE_MASK (0xffffffff << 0) @@ -216,8 +207,6 @@ #define DRA752_GPU_MAX_TEMP 125000 #define DRA752_GPU_HYST_VAL 5000 /* interrupts thresholds */ -#define DRA752_GPU_TSHUT_HOT 915 -#define DRA752_GPU_TSHUT_COLD 900 #define DRA752_GPU_T_HOT 800 #define DRA752_GPU_T_COLD 795 @@ -230,8 +219,6 @@ #define DRA752_MPU_MAX_TEMP 125000 #define DRA752_MPU_HYST_VAL 5000 /* interrupts thresholds */ -#define DRA752_MPU_TSHUT_HOT 915 -#define DRA752_MPU_TSHUT_COLD 900 #define DRA752_MPU_T_HOT 800 #define DRA752_MPU_T_COLD 795 @@ -244,8 +231,6 @@ #define DRA752_CORE_MAX_TEMP 125000 #define DRA752_CORE_HYST_VAL 5000 /* interrupts thresholds */ -#define DRA752_CORE_TSHUT_HOT 915 -#define DRA752_CORE_TSHUT_COLD 900 #define DRA752_CORE_T_HOT 800 #define DRA752_CORE_T_COLD 795 @@ -258,8 +243,6 @@ #define DRA752_DSPEVE_MAX_TEMP 125000 #define DRA752_DSPEVE_HYST_VAL 5000 /* interrupts thresholds */ -#define DRA752_DSPEVE_TSHUT_HOT 915 -#define DRA752_DSPEVE_TSHUT_COLD 900 #define DRA752_DSPEVE_T_HOT 800 #define DRA752_DSPEVE_T_COLD 795 @@ -272,8 +255,6 @@ #define DRA752_IVA_MAX_TEMP 125000 #define DRA752_IVA_HYST_VAL 5000 /* interrupts thresholds */ -#define DRA752_IVA_TSHUT_HOT 915 -#define DRA752_IVA_TSHUT_COLD 900 #define DRA752_IVA_T_HOT 800 #define DRA752_IVA_T_COLD 795 diff --git a/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c index 58b5c6694cd4..118d7d847715 100644 --- a/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c +++ b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c @@ -49,9 +49,6 @@ dra752_core_temp_sensor_registers = { .bgap_threshold = DRA752_BANDGAP_THRESHOLD_CORE_OFFSET, .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK, .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK, - .tshut_threshold = DRA752_BANDGAP_TSHUT_CORE_OFFSET, - .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK, - .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK, .bgap_status = DRA752_BANDGAP_STATUS_1_OFFSET, .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK, .status_hot_mask = DRA752_BANDGAP_STATUS_1_HOT_CORE_MASK, @@ -85,9 +82,6 @@ dra752_iva_temp_sensor_registers = { .bgap_threshold = DRA752_BANDGAP_THRESHOLD_IVA_OFFSET, .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK, .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK, - .tshut_threshold = DRA752_BANDGAP_TSHUT_IVA_OFFSET, - .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK, - .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK, .bgap_status = DRA752_BANDGAP_STATUS_2_OFFSET, .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK, .status_hot_mask = DRA752_BANDGAP_STATUS_2_HOT_IVA_MASK, @@ -121,9 +115,6 @@ dra752_mpu_temp_sensor_registers = { .bgap_threshold = DRA752_BANDGAP_THRESHOLD_MPU_OFFSET, .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK, .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK, - .tshut_threshold = DRA752_BANDGAP_TSHUT_MPU_OFFSET, - .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK, - .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK, .bgap_status = DRA752_BANDGAP_STATUS_1_OFFSET, .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK, .status_hot_mask = DRA752_BANDGAP_STATUS_1_HOT_MPU_MASK, @@ -157,9 +148,6 @@ dra752_dspeve_temp_sensor_registers = { .bgap_threshold = DRA752_BANDGAP_THRESHOLD_DSPEVE_OFFSET, .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK, .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK, - .tshut_threshold = DRA752_BANDGAP_TSHUT_DSPEVE_OFFSET, - .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK, - .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK, .bgap_status = DRA752_BANDGAP_STATUS_2_OFFSET, .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK, .status_hot_mask = DRA752_BANDGAP_STATUS_2_HOT_DSPEVE_MASK, @@ -193,9 +181,6 @@ dra752_gpu_temp_sensor_registers = { .bgap_threshold = DRA752_BANDGAP_THRESHOLD_GPU_OFFSET, .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK, .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK, - .tshut_threshold = DRA752_BANDGAP_TSHUT_GPU_OFFSET, - .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK, - .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK, .bgap_status = DRA752_BANDGAP_STATUS_1_OFFSET, .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK, .status_hot_mask = DRA752_BANDGAP_STATUS_1_HOT_GPU_MASK, @@ -211,8 +196,6 @@ dra752_gpu_temp_sensor_registers = { /* Thresholds and limits for DRA752 MPU temperature sensor */ static struct temp_sensor_data dra752_mpu_temp_sensor_data = { - .tshut_hot = DRA752_MPU_TSHUT_HOT, - .tshut_cold = DRA752_MPU_TSHUT_COLD, .t_hot = DRA752_MPU_T_HOT, .t_cold = DRA752_MPU_T_COLD, .min_freq = DRA752_MPU_MIN_FREQ, @@ -226,8 +209,6 @@ static struct temp_sensor_data dra752_mpu_temp_sensor_data = { /* Thresholds and limits for DRA752 GPU temperature sensor */ static struct temp_sensor_data dra752_gpu_temp_sensor_data = { - .tshut_hot = DRA752_GPU_TSHUT_HOT, - .tshut_cold = DRA752_GPU_TSHUT_COLD, .t_hot = DRA752_GPU_T_HOT, .t_cold = DRA752_GPU_T_COLD, .min_freq = DRA752_GPU_MIN_FREQ, @@ -241,8 +222,6 @@ static struct temp_sensor_data dra752_gpu_temp_sensor_data = { /* Thresholds and limits for DRA752 CORE temperature sensor */ static struct temp_sensor_data dra752_core_temp_sensor_data = { - .tshut_hot = DRA752_CORE_TSHUT_HOT, - .tshut_cold = DRA752_CORE_TSHUT_COLD, .t_hot = DRA752_CORE_T_HOT, .t_cold = DRA752_CORE_T_COLD, .min_freq = DRA752_CORE_MIN_FREQ, @@ -256,8 +235,6 @@ static struct temp_sensor_data dra752_core_temp_sensor_data = { /* Thresholds and limits for DRA752 DSPEVE temperature sensor */ static struct temp_sensor_data dra752_dspeve_temp_sensor_data = { - .tshut_hot = DRA752_DSPEVE_TSHUT_HOT, - .tshut_cold = DRA752_DSPEVE_TSHUT_COLD, .t_hot = DRA752_DSPEVE_T_HOT, .t_cold = DRA752_DSPEVE_T_COLD, .min_freq = DRA752_DSPEVE_MIN_FREQ, @@ -271,8 +248,6 @@ static struct temp_sensor_data dra752_dspeve_temp_sensor_data = { /* Thresholds and limits for DRA752 IVA temperature sensor */ static struct temp_sensor_data dra752_iva_temp_sensor_data = { - .tshut_hot = DRA752_IVA_TSHUT_HOT, - .tshut_cold = DRA752_IVA_TSHUT_COLD, .t_hot = DRA752_IVA_T_HOT, .t_cold = DRA752_IVA_T_COLD, .min_freq = DRA752_IVA_MIN_FREQ, @@ -416,8 +391,7 @@ int dra752_adc_to_temp[DRA752_ADC_END_VALUE - DRA752_ADC_START_VALUE + 1] = { /* DRA752 data */ const struct ti_bandgap_data dra752_data = { - .features = TI_BANDGAP_FEATURE_TSHUT_CONFIG | - TI_BANDGAP_FEATURE_FREEZE_BIT | + .features = TI_BANDGAP_FEATURE_FREEZE_BIT | TI_BANDGAP_FEATURE_TALERT | TI_BANDGAP_FEATURE_COUNTER_DELAY | TI_BANDGAP_FEATURE_HISTORY_BUFFER | diff --git a/drivers/thermal/zx2967_thermal.c b/drivers/thermal/zx2967_thermal.c new file mode 100644 index 000000000000..a5670ad2cfc8 --- /dev/null +++ b/drivers/thermal/zx2967_thermal.c @@ -0,0 +1,258 @@ +/* + * ZTE's zx2967 family thermal sensor driver + * + * Copyright (C) 2017 ZTE Ltd. + * + * Author: Baoyou Xie <baoyou.xie@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/thermal.h> + +/* Power Mode: 0->low 1->high */ +#define ZX2967_THERMAL_POWER_MODE 0 +#define ZX2967_POWER_MODE_LOW 0 +#define ZX2967_POWER_MODE_HIGH 1 + +/* DCF Control Register */ +#define ZX2967_THERMAL_DCF 0x4 +#define ZX2967_DCF_EN BIT(1) +#define ZX2967_DCF_FREEZE BIT(0) + +/* Selection Register */ +#define ZX2967_THERMAL_SEL 0x8 + +/* Control Register */ +#define ZX2967_THERMAL_CTRL 0x10 + +#define ZX2967_THERMAL_READY BIT(12) +#define ZX2967_THERMAL_TEMP_MASK GENMASK(11, 0) +#define ZX2967_THERMAL_ID_MASK 0x18 +#define ZX2967_THERMAL_ID 0x10 + +#define ZX2967_GET_TEMP_TIMEOUT_US (100 * 1024) + +/** + * struct zx2967_thermal_priv - zx2967 thermal sensor private structure + * @tzd: struct thermal_zone_device where the sensor is registered + * @lock: prevents read sensor in parallel + * @clk_topcrm: topcrm clk structure + * @clk_apb: apb clk structure + * @regs: pointer to base address of the thermal sensor + */ + +struct zx2967_thermal_priv { + struct thermal_zone_device *tzd; + struct mutex lock; + struct clk *clk_topcrm; + struct clk *clk_apb; + void __iomem *regs; + struct device *dev; +}; + +static int zx2967_thermal_get_temp(void *data, int *temp) +{ + void __iomem *regs; + struct zx2967_thermal_priv *priv = data; + u32 val; + int ret; + + if (!priv->tzd) + return -EAGAIN; + + regs = priv->regs; + mutex_lock(&priv->lock); + writel_relaxed(ZX2967_POWER_MODE_LOW, + regs + ZX2967_THERMAL_POWER_MODE); + writel_relaxed(ZX2967_DCF_EN, regs + ZX2967_THERMAL_DCF); + + val = readl_relaxed(regs + ZX2967_THERMAL_SEL); + val &= ~ZX2967_THERMAL_ID_MASK; + val |= ZX2967_THERMAL_ID; + writel_relaxed(val, regs + ZX2967_THERMAL_SEL); + + /* + * Must wait for a while, surely it's a bit odd. + * otherwise temperature value we got has a few deviation, even if + * the THERMAL_READY bit is set. + */ + usleep_range(100, 300); + ret = readx_poll_timeout(readl, regs + ZX2967_THERMAL_CTRL, + val, val & ZX2967_THERMAL_READY, 300, + ZX2967_GET_TEMP_TIMEOUT_US); + if (ret) { + dev_err(priv->dev, "Thermal sensor data timeout\n"); + goto unlock; + } + + writel_relaxed(ZX2967_DCF_FREEZE | ZX2967_DCF_EN, + regs + ZX2967_THERMAL_DCF); + val = readl_relaxed(regs + ZX2967_THERMAL_CTRL) + & ZX2967_THERMAL_TEMP_MASK; + writel_relaxed(ZX2967_POWER_MODE_HIGH, + regs + ZX2967_THERMAL_POWER_MODE); + + /* + * Calculate temperature + * In dts, slope is multiplied by 1000. + */ + *temp = DIV_ROUND_CLOSEST(((s32)val + priv->tzd->tzp->offset) * 1000, + priv->tzd->tzp->slope); + +unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static struct thermal_zone_of_device_ops zx2967_of_thermal_ops = { + .get_temp = zx2967_thermal_get_temp, +}; + +static int zx2967_thermal_probe(struct platform_device *pdev) +{ + struct zx2967_thermal_priv *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->clk_topcrm = devm_clk_get(&pdev->dev, "topcrm"); + if (IS_ERR(priv->clk_topcrm)) { + ret = PTR_ERR(priv->clk_topcrm); + dev_err(&pdev->dev, "failed to get topcrm clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(priv->clk_topcrm); + if (ret) { + dev_err(&pdev->dev, "failed to enable topcrm clock: %d\n", + ret); + return ret; + } + + priv->clk_apb = devm_clk_get(&pdev->dev, "apb"); + if (IS_ERR(priv->clk_apb)) { + ret = PTR_ERR(priv->clk_apb); + dev_err(&pdev->dev, "failed to get apb clock: %d\n", ret); + goto disable_clk_topcrm; + } + + ret = clk_prepare_enable(priv->clk_apb); + if (ret) { + dev_err(&pdev->dev, "failed to enable apb clock: %d\n", + ret); + goto disable_clk_topcrm; + } + + mutex_init(&priv->lock); + priv->tzd = thermal_zone_of_sensor_register(&pdev->dev, + 0, priv, &zx2967_of_thermal_ops); + + if (IS_ERR(priv->tzd)) { + ret = PTR_ERR(priv->tzd); + dev_err(&pdev->dev, "failed to register sensor: %d\n", ret); + goto disable_clk_all; + } + + if (priv->tzd->tzp->slope == 0) { + thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd); + dev_err(&pdev->dev, "coefficients of sensor is invalid\n"); + ret = -EINVAL; + goto disable_clk_all; + } + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + return 0; + +disable_clk_all: + clk_disable_unprepare(priv->clk_apb); +disable_clk_topcrm: + clk_disable_unprepare(priv->clk_topcrm); + return ret; +} + +static int zx2967_thermal_exit(struct platform_device *pdev) +{ + struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); + + thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd); + clk_disable_unprepare(priv->clk_topcrm); + clk_disable_unprepare(priv->clk_apb); + + return 0; +} + +static const struct of_device_id zx2967_thermal_id_table[] = { + { .compatible = "zte,zx296718-thermal" }, + {} +}; +MODULE_DEVICE_TABLE(of, zx2967_thermal_id_table); + +#ifdef CONFIG_PM_SLEEP +static int zx2967_thermal_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); + + if (priv && priv->clk_topcrm) + clk_disable_unprepare(priv->clk_topcrm); + + if (priv && priv->clk_apb) + clk_disable_unprepare(priv->clk_apb); + + return 0; +} + +static int zx2967_thermal_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev); + int error; + + error = clk_prepare_enable(priv->clk_topcrm); + if (error) + return error; + + error = clk_prepare_enable(priv->clk_apb); + if (error) { + clk_disable_unprepare(priv->clk_topcrm); + return error; + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(zx2967_thermal_pm_ops, + zx2967_thermal_suspend, zx2967_thermal_resume); + +static struct platform_driver zx2967_thermal_driver = { + .probe = zx2967_thermal_probe, + .remove = zx2967_thermal_exit, + .driver = { + .name = "zx2967_thermal", + .of_match_table = zx2967_thermal_id_table, + .pm = &zx2967_thermal_pm_ops, + }, +}; +module_platform_driver(zx2967_thermal_driver); + +MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); +MODULE_DESCRIPTION("ZTE zx2967 thermal driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index f3932baed07d..55577cf9b6a4 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -39,7 +39,7 @@ #include <linux/errno.h> #include <linux/signal.h> #include <linux/fcntl.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/ctype.h> diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index eb278832f5ce..1bacbc3b19a0 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -667,7 +667,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, struct n_hdlc_buf *tbuf; if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_write() called count=%Zd\n", + printk("%s(%d)n_hdlc_tty_write() called count=%zd\n", __FILE__,__LINE__,count); /* Verify pointers */ diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index a23fa5ed1d67..66b59a15780d 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -12,7 +12,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/fcntl.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/string.h> #include <linux/major.h> #include <linux/mm.h> diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index e92c23470e51..59a2a7e18b5a 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -12,7 +12,7 @@ static char *serial_version = "$Revision: 1.25 $"; #include <linux/types.h> #include <linux/errno.h> #include <linux/signal.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/tty.h> diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index 6ad26f802b51..f96bcf9bee25 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -210,7 +210,7 @@ #define IOC4_SSCR_PAUSE_STATE 0x40000000 /* Sets when PAUSE takes effect */ #define IOC4_SSCR_RESET 0x80000000 /* Reset DMA channels */ -/* All producer/comsumer pointers are the same bitfield */ +/* All producer/consumer pointers are the same bitfield */ #define IOC4_PROD_CONS_PTR_4K 0x00000ff8 /* For 4K buffers */ #define IOC4_PROD_CONS_PTR_1K 0x000003f8 /* For 1K buffers */ #define IOC4_PROD_CONS_PTR_OFF 3 diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 793395451982..ca54ce074a5f 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -29,6 +29,7 @@ #include <linux/tty_flip.h> #include <linux/spi/spi.h> #include <linux/uaccess.h> +#include <uapi/linux/sched/types.h> #define SC16IS7XX_NAME "sc16is7xx" #define SC16IS7XX_MAX_DEVS 8 diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9939c3d9912b..3fe56894974a 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -24,6 +24,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/slab.h> +#include <linux/sched/signal.h> #include <linux/init.h> #include <linux/console.h> #include <linux/of.h> diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 73abd89c0108..46e46894e918 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -116,7 +116,7 @@ static int receive_chars_getchar(struct uart_port *port) static int receive_chars_read(struct uart_port *port) { - int saw_console_brk = 0; + static int saw_console_brk; int limit = 10000; while (limit-- > 0) { @@ -128,6 +128,9 @@ static int receive_chars_read(struct uart_port *port) bytes_read = 0; if (stat == CON_BREAK) { + if (saw_console_brk) + sun_do_break(); + if (uart_handle_break(port)) continue; saw_console_brk = 1; @@ -151,6 +154,7 @@ static int receive_chars_read(struct uart_port *port) if (port->sysrq != 0 && *con_read_page) { for (i = 0; i < bytes_read; i++) uart_handle_sysrq_char(port, con_read_page[i]); + saw_console_brk = 0; } if (port->state == NULL) @@ -398,6 +402,12 @@ static struct uart_driver sunhv_reg = { static struct uart_port *sunhv_port; +void sunhv_migrate_hvcons_irq(int cpu) +{ + /* Migrate hvcons irq to param cpu */ + irq_force_affinity(sunhv_port->irq, cpumask_of(cpu)); +} + /* Copy 's' into the con_write_page, decoding "\n" into * "\r\n" along the way. We have to return two lengths * because the caller needs to know how much to advance diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 71136742e606..c6fc7141d7b2 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -14,8 +14,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/sched/rt.h> +#include <linux/sched/debug.h> +#include <linux/sched/task.h> #include <linux/interrupt.h> #include <linux/mm.h> #include <linux/fs.h> diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index a1fd3f7d487a..e6d1a6510886 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -69,7 +69,8 @@ #include <linux/errno.h> #include <linux/signal.h> #include <linux/fcntl.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/task.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index f27fc0f14c11..a9a978731c5b 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -9,7 +9,7 @@ #include <linux/types.h> #include <linux/termios.h> #include <linux/errno.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/tty.h> diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c index 9229de43e19d..52b7baef4f7a 100644 --- a/drivers/tty/tty_ldsem.c +++ b/drivers/tty/tty_ldsem.c @@ -32,6 +32,8 @@ #include <linux/atomic.h> #include <linux/tty.h> #include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/sched/task.h> #ifdef CONFIG_DEBUG_LOCK_ALLOC diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 5cd3cd932293..1d21a9c1d33e 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -11,7 +11,7 @@ #include <linux/timer.h> #include <linux/string.h> #include <linux/slab.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/bitops.h> #include <linux/delay.h> diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 397e1509fe51..c5f0fc906136 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -26,7 +26,9 @@ #include <linux/consolemap.h> #include <linux/module.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/debug.h> +#include <linux/sched/debug.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/mm.h> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 9d3ce505e7ab..5c4933bb4b53 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -72,7 +72,7 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/kernel.h> diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index a56edf2d58eb..0cbfe1ff6f6c 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -10,7 +10,7 @@ #include <linux/types.h> #include <linux/errno.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/tty.h> #include <linux/timer.h> #include <linux/kernel.h> diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index fba021f5736a..60ce7fd54e89 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -20,7 +20,7 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/idr.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/string.h> #include <linux/kobject.h> #include <linux/cdev.h> @@ -597,14 +597,14 @@ static int uio_find_mem_index(struct vm_area_struct *vma) return -1; } -static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int uio_vma_fault(struct vm_fault *vmf) { - struct uio_device *idev = vma->vm_private_data; + struct uio_device *idev = vmf->vma->vm_private_data; struct page *page; unsigned long offset; void *addr; - int mi = uio_find_mem_index(vma); + int mi = uio_find_mem_index(vmf->vma); if (mi < 0) return VM_FAULT_SIGBUS; diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 5a59da0dc98a..3e80aa3b917a 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -74,7 +74,7 @@ #include <linux/moduleparam.h> #include <linux/netdevice.h> #include <linux/proc_fs.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/signal.h> #include <linux/slab.h> #include <linux/stat.h> diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 235e305f8473..d5388938bc7a 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -32,6 +32,7 @@ #undef VERBOSE_DEBUG #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 071964c7847f..cc61055fb9be 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -49,7 +49,7 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/signal.h> #include <linux/poll.h> #include <linux/slab.h> diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 52747b6ac89a..cfc3cff6e8d5 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -36,6 +36,7 @@ #include <linux/fs.h> #include <linux/mm.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/signal.h> #include <linux/poll.h> @@ -2335,7 +2336,7 @@ static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg) if (copy_from_user(&data, arg, sizeof(data))) return -EFAULT; - /* This is an one way operation. Once privileges are + /* This is a one way operation. Once privileges are * dropped, you cannot regain them. You may however reissue * this ioctl to shrink the allowed interfaces mask. */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a56c75e09786..f0dd08198d74 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -15,7 +15,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/completion.h> -#include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/list.h> #include <linux/slab.h> #include <linux/ioctl.h> diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 87fccf611b69..a5b7cd615698 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -23,6 +23,7 @@ #include <linux/export.h> #include <linux/hid.h> #include <linux/module.h> +#include <linux/sched/signal.h> #include <linux/uio.h> #include <asm/unaligned.h> diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 8f3659b65f53..4c8aacc232c0 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -207,6 +207,7 @@ #include <linux/fs.h> #include <linux/kref.h> #include <linux/kthread.h> +#include <linux/sched/signal.h> #include <linux/limits.h> #include <linux/rwsem.h> #include <linux/slab.h> diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 6bde4396927c..a2615d64d07c 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1848,7 +1848,7 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) fail: spin_unlock_irq (&dev->lock); - pr_debug ("%s: %s fail %Zd, %p\n", shortname, __func__, value, dev); + pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev); kfree (dev->buf); dev->buf = NULL; return value; diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 2e41ef36b944..b76fcdb763a0 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -520,7 +520,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, /* Setup qh structure and ep register for ep0. */ static void ep0_setup(struct fsl_udc *udc) { - /* the intialization of an ep includes: fields in QH, Regs, + /* the initialization of an ep includes: fields in QH, Regs, * fsl_ep struct */ struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL, USB_MAX_CTRL_PAYLOAD, 0, 0); @@ -2349,7 +2349,7 @@ static int struct_ep_setup(struct fsl_udc *udc, unsigned char index, } /* Driver probe function - * all intialization operations implemented here except enabling usb_intr reg + * all initialization operations implemented here except enabling usb_intr reg * board setup should have been done in the platform code */ static int fsl_udc_probe(struct platform_device *pdev) diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index fb8fc34827ab..2218f91e92a6 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -1791,7 +1791,7 @@ static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev, dev_dbg(dev, "%s: num_usb3_eps = %d\n", __func__, usb3->num_usb3_eps); /* - * This driver prepares pipes as the followings: + * This driver prepares pipes as follows: * - odd pipes = IN pipe * - even pipes = OUT pipe (except pipe 0) */ @@ -1841,7 +1841,7 @@ static void renesas_usb3_init_ram(struct renesas_usb3 *usb3, struct device *dev, memset(basead, 0, sizeof(basead)); /* - * This driver prepares pipes as the followings: + * This driver prepares pipes as follows: * - all pipes = the same size as "ramsize_per_pipe" * Please refer to the "Method of Specifying RAM Mapping" */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 063064801ceb..ac2c4eab478d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1322,7 +1322,7 @@ static int __init ehci_hcd_init(void) printk(KERN_WARNING "Warning! ehci_hcd should always be loaded" " before uhci_hcd and ohci_hcd, not after\n"); - pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n", + pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n", hcd_name, sizeof(struct ehci_qh), sizeof(struct ehci_qtd), sizeof(struct ehci_itd), sizeof(struct ehci_sitd)); diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 9d0b0518290a..1c5b34b74860 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -5697,7 +5697,7 @@ static int __init fotg210_hcd_init(void) test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); - pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n", + pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", hcd_name, sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), sizeof(struct fotg210_itd)); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 8685cf3e6292..b6daf2e69989 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1252,7 +1252,7 @@ static int __init ohci_hcd_mod_init(void) return -ENODEV; printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name); - pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name, + pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name, sizeof (struct ed), sizeof (struct td)); set_bit(USB_OHCI_LOADED, &usb_hcds_loaded); diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 5cf2633cdb04..e92540a21b6b 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -85,7 +85,7 @@ * (20/10/1999) */ -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/signal.h> #include <linux/spinlock.h> #include <linux/errno.h> diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index a540e4f206c4..db9a9e6ff6be 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -21,6 +21,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/module.h> @@ -563,20 +564,20 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, } dev_dbg(&dev->udev->dev, - "%s : in progress, count = %Zd\n", + "%s : in progress, count = %zd\n", __func__, count); } else { spin_unlock_irqrestore(&dev->buflock, flags); set_current_state(TASK_RUNNING); remove_wait_queue(&dev->write_wait, &waita); - dev_dbg(&dev->udev->dev, "%s : sending, count = %Zd\n", + dev_dbg(&dev->udev->dev, "%s : sending, count = %zd\n", __func__, count); /* write the data into interrupt_out_buffer from userspace */ buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); bytes_to_write = count > buffer_size ? buffer_size : count; dev_dbg(&dev->udev->dev, - "%s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd\n", + "%s : buffer_size = %zd, count = %zd, bytes_to_write = %zd\n", __func__, buffer_size, count, bytes_to_write); if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index debc1fd74b0d..8b9fd7534f69 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -17,6 +17,7 @@ */ #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/errno.h> #include <linux/delay.h> #include <linux/slab.h> diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index b10e26c74a90..322a042d6e59 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -673,7 +673,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t /* write the data into interrupt_out_buffer from userspace */ bytes_to_write = min_t(int, count, write_buffer_size); - dev_dbg(&dev->udev->dev, "%s: count = %Zd, bytes_to_write = %Zd\n", + dev_dbg(&dev->udev->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write); if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index fc329c98a6e8..b106ce76997b 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -31,7 +31,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/signal.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/mutex.h> #include <linux/errno.h> #include <linux/random.h> diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 356d312add57..e45a3a680db8 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -50,6 +50,7 @@ #include <linux/completion.h> #include <linux/kref.h> #include <linux/slab.h> +#include <linux/sched/signal.h> /* * Version Information @@ -526,7 +527,7 @@ static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, return 0; i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buf, length, &rlen, 20000); if (i) - printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buf, length, rlen); + printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %zu rlen %u\n", buf, length, rlen); change_mode(pp, ECR_PS2); return rlen; #endif @@ -587,7 +588,7 @@ static size_t parport_uss720_ecp_write_data(struct parport *pp, const void *buff return 0; i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000); if (i) - printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen); + printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %zu rlen %u\n", buffer, len, rlen); change_mode(pp, ECR_PS2); return rlen; } @@ -605,7 +606,7 @@ static size_t parport_uss720_ecp_read_data(struct parport *pp, void *buffer, siz return 0; i = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, 20000); if (i) - printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %Zu rlen %u\n", buffer, len, rlen); + printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %zu rlen %u\n", buffer, len, rlen); change_mode(pp, ECR_PS2); return rlen; } @@ -638,7 +639,7 @@ static size_t parport_uss720_write_compat(struct parport *pp, const void *buffer return 0; i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000); if (i) - printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen); + printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %zu rlen %u\n", buffer, len, rlen); change_mode(pp, ECR_PS2); return rlen; } diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 91c22276c03b..b6d8bf475c92 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -8,6 +8,7 @@ */ #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/cdev.h> @@ -1223,9 +1224,9 @@ static void mon_bin_vma_close(struct vm_area_struct *vma) /* * Map ring pages to user space. */ -static int mon_bin_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int mon_bin_vma_fault(struct vm_fault *vmf) { - struct mon_reader_bin *rp = vma->vm_private_data; + struct mon_reader_bin *rp = vmf->vma->vm_private_data; unsigned long offset, chunk_idx; struct page *pageptr; diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index db1a4abf2806..19c416d69eb9 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -8,6 +8,7 @@ #include <linux/list.h> #include <linux/usb.h> #include <linux/slab.h> +#include <linux/sched/signal.h> #include <linux/time.h> #include <linux/ktime.h> #include <linux/export.h> diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index eb433922598c..ab78111e0968 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -27,6 +27,7 @@ #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/wait.h> +#include <linux/sched/signal.h> #include <linux/usb/serial.h> /* Defines */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 944de657a07a..49ce2be90fa0 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -10,6 +10,7 @@ */ #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/sysrq.h> diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 8b232290be6b..cab2b71a80d0 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -327,13 +327,11 @@ EXPORT_SYMBOL_GPL(usbip_dump_header); int usbip_recv(struct socket *sock, void *buf, int size) { int result; - struct msghdr msg; - struct kvec iov; + struct kvec iov = {.iov_base = buf, .iov_len = size}; + struct msghdr msg = {.msg_flags = MSG_NOSIGNAL}; int total = 0; - /* for blocks of if (usbip_dbg_flag_xmit) */ - char *bp = buf; - int osize = size; + iov_iter_kvec(&msg.msg_iter, READ|ITER_KVEC, &iov, 1, size); usbip_dbg_xmit("enter\n"); @@ -344,26 +342,18 @@ int usbip_recv(struct socket *sock, void *buf, int size) } do { + int sz = msg_data_left(&msg); sock->sk->sk_allocation = GFP_NOIO; - iov.iov_base = buf; - iov.iov_len = size; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = MSG_NOSIGNAL; - - result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL); + + result = sock_recvmsg(sock, &msg, MSG_WAITALL); if (result <= 0) { pr_debug("receive sock %p buf %p size %u ret %d total %d\n", - sock, buf, size, result, total); + sock, buf + total, sz, result, total); goto err; } - size -= result; - buf += result; total += result; - } while (size > 0); + } while (msg_data_left(&msg)); if (usbip_dbg_flag_xmit) { if (!in_interrupt()) @@ -372,9 +362,9 @@ int usbip_recv(struct socket *sock, void *buf, int size) pr_debug("interrupt :"); pr_debug("receiving....\n"); - usbip_dump_buffer(bp, osize); - pr_debug("received, osize %d ret %d size %d total %d\n", - osize, result, size, total); + usbip_dump_buffer(buf, size); + pr_debug("received, osize %d ret %d size %zd total %d\n", + size, result, msg_data_left(&msg), total); } return total; @@ -707,7 +697,7 @@ void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) return; /* - * loop over all packets from last to first (to prevent overwritting + * loop over all packets from last to first (to prevent overwriting * memory when padding) and move them into the proper place */ for (i = np-1; i > 0; i--) { diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 9f490375ac92..f8573a52e41a 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -31,6 +31,7 @@ #include <linux/types.h> #include <linux/usb.h> #include <linux/wait.h> +#include <linux/sched/task.h> #include <uapi/linux/usbip.h> #define USBIP_VERSION "1.0.0" diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 23eced02aaf6..c84333eb5eb5 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -6,12 +6,12 @@ config VFIO_IOMMU_TYPE1 config VFIO_IOMMU_SPAPR_TCE tristate depends on VFIO && SPAPR_TCE_IOMMU - default n + default VFIO config VFIO_SPAPR_EEH tristate depends on EEH && VFIO_IOMMU_SPAPR_TCE - default n + default VFIO config VFIO_VIRQFD tristate @@ -22,8 +22,6 @@ menuconfig VFIO tristate "VFIO Non-Privileged userspace driver framework" depends on IOMMU_API select VFIO_IOMMU_TYPE1 if (X86 || S390 || ARM_SMMU || ARM_SMMU_V3) - select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES) - select VFIO_SPAPR_EEH if (PPC_POWERNV || PPC_PSERIES) select ANON_INODES help VFIO provides a framework for secure userspace device drivers. diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index 36d75c367d22..126991046eb7 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -422,18 +422,7 @@ int mdev_device_remove(struct device *dev, bool force_remove) static int __init mdev_init(void) { - int ret; - - ret = mdev_bus_register(); - - /* - * Attempt to load known vfio_mdev. This gives us a working environment - * without the user needing to explicitly load vfio_mdev driver. - */ - if (!ret) - request_module_nowait("vfio_mdev"); - - return ret; + return mdev_bus_register(); } static void __exit mdev_exit(void) @@ -451,3 +440,4 @@ MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_SOFTDEP("post: vfio_mdev"); diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 9901c4671e2f..609f4f982c74 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -1917,7 +1917,7 @@ EXPORT_SYMBOL(vfio_set_irqs_validate_and_prepare); * Pin a set of guest PFNs and return their associated host PFNs for local * domain only. * @dev [in] : device - * @user_pfn [in]: array of user/guest PFNs to be unpinned. + * @user_pfn [in]: array of user/guest PFNs to be pinned. * @npage [in] : count of elements in user_pfn array. This count should not * be greater VFIO_PIN_PAGES_MAX_ENTRIES. * @prot [in] : protection flags @@ -2250,14 +2250,6 @@ static int __init vfio_init(void) pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - /* - * Attempt to load known iommu-drivers. This gives us a working - * environment without the user needing to explicitly load iommu - * drivers. - */ - request_module_nowait("vfio_iommu_type1"); - request_module_nowait("vfio_iommu_spapr_tce"); - #ifdef CONFIG_VFIO_NOIOMMU vfio_register_iommu_driver(&vfio_noiommu_ops); #endif @@ -2297,3 +2289,4 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_ALIAS_MISCDEV(VFIO_MINOR); MODULE_ALIAS("devname:vfio/vfio"); +MODULE_SOFTDEP("post: vfio_iommu_type1 vfio_iommu_spapr_tce"); diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index 59b3f62a2d64..cf3de91fbfe7 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c @@ -20,6 +20,9 @@ #include <linux/err.h> #include <linux/vfio.h> #include <linux/vmalloc.h> +#include <linux/sched/mm.h> +#include <linux/sched/signal.h> + #include <asm/iommu.h> #include <asm/tce.h> #include <asm/mmu_context.h> diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index bd6f293c4ebd..c26fa1f3ed86 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -31,7 +31,8 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/rbtree.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/mm.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/vfio.h> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 2fe35354f20e..9b519897cc17 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -17,6 +17,8 @@ #include <linux/workqueue.h> #include <linux/file.h> #include <linux/slab.h> +#include <linux/sched/clock.h> +#include <linux/sched/signal.h> #include <linux/vmalloc.h> #include <linux/net.h> diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 4269e621e254..f0ba362d4c10 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -27,6 +27,8 @@ #include <linux/cgroup.h> #include <linux/module.h> #include <linux/sort.h> +#include <linux/sched/mm.h> +#include <linux/sched/signal.h> #include <linux/interval_tree_generic.h> #include "vhost.h" @@ -282,6 +284,22 @@ void vhost_poll_queue(struct vhost_poll *poll) } EXPORT_SYMBOL_GPL(vhost_poll_queue); +static void __vhost_vq_meta_reset(struct vhost_virtqueue *vq) +{ + int j; + + for (j = 0; j < VHOST_NUM_ADDRS; j++) + vq->meta_iotlb[j] = NULL; +} + +static void vhost_vq_meta_reset(struct vhost_dev *d) +{ + int i; + + for (i = 0; i < d->nvqs; ++i) + __vhost_vq_meta_reset(d->vqs[i]); +} + static void vhost_vq_reset(struct vhost_dev *dev, struct vhost_virtqueue *vq) { @@ -312,6 +330,7 @@ static void vhost_vq_reset(struct vhost_dev *dev, vq->busyloop_timeout = 0; vq->umem = NULL; vq->iotlb = NULL; + __vhost_vq_meta_reset(vq); } static int vhost_worker(void *data) @@ -691,6 +710,18 @@ static int vq_memory_access_ok(void __user *log_base, struct vhost_umem *umem, return 1; } +static inline void __user *vhost_vq_meta_fetch(struct vhost_virtqueue *vq, + u64 addr, unsigned int size, + int type) +{ + const struct vhost_umem_node *node = vq->meta_iotlb[type]; + + if (!node) + return NULL; + + return (void *)(uintptr_t)(node->userspace_addr + addr - node->start); +} + /* Can we switch to this memory table? */ /* Caller should have device mutex but not vq mutex */ static int memory_access_ok(struct vhost_dev *d, struct vhost_umem *umem, @@ -733,8 +764,14 @@ static int vhost_copy_to_user(struct vhost_virtqueue *vq, void __user *to, * could be access through iotlb. So -EAGAIN should * not happen in this case. */ - /* TODO: more fast path */ struct iov_iter t; + void __user *uaddr = vhost_vq_meta_fetch(vq, + (u64)(uintptr_t)to, size, + VHOST_ADDR_DESC); + + if (uaddr) + return __copy_to_user(uaddr, from, size); + ret = translate_desc(vq, (u64)(uintptr_t)to, size, vq->iotlb_iov, ARRAY_SIZE(vq->iotlb_iov), VHOST_ACCESS_WO); @@ -762,8 +799,14 @@ static int vhost_copy_from_user(struct vhost_virtqueue *vq, void *to, * could be access through iotlb. So -EAGAIN should * not happen in this case. */ - /* TODO: more fast path */ + void __user *uaddr = vhost_vq_meta_fetch(vq, + (u64)(uintptr_t)from, size, + VHOST_ADDR_DESC); struct iov_iter f; + + if (uaddr) + return __copy_from_user(to, uaddr, size); + ret = translate_desc(vq, (u64)(uintptr_t)from, size, vq->iotlb_iov, ARRAY_SIZE(vq->iotlb_iov), VHOST_ACCESS_RO); @@ -783,17 +826,12 @@ out: return ret; } -static void __user *__vhost_get_user(struct vhost_virtqueue *vq, - void __user *addr, unsigned size) +static void __user *__vhost_get_user_slow(struct vhost_virtqueue *vq, + void __user *addr, unsigned int size, + int type) { int ret; - /* This function should be called after iotlb - * prefetch, which means we're sure that vq - * could be access through iotlb. So -EAGAIN should - * not happen in this case. - */ - /* TODO: more fast path */ ret = translate_desc(vq, (u64)(uintptr_t)addr, size, vq->iotlb_iov, ARRAY_SIZE(vq->iotlb_iov), VHOST_ACCESS_RO); @@ -814,14 +852,32 @@ static void __user *__vhost_get_user(struct vhost_virtqueue *vq, return vq->iotlb_iov[0].iov_base; } -#define vhost_put_user(vq, x, ptr) \ +/* This function should be called after iotlb + * prefetch, which means we're sure that vq + * could be access through iotlb. So -EAGAIN should + * not happen in this case. + */ +static inline void __user *__vhost_get_user(struct vhost_virtqueue *vq, + void *addr, unsigned int size, + int type) +{ + void __user *uaddr = vhost_vq_meta_fetch(vq, + (u64)(uintptr_t)addr, size, type); + if (uaddr) + return uaddr; + + return __vhost_get_user_slow(vq, addr, size, type); +} + +#define vhost_put_user(vq, x, ptr) \ ({ \ int ret = -EFAULT; \ if (!vq->iotlb) { \ ret = __put_user(x, ptr); \ } else { \ __typeof__(ptr) to = \ - (__typeof__(ptr)) __vhost_get_user(vq, ptr, sizeof(*ptr)); \ + (__typeof__(ptr)) __vhost_get_user(vq, ptr, \ + sizeof(*ptr), VHOST_ADDR_USED); \ if (to != NULL) \ ret = __put_user(x, to); \ else \ @@ -830,14 +886,16 @@ static void __user *__vhost_get_user(struct vhost_virtqueue *vq, ret; \ }) -#define vhost_get_user(vq, x, ptr) \ +#define vhost_get_user(vq, x, ptr, type) \ ({ \ int ret; \ if (!vq->iotlb) { \ ret = __get_user(x, ptr); \ } else { \ __typeof__(ptr) from = \ - (__typeof__(ptr)) __vhost_get_user(vq, ptr, sizeof(*ptr)); \ + (__typeof__(ptr)) __vhost_get_user(vq, ptr, \ + sizeof(*ptr), \ + type); \ if (from != NULL) \ ret = __get_user(x, from); \ else \ @@ -846,6 +904,12 @@ static void __user *__vhost_get_user(struct vhost_virtqueue *vq, ret; \ }) +#define vhost_get_avail(vq, x, ptr) \ + vhost_get_user(vq, x, ptr, VHOST_ADDR_AVAIL) + +#define vhost_get_used(vq, x, ptr) \ + vhost_get_user(vq, x, ptr, VHOST_ADDR_USED) + static void vhost_dev_lock_vqs(struct vhost_dev *d) { int i = 0; @@ -951,6 +1015,7 @@ static int vhost_process_iotlb_msg(struct vhost_dev *dev, ret = -EFAULT; break; } + vhost_vq_meta_reset(dev); if (vhost_new_umem_range(dev->iotlb, msg->iova, msg->size, msg->iova + msg->size - 1, msg->uaddr, msg->perm)) { @@ -960,6 +1025,7 @@ static int vhost_process_iotlb_msg(struct vhost_dev *dev, vhost_iotlb_notify_vq(dev, msg); break; case VHOST_IOTLB_INVALIDATE: + vhost_vq_meta_reset(dev); vhost_del_umem_range(dev->iotlb, msg->iova, msg->iova + msg->size - 1); break; @@ -1103,12 +1169,26 @@ static int vq_access_ok(struct vhost_virtqueue *vq, unsigned int num, sizeof *used + num * sizeof *used->ring + s); } +static void vhost_vq_meta_update(struct vhost_virtqueue *vq, + const struct vhost_umem_node *node, + int type) +{ + int access = (type == VHOST_ADDR_USED) ? + VHOST_ACCESS_WO : VHOST_ACCESS_RO; + + if (likely(node->perm & access)) + vq->meta_iotlb[type] = node; +} + static int iotlb_access_ok(struct vhost_virtqueue *vq, - int access, u64 addr, u64 len) + int access, u64 addr, u64 len, int type) { const struct vhost_umem_node *node; struct vhost_umem *umem = vq->iotlb; - u64 s = 0, size; + u64 s = 0, size, orig_addr = addr; + + if (vhost_vq_meta_fetch(vq, addr, len, type)) + return true; while (len > s) { node = vhost_umem_interval_tree_iter_first(&umem->umem_tree, @@ -1125,6 +1205,10 @@ static int iotlb_access_ok(struct vhost_virtqueue *vq, } size = node->size - addr + node->start; + + if (orig_addr == addr && size >= len) + vhost_vq_meta_update(vq, node, type); + s += size; addr += size; } @@ -1141,13 +1225,15 @@ int vq_iotlb_prefetch(struct vhost_virtqueue *vq) return 1; return iotlb_access_ok(vq, VHOST_ACCESS_RO, (u64)(uintptr_t)vq->desc, - num * sizeof *vq->desc) && + num * sizeof(*vq->desc), VHOST_ADDR_DESC) && iotlb_access_ok(vq, VHOST_ACCESS_RO, (u64)(uintptr_t)vq->avail, sizeof *vq->avail + - num * sizeof *vq->avail->ring + s) && + num * sizeof(*vq->avail->ring) + s, + VHOST_ADDR_AVAIL) && iotlb_access_ok(vq, VHOST_ACCESS_WO, (u64)(uintptr_t)vq->used, sizeof *vq->used + - num * sizeof *vq->used->ring + s); + num * sizeof(*vq->used->ring) + s, + VHOST_ADDR_USED); } EXPORT_SYMBOL_GPL(vq_iotlb_prefetch); @@ -1728,7 +1814,7 @@ int vhost_vq_init_access(struct vhost_virtqueue *vq) r = -EFAULT; goto err; } - r = vhost_get_user(vq, last_used_idx, &vq->used->idx); + r = vhost_get_used(vq, last_used_idx, &vq->used->idx); if (r) { vq_err(vq, "Can't access used idx at %p\n", &vq->used->idx); @@ -1930,29 +2016,36 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq, /* Check it isn't doing very strange things with descriptor numbers. */ last_avail_idx = vq->last_avail_idx; - if (unlikely(vhost_get_user(vq, avail_idx, &vq->avail->idx))) { - vq_err(vq, "Failed to access avail idx at %p\n", - &vq->avail->idx); - return -EFAULT; - } - vq->avail_idx = vhost16_to_cpu(vq, avail_idx); - if (unlikely((u16)(vq->avail_idx - last_avail_idx) > vq->num)) { - vq_err(vq, "Guest moved used index from %u to %u", - last_avail_idx, vq->avail_idx); - return -EFAULT; - } + if (vq->avail_idx == vq->last_avail_idx) { + if (unlikely(vhost_get_avail(vq, avail_idx, &vq->avail->idx))) { + vq_err(vq, "Failed to access avail idx at %p\n", + &vq->avail->idx); + return -EFAULT; + } + vq->avail_idx = vhost16_to_cpu(vq, avail_idx); - /* If there's nothing new since last we looked, return invalid. */ - if (vq->avail_idx == last_avail_idx) - return vq->num; + if (unlikely((u16)(vq->avail_idx - last_avail_idx) > vq->num)) { + vq_err(vq, "Guest moved used index from %u to %u", + last_avail_idx, vq->avail_idx); + return -EFAULT; + } + + /* If there's nothing new since last we looked, return + * invalid. + */ + if (vq->avail_idx == last_avail_idx) + return vq->num; - /* Only get avail ring entries after they have been exposed by guest. */ - smp_rmb(); + /* Only get avail ring entries after they have been + * exposed by guest. + */ + smp_rmb(); + } /* Grab the next descriptor number they're advertising, and increment * the index we've seen. */ - if (unlikely(vhost_get_user(vq, ring_head, + if (unlikely(vhost_get_avail(vq, ring_head, &vq->avail->ring[last_avail_idx & (vq->num - 1)]))) { vq_err(vq, "Failed to read head: idx %d address %p\n", last_avail_idx, @@ -2168,7 +2261,7 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) * with the barrier that the Guest executes when enabling * interrupts. */ smp_mb(); - if (vhost_get_user(vq, flags, &vq->avail->flags)) { + if (vhost_get_avail(vq, flags, &vq->avail->flags)) { vq_err(vq, "Failed to get flags"); return true; } @@ -2195,7 +2288,7 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) * interrupts. */ smp_mb(); - if (vhost_get_user(vq, event, vhost_used_event(vq))) { + if (vhost_get_avail(vq, event, vhost_used_event(vq))) { vq_err(vq, "Failed to get used event idx"); return true; } @@ -2242,7 +2335,7 @@ bool vhost_vq_avail_empty(struct vhost_dev *dev, struct vhost_virtqueue *vq) if (vq->avail_idx != vq->last_avail_idx) return false; - r = vhost_get_user(vq, avail_idx, &vq->avail->idx); + r = vhost_get_avail(vq, avail_idx, &vq->avail->idx); if (unlikely(r)) return false; vq->avail_idx = vhost16_to_cpu(vq, avail_idx); @@ -2278,7 +2371,7 @@ bool vhost_enable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) /* They could have slipped one in as we were doing that: make * sure it's written, then check again. */ smp_mb(); - r = vhost_get_user(vq, avail_idx, &vq->avail->idx); + r = vhost_get_avail(vq, avail_idx, &vq->avail->idx); if (r) { vq_err(vq, "Failed to check avail idx at %p: %d\n", &vq->avail->idx, r); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index a9cbbb148f46..f55671d53f28 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -76,6 +76,13 @@ struct vhost_umem { int numem; }; +enum vhost_uaddr_type { + VHOST_ADDR_DESC = 0, + VHOST_ADDR_AVAIL = 1, + VHOST_ADDR_USED = 2, + VHOST_NUM_ADDRS = 3, +}; + /* The virtqueue structure describes a queue attached to a device. */ struct vhost_virtqueue { struct vhost_dev *dev; @@ -86,6 +93,7 @@ struct vhost_virtqueue { struct vring_desc __user *desc; struct vring_avail __user *avail; struct vring_used __user *used; + const struct vhost_umem_node *meta_iotlb[VHOST_NUM_ADDRS]; struct file *kick; struct file *call; struct file *error; diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c index dd88ba1d71ce..35373e2065b2 100644 --- a/drivers/video/backlight/adp5520_bl.c +++ b/drivers/video/backlight/adp5520_bl.c @@ -332,10 +332,18 @@ static int adp5520_bl_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, bl); - ret |= adp5520_bl_setup(bl); + ret = adp5520_bl_setup(bl); + if (ret) { + dev_err(&pdev->dev, "failed to setup\n"); + if (data->pdata->en_ambl_sens) + sysfs_remove_group(&bl->dev.kobj, + &adp5520_bl_attr_group); + return ret; + } + backlight_update_status(bl); - return ret; + return 0; } static int adp5520_bl_remove(struct platform_device *pdev) diff --git a/drivers/video/backlight/da9052_bl.c b/drivers/video/backlight/da9052_bl.c index fd2be417aa64..49035c12739a 100644 --- a/drivers/video/backlight/da9052_bl.c +++ b/drivers/video/backlight/da9052_bl.c @@ -167,6 +167,7 @@ static const struct platform_device_id da9052_wled_ids[] = { }, { }, }; +MODULE_DEVICE_TABLE(platform, da9052_wled_ids); static struct platform_driver da9052_wled_driver = { .probe = da9052_backlight_probe, @@ -182,4 +183,3 @@ module_platform_driver(da9052_wled_driver); MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); MODULE_DESCRIPTION("Backlight driver for DA9052 PMIC"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:da9052-backlight"); diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 7de847df224f..4b40c6a4d441 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -226,6 +226,8 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, dev_set_name(&new_ld->dev, "%s", name); dev_set_drvdata(&new_ld->dev, devdata); + new_ld->ops = ops; + rc = device_register(&new_ld->dev); if (rc) { put_device(&new_ld->dev); @@ -238,8 +240,6 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, return ERR_PTR(rc); } - new_ld->ops = ops; - return new_ld; } EXPORT_SYMBOL(lcd_device_register); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 12614006211e..d7efcb632f7d 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -192,6 +192,36 @@ static int pwm_backlight_parse_dt(struct device *dev, } #endif +static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb) +{ + struct device_node *node = pb->dev->of_node; + + /* Not booted with device tree or no phandle link to the node */ + if (!node || !node->phandle) + return FB_BLANK_UNBLANK; + + /* + * If the driver is probed from the device tree and there is a + * phandle link pointing to the backlight node, it is safe to + * assume that another driver will enable the backlight at the + * appropriate time. Therefore, if it is disabled, keep it so. + */ + + /* if the enable GPIO is disabled, do not enable the backlight */ + if (pb->enable_gpio && gpiod_get_value(pb->enable_gpio) == 0) + return FB_BLANK_POWERDOWN; + + /* The regulator is disabled, do not enable the backlight */ + if (!regulator_is_enabled(pb->power_supply)) + return FB_BLANK_POWERDOWN; + + /* The PWM is disabled, keep it like this */ + if (!pwm_is_enabled(pb->pwm)) + return FB_BLANK_POWERDOWN; + + return FB_BLANK_UNBLANK; +} + static int pwm_backlight_probe(struct platform_device *pdev) { struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev); @@ -200,7 +230,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) struct backlight_device *bl; struct device_node *node = pdev->dev.of_node; struct pwm_bl_data *pb; - int initial_blank = FB_BLANK_UNBLANK; struct pwm_args pargs; int ret; @@ -267,20 +296,16 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->enable_gpio = gpio_to_desc(data->enable_gpio); } - if (pb->enable_gpio) { - /* - * If the driver is probed from the device tree and there is a - * phandle link pointing to the backlight node, it is safe to - * assume that another driver will enable the backlight at the - * appropriate time. Therefore, if it is disabled, keep it so. - */ - if (node && node->phandle && - gpiod_get_direction(pb->enable_gpio) == GPIOF_DIR_OUT && - gpiod_get_value(pb->enable_gpio) == 0) - initial_blank = FB_BLANK_POWERDOWN; - else - gpiod_direction_output(pb->enable_gpio, 1); - } + /* + * If the GPIO is configured as input, change the direction to output + * and set the GPIO as active. + * Do not force the GPIO to active when it was already output as it + * could cause backlight flickering or we would enable the backlight too + * early. Leave the decision of the initial backlight state for later. + */ + if (pb->enable_gpio && + gpiod_get_direction(pb->enable_gpio) == GPIOF_DIR_IN) + gpiod_direction_output(pb->enable_gpio, 1); pb->power_supply = devm_regulator_get(&pdev->dev, "power"); if (IS_ERR(pb->power_supply)) { @@ -288,9 +313,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) goto err_alloc; } - if (node && node->phandle && !regulator_is_enabled(pb->power_supply)) - initial_blank = FB_BLANK_POWERDOWN; - pb->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) { dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n"); @@ -347,7 +369,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) } bl->props.brightness = data->dft_brightness; - bl->props.power = initial_blank; + bl->props.power = pwm_backlight_initial_power_state(pb); backlight_update_status(bl); platform_set_drvdata(pdev, bl); diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index a44f5627b82a..12ded23f1aaf 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -412,11 +412,9 @@ static void fbcon_add_cursor_timer(struct fb_info *info) if (!info->queue.func) INIT_WORK(&info->queue, fb_flashcursor); - init_timer(&ops->cursor_timer); - ops->cursor_timer.function = cursor_timer_handler; - ops->cursor_timer.expires = jiffies + ops->cur_blink_jiffies; - ops->cursor_timer.data = (unsigned long ) info; - add_timer(&ops->cursor_timer); + setup_timer(&ops->cursor_timer, cursor_timer_handler, + (unsigned long) info); + mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); ops->flags |= FBCON_FLAGS_CURSOR_TIMER; } } @@ -1165,6 +1163,8 @@ static void fbcon_free_font(struct display *p, bool freefont) p->userfont = 0; } +static void set_vc_hi_font(struct vc_data *vc, bool set); + static void fbcon_deinit(struct vc_data *vc) { struct display *p = &fb_display[vc->vc_num]; @@ -1200,6 +1200,9 @@ finished: if (free_font) vc->vc_font.data = NULL; + if (vc->vc_hi_font_mask) + set_vc_hi_font(vc, false); + if (!con_is_bound(&fb_con)) fbcon_exit(); @@ -2436,32 +2439,10 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) return 0; } -static int fbcon_do_set_font(struct vc_data *vc, int w, int h, - const u8 * data, int userfont) +/* set/clear vc_hi_font_mask and update vc attrs accordingly */ +static void set_vc_hi_font(struct vc_data *vc, bool set) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - struct display *p = &fb_display[vc->vc_num]; - int resize; - int cnt; - char *old_data = NULL; - - if (con_is_visible(vc) && softback_lines) - fbcon_set_origin(vc); - - resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); - if (p->userfont) - old_data = vc->vc_font.data; - if (userfont) - cnt = FNTCHARCNT(data); - else - cnt = 256; - vc->vc_font.data = (void *)(p->fontdata = data); - if ((p->userfont = userfont)) - REFCOUNT(data)++; - vc->vc_font.width = w; - vc->vc_font.height = h; - if (vc->vc_hi_font_mask && cnt == 256) { + if (!set) { vc->vc_hi_font_mask = 0; if (vc->vc_can_do_color) { vc->vc_complement_mask >>= 1; @@ -2484,7 +2465,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, ((c & 0xfe00) >> 1) | (c & 0xff); vc->vc_attr >>= 1; } - } else if (!vc->vc_hi_font_mask && cnt == 512) { + } else { vc->vc_hi_font_mask = 0x100; if (vc->vc_can_do_color) { vc->vc_complement_mask <<= 1; @@ -2516,8 +2497,38 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, } else vc->vc_video_erase_char = c & ~0x100; } - } +} + +static int fbcon_do_set_font(struct vc_data *vc, int w, int h, + const u8 * data, int userfont) +{ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + struct display *p = &fb_display[vc->vc_num]; + int resize; + int cnt; + char *old_data = NULL; + + if (con_is_visible(vc) && softback_lines) + fbcon_set_origin(vc); + + resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); + if (p->userfont) + old_data = vc->vc_font.data; + if (userfont) + cnt = FNTCHARCNT(data); + else + cnt = 256; + vc->vc_font.data = (void *)(p->fontdata = data); + if ((p->userfont = userfont)) + REFCOUNT(data)++; + vc->vc_font.width = w; + vc->vc_font.height = h; + if (vc->vc_hi_font_mask && cnt == 256) + set_vc_hi_font(vc, false); + else if (!vc->vc_hi_font_mask && cnt == 512) + set_vc_hi_font(vc, true); if (resize) { int cols, rows; diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5d3b0db5ce0a..922e4eaed9c5 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -138,6 +138,14 @@ config FB_SYS_IMAGEBLIT blitting. This is used by drivers that don't provide their own (accelerated) version and the framebuffer is in system RAM. +config FB_PROVIDE_GET_FB_UNMAPPED_AREA + bool + depends on FB + default n + ---help--- + Allow generic frame-buffer to provide get_fb_unmapped_area + function. + menuconfig FB_FOREIGN_ENDIAN bool "Framebuffer foreign endianness support" depends on FB diff --git a/drivers/video/fbdev/amba-clcd-nomadik.c b/drivers/video/fbdev/amba-clcd-nomadik.c index 0c06fcaaa6e8..cd2db1113e67 100644 --- a/drivers/video/fbdev/amba-clcd-nomadik.c +++ b/drivers/video/fbdev/amba-clcd-nomadik.c @@ -184,45 +184,37 @@ static void tpg110_init(struct device *dev, struct device_node *np, { dev_info(dev, "TPG110 display init\n"); - grestb = devm_get_gpiod_from_child(dev, "grestb", &np->fwnode); + /* This asserts the GRESTB signal, putting the display into reset */ + grestb = devm_fwnode_get_gpiod_from_child(dev, "grestb", &np->fwnode, + GPIOD_OUT_HIGH, "grestb"); if (IS_ERR(grestb)) { dev_err(dev, "no GRESTB GPIO\n"); return; } - /* This asserts the GRESTB signal, putting the display into reset */ - gpiod_direction_output(grestb, 1); - - scen = devm_get_gpiod_from_child(dev, "scen", &np->fwnode); + scen = devm_fwnode_get_gpiod_from_child(dev, "scen", &np->fwnode, + GPIOD_OUT_LOW, "scen"); if (IS_ERR(scen)) { dev_err(dev, "no SCEN GPIO\n"); return; } - gpiod_direction_output(scen, 0); - scl = devm_get_gpiod_from_child(dev, "scl", &np->fwnode); + scl = devm_fwnode_get_gpiod_from_child(dev, "scl", &np->fwnode, + GPIOD_OUT_LOW, "scl"); if (IS_ERR(scl)) { dev_err(dev, "no SCL GPIO\n"); return; } - gpiod_direction_output(scl, 0); - sda = devm_get_gpiod_from_child(dev, "sda", &np->fwnode); + sda = devm_fwnode_get_gpiod_from_child(dev, "sda", &np->fwnode, + GPIOD_OUT_LOW, "sda"); if (IS_ERR(sda)) { dev_err(dev, "no SDA GPIO\n"); return; } - gpiod_direction_output(sda, 0); board->enable = tpg110_enable; board->disable = tpg110_disable; } -int nomadik_clcd_init_panel(struct clcd_fb *fb, - struct device_node *endpoint) +int nomadik_clcd_init_panel(struct clcd_fb *fb, struct device_node *panel) { - struct device_node *panel; - - panel = of_graph_get_remote_port_parent(endpoint); - if (!panel) - return -ENODEV; - if (of_device_is_compatible(panel, "tpo,tpg110")) tpg110_init(&fb->dev->dev, panel, fb->board); else diff --git a/drivers/video/fbdev/amba-clcd-nomadik.h b/drivers/video/fbdev/amba-clcd-nomadik.h index 50aa9bda69fd..a24032c8156e 100644 --- a/drivers/video/fbdev/amba-clcd-nomadik.h +++ b/drivers/video/fbdev/amba-clcd-nomadik.h @@ -6,8 +6,7 @@ #ifdef CONFIG_ARCH_NOMADIK int nomadik_clcd_init_board(struct amba_device *adev, struct clcd_board *board); -int nomadik_clcd_init_panel(struct clcd_fb *fb, - struct device_node *endpoint); +int nomadik_clcd_init_panel(struct clcd_fb *fb, struct device_node *panel); #else static inline int nomadik_clcd_init_board(struct amba_device *adev, struct clcd_board *board) @@ -15,7 +14,7 @@ static inline int nomadik_clcd_init_board(struct amba_device *adev, return 0; } static inline int nomadik_clcd_init_panel(struct clcd_fb *fb, - struct device_node *endpoint) + struct device_node *panel) { return 0; } diff --git a/drivers/video/fbdev/amba-clcd-versatile.c b/drivers/video/fbdev/amba-clcd-versatile.c index e5d9bfc1703a..d42047dc4e4e 100644 --- a/drivers/video/fbdev/amba-clcd-versatile.c +++ b/drivers/video/fbdev/amba-clcd-versatile.c @@ -452,11 +452,9 @@ static const struct versatile_panel versatile_panels[] = { }, }; -static void versatile_panel_probe(struct device *dev, - struct device_node *endpoint) +static void versatile_panel_probe(struct device *dev, struct device_node *panel) { struct versatile_panel const *vpanel = NULL; - struct device_node *panel = NULL; u32 val; int ret; int i; @@ -488,11 +486,6 @@ static void versatile_panel_probe(struct device *dev, return; } - panel = of_graph_get_remote_port_parent(endpoint); - if (!panel) { - dev_err(dev, "could not locate panel in DT\n"); - return; - } if (!of_device_is_compatible(panel, vpanel->compatible)) dev_err(dev, "panel in DT is not compatible with the " "auto-detected panel, continuing anyway\n"); @@ -514,8 +507,7 @@ static void versatile_panel_probe(struct device *dev, } } -int versatile_clcd_init_panel(struct clcd_fb *fb, - struct device_node *endpoint) +int versatile_clcd_init_panel(struct clcd_fb *fb, struct device_node *panel) { const struct of_device_id *clcd_id; enum versatile_clcd versatile_clcd_type; @@ -551,7 +543,7 @@ int versatile_clcd_init_panel(struct clcd_fb *fb, fb->board->enable = versatile_clcd_enable; fb->board->disable = versatile_clcd_disable; fb->board->decode = versatile_clcd_decode; - versatile_panel_probe(dev, endpoint); + versatile_panel_probe(dev, panel); dev_info(dev, "set up callbacks for Versatile\n"); break; case REALVIEW_CLCD_EB: diff --git a/drivers/video/fbdev/amba-clcd-versatile.h b/drivers/video/fbdev/amba-clcd-versatile.h index 1b14359c2cf6..4692c3092823 100644 --- a/drivers/video/fbdev/amba-clcd-versatile.h +++ b/drivers/video/fbdev/amba-clcd-versatile.h @@ -6,11 +6,10 @@ #include <linux/platform_data/video-clcd-versatile.h> #if defined(CONFIG_PLAT_VERSATILE_CLCD) && defined(CONFIG_OF) -int versatile_clcd_init_panel(struct clcd_fb *fb, - struct device_node *endpoint); +int versatile_clcd_init_panel(struct clcd_fb *fb, struct device_node *panel); #else static inline int versatile_clcd_init_panel(struct clcd_fb *fb, - struct device_node *endpoint) + struct device_node *panel) { return 0; } diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c index ec2671d98abc..0fab92c62828 100644 --- a/drivers/video/fbdev/amba-clcd.c +++ b/drivers/video/fbdev/amba-clcd.c @@ -10,27 +10,22 @@ * * ARM PrimeCell PL110 Color LCD Controller */ -#include <linux/dma-mapping.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/slab.h> +#include <linux/amba/bus.h> +#include <linux/amba/clcd.h> +#include <linux/backlight.h> +#include <linux/clk.h> #include <linux/delay.h> -#include <linux/mm.h> +#include <linux/dma-mapping.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/list.h> -#include <linux/amba/bus.h> -#include <linux/amba/clcd.h> -#include <linux/bitops.h> -#include <linux/clk.h> -#include <linux/hardirq.h> -#include <linux/of.h> +#include <linux/mm.h> +#include <linux/module.h> #include <linux/of_address.h> #include <linux/of_graph.h> -#include <linux/backlight.h> +#include <linux/slab.h> +#include <linux/string.h> #include <video/display_timing.h> #include <video/of_display_timing.h> #include <video/videomode.h> @@ -629,16 +624,11 @@ static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode) mode->refresh); } -static int clcdfb_of_get_backlight(struct device_node *endpoint, +static int clcdfb_of_get_backlight(struct device_node *panel, struct clcd_panel *clcd_panel) { - struct device_node *panel; struct device_node *backlight; - panel = of_graph_get_remote_port_parent(endpoint); - if (!panel) - return -ENODEV; - /* Look up the optional backlight phandle */ backlight = of_parse_phandle(panel, "backlight", 0); if (backlight) { @@ -651,19 +641,14 @@ static int clcdfb_of_get_backlight(struct device_node *endpoint, return 0; } -static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint, - struct clcd_panel *clcd_panel) +static int clcdfb_of_get_mode(struct device *dev, struct device_node *panel, + struct clcd_panel *clcd_panel) { int err; - struct device_node *panel; struct fb_videomode *mode; char *name; int len; - panel = of_graph_get_remote_port_parent(endpoint); - if (!panel) - return -ENODEV; - /* Only directly connected DPI panels supported for now */ if (of_device_is_compatible(panel, "panel-dpi")) err = clcdfb_of_get_dpi_panel_mode(panel, clcd_panel); @@ -769,7 +754,7 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0) static int clcdfb_of_init_display(struct clcd_fb *fb) { - struct device_node *endpoint; + struct device_node *endpoint, *panel; int err; unsigned int bpp; u32 max_bandwidth; @@ -786,17 +771,21 @@ static int clcdfb_of_init_display(struct clcd_fb *fb) if (!endpoint) return -ENODEV; + panel = of_graph_get_remote_port_parent(endpoint); + if (!panel) + return -ENODEV; + if (fb->vendor->init_panel) { - err = fb->vendor->init_panel(fb, endpoint); + err = fb->vendor->init_panel(fb, panel); if (err) return err; } - err = clcdfb_of_get_backlight(endpoint, fb->panel); + err = clcdfb_of_get_backlight(panel, fb->panel); if (err) return err; - err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, fb->panel); + err = clcdfb_of_get_mode(&fb->dev->dev, panel, fb->panel); if (err) return err; diff --git a/drivers/video/fbdev/amifb.c b/drivers/video/fbdev/amifb.c index 1d702e13aaff..cc11c6061298 100644 --- a/drivers/video/fbdev/amifb.c +++ b/drivers/video/fbdev/amifb.c @@ -1484,13 +1484,11 @@ static int ami_decode_var(struct fb_var_screeninfo *var, struct amifb_par *par, par->xoffset = var->xoffset; par->yoffset = var->yoffset; if (par->vmode & FB_VMODE_YWRAP) { - if (par->xoffset || par->yoffset < 0 || - par->yoffset >= par->vyres) + if (par->yoffset >= par->vyres) par->xoffset = par->yoffset = 0; } else { - if (par->xoffset < 0 || - par->xoffset > upx(16 << maxfmode, par->vxres - par->xres) || - par->yoffset < 0 || par->yoffset > par->vyres - par->yres) + if (par->xoffset > upx(16 << maxfmode, par->vxres - par->xres) || + par->yoffset > par->vyres - par->yres) par->xoffset = par->yoffset = 0; } } else diff --git a/drivers/video/fbdev/aty/radeon_monitor.c b/drivers/video/fbdev/aty/radeon_monitor.c index 278b421ab3fe..dd823f5fe4c9 100644 --- a/drivers/video/fbdev/aty/radeon_monitor.c +++ b/drivers/video/fbdev/aty/radeon_monitor.c @@ -646,7 +646,7 @@ void radeon_probe_screens(struct radeonfb_info *rinfo, /* - * This functions applyes any arch/model/machine specific fixups + * This function applies any arch/model/machine specific fixups * to the panel info. It may eventually alter EDID block as * well or whatever is specific to a given model and not probed * properly by the default code diff --git a/drivers/video/fbdev/auo_k190x.c b/drivers/video/fbdev/auo_k190x.c index 9580374667ba..0d06038324e0 100644 --- a/drivers/video/fbdev/auo_k190x.c +++ b/drivers/video/fbdev/auo_k190x.c @@ -9,6 +9,7 @@ */ #include <linux/module.h> +#include <linux/sched/mm.h> #include <linux/kernel.h> #include <linux/gpio.h> #include <linux/platform_device.h> diff --git a/drivers/video/fbdev/cobalt_lcdfb.c b/drivers/video/fbdev/cobalt_lcdfb.c index 038ac6934fe9..9da90bd242f4 100644 --- a/drivers/video/fbdev/cobalt_lcdfb.c +++ b/drivers/video/fbdev/cobalt_lcdfb.c @@ -26,6 +26,7 @@ #include <linux/uaccess.h> #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/sched/signal.h> /* * Cursor position address diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 74b5bcac8bf2..37f69c061210 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -37,12 +37,11 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs } /* this is to find and return the vmalloc-ed fb pages */ -static int fb_deferred_io_fault(struct vm_area_struct *vma, - struct vm_fault *vmf) +static int fb_deferred_io_fault(struct vm_fault *vmf) { unsigned long offset; struct page *page; - struct fb_info *info = vma->vm_private_data; + struct fb_info *info = vmf->vma->vm_private_data; offset = vmf->pgoff << PAGE_SHIFT; if (offset >= info->fix.smem_len) @@ -54,8 +53,8 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma, get_page(page); - if (vma->vm_file) - page->mapping = vma->vm_file->f_mapping; + if (vmf->vma->vm_file) + page->mapping = vmf->vma->vm_file->f_mapping; else printk(KERN_ERR "no mapping available\n"); @@ -91,11 +90,10 @@ int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasy EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); /* vm_ops->page_mkwrite handler */ -static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, - struct vm_fault *vmf) +static int fb_deferred_io_mkwrite(struct vm_fault *vmf) { struct page *page = vmf->page; - struct fb_info *info = vma->vm_private_data; + struct fb_info *info = vmf->vma->vm_private_data; struct fb_deferred_io *fbdefio = info->fbdefio; struct page *cur; @@ -105,7 +103,7 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, deferred framebuffer IO. then if userspace touches a page again, we repeat the same scheme */ - file_update_time(vma->vm_file); + file_update_time(vmf->vma->vm_file); /* protect against the workqueue changing the page list */ mutex_lock(&fbdefio->lock); diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 76c1ad96fb37..069fe7960df1 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1492,6 +1492,21 @@ __releases(&info->lock) return 0; } +#ifdef CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA +unsigned long get_fb_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + struct fb_info * const info = filp->private_data; + unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); + + if (pgoff > fb_size || len > fb_size - pgoff) + return -EINVAL; + + return (unsigned long)info->screen_base + pgoff; +} +#endif + static const struct file_operations fb_fops = { .owner = THIS_MODULE, .read = fb_read, @@ -1503,7 +1518,8 @@ static const struct file_operations fb_fops = { .mmap = fb_mmap, .open = fb_open, .release = fb_release, -#ifdef HAVE_ARCH_FB_UNMAPPED_AREA +#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ + defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) .get_unmapped_area = get_fb_unmapped_area, #endif #ifdef CONFIG_FB_DEFERRED_IO diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c index fe00a07c122e..ca3d6b366471 100644 --- a/drivers/video/fbdev/fsl-diu-fb.c +++ b/drivers/video/fbdev/fsl-diu-fb.c @@ -439,12 +439,12 @@ static struct mfb_info mfb_template[] = { static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw) { mb(); - pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x pallete=%08x " + pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x palette=%08x " "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x " "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x " "thresholds=%08x int_mask=%08x plut=%08x\n", hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma, - hw->pallete, hw->cursor, hw->curs_pos, hw->diu_mode, + hw->palette, hw->cursor, hw->curs_pos, hw->diu_mode, hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para, hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut); rmb(); @@ -703,12 +703,6 @@ static int fsl_diu_check_var(struct fb_var_screeninfo *var, if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; - if (var->xoffset < 0) - var->xoffset = 0; - - if (var->yoffset < 0) - var->yoffset = 0; - if (var->xoffset + info->var.xres > info->var.xres_virtual) var->xoffset = info->var.xres_virtual - info->var.xres; @@ -1254,8 +1248,7 @@ static int fsl_diu_pan_display(struct fb_var_screeninfo *var, (info->var.yoffset == var->yoffset)) return 0; /* No change, do nothing */ - if (var->xoffset < 0 || var->yoffset < 0 - || var->xoffset + info->var.xres > info->var.xres_virtual + if (var->xoffset + info->var.xres > info->var.xres_virtual || var->yoffset + info->var.yres > info->var.yres_virtual) return -EINVAL; diff --git a/drivers/video/fbdev/imxfb.c b/drivers/video/fbdev/imxfb.c index fe0c4eeff2e4..1b0faadb3080 100644 --- a/drivers/video/fbdev/imxfb.c +++ b/drivers/video/fbdev/imxfb.c @@ -985,7 +985,11 @@ static int imxfb_probe(struct platform_device *pdev) */ imxfb_check_var(&info->var, info); - ret = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0); + /* + * For modes > 8bpp, the color map is bypassed. + * Therefore, 256 entries are enough. + */ + ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret < 0) goto failed_cmap; diff --git a/drivers/video/fbdev/matrox/matroxfb_DAC1064.c b/drivers/video/fbdev/matrox/matroxfb_DAC1064.c index a01147fdf270..b380a393cbc3 100644 --- a/drivers/video/fbdev/matrox/matroxfb_DAC1064.c +++ b/drivers/video/fbdev/matrox/matroxfb_DAC1064.c @@ -1088,14 +1088,20 @@ static void MGAG100_restore(struct matrox_fb_info *minfo) #ifdef CONFIG_FB_MATROX_MYSTIQUE struct matrox_switch matrox_mystique = { - MGA1064_preinit, MGA1064_reset, MGA1064_init, MGA1064_restore, + .preinit = MGA1064_preinit, + .reset = MGA1064_reset, + .init = MGA1064_init, + .restore = MGA1064_restore, }; EXPORT_SYMBOL(matrox_mystique); #endif #ifdef CONFIG_FB_MATROX_G struct matrox_switch matrox_G100 = { - MGAG100_preinit, MGAG100_reset, MGAG100_init, MGAG100_restore, + .preinit = MGAG100_preinit, + .reset = MGAG100_reset, + .init = MGAG100_init, + .restore = MGAG100_restore, }; EXPORT_SYMBOL(matrox_G100); #endif diff --git a/drivers/video/fbdev/matrox/matroxfb_Ti3026.c b/drivers/video/fbdev/matrox/matroxfb_Ti3026.c index 68fa037d8cbc..9ff9be85759e 100644 --- a/drivers/video/fbdev/matrox/matroxfb_Ti3026.c +++ b/drivers/video/fbdev/matrox/matroxfb_Ti3026.c @@ -738,7 +738,10 @@ static int Ti3026_preinit(struct matrox_fb_info *minfo) } struct matrox_switch matrox_millennium = { - Ti3026_preinit, Ti3026_reset, Ti3026_init, Ti3026_restore + .preinit = Ti3026_preinit, + .reset = Ti3026_reset, + .init = Ti3026_init, + .restore = Ti3026_restore }; EXPORT_SYMBOL(matrox_millennium); #endif diff --git a/drivers/video/fbdev/maxinefb.c b/drivers/video/fbdev/maxinefb.c index 5cf52d3c8e75..cab7333208ea 100644 --- a/drivers/video/fbdev/maxinefb.c +++ b/drivers/video/fbdev/maxinefb.c @@ -51,7 +51,7 @@ static struct fb_var_screeninfo maxinefb_defined = { .vmode = FB_VMODE_NONINTERLACED, }; -static struct fb_fix_screeninfo maxinefb_fix = { +static struct fb_fix_screeninfo maxinefb_fix __initdata = { .id = "Maxine", .smem_len = (1024*768), .type = FB_TYPE_PACKED_PIXELS, diff --git a/drivers/video/fbdev/mbx/mbxdebugfs.c b/drivers/video/fbdev/mbx/mbxdebugfs.c index e3bc00a75296..2528d3e609a4 100644 --- a/drivers/video/fbdev/mbx/mbxdebugfs.c +++ b/drivers/video/fbdev/mbx/mbxdebugfs.c @@ -15,12 +15,6 @@ struct mbxfb_debugfs_data { struct dentry *misc; }; -static int open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t write_file_dummy(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -174,42 +168,42 @@ static ssize_t misc_read_file(struct file *file, char __user *userbuf, static const struct file_operations sysconf_fops = { .read = sysconf_read_file, .write = write_file_dummy, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; static const struct file_operations clock_fops = { .read = clock_read_file, .write = write_file_dummy, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; static const struct file_operations display_fops = { .read = display_read_file, .write = write_file_dummy, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; static const struct file_operations gsctl_fops = { .read = gsctl_read_file, .write = write_file_dummy, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; static const struct file_operations sdram_fops = { .read = sdram_read_file, .write = write_file_dummy, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; static const struct file_operations misc_fops = { .read = misc_read_file, .write = write_file_dummy, - .open = open_file_generic, + .open = simple_open, .llseek = default_llseek, }; diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index abb6bbf226d5..9085e9525341 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -187,7 +187,7 @@ static int load_waveform(u8 *mem, size_t size, int m, int t, epd_frame_table[par->dt].wfm_size = user_wfm_size; if (size != epd_frame_table[par->dt].wfm_size) { - dev_err(dev, "Error: unexpected size %Zd != %d\n", size, + dev_err(dev, "Error: unexpected size %zd != %d\n", size, epd_frame_table[par->dt].wfm_size); return -EINVAL; } diff --git a/drivers/video/fbdev/nvidia/nv_accel.c b/drivers/video/fbdev/nvidia/nv_accel.c index ad6472a894ea..7341fed63e35 100644 --- a/drivers/video/fbdev/nvidia/nv_accel.c +++ b/drivers/video/fbdev/nvidia/nv_accel.c @@ -48,6 +48,8 @@ */ #include <linux/fb.h> +#include <linux/nmi.h> + #include "nv_type.h" #include "nv_proto.h" #include "nv_dma.h" diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index 906c6e75c260..9be884b0c778 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -668,14 +668,14 @@ static int __init offb_init(void) offb_init_nodriver(of_chosen, 1); } - for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { + for_each_node_by_type(dp, "display") { if (of_get_property(dp, "linux,opened", NULL) && of_get_property(dp, "linux,boot-display", NULL)) { boot_disp = dp; offb_init_nodriver(dp, 0); } } - for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { + for_each_node_by_type(dp, "display") { if (of_get_property(dp, "linux,opened", NULL) && dp != boot_disp) offb_init_nodriver(dp, 0); diff --git a/drivers/video/fbdev/omap/lcd_ams_delta.c b/drivers/video/fbdev/omap/lcd_ams_delta.c index f912a207b394..a4ee947006c7 100644 --- a/drivers/video/fbdev/omap/lcd_ams_delta.c +++ b/drivers/video/fbdev/omap/lcd_ams_delta.c @@ -136,11 +136,6 @@ static void ams_delta_panel_disable(struct lcd_panel *panel) gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 0); } -static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - static struct lcd_panel ams_delta_panel = { .name = "ams-delta", .config = 0, @@ -163,7 +158,6 @@ static struct lcd_panel ams_delta_panel = { .cleanup = ams_delta_panel_cleanup, .enable = ams_delta_panel_enable, .disable = ams_delta_panel_disable, - .get_caps = ams_delta_panel_get_caps, }; @@ -195,27 +189,8 @@ static int ams_delta_panel_probe(struct platform_device *pdev) return 0; } -static int ams_delta_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int ams_delta_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int ams_delta_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver ams_delta_panel_driver = { .probe = ams_delta_panel_probe, - .remove = ams_delta_panel_remove, - .suspend = ams_delta_panel_suspend, - .resume = ams_delta_panel_resume, .driver = { .name = "lcd_ams_delta", }, diff --git a/drivers/video/fbdev/omap/lcd_h3.c b/drivers/video/fbdev/omap/lcd_h3.c index 21512b027ff7..9d2da146813e 100644 --- a/drivers/video/fbdev/omap/lcd_h3.c +++ b/drivers/video/fbdev/omap/lcd_h3.c @@ -28,15 +28,6 @@ #define MODULE_NAME "omapfb-lcd_h3" -static int h3_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) -{ - return 0; -} - -static void h3_panel_cleanup(struct lcd_panel *panel) -{ -} - static int h3_panel_enable(struct lcd_panel *panel) { int r = 0; @@ -63,12 +54,7 @@ static void h3_panel_disable(struct lcd_panel *panel) pr_err(MODULE_NAME ": Unable to turn off LCD panel\n"); } -static unsigned long h3_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel h3_panel = { +static struct lcd_panel h3_panel = { .name = "h3", .config = OMAP_LCDC_PANEL_TFT, @@ -85,11 +71,8 @@ struct lcd_panel h3_panel = { .vbp = 0, .pcd = 0, - .init = h3_panel_init, - .cleanup = h3_panel_cleanup, .enable = h3_panel_enable, .disable = h3_panel_disable, - .get_caps = h3_panel_get_caps, }; static int h3_panel_probe(struct platform_device *pdev) @@ -98,26 +81,8 @@ static int h3_panel_probe(struct platform_device *pdev) return 0; } -static int h3_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int h3_panel_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return 0; -} - -static int h3_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver h3_panel_driver = { .probe = h3_panel_probe, - .remove = h3_panel_remove, - .suspend = h3_panel_suspend, - .resume = h3_panel_resume, .driver = { .name = "lcd_h3", }, diff --git a/drivers/video/fbdev/omap/lcd_htcherald.c b/drivers/video/fbdev/omap/lcd_htcherald.c index 8b4dfa058258..9d692f5b8025 100644 --- a/drivers/video/fbdev/omap/lcd_htcherald.c +++ b/drivers/video/fbdev/omap/lcd_htcherald.c @@ -31,32 +31,8 @@ #include "omapfb.h" -static int htcherald_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - return 0; -} - -static void htcherald_panel_cleanup(struct lcd_panel *panel) -{ -} - -static int htcherald_panel_enable(struct lcd_panel *panel) -{ - return 0; -} - -static void htcherald_panel_disable(struct lcd_panel *panel) -{ -} - -static unsigned long htcherald_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - /* Found on WIZ200 (miknix) and some HERA110 models (darkstar62) */ -struct lcd_panel htcherald_panel_1 = { +static struct lcd_panel htcherald_panel_1 = { .name = "lcd_herald", .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_HSYNC | @@ -74,12 +50,6 @@ struct lcd_panel htcherald_panel_1 = { .vsw = 3, .vfp = 2, .vbp = 2, - - .init = htcherald_panel_init, - .cleanup = htcherald_panel_cleanup, - .enable = htcherald_panel_enable, - .disable = htcherald_panel_disable, - .get_caps = htcherald_panel_get_caps, }; static int htcherald_panel_probe(struct platform_device *pdev) @@ -88,27 +58,8 @@ static int htcherald_panel_probe(struct platform_device *pdev) return 0; } -static int htcherald_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int htcherald_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int htcherald_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver htcherald_panel_driver = { .probe = htcherald_panel_probe, - .remove = htcherald_panel_remove, - .suspend = htcherald_panel_suspend, - .resume = htcherald_panel_resume, .driver = { .name = "lcd_htcherald", }, diff --git a/drivers/video/fbdev/omap/lcd_inn1510.c b/drivers/video/fbdev/omap/lcd_inn1510.c index 49907fab36ac..b284050f5471 100644 --- a/drivers/video/fbdev/omap/lcd_inn1510.c +++ b/drivers/video/fbdev/omap/lcd_inn1510.c @@ -27,16 +27,6 @@ #include "omapfb.h" -static int innovator1510_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - return 0; -} - -static void innovator1510_panel_cleanup(struct lcd_panel *panel) -{ -} - static int innovator1510_panel_enable(struct lcd_panel *panel) { __raw_writeb(0x7, OMAP1510_FPGA_LCD_PANEL_CONTROL); @@ -48,12 +38,7 @@ static void innovator1510_panel_disable(struct lcd_panel *panel) __raw_writeb(0x0, OMAP1510_FPGA_LCD_PANEL_CONTROL); } -static unsigned long innovator1510_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel innovator1510_panel = { +static struct lcd_panel innovator1510_panel = { .name = "inn1510", .config = OMAP_LCDC_PANEL_TFT, @@ -70,11 +55,8 @@ struct lcd_panel innovator1510_panel = { .vbp = 0, .pcd = 12, - .init = innovator1510_panel_init, - .cleanup = innovator1510_panel_cleanup, .enable = innovator1510_panel_enable, .disable = innovator1510_panel_disable, - .get_caps = innovator1510_panel_get_caps, }; static int innovator1510_panel_probe(struct platform_device *pdev) @@ -83,27 +65,8 @@ static int innovator1510_panel_probe(struct platform_device *pdev) return 0; } -static int innovator1510_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int innovator1510_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int innovator1510_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver innovator1510_panel_driver = { .probe = innovator1510_panel_probe, - .remove = innovator1510_panel_remove, - .suspend = innovator1510_panel_suspend, - .resume = innovator1510_panel_resume, .driver = { .name = "lcd_inn1510", }, diff --git a/drivers/video/fbdev/omap/lcd_inn1610.c b/drivers/video/fbdev/omap/lcd_inn1610.c index 8b42894eeb77..1841710e796f 100644 --- a/drivers/video/fbdev/omap/lcd_inn1610.c +++ b/drivers/video/fbdev/omap/lcd_inn1610.c @@ -69,12 +69,7 @@ static void innovator1610_panel_disable(struct lcd_panel *panel) gpio_set_value(15, 0); } -static unsigned long innovator1610_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel innovator1610_panel = { +static struct lcd_panel innovator1610_panel = { .name = "inn1610", .config = OMAP_LCDC_PANEL_TFT, @@ -95,7 +90,6 @@ struct lcd_panel innovator1610_panel = { .cleanup = innovator1610_panel_cleanup, .enable = innovator1610_panel_enable, .disable = innovator1610_panel_disable, - .get_caps = innovator1610_panel_get_caps, }; static int innovator1610_panel_probe(struct platform_device *pdev) @@ -104,27 +98,8 @@ static int innovator1610_panel_probe(struct platform_device *pdev) return 0; } -static int innovator1610_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int innovator1610_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int innovator1610_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver innovator1610_panel_driver = { .probe = innovator1610_panel_probe, - .remove = innovator1610_panel_remove, - .suspend = innovator1610_panel_suspend, - .resume = innovator1610_panel_resume, .driver = { .name = "lcd_inn1610", }, diff --git a/drivers/video/fbdev/omap/lcd_osk.c b/drivers/video/fbdev/omap/lcd_osk.c index b56886c7055e..b0be5771fe90 100644 --- a/drivers/video/fbdev/omap/lcd_osk.c +++ b/drivers/video/fbdev/omap/lcd_osk.c @@ -29,16 +29,6 @@ #include "omapfb.h" -static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) -{ - /* gpio2 was allocated in board init */ - return 0; -} - -static void osk_panel_cleanup(struct lcd_panel *panel) -{ -} - static int osk_panel_enable(struct lcd_panel *panel) { /* configure PWL pin */ @@ -68,12 +58,7 @@ static void osk_panel_disable(struct lcd_panel *panel) gpio_set_value(2, 0); } -static unsigned long osk_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel osk_panel = { +static struct lcd_panel osk_panel = { .name = "osk", .config = OMAP_LCDC_PANEL_TFT, @@ -90,11 +75,8 @@ struct lcd_panel osk_panel = { .vbp = 0, .pcd = 12, - .init = osk_panel_init, - .cleanup = osk_panel_cleanup, .enable = osk_panel_enable, .disable = osk_panel_disable, - .get_caps = osk_panel_get_caps, }; static int osk_panel_probe(struct platform_device *pdev) @@ -103,26 +85,8 @@ static int osk_panel_probe(struct platform_device *pdev) return 0; } -static int osk_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int osk_panel_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return 0; -} - -static int osk_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver osk_panel_driver = { .probe = osk_panel_probe, - .remove = osk_panel_remove, - .suspend = osk_panel_suspend, - .resume = osk_panel_resume, .driver = { .name = "lcd_osk", }, diff --git a/drivers/video/fbdev/omap/lcd_palmte.c b/drivers/video/fbdev/omap/lcd_palmte.c index 2713fed286f7..cef96386cf80 100644 --- a/drivers/video/fbdev/omap/lcd_palmte.c +++ b/drivers/video/fbdev/omap/lcd_palmte.c @@ -25,31 +25,7 @@ #include "omapfb.h" -static int palmte_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - return 0; -} - -static void palmte_panel_cleanup(struct lcd_panel *panel) -{ -} - -static int palmte_panel_enable(struct lcd_panel *panel) -{ - return 0; -} - -static void palmte_panel_disable(struct lcd_panel *panel) -{ -} - -static unsigned long palmte_panel_get_caps(struct lcd_panel *panel) -{ - return 0; -} - -struct lcd_panel palmte_panel = { +static struct lcd_panel palmte_panel = { .name = "palmte", .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE | @@ -67,12 +43,6 @@ struct lcd_panel palmte_panel = { .vfp = 8, .vbp = 7, .pcd = 0, - - .init = palmte_panel_init, - .cleanup = palmte_panel_cleanup, - .enable = palmte_panel_enable, - .disable = palmte_panel_disable, - .get_caps = palmte_panel_get_caps, }; static int palmte_panel_probe(struct platform_device *pdev) @@ -81,26 +51,8 @@ static int palmte_panel_probe(struct platform_device *pdev) return 0; } -static int palmte_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int palmte_panel_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return 0; -} - -static int palmte_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver palmte_panel_driver = { .probe = palmte_panel_probe, - .remove = palmte_panel_remove, - .suspend = palmte_panel_suspend, - .resume = palmte_panel_resume, .driver = { .name = "lcd_palmte", }, diff --git a/drivers/video/fbdev/omap/lcd_palmtt.c b/drivers/video/fbdev/omap/lcd_palmtt.c index 1a936d5c7b6f..627f13dae5ad 100644 --- a/drivers/video/fbdev/omap/lcd_palmtt.c +++ b/drivers/video/fbdev/omap/lcd_palmtt.c @@ -32,31 +32,12 @@ GPIO13 - screen blanking #include "omapfb.h" -static int palmtt_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - return 0; -} - -static void palmtt_panel_cleanup(struct lcd_panel *panel) -{ -} - -static int palmtt_panel_enable(struct lcd_panel *panel) -{ - return 0; -} - -static void palmtt_panel_disable(struct lcd_panel *panel) -{ -} - static unsigned long palmtt_panel_get_caps(struct lcd_panel *panel) { return OMAPFB_CAPS_SET_BACKLIGHT; } -struct lcd_panel palmtt_panel = { +static struct lcd_panel palmtt_panel = { .name = "palmtt", .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE | @@ -74,10 +55,6 @@ struct lcd_panel palmtt_panel = { .vbp = 7, .pcd = 0, - .init = palmtt_panel_init, - .cleanup = palmtt_panel_cleanup, - .enable = palmtt_panel_enable, - .disable = palmtt_panel_disable, .get_caps = palmtt_panel_get_caps, }; @@ -87,26 +64,8 @@ static int palmtt_panel_probe(struct platform_device *pdev) return 0; } -static int palmtt_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int palmtt_panel_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return 0; -} - -static int palmtt_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver palmtt_panel_driver = { .probe = palmtt_panel_probe, - .remove = palmtt_panel_remove, - .suspend = palmtt_panel_suspend, - .resume = palmtt_panel_resume, .driver = { .name = "lcd_palmtt", }, diff --git a/drivers/video/fbdev/omap/lcd_palmz71.c b/drivers/video/fbdev/omap/lcd_palmz71.c index a20db4f7ea99..c46d4db1f839 100644 --- a/drivers/video/fbdev/omap/lcd_palmz71.c +++ b/drivers/video/fbdev/omap/lcd_palmz71.c @@ -26,32 +26,12 @@ #include "omapfb.h" -static int palmz71_panel_init(struct lcd_panel *panel, - struct omapfb_device *fbdev) -{ - return 0; -} - -static void palmz71_panel_cleanup(struct lcd_panel *panel) -{ - -} - -static int palmz71_panel_enable(struct lcd_panel *panel) -{ - return 0; -} - -static void palmz71_panel_disable(struct lcd_panel *panel) -{ -} - static unsigned long palmz71_panel_get_caps(struct lcd_panel *panel) { return OMAPFB_CAPS_SET_BACKLIGHT; } -struct lcd_panel palmz71_panel = { +static struct lcd_panel palmz71_panel = { .name = "palmz71", .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE | @@ -69,10 +49,6 @@ struct lcd_panel palmz71_panel = { .vbp = 7, .pcd = 0, - .init = palmz71_panel_init, - .cleanup = palmz71_panel_cleanup, - .enable = palmz71_panel_enable, - .disable = palmz71_panel_disable, .get_caps = palmz71_panel_get_caps, }; @@ -82,27 +58,8 @@ static int palmz71_panel_probe(struct platform_device *pdev) return 0; } -static int palmz71_panel_remove(struct platform_device *pdev) -{ - return 0; -} - -static int palmz71_panel_suspend(struct platform_device *pdev, - pm_message_t mesg) -{ - return 0; -} - -static int palmz71_panel_resume(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver palmz71_panel_driver = { .probe = palmz71_panel_probe, - .remove = palmz71_panel_remove, - .suspend = palmz71_panel_suspend, - .resume = palmz71_panel_resume, .driver = { .name = "lcd_palmz71", }, diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c index 6429f33167f5..1abba07b84b3 100644 --- a/drivers/video/fbdev/omap/omapfb_main.c +++ b/drivers/video/fbdev/omap/omapfb_main.c @@ -337,7 +337,8 @@ static int omapfb_blank(int blank, struct fb_info *fbi) if (fbdev->state == OMAPFB_SUSPENDED) { if (fbdev->ctrl->resume) fbdev->ctrl->resume(); - fbdev->panel->enable(fbdev->panel); + if (fbdev->panel->enable) + fbdev->panel->enable(fbdev->panel); fbdev->state = OMAPFB_ACTIVE; if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) @@ -346,7 +347,8 @@ static int omapfb_blank(int blank, struct fb_info *fbi) break; case FB_BLANK_POWERDOWN: if (fbdev->state == OMAPFB_ACTIVE) { - fbdev->panel->disable(fbdev->panel); + if (fbdev->panel->disable) + fbdev->panel->disable(fbdev->panel); if (fbdev->ctrl->suspend) fbdev->ctrl->suspend(); fbdev->state = OMAPFB_SUSPENDED; @@ -1030,7 +1032,8 @@ static void omapfb_get_caps(struct omapfb_device *fbdev, int plane, { memset(caps, 0, sizeof(*caps)); fbdev->ctrl->get_caps(plane, caps); - caps->ctrl |= fbdev->panel->get_caps(fbdev->panel); + if (fbdev->panel->get_caps) + caps->ctrl |= fbdev->panel->get_caps(fbdev->panel); } /* For lcd testing */ @@ -1549,7 +1552,8 @@ static void omapfb_free_resources(struct omapfb_device *fbdev, int state) case 7: omapfb_unregister_sysfs(fbdev); case 6: - fbdev->panel->disable(fbdev->panel); + if (fbdev->panel->disable) + fbdev->panel->disable(fbdev->panel); case 5: omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED); case 4: @@ -1557,7 +1561,8 @@ static void omapfb_free_resources(struct omapfb_device *fbdev, int state) case 3: ctrl_cleanup(fbdev); case 2: - fbdev->panel->cleanup(fbdev->panel); + if (fbdev->panel->cleanup) + fbdev->panel->cleanup(fbdev->panel); case 1: dev_set_drvdata(fbdev->dev, NULL); kfree(fbdev); @@ -1680,9 +1685,11 @@ static int omapfb_do_probe(struct platform_device *pdev, goto cleanup; } - r = fbdev->panel->init(fbdev->panel, fbdev); - if (r) - goto cleanup; + if (fbdev->panel->init) { + r = fbdev->panel->init(fbdev->panel, fbdev); + if (r) + goto cleanup; + } pr_info("omapfb: configured for panel %s\n", fbdev->panel->name); @@ -1725,9 +1732,11 @@ static int omapfb_do_probe(struct platform_device *pdev, OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE); init_state++; - r = fbdev->panel->enable(fbdev->panel); - if (r) - goto cleanup; + if (fbdev->panel->enable) { + r = fbdev->panel->enable(fbdev->panel); + if (r) + goto cleanup; + } init_state++; r = omapfb_register_sysfs(fbdev); diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c index 8b810696a42b..fd2b372d0264 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c @@ -19,7 +19,7 @@ #include <linux/jiffies.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/of_device.h> diff --git a/drivers/video/fbdev/pmag-ba-fb.c b/drivers/video/fbdev/pmag-ba-fb.c index 5872bc4af3ce..df02fb4b7fd1 100644 --- a/drivers/video/fbdev/pmag-ba-fb.c +++ b/drivers/video/fbdev/pmag-ba-fb.c @@ -129,7 +129,7 @@ static struct fb_ops pmagbafb_ops = { /* * Turn the hardware cursor off. */ -static void __init pmagbafb_erase_cursor(struct fb_info *info) +static void pmagbafb_erase_cursor(struct fb_info *info) { struct pmagbafb_par *par = info->par; diff --git a/drivers/video/fbdev/pmagb-b-fb.c b/drivers/video/fbdev/pmagb-b-fb.c index 0822b6f8dddc..a7a179a0bb33 100644 --- a/drivers/video/fbdev/pmagb-b-fb.c +++ b/drivers/video/fbdev/pmagb-b-fb.c @@ -133,7 +133,7 @@ static struct fb_ops pmagbbfb_ops = { /* * Turn the hardware cursor off. */ -static void __init pmagbbfb_erase_cursor(struct fb_info *info) +static void pmagbbfb_erase_cursor(struct fb_info *info) { struct pmagbbfb_par *par = info->par; diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c index 82c0a8caa9b8..885ee3a563aa 100644 --- a/drivers/video/fbdev/sh_mobile_lcdcfb.c +++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c @@ -439,9 +439,9 @@ static unsigned long lcdc_sys_read_data(void *handle) } static struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { - lcdc_sys_write_index, - lcdc_sys_write_data, - lcdc_sys_read_data, + .write_index = lcdc_sys_write_index, + .write_data = lcdc_sys_write_data, + .read_data = lcdc_sys_read_data, }; static int sh_mobile_lcdc_sginit(struct fb_info *info, @@ -2782,8 +2782,10 @@ static int sh_mobile_lcdc_probe(struct platform_device *pdev) priv->forced_fourcc = pdata->ch[0].fourcc; priv->base = ioremap_nocache(res->start, resource_size(res)); - if (!priv->base) + if (!priv->base) { + error = -ENOMEM; goto err1; + } error = sh_mobile_lcdc_setup_clocks(priv, pdata->clock_source); if (error) { diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index 61f799a515dc..a3c44ecf4523 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -180,10 +180,12 @@ static int simplefb_parse_pd(struct platform_device *pdev, struct simplefb_par { u32 palette[PSEUDO_PALETTE_SIZE]; #if defined CONFIG_OF && defined CONFIG_COMMON_CLK + bool clks_enabled; unsigned int clk_count; struct clk **clks; #endif #if defined CONFIG_OF && defined CONFIG_REGULATOR + bool regulators_enabled; u32 regulator_count; struct regulator **regulators; #endif @@ -208,12 +210,12 @@ struct simplefb_par { * the fb probe will not help us much either. So just complain and carry on, * and hope that the user actually gets a working fb at the end of things. */ -static int simplefb_clocks_init(struct simplefb_par *par, - struct platform_device *pdev) +static int simplefb_clocks_get(struct simplefb_par *par, + struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct clk *clock; - int i, ret; + int i; if (dev_get_platdata(&pdev->dev) || !np) return 0; @@ -244,6 +246,14 @@ static int simplefb_clocks_init(struct simplefb_par *par, par->clks[i] = clock; } + return 0; +} + +static void simplefb_clocks_enable(struct simplefb_par *par, + struct platform_device *pdev) +{ + int i, ret; + for (i = 0; i < par->clk_count; i++) { if (par->clks[i]) { ret = clk_prepare_enable(par->clks[i]); @@ -256,8 +266,7 @@ static int simplefb_clocks_init(struct simplefb_par *par, } } } - - return 0; + par->clks_enabled = true; } static void simplefb_clocks_destroy(struct simplefb_par *par) @@ -269,7 +278,8 @@ static void simplefb_clocks_destroy(struct simplefb_par *par) for (i = 0; i < par->clk_count; i++) { if (par->clks[i]) { - clk_disable_unprepare(par->clks[i]); + if (par->clks_enabled) + clk_disable_unprepare(par->clks[i]); clk_put(par->clks[i]); } } @@ -277,8 +287,10 @@ static void simplefb_clocks_destroy(struct simplefb_par *par) kfree(par->clks); } #else -static int simplefb_clocks_init(struct simplefb_par *par, +static int simplefb_clocks_get(struct simplefb_par *par, struct platform_device *pdev) { return 0; } +static void simplefb_clocks_enable(struct simplefb_par *par, + struct platform_device *pdev) { } static void simplefb_clocks_destroy(struct simplefb_par *par) { } #endif @@ -305,14 +317,14 @@ static void simplefb_clocks_destroy(struct simplefb_par *par) { } * the fb probe will not help us much either. So just complain and carry on, * and hope that the user actually gets a working fb at the end of things. */ -static int simplefb_regulators_init(struct simplefb_par *par, - struct platform_device *pdev) +static int simplefb_regulators_get(struct simplefb_par *par, + struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct property *prop; struct regulator *regulator; const char *p; - int count = 0, i = 0, ret; + int count = 0, i = 0; if (dev_get_platdata(&pdev->dev) || !np) return 0; @@ -354,6 +366,14 @@ static int simplefb_regulators_init(struct simplefb_par *par, } par->regulator_count = i; + return 0; +} + +static void simplefb_regulators_enable(struct simplefb_par *par, + struct platform_device *pdev) +{ + int i, ret; + /* Enable all the regulators */ for (i = 0; i < par->regulator_count; i++) { ret = regulator_enable(par->regulators[i]); @@ -365,15 +385,14 @@ static int simplefb_regulators_init(struct simplefb_par *par, par->regulators[i] = NULL; } } - - return 0; + par->regulators_enabled = true; } static void simplefb_regulators_destroy(struct simplefb_par *par) { int i; - if (!par->regulators) + if (!par->regulators || !par->regulators_enabled) return; for (i = 0; i < par->regulator_count; i++) @@ -381,8 +400,10 @@ static void simplefb_regulators_destroy(struct simplefb_par *par) regulator_disable(par->regulators[i]); } #else -static int simplefb_regulators_init(struct simplefb_par *par, +static int simplefb_regulators_get(struct simplefb_par *par, struct platform_device *pdev) { return 0; } +static void simplefb_regulators_enable(struct simplefb_par *par, + struct platform_device *pdev) { } static void simplefb_regulators_destroy(struct simplefb_par *par) { } #endif @@ -453,14 +474,17 @@ static int simplefb_probe(struct platform_device *pdev) } info->pseudo_palette = par->palette; - ret = simplefb_clocks_init(par, pdev); + ret = simplefb_clocks_get(par, pdev); if (ret < 0) goto error_unmap; - ret = simplefb_regulators_init(par, pdev); + ret = simplefb_regulators_get(par, pdev); if (ret < 0) goto error_clocks; + simplefb_clocks_enable(par, pdev); + simplefb_regulators_enable(par, pdev); + dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n", info->fix.smem_start, info->fix.smem_len, info->screen_base); diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 2925d5ce8d3e..bd017b57c47f 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -9,6 +9,7 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/fb.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> @@ -16,6 +17,7 @@ #include <linux/of_gpio.h> #include <linux/pwm.h> #include <linux/uaccess.h> +#include <linux/regulator/consumer.h> #define SSD1307FB_DATA 0x40 #define SSD1307FB_COMMAND 0x80 @@ -73,7 +75,8 @@ struct ssd1307fb_par { u32 prechargep2; struct pwm_device *pwm; u32 pwm_period; - int reset; + struct gpio_desc *reset; + struct regulator *vbat_reg; u32 seg_remap; u32 vcomh; u32 width; @@ -439,6 +442,9 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) if (ret < 0) return ret; + /* Clear the screen */ + ssd1307fb_update_display(par); + /* Turn on the display */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); if (ret < 0) @@ -561,10 +567,20 @@ static int ssd1307fb_probe(struct i2c_client *client, par->device_info = of_device_get_match_data(&client->dev); - par->reset = of_get_named_gpio(client->dev.of_node, - "reset-gpios", 0); - if (!gpio_is_valid(par->reset)) { - ret = -EINVAL; + par->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(par->reset)) { + dev_err(&client->dev, "failed to get reset gpio: %ld\n", + PTR_ERR(par->reset)); + ret = PTR_ERR(par->reset); + goto fb_alloc_error; + } + + par->vbat_reg = devm_regulator_get_optional(&client->dev, "vbat"); + if (IS_ERR(par->vbat_reg)) { + dev_err(&client->dev, "failed to get VBAT regulator: %ld\n", + PTR_ERR(par->vbat_reg)); + ret = PTR_ERR(par->vbat_reg); goto fb_alloc_error; } @@ -642,27 +658,25 @@ static int ssd1307fb_probe(struct i2c_client *client, fb_deferred_io_init(info); - ret = devm_gpio_request_one(&client->dev, par->reset, - GPIOF_OUT_INIT_HIGH, - "oled-reset"); + i2c_set_clientdata(client, info); + + if (par->reset) { + /* Reset the screen */ + gpiod_set_value(par->reset, 0); + udelay(4); + gpiod_set_value(par->reset, 1); + udelay(4); + } + + ret = regulator_enable(par->vbat_reg); if (ret) { - dev_err(&client->dev, - "failed to request gpio %d: %d\n", - par->reset, ret); + dev_err(&client->dev, "failed to enable VBAT: %d\n", ret); goto reset_oled_error; } - i2c_set_clientdata(client, info); - - /* Reset the screen */ - gpio_set_value(par->reset, 0); - udelay(4); - gpio_set_value(par->reset, 1); - udelay(4); - ret = ssd1307fb_init(par); if (ret) - goto reset_oled_error; + goto regulator_enable_error; ret = register_framebuffer(info); if (ret) { @@ -695,6 +709,8 @@ panel_init_error: pwm_disable(par->pwm); pwm_put(par->pwm); }; +regulator_enable_error: + regulator_disable(par->vbat_reg); reset_oled_error: fb_deferred_io_cleanup(info); fb_alloc_error: diff --git a/drivers/video/fbdev/stifb.c b/drivers/video/fbdev/stifb.c index accfef71e984..6ded5c198998 100644 --- a/drivers/video/fbdev/stifb.c +++ b/drivers/video/fbdev/stifb.c @@ -1294,6 +1294,10 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref) strcpy(fix->id, "stifb"); info->fbops = &stifb_ops; info->screen_base = ioremap_nocache(REGION_BASE(fb,1), fix->smem_len); + if (!info->screen_base) { + printk(KERN_ERR "stifb: failed to map memory\n"); + goto out_err0; + } info->screen_size = fix->smem_len; info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA; info->pseudo_palette = &fb->pseudo_palette; diff --git a/drivers/video/fbdev/wm8505fb.c b/drivers/video/fbdev/wm8505fb.c index e925619da39b..253ffe9baab2 100644 --- a/drivers/video/fbdev/wm8505fb.c +++ b/drivers/video/fbdev/wm8505fb.c @@ -182,7 +182,7 @@ static ssize_t contrast_store(struct device *dev, return count; } -static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store); +static DEVICE_ATTR_RW(contrast); static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) { diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 181793f07852..4e1191508228 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -31,6 +31,7 @@ #include <linux/wait.h> #include <linux/mm.h> #include <linux/mount.h> +#include <linux/magic.h> /* * Balloon device works in 4K page units. So each page is pointed to by @@ -413,7 +414,8 @@ static int init_vqs(struct virtio_balloon *vb) * optionally stat. */ nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2; - err = vb->vdev->config->find_vqs(vb->vdev, nvqs, vqs, callbacks, names); + err = vb->vdev->config->find_vqs(vb->vdev, nvqs, vqs, callbacks, names, + NULL); if (err) return err; @@ -615,8 +617,12 @@ static void virtballoon_remove(struct virtio_device *vdev) cancel_work_sync(&vb->update_balloon_stats_work); remove_common(vb); +#ifdef CONFIG_BALLOON_COMPACTION if (vb->vb_dev_info.inode) iput(vb->vb_dev_info.inode); + + kern_unmount(balloon_mnt); +#endif kfree(vb); } diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c index 350a2a5a49db..79f1293cda93 100644 --- a/drivers/virtio/virtio_input.c +++ b/drivers/virtio/virtio_input.c @@ -173,7 +173,8 @@ static int virtinput_init_vqs(struct virtio_input *vi) static const char * const names[] = { "events", "status" }; int err; - err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names); + err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names, + NULL); if (err) return err; vi->evt = vqs[0]; diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index c71fde5fe835..78343b8f9034 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -70,7 +70,7 @@ #include <linux/spinlock.h> #include <linux/virtio.h> #include <linux/virtio_config.h> -#include <linux/virtio_mmio.h> +#include <uapi/linux/virtio_mmio.h> #include <linux/virtio_ring.h> @@ -446,7 +446,8 @@ error_available: static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], + struct irq_affinity *desc) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); unsigned int irq = platform_get_irq(vm_dev->pdev, 0); diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 186cbab327b8..df548a6fb844 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -33,10 +33,8 @@ void vp_synchronize_vectors(struct virtio_device *vdev) struct virtio_pci_device *vp_dev = to_vp_device(vdev); int i; - if (vp_dev->intx_enabled) - synchronize_irq(vp_dev->pci_dev->irq); - - for (i = 0; i < vp_dev->msix_vectors; ++i) + synchronize_irq(pci_irq_vector(vp_dev->pci_dev, 0)); + for (i = 1; i < vp_dev->msix_vectors; i++) synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i)); } @@ -62,16 +60,13 @@ static irqreturn_t vp_config_changed(int irq, void *opaque) static irqreturn_t vp_vring_interrupt(int irq, void *opaque) { struct virtio_pci_device *vp_dev = opaque; - struct virtio_pci_vq_info *info; irqreturn_t ret = IRQ_NONE; - unsigned long flags; + struct virtqueue *vq; - spin_lock_irqsave(&vp_dev->lock, flags); - list_for_each_entry(info, &vp_dev->virtqueues, node) { - if (vring_interrupt(irq, info->vq) == IRQ_HANDLED) + list_for_each_entry(vq, &vp_dev->vdev.vqs, list) { + if (vq->callback && vring_interrupt(irq, vq) == IRQ_HANDLED) ret = IRQ_HANDLED; } - spin_unlock_irqrestore(&vp_dev->lock, flags); return ret; } @@ -102,237 +97,185 @@ static irqreturn_t vp_interrupt(int irq, void *opaque) return vp_vring_interrupt(irq, opaque); } -static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, - bool per_vq_vectors) +static void vp_remove_vqs(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); - const char *name = dev_name(&vp_dev->vdev.dev); - unsigned i, v; - int err = -ENOMEM; - - vp_dev->msix_vectors = nvectors; - - vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names, - GFP_KERNEL); - if (!vp_dev->msix_names) - goto error; - vp_dev->msix_affinity_masks - = kzalloc(nvectors * sizeof *vp_dev->msix_affinity_masks, - GFP_KERNEL); - if (!vp_dev->msix_affinity_masks) - goto error; - for (i = 0; i < nvectors; ++i) - if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i], - GFP_KERNEL)) - goto error; - - err = pci_alloc_irq_vectors(vp_dev->pci_dev, nvectors, nvectors, - PCI_IRQ_MSIX); - if (err < 0) - goto error; - vp_dev->msix_enabled = 1; - - /* Set the vector used for configuration */ - v = vp_dev->msix_used_vectors; - snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, - "%s-config", name); - err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), - vp_config_changed, 0, vp_dev->msix_names[v], - vp_dev); - if (err) - goto error; - ++vp_dev->msix_used_vectors; - - v = vp_dev->config_vector(vp_dev, v); - /* Verify we had enough resources to assign the vector */ - if (v == VIRTIO_MSI_NO_VECTOR) { - err = -EBUSY; - goto error; - } - - if (!per_vq_vectors) { - /* Shared vector for all VQs */ - v = vp_dev->msix_used_vectors; - snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, - "%s-virtqueues", name); - err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), - vp_vring_interrupt, 0, vp_dev->msix_names[v], - vp_dev); - if (err) - goto error; - ++vp_dev->msix_used_vectors; - } - return 0; -error: - return err; -} - -static struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned index, - void (*callback)(struct virtqueue *vq), - const char *name, - u16 msix_vec) -{ - struct virtio_pci_device *vp_dev = to_vp_device(vdev); - struct virtio_pci_vq_info *info = kmalloc(sizeof *info, GFP_KERNEL); - struct virtqueue *vq; - unsigned long flags; - - /* fill out our structure that represents an active queue */ - if (!info) - return ERR_PTR(-ENOMEM); + struct virtqueue *vq, *n; - vq = vp_dev->setup_vq(vp_dev, info, index, callback, name, msix_vec); - if (IS_ERR(vq)) - goto out_info; + list_for_each_entry_safe(vq, n, &vdev->vqs, list) { + if (vp_dev->msix_vector_map) { + int v = vp_dev->msix_vector_map[vq->index]; - info->vq = vq; - if (callback) { - spin_lock_irqsave(&vp_dev->lock, flags); - list_add(&info->node, &vp_dev->virtqueues); - spin_unlock_irqrestore(&vp_dev->lock, flags); - } else { - INIT_LIST_HEAD(&info->node); + if (v != VIRTIO_MSI_NO_VECTOR) + free_irq(pci_irq_vector(vp_dev->pci_dev, v), + vq); + } + vp_dev->del_vq(vq); } - - vp_dev->vqs[index] = info; - return vq; - -out_info: - kfree(info); - return vq; -} - -static void vp_del_vq(struct virtqueue *vq) -{ - struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); - struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index]; - unsigned long flags; - - spin_lock_irqsave(&vp_dev->lock, flags); - list_del(&info->node); - spin_unlock_irqrestore(&vp_dev->lock, flags); - - vp_dev->del_vq(info); - kfree(info); } /* the config->del_vqs() implementation */ void vp_del_vqs(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); - struct virtqueue *vq, *n; int i; - list_for_each_entry_safe(vq, n, &vdev->vqs, list) { - if (vp_dev->per_vq_vectors) { - int v = vp_dev->vqs[vq->index]->msix_vector; - - if (v != VIRTIO_MSI_NO_VECTOR) - free_irq(pci_irq_vector(vp_dev->pci_dev, v), - vq); - } - vp_del_vq(vq); - } - vp_dev->per_vq_vectors = false; - - if (vp_dev->intx_enabled) { - free_irq(vp_dev->pci_dev->irq, vp_dev); - vp_dev->intx_enabled = 0; - } + if (WARN_ON_ONCE(list_empty_careful(&vdev->vqs))) + return; - for (i = 0; i < vp_dev->msix_used_vectors; ++i) - free_irq(pci_irq_vector(vp_dev->pci_dev, i), vp_dev); + vp_remove_vqs(vdev); - for (i = 0; i < vp_dev->msix_vectors; i++) - if (vp_dev->msix_affinity_masks[i]) + if (vp_dev->pci_dev->msix_enabled) { + for (i = 0; i < vp_dev->msix_vectors; i++) free_cpumask_var(vp_dev->msix_affinity_masks[i]); - if (vp_dev->msix_enabled) { /* Disable the vector used for configuration */ vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR); - pci_free_irq_vectors(vp_dev->pci_dev); - vp_dev->msix_enabled = 0; + kfree(vp_dev->msix_affinity_masks); + kfree(vp_dev->msix_names); + kfree(vp_dev->msix_vector_map); } - vp_dev->msix_vectors = 0; - vp_dev->msix_used_vectors = 0; - kfree(vp_dev->msix_names); - vp_dev->msix_names = NULL; - kfree(vp_dev->msix_affinity_masks); - vp_dev->msix_affinity_masks = NULL; - kfree(vp_dev->vqs); - vp_dev->vqs = NULL; + free_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_dev); + pci_free_irq_vectors(vp_dev->pci_dev); } static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[], - bool per_vq_vectors) + struct virtqueue *vqs[], vq_callback_t *callbacks[], + const char * const names[], struct irq_affinity *desc) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); + const char *name = dev_name(&vp_dev->vdev.dev); + int i, err = -ENOMEM, allocated_vectors, nvectors; + unsigned flags = PCI_IRQ_MSIX; + bool shared = false; u16 msix_vec; - int i, err, nvectors, allocated_vectors; - vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL); - if (!vp_dev->vqs) - return -ENOMEM; + if (desc) { + flags |= PCI_IRQ_AFFINITY; + desc->pre_vectors++; /* virtio config vector */ + } - if (per_vq_vectors) { - /* Best option: one for change interrupt, one per vq. */ - nvectors = 1; - for (i = 0; i < nvqs; ++i) - if (callbacks[i]) - ++nvectors; - } else { - /* Second best: one for change, shared for all vqs. */ - nvectors = 2; + nvectors = 1; + for (i = 0; i < nvqs; i++) + if (callbacks[i]) + nvectors++; + + /* Try one vector per queue first. */ + err = pci_alloc_irq_vectors_affinity(vp_dev->pci_dev, nvectors, + nvectors, flags, desc); + if (err < 0) { + /* Fallback to one vector for config, one shared for queues. */ + shared = true; + err = pci_alloc_irq_vectors(vp_dev->pci_dev, 2, 2, + PCI_IRQ_MSIX); + if (err < 0) + return err; + } + if (err < 0) + return err; + + vp_dev->msix_vectors = nvectors; + vp_dev->msix_names = kmalloc_array(nvectors, + sizeof(*vp_dev->msix_names), GFP_KERNEL); + if (!vp_dev->msix_names) + goto out_free_irq_vectors; + + vp_dev->msix_affinity_masks = kcalloc(nvectors, + sizeof(*vp_dev->msix_affinity_masks), GFP_KERNEL); + if (!vp_dev->msix_affinity_masks) + goto out_free_msix_names; + + for (i = 0; i < nvectors; ++i) { + if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i], + GFP_KERNEL)) + goto out_free_msix_affinity_masks; } - err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors); + /* Set the vector used for configuration */ + snprintf(vp_dev->msix_names[0], sizeof(*vp_dev->msix_names), + "%s-config", name); + err = request_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_config_changed, + 0, vp_dev->msix_names[0], vp_dev); if (err) - goto error_find; + goto out_free_msix_affinity_masks; + + /* Verify we had enough resources to assign the vector */ + if (vp_dev->config_vector(vp_dev, 0) == VIRTIO_MSI_NO_VECTOR) { + err = -EBUSY; + goto out_free_config_irq; + } + + vp_dev->msix_vector_map = kmalloc_array(nvqs, + sizeof(*vp_dev->msix_vector_map), GFP_KERNEL); + if (!vp_dev->msix_vector_map) + goto out_disable_config_irq; - vp_dev->per_vq_vectors = per_vq_vectors; - allocated_vectors = vp_dev->msix_used_vectors; + allocated_vectors = 1; /* vector 0 is the config interrupt */ for (i = 0; i < nvqs; ++i) { if (!names[i]) { vqs[i] = NULL; continue; } - if (!callbacks[i]) - msix_vec = VIRTIO_MSI_NO_VECTOR; - else if (vp_dev->per_vq_vectors) - msix_vec = allocated_vectors++; + if (callbacks[i]) + msix_vec = allocated_vectors; else - msix_vec = VP_MSIX_VQ_VECTOR; - vqs[i] = vp_setup_vq(vdev, i, callbacks[i], names[i], msix_vec); + msix_vec = VIRTIO_MSI_NO_VECTOR; + + vqs[i] = vp_dev->setup_vq(vp_dev, i, callbacks[i], names[i], + msix_vec); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); - goto error_find; + goto out_remove_vqs; } - if (!vp_dev->per_vq_vectors || msix_vec == VIRTIO_MSI_NO_VECTOR) + if (msix_vec == VIRTIO_MSI_NO_VECTOR) { + vp_dev->msix_vector_map[i] = VIRTIO_MSI_NO_VECTOR; continue; + } - /* allocate per-vq irq if available and necessary */ - snprintf(vp_dev->msix_names[msix_vec], - sizeof *vp_dev->msix_names, - "%s-%s", + snprintf(vp_dev->msix_names[i + 1], + sizeof(*vp_dev->msix_names), "%s-%s", dev_name(&vp_dev->vdev.dev), names[i]); err = request_irq(pci_irq_vector(vp_dev->pci_dev, msix_vec), - vring_interrupt, 0, - vp_dev->msix_names[msix_vec], - vqs[i]); - if (err) - goto error_find; + vring_interrupt, IRQF_SHARED, + vp_dev->msix_names[i + 1], vqs[i]); + if (err) { + /* don't free this irq on error */ + vp_dev->msix_vector_map[i] = VIRTIO_MSI_NO_VECTOR; + goto out_remove_vqs; + } + vp_dev->msix_vector_map[i] = msix_vec; + + /* + * Use a different vector for each queue if they are available, + * else share the same vector for all VQs. + */ + if (!shared) + allocated_vectors++; } + return 0; -error_find: - vp_del_vqs(vdev); +out_remove_vqs: + vp_remove_vqs(vdev); + kfree(vp_dev->msix_vector_map); +out_disable_config_irq: + vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR); +out_free_config_irq: + free_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_dev); +out_free_msix_affinity_masks: + for (i = 0; i < nvectors; i++) { + if (vp_dev->msix_affinity_masks[i]) + free_cpumask_var(vp_dev->msix_affinity_masks[i]); + } + kfree(vp_dev->msix_affinity_masks); +out_free_msix_names: + kfree(vp_dev->msix_names); +out_free_irq_vectors: + pci_free_irq_vectors(vp_dev->pci_dev); return err; } @@ -343,53 +286,42 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned nvqs, struct virtio_pci_device *vp_dev = to_vp_device(vdev); int i, err; - vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL); - if (!vp_dev->vqs) - return -ENOMEM; - err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED, dev_name(&vdev->dev), vp_dev); if (err) - goto out_del_vqs; + return err; - vp_dev->intx_enabled = 1; - vp_dev->per_vq_vectors = false; for (i = 0; i < nvqs; ++i) { if (!names[i]) { vqs[i] = NULL; continue; } - vqs[i] = vp_setup_vq(vdev, i, callbacks[i], names[i], + vqs[i] = vp_dev->setup_vq(vp_dev, i, callbacks[i], names[i], VIRTIO_MSI_NO_VECTOR); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); - goto out_del_vqs; + goto out_remove_vqs; } } return 0; -out_del_vqs: - vp_del_vqs(vdev); + +out_remove_vqs: + vp_remove_vqs(vdev); + free_irq(pci_irq_vector(vp_dev->pci_dev, 0), vp_dev); return err; } /* the config->find_vqs() implementation */ int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[]) + struct virtqueue *vqs[], vq_callback_t *callbacks[], + const char * const names[], struct irq_affinity *desc) { int err; - /* Try MSI-X with one vector per queue. */ - err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, true); + err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, desc); if (!err) return 0; - /* Fallback: MSI-X with one vector for config, one shared for queues. */ - err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, false); - if (!err) - return 0; - /* Finally fall back to regular interrupts. */ return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names); } @@ -409,16 +341,15 @@ int vp_set_vq_affinity(struct virtqueue *vq, int cpu) { struct virtio_device *vdev = vq->vdev; struct virtio_pci_device *vp_dev = to_vp_device(vdev); - struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index]; - struct cpumask *mask; - unsigned int irq; if (!vq->callback) return -EINVAL; - if (vp_dev->msix_enabled) { - mask = vp_dev->msix_affinity_masks[info->msix_vector]; - irq = pci_irq_vector(vp_dev->pci_dev, info->msix_vector); + if (vp_dev->pci_dev->msix_enabled) { + int vec = vp_dev->msix_vector_map[vq->index]; + struct cpumask *mask = vp_dev->msix_affinity_masks[vec]; + unsigned int irq = pci_irq_vector(vp_dev->pci_dev, vec); + if (cpu == -1) irq_set_affinity_hint(irq, NULL); else { @@ -430,6 +361,17 @@ int vp_set_vq_affinity(struct virtqueue *vq, int cpu) return 0; } +const struct cpumask *vp_get_vq_affinity(struct virtio_device *vdev, int index) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + unsigned int *map = vp_dev->msix_vector_map; + + if (!map || map[index] == VIRTIO_MSI_NO_VECTOR) + return NULL; + + return pci_irq_get_affinity(vp_dev->pci_dev, map[index]); +} + #ifdef CONFIG_PM_SLEEP static int virtio_pci_freeze(struct device *dev) { @@ -498,8 +440,6 @@ static int virtio_pci_probe(struct pci_dev *pci_dev, vp_dev->vdev.dev.parent = &pci_dev->dev; vp_dev->vdev.dev.release = virtio_pci_release_dev; vp_dev->pci_dev = pci_dev; - INIT_LIST_HEAD(&vp_dev->virtqueues); - spin_lock_init(&vp_dev->lock); /* enable the device */ rc = pci_enable_device(pci_dev); diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index b2f666250ae0..ac8c9d788964 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -31,17 +31,6 @@ #include <linux/highmem.h> #include <linux/spinlock.h> -struct virtio_pci_vq_info { - /* the actual virtqueue */ - struct virtqueue *vq; - - /* the list node for the virtqueues list */ - struct list_head node; - - /* MSI-X vector (or none) */ - unsigned msix_vector; -}; - /* Our device structure */ struct virtio_pci_device { struct virtio_device vdev; @@ -75,47 +64,25 @@ struct virtio_pci_device { /* the IO mapping for the PCI config space */ void __iomem *ioaddr; - /* a list of queues so we can dispatch IRQs */ - spinlock_t lock; - struct list_head virtqueues; - - /* array of all queues for house-keeping */ - struct virtio_pci_vq_info **vqs; - - /* MSI-X support */ - int msix_enabled; - int intx_enabled; cpumask_var_t *msix_affinity_masks; /* Name strings for interrupts. This size should be enough, * and I'm too lazy to allocate each name separately. */ char (*msix_names)[256]; - /* Number of available vectors */ - unsigned msix_vectors; - /* Vectors allocated, excluding per-vq vectors if any */ - unsigned msix_used_vectors; - - /* Whether we have vector per vq */ - bool per_vq_vectors; + /* Total Number of MSI-X vectors (including per-VQ ones). */ + int msix_vectors; + /* Map of per-VQ MSI-X vectors, may be NULL */ + unsigned *msix_vector_map; struct virtqueue *(*setup_vq)(struct virtio_pci_device *vp_dev, - struct virtio_pci_vq_info *info, unsigned idx, void (*callback)(struct virtqueue *vq), const char *name, u16 msix_vec); - void (*del_vq)(struct virtio_pci_vq_info *info); + void (*del_vq)(struct virtqueue *vq); u16 (*config_vector)(struct virtio_pci_device *vp_dev, u16 vector); }; -/* Constants for MSI-X */ -/* Use first vector for configuration changes, second and the rest for - * virtqueues Thus, we need at least 2 vectors for MSI. */ -enum { - VP_MSIX_CONFIG_VECTOR = 0, - VP_MSIX_VQ_VECTOR = 1, -}; - /* Convert a generic virtio device to our structure */ static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev) { @@ -130,9 +97,8 @@ bool vp_notify(struct virtqueue *vq); void vp_del_vqs(struct virtio_device *vdev); /* the config->find_vqs() implementation */ int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[]); + struct virtqueue *vqs[], vq_callback_t *callbacks[], + const char * const names[], struct irq_affinity *desc); const char *vp_bus_name(struct virtio_device *vdev); /* Setup the affinity for a virtqueue: @@ -142,6 +108,8 @@ const char *vp_bus_name(struct virtio_device *vdev); */ int vp_set_vq_affinity(struct virtqueue *vq, int cpu); +const struct cpumask *vp_get_vq_affinity(struct virtio_device *vdev, int index); + #if IS_ENABLED(CONFIG_VIRTIO_PCI_LEGACY) int virtio_pci_legacy_probe(struct virtio_pci_device *); void virtio_pci_legacy_remove(struct virtio_pci_device *); diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index 6d9e5173d5fa..f7362c5fe18a 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -112,7 +112,6 @@ static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) } static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, - struct virtio_pci_vq_info *info, unsigned index, void (*callback)(struct virtqueue *vq), const char *name, @@ -130,8 +129,6 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, if (!num || ioread32(vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN)) return ERR_PTR(-ENOENT); - info->msix_vector = msix_vec; - /* create the vring */ vq = vring_create_virtqueue(index, num, VIRTIO_PCI_VRING_ALIGN, &vp_dev->vdev, @@ -162,14 +159,13 @@ out_deactivate: return ERR_PTR(err); } -static void del_vq(struct virtio_pci_vq_info *info) +static void del_vq(struct virtqueue *vq) { - struct virtqueue *vq = info->vq; struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); iowrite16(vq->index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL); - if (vp_dev->msix_enabled) { + if (vp_dev->pci_dev->msix_enabled) { iowrite16(VIRTIO_MSI_NO_VECTOR, vp_dev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); /* Flush the write out to device */ @@ -194,6 +190,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .finalize_features = vp_finalize_features, .bus_name = vp_bus_name, .set_vq_affinity = vp_set_vq_affinity, + .get_vq_affinity = vp_get_vq_affinity, }; /* the PCI probing function */ diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 4bf7ab375894..7bc3004b840e 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -293,7 +293,6 @@ static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) } static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, - struct virtio_pci_vq_info *info, unsigned index, void (*callback)(struct virtqueue *vq), const char *name, @@ -323,8 +322,6 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, /* get offset of notification word for this vq */ off = vp_ioread16(&cfg->queue_notify_off); - info->msix_vector = msix_vec; - /* create the vring */ vq = vring_create_virtqueue(index, num, SMP_CACHE_BYTES, &vp_dev->vdev, @@ -387,13 +384,12 @@ err_map_notify: } static int vp_modern_find_vqs(struct virtio_device *vdev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[]) + struct virtqueue *vqs[], vq_callback_t *callbacks[], + const char * const names[], struct irq_affinity *desc) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); struct virtqueue *vq; - int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names); + int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names, desc); if (rc) return rc; @@ -409,14 +405,13 @@ static int vp_modern_find_vqs(struct virtio_device *vdev, unsigned nvqs, return 0; } -static void del_vq(struct virtio_pci_vq_info *info) +static void del_vq(struct virtqueue *vq) { - struct virtqueue *vq = info->vq; struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); vp_iowrite16(vq->index, &vp_dev->common->queue_select); - if (vp_dev->msix_enabled) { + if (vp_dev->pci_dev->msix_enabled) { vp_iowrite16(VIRTIO_MSI_NO_VECTOR, &vp_dev->common->queue_msix_vector); /* Flush the write out to device */ @@ -442,6 +437,7 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .finalize_features = vp_finalize_features, .bus_name = vp_bus_name, .set_vq_affinity = vp_set_vq_affinity, + .get_vq_affinity = vp_get_vq_affinity, }; static const struct virtio_config_ops virtio_pci_config_ops = { @@ -457,6 +453,7 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .finalize_features = vp_finalize_features, .bus_name = vp_bus_name, .set_vq_affinity = vp_set_vq_affinity, + .get_vq_affinity = vp_get_vq_affinity, }; /** diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index df1c9bb90eb5..2096f460498f 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -14,7 +14,7 @@ #include <linux/spinlock.h> #include <linux/list.h> -#include <linux/sched.h> /* schedule_timeout() */ +#include <linux/sched/signal.h> #include <linux/delay.h> #include <linux/export.h> diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 4ce1b66d5092..2cae7b29bb5f 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/slab.h> +#include <linux/sched/signal.h> #include <linux/export.h> #include <linux/moduleparam.h> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index acb00b53a520..52a70ee6014f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -71,9 +71,17 @@ config SOFT_WATCHDOG To compile this driver as a module, choose M here: the module will be called softdog. +config SOFT_WATCHDOG_PRETIMEOUT + bool "Software watchdog pretimeout governor support" + depends on SOFT_WATCHDOG && WATCHDOG_PRETIMEOUT_GOV + help + Enable this if you want to use pretimeout governors with the software + watchdog. Be aware that governors might affect the watchdog because it + is purely software, e.g. the panic governor will stall it! + config DA9052_WATCHDOG tristate "Dialog DA9052 Watchdog" - depends on PMIC_DA9052 + depends on PMIC_DA9052 || COMPILE_TEST select WATCHDOG_CORE help Support for the watchdog in the DA9052 PMIC. Watchdog trigger @@ -85,7 +93,7 @@ config DA9052_WATCHDOG config DA9055_WATCHDOG tristate "Dialog Semiconductor DA9055 Watchdog" - depends on MFD_DA9055 + depends on MFD_DA9055 || COMPILE_TEST select WATCHDOG_CORE help If you say yes here you get support for watchdog on the Dialog @@ -96,7 +104,7 @@ config DA9055_WATCHDOG config DA9063_WATCHDOG tristate "Dialog DA9063 Watchdog" - depends on MFD_DA9063 + depends on MFD_DA9063 || COMPILE_TEST select WATCHDOG_CORE help Support for the watchdog in the DA9063 PMIC. @@ -105,7 +113,7 @@ config DA9063_WATCHDOG config DA9062_WATCHDOG tristate "Dialog DA9062/61 Watchdog" - depends on MFD_DA9062 + depends on MFD_DA9062 || COMPILE_TEST select WATCHDOG_CORE help Support for the watchdog in the DA9062 and DA9061 PMICs. @@ -133,7 +141,8 @@ config GPIO_WATCHDOG_ARCH_INITCALL config MENF21BMC_WATCHDOG tristate "MEN 14F021P00 BMC Watchdog" - depends on MFD_MENF21BMC + depends on MFD_MENF21BMC || COMPILE_TEST + depends on I2C select WATCHDOG_CORE help Say Y here to include support for the MEN 14F021P00 BMC Watchdog. @@ -209,7 +218,7 @@ config ZIIRAVE_WATCHDOG config ARM_SP805_WATCHDOG tristate "ARM SP805 Watchdog" - depends on (ARM || ARM64) && ARM_AMBA + depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA select WATCHDOG_CORE help ARM Primecell SP805 Watchdog timer. This will reboot your system when @@ -237,7 +246,7 @@ config ARM_SBSA_WATCHDOG config ASM9260_WATCHDOG tristate "Alphascale ASM9260 watchdog" - depends on MACH_ASM9260 + depends on MACH_ASM9260 || COMPILE_TEST depends on OF select WATCHDOG_CORE select RESET_CONTROLLER @@ -247,14 +256,14 @@ config ASM9260_WATCHDOG config AT91RM9200_WATCHDOG tristate "AT91RM9200 watchdog" - depends on SOC_AT91RM9200 && MFD_SYSCON + depends on (SOC_AT91RM9200 && MFD_SYSCON) || COMPILE_TEST help Watchdog timer embedded into AT91RM9200 chips. This will reboot your system when the timeout is reached. config AT91SAM9X_WATCHDOG tristate "AT91SAM9X / AT91CAP9 watchdog" - depends on ARCH_AT91 + depends on ARCH_AT91 || COMPILE_TEST select WATCHDOG_CORE help Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will @@ -262,7 +271,7 @@ config AT91SAM9X_WATCHDOG config SAMA5D4_WATCHDOG tristate "Atmel SAMA5D4 Watchdog Timer" - depends on ARCH_AT91 + depends on ARCH_AT91 || COMPILE_TEST select WATCHDOG_CORE help Atmel SAMA5D4 watchdog timer is embedded into SAMA5D4 chips. @@ -293,7 +302,7 @@ config 21285_WATCHDOG config 977_WATCHDOG tristate "NetWinder WB83C977 watchdog" - depends on FOOTBRIDGE && ARCH_NETWINDER + depends on (FOOTBRIDGE && ARCH_NETWINDER) || (ARM && COMPILE_TEST) help Say Y here to include support for the WB977 watchdog included in NetWinder machines. Alternatively say M to compile the driver as @@ -301,6 +310,17 @@ config 977_WATCHDOG Not sure? It's safe to say N. +config GEMINI_WATCHDOG + tristate "Gemini watchdog" + depends on ARCH_GEMINI + select WATCHDOG_CORE + help + Say Y here if to include support for the watchdog timer + embedded in the Cortina Systems Gemini family of devices. + + To compile this driver as a module, choose M here: the + module will be called gemini_wdt. + config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" depends on ARCH_IXP4XX @@ -333,9 +353,9 @@ config HAVE_S3C2410_WATCHDOG config S3C2410_WATCHDOG tristate "S3C2410 Watchdog" - depends on HAVE_S3C2410_WATCHDOG + depends on HAVE_S3C2410_WATCHDOG || COMPILE_TEST select WATCHDOG_CORE - select MFD_SYSCON if ARCH_EXYNOS5 + select MFD_SYSCON if ARCH_EXYNOS help Watchdog timer block in the Samsung SoCs. This will reboot the system when the timer expires with the watchdog enabled. @@ -372,7 +392,7 @@ config DW_WATCHDOG config EP93XX_WATCHDOG tristate "EP93xx Watchdog" - depends on ARCH_EP93XX + depends on ARCH_EP93XX || COMPILE_TEST select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer @@ -383,7 +403,7 @@ config EP93XX_WATCHDOG config OMAP_WATCHDOG tristate "OMAP Watchdog" - depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS + depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || COMPILE_TEST select WATCHDOG_CORE help Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y' @@ -419,7 +439,7 @@ config IOP_WATCHDOG config DAVINCI_WATCHDOG tristate "DaVinci watchdog" - depends on ARCH_DAVINCI || ARCH_KEYSTONE + depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer @@ -432,7 +452,7 @@ config DAVINCI_WATCHDOG config ORION_WATCHDOG tristate "Orion watchdog" - depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU + depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU || COMPILE_TEST depends on ARM select WATCHDOG_CORE help @@ -443,7 +463,7 @@ config ORION_WATCHDOG config RN5T618_WATCHDOG tristate "Ricoh RN5T618 watchdog" - depends on MFD_RN5T618 + depends on MFD_RN5T618 || COMPILE_TEST select WATCHDOG_CORE help If you say yes here you get support for watchdog on the Ricoh @@ -454,7 +474,7 @@ config RN5T618_WATCHDOG config SUNXI_WATCHDOG tristate "Allwinner SoCs watchdog support" - depends on ARCH_SUNXI + depends on ARCH_SUNXI || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include support for the watchdog timer @@ -464,7 +484,7 @@ config SUNXI_WATCHDOG config COH901327_WATCHDOG bool "ST-Ericsson COH 901 327 watchdog" - depends on ARCH_U300 + depends on ARCH_U300 || (ARM && COMPILE_TEST) default y if MACH_U300 select WATCHDOG_CORE help @@ -483,7 +503,7 @@ config TWL4030_WATCHDOG config STMP3XXX_RTC_WATCHDOG tristate "Freescale STMP3XXX & i.MX23/28 watchdog" - depends on RTC_DRV_STMP + depends on RTC_DRV_STMP || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include support for the watchdog timer inside @@ -493,7 +513,7 @@ config STMP3XXX_RTC_WATCHDOG config NUC900_WATCHDOG tristate "Nuvoton NUC900 watchdog" - depends on ARCH_W90X900 + depends on ARCH_W90X900 || COMPILE_TEST help Say Y here if to include support for the watchdog timer for the Nuvoton NUC900 series SoCs. @@ -513,7 +533,7 @@ config TS4800_WATCHDOG config TS72XX_WATCHDOG tristate "TS-72XX SBC Watchdog" - depends on MACH_TS72XX + depends on MACH_TS72XX || COMPILE_TEST help Technologic Systems TS-7200, TS-7250 and TS-7260 boards have watchdog timer implemented in a external CPLD chip. Say Y here @@ -531,7 +551,7 @@ config MAX63XX_WATCHDOG config MAX77620_WATCHDOG tristate "Maxim Max77620 Watchdog Timer" - depends on MFD_MAX77620 + depends on MFD_MAX77620 || COMPILE_TEST help This is the driver for the Max77620 watchdog timer. Say 'Y' here to enable the watchdog timer support for @@ -540,7 +560,7 @@ config MAX77620_WATCHDOG config IMX2_WDT tristate "IMX2+ Watchdog" - depends on ARCH_MXC || ARCH_LAYERSCAPE + depends on ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST select REGMAP_MMIO select WATCHDOG_CORE help @@ -578,7 +598,7 @@ config RETU_WATCHDOG config MOXART_WDT tristate "MOXART watchdog" - depends on ARCH_MOXART + depends on ARCH_MOXART || COMPILE_TEST help Say Y here to include Watchdog timer support for the watchdog existing on the MOXA ART SoC series platforms. @@ -588,7 +608,7 @@ config MOXART_WDT config SIRFSOC_WATCHDOG tristate "SiRFSOC watchdog" - depends on ARCH_SIRF + depends on ARCH_SIRF || COMPILE_TEST select WATCHDOG_CORE default y help @@ -597,7 +617,7 @@ config SIRFSOC_WATCHDOG config ST_LPC_WATCHDOG tristate "STMicroelectronics LPC Watchdog" - depends on ARCH_STI + depends on ARCH_STI || COMPILE_TEST depends on OF select WATCHDOG_CORE help @@ -621,7 +641,7 @@ config TEGRA_WATCHDOG config QCOM_WDT tristate "QCOM watchdog" depends on HAS_IOMEM - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include Watchdog timer support for the watchdog found @@ -633,7 +653,7 @@ config QCOM_WDT config MESON_GXBB_WATCHDOG tristate "Amlogic Meson GXBB SoCs watchdog support" - depends on ARCH_MESON + depends on ARCH_MESON || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include support for the watchdog timer @@ -643,7 +663,7 @@ config MESON_GXBB_WATCHDOG config MESON_WATCHDOG tristate "Amlogic Meson SoCs watchdog support" - depends on ARCH_MESON + depends on ARCH_MESON || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include support for the watchdog timer @@ -653,7 +673,7 @@ config MESON_WATCHDOG config MEDIATEK_WATCHDOG tristate "Mediatek SoCs watchdog support" - depends on ARCH_MEDIATEK + depends on ARCH_MEDIATEK || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include support for the watchdog timer @@ -663,7 +683,7 @@ config MEDIATEK_WATCHDOG config DIGICOLOR_WATCHDOG tristate "Conexant Digicolor SoCs watchdog support" - depends on ARCH_DIGICOLOR + depends on ARCH_DIGICOLOR || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include support for the watchdog timer @@ -685,7 +705,7 @@ config LPC18XX_WATCHDOG config ATLAS7_WATCHDOG tristate "CSRatlas7 watchdog" - depends on ARCH_ATLAS7 + depends on ARCH_ATLAS7 || COMPILE_TEST help Say Y here to include Watchdog timer support for the watchdog existing on the CSRatlas7 series platforms. @@ -714,11 +734,21 @@ config ASPEED_WATCHDOG To compile this driver as a module, choose M here: the module will be called aspeed_wdt. +config ZX2967_WATCHDOG + tristate "ZTE zx2967 SoCs watchdog support" + depends on ARCH_ZX + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in ZTE zx2967 SoCs. + To compile this driver as a module, choose M here: the + module will be called zx2967_wdt. + # AVR32 Architecture config AT32AP700X_WDT tristate "AT32AP700x watchdog" - depends on CPU_AT32AP700X + depends on CPU_AT32AP700X || COMPILE_TEST help Watchdog timer embedded into AT32AP700x devices. This will reboot your system when the timeout is reached. @@ -835,7 +865,7 @@ config GEODE_WDT config SC520_WDT tristate "AMD Elan SC520 processor Watchdog" - depends on MELAN + depends on MELAN || COMPILE_TEST help This is the driver for the hardware watchdog built in to the AMD "Elan" SC520 microcomputer commonly used in embedded systems. @@ -1108,7 +1138,8 @@ config NV_TCO config RDC321X_WDT tristate "RDC R-321x SoC watchdog" - depends on X86_RDC321X + depends on X86_RDC321X || COMPILE_TEST + depends on PCI help This is the driver for the built in hardware watchdog in the RDC R-321x SoC. @@ -1326,6 +1357,16 @@ config NI903X_WDT To compile this driver as a module, choose M here: the module will be called ni903x_wdt. +config NIC7018_WDT + tristate "NIC7018 Watchdog" + depends on X86 && ACPI + select WATCHDOG_CORE + ---help--- + Support for National Instruments NIC7018 Watchdog. + + To compile this driver as a module, choose M here: the module will be + called nic7018_wdt. + # M32R Architecture # M68K Architecture @@ -1343,14 +1384,14 @@ config M54xx_WATCHDOG config ATH79_WDT tristate "Atheros AR71XX/AR724X/AR913X hardware watchdog" - depends on ATH79 + depends on ATH79 || (ARM && COMPILE_TEST) help Hardware driver for the built-in watchdog timer on the Atheros AR71XX/AR724X/AR913X SoCs. config BCM47XX_WDT tristate "Broadcom BCM47xx Watchdog Timer" - depends on BCM47XX || ARCH_BCM_5301X + depends on BCM47XX || ARCH_BCM_5301X || COMPILE_TEST select WATCHDOG_CORE help Hardware driver for the Broadcom BCM47xx Watchdog Timer. @@ -1367,7 +1408,7 @@ config RC32434_WDT config INDYDOG tristate "Indy/I2 Hardware Watchdog" - depends on SGI_HAS_INDYDOG + depends on SGI_HAS_INDYDOG || (MIPS && COMPILE_TEST) help Hardware driver for the Indy's/I2's watchdog. This is a watchdog timer that will reboot the machine after a 60 second @@ -1383,7 +1424,7 @@ config JZ4740_WDT config WDT_MTX1 tristate "MTX-1 Hardware Watchdog" - depends on MIPS_MTX1 + depends on MIPS_MTX1 || (MIPS && COMPILE_TEST) help Hardware driver for the MTX-1 boards. This is a watchdog timer that will reboot the machine after a 100 seconds timer expired. @@ -1391,6 +1432,7 @@ config WDT_MTX1 config PNX833X_WDT tristate "PNX833x Hardware Watchdog" depends on SOC_PNX8335 + depends on BROKEN help Hardware driver for the PNX833x's watchdog. This is a watchdog timer that will reboot the machine after a programmable @@ -1399,7 +1441,7 @@ config PNX833X_WDT config SIBYTE_WDOG tristate "Sibyte SoC hardware watchdog" - depends on CPU_SB1 + depends on CPU_SB1 || (MIPS && COMPILE_TEST) help Watchdog driver for the built in watchdog hardware in Sibyte SoC processors. There are apparently two watchdog timers @@ -1412,13 +1454,13 @@ config SIBYTE_WDOG config AR7_WDT tristate "TI AR7 Watchdog Timer" - depends on AR7 + depends on AR7 || (MIPS && COMPILE_TEST) help Hardware driver for the TI AR7 Watchdog Timer. config TXX9_WDT tristate "Toshiba TXx9 Watchdog Timer" - depends on CPU_TX39XX || CPU_TX49XX + depends on CPU_TX39XX || CPU_TX49XX || (MIPS && COMPILE_TEST) select WATCHDOG_CORE help Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs. @@ -1454,7 +1496,7 @@ config BCM63XX_WDT config BCM2835_WDT tristate "Broadcom BCM2835 hardware watchdog" - depends on ARCH_BCM2835 + depends on ARCH_BCM2835 || (OF && COMPILE_TEST) select WATCHDOG_CORE help Watchdog driver for the built in watchdog hardware in Broadcom @@ -1465,7 +1507,7 @@ config BCM2835_WDT config BCM_KONA_WDT tristate "BCM Kona Watchdog" - depends on ARCH_BCM_MOBILE + depends on ARCH_BCM_MOBILE || COMPILE_TEST select WATCHDOG_CORE help Support for the watchdog timer on the following Broadcom BCM281xx @@ -1477,7 +1519,7 @@ config BCM_KONA_WDT config BCM_KONA_WDT_DEBUG bool "DEBUGFS support for BCM Kona Watchdog" - depends on BCM_KONA_WDT + depends on BCM_KONA_WDT || COMPILE_TEST help If enabled, adds /sys/kernel/debug/bcm_kona_wdt/info which provides access to the driver's internal data structures as well as watchdog @@ -1538,7 +1580,7 @@ config MT7621_WDT config PIC32_WDT tristate "Microchip PIC32 hardware watchdog" select WATCHDOG_CORE - depends on MACH_PIC32 + depends on MACH_PIC32 || (MIPS && COMPILE_TEST) help Watchdog driver for the built in watchdog hardware in a PIC32. @@ -1551,7 +1593,7 @@ config PIC32_WDT config PIC32_DMT tristate "Microchip PIC32 Deadman Timer" select WATCHDOG_CORE - depends on MACH_PIC32 + depends on MACH_PIC32 || (MIPS && COMPILE_TEST) help Watchdog driver for PIC32 instruction fetch counting timer. This specific timer is typically be used in misson critical and safety critical @@ -1573,7 +1615,7 @@ config GEF_WDT config MPC5200_WDT bool "MPC52xx Watchdog Timer" - depends on PPC_MPC52xx + depends on PPC_MPC52xx || COMPILE_TEST help Use General Purpose Timer (GPT) 0 on the MPC5200 as Watchdog. @@ -1592,11 +1634,11 @@ config 8xxx_WDT config MV64X60_WDT tristate "MV64X60 (Marvell Discovery) Watchdog Timer" - depends on MV64X60 + depends on MV64X60 || COMPILE_TEST config PIKA_WDT tristate "PIKA FPGA Watchdog" - depends on WARP + depends on WARP || (PPC64 && COMPILE_TEST) default y help This enables the watchdog in the PIKA FPGA. Currently used on @@ -1646,7 +1688,7 @@ config MEN_A21_WDT config WATCHDOG_RTAS tristate "RTAS watchdog" - depends on PPC_RTAS + depends on PPC_RTAS || (PPC64 && COMPILE_TEST) help This driver adds watchdog support for the RTAS watchdog. @@ -1674,7 +1716,7 @@ config DIAG288_WATCHDOG config SH_WDT tristate "SuperH Watchdog" - depends on SUPERH && (CPU_SH3 || CPU_SH4) + depends on SUPERH && (CPU_SH3 || CPU_SH4 || COMPILE_TEST) select WATCHDOG_CORE help This driver adds watchdog support for the integrated watchdog in the @@ -1741,7 +1783,7 @@ config XEN_WDT config UML_WATCHDOG tristate "UML watchdog" - depends on UML + depends on UML || COMPILE_TEST # # ISA-based Watchdog Cards diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 0c3d35e3c334..a2126e2a99ae 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o +obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o @@ -82,6 +83,7 @@ obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o +obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o @@ -139,6 +141,7 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o +obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o # M32R Architecture diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c index d0b59ba0f661..53da001f0838 100644 --- a/drivers/watchdog/asm9260_wdt.c +++ b/drivers/watchdog/asm9260_wdt.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/reboot.h> #include <linux/reset.h> #include <linux/watchdog.h> @@ -59,7 +58,6 @@ struct asm9260_wdt_priv { struct clk *clk; struct clk *clk_ahb; struct reset_control *rst; - struct notifier_block restart_handler; void __iomem *iobase; int irq; @@ -172,15 +170,14 @@ static irqreturn_t asm9260_wdt_irq(int irq, void *devid) return IRQ_HANDLED; } -static int asm9260_restart_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int asm9260_restart(struct watchdog_device *wdd, unsigned long action, + void *data) { - struct asm9260_wdt_priv *priv = - container_of(this, struct asm9260_wdt_priv, restart_handler); + struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd); asm9260_wdt_sys_reset(priv); - return NOTIFY_DONE; + return 0; } static const struct watchdog_info asm9260_wdt_ident = { @@ -189,13 +186,14 @@ static const struct watchdog_info asm9260_wdt_ident = { .identity = "Alphascale asm9260 Watchdog", }; -static struct watchdog_ops asm9260_wdt_ops = { +static const struct watchdog_ops asm9260_wdt_ops = { .owner = THIS_MODULE, .start = asm9260_wdt_enable, .stop = asm9260_wdt_disable, .get_timeleft = asm9260_wdt_gettimeleft, .ping = asm9260_wdt_feed, .set_timeout = asm9260_wdt_settimeout, + .restart = asm9260_restart, }; static int asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv) @@ -335,18 +333,14 @@ static int asm9260_wdt_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to request IRQ\n"); } + watchdog_set_restart_priority(wdd, 128); + ret = watchdog_register_device(wdd); if (ret) goto clk_off; platform_set_drvdata(pdev, priv); - priv->restart_handler.notifier_call = asm9260_restart_handler; - priv->restart_handler.priority = 128; - ret = register_restart_handler(&priv->restart_handler); - if (ret) - dev_warn(&pdev->dev, "cannot register restart handler\n"); - dev_info(&pdev->dev, "Watchdog enabled (timeout: %d sec, mode: %s)\n", wdd->timeout, mode_name[priv->mode]); return 0; @@ -370,8 +364,6 @@ static int asm9260_wdt_remove(struct platform_device *pdev) asm9260_wdt_disable(&priv->wdd); - unregister_restart_handler(&priv->restart_handler); - watchdog_unregister_device(&priv->wdd); clk_disable_unprepare(priv->clk); diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index f5ad8023c2e6..1c652582de40 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -136,15 +136,6 @@ static const struct watchdog_info aspeed_wdt_info = { .identity = KBUILD_MODNAME, }; -static int aspeed_wdt_remove(struct platform_device *pdev) -{ - struct aspeed_wdt *wdt = platform_get_drvdata(pdev); - - watchdog_unregister_device(&wdt->wdd); - - return 0; -} - static int aspeed_wdt_probe(struct platform_device *pdev) { struct aspeed_wdt *wdt; @@ -187,20 +178,17 @@ static int aspeed_wdt_probe(struct platform_device *pdev) set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); } - ret = watchdog_register_device(&wdt->wdd); + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); if (ret) { dev_err(&pdev->dev, "failed to register\n"); return ret; } - platform_set_drvdata(pdev, wdt); - return 0; } static struct platform_driver aspeed_watchdog_driver = { .probe = aspeed_wdt_probe, - .remove = aspeed_wdt_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = of_match_ptr(aspeed_wdt_of_table), diff --git a/drivers/watchdog/atlas7_wdt.c b/drivers/watchdog/atlas7_wdt.c index ed80734befae..4abdcabd8219 100644 --- a/drivers/watchdog/atlas7_wdt.c +++ b/drivers/watchdog/atlas7_wdt.c @@ -105,7 +105,7 @@ static const struct watchdog_info atlas7_wdt_ident = { .identity = "atlas7 Watchdog", }; -static struct watchdog_ops atlas7_wdt_ops = { +static const struct watchdog_ops atlas7_wdt_ops = { .owner = THIS_MODULE, .start = atlas7_wdt_enable, .stop = atlas7_wdt_disable, diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index c32c45bd8b09..b339e0e67b4c 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -14,7 +14,6 @@ */ #include <linux/delay.h> -#include <linux/reboot.h> #include <linux/types.h> #include <linux/module.h> #include <linux/io.h> @@ -37,9 +36,9 @@ #define PM_RSTC_RESET 0x00000102 /* - * The Raspberry Pi firmware uses the RSTS register to know which partiton - * to boot from. The partiton value is spread into bits 0, 2, 4, 6, 8, 10. - * Partiton 63 is a special partition used by the firmware to indicate halt. + * The Raspberry Pi firmware uses the RSTS register to know which partition + * to boot from. The partition value is spread into bits 0, 2, 4, 6, 8, 10. + * Partition 63 is a special partition used by the firmware to indicate halt. */ #define PM_RSTS_RASPBERRYPI_HALT 0x555 @@ -49,7 +48,6 @@ struct bcm2835_wdt { void __iomem *base; spinlock_t lock; - struct notifier_block restart_handler; }; static unsigned int heartbeat; @@ -99,11 +97,37 @@ static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog) return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET); } +static void __bcm2835_restart(struct bcm2835_wdt *wdt) +{ + u32 val; + + /* use a timeout of 10 ticks (~150us) */ + writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG); + val = readl_relaxed(wdt->base + PM_RSTC); + val &= PM_RSTC_WRCFG_CLR; + val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + writel_relaxed(val, wdt->base + PM_RSTC); + + /* No sleeping, possibly atomic. */ + mdelay(1); +} + +static int bcm2835_restart(struct watchdog_device *wdog, + unsigned long action, void *data) +{ + struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); + + __bcm2835_restart(wdt); + + return 0; +} + static const struct watchdog_ops bcm2835_wdt_ops = { .owner = THIS_MODULE, .start = bcm2835_wdt_start, .stop = bcm2835_wdt_stop, .get_timeleft = bcm2835_wdt_get_timeleft, + .restart = bcm2835_restart, }; static const struct watchdog_info bcm2835_wdt_info = { @@ -120,26 +144,6 @@ static struct watchdog_device bcm2835_wdt_wdd = { .timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET), }; -static int -bcm2835_restart(struct notifier_block *this, unsigned long mode, void *cmd) -{ - struct bcm2835_wdt *wdt = container_of(this, struct bcm2835_wdt, - restart_handler); - u32 val; - - /* use a timeout of 10 ticks (~150us) */ - writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG); - val = readl_relaxed(wdt->base + PM_RSTC); - val &= PM_RSTC_WRCFG_CLR; - val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; - writel_relaxed(val, wdt->base + PM_RSTC); - - /* No sleeping, possibly atomic. */ - mdelay(1); - - return 0; -} - /* * We can't really power off, but if we do the normal reset scheme, and * indicate to bootcode.bin not to reboot, then most of the chip will be @@ -163,13 +167,13 @@ static void bcm2835_power_off(void) writel_relaxed(val, wdt->base + PM_RSTS); /* Continue with normal reset mechanism */ - bcm2835_restart(&wdt->restart_handler, REBOOT_HARD, NULL); + __bcm2835_restart(wdt); } static int bcm2835_wdt_probe(struct platform_device *pdev) { + struct resource *res; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct bcm2835_wdt *wdt; int err; @@ -180,16 +184,15 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) spin_lock_init(&wdt->lock); - wdt->base = of_iomap(np, 0); - if (!wdt->base) { - dev_err(dev, "Failed to remap watchdog regs"); - return -ENODEV; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt); watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev); watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout); - bcm2835_wdt_wdd.parent = &pdev->dev; + bcm2835_wdt_wdd.parent = dev; if (bcm2835_wdt_is_running(wdt)) { /* * The currently active timeout value (set by the @@ -201,16 +204,16 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) */ set_bit(WDOG_HW_RUNNING, &bcm2835_wdt_wdd.status); } - err = watchdog_register_device(&bcm2835_wdt_wdd); + + watchdog_set_restart_priority(&bcm2835_wdt_wdd, 128); + + watchdog_stop_on_reboot(&bcm2835_wdt_wdd); + err = devm_watchdog_register_device(dev, &bcm2835_wdt_wdd); if (err) { dev_err(dev, "Failed to register watchdog device"); - iounmap(wdt->base); return err; } - wdt->restart_handler.notifier_call = bcm2835_restart; - wdt->restart_handler.priority = 128; - register_restart_handler(&wdt->restart_handler); if (pm_power_off == NULL) pm_power_off = bcm2835_power_off; @@ -220,22 +223,12 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) static int bcm2835_wdt_remove(struct platform_device *pdev) { - struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); - - unregister_restart_handler(&wdt->restart_handler); if (pm_power_off == bcm2835_power_off) pm_power_off = NULL; - watchdog_unregister_device(&bcm2835_wdt_wdd); - iounmap(wdt->base); return 0; } -static void bcm2835_wdt_shutdown(struct platform_device *pdev) -{ - bcm2835_wdt_stop(&bcm2835_wdt_wdd); -} - static const struct of_device_id bcm2835_wdt_of_match[] = { { .compatible = "brcm,bcm2835-pm-wdt", }, {}, @@ -245,7 +238,6 @@ MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match); static struct platform_driver bcm2835_wdt_driver = { .probe = bcm2835_wdt_probe, .remove = bcm2835_wdt_remove, - .shutdown = bcm2835_wdt_shutdown, .driver = { .name = "bcm2835-wdt", .of_match_table = bcm2835_wdt_of_match, diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index a1900b9ab6c4..35725e21b18a 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -226,9 +226,6 @@ static int bcm47xx_wdt_remove(struct platform_device *pdev) { struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev); - if (!wdt) - return -ENXIO; - watchdog_unregister_device(&wdt->wdd); return 0; diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c index 4814c00b32f6..c1b8e534fb55 100644 --- a/drivers/watchdog/bcm7038_wdt.c +++ b/drivers/watchdog/bcm7038_wdt.c @@ -101,7 +101,7 @@ static unsigned int bcm7038_wdt_get_timeleft(struct watchdog_device *wdog) return time_left / wdt->rate; } -static struct watchdog_info bcm7038_wdt_info = { +static const struct watchdog_info bcm7038_wdt_info = { .identity = "Broadcom BCM7038 Watchdog Timer", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index e0c98423f2c9..6fce17d5b9f1 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -266,7 +266,7 @@ static int bcm_kona_wdt_stop(struct watchdog_device *wdog) SECWDOG_SRSTEN_MASK, 0); } -static struct watchdog_ops bcm_kona_wdt_ops = { +static const struct watchdog_ops bcm_kona_wdt_ops = { .owner = THIS_MODULE, .start = bcm_kona_wdt_start, .stop = bcm_kona_wdt_stop, @@ -274,7 +274,7 @@ static struct watchdog_ops bcm_kona_wdt_ops = { .get_timeleft = bcm_kona_wdt_get_timeleft, }; -static struct watchdog_info bcm_kona_wdt_info = { +static const struct watchdog_info bcm_kona_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "Broadcom Kona Watchdog Timer", diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 04da4b66c75e..3ad1e44bef44 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -192,12 +192,12 @@ static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev, return 0; } -static struct watchdog_info booke_wdt_info = { +static struct watchdog_info booke_wdt_info __ro_after_init = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "PowerPC Book-E Watchdog", }; -static struct watchdog_ops booke_wdt_ops = { +static const struct watchdog_ops booke_wdt_ops = { .owner = THIS_MODULE, .start = booke_wdt_start, .stop = booke_wdt_stop, diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 98acef72334d..8d61e8bfe60b 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -262,7 +262,7 @@ static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) * Info structure used to indicate the features supported by the device * to the upper layers. This is defined in watchdog.h header file. */ -static struct watchdog_info cdns_wdt_info = { +static const struct watchdog_info cdns_wdt_info = { .identity = "cdns_wdt watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index a099b77fc0b9..38dd60f0cfcc 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c @@ -68,17 +68,10 @@ /* Default timeout in seconds = 1 minute */ static unsigned int margin = 60; -static resource_size_t phybase; -static resource_size_t physize; static int irq; static void __iomem *virtbase; static struct device *parent; -/* - * The watchdog block is of course always clocked, the - * clk_enable()/clk_disable() calls are mainly for performing reference - * counting higher up in the clock hierarchy. - */ static struct clk *clk; /* @@ -90,7 +83,6 @@ static void coh901327_enable(u16 timeout) unsigned long freq; unsigned long delay_ns; - clk_enable(clk); /* Restart timer if it is disabled */ val = readw(virtbase + U300_WDOG_D2R); if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED) @@ -118,7 +110,6 @@ static void coh901327_enable(u16 timeout) */ (void) readw(virtbase + U300_WDOG_CR); val = readw(virtbase + U300_WDOG_D2R); - clk_disable(clk); if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED) dev_err(parent, "%s(): watchdog not enabled! D2R value %04x\n", @@ -129,7 +120,6 @@ static void coh901327_disable(void) { u16 val; - clk_enable(clk); /* Disable the watchdog interrupt if it is active */ writew(0x0000U, virtbase + U300_WDOG_IMR); /* If the watchdog is currently enabled, attempt to disable it */ @@ -144,7 +134,6 @@ static void coh901327_disable(void) virtbase + U300_WDOG_D2R); } val = readw(virtbase + U300_WDOG_D2R); - clk_disable(clk); if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) dev_err(parent, "%s(): watchdog not disabled! D2R value %04x\n", @@ -165,11 +154,9 @@ static int coh901327_stop(struct watchdog_device *wdt_dev) static int coh901327_ping(struct watchdog_device *wdd) { - clk_enable(clk); /* Feed the watchdog */ writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); - clk_disable(clk); return 0; } @@ -177,13 +164,11 @@ static int coh901327_settimeout(struct watchdog_device *wdt_dev, unsigned int time) { wdt_dev->timeout = time; - clk_enable(clk); /* Set new timeout value */ writew(time * 100, virtbase + U300_WDOG_TR); /* Feed the dog */ writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); - clk_disable(clk); return 0; } @@ -191,13 +176,11 @@ static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev) { u16 val; - clk_enable(clk); /* Read repeatedly until the value is stable! */ val = readw(virtbase + U300_WDOG_CR); while (val & U300_WDOG_CR_VALID_IND) val = readw(virtbase + U300_WDOG_CR); val &= U300_WDOG_CR_COUNT_VALUE_MASK; - clk_disable(clk); if (val != 0) val /= 100; @@ -221,13 +204,11 @@ static irqreturn_t coh901327_interrupt(int irq, void *data) * to prevent a watchdog reset by feeding the watchdog at this * point. */ - clk_enable(clk); val = readw(virtbase + U300_WDOG_IER); if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND) writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, virtbase + U300_WDOG_IER); writew(0x0000U, virtbase + U300_WDOG_IMR); - clk_disable(clk); dev_crit(parent, "watchdog is barking!\n"); return IRQ_HANDLED; } @@ -263,81 +244,63 @@ static int __exit coh901327_remove(struct platform_device *pdev) watchdog_unregister_device(&coh901327_wdt); coh901327_disable(); free_irq(irq, pdev); - clk_unprepare(clk); + clk_disable_unprepare(clk); clk_put(clk); - iounmap(virtbase); - release_mem_region(phybase, physize); return 0; } static int __init coh901327_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int ret; u16 val; struct resource *res; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - - parent = &pdev->dev; - physize = resource_size(res); - phybase = res->start; + parent = dev; - if (request_mem_region(phybase, physize, DRV_NAME) == NULL) { - ret = -EBUSY; - goto out; - } - - virtbase = ioremap(phybase, physize); - if (!virtbase) { - ret = -ENOMEM; - goto out_no_remap; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + virtbase = devm_ioremap_resource(dev, res); + if (IS_ERR(virtbase)) + return PTR_ERR(virtbase); - clk = clk_get(&pdev->dev, NULL); + clk = clk_get(dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - dev_err(&pdev->dev, "could not get clock\n"); - goto out_no_clk; + dev_err(dev, "could not get clock\n"); + return ret; } ret = clk_prepare_enable(clk); if (ret) { - dev_err(&pdev->dev, "could not prepare and enable clock\n"); + dev_err(dev, "could not prepare and enable clock\n"); goto out_no_clk_enable; } val = readw(virtbase + U300_WDOG_SR); switch (val) { case U300_WDOG_SR_STATUS_TIMED_OUT: - dev_info(&pdev->dev, - "watchdog timed out since last chip reset!\n"); + dev_info(dev, "watchdog timed out since last chip reset!\n"); coh901327_wdt.bootstatus |= WDIOF_CARDRESET; /* Status will be cleared below */ break; case U300_WDOG_SR_STATUS_NORMAL: - dev_info(&pdev->dev, - "in normal status, no timeouts have occurred.\n"); + dev_info(dev, "in normal status, no timeouts have occurred.\n"); break; default: - dev_info(&pdev->dev, - "contains an illegal status code (%08x)\n", val); + dev_info(dev, "contains an illegal status code (%08x)\n", val); break; } val = readw(virtbase + U300_WDOG_D2R); switch (val) { case U300_WDOG_D2R_DISABLE_STATUS_DISABLED: - dev_info(&pdev->dev, "currently disabled.\n"); + dev_info(dev, "currently disabled.\n"); break; case U300_WDOG_D2R_DISABLE_STATUS_ENABLED: - dev_info(&pdev->dev, - "currently enabled! (disabling it now)\n"); + dev_info(dev, "currently enabled! (disabling it now)\n"); coh901327_disable(); break; default: - dev_err(&pdev->dev, - "contains an illegal enable/disable code (%08x)\n", + dev_err(dev, "contains an illegal enable/disable code (%08x)\n", val); break; } @@ -352,20 +315,16 @@ static int __init coh901327_probe(struct platform_device *pdev) goto out_no_irq; } - clk_disable(clk); - - ret = watchdog_init_timeout(&coh901327_wdt, margin, &pdev->dev); + ret = watchdog_init_timeout(&coh901327_wdt, margin, dev); if (ret < 0) coh901327_wdt.timeout = 60; - coh901327_wdt.parent = &pdev->dev; + coh901327_wdt.parent = dev; ret = watchdog_register_device(&coh901327_wdt); - if (ret == 0) - dev_info(&pdev->dev, - "initialized. timer margin=%d sec\n", margin); - else + if (ret) goto out_no_wdog; + dev_info(dev, "initialized. timer margin=%d sec\n", margin); return 0; out_no_wdog: @@ -374,11 +333,6 @@ out_no_irq: clk_disable_unprepare(clk); out_no_clk_enable: clk_put(clk); -out_no_clk: - iounmap(virtbase); -out_no_remap: - release_mem_region(phybase, SZ_4K); -out: return ret; } diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c index 2fc19a32a320..d6d5006efa71 100644 --- a/drivers/watchdog/da9052_wdt.c +++ b/drivers/watchdog/da9052_wdt.c @@ -128,19 +128,17 @@ static int da9052_wdt_ping(struct watchdog_device *wdt_dev) ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, DA9052_CONTROLD_WATCHDOG, 1 << 7); if (ret < 0) - goto err_strobe; + return ret; /* * FIXME: Reset the watchdog core, in general PMIC * is supposed to do this */ - ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, - DA9052_CONTROLD_WATCHDOG, 0 << 7); -err_strobe: - return ret; + return da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_WATCHDOG, 0 << 7); } -static struct watchdog_info da9052_wdt_info = { +static const struct watchdog_info da9052_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "DA9052 Watchdog", }; @@ -163,10 +161,8 @@ static int da9052_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); - if (!driver_data) { - ret = -ENOMEM; - goto err; - } + if (!driver_data) + return -ENOMEM; driver_data->da9052 = da9052; da9052_wdt = &driver_data->wdt; @@ -182,33 +178,21 @@ static int da9052_wdt_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Failed to disable watchdog bits, %d\n", ret); - goto err; + return ret; } - ret = watchdog_register_device(&driver_data->wdt); + ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt); if (ret != 0) { dev_err(da9052->dev, "watchdog_register_device() failed: %d\n", ret); - goto err; + return ret; } - platform_set_drvdata(pdev, driver_data); -err: return ret; } -static int da9052_wdt_remove(struct platform_device *pdev) -{ - struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); - - return 0; -} - static struct platform_driver da9052_wdt_driver = { .probe = da9052_wdt_probe, - .remove = da9052_wdt_remove, .driver = { .name = "da9052-watchdog", }, diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c index 8377c43f3f20..50bdd1022186 100644 --- a/drivers/watchdog/da9055_wdt.c +++ b/drivers/watchdog/da9055_wdt.c @@ -108,7 +108,7 @@ static int da9055_wdt_stop(struct watchdog_device *wdt_dev) return da9055_wdt_set_timeout(wdt_dev, 0); } -static struct watchdog_info da9055_wdt_info = { +static const struct watchdog_info da9055_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "DA9055 Watchdog", }; @@ -147,32 +147,19 @@ static int da9055_wdt_probe(struct platform_device *pdev) ret = da9055_wdt_stop(da9055_wdt); if (ret < 0) { dev_err(&pdev->dev, "Failed to stop watchdog, %d\n", ret); - goto err; + return ret; } - platform_set_drvdata(pdev, driver_data); - - ret = watchdog_register_device(&driver_data->wdt); + ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt); if (ret != 0) dev_err(da9055->dev, "watchdog_register_device() failed: %d\n", ret); -err: return ret; } -static int da9055_wdt_remove(struct platform_device *pdev) -{ - struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); - - return 0; -} - static struct platform_driver da9055_wdt_driver = { .probe = da9055_wdt_probe, - .remove = da9055_wdt_remove, .driver = { .name = "da9055-watchdog", }, diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index a02cee6820a1..9083d3d922b0 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -220,9 +220,8 @@ static int da9062_wdt_probe(struct platform_device *pdev) wdt->wdtdev.parent = &pdev->dev; watchdog_set_drvdata(&wdt->wdtdev, wdt); - dev_set_drvdata(&pdev->dev, wdt); - ret = watchdog_register_device(&wdt->wdtdev); + ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); if (ret < 0) { dev_err(wdt->hw->dev, "watchdog registration failed (%d)\n", ret); @@ -231,24 +230,11 @@ static int da9062_wdt_probe(struct platform_device *pdev) da9062_set_window_start(wdt); - ret = da9062_wdt_ping(&wdt->wdtdev); - if (ret < 0) - watchdog_unregister_device(&wdt->wdtdev); - - return ret; -} - -static int da9062_wdt_remove(struct platform_device *pdev) -{ - struct da9062_watchdog *wdt = dev_get_drvdata(&pdev->dev); - - watchdog_unregister_device(&wdt->wdtdev); - return 0; + return da9062_wdt_ping(&wdt->wdtdev); } static struct platform_driver da9062_wdt_driver = { .probe = da9062_wdt_probe, - .remove = da9062_wdt_remove, .driver = { .name = "da9062-watchdog", .of_match_table = da9062_compatible_id_table, diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 5d6b4e5f7989..4691c5509129 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -151,7 +151,6 @@ static const struct watchdog_ops da9063_watchdog_ops = { static int da9063_wdt_probe(struct platform_device *pdev) { - int ret; struct da9063 *da9063; struct da9063_watchdog *wdt; @@ -181,27 +180,12 @@ static int da9063_wdt_probe(struct platform_device *pdev) watchdog_set_restart_priority(&wdt->wdtdev, 128); watchdog_set_drvdata(&wdt->wdtdev, wdt); - dev_set_drvdata(&pdev->dev, wdt); - - ret = watchdog_register_device(&wdt->wdtdev); - if (ret) - return ret; - - return 0; -} - -static int da9063_wdt_remove(struct platform_device *pdev) -{ - struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); - - watchdog_unregister_device(&wdt->wdtdev); - return 0; + return devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); } static struct platform_driver da9063_wdt_driver = { .probe = da9063_wdt_probe, - .remove = da9063_wdt_remove, .driver = { .name = DA9063_DRVNAME_WATCHDOG, }, diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 861d3d3133f8..6f591084bb7a 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -205,7 +205,7 @@ static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to) return wdt_ping(dev); } -static struct watchdog_ops wdt_ops = { +static const struct watchdog_ops wdt_ops = { .owner = THIS_MODULE, .start = wdt_start, .stop = wdt_stop, diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c index 77df772406b0..5e4ef93caa02 100644 --- a/drivers/watchdog/digicolor_wdt.c +++ b/drivers/watchdog/digicolor_wdt.c @@ -96,7 +96,7 @@ static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) return count / clk_get_rate(wdt->clk); } -static struct watchdog_ops dc_wdt_ops = { +static const struct watchdog_ops dc_wdt_ops = { .owner = THIS_MODULE, .start = dc_wdt_start, .stop = dc_wdt_stop, @@ -105,7 +105,7 @@ static struct watchdog_ops dc_wdt_ops = { .restart = dc_wdt_restart, }; -static struct watchdog_info dc_wdt_info = { +static const struct watchdog_info dc_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "Conexant Digicolor Watchdog", @@ -119,62 +119,40 @@ static struct watchdog_device dc_wdt_wdd = { static int dc_wdt_probe(struct platform_device *pdev) { + struct resource *res; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct dc_wdt *wdt; int ret; wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; - platform_set_drvdata(pdev, wdt); - wdt->base = of_iomap(np, 0); - if (!wdt->base) { - dev_err(dev, "Failed to remap watchdog regs"); - return -ENODEV; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); - wdt->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(wdt->clk)) { - ret = PTR_ERR(wdt->clk); - goto err_iounmap; - } + wdt->clk = devm_clk_get(dev, NULL); + if (IS_ERR(wdt->clk)) + return PTR_ERR(wdt->clk); dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout; - dc_wdt_wdd.parent = &pdev->dev; + dc_wdt_wdd.parent = dev; spin_lock_init(&wdt->lock); watchdog_set_drvdata(&dc_wdt_wdd, wdt); watchdog_set_restart_priority(&dc_wdt_wdd, 128); watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); - ret = watchdog_register_device(&dc_wdt_wdd); + watchdog_stop_on_reboot(&dc_wdt_wdd); + ret = devm_watchdog_register_device(dev, &dc_wdt_wdd); if (ret) { dev_err(dev, "Failed to register watchdog device"); - goto err_iounmap; + return ret; } return 0; - -err_iounmap: - iounmap(wdt->base); - return ret; -} - -static int dc_wdt_remove(struct platform_device *pdev) -{ - struct dc_wdt *wdt = platform_get_drvdata(pdev); - - watchdog_unregister_device(&dc_wdt_wdd); - iounmap(wdt->base); - - return 0; -} - -static void dc_wdt_shutdown(struct platform_device *pdev) -{ - dc_wdt_stop(&dc_wdt_wdd); } static const struct of_device_id dc_wdt_of_match[] = { @@ -185,8 +163,6 @@ MODULE_DEVICE_TABLE(of, dc_wdt_of_match); static struct platform_driver dc_wdt_driver = { .probe = dc_wdt_probe, - .remove = dc_wdt_remove, - .shutdown = dc_wdt_shutdown, .driver = { .name = "digicolor-wdt", .of_match_table = dc_wdt_of_match, diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 3c6a3de13a1b..914da3a4d334 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -26,11 +26,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/notifier.h> #include <linux/of.h> #include <linux/pm.h> #include <linux/platform_device.h> -#include <linux/reboot.h> #include <linux/watchdog.h> #define WDOG_CONTROL_REG_OFFSET 0x00 @@ -55,7 +53,6 @@ struct dw_wdt { void __iomem *regs; struct clk *clk; unsigned long rate; - struct notifier_block restart_handler; struct watchdog_device wdd; }; @@ -136,14 +133,12 @@ static int dw_wdt_start(struct watchdog_device *wdd) return 0; } -static int dw_wdt_restart_handle(struct notifier_block *this, - unsigned long mode, void *cmd) +static int dw_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) { - struct dw_wdt *dw_wdt; + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); u32 val; - dw_wdt = container_of(this, struct dw_wdt, restart_handler); - writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); if (val & WDOG_CONTROL_REG_WDT_EN_MASK) @@ -156,7 +151,7 @@ static int dw_wdt_restart_handle(struct notifier_block *this, /* wait for reset to assert... */ mdelay(500); - return NOTIFY_DONE; + return 0; } static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) @@ -179,6 +174,7 @@ static const struct watchdog_ops dw_wdt_ops = { .ping = dw_wdt_ping, .set_timeout = dw_wdt_set_timeout, .get_timeleft = dw_wdt_get_timeleft, + .restart = dw_wdt_restart, }; #ifdef CONFIG_PM_SLEEP @@ -265,16 +261,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw_wdt); + watchdog_set_restart_priority(wdd, 128); + ret = watchdog_register_device(wdd); if (ret) goto out_disable_clk; - dw_wdt->restart_handler.notifier_call = dw_wdt_restart_handle; - dw_wdt->restart_handler.priority = 128; - ret = register_restart_handler(&dw_wdt->restart_handler); - if (ret) - pr_warn("cannot register restart handler\n"); - return 0; out_disable_clk: @@ -286,7 +278,6 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) { struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); - unregister_restart_handler(&dw_wdt->restart_handler); watchdog_unregister_device(&dw_wdt->wdd); clk_disable_unprepare(dw_wdt->clk); diff --git a/drivers/watchdog/ebc-c384_wdt.c b/drivers/watchdog/ebc-c384_wdt.c index 4b849b8e37c2..2170b275ea01 100644 --- a/drivers/watchdog/ebc-c384_wdt.c +++ b/drivers/watchdog/ebc-c384_wdt.c @@ -121,18 +121,7 @@ static int ebc_c384_wdt_probe(struct device *dev, unsigned int id) dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n", timeout, WATCHDOG_TIMEOUT); - dev_set_drvdata(dev, wdd); - - return watchdog_register_device(wdd); -} - -static int ebc_c384_wdt_remove(struct device *dev, unsigned int id) -{ - struct watchdog_device *wdd = dev_get_drvdata(dev); - - watchdog_unregister_device(wdd); - - return 0; + return devm_watchdog_register_device(dev, wdd); } static struct isa_driver ebc_c384_wdt_driver = { @@ -140,7 +129,6 @@ static struct isa_driver ebc_c384_wdt_driver = { .driver = { .name = MODULE_NAME }, - .remove = ebc_c384_wdt_remove }; static int __init ebc_c384_wdt_init(void) diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index 0a4d7cc05d54..f9b14e6efd9a 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -19,21 +19,13 @@ * for us to rely on the user space daemon alone. So we ping the * wdt each ~200msec and eventually stop doing it if the user space * daemon dies. - * - * TODO: - * - * - Test last reset from watchdog status - * - Add a few missing ioctls */ #include <linux/platform_device.h> #include <linux/module.h> #include <linux/watchdog.h> -#include <linux/timer.h> #include <linux/io.h> -#define WDT_VERSION "0.4" - /* default timeout (secs) */ #define WDT_TIMEOUT 30 @@ -41,117 +33,101 @@ static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); -static unsigned int timeout = WDT_TIMEOUT; +static unsigned int timeout; module_param(timeout, uint, 0); -MODULE_PARM_DESC(timeout, - "Watchdog timeout in seconds. (1<=timeout<=3600, default=" - __MODULE_STRING(WDT_TIMEOUT) ")"); - -static void __iomem *mmio_base; -static struct timer_list timer; -static unsigned long next_heartbeat; +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds."); #define EP93XX_WATCHDOG 0x00 #define EP93XX_WDSTATUS 0x04 -/* reset the wdt every ~200ms - the heartbeat of the device is 0.250 seconds*/ -#define WDT_INTERVAL (HZ/5) - -static void ep93xx_wdt_timer_ping(unsigned long data) -{ - if (time_before(jiffies, next_heartbeat)) - writel(0x5555, mmio_base + EP93XX_WATCHDOG); - - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); -} +struct ep93xx_wdt_priv { + void __iomem *mmio; + struct watchdog_device wdd; +}; static int ep93xx_wdt_start(struct watchdog_device *wdd) { - next_heartbeat = jiffies + (timeout * HZ); + struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); - writel(0xaaaa, mmio_base + EP93XX_WATCHDOG); - mod_timer(&timer, jiffies + WDT_INTERVAL); + writel(0xaaaa, priv->mmio + EP93XX_WATCHDOG); return 0; } static int ep93xx_wdt_stop(struct watchdog_device *wdd) { - del_timer_sync(&timer); - writel(0xaa55, mmio_base + EP93XX_WATCHDOG); + struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); + + writel(0xaa55, priv->mmio + EP93XX_WATCHDOG); return 0; } -static int ep93xx_wdt_keepalive(struct watchdog_device *wdd) +static int ep93xx_wdt_ping(struct watchdog_device *wdd) { - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); + struct ep93xx_wdt_priv *priv = watchdog_get_drvdata(wdd); + + writel(0x5555, priv->mmio + EP93XX_WATCHDOG); return 0; } static const struct watchdog_info ep93xx_wdt_ident = { .options = WDIOF_CARDRESET | + WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "EP93xx Watchdog", }; -static struct watchdog_ops ep93xx_wdt_ops = { +static const struct watchdog_ops ep93xx_wdt_ops = { .owner = THIS_MODULE, .start = ep93xx_wdt_start, .stop = ep93xx_wdt_stop, - .ping = ep93xx_wdt_keepalive, -}; - -static struct watchdog_device ep93xx_wdt_wdd = { - .info = &ep93xx_wdt_ident, - .ops = &ep93xx_wdt_ops, + .ping = ep93xx_wdt_ping, }; static int ep93xx_wdt_probe(struct platform_device *pdev) { + struct ep93xx_wdt_priv *priv; + struct watchdog_device *wdd; struct resource *res; unsigned long val; - int err; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mmio_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mmio_base)) - return PTR_ERR(mmio_base); + priv->mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->mmio)) + return PTR_ERR(priv->mmio); - if (timeout < 1 || timeout > 3600) { - timeout = WDT_TIMEOUT; - dev_warn(&pdev->dev, - "timeout value must be 1<=x<=3600, using %d\n", - timeout); - } + val = readl(priv->mmio + EP93XX_WATCHDOG); - val = readl(mmio_base + EP93XX_WATCHDOG); - ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0; - ep93xx_wdt_wdd.timeout = timeout; - ep93xx_wdt_wdd.parent = &pdev->dev; + wdd = &priv->wdd; + wdd->bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0; + wdd->info = &ep93xx_wdt_ident; + wdd->ops = &ep93xx_wdt_ops; + wdd->min_timeout = 1; + wdd->max_hw_heartbeat_ms = 200; + wdd->parent = &pdev->dev; - watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout); + watchdog_set_nowayout(wdd, nowayout); - setup_timer(&timer, ep93xx_wdt_timer_ping, 1); + wdd->timeout = WDT_TIMEOUT; + watchdog_init_timeout(wdd, timeout, &pdev->dev); - err = watchdog_register_device(&ep93xx_wdt_wdd); - if (err) - return err; + watchdog_set_drvdata(wdd, priv); - dev_info(&pdev->dev, - "EP93XX watchdog, driver version " WDT_VERSION "%s\n", - (val & 0x08) ? " (nCS1 disable detected)" : ""); + ret = devm_watchdog_register_device(&pdev->dev, wdd); + if (ret) + return ret; - return 0; -} + dev_info(&pdev->dev, "EP93XX watchdog driver %s\n", + (val & 0x08) ? " (nCS1 disable detected)" : ""); -static int ep93xx_wdt_remove(struct platform_device *pdev) -{ - watchdog_unregister_device(&ep93xx_wdt_wdd); return 0; } @@ -160,7 +136,6 @@ static struct platform_driver ep93xx_wdt_driver = { .name = "ep93xx-wdt", }, .probe = ep93xx_wdt_probe, - .remove = ep93xx_wdt_remove, }; module_platform_driver(ep93xx_wdt_driver); @@ -170,4 +145,3 @@ MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); MODULE_DESCRIPTION("EP93xx Watchdog"); MODULE_LICENSE("GPL"); -MODULE_VERSION(WDT_VERSION); diff --git a/drivers/watchdog/gemini_wdt.c b/drivers/watchdog/gemini_wdt.c new file mode 100644 index 000000000000..8155aa619e4c --- /dev/null +++ b/drivers/watchdog/gemini_wdt.c @@ -0,0 +1,229 @@ +/* + * Watchdog driver for Cortina Systems Gemini SoC + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * + * Inspired by the out-of-tree drivers from OpenWRT: + * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> + * + * 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/bitops.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/watchdog.h> + +#define GEMINI_WDCOUNTER 0x0 +#define GEMINI_WDLOAD 0x4 +#define GEMINI_WDRESTART 0x8 +#define GEMINI_WDCR 0xC + +#define WDRESTART_MAGIC 0x5AB9 + +#define WDCR_CLOCK_5MHZ BIT(4) +#define WDCR_SYS_RST BIT(1) +#define WDCR_ENABLE BIT(0) + +#define WDT_CLOCK 5000000 /* 5 MHz */ + +struct gemini_wdt { + struct watchdog_device wdd; + struct device *dev; + void __iomem *base; +}; + +static inline +struct gemini_wdt *to_gemini_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct gemini_wdt, wdd); +} + +static int gemini_wdt_start(struct watchdog_device *wdd) +{ + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); + + writel(wdd->timeout * WDT_CLOCK, gwdt->base + GEMINI_WDLOAD); + writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); + /* set clock before enabling */ + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, + gwdt->base + GEMINI_WDCR); + writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, + gwdt->base + GEMINI_WDCR); + + return 0; +} + +static int gemini_wdt_stop(struct watchdog_device *wdd) +{ + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); + + writel(0, gwdt->base + GEMINI_WDCR); + + return 0; +} + +static int gemini_wdt_ping(struct watchdog_device *wdd) +{ + struct gemini_wdt *gwdt = to_gemini_wdt(wdd); + + writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART); + + return 0; +} + +static int gemini_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + if (watchdog_active(wdd)) + gemini_wdt_start(wdd); + + return 0; +} + +static irqreturn_t gemini_wdt_interrupt(int irq, void *data) +{ + struct gemini_wdt *gwdt = data; + + watchdog_notify_pretimeout(&gwdt->wdd); + + return IRQ_HANDLED; +} + +static const struct watchdog_ops gemini_wdt_ops = { + .start = gemini_wdt_start, + .stop = gemini_wdt_stop, + .ping = gemini_wdt_ping, + .set_timeout = gemini_wdt_set_timeout, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info gemini_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, +}; + + +static int gemini_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct gemini_wdt *gwdt; + unsigned int reg; + int irq; + int ret; + + gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); + if (!gwdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gwdt->base = devm_ioremap_resource(dev, res); + if (IS_ERR(gwdt->base)) + return PTR_ERR(gwdt->base); + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + gwdt->dev = dev; + gwdt->wdd.info = &gemini_wdt_info; + gwdt->wdd.ops = &gemini_wdt_ops; + gwdt->wdd.min_timeout = 1; + gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK; + gwdt->wdd.parent = dev; + + /* + * If 'timeout-sec' unspecified in devicetree, assume a 13 second + * default. + */ + gwdt->wdd.timeout = 13U; + watchdog_init_timeout(&gwdt->wdd, 0, dev); + + reg = readw(gwdt->base + GEMINI_WDCR); + if (reg & WDCR_ENABLE) { + /* Watchdog was enabled by the bootloader, disable it. */ + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + GEMINI_WDCR); + } + + ret = devm_request_irq(dev, irq, gemini_wdt_interrupt, 0, + "watchdog bark", gwdt); + if (ret) + return ret; + + ret = devm_watchdog_register_device(dev, &gwdt->wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register watchdog\n"); + return ret; + } + + /* Set up platform driver data */ + platform_set_drvdata(pdev, gwdt); + dev_info(dev, "Gemini watchdog driver enabled\n"); + + return 0; +} + +static int __maybe_unused gemini_wdt_suspend(struct device *dev) +{ + struct gemini_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + reg = readw(gwdt->base + GEMINI_WDCR); + reg &= ~WDCR_ENABLE; + writel(reg, gwdt->base + GEMINI_WDCR); + + return 0; +} + +static int __maybe_unused gemini_wdt_resume(struct device *dev) +{ + struct gemini_wdt *gwdt = dev_get_drvdata(dev); + unsigned int reg; + + if (watchdog_active(&gwdt->wdd)) { + reg = readw(gwdt->base + GEMINI_WDCR); + reg |= WDCR_ENABLE; + writel(reg, gwdt->base + GEMINI_WDCR); + } + + return 0; +} + +static const struct dev_pm_ops gemini_wdt_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(gemini_wdt_suspend, + gemini_wdt_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id gemini_wdt_match[] = { + { .compatible = "cortina,gemini-watchdog" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gemini_wdt_match); +#endif + +static struct platform_driver gemini_wdt_driver = { + .probe = gemini_wdt_probe, + .driver = { + .name = "gemini-wdt", + .of_match_table = of_match_ptr(gemini_wdt_match), + .pm = &gemini_wdt_dev_pm_ops, + }, +}; +module_platform_driver(gemini_wdt_driver); +MODULE_AUTHOR("Linus Walleij"); +MODULE_DESCRIPTION("Watchdog driver for Gemini"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 06fcb6c8c917..3d0abc0d59b4 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -72,22 +72,24 @@ /* Address definitions for the TCO */ /* TCO base address */ -#define TCOBASE (iTCO_wdt_private.tco_res->start) +#define TCOBASE(p) ((p)->tco_res->start) /* SMI Control and Enable Register */ -#define SMI_EN (iTCO_wdt_private.smi_res->start) - -#define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */ -#define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */ -#define TCO_DAT_IN (TCOBASE + 0x02) /* TCO Data In Register */ -#define TCO_DAT_OUT (TCOBASE + 0x03) /* TCO Data Out Register */ -#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */ -#define TCO2_STS (TCOBASE + 0x06) /* TCO2 Status Register */ -#define TCO1_CNT (TCOBASE + 0x08) /* TCO1 Control Register */ -#define TCO2_CNT (TCOBASE + 0x0a) /* TCO2 Control Register */ -#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */ +#define SMI_EN(p) ((p)->smi_res->start) + +#define TCO_RLD(p) (TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */ +#define TCOv1_TMR(p) (TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/ +#define TCO_DAT_IN(p) (TCOBASE(p) + 0x02) /* TCO Data In Register */ +#define TCO_DAT_OUT(p) (TCOBASE(p) + 0x03) /* TCO Data Out Register */ +#define TCO1_STS(p) (TCOBASE(p) + 0x04) /* TCO1 Status Register */ +#define TCO2_STS(p) (TCOBASE(p) + 0x06) /* TCO2 Status Register */ +#define TCO1_CNT(p) (TCOBASE(p) + 0x08) /* TCO1 Control Register */ +#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */ +#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/ /* internal variables */ -static struct { /* this is private data for the iTCO_wdt device */ +struct iTCO_wdt_private { + struct watchdog_device wddev; + /* TCO version/generation */ unsigned int iTCO_version; struct resource *tco_res; @@ -100,12 +102,11 @@ static struct { /* this is private data for the iTCO_wdt device */ unsigned long __iomem *gcs_pmc; /* the lock for io operations */ spinlock_t io_lock; - struct platform_device *dev; /* the PCI-device */ - struct pci_dev *pdev; + struct pci_dev *pci_dev; /* whether or not the watchdog has been suspended */ bool suspended; -} iTCO_wdt_private; +}; /* module parameters */ #define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */ @@ -135,21 +136,23 @@ MODULE_PARM_DESC(turn_SMI_watchdog_clear_off, * every 0.6 seconds. v3's internal timer is stored as seconds (some * datasheets incorrectly state 0.6 seconds). */ -static inline unsigned int seconds_to_ticks(int secs) +static inline unsigned int seconds_to_ticks(struct iTCO_wdt_private *p, + int secs) { - return iTCO_wdt_private.iTCO_version == 3 ? secs : (secs * 10) / 6; + return p->iTCO_version == 3 ? secs : (secs * 10) / 6; } -static inline unsigned int ticks_to_seconds(int ticks) +static inline unsigned int ticks_to_seconds(struct iTCO_wdt_private *p, + int ticks) { - return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10; + return p->iTCO_version == 3 ? ticks : (ticks * 6) / 10; } -static inline u32 no_reboot_bit(void) +static inline u32 no_reboot_bit(struct iTCO_wdt_private *p) { u32 enable_bit; - switch (iTCO_wdt_private.iTCO_version) { + switch (p->iTCO_version) { case 5: case 3: enable_bit = 0x00000010; @@ -167,40 +170,40 @@ static inline u32 no_reboot_bit(void) return enable_bit; } -static void iTCO_wdt_set_NO_REBOOT_bit(void) +static void iTCO_wdt_set_NO_REBOOT_bit(struct iTCO_wdt_private *p) { u32 val32; /* Set the NO_REBOOT bit: this disables reboots */ - if (iTCO_wdt_private.iTCO_version >= 2) { - val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 |= no_reboot_bit(); - writel(val32, iTCO_wdt_private.gcs_pmc); - } else if (iTCO_wdt_private.iTCO_version == 1) { - pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); - val32 |= no_reboot_bit(); - pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32); + if (p->iTCO_version >= 2) { + val32 = readl(p->gcs_pmc); + val32 |= no_reboot_bit(p); + writel(val32, p->gcs_pmc); + } else if (p->iTCO_version == 1) { + pci_read_config_dword(p->pci_dev, 0xd4, &val32); + val32 |= no_reboot_bit(p); + pci_write_config_dword(p->pci_dev, 0xd4, val32); } } -static int iTCO_wdt_unset_NO_REBOOT_bit(void) +static int iTCO_wdt_unset_NO_REBOOT_bit(struct iTCO_wdt_private *p) { - u32 enable_bit = no_reboot_bit(); + u32 enable_bit = no_reboot_bit(p); u32 val32 = 0; /* Unset the NO_REBOOT bit: this enables reboots */ - if (iTCO_wdt_private.iTCO_version >= 2) { - val32 = readl(iTCO_wdt_private.gcs_pmc); + if (p->iTCO_version >= 2) { + val32 = readl(p->gcs_pmc); val32 &= ~enable_bit; - writel(val32, iTCO_wdt_private.gcs_pmc); + writel(val32, p->gcs_pmc); - val32 = readl(iTCO_wdt_private.gcs_pmc); - } else if (iTCO_wdt_private.iTCO_version == 1) { - pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); + val32 = readl(p->gcs_pmc); + } else if (p->iTCO_version == 1) { + pci_read_config_dword(p->pci_dev, 0xd4, &val32); val32 &= ~enable_bit; - pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32); + pci_write_config_dword(p->pci_dev, 0xd4, val32); - pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32); + pci_read_config_dword(p->pci_dev, 0xd4, &val32); } if (val32 & enable_bit) @@ -211,32 +214,33 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) static int iTCO_wdt_start(struct watchdog_device *wd_dev) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; - spin_lock(&iTCO_wdt_private.io_lock); + spin_lock(&p->io_lock); - iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout); + iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); /* disable chipset's NO_REBOOT bit */ - if (iTCO_wdt_unset_NO_REBOOT_bit()) { - spin_unlock(&iTCO_wdt_private.io_lock); + if (iTCO_wdt_unset_NO_REBOOT_bit(p)) { + spin_unlock(&p->io_lock); pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n"); return -EIO; } /* Force the timer to its reload value by writing to the TCO_RLD register */ - if (iTCO_wdt_private.iTCO_version >= 2) - outw(0x01, TCO_RLD); - else if (iTCO_wdt_private.iTCO_version == 1) - outb(0x01, TCO_RLD); + if (p->iTCO_version >= 2) + outw(0x01, TCO_RLD(p)); + else if (p->iTCO_version == 1) + outb(0x01, TCO_RLD(p)); /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */ - val = inw(TCO1_CNT); + val = inw(TCO1_CNT(p)); val &= 0xf7ff; - outw(val, TCO1_CNT); - val = inw(TCO1_CNT); - spin_unlock(&iTCO_wdt_private.io_lock); + outw(val, TCO1_CNT(p)); + val = inw(TCO1_CNT(p)); + spin_unlock(&p->io_lock); if (val & 0x0800) return -1; @@ -245,22 +249,23 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) static int iTCO_wdt_stop(struct watchdog_device *wd_dev) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; - spin_lock(&iTCO_wdt_private.io_lock); + spin_lock(&p->io_lock); - iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res); + iTCO_vendor_pre_stop(p->smi_res); /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ - val = inw(TCO1_CNT); + val = inw(TCO1_CNT(p)); val |= 0x0800; - outw(val, TCO1_CNT); - val = inw(TCO1_CNT); + outw(val, TCO1_CNT(p)); + val = inw(TCO1_CNT(p)); /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ - iTCO_wdt_set_NO_REBOOT_bit(); + iTCO_wdt_set_NO_REBOOT_bit(p); - spin_unlock(&iTCO_wdt_private.io_lock); + spin_unlock(&p->io_lock); if ((val & 0x0800) == 0) return -1; @@ -269,67 +274,70 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev) static int iTCO_wdt_ping(struct watchdog_device *wd_dev) { - spin_lock(&iTCO_wdt_private.io_lock); + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); - iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout); + spin_lock(&p->io_lock); + + iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); /* Reload the timer by writing to the TCO Timer Counter register */ - if (iTCO_wdt_private.iTCO_version >= 2) { - outw(0x01, TCO_RLD); - } else if (iTCO_wdt_private.iTCO_version == 1) { + if (p->iTCO_version >= 2) { + outw(0x01, TCO_RLD(p)); + } else if (p->iTCO_version == 1) { /* Reset the timeout status bit so that the timer * needs to count down twice again before rebooting */ - outw(0x0008, TCO1_STS); /* write 1 to clear bit */ + outw(0x0008, TCO1_STS(p)); /* write 1 to clear bit */ - outb(0x01, TCO_RLD); + outb(0x01, TCO_RLD(p)); } - spin_unlock(&iTCO_wdt_private.io_lock); + spin_unlock(&p->io_lock); return 0; } static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val16; unsigned char val8; unsigned int tmrval; - tmrval = seconds_to_ticks(t); + tmrval = seconds_to_ticks(p, t); /* For TCO v1 the timer counts down twice before rebooting */ - if (iTCO_wdt_private.iTCO_version == 1) + if (p->iTCO_version == 1) tmrval /= 2; /* from the specs: */ /* "Values of 0h-3h are ignored and should not be attempted" */ if (tmrval < 0x04) return -EINVAL; - if (((iTCO_wdt_private.iTCO_version >= 2) && (tmrval > 0x3ff)) || - ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f))) + if ((p->iTCO_version >= 2 && tmrval > 0x3ff) || + (p->iTCO_version == 1 && tmrval > 0x03f)) return -EINVAL; iTCO_vendor_pre_set_heartbeat(tmrval); /* Write new heartbeat to watchdog */ - if (iTCO_wdt_private.iTCO_version >= 2) { - spin_lock(&iTCO_wdt_private.io_lock); - val16 = inw(TCOv2_TMR); + if (p->iTCO_version >= 2) { + spin_lock(&p->io_lock); + val16 = inw(TCOv2_TMR(p)); val16 &= 0xfc00; val16 |= tmrval; - outw(val16, TCOv2_TMR); - val16 = inw(TCOv2_TMR); - spin_unlock(&iTCO_wdt_private.io_lock); + outw(val16, TCOv2_TMR(p)); + val16 = inw(TCOv2_TMR(p)); + spin_unlock(&p->io_lock); if ((val16 & 0x3ff) != tmrval) return -EINVAL; - } else if (iTCO_wdt_private.iTCO_version == 1) { - spin_lock(&iTCO_wdt_private.io_lock); - val8 = inb(TCOv1_TMR); + } else if (p->iTCO_version == 1) { + spin_lock(&p->io_lock); + val8 = inb(TCOv1_TMR(p)); val8 &= 0xc0; val8 |= (tmrval & 0xff); - outb(val8, TCOv1_TMR); - val8 = inb(TCOv1_TMR); - spin_unlock(&iTCO_wdt_private.io_lock); + outb(val8, TCOv1_TMR(p)); + val8 = inb(TCOv1_TMR(p)); + spin_unlock(&p->io_lock); if ((val8 & 0x3f) != tmrval) return -EINVAL; @@ -341,27 +349,28 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) { + struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val16; unsigned char val8; unsigned int time_left = 0; /* read the TCO Timer */ - if (iTCO_wdt_private.iTCO_version >= 2) { - spin_lock(&iTCO_wdt_private.io_lock); - val16 = inw(TCO_RLD); + if (p->iTCO_version >= 2) { + spin_lock(&p->io_lock); + val16 = inw(TCO_RLD(p)); val16 &= 0x3ff; - spin_unlock(&iTCO_wdt_private.io_lock); + spin_unlock(&p->io_lock); - time_left = ticks_to_seconds(val16); - } else if (iTCO_wdt_private.iTCO_version == 1) { - spin_lock(&iTCO_wdt_private.io_lock); - val8 = inb(TCO_RLD); + time_left = ticks_to_seconds(p, val16); + } else if (p->iTCO_version == 1) { + spin_lock(&p->io_lock); + val8 = inb(TCO_RLD(p)); val8 &= 0x3f; - if (!(inw(TCO1_STS) & 0x0008)) - val8 += (inb(TCOv1_TMR) & 0x3f); - spin_unlock(&iTCO_wdt_private.io_lock); + if (!(inw(TCO1_STS(p)) & 0x0008)) + val8 += (inb(TCOv1_TMR(p)) & 0x3f); + spin_unlock(&p->io_lock); - time_left = ticks_to_seconds(val8); + time_left = ticks_to_seconds(p, val8); } return time_left; } @@ -387,209 +396,152 @@ static const struct watchdog_ops iTCO_wdt_ops = { .get_timeleft = iTCO_wdt_get_timeleft, }; -static struct watchdog_device iTCO_wdt_watchdog_dev = { - .info = &ident, - .ops = &iTCO_wdt_ops, -}; - /* * Init & exit routines */ -static void iTCO_wdt_cleanup(void) -{ - /* Stop the timer before we leave */ - if (!nowayout) - iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); - - /* Deregister */ - watchdog_unregister_device(&iTCO_wdt_watchdog_dev); - - /* release resources */ - release_region(iTCO_wdt_private.tco_res->start, - resource_size(iTCO_wdt_private.tco_res)); - release_region(iTCO_wdt_private.smi_res->start, - resource_size(iTCO_wdt_private.smi_res)); - if (iTCO_wdt_private.iTCO_version >= 2) { - iounmap(iTCO_wdt_private.gcs_pmc); - release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res)); - } - - iTCO_wdt_private.tco_res = NULL; - iTCO_wdt_private.smi_res = NULL; - iTCO_wdt_private.gcs_pmc_res = NULL; - iTCO_wdt_private.gcs_pmc = NULL; -} - -static int iTCO_wdt_probe(struct platform_device *dev) +static int iTCO_wdt_probe(struct platform_device *pdev) { - int ret = -ENODEV; + struct device *dev = &pdev->dev; + struct itco_wdt_platform_data *pdata = dev_get_platdata(dev); + struct iTCO_wdt_private *p; unsigned long val32; - struct itco_wdt_platform_data *pdata = dev_get_platdata(&dev->dev); + int ret; if (!pdata) - goto out; + return -ENODEV; + + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; - spin_lock_init(&iTCO_wdt_private.io_lock); + spin_lock_init(&p->io_lock); - iTCO_wdt_private.tco_res = - platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO); - if (!iTCO_wdt_private.tco_res) - goto out; + p->tco_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_TCO); + if (!p->tco_res) + return -ENODEV; - iTCO_wdt_private.smi_res = - platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI); - if (!iTCO_wdt_private.smi_res) - goto out; + p->smi_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_SMI); + if (!p->smi_res) + return -ENODEV; - iTCO_wdt_private.iTCO_version = pdata->version; - iTCO_wdt_private.dev = dev; - iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent); + p->iTCO_version = pdata->version; + p->pci_dev = to_pci_dev(dev->parent); /* * Get the Memory-Mapped GCS or PMC register, we need it for the * NO_REBOOT flag (TCO v2 and v3). */ - if (iTCO_wdt_private.iTCO_version >= 2) { - iTCO_wdt_private.gcs_pmc_res = platform_get_resource(dev, - IORESOURCE_MEM, - ICH_RES_MEM_GCS_PMC); - - if (!iTCO_wdt_private.gcs_pmc_res) - goto out; - - if (!request_mem_region(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res), dev->name)) { - ret = -EBUSY; - goto out; - } - iTCO_wdt_private.gcs_pmc = ioremap(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res)); - if (!iTCO_wdt_private.gcs_pmc) { - ret = -EIO; - goto unreg_gcs_pmc; - } + if (p->iTCO_version >= 2) { + p->gcs_pmc_res = platform_get_resource(pdev, + IORESOURCE_MEM, + ICH_RES_MEM_GCS_PMC); + p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res); + if (IS_ERR(p->gcs_pmc)) + return PTR_ERR(p->gcs_pmc); } /* Check chipset's NO_REBOOT bit */ - if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { + if (iTCO_wdt_unset_NO_REBOOT_bit(p) && + iTCO_vendor_check_noreboot_on()) { pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); - ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ - goto unmap_gcs_pmc; + return -ENODEV; /* Cannot reset NO_REBOOT bit */ } /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ - iTCO_wdt_set_NO_REBOOT_bit(); + iTCO_wdt_set_NO_REBOOT_bit(p); /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ - if (!request_region(iTCO_wdt_private.smi_res->start, - resource_size(iTCO_wdt_private.smi_res), dev->name)) { + if (!devm_request_region(dev, p->smi_res->start, + resource_size(p->smi_res), + pdev->name)) { pr_err("I/O address 0x%04llx already in use, device disabled\n", - (u64)SMI_EN); - ret = -EBUSY; - goto unmap_gcs_pmc; + (u64)SMI_EN(p)); + return -EBUSY; } - if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) { + if (turn_SMI_watchdog_clear_off >= p->iTCO_version) { /* * Bit 13: TCO_EN -> 0 * Disables TCO logic generating an SMI# */ - val32 = inl(SMI_EN); + val32 = inl(SMI_EN(p)); val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); + outl(val32, SMI_EN(p)); } - if (!request_region(iTCO_wdt_private.tco_res->start, - resource_size(iTCO_wdt_private.tco_res), dev->name)) { + if (!devm_request_region(dev, p->tco_res->start, + resource_size(p->tco_res), + pdev->name)) { pr_err("I/O address 0x%04llx already in use, device disabled\n", - (u64)TCOBASE); - ret = -EBUSY; - goto unreg_smi; + (u64)TCOBASE(p)); + return -EBUSY; } pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", - pdata->name, pdata->version, (u64)TCOBASE); + pdata->name, pdata->version, (u64)TCOBASE(p)); /* Clear out the (probably old) status */ - switch (iTCO_wdt_private.iTCO_version) { + switch (p->iTCO_version) { case 5: case 4: - outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ - outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ + outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */ break; case 3: - outl(0x20008, TCO1_STS); + outl(0x20008, TCO1_STS(p)); break; case 2: case 1: default: - outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ - outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ - outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ + outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ + outw(0x0002, TCO2_STS(p)); /* Clear SECOND_TO_STS bit */ + outw(0x0004, TCO2_STS(p)); /* Clear BOOT_STS bit */ break; } - iTCO_wdt_watchdog_dev.bootstatus = 0; - iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT; - watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout); - iTCO_wdt_watchdog_dev.parent = &dev->dev; + p->wddev.info = &ident, + p->wddev.ops = &iTCO_wdt_ops, + p->wddev.bootstatus = 0; + p->wddev.timeout = WATCHDOG_TIMEOUT; + watchdog_set_nowayout(&p->wddev, nowayout); + p->wddev.parent = dev; + + watchdog_set_drvdata(&p->wddev, p); + platform_set_drvdata(pdev, p); /* Make sure the watchdog is not running */ - iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); + iTCO_wdt_stop(&p->wddev); /* Check that the heartbeat value is within it's range; if not reset to the default */ - if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) { - iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT); + if (iTCO_wdt_set_timeout(&p->wddev, heartbeat)) { + iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT); pr_info("timeout value out of range, using %d\n", WATCHDOG_TIMEOUT); } - ret = watchdog_register_device(&iTCO_wdt_watchdog_dev); + watchdog_stop_on_reboot(&p->wddev); + ret = devm_watchdog_register_device(dev, &p->wddev); if (ret != 0) { pr_err("cannot register watchdog device (err=%d)\n", ret); - goto unreg_tco; + return ret; } pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); return 0; - -unreg_tco: - release_region(iTCO_wdt_private.tco_res->start, - resource_size(iTCO_wdt_private.tco_res)); -unreg_smi: - release_region(iTCO_wdt_private.smi_res->start, - resource_size(iTCO_wdt_private.smi_res)); -unmap_gcs_pmc: - if (iTCO_wdt_private.iTCO_version >= 2) - iounmap(iTCO_wdt_private.gcs_pmc); -unreg_gcs_pmc: - if (iTCO_wdt_private.iTCO_version >= 2) - release_mem_region(iTCO_wdt_private.gcs_pmc_res->start, - resource_size(iTCO_wdt_private.gcs_pmc_res)); -out: - iTCO_wdt_private.tco_res = NULL; - iTCO_wdt_private.smi_res = NULL; - iTCO_wdt_private.gcs_pmc_res = NULL; - iTCO_wdt_private.gcs_pmc = NULL; - - return ret; } -static int iTCO_wdt_remove(struct platform_device *dev) +static int iTCO_wdt_remove(struct platform_device *pdev) { - if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res) - iTCO_wdt_cleanup(); + struct iTCO_wdt_private *p = platform_get_drvdata(pdev); - return 0; -} + /* Stop the timer before we leave */ + if (!nowayout) + iTCO_wdt_stop(&p->wddev); -static void iTCO_wdt_shutdown(struct platform_device *dev) -{ - iTCO_wdt_stop(NULL); + return 0; } #ifdef CONFIG_PM_SLEEP @@ -610,21 +562,24 @@ static inline bool need_suspend(void) { return true; } static int iTCO_wdt_suspend_noirq(struct device *dev) { + struct iTCO_wdt_private *p = dev_get_drvdata(dev); int ret = 0; - iTCO_wdt_private.suspended = false; - if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) { - ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); + p->suspended = false; + if (watchdog_active(&p->wddev) && need_suspend()) { + ret = iTCO_wdt_stop(&p->wddev); if (!ret) - iTCO_wdt_private.suspended = true; + p->suspended = true; } return ret; } static int iTCO_wdt_resume_noirq(struct device *dev) { - if (iTCO_wdt_private.suspended) - iTCO_wdt_start(&iTCO_wdt_watchdog_dev); + struct iTCO_wdt_private *p = dev_get_drvdata(dev); + + if (p->suspended) + iTCO_wdt_start(&p->wddev); return 0; } @@ -642,7 +597,6 @@ static const struct dev_pm_ops iTCO_wdt_pm = { static struct platform_driver iTCO_wdt_driver = { .probe = iTCO_wdt_probe, .remove = iTCO_wdt_remove, - .shutdown = iTCO_wdt_shutdown, .driver = { .name = DRV_NAME, .pm = ITCO_WDT_PM_OPS, @@ -651,15 +605,9 @@ static struct platform_driver iTCO_wdt_driver = { static int __init iTCO_wdt_init_module(void) { - int err; - pr_info("Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION); - err = platform_driver_register(&iTCO_wdt_driver); - if (err) - return err; - - return 0; + return platform_driver_register(&iTCO_wdt_driver); } static void __exit iTCO_wdt_cleanup_module(void) diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index 516fbef00856..6ed39dee995f 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c @@ -161,7 +161,7 @@ static int pdc_wdt_restart(struct watchdog_device *wdt_dev, return 0; } -static struct watchdog_info pdc_wdt_info = { +static const struct watchdog_info pdc_wdt_info = { .identity = "IMG PDC Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index a4b729259b12..45e4d02221b5 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -137,7 +137,6 @@ static int mid_wdt_probe(struct platform_device *pdev) wdt_dev->parent = &pdev->dev; watchdog_set_drvdata(wdt_dev, &pdev->dev); - platform_set_drvdata(pdev, wdt_dev); ret = devm_request_irq(&pdev->dev, pdata->irq, mid_wdt_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog", @@ -151,7 +150,7 @@ static int mid_wdt_probe(struct platform_device *pdev) /* Make sure the watchdog is not running */ wdt_stop(wdt_dev); - ret = watchdog_register_device(wdt_dev); + ret = devm_watchdog_register_device(&pdev->dev, wdt_dev); if (ret) { dev_err(&pdev->dev, "error registering watchdog device\n"); return ret; @@ -162,16 +161,8 @@ static int mid_wdt_probe(struct platform_device *pdev) return 0; } -static int mid_wdt_remove(struct platform_device *pdev) -{ - struct watchdog_device *wd = platform_get_drvdata(pdev); - watchdog_unregister_device(wd); - return 0; -} - static struct platform_driver mid_wdt_driver = { .probe = mid_wdt_probe, - .remove = mid_wdt_remove, .driver = { .name = "intel_mid_wdt", }, diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c index 8e302d0e346c..2f3b049ea301 100644 --- a/drivers/watchdog/kempld_wdt.c +++ b/drivers/watchdog/kempld_wdt.c @@ -140,12 +140,19 @@ static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data, unsigned int timeout) { struct kempld_device_data *pld = wdt_data->pld; - u32 prescaler = kempld_prescaler[PRESCALER_21]; + u32 prescaler; u64 stage_timeout64; u32 stage_timeout; u32 remainder; u8 stage_cfg; +#if GCC_VERSION < 40400 + /* work around a bug compiling do_div() */ + prescaler = READ_ONCE(kempld_prescaler[PRESCALER_21]); +#else + prescaler = kempld_prescaler[PRESCALER_21]; +#endif + if (!stage) return -EINVAL; @@ -422,7 +429,7 @@ static int kempld_wdt_probe_stages(struct watchdog_device *wdd) return 0; } -static struct watchdog_info kempld_wdt_info = { +static const struct watchdog_info kempld_wdt_info = { .identity = "KEMPLD Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c index 582f2fa1b8d9..e0823677d8c1 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -3,7 +3,7 @@ * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * - * Copyright (C) 2010 John Crispin <blogic@openwrt.org> + * Copyright (C) 2010 John Crispin <john@phrozen.org> * Based on EP93xx wdt driver */ @@ -240,6 +240,6 @@ module_platform_driver(ltq_wdt_driver); module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); -MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); +MODULE_AUTHOR("John Crispin <john@phrozen.org>"); MODULE_DESCRIPTION("Lantiq SoC Watchdog"); MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c index fd171e6caa16..3b8bb59adf02 100644 --- a/drivers/watchdog/lpc18xx_wdt.c +++ b/drivers/watchdog/lpc18xx_wdt.c @@ -181,7 +181,7 @@ static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev, return 0; } -static struct watchdog_info lpc18xx_wdt_info = { +static const struct watchdog_info lpc18xx_wdt_info = { .identity = "NXP LPC18xx Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c index af6a7c489f08..045201a6fdb3 100644 --- a/drivers/watchdog/mena21_wdt.c +++ b/drivers/watchdog/mena21_wdt.c @@ -212,34 +212,15 @@ static int a21_wdt_probe(struct platform_device *pdev) drv->wdt = a21_wdt; dev_set_drvdata(&pdev->dev, drv); - ret = watchdog_register_device(&a21_wdt); + ret = devm_watchdog_register_device(&pdev->dev, &a21_wdt); if (ret) { dev_err(&pdev->dev, "Cannot register watchdog device\n"); - goto err_register_wd; + return ret; } dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n"); return 0; - -err_register_wd: - mutex_destroy(&drv->lock); - - return ret; -} - -static int a21_wdt_remove(struct platform_device *pdev) -{ - struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev); - - dev_warn(&pdev->dev, - "Unregistering A21 watchdog driver, board may reboot\n"); - - watchdog_unregister_device(&drv->wdt); - - mutex_destroy(&drv->lock); - - return 0; } static void a21_wdt_shutdown(struct platform_device *pdev) @@ -257,7 +238,6 @@ MODULE_DEVICE_TABLE(of, a21_wdt_ids); static struct platform_driver a21_wdt_driver = { .probe = a21_wdt_probe, - .remove = a21_wdt_remove, .shutdown = a21_wdt_shutdown, .driver = { .name = "a21-watchdog", diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index 56ea1caf71c3..491b9bf13d84 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c @@ -201,38 +201,19 @@ static int meson_wdt_probe(struct platform_device *pdev) meson_wdt_stop(&meson_wdt->wdt_dev); - err = watchdog_register_device(&meson_wdt->wdt_dev); + watchdog_stop_on_reboot(&meson_wdt->wdt_dev); + err = devm_watchdog_register_device(&pdev->dev, &meson_wdt->wdt_dev); if (err) return err; - platform_set_drvdata(pdev, meson_wdt); - dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", meson_wdt->wdt_dev.timeout, nowayout); return 0; } -static int meson_wdt_remove(struct platform_device *pdev) -{ - struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); - - watchdog_unregister_device(&meson_wdt->wdt_dev); - - return 0; -} - -static void meson_wdt_shutdown(struct platform_device *pdev) -{ - struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); - - meson_wdt_stop(&meson_wdt->wdt_dev); -} - static struct platform_driver meson_wdt_driver = { .probe = meson_wdt_probe, - .remove = meson_wdt_remove, - .shutdown = meson_wdt_shutdown, .driver = { .name = DRV_NAME, .of_match_table = meson_wdt_dt_ids, diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c index d5735c12067d..48a06067075d 100644 --- a/drivers/watchdog/mt7621_wdt.c +++ b/drivers/watchdog/mt7621_wdt.c @@ -1,7 +1,7 @@ /* * Ralink MT7621/MT7628 built-in hardware watchdog timer * - * Copyright (C) 2014 John Crispin <blogic@openwrt.org> + * Copyright (C) 2014 John Crispin <john@phrozen.org> * * This driver was based on: drivers/watchdog/rt2880_wdt.c * @@ -110,7 +110,7 @@ static struct watchdog_info mt7621_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, }; -static struct watchdog_ops mt7621_wdt_ops = { +static const struct watchdog_ops mt7621_wdt_ops = { .owner = THIS_MODULE, .start = mt7621_wdt_start, .stop = mt7621_wdt_stop, @@ -181,5 +181,5 @@ static struct platform_driver mt7621_wdt_driver = { module_platform_driver(mt7621_wdt_driver); MODULE_DESCRIPTION("MediaTek MT762x hardware watchdog driver"); -MODULE_AUTHOR("John Crispin <blogic@openwrt.org"); +MODULE_AUTHOR("John Crispin <john@phrozen.org"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/nic7018_wdt.c b/drivers/watchdog/nic7018_wdt.c new file mode 100644 index 000000000000..dcd265685837 --- /dev/null +++ b/drivers/watchdog/nic7018_wdt.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2016 National Instruments Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#define LOCK 0xA5 +#define UNLOCK 0x5A + +#define WDT_CTRL_RESET_EN BIT(7) +#define WDT_RELOAD_PORT_EN BIT(7) + +#define WDT_CTRL 1 +#define WDT_RELOAD_CTRL 2 +#define WDT_PRESET_PRESCALE 4 +#define WDT_REG_LOCK 5 +#define WDT_COUNT 6 +#define WDT_RELOAD_PORT 7 + +#define WDT_MIN_TIMEOUT 1 +#define WDT_MAX_TIMEOUT 464 +#define WDT_DEFAULT_TIMEOUT 80 + +#define WDT_MAX_COUNTER 15 + +static unsigned int timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (default=" + __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started. (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct nic7018_wdt { + u16 io_base; + u32 period; + struct watchdog_device wdd; +}; + +struct nic7018_config { + u32 period; + u8 divider; +}; + +static const struct nic7018_config nic7018_configs[] = { + { 2, 4 }, + { 32, 5 }, +}; + +static inline u32 nic7018_timeout(u32 period, u8 counter) +{ + return period * counter - period / 2; +} + +static const struct nic7018_config *nic7018_get_config(u32 timeout, + u8 *counter) +{ + const struct nic7018_config *config; + u8 count; + + if (timeout < 30 && timeout != 16) { + config = &nic7018_configs[0]; + count = timeout / 2 + 1; + } else { + config = &nic7018_configs[1]; + count = DIV_ROUND_UP(timeout + 16, 32); + + if (count > WDT_MAX_COUNTER) + count = WDT_MAX_COUNTER; + } + *counter = count; + return config; +} + +static int nic7018_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + const struct nic7018_config *config; + u8 counter; + + config = nic7018_get_config(timeout, &counter); + + outb(counter << 4 | config->divider, + wdt->io_base + WDT_PRESET_PRESCALE); + + wdd->timeout = nic7018_timeout(config->period, counter); + wdt->period = config->period; + + return 0; +} + +static int nic7018_start(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + u8 control; + + nic7018_set_timeout(wdd, wdd->timeout); + + control = inb(wdt->io_base + WDT_RELOAD_CTRL); + outb(control | WDT_RELOAD_PORT_EN, wdt->io_base + WDT_RELOAD_CTRL); + + outb(1, wdt->io_base + WDT_RELOAD_PORT); + + control = inb(wdt->io_base + WDT_CTRL); + outb(control | WDT_CTRL_RESET_EN, wdt->io_base + WDT_CTRL); + + return 0; +} + +static int nic7018_stop(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + + outb(0, wdt->io_base + WDT_CTRL); + outb(0, wdt->io_base + WDT_RELOAD_CTRL); + outb(0xF0, wdt->io_base + WDT_PRESET_PRESCALE); + + return 0; +} + +static int nic7018_ping(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + + outb(1, wdt->io_base + WDT_RELOAD_PORT); + + return 0; +} + +static unsigned int nic7018_get_timeleft(struct watchdog_device *wdd) +{ + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + u8 count; + + count = inb(wdt->io_base + WDT_COUNT) & 0xF; + if (!count) + return 0; + + return nic7018_timeout(wdt->period, count); +} + +static const struct watchdog_info nic7018_wdd_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "NIC7018 Watchdog", +}; + +static const struct watchdog_ops nic7018_wdd_ops = { + .owner = THIS_MODULE, + .start = nic7018_start, + .stop = nic7018_stop, + .ping = nic7018_ping, + .set_timeout = nic7018_set_timeout, + .get_timeleft = nic7018_get_timeleft, +}; + +static int nic7018_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct watchdog_device *wdd; + struct nic7018_wdt *wdt; + struct resource *io_rc; + int ret; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, wdt); + + io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!io_rc) { + dev_err(dev, "missing IO resources\n"); + return -EINVAL; + } + + if (!devm_request_region(dev, io_rc->start, resource_size(io_rc), + KBUILD_MODNAME)) { + dev_err(dev, "failed to get IO region\n"); + return -EBUSY; + } + + wdt->io_base = io_rc->start; + wdd = &wdt->wdd; + wdd->info = &nic7018_wdd_info; + wdd->ops = &nic7018_wdd_ops; + wdd->min_timeout = WDT_MIN_TIMEOUT; + wdd->max_timeout = WDT_MAX_TIMEOUT; + wdd->timeout = WDT_DEFAULT_TIMEOUT; + wdd->parent = dev; + + watchdog_set_drvdata(wdd, wdt); + watchdog_set_nowayout(wdd, nowayout); + + ret = watchdog_init_timeout(wdd, timeout, dev); + if (ret) + dev_warn(dev, "unable to set timeout value, using default\n"); + + /* Unlock WDT register */ + outb(UNLOCK, wdt->io_base + WDT_REG_LOCK); + + ret = watchdog_register_device(wdd); + if (ret) { + outb(LOCK, wdt->io_base + WDT_REG_LOCK); + dev_err(dev, "failed to register watchdog\n"); + return ret; + } + + dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n", + wdt->io_base, timeout, nowayout); + return 0; +} + +static int nic7018_remove(struct platform_device *pdev) +{ + struct nic7018_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdd); + + /* Lock WDT register */ + outb(LOCK, wdt->io_base + WDT_REG_LOCK); + + return 0; +} + +static const struct acpi_device_id nic7018_device_ids[] = { + {"NIC7018", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, nic7018_device_ids); + +static struct platform_driver watchdog_driver = { + .probe = nic7018_probe, + .remove = nic7018_remove, + .driver = { + .name = KBUILD_MODNAME, + .acpi_match_table = ACPI_PTR(nic7018_device_ids), + }, +}; + +module_platform_driver(watchdog_driver); + +MODULE_DESCRIPTION("National Instruments NIC7018 Watchdog driver"); +MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index c6b8f4a43bde..39be4dd8035e 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -395,7 +395,7 @@ static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev, rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET; - WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout); + WARN(1, FW_BUG "falling back to hardcoded RSTOUT reg %pa\n", &rstout); return devm_ioremap(&pdev->dev, rstout, 0x4); } diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index 0cdfee266690..e35cf5e87907 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -54,7 +54,7 @@ static struct { struct timer_list timer; /* The timer that pings the watchdog */ } pikawdt_private; -static struct watchdog_info ident = { +static struct watchdog_info ident __ro_after_init = { .identity = DRV_NAME, .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c index 0805ee2acd7a..e60f55702ab7 100644 --- a/drivers/watchdog/rn5t618_wdt.c +++ b/drivers/watchdog/rn5t618_wdt.c @@ -130,7 +130,7 @@ static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev) RN5T618_PWRIRQ_IR_WDOG, 0); } -static struct watchdog_info rn5t618_wdt_info = { +static const struct watchdog_info rn5t618_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = DRIVER_NAME, diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c index 14b4fd428fff..05524baf7dcc 100644 --- a/drivers/watchdog/rt2880_wdt.c +++ b/drivers/watchdog/rt2880_wdt.c @@ -2,7 +2,7 @@ * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer * * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> - * Copyright (C) 2013 John Crispin <blogic@openwrt.org> + * Copyright (C) 2013 John Crispin <john@phrozen.org> * * This driver was based on: drivers/watchdog/softdog.c * @@ -124,7 +124,7 @@ static struct watchdog_info rt288x_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, }; -static struct watchdog_ops rt288x_wdt_ops = { +static const struct watchdog_ops rt288x_wdt_ops = { .owner = THIS_MODULE, .start = rt288x_wdt_start, .stop = rt288x_wdt_stop, diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 59e95762a6de..6ed97596ca80 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -23,8 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -46,6 +44,7 @@ #define S3C2410_WTCON 0x00 #define S3C2410_WTDAT 0x04 #define S3C2410_WTCNT 0x08 +#define S3C2410_WTCLRINT 0x0c #define S3C2410_WTCNT_MAXCNT 0xffff @@ -64,14 +63,15 @@ #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8) #define S3C2410_WTCON_PRESCALE_MAX 0xff -#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) -#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) +#define S3C2410_WATCHDOG_ATBOOT (0) +#define S3C2410_WATCHDOG_DEFAULT_TIME (15) #define EXYNOS5_RST_STAT_REG_OFFSET 0x0404 #define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408 #define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c #define QUIRK_HAS_PMU_CONFIG (1 << 0) #define QUIRK_HAS_RST_STAT (1 << 1) +#define QUIRK_HAS_WTCLRINT_REG (1 << 2) /* These quirks require that we have a PMU register map */ #define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \ @@ -79,26 +79,23 @@ static bool nowayout = WATCHDOG_NOWAYOUT; static int tmr_margin; -static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT; +static int tmr_atboot = S3C2410_WATCHDOG_ATBOOT; static int soft_noboot; -static int debug; module_param(tmr_margin, int, 0); module_param(tmr_atboot, int, 0); module_param(nowayout, bool, 0); module_param(soft_noboot, int, 0); -module_param(debug, int, 0); MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default=" - __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")"); + __MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")"); MODULE_PARM_DESC(tmr_atboot, "Watchdog is started at boot time if set to 1, default=" - __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT)); + __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT)); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " "0 to reboot (default 0)"); -MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)"); /** * struct s3c2410_wdt_variant - Per-variant config data @@ -143,13 +140,18 @@ static const struct s3c2410_wdt_variant drv_data_s3c2410 = { }; #ifdef CONFIG_OF +static const struct s3c2410_wdt_variant drv_data_s3c6410 = { + .quirks = QUIRK_HAS_WTCLRINT_REG, +}; + static const struct s3c2410_wdt_variant drv_data_exynos5250 = { .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, .mask_bit = 20, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 20, - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \ + | QUIRK_HAS_WTCLRINT_REG, }; static const struct s3c2410_wdt_variant drv_data_exynos5420 = { @@ -158,7 +160,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { .mask_bit = 0, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 9, - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \ + | QUIRK_HAS_WTCLRINT_REG, }; static const struct s3c2410_wdt_variant drv_data_exynos7 = { @@ -167,12 +170,15 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = { .mask_bit = 23, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, .rst_stat_bit = 23, /* A57 WDTRESET */ - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, + .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \ + | QUIRK_HAS_WTCLRINT_REG, }; static const struct of_device_id s3c2410_wdt_match[] = { { .compatible = "samsung,s3c2410-wdt", .data = &drv_data_s3c2410 }, + { .compatible = "samsung,s3c6410-wdt", + .data = &drv_data_s3c6410 }, { .compatible = "samsung,exynos5250-wdt", .data = &drv_data_exynos5250 }, { .compatible = "samsung,exynos5420-wdt", @@ -193,14 +199,6 @@ static const struct platform_device_id s3c2410_wdt_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids); -/* watchdog control routines */ - -#define DBG(fmt, ...) \ -do { \ - if (debug) \ - pr_info(fmt, ##__VA_ARGS__); \ -} while (0) - /* functions */ static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock) @@ -296,8 +294,8 @@ static int s3c2410wdt_start(struct watchdog_device *wdd) wtcon |= S3C2410_WTCON_RSTEN; } - DBG("%s: count=0x%08x, wtcon=%08lx\n", - __func__, wdt->count, wtcon); + dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n", + wdt->count, wtcon); writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); @@ -326,8 +324,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou freq = DIV_ROUND_UP(freq, 128); count = timeout * freq; - DBG("%s: count=%d, timeout=%d, freq=%lu\n", - __func__, count, timeout, freq); + dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n", + count, timeout, freq); /* if the count is bigger than the watchdog register, then work out what we need to do (and if) we can @@ -343,8 +341,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou } } - DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n", - __func__, timeout, divisor, count, DIV_ROUND_UP(count, divisor)); + dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n", + timeout, divisor, count, DIV_ROUND_UP(count, divisor)); count = DIV_ROUND_UP(count, divisor); wdt->count = count; @@ -394,7 +392,7 @@ static const struct watchdog_info s3c2410_wdt_ident = { .identity = "S3C2410 Watchdog", }; -static struct watchdog_ops s3c2410wdt_ops = { +static const struct watchdog_ops s3c2410wdt_ops = { .owner = THIS_MODULE, .start = s3c2410wdt_start, .stop = s3c2410wdt_stop, @@ -406,7 +404,7 @@ static struct watchdog_ops s3c2410wdt_ops = { static struct watchdog_device s3c2410_wdd = { .info = &s3c2410_wdt_ident, .ops = &s3c2410wdt_ops, - .timeout = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME, + .timeout = S3C2410_WATCHDOG_DEFAULT_TIME, }; /* interrupt handler code */ @@ -418,6 +416,10 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param) dev_info(wdt->dev, "watchdog timer expired (irq)\n"); s3c2410wdt_keepalive(&wdt->wdt_device); + + if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG) + writel(0x1, wdt->reg_base + S3C2410_WTCLRINT); + return IRQ_HANDLED; } @@ -505,9 +507,8 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) return 0; } -/* s3c2410_get_wdt_driver_data */ static inline struct s3c2410_wdt_variant * -get_wdt_drv_data(struct platform_device *pdev) +s3c2410_get_wdt_drv_data(struct platform_device *pdev) { if (pdev->dev.of_node) { const struct of_device_id *match; @@ -529,8 +530,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev) int started = 0; int ret; - DBG("%s: probe=%p\n", __func__, pdev); - dev = &pdev->dev; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); @@ -541,7 +540,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) spin_lock_init(&wdt->lock); wdt->wdt_device = s3c2410_wdd; - wdt->drv_data = get_wdt_drv_data(pdev); + wdt->drv_data = s3c2410_get_wdt_drv_data(pdev); if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, "samsung,syscon-phandle"); @@ -566,8 +565,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev) goto err; } - DBG("probe: mapped reg_base=%p\n", wdt->reg_base); - wdt->clock = devm_clk_get(dev, "watchdog"); if (IS_ERR(wdt->clock)) { dev_err(dev, "failed to find watchdog clock source\n"); @@ -600,12 +597,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev) wdt->wdt_device.timeout); if (ret) { started = s3c2410wdt_set_heartbeat(&wdt->wdt_device, - CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); + S3C2410_WATCHDOG_DEFAULT_TIME); if (started == 0) dev_info(dev, "tmr_margin value out of range, default %d used\n", - CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); + S3C2410_WATCHDOG_DEFAULT_TIME); else dev_info(dev, "default timer value is out of range, " "cannot start\n"); diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index 8965e3f536c3..d3be4f831db5 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -188,12 +188,14 @@ static int __init sa1100dog_init(void) pre_margin = oscr_freq * margin; ret = misc_register(&sa1100dog_miscdev); - if (ret == 0) + if (ret == 0) { pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", margin); - return ret; -err: + return 0; + } + clk_disable_unprepare(clk); +err: clk_put(clk); return ret; } diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index a49634cdc1cc..f709962018ac 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -28,7 +28,7 @@ struct sama5d4_wdt { struct watchdog_device wdd; void __iomem *reg_base; - u32 config; + u32 mr; }; static int wdt_timeout = WDT_DEFAULT_TIMEOUT; @@ -53,11 +53,9 @@ MODULE_PARM_DESC(nowayout, static int sama5d4_wdt_start(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - u32 reg; - reg = wdt_read(wdt, AT91_WDT_MR); - reg &= ~AT91_WDT_WDDIS; - wdt_write(wdt, AT91_WDT_MR, reg); + wdt->mr &= ~AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; } @@ -65,11 +63,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) static int sama5d4_wdt_stop(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - u32 reg; - reg = wdt_read(wdt, AT91_WDT_MR); - reg |= AT91_WDT_WDDIS; - wdt_write(wdt, AT91_WDT_MR, reg); + wdt->mr |= AT91_WDT_WDDIS; + wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; } @@ -88,14 +84,12 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); u32 value = WDT_SEC2TICKS(timeout); - u32 reg; - reg = wdt_read(wdt, AT91_WDT_MR); - reg &= ~AT91_WDT_WDV; - reg &= ~AT91_WDT_WDD; - reg |= AT91_WDT_SET_WDV(value); - reg |= AT91_WDT_SET_WDD(value); - wdt_write(wdt, AT91_WDT_MR, reg); + wdt->mr &= ~AT91_WDT_WDV; + wdt->mr &= ~AT91_WDT_WDD; + wdt->mr |= AT91_WDT_SET_WDV(value); + wdt->mr |= AT91_WDT_SET_WDD(value); + wdt_write(wdt, AT91_WDT_MR, wdt->mr); wdd->timeout = timeout; @@ -107,7 +101,7 @@ static const struct watchdog_info sama5d4_wdt_info = { .identity = "Atmel SAMA5D4 Watchdog", }; -static struct watchdog_ops sama5d4_wdt_ops = { +static const struct watchdog_ops sama5d4_wdt_ops = { .owner = THIS_MODULE, .start = sama5d4_wdt_start, .stop = sama5d4_wdt_stop, @@ -132,19 +126,19 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - wdt->config = AT91_WDT_WDDIS; + wdt->mr = AT91_WDT_WDDIS; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) - wdt->config |= AT91_WDT_WDFIEN; + wdt->mr |= AT91_WDT_WDFIEN; else - wdt->config |= AT91_WDT_WDRSTEN; + wdt->mr |= AT91_WDT_WDRSTEN; if (of_property_read_bool(np, "atmel,idle-halt")) - wdt->config |= AT91_WDT_WDIDLEHLT; + wdt->mr |= AT91_WDT_WDIDLEHLT; if (of_property_read_bool(np, "atmel,dbg-halt")) - wdt->config |= AT91_WDT_WDDBGHLT; + wdt->mr |= AT91_WDT_WDDBGHLT; return 0; } @@ -163,11 +157,10 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) reg &= ~AT91_WDT_WDDIS; wdt_write(wdt, AT91_WDT_MR, reg); - reg = wdt->config; - reg |= AT91_WDT_SET_WDD(value); - reg |= AT91_WDT_SET_WDV(value); + wdt->mr |= AT91_WDT_SET_WDD(value); + wdt->mr |= AT91_WDT_SET_WDV(value); - wdt_write(wdt, AT91_WDT_MR, reg); + wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; } @@ -211,7 +204,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) return ret; } - if ((wdt->config & AT91_WDT_WDFIEN) && irq) { + if ((wdt->mr & AT91_WDT_WDFIEN) && irq) { ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler, IRQF_SHARED | IRQF_IRQPOLL | IRQF_NO_SUSPEND, pdev->name, pdev); @@ -265,11 +258,28 @@ static const struct of_device_id sama5d4_wdt_of_match[] = { }; MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); +#ifdef CONFIG_PM_SLEEP +static int sama5d4_wdt_resume(struct device *dev) +{ + struct sama5d4_wdt *wdt = dev_get_drvdata(dev); + + wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS); + if (wdt->mr & AT91_WDT_WDDIS) + wdt_write(wdt, AT91_WDT_MR, wdt->mr); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sama5d4_wdt_pm_ops, NULL, + sama5d4_wdt_resume); + static struct platform_driver sama5d4_wdt_driver = { .probe = sama5d4_wdt_probe, .remove = sama5d4_wdt_remove, .driver = { .name = "sama5d4_wdt", + .pm = &sama5d4_wdt_pm_ops, .of_match_table = sama5d4_wdt_of_match, } }; diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index ce0c38bd0f00..316c2eb122d2 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -207,7 +207,7 @@ static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct watchdog_info sbsa_gwdt_info = { +static const struct watchdog_info sbsa_gwdt_info = { .identity = WATCHDOG_NAME, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | @@ -215,7 +215,7 @@ static struct watchdog_info sbsa_gwdt_info = { WDIOF_CARDRESET, }; -static struct watchdog_ops sbsa_gwdt_ops = { +static const struct watchdog_ops sbsa_gwdt_ops = { .owner = THIS_MODULE, .start = sbsa_gwdt_start, .stop = sbsa_gwdt_stop, diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c index 3050a0031479..4eea351e09b0 100644 --- a/drivers/watchdog/sirfsoc_wdt.c +++ b/drivers/watchdog/sirfsoc_wdt.c @@ -127,7 +127,7 @@ static const struct watchdog_info sirfsoc_wdt_ident = { .identity = "SiRFSOC Watchdog", }; -static struct watchdog_ops sirfsoc_wdt_ops = { +static const struct watchdog_ops sirfsoc_wdt_ops = { .owner = THIS_MODULE, .start = sirfsoc_wdt_enable, .stop = sirfsoc_wdt_disable, diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index c7bdc986dca1..060740625485 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -21,13 +21,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/hrtimer.h> #include <linux/init.h> -#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/reboot.h> -#include <linux/timer.h> #include <linux/types.h> #include <linux/watchdog.h> @@ -54,7 +53,10 @@ module_param(soft_panic, int, 0); MODULE_PARM_DESC(soft_panic, "Softdog action, set to 1 to panic, 0 to reboot (default=0)"); -static void softdog_fire(unsigned long data) +static struct hrtimer softdog_ticktock; +static struct hrtimer softdog_preticktock; + +static enum hrtimer_restart softdog_fire(struct hrtimer *timer) { module_put(THIS_MODULE); if (soft_noboot) { @@ -67,49 +69,52 @@ static void softdog_fire(unsigned long data) emergency_restart(); pr_crit("Reboot didn't ?????\n"); } -} -static struct timer_list softdog_ticktock = - TIMER_INITIALIZER(softdog_fire, 0, 0); + return HRTIMER_NORESTART; +} static struct watchdog_device softdog_dev; -static void softdog_pretimeout(unsigned long data) +static enum hrtimer_restart softdog_pretimeout(struct hrtimer *timer) { watchdog_notify_pretimeout(&softdog_dev); -} -static struct timer_list softdog_preticktock = - TIMER_INITIALIZER(softdog_pretimeout, 0, 0); + return HRTIMER_NORESTART; +} static int softdog_ping(struct watchdog_device *w) { - if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ))) + if (!hrtimer_active(&softdog_ticktock)) __module_get(THIS_MODULE); - - if (w->pretimeout) - mod_timer(&softdog_preticktock, jiffies + - (w->timeout - w->pretimeout) * HZ); - else - del_timer(&softdog_preticktock); + hrtimer_start(&softdog_ticktock, ktime_set(w->timeout, 0), + HRTIMER_MODE_REL); + + if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) { + if (w->pretimeout) + hrtimer_start(&softdog_preticktock, + ktime_set(w->timeout - w->pretimeout, 0), + HRTIMER_MODE_REL); + else + hrtimer_cancel(&softdog_preticktock); + } return 0; } static int softdog_stop(struct watchdog_device *w) { - if (del_timer(&softdog_ticktock)) + if (hrtimer_cancel(&softdog_ticktock)) module_put(THIS_MODULE); - del_timer(&softdog_preticktock); + if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) + hrtimer_cancel(&softdog_preticktock); return 0; } static struct watchdog_info softdog_info = { .identity = "Software Watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | - WDIOF_PRETIMEOUT, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, }; static const struct watchdog_ops softdog_ops = { @@ -134,6 +139,16 @@ static int __init softdog_init(void) watchdog_set_nowayout(&softdog_dev, nowayout); watchdog_stop_on_reboot(&softdog_dev); + hrtimer_init(&softdog_ticktock, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + softdog_ticktock.function = softdog_fire; + + if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) { + softdog_info.options |= WDIOF_PRETIMEOUT; + hrtimer_init(&softdog_preticktock, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + softdog_preticktock.function = softdog_pretimeout; + } + ret = watchdog_register_device(&softdog_dev); if (ret) return ret; diff --git a/drivers/watchdog/sun4v_wdt.c b/drivers/watchdog/sun4v_wdt.c index 1467fe50a76f..00907973608c 100644 --- a/drivers/watchdog/sun4v_wdt.c +++ b/drivers/watchdog/sun4v_wdt.c @@ -77,7 +77,7 @@ static const struct watchdog_info sun4v_wdt_ident = { .firmware_version = 0, }; -static struct watchdog_ops sun4v_wdt_ops = { +static const struct watchdog_ops sun4v_wdt_ops = { .owner = THIS_MODULE, .start = sun4v_wdt_ping, .stop = sun4v_wdt_stop, diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 953bb7b7446f..9728fa32c357 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -242,8 +242,6 @@ static int sunxi_wdt_probe(struct platform_device *pdev) if (!sunxi_wdt) return -EINVAL; - platform_set_drvdata(pdev, sunxi_wdt); - device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev); if (!device) return -ENODEV; @@ -270,7 +268,8 @@ static int sunxi_wdt_probe(struct platform_device *pdev) sunxi_wdt_stop(&sunxi_wdt->wdt_dev); - err = watchdog_register_device(&sunxi_wdt->wdt_dev); + watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev); + err = devm_watchdog_register_device(&pdev->dev, &sunxi_wdt->wdt_dev); if (unlikely(err)) return err; @@ -280,27 +279,8 @@ static int sunxi_wdt_probe(struct platform_device *pdev) return 0; } -static int sunxi_wdt_remove(struct platform_device *pdev) -{ - struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); - - watchdog_unregister_device(&sunxi_wdt->wdt_dev); - watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); - - return 0; -} - -static void sunxi_wdt_shutdown(struct platform_device *pdev) -{ - struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); - - sunxi_wdt_stop(&sunxi_wdt->wdt_dev); -} - static struct platform_driver sunxi_wdt_driver = { .probe = sunxi_wdt_probe, - .remove = sunxi_wdt_remove, - .shutdown = sunxi_wdt_shutdown, .driver = { .name = DRV_NAME, .of_match_table = sunxi_wdt_dt_ids, diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c index 202c4b9cc921..d5fcce062920 100644 --- a/drivers/watchdog/tangox_wdt.c +++ b/drivers/watchdog/tangox_wdt.c @@ -15,9 +15,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/notifier.h> #include <linux/platform_device.h> -#include <linux/reboot.h> #include <linux/watchdog.h> #define DEFAULT_TIMEOUT 30 @@ -47,7 +45,6 @@ struct tangox_wdt_device { void __iomem *base; unsigned long clk_rate; struct clk *clk; - struct notifier_block restart; }; static int tangox_wdt_set_timeout(struct watchdog_device *wdt, @@ -96,24 +93,24 @@ static const struct watchdog_info tangox_wdt_info = { .identity = "tangox watchdog", }; +static int tangox_wdt_restart(struct watchdog_device *wdt, + unsigned long action, void *data) +{ + struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); + + writel(1, dev->base + WD_COUNTER); + + return 0; +} + static const struct watchdog_ops tangox_wdt_ops = { .start = tangox_wdt_start, .stop = tangox_wdt_stop, .set_timeout = tangox_wdt_set_timeout, .get_timeleft = tangox_wdt_get_timeleft, + .restart = tangox_wdt_restart, }; -static int tangox_wdt_restart(struct notifier_block *nb, unsigned long action, - void *data) -{ - struct tangox_wdt_device *dev = - container_of(nb, struct tangox_wdt_device, restart); - - writel(1, dev->base + WD_COUNTER); - - return NOTIFY_DONE; -} - static int tangox_wdt_probe(struct platform_device *pdev) { struct tangox_wdt_device *dev; @@ -174,18 +171,14 @@ static int tangox_wdt_probe(struct platform_device *pdev) tangox_wdt_start(&dev->wdt); } + watchdog_set_restart_priority(&dev->wdt, 128); + err = watchdog_register_device(&dev->wdt); if (err) goto err; platform_set_drvdata(pdev, dev); - dev->restart.notifier_call = tangox_wdt_restart; - dev->restart.priority = 128; - err = register_restart_handler(&dev->restart); - if (err) - dev_warn(&pdev->dev, "failed to register restart handler\n"); - dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n"); return 0; @@ -202,7 +195,6 @@ static int tangox_wdt_remove(struct platform_device *pdev) tangox_wdt_stop(&dev->wdt); clk_disable_unprepare(dev->clk); - unregister_restart_handler(&dev->restart); watchdog_unregister_device(&dev->wdt); return 0; diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index 2d53c3f9394f..9403c08816e3 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -226,7 +226,7 @@ static int tegra_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(wdd, nowayout); - ret = watchdog_register_device(wdd); + ret = devm_watchdog_register_device(&pdev->dev, wdd); if (ret) { dev_err(&pdev->dev, "failed to register watchdog device\n"); @@ -248,8 +248,6 @@ static int tegra_wdt_remove(struct platform_device *pdev) tegra_wdt_stop(&wdt->wdd); - watchdog_unregister_device(&wdt->wdd); - dev_info(&pdev->dev, "removed wdt\n"); return 0; diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 4b541934b6c5..17c25daebcce 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -13,428 +13,159 @@ * warranty of any kind, whether express or implied. */ -#include <linux/fs.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/miscdevice.h> -#include <linux/mutex.h> #include <linux/platform_device.h> -#include <linux/slab.h> +#include <linux/module.h> #include <linux/watchdog.h> -#include <linux/uaccess.h> +#include <linux/io.h> -#define TS72XX_WDT_FEED_VAL 0x05 -#define TS72XX_WDT_DEFAULT_TIMEOUT 8 +#define TS72XX_WDT_DEFAULT_TIMEOUT 30 -static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT; +static int timeout; module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. " - "(1 <= timeout <= 8, default=" - __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT) - ")"); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds."); static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); -/** - * struct ts72xx_wdt - watchdog control structure - * @lock: lock that protects this structure - * @regval: watchdog timeout value suitable for control register - * @flags: flags controlling watchdog device state - * @control_reg: watchdog control register - * @feed_reg: watchdog feed register - * @pdev: back pointer to platform dev - */ -struct ts72xx_wdt { - struct mutex lock; - int regval; - -#define TS72XX_WDT_BUSY_FLAG 1 -#define TS72XX_WDT_EXPECT_CLOSE_FLAG 2 - int flags; +/* priv->control_reg */ +#define TS72XX_WDT_CTRL_DISABLE 0x00 +#define TS72XX_WDT_CTRL_250MS 0x01 +#define TS72XX_WDT_CTRL_500MS 0x02 +#define TS72XX_WDT_CTRL_1SEC 0x03 +#define TS72XX_WDT_CTRL_RESERVED 0x04 +#define TS72XX_WDT_CTRL_2SEC 0x05 +#define TS72XX_WDT_CTRL_4SEC 0x06 +#define TS72XX_WDT_CTRL_8SEC 0x07 + +/* priv->feed_reg */ +#define TS72XX_WDT_FEED_VAL 0x05 +struct ts72xx_wdt_priv { void __iomem *control_reg; void __iomem *feed_reg; - - struct platform_device *pdev; + struct watchdog_device wdd; + unsigned char regval; }; -static struct platform_device *ts72xx_wdt_pdev; - -/* - * TS-72xx Watchdog supports following timeouts (value written - * to control register): - * value description - * ------------------------- - * 0x00 watchdog disabled - * 0x01 250ms - * 0x02 500ms - * 0x03 1s - * 0x04 reserved - * 0x05 2s - * 0x06 4s - * 0x07 8s - * - * Timeouts below 1s are not very usable so we don't - * allow them at all. - * - * We provide two functions that convert between these: - * timeout_to_regval() and regval_to_timeout(). - */ -static const struct { - int timeout; - int regval; -} ts72xx_wdt_map[] = { - { 1, 3 }, - { 2, 5 }, - { 4, 6 }, - { 8, 7 }, -}; - -/** - * timeout_to_regval() - converts given timeout to control register value - * @new_timeout: timeout in seconds to be converted - * - * Function converts given @new_timeout into valid value that can - * be programmed into watchdog control register. When conversion is - * not possible, function returns %-EINVAL. - */ -static int timeout_to_regval(int new_timeout) -{ - int i; - - /* first limit it to 1 - 8 seconds */ - new_timeout = clamp_val(new_timeout, 1, 8); - - for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { - if (ts72xx_wdt_map[i].timeout >= new_timeout) - return ts72xx_wdt_map[i].regval; - } - - return -EINVAL; -} - -/** - * regval_to_timeout() - converts control register value to timeout - * @regval: control register value to be converted - * - * Function converts given @regval to timeout in seconds (1, 2, 4 or 8). - * If @regval cannot be converted, function returns %-EINVAL. - */ -static int regval_to_timeout(int regval) +static int ts72xx_wdt_start(struct watchdog_device *wdd) { - int i; + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); - for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { - if (ts72xx_wdt_map[i].regval == regval) - return ts72xx_wdt_map[i].timeout; - } + writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg); + writeb(priv->regval, priv->control_reg); - return -EINVAL; + return 0; } -/** - * ts72xx_wdt_kick() - kick the watchdog - * @wdt: watchdog to be kicked - * - * Called with @wdt->lock held. - */ -static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt) +static int ts72xx_wdt_stop(struct watchdog_device *wdd) { - __raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg); -} + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); -/** - * ts72xx_wdt_start() - starts the watchdog timer - * @wdt: watchdog to be started - * - * This function programs timeout to watchdog timer - * and starts it. - * - * Called with @wdt->lock held. - */ -static void ts72xx_wdt_start(struct ts72xx_wdt *wdt) -{ - /* - * To program the wdt, it first must be "fed" and - * only after that (within 30 usecs) the configuration - * can be changed. - */ - ts72xx_wdt_kick(wdt); - __raw_writeb((u8)wdt->regval, wdt->control_reg); -} + writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg); + writeb(TS72XX_WDT_CTRL_DISABLE, priv->control_reg); -/** - * ts72xx_wdt_stop() - stops the watchdog timer - * @wdt: watchdog to be stopped - * - * Called with @wdt->lock held. - */ -static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt) -{ - ts72xx_wdt_kick(wdt); - __raw_writeb(0, wdt->control_reg); + return 0; } -static int ts72xx_wdt_open(struct inode *inode, struct file *file) +static int ts72xx_wdt_ping(struct watchdog_device *wdd) { - struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev); - int regval; - - /* - * Try to convert default timeout to valid register - * value first. - */ - regval = timeout_to_regval(timeout); - if (regval < 0) { - dev_err(&wdt->pdev->dev, - "failed to convert timeout (%d) to register value\n", - timeout); - return regval; - } - - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); - if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) { - mutex_unlock(&wdt->lock); - return -EBUSY; - } - - wdt->flags = TS72XX_WDT_BUSY_FLAG; - wdt->regval = regval; - file->private_data = wdt; - - ts72xx_wdt_start(wdt); + writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg); - mutex_unlock(&wdt->lock); - return nonseekable_open(inode, file); + return 0; } -static int ts72xx_wdt_release(struct inode *inode, struct file *file) +static int ts72xx_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) { - struct ts72xx_wdt *wdt = file->private_data; - - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; - - if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) { - ts72xx_wdt_stop(wdt); + struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd); + + if (to == 1) { + priv->regval = TS72XX_WDT_CTRL_1SEC; + } else if (to == 2) { + priv->regval = TS72XX_WDT_CTRL_2SEC; + } else if (to <= 4) { + priv->regval = TS72XX_WDT_CTRL_4SEC; + to = 4; } else { - dev_warn(&wdt->pdev->dev, - "TS-72XX WDT device closed unexpectly. " - "Watchdog timer will not stop!\n"); - /* - * Kick it one more time, to give userland some time - * to recover (for example, respawning the kicker - * daemon). - */ - ts72xx_wdt_kick(wdt); + priv->regval = TS72XX_WDT_CTRL_8SEC; + if (to <= 8) + to = 8; } - wdt->flags = 0; + wdd->timeout = to; - mutex_unlock(&wdt->lock); - return 0; -} - -static ssize_t ts72xx_wdt_write(struct file *file, - const char __user *data, - size_t len, - loff_t *ppos) -{ - struct ts72xx_wdt *wdt = file->private_data; - - if (!len) - return 0; - - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; - - ts72xx_wdt_kick(wdt); - - /* - * Support for magic character closing. User process - * writes 'V' into the device, just before it is closed. - * This means that we know that the wdt timer can be - * stopped after user closes the device. - */ - if (!nowayout) { - int i; - - for (i = 0; i < len; i++) { - char c; - - /* In case it was set long ago */ - wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG; - - if (get_user(c, data + i)) { - mutex_unlock(&wdt->lock); - return -EFAULT; - } - if (c == 'V') { - wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG; - break; - } - } + if (watchdog_active(wdd)) { + ts72xx_wdt_stop(wdd); + ts72xx_wdt_start(wdd); } - mutex_unlock(&wdt->lock); - return len; + return 0; } -static const struct watchdog_info winfo = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | +static const struct watchdog_info ts72xx_wdt_ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "TS-72XX WDT", }; -static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct ts72xx_wdt *wdt = file->private_data; - void __user *argp = (void __user *)arg; - int __user *p = (int __user *)argp; - int error = 0; - - if (mutex_lock_interruptible(&wdt->lock)) - return -ERESTARTSYS; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &winfo, sizeof(winfo))) - error = -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - error = put_user(0, p); - break; - - case WDIOC_KEEPALIVE: - ts72xx_wdt_kick(wdt); - break; - - case WDIOC_SETOPTIONS: { - int options; - - error = get_user(options, p); - if (error) - break; - - error = -EINVAL; - - if ((options & WDIOS_DISABLECARD) != 0) { - ts72xx_wdt_stop(wdt); - error = 0; - } - if ((options & WDIOS_ENABLECARD) != 0) { - ts72xx_wdt_start(wdt); - error = 0; - } - - break; - } - - case WDIOC_SETTIMEOUT: { - int new_timeout; - int regval; - - error = get_user(new_timeout, p); - if (error) - break; - - regval = timeout_to_regval(new_timeout); - if (regval < 0) { - error = regval; - break; - } - ts72xx_wdt_stop(wdt); - wdt->regval = regval; - ts72xx_wdt_start(wdt); - - /*FALLTHROUGH*/ - } - - case WDIOC_GETTIMEOUT: - error = put_user(regval_to_timeout(wdt->regval), p); - break; - - default: - error = -ENOTTY; - break; - } - - mutex_unlock(&wdt->lock); - return error; -} - -static const struct file_operations ts72xx_wdt_fops = { +static struct watchdog_ops ts72xx_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .open = ts72xx_wdt_open, - .release = ts72xx_wdt_release, - .write = ts72xx_wdt_write, - .unlocked_ioctl = ts72xx_wdt_ioctl, -}; - -static struct miscdevice ts72xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ts72xx_wdt_fops, + .start = ts72xx_wdt_start, + .stop = ts72xx_wdt_stop, + .ping = ts72xx_wdt_ping, + .set_timeout = ts72xx_wdt_settimeout, }; static int ts72xx_wdt_probe(struct platform_device *pdev) { - struct ts72xx_wdt *wdt; - struct resource *r1, *r2; - int error = 0; + struct ts72xx_wdt_priv *priv; + struct watchdog_device *wdd; + struct resource *res; + int ret; - wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL); - if (!wdt) + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1); - if (IS_ERR(wdt->control_reg)) - return PTR_ERR(wdt->control_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->control_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->control_reg)) + return PTR_ERR(priv->control_reg); - r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2); - if (IS_ERR(wdt->feed_reg)) - return PTR_ERR(wdt->feed_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->feed_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->feed_reg)) + return PTR_ERR(priv->feed_reg); - platform_set_drvdata(pdev, wdt); - ts72xx_wdt_pdev = pdev; - wdt->pdev = pdev; - mutex_init(&wdt->lock); + wdd = &priv->wdd; + wdd->info = &ts72xx_wdt_ident; + wdd->ops = &ts72xx_wdt_ops; + wdd->min_timeout = 1; + wdd->max_hw_heartbeat_ms = 8000; + wdd->parent = &pdev->dev; - /* make sure that the watchdog is disabled */ - ts72xx_wdt_stop(wdt); + watchdog_set_nowayout(wdd, nowayout); - error = misc_register(&ts72xx_wdt_miscdev); - if (error) { - dev_err(&pdev->dev, "failed to register miscdev\n"); - return error; - } + wdd->timeout = TS72XX_WDT_DEFAULT_TIMEOUT; + watchdog_init_timeout(wdd, timeout, &pdev->dev); - dev_info(&pdev->dev, "TS-72xx Watchdog driver\n"); + watchdog_set_drvdata(wdd, priv); - return 0; -} + ret = devm_watchdog_register_device(&pdev->dev, wdd); + if (ret) + return ret; + + dev_info(&pdev->dev, "TS-72xx Watchdog driver\n"); -static int ts72xx_wdt_remove(struct platform_device *pdev) -{ - misc_deregister(&ts72xx_wdt_miscdev); return 0; } static struct platform_driver ts72xx_wdt_driver = { .probe = ts72xx_wdt_probe, - .remove = ts72xx_wdt_remove, .driver = { .name = "ts72xx-wdt", }, diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index ef2ecaf53a14..98fd186c6878 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -297,7 +297,7 @@ static unsigned int wdt_get_time(struct watchdog_device *wdog) * Kernel Interfaces */ -static struct watchdog_info wdt_info = { +static const struct watchdog_info wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "W83627HF Watchdog", }; diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 32930a073a12..d5d2bbd8f428 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -987,6 +987,11 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) wdd->wd_data = NULL; mutex_unlock(&wd_data->lock); + if (watchdog_active(wdd) && + test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) { + watchdog_stop(wdd); + } + cancel_delayed_work_sync(&wd_data->work); kref_put(&wd_data->kref, watchdog_core_data_release); diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c index 8d1184aee932..1ddc1f742cd4 100644 --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c @@ -194,7 +194,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev) if (ret < 0) { dev_err(wm831x->dev, "Failed to read watchdog status: %d\n", ret); - goto err; + return ret; } reg = ret; @@ -203,10 +203,8 @@ static int wm831x_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); - if (!driver_data) { - ret = -ENOMEM; - goto err; - } + if (!driver_data) + return -ENOMEM; mutex_init(&driver_data->lock); driver_data->wm831x = wm831x; @@ -253,7 +251,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev) dev_err(wm831x->dev, "Failed to request update GPIO: %d\n", ret); - goto err; + return ret; } driver_data->update_gpio = pdata->update_gpio; @@ -269,37 +267,22 @@ static int wm831x_wdt_probe(struct platform_device *pdev) } else { dev_err(wm831x->dev, "Failed to unlock security key: %d\n", ret); - goto err; + return ret; } } - ret = watchdog_register_device(&driver_data->wdt); + ret = devm_watchdog_register_device(&pdev->dev, &driver_data->wdt); if (ret != 0) { dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n", ret); - goto err; + return ret; } - platform_set_drvdata(pdev, driver_data); - - return 0; - -err: - return ret; -} - -static int wm831x_wdt_remove(struct platform_device *pdev) -{ - struct wm831x_wdt_drvdata *driver_data = platform_get_drvdata(pdev); - - watchdog_unregister_device(&driver_data->wdt); - return 0; } static struct platform_driver wm831x_wdt_driver = { .probe = wm831x_wdt_probe, - .remove = wm831x_wdt_remove, .driver = { .name = "wm831x-watchdog", }, diff --git a/drivers/watchdog/zx2967_wdt.c b/drivers/watchdog/zx2967_wdt.c new file mode 100644 index 000000000000..e290d5a13a6d --- /dev/null +++ b/drivers/watchdog/zx2967_wdt.c @@ -0,0 +1,291 @@ +/* + * watchdog driver for ZTE's zx2967 family + * + * Copyright (C) 2017 ZTE Ltd. + * + * Author: Baoyou Xie <baoyou.xie@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/watchdog.h> + +#define ZX2967_WDT_CFG_REG 0x4 +#define ZX2967_WDT_LOAD_REG 0x8 +#define ZX2967_WDT_REFRESH_REG 0x18 +#define ZX2967_WDT_START_REG 0x1c + +#define ZX2967_WDT_REFRESH_MASK GENMASK(5, 0) + +#define ZX2967_WDT_CFG_DIV(n) ((((n) & 0xff) - 1) << 8) +#define ZX2967_WDT_START_EN 0x1 + +/* + * Hardware magic number. + * When watchdog reg is written, the lowest 16 bits are valid, but + * the highest 16 bits should be always this number. + */ +#define ZX2967_WDT_WRITEKEY (0x1234 << 16) +#define ZX2967_WDT_VAL_MASK GENMASK(15, 0) + +#define ZX2967_WDT_DIV_DEFAULT 16 +#define ZX2967_WDT_DEFAULT_TIMEOUT 32 +#define ZX2967_WDT_MIN_TIMEOUT 1 +#define ZX2967_WDT_MAX_TIMEOUT 524 +#define ZX2967_WDT_MAX_COUNT 0xffff + +#define ZX2967_WDT_CLK_FREQ 0x8000 + +#define ZX2967_WDT_FLAG_REBOOT_MON BIT(0) + +struct zx2967_wdt { + struct watchdog_device wdt_device; + void __iomem *reg_base; + struct clk *clock; +}; + +static inline u32 zx2967_wdt_readl(struct zx2967_wdt *wdt, u16 reg) +{ + return readl_relaxed(wdt->reg_base + reg); +} + +static inline void zx2967_wdt_writel(struct zx2967_wdt *wdt, u16 reg, u32 val) +{ + writel_relaxed(val | ZX2967_WDT_WRITEKEY, wdt->reg_base + reg); +} + +static void zx2967_wdt_refresh(struct zx2967_wdt *wdt) +{ + u32 val; + + val = zx2967_wdt_readl(wdt, ZX2967_WDT_REFRESH_REG); + /* + * Bit 4-5, 1 and 2: refresh config info + * Bit 2-3, 1 and 2: refresh counter + * Bit 0-1, 1 and 2: refresh int-value + * we shift each group value between 1 and 2 to refresh all data. + */ + val ^= ZX2967_WDT_REFRESH_MASK; + zx2967_wdt_writel(wdt, ZX2967_WDT_REFRESH_REG, + val & ZX2967_WDT_VAL_MASK); +} + +static int +zx2967_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned int divisor = ZX2967_WDT_DIV_DEFAULT; + u32 count; + + count = timeout * ZX2967_WDT_CLK_FREQ; + if (count > divisor * ZX2967_WDT_MAX_COUNT) + divisor = DIV_ROUND_UP(count, ZX2967_WDT_MAX_COUNT); + count = DIV_ROUND_UP(count, divisor); + zx2967_wdt_writel(wdt, ZX2967_WDT_CFG_REG, + ZX2967_WDT_CFG_DIV(divisor) & ZX2967_WDT_VAL_MASK); + zx2967_wdt_writel(wdt, ZX2967_WDT_LOAD_REG, + count & ZX2967_WDT_VAL_MASK); + zx2967_wdt_refresh(wdt); + wdd->timeout = (count * divisor) / ZX2967_WDT_CLK_FREQ; + + return 0; +} + +static void __zx2967_wdt_start(struct zx2967_wdt *wdt) +{ + u32 val; + + val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); + val |= ZX2967_WDT_START_EN; + zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, + val & ZX2967_WDT_VAL_MASK); +} + +static void __zx2967_wdt_stop(struct zx2967_wdt *wdt) +{ + u32 val; + + val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); + val &= ~ZX2967_WDT_START_EN; + zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, + val & ZX2967_WDT_VAL_MASK); +} + +static int zx2967_wdt_start(struct watchdog_device *wdd) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + + zx2967_wdt_set_timeout(wdd, wdd->timeout); + __zx2967_wdt_start(wdt); + + return 0; +} + +static int zx2967_wdt_stop(struct watchdog_device *wdd) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + + __zx2967_wdt_stop(wdt); + + return 0; +} + +static int zx2967_wdt_keepalive(struct watchdog_device *wdd) +{ + struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); + + zx2967_wdt_refresh(wdt); + + return 0; +} + +#define ZX2967_WDT_OPTIONS \ + (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) +static const struct watchdog_info zx2967_wdt_ident = { + .options = ZX2967_WDT_OPTIONS, + .identity = "zx2967 watchdog", +}; + +static struct watchdog_ops zx2967_wdt_ops = { + .owner = THIS_MODULE, + .start = zx2967_wdt_start, + .stop = zx2967_wdt_stop, + .ping = zx2967_wdt_keepalive, + .set_timeout = zx2967_wdt_set_timeout, +}; + +static void zx2967_wdt_reset_sysctrl(struct device *dev) +{ + int ret; + void __iomem *regmap; + unsigned int offset, mask, config; + struct of_phandle_args out_args; + + ret = of_parse_phandle_with_fixed_args(dev->of_node, + "zte,wdt-reset-sysctrl", 3, 0, &out_args); + if (ret) + return; + + offset = out_args.args[0]; + config = out_args.args[1]; + mask = out_args.args[2]; + + regmap = syscon_node_to_regmap(out_args.np); + if (IS_ERR(regmap)) { + of_node_put(out_args.np); + return; + } + + regmap_update_bits(regmap, offset, mask, config); + of_node_put(out_args.np); +} + +static int zx2967_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct zx2967_wdt *wdt; + struct resource *base; + int ret; + struct reset_control *rstc; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, wdt); + + wdt->wdt_device.info = &zx2967_wdt_ident; + wdt->wdt_device.ops = &zx2967_wdt_ops; + wdt->wdt_device.timeout = ZX2967_WDT_DEFAULT_TIMEOUT; + wdt->wdt_device.max_timeout = ZX2967_WDT_MAX_TIMEOUT; + wdt->wdt_device.min_timeout = ZX2967_WDT_MIN_TIMEOUT; + wdt->wdt_device.parent = &pdev->dev; + + base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->reg_base = devm_ioremap_resource(dev, base); + if (IS_ERR(wdt->reg_base)) { + dev_err(dev, "ioremap failed\n"); + return PTR_ERR(wdt->reg_base); + } + + zx2967_wdt_reset_sysctrl(dev); + + wdt->clock = devm_clk_get(dev, NULL); + if (IS_ERR(wdt->clock)) { + dev_err(dev, "failed to find watchdog clock source\n"); + return PTR_ERR(wdt->clock); + } + + ret = clk_prepare_enable(wdt->clock); + if (ret < 0) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + clk_set_rate(wdt->clock, ZX2967_WDT_CLK_FREQ); + + rstc = devm_reset_control_get(dev, NULL); + if (IS_ERR(rstc)) { + dev_err(dev, "failed to get rstc"); + ret = PTR_ERR(rstc); + goto err; + } + + reset_control_assert(rstc); + reset_control_deassert(rstc); + + watchdog_set_drvdata(&wdt->wdt_device, wdt); + watchdog_init_timeout(&wdt->wdt_device, + ZX2967_WDT_DEFAULT_TIMEOUT, dev); + watchdog_set_nowayout(&wdt->wdt_device, WATCHDOG_NOWAYOUT); + + ret = watchdog_register_device(&wdt->wdt_device); + if (ret) + goto err; + + dev_info(dev, "watchdog enabled (timeout=%d sec, nowayout=%d)", + wdt->wdt_device.timeout, WATCHDOG_NOWAYOUT); + + return 0; + +err: + clk_disable_unprepare(wdt->clock); + return ret; +} + +static int zx2967_wdt_remove(struct platform_device *pdev) +{ + struct zx2967_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdt_device); + clk_disable_unprepare(wdt->clock); + + return 0; +} + +static const struct of_device_id zx2967_wdt_match[] = { + { .compatible = "zte,zx296718-wdt", }, + {} +}; +MODULE_DEVICE_TABLE(of, zx2967_wdt_match); + +static struct platform_driver zx2967_wdt_driver = { + .probe = zx2967_wdt_probe, + .remove = zx2967_wdt_remove, + .driver = { + .name = "zx2967-wdt", + .of_match_table = of_match_ptr(zx2967_wdt_match), + }, +}; +module_platform_driver(zx2967_wdt_driver); + +MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); +MODULE_DESCRIPTION("ZTE zx2967 Watchdog Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index db107fa50ca1..a6d4378eb8d9 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -41,6 +41,7 @@ #include <linux/cpu.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/cred.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/bootmem.h> diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 2ef2b61b69df..c77a0751a311 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -32,6 +32,7 @@ #include <linux/types.h> #include <linux/uaccess.h> #include <linux/sched.h> +#include <linux/sched/mm.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/highmem.h> diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 2077a3ac7c0c..7a92a5e1d40c 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -804,10 +804,10 @@ static void privcmd_close(struct vm_area_struct *vma) kfree(pages); } -static int privcmd_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int privcmd_fault(struct vm_fault *vmf) { printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", - vma, vma->vm_start, vma->vm_end, + vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end, vmf->pgoff, (void *)vmf->address); return VM_FAULT_SIGBUS; |