diff options
69 files changed, 2974 insertions, 337 deletions
diff --git a/Documentation/devicetree/bindings/extcon/extcon-arizona.txt b/Documentation/devicetree/bindings/extcon/extcon-arizona.txt index 7f3d94ae81ff..208daaff0be4 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-arizona.txt +++ b/Documentation/devicetree/bindings/extcon/extcon-arizona.txt @@ -72,5 +72,5 @@ codec: wm8280@0 { 1 2 1 /* MICDET2 MICBIAS2 GPIO=high */ >; - wlf,gpsw = <0>; + wlf,gpsw = <ARIZONA_GPSW_OPEN>; }; diff --git a/Documentation/devicetree/bindings/extcon/extcon-fsa9480.txt b/Documentation/devicetree/bindings/extcon/extcon-fsa9480.txt index d592c21245f2..624bd76f468e 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-fsa9480.txt +++ b/Documentation/devicetree/bindings/extcon/extcon-fsa9480.txt @@ -5,7 +5,9 @@ controlled using I2C and enables USB data, stereo and mono audio, video, microphone, and UART data to use a common connector port. Required properties: - - compatible : Must be "fcs,fsa9480" + - compatible : Must be one of + "fcs,fsa9480" + "fcs,fsa880" - reg : Specifies i2c slave address. Must be 0x25. - interrupts : Should contain one entry specifying interrupt signal of interrupt parent to which interrupt pin of the chip is connected. diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcs404.txt b/Documentation/devicetree/bindings/interconnect/qcom,qcs404.txt new file mode 100644 index 000000000000..c07d89812b73 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcs404.txt @@ -0,0 +1,45 @@ +Qualcomm QCS404 Network-On-Chip interconnect driver binding +----------------------------------------------------------- + +Required properties : +- compatible : shall contain only one of the following: + "qcom,qcs404-bimc" + "qcom,qcs404-pcnoc" + "qcom,qcs404-snoc" +- #interconnect-cells : should contain 1 + +reg : specifies the physical base address and size of registers +clocks : list of phandles and specifiers to all interconnect bus clocks +clock-names : clock names should include both "bus" and "bus_a" + +Example: + +soc { + ... + bimc: interconnect@400000 { + reg = <0x00400000 0x80000>; + compatible = "qcom,qcs404-bimc"; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; + }; + + pnoc: interconnect@500000 { + reg = <0x00500000 0x15080>; + compatible = "qcom,qcs404-pcnoc"; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_PNOC_CLK>, + <&rpmcc RPM_SMD_PNOC_A_CLK>; + }; + + snoc: interconnect@580000 { + reg = <0x00580000 0x23080>; + compatible = "qcom,qcs404-snoc"; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, + <&rpmcc RPM_SMD_SNOC_A_CLK>; + }; +}; diff --git a/Documentation/devicetree/bindings/pci/pci-armada8k.txt b/Documentation/devicetree/bindings/pci/pci-armada8k.txt index 9e3fc15e1af8..8324a4ee6f06 100644 --- a/Documentation/devicetree/bindings/pci/pci-armada8k.txt +++ b/Documentation/devicetree/bindings/pci/pci-armada8k.txt @@ -17,6 +17,14 @@ Required properties: name must be "core" for the first clock and "reg" for the second one +Optional properties: +- phys: phandle(s) to PHY node(s) following the generic PHY bindings. + Either 1, 2 or 4 PHYs might be needed depending on the number of + PCIe lanes. +- phy-names: names of the PHYs corresponding to the number of lanes. + Must be "cp0-pcie0-x4-lane0-phy", "cp0-pcie0-x4-lane1-phy" for + 2 PHYs. + Example: pcie@f2600000 { diff --git a/Documentation/devicetree/bindings/phy/lantiq,vrx200-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/lantiq,vrx200-pcie-phy.yaml new file mode 100644 index 000000000000..8a56a8526cef --- /dev/null +++ b/Documentation/devicetree/bindings/phy/lantiq,vrx200-pcie-phy.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/lantiq,vrx200-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq VRX200 and ARX300 PCIe PHY Device Tree Bindings + +maintainers: + - Martin Blumenstingl <[email protected]> + +properties: + "#phy-cells": + const: 1 + description: selects the PHY mode as defined in <dt-bindings/phy/phy-lantiq-vrx200-pcie.h> + + compatible: + enum: + - lantiq,vrx200-pcie-phy + - lantiq,arx300-pcie-phy + + reg: + maxItems: 1 + + clocks: + items: + - description: PHY module clock + - description: PDI register clock + + clock-names: + items: + - const: phy + - const: pdi + + resets: + items: + - description: exclusive PHY reset line + - description: shared reset line between the PCIe PHY and PCIe controller + + resets-names: + items: + - const: phy + - const: pcie + + lantiq,rcu: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to the RCU syscon + + lantiq,rcu-endian-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: the offset of the endian registers for this PHY instance in the RCU syscon + + lantiq,rcu-big-endian-mask: + $ref: /schemas/types.yaml#/definitions/uint32 + description: the mask to set the PDI (PHY) registers for this PHY instance to big endian + + big-endian: + description: Configures the PDI (PHY) registers in big-endian mode + type: boolean + + little-endian: + description: Configures the PDI (PHY) registers in big-endian mode + type: boolean + +required: + - "#phy-cells" + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - lantiq,rcu + - lantiq,rcu-endian-offset + - lantiq,rcu-big-endian-mask + +additionalProperties: false + +examples: + - | + pcie0_phy: phy@106800 { + compatible = "lantiq,vrx200-pcie-phy"; + reg = <0x106800 0x100>; + lantiq,rcu = <&rcu0>; + lantiq,rcu-endian-offset = <0x4c>; + lantiq,rcu-big-endian-mask = <0x80>; /* bit 7 */ + big-endian; + clocks = <&pmu 32>, <&pmu 36>; + clock-names = "phy", "pdi"; + resets = <&reset0 12 24>, <&reset0 22 22>; + reset-names = "phy", "pcie"; + #phy-cells = <1>; + }; + +... diff --git a/Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt b/Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt index cf2cd86db267..8c60e6985950 100644 --- a/Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt +++ b/Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt @@ -25,6 +25,13 @@ Required properties: - #address-cells: should be 1. - #size-cells: should be 0. +Optional properlties: + +- clocks: pointers to the reference clocks for this device (CP110 only), + consequently: MG clock, MG Core clock, AXI clock. +- clock-names: names of used clocks for CP110 only, must be : + "mg_clk", "mg_core_clk" and "axi_clk". + A sub-node is required for each comphy lane provided by the comphy. Required properties (child nodes): @@ -39,6 +46,9 @@ Examples: compatible = "marvell,comphy-cp110"; reg = <0x120000 0x6000>; marvell,system-controller = <&cpm_syscon0>; + clocks = <&CP110_LABEL(clk) 1 5>, <&CP110_LABEL(clk) 1 6>, + <&CP110_LABEL(clk) 1 18>; + clock-names = "mg_clk", "mg_core_clk", "axi_clk"; #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index ea3d700da3ca..e095334eaec9 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -39,6 +39,12 @@ static const guid_t prp_guids[] = { /* External facing port GUID: efcc06cc-73ac-4bc3-bff0-76143807c389 */ GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3, 0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89), + /* Thunderbolt GUID for IMR_VALID: c44d002f-69f9-4e7d-a904-a7baabdf43f7 */ + GUID_INIT(0xc44d002f, 0x69f9, 0x4e7d, + 0xa9, 0x04, 0xa7, 0xba, 0xab, 0xdf, 0x43, 0xf7), + /* Thunderbolt GUID for WAKE_SUPPORTED: 6c501103-c189-4296-ba72-9bf5a26ebe5d */ + GUID_INIT(0x6c501103, 0xc189, 0x4296, + 0xba, 0x72, 0x9b, 0xf5, 0xa2, 0x6e, 0xbe, 0x5d), }; /* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */ diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index c3fd632af119..a32bfaeb7e61 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ #include <linux/clk-provider.h> @@ -12,23 +12,13 @@ #include <linux/platform_device.h> #include <soc/qcom/cmd-db.h> #include <soc/qcom/rpmh.h> +#include <soc/qcom/tcs.h> #include <dt-bindings/clock/qcom,rpmh.h> #define CLK_RPMH_ARC_EN_OFFSET 0 #define CLK_RPMH_VRM_EN_OFFSET 4 -#define BCM_TCS_CMD_COMMIT_MASK 0x40000000 -#define BCM_TCS_CMD_VALID_SHIFT 29 -#define BCM_TCS_CMD_VOTE_MASK 0x3fff -#define BCM_TCS_CMD_VOTE_SHIFT 0 - -#define BCM_TCS_CMD(valid, vote) \ - (BCM_TCS_CMD_COMMIT_MASK | \ - ((valid) << BCM_TCS_CMD_VALID_SHIFT) | \ - ((vote & BCM_TCS_CMD_VOTE_MASK) \ - << BCM_TCS_CMD_VOTE_SHIFT)) - /** * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM) * @unit: divisor used to convert Hz value to an RPMh msg @@ -269,7 +259,7 @@ static int clk_rpmh_bcm_send_cmd(struct clk_rpmh *c, bool enable) } cmd.addr = c->res_addr; - cmd.data = BCM_TCS_CMD(enable, cmd_state); + cmd.data = BCM_TCS_CMD(1, enable, 0, cmd_state); ret = rpmh_write_async(c->dev, RPMH_ACTIVE_ONLY_STATE, &cmd, 1); if (ret) { diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index ee9b5f70bfa4..ad02dc6747a4 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -140,10 +140,8 @@ static int adc_jack_probe(struct platform_device *pdev) return err; data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); + if (data->irq < 0) return -ENODEV; - } err = request_any_context_irq(data->irq, adc_jack_irq_thread, pdata->irq_flags, pdata->name, data); diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 7e9f4c9ee87d..e970134c95fa 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -1253,7 +1253,7 @@ static int arizona_extcon_get_micd_configs(struct device *dev, int i, j; u32 *vals; - nconfs = device_property_read_u32_array(arizona->dev, prop, NULL, 0); + nconfs = device_property_count_u32(arizona->dev, prop); if (nconfs <= 0) return 0; diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 7254852e6ec0..415afaf479e7 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -121,7 +121,6 @@ static const char * const axp288_pwr_up_down_info[] = { "Last shutdown caused by PMIC UVLO threshold", "Last shutdown caused by SOC initiated cold off", "Last shutdown caused by user pressing the power button", - NULL, }; /* @@ -130,18 +129,21 @@ static const char * const axp288_pwr_up_down_info[] = { */ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info) { - const char * const *rsi; unsigned int val, i, clear_mask = 0; + unsigned long bits; int ret; ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val); - for (i = 0, rsi = axp288_pwr_up_down_info; *rsi; rsi++, i++) { - if (val & BIT(i)) { - dev_dbg(info->dev, "%s\n", *rsi); - clear_mask |= BIT(i); - } + if (ret < 0) { + dev_err(info->dev, "failed to read reset source indicator\n"); + return; } + bits = val & GENMASK(ARRAY_SIZE(axp288_pwr_up_down_info) - 1, 0); + for_each_set_bit(i, &bits, ARRAY_SIZE(axp288_pwr_up_down_info)) + dev_dbg(info->dev, "%s\n", axp288_pwr_up_down_info[i]); + clear_mask = bits; + /* Clear the register value for next reboot (write 1 to clear bit) */ regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask); } diff --git a/drivers/extcon/extcon-fsa9480.c b/drivers/extcon/extcon-fsa9480.c index 350fb34abfa0..8405512f5199 100644 --- a/drivers/extcon/extcon-fsa9480.c +++ b/drivers/extcon/extcon-fsa9480.c @@ -363,6 +363,7 @@ MODULE_DEVICE_TABLE(i2c, fsa9480_id); static const struct of_device_id fsa9480_of_match[] = { { .compatible = "fcs,fsa9480", }, + { .compatible = "fcs,fsa880", }, { }, }; MODULE_DEVICE_TABLE(of, fsa9480_of_match); diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index faddeac948db..c211222f5d0c 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -22,26 +22,22 @@ /** * struct gpio_extcon_data - A simple GPIO-controlled extcon device state container. * @edev: Extcon device. - * @irq: Interrupt line for the external connector. * @work: Work fired by the interrupt. * @debounce_jiffies: Number of jiffies to wait for the GPIO to stabilize, from the debounce * value. * @gpiod: GPIO descriptor for this external connector. * @extcon_id: The unique id of specific external connector. * @debounce: Debounce time for GPIO IRQ in ms. - * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW). * @check_on_resume: Boolean describing whether to check the state of gpio * while resuming from sleep. */ struct gpio_extcon_data { struct extcon_dev *edev; - int irq; struct delayed_work work; unsigned long debounce_jiffies; struct gpio_desc *gpiod; unsigned int extcon_id; unsigned long debounce; - unsigned long irq_flags; bool check_on_resume; }; @@ -69,6 +65,8 @@ static int gpio_extcon_probe(struct platform_device *pdev) { struct gpio_extcon_data *data; struct device *dev = &pdev->dev; + unsigned long irq_flags; + int irq; int ret; data = devm_kzalloc(dev, sizeof(struct gpio_extcon_data), GFP_KERNEL); @@ -82,15 +80,26 @@ static int gpio_extcon_probe(struct platform_device *pdev) * developed to get the extcon id from device-tree or others. * On later, it have to be solved. */ - if (!data->irq_flags || data->extcon_id > EXTCON_NONE) + if (data->extcon_id > EXTCON_NONE) return -EINVAL; data->gpiod = devm_gpiod_get(dev, "extcon", GPIOD_IN); if (IS_ERR(data->gpiod)) return PTR_ERR(data->gpiod); - data->irq = gpiod_to_irq(data->gpiod); - if (data->irq <= 0) - return data->irq; + irq = gpiod_to_irq(data->gpiod); + if (irq <= 0) + return irq; + + /* + * It is unlikely that this is an acknowledged interrupt that goes + * away after handling, what we are looking for are falling edges + * if the signal is active low, and rising edges if the signal is + * active high. + */ + if (gpiod_is_active_low(data->gpiod)) + irq_flags = IRQF_TRIGGER_FALLING; + else + irq_flags = IRQF_TRIGGER_RISING; /* Allocate the memory of extcon devie and register extcon device */ data->edev = devm_extcon_dev_allocate(dev, &data->extcon_id); @@ -109,8 +118,8 @@ static int gpio_extcon_probe(struct platform_device *pdev) * Request the interrupt of gpio to detect whether external connector * is attached or detached. */ - ret = devm_request_any_context_irq(dev, data->irq, - gpio_irq_handler, data->irq_flags, + ret = devm_request_any_context_irq(dev, irq, + gpio_irq_handler, irq_flags, pdev->name, data); if (ret < 0) return ret; diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index a343a6ef3506..e6b50ca83008 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -774,12 +774,12 @@ static int max77843_init_muic_regmap(struct max77693_dev *max77843) { int ret; - max77843->i2c_muic = i2c_new_dummy(max77843->i2c->adapter, + max77843->i2c_muic = i2c_new_dummy_device(max77843->i2c->adapter, I2C_ADDR_MUIC); - if (!max77843->i2c_muic) { + if (IS_ERR(max77843->i2c_muic)) { dev_err(&max77843->i2c->dev, "Cannot allocate I2C device for MUIC\n"); - return -ENOMEM; + return PTR_ERR(max77843->i2c_muic); } i2c_set_clientdata(max77843->i2c_muic, max77843); diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index 98e4f616b8f1..dc43847ad2b0 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -597,7 +597,7 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c, ret = devm_request_threaded_irq(info->dev, virq, NULL, sm5502_muic_irq_handler, - IRQF_NO_SUSPEND, + IRQF_NO_SUSPEND | IRQF_ONESHOT, muic_irq->name, info); if (ret) { dev_err(info->dev, diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 2463aa7ab4f6..96544b348c27 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -646,24 +646,23 @@ static int debug_remove(struct amba_device *adev) return 0; } +static const struct amba_cs_uci_id uci_id_debug[] = { + { + /* CPU Debug UCI data */ + .devarch = 0x47706a15, + .devarch_mask = 0xfff0ffff, + .devtype = 0x00000015, + } +}; + static const struct amba_id debug_ids[] = { - { /* Debug for Cortex-A53 */ - .id = 0x000bbd03, - .mask = 0x000fffff, - }, - { /* Debug for Cortex-A57 */ - .id = 0x000bbd07, - .mask = 0x000fffff, - }, - { /* Debug for Cortex-A72 */ - .id = 0x000bbd08, - .mask = 0x000fffff, - }, - { /* Debug for Cortex-A73 */ - .id = 0x000bbd09, - .mask = 0x000fffff, - }, - { 0, 0 }, + CS_AMBA_ID(0x000bbd03), /* Cortex-A53 */ + CS_AMBA_ID(0x000bbd07), /* Cortex-A57 */ + CS_AMBA_ID(0x000bbd08), /* Cortex-A72 */ + CS_AMBA_ID(0x000bbd09), /* Cortex-A73 */ + CS_AMBA_UCI_ID(0x000f0205, uci_id_debug), /* Qualcomm Kryo */ + CS_AMBA_UCI_ID(0x000f0211, uci_id_debug), /* Qualcomm Kryo */ + {}, }; static struct amba_driver debug_driver = { diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index a0365e23678e..219c10eb752c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -296,11 +296,8 @@ static ssize_t mode_store(struct device *dev, spin_lock(&drvdata->spinlock); config->mode = val & ETMv4_MODE_ALL; - - if (config->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); + etm4_set_mode_exclude(drvdata, + config->mode & ETM_MODE_EXCLUDE ? true : false); if (drvdata->instrp0 == true) { /* start by clearing instruction P0 field */ @@ -999,10 +996,8 @@ static ssize_t addr_range_store(struct device *dev, * Program include or exclude control bits for vinst or vdata * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE */ - if (config->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); + etm4_set_mode_exclude(drvdata, + config->mode & ETM_MODE_EXCLUDE ? true : false); spin_unlock(&drvdata->spinlock); return size; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 7bcac8896fc1..a128b5063f46 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,7 +34,8 @@ #include "coresight-etm-perf.h" static int boot_enable; -module_param_named(boot_enable, boot_enable, int, S_IRUGO); +module_param(boot_enable, int, 0444); +MODULE_PARM_DESC(boot_enable, "Enable tracing on boot"); /* The number of ETMv4 currently registered */ static int etm4_count; @@ -47,7 +48,7 @@ static enum cpuhp_state hp_online; static void etm4_os_unlock(struct etmv4_drvdata *drvdata) { - /* Writing any value to ETMOSLAR unlocks the trace registers */ + /* Writing 0 to TRCOSLAR unlocks the trace registers */ writel_relaxed(0x0, drvdata->base + TRCOSLAR); drvdata->os_unlock = true; isb(); @@ -188,6 +189,13 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) dev_err(etm_dev, "timeout while waiting for Idle Trace Status\n"); + /* + * As recommended by section 4.3.7 ("Synchronization when using the + * memory-mapped interface") of ARM IHI 0064D + */ + dsb(sy); + isb(); + done: CS_LOCK(drvdata->base); @@ -453,8 +461,12 @@ static void etm4_disable_hw(void *info) /* EN, bit[0] Trace unit enable bit */ control &= ~0x1; - /* make sure everything completes before disabling */ - mb(); + /* + * Make sure everything completes before disabling, as recommended + * by section 7.3.77 ("TRCVICTLR, ViewInst Main Control Register, + * SSTATUS") of ARM IHI 0064D + */ + dsb(sy); isb(); writel_relaxed(control, drvdata->base + TRCPRGCTLR); @@ -1047,10 +1059,8 @@ static int etm4_starting_cpu(unsigned int cpu) return 0; spin_lock(&etmdrvdata[cpu]->spinlock); - if (!etmdrvdata[cpu]->os_unlock) { + if (!etmdrvdata[cpu]->os_unlock) etm4_os_unlock(etmdrvdata[cpu]); - etmdrvdata[cpu]->os_unlock = true; - } if (local_read(&etmdrvdata[cpu]->mode)) etm4_enable_hw(etmdrvdata[cpu]); @@ -1192,11 +1202,15 @@ static struct amba_cs_uci_id uci_id_etm4[] = { }; static const struct amba_id etm4_ids[] = { - CS_AMBA_ID(0x000bb95d), /* Cortex-A53 */ - CS_AMBA_ID(0x000bb95e), /* Cortex-A57 */ - CS_AMBA_ID(0x000bb95a), /* Cortex-A72 */ - CS_AMBA_ID(0x000bb959), /* Cortex-A73 */ - CS_AMBA_UCI_ID(0x000bb9da, uci_id_etm4), /* Cortex-A35 */ + CS_AMBA_ID(0x000bb95d), /* Cortex-A53 */ + CS_AMBA_ID(0x000bb95e), /* Cortex-A57 */ + CS_AMBA_ID(0x000bb95a), /* Cortex-A72 */ + CS_AMBA_ID(0x000bb959), /* Cortex-A73 */ + CS_AMBA_UCI_ID(0x000bb9da, uci_id_etm4),/* Cortex-A35 */ + CS_AMBA_UCI_ID(0x000f0205, uci_id_etm4),/* Qualcomm Kryo */ + CS_AMBA_UCI_ID(0x000f0211, uci_id_etm4),/* Qualcomm Kryo */ + CS_AMBA_ID(0x000bb802), /* Qualcomm Kryo 385 Cortex-A55 */ + CS_AMBA_ID(0x000bb803), /* Qualcomm Kryo 385 Cortex-A75 */ {}, }; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index fa97cb9ab4f9..05f7896c3a01 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -5,6 +5,7 @@ * Description: CoreSight Funnel driver */ +#include <linux/acpi.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> @@ -192,7 +193,7 @@ static int funnel_probe(struct device *dev, struct resource *res) if (is_of_node(dev_fwnode(dev)) && of_device_is_compatible(dev->of_node, "arm,coresight-funnel")) - pr_warn_once("Uses OBSOLETE CoreSight funnel binding\n"); + dev_warn_once(dev, "Uses OBSOLETE CoreSight funnel binding\n"); desc.name = coresight_alloc_device_name(&funnel_devs, dev); if (!desc.name) @@ -302,11 +303,19 @@ static const struct of_device_id static_funnel_match[] = { {} }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id static_funnel_ids[] = { + {"ARMHC9FE", 0}, + {}, +}; +#endif + static struct platform_driver static_funnel_driver = { .probe = static_funnel_probe, .driver = { .name = "coresight-static-funnel", .of_match_table = static_funnel_match, + .acpi_match_table = ACPI_PTR(static_funnel_ids), .pm = &funnel_dev_pm_ops, .suppress_bind_attrs = true, }, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 7d401790dd7e..41ae5863104d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -185,11 +185,11 @@ static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } } /* coresight AMBA ID, full UCI structure: id table entry. */ -#define CS_AMBA_UCI_ID(pid, uci_ptr) \ - { \ - .id = pid, \ - .mask = 0x000fffff, \ - .data = uci_ptr \ +#define CS_AMBA_UCI_ID(pid, uci_ptr) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + .data = (void *)uci_ptr \ } /* extract the data value from a UCI structure given amba_id pointer. */ diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b7d6d59d56db..b29ba640eb25 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -184,7 +184,8 @@ static int replicator_probe(struct device *dev, struct resource *res) if (is_of_node(dev_fwnode(dev)) && of_device_is_compatible(dev->of_node, "arm,coresight-replicator")) - pr_warn_once("Uses OBSOLETE CoreSight replicator binding\n"); + dev_warn_once(dev, + "Uses OBSOLETE CoreSight replicator binding\n"); desc.name = coresight_alloc_device_name(&replicator_devs, dev); if (!desc.name) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 23b7ff00af5c..807416b75ecc 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -479,30 +479,11 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, * traces. */ if (!buf->snapshot && to_read > handle->size) { - u32 mask = 0; - - /* - * The value written to RRP must be byte-address aligned to - * the width of the trace memory databus _and_ to a frame - * boundary (16 byte), whichever is the biggest. For example, - * for 32-bit, 64-bit and 128-bit wide trace memory, the four - * LSBs must be 0s. For 256-bit wide trace memory, the five - * LSBs must be 0s. - */ - switch (drvdata->memwidth) { - case TMC_MEM_INTF_WIDTH_32BITS: - case TMC_MEM_INTF_WIDTH_64BITS: - case TMC_MEM_INTF_WIDTH_128BITS: - mask = GENMASK(31, 4); - break; - case TMC_MEM_INTF_WIDTH_256BITS: - mask = GENMASK(31, 5); - break; - } + u32 mask = tmc_get_memwidth_mask(drvdata); /* * Make sure the new size is aligned in accordance with the - * requirement explained above. + * requirement explained in function tmc_get_memwidth_mask(). */ to_read = handle->size & mask; /* Move the RAM read pointer up */ diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 17006705287a..625882bc8b08 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -871,6 +871,7 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata, return ERR_PTR(rc); } + refcount_set(&etr_buf->refcount, 1); dev_dbg(dev, "allocated buffer of size %ldKB in mode %d\n", (unsigned long)size >> 10, etr_buf->mode); return etr_buf; @@ -927,15 +928,24 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) rrp = tmc_read_rrp(drvdata); rwp = tmc_read_rwp(drvdata); status = readl_relaxed(drvdata->base + TMC_STS); + + /* + * If there were memory errors in the session, truncate the + * buffer. + */ + if (WARN_ON_ONCE(status & TMC_STS_MEMERR)) { + dev_dbg(&drvdata->csdev->dev, + "tmc memory error detected, truncating buffer\n"); + etr_buf->len = 0; + etr_buf->full = 0; + return; + } + etr_buf->full = status & TMC_STS_FULL; WARN_ON(!etr_buf->ops || !etr_buf->ops->sync); etr_buf->ops->sync(etr_buf, rrp, rwp); - - /* Insert barrier packets at the beginning, if there was an overflow */ - if (etr_buf->full) - tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); } static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) @@ -1072,6 +1082,13 @@ static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata) drvdata->sysfs_buf = NULL; } else { tmc_sync_etr_buf(drvdata); + /* + * Insert barrier packets at the beginning, if there was + * an overflow. + */ + if (etr_buf->full) + tmc_etr_buf_insert_barrier_packet(etr_buf, + etr_buf->offset); } } @@ -1263,8 +1280,6 @@ retry: if (IS_ERR(etr_buf)) return etr_buf; - refcount_set(&etr_buf->refcount, 1); - /* Now that we have a buffer, add it to the IDR. */ mutex_lock(&drvdata->idr_mutex); ret = idr_alloc(&drvdata->idr, etr_buf, pid, pid + 1, GFP_KERNEL); @@ -1291,19 +1306,11 @@ get_perf_etr_buf_per_thread(struct tmc_drvdata *drvdata, struct perf_event *event, int nr_pages, void **pages, bool snapshot) { - struct etr_buf *etr_buf; - /* * In per-thread mode the etr_buf isn't shared, so just go ahead * with memory allocation. */ - etr_buf = alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot); - if (IS_ERR(etr_buf)) - goto out; - - refcount_set(&etr_buf->refcount, 1); -out: - return etr_buf; + return alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot); } static struct etr_buf * @@ -1410,10 +1417,12 @@ free_etr_perf_buffer: * tmc_etr_sync_perf_buffer: Copy the actual trace data from the hardware * buffer to the perf ring buffer. */ -static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) +static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf, + unsigned long src_offset, + unsigned long to_copy) { - long bytes, to_copy; - long pg_idx, pg_offset, src_offset; + long bytes; + long pg_idx, pg_offset; unsigned long head = etr_perf->head; char **dst_pages, *src_buf; struct etr_buf *etr_buf = etr_perf->etr_buf; @@ -1422,8 +1431,6 @@ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) pg_idx = head >> PAGE_SHIFT; pg_offset = head & (PAGE_SIZE - 1); dst_pages = (char **)etr_perf->pages; - src_offset = etr_buf->offset; - to_copy = etr_buf->len; while (to_copy > 0) { /* @@ -1434,6 +1441,8 @@ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) * 3) what is available in the destination page. * in one iteration. */ + if (src_offset >= etr_buf->size) + src_offset -= etr_buf->size; bytes = tmc_etr_buf_get_data(etr_buf, src_offset, to_copy, &src_buf); if (WARN_ON_ONCE(bytes <= 0)) @@ -1454,8 +1463,6 @@ static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) /* Move source pointers */ src_offset += bytes; - if (src_offset >= etr_buf->size) - src_offset -= etr_buf->size; } } @@ -1471,7 +1478,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev, void *config) { bool lost = false; - unsigned long flags, size = 0; + unsigned long flags, offset, size = 0; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etr_perf_buffer *etr_perf = config; struct etr_buf *etr_buf = etr_perf->etr_buf; @@ -1484,7 +1491,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev, goto out; } - if (WARN_ON(drvdata->perf_data != etr_perf)) { + if (WARN_ON(drvdata->perf_buf != etr_buf)) { lost = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); goto out; @@ -1496,12 +1503,38 @@ tmc_update_etr_buffer(struct coresight_device *csdev, tmc_sync_etr_buf(drvdata); CS_LOCK(drvdata->base); - /* Reset perf specific data */ - drvdata->perf_data = NULL; spin_unlock_irqrestore(&drvdata->spinlock, flags); + lost = etr_buf->full; + offset = etr_buf->offset; size = etr_buf->len; - tmc_etr_sync_perf_buffer(etr_perf); + + /* + * The ETR buffer may be bigger than the space available in the + * perf ring buffer (handle->size). If so advance the offset so that we + * get the latest trace data. In snapshot mode none of that matters + * since we are expected to clobber stale data in favour of the latest + * traces. + */ + if (!etr_perf->snapshot && size > handle->size) { + u32 mask = tmc_get_memwidth_mask(drvdata); + + /* + * Make sure the new size is aligned in accordance with the + * requirement explained in function tmc_get_memwidth_mask(). + */ + size = handle->size & mask; + offset = etr_buf->offset + etr_buf->len - size; + + if (offset >= etr_buf->size) + offset -= etr_buf->size; + lost = true; + } + + /* Insert barrier packets at the beginning, if there was an overflow */ + if (lost) + tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); + tmc_etr_sync_perf_buffer(etr_perf, offset, size); /* * In snapshot mode we simply increment the head by the number of byte @@ -1511,8 +1544,6 @@ tmc_update_etr_buffer(struct coresight_device *csdev, */ if (etr_perf->snapshot) handle->head += size; - - lost |= etr_buf->full; out: /* * Don't set the TRUNCATED flag in snapshot mode because 1) the @@ -1556,7 +1587,6 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) } etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf); - drvdata->perf_data = etr_perf; /* * No HW configuration is needed if the sink is already in @@ -1572,6 +1602,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) /* Associate with monitored process. */ drvdata->pid = pid; drvdata->mode = CS_MODE_PERF; + drvdata->perf_buf = etr_perf->etr_buf; atomic_inc(csdev->refcnt); } @@ -1617,6 +1648,8 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev) /* Dissociate from monitored process. */ drvdata->pid = -1; drvdata->mode = CS_MODE_DISABLED; + /* Reset perf specific data */ + drvdata->perf_buf = NULL; spin_unlock_irqrestore(&drvdata->spinlock, flags); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index be37aff573b4..1cf82fa58289 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -70,6 +70,34 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata) writel_relaxed(0x0, drvdata->base + TMC_CTL); } +u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata) +{ + u32 mask = 0; + + /* + * When moving RRP or an offset address forward, the new values must + * be byte-address aligned to the width of the trace memory databus + * _and_ to a frame boundary (16 byte), whichever is the biggest. For + * example, for 32-bit, 64-bit and 128-bit wide trace memory, the four + * LSBs must be 0s. For 256-bit wide trace memory, the five LSBs must + * be 0s. + */ + switch (drvdata->memwidth) { + case TMC_MEM_INTF_WIDTH_32BITS: + /* fallthrough */ + case TMC_MEM_INTF_WIDTH_64BITS: + /* fallthrough */ + case TMC_MEM_INTF_WIDTH_128BITS: + mask = GENMASK(31, 4); + break; + case TMC_MEM_INTF_WIDTH_256BITS: + mask = GENMASK(31, 5); + break; + } + + return mask; +} + static int tmc_read_prepare(struct tmc_drvdata *drvdata) { int ret = 0; @@ -236,6 +264,7 @@ coresight_tmc_reg(ffcr, TMC_FFCR); coresight_tmc_reg(mode, TMC_MODE); coresight_tmc_reg(pscr, TMC_PSCR); coresight_tmc_reg(axictl, TMC_AXICTL); +coresight_tmc_reg(authstatus, TMC_AUTHSTATUS); coresight_tmc_reg(devid, CORESIGHT_DEVID); coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); @@ -255,6 +284,7 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = { &dev_attr_devid.attr, &dev_attr_dba.attr, &dev_attr_axictl.attr, + &dev_attr_authstatus.attr, NULL, }; @@ -342,6 +372,13 @@ static inline bool tmc_etr_can_use_sg(struct device *dev) return fwnode_property_present(dev->fwnode, "arm,scatter-gather"); } +static inline bool tmc_etr_has_non_secure_access(struct tmc_drvdata *drvdata) +{ + u32 auth = readl_relaxed(drvdata->base + TMC_AUTHSTATUS); + + return (auth & TMC_AUTH_NSID_MASK) == 0x3; +} + /* Detect and initialise the capabilities of a TMC ETR */ static int tmc_etr_setup_caps(struct device *parent, u32 devid, void *dev_caps) { @@ -349,6 +386,9 @@ static int tmc_etr_setup_caps(struct device *parent, u32 devid, void *dev_caps) u32 dma_mask = 0; struct tmc_drvdata *drvdata = dev_get_drvdata(parent); + if (!tmc_etr_has_non_secure_access(drvdata)) + return -EACCES; + /* Set the unadvertised capabilities */ tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 1ed50411cc3c..71de978575f3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -39,6 +39,7 @@ #define TMC_ITATBCTR2 0xef0 #define TMC_ITATBCTR1 0xef4 #define TMC_ITATBCTR0 0xef8 +#define TMC_AUTHSTATUS 0xfb8 /* register description */ /* TMC_CTL - 0x020 */ @@ -47,6 +48,7 @@ #define TMC_STS_TMCREADY_BIT 2 #define TMC_STS_FULL BIT(0) #define TMC_STS_TRIGGERED BIT(1) +#define TMC_STS_MEMERR BIT(5) /* * TMC_AXICTL - 0x110 * @@ -89,6 +91,8 @@ #define TMC_DEVID_AXIAW_SHIFT 17 #define TMC_DEVID_AXIAW_MASK 0x7f +#define TMC_AUTH_NSID_MASK GENMASK(1, 0) + enum tmc_config_type { TMC_CONFIG_TYPE_ETB, TMC_CONFIG_TYPE_ETR, @@ -178,8 +182,8 @@ struct etr_buf { * device configuration register (DEVID) * @idr: Holds etr_bufs allocated for this ETR. * @idr_mutex: Access serialisation for idr. - * @perf_data: PERF buffer for ETR. - * @sysfs_data: SYSFS buffer for ETR. + * @sysfs_buf: SYSFS buffer for ETR. + * @perf_buf: PERF buffer for ETR. */ struct tmc_drvdata { void __iomem *base; @@ -202,7 +206,7 @@ struct tmc_drvdata { struct idr idr; struct mutex idr_mutex; struct etr_buf *sysfs_buf; - void *perf_data; + struct etr_buf *perf_buf; }; struct etr_buf_operations { @@ -251,6 +255,7 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata); void tmc_flush_and_stop(struct tmc_drvdata *drvdata); void tmc_enable_hw(struct tmc_drvdata *drvdata); void tmc_disable_hw(struct tmc_drvdata *drvdata); +u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata); /* ETB/ETF functions */ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata); diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 871eb4bc4efc..7b971228df38 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -29,6 +29,7 @@ static struct dentry *icc_debugfs_dir; * @req_node: entry in list of requests for the particular @node * @node: the interconnect node to which this constraint applies * @dev: reference to the device that sets the constraints + * @tag: path tag (optional) * @avg_bw: an integer describing the average bandwidth in kBps * @peak_bw: an integer describing the peak bandwidth in kBps */ @@ -36,6 +37,7 @@ struct icc_req { struct hlist_node req_node; struct icc_node *node; struct device *dev; + u32 tag; u32 avg_bw; u32 peak_bw; }; @@ -203,8 +205,11 @@ static int aggregate_requests(struct icc_node *node) node->avg_bw = 0; node->peak_bw = 0; + if (p->pre_aggregate) + p->pre_aggregate(node); + hlist_for_each_entry(r, &node->req_list, req_node) - p->aggregate(node, r->avg_bw, r->peak_bw, + p->aggregate(node, r->tag, r->avg_bw, r->peak_bw, &node->avg_bw, &node->peak_bw); return 0; @@ -386,6 +391,26 @@ struct icc_path *of_icc_get(struct device *dev, const char *name) EXPORT_SYMBOL_GPL(of_icc_get); /** + * icc_set_tag() - set an optional tag on a path + * @path: the path we want to tag + * @tag: the tag value + * + * This function allows consumers to append a tag to the requests associated + * with a path, so that a different aggregation could be done based on this tag. + */ +void icc_set_tag(struct icc_path *path, u32 tag) +{ + int i; + + if (!path) + return; + + for (i = 0; i < path->num_nodes; i++) + path->reqs[i].tag = tag; +} +EXPORT_SYMBOL_GPL(icc_set_tag); + +/** * icc_set_bw() - set bandwidth constraints on an interconnect path * @path: reference to the path returned by icc_get() * @avg_bw: average bandwidth in kilobytes per second diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index d5e70ebc2410..6ab4012a059a 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -5,6 +5,15 @@ config INTERCONNECT_QCOM help Support for Qualcomm's Network-on-Chip interconnect hardware. +config INTERCONNECT_QCOM_QCS404 + tristate "Qualcomm QCS404 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on qcs404-based + platforms. + config INTERCONNECT_QCOM_SDM845 tristate "Qualcomm SDM845 interconnect driver" depends on INTERCONNECT_QCOM @@ -12,3 +21,6 @@ config INTERCONNECT_QCOM_SDM845 help This is a driver for the Qualcomm Network-on-Chip on sdm845-based platforms. + +config INTERCONNECT_QCOM_SMD_RPM + tristate diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 1c1cea690f92..67dafb783dec 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -1,5 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 +qnoc-qcs404-objs := qcs404.o qnoc-sdm845-objs := sdm845.o +icc-smd-rpm-objs := smd-rpm.o +obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o +obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o diff --git a/drivers/interconnect/qcom/qcs404.c b/drivers/interconnect/qcom/qcs404.c new file mode 100644 index 000000000000..910081d6ddc0 --- /dev/null +++ b/drivers/interconnect/qcom/qcs404.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Ltd + */ + +#include <dt-bindings/interconnect/qcom,qcs404.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/interconnect-provider.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "smd-rpm.h" + +#define RPM_BUS_MASTER_REQ 0x73616d62 +#define RPM_BUS_SLAVE_REQ 0x766c7362 + +enum { + QCS404_MASTER_AMPSS_M0 = 1, + QCS404_MASTER_GRAPHICS_3D, + QCS404_MASTER_MDP_PORT0, + QCS404_SNOC_BIMC_1_MAS, + QCS404_MASTER_TCU_0, + QCS404_MASTER_SPDM, + QCS404_MASTER_BLSP_1, + QCS404_MASTER_BLSP_2, + QCS404_MASTER_XM_USB_HS1, + QCS404_MASTER_CRYPTO_CORE0, + QCS404_MASTER_SDCC_1, + QCS404_MASTER_SDCC_2, + QCS404_SNOC_PNOC_MAS, + QCS404_MASTER_QPIC, + QCS404_MASTER_QDSS_BAM, + QCS404_BIMC_SNOC_MAS, + QCS404_PNOC_SNOC_MAS, + QCS404_MASTER_QDSS_ETR, + QCS404_MASTER_EMAC, + QCS404_MASTER_PCIE, + QCS404_MASTER_USB3, + QCS404_PNOC_INT_0, + QCS404_PNOC_INT_2, + QCS404_PNOC_INT_3, + QCS404_PNOC_SLV_0, + QCS404_PNOC_SLV_1, + QCS404_PNOC_SLV_2, + QCS404_PNOC_SLV_3, + QCS404_PNOC_SLV_4, + QCS404_PNOC_SLV_6, + QCS404_PNOC_SLV_7, + QCS404_PNOC_SLV_8, + QCS404_PNOC_SLV_9, + QCS404_PNOC_SLV_10, + QCS404_PNOC_SLV_11, + QCS404_SNOC_QDSS_INT, + QCS404_SNOC_INT_0, + QCS404_SNOC_INT_1, + QCS404_SNOC_INT_2, + QCS404_SLAVE_EBI_CH0, + QCS404_BIMC_SNOC_SLV, + QCS404_SLAVE_SPDM_WRAPPER, + QCS404_SLAVE_PDM, + QCS404_SLAVE_PRNG, + QCS404_SLAVE_TCSR, + QCS404_SLAVE_SNOC_CFG, + QCS404_SLAVE_MESSAGE_RAM, + QCS404_SLAVE_DISPLAY_CFG, + QCS404_SLAVE_GRAPHICS_3D_CFG, + QCS404_SLAVE_BLSP_1, + QCS404_SLAVE_TLMM_NORTH, + QCS404_SLAVE_PCIE_1, + QCS404_SLAVE_EMAC_CFG, + QCS404_SLAVE_BLSP_2, + QCS404_SLAVE_TLMM_EAST, + QCS404_SLAVE_TCU, + QCS404_SLAVE_PMIC_ARB, + QCS404_SLAVE_SDCC_1, + QCS404_SLAVE_SDCC_2, + QCS404_SLAVE_TLMM_SOUTH, + QCS404_SLAVE_USB_HS, + QCS404_SLAVE_USB3, + QCS404_SLAVE_CRYPTO_0_CFG, + QCS404_PNOC_SNOC_SLV, + QCS404_SLAVE_APPSS, + QCS404_SLAVE_WCSS, + QCS404_SNOC_BIMC_1_SLV, + QCS404_SLAVE_OCIMEM, + QCS404_SNOC_PNOC_SLV, + QCS404_SLAVE_QDSS_STM, + QCS404_SLAVE_CATS_128, + QCS404_SLAVE_OCMEM_64, + QCS404_SLAVE_LPASS, +}; + +#define to_qcom_provider(_provider) \ + container_of(_provider, struct qcom_icc_provider, provider) + +static const struct clk_bulk_data bus_clocks[] = { + { .id = "bus" }, + { .id = "bus_a" }, +}; + +/** + * struct qcom_icc_provider - Qualcomm specific interconnect provider + * @provider: generic interconnect provider + * @bus_clks: the clk_bulk_data table of bus clocks + * @num_clks: the total number of clk_bulk_data entries + */ +struct qcom_icc_provider { + struct icc_provider provider; + struct clk_bulk_data *bus_clks; + int num_clks; +}; + +#define QCS404_MAX_LINKS 12 + +/** + * struct qcom_icc_node - Qualcomm specific interconnect nodes + * @name: the node name used in debugfs + * @id: a unique node identifier + * @links: an array of nodes where we can go next while traversing + * @num_links: the total number of @links + * @buswidth: width of the interconnect between a node and the bus (bytes) + * @mas_rpm_id: RPM id for devices that are bus masters + * @slv_rpm_id: RPM id for devices that are bus slaves + * @rate: current bus clock rate in Hz + */ +struct qcom_icc_node { + unsigned char *name; + u16 id; + u16 links[QCS404_MAX_LINKS]; + u16 num_links; + u16 buswidth; + int mas_rpm_id; + int slv_rpm_id; + u64 rate; +}; + +struct qcom_icc_desc { + struct qcom_icc_node **nodes; + size_t num_nodes; +}; + +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ + ...) \ + static struct qcom_icc_node _name = { \ + .name = #_name, \ + .id = _id, \ + .buswidth = _buswidth, \ + .mas_rpm_id = _mas_rpm_id, \ + .slv_rpm_id = _slv_rpm_id, \ + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ + .links = { __VA_ARGS__ }, \ + } + +DEFINE_QNODE(mas_apps_proc, QCS404_MASTER_AMPSS_M0, 8, 0, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); +DEFINE_QNODE(mas_oxili, QCS404_MASTER_GRAPHICS_3D, 8, 6, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); +DEFINE_QNODE(mas_mdp, QCS404_MASTER_MDP_PORT0, 8, 8, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); +DEFINE_QNODE(mas_snoc_bimc_1, QCS404_SNOC_BIMC_1_MAS, 8, 76, -1, QCS404_SLAVE_EBI_CH0); +DEFINE_QNODE(mas_tcu_0, QCS404_MASTER_TCU_0, 8, -1, -1, QCS404_SLAVE_EBI_CH0, QCS404_BIMC_SNOC_SLV); +DEFINE_QNODE(mas_spdm, QCS404_MASTER_SPDM, 4, -1, -1, QCS404_PNOC_INT_3); +DEFINE_QNODE(mas_blsp_1, QCS404_MASTER_BLSP_1, 4, 41, -1, QCS404_PNOC_INT_3); +DEFINE_QNODE(mas_blsp_2, QCS404_MASTER_BLSP_2, 4, 39, -1, QCS404_PNOC_INT_3); +DEFINE_QNODE(mas_xi_usb_hs1, QCS404_MASTER_XM_USB_HS1, 8, 138, -1, QCS404_PNOC_INT_0); +DEFINE_QNODE(mas_crypto, QCS404_MASTER_CRYPTO_CORE0, 8, 23, -1, QCS404_PNOC_SNOC_SLV, QCS404_PNOC_INT_2); +DEFINE_QNODE(mas_sdcc_1, QCS404_MASTER_SDCC_1, 8, 33, -1, QCS404_PNOC_INT_0); +DEFINE_QNODE(mas_sdcc_2, QCS404_MASTER_SDCC_2, 8, 35, -1, QCS404_PNOC_INT_0); +DEFINE_QNODE(mas_snoc_pcnoc, QCS404_SNOC_PNOC_MAS, 8, 77, -1, QCS404_PNOC_INT_2); +DEFINE_QNODE(mas_qpic, QCS404_MASTER_QPIC, 4, -1, -1, QCS404_PNOC_INT_0); +DEFINE_QNODE(mas_qdss_bam, QCS404_MASTER_QDSS_BAM, 4, -1, -1, QCS404_SNOC_QDSS_INT); +DEFINE_QNODE(mas_bimc_snoc, QCS404_BIMC_SNOC_MAS, 8, 21, -1, QCS404_SLAVE_OCMEM_64, QCS404_SLAVE_CATS_128, QCS404_SNOC_INT_0, QCS404_SNOC_INT_1); +DEFINE_QNODE(mas_pcnoc_snoc, QCS404_PNOC_SNOC_MAS, 8, 29, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_2, QCS404_SNOC_INT_0); +DEFINE_QNODE(mas_qdss_etr, QCS404_MASTER_QDSS_ETR, 8, -1, -1, QCS404_SNOC_QDSS_INT); +DEFINE_QNODE(mas_emac, QCS404_MASTER_EMAC, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); +DEFINE_QNODE(mas_pcie, QCS404_MASTER_PCIE, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); +DEFINE_QNODE(mas_usb3, QCS404_MASTER_USB3, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); +DEFINE_QNODE(pcnoc_int_0, QCS404_PNOC_INT_0, 8, 85, 114, QCS404_PNOC_SNOC_SLV, QCS404_PNOC_INT_2); +DEFINE_QNODE(pcnoc_int_2, QCS404_PNOC_INT_2, 8, 124, 184, QCS404_PNOC_SLV_10, QCS404_SLAVE_TCU, QCS404_PNOC_SLV_11, QCS404_PNOC_SLV_2, QCS404_PNOC_SLV_3, QCS404_PNOC_SLV_0, QCS404_PNOC_SLV_1, QCS404_PNOC_SLV_6, QCS404_PNOC_SLV_7, QCS404_PNOC_SLV_4, QCS404_PNOC_SLV_8, QCS404_PNOC_SLV_9); +DEFINE_QNODE(pcnoc_int_3, QCS404_PNOC_INT_3, 8, 125, 185, QCS404_PNOC_SNOC_SLV); +DEFINE_QNODE(pcnoc_s_0, QCS404_PNOC_SLV_0, 4, 89, 118, QCS404_SLAVE_PRNG, QCS404_SLAVE_SPDM_WRAPPER, QCS404_SLAVE_PDM); +DEFINE_QNODE(pcnoc_s_1, QCS404_PNOC_SLV_1, 4, 90, 119, QCS404_SLAVE_TCSR); +DEFINE_QNODE(pcnoc_s_2, QCS404_PNOC_SLV_2, 4, -1, -1, QCS404_SLAVE_GRAPHICS_3D_CFG); +DEFINE_QNODE(pcnoc_s_3, QCS404_PNOC_SLV_3, 4, 92, 121, QCS404_SLAVE_MESSAGE_RAM); +DEFINE_QNODE(pcnoc_s_4, QCS404_PNOC_SLV_4, 4, 93, 122, QCS404_SLAVE_SNOC_CFG); +DEFINE_QNODE(pcnoc_s_6, QCS404_PNOC_SLV_6, 4, 94, 123, QCS404_SLAVE_BLSP_1, QCS404_SLAVE_TLMM_NORTH, QCS404_SLAVE_EMAC_CFG); +DEFINE_QNODE(pcnoc_s_7, QCS404_PNOC_SLV_7, 4, 95, 124, QCS404_SLAVE_TLMM_SOUTH, QCS404_SLAVE_DISPLAY_CFG, QCS404_SLAVE_SDCC_1, QCS404_SLAVE_PCIE_1, QCS404_SLAVE_SDCC_2); +DEFINE_QNODE(pcnoc_s_8, QCS404_PNOC_SLV_8, 4, 96, 125, QCS404_SLAVE_CRYPTO_0_CFG); +DEFINE_QNODE(pcnoc_s_9, QCS404_PNOC_SLV_9, 4, 97, 126, QCS404_SLAVE_BLSP_2, QCS404_SLAVE_TLMM_EAST, QCS404_SLAVE_PMIC_ARB); +DEFINE_QNODE(pcnoc_s_10, QCS404_PNOC_SLV_10, 4, 157, -1, QCS404_SLAVE_USB_HS); +DEFINE_QNODE(pcnoc_s_11, QCS404_PNOC_SLV_11, 4, 158, 246, QCS404_SLAVE_USB3); +DEFINE_QNODE(qdss_int, QCS404_SNOC_QDSS_INT, 8, -1, -1, QCS404_SNOC_BIMC_1_SLV, QCS404_SNOC_INT_1); +DEFINE_QNODE(snoc_int_0, QCS404_SNOC_INT_0, 8, 99, 130, QCS404_SLAVE_LPASS, QCS404_SLAVE_APPSS, QCS404_SLAVE_WCSS); +DEFINE_QNODE(snoc_int_1, QCS404_SNOC_INT_1, 8, 100, 131, QCS404_SNOC_PNOC_SLV, QCS404_SNOC_INT_2); +DEFINE_QNODE(snoc_int_2, QCS404_SNOC_INT_2, 8, 134, 197, QCS404_SLAVE_QDSS_STM, QCS404_SLAVE_OCIMEM); +DEFINE_QNODE(slv_ebi, QCS404_SLAVE_EBI_CH0, 8, -1, 0, 0); +DEFINE_QNODE(slv_bimc_snoc, QCS404_BIMC_SNOC_SLV, 8, -1, 2, QCS404_BIMC_SNOC_MAS); +DEFINE_QNODE(slv_spdm, QCS404_SLAVE_SPDM_WRAPPER, 4, -1, -1, 0); +DEFINE_QNODE(slv_pdm, QCS404_SLAVE_PDM, 4, -1, 41, 0); +DEFINE_QNODE(slv_prng, QCS404_SLAVE_PRNG, 4, -1, 44, 0); +DEFINE_QNODE(slv_tcsr, QCS404_SLAVE_TCSR, 4, -1, 50, 0); +DEFINE_QNODE(slv_snoc_cfg, QCS404_SLAVE_SNOC_CFG, 4, -1, 70, 0); +DEFINE_QNODE(slv_message_ram, QCS404_SLAVE_MESSAGE_RAM, 4, -1, 55, 0); +DEFINE_QNODE(slv_disp_ss_cfg, QCS404_SLAVE_DISPLAY_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_gpu_cfg, QCS404_SLAVE_GRAPHICS_3D_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_blsp_1, QCS404_SLAVE_BLSP_1, 4, -1, 39, 0); +DEFINE_QNODE(slv_tlmm_north, QCS404_SLAVE_TLMM_NORTH, 4, -1, 214, 0); +DEFINE_QNODE(slv_pcie, QCS404_SLAVE_PCIE_1, 4, -1, -1, 0); +DEFINE_QNODE(slv_ethernet, QCS404_SLAVE_EMAC_CFG, 4, -1, -1, 0); +DEFINE_QNODE(slv_blsp_2, QCS404_SLAVE_BLSP_2, 4, -1, 37, 0); +DEFINE_QNODE(slv_tlmm_east, QCS404_SLAVE_TLMM_EAST, 4, -1, 213, 0); +DEFINE_QNODE(slv_tcu, QCS404_SLAVE_TCU, 8, -1, -1, 0); +DEFINE_QNODE(slv_pmic_arb, QCS404_SLAVE_PMIC_ARB, 4, -1, 59, 0); +DEFINE_QNODE(slv_sdcc_1, QCS404_SLAVE_SDCC_1, 4, -1, 31, 0); +DEFINE_QNODE(slv_sdcc_2, QCS404_SLAVE_SDCC_2, 4, -1, 33, 0); +DEFINE_QNODE(slv_tlmm_south, QCS404_SLAVE_TLMM_SOUTH, 4, -1, -1, 0); +DEFINE_QNODE(slv_usb_hs, QCS404_SLAVE_USB_HS, 4, -1, 40, 0); +DEFINE_QNODE(slv_usb3, QCS404_SLAVE_USB3, 4, -1, 22, 0); +DEFINE_QNODE(slv_crypto_0_cfg, QCS404_SLAVE_CRYPTO_0_CFG, 4, -1, 52, 0); +DEFINE_QNODE(slv_pcnoc_snoc, QCS404_PNOC_SNOC_SLV, 8, -1, 45, QCS404_PNOC_SNOC_MAS); +DEFINE_QNODE(slv_kpss_ahb, QCS404_SLAVE_APPSS, 4, -1, -1, 0); +DEFINE_QNODE(slv_wcss, QCS404_SLAVE_WCSS, 4, -1, 23, 0); +DEFINE_QNODE(slv_snoc_bimc_1, QCS404_SNOC_BIMC_1_SLV, 8, -1, 104, QCS404_SNOC_BIMC_1_MAS); +DEFINE_QNODE(slv_imem, QCS404_SLAVE_OCIMEM, 8, -1, 26, 0); +DEFINE_QNODE(slv_snoc_pcnoc, QCS404_SNOC_PNOC_SLV, 8, -1, 28, QCS404_SNOC_PNOC_MAS); +DEFINE_QNODE(slv_qdss_stm, QCS404_SLAVE_QDSS_STM, 4, -1, 30, 0); +DEFINE_QNODE(slv_cats_0, QCS404_SLAVE_CATS_128, 16, -1, -1, 0); +DEFINE_QNODE(slv_cats_1, QCS404_SLAVE_OCMEM_64, 8, -1, -1, 0); +DEFINE_QNODE(slv_lpass, QCS404_SLAVE_LPASS, 4, -1, -1, 0); + +static struct qcom_icc_node *qcs404_bimc_nodes[] = { + [MASTER_AMPSS_M0] = &mas_apps_proc, + [MASTER_OXILI] = &mas_oxili, + [MASTER_MDP_PORT0] = &mas_mdp, + [MASTER_SNOC_BIMC_1] = &mas_snoc_bimc_1, + [MASTER_TCU_0] = &mas_tcu_0, + [SLAVE_EBI_CH0] = &slv_ebi, + [SLAVE_BIMC_SNOC] = &slv_bimc_snoc, +}; + +static struct qcom_icc_desc qcs404_bimc = { + .nodes = qcs404_bimc_nodes, + .num_nodes = ARRAY_SIZE(qcs404_bimc_nodes), +}; + +static struct qcom_icc_node *qcs404_pcnoc_nodes[] = { + [MASTER_SPDM] = &mas_spdm, + [MASTER_BLSP_1] = &mas_blsp_1, + [MASTER_BLSP_2] = &mas_blsp_2, + [MASTER_XI_USB_HS1] = &mas_xi_usb_hs1, + [MASTER_CRYPT0] = &mas_crypto, + [MASTER_SDCC_1] = &mas_sdcc_1, + [MASTER_SDCC_2] = &mas_sdcc_2, + [MASTER_SNOC_PCNOC] = &mas_snoc_pcnoc, + [MASTER_QPIC] = &mas_qpic, + [PCNOC_INT_0] = &pcnoc_int_0, + [PCNOC_INT_2] = &pcnoc_int_2, + [PCNOC_INT_3] = &pcnoc_int_3, + [PCNOC_S_0] = &pcnoc_s_0, + [PCNOC_S_1] = &pcnoc_s_1, + [PCNOC_S_2] = &pcnoc_s_2, + [PCNOC_S_3] = &pcnoc_s_3, + [PCNOC_S_4] = &pcnoc_s_4, + [PCNOC_S_6] = &pcnoc_s_6, + [PCNOC_S_7] = &pcnoc_s_7, + [PCNOC_S_8] = &pcnoc_s_8, + [PCNOC_S_9] = &pcnoc_s_9, + [PCNOC_S_10] = &pcnoc_s_10, + [PCNOC_S_11] = &pcnoc_s_11, + [SLAVE_SPDM] = &slv_spdm, + [SLAVE_PDM] = &slv_pdm, + [SLAVE_PRNG] = &slv_prng, + [SLAVE_TCSR] = &slv_tcsr, + [SLAVE_SNOC_CFG] = &slv_snoc_cfg, + [SLAVE_MESSAGE_RAM] = &slv_message_ram, + [SLAVE_DISP_SS_CFG] = &slv_disp_ss_cfg, + [SLAVE_GPU_CFG] = &slv_gpu_cfg, + [SLAVE_BLSP_1] = &slv_blsp_1, + [SLAVE_BLSP_2] = &slv_blsp_2, + [SLAVE_TLMM_NORTH] = &slv_tlmm_north, + [SLAVE_PCIE] = &slv_pcie, + [SLAVE_ETHERNET] = &slv_ethernet, + [SLAVE_TLMM_EAST] = &slv_tlmm_east, + [SLAVE_TCU] = &slv_tcu, + [SLAVE_PMIC_ARB] = &slv_pmic_arb, + [SLAVE_SDCC_1] = &slv_sdcc_1, + [SLAVE_SDCC_2] = &slv_sdcc_2, + [SLAVE_TLMM_SOUTH] = &slv_tlmm_south, + [SLAVE_USB_HS] = &slv_usb_hs, + [SLAVE_USB3] = &slv_usb3, + [SLAVE_CRYPTO_0_CFG] = &slv_crypto_0_cfg, + [SLAVE_PCNOC_SNOC] = &slv_pcnoc_snoc, +}; + +static struct qcom_icc_desc qcs404_pcnoc = { + .nodes = qcs404_pcnoc_nodes, + .num_nodes = ARRAY_SIZE(qcs404_pcnoc_nodes), +}; + +static struct qcom_icc_node *qcs404_snoc_nodes[] = { + [MASTER_QDSS_BAM] = &mas_qdss_bam, + [MASTER_BIMC_SNOC] = &mas_bimc_snoc, + [MASTER_PCNOC_SNOC] = &mas_pcnoc_snoc, + [MASTER_QDSS_ETR] = &mas_qdss_etr, + [MASTER_EMAC] = &mas_emac, + [MASTER_PCIE] = &mas_pcie, + [MASTER_USB3] = &mas_usb3, + [QDSS_INT] = &qdss_int, + [SNOC_INT_0] = &snoc_int_0, + [SNOC_INT_1] = &snoc_int_1, + [SNOC_INT_2] = &snoc_int_2, + [SLAVE_KPSS_AHB] = &slv_kpss_ahb, + [SLAVE_WCSS] = &slv_wcss, + [SLAVE_SNOC_BIMC_1] = &slv_snoc_bimc_1, + [SLAVE_IMEM] = &slv_imem, + [SLAVE_SNOC_PCNOC] = &slv_snoc_pcnoc, + [SLAVE_QDSS_STM] = &slv_qdss_stm, + [SLAVE_CATS_0] = &slv_cats_0, + [SLAVE_CATS_1] = &slv_cats_1, + [SLAVE_LPASS] = &slv_lpass, +}; + +static struct qcom_icc_desc qcs404_snoc = { + .nodes = qcs404_snoc_nodes, + .num_nodes = ARRAY_SIZE(qcs404_snoc_nodes), +}; + +static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak) +{ + *agg_avg += avg_bw; + *agg_peak = max(*agg_peak, peak_bw); + + return 0; +} + +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + struct icc_node *n; + u64 sum_bw; + u64 max_peak_bw; + u64 rate; + u32 agg_avg = 0; + u32 agg_peak = 0; + int ret, i; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + list_for_each_entry(n, &provider->nodes, node_list) + qcom_icc_aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); + + sum_bw = icc_units_to_bps(agg_avg); + max_peak_bw = icc_units_to_bps(agg_peak); + + /* send bandwidth request message to the RPM processor */ + if (qn->mas_rpm_id != -1) { + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, + RPM_BUS_MASTER_REQ, + qn->mas_rpm_id, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", + qn->mas_rpm_id, ret); + return ret; + } + } + + if (qn->slv_rpm_id != -1) { + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, + RPM_BUS_SLAVE_REQ, + qn->slv_rpm_id, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send slv error %d\n", + ret); + return ret; + } + } + + rate = max(sum_bw, max_peak_bw); + + do_div(rate, qn->buswidth); + + if (qn->rate == rate) + return 0; + + for (i = 0; i < qp->num_clks; i++) { + ret = clk_set_rate(qp->bus_clks[i].clk, rate); + if (ret) { + pr_err("%s clk_set_rate error: %d\n", + qp->bus_clks[i].id, ret); + return ret; + } + } + + qn->rate = rate; + + return 0; +} + +static int qnoc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct qcom_icc_desc *desc; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct qcom_icc_node **qnodes; + struct qcom_icc_provider *qp; + struct icc_node *node; + size_t num_nodes, i; + int ret; + + /* wait for the RPM proxy */ + if (!qcom_icc_rpm_smd_available()) + return -EPROBE_DEFER; + + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + qnodes = desc->nodes; + num_nodes = desc->num_nodes; + + qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data = devm_kcalloc(dev, num_nodes, sizeof(*node), GFP_KERNEL); + if (!data) + return -ENOMEM; + + qp->bus_clks = devm_kmemdup(dev, bus_clocks, sizeof(bus_clocks), + GFP_KERNEL); + if (!qp->bus_clks) + return -ENOMEM; + + qp->num_clks = ARRAY_SIZE(bus_clocks); + ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + provider = &qp->provider; + INIT_LIST_HEAD(&provider->nodes); + provider->dev = dev; + provider->set = qcom_icc_set; + provider->aggregate = qcom_icc_aggregate; + provider->xlate = of_icc_xlate_onecell; + provider->data = data; + + ret = icc_provider_add(provider); + if (ret) { + dev_err(dev, "error adding interconnect provider: %d\n", ret); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + return ret; + } + + for (i = 0; i < num_nodes; i++) { + size_t j; + + node = icc_node_create(qnodes[i]->id); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err; + } + + node->name = qnodes[i]->name; + node->data = qnodes[i]; + icc_node_add(node, provider); + + dev_dbg(dev, "registered node %s\n", node->name); + + /* populate links */ + for (j = 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] = node; + } + data->num_nodes = num_nodes; + + platform_set_drvdata(pdev, qp); + + return 0; +err: + list_for_each_entry(node, &provider->nodes, node_list) { + icc_node_del(node); + icc_node_destroy(node->id); + } + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + icc_provider_del(provider); + + return ret; +} + +static int qnoc_remove(struct platform_device *pdev) +{ + struct qcom_icc_provider *qp = platform_get_drvdata(pdev); + struct icc_provider *provider = &qp->provider; + struct icc_node *n; + + list_for_each_entry(n, &provider->nodes, node_list) { + icc_node_del(n); + icc_node_destroy(n->id); + } + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + + return icc_provider_del(provider); +} + +static const struct of_device_id qcs404_noc_of_match[] = { + { .compatible = "qcom,qcs404-bimc", .data = &qcs404_bimc }, + { .compatible = "qcom,qcs404-pcnoc", .data = &qcs404_pcnoc }, + { .compatible = "qcom,qcs404-snoc", .data = &qcs404_snoc }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcs404_noc_of_match); + +static struct platform_driver qcs404_noc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-qcs404", + .of_match_table = qcs404_noc_of_match, + }, +}; +module_platform_driver(qcs404_noc_driver); +MODULE_DESCRIPTION("Qualcomm QCS404 NoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index 4915b78da673..57955596bb59 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * */ @@ -20,23 +20,6 @@ #include <soc/qcom/rpmh.h> #include <soc/qcom/tcs.h> -#define BCM_TCS_CMD_COMMIT_SHFT 30 -#define BCM_TCS_CMD_COMMIT_MASK 0x40000000 -#define BCM_TCS_CMD_VALID_SHFT 29 -#define BCM_TCS_CMD_VALID_MASK 0x20000000 -#define BCM_TCS_CMD_VOTE_X_SHFT 14 -#define BCM_TCS_CMD_VOTE_MASK 0x3fff -#define BCM_TCS_CMD_VOTE_Y_SHFT 0 -#define BCM_TCS_CMD_VOTE_Y_MASK 0xfffc000 - -#define BCM_TCS_CMD(commit, valid, vote_x, vote_y) \ - (((commit) << BCM_TCS_CMD_COMMIT_SHFT) | \ - ((valid) << BCM_TCS_CMD_VALID_SHFT) | \ - ((cpu_to_le32(vote_x) & \ - BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_X_SHFT) | \ - ((cpu_to_le32(vote_y) & \ - BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT)) - #define to_qcom_provider(_provider) \ container_of(_provider, struct qcom_icc_provider, provider) @@ -66,6 +49,22 @@ struct bcm_db { #define SDM845_MAX_BCM_PER_NODE 2 #define SDM845_MAX_VCD 10 +/* + * The AMC bucket denotes constraints that are applied to hardware when + * icc_set_bw() completes, whereas the WAKE and SLEEP constraints are applied + * when the execution environment transitions between active and low power mode. + */ +#define QCOM_ICC_BUCKET_AMC 0 +#define QCOM_ICC_BUCKET_WAKE 1 +#define QCOM_ICC_BUCKET_SLEEP 2 +#define QCOM_ICC_NUM_BUCKETS 3 +#define QCOM_ICC_TAG_AMC BIT(QCOM_ICC_BUCKET_AMC) +#define QCOM_ICC_TAG_WAKE BIT(QCOM_ICC_BUCKET_WAKE) +#define QCOM_ICC_TAG_SLEEP BIT(QCOM_ICC_BUCKET_SLEEP) +#define QCOM_ICC_TAG_ACTIVE_ONLY (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE) +#define QCOM_ICC_TAG_ALWAYS (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE |\ + QCOM_ICC_TAG_SLEEP) + /** * struct qcom_icc_node - Qualcomm specific interconnect nodes * @name: the node name used in debugfs @@ -86,8 +85,8 @@ struct qcom_icc_node { u16 num_links; u16 channels; u16 buswidth; - u64 sum_avg; - u64 max_peak; + u64 sum_avg[QCOM_ICC_NUM_BUCKETS]; + u64 max_peak[QCOM_ICC_NUM_BUCKETS]; struct qcom_icc_bcm *bcms[SDM845_MAX_BCM_PER_NODE]; size_t num_bcms; }; @@ -112,8 +111,8 @@ struct qcom_icc_bcm { const char *name; u32 type; u32 addr; - u64 vote_x; - u64 vote_y; + u64 vote_x[QCOM_ICC_NUM_BUCKETS]; + u64 vote_y[QCOM_ICC_NUM_BUCKETS]; bool dirty; bool keepalive; struct bcm_db aux_data; @@ -555,7 +554,7 @@ inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y, cmd->wait = true; } -static void tcs_list_gen(struct list_head *bcm_list, +static void tcs_list_gen(struct list_head *bcm_list, int bucket, struct tcs_cmd tcs_list[SDM845_MAX_VCD], int n[SDM845_MAX_VCD]) { @@ -573,8 +572,8 @@ static void tcs_list_gen(struct list_head *bcm_list, commit = true; cur_vcd_size = 0; } - tcs_cmd_gen(&tcs_list[idx], bcm->vote_x, bcm->vote_y, - bcm->addr, commit); + tcs_cmd_gen(&tcs_list[idx], bcm->vote_x[bucket], + bcm->vote_y[bucket], bcm->addr, commit); idx++; n[batch]++; /* @@ -595,38 +594,56 @@ static void tcs_list_gen(struct list_head *bcm_list, static void bcm_aggregate(struct qcom_icc_bcm *bcm) { - size_t i; - u64 agg_avg = 0; - u64 agg_peak = 0; + size_t i, bucket; + u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0}; + u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0}; u64 temp; - for (i = 0; i < bcm->num_nodes; i++) { - temp = bcm->nodes[i]->sum_avg * bcm->aux_data.width; - do_div(temp, bcm->nodes[i]->buswidth * bcm->nodes[i]->channels); - agg_avg = max(agg_avg, temp); + for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) { + for (i = 0; i < bcm->num_nodes; i++) { + temp = bcm->nodes[i]->sum_avg[bucket] * bcm->aux_data.width; + do_div(temp, bcm->nodes[i]->buswidth * bcm->nodes[i]->channels); + agg_avg[bucket] = max(agg_avg[bucket], temp); - temp = bcm->nodes[i]->max_peak * bcm->aux_data.width; - do_div(temp, bcm->nodes[i]->buswidth); - agg_peak = max(agg_peak, temp); - } + temp = bcm->nodes[i]->max_peak[bucket] * bcm->aux_data.width; + do_div(temp, bcm->nodes[i]->buswidth); + agg_peak[bucket] = max(agg_peak[bucket], temp); + } - temp = agg_avg * 1000ULL; - do_div(temp, bcm->aux_data.unit); - bcm->vote_x = temp; + temp = agg_avg[bucket] * 1000ULL; + do_div(temp, bcm->aux_data.unit); + bcm->vote_x[bucket] = temp; - temp = agg_peak * 1000ULL; - do_div(temp, bcm->aux_data.unit); - bcm->vote_y = temp; + temp = agg_peak[bucket] * 1000ULL; + do_div(temp, bcm->aux_data.unit); + bcm->vote_y[bucket] = temp; + } - if (bcm->keepalive && bcm->vote_x == 0 && bcm->vote_y == 0) { - bcm->vote_x = 1; - bcm->vote_y = 1; + if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 && + bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) { + bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1; + bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1; + bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1; + bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1; } bcm->dirty = false; } -static int qcom_icc_aggregate(struct icc_node *node, u32 avg_bw, +static void qcom_icc_pre_aggregate(struct icc_node *node) +{ + size_t i; + struct qcom_icc_node *qn; + + qn = node->data; + + for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { + qn->sum_avg[i] = 0; + qn->max_peak[i] = 0; + } +} + +static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, u32 peak_bw, u32 *agg_avg, u32 *agg_peak) { size_t i; @@ -634,12 +651,19 @@ static int qcom_icc_aggregate(struct icc_node *node, u32 avg_bw, qn = node->data; + if (!tag) + tag = QCOM_ICC_TAG_ALWAYS; + + for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { + if (tag & BIT(i)) { + qn->sum_avg[i] += avg_bw; + qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw); + } + } + *agg_avg += avg_bw; *agg_peak = max_t(u32, *agg_peak, peak_bw); - qn->sum_avg = *agg_avg; - qn->max_peak = *agg_peak; - for (i = 0; i < qn->num_bcms; i++) qn->bcms[i]->dirty = true; @@ -675,7 +699,7 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) * Construct the command list based on a pre ordered list of BCMs * based on VCD. */ - tcs_list_gen(&commit_list, cmds, commit_idx); + tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_AMC, cmds, commit_idx); if (!commit_idx[0]) return ret; @@ -693,6 +717,41 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) return ret; } + INIT_LIST_HEAD(&commit_list); + + for (i = 0; i < qp->num_bcms; i++) { + /* + * Only generate WAKE and SLEEP commands if a resource's + * requirements change as the execution environment transitions + * between different power states. + */ + if (qp->bcms[i]->vote_x[QCOM_ICC_BUCKET_WAKE] != + qp->bcms[i]->vote_x[QCOM_ICC_BUCKET_SLEEP] || + qp->bcms[i]->vote_y[QCOM_ICC_BUCKET_WAKE] != + qp->bcms[i]->vote_y[QCOM_ICC_BUCKET_SLEEP]) { + list_add_tail(&qp->bcms[i]->list, &commit_list); + } + } + + if (list_empty(&commit_list)) + return ret; + + tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_WAKE, cmds, commit_idx); + + ret = rpmh_write_batch(qp->dev, RPMH_WAKE_ONLY_STATE, cmds, commit_idx); + if (ret) { + pr_err("Error sending WAKE RPMH requests (%d)\n", ret); + return ret; + } + + tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_SLEEP, cmds, commit_idx); + + ret = rpmh_write_batch(qp->dev, RPMH_SLEEP_STATE, cmds, commit_idx); + if (ret) { + pr_err("Error sending SLEEP RPMH requests (%d)\n", ret); + return ret; + } + return ret; } @@ -738,6 +797,7 @@ static int qnoc_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = &pdev->dev; provider->set = qcom_icc_set; + provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate = of_icc_xlate_onecell; INIT_LIST_HEAD(&provider->nodes); diff --git a/drivers/interconnect/qcom/smd-rpm.c b/drivers/interconnect/qcom/smd-rpm.c new file mode 100644 index 000000000000..dc8ff8d133a9 --- /dev/null +++ b/drivers/interconnect/qcom/smd-rpm.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RPM over SMD communication wrapper for interconnects + * + * Copyright (C) 2019 Linaro Ltd + * Author: Georgi Djakov <[email protected]> + */ + +#include <linux/interconnect-provider.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/soc/qcom/smd-rpm.h> + +#include "smd-rpm.h" + +#define RPM_KEY_BW 0x00007762 + +static struct qcom_smd_rpm *icc_smd_rpm; + +struct icc_rpm_smd_req { + __le32 key; + __le32 nbytes; + __le32 value; +}; + +bool qcom_icc_rpm_smd_available(void) +{ + return !!icc_smd_rpm; +} +EXPORT_SYMBOL_GPL(qcom_icc_rpm_smd_available); + +int qcom_icc_rpm_smd_send(int ctx, int rsc_type, int id, u32 val) +{ + struct icc_rpm_smd_req req = { + .key = cpu_to_le32(RPM_KEY_BW), + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(val), + }; + + return qcom_rpm_smd_write(icc_smd_rpm, ctx, rsc_type, id, &req, + sizeof(req)); +} +EXPORT_SYMBOL_GPL(qcom_icc_rpm_smd_send); + +static int qcom_icc_rpm_smd_remove(struct platform_device *pdev) +{ + icc_smd_rpm = NULL; + + return 0; +} + +static int qcom_icc_rpm_smd_probe(struct platform_device *pdev) +{ + icc_smd_rpm = dev_get_drvdata(pdev->dev.parent); + + if (!icc_smd_rpm) { + dev_err(&pdev->dev, "unable to retrieve handle to RPM\n"); + return -ENODEV; + } + + return 0; +} + +static struct platform_driver qcom_interconnect_rpm_smd_driver = { + .driver = { + .name = "icc_smd_rpm", + }, + .probe = qcom_icc_rpm_smd_probe, + .remove = qcom_icc_rpm_smd_remove, +}; +module_platform_driver(qcom_interconnect_rpm_smd_driver); +MODULE_AUTHOR("Georgi Djakov <[email protected]>"); +MODULE_DESCRIPTION("Qualcomm SMD RPM interconnect proxy driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:icc_smd_rpm"); diff --git a/drivers/interconnect/qcom/smd-rpm.h b/drivers/interconnect/qcom/smd-rpm.h new file mode 100644 index 000000000000..ca9d0327b8ac --- /dev/null +++ b/drivers/interconnect/qcom/smd-rpm.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019, Linaro Ltd. + * Author: Georgi Djakov <[email protected]> + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_SMD_RPM_H +#define __DRIVERS_INTERCONNECT_QCOM_SMD_RPM_H + +#include <linux/soc/qcom/smd-rpm.h> + +bool qcom_icc_rpm_smd_available(void); +int qcom_icc_rpm_smd_send(int ctx, int rsc_type, int id, u32 val); + +#endif diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 0d9fddc498a6..c96a1afc95bd 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_ARCH_SUNXI) += allwinner/ obj-$(CONFIG_ARCH_MESON) += amlogic/ -obj-$(CONFIG_LANTIQ) += lantiq/ obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ obj-$(CONFIG_ARCH_RENESAS) += renesas/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ @@ -19,6 +18,7 @@ obj-y += broadcom/ \ cadence/ \ freescale/ \ hisilicon/ \ + lantiq/ \ marvell/ \ motorola/ \ mscc/ \ diff --git a/drivers/phy/lantiq/Kconfig b/drivers/phy/lantiq/Kconfig index eb66c857ce25..c4df9709d53f 100644 --- a/drivers/phy/lantiq/Kconfig +++ b/drivers/phy/lantiq/Kconfig @@ -2,6 +2,17 @@ # # Phy drivers for Lantiq / Intel platforms # +config PHY_LANTIQ_VRX200_PCIE + tristate "Lantiq VRX200/ARX300 PCIe PHY" + depends on SOC_TYPE_XWAY || COMPILE_TEST + depends on OF && HAS_IOMEM + select GENERIC_PHY + select REGMAP_MMIO + help + Support for the PCIe PHY(s) on the Lantiq / Intel VRX200 and ARX300 + family SoCs. + If unsure, say N. + config PHY_LANTIQ_RCU_USB2 tristate "Lantiq XWAY SoC RCU based USB PHY" depends on OF && (SOC_TYPE_XWAY || COMPILE_TEST) diff --git a/drivers/phy/lantiq/Makefile b/drivers/phy/lantiq/Makefile index 540049039092..7c14eb24ab73 100644 --- a/drivers/phy/lantiq/Makefile +++ b/drivers/phy/lantiq/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_LANTIQ_RCU_USB2) += phy-lantiq-rcu-usb2.o +obj-$(CONFIG_PHY_LANTIQ_VRX200_PCIE) += phy-lantiq-vrx200-pcie.o diff --git a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c new file mode 100644 index 000000000000..544d64a84cc0 --- /dev/null +++ b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PCIe PHY driver for Lantiq VRX200 and ARX300 SoCs. + * + * Copyright (C) 2019 Martin Blumenstingl <[email protected]> + * + * Based on the BSP (called "UGW") driver: + * Copyright (C) 2009-2015 Lei Chuanhua <[email protected]> + * Copyright (C) 2016 Intel Corporation + * + * TODO: PHY modes other than 36MHz (without "SSC") + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <dt-bindings/phy/phy-lantiq-vrx200-pcie.h> + +#define PCIE_PHY_PLL_CTRL1 0x44 + +#define PCIE_PHY_PLL_CTRL2 0x46 +#define PCIE_PHY_PLL_CTRL2_CONST_SDM_MASK GENMASK(7, 0) +#define PCIE_PHY_PLL_CTRL2_CONST_SDM_EN BIT(8) +#define PCIE_PHY_PLL_CTRL2_PLL_SDM_EN BIT(9) + +#define PCIE_PHY_PLL_CTRL3 0x48 +#define PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_EN BIT(1) +#define PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_MASK GENMASK(6, 4) + +#define PCIE_PHY_PLL_CTRL4 0x4a +#define PCIE_PHY_PLL_CTRL5 0x4c +#define PCIE_PHY_PLL_CTRL6 0x4e +#define PCIE_PHY_PLL_CTRL7 0x50 +#define PCIE_PHY_PLL_A_CTRL1 0x52 + +#define PCIE_PHY_PLL_A_CTRL2 0x54 +#define PCIE_PHY_PLL_A_CTRL2_LF_MODE_EN BIT(14) + +#define PCIE_PHY_PLL_A_CTRL3 0x56 +#define PCIE_PHY_PLL_A_CTRL3_MMD_MASK GENMASK(15, 13) + +#define PCIE_PHY_PLL_STATUS 0x58 + +#define PCIE_PHY_TX1_CTRL1 0x60 +#define PCIE_PHY_TX1_CTRL1_FORCE_EN BIT(3) +#define PCIE_PHY_TX1_CTRL1_LOAD_EN BIT(4) + +#define PCIE_PHY_TX1_CTRL2 0x62 +#define PCIE_PHY_TX1_CTRL3 0x64 +#define PCIE_PHY_TX1_A_CTRL1 0x66 +#define PCIE_PHY_TX1_A_CTRL2 0x68 +#define PCIE_PHY_TX1_MOD1 0x6a +#define PCIE_PHY_TX1_MOD2 0x6c +#define PCIE_PHY_TX1_MOD3 0x6e + +#define PCIE_PHY_TX2_CTRL1 0x70 +#define PCIE_PHY_TX2_CTRL1_LOAD_EN BIT(4) + +#define PCIE_PHY_TX2_CTRL2 0x72 +#define PCIE_PHY_TX2_A_CTRL1 0x76 +#define PCIE_PHY_TX2_A_CTRL2 0x78 +#define PCIE_PHY_TX2_MOD1 0x7a +#define PCIE_PHY_TX2_MOD2 0x7c +#define PCIE_PHY_TX2_MOD3 0x7e + +#define PCIE_PHY_RX1_CTRL1 0xa0 +#define PCIE_PHY_RX1_CTRL1_LOAD_EN BIT(1) + +#define PCIE_PHY_RX1_CTRL2 0xa2 +#define PCIE_PHY_RX1_CDR 0xa4 +#define PCIE_PHY_RX1_EI 0xa6 +#define PCIE_PHY_RX1_A_CTRL 0xaa + +struct ltq_vrx200_pcie_phy_priv { + struct phy *phy; + unsigned int mode; + struct device *dev; + struct regmap *phy_regmap; + struct regmap *rcu_regmap; + struct clk *pdi_clk; + struct clk *phy_clk; + struct reset_control *phy_reset; + struct reset_control *pcie_reset; + u32 rcu_ahb_endian_offset; + u32 rcu_ahb_endian_big_endian_mask; +}; + +static void ltq_vrx200_pcie_phy_common_setup(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + + /* PLL Setting */ + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL1, 0x120e); + + /* increase the bias reference voltage */ + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL2, 0x39d7); + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL3, 0x0900); + + /* Endcnt */ + regmap_write(priv->phy_regmap, PCIE_PHY_RX1_EI, 0x0004); + regmap_write(priv->phy_regmap, PCIE_PHY_RX1_A_CTRL, 0x6803); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_TX1_CTRL1, + PCIE_PHY_TX1_CTRL1_FORCE_EN, + PCIE_PHY_TX1_CTRL1_FORCE_EN); + + /* predrv_ser_en */ + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_A_CTRL2, 0x0706); + + /* ctrl_lim */ + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_CTRL3, 0x1fff); + + /* ctrl */ + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_A_CTRL1, 0x0810); + + /* predrv_ser_en */ + regmap_update_bits(priv->phy_regmap, PCIE_PHY_TX2_A_CTRL2, 0x7f00, + 0x4700); + + /* RTERM */ + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_CTRL2, 0x2e00); + + /* Improved 100MHz clock output */ + regmap_write(priv->phy_regmap, PCIE_PHY_TX2_CTRL2, 0x3096); + regmap_write(priv->phy_regmap, PCIE_PHY_TX2_A_CTRL2, 0x4707); + + /* Reduced CDR BW to avoid glitches */ + regmap_write(priv->phy_regmap, PCIE_PHY_RX1_CDR, 0x0235); +} + +static void pcie_phy_36mhz_mode_setup(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL3, + PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_EN, 0x0000); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL3, + PCIE_PHY_PLL_CTRL3_EXT_MMD_DIV_RATIO_MASK, 0x0000); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL2, + PCIE_PHY_PLL_CTRL2_PLL_SDM_EN, + PCIE_PHY_PLL_CTRL2_PLL_SDM_EN); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL2, + PCIE_PHY_PLL_CTRL2_CONST_SDM_EN, + PCIE_PHY_PLL_CTRL2_CONST_SDM_EN); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL3, + PCIE_PHY_PLL_A_CTRL3_MMD_MASK, + FIELD_PREP(PCIE_PHY_PLL_A_CTRL3_MMD_MASK, 0x1)); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_A_CTRL2, + PCIE_PHY_PLL_A_CTRL2_LF_MODE_EN, 0x0000); + + /* const_sdm */ + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL1, 0x38e4); + + regmap_update_bits(priv->phy_regmap, PCIE_PHY_PLL_CTRL2, + PCIE_PHY_PLL_CTRL2_CONST_SDM_MASK, + FIELD_PREP(PCIE_PHY_PLL_CTRL2_CONST_SDM_MASK, + 0xee)); + + /* pllmod */ + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL7, 0x0002); + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL6, 0x3a04); + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL5, 0xfae3); + regmap_write(priv->phy_regmap, PCIE_PHY_PLL_CTRL4, 0x1b72); +} + +static int ltq_vrx200_pcie_phy_wait_for_pll(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + unsigned int tmp; + int ret; + + ret = regmap_read_poll_timeout(priv->phy_regmap, PCIE_PHY_PLL_STATUS, + tmp, ((tmp & 0x0070) == 0x0070), 10, + 10000); + if (ret) { + dev_err(priv->dev, "PLL Link timeout, PLL status = 0x%04x\n", + tmp); + return ret; + } + + return 0; +} + +static void ltq_vrx200_pcie_phy_apply_workarounds(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + static const struct reg_default slices[] = { + { + .reg = PCIE_PHY_TX1_CTRL1, + .def = PCIE_PHY_TX1_CTRL1_LOAD_EN, + }, + { + .reg = PCIE_PHY_TX2_CTRL1, + .def = PCIE_PHY_TX2_CTRL1_LOAD_EN, + }, + { + .reg = PCIE_PHY_RX1_CTRL1, + .def = PCIE_PHY_RX1_CTRL1_LOAD_EN, + } + }; + int i; + + for (i = 0; i < ARRAY_SIZE(slices); i++) { + /* enable load_en */ + regmap_update_bits(priv->phy_regmap, slices[i].reg, + slices[i].def, slices[i].def); + + udelay(1); + + /* disable load_en */ + regmap_update_bits(priv->phy_regmap, slices[i].reg, + slices[i].def, 0x0); + } + + for (i = 0; i < 5; i++) { + /* TX2 modulation */ + regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD1, 0x1ffe); + regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD2, 0xfffe); + regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD3, 0x0601); + usleep_range(1000, 2000); + regmap_write(priv->phy_regmap, PCIE_PHY_TX2_MOD3, 0x0001); + + /* TX1 modulation */ + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD1, 0x1ffe); + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD2, 0xfffe); + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD3, 0x0601); + usleep_range(1000, 2000); + regmap_write(priv->phy_regmap, PCIE_PHY_TX1_MOD3, 0x0001); + } +} + +static int ltq_vrx200_pcie_phy_init(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + int ret; + + if (of_device_is_big_endian(priv->dev->of_node)) + regmap_update_bits(priv->rcu_regmap, + priv->rcu_ahb_endian_offset, + priv->rcu_ahb_endian_big_endian_mask, + priv->rcu_ahb_endian_big_endian_mask); + else + regmap_update_bits(priv->rcu_regmap, + priv->rcu_ahb_endian_offset, + priv->rcu_ahb_endian_big_endian_mask, 0x0); + + ret = reset_control_assert(priv->phy_reset); + if (ret) + goto err; + + udelay(1); + + ret = reset_control_deassert(priv->phy_reset); + if (ret) + goto err; + + udelay(1); + + ret = reset_control_deassert(priv->pcie_reset); + if (ret) + goto err_assert_phy_reset; + + /* Make sure PHY PLL is stable */ + usleep_range(20, 40); + + return 0; + +err_assert_phy_reset: + reset_control_assert(priv->phy_reset); +err: + return ret; +} + +static int ltq_vrx200_pcie_phy_exit(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = reset_control_assert(priv->pcie_reset); + if (ret) + return ret; + + ret = reset_control_assert(priv->phy_reset); + if (ret) + return ret; + + return 0; +} + +static int ltq_vrx200_pcie_phy_power_on(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + int ret; + + /* Enable PDI to access PCIe PHY register */ + ret = clk_prepare_enable(priv->pdi_clk); + if (ret) + goto err; + + /* Configure PLL and PHY clock */ + ltq_vrx200_pcie_phy_common_setup(phy); + + pcie_phy_36mhz_mode_setup(phy); + + /* Enable the PCIe PHY and make PLL setting take effect */ + ret = clk_prepare_enable(priv->phy_clk); + if (ret) + goto err_disable_pdi_clk; + + /* Check if we are in "startup ready" status */ + if (ltq_vrx200_pcie_phy_wait_for_pll(phy) != 0) + goto err_disable_phy_clk; + + ltq_vrx200_pcie_phy_apply_workarounds(phy); + + return 0; + +err_disable_phy_clk: + clk_disable_unprepare(priv->phy_clk); +err_disable_pdi_clk: + clk_disable_unprepare(priv->pdi_clk); +err: + return ret; +} + +static int ltq_vrx200_pcie_phy_power_off(struct phy *phy) +{ + struct ltq_vrx200_pcie_phy_priv *priv = phy_get_drvdata(phy); + + clk_disable_unprepare(priv->phy_clk); + clk_disable_unprepare(priv->pdi_clk); + + return 0; +} + +static struct phy_ops ltq_vrx200_pcie_phy_ops = { + .init = ltq_vrx200_pcie_phy_init, + .exit = ltq_vrx200_pcie_phy_exit, + .power_on = ltq_vrx200_pcie_phy_power_on, + .power_off = ltq_vrx200_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct phy *ltq_vrx200_pcie_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct ltq_vrx200_pcie_phy_priv *priv = dev_get_drvdata(dev); + unsigned int mode; + + if (args->args_count != 1) { + dev_err(dev, "invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + mode = args->args[0]; + + switch (mode) { + case LANTIQ_PCIE_PHY_MODE_36MHZ: + priv->mode = mode; + break; + + case LANTIQ_PCIE_PHY_MODE_25MHZ: + case LANTIQ_PCIE_PHY_MODE_25MHZ_SSC: + case LANTIQ_PCIE_PHY_MODE_36MHZ_SSC: + case LANTIQ_PCIE_PHY_MODE_100MHZ: + case LANTIQ_PCIE_PHY_MODE_100MHZ_SSC: + dev_err(dev, "PHY mode not implemented yet: %u\n", mode); + return ERR_PTR(-EINVAL); + + default: + dev_err(dev, "invalid PHY mode %u\n", mode); + return ERR_PTR(-EINVAL); + }; + + return priv->phy; +} + +static int ltq_vrx200_pcie_phy_probe(struct platform_device *pdev) +{ + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .reg_stride = 2, + .max_register = PCIE_PHY_RX1_A_CTRL, + }; + struct ltq_vrx200_pcie_phy_priv *priv; + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct resource *res; + void __iomem *base; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->phy_regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(priv->phy_regmap)) + return PTR_ERR(priv->phy_regmap); + + priv->rcu_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "lantiq,rcu"); + if (IS_ERR(priv->rcu_regmap)) + return PTR_ERR(priv->rcu_regmap); + + ret = device_property_read_u32(dev, "lantiq,rcu-endian-offset", + &priv->rcu_ahb_endian_offset); + if (ret) { + dev_err(dev, + "failed to parse the 'lantiq,rcu-endian-offset' property\n"); + return ret; + } + + ret = device_property_read_u32(dev, "lantiq,rcu-big-endian-mask", + &priv->rcu_ahb_endian_big_endian_mask); + if (ret) { + dev_err(dev, + "failed to parse the 'lantiq,rcu-big-endian-mask' property\n"); + return ret; + } + + priv->pdi_clk = devm_clk_get(dev, "pdi"); + if (IS_ERR(priv->pdi_clk)) + return PTR_ERR(priv->pdi_clk); + + priv->phy_clk = devm_clk_get(dev, "phy"); + if (IS_ERR(priv->phy_clk)) + return PTR_ERR(priv->phy_clk); + + priv->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + if (IS_ERR(priv->phy_reset)) + return PTR_ERR(priv->phy_reset); + + priv->pcie_reset = devm_reset_control_get_shared(dev, "pcie"); + if (IS_ERR(priv->pcie_reset)) + return PTR_ERR(priv->pcie_reset); + + priv->dev = dev; + + priv->phy = devm_phy_create(dev, dev->of_node, + <q_vrx200_pcie_phy_ops); + if (IS_ERR(priv->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(priv->phy); + } + + phy_set_drvdata(priv->phy, priv); + dev_set_drvdata(dev, priv); + + provider = devm_of_phy_provider_register(dev, + ltq_vrx200_pcie_phy_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id ltq_vrx200_pcie_phy_of_match[] = { + { .compatible = "lantiq,vrx200-pcie-phy", }, + { .compatible = "lantiq,arx300-pcie-phy", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ltq_vrx200_pcie_phy_of_match); + +static struct platform_driver ltq_vrx200_pcie_phy_driver = { + .probe = ltq_vrx200_pcie_phy_probe, + .driver = { + .name = "ltq-vrx200-pcie-phy", + .of_match_table = ltq_vrx200_pcie_phy_of_match, + } +}; +module_platform_driver(ltq_vrx200_pcie_phy_driver); + +MODULE_AUTHOR("Martin Blumenstingl <[email protected]>"); +MODULE_DESCRIPTION("Lantiq VRX200 and ARX300 PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig index 0e1642419c0b..4053ba6cd0fb 100644 --- a/drivers/phy/marvell/Kconfig +++ b/drivers/phy/marvell/Kconfig @@ -57,6 +57,7 @@ config PHY_MVEBU_CP110_COMPHY tristate "Marvell CP110 comphy driver" depends on ARCH_MVEBU || COMPILE_TEST depends on OF + depends on HAVE_ARM_SMCCC select GENERIC_PHY help This driver allows to control the comphy, an hardware block providing diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 3e00bc679d4e..6960dfd8ad8c 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -200,8 +200,10 @@ static int a38x_comphy_probe(struct platform_device *pdev) } phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops); - if (IS_ERR(phy)) + if (IS_ERR(phy)) { + of_node_put(child); return PTR_ERR(phy); + } priv->lane[val].base = base + 0x28 * val; priv->lane[val].priv = priv; diff --git a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c index 8812a104c233..1a138be8bd6a 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c @@ -26,6 +26,7 @@ #define COMPHY_SIP_POWER_ON 0x82000001 #define COMPHY_SIP_POWER_OFF 0x82000002 #define COMPHY_SIP_PLL_LOCK 0x82000003 +#define COMPHY_FW_NOT_SUPPORTED (-1) #define COMPHY_FW_MODE_SATA 0x1 #define COMPHY_FW_MODE_SGMII 0x2 @@ -169,6 +170,7 @@ static int mvebu_a3700_comphy_power_on(struct phy *phy) struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); u32 fw_param; int fw_mode; + int ret; fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, lane->mode, lane->submode); @@ -217,7 +219,12 @@ static int mvebu_a3700_comphy_power_on(struct phy *phy) return -ENOTSUPP; } - return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); + ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); + if (ret == COMPHY_FW_NOT_SUPPORTED) + dev_err(lane->dev, + "unsupported SMC call, try updating your firmware\n"); + + return ret; } static int mvebu_a3700_comphy_power_off(struct phy *phy) @@ -277,13 +284,17 @@ static int mvebu_a3700_comphy_probe(struct platform_device *pdev) } lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL); - if (!lane) + if (!lane) { + of_node_put(child); return -ENOMEM; + } phy = devm_phy_create(&pdev->dev, child, &mvebu_a3700_comphy_ops); - if (IS_ERR(phy)) + if (IS_ERR(phy)) { + of_node_put(child); return PTR_ERR(phy); + } lane->dev = &pdev->dev; lane->mode = PHY_MODE_INVALID; diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index d98e0451f6a1..e3b87c94aaf6 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -5,6 +5,8 @@ * Antoine Tenart <[email protected]> */ +#include <linux/arm-smccc.h> +#include <linux/clk.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/mfd/syscon.h> @@ -22,6 +24,7 @@ #define MVEBU_COMPHY_SERDES_CFG0_PU_RX BIT(11) #define MVEBU_COMPHY_SERDES_CFG0_PU_TX BIT(12) #define MVEBU_COMPHY_SERDES_CFG0_HALF_BUS BIT(14) +#define MVEBU_COMPHY_SERDES_CFG0_RXAUI_MODE BIT(15) #define MVEBU_COMPHY_SERDES_CFG1(n) (0x4 + (n) * 0x1000) #define MVEBU_COMPHY_SERDES_CFG1_RESET BIT(3) #define MVEBU_COMPHY_SERDES_CFG1_RX_INIT BIT(4) @@ -77,8 +80,8 @@ #define MVEBU_COMPHY_TX_SLEW_RATE(n) (0x974 + (n) * 0x1000) #define MVEBU_COMPHY_TX_SLEW_RATE_EMPH(n) ((n) << 5) #define MVEBU_COMPHY_TX_SLEW_RATE_SLC(n) ((n) << 10) -#define MVEBU_COMPHY_DLT_CTRL(n) (0x984 + (n) * 0x1000) -#define MVEBU_COMPHY_DLT_CTRL_DTL_FLOOP_EN BIT(2) +#define MVEBU_COMPHY_DTL_CTRL(n) (0x984 + (n) * 0x1000) +#define MVEBU_COMPHY_DTL_CTRL_DTL_FLOOP_EN BIT(2) #define MVEBU_COMPHY_FRAME_DETECT0(n) (0xa14 + (n) * 0x1000) #define MVEBU_COMPHY_FRAME_DETECT0_PATN(n) ((n) << 7) #define MVEBU_COMPHY_FRAME_DETECT3(n) (0xa20 + (n) * 0x1000) @@ -111,55 +114,151 @@ #define MVEBU_COMPHY_SELECTOR_PHY(n) ((n) * 0x4) #define MVEBU_COMPHY_PIPE_SELECTOR 0x1144 #define MVEBU_COMPHY_PIPE_SELECTOR_PIPE(n) ((n) * 0x4) +#define MVEBU_COMPHY_SD1_CTRL1 0x1148 +#define MVEBU_COMPHY_SD1_CTRL1_RXAUI1_EN BIT(26) +#define MVEBU_COMPHY_SD1_CTRL1_RXAUI0_EN BIT(27) #define MVEBU_COMPHY_LANES 6 #define MVEBU_COMPHY_PORTS 3 +#define COMPHY_SIP_POWER_ON 0x82000001 +#define COMPHY_SIP_POWER_OFF 0x82000002 +#define COMPHY_FW_NOT_SUPPORTED (-1) + +/* + * A lane is described by the following bitfields: + * [ 1- 0]: COMPHY polarity invertion + * [ 2- 7]: COMPHY speed + * [ 5-11]: COMPHY port index + * [12-16]: COMPHY mode + * [17]: Clock source + * [18-20]: PCIe width (x1, x2, x4) + */ +#define COMPHY_FW_POL_OFFSET 0 +#define COMPHY_FW_POL_MASK GENMASK(1, 0) +#define COMPHY_FW_SPEED_OFFSET 2 +#define COMPHY_FW_SPEED_MASK GENMASK(7, 2) +#define COMPHY_FW_SPEED_MAX COMPHY_FW_SPEED_MASK +#define COMPHY_FW_SPEED_1250 0 +#define COMPHY_FW_SPEED_3125 2 +#define COMPHY_FW_SPEED_5000 3 +#define COMPHY_FW_SPEED_103125 6 +#define COMPHY_FW_PORT_OFFSET 8 +#define COMPHY_FW_PORT_MASK GENMASK(11, 8) +#define COMPHY_FW_MODE_OFFSET 12 +#define COMPHY_FW_MODE_MASK GENMASK(16, 12) +#define COMPHY_FW_WIDTH_OFFSET 18 +#define COMPHY_FW_WIDTH_MASK GENMASK(20, 18) + +#define COMPHY_FW_PARAM_FULL(mode, port, speed, pol, width) \ + ((((pol) << COMPHY_FW_POL_OFFSET) & COMPHY_FW_POL_MASK) | \ + (((mode) << COMPHY_FW_MODE_OFFSET) & COMPHY_FW_MODE_MASK) | \ + (((port) << COMPHY_FW_PORT_OFFSET) & COMPHY_FW_PORT_MASK) | \ + (((speed) << COMPHY_FW_SPEED_OFFSET) & COMPHY_FW_SPEED_MASK) | \ + (((width) << COMPHY_FW_WIDTH_OFFSET) & COMPHY_FW_WIDTH_MASK)) + +#define COMPHY_FW_PARAM(mode, port) \ + COMPHY_FW_PARAM_FULL(mode, port, COMPHY_FW_SPEED_MAX, 0, 0) + +#define COMPHY_FW_PARAM_ETH(mode, port, speed) \ + COMPHY_FW_PARAM_FULL(mode, port, speed, 0, 0) + +#define COMPHY_FW_PARAM_PCIE(mode, port, width) \ + COMPHY_FW_PARAM_FULL(mode, port, COMPHY_FW_SPEED_5000, 0, width) + +#define COMPHY_FW_MODE_SATA 0x1 +#define COMPHY_FW_MODE_SGMII 0x2 /* SGMII 1G */ +#define COMPHY_FW_MODE_HS_SGMII 0x3 /* SGMII 2.5G */ +#define COMPHY_FW_MODE_USB3H 0x4 +#define COMPHY_FW_MODE_USB3D 0x5 +#define COMPHY_FW_MODE_PCIE 0x6 +#define COMPHY_FW_MODE_RXAUI 0x7 +#define COMPHY_FW_MODE_XFI 0x8 /* SFI: 0x9 (is treated like XFI) */ + struct mvebu_comphy_conf { enum phy_mode mode; int submode; unsigned lane; unsigned port; u32 mux; + u32 fw_mode; }; -#define MVEBU_COMPHY_CONF(_lane, _port, _submode, _mux) \ +#define ETH_CONF(_lane, _port, _submode, _mux, _fw) \ { \ .lane = _lane, \ .port = _port, \ .mode = PHY_MODE_ETHERNET, \ .submode = _submode, \ .mux = _mux, \ + .fw_mode = _fw, \ + } + +#define GEN_CONF(_lane, _port, _mode, _fw) \ + { \ + .lane = _lane, \ + .port = _port, \ + .mode = _mode, \ + .submode = PHY_INTERFACE_MODE_NA, \ + .mux = -1, \ + .fw_mode = _fw, \ } static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = { /* lane 0 */ - MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1), + GEN_CONF(0, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), + ETH_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + ETH_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + GEN_CONF(0, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), /* lane 1 */ - MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1), + GEN_CONF(1, 0, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), + GEN_CONF(1, 0, PHY_MODE_USB_DEVICE_SS, COMPHY_FW_MODE_USB3D), + GEN_CONF(1, 0, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), + GEN_CONF(1, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), + ETH_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + ETH_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), /* lane 2 */ - MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1), - MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1), + ETH_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + ETH_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(2, 0, PHY_INTERFACE_MODE_RXAUI, 0x1, COMPHY_FW_MODE_RXAUI), + ETH_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1, COMPHY_FW_MODE_XFI), + GEN_CONF(2, 0, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), + GEN_CONF(2, 0, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), + GEN_CONF(2, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), /* lane 3 */ - MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2), - MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2), + GEN_CONF(3, 0, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), + ETH_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII), + ETH_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(3, 1, PHY_INTERFACE_MODE_RXAUI, 0x1, COMPHY_FW_MODE_RXAUI), + GEN_CONF(3, 1, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), + GEN_CONF(3, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), /* lane 4 */ - MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2), - MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2), - MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2), - MVEBU_COMPHY_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1), + ETH_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2, COMPHY_FW_MODE_SGMII), + ETH_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2, COMPHY_FW_MODE_XFI), + ETH_CONF(4, 0, PHY_INTERFACE_MODE_RXAUI, 0x2, COMPHY_FW_MODE_RXAUI), + GEN_CONF(4, 0, PHY_MODE_USB_DEVICE_SS, COMPHY_FW_MODE_USB3D), + GEN_CONF(4, 1, PHY_MODE_USB_HOST_SS, COMPHY_FW_MODE_USB3H), + GEN_CONF(4, 1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), + ETH_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + ETH_CONF(4, 1, PHY_INTERFACE_MODE_2500BASEX, -1, COMPHY_FW_MODE_HS_SGMII), + ETH_CONF(4, 1, PHY_INTERFACE_MODE_10GKR, -1, COMPHY_FW_MODE_XFI), /* lane 5 */ - MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1), - MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1), + ETH_CONF(5, 1, PHY_INTERFACE_MODE_RXAUI, 0x2, COMPHY_FW_MODE_RXAUI), + GEN_CONF(5, 1, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), + ETH_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1, COMPHY_FW_MODE_SGMII), + ETH_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1, COMPHY_FW_MODE_HS_SGMII), + GEN_CONF(5, 2, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), }; struct mvebu_comphy_priv { void __iomem *base; struct regmap *regmap; struct device *dev; + struct clk *mg_domain_clk; + struct clk *mg_core_clk; + struct clk *axi_clk; + unsigned long cp_phys; }; struct mvebu_comphy_lane { @@ -170,30 +269,59 @@ struct mvebu_comphy_lane { int port; }; -static int mvebu_comphy_get_mux(int lane, int port, - enum phy_mode mode, int submode) +static int mvebu_comphy_smc(unsigned long function, unsigned long phys, + unsigned long lane, unsigned long mode) +{ + struct arm_smccc_res res; + + arm_smccc_smc(function, phys, lane, mode, 0, 0, 0, 0, &res); + + return res.a0; +} + +static int mvebu_comphy_get_mode(bool fw_mode, int lane, int port, + enum phy_mode mode, int submode) { int i, n = ARRAY_SIZE(mvebu_comphy_cp110_modes); + /* Ignore PCIe submode: it represents the width */ + bool ignore_submode = (mode == PHY_MODE_PCIE); + const struct mvebu_comphy_conf *conf; /* Unused PHY mux value is 0x0 */ if (mode == PHY_MODE_INVALID) return 0; for (i = 0; i < n; i++) { - if (mvebu_comphy_cp110_modes[i].lane == lane && - mvebu_comphy_cp110_modes[i].port == port && - mvebu_comphy_cp110_modes[i].mode == mode && - mvebu_comphy_cp110_modes[i].submode == submode) + conf = &mvebu_comphy_cp110_modes[i]; + if (conf->lane == lane && + conf->port == port && + conf->mode == mode && + (conf->submode == submode || ignore_submode)) break; } if (i == n) return -EINVAL; - return mvebu_comphy_cp110_modes[i].mux; + if (fw_mode) + return conf->fw_mode; + else + return conf->mux; } -static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane) +static inline int mvebu_comphy_get_mux(int lane, int port, + enum phy_mode mode, int submode) +{ + return mvebu_comphy_get_mode(false, lane, port, mode, submode); +} + +static inline int mvebu_comphy_get_fw_mode(int lane, int port, + enum phy_mode mode, int submode) +{ + return mvebu_comphy_get_mode(true, lane, port, mode, submode); +} + +static int mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane) { struct mvebu_comphy_priv *priv = lane->priv; u32 val; @@ -210,20 +338,61 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane) MVEBU_COMPHY_SERDES_CFG0_PU_TX | MVEBU_COMPHY_SERDES_CFG0_HALF_BUS | MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xf) | - MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xf)); - if (lane->submode == PHY_INTERFACE_MODE_10GKR) + MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xf) | + MVEBU_COMPHY_SERDES_CFG0_RXAUI_MODE); + + switch (lane->submode) { + case PHY_INTERFACE_MODE_10GKR: val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xe) | MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xe); - else if (lane->submode == PHY_INTERFACE_MODE_2500BASEX) + break; + case PHY_INTERFACE_MODE_RXAUI: + val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xb) | + MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xb) | + MVEBU_COMPHY_SERDES_CFG0_RXAUI_MODE; + break; + case PHY_INTERFACE_MODE_2500BASEX: val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x8) | MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x8) | MVEBU_COMPHY_SERDES_CFG0_HALF_BUS; - else if (lane->submode == PHY_INTERFACE_MODE_SGMII) + break; + case PHY_INTERFACE_MODE_SGMII: val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x6) | MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x6) | MVEBU_COMPHY_SERDES_CFG0_HALF_BUS; + break; + default: + dev_err(priv->dev, + "unsupported comphy submode (%d) on lane %d\n", + lane->submode, + lane->id); + return -ENOTSUPP; + } + writel(val, priv->base + MVEBU_COMPHY_SERDES_CFG0(lane->id)); + if (lane->submode == PHY_INTERFACE_MODE_RXAUI) { + regmap_read(priv->regmap, MVEBU_COMPHY_SD1_CTRL1, &val); + + switch (lane->id) { + case 2: + case 3: + val |= MVEBU_COMPHY_SD1_CTRL1_RXAUI0_EN; + break; + case 4: + case 5: + val |= MVEBU_COMPHY_SD1_CTRL1_RXAUI1_EN; + break; + default: + dev_err(priv->dev, + "RXAUI is not supported on comphy lane %d\n", + lane->id); + return -EINVAL; + } + + regmap_write(priv->regmap, MVEBU_COMPHY_SD1_CTRL1, val); + } + /* reset */ val = readl(priv->base + MVEBU_COMPHY_SERDES_CFG1(lane->id)); val &= ~(MVEBU_COMPHY_SERDES_CFG1_RESET | @@ -264,6 +433,8 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane) val &= ~MVEBU_COMPHY_LOOPBACK_DBUS_WIDTH(0x7); val |= MVEBU_COMPHY_LOOPBACK_DBUS_WIDTH(0x1); writel(val, priv->base + MVEBU_COMPHY_LOOPBACK(lane->id)); + + return 0; } static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane) @@ -312,17 +483,20 @@ static int mvebu_comphy_set_mode_sgmii(struct phy *phy) struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); struct mvebu_comphy_priv *priv = lane->priv; u32 val; + int err; - mvebu_comphy_ethernet_init_reset(lane); + err = mvebu_comphy_ethernet_init_reset(lane); + if (err) + return err; val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); val &= ~MVEBU_COMPHY_RX_CTRL1_CLK8T_EN; val |= MVEBU_COMPHY_RX_CTRL1_RXCLK2X_SEL; writel(val, priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); - val = readl(priv->base + MVEBU_COMPHY_DLT_CTRL(lane->id)); - val &= ~MVEBU_COMPHY_DLT_CTRL_DTL_FLOOP_EN; - writel(val, priv->base + MVEBU_COMPHY_DLT_CTRL(lane->id)); + val = readl(priv->base + MVEBU_COMPHY_DTL_CTRL(lane->id)); + val &= ~MVEBU_COMPHY_DTL_CTRL_DTL_FLOOP_EN; + writel(val, priv->base + MVEBU_COMPHY_DTL_CTRL(lane->id)); regmap_read(priv->regmap, MVEBU_COMPHY_CONF1(lane->id), &val); val &= ~MVEBU_COMPHY_CONF1_USB_PCIE; @@ -337,22 +511,78 @@ static int mvebu_comphy_set_mode_sgmii(struct phy *phy) return mvebu_comphy_init_plls(lane); } +static int mvebu_comphy_set_mode_rxaui(struct phy *phy) +{ + struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); + struct mvebu_comphy_priv *priv = lane->priv; + u32 val; + int err; + + err = mvebu_comphy_ethernet_init_reset(lane); + if (err) + return err; + + val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); + val |= MVEBU_COMPHY_RX_CTRL1_RXCLK2X_SEL | + MVEBU_COMPHY_RX_CTRL1_CLK8T_EN; + writel(val, priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); + + val = readl(priv->base + MVEBU_COMPHY_DTL_CTRL(lane->id)); + val |= MVEBU_COMPHY_DTL_CTRL_DTL_FLOOP_EN; + writel(val, priv->base + MVEBU_COMPHY_DTL_CTRL(lane->id)); + + val = readl(priv->base + MVEBU_COMPHY_SERDES_CFG2(lane->id)); + val |= MVEBU_COMPHY_SERDES_CFG2_DFE_EN; + writel(val, priv->base + MVEBU_COMPHY_SERDES_CFG2(lane->id)); + + val = readl(priv->base + MVEBU_COMPHY_DFE_RES(lane->id)); + val |= MVEBU_COMPHY_DFE_RES_FORCE_GEN_TBL; + writel(val, priv->base + MVEBU_COMPHY_DFE_RES(lane->id)); + + val = readl(priv->base + MVEBU_COMPHY_GEN1_S0(lane->id)); + val &= ~MVEBU_COMPHY_GEN1_S0_TX_EMPH(0xf); + val |= MVEBU_COMPHY_GEN1_S0_TX_EMPH(0xd); + writel(val, priv->base + MVEBU_COMPHY_GEN1_S0(lane->id)); + + val = readl(priv->base + MVEBU_COMPHY_GEN1_S1(lane->id)); + val &= ~(MVEBU_COMPHY_GEN1_S1_RX_MUL_PI(0x7) | + MVEBU_COMPHY_GEN1_S1_RX_MUL_PF(0x7)); + val |= MVEBU_COMPHY_GEN1_S1_RX_MUL_PI(0x1) | + MVEBU_COMPHY_GEN1_S1_RX_MUL_PF(0x1) | + MVEBU_COMPHY_GEN1_S1_RX_DFE_EN; + writel(val, priv->base + MVEBU_COMPHY_GEN1_S1(lane->id)); + + val = readl(priv->base + MVEBU_COMPHY_COEF(lane->id)); + val &= ~(MVEBU_COMPHY_COEF_DFE_EN | MVEBU_COMPHY_COEF_DFE_CTRL); + writel(val, priv->base + MVEBU_COMPHY_COEF(lane->id)); + + val = readl(priv->base + MVEBU_COMPHY_GEN1_S4(lane->id)); + val &= ~MVEBU_COMPHY_GEN1_S4_DFE_RES(0x3); + val |= MVEBU_COMPHY_GEN1_S4_DFE_RES(0x1); + writel(val, priv->base + MVEBU_COMPHY_GEN1_S4(lane->id)); + + return mvebu_comphy_init_plls(lane); +} + static int mvebu_comphy_set_mode_10gkr(struct phy *phy) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); struct mvebu_comphy_priv *priv = lane->priv; u32 val; + int err; - mvebu_comphy_ethernet_init_reset(lane); + err = mvebu_comphy_ethernet_init_reset(lane); + if (err) + return err; val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); val |= MVEBU_COMPHY_RX_CTRL1_RXCLK2X_SEL | MVEBU_COMPHY_RX_CTRL1_CLK8T_EN; writel(val, priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id)); - val = readl(priv->base + MVEBU_COMPHY_DLT_CTRL(lane->id)); - val |= MVEBU_COMPHY_DLT_CTRL_DTL_FLOOP_EN; - writel(val, priv->base + MVEBU_COMPHY_DLT_CTRL(lane->id)); + val = readl(priv->base + MVEBU_COMPHY_DTL_CTRL(lane->id)); + val |= MVEBU_COMPHY_DTL_CTRL_DTL_FLOOP_EN; + writel(val, priv->base + MVEBU_COMPHY_DTL_CTRL(lane->id)); /* Speed divider */ val = readl(priv->base + MVEBU_COMPHY_SPEED_DIV(lane->id)); @@ -476,7 +706,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy) return mvebu_comphy_init_plls(lane); } -static int mvebu_comphy_power_on(struct phy *phy) +static int mvebu_comphy_power_on_legacy(struct phy *phy) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); struct mvebu_comphy_priv *priv = lane->priv; @@ -502,6 +732,9 @@ static int mvebu_comphy_power_on(struct phy *phy) case PHY_INTERFACE_MODE_2500BASEX: ret = mvebu_comphy_set_mode_sgmii(phy); break; + case PHY_INTERFACE_MODE_RXAUI: + ret = mvebu_comphy_set_mode_rxaui(phy); + break; case PHY_INTERFACE_MODE_10GKR: ret = mvebu_comphy_set_mode_10gkr(phy); break; @@ -517,26 +750,110 @@ static int mvebu_comphy_power_on(struct phy *phy) return ret; } +static int mvebu_comphy_power_on(struct phy *phy) +{ + struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); + struct mvebu_comphy_priv *priv = lane->priv; + int fw_mode, fw_speed; + u32 fw_param = 0; + int ret; + + fw_mode = mvebu_comphy_get_fw_mode(lane->id, lane->port, + lane->mode, lane->submode); + if (fw_mode < 0) + goto try_legacy; + + /* Try SMC flow first */ + switch (lane->mode) { + case PHY_MODE_ETHERNET: + switch (lane->submode) { + case PHY_INTERFACE_MODE_RXAUI: + dev_dbg(priv->dev, "set lane %d to RXAUI mode\n", + lane->id); + fw_speed = 0; + break; + case PHY_INTERFACE_MODE_SGMII: + dev_dbg(priv->dev, "set lane %d to 1000BASE-X mode\n", + lane->id); + fw_speed = COMPHY_FW_SPEED_1250; + break; + case PHY_INTERFACE_MODE_2500BASEX: + dev_dbg(priv->dev, "set lane %d to 2500BASE-X mode\n", + lane->id); + fw_speed = COMPHY_FW_SPEED_3125; + break; + case PHY_INTERFACE_MODE_10GKR: + dev_dbg(priv->dev, "set lane %d to 10G-KR mode\n", + lane->id); + fw_speed = COMPHY_FW_SPEED_103125; + break; + default: + dev_err(priv->dev, "unsupported Ethernet mode (%d)\n", + lane->submode); + return -ENOTSUPP; + } + fw_param = COMPHY_FW_PARAM_ETH(fw_mode, lane->port, fw_speed); + break; + case PHY_MODE_USB_HOST_SS: + case PHY_MODE_USB_DEVICE_SS: + dev_dbg(priv->dev, "set lane %d to USB3 mode\n", lane->id); + fw_param = COMPHY_FW_PARAM(fw_mode, lane->port); + break; + case PHY_MODE_SATA: + dev_dbg(priv->dev, "set lane %d to SATA mode\n", lane->id); + fw_param = COMPHY_FW_PARAM(fw_mode, lane->port); + break; + case PHY_MODE_PCIE: + dev_dbg(priv->dev, "set lane %d to PCIe mode (x%d)\n", lane->id, + lane->submode); + fw_param = COMPHY_FW_PARAM_PCIE(fw_mode, lane->port, + lane->submode); + break; + default: + dev_err(priv->dev, "unsupported PHY mode (%d)\n", lane->mode); + return -ENOTSUPP; + } + + ret = mvebu_comphy_smc(COMPHY_SIP_POWER_ON, priv->cp_phys, lane->id, + fw_param); + if (!ret) + return ret; + + if (ret == COMPHY_FW_NOT_SUPPORTED) + dev_err(priv->dev, + "unsupported SMC call, try updating your firmware\n"); + + dev_warn(priv->dev, + "Firmware could not configure PHY %d with mode %d (ret: %d), trying legacy method\n", + lane->id, lane->mode, ret); + +try_legacy: + /* Fallback to Linux's implementation */ + return mvebu_comphy_power_on_legacy(phy); +} + static int mvebu_comphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); - if (mode != PHY_MODE_ETHERNET) - return -EINVAL; - if (submode == PHY_INTERFACE_MODE_1000BASEX) submode = PHY_INTERFACE_MODE_SGMII; - if (mvebu_comphy_get_mux(lane->id, lane->port, mode, submode) < 0) + if (mvebu_comphy_get_fw_mode(lane->id, lane->port, mode, submode) < 0) return -EINVAL; lane->mode = mode; lane->submode = submode; + + /* PCIe submode represents the width */ + if (mode == PHY_MODE_PCIE && !lane->submode) + lane->submode = 1; + return 0; } -static int mvebu_comphy_power_off(struct phy *phy) +static int mvebu_comphy_power_off_legacy(struct phy *phy) { struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); struct mvebu_comphy_priv *priv = lane->priv; @@ -559,6 +876,21 @@ static int mvebu_comphy_power_off(struct phy *phy) return 0; } +static int mvebu_comphy_power_off(struct phy *phy) +{ + struct mvebu_comphy_lane *lane = phy_get_drvdata(phy); + struct mvebu_comphy_priv *priv = lane->priv; + int ret; + + ret = mvebu_comphy_smc(COMPHY_SIP_POWER_OFF, priv->cp_phys, + lane->id, 0); + if (!ret) + return ret; + + /* Fallback to Linux's implementation */ + return mvebu_comphy_power_off_legacy(phy); +} + static const struct phy_ops mvebu_comphy_ops = { .power_on = mvebu_comphy_power_on, .power_off = mvebu_comphy_power_off, @@ -585,12 +917,72 @@ static struct phy *mvebu_comphy_xlate(struct device *dev, return phy; } +static int mvebu_comphy_init_clks(struct mvebu_comphy_priv *priv) +{ + int ret; + + priv->mg_domain_clk = devm_clk_get(priv->dev, "mg_clk"); + if (IS_ERR(priv->mg_domain_clk)) + return PTR_ERR(priv->mg_domain_clk); + + ret = clk_prepare_enable(priv->mg_domain_clk); + if (ret < 0) + return ret; + + priv->mg_core_clk = devm_clk_get(priv->dev, "mg_core_clk"); + if (IS_ERR(priv->mg_core_clk)) { + ret = PTR_ERR(priv->mg_core_clk); + goto dis_mg_domain_clk; + } + + ret = clk_prepare_enable(priv->mg_core_clk); + if (ret < 0) + goto dis_mg_domain_clk; + + priv->axi_clk = devm_clk_get(priv->dev, "axi_clk"); + if (IS_ERR(priv->axi_clk)) { + ret = PTR_ERR(priv->axi_clk); + goto dis_mg_core_clk; + } + + ret = clk_prepare_enable(priv->axi_clk); + if (ret < 0) + goto dis_mg_core_clk; + + return 0; + +dis_mg_core_clk: + clk_disable_unprepare(priv->mg_core_clk); + +dis_mg_domain_clk: + clk_disable_unprepare(priv->mg_domain_clk); + + priv->mg_domain_clk = NULL; + priv->mg_core_clk = NULL; + priv->axi_clk = NULL; + + return ret; +}; + +static void mvebu_comphy_disable_unprepare_clks(struct mvebu_comphy_priv *priv) +{ + if (priv->axi_clk) + clk_disable_unprepare(priv->axi_clk); + + if (priv->mg_core_clk) + clk_disable_unprepare(priv->mg_core_clk); + + if (priv->mg_domain_clk) + clk_disable_unprepare(priv->mg_domain_clk); +} + static int mvebu_comphy_probe(struct platform_device *pdev) { struct mvebu_comphy_priv *priv; struct phy_provider *provider; struct device_node *child; struct resource *res; + int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -607,10 +999,26 @@ static int mvebu_comphy_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); + /* + * Ignore error if clocks have not been initialized properly for DT + * compatibility reasons. + */ + ret = mvebu_comphy_init_clks(priv); + if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + dev_warn(&pdev->dev, "cannot initialize clocks\n"); + } + + /* + * Hack to retrieve a physical offset relative to this CP that will be + * given to the firmware + */ + priv->cp_phys = res->start; + for_each_available_child_of_node(pdev->dev.of_node, child) { struct mvebu_comphy_lane *lane; struct phy *phy; - int ret; u32 val; ret = of_property_read_u32(child, "reg", &val); @@ -626,30 +1034,45 @@ static int mvebu_comphy_probe(struct platform_device *pdev) } lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL); - if (!lane) - return -ENOMEM; + if (!lane) { + of_node_put(child); + ret = -ENOMEM; + goto disable_clks; + } phy = devm_phy_create(&pdev->dev, child, &mvebu_comphy_ops); - if (IS_ERR(phy)) - return PTR_ERR(phy); + if (IS_ERR(phy)) { + of_node_put(child); + ret = PTR_ERR(phy); + goto disable_clks; + } lane->priv = priv; lane->mode = PHY_MODE_INVALID; + lane->submode = PHY_INTERFACE_MODE_NA; lane->id = val; lane->port = -1; phy_set_drvdata(phy, lane); /* - * Once all modes are supported in this driver we should call + * All modes are supported in this driver so we could call * mvebu_comphy_power_off(phy) here to avoid relying on the - * bootloader/firmware configuration. + * bootloader/firmware configuration, but for compatibility + * reasons we cannot de-configure the COMPHY without being sure + * that the firmware is up-to-date and fully-featured. */ } dev_set_drvdata(&pdev->dev, priv); provider = devm_of_phy_provider_register(&pdev->dev, mvebu_comphy_xlate); + return PTR_ERR_OR_ZERO(provider); + +disable_clks: + mvebu_comphy_disable_unprepare_clks(priv); + + return ret; } static const struct of_device_id mvebu_comphy_of_match_table[] = { diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index e3880c4a15f2..b04f4fe85ac2 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -394,6 +394,16 @@ int phy_reset(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_reset); +/** + * phy_calibrate() - Tunes the phy hw parameters for current configuration + * @phy: the phy returned by phy_get() + * + * Used to calibrate phy hardware, typically by adjusting some parameters in + * runtime, which are otherwise lost after host controller reset and cannot + * be applied in phy_init() or phy_power_on(). + * + * Returns: 0 if successful, an negative error code otherwise + */ int phy_calibrate(struct phy *phy) { int ret; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 34ff6434da8f..39e8deb8001e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -35,7 +35,7 @@ #define PLL_READY_GATE_EN BIT(3) /* QPHY_PCS_STATUS bit */ #define PHYSTATUS BIT(6) -/* QPHY_COM_PCS_READY_STATUS bit */ +/* QPHY_PCS_READY_STATUS & QPHY_COM_PCS_READY_STATUS bit */ #define PCS_READY BIT(0) /* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ @@ -115,6 +115,7 @@ enum qphy_reg_layout { QPHY_SW_RESET, QPHY_START_CTRL, QPHY_PCS_READY_STATUS, + QPHY_PCS_STATUS, QPHY_PCS_AUTONOMOUS_MODE_CTRL, QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, @@ -133,7 +134,7 @@ static const unsigned int pciephy_regs_layout[] = { [QPHY_FLL_MAN_CODE] = 0xd4, [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_READY_STATUS] = 0x174, + [QPHY_PCS_STATUS] = 0x174, }; static const unsigned int usb3phy_regs_layout[] = { @@ -144,7 +145,7 @@ static const unsigned int usb3phy_regs_layout[] = { [QPHY_FLL_MAN_CODE] = 0xd0, [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_READY_STATUS] = 0x17c, + [QPHY_PCS_STATUS] = 0x17c, [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4, [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8, [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178, @@ -153,7 +154,7 @@ static const unsigned int usb3phy_regs_layout[] = { static const unsigned int qmp_v3_usb3phy_regs_layout[] = { [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_READY_STATUS] = 0x174, + [QPHY_PCS_STATUS] = 0x174, [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8, [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc, [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, @@ -911,7 +912,6 @@ struct qmp_phy_cfg { unsigned int start_ctrl; unsigned int pwrdn_ctrl; - unsigned int mask_pcs_ready; unsigned int mask_com_pcs_ready; /* true, if PHY has a separate PHY_COM control block */ @@ -1074,7 +1074,6 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = { .start_ctrl = PCS_START | PLL_READY_GATE_EN, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, - .mask_pcs_ready = PHYSTATUS, .mask_com_pcs_ready = PCS_READY, .has_phy_com_ctrl = true, @@ -1106,7 +1105,6 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, - .mask_pcs_ready = PHYSTATUS, }; /* list of resets */ @@ -1136,7 +1134,6 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, - .mask_pcs_ready = PHYSTATUS, .has_phy_com_ctrl = false, .has_lane_rst = false, @@ -1167,7 +1164,6 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, - .mask_pcs_ready = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -1199,7 +1195,6 @@ static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, - .mask_pcs_ready = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -1226,7 +1221,6 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { .start_ctrl = SERDES_START, .pwrdn_ctrl = SW_PWRDN, - .mask_pcs_ready = PCS_READY, .is_dual_lane_phy = true, .no_pcs_sw_reset = true, @@ -1254,7 +1248,6 @@ static const struct qmp_phy_cfg msm8998_pciephy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, - .mask_pcs_ready = PHYSTATUS, }; static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { @@ -1279,7 +1272,6 @@ static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, - .mask_pcs_ready = PHYSTATUS, .is_dual_lane_phy = true, }; @@ -1457,7 +1449,7 @@ static int qcom_qmp_phy_enable(struct phy *phy) void __iomem *pcs = qphy->pcs; void __iomem *dp_com = qmp->dp_com; void __iomem *status; - unsigned int mask, val; + unsigned int mask, val, ready; int ret; dev_vdbg(qmp->dev, "Initializing QMP phy\n"); @@ -1545,10 +1537,17 @@ static int qcom_qmp_phy_enable(struct phy *phy) /* start SerDes and Phy-Coding-Sublayer */ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); - status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; - mask = cfg->mask_pcs_ready; + if (cfg->type == PHY_TYPE_UFS) { + status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; + mask = PCS_READY; + ready = PCS_READY; + } else { + status = pcs + cfg->regs[QPHY_PCS_STATUS]; + mask = PHYSTATUS; + ready = 0; + } - ret = readl_poll_timeout(status, val, val & mask, 10, + ret = readl_poll_timeout(status, val, (val & mask) == ready, 10, PHY_INIT_COMPLETE_TIMEOUT); if (ret) { dev_err(qmp->dev, "phy initialization timed-out\n"); @@ -2093,8 +2092,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "failed to create lane%d phy, %d\n", id, ret); - pm_runtime_disable(dev); - return ret; + goto err_node_put; } /* @@ -2105,8 +2103,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) if (ret) { dev_err(qmp->dev, "failed to register pipe clock source\n"); - pm_runtime_disable(dev); - return ret; + goto err_node_put; } id++; } @@ -2118,6 +2115,11 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) pm_runtime_disable(dev); return PTR_ERR_OR_ZERO(phy_provider); + +err_node_put: + pm_runtime_disable(dev); + of_node_put(child); + return ret; } static struct platform_driver qcom_qmp_phy_driver = { diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 8ffba67568ec..b7f6b1324395 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -61,6 +61,7 @@ USB2_OBINT_IDDIGCHG) /* VBCTRL */ +#define USB2_VBCTRL_OCCLREN BIT(16) #define USB2_VBCTRL_DRVVBUSSEL BIT(8) /* LINECTRL1 */ @@ -374,6 +375,7 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) writel(val, usb2_base + USB2_LINECTRL1); val = readl(usb2_base + USB2_VBCTRL); + val &= ~USB2_VBCTRL_OCCLREN; writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); val = readl(usb2_base + USB2_ADPCTRL); writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c index b10a84cab4a7..2b97fb1185a0 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c @@ -198,7 +198,7 @@ #define RK3328_BYPASS_TERM_RESISTOR_CALIB BIT(7) #define RK3328_TERM_RESISTOR_CALIB_SPEED_14_8(x) UPDATE((x) >> 8, 6, 0) /* REG:0xc6 */ -#define RK3328_TERM_RESISTOR_CALIB_SPEED_7_0(x) UPDATE(x, 7, 9) +#define RK3328_TERM_RESISTOR_CALIB_SPEED_7_0(x) UPDATE(x, 7, 0) /* REG:0xc7 */ #define RK3328_TERM_RESISTOR_50 UPDATE(0, 2, 1) #define RK3328_TERM_RESISTOR_62_5 UPDATE(1, 2, 1) diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c index aebd216dcf2f..6c607df1dc9a 100644 --- a/drivers/phy/samsung/phy-exynos-dp-video.c +++ b/drivers/phy/samsung/phy-exynos-dp-video.c @@ -109,6 +109,7 @@ static struct platform_driver exynos_dp_video_phy_driver = { .driver = { .name = "exynos-dp-video-phy", .of_match_table = exynos_dp_video_phy_of_match, + .suppress_bind_attrs = true, } }; module_platform_driver(exynos_dp_video_phy_driver); diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c index 3784bf100b95..bb51195f189f 100644 --- a/drivers/phy/samsung/phy-exynos-mipi-video.c +++ b/drivers/phy/samsung/phy-exynos-mipi-video.c @@ -359,6 +359,7 @@ static struct platform_driver exynos_mipi_video_phy_driver = { .driver = { .of_match_table = exynos_mipi_video_phy_of_match, .name = "exynos-mipi-video-phy", + .suppress_bind_attrs = true, } }; module_platform_driver(exynos_mipi_video_phy_driver); diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c index 1b4ba8bdb43c..659e7ae0a6cf 100644 --- a/drivers/phy/samsung/phy-exynos-pcie.c +++ b/drivers/phy/samsung/phy-exynos-pcie.c @@ -272,6 +272,7 @@ static struct platform_driver exynos_pcie_phy_driver = { .driver = { .of_match_table = exynos_pcie_phy_match, .name = "exynos_pcie_phy", + .suppress_bind_attrs = true, } }; diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 646259bee909..e510732afb8b 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -953,6 +953,7 @@ static struct platform_driver exynos5_usb3drd_phy = { .driver = { .of_match_table = exynos5_usbdrd_phy_of_match, .name = "exynos5_usb3drd_phy", + .suppress_bind_attrs = true, } }; diff --git a/drivers/phy/samsung/phy-exynos5250-sata.c b/drivers/phy/samsung/phy-exynos5250-sata.c index 9e5fc126032c..4dd7324d91b2 100644 --- a/drivers/phy/samsung/phy-exynos5250-sata.c +++ b/drivers/phy/samsung/phy-exynos5250-sata.c @@ -237,6 +237,7 @@ static struct platform_driver exynos_sata_phy_driver = { .driver = { .of_match_table = exynos_sata_phy_of_match, .name = "samsung,sata-phy", + .suppress_bind_attrs = true, } }; module_platform_driver(exynos_sata_phy_driver); diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c index 6c82f4fbe8a2..090aa02e02de 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.c +++ b/drivers/phy/samsung/phy-samsung-usb2.c @@ -250,6 +250,7 @@ static struct platform_driver samsung_usb2_phy_driver = { .driver = { .of_match_table = samsung_usb2_phy_of_match, .name = "samsung-usb2-phy", + .suppress_bind_attrs = true, } }; diff --git a/drivers/phy/ti/phy-am654-serdes.c b/drivers/phy/ti/phy-am654-serdes.c index f8edd0840fa2..f14f1f053a75 100644 --- a/drivers/phy/ti/phy-am654-serdes.c +++ b/drivers/phy/ti/phy-am654-serdes.c @@ -405,6 +405,7 @@ static int serdes_am654_clk_register(struct serdes_am654 *am654_phy, const __be32 *addr; unsigned int reg; struct clk *clk; + int ret = 0; mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); if (!mux) @@ -413,34 +414,40 @@ static int serdes_am654_clk_register(struct serdes_am654 *am654_phy, init = &mux->clk_data; regmap_node = of_parse_phandle(node, "ti,serdes-clk", 0); - of_node_put(regmap_node); if (!regmap_node) { dev_err(dev, "Fail to get serdes-clk node\n"); - return -ENODEV; + ret = -ENODEV; + goto out_put_node; } regmap = syscon_node_to_regmap(regmap_node->parent); if (IS_ERR(regmap)) { dev_err(dev, "Fail to get Syscon regmap\n"); - return PTR_ERR(regmap); + ret = PTR_ERR(regmap); + goto out_put_node; } num_parents = of_clk_get_parent_count(node); if (num_parents < 2) { dev_err(dev, "SERDES clock must have parents\n"); - return -EINVAL; + ret = -EINVAL; + goto out_put_node; } parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL); - if (!parent_names) - return -ENOMEM; + if (!parent_names) { + ret = -ENOMEM; + goto out_put_node; + } of_clk_parent_fill(node, parent_names, num_parents); addr = of_get_address(regmap_node, 0, NULL, NULL); - if (!addr) - return -EINVAL; + if (!addr) { + ret = -EINVAL; + goto out_put_node; + } reg = be32_to_cpu(*addr); @@ -456,12 +463,16 @@ static int serdes_am654_clk_register(struct serdes_am654 *am654_phy, mux->hw.init = init; clk = devm_clk_register(dev, &mux->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto out_put_node; + } am654_phy->clks[clock_num] = clk; - return 0; +out_put_node: + of_node_put(regmap_node); + return ret; } static const struct of_device_id serdes_am654_id_table[] = { diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 3f55cb3c81b2..001187c577bf 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-${CONFIG_THUNDERBOLT} := thunderbolt.o -thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o +thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 2427d73be731..2ec1af8f7968 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -930,6 +930,23 @@ struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, const void *buffer, return res; } +static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space, + const struct tb_cfg_result *res) +{ + /* + * For unimplemented ports access to port config space may return + * TB_CFG_ERROR_INVALID_CONFIG_SPACE (alternatively their type is + * set to TB_TYPE_INACTIVE). In the former case return -ENODEV so + * that the caller can mark the port as disabled. + */ + if (space == TB_CFG_PORT && + res->tb_error == TB_CFG_ERROR_INVALID_CONFIG_SPACE) + return -ENODEV; + + tb_cfg_print_error(ctl, res); + return -EIO; +} + int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, enum tb_cfg_space space, u32 offset, u32 length) { @@ -942,8 +959,7 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, case 1: /* Thunderbolt error, tb_error holds the actual number */ - tb_cfg_print_error(ctl, &res); - return -EIO; + return tb_cfg_get_error(ctl, space, &res); case -ETIMEDOUT: tb_ctl_warn(ctl, "timeout reading config space %u from %#x\n", @@ -969,8 +985,7 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port, case 1: /* Thunderbolt error, tb_error holds the actual number */ - tb_cfg_print_error(ctl, &res); - return -EIO; + return tb_cfg_get_error(ctl, space, &res); case -ETIMEDOUT: tb_ctl_warn(ctl, "timeout writing config space %u to %#x\n", diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 81e8ac4c5805..ee5196479854 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -414,7 +414,7 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size) struct device *dev = &sw->tb->nhi->pdev->dev; int len, res; - len = device_property_read_u8_array(dev, "ThunderboltDROM", NULL, 0); + len = device_property_count_u8(dev, "ThunderboltDROM"); if (len < 0 || len < sizeof(struct tb_drom_header)) return -EINVAL; @@ -525,10 +525,6 @@ int tb_drom_read(struct tb_switch *sw) sw->ports[3].dual_link_port = &sw->ports[4]; sw->ports[4].dual_link_port = &sw->ports[3]; - /* Port 5 is inaccessible on this gen 1 controller */ - if (sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE) - sw->ports[5].disabled = true; - return 0; } diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index fbdcef56a676..245588f691e7 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -55,16 +55,20 @@ * @safe_mode: ICM is in safe mode * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) * @rpm: Does the controller support runtime PM (RTD3) + * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller + * @veto: Is RTD3 veto in effect * @is_supported: Checks if we can support ICM on this controller * @cio_reset: Trigger CIO reset * @get_mode: Read and return the ICM firmware mode (optional) * @get_route: Find a route string for given switch * @save_devices: Ask ICM to save devices to ACL when suspending (optional) * @driver_ready: Send driver ready message to ICM + * @set_uuid: Set UUID for the root switch (optional) * @device_connected: Handle device connected ICM message * @device_disconnected: Handle device disconnected ICM message * @xdomain_connected - Handle XDomain connected ICM message * @xdomain_disconnected - Handle XDomain disconnected ICM message + * @rtd3_veto: Handle RTD3 veto notification ICM message */ struct icm { struct mutex request_lock; @@ -74,6 +78,8 @@ struct icm { int vnd_cap; bool safe_mode; bool rpm; + bool can_upgrade_nvm; + bool veto; bool (*is_supported)(struct tb *tb); int (*cio_reset)(struct tb *tb); int (*get_mode)(struct tb *tb); @@ -82,6 +88,7 @@ struct icm { int (*driver_ready)(struct tb *tb, enum tb_security_level *security_level, size_t *nboot_acl, bool *rpm); + void (*set_uuid)(struct tb *tb); void (*device_connected)(struct tb *tb, const struct icm_pkg_header *hdr); void (*device_disconnected)(struct tb *tb, @@ -90,6 +97,7 @@ struct icm { const struct icm_pkg_header *hdr); void (*xdomain_disconnected)(struct tb *tb, const struct icm_pkg_header *hdr); + void (*rtd3_veto)(struct tb *tb, const struct icm_pkg_header *hdr); }; struct icm_notification { @@ -294,6 +302,43 @@ static int icm_request(struct tb *tb, const void *request, size_t request_size, return -ETIMEDOUT; } +/* + * If rescan is queued to run (we are resuming), postpone it to give the + * firmware some more time to send device connected notifications for next + * devices in the chain. + */ +static void icm_postpone_rescan(struct tb *tb) +{ + struct icm *icm = tb_priv(tb); + + if (delayed_work_pending(&icm->rescan_work)) + mod_delayed_work(tb->wq, &icm->rescan_work, + msecs_to_jiffies(500)); +} + +static void icm_veto_begin(struct tb *tb) +{ + struct icm *icm = tb_priv(tb); + + if (!icm->veto) { + icm->veto = true; + /* Keep the domain powered while veto is in effect */ + pm_runtime_get(&tb->dev); + } +} + +static void icm_veto_end(struct tb *tb) +{ + struct icm *icm = tb_priv(tb); + + if (icm->veto) { + icm->veto = false; + /* Allow the domain suspend now */ + pm_runtime_mark_last_busy(&tb->dev); + pm_runtime_put_autosuspend(&tb->dev); + } +} + static bool icm_fr_is_supported(struct tb *tb) { return !x86_apple_machine; @@ -517,14 +562,16 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) return 0; } -static void add_switch(struct tb_switch *parent_sw, u64 route, - const uuid_t *uuid, const u8 *ep_name, - size_t ep_name_size, u8 connection_id, u8 connection_key, - u8 link, u8 depth, enum tb_security_level security_level, - bool authorized, bool boot) +static struct tb_switch *add_switch(struct tb_switch *parent_sw, u64 route, + const uuid_t *uuid, const u8 *ep_name, + size_t ep_name_size, u8 connection_id, + u8 connection_key, u8 link, u8 depth, + enum tb_security_level security_level, + bool authorized, bool boot) { const struct intel_vss *vss; struct tb_switch *sw; + int ret; pm_runtime_get_sync(&parent_sw->dev); @@ -555,14 +602,18 @@ static void add_switch(struct tb_switch *parent_sw, u64 route, tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); - if (tb_switch_add(sw)) { + ret = tb_switch_add(sw); + if (ret) { tb_port_at(tb_route(sw), parent_sw)->remote = NULL; tb_switch_put(sw); + sw = ERR_PTR(ret); } out: pm_runtime_mark_last_busy(&parent_sw->dev); pm_runtime_put_autosuspend(&parent_sw->dev); + + return sw; } static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, @@ -654,6 +705,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) u64 route; int ret; + icm_postpone_rescan(tb); + link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> ICM_LINK_INFO_DEPTH_SHIFT; @@ -1084,7 +1137,8 @@ static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) } static void -icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) +__icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, + bool force_rtd3) { const struct icm_tr_event_device_connected *pkg = (const struct icm_tr_event_device_connected *)hdr; @@ -1094,6 +1148,8 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) bool authorized, boot; u64 route; + icm_postpone_rescan(tb); + /* * Currently we don't use the QoS information coming with the * device connected message so simply just ignore that extra @@ -1149,14 +1205,22 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) return; } - add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name, - sizeof(pkg->ep_name), pkg->connection_id, - 0, 0, 0, security_level, authorized, boot); + sw = add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name, + sizeof(pkg->ep_name), pkg->connection_id, 0, 0, 0, + security_level, authorized, boot); + if (!IS_ERR(sw) && force_rtd3) + sw->rpm = true; tb_switch_put(parent_sw); } static void +icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) +{ + __icm_tr_device_connected(tb, hdr, false); +} + +static void icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) { const struct icm_tr_event_device_disconnected *pkg = @@ -1466,6 +1530,61 @@ static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, return 0; } +static int +icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level, + size_t *nboot_acl, bool *rpm) +{ + struct icm_tr_pkg_driver_ready_response reply; + struct icm_pkg_driver_ready request = { + .hdr.code = ICM_DRIVER_READY, + }; + int ret; + + memset(&reply, 0, sizeof(reply)); + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), + 1, 20000); + if (ret) + return ret; + + /* Ice Lake always supports RTD3 */ + if (rpm) + *rpm = true; + + return 0; +} + +static void icm_icl_set_uuid(struct tb *tb) +{ + struct tb_nhi *nhi = tb->nhi; + u32 uuid[4]; + + pci_read_config_dword(nhi->pdev, VS_CAP_10, &uuid[0]); + pci_read_config_dword(nhi->pdev, VS_CAP_11, &uuid[1]); + uuid[2] = 0xffffffff; + uuid[3] = 0xffffffff; + + tb->root_switch->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL); +} + +static void +icm_icl_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) +{ + __icm_tr_device_connected(tb, hdr, true); +} + +static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr) +{ + const struct icm_icl_event_rtd3_veto *pkg = + (const struct icm_icl_event_rtd3_veto *)hdr; + + tb_dbg(tb, "ICM rtd3 veto=0x%08x\n", pkg->veto_reason); + + if (pkg->veto_reason) + icm_veto_begin(tb); + else + icm_veto_end(tb); +} + static void icm_handle_notification(struct work_struct *work) { struct icm_notification *n = container_of(work, typeof(*n), work); @@ -1493,6 +1612,9 @@ static void icm_handle_notification(struct work_struct *work) case ICM_EVENT_XDOMAIN_DISCONNECTED: icm->xdomain_disconnected(tb, n->pkg); break; + case ICM_EVENT_RTD3_VETO: + icm->rtd3_veto(tb, n->pkg); + break; } } @@ -1851,6 +1973,13 @@ static void icm_complete(struct tb *tb) if (tb->nhi->going_away) return; + /* + * If RTD3 was vetoed before we entered system suspend allow it + * again now before driver ready is sent. Firmware sends a new RTD3 + * veto if it is still the case after we have sent it driver ready + * command. + */ + icm_veto_end(tb); icm_unplug_children(tb->root_switch); /* @@ -1913,14 +2042,12 @@ static int icm_start(struct tb *tb) if (IS_ERR(tb->root_switch)) return PTR_ERR(tb->root_switch); - /* - * NVM upgrade has not been tested on Apple systems and they - * don't provide images publicly either. To be on the safe side - * prevent root switch NVM upgrade on Macs for now. - */ - tb->root_switch->no_nvm_upgrade = x86_apple_machine; + tb->root_switch->no_nvm_upgrade = !icm->can_upgrade_nvm; tb->root_switch->rpm = icm->rpm; + if (icm->set_uuid) + icm->set_uuid(tb); + ret = tb_switch_add(tb->root_switch); if (ret) { tb_switch_put(tb->root_switch); @@ -2005,6 +2132,19 @@ static const struct tb_cm_ops icm_tr_ops = { .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, }; +/* Ice Lake */ +static const struct tb_cm_ops icm_icl_ops = { + .driver_ready = icm_driver_ready, + .start = icm_start, + .stop = icm_stop, + .complete = icm_complete, + .runtime_suspend = icm_runtime_suspend, + .runtime_resume = icm_runtime_resume, + .handle_event = icm_handle_event, + .approve_xdomain_paths = icm_tr_approve_xdomain_paths, + .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths, +}; + struct tb *icm_probe(struct tb_nhi *nhi) { struct icm *icm; @@ -2021,6 +2161,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) switch (nhi->pdev->device) { case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: + icm->can_upgrade_nvm = true; icm->is_supported = icm_fr_is_supported; icm->get_route = icm_fr_get_route; icm->save_devices = icm_fr_save_devices; @@ -2038,6 +2179,13 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; + /* + * NVM upgrade has not been tested on Apple systems and + * they don't provide images publicly either. To be on + * the safe side prevent root switch NVM upgrade on Macs + * for now. + */ + icm->can_upgrade_nvm = !x86_apple_machine; icm->is_supported = icm_ar_is_supported; icm->cio_reset = icm_ar_cio_reset; icm->get_mode = icm_ar_get_mode; @@ -2054,6 +2202,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI: case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI: icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; + icm->can_upgrade_nvm = !x86_apple_machine; icm->is_supported = icm_ar_is_supported; icm->cio_reset = icm_tr_cio_reset; icm->get_mode = icm_ar_get_mode; @@ -2064,6 +2213,19 @@ struct tb *icm_probe(struct tb_nhi *nhi) icm->xdomain_disconnected = icm_tr_xdomain_disconnected; tb->cm_ops = &icm_tr_ops; break; + + case PCI_DEVICE_ID_INTEL_ICL_NHI0: + case PCI_DEVICE_ID_INTEL_ICL_NHI1: + icm->is_supported = icm_ar_is_supported; + icm->driver_ready = icm_icl_driver_ready; + icm->set_uuid = icm_icl_set_uuid; + icm->device_connected = icm_icl_device_connected; + icm->device_disconnected = icm_tr_device_disconnected; + icm->xdomain_connected = icm_tr_xdomain_connected; + icm->xdomain_disconnected = icm_tr_xdomain_disconnected; + icm->rtd3_veto = icm_icl_rtd3_veto; + tb->cm_ops = &icm_icl_ops; + break; } if (!icm->is_supported || !icm->is_supported(tb)) { diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 27fbe62c7ddd..641b21b54460 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/property.h> #include "nhi.h" #include "nhi_regs.h" @@ -143,9 +144,20 @@ static void __iomem *ring_options_base(struct tb_ring *ring) return io; } -static void ring_iowrite16desc(struct tb_ring *ring, u32 value, u32 offset) +static void ring_iowrite_cons(struct tb_ring *ring, u16 cons) { - iowrite16(value, ring_desc_base(ring) + offset); + /* + * The other 16-bits in the register is read-only and writes to it + * are ignored by the hardware so we can save one ioread32() by + * filling the read-only bits with zeroes. + */ + iowrite32(cons, ring_desc_base(ring) + 8); +} + +static void ring_iowrite_prod(struct tb_ring *ring, u16 prod) +{ + /* See ring_iowrite_cons() above for explanation */ + iowrite32(prod << 16, ring_desc_base(ring) + 8); } static void ring_iowrite32desc(struct tb_ring *ring, u32 value, u32 offset) @@ -197,7 +209,10 @@ static void ring_write_descriptors(struct tb_ring *ring) descriptor->sof = frame->sof; } ring->head = (ring->head + 1) % ring->size; - ring_iowrite16desc(ring, ring->head, ring->is_tx ? 10 : 8); + if (ring->is_tx) + ring_iowrite_prod(ring, ring->head); + else + ring_iowrite_cons(ring, ring->head); } } @@ -662,7 +677,7 @@ void tb_ring_stop(struct tb_ring *ring) ring_iowrite32options(ring, 0, 0); ring_iowrite64desc(ring, 0, 0); - ring_iowrite16desc(ring, 0, ring->is_tx ? 10 : 8); + ring_iowrite32desc(ring, 0, 8); ring_iowrite32desc(ring, 0, 12); ring->head = 0; ring->tail = 0; @@ -845,12 +860,52 @@ static irqreturn_t nhi_msi(int irq, void *data) return IRQ_HANDLED; } -static int nhi_suspend_noirq(struct device *dev) +static int __nhi_suspend_noirq(struct device *dev, bool wakeup) { struct pci_dev *pdev = to_pci_dev(dev); struct tb *tb = pci_get_drvdata(pdev); + struct tb_nhi *nhi = tb->nhi; + int ret; + + ret = tb_domain_suspend_noirq(tb); + if (ret) + return ret; + + if (nhi->ops && nhi->ops->suspend_noirq) { + ret = nhi->ops->suspend_noirq(tb->nhi, wakeup); + if (ret) + return ret; + } + + return 0; +} + +static int nhi_suspend_noirq(struct device *dev) +{ + return __nhi_suspend_noirq(dev, device_may_wakeup(dev)); +} + +static bool nhi_wake_supported(struct pci_dev *pdev) +{ + u8 val; + + /* + * If power rails are sustainable for wakeup from S4 this + * property is set by the BIOS. + */ + if (device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val)) + return !!val; + + return true; +} + +static int nhi_poweroff_noirq(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + bool wakeup; - return tb_domain_suspend_noirq(tb); + wakeup = device_may_wakeup(dev) && nhi_wake_supported(pdev); + return __nhi_suspend_noirq(dev, wakeup); } static void nhi_enable_int_throttling(struct tb_nhi *nhi) @@ -873,16 +928,24 @@ static int nhi_resume_noirq(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct tb *tb = pci_get_drvdata(pdev); + struct tb_nhi *nhi = tb->nhi; + int ret; /* * Check that the device is still there. It may be that the user * unplugged last device which causes the host controller to go * away on PCs. */ - if (!pci_device_is_present(pdev)) - tb->nhi->going_away = true; - else + if (!pci_device_is_present(pdev)) { + nhi->going_away = true; + } else { + if (nhi->ops && nhi->ops->resume_noirq) { + ret = nhi->ops->resume_noirq(nhi); + if (ret) + return ret; + } nhi_enable_int_throttling(tb->nhi); + } return tb_domain_resume_noirq(tb); } @@ -915,16 +978,35 @@ static int nhi_runtime_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct tb *tb = pci_get_drvdata(pdev); + struct tb_nhi *nhi = tb->nhi; + int ret; + + ret = tb_domain_runtime_suspend(tb); + if (ret) + return ret; - return tb_domain_runtime_suspend(tb); + if (nhi->ops && nhi->ops->runtime_suspend) { + ret = nhi->ops->runtime_suspend(tb->nhi); + if (ret) + return ret; + } + return 0; } static int nhi_runtime_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct tb *tb = pci_get_drvdata(pdev); + struct tb_nhi *nhi = tb->nhi; + int ret; - nhi_enable_int_throttling(tb->nhi); + if (nhi->ops && nhi->ops->runtime_resume) { + ret = nhi->ops->runtime_resume(nhi); + if (ret) + return ret; + } + + nhi_enable_int_throttling(nhi); return tb_domain_runtime_resume(tb); } @@ -952,6 +1034,9 @@ static void nhi_shutdown(struct tb_nhi *nhi) flush_work(&nhi->interrupt_work); } ida_destroy(&nhi->msix_ida); + + if (nhi->ops && nhi->ops->shutdown) + nhi->ops->shutdown(nhi); } static int nhi_init_msi(struct tb_nhi *nhi) @@ -996,12 +1081,27 @@ static int nhi_init_msi(struct tb_nhi *nhi) return 0; } +static bool nhi_imr_valid(struct pci_dev *pdev) +{ + u8 val; + + if (!device_property_read_u8(&pdev->dev, "IMR_VALID", &val)) + return !!val; + + return true; +} + static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct tb_nhi *nhi; struct tb *tb; int res; + if (!nhi_imr_valid(pdev)) { + dev_warn(&pdev->dev, "firmware image not valid, aborting\n"); + return -ENODEV; + } + res = pcim_enable_device(pdev); if (res) { dev_err(&pdev->dev, "cannot enable PCI device, aborting\n"); @@ -1019,6 +1119,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; nhi->pdev = pdev; + nhi->ops = (const struct tb_nhi_ops *)id->driver_data; /* cannot fail - table is allocated bin pcim_iomap_regions */ nhi->iobase = pcim_iomap_table(pdev)[0]; nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff; @@ -1051,6 +1152,12 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); + if (nhi->ops && nhi->ops->init) { + res = nhi->ops->init(nhi); + if (res) + return res; + } + tb = icm_probe(nhi); if (!tb) tb = tb_probe(nhi); @@ -1111,6 +1218,7 @@ static const struct dev_pm_ops nhi_pm_ops = { .restore_noirq = nhi_resume_noirq, .suspend = nhi_suspend, .freeze = nhi_suspend, + .poweroff_noirq = nhi_poweroff_noirq, .poweroff = nhi_suspend, .complete = nhi_complete, .runtime_suspend = nhi_runtime_suspend, @@ -1158,6 +1266,10 @@ static struct pci_device_id nhi_ids[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_USBONLY_NHI) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { 0,} }; diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 1b5d47ecd3ed..b7b973949f8e 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -30,6 +30,26 @@ enum nhi_mailbox_cmd { int nhi_mailbox_cmd(struct tb_nhi *nhi, enum nhi_mailbox_cmd cmd, u32 data); enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi); +/** + * struct tb_nhi_ops - NHI specific optional operations + * @init: NHI specific initialization + * @suspend_noirq: NHI specific suspend_noirq hook + * @resume_noirq: NHI specific resume_noirq hook + * @runtime_suspend: NHI specific runtime_suspend hook + * @runtime_resume: NHI specific runtime_resume hook + * @shutdown: NHI specific shutdown + */ +struct tb_nhi_ops { + int (*init)(struct tb_nhi *nhi); + int (*suspend_noirq)(struct tb_nhi *nhi, bool wakeup); + int (*resume_noirq)(struct tb_nhi *nhi); + int (*runtime_suspend)(struct tb_nhi *nhi); + int (*runtime_resume)(struct tb_nhi *nhi); + void (*shutdown)(struct tb_nhi *nhi); +}; + +extern const struct tb_nhi_ops icl_nhi_ops; + /* * PCI IDs used in this driver from Win Ridge forward. There is no * need for the PCI quirk anymore as we will use ICM also on Apple @@ -51,5 +71,7 @@ enum nhi_fw_mode nhi_mailbox_mode(struct tb_nhi *nhi); #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE 0x15ea #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI 0x15eb #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef +#define PCI_DEVICE_ID_INTEL_ICL_NHI1 0x8a0d +#define PCI_DEVICE_ID_INTEL_ICL_NHI0 0x8a17 #endif diff --git a/drivers/thunderbolt/nhi_ops.c b/drivers/thunderbolt/nhi_ops.c new file mode 100644 index 000000000000..61cd09cef943 --- /dev/null +++ b/drivers/thunderbolt/nhi_ops.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NHI specific operations + * + * Copyright (C) 2019, Intel Corporation + * Author: Mika Westerberg <[email protected]> + */ + +#include <linux/delay.h> +#include <linux/suspend.h> + +#include "nhi.h" +#include "nhi_regs.h" +#include "tb.h" + +/* Ice Lake specific NHI operations */ + +#define ICL_LC_MAILBOX_TIMEOUT 500 /* ms */ + +static int check_for_device(struct device *dev, void *data) +{ + return tb_is_switch(dev); +} + +static bool icl_nhi_is_device_connected(struct tb_nhi *nhi) +{ + struct tb *tb = pci_get_drvdata(nhi->pdev); + int ret; + + ret = device_for_each_child(&tb->root_switch->dev, NULL, + check_for_device); + return ret > 0; +} + +static int icl_nhi_force_power(struct tb_nhi *nhi, bool power) +{ + u32 vs_cap; + + /* + * The Thunderbolt host controller is present always in Ice Lake + * but the firmware may not be loaded and running (depending + * whether there is device connected and so on). Each time the + * controller is used we need to "Force Power" it first and wait + * for the firmware to indicate it is up and running. This "Force + * Power" is really not about actually powering on/off the + * controller so it is accessible even if "Force Power" is off. + * + * The actual power management happens inside shared ACPI power + * resources using standard ACPI methods. + */ + pci_read_config_dword(nhi->pdev, VS_CAP_22, &vs_cap); + if (power) { + vs_cap &= ~VS_CAP_22_DMA_DELAY_MASK; + vs_cap |= 0x22 << VS_CAP_22_DMA_DELAY_SHIFT; + vs_cap |= VS_CAP_22_FORCE_POWER; + } else { + vs_cap &= ~VS_CAP_22_FORCE_POWER; + } + pci_write_config_dword(nhi->pdev, VS_CAP_22, vs_cap); + + if (power) { + unsigned int retries = 10; + u32 val; + + /* Wait until the firmware tells it is up and running */ + do { + pci_read_config_dword(nhi->pdev, VS_CAP_9, &val); + if (val & VS_CAP_9_FW_READY) + return 0; + msleep(250); + } while (--retries); + + return -ETIMEDOUT; + } + + return 0; +} + +static void icl_nhi_lc_mailbox_cmd(struct tb_nhi *nhi, enum icl_lc_mailbox_cmd cmd) +{ + u32 data; + + pci_read_config_dword(nhi->pdev, VS_CAP_19, &data); + data = (cmd << VS_CAP_19_CMD_SHIFT) & VS_CAP_19_CMD_MASK; + pci_write_config_dword(nhi->pdev, VS_CAP_19, data | VS_CAP_19_VALID); +} + +static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout) +{ + unsigned long end; + u32 data; + + if (!timeout) + goto clear; + + end = jiffies + msecs_to_jiffies(timeout); + do { + pci_read_config_dword(nhi->pdev, VS_CAP_18, &data); + if (data & VS_CAP_18_DONE) + goto clear; + msleep(100); + } while (time_before(jiffies, end)); + + return -ETIMEDOUT; + +clear: + /* Clear the valid bit */ + pci_write_config_dword(nhi->pdev, VS_CAP_19, 0); + return 0; +} + +static void icl_nhi_set_ltr(struct tb_nhi *nhi) +{ + u32 max_ltr, ltr; + + pci_read_config_dword(nhi->pdev, VS_CAP_16, &max_ltr); + max_ltr &= 0xffff; + /* Program the same value for both snoop and no-snoop */ + ltr = max_ltr << 16 | max_ltr; + pci_write_config_dword(nhi->pdev, VS_CAP_15, ltr); +} + +static int icl_nhi_suspend(struct tb_nhi *nhi) +{ + int ret; + + if (icl_nhi_is_device_connected(nhi)) + return 0; + + /* + * If there is no device connected we need to perform both: a + * handshake through LC mailbox and force power down before + * entering D3. + */ + icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET); + ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); + if (ret) + return ret; + + return icl_nhi_force_power(nhi, false); +} + +static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup) +{ + enum icl_lc_mailbox_cmd cmd; + + if (!pm_suspend_via_firmware()) + return icl_nhi_suspend(nhi); + + cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE; + icl_nhi_lc_mailbox_cmd(nhi, cmd); + return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT); +} + +static int icl_nhi_resume(struct tb_nhi *nhi) +{ + int ret; + + ret = icl_nhi_force_power(nhi, true); + if (ret) + return ret; + + icl_nhi_set_ltr(nhi); + return 0; +} + +static void icl_nhi_shutdown(struct tb_nhi *nhi) +{ + icl_nhi_force_power(nhi, false); +} + +const struct tb_nhi_ops icl_nhi_ops = { + .init = icl_nhi_resume, + .suspend_noirq = icl_nhi_suspend_noirq, + .resume_noirq = icl_nhi_resume, + .runtime_suspend = icl_nhi_suspend, + .runtime_resume = icl_nhi_resume, + .shutdown = icl_nhi_shutdown, +}; diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index a60bd98c1d04..0d4970dcef84 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -124,4 +124,41 @@ struct ring_desc { #define REG_FW_STS_ICM_EN_INVERT BIT(1) #define REG_FW_STS_ICM_EN BIT(0) +/* ICL NHI VSEC registers */ + +/* FW ready */ +#define VS_CAP_9 0xc8 +#define VS_CAP_9_FW_READY BIT(31) +/* UUID */ +#define VS_CAP_10 0xcc +#define VS_CAP_11 0xd0 +/* LTR */ +#define VS_CAP_15 0xe0 +#define VS_CAP_16 0xe4 +/* TBT2PCIe */ +#define VS_CAP_18 0xec +#define VS_CAP_18_DONE BIT(0) +/* PCIe2TBT */ +#define VS_CAP_19 0xf0 +#define VS_CAP_19_VALID BIT(0) +#define VS_CAP_19_CMD_SHIFT 1 +#define VS_CAP_19_CMD_MASK GENMASK(7, 1) +/* Force power */ +#define VS_CAP_22 0xfc +#define VS_CAP_22_FORCE_POWER BIT(1) +#define VS_CAP_22_DMA_DELAY_MASK GENMASK(31, 24) +#define VS_CAP_22_DMA_DELAY_SHIFT 24 + +/** + * enum icl_lc_mailbox_cmd - ICL specific LC mailbox commands + * @ICL_LC_GO2SX: Ask LC to enter Sx without wake + * @ICL_LC_GO2SX_NO_WAKE: Ask LC to enter Sx with wake + * @ICL_LC_PREPARE_FOR_RESET: Prepare LC for reset + */ +enum icl_lc_mailbox_cmd { + ICL_LC_GO2SX = 0x02, + ICL_LC_GO2SX_NO_WAKE = 0x03, + ICL_LC_PREPARE_FOR_RESET = 0x21, +}; + #endif diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 5668a44e0653..410bf1bceeee 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -364,12 +364,14 @@ static int tb_switch_nvm_add(struct tb_switch *sw) nvm->active = nvm_dev; } - nvm_dev = register_nvmem(sw, nvm->id, NVM_MAX_SIZE, false); - if (IS_ERR(nvm_dev)) { - ret = PTR_ERR(nvm_dev); - goto err_nvm_active; + if (!sw->no_nvm_upgrade) { + nvm_dev = register_nvmem(sw, nvm->id, NVM_MAX_SIZE, false); + if (IS_ERR(nvm_dev)) { + ret = PTR_ERR(nvm_dev); + goto err_nvm_active; + } + nvm->non_active = nvm_dev; } - nvm->non_active = nvm_dev; sw->nvm = nvm; return 0; @@ -398,7 +400,8 @@ static void tb_switch_nvm_remove(struct tb_switch *sw) if (!nvm->authenticating) nvm_clear_auth_status(sw); - nvmem_unregister(nvm->non_active); + if (nvm->non_active) + nvmem_unregister(nvm->non_active); if (nvm->active) nvmem_unregister(nvm->active); ida_simple_remove(&nvm_ida, nvm->id); @@ -611,8 +614,14 @@ static int tb_init_port(struct tb_port *port) int cap; res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); - if (res) + if (res) { + if (res == -ENODEV) { + tb_dbg(port->sw->tb, " Port %d: not implemented\n", + port->port); + return 0; + } return res; + } /* Port 0 is the switch itself and has no PHY. */ if (port->config.type == TB_TYPE_PORT && port->port != 0) { @@ -1331,14 +1340,29 @@ static umode_t switch_attr_is_visible(struct kobject *kobj, struct device *dev = container_of(kobj, struct device, kobj); struct tb_switch *sw = tb_to_switch(dev); - if (attr == &dev_attr_key.attr) { + if (attr == &dev_attr_device.attr) { + if (!sw->device) + return 0; + } else if (attr == &dev_attr_device_name.attr) { + if (!sw->device_name) + return 0; + } else if (attr == &dev_attr_vendor.attr) { + if (!sw->vendor) + return 0; + } else if (attr == &dev_attr_vendor_name.attr) { + if (!sw->vendor_name) + return 0; + } else if (attr == &dev_attr_key.attr) { if (tb_route(sw) && sw->tb->security_level == TB_SECURITY_SECURE && sw->security_level == TB_SECURITY_SECURE) return attr->mode; return 0; - } else if (attr == &dev_attr_nvm_authenticate.attr || - attr == &dev_attr_nvm_version.attr) { + } else if (attr == &dev_attr_nvm_authenticate.attr) { + if (sw->dma_port && !sw->no_nvm_upgrade) + return attr->mode; + return 0; + } else if (attr == &dev_attr_nvm_version.attr) { if (sw->dma_port) return attr->mode; return 0; @@ -1446,6 +1470,8 @@ static int tb_switch_get_generation(struct tb_switch *sw) case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE: + case PCI_DEVICE_ID_INTEL_ICL_NHI0: + case PCI_DEVICE_ID_INTEL_ICL_NHI1: return 3; default: @@ -1689,13 +1715,17 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) break; } - if (sw->no_nvm_upgrade) + /* Root switch DMA port requires running firmware */ + if (!tb_route(sw) && sw->config.enabled) return 0; sw->dma_port = dma_port_alloc(sw); if (!sw->dma_port) return 0; + if (sw->no_nvm_upgrade) + return 0; + /* * Check status of the previous flash authentication. If there * is one we need to power cycle the switch in any case to make diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index afbe1d29bb03..4b641e4ee0c5 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -104,10 +104,11 @@ enum icm_pkg_code { }; enum icm_event_code { - ICM_EVENT_DEVICE_CONNECTED = 3, - ICM_EVENT_DEVICE_DISCONNECTED = 4, - ICM_EVENT_XDOMAIN_CONNECTED = 6, - ICM_EVENT_XDOMAIN_DISCONNECTED = 7, + ICM_EVENT_DEVICE_CONNECTED = 0x3, + ICM_EVENT_DEVICE_DISCONNECTED = 0x4, + ICM_EVENT_XDOMAIN_CONNECTED = 0x6, + ICM_EVENT_XDOMAIN_DISCONNECTED = 0x7, + ICM_EVENT_RTD3_VETO = 0xa, }; struct icm_pkg_header { @@ -463,6 +464,13 @@ struct icm_tr_pkg_disconnect_xdomain_response { uuid_t remote_uuid; }; +/* Ice Lake messages */ + +struct icm_icl_event_rtd3_veto { + struct icm_pkg_header hdr; + u32 veto_reason; +}; + /* XDomain messages */ struct tb_xdomain_header { diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 31d0234837e4..5a99234826e7 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -211,7 +211,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, return NULL; } tb_pci_init_path(path); - tunnel->paths[TB_PCI_PATH_UP] = path; + tunnel->paths[TB_PCI_PATH_DOWN] = path; path = tb_path_alloc(tb, up, TB_PCI_HOPID, down, TB_PCI_HOPID, 0, "PCIe Up"); @@ -220,7 +220,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, return NULL; } tb_pci_init_path(path); - tunnel->paths[TB_PCI_PATH_DOWN] = path; + tunnel->paths[TB_PCI_PATH_UP] = path; return tunnel; } diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 5118d46702d5..4e17a7c7bf0a 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -636,7 +636,7 @@ static ssize_t key_show(struct device *dev, struct device_attribute *attr, * It should be null terminated but anything else is pretty much * allowed. */ - return sprintf(buf, "%*pEp\n", (int)strlen(svc->key), svc->key); + return sprintf(buf, "%*pE\n", (int)strlen(svc->key), svc->key); } static DEVICE_ATTR_RO(key); diff --git a/include/dt-bindings/interconnect/qcom,qcs404.h b/include/dt-bindings/interconnect/qcom,qcs404.h new file mode 100644 index 000000000000..960f6e39c5f2 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,qcs404.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Qualcomm interconnect IDs + * + * Copyright (c) 2019, Linaro Ltd. + * Author: Georgi Djakov <[email protected]> + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_QCS404_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_QCS404_H + +#define MASTER_AMPSS_M0 0 +#define MASTER_OXILI 1 +#define MASTER_MDP_PORT0 2 +#define MASTER_SNOC_BIMC_1 3 +#define MASTER_TCU_0 4 +#define SLAVE_EBI_CH0 5 +#define SLAVE_BIMC_SNOC 6 + +#define MASTER_SPDM 0 +#define MASTER_BLSP_1 1 +#define MASTER_BLSP_2 2 +#define MASTER_XI_USB_HS1 3 +#define MASTER_CRYPT0 4 +#define MASTER_SDCC_1 5 +#define MASTER_SDCC_2 6 +#define MASTER_SNOC_PCNOC 7 +#define MASTER_QPIC 8 +#define PCNOC_INT_0 9 +#define PCNOC_INT_2 10 +#define PCNOC_INT_3 11 +#define PCNOC_S_0 12 +#define PCNOC_S_1 13 +#define PCNOC_S_2 14 +#define PCNOC_S_3 15 +#define PCNOC_S_4 16 +#define PCNOC_S_6 17 +#define PCNOC_S_7 18 +#define PCNOC_S_8 19 +#define PCNOC_S_9 20 +#define PCNOC_S_10 21 +#define PCNOC_S_11 22 +#define SLAVE_SPDM 23 +#define SLAVE_PDM 24 +#define SLAVE_PRNG 25 +#define SLAVE_TCSR 26 +#define SLAVE_SNOC_CFG 27 +#define SLAVE_MESSAGE_RAM 28 +#define SLAVE_DISP_SS_CFG 29 +#define SLAVE_GPU_CFG 30 +#define SLAVE_BLSP_1 31 +#define SLAVE_BLSP_2 32 +#define SLAVE_TLMM_NORTH 33 +#define SLAVE_PCIE 34 +#define SLAVE_ETHERNET 35 +#define SLAVE_TLMM_EAST 36 +#define SLAVE_TCU 37 +#define SLAVE_PMIC_ARB 38 +#define SLAVE_SDCC_1 39 +#define SLAVE_SDCC_2 40 +#define SLAVE_TLMM_SOUTH 41 +#define SLAVE_USB_HS 42 +#define SLAVE_USB3 43 +#define SLAVE_CRYPTO_0_CFG 44 +#define SLAVE_PCNOC_SNOC 45 + +#define MASTER_QDSS_BAM 0 +#define MASTER_BIMC_SNOC 1 +#define MASTER_PCNOC_SNOC 2 +#define MASTER_QDSS_ETR 3 +#define MASTER_EMAC 4 +#define MASTER_PCIE 5 +#define MASTER_USB3 6 +#define QDSS_INT 7 +#define SNOC_INT_0 8 +#define SNOC_INT_1 9 +#define SNOC_INT_2 10 +#define SLAVE_KPSS_AHB 11 +#define SLAVE_WCSS 12 +#define SLAVE_SNOC_BIMC_1 13 +#define SLAVE_IMEM 14 +#define SLAVE_SNOC_PCNOC 15 +#define SLAVE_QDSS_STM 16 +#define SLAVE_CATS_0 17 +#define SLAVE_CATS_1 18 +#define SLAVE_LPASS 19 + +#endif diff --git a/include/dt-bindings/phy/phy-lantiq-vrx200-pcie.h b/include/dt-bindings/phy/phy-lantiq-vrx200-pcie.h new file mode 100644 index 000000000000..95a7896356d6 --- /dev/null +++ b/include/dt-bindings/phy/phy-lantiq-vrx200-pcie.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2019 Martin Blumenstingl <[email protected]> + */ + +#define LANTIQ_PCIE_PHY_MODE_25MHZ 0 +#define LANTIQ_PCIE_PHY_MODE_25MHZ_SSC 1 +#define LANTIQ_PCIE_PHY_MODE_36MHZ 2 +#define LANTIQ_PCIE_PHY_MODE_36MHZ_SSC 3 +#define LANTIQ_PCIE_PHY_MODE_100MHZ 4 +#define LANTIQ_PCIE_PHY_MODE_100MHZ_SSC 5 diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index 63caccadc2db..b16f9effa555 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -36,6 +36,8 @@ struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec, * @nodes: internal list of the interconnect provider nodes * @set: pointer to device specific set operation function * @aggregate: pointer to device specific aggregate operation function + * @pre_aggregate: pointer to device specific function that is called + * before the aggregation begins (optional) * @xlate: provider-specific callback for mapping nodes from phandle arguments * @dev: the device this interconnect provider belongs to * @users: count of active users @@ -45,8 +47,9 @@ struct icc_provider { struct list_head provider_list; struct list_head nodes; int (*set)(struct icc_node *src, struct icc_node *dst); - int (*aggregate)(struct icc_node *node, u32 avg_bw, u32 peak_bw, - u32 *agg_avg, u32 *agg_peak); + int (*aggregate)(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak); + void (*pre_aggregate)(struct icc_node *node); struct icc_node* (*xlate)(struct of_phandle_args *spec, void *data); struct device *dev; int users; diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h index dc25864755ba..d70a914cba11 100644 --- a/include/linux/interconnect.h +++ b/include/linux/interconnect.h @@ -30,6 +30,7 @@ struct icc_path *icc_get(struct device *dev, const int src_id, struct icc_path *of_icc_get(struct device *dev, const char *name); void icc_put(struct icc_path *path); int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw); +void icc_set_tag(struct icc_path *path, u32 tag); #else @@ -54,6 +55,10 @@ static inline int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw) return 0; } +static inline void icc_set_tag(struct icc_path *path, u32 tag) +{ +} + #endif /* CONFIG_INTERCONNECT */ #endif /* __LINUX_INTERCONNECT_H */ diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 2d7e012db03f..ece782ef5466 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -429,6 +429,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) * @lock: Must be held during ring creation/destruction. Is acquired by * interrupt_work when dispatching interrupts to individual rings. * @pdev: Pointer to the PCI device + * @ops: NHI specific optional ops * @iobase: MMIO space of the NHI * @tx_rings: All Tx rings available on this host controller * @rx_rings: All Rx rings available on this host controller @@ -442,6 +443,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) struct tb_nhi { spinlock_t lock; struct pci_dev *pdev; + const struct tb_nhi_ops *ops; void __iomem *iobase; struct tb_ring **tx_rings; struct tb_ring **rx_rings; diff --git a/include/soc/qcom/tcs.h b/include/soc/qcom/tcs.h index 262876a59e86..7a2a055ba6b0 100644 --- a/include/soc/qcom/tcs.h +++ b/include/soc/qcom/tcs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ #ifndef __SOC_QCOM_TCS_H__ @@ -53,4 +53,22 @@ struct tcs_request { struct tcs_cmd *cmds; }; +#define BCM_TCS_CMD_COMMIT_SHFT 30 +#define BCM_TCS_CMD_COMMIT_MASK 0x40000000 +#define BCM_TCS_CMD_VALID_SHFT 29 +#define BCM_TCS_CMD_VALID_MASK 0x20000000 +#define BCM_TCS_CMD_VOTE_X_SHFT 14 +#define BCM_TCS_CMD_VOTE_MASK 0x3fff +#define BCM_TCS_CMD_VOTE_Y_SHFT 0 +#define BCM_TCS_CMD_VOTE_Y_MASK 0xfffc000 + +/* Construct a Bus Clock Manager (BCM) specific TCS command */ +#define BCM_TCS_CMD(commit, valid, vote_x, vote_y) \ + (((commit) << BCM_TCS_CMD_COMMIT_SHFT) | \ + ((valid) << BCM_TCS_CMD_VALID_SHFT) | \ + ((cpu_to_le32(vote_x) & \ + BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_X_SHFT) | \ + ((cpu_to_le32(vote_y) & \ + BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT)) + #endif /* __SOC_QCOM_TCS_H__ */ |