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/of.h> 19 #include <linux/of_address.h> 20 #include <linux/reset-controller.h> 21 #include <linux/slab.h> 22 #include <linux/spinlock.h> 23 24 static DEFINE_SPINLOCK(ve_lock); 25 26 #define SUN4I_VE_ENABLE 31 27 #define SUN4I_VE_DIVIDER_SHIFT 16 28 #define SUN4I_VE_DIVIDER_WIDTH 3 29 #define SUN4I_VE_RESET 0 30 31 /** 32 * sunxi_ve_reset... - reset bit in ve clk registers handling 33 */ 34 35 struct ve_reset_data { 36 void __iomem *reg; 37 spinlock_t *lock; 38 struct reset_controller_dev rcdev; 39 }; 40 41 static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev, 42 unsigned long id) 43 { 44 struct ve_reset_data *data = container_of(rcdev, 45 struct ve_reset_data, 46 rcdev); 47 unsigned long flags; 48 u32 reg; 49 50 spin_lock_irqsave(data->lock, flags); 51 52 reg = readl(data->reg); 53 writel(reg & ~BIT(SUN4I_VE_RESET), data->reg); 54 55 spin_unlock_irqrestore(data->lock, flags); 56 57 return 0; 58 } 59 60 static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev, 61 unsigned long id) 62 { 63 struct ve_reset_data *data = container_of(rcdev, 64 struct ve_reset_data, 65 rcdev); 66 unsigned long flags; 67 u32 reg; 68 69 spin_lock_irqsave(data->lock, flags); 70 71 reg = readl(data->reg); 72 writel(reg | BIT(SUN4I_VE_RESET), data->reg); 73 74 spin_unlock_irqrestore(data->lock, flags); 75 76 return 0; 77 } 78 79 static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev, 80 const struct of_phandle_args *reset_spec) 81 { 82 if (WARN_ON(reset_spec->args_count != 0)) 83 return -EINVAL; 84 85 return 0; 86 } 87 88 static const struct reset_control_ops sunxi_ve_reset_ops = { 89 .assert = sunxi_ve_reset_assert, 90 .deassert = sunxi_ve_reset_deassert, 91 }; 92 93 static void __init sun4i_ve_clk_setup(struct device_node *node) 94 { 95 struct clk *clk; 96 struct clk_divider *div; 97 struct clk_gate *gate; 98 struct ve_reset_data *reset_data; 99 const char *parent; 100 const char *clk_name = node->name; 101 void __iomem *reg; 102 int err; 103 104 reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 105 if (IS_ERR(reg)) 106 return; 107 108 div = kzalloc(sizeof(*div), GFP_KERNEL); 109 if (!div) 110 goto err_unmap; 111 112 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 113 if (!gate) 114 goto err_free_div; 115 116 of_property_read_string(node, "clock-output-names", &clk_name); 117 parent = of_clk_get_parent_name(node, 0); 118 119 gate->reg = reg; 120 gate->bit_idx = SUN4I_VE_ENABLE; 121 gate->lock = &ve_lock; 122 123 div->reg = reg; 124 div->shift = SUN4I_VE_DIVIDER_SHIFT; 125 div->width = SUN4I_VE_DIVIDER_WIDTH; 126 div->lock = &ve_lock; 127 128 clk = clk_register_composite(NULL, clk_name, &parent, 1, 129 NULL, NULL, 130 &div->hw, &clk_divider_ops, 131 &gate->hw, &clk_gate_ops, 132 CLK_SET_RATE_PARENT); 133 if (IS_ERR(clk)) 134 goto err_free_gate; 135 136 err = of_clk_add_provider(node, of_clk_src_simple_get, clk); 137 if (err) 138 goto err_unregister_clk; 139 140 reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 141 if (!reset_data) 142 goto err_del_provider; 143 144 reset_data->reg = reg; 145 reset_data->lock = &ve_lock; 146 reset_data->rcdev.nr_resets = 1; 147 reset_data->rcdev.ops = &sunxi_ve_reset_ops; 148 reset_data->rcdev.of_node = node; 149 reset_data->rcdev.of_xlate = sunxi_ve_of_xlate; 150 reset_data->rcdev.of_reset_n_cells = 0; 151 err = reset_controller_register(&reset_data->rcdev); 152 if (err) 153 goto err_free_reset; 154 155 return; 156 157 err_free_reset: 158 kfree(reset_data); 159 err_del_provider: 160 of_clk_del_provider(node); 161 err_unregister_clk: 162 clk_unregister(clk); 163 err_free_gate: 164 kfree(gate); 165 err_free_div: 166 kfree(div); 167 err_unmap: 168 iounmap(reg); 169 } 170 CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk", 171 sun4i_ve_clk_setup); 172