You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
4.9 KiB
205 lines
4.9 KiB
/* |
|
* Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com |
|
* Author: Jyri Sarha <jsarha@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. |
|
* |
|
* Gpio gated clock implementation |
|
*/ |
|
|
|
#include <linux/clk-provider.h> |
|
#include <linux/module.h> |
|
#include <linux/slab.h> |
|
#include <linux/gpio.h> |
|
#include <linux/gpio/consumer.h> |
|
#include <linux/of_gpio.h> |
|
#include <linux/err.h> |
|
#include <linux/device.h> |
|
|
|
/** |
|
* DOC: basic gpio gated clock which can be enabled and disabled |
|
* with gpio output |
|
* Traits of this clock: |
|
* prepare - clk_(un)prepare only ensures parent is (un)prepared |
|
* enable - clk_enable and clk_disable are functional & control gpio |
|
* rate - inherits rate from parent. No clk_set_rate support |
|
* parent - fixed parent. No clk_set_parent support |
|
*/ |
|
|
|
#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw) |
|
|
|
static int clk_gpio_gate_enable(struct clk_hw *hw) |
|
{ |
|
struct clk_gpio *clk = to_clk_gpio(hw); |
|
|
|
gpiod_set_value(clk->gpiod, 1); |
|
|
|
return 0; |
|
} |
|
|
|
static void clk_gpio_gate_disable(struct clk_hw *hw) |
|
{ |
|
struct clk_gpio *clk = to_clk_gpio(hw); |
|
|
|
gpiod_set_value(clk->gpiod, 0); |
|
} |
|
|
|
static int clk_gpio_gate_is_enabled(struct clk_hw *hw) |
|
{ |
|
struct clk_gpio *clk = to_clk_gpio(hw); |
|
|
|
return gpiod_get_value(clk->gpiod); |
|
} |
|
|
|
const struct clk_ops clk_gpio_gate_ops = { |
|
.enable = clk_gpio_gate_enable, |
|
.disable = clk_gpio_gate_disable, |
|
.is_enabled = clk_gpio_gate_is_enabled, |
|
}; |
|
EXPORT_SYMBOL_GPL(clk_gpio_gate_ops); |
|
|
|
/** |
|
* clk_register_gpio - register a gpip clock with the clock framework |
|
* @dev: device that is registering this clock |
|
* @name: name of this clock |
|
* @parent_name: name of this clock's parent |
|
* @gpiod: gpio descriptor to gate this clock |
|
*/ |
|
struct clk *clk_register_gpio_gate(struct device *dev, const char *name, |
|
const char *parent_name, struct gpio_desc *gpiod, |
|
unsigned long flags) |
|
{ |
|
struct clk_gpio *clk_gpio = NULL; |
|
struct clk *clk = ERR_PTR(-EINVAL); |
|
struct clk_init_data init = { NULL }; |
|
unsigned long gpio_flags; |
|
int err; |
|
|
|
if (gpiod_is_active_low(gpiod)) |
|
gpio_flags = GPIOF_OUT_INIT_HIGH; |
|
else |
|
gpio_flags = GPIOF_OUT_INIT_LOW; |
|
|
|
if (dev) |
|
err = devm_gpio_request_one(dev, desc_to_gpio(gpiod), |
|
gpio_flags, name); |
|
else |
|
err = gpio_request_one(desc_to_gpio(gpiod), gpio_flags, name); |
|
|
|
if (err) { |
|
pr_err("%s: %s: Error requesting clock control gpio %u\n", |
|
__func__, name, desc_to_gpio(gpiod)); |
|
return ERR_PTR(err); |
|
} |
|
|
|
if (dev) |
|
clk_gpio = devm_kzalloc(dev, sizeof(struct clk_gpio), |
|
GFP_KERNEL); |
|
else |
|
clk_gpio = kzalloc(sizeof(struct clk_gpio), GFP_KERNEL); |
|
|
|
if (!clk_gpio) { |
|
clk = ERR_PTR(-ENOMEM); |
|
goto clk_register_gpio_gate_err; |
|
} |
|
|
|
init.name = name; |
|
init.ops = &clk_gpio_gate_ops; |
|
init.flags = flags | CLK_IS_BASIC; |
|
init.parent_names = (parent_name ? &parent_name : NULL); |
|
init.num_parents = (parent_name ? 1 : 0); |
|
|
|
clk_gpio->gpiod = gpiod; |
|
clk_gpio->hw.init = &init; |
|
|
|
clk = clk_register(dev, &clk_gpio->hw); |
|
|
|
if (!IS_ERR(clk)) |
|
return clk; |
|
|
|
if (!dev) |
|
kfree(clk_gpio); |
|
|
|
clk_register_gpio_gate_err: |
|
gpiod_put(gpiod); |
|
|
|
return clk; |
|
} |
|
EXPORT_SYMBOL_GPL(clk_register_gpio_gate); |
|
|
|
#ifdef CONFIG_OF |
|
/** |
|
* The clk_register_gpio_gate has to be delayed, because the EPROBE_DEFER |
|
* can not be handled properly at of_clk_init() call time. |
|
*/ |
|
|
|
struct clk_gpio_gate_delayed_register_data { |
|
struct device_node *node; |
|
struct mutex lock; |
|
struct clk *clk; |
|
}; |
|
|
|
static struct clk *of_clk_gpio_gate_delayed_register_get( |
|
struct of_phandle_args *clkspec, |
|
void *_data) |
|
{ |
|
struct clk_gpio_gate_delayed_register_data *data = _data; |
|
struct clk *clk; |
|
const char *clk_name = data->node->name; |
|
const char *parent_name; |
|
struct gpio_desc *gpiod; |
|
int gpio; |
|
|
|
mutex_lock(&data->lock); |
|
|
|
if (data->clk) { |
|
mutex_unlock(&data->lock); |
|
return data->clk; |
|
} |
|
|
|
gpio = of_get_named_gpio_flags(data->node, "enable-gpios", 0, NULL); |
|
if (gpio < 0) { |
|
mutex_unlock(&data->lock); |
|
if (gpio != -EPROBE_DEFER) |
|
pr_err("%s: %s: Can't get 'enable-gpios' DT property\n", |
|
__func__, clk_name); |
|
return ERR_PTR(gpio); |
|
} |
|
gpiod = gpio_to_desc(gpio); |
|
|
|
parent_name = of_clk_get_parent_name(data->node, 0); |
|
|
|
clk = clk_register_gpio_gate(NULL, clk_name, parent_name, gpiod, 0); |
|
if (IS_ERR(clk)) { |
|
mutex_unlock(&data->lock); |
|
return clk; |
|
} |
|
|
|
data->clk = clk; |
|
mutex_unlock(&data->lock); |
|
|
|
return clk; |
|
} |
|
|
|
/** |
|
* of_gpio_gate_clk_setup() - Setup function for gpio controlled clock |
|
*/ |
|
void __init of_gpio_gate_clk_setup(struct device_node *node) |
|
{ |
|
struct clk_gpio_gate_delayed_register_data *data; |
|
|
|
data = kzalloc(sizeof(struct clk_gpio_gate_delayed_register_data), |
|
GFP_KERNEL); |
|
if (!data) |
|
return; |
|
|
|
data->node = node; |
|
mutex_init(&data->lock); |
|
|
|
of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data); |
|
} |
|
EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup); |
|
CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup); |
|
#endif
|
|
|