Rework sdmmc clocking configuration

Use 204 MHz as host clock in SDR104 mode instead of 136 MHz.
Due to this, also change the frequency init divider so the
initialization frequency is below 400 kHz.
This makes the clocks for SDMMC1 in all modes to match the TRM table.

Make it clear in the code that HS200/HS400 modes in fact use PLLP_OUT0
and not PLLC4_OUT2_LJ like the comment suggest. In fact selecting the
PLLC4_OUT2_LJ as clock source results in freeze after switching to
HS200/HS400 mode. This is most likely related to the PLLC4 not being
enabled, but it should be checked later.

Set the HS200/HS400 divider to 3, as this is what the code really did
set prior to this change - so this commit does not change that.

Configure Legacy 12 MHz clock to run at 12 MHz using the SW default
configuration (as per TRM) for the SDMMC legacy timer.

Introduce initial version of sdmmc_host_clock_delay() in order to use it
in places where the wait is host clock dependent. The way it is
implemented now does not change the sleep that was used instead.
This commit is contained in:
Tomasz Moń 2018-06-12 17:20:15 +02:00
parent d8c9399cff
commit 206c10f333
6 changed files with 138 additions and 72 deletions

View file

@ -49,7 +49,10 @@ enum {
*/ */
enum { enum {
CLK_SOURCE_MASK = (0b111 << 29), CLK_SOURCE_MASK = (0b111 << 29),
CLK_SOURCE_FIRST = (0b000 << 29), CLK_SOURCE_SDMMC1_PLLP_OUT0 = (0b000 << 29), /* Fixed 408 MHz */
CLK_SOURCE_SDMMC4_PLLP_OUT0 = (0b000 << 29), /* Fixed 408 MHz */
CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ = (0b001 << 29), /* 199.68 MHz */
CLK_SOURCE_SDMMC_LEGACY_PLLP_OUT0 = (0b100 << 29), /* Fixed 408 MHz */
CLK_DIVIDER_MASK = (0xff << 0), CLK_DIVIDER_MASK = (0xff << 0),
CLK_DIVIDER_UNITY = (0x00 << 0), CLK_DIVIDER_UNITY = (0x00 << 0),

View file

@ -142,34 +142,52 @@ enum sdmmc_clock_dividers {
MMC_CLOCK_DIVIDER_SDR12 = 31, // 16.5, from the TRM table MMC_CLOCK_DIVIDER_SDR12 = 31, // 16.5, from the TRM table
MMC_CLOCK_DIVIDER_SDR25 = 15, // 8.5, from the table MMC_CLOCK_DIVIDER_SDR25 = 15, // 8.5, from the table
MMC_CLOCK_DIVIDER_SDR50 = 7, // 4.5, from the table MMC_CLOCK_DIVIDER_SDR50 = 7, // 4.5, from the table
MMC_CLOCK_DIVIDER_SDR104 = 4, // 2, from the datasheet MMC_CLOCK_DIVIDER_SDR104 = 2, // 2, from the table
/* Clock dividers: MMC */ /* Clock dividers: MMC */
MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table
MMC_CLOCK_DIVIDER_HS52 = 14, // 8, from the table MMC_CLOCK_DIVIDER_HS52 = 14, // 8, from the table
MMC_CLOCK_DIVIDER_HS200 = 2, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ #if 0
MMC_CLOCK_DIVIDER_HS400 = 2, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ // TODO: Figure out why PLLC4_OUT2_LJ doesn't work, most likely need to be enabled in hwinit
}; MMC_CLOCK_DIVIDER_HS200 = 0, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ
MMC_CLOCK_DIVIDER_HS400 = 0, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ
#else
MMC_CLOCK_DIVIDER_HS200 = 3,
MMC_CLOCK_DIVIDER_HS400 = 3,
#endif
/* Clock dividers: Legacy 12 MHz timer */
MMC_CLOCK_DIVIDER_LEGACY = 66, // 34 - to get 12 MHz out of 408 MHz
};
/** /**
* SDMMC clock divider constants * SDMMC clock divider constants
*/ */
enum sdmmc_clock_sources { enum sdmmc_clock_sources {
/* Clock dividers: SD */ /* Clock sources: SD */
MMC_CLOCK_SOURCE_SDR12 = 0, // PLLP MMC_CLOCK_SOURCE_SDR12 = CLK_SOURCE_SDMMC1_PLLP_OUT0, // PLLP
MMC_CLOCK_SOURCE_SDR25 = 0, MMC_CLOCK_SOURCE_SDR25 = CLK_SOURCE_SDMMC1_PLLP_OUT0,
MMC_CLOCK_SOURCE_SDR50 = 0, MMC_CLOCK_SOURCE_SDR50 = CLK_SOURCE_SDMMC1_PLLP_OUT0,
MMC_CLOCK_SOURCE_SDR104 = 0, MMC_CLOCK_SOURCE_SDR104 = CLK_SOURCE_SDMMC1_PLLP_OUT0,
/* Clock dividers: MMC */ /* Clock sources: MMC */
MMC_CLOCK_SOURCE_HS26 = 0, // PLLP MMC_CLOCK_SOURCE_HS26 = CLK_SOURCE_SDMMC4_PLLP_OUT0, // PLLP
MMC_CLOCK_SOURCE_HS52 = 0, MMC_CLOCK_SOURCE_HS52 = CLK_SOURCE_SDMMC4_PLLP_OUT0,
MMC_CLOCK_SOURCE_HS200 = 1, // PLLC4_OUT2_LJ
MMC_CLOCK_SOURCE_HS400 = 1,
#if 0
// TODO: Figure out why PLLC4_OUT2_LJ doesn't work, most likely need to be enabled in hwinit
MMC_CLOCK_SOURCE_HS200 = CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ, // PLLC4_OUT2_LJ
MMC_CLOCK_SOURCE_HS400 = CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ,
#else
// For the time being, use PLLP_OUT0
MMC_CLOCK_SOURCE_HS200 = CLK_SOURCE_SDMMC4_PLLP_OUT0,
MMC_CLOCK_SOURCE_HS400 = CLK_SOURCE_SDMMC4_PLLP_OUT0,
#endif
/* Clock sources: Legacy 12 MHz timer */
MMC_CLOCK_SOURCE_LEGACY = CLK_SOURCE_SDMMC_LEGACY_PLLP_OUT0,
}; };
/** /**
@ -240,7 +258,7 @@ enum sdmmc_register_bits {
MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2), MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2),
MMC_CLOCK_CONTROL_FREQUENCY_MASK = (0x3FF << 6), MMC_CLOCK_CONTROL_FREQUENCY_MASK = (0x3FF << 6),
MMC_CLOCK_CONTROL_FREQUENCY_SHIFT = 8, MMC_CLOCK_CONTROL_FREQUENCY_SHIFT = 8,
MMC_CLOCK_CONTROL_FREQUENCY_INIT = 0x18, // generates 400kHz from the TRM dividers MMC_CLOCK_CONTROL_FREQUENCY_INIT = 0x1F, // generates 400kHz from the TRM dividers
MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH = 0x00, // passes through the CAR clock unmodified MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH = 0x00, // passes through the CAR clock unmodified
/* Host control */ /* Host control */
@ -796,6 +814,20 @@ static int sdmmc_hardware_reset(struct mmc *mmc, uint32_t reset_flags)
return 0; return 0;
} }
/**
* Delays for a given amount of host clock cycles.
*
* @param mmc The MMC controller whose clock cycles should be waited upon.
* @param clocks The number of clock cycles to wait.
*/
static void sdmmc_host_clock_delay(struct mmc *mmc, unsigned int clocks)
{
// For the time being simply wait for clocks * 50 us
// This covers clocks as slow as 20 kHz and hence should always be safe
// TODO: determine the actual wait time based on clock source and divider
udelay(50 * clocks);
}
/** /**
* Performs low-level initialization for SDMMC4, used for the eMMC. * Performs low-level initialization for SDMMC4, used for the eMMC.
*/ */
@ -803,23 +835,22 @@ static int sdmmc4_set_up_clock_and_io(struct mmc *mmc)
{ {
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
volatile struct tegra_padctl *padctl = padctl_get_regs(); volatile struct tegra_padctl *padctl = padctl_get_regs();
(void)mmc;
// Put SDMMC4 in reset // Put SDMMC4 in reset
car->rst_dev_l_set |= 0x8000; car->rst_dev_l_set |= 0x8000;
// Configure the clock to place the device into the initial mode. // Configure the clock to place the device into the initial mode.
car->clk_src[CLK_SOURCE_SDMMC4] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src[CLK_SOURCE_SDMMC4] = MMC_CLOCK_SOURCE_SDR12 | MMC_CLOCK_DIVIDER_SDR12;
// Set the legacy divier used for detecting timeouts. // Set the legacy divier used for detecting timeouts.
car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = MMC_CLOCK_SOURCE_LEGACY | MMC_CLOCK_DIVIDER_LEGACY;
// Set SDMMC4 clock enable // Set SDMMC4 clock enable
car->clk_enb_l_set |= 0x8000; car->clk_enb_l_set |= 0x8000;
car->clk_enb_y_set |= CAR_CONTROL_SDMMC_LEGACY; car->clk_enb_y_set |= CAR_CONTROL_SDMMC_LEGACY;
// host_clk_delay(0x64, clk_freq) -> Delay 100 host clock cycles // Delay 100 host clock cycles
udelay(5000); sdmmc_host_clock_delay(mmc, 100);
// Take SDMMC4 out of reset // Take SDMMC4 out of reset
car->rst_dev_l_clr |= 0x8000; car->rst_dev_l_clr |= 0x8000;
@ -1102,11 +1133,11 @@ static int sdmmc_always_fail(struct mmc *mmc)
* a divider of N results in a clock that's (N/2) + 1 slower. * a divider of N results in a clock that's (N/2) + 1 slower.
* @param sdmmc_divisor An additional divisor applied in the SDMMC controller. * @param sdmmc_divisor An additional divisor applied in the SDMMC controller.
*/ */
static void sdmmc4_configure_clock(struct mmc *mmc, int source, int car_divisor, int sdmmc_divisor) static void sdmmc4_configure_clock(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor)
{ {
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
// Set up the CAR aspect of the clock, and wait 2uS per change per the TRM. // Set up the CAR aspect of the clock, and wait 2us per change per the TRM.
car->clk_enb_l_clr = CAR_CONTROL_SDMMC4; car->clk_enb_l_clr = CAR_CONTROL_SDMMC4;
car->clk_src[CLK_SOURCE_SDMMC4] = source | car_divisor; car->clk_src[CLK_SOURCE_SDMMC4] = source | car_divisor;
udelay(2); udelay(2);
@ -1128,11 +1159,11 @@ static void sdmmc4_configure_clock(struct mmc *mmc, int source, int car_divisor,
* a divider of N results in a clock that's (N/2) + 1 slower. * a divider of N results in a clock that's (N/2) + 1 slower.
* @param sdmmc_divisor An additional divisor applied in the SDMMC controller. * @param sdmmc_divisor An additional divisor applied in the SDMMC controller.
*/ */
static void sdmmc1_configure_clock(struct mmc *mmc, int source, int car_divisor, int sdmmc_divisor) static void sdmmc1_configure_clock(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor)
{ {
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
// Set up the CAR aspect of the clock, and wait 2uS per change per the TRM. // Set up the CAR aspect of the clock, and wait 2us per change per the TRM.
car->clk_enb_l_clr = CAR_CONTROL_SDMMC1; car->clk_enb_l_clr = CAR_CONTROL_SDMMC1;
car->clk_src[CLK_SOURCE_SDMMC1] = source | car_divisor; car->clk_src[CLK_SOURCE_SDMMC1] = source | car_divisor;
udelay(2); udelay(2);
@ -1469,7 +1500,6 @@ static int sdmmc1_set_up_clock_and_io(struct mmc *mmc)
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
volatile struct tegra_pinmux *pinmux = pinmux_get_regs(); volatile struct tegra_pinmux *pinmux = pinmux_get_regs();
volatile struct tegra_padctl *padctl = padctl_get_regs(); volatile struct tegra_padctl *padctl = padctl_get_regs();
(void)mmc;
// Set up each of the relevant pins to be connected to output drivers, // Set up each of the relevant pins to be connected to output drivers,
// and selected for SDMMC use. // and selected for SDMMC use.
@ -1493,19 +1523,19 @@ static int sdmmc1_set_up_clock_and_io(struct mmc *mmc)
car->rst_dev_l_set = CAR_CONTROL_SDMMC1; car->rst_dev_l_set = CAR_CONTROL_SDMMC1;
// Configure the clock to place the device into the initial mode. // Configure the clock to place the device into the initial mode.
car->clk_src[CLK_SOURCE_SDMMC1] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src[CLK_SOURCE_SDMMC1] = MMC_CLOCK_SOURCE_SDR12 | MMC_CLOCK_DIVIDER_SDR12;
// Set the legacy divier used for detecting timeouts. // Set the legacy divier used for detecting timeouts.
car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = MMC_CLOCK_SOURCE_LEGACY | MMC_CLOCK_DIVIDER_LEGACY;
// Set SDMMC1 clock enable // Set SDMMC1 clock enable
car->clk_enb_l_set = CAR_CONTROL_SDMMC1; car->clk_enb_l_set = CAR_CONTROL_SDMMC1;
car->clk_enb_y_set = CAR_CONTROL_SDMMC_LEGACY; car->clk_enb_y_set = CAR_CONTROL_SDMMC_LEGACY;
// host_clk_delay(0x64, clk_freq) -> Delay 100 host clock cycles // Delay 100 host clock cycles
udelay(5000); sdmmc_host_clock_delay(mmc, 100);
// Take SDMMC4 out of reset // Take SDMMC1 out of reset
car->rst_dev_l_clr |= CAR_CONTROL_SDMMC1; car->rst_dev_l_clr |= CAR_CONTROL_SDMMC1;
// Enable clock loopback. // Enable clock loopback.
@ -3443,7 +3473,7 @@ int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller, bool allow_vol
return rc; return rc;
} }
// Default to a timeout of 1S. // Default to a timeout of 1s.
mmc->timeout = 1000000; mmc->timeout = 1000000;
mmc->partition_switch_time = 1000; mmc->partition_switch_time = 1000;

View file

@ -157,7 +157,7 @@ struct mmc {
/* Per-controller operations. */ /* Per-controller operations. */
int (*set_up_clock_and_io)(struct mmc *mmc); int (*set_up_clock_and_io)(struct mmc *mmc);
void (*configure_clock)(struct mmc *mmc, int source, int car_divisor, int sdmmc_divisor); void (*configure_clock)(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor);
int (*enable_supplies)(struct mmc *mmc); int (*enable_supplies)(struct mmc *mmc);
int (*switch_to_low_voltage)(struct mmc *mmc); int (*switch_to_low_voltage)(struct mmc *mmc);
bool (*card_present)(struct mmc *mmc); bool (*card_present)(struct mmc *mmc);

View file

@ -49,7 +49,10 @@ enum {
*/ */
enum { enum {
CLK_SOURCE_MASK = (0b111 << 29), CLK_SOURCE_MASK = (0b111 << 29),
CLK_SOURCE_FIRST = (0b000 << 29), CLK_SOURCE_SDMMC1_PLLP_OUT0 = (0b000 << 29), /* Fixed 408 MHz */
CLK_SOURCE_SDMMC4_PLLP_OUT0 = (0b000 << 29), /* Fixed 408 MHz */
CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ = (0b001 << 29), /* 199.68 MHz */
CLK_SOURCE_SDMMC_LEGACY_PLLP_OUT0 = (0b100 << 29), /* Fixed 408 MHz */
CLK_DIVIDER_MASK = (0xff << 0), CLK_DIVIDER_MASK = (0xff << 0),
CLK_DIVIDER_UNITY = (0x00 << 0), CLK_DIVIDER_UNITY = (0x00 << 0),

View file

@ -141,34 +141,52 @@ enum sdmmc_clock_dividers {
MMC_CLOCK_DIVIDER_SDR12 = 31, // 16.5, from the TRM table MMC_CLOCK_DIVIDER_SDR12 = 31, // 16.5, from the TRM table
MMC_CLOCK_DIVIDER_SDR25 = 15, // 8.5, from the table MMC_CLOCK_DIVIDER_SDR25 = 15, // 8.5, from the table
MMC_CLOCK_DIVIDER_SDR50 = 7, // 4.5, from the table MMC_CLOCK_DIVIDER_SDR50 = 7, // 4.5, from the table
MMC_CLOCK_DIVIDER_SDR104 = 4, // 2, from the datasheet MMC_CLOCK_DIVIDER_SDR104 = 2, // 2, from the table
/* Clock dividers: MMC */ /* Clock dividers: MMC */
MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table
MMC_CLOCK_DIVIDER_HS52 = 14, // 8, from the table MMC_CLOCK_DIVIDER_HS52 = 14, // 8, from the table
MMC_CLOCK_DIVIDER_HS200 = 2, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ #if 0
MMC_CLOCK_DIVIDER_HS400 = 2, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ // TODO: Figure out why PLLC4_OUT2_LJ doesn't work, most likely need to be enabled in hwinit
}; MMC_CLOCK_DIVIDER_HS200 = 0, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ
MMC_CLOCK_DIVIDER_HS400 = 0, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ
#else
MMC_CLOCK_DIVIDER_HS200 = 3,
MMC_CLOCK_DIVIDER_HS400 = 3,
#endif
/* Clock dividers: Legacy 12 MHz timer */
MMC_CLOCK_DIVIDER_LEGACY = 66, // 34 - to get 12 MHz out of 408 MHz
};
/** /**
* SDMMC clock divider constants * SDMMC clock divider constants
*/ */
enum sdmmc_clock_sources { enum sdmmc_clock_sources {
/* Clock dividers: SD */ /* Clock sources: SD */
MMC_CLOCK_SOURCE_SDR12 = 0, // PLLP MMC_CLOCK_SOURCE_SDR12 = CLK_SOURCE_SDMMC1_PLLP_OUT0, // PLLP
MMC_CLOCK_SOURCE_SDR25 = 0, MMC_CLOCK_SOURCE_SDR25 = CLK_SOURCE_SDMMC1_PLLP_OUT0,
MMC_CLOCK_SOURCE_SDR50 = 0, MMC_CLOCK_SOURCE_SDR50 = CLK_SOURCE_SDMMC1_PLLP_OUT0,
MMC_CLOCK_SOURCE_SDR104 = 0, MMC_CLOCK_SOURCE_SDR104 = CLK_SOURCE_SDMMC1_PLLP_OUT0,
/* Clock dividers: MMC */ /* Clock sources: MMC */
MMC_CLOCK_SOURCE_HS26 = 0, // PLLP MMC_CLOCK_SOURCE_HS26 = CLK_SOURCE_SDMMC4_PLLP_OUT0, // PLLP
MMC_CLOCK_SOURCE_HS52 = 0, MMC_CLOCK_SOURCE_HS52 = CLK_SOURCE_SDMMC4_PLLP_OUT0,
MMC_CLOCK_SOURCE_HS200 = 1, // PLLC4_OUT2_LJ
MMC_CLOCK_SOURCE_HS400 = 1,
#if 0
// TODO: Figure out why PLLC4_OUT2_LJ doesn't work, most likely need to be enabled in hwinit
MMC_CLOCK_SOURCE_HS200 = CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ, // PLLC4_OUT2_LJ
MMC_CLOCK_SOURCE_HS400 = CLK_SOURCE_SDMMC4_PLLC4_OUT2_LJ,
#else
// For the time being, use PLLP_OUT0
MMC_CLOCK_SOURCE_HS200 = CLK_SOURCE_SDMMC4_PLLP_OUT0,
MMC_CLOCK_SOURCE_HS400 = CLK_SOURCE_SDMMC4_PLLP_OUT0,
#endif
/* Clock sources: Legacy 12 MHz timer */
MMC_CLOCK_SOURCE_LEGACY = CLK_SOURCE_SDMMC_LEGACY_PLLP_OUT0,
}; };
/** /**
@ -239,7 +257,7 @@ enum sdmmc_register_bits {
MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2), MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2),
MMC_CLOCK_CONTROL_FREQUENCY_MASK = (0x3FF << 6), MMC_CLOCK_CONTROL_FREQUENCY_MASK = (0x3FF << 6),
MMC_CLOCK_CONTROL_FREQUENCY_SHIFT = 8, MMC_CLOCK_CONTROL_FREQUENCY_SHIFT = 8,
MMC_CLOCK_CONTROL_FREQUENCY_INIT = 0x18, // generates 400kHz from the TRM dividers MMC_CLOCK_CONTROL_FREQUENCY_INIT = 0x1F, // generates 400kHz from the TRM dividers
MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH = 0x00, // passes through the CAR clock unmodified MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH = 0x00, // passes through the CAR clock unmodified
/* Host control */ /* Host control */
@ -797,6 +815,20 @@ static int sdmmc_hardware_reset(struct mmc *mmc, uint32_t reset_flags)
return 0; return 0;
} }
/**
* Delays for a given amount of host clock cycles.
*
* @param mmc The MMC controller whose clock cycles should be waited upon.
* @param clocks The number of clock cycles to wait.
*/
static void sdmmc_host_clock_delay(struct mmc *mmc, unsigned int clocks)
{
// For the time being simply wait for clocks * 50 us
// This covers clocks as slow as 20 kHz and hence should always be safe
// TODO: determine the actual wait time based on clock source and divider
udelay(50 * clocks);
}
/** /**
* Performs low-level initialization for SDMMC4, used for the eMMC. * Performs low-level initialization for SDMMC4, used for the eMMC.
*/ */
@ -804,23 +836,22 @@ static int sdmmc4_set_up_clock_and_io(struct mmc *mmc)
{ {
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
volatile struct tegra_padctl *padctl = padctl_get_regs(); volatile struct tegra_padctl *padctl = padctl_get_regs();
(void)mmc;
// Put SDMMC4 in reset // Put SDMMC4 in reset
car->rst_dev_l_set |= 0x8000; car->rst_dev_l_set |= 0x8000;
// Configure the clock to place the device into the initial mode. // Configure the clock to place the device into the initial mode.
car->clk_src[CLK_SOURCE_SDMMC4] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src[CLK_SOURCE_SDMMC4] = MMC_CLOCK_SOURCE_SDR12 | MMC_CLOCK_DIVIDER_SDR12;
// Set the legacy divier used for detecting timeouts. // Set the legacy divier used for detecting timeouts.
car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = MMC_CLOCK_SOURCE_LEGACY | MMC_CLOCK_DIVIDER_LEGACY;
// Set SDMMC4 clock enable // Set SDMMC4 clock enable
car->clk_enb_l_set |= 0x8000; car->clk_enb_l_set |= 0x8000;
car->clk_enb_y_set |= CAR_CONTROL_SDMMC_LEGACY; car->clk_enb_y_set |= CAR_CONTROL_SDMMC_LEGACY;
// host_clk_delay(0x64, clk_freq) -> Delay 100 host clock cycles // Delay 100 host clock cycles
udelay(5000); sdmmc_host_clock_delay(mmc, 100);
// Take SDMMC4 out of reset // Take SDMMC4 out of reset
car->rst_dev_l_clr |= 0x8000; car->rst_dev_l_clr |= 0x8000;
@ -1103,11 +1134,11 @@ static int sdmmc_always_fail(struct mmc *mmc)
* a divider of N results in a clock that's (N/2) + 1 slower. * a divider of N results in a clock that's (N/2) + 1 slower.
* @param sdmmc_divisor An additional divisor applied in the SDMMC controller. * @param sdmmc_divisor An additional divisor applied in the SDMMC controller.
*/ */
static void sdmmc4_configure_clock(struct mmc *mmc, int source, int car_divisor, int sdmmc_divisor) static void sdmmc4_configure_clock(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor)
{ {
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
// Set up the CAR aspect of the clock, and wait 2uS per change per the TRM. // Set up the CAR aspect of the clock, and wait 2us per change per the TRM.
car->clk_enb_l_clr = CAR_CONTROL_SDMMC4; car->clk_enb_l_clr = CAR_CONTROL_SDMMC4;
car->clk_src[CLK_SOURCE_SDMMC4] = source | car_divisor; car->clk_src[CLK_SOURCE_SDMMC4] = source | car_divisor;
udelay(2); udelay(2);
@ -1129,11 +1160,11 @@ static void sdmmc4_configure_clock(struct mmc *mmc, int source, int car_divisor,
* a divider of N results in a clock that's (N/2) + 1 slower. * a divider of N results in a clock that's (N/2) + 1 slower.
* @param sdmmc_divisor An additional divisor applied in the SDMMC controller. * @param sdmmc_divisor An additional divisor applied in the SDMMC controller.
*/ */
static void sdmmc1_configure_clock(struct mmc *mmc, int source, int car_divisor, int sdmmc_divisor) static void sdmmc1_configure_clock(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor)
{ {
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
// Set up the CAR aspect of the clock, and wait 2uS per change per the TRM. // Set up the CAR aspect of the clock, and wait 2us per change per the TRM.
car->clk_enb_l_clr = CAR_CONTROL_SDMMC1; car->clk_enb_l_clr = CAR_CONTROL_SDMMC1;
car->clk_src[CLK_SOURCE_SDMMC1] = source | car_divisor; car->clk_src[CLK_SOURCE_SDMMC1] = source | car_divisor;
udelay(2); udelay(2);
@ -1470,7 +1501,6 @@ static int sdmmc1_set_up_clock_and_io(struct mmc *mmc)
volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_car *car = car_get_regs();
volatile struct tegra_pinmux *pinmux = pinmux_get_regs(); volatile struct tegra_pinmux *pinmux = pinmux_get_regs();
volatile struct tegra_padctl *padctl = padctl_get_regs(); volatile struct tegra_padctl *padctl = padctl_get_regs();
(void)mmc;
// Set up each of the relevant pins to be connected to output drivers, // Set up each of the relevant pins to be connected to output drivers,
// and selected for SDMMC use. // and selected for SDMMC use.
@ -1494,19 +1524,19 @@ static int sdmmc1_set_up_clock_and_io(struct mmc *mmc)
car->rst_dev_l_set = CAR_CONTROL_SDMMC1; car->rst_dev_l_set = CAR_CONTROL_SDMMC1;
// Configure the clock to place the device into the initial mode. // Configure the clock to place the device into the initial mode.
car->clk_src[CLK_SOURCE_SDMMC1] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src[CLK_SOURCE_SDMMC1] = MMC_CLOCK_SOURCE_SDR12 | MMC_CLOCK_DIVIDER_SDR12;
// Set the legacy divier used for detecting timeouts. // Set the legacy divier used for detecting timeouts.
car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12; car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = MMC_CLOCK_SOURCE_LEGACY | MMC_CLOCK_DIVIDER_LEGACY;
// Set SDMMC1 clock enable // Set SDMMC1 clock enable
car->clk_enb_l_set = CAR_CONTROL_SDMMC1; car->clk_enb_l_set = CAR_CONTROL_SDMMC1;
car->clk_enb_y_set = CAR_CONTROL_SDMMC_LEGACY; car->clk_enb_y_set = CAR_CONTROL_SDMMC_LEGACY;
// host_clk_delay(0x64, clk_freq) -> Delay 100 host clock cycles // Delay 100 host clock cycles
udelay(5000); sdmmc_host_clock_delay(mmc, 100);
// Take SDMMC4 out of reset // Take SDMMC1 out of reset
car->rst_dev_l_clr |= CAR_CONTROL_SDMMC1; car->rst_dev_l_clr |= CAR_CONTROL_SDMMC1;
// Enable clock loopback. // Enable clock loopback.
@ -3444,7 +3474,7 @@ int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller, bool allow_vol
return rc; return rc;
} }
// Default to a timeout of 1S. // Default to a timeout of 1s.
mmc->timeout = 1000000; mmc->timeout = 1000000;
mmc->partition_switch_time = 1000; mmc->partition_switch_time = 1000;

View file

@ -157,7 +157,7 @@ struct mmc {
/* Per-controller operations. */ /* Per-controller operations. */
int (*set_up_clock_and_io)(struct mmc *mmc); int (*set_up_clock_and_io)(struct mmc *mmc);
void (*configure_clock)(struct mmc *mmc, int source, int car_divisor, int sdmmc_divisor); void (*configure_clock)(struct mmc *mmc, uint32_t source, int car_divisor, int sdmmc_divisor);
int (*enable_supplies)(struct mmc *mmc); int (*enable_supplies)(struct mmc *mmc);
int (*switch_to_low_voltage)(struct mmc *mmc); int (*switch_to_low_voltage)(struct mmc *mmc);
bool (*card_present)(struct mmc *mmc); bool (*card_present)(struct mmc *mmc);