Setting up Gclk in board config

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

I'm trying to add some code into my board setup.c to set the gclk0, and am having an issue.. code is here:

static void __init gclk_init(void)
{
   struct clk *gclk;
   unsigned long clkrate;

   gclk = clk_get(NULL, "gclk0");
   if (IS_ERR(gclk)) {
      printk("Error! GCLK0 not found!\n");
      return;
   }

   clk_enable(gclk);
   at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
   clkrate = clk_set_rate(gclk, 2000000);
   printk("GCLK0 set to: %lu Hz\n", clkrate);
}

To which I get:

GCLK0 set to: 0 Hz

What am I missing there? Something simple no doubt..

edit: clk_enable perhaps, trying that now..

edit2: nope, updated code too

edit3: I am a goofball. clk_set_rate just returns 0 if it works, not the actual rate.. I'll read that with clk_get_rate

*foreheadslap!*

Thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Awesome, works now with this:

static void __init gclk0_init(void)
{
   struct clk *gclk;
   unsigned long clkrate;
   int clken, clkset;

   gclk = clk_get(NULL, "gclk0");
   if (IS_ERR(gclk)) {
      printk("Error! GCLK0 not found!\n");
      return;
   }

   clken = clk_enable(gclk);
   if (clken != 0)
     printk("Clock not enabled: %d\n",clken);

   at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
   clkset = clk_set_rate(gclk, GCLK0_RATE);
   if (clkset != 0)
      printk("Clock not set: %d\n",clkset);
   clkrate = clk_get_rate(gclk);
   printk("GCLK0 set to: %lu Hz\n", clkrate);
}

comments still welcome if there are better ways to do it..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You might want to enable the clock after setting the clock rate to avoid unexpected behavior.

GCLK0_RATE may or may not be GCLK0_RATE. clk_round_rate(gclk, GCLK0_RATE) returns the actual rate that could be used. You might need that value for future reference. Then set the clock rate to either GCLK0_RATE or the actual rate returned by clk_round_rate().

Also, revision A of AP7000 has a nasty bug in conjunction with the Kernel code. DIVEN can't be read (it's always 0) and will be reset. Check the clock rate with clk_get_rate() if it matches the value of clk_round_rate().

A work around would be writing your own versions of genclk_mode() (switches DIVEN off) and genclk_get_rate() (doesn't know the real value of DIVEN, so it may return the wrong clock rate). I've modified those functions to suite my needs and put them into my own Kernel driver:

 static void mydriver_genclk_mode(struct clk *clk, int enabled)
{
	u32 control;

	control = pm_readl(GCCTRL(clk->index));
	if (enabled)
		control |= PM_BIT(CEN);
	else
		control &= ~PM_BIT(CEN);
	/* DIVEN always on for our needs */
	/* pm_writel(GCCTRL(clk->index), control); */
	pm_writel(GCCTRL(clk->index), control | PM_BIT(DIVEN));
}

static unsigned long mydriver_genclk_get_rate(struct clk *clk)
{
	u32 control;
	unsigned long div = 1;

	control = pm_readl(GCCTRL(clk->index));
	/* DIVEN always on for our needs */
	/* if (control & PM_BIT(DIVEN))
		div = 2 * (PM_BFEXT(DIV, control) + 1); */
	div = 2 * (PM_BFEXT(DIV, control) + 1);

	return clk->parent->get_rate(clk->parent) / div;
}

Now, all you have to do after clk_get():

	clk->mode = mydriver_genclk_mode;
	clk->get_rate = mydriver_genclk_get_rate;

Franco