Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 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 | |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 11 | #include <linux/bitops.h> |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 12 | #include <linux/clk-provider.h> |
Paul Gortmaker | 439a36d | 2016-07-04 17:12:18 -0400 | [diff] [blame] | 13 | #include <linux/init.h> |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 14 | #include <linux/of.h> |
| 15 | #include <linux/platform_device.h> |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 16 | #include <linux/spinlock.h> |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 17 | |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 18 | #include "clk-factors.h" |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 19 | |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 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) |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 27 | { |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 28 | unsigned long div; |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 29 | int shift; |
| 30 | |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 31 | /* clock only divides */ |
| 32 | if (req->rate > req->parent_rate) |
| 33 | req->rate = req->parent_rate; |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 34 | |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 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; |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 45 | |
| 46 | div >>= shift; |
| 47 | |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 48 | if (div > 32) |
| 49 | div = 32; |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 50 | |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 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 | |
Arnd Bergmann | 14b5a4b | 2016-02-02 16:55:30 +0100 | [diff] [blame] | 63 | static const struct factors_data sun6i_ar100_data = { |
Chen-Yu Tsai | 3ca2377 | 2016-01-25 21:15:46 +0800 | [diff] [blame] | 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); |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 90 | |
| 91 | return 0; |
| 92 | } |
| 93 | |
Emilio López | 381c1cc | 2014-07-28 00:49:43 -0300 | [diff] [blame] | 94 | static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = { |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 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", |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 102 | .of_match_table = sun6i_a31_ar100_clk_dt_ids, |
Paul Gortmaker | 439a36d | 2016-07-04 17:12:18 -0400 | [diff] [blame] | 103 | .suppress_bind_attrs = true, |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 104 | }, |
| 105 | .probe = sun6i_a31_ar100_clk_probe, |
Boris BREZILLON | c8a76ca | 2014-05-15 10:55:11 +0200 | [diff] [blame] | 106 | }; |
Paul Gortmaker | 439a36d | 2016-07-04 17:12:18 -0400 | [diff] [blame] | 107 | builtin_platform_driver(sun6i_a31_ar100_clk_driver); |