13cdd9f5cSChen-Yu Tsai /* 23cdd9f5cSChen-Yu Tsai * Copyright 2015 Chen-Yu Tsai 33cdd9f5cSChen-Yu Tsai * 43cdd9f5cSChen-Yu Tsai * Chen-Yu Tsai <wens@csie.org> 53cdd9f5cSChen-Yu Tsai * 63cdd9f5cSChen-Yu Tsai * This program is free software; you can redistribute it and/or modify 73cdd9f5cSChen-Yu Tsai * it under the terms of the GNU General Public License as published by 83cdd9f5cSChen-Yu Tsai * the Free Software Foundation; either version 2 of the License, or 93cdd9f5cSChen-Yu Tsai * (at your option) any later version. 103cdd9f5cSChen-Yu Tsai * 113cdd9f5cSChen-Yu Tsai * This program is distributed in the hope that it will be useful, 123cdd9f5cSChen-Yu Tsai * but WITHOUT ANY WARRANTY; without even the implied warranty of 133cdd9f5cSChen-Yu Tsai * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 143cdd9f5cSChen-Yu Tsai * GNU General Public License for more details. 153cdd9f5cSChen-Yu Tsai */ 163cdd9f5cSChen-Yu Tsai 173cdd9f5cSChen-Yu Tsai #include <linux/clk-provider.h> 18*62e59c4eSStephen Boyd #include <linux/io.h> 193cdd9f5cSChen-Yu Tsai #include <linux/of.h> 203cdd9f5cSChen-Yu Tsai #include <linux/of_address.h> 213cdd9f5cSChen-Yu Tsai #include <linux/reset-controller.h> 223cdd9f5cSChen-Yu Tsai #include <linux/slab.h> 233cdd9f5cSChen-Yu Tsai #include <linux/spinlock.h> 243cdd9f5cSChen-Yu Tsai 253cdd9f5cSChen-Yu Tsai static DEFINE_SPINLOCK(ve_lock); 263cdd9f5cSChen-Yu Tsai 273cdd9f5cSChen-Yu Tsai #define SUN4I_VE_ENABLE 31 283cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_SHIFT 16 293cdd9f5cSChen-Yu Tsai #define SUN4I_VE_DIVIDER_WIDTH 3 303cdd9f5cSChen-Yu Tsai #define SUN4I_VE_RESET 0 313cdd9f5cSChen-Yu Tsai 323cdd9f5cSChen-Yu Tsai /** 333cdd9f5cSChen-Yu Tsai * sunxi_ve_reset... - reset bit in ve clk registers handling 343cdd9f5cSChen-Yu Tsai */ 353cdd9f5cSChen-Yu Tsai 363cdd9f5cSChen-Yu Tsai struct ve_reset_data { 373cdd9f5cSChen-Yu Tsai void __iomem *reg; 383cdd9f5cSChen-Yu Tsai spinlock_t *lock; 393cdd9f5cSChen-Yu Tsai struct reset_controller_dev rcdev; 403cdd9f5cSChen-Yu Tsai }; 413cdd9f5cSChen-Yu Tsai 423cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_assert(struct reset_controller_dev *rcdev, 433cdd9f5cSChen-Yu Tsai unsigned long id) 443cdd9f5cSChen-Yu Tsai { 453cdd9f5cSChen-Yu Tsai struct ve_reset_data *data = container_of(rcdev, 463cdd9f5cSChen-Yu Tsai struct ve_reset_data, 473cdd9f5cSChen-Yu Tsai rcdev); 483cdd9f5cSChen-Yu Tsai unsigned long flags; 493cdd9f5cSChen-Yu Tsai u32 reg; 503cdd9f5cSChen-Yu Tsai 513cdd9f5cSChen-Yu Tsai spin_lock_irqsave(data->lock, flags); 523cdd9f5cSChen-Yu Tsai 533cdd9f5cSChen-Yu Tsai reg = readl(data->reg); 543cdd9f5cSChen-Yu Tsai writel(reg & ~BIT(SUN4I_VE_RESET), data->reg); 553cdd9f5cSChen-Yu Tsai 563cdd9f5cSChen-Yu Tsai spin_unlock_irqrestore(data->lock, flags); 573cdd9f5cSChen-Yu Tsai 583cdd9f5cSChen-Yu Tsai return 0; 593cdd9f5cSChen-Yu Tsai } 603cdd9f5cSChen-Yu Tsai 613cdd9f5cSChen-Yu Tsai static int sunxi_ve_reset_deassert(struct reset_controller_dev *rcdev, 623cdd9f5cSChen-Yu Tsai unsigned long id) 633cdd9f5cSChen-Yu Tsai { 643cdd9f5cSChen-Yu Tsai struct ve_reset_data *data = container_of(rcdev, 653cdd9f5cSChen-Yu Tsai struct ve_reset_data, 663cdd9f5cSChen-Yu Tsai rcdev); 673cdd9f5cSChen-Yu Tsai unsigned long flags; 683cdd9f5cSChen-Yu Tsai u32 reg; 693cdd9f5cSChen-Yu Tsai 703cdd9f5cSChen-Yu Tsai spin_lock_irqsave(data->lock, flags); 713cdd9f5cSChen-Yu Tsai 723cdd9f5cSChen-Yu Tsai reg = readl(data->reg); 733cdd9f5cSChen-Yu Tsai writel(reg | BIT(SUN4I_VE_RESET), data->reg); 743cdd9f5cSChen-Yu Tsai 753cdd9f5cSChen-Yu Tsai spin_unlock_irqrestore(data->lock, flags); 763cdd9f5cSChen-Yu Tsai 773cdd9f5cSChen-Yu Tsai return 0; 783cdd9f5cSChen-Yu Tsai } 793cdd9f5cSChen-Yu Tsai 803cdd9f5cSChen-Yu Tsai static int sunxi_ve_of_xlate(struct reset_controller_dev *rcdev, 813cdd9f5cSChen-Yu Tsai const struct of_phandle_args *reset_spec) 823cdd9f5cSChen-Yu Tsai { 833cdd9f5cSChen-Yu Tsai if (WARN_ON(reset_spec->args_count != 0)) 843cdd9f5cSChen-Yu Tsai return -EINVAL; 853cdd9f5cSChen-Yu Tsai 863cdd9f5cSChen-Yu Tsai return 0; 873cdd9f5cSChen-Yu Tsai } 883cdd9f5cSChen-Yu Tsai 895e7bc9c6SPhilipp Zabel static const struct reset_control_ops sunxi_ve_reset_ops = { 903cdd9f5cSChen-Yu Tsai .assert = sunxi_ve_reset_assert, 913cdd9f5cSChen-Yu Tsai .deassert = sunxi_ve_reset_deassert, 923cdd9f5cSChen-Yu Tsai }; 933cdd9f5cSChen-Yu Tsai 943cdd9f5cSChen-Yu Tsai static void __init sun4i_ve_clk_setup(struct device_node *node) 953cdd9f5cSChen-Yu Tsai { 963cdd9f5cSChen-Yu Tsai struct clk *clk; 973cdd9f5cSChen-Yu Tsai struct clk_divider *div; 983cdd9f5cSChen-Yu Tsai struct clk_gate *gate; 993cdd9f5cSChen-Yu Tsai struct ve_reset_data *reset_data; 1003cdd9f5cSChen-Yu Tsai const char *parent; 1013cdd9f5cSChen-Yu Tsai const char *clk_name = node->name; 1023cdd9f5cSChen-Yu Tsai void __iomem *reg; 1033cdd9f5cSChen-Yu Tsai int err; 1043cdd9f5cSChen-Yu Tsai 1053cdd9f5cSChen-Yu Tsai reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 1063cdd9f5cSChen-Yu Tsai if (IS_ERR(reg)) 1073cdd9f5cSChen-Yu Tsai return; 1083cdd9f5cSChen-Yu Tsai 1093cdd9f5cSChen-Yu Tsai div = kzalloc(sizeof(*div), GFP_KERNEL); 1103cdd9f5cSChen-Yu Tsai if (!div) 1113cdd9f5cSChen-Yu Tsai goto err_unmap; 1123cdd9f5cSChen-Yu Tsai 1133cdd9f5cSChen-Yu Tsai gate = kzalloc(sizeof(*gate), GFP_KERNEL); 1143cdd9f5cSChen-Yu Tsai if (!gate) 1153cdd9f5cSChen-Yu Tsai goto err_free_div; 1163cdd9f5cSChen-Yu Tsai 1173cdd9f5cSChen-Yu Tsai of_property_read_string(node, "clock-output-names", &clk_name); 1183cdd9f5cSChen-Yu Tsai parent = of_clk_get_parent_name(node, 0); 1193cdd9f5cSChen-Yu Tsai 1203cdd9f5cSChen-Yu Tsai gate->reg = reg; 1213cdd9f5cSChen-Yu Tsai gate->bit_idx = SUN4I_VE_ENABLE; 1223cdd9f5cSChen-Yu Tsai gate->lock = &ve_lock; 1233cdd9f5cSChen-Yu Tsai 1243cdd9f5cSChen-Yu Tsai div->reg = reg; 1253cdd9f5cSChen-Yu Tsai div->shift = SUN4I_VE_DIVIDER_SHIFT; 1263cdd9f5cSChen-Yu Tsai div->width = SUN4I_VE_DIVIDER_WIDTH; 1273cdd9f5cSChen-Yu Tsai div->lock = &ve_lock; 1283cdd9f5cSChen-Yu Tsai 1293cdd9f5cSChen-Yu Tsai clk = clk_register_composite(NULL, clk_name, &parent, 1, 1303cdd9f5cSChen-Yu Tsai NULL, NULL, 1313cdd9f5cSChen-Yu Tsai &div->hw, &clk_divider_ops, 1323cdd9f5cSChen-Yu Tsai &gate->hw, &clk_gate_ops, 1333cdd9f5cSChen-Yu Tsai CLK_SET_RATE_PARENT); 1343cdd9f5cSChen-Yu Tsai if (IS_ERR(clk)) 1353cdd9f5cSChen-Yu Tsai goto err_free_gate; 1363cdd9f5cSChen-Yu Tsai 1373cdd9f5cSChen-Yu Tsai err = of_clk_add_provider(node, of_clk_src_simple_get, clk); 1383cdd9f5cSChen-Yu Tsai if (err) 1393cdd9f5cSChen-Yu Tsai goto err_unregister_clk; 1403cdd9f5cSChen-Yu Tsai 1413cdd9f5cSChen-Yu Tsai reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 1423cdd9f5cSChen-Yu Tsai if (!reset_data) 1433cdd9f5cSChen-Yu Tsai goto err_del_provider; 1443cdd9f5cSChen-Yu Tsai 1453cdd9f5cSChen-Yu Tsai reset_data->reg = reg; 1463cdd9f5cSChen-Yu Tsai reset_data->lock = &ve_lock; 1473cdd9f5cSChen-Yu Tsai reset_data->rcdev.nr_resets = 1; 1483cdd9f5cSChen-Yu Tsai reset_data->rcdev.ops = &sunxi_ve_reset_ops; 1493cdd9f5cSChen-Yu Tsai reset_data->rcdev.of_node = node; 1503cdd9f5cSChen-Yu Tsai reset_data->rcdev.of_xlate = sunxi_ve_of_xlate; 1513cdd9f5cSChen-Yu Tsai reset_data->rcdev.of_reset_n_cells = 0; 1523cdd9f5cSChen-Yu Tsai err = reset_controller_register(&reset_data->rcdev); 1533cdd9f5cSChen-Yu Tsai if (err) 1543cdd9f5cSChen-Yu Tsai goto err_free_reset; 1553cdd9f5cSChen-Yu Tsai 1563cdd9f5cSChen-Yu Tsai return; 1573cdd9f5cSChen-Yu Tsai 1583cdd9f5cSChen-Yu Tsai err_free_reset: 1593cdd9f5cSChen-Yu Tsai kfree(reset_data); 1603cdd9f5cSChen-Yu Tsai err_del_provider: 1613cdd9f5cSChen-Yu Tsai of_clk_del_provider(node); 1623cdd9f5cSChen-Yu Tsai err_unregister_clk: 1633cdd9f5cSChen-Yu Tsai clk_unregister(clk); 1643cdd9f5cSChen-Yu Tsai err_free_gate: 1653cdd9f5cSChen-Yu Tsai kfree(gate); 1663cdd9f5cSChen-Yu Tsai err_free_div: 1673cdd9f5cSChen-Yu Tsai kfree(div); 1683cdd9f5cSChen-Yu Tsai err_unmap: 1693cdd9f5cSChen-Yu Tsai iounmap(reg); 1703cdd9f5cSChen-Yu Tsai } 1713cdd9f5cSChen-Yu Tsai CLK_OF_DECLARE(sun4i_ve, "allwinner,sun4i-a10-ve-clk", 1723cdd9f5cSChen-Yu Tsai sun4i_ve_clk_setup); 173