Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/mmc/mmc_fdt_helpers.c
Show All 39 Lines | |||||
#include <dev/gpio/gpiobusvar.h> | #include <dev/gpio/gpiobusvar.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#ifdef EXT_RESOURCES | #ifdef EXT_RESOURCES | ||||
#include <dev/extres/regulator/regulator.h> | #include <dev/extres/regulator/regulator.h> | ||||
#endif | #endif | ||||
#include <dev/mmc/mmc_helpers.h> | |||||
#include "mmc_pwrseq_if.h" | #include "mmc_pwrseq_if.h" | ||||
static inline void | |||||
mmc_fdt_parse_sd_speed(phandle_t node, struct mmc_host *host) | |||||
{ | |||||
bool no_18v = false; | |||||
/* | |||||
* Parse SD supported modes | |||||
* All UHS-I modes requires 1.8V signaling. | |||||
*/ | |||||
if (OF_hasprop(node, "no-1-8-v")) | |||||
no_18v = true; | |||||
if (OF_hasprop(node, "cap-sd-highspeed")) | |||||
host->caps |= MMC_CAP_HSPEED; | |||||
if (OF_hasprop(node, "sd-uhs-sdr12") && no_18v == false) | |||||
host->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_SIGNALING_180; | |||||
if (OF_hasprop(node, "sd-uhs-sdr25") && no_18v == false) | |||||
host->caps |= MMC_CAP_UHS_SDR25 | MMC_CAP_SIGNALING_180; | |||||
if (OF_hasprop(node, "sd-uhs-sdr50") && no_18v == false) | |||||
host->caps |= MMC_CAP_UHS_SDR50 | MMC_CAP_SIGNALING_180; | |||||
if (OF_hasprop(node, "sd-uhs-sdr104") && no_18v == false) | |||||
host->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_SIGNALING_180; | |||||
if (OF_hasprop(node, "sd-uhs-ddr50") && no_18v == false) | |||||
host->caps |= MMC_CAP_UHS_DDR50 | MMC_CAP_SIGNALING_180; | |||||
} | |||||
static inline void | |||||
mmc_fdt_parse_mmc_speed(phandle_t node, struct mmc_host *host) | |||||
{ | |||||
/* Parse eMMC supported modes */ | |||||
if (OF_hasprop(node, "cap-mmc-highspeed")) | |||||
host->caps |= MMC_CAP_HSPEED; | |||||
if (OF_hasprop(node, "mmc-ddr-1_2v")) | |||||
host->caps |= MMC_CAP_MMC_DDR52_120 | MMC_CAP_SIGNALING_120; | |||||
if (OF_hasprop(node, "mmc-ddr-1_8v")) | |||||
host->caps |= MMC_CAP_MMC_DDR52_180 | MMC_CAP_SIGNALING_180; | |||||
if (OF_hasprop(node, "mmc-ddr-3_3v")) | |||||
host->caps |= MMC_CAP_SIGNALING_330; | |||||
if (OF_hasprop(node, "mmc-hs200-1_2v")) | |||||
host->caps |= MMC_CAP_MMC_HS200_120 | MMC_CAP_SIGNALING_120; | |||||
if (OF_hasprop(node, "mmc-hs200-1_8v")) | |||||
host->caps |= MMC_CAP_MMC_HS200_180 | MMC_CAP_SIGNALING_180; | |||||
if (OF_hasprop(node, "mmc-hs400-1_2v")) | |||||
host->caps |= MMC_CAP_MMC_HS400_120 | MMC_CAP_SIGNALING_120; | |||||
if (OF_hasprop(node, "mmc-hs400-1_8v")) | |||||
host->caps |= MMC_CAP_MMC_HS400_180 | MMC_CAP_SIGNALING_180; | |||||
if (OF_hasprop(node, "mmc-hs400-enhanced-strobe")) | |||||
host->caps |= MMC_CAP_MMC_ENH_STROBE; | |||||
} | |||||
int | int | ||||
mmc_fdt_parse(device_t dev, phandle_t node, struct mmc_fdt_helper *helper, | mmc_fdt_parse(device_t dev, phandle_t node, struct mmc_helper *helper, | ||||
struct mmc_host *host) | struct mmc_host *host) | ||||
{ | { | ||||
uint32_t bus_width; | struct mmc_helper mmc_helper; | ||||
phandle_t pwrseq_xref; | phandle_t pwrseq_xref; | ||||
if (node <= 0) | memset(&mmc_helper, 0, sizeof(mmc_helper)); | ||||
node = ofw_bus_get_node(dev); | mmc_parse(dev, &mmc_helper, host); | ||||
if (node <= 0) | |||||
return (ENXIO); | |||||
if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0) | helper->props = mmc_helper.props; | ||||
bus_width = 1; | |||||
if (bus_width >= 4) | |||||
host->caps |= MMC_CAP_4_BIT_DATA; | |||||
if (bus_width >= 8) | |||||
host->caps |= MMC_CAP_8_BIT_DATA; | |||||
/* | |||||
* max-frequency is optional, drivers should tweak this value | |||||
* if it's not present based on the clock that the mmc controller | |||||
* operates on | |||||
*/ | |||||
OF_getencprop(node, "max-frequency", &host->f_max, sizeof(uint32_t)); | |||||
if (OF_hasprop(node, "broken-cd")) | |||||
helper->props |= MMC_PROP_BROKEN_CD; | |||||
if (OF_hasprop(node, "non-removable")) | |||||
helper->props |= MMC_PROP_NON_REMOVABLE; | |||||
if (OF_hasprop(node, "wp-inverted")) | |||||
helper->props |= MMC_PROP_WP_INVERTED; | |||||
if (OF_hasprop(node, "cd-inverted")) | |||||
helper->props |= MMC_PROP_CD_INVERTED; | |||||
if (OF_hasprop(node, "no-sdio")) | |||||
helper->props |= MMC_PROP_NO_SDIO; | |||||
if (OF_hasprop(node, "no-sd")) | |||||
helper->props |= MMC_PROP_NO_SD; | |||||
if (OF_hasprop(node, "no-mmc")) | |||||
helper->props |= MMC_PROP_NO_MMC; | |||||
if (!(helper->props & MMC_PROP_NO_SD)) | |||||
mmc_fdt_parse_sd_speed(node, host); | |||||
if (!(helper->props & MMC_PROP_NO_MMC)) | |||||
mmc_fdt_parse_mmc_speed(node, host); | |||||
#ifdef EXT_RESOURCES | #ifdef EXT_RESOURCES | ||||
/* | /* | ||||
* Get the regulators if they are supported and | * Get the regulators if they are supported and | ||||
* clean the non supported modes based on the available voltages. | * clean the non supported modes based on the available voltages. | ||||
*/ | */ | ||||
if (regulator_get_by_ofw_property(dev, 0, "vmmc-supply", | if (regulator_get_by_ofw_property(dev, 0, "vmmc-supply", | ||||
&helper->vmmc_supply) == 0) { | &helper->vmmc_supply) == 0) { | ||||
if (bootverbose) | if (bootverbose) | ||||
Show All 39 Lines | |||||
} | } | ||||
/* | /* | ||||
* Card detect interrupt handler. | * Card detect interrupt handler. | ||||
*/ | */ | ||||
static void | static void | ||||
cd_intr(void *arg) | cd_intr(void *arg) | ||||
{ | { | ||||
struct mmc_fdt_helper *helper = arg; | struct mmc_helper *helper = arg; | ||||
taskqueue_enqueue_timeout(taskqueue_swi_giant, | taskqueue_enqueue_timeout(taskqueue_swi_giant, | ||||
&helper->cd_delayed_task, -(hz / 2)); | &helper->cd_delayed_task, -(hz / 2)); | ||||
} | } | ||||
static void | static void | ||||
cd_card_task(void *arg, int pending __unused) | cd_card_task(void *arg, int pending __unused) | ||||
{ | { | ||||
struct mmc_fdt_helper *helper = arg; | struct mmc_helper *helper = arg; | ||||
bool cd_present; | bool cd_present; | ||||
cd_present = mmc_fdt_gpio_get_present(helper); | cd_present = mmc_fdt_gpio_get_present(helper); | ||||
if(helper->cd_handler && cd_present != helper->cd_present) | if(helper->cd_handler && cd_present != helper->cd_present) | ||||
helper->cd_handler(helper->dev, | helper->cd_handler(helper->dev, | ||||
cd_present); | cd_present); | ||||
helper->cd_present = cd_present; | helper->cd_present = cd_present; | ||||
/* If we're polling re-schedule the task */ | /* If we're polling re-schedule the task */ | ||||
if (helper->cd_ihandler == NULL) | if (helper->cd_ihandler == NULL) | ||||
taskqueue_enqueue_timeout_sbt(taskqueue_swi_giant, | taskqueue_enqueue_timeout_sbt(taskqueue_swi_giant, | ||||
&helper->cd_delayed_task, mstosbt(500), 0, C_PREL(2)); | &helper->cd_delayed_task, mstosbt(500), 0, C_PREL(2)); | ||||
} | } | ||||
/* | /* | ||||
* Card detect setup. | * Card detect setup. | ||||
*/ | */ | ||||
static void | static void | ||||
cd_setup(struct mmc_fdt_helper *helper, phandle_t node) | cd_setup(struct mmc_helper *helper, phandle_t node) | ||||
{ | { | ||||
int pincaps; | int pincaps; | ||||
device_t dev; | device_t dev; | ||||
const char *cd_mode_str; | const char *cd_mode_str; | ||||
dev = helper->dev; | dev = helper->dev; | ||||
TIMEOUT_TASK_INIT(taskqueue_swi_giant, &helper->cd_delayed_task, 0, | TIMEOUT_TASK_INIT(taskqueue_swi_giant, &helper->cd_delayed_task, 0, | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | device_printf(dev, "Card presence detect on %s pin %u, " | ||||
cd_mode_str); | cd_mode_str); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Write protect setup. | * Write protect setup. | ||||
*/ | */ | ||||
static void | static void | ||||
wp_setup(struct mmc_fdt_helper *helper, phandle_t node) | wp_setup(struct mmc_helper *helper, phandle_t node) | ||||
{ | { | ||||
device_t dev; | device_t dev; | ||||
dev = helper->dev; | dev = helper->dev; | ||||
if (OF_hasprop(node, "disable-wp")) { | if (OF_hasprop(node, "disable-wp")) { | ||||
helper->wp_disabled = true; | helper->wp_disabled = true; | ||||
if (bootverbose) | if (bootverbose) | ||||
device_printf(dev, "Write protect disabled\n"); | device_printf(dev, "Write protect disabled\n"); | ||||
return; | return; | ||||
} | } | ||||
if (gpio_pin_get_by_ofw_property(dev, node, "wp-gpios", &helper->wp_pin)) | if (gpio_pin_get_by_ofw_property(dev, node, "wp-gpios", &helper->wp_pin)) | ||||
return; | return; | ||||
if (bootverbose) | if (bootverbose) | ||||
device_printf(dev, "Write protect switch on %s pin %u\n", | device_printf(dev, "Write protect switch on %s pin %u\n", | ||||
device_get_nameunit(helper->wp_pin->dev), helper->wp_pin->pin); | device_get_nameunit(helper->wp_pin->dev), helper->wp_pin->pin); | ||||
} | } | ||||
int | int | ||||
mmc_fdt_gpio_setup(device_t dev, phandle_t node, struct mmc_fdt_helper *helper, | mmc_fdt_gpio_setup(device_t dev, phandle_t node, struct mmc_helper *helper, | ||||
mmc_fdt_cd_handler handler) | mmc_fdt_cd_handler handler) | ||||
{ | { | ||||
if (node <= 0) | if (node <= 0) | ||||
node = ofw_bus_get_node(dev); | node = ofw_bus_get_node(dev); | ||||
if (node <= 0) { | if (node <= 0) { | ||||
device_printf(dev, "Cannot get node for device\n"); | device_printf(dev, "Cannot get node for device\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
helper->dev = dev; | helper->dev = dev; | ||||
helper->cd_handler = handler; | helper->cd_handler = handler; | ||||
cd_setup(helper, node); | cd_setup(helper, node); | ||||
wp_setup(helper, node); | wp_setup(helper, node); | ||||
/* | /* | ||||
* Schedule a card detection | * Schedule a card detection | ||||
*/ | */ | ||||
taskqueue_enqueue_timeout_sbt(taskqueue_swi_giant, | taskqueue_enqueue_timeout_sbt(taskqueue_swi_giant, | ||||
&helper->cd_delayed_task, mstosbt(500), 0, C_PREL(2)); | &helper->cd_delayed_task, mstosbt(500), 0, C_PREL(2)); | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
mmc_fdt_gpio_teardown(struct mmc_fdt_helper *helper) | mmc_fdt_gpio_teardown(struct mmc_helper *helper) | ||||
{ | { | ||||
if (helper == NULL) | if (helper == NULL) | ||||
return; | return; | ||||
if (helper->cd_ihandler != NULL) | if (helper->cd_ihandler != NULL) | ||||
bus_teardown_intr(helper->dev, helper->cd_ires, helper->cd_ihandler); | bus_teardown_intr(helper->dev, helper->cd_ires, helper->cd_ihandler); | ||||
if (helper->wp_pin != NULL) | if (helper->wp_pin != NULL) | ||||
gpio_pin_release(helper->wp_pin); | gpio_pin_release(helper->wp_pin); | ||||
if (helper->cd_pin != NULL) | if (helper->cd_pin != NULL) | ||||
gpio_pin_release(helper->cd_pin); | gpio_pin_release(helper->cd_pin); | ||||
if (helper->cd_ires != NULL) | if (helper->cd_ires != NULL) | ||||
bus_release_resource(helper->dev, SYS_RES_IRQ, 0, helper->cd_ires); | bus_release_resource(helper->dev, SYS_RES_IRQ, 0, helper->cd_ires); | ||||
taskqueue_drain_timeout(taskqueue_swi_giant, &helper->cd_delayed_task); | taskqueue_drain_timeout(taskqueue_swi_giant, &helper->cd_delayed_task); | ||||
} | } | ||||
bool | bool | ||||
mmc_fdt_gpio_get_present(struct mmc_fdt_helper *helper) | mmc_fdt_gpio_get_present(struct mmc_helper *helper) | ||||
{ | { | ||||
bool pinstate; | bool pinstate; | ||||
if (helper->cd_disabled) | if (helper->cd_disabled) | ||||
return (true); | return (true); | ||||
if (helper->cd_pin == NULL) | if (helper->cd_pin == NULL) | ||||
return (false); | return (false); | ||||
gpio_pin_is_active(helper->cd_pin, &pinstate); | gpio_pin_is_active(helper->cd_pin, &pinstate); | ||||
return (pinstate ^ (bool)(helper->props & MMC_PROP_CD_INVERTED)); | return (pinstate ^ (bool)(helper->props & MMC_PROP_CD_INVERTED)); | ||||
} | } | ||||
bool | bool | ||||
mmc_fdt_gpio_get_readonly(struct mmc_fdt_helper *helper) | mmc_fdt_gpio_get_readonly(struct mmc_helper *helper) | ||||
{ | { | ||||
bool pinstate; | bool pinstate; | ||||
if (helper->wp_disabled) | if (helper->wp_disabled) | ||||
return (false); | return (false); | ||||
if (helper->wp_pin == NULL) | if (helper->wp_pin == NULL) | ||||
return (false); | return (false); | ||||
gpio_pin_is_active(helper->wp_pin, &pinstate); | gpio_pin_is_active(helper->wp_pin, &pinstate); | ||||
return (pinstate ^ (bool)(helper->props & MMC_PROP_WP_INVERTED)); | return (pinstate ^ (bool)(helper->props & MMC_PROP_WP_INVERTED)); | ||||
} | } | ||||
void | void | ||||
mmc_fdt_set_power(struct mmc_fdt_helper *helper, enum mmc_power_mode power_mode) | mmc_fdt_set_power(struct mmc_helper *helper, enum mmc_power_mode power_mode) | ||||
{ | { | ||||
int reg_status; | int reg_status; | ||||
int rv; | int rv; | ||||
switch (power_mode) { | switch (power_mode) { | ||||
case power_on: | case power_on: | ||||
break; | break; | ||||
case power_off: | case power_off: | ||||
Show All 29 Lines |