[PATCH] clk: add LP8788 clock driver
Mike Turquette
mturquette at linaro.org
Mon Apr 1 21:12:36 EDT 2013
Quoting Kim, Milo (2013-03-20 17:37:14)
> LP8788 PMU consists of regulator, battery charger, RTC, ADC, backlight driver
> and current sinks.
> Here is additional driver, clock.
>
> * Clock source
> LP8788 clock is generated by two clock source.
> One is internal oscillator. The other is attached external crystal oscillator.
> LP8788 provides automatic clock source selection through the I2C register.
> This operation is executed in 'prepare' of common clock driver architecture.
>
> * Clock rate
> LP8788 generates a fixed 32768Hz clock which is used for the system.
>
> * Supported operation
> .prepare: Before the clock is enabled, automatic clock source selection is done
> by register access.
> .unprepare: No register for disable or remove the clock source, so do nothing.
> .is_enabled
> .recalc_rate: Fixed clock rate, 32.768KHz.
>
> * clk_register_fixed_rate() vs devm_clk_register() and clkdev_add()
> Fixed clock driver can be created by common clock helper function but
> but LP8788 should be built as a module.
> Using devm_clk_register() and clkdev_add() is more appropriate method to
> implement LP8788 clock driver.
>
> Signed-off-by: Milo(Woogyom) Kim <milo.kim at ti.com>
> ---
> drivers/clk/Kconfig | 7 ++
> drivers/clk/Makefile | 1 +
> drivers/clk/clk-lp8788.c | 186 ++++++++++++++++++++++++++++++++++++++++++++
> drivers/mfd/lp8788.c | 2 +
> include/linux/mfd/lp8788.h | 1 +
> 5 files changed, 197 insertions(+)
> create mode 100644 drivers/clk/clk-lp8788.c
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index a64caef..4b5109c 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -71,6 +71,13 @@ config COMMON_CLK_AXI_CLKGEN
> Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
> FPGAs. It is commonly used in Analog Devices' reference designs.
>
> +config CLK_LP8788
> + tristate "Clock driver for TI LP8788 PMU"
> + depends on MFD_LP8788
> + ---help---
> + Supports the clock subsystem of TI LP8788. It generates the 32KHz
> + clock output.
> +
> endmenu
>
> source "drivers/clk/mvebu/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 1c22f9d..06d0763 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -35,3 +35,4 @@ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
> obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
> obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
> obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
> +obj-$(CONFIG_CLK_LP8788) += clk-lp8788.o
> diff --git a/drivers/clk/clk-lp8788.c b/drivers/clk/clk-lp8788.c
> new file mode 100644
> index 0000000..de56887
> --- /dev/null
> +++ b/drivers/clk/clk-lp8788.c
> @@ -0,0 +1,186 @@
> +/*
> + * TI LP8788 Clock Driver
> + *
> + * Copyright 2013 Texas Instruments
> + *
> + * Author: Milo(Woogyom) Kim <milo.kim at ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/mfd/lp8788.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +
> +/* Registers */
> +#define LP8788_REG_STATUS 0x06
> +#define LP8788_OSC_WORKING 0x10
> +
> +#define LP8788_REG_CLKCTRL 0xA2
> +#define LP8788_CLKMODE_MASK 0x02
> +#define LP8788_CLKMODE_AUTO 0X02
> +
> +#define CLKOUT_32KHZ 32768
> +
> +struct lp8788_clk {
> + struct lp8788 *lp;
> + struct clk *clk;
> + struct clk_hw hw;
> + struct clk_lookup *lookup;
> + bool enabled;
> +};
> +
> +static struct lp8788_clk *to_lp8788_clk(struct clk_hw *hw)
> +{
> + return container_of(hw, struct lp8788_clk, hw);
> +}
Hi Milo,
Just FYI most folks are simply using a macro to do the above. E.g:
#define to_lp8788_clk(_hw) container_of(_hw, struct lp8788_clk, hw)
> +
> +static int lp8788_clk_prepare(struct clk_hw *hw)
> +{
> + struct lp8788_clk *pclk = to_lp8788_clk(hw);
> +
> + /* Automatic oscillator source selection */
> + return lp8788_update_bits(pclk->lp, LP8788_REG_CLKCTRL,
> + LP8788_CLKMODE_MASK, LP8788_CLKMODE_AUTO);
> +}
> +
> +static void lp8788_clk_unprepare(struct clk_hw *hw)
> +{
> + /* Do nothing */
> + return;
> +}
> +
> +static int lp8788_clk_is_enabled(struct clk_hw *hw)
> +{
> + struct lp8788_clk *pclk = to_lp8788_clk(hw);
> +
> + /*
> + * Do not use the LP8788 register access here, unsafe locking problem
> + * may occur during loading the driver. So stored varible is prefered.
> + */
What unsafe locking problem?
Also have you looked into using the new .is_prepare callback? See:
https://git.linaro.org/gitweb?p=people/mturquette/linux.git;a=commitdiff;h=3d6ee287a3e341c88eafd0b4620b12d640b3736b;hp=30ee400614385ac49f4c9b4bc03d77ff8f07a61e
> +
> + return pclk->enabled;
Why provide a .is_enabled callback when there are no .enable or .disable
callbacks?
Regards,
Mike
More information about the linux-arm-kernel
mailing list