diff --git a/Documentation/ABI/testing/sysfs-kernel-address_bits b/Documentation/ABI/testing/sysfs-kernel-address_bits index 5d09ff84d4d6..3b72e48086aa 100644 --- a/Documentation/ABI/testing/sysfs-kernel-address_bits +++ b/Documentation/ABI/testing/sysfs-kernel-address_bits @@ -1,4 +1,4 @@ -What: /sys/kernel/address_bit +What: /sys/kernel/address_bits Date: May 2023 KernelVersion: 6.3 Contact: Thomas Weißschuh diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst index 038f480074fd..066ecda1dd98 100644 --- a/Documentation/dev-tools/kunit/usage.rst +++ b/Documentation/dev-tools/kunit/usage.rst @@ -699,7 +699,7 @@ the template below. #include #include ... - MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); ... // Use do_interesting_thing() in tests diff --git a/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml b/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml index 8d47b290b4ed..7ae8c7b1d006 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-exynos5.yaml @@ -36,6 +36,7 @@ properties: - items: - enum: - google,gs101-hsi2c + - samsung,exynos2200-hsi2c - samsung,exynos850-hsi2c - const: samsung,exynosautov9-hsi2c - const: samsung,exynos5-hsi2c # Exynos5250 and Exynos5420 diff --git a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml index 2f1e97969c3f..4ac5a40a3886 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml @@ -105,6 +105,9 @@ properties: (t(f) in the I2C specification). If not specified we will use the SCL value since they are the same in nearly all cases. + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml b/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml index 798a6939b894..e645784b77d3 100644 --- a/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml @@ -22,6 +22,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + clock-frequency: default: 100000 diff --git a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml index cc39511a49d6..6876eade431b 100644 --- a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml +++ b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml @@ -10,9 +10,6 @@ maintainers: - Chris Brandt - Wolfram Sang -allOf: - - $ref: /schemas/i2c/i2c-controller.yaml# - properties: compatible: oneOf: @@ -32,32 +29,50 @@ properties: - renesas,riic-r9a09g056 # RZ/V2N - const: renesas,riic-r9a09g057 # RZ/V2H(P) - - const: renesas,riic-r9a09g057 # RZ/V2H(P) + - enum: + - renesas,riic-r9a09g057 # RZ/V2H(P) + - renesas,riic-r9a09g077 # RZ/T2H + + - items: + - const: renesas,riic-r9a09g087 # RZ/N2H + - const: renesas,riic-r9a09g077 # RZ/T2H reg: maxItems: 1 interrupts: - items: - - description: Transmit End Interrupt - - description: Receive Data Full Interrupt - - description: Transmit Data Empty Interrupt - - description: Stop Condition Detection Interrupt - - description: Start Condition Detection Interrupt - - description: NACK Reception Interrupt - - description: Arbitration-Lost Interrupt - - description: Timeout Interrupt + oneOf: + - items: + - description: Transmit End Interrupt + - description: Receive Data Full Interrupt + - description: Transmit Data Empty Interrupt + - description: Stop Condition Detection Interrupt + - description: Start Condition Detection Interrupt + - description: NACK Reception Interrupt + - description: Arbitration-Lost Interrupt + - description: Timeout Interrupt + - items: + - description: Transfer Error Or Event Generation + - description: Receive Data Full Interrupt + - description: Transmit Data Empty Interrupt + - description: Transmit End Interrupt interrupt-names: - items: - - const: tei - - const: ri - - const: ti - - const: spi - - const: sti - - const: naki - - const: ali - - const: tmoi + oneOf: + - items: + - const: tei + - const: ri + - const: ti + - const: spi + - const: sti + - const: naki + - const: ali + - const: tmoi + - items: + - const: eei + - const: rxi + - const: txi + - const: tei clock-frequency: description: @@ -84,18 +99,40 @@ required: - '#address-cells' - '#size-cells' -if: - properties: - compatible: - contains: - enum: - - renesas,riic-r9a07g043 - - renesas,riic-r9a07g044 - - renesas,riic-r9a07g054 - - renesas,riic-r9a09g057 -then: - required: - - resets +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + + - if: + properties: + compatible: + contains: + const: renesas,riic-r9a09g077 + then: + properties: + interrupts: + maxItems: 4 + interrupt-names: + maxItems: 4 + resets: false + else: + properties: + interrupts: + minItems: 8 + interrupt-names: + minItems: 8 + + - if: + properties: + compatible: + contains: + enum: + - renesas,riic-r9a07g043 + - renesas,riic-r9a07g044 + - renesas,riic-r9a07g054 + - renesas,riic-r9a09g057 + then: + required: + - resets unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml index 0432cc96f7ca..ac75d694611a 100644 --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml @@ -16,6 +16,7 @@ properties: - amd,pensando-elba-sd4hc - microchip,mpfs-sd4hc - microchip,pic64gx-sd4hc + - mobileye,eyeq-sd4hc - socionext,uniphier-sd4hc - const: cdns,sd4hc diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml new file mode 100644 index 000000000000..c142421bc723 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/loongson,ls2k0500-mmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: The SD/SDIO/eMMC host controller for Loongson-2K family SoCs + +description: + The MMC host controller on the Loongson-2K0500/2K1000 (using an externally + shared apbdma controller) provides the SD and SDIO device interfaces. + The two MMC host controllers on the Loongson-2K2000 are similar, + except that they use internal exclusive DMA. one controller provides + the eMMC interface and the other provides the SD/SDIO interface. + +maintainers: + - Binbin Zhou + +allOf: + - $ref: mmc-controller.yaml# + +properties: + compatible: + enum: + - loongson,ls2k0500-mmc + - loongson,ls2k1000-mmc + - loongson,ls2k2000-mmc + + reg: + minItems: 1 + items: + - description: Loongson-2K MMC controller registers. + - description: APB DMA config register for Loongson-2K MMC controller. + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + dmas: + maxItems: 1 + + dma-names: + const: rx-tx + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +if: + properties: + compatible: + contains: + enum: + - loongson,ls2k0500-mmc + - loongson,ls2k1000-mmc + +then: + properties: + reg: + minItems: 2 + + required: + - dmas + - dma-names + +else: + properties: + reg: + maxItems: 1 + +examples: + - | + #include + #include + #include + + mmc@1fe2c000 { + compatible = "loongson,ls2k1000-mmc"; + reg = <0x1fe2c000 0x68>, + <0x1fe00438 0x8>; + interrupt-parent = <&liointc0>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_APB_CLK>; + dmas = <&apbdma1 0>; + dma-names = "rx-tx"; + bus-width = <4>; + cd-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; + }; + + - | + #include + #include + + mmc@79990000 { + compatible = "loongson,ls2k2000-mmc"; + reg = <0x79990000 0x1000>; + interrupt-parent = <&pic>; + interrupts = <51 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_EMMC_CLK>; + bus-width = <8>; + non-removable; + cap-mmc-highspeed; + mmc-hs200-1_8v; + no-sd; + no-sdio; + }; diff --git a/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml b/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml index 32e512a68ed6..df07ea3b81d1 100644 --- a/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/mxs-mmc.yaml @@ -17,7 +17,7 @@ description: | and the properties used by the mxsmmc driver. allOf: - - $ref: mmc-controller.yaml + - $ref: mmc-controller-common.yaml# properties: compatible: @@ -31,6 +31,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + dmas: maxItems: 1 @@ -41,6 +44,7 @@ required: - compatible - reg - interrupts + - clocks - dmas - dma-names @@ -52,6 +56,7 @@ examples: compatible = "fsl,imx28-mmc"; reg = <0x80010000 2000>; interrupts = <96>; + clocks = <&clks 46>; dmas = <&dma_apbh 0>; dma-names = "rx-tx"; bus-width = <8>; diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 2fa2c0bcefed..c754ea71f51f 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -72,6 +72,8 @@ properties: - enum: - renesas,sdhi-r9a09g047 # RZ/G3E - renesas,sdhi-r9a09g056 # RZ/V2N + - renesas,sdhi-r9a09g077 # RZ/T2H + - renesas,sdhi-r9a09g087 # RZ/N2H - const: renesas,sdhi-r9a09g057 # RZ/V2H(P) reg: @@ -129,59 +131,78 @@ allOf: compatible: contains: enum: - - renesas,sdhi-r9a09g057 - - renesas,rzg2l-sdhi + - renesas,sdhi-r9a09g077 + - renesas,sdhi-r9a09g087 then: properties: + resets: false clocks: items: - - description: IMCLK, SDHI channel main clock1. - - description: CLK_HS, SDHI channel High speed clock which operates - 4 times that of SDHI channel main clock1. - - description: IMCLK2, SDHI channel main clock2. When this clock is - turned off, external SD card detection cannot be - detected. - - description: ACLK, SDHI channel bus clock. + - description: ACLK, IMCLK, SDHI channel bus and main clocks. + - description: CLK_HS, SDHI channel High speed clock. clock-names: items: - - const: core - - const: clkh - - const: cd - const: aclk - required: - - clock-names - - resets + - const: clkh else: if: properties: compatible: contains: enum: - - renesas,rcar-gen2-sdhi - - renesas,rcar-gen3-sdhi - - renesas,rcar-gen4-sdhi + - renesas,sdhi-r9a09g057 + - renesas,rzg2l-sdhi then: properties: clocks: - minItems: 1 - maxItems: 3 + items: + - description: IMCLK, SDHI channel main clock1. + - description: CLK_HS, SDHI channel High speed clock which operates + 4 times that of SDHI channel main clock1. + - description: IMCLK2, SDHI channel main clock2. When this clock is + turned off, external SD card detection cannot be + detected. + - description: ACLK, SDHI channel bus clock. clock-names: - minItems: 1 - uniqueItems: true items: - const: core - - enum: [ clkh, cd ] + - const: clkh - const: cd + - const: aclk + required: + - clock-names + - resets else: - properties: - clocks: - minItems: 1 - maxItems: 2 - clock-names: - minItems: 1 - items: - - const: core - - const: cd + if: + properties: + compatible: + contains: + enum: + - renesas,rcar-gen2-sdhi + - renesas,rcar-gen3-sdhi + - renesas,rcar-gen4-sdhi + then: + properties: + clocks: + minItems: 1 + maxItems: 3 + clock-names: + minItems: 1 + uniqueItems: true + items: + - const: core + - enum: [ clkh, cd ] + - const: cd + else: + properties: + clocks: + minItems: 1 + maxItems: 2 + clock-names: + minItems: 1 + items: + - const: core + - const: cd - if: properties: diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml index 2b2cbce2458b..22d1f50c3fd1 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -42,9 +42,11 @@ properties: - qcom,ipq5424-sdhci - qcom,ipq6018-sdhci - qcom,ipq9574-sdhci + - qcom,milos-sdhci - qcom,qcm2290-sdhci - qcom,qcs404-sdhci - qcom,qcs615-sdhci + - qcom,qcs8300-sdhci - qcom,qdu1000-sdhci - qcom,sar2130p-sdhci - qcom,sc7180-sdhci diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 7d4394a3ccbc..af5fef872529 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -17,6 +17,7 @@ properties: compatible: oneOf: - enum: + - qcom,glymur-rpmhpd - qcom,mdm9607-rpmpd - qcom,milos-rpmhpd - qcom,msm8226-rpmpd diff --git a/MAINTAINERS b/MAINTAINERS index 7787832cb3d4..b7d6dee0e200 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -302,6 +302,7 @@ F: include/linux/acpi.h F: include/linux/fwnode.h F: include/linux/fw_table.h F: lib/fw_table.c +F: rust/kernel/acpi.rs F: tools/power/acpi/ ACPI APEI @@ -7438,6 +7439,7 @@ F: include/linux/property.h F: include/linux/sysfs.h F: lib/kobj* F: rust/kernel/device.rs +F: rust/kernel/device/ F: rust/kernel/device_id.rs F: rust/kernel/devres.rs F: rust/kernel/driver.rs @@ -14275,6 +14277,13 @@ S: Maintained F: Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml F: drivers/soc/loongson/loongson2_guts.c +LOONGSON-2 SOC SERIES MMC/SD/SDIO CONTROLLER DRIVER +M: Binbin Zhou +L: linux-mmc@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml +F: drivers/mmc/host/loongson2-mmc.c + LOONGSON-2 SOC SERIES PM DRIVER M: Yinbo Zhu L: linux-pm@vger.kernel.org @@ -18715,6 +18724,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git F: Documentation/ABI/testing/sysfs-firmware-ofw F: drivers/of/ F: include/linux/of*.h +F: rust/helpers/of.c F: rust/kernel/of.rs F: scripts/dtc/ F: tools/testing/selftests/dt/ diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h index 99cd6546e72e..09963004ceea 100644 --- a/arch/arm64/include/asm/cache.h +++ b/arch/arm64/include/asm/cache.h @@ -87,6 +87,23 @@ int cache_line_size(void); #define dma_get_cache_alignment cache_line_size +/* Compress a u64 MPIDR value into 32 bits. */ +static inline u64 arch_compact_of_hwid(u64 id) +{ + u64 aff3 = MPIDR_AFFINITY_LEVEL(id, 3); + + /* + * These bits are expected to be RES0. If not, return a value with + * the upper 32 bits set to force the caller to give up on 32 bit + * cache ids. + */ + if (FIELD_GET(GENMASK_ULL(63, 40), id)) + return id; + + return (aff3 << 24) | FIELD_GET(GENMASK_ULL(23, 0), id); +} +#define arch_compact_of_hwid arch_compact_of_hwid + /* * Read the effective value of CTR_EL0. * diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c index afb690a172b4..ec900bce0257 100644 --- a/arch/powerpc/kernel/secvar-sysfs.c +++ b/arch/powerpc/kernel/secvar-sysfs.c @@ -121,7 +121,7 @@ static struct attribute *secvar_attrs[] = { static const struct attribute_group secvar_attr_group = { .attrs = secvar_attrs, - .bin_attrs_new = secvar_bin_attrs, + .bin_attrs = secvar_bin_attrs, }; __ATTRIBUTE_GROUPS(secvar_attr); diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index b0768f3d2893..681cf85af2b3 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1141,7 +1141,7 @@ static struct attribute *if_attrs[] = { static const struct attribute_group if_group = { .name = "interface", - .bin_attrs_new = if_bin_attrs, + .bin_attrs = if_bin_attrs, .attrs = if_attrs, }; diff --git a/arch/powerpc/platforms/powernv/opal-core.c b/arch/powerpc/platforms/powernv/opal-core.c index e652da8f986f..784602a48afb 100644 --- a/arch/powerpc/platforms/powernv/opal-core.c +++ b/arch/powerpc/platforms/powernv/opal-core.c @@ -208,7 +208,7 @@ static ssize_t read_opalcore(struct file *file, struct kobject *kobj, static struct bin_attribute opal_core_attr __ro_after_init = { .attr = {.name = "core", .mode = 0400}, - .read_new = read_opalcore + .read = read_opalcore }; /* @@ -607,7 +607,7 @@ static const struct bin_attribute *const mpipl_bin_attr[] = { static const struct attribute_group mpipl_group = { .attrs = mpipl_attr, - .bin_attrs_new = mpipl_bin_attr, + .bin_attrs = mpipl_bin_attr, }; static int __init opalcore_init(void) diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c index 27e25693cf39..cc3cc9ddf9d1 100644 --- a/arch/powerpc/platforms/powernv/opal-dump.c +++ b/arch/powerpc/platforms/powernv/opal-dump.c @@ -342,7 +342,7 @@ static void create_dump_obj(uint32_t id, size_t size, uint32_t type) dump->dump_attr.attr.name = "dump"; dump->dump_attr.attr.mode = 0400; dump->dump_attr.size = size; - dump->dump_attr.read_new = dump_attr_read; + dump->dump_attr.read = dump_attr_read; dump->id = id; dump->size = size; diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c index de33f354e9fd..c3fc5d258146 100644 --- a/arch/powerpc/platforms/powernv/opal-elog.c +++ b/arch/powerpc/platforms/powernv/opal-elog.c @@ -203,7 +203,7 @@ static void create_elog_obj(uint64_t id, size_t size, uint64_t type) elog->raw_attr.attr.name = "raw"; elog->raw_attr.attr.mode = 0400; elog->raw_attr.size = size; - elog->raw_attr.read_new = raw_attr_read; + elog->raw_attr.read = raw_attr_read; elog->id = id; elog->size = size; diff --git a/arch/powerpc/platforms/powernv/opal-flash.c b/arch/powerpc/platforms/powernv/opal-flash.c index fd8c8621e973..a3f7a2928767 100644 --- a/arch/powerpc/platforms/powernv/opal-flash.c +++ b/arch/powerpc/platforms/powernv/opal-flash.c @@ -493,7 +493,7 @@ out: static const struct bin_attribute image_data_attr = { .attr = {.name = "image", .mode = 0200}, .size = MAX_IMAGE_SIZE, /* Limit image size */ - .write_new = image_data_write, + .write = image_data_write, }; static struct kobj_attribute validate_attribute = diff --git a/arch/powerpc/platforms/powernv/opal-msglog.c b/arch/powerpc/platforms/powernv/opal-msglog.c index f1988d0ab45c..992a6b379a66 100644 --- a/arch/powerpc/platforms/powernv/opal-msglog.c +++ b/arch/powerpc/platforms/powernv/opal-msglog.c @@ -102,7 +102,7 @@ static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, static struct bin_attribute opal_msglog_attr __ro_after_init = { .attr = {.name = "msglog", .mode = 0400}, - .read_new = opal_msglog_read + .read = opal_msglog_read }; struct memcons *__init memcons_init(struct device_node *node, const char *mc_prop_name) diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 9ec265fcaff4..09bd93464b4f 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -815,7 +815,7 @@ static int opal_add_one_export(struct kobject *parent, const char *export_name, sysfs_bin_attr_init(attr); attr->attr.name = name; attr->attr.mode = 0400; - attr->read_new = sysfs_bin_attr_simple_read; + attr->read = sysfs_bin_attr_simple_read; attr->private = __va(vals[0]); attr->size = vals[1]; diff --git a/arch/powerpc/platforms/powernv/ultravisor.c b/arch/powerpc/platforms/powernv/ultravisor.c index 157d9a8134e4..c526871a1229 100644 --- a/arch/powerpc/platforms/powernv/ultravisor.c +++ b/arch/powerpc/platforms/powernv/ultravisor.c @@ -40,7 +40,7 @@ static ssize_t uv_msglog_read(struct file *file, struct kobject *kobj, static struct bin_attribute uv_msglog_attr __ro_after_init = { .attr = {.name = "msglog", .mode = 0400}, - .read_new = uv_msglog_read + .read = uv_msglog_read }; static int __init uv_init(void) diff --git a/arch/s390/kernel/cpacf.c b/arch/s390/kernel/cpacf.c index 4b9b34f95d72..3bebc47beeab 100644 --- a/arch/s390/kernel/cpacf.c +++ b/arch/s390/kernel/cpacf.c @@ -101,7 +101,7 @@ static const struct bin_attribute *const cpacf_attrs[] = { static const struct attribute_group cpacf_attr_grp = { .name = "cpacf", - .bin_attrs_new = cpacf_attrs, + .bin_attrs = cpacf_attrs, }; static int __init cpacf_init(void) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index ff15f91affde..961a3d60a4dd 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -596,7 +596,7 @@ static struct attribute *ipl_fcp_attrs[] = { static const struct attribute_group ipl_fcp_attr_group = { .attrs = ipl_fcp_attrs, - .bin_attrs_new = ipl_fcp_bin_attrs, + .bin_attrs = ipl_fcp_bin_attrs, }; static struct attribute *ipl_nvme_attrs[] = { @@ -610,7 +610,7 @@ static struct attribute *ipl_nvme_attrs[] = { static const struct attribute_group ipl_nvme_attr_group = { .attrs = ipl_nvme_attrs, - .bin_attrs_new = ipl_nvme_bin_attrs, + .bin_attrs = ipl_nvme_bin_attrs, }; static struct attribute *ipl_eckd_attrs[] = { @@ -623,7 +623,7 @@ static struct attribute *ipl_eckd_attrs[] = { static const struct attribute_group ipl_eckd_attr_group = { .attrs = ipl_eckd_attrs, - .bin_attrs_new = ipl_eckd_bin_attrs, + .bin_attrs = ipl_eckd_bin_attrs, }; /* CCW ipl device attributes */ @@ -920,7 +920,7 @@ static struct attribute *reipl_fcp_attrs[] = { static const struct attribute_group reipl_fcp_attr_group = { .attrs = reipl_fcp_attrs, - .bin_attrs_new = reipl_fcp_bin_attrs, + .bin_attrs = reipl_fcp_bin_attrs, }; static struct kobj_attribute sys_reipl_fcp_clear_attr = @@ -958,7 +958,7 @@ static struct attribute *reipl_nvme_attrs[] = { static const struct attribute_group reipl_nvme_attr_group = { .attrs = reipl_nvme_attrs, - .bin_attrs_new = reipl_nvme_bin_attrs + .bin_attrs = reipl_nvme_bin_attrs }; static ssize_t reipl_nvme_clear_show(struct kobject *kobj, @@ -1051,7 +1051,7 @@ static struct attribute *reipl_eckd_attrs[] = { static const struct attribute_group reipl_eckd_attr_group = { .attrs = reipl_eckd_attrs, - .bin_attrs_new = reipl_eckd_bin_attrs + .bin_attrs = reipl_eckd_bin_attrs }; static ssize_t reipl_eckd_clear_show(struct kobject *kobj, @@ -1596,7 +1596,7 @@ static const struct bin_attribute *const dump_fcp_bin_attrs[] = { static const struct attribute_group dump_fcp_attr_group = { .name = IPL_FCP_STR, .attrs = dump_fcp_attrs, - .bin_attrs_new = dump_fcp_bin_attrs, + .bin_attrs = dump_fcp_bin_attrs, }; /* NVME dump device attributes */ @@ -1630,7 +1630,7 @@ static const struct bin_attribute *const dump_nvme_bin_attrs[] = { static const struct attribute_group dump_nvme_attr_group = { .name = IPL_NVME_STR, .attrs = dump_nvme_attrs, - .bin_attrs_new = dump_nvme_bin_attrs, + .bin_attrs = dump_nvme_bin_attrs, }; /* ECKD dump device attributes */ @@ -1664,7 +1664,7 @@ static const struct bin_attribute *const dump_eckd_bin_attrs[] = { static const struct attribute_group dump_eckd_attr_group = { .name = IPL_ECKD_STR, .attrs = dump_eckd_attrs, - .bin_attrs_new = dump_eckd_bin_attrs, + .bin_attrs = dump_eckd_bin_attrs, }; /* CCW dump device attributes */ diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index 0ecad08e1b1e..0ee0924cfab7 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -218,7 +218,7 @@ static struct attribute *zpci_dev_attrs[] = { const struct attribute_group zpci_attr_group = { .attrs = zpci_dev_attrs, - .bin_attrs_new = zpci_bin_attrs, + .bin_attrs = zpci_bin_attrs, }; static struct attribute *pfip_attrs[] = { diff --git a/arch/x86/kernel/ksysfs.c b/arch/x86/kernel/ksysfs.c index b68d4be9464e..d547de9b3ed8 100644 --- a/arch/x86/kernel/ksysfs.c +++ b/arch/x86/kernel/ksysfs.c @@ -40,7 +40,7 @@ static const struct bin_attribute boot_params_data_attr = { .name = "data", .mode = S_IRUGO, }, - .read_new = boot_params_data_read, + .read = boot_params_data_read, .size = sizeof(boot_params), }; @@ -56,7 +56,7 @@ static const struct bin_attribute *const boot_params_data_attrs[] = { static const struct attribute_group boot_params_attr_group = { .attrs = boot_params_version_attrs, - .bin_attrs_new = boot_params_data_attrs, + .bin_attrs = boot_params_data_attrs, }; static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr) @@ -250,7 +250,7 @@ static struct bin_attribute data_attr __ro_after_init = { .name = "data", .mode = S_IRUGO, }, - .read_new = setup_data_data_read, + .read = setup_data_data_read, }; static struct attribute *setup_data_type_attrs[] = { @@ -265,7 +265,7 @@ static const struct bin_attribute *const setup_data_data_attrs[] = { static const struct attribute_group setup_data_attr_group = { .attrs = setup_data_type_attrs, - .bin_attrs_new = setup_data_data_attrs, + .bin_attrs = setup_data_data_attrs, }; static int __init create_setup_data_node(struct kobject *parent, diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index 29b3540dd180..7ed3e71f2fc0 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -521,7 +521,7 @@ CTX_RQ_SEQ_OPS(poll, HCTX_TYPE_POLL); static int blk_mq_debugfs_show(struct seq_file *m, void *v) { const struct blk_mq_debugfs_attr *attr = m->private; - void *data = d_inode(m->file->f_path.dentry->d_parent)->i_private; + void *data = debugfs_get_aux(m->file); return attr->show(data, m); } @@ -531,7 +531,7 @@ static ssize_t blk_mq_debugfs_write(struct file *file, const char __user *buf, { struct seq_file *m = file->private_data; const struct blk_mq_debugfs_attr *attr = m->private; - void *data = d_inode(file->f_path.dentry->d_parent)->i_private; + void *data = debugfs_get_aux(file); /* * Attributes that only implement .seq_ops are read-only and 'attr' is @@ -546,7 +546,7 @@ static ssize_t blk_mq_debugfs_write(struct file *file, const char __user *buf, static int blk_mq_debugfs_open(struct inode *inode, struct file *file) { const struct blk_mq_debugfs_attr *attr = inode->i_private; - void *data = d_inode(file->f_path.dentry->d_parent)->i_private; + void *data = debugfs_get_aux(file); struct seq_file *m; int ret; @@ -612,11 +612,9 @@ static void debugfs_create_files(struct dentry *parent, void *data, if (IS_ERR_OR_NULL(parent)) return; - d_inode(parent)->i_private = data; - for (; attr->name; attr++) - debugfs_create_file(attr->name, attr->mode, parent, - (void *)attr, &blk_mq_debugfs_fops); + debugfs_create_file_aux(attr->name, attr->mode, parent, + (void *)attr, data, &blk_mq_debugfs_fops); } void blk_mq_debugfs_register(struct request_queue *q) diff --git a/drivers/accel/habanalabs/common/sysfs.c b/drivers/accel/habanalabs/common/sysfs.c index 9d58efa2ff38..82f66520ec18 100644 --- a/drivers/accel/habanalabs/common/sysfs.c +++ b/drivers/accel/habanalabs/common/sysfs.c @@ -446,7 +446,7 @@ static DEVICE_ATTR_RO(parent_device); static const struct bin_attribute bin_attr_eeprom = { .attr = {.name = "eeprom", .mode = (0444)}, .size = PAGE_SIZE, - .read_new = eeprom_read_handler + .read = eeprom_read_handler }; static struct attribute *hl_dev_attrs[] = { @@ -479,7 +479,7 @@ static const struct bin_attribute *const hl_dev_bin_attrs[] = { static struct attribute_group hl_dev_attr_group = { .attrs = hl_dev_attrs, - .bin_attrs_new = hl_dev_bin_attrs, + .bin_attrs = hl_dev_bin_attrs, }; static struct attribute_group hl_dev_clks_attr_group; diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c index 35ece8e9f15d..0fdd581ef96f 100644 --- a/drivers/acpi/bgrt.c +++ b/drivers/acpi/bgrt.c @@ -47,7 +47,7 @@ static const struct bin_attribute *const bgrt_bin_attributes[] = { static const struct attribute_group bgrt_attribute_group = { .attrs = bgrt_attributes, - .bin_attrs_new = bgrt_bin_attributes, + .bin_attrs = bgrt_bin_attributes, }; int __init acpi_parse_bgrt(struct acpi_table_header *table) diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index a48ebbf768f9..e596224302f4 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -372,7 +372,7 @@ static int acpi_table_attr_init(struct kobject *tables_obj, } table_attr->attr.size = table_header->length; - table_attr->attr.read_new = acpi_table_show; + table_attr->attr.read = acpi_table_show; table_attr->attr.attr.name = table_attr->filename; table_attr->attr.attr.mode = 0400; @@ -495,7 +495,7 @@ static int acpi_table_data_init(struct acpi_table_header *th) if (!data_attr) return -ENOMEM; sysfs_attr_init(&data_attr->attr.attr); - data_attr->attr.read_new = acpi_data_show; + data_attr->attr.read = acpi_data_show; data_attr->attr.attr.mode = 0400; return acpi_data_objs[i].fn(th, data_attr); } diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index 44cd3f85b659..12ffdd843756 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -399,6 +399,7 @@ static void auxiliary_device_release(struct device *dev) { struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + of_node_put(dev->of_node); kfree(auxdev); } @@ -435,6 +436,7 @@ struct auxiliary_device *auxiliary_device_create(struct device *dev, ret = auxiliary_device_init(auxdev); if (ret) { + of_node_put(auxdev->dev.of_node); kfree(auxdev); return NULL; } diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index cf0d455209d7..613410705a47 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -183,6 +184,54 @@ static bool cache_node_is_unified(struct cacheinfo *this_leaf, return of_property_read_bool(np, "cache-unified"); } +static bool match_cache_node(struct device_node *cpu, + const struct device_node *cache_node) +{ + struct device_node *prev, *cache = of_find_next_cache_node(cpu); + + while (cache) { + if (cache == cache_node) { + of_node_put(cache); + return true; + } + + prev = cache; + cache = of_find_next_cache_node(cache); + of_node_put(prev); + } + + return false; +} + +#ifndef arch_compact_of_hwid +#define arch_compact_of_hwid(_x) (_x) +#endif + +static void cache_of_set_id(struct cacheinfo *this_leaf, + struct device_node *cache_node) +{ + struct device_node *cpu; + u32 min_id = ~0; + + for_each_of_cpu_node(cpu) { + u64 id = of_get_cpu_hwid(cpu, 0); + + id = arch_compact_of_hwid(id); + if (FIELD_GET(GENMASK_ULL(63, 32), id)) { + of_node_put(cpu); + return; + } + + if (match_cache_node(cpu, cache_node)) + min_id = min(min_id, id); + } + + if (min_id != ~0) { + this_leaf->id = min_id; + this_leaf->attributes |= CACHE_ID; + } +} + static void cache_of_set_props(struct cacheinfo *this_leaf, struct device_node *np) { @@ -198,6 +247,7 @@ static void cache_of_set_props(struct cacheinfo *this_leaf, cache_get_line_size(this_leaf, np); cache_nr_sets(this_leaf, np); cache_associativity(this_leaf); + cache_of_set_id(this_leaf, np); } static int cache_setup_of_node(unsigned int cpu) diff --git a/drivers/base/core.c b/drivers/base/core.c index cbc0099d8ef2..d22d6b23e758 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -460,9 +460,9 @@ static ssize_t auto_remove_on_show(struct device *dev, struct device_link *link = to_devlink(dev); const char *output; - if (link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER) + if (device_link_test(link, DL_FLAG_AUTOREMOVE_SUPPLIER)) output = "supplier unbind"; - else if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) + else if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) output = "consumer unbind"; else output = "never"; @@ -476,7 +476,7 @@ static ssize_t runtime_pm_show(struct device *dev, { struct device_link *link = to_devlink(dev); - return sysfs_emit(buf, "%d\n", !!(link->flags & DL_FLAG_PM_RUNTIME)); + return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_PM_RUNTIME)); } static DEVICE_ATTR_RO(runtime_pm); @@ -485,8 +485,7 @@ static ssize_t sync_state_only_show(struct device *dev, { struct device_link *link = to_devlink(dev); - return sysfs_emit(buf, "%d\n", - !!(link->flags & DL_FLAG_SYNC_STATE_ONLY)); + return sysfs_emit(buf, "%d\n", device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); } static DEVICE_ATTR_RO(sync_state_only); @@ -792,12 +791,12 @@ struct device_link *device_link_add(struct device *consumer, if (link->consumer != consumer) continue; - if (link->flags & DL_FLAG_INFERRED && + if (device_link_test(link, DL_FLAG_INFERRED) && !(flags & DL_FLAG_INFERRED)) link->flags &= ~DL_FLAG_INFERRED; if (flags & DL_FLAG_PM_RUNTIME) { - if (!(link->flags & DL_FLAG_PM_RUNTIME)) { + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) { pm_runtime_new_link(consumer); link->flags |= DL_FLAG_PM_RUNTIME; } @@ -807,8 +806,8 @@ struct device_link *device_link_add(struct device *consumer, if (flags & DL_FLAG_STATELESS) { kref_get(&link->kref); - if (link->flags & DL_FLAG_SYNC_STATE_ONLY && - !(link->flags & DL_FLAG_STATELESS)) { + if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) && + !device_link_test(link, DL_FLAG_STATELESS)) { link->flags |= DL_FLAG_STATELESS; goto reorder; } else { @@ -823,7 +822,7 @@ struct device_link *device_link_add(struct device *consumer, * update the existing link to stay around longer. */ if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) { - if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) { + if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) { link->flags &= ~DL_FLAG_AUTOREMOVE_CONSUMER; link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER; } @@ -831,12 +830,12 @@ struct device_link *device_link_add(struct device *consumer, link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER); } - if (!(link->flags & DL_FLAG_MANAGED)) { + if (!device_link_test(link, DL_FLAG_MANAGED)) { kref_get(&link->kref); link->flags |= DL_FLAG_MANAGED; device_link_init_status(link, consumer, supplier); } - if (link->flags & DL_FLAG_SYNC_STATE_ONLY && + if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) && !(flags & DL_FLAG_SYNC_STATE_ONLY)) { link->flags &= ~DL_FLAG_SYNC_STATE_ONLY; goto reorder; @@ -940,7 +939,7 @@ static void __device_link_del(struct kref *kref) static void device_link_put_kref(struct device_link *link) { - if (link->flags & DL_FLAG_STATELESS) + if (device_link_test(link, DL_FLAG_STATELESS)) kref_put(&link->kref, __device_link_del); else if (!device_is_registered(link->consumer)) __device_link_del(&link->kref); @@ -1004,7 +1003,7 @@ static void device_links_missing_supplier(struct device *dev) if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) { WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } else { - WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY)); + WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); WRITE_ONCE(link->status, DL_STATE_DORMANT); } } @@ -1072,14 +1071,14 @@ int device_links_check_suppliers(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status != DL_STATE_AVAILABLE && - !(link->flags & DL_FLAG_SYNC_STATE_ONLY)) { + !device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) { if (dev_is_best_effort(dev) && - link->flags & DL_FLAG_INFERRED && + device_link_test(link, DL_FLAG_INFERRED) && !link->supplier->can_match) { ret = -EAGAIN; continue; @@ -1128,7 +1127,7 @@ static void __device_links_queue_sync_state(struct device *dev, return; list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status != DL_STATE_ACTIVE) return; @@ -1268,7 +1267,7 @@ void device_links_force_bind(struct device *dev) device_links_write_lock(); list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status != DL_STATE_AVAILABLE) { @@ -1329,7 +1328,7 @@ void device_links_driver_bound(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; /* @@ -1345,7 +1344,7 @@ void device_links_driver_bound(struct device *dev) WARN_ON(link->status != DL_STATE_DORMANT); WRITE_ONCE(link->status, DL_STATE_AVAILABLE); - if (link->flags & DL_FLAG_AUTOPROBE_CONSUMER) + if (device_link_test(link, DL_FLAG_AUTOPROBE_CONSUMER)) driver_deferred_probe_add(link->consumer); } @@ -1357,11 +1356,11 @@ void device_links_driver_bound(struct device *dev) list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) { struct device *supplier; - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; supplier = link->supplier; - if (link->flags & DL_FLAG_SYNC_STATE_ONLY) { + if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) { /* * When DL_FLAG_SYNC_STATE_ONLY is set, it means no * other DL_MANAGED_LINK_FLAGS have been set. So, it's @@ -1369,7 +1368,7 @@ void device_links_driver_bound(struct device *dev) */ device_link_drop_managed(link); } else if (dev_is_best_effort(dev) && - link->flags & DL_FLAG_INFERRED && + device_link_test(link, DL_FLAG_INFERRED) && link->status != DL_STATE_CONSUMER_PROBE && !link->supplier->can_match) { /* @@ -1421,10 +1420,10 @@ static void __device_links_no_driver(struct device *dev) struct device_link *link, *ln; list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; - if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) { + if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) { device_link_drop_managed(link); continue; } @@ -1436,7 +1435,7 @@ static void __device_links_no_driver(struct device *dev) if (link->supplier->links.status == DL_DEV_DRIVER_BOUND) { WRITE_ONCE(link->status, DL_STATE_AVAILABLE); } else { - WARN_ON(!(link->flags & DL_FLAG_SYNC_STATE_ONLY)); + WARN_ON(!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)); WRITE_ONCE(link->status, DL_STATE_DORMANT); } } @@ -1461,7 +1460,7 @@ void device_links_no_driver(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; /* @@ -1498,10 +1497,10 @@ void device_links_driver_cleanup(struct device *dev) device_links_write_lock(); list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; - WARN_ON(link->flags & DL_FLAG_AUTOREMOVE_CONSUMER); + WARN_ON(device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)); WARN_ON(link->status != DL_STATE_SUPPLIER_UNBIND); /* @@ -1510,7 +1509,7 @@ void device_links_driver_cleanup(struct device *dev) * has moved to DL_STATE_SUPPLIER_UNBIND. */ if (link->status == DL_STATE_SUPPLIER_UNBIND && - link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER) + device_link_test(link, DL_FLAG_AUTOREMOVE_SUPPLIER)) device_link_drop_managed(link); WRITE_ONCE(link->status, DL_STATE_DORMANT); @@ -1544,7 +1543,7 @@ bool device_links_busy(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!device_link_test(link, DL_FLAG_MANAGED)) continue; if (link->status == DL_STATE_CONSUMER_PROBE @@ -1586,8 +1585,8 @@ void device_links_unbind_consumers(struct device *dev) list_for_each_entry(link, &dev->links.consumers, s_node) { enum device_link_state status; - if (!(link->flags & DL_FLAG_MANAGED) || - link->flags & DL_FLAG_SYNC_STATE_ONLY) + if (!device_link_test(link, DL_FLAG_MANAGED) || + device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) continue; status = link->status; @@ -1743,7 +1742,7 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) static void fw_devlink_relax_link(struct device_link *link) { - if (!(link->flags & DL_FLAG_INFERRED)) + if (!device_link_test(link, DL_FLAG_INFERRED)) return; if (device_link_flag_is_sync_state_only(link->flags)) @@ -1779,7 +1778,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data) struct device_link *link = to_devlink(dev); struct device *sup = link->supplier; - if (!(link->flags & DL_FLAG_MANAGED) || + if (!device_link_test(link, DL_FLAG_MANAGED) || link->status == DL_STATE_ACTIVE || sup->state_synced || !dev_has_sync_state(sup)) return 0; @@ -1881,8 +1880,6 @@ static void fw_devlink_unblock_consumers(struct device *dev) device_links_write_unlock(); } -#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) - static bool fwnode_init_without_drv(struct fwnode_handle *fwnode) { struct device *dev; @@ -2063,7 +2060,7 @@ static bool __fw_devlink_relax_cycles(struct fwnode_handle *con_handle, * such due to a cycle. */ if (device_link_flag_is_sync_state_only(dev_link->flags) && - !(dev_link->flags & DL_FLAG_CYCLE)) + !device_link_test(dev_link, DL_FLAG_CYCLE)) continue; if (__fw_devlink_relax_cycles(con_handle, @@ -5281,6 +5278,12 @@ void device_set_node(struct device *dev, struct fwnode_handle *fwnode) } EXPORT_SYMBOL_GPL(device_set_node); +struct device *get_dev_from_fwnode(struct fwnode_handle *fwnode) +{ + return get_device((fwnode)->dev); +} +EXPORT_SYMBOL_GPL(get_dev_from_fwnode); + int device_match_name(struct device *dev, const void *name) { return sysfs_streq(dev_name(dev), name); diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 03a39c417dc4..37faf6156d7c 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -140,7 +140,7 @@ static const struct bin_attribute *const devcd_dev_bin_attrs[] = { }; static const struct attribute_group devcd_dev_group = { - .bin_attrs_new = devcd_dev_bin_attrs, + .bin_attrs = devcd_dev_bin_attrs, }; static const struct attribute_group *devcd_dev_groups[] = { diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c index d254ceb56d84..add0b9b75edd 100644 --- a/drivers/base/firmware_loader/sysfs.c +++ b/drivers/base/firmware_loader/sysfs.c @@ -359,8 +359,8 @@ out: static const struct bin_attribute firmware_attr_data = { .attr = { .name = "data", .mode = 0644 }, .size = 0, - .read_new = firmware_data_read, - .write_new = firmware_data_write, + .read = firmware_data_read, + .write = firmware_data_write, }; static struct attribute *fw_dev_attrs[] = { @@ -381,7 +381,7 @@ static const struct bin_attribute *const fw_dev_bin_attrs[] = { static const struct attribute_group fw_dev_attr_group = { .attrs = fw_dev_attrs, - .bin_attrs_new = fw_dev_bin_attrs, + .bin_attrs = fw_dev_bin_attrs, #ifdef CONFIG_FW_UPLOAD .is_visible = fw_upload_is_visible, #endif diff --git a/drivers/base/node.c b/drivers/base/node.c index c19094481630..9328b81c2f47 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -597,7 +597,7 @@ static const struct bin_attribute *node_dev_bin_attrs[] = { static const struct attribute_group node_dev_group = { .attrs = node_dev_attrs, - .bin_attrs_new = node_dev_bin_attrs, + .bin_attrs = node_dev_bin_attrs, }; static const struct attribute_group *node_dev_groups[] = { diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index c48c7993cf2f..cebfa59e75e9 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -2061,7 +2061,7 @@ static bool device_prepare_smart_suspend(struct device *dev) idx = device_links_read_lock(); list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_PM_RUNTIME)) + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) continue; if (!dev_pm_smart_suspend(link->supplier) && diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index f835b85bd4d9..3e84dc4122de 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -302,7 +302,7 @@ static int rpm_get_suppliers(struct device *dev) device_links_read_lock_held()) { int retval; - if (!(link->flags & DL_FLAG_PM_RUNTIME)) + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) continue; retval = pm_runtime_get_sync(link->supplier); @@ -1905,7 +1905,7 @@ void pm_runtime_get_suppliers(struct device *dev) list_for_each_entry_rcu(link, &dev->links.suppliers, c_node, device_links_read_lock_held()) - if (link->flags & DL_FLAG_PM_RUNTIME) { + if (device_link_test(link, DL_FLAG_PM_RUNTIME)) { link->supplier_preactivated = true; pm_runtime_get_sync(link->supplier); } @@ -1959,7 +1959,7 @@ static void pm_runtime_drop_link_count(struct device *dev) */ void pm_runtime_drop_link(struct device_link *link) { - if (!(link->flags & DL_FLAG_PM_RUNTIME)) + if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) return; pm_runtime_drop_link_count(link->consumer); diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 8b42df05feff..c890e2a5b428 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -179,7 +179,7 @@ static umode_t topology_is_visible(struct kobject *kobj, static const struct attribute_group topology_attr_group = { .attrs = default_attrs, - .bin_attrs_new = bin_attrs, + .bin_attrs = bin_attrs, .is_visible = topology_is_visible, .name = "topology" }; diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index 43c87d0259b6..9ad85fe6fd05 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -20,7 +20,8 @@ use kernel::{ /// Finds exact supply name from the OF node. fn find_supply_name_exact(dev: &Device, name: &str) -> Option { let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?; - dev.property_present(&prop_name) + dev.fwnode()? + .property_present(&prop_name) .then(|| CString::try_from_fmt(fmt!("{name}")).ok()) .flatten() } diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index 2041f59116ce..37c41209eaf9 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -28,7 +28,6 @@ struct psci_pd_provider { }; static LIST_HEAD(psci_pd_providers); -static bool psci_pd_allow_domain_state; static int psci_pd_power_off(struct generic_pm_domain *pd) { @@ -38,9 +37,6 @@ static int psci_pd_power_off(struct generic_pm_domain *pd) if (!state->data) return 0; - if (!psci_pd_allow_domain_state) - return -EBUSY; - /* OSI mode is enabled, set the corresponding domain state. */ pd_state = state->data; psci_set_domain_state(pd, pd->state_idx, *pd_state); @@ -126,15 +122,6 @@ static void psci_pd_remove(void) } } -static void psci_cpuidle_domain_sync_state(struct device *dev) -{ - /* - * All devices have now been attached/probed to the PM domain topology, - * hence it's fine to allow domain states to be picked. - */ - psci_pd_allow_domain_state = true; -} - static const struct of_device_id psci_of_match[] = { { .compatible = "arm,psci-1.0" }, {} @@ -195,7 +182,6 @@ static struct platform_driver psci_cpuidle_domain_driver = { .driver = { .name = "psci-cpuidle-domain", .of_match_table = psci_of_match, - .sync_state = psci_cpuidle_domain_sync_state, }, }; diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c index 0fe1ece9fbdc..a360bc4d20b7 100644 --- a/drivers/cpuidle/cpuidle-riscv-sbi.c +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -44,7 +44,6 @@ static DEFINE_PER_CPU_READ_MOSTLY(struct sbi_cpuidle_data, sbi_cpuidle_data); static DEFINE_PER_CPU(struct sbi_domain_state, domain_state); static bool sbi_cpuidle_use_osi; static bool sbi_cpuidle_use_cpuhp; -static bool sbi_cpuidle_pd_allow_domain_state; static inline void sbi_set_domain_state(u32 state) { @@ -345,15 +344,6 @@ deinit: return ret; } -static void sbi_cpuidle_domain_sync_state(struct device *dev) -{ - /* - * All devices have now been attached/probed to the PM domain - * topology, hence it's fine to allow domain states to be picked. - */ - sbi_cpuidle_pd_allow_domain_state = true; -} - #ifdef CONFIG_DT_IDLE_GENPD static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) @@ -364,9 +354,6 @@ static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) if (!state->data) return 0; - if (!sbi_cpuidle_pd_allow_domain_state) - return -EBUSY; - /* OSI mode is enabled, set the corresponding domain state. */ pd_state = state->data; sbi_set_domain_state(*pd_state); @@ -564,7 +551,6 @@ static struct platform_driver sbi_cpuidle_driver = { .probe = sbi_cpuidle_probe, .driver = { .name = "sbi-cpuidle", - .sync_state = sbi_cpuidle_domain_sync_state, }, }; diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index fe4b593331da..cf32dc50b7a6 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -181,7 +181,7 @@ static const struct bin_attribute *const cxl_cdat_bin_attributes[] = { }; static const struct attribute_group cxl_cdat_attribute_group = { - .bin_attrs_new = cxl_cdat_bin_attributes, + .bin_attrs = cxl_cdat_bin_attributes, .is_bin_visible = cxl_port_bin_attr_is_visible, }; diff --git a/drivers/firmware/efi/mokvar-table.c b/drivers/firmware/efi/mokvar-table.c index 0a856c3f69a3..aedbbd627706 100644 --- a/drivers/firmware/efi/mokvar-table.c +++ b/drivers/firmware/efi/mokvar-table.c @@ -340,7 +340,7 @@ static int __init efi_mokvar_sysfs_init(void) mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name; mokvar_sysfs->bin_attr.attr.mode = 0400; mokvar_sysfs->bin_attr.size = mokvar_entry->data_size; - mokvar_sysfs->bin_attr.read_new = efi_mokvar_sysfs_read; + mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read; err = sysfs_create_bin_file(mokvar_kobj, &mokvar_sysfs->bin_attr); diff --git a/drivers/firmware/google/cbmem.c b/drivers/firmware/google/cbmem.c index 773d05078e0a..54c3b8b05e5d 100644 --- a/drivers/firmware/google/cbmem.c +++ b/drivers/firmware/google/cbmem.c @@ -86,7 +86,7 @@ static const struct bin_attribute *const bin_attrs[] = { static const struct attribute_group cbmem_entry_group = { .attrs = attrs, - .bin_attrs_new = bin_attrs, + .bin_attrs = bin_attrs, }; static const struct attribute_group *dev_groups[] = { diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index e8fb00dcaf65..0ceccde5a302 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -530,7 +530,7 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, static const struct bin_attribute eventlog_bin_attr = { .attr = {.name = "append_to_eventlog", .mode = 0200}, - .write_new = eventlog_write, + .write = eventlog_write, }; static ssize_t gsmi_clear_eventlog_store(struct kobject *kobj, diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c index d957af6f9349..6138a1653ec5 100644 --- a/drivers/firmware/google/memconsole.c +++ b/drivers/firmware/google/memconsole.c @@ -28,7 +28,7 @@ static ssize_t memconsole_read(struct file *filp, struct kobject *kobp, static struct bin_attribute memconsole_bin_attr = { .attr = {.name = "log", .mode = 0444}, - .read_new = memconsole_read, + .read = memconsole_read, }; void memconsole_setup(ssize_t (*read_func)(char *, loff_t, size_t)) diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index 254ac6545d68..339a3f74b247 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -121,7 +121,7 @@ static int vpd_section_attrib_add(const u8 *key, u32 key_len, info->bin_attr.attr.name = info->key; info->bin_attr.attr.mode = 0444; info->bin_attr.size = value_len; - info->bin_attr.read_new = vpd_attrib_read; + info->bin_attr.read = vpd_attrib_read; info->bin_attr.private = info; info->value = value; @@ -201,7 +201,7 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, sec->bin_attr.attr.name = sec->raw_name; sec->bin_attr.attr.mode = 0444; sec->bin_attr.size = size; - sec->bin_attr.read_new = vpd_section_read; + sec->bin_attr.read = vpd_section_read; sec->bin_attr.private = sec; err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr); diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 2615fb780e3c..0eebd572f9a5 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -476,7 +476,7 @@ static ssize_t fw_cfg_sysfs_read_raw(struct file *filp, struct kobject *kobj, static const struct bin_attribute fw_cfg_sysfs_attr_raw = { .attr = { .name = "raw", .mode = S_IRUSR }, - .read_new = fw_cfg_sysfs_read_raw, + .read = fw_cfg_sysfs_read_raw, }; /* diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 7356e860e65c..02da3e48bc8f 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1299,11 +1300,10 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write); * This API function is to be used for notify the power management controller * about the completed power management initialization. */ -int zynqmp_pm_init_finalize(void) +static int zynqmp_pm_init_finalize(void) { return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, NULL, 0); } -EXPORT_SYMBOL_GPL(zynqmp_pm_init_finalize); /** * zynqmp_pm_set_suspend_mode() - Set system suspend mode @@ -2100,6 +2100,19 @@ static void zynqmp_firmware_remove(struct platform_device *pdev) platform_device_unregister(em_dev); } +static void zynqmp_firmware_sync_state(struct device *dev) +{ + struct device_node *np = dev->of_node; + + if (!of_device_is_compatible(np, "xlnx,zynqmp-firmware")) + return; + + of_genpd_sync_state(np); + + if (zynqmp_pm_init_finalize()) + dev_warn(dev, "failed to release power management to firmware\n"); +} + static const struct of_device_id zynqmp_firmware_of_match[] = { {.compatible = "xlnx,zynqmp-firmware"}, {.compatible = "xlnx,versal-firmware"}, @@ -2112,6 +2125,7 @@ static struct platform_driver zynqmp_firmware_driver = { .name = "zynqmp_firmware", .of_match_table = zynqmp_firmware_of_match, .dev_groups = zynqmp_firmware_groups, + .sync_state = zynqmp_firmware_sync_state, }, .probe = zynqmp_firmware_probe, .remove = zynqmp_firmware_remove, diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index ee39d1699387..c6c115993ebc 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -613,8 +613,8 @@ static const struct bin_attribute fsi_slave_raw_attr = { .mode = 0600, }, .size = 0, - .read_new = fsi_slave_sysfs_raw_read, - .write_new = fsi_slave_sysfs_raw_write, + .read = fsi_slave_sysfs_raw_read, + .write = fsi_slave_sysfs_raw_write, }; static void fsi_slave_release(struct device *dev) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index c14f63cefe67..98cc9c450192 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -4125,8 +4125,8 @@ rel_buf: static const struct bin_attribute psp_vbflash_bin_attr = { .attr = {.name = "psp_vbflash", .mode = 0660}, .size = 0, - .write_new = amdgpu_psp_vbflash_write, - .read_new = amdgpu_psp_vbflash_read, + .write = amdgpu_psp_vbflash_write, + .read = amdgpu_psp_vbflash_read, }; /** @@ -4189,7 +4189,7 @@ static umode_t amdgpu_bin_flash_attr_is_visible(struct kobject *kobj, const struct attribute_group amdgpu_flash_attr_group = { .attrs = flash_attrs, - .bin_attrs_new = bin_flash_attrs, + .bin_attrs = bin_flash_attrs, .is_bin_visible = amdgpu_bin_flash_attr_is_visible, .is_visible = amdgpu_flash_attr_is_visible, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index de0944947eaf..9c8829bd5a58 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -2124,7 +2124,7 @@ static int amdgpu_ras_fs_init(struct amdgpu_device *adev) con->badpages_attr = bin_attr_gpu_vram_bad_pages; sysfs_bin_attr_init(&con->badpages_attr); bin_attrs[0] = &con->badpages_attr; - group.bin_attrs_new = bin_attrs; + group.bin_attrs = bin_attrs; } r = sysfs_create_group(&adev->dev->kobj, &group); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index c16962256514..b1d1897f5eaf 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -723,8 +723,8 @@ ret: static const struct bin_attribute data_attr = { .attr = {.name = "hdcp_srm", .mode = 0664}, .size = PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, /* Limit SRM size */ - .write_new = srm_data_write, - .read_new = srm_data_read, + .write = srm_data_write, + .read = srm_data_read, }; struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 60c1f26edb6f..a455c56dbbeb 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -319,7 +319,7 @@ static const struct bin_attribute edid_attr = { .attr.name = "edid", .attr.mode = 0444, .size = 0, - .read_new = edid_show, + .read = edid_show, }; static const struct bin_attribute *const connector_bin_attrs[] = { @@ -329,7 +329,7 @@ static const struct bin_attribute *const connector_bin_attrs[] = { static const struct attribute_group connector_dev_group = { .attrs = connector_dev_attrs, - .bin_attrs_new = connector_bin_attrs, + .bin_attrs = connector_bin_attrs, }; static const struct attribute_group *connector_dev_groups[] = { diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 4f785cdbd155..568525d49428 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -2506,8 +2506,8 @@ static const struct bin_attribute error_state_attr = { .attr.name = "error", .attr.mode = S_IRUSR | S_IWUSR, .size = 0, - .read_new = error_state_read, - .write_new = error_state_write, + .read = error_state_read, + .write = error_state_write, }; void i915_gpu_error_sysfs_setup(struct drm_i915_private *i915) diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index f936e8f1f129..622c66666935 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -140,8 +140,8 @@ i915_l3_write(struct file *filp, struct kobject *kobj, static const struct bin_attribute dpf_attrs = { .attr = {.name = "l3_parity", .mode = (S_IRUSR | S_IWUSR)}, .size = GEN7_L3LOG_SIZE, - .read_new = i915_l3_read, - .write_new = i915_l3_write, + .read = i915_l3_read, + .write = i915_l3_write, .mmap = NULL, .private = (void *)0 }; @@ -149,8 +149,8 @@ static const struct bin_attribute dpf_attrs = { static const struct bin_attribute dpf_attrs_1 = { .attr = {.name = "l3_parity_slice_1", .mode = (S_IRUSR | S_IWUSR)}, .size = GEN7_L3LOG_SIZE, - .read_new = i915_l3_read, - .write_new = i915_l3_write, + .read = i915_l3_read, + .write = i915_l3_write, .mmap = NULL, .private = (void *)1 }; diff --git a/drivers/gpu/drm/lima/lima_drv.c b/drivers/gpu/drm/lima/lima_drv.c index 11ace5cebf4c..65210ab081bb 100644 --- a/drivers/gpu/drm/lima/lima_drv.c +++ b/drivers/gpu/drm/lima/lima_drv.c @@ -362,8 +362,8 @@ static const struct bin_attribute lima_error_state_attr = { .attr.name = "error", .attr.mode = 0600, .size = 0, - .read_new = lima_error_state_read, - .write_new = lima_error_state_write, + .read = lima_error_state_read, + .write = lima_error_state_write, }; static int lima_pdev_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 238cbb49963e..197defe4f928 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1869,20 +1869,14 @@ static int zynqmp_dp_test_setup(struct zynqmp_dp *dp) static ssize_t zynqmp_dp_pattern_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct dentry *dentry = file->f_path.dentry; struct zynqmp_dp *dp = file->private_data; char buf[16]; ssize_t ret; - ret = debugfs_file_get(dentry); - if (unlikely(ret)) - return ret; - scoped_guard(mutex, &dp->lock) ret = snprintf(buf, sizeof(buf), "%s\n", test_pattern_str[dp->test.pattern]); - debugfs_file_put(dentry); return simple_read_from_buffer(user_buf, count, ppos, buf, ret); } @@ -1890,27 +1884,20 @@ static ssize_t zynqmp_dp_pattern_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - struct dentry *dentry = file->f_path.dentry; struct zynqmp_dp *dp = file->private_data; char buf[16]; ssize_t ret; int pattern; - ret = debugfs_file_get(dentry); - if (unlikely(ret)) - return ret; - ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); if (ret < 0) - goto out; + return ret; buf[ret] = '\0'; pattern = sysfs_match_string(test_pattern_str, buf); - if (pattern < 0) { - ret = -EINVAL; - goto out; - } + if (pattern < 0) + return -EINVAL; mutex_lock(&dp->lock); dp->test.pattern = pattern; @@ -1919,8 +1906,6 @@ static ssize_t zynqmp_dp_pattern_write(struct file *file, dp->test.custom) ?: ret; mutex_unlock(&dp->lock); -out: - debugfs_file_put(dentry); return ret; } @@ -2026,20 +2011,13 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_active, zynqmp_dp_active_get, static ssize_t zynqmp_dp_custom_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct dentry *dentry = file->f_path.dentry; struct zynqmp_dp *dp = file->private_data; ssize_t ret; - ret = debugfs_file_get(dentry); - if (unlikely(ret)) - return ret; - mutex_lock(&dp->lock); ret = simple_read_from_buffer(user_buf, count, ppos, &dp->test.custom, sizeof(dp->test.custom)); mutex_unlock(&dp->lock); - - debugfs_file_put(dentry); return ret; } @@ -2047,18 +2025,13 @@ static ssize_t zynqmp_dp_custom_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - struct dentry *dentry = file->f_path.dentry; struct zynqmp_dp *dp = file->private_data; ssize_t ret; char buf[sizeof(dp->test.custom)]; - ret = debugfs_file_get(dentry); - if (unlikely(ret)) - return ret; - ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); if (ret < 0) - goto out; + return ret; mutex_lock(&dp->lock); memcpy(dp->test.custom, buf, ret); @@ -2066,9 +2039,6 @@ static ssize_t zynqmp_dp_custom_write(struct file *file, ret = zynqmp_dp_set_test_pattern(dp, dp->test.pattern, dp->test.custom) ?: ret; mutex_unlock(&dp->lock); - -out: - debugfs_file_put(dentry); return ret; } diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index 8c86101c26cb..110f2b355db4 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*}; +use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*, sync::Arc}; use crate::gpu::Gpu; @@ -34,7 +34,10 @@ impl pci::Driver for NovaCore { pdev.enable_device_mem()?; pdev.set_master(); - let bar = pdev.iomap_region_sized::(0, c_str!("nova-core/bar0"))?; + let bar = Arc::pin_init( + pdev.iomap_region_sized::(0, c_str!("nova-core/bar0")), + GFP_KERNEL, + )?; let this = KBox::pin_init( try_pin_init!(Self { diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 60b86f370284..47653c14838b 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use kernel::{device, devres::Devres, error::code::*, pci, prelude::*}; +use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc}; use crate::driver::Bar0; use crate::firmware::{Firmware, FIRMWARE_VERSION}; @@ -161,14 +161,14 @@ impl Spec { pub(crate) struct Gpu { spec: Spec, /// MMIO mapping of PCI BAR 0 - bar: Devres, + bar: Arc>, fw: Firmware, } impl Gpu { pub(crate) fn new( pdev: &pci::Device, - devres_bar: Devres, + devres_bar: Arc>, ) -> Result> { let bar = devres_bar.access(pdev.as_ref())?; let spec = Spec::new(bar)?; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 768f4df623c4..a63be219525a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2824,7 +2824,7 @@ static const struct bin_attribute *hid_dev_bin_attrs[] = { }; static const struct attribute_group hid_dev_group = { .attrs = hid_dev_attrs, - .bin_attrs_new = hid_dev_bin_attrs, + .bin_attrs = hid_dev_bin_attrs, }; __ATTRIBUTE_GROUPS(hid_dev); diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 3048297569c5..7b09adfa44a1 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -258,7 +258,7 @@ static const struct bin_attribute *const arvo_bin_attributes[] = { static const struct attribute_group arvo_group = { .attrs = arvo_attrs, - .bin_attrs_new = arvo_bin_attributes, + .bin_attrs = arvo_bin_attributes, }; static const struct attribute_group *arvo_groups[] = { diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h index 0f9a2db04df9..e931d0b48efe 100644 --- a/drivers/hid/hid-roccat-common.h +++ b/drivers/hid/hid-roccat-common.h @@ -71,8 +71,8 @@ ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE); \ static const struct bin_attribute bin_attr_ ## thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = SIZE, \ - .read_new = roccat_common2_sysfs_read_ ## thingy, \ - .write_new = roccat_common2_sysfs_write_ ## thingy \ + .read = roccat_common2_sysfs_read_ ## thingy, \ + .write = roccat_common2_sysfs_write_ ## thingy \ } #define ROCCAT_COMMON2_BIN_ATTRIBUTE_R(thingy, COMMAND, SIZE) \ @@ -80,7 +80,7 @@ ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE); \ static const struct bin_attribute bin_attr_ ## thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = SIZE, \ - .read_new = roccat_common2_sysfs_read_ ## thingy, \ + .read = roccat_common2_sysfs_read_ ## thingy, \ } #define ROCCAT_COMMON2_BIN_ATTRIBUTE_W(thingy, COMMAND, SIZE) \ @@ -88,7 +88,7 @@ ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE); \ static const struct bin_attribute bin_attr_ ## thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = SIZE, \ - .write_new = roccat_common2_sysfs_write_ ## thingy \ + .write = roccat_common2_sysfs_write_ ## thingy \ } #endif diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 65a84bfcc2f8..339378771ed5 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -181,8 +181,8 @@ ISKU_SYSFS_RW(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = ISKU_SIZE_ ## THINGY, \ - .read_new = isku_sysfs_read_ ## thingy, \ - .write_new = isku_sysfs_write_ ## thingy \ + .read = isku_sysfs_read_ ## thingy, \ + .write = isku_sysfs_write_ ## thingy \ } #define ISKU_BIN_ATTR_R(thingy, THINGY) \ @@ -190,7 +190,7 @@ ISKU_SYSFS_R(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = ISKU_SIZE_ ## THINGY, \ - .read_new = isku_sysfs_read_ ## thingy, \ + .read = isku_sysfs_read_ ## thingy, \ } #define ISKU_BIN_ATTR_W(thingy, THINGY) \ @@ -198,7 +198,7 @@ ISKU_SYSFS_W(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = ISKU_SIZE_ ## THINGY, \ - .write_new = isku_sysfs_write_ ## thingy \ + .write = isku_sysfs_write_ ## thingy \ } ISKU_BIN_ATTR_RW(macro, MACRO); @@ -238,7 +238,7 @@ static const struct bin_attribute *const isku_bin_attributes[] = { static const struct attribute_group isku_group = { .attrs = isku_attrs, - .bin_attrs_new = isku_bin_attributes, + .bin_attrs = isku_bin_attributes, }; static const struct attribute_group *isku_groups[] = { diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index b3c0242e5a37..fabc08efcfd8 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -385,8 +385,8 @@ static ssize_t kone_sysfs_write_profilex(struct file *fp, static const struct bin_attribute bin_attr_profile##number = { \ .attr = { .name = "profile" #number, .mode = 0660 }, \ .size = sizeof(struct kone_profile), \ - .read_new = kone_sysfs_read_profilex, \ - .write_new = kone_sysfs_write_profilex, \ + .read = kone_sysfs_read_profilex, \ + .write = kone_sysfs_write_profilex, \ .private = &profile_numbers[number-1], \ } PROFILE_ATTR(1); @@ -646,7 +646,7 @@ static const struct bin_attribute *const kone_bin_attributes[] = { static const struct attribute_group kone_group = { .attrs = kone_attrs, - .bin_attrs_new = kone_bin_attributes, + .bin_attrs = kone_bin_attributes, }; static const struct attribute_group *kone_groups[] = { diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 5d8a5ce88b4c..77d45d36421a 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -153,8 +153,8 @@ KONEPLUS_SYSFS_RW(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ - .read_new = koneplus_sysfs_read_ ## thingy, \ - .write_new = koneplus_sysfs_write_ ## thingy \ + .read = koneplus_sysfs_read_ ## thingy, \ + .write = koneplus_sysfs_write_ ## thingy \ } #define KONEPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \ @@ -162,7 +162,7 @@ KONEPLUS_SYSFS_R(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ - .read_new = koneplus_sysfs_read_ ## thingy, \ + .read = koneplus_sysfs_read_ ## thingy, \ } #define KONEPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ @@ -170,7 +170,7 @@ KONEPLUS_SYSFS_W(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ - .write_new = koneplus_sysfs_write_ ## thingy \ + .write = koneplus_sysfs_write_ ## thingy \ } KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL); KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK); @@ -222,13 +222,13 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, static const struct bin_attribute bin_attr_profile##number##_settings = { \ .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ .size = KONEPLUS_SIZE_PROFILE_SETTINGS, \ - .read_new = koneplus_sysfs_read_profilex_settings, \ + .read = koneplus_sysfs_read_profilex_settings, \ .private = &profile_numbers[number-1], \ }; \ static const struct bin_attribute bin_attr_profile##number##_buttons = { \ .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ .size = KONEPLUS_SIZE_PROFILE_BUTTONS, \ - .read_new = koneplus_sysfs_read_profilex_buttons, \ + .read = koneplus_sysfs_read_profilex_buttons, \ .private = &profile_numbers[number-1], \ }; PROFILE_ATTR(1); @@ -346,7 +346,7 @@ static const struct bin_attribute *const koneplus_bin_attributes[] = { static const struct attribute_group koneplus_group = { .attrs = koneplus_attrs, - .bin_attrs_new = koneplus_bin_attributes, + .bin_attrs = koneplus_bin_attributes, }; static const struct attribute_group *koneplus_groups[] = { diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c index 7fb705789d4e..027bfc55ef9c 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -62,7 +62,7 @@ static const struct bin_attribute *const konepure_bin_attrs[] = { }; static const struct attribute_group konepure_group = { - .bin_attrs_new = konepure_bin_attrs, + .bin_attrs = konepure_bin_attrs, }; static const struct attribute_group *konepure_groups[] = { diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index e31e4a2e62d5..a66f1b4730f3 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -196,8 +196,8 @@ KOVAPLUS_SYSFS_RW(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = KOVAPLUS_SIZE_ ## THINGY, \ - .read_new = kovaplus_sysfs_read_ ## thingy, \ - .write_new = kovaplus_sysfs_write_ ## thingy \ + .read = kovaplus_sysfs_read_ ## thingy, \ + .write = kovaplus_sysfs_write_ ## thingy \ } #define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ @@ -205,7 +205,7 @@ KOVAPLUS_SYSFS_W(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = KOVAPLUS_SIZE_ ## THINGY, \ - .write_new = kovaplus_sysfs_write_ ## thingy \ + .write = kovaplus_sysfs_write_ ## thingy \ } KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL); KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO); @@ -252,13 +252,13 @@ static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, static const struct bin_attribute bin_attr_profile##number##_settings = { \ .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, \ - .read_new = kovaplus_sysfs_read_profilex_settings, \ + .read = kovaplus_sysfs_read_profilex_settings, \ .private = &profile_numbers[number-1], \ }; \ static const struct bin_attribute bin_attr_profile##number##_buttons = { \ .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, \ - .read_new = kovaplus_sysfs_read_profilex_buttons, \ + .read = kovaplus_sysfs_read_profilex_buttons, \ .private = &profile_numbers[number-1], \ }; PROFILE_ATTR(1); @@ -399,7 +399,7 @@ static const struct bin_attribute *const kovaplus_bin_attributes[] = { static const struct attribute_group kovaplus_group = { .attrs = kovaplus_attrs, - .bin_attrs_new = kovaplus_bin_attributes, + .bin_attrs = kovaplus_bin_attributes, }; static const struct attribute_group *kovaplus_groups[] = { diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index 023ec64b4b0e..45e30549c236 100644 --- a/drivers/hid/hid-roccat-lua.c +++ b/drivers/hid/hid-roccat-lua.c @@ -88,8 +88,8 @@ LUA_SYSFS_R(thingy, THINGY) \ static const struct bin_attribute lua_ ## thingy ## _attr = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = LUA_SIZE_ ## THINGY, \ - .read_new = lua_sysfs_read_ ## thingy, \ - .write_new = lua_sysfs_write_ ## thingy \ + .read = lua_sysfs_read_ ## thingy, \ + .write = lua_sysfs_write_ ## thingy \ }; LUA_BIN_ATTRIBUTE_RW(control, CONTROL) diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 2b53fbfbb897..de2da6086e0b 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -154,8 +154,8 @@ PYRA_SYSFS_RW(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = PYRA_SIZE_ ## THINGY, \ - .read_new = pyra_sysfs_read_ ## thingy, \ - .write_new = pyra_sysfs_write_ ## thingy \ + .read = pyra_sysfs_read_ ## thingy, \ + .write = pyra_sysfs_write_ ## thingy \ } #define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ @@ -163,7 +163,7 @@ PYRA_SYSFS_R(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size_new = PYRA_SIZE_ ## THINGY, \ - .read_new = pyra_sysfs_read_ ## thingy, \ + .read = pyra_sysfs_read_ ## thingy, \ } #define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ @@ -171,7 +171,7 @@ PYRA_SYSFS_W(thingy, THINGY); \ static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = PYRA_SIZE_ ## THINGY, \ - .write_new = pyra_sysfs_write_ ## thingy \ + .write = pyra_sysfs_write_ ## thingy \ } PYRA_BIN_ATTRIBUTE_W(control, CONTROL); @@ -219,13 +219,13 @@ static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, static const struct bin_attribute bin_attr_profile##number##_settings = { \ .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ .size = PYRA_SIZE_PROFILE_SETTINGS, \ - .read_new = pyra_sysfs_read_profilex_settings, \ + .read = pyra_sysfs_read_profilex_settings, \ .private = &profile_numbers[number-1], \ }; \ static const struct bin_attribute bin_attr_profile##number##_buttons = { \ .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ .size = PYRA_SIZE_PROFILE_BUTTONS, \ - .read_new = pyra_sysfs_read_profilex_buttons, \ + .read = pyra_sysfs_read_profilex_buttons, \ .private = &profile_numbers[number-1], \ }; PROFILE_ATTR(1); @@ -355,7 +355,7 @@ static const struct bin_attribute *const pyra_bin_attributes[] = { static const struct attribute_group pyra_group = { .attrs = pyra_attrs, - .bin_attrs_new = pyra_bin_attributes, + .bin_attrs = pyra_bin_attributes, }; static const struct attribute_group *pyra_groups[] = { diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c index 902dac1e714e..36911c9da4fe 100644 --- a/drivers/hid/hid-roccat-ryos.c +++ b/drivers/hid/hid-roccat-ryos.c @@ -70,7 +70,7 @@ static const struct bin_attribute *const ryos_bin_attrs[] = { }; static const struct attribute_group ryos_group = { - .bin_attrs_new = ryos_bin_attrs, + .bin_attrs = ryos_bin_attrs, }; static const struct attribute_group *ryos_groups[] = { diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 7399b8ffb5c7..fb2e464c3ada 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -42,7 +42,7 @@ static const struct bin_attribute *const savu_bin_attrs[] = { }; static const struct attribute_group savu_group = { - .bin_attrs_new = savu_bin_attrs, + .bin_attrs = savu_bin_attrs, }; static const struct attribute_group *savu_groups[] = { diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 064bc83840a6..03b5a7e8c361 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -187,41 +188,48 @@ struct lpi2c_imx_struct { struct i2c_client *target; }; +#define lpi2c_imx_read_msr_poll_timeout(atomic, val, cond) \ + (atomic ? readl_poll_timeout_atomic(lpi2c_imx->base + LPI2C_MSR, val, \ + cond, 0, 500000) : \ + readl_poll_timeout(lpi2c_imx->base + LPI2C_MSR, val, cond, \ + 0, 500000)) + static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx, unsigned int enable) { writel(enable, lpi2c_imx->base + LPI2C_MIER); } -static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx) +static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) { - unsigned long orig_jiffies = jiffies; unsigned int temp; + int err; - while (1) { - temp = readl(lpi2c_imx->base + LPI2C_MSR); + err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, + temp & (MSR_ALF | MSR_BBF | MSR_MBF)); - /* check for arbitration lost, clear if set */ - if (temp & MSR_ALF) { - writel(temp, lpi2c_imx->base + LPI2C_MSR); - return -EAGAIN; - } + /* check for arbitration lost, clear if set */ + if (temp & MSR_ALF) { + writel(temp, lpi2c_imx->base + LPI2C_MSR); + return -EAGAIN; + } - if (temp & (MSR_BBF | MSR_MBF)) - break; - - if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { - dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n"); - if (lpi2c_imx->adapter.bus_recovery_info) - i2c_recover_bus(&lpi2c_imx->adapter); - return -ETIMEDOUT; - } - schedule(); + /* check for bus not busy */ + if (err) { + dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n"); + if (lpi2c_imx->adapter.bus_recovery_info) + i2c_recover_bus(&lpi2c_imx->adapter); + return -ETIMEDOUT; } return 0; } +static u32 lpi2c_imx_txfifo_cnt(struct lpi2c_imx_struct *lpi2c_imx) +{ + return readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff; +} + static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx) { unsigned int bitrate = lpi2c_imx->bitrate; @@ -242,7 +250,7 @@ static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx) } static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx, - struct i2c_msg *msgs) + struct i2c_msg *msgs, bool atomic) { unsigned int temp; @@ -254,30 +262,23 @@ static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx, temp = i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8); writel(temp, lpi2c_imx->base + LPI2C_MTDR); - return lpi2c_imx_bus_busy(lpi2c_imx); + return lpi2c_imx_bus_busy(lpi2c_imx, atomic); } -static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx) +static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) { - unsigned long orig_jiffies = jiffies; unsigned int temp; + int err; writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR); - do { - temp = readl(lpi2c_imx->base + LPI2C_MSR); - if (temp & MSR_SDF) - break; + err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, temp & MSR_SDF); - if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { - dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n"); - if (lpi2c_imx->adapter.bus_recovery_info) - i2c_recover_bus(&lpi2c_imx->adapter); - break; - } - schedule(); - - } while (1); + if (err) { + dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n"); + if (lpi2c_imx->adapter.bus_recovery_info) + i2c_recover_bus(&lpi2c_imx->adapter); + } } /* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */ @@ -391,28 +392,25 @@ static int lpi2c_imx_pio_msg_complete(struct lpi2c_imx_struct *lpi2c_imx) return time_left ? 0 : -ETIMEDOUT; } -static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx) +static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) { - unsigned long orig_jiffies = jiffies; - u32 txcnt; + unsigned int temp; + int err; - do { - txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff; + err = lpi2c_imx_read_msr_poll_timeout(atomic, temp, + (temp & MSR_NDF) || !lpi2c_imx_txfifo_cnt(lpi2c_imx)); - if (readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) { - dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n"); - return -EIO; - } + if (temp & MSR_NDF) { + dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n"); + return -EIO; + } - if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { - dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n"); - if (lpi2c_imx->adapter.bus_recovery_info) - i2c_recover_bus(&lpi2c_imx->adapter); - return -ETIMEDOUT; - } - schedule(); - - } while (txcnt); + if (err) { + dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n"); + if (lpi2c_imx->adapter.bus_recovery_info) + i2c_recover_bus(&lpi2c_imx->adapter); + return -ETIMEDOUT; + } return 0; } @@ -436,7 +434,7 @@ static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx) writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR); } -static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx) +static bool lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) { unsigned int data, txcnt; @@ -451,13 +449,19 @@ static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx) txcnt++; } - if (lpi2c_imx->delivered < lpi2c_imx->msglen) - lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE); - else + if (lpi2c_imx->delivered < lpi2c_imx->msglen) { + if (!atomic) + lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE); + return false; + } + + if (!atomic) complete(&lpi2c_imx->complete); + + return true; } -static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx) +static bool lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx, bool atomic) { unsigned int blocklen, remaining; unsigned int temp, data; @@ -482,8 +486,9 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx) remaining = lpi2c_imx->msglen - lpi2c_imx->delivered; if (!remaining) { - complete(&lpi2c_imx->complete); - return; + if (!atomic) + complete(&lpi2c_imx->complete); + return true; } /* not finished, still waiting for rx data */ @@ -501,7 +506,10 @@ static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx) writel(temp, lpi2c_imx->base + LPI2C_MTDR); } - lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE); + if (!atomic) + lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE); + + return false; } static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx, @@ -509,11 +517,29 @@ static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx, { lpi2c_imx->tx_buf = msgs->buf; lpi2c_imx_set_tx_watermark(lpi2c_imx); - lpi2c_imx_write_txfifo(lpi2c_imx); + lpi2c_imx_write_txfifo(lpi2c_imx, false); } -static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx, - struct i2c_msg *msgs) +static int lpi2c_imx_write_atomic(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) +{ + u32 temp; + int err; + + lpi2c_imx->tx_buf = msgs->buf; + + err = lpi2c_imx_read_msr_poll_timeout(true, temp, + (temp & MSR_NDF) || + lpi2c_imx_write_txfifo(lpi2c_imx, true)); + + if (temp & MSR_NDF) + return -EIO; + + return err; +} + +static void lpi2c_imx_read_init(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) { unsigned int temp; @@ -524,8 +550,43 @@ static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx, temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1; temp |= (RECV_DATA << 8); writel(temp, lpi2c_imx->base + LPI2C_MTDR); +} - lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE); +static bool lpi2c_imx_read_chunk_atomic(struct lpi2c_imx_struct *lpi2c_imx) +{ + u32 rxcnt; + + rxcnt = (readl(lpi2c_imx->base + LPI2C_MFSR) >> 16) & 0xFF; + if (!rxcnt) + return false; + + if (!lpi2c_imx_read_rxfifo(lpi2c_imx, true)) + return false; + + return true; +} + +static int lpi2c_imx_read_atomic(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msgs) +{ + u32 temp; + int tmo_us; + + tmo_us = 1000000; + do { + if (lpi2c_imx_read_chunk_atomic(lpi2c_imx)) + return 0; + + temp = readl(lpi2c_imx->base + LPI2C_MSR); + + if (temp & MSR_NDF) + return -EIO; + + udelay(100); + tmo_us -= 100; + } while (tmo_us > 0); + + return -ETIMEDOUT; } static bool is_use_dma(struct lpi2c_imx_struct *lpi2c_imx, struct i2c_msg *msg) @@ -545,14 +606,27 @@ static int lpi2c_imx_pio_xfer(struct lpi2c_imx_struct *lpi2c_imx, { reinit_completion(&lpi2c_imx->complete); - if (msg->flags & I2C_M_RD) - lpi2c_imx_read(lpi2c_imx, msg); - else + if (msg->flags & I2C_M_RD) { + lpi2c_imx_read_init(lpi2c_imx, msg); + lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE); + } else { lpi2c_imx_write(lpi2c_imx, msg); + } return lpi2c_imx_pio_msg_complete(lpi2c_imx); } +static int lpi2c_imx_pio_xfer_atomic(struct lpi2c_imx_struct *lpi2c_imx, + struct i2c_msg *msg) +{ + if (msg->flags & I2C_M_RD) { + lpi2c_imx_read_init(lpi2c_imx, msg); + return lpi2c_imx_read_atomic(lpi2c_imx, msg); + } + + return lpi2c_imx_write_atomic(lpi2c_imx, msg); +} + static int lpi2c_imx_dma_timeout_calculate(struct lpi2c_imx_struct *lpi2c_imx) { unsigned long time = 0; @@ -563,7 +637,7 @@ static int lpi2c_imx_dma_timeout_calculate(struct lpi2c_imx_struct *lpi2c_imx) time += 1; /* Double calculated time */ - return msecs_to_jiffies(time * MSEC_PER_SEC); + return secs_to_jiffies(time); } static int lpi2c_imx_alloc_rx_cmd_buf(struct lpi2c_imx_struct *lpi2c_imx) @@ -947,8 +1021,8 @@ disable_dma: return ret; } -static int lpi2c_imx_xfer(struct i2c_adapter *adapter, - struct i2c_msg *msgs, int num) +static int lpi2c_imx_xfer_common(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num, bool atomic) { struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter); unsigned int temp; @@ -959,7 +1033,7 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter, return result; for (i = 0; i < num; i++) { - result = lpi2c_imx_start(lpi2c_imx, &msgs[i]); + result = lpi2c_imx_start(lpi2c_imx, &msgs[i], atomic); if (result) goto disable; @@ -971,28 +1045,33 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter, lpi2c_imx->tx_buf = NULL; lpi2c_imx->delivered = 0; lpi2c_imx->msglen = msgs[i].len; - init_completion(&lpi2c_imx->complete); - if (is_use_dma(lpi2c_imx, &msgs[i])) { - result = lpi2c_imx_dma_xfer(lpi2c_imx, &msgs[i]); - if (result && lpi2c_imx->dma->using_pio_mode) - result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + if (atomic) { + result = lpi2c_imx_pio_xfer_atomic(lpi2c_imx, &msgs[i]); } else { - result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + init_completion(&lpi2c_imx->complete); + + if (is_use_dma(lpi2c_imx, &msgs[i])) { + result = lpi2c_imx_dma_xfer(lpi2c_imx, &msgs[i]); + if (result && lpi2c_imx->dma->using_pio_mode) + result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + } else { + result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]); + } } if (result) goto stop; if (!(msgs[i].flags & I2C_M_RD)) { - result = lpi2c_imx_txfifo_empty(lpi2c_imx); + result = lpi2c_imx_txfifo_empty(lpi2c_imx, atomic); if (result) goto stop; } } stop: - lpi2c_imx_stop(lpi2c_imx); + lpi2c_imx_stop(lpi2c_imx, atomic); temp = readl(lpi2c_imx->base + LPI2C_MSR); if ((temp & MSR_NDF) && !result) @@ -1008,6 +1087,16 @@ disable: return (result < 0) ? result : num; } +static int lpi2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +{ + return lpi2c_imx_xfer_common(adapter, msgs, num, false); +} + +static int lpi2c_imx_xfer_atomic(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +{ + return lpi2c_imx_xfer_common(adapter, msgs, num, true); +} + static irqreturn_t lpi2c_imx_target_isr(struct lpi2c_imx_struct *lpi2c_imx, u32 ssr, u32 sier_filter) { @@ -1070,9 +1159,9 @@ static irqreturn_t lpi2c_imx_master_isr(struct lpi2c_imx_struct *lpi2c_imx) if (temp & MSR_NDF) complete(&lpi2c_imx->complete); else if (temp & MSR_RDF) - lpi2c_imx_read_rxfifo(lpi2c_imx); + lpi2c_imx_read_rxfifo(lpi2c_imx, false); else if (temp & MSR_TDF) - lpi2c_imx_write_txfifo(lpi2c_imx); + lpi2c_imx_write_txfifo(lpi2c_imx, false); return IRQ_HANDLED; } @@ -1269,6 +1358,7 @@ static u32 lpi2c_imx_func(struct i2c_adapter *adapter) static const struct i2c_algorithm lpi2c_imx_algo = { .xfer = lpi2c_imx_xfer, + .xfer_atomic = lpi2c_imx_xfer_atomic, .functionality = lpi2c_imx_func, .reg_target = lpi2c_imx_register_target, .unreg_target = lpi2c_imx_unregister_target, diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 205cc132fdec..60f5c790ad7c 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -891,13 +892,13 @@ static enum hrtimer_restart i2c_imx_slave_timeout(struct hrtimer *t) struct imx_i2c_struct *i2c_imx = container_of(t, struct imx_i2c_struct, slave_timer); unsigned int ctl, status; - unsigned long flags; - spin_lock_irqsave(&i2c_imx->slave_lock, flags); + guard(spinlock_irqsave)(&i2c_imx->slave_lock); + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); i2c_imx_slave_handle(i2c_imx, status, ctl); - spin_unlock_irqrestore(&i2c_imx->slave_lock, flags); + return HRTIMER_NORESTART; } @@ -1126,32 +1127,26 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id) { struct imx_i2c_struct *i2c_imx = dev_id; unsigned int ctl, status; - unsigned long flags; - spin_lock_irqsave(&i2c_imx->slave_lock, flags); - status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); - ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) { + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); + + if (!(status & I2SR_IIF)) + return IRQ_NONE; - if (status & I2SR_IIF) { i2c_imx_clear_irq(i2c_imx, I2SR_IIF); - if (i2c_imx->slave) { - if (!(ctl & I2CR_MSTA)) { - irqreturn_t ret; - ret = i2c_imx_slave_handle(i2c_imx, - status, ctl); - spin_unlock_irqrestore(&i2c_imx->slave_lock, - flags); - return ret; - } + if (i2c_imx->slave) { + if (!(ctl & I2CR_MSTA)) + return i2c_imx_slave_handle(i2c_imx, + status, ctl); + i2c_imx_slave_finish_op(i2c_imx); } - spin_unlock_irqrestore(&i2c_imx->slave_lock, flags); - return i2c_imx_master_isr(i2c_imx, status); } - spin_unlock_irqrestore(&i2c_imx->slave_lock, flags); - return IRQ_NONE; + return i2c_imx_master_isr(i2c_imx, status); } static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index 23375f7fe3ad..9c164a4b9bb9 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -79,6 +79,7 @@ #define ICIER_SPIE BIT(3) #define ICSR2_NACKF BIT(4) +#define ICSR2_STOP BIT(3) #define ICBR_RESERVED GENMASK(7, 5) /* Should be 1 on writes */ @@ -102,6 +103,8 @@ enum riic_reg_list { struct riic_of_data { const u8 *regs; + const struct riic_irq_desc *irqs; + u8 num_irqs; bool fast_mode_plus; }; @@ -324,6 +327,19 @@ static irqreturn_t riic_stop_isr(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t riic_eei_isr(int irq, void *data) +{ + u8 icsr2 = riic_readb(data, RIIC_ICSR2); + + if (icsr2 & ICSR2_NACKF) + return riic_tend_isr(irq, data); + + if (icsr2 & ICSR2_STOP) + return riic_stop_isr(irq, data); + + return IRQ_NONE; +} + static u32 riic_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; @@ -495,6 +511,13 @@ static const struct riic_irq_desc riic_irqs[] = { { .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" }, }; +static const struct riic_irq_desc riic_rzt2h_irqs[] = { + { .res_num = 0, .isr = riic_eei_isr, .name = "riic-eei" }, + { .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rxi" }, + { .res_num = 2, .isr = riic_tdre_isr, .name = "riic-txi" }, + { .res_num = 3, .isr = riic_tend_isr, .name = "riic-tei" }, +}; + static int riic_i2c_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -520,21 +543,23 @@ static int riic_i2c_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(riic->rstc), "failed to acquire deasserted reset\n"); - for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) { + riic->info = of_device_get_match_data(dev); + + for (i = 0; i < riic->info->num_irqs; i++) { + const struct riic_irq_desc *irq_desc; int irq; - irq = platform_get_irq(pdev, riic_irqs[i].res_num); + irq_desc = &riic->info->irqs[i]; + irq = platform_get_irq(pdev, irq_desc->res_num); if (irq < 0) return irq; - ret = devm_request_irq(dev, irq, riic_irqs[i].isr, - 0, riic_irqs[i].name, riic); + ret = devm_request_irq(dev, irq, irq_desc->isr, 0, irq_desc->name, riic); if (ret) return dev_err_probe(dev, ret, "failed to request irq %s\n", - riic_irqs[i].name); + irq_desc->name); } - riic->info = of_device_get_match_data(dev); adap = &riic->adapter; i2c_set_adapdata(adap, riic); @@ -606,11 +631,15 @@ static const u8 riic_rz_a_regs[RIIC_REG_END] = { static const struct riic_of_data riic_rz_a_info = { .regs = riic_rz_a_regs, + .irqs = riic_irqs, + .num_irqs = ARRAY_SIZE(riic_irqs), .fast_mode_plus = true, }; static const struct riic_of_data riic_rz_a1h_info = { .regs = riic_rz_a_regs, + .irqs = riic_irqs, + .num_irqs = ARRAY_SIZE(riic_irqs), }; static const u8 riic_rz_v2h_regs[RIIC_REG_END] = { @@ -630,9 +659,17 @@ static const u8 riic_rz_v2h_regs[RIIC_REG_END] = { static const struct riic_of_data riic_rz_v2h_info = { .regs = riic_rz_v2h_regs, + .irqs = riic_irqs, + .num_irqs = ARRAY_SIZE(riic_irqs), .fast_mode_plus = true, }; +static const struct riic_of_data riic_rz_t2h_info = { + .regs = riic_rz_v2h_regs, + .irqs = riic_rzt2h_irqs, + .num_irqs = ARRAY_SIZE(riic_rzt2h_irqs), +}; + static int riic_i2c_suspend(struct device *dev) { struct riic_dev *riic = dev_get_drvdata(dev); @@ -683,10 +720,11 @@ static const struct dev_pm_ops riic_i2c_pm_ops = { }; static const struct of_device_id riic_i2c_dt_ids[] = { - { .compatible = "renesas,riic-rz", .data = &riic_rz_a_info }, { .compatible = "renesas,riic-r7s72100", .data = &riic_rz_a1h_info, }, { .compatible = "renesas,riic-r9a09g057", .data = &riic_rz_v2h_info }, - { /* Sentinel */ }, + { .compatible = "renesas,riic-r9a09g077", .data = &riic_rz_t2h_info }, + { .compatible = "renesas,riic-rz", .data = &riic_rz_a_info }, + { /* Sentinel */ } }; static struct platform_driver riic_i2c_driver = { diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 750fff3d2389..bf28f8e3ee6b 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -422,12 +423,8 @@ static void st_i2c_wr_fill_tx_fifo(struct st_i2c_dev *i2c_dev) tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT); tx_fstat &= SSC_TX_FSTAT_STATUS; - if (c->count < (SSC_TXFIFO_SIZE - tx_fstat)) - i = c->count; - else - i = SSC_TXFIFO_SIZE - tx_fstat; - - for (; i > 0; i--, c->count--, c->buf++) + for (i = min(c->count, SSC_TXFIFO_SIZE - tx_fstat); + i > 0; i--, c->count--, c->buf++) st_i2c_write_tx_fifo(i2c_dev, *c->buf); } @@ -439,7 +436,7 @@ static void st_i2c_wr_fill_tx_fifo(struct st_i2c_dev *i2c_dev) * This functions fills the Tx FIFO with fixed pattern when * in read mode to trigger clock. */ -static void st_i2c_rd_fill_tx_fifo(struct st_i2c_dev *i2c_dev, int max) +static void st_i2c_rd_fill_tx_fifo(struct st_i2c_dev *i2c_dev, u32 max) { struct st_i2c_client *c = &i2c_dev->client; u32 tx_fstat, sta; @@ -452,12 +449,8 @@ static void st_i2c_rd_fill_tx_fifo(struct st_i2c_dev *i2c_dev, int max) tx_fstat = readl_relaxed(i2c_dev->base + SSC_TX_FSTAT); tx_fstat &= SSC_TX_FSTAT_STATUS; - if (max < (SSC_TXFIFO_SIZE - tx_fstat)) - i = max; - else - i = SSC_TXFIFO_SIZE - tx_fstat; - - for (; i > 0; i--, c->xfered++) + for (i = min(max, SSC_TXFIFO_SIZE - tx_fstat); + i > 0; i--, c->xfered++) st_i2c_write_tx_fifo(i2c_dev, 0xff); } diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 73a7b8894c0d..c8b4b404f6c1 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "i2c-stm32.h" @@ -722,7 +723,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n", setup->rise_time, setup->fall_time); dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n", - (i2c_dev->analog_filter ? "On" : "Off"), i2c_dev->dnf); + str_on_off(i2c_dev->analog_filter), i2c_dev->dnf); i2c_dev->bus_rate = setup->speed_freq; diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 6316360475e7..4f05afab161f 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -253,6 +253,7 @@ struct tegra_i2c_hw_feature { * @dma_phys: handle to DMA resources * @dma_buf: pointer to allocated DMA buffer * @dma_buf_size: DMA buffer size + * @dma_dev: DMA device used for transfers * @dma_mode: indicates active DMA transfer * @dma_complete: DMA completion notifier * @atomic_mode: indicates active atomic transfer diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index d2499f302b50..3445cc3b476b 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -250,7 +250,7 @@ static int i2c_acpi_get_info(struct acpi_device *adev, if (adapter) { /* The adapter must match the one in I2cSerialBus() connector */ - if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle) + if (!device_match_acpi_handle(&adapter->dev, lookup.adapter_handle)) return -ENODEV; } else { struct acpi_device *adapter_adev; diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 38eabf1173da..ecca8c006b02 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1066,7 +1066,13 @@ void i2c_unregister_device(struct i2c_client *client) of_node_clear_flag(to_of_node(fwnode), OF_POPULATED); else if (is_acpi_device_node(fwnode)) acpi_device_clear_enumerated(to_acpi_device_node(fwnode)); - fwnode_handle_put(fwnode); + + /* + * If the primary fwnode is a software node it is free-ed by + * device_remove_software_node() below, avoid double-free. + */ + if (!is_software_node(fwnode)) + fwnode_handle_put(fwnode); device_remove_software_node(&client->dev); device_unregister(&client->dev); diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c index 275d1d0e910f..6bc2ef650a74 100644 --- a/drivers/i2c/i2c-slave-eeprom.c +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -165,8 +165,8 @@ static int i2c_slave_eeprom_probe(struct i2c_client *client) sysfs_bin_attr_init(&eeprom->bin); eeprom->bin.attr.name = "slave-eeprom"; eeprom->bin.attr.mode = S_IRUSR | S_IWUSR; - eeprom->bin.read_new = i2c_slave_eeprom_bin_read; - eeprom->bin.write_new = i2c_slave_eeprom_bin_write; + eeprom->bin.read = i2c_slave_eeprom_bin_read; + eeprom->bin.write = i2c_slave_eeprom_bin_write; eeprom->bin.size = size; ret = sysfs_create_bin_file(&client->dev.kobj, &eeprom->bin); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 5bb26af0f532..b9f370c9f018 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -442,8 +442,7 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc) raw_spin_lock_init(&data->lock); - data->irq = irq_domain_create_linear(of_fwnode_handle(client->dev.of_node), - data->chip->nchans, + data->irq = irq_domain_create_linear(dev_fwnode(&client->dev), data->chip->nchans, &irq_domain_simple_ops, data); if (!data->irq) return -ENODEV; diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index a8e71679ec21..303bc308f80a 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -1365,7 +1365,7 @@ static const struct bin_attribute *const bno055_bin_attrs[] = { static const struct attribute_group bno055_attrs_group = { .attrs = bno055_attrs, - .bin_attrs_new = bno055_bin_attrs, + .bin_attrs = bno055_bin_attrs, }; static const struct iio_info bno055_info = { diff --git a/drivers/infiniband/hw/hfi1/debugfs.c b/drivers/infiniband/hw/hfi1/debugfs.c index a1e01b447265..ac37ab7f8995 100644 --- a/drivers/infiniband/hw/hfi1/debugfs.c +++ b/drivers/infiniband/hw/hfi1/debugfs.c @@ -22,34 +22,6 @@ static struct dentry *hfi1_dbg_root; -/* wrappers to enforce srcu in seq file */ -ssize_t hfi1_seq_read(struct file *file, char __user *buf, size_t size, - loff_t *ppos) -{ - struct dentry *d = file->f_path.dentry; - ssize_t r; - - r = debugfs_file_get(d); - if (unlikely(r)) - return r; - r = seq_read(file, buf, size, ppos); - debugfs_file_put(d); - return r; -} - -loff_t hfi1_seq_lseek(struct file *file, loff_t offset, int whence) -{ - struct dentry *d = file->f_path.dentry; - loff_t r; - - r = debugfs_file_get(d); - if (unlikely(r)) - return r; - r = seq_lseek(file, offset, whence); - debugfs_file_put(d); - return r; -} - #define private2dd(file) (file_inode(file)->i_private) #define private2ppd(file) (file_inode(file)->i_private) diff --git a/drivers/infiniband/hw/hfi1/debugfs.h b/drivers/infiniband/hw/hfi1/debugfs.h index 54d952a4016c..65b48839abc6 100644 --- a/drivers/infiniband/hw/hfi1/debugfs.h +++ b/drivers/infiniband/hw/hfi1/debugfs.h @@ -33,16 +33,11 @@ static int _##name##_open(struct inode *inode, struct file *s) \ static const struct file_operations _##name##_file_ops = { \ .owner = THIS_MODULE, \ .open = _##name##_open, \ - .read = hfi1_seq_read, \ - .llseek = hfi1_seq_lseek, \ + .read = seq_read, \ + .llseek = seq_lseek, \ .release = seq_release \ } - -ssize_t hfi1_seq_read(struct file *file, char __user *buf, size_t size, - loff_t *ppos); -loff_t hfi1_seq_lseek(struct file *file, loff_t offset, int whence); - #ifdef CONFIG_DEBUG_FS void hfi1_dbg_ibdev_init(struct hfi1_ibdev *ibd); void hfi1_dbg_ibdev_exit(struct hfi1_ibdev *ibd); diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c index ec9ee59fcf0c..a45cbffd52c7 100644 --- a/drivers/infiniband/hw/hfi1/fault.c +++ b/drivers/infiniband/hw/hfi1/fault.c @@ -104,9 +104,6 @@ static ssize_t fault_opcodes_write(struct file *file, const char __user *buf, goto free_data; } - ret = debugfs_file_get(file->f_path.dentry); - if (unlikely(ret)) - goto free_data; ptr = data; token = ptr; for (ptr = data; *ptr; ptr = end + 1, token = ptr) { @@ -154,7 +151,6 @@ static ssize_t fault_opcodes_write(struct file *file, const char __user *buf, } ret = len; - debugfs_file_put(file->f_path.dentry); free_data: kfree(data); return ret; @@ -173,9 +169,6 @@ static ssize_t fault_opcodes_read(struct file *file, char __user *buf, data = kcalloc(datalen, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - ret = debugfs_file_get(file->f_path.dentry); - if (unlikely(ret)) - goto free_data; bit = find_first_bit(fault->opcodes, bitsize); while (bit < bitsize) { zero = find_next_zero_bit(fault->opcodes, bitsize, bit); @@ -189,11 +182,9 @@ static ssize_t fault_opcodes_read(struct file *file, char __user *buf, bit); bit = find_next_bit(fault->opcodes, bitsize, zero); } - debugfs_file_put(file->f_path.dentry); data[size - 1] = '\n'; data[size] = '\0'; ret = simple_read_from_buffer(buf, len, pos, data, size); -free_data: kfree(data); return ret; } diff --git a/drivers/infiniband/hw/hfi1/sysfs.c b/drivers/infiniband/hw/hfi1/sysfs.c index d94216c7d576..372cfd13dc61 100644 --- a/drivers/infiniband/hw/hfi1/sysfs.c +++ b/drivers/infiniband/hw/hfi1/sysfs.c @@ -134,7 +134,7 @@ static struct attribute *port_cc_attributes[] = { static const struct attribute_group port_cc_group = { .name = "CCMgtA", .attrs = port_cc_attributes, - .bin_attrs_new = port_cc_bin_attributes, + .bin_attrs = port_cc_bin_attributes, }; /* Start sc2vl */ diff --git a/drivers/infiniband/hw/qib/qib_sysfs.c b/drivers/infiniband/hw/qib/qib_sysfs.c index 805e37dc7621..a6571bc38366 100644 --- a/drivers/infiniband/hw/qib/qib_sysfs.c +++ b/drivers/infiniband/hw/qib/qib_sysfs.c @@ -295,7 +295,7 @@ static umode_t qib_ccmgta_is_bin_visible(struct kobject *kobj, static const struct attribute_group port_ccmgta_attribute_group = { .name = "CCMgtA", .is_bin_visible = qib_ccmgta_is_bin_visible, - .bin_attrs_new = port_ccmgta_attributes, + .bin_attrs = port_ccmgta_attributes, }; /* Start sl2vl */ diff --git a/drivers/input/touchscreen/goodix_berlin_core.c b/drivers/input/touchscreen/goodix_berlin_core.c index 02a1d9a465f2..c78d512d97cd 100644 --- a/drivers/input/touchscreen/goodix_berlin_core.c +++ b/drivers/input/touchscreen/goodix_berlin_core.c @@ -707,7 +707,7 @@ static const struct bin_attribute *const goodix_berlin_bin_attrs[] = { }; static const struct attribute_group goodix_berlin_attr_group = { - .bin_attrs_new = goodix_berlin_bin_attrs, + .bin_attrs = goodix_berlin_bin_attrs, }; const struct attribute_group *goodix_berlin_groups[] = { diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index c20ac8ccf52b..58592593b8e9 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -91,7 +91,7 @@ static const struct bin_attribute *const led_trigger_bin_attrs[] = { NULL, }; static const struct attribute_group led_trigger_group = { - .bin_attrs_new = led_trigger_bin_attrs, + .bin_attrs = led_trigger_bin_attrs, }; #endif diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index febb2c156cf6..d1d3a83d0122 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -432,7 +432,7 @@ static int solo_sysfs_init(struct solo_dev *solo_dev) sysfs_attr_init(&sdram_attr->attr); sdram_attr->attr.name = "sdram"; sdram_attr->attr.mode = 0440; - sdram_attr->read_new = sdram_show; + sdram_attr->read = sdram_show; sdram_attr->size = solo_dev->sdram_size; if (device_create_bin_file(dev, sdram_attr)) { diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index fc64474b8241..babdb60cc46c 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -888,7 +888,7 @@ static size_t c2port_bin_attr_size(struct kobject *kobj, static const struct attribute_group c2port_group = { .attrs = c2port_attrs, - .bin_attrs_new = c2port_bin_attrs, + .bin_attrs = c2port_bin_attrs, .bin_size = c2port_bin_attr_size, }; diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 5d5a70a62e98..cb09e056531a 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -194,8 +194,8 @@ static const struct bin_attribute ds1682_eeprom_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = DS1682_EEPROM_SIZE, - .read_new = ds1682_eeprom_read, - .write_new = ds1682_eeprom_write, + .read = ds1682_eeprom_read, + .write = ds1682_eeprom_write, }; static int ds1682_nvmem_read(void *priv, unsigned int offset, void *val, diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 1c36ad153e78..a3e4cada3b51 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -127,7 +127,7 @@ static const struct bin_attribute user_eeprom_attr = { .mode = S_IRUGO, }, .size = USER_EEPROM_SIZE, - .read_new = max6875_read, + .read = max6875_read, }; static int max6875_probe(struct i2c_client *client) diff --git a/drivers/misc/ocxl/sysfs.c b/drivers/misc/ocxl/sysfs.c index f194c159a778..1b6a86f17b6c 100644 --- a/drivers/misc/ocxl/sysfs.c +++ b/drivers/misc/ocxl/sysfs.c @@ -155,7 +155,7 @@ int ocxl_sysfs_register_afu(struct ocxl_file_info *info) info->attr_global_mmio.attr.name = "global_mmio_area"; info->attr_global_mmio.attr.mode = 0600; info->attr_global_mmio.size = info->afu->config.global_mmio_size; - info->attr_global_mmio.read_new = global_mmio_read; + info->attr_global_mmio.read = global_mmio_read; info->attr_global_mmio.mmap = global_mmio_mmap; rc = device_create_bin_file(&info->dev, &info->attr_global_mmio); if (rc) { diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 6121c0940cd1..7bee179841bc 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -655,8 +655,8 @@ static const struct bin_attribute pch_bin_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = PCH_PHUB_OROM_SIZE + 1, - .read_new = pch_phub_bin_read, - .write_new = pch_phub_bin_write, + .read = pch_phub_bin_read, + .write = pch_phub_bin_write, }; static int pch_phub_probe(struct pci_dev *pdev, diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index c69644be4176..c98ff8aa221c 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -85,8 +85,8 @@ static int sram_add_export(struct sram_dev *sram, struct sram_reserve *block, return -ENOMEM; part->battr.attr.mode = S_IRUSR | S_IWUSR; - part->battr.read_new = sram_read; - part->battr.write_new = sram_write; + part->battr.read = sram_read; + part->battr.write = sram_write; part->battr.size = block->size; return device_create_bin_file(sram->dev, &part->battr); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a0e2dce70434..874c6fe92855 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -882,7 +882,6 @@ void mmc_put_card(struct mmc_card *card, struct mmc_ctx *ctx) WARN_ON(ctx && host->claimer != ctx); mmc_release_host(host); - pm_runtime_mark_last_busy(&card->dev); pm_runtime_put_autosuspend(&card->dev); } EXPORT_SYMBOL(mmc_put_card); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index c3f0f41a426d..7232de1c0688 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -1111,6 +1111,19 @@ config MMC_OWL This selects support for the SD/MMC Host Controller on Actions Semi Owl SoCs. +config MMC_LOONGSON2 + tristate "Loongson-2K SD/SDIO/eMMC Host Interface support" + depends on LOONGARCH || COMPILE_TEST + depends on HAS_DMA + help + This selects support for the SD/SDIO/eMMC Host Controller on + Loongson-2K series CPUs. + + To compile this driver as a module, choose M here: the + module will be called mmc_loongson2. + + If unsure, say N. + config MMC_SDHCI_EXTERNAL_DMA bool diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 75bafc7b162b..5057fea8afb6 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o obj-$(CONFIG_MMC_BCM2835) += bcm2835.o obj-$(CONFIG_MMC_OWL) += owl-mmc.o +obj-$(CONFIG_MMC_LOONGSON2) += loongson2-mmc.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c index 24abd3a93da9..288c3a91a0af 100644 --- a/drivers/mmc/host/alcor.c +++ b/drivers/mmc/host/alcor.c @@ -1084,7 +1084,7 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) struct alcor_sdmmc_host *host; int ret; - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { dev_err(&pdev->dev, "Can't allocate MMC\n"); return -ENOMEM; @@ -1102,11 +1102,9 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, priv->irq, alcor_irq, alcor_irq_thread, IRQF_SHARED, DRV_NAME_ALCOR_PCI_SDMMC, host); - - if (ret) { - dev_err(&pdev->dev, "Failed to get irq for data line\n"); - goto free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to get irq for data line\n"); mutex_init(&host->cmd_mutex); INIT_DELAYED_WORK(&host->timeout_work, alcor_timeout_timer); @@ -1115,15 +1113,8 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) alcor_hw_init(host); dev_set_drvdata(&pdev->dev, host); - ret = mmc_add_host(mmc); - if (ret) - goto free_host; - return 0; - -free_host: - mmc_free_host(mmc); - return ret; + return mmc_add_host(mmc); } static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) @@ -1136,7 +1127,6 @@ static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) alcor_hw_uninit(host); mmc_remove_host(mmc); - mmc_free_host(mmc); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 0e0666c0bb6e..777342fb7657 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -38,6 +38,7 @@ #include #include #include +#include #define ATMCI_MAX_NR_SLOTS 2 @@ -541,7 +542,6 @@ static int atmci_regs_show(struct seq_file *s, void *v) memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE); spin_unlock_bh(&host->lock); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); seq_printf(s, "MR:\t0x%08x%s%s ", @@ -2248,7 +2248,7 @@ static int atmci_init_slot(struct atmel_mci *host, struct atmel_mci_slot *slot; int ret; - mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*slot)); if (!mmc) return -ENOMEM; @@ -2264,7 +2264,7 @@ static int atmci_init_slot(struct atmel_mci *host, "slot[%u]: bus_width=%u, detect_pin=%d, " "detect_is_active_high=%s, wp_pin=%d\n", id, slot_data->bus_width, desc_to_gpio(slot_data->detect_pin), - !gpiod_is_active_low(slot_data->detect_pin) ? "true" : "false", + str_true_false(!gpiod_is_active_low(slot_data->detect_pin)), desc_to_gpio(slot_data->wp_pin)); mmc->ops = &atmci_ops; @@ -2321,10 +2321,8 @@ static int atmci_init_slot(struct atmel_mci *host, host->slot[id] = slot; mmc_regulator_get_supply(mmc); ret = mmc_add_host(mmc); - if (ret) { - mmc_free_host(mmc); + if (ret) return ret; - } if (slot->detect_pin) { timer_setup(&slot->detect_timer, atmci_detect_change, 0); @@ -2362,7 +2360,6 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot, } slot->host->slot[id] = NULL; - mmc_free_host(slot->mmc); } static int atmci_configure_dma(struct atmel_mci *host) @@ -2570,7 +2567,6 @@ static int atmci_probe(struct platform_device *pdev) dev_info(dev, "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", host->mapbase, irq, nr_slots); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index 057d42307832..85470773650d 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -937,11 +937,10 @@ static int au1xmmc_probe(struct platform_device *pdev) struct resource *r; int ret, iflag; - mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { dev_err(&pdev->dev, "no memory for mmc_host\n"); - ret = -ENOMEM; - goto out0; + return -ENOMEM; } host = mmc_priv(mmc); @@ -953,14 +952,14 @@ static int au1xmmc_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { dev_err(&pdev->dev, "no mmio defined\n"); - goto out1; + return ret; } host->ioarea = request_mem_region(r->start, resource_size(r), pdev->name); if (!host->ioarea) { dev_err(&pdev->dev, "mmio already in use\n"); - goto out1; + return ret; } host->iobase = ioremap(r->start, 0x3c); @@ -1109,9 +1108,6 @@ out3: out2: release_resource(host->ioarea); kfree(host->ioarea); -out1: - mmc_free_host(mmc); -out0: return ret; } @@ -1151,8 +1147,6 @@ static void au1xmmc_remove(struct platform_device *pdev) iounmap((void *)host->iobase); release_resource(host->ioarea); kfree(host->ioarea); - - mmc_free_host(host->mmc); } } diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 4fced9b36c80..ee63835b3ca0 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1372,7 +1372,7 @@ static int bcm2835_probe(struct platform_device *pdev) int ret; dev_dbg(dev, "%s\n", __func__); - mmc = mmc_alloc_host(sizeof(*host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1451,7 +1451,6 @@ err: dev_dbg(dev, "%s -> err %d\n", __func__, ret); if (host->dma_chan_rxtx) dma_release_channel(host->dma_chan_rxtx); - mmc_free_host(mmc); return ret; } @@ -1474,8 +1473,6 @@ static void bcm2835_remove(struct platform_device *pdev) if (host->dma_chan_rxtx) dma_release_channel(host->dma_chan_rxtx); - - mmc_free_host(mmc); } static const struct of_device_id bcm2835_match[] = { diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index 95a41983c6c0..9a55db0e657c 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -1012,7 +1012,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) struct mmc_host *mmc; int ret, id; - mmc = mmc_alloc_host(sizeof(struct cvm_mmc_slot), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*slot)); if (!mmc) return -ENOMEM; @@ -1022,7 +1022,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) ret = cvm_mmc_of_parse(dev, slot); if (ret < 0) - goto error; + return ret; id = ret; /* Set up host parameters */ @@ -1066,12 +1066,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) if (ret) { dev_err(dev, "mmc_add_host() returned %d\n", ret); slot->host->slot[id] = NULL; - goto error; } - return 0; - -error: - mmc_free_host(slot->mmc); return ret; } @@ -1079,6 +1074,5 @@ int cvm_mmc_of_slot_remove(struct cvm_mmc_slot *slot) { mmc_remove_host(slot->mmc); slot->host->slot[slot->bus_id] = NULL; - mmc_free_host(slot->mmc); return 0; } diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index d741c1f9cf87..448d2f9159ea 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "cb710-mmc.h" #define CB710_MMC_REQ_TIMEOUT_MS 2000 @@ -215,7 +216,7 @@ static void cb710_mmc_set_transfer_size(struct cb710_slot *slot, ((count - 1) << 16)|(blocksize - 1)); dev_vdbg(cb710_slot_dev(slot), "set up for %zu block%s of %zu bytes\n", - count, count == 1 ? "" : "s", blocksize); + count, str_plural(count), blocksize); } static void cb710_mmc_fifo_hack(struct cb710_slot *slot) @@ -692,7 +693,7 @@ static int cb710_mmc_init(struct platform_device *pdev) int err; u32 val; - mmc = mmc_alloc_host(sizeof(*reader), cb710_slot_dev(slot)); + mmc = devm_mmc_alloc_host(cb710_slot_dev(slot), sizeof(*reader)); if (!mmc) return -ENOMEM; @@ -741,7 +742,6 @@ err_free_mmc: dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err); cb710_set_irq_handler(slot, NULL); - mmc_free_host(mmc); return err; } @@ -764,8 +764,6 @@ static void cb710_mmc_exit(struct platform_device *pdev) cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0); cancel_work_sync(&reader->finish_req_bh_work); - - mmc_free_host(mmc); } static struct platform_driver cb710_mmc_driver = { diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index cde4c4339ab7..c691f1b60395 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1203,7 +1203,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) if (!mem) return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1212,19 +1212,16 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) host->mem_res = mem; host->base = devm_ioremap(&pdev->dev, mem->start, mem_size); - if (!host->base) { - ret = -ENOMEM; - goto ioremap_fail; - } + if (!host->base) + return -ENOMEM; host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto clk_get_fail; - } + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) - goto clk_prepare_enable_fail; + return ret; host->mmc_input_clk = clk_get_rate(host->clk); @@ -1336,10 +1333,6 @@ cpu_freq_fail: parse_fail: dma_probe_defer: clk_disable_unprepare(host->clk); -clk_prepare_enable_fail: -clk_get_fail: -ioremap_fail: - mmc_free_host(mmc); return ret; } @@ -1352,7 +1345,6 @@ static void davinci_mmcsd_remove(struct platform_device *pdev) mmc_davinci_cpufreq_deregister(host); davinci_release_dma_channels(host); clk_disable_unprepare(host->clk); - mmc_free_host(host->mmc); } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 988492237707..c5db92bbb094 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3008,7 +3008,7 @@ static int dw_mci_init_slot(struct dw_mci *host) struct dw_mci_slot *slot; int ret; - mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); + mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot)); if (!mmc) return -ENOMEM; @@ -3024,18 +3024,18 @@ static int dw_mci_init_slot(struct dw_mci *host) /*if there are external regulators, get them*/ ret = mmc_regulator_get_supply(mmc); if (ret) - goto err_host_allocated; + return ret; if (!mmc->ocr_avail) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ret = mmc_of_parse(mmc); if (ret) - goto err_host_allocated; + return ret; ret = dw_mci_init_slot_caps(slot); if (ret) - goto err_host_allocated; + return ret; /* Useful defaults if platform data is unset. */ if (host->use_dma == TRANS_MODE_IDMAC) { @@ -3065,17 +3065,13 @@ static int dw_mci_init_slot(struct dw_mci *host) ret = mmc_add_host(mmc); if (ret) - goto err_host_allocated; + return ret; #if defined(CONFIG_DEBUG_FS) dw_mci_init_debugfs(slot); #endif return 0; - -err_host_allocated: - mmc_free_host(mmc); - return ret; } static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) @@ -3083,7 +3079,6 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) /* Debugfs stuff is cleaned up by mmc core */ mmc_remove_host(slot->mmc); slot->host->slot = NULL; - mmc_free_host(slot->mmc); } static void dw_mci_init_dma(struct dw_mci *host) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 0fbbf57db52e..6a0d0250d47b 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1043,7 +1043,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) struct mmc_host *mmc; struct jz4740_mmc_host *host; - mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); return -ENOMEM; @@ -1055,31 +1055,24 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev); ret = mmc_of_parse(mmc); - if (ret) { - dev_err_probe(&pdev->dev, ret, "could not parse device properties\n"); - goto err_free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "could not parse device properties\n"); mmc_regulator_get_supply(mmc); host->irq = platform_get_irq(pdev, 0); - if (host->irq < 0) { - ret = host->irq; - goto err_free_host; - } + if (host->irq < 0) + return host->irq; host->clk = devm_clk_get(&pdev->dev, "mmc"); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - dev_err(&pdev->dev, "Failed to get mmc clock\n"); - goto err_free_host; - } + if (IS_ERR(host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), + "Failed to get mmc clock\n"); host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->mem_res); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto err_free_host; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); mmc->ops = &jz4740_mmc_ops; if (!mmc->f_max) @@ -1119,10 +1112,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev) ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, dev_name(&pdev->dev), host); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); - goto err_free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to request irq\n"); jz4740_mmc_clock_disable(host); timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); @@ -1153,9 +1144,6 @@ err_release_dma: jz4740_mmc_release_dma_channels(host); err_free_irq: free_irq(host->irq, host); -err_free_host: - mmc_free_host(mmc); - return ret; } @@ -1173,8 +1161,6 @@ static void jz4740_mmc_remove(struct platform_device *pdev) if (host->use_dma) jz4740_mmc_release_dma_channels(host); - - mmc_free_host(host->mmc); } static int jz4740_mmc_suspend(struct device *dev) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index b338ccfa8f33..d2f19c2dc673 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -506,11 +506,6 @@ use_polling: return 0; } -static void litex_mmc_free_host_wrapper(void *mmc) -{ - mmc_free_host(mmc); -} - static int litex_mmc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -525,15 +520,10 @@ static int litex_mmc_probe(struct platform_device *pdev) * If for some reason we need to modify max_blk_count, we must also * re-calculate `max_[req,seg]_size = max_blk_size * max_blk_count;` */ - mmc = mmc_alloc_host(sizeof(struct litex_mmc_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; - ret = devm_add_action_or_reset(dev, litex_mmc_free_host_wrapper, mmc); - if (ret) - return dev_err_probe(dev, ret, - "Can't register mmc_free_host action\n"); - host = mmc_priv(mmc); host->mmc = mmc; diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c new file mode 100644 index 000000000000..da3daab5f3d6 --- /dev/null +++ b/drivers/mmc/host/loongson2-mmc.c @@ -0,0 +1,1030 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Loongson-2K MMC/SDIO controller driver + * + * Copyright (C) 2018-2025 Loongson Technology Corporation Limited. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOONGSON2_MMC_REG_CTL 0x00 /* Control Register */ +#define LOONGSON2_MMC_REG_PRE 0x04 /* Prescaler Register */ +#define LOONGSON2_MMC_REG_CARG 0x08 /* Command Register */ +#define LOONGSON2_MMC_REG_CCTL 0x0c /* Command Control Register */ +#define LOONGSON2_MMC_REG_CSTS 0x10 /* Command Status Register */ +#define LOONGSON2_MMC_REG_RSP0 0x14 /* Command Response Register 0 */ +#define LOONGSON2_MMC_REG_RSP1 0x18 /* Command Response Register 1 */ +#define LOONGSON2_MMC_REG_RSP2 0x1c /* Command Response Register 2 */ +#define LOONGSON2_MMC_REG_RSP3 0x20 /* Command Response Register 3 */ +#define LOONGSON2_MMC_REG_TIMER 0x24 /* Data Timeout Register */ +#define LOONGSON2_MMC_REG_BSIZE 0x28 /* Block Size Register */ +#define LOONGSON2_MMC_REG_DCTL 0x2c /* Data Control Register */ +#define LOONGSON2_MMC_REG_DCNT 0x30 /* Data Counter Register */ +#define LOONGSON2_MMC_REG_DSTS 0x34 /* Data Status Register */ +#define LOONGSON2_MMC_REG_FSTS 0x38 /* FIFO Status Register */ +#define LOONGSON2_MMC_REG_INT 0x3c /* Interrupt Register */ +#define LOONGSON2_MMC_REG_DATA 0x40 /* Data Register */ +#define LOONGSON2_MMC_REG_IEN 0x64 /* Interrupt Enable Register */ + +/* EMMC DLL Mode Registers */ +#define LOONGSON2_MMC_REG_DLLVAL 0xf0 /* DLL Master Lock-value Register */ +#define LOONGSON2_MMC_REG_DLLCTL 0xf4 /* DLL Control Register */ +#define LOONGSON2_MMC_REG_DELAY 0xf8 /* DLL Delayed Parameter Register */ +#define LOONGSON2_MMC_REG_SEL 0xfc /* Bus Mode Selection Register */ + +/* Exclusive DMA R/W Registers */ +#define LOONGSON2_MMC_REG_WDMA_LO 0x400 +#define LOONGSON2_MMC_REG_WDMA_HI 0x404 +#define LOONGSON2_MMC_REG_RDMA_LO 0x800 +#define LOONGSON2_MMC_REG_RDMA_HI 0x804 + +/* Bitfields of control register */ +#define LOONGSON2_MMC_CTL_ENCLK BIT(0) +#define LOONGSON2_MMC_CTL_EXTCLK BIT(1) +#define LOONGSON2_MMC_CTL_RESET BIT(8) + +/* Bitfields of prescaler register */ +#define LOONGSON2_MMC_PRE GENMASK(9, 0) +#define LOONGSON2_MMC_PRE_EN BIT(31) + +/* Bitfields of command control register */ +#define LOONGSON2_MMC_CCTL_INDEX GENMASK(5, 0) +#define LOONGSON2_MMC_CCTL_HOST BIT(6) +#define LOONGSON2_MMC_CCTL_START BIT(8) +#define LOONGSON2_MMC_CCTL_WAIT_RSP BIT(9) +#define LOONGSON2_MMC_CCTL_LONG_RSP BIT(10) +#define LOONGSON2_MMC_CCTL_ABORT BIT(12) +#define LOONGSON2_MMC_CCTL_CHECK BIT(13) +#define LOONGSON2_MMC_CCTL_SDIO BIT(14) +#define LOONGSON2_MMC_CCTL_CMD6 BIT(18) + +/* Bitfields of command status register */ +#define LOONGSON2_MMC_CSTS_INDEX GENMASK(7, 0) +#define LOONGSON2_MMC_CSTS_ON BIT(8) +#define LOONGSON2_MMC_CSTS_RSP BIT(9) +#define LOONGSON2_MMC_CSTS_TIMEOUT BIT(10) +#define LOONGSON2_MMC_CSTS_END BIT(11) +#define LOONGSON2_MMC_CSTS_CRC_ERR BIT(12) +#define LOONGSON2_MMC_CSTS_AUTO_STOP BIT(13) +#define LOONGSON2_MMC_CSTS_FIN BIT(14) + +/* Bitfields of data timeout register */ +#define LOONGSON2_MMC_DTIMR GENMASK(23, 0) + +/* Bitfields of block size register */ +#define LOONGSON2_MMC_BSIZE GENMASK(11, 0) + +/* Bitfields of data control register */ +#define LOONGSON2_MMC_DCTL_BNUM GENMASK(11, 0) +#define LOONGSON2_MMC_DCTL_START BIT(14) +#define LOONGSON2_MMC_DCTL_ENDMA BIT(15) +#define LOONGSON2_MMC_DCTL_WIDE BIT(16) +#define LOONGSON2_MMC_DCTL_RWAIT BIT(17) +#define LOONGSON2_MMC_DCTL_IO_SUSPEND BIT(18) +#define LOONGSON2_MMC_DCTL_IO_RESUME BIT(19) +#define LOONGSON2_MMC_DCTL_RW_RESUME BIT(20) +#define LOONGSON2_MMC_DCTL_8BIT_BUS BIT(26) + +/* Bitfields of sata counter register */ +#define LOONGSON2_MMC_DCNT_BNUM GENMASK(11, 0) +#define LOONGSON2_MMC_DCNT_BYTE GENMASK(23, 12) + +/* Bitfields of command status register */ +#define LOONGSON2_MMC_DSTS_RXON BIT(0) +#define LOONGSON2_MMC_DSTS_TXON BIT(1) +#define LOONGSON2_MMC_DSTS_SBITERR BIT(2) +#define LOONGSON2_MMC_DSTS_BUSYFIN BIT(3) +#define LOONGSON2_MMC_DSTS_XFERFIN BIT(4) +#define LOONGSON2_MMC_DSTS_DTIMEOUT BIT(5) +#define LOONGSON2_MMC_DSTS_RXCRC BIT(6) +#define LOONGSON2_MMC_DSTS_TXCRC BIT(7) +#define LOONGSON2_MMC_DSTS_IRQ BIT(8) +#define LOONGSON2_MMC_DSTS_START BIT(13) +#define LOONGSON2_MMC_DSTS_RESUME BIT(15) +#define LOONGSON2_MMC_DSTS_SUSPEND BIT(16) + +/* Bitfields of FIFO Status Register */ +#define LOONGSON2_MMC_FSTS_TXFULL BIT(11) + +/* Bitfields of interrupt register */ +#define LOONGSON2_MMC_INT_DFIN BIT(0) +#define LOONGSON2_MMC_INT_DTIMEOUT BIT(1) +#define LOONGSON2_MMC_INT_RXCRC BIT(2) +#define LOONGSON2_MMC_INT_TXCRC BIT(3) +#define LOONGSON2_MMC_INT_PROGERR BIT(4) +#define LOONGSON2_MMC_INT_SDIOIRQ BIT(5) +#define LOONGSON2_MMC_INT_CSENT BIT(6) +#define LOONGSON2_MMC_INT_CTIMEOUT BIT(7) +#define LOONGSON2_MMC_INT_RESPCRC BIT(8) +#define LOONGSON2_MMC_INT_BUSYEND BIT(9) + +/* Bitfields of interrupt enable register */ +#define LOONGSON2_MMC_IEN_DFIN BIT(0) +#define LOONGSON2_MMC_IEN_DTIMEOUT BIT(1) +#define LOONGSON2_MMC_IEN_RXCRC BIT(2) +#define LOONGSON2_MMC_IEN_TXCRC BIT(3) +#define LOONGSON2_MMC_IEN_PROGERR BIT(4) +#define LOONGSON2_MMC_IEN_SDIOIRQ BIT(5) +#define LOONGSON2_MMC_IEN_CSENT BIT(6) +#define LOONGSON2_MMC_IEN_CTIMEOUT BIT(7) +#define LOONGSON2_MMC_IEN_RESPCRC BIT(8) +#define LOONGSON2_MMC_IEN_BUSYEND BIT(9) + +#define LOONGSON2_MMC_IEN_ALL GENMASK(9, 0) +#define LOONGSON2_MMC_INT_CLEAR GENMASK(9, 0) + +/* Bitfields of DLL master lock-value register */ +#define LOONGSON2_MMC_DLLVAL_DONE BIT(8) + +/* Bitfields of DLL control register */ +#define LOONGSON2_MMC_DLLCTL_TIME GENMASK(7, 0) +#define LOONGSON2_MMC_DLLCTL_INCRE GENMASK(15, 8) +#define LOONGSON2_MMC_DLLCTL_START GENMASK(23, 16) +#define LOONGSON2_MMC_DLLCTL_CLK_MODE BIT(24) +#define LOONGSON2_MMC_DLLCTL_START_BIT BIT(25) +#define LOONGSON2_MMC_DLLCTL_TIME_BPASS GENMASK(29, 26) + +#define LOONGSON2_MMC_DELAY_PAD GENMASK(7, 0) +#define LOONGSON2_MMC_DELAY_RD GENMASK(15, 8) + +#define LOONGSON2_MMC_SEL_DATA BIT(0) /* 0: SDR, 1: DDR */ +#define LOONGSON2_MMC_SEL_BUS BIT(0) /* 0: EMMC, 1: SDIO */ + +/* Internal dma controller registers */ + +/* Bitfields of Global Configuration Register */ +#define LOONGSON2_MMC_DMA_64BIT_EN BIT(0) /* 1: 64 bit support */ +#define LOONGSON2_MMC_DMA_UNCOHERENT_EN BIT(1) /* 0: cache, 1: uncache */ +#define LOONGSON2_MMC_DMA_ASK_VALID BIT(2) +#define LOONGSON2_MMC_DMA_START BIT(3) /* DMA start operation */ +#define LOONGSON2_MMC_DMA_STOP BIT(4) /* DMA stop operation */ +#define LOONGSON2_MMC_DMA_CONFIG_MASK GENMASK_ULL(4, 0) /* DMA controller config bits mask */ + +/* Bitfields of ndesc_addr field of HW descriptor */ +#define LOONGSON2_MMC_DMA_DESC_EN BIT(0) /*1: The next descriptor is valid */ +#define LOONGSON2_MMC_DMA_DESC_ADDR_LOW GENMASK(31, 1) + +/* Bitfields of cmd field of HW descriptor */ +#define LOONGSON2_MMC_DMA_INT BIT(1) /* Enable DMA interrupts */ +#define LOONGSON2_MMC_DMA_DATA_DIR BIT(12) /* 1: write to device, 0: read from device */ + +#define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000 +#define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500 + +/* Loongson-2K1000 SDIO2 DMA routing register */ +#define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15) +#define LS2K1000_DMA0_CONF 0x0 +#define LS2K1000_DMA1_CONF 0x1 +#define LS2K1000_DMA2_CONF 0x2 +#define LS2K1000_DMA3_CONF 0x3 +#define LS2K1000_DMA4_CONF 0x4 + +/* Loongson-2K0500 SDIO2 DMA routing register */ +#define LS2K0500_SDIO_DMA_MASK GENMASK(15, 14) +#define LS2K0500_DMA0_CONF 0x1 +#define LS2K0500_DMA1_CONF 0x2 +#define LS2K0500_DMA2_CONF 0x3 + +enum loongson2_mmc_state { + STATE_NONE, + STATE_FINALIZE, + STATE_CMDSENT, + STATE_RSPFIN, + STATE_XFERFINISH, + STATE_XFERFINISH_RSPFIN, +}; + +struct loongson2_dma_desc { + u32 ndesc_addr; + u32 mem_addr; + u32 apb_addr; + u32 len; + u32 step_len; + u32 step_times; + u32 cmd; + u32 stats; + u32 high_ndesc_addr; + u32 high_mem_addr; + u32 reserved[2]; +} __packed; + +struct loongson2_mmc_host { + struct device *dev; + struct mmc_request *mrq; + struct regmap *regmap; + struct resource *res; + struct clk *clk; + u32 current_clk; + void *sg_cpu; + dma_addr_t sg_dma; + int dma_complete; + struct dma_chan *chan; + int cmd_is_stop; + int bus_width; + spinlock_t lock; /* Prevent races with irq handler */ + enum loongson2_mmc_state state; + const struct loongson2_mmc_pdata *pdata; +}; + +struct loongson2_mmc_pdata { + const struct regmap_config *regmap_config; + void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd); + void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd); + int (*setting_dma)(struct loongson2_mmc_host *host, struct platform_device *pdev); + int (*prepare_dma)(struct loongson2_mmc_host *host, struct mmc_data *data); + void (*release_dma)(struct loongson2_mmc_host *host, struct device *dev); +}; + +static void loongson2_mmc_send_command(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + u32 cctrl; + + if (cmd->data) + host->state = STATE_XFERFINISH_RSPFIN; + else if (cmd->flags & MMC_RSP_PRESENT) + host->state = STATE_RSPFIN; + else + host->state = STATE_CMDSENT; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, cmd->arg); + + cctrl = FIELD_PREP(LOONGSON2_MMC_CCTL_INDEX, cmd->opcode); + cctrl |= LOONGSON2_MMC_CCTL_HOST | LOONGSON2_MMC_CCTL_START; + + if (cmd->opcode == SD_SWITCH && cmd->data) + cctrl |= LOONGSON2_MMC_CCTL_CMD6; + + if (cmd->flags & MMC_RSP_PRESENT) + cctrl |= LOONGSON2_MMC_CCTL_WAIT_RSP; + + if (cmd->flags & MMC_RSP_136) + cctrl |= LOONGSON2_MMC_CCTL_LONG_RSP; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, cctrl); +} + +static int loongson2_mmc_setup_data(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + u32 dctrl; + + if ((data->blksz & 3) != 0) + return -EINVAL; + + dctrl = FIELD_PREP(LOONGSON2_MMC_DCTL_BNUM, data->blocks); + dctrl |= LOONGSON2_MMC_DCTL_START | LOONGSON2_MMC_DCTL_ENDMA; + + if (host->bus_width == MMC_BUS_WIDTH_4) + dctrl |= LOONGSON2_MMC_DCTL_WIDE; + else if (host->bus_width == MMC_BUS_WIDTH_8) + dctrl |= LOONGSON2_MMC_DCTL_8BIT_BUS; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_DCTL, dctrl); + regmap_write(host->regmap, LOONGSON2_MMC_REG_BSIZE, data->blksz); + regmap_write(host->regmap, LOONGSON2_MMC_REG_TIMER, U32_MAX); + + return 0; +} + +static int loongson2_mmc_prepare_dma(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + int ret; + + if (!data) + return 0; + + ret = loongson2_mmc_setup_data(host, data); + if (ret) + return ret; + + host->dma_complete = 0; + + return host->pdata->prepare_dma(host, data); +} + +static void loongson2_mmc_send_request(struct mmc_host *mmc) +{ + int ret; + struct loongson2_mmc_host *host = mmc_priv(mmc); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + ret = loongson2_mmc_prepare_dma(host, cmd->data); + if (ret) { + dev_err(host->dev, "DMA data prepared failed with %d\n", ret); + cmd->error = ret; + cmd->data->error = ret; + mmc_request_done(mmc, mrq); + return; + } + + if (host->pdata->fix_data_timeout) + host->pdata->fix_data_timeout(host, cmd); + + loongson2_mmc_send_command(host, cmd); + + /* Fix deselect card */ + if (cmd->opcode == MMC_SELECT_CARD && cmd->arg == 0) { + cmd->error = 0; + mmc_request_done(mmc, mrq); + } +} + +static irqreturn_t loongson2_mmc_irq_worker(int irq, void *devid) +{ + struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid; + struct mmc_host *mmc = mmc_from_priv(host); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + if (cmd->data) + dma_unmap_sg(mmc_dev(mmc), cmd->data->sg, cmd->data->sg_len, + mmc_get_dma_dir(cmd->data)); + + if (cmd->data && !cmd->error && + !cmd->data->error && !host->dma_complete) + return IRQ_HANDLED; + + /* Read response from controller. */ + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP0, &cmd->resp[0]); + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP1, &cmd->resp[1]); + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP2, &cmd->resp[2]); + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP3, &cmd->resp[3]); + + /* Cleanup controller */ + regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, 0); + regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, 0); + + if (cmd->data && cmd->error) + cmd->data->error = cmd->error; + + if (cmd->data && cmd->data->stop && !host->cmd_is_stop) { + host->cmd_is_stop = 1; + loongson2_mmc_send_request(mmc); + return IRQ_HANDLED; + } + + /* If we have no data transfer we are finished here */ + if (!mrq->data) + goto request_done; + + /* Calculate the amount of bytes transfer if there was no error */ + if (mrq->data->error == 0) { + mrq->data->bytes_xfered = + (mrq->data->blocks * mrq->data->blksz); + } else { + mrq->data->bytes_xfered = 0; + } + +request_done: + host->state = STATE_NONE; + host->mrq = NULL; + mmc_request_done(mmc, mrq); + return IRQ_HANDLED; +} + +static irqreturn_t loongson2_mmc_irq(int irq, void *devid) +{ + struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid; + struct mmc_host *mmc = mmc_from_priv(host); + struct mmc_command *cmd; + unsigned long iflags; + u32 dsts, imsk; + + regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk); + regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts); + + if ((dsts & LOONGSON2_MMC_DSTS_IRQ) && + (imsk & LOONGSON2_MMC_INT_SDIOIRQ)) { + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT, + LOONGSON2_MMC_INT_SDIOIRQ, LOONGSON2_MMC_INT_SDIOIRQ); + + sdio_signal_irq(mmc); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&host->lock, iflags); + + if (host->state == STATE_NONE || host->state == STATE_FINALIZE || !host->mrq) + goto irq_out; + + cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd; + if (!cmd) + goto irq_out; + + cmd->error = 0; + + if (imsk & LOONGSON2_MMC_INT_CTIMEOUT) { + cmd->error = -ETIMEDOUT; + goto close_transfer; + } + + if (imsk & LOONGSON2_MMC_INT_CSENT) { + if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT) + goto close_transfer; + + if (host->state == STATE_XFERFINISH_RSPFIN) + host->state = STATE_XFERFINISH; + } + + if (!cmd->data) + goto irq_out; + + if (imsk & (LOONGSON2_MMC_INT_RXCRC | LOONGSON2_MMC_INT_TXCRC)) { + cmd->data->error = -EILSEQ; + goto close_transfer; + } + + if (imsk & LOONGSON2_MMC_INT_DTIMEOUT) { + cmd->data->error = -ETIMEDOUT; + goto close_transfer; + } + + if (imsk & LOONGSON2_MMC_INT_DFIN) { + if (host->state == STATE_XFERFINISH) { + host->dma_complete = 1; + goto close_transfer; + } + + if (host->state == STATE_XFERFINISH_RSPFIN) + host->state = STATE_RSPFIN; + } + +irq_out: + regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk); + spin_unlock_irqrestore(&host->lock, iflags); + return IRQ_HANDLED; + +close_transfer: + host->state = STATE_FINALIZE; + host->pdata->reorder_cmd_data(host, cmd); + regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk); + spin_unlock_irqrestore(&host->lock, iflags); + return IRQ_WAKE_THREAD; +} + +static void loongson2_mmc_dll_mode_init(struct loongson2_mmc_host *host) +{ + u32 val, pad_delay, delay; + int ret; + + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_SEL, + LOONGSON2_MMC_SEL_DATA, LOONGSON2_MMC_SEL_DATA); + + val = FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME, 0xc8) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_INCRE, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_CLK_MODE, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START_BIT, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME_BPASS, 0xf); + + regmap_write(host->regmap, LOONGSON2_MMC_REG_DLLCTL, val); + + ret = regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_DLLVAL, val, + (val & LOONGSON2_MMC_DLLVAL_DONE), 0, + LOONGSON2_MMC_DLLVAL_TIMEOUT_US); + if (ret < 0) + return; + + regmap_read(host->regmap, LOONGSON2_MMC_REG_DLLVAL, &val); + pad_delay = FIELD_GET(GENMASK(7, 1), val); + + delay = FIELD_PREP(LOONGSON2_MMC_DELAY_PAD, pad_delay) + | FIELD_PREP(LOONGSON2_MMC_DELAY_RD, pad_delay + 1); + + regmap_write(host->regmap, LOONGSON2_MMC_REG_DELAY, delay); +} + +static void loongson2_mmc_set_clk(struct loongson2_mmc_host *host, struct mmc_ios *ios) +{ + u32 pre; + + pre = DIV_ROUND_UP(host->current_clk, ios->clock); + if (pre > 255) + pre = 255; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_PRE, pre | LOONGSON2_MMC_PRE_EN); + + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL, + LOONGSON2_MMC_CTL_ENCLK, LOONGSON2_MMC_CTL_ENCLK); + + /* EMMC DLL mode setting */ + if (ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_DDR52) + loongson2_mmc_dll_mode_init(host); +} + +static void loongson2_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct loongson2_mmc_host *host = mmc_priv(mmc); + int ret; + + if (ios->power_mode == MMC_POWER_UP) { + if (!IS_ERR(mmc->supply.vmmc)) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + if (ret) { + dev_err(host->dev, "failed to enable vmmc regulator\n"); + return; /* return, if failed turn on vmmc */ + } + } + regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_RESET); + mdelay(10); + regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_EXTCLK); + regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, LOONGSON2_MMC_IEN_ALL); + regmap_write(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_CLEAR); + } else if (ios->power_mode == MMC_POWER_OFF) { + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL, + LOONGSON2_MMC_CTL_RESET, LOONGSON2_MMC_CTL_RESET); + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + return; + } + + loongson2_mmc_set_clk(host, ios); + + host->bus_width = ios->bus_width; +} + +static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct loongson2_mmc_host *host = mmc_priv(mmc); + + host->cmd_is_stop = 0; + host->mrq = mrq; + loongson2_mmc_send_request(mmc); +} + +static void loongson2_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct loongson2_mmc_host *host = mmc_priv(mmc); + + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_SDIOIRQ, enable); +} + +static void loongson2_mmc_ack_sdio_irq(struct mmc_host *mmc) +{ + loongson2_mmc_enable_sdio_irq(mmc, 1); +} + +static struct mmc_host_ops loongson2_mmc_ops = { + .request = loongson2_mmc_request, + .set_ios = loongson2_mmc_set_ios, + .get_ro = mmc_gpio_get_ro, + .get_cd = mmc_gpio_get_cd, + .enable_sdio_irq = loongson2_mmc_enable_sdio_irq, + .ack_sdio_irq = loongson2_mmc_ack_sdio_irq, +}; + +static const struct regmap_config ls2k0500_mmc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_MMC_REG_IEN, +}; + +static int loongson2_reorder_cmd_list[] = { SD_APP_SEND_SCR, SD_APP_SEND_NUM_WR_BLKS, + SD_APP_SD_STATUS, MMC_SEND_WRITE_PROT, SD_SWITCH }; + +/* + * According to SD spec, ACMD13, ACMD22, ACMD51 and CMD30 + * response datas has different byte order with usual data packets. + * However sdio controller will send these datas in usual data format, + * so we need to adjust these datas to a protocol consistent byte order. + */ +static void ls2k0500_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + struct scatterlist *sg; + u32 *data; + int i, j; + + if (mmc_cmd_type(cmd) != MMC_CMD_ADTC) + return; + + for (i = 0; i < ARRAY_SIZE(loongson2_reorder_cmd_list); i++) + if (cmd->opcode == loongson2_reorder_cmd_list[i]) + break; + + if (i == ARRAY_SIZE(loongson2_reorder_cmd_list)) + return; + + for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { + data = sg_virt(&sg[i]); + for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + if (cmd->opcode == SD_SWITCH) + data[j] = bitrev8x4(data[j]); + else + data[j] = (__force u32)cpu_to_be32(data[j]); + } +} + +static int loongson2_mmc_prepare_external_dma(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + struct mmc_host *mmc = mmc_from_priv(host); + struct dma_slave_config dma_conf = { }; + struct dma_async_tx_descriptor *desc; + int ret; + + ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (!ret) + return -ENOMEM; + + dma_conf.src_addr = host->res->start + LOONGSON2_MMC_REG_DATA, + dma_conf.dst_addr = host->res->start + LOONGSON2_MMC_REG_DATA, + dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + dma_conf.direction = !(data->flags & MMC_DATA_WRITE) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + + dmaengine_slave_config(host->chan, &dma_conf); + desc = dmaengine_prep_slave_sg(host->chan, data->sg, data->sg_len, + dma_conf.direction, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) + goto unmap_exit; + + dmaengine_submit(desc); + dma_async_issue_pending(host->chan); + + return 0; + +unmap_exit: + dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, mmc_get_dma_dir(data)); + return -ENOMEM; +} + +static void loongson2_mmc_release_external_dma(struct loongson2_mmc_host *host, + struct device *dev) +{ + dma_release_channel(host->chan); +} + +static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host, + struct platform_device *pdev) +{ + int ret, val; + void __iomem *regs; + + regs = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + val = readl(regs); + val |= FIELD_PREP(LS2K0500_SDIO_DMA_MASK, LS2K0500_DMA2_CONF); + writel(val, regs); + + host->chan = dma_request_chan(&pdev->dev, "rx-tx"); + ret = PTR_ERR_OR_ZERO(host->chan); + if (ret) { + dev_err(&pdev->dev, "Cannot get DMA channel.\n"); + return ret; + } + + return 0; +} + +static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k0500_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + +static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host, + struct platform_device *pdev) +{ + int ret, val; + void __iomem *regs; + + regs = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + val = readl(regs); + val |= FIELD_PREP(LS2K1000_SDIO_DMA_MASK, LS2K1000_DMA1_CONF); + writel(val, regs); + + host->chan = dma_request_chan(&pdev->dev, "rx-tx"); + ret = PTR_ERR_OR_ZERO(host->chan); + if (ret) { + dev_err(&pdev->dev, "Cannot get DMA channel.\n"); + return ret; + } + + return 0; +} + +static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k1000_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + +static const struct regmap_config ls2k2000_mmc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_MMC_REG_RDMA_HI, +}; + +static void ls2k2000_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + struct scatterlist *sg; + u32 *data; + int i, j; + + if (cmd->opcode != SD_SWITCH || mmc_cmd_type(cmd) != MMC_CMD_ADTC) + return; + + for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { + data = sg_virt(&sg[i]); + for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + data[j] = bitrev8x4(data[j]); + } +} + +/* + * This is a controller hardware defect. Single/multiple block write commands + * must be sent after the TX FULL flag is set, otherwise a data timeout interrupt + * will occur. + */ +static void ls2k2000_mmc_fix_data_timeout(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + int val; + + if (cmd->opcode != MMC_WRITE_BLOCK && cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) + return; + + regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_FSTS, val, + (val & LOONGSON2_MMC_FSTS_TXFULL), 0, + LOONGSON2_MMC_TXFULL_TIMEOUT_US); +} + +static int loongson2_mmc_prepare_internal_dma(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + struct loongson2_dma_desc *pdes = (struct loongson2_dma_desc *)host->sg_cpu; + struct mmc_host *mmc = mmc_from_priv(host); + dma_addr_t next_desc = host->sg_dma; + struct scatterlist *sg; + int reg_lo, reg_hi; + u64 dma_order; + int i, ret; + + ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (!ret) + return -ENOMEM; + + for_each_sg(data->sg, sg, data->sg_len, i) { + pdes[i].len = sg_dma_len(&sg[i]) / 4; + pdes[i].step_len = 0; + pdes[i].step_times = 1; + pdes[i].mem_addr = lower_32_bits(sg_dma_address(&sg[i])); + pdes[i].high_mem_addr = upper_32_bits(sg_dma_address(&sg[i])); + pdes[i].apb_addr = host->res->start + LOONGSON2_MMC_REG_DATA; + pdes[i].cmd = LOONGSON2_MMC_DMA_INT; + + if (data->flags & MMC_DATA_READ) { + reg_lo = LOONGSON2_MMC_REG_RDMA_LO; + reg_hi = LOONGSON2_MMC_REG_RDMA_HI; + } else { + pdes[i].cmd |= LOONGSON2_MMC_DMA_DATA_DIR; + reg_lo = LOONGSON2_MMC_REG_WDMA_LO; + reg_hi = LOONGSON2_MMC_REG_WDMA_HI; + } + + next_desc += sizeof(struct loongson2_dma_desc); + pdes[i].ndesc_addr = lower_32_bits(next_desc) | + LOONGSON2_MMC_DMA_DESC_EN; + pdes[i].high_ndesc_addr = upper_32_bits(next_desc); + } + + /* Setting the last descriptor enable bit */ + pdes[i - 1].ndesc_addr &= ~LOONGSON2_MMC_DMA_DESC_EN; + + dma_order = (host->sg_dma & ~LOONGSON2_MMC_DMA_CONFIG_MASK) | + LOONGSON2_MMC_DMA_64BIT_EN | + LOONGSON2_MMC_DMA_START; + + regmap_write(host->regmap, reg_hi, upper_32_bits(dma_order)); + regmap_write(host->regmap, reg_lo, lower_32_bits(dma_order)); + + return 0; +} + +static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host, + struct platform_device *pdev) +{ + host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, + &host->sg_dma, GFP_KERNEL); + if (!host->sg_cpu) + return -ENOMEM; + + memset(host->sg_cpu, 0, PAGE_SIZE); + return 0; +} + +static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host, + struct device *dev) +{ + dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); +} + +static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = { + .regmap_config = &ls2k2000_mmc_regmap_config, + .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, + .fix_data_timeout = ls2k2000_mmc_fix_data_timeout, + .setting_dma = ls2k2000_mmc_set_internal_dma, + .prepare_dma = loongson2_mmc_prepare_internal_dma, + .release_dma = loongson2_mmc_release_internal_dma, +}; + +static int loongson2_mmc_resource_request(struct platform_device *pdev, + struct loongson2_mmc_host *host) +{ + struct device *dev = &pdev->dev; + void __iomem *base; + int ret, irq; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res); + if (IS_ERR(base)) + return PTR_ERR(base); + + host->regmap = devm_regmap_init_mmio(dev, base, host->pdata->regmap_config); + if (IS_ERR(host->regmap)) + return PTR_ERR(host->regmap); + + host->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + if (host->clk) { + ret = devm_clk_rate_exclusive_get(dev, host->clk); + if (ret) + return ret; + + host->current_clk = clk_get_rate(host->clk); + } else { + /* For ACPI, the clock is accessed via the clock-frequency attribute. */ + device_property_read_u32(dev, "clock-frequency", &host->current_clk); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, loongson2_mmc_irq, + loongson2_mmc_irq_worker, + IRQF_ONESHOT, "loongson2-mmc", host); + if (ret) + return ret; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + return host->pdata->setting_dma(host, pdev); +} + +static int loongson2_mmc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson2_mmc_host *host; + struct mmc_host *mmc; + int ret; + + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; + + platform_set_drvdata(pdev, mmc); + + host = mmc_priv(mmc); + host->state = STATE_NONE; + spin_lock_init(&host->lock); + + host->pdata = device_get_match_data(dev); + if (!host->pdata) + return dev_err_probe(dev, -EINVAL, "Failed to get match data\n"); + + ret = loongson2_mmc_resource_request(pdev, host); + if (ret) + return dev_err_probe(dev, ret, "Failed to request resource\n"); + + mmc->ops = &loongson2_mmc_ops; + mmc->f_min = DIV_ROUND_UP(host->current_clk, 256); + mmc->f_max = host->current_clk; + mmc->max_blk_count = 4095; + mmc->max_blk_size = 4095; + mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; + mmc->max_segs = 1; + mmc->max_seg_size = mmc->max_req_size; + + /* Process SDIO IRQs through the sdio_irq_work. */ + if (mmc->caps & MMC_CAP_SDIO_IRQ) + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + + ret = mmc_regulator_get_supply(mmc); + if (ret || mmc->ocr_avail == 0) { + dev_warn(dev, "Can't get voltage, defaulting to 3.3V\n"); + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + } + + ret = mmc_of_parse(mmc); + if (ret) { + dev_err(dev, "Failed to parse device node\n"); + goto free_dma; + } + + ret = mmc_add_host(mmc); + if (ret) { + dev_err(dev, "Failed to add mmc host\n"); + goto free_dma; + } + + return 0; + +free_dma: + host->pdata->release_dma(host, dev); + return ret; +} + +static void loongson2_mmc_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct loongson2_mmc_host *host = mmc_priv(mmc); + + mmc_remove_host(mmc); + host->pdata->release_dma(host, &pdev->dev); +} + +static const struct of_device_id loongson2_mmc_of_ids[] = { + { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata }, + { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata }, + { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata }, + { }, +}; +MODULE_DEVICE_TABLE(of, loongson2_mmc_of_ids); + +static int loongson2_mmc_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct loongson2_mmc_host *host = mmc_priv(mmc); + + clk_disable_unprepare(host->clk); + + return 0; +} + +static int loongson2_mmc_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct loongson2_mmc_host *host = mmc_priv(mmc); + + return clk_prepare_enable(host->clk); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(loongson2_mmc_pm_ops, loongson2_mmc_suspend, loongson2_mmc_resume); + +static struct platform_driver loongson2_mmc_driver = { + .driver = { + .name = "loongson2-mmc", + .of_match_table = loongson2_mmc_of_ids, + .pm = pm_ptr(&loongson2_mmc_pm_ops), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = loongson2_mmc_probe, + .remove = loongson2_mmc_remove, +}; + +module_platform_driver(loongson2_mmc_driver); + +MODULE_DESCRIPTION("Loongson-2K SD/SDIO/eMMC Interface driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c index b4e56ccffca2..fb49ea71289e 100644 --- a/drivers/mmc/host/meson-mx-sdhc-mmc.c +++ b/drivers/mmc/host/meson-mx-sdhc-mmc.c @@ -757,11 +757,6 @@ static void meson_mx_sdhc_init_hw(struct mmc_host *mmc) regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS); } -static void meason_mx_mmc_free_host(void *data) -{ - mmc_free_host(data); -} - static int meson_mx_sdhc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -770,16 +765,10 @@ static int meson_mx_sdhc_probe(struct platform_device *pdev) void __iomem *base; int ret, irq; - mmc = mmc_alloc_host(sizeof(*host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; - ret = devm_add_action_or_reset(dev, meason_mx_mmc_free_host, mmc); - if (ret) { - dev_err(dev, "Failed to register mmc_free_host action\n"); - return ret; - } - host = mmc_priv(mmc); host->mmc = mmc; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 47443fb5eb33..35b0ad273b4f 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1185,7 +1185,7 @@ static int mmc_spi_probe(struct spi_device *spi) goto nomem; memset(ones, 0xff, MMC_SPI_BLOCKSIZE); - mmc = mmc_alloc_host(sizeof(*host), &spi->dev); + mmc = devm_mmc_alloc_host(&spi->dev, sizeof(*host)); if (!mmc) goto nomem; @@ -1305,7 +1305,6 @@ fail_glue_init: kfree(host->data); fail_nobuf1: mmc_spi_put_pdata(spi); - mmc_free_host(mmc); nomem: kfree(ones); return status; @@ -1328,7 +1327,6 @@ static void mmc_spi_remove(struct spi_device *spi) spi->max_speed_hz = mmc->f_max; mmc_spi_put_pdata(spi); - mmc_free_host(mmc); } static const struct spi_device_id mmc_spi_dev_ids[] = { diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b790c3c3c8f9..8367283647a9 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2082,7 +2082,6 @@ static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable) spin_unlock_irqrestore(&host->lock, flags); if (!enable) { - pm_runtime_mark_last_busy(mmc_dev(mmc)); pm_runtime_put_autosuspend(mmc_dev(mmc)); } } @@ -2223,7 +2222,7 @@ static int mmci_probe(struct amba_device *dev, return -ENOMEM; } - mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); + mmc = devm_mmc_alloc_host(&dev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -2234,7 +2233,7 @@ static int mmci_probe(struct amba_device *dev, ret = mmci_of_parse(np, mmc); if (ret) - goto host_free; + return ret; /* * Some variant (STM32) doesn't have opendrain bit, nevertheless @@ -2242,19 +2241,15 @@ static int mmci_probe(struct amba_device *dev, */ if (!variant->opendrain) { host->pinctrl = devm_pinctrl_get(&dev->dev); - if (IS_ERR(host->pinctrl)) { - dev_err(&dev->dev, "failed to get pinctrl"); - ret = PTR_ERR(host->pinctrl); - goto host_free; - } + if (IS_ERR(host->pinctrl)) + return dev_err_probe(&dev->dev, PTR_ERR(host->pinctrl), + "failed to get pinctrl\n"); host->pins_opendrain = pinctrl_lookup_state(host->pinctrl, MMCI_PINCTRL_STATE_OPENDRAIN); - if (IS_ERR(host->pins_opendrain)) { - dev_err(mmc_dev(mmc), "Can't select opendrain pins\n"); - ret = PTR_ERR(host->pins_opendrain); - goto host_free; - } + if (IS_ERR(host->pins_opendrain)) + return dev_err_probe(&dev->dev, PTR_ERR(host->pins_opendrain), + "Can't select opendrain pins\n"); } host->hw_designer = amba_manf(dev); @@ -2263,14 +2258,12 @@ static int mmci_probe(struct amba_device *dev, dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision); host->clk = devm_clk_get(&dev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto host_free; - } + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); ret = clk_prepare_enable(host->clk); if (ret) - goto host_free; + return ret; if (variant->qcom_fifo) host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt; @@ -2491,8 +2484,6 @@ static int mmci_probe(struct amba_device *dev, clk_disable: clk_disable_unprepare(host->clk); - host_free: - mmc_free_host(mmc); return ret; } @@ -2522,7 +2513,6 @@ static void mmci_remove(struct amba_device *dev) mmci_dma_release(host); clk_disable_unprepare(host->clk); - mmc_free_host(mmc); } } diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index a12048e5de63..3dd8f232052f 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -558,41 +558,33 @@ static int moxart_probe(struct platform_device *pdev) int irq, ret; u32 i; - mmc = mmc_alloc_host(sizeof(struct moxart_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) { - dev_err(dev, "mmc_alloc_host failed\n"); - ret = -ENOMEM; - goto out_mmc; + dev_err(dev, "devm_mmc_alloc_host failed\n"); + return -ENOMEM; } ret = of_address_to_resource(node, 0, &res_mmc); - if (ret) { - dev_err(dev, "of_address_to_resource failed\n"); - goto out_mmc; - } + if (ret) + return dev_err_probe(dev, ret, + "of_address_to_resource failed\n"); irq = irq_of_parse_and_map(node, 0); - if (irq <= 0) { - dev_err(dev, "irq_of_parse_and_map failed\n"); - ret = -EINVAL; - goto out_mmc; - } + if (irq <= 0) + return dev_err_probe(dev, -EINVAL, + "irq_of_parse_and_map failed\n"); clk = devm_clk_get(dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto out_mmc; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); reg_mmc = devm_ioremap_resource(dev, &res_mmc); - if (IS_ERR(reg_mmc)) { - ret = PTR_ERR(reg_mmc); - goto out_mmc; - } + if (IS_ERR(reg_mmc)) + return PTR_ERR(reg_mmc); ret = mmc_of_parse(mmc); if (ret) - goto out_mmc; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -686,9 +678,6 @@ out: dma_release_channel(host->dma_chan_tx); if (!IS_ERR_OR_NULL(host->dma_chan_rx)) dma_release_channel(host->dma_chan_rx); -out_mmc: - if (mmc) - mmc_free_host(mmc); return ret; } @@ -707,7 +696,6 @@ static void moxart_remove(struct platform_device *pdev) writel(0, host->base + REG_POWER_CONTROL); writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF, host->base + REG_CLOCK_CONTROL); - mmc_free_host(mmc); } static const struct of_device_id moxart_mmc_match[] = { diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 101f36de7b63..a9e6277789ba 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -706,11 +706,9 @@ static int mvsd_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; host = mmc_priv(mmc); host->mmc = mmc; @@ -724,11 +722,9 @@ static int mvsd_probe(struct platform_device *pdev) * fixed rate clock). */ host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - dev_err(&pdev->dev, "no clock associated\n"); - ret = -EINVAL; - goto out; - } + if (IS_ERR(host->clk)) + return dev_err_probe(&pdev->dev, -EINVAL, "no clock associated\n"); + clk_prepare_enable(host->clk); mmc->ops = &mvsd_ops; @@ -787,12 +783,7 @@ static int mvsd_probe(struct platform_device *pdev) return 0; out: - if (mmc) { - if (!IS_ERR(host->clk)) - clk_disable_unprepare(host->clk); - mmc_free_host(mmc); - } - + clk_disable_unprepare(host->clk); return ret; } @@ -808,7 +799,6 @@ static void mvsd_remove(struct platform_device *pdev) if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); - mmc_free_host(mmc); } static const struct of_device_id mvsdio_dt_ids[] = { diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index e588e24256cc..c405cfb8b269 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1005,23 +1005,21 @@ static int mxcmci_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; host = mmc_priv(mmc); host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto out_free; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->phys_base = res->start; ret = mmc_of_parse(mmc); if (ret) - goto out_free; + return ret; mmc->ops = &mxcmci_ops; /* For devicetree parsing, the bus width is read from devicetree */ @@ -1054,7 +1052,7 @@ static int mxcmci_probe(struct platform_device *pdev) ret = mmc_regulator_get_supply(mmc); if (ret) - goto out_free; + return ret; if (!mmc->ocr_avail) { if (pdata && pdata->ocr_avail) @@ -1070,20 +1068,16 @@ static int mxcmci_probe(struct platform_device *pdev) host->default_irq_mask = 0; host->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(host->clk_ipg)) { - ret = PTR_ERR(host->clk_ipg); - goto out_free; - } + if (IS_ERR(host->clk_ipg)) + return PTR_ERR(host->clk_ipg); host->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(host->clk_per)) { - ret = PTR_ERR(host->clk_per); - goto out_free; - } + if (IS_ERR(host->clk_per)) + return PTR_ERR(host->clk_per); ret = clk_prepare_enable(host->clk_per); if (ret) - goto out_free; + return ret; ret = clk_prepare_enable(host->clk_ipg); if (ret) @@ -1169,9 +1163,6 @@ out_clk_put: out_clk_per_put: clk_disable_unprepare(host->clk_per); -out_free: - mmc_free_host(mmc); - return ret; } @@ -1190,8 +1181,6 @@ static void mxcmci_remove(struct platform_device *pdev) clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); - - mmc_free_host(mmc); } static int mxcmci_suspend(struct device *dev) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 80e6f48c83aa..a6e44e406106 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -569,7 +569,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) if (irq_err < 0) return irq_err; - mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -577,10 +577,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) ssp = &host->ssp; ssp->dev = &pdev->dev; ssp->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ssp->base)) { - ret = PTR_ERR(ssp->base); - goto out_mmc_free; - } + if (IS_ERR(ssp->base)) + return PTR_ERR(ssp->base); ssp->devid = (enum mxs_ssp_id)of_device_get_match_data(&pdev->dev); @@ -590,26 +588,23 @@ static int mxs_mmc_probe(struct platform_device *pdev) reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc"); if (!IS_ERR(reg_vmmc)) { ret = regulator_enable(reg_vmmc); - if (ret) { - dev_err(&pdev->dev, - "Failed to enable vmmc regulator: %d\n", ret); - goto out_mmc_free; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to enable vmmc regulator\n"); ret = devm_add_action_or_reset(&pdev->dev, mxs_mmc_regulator_disable, reg_vmmc); if (ret) - goto out_mmc_free; + return ret; } ssp->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(ssp->clk)) { - ret = PTR_ERR(ssp->clk); - goto out_mmc_free; - } + if (IS_ERR(ssp->clk)) + return PTR_ERR(ssp->clk); + ret = clk_prepare_enable(ssp->clk); if (ret) - goto out_mmc_free; + return ret; ret = mxs_mmc_reset(host); if (ret) { @@ -668,8 +663,6 @@ out_free_dma: dma_release_channel(ssp->dmach); out_clk_disable: clk_disable_unprepare(ssp->clk); -out_mmc_free: - mmc_free_host(mmc); return ret; } @@ -685,8 +678,6 @@ static void mxs_mmc_remove(struct platform_device *pdev) dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); - - mmc_free_host(mmc); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index c2be0f04439d..52ac3f128a1c 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1259,7 +1259,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) struct mmc_host *mmc; int r; - mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev); + mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot)); if (mmc == NULL) return -ENOMEM; @@ -1273,25 +1273,21 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) /* Check for some optional GPIO controls */ slot->vsd = devm_gpiod_get_index_optional(host->dev, "vsd", id, GPIOD_OUT_LOW); - if (IS_ERR(slot->vsd)) { - r = dev_err_probe(host->dev, PTR_ERR(slot->vsd), + if (IS_ERR(slot->vsd)) + return dev_err_probe(host->dev, PTR_ERR(slot->vsd), "error looking up VSD GPIO\n"); - goto err_free_host; - } + slot->vio = devm_gpiod_get_index_optional(host->dev, "vio", id, GPIOD_OUT_LOW); - if (IS_ERR(slot->vio)) { - r = dev_err_probe(host->dev, PTR_ERR(slot->vio), + if (IS_ERR(slot->vio)) + return dev_err_probe(host->dev, PTR_ERR(slot->vio), "error looking up VIO GPIO\n"); - goto err_free_host; - } + slot->cover = devm_gpiod_get_index_optional(host->dev, "cover", id, GPIOD_IN); - if (IS_ERR(slot->cover)) { - r = dev_err_probe(host->dev, PTR_ERR(slot->cover), + if (IS_ERR(slot->cover)) + return dev_err_probe(host->dev, PTR_ERR(slot->cover), "error looking up cover switch GPIO\n"); - goto err_free_host; - } host->slots[id] = slot; @@ -1351,8 +1347,6 @@ err_remove_slot_name: device_remove_file(&mmc->class_dev, &dev_attr_slot_name); err_remove_host: mmc_remove_host(mmc); -err_free_host: - mmc_free_host(mmc); return r; } @@ -1370,7 +1364,6 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) flush_workqueue(slot->host->mmc_omap_wq); mmc_remove_host(mmc); - mmc_free_host(mmc); } static int mmc_omap_probe(struct platform_device *pdev) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 59e36e0ebbbf..adc0d0b6ae37 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1663,7 +1663,6 @@ static int mmc_regs_show(struct seq_file *s, void *data) seq_printf(s, "CAPA:\t\t0x%08x\n", OMAP_HSMMC_READ(host->base, CAPA)); - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; @@ -1798,15 +1797,13 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto err; - } + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; ret = mmc_of_parse(mmc); if (ret) - goto err1; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -1842,7 +1839,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); host->fclk = NULL; - goto err1; + return ret; } if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { @@ -1956,7 +1953,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) } omap_hsmmc_debugfs(mmc); - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; @@ -1973,9 +1969,6 @@ err_irq: pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); clk_disable_unprepare(host->dbclk); -err1: - mmc_free_host(mmc); -err: return ret; } @@ -1995,8 +1988,6 @@ static void omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_disable(host->dev); device_init_wakeup(&pdev->dev, false); clk_disable_unprepare(host->dbclk); - - mmc_free_host(host->mmc); } #ifdef CONFIG_PM_SLEEP @@ -2038,7 +2029,6 @@ static int omap_hsmmc_resume(struct device *dev) if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) omap_hsmmc_conf_bus_power(host); - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; } diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c index 797ef48d9204..dc585726b66e 100644 --- a/drivers/mmc/host/owl-mmc.c +++ b/drivers/mmc/host/owl-mmc.c @@ -567,7 +567,7 @@ static int owl_mmc_probe(struct platform_device *pdev) struct resource *res; int ret; - mmc = mmc_alloc_host(sizeof(struct owl_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*owl_host)); if (!mmc) { dev_err(&pdev->dev, "mmc alloc host failed\n"); return -ENOMEM; @@ -580,24 +580,18 @@ static int owl_mmc_probe(struct platform_device *pdev) spin_lock_init(&owl_host->lock); owl_host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(owl_host->base)) { - ret = PTR_ERR(owl_host->base); - goto err_free_host; - } + if (IS_ERR(owl_host->base)) + return PTR_ERR(owl_host->base); owl_host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(owl_host->clk)) { - dev_err(&pdev->dev, "No clock defined\n"); - ret = PTR_ERR(owl_host->clk); - goto err_free_host; - } + if (IS_ERR(owl_host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->clk), + "No clock defined\n"); owl_host->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(owl_host->reset)) { - dev_err(&pdev->dev, "Could not get reset control\n"); - ret = PTR_ERR(owl_host->reset); - goto err_free_host; - } + if (IS_ERR(owl_host->reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->reset), + "Could not get reset control\n"); mmc->ops = &owl_mmc_ops; mmc->max_blk_count = 512; @@ -616,16 +610,14 @@ static int owl_mmc_probe(struct platform_device *pdev) ret = mmc_of_parse(mmc); if (ret) - goto err_free_host; + return ret; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; owl_host->dma = dma_request_chan(&pdev->dev, "mmc"); - if (IS_ERR(owl_host->dma)) { - dev_err(owl_host->dev, "Failed to get external DMA channel.\n"); - ret = PTR_ERR(owl_host->dma); - goto err_free_host; - } + if (IS_ERR(owl_host->dma)) + return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->dma), + "Failed to get external DMA channel.\n"); dev_info(&pdev->dev, "Using %s for DMA transfers\n", dma_chan_name(owl_host->dma)); @@ -662,8 +654,6 @@ static int owl_mmc_probe(struct platform_device *pdev) err_release_channel: dma_release_channel(owl_host->dma); -err_free_host: - mmc_free_host(mmc); return ret; } @@ -676,7 +666,6 @@ static void owl_mmc_remove(struct platform_device *pdev) mmc_remove_host(mmc); disable_irq(owl_host->irq); dma_release_channel(owl_host->dma); - mmc_free_host(mmc); } static const struct of_device_id owl_mmc_of_match[] = { diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 2d0ad006913d..26d03352af63 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -615,11 +615,9 @@ static int pxamci_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; mmc->ops = &pxamci_ops; @@ -646,7 +644,7 @@ static int pxamci_probe(struct platform_device *pdev) ret = pxamci_of_init(pdev, mmc); if (ret) - goto out; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -655,9 +653,8 @@ static int pxamci_probe(struct platform_device *pdev) host->clk = devm_clk_get(dev, NULL); if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); host->clk = NULL; - goto out; + return PTR_ERR(host->clk); } host->clkrate = clk_get_rate(host->clk); @@ -670,7 +667,7 @@ static int pxamci_probe(struct platform_device *pdev) ret = pxamci_init_ocr(host); if (ret < 0) - goto out; + return ret; mmc->caps = 0; host->cmdat = 0; @@ -686,10 +683,8 @@ static int pxamci_probe(struct platform_device *pdev) host->imask = MMC_I_MASK_ALL; host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto out; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->res = r; /* @@ -704,16 +699,15 @@ static int pxamci_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) - goto out; + return ret; platform_set_drvdata(pdev, mmc); host->dma_chan_rx = dma_request_chan(dev, "rx"); if (IS_ERR(host->dma_chan_rx)) { - dev_err(dev, "unable to request rx dma channel\n"); - ret = PTR_ERR(host->dma_chan_rx); host->dma_chan_rx = NULL; - goto out; + return dev_err_probe(dev, PTR_ERR(host->dma_chan_rx), + "unable to request rx dma channel\n"); } host->dma_chan_tx = dma_request_chan(dev, "tx"); @@ -771,14 +765,10 @@ static int pxamci_probe(struct platform_device *pdev) return 0; out: - if (host) { - if (host->dma_chan_rx) - dma_release_channel(host->dma_chan_rx); - if (host->dma_chan_tx) - dma_release_channel(host->dma_chan_tx); - } - if (mmc) - mmc_free_host(mmc); + if (host->dma_chan_rx) + dma_release_channel(host->dma_chan_rx); + if (host->dma_chan_tx) + dma_release_channel(host->dma_chan_tx); return ret; } @@ -803,8 +793,6 @@ static void pxamci_remove(struct platform_device *pdev) dmaengine_terminate_all(host->dma_chan_tx); dma_release_channel(host->dma_chan_rx); dma_release_channel(host->dma_chan_tx); - - mmc_free_host(mmc); } } diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index 291ddb4ad9be..084964cecf9d 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -85,6 +85,7 @@ struct renesas_sdhi { u32 scc_tappos_hs400; const u8 *adjust_hs400_calib_table; bool needs_adjust_hs400; + bool card_is_sdio; /* Tuning values: 1 for success, 0 for failure */ DECLARE_BITMAP(taps, BITS_PER_LONG); diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index e6fa3ed42560..fb8ca03f661d 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -686,9 +686,8 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) /* Set SCC */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, priv->tap_set); - /* Enable auto re-tuning */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, - SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN | + (priv->card_is_sdio ? 0 : SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN) | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); return 0; @@ -778,6 +777,14 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_ if (bad_taps & BIT(new_tap % priv->tap_num)) return test_bit(error_tap % priv->tap_num, priv->smpcmp); } else { + if (!priv->card_is_sdio && + !(val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)) { + u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP); + + /* DAT1 is unmatched because of an SDIO irq */ + if (smpcmp & (BIT(17) | BIT(1))) + return false; + } if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) return true; /* need retune */ else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP) @@ -828,11 +835,14 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host, if (mmc_doing_tune(host->mmc)) return false; - if (((mrq->cmd->error == -ETIMEDOUT) || - (mrq->data && mrq->data->error == -ETIMEDOUT)) && - ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || - (host->ops.get_cd && host->ops.get_cd(host->mmc)))) - ret |= true; + /* mrq can be NULL to check SCC error on SDIO irq without any request */ + if (mrq) { + if (((mrq->cmd->error == -ETIMEDOUT) || + (mrq->data && mrq->data->error == -ETIMEDOUT)) && + ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || + (host->ops.get_cd && host->ops.get_cd(host->mmc)))) + ret |= true; + } if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) & SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN) @@ -843,6 +853,28 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host, return ret; } +static void renesas_sdhi_init_card(struct mmc_host *mmc, struct mmc_card *card) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + struct renesas_sdhi *priv = host_to_priv(host); + + /* + * This controller cannot do auto-retune with SDIO irqs, so we + * then need to enforce manual correction. However, when tuning, + * mmc->card is not populated yet, so we don't know if the card + * is SDIO. init_card provides this information earlier, so we + * keep a copy of it. + */ + priv->card_is_sdio = mmc_card_sdio(card); +} + +static void renesas_sdhi_sdio_irq(struct tmio_mmc_host *host) +{ + /* This controller requires retune when an SDIO irq occurs */ + if (renesas_sdhi_check_scc_error(host, NULL)) + mmc_retune_needed(host->mmc); +} + static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit) { int timeout = 1000; @@ -1164,7 +1196,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, ret = renesas_sdhi_clk_enable(host); if (ret) - goto efree; + return ret; rcfg.of_node = of_get_available_child_by_name(dev->of_node, "vqmmc-regulator"); if (rcfg.of_node) { @@ -1227,6 +1259,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n"); host->check_retune = renesas_sdhi_check_scc_error; + host->sdio_irq = renesas_sdhi_sdio_irq; + host->ops.init_card = renesas_sdhi_init_card; host->ops.execute_tuning = renesas_sdhi_execute_tuning; host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning; host->ops.hs400_downgrade = renesas_sdhi_disable_scc; @@ -1266,9 +1300,6 @@ int renesas_sdhi_probe(struct platform_device *pdev, edisclk: renesas_sdhi_clk_disable(host); -efree: - tmio_mmc_host_free(host); - return ret; } EXPORT_SYMBOL_GPL(renesas_sdhi_probe); @@ -1279,7 +1310,6 @@ void renesas_sdhi_remove(struct platform_device *pdev) tmio_mmc_host_remove(host); renesas_sdhi_clk_disable(host); - tmio_mmc_host_free(host); } EXPORT_SYMBOL_GPL(renesas_sdhi_remove); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 0c6eb60a95fd..dc2587ff8519 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1498,7 +1498,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller found\n"); - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1529,7 +1529,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) if (ret) { pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); - mmc_free_host(mmc); return ret; } @@ -1572,8 +1571,6 @@ static void rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); - mmc_free_host(mmc); - dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller has been removed\n"); } diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index d229c2b83ea9..c5f6b9df066b 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1010,29 +1010,45 @@ static int sd_power_off(struct rtsx_usb_sdmmc *host) return sd_pull_ctl_disable_qfn24(ucr); } -static int sd_set_power_mode(struct rtsx_usb_sdmmc *host, +static void sd_set_power_mode(struct rtsx_usb_sdmmc *host, unsigned char power_mode) { int err; - - if (power_mode != MMC_POWER_OFF) - power_mode = MMC_POWER_ON; + struct rtsx_ucr *ucr = host->ucr; if (power_mode == host->power_mode) - return 0; + return; - if (power_mode == MMC_POWER_OFF) { + switch (power_mode) { + case MMC_POWER_OFF: err = sd_power_off(host); + if (err) + dev_dbg(sdmmc_dev(host), "power-off (err = %d)\n", err); pm_runtime_put_noidle(sdmmc_dev(host)); - } else { + break; + + case MMC_POWER_UP: pm_runtime_get_noresume(sdmmc_dev(host)); err = sd_power_on(host); + if (err) + dev_dbg(sdmmc_dev(host), "power-on (err = %d)\n", err); + /* issue the clock signals to card at least 74 clocks */ + rtsx_usb_write_register(ucr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, SD_CLK_TOGGLE_EN); + break; + + case MMC_POWER_ON: + /* stop to send the clock signals */ + rtsx_usb_write_register(ucr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, 0x00); + break; + + case MMC_POWER_UNDEFINED: + break; + + default: + break; } - if (!err) - host->power_mode = power_mode; - - return err; + host->power_mode = power_mode; } static int sd_set_timing(struct rtsx_usb_sdmmc *host, @@ -1334,7 +1350,7 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) dev_dbg(&(pdev->dev), ": Realtek USB SD/MMC controller found\n"); - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1368,7 +1384,6 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) #ifdef RTSX_USB_USE_LEDS_CLASS led_classdev_unregister(&host->led); #endif - mmc_free_host(mmc); pm_runtime_disable(&pdev->dev); return ret; } @@ -1406,7 +1421,6 @@ static void rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev) led_classdev_unregister(&host->led); #endif - mmc_free_host(mmc); pm_runtime_disable(&pdev->dev); platform_set_drvdata(pdev, NULL); diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index e6c5c82f64fa..2d46d4854fa1 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -948,7 +948,6 @@ err_free: if (c->slot && c->slot->free_slot) c->slot->free_slot(pdev); - sdhci_free_host(c->host); return err; } @@ -972,8 +971,6 @@ static void sdhci_acpi_remove(struct platform_device *pdev) if (c->slot && c->slot->free_slot) c->slot->free_slot(pdev); - - sdhci_free_host(c->host); } static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed( @@ -1028,14 +1025,11 @@ static int sdhci_acpi_runtime_suspend(struct device *dev) { struct sdhci_acpi_host *c = dev_get_drvdata(dev); struct sdhci_host *host = c->host; - int ret; if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); sdhci_acpi_reset_signal_voltage_if_needed(dev); return 0; @@ -1047,7 +1041,8 @@ static int sdhci_acpi_runtime_resume(struct device *dev) sdhci_acpi_byt_setting(&c->pdev->dev); - return sdhci_runtime_resume_host(c->host, 0); + sdhci_runtime_resume_host(c->host, 0); + return 0; } #endif diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index fda911fb28e5..0f2a84f769b6 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -304,8 +304,6 @@ err_clk_disable: clk_disable_unprepare(pltfm_priv->clk); err_pltfm_free: - sdhci_pltfm_free(pdev); - dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret); return ret; } diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 48cdcba0f39c..efc2f3bdc631 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -485,7 +485,6 @@ add_host: return res; err: - sdhci_pltfm_free(pdev); clk_disable_unprepare(base_clk); return res; } diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index a94b297fcf2a..2d823e158c59 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -144,7 +144,7 @@ static unsigned int sdhci_cdns_phy_param_count(struct device_node *np) int i; for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) - if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property)) + if (of_property_present(np, sdhci_cdns_phy_cfgs[i].property)) count++; return count; @@ -433,6 +433,13 @@ static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { }, }; +static const struct sdhci_cdns_drv_data sdhci_eyeq_drv_data = { + .pltfm_data = { + .ops = &sdhci_cdns_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, +}; + static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = { .pltfm_data = { .ops = &sdhci_cdns_ops, @@ -515,7 +522,7 @@ static int sdhci_cdns_probe(struct platform_device *pdev) if (data->init) { ret = data->init(pdev); if (ret) - goto free; + return ret; } sdhci_enable_v4_mode(host); __sdhci_read_caps(host, &version, NULL, NULL); @@ -524,33 +531,24 @@ static int sdhci_cdns_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto free; + return ret; sdhci_cdns_phy_param_parse(dev->of_node, priv); ret = sdhci_cdns_phy_init(priv); if (ret) - goto free; + return ret; if (host->mmc->caps & MMC_CAP_HW_RESET) { priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(priv->rst_hw)) { - ret = dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw), + if (IS_ERR(priv->rst_hw)) + return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw), "reset controller error\n"); - goto free; - } if (priv->rst_hw) host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset; } - ret = sdhci_add_host(host); - if (ret) - goto free; - - return 0; -free: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } #ifdef CONFIG_PM_SLEEP @@ -595,6 +593,10 @@ static const struct of_device_id sdhci_cdns_match[] = { .compatible = "amd,pensando-elba-sd4hc", .data = &sdhci_elba_drv_data, }, + { + .compatible = "mobileye,eyeq-sd4hc", + .data = &sdhci_eyeq_drv_data, + }, { .compatible = "cdns,sd4hc" }, { /* sentinel */ } }; diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 77034b13fa66..dbfaee4a5ada 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -79,17 +79,9 @@ static int sdhci_dove_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err_sdhci_add; + return ret; - ret = sdhci_add_host(host); - if (ret) - goto err_sdhci_add; - - return 0; - -err_sdhci_add: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static const struct of_device_id sdhci_dove_of_match_table[] = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ac187a8798b7..a040c0896a7b 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -212,6 +212,9 @@ /* The IP does not have GPIO CD wake capabilities */ #define ESDHC_FLAG_SKIP_CD_WAKE BIT(18) +/* the controller has dummy pad for clock loopback */ +#define ESDHC_FLAG_DUMMY_PAD BIT(19) + #define ESDHC_AUTO_TUNING_WINDOW 3 enum wp_types { @@ -348,6 +351,15 @@ static struct esdhc_soc_data usdhc_imx8mm_data = { .quirks = SDHCI_QUIRK_NO_LED, }; +static struct esdhc_soc_data usdhc_imx95_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES + | ESDHC_FLAG_STATE_LOST_IN_LPMODE + | ESDHC_FLAG_DUMMY_PAD, + .quirks = SDHCI_QUIRK_NO_LED, +}; + struct pltfm_imx_data { u32 scratchpad; struct pinctrl *pinctrl; @@ -392,6 +404,8 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, }, { .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, }, { .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, }, + { .compatible = "fsl,imx94-usdhc", .data = &usdhc_imx95_data, }, + { .compatible = "fsl,imx95-usdhc", .data = &usdhc_imx95_data, }, { .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, }, { .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, }, { /* sentinel */ } @@ -728,23 +742,17 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); - u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); - if (val & SDHCI_CTRL_TUNED_CLK) { + if (val & SDHCI_CTRL_TUNED_CLK) v |= ESDHC_MIX_CTRL_SMPCLK_SEL; - } else { + else v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; - m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; - } - if (val & SDHCI_CTRL_EXEC_TUNING) { + if (val & SDHCI_CTRL_EXEC_TUNING) v |= ESDHC_MIX_CTRL_EXE_TUNE; - m |= ESDHC_MIX_CTRL_FBCLK_SEL; - } else { + else v &= ~ESDHC_MIX_CTRL_EXE_TUNE; - } writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS); - writel(m, host->ioaddr + ESDHC_MIX_CTRL); } return; case SDHCI_TRANSFER_MODE: @@ -1082,7 +1090,6 @@ static void esdhc_reset_tuning(struct sdhci_host *host) ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN; if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; - ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL; writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL); writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { @@ -1177,8 +1184,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) "warning! RESET_ALL never complete before sending tuning command\n"); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); - reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | - ESDHC_MIX_CTRL_FBCLK_SEL; + reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL; writel(reg, host->ioaddr + ESDHC_MIX_CTRL); writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, val), host->ioaddr + ESDHC_TUNE_CTRL_STATUS); @@ -1432,6 +1438,16 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) break; } + if (!(imx_data->socdata->flags & ESDHC_FLAG_DUMMY_PAD) && + (timing == MMC_TIMING_UHS_SDR104 || + timing == MMC_TIMING_MMC_HS200 || + timing == MMC_TIMING_MMC_HS400)) + m |= ESDHC_MIX_CTRL_FBCLK_SEL; + else + m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; + + writel(m, host->ioaddr + ESDHC_MIX_CTRL); + esdhc_change_pinstate(host, timing); } @@ -1677,7 +1693,9 @@ static void sdhc_esdhc_tuning_restore(struct sdhci_host *host) writel(reg, host->ioaddr + ESDHC_TUNING_CTRL); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); - reg |= ESDHC_MIX_CTRL_SMPCLK_SEL | ESDHC_MIX_CTRL_FBCLK_SEL; + reg |= ESDHC_MIX_CTRL_SMPCLK_SEL; + if (!(imx_data->socdata->flags & ESDHC_FLAG_DUMMY_PAD)) + reg |= ESDHC_MIX_CTRL_FBCLK_SEL; writel(reg, host->ioaddr + ESDHC_MIX_CTRL); writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, @@ -1973,7 +1991,6 @@ disable_per_clk: free_sdhci: if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) cpu_latency_qos_remove_request(&imx_data->pm_qos_req); - sdhci_pltfm_free(pdev); return err; } @@ -1997,8 +2014,6 @@ static void sdhci_esdhc_imx_remove(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) cpu_latency_qos_remove_request(&imx_data->pm_qos_req); - - sdhci_pltfm_free(pdev); } #ifdef CONFIG_PM_SLEEP @@ -2039,12 +2054,20 @@ static int sdhci_esdhc_suspend(struct device *dev) ret = sdhci_enable_irq_wakeups(host); if (!ret) dev_warn(dev, "Failed to enable irq wakeup\n"); + } else { + /* + * For the device which works as wakeup source, no need + * to change the pinctrl to sleep state. + * e.g. For SDIO device, the interrupt share with data pin, + * but the pinctrl sleep state may config the data pin to + * other function like GPIO function to save power in PM, + * which finally block the SDIO wakeup function. + */ + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + return ret; } - ret = pinctrl_pm_select_sleep_state(dev); - if (ret) - return ret; - ret = mmc_gpio_set_cd_wake(host->mmc, true); /* @@ -2085,7 +2108,6 @@ static int sdhci_esdhc_resume(struct device *dev) esdhc_is_usdhc(imx_data)) sdhc_esdhc_tuning_restore(host); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -2106,9 +2128,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) return ret; } - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -2122,7 +2142,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) cpu_latency_qos_remove_request(&imx_data->pm_qos_req); - return ret; + return 0; } static int sdhci_esdhc_runtime_resume(struct device *dev) @@ -2152,17 +2172,13 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) esdhc_pltfm_set_clock(host, imx_data->actual_clock); - err = sdhci_runtime_resume_host(host, 0); - if (err) - goto disable_ipg_clk; + sdhci_runtime_resume_host(host, 0); if (host->mmc->caps2 & MMC_CAP2_CQE) err = cqhci_resume(host->mmc); return err; -disable_ipg_clk: - clk_disable_unprepare(imx_data->clk_ipg); disable_per_clk: clk_disable_unprepare(imx_data->clk_per); disable_ahb_clk: diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c index 327662ba5bd9..375fce5639d7 100644 --- a/drivers/mmc/host/sdhci-esdhc-mcf.c +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c @@ -426,28 +426,22 @@ static int sdhci_esdhc_mcf_probe(struct platform_device *pdev) host->flags |= SDHCI_AUTO_CMD12; mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(mcf_data->clk_ipg)) { - err = PTR_ERR(mcf_data->clk_ipg); - goto err_exit; - } + if (IS_ERR(mcf_data->clk_ipg)) + return PTR_ERR(mcf_data->clk_ipg); mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(mcf_data->clk_ahb)) { - err = PTR_ERR(mcf_data->clk_ahb); - goto err_exit; - } + if (IS_ERR(mcf_data->clk_ahb)) + return PTR_ERR(mcf_data->clk_ahb); mcf_data->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(mcf_data->clk_per)) { - err = PTR_ERR(mcf_data->clk_per); - goto err_exit; - } + if (IS_ERR(mcf_data->clk_per)) + return PTR_ERR(mcf_data->clk_per); pltfm_host->clk = mcf_data->clk_per; pltfm_host->clock = clk_get_rate(pltfm_host->clk); err = clk_prepare_enable(mcf_data->clk_per); if (err) - goto err_exit; + return err; err = clk_prepare_enable(mcf_data->clk_ipg); if (err) @@ -485,9 +479,6 @@ unprep_ipg: clk_disable_unprepare(mcf_data->clk_ipg); unprep_per: clk_disable_unprepare(mcf_data->clk_per); -err_exit: - sdhci_pltfm_free(pdev); - return err; } @@ -502,8 +493,6 @@ static void sdhci_esdhc_mcf_remove(struct platform_device *pdev) clk_disable_unprepare(mcf_data->clk_ipg); clk_disable_unprepare(mcf_data->clk_ahb); clk_disable_unprepare(mcf_data->clk_per); - - sdhci_pltfm_free(pdev); } static struct platform_driver sdhci_esdhc_mcf_driver = { diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index 80b2567a488b..35ef5c5f5146 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -379,7 +379,7 @@ static int sdhci_iproc_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; sdhci_get_property(pdev); @@ -387,10 +387,8 @@ static int sdhci_iproc_probe(struct platform_device *pdev) if (dev->of_node) { pltfm_host->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - goto err; - } + if (IS_ERR(pltfm_host->clk)) + return PTR_ERR(pltfm_host->clk); } if (iproc_host->data->missing_caps) { @@ -399,15 +397,7 @@ static int sdhci_iproc_probe(struct platform_device *pdev) &iproc_host->data->caps1); } - ret = sdhci_add_host(host); - if (ret) - goto err; - - return 0; - -err: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static void sdhci_iproc_shutdown(struct platform_device *pdev) diff --git a/drivers/mmc/host/sdhci-milbeaut.c b/drivers/mmc/host/sdhci-milbeaut.c index a4675456f9c7..bda71d5966dc 100644 --- a/drivers/mmc/host/sdhci-milbeaut.c +++ b/drivers/mmc/host/sdhci-milbeaut.c @@ -258,7 +258,7 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; platform_set_drvdata(pdev, host); @@ -267,23 +267,19 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev) host->irq = irq; host->ioaddr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host->ioaddr)) { - ret = PTR_ERR(host->ioaddr); - goto err; - } + if (IS_ERR(host->ioaddr)) + return PTR_ERR(host->ioaddr); if (dev_of_node(dev)) { sdhci_get_of_property(pdev); priv->clk_iface = devm_clk_get(&pdev->dev, "iface"); - if (IS_ERR(priv->clk_iface)) { - ret = PTR_ERR(priv->clk_iface); - goto err; - } + if (IS_ERR(priv->clk_iface)) + return PTR_ERR(priv->clk_iface); ret = clk_prepare_enable(priv->clk_iface); if (ret) - goto err; + return ret; priv->clk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(priv->clk)) { @@ -308,8 +304,6 @@ err_add_host: clk_disable_unprepare(priv->clk); err_clk: clk_disable_unprepare(priv->clk_iface); -err: - sdhci_free_host(host); return ret; } @@ -324,7 +318,6 @@ static void sdhci_milbeaut_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk_iface); clk_disable_unprepare(priv->clk); - sdhci_free_host(host); platform_set_drvdata(pdev, NULL); } diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 66c0d1ba2a33..9d8e20dc8ca1 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1564,6 +1564,7 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = host->mmc; bool done = false; u32 val = SWITCHABLE_SIGNALING_VOLTAGE; const struct sdhci_msm_offset *msm_offset = @@ -1621,6 +1622,12 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) "%s: pwr_irq for req: (%d) timed out\n", mmc_hostname(host->mmc), req_type); } + + if ((req_type & REQ_BUS_ON) && mmc->card && !mmc->ops->get_cd(mmc)) { + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + host->pwr = 0; + } + pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), __func__, req_type); } @@ -1679,6 +1686,13 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) udelay(10); } + if ((irq_status & CORE_PWRCTL_BUS_ON) && mmc->card && + !mmc->ops->get_cd(mmc)) { + msm_host_writel(msm_host, CORE_PWRCTL_BUS_FAIL, host, + msm_offset->core_pwrctl_ctl); + return; + } + /* Handle BUS ON/OFF*/ if (irq_status & CORE_PWRCTL_BUS_ON) { pwr_state = REQ_BUS_ON; @@ -2526,7 +2540,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto pltfm_free; + return ret; /* * Based on the compatible string, load the required msm host info from @@ -2548,7 +2562,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_msm_gcc_reset(&pdev->dev, host); if (ret) - goto pltfm_free; + return ret; /* Setup SDCC bus voter clock. */ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); @@ -2556,10 +2570,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) /* Vote for max. clk rate for max. performance */ ret = clk_set_rate(msm_host->bus_clk, INT_MAX); if (ret) - goto pltfm_free; + return ret; ret = clk_prepare_enable(msm_host->bus_clk); if (ret) - goto pltfm_free; + return ret; } /* Setup main peripheral bus clock */ @@ -2750,7 +2764,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (ret) goto pm_runtime_disable; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; @@ -2765,8 +2778,6 @@ clk_disable: bus_clk_disable: if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); -pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -2788,7 +2799,6 @@ static void sdhci_msm_remove(struct platform_device *pdev) msm_host->bulk_clks); if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); - sdhci_pltfm_free(pdev); } static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) diff --git a/drivers/mmc/host/sdhci-npcm.c b/drivers/mmc/host/sdhci-npcm.c index bee0585ba5c1..71b635dfdf1d 100644 --- a/drivers/mmc/host/sdhci-npcm.c +++ b/drivers/mmc/host/sdhci-npcm.c @@ -48,8 +48,7 @@ static int npcm_sdhci_probe(struct platform_device *pdev) pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - goto err_sdhci; + return PTR_ERR(pltfm_host->clk); } caps = sdhci_readl(host, SDHCI_CAPABILITIES); @@ -58,17 +57,9 @@ static int npcm_sdhci_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err_sdhci; + return ret; - ret = sdhci_add_host(host); - if (ret) - goto err_sdhci; - - return 0; - -err_sdhci: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static const struct of_device_id npcm_sdhci_of_match[] = { diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 8c29676ab662..42878474e56e 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1883,34 +1883,26 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); of_node_put(node); - if (IS_ERR(sdhci_arasan->soc_ctl_base)) { - ret = dev_err_probe(dev, + if (IS_ERR(sdhci_arasan->soc_ctl_base)) + return dev_err_probe(dev, PTR_ERR(sdhci_arasan->soc_ctl_base), "Can't get syscon\n"); - goto err_pltfm_free; - } } sdhci_get_of_property(pdev); sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb"); - if (IS_ERR(sdhci_arasan->clk_ahb)) { - ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb), + if (IS_ERR(sdhci_arasan->clk_ahb)) + return dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb), "clk_ahb clock not found.\n"); - goto err_pltfm_free; - } clk_xin = devm_clk_get(dev, "clk_xin"); - if (IS_ERR(clk_xin)) { - ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n"); - goto err_pltfm_free; - } + if (IS_ERR(clk_xin)) + return dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n"); ret = clk_prepare_enable(sdhci_arasan->clk_ahb); - if (ret) { - dev_err(dev, "Unable to enable AHB clock.\n"); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(dev, ret, "Unable to enable AHB clock.\n"); /* If clock-frequency property is set, use the provided value */ if (pltfm_host->clock && @@ -2029,8 +2021,6 @@ clk_disable_all: clk_disable_unprepare(clk_xin); clk_dis_ahb: clk_disable_unprepare(sdhci_arasan->clk_ahb); -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index d6de010551b9..ca97b01996b1 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -425,10 +425,8 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) return PTR_ERR(pltfm_host->clk); ret = clk_prepare_enable(pltfm_host->clk); - if (ret) { - dev_err(&pdev->dev, "Unable to enable SDIO clock\n"); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Unable to enable SDIO clock\n"); ret = mmc_of_parse(host->mmc); if (ret) @@ -445,8 +443,6 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) err_sdhci_add: clk_disable_unprepare(pltfm_host->clk); -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -461,8 +457,6 @@ static void aspeed_sdhci_remove(struct platform_device *pdev) sdhci_remove_host(host, 0); clk_disable_unprepare(pltfm_host->clk); - - sdhci_pltfm_free(pdev); } static const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = { diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 97988ed37467..1ba2effaf376 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -251,9 +251,8 @@ static int sdhci_at91_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); - int ret; - ret = sdhci_runtime_suspend_host(host); + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -262,7 +261,7 @@ static int sdhci_at91_runtime_suspend(struct device *dev) clk_disable_unprepare(priv->hclock); clk_disable_unprepare(priv->mainck); - return ret; + return 0; } static int sdhci_at91_runtime_resume(struct device *dev) @@ -300,7 +299,8 @@ static int sdhci_at91_runtime_resume(struct device *dev) } out: - return sdhci_runtime_resume_host(host, 0); + sdhci_runtime_resume_host(host, 0); + return 0; } #endif /* CONFIG_PM */ @@ -333,32 +333,26 @@ static int sdhci_at91_probe(struct platform_device *pdev) priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); if (IS_ERR(priv->mainck)) { - if (soc_data->baseclk_is_generated_internally) { + if (soc_data->baseclk_is_generated_internally) priv->mainck = NULL; - } else { - dev_err(&pdev->dev, "failed to get baseclk\n"); - ret = PTR_ERR(priv->mainck); - goto sdhci_pltfm_free; - } + else + return dev_err_probe(&pdev->dev, PTR_ERR(priv->mainck), + "failed to get baseclk\n"); } priv->hclock = devm_clk_get(&pdev->dev, "hclock"); - if (IS_ERR(priv->hclock)) { - dev_err(&pdev->dev, "failed to get hclock\n"); - ret = PTR_ERR(priv->hclock); - goto sdhci_pltfm_free; - } + if (IS_ERR(priv->hclock)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->hclock), + "failed to get hclock\n"); priv->gck = devm_clk_get(&pdev->dev, "multclk"); - if (IS_ERR(priv->gck)) { - dev_err(&pdev->dev, "failed to get multclk\n"); - ret = PTR_ERR(priv->gck); - goto sdhci_pltfm_free; - } + if (IS_ERR(priv->gck)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->gck), + "failed to get multclk\n"); ret = sdhci_at91_set_clks_presets(&pdev->dev); if (ret) - goto sdhci_pltfm_free; + return ret; priv->restore_needed = false; @@ -438,8 +432,6 @@ clocks_disable_unprepare: clk_disable_unprepare(priv->gck); clk_disable_unprepare(priv->mainck); clk_disable_unprepare(priv->hclock); -sdhci_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index a20d03fdd6a9..ee6b1096f709 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -1387,14 +1387,13 @@ static int dwcmshc_probe(struct platform_device *pdev) if (dev->of_node) { pltfm_host->clk = devm_clk_get(dev, "core"); - if (IS_ERR(pltfm_host->clk)) { - err = PTR_ERR(pltfm_host->clk); - dev_err(dev, "failed to get core clk: %d\n", err); - goto free_pltfm; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(dev, PTR_ERR(pltfm_host->clk), + "failed to get core clk\n"); + err = clk_prepare_enable(pltfm_host->clk); if (err) - goto free_pltfm; + return err; priv->bus_clk = devm_clk_get(dev, "bus"); if (!IS_ERR(priv->bus_clk)) @@ -1467,8 +1466,6 @@ err_clk: clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); -free_pltfm: - sdhci_pltfm_free(pdev); return err; } @@ -1500,7 +1497,6 @@ static void dwcmshc_remove(struct platform_device *pdev) clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); - sdhci_pltfm_free(pdev); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 002d0d59b992..c6ee0099ead0 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -1499,18 +1499,11 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) /* call to generic mmc_of_parse to support additional capabilities */ ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; mmc_of_parse_voltage(host->mmc, &host->ocr_mask); - ret = sdhci_add_host(host); - if (ret) - goto err; - - return 0; - err: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static struct platform_driver sdhci_esdhc_driver = { diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index 2e5da7c5834c..0cc97e23a2f9 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -20,45 +20,45 @@ #include "sdhci.h" #include "sdhci-pltfm.h" -#define SDHC_MMC_CTRL_REG 0x114 -#define MISC_INT_EN BIT(1) -#define MISC_INT BIT(2) -#define ENHANCE_STROBE_EN BIT(8) -#define MMC_HS400 BIT(9) -#define MMC_HS200 BIT(10) -#define MMC_CARD_MODE BIT(12) +#define SPACEMIT_SDHC_MMC_CTRL_REG 0x114 +#define SDHC_MISC_INT_EN BIT(1) +#define SDHC_MISC_INT BIT(2) +#define SDHC_ENHANCE_STROBE_EN BIT(8) +#define SDHC_MMC_HS400 BIT(9) +#define SDHC_MMC_HS200 BIT(10) +#define SDHC_MMC_CARD_MODE BIT(12) -#define SDHC_TX_CFG_REG 0x11C -#define TX_INT_CLK_SEL BIT(30) -#define TX_MUX_SEL BIT(31) +#define SPACEMIT_SDHC_TX_CFG_REG 0x11C +#define SDHC_TX_INT_CLK_SEL BIT(30) +#define SDHC_TX_MUX_SEL BIT(31) -#define SDHC_PHY_CTRL_REG 0x160 -#define PHY_FUNC_EN BIT(0) -#define PHY_PLL_LOCK BIT(1) -#define HOST_LEGACY_MODE BIT(31) +#define SPACEMIT_SDHC_PHY_CTRL_REG 0x160 +#define SDHC_PHY_FUNC_EN BIT(0) +#define SDHC_PHY_PLL_LOCK BIT(1) +#define SDHC_HOST_LEGACY_MODE BIT(31) -#define SDHC_PHY_FUNC_REG 0x164 -#define PHY_TEST_EN BIT(7) -#define HS200_USE_RFIFO BIT(15) +#define SPACEMIT_SDHC_PHY_FUNC_REG 0x164 +#define SDHC_PHY_TEST_EN BIT(7) +#define SDHC_HS200_USE_RFIFO BIT(15) -#define SDHC_PHY_DLLCFG 0x168 -#define DLL_PREDLY_NUM GENMASK(3, 2) -#define DLL_FULLDLY_RANGE GENMASK(5, 4) -#define DLL_VREG_CTRL GENMASK(7, 6) -#define DLL_ENABLE BIT(31) +#define SPACEMIT_SDHC_PHY_DLLCFG 0x168 +#define SDHC_DLL_PREDLY_NUM GENMASK(3, 2) +#define SDHC_DLL_FULLDLY_RANGE GENMASK(5, 4) +#define SDHC_DLL_VREG_CTRL GENMASK(7, 6) +#define SDHC_DLL_ENABLE BIT(31) -#define SDHC_PHY_DLLCFG1 0x16C -#define DLL_REG1_CTRL GENMASK(7, 0) -#define DLL_REG2_CTRL GENMASK(15, 8) -#define DLL_REG3_CTRL GENMASK(23, 16) -#define DLL_REG4_CTRL GENMASK(31, 24) +#define SPACEMIT_SDHC_PHY_DLLCFG1 0x16C +#define SDHC_DLL_REG1_CTRL GENMASK(7, 0) +#define SDHC_DLL_REG2_CTRL GENMASK(15, 8) +#define SDHC_DLL_REG3_CTRL GENMASK(23, 16) +#define SDHC_DLL_REG4_CTRL GENMASK(31, 24) -#define SDHC_PHY_DLLSTS 0x170 -#define DLL_LOCK_STATE BIT(0) +#define SPACEMIT_SDHC_PHY_DLLSTS 0x170 +#define SDHC_DLL_LOCK_STATE BIT(0) -#define SDHC_PHY_PADCFG_REG 0x178 -#define PHY_DRIVE_SEL GENMASK(2, 0) -#define RX_BIAS_CTRL BIT(5) +#define SPACEMIT_SDHC_PHY_PADCFG_REG 0x178 +#define SDHC_PHY_DRIVE_SEL GENMASK(2, 0) +#define SDHC_RX_BIAS_CTRL BIT(5) struct spacemit_sdhci_host { struct clk *clk_core; @@ -91,23 +91,24 @@ static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask) if (mask != SDHCI_RESET_ALL) return; - spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG); + spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, + SPACEMIT_SDHC_PHY_CTRL_REG); - spacemit_sdhci_clrsetbits(host, PHY_DRIVE_SEL, - RX_BIAS_CTRL | FIELD_PREP(PHY_DRIVE_SEL, 4), - SDHC_PHY_PADCFG_REG); + spacemit_sdhci_clrsetbits(host, SDHC_PHY_DRIVE_SEL, + SDHC_RX_BIAS_CTRL | FIELD_PREP(SDHC_PHY_DRIVE_SEL, 4), + SPACEMIT_SDHC_PHY_PADCFG_REG); if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) - spacemit_sdhci_setbits(host, MMC_CARD_MODE, SDHC_MMC_CTRL_REG); + spacemit_sdhci_setbits(host, SDHC_MMC_CARD_MODE, SPACEMIT_SDHC_MMC_CTRL_REG); } static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { if (timing == MMC_TIMING_MMC_HS200) - spacemit_sdhci_setbits(host, MMC_HS200, SDHC_MMC_CTRL_REG); + spacemit_sdhci_setbits(host, SDHC_MMC_HS200, SPACEMIT_SDHC_MMC_CTRL_REG); if (timing == MMC_TIMING_MMC_HS400) - spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG); + spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG); sdhci_set_uhs_signaling(host, timing); @@ -120,9 +121,9 @@ static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock struct mmc_host *mmc = host->mmc; if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50) - spacemit_sdhci_setbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG); + spacemit_sdhci_setbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG); else - spacemit_sdhci_clrbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG); + spacemit_sdhci_clrbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG); sdhci_set_clock(host, clock); }; @@ -132,20 +133,22 @@ static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host) u32 state; int ret; - spacemit_sdhci_clrsetbits(host, DLL_PREDLY_NUM | DLL_FULLDLY_RANGE | DLL_VREG_CTRL, - FIELD_PREP(DLL_PREDLY_NUM, 1) | - FIELD_PREP(DLL_FULLDLY_RANGE, 1) | - FIELD_PREP(DLL_VREG_CTRL, 1), - SDHC_PHY_DLLCFG); + spacemit_sdhci_clrsetbits(host, SDHC_DLL_PREDLY_NUM | + SDHC_DLL_FULLDLY_RANGE | + SDHC_DLL_VREG_CTRL, + FIELD_PREP(SDHC_DLL_PREDLY_NUM, 1) | + FIELD_PREP(SDHC_DLL_FULLDLY_RANGE, 1) | + FIELD_PREP(SDHC_DLL_VREG_CTRL, 1), + SPACEMIT_SDHC_PHY_DLLCFG); - spacemit_sdhci_clrsetbits(host, DLL_REG1_CTRL, - FIELD_PREP(DLL_REG1_CTRL, 0x92), - SDHC_PHY_DLLCFG1); + spacemit_sdhci_clrsetbits(host, SDHC_DLL_REG1_CTRL, + FIELD_PREP(SDHC_DLL_REG1_CTRL, 0x92), + SPACEMIT_SDHC_PHY_DLLCFG1); - spacemit_sdhci_setbits(host, DLL_ENABLE, SDHC_PHY_DLLCFG); + spacemit_sdhci_setbits(host, SDHC_DLL_ENABLE, SPACEMIT_SDHC_PHY_DLLCFG); - ret = readl_poll_timeout(host->ioaddr + SDHC_PHY_DLLSTS, state, - state & DLL_LOCK_STATE, 2, 100); + ret = readl_poll_timeout(host->ioaddr + SPACEMIT_SDHC_PHY_DLLSTS, state, + state & SDHC_DLL_LOCK_STATE, 2, 100); if (ret == -ETIMEDOUT) dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n"); } @@ -155,11 +158,11 @@ static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mm struct sdhci_host *host = mmc_priv(mmc); if (!ios->enhanced_strobe) { - spacemit_sdhci_clrbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG); + spacemit_sdhci_clrbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG); return; } - spacemit_sdhci_setbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG); + spacemit_sdhci_setbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG); spacemit_sdhci_phy_dll_init(host); } @@ -174,8 +177,7 @@ static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); - spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG); - host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; + spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG); return 0; } @@ -185,20 +187,22 @@ static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc) struct sdhci_host *host = mmc_priv(mmc); spacemit_sdhci_phy_dll_init(host); - host->mmc->caps &= ~MMC_CAP_WAIT_WHILE_BUSY; } static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); - spacemit_sdhci_clrbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG); - spacemit_sdhci_clrbits(host, MMC_HS400 | MMC_HS200 | ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG); - spacemit_sdhci_clrbits(host, HS200_USE_RFIFO, SDHC_PHY_FUNC_REG); + spacemit_sdhci_clrbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, + SPACEMIT_SDHC_PHY_CTRL_REG); + spacemit_sdhci_clrbits(host, SDHC_MMC_HS400 | SDHC_MMC_HS200 | SDHC_ENHANCE_STROBE_EN, + SPACEMIT_SDHC_MMC_CTRL_REG); + spacemit_sdhci_clrbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG); udelay(5); - spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG); + spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, + SPACEMIT_SDHC_PHY_CTRL_REG); } static inline int spacemit_sdhci_get_clocks(struct device *dev, @@ -287,7 +291,6 @@ static int spacemit_sdhci_probe(struct platform_device *pdev) return 0; err_pltfm: - sdhci_pltfm_free(pdev); return ret; } diff --git a/drivers/mmc/host/sdhci-of-ma35d1.c b/drivers/mmc/host/sdhci-of-ma35d1.c index 1e6d180100ad..287026422616 100644 --- a/drivers/mmc/host/sdhci-of-ma35d1.c +++ b/drivers/mmc/host/sdhci-of-ma35d1.c @@ -211,20 +211,18 @@ static int ma35_probe(struct platform_device *pdev) priv = sdhci_pltfm_priv(pltfm_host); pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL); - if (IS_ERR(pltfm_host->clk)) { - err = dev_err_probe(dev, PTR_ERR(pltfm_host->clk), "failed to get clk\n"); - goto err_sdhci; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(dev, PTR_ERR(pltfm_host->clk), + "failed to get clk\n"); err = mmc_of_parse(host->mmc); if (err) - goto err_sdhci; + return err; priv->rst = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(priv->rst)) { - err = dev_err_probe(dev, PTR_ERR(priv->rst), "failed to get reset control\n"); - goto err_sdhci; - } + if (IS_ERR(priv->rst)) + return dev_err_probe(dev, PTR_ERR(priv->rst), + "failed to get reset control\n"); sdhci_get_of_property(pdev); @@ -255,7 +253,7 @@ static int ma35_probe(struct platform_device *pdev) err = sdhci_add_host(host); if (err) - goto err_sdhci; + return err; /* * Split data into chunks of 16 or 8 bytes for transmission. @@ -268,10 +266,6 @@ static int ma35_probe(struct platform_device *pdev) sdhci_writew(host, ctl, MA35_SDHCI_MBIUCTL); return 0; - -err_sdhci: - sdhci_pltfm_free(pdev); - return err; } static void ma35_disable_card_clk(struct sdhci_host *host) @@ -291,7 +285,6 @@ static void ma35_remove(struct platform_device *pdev) sdhci_remove_host(host, 0); ma35_disable_card_clk(host); - sdhci_pltfm_free(pdev); } static const struct of_device_id sdhci_ma35_dt_ids[] = { diff --git a/drivers/mmc/host/sdhci-of-sparx5.c b/drivers/mmc/host/sdhci-of-sparx5.c index d2aa684e786f..b3db1e2c4c0e 100644 --- a/drivers/mmc/host/sdhci-of-sparx5.c +++ b/drivers/mmc/host/sdhci-of-sparx5.c @@ -185,11 +185,9 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) sdhci_sparx5->host = host; pltfm_host->clk = devm_clk_get_enabled(&pdev->dev, "core"); - if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - dev_err(&pdev->dev, "failed to get and enable core clk: %d\n", ret); - goto free_pltfm; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pltfm_host->clk), + "failed to get and enable core clk\n"); if (!of_property_read_u32(np, "microchip,clock-delay", &value) && (value > 0 && value <= MSHC_DLY_CC_MAX)) @@ -199,14 +197,12 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto free_pltfm; + return ret; sdhci_sparx5->cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon); - if (IS_ERR(sdhci_sparx5->cpu_ctrl)) { - dev_err(&pdev->dev, "No CPU syscon regmap !\n"); - ret = PTR_ERR(sdhci_sparx5->cpu_ctrl); - goto free_pltfm; - } + if (IS_ERR(sdhci_sparx5->cpu_ctrl)) + return dev_err_probe(&pdev->dev, PTR_ERR(sdhci_sparx5->cpu_ctrl), + "No CPU syscon regmap !\n"); if (sdhci_sparx5->delay_clock >= 0) sparx5_set_delay(host, sdhci_sparx5->delay_clock); @@ -222,7 +218,7 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) - goto free_pltfm; + return ret; /* Set AXI bus master to use un-cached access (for DMA) */ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA) && @@ -235,10 +231,6 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_TYPE)); return ret; - -free_pltfm: - sdhci_pltfm_free(pdev); - return ret; } static const struct of_device_id sdhci_sparx5_of_match[] = { diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 8897839ab2aa..cdb09605e009 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -1257,7 +1257,7 @@ static int sdhci_omap_probe(struct platform_device *pdev) sdhci_get_of_property(pdev); ret = mmc_of_parse(mmc); if (ret) - goto err_pltfm_free; + return ret; soc = soc_device_match(sdhci_omap_soc_devices); if (soc) { @@ -1274,22 +1274,19 @@ static int sdhci_omap_probe(struct platform_device *pdev) mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; pltfm_host->clk = devm_clk_get(dev, "fck"); - if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - goto err_pltfm_free; - } + if (IS_ERR(pltfm_host->clk)) + return PTR_ERR(pltfm_host->clk); ret = clk_set_rate(pltfm_host->clk, mmc->f_max); - if (ret) { - dev_err(dev, "failed to set clock to %d\n", mmc->f_max); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock to %d\n", mmc->f_max); omap_host->pbias = devm_regulator_get_optional(dev, "pbias"); if (IS_ERR(omap_host->pbias)) { ret = PTR_ERR(omap_host->pbias); if (ret != -ENODEV) - goto err_pltfm_free; + return ret; dev_dbg(dev, "unable to get pbias regulator %d\n", ret); } omap_host->pbias_enabled = false; @@ -1373,7 +1370,6 @@ static int sdhci_omap_probe(struct platform_device *pdev) host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; } - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -1382,14 +1378,10 @@ err_cleanup_host: sdhci_cleanup_host(host); err_rpm_put: - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); err_rpm_disable: pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); - -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -1406,7 +1398,6 @@ static void sdhci_omap_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); /* Ensure device gets disabled despite userspace sysfs config */ pm_runtime_force_suspend(dev); - sdhci_pltfm_free(pdev); } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index e3877a1c72a9..826958992dfe 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -152,18 +152,15 @@ static int sdhci_pci_runtime_suspend_host(struct sdhci_pci_chip *chip) { struct sdhci_pci_slot *slot; struct sdhci_host *host; - int i, ret; - for (i = 0; i < chip->num_slots; i++) { + for (int i = 0; i < chip->num_slots; i++) { slot = chip->slots[i]; if (!slot) continue; host = slot->host; - ret = sdhci_runtime_suspend_host(host); - if (ret) - goto err_pci_runtime_suspend; + sdhci_runtime_suspend_host(host); if (chip->rpm_retune && host->tuning_mode != SDHCI_TUNING_MODE_3) @@ -171,26 +168,18 @@ static int sdhci_pci_runtime_suspend_host(struct sdhci_pci_chip *chip) } return 0; - -err_pci_runtime_suspend: - while (--i >= 0) - sdhci_runtime_resume_host(chip->slots[i]->host, 0); - return ret; } static int sdhci_pci_runtime_resume_host(struct sdhci_pci_chip *chip) { struct sdhci_pci_slot *slot; - int i, ret; - for (i = 0; i < chip->num_slots; i++) { + for (int i = 0; i < chip->num_slots; i++) { slot = chip->slots[i]; if (!slot) continue; - ret = sdhci_runtime_resume_host(slot->host, 0); - if (ret) - return ret; + sdhci_runtime_resume_host(slot->host, 0); } return 0; @@ -2174,7 +2163,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( ret = pcim_iomap_regions(pdev, BIT(bar), mmc_hostname(host->mmc)); if (ret) { dev_err(&pdev->dev, "cannot request region\n"); - goto cleanup; + return ERR_PTR(ret); } host->ioaddr = pcim_iomap_table(pdev)[bar]; @@ -2182,7 +2171,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( if (chip->fixes && chip->fixes->probe_slot) { ret = chip->fixes->probe_slot(slot); if (ret) - goto cleanup; + return ERR_PTR(ret); } host->mmc->pm_caps = MMC_PM_KEEP_POWER; @@ -2243,9 +2232,6 @@ remove: if (chip->fixes && chip->fixes->remove_slot) chip->fixes->remove_slot(slot, 0); -cleanup: - sdhci_free_host(host); - return ERR_PTR(ret); } @@ -2266,8 +2252,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) if (slot->chip->fixes && slot->chip->fixes->remove_slot) slot->chip->fixes->remove_slot(slot, dead); - - sdhci_free_host(slot->host); } int sdhci_pci_uhs2_add_host(struct sdhci_pci_slot *slot) diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c index d6a299f49900..7ddac0befed8 100644 --- a/drivers/mmc/host/sdhci-pic32.c +++ b/drivers/mmc/host/sdhci-pic32.c @@ -157,20 +157,20 @@ static int pic32_sdhci_probe(struct platform_device *pdev) ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD, ADMA_FIFO_WR_THSHLD); if (ret) - goto err_host; + goto err; } sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk"); if (IS_ERR(sdhci_pdata->sys_clk)) { ret = PTR_ERR(sdhci_pdata->sys_clk); dev_err(&pdev->dev, "Error getting clock\n"); - goto err_host; + goto err; } ret = clk_prepare_enable(sdhci_pdata->sys_clk); if (ret) { dev_err(&pdev->dev, "Error enabling clock\n"); - goto err_host; + goto err; } sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk"); @@ -203,8 +203,6 @@ err_base_clk: clk_disable_unprepare(sdhci_pdata->base_clk); err_sys_clk: clk_disable_unprepare(sdhci_pdata->sys_clk); -err_host: - sdhci_pltfm_free(pdev); err: dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret); return ret; @@ -220,7 +218,6 @@ static void pic32_sdhci_remove(struct platform_device *pdev) sdhci_remove_host(host, scratch == (u32)~0); clk_disable_unprepare(sdhci_pdata->base_clk); clk_disable_unprepare(sdhci_pdata->sys_clk); - sdhci_pltfm_free(pdev); } static const struct of_device_id pic32_sdhci_id_table[] = { diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 62753d72198a..7f6ac636f040 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -146,20 +146,11 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(sdhci_pltfm_init); -void sdhci_pltfm_free(struct platform_device *pdev) -{ - struct sdhci_host *host = platform_get_drvdata(pdev); - - sdhci_free_host(host); -} -EXPORT_SYMBOL_GPL(sdhci_pltfm_free); - int sdhci_pltfm_init_and_add_host(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata, size_t priv_size) { struct sdhci_host *host; - int ret = 0; host = sdhci_pltfm_init(pdev, pdata, priv_size); if (IS_ERR(host)) @@ -167,11 +158,7 @@ int sdhci_pltfm_init_and_add_host(struct platform_device *pdev, sdhci_get_property(pdev); - ret = sdhci_add_host(host); - if (ret) - sdhci_pltfm_free(pdev); - - return ret; + return sdhci_add_host(host); } EXPORT_SYMBOL_GPL(sdhci_pltfm_init_and_add_host); @@ -181,7 +168,6 @@ void sdhci_pltfm_remove(struct platform_device *pdev) int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); - sdhci_pltfm_free(pdev); } EXPORT_SYMBOL_GPL(sdhci_pltfm_remove); diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index b81d5b0fd616..9c32e8a289d6 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -97,7 +97,6 @@ static inline void sdhci_get_of_property(struct platform_device *pdev) extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata, size_t priv_size); -extern void sdhci_pltfm_free(struct platform_device *pdev); extern int sdhci_pltfm_init_and_add_host(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata, diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 45b6f0891c47..76346353dc55 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -258,7 +258,6 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) struct sdhci_host *host = NULL; const struct sdhci_pxa_variant *variant; - int ret; struct clk *clk, *clk_core; host = sdhci_pltfm_init(pdev, NULL, sizeof(*pxav2_host)); @@ -271,19 +270,14 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) clk = devm_clk_get_optional_enabled(dev, "io"); if (!clk) clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err_probe(dev, ret, "failed to get io clock\n"); - goto free; - } + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get io clock\n"); pltfm_host->clk = clk; clk_core = devm_clk_get_optional_enabled(dev, "core"); - if (IS_ERR(clk_core)) { - ret = PTR_ERR(clk_core); - dev_err_probe(dev, ret, "failed to enable core clock\n"); - goto free; - } + if (IS_ERR(clk_core)) + return dev_err_probe(dev, PTR_ERR(clk_core), + "failed to enable core clock\n"); host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL @@ -332,15 +326,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) pxav2_host->pinctrl = NULL; } - ret = sdhci_add_host(host); - if (ret) - goto free; - - return 0; - -free: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static struct platform_driver sdhci_pxav2_driver = { diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 3fb56face3d8..1371960e34eb 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -389,8 +389,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) pxa->clk_io = devm_clk_get(dev, NULL); if (IS_ERR(pxa->clk_io)) { dev_err(dev, "failed to get io clock\n"); - ret = PTR_ERR(pxa->clk_io); - goto err_clk_get; + return PTR_ERR(pxa->clk_io); } pltfm_host->clk = pxa->clk_io; clk_prepare_enable(pxa->clk_io); @@ -466,8 +465,6 @@ err_of_parse: err_mbus_win: clk_disable_unprepare(pxa->clk_io); clk_disable_unprepare(pxa->clk_core); -err_clk_get: - sdhci_pltfm_free(pdev); return ret; } @@ -485,8 +482,6 @@ static void sdhci_pxav3_remove(struct platform_device *pdev) clk_disable_unprepare(pxa->clk_io); clk_disable_unprepare(pxa->clk_core); - - sdhci_pltfm_free(pdev); } #ifdef CONFIG_PM_SLEEP @@ -499,7 +494,6 @@ static int sdhci_pxav3_suspend(struct device *dev) if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); ret = sdhci_suspend_host(host); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -512,7 +506,6 @@ static int sdhci_pxav3_resume(struct device *dev) pm_runtime_get_sync(dev); ret = sdhci_resume_host(host); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -525,11 +518,8 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host); - int ret; - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -551,7 +541,8 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); - return sdhci_runtime_resume_host(host, 0); + sdhci_runtime_resume_host(host, 0); + return 0; } #endif diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index bdf4dc0d6b77..40857fc2e21b 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -507,15 +507,13 @@ static int sdhci_s3c_probe(struct platform_device *pdev) sc = sdhci_priv(host); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - goto err_pdata_io_clk; - } + if (!pdata) + return -ENOMEM; if (pdev->dev.of_node) { ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); if (ret) - goto err_pdata_io_clk; + return ret; } else { memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); } @@ -532,8 +530,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev) sc->clk_io = devm_clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); - ret = PTR_ERR(sc->clk_io); - goto err_pdata_io_clk; + return PTR_ERR(sc->clk_io); } /* enable the local io clock and keep it running for the moment. */ @@ -661,9 +658,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev) err_no_busclks: clk_disable_unprepare(sc->clk_io); - err_pdata_io_clk: - sdhci_free_host(host); - return ret; } @@ -685,8 +679,6 @@ static void sdhci_s3c_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable_unprepare(sc->clk_io); - - sdhci_free_host(host); } #ifdef CONFIG_PM_SLEEP @@ -714,9 +706,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_s3c *ourhost = to_s3c(host); struct clk *busclk = ourhost->clk_io; - int ret; - ret = sdhci_runtime_suspend_host(host); + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -724,7 +715,7 @@ static int sdhci_s3c_runtime_suspend(struct device *dev) if (ourhost->cur_clk >= 0) clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); clk_disable_unprepare(busclk); - return ret; + return 0; } static int sdhci_s3c_runtime_resume(struct device *dev) @@ -732,13 +723,12 @@ static int sdhci_s3c_runtime_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_s3c *ourhost = to_s3c(host); struct clk *busclk = ourhost->clk_io; - int ret; clk_prepare_enable(busclk); if (ourhost->cur_clk >= 0) clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]); - ret = sdhci_runtime_resume_host(host, 0); - return ret; + sdhci_runtime_resume_host(host, 0); + return 0; } #endif diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 770dc12b9ae9..fa0f8aeb7ee0 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -59,7 +59,7 @@ static int sdhci_probe(struct platform_device *pdev) if (IS_ERR(host->ioaddr)) { ret = PTR_ERR(host->ioaddr); dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret); - goto err_host; + goto err; } host->hw_name = "sdhci"; @@ -67,7 +67,7 @@ static int sdhci_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { ret = host->irq; - goto err_host; + goto err; } host->quirks = SDHCI_QUIRK_BROKEN_ADMA; @@ -78,13 +78,13 @@ static int sdhci_probe(struct platform_device *pdev) if (IS_ERR(sdhci->clk)) { ret = PTR_ERR(sdhci->clk); dev_dbg(&pdev->dev, "Error getting clock\n"); - goto err_host; + goto err; } ret = clk_prepare_enable(sdhci->clk); if (ret) { dev_dbg(&pdev->dev, "Error enabling clock\n"); - goto err_host; + goto err; } ret = clk_set_rate(sdhci->clk, 50000000); @@ -110,8 +110,6 @@ static int sdhci_probe(struct platform_device *pdev) disable_clk: clk_disable_unprepare(sdhci->clk); -err_host: - sdhci_free_host(host); err: dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); return ret; @@ -130,7 +128,6 @@ static void sdhci_remove(struct platform_device *pdev) sdhci_remove_host(host, dead); clk_disable_unprepare(sdhci->clk); - sdhci_free_host(host); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index db5e253b0f79..fe2fe52b23b2 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -764,7 +764,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto pltfm_free; + return ret; if (!mmc_card_is_removable(host->mmc)) host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic; @@ -778,34 +778,26 @@ static int sdhci_sprd_probe(struct platform_device *pdev) if (!IS_ERR(sprd_host->pinctrl)) { sprd_host->pins_uhs = pinctrl_lookup_state(sprd_host->pinctrl, "state_uhs"); - if (IS_ERR(sprd_host->pins_uhs)) { - ret = PTR_ERR(sprd_host->pins_uhs); - goto pltfm_free; - } + if (IS_ERR(sprd_host->pins_uhs)) + return PTR_ERR(sprd_host->pins_uhs); sprd_host->pins_default = pinctrl_lookup_state(sprd_host->pinctrl, "default"); - if (IS_ERR(sprd_host->pins_default)) { - ret = PTR_ERR(sprd_host->pins_default); - goto pltfm_free; - } + if (IS_ERR(sprd_host->pins_default)) + return PTR_ERR(sprd_host->pins_default); } clk = devm_clk_get(&pdev->dev, "sdio"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto pltfm_free; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); sprd_host->clk_sdio = clk; sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio); if (!sprd_host->base_rate) sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE; clk = devm_clk_get(&pdev->dev, "enable"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto pltfm_free; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); sprd_host->clk_enable = clk; clk = devm_clk_get(&pdev->dev, "2x_enable"); @@ -814,7 +806,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev) ret = clk_prepare_enable(sprd_host->clk_sdio); if (ret) - goto pltfm_free; + return ret; ret = clk_prepare_enable(sprd_host->clk_enable); if (ret) @@ -871,7 +863,6 @@ static int sdhci_sprd_probe(struct platform_device *pdev) if (ret) goto err_cleanup_host; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; @@ -891,9 +882,6 @@ clk_disable2: clk_disable: clk_disable_unprepare(sprd_host->clk_sdio); - -pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -907,8 +895,6 @@ static void sdhci_sprd_remove(struct platform_device *pdev) clk_disable_unprepare(sprd_host->clk_sdio); clk_disable_unprepare(sprd_host->clk_enable); clk_disable_unprepare(sprd_host->clk_2x_enable); - - sdhci_pltfm_free(pdev); } static const struct of_device_id sdhci_sprd_of_match[] = { diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index 4973e08a98f8..9157342ff7a4 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -380,13 +380,13 @@ static int sdhci_st_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) { dev_err(&pdev->dev, "Failed mmc_of_parse\n"); - goto err_of; + goto err_pltfm_init; } ret = clk_prepare_enable(clk); if (ret) { dev_err(&pdev->dev, "Failed to prepare clock\n"); - goto err_of; + goto err_pltfm_init; } ret = clk_prepare_enable(icnclk); @@ -423,8 +423,6 @@ err_out: clk_disable_unprepare(icnclk); err_icnclk: clk_disable_unprepare(clk); -err_of: - sdhci_pltfm_free(pdev); err_pltfm_init: reset_control_assert(rstc); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b2f5c3f8b839..c811297185d8 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -1693,7 +1693,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) rc = mmc_of_parse(host->mmc); if (rc) - goto err_parse_dt; + return rc; if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -1739,7 +1739,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) if (IS_ERR(clk)) { rc = PTR_ERR(clk); if (rc == -EPROBE_DEFER) - goto err_power_req; + return rc; dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc); clk = NULL; @@ -1750,7 +1750,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) if (rc) { dev_err(&pdev->dev, "failed to enable tmclk: %d\n", rc); - goto err_power_req; + return rc; } tegra_host->tmclk = clk; @@ -1811,8 +1811,6 @@ err_rst_get: err_clk_get: clk_disable_unprepare(tegra_host->tmclk); err_power_req: -err_parse_dt: - sdhci_pltfm_free(pdev); return rc; } @@ -1831,7 +1829,6 @@ static void sdhci_tegra_remove(struct platform_device *pdev) pm_runtime_force_suspend(&pdev->dev); clk_disable_unprepare(tegra_host->tmclk); - sdhci_pltfm_free(pdev); } static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev) diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c index 098f0ea45cbe..b12bee8342bd 100644 --- a/drivers/mmc/host/sdhci-xenon.c +++ b/drivers/mmc/host/sdhci-xenon.c @@ -532,14 +532,13 @@ static int xenon_probe(struct platform_device *pdev) if (dev->of_node) { pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(pltfm_host->clk)) { - err = PTR_ERR(pltfm_host->clk); - dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err); - goto free_pltfm; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pltfm_host->clk), + "Failed to setup input clk.\n"); + err = clk_prepare_enable(pltfm_host->clk); if (err) - goto free_pltfm; + return err; priv->axi_clk = devm_clk_get(&pdev->dev, "axi"); if (IS_ERR(priv->axi_clk)) { @@ -603,8 +602,6 @@ err_clk_axi: clk_disable_unprepare(priv->axi_clk); err_clk: clk_disable_unprepare(pltfm_host->clk); -free_pltfm: - sdhci_pltfm_free(pdev); return err; } @@ -623,8 +620,6 @@ static void xenon_remove(struct platform_device *pdev) xenon_sdhc_unprepare(host); clk_disable_unprepare(priv->axi_clk); clk_disable_unprepare(pltfm_host->clk); - - sdhci_pltfm_free(pdev); } #ifdef CONFIG_PM_SLEEP @@ -648,11 +643,8 @@ static int xenon_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); - int ret; - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -687,9 +679,7 @@ static int xenon_runtime_resume(struct device *dev) priv->restore_needed = false; } - ret = sdhci_runtime_resume_host(host, 0); - if (ret) - goto out; + sdhci_runtime_resume_host(host, 0); return 0; out: clk_disable_unprepare(pltfm_host->clk); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e116f2db34d5..3a17821efa5c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3858,7 +3858,7 @@ int sdhci_resume_host(struct sdhci_host *host) EXPORT_SYMBOL_GPL(sdhci_resume_host); -int sdhci_runtime_suspend_host(struct sdhci_host *host) +void sdhci_runtime_suspend_host(struct sdhci_host *host) { unsigned long flags; @@ -3875,12 +3875,10 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = true; spin_unlock_irqrestore(&host->lock, flags); - - return 0; } EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host); -int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) +void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) { struct mmc_host *mmc = host->mmc; unsigned long flags; @@ -3926,8 +3924,6 @@ int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) sdhci_enable_card_detection(host); spin_unlock_irqrestore(&host->lock, flags); - - return 0; } EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host); @@ -4071,7 +4067,7 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, WARN_ON(dev == NULL); - mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev); + mmc = devm_mmc_alloc_host(dev, sizeof(struct sdhci_host) + priv_size); if (!mmc) return ERR_PTR(-ENOMEM); @@ -4995,13 +4991,6 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) EXPORT_SYMBOL_GPL(sdhci_remove_host); -void sdhci_free_host(struct sdhci_host *host) -{ - mmc_free_host(host->mmc); -} - -EXPORT_SYMBOL_GPL(sdhci_free_host); - /*****************************************************************************\ * * * Driver init/exit * diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 70ada1857a4c..58fcbeaf281e 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -811,7 +811,6 @@ static inline u8 sdhci_readb(struct sdhci_host *host, int reg) #endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */ struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size); -void sdhci_free_host(struct sdhci_host *host); static inline void *sdhci_priv(struct sdhci_host *host) { @@ -879,8 +878,8 @@ bool sdhci_enable_irq_wakeups(struct sdhci_host *host); void sdhci_disable_irq_wakeups(struct sdhci_host *host); int sdhci_suspend_host(struct sdhci_host *host); int sdhci_resume_host(struct sdhci_host *host); -int sdhci_runtime_suspend_host(struct sdhci_host *host); -int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset); +void sdhci_runtime_suspend_host(struct sdhci_host *host); +void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset); #endif void sdhci_cqe_enable(struct mmc_host *mmc); diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 9e94998e8df7..e4fc345be7e5 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -945,35 +945,30 @@ static int sdhci_am654_probe(struct platform_device *pdev) clk_xin = devm_clk_get(dev, "clk_xin"); if (IS_ERR(clk_xin)) { dev_err(dev, "clk_xin clock not found.\n"); - ret = PTR_ERR(clk_xin); - goto err_pltfm_free; + return PTR_ERR(clk_xin); } pltfm_host->clk = clk_xin; base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto err_pltfm_free; + return PTR_ERR(base); } sdhci_am654->base = devm_regmap_init_mmio(dev, base, &sdhci_am654_regmap_config); if (IS_ERR(sdhci_am654->base)) { dev_err(dev, "Failed to initialize regmap\n"); - ret = PTR_ERR(sdhci_am654->base); - goto err_pltfm_free; + return PTR_ERR(sdhci_am654->base); } ret = sdhci_am654_get_of_property(pdev, sdhci_am654); if (ret) - goto err_pltfm_free; + return ret; ret = mmc_of_parse(host->mmc); - if (ret) { - dev_err_probe(dev, ret, "parsing dt failed\n"); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(dev, ret, "parsing dt failed\n"); host->mmc_host_ops.start_signal_voltage_switch = sdhci_am654_start_signal_voltage_switch; host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning; @@ -994,7 +989,6 @@ static int sdhci_am654_probe(struct platform_device *pdev) /* Setting up autosuspend */ pm_runtime_set_autosuspend_delay(dev, SDHCI_AM654_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -1004,8 +998,6 @@ pm_disable: pm_runtime_disable(dev); pm_put: pm_runtime_put_noidle(dev); -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -1024,7 +1016,6 @@ static void sdhci_am654_remove(struct platform_device *pdev) clk_disable_unprepare(pltfm_host->clk); pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - sdhci_pltfm_free(pdev); } #ifdef CONFIG_PM @@ -1085,9 +1076,7 @@ static int sdhci_am654_runtime_suspend(struct device *dev) if (ret) return ret; - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); /* disable the clock */ clk_disable_unprepare(pltfm_host->clk); @@ -1109,9 +1098,7 @@ static int sdhci_am654_runtime_resume(struct device *dev) if (ret) return ret; - ret = sdhci_runtime_resume_host(host, 0); - if (ret) - return ret; + sdhci_runtime_resume_host(host, 0); ret = cqhci_resume(host->mmc); if (ret) diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index ee66e4f3683d..e9b934e159ad 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -133,20 +133,18 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; if (dev_of_node(dev)) { sdhci_get_of_property(pdev); priv->clk_iface = devm_clk_get(&pdev->dev, "iface"); - if (IS_ERR(priv->clk_iface)) { - ret = PTR_ERR(priv->clk_iface); - goto err; - } + if (IS_ERR(priv->clk_iface)) + return PTR_ERR(priv->clk_iface); ret = clk_prepare_enable(priv->clk_iface); if (ret) - goto err; + return ret; priv->clk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(priv->clk)) { @@ -200,9 +198,6 @@ err_rst: clk_disable_unprepare(priv->clk); err_clk: clk_disable_unprepare(priv->clk_iface); -err: - sdhci_pltfm_free(pdev); - return ret; } diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 57b8c1a96756..481cb552c2b4 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -403,9 +403,9 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, } /* allocate privdata */ mmc = pcmcia_dev->priv = - mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev); + devm_mmc_alloc_host(&pcmcia_dev->dev, sizeof(*host)); if (!mmc) { - dev_err(dev, "mmc_alloc_host failed\n"); + dev_err(dev, "devm_mmc_alloc_host failed\n"); result = -ENOMEM; goto unmap_io; } @@ -431,7 +431,7 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, if (sdricoh_reset(host)) { dev_dbg(dev, "could not reset\n"); result = -EIO; - goto free_host; + goto unmap_io; } result = mmc_add_host(mmc); @@ -440,8 +440,6 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, dev_dbg(dev, "mmc host registered\n"); return 0; } -free_host: - mmc_free_host(mmc); unmap_io: pci_iounmap(pci_dev, iobase); return result; @@ -483,10 +481,8 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link) mmc_remove_host(mmc); pci_iounmap(host->pci_dev, host->iobase); pci_dev_put(host->pci_dev); - mmc_free_host(mmc); } pcmcia_disable_device(link); - } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index ce60cec26b98..19f84584ecfa 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1444,13 +1444,13 @@ static int sh_mmcif_probe(struct platform_device *pdev) if (IS_ERR(reg)) return PTR_ERR(reg); - mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; ret = mmc_of_parse(mmc); if (ret < 0) - goto err_host; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -1481,15 +1481,13 @@ static int sh_mmcif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); host->clk = devm_clk_get(dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - dev_err(dev, "cannot get clock: %d\n", ret); - goto err_host; - } + if (IS_ERR(host->clk)) + return dev_err_probe(dev, PTR_ERR(host->clk), + "cannot get clock\n"); ret = clk_prepare_enable(host->clk); if (ret < 0) - goto err_host; + return ret; sh_mmcif_clk_setup(host); @@ -1542,8 +1540,6 @@ err_clk: clk_disable_unprepare(host->clk); pm_runtime_put_sync(dev); pm_runtime_disable(dev); -err_host: - mmc_free_host(mmc); return ret; } @@ -1568,7 +1564,6 @@ static void sh_mmcif_remove(struct platform_device *pdev) cancel_delayed_work_sync(&host->timeout_work); clk_disable_unprepare(host->clk); - mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); } diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 1508eead5d01..ee4a65b0a22d 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1369,11 +1369,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev) struct mmc_host *mmc; int ret; - mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev); - if (!mmc) { - dev_err(&pdev->dev, "mmc alloc host failed\n"); - return -ENOMEM; - } + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); + if (!mmc) + return dev_err_probe(&pdev->dev, -ENOMEM, + "mmc alloc host failed\n"); platform_set_drvdata(pdev, mmc); host = mmc_priv(mmc); @@ -1383,15 +1382,13 @@ static int sunxi_mmc_probe(struct platform_device *pdev) ret = sunxi_mmc_resource_request(host, pdev); if (ret) - goto error_free_host; + return ret; host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); - if (!host->sg_cpu) { - dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n"); - ret = -ENOMEM; - goto error_free_host; - } + if (!host->sg_cpu) + return dev_err_probe(&pdev->dev, -ENOMEM, + "Failed to allocate DMA descriptor mem\n"); if (host->cfg->ccu_has_timings_switch) { /* @@ -1481,8 +1478,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev) error_free_dma: dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); -error_free_host: - mmc_free_host(mmc); return ret; } @@ -1498,7 +1493,6 @@ static void sunxi_mmc_remove(struct platform_device *pdev) sunxi_mmc_disable(host); } dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); - mmc_free_host(mmc); } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 5e5ec92f80e6..ac636efd911d 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -947,7 +947,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) return rc; } - mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); + mmc = devm_mmc_alloc_host(&sock->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -982,10 +982,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) if (!rc) rc = mmc_add_host(mmc); - if (!rc) - return 0; - mmc_free_host(mmc); return rc; } @@ -1015,8 +1012,6 @@ static void tifm_sd_remove(struct tifm_dev *sock) spin_unlock_irqrestore(&sock->lock, flags); mmc_remove_host(mmc); dev_dbg(&sock->dev, "after remove\n"); - - mmc_free_host(mmc); } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 41787ea77a13..d730b7633ae1 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -193,13 +193,13 @@ struct tmio_mmc_host { bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq); void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq); unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host); + void (*sdio_irq)(struct tmio_mmc_host *host); const struct tmio_mmc_dma_ops *dma_ops; }; struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev, struct tmio_mmc_data *pdata); -void tmio_mmc_host_free(struct tmio_mmc_host *host); int tmio_mmc_host_probe(struct tmio_mmc_host *host); void tmio_mmc_host_remove(struct tmio_mmc_host *host); void tmio_mmc_do_data_irq(struct tmio_mmc_host *host); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index b71241f55df5..21c2f9095bac 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -160,7 +160,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); host->sdio_irq_enabled = false; - pm_runtime_mark_last_busy(mmc_dev(mmc)); pm_runtime_put_autosuspend(mmc_dev(mmc)); } } @@ -696,8 +695,11 @@ static bool __tmio_mmc_sdio_irq(struct tmio_mmc_host *host) sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status); - if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) + if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) { + if (host->sdio_irq) + host->sdio_irq(host); mmc_signal_sdio_irq(mmc); + } return ireg; } @@ -1097,7 +1099,7 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev, if (IS_ERR(ctl)) return ERR_CAST(ctl); - mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return ERR_PTR(-ENOMEM); @@ -1110,29 +1112,17 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev, mmc->ops = &host->ops; ret = mmc_of_parse(host->mmc); - if (ret) { - host = ERR_PTR(ret); - goto free; - } + if (ret) + return ERR_PTR(ret); tmio_mmc_of_parse(pdev, mmc); platform_set_drvdata(pdev, host); - return host; -free: - mmc_free_host(mmc); - return host; } EXPORT_SYMBOL_GPL(tmio_mmc_host_alloc); -void tmio_mmc_host_free(struct tmio_mmc_host *host) -{ - mmc_free_host(host->mmc); -} -EXPORT_SYMBOL_GPL(tmio_mmc_host_free); - int tmio_mmc_host_probe(struct tmio_mmc_host *_host) { struct platform_device *pdev = _host->pdev; diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c index 497791ffada6..e5f7f8abafc0 100644 --- a/drivers/mmc/host/toshsd.c +++ b/drivers/mmc/host/toshsd.c @@ -612,7 +612,7 @@ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) return ret; - mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { ret = -ENOMEM; goto err; @@ -669,7 +669,6 @@ unmap: release: pci_release_regions(pdev); free: - mmc_free_host(mmc); pci_set_drvdata(pdev, NULL); err: pci_disable_device(pdev); @@ -685,7 +684,6 @@ static void toshsd_remove(struct pci_dev *pdev) free_irq(pdev->irq, host); pci_iounmap(pdev, host->ioaddr); pci_release_regions(pdev); - mmc_free_host(host->mmc); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index 4ad02cfdc238..1eae2f4b6c1f 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -663,8 +663,7 @@ static int uniphier_sd_probe(struct platform_device *pdev) priv->rst_hw = devm_reset_control_get_exclusive(dev, "hw"); if (IS_ERR(priv->rst_hw)) { dev_err(dev, "failed to get hw reset\n"); - ret = PTR_ERR(priv->rst_hw); - goto free_host; + return PTR_ERR(priv->rst_hw); } host->ops.card_hw_reset = uniphier_sd_hw_reset; } @@ -694,7 +693,7 @@ static int uniphier_sd_probe(struct platform_device *pdev) ret = uniphier_sd_clk_enable(host); if (ret) - goto free_host; + return ret; uniphier_sd_host_init(host); @@ -720,8 +719,6 @@ static int uniphier_sd_probe(struct platform_device *pdev) disable_clk: uniphier_sd_clk_disable(host); -free_host: - tmio_mmc_host_free(host); return ret; } @@ -732,7 +729,6 @@ static void uniphier_sd_remove(struct platform_device *pdev) tmio_mmc_host_remove(host); uniphier_sd_clk_disable(host); - tmio_mmc_host_free(host); } static const struct of_device_id uniphier_sd_match[] = { diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 49efb960a052..85b49c07918b 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1762,17 +1762,17 @@ static int usdhi6_probe(struct platform_device *pdev) if (irq_sdio < 0) return irq_sdio; - mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; ret = mmc_regulator_get_supply(mmc); if (ret) - goto e_free_mmc; + return ret; ret = mmc_of_parse(mmc); if (ret < 0) - goto e_free_mmc; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -1785,30 +1785,24 @@ static int usdhi6_probe(struct platform_device *pdev) mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS; host->pinctrl = devm_pinctrl_get(&pdev->dev); - if (IS_ERR(host->pinctrl)) { - ret = PTR_ERR(host->pinctrl); - goto e_free_mmc; - } + if (IS_ERR(host->pinctrl)) + return PTR_ERR(host->pinctrl); host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs"); host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto e_free_mmc; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->clk = devm_clk_get(dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto e_free_mmc; - } + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); host->imclk = clk_get_rate(host->clk); ret = clk_prepare_enable(host->clk); if (ret < 0) - goto e_free_mmc; + return ret; version = usdhi6_read(host, USDHI6_VERSION); if ((version & 0xfff) != 0xa0d) { @@ -1878,9 +1872,6 @@ e_release_dma: usdhi6_dma_release(host); e_clk_off: clk_disable_unprepare(host->clk); -e_free_mmc: - mmc_free_host(mmc); - return ret; } @@ -1894,7 +1885,6 @@ static void usdhi6_remove(struct platform_device *pdev) cancel_delayed_work_sync(&host->timeout_work); usdhi6_dma_release(host); clk_disable_unprepare(host->clk); - mmc_free_host(host->mmc); } static struct platform_driver usdhi6_driver = { diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index 9a6358fd9512..2b7456e942f7 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -404,8 +404,6 @@ static void ushc_clean_up(struct ushc_data *ushc) kfree(ushc->int_data); kfree(ushc->cbw); kfree(ushc->csw); - - mmc_free_host(ushc->mmc); } static const struct mmc_host_ops ushc_ops = { @@ -425,7 +423,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id if (intf->cur_altsetting->desc.bNumEndpoints < 1) return -ENODEV; - mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); + mmc = devm_mmc_alloc_host(&intf->dev, sizeof(*ushc)); if (mmc == NULL) return -ENOMEM; ushc = mmc_priv(mmc); diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 9903966c2f54..3bd49f64899d 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1100,7 +1100,7 @@ static int via_sd_probe(struct pci_dev *pcidev, pci_write_config_byte(pcidev, VIA_CRDR_PCI_WORK_MODE, 0); pci_write_config_byte(pcidev, VIA_CRDR_PCI_DBG_MODE, 0); - mmc = mmc_alloc_host(sizeof(struct via_crdr_mmc_host), &pcidev->dev); + mmc = devm_mmc_alloc_host(&pcidev->dev, sizeof(*sdhost)); if (!mmc) { ret = -ENOMEM; goto release; @@ -1115,7 +1115,7 @@ static int via_sd_probe(struct pci_dev *pcidev, sdhost->mmiobase = ioremap(base, len); if (!sdhost->mmiobase) { ret = -ENOMEM; - goto free_mmc_host; + goto release; } sdhost->sdhc_mmiobase = @@ -1160,8 +1160,6 @@ static int via_sd_probe(struct pci_dev *pcidev, unmap: iounmap(sdhost->mmiobase); -free_mmc_host: - mmc_free_host(mmc); release: pci_release_regions(pcidev); disable: @@ -1212,7 +1210,6 @@ static void via_sd_remove(struct pci_dev *pcidev) writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); iounmap(sdhost->mmiobase); - mmc_free_host(sdhost->mmc); pci_release_regions(pcidev); pci_disable_device(pcidev); diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index f498fe11ecdf..ff49d0770506 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -369,13 +369,11 @@ struct vub300_mmc_host { static void vub300_delete(struct kref *kref) { /* kref callback - softirq */ struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref); - struct mmc_host *mmc = vub300->mmc; usb_free_urb(vub300->command_out_urb); vub300->command_out_urb = NULL; usb_free_urb(vub300->command_res_urb); vub300->command_res_urb = NULL; usb_put_dev(vub300->udev); - mmc_free_host(mmc); /* * and hence also frees vub300 * which is contained at the end of struct mmc @@ -2114,7 +2112,7 @@ static int vub300_probe(struct usb_interface *interface, goto error1; } /* this also allocates memory for our VUB300 mmc host device */ - mmc = mmc_alloc_host(sizeof(struct vub300_mmc_host), &udev->dev); + mmc = devm_mmc_alloc_host(&udev->dev, sizeof(*vub300)); if (!mmc) { retval = -ENOMEM; dev_err(&udev->dev, "not enough memory for the mmc_host\n"); @@ -2271,7 +2269,7 @@ static int vub300_probe(struct usb_interface *interface, dev_err(&vub300->udev->dev, "Could not find two sets of bulk-in/out endpoint pairs\n"); retval = -EINVAL; - goto error5; + goto error4; } retval = usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), @@ -2280,14 +2278,14 @@ static int vub300_probe(struct usb_interface *interface, 0x0000, 0x0000, &vub300->hc_info, sizeof(vub300->hc_info), 1000); if (retval < 0) - goto error5; + goto error4; retval = usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), SET_ROM_WAIT_STATES, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, firmware_rom_wait_states, 0x0000, NULL, 0, 1000); if (retval < 0) - goto error5; + goto error4; dev_info(&vub300->udev->dev, "operating_mode = %s %s %d MHz %s %d byte USB packets\n", (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL", @@ -2302,7 +2300,7 @@ static int vub300_probe(struct usb_interface *interface, 0x0000, 0x0000, &vub300->system_port_status, sizeof(vub300->system_port_status), 1000); if (retval < 0) { - goto error5; + goto error4; } else if (sizeof(vub300->system_port_status) == retval) { vub300->card_present = (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; @@ -2310,7 +2308,7 @@ static int vub300_probe(struct usb_interface *interface, (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; } else { retval = -EINVAL; - goto error5; + goto error4; } usb_set_intfdata(interface, vub300); INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread); @@ -2340,8 +2338,6 @@ static int vub300_probe(struct usb_interface *interface, return 0; error6: timer_delete_sync(&vub300->inactivity_timer); -error5: - mmc_free_host(mmc); /* * and hence also frees vub300 * which is contained at the end of struct mmc diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 2ae787d966de..c33a0223ce7f 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1190,7 +1190,7 @@ static int wbsd_alloc_mmc(struct device *dev) /* * Allocate MMC structure. */ - mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1262,8 +1262,6 @@ static void wbsd_free_mmc(struct device *dev) BUG_ON(host == NULL); timer_delete_sync(&host->ignore_timer); - - mmc_free_host(mmc); } /* diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index cdb36a9f9e38..0d2929cfe397 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -774,7 +774,7 @@ static int wmt_mci_probe(struct platform_device *pdev) goto fail1; } - mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*priv)); if (!mmc) { dev_err(&pdev->dev, "Failed to allocate mmc_host\n"); ret = -ENOMEM; @@ -808,7 +808,7 @@ static int wmt_mci_probe(struct platform_device *pdev) if (!priv->sdmmc_base) { dev_err(&pdev->dev, "Failed to map IO space\n"); ret = -ENOMEM; - goto fail2; + goto fail1; } priv->irq_regular = regular_irq; @@ -873,8 +873,6 @@ fail4: free_irq(regular_irq, priv); fail3: iounmap(priv->sdmmc_base); -fail2: - mmc_free_host(mmc); fail1: return ret; } @@ -910,8 +908,6 @@ static void wmt_mci_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk_sdmmc); clk_put(priv->clk_sdmmc); - mmc_free_host(mmc); - dev_info(&pdev->dev, "WMT MCI device removed\n"); } diff --git a/drivers/mtd/spi-nor/sysfs.c b/drivers/mtd/spi-nor/sysfs.c index 4f12ff755df0..643513ee891b 100644 --- a/drivers/mtd/spi-nor/sysfs.c +++ b/drivers/mtd/spi-nor/sysfs.c @@ -104,7 +104,7 @@ static const struct attribute_group spi_nor_sysfs_group = { .is_visible = spi_nor_sysfs_is_visible, .is_bin_visible = spi_nor_sysfs_is_bin_visible, .attrs = spi_nor_sysfs_entries, - .bin_attrs_new = spi_nor_sysfs_bin_entries, + .bin_attrs = spi_nor_sysfs_bin_entries, }; const struct attribute_group *spi_nor_sysfs_groups[] = { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index d8b735ccf899..d843d1e19715 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -77,7 +77,7 @@ DEFINE_SHOW_ATTRIBUTE(nfp_rx_q); static int nfp_tx_q_show(struct seq_file *file, void *data); DEFINE_SHOW_ATTRIBUTE(nfp_tx_q); -static int nfp_tx_q_show(struct seq_file *file, void *data) +static int __nfp_tx_q_show(struct seq_file *file, void *data, bool is_xdp) { struct nfp_net_r_vector *r_vec = file->private; struct nfp_net_tx_ring *tx_ring; @@ -86,10 +86,10 @@ static int nfp_tx_q_show(struct seq_file *file, void *data) rtnl_lock(); - if (debugfs_real_fops(file->file) == &nfp_tx_q_fops) - tx_ring = r_vec->tx_ring; - else + if (is_xdp) tx_ring = r_vec->xdp_ring; + else + tx_ring = r_vec->tx_ring; if (!r_vec->nfp_net || !tx_ring) goto out; nn = r_vec->nfp_net; @@ -115,9 +115,14 @@ out: return 0; } +static int nfp_tx_q_show(struct seq_file *file, void *data) +{ + return __nfp_tx_q_show(file, data, false); +} + static int nfp_xdp_q_show(struct seq_file *file, void *data) { - return nfp_tx_q_show(file, data); + return __nfp_tx_q_show(file, data, true); } DEFINE_SHOW_ATTRIBUTE(nfp_xdp_q); diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 3383ee1dad14..e8ff661fa4a5 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -2946,15 +2946,15 @@ static ssize_t netxen_sysfs_write_mem(struct file *filp, struct kobject *kobj, static const struct bin_attribute bin_attr_crb = { .attr = { .name = "crb", .mode = 0644 }, .size = 0, - .read_new = netxen_sysfs_read_crb, - .write_new = netxen_sysfs_write_crb, + .read = netxen_sysfs_read_crb, + .write = netxen_sysfs_write_crb, }; static const struct bin_attribute bin_attr_mem = { .attr = { .name = "mem", .mode = 0644 }, .size = 0, - .read_new = netxen_sysfs_read_mem, - .write_new = netxen_sysfs_write_mem, + .read = netxen_sysfs_read_mem, + .write = netxen_sysfs_write_mem, }; static ssize_t @@ -3082,7 +3082,7 @@ out: static const struct bin_attribute bin_attr_dimm = { .attr = { .name = "dimm", .mode = 0644 }, .size = sizeof(struct netxen_dimm_cfg), - .read_new = netxen_sysfs_read_dimm, + .read = netxen_sysfs_read_dimm, }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index c0f20464fd1e..5296d9a6ee83 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -1195,63 +1195,63 @@ static const struct device_attribute dev_attr_beacon = { static const struct bin_attribute bin_attr_crb = { .attr = { .name = "crb", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_read_crb, - .write_new = qlcnic_sysfs_write_crb, + .read = qlcnic_sysfs_read_crb, + .write = qlcnic_sysfs_write_crb, }; static const struct bin_attribute bin_attr_mem = { .attr = { .name = "mem", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_read_mem, - .write_new = qlcnic_sysfs_write_mem, + .read = qlcnic_sysfs_read_mem, + .write = qlcnic_sysfs_write_mem, }; static const struct bin_attribute bin_attr_npar_config = { .attr = { .name = "npar_config", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_read_npar_config, - .write_new = qlcnic_sysfs_write_npar_config, + .read = qlcnic_sysfs_read_npar_config, + .write = qlcnic_sysfs_write_npar_config, }; static const struct bin_attribute bin_attr_pci_config = { .attr = { .name = "pci_config", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_read_pci_config, + .read = qlcnic_sysfs_read_pci_config, }; static const struct bin_attribute bin_attr_port_stats = { .attr = { .name = "port_stats", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_get_port_stats, - .write_new = qlcnic_sysfs_clear_port_stats, + .read = qlcnic_sysfs_get_port_stats, + .write = qlcnic_sysfs_clear_port_stats, }; static const struct bin_attribute bin_attr_esw_stats = { .attr = { .name = "esw_stats", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_get_esw_stats, - .write_new = qlcnic_sysfs_clear_esw_stats, + .read = qlcnic_sysfs_get_esw_stats, + .write = qlcnic_sysfs_clear_esw_stats, }; static const struct bin_attribute bin_attr_esw_config = { .attr = { .name = "esw_config", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_read_esw_config, - .write_new = qlcnic_sysfs_write_esw_config, + .read = qlcnic_sysfs_read_esw_config, + .write = qlcnic_sysfs_write_esw_config, }; static const struct bin_attribute bin_attr_pm_config = { .attr = { .name = "pm_config", .mode = 0644 }, .size = 0, - .read_new = qlcnic_sysfs_read_pm_config, - .write_new = qlcnic_sysfs_write_pm_config, + .read = qlcnic_sysfs_read_pm_config, + .write = qlcnic_sysfs_write_pm_config, }; static const struct bin_attribute bin_attr_flash = { .attr = { .name = "flash", .mode = 0644 }, .size = 0, - .read_new = qlcnic_83xx_sysfs_flash_read_handler, - .write_new = qlcnic_83xx_sysfs_flash_write_handler, + .read = qlcnic_83xx_sysfs_flash_read_handler, + .write = qlcnic_83xx_sysfs_flash_write_handler, }; #ifdef CONFIG_QLCNIC_HWMON diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index 076a370be849..d135b061d810 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -401,8 +401,8 @@ static const struct bin_attribute ks8995_registers_attr = { .mode = 0600, }, .size = KS8995_REGS_SIZE, - .read_new = ks8995_registers_read, - .write_new = ks8995_registers_write, + .read = ks8995_registers_read, + .write = ks8995_registers_write, }; /* ------------------------------------------------------------------------ */ diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c index 7c57d4c8744a..65ca5dc569a0 100644 --- a/drivers/net/wireless/ti/wlcore/sysfs.c +++ b/drivers/net/wireless/ti/wlcore/sysfs.c @@ -121,7 +121,7 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, static const struct bin_attribute fwlog_attr = { .attr = { .name = "fwlog", .mode = 0400 }, - .read_new = wl1271_sysfs_read_fwlog, + .read = wl1271_sysfs_read_fwlog, }; int wlcore_sysfs_init(struct wl1271 *wl) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 817f55f3a19e..387c88c55259 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -376,8 +376,8 @@ static const struct bin_attribute bin_attr_rw_nvmem = { .name = "nvmem", .mode = 0644, }, - .read_new = bin_attr_nvmem_read, - .write_new = bin_attr_nvmem_write, + .read = bin_attr_nvmem_read, + .write = bin_attr_nvmem_write, }; static const struct bin_attribute *const nvmem_bin_attributes[] = { @@ -386,7 +386,7 @@ static const struct bin_attribute *const nvmem_bin_attributes[] = { }; static const struct attribute_group nvmem_bin_group = { - .bin_attrs_new = nvmem_bin_attributes, + .bin_attrs = nvmem_bin_attributes, .attrs = nvmem_attrs, .is_bin_visible = nvmem_bin_attr_is_visible, .bin_size = nvmem_bin_attr_size, @@ -402,8 +402,8 @@ static const struct bin_attribute bin_attr_nvmem_eeprom_compat = { .attr = { .name = "eeprom", }, - .read_new = bin_attr_nvmem_read, - .write_new = bin_attr_nvmem_write, + .read = bin_attr_nvmem_read, + .write = bin_attr_nvmem_write, }; /* @@ -492,7 +492,7 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) entry->bit_offset); attrs[i].attr.mode = 0444 & nvmem_bin_attr_get_umode(nvmem); attrs[i].size = entry->bytes; - attrs[i].read_new = &nvmem_cell_attr_read; + attrs[i].read = &nvmem_cell_attr_read; attrs[i].private = entry; if (!attrs[i].attr.name) { ret = -ENOMEM; @@ -503,7 +503,7 @@ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) i++; } - group.bin_attrs_new = pattrs; + group.bin_attrs = pattrs; ret = device_add_group(&nvmem->dev, &group); if (ret) diff --git a/drivers/of/kobj.c b/drivers/of/kobj.c index aa887166f0d2..1bb61a2c3399 100644 --- a/drivers/of/kobj.c +++ b/drivers/of/kobj.c @@ -77,7 +77,7 @@ int __of_add_property_sysfs(struct device_node *np, struct property *pp) pp->attr.attr.name = safe_name(&np->kobj, pp->name); pp->attr.attr.mode = secure ? 0400 : 0444; pp->attr.size = secure ? 0 : pp->length; - pp->attr.read_new = of_node_property_read; + pp->attr.read = of_node_property_read; rc = sysfs_create_bin_file(&np->kobj, &pp->attr); WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np); diff --git a/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unittest-data/tests-platform.dtsi index 4171f43cf01c..59aa2a9731a7 100644 --- a/drivers/of/unittest-data/tests-platform.dtsi +++ b/drivers/of/unittest-data/tests-platform.dtsi @@ -37,6 +37,16 @@ test-device@2 { compatible = "test,rust-device"; reg = <0x2>; + + test,u32-prop = <0xdeadbeef>; + test,i16-array = /bits/ 16 <1 2 (-3) (-4)>; + + ref_child_0: child-0 { + test,ref-arg = <&ref_child_1 0x20 0x32>; + }; + ref_child_1: child-1 { + test,ref-arg = <&ref_child_0 0x10 0x64>; + }; }; }; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index eeb370e0f507..e3503ec20f6c 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1856,6 +1856,8 @@ static void __init of_unittest_platform_populate(void) of_platform_populate(np, match, NULL, &test_bus->dev); for_each_child_of_node(np, child) { for_each_child_of_node(child, grandchild) { + if (!of_property_present(grandchild, "compatible")) + continue; pdev = of_find_device_by_node(grandchild); unittest(pdev, "Could not create device for node '%pOFn'\n", diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index b3aa34e3a4a2..18e01cd55a8e 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -98,7 +98,7 @@ static struct bin_attribute ibm_apci_table_attr __ro_after_init = { .name = "apci_table", .mode = S_IRUGO, }, - .read_new = ibm_read_apci_table, + .read = ibm_read_apci_table, .write = NULL, }; static struct acpiphp_attention_info ibm_attention_info = diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index d686488f4111..8d8de0ad2bb7 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -113,7 +113,8 @@ static int pci_setup_msi_context(struct pci_dev *dev) void pci_msi_update_mask(struct msi_desc *desc, u32 clear, u32 set) { - raw_spinlock_t *lock = &to_pci_dev(desc->dev)->msi_lock; + struct pci_dev *dev = msi_desc_to_pci_dev(desc); + raw_spinlock_t *lock = &dev->msi_lock; unsigned long flags; if (!desc->pci.msi_attrib.can_mask) @@ -122,8 +123,7 @@ void pci_msi_update_mask(struct msi_desc *desc, u32 clear, u32 set) raw_spin_lock_irqsave(lock, flags); desc->pci.msi_mask &= ~clear; desc->pci.msi_mask |= set; - pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->pci.mask_pos, - desc->pci.msi_mask); + pci_write_config_dword(dev, desc->pci.mask_pos, desc->pci.msi_mask); raw_spin_unlock_irqrestore(lock, flags); } diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 8d955c25aed3..da5657a02007 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -196,7 +196,7 @@ static const struct bin_attribute *const p2pmem_bin_attrs[] = { static const struct attribute_group p2pmem_group = { .attrs = p2pmem_attrs, - .bin_attrs_new = p2pmem_bin_attrs, + .bin_attrs = p2pmem_bin_attrs, .name = "p2pmem", }; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 268c69daa4d5..5eea14c1f7f5 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -857,7 +857,7 @@ static size_t pci_dev_config_attr_bin_size(struct kobject *kobj, } static const struct attribute_group pci_dev_config_attr_group = { - .bin_attrs_new = pci_dev_config_attrs, + .bin_attrs = pci_dev_config_attrs, .bin_size = pci_dev_config_attr_bin_size, }; @@ -1004,8 +1004,8 @@ void pci_create_legacy_files(struct pci_bus *b) b->legacy_io->attr.name = "legacy_io"; b->legacy_io->size = 0xffff; b->legacy_io->attr.mode = 0600; - b->legacy_io->read_new = pci_read_legacy_io; - b->legacy_io->write_new = pci_write_legacy_io; + b->legacy_io->read = pci_read_legacy_io; + b->legacy_io->write = pci_write_legacy_io; /* See pci_create_attr() for motivation */ b->legacy_io->llseek = pci_llseek_resource; b->legacy_io->mmap = pci_mmap_legacy_io; @@ -1211,8 +1211,8 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) } else { sprintf(res_attr_name, "resource%d", num); if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { - res_attr->read_new = pci_read_resource_io; - res_attr->write_new = pci_write_resource_io; + res_attr->read = pci_read_resource_io; + res_attr->write = pci_write_resource_io; if (arch_can_pci_mmap_io()) res_attr->mmap = pci_mmap_resource_uc; } else { @@ -1377,7 +1377,7 @@ static size_t pci_dev_rom_attr_bin_size(struct kobject *kobj, } static const struct attribute_group pci_dev_rom_attr_group = { - .bin_attrs_new = pci_dev_rom_attrs, + .bin_attrs = pci_dev_rom_attrs, .is_bin_visible = pci_dev_rom_attr_is_visible, .bin_size = pci_dev_rom_attr_bin_size, }; diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 3d29b2602d0f..153394a652d3 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -336,7 +336,7 @@ static umode_t vpd_attr_is_visible(struct kobject *kobj, } const struct attribute_group pci_dev_vpd_attr_group = { - .bin_attrs_new = vpd_attrs, + .bin_attrs = vpd_attrs, .is_bin_visible = vpd_attr_is_visible, }; diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 0c801e4ccc6c..05b67fd93de6 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1605,6 +1605,6 @@ static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj, const struct bin_attribute pccard_cis_attr = { .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, .size = 0x200, - .read_new = pccard_show_cis, - .write_new = pccard_store_cis, + .read = pccard_show_cis, + .write = pccard_store_cis, }; diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index 963c4db23055..5ee8adaa6564 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -108,7 +108,7 @@ static const struct bin_attribute *const cros_ec_vbc_bin_attrs[] = { static const struct attribute_group cros_ec_vbc_attr_group = { .name = "vbc", - .bin_attrs_new = cros_ec_vbc_bin_attrs, + .bin_attrs = cros_ec_vbc_bin_attrs, }; static int cros_ec_vbc_probe(struct platform_device *pd) diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index c18a5b96de5c..f67c7f56ab2b 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -993,7 +993,7 @@ static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, static const struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { .attr = { .name = "bootfifo", .mode = 0400 }, - .read_new = mlxbf_bootctl_bootfifo_read, + .read = mlxbf_bootctl_bootfifo_read, }; static bool mlxbf_bootctl_guid_match(const guid_t *guid, diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index d974c2289f5a..54986a752f7d 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -509,7 +509,7 @@ static int init_acpi(struct device *dev) static const struct bin_attribute hsmp_metric_tbl_attr = { .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, - .read_new = hsmp_metric_tbl_acpi_read, + .read = hsmp_metric_tbl_acpi_read, .size = sizeof(struct hsmp_metric_table), }; @@ -560,7 +560,7 @@ static struct attribute *hsmp_dev_attr_list[] = { }; static const struct attribute_group hsmp_attr_grp = { - .bin_attrs_new = hsmp_attr_list, + .bin_attrs = hsmp_attr_list, .attrs = hsmp_dev_attr_list, .is_bin_visible = hsmp_is_sock_attr_visible, .is_visible = hsmp_is_sock_dev_attr_visible, diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index f8aa844d33e4..22f50b6235d6 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -93,7 +93,7 @@ static_assert(MAX_AMD_NUM_NODES == 8); static const struct bin_attribute attr##index = { \ .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, \ .private = (void *)index, \ - .read_new = hsmp_metric_tbl_plat_read, \ + .read = hsmp_metric_tbl_plat_read, \ .size = sizeof(struct hsmp_metric_table), \ }; \ static const struct bin_attribute _list[] = { \ @@ -112,7 +112,7 @@ HSMP_BIN_ATTR(7, *sock7_attr_list); #define HSMP_BIN_ATTR_GRP(index, _list, _name) \ static const struct attribute_group sock##index##_attr_grp = { \ - .bin_attrs_new = _list, \ + .bin_attrs = _list, \ .is_bin_visible = hsmp_is_sock_attr_visible, \ .name = #_name, \ } diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c index 8149be25fa26..678f44252a45 100644 --- a/drivers/platform/x86/dell/dcdbas.c +++ b/drivers/platform/x86/dell/dcdbas.c @@ -662,7 +662,7 @@ static struct attribute *dcdbas_dev_attrs[] = { static const struct attribute_group dcdbas_attr_group = { .attrs = dcdbas_dev_attrs, - .bin_attrs_new = dcdbas_bin_attrs, + .bin_attrs = dcdbas_bin_attrs, }; static int dcdbas_probe(struct platform_device *dev) diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index 45c0a72e494a..2a140d1c656a 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -636,7 +636,7 @@ static const struct bin_attribute *const rbu_bin_attrs[] = { }; static const struct attribute_group rbu_group = { - .bin_attrs_new = rbu_bin_attrs, + .bin_attrs = rbu_bin_attrs, }; static int __init dcdrbu_init(void) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index edcce340ea67..7c3023d5d91d 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -340,7 +340,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, entry->pmt_bin_attr.attr.name = ns->name; entry->pmt_bin_attr.attr.mode = 0440; entry->pmt_bin_attr.mmap = intel_pmt_mmap; - entry->pmt_bin_attr.read_new = intel_pmt_read; + entry->pmt_bin_attr.read = intel_pmt_read; entry->pmt_bin_attr.size = entry->size; ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 30d1c2caf984..da75f53d0bcc 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -576,7 +576,7 @@ static struct attribute *sdsi_attrs[] = { static const struct attribute_group sdsi_group = { .attrs = sdsi_attrs, - .bin_attrs_new = sdsi_bin_attrs, + .bin_attrs = sdsi_bin_attrs, .is_bin_visible = sdsi_battr_is_visible, }; __ATTRIBUTE_GROUPS(sdsi); diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c index 3e33da36da8a..5b00370a9a22 100644 --- a/drivers/platform/x86/wmi-bmof.c +++ b/drivers/platform/x86/wmi-bmof.c @@ -46,7 +46,7 @@ static size_t bmof_bin_size(struct kobject *kobj, const struct bin_attribute *at static const struct attribute_group bmof_group = { .bin_size = bmof_bin_size, - .bin_attrs_new = bmof_attrs, + .bin_attrs = bmof_attrs, }; static const struct attribute_group *bmof_groups[] = { diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c index ff76ea36835e..e8bda60078c4 100644 --- a/drivers/pmdomain/amlogic/meson-secure-pwrc.c +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -342,32 +342,32 @@ static int meson_secure_pwrc_probe(struct platform_device *pdev) return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); } -static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { +static const struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { .domains = a1_pwrc_domains, .count = ARRAY_SIZE(a1_pwrc_domains), }; -static struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = { +static const struct meson_secure_pwrc_domain_data amlogic_secure_a4_pwrc_data = { .domains = a4_pwrc_domains, .count = ARRAY_SIZE(a4_pwrc_domains), }; -static struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = { +static const struct meson_secure_pwrc_domain_data amlogic_secure_a5_pwrc_data = { .domains = a5_pwrc_domains, .count = ARRAY_SIZE(a5_pwrc_domains), }; -static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { +static const struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { .domains = c3_pwrc_domains, .count = ARRAY_SIZE(c3_pwrc_domains), }; -static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = { +static const struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = { .domains = s4_pwrc_domains, .count = ARRAY_SIZE(s4_pwrc_domains), }; -static struct meson_secure_pwrc_domain_data amlogic_secure_t7_pwrc_data = { +static const struct meson_secure_pwrc_domain_data amlogic_secure_t7_pwrc_data = { .domains = t7_pwrc_domains, .count = ARRAY_SIZE(t7_pwrc_domains), }; diff --git a/drivers/pmdomain/apple/Kconfig b/drivers/pmdomain/apple/Kconfig index 12237cbcfaa9..a8973f8057fb 100644 --- a/drivers/pmdomain/apple/Kconfig +++ b/drivers/pmdomain/apple/Kconfig @@ -9,7 +9,6 @@ config APPLE_PMGR_PWRSTATE select MFD_SYSCON select PM_GENERIC_DOMAINS select RESET_CONTROLLER - default ARCH_APPLE help The PMGR block in Apple SoCs provides high-level power state controls for SoC devices. This driver manages them through the diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c index 2a213c218126..8fe1c0a501c9 100644 --- a/drivers/pmdomain/arm/scmi_pm_domain.c +++ b/drivers/pmdomain/arm/scmi_pm_domain.c @@ -22,27 +22,21 @@ struct scmi_pm_domain { #define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd) -static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on) +static int scmi_pd_power(struct generic_pm_domain *domain, u32 state) { - u32 state; struct scmi_pm_domain *pd = to_scmi_pd(domain); - if (power_on) - state = SCMI_POWER_STATE_GENERIC_ON; - else - state = SCMI_POWER_STATE_GENERIC_OFF; - return power_ops->state_set(pd->ph, pd->domain, state); } static int scmi_pd_power_on(struct generic_pm_domain *domain) { - return scmi_pd_power(domain, true); + return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_ON); } static int scmi_pd_power_off(struct generic_pm_domain *domain) { - return scmi_pd_power(domain, false); + return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_OFF); } static int scmi_pm_domain_probe(struct scmi_device *sdev) diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index ff5c7f2b69ce..0006ab3d0789 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -27,6 +27,16 @@ /* Provides a unique ID for each genpd device */ static DEFINE_IDA(genpd_ida); +/* The bus for genpd_providers. */ +static const struct bus_type genpd_provider_bus_type = { + .name = "genpd_provider", +}; + +/* The parent for genpd_provider devices. */ +static struct device genpd_provider_bus = { + .init_name = "genpd_provider", +}; + #define GENPD_RETRY_MAX_MS 250 /* Approximate */ #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ @@ -176,6 +186,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = { #define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON) #define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW) #define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW) +#define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE) static inline bool irq_safe_dev_in_sleep_domain(struct device *dev, const struct generic_pm_domain *genpd) @@ -758,6 +769,39 @@ int dev_pm_genpd_rpm_always_on(struct device *dev, bool on) } EXPORT_SYMBOL_GPL(dev_pm_genpd_rpm_always_on); +/** + * dev_pm_genpd_is_on() - Get device's current power domain status + * + * @dev: Device to get the current power status + * + * This function checks whether the generic power domain associated with the + * given device is on or not by verifying if genpd_status_on equals + * GENPD_STATE_ON. + * + * Note: this function returns the power status of the genpd at the time of the + * call. The power status may change after due to activity from other devices + * sharing the same genpd. Therefore, this information should not be relied for + * long-term decisions about the device power state. + * + * Return: 'true' if the device's power domain is on, 'false' otherwise. + */ +bool dev_pm_genpd_is_on(struct device *dev) +{ + struct generic_pm_domain *genpd; + bool is_on; + + genpd = dev_to_genpd_safe(dev); + if (!genpd) + return false; + + genpd_lock(genpd); + is_on = genpd_status_on(genpd); + genpd_unlock(genpd); + + return is_on; +} +EXPORT_SYMBOL_GPL(dev_pm_genpd_is_on); + /** * pm_genpd_inc_rejected() - Adjust the rejected/usage counts for an idle-state. * @@ -920,11 +964,12 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, * The domain is already in the "power off" state. * System suspend is in progress. * The domain is configured as always on. + * The domain was on at boot and still need to stay on. * The domain has a subdomain being powered on. */ if (!genpd_status_on(genpd) || genpd->prepared_count > 0 || genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd) || - atomic_read(&genpd->sd_count) > 0) + genpd->stay_on || atomic_read(&genpd->sd_count) > 0) return; /* @@ -1312,6 +1357,7 @@ err_poweroff: return ret; } +#ifndef CONFIG_PM_GENERIC_DOMAINS_OF static bool pd_ignore_unused; static int __init pd_ignore_unused_setup(char *__unused) { @@ -1335,14 +1381,19 @@ static int __init genpd_power_off_unused(void) pr_info("genpd: Disabling unused power domains\n"); mutex_lock(&gpd_list_lock); - list_for_each_entry(genpd, &gpd_list, gpd_list_node) + list_for_each_entry(genpd, &gpd_list, gpd_list_node) { + genpd_lock(genpd); + genpd->stay_on = false; + genpd_unlock(genpd); genpd_queue_power_off_work(genpd); + } mutex_unlock(&gpd_list_lock); return 0; } late_initcall_sync(genpd_power_off_unused); +#endif #ifdef CONFIG_PM_SLEEP @@ -2262,6 +2313,8 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd) genpd->gd = gd; device_initialize(&genpd->dev); genpd->dev.release = genpd_provider_release; + genpd->dev.bus = &genpd_provider_bus_type; + genpd->dev.parent = &genpd_provider_bus; if (!genpd_is_dev_name_fw(genpd)) { dev_set_name(&genpd->dev, "%s", genpd->name); @@ -2339,6 +2392,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd, INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); atomic_set(&genpd->sd_count, 0); genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON; + genpd->stay_on = !is_off; + genpd->sync_state = GENPD_SYNC_STATE_OFF; genpd->device_count = 0; genpd->provider = NULL; genpd->device_id = -ENXIO; @@ -2491,6 +2546,8 @@ struct of_genpd_provider { static LIST_HEAD(of_genpd_providers); /* Mutex to protect the list above. */ static DEFINE_MUTEX(of_genpd_mutex); +/* Used to prevent registering devices before the bus. */ +static bool genpd_bus_registered; /** * genpd_xlate_simple() - Xlate function for direct node-domain mapping @@ -2557,7 +2614,7 @@ static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, cp->node = of_node_get(np); cp->data = data; cp->xlate = xlate; - fwnode_dev_initialized(&np->fwnode, true); + fwnode_dev_initialized(of_fwnode_handle(np), true); mutex_lock(&of_genpd_mutex); list_add(&cp->link, &of_genpd_providers); @@ -2584,6 +2641,11 @@ static bool genpd_present(const struct generic_pm_domain *genpd) return ret; } +static void genpd_sync_state(struct device *dev) +{ + return of_genpd_sync_state(dev->of_node); +} + /** * of_genpd_add_provider_simple() - Register a simple PM domain provider * @np: Device node pointer associated with the PM domain provider. @@ -2592,21 +2654,43 @@ static bool genpd_present(const struct generic_pm_domain *genpd) int of_genpd_add_provider_simple(struct device_node *np, struct generic_pm_domain *genpd) { + struct fwnode_handle *fwnode; + struct device *dev; int ret; if (!np || !genpd) return -EINVAL; + if (!genpd_bus_registered) + return -ENODEV; + if (!genpd_present(genpd)) return -EINVAL; genpd->dev.of_node = np; + fwnode = of_fwnode_handle(np); + dev = get_dev_from_fwnode(fwnode); + if (!dev && !genpd_is_no_sync_state(genpd)) { + genpd->sync_state = GENPD_SYNC_STATE_SIMPLE; + device_set_node(&genpd->dev, fwnode); + } else { + dev_set_drv_sync_state(dev, genpd_sync_state); + } + + put_device(dev); + + ret = device_add(&genpd->dev); + if (ret) + return ret; + /* Parse genpd OPP table */ if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { ret = dev_pm_opp_of_add_table(&genpd->dev); - if (ret) - return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n"); + if (ret) { + dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n"); + goto err_del; + } /* * Save table for faster processing while setting performance @@ -2617,19 +2701,22 @@ int of_genpd_add_provider_simple(struct device_node *np, } ret = genpd_add_provider(np, genpd_xlate_simple, genpd); - if (ret) { - if (genpd->opp_table) { - dev_pm_opp_put_opp_table(genpd->opp_table); - dev_pm_opp_of_remove_table(&genpd->dev); - } + if (ret) + goto err_opp; - return ret; - } - - genpd->provider = &np->fwnode; + genpd->provider = fwnode; genpd->has_provider = true; return 0; + +err_opp: + if (genpd->opp_table) { + dev_pm_opp_put_opp_table(genpd->opp_table); + dev_pm_opp_of_remove_table(&genpd->dev); + } +err_del: + device_del(&genpd->dev); + return ret; } EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); @@ -2642,15 +2729,30 @@ int of_genpd_add_provider_onecell(struct device_node *np, struct genpd_onecell_data *data) { struct generic_pm_domain *genpd; + struct fwnode_handle *fwnode; + struct device *dev; unsigned int i; int ret = -EINVAL; + bool sync_state = false; if (!np || !data) return -EINVAL; + if (!genpd_bus_registered) + return -ENODEV; + if (!data->xlate) data->xlate = genpd_xlate_onecell; + fwnode = of_fwnode_handle(np); + dev = get_dev_from_fwnode(fwnode); + if (!dev) + sync_state = true; + else + dev_set_drv_sync_state(dev, genpd_sync_state); + + put_device(dev); + for (i = 0; i < data->num_domains; i++) { genpd = data->domains[i]; @@ -2661,12 +2763,23 @@ int of_genpd_add_provider_onecell(struct device_node *np, genpd->dev.of_node = np; + if (sync_state && !genpd_is_no_sync_state(genpd)) { + genpd->sync_state = GENPD_SYNC_STATE_ONECELL; + device_set_node(&genpd->dev, fwnode); + sync_state = false; + } + + ret = device_add(&genpd->dev); + if (ret) + goto error; + /* Parse genpd OPP table */ if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) { ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); if (ret) { dev_err_probe(&genpd->dev, ret, "Failed to add OPP table for index %d\n", i); + device_del(&genpd->dev); goto error; } @@ -2678,7 +2791,7 @@ int of_genpd_add_provider_onecell(struct device_node *np, WARN_ON(IS_ERR(genpd->opp_table)); } - genpd->provider = &np->fwnode; + genpd->provider = fwnode; genpd->has_provider = true; } @@ -2702,6 +2815,8 @@ error: dev_pm_opp_put_opp_table(genpd->opp_table); dev_pm_opp_of_remove_table(&genpd->dev); } + + device_del(&genpd->dev); } return ret; @@ -2727,17 +2842,19 @@ void of_genpd_del_provider(struct device_node *np) * so that the PM domain can be safely removed. */ list_for_each_entry(gpd, &gpd_list, gpd_list_node) { - if (gpd->provider == &np->fwnode) { + if (gpd->provider == of_fwnode_handle(np)) { gpd->has_provider = false; if (gpd->opp_table) { dev_pm_opp_put_opp_table(gpd->opp_table); dev_pm_opp_of_remove_table(&gpd->dev); } + + device_del(&gpd->dev); } } - fwnode_dev_initialized(&cp->node->fwnode, false); + fwnode_dev_initialized(of_fwnode_handle(cp->node), false); list_del(&cp->link); of_node_put(cp->node); kfree(cp); @@ -2916,7 +3033,7 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) mutex_lock(&gpd_list_lock); list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) { - if (gpd->provider == &np->fwnode) { + if (gpd->provider == of_fwnode_handle(np)) { ret = genpd_remove(gpd); genpd = ret ? ERR_PTR(ret) : gpd; break; @@ -3179,6 +3296,9 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev, if (num_domains < 0 || index >= num_domains) return NULL; + if (!genpd_bus_registered) + return ERR_PTR(-ENODEV); + /* Allocate and register device on the genpd bus. */ virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL); if (!virt_dev) @@ -3269,7 +3389,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state, genpd_state->power_on_latency_ns = 1000LL * exit_latency; genpd_state->power_off_latency_ns = 1000LL * entry_latency; - genpd_state->fwnode = &state_node->fwnode; + genpd_state->fwnode = of_fwnode_handle(state_node); return 0; } @@ -3355,9 +3475,103 @@ int of_genpd_parse_idle_states(struct device_node *dn, } EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); +/** + * of_genpd_sync_state() - A common sync_state function for genpd providers + * @np: The device node the genpd provider is associated with. + * + * The @np that corresponds to a genpd provider may provide one or multiple + * genpds. This function makes use @np to find the genpds that belongs to the + * provider. For each genpd we try a power-off. + */ +void of_genpd_sync_state(struct device_node *np) +{ + struct generic_pm_domain *genpd; + + if (!np) + return; + + mutex_lock(&gpd_list_lock); + list_for_each_entry(genpd, &gpd_list, gpd_list_node) { + if (genpd->provider == of_fwnode_handle(np)) { + genpd_lock(genpd); + genpd->stay_on = false; + genpd_power_off(genpd, false, 0); + genpd_unlock(genpd); + } + } + mutex_unlock(&gpd_list_lock); +} +EXPORT_SYMBOL_GPL(of_genpd_sync_state); + +static int genpd_provider_probe(struct device *dev) +{ + return 0; +} + +static void genpd_provider_sync_state(struct device *dev) +{ + struct generic_pm_domain *genpd = container_of(dev, struct generic_pm_domain, dev); + + switch (genpd->sync_state) { + case GENPD_SYNC_STATE_OFF: + break; + + case GENPD_SYNC_STATE_ONECELL: + of_genpd_sync_state(dev->of_node); + break; + + case GENPD_SYNC_STATE_SIMPLE: + genpd_lock(genpd); + genpd->stay_on = false; + genpd_power_off(genpd, false, 0); + genpd_unlock(genpd); + break; + + default: + break; + } +} + +static struct device_driver genpd_provider_drv = { + .name = "genpd_provider", + .bus = &genpd_provider_bus_type, + .probe = genpd_provider_probe, + .sync_state = genpd_provider_sync_state, + .suppress_bind_attrs = true, +}; + static int __init genpd_bus_init(void) { - return bus_register(&genpd_bus_type); + int ret; + + ret = device_register(&genpd_provider_bus); + if (ret) { + put_device(&genpd_provider_bus); + return ret; + } + + ret = bus_register(&genpd_provider_bus_type); + if (ret) + goto err_dev; + + ret = bus_register(&genpd_bus_type); + if (ret) + goto err_prov_bus; + + ret = driver_register(&genpd_provider_drv); + if (ret) + goto err_bus; + + genpd_bus_registered = true; + return 0; + +err_bus: + bus_unregister(&genpd_bus_type); +err_prov_bus: + bus_unregister(&genpd_provider_bus_type); +err_dev: + device_unregister(&genpd_provider_bus); + return ret; } core_initcall(genpd_bus_init); diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c index 912802b5215b..5c83e5599f1e 100644 --- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -665,6 +665,11 @@ static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = { #define LCDIF_1_RD_HURRY GENMASK(15, 13) #define LCDIF_0_RD_HURRY GENMASK(12, 10) +#define ISI_CACHE_CTRL 0x50 +#define ISI_V_WR_HURRY GENMASK(28, 26) +#define ISI_U_WR_HURRY GENMASK(25, 23) +#define ISI_Y_WR_HURRY GENMASK(22, 20) + static int imx8mp_media_power_notifier(struct notifier_block *nb, unsigned long action, void *data) { @@ -694,6 +699,11 @@ static int imx8mp_media_power_notifier(struct notifier_block *nb, regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL, FIELD_PREP(LCDIF_1_RD_HURRY, 7) | FIELD_PREP(LCDIF_0_RD_HURRY, 7)); + /* Same here for ISI */ + regmap_set_bits(bc->regmap, ISI_CACHE_CTRL, + FIELD_PREP(ISI_V_WR_HURRY, 7) | + FIELD_PREP(ISI_U_WR_HURRY, 7) | + FIELD_PREP(ISI_Y_WR_HURRY, 7)); } return NOTIFY_OK; diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 078323b85b56..4faa8a256186 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -217,6 +217,24 @@ static struct rpmhpd gmxc = { .res_name = "gmxc.lvl", }; +/* Milos RPMH powerdomains */ +static struct rpmhpd *milos_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc milos_desc = { + .rpmhpds = milos_rpmhpds, + .num_pds = ARRAY_SIZE(milos_rpmhpds), +}; + /* SA8540P RPMH powerdomains */ static struct rpmhpd *sa8540p_rpmhpds[] = { [SC8280XP_CX] = &cx, @@ -666,6 +684,31 @@ static const struct rpmhpd_desc sc8280xp_desc = { .num_pds = ARRAY_SIZE(sc8280xp_rpmhpds), }; +/* Glymur RPMH powerdomains */ +static struct rpmhpd *glymur_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx, + [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_MSS] = &mss, + [RPMHPD_NSP] = &nsp, + [RPMHPD_NSP2] = &nsp2, + [RPMHPD_GMXC] = &gmxc, +}; + +static const struct rpmhpd_desc glymur_desc = { + .rpmhpds = glymur_rpmhpds, + .num_pds = ARRAY_SIZE(glymur_rpmhpds), +}; + /* X1E80100 RPMH powerdomains */ static struct rpmhpd *x1e80100_rpmhpds[] = { [RPMHPD_CX] = &cx, @@ -723,6 +766,8 @@ static const struct rpmhpd_desc qcs615_desc = { }; static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,glymur-rpmhpd", .data = &glymur_desc }, + { .compatible = "qcom,milos-rpmhpd", .data = &milos_desc }, { .compatible = "qcom,qcs615-rpmhpd", .data = &qcs615_desc }, { .compatible = "qcom,qcs8300-rpmhpd", .data = &qcs8300_desc }, { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, @@ -1027,6 +1072,8 @@ static void rpmhpd_sync_state(struct device *dev) unsigned int i; int ret; + of_genpd_sync_state(dev->of_node); + mutex_lock(&rpmhpd_lock); for (i = 0; i < desc->num_pds; i++) { pd = rpmhpds[i]; diff --git a/drivers/pmdomain/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c index 0be6b3026e3a..833c46944600 100644 --- a/drivers/pmdomain/qcom/rpmpd.c +++ b/drivers/pmdomain/qcom/rpmpd.c @@ -1144,6 +1144,8 @@ static void rpmpd_sync_state(struct device *dev) unsigned int i; int ret; + of_genpd_sync_state(dev->of_node); + mutex_lock(&rpmpd_lock); for (i = 0; i < desc->num_pds; i++) { pd = rpmpds[i]; diff --git a/drivers/pmdomain/renesas/Kconfig b/drivers/pmdomain/renesas/Kconfig index 54acb4b1ec7c..b507c3e0d723 100644 --- a/drivers/pmdomain/renesas/Kconfig +++ b/drivers/pmdomain/renesas/Kconfig @@ -1,113 +1,117 @@ # SPDX-License-Identifier: GPL-2.0 if SOC_RENESAS +menu "Renesas PM Domains" +# SoC Family config SYSC_RCAR bool "System Controller support for R-Car" if COMPILE_TEST config SYSC_RCAR_GEN4 bool "System Controller support for R-Car Gen4" if COMPILE_TEST -config SYSC_R8A77995 - bool "System Controller support for R-Car D3" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A7794 - bool "System Controller support for R-Car E2" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A77990 - bool "System Controller support for R-Car E3" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A7779 - bool "System Controller support for R-Car H1" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A7790 - bool "System Controller support for R-Car H2" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A7795 - bool "System Controller support for R-Car H3" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A7791 - bool "System Controller support for R-Car M2-W/N" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A77965 - bool "System Controller support for R-Car M3-N" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A77960 - bool "System Controller support for R-Car M3-W" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A77961 - bool "System Controller support for R-Car M3-W+" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A779F0 - bool "System Controller support for R-Car S4-8" if COMPILE_TEST - select SYSC_RCAR_GEN4 - -config SYSC_R8A7792 - bool "System Controller support for R-Car V2H" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A77980 - bool "System Controller support for R-Car V3H" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A77970 - bool "System Controller support for R-Car V3M" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A779A0 - bool "System Controller support for R-Car V3U" if COMPILE_TEST - select SYSC_RCAR_GEN4 - -config SYSC_R8A779G0 - bool "System Controller support for R-Car V4H" if COMPILE_TEST - select SYSC_RCAR_GEN4 - -config SYSC_R8A779H0 - bool "System Controller support for R-Car V4M" if COMPILE_TEST - select SYSC_RCAR_GEN4 - config SYSC_RMOBILE bool "System Controller support for R-Mobile" if COMPILE_TEST -config SYSC_R8A77470 - bool "System Controller support for RZ/G1C" if COMPILE_TEST - select SYSC_RCAR - -config SYSC_R8A7745 - bool "System Controller support for RZ/G1E" if COMPILE_TEST - select SYSC_RCAR - +# SoC config SYSC_R8A7742 - bool "System Controller support for RZ/G1H" if COMPILE_TEST + bool "System Controller support for R8A7742 (RZ/G1H)" if COMPILE_TEST select SYSC_RCAR config SYSC_R8A7743 - bool "System Controller support for RZ/G1M" if COMPILE_TEST + bool "System Controller support for R8A7743 (RZ/G1M)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A774C0 - bool "System Controller support for RZ/G2E" if COMPILE_TEST +config SYSC_R8A7745 + bool "System Controller support for R8A7745 (RZ/G1E)" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A774E1 - bool "System Controller support for RZ/G2H" if COMPILE_TEST +config SYSC_R8A77470 + bool "System Controller support for R8A77470 (RZ/G1C)" if COMPILE_TEST select SYSC_RCAR config SYSC_R8A774A1 - bool "System Controller support for RZ/G2M" if COMPILE_TEST + bool "System Controller support for R8A774A1 (RZ/G2M)" if COMPILE_TEST select SYSC_RCAR config SYSC_R8A774B1 - bool "System Controller support for RZ/G2N" if COMPILE_TEST + bool "System Controller support for R8A774B1 (RZ/G2N)" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A774C0 + bool "System Controller support for R8A774C0 (RZ/G2E)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A774E1 + bool "System Controller support for R8A774E1 (RZ/G2H)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A7779 + bool "System Controller support for R8A7779 (R-Car H1)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A7790 + bool "System Controller support for R8A7790 (R-Car H2)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A7791 + bool "System Controller support for R8A7791/R8A7793 (R-Car M2-W/N)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A7792 + bool "System Controller support for R8A7792 (R-Car V2H)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A7794 + bool "System Controller support for R8A7794 (R-Car E2)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A7795 + bool "System Controller support for R8A7795 (R-Car H3)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A77960 + bool "System Controller support for R8A77960 (R-Car M3-W)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A77961 + bool "System Controller support for R8A77961 (R-Car M3-W+)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A77965 + bool "System Controller support for R8A77965 (R-Car M3-N)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A77970 + bool "System Controller support for R8A77970 (R-Car V3M)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A77980 + bool "System Controller support for R8A77980 (R-Car V3H)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A77990 + bool "System Controller support for R8A77990 (R-Car E3)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A77995 + bool "System Controller support for R8A77995 (R-Car D3)" if COMPILE_TEST + select SYSC_RCAR + +config SYSC_R8A779A0 + bool "System Controller support for R8A779A0 (R-Car V3U)" if COMPILE_TEST + select SYSC_RCAR_GEN4 + +config SYSC_R8A779F0 + bool "System Controller support for R8A779F0 (R-Car S4-8)" if COMPILE_TEST + select SYSC_RCAR_GEN4 + +config SYSC_R8A779G0 + bool "System Controller support for R8A779G0 (R-Car V4H)" if COMPILE_TEST + select SYSC_RCAR_GEN4 + +config SYSC_R8A779H0 + bool "System Controller support for R8A779H0 (R-Car V4M)" if COMPILE_TEST + select SYSC_RCAR_GEN4 + +endmenu endif diff --git a/drivers/pmdomain/renesas/rcar-gen4-sysc.c b/drivers/pmdomain/renesas/rcar-gen4-sysc.c index e001b5c25bed..5aa7fa1df8fe 100644 --- a/drivers/pmdomain/renesas/rcar-gen4-sysc.c +++ b/drivers/pmdomain/renesas/rcar-gen4-sysc.c @@ -374,4 +374,4 @@ out_put: of_node_put(np); return error; } -early_initcall(rcar_gen4_sysc_pd_init); +postcore_initcall(rcar_gen4_sysc_pd_init); diff --git a/drivers/pmdomain/renesas/rcar-sysc.c b/drivers/pmdomain/renesas/rcar-sysc.c index 047495f54e8a..4b310c1d35fa 100644 --- a/drivers/pmdomain/renesas/rcar-sysc.c +++ b/drivers/pmdomain/renesas/rcar-sysc.c @@ -342,6 +342,7 @@ struct rcar_pm_domains { }; static struct genpd_onecell_data *rcar_sysc_onecell_data; +static struct device_node *rcar_sysc_onecell_np; static int __init rcar_sysc_pd_init(void) { @@ -428,7 +429,8 @@ static int __init rcar_sysc_pd_init(void) } } - error = of_genpd_add_provider_onecell(np, &domains->onecell_data); + rcar_sysc_onecell_np = np; + return 0; out_put: of_node_put(np); @@ -436,6 +438,21 @@ out_put: } early_initcall(rcar_sysc_pd_init); +static int __init rcar_sysc_pd_init_provider(void) +{ + int error; + + if (!rcar_sysc_onecell_np) + return -ENODEV; + + error = of_genpd_add_provider_onecell(rcar_sysc_onecell_np, + rcar_sysc_onecell_data); + + of_node_put(rcar_sysc_onecell_np); + return error; +} +postcore_initcall(rcar_sysc_pd_init_provider); + #ifdef CONFIG_ARCH_R8A7779 static int rcar_sysc_power_cpu(unsigned int idx, bool on) { diff --git a/drivers/pmdomain/renesas/rmobile-sysc.c b/drivers/pmdomain/renesas/rmobile-sysc.c index 5848e79aa438..8eedc9a1d825 100644 --- a/drivers/pmdomain/renesas/rmobile-sysc.c +++ b/drivers/pmdomain/renesas/rmobile-sysc.c @@ -335,5 +335,4 @@ static int __init rmobile_init_pm_domains(void) return ret; } - -core_initcall(rmobile_init_pm_domains); +postcore_initcall(rmobile_init_pm_domains); diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c index 4cce407bb1eb..242570c505fb 100644 --- a/drivers/pmdomain/rockchip/pm-domains.c +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -216,6 +217,9 @@ struct rockchip_pmu { #define DOMAIN_RK3399(name, pwr, status, req, wakeup) \ DOMAIN(name, pwr, status, req, req, req, wakeup) +#define DOMAIN_RK3528(name, pwr, req) \ + DOMAIN_M(name, pwr, pwr, req, req, req, false) + #define DOMAIN_RK3562(name, pwr, req, g_mask, mem, wakeup) \ DOMAIN_M_G_SD(name, pwr, pwr, req, req, req, g_mask, mem, wakeup, false) @@ -1215,6 +1219,14 @@ static const struct rockchip_domain_info rk3399_pm_domains[] = { [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399("sdioaudio", BIT(31), BIT(31), BIT(29), true), }; +static const struct rockchip_domain_info rk3528_pm_domains[] = { + [RK3528_PD_GPU] = DOMAIN_RK3528("gpu", BIT(0), BIT(4)), + [RK3528_PD_RKVDEC] = DOMAIN_RK3528("vdec", 0, BIT(5)), + [RK3528_PD_RKVENC] = DOMAIN_RK3528("venc", 0, BIT(6)), + [RK3528_PD_VO] = DOMAIN_RK3528("vo", 0, BIT(7)), + [RK3528_PD_VPU] = DOMAIN_RK3528("vpu", 0, BIT(8)), +}; + static const struct rockchip_domain_info rk3562_pm_domains[] = { /* name pwr req g_mask mem wakeup */ [RK3562_PD_GPU] = DOMAIN_RK3562("gpu", BIT(0), BIT(1), BIT(1), 0, false), @@ -1428,6 +1440,17 @@ static const struct rockchip_pmu_info rk3399_pmu = { .domain_info = rk3399_pm_domains, }; +static const struct rockchip_pmu_info rk3528_pmu = { + .pwr_offset = 0x1210, + .status_offset = 0x1230, + .req_offset = 0x1110, + .idle_offset = 0x1128, + .ack_offset = 0x1120, + + .num_domains = ARRAY_SIZE(rk3528_pm_domains), + .domain_info = rk3528_pm_domains, +}; + static const struct rockchip_pmu_info rk3562_pmu = { .pwr_offset = 0x210, .status_offset = 0x230, @@ -1538,6 +1561,10 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = { .compatible = "rockchip,rk3399-power-controller", .data = (void *)&rk3399_pmu, }, + { + .compatible = "rockchip,rk3528-power-controller", + .data = (void *)&rk3528_pmu, + }, { .compatible = "rockchip,rk3562-power-controller", .data = (void *)&rk3562_pmu, diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index 9b502e8751d1..5d478bb37ad6 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -147,6 +147,15 @@ static int exynos_pd_probe(struct platform_device *pdev) parent.np, child.np); } + /* + * Some Samsung platforms with bootloaders turning on the splash-screen + * and handing it over to the kernel, requires the power-domains to be + * reset during boot. As a temporary hack to manage this, let's enforce + * a sync_state. + */ + if (!ret) + of_genpd_sync_state(np); + pm_runtime_enable(dev); return ret; } diff --git a/drivers/pmdomain/sunxi/Kconfig b/drivers/pmdomain/sunxi/Kconfig index 43eecb3ea981..858446594c88 100644 --- a/drivers/pmdomain/sunxi/Kconfig +++ b/drivers/pmdomain/sunxi/Kconfig @@ -1,13 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only config SUN20I_PPU - bool "Allwinner D1 PPU power domain driver" + tristate "Allwinner D1 PPU power domain driver" depends on ARCH_SUNXI || COMPILE_TEST depends on PM + default ARCH_SUNXI select PM_GENERIC_DOMAINS help - Say y to enable the PPU power domain driver. This saves power - when certain peripherals, such as the video engine, are idle. + Say y to enable the PPU power domain driver. This is required + to enable power to certain peripherals, such as the display + engine. config SUN50I_H6_PRCM_PPU tristate "Allwinner H6 PRCM power domain driver" @@ -18,3 +20,14 @@ config SUN50I_H6_PRCM_PPU Say y to enable the Allwinner H6/H616 PRCM power domain driver. This is required to enable the Mali GPU in the H616 SoC, it is optional for the H6. + +config SUN55I_PCK600 + tristate "Allwinner A523 PCK-600 power domain driver" + depends on ARCH_SUNXI || COMPILE_TEST + depends on PM + default ARCH_SUNXI + select PM_GENERIC_DOMAINS + help + Say y to enable the PCK-600 power domain driver. This is required + to enable power to certain peripherals, such as the display and + video engines. diff --git a/drivers/pmdomain/sunxi/Makefile b/drivers/pmdomain/sunxi/Makefile index c1343e123759..e344b232fc9f 100644 --- a/drivers/pmdomain/sunxi/Makefile +++ b/drivers/pmdomain/sunxi/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o obj-$(CONFIG_SUN50I_H6_PRCM_PPU) += sun50i-h6-prcm-ppu.o +obj-$(CONFIG_SUN55I_PCK600) += sun55i-pck600.o diff --git a/drivers/pmdomain/sunxi/sun20i-ppu.c b/drivers/pmdomain/sunxi/sun20i-ppu.c index 9f002748d224..b65876a68cc1 100644 --- a/drivers/pmdomain/sunxi/sun20i-ppu.c +++ b/drivers/pmdomain/sunxi/sun20i-ppu.c @@ -193,6 +193,19 @@ static const struct sun20i_ppu_desc sun8i_v853_ppu_desc = { .num_domains = ARRAY_SIZE(sun8i_v853_ppu_pd_names), }; +static const char *const sun55i_a523_ppu_pd_names[] = { + "DSP", + "NPU", + "AUDIO", + "SRAM", + "RISCV", +}; + +static const struct sun20i_ppu_desc sun55i_a523_ppu_desc = { + .names = sun55i_a523_ppu_pd_names, + .num_domains = ARRAY_SIZE(sun55i_a523_ppu_pd_names), +}; + static const struct of_device_id sun20i_ppu_of_match[] = { { .compatible = "allwinner,sun20i-d1-ppu", @@ -202,6 +215,10 @@ static const struct of_device_id sun20i_ppu_of_match[] = { .compatible = "allwinner,sun8i-v853-ppu", .data = &sun8i_v853_ppu_desc, }, + { + .compatible = "allwinner,sun55i-a523-ppu", + .data = &sun55i_a523_ppu_desc, + }, { } }; MODULE_DEVICE_TABLE(of, sun20i_ppu_of_match); diff --git a/drivers/pmdomain/sunxi/sun55i-pck600.c b/drivers/pmdomain/sunxi/sun55i-pck600.c new file mode 100644 index 000000000000..c7ab51514531 --- /dev/null +++ b/drivers/pmdomain/sunxi/sun55i-pck600.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Allwinner PCK-600 power domain support + * + * Copyright (c) 2025 Chen-Yu Tsai + * + * The hardware is likely based on the Arm PCK-600 IP, since some of + * the registers match Arm's documents, with additional delay controls + * that are in registers listed as reserved. + * + * Documents include: + * - "Arm CoreLink PCK-600 Power Control Kit" TRM + * - "Arm Power Policy Unit" architecture specification (DEN0051E) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PPU_PWPR 0x0 +#define PPU_PWSR 0x8 +#define PPU_DCDR0 0x170 +#define PPU_DCDR1 0x174 + +/* shared definition for PPU_PWPR and PPU_PWSR */ +#define PPU_PWR_STATUS GENMASK(3, 0) +#define PPU_POWER_MODE_ON 0x8 +#define PPU_POWER_MODE_OFF 0x0 + +#define PPU_REG_SIZE 0x1000 + +struct sunxi_pck600_desc { + const char * const *pd_names; + unsigned int num_domains; + u32 logic_power_switch0_delay_offset; + u32 logic_power_switch1_delay_offset; + u32 off2on_delay_offset; + u32 device_ctrl0_delay; + u32 device_ctrl1_delay; + u32 logic_power_switch0_delay; + u32 logic_power_switch1_delay; + u32 off2on_delay; +}; + +struct sunxi_pck600_pd { + struct generic_pm_domain genpd; + struct sunxi_pck600 *pck; + void __iomem *base; +}; + +struct sunxi_pck600 { + struct device *dev; + struct genpd_onecell_data genpd_data; + struct sunxi_pck600_pd pds[]; +}; + +#define to_sunxi_pd(gpd) container_of(gpd, struct sunxi_pck600_pd, genpd) + +static int sunxi_pck600_pd_set_power(struct sunxi_pck600_pd *pd, bool on) +{ + struct sunxi_pck600 *pck = pd->pck; + struct generic_pm_domain *genpd = &pd->genpd; + int ret; + u32 val, reg; + + val = on ? PPU_POWER_MODE_ON : PPU_POWER_MODE_OFF; + + reg = readl(pd->base + PPU_PWPR); + FIELD_MODIFY(PPU_PWR_STATUS, ®, val); + writel(reg, pd->base + PPU_PWPR); + + /* push write out to hardware */ + reg = readl(pd->base + PPU_PWPR); + + ret = readl_poll_timeout_atomic(pd->base + PPU_PWSR, reg, + FIELD_GET(PPU_PWR_STATUS, reg) == val, + 0, 10000); + if (ret) + dev_err(pck->dev, "failed to turn domain \"%s\" %s: %d\n", + genpd->name, str_on_off(on), ret); + + return ret; +} + +static int sunxi_pck600_power_on(struct generic_pm_domain *domain) +{ + struct sunxi_pck600_pd *pd = to_sunxi_pd(domain); + + return sunxi_pck600_pd_set_power(pd, true); +} + +static int sunxi_pck600_power_off(struct generic_pm_domain *domain) +{ + struct sunxi_pck600_pd *pd = to_sunxi_pd(domain); + + return sunxi_pck600_pd_set_power(pd, false); +} + +static void sunxi_pck600_pd_setup(struct sunxi_pck600_pd *pd, + const struct sunxi_pck600_desc *desc) +{ + writel(desc->device_ctrl0_delay, pd->base + PPU_DCDR0); + writel(desc->device_ctrl1_delay, pd->base + PPU_DCDR1); + writel(desc->logic_power_switch0_delay, + pd->base + desc->logic_power_switch0_delay_offset); + writel(desc->logic_power_switch1_delay, + pd->base + desc->logic_power_switch1_delay_offset); + writel(desc->off2on_delay, pd->base + desc->off2on_delay_offset); +} + +static int sunxi_pck600_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct sunxi_pck600_desc *desc; + struct genpd_onecell_data *genpds; + struct sunxi_pck600 *pck; + struct reset_control *rst; + struct clk *clk; + void __iomem *base; + int i, ret; + + desc = of_device_get_match_data(dev); + + pck = devm_kzalloc(dev, struct_size(pck, pds, desc->num_domains), GFP_KERNEL); + if (!pck) + return -ENOMEM; + + pck->dev = &pdev->dev; + platform_set_drvdata(pdev, pck); + + genpds = &pck->genpd_data; + genpds->num_domains = desc->num_domains; + genpds->domains = devm_kcalloc(dev, desc->num_domains, + sizeof(*genpds->domains), GFP_KERNEL); + if (!genpds->domains) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + rst = devm_reset_control_get_exclusive_released(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "failed to get reset control\n"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); + + for (i = 0; i < desc->num_domains; i++) { + struct sunxi_pck600_pd *pd = &pck->pds[i]; + + pd->genpd.name = desc->pd_names[i]; + pd->genpd.power_off = sunxi_pck600_power_off; + pd->genpd.power_on = sunxi_pck600_power_on; + pd->base = base + PPU_REG_SIZE * i; + + sunxi_pck600_pd_setup(pd, desc); + ret = pm_genpd_init(&pd->genpd, NULL, false); + if (ret) { + dev_err_probe(dev, ret, "failed to initialize power domain\n"); + goto err_remove_pds; + } + + genpds->domains[i] = &pd->genpd; + } + + ret = of_genpd_add_provider_onecell(dev_of_node(dev), genpds); + if (ret) { + dev_err_probe(dev, ret, "failed to add PD provider\n"); + goto err_remove_pds; + } + + return 0; + +err_remove_pds: + for (i--; i >= 0; i--) + pm_genpd_remove(genpds->domains[i]); + + return ret; +} + +static const char * const sun55i_a523_pck600_pd_names[] = { + "VE", "GPU", "VI", "VO0", "VO1", "DE", "NAND", "PCIE" +}; + +static const struct sunxi_pck600_desc sun55i_a523_pck600_desc = { + .pd_names = sun55i_a523_pck600_pd_names, + .num_domains = ARRAY_SIZE(sun55i_a523_pck600_pd_names), + .logic_power_switch0_delay_offset = 0xc00, + .logic_power_switch1_delay_offset = 0xc04, + .off2on_delay_offset = 0xc10, + .device_ctrl0_delay = 0xffffff, + .device_ctrl1_delay = 0xffff, + .logic_power_switch0_delay = 0x8080808, + .logic_power_switch1_delay = 0x808, + .off2on_delay = 0x8 +}; + +static const struct of_device_id sunxi_pck600_of_match[] = { + { + .compatible = "allwinner,sun55i-a523-pck-600", + .data = &sun55i_a523_pck600_desc, + }, + {} +}; +MODULE_DEVICE_TABLE(of, sunxi_pck600_of_match); + +static struct platform_driver sunxi_pck600_driver = { + .probe = sunxi_pck600_probe, + .driver = { + .name = "sunxi-pck-600", + .of_match_table = sunxi_pck600_of_match, + /* Power domains cannot be removed if in use. */ + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(sunxi_pck600_driver); + +MODULE_DESCRIPTION("Allwinner PCK-600 power domain driver"); +MODULE_AUTHOR("Chen-Yu Tsai "); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/thead/Kconfig b/drivers/pmdomain/thead/Kconfig index 7d52f8374b07..208828e0fa0d 100644 --- a/drivers/pmdomain/thead/Kconfig +++ b/drivers/pmdomain/thead/Kconfig @@ -4,6 +4,7 @@ config TH1520_PM_DOMAINS tristate "Support TH1520 Power Domains" depends on TH1520_AON_PROTOCOL select REGMAP_MMIO + select AUXILIARY_BUS help This driver enables power domain management for the T-HEAD TH-1520 SoC. On this SoC there are number of power domains, diff --git a/drivers/pmdomain/thead/th1520-pm-domains.c b/drivers/pmdomain/thead/th1520-pm-domains.c index f702e20306f4..9040b698e7f7 100644 --- a/drivers/pmdomain/thead/th1520-pm-domains.c +++ b/drivers/pmdomain/thead/th1520-pm-domains.c @@ -5,6 +5,7 @@ * Author: Michal Wilczynski */ +#include #include #include #include @@ -128,6 +129,50 @@ static void th1520_pd_init_all_off(struct generic_pm_domain **domains, } } +static void th1520_pd_pwrseq_unregister_adev(void *adev) +{ + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static int th1520_pd_pwrseq_gpu_init(struct device *dev) +{ + struct auxiliary_device *adev; + int ret; + + /* + * Correctly check only for the property's existence in the DT node. + * We don't need to get/claim the reset here; that is the job of + * the auxiliary driver that we are about to spawn. + */ + if (device_property_match_string(dev, "reset-names", "gpu-clkgen") < 0) + /* + * This is not an error. It simply means the optional sequencer + * is not described in the device tree. + */ + return 0; + + adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + adev->name = "pwrseq-gpu"; + adev->dev.parent = dev; + + ret = auxiliary_device_init(adev); + if (ret) + return ret; + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(dev, th1520_pd_pwrseq_unregister_adev, + adev); +} + static int th1520_pd_probe(struct platform_device *pdev) { struct generic_pm_domain **domains; @@ -186,8 +231,14 @@ static int th1520_pd_probe(struct platform_device *pdev) if (ret) goto err_clean_genpd; + ret = th1520_pd_pwrseq_gpu_init(dev); + if (ret) + goto err_clean_provider; + return 0; +err_clean_provider: + of_genpd_del_provider(dev->of_node); err_clean_genpd: for (i--; i >= 0; i--) pm_genpd_remove(domains[i]); diff --git a/drivers/pmdomain/ti/Kconfig b/drivers/pmdomain/ti/Kconfig index 67c608bf7ed0..5386b362a7ab 100644 --- a/drivers/pmdomain/ti/Kconfig +++ b/drivers/pmdomain/ti/Kconfig @@ -10,7 +10,7 @@ if SOC_TI config TI_SCI_PM_DOMAINS tristate "TI SCI PM Domains Driver" depends on TI_SCI_PROTOCOL - depends on PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS if PM help Generic power domain implementation for TI device implementing the TI SCI protocol. diff --git a/drivers/pmdomain/xilinx/zynqmp-pm-domains.c b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c index d579220a4500..b5aedd6e33ad 100644 --- a/drivers/pmdomain/xilinx/zynqmp-pm-domains.c +++ b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c @@ -153,14 +153,8 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, struct device *dev) { struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); - struct device_link *link; int ret; - link = device_link_add(dev, &domain->dev, DL_FLAG_SYNC_STATE_ONLY); - if (!link) - dev_dbg(&domain->dev, "failed to create device link for %s\n", - dev_name(dev)); - /* If this is not the first device to attach there is nothing to do */ if (domain->device_count) return 0; @@ -298,19 +292,9 @@ static void zynqmp_gpd_remove(struct platform_device *pdev) of_genpd_del_provider(pdev->dev.parent->of_node); } -static void zynqmp_gpd_sync_state(struct device *dev) -{ - int ret; - - ret = zynqmp_pm_init_finalize(); - if (ret) - dev_warn(dev, "failed to release power management to firmware\n"); -} - static struct platform_driver zynqmp_power_domain_driver = { .driver = { .name = "zynqmp_power_controller", - .sync_state = zynqmp_gpd_sync_state, }, .probe = zynqmp_gpd_probe, .remove = zynqmp_gpd_remove, diff --git a/drivers/power/supply/ds2760_battery.c b/drivers/power/supply/ds2760_battery.c index 5badf58c6edb..142c7492c3c2 100644 --- a/drivers/power/supply/ds2760_battery.c +++ b/drivers/power/supply/ds2760_battery.c @@ -209,7 +209,7 @@ static const struct bin_attribute *const w1_ds2760_bin_attrs[] = { }; static const struct attribute_group w1_ds2760_group = { - .bin_attrs_new = w1_ds2760_bin_attrs, + .bin_attrs = w1_ds2760_bin_attrs, }; static const struct attribute_group *w1_ds2760_groups[] = { diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c index dd9ac7a32967..5b57bbba79d4 100644 --- a/drivers/power/supply/ds2780_battery.c +++ b/drivers/power/supply/ds2780_battery.c @@ -660,8 +660,8 @@ static const struct bin_attribute ds2780_param_eeprom_bin_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = DS2780_PARAM_EEPROM_SIZE, - .read_new = ds2780_read_param_eeprom_bin, - .write_new = ds2780_write_param_eeprom_bin, + .read = ds2780_read_param_eeprom_bin, + .write = ds2780_write_param_eeprom_bin, }; static ssize_t ds2780_read_user_eeprom_bin(struct file *filp, @@ -705,8 +705,8 @@ static const struct bin_attribute ds2780_user_eeprom_bin_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = DS2780_USER_EEPROM_SIZE, - .read_new = ds2780_read_user_eeprom_bin, - .write_new = ds2780_write_user_eeprom_bin, + .read = ds2780_read_user_eeprom_bin, + .write = ds2780_write_user_eeprom_bin, }; static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled, @@ -734,7 +734,7 @@ static const struct bin_attribute *const ds2780_sysfs_bin_attrs[] = { static const struct attribute_group ds2780_sysfs_group = { .attrs = ds2780_sysfs_attrs, - .bin_attrs_new = ds2780_sysfs_bin_attrs, + .bin_attrs = ds2780_sysfs_bin_attrs, }; static const struct attribute_group *ds2780_sysfs_groups[] = { diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c index 8a1f1f9835e0..1319e02f3f95 100644 --- a/drivers/power/supply/ds2781_battery.c +++ b/drivers/power/supply/ds2781_battery.c @@ -662,8 +662,8 @@ static const struct bin_attribute ds2781_param_eeprom_bin_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = DS2781_PARAM_EEPROM_SIZE, - .read_new = ds2781_read_param_eeprom_bin, - .write_new = ds2781_write_param_eeprom_bin, + .read = ds2781_read_param_eeprom_bin, + .write = ds2781_write_param_eeprom_bin, }; static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, @@ -708,8 +708,8 @@ static const struct bin_attribute ds2781_user_eeprom_bin_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = DS2781_USER_EEPROM_SIZE, - .read_new = ds2781_read_user_eeprom_bin, - .write_new = ds2781_write_user_eeprom_bin, + .read = ds2781_read_user_eeprom_bin, + .write = ds2781_write_user_eeprom_bin, }; static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled, @@ -737,7 +737,7 @@ static const struct bin_attribute *const ds2781_sysfs_bin_attrs[] = { static const struct attribute_group ds2781_sysfs_group = { .attrs = ds2781_sysfs_attrs, - .bin_attrs_new = ds2781_sysfs_bin_attrs, + .bin_attrs = ds2781_sysfs_bin_attrs, }; diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index b9b607822676..202c4fa9b903 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -553,7 +553,7 @@ static const struct bin_attribute olpc_bat_eeprom = { .mode = S_IRUGO, }, .size = EEPROM_SIZE, - .read_new = olpc_bat_eeprom_read, + .read = olpc_bat_eeprom_read, }; /* Allow userspace to see the specific error value pulled from the EC */ @@ -591,7 +591,7 @@ static const struct bin_attribute *const olpc_bat_sysfs_bin_attrs[] = { static const struct attribute_group olpc_bat_sysfs_group = { .attrs = olpc_bat_sysfs_attrs, - .bin_attrs_new = olpc_bat_sysfs_bin_attrs, + .bin_attrs = olpc_bat_sysfs_bin_attrs, }; static const struct attribute_group *olpc_bat_sysfs_groups[] = { diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 1e7f72e57557..d39073dc4072 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -3938,7 +3938,7 @@ static const struct bin_attribute *const bin_art_timecard_attrs[] = { static const struct attribute_group art_timecard_group = { .attrs = art_timecard_attrs, - .bin_attrs_new = bin_art_timecard_attrs, + .bin_attrs = bin_art_timecard_attrs, }; static const struct ocp_attr_group art_timecard_groups[] = { diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index 6f89b232f1d5..0949c869b2f1 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -247,8 +247,8 @@ static const struct bin_attribute rio_config_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = RIO_MAINT_SPACE_SZ, - .read_new = rio_read_config, - .write_new = rio_write_config, + .read = rio_read_config, + .write = rio_write_config, }; static const struct bin_attribute *const rio_dev_bin_attrs[] = { @@ -278,7 +278,7 @@ static umode_t rio_dev_is_attr_visible(struct kobject *kobj, static const struct attribute_group rio_dev_group = { .attrs = rio_dev_attrs, .is_visible = rio_dev_is_attr_visible, - .bin_attrs_new = rio_dev_bin_attrs, + .bin_attrs = rio_dev_bin_attrs, }; const struct attribute_group *rio_dev_groups[] = { diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 74299af1d7f1..a6eef0080ca9 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -890,10 +891,8 @@ static int imx_rproc_partition_notify(struct notifier_block *nb, static int imx_rproc_attach_pd(struct imx_rproc *priv) { struct device *dev = priv->dev; - int ret; - struct dev_pm_domain_attach_data pd_data = { - .pd_flags = PD_FLAG_DEV_LINK_ON, - }; + int ret, i; + bool detached = true; /* * If there is only one power-domain entry, the platform driver framework @@ -902,8 +901,25 @@ static int imx_rproc_attach_pd(struct imx_rproc *priv) if (dev->pm_domain) return 0; - ret = dev_pm_domain_attach_list(dev, &pd_data, &priv->pd_list); - return ret < 0 ? ret : 0; + ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list); + if (ret < 0) + return ret; + /* + * If all the power domain devices are already turned on, the remote + * core is already powered up and running when the kernel booted (e.g., + * started by U-Boot's bootaux command). In this case attach to it. + */ + for (i = 0; i < ret; i++) { + if (!dev_pm_genpd_is_on(priv->pd_list->pd_devs[i])) { + detached = false; + break; + } + } + + if (detached) + priv->rproc->state = RPROC_DETACHED; + + return 0; } static int imx_rproc_detect_mode(struct imx_rproc *priv) @@ -1029,8 +1045,8 @@ static int imx_rproc_clk_enable(struct imx_rproc *priv) struct device *dev = priv->dev; int ret; - /* Remote core is not under control of Linux */ - if (dcfg->method == IMX_RPROC_NONE) + /* Remote core is not under control of Linux or it is managed by SCU API */ + if (dcfg->method == IMX_RPROC_NONE || dcfg->method == IMX_RPROC_SCU_API) return 0; priv->clk = devm_clk_get(dev, NULL); @@ -1146,6 +1162,15 @@ static int imx_rproc_probe(struct platform_device *pdev) } } + if (dcfg->method == IMX_RPROC_SCU_API) { + pm_runtime_enable(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(dev, "pm_runtime get failed: %d\n", ret); + goto err_put_clk; + } + } + ret = rproc_add(rproc); if (ret) { dev_err(dev, "rproc_add failed\n"); @@ -1171,6 +1196,10 @@ static void imx_rproc_remove(struct platform_device *pdev) struct rproc *rproc = platform_get_drvdata(pdev); struct imx_rproc *priv = rproc->priv; + if (priv->dcfg->method == IMX_RPROC_SCU_API) { + pm_runtime_disable(priv->dev); + pm_runtime_put(priv->dev); + } clk_disable_unprepare(priv->clk); rproc_del(rproc); imx_rproc_put_scu(rproc); diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index ae5d28987177..356d26a09af0 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -142,7 +142,7 @@ static const struct bin_attribute ofb_bin_attr = { .name = "event_data", .mode = S_IWUSR, }, - .write_new = sysfs_ofb_data_write, + .write = sysfs_ofb_data_write, }; #endif diff --git a/drivers/s390/char/sclp_sd.c b/drivers/s390/char/sclp_sd.c index 8524c14affed..ddd26c4da26a 100644 --- a/drivers/s390/char/sclp_sd.c +++ b/drivers/s390/char/sclp_sd.c @@ -539,7 +539,7 @@ static __init struct sclp_sd_file *sclp_sd_file_create(const char *name, u8 di) sysfs_bin_attr_init(&sd_file->data_attr); sd_file->data_attr.attr.name = "data"; sd_file->data_attr.attr.mode = 0444; - sd_file->data_attr.read_new = data_read; + sd_file->data_attr.read = data_read; rc = sysfs_create_bin_file(&sd_file->kobj, &sd_file->data_attr); if (rc) { diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 4f01b1929240..caa300160b17 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -470,7 +470,7 @@ static struct attribute *chp_attrs[] = { }; static const struct attribute_group chp_attr_group = { .attrs = chp_attrs, - .bin_attrs_new = chp_bin_attrs, + .bin_attrs = chp_bin_attrs, }; static const struct attribute_group *chp_attr_groups[] = { &chp_attr_group, diff --git a/drivers/s390/crypto/pkey_sysfs.c b/drivers/s390/crypto/pkey_sysfs.c index cea772973649..792c0fce88fa 100644 --- a/drivers/s390/crypto/pkey_sysfs.c +++ b/drivers/s390/crypto/pkey_sysfs.c @@ -297,7 +297,7 @@ static const struct bin_attribute *const protkey_attrs[] = { static const struct attribute_group protkey_attr_group = { .name = "protkey", - .bin_attrs_new = protkey_attrs, + .bin_attrs = protkey_attrs, }; /* @@ -406,7 +406,7 @@ static const struct bin_attribute *const ccadata_attrs[] = { static const struct attribute_group ccadata_attr_group = { .name = "ccadata", - .bin_attrs_new = ccadata_attrs, + .bin_attrs = ccadata_attrs, }; #define CCACIPHERTOKENSIZE (sizeof(struct cipherkeytoken) + 80) @@ -520,7 +520,7 @@ static const struct bin_attribute *const ccacipher_attrs[] = { static const struct attribute_group ccacipher_attr_group = { .name = "ccacipher", - .bin_attrs_new = ccacipher_attrs, + .bin_attrs = ccacipher_attrs, }; /* @@ -635,7 +635,7 @@ static const struct bin_attribute *const ep11_attrs[] = { static const struct attribute_group ep11_attr_group = { .name = "ep11", - .bin_attrs_new = ep11_attrs, + .bin_attrs = ep11_attrs, }; const struct attribute_group *pkey_attr_groups[] = { diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c index e057ab9c7b90..8d4174c7107e 100644 --- a/drivers/scsi/3w-sas.c +++ b/drivers/scsi/3w-sas.c @@ -122,7 +122,7 @@ static const struct bin_attribute twl_sysfs_aen_read_attr = { .mode = S_IRUSR, }, .size = 0, - .read_new = twl_sysfs_aen_read + .read = twl_sysfs_aen_read }; /* This function returns driver compatibility info through sysfs */ @@ -153,7 +153,7 @@ static const struct bin_attribute twl_sysfs_compat_info_attr = { .mode = S_IRUSR, }, .size = 0, - .read_new = twl_sysfs_compat_info + .read = twl_sysfs_compat_info }; /* Show some statistics about the card */ diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c index 8e3d4799ce93..1990af2bef95 100644 --- a/drivers/scsi/arcmsr/arcmsr_attr.c +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -194,7 +194,7 @@ static const struct bin_attribute arcmsr_sysfs_message_read_attr = { .mode = S_IRUSR , }, .size = ARCMSR_API_DATA_BUFLEN, - .read_new = arcmsr_sysfs_iop_message_read, + .read = arcmsr_sysfs_iop_message_read, }; static const struct bin_attribute arcmsr_sysfs_message_write_attr = { @@ -203,7 +203,7 @@ static const struct bin_attribute arcmsr_sysfs_message_write_attr = { .mode = S_IWUSR, }, .size = ARCMSR_API_DATA_BUFLEN, - .write_new = arcmsr_sysfs_iop_message_write, + .write = arcmsr_sysfs_iop_message_write, }; static const struct bin_attribute arcmsr_sysfs_message_clear_attr = { @@ -212,7 +212,7 @@ static const struct bin_attribute arcmsr_sysfs_message_clear_attr = { .mode = S_IWUSR, }, .size = 1, - .write_new = arcmsr_sysfs_iop_message_clear, + .write = arcmsr_sysfs_iop_message_clear, }; int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb) diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c index 3f31875ff46e..be6bf518eb7c 100644 --- a/drivers/scsi/esas2r/esas2r_main.c +++ b/drivers/scsi/esas2r/esas2r_main.c @@ -215,8 +215,8 @@ static ssize_t write_hw(struct file *file, struct kobject *kobj, .attr = \ { .name = __stringify(_name), .mode = S_IRUSR | S_IWUSR }, \ .size = 0, \ - .read_new = read_ ## _name, \ - .write_new = write_ ## _name } + .read = read_ ## _name, \ + .write = write_ ## _name } ESAS2R_RW_BIN_ATTR(fw); ESAS2R_RW_BIN_ATTR(fs); @@ -227,7 +227,7 @@ ESAS2R_RW_BIN_ATTR(live_nvram); const struct bin_attribute bin_attr_default_nvram = { .attr = { .name = "default_nvram", .mode = S_IRUGO }, .size = 0, - .read_new = read_default_nvram, + .read = read_default_nvram, .write = NULL }; diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 862ab0fbc893..228daffb286d 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -3670,7 +3670,7 @@ static const struct bin_attribute ibmvfc_trace_attr = { .mode = S_IRUGO, }, .size = 0, - .read_new = ibmvfc_read_trace, + .read = ibmvfc_read_trace, }; #endif diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index b29bec6abd72..d06b79f03538 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3389,7 +3389,7 @@ static const struct bin_attribute ipr_trace_attr = { .mode = S_IRUGO, }, .size = 0, - .read_new = ipr_read_trace, + .read = ipr_read_trace, }; #endif @@ -4140,8 +4140,8 @@ static const struct bin_attribute ipr_ioa_async_err_log = { .mode = S_IRUGO | S_IWUSR, }, .size = 0, - .read_new = ipr_read_async_err_log, - .write_new = ipr_next_async_err_log + .read = ipr_read_async_err_log, + .write = ipr_next_async_err_log }; static struct attribute *ipr_ioa_attrs[] = { @@ -4391,8 +4391,8 @@ static const struct bin_attribute ipr_dump_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 0, - .read_new = ipr_read_dump, - .write_new = ipr_write_dump + .read = ipr_read_dump, + .write = ipr_write_dump }; #else static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; }; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 54ee8ecec3b3..33582d48ec09 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -6420,8 +6420,8 @@ static const struct bin_attribute sysfs_ctlreg_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 256, - .read_new = sysfs_ctlreg_read, - .write_new = sysfs_ctlreg_write, + .read = sysfs_ctlreg_read, + .write = sysfs_ctlreg_write, }; /** @@ -6478,8 +6478,8 @@ static const struct bin_attribute sysfs_mbox_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = MAILBOX_SYSFS_MAX, - .read_new = sysfs_mbox_read, - .write_new = sysfs_mbox_write, + .read = sysfs_mbox_read, + .write = sysfs_mbox_write, }; /** diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 3fd1aa5cc78c..42d138ec11b4 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -2375,32 +2375,32 @@ static ssize_t lpfc_debugfs_dif_err_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - struct dentry *dent = file->f_path.dentry; struct lpfc_hba *phba = file->private_data; + int kind = debugfs_get_aux_num(file); char cbuf[32]; uint64_t tmp = 0; int cnt = 0; - if (dent == phba->debug_writeGuard) + if (kind == writeGuard) cnt = scnprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wgrd_cnt); - else if (dent == phba->debug_writeApp) + else if (kind == writeApp) cnt = scnprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wapp_cnt); - else if (dent == phba->debug_writeRef) + else if (kind == writeRef) cnt = scnprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_wref_cnt); - else if (dent == phba->debug_readGuard) + else if (kind == readGuard) cnt = scnprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rgrd_cnt); - else if (dent == phba->debug_readApp) + else if (kind == readApp) cnt = scnprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rapp_cnt); - else if (dent == phba->debug_readRef) + else if (kind == readRef) cnt = scnprintf(cbuf, 32, "%u\n", phba->lpfc_injerr_rref_cnt); - else if (dent == phba->debug_InjErrNPortID) + else if (kind == InjErrNPortID) cnt = scnprintf(cbuf, 32, "0x%06x\n", phba->lpfc_injerr_nportid); - else if (dent == phba->debug_InjErrWWPN) { + else if (kind == InjErrWWPN) { memcpy(&tmp, &phba->lpfc_injerr_wwpn, sizeof(struct lpfc_name)); tmp = cpu_to_be64(tmp); cnt = scnprintf(cbuf, 32, "0x%016llx\n", tmp); - } else if (dent == phba->debug_InjErrLBA) { + } else if (kind == InjErrLBA) { if (phba->lpfc_injerr_lba == (sector_t)(-1)) cnt = scnprintf(cbuf, 32, "off\n"); else @@ -2417,8 +2417,8 @@ static ssize_t lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos) { - struct dentry *dent = file->f_path.dentry; struct lpfc_hba *phba = file->private_data; + int kind = debugfs_get_aux_num(file); char dstbuf[33]; uint64_t tmp = 0; int size; @@ -2428,7 +2428,7 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf, if (copy_from_user(dstbuf, buf, size)) return -EFAULT; - if (dent == phba->debug_InjErrLBA) { + if (kind == InjErrLBA) { if ((dstbuf[0] == 'o') && (dstbuf[1] == 'f') && (dstbuf[2] == 'f')) tmp = (uint64_t)(-1); @@ -2437,23 +2437,23 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf, if ((tmp == 0) && (kstrtoull(dstbuf, 0, &tmp))) return -EINVAL; - if (dent == phba->debug_writeGuard) + if (kind == writeGuard) phba->lpfc_injerr_wgrd_cnt = (uint32_t)tmp; - else if (dent == phba->debug_writeApp) + else if (kind == writeApp) phba->lpfc_injerr_wapp_cnt = (uint32_t)tmp; - else if (dent == phba->debug_writeRef) + else if (kind == writeRef) phba->lpfc_injerr_wref_cnt = (uint32_t)tmp; - else if (dent == phba->debug_readGuard) + else if (kind == readGuard) phba->lpfc_injerr_rgrd_cnt = (uint32_t)tmp; - else if (dent == phba->debug_readApp) + else if (kind == readApp) phba->lpfc_injerr_rapp_cnt = (uint32_t)tmp; - else if (dent == phba->debug_readRef) + else if (kind == readRef) phba->lpfc_injerr_rref_cnt = (uint32_t)tmp; - else if (dent == phba->debug_InjErrLBA) + else if (kind == InjErrLBA) phba->lpfc_injerr_lba = (sector_t)tmp; - else if (dent == phba->debug_InjErrNPortID) + else if (kind == InjErrNPortID) phba->lpfc_injerr_nportid = (uint32_t)(tmp & Mask_DID); - else if (dent == phba->debug_InjErrWWPN) { + else if (kind == InjErrWWPN) { tmp = cpu_to_be64(tmp); memcpy(&phba->lpfc_injerr_wwpn, &tmp, sizeof(struct lpfc_name)); } else @@ -6160,60 +6160,51 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) phba->debug_dumpHostSlim = NULL; /* Setup DIF Error Injections */ - snprintf(name, sizeof(name), "InjErrLBA"); phba->debug_InjErrLBA = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("InjErrLBA", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, InjErrLBA, &lpfc_debugfs_op_dif_err); phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; - snprintf(name, sizeof(name), "InjErrNPortID"); phba->debug_InjErrNPortID = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("InjErrNPortID", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, InjErrNPortID, &lpfc_debugfs_op_dif_err); - snprintf(name, sizeof(name), "InjErrWWPN"); phba->debug_InjErrWWPN = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("InjErrWWPN", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, InjErrWWPN, &lpfc_debugfs_op_dif_err); - snprintf(name, sizeof(name), "writeGuardInjErr"); phba->debug_writeGuard = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("writeGuardInjErr", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, writeGuard, &lpfc_debugfs_op_dif_err); - snprintf(name, sizeof(name), "writeAppInjErr"); phba->debug_writeApp = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("writeAppInjErr", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, writeApp, &lpfc_debugfs_op_dif_err); - snprintf(name, sizeof(name), "writeRefInjErr"); phba->debug_writeRef = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("writeRefInjErr", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, writeRef, &lpfc_debugfs_op_dif_err); - snprintf(name, sizeof(name), "readGuardInjErr"); phba->debug_readGuard = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("readGuardInjErr", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, readGuard, &lpfc_debugfs_op_dif_err); - snprintf(name, sizeof(name), "readAppInjErr"); phba->debug_readApp = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("readAppInjErr", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, readApp, &lpfc_debugfs_op_dif_err); - snprintf(name, sizeof(name), "readRefInjErr"); phba->debug_readRef = - debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, + debugfs_create_file_aux_num("readRefInjErr", 0644, phba->hba_debugfs_root, - phba, &lpfc_debugfs_op_dif_err); + phba, readRef, &lpfc_debugfs_op_dif_err); /* Setup slow ring trace */ if (lpfc_debugfs_max_slow_ring_trc) { diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h index 8d2e8d05bbc0..f319f3af0400 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.h +++ b/drivers/scsi/lpfc/lpfc_debugfs.h @@ -322,6 +322,17 @@ enum { * discovery */ #endif /* H_LPFC_DEBUG_FS */ +enum { + writeGuard = 1, + writeApp, + writeRef, + readGuard, + readApp, + readRef, + InjErrLBA, + InjErrNPortID, + InjErrWWPN, +}; /* * Driver debug utility routines outside of debugfs. The debug utility diff --git a/drivers/scsi/qedf/qedf_attr.c b/drivers/scsi/qedf/qedf_attr.c index 769da92ee20d..7ebb46689f97 100644 --- a/drivers/scsi/qedf/qedf_attr.c +++ b/drivers/scsi/qedf/qedf_attr.c @@ -166,8 +166,8 @@ static const struct bin_attribute sysfs_grcdump_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 0, - .read_new = qedf_sysfs_read_grcdump, - .write_new = qedf_sysfs_write_grcdump, + .read = qedf_sysfs_read_grcdump, + .write = qedf_sysfs_write_grcdump, }; static struct sysfs_bin_attrs bin_file_entries[] = { diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index dcb0c2af1fa7..2e584a8bf66b 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -174,8 +174,8 @@ static const struct bin_attribute sysfs_fw_dump_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 0, - .read_new = qla2x00_sysfs_read_fw_dump, - .write_new = qla2x00_sysfs_write_fw_dump, + .read = qla2x00_sysfs_read_fw_dump, + .write = qla2x00_sysfs_write_fw_dump, }; static ssize_t @@ -288,8 +288,8 @@ static const struct bin_attribute sysfs_nvram_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 512, - .read_new = qla2x00_sysfs_read_nvram, - .write_new = qla2x00_sysfs_write_nvram, + .read = qla2x00_sysfs_read_nvram, + .write = qla2x00_sysfs_write_nvram, }; static ssize_t @@ -350,8 +350,8 @@ static const struct bin_attribute sysfs_optrom_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 0, - .read_new = qla2x00_sysfs_read_optrom, - .write_new = qla2x00_sysfs_write_optrom, + .read = qla2x00_sysfs_read_optrom, + .write = qla2x00_sysfs_write_optrom, }; static ssize_t @@ -535,7 +535,7 @@ static const struct bin_attribute sysfs_optrom_ctl_attr = { .mode = S_IWUSR, }, .size = 0, - .write_new = qla2x00_sysfs_write_optrom_ctl, + .write = qla2x00_sysfs_write_optrom_ctl, }; static ssize_t @@ -648,8 +648,8 @@ static const struct bin_attribute sysfs_vpd_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 0, - .read_new = qla2x00_sysfs_read_vpd, - .write_new = qla2x00_sysfs_write_vpd, + .read = qla2x00_sysfs_read_vpd, + .write = qla2x00_sysfs_write_vpd, }; static ssize_t @@ -685,7 +685,7 @@ static const struct bin_attribute sysfs_sfp_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = SFP_DEV_SIZE, - .read_new = qla2x00_sysfs_read_sfp, + .read = qla2x00_sysfs_read_sfp, }; static ssize_t @@ -829,7 +829,7 @@ static const struct bin_attribute sysfs_reset_attr = { .mode = S_IWUSR, }, .size = 0, - .write_new = qla2x00_sysfs_write_reset, + .write = qla2x00_sysfs_write_reset, }; static ssize_t @@ -872,7 +872,7 @@ static const struct bin_attribute sysfs_issue_logo_attr = { .mode = S_IWUSR, }, .size = 0, - .write_new = qla2x00_issue_logo, + .write = qla2x00_issue_logo, }; static ssize_t @@ -935,7 +935,7 @@ static const struct bin_attribute sysfs_xgmac_stats_attr = { .mode = S_IRUSR, }, .size = 0, - .read_new = qla2x00_sysfs_read_xgmac_stats, + .read = qla2x00_sysfs_read_xgmac_stats, }; static ssize_t @@ -993,7 +993,7 @@ static const struct bin_attribute sysfs_dcbx_tlv_attr = { .mode = S_IRUSR, }, .size = 0, - .read_new = qla2x00_sysfs_read_dcbx_tlv, + .read = qla2x00_sysfs_read_dcbx_tlv, }; static struct sysfs_entry { diff --git a/drivers/scsi/qla4xxx/ql4_attr.c b/drivers/scsi/qla4xxx/ql4_attr.c index e3f85d6ea0db..84f99ff8e69a 100644 --- a/drivers/scsi/qla4xxx/ql4_attr.c +++ b/drivers/scsi/qla4xxx/ql4_attr.c @@ -110,8 +110,8 @@ static const struct bin_attribute sysfs_fw_dump_attr = { .mode = S_IRUSR | S_IWUSR, }, .size = 0, - .read_new = qla4_8xxx_sysfs_read_fw_dump, - .write_new = qla4_8xxx_sysfs_write_fw_dump, + .read = qla4_8xxx_sysfs_read_fw_dump, + .write = qla4_8xxx_sysfs_write_fw_dump, }; static struct sysfs_entry { diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index d772258e29ad..169af7d47ce7 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -917,7 +917,7 @@ show_vpd_##_page(struct file *filp, struct kobject *kobj, \ static const struct bin_attribute dev_attr_vpd_##_page = { \ .attr = {.name = __stringify(vpd_##_page), .mode = S_IRUGO }, \ .size = 0, \ - .read_new = show_vpd_##_page, \ + .read = show_vpd_##_page, \ }; sdev_vpd_pg_attr(pg83); @@ -949,7 +949,7 @@ static const struct bin_attribute dev_attr_inquiry = { .mode = S_IRUGO, }, .size = 0, - .read_new = show_inquiry, + .read = show_inquiry, }; static ssize_t @@ -1362,7 +1362,7 @@ static const struct bin_attribute *const scsi_sdev_bin_attrs[] = { }; static struct attribute_group scsi_sdev_attr_group = { .attrs = scsi_sdev_attrs, - .bin_attrs_new = scsi_sdev_bin_attrs, + .bin_attrs = scsi_sdev_bin_attrs, .is_visible = scsi_sdev_attr_is_visible, .is_bin_visible = scsi_sdev_bin_attr_is_visible, }; diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 4d6a46bc8c21..2a5f24ee858c 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -418,7 +418,6 @@ struct tegra_pmc_soc { * @irq: chip implementation for the IRQ domain * @clk_nb: pclk clock changes handler * @core_domain_state_synced: flag marking the core domain's state as synced - * @core_domain_registered: flag marking the core domain as registered * @wake_type_level_map: Bitmap indicating level type for non-dual edge wakes * @wake_type_dual_edge_map: Bitmap indicating if a wake is dual-edge or not * @wake_sw_status_map: Bitmap to hold raw status of wakes without mask @@ -462,7 +461,6 @@ struct tegra_pmc { struct notifier_block clk_nb; bool core_domain_state_synced; - bool core_domain_registered; unsigned long *wake_type_level_map; unsigned long *wake_type_dual_edge_map; @@ -1297,6 +1295,7 @@ static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) pg->id = id; pg->genpd.name = np->name; + pg->genpd.flags = GENPD_FLAG_NO_SYNC_STATE; pg->genpd.power_off = tegra_genpd_power_off; pg->genpd.power_on = tegra_genpd_power_on; pg->pmc = pmc; @@ -1406,6 +1405,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) return -ENOMEM; genpd->name = "core"; + genpd->flags = GENPD_FLAG_NO_SYNC_STATE; genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state; err = devm_pm_opp_set_regulators(pmc->dev, rname); @@ -1425,8 +1425,6 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) goto remove_genpd; } - pmc->core_domain_registered = true; - return 0; remove_genpd: @@ -4383,8 +4381,25 @@ static const struct of_device_id tegra_pmc_match[] = { static void tegra_pmc_sync_state(struct device *dev) { + struct device_node *np, *child; int err; + np = of_get_child_by_name(dev->of_node, "powergates"); + if (!np) + return; + + for_each_child_of_node(np, child) + of_genpd_sync_state(child); + + of_node_put(np); + + np = of_get_child_by_name(dev->of_node, "core-domain"); + if (!np) + return; + + of_genpd_sync_state(np); + of_node_put(np); + /* * Newer device-trees have power domains, but we need to prepare all * device drivers with runtime PM and OPP support first, otherwise @@ -4398,9 +4413,6 @@ static void tegra_pmc_sync_state(struct device *dev) * no dependencies that will block the state syncing. We shouldn't * mark the domain as synced in this case. */ - if (!pmc->core_domain_registered) - return; - pmc->core_domain_state_synced = true; /* this is a no-op if core regulator isn't used */ diff --git a/drivers/staging/greybus/camera.c b/drivers/staging/greybus/camera.c index ec9fddfc0b14..5ac19c0055d9 100644 --- a/drivers/staging/greybus/camera.c +++ b/drivers/staging/greybus/camera.c @@ -1128,7 +1128,7 @@ done: static int gb_camera_debugfs_open(struct inode *inode, struct file *file) { - file->private_data = (void *)debugfs_get_aux(file); + file->private_data = debugfs_get_aux(file); return 0; } diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c index ba11d70e8021..1159ecea57e7 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -139,31 +139,21 @@ static int tt_command_exec(int index, const char *arg) return ret; } -static ssize_t tt_command_process(struct dentry *dentry, const char __user *user_buf, - size_t count) +static ssize_t tt_command_process(char *s) { - char *buf __free(kfree); char *arg; int i; - buf = kmalloc(count + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; + strim(s); - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - buf[count] = '\0'; - strim(buf); - - arg = strstr(buf, ":"); + arg = strchr(s, ':'); if (arg) { *arg = '\0'; arg++; } for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) { - if (!strcmp(buf, tt_command_strings[i])) + if (!strcmp(s, tt_command_strings[i])) return tt_command_exec(i, arg); } @@ -173,20 +163,20 @@ static ssize_t tt_command_process(struct dentry *dentry, const char __user *user static ssize_t tt_command_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - struct dentry *dentry = file->f_path.dentry; + char buf[TT_COMMAND_SIZE]; ssize_t ret; if (*ppos) return -EINVAL; - if (count + 1 > TT_COMMAND_SIZE) + if (count > TT_COMMAND_SIZE - 1) return -E2BIG; - ret = debugfs_file_get(dentry); - if (unlikely(ret)) - return ret; + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + buf[count] = '\0'; - ret = tt_command_process(dentry, user_buf, count); + ret = tt_command_process(buf); if (ret) return ret; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 23f3cb1989f4..a07866f1060c 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -944,7 +944,7 @@ static umode_t dev_bin_attrs_are_visible(struct kobject *kobj, } static const struct attribute_group dev_bin_attr_grp = { - .bin_attrs_new = dev_bin_attrs, + .bin_attrs = dev_bin_attrs, .is_bin_visible = dev_bin_attrs_are_visible, }; diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index 2aae3edfc813..47f3a7d51736 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -259,7 +259,7 @@ static const struct bin_attribute *const dev_bin_attrs[] = { static const struct attribute_group vudc_attr_group = { .attrs = dev_attrs, - .bin_attrs_new = dev_bin_attrs, + .bin_attrs = dev_bin_attrs, }; const struct attribute_group *vudc_groups[] = { diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index 0eef8c6b98c8..1d2c57ba25a3 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -2227,7 +2227,7 @@ static const struct bin_attribute edid1_attr = { .mode = 0444, }, .size = EDID_LENGTH, - .read_new = radeon_show_edid1, + .read = radeon_show_edid1, }; static const struct bin_attribute edid2_attr = { @@ -2236,7 +2236,7 @@ static const struct bin_attribute edid2_attr = { .mode = 0444, }, .size = EDID_LENGTH, - .read_new = radeon_show_edid2, + .read = radeon_show_edid2, }; static int radeonfb_pci_register(struct pci_dev *pdev, diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index acadf0eb450c..ccede85df1e1 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -1482,8 +1482,8 @@ static const struct bin_attribute edid_attr = { .attr.name = "edid", .attr.mode = 0666, .size = EDID_LENGTH, - .read_new = edid_show, - .write_new = edid_store + .read = edid_show, + .write = edid_store }; static const struct device_attribute fb_device_attrs[] = { diff --git a/drivers/virt/coco/guest/tsm-mr.c b/drivers/virt/coco/guest/tsm-mr.c index feb30af90a20..bc509df04db1 100644 --- a/drivers/virt/coco/guest/tsm-mr.c +++ b/drivers/virt/coco/guest/tsm-mr.c @@ -209,12 +209,12 @@ tsm_mr_create_attribute_group(const struct tsm_measurements *tm) if (tm->mrs[i].mr_flags & TSM_MR_F_READABLE) { bap->attr.mode |= 0444; - bap->read_new = tm_digest_read; + bap->read = tm_digest_read; } if (tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) { bap->attr.mode |= 0200; - bap->write_new = tm_digest_write; + bap->write = tm_digest_write; } bap->size = tm->mrs[i].mr_size; @@ -228,7 +228,7 @@ tsm_mr_create_attribute_group(const struct tsm_measurements *tm) init_rwsem(&ctx->rwsem); ctx->agrp.name = "measurements"; - ctx->agrp.bin_attrs_new = no_free_ptr(attrs); + ctx->agrp.bin_attrs = no_free_ptr(attrs); ctx->tm = tm; return &no_free_ptr(ctx)->agrp; } @@ -244,7 +244,7 @@ EXPORT_SYMBOL_GPL(tsm_mr_create_attribute_group); void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp) { if (!IS_ERR_OR_NULL(attr_grp)) { - kfree(attr_grp->bin_attrs_new); + kfree(attr_grp->bin_attrs); kfree(container_of(attr_grp, struct tm_context, agrp)); } } diff --git a/drivers/w1/slaves/w1_ds2406.c b/drivers/w1/slaves/w1_ds2406.c index 76026d615111..efb2e784f8d7 100644 --- a/drivers/w1/slaves/w1_ds2406.c +++ b/drivers/w1/slaves/w1_ds2406.c @@ -94,7 +94,7 @@ static const struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { .mode = 0444, }, .size = 1, - .read_new = w1_f12_read_state, + .read = w1_f12_read_state, }, { .attr = { @@ -102,7 +102,7 @@ static const struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { .mode = 0664, }, .size = 1, - .write_new = w1_f12_write_output, + .write = w1_f12_write_output, } }; diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index beccd2912d2a..30d1d574d2e5 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -328,7 +328,7 @@ static const struct bin_attribute *const w1_f29_bin_attrs[] = { }; static const struct attribute_group w1_f29_group = { - .bin_attrs_new = w1_f29_bin_attrs, + .bin_attrs = w1_f29_bin_attrs, }; static const struct attribute_group *w1_f29_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2413.c b/drivers/w1/slaves/w1_ds2413.c index 5fa46017ca7c..94d3cd2a0ec9 100644 --- a/drivers/w1/slaves/w1_ds2413.c +++ b/drivers/w1/slaves/w1_ds2413.c @@ -137,7 +137,7 @@ static const struct bin_attribute *const w1_f3a_bin_attrs[] = { }; static const struct attribute_group w1_f3a_group = { - .bin_attrs_new = w1_f3a_bin_attrs, + .bin_attrs = w1_f3a_bin_attrs, }; static const struct attribute_group *w1_f3a_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2430.c b/drivers/w1/slaves/w1_ds2430.c index ff56e2e68e58..3d8c2b238aed 100644 --- a/drivers/w1/slaves/w1_ds2430.c +++ b/drivers/w1/slaves/w1_ds2430.c @@ -271,7 +271,7 @@ static const struct bin_attribute *const w1_f14_bin_attrs[] = { }; static const struct attribute_group w1_f14_group = { - .bin_attrs_new = w1_f14_bin_attrs, + .bin_attrs = w1_f14_bin_attrs, }; static const struct attribute_group *w1_f14_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index 27b390fb59da..5749880b67c5 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.c @@ -270,7 +270,7 @@ static const struct bin_attribute *const w1_f2d_bin_attrs[] = { }; static const struct attribute_group w1_f2d_group = { - .bin_attrs_new = w1_f2d_bin_attrs, + .bin_attrs = w1_f2d_bin_attrs, }; static const struct attribute_group *w1_f2d_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 22331d840ec1..3371d804dc6c 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -276,15 +276,15 @@ out_up: static const struct bin_attribute bin_attr_f23_eeprom = { .attr = { .name = "eeprom", .mode = 0644 }, - .read_new = eeprom_read, - .write_new = eeprom_write, + .read = eeprom_read, + .write = eeprom_write, .size = W1_EEPROM_DS2433_SIZE, }; static const struct bin_attribute bin_attr_f43_eeprom = { .attr = { .name = "eeprom", .mode = 0644 }, - .read_new = eeprom_read, - .write_new = eeprom_write, + .read = eeprom_read, + .write = eeprom_write, .size = W1_EEPROM_DS28EC20_SIZE, }; @@ -294,7 +294,7 @@ static const struct bin_attribute *const w1_f23_bin_attributes[] = { }; static const struct attribute_group w1_f23_group = { - .bin_attrs_new = w1_f23_bin_attributes, + .bin_attrs = w1_f23_bin_attributes, }; static const struct attribute_group *w1_f23_groups[] = { @@ -308,7 +308,7 @@ static const struct bin_attribute *const w1_f43_bin_attributes[] = { }; static const struct attribute_group w1_f43_group = { - .bin_attrs_new = w1_f43_bin_attributes, + .bin_attrs = w1_f43_bin_attributes, }; static const struct attribute_group *w1_f43_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c index 630a6db5045e..86860f727e96 100644 --- a/drivers/w1/slaves/w1_ds2438.c +++ b/drivers/w1/slaves/w1_ds2438.c @@ -492,7 +492,7 @@ static const struct bin_attribute *const w1_ds2438_bin_attrs[] = { }; static const struct attribute_group w1_ds2438_group = { - .bin_attrs_new = w1_ds2438_bin_attrs, + .bin_attrs = w1_ds2438_bin_attrs, }; static const struct attribute_group *w1_ds2438_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c index ba7beb7b01f9..889a6099c314 100644 --- a/drivers/w1/slaves/w1_ds2780.c +++ b/drivers/w1/slaves/w1_ds2780.c @@ -103,7 +103,7 @@ static const struct bin_attribute *const w1_ds2780_bin_attrs[] = { }; static const struct attribute_group w1_ds2780_group = { - .bin_attrs_new = w1_ds2780_bin_attrs, + .bin_attrs = w1_ds2780_bin_attrs, }; static const struct attribute_group *w1_ds2780_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c index acd04ee96e81..88f3abd5cd4b 100644 --- a/drivers/w1/slaves/w1_ds2781.c +++ b/drivers/w1/slaves/w1_ds2781.c @@ -100,7 +100,7 @@ static const struct bin_attribute *const w1_ds2781_bin_attrs[] = { }; static const struct attribute_group w1_ds2781_group = { - .bin_attrs_new = w1_ds2781_bin_attrs, + .bin_attrs = w1_ds2781_bin_attrs, }; static const struct attribute_group *w1_ds2781_groups[] = { diff --git a/drivers/w1/slaves/w1_ds2805.c b/drivers/w1/slaves/w1_ds2805.c index 6ee895640d4a..9c86b7985d0b 100644 --- a/drivers/w1/slaves/w1_ds2805.c +++ b/drivers/w1/slaves/w1_ds2805.c @@ -267,8 +267,8 @@ static const struct bin_attribute w1_f0d_bin_attr = { .mode = 0644, }, .size = W1_F0D_EEPROM_SIZE, - .read_new = w1_f0d_read_bin, - .write_new = w1_f0d_write_bin, + .read = w1_f0d_read_bin, + .write = w1_f0d_write_bin, }; static int w1_f0d_add_slave(struct w1_slave *sl) diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c index d99ffadbe29b..c577b5973032 100644 --- a/drivers/w1/slaves/w1_ds28e04.c +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -371,7 +371,7 @@ static const struct bin_attribute *const w1_f1C_bin_attrs[] = { static const struct attribute_group w1_f1C_group = { .attrs = w1_f1C_attrs, - .bin_attrs_new = w1_f1C_bin_attrs, + .bin_attrs = w1_f1C_bin_attrs, }; static const struct attribute_group *w1_f1C_groups[] = { diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 29f200bbab41..d0474a0532ec 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -149,7 +149,7 @@ static const struct bin_attribute *const w1_slave_bin_attrs[] = { }; static const struct attribute_group w1_slave_default_group = { - .bin_attrs_new = w1_slave_bin_attrs, + .bin_attrs = w1_slave_bin_attrs, }; static const struct attribute_group *w1_slave_default_groups[] = { diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index 10aedcd21363..4e967754d8ad 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -104,7 +104,7 @@ static const struct bin_attribute zorro_config_attr = { .mode = S_IRUGO, }, .size = sizeof(struct ConfigDev), - .read_new = zorro_read_config, + .read = zorro_read_config, }; static const struct bin_attribute *const zorro_device_bin_attrs[] = { @@ -114,7 +114,7 @@ static const struct bin_attribute *const zorro_device_bin_attrs[] = { static const struct attribute_group zorro_device_attr_group = { .attrs = zorro_device_attrs, - .bin_attrs_new = zorro_device_bin_attrs, + .bin_attrs = zorro_device_bin_attrs, }; const struct attribute_group *zorro_device_attribute_groups[] = { diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 69e9ddcb113d..3ec3324c2060 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -47,29 +47,12 @@ const struct file_operations debugfs_noop_file_operations = { #define F_DENTRY(filp) ((filp)->f_path.dentry) -const void *debugfs_get_aux(const struct file *file) +void *debugfs_get_aux(const struct file *file) { return DEBUGFS_I(file_inode(file))->aux; } EXPORT_SYMBOL_GPL(debugfs_get_aux); -const struct file_operations *debugfs_real_fops(const struct file *filp) -{ - struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata; - - if (!fsd) { - /* - * Urgh, we've been called w/o a protecting - * debugfs_file_get(). - */ - WARN_ON(1); - return NULL; - } - - return fsd->real_fops; -} -EXPORT_SYMBOL_GPL(debugfs_real_fops); - enum dbgfs_get_mode { DBGFS_GET_ALREADY, DBGFS_GET_REGULAR, @@ -302,15 +285,13 @@ static int debugfs_locked_down(struct inode *inode, static int open_proxy_open(struct inode *inode, struct file *filp) { struct dentry *dentry = F_DENTRY(filp); - const struct file_operations *real_fops = NULL; + const struct file_operations *real_fops = DEBUGFS_I(inode)->real_fops; int r; r = __debugfs_file_get(dentry, DBGFS_GET_REGULAR); if (r) return r == -EIO ? -ENOENT : r; - real_fops = debugfs_real_fops(filp); - r = debugfs_locked_down(inode, filp, real_fops); if (r) goto out; @@ -352,7 +333,6 @@ static ret_type full_proxy_ ## name(proto) \ { \ struct dentry *dentry = F_DENTRY(filp); \ struct debugfs_fsdata *fsd = dentry->d_fsdata; \ - const struct file_operations *real_fops; \ ret_type r; \ \ if (!(fsd->methods & bit)) \ @@ -360,14 +340,13 @@ static ret_type full_proxy_ ## name(proto) \ r = debugfs_file_get(dentry); \ if (unlikely(r)) \ return r; \ - real_fops = debugfs_real_fops(filp); \ - r = real_fops->name(args); \ + r = fsd->real_fops->name(args); \ debugfs_file_put(dentry); \ return r; \ } -#define FULL_PROXY_FUNC_BOTH(name, ret_type, filp, proto, args, bit, ret) \ -static ret_type full_proxy_ ## name(proto) \ +#define SHORT_PROXY_FUNC(name, ret_type, filp, proto, args, bit, ret) \ +static ret_type short_proxy_ ## name(proto) \ { \ struct dentry *dentry = F_DENTRY(filp); \ struct debugfs_fsdata *fsd = dentry->d_fsdata; \ @@ -378,27 +357,38 @@ static ret_type full_proxy_ ## name(proto) \ r = debugfs_file_get(dentry); \ if (unlikely(r)) \ return r; \ - if (fsd->real_fops) \ - r = fsd->real_fops->name(args); \ - else \ - r = fsd->short_fops->name(args); \ + r = fsd->short_fops->name(args); \ debugfs_file_put(dentry); \ return r; \ } -FULL_PROXY_FUNC_BOTH(llseek, loff_t, filp, - PROTO(struct file *filp, loff_t offset, int whence), - ARGS(filp, offset, whence), HAS_LSEEK, -ESPIPE); +SHORT_PROXY_FUNC(llseek, loff_t, filp, + PROTO(struct file *filp, loff_t offset, int whence), + ARGS(filp, offset, whence), HAS_LSEEK, -ESPIPE); -FULL_PROXY_FUNC_BOTH(read, ssize_t, filp, - PROTO(struct file *filp, char __user *buf, size_t size, - loff_t *ppos), - ARGS(filp, buf, size, ppos), HAS_READ, -EINVAL); +FULL_PROXY_FUNC(llseek, loff_t, filp, + PROTO(struct file *filp, loff_t offset, int whence), + ARGS(filp, offset, whence), HAS_LSEEK, -ESPIPE); -FULL_PROXY_FUNC_BOTH(write, ssize_t, filp, - PROTO(struct file *filp, const char __user *buf, - size_t size, loff_t *ppos), - ARGS(filp, buf, size, ppos), HAS_WRITE, -EINVAL); +SHORT_PROXY_FUNC(read, ssize_t, filp, + PROTO(struct file *filp, char __user *buf, size_t size, + loff_t *ppos), + ARGS(filp, buf, size, ppos), HAS_READ, -EINVAL); + +FULL_PROXY_FUNC(read, ssize_t, filp, + PROTO(struct file *filp, char __user *buf, size_t size, + loff_t *ppos), + ARGS(filp, buf, size, ppos), HAS_READ, -EINVAL); + +SHORT_PROXY_FUNC(write, ssize_t, filp, + PROTO(struct file *filp, const char __user *buf, + size_t size, loff_t *ppos), + ARGS(filp, buf, size, ppos), HAS_WRITE, -EINVAL); + +FULL_PROXY_FUNC(write, ssize_t, filp, + PROTO(struct file *filp, const char __user *buf, + size_t size, loff_t *ppos), + ARGS(filp, buf, size, ppos), HAS_WRITE, -EINVAL); FULL_PROXY_FUNC(unlocked_ioctl, long, filp, PROTO(struct file *filp, unsigned int cmd, unsigned long arg), @@ -410,22 +400,21 @@ static __poll_t full_proxy_poll(struct file *filp, struct dentry *dentry = F_DENTRY(filp); struct debugfs_fsdata *fsd = dentry->d_fsdata; __poll_t r = 0; - const struct file_operations *real_fops; if (!(fsd->methods & HAS_POLL)) return DEFAULT_POLLMASK; if (debugfs_file_get(dentry)) return EPOLLHUP; - real_fops = debugfs_real_fops(filp); - r = real_fops->poll(filp, wait); + r = fsd->real_fops->poll(filp, wait); debugfs_file_put(dentry); return r; } -static int full_proxy_release(struct inode *inode, struct file *filp) +static int full_proxy_release(struct inode *inode, struct file *file) { - const struct file_operations *real_fops = debugfs_real_fops(filp); + struct debugfs_fsdata *fsd = F_DENTRY(file)->d_fsdata; + const struct file_operations *real_fops = fsd->real_fops; int r = 0; /* @@ -435,7 +424,7 @@ static int full_proxy_release(struct inode *inode, struct file *filp) * ->i_private is still being meaningful here. */ if (real_fops->release) - r = real_fops->release(inode, filp); + r = real_fops->release(inode, file); fops_put(real_fops); return r; @@ -517,9 +506,9 @@ static int full_proxy_open_short(struct inode *inode, struct file *filp) const struct file_operations debugfs_full_short_proxy_file_operations = { .open = full_proxy_open_short, - .llseek = full_proxy_llseek, - .read = full_proxy_read, - .write = full_proxy_write, + .llseek = short_proxy_llseek, + .read = short_proxy_read, + .write = short_proxy_write, }; ssize_t debugfs_attr_read(struct file *file, char __user *buf, diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 6677991c7e4b..a0357b0cf362 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -444,7 +444,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode, proxy_fops = &debugfs_noop_file_operations; inode->i_fop = proxy_fops; DEBUGFS_I(inode)->raw = real_fops; - DEBUGFS_I(inode)->aux = aux; + DEBUGFS_I(inode)->aux = (void *)aux; d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h index 93483fe84425..427987f81571 100644 --- a/fs/debugfs/internal.h +++ b/fs/debugfs/internal.h @@ -19,7 +19,7 @@ struct debugfs_inode_info { const struct debugfs_short_fops *short_fops; debugfs_automount_t automount; }; - const void *aux; + void *aux; }; static inline struct debugfs_inode_info *DEBUGFS_I(struct inode *inode) diff --git a/fs/resctrl/pseudo_lock.c b/fs/resctrl/pseudo_lock.c index ccc2f9213b4b..87bbc2605de1 100644 --- a/fs/resctrl/pseudo_lock.c +++ b/fs/resctrl/pseudo_lock.c @@ -764,13 +764,9 @@ static ssize_t pseudo_lock_measure_trigger(struct file *file, if (ret == 0) { if (sel != 1 && sel != 2 && sel != 3) return -EINVAL; - ret = debugfs_file_get(file->f_path.dentry); - if (ret) - return ret; ret = pseudo_lock_measure_cycles(rdtgrp, sel); if (ret == 0) ret = count; - debugfs_file_put(file->f_path.dentry); } return ret; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index c3d3b079aedd..1ca143d2f22a 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -83,7 +83,7 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf, size_t count, loff_t pos) { - struct bin_attribute *battr = of->kn->priv; + const struct bin_attribute *battr = of->kn->priv; struct kobject *kobj = sysfs_file_kobj(of->kn); loff_t size = file_inode(of->file)->i_size; @@ -149,7 +149,7 @@ static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf, static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf, size_t count, loff_t pos) { - struct bin_attribute *battr = of->kn->priv; + const struct bin_attribute *battr = of->kn->priv; struct kobject *kobj = sysfs_file_kobj(of->kn); loff_t size = file_inode(of->file)->i_size; @@ -173,7 +173,7 @@ static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf, static int sysfs_kf_bin_mmap(struct kernfs_open_file *of, struct vm_area_struct *vma) { - struct bin_attribute *battr = of->kn->priv; + const struct bin_attribute *battr = of->kn->priv; struct kobject *kobj = sysfs_file_kobj(of->kn); return battr->mmap(of->file, kobj, battr, vma); @@ -182,7 +182,7 @@ static int sysfs_kf_bin_mmap(struct kernfs_open_file *of, static loff_t sysfs_kf_bin_llseek(struct kernfs_open_file *of, loff_t offset, int whence) { - struct bin_attribute *battr = of->kn->priv; + const struct bin_attribute *battr = of->kn->priv; struct kobject *kobj = sysfs_file_kobj(of->kn); if (battr->llseek) @@ -193,7 +193,7 @@ static loff_t sysfs_kf_bin_llseek(struct kernfs_open_file *of, loff_t offset, static int sysfs_kf_bin_open(struct kernfs_open_file *of) { - struct bin_attribute *battr = of->kn->priv; + const struct bin_attribute *battr = of->kn->priv; if (battr->f_mapping) of->file->f_mapping = battr->f_mapping(); diff --git a/include/kunit/try-catch.h b/include/kunit/try-catch.h index 7c966a1adbd3..d4e1a5b98ed6 100644 --- a/include/kunit/try-catch.h +++ b/include/kunit/try-catch.h @@ -47,6 +47,7 @@ struct kunit_try_catch { int try_result; kunit_try_catch_func_t try; kunit_try_catch_func_t catch; + unsigned long timeout; void *context; }; diff --git a/include/linux/container_of.h b/include/linux/container_of.h index 713890c867be..1f6ebf27d962 100644 --- a/include/linux/container_of.h +++ b/include/linux/container_of.h @@ -14,6 +14,7 @@ * @member: the name of the member within the struct. * * WARNING: any const qualifier of @ptr is lost. + * Do not use container_of() in new code. */ #define container_of(ptr, type, member) ({ \ void *__mptr = (void *)(ptr); \ @@ -28,6 +29,8 @@ * @ptr: the pointer to the member * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. + * + * Always prefer container_of_const() instead of container_of() in new code. */ #define container_of_const(ptr, type, member) \ _Generic(ptr, \ diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index fa2568b4380d..7cecda29447e 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -162,8 +162,7 @@ void debugfs_remove(struct dentry *dentry); void debugfs_lookup_and_remove(const char *name, struct dentry *parent); -const struct file_operations *debugfs_real_fops(const struct file *filp); -const void *debugfs_get_aux(const struct file *file); +void *debugfs_get_aux(const struct file *file); int debugfs_file_get(struct dentry *dentry); void debugfs_file_put(struct dentry *dentry); @@ -329,7 +328,6 @@ static inline void debugfs_lookup_and_remove(const char *name, struct dentry *parent) { } -const struct file_operations *debugfs_real_fops(const struct file *filp); void *debugfs_get_aux(const struct file *file); static inline int debugfs_file_get(struct dentry *dentry) diff --git a/include/linux/device.h b/include/linux/device.h index 5137f9d213ec..0470d19da7f2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -944,6 +944,18 @@ static inline bool dev_has_sync_state(struct device *dev) return false; } +static inline int dev_set_drv_sync_state(struct device *dev, + void (*fn)(struct device *dev)) +{ + if (!dev || !dev->driver) + return 0; + if (dev->driver->sync_state && dev->driver->sync_state != fn) + return -EBUSY; + if (!dev->driver->sync_state) + dev->driver->sync_state = fn; + return 0; +} + static inline void dev_set_removable(struct device *dev, enum device_removable removable) { @@ -1075,6 +1087,7 @@ void device_set_node(struct device *dev, struct fwnode_handle *fwnode); int device_add_of_node(struct device *dev, struct device_node *of_node); void device_remove_of_node(struct device *dev); void device_set_of_node_from_dev(struct device *dev, const struct device *dev2); +struct device *get_dev_from_fwnode(struct fwnode_handle *fwnode); static inline struct device_node *dev_of_node(struct device *dev) { @@ -1189,6 +1202,11 @@ void device_links_supplier_sync_state_pause(void); void device_links_supplier_sync_state_resume(void); void device_link_wait_removal(void); +static inline bool device_link_test(const struct device_link *link, u32 flags) +{ + return !!(link->flags & flags); +} + /* Create alias, so I can be autoloaded. */ #define MODULE_ALIAS_CHARDEV(major,minor) \ MODULE_ALIAS("char-major-" __stringify(major) "-" __stringify(minor)) diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 6d4dbc196b93..ae48d619c4e0 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -585,7 +585,6 @@ int zynqmp_pm_reset_assert(const u32 reset, int zynqmp_pm_reset_get_status(const u32 reset, u32 *status); unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode); int zynqmp_pm_bootmode_write(u32 ps_mode); -int zynqmp_pm_init_finalize(void); int zynqmp_pm_set_suspend_mode(u32 mode); int zynqmp_pm_request_node(const u32 node, const u32 capabilities, const u32 qos, const enum zynqmp_pm_request_ack ack); @@ -746,11 +745,6 @@ static inline int zynqmp_pm_bootmode_write(u32 ps_mode) return -ENODEV; } -static inline int zynqmp_pm_init_finalize(void) -{ - return -ENODEV; -} - static inline int zynqmp_pm_set_suspend_mode(u32 mode) { return -ENODEV; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 62a35a78ce9b..c84edf217819 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -110,6 +110,11 @@ struct dev_pm_domain_list { * GENPD_FLAG_DEV_NAME_FW: Instructs genpd to generate an unique device name * using ida. It is used by genpd providers which * get their genpd-names directly from FW. + * + * GENPD_FLAG_NO_SYNC_STATE: The ->sync_state() support is implemented in a + * genpd provider specific way, likely through a + * parent device node. This flag makes genpd to + * skip its internal support for this. */ #define GENPD_FLAG_PM_CLK (1U << 0) #define GENPD_FLAG_IRQ_SAFE (1U << 1) @@ -120,6 +125,7 @@ struct dev_pm_domain_list { #define GENPD_FLAG_MIN_RESIDENCY (1U << 6) #define GENPD_FLAG_OPP_TABLE_FW (1U << 7) #define GENPD_FLAG_DEV_NAME_FW (1U << 8) +#define GENPD_FLAG_NO_SYNC_STATE (1U << 9) enum gpd_status { GENPD_STATE_ON = 0, /* PM domain is on */ @@ -133,6 +139,12 @@ enum genpd_notication { GENPD_NOTIFY_ON, }; +enum genpd_sync_state { + GENPD_SYNC_STATE_OFF = 0, + GENPD_SYNC_STATE_SIMPLE, + GENPD_SYNC_STATE_ONECELL, +}; + struct dev_power_governor { bool (*power_down_ok)(struct dev_pm_domain *domain); bool (*suspend_ok)(struct device *dev); @@ -193,6 +205,8 @@ struct generic_pm_domain { unsigned int performance_state; /* Aggregated max performance state */ cpumask_var_t cpus; /* A cpumask of the attached CPUs */ bool synced_poweroff; /* A consumer needs a synced poweroff */ + bool stay_on; /* Stay powered-on during boot. */ + enum genpd_sync_state sync_state; /* How sync_state is managed. */ int (*power_off)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain); struct raw_notifier_head power_notifiers; /* Power on/off notifiers */ @@ -307,6 +321,7 @@ void dev_pm_genpd_synced_poweroff(struct device *dev); int dev_pm_genpd_set_hwmode(struct device *dev, bool enable); bool dev_pm_genpd_get_hwmode(struct device *dev); int dev_pm_genpd_rpm_always_on(struct device *dev, bool on); +bool dev_pm_genpd_is_on(struct device *dev); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -399,6 +414,11 @@ static inline int dev_pm_genpd_rpm_always_on(struct device *dev, bool on) return -EOPNOTSUPP; } +static inline bool dev_pm_genpd_is_on(struct device *dev) +{ + return false; +} + #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) #endif @@ -437,6 +457,7 @@ int of_genpd_remove_subdomain(const struct of_phandle_args *parent_spec, struct generic_pm_domain *of_genpd_remove_last(struct device_node *np); int of_genpd_parse_idle_states(struct device_node *dn, struct genpd_power_state **states, int *n); +void of_genpd_sync_state(struct device_node *np); int genpd_dev_pm_attach(struct device *dev); struct device *genpd_dev_pm_attach_by_id(struct device *dev, @@ -482,6 +503,8 @@ static inline int of_genpd_parse_idle_states(struct device_node *dn, return -ENODEV; } +static inline void of_genpd_sync_state(struct device_node *np) {} + static inline int genpd_dev_pm_attach(struct device *dev) { return 0; diff --git a/include/linux/property.h b/include/linux/property.h index f718dd4789e5..82f0cb3abd1e 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -17,6 +17,7 @@ #include #include #include +#include struct device; @@ -169,7 +170,7 @@ struct fwnode_handle *fwnode_get_next_available_child_node( #define fwnode_for_each_named_child_node(fwnode, child, name) \ fwnode_for_each_child_node(fwnode, child) \ - if (!fwnode_name_eq(child, name)) { } else + for_each_if(fwnode_name_eq(child, name)) #define fwnode_for_each_available_child_node(fwnode, child) \ for (child = fwnode_get_next_available_child_node(fwnode, NULL); child;\ @@ -184,7 +185,7 @@ struct fwnode_handle *device_get_next_child_node(const struct device *dev, #define device_for_each_named_child_node(dev, child, name) \ device_for_each_child_node(dev, child) \ - if (!fwnode_name_eq(child, name)) { } else + for_each_if(fwnode_name_eq(child, name)) #define device_for_each_child_node_scoped(dev, child) \ for (struct fwnode_handle *child __free(fwnode_handle) = \ @@ -193,7 +194,7 @@ struct fwnode_handle *device_get_next_child_node(const struct device *dev, #define device_for_each_named_child_node_scoped(dev, child, name) \ device_for_each_child_node_scoped(dev, child) \ - if (!fwnode_name_eq(child, name)) { } else + for_each_if(fwnode_name_eq(child, name)) struct fwnode_handle *fwnode_get_named_child_node(const struct fwnode_handle *fwnode, const char *childname); diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h index 92326ebde350..a2db2a56c8b0 100644 --- a/include/uapi/linux/i2c.h +++ b/include/uapi/linux/i2c.h @@ -21,7 +21,8 @@ * * @flags: * Supported by all adapters: - * %I2C_M_RD: read data (from slave to master). Guaranteed to be 0x0001! + * %I2C_M_RD: read data (from slave to master). Guaranteed to be 0x0001! If + * not set, the transaction is interpreted as write. * * Optional: * %I2C_M_DMA_SAFE: the buffer of this message is DMA safe. Makes only sense diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 1d2cf898e21e..e8e63bd025c7 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -8183,7 +8183,7 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op, attr->attr.mode = 0444; attr->size = btf->data_size; attr->private = btf->data; - attr->read_new = sysfs_bin_attr_simple_read; + attr->read = sysfs_bin_attr_simple_read; err = sysfs_create_bin_file(btf_kobj, attr); if (err) { diff --git a/kernel/bpf/sysfs_btf.c b/kernel/bpf/sysfs_btf.c index 8e61dc555415..9cbe15ce3540 100644 --- a/kernel/bpf/sysfs_btf.c +++ b/kernel/bpf/sysfs_btf.c @@ -45,7 +45,7 @@ static int btf_sysfs_vmlinux_mmap(struct file *filp, struct kobject *kobj, static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = { .attr = { .name = "vmlinux", .mode = 0444, }, - .read_new = sysfs_bin_attr_simple_read, + .read = sysfs_bin_attr_simple_read, .mmap = btf_sysfs_vmlinux_mmap, }; diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c index b401ff4b02d2..c7622ff5226a 100644 --- a/kernel/module/sysfs.c +++ b/kernel/module/sysfs.c @@ -56,9 +56,9 @@ static void free_sect_attrs(struct module_sect_attrs *sect_attrs) { const struct bin_attribute *const *bin_attr; - for (bin_attr = sect_attrs->grp.bin_attrs_new; *bin_attr; bin_attr++) + for (bin_attr = sect_attrs->grp.bin_attrs; *bin_attr; bin_attr++) kfree((*bin_attr)->attr.name); - kfree(sect_attrs->grp.bin_attrs_new); + kfree(sect_attrs->grp.bin_attrs); kfree(sect_attrs); } @@ -86,7 +86,7 @@ static int add_sect_attrs(struct module *mod, const struct load_info *info) /* Setup section attributes. */ sect_attrs->grp.name = "sections"; - sect_attrs->grp.bin_attrs_new = gattr; + sect_attrs->grp.bin_attrs = gattr; sattr = §_attrs->attrs[0]; for (i = 0; i < info->hdr->e_shnum; i++) { @@ -101,7 +101,7 @@ static int add_sect_attrs(struct module *mod, const struct load_info *info) ret = -ENOMEM; goto out; } - sattr->read_new = module_sect_read; + sattr->read = module_sect_read; sattr->private = (void *)sec->sh_addr; sattr->size = MODULE_SECT_READ_SIZE; sattr->attr.mode = 0400; @@ -144,7 +144,7 @@ struct module_notes_attrs { static void free_notes_attrs(struct module_notes_attrs *notes_attrs) { - kfree(notes_attrs->grp.bin_attrs_new); + kfree(notes_attrs->grp.bin_attrs); kfree(notes_attrs); } @@ -178,7 +178,7 @@ static int add_notes_attrs(struct module *mod, const struct load_info *info) } notes_attrs->grp.name = "notes"; - notes_attrs->grp.bin_attrs_new = gattr; + notes_attrs->grp.bin_attrs = gattr; nattr = ¬es_attrs->attrs[0]; for (loaded = i = 0; i < info->hdr->e_shnum; ++i) { @@ -190,7 +190,7 @@ static int add_notes_attrs(struct module *mod, const struct load_info *info) nattr->attr.mode = 0444; nattr->size = info->sechdrs[i].sh_size; nattr->private = (void *)info->sechdrs[i].sh_addr; - nattr->read_new = sysfs_bin_attr_simple_read; + nattr->read = sysfs_bin_attr_simple_read; *(gattr++) = nattr++; } ++loaded; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ce50be42b166..0a1881c07b16 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2900,6 +2900,7 @@ config FORTIFY_KUNIT_TEST config LONGEST_SYM_KUNIT_TEST tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS depends on KUNIT && KPROBES + depends on !PREFIX_SYMBOLS && !CFI_CLANG && !GCOV_KERNEL default KUNIT_ALL_TESTS help Tests the longest symbol possible diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index a97897edd964..c10ede4b1d22 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -93,4 +93,17 @@ config KUNIT_AUTORUN_ENABLED In most cases this should be left as Y. Only if additional opt-in behavior is needed should this be set to N. +config KUNIT_DEFAULT_TIMEOUT + int "Default value of the timeout module parameter" + default 300 + help + Sets the default timeout, in seconds, for Kunit test cases. This value + is further multiplied by a factor determined by the assigned speed + setting: 1x for `DEFAULT`, 3x for `KUNIT_SPEED_SLOW`, and 12x for + `KUNIT_SPEED_VERY_SLOW`. This allows slower tests on slower machines + sufficient time to complete. + + If unsure, the default timeout of 300 seconds is suitable for most + cases. + endif # KUNIT diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c index d9c781c859fd..8c01eabd4eaf 100644 --- a/lib/kunit/kunit-test.c +++ b/lib/kunit/kunit-test.c @@ -8,6 +8,7 @@ #include "linux/gfp_types.h" #include #include +#include #include #include @@ -43,7 +44,8 @@ static void kunit_test_try_catch_successful_try_no_catch(struct kunit *test) kunit_try_catch_init(try_catch, test, kunit_test_successful_try, - kunit_test_no_catch); + kunit_test_no_catch, + 300 * msecs_to_jiffies(MSEC_PER_SEC)); kunit_try_catch_run(try_catch, test); KUNIT_EXPECT_TRUE(test, ctx->function_called); @@ -75,7 +77,8 @@ static void kunit_test_try_catch_unsuccessful_try_does_catch(struct kunit *test) kunit_try_catch_init(try_catch, test, kunit_test_unsuccessful_try, - kunit_test_catch); + kunit_test_catch, + 300 * msecs_to_jiffies(MSEC_PER_SEC)); kunit_try_catch_run(try_catch, test); KUNIT_EXPECT_TRUE(test, ctx->function_called); @@ -129,7 +132,8 @@ static void kunit_test_fault_null_dereference(struct kunit *test) kunit_try_catch_init(try_catch, test, kunit_test_null_dereference, - kunit_test_catch); + kunit_test_catch, + 300 * msecs_to_jiffies(MSEC_PER_SEC)); kunit_try_catch_run(try_catch, test); KUNIT_EXPECT_EQ(test, try_catch->try_result, -EINTR); @@ -868,10 +872,53 @@ static struct kunit_suite kunit_current_test_suite = { .test_cases = kunit_current_test_cases, }; +static void kunit_stub_test(struct kunit *test) +{ + struct kunit fake_test; + const unsigned long fake_real_fn_addr = 0x1234; + const unsigned long fake_replacement_addr = 0x5678; + struct kunit_resource *res; + struct { + void *real_fn_addr; + void *replacement_addr; + } *stub_ctx; + + kunit_init_test(&fake_test, "kunit_stub_fake_test", NULL); + KUNIT_ASSERT_EQ(test, fake_test.status, KUNIT_SUCCESS); + KUNIT_ASSERT_EQ(test, list_count_nodes(&fake_test.resources), 0); + + __kunit_activate_static_stub(&fake_test, (void *)fake_real_fn_addr, + (void *)fake_replacement_addr); + KUNIT_ASSERT_EQ(test, fake_test.status, KUNIT_SUCCESS); + KUNIT_ASSERT_EQ(test, list_count_nodes(&fake_test.resources), 1); + + res = list_first_entry(&fake_test.resources, struct kunit_resource, node); + KUNIT_EXPECT_NOT_NULL(test, res); + + stub_ctx = res->data; + KUNIT_EXPECT_NOT_NULL(test, stub_ctx); + KUNIT_EXPECT_EQ(test, (unsigned long)stub_ctx->real_fn_addr, fake_real_fn_addr); + KUNIT_EXPECT_EQ(test, (unsigned long)stub_ctx->replacement_addr, fake_replacement_addr); + + __kunit_activate_static_stub(&fake_test, (void *)fake_real_fn_addr, NULL); + KUNIT_ASSERT_EQ(test, fake_test.status, KUNIT_SUCCESS); + KUNIT_ASSERT_EQ(test, list_count_nodes(&fake_test.resources), 0); +} + +static struct kunit_case kunit_stub_test_cases[] = { + KUNIT_CASE(kunit_stub_test), + {} +}; + +static struct kunit_suite kunit_stub_test_suite = { + .name = "kunit_stub", + .test_cases = kunit_stub_test_cases, +}; + kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite, &kunit_log_test_suite, &kunit_status_test_suite, &kunit_current_test_suite, &kunit_device_test_suite, - &kunit_fault_test_suite); + &kunit_fault_test_suite, &kunit_stub_test_suite); MODULE_DESCRIPTION("KUnit test for core test infrastructure"); MODULE_LICENSE("GPL v2"); diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 146d1b48a096..f3c6b11f12b8 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -69,6 +69,13 @@ static bool enable_param; module_param_named(enable, enable_param, bool, 0); MODULE_PARM_DESC(enable, "Enable KUnit tests"); +/* + * Configure the base timeout. + */ +static unsigned long kunit_base_timeout = CONFIG_KUNIT_DEFAULT_TIMEOUT; +module_param_named(timeout, kunit_base_timeout, ulong, 0644); +MODULE_PARM_DESC(timeout, "Set the base timeout for Kunit test cases"); + /* * KUnit statistic mode: * 0 - disabled @@ -373,6 +380,40 @@ static void kunit_run_case_check_speed(struct kunit *test, duration.tv_sec, duration.tv_nsec); } +/* Returns timeout multiplier based on speed. + * DEFAULT: 1 + * KUNIT_SPEED_SLOW: 3 + * KUNIT_SPEED_VERY_SLOW: 12 + */ +static int kunit_timeout_mult(enum kunit_speed speed) +{ + switch (speed) { + case KUNIT_SPEED_SLOW: + return 3; + case KUNIT_SPEED_VERY_SLOW: + return 12; + default: + return 1; + } +} + +static unsigned long kunit_test_timeout(struct kunit_suite *suite, struct kunit_case *test_case) +{ + int mult = 1; + + /* + * The default test timeout is 300 seconds and will be adjusted by mult + * based on the test speed. The test speed will be overridden by the + * innermost test component. + */ + if (suite->attr.speed != KUNIT_SPEED_UNSET) + mult = kunit_timeout_mult(suite->attr.speed); + if (test_case->attr.speed != KUNIT_SPEED_UNSET) + mult = kunit_timeout_mult(test_case->attr.speed); + return mult * kunit_base_timeout * msecs_to_jiffies(MSEC_PER_SEC); +} + + /* * Initializes and runs test case. Does not clean up or do post validations. */ @@ -527,7 +568,8 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite, kunit_try_catch_init(try_catch, test, kunit_try_run_case, - kunit_catch_run_case); + kunit_catch_run_case, + kunit_test_timeout(suite, test_case)); context.test = test; context.suite = suite; context.test_case = test_case; @@ -537,7 +579,8 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite, kunit_try_catch_init(try_catch, test, kunit_try_run_case_cleanup, - kunit_catch_run_case_cleanup); + kunit_catch_run_case_cleanup, + kunit_test_timeout(suite, test_case)); kunit_try_catch_run(try_catch, &context); /* Propagate the parameter result to the test case. */ diff --git a/lib/kunit/try-catch-impl.h b/lib/kunit/try-catch-impl.h index 203ba6a5e740..6f401b97cd0b 100644 --- a/lib/kunit/try-catch-impl.h +++ b/lib/kunit/try-catch-impl.h @@ -17,11 +17,13 @@ struct kunit; static inline void kunit_try_catch_init(struct kunit_try_catch *try_catch, struct kunit *test, kunit_try_catch_func_t try, - kunit_try_catch_func_t catch) + kunit_try_catch_func_t catch, + unsigned long timeout) { try_catch->test = test; try_catch->try = try; try_catch->catch = catch; + try_catch->timeout = timeout; } #endif /* _KUNIT_TRY_CATCH_IMPL_H */ diff --git a/lib/kunit/try-catch.c b/lib/kunit/try-catch.c index 6bbe0025b079..d84a879f0a78 100644 --- a/lib/kunit/try-catch.c +++ b/lib/kunit/try-catch.c @@ -34,31 +34,6 @@ static int kunit_generic_run_threadfn_adapter(void *data) return 0; } -static unsigned long kunit_test_timeout(void) -{ - /* - * TODO(brendanhiggins@google.com): We should probably have some type of - * variable timeout here. The only question is what that timeout value - * should be. - * - * The intention has always been, at some point, to be able to label - * tests with some type of size bucket (unit/small, integration/medium, - * large/system/end-to-end, etc), where each size bucket would get a - * default timeout value kind of like what Bazel does: - * https://docs.bazel.build/versions/master/be/common-definitions.html#test.size - * There is still some debate to be had on exactly how we do this. (For - * one, we probably want to have some sort of test runner level - * timeout.) - * - * For more background on this topic, see: - * https://mike-bland.com/2011/11/01/small-medium-large.html - * - * If tests timeout due to exceeding sysctl_hung_task_timeout_secs, - * the task will be killed and an oops generated. - */ - return 300 * msecs_to_jiffies(MSEC_PER_SEC); /* 5 min */ -} - void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context) { struct kunit *test = try_catch->test; @@ -85,8 +60,8 @@ void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context) task_done = task_struct->vfork_done; wake_up_process(task_struct); - time_remaining = wait_for_completion_timeout(task_done, - kunit_test_timeout()); + time_remaining = wait_for_completion_timeout( + task_done, try_catch->timeout); if (time_remaining == 0) { try_catch->try_result = -ETIMEDOUT; kthread_stop(task_struct); diff --git a/lib/tests/longest_symbol_kunit.c b/lib/tests/longest_symbol_kunit.c index e3c28ff1807f..9b4de3050ba7 100644 --- a/lib/tests/longest_symbol_kunit.c +++ b/lib/tests/longest_symbol_kunit.c @@ -3,8 +3,7 @@ * Test the longest symbol length. Execute with: * ./tools/testing/kunit/kunit.py run longest-symbol * --arch=x86_64 --kconfig_add CONFIG_KPROBES=y --kconfig_add CONFIG_MODULES=y - * --kconfig_add CONFIG_RETPOLINE=n --kconfig_add CONFIG_CFI_CLANG=n - * --kconfig_add CONFIG_MITIGATION_RETPOLINE=n + * --kconfig_add CONFIG_CPU_MITIGATIONS=n --kconfig_add CONFIG_GCOV_KERNEL=n */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/mm/page_idle.c b/mm/page_idle.c index 408aaf29a3ea..a82b340dc204 100644 --- a/mm/page_idle.c +++ b/mm/page_idle.c @@ -208,7 +208,7 @@ static const struct bin_attribute *const page_idle_bin_attrs[] = { }; static const struct attribute_group page_idle_attr_group = { - .bin_attrs_new = page_idle_bin_attrs, + .bin_attrs = page_idle_bin_attrs, .name = "page_idle", }; diff --git a/mm/vmscan.c b/mm/vmscan.c index 543afe9913d8..ac77030e9ac4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -5434,7 +5434,7 @@ static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, static int lru_gen_seq_show(struct seq_file *m, void *v) { unsigned long seq; - bool full = !debugfs_real_fops(m->file)->write; + bool full = debugfs_get_aux_num(m->file); struct lruvec *lruvec = v; struct lru_gen_folio *lrugen = &lruvec->lrugen; int nid = lruvec_pgdat(lruvec)->node_id; @@ -5770,8 +5770,10 @@ static int __init init_lru_gen(void) if (sysfs_create_group(mm_kobj, &lru_gen_attr_group)) pr_err("lru_gen: failed to create sysfs group\n"); - debugfs_create_file("lru_gen", 0644, NULL, NULL, &lru_gen_rw_fops); - debugfs_create_file("lru_gen_full", 0444, NULL, NULL, &lru_gen_ro_fops); + debugfs_create_file_aux_num("lru_gen", 0644, NULL, NULL, 1, + &lru_gen_rw_fops); + debugfs_create_file_aux_num("lru_gen_full", 0444, NULL, NULL, 0, + &lru_gen_ro_fops); return 0; }; diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index c1176a5e02c4..cb4855ed9500 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -1026,7 +1026,7 @@ static ssize_t brforward_read(struct file *filp, struct kobject *kobj, static const struct bin_attribute bridge_forward = { .attr = { .name = SYSFS_BRIDGE_FDB, .mode = 0444, }, - .read_new = brforward_read, + .read = brforward_read, }; /* diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 2d51f9d05609..84d60635e8a9 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -28,6 +28,7 @@ */ #include +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/auxiliary.c b/rust/helpers/auxiliary.c index 0db3860d774e..8b5e0fea4493 100644 --- a/rust/helpers/auxiliary.c +++ b/rust/helpers/auxiliary.c @@ -2,16 +2,6 @@ #include -void rust_helper_auxiliary_set_drvdata(struct auxiliary_device *adev, void *data) -{ - auxiliary_set_drvdata(adev, data); -} - -void *rust_helper_auxiliary_get_drvdata(struct auxiliary_device *adev) -{ - return auxiliary_get_drvdata(adev); -} - void rust_helper_auxiliary_device_uninit(struct auxiliary_device *adev) { return auxiliary_device_uninit(adev); diff --git a/rust/helpers/device.c b/rust/helpers/device.c index b2135c6686b0..9a4316bafedf 100644 --- a/rust/helpers/device.c +++ b/rust/helpers/device.c @@ -8,3 +8,20 @@ int rust_helper_devm_add_action(struct device *dev, { return devm_add_action(dev, action, data); } + +int rust_helper_devm_add_action_or_reset(struct device *dev, + void (*action)(void *), + void *data) +{ + return devm_add_action_or_reset(dev, action, data); +} + +void *rust_helper_dev_get_drvdata(const struct device *dev) +{ + return dev_get_drvdata(dev); +} + +void rust_helper_dev_set_drvdata(struct device *dev, void *data) +{ + dev_set_drvdata(dev, data); +} diff --git a/rust/helpers/dma.c b/rust/helpers/dma.c index df8b8a77355a..6e741c197242 100644 --- a/rust/helpers/dma.c +++ b/rust/helpers/dma.c @@ -14,3 +14,8 @@ void rust_helper_dma_free_attrs(struct device *dev, size_t size, void *cpu_addr, { dma_free_attrs(dev, size, cpu_addr, dma_handle, attrs); } + +int rust_helper_dma_set_mask_and_coherent(struct device *dev, u64 mask) +{ + return dma_set_mask_and_coherent(dev, mask); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 2593c38491ca..2bb13285825b 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -28,11 +28,13 @@ #include "kunit.c" #include "mm.c" #include "mutex.c" +#include "of.c" #include "page.c" #include "platform.c" #include "pci.c" #include "pid_namespace.c" #include "poll.c" +#include "property.c" #include "rbtree.c" #include "regulator.c" #include "rcu.c" diff --git a/rust/helpers/io.c b/rust/helpers/io.c index 15ea187c5466..c475913c69e6 100644 --- a/rust/helpers/io.c +++ b/rust/helpers/io.c @@ -1,12 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size) { return ioremap(offset, size); } +void __iomem *rust_helper_ioremap_np(phys_addr_t offset, size_t size) +{ + return ioremap_np(offset, size); +} + void rust_helper_iounmap(void __iomem *addr) { iounmap(addr); @@ -99,3 +105,38 @@ void rust_helper_writeq_relaxed(u64 value, void __iomem *addr) writeq_relaxed(value, addr); } #endif + +resource_size_t rust_helper_resource_size(struct resource *res) +{ + return resource_size(res); +} + +struct resource *rust_helper_request_mem_region(resource_size_t start, + resource_size_t n, + const char *name) +{ + return request_mem_region(start, n, name); +} + +void rust_helper_release_mem_region(resource_size_t start, resource_size_t n) +{ + release_mem_region(start, n); +} + +struct resource *rust_helper_request_region(resource_size_t start, + resource_size_t n, const char *name) +{ + return request_region(start, n, name); +} + +struct resource *rust_helper_request_muxed_region(resource_size_t start, + resource_size_t n, + const char *name) +{ + return request_muxed_region(start, n, name); +} + +void rust_helper_release_region(resource_size_t start, resource_size_t n) +{ + release_region(start, n); +} diff --git a/rust/helpers/of.c b/rust/helpers/of.c new file mode 100644 index 000000000000..86b51167c913 --- /dev/null +++ b/rust/helpers/of.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +bool rust_helper_is_of_node(const struct fwnode_handle *fwnode) +{ + return is_of_node(fwnode); +} diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c index cd0e6bf2cc4d..ef9cb38c81a6 100644 --- a/rust/helpers/pci.c +++ b/rust/helpers/pci.c @@ -2,16 +2,6 @@ #include -void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) -{ - pci_set_drvdata(pdev, data); -} - -void *rust_helper_pci_get_drvdata(struct pci_dev *pdev) -{ - return pci_get_drvdata(pdev); -} - resource_size_t rust_helper_pci_resource_len(struct pci_dev *pdev, int bar) { return pci_resource_len(pdev, bar); diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c index 82171233d12f..1ce89c1a36f7 100644 --- a/rust/helpers/platform.c +++ b/rust/helpers/platform.c @@ -2,16 +2,6 @@ #include -void *rust_helper_platform_get_drvdata(const struct platform_device *pdev) -{ - return platform_get_drvdata(pdev); -} - -void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data) -{ - platform_set_drvdata(pdev, data); -} - bool rust_helper_dev_is_platform(const struct device *dev) { return dev_is_platform(dev); diff --git a/rust/helpers/property.c b/rust/helpers/property.c new file mode 100644 index 000000000000..08f68e2dac4a --- /dev/null +++ b/rust/helpers/property.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_fwnode_handle_put(struct fwnode_handle *fwnode) +{ + fwnode_handle_put(fwnode); +} diff --git a/rust/kernel/acpi.rs b/rust/kernel/acpi.rs new file mode 100644 index 000000000000..7ae317368b00 --- /dev/null +++ b/rust/kernel/acpi.rs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Advanced Configuration and Power Interface abstractions. + +use crate::{ + bindings, + device_id::{RawDeviceId, RawDeviceIdIndex}, + prelude::*, +}; + +/// IdTable type for ACPI drivers. +pub type IdTable = &'static dyn kernel::device_id::IdTable; + +/// An ACPI device id. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct DeviceId(bindings::acpi_device_id); + +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `acpi_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. +unsafe impl RawDeviceId for DeviceId { + type RawType = bindings::acpi_device_id; +} + +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceIdIndex for DeviceId { + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::acpi_device_id, driver_data); + + fn index(&self) -> usize { + self.0.driver_data + } +} + +impl DeviceId { + const ACPI_ID_LEN: usize = 16; + + /// Create a new device id from an ACPI 'id' string. + #[inline(always)] + pub const fn new(id: &'static CStr) -> Self { + build_assert!( + id.len_with_nul() <= Self::ACPI_ID_LEN, + "ID exceeds 16 bytes" + ); + let src = id.as_bytes_with_nul(); + // Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. + // SAFETY: FFI type is valid to be zero-initialized. + let mut acpi: bindings::acpi_device_id = unsafe { core::mem::zeroed() }; + let mut i = 0; + while i < src.len() { + acpi.id[i] = src[i]; + i += 1; + } + + Self(acpi) + } +} + +/// Create an ACPI `IdTable` with an "alias" for modpost. +#[macro_export] +macro_rules! acpi_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { + const $table_name: $crate::device_id::IdArray< + $crate::acpi::DeviceId, + $id_info_type, + { $table_data.len() }, + > = $crate::device_id::IdArray::new($table_data); + + $crate::module_device_table!("acpi", $module_table_name, $table_name); + }; +} diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index d2cfe1eeefb6..4749fb6bffef 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -6,12 +6,11 @@ use crate::{ bindings, container_of, device, - device_id::RawDeviceId, + device_id::{RawDeviceId, RawDeviceIdIndex}, driver, - error::{to_result, Result}, + error::{from_result, to_result, Result}, prelude::*, - str::CStr, - types::{ForeignOwnable, Opaque}, + types::Opaque, ThisModule, }; use core::{ @@ -61,37 +60,32 @@ impl Adapter { // `struct auxiliary_device`. // // INVARIANT: `adev` is valid for the duration of `probe_callback()`. - let adev = unsafe { &*adev.cast::>() }; + let adev = unsafe { &*adev.cast::>() }; // SAFETY: `DeviceId` is a `#[repr(transparent)`] wrapper of `struct auxiliary_device_id` // and does not add additional invariants, so it's safe to transmute. let id = unsafe { &*id.cast::() }; let info = T::ID_TABLE.info(id.index()); - match T::probe(adev, info) { - Ok(data) => { - // Let the `struct auxiliary_device` own a reference of the driver's private data. - // SAFETY: By the type invariant `adev.as_raw` returns a valid pointer to a - // `struct auxiliary_device`. - unsafe { - bindings::auxiliary_set_drvdata(adev.as_raw(), data.into_foreign().cast()) - }; - } - Err(err) => return Error::to_errno(err), - } + from_result(|| { + let data = T::probe(adev, info)?; - 0 + adev.as_ref().set_drvdata(data); + Ok(0) + }) } extern "C" fn remove_callback(adev: *mut bindings::auxiliary_device) { - // SAFETY: The auxiliary bus only ever calls the remove callback with a valid pointer to a + // SAFETY: The auxiliary bus only ever calls the probe callback with a valid pointer to a // `struct auxiliary_device`. - let ptr = unsafe { bindings::auxiliary_get_drvdata(adev) }; + // + // INVARIANT: `adev` is valid for the duration of `probe_callback()`. + let adev = unsafe { &*adev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to - // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized - // `KBox` pointer created through `KBox::into_foreign`. - drop(unsafe { KBox::::from_foreign(ptr.cast()) }); + // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called + // and stored a `Pin>`. + drop(unsafe { adev.as_ref().drvdata_obtain::>>() }); } } @@ -140,13 +134,14 @@ impl DeviceId { } } -// SAFETY: -// * `DeviceId` is a `#[repr(transparent)`] wrapper of `auxiliary_device_id` and does not add -// additional invariants, so it's safe to transmute to `RawType`. -// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `auxiliary_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. unsafe impl RawDeviceId for DeviceId { type RawType = bindings::auxiliary_device_id; +} +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceIdIndex for DeviceId { const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::auxiliary_device_id, driver_data); @@ -276,7 +271,7 @@ impl AsRef> for Device { let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) }; // SAFETY: `dev` points to a valid `struct device`. - unsafe { device::Device::as_ref(dev) } + unsafe { device::Device::from_raw(dev) } } } diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set.rs index bcf4214ad149..c3cf56d52bee 100644 --- a/rust/kernel/block/mq/tag_set.rs +++ b/rust/kernel/block/mq/tag_set.rs @@ -9,7 +9,7 @@ use core::pin::Pin; use crate::{ bindings, block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations}, - error, + error::{self, Result}, prelude::try_pin_init, types::Opaque, }; @@ -41,7 +41,7 @@ impl TagSet { // SAFETY: `blk_mq_tag_set` only contains integers and pointers, which // all are allowed to be 0. let tag_set: bindings::blk_mq_tag_set = unsafe { core::mem::zeroed() }; - let tag_set = core::mem::size_of::() + let tag_set: Result<_> = core::mem::size_of::() .try_into() .map(|cmd_size| { bindings::blk_mq_tag_set { @@ -56,12 +56,14 @@ impl TagSet { nr_maps: num_maps, ..tag_set } - }); + }) + .map(Opaque::new) + .map_err(|e| e.into()); try_pin_init!(TagSet { - inner <- PinInit::<_, error::Error>::pin_chain(Opaque::new(tag_set?), |tag_set| { + inner <- tag_set.pin_chain(|tag_set| { // SAFETY: we do not move out of `tag_set`. - let tag_set = unsafe { Pin::get_unchecked_mut(tag_set) }; + let tag_set: &mut Opaque<_> = unsafe { Pin::get_unchecked_mut(tag_set) }; // SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`. error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())}) }), diff --git a/rust/kernel/cpu.rs b/rust/kernel/cpu.rs index b75403b0eb56..5de730c8d817 100644 --- a/rust/kernel/cpu.rs +++ b/rust/kernel/cpu.rs @@ -147,5 +147,5 @@ pub unsafe fn from_cpu(cpu: CpuId) -> Result<&'static Device> { // SAFETY: The pointer returned by `get_cpu_device()`, if not `NULL`, is a valid pointer to // a `struct device` and is never freed by the C code. - Ok(unsafe { Device::as_ref(ptr) }) + Ok(unsafe { Device::from_raw(ptr) }) } diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index d6a14239f4ba..d0ea24236ae4 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -13,7 +13,7 @@ use crate::{ cpu::CpuId, cpumask, device::{Bound, Device}, - devres::Devres, + devres, error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR}, ffi::{c_char, c_ulong}, prelude::*, @@ -1046,10 +1046,13 @@ impl Registration { /// Same as [`Registration::new`], but does not return a [`Registration`] instance. /// - /// Instead the [`Registration`] is owned by [`Devres`] and will be revoked / dropped, once the + /// Instead the [`Registration`] is owned by [`devres::register`] and will be dropped, once the /// device is detached. - pub fn new_foreign_owned(dev: &Device) -> Result { - Devres::new_foreign_owned(dev, Self::new()?, GFP_KERNEL) + pub fn new_foreign_owned(dev: &Device) -> Result + where + T: 'static, + { + devres::register(dev, Self::new()?, GFP_KERNEL) } } diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index dea06b79ecb5..ca82926fd67f 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -6,14 +6,15 @@ use crate::{ bindings, - str::CStr, - types::{ARef, Opaque}, + types::{ARef, ForeignOwnable, Opaque}, }; use core::{fmt, marker::PhantomData, ptr}; #[cfg(CONFIG_PRINTK)] use crate::c_str; +pub mod property; + /// A reference-counted device. /// /// This structure represents the Rust abstraction for a C `struct device`. This implementation @@ -57,7 +58,73 @@ impl Device { /// While not officially documented, this should be the case for any `struct device`. pub unsafe fn get_device(ptr: *mut bindings::device) -> ARef { // SAFETY: By the safety requirements ptr is valid - unsafe { Self::as_ref(ptr) }.into() + unsafe { Self::from_raw(ptr) }.into() + } + + /// Convert a [`&Device`](Device) into a [`&Device`](Device). + /// + /// # Safety + /// + /// The caller is responsible to ensure that the returned [`&Device`](Device) + /// only lives as long as it can be guaranteed that the [`Device`] is actually bound. + pub unsafe fn as_bound(&self) -> &Device { + let ptr = core::ptr::from_ref(self); + + // CAST: By the safety requirements the caller is responsible to guarantee that the + // returned reference only lives as long as the device is actually bound. + let ptr = ptr.cast(); + + // SAFETY: + // - `ptr` comes from `from_ref(self)` above, hence it's guaranteed to be valid. + // - Any valid `Device` pointer is also a valid pointer for `Device`. + unsafe { &*ptr } + } +} + +impl Device { + /// Store a pointer to the bound driver's private data. + pub fn set_drvdata(&self, data: impl ForeignOwnable) { + // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. + unsafe { bindings::dev_set_drvdata(self.as_raw(), data.into_foreign().cast()) } + } + + /// Take ownership of the private data stored in this [`Device`]. + /// + /// # Safety + /// + /// - Must only be called once after a preceding call to [`Device::set_drvdata`]. + /// - The type `T` must match the type of the `ForeignOwnable` previously stored by + /// [`Device::set_drvdata`]. + pub unsafe fn drvdata_obtain(&self) -> T { + // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. + let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) }; + + // SAFETY: + // - By the safety requirements of this function, `ptr` comes from a previous call to + // `into_foreign()`. + // - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()` + // in `into_foreign()`. + unsafe { T::from_foreign(ptr.cast()) } + } + + /// Borrow the driver's private data bound to this [`Device`]. + /// + /// # Safety + /// + /// - Must only be called after a preceding call to [`Device::set_drvdata`] and before + /// [`Device::drvdata_obtain`]. + /// - The type `T` must match the type of the `ForeignOwnable` previously stored by + /// [`Device::set_drvdata`]. + pub unsafe fn drvdata_borrow(&self) -> T::Borrowed<'_> { + // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. + let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) }; + + // SAFETY: + // - By the safety requirements of this function, `ptr` comes from a previous call to + // `into_foreign()`. + // - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()` + // in `into_foreign()`. + unsafe { T::borrow(ptr.cast()) } } } @@ -82,7 +149,7 @@ impl Device { // - Since `parent` is not NULL, it must be a valid pointer to a `struct device`. // - `parent` is valid for the lifetime of `self`, since a `struct device` holds a // reference count of its parent. - Some(unsafe { Self::as_ref(parent) }) + Some(unsafe { Self::from_raw(parent) }) } } @@ -94,7 +161,7 @@ impl Device { /// i.e. it must be ensured that the reference count of the C `struct device` `ptr` points to /// can't drop to zero, for the duration of this function call and the entire duration when the /// returned reference exists. - pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *mut bindings::device) -> &'a Self { // SAFETY: Guaranteed by the safety requirements of the function. unsafe { &*ptr.cast() } } @@ -203,10 +270,19 @@ impl Device { }; } - /// Checks if property is present or not. - pub fn property_present(&self, name: &CStr) -> bool { - // SAFETY: By the invariant of `CStr`, `name` is null-terminated. - unsafe { bindings::device_property_present(self.as_raw().cast_const(), name.as_char_ptr()) } + /// Obtain the [`FwNode`](property::FwNode) corresponding to this [`Device`]. + pub fn fwnode(&self) -> Option<&property::FwNode> { + // SAFETY: `self` is valid. + let fwnode_handle = unsafe { bindings::__dev_fwnode(self.as_raw()) }; + if fwnode_handle.is_null() { + return None; + } + // SAFETY: `fwnode_handle` is valid. Its lifetime is tied to `&self`. We + // return a reference instead of an `ARef` because `dev_fwnode()` + // doesn't increment the refcount. It is safe to cast from a + // `struct fwnode_handle*` to a `*const FwNode` because `FwNode` is + // defined as a `#[repr(transparent)]` wrapper around `fwnode_handle`. + Some(unsafe { &*fwnode_handle.cast() }) } } @@ -251,6 +327,10 @@ pub struct Normal; /// any of the bus callbacks, such as `probe()`. pub struct Core; +/// Semantically the same as [`Core`] but reserved for internal usage of the corresponding bus +/// abstraction. +pub struct CoreInternal; + /// The [`Bound`] context is the context of a bus specific device reference when it is guaranteed to /// be bound for the duration of its lifetime. pub struct Bound; @@ -260,11 +340,13 @@ mod private { impl Sealed for super::Bound {} impl Sealed for super::Core {} + impl Sealed for super::CoreInternal {} impl Sealed for super::Normal {} } impl DeviceContext for Bound {} impl DeviceContext for Core {} +impl DeviceContext for CoreInternal {} impl DeviceContext for Normal {} /// # Safety @@ -302,6 +384,13 @@ macro_rules! __impl_device_context_deref { #[macro_export] macro_rules! impl_device_context_deref { (unsafe { $device:ident }) => { + // SAFETY: This macro has the exact same safety requirement as + // `__impl_device_context_deref!`. + ::kernel::__impl_device_context_deref!(unsafe { + $device, + $crate::device::CoreInternal => $crate::device::Core + }); + // SAFETY: This macro has the exact same safety requirement as // `__impl_device_context_deref!`. ::kernel::__impl_device_context_deref!(unsafe { @@ -335,6 +424,7 @@ macro_rules! __impl_device_context_into_aref { #[macro_export] macro_rules! impl_device_context_into_aref { ($device:tt) => { + ::kernel::__impl_device_context_into_aref!($crate::device::CoreInternal, $device); ::kernel::__impl_device_context_into_aref!($crate::device::Core, $device); ::kernel::__impl_device_context_into_aref!($crate::device::Bound, $device); }; diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs new file mode 100644 index 000000000000..49ee12a906db --- /dev/null +++ b/rust/kernel/device/property.rs @@ -0,0 +1,631 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Unified device property interface. +//! +//! C header: [`include/linux/property.h`](srctree/include/linux/property.h) + +use core::{mem::MaybeUninit, ptr}; + +use super::private::Sealed; +use crate::{ + alloc::KVec, + bindings, + error::{to_result, Result}, + prelude::*, + str::{CStr, CString}, + types::{ARef, Opaque}, +}; + +/// A reference-counted fwnode_handle. +/// +/// This structure represents the Rust abstraction for a +/// C `struct fwnode_handle`. This implementation abstracts the usage of an +/// already existing C `struct fwnode_handle` within Rust code that we get +/// passed from the C side. +/// +/// # Invariants +/// +/// A `FwNode` instance represents a valid `struct fwnode_handle` created by the +/// C portion of the kernel. +/// +/// Instances of this type are always reference-counted, that is, a call to +/// `fwnode_handle_get` ensures that the allocation remains valid at least until +/// the matching call to `fwnode_handle_put`. +#[repr(transparent)] +pub struct FwNode(Opaque); + +impl FwNode { + /// # Safety + /// + /// Callers must ensure that: + /// - The reference count was incremented at least once. + /// - They relinquish that increment. That is, if there is only one + /// increment, callers must not use the underlying object anymore -- it is + /// only safe to do so via the newly created `ARef`. + unsafe fn from_raw(raw: *mut bindings::fwnode_handle) -> ARef { + // SAFETY: As per the safety requirements of this function: + // - `NonNull::new_unchecked`: + // - `raw` is not null. + // - `ARef::from_raw`: + // - `raw` has an incremented refcount. + // - that increment is relinquished, i.e. it won't be decremented + // elsewhere. + // CAST: It is safe to cast from a `*mut fwnode_handle` to + // `*mut FwNode`, because `FwNode` is defined as a + // `#[repr(transparent)]` wrapper around `fwnode_handle`. + unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(raw.cast())) } + } + + /// Obtain the raw `struct fwnode_handle *`. + pub(crate) fn as_raw(&self) -> *mut bindings::fwnode_handle { + self.0.get() + } + + /// Returns `true` if `&self` is an OF node, `false` otherwise. + pub fn is_of_node(&self) -> bool { + // SAFETY: The type invariant of `Self` guarantees that `self.as_raw() is a pointer to a + // valid `struct fwnode_handle`. + unsafe { bindings::is_of_node(self.as_raw()) } + } + + /// Returns an object that implements [`Display`](core::fmt::Display) for + /// printing the name of a node. + /// + /// This is an alternative to the default `Display` implementation, which + /// prints the full path. + pub fn display_name(&self) -> impl core::fmt::Display + '_ { + struct FwNodeDisplayName<'a>(&'a FwNode); + + impl core::fmt::Display for FwNodeDisplayName<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + // SAFETY: `self` is valid by its type invariant. + let name = unsafe { bindings::fwnode_get_name(self.0.as_raw()) }; + if name.is_null() { + return Ok(()); + } + // SAFETY: + // - `fwnode_get_name` returns null or a valid C string. + // - `name` was checked to be non-null. + let name = unsafe { CStr::from_char_ptr(name) }; + write!(f, "{name}") + } + } + + FwNodeDisplayName(self) + } + + /// Checks if property is present or not. + pub fn property_present(&self, name: &CStr) -> bool { + // SAFETY: By the invariant of `CStr`, `name` is null-terminated. + unsafe { bindings::fwnode_property_present(self.as_raw().cast_const(), name.as_char_ptr()) } + } + + /// Returns firmware property `name` boolean value. + pub fn property_read_bool(&self, name: &CStr) -> bool { + // SAFETY: + // - `name` is non-null and null-terminated. + // - `self.as_raw()` is valid because `self` is valid. + unsafe { bindings::fwnode_property_read_bool(self.as_raw(), name.as_char_ptr()) } + } + + /// Returns the index of matching string `match_str` for firmware string + /// property `name`. + pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> Result { + // SAFETY: + // - `name` and `match_str` are non-null and null-terminated. + // - `self.as_raw` is valid because `self` is valid. + let ret = unsafe { + bindings::fwnode_property_match_string( + self.as_raw(), + name.as_char_ptr(), + match_str.as_char_ptr(), + ) + }; + to_result(ret)?; + Ok(ret as usize) + } + + /// Returns firmware property `name` integer array values in a [`KVec`]. + pub fn property_read_array_vec<'fwnode, 'name, T: PropertyInt>( + &'fwnode self, + name: &'name CStr, + len: usize, + ) -> Result>> { + let mut val: KVec = KVec::with_capacity(len, GFP_KERNEL)?; + + let res = T::read_array_from_fwnode_property(self, name, val.spare_capacity_mut()); + let res = match res { + Ok(_) => { + // SAFETY: + // - `len` is equal to `val.capacity - val.len`, because + // `val.capacity` is `len` and `val.len` is zero. + // - All elements within the interval [`0`, `len`) were initialized + // by `read_array_from_fwnode_property`. + unsafe { val.inc_len(len) } + Ok(val) + } + Err(e) => Err(e), + }; + Ok(PropertyGuard { + inner: res, + fwnode: self, + name, + }) + } + + /// Returns integer array length for firmware property `name`. + pub fn property_count_elem(&self, name: &CStr) -> Result { + T::read_array_len_from_fwnode_property(self, name) + } + + /// Returns the value of firmware property `name`. + /// + /// This method is generic over the type of value to read. The types that + /// can be read are strings, integers and arrays of integers. + /// + /// Reading a [`KVec`] of integers is done with the separate + /// method [`Self::property_read_array_vec`], because it takes an + /// additional `len` argument. + /// + /// Reading a boolean is done with the separate method + /// [`Self::property_read_bool`], because this operation is infallible. + /// + /// For more precise documentation about what types can be read, see + /// the [implementors of Property][Property#implementors] and [its + /// implementations on foreign types][Property#foreign-impls]. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{c_str, device::{Device, property::FwNode}, str::CString}; + /// fn examples(dev: &Device) -> Result { + /// let fwnode = dev.fwnode().ok_or(ENOENT)?; + /// let b: u32 = fwnode.property_read(c_str!("some-number")).required_by(dev)?; + /// if let Some(s) = fwnode.property_read::(c_str!("some-str")).optional() { + /// // ... + /// } + /// Ok(()) + /// } + /// ``` + pub fn property_read<'fwnode, 'name, T: Property>( + &'fwnode self, + name: &'name CStr, + ) -> PropertyGuard<'fwnode, 'name, T> { + PropertyGuard { + inner: T::read_from_fwnode_property(self, name), + fwnode: self, + name, + } + } + + /// Returns first matching named child node handle. + pub fn get_child_by_name(&self, name: &CStr) -> Option> { + // SAFETY: `self` and `name` are valid by their type invariants. + let child = + unsafe { bindings::fwnode_get_named_child_node(self.as_raw(), name.as_char_ptr()) }; + if child.is_null() { + return None; + } + // SAFETY: + // - `fwnode_get_named_child_node` returns a pointer with its refcount + // incremented. + // - That increment is relinquished, i.e. the underlying object is not + // used anymore except via the newly created `ARef`. + Some(unsafe { Self::from_raw(child) }) + } + + /// Returns an iterator over a node's children. + pub fn children<'a>(&'a self) -> impl Iterator> + 'a { + let mut prev: Option> = None; + + core::iter::from_fn(move || { + let prev_ptr = match prev.take() { + None => ptr::null_mut(), + Some(prev) => { + // We will pass `prev` to `fwnode_get_next_child_node`, + // which decrements its refcount, so we use + // `ARef::into_raw` to avoid decrementing the refcount + // twice. + let prev = ARef::into_raw(prev); + prev.as_ptr().cast() + } + }; + // SAFETY: + // - `self.as_raw()` is valid by its type invariant. + // - `prev_ptr` may be null, which is allowed and corresponds to + // getting the first child. Otherwise, `prev_ptr` is valid, as it + // is the stored return value from the previous invocation. + // - `prev_ptr` has its refount incremented. + // - The increment of `prev_ptr` is relinquished, i.e. the + // underlying object won't be used anymore. + let next = unsafe { bindings::fwnode_get_next_child_node(self.as_raw(), prev_ptr) }; + if next.is_null() { + return None; + } + // SAFETY: + // - `next` is valid because `fwnode_get_next_child_node` returns a + // pointer with its refcount incremented. + // - That increment is relinquished, i.e. the underlying object + // won't be used anymore, except via the newly created + // `ARef`. + let next = unsafe { FwNode::from_raw(next) }; + prev = Some(next.clone()); + Some(next) + }) + } + + /// Finds a reference with arguments. + pub fn property_get_reference_args( + &self, + prop: &CStr, + nargs: NArgs<'_>, + index: u32, + ) -> Result { + let mut out_args = FwNodeReferenceArgs::default(); + + let (nargs_prop, nargs) = match nargs { + NArgs::Prop(nargs_prop) => (nargs_prop.as_char_ptr(), 0), + NArgs::N(nargs) => (ptr::null(), nargs), + }; + + // SAFETY: + // - `self.0.get()` is valid. + // - `prop.as_char_ptr()` is valid and zero-terminated. + // - `nargs_prop` is valid and zero-terminated if `nargs` + // is zero, otherwise it is allowed to be a null-pointer. + // - The function upholds the type invariants of `out_args`, + // namely: + // - It may fill the field `fwnode` with a valid pointer, + // in which case its refcount is incremented. + // - It may modify the field `nargs`, in which case it + // initializes at least as many elements in `args`. + let ret = unsafe { + bindings::fwnode_property_get_reference_args( + self.0.get(), + prop.as_char_ptr(), + nargs_prop, + nargs, + index, + &mut out_args.0, + ) + }; + to_result(ret)?; + + Ok(out_args) + } +} + +/// The number of arguments to request [`FwNodeReferenceArgs`]. +pub enum NArgs<'a> { + /// The name of the property of the reference indicating the number of + /// arguments. + Prop(&'a CStr), + /// The known number of arguments. + N(u32), +} + +/// The return value of [`FwNode::property_get_reference_args`]. +/// +/// This structure represents the Rust abstraction for a C +/// `struct fwnode_reference_args` which was initialized by the C side. +/// +/// # Invariants +/// +/// If the field `fwnode` is valid, it owns an increment of its refcount. +/// +/// The field `args` contains at least as many initialized elements as indicated +/// by the field `nargs`. +#[repr(transparent)] +#[derive(Default)] +pub struct FwNodeReferenceArgs(bindings::fwnode_reference_args); + +impl Drop for FwNodeReferenceArgs { + fn drop(&mut self) { + if !self.0.fwnode.is_null() { + // SAFETY: + // - By the type invariants of `FwNodeReferenceArgs`, its field + // `fwnode` owns an increment of its refcount. + // - That increment is relinquished. The underlying object won't be + // used anymore because we are dropping it. + let _ = unsafe { FwNode::from_raw(self.0.fwnode) }; + } + } +} + +impl FwNodeReferenceArgs { + /// Returns the slice of reference arguments. + pub fn as_slice(&self) -> &[u64] { + // SAFETY: As per the safety invariant of `FwNodeReferenceArgs`, `nargs` + // is the minimum number of elements in `args` that is valid. + unsafe { core::slice::from_raw_parts(self.0.args.as_ptr(), self.0.nargs as usize) } + } + + /// Returns the number of reference arguments. + pub fn len(&self) -> usize { + self.0.nargs as usize + } + + /// Returns `true` if there are no reference arguments. + pub fn is_empty(&self) -> bool { + self.0.nargs == 0 + } +} + +impl core::fmt::Debug for FwNodeReferenceArgs { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self.as_slice()) + } +} + +// SAFETY: Instances of `FwNode` are always reference-counted. +unsafe impl crate::types::AlwaysRefCounted for FwNode { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the + // refcount is non-zero. + unsafe { bindings::fwnode_handle_get(self.as_raw()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is + // non-zero. + unsafe { bindings::fwnode_handle_put(obj.cast().as_ptr()) } + } +} + +enum Node<'a> { + Borrowed(&'a FwNode), + Owned(ARef), +} + +impl core::fmt::Display for FwNode { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + // The logic here is the same as the one in lib/vsprintf.c + // (fwnode_full_name_string). + + // SAFETY: `self.as_raw()` is valid by its type invariant. + let num_parents = unsafe { bindings::fwnode_count_parents(self.as_raw()) }; + + for depth in (0..=num_parents).rev() { + let fwnode = if depth == 0 { + Node::Borrowed(self) + } else { + // SAFETY: `self.as_raw()` is valid. + let ptr = unsafe { bindings::fwnode_get_nth_parent(self.as_raw(), depth) }; + // SAFETY: + // - The depth passed to `fwnode_get_nth_parent` is + // within the valid range, so the returned pointer is + // not null. + // - The reference count was incremented by + // `fwnode_get_nth_parent`. + // - That increment is relinquished to + // `FwNode::from_raw`. + Node::Owned(unsafe { FwNode::from_raw(ptr) }) + }; + // Take a reference to the owned or borrowed `FwNode`. + let fwnode: &FwNode = match &fwnode { + Node::Borrowed(f) => f, + Node::Owned(f) => f, + }; + + // SAFETY: `fwnode` is valid by its type invariant. + let prefix = unsafe { bindings::fwnode_get_name_prefix(fwnode.as_raw()) }; + if !prefix.is_null() { + // SAFETY: `fwnode_get_name_prefix` returns null or a + // valid C string. + let prefix = unsafe { CStr::from_char_ptr(prefix) }; + write!(f, "{prefix}")?; + } + write!(f, "{}", fwnode.display_name())?; + } + + Ok(()) + } +} + +/// Implemented for types that can be read as properties. +/// +/// This is implemented for strings, integers and arrays of integers. It's used +/// to make [`FwNode::property_read`] generic over the type of property being +/// read. There are also two dedicated methods to read other types, because they +/// require more specialized function signatures: +/// - [`property_read_bool`](FwNode::property_read_bool) +/// - [`property_read_array_vec`](FwNode::property_read_array_vec) +/// +/// It must be public, because it appears in the signatures of other public +/// functions, but its methods shouldn't be used outside the kernel crate. +pub trait Property: Sized + Sealed { + /// Used to make [`FwNode::property_read`] generic. + fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result; +} + +impl Sealed for CString {} + +impl Property for CString { + fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result { + let mut str: *mut u8 = ptr::null_mut(); + let pstr: *mut _ = &mut str; + + // SAFETY: + // - `name` is non-null and null-terminated. + // - `fwnode.as_raw` is valid because `fwnode` is valid. + let ret = unsafe { + bindings::fwnode_property_read_string(fwnode.as_raw(), name.as_char_ptr(), pstr.cast()) + }; + to_result(ret)?; + + // SAFETY: + // - `pstr` is a valid pointer to a NUL-terminated C string. + // - It is valid for at least as long as `fwnode`, but it's only used + // within the current function. + // - The memory it points to is not mutated during that time. + let str = unsafe { CStr::from_char_ptr(*pstr) }; + Ok(str.try_into()?) + } +} + +/// Implemented for all integers that can be read as properties. +/// +/// This helper trait is needed on top of the existing [`Property`] +/// trait to associate the integer types of various sizes with their +/// corresponding `fwnode_property_read_*_array` functions. +/// +/// It must be public, because it appears in the signatures of other public +/// functions, but its methods shouldn't be used outside the kernel crate. +pub trait PropertyInt: Copy + Sealed { + /// Reads a property array. + fn read_array_from_fwnode_property<'a>( + fwnode: &FwNode, + name: &CStr, + out: &'a mut [MaybeUninit], + ) -> Result<&'a mut [Self]>; + + /// Reads the length of a property array. + fn read_array_len_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result; +} +// This macro generates implementations of the traits `Property` and +// `PropertyInt` for integers of various sizes. Its input is a list +// of pairs separated by commas. The first element of the pair is the +// type of the integer, the second one is the name of its corresponding +// `fwnode_property_read_*_array` function. +macro_rules! impl_property_for_int { + ($($int:ty: $f:ident),* $(,)?) => { $( + impl Sealed for $int {} + impl Sealed for [$int; N] {} + + impl PropertyInt for $int { + fn read_array_from_fwnode_property<'a>( + fwnode: &FwNode, + name: &CStr, + out: &'a mut [MaybeUninit], + ) -> Result<&'a mut [Self]> { + // SAFETY: + // - `fwnode`, `name` and `out` are all valid by their type + // invariants. + // - `out.len()` is a valid bound for the memory pointed to by + // `out.as_mut_ptr()`. + // CAST: It's ok to cast from `*mut MaybeUninit<$int>` to a + // `*mut $int` because they have the same memory layout. + let ret = unsafe { + bindings::$f( + fwnode.as_raw(), + name.as_char_ptr(), + out.as_mut_ptr().cast(), + out.len(), + ) + }; + to_result(ret)?; + // SAFETY: Transmuting from `&'a mut [MaybeUninit]` to + // `&'a mut [Self]` is sound, because the previous call to a + // `fwnode_property_read_*_array` function (which didn't fail) + // fully initialized the slice. + Ok(unsafe { core::mem::transmute::<&mut [MaybeUninit], &mut [Self]>(out) }) + } + + fn read_array_len_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result { + // SAFETY: + // - `fwnode` and `name` are valid by their type invariants. + // - It's ok to pass a null pointer to the + // `fwnode_property_read_*_array` functions if `nval` is zero. + // This will return the length of the array. + let ret = unsafe { + bindings::$f( + fwnode.as_raw(), + name.as_char_ptr(), + ptr::null_mut(), + 0, + ) + }; + to_result(ret)?; + Ok(ret as usize) + } + } + + impl Property for $int { + fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result { + let val: [_; 1] = <[$int; 1]>::read_from_fwnode_property(fwnode, name)?; + Ok(val[0]) + } + } + + impl Property for [$int; N] { + fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result { + let mut val: [MaybeUninit<$int>; N] = [const { MaybeUninit::uninit() }; N]; + + <$int>::read_array_from_fwnode_property(fwnode, name, &mut val)?; + + // SAFETY: `val` is always initialized when + // `fwnode_property_read_*_array` is successful. + Ok(val.map(|v| unsafe { v.assume_init() })) + } + } + )* }; +} +impl_property_for_int! { + u8: fwnode_property_read_u8_array, + u16: fwnode_property_read_u16_array, + u32: fwnode_property_read_u32_array, + u64: fwnode_property_read_u64_array, + i8: fwnode_property_read_u8_array, + i16: fwnode_property_read_u16_array, + i32: fwnode_property_read_u32_array, + i64: fwnode_property_read_u64_array, +} + +/// A helper for reading device properties. +/// +/// Use [`Self::required_by`] if a missing property is considered a bug and +/// [`Self::optional`] otherwise. +/// +/// For convenience, [`Self::or`] and [`Self::or_default`] are provided. +pub struct PropertyGuard<'fwnode, 'name, T> { + /// The result of reading the property. + inner: Result, + /// The fwnode of the property, used for logging in the "required" case. + fwnode: &'fwnode FwNode, + /// The name of the property, used for logging in the "required" case. + name: &'name CStr, +} + +impl PropertyGuard<'_, '_, T> { + /// Access the property, indicating it is required. + /// + /// If the property is not present, the error is automatically logged. If a + /// missing property is not an error, use [`Self::optional`] instead. The + /// device is required to associate the log with it. + pub fn required_by(self, dev: &super::Device) -> Result { + if self.inner.is_err() { + dev_err!( + dev, + "{}: property '{}' is missing\n", + self.fwnode, + self.name + ); + } + self.inner + } + + /// Access the property, indicating it is optional. + /// + /// In contrast to [`Self::required_by`], no error message is logged if + /// the property is not present. + pub fn optional(self) -> Option { + self.inner.ok() + } + + /// Access the property or the specified default value. + /// + /// Do not pass a sentinel value as default to detect a missing property. + /// Use [`Self::required_by`] or [`Self::optional`] instead. + pub fn or(self, default: T) -> T { + self.inner.unwrap_or(default) + } +} + +impl PropertyGuard<'_, '_, T> { + /// Access the property or a default value. + /// + /// Use [`Self::or`] to specify a custom default value. + pub fn or_default(self) -> T { + self.inner.unwrap_or_default() + } +} diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs index 0a4eb56d98f2..8ed2c946144c 100644 --- a/rust/kernel/device_id.rs +++ b/rust/kernel/device_id.rs @@ -14,32 +14,41 @@ use core::mem::MaybeUninit; /// /// # Safety /// -/// Implementers must ensure that: -/// - `Self` is layout-compatible with [`RawDeviceId::RawType`]; i.e. it's safe to transmute to -/// `RawDeviceId`. +/// Implementers must ensure that `Self` is layout-compatible with [`RawDeviceId::RawType`]; +/// i.e. it's safe to transmute to `RawDeviceId`. /// -/// This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building -/// the ID table. +/// This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building +/// the ID table. /// -/// Ideally, this should be achieved using a const function that does conversion instead of -/// transmute; however, const trait functions relies on `const_trait_impl` unstable feature, -/// which is broken/gone in Rust 1.73. -/// -/// - `DRIVER_DATA_OFFSET` is the offset of context/data field of the device ID (usually named -/// `driver_data`) of the device ID, the field is suitable sized to write a `usize` value. -/// -/// Similar to the previous requirement, the data should ideally be added during `Self` to -/// `RawType` conversion, but there's currently no way to do it when using traits in const. +/// Ideally, this should be achieved using a const function that does conversion instead of +/// transmute; however, const trait functions relies on `const_trait_impl` unstable feature, +/// which is broken/gone in Rust 1.73. pub unsafe trait RawDeviceId { /// The raw type that holds the device id. /// /// Id tables created from [`Self`] are going to hold this type in its zero-terminated array. type RawType: Copy; +} - /// The offset to the context/data field. +/// Extension trait for [`RawDeviceId`] for devices that embed an index or context value. +/// +/// This is typically used when the device ID struct includes a field like `driver_data` +/// that is used to store a pointer-sized value (e.g., an index or context pointer). +/// +/// # Safety +/// +/// Implementers must ensure that `DRIVER_DATA_OFFSET` is the correct offset (in bytes) to +/// the context/data field (e.g., the `driver_data` field) within the raw device ID structure. +/// This field must be correctly sized to hold a `usize`. +/// +/// Ideally, the data should be added during `Self` to `RawType` conversion, +/// but there's currently no way to do it when using traits in const. +pub unsafe trait RawDeviceIdIndex: RawDeviceId { + /// The offset (in bytes) to the context/data field in the raw device ID. const DRIVER_DATA_OFFSET: usize; - /// The index stored at `DRIVER_DATA_OFFSET` of the implementor of the [`RawDeviceId`] trait. + /// The index stored at `DRIVER_DATA_OFFSET` of the implementor of the [`RawDeviceIdIndex`] + /// trait. fn index(&self) -> usize; } @@ -68,7 +77,15 @@ impl IdArray { /// Creates a new instance of the array. /// /// The contents are derived from the given identifiers and context information. - pub const fn new(ids: [(T, U); N]) -> Self { + /// + /// # Safety + /// + /// `data_offset` as `None` is always safe. + /// If `data_offset` is `Some(data_offset)`, then: + /// - `data_offset` must be the correct offset (in bytes) to the context/data field + /// (e.g., the `driver_data` field) within the raw device ID structure. + /// - The field at `data_offset` must be correctly sized to hold a `usize`. + const unsafe fn build(ids: [(T, U); N], data_offset: Option) -> Self { let mut raw_ids = [const { MaybeUninit::::uninit() }; N]; let mut infos = [const { MaybeUninit::uninit() }; N]; @@ -77,14 +94,16 @@ impl IdArray { // SAFETY: by the safety requirement of `RawDeviceId`, we're guaranteed that `T` is // layout-wise compatible with `RawType`. raw_ids[i] = unsafe { core::mem::transmute_copy(&ids[i].0) }; - // SAFETY: by the safety requirement of `RawDeviceId`, this would be effectively - // `raw_ids[i].driver_data = i;`. - unsafe { - raw_ids[i] - .as_mut_ptr() - .byte_offset(T::DRIVER_DATA_OFFSET as _) - .cast::() - .write(i); + if let Some(data_offset) = data_offset { + // SAFETY: by the safety requirement of this function, this would be effectively + // `raw_ids[i].driver_data = i;`. + unsafe { + raw_ids[i] + .as_mut_ptr() + .byte_offset(data_offset as _) + .cast::() + .write(i); + } } // SAFETY: this is effectively a move: `infos[i] = ids[i].1`. We make a copy here but @@ -109,12 +128,34 @@ impl IdArray { } } + /// Creates a new instance of the array without writing index values. + /// + /// The contents are derived from the given identifiers and context information. + /// If the device implements [`RawDeviceIdIndex`], consider using [`IdArray::new`] instead. + pub const fn new_without_index(ids: [(T, U); N]) -> Self { + // SAFETY: Calling `Self::build` with `offset = None` is always safe, + // because no raw memory writes are performed in this case. + unsafe { Self::build(ids, None) } + } + /// Reference to the contained [`RawIdArray`]. pub const fn raw_ids(&self) -> &RawIdArray { &self.raw_ids } } +impl IdArray { + /// Creates a new instance of the array. + /// + /// The contents are derived from the given identifiers and context information. + pub const fn new(ids: [(T, U); N]) -> Self { + // SAFETY: by the safety requirement of `RawDeviceIdIndex`, + // `T::DRIVER_DATA_OFFSET` is guaranteed to be the correct offset (in bytes) to + // a field within `T::RawType`. + unsafe { Self::build(ids, Some(T::DRIVER_DATA_OFFSET)) } + } +} + /// A device id table. /// /// This trait is only implemented by `IdArray`. diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 57502534d985..152a89b78943 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -9,20 +9,25 @@ use crate::{ alloc::Flags, bindings, device::{Bound, Device}, - error::{Error, Result}, + error::{to_result, Error, Result}, ffi::c_void, prelude::*, revocable::{Revocable, RevocableGuard}, - sync::{rcu, Arc, Completion}, - types::ARef, + sync::{rcu, Completion}, + types::{ARef, ForeignOwnable, Opaque, ScopeGuard}, }; +use pin_init::Wrapper; + +/// [`Devres`] inner data accessed from [`Devres::callback`]. #[pin_data] -struct DevresInner { - dev: ARef, - callback: unsafe extern "C" fn(*mut c_void), +struct Inner { #[pin] data: Revocable, + /// Tracks whether [`Devres::callback`] has been completed. + #[pin] + devm: Completion, + /// Tracks whether revoking [`Self::data`] has been completed. #[pin] revoke: Completion, } @@ -47,7 +52,7 @@ struct DevresInner { /// # Example /// /// ```no_run -/// # use kernel::{bindings, c_str, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}}; +/// # use kernel::{bindings, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}}; /// # use core::ops::Deref; /// /// // See also [`pci::Bar`] for a real example. @@ -88,108 +93,120 @@ struct DevresInner { /// # fn no_run(dev: &Device) -> Result<(), Error> { /// // SAFETY: Invalid usage for example purposes. /// let iomem = unsafe { IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD)? }; -/// let devres = Devres::new(dev, iomem, GFP_KERNEL)?; +/// let devres = KBox::pin_init(Devres::new(dev, iomem), GFP_KERNEL)?; /// /// let res = devres.try_access().ok_or(ENXIO)?; /// res.write8(0x42, 0x0); /// # Ok(()) /// # } /// ``` -pub struct Devres(Arc>); +/// +/// # Invariants +/// +/// [`Self::inner`] is guaranteed to be initialized and is always accessed read-only. +#[pin_data(PinnedDrop)] +pub struct Devres { + dev: ARef, + /// Pointer to [`Self::devres_callback`]. + /// + /// Has to be stored, since Rust does not guarantee to always return the same address for a + /// function. However, the C API uses the address as a key. + callback: unsafe extern "C" fn(*mut c_void), + /// Contains all the fields shared with [`Self::callback`]. + // TODO: Replace with `UnsafePinned`, once available. + // + // Subsequently, the `drop_in_place()` in `Devres::drop` and the explicit `Send` and `Sync' + // impls can be removed. + #[pin] + inner: Opaque>, +} -impl DevresInner { - fn new(dev: &Device, data: T, flags: Flags) -> Result>> { - let inner = Arc::pin_init( - pin_init!( DevresInner { - dev: dev.into(), - callback: Self::devres_callback, - data <- Revocable::new(data), - revoke <- Completion::new(), - }), - flags, - )?; +impl Devres { + /// Creates a new [`Devres`] instance of the given `data`. + /// + /// The `data` encapsulated within the returned `Devres` instance' `data` will be + /// (revoked)[`Revocable`] once the device is detached. + pub fn new<'a, E>( + dev: &'a Device, + data: impl PinInit + 'a, + ) -> impl PinInit + 'a + where + T: 'a, + Error: From, + { + let callback = Self::devres_callback; - // Convert `Arc` into a raw pointer and make devres own this reference until - // `Self::devres_callback` is called. - let data = inner.clone().into_raw(); + try_pin_init!(&this in Self { + dev: dev.into(), + callback, + // INVARIANT: `inner` is properly initialized. + inner <- { + // SAFETY: `this` is a valid pointer to uninitialized memory. + let inner = unsafe { &raw mut (*this.as_ptr()).inner }; - // SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is - // detached. - let ret = - unsafe { bindings::devm_add_action(dev.as_raw(), Some(inner.callback), data as _) }; + // SAFETY: + // - `dev.as_raw()` is a pointer to a valid bound device. + // - `inner` is guaranteed to be a valid for the duration of the lifetime of `Self`. + // - `devm_add_action()` is guaranteed not to call `callback` until `this` has been + // properly initialized, because we require `dev` (i.e. the *bound* device) to + // live at least as long as the returned `impl PinInit`. + to_result(unsafe { + bindings::devm_add_action(dev.as_raw(), Some(callback), inner.cast()) + })?; - if ret != 0 { - // SAFETY: We just created another reference to `inner` in order to pass it to - // `bindings::devm_add_action`. If `bindings::devm_add_action` fails, we have to drop - // this reference accordingly. - let _ = unsafe { Arc::from_raw(data) }; - return Err(Error::from_errno(ret)); - } - - Ok(inner) + Opaque::pin_init(try_pin_init!(Inner { + devm <- Completion::new(), + revoke <- Completion::new(), + data <- Revocable::new(data), + })) + }, + }) } - fn as_ptr(&self) -> *const Self { - self as _ + fn inner(&self) -> &Inner { + // SAFETY: By the type invairants of `Self`, `inner` is properly initialized and always + // accessed read-only. + unsafe { &*self.inner.get() } } - fn remove_action(this: &Arc) -> bool { - // SAFETY: - // - `self.inner.dev` is a valid `Device`, - // - the `action` and `data` pointers are the exact same ones as given to devm_add_action() - // previously, - // - `self` is always valid, even if the action has been released already. - let success = unsafe { - bindings::devm_remove_action_nowarn( - this.dev.as_raw(), - Some(this.callback), - this.as_ptr() as _, - ) - } == 0; - - if success { - // SAFETY: We leaked an `Arc` reference to devm_add_action() in `DevresInner::new`; if - // devm_remove_action_nowarn() was successful we can (and have to) claim back ownership - // of this reference. - let _ = unsafe { Arc::from_raw(this.as_ptr()) }; - } - - success + fn data(&self) -> &Revocable { + &self.inner().data } #[allow(clippy::missing_safety_doc)] unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) { - let ptr = ptr as *mut DevresInner; - // Devres owned this memory; now that we received the callback, drop the `Arc` and hence the - // reference. - // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in - // `DevresInner::new`. - let inner = unsafe { Arc::from_raw(ptr) }; + // SAFETY: In `Self::new` we've passed a valid pointer to `Inner` to `devm_add_action()`, + // hence `ptr` must be a valid pointer to `Inner`. + let inner = unsafe { &*ptr.cast::>() }; + + // Ensure that `inner` can't be used anymore after we signal completion of this callback. + let inner = ScopeGuard::new_with_data(inner, |inner| inner.devm.complete_all()); if !inner.data.revoke() { // If `revoke()` returns false, it means that `Devres::drop` already started revoking - // `inner.data` for us. Hence we have to wait until `Devres::drop()` signals that it - // completed revoking `inner.data`. + // `data` for us. Hence we have to wait until `Devres::drop` signals that it + // completed revoking `data`. inner.revoke.wait_for_completion(); } } -} -impl Devres { - /// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the - /// returned `Devres` instance' `data` will be revoked once the device is detached. - pub fn new(dev: &Device, data: T, flags: Flags) -> Result { - let inner = DevresInner::new(dev, data, flags)?; - - Ok(Devres(inner)) + fn remove_action(&self) -> bool { + // SAFETY: + // - `self.dev` is a valid `Device`, + // - the `action` and `data` pointers are the exact same ones as given to + // `devm_add_action()` previously, + (unsafe { + bindings::devm_remove_action_nowarn( + self.dev.as_raw(), + Some(self.callback), + core::ptr::from_ref(self.inner()).cast_mut().cast(), + ) + } == 0) } - /// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data` - /// is owned by devres and will be revoked / dropped, once the device is detached. - pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result { - let _ = DevresInner::new(dev, data, flags)?; - - Ok(()) + /// Return a reference of the [`Device`] this [`Devres`] instance has been created with. + pub fn device(&self) -> &Device { + &self.dev } /// Obtain `&'a T`, bypassing the [`Revocable`]. @@ -221,43 +238,123 @@ impl Devres { /// } /// ``` pub fn access<'a>(&'a self, dev: &'a Device) -> Result<&'a T> { - if self.0.dev.as_raw() != dev.as_raw() { + if self.dev.as_raw() != dev.as_raw() { return Err(EINVAL); } // SAFETY: `dev` being the same device as the device this `Devres` has been created for - // proves that `self.0.data` hasn't been revoked and is guaranteed to not be revoked as - // long as `dev` lives; `dev` lives at least as long as `self`. - Ok(unsafe { self.0.data.access() }) + // proves that `self.data` hasn't been revoked and is guaranteed to not be revoked as long + // as `dev` lives; `dev` lives at least as long as `self`. + Ok(unsafe { self.data().access() }) } /// [`Devres`] accessor for [`Revocable::try_access`]. pub fn try_access(&self) -> Option> { - self.0.data.try_access() + self.data().try_access() } /// [`Devres`] accessor for [`Revocable::try_access_with`]. pub fn try_access_with R>(&self, f: F) -> Option { - self.0.data.try_access_with(f) + self.data().try_access_with(f) } /// [`Devres`] accessor for [`Revocable::try_access_with_guard`]. pub fn try_access_with_guard<'a>(&'a self, guard: &'a rcu::Guard) -> Option<&'a T> { - self.0.data.try_access_with_guard(guard) + self.data().try_access_with_guard(guard) } } -impl Drop for Devres { - fn drop(&mut self) { +// SAFETY: `Devres` can be send to any task, if `T: Send`. +unsafe impl Send for Devres {} + +// SAFETY: `Devres` can be shared with any task, if `T: Sync`. +unsafe impl Sync for Devres {} + +#[pinned_drop] +impl PinnedDrop for Devres { + fn drop(self: Pin<&mut Self>) { // SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data // anymore, hence it is safe not to wait for the grace period to finish. - if unsafe { self.0.data.revoke_nosync() } { - // We revoked `self.0.data` before the devres action did, hence try to remove it. - if !DevresInner::remove_action(&self.0) { + if unsafe { self.data().revoke_nosync() } { + // We revoked `self.data` before the devres action did, hence try to remove it. + if !self.remove_action() { // We could not remove the devres action, which means that it now runs concurrently, - // hence signal that `self.0.data` has been revoked successfully. - self.0.revoke.complete_all(); + // hence signal that `self.data` has been revoked by us successfully. + self.inner().revoke.complete_all(); + + // Wait for `Self::devres_callback` to be done using this object. + self.inner().devm.wait_for_completion(); } + } else { + // `Self::devres_callback` revokes `self.data` for us, hence wait for it to be done + // using this object. + self.inner().devm.wait_for_completion(); } + + // INVARIANT: At this point it is guaranteed that `inner` can't be accessed any more. + // + // SAFETY: `inner` is valid for dropping. + unsafe { core::ptr::drop_in_place(self.inner.get()) }; } } + +/// Consume `data` and [`Drop::drop`] `data` once `dev` is unbound. +fn register_foreign

(dev: &Device, data: P) -> Result +where + P: ForeignOwnable + Send + 'static, +{ + let ptr = data.into_foreign(); + + #[allow(clippy::missing_safety_doc)] + unsafe extern "C" fn callback(ptr: *mut kernel::ffi::c_void) { + // SAFETY: `ptr` is the pointer to the `ForeignOwnable` leaked above and hence valid. + drop(unsafe { P::from_foreign(ptr.cast()) }); + } + + // SAFETY: + // - `dev.as_raw()` is a pointer to a valid and bound device. + // - `ptr` is a valid pointer the `ForeignOwnable` devres takes ownership of. + to_result(unsafe { + // `devm_add_action_or_reset()` also calls `callback` on failure, such that the + // `ForeignOwnable` is released eventually. + bindings::devm_add_action_or_reset(dev.as_raw(), Some(callback::

), ptr.cast()) + }) +} + +/// Encapsulate `data` in a [`KBox`] and [`Drop::drop`] `data` once `dev` is unbound. +/// +/// # Examples +/// +/// ```no_run +/// use kernel::{device::{Bound, Device}, devres}; +/// +/// /// Registration of e.g. a class device, IRQ, etc. +/// struct Registration; +/// +/// impl Registration { +/// fn new() -> Self { +/// // register +/// +/// Self +/// } +/// } +/// +/// impl Drop for Registration { +/// fn drop(&mut self) { +/// // unregister +/// } +/// } +/// +/// fn from_bound_context(dev: &Device) -> Result { +/// devres::register(dev, Registration::new(), GFP_KERNEL) +/// } +/// ``` +pub fn register(dev: &Device, data: impl PinInit, flags: Flags) -> Result +where + T: Send + 'static, + Error: From, +{ + let data = KBox::pin_init(data, flags)?; + + register_foreign(dev, data) +} diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index a33261c62e0c..99dcf79f0897 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -5,14 +5,156 @@ //! C header: [`include/linux/dma-mapping.h`](srctree/include/linux/dma-mapping.h) use crate::{ - bindings, build_assert, - device::{Bound, Device}, - error::code::*, - error::Result, + bindings, build_assert, device, + device::{Bound, Core}, + error::{to_result, Result}, + prelude::*, transmute::{AsBytes, FromBytes}, types::ARef, }; +/// Trait to be implemented by DMA capable bus devices. +/// +/// The [`dma::Device`](Device) trait should be implemented by bus specific device representations, +/// where the underlying bus is DMA capable, such as [`pci::Device`](::kernel::pci::Device) or +/// [`platform::Device`](::kernel::platform::Device). +pub trait Device: AsRef> { + /// Set up the device's DMA streaming addressing capabilities. + /// + /// This method is usually called once from `probe()` as soon as the device capabilities are + /// known. + /// + /// # Safety + /// + /// This method must not be called concurrently with any DMA allocation or mapping primitives, + /// such as [`CoherentAllocation::alloc_attrs`]. + unsafe fn dma_set_mask(&self, mask: DmaMask) -> Result { + // SAFETY: + // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. + // - The safety requirement of this function guarantees that there are no concurrent calls + // to DMA allocation and mapping primitives using this mask. + to_result(unsafe { bindings::dma_set_mask(self.as_ref().as_raw(), mask.value()) }) + } + + /// Set up the device's DMA coherent addressing capabilities. + /// + /// This method is usually called once from `probe()` as soon as the device capabilities are + /// known. + /// + /// # Safety + /// + /// This method must not be called concurrently with any DMA allocation or mapping primitives, + /// such as [`CoherentAllocation::alloc_attrs`]. + unsafe fn dma_set_coherent_mask(&self, mask: DmaMask) -> Result { + // SAFETY: + // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. + // - The safety requirement of this function guarantees that there are no concurrent calls + // to DMA allocation and mapping primitives using this mask. + to_result(unsafe { bindings::dma_set_coherent_mask(self.as_ref().as_raw(), mask.value()) }) + } + + /// Set up the device's DMA addressing capabilities. + /// + /// This is a combination of [`Device::dma_set_mask`] and [`Device::dma_set_coherent_mask`]. + /// + /// This method is usually called once from `probe()` as soon as the device capabilities are + /// known. + /// + /// # Safety + /// + /// This method must not be called concurrently with any DMA allocation or mapping primitives, + /// such as [`CoherentAllocation::alloc_attrs`]. + unsafe fn dma_set_mask_and_coherent(&self, mask: DmaMask) -> Result { + // SAFETY: + // - By the type invariant of `device::Device`, `self.as_ref().as_raw()` is valid. + // - The safety requirement of this function guarantees that there are no concurrent calls + // to DMA allocation and mapping primitives using this mask. + to_result(unsafe { + bindings::dma_set_mask_and_coherent(self.as_ref().as_raw(), mask.value()) + }) + } +} + +/// A DMA mask that holds a bitmask with the lowest `n` bits set. +/// +/// Use [`DmaMask::new`] or [`DmaMask::try_new`] to construct a value. Values +/// are guaranteed to never exceed the bit width of `u64`. +/// +/// This is the Rust equivalent of the C macro `DMA_BIT_MASK()`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DmaMask(u64); + +impl DmaMask { + /// Constructs a `DmaMask` with the lowest `n` bits set to `1`. + /// + /// For `n <= 64`, sets exactly the lowest `n` bits. + /// For `n > 64`, results in a build error. + /// + /// # Examples + /// + /// ``` + /// use kernel::dma::DmaMask; + /// + /// let mask0 = DmaMask::new::<0>(); + /// assert_eq!(mask0.value(), 0); + /// + /// let mask1 = DmaMask::new::<1>(); + /// assert_eq!(mask1.value(), 0b1); + /// + /// let mask64 = DmaMask::new::<64>(); + /// assert_eq!(mask64.value(), u64::MAX); + /// + /// // Build failure. + /// // let mask_overflow = DmaMask::new::<100>(); + /// ``` + #[inline] + pub const fn new() -> Self { + let Ok(mask) = Self::try_new(N) else { + build_error!("Invalid DMA Mask."); + }; + + mask + } + + /// Constructs a `DmaMask` with the lowest `n` bits set to `1`. + /// + /// For `n <= 64`, sets exactly the lowest `n` bits. + /// For `n > 64`, returns [`EINVAL`]. + /// + /// # Examples + /// + /// ``` + /// use kernel::dma::DmaMask; + /// + /// let mask0 = DmaMask::try_new(0)?; + /// assert_eq!(mask0.value(), 0); + /// + /// let mask1 = DmaMask::try_new(1)?; + /// assert_eq!(mask1.value(), 0b1); + /// + /// let mask64 = DmaMask::try_new(64)?; + /// assert_eq!(mask64.value(), u64::MAX); + /// + /// let mask_overflow = DmaMask::try_new(100); + /// assert!(mask_overflow.is_err()); + /// # Ok::<(), Error>(()) + /// ``` + #[inline] + pub const fn try_new(n: u32) -> Result { + Ok(Self(match n { + 0 => 0, + 1..=64 => u64::MAX >> (64 - n), + _ => return Err(EINVAL), + })) + } + + /// Returns the underlying `u64` bitmask value. + #[inline] + pub const fn value(&self) -> u64 { + self.0 + } +} + /// Possible attributes associated with a DMA mapping. /// /// They can be combined with the operators `|`, `&`, and `!`. @@ -130,7 +272,7 @@ pub mod attrs { // Hence, find a way to revoke the device resources of a `CoherentAllocation`, but not the // entire `CoherentAllocation` including the allocated memory itself. pub struct CoherentAllocation { - dev: ARef, + dev: ARef, dma_handle: bindings::dma_addr_t, count: usize, cpu_addr: *mut T, @@ -152,7 +294,7 @@ impl CoherentAllocation { /// # Ok::<(), Error>(()) } /// ``` pub fn alloc_attrs( - dev: &Device, + dev: &device::Device, count: usize, gfp_flags: kernel::alloc::Flags, dma_attrs: Attrs, @@ -194,7 +336,7 @@ impl CoherentAllocation { /// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the /// `dma_attrs` is 0 by default. pub fn alloc_coherent( - dev: &Device, + dev: &device::Device, count: usize, gfp_flags: kernel::alloc::Flags, ) -> Result> { diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index ec9166cedfa7..a8f2675ba7a7 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -6,7 +6,7 @@ //! register using the [`Registration`] class. use crate::error::{Error, Result}; -use crate::{device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; +use crate::{acpi, device, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; use core::pin::Pin; use pin_init::{pin_data, pinned_drop, PinInit}; @@ -141,36 +141,76 @@ pub trait Adapter { /// The type holding driver private data about each device id supported by the driver. type IdInfo: 'static; + /// The [`acpi::IdTable`] of the corresponding driver + fn acpi_id_table() -> Option>; + + /// Returns the driver's private data from the matching entry in the [`acpi::IdTable`], if any. + /// + /// If this returns `None`, it means there is no match with an entry in the [`acpi::IdTable`]. + fn acpi_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { + #[cfg(not(CONFIG_ACPI))] + { + let _ = dev; + None + } + + #[cfg(CONFIG_ACPI)] + { + let table = Self::acpi_id_table()?; + + // SAFETY: + // - `table` has static lifetime, hence it's valid for read, + // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`. + let raw_id = unsafe { bindings::acpi_match_device(table.as_ptr(), dev.as_raw()) }; + + if raw_id.is_null() { + None + } else { + // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct acpi_device_id` + // and does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*raw_id.cast::() }; + + Some(table.info(::index(id))) + } + } + } + /// The [`of::IdTable`] of the corresponding driver. fn of_id_table() -> Option>; /// Returns the driver's private data from the matching entry in the [`of::IdTable`], if any. /// /// If this returns `None`, it means there is no match with an entry in the [`of::IdTable`]. - #[cfg(CONFIG_OF)] fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { - let table = Self::of_id_table()?; - - // SAFETY: - // - `table` has static lifetime, hence it's valid for read, - // - `dev` is guaranteed to be valid while it's alive, and so is `pdev.as_ref().as_raw()`. - let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) }; - - if raw_id.is_null() { + #[cfg(not(CONFIG_OF))] + { + let _ = dev; None - } else { - // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and - // does not add additional invariants, so it's safe to transmute. - let id = unsafe { &*raw_id.cast::() }; - - Some(table.info(::index(id))) } - } - #[cfg(not(CONFIG_OF))] - #[allow(missing_docs)] - fn of_id_info(_dev: &device::Device) -> Option<&'static Self::IdInfo> { - None + #[cfg(CONFIG_OF)] + { + let table = Self::of_id_table()?; + + // SAFETY: + // - `table` has static lifetime, hence it's valid for read, + // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`. + let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) }; + + if raw_id.is_null() { + None + } else { + // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` + // and does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*raw_id.cast::() }; + + Some( + table.info(::index( + id, + )), + ) + } + } } /// Returns the driver's private data from the matching entry of any of the ID tables, if any. @@ -178,6 +218,11 @@ pub trait Adapter { /// If this returns `None`, it means that there is no match in any of the ID tables directly /// associated with a [`device::Device`]. fn id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { + let id = Self::acpi_id_info(dev); + if id.is_some() { + return id; + } + let id = Self::of_id_info(dev); if id.is_some() { return id; diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 14c1aa402951..16935f42fe2e 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -200,7 +200,7 @@ impl AsRef for Device { fn as_ref(&self) -> &device::Device { // SAFETY: `bindings::drm_device::dev` is valid as long as the DRM device itself is valid, // which is guaranteed by the type invariant. - unsafe { device::Device::as_ref((*self.as_raw()).dev) } + unsafe { device::Device::from_raw((*self.as_raw()).dev) } } } diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index af93d46d03d3..fe7e8d06961a 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -5,9 +5,7 @@ //! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/drm_drv.h) use crate::{ - bindings, device, - devres::Devres, - drm, + bindings, device, devres, drm, error::{to_result, Result}, prelude::*, types::ARef, @@ -129,18 +127,22 @@ impl Registration { } /// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to - /// [`Devres`]. + /// [`devres::register`]. pub fn new_foreign_owned( drm: &drm::Device, dev: &device::Device, flags: usize, - ) -> Result { + ) -> Result + where + T: 'static, + { if drm.as_ref().as_raw() != dev.as_raw() { return Err(EINVAL); } let reg = Registration::::new(drm, flags)?; - Devres::new_foreign_owned(dev, reg, GFP_KERNEL) + + devres::register(dev, reg, GFP_KERNEL) } /// Returns a reference to the `Device` instance for this registration. diff --git a/rust/kernel/faux.rs b/rust/kernel/faux.rs index 8a50fcd4c9bb..7a906099993f 100644 --- a/rust/kernel/faux.rs +++ b/rust/kernel/faux.rs @@ -54,7 +54,7 @@ impl AsRef for Registration { fn as_ref(&self) -> &device::Device { // SAFETY: The underlying `device` in `faux_device` is guaranteed by the C API to be // a valid initialized `device`. - unsafe { device::Device::as_ref(addr_of_mut!((*self.as_raw()).dev)) } + unsafe { device::Device::from_raw(addr_of_mut!((*self.as_raw()).dev)) } } } diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 72d80a6f131e..b7fc759f8b5d 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -7,6 +7,11 @@ use crate::error::{code::EINVAL, Result}; use crate::{bindings, build_assert}; +pub mod mem; +pub mod resource; + +pub use resource::Resource; + /// Raw representation of an MMIO region. /// /// By itself, the existence of an instance of this structure does not provide any guarantees that diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs new file mode 100644 index 000000000000..6f99510bfc3a --- /dev/null +++ b/rust/kernel/io/mem.rs @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic memory-mapped IO. + +use core::ops::Deref; + +use crate::c_str; +use crate::device::Bound; +use crate::device::Device; +use crate::devres::Devres; +use crate::io; +use crate::io::resource::Region; +use crate::io::resource::Resource; +use crate::io::Io; +use crate::io::IoRaw; +use crate::prelude::*; + +/// An IO request for a specific device and resource. +pub struct IoRequest<'a> { + device: &'a Device, + resource: &'a Resource, +} + +impl<'a> IoRequest<'a> { + /// Creates a new [`IoRequest`] instance. + /// + /// # Safety + /// + /// Callers must ensure that `resource` is valid for `device` during the + /// lifetime `'a`. + pub(crate) unsafe fn new(device: &'a Device, resource: &'a Resource) -> Self { + IoRequest { device, resource } + } + + /// Maps an [`IoRequest`] where the size is known at compile time. + /// + /// This uses the [`ioremap()`] C API. + /// + /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + /// + /// # Examples + /// + /// The following example uses a [`kernel::platform::Device`] for + /// illustration purposes. + /// + /// ```no_run + /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// struct SampleDriver; + /// + /// impl platform::Driver for SampleDriver { + /// # type IdInfo = (); + /// + /// fn probe( + /// pdev: &platform::Device, + /// info: Option<&Self::IdInfo>, + /// ) -> Result>> { + /// let offset = 0; // Some offset. + /// + /// // If the size is known at compile time, use [`Self::iomap_sized`]. + /// // + /// // No runtime checks will apply when reading and writing. + /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; + /// let iomem = request.iomap_sized::<42>(); + /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; + /// + /// let io = iomem.access(pdev.as_ref())?; + /// + /// // Read and write a 32-bit value at `offset`. + /// let data = io.read32_relaxed(offset); + /// + /// io.write32_relaxed(data, offset); + /// + /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// } + /// } + /// ``` + pub fn iomap_sized(self) -> impl PinInit>, Error> + 'a { + IoMem::new(self) + } + + /// Same as [`Self::iomap_sized`] but with exclusive access to the + /// underlying region. + /// + /// This uses the [`ioremap()`] C API. + /// + /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + pub fn iomap_exclusive_sized( + self, + ) -> impl PinInit>, Error> + 'a { + ExclusiveIoMem::new(self) + } + + /// Maps an [`IoRequest`] where the size is not known at compile time, + /// + /// This uses the [`ioremap()`] C API. + /// + /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + /// + /// # Examples + /// + /// The following example uses a [`kernel::platform::Device`] for + /// illustration purposes. + /// + /// ```no_run + /// use kernel::{bindings, c_str, platform, of, device::Core}; + /// struct SampleDriver; + /// + /// impl platform::Driver for SampleDriver { + /// # type IdInfo = (); + /// + /// fn probe( + /// pdev: &platform::Device, + /// info: Option<&Self::IdInfo>, + /// ) -> Result>> { + /// let offset = 0; // Some offset. + /// + /// // Unlike [`Self::iomap_sized`], here the size of the memory region + /// // is not known at compile time, so only the `try_read*` and `try_write*` + /// // family of functions should be used, leading to runtime checks on every + /// // access. + /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; + /// let iomem = request.iomap(); + /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; + /// + /// let io = iomem.access(pdev.as_ref())?; + /// + /// let data = io.try_read32_relaxed(offset)?; + /// + /// io.try_write32_relaxed(data, offset)?; + /// + /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// } + /// } + /// ``` + pub fn iomap(self) -> impl PinInit>, Error> + 'a { + Self::iomap_sized::<0>(self) + } + + /// Same as [`Self::iomap`] but with exclusive access to the underlying + /// region. + pub fn iomap_exclusive(self) -> impl PinInit>, Error> + 'a { + Self::iomap_exclusive_sized::<0>(self) + } +} + +/// An exclusive memory-mapped IO region. +/// +/// # Invariants +/// +/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. +pub struct ExclusiveIoMem { + /// The underlying `IoMem` instance. + iomem: IoMem, + + /// The region abstraction. This represents exclusive access to the + /// range represented by the underlying `iomem`. + /// + /// This field is needed for ownership of the region. + _region: Region, +} + +impl ExclusiveIoMem { + /// Creates a new `ExclusiveIoMem` instance. + fn ioremap(resource: &Resource) -> Result { + let start = resource.start(); + let size = resource.size(); + let name = resource.name().unwrap_or(c_str!("")); + + let region = resource + .request_region( + start, + size, + name.to_cstring()?, + io::resource::Flags::IORESOURCE_MEM, + ) + .ok_or(EBUSY)?; + + let iomem = IoMem::ioremap(resource)?; + + let iomem = ExclusiveIoMem { + iomem, + _region: region, + }; + + Ok(iomem) + } + + /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`]. + pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit, Error> + 'a { + let dev = io_request.device; + let res = io_request.resource; + + Devres::new(dev, Self::ioremap(res)) + } +} + +impl Deref for ExclusiveIoMem { + type Target = Io; + + fn deref(&self) -> &Self::Target { + &self.iomem + } +} + +/// A generic memory-mapped IO region. +/// +/// Accesses to the underlying region is checked either at compile time, if the +/// region's size is known at that point, or at runtime otherwise. +/// +/// # Invariants +/// +/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the +/// start of the I/O memory mapped region. +pub struct IoMem { + io: IoRaw, +} + +impl IoMem { + fn ioremap(resource: &Resource) -> Result { + // Note: Some ioremap() implementations use types that depend on the CPU + // word width rather than the bus address width. + // + // TODO: Properly address this in the C code to avoid this `try_into`. + let size = resource.size().try_into()?; + if size == 0 { + return Err(EINVAL); + } + + let res_start = resource.start(); + + let addr = if resource + .flags() + .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED) + { + // SAFETY: + // - `res_start` and `size` are read from a presumably valid `struct resource`. + // - `size` is known not to be zero at this point. + unsafe { bindings::ioremap_np(res_start, size) } + } else { + // SAFETY: + // - `res_start` and `size` are read from a presumably valid `struct resource`. + // - `size` is known not to be zero at this point. + unsafe { bindings::ioremap(res_start, size) } + }; + + if addr.is_null() { + return Err(ENOMEM); + } + + let io = IoRaw::new(addr as usize, size)?; + let io = IoMem { io }; + + Ok(io) + } + + /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`]. + pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit, Error> + 'a { + let dev = io_request.device; + let res = io_request.resource; + + Devres::new(dev, Self::ioremap(res)) + } +} + +impl Drop for IoMem { + fn drop(&mut self) { + // SAFETY: Safe as by the invariant of `Io`. + unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } + } +} + +impl Deref for IoMem { + type Target = Io; + + fn deref(&self) -> &Self::Target { + // SAFETY: Safe as by the invariant of `IoMem`. + unsafe { Io::from_raw(&self.io) } + } +} diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs new file mode 100644 index 000000000000..bea3ee0ed87b --- /dev/null +++ b/rust/kernel/io/resource.rs @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Abstractions for [system +//! resources](https://docs.kernel.org/core-api/kernel-api.html#resources-management). +//! +//! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h) + +use core::ops::Deref; +use core::ptr::NonNull; + +use crate::prelude::*; +use crate::str::{CStr, CString}; +use crate::types::Opaque; + +/// Resource Size type. +/// +/// This is a type alias to either `u32` or `u64` depending on the config option +/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures. +pub type ResourceSize = bindings::phys_addr_t; + +/// A region allocated from a parent [`Resource`]. +/// +/// # Invariants +/// +/// - `self.0` points to a valid `bindings::resource` that was obtained through +/// `bindings::__request_region`. +pub struct Region { + /// The resource returned when the region was requested. + resource: NonNull, + /// The name that was passed in when the region was requested. We need to + /// store it for ownership reasons. + _name: CString, +} + +impl Deref for Region { + type Target = Resource; + + fn deref(&self) -> &Self::Target { + // SAFETY: Safe as per the invariant of `Region`. + unsafe { Resource::from_raw(self.resource.as_ptr()) } + } +} + +impl Drop for Region { + fn drop(&mut self) { + let (flags, start, size) = { + let res = &**self; + (res.flags(), res.start(), res.size()) + }; + + let release_fn = if flags.contains(Flags::IORESOURCE_MEM) { + bindings::release_mem_region + } else { + bindings::release_region + }; + + // SAFETY: Safe as per the invariant of `Region`. + unsafe { release_fn(start, size) }; + } +} + +// SAFETY: `Region` only holds a pointer to a C `struct resource`, which is safe to be used from +// any thread. +unsafe impl Send for Region {} + +// SAFETY: `Region` only holds a pointer to a C `struct resource`, references to which are +// safe to be used from any thread. +unsafe impl Sync for Region {} + +/// A resource abstraction. +/// +/// # Invariants +/// +/// [`Resource`] is a transparent wrapper around a valid `bindings::resource`. +#[repr(transparent)] +pub struct Resource(Opaque); + +impl Resource { + /// Creates a reference to a [`Resource`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that for the duration of 'a, the pointer will + /// point at a valid `bindings::resource`. + /// + /// The caller must also ensure that the [`Resource`] is only accessed via the + /// returned reference for the duration of 'a. + pub(crate) const unsafe fn from_raw<'a>(ptr: *mut bindings::resource) -> &'a Self { + // SAFETY: Self is a transparent wrapper around `Opaque`. + unsafe { &*ptr.cast() } + } + + /// Requests a resource region. + /// + /// Exclusive access will be given and the region will be marked as busy. + /// Further calls to [`Self::request_region`] will return [`None`] if + /// the region, or a part of it, is already in use. + pub fn request_region( + &self, + start: ResourceSize, + size: ResourceSize, + name: CString, + flags: Flags, + ) -> Option { + // SAFETY: + // - Safe as per the invariant of `Resource`. + // - `__request_region` will store a reference to the name, but that is + // safe as we own it and it will not be dropped until the `Region` is + // dropped. + let region = unsafe { + bindings::__request_region( + self.0.get(), + start, + size, + name.as_char_ptr(), + flags.0 as c_int, + ) + }; + + Some(Region { + resource: NonNull::new(region)?, + _name: name, + }) + } + + /// Returns the size of the resource. + pub fn size(&self) -> ResourceSize { + let inner = self.0.get(); + // SAFETY: Safe as per the invariants of `Resource`. + unsafe { bindings::resource_size(inner) } + } + + /// Returns the start address of the resource. + pub fn start(&self) -> ResourceSize { + let inner = self.0.get(); + // SAFETY: Safe as per the invariants of `Resource`. + unsafe { (*inner).start } + } + + /// Returns the name of the resource. + pub fn name(&self) -> Option<&CStr> { + let inner = self.0.get(); + + // SAFETY: Safe as per the invariants of `Resource`. + let name = unsafe { (*inner).name }; + + if name.is_null() { + return None; + } + + // SAFETY: In the C code, `resource::name` either contains a null + // pointer or points to a valid NUL-terminated C string, and at this + // point we know it is not null, so we can safely convert it to a + // `CStr`. + Some(unsafe { CStr::from_char_ptr(name) }) + } + + /// Returns the flags associated with the resource. + pub fn flags(&self) -> Flags { + let inner = self.0.get(); + // SAFETY: Safe as per the invariants of `Resource`. + let flags = unsafe { (*inner).flags }; + + Flags(flags) + } +} + +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, which is +// safe to be used from any thread. +unsafe impl Send for Resource {} + +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, references +// to which are safe to be used from any thread. +unsafe impl Sync for Resource {} + +/// Resource flags as stored in the C `struct resource::flags` field. +/// +/// They can be combined with the operators `|`, `&`, and `!`. +/// +/// Values can be used from the associated constants such as +/// [`Flags::IORESOURCE_IO`]. +#[derive(Clone, Copy, PartialEq)] +pub struct Flags(c_ulong); + +impl Flags { + /// Check whether `flags` is contained in `self`. + pub fn contains(self, flags: Flags) -> bool { + (self & flags) == flags + } +} + +impl core::ops::BitOr for Flags { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitAnd for Flags { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl core::ops::Not for Flags { + type Output = Self; + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Flags { + /// PCI/ISA I/O ports. + pub const IORESOURCE_IO: Flags = Flags::new(bindings::IORESOURCE_IO); + + /// Resource is software muxed. + pub const IORESOURCE_MUXED: Flags = Flags::new(bindings::IORESOURCE_MUXED); + + /// Resource represents a memory region. + pub const IORESOURCE_MEM: Flags = Flags::new(bindings::IORESOURCE_MEM); + + /// Resource represents a memory region that must be ioremaped using `ioremap_np`. + pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED); + + const fn new(value: u32) -> Self { + crate::build_assert!(value as u64 <= c_ulong::MAX as u64); + Flags(value as c_ulong) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 46d36a3e0bf1..a356b8ad9464 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -54,6 +54,7 @@ extern crate self as kernel; pub use ffi; +pub mod acpi; pub mod alloc; #[cfg(CONFIG_AUXILIARY_BUS)] pub mod auxiliary; diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index f5b9a6130a7a..a1eb5737e3cb 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -44,7 +44,13 @@ impl MiscDeviceOptions { /// /// # Invariants /// -/// `inner` is a registered misc device. +/// - `inner` contains a `struct miscdevice` that is registered using +/// `misc_register()`. +/// - This registration remains valid for the entire lifetime of the +/// [`MiscDeviceRegistration`] instance. +/// - Deregistration occurs exactly once in [`Drop`] via `misc_deregister()`. +/// - `inner` wraps a valid, pinned `miscdevice` created using +/// [`MiscDeviceOptions::into_raw`]. #[repr(transparent)] #[pin_data(PinnedDrop)] pub struct MiscDeviceRegistration { @@ -91,7 +97,7 @@ impl MiscDeviceRegistration { // function tells the borrow-checker that the `&Device` reference must not outlive the // `&MiscDeviceRegistration` used to obtain it, so the last use of the reference must be // before the underlying `struct miscdevice` is destroyed. - unsafe { Device::as_ref((*self.as_raw()).this_device) } + unsafe { Device::from_raw((*self.as_raw()).this_device) } } } diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index 32ea43ece646..95bd17d3e927 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -6,7 +6,7 @@ //! //! C headers: [`include/linux/phy.h`](srctree/include/linux/phy.h). -use crate::{error::*, prelude::*, types::Opaque}; +use crate::{device_id::RawDeviceId, error::*, prelude::*, types::Opaque}; use core::{marker::PhantomData, ptr::addr_of_mut}; pub mod reg; @@ -285,7 +285,7 @@ impl AsRef for Device { fn as_ref(&self) -> &kernel::device::Device { let phydev = self.0.get(); // SAFETY: The struct invariant ensures that `mdio.dev` is valid. - unsafe { kernel::device::Device::as_ref(addr_of_mut!((*phydev).mdio.dev)) } + unsafe { kernel::device::Device::from_raw(addr_of_mut!((*phydev).mdio.dev)) } } } @@ -507,7 +507,7 @@ pub const fn create_phy_driver() -> DriverVTable { DriverVTable(Opaque::new(bindings::phy_driver { name: T::NAME.as_char_ptr().cast_mut(), flags: T::FLAGS, - phy_id: T::PHY_DEVICE_ID.id, + phy_id: T::PHY_DEVICE_ID.id(), phy_id_mask: T::PHY_DEVICE_ID.mask_as_int(), soft_reset: if T::HAS_SOFT_RESET { Some(Adapter::::soft_reset_callback) @@ -691,42 +691,41 @@ impl Drop for Registration { /// /// Represents the kernel's `struct mdio_device_id`. This is used to find an appropriate /// PHY driver. -pub struct DeviceId { - id: u32, - mask: DeviceMask, -} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct DeviceId(bindings::mdio_device_id); impl DeviceId { /// Creates a new instance with the exact match mask. pub const fn new_with_exact_mask(id: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Exact, - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Exact.as_int(), + }) } /// Creates a new instance with the model match mask. pub const fn new_with_model_mask(id: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Model, - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Model.as_int(), + }) } /// Creates a new instance with the vendor match mask. pub const fn new_with_vendor_mask(id: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Vendor, - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Vendor.as_int(), + }) } /// Creates a new instance with a custom match mask. pub const fn new_with_custom_mask(id: u32, mask: u32) -> Self { - DeviceId { - id, - mask: DeviceMask::Custom(mask), - } + Self(bindings::mdio_device_id { + phy_id: id, + phy_id_mask: DeviceMask::Custom(mask).as_int(), + }) } /// Creates a new instance from [`Driver`]. @@ -734,21 +733,29 @@ impl DeviceId { T::PHY_DEVICE_ID } - /// Get a `mask` as u32. + /// Get the MDIO device's PHY ID. + pub const fn id(&self) -> u32 { + self.0.phy_id + } + + /// Get the MDIO device's match mask. pub const fn mask_as_int(&self) -> u32 { - self.mask.as_int() + self.0.phy_id_mask } // macro use only #[doc(hidden)] pub const fn mdio_device_id(&self) -> bindings::mdio_device_id { - bindings::mdio_device_id { - phy_id: self.id, - phy_id_mask: self.mask.as_int(), - } + self.0 } } +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct mdio_device_id` +// and does not add additional invariants, so it's safe to transmute to `RawType`. +unsafe impl RawDeviceId for DeviceId { + type RawType = bindings::mdio_device_id; +} + enum DeviceMask { Exact, Model, @@ -849,19 +856,18 @@ impl DeviceMask { /// } /// }; /// -/// const _DEVICE_TABLE: [::kernel::bindings::mdio_device_id; 2] = [ -/// ::kernel::bindings::mdio_device_id { -/// phy_id: 0x00000001, -/// phy_id_mask: 0xffffffff, -/// }, -/// ::kernel::bindings::mdio_device_id { -/// phy_id: 0, -/// phy_id_mask: 0, -/// }, -/// ]; -/// #[cfg(MODULE)] -/// #[no_mangle] -/// static __mod_device_table__mdio__phydev: [::kernel::bindings::mdio_device_id; 2] = _DEVICE_TABLE; +/// const N: usize = 1; +/// +/// const TABLE: ::kernel::device_id::IdArray<::kernel::net::phy::DeviceId, (), N> = +/// ::kernel::device_id::IdArray::new_without_index([ +/// ::kernel::net::phy::DeviceId( +/// ::kernel::bindings::mdio_device_id { +/// phy_id: 0x00000001, +/// phy_id_mask: 0xffffffff, +/// }), +/// ]); +/// +/// ::kernel::module_device_table!("mdio", phydev, TABLE); /// ``` #[macro_export] macro_rules! module_phy_driver { @@ -872,20 +878,12 @@ macro_rules! module_phy_driver { }; (@device_table [$($dev:expr),+]) => { - // SAFETY: C will not read off the end of this constant since the last element is zero. - const _DEVICE_TABLE: [$crate::bindings::mdio_device_id; - $crate::module_phy_driver!(@count_devices $($dev),+) + 1] = [ - $($dev.mdio_device_id()),+, - $crate::bindings::mdio_device_id { - phy_id: 0, - phy_id_mask: 0 - } - ]; + const N: usize = $crate::module_phy_driver!(@count_devices $($dev),+); - #[cfg(MODULE)] - #[no_mangle] - static __mod_device_table__mdio__phydev: [$crate::bindings::mdio_device_id; - $crate::module_phy_driver!(@count_devices $($dev),+) + 1] = _DEVICE_TABLE; + const TABLE: $crate::device_id::IdArray<$crate::net::phy::DeviceId, (), N> = + $crate::device_id::IdArray::new_without_index([ $(($dev,())),+, ]); + + $crate::module_device_table!("mdio", phydev, TABLE); }; (drivers: [$($driver:ident),+ $(,)?], device_table: [$($dev:expr),+ $(,)?], $($f:tt)*) => { diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs index 04f2d8ef29cb..0888469bddb7 100644 --- a/rust/kernel/of.rs +++ b/rust/kernel/of.rs @@ -2,7 +2,11 @@ //! Device Tree / Open Firmware abstractions. -use crate::{bindings, device_id::RawDeviceId, prelude::*}; +use crate::{ + bindings, + device_id::{RawDeviceId, RawDeviceIdIndex}, + prelude::*, +}; /// IdTable type for OF drivers. pub type IdTable = &'static dyn kernel::device_id::IdTable; @@ -12,13 +16,14 @@ pub type IdTable = &'static dyn kernel::device_id::IdTable; #[derive(Clone, Copy)] pub struct DeviceId(bindings::of_device_id); -// SAFETY: -// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add -// additional invariants, so it's safe to transmute to `RawType`. -// * `DRIVER_DATA_OFFSET` is the offset to the `data` field. +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct of_device_id` and +// does not add additional invariants, so it's safe to transmute to `RawType`. unsafe impl RawDeviceId for DeviceId { type RawType = bindings::of_device_id; +} +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `data` field. +unsafe impl RawDeviceIdIndex for DeviceId { const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data); fn index(&self) -> usize { diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 8435f8132e38..44a2f3d2884a 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,16 +5,15 @@ //! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) use crate::{ - alloc::flags::*, bindings, container_of, device, - device_id::RawDeviceId, + device_id::{RawDeviceId, RawDeviceIdIndex}, devres::Devres, driver, - error::{to_result, Result}, + error::{from_result, to_result, Result}, io::Io, io::IoRaw, str::CStr, - types::{ARef, ForeignOwnable, Opaque}, + types::{ARef, Opaque}, ThisModule, }; use core::{ @@ -66,35 +65,34 @@ impl Adapter { // `struct pci_dev`. // // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; + let pdev = unsafe { &*pdev.cast::>() }; - // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_device_id` and + // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct pci_device_id` and // does not add additional invariants, so it's safe to transmute. let id = unsafe { &*id.cast::() }; let info = T::ID_TABLE.info(id.index()); - match T::probe(pdev, info) { - Ok(data) => { - // Let the `struct pci_dev` own a reference of the driver's private data. - // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a - // `struct pci_dev`. - unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; - } - Err(err) => return Error::to_errno(err), - } + from_result(|| { + let data = T::probe(pdev, info)?; - 0 + pdev.as_ref().set_drvdata(data); + Ok(0) + }) } extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { // SAFETY: The PCI bus only ever calls the remove callback with a valid pointer to a // `struct pci_dev`. - let ptr = unsafe { bindings::pci_get_drvdata(pdev) }.cast(); + // + // INVARIANT: `pdev` is valid for the duration of `remove_callback()`. + let pdev = unsafe { &*pdev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to - // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized - // `KBox` pointer created through `KBox::into_foreign`. - let _ = unsafe { KBox::::from_foreign(ptr) }; + // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called + // and stored a `Pin>`. + let data = unsafe { pdev.as_ref().drvdata_obtain::>>() }; + + T::unbind(pdev, data.as_ref()); } } @@ -161,13 +159,14 @@ impl DeviceId { } } -// SAFETY: -// * `DeviceId` is a `#[repr(transparent)` wrapper of `pci_device_id` and does not add -// additional invariants, so it's safe to transmute to `RawType`. -// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `pci_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. unsafe impl RawDeviceId for DeviceId { type RawType = bindings::pci_device_id; +} +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceIdIndex for DeviceId { const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::pci_device_id, driver_data); fn index(&self) -> usize { @@ -241,6 +240,20 @@ pub trait Driver: Send { /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. fn probe(dev: &Device, id_info: &Self::IdInfo) -> Result>>; + + /// Platform driver unbind. + /// + /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback + /// is optional. + /// + /// This callback serves as a place for drivers to perform teardown operations that require a + /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O + /// operations to gracefully tear down the device. + /// + /// Otherwise, release operations for driver resources should be performed in `Self::drop`. + fn unbind(dev: &Device, this: Pin<&Self>) { + let _ = (dev, this); + } } /// The PCI device representation. @@ -251,7 +264,8 @@ pub trait Driver: Send { /// /// # Invariants /// -/// A [`Device`] instance represents a valid `struct device` created by the C portion of the kernel. +/// A [`Device`] instance represents a valid `struct pci_dev` created by the C portion of the +/// kernel. #[repr(transparent)] pub struct Device( Opaque, @@ -398,19 +412,20 @@ impl Device { impl Device { /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks /// can be performed on compile time for offsets (plus the requested type size) < SIZE. - pub fn iomap_region_sized( - &self, + pub fn iomap_region_sized<'a, const SIZE: usize>( + &'a self, bar: u32, - name: &CStr, - ) -> Result>> { - let bar = Bar::::new(self, bar, name)?; - let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; - - Ok(devres) + name: &'a CStr, + ) -> impl PinInit>, Error> + 'a { + Devres::new(self.as_ref(), Bar::::new(self, bar, name)) } /// Mapps an entire PCI-BAR after performing a region-request on it. - pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result> { + pub fn iomap_region<'a>( + &'a self, + bar: u32, + name: &'a CStr, + ) -> impl PinInit, Error> + 'a { self.iomap_region_sized::<0>(bar, name) } } @@ -434,6 +449,8 @@ impl Device { kernel::impl_device_context_deref!(unsafe { Device }); kernel::impl_device_context_into_aref!(Device); +impl crate::dma::Device for Device {} + // SAFETY: Instances of `Device` are always reference-counted. unsafe impl crate::types::AlwaysRefCounted for Device { fn inc_ref(&self) { @@ -454,7 +471,7 @@ impl AsRef> for Device { let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) }; // SAFETY: `dev` points to a valid `struct device`. - unsafe { device::Device::as_ref(dev) } + unsafe { device::Device::from_raw(dev) } } } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 5b21fa517e55..b4d3087aff52 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -5,12 +5,14 @@ //! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) use crate::{ - bindings, container_of, device, driver, - error::{to_result, Result}, + acpi, bindings, container_of, + device::{self, Bound}, + driver, + error::{from_result, to_result, Result}, + io::{mem::IoRequest, Resource}, of, prelude::*, - str::CStr, - types::{ForeignOwnable, Opaque}, + types::Opaque, ThisModule, }; @@ -37,12 +39,18 @@ unsafe impl driver::RegistrationOps for Adapter { None => core::ptr::null(), }; + let acpi_table = match T::ACPI_ID_TABLE { + Some(table) => table.as_ptr(), + None => core::ptr::null(), + }; + // SAFETY: It's safe to set the fields of `struct platform_driver` on initialization. unsafe { (*pdrv.get()).driver.name = name.as_char_ptr(); (*pdrv.get()).probe = Some(Self::probe_callback); (*pdrv.get()).remove = Some(Self::remove_callback); (*pdrv.get()).driver.of_match_table = of_table; + (*pdrv.get()).driver.acpi_match_table = acpi_table; } // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. @@ -61,30 +69,30 @@ impl Adapter { // `struct platform_device`. // // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. - let pdev = unsafe { &*pdev.cast::>() }; - + let pdev = unsafe { &*pdev.cast::>() }; let info = ::id_info(pdev.as_ref()); - match T::probe(pdev, info) { - Ok(data) => { - // Let the `struct platform_device` own a reference of the driver's private data. - // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a - // `struct platform_device`. - unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; - } - Err(err) => return Error::to_errno(err), - } - 0 + from_result(|| { + let data = T::probe(pdev, info)?; + + pdev.as_ref().set_drvdata(data); + Ok(0) + }) } extern "C" fn remove_callback(pdev: *mut bindings::platform_device) { - // SAFETY: `pdev` is a valid pointer to a `struct platform_device`. - let ptr = unsafe { bindings::platform_get_drvdata(pdev) }.cast(); + // SAFETY: The platform bus only ever calls the remove callback with a valid pointer to a + // `struct platform_device`. + // + // INVARIANT: `pdev` is valid for the duration of `probe_callback()`. + let pdev = unsafe { &*pdev.cast::>() }; // SAFETY: `remove_callback` is only ever called after a successful call to - // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized - // `KBox` pointer created through `KBox::into_foreign`. - let _ = unsafe { KBox::::from_foreign(ptr) }; + // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called + // and stored a `Pin>`. + let data = unsafe { pdev.as_ref().drvdata_obtain::>>() }; + + T::unbind(pdev, data.as_ref()); } } @@ -94,6 +102,10 @@ impl driver::Adapter for Adapter { fn of_id_table() -> Option> { T::OF_ID_TABLE } + + fn acpi_id_table() -> Option> { + T::ACPI_ID_TABLE + } } /// Declares a kernel module that exposes a single platform driver. @@ -123,7 +135,7 @@ macro_rules! module_platform_driver { /// # Example /// ///``` -/// # use kernel::{bindings, c_str, device::Core, of, platform}; +/// # use kernel::{acpi, bindings, c_str, device::Core, of, platform}; /// /// struct MyDriver; /// @@ -136,9 +148,19 @@ macro_rules! module_platform_driver { /// ] /// ); /// +/// kernel::acpi_device_table!( +/// ACPI_TABLE, +/// MODULE_ACPI_TABLE, +/// ::IdInfo, +/// [ +/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// ] +/// ); +/// /// impl platform::Driver for MyDriver { /// type IdInfo = (); /// const OF_ID_TABLE: Option> = Some(&OF_TABLE); +/// const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); /// /// fn probe( /// _pdev: &platform::Device, @@ -158,7 +180,10 @@ pub trait Driver: Send { type IdInfo: 'static; /// The table of OF device ids supported by the driver. - const OF_ID_TABLE: Option>; + const OF_ID_TABLE: Option> = None; + + /// The table of ACPI device ids supported by the driver. + const ACPI_ID_TABLE: Option> = None; /// Platform driver probe. /// @@ -166,6 +191,20 @@ pub trait Driver: Send { /// Implementers should attempt to initialize the device here. fn probe(dev: &Device, id_info: Option<&Self::IdInfo>) -> Result>>; + + /// Platform driver unbind. + /// + /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback + /// is optional. + /// + /// This callback serves as a place for drivers to perform teardown operations that require a + /// `&Device` or `&Device` reference. For instance, drivers may try to perform I/O + /// operations to gracefully tear down the device. + /// + /// Otherwise, release operations for driver resources should be performed in `Self::drop`. + fn unbind(dev: &Device, this: Pin<&Self>) { + let _ = (dev, this); + } } /// The platform device representation. @@ -188,6 +227,61 @@ impl Device { fn as_raw(&self) -> *mut bindings::platform_device { self.0.get() } + + /// Returns the resource at `index`, if any. + pub fn resource_by_index(&self, index: u32) -> Option<&Resource> { + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct platform_device`. + let resource = unsafe { + bindings::platform_get_resource(self.as_raw(), bindings::IORESOURCE_MEM, index) + }; + + if resource.is_null() { + return None; + } + + // SAFETY: `resource` is a valid pointer to a `struct resource` as + // returned by `platform_get_resource`. + Some(unsafe { Resource::from_raw(resource) }) + } + + /// Returns the resource with a given `name`, if any. + pub fn resource_by_name(&self, name: &CStr) -> Option<&Resource> { + // SAFETY: `self.as_raw()` returns a valid pointer to a `struct + // platform_device` and `name` points to a valid C string. + let resource = unsafe { + bindings::platform_get_resource_byname( + self.as_raw(), + bindings::IORESOURCE_MEM, + name.as_char_ptr(), + ) + }; + + if resource.is_null() { + return None; + } + + // SAFETY: `resource` is a valid pointer to a `struct resource` as + // returned by `platform_get_resource`. + Some(unsafe { Resource::from_raw(resource) }) + } +} + +impl Device { + /// Returns an `IoRequest` for the resource at `index`, if any. + pub fn io_request_by_index(&self, index: u32) -> Option> { + self.resource_by_index(index) + // SAFETY: `resource` is a valid resource for `&self` during the + // lifetime of the `IoRequest`. + .map(|resource| unsafe { IoRequest::new(self.as_ref(), resource) }) + } + + /// Returns an `IoRequest` for the resource with a given `name`, if any. + pub fn io_request_by_name(&self, name: &CStr) -> Option> { + self.resource_by_name(name) + // SAFETY: `resource` is a valid resource for `&self` during the + // lifetime of the `IoRequest`. + .map(|resource| unsafe { IoRequest::new(self.as_ref(), resource) }) + } } // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic @@ -195,6 +289,8 @@ impl Device { kernel::impl_device_context_deref!(unsafe { Device }); kernel::impl_device_context_into_aref!(Device); +impl crate::dma::Device for Device {} + // SAFETY: Instances of `Device` are always reference-counted. unsafe impl crate::types::AlwaysRefCounted for Device { fn inc_ref(&self) { @@ -215,7 +311,7 @@ impl AsRef> for Device { let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) }; // SAFETY: `dev` points to a valid `struct device`. - unsafe { device::Device::as_ref(dev) } + unsafe { device::Device::from_raw(dev) } } } diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs index 06a3cdfce344..46768b374656 100644 --- a/rust/kernel/revocable.rs +++ b/rust/kernel/revocable.rs @@ -5,6 +5,8 @@ //! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence //! of a [`RevocableGuard`] ensures that objects remain valid. +use pin_init::Wrapper; + use crate::{bindings, prelude::*, sync::rcu, types::Opaque}; use core::{ marker::PhantomData, @@ -80,11 +82,11 @@ unsafe impl Sync for Revocable {} impl Revocable { /// Creates a new revocable instance of the given data. - pub fn new(data: impl PinInit) -> impl PinInit { - pin_init!(Self { + pub fn new(data: impl PinInit) -> impl PinInit { + try_pin_init!(Self { is_available: AtomicBool::new(true), data <- Opaque::pin_init(data), - }) + }? E) } /// Tries to access the revocable wrapped object. diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 22985b6f6982..3958a5f44d56 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -9,7 +9,7 @@ use core::{ ops::{Deref, DerefMut}, ptr::NonNull, }; -use pin_init::{PinInit, Zeroable}; +use pin_init::{PinInit, Wrapper, Zeroable}; /// Used to transfer ownership to and from foreign (non-Rust) languages. /// @@ -353,17 +353,6 @@ impl Opaque { } } - /// Create an opaque pin-initializer from the given pin-initializer. - pub fn pin_init(slot: impl PinInit) -> impl PinInit { - Self::ffi_init(|ptr: *mut T| { - // SAFETY: - // - `ptr` is a valid pointer to uninitialized memory, - // - `slot` is not accessed on error; the call is infallible, - // - `slot` is pinned in memory. - let _ = unsafe { PinInit::::__pinned_init(slot, ptr) }; - }) - } - /// Creates a pin-initializer from the given initializer closure. /// /// The returned initializer calls the given closure with the pointer to the inner `T` of this @@ -415,6 +404,19 @@ impl Opaque { } } +impl Wrapper for Opaque { + /// Create an opaque pin-initializer from the given pin-initializer. + fn pin_init(slot: impl PinInit) -> impl PinInit { + Self::try_ffi_init(|ptr: *mut T| { + // SAFETY: + // - `ptr` is a valid pointer to uninitialized memory, + // - `slot` is not accessed on error, + // - `slot` is pinned in memory. + unsafe { PinInit::::__pinned_init(slot, ptr) } + }) + } +} + /// Types that are _always_ reference counted. /// /// It allows such types to define their own custom ref increment and decrement functions. diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 9ab34036e6bc..f4e034497cdd 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1390,20 +1390,44 @@ where unsafe { pin_init_from_closure(init) } } -// SAFETY: Every type can be initialized by-value. -unsafe impl Init for T { - unsafe fn __init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: TODO. +// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`. +unsafe impl Init for T { + unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { + // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self) }; Ok(()) } } -// SAFETY: Every type can be initialized by-value. `__pinned_init` calls `__init`. -unsafe impl PinInit for T { +// SAFETY: the `__pinned_init` function always returns `Ok(())` and initializes every field of +// `slot`. Additionally, all pinning invariants of `T` are upheld. +unsafe impl PinInit for T { + unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), Infallible> { + // SAFETY: `slot` is valid for writes by the safety requirements of this function. + unsafe { slot.write(self) }; + Ok(()) + } +} + +// SAFETY: when the `__init` function returns with +// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. +// - `Err(err)`, slot was not written to. +unsafe impl Init for Result { + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: `slot` is valid for writes by the safety requirements of this function. + unsafe { slot.write(self?) }; + Ok(()) + } +} + +// SAFETY: when the `__pinned_init` function returns with +// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. +// - `Err(err)`, slot was not written to. +unsafe impl PinInit for Result { unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: TODO. - unsafe { self.__init(slot) } + // SAFETY: `slot` is valid for writes by the safety requirements of this function. + unsafe { slot.write(self?) }; + Ok(()) } } diff --git a/samples/kobject/kobject-example.c b/samples/kobject/kobject-example.c index c9c3db19799a..36d87ca0bee2 100644 --- a/samples/kobject/kobject-example.c +++ b/samples/kobject/kobject-example.c @@ -13,7 +13,7 @@ /* * This module shows how to create a simple subdirectory in sysfs called - * /sys/kernel/kobject-example In that directory, 3 files are created: + * /sys/kernel/kobject_example In that directory, 3 files are created: * "foo", "baz", and "bar". If an integer is written to these files, it can be * later read out of it. */ @@ -102,7 +102,7 @@ static struct attribute *attrs[] = { * created for the attributes with the directory being the name of the * attribute group. */ -static struct attribute_group attr_group = { +static const struct attribute_group attr_group = { .attrs = attrs, }; diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c index 552d7e363539..579ce150217c 100644 --- a/samples/kobject/kset-example.c +++ b/samples/kobject/kset-example.c @@ -14,8 +14,8 @@ /* * This module shows how to create a kset in sysfs called - * /sys/kernel/kset-example - * Then tree kobjects are created and assigned to this kset, "foo", "baz", + * /sys/kernel/kset_example + * Then three kobjects are created and assigned to this kset, "foo", "baz", * and "bar". In those kobjects, attributes of the same name are also * created and if an integer is written to these files, it can be later * read out of it. diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 874c2c964afa..12370bca97bc 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -4,7 +4,14 @@ //! //! To make this driver probe, QEMU must be run with `-device pci-testdev`. -use kernel::{bindings, device::Core, dma::CoherentAllocation, pci, prelude::*, types::ARef}; +use kernel::{ + bindings, + device::Core, + dma::{CoherentAllocation, Device, DmaMask}, + pci, + prelude::*, + types::ARef, +}; struct DmaSampleDriver { pdev: ARef, @@ -51,6 +58,11 @@ impl pci::Driver for DmaSampleDriver { fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); + let mask = DmaMask::new::<64>(); + + // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives. + unsafe { pdev.dma_set_mask_and_coherent(mask)? }; + let ca: CoherentAllocation = CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 3e15e6d002bb..b25628604a93 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -5,8 +5,7 @@ //! To make this driver probe, QEMU must be run with `-device pci-testdev`. use kernel::{ - auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, str::CStr, - InPlaceModule, + auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, InPlaceModule, }; use pin_init::PinInit; diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 15147e4401b2..606946ff4d7f 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -18,16 +18,19 @@ impl Regs { type Bar0 = pci::Bar<{ Regs::END }>; -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] struct TestIndex(u8); impl TestIndex { const NO_EVENTFD: Self = Self(0); } +#[pin_data(PinnedDrop)] struct SampleDriver { pdev: ARef, + #[pin] bar: Devres, + index: TestIndex, } kernel::pci_device_table!( @@ -73,13 +76,12 @@ impl pci::Driver for SampleDriver { pdev.enable_device_mem()?; pdev.set_master(); - let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?; - - let drvdata = KBox::new( - Self { + let drvdata = KBox::pin_init( + try_pin_init!(Self { pdev: pdev.into(), - bar, - }, + bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci")), + index: *info, + }), GFP_KERNEL, )?; @@ -90,12 +92,20 @@ impl pci::Driver for SampleDriver { Self::testdev(info, bar)? ); - Ok(drvdata.into()) + Ok(drvdata) + } + + fn unbind(pdev: &pci::Device, this: Pin<&Self>) { + if let Ok(bar) = this.bar.access(pdev.as_ref()) { + // Reset pci-testdev by writing a new test index. + bar.write8(this.index.0, Regs::TEST); + } } } -impl Drop for SampleDriver { - fn drop(&mut self) { +#[pinned_drop] +impl PinnedDrop for SampleDriver { + fn drop(self: Pin<&mut Self>) { dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n"); } } diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 8b42b3cfb363..69ed55b7b0fa 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -2,7 +2,78 @@ //! Rust Platform driver sample. -use kernel::{c_str, device::Core, of, platform, prelude::*, types::ARef}; +//! ACPI match table test +//! +//! This demonstrates how to test an ACPI-based Rust platform driver using QEMU +//! with a custom SSDT. +//! +//! Steps: +//! +//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content: +//! +//! ```asl +//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001) +//! { +//! Scope (\_SB) +//! { +//! Device (T432) +//! { +//! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match +//! Name (_UID, 1) +//! Name (_STA, 0x0F) // Device present, enabled +//! Name (_CRS, ResourceTemplate () +//! { +//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000) +//! }) +//! } +//! } +//! } +//! ``` +//! +//! 2. **Compile the table**: +//! +//! ```sh +//! iasl -tc ssdt.dsl +//! ``` +//! +//! This generates `ssdt.aml` +//! +//! 3. **Run QEMU** with the compiled AML file: +//! +//! ```sh +//! qemu-system-x86_64 -m 512M \ +//! -enable-kvm \ +//! -kernel path/to/bzImage \ +//! -append "root=/dev/sda console=ttyS0" \ +//! -hda rootfs.img \ +//! -serial stdio \ +//! -acpitable file=ssdt.aml +//! ``` +//! +//! Requirements: +//! - The `rust_driver_platform` must be present either: +//! - built directly into the kernel (`bzImage`), or +//! - available as a `.ko` file and loadable from `rootfs.img` +//! +//! 4. **Verify it worked** by checking `dmesg`: +//! +//! ``` +//! rust_driver_platform LNUXBEEF:00: Probed with info: '0'. +//! ``` +//! + +use kernel::{ + acpi, c_str, + device::{ + self, + property::{FwNodeReferenceArgs, NArgs}, + Core, + }, + of, platform, + prelude::*, + str::CString, + types::ARef, +}; struct SampleDriver { pdev: ARef, @@ -17,18 +88,32 @@ kernel::of_device_table!( [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))] ); +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + ::IdInfo, + [(acpi::DeviceId::new(c_str!("LNUXBEEF")), Info(0))] +); + impl platform::Driver for SampleDriver { type IdInfo = Info; const OF_ID_TABLE: Option> = Some(&OF_TABLE); + const ACPI_ID_TABLE: Option> = Some(&ACPI_TABLE); fn probe( pdev: &platform::Device, info: Option<&Self::IdInfo>, ) -> Result>> { - dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n"); + let dev = pdev.as_ref(); + + dev_dbg!(dev, "Probe Rust Platform driver sample.\n"); if let Some(info) = info { - dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0); + dev_info!(dev, "Probed with info: '{}'.\n", info.0); + } + + if dev.fwnode().is_some_and(|node| node.is_of_node()) { + Self::properties_parse(dev)?; } let drvdata = KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?; @@ -37,6 +122,62 @@ impl platform::Driver for SampleDriver { } } +impl SampleDriver { + fn properties_parse(dev: &device::Device) -> Result { + let fwnode = dev.fwnode().ok_or(ENOENT)?; + + if let Ok(idx) = + fwnode.property_match_string(c_str!("compatible"), c_str!("test,rust-device")) + { + dev_info!(dev, "matched compatible string idx = {}\n", idx); + } + + let name = c_str!("compatible"); + let prop = fwnode.property_read::(name).required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:?}'\n"); + + let name = c_str!("test,bool-prop"); + let prop = fwnode.property_read_bool(c_str!("test,bool-prop")); + dev_info!(dev, "'{name}'='{prop}'\n"); + + if fwnode.property_present(c_str!("test,u32-prop")) { + dev_info!(dev, "'test,u32-prop' is present\n"); + } + + let name = c_str!("test,u32-optional-prop"); + let prop = fwnode.property_read::(name).or(0x12); + dev_info!(dev, "'{name}'='{prop:#x}' (default = 0x12)\n",); + + // A missing required property will print an error. Discard the error to + // prevent properties_parse from failing in that case. + let name = c_str!("test,u32-required-prop"); + let _ = fwnode.property_read::(name).required_by(dev); + + let name = c_str!("test,u32-prop"); + let prop: u32 = fwnode.property_read(name).required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:#x}'\n"); + + let name = c_str!("test,i16-array"); + let prop: [i16; 4] = fwnode.property_read(name).required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:?}'\n"); + let len = fwnode.property_count_elem::(name)?; + dev_info!(dev, "'{name}' length is {len}\n",); + + let name = c_str!("test,i16-array"); + let prop: KVec = fwnode.property_read_array_vec(name, 4)?.required_by(dev)?; + dev_info!(dev, "'{name}'='{prop:?}' (KVec)\n"); + + for child in fwnode.children() { + let name = c_str!("test,ref-arg"); + let nargs = NArgs::N(2); + let prop: FwNodeReferenceArgs = child.property_get_reference_args(name, nargs, 0)?; + dev_info!(dev, "'{name}'='{prop:?}'\n"); + } + + Ok(()) + } +} + impl Drop for SampleDriver { fn drop(&mut self) { dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n"); diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index e8609a03c3d8..6eb94fddc338 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -1,6 +1,7 @@ acpi_dock_ops address_space_operations backlight_ops +bin_attribute block_device_operations bus_type clk_ops diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 339b31e6a6b5..b95de208265a 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -293,6 +293,14 @@ ifdef INSTALL_PATH $(MAKE) -s --no-print-directory OUTPUT=$$BUILD_TARGET COLLECTION=$$TARGET \ -C $$TARGET emit_tests >> $(TEST_LIST); \ done; + @VERSION=$$(git describe HEAD 2>/dev/null); \ + if [ -n "$$VERSION" ]; then \ + echo "$$VERSION" > $(INSTALL_PATH)/VERSION; \ + printf "Version saved to $(INSTALL_PATH)/VERSION\n"; \ + else \ + printf "Unable to get version from git describe\n"; \ + fi + @echo "**Kselftest Installation is complete: $(INSTALL_PATH)**" else $(error Error: set INSTALL_PATH to use install) endif diff --git a/tools/testing/selftests/breakpoints/step_after_suspend_test.c b/tools/testing/selftests/breakpoints/step_after_suspend_test.c index 8d275f03e977..8d233ac95696 100644 --- a/tools/testing/selftests/breakpoints/step_after_suspend_test.c +++ b/tools/testing/selftests/breakpoints/step_after_suspend_test.c @@ -127,22 +127,42 @@ int run_test(int cpu) return KSFT_PASS; } +/* + * Reads the suspend success count from sysfs. + * Returns the count on success or exits on failure. + */ +static int get_suspend_success_count_or_fail(void) +{ + FILE *fp; + int val; + + fp = fopen("/sys/power/suspend_stats/success", "r"); + if (!fp) + ksft_exit_fail_msg( + "Failed to open suspend_stats/success: %s\n", + strerror(errno)); + + if (fscanf(fp, "%d", &val) != 1) { + fclose(fp); + ksft_exit_fail_msg( + "Failed to read suspend success count\n"); + } + + fclose(fp); + return val; +} + void suspend(void) { - int power_state_fd; int timerfd; int err; + int count_before; + int count_after; struct itimerspec spec = {}; if (getuid() != 0) ksft_exit_skip("Please run the test as root - Exiting.\n"); - power_state_fd = open("/sys/power/state", O_RDWR); - if (power_state_fd < 0) - ksft_exit_fail_msg( - "open(\"/sys/power/state\") failed %s)\n", - strerror(errno)); - timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0); if (timerfd < 0) ksft_exit_fail_msg("timerfd_create() failed\n"); @@ -152,14 +172,15 @@ void suspend(void) if (err < 0) ksft_exit_fail_msg("timerfd_settime() failed\n"); + count_before = get_suspend_success_count_or_fail(); + system("(echo mem > /sys/power/state) 2> /dev/null"); - timerfd_gettime(timerfd, &spec); - if (spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0) + count_after = get_suspend_success_count_or_fail(); + if (count_after <= count_before) ksft_exit_fail_msg("Failed to enter Suspend state\n"); close(timerfd); - close(power_state_fd); } int main(int argc, char **argv) diff --git a/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh b/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh index d5dc7e0dc726..6232a46ca6e1 100755 --- a/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh +++ b/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh @@ -67,7 +67,7 @@ hotpluggable_cpus() done } -hotplaggable_offline_cpus() +hotpluggable_offline_cpus() { hotpluggable_cpus 0 } @@ -151,7 +151,7 @@ offline_cpu_expect_fail() online_all_hot_pluggable_cpus() { - for cpu in `hotplaggable_offline_cpus`; do + for cpu in `hotpluggable_offline_cpus`; do online_cpu_expect_success $cpu done } diff --git a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc index b7c8f29c09a9..65916bb55dfb 100644 --- a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc @@ -14,11 +14,35 @@ fail() { #msg exit_fail } +# As reading trace can last forever, simply look for 3 different +# events then exit out of reading the file. If there's not 3 different +# events, then the test has failed. +check_unique() { + cat trace | grep -v '^#' | awk ' + BEGIN { cnt = 0; } + { + for (i = 0; i < cnt; i++) { + if (event[i] == $5) { + break; + } + } + if (i == cnt) { + event[cnt++] = $5; + if (cnt > 2) { + exit; + } + } + } + END { + printf "%d", cnt; + }' +} + echo 'sched:*' > set_event yield -count=`head -n 100 trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` +count=`check_unique` if [ $count -lt 3 ]; then fail "at least fork, exec and exit events should be recorded" fi @@ -29,7 +53,7 @@ echo 1 > events/sched/enable yield -count=`head -n 100 trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` +count=`check_unique` if [ $count -lt 3 ]; then fail "at least fork, exec and exit events should be recorded" fi diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc index 4b994b6df5ac..ed81eaf2afd6 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc @@ -29,7 +29,7 @@ ftrace_filter_check 'schedule*' '^schedule.*$' ftrace_filter_check '*pin*lock' '.*pin.*lock$' # filter by start*mid* -ftrace_filter_check 'mutex*try*' '^mutex.*try.*' +ftrace_filter_check 'mutex*unl*' '^mutex.*unl.*' # Advanced full-glob matching feature is recently supported. # Skip the tests if we are sure the kernel does not support it. diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c index e9dbb84c100a..5e36aeeb9901 100644 --- a/tools/testing/selftests/ipc/msgque.c +++ b/tools/testing/selftests/ipc/msgque.c @@ -39,26 +39,26 @@ int restore_queue(struct msgque_data *msgque) fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); if (fd == -1) { - printf("Failed to open /proc/sys/kernel/msg_next_id\n"); + ksft_test_result_fail("Failed to open /proc/sys/kernel/msg_next_id\n"); return -errno; } sprintf(buf, "%d", msgque->msq_id); ret = write(fd, buf, strlen(buf)); if (ret != strlen(buf)) { - printf("Failed to write to /proc/sys/kernel/msg_next_id\n"); + ksft_test_result_fail("Failed to write to /proc/sys/kernel/msg_next_id\n"); return -errno; } id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); if (id == -1) { - printf("Failed to create queue\n"); + ksft_test_result_fail("Failed to create queue\n"); return -errno; } if (id != msgque->msq_id) { - printf("Restored queue has wrong id (%d instead of %d)\n", - id, msgque->msq_id); + ksft_test_result_fail("Restored queue has wrong id (%d instead of %d)\n" + , id, msgque->msq_id); ret = -EFAULT; goto destroy; } @@ -66,7 +66,7 @@ int restore_queue(struct msgque_data *msgque) for (i = 0; i < msgque->qnum; i++) { if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, msgque->messages[i].msize, IPC_NOWAIT) != 0) { - printf("msgsnd failed (%m)\n"); + ksft_test_result_fail("msgsnd failed (%m)\n"); ret = -errno; goto destroy; } @@ -90,23 +90,22 @@ int check_and_destroy_queue(struct msgque_data *msgque) if (ret < 0) { if (errno == ENOMSG) break; - printf("Failed to read IPC message: %m\n"); + ksft_test_result_fail("Failed to read IPC message: %m\n"); ret = -errno; goto err; } if (ret != msgque->messages[cnt].msize) { - printf("Wrong message size: %d (expected %d)\n", ret, - msgque->messages[cnt].msize); + ksft_test_result_fail("Wrong message size: %d (expected %d)\n", ret, msgque->messages[cnt].msize); ret = -EINVAL; goto err; } if (message.mtype != msgque->messages[cnt].mtype) { - printf("Wrong message type\n"); + ksft_test_result_fail("Wrong message type\n"); ret = -EINVAL; goto err; } if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { - printf("Wrong message content\n"); + ksft_test_result_fail("Wrong message content\n"); ret = -EINVAL; goto err; } @@ -114,7 +113,7 @@ int check_and_destroy_queue(struct msgque_data *msgque) } if (cnt != msgque->qnum) { - printf("Wrong message number\n"); + ksft_test_result_fail("Wrong message number\n"); ret = -EINVAL; goto err; } @@ -139,7 +138,7 @@ int dump_queue(struct msgque_data *msgque) if (ret < 0) { if (errno == EINVAL) continue; - printf("Failed to get stats for IPC queue with id %d\n", + ksft_test_result_fail("Failed to get stats for IPC queue with id %d\n", kern_id); return -errno; } @@ -150,7 +149,7 @@ int dump_queue(struct msgque_data *msgque) msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); if (msgque->messages == NULL) { - printf("Failed to get stats for IPC queue\n"); + ksft_test_result_fail("Failed to get stats for IPC queue\n"); return -ENOMEM; } @@ -162,7 +161,7 @@ int dump_queue(struct msgque_data *msgque) ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); if (ret < 0) { - printf("Failed to copy IPC message: %m (%d)\n", errno); + ksft_test_result_fail("Failed to copy IPC message: %m (%d)\n", errno); return -errno; } msgque->messages[i].msize = ret; @@ -178,7 +177,7 @@ int fill_msgque(struct msgque_data *msgque) memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), IPC_NOWAIT) != 0) { - printf("First message send failed (%m)\n"); + ksft_test_result_fail("First message send failed (%m)\n"); return -errno; } @@ -186,7 +185,7 @@ int fill_msgque(struct msgque_data *msgque) memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), IPC_NOWAIT) != 0) { - printf("Second message send failed (%m)\n"); + ksft_test_result_fail("Second message send failed (%m)\n"); return -errno; } return 0; @@ -202,44 +201,44 @@ int main(int argc, char **argv) msgque.key = ftok(argv[0], 822155650); if (msgque.key == -1) { - printf("Can't make key: %d\n", -errno); + ksft_test_result_fail("Can't make key: %d\n", -errno); ksft_exit_fail(); } msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); if (msgque.msq_id == -1) { err = -errno; - printf("Can't create queue: %d\n", err); + ksft_test_result_fail("Can't create queue: %d\n", err); goto err_out; } err = fill_msgque(&msgque); if (err) { - printf("Failed to fill queue: %d\n", err); + ksft_test_result_fail("Failed to fill queue: %d\n", err); goto err_destroy; } err = dump_queue(&msgque); if (err) { - printf("Failed to dump queue: %d\n", err); + ksft_test_result_fail("Failed to dump queue: %d\n", err); goto err_destroy; } err = check_and_destroy_queue(&msgque); if (err) { - printf("Failed to check and destroy queue: %d\n", err); + ksft_test_result_fail("Failed to check and destroy queue: %d\n", err); goto err_out; } err = restore_queue(&msgque); if (err) { - printf("Failed to restore queue: %d\n", err); + ksft_test_result_fail("Failed to restore queue: %d\n", err); goto err_destroy; } err = check_and_destroy_queue(&msgque); if (err) { - printf("Failed to test queue: %d\n", err); + ksft_test_result_fail("Failed to test queue: %d\n", err); goto err_out; } ksft_exit_pass(); diff --git a/tools/testing/selftests/kexec/Makefile b/tools/testing/selftests/kexec/Makefile index e3000ccb9a5d..874cfdd3b75b 100644 --- a/tools/testing/selftests/kexec/Makefile +++ b/tools/testing/selftests/kexec/Makefile @@ -12,7 +12,7 @@ include ../../../scripts/Makefile.arch ifeq ($(IS_64_BIT)$(ARCH_PROCESSED),1x86) TEST_PROGS += test_kexec_jump.sh -test_kexec_jump.sh: $(OUTPUT)/test_kexec_jump +TEST_GEN_PROGS := test_kexec_jump endif include ../lib.mk diff --git a/tools/testing/selftests/pidfd/pidfd.h b/tools/testing/selftests/pidfd/pidfd.h index cd244d0860ff..f87993def738 100644 --- a/tools/testing/selftests/pidfd/pidfd.h +++ b/tools/testing/selftests/pidfd/pidfd.h @@ -16,6 +16,15 @@ #include #include +/* + * Remove the userspace definitions of the following preprocessor symbols + * to avoid duplicate-definition warnings from the subsequent in-kernel + * definitions. + */ +#undef SCHED_NORMAL +#undef SCHED_FLAG_KEEP_ALL +#undef SCHED_FLAG_UTIL_CLAMP + #include "../kselftest.h" #include "../clone3/clone3_selftests.h" diff --git a/tools/testing/selftests/ptrace/peeksiginfo.c b/tools/testing/selftests/ptrace/peeksiginfo.c index a6884f66dc01..2f345d11e4b8 100644 --- a/tools/testing/selftests/ptrace/peeksiginfo.c +++ b/tools/testing/selftests/ptrace/peeksiginfo.c @@ -199,7 +199,7 @@ int main(int argc, char *argv[]) /* * Dump signal from the process-wide queue. - * The number of signals is not multible to the buffer size + * The number of signals is not multiple to the buffer size */ if (check_direct_path(child, 1, 3)) goto out;