[RFC 0/2] dw_mmc: add multislot support
This series consists of two patches: 1. revert removal of previously existed "pseudo-multislot" support. * Revert "mmc: dw_mmc: remove the deprecated "num-slots"" * Revert "mmc: dw_mmc: fix the wrong condition check of getting num-slots from DT" * Revert "mmc: dw_mmc: remove the unnecessary slot variable" * Revert "mmc: dw_mmc: update kernel-doc comments for dw_mci" * Revert "mmc: dw_mmc: use the 'slot' instead of 'cur_slot'" * Revert "mmc: dw_mmc: remove the 'id' arguments about functions relevant to slot" * Revert "mmc: dw_mmc: change the array of slots" * Revert "mmc: dw_mmc: remove the loop about finding slots" * Revert "mmc: dw_mmc: deprecated the "num-slots" property" 2. Add missing stuff to support multislot mode in DesignWare MMC driver. * Add missing slot switch to __dw_mci_start_request() function. * Refactor set_ios function: a) Calculate common clock which is suitable for all slots instead of directly use clock value provided by mmc core. We calculate common clock as the minimum among each used slot clocks. This clock is calculated in dw_mci_calc_common_clock() function which is called from set_ios() b) Disable clock only if no other slots are ON. c) Setup clock directly in set_ios() only if no other slots are ON. Otherwise adjust clock in __dw_mci_start_request() function before slot switch. d) Move timings and bus_width setup to separate funcions. * Use timing field in each slot structure instead of common field in host structure. * Add locks to serialize access to registers. NOTE: this patch is based off of v4.17-rc1 NOTE: as of today I tested this changes (in singleslot and multislot modes) only on Synopsys HSDK board. But I will get ODROID-XU4 board (with Exynos5422 which has DW MMC controller) the next week so I will test it on this board too to catch any regressions. Eugeniy Paltsev (2): dw_mmc: revert removal multislot support dw_mmc: add multislot support .../devicetree/bindings/mmc/synopsys-dw-mshc.txt | 5 + drivers/mmc/host/dw_mmc-exynos.c | 4 +- drivers/mmc/host/dw_mmc-pci.c | 1 + drivers/mmc/host/dw_mmc.c | 486 +++-- drivers/mmc/host/dw_mmc.h | 35 +- 5 files changed, 387 insertions(+), 144 deletions(-) -- 2.14.3 ___ linux-snps-arc mailing list linux-snps-arc@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-snps-arc
[RFC 1/2] dw_mmc: revert removal multislot support
Revert "mmc: dw_mmc: remove the deprecated "num-slots"" Revert "mmc: dw_mmc: fix the wrong condition check of getting num-slots from DT" Revert "mmc: dw_mmc: remove the unnecessary slot variable" Revert "mmc: dw_mmc: update kernel-doc comments for dw_mci" Revert "mmc: dw_mmc: use the 'slot' instead of 'cur_slot'" Revert "mmc: dw_mmc: remove the 'id' arguments about functions relevant to slot" Revert "mmc: dw_mmc: change the array of slots" Revert "mmc: dw_mmc: remove the loop about finding slots" Revert "mmc: dw_mmc: deprecated the "num-slots" property" --- .../devicetree/bindings/mmc/synopsys-dw-mshc.txt | 5 + drivers/mmc/host/dw_mmc-exynos.c | 4 +- drivers/mmc/host/dw_mmc-pci.c | 1 + drivers/mmc/host/dw_mmc.c | 167 ++--- drivers/mmc/host/dw_mmc.h | 21 ++- 5 files changed, 137 insertions(+), 61 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt index 7e5e427a22ce..75c9fdca4aaf 100644 --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt @@ -59,6 +59,11 @@ Optional properties: is specified and the ciu clock is specified then we'll try to set the ciu clock to this at probe time. +* num-slots (DEPRECATED): specifies the number of slots supported by the controller. + The number of physical slots actually used could be equal or less than the + value specified by num-slots. If this property is not specified, the value + of num-slot property is assumed to be 1. + * fifo-depth: The maximum size of the tx/rx fifo's. If this property is not specified, the default value of the fifo size is determined from the controller registers. diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index a84aa3f1ae85..6de892443207 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -157,8 +157,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) * HOLD register should be bypassed in case there is no phase shift * applied on CMD/DATA that is sent to the card. */ - if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot) - set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags); + if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->cur_slot) + set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags); } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 3ad07d7b2c97..ab8713297edb 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -29,6 +29,7 @@ MMC_CAP_SDIO_IRQ) static struct dw_mci_board pci_board_data = { + .num_slots = 1, .caps = DW_MCI_CAPABILITIES, .bus_hz = 33 * 1000 * 1000, .detect_delay_ms= 200, diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 29a1afa81f66..f8b1e3528e99 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -372,7 +372,7 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) cmdr = stop->opcode | SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; - if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags)) cmdr |= SDMMC_CMD_USE_HOLD_REG; return cmdr; @@ -502,7 +502,7 @@ static void dw_mci_dmac_complete_dma(void *arg) if ((host->use_dma == TRANS_MODE_EDMAC) && data && (data->flags & MMC_DATA_READ)) /* Invalidate cache after read */ - dma_sync_sg_for_cpu(mmc_dev(host->slot->mmc), + dma_sync_sg_for_cpu(mmc_dev(host->cur_slot->mmc), data->sg, data->sg_len, DMA_FROM_DEVICE); @@ -844,7 +844,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host, /* Flush cache before write */ if (host->data->flags & MMC_DATA_WRITE) - dma_sync_sg_for_device(mmc_dev(host->slot->mmc), sgl, + dma_sync_sg_for_device(mmc_dev(host->cur_slot->mmc), sgl, sg_elems, DMA_TO_DEVICE); dma_async_issue_pending(host->dms->ch); @@ -1306,6 +1306,7 @@ static void __dw_mci_start_request(struct dw_mci *host, mrq = slot->mrq; + host->cur_slot = slot; host->mrq = mrq; host->pending_events = 0; @@ -1786,7 +1787,7 @@ static bool dw_mci_reset(struct dw_mci *host) ciu_out: /* After a CTRL reset we need to have CIU set clock registers */ - mci_send_cmd(ho
[RFC 2/2] dw_mmc: add multislot support
This patch adds missing stuff to support multislot mode in DesignWare MMC driver. The main changes: * Add missing slot switch to __dw_mci_start_request() function. * Refactor set_ios function: a) Calculate common clock which is suitable for all slots instead of directly use clock value provided by mmc core. We calculate common clock as the minimum among each used slot clocks. This clock is calculated in dw_mci_calc_common_clock() function which is called from set_ios() b) Disable clock only if no other slots are ON. c) Setup clock directly in set_ios() only if no other slots are ON. Otherwise adjust clock in __dw_mci_start_request() function before slot switch. d) Move timings and bus_width setup to separate funcions. * Use timing field in each slot structure instead of common field in host structure. * Add locks to serialize access to registers. NOTE: this patch is based off of v4.17-rc1 NOTE: as of today I tested this changes (in singleslot and multislot modes) only on Synopsys HSDK board. But I will get ODROID-XU4 board (with Exynos5422 which has DW MMC controller) the next week so I will test it on this board too to catch any regressions. Signed-off-by: Eugeniy Paltsev --- drivers/mmc/host/dw_mmc.c | 325 ++ drivers/mmc/host/dw_mmc.h | 14 +- 2 files changed, 253 insertions(+), 86 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index f8b1e3528e99..e0d30f56cc59 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -261,7 +261,8 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) mci_writel(host, CMDARG, arg); wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd); - mci_writel(host, CMD, SDMMC_CMD_START | cmd); + mci_writel(host, CMD, SDMMC_CMD_START | cmd | + (slot->id << SDMMC_CMD_CARD_NUM_OFFSET)); if (readl_poll_timeout_atomic(host->regs + SDMMC_CMD, cmd_status, !(cmd_status & SDMMC_CMD_START), @@ -428,7 +429,8 @@ static void dw_mci_start_command(struct dw_mci *host, wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd_flags); - mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); + mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START | + (host->cur_slot->id << SDMMC_CMD_CARD_NUM_OFFSET)); /* response expected command only */ if (cmd_flags & SDMMC_CMD_RESP_EXP) @@ -1065,7 +1067,7 @@ static void dw_mci_ctrl_thld(struct dw_mci *host, struct mmc_data *data) * It's used when HS400 mode is enabled. */ if (data->flags & MMC_DATA_WRITE && - !(host->timing != MMC_TIMING_MMC_HS400)) + !(host->cur_slot->timing != MMC_TIMING_MMC_HS400)) return; if (data->flags & MMC_DATA_WRITE) @@ -1073,8 +1075,8 @@ static void dw_mci_ctrl_thld(struct dw_mci *host, struct mmc_data *data) else enable = SDMMC_CARD_RD_THR_EN; - if (host->timing != MMC_TIMING_MMC_HS200 && - host->timing != MMC_TIMING_UHS_SDR104) + if (host->cur_slot->timing != MMC_TIMING_MMC_HS200 && + host->cur_slot->timing != MMC_TIMING_UHS_SDR104) goto disable; blksz_depth = blksz / (1 << host->data_shift); @@ -1218,13 +1220,45 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) } } -static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) +/* must be called in the locked context */ +static void dw_mci_setup_clock_off(struct dw_mci_slot *slot) +{ + u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT | +(slot->id << SDMMC_CMD_CARD_NUM_OFFSET); + + if (!slot->host->new_clk_speed) { + mci_writel(slot->host, CLKENA, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); + slot->host->current_speed = 0; + } +} + +static u32 dw_mci_calc_clock_div(struct dw_mci *host) +{ + unsigned int clock = host->new_clk_speed; + u32 bus_hz = host->bus_hz; + u32 div; + + div = bus_hz / clock; + if (bus_hz % clock && bus_hz > clock) + /* +* move the + 1 after the divide to prevent +* over-clocking the card. +*/ + div += 1; + + return (bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; +} + +/* must be called in the locked context */ +static void dw_mci_setup_clock(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot->host; - unsigned int clock = slot->clock; + unsigned int clock = host->new_clk_speed; u32 div; u32 clk_en_a; - u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; + u32 sdmmc_cmd_bits = SDMMC_CMD
[PATCH] clocksource: arc_timer: add comments about locking while read GFRC
This came to light in some internal discussions and it is nice to have this documented rather than digging up the PRM (Prog Ref Manual) again. Signed-off-by: Eugeniy Paltsev --- drivers/clocksource/arc_timer.c | 13 + 1 file changed, 13 insertions(+) diff --git a/drivers/clocksource/arc_timer.c b/drivers/clocksource/arc_timer.c index 4927355f9cbe..b594c373debc 100644 --- a/drivers/clocksource/arc_timer.c +++ b/drivers/clocksource/arc_timer.c @@ -61,6 +61,19 @@ static u64 arc_read_gfrc(struct clocksource *cs) unsigned long flags; u32 l, h; + /* +* MCIP_CMD/MCIP_READBACK registers are allocated +* PER ARC CORE (not per cluster), and there are dedicated hardware +* decode logic (per core) inside ARConnect to handle simultaneous +* read/write accesses from cores via those two registers. +* So several concurrent commands to ARConnect are OK if they are +* trying to access two different sub-components (like GFRC, +* inter-core interrupt, etc...). HW also support simultaneously +* accessing GFRC by multiple cores. +* That's why it is safe to disable hard interrupts on the local CPU +* before access to GFRC instead of taking global MCIP spinlock +* defined in arch/arc/kernel/mcip.c +*/ local_irq_save(flags); __mcip_cmd(CMD_GFRC_READ_LO, 0); -- 2.14.3 ___ linux-snps-arc mailing list linux-snps-arc@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-snps-arc
Re: [PATCH] clocksource: arc_timer: add comments about locking while read GFRC
On 04/17/2018 08:52 AM, Eugeniy Paltsev wrote: This came to light in some internal discussions and it is nice to have this documented rather than digging up the PRM (Prog Ref Manual) again. Signed-off-by: Eugeniy Paltsev Minor nits below, otherwise LGTM ! Acked-by: Vineet Gupta --- drivers/clocksource/arc_timer.c | 13 + 1 file changed, 13 insertions(+) diff --git a/drivers/clocksource/arc_timer.c b/drivers/clocksource/arc_timer.c index 4927355f9cbe..b594c373debc 100644 --- a/drivers/clocksource/arc_timer.c +++ b/drivers/clocksource/arc_timer.c @@ -61,6 +61,19 @@ static u64 arc_read_gfrc(struct clocksource *cs) unsigned long flags; u32 l, h; + /* +* MCIP_CMD/MCIP_READBACK registers are allocated From a programming model pov, there seems to be just one instance of MCIP_CMD/MCIP_READBACK however micro-architecturally there's an instance PER ARC CORE. +* PER ARC CORE (not per cluster), and there are dedicated hardware +* decode logic (per core) inside ARConnect to handle simultaneous +* read/write accesses from cores via those two registers. +* So several concurrent commands to ARConnect are OK if they are +* trying to access two different sub-components (like GFRC, +* inter-core interrupt, etc...). HW also support simultaneously s/support/supports/ +* accessing GFRC by multiple cores. +* That's why it is safe to disable hard interrupts on the local CPU +* before access to GFRC instead of taking global MCIP spinlock +* defined in arch/arc/kernel/mcip.c +*/ local_irq_save(flags); __mcip_cmd(CMD_GFRC_READ_LO, 0); ___ linux-snps-arc mailing list linux-snps-arc@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-snps-arc