1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-23 04:12:02 +00:00

Merge pull request #138 from desowin/sdmmc-frequency

Rework sdmmc clocking configuration
This commit is contained in:
TuxSH 2018-06-13 17:31:19 +02:00 committed by GitHub
commit 82b248aeac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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);