* [PATCH v5 0/2] Add support for Eswin EIC7700 SD/eMMC controller
@ 2025-10-19 11:51 hehuan1
2025-10-19 11:52 ` [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1
2025-10-19 11:53 ` [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1
0 siblings, 2 replies; 11+ messages in thread
From: hehuan1 @ 2025-10-19 11:51 UTC (permalink / raw)
To: ulf.hansson, robh, krzk+dt, conor+dt, jszhang, adrian.hunter,
p.zabel, linux-mmc, devicetree, linux-kernel
Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang,
zhangsenchuan, weishangjuan, lizhi2, caohang, hehuan1
From: Huan He <hehuan1@eswincomputing.com>
Updates:
Changes in v5:
- Update snps,dwcmshc-sdhci.yaml
- Update description for eswin,hsp-sp-csr
- Fix eswin,hsp-sp-csr property structure to use nested items format
- Remove unnecessary '|' symbol from description field
- Wrap description lines to 80-chars
- Update sdhci-of-dwcmshc.c
- Remove inappropriate Reported-by and Closes tags, as the fixes are part
of this patch
- Fix error code return in eic7700_init() when syscon_node_to_regmap()
fails (return PTR_ERR(hsp_regmap))
- Remove unnecessary clock disable/enable operations when changing clock
rates
- Remove unnecessary parentheses around ~PHY_CNFG_RSTN_DEASSERT in
sdhci_eic7700_config_phy()
- Update misleading comments: change "SDIO specific" to "SD specific" in
tuning logic
- Fix multi-line comment format
- Link to v4: https://lore.kernel.org/all/20251011111039.533-1-hehuan1@eswincomputing.com/
Changes in v4:
- Update sdhci-of-dwcmshc.c
- Address the compile error from kernel test robot
- Remove duplicate implementation of dwcmshc_enable_card_clk()
- Add missing dwcmshc_disable_card_clk() function implementation
- Link to v3: https://lore.kernel.org/all/20251010093807.1579-1-hehuan1@eswincomputing.com/
Changes in v3:
- Update snps,dwcmshc-sdhci.yaml
- Delete clock-output-names, '#clock-cells' and eswin,syscrg-csr
- Update description for eswin,hsp-sp-csr
- Update drive-impedance-ohm
- Update the item of reset-names
- Update sdhci-of-dwcmshc.c
- Add descriptions for PHY registers
- Simplify clock management(remove custom clock provider, use
standard clk API)
- Replace magic numbers with GENMASK() or FIELD_PREP() macros
- Add comments explaining HSP stability assertion writes
- Adjust line wrapping to fit within 100-column
- Delete forward declarations by moving function definitions
- Rename variable is_sdio to is_sd
- Replace unclear macros with meaningful alternatives
- Link to v2: https://lore.kernel.org/all/20250912093451.125-1-hehuan1@eswincomputing.com/
Changes in v2:
- Delete the previous separate driver and yaml binding file
- Update snps,dwcmshc-sdhci.yaml to add support for Eswin EIC7700
- Add the new compautible string: "eswin,eic7700-dwcmshc"
- Add new properties: clock-output-names, '#clock-cells',
drive-impedance-ohm, eswin,hsp-sp-csr and eswin,syscrg-csr
- Add customized reset-names for EIC7700 platform
- Update sdhci-of-dwcmshc.c to add support for Eswin EIC7700
- Add a new struct eic7700_priv to hold Eswin-specific data,
including clock phases, register mappings, and drive
impedance configuration
- Implement EIC7700-specific sdhci_ops
- set_clock: support core clock configuration with phase delay
- reset: add PHY reset and configuration
- set_uhs_signaling: support HS400 DLL lock
- platform_execute_tuning: implement delay line tuning and phase
code adjustment
- Add initialization routine (eic7700_init)
- Integrate the new platform data and ops into the driver's match table
- Link to v1: https://lore.kernel.org/all/20250516091259.774-1-dongxuyang@eswincomputing.com/
Huan He (2):
dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700
mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700
.../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +-
drivers/mmc/host/sdhci-of-dwcmshc.c | 502 +++++++++++++++++-
2 files changed, 542 insertions(+), 17 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 2025-10-19 11:51 [PATCH v5 0/2] Add support for Eswin EIC7700 SD/eMMC controller hehuan1 @ 2025-10-19 11:52 ` hehuan1 2025-10-20 11:35 ` Rob Herring (Arm) 2025-10-24 13:57 ` Ulf Hansson 2025-10-19 11:53 ` [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1 1 sibling, 2 replies; 11+ messages in thread From: hehuan1 @ 2025-10-19 11:52 UTC (permalink / raw) To: ulf.hansson, robh, krzk+dt, conor+dt, jszhang, adrian.hunter, p.zabel, linux-mmc, devicetree, linux-kernel Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang, hehuan1, Conor Dooley From: Huan He <hehuan1@eswincomputing.com> EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml. Signed-off-by: Huan He <hehuan1@eswincomputing.com> Reviewed-by: Conor Dooley <conor.dooley@microchip.com> --- .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index f882219a0a26..7e7c55dc2440 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -30,6 +30,7 @@ properties: - sophgo,sg2002-dwcmshc - sophgo,sg2042-dwcmshc - thead,th1520-dwcmshc + - eswin,eic7700-dwcmshc reg: maxItems: 1 @@ -52,17 +53,30 @@ properties: maxItems: 5 reset-names: - items: - - const: core - - const: bus - - const: axi - - const: block - - const: timer + maxItems: 5 rockchip,txclk-tapnum: description: Specify the number of delay for tx sampling. $ref: /schemas/types.yaml#/definitions/uint8 + eswin,hsp-sp-csr: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: Phandle to HSP(High-Speed Peripheral) device + - description: Offset of the stability status register for internal + clock. + - description: Offset of the stability register for host regulator + voltage. + description: + HSP CSR is to control and get status of different high-speed peripherals + (such as Ethernet, USB, SATA, etc.) via register, which can tune + board-level's parameters of PHY, etc. + + eswin,drive-impedance-ohms: + description: Specifies the drive impedance in Ohm. + enum: [33, 40, 50, 66, 100] + required: - compatible - reg @@ -110,6 +124,37 @@ allOf: - const: block - const: timer + - if: + properties: + compatible: + contains: + const: eswin,eic7700-dwcmshc + then: + properties: + resets: + minItems: 4 + maxItems: 4 + reset-names: + items: + - const: axi + - const: phy + - const: prstn + - const: txrx + required: + - eswin,hsp-sp-csr + - eswin,drive-impedance-ohms + else: + properties: + resets: + maxItems: 5 + reset-names: + items: + - const: core + - const: bus + - const: axi + - const: block + - const: timer + - if: properties: compatible: -- 2.25.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 2025-10-19 11:52 ` [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1 @ 2025-10-20 11:35 ` Rob Herring (Arm) 2025-10-24 13:57 ` Ulf Hansson 1 sibling, 0 replies; 11+ messages in thread From: Rob Herring (Arm) @ 2025-10-20 11:35 UTC (permalink / raw) To: hehuan1 Cc: linmin, ulf.hansson, lizhi2, devicetree, pinkesh.vaghela, weishangjuan, jszhang, linux-mmc, zhangsenchuan, conor+dt, p.zabel, caohang, dongxuyang, luyulin, ningyu, Conor Dooley, adrian.hunter, linux-kernel, xuxiang, krzk+dt On Sun, 19 Oct 2025 19:52:38 +0800, hehuan1@eswincomputing.com wrote: > From: Huan He <hehuan1@eswincomputing.com> > > EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. > Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml. > > Signed-off-by: Huan He <hehuan1@eswincomputing.com> > Reviewed-by: Conor Dooley <conor.dooley@microchip.com> > --- > .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- > 1 file changed, 51 insertions(+), 6 deletions(-) > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/net/eswin,eic7700-eth.example.dtb: ethernet@50400000 (eswin,eic7700-qos-eth): eswin,hsp-sp-csr: [[4294967295, 256], [264, 280]] is too short from schema $id: http://devicetree.org/schemas/net/eswin,eic7700-eth.yaml# /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/net/eswin,eic7700-eth.example.dtb: ethernet@50400000 (eswin,eic7700-qos-eth): Unevaluated properties are not allowed ('eswin,hsp-sp-csr' was unexpected) from schema $id: http://devicetree.org/schemas/net/eswin,eic7700-eth.yaml# doc reference errors (make refcheckdocs): See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20251019115238.320-1-hehuan1@eswincomputing.com The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 2025-10-19 11:52 ` [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1 2025-10-20 11:35 ` Rob Herring (Arm) @ 2025-10-24 13:57 ` Ulf Hansson 2025-10-24 16:37 ` Conor Dooley 1 sibling, 1 reply; 11+ messages in thread From: Ulf Hansson @ 2025-10-24 13:57 UTC (permalink / raw) To: hehuan1, robh, krzk+dt Cc: conor+dt, jszhang, adrian.hunter, p.zabel, linux-mmc, devicetree, linux-kernel, ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang, Conor Dooley On Sun, 19 Oct 2025 at 13:52, <hehuan1@eswincomputing.com> wrote: > > From: Huan He <hehuan1@eswincomputing.com> > > EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. > Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml. > > Signed-off-by: Huan He <hehuan1@eswincomputing.com> > Reviewed-by: Conor Dooley <conor.dooley@microchip.com> > --- > .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- > 1 file changed, 51 insertions(+), 6 deletions(-) > > diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > index f882219a0a26..7e7c55dc2440 100644 > --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > @@ -30,6 +30,7 @@ properties: > - sophgo,sg2002-dwcmshc > - sophgo,sg2042-dwcmshc > - thead,th1520-dwcmshc > + - eswin,eic7700-dwcmshc > > reg: > maxItems: 1 > @@ -52,17 +53,30 @@ properties: > maxItems: 5 > > reset-names: > - items: > - - const: core > - - const: bus > - - const: axi > - - const: block > - - const: timer > + maxItems: 5 > > rockchip,txclk-tapnum: > description: Specify the number of delay for tx sampling. > $ref: /schemas/types.yaml#/definitions/uint8 > > + eswin,hsp-sp-csr: > + $ref: /schemas/types.yaml#/definitions/phandle-array > + items: > + - items: > + - description: Phandle to HSP(High-Speed Peripheral) device > + - description: Offset of the stability status register for internal > + clock. > + - description: Offset of the stability register for host regulator > + voltage. > + description: > + HSP CSR is to control and get status of different high-speed peripherals > + (such as Ethernet, USB, SATA, etc.) via register, which can tune > + board-level's parameters of PHY, etc. I would like second confirmation from DT maintainers, to make sure it's reasonable to model the HW like this. In principle the phandle above gets translated into a regmap via a call to syscon_node_to_regmap() in the driver, to allow some registers to be written that are outside the controllers address space. > + > + eswin,drive-impedance-ohms: > + description: Specifies the drive impedance in Ohm. > + enum: [33, 40, 50, 66, 100] > + > required: > - compatible > - reg > @@ -110,6 +124,37 @@ allOf: > - const: block > - const: timer > > + - if: > + properties: > + compatible: > + contains: > + const: eswin,eic7700-dwcmshc > + then: > + properties: > + resets: > + minItems: 4 > + maxItems: 4 > + reset-names: > + items: > + - const: axi > + - const: phy > + - const: prstn > + - const: txrx > + required: > + - eswin,hsp-sp-csr > + - eswin,drive-impedance-ohms > + else: > + properties: > + resets: > + maxItems: 5 > + reset-names: > + items: > + - const: core > + - const: bus > + - const: axi > + - const: block > + - const: timer > + > - if: > properties: > compatible: > -- > 2.25.1 > Kind regards Uffe ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 2025-10-24 13:57 ` Ulf Hansson @ 2025-10-24 16:37 ` Conor Dooley 2025-10-24 21:47 ` Conor Dooley 0 siblings, 1 reply; 11+ messages in thread From: Conor Dooley @ 2025-10-24 16:37 UTC (permalink / raw) To: Ulf Hansson Cc: hehuan1, robh, krzk+dt, conor+dt, jszhang, adrian.hunter, p.zabel, linux-mmc, devicetree, linux-kernel, ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang, Conor Dooley [-- Attachment #1: Type: text/plain, Size: 4242 bytes --] On Fri, Oct 24, 2025 at 03:57:33PM +0200, Ulf Hansson wrote: > On Sun, 19 Oct 2025 at 13:52, <hehuan1@eswincomputing.com> wrote: > > > > From: Huan He <hehuan1@eswincomputing.com> > > > > EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. > > Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml. > > > > Signed-off-by: Huan He <hehuan1@eswincomputing.com> > > Reviewed-by: Conor Dooley <conor.dooley@microchip.com> > > --- > > .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- > > 1 file changed, 51 insertions(+), 6 deletions(-) > > > > diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > index f882219a0a26..7e7c55dc2440 100644 > > --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > @@ -30,6 +30,7 @@ properties: > > - sophgo,sg2002-dwcmshc > > - sophgo,sg2042-dwcmshc > > - thead,th1520-dwcmshc > > + - eswin,eic7700-dwcmshc > > > > reg: > > maxItems: 1 > > @@ -52,17 +53,30 @@ properties: > > maxItems: 5 > > > > reset-names: > > - items: > > - - const: core > > - - const: bus > > - - const: axi > > - - const: block > > - - const: timer > > + maxItems: 5 > > > > rockchip,txclk-tapnum: > > description: Specify the number of delay for tx sampling. > > $ref: /schemas/types.yaml#/definitions/uint8 > > > > + eswin,hsp-sp-csr: > > + $ref: /schemas/types.yaml#/definitions/phandle-array > > + items: > > + - items: > > + - description: Phandle to HSP(High-Speed Peripheral) device > > + - description: Offset of the stability status register for internal > > + clock. > > + - description: Offset of the stability register for host regulator > > + voltage. > > + description: > > + HSP CSR is to control and get status of different high-speed peripherals > > + (such as Ethernet, USB, SATA, etc.) via register, which can tune > > + board-level's parameters of PHY, etc. > > I would like second confirmation from DT maintainers, to make sure > it's reasonable to model the HW like this. If by second confirmation, you mean by someone other than me, obviously ignore this, but I think this is "fine". It discussed on a previous revision that all is being done with it is setting a handful bits that signify that the peripheral has been configured correctly. That said, I don't have a clue what's going on with the warning about the dwmac device. That's definitely one for Rob. > > In principle the phandle above gets translated into a regmap via a > call to syscon_node_to_regmap() in the driver, to allow some registers > to be written that are outside the controllers address space. > > > + > > + eswin,drive-impedance-ohms: > > + description: Specifies the drive impedance in Ohm. > > + enum: [33, 40, 50, 66, 100] > > + > > required: > > - compatible > > - reg > > @@ -110,6 +124,37 @@ allOf: > > - const: block > > - const: timer > > > > + - if: > > + properties: > > + compatible: > > + contains: > > + const: eswin,eic7700-dwcmshc > > + then: > > + properties: > > + resets: > > + minItems: 4 > > + maxItems: 4 > > + reset-names: > > + items: > > + - const: axi > > + - const: phy > > + - const: prstn > > + - const: txrx > > + required: > > + - eswin,hsp-sp-csr > > + - eswin,drive-impedance-ohms > > + else: > > + properties: > > + resets: > > + maxItems: 5 > > + reset-names: > > + items: > > + - const: core > > + - const: bus > > + - const: axi > > + - const: block > > + - const: timer > > + > > - if: > > properties: > > compatible: > > -- > > 2.25.1 > > > > Kind regards > Uffe [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 2025-10-24 16:37 ` Conor Dooley @ 2025-10-24 21:47 ` Conor Dooley 2025-10-24 22:22 ` Rob Herring 0 siblings, 1 reply; 11+ messages in thread From: Conor Dooley @ 2025-10-24 21:47 UTC (permalink / raw) To: Ulf Hansson Cc: hehuan1, robh, krzk+dt, conor+dt, jszhang, adrian.hunter, p.zabel, linux-mmc, devicetree, linux-kernel, ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang, Conor Dooley [-- Attachment #1: Type: text/plain, Size: 4813 bytes --] On Fri, Oct 24, 2025 at 05:37:59PM +0100, Conor Dooley wrote: > On Fri, Oct 24, 2025 at 03:57:33PM +0200, Ulf Hansson wrote: > > On Sun, 19 Oct 2025 at 13:52, <hehuan1@eswincomputing.com> wrote: > > > > > > From: Huan He <hehuan1@eswincomputing.com> > > > > > > EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. > > > Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml. > > > > > > Signed-off-by: Huan He <hehuan1@eswincomputing.com> > > > Reviewed-by: Conor Dooley <conor.dooley@microchip.com> > > > --- > > > .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- > > > 1 file changed, 51 insertions(+), 6 deletions(-) > > > > > > diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > > index f882219a0a26..7e7c55dc2440 100644 > > > --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > > +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > > @@ -30,6 +30,7 @@ properties: > > > - sophgo,sg2002-dwcmshc > > > - sophgo,sg2042-dwcmshc > > > - thead,th1520-dwcmshc > > > + - eswin,eic7700-dwcmshc > > > > > > reg: > > > maxItems: 1 > > > @@ -52,17 +53,30 @@ properties: > > > maxItems: 5 > > > > > > reset-names: > > > - items: > > > - - const: core > > > - - const: bus > > > - - const: axi > > > - - const: block > > > - - const: timer > > > + maxItems: 5 > > > > > > rockchip,txclk-tapnum: > > > description: Specify the number of delay for tx sampling. > > > $ref: /schemas/types.yaml#/definitions/uint8 > > > > > > + eswin,hsp-sp-csr: > > > + $ref: /schemas/types.yaml#/definitions/phandle-array > > > + items: > > > + - items: > > > + - description: Phandle to HSP(High-Speed Peripheral) device > > > + - description: Offset of the stability status register for internal > > > + clock. > > > + - description: Offset of the stability register for host regulator > > > + voltage. > > > + description: > > > + HSP CSR is to control and get status of different high-speed peripherals > > > + (such as Ethernet, USB, SATA, etc.) via register, which can tune > > > + board-level's parameters of PHY, etc. > > > > I would like second confirmation from DT maintainers, to make sure > > it's reasonable to model the HW like this. > > If by second confirmation, you mean by someone other than me, obviously > ignore this, but I think this is "fine". It discussed on a previous > revision that all is being done with it is setting a handful bits that > signify that the peripheral has been configured correctly. > > That said, I don't have a clue what's going on with the warning about > the dwmac device. That's definitely one for Rob. Apparently it's just as simple as there being more than one definition of the same property. I had it in my head that that was okay when only one binding was applied to the node, but clearly not. I'll have to un-review it until that error is sorted out. > > In principle the phandle above gets translated into a regmap via a > > call to syscon_node_to_regmap() in the driver, to allow some registers > > to be written that are outside the controllers address space. > > > > > + > > > + eswin,drive-impedance-ohms: > > > + description: Specifies the drive impedance in Ohm. > > > + enum: [33, 40, 50, 66, 100] > > > + > > > required: > > > - compatible > > > - reg > > > @@ -110,6 +124,37 @@ allOf: > > > - const: block > > > - const: timer > > > > > > + - if: > > > + properties: > > > + compatible: > > > + contains: > > > + const: eswin,eic7700-dwcmshc > > > + then: > > > + properties: > > > + resets: > > > + minItems: 4 > > > + maxItems: 4 > > > + reset-names: > > > + items: > > > + - const: axi > > > + - const: phy > > > + - const: prstn > > > + - const: txrx > > > + required: > > > + - eswin,hsp-sp-csr > > > + - eswin,drive-impedance-ohms > > > + else: > > > + properties: > > > + resets: > > > + maxItems: 5 > > > + reset-names: > > > + items: > > > + - const: core > > > + - const: bus > > > + - const: axi > > > + - const: block > > > + - const: timer > > > + > > > - if: > > > properties: > > > compatible: > > > -- > > > 2.25.1 > > > > > > > Kind regards > > Uffe [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 2025-10-24 21:47 ` Conor Dooley @ 2025-10-24 22:22 ` Rob Herring 0 siblings, 0 replies; 11+ messages in thread From: Rob Herring @ 2025-10-24 22:22 UTC (permalink / raw) To: Conor Dooley Cc: Ulf Hansson, hehuan1, krzk+dt, conor+dt, jszhang, adrian.hunter, p.zabel, linux-mmc, devicetree, linux-kernel, ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang, Conor Dooley On Fri, Oct 24, 2025 at 10:47:34PM +0100, Conor Dooley wrote: > On Fri, Oct 24, 2025 at 05:37:59PM +0100, Conor Dooley wrote: > > On Fri, Oct 24, 2025 at 03:57:33PM +0200, Ulf Hansson wrote: > > > On Sun, 19 Oct 2025 at 13:52, <hehuan1@eswincomputing.com> wrote: > > > > > > > > From: Huan He <hehuan1@eswincomputing.com> > > > > > > > > EIC7700 use Synopsys dwcmshc IP for SD/eMMC controllers. > > > > Add Eswin EIC7700 support in sdhci-of-dwcmshc.yaml. > > > > > > > > Signed-off-by: Huan He <hehuan1@eswincomputing.com> > > > > Reviewed-by: Conor Dooley <conor.dooley@microchip.com> > > > > --- > > > > .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 57 +++++++++++++++++-- > > > > 1 file changed, 51 insertions(+), 6 deletions(-) > > > > > > > > diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > > > index f882219a0a26..7e7c55dc2440 100644 > > > > --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > > > +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml > > > > @@ -30,6 +30,7 @@ properties: > > > > - sophgo,sg2002-dwcmshc > > > > - sophgo,sg2042-dwcmshc > > > > - thead,th1520-dwcmshc > > > > + - eswin,eic7700-dwcmshc > > > > > > > > reg: > > > > maxItems: 1 > > > > @@ -52,17 +53,30 @@ properties: > > > > maxItems: 5 > > > > > > > > reset-names: > > > > - items: > > > > - - const: core > > > > - - const: bus > > > > - - const: axi > > > > - - const: block > > > > - - const: timer > > > > + maxItems: 5 > > > > > > > > rockchip,txclk-tapnum: > > > > description: Specify the number of delay for tx sampling. > > > > $ref: /schemas/types.yaml#/definitions/uint8 > > > > > > > > + eswin,hsp-sp-csr: > > > > + $ref: /schemas/types.yaml#/definitions/phandle-array > > > > + items: > > > > + - items: > > > > + - description: Phandle to HSP(High-Speed Peripheral) device > > > > + - description: Offset of the stability status register for internal > > > > + clock. > > > > + - description: Offset of the stability register for host regulator > > > > + voltage. > > > > + description: > > > > + HSP CSR is to control and get status of different high-speed peripherals > > > > + (such as Ethernet, USB, SATA, etc.) via register, which can tune > > > > + board-level's parameters of PHY, etc. > > > > > > I would like second confirmation from DT maintainers, to make sure > > > it's reasonable to model the HW like this. > > > > If by second confirmation, you mean by someone other than me, obviously > > ignore this, but I think this is "fine". It discussed on a previous > > revision that all is being done with it is setting a handful bits that > > signify that the peripheral has been configured correctly. > > > > That said, I don't have a clue what's going on with the warning about > > the dwmac device. That's definitely one for Rob. > > Apparently it's just as simple as there being more than one definition > of the same property. I had it in my head that that was okay when only > one binding was applied to the node, but clearly not. > > I'll have to un-review it until that error is sorted out. This binding is fine. The error is in the eswin,eic770-eth.yaml binding: eswin,hsp-sp-csr: $ref: /schemas/types.yaml#/definitions/phandle-array items: - description: Phandle to HSP(High-Speed Peripheral) device - description: Offset of phy control register for internal or external clock selection - description: Offset of AXI clock controller Low-Power request register - description: Offset of register controlling TX/RX clock delay description: | High-Speed Peripheral device needed to configure clock selection, clock low-power mode and clock delay. The issue here is phandle-array is really a matrix and an outer 'items' is needed to say there is 1 entry with 4 cells. Like this: items: - items: - description: ... - description: ... - description: ... - description: ... Please send a fix for that. The tools could handle this case better, so I'll look into a fix for them. Rob ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700 2025-10-19 11:51 [PATCH v5 0/2] Add support for Eswin EIC7700 SD/eMMC controller hehuan1 2025-10-19 11:52 ` [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1 @ 2025-10-19 11:53 ` hehuan1 2025-10-23 11:08 ` Adrian Hunter 1 sibling, 1 reply; 11+ messages in thread From: hehuan1 @ 2025-10-19 11:53 UTC (permalink / raw) To: ulf.hansson, robh, krzk+dt, conor+dt, jszhang, adrian.hunter, p.zabel, linux-mmc, devicetree, linux-kernel Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang, hehuan1 From: Huan He <hehuan1@eswincomputing.com> Add support for the mmc controller in the Eswin EIC7700 with the new compatible "eswin,eic7700-dwcmshc". Implement custom sdhci_ops for set_clock, reset, set_uhs_signaling, platform_execute_tuning. Signed-off-by: Huan He <hehuan1@eswincomputing.com> --- drivers/mmc/host/sdhci-of-dwcmshc.c | 502 +++++++++++++++++++++++++++- 1 file changed, 491 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index eebd45389956..c8726e6e0905 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -11,6 +11,7 @@ #include <linux/arm-smccc.h> #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/dma-mapping.h> #include <linux/iopoll.h> #include <linux/kernel.h> @@ -19,8 +20,11 @@ #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/reset.h> #include <linux/sizes.h> +#include <linux/mfd/syscon.h> +#include <linux/units.h> #include "sdhci-pltfm.h" #include "cqhci.h" @@ -39,6 +43,7 @@ #define DWCMSHC_CARD_IS_EMMC BIT(0) #define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_EMMC_ATCTRL 0x40 +#define DWCMSHC_AT_STAT 0x44 /* Tuning and auto-tuning fields in AT_CTRL_R control register */ #define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */ #define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */ @@ -194,6 +199,19 @@ #define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */ #define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */ +/* PHY DLL offset setting register */ +#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29) +/* DLL LBT setting register */ +#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c) +/* DLL Status register */ +#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e) +#define DLL_LOCK_STS BIT(0)/* DLL is locked and ready */ +/* + * Captures the value of DLL's lock error status information. Value is valid + * only when LOCK_STS is set. + */ +#define DLL_ERROR_STS BIT(1) + #define FLAG_IO_FIXED_1V8 BIT(0) #define BOUNDARY_OK(addr, len) \ @@ -206,6 +224,31 @@ /* SMC call for BlueField-3 eMMC RST_N */ #define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 +/* Eswin specific Registers */ +#define EIC7700_CARD_CLK_STABLE BIT(28) +#define EIC7700_INT_BCLK_STABLE BIT(16) +#define EIC7700_INT_ACLK_STABLE BIT(8) +#define EIC7700_INT_TMCLK_STABLE BIT(0) +#define EIC7700_INT_CLK_STABLE (EIC7700_CARD_CLK_STABLE | \ + EIC7700_INT_ACLK_STABLE | \ + EIC7700_INT_BCLK_STABLE | \ + EIC7700_INT_TMCLK_STABLE) +#define EIC7700_HOST_VAL_STABLE BIT(0) + +/* strength definition */ +#define PHYCTRL_DR_33OHM 0xee +#define PHYCTRL_DR_40OHM 0xcc +#define PHYCTRL_DR_50OHM 0x88 +#define PHYCTRL_DR_66OHM 0x44 +#define PHYCTRL_DR_100OHM 0x00 + +#define MAX_PHASE_CODE 0xff +#define TUNING_RANGE_THRESHOLD 40 +#define PHY_CLK_MAX_DELAY_MASK 0x7f +#define PHY_DELAY_CODE_MAX 0x7f +#define PHY_DELAY_CODE_EMMC 0x17 +#define PHY_DELAY_CODE_SD 0x55 + enum dwcmshc_rk_type { DWCMSHC_RK3568, DWCMSHC_RK3588, @@ -217,6 +260,11 @@ struct rk35xx_priv { u8 txclk_tapnum; }; +struct eic7700_priv { + struct reset_control *reset; + unsigned int drive_impedance; +}; + #define DWCMSHC_MAX_OTHER_CLKS 3 struct dwcmshc_priv { @@ -238,6 +286,17 @@ struct dwcmshc_pltfm_data { void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); }; +static void dwcmshc_enable_card_clk(struct sdhci_host *host) +{ + u16 ctrl; + + ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { + ctrl |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); + } +} + static int dwcmshc_get_enable_other_clks(struct device *dev, struct dwcmshc_priv *priv, int num_clks, @@ -1095,6 +1154,411 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host, ARRAY_SIZE(clk_ids), clk_ids); } +static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + u16 clk; + + host->mmc->actual_clock = clock; + + if (clock == 0) { + sdhci_set_clock(host, clock); + return; + } + + clk_set_rate(pltfm_host->clk, clock); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + dwcmshc_enable_card_clk(host); +} + +static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay) +{ + delay &= PHY_CLK_MAX_DELAY_MASK; + + /* phy clk delay line config */ + sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R); + sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R); + sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R); +} + +static void sdhci_eic7700_config_phy(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + struct eic7700_priv *priv = dwc_priv->priv; + unsigned int val, drv; + + drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF); + drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF); + + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { + val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + val |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + } + + /* reset phy, config phy's pad */ + sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R); + + /* configure phy pads */ + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); + val |= PHY_PAD_RXSEL_1V8; + sdhci_writew(host, val, PHY_CMDPAD_CNFG_R); + sdhci_writew(host, val, PHY_DATAPAD_CNFG_R); + sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R); + + /* Clock PAD Setting */ + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); + + /* PHY strobe PAD setting (EMMC only) */ + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= PHY_PAD_RXSEL_1V8; + sdhci_writew(host, val, PHY_STBPAD_CNFG_R); + } + usleep_range(2000, 3000); + sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R); + sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line); +} + +static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + /* after reset all, the phy's config will be clear */ + if (mask == SDHCI_RESET_ALL) + sdhci_eic7700_config_phy(host); +} + +static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv) +{ + int ret; + + priv->reset = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_err(dev, "failed to get reset control %d\n", ret); + return ret; + } + + ret = reset_control_assert(priv->reset); + if (ret) { + dev_err(dev, "Failed to assert reset signals: %d\n", ret); + return ret; + } + usleep_range(2000, 2100); + ret = reset_control_deassert(priv->reset); + if (ret) { + dev_err(dev, "Failed to deassert reset signals: %d\n", ret); + return ret; + } + + return ret; +} + +static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, unsigned int dr_ohm) +{ + switch (dr_ohm) { + case 100: + return PHYCTRL_DR_100OHM; + case 66: + return PHYCTRL_DR_66OHM; + case 50: + return PHYCTRL_DR_50OHM; + case 40: + return PHYCTRL_DR_40OHM; + case 33: + return PHYCTRL_DR_33OHM; + } + + dev_warn(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm); + return PHYCTRL_DR_50OHM; +} + +static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + int delay_min = -1; + int delay_max = -1; + int cmd_error = 0; + int delay = 0; + int i = 0; + int ret; + + for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) { + sdhci_eic7700_config_phy_delay(host, i); + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); + if (ret) { + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + usleep_range(200, 210); + if (delay_min != -1 && delay_max != -1) + break; + } else { + if (delay_min == -1) { + delay_min = i; + continue; + } else { + delay_max = i; + continue; + } + } + } + if (delay_min == -1 && delay_max == -1) { + pr_err("%s: delay code tuning failed!\n", mmc_hostname(host->mmc)); + sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line); + return ret; + } + + delay = (delay_min + delay_max) / 2; + sdhci_eic7700_config_phy_delay(host, delay); + + return 0; +} + +static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + int phase_code = -1; + int code_range = -1; + bool is_sd = false; + int code_min = -1; + int code_max = -1; + int cmd_error = 0; + int ret = 0; + int i = 0; + + if ((host->mmc->caps2 & sd_caps) == sd_caps) + is_sd = true; + + for (i = 0; i <= MAX_PHASE_CODE; i++) { + /* Centered Phase code */ + sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + if (ret) { + /* SD specific range tracking */ + if (is_sd && code_min != -1 && code_max != -1) { + if (code_max - code_min > code_range) { + code_range = code_max - code_min; + phase_code = (code_min + code_max) / 2; + if (code_range > TUNING_RANGE_THRESHOLD) + break; + } + code_min = -1; + code_max = -1; + } + /* EMMC breaks after first valid range */ + if (!is_sd && code_min != -1 && code_max != -1) + break; + } else { + /* Track valid phase code range */ + if (code_min == -1) { + code_min = i; + if (!is_sd) + continue; + } + code_max = i; + if (is_sd && i == MAX_PHASE_CODE) { + if (code_max - code_min > code_range) { + code_range = code_max - code_min; + phase_code = (code_min + code_max) / 2; + } + } + } + } + + /* Handle tuning failure case */ + if ((is_sd && phase_code == -1) || + (!is_sd && code_min == -1 && code_max == -1)) { + pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc)); + sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + return -EIO; + } + if (!is_sd) + phase_code = (code_min + code_max) / 2; + + sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + + /* SD specific final verification */ + if (is_sd) { + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + if (ret) { + pr_err("%s: Final phase code 0x%x verification failed!\n", + mmc_hostname(host->mmc), phase_code); + return ret; + } + } + + return 0; +} + +static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + int ret = 0; + u16 ctrl; + u32 val; + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl &= ~SDHCI_CTRL_TUNED_CLK; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + val |= AT_CTRL_SW_TUNE_EN; + sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + + sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + sdhci_writew(host, 0x0, SDHCI_CMD_DATA); + + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { + ret = sdhci_eic7700_delay_tuning(host, opcode); + if (ret) + return ret; + } + + ret = sdhci_eic7700_phase_code_tuning(host, opcode); + if (ret) + return ret; + + return 0; +} + +static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u8 status; + u32 val; + int ret; + + dwcmshc_set_uhs_signaling(host, timing); + + /* here need make dll locked when in hs400 at 200MHz */ + if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) { + val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY)); + /* 2-cycle latency */ + val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2); + sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + + sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) | + 0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */ + /* DLL jump step input */ + sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R); + sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, + PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R); + /* Sets the value of DLL's offset input */ + sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R); + /* + * Sets the value of DLL's olbt loadval input. Controls the Ibt + * timer's timeout value at which DLL runs a revalidation cycle. + */ + sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R); + sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R); + usleep_range(100, 110); + + ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000, + false, host, PHY_DLL_STATUS_R); + if (ret) { + pr_err("%s: DLL lock timeout! status: 0x%x\n", + mmc_hostname(host->mmc), status); + return; + } + + status = sdhci_readb(host, PHY_DLL_STATUS_R); + if (status & DLL_ERROR_STS) { + pr_err("%s: DLL lock failed!err_status:0x%x\n", + mmc_hostname(host->mmc), status); + } + } +} + +static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing) +{ + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + + if ((host->mmc->caps2 & sd_caps) == sd_caps) + sdhci_set_uhs_signaling(host, timing); + else + sdhci_eic7700_set_uhs_signaling(host, timing); +} + +static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +{ + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + unsigned int val, hsp_int_status, hsp_pwr_ctrl; + struct of_phandle_args args; + struct eic7700_priv *priv; + struct regmap *hsp_regmap; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dwc_priv->priv = priv; + + ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv); + if (ret) { + dev_err(dev, "failed to reset\n"); + return ret; + } + + ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args); + if (ret) { + dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret); + return ret; + } + + hsp_regmap = syscon_node_to_regmap(args.np); + if (IS_ERR(hsp_regmap)) { + dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n"); + of_node_put(args.np); + return PTR_ERR(hsp_regmap); + } + hsp_int_status = args.args[0]; + hsp_pwr_ctrl = args.args[1]; + of_node_put(args.np); + /* + * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status. + * This signals to the eMMC controller that platform clocks (card, ACLK, + * BCLK, TMCLK) are enabled and stable. + */ + regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE); + /* + * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl. + * This signals that VDD is stable and permits transition to high-speed + * modes (e.g., UHS-I). + */ + regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE); + + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) + dwc_priv->delay_line = PHY_DELAY_CODE_EMMC; + else + dwc_priv->delay_line = PHY_DELAY_CODE_SD; + + if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val)) + priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val); + return 0; +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -1169,6 +1633,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = { .platform_execute_tuning = th1520_execute_tuning, }; +static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = { + .set_clock = sdhci_eic7700_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_eic7700_reset, + .set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper, + .set_power = sdhci_set_power_and_bus_voltage, + .irq = dwcmshc_cqe_irq_handler, + .platform_execute_tuning = sdhci_eic7700_executing_tuning, +}; + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { .pdata = { .ops = &sdhci_dwcmshc_ops, @@ -1238,6 +1714,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = { .init = sg2042_init, }; +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_eic7700_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .init = eic7700_init, +}; + static const struct cqhci_host_ops dwcmshc_cqhci_ops = { .enable = dwcmshc_sdhci_cqe_enable, .disable = sdhci_cqe_disable, @@ -1338,6 +1825,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { .compatible = "sophgo,sg2042-dwcmshc", .data = &sdhci_dwcmshc_sg2042_pdata, }, + { + .compatible = "eswin,eic7700-dwcmshc", + .data = &sdhci_dwcmshc_eic7700_pdata, + }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); @@ -1570,17 +2061,6 @@ static int dwcmshc_resume(struct device *dev) return ret; } -static void dwcmshc_enable_card_clk(struct sdhci_host *host) -{ - u16 ctrl; - - ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { - ctrl |= SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); - } -} - static int dwcmshc_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); -- 2.25.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700 2025-10-19 11:53 ` [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1 @ 2025-10-23 11:08 ` Adrian Hunter 2025-10-24 3:19 ` 何欢 0 siblings, 1 reply; 11+ messages in thread From: Adrian Hunter @ 2025-10-23 11:08 UTC (permalink / raw) To: hehuan1, ulf.hansson, robh, krzk+dt, conor+dt, jszhang, p.zabel, linux-mmc, devicetree, linux-kernel Cc: ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang On 19/10/2025 14:53, hehuan1@eswincomputing.com wrote: > From: Huan He <hehuan1@eswincomputing.com> > > Add support for the mmc controller in the Eswin EIC7700 with the new > compatible "eswin,eic7700-dwcmshc". Implement custom sdhci_ops for > set_clock, reset, set_uhs_signaling, platform_execute_tuning. > > Signed-off-by: Huan He <hehuan1@eswincomputing.com> Noting that the dt-bindings patch still seems to have issues, for this patch, anyway: Acked-by: Adrian Hunter <adrian.hunter@intel.com> > --- > drivers/mmc/host/sdhci-of-dwcmshc.c | 502 +++++++++++++++++++++++++++- > 1 file changed, 491 insertions(+), 11 deletions(-) > > diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c > index eebd45389956..c8726e6e0905 100644 > --- a/drivers/mmc/host/sdhci-of-dwcmshc.c > +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c > @@ -11,6 +11,7 @@ > #include <linux/arm-smccc.h> > #include <linux/bitfield.h> > #include <linux/clk.h> > +#include <linux/clk-provider.h> > #include <linux/dma-mapping.h> > #include <linux/iopoll.h> > #include <linux/kernel.h> > @@ -19,8 +20,11 @@ > #include <linux/platform_device.h> > #include <linux/pm_domain.h> > #include <linux/pm_runtime.h> > +#include <linux/regmap.h> > #include <linux/reset.h> > #include <linux/sizes.h> > +#include <linux/mfd/syscon.h> > +#include <linux/units.h> > > #include "sdhci-pltfm.h" > #include "cqhci.h" > @@ -39,6 +43,7 @@ > #define DWCMSHC_CARD_IS_EMMC BIT(0) > #define DWCMSHC_ENHANCED_STROBE BIT(8) > #define DWCMSHC_EMMC_ATCTRL 0x40 > +#define DWCMSHC_AT_STAT 0x44 > /* Tuning and auto-tuning fields in AT_CTRL_R control register */ > #define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */ > #define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */ > @@ -194,6 +199,19 @@ > #define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */ > #define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */ > > +/* PHY DLL offset setting register */ > +#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29) > +/* DLL LBT setting register */ > +#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c) > +/* DLL Status register */ > +#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e) > +#define DLL_LOCK_STS BIT(0)/* DLL is locked and ready */ > +/* > + * Captures the value of DLL's lock error status information. Value is valid > + * only when LOCK_STS is set. > + */ > +#define DLL_ERROR_STS BIT(1) > + > #define FLAG_IO_FIXED_1V8 BIT(0) > > #define BOUNDARY_OK(addr, len) \ > @@ -206,6 +224,31 @@ > /* SMC call for BlueField-3 eMMC RST_N */ > #define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 > > +/* Eswin specific Registers */ > +#define EIC7700_CARD_CLK_STABLE BIT(28) > +#define EIC7700_INT_BCLK_STABLE BIT(16) > +#define EIC7700_INT_ACLK_STABLE BIT(8) > +#define EIC7700_INT_TMCLK_STABLE BIT(0) > +#define EIC7700_INT_CLK_STABLE (EIC7700_CARD_CLK_STABLE | \ > + EIC7700_INT_ACLK_STABLE | \ > + EIC7700_INT_BCLK_STABLE | \ > + EIC7700_INT_TMCLK_STABLE) > +#define EIC7700_HOST_VAL_STABLE BIT(0) > + > +/* strength definition */ > +#define PHYCTRL_DR_33OHM 0xee > +#define PHYCTRL_DR_40OHM 0xcc > +#define PHYCTRL_DR_50OHM 0x88 > +#define PHYCTRL_DR_66OHM 0x44 > +#define PHYCTRL_DR_100OHM 0x00 > + > +#define MAX_PHASE_CODE 0xff > +#define TUNING_RANGE_THRESHOLD 40 > +#define PHY_CLK_MAX_DELAY_MASK 0x7f > +#define PHY_DELAY_CODE_MAX 0x7f > +#define PHY_DELAY_CODE_EMMC 0x17 > +#define PHY_DELAY_CODE_SD 0x55 > + > enum dwcmshc_rk_type { > DWCMSHC_RK3568, > DWCMSHC_RK3588, > @@ -217,6 +260,11 @@ struct rk35xx_priv { > u8 txclk_tapnum; > }; > > +struct eic7700_priv { > + struct reset_control *reset; > + unsigned int drive_impedance; > +}; > + > #define DWCMSHC_MAX_OTHER_CLKS 3 > > struct dwcmshc_priv { > @@ -238,6 +286,17 @@ struct dwcmshc_pltfm_data { > void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); > }; > > +static void dwcmshc_enable_card_clk(struct sdhci_host *host) > +{ > + u16 ctrl; > + > + ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); > + if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { > + ctrl |= SDHCI_CLOCK_CARD_EN; > + sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); > + } > +} > + > static int dwcmshc_get_enable_other_clks(struct device *dev, > struct dwcmshc_priv *priv, > int num_clks, > @@ -1095,6 +1154,411 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host, > ARRAY_SIZE(clk_ids), clk_ids); > } > > +static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) > +{ > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + u16 clk; > + > + host->mmc->actual_clock = clock; > + > + if (clock == 0) { > + sdhci_set_clock(host, clock); > + return; > + } > + > + clk_set_rate(pltfm_host->clk, clock); > + > + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); > + clk |= SDHCI_CLOCK_INT_EN; > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); > + > + dwcmshc_enable_card_clk(host); > +} > + > +static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay) > +{ > + delay &= PHY_CLK_MAX_DELAY_MASK; > + > + /* phy clk delay line config */ > + sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R); > + sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R); > + sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R); > +} > + > +static void sdhci_eic7700_config_phy(struct sdhci_host *host) > +{ > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); > + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; > + struct eic7700_priv *priv = dwc_priv->priv; > + unsigned int val, drv; > + > + drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF); > + drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF); > + > + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { > + val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); > + val |= DWCMSHC_CARD_IS_EMMC; > + sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); > + } > + > + /* reset phy, config phy's pad */ > + sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R); > + > + /* configure phy pads */ > + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); > + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); > + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); > + val |= PHY_PAD_RXSEL_1V8; > + sdhci_writew(host, val, PHY_CMDPAD_CNFG_R); > + sdhci_writew(host, val, PHY_DATAPAD_CNFG_R); > + sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R); > + > + /* Clock PAD Setting */ > + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); > + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); > + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); > + > + /* PHY strobe PAD setting (EMMC only) */ > + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { > + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); > + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); > + val |= PHY_PAD_RXSEL_1V8; > + sdhci_writew(host, val, PHY_STBPAD_CNFG_R); > + } > + usleep_range(2000, 3000); > + sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R); > + sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line); > +} > + > +static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask) > +{ > + sdhci_reset(host, mask); > + > + /* after reset all, the phy's config will be clear */ > + if (mask == SDHCI_RESET_ALL) > + sdhci_eic7700_config_phy(host); > +} > + > +static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv) > +{ > + int ret; > + > + priv->reset = devm_reset_control_array_get_optional_exclusive(dev); > + if (IS_ERR(priv->reset)) { > + ret = PTR_ERR(priv->reset); > + dev_err(dev, "failed to get reset control %d\n", ret); > + return ret; > + } > + > + ret = reset_control_assert(priv->reset); > + if (ret) { > + dev_err(dev, "Failed to assert reset signals: %d\n", ret); > + return ret; > + } > + usleep_range(2000, 2100); > + ret = reset_control_deassert(priv->reset); > + if (ret) { > + dev_err(dev, "Failed to deassert reset signals: %d\n", ret); > + return ret; > + } > + > + return ret; > +} > + > +static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, unsigned int dr_ohm) > +{ > + switch (dr_ohm) { > + case 100: > + return PHYCTRL_DR_100OHM; > + case 66: > + return PHYCTRL_DR_66OHM; > + case 50: > + return PHYCTRL_DR_50OHM; > + case 40: > + return PHYCTRL_DR_40OHM; > + case 33: > + return PHYCTRL_DR_33OHM; > + } > + > + dev_warn(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm); > + return PHYCTRL_DR_50OHM; > +} > + > +static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode) > +{ > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); > + int delay_min = -1; > + int delay_max = -1; > + int cmd_error = 0; > + int delay = 0; > + int i = 0; > + int ret; > + > + for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) { > + sdhci_eic7700_config_phy_delay(host, i); > + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); > + if (ret) { > + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); > + usleep_range(200, 210); > + if (delay_min != -1 && delay_max != -1) > + break; > + } else { > + if (delay_min == -1) { > + delay_min = i; > + continue; > + } else { > + delay_max = i; > + continue; > + } > + } > + } > + if (delay_min == -1 && delay_max == -1) { > + pr_err("%s: delay code tuning failed!\n", mmc_hostname(host->mmc)); > + sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line); > + return ret; > + } > + > + delay = (delay_min + delay_max) / 2; > + sdhci_eic7700_config_phy_delay(host, delay); > + > + return 0; > +} > + > +static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) > +{ > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); > + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; > + int phase_code = -1; > + int code_range = -1; > + bool is_sd = false; > + int code_min = -1; > + int code_max = -1; > + int cmd_error = 0; > + int ret = 0; > + int i = 0; > + > + if ((host->mmc->caps2 & sd_caps) == sd_caps) > + is_sd = true; > + > + for (i = 0; i <= MAX_PHASE_CODE; i++) { > + /* Centered Phase code */ > + sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); > + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); > + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); > + > + if (ret) { > + /* SD specific range tracking */ > + if (is_sd && code_min != -1 && code_max != -1) { > + if (code_max - code_min > code_range) { > + code_range = code_max - code_min; > + phase_code = (code_min + code_max) / 2; > + if (code_range > TUNING_RANGE_THRESHOLD) > + break; > + } > + code_min = -1; > + code_max = -1; > + } > + /* EMMC breaks after first valid range */ > + if (!is_sd && code_min != -1 && code_max != -1) > + break; > + } else { > + /* Track valid phase code range */ > + if (code_min == -1) { > + code_min = i; > + if (!is_sd) > + continue; > + } > + code_max = i; > + if (is_sd && i == MAX_PHASE_CODE) { > + if (code_max - code_min > code_range) { > + code_range = code_max - code_min; > + phase_code = (code_min + code_max) / 2; > + } > + } > + } > + } > + > + /* Handle tuning failure case */ > + if ((is_sd && phase_code == -1) || > + (!is_sd && code_min == -1 && code_max == -1)) { > + pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc)); > + sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); > + return -EIO; > + } > + if (!is_sd) > + phase_code = (code_min + code_max) / 2; > + > + sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); > + > + /* SD specific final verification */ > + if (is_sd) { > + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); > + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); > + if (ret) { > + pr_err("%s: Final phase code 0x%x verification failed!\n", > + mmc_hostname(host->mmc), phase_code); > + return ret; > + } > + } > + > + return 0; > +} > + > +static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode) > +{ > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); > + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; > + int ret = 0; > + u16 ctrl; > + u32 val; > + > + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); > + ctrl &= ~SDHCI_CTRL_TUNED_CLK; > + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); > + > + val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); > + val |= AT_CTRL_SW_TUNE_EN; > + sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); > + > + sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); > + sdhci_writew(host, 0x0, SDHCI_CMD_DATA); > + > + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { > + ret = sdhci_eic7700_delay_tuning(host, opcode); > + if (ret) > + return ret; > + } > + > + ret = sdhci_eic7700_phase_code_tuning(host, opcode); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) > +{ > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); > + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); > + u8 status; > + u32 val; > + int ret; > + > + dwcmshc_set_uhs_signaling(host, timing); > + > + /* here need make dll locked when in hs400 at 200MHz */ > + if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) { > + val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); > + val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY)); > + /* 2-cycle latency */ > + val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2); > + sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); > + > + sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) | > + 0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */ > + /* DLL jump step input */ > + sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R); > + sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, > + PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R); > + /* Sets the value of DLL's offset input */ > + sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R); > + /* > + * Sets the value of DLL's olbt loadval input. Controls the Ibt > + * timer's timeout value at which DLL runs a revalidation cycle. > + */ > + sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R); > + sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R); > + usleep_range(100, 110); > + > + ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000, > + false, host, PHY_DLL_STATUS_R); > + if (ret) { > + pr_err("%s: DLL lock timeout! status: 0x%x\n", > + mmc_hostname(host->mmc), status); > + return; > + } > + > + status = sdhci_readb(host, PHY_DLL_STATUS_R); > + if (status & DLL_ERROR_STS) { > + pr_err("%s: DLL lock failed!err_status:0x%x\n", > + mmc_hostname(host->mmc), status); > + } > + } > +} > + > +static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing) > +{ > + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; > + > + if ((host->mmc->caps2 & sd_caps) == sd_caps) > + sdhci_set_uhs_signaling(host, timing); > + else > + sdhci_eic7700_set_uhs_signaling(host, timing); > +} > + > +static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) > +{ > + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; > + unsigned int val, hsp_int_status, hsp_pwr_ctrl; > + struct of_phandle_args args; > + struct eic7700_priv *priv; > + struct regmap *hsp_regmap; > + int ret; > + > + priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + dwc_priv->priv = priv; > + > + ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv); > + if (ret) { > + dev_err(dev, "failed to reset\n"); > + return ret; > + } > + > + ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args); > + if (ret) { > + dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret); > + return ret; > + } > + > + hsp_regmap = syscon_node_to_regmap(args.np); > + if (IS_ERR(hsp_regmap)) { > + dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n"); > + of_node_put(args.np); > + return PTR_ERR(hsp_regmap); > + } > + hsp_int_status = args.args[0]; > + hsp_pwr_ctrl = args.args[1]; > + of_node_put(args.np); > + /* > + * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status. > + * This signals to the eMMC controller that platform clocks (card, ACLK, > + * BCLK, TMCLK) are enabled and stable. > + */ > + regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE); > + /* > + * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl. > + * This signals that VDD is stable and permits transition to high-speed > + * modes (e.g., UHS-I). > + */ > + regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE); > + > + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) > + dwc_priv->delay_line = PHY_DELAY_CODE_EMMC; > + else > + dwc_priv->delay_line = PHY_DELAY_CODE_SD; > + > + if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val)) > + priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val); > + return 0; > +} > + > static const struct sdhci_ops sdhci_dwcmshc_ops = { > .set_clock = sdhci_set_clock, > .set_bus_width = sdhci_set_bus_width, > @@ -1169,6 +1633,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = { > .platform_execute_tuning = th1520_execute_tuning, > }; > > +static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = { > + .set_clock = sdhci_eic7700_set_clock, > + .get_max_clock = sdhci_pltfm_clk_get_max_clock, > + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, > + .set_bus_width = sdhci_set_bus_width, > + .reset = sdhci_eic7700_reset, > + .set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper, > + .set_power = sdhci_set_power_and_bus_voltage, > + .irq = dwcmshc_cqe_irq_handler, > + .platform_execute_tuning = sdhci_eic7700_executing_tuning, > +}; > + > static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { > .pdata = { > .ops = &sdhci_dwcmshc_ops, > @@ -1238,6 +1714,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = { > .init = sg2042_init, > }; > > +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = { > + .pdata = { > + .ops = &sdhci_dwcmshc_eic7700_ops, > + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | > + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, > + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | > + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, > + }, > + .init = eic7700_init, > +}; > + > static const struct cqhci_host_ops dwcmshc_cqhci_ops = { > .enable = dwcmshc_sdhci_cqe_enable, > .disable = sdhci_cqe_disable, > @@ -1338,6 +1825,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { > .compatible = "sophgo,sg2042-dwcmshc", > .data = &sdhci_dwcmshc_sg2042_pdata, > }, > + { > + .compatible = "eswin,eic7700-dwcmshc", > + .data = &sdhci_dwcmshc_eic7700_pdata, > + }, > {}, > }; > MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); > @@ -1570,17 +2061,6 @@ static int dwcmshc_resume(struct device *dev) > return ret; > } > > -static void dwcmshc_enable_card_clk(struct sdhci_host *host) > -{ > - u16 ctrl; > - > - ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); > - if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { > - ctrl |= SDHCI_CLOCK_CARD_EN; > - sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); > - } > -} > - > static int dwcmshc_runtime_suspend(struct device *dev) > { > struct sdhci_host *host = dev_get_drvdata(dev); ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Re: [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700 2025-10-23 11:08 ` Adrian Hunter @ 2025-10-24 3:19 ` 何欢 2025-10-24 6:36 ` Adrian Hunter 0 siblings, 1 reply; 11+ messages in thread From: 何欢 @ 2025-10-24 3:19 UTC (permalink / raw) To: Adrian Hunter Cc: ulf.hansson, robh, krzk+dt, conor+dt, jszhang, p.zabel, linux-mmc, devicetree, linux-kernel, ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang, 何欢 Hi Adrian, Thank you very much for your Acked-by and the review! > On 19/10/2025 14:53, hehuan1@eswincomputing.com wrote: > > From: Huan He <hehuan1@eswincomputing.com> > > > > Add support for the mmc controller in the Eswin EIC7700 with the new > > compatible "eswin,eic7700-dwcmshc". Implement custom sdhci_ops for > > set_clock, reset, set_uhs_signaling, platform_execute_tuning. > > > > Signed-off-by: Huan He <hehuan1@eswincomputing.com> > > Noting that the dt-bindings patch still seems to have issues, > for this patch, anyway: > > Acked-by: Adrian Hunter <adrian.hunter@intel.com> I recieved an dt_binding_check error report: https://lore.kernel.org/all/176096011380.22917.1988679321096076522.robh@kernel.org/ It seems the error has nothing to do with MMC, but is related to the Ethernet binding (eswin,eic7700-eth.yaml), that was pushed by us and applied recently, link: https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/commit/?id=888bd0eca93c We're fixing this issue for the Ethernet. The Ethernet patch with this bug fix will be sent out later. I would be grateful for your reply. Regards, Huan He ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700 2025-10-24 3:19 ` 何欢 @ 2025-10-24 6:36 ` Adrian Hunter 0 siblings, 0 replies; 11+ messages in thread From: Adrian Hunter @ 2025-10-24 6:36 UTC (permalink / raw) To: 何欢, ulf.hansson Cc: robh, krzk+dt, conor+dt, jszhang, p.zabel, linux-mmc, devicetree, linux-kernel, ningyu, linmin, pinkesh.vaghela, xuxiang, luyulin, dongxuyang, zhangsenchuan, weishangjuan, lizhi2, caohang On 24/10/2025 06:19, 何欢 wrote: > Hi Adrian, > > Thank you very much for your Acked-by and the review! > >> On 19/10/2025 14:53, hehuan1@eswincomputing.com wrote: >>> From: Huan He <hehuan1@eswincomputing.com> >>> >>> Add support for the mmc controller in the Eswin EIC7700 with the new >>> compatible "eswin,eic7700-dwcmshc". Implement custom sdhci_ops for >>> set_clock, reset, set_uhs_signaling, platform_execute_tuning. >>> >>> Signed-off-by: Huan He <hehuan1@eswincomputing.com> >> >> Noting that the dt-bindings patch still seems to have issues, >> for this patch, anyway: >> >> Acked-by: Adrian Hunter <adrian.hunter@intel.com> > > I recieved an dt_binding_check error report: > https://lore.kernel.org/all/176096011380.22917.1988679321096076522.robh@kernel.org/ > > It seems the error has nothing to do with MMC, but is related to the > Ethernet binding (eswin,eic7700-eth.yaml), that was pushed by us and > applied recently, link: > https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/commit/?id=888bd0eca93c > > We're fixing this issue for the Ethernet. The Ethernet patch with > this bug fix will be sent out later. That seems OK, but it is up to Ulf now. ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-10-24 22:22 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-10-19 11:51 [PATCH v5 0/2] Add support for Eswin EIC7700 SD/eMMC controller hehuan1 2025-10-19 11:52 ` [PATCH v5 1/2] dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700 hehuan1 2025-10-20 11:35 ` Rob Herring (Arm) 2025-10-24 13:57 ` Ulf Hansson 2025-10-24 16:37 ` Conor Dooley 2025-10-24 21:47 ` Conor Dooley 2025-10-24 22:22 ` Rob Herring 2025-10-19 11:53 ` [PATCH v5 2/2] mmc: sdhci-of-dwcmshc: Add support for " hehuan1 2025-10-23 11:08 ` Adrian Hunter 2025-10-24 3:19 ` 何欢 2025-10-24 6:36 ` Adrian Hunter
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).