1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 23cdd9f5cSChen-Yu Tsai /* 33cdd9f5cSChen-Yu Tsai * Copyright 2015 Chen-Yu Tsai 43cdd9f5cSChen-Yu Tsai * 53cdd9f5cSChen-Yu Tsai * Chen-Yu Tsai <wens@csie.org> 63cdd9f5cSChen-Yu Tsai */ 73cdd9f5cSChen-Yu Tsai 83cdd9f5cSChen-Yu Tsai #include <linux/clk-provider.h> 962e59c4eSStephen Boyd #include <linux/io.h> 103cdd9f5cSChen-Yu Tsai #include <linux/of.h> 113cdd9f5cSChen-Yu Tsai #include <linux/of_address.h> 123cdd9f5cSChen-Yu Tsai #include <linux/reset-controller.h> 133cdd9f5cSChen-Yu Tsai #include <linux/slab.h> 143cdd9f5cSChen-Yu Tsai #include <linux/spinlock.h> 153cdd9f5cSChen-Yu Tsai 163cdd9f5cSChen-Yu Tsai static DEFINE_SPINLOCK(ve_lock); 173cdd9f5cSChen-Yu Tsai 183cdd9f5cSChen-Yu Tsai #define SUN4I_VE_ENABLE 31 193cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_SHIFT 16 203cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_WIDTH 3 213cdd9f5cSChen-Yu Tsai #define SUN4I_VE_RESET 0 223cdd9f5cSChen-Yu Tsai 23*29f476d4SLee Jones /* 243cdd9f5cSChen-Yu Tsai * sunxi_ve_reset... - reset bit in ve clk registers handling 253cdd9f5cSChen-Yu Tsai */ 263cdd9f5cSChen-Yu Tsai 273cdd9f5cSChen-Yu Tsai struct ve_reset_data { 283cdd9f5cSChen-Yu Tsai void __iomem *reg; 293cdd9f5cSChen-Yu Tsai spinlock_t *lock; 303cdd9f5cSChen-Yu Tsai struct reset_controller_dev rcdev; 313cdd9f5cSChen-Yu Tsai }; 323cdd9f5cSChen-Yu Tsai 333cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev, 343cdd9f5cSChen-Yu Tsai unsigned long id) 353cdd9f5cSChen-Yu Tsai { 363cdd9f5cSChen-Yu Tsai struct ve_reset_data *data = container_of(rcdev, 373cdd9f5cSChen-Yu Tsai struct ve_reset_data, 383cdd9f5cSChen-Yu Tsai rcdev); 393cdd9f5cSChen-Yu Tsai unsigned long flags; 403cdd9f5cSChen-Yu Tsai u32 reg; 413cdd9f5cSChen-Yu Tsai 423cdd9f5cSChen-Yu Tsai spin_lock_irqsave(data->lock, flags); 433cdd9f5cSChen-Yu Tsai 443cdd9f5cSChen-Yu Tsai reg = readl(data->reg); 453cdd9f5cSChen-Yu Tsai writel(reg & ~BIT(SUN4I_VE_RESET), data->reg); 463cdd9f5cSChen-Yu Tsai 473cdd9f5cSChen-Yu Tsai spin_unlock_irqrestore(data->lock, flags); 483cdd9f5cSChen-Yu Tsai 493cdd9f5cSChen-Yu Tsai return 0; 503cdd9f5cSChen-Yu Tsai } 513cdd9f5cSChen-Yu Tsai 523cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev, 533cdd9f5cSChen-Yu Tsai unsigned long id) 543cdd9f5cSChen-Yu Tsai { 553cdd9f5cSChen-Yu Tsai struct ve_reset_data *data = container_of(rcdev, 563cdd9f5cSChen-Yu Tsai struct ve_reset_data, 573cdd9f5cSChen-Yu Tsai rcdev); 583cdd9f5cSChen-Yu Tsai unsigned long flags; 593cdd9f5cSChen-Yu Tsai u32 reg; 603cdd9f5cSChen-Yu Tsai 613cdd9f5cSChen-Yu Tsai spin_lock_irqsave(data->lock, flags); 623cdd9f5cSChen-Yu Tsai 633cdd9f5cSChen-Yu Tsai reg = readl(data->reg); 643cdd9f5cSChen-Yu Tsai writel(reg | BIT(SUN4I_VE_RESET), data->reg); 653cdd9f5cSChen-Yu Tsai 663cdd9f5cSChen-Yu Tsai spin_unlock_irqrestore(data->lock, flags); 673cdd9f5cSChen-Yu Tsai 683cdd9f5cSChen-Yu Tsai return 0; 693cdd9f5cSChen-Yu Tsai } 703cdd9f5cSChen-Yu Tsai 713cdd9f5cSChen-Yu Tsai static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev, 723cdd9f5cSChen-Yu Tsai const struct of_phandle_args *reset_spec) 733cdd9f5cSChen-Yu Tsai { 743cdd9f5cSChen-Yu Tsai if (WARN_ON(reset_spec->args_count != 0)) 753cdd9f5cSChen-Yu Tsai return -EINVAL; 763cdd9f5cSChen-Yu Tsai 773cdd9f5cSChen-Yu Tsai return 0; 783cdd9f5cSChen-Yu Tsai } 793cdd9f5cSChen-Yu Tsai 805e7bc9c6SPhilipp Zabel static const struct reset_control_ops sunxi_ve_reset_ops = { 813cdd9f5cSChen-Yu Tsai .assert = sunxi_ve_reset_assert, 823cdd9f5cSChen-Yu Tsai .deassert = sunxi_ve_reset_deassert, 833cdd9f5cSChen-Yu Tsai }; 843cdd9f5cSChen-Yu Tsai 853cdd9f5cSChen-Yu Tsai static void __init sun4i_ve_clk_setup(struct device_node *node) 863cdd9f5cSChen-Yu Tsai { 873cdd9f5cSChen-Yu Tsai struct clk *clk; 883cdd9f5cSChen-Yu Tsai struct clk_divider *div; 893cdd9f5cSChen-Yu Tsai struct clk_gate *gate; 903cdd9f5cSChen-Yu Tsai struct ve_reset_data *reset_data; 913cdd9f5cSChen-Yu Tsai const char *parent; 923cdd9f5cSChen-Yu Tsai const char *clk_name = node->name; 933cdd9f5cSChen-Yu Tsai void __iomem *reg; 943cdd9f5cSChen-Yu Tsai int err; 953cdd9f5cSChen-Yu Tsai 963cdd9f5cSChen-Yu Tsai reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 973cdd9f5cSChen-Yu Tsai if (IS_ERR(reg)) 983cdd9f5cSChen-Yu Tsai return; 993cdd9f5cSChen-Yu Tsai 1003cdd9f5cSChen-Yu Tsai div = kzalloc(sizeof(*div), GFP_KERNEL); 1013cdd9f5cSChen-Yu Tsai if (!div) 1023cdd9f5cSChen-Yu Tsai goto err_unmap; 1033cdd9f5cSChen-Yu Tsai 1043cdd9f5cSChen-Yu Tsai gate = kzalloc(sizeof(*gate), GFP_KERNEL); 1053cdd9f5cSChen-Yu Tsai if (!gate) 1063cdd9f5cSChen-Yu Tsai goto err_free_div; 1073cdd9f5cSChen-Yu Tsai 1083cdd9f5cSChen-Yu Tsai of_property_read_string(node, "clock-output-names", &clk_name); 1093cdd9f5cSChen-Yu Tsai parent = of_clk_get_parent_name(node, 0); 1103cdd9f5cSChen-Yu Tsai 1113cdd9f5cSChen-Yu Tsai gate->reg = reg; 1123cdd9f5cSChen-Yu Tsai gate->bit_idx = SUN4I_VE_ENABLE; 1133cdd9f5cSChen-Yu Tsai gate->lock = &ve_lock; 1143cdd9f5cSChen-Yu Tsai 1153cdd9f5cSChen-Yu Tsai div->reg = reg; 1163cdd9f5cSChen-Yu Tsai div->shift = SUN4I_VE_DIVIDER_SHIFT; 1173cdd9f5cSChen-Yu Tsai div->width = SUN4I_VE_DIVIDER_WIDTH; 1183cdd9f5cSChen-Yu Tsai div->lock = &ve_lock; 1193cdd9f5cSChen-Yu Tsai 1203cdd9f5cSChen-Yu Tsai clk = clk_register_composite(NULL, clk_name, &parent, 1, 1213cdd9f5cSChen-Yu Tsai NULL, NULL, 1223cdd9f5cSChen-Yu Tsai &div->hw, &clk_divider_ops, 1233cdd9f5cSChen-Yu Tsai &gate->hw, &clk_gate_ops, 1243cdd9f5cSChen-Yu Tsai CLK_SET_RATE_PARENT); 1253cdd9f5cSChen-Yu Tsai if (IS_ERR(clk)) 1263cdd9f5cSChen-Yu Tsai goto err_free_gate; 1273cdd9f5cSChen-Yu Tsai 1283cdd9f5cSChen-Yu Tsai err = of_clk_add_provider(node, of_clk_src_simple_get, clk); 1293cdd9f5cSChen-Yu Tsai if (err) 1303cdd9f5cSChen-Yu Tsai goto err_unregister_clk; 1313cdd9f5cSChen-Yu Tsai 1323cdd9f5cSChen-Yu Tsai reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 1333cdd9f5cSChen-Yu Tsai if (!reset_data) 1343cdd9f5cSChen-Yu Tsai goto err_del_provider; 1353cdd9f5cSChen-Yu Tsai 1363cdd9f5cSChen-Yu Tsai reset_data->reg = reg; 1373cdd9f5cSChen-Yu Tsai reset_data->lock = &ve_lock; 1383cdd9f5cSChen-Yu Tsai reset_data->rcdev.nr_resets = 1; 1393cdd9f5cSChen-Yu Tsai reset_data->rcdev.ops = &sunxi_ve_reset_ops; 1403cdd9f5cSChen-Yu Tsai reset_data->rcdev.of_node = node; 1413cdd9f5cSChen-Yu Tsai reset_data->rcdev.of_xlate = sunxi_ve_of_xlate; 1423cdd9f5cSChen-Yu Tsai reset_data->rcdev.of_reset_n_cells = 0; 1433cdd9f5cSChen-Yu Tsai err = reset_controller_register(&reset_data->rcdev); 1443cdd9f5cSChen-Yu Tsai if (err) 1453cdd9f5cSChen-Yu Tsai goto err_free_reset; 1463cdd9f5cSChen-Yu Tsai 1473cdd9f5cSChen-Yu Tsai return; 1483cdd9f5cSChen-Yu Tsai 1493cdd9f5cSChen-Yu Tsai err_free_reset: 1503cdd9f5cSChen-Yu Tsai kfree(reset_data); 1513cdd9f5cSChen-Yu Tsai err_del_provider: 1523cdd9f5cSChen-Yu Tsai of_clk_del_provider(node); 1533cdd9f5cSChen-Yu Tsai err_unregister_clk: 1543cdd9f5cSChen-Yu Tsai clk_unregister(clk); 1553cdd9f5cSChen-Yu Tsai err_free_gate: 1563cdd9f5cSChen-Yu Tsai kfree(gate); 1573cdd9f5cSChen-Yu Tsai err_free_div: 1583cdd9f5cSChen-Yu Tsai kfree(div); 1593cdd9f5cSChen-Yu Tsai err_unmap: 1603cdd9f5cSChen-Yu Tsai iounmap(reg); 1613cdd9f5cSChen-Yu Tsai } 1623cdd9f5cSChen-Yu Tsai CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk", 1633cdd9f5cSChen-Yu Tsai sun4i_ve_clk_setup); 164