1*fcf77be8SMichael Walle // SPDX-License-Identifier: GPL-2.0-only
2*fcf77be8SMichael Walle /*
3*fcf77be8SMichael Walle * Layerscape FlexSPI clock driver
4*fcf77be8SMichael Walle *
5*fcf77be8SMichael Walle * Copyright 2020 Michael Walle <michael@walle.cc>
6*fcf77be8SMichael Walle */
7*fcf77be8SMichael Walle
8*fcf77be8SMichael Walle #include <linux/clk-provider.h>
9*fcf77be8SMichael Walle #include <linux/io.h>
10*fcf77be8SMichael Walle #include <linux/module.h>
11*fcf77be8SMichael Walle #include <linux/platform_device.h>
12*fcf77be8SMichael Walle
13*fcf77be8SMichael Walle static const struct clk_div_table ls1028a_flexspi_divs[] = {
14*fcf77be8SMichael Walle { .val = 0, .div = 1, },
15*fcf77be8SMichael Walle { .val = 1, .div = 2, },
16*fcf77be8SMichael Walle { .val = 2, .div = 3, },
17*fcf77be8SMichael Walle { .val = 3, .div = 4, },
18*fcf77be8SMichael Walle { .val = 4, .div = 5, },
19*fcf77be8SMichael Walle { .val = 5, .div = 6, },
20*fcf77be8SMichael Walle { .val = 6, .div = 7, },
21*fcf77be8SMichael Walle { .val = 7, .div = 8, },
22*fcf77be8SMichael Walle { .val = 11, .div = 12, },
23*fcf77be8SMichael Walle { .val = 15, .div = 16, },
24*fcf77be8SMichael Walle { .val = 16, .div = 20, },
25*fcf77be8SMichael Walle { .val = 17, .div = 24, },
26*fcf77be8SMichael Walle { .val = 18, .div = 28, },
27*fcf77be8SMichael Walle { .val = 19, .div = 32, },
28*fcf77be8SMichael Walle { .val = 20, .div = 80, },
29*fcf77be8SMichael Walle {}
30*fcf77be8SMichael Walle };
31*fcf77be8SMichael Walle
32*fcf77be8SMichael Walle static const struct clk_div_table lx2160a_flexspi_divs[] = {
33*fcf77be8SMichael Walle { .val = 1, .div = 2, },
34*fcf77be8SMichael Walle { .val = 3, .div = 4, },
35*fcf77be8SMichael Walle { .val = 5, .div = 6, },
36*fcf77be8SMichael Walle { .val = 7, .div = 8, },
37*fcf77be8SMichael Walle { .val = 11, .div = 12, },
38*fcf77be8SMichael Walle { .val = 15, .div = 16, },
39*fcf77be8SMichael Walle { .val = 16, .div = 20, },
40*fcf77be8SMichael Walle { .val = 17, .div = 24, },
41*fcf77be8SMichael Walle { .val = 18, .div = 28, },
42*fcf77be8SMichael Walle { .val = 19, .div = 32, },
43*fcf77be8SMichael Walle { .val = 20, .div = 80, },
44*fcf77be8SMichael Walle {}
45*fcf77be8SMichael Walle };
46*fcf77be8SMichael Walle
fsl_flexspi_clk_probe(struct platform_device * pdev)47*fcf77be8SMichael Walle static int fsl_flexspi_clk_probe(struct platform_device *pdev)
48*fcf77be8SMichael Walle {
49*fcf77be8SMichael Walle struct device *dev = &pdev->dev;
50*fcf77be8SMichael Walle struct device_node *np = dev->of_node;
51*fcf77be8SMichael Walle const char *clk_name = np->name;
52*fcf77be8SMichael Walle const char *clk_parent;
53*fcf77be8SMichael Walle struct resource *res;
54*fcf77be8SMichael Walle void __iomem *reg;
55*fcf77be8SMichael Walle struct clk_hw *hw;
56*fcf77be8SMichael Walle const struct clk_div_table *divs;
57*fcf77be8SMichael Walle
58*fcf77be8SMichael Walle divs = device_get_match_data(dev);
59*fcf77be8SMichael Walle if (!divs)
60*fcf77be8SMichael Walle return -ENOENT;
61*fcf77be8SMichael Walle
62*fcf77be8SMichael Walle res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
63*fcf77be8SMichael Walle if (!res)
64*fcf77be8SMichael Walle return -ENOENT;
65*fcf77be8SMichael Walle
66*fcf77be8SMichael Walle /*
67*fcf77be8SMichael Walle * Can't use devm_ioremap_resource() or devm_of_iomap() because the
68*fcf77be8SMichael Walle * resource might already be taken by the parent device.
69*fcf77be8SMichael Walle */
70*fcf77be8SMichael Walle reg = devm_ioremap(dev, res->start, resource_size(res));
71*fcf77be8SMichael Walle if (!reg)
72*fcf77be8SMichael Walle return -ENOMEM;
73*fcf77be8SMichael Walle
74*fcf77be8SMichael Walle clk_parent = of_clk_get_parent_name(np, 0);
75*fcf77be8SMichael Walle if (!clk_parent)
76*fcf77be8SMichael Walle return -EINVAL;
77*fcf77be8SMichael Walle
78*fcf77be8SMichael Walle of_property_read_string(np, "clock-output-names", &clk_name);
79*fcf77be8SMichael Walle
80*fcf77be8SMichael Walle hw = devm_clk_hw_register_divider_table(dev, clk_name, clk_parent, 0,
81*fcf77be8SMichael Walle reg, 0, 5, 0, divs, NULL);
82*fcf77be8SMichael Walle if (IS_ERR(hw))
83*fcf77be8SMichael Walle return PTR_ERR(hw);
84*fcf77be8SMichael Walle
85*fcf77be8SMichael Walle return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
86*fcf77be8SMichael Walle }
87*fcf77be8SMichael Walle
88*fcf77be8SMichael Walle static const struct of_device_id fsl_flexspi_clk_dt_ids[] = {
89*fcf77be8SMichael Walle { .compatible = "fsl,ls1028a-flexspi-clk", .data = &ls1028a_flexspi_divs },
90*fcf77be8SMichael Walle { .compatible = "fsl,lx2160a-flexspi-clk", .data = &lx2160a_flexspi_divs },
91*fcf77be8SMichael Walle {}
92*fcf77be8SMichael Walle };
93*fcf77be8SMichael Walle MODULE_DEVICE_TABLE(of, fsl_flexspi_clk_dt_ids);
94*fcf77be8SMichael Walle
95*fcf77be8SMichael Walle static struct platform_driver fsl_flexspi_clk_driver = {
96*fcf77be8SMichael Walle .driver = {
97*fcf77be8SMichael Walle .name = "fsl-flexspi-clk",
98*fcf77be8SMichael Walle .of_match_table = fsl_flexspi_clk_dt_ids,
99*fcf77be8SMichael Walle },
100*fcf77be8SMichael Walle .probe = fsl_flexspi_clk_probe,
101*fcf77be8SMichael Walle };
102*fcf77be8SMichael Walle module_platform_driver(fsl_flexspi_clk_driver);
103*fcf77be8SMichael Walle
104*fcf77be8SMichael Walle MODULE_DESCRIPTION("FlexSPI clock driver for Layerscape SoCs");
105*fcf77be8SMichael Walle MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
106*fcf77be8SMichael Walle MODULE_LICENSE("GPL");
107