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/module.h> 19 #include <linux/of.h> 20 #include <linux/of_address.h> 21 #include <linux/platform_device.h> 22 23 static struct clk *sun8i_a23_apb0_register(struct device_node *node, 24 void __iomem *reg) 25 { 26 const char *clk_name = node->name; 27 const char *clk_parent; 28 struct clk *clk; 29 int ret; 30 31 clk_parent = of_clk_get_parent_name(node, 0); 32 if (!clk_parent) 33 return ERR_PTR(-EINVAL); 34 35 of_property_read_string(node, "clock-output-names", &clk_name); 36 37 /* The A23 APB0 clock is a standard 2 bit wide divider clock */ 38 clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg, 39 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL); 40 if (IS_ERR(clk)) 41 return clk; 42 43 ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); 44 if (ret) 45 goto err_unregister; 46 47 return clk; 48 49 err_unregister: 50 clk_unregister_divider(clk); 51 52 return ERR_PTR(ret); 53 } 54 55 static void sun8i_a23_apb0_setup(struct device_node *node) 56 { 57 void __iomem *reg; 58 struct resource res; 59 struct clk *clk; 60 61 reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 62 if (IS_ERR(reg)) { 63 /* 64 * This happens with clk nodes instantiated through mfd, 65 * as those do not have their resources assigned in the 66 * device tree. Do not print an error in this case. 67 */ 68 if (PTR_ERR(reg) != -EINVAL) 69 pr_err("Could not get registers for a23-apb0-clk\n"); 70 71 return; 72 } 73 74 clk = sun8i_a23_apb0_register(node, reg); 75 if (IS_ERR(clk)) 76 goto err_unmap; 77 78 return; 79 80 err_unmap: 81 iounmap(reg); 82 of_address_to_resource(node, 0, &res); 83 release_mem_region(res.start, resource_size(&res)); 84 } 85 CLK_OF_DECLARE(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk", 86 sun8i_a23_apb0_setup); 87 88 static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev) 89 { 90 struct device_node *np = pdev->dev.of_node; 91 struct resource *r; 92 void __iomem *reg; 93 struct clk *clk; 94 95 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 96 reg = devm_ioremap_resource(&pdev->dev, r); 97 if (IS_ERR(reg)) 98 return PTR_ERR(reg); 99 100 clk = sun8i_a23_apb0_register(np, reg); 101 if (IS_ERR(clk)) 102 return PTR_ERR(clk); 103 104 return 0; 105 } 106 107 static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { 108 { .compatible = "allwinner,sun8i-a23-apb0-clk" }, 109 { /* sentinel */ } 110 }; 111 MODULE_DEVICE_TABLE(of, sun8i_a23_apb0_clk_dt_ids); 112 113 static struct platform_driver sun8i_a23_apb0_clk_driver = { 114 .driver = { 115 .name = "sun8i-a23-apb0-clk", 116 .of_match_table = sun8i_a23_apb0_clk_dt_ids, 117 }, 118 .probe = sun8i_a23_apb0_clk_probe, 119 }; 120 module_platform_driver(sun8i_a23_apb0_clk_driver); 121 122 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); 123 MODULE_DESCRIPTION("Allwinner A23 APB0 clock Driver"); 124 MODULE_LICENSE("GPL v2"); 125