1 /* 2 * Copyright 2015 Chen-Yu Tsai 3 * 4 * Chen-Yu Tsai <wens@csie.org> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #include <linux/clk-provider.h> 18 #include <linux/io.h> 19 #include <linux/of.h> 20 #include <linux/of_address.h> 21 #include <linux/reset-controller.h> 22 #include <linux/slab.h> 23 #include <linux/spinlock.h> 24 25 static DEFINE_SPINLOCK(ve_lock); 26 27 #define SUN4I_VE_ENABLE 31 28 #define SUN4I_VE_DIVIDER_SHIFT 16 29 #define SUN4I_VE_DIVIDER_WIDTH 3 30 #define SUN4I_VE_RESET 0 31 32 /** 33 * sunxi_ve_reset... - reset bit in ve clk registers handling 34 */ 35 36 struct ve_reset_data { 37 void __iomem *reg; 38 spinlock_t *lock; 39 struct reset_controller_dev rcdev; 40 }; 41 42 static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev, 43 unsigned long id) 44 { 45 struct ve_reset_data *data = container_of(rcdev, 46 struct ve_reset_data, 47 rcdev); 48 unsigned long flags; 49 u32 reg; 50 51 spin_lock_irqsave(data->lock, flags); 52 53 reg = readl(data->reg); 54 writel(reg & ~BIT(SUN4I_VE_RESET), data->reg); 55 56 spin_unlock_irqrestore(data->lock, flags); 57 58 return 0; 59 } 60 61 static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev, 62 unsigned long id) 63 { 64 struct ve_reset_data *data = container_of(rcdev, 65 struct ve_reset_data, 66 rcdev); 67 unsigned long flags; 68 u32 reg; 69 70 spin_lock_irqsave(data->lock, flags); 71 72 reg = readl(data->reg); 73 writel(reg | BIT(SUN4I_VE_RESET), data->reg); 74 75 spin_unlock_irqrestore(data->lock, flags); 76 77 return 0; 78 } 79 80 static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev, 81 const struct of_phandle_args *reset_spec) 82 { 83 if (WARN_ON(reset_spec->args_count != 0)) 84 return -EINVAL; 85 86 return 0; 87 } 88 89 static const struct reset_control_ops sunxi_ve_reset_ops = { 90 .assert = sunxi_ve_reset_assert, 91 .deassert = sunxi_ve_reset_deassert, 92 }; 93 94 static void __init sun4i_ve_clk_setup(struct device_node *node) 95 { 96 struct clk *clk; 97 struct clk_divider *div; 98 struct clk_gate *gate; 99 struct ve_reset_data *reset_data; 100 const char *parent; 101 const char *clk_name = node->name; 102 void __iomem *reg; 103 int err; 104 105 reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 106 if (IS_ERR(reg)) 107 return; 108 109 div = kzalloc(sizeof(*div), GFP_KERNEL); 110 if (!div) 111 goto err_unmap; 112 113 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 114 if (!gate) 115 goto err_free_div; 116 117 of_property_read_string(node, "clock-output-names", &clk_name); 118 parent = of_clk_get_parent_name(node, 0); 119 120 gate->reg = reg; 121 gate->bit_idx = SUN4I_VE_ENABLE; 122 gate->lock = &ve_lock; 123 124 div->reg = reg; 125 div->shift = SUN4I_VE_DIVIDER_SHIFT; 126 div->width = SUN4I_VE_DIVIDER_WIDTH; 127 div->lock = &ve_lock; 128 129 clk = clk_register_composite(NULL, clk_name, &parent, 1, 130 NULL, NULL, 131 &div->hw, &clk_divider_ops, 132 &gate->hw, &clk_gate_ops, 133 CLK_SET_RATE_PARENT); 134 if (IS_ERR(clk)) 135 goto err_free_gate; 136 137 err = of_clk_add_provider(node, of_clk_src_simple_get, clk); 138 if (err) 139 goto err_unregister_clk; 140 141 reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 142 if (!reset_data) 143 goto err_del_provider; 144 145 reset_data->reg = reg; 146 reset_data->lock = &ve_lock; 147 reset_data->rcdev.nr_resets = 1; 148 reset_data->rcdev.ops = &sunxi_ve_reset_ops; 149 reset_data->rcdev.of_node = node; 150 reset_data->rcdev.of_xlate = sunxi_ve_of_xlate; 151 reset_data->rcdev.of_reset_n_cells = 0; 152 err = reset_controller_register(&reset_data->rcdev); 153 if (err) 154 goto err_free_reset; 155 156 return; 157 158 err_free_reset: 159 kfree(reset_data); 160 err_del_provider: 161 of_clk_del_provider(node); 162 err_unregister_clk: 163 clk_unregister(clk); 164 err_free_gate: 165 kfree(gate); 166 err_free_div: 167 kfree(div); 168 err_unmap: 169 iounmap(reg); 170 } 171 CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk", 172 sun4i_ve_clk_setup); 173