Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/clk/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 7 kB image not shown  

Quelle  clk-lan966x.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Microchip LAN966x SoC Clock driver.
 *
 * Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries
 *
 * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
 */


#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#include <dt-bindings/clock/microchip,lan966x.h>

#define GCK_ENA         BIT(0)
#define GCK_SRC_SEL     GENMASK(9, 8)
#define GCK_PRESCALER   GENMASK(23, 16)

#define DIV_MAX  255

static const char * const lan966x_clk_names[] = {
 "qspi0""qspi1""qspi2""sdmmc0",
 "pi""mcan0""mcan1""flexcom0",
 "flexcom1""flexcom2""flexcom3",
 "flexcom4""timer1""usb_refclk",
};

static const char * const lan969x_clk_names[] = {
 "qspi0""qspi2""sdmmc0""sdmmc1",
 "mcan0""mcan1""flexcom0",
 "flexcom1""flexcom2""flexcom3",
 "timer1""usb_refclk",
};

struct lan966x_gck {
 struct clk_hw hw;
 void __iomem *reg;
};
#define to_lan966x_gck(hw) container_of(hw, struct lan966x_gck, hw)

static const struct clk_parent_data lan966x_gck_pdata[] = {
 { .fw_name = "cpu", },
 { .fw_name = "ddr", },
 { .fw_name = "sys", },
};

static struct clk_init_data init = {
 .parent_data = lan966x_gck_pdata,
 .num_parents = ARRAY_SIZE(lan966x_gck_pdata),
};

struct clk_gate_soc_desc {
 const char *name;
 int bit_idx;
};

static const struct clk_gate_soc_desc lan966x_clk_gate_desc[] = {
 { "uhphs", 11 },
 { "udphs", 10 },
 { "mcramc", 9 },
 { "hmatrix", 8 },
 { }
};

static const struct clk_gate_soc_desc lan969x_clk_gate_desc[] = {
 { "usb_drd", 10 },
 { "mcramc", 9 },
 { "hmatrix", 8 },
 { }
};

struct lan966x_match_data {
 char *name;
 const char * const *clk_name;
 const struct clk_gate_soc_desc *clk_gate_desc;
 u8 num_generic_clks;
 u8 num_total_clks;
};

static struct lan966x_match_data lan966x_desc = {
 .name = "lan966x",
 .clk_name = lan966x_clk_names,
 .clk_gate_desc = lan966x_clk_gate_desc,
 .num_total_clks = 18,
 .num_generic_clks = 14,
};

static struct lan966x_match_data lan969x_desc = {
 .name = "lan969x",
 .clk_name = lan969x_clk_names,
 .clk_gate_desc = lan969x_clk_gate_desc,
 .num_total_clks = 15,
 .num_generic_clks = 12,
};

static DEFINE_SPINLOCK(clk_gate_lock);
static void __iomem *base;

static int lan966x_gck_enable(struct clk_hw *hw)
{
 struct lan966x_gck *gck = to_lan966x_gck(hw);
 u32 val = readl(gck->reg);

 val |= GCK_ENA;
 writel(val, gck->reg);

 return 0;
}

static void lan966x_gck_disable(struct clk_hw *hw)
{
 struct lan966x_gck *gck = to_lan966x_gck(hw);
 u32 val = readl(gck->reg);

 val &= ~GCK_ENA;
 writel(val, gck->reg);
}

static int lan966x_gck_set_rate(struct clk_hw *hw,
    unsigned long rate,
    unsigned long parent_rate)
{
 struct lan966x_gck *gck = to_lan966x_gck(hw);
 u32 div, val = readl(gck->reg);

 if (rate == 0 || parent_rate == 0)
  return -EINVAL;

 /* Set Prescalar */
 div = parent_rate / rate;
 val &= ~GCK_PRESCALER;
 val |= FIELD_PREP(GCK_PRESCALER, (div - 1));
 writel(val, gck->reg);

 return 0;
}

static unsigned long lan966x_gck_recalc_rate(struct clk_hw *hw,
          unsigned long parent_rate)
{
 struct lan966x_gck *gck = to_lan966x_gck(hw);
 u32 div, val = readl(gck->reg);

 div = FIELD_GET(GCK_PRESCALER, val);

 return parent_rate / (div + 1);
}

static int lan966x_gck_determine_rate(struct clk_hw *hw,
          struct clk_rate_request *req)
{
 struct clk_hw *parent;
 int i;

 for (i = 0; i < clk_hw_get_num_parents(hw); ++i) {
  parent = clk_hw_get_parent_by_index(hw, i);
  if (!parent)
   continue;

  /* Allowed prescaler divider range is 0-255 */
  if (clk_hw_get_rate(parent) / req->rate <= DIV_MAX) {
   req->best_parent_hw = parent;
   req->best_parent_rate = clk_hw_get_rate(parent);

   return 0;
  }
 }

 return -EINVAL;
}

static u8 lan966x_gck_get_parent(struct clk_hw *hw)
{
 struct lan966x_gck *gck = to_lan966x_gck(hw);
 u32 val = readl(gck->reg);

 return FIELD_GET(GCK_SRC_SEL, val);
}

static int lan966x_gck_set_parent(struct clk_hw *hw, u8 index)
{
 struct lan966x_gck *gck = to_lan966x_gck(hw);
 u32 val = readl(gck->reg);

 val &= ~GCK_SRC_SEL;
 val |= FIELD_PREP(GCK_SRC_SEL, index);
 writel(val, gck->reg);

 return 0;
}

static const struct clk_ops lan966x_gck_ops = {
 .enable         = lan966x_gck_enable,
 .disable        = lan966x_gck_disable,
 .set_rate       = lan966x_gck_set_rate,
 .recalc_rate    = lan966x_gck_recalc_rate,
 .determine_rate = lan966x_gck_determine_rate,
 .set_parent     = lan966x_gck_set_parent,
 .get_parent     = lan966x_gck_get_parent,
};

static struct clk_hw *lan966x_gck_clk_register(struct device *dev, int i)
{
 struct lan966x_gck *priv;
 int ret;

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

 priv->reg = base + (i * 4);
 priv->hw.init = &init;
 ret = devm_clk_hw_register(dev, &priv->hw);
 if (ret)
  return ERR_PTR(ret);

 return &priv->hw;
};

static int lan966x_gate_clk_register(struct device *dev,
         const struct lan966x_match_data *data,
         struct clk_hw_onecell_data *hw_data,
         void __iomem *gate_base)
{
 for (int i = data->num_generic_clks; i < data->num_total_clks; ++i) {
  int idx = i - data->num_generic_clks;
  const struct clk_gate_soc_desc *desc;

  desc = &data->clk_gate_desc[idx];

  hw_data->hws[i] =
   devm_clk_hw_register_gate(dev, desc->name,
        data->name, 0, gate_base,
        desc->bit_idx,
        0, &clk_gate_lock);

  if (IS_ERR(hw_data->hws[i]))
   return dev_err_probe(dev, PTR_ERR(hw_data->hws[i]),
          "failed to register %s clock\n",
          desc->name);
 }

 return 0;
}

static int lan966x_clk_probe(struct platform_device *pdev)
{
 const struct lan966x_match_data *data;
 struct clk_hw_onecell_data *hw_data;
 struct device *dev = &pdev->dev;
 void __iomem *gate_base;
 struct resource *res;
 int i, ret;

 data = device_get_match_data(dev);
 if (!data)
  return -EINVAL;

 hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, data->num_total_clks),
          GFP_KERNEL);
 if (!hw_data)
  return -ENOMEM;

 base = devm_platform_ioremap_resource(pdev, 0);
 if (IS_ERR(base))
  return PTR_ERR(base);

 init.ops = &lan966x_gck_ops;

 hw_data->num = data->num_generic_clks;

 for (i = 0; i < data->num_generic_clks; i++) {
  init.name = data->clk_name[i];
  hw_data->hws[i] = lan966x_gck_clk_register(dev, i);
  if (IS_ERR(hw_data->hws[i])) {
   dev_err(dev, "failed to register %s clock\n",
    init.name);
   return PTR_ERR(hw_data->hws[i]);
  }
 }

 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 if (res) {
  gate_base = devm_ioremap_resource(&pdev->dev, res);
  if (IS_ERR(gate_base))
   return PTR_ERR(gate_base);

  hw_data->num = data->num_total_clks;

  ret = lan966x_gate_clk_register(dev, data, hw_data, gate_base);
  if (ret)
   return ret;
 }

 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
}

static const struct of_device_id lan966x_clk_dt_ids[] = {
 { .compatible = "microchip,lan966x-gck", .data = &lan966x_desc },
 { .compatible = "microchip,lan9691-gck", .data = &lan969x_desc },
 { }
};
MODULE_DEVICE_TABLE(of, lan966x_clk_dt_ids);

static struct platform_driver lan966x_clk_driver = {
 .probe  = lan966x_clk_probe,
 .driver = {
  .name = "lan966x-clk",
  .of_match_table = lan966x_clk_dt_ids,
 },
};
module_platform_driver(lan966x_clk_driver);

MODULE_AUTHOR("Kavyasree Kotagiri ");
MODULE_DESCRIPTION("LAN966X clock driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=76 H=93 G=84

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© 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.