Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  dw_mmc-hi3798mv200.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Modified from dw_mmc-hi3798cv200.c
 *
 * Copyright (c) 2024 Yang Xiwen <forbidden405@outlook.com>
 * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
 */


#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>

#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"

#define SDMMC_TUNING_CTRL 0x118
#define SDMMC_TUNING_FIND_EDGE BIT(5)

#define ALL_INT_CLR  0x1ffff

/* DLL ctrl reg */
#define SAP_DLL_CTRL_DLLMODE BIT(16)

struct dw_mci_hi3798mv200_priv {
 struct clk *sample_clk;
 struct clk *drive_clk;
 struct regmap *crg_reg;
 u32 sap_dll_offset;
 struct mmc_clk_phase_map phase_map;
};

static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
 struct dw_mci_hi3798mv200_priv *priv = host->priv;
 struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing];
 u32 val;

 val = mci_readl(host, ENABLE_SHIFT);
 if (ios->timing == MMC_TIMING_MMC_DDR52
     || ios->timing == MMC_TIMING_UHS_DDR50)
  val |= SDMMC_ENABLE_PHASE;
 else
  val &= ~SDMMC_ENABLE_PHASE;
 mci_writel(host, ENABLE_SHIFT, val);

 val = mci_readl(host, DDR_REG);
 if (ios->timing == MMC_TIMING_MMC_HS400)
  val |= SDMMC_DDR_HS400;
 else
  val &= ~SDMMC_DDR_HS400;
 mci_writel(host, DDR_REG, val);

 if (clk_set_rate(host->ciu_clk, ios->clock))
  dev_warn(host->dev, "Failed to set rate to %u\n", ios->clock);
 else
  /*
 * CLK_MUX_ROUND_NEAREST is enabled for this clock
 * The actual clock rate is not what we set, but a rounded value
 * so we should get the rate once again
 */

  host->bus_hz = clk_get_rate(host->ciu_clk);

 if (phase.valid) {
  clk_set_phase(priv->drive_clk, phase.out_deg);
  clk_set_phase(priv->sample_clk, phase.in_deg);
 } else {
  dev_warn(host->dev,
    "The phase entry for timing mode %d is missing in device tree.\n",
    ios->timing);
 }
}

static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot)
{
 struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;

 return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
}

static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot)
{
 struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;

 return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
}

static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
          u32 opcode)
{
 static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
 struct dw_mci *host = slot->host;
 struct dw_mci_hi3798mv200_priv *priv = host->priv;
 int raise_point = -1, fall_point = -1, mid;
 int err, prev_err = -1;
 int found = 0;
 int regval;
 int i;
 int ret;

 ret = dw_mci_hi3798mv200_enable_tuning(slot);
 if (ret < 0)
  return ret;

 for (i = 0; i < ARRAY_SIZE(degrees); i++) {
  clk_set_phase(priv->sample_clk, degrees[i]);
  mci_writel(host, RINTSTS, ALL_INT_CLR);

  /*
 * HiSilicon implemented a tuning mechanism.
 * It needs special interaction with the DLL.
 *
 * Treat edge(flip) found as an error too.
 */

  err = mmc_send_tuning(slot->mmc, opcode, NULL);
  regval = mci_readl(host, TUNING_CTRL);
  if (err || (regval & SDMMC_TUNING_FIND_EDGE))
   err = 1;
  else
   found = 1;

  if (i > 0) {
   if (err && !prev_err)
    fall_point = i - 1;
   if (!err && prev_err)
    raise_point = i;
  }

  if (raise_point != -1 && fall_point != -1)
   goto tuning_out;

  prev_err = err;
 }

tuning_out:
 ret = dw_mci_hi3798mv200_disable_tuning(slot);
 if (ret < 0)
  return ret;

 if (found) {
  if (raise_point == -1)
   raise_point = 0;
  if (fall_point == -1)
   fall_point = ARRAY_SIZE(degrees) - 1;
  if (fall_point < raise_point) {
   if ((raise_point + fall_point) >
       (ARRAY_SIZE(degrees) - 1))
    mid = fall_point / 2;
   else
    mid = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
  } else {
   mid = (raise_point + fall_point) / 2;
  }

  /*
 * We don't care what timing we are tuning for,
 * simply use the same phase for all timing needs tuning.
 */

  priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid];
  priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid];
  priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid];

  clk_set_phase(priv->sample_clk, degrees[mid]);
  dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
   raise_point, fall_point, degrees[mid]);
  ret = 0;
 } else {
  dev_err(host->dev, "No valid clk_sample shift!\n");
  ret = -EINVAL;
 }

 mci_writel(host, RINTSTS, ALL_INT_CLR);

 return ret;
}

static int dw_mci_hi3798mv200_init(struct dw_mci *host)
{
 struct dw_mci_hi3798mv200_priv *priv;
 struct device_node *np = host->dev->of_node;

 priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
 if (!priv)
  return -ENOMEM;

 mmc_of_parse_clk_phase(host->dev, &priv->phase_map);

 priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample");
 if (IS_ERR(priv->sample_clk))
  return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk),
         "failed to get enabled ciu-sample clock\n");

 priv->drive_clk = devm_clk_get_enabled(host->dev, "ciu-drive");
 if (IS_ERR(priv->drive_clk))
  return dev_err_probe(host->dev, PTR_ERR(priv->drive_clk),
         "failed to get enabled ciu-drive clock\n");

 priv->crg_reg = syscon_regmap_lookup_by_phandle_args(np, "hisilicon,sap-dll-reg",
            1, &priv->sap_dll_offset);
 if (IS_ERR(priv->crg_reg))
  return dev_err_probe(host->dev, PTR_ERR(priv->crg_reg),
         "failed to get CRG reg\n");

 host->priv = priv;
 return 0;
}

static const struct dw_mci_drv_data hi3798mv200_data = {
 .common_caps = MMC_CAP_CMD23,
 .init = dw_mci_hi3798mv200_init,
 .set_ios = dw_mci_hi3798mv200_set_ios,
 .execute_tuning = dw_mci_hi3798mv200_execute_tuning_mix_mode,
};

static const struct of_device_id dw_mci_hi3798mv200_match[] = {
 { .compatible = "hisilicon,hi3798mv200-dw-mshc" },
 {},
};

static int dw_mci_hi3798mv200_probe(struct platform_device *pdev)
{
 return dw_mci_pltfm_register(pdev, &hi3798mv200_data);
}

static void dw_mci_hi3798mv200_remove(struct platform_device *pdev)
{
 dw_mci_pltfm_remove(pdev);
}

MODULE_DEVICE_TABLE(of, dw_mci_hi3798mv200_match);
static struct platform_driver dw_mci_hi3798mv200_driver = {
 .probe = dw_mci_hi3798mv200_probe,
 .remove = dw_mci_hi3798mv200_remove,
 .driver = {
  .name = "dwmmc_hi3798mv200",
  .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  .of_match_table = dw_mci_hi3798mv200_match,
 },
};
module_platform_driver(dw_mci_hi3798mv200_driver);

MODULE_DESCRIPTION("HiSilicon Hi3798MV200 Specific DW-MSHC Driver Extension");
MODULE_LICENSE("GPL");

Messung V0.5
C=95 H=98 G=96

¤ Dauer der Verarbeitung: 0.0 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge