Add Renesas RZ/G3L SD/eMMC support
Subsystems (3)
v25 cover Add Renesas RZ/G3L SD/eMMC support
Hi All,
The Renesas RZ/G3L SoC has:
Channel 0 supports SD and eMMC (including HS400/HS400ES). Channel 1 supports SD and eMMC (except for HS400). Channel 2 supports SD.
The SoC supports a maximum frequency of 150 MHz. The SD0 interface does not support IOVS and PWEN in the SDHI register (no internal regulator), unlike SD1 and SD2. It has an internal divider for all modes except HS400. It also has a 2048-bit divider compared to 512 on others. Moreover RZ/G3L supports HS400 enhanced strobe mode.
This patch series support all modes except HS400. HS400 support will be added later.
This patch series tested on smarc-rz{v2l,g2l,g2lc,g2ul,g3s,g3l,g3e}, rz{v2h,t2h}-evk and hihope-rz{g2h,g2m,g2n} boards
v24->v25:
* Added max-frequency is a required property for SoCs with internal divider.
* Retained the tags as the change is trivial.
* Restored struct of_default_cfg.
* Updated commit description for patch#3,#4,#6,#10 and #11
* Dropped initializing clk_mask as it uses the default value.
* Added suffix ULL to macro SDHI_CLK_MASK_DEFAULT.
* Fixed a very low or zero clock rate from the clock provider that causes an infinite loop in renesas_sdhi_set_clock by max operator.
* Restored the data type of max_divider from signed int to unsigned int.
* Dropped initializing max_divider as it use default value.
* Updated mmc->f_min calculation with clk_round_rate().
* Updated the comment using "ilog2(max_divider)" instead of "{9,11}".
* Dropped initializing clk_div_mask as it use default value.
* Updated renesas_sdhi_set_hw_adjustment_delay() with force_clear parameter
* Added a comment in renesas_sdhi_execute_tuning() for tuning delay.
* In probe, if the SCC registers are not available, it clear the tuning delay flag.
* Dropped default masks from struct of_data_rz_g2l.
* Dropped renesas_sdhi_is_internal_divider_enabled().
* Added a comment about the internal divider in renesas_sdhi_clk_update() and replaced 'clkh_shift = 1' with 'clkh_shift--'
* Moved mmc->f_max to probe() and made it fail if max-frequency is not defined in DT for SoCs with an internal divider.
* Updated tmio_mmc_host_probe() to set write32_hook() to NULL if TMIO_MMC_HAS_IDLE_WAIT is not defined.
* Added macros RZG3L_SDHI_SCC_HWADJ2_{1_8V,3_3V}
* Replaced the quirks sdhi_quirks_rzg3l with sdhi_quirks_fixed_addr.
* Added helper renesas_sdhi_sclken() to handle 16-bit and 32-bit access.
* Updated renesas_sdhi_set_clock() with max_divider check for TMIO_MMC_INTERNAL_DIVIDER instead of SDHI_MAX_DIVIDER_DEFAULT for consistency with tmio_mmc_reset().
- Link to v24: https://lore.kernel.org/all/[email protected]/v23->v24:
* Added Add SDHI_SD_CLK_CTL_DIV1 macro for handling 1/1 option.
* Updated commit description for patch#6.
* Replaced GENMASK(7, 0) with CLK_CTL_DIV_MASK.
* Replaced magic bits GENMASK(9, 8) and GENMASK(17, 16) with RZG3L_CLK_CTL_DIV9_DIV8 and RZG3L_CLK_CTL_DIV9_DIV8_SRC.
* Simplified renesas_sdhi_set_tmpport().
* Replaced GENMASK(15, 0) with RZG3L_SDHI_SCC_HS400MODE1_TMPOUT.v22->v23:
* Added write32hook function for hardware idle synchronization for CTL_SD_CARD_CLK_CTL writes.
* Changed the data type of max_divider from unsigned int to signed int.
* Assigned of_data->clk_div_mask in renesas_sdhi_probe().
* Updated commit description for patch#7.
* Updated tmio_mmc_reset() to handle 32 bit divider register in RZ/G3L.v21->v22:
* Added HS400 ES mode after fixing inode corruption when eMMC is
mounted as rootfs.
* Updated commit descriptions for patches #4, #5, #6, #7, #9, #12,
and #13.
* Added clk_div_mask to of_default_cfg and of_rcar_gen2_compatible.
* Added a renesas_sdhi_set_hw_adjustment_delay() call to renesas_sdhi_init_tuning().
* Added renesas_sdhi_is_internal_divider_enabled() and renesas_sdhi_clk_get_rate() to simplify the clock rate logic.
* Added clock &= ~GENMASK(9, 8) to clear the divider mask bits in
the register value.
* Added a renesas_sdhi_set_tmpport() helper to set delay values.
* Added a check in renesas_sdhi_is_internal_divider_enabled() to
return disabled status when in HS400 mode.
* renesas_sdhi_reset() now restores data strobe register values
after reset.v20->v21:
* Dropped HS400 ES support due to file system corruption, will add this
mode later after investigation.
* Added support for make clock divider mask configurable
* Added clk_div_mask to RZ/G2L of_data.
* Added clk_div_mask to RZ/G3L of_data.
* Restored actual_clk calculation in renesas_sdhi_set_clock().
* Updated renesas_sdhi_set_clock() for RZ/G3L HS400 clock handling as
it supports only single clk divider value and other values are
prohibited.
* Updated commit description for patch#12.v19->v20:
* Replaced the check mmd->clk_mask with mmc_data->clk_mask and moved
the code after assignment of variable mmd, this ensures assigning
the default values for non-DT platforms and DT platforms with no
device data.
* Replaced the check mmd->max_divider with mmc_data->max_divider and
moved the code after assignment of variable mmd, this ensures
assigning the default values for non-DT platforms and DT platforms with
no device data.
* Fixed the ordering of resets in suspend/resume paths.
* Added bitfield.h header file.
* Reworked on renesas_sdhi_set_clock() to handle the 11-bit divider
case and the 32-bit register write.
* Updated resume() with scoped PM runtime call.
* SD_CLK_CTRL clk enable turned off before updating SCC_CKSEL_DTSEL
register.v18->v19:
* Updated commit description for patch#1,#2,#6,#8,#11 and #12
* Updated clock and reset description with AXI master and AXI slave
* Added else condition for reset and reset-names.
* Renamed aclk->aclkm and reordered the axi clocks similar to resets.
* Retained the tag got binding patch as the changes are trivial.
* Fixed the clk_mask for non-DT platforms.
* Fixed max-divider setting for non-DT platforms.
* Replaced the magic number '9' with ilog2 function in renesas_sdhi_clk_enable().
* Dropped divider variable from struct renesas_sdhi.
* Updated renesas_sdhi_clk_update() to return rate for HS400 mode
and non HS400 mode(uses 1/2 internal divider).
* Updated handling for clk divider.
* Renamed TMIO_MMC_HWADJ2->TMIO_MMC_HWADJ to make it generic for
hardware tuning delays
* Dropped duplicate SH_MOBILE_SDHI_SCC_TMPPORT2 macro
* Updated suspend/resume with scoped runtime calls in suspend.
* Fixed extra space in HS400MODE2 comment block.
* Updated the comment HS400mode2->HS400MODE2.
* Dropped the updation of clk handling as it is taken care in
previous patches.
* HS400ES support is enabled based on of_data.
* Fixed the space in HS400ES comment block.
v17->v18:
* Collected tag
* Merged patch #4 and #5 and updated commit description
* Annotated the empty sentinel entries in the OF match tables with a "Sentinel." comment for clarity.
* Retained the tag as it is a trivial cleanup.
* New patches drop struct renesas_sdhi_hw_info, instead using renesas_sdhi_of_data and tmio_mmc_data.
* Dropped clk, pinctrl, SoC, and board dtsi from this patch series; will send later.v1->v17:
* Collected tag for binding patch.
* Resending the series as there is an issue with patch threading from patch #14.
---
Biju Das (11):
dt-bindings: mmc: renesas,sdhi: Document RZ/G3L (r9a08g046) SoC
mmc: renesas_sdhi: Clean up whitespace and add OF table sentinels
mmc: renesas_sdhi: Add clk_mask to support wider clock dividers
mmc: renesas_sdhi: Add max_divider field to support SoC-specific clock ranges
mmc: renesas_sdhi: Add SDHI_SD_CLK_CTL_DIV1 macro for 1/1 option
mmc: renesas_sdhi: Make clock divider mask configurable
mmc: renesas_sdhi: Add tuning delay support for RZ/G2L
mmc: renesas_sdhi: Add TMIO_MMC_INTERNAL_DIVIDER flag
mmc: renesas_sdhi: Add optional axis/axim reset controls
mmc: renesas_sdhi: Add write32_hook for CTL_SD_CARD_CLK_CTL register writes
mmc: renesas_sdhi: Add RZ/G3L SDHI support
.../devicetree/bindings/mmc/renesas,sdhi.yaml | 109 ++++++-- drivers/mmc/host/renesas_sdhi.h | 10 +- drivers/mmc/host/renesas_sdhi_core.c | 305 +++++++++++++++------ drivers/mmc/host/renesas_sdhi_internal_dmac.c | 59 +++- drivers/mmc/host/renesas_sdhi_sys_dmac.c | 2 +- drivers/mmc/host/tmio_mmc.h | 7 + drivers/mmc/host/tmio_mmc_core.c | 20 +- include/linux/platform_data/tmio.h | 12 + 8 files changed, 405 insertions(+), 119 deletions(-)
---
base-commit: 20a33c6b6cbe7e4805dfb6cd68816937443aa148
change-id: 20261022-rzg3l_sdhi-b0bd3257a2d8
Best regards,
Signature · 1 line
Biju Das <[email protected]>
PATCH v25 1/11 dt-bindings: mmc: renesas,sdhi: Document RZ/G3L (r9a08g046) SoC
From: Biju Das <[email protected]>
Document the RZ/G3L (r9a08g046) SDHI controller. The RZ/G3L SDHI controller is similar to RZ/G2L but has five clocks (core, clkh, cd, aclkm, aclks) and three resets (rst, axim, axis), so update the clocks/clock-names maximum to 5 and resets/reset-names maximum to 3.
It also has an internal divider for all modes except HS400, with a maximum division ratio of 2048 compared to 512 on other SoCs. Because of this divider, make max-frequency required so that it is always defined in DT.
---v24->v25:
* Updated commit description.
* Added max-frequency is a required property for SoCs with internal divider.
* Retained the tags as the change is trivial.v23->v24:
* No change.v22->v23:
* No change.v21->v22:
* No change.v20->v21:
* No change.v19->v20:
* No change.v18->v19:
* Updated commit description
* Updated clock and reset description with AXI master and AXI slave
* Added else condition for reset and reset-names.
* Renamed aclk->aclkm and reordered the axi clocks similar to resets.
* Retained the tag as the changes are trivial.v17->v18:
* No change.v1->v17:
* Collected tag.
--- .../devicetree/bindings/mmc/renesas,sdhi.yaml | 109 ++++++++++++++++----- 1 file changed, 82 insertions(+), 27 deletions(-)
Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +82 -27
@@ -18,6 +18,7 @@ properties: - renesas,sdhi-r7s9210 # SH-Mobile AG5 - renesas,sdhi-r8a73a4 # R-Mobile APE6 - renesas,sdhi-r8a7740 # R-Mobile A1 + - renesas,sdhi-r9a08g046 # RZ/G3L - renesas,sdhi-r9a09g057 # RZ/V2H(P) - renesas,sdhi-sh73a0 # R-Mobile APE6 - items:
@@ -86,11 +87,11 @@ properties: clocks: minItems: 1 - maxItems: 4 + maxItems: 5 clock-names: minItems: 1 - maxItems: 4 + maxItems: 5 dmas: minItems: 4
@@ -116,7 +117,12 @@ properties: maxItems: 1 resets: - maxItems: 1 + minItems: 1 + maxItems: 3 + + reset-names: + minItems: 1 + maxItems: 3 pinctrl-0: minItems: 1
@@ -155,60 +161,107 @@ allOf: properties: compatible: contains: - enum: - - renesas,sdhi-r9a09g057 - - renesas,rzg2l-sdhi + const: renesas,sdhi-r9a08g046 then: properties: 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. + 2 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: IACLKM, SDHI channel AXI master bus clock. + - description: IACLKS, SDHI channel AXI slave bus clock. clock-names: items: - const: core - const: clkh - const: cd - - const: aclk + - const: aclkm + - const: aclks + resets: + items: + - description: rst, Core reset. + - description: axim, SDHI channel AXI master bus reset. + - description: axis, SDHI channel AXI slave bus reset. + reset-names: + items: + - const: rst + - const: axim + - const: axis required: - clock-names - resets + - reset-names + - max-frequency else: + properties: + resets: + maxItems: 1 + reset-names: + maxItems: 1 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 - clock-names: - minItems: 1 - uniqueItems: true items: - - const: core - - enum: [ clkh, cd ] - - const: cd - else: - properties: - clocks: - minItems: 1 - maxItems: 2 + - 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 items: - const: core + - const: clkh - const: cd + - const: aclk + resets: + maxItems: 1 + required: + - clock-names + - resets + else: + 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:
@@ -247,7 +300,9 @@ allOf: properties: compatible: contains: - const: renesas,sdhi-r9a09g057 + enum: + - renesas,sdhi-r9a08g046 + - renesas,sdhi-r9a09g057 then: properties: vqmmc-regulator:
Signature · 1 line
2.43.0
PATCH v25 2/11 mmc: renesas_sdhi: Clean up whitespace and add OF table sentinels
In reply to Biju DasFrom: Biju Das <[email protected]>
Clean up coding style inconsistencies across the Renesas SDHI host driver files. Remove redundant internal spacing in the struct definitions and static initializers to enforce a single space separator or uniform tabs, aligning the code with standard kernel formatting guidelines.
While at it, add a "Sentinel." comment block to the empty terminating entries of the Open Firmware (OF) device matching tables to improve code readability and explicitly mark the table boundaries.
---v24->v25:
* Restored struct of_default_cfg.v23->v24:
* No change.v22->v23:
* No change.v21->v22:
* No change.v20->v21:
* No change.v19->v20:
* No change.v18->v19:
* Updated commit description.v17->v18:
* Merged patch#4 and #5
* Updated commit description
* Annotate the empty sentinel entries in the OF match tables with a "Sentinel." comment for clarity.
* Retained the tag as it is trivial cleanups.v1->v17:
* No change.
--- drivers/mmc/host/renesas_sdhi.h | 2 +- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 6 +++--- drivers/mmc/host/renesas_sdhi_sys_dmac.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-)
drivers/mmc/host/renesas_sdhi.h +1 -1
@@ -25,7 +25,7 @@ struct renesas_sdhi_scc {struct renesas_sdhi_of_data {unsigned long tmio_flags; -u32 tmio_ocr_mask; +u32 tmio_ocr_mask;unsigned long capabilities;unsigned long capabilities2;enum dma_slave_buswidth dma_buswidth;
drivers/mmc/host/renesas_sdhi_internal_dmac.c +3 -3
@@ -270,8 +270,8 @@ static const struct renesas_sdhi_of_data_with_quirks of_rcar_gen3_nohs400_compat };static const struct renesas_sdhi_of_data_with_quirks of_rza2_compatible = { - .of_data = &of_data_rza2, - .quirks = &sdhi_quirks_fixed_addr, + .of_data = &of_data_rza2, + .quirks = &sdhi_quirks_fixed_addr, };static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
@@ -292,7 +292,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, }, - {}, + { /* Sentinel. */ } }; MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match);
drivers/mmc/host/renesas_sdhi_sys_dmac.c +1 -1
@@ -88,7 +88,7 @@ static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { { .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, }, { .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-shmobile" }, - {}, + { /* Sentinel. */ } }; MODULE_DEVICE_TABLE(of, renesas_sdhi_sys_dmac_of_match);
Signature · 1 line
2.43.0
PATCH v25 3/11 mmc: renesas_sdhi: Add clk_mask to support wider clock dividers
In reply to Biju DasFrom: Biju Das <[email protected]>
The clock divider on the RZ/G3L SoC is 11 bits wide, which needs a clock mask of 0x200000200 in renesas_sdhi_set_clock() instead of the hardcoded 0x80000080 used so far.
Add a u64 clk_mask field to struct renesas_sdhi_of_data and struct tmio_mmc_data so SoC-specific match data can supply its own mask, and widen the clk and clock variables in renesas_sdhi_set_clock() from u32 to u64 to hold it.
While at it, clamp the initial clock value to a minimum of 1. If actual_clock / 512 evaluates to 0, the loop condition "new_clock + clk_margin >= (clock << 1)" is always true, and the loop never terminates.
In probe, copy clk_mask from the OF data and fall back to SDHI_CLK_MASK_DEFAULT when none is provided, so existing SoCs keep their current behaviour.
---v24->v25:
* Updated commit header and description.
* Dropped initializing clk_mask as it uses the default value.
* Added suffix ULL to macro SDHI_CLK_MASK_DEFAULT.
* Fixed a very low or zero clock rate from the clock provider that causes an infinite loop in renesas_sdhi_set_clock by max operator.v23->v24:
* No change.v22->v23:
* No change.v21->v22:
* No change.v20->v21:
* No change.v19->v20:
* Replaced the check mmd->clk_mask with mmc_data->clk_mask and moved the code after assignment of variable mmd, this ensures assigning the default values for non-DT platforms and DT platforms with no device data.v18->v19:
* Fixed the clk_mask for non-DT platforms.
v18:
* New patch dropping struct renesas_sdhi_hw_info instead using renesas_sdhi_of_data and tmio_mmc_data.
--- drivers/mmc/host/renesas_sdhi.h | 2 ++ drivers/mmc/host/renesas_sdhi_core.c | 10 +++++++--- include/linux/platform_data/tmio.h | 1 + 3 files changed, 10 insertions(+), 3 deletions(-)
drivers/mmc/host/renesas_sdhi.h +2 -0
@@ -22,6 +22,7 @@ struct renesas_sdhi_scc { };#define SDHI_FLAG_NEED_CLKH_FALLBACK BIT(0) +#define SDHI_CLK_MASK_DEFAULT 0x80000080ULLstruct renesas_sdhi_of_data {unsigned long tmio_flags;
@@ -37,6 +38,7 @@ struct renesas_sdhi_of_data {unsigned int max_blk_count;unsigned short max_segs;unsigned long sdhi_flags; +u64 clk_mask; };#define SDHI_CALIB_TABLE_MAX 32
drivers/mmc/host/renesas_sdhi_core.c +7 -3
@@ -194,7 +194,7 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host, unsigned int new_clock) {unsigned int clk_margin; -u32 clk =0 , clock; +u64 clk =0 , clock;sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , ~CLK_CTL_SCLKEN &sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL ));
@@ -205,7 +205,7 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host, } host->mmc->actual_clock =renesas_sdhi_clk_update (host, new_clock); - clock = host->mmc->actual_clock /512 ; + clock =max (host->mmc->actual_clock /512 ,1 );/* * Add a margin of 1/1024 rate higher to the clock rate in order
@@ -213,7 +213,7 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,* provided for actual_clock in renesas_sdhi_clk_update(). */ clk_margin = new_clock >>10 ; -for (clk =0x80000080 ; new_clock + clk_margin >= (clock <<1 ); clk >>=1 ) +for (clk = host->pdata->clk_mask; new_clock + clk_margin >= (clock <<1 ); clk >>=1 ) clock <<=1 ;/* 1/1 clock is option */
@@ -1136,6 +1136,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, mmc_data->dma_rx_offset = of_data->dma_rx_offset; mmc_data->max_blk_count = of_data->max_blk_count; mmc_data->max_segs = of_data->max_segs; + mmc_data->clk_mask = of_data->clk_mask; dma_priv->dma_buswidth = of_data->dma_buswidth; host->bus_shift = of_data->bus_shift;/* Fallback for old DTs */
@@ -1178,6 +1179,9 @@ int renesas_sdhi_probe(struct platform_device *pdev,if (mmd) *mmc_data = *mmd; +if (!mmc_data->clk_mask) + mmc_data->clk_mask =SDHI_CLK_MASK_DEFAULT ; + dma_priv->filter = shdma_chan_filter; dma_priv->enable = renesas_sdhi_enable_dma;
include/linux/platform_data/tmio.h +1 -0
@@ -61,5 +61,6 @@ struct tmio_mmc_data {dma_addr_t dma_rx_offset;unsigned int max_blk_count;unsigned short max_segs; + u64 clk_mask; };#endif
Signature · 1 line
2.43.0
Re: [PATCH v25 03/11] mmc: renesas_sdhi: Add clk_mask to support wider clock dividers
Quoted 16 lines · Biju Das
From: Biju Das <[email protected]>
The clock divider on the RZ/G3L SoC is 11 bits wide, which needs a clock mask of
0x200000200inrenesas_sdhi_set_clock()instead of the hardcoded0x80000080used so far.Add a u64
clk_maskfield to structrenesas_sdhi_of_dataand structtmio_mmc_dataso SoC-specific match data can supply its own mask, and widen the clk and clock variables inrenesas_sdhi_set_clock()from u32 to u64 to hold it.While at it, clamp the initial clock value to a minimum of 1. If
actual_clock/ 512 evaluates to 0, the loop condition "new_clock+clk_margin>= (clock << 1)" is always true, and the loop never terminates.
Quoted 5 lines
In probe, copy
clk_maskfrom the OF data and fall back toSDHI_CLK_MASK_DEFAULTwhen none is provided, so existing SoCs keep their current behaviour.Signed-off-by Biju Das <[email protected]>
PATCH v25 4/11 mmc: renesas_sdhi: Add max_divider field to support SoC-specific clock ranges
In reply to Biju DasFrom: Biju Das <[email protected]>
RZ/G3L supports a wider clock divider range than the hardcoded 512 used by existing SoCs. Add a max_divider field to renesas_sdhi_of_data and tmio_mmc_data, and use it in renesas_sdhi_clk_enable(), renesas_sdhi_clk_update(), and renesas_sdhi_set_clock() instead of the fixed value.
While at it, check the return value of clk_round_rate() in renesas_sdhi_clk_enable(). A negative error code was previously divided and clamped into f_min; now the error is propagated and the clk_cd clock is disabled again.
Default to SDHI_MAX_DIVIDER_DEFAULT (512) at probe time when platform data doesn't set it. This paves the way for RZ/G3L's extended divider support.
---v24->v25:
* Updated commit description.
* Restored the data type of max_divider from signed int to unsigned int.
* Dropped initializing max_divider as it use default value.
* Updated mmc->f_min calculation with clk_round_rate().
* Updated the comment using "ilog2(max_divider)" instead of "{9,11}".v23->v24:
* No change.v22->v23:
* Changed the data type of max_divider from unsigned int to signed int.v21->v22:
* Updated comit description.v20->v21:
* No change.v19->v20:
* Replaced the check mmd->max_divider with mmc_data->max_divider and moved the code after assignment of variable mmd, this ensures assigning the default values for non-DT platforms and DT platforms with no device data.v18->v19:
* Fixed max-divider setting for non-DT platforms.
* Replaced the magic number '9' with ilog2 function in renesas_sdhi_clk_enable().
v18:
* New patch.
--- drivers/mmc/host/renesas_sdhi.h | 2 ++ drivers/mmc/host/renesas_sdhi_core.c | 22 +++++++++++++++++----- include/linux/platform_data/tmio.h | 1 + 3 files changed, 20 insertions(+), 5 deletions(-)
drivers/mmc/host/renesas_sdhi.h +2 -0
@@ -23,6 +23,7 @@ struct renesas_sdhi_scc {#define SDHI_FLAG_NEED_CLKH_FALLBACK BIT(0)#define SDHI_CLK_MASK_DEFAULT 0x80000080ULL +#define SDHI_MAX_DIVIDER_DEFAULT 512struct renesas_sdhi_of_data {unsigned long tmio_flags;
@@ -39,6 +40,7 @@ struct renesas_sdhi_of_data {unsigned short max_segs;unsigned long sdhi_flags;u64 clk_mask; +unsigned int max_divider; };#define SDHI_CALIB_TABLE_MAX 32
drivers/mmc/host/renesas_sdhi_core.c +17 -5
@@ -22,6 +22,7 @@#include <linux/delay.h> #include <linux/iopoll.h> #include <linux/kernel.h> +#include <linux/minmax.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/mmc/slot-gpio.h>
@@ -98,6 +99,7 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host) {struct mmc_host *mmc = host->mmc;struct renesas_sdhi *priv =host_to_priv (host); +long rate;int ret; ret =clk_prepare_enable (priv->clk_cd);
@@ -117,7 +119,13 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)* Minimum frequency is the minimum input clock frequency * divided by our maximum divider. */ - mmc->f_min =max (clk_round_rate (priv->clk,1 ) /512 ,1L ); + rate =clk_round_rate (priv->clk,1 ); +if (rate <0 ) { +clk_disable_unprepare (priv->clk_cd); +return rate; + } + + mmc->f_min =max_t (long, rate / host->pdata->max_divider,1 );/* enable 16bit data access on SDBUF as default */ renesas_sdhi_sdbuf_width (host,16 );
@@ -156,8 +164,8 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,/* * We want the bus clock to be as close as possible to, but no * greater than, new_clock. As we can divide by 1 << i for -* any i in [0, 9] we want the input clock to be as close as -* possible, but no greater than, new_clock << i. +* any i in [0, ilog2(max_divider)] we want the input clock to be as +* close as possible, but no greater than, new_clock << i. * * Add an upper limit of 1/1024 rate higher to the clock rate to fix * clk rate jumping to lower rate due to rounding error (eg: RZ/G2L has
@@ -165,7 +173,7 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,* for 533.333333 MHz will selects a slower 400 MHz due to rounding * error (533333333 Hz / 4 * 4 = 533333332 Hz < 533333333 Hz)). */ -for (i =min (9 ,ilog2 (UINT_MAX / new_clock)); i >=0 ; i--) { +for (i =min (ilog2 (host->pdata->max_divider),ilog2 (UINT_MAX / new_clock)); i >=0 ; i--) { freq =clk_round_rate (ref_clk, new_clock << i); new_upper_limit = (new_clock << i) + ((new_clock << i) >>10 );if (freq > new_upper_limit) {
@@ -205,7 +213,7 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host, } host->mmc->actual_clock =renesas_sdhi_clk_update (host, new_clock); - clock =max (host->mmc->actual_clock /512 ,1 ); + clock =max (host->mmc->actual_clock / host->pdata->max_divider,1 );/* * Add a margin of 1/1024 rate higher to the clock rate in order
@@ -1137,6 +1145,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, mmc_data->max_blk_count = of_data->max_blk_count; mmc_data->max_segs = of_data->max_segs; mmc_data->clk_mask = of_data->clk_mask; + mmc_data->max_divider = of_data->max_divider; dma_priv->dma_buswidth = of_data->dma_buswidth; host->bus_shift = of_data->bus_shift;/* Fallback for old DTs */
@@ -1182,6 +1191,9 @@ int renesas_sdhi_probe(struct platform_device *pdev,if (!mmc_data->clk_mask) mmc_data->clk_mask =SDHI_CLK_MASK_DEFAULT ; +if (!mmc_data->max_divider) + mmc_data->max_divider =SDHI_MAX_DIVIDER_DEFAULT ; + dma_priv->filter = shdma_chan_filter; dma_priv->enable = renesas_sdhi_enable_dma;
include/linux/platform_data/tmio.h +1 -0
@@ -62,5 +62,6 @@ struct tmio_mmc_data {unsigned int max_blk_count;unsigned short max_segs;u64 clk_mask; + unsigned int max_divider; };#endif
Signature · 1 line
2.43.0
Re: [PATCH v25 04/11] mmc: renesas_sdhi: Add max_divider field to support SoC-specific clock ranges
Quoted 13 lines · Biju Das
From: Biju Das <[email protected]>
RZ/G3L supports a wider clock divider range than the hardcoded 512 used by existing SoCs. Add a
max_dividerfield torenesas_sdhi_of_dataandtmio_mmc_data, and use it inrenesas_sdhi_clk_enable(),renesas_sdhi_clk_update(), andrenesas_sdhi_set_clock()instead of the fixed value.While at it, check the return value of
clk_round_rate()inrenesas_sdhi_clk_enable(). A negative error code was previously divided and clamped intof_min; now the error is propagated and theclk_cdclock is disabled again.
Quoted 5 lines
Default to
SDHI_MAX_DIVIDER_DEFAULT(512) at probe time when platform data doesn't set it. This paves the way for RZ/G3L's extended divider support.Signed-off-by Biju Das <[email protected]>
PATCH v25 5/11 mmc: renesas_sdhi: Add SDHI_SD_CLK_CTL_DIV1 macro for 1/1 option
In reply to Biju DasFrom: Biju Das <[email protected]>
Replace the magic number 0xff, used for selecting the SD clock 1/1 divider option, with a new SDHI_SD_CLK_CTL_DIV1 macro. This improves readability and self-documents the purpose of the value at each use site (setting/clearing the divider bits and comparing against CLK_CTL_DIV_MASK).
No functional change.
---v24->v25:
* No change
v24:
* New patch
--- drivers/mmc/host/renesas_sdhi.h | 1 + drivers/mmc/host/renesas_sdhi_core.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-)
drivers/mmc/host/renesas_sdhi.h +1 -0
@@ -24,6 +24,7 @@ struct renesas_sdhi_scc {#define SDHI_FLAG_NEED_CLKH_FALLBACK BIT(0)#define SDHI_CLK_MASK_DEFAULT 0x80000080ULL#define SDHI_MAX_DIVIDER_DEFAULT 512 +#define SDHI_SD_CLK_CTL_DIV1 0xffstruct renesas_sdhi_of_data {unsigned long tmio_flags;
drivers/mmc/host/renesas_sdhi_core.c +3 -3
@@ -227,13 +227,13 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,/* 1/1 clock is option */ if ((host->pdata->flags &TMIO_MMC_CLK_ACTUAL ) && ((clk >>22 ) &0x1 )) {if (!(host->mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) - clk |=0xff ; + clk |=SDHI_SD_CLK_CTL_DIV1 ;else - clk &= ~0xff ; + clk &= ~SDHI_SD_CLK_CTL_DIV1 ; } clock = clk &CLK_CTL_DIV_MASK ; -if (clock !=CLK_CTL_DIV_MASK ) +if (clock !=SDHI_SD_CLK_CTL_DIV1 ) host->mmc->actual_clock /= (1 << (ffs (clock) +1 ));sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , clock);
Signature · 1 line
2.43.0
PATCH v25 6/11 mmc: renesas_sdhi: Make clock divider mask configurable
In reply to Biju DasFrom: Biju Das <[email protected]>
Add a clk_div_mask field to renesas_sdhi_of_data and tmio_mmc_data so each SoC can specify its own divider mask instead of the hardcoded CLK_CTL_DIV_MASK used in renesas_sdhi_set_clock().
Default to CLK_CTL_DIV_MASK at probe time when unset. This lets RZ/G3L, which has a wider divider field, reuse the same clock-setting code.
---v24->v25:
* Updated commit description
* Dropped initializing clk_div_mask as it use default value.v23->v24:
* Updated commit description.
* Replaced GENMASK(7, 0) with CLK_CTL_DIV_MASK.v22->v23:
* Assigned of_data->clk_div_mask in renesas_sdhi_probe().v21->v22:
* Updated commit description.
* Added clk_div_mask to of_default_cfg and of_rcar_gen2_compatible.
v21:
* New patch.
--- drivers/mmc/host/renesas_sdhi.h | 1 + drivers/mmc/host/renesas_sdhi_core.c | 6 +++++- include/linux/platform_data/tmio.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-)
drivers/mmc/host/renesas_sdhi.h +1 -0
@@ -42,6 +42,7 @@ struct renesas_sdhi_of_data {unsigned long sdhi_flags;u64 clk_mask;unsigned int max_divider; +u16 clk_div_mask; };#define SDHI_CALIB_TABLE_MAX 32
drivers/mmc/host/renesas_sdhi_core.c +5 -1
@@ -232,7 +232,7 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host, clk &= ~SDHI_SD_CLK_CTL_DIV1 ; } - clock = clk &CLK_CTL_DIV_MASK ; + clock = clk & host->pdata->clk_div_mask;if (clock !=SDHI_SD_CLK_CTL_DIV1 ) host->mmc->actual_clock /= (1 << (ffs (clock) +1 ));
@@ -1146,6 +1146,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, mmc_data->max_segs = of_data->max_segs; mmc_data->clk_mask = of_data->clk_mask; mmc_data->max_divider = of_data->max_divider; + mmc_data->clk_div_mask = of_data->clk_div_mask; dma_priv->dma_buswidth = of_data->dma_buswidth; host->bus_shift = of_data->bus_shift;/* Fallback for old DTs */
@@ -1194,6 +1195,9 @@ int renesas_sdhi_probe(struct platform_device *pdev,if (!mmc_data->max_divider) mmc_data->max_divider =SDHI_MAX_DIVIDER_DEFAULT ; +if (!mmc_data->clk_div_mask) + mmc_data->clk_div_mask =CLK_CTL_DIV_MASK ; + dma_priv->filter = shdma_chan_filter; dma_priv->enable = renesas_sdhi_enable_dma;
include/linux/platform_data/tmio.h +1 -0
@@ -63,5 +63,6 @@ struct tmio_mmc_data {unsigned short max_segs;u64 clk_mask;unsigned int max_divider; + u16 clk_div_mask; };#endif
Signature · 1 line
2.43.0
Re: [PATCH v25 06/11] mmc: renesas_sdhi: Make clock divider mask configurable
Quoted 11 lines · Biju Das
From: Biju Das <[email protected]>
Add a
clk_div_maskfield torenesas_sdhi_of_dataandtmio_mmc_dataso each SoC can specify its own divider mask instead of the hardcodedCLK_CTL_DIV_MASKused inrenesas_sdhi_set_clock().Default to
CLK_CTL_DIV_MASKat probe time when unset. This lets RZ/G3L, which has a wider divider field, reuse the same clock-setting code.Signed-off-by Biju Das <[email protected]>
PATCH v25 7/11 mmc: renesas_sdhi: Add tuning delay support for RZ/G2L
In reply to Biju DasFrom: Biju Das <[email protected]>
RZ/G2L and later SoCs (RZ/V2M and RZ/G3E) need a hardware adjustment delay written to SCC_TMPPORT2 on signal-voltage switch during
tuning: 0x1 for 1.8V, 0x0 for 3.3V.
Add a TMIO_MMC_TUNING_DELAY flag and renesas_sdhi_set_hw_adjustment_delay(), called from renesas_sdhi_start_signal_voltage_switch() after the regulator is set, and reset SCC_TMPPORT2 to 0 at the start of renesas_sdhi_execute_tuning() when the flag is set and tap_num == 8.
Add a dedicated of_data_rz_g2l descriptor with this flag and switch of_rzg2l_compatible to use it instead of the generic R-Car Gen3 descriptor.
---v24->v25:
* Updated commit description.
* Updated renesas_sdhi_set_hw_adjustment_delay() with force_clear parameter
* Added a comment in renesas_sdhi_execute_tuning() for tuning delay.
* In probe, if the SCC registers are not available, it clear the tuning delay flag.
* Dropped default masks from struct of_data_rz_g2l.v23->v24:
* Replaced GENMASK(7, 0) with CLK_CTL_DIV_MASK.v22->v23:
* No change.v21->v22:
* Updated commit description.
* Added a renesas_sdhi_set_hw_adjustment_delay() call to renesas_sdhi_init_tuning().v20->v21:
* Added clk_div_mask to RZ/G2L of_data.v19->v20:
* No change.v18->v19:
* No change.
v18:
* New patch.
--- drivers/mmc/host/renesas_sdhi_core.c | 101 +++++++++++++++++--------- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 19 ++++- include/linux/platform_data/tmio.h | 3 + 3 files changed, 88 insertions(+), 35 deletions(-)
drivers/mmc/host/renesas_sdhi_core.c +67 -34
@@ -264,40 +264,6 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc) TMIO_STAT_DAT0); } -static int renesas_sdhi_start_signal_voltage_switch (struct mmc_host *mmc, -struct mmc_ios *ios) -{ -struct tmio_mmc_host *host =mmc_priv (mmc); -struct renesas_sdhi *priv =host_to_priv (host); -struct pinctrl_state *pin_state; -int ret; - -switch (ios->signal_voltage) { -case MMC_SIGNAL_VOLTAGE_330 : - pin_state = priv->pins_default; -break ; -case MMC_SIGNAL_VOLTAGE_180 : - pin_state = priv->pins_uhs; -break ; -default : -return -EINVAL ; - } - -/* -* If anything is missing, assume signal voltage is fixed at -* 3.3V and succeed/fail accordingly. -*/ -if (IS_ERR (priv->pinctrl) ||IS_ERR (pin_state)) -return ios->signal_voltage == -MMC_SIGNAL_VOLTAGE_330 ?0 : -EINVAL ; - - ret =mmc_regulator_set_vqmmc (host->mmc, ios); -if (ret <0 ) -return ret; - -return pinctrl_select_state (priv->pinctrl, pin_state); -} -/* SCC registers */ #define SH_MOBILE_SDHI_SCC_DTCNTL 0x000#define SH_MOBILE_SDHI_SCC_TAPSET 0x002
@@ -358,6 +324,56 @@ static inline void sd_scc_write32(struct tmio_mmc_host *host,writel (val, priv->scc_ctl + (addr << host->bus_shift)); } +static void renesas_sdhi_set_hw_adjustment_delay (struct tmio_mmc_host *host, +bool force_clear) +{ +struct renesas_sdhi *priv =host_to_priv (host); + +if (!(host->pdata->flags &TMIO_MMC_TUNING_DELAY )) +return ; + +if (host->mmc->ios.signal_voltage ==MMC_SIGNAL_VOLTAGE_330 || force_clear) +sd_scc_write32 (host, priv,SH_MOBILE_SDHI_SCC_TMPPORT2 ,0x0 ); +else +sd_scc_write32 (host, priv,SH_MOBILE_SDHI_SCC_TMPPORT2 ,0x1 ); +} + +static int renesas_sdhi_start_signal_voltage_switch (struct mmc_host *mmc, +struct mmc_ios *ios) +{ +struct tmio_mmc_host *host =mmc_priv (mmc); +struct renesas_sdhi *priv =host_to_priv (host); +struct pinctrl_state *pin_state; +int ret; + +switch (ios->signal_voltage) { +case MMC_SIGNAL_VOLTAGE_330 : + pin_state = priv->pins_default; +break ; +case MMC_SIGNAL_VOLTAGE_180 : + pin_state = priv->pins_uhs; +break ; +default : +return -EINVAL ; + } + +/* +* If anything is missing, assume signal voltage is fixed at +* 3.3V and succeed/fail accordingly. +*/ +if (IS_ERR (priv->pinctrl) ||IS_ERR (pin_state)) +return ios->signal_voltage == +MMC_SIGNAL_VOLTAGE_330 ?0 : -EINVAL ; + + ret =mmc_regulator_set_vqmmc (host->mmc, ios); +if (ret <0 ) +return ret; + +renesas_sdhi_set_hw_adjustment_delay (host, false); + +return pinctrl_select_state (priv->pinctrl, pin_state); +} +static unsigned int renesas_sdhi_init_tuning (struct tmio_mmc_host *host) {struct renesas_sdhi *priv;
@@ -385,6 +401,8 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host)sd_scc_write32 (host, priv,SH_MOBILE_SDHI_SCC_DT2FF , priv->scc_tappos); +renesas_sdhi_set_hw_adjustment_delay (host, false); +sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL ,CLK_CTL_SCLKEN |sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL ));
@@ -717,6 +735,14 @@ static int renesas_sdhi_execute_tuning(struct mmc_host *mmc, u32 opcode)if (!priv->tap_num)return 0 ;/* Tuning is not supported */ +/* +* As per section 40.7.8 (Hardware adjustment register 2, SCC_TMPPORT) +* of the RZ/G2L hardware manual, if the tap number is 8, the tmpout +* bits must be set to 0. +*/ +if ((host->pdata->flags &TMIO_MMC_TUNING_DELAY ) && priv->tap_num ==8 ) +renesas_sdhi_set_hw_adjustment_delay (host, true); +if (priv->tap_num *2 >=sizeof (priv->taps) *BITS_PER_BYTE ) {dev_err (&host->pdev->dev,"Too many taps, please update 'taps' in tmio_mmc_host!\n" );
@@ -1267,6 +1293,13 @@ int renesas_sdhi_probe(struct platform_device *pdev,if (of_data && of_data->scc_offset && ver >=SDHI_VER_GEN2_SDR104 ) priv->scc_ctl = host->ctl + of_data->scc_offset; +/* +* The tuning delay is applied through SCC registers, so it must not +* be used when the SCC isn't mapped. +*/ +if (!priv->scc_ctl) + mmc_data->flags &= ~TMIO_MMC_TUNING_DELAY ; +/* Enable tuning iff we have an SCC and a supported mode */ if (priv->scc_ctl && (host->mmc->caps &MMC_CAP_UHS_SDR104 || host->mmc->caps2 &MMC_CAP2_HSX00_1_8V )) {
drivers/mmc/host/renesas_sdhi_internal_dmac.c +18 -1
@@ -135,6 +135,23 @@ static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = { .max_segs = 1, }; +static const struct renesas_sdhi_of_data of_data_rz_g2l = { + .tmio_flags =TMIO_MMC_HAS_IDLE_WAIT |TMIO_MMC_CLK_ACTUAL | +TMIO_MMC_HAVE_CBSY |TMIO_MMC_MIN_RCAR2 | +TMIO_MMC_64BIT_DATA_PORT |TMIO_MMC_TUNING_DELAY , + .capabilities =MMC_CAP_SD_HIGHSPEED |MMC_CAP_SDIO_IRQ | +MMC_CAP_CMD23 |MMC_CAP_WAIT_WHILE_BUSY , + .capabilities2 =MMC_CAP2_NO_WRITE_PROTECT |MMC_CAP2_MERGE_CAPABLE , + .bus_shift =2 , + .scc_offset =0x1000 , + .taps = rcar_gen3_scc_taps, + .taps_num =ARRAY_SIZE (rcar_gen3_scc_taps), +/* DMAC can handle 32bit blk count but only 1 segment */ + .max_blk_count =UINT_MAX /TMIO_MAX_BLK_SIZE , + .max_segs =1 , + .sdhi_flags =SDHI_FLAG_NEED_CLKH_FALLBACK , +}; +static const u8 r8a7796_es13_calib_table[2 ][SDHI_CALIB_TABLE_MAX ] = { {3 ,3 ,3 ,3 ,3 ,3 ,3 ,4 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,15 ,16 ,16 ,16 ,16 ,16 ,16 ,17 ,18 ,18 ,19 ,20 ,21 ,22 ,23 ,24 ,25 },
@@ -256,7 +273,7 @@ static const struct renesas_sdhi_of_data_with_quirks of_r8a77990_compatible = { };static const struct renesas_sdhi_of_data_with_quirks of_rzg2l_compatible = { - .of_data = &of_data_rcar_gen3, + .of_data = &of_data_rz_g2l, .quirks = &sdhi_quirks_rzg2l, };
include/linux/platform_data/tmio.h +3 -0
@@ -50,6 +50,9 @@/* Some controllers have a 64-bit wide data port register */ #define TMIO_MMC_64BIT_DATA_PORT BIT(12) +/* Some controllers have tuning delay */ +#define TMIO_MMC_TUNING_DELAY BIT(13) +struct tmio_mmc_data {void *chan_priv_tx;void *chan_priv_rx;
Signature · 1 line
2.43.0
Re: [PATCH v25 07/11] mmc: renesas_sdhi: Add tuning delay support for RZ/G2L
Quoted 12 lines · Biju Das
From: Biju Das <[email protected]>
RZ/G2L and later SoCs (RZ/V2M and RZ/G3E) need a hardware adjustment delay written to
SCC_TMPPORT2on signal-voltage switch during
tuning:0x1for 1.8V,0x0for 3.3V.Add a
TMIO_MMC_TUNING_DELAYflag andrenesas_sdhi_set_hw_adjustment_delay(), called fromrenesas_sdhi_start_signal_voltage_switch()after the regulator is set, and resetSCC_TMPPORT2to 0 at the start ofrenesas_sdhi_execute_tuning()when the flag is set andtap_num== 8.
Quoted 5 lines
Add a dedicated
of_data_rz_g2ldescriptor with this flag and switchof_rzg2l_compatibleto use it instead of the generic R-Car Gen3 descriptor.Signed-off-by Biju Das <[email protected]>
PATCH v25 8/11 mmc: renesas_sdhi: Add TMIO_MMC_INTERNAL_DIVIDER flag
In reply to Biju DasFrom: Biju Das <[email protected]>
RZ/G3L has a fixed internal /2 clock divider active in all bus timing modes except HS400, where the divider is bypassed. Add a TMIO_MMC_INTERNAL_DIVIDER flag to mark controllers with this divider.
Add renesas_sdhi_clk_get_rate() to fold the /2 division into the reported clock rate when the flag is set, and use it in renesas_sdhi_clk_update() in place of clk_get_rate(). HS400 is not yet supported in software for this SoC. Adjust clkh_shift for the internal divider case.
No existing platform sets this flag, so this is infrastructure only, with no functional change for current SoCs.
---v24->v25:
* Dropped renesas_sdhi_is_internal_divider_enabled().
* Added a comment about the internal divider in renesas_sdhi_clk_update() and replaced 'clkh_shift = 1' with 'clkh_shift--'
* Moved mmc->f_max to probe() and made it fail if max-frequency is not defined in DT for SoCs with an internal divider.v23->v24:
* No change.v22->v23:
* Updated commit description.v21->v22:
* Updated commit description.
* Added renesas_sdhi_is_internal_divider_enabled() and renesas_sdhi_clk_get_rate() to simplify the clock rate logic.v20->v21:
* No change.v19->v20:
* No change.v18->v19:
* Updated commit description.
* Dropped divider variable from struct renesas_sdhi.
* Updated renesas_sdhi_clk_update() to return rate for HS400mode and nonHS400mode(uses 1/2 internal divider).
v18:
* New patch using flag.
--- drivers/mmc/host/renesas_sdhi_core.c | 47 +++++++++++++++++++++++++++--------- include/linux/platform_data/tmio.h | 3 +++ 2 files changed, 38 insertions(+), 12 deletions(-)
drivers/mmc/host/renesas_sdhi_core.c +35 -12
@@ -106,15 +106,6 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)if (ret <0 )return ret; -/* -* The clock driver may not know what maximum frequency -* actually works, so it should be set with the max-frequency -* property which will already have been read to f_max. If it -* was missing, assume the current frequency is the maximum. -*/ -if (!mmc->f_max) - mmc->f_max =clk_get_rate (priv->clk); -/* * Minimum frequency is the minimum input clock frequency * divided by our maximum divider.
@@ -133,6 +124,17 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)return 0 ; } +static unsigned int renesas_sdhi_clk_get_rate (struct tmio_mmc_host *host, +struct clk *clk) +{ +unsigned int freq =clk_get_rate (clk); + +if (host->pdata->flags &TMIO_MMC_INTERNAL_DIVIDER ) + freq /=2 ; + +return freq; +} +static unsigned int renesas_sdhi_clk_update (struct tmio_mmc_host *host,unsigned int wanted_clock) {
@@ -149,7 +151,7 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,* clock during tuning, so we don't change the external clock setup. */ if (!(host->pdata->flags &TMIO_MMC_MIN_RCAR2 ) ||mmc_doing_tune (host->mmc)) -return clk_get_rate (priv->clk); +return renesas_sdhi_clk_get_rate (host, priv->clk);if (priv->clkh) {/* HS400 with 4TAP needs different clock settings */
@@ -192,10 +194,15 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,clk_set_rate (ref_clk, best_freq); -if (priv->clkh) +if (priv->clkh) { +/* internal /2 divider is always active here */ +if (host->pdata->flags &TMIO_MMC_INTERNAL_DIVIDER ) + clkh_shift--; +clk_set_rate (priv->clk, best_freq >> clkh_shift); + } -return clk_get_rate (priv->clk); +return renesas_sdhi_clk_get_rate (host, priv->clk); } static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
@@ -1255,6 +1262,22 @@ int renesas_sdhi_probe(struct platform_device *pdev,if (ret)return ret; +/* +* The clock driver may not know the maximum working frequency, so it +* should come from the 'max-frequency' property. If it is missing, +* assume the current rate is the maximum. SoCs with an internal +* divider can't do that, because the current rate is not +* meaningful for them, so the property is mandatory there. +*/ +if (!host->mmc->f_max) { +if (mmc_data->flags &TMIO_MMC_INTERNAL_DIVIDER ) { + ret =dev_err_probe (dev, -EINVAL ,"max-frequency is required\n" ); + goto edisclk; + } + + host->mmc->f_max =clk_get_rate (priv->clk); + } + rcfg.of_node =of_get_available_child_by_name (dev->of_node,"vqmmc-regulator" );if (rcfg.of_node) { rcfg.driver_data = priv->host;
include/linux/platform_data/tmio.h +3 -0
@@ -53,6 +53,9 @@/* Some controllers have tuning delay */ #define TMIO_MMC_TUNING_DELAY BIT(13) +/* Some controllers have internal divider */ +#define TMIO_MMC_INTERNAL_DIVIDER BIT(14) +struct tmio_mmc_data {void *chan_priv_tx;void *chan_priv_rx;
Signature · 1 line
2.43.0
Re: [PATCH v25 08/11] mmc: renesas_sdhi: Add TMIO_MMC_INTERNAL_DIVIDER flag
Quoted 13 lines · Biju Das
From: Biju Das <[email protected]>
RZ/G3L has a fixed internal /2 clock divider active in all bus timing modes except HS400, where the divider is bypassed. Add a
TMIO_MMC_INTERNAL_DIVIDERflag to mark controllers with this divider.Add
renesas_sdhi_clk_get_rate()to fold the /2 division into the reported clock rate when the flag is set, and use it inrenesas_sdhi_clk_update()in place ofclk_get_rate(). HS400 is not yet supported in software for this SoC. Adjustclkh_shiftfor the internal divider case.
Quoted 4 lines
No existing platform sets this flag, so this is infrastructure only, with no functional change for current SoCs.
Signed-off-by Biju Das <[email protected]>
drivers/mmc/host/renesas_sdhi_core.c int renesas_sdhi_probe(struct platform_device *pdev, · line 1271
Quoted 7 lines
drivers/mmc/host/renesas_sdhi_core.c +7
-0+/* +* The clock driver may not know the maximum working frequency, so it +* should come from the 'max-frequency' property. If it is missing, +* assume the current rate is the maximum. SoCs with an internal +* divider can't do that, because the current rate is not +* meaningful for them, so the property is mandatory there. +*/
Good description!
PATCH v25 9/11 mmc: renesas_sdhi: Add optional axis/axim reset controls
In reply to Biju DasFrom: Biju Das <[email protected]>
The RZ/G3L SoC has axis/axim resets compared to other SoCs.
Add two optional reset controls, rstc_axis and rstc_axim, to the renesas_sdhi struct. Both are acquired at probe time using devm_reset_control_get_optional_exclusive_deasserted() with the "axis" and "axim" reset names respectively.
Include them alongside the existing rstc in bulk reset/assert/deassert
operations: triggered together in renesas_sdhi_reset(), and managed via reset_control_bulk_assert/deassert() in the suspend and resume paths, replacing the previous single-control calls.
Being optional, these resets are a no-op on platforms that do not provide them, so existing behaviour is preserved.
---v24->v25:
* No change.v23->v24:
* No change.v22->v23:
* No change.v21->v22:
* No change.v20->v21:
* No change.v19->v20:
* Fixed the ordering of resets in suspend/resume paths.V18->v19:
* No change.v17->v18:
* Fixed ordering of reset in renesas_sdhi_reset().v1->v17:
* No change.
--- drivers/mmc/host/renesas_sdhi.h | 2 ++ drivers/mmc/host/renesas_sdhi_core.c | 26 +++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-)
drivers/mmc/host/renesas_sdhi.h +2 -0
@@ -102,6 +102,8 @@ struct renesas_sdhi {unsigned int tap_set;struct reset_control *rstc; +struct reset_control *rstc_axis; +struct reset_control *rstc_axim;struct tmio_mmc_host *host;struct regulator_dev *rdev; };
drivers/mmc/host/renesas_sdhi_core.c +23 -3
@@ -632,6 +632,8 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve) sd_status =sd_ctrl_read32 (host,CTL_SD_STATUS );reset_control_reset (priv->rstc); +reset_control_reset (priv->rstc_axim); +reset_control_reset (priv->rstc_axis);/* Unknown why but without polling reset status, it will hang */ read_poll_timeout (reset_control_status, ret, ret ==0 ,1 ,100 , false, priv->rstc);
@@ -1151,6 +1153,14 @@ int renesas_sdhi_probe(struct platform_device *pdev,if (IS_ERR (priv->rstc))return PTR_ERR (priv->rstc); + priv->rstc_axim =devm_reset_control_get_optional_exclusive_deasserted (&pdev->dev,"axim" ); +if (IS_ERR (priv->rstc_axim)) +return PTR_ERR (priv->rstc_axim); + + priv->rstc_axis =devm_reset_control_get_optional_exclusive_deasserted (&pdev->dev,"axis" ); +if (IS_ERR (priv->rstc_axis)) +return PTR_ERR (priv->rstc_axis); + priv->pinctrl =devm_pinctrl_get (&pdev->dev);if (!IS_ERR (priv->pinctrl)) { priv->pins_default = pinctrl_lookup_state(priv->pinctrl,
@@ -1404,13 +1414,18 @@ int renesas_sdhi_suspend(struct device *dev) {struct tmio_mmc_host *host =dev_get_drvdata (dev);struct renesas_sdhi *priv =host_to_priv (host); +struct reset_control_bulk_data resets[] = { + { .rstc = priv->rstc_axis }, + { .rstc = priv->rstc_axim }, + { .rstc = priv->rstc }, + };int ret; ret =pm_runtime_force_suspend (dev);if (ret)return ret; - ret =reset_control_assert (priv->rstc); + ret =reset_control_bulk_assert (ARRAY_SIZE (resets), resets);if (ret)pm_runtime_force_resume (dev);
@@ -1422,15 +1437,20 @@ int renesas_sdhi_resume(struct device *dev) {struct tmio_mmc_host *host =dev_get_drvdata (dev);struct renesas_sdhi *priv =host_to_priv (host); +struct reset_control_bulk_data resets[] = { + { .rstc = priv->rstc_axis }, + { .rstc = priv->rstc_axim }, + { .rstc = priv->rstc }, + };int ret; - ret =reset_control_deassert (priv->rstc); + ret =reset_control_bulk_deassert (ARRAY_SIZE (resets), resets);if (ret)return ret; ret =pm_runtime_force_resume (dev);if (ret) -reset_control_assert (priv->rstc); +reset_control_bulk_assert (ARRAY_SIZE (resets), resets);return ret; }
Signature · 1 line
2.43.0
PATCH v25 10/11 mmc: renesas_sdhi: Add write32_hook for CTL_SD_CARD_CLK_CTL register writes
In reply to Biju DasFrom: Biju Das <[email protected]>
Add a write32_hook, analogous to the existing write16_hook, so that sd_ctrl_write32() can wait for the SD bus to become idle before certain 32-bit register writes.
renesas_sdhi_write32_hook() checks whether the target register is CTL_SD_CARD_CLK_CTL and, if so, waits for TMIO_STAT_SCLKDIVEN via renesas_sdhi_wait_idle() before the write proceeds. Other registers are unaffected. As with write16_hook, the write is skipped if the hook returns a non-zero error.
---v24->v25:
* Updated comit description.
* Updated tmio_mmc_host_probe() to set write32_hook() to NULL if TMIO_MMC_HAS_IDLE_WAIT is not defined.v23->v24:
* No change.
v23:
* New patch.
--- drivers/mmc/host/renesas_sdhi_core.c | 9 +++++++++ drivers/mmc/host/tmio_mmc.h | 7 +++++++ drivers/mmc/host/tmio_mmc_core.c | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-)
drivers/mmc/host/renesas_sdhi_core.c +9 -0
@@ -965,6 +965,14 @@ static int renesas_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)return 0 ; } +static int renesas_sdhi_write32_hook (struct tmio_mmc_host *host,int addr) +{ +if (addr ==CTL_SD_CARD_CLK_CTL ) +return renesas_sdhi_wait_idle (host,TMIO_STAT_SCLKDIVEN ); + +return 0 ; +} +static int renesas_sdhi_multi_io_quirk (struct mmc_card *card,unsigned int direction,int blk_size) {
@@ -1199,6 +1207,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, } host->write16_hook = renesas_sdhi_write16_hook; + host->write32_hook = renesas_sdhi_write32_hook; host->clk_enable = renesas_sdhi_clk_enable; host->clk_disable = renesas_sdhi_clk_disable; host->set_clock = renesas_sdhi_set_clock;
drivers/mmc/host/tmio_mmc.h +7 -0
@@ -189,6 +189,7 @@ struct tmio_mmc_host {int (*multi_io_quirk)(struct mmc_card *card,unsigned int direction,int blk_size);int (*write16_hook)(struct tmio_mmc_host *host,int addr); +int (*write32_hook)(struct tmio_mmc_host *host,int addr);void (*reset)(struct tmio_mmc_host *host,bool preserve);bool (*check_retune)(struct tmio_mmc_host *host,struct mmc_request *mrq);void (*fixup_request)(struct tmio_mmc_host *host,struct mmc_request *mrq);
@@ -283,6 +284,12 @@ static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host,static inline void sd_ctrl_write32 (struct tmio_mmc_host *host,int addr,u32 val) { +/* If there is a hook and it returns non-zero then there +* is an error and the write should be skipped +*/ +if (host->write32_hook && host->write32_hook (host, addr)) +return ; +iowrite32 (val, host->ctl + (addr << host->bus_shift)); }
drivers/mmc/host/tmio_mmc_core.c +3 -1
@@ -1169,8 +1169,10 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)if (mmc->f_min ==0 )return -EINVAL ; -if (!(pdata->flags &TMIO_MMC_HAS_IDLE_WAIT )) +if (!(pdata->flags &TMIO_MMC_HAS_IDLE_WAIT )) { _host->write16_hook =NULL ; + _host->write32_hook =NULL ; + }if (pdata->flags &TMIO_MMC_USE_BUSY_TIMEOUT && !_host->get_timeout_cycles) _host->get_timeout_cycles = tmio_mmc_get_timeout_cycles;
Signature · 1 line
2.43.0
Re: [PATCH v25 10/11] mmc: renesas_sdhi: Add write32_hook for CTL_SD_CARD_CLK_CTL register writes
Quoted 6 lines · Biju Das
From: Biju Das <[email protected]>
Add a
write32_hook, analogous to the existingwrite16_hook, so thatsd_ctrl_write32()can wait for the SD bus to become idle before certain 32-bit register writes.
Quoted 7 lines
renesas_sdhi_write32_hook()checks whether the target register isCTL_SD_CARD_CLK_CTLand, if so, waits forTMIO_STAT_SCLKDIVENviarenesas_sdhi_wait_idle()before the write proceeds. Other registers are unaffected. As withwrite16_hook, the write is skipped if the hook returns a non-zero error.Signed-off-by Biju Das <[email protected]>
PATCH v25 11/11 mmc: renesas_sdhi: Add RZ/G3L SDHI support
In reply to Biju DasFrom: Biju Das <[email protected]>
The RZ/G3L SDHI is close to existing variants but differs in three ways the driver can't handle today:
- It has an 11-bit clock divider. The upper bits live in CLK_CTL bits [17:16], so the register must be written with a 32-bit access on this SoC.
- Tuning needs a second HW adjustment register (HWADJ2), programmed together with TMPPORT2.
- HWADJ4 must be cleared when tuning starts.
---v24->v25:
* Updated commit description.
* Added macros RZG3L_SDHI_SCC_HWADJ2_{1_8V,3_3V}
* Replaced the quirks sdhi_quirks_rzg3l with sdhi_quirks_fixed_addr.
* Added helper renesas_sdhi_sclken() to handle 16-bit and 32-bit access.
* Updated renesas_sdhi_set_clock() with max_divider check for TMIO_MMC_INTERNAL_DIVIDER instead of SDHI_MAX_DIVIDER_DEFAULT for consistency with tmio_mmc_reset().v23->v24:
* Replaced magic bits GENMASK(9, 8) and GENMASK(17, 16) with RZG3L_CLK_CTL_DIV9_DIV8 and RZG3L_CLK_CTL_DIV9_DIV8_SRC.v22->v23:
* Updated tmio_mmc_reset() to handle 32 bit divider register in RZ/G3L.v21->v22:
* Updated commit description.
* Added clock &= ~GENMASK(9, 8) to clear the divider mask bits in the register value.v20->v21:
* Added clk_div_mask to RZ/G3L of_data.
* Restored actual_clk calculation in renesas_sdhi_set_clock().v19->v20:
* Added bitfield.h header file.
* Reworked on renesas_sdhi_set_clock() to handle the 11-bit divider case and the 32-bit register write.v18->v19:
* Update commit description.
* Updated handling for clk divider.
* Renamed TMIO_MMC_HWADJ2->TMIO_MMC_HWADJ to make it generic for hardware tuning delays
* Dropped duplicate SH_MOBILE_SDHI_SCC_TMPPORT2 macro
v18:
* New patch.
--- drivers/mmc/host/renesas_sdhi_core.c | 84 +++++++++++++++++++-------- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 34 +++++++++++ drivers/mmc/host/tmio_mmc_core.c | 16 ++++- include/linux/platform_data/tmio.h | 3 + 4 files changed, 110 insertions(+), 27 deletions(-)
drivers/mmc/host/renesas_sdhi_core.c +60 -24
@@ -18,6 +18,7 @@* */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/iopoll.h>
@@ -60,9 +61,13 @@#define SDHI_VER_GEN2_SDR104 0xcb0d#define SDHI_VER_GEN3_SD 0xcc10#define SDHI_VER_GEN3_SDMMC 0xcd10 +#define SDHI_VER_RZ_G3L_SDMMC 0xce10#define SDHI_GEN3_MMC0_ADDR 0xee140000 +#define RZG3L_CLK_CTL_DIV9_DIV8 GENMASK(17, 16) +#define RZG3L_CLK_CTL_DIV9_DIV8_SRC GENMASK(9, 8) +static void renesas_sdhi_sdbuf_width (struct tmio_mmc_host *host,int width) {u32 val;
@@ -80,6 +85,7 @@ static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)break ;case SDHI_VER_GEN3_SD :case SDHI_VER_GEN3_SDMMC : +case SDHI_VER_RZ_G3L_SDMMC :if (width ==64 ) val =HOST_MODE_GEN3_64BIT ;else if (width ==32 )
@@ -205,14 +211,28 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,return renesas_sdhi_clk_get_rate (host, priv->clk); } +static void renesas_sdhi_sclken (struct tmio_mmc_host *host,bool enable) +{ +if (host->pdata->flags &TMIO_MMC_INTERNAL_DIVIDER ) { +u32 val =sd_ctrl_read32 (host,CTL_SD_CARD_CLK_CTL ); + + val = enable ? val |CLK_CTL_SCLKEN : val & ~CLK_CTL_SCLKEN ; +sd_ctrl_write32 (host,CTL_SD_CARD_CLK_CTL , val); + }else { +u16 val =sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL ); + + val = enable ? val |CLK_CTL_SCLKEN : val & ~CLK_CTL_SCLKEN ; +sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , val); + } +} +static void renesas_sdhi_set_clock (struct tmio_mmc_host *host,unsigned int new_clock) {unsigned int clk_margin;u64 clk =0 , clock; -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , ~CLK_CTL_SCLKEN & -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, false);if (new_clock ==0 ) { host->mmc->actual_clock =0 ;
@@ -243,12 +263,21 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,if (clock !=SDHI_SD_CLK_CTL_DIV1 ) host->mmc->actual_clock /= (1 << (ffs (clock) +1 )); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , clock); +if (host->pdata->flags &TMIO_MMC_INTERNAL_DIVIDER ) { +u64 tmp; + + tmp =FIELD_GET (RZG3L_CLK_CTL_DIV9_DIV8_SRC , clk); + clock |=FIELD_PREP (RZG3L_CLK_CTL_DIV9_DIV8 , tmp); + clock &= ~RZG3L_CLK_CTL_DIV9_DIV8_SRC ; +sd_ctrl_write32 (host,CTL_SD_CARD_CLK_CTL , clock); + }else { +sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , clock); + } +if (!(host->pdata->flags &TMIO_MMC_MIN_RCAR2 ))usleep_range (10000 ,11000 ); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL ,CLK_CTL_SCLKEN | -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, true); out:/* HW engineers overrode docs: no sleep needed on R-Car2+ */
@@ -279,12 +308,14 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc)#define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008#define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A#define SH_MOBILE_SDHI_SCC_SMPCMP 0x00C -#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E +#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E/* G3L: SDm_SCC_HS400MODE1 */ +#define RZG3L_SDHI_SCC_HWADJ2 0x010#define SH_MOBILE_SDHI_SCC_TMPPORT3 0x014#define SH_MOBILE_SDHI_SCC_TMPPORT4 0x016#define SH_MOBILE_SDHI_SCC_TMPPORT5 0x018#define SH_MOBILE_SDHI_SCC_TMPPORT6 0x01A#define SH_MOBILE_SDHI_SCC_TMPPORT7 0x01C +#define RZG3L_SDHI_SCC_HWADJ4 0x022#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0)#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16
@@ -305,6 +336,9 @@ static int renesas_sdhi_card_busy(struct mmc_host *mmc)#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4)#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31) +#define RZG3L_SDHI_SCC_HWADJ2_1_8V 0xff +#define RZG3L_SDHI_SCC_HWADJ2_3_3V 0x3fff +/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT4 register */ #define SH_MOBILE_SDHI_SCC_TMPPORT4_DLL_ACC_START BIT(0)
@@ -335,14 +369,22 @@ static void renesas_sdhi_set_hw_adjustment_delay(struct tmio_mmc_host *host, bool force_clear) {struct renesas_sdhi *priv =host_to_priv (host); +bool hwadj2 = host->pdata->flags &TMIO_MMC_HWADJ ;if (!(host->pdata->flags &TMIO_MMC_TUNING_DELAY ))return ; -if (host->mmc->ios.signal_voltage ==MMC_SIGNAL_VOLTAGE_330 || force_clear) +if (host->mmc->ios.signal_voltage ==MMC_SIGNAL_VOLTAGE_330 || force_clear) {sd_scc_write32 (host, priv,SH_MOBILE_SDHI_SCC_TMPPORT2 ,0x0 ); -else +if (hwadj2) +sd_scc_write32 (host, priv,RZG3L_SDHI_SCC_HWADJ2 , +RZG3L_SDHI_SCC_HWADJ2_3_3V ); + }else {sd_scc_write32 (host, priv,SH_MOBILE_SDHI_SCC_TMPPORT2 ,0x1 ); +if (hwadj2) +sd_scc_write32 (host, priv,RZG3L_SDHI_SCC_HWADJ2 , +RZG3L_SDHI_SCC_HWADJ2_1_8V ); + } } static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
@@ -390,8 +432,7 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host)/* Initialize SCC */ sd_ctrl_write32_as_16_and_16 (host,CTL_STATUS ,0x0 ); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , ~CLK_CTL_SCLKEN & -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, false);/* set sampling clock selection range */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL,
@@ -409,9 +450,10 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host)sd_scc_write32 (host, priv,SH_MOBILE_SDHI_SCC_DT2FF , priv->scc_tappos);renesas_sdhi_set_hw_adjustment_delay (host, false); +if (host->pdata->flags &TMIO_MMC_HWADJ ) +sd_scc_write32 (host, priv,RZG3L_SDHI_SCC_HWADJ4 ,0x0 ); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL ,CLK_CTL_SCLKEN | -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, true);/* Read TAPNUM */ return (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL) >>
@@ -426,8 +468,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps :0 ;bool use_4tap =sdhi_has_quirk (priv, hs400_4taps); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , ~CLK_CTL_SCLKEN & -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, false);/* Set HS400 mode */ sd_ctrl_write16(host, CTL_SDIF_MODE, SDIF_MODE_HS400 |
@@ -473,8 +514,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL ,CLK_CTL_SCLKEN | -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, true);if (priv->adjust_hs400_calib_table) priv->needs_adjust_hs400 = true;
@@ -485,8 +525,7 @@ static void renesas_sdhi_disable_scc(struct mmc_host *mmc)struct tmio_mmc_host *host =mmc_priv (mmc);struct renesas_sdhi *priv =host_to_priv (host); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , ~CLK_CTL_SCLKEN & -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, false); sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL &
@@ -498,8 +537,7 @@ static void renesas_sdhi_disable_scc(struct mmc_host *mmc)sd_scc_read32 (host, priv, SH_MOBILE_SDHI_SCC_DTCNTL)); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL ,CLK_CTL_SCLKEN | -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, true); } static u32 sd_scc_tmpport_read32(struct tmio_mmc_host *host,
@@ -573,8 +611,7 @@ static void renesas_sdhi_adjust_hs400_mode_disable(struct tmio_mmc_host *host)static void renesas_sdhi_reset_hs400_mode (struct tmio_mmc_host *host,struct renesas_sdhi *priv) { -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , ~CLK_CTL_SCLKEN & -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, false);/* Reset HS400 mode */ sd_ctrl_write16(host, CTL_SDIF_MODE, ~SDIF_MODE_HS400 &
@@ -590,8 +627,7 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,if (sdhi_has_quirk (priv, hs400_calib_table) ||sdhi_has_quirk (priv, hs400_bad_taps))renesas_sdhi_adjust_hs400_mode_disable (host); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL ,CLK_CTL_SCLKEN | -sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL )); +renesas_sdhi_sclken (host, true); }static int renesas_sdhi_prepare_hs400_tuning (struct mmc_host *mmc,struct mmc_ios *ios)
drivers/mmc/host/renesas_sdhi_internal_dmac.c +34 -0
@@ -87,6 +87,13 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { }, }; +static struct renesas_sdhi_scc rzg3l_scc_taps[] = { + { + .clk_rate =0 , + .tap =0x00000300 , + }, +}; +static const struct renesas_sdhi_of_data of_data_rza2 = { .tmio_flags =TMIO_MMC_HAS_IDLE_WAIT |TMIO_MMC_CLK_ACTUAL |TMIO_MMC_HAVE_CBSY ,
@@ -152,6 +159,27 @@ static const struct renesas_sdhi_of_data of_data_rz_g2l = { .sdhi_flags = SDHI_FLAG_NEED_CLKH_FALLBACK, }; +static const struct renesas_sdhi_of_data of_data_rzg3l = { + .tmio_flags =TMIO_MMC_HAS_IDLE_WAIT |TMIO_MMC_CLK_ACTUAL | +TMIO_MMC_HAVE_CBSY |TMIO_MMC_MIN_RCAR2 | +TMIO_MMC_64BIT_DATA_PORT |TMIO_MMC_TUNING_DELAY | +TMIO_MMC_INTERNAL_DIVIDER |TMIO_MMC_HWADJ , + .capabilities =MMC_CAP_SD_HIGHSPEED |MMC_CAP_SDIO_IRQ | +MMC_CAP_CMD23 |MMC_CAP_WAIT_WHILE_BUSY , + .capabilities2 =MMC_CAP2_NO_WRITE_PROTECT |MMC_CAP2_MERGE_CAPABLE , + .bus_shift =2 , + .scc_offset =0x1000 , + .taps = rzg3l_scc_taps, + .taps_num =ARRAY_SIZE (rzg3l_scc_taps), +/* DMAC can handle 32bit blk count but only 1 segment */ + .max_blk_count =UINT_MAX /TMIO_MAX_BLK_SIZE , + .max_segs =1 , + .sdhi_flags =SDHI_FLAG_NEED_CLKH_FALLBACK , + .clk_mask =0x200000200 , + .max_divider =2048 , + .clk_div_mask =GENMASK (9 ,0 ), +}; +static const u8 r8a7796_es13_calib_table[2 ][SDHI_CALIB_TABLE_MAX ] = { {3 ,3 ,3 ,3 ,3 ,3 ,3 ,4 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,15 ,16 ,16 ,16 ,16 ,16 ,16 ,17 ,18 ,18 ,19 ,20 ,21 ,22 ,23 ,24 ,25 },
@@ -291,6 +319,11 @@ static const struct renesas_sdhi_of_data_with_quirks of_rza2_compatible = { .quirks = &sdhi_quirks_fixed_addr, }; +static const struct renesas_sdhi_of_data_with_quirks of_rzg3l_compatible = { + .of_data = &of_data_rzg3l, + .quirks = &sdhi_quirks_fixed_addr, +}; +static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible ="renesas,sdhi-r7s9210" , .data = &of_rza2_compatible, }, { .compatible ="renesas,sdhi-mmc-r8a77470" , .data = &of_rcar_gen3_compatible, },
@@ -304,6 +337,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, }, { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, }, { .compatible = "renesas,sdhi-r8a779md", .data = &of_rcar_gen3_nohs400_compatible, }, + { .compatible = "renesas,sdhi-r9a08g046", .data = &of_rzg3l_compatible, }, { .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,sdhi-r9a09g057", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, },
drivers/mmc/host/tmio_mmc_core.c +13 -3
@@ -180,11 +180,17 @@ static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,static void tmio_mmc_reset (struct tmio_mmc_host *host,bool preserve) { -u16 card_opt, clk_ctrl, sdif_mode; +u16 card_opt, sdif_mode; +u32 clk_ctrl; +bool wide_clk = host->pdata->flags &TMIO_MMC_INTERNAL_DIVIDER ;if (preserve) { card_opt =sd_ctrl_read16 (host,CTL_SD_MEM_CARD_OPT ); - clk_ctrl =sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL ); +if (wide_clk) + clk_ctrl =sd_ctrl_read32 (host,CTL_SD_CARD_CLK_CTL ); +else + clk_ctrl =sd_ctrl_read16 (host,CTL_SD_CARD_CLK_CTL ); +if (host->pdata->flags &TMIO_MMC_MIN_RCAR2 ) sdif_mode =sd_ctrl_read16 (host,CTL_SDIF_MODE ); }
@@ -216,7 +222,11 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve)if (preserve) {sd_ctrl_write16 (host,CTL_SD_MEM_CARD_OPT , card_opt); -sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , clk_ctrl); +if (wide_clk) +sd_ctrl_write32 (host,CTL_SD_CARD_CLK_CTL , clk_ctrl); +else +sd_ctrl_write16 (host,CTL_SD_CARD_CLK_CTL , clk_ctrl); +if (host->pdata->flags &TMIO_MMC_MIN_RCAR2 )sd_ctrl_write16 (host,CTL_SDIF_MODE , sdif_mode); }
include/linux/platform_data/tmio.h +3 -0
@@ -56,6 +56,9 @@/* Some controllers have internal divider */ #define TMIO_MMC_INTERNAL_DIVIDER BIT(14) +/* Some controllers have hw adjustment delay */ +#define TMIO_MMC_HWADJ BIT(15) +struct tmio_mmc_data {void *chan_priv_tx;void *chan_priv_rx;
Signature · 1 line
2.43.0
Re: [PATCH v25 11/11] mmc: renesas_sdhi: Add RZ/G3L SDHI support
Quoted 5 lines · Biju Das
From: Biju Das <[email protected]>
The RZ/G3L SDHI is close to existing variants but differs in three ways the driver can't handle today:
Quoted 8 lines
- It has an 11-bit clock divider. The upper bits live in
CLK_CTLbits [17:16], so the register must be written with a 32-bit access on this SoC.
- Tuning needs a second HW adjustment register (HWADJ2), programmed together with TMPPORT2.
- HWADJ4 must be cleared when tuning starts.Signed-off-by Biju Das <[email protected]>
I like the renesas_sdhi_sclken() helper.
All review done, congrats! I will run some tests on R-Car hardware in a few minutes.
Quoted 14 lines · Biju Das
Hi All,
The Renesas RZ/G3L SoC has:
Channel 0 supports SD and eMMC (including HS400/HS400ES). Channel 1 supports SD and eMMC (except for HS400). Channel 2 supports SD.
The SoC supports a maximum frequency of 150 MHz. The SD0 interface does not support IOVS and PWEN in the SDHI register (no internal regulator), unlike SD1 and SD2. It has an internal divider for all modes except HS400. It also has a 2048-bit divider compared to 512 on others. Moreover RZ/G3L supports HS400 enhanced strobe mode.
Quoted 5 lines
This patch series support all modes except HS400. HS400 support will be added later.
This patch series tested on smarc-rz{v2l,g2l,g2lc,g2ul,g3s,g3l,g3e}, rz{v2h,t2h}-evk and hihope-rz{g2h,g2m,g2n} boards
Tested with a Renesas Salvator-XS board (R-Car M3N). I don't see bugs or performance regressions. I think the series is good to go!
Thank you!
Quoted 48 lines · Biju Das
Hi All,
The Renesas RZ/G3L SoC has:
Channel 0 supports SD and eMMC (including HS400/HS400ES). Channel 1 supports SD and eMMC (except for HS400). Channel 2 supports SD.
The SoC supports a maximum frequency of 150 MHz. The SD0 interface does not support IOVS and PWEN in the SDHI register (no internal regulator), unlike SD1 and SD2. It has an internal divider for all modes except HS400. It also has a 2048-bit divider compared to 512 on others. Moreover RZ/G3L supports HS400 enhanced strobe mode.
This patch series support all modes except HS400. HS400 support will be added later.
This patch series tested on smarc-rz{v2l,g2l,g2lc,g2ul,g3s,g3l,g3e}, rz{v2h,t2h}-evk and hihope-rz{g2h,g2m,g2n} boards
v24->v25:
* Added max-frequency is a required property for SoCs with internal divider.
* Retained the tags as the change is trivial.
* Restored structof_default_cfg.
* Updated commit description for patch#3,#4,#6,#10 and #11
* Dropped initializingclk_maskas it uses the default value.
* Added suffix ULL to macroSDHI_CLK_MASK_DEFAULT.
* Fixed a very low or zero clock rate from the clock provider that causes an infinite loop inrenesas_sdhi_set_clockby max operator.
* Restored the data type ofmax_dividerfrom signed int to unsigned int.
* Dropped initializingmax_divideras it use default value.
* Updatedmmc->f_mincalculation withclk_round_rate().
* Updated the comment using "ilog2(max_divider)" instead of "{9,11}".
* Dropped initializingclk_div_maskas it use default value.
* Updatedrenesas_sdhi_set_hw_adjustment_delay()withforce_clearparameter
* Added a comment inrenesas_sdhi_execute_tuning()for tuning delay.
* In probe, if the SCC registers are not available, it clear the tuning delay flag.
* Dropped default masks from structof_data_rz_g2l.
* Droppedrenesas_sdhi_is_internal_divider_enabled().
* Added a comment about the internal divider inrenesas_sdhi_clk_update()and replaced 'clkh_shift= 1' with 'clkh_shift--'
* Movedmmc->f_maxtoprobe()and made it fail if max-frequency is not defined in DT for SoCs with an internal divider.
Quoted 8 lines
* Updated
tmio_mmc_host_probe()to setwrite32_hook()to NULL ifTMIO_MMC_HAS_IDLE_WAITis not defined.
* Added macros RZG3L_SDHI_SCC_HWADJ2_{1_8V,3_3V}
* Replaced the quirkssdhi_quirks_rzg3lwithsdhi_quirks_fixed_addr.
* Added helperrenesas_sdhi_sclken()to handle 16-bit and 32-bit access.
* Updatedrenesas_sdhi_set_clock()withmax_dividercheck forTMIO_MMC_INTERNAL_DIVIDERinstead ofSDHI_MAX_DIVIDER_DEFAULTfor consistency withtmio_mmc_reset().
The v25 series applied for next, thanks!
[...]
Kind regards
Uffe