1 /* 2 * Copyright (C) 2014 Chen-Yu Tsai 3 * Author: Chen-Yu Tsai <wens@csie.org> 4 * 5 * Allwinner A23 APB0 clock driver 6 * 7 * License Terms: GNU General Public License v2 8 * 9 * Based on clk-sun6i-apb0.c 10 * Allwinner A31 APB0 clock driver 11 * 12 * Copyright (C) 2014 Free Electrons 13 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 14 * 15 */ 16 17 #include <linux/clk-provider.h> 18 #include <linux/init.h> 19 #include <linux/io.h> 20 #include <linux/of.h> 21 #include <linux/of_address.h> 22 #include <linux/platform_device.h> 23 24 static struct clk *sun8i_a23_apb0_register(struct device_node *node, 25 void __iomem *reg) 26 { 27 const char *clk_name = node->name; 28 const char *clk_parent; 29 struct clk *clk; 30 int ret; 31 32 clk_parent = of_clk_get_parent_name(node, 0); 33 if (!clk_parent) 34 return ERR_PTR(-EINVAL); 35 36 of_property_read_string(node, "clock-output-names", &clk_name); 37 38 /* The A23 APB0 clock is a standard 2 bit wide divider clock */ 39 clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg, 40 0, 2, 0, NULL); 41 if (IS_ERR(clk)) 42 return clk; 43 44 ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); 45 if (ret) 46 goto err_unregister; 47 48 return clk; 49 50 err_unregister: 51 clk_unregister_divider(clk); 52 53 return ERR_PTR(ret); 54 } 55 56 static void sun8i_a23_apb0_setup(struct device_node *node) 57 { 58 void __iomem *reg; 59 struct resource res; 60 struct clk *clk; 61 62 reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 63 if (IS_ERR(reg)) { 64 /* 65 * This happens with clk nodes instantiated through mfd, 66 * as those do not have their resources assigned in the 67 * device tree. Do not print an error in this case. 68 */ 69 if (PTR_ERR(reg) != -EINVAL) 70 pr_err("Could not get registers for a23-apb0-clk\n"); 71 72 return; 73 } 74 75 clk = sun8i_a23_apb0_register(node, reg); 76 if (IS_ERR(clk)) 77 goto err_unmap; 78 79 return; 80 81 err_unmap: 82 iounmap(reg); 83 of_address_to_resource(node, 0, &res); 84 release_mem_region(res.start, resource_size(&res)); 85 } 86 CLK_OF_DECLARE_DRIVER(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk", 87 sun8i_a23_apb0_setup); 88 89 static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev) 90 { 91 struct device_node *np = pdev->dev.of_node; 92 struct resource *r; 93 void __iomem *reg; 94 struct clk *clk; 95 96 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 97 reg = devm_ioremap_resource(&pdev->dev, r); 98 if (IS_ERR(reg)) 99 return PTR_ERR(reg); 100 101 clk = sun8i_a23_apb0_register(np, reg); 102 return PTR_ERR_OR_ZERO(clk); 103 } 104 105 static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { 106 { .compatible = "allwinner,sun8i-a23-apb0-clk" }, 107 { /* sentinel */ } 108 }; 109 110 static struct platform_driver sun8i_a23_apb0_clk_driver = { 111 .driver = { 112 .name = "sun8i-a23-apb0-clk", 113 .of_match_table = sun8i_a23_apb0_clk_dt_ids, 114 }, 115 .probe = sun8i_a23_apb0_clk_probe, 116 }; 117 builtin_platform_driver(sun8i_a23_apb0_clk_driver); 118