1 /* 2 * Copyright (C) 2014 Free Electrons 3 * 4 * License Terms: GNU General Public License v2 5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 6 * 7 * Allwinner A31 AR100 clock driver 8 * 9 */ 10 11 #include <linux/bitops.h> 12 #include <linux/clk-provider.h> 13 #include <linux/init.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/spinlock.h> 17 18 #include "clk-factors.h" 19 20 /** 21 * sun6i_get_ar100_factors - Calculates factors p, m for AR100 22 * 23 * AR100 rate is calculated as follows 24 * rate = (parent_rate >> p) / (m + 1); 25 */ 26 static void sun6i_get_ar100_factors(struct factors_request *req) 27 { 28 unsigned long div; 29 int shift; 30 31 /* clock only divides */ 32 if (req->rate > req->parent_rate) 33 req->rate = req->parent_rate; 34 35 div = DIV_ROUND_UP(req->parent_rate, req->rate); 36 37 if (div < 32) 38 shift = 0; 39 else if (div >> 1 < 32) 40 shift = 1; 41 else if (div >> 2 < 32) 42 shift = 2; 43 else 44 shift = 3; 45 46 div >>= shift; 47 48 if (div > 32) 49 div = 32; 50 51 req->rate = (req->parent_rate >> shift) / div; 52 req->m = div - 1; 53 req->p = shift; 54 } 55 56 static const struct clk_factors_config sun6i_ar100_config = { 57 .mwidth = 5, 58 .mshift = 8, 59 .pwidth = 2, 60 .pshift = 4, 61 }; 62 63 static const struct factors_data sun6i_ar100_data = { 64 .mux = 16, 65 .muxmask = GENMASK(1, 0), 66 .table = &sun6i_ar100_config, 67 .getter = sun6i_get_ar100_factors, 68 }; 69 70 static DEFINE_SPINLOCK(sun6i_ar100_lock); 71 72 static int sun6i_a31_ar100_clk_probe(struct platform_device *pdev) 73 { 74 struct device_node *np = pdev->dev.of_node; 75 struct resource *r; 76 void __iomem *reg; 77 struct clk *clk; 78 79 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 80 reg = devm_ioremap_resource(&pdev->dev, r); 81 if (IS_ERR(reg)) 82 return PTR_ERR(reg); 83 84 clk = sunxi_factors_register(np, &sun6i_ar100_data, &sun6i_ar100_lock, 85 reg); 86 if (!clk) 87 return -ENOMEM; 88 89 platform_set_drvdata(pdev, clk); 90 91 return 0; 92 } 93 94 static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = { 95 { .compatible = "allwinner,sun6i-a31-ar100-clk" }, 96 { /* sentinel */ } 97 }; 98 99 static struct platform_driver sun6i_a31_ar100_clk_driver = { 100 .driver = { 101 .name = "sun6i-a31-ar100-clk", 102 .of_match_table = sun6i_a31_ar100_clk_dt_ids, 103 .suppress_bind_attrs = true, 104 }, 105 .probe = sun6i_a31_ar100_clk_probe, 106 }; 107 builtin_platform_driver(sun6i_a31_ar100_clk_driver); 108