TERES/SOFTWARE/A64-TERES/linux-a64/drivers/mmc/host/sunxi-mmc-sun8iw10p1-2.c
Dimitar Gamishev f9b0e7a283 linux
2017-10-13 14:07:04 +03:00

403 lines
9.3 KiB
C

#ifdef CONFIG_ARCH_SUN8IW10P1
#include <linux/clk.h>
#include <linux/clk-private.h>
#include <linux/clk/sunxi.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/reset.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/slot-gpio.h>
#include "sunxi-smhc.h"
#include "sunxi-mmc-sun8iw10p1-2.h"
static void sunxi_mmc_set_clk_dly(struct sunxi_mmc_host *host,int clk,int bus_width,int timing)
{
dev_dbg(mmc_dev(host->mmc),"no imple %s %d\n",__FUNCTION__,__LINE__);
}
void sunxi_mmc_dump_dly2(struct sunxi_mmc_host *host)
{
dev_dbg(mmc_dev(host->mmc),"no imple %s %d\n",__FUNCTION__,__LINE__);
}
static int __sunxi_mmc_do_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en,u32 pwr_save,u32 ignore_dat0)
{
u32 tmp = 0;
tmp = smhc_readl(host,SMHC_RST_CLK_CTRL);
if (oclk_en) {
tmp |= SdclkEn;
} else {
tmp &= ~SdclkEn;
}
smhc_writel(host,SMHC_RST_CLK_CTRL,tmp);
tmp = smhc_readl(host,SMHC_CTRL3);
if (pwr_save) {
tmp |= SdclkIdleCtrl;
} else {
tmp &= ~SdclkIdleCtrl;
}
smhc_writel(host,SMHC_CTRL3,tmp);
return 0;
}
static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
{
struct device_node *np = NULL;
struct mmc_host *mmc = host->mmc;
int pwr_save = 0;
int len = 0;
if (!mmc->parent || !mmc->parent->of_node){
dev_err(mmc_dev(host->mmc), "no dts to parse power save mode\n");
return -EIO; ;
}
np = mmc->parent->of_node;
if (of_find_property(np, "sunxi-power-save-mode", &len))
pwr_save = 1;
return __sunxi_mmc_do_oclk_onoff(host,oclk_en,pwr_save,1);
}
int sunxi_mmc_clk_set_rate_for_sdmmc2(struct sunxi_mmc_host *host,
struct mmc_ios *ios)
{
u32 mod_clk = 0;
u32 src_clk = 0;
u32 rval = 0;
s32 err = 0;
u32 rate = 0;
char *sclk_name = NULL;
struct clk *mclk = host->clk_mmc;
struct clk *sclk = NULL;
struct device *dev = mmc_dev(host->mmc);
if(ios->clock == 0){
__sunxi_mmc_do_oclk_onoff(host, 0,0,1);
return 0;
}
if(ios->timing == MMC_TIMING_UHS_DDR50){
mod_clk = ios->clock<<3;
}else{
mod_clk = ios->clock<<2;
}
if (ios->clock<= 400000) {
//sclk = of_clk_get(np, 0);
sclk = clk_get(dev,"osc24m");
sclk_name = "osc24m";
} else {
//sclk = clk_get(np, 1);
sclk = clk_get(dev,"pll_periph");
sclk_name = "pll_periph";
}
if (IS_ERR(sclk)) {
dev_err(mmc_dev(host->mmc), "Error to get source clock %s\n",sclk_name);
return -1;
}
sunxi_mmc_oclk_onoff(host, 0);
err = clk_set_parent(mclk, sclk);
if(err){
dev_err(mmc_dev(host->mmc), "set parent failed\n");
clk_put(sclk);
return -1;
}
rate = clk_round_rate(mclk, mod_clk);
dev_dbg(mmc_dev(host->mmc),"get round rate %d\n", rate);
clk_disable_unprepare(host->clk_mmc);
err = clk_set_rate(mclk, rate);
if (err) {
dev_err(mmc_dev(host->mmc),"set mclk rate error, rate %dHz\n",rate);
clk_put(sclk);
return -1;
}
rval = clk_prepare_enable(host->clk_mmc);
if (rval) {
dev_err(mmc_dev(host->mmc), "Enable mmc clk err %d\n", rval);
return -1;
}
src_clk = clk_get_rate(sclk);
clk_put(sclk);
dev_dbg(mmc_dev(host->mmc),"set round clock %d, soure clk is %d\n", rate, src_clk);
//sunxi_of_parse_clk_dly(host);
if(ios->timing == MMC_TIMING_UHS_DDR50){
ios->clock = rate>>3;
}else{
ios->clock = rate>>2;
}
sunxi_mmc_set_clk_dly(host,ios->clock,ios->bus_width,ios->timing);
return sunxi_mmc_oclk_onoff(host, 1);
}
void sunxi_mmc_thld_ctl_for_sdmmc2(struct sunxi_mmc_host *host,
struct mmc_ios *ios, struct mmc_data *data)
{
dev_dbg(mmc_dev(host->mmc),"no imple %s %d\n",__FUNCTION__,__LINE__);
}
void sunxi_mmc_save_spec_reg2(struct sunxi_mmc_host *host)
{
dev_dbg(mmc_dev(host->mmc),"no imple %s %d\n",__FUNCTION__,__LINE__);
}
void sunxi_mmc_restore_spec_reg2(struct sunxi_mmc_host *host)
{
dev_dbg(mmc_dev(host->mmc),"no imple %s %d\n",__FUNCTION__,__LINE__);
}
/*
extern int mmc_go_idle(struct mmc_host *host);
extern int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
extern int mmc_send_status(struct mmc_card *card, u32 *status);
extern void mmc_set_clock(struct mmc_host *host, unsigned int hz);
extern void mmc_set_timing(struct mmc_host *host, unsigned int timing);
extern void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
void sunxi_mmc_do_shutdown2(struct platform_device * pdev)
{
u32 ocr = 0;
u32 err = 0;
struct mmc_host *mmc = NULL;
struct sunxi_mmc_host *host = NULL;
u32 status = 0;
mmc = platform_get_drvdata(pdev);
if (mmc == NULL) {
dev_err(&pdev->dev,"%s: mmc is NULL\n", __FUNCTION__);
goto out;
}
host = mmc_priv(mmc);
if (host == NULL) {
dev_err(&pdev->dev,"%s: host is NULL\n", __FUNCTION__);
goto out;
}
dev_info(mmc_dev(mmc),"try to disable cache\n");
mmc_claim_host(mmc);
err = mmc_cache_ctrl(mmc, 0);
mmc_release_host(mmc);
if (err){
dev_err(mmc_dev(mmc),"disable cache failed\n");
mmc_claim_host(mmc);//not release host to not allow android to read/write after shutdown
goto out;
}
//claim host to not allow androd read/write during shutdown
dev_dbg(mmc_dev(mmc),"%s: claim host\n", __FUNCTION__);
mmc_claim_host(mmc);
do {
if (mmc_send_status(mmc->card, &status) != 0) {
dev_err(mmc_dev(mmc),"%s: send status failed\n", __FUNCTION__);
goto out; //err_out; //not release host to not allow android to read/write after shutdown
}
} while(status != 0x00000900);
//mmc_card_set_ddr_mode(card);
mmc_set_timing(mmc, MMC_TIMING_LEGACY);
mmc_set_bus_width(mmc, MMC_BUS_WIDTH_1);
mmc_set_clock(mmc, 400000);
err = mmc_go_idle(mmc);
if (err) {
dev_err(mmc_dev(mmc),"%s: mmc_go_idle err\n", __FUNCTION__);
goto out; //err_out; //not release host to not allow android to read/write after shutdown
}
if (mmc->card->type != MMC_TYPE_MMC) {//sd can support cmd1,so not send cmd1
goto out;//not release host to not allow android to read/write after shutdown
}
err = mmc_send_op_cond(mmc, 0, &ocr);
if (err) {
dev_err(mmc_dev(mmc),"%s: first mmc_send_op_cond err\n", __FUNCTION__);
goto out; //err_out; //not release host to not allow android to read/write after shutdown
}
err = mmc_send_op_cond(mmc, ocr | (1 << 30), &ocr);
if (err) {
dev_err(mmc_dev(mmc),"%s: mmc_send_op_cond err\n", __FUNCTION__);
goto out; //err_out; //not release host to not allow android to read/write after shutdown
}
//do not release host to not allow android to read/write after shutdown
goto out;
out:
dev_info(mmc_dev(mmc),"%s: mmc shutdown exit..ok\n", __FUNCTION__);
return ;
}
*/
int mmc_card_sleep(struct mmc_host *host);
int mmc_deselect_cards(struct mmc_host *host);
void mmc_power_off(struct mmc_host *host);
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
static int sunxi_mmc_can_poweroff_notify(const struct mmc_card *card)
{
return card &&
mmc_card_mmc(card) &&
(card->ext_csd.power_off_notification == EXT_CSD_POWER_ON);
}
static int sunxi_mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
{
unsigned int timeout = card->ext_csd.generic_cmd6_time;
int err;
/* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */
if (notify_type == EXT_CSD_POWER_OFF_LONG)
timeout = card->ext_csd.power_off_longtime;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout, true, false, false);
if (err)
pr_err("%s: Power Off Notification timed out, %u\n",
mmc_hostname(card->host), timeout);
/* Disable the power off notification after the switch operation. */
card->ext_csd.power_off_notification = EXT_CSD_NO_POWER_NOTIFICATION;
return err;
}
static int sunxi_mmc_sleep(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err = -ENOSYS;
if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 1);
if (err < 0)
pr_debug("%s: Error %d while putting card into sleep",
mmc_hostname(host), err);
}
return err;
}
static int sunxi_mmc_suspend(struct mmc_host *host, bool is_suspend)
{
int err = 0;
unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
EXT_CSD_POWER_OFF_LONG;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
//if (mmc_card_suspended(host->card))
// goto out;
if (mmc_card_doing_bkops(host->card)) {
err = mmc_stop_bkops(host->card);
if (err)
goto out;
}
err = mmc_flush_cache(host->card);
if (err)
goto out;
if (sunxi_mmc_can_poweroff_notify(host->card) &&
((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) || !is_suspend)){
err = sunxi_mmc_poweroff_notify(host->card, notify_type);
}else if (mmc_card_can_sleep(host)){
err = sunxi_mmc_sleep(host);
}else if (!mmc_host_is_spi(host)){
err = mmc_deselect_cards(host);
}
if (!err) {
pr_info("%s: %s %d\n",
mmc_hostname(host),__FUNCTION__,__LINE__);
mmc_power_off(host);
// mmc_card_set_suspended(host->card);
}
out:
mmc_release_host(host);
return err;
}
void sunxi_mmc_do_shutdown2(struct platform_device * pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
u32 shutdown_notify_type = 0;
u32 rval = of_property_read_u32(mmc->parent->of_node, "shutdown_notify_type", &shutdown_notify_type);
if(!rval){
sunxi_mmc_suspend(mmc ,shutdown_notify_type);
}else{
sunxi_mmc_suspend(mmc ,false);
}
}
#endif