Commit | Line | Data |
---|---|---|
fee10bd2 NSR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * ARM PL353 SMC driver | |
4 | * | |
5 | * Copyright (C) 2012 - 2018 Xilinx, Inc | |
6 | * Author: Punnaiah Choudary Kalluri <punnaiah@xilinx.com> | |
7 | * Author: Naga Sureshkumar Relli <nagasure@xilinx.com> | |
8 | */ | |
9 | ||
10 | #include <linux/clk.h> | |
fee10bd2 NSR |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> | |
0b483871 | 13 | #include <linux/of.h> |
fee10bd2 NSR |
14 | #include <linux/of_platform.h> |
15 | #include <linux/platform_device.h> | |
fee10bd2 NSR |
16 | #include <linux/amba/bus.h> |
17 | ||
fee10bd2 NSR |
18 | /** |
19 | * struct pl353_smc_data - Private smc driver structure | |
20 | * @memclk: Pointer to the peripheral clock | |
9d7bb449 | 21 | * @aclk: Pointer to the AXI peripheral clock |
fee10bd2 NSR |
22 | */ |
23 | struct pl353_smc_data { | |
24 | struct clk *memclk; | |
25 | struct clk *aclk; | |
26 | }; | |
27 | ||
fee10bd2 NSR |
28 | static int __maybe_unused pl353_smc_suspend(struct device *dev) |
29 | { | |
30 | struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); | |
31 | ||
32 | clk_disable(pl353_smc->memclk); | |
33 | clk_disable(pl353_smc->aclk); | |
34 | ||
35 | return 0; | |
36 | } | |
37 | ||
38 | static int __maybe_unused pl353_smc_resume(struct device *dev) | |
39 | { | |
fee10bd2 | 40 | struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); |
df6c2646 | 41 | int ret; |
fee10bd2 NSR |
42 | |
43 | ret = clk_enable(pl353_smc->aclk); | |
44 | if (ret) { | |
45 | dev_err(dev, "Cannot enable axi domain clock.\n"); | |
46 | return ret; | |
47 | } | |
48 | ||
49 | ret = clk_enable(pl353_smc->memclk); | |
50 | if (ret) { | |
51 | dev_err(dev, "Cannot enable memory clock.\n"); | |
52 | clk_disable(pl353_smc->aclk); | |
53 | return ret; | |
54 | } | |
55 | ||
56 | return ret; | |
57 | } | |
58 | ||
fee10bd2 NSR |
59 | static SIMPLE_DEV_PM_OPS(pl353_smc_dev_pm_ops, pl353_smc_suspend, |
60 | pl353_smc_resume); | |
61 | ||
fee10bd2 NSR |
62 | static const struct of_device_id pl353_smc_supported_children[] = { |
63 | { | |
64 | .compatible = "cfi-flash" | |
65 | }, | |
66 | { | |
67 | .compatible = "arm,pl353-nand-r2p1", | |
fee10bd2 NSR |
68 | }, |
69 | {} | |
70 | }; | |
71 | ||
72 | static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id) | |
73 | { | |
df6c2646 MR |
74 | struct device_node *of_node = adev->dev.of_node; |
75 | const struct of_device_id *match = NULL; | |
fee10bd2 NSR |
76 | struct pl353_smc_data *pl353_smc; |
77 | struct device_node *child; | |
fee10bd2 | 78 | int err; |
fee10bd2 NSR |
79 | |
80 | pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL); | |
81 | if (!pl353_smc) | |
82 | return -ENOMEM; | |
83 | ||
fee10bd2 NSR |
84 | pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk"); |
85 | if (IS_ERR(pl353_smc->aclk)) { | |
86 | dev_err(&adev->dev, "aclk clock not found.\n"); | |
87 | return PTR_ERR(pl353_smc->aclk); | |
88 | } | |
89 | ||
90 | pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk"); | |
91 | if (IS_ERR(pl353_smc->memclk)) { | |
92 | dev_err(&adev->dev, "memclk clock not found.\n"); | |
93 | return PTR_ERR(pl353_smc->memclk); | |
94 | } | |
95 | ||
96 | err = clk_prepare_enable(pl353_smc->aclk); | |
97 | if (err) { | |
98 | dev_err(&adev->dev, "Unable to enable AXI clock.\n"); | |
99 | return err; | |
100 | } | |
101 | ||
102 | err = clk_prepare_enable(pl353_smc->memclk); | |
103 | if (err) { | |
104 | dev_err(&adev->dev, "Unable to enable memory clock.\n"); | |
edd84c42 | 105 | goto disable_axi_clk; |
fee10bd2 NSR |
106 | } |
107 | ||
108 | amba_set_drvdata(adev, pl353_smc); | |
109 | ||
fee10bd2 NSR |
110 | /* Find compatible children. Only a single child is supported */ |
111 | for_each_available_child_of_node(of_node, child) { | |
112 | match = of_match_node(pl353_smc_supported_children, child); | |
113 | if (!match) { | |
114 | dev_warn(&adev->dev, "unsupported child node\n"); | |
115 | continue; | |
116 | } | |
117 | break; | |
118 | } | |
119 | if (!match) { | |
76e5624f | 120 | err = -ENODEV; |
fee10bd2 | 121 | dev_err(&adev->dev, "no matching children\n"); |
edd84c42 | 122 | goto disable_mem_clk; |
fee10bd2 NSR |
123 | } |
124 | ||
fee10bd2 | 125 | of_platform_device_create(child, NULL, &adev->dev); |
61b3c876 | 126 | of_node_put(child); |
fee10bd2 NSR |
127 | |
128 | return 0; | |
129 | ||
edd84c42 | 130 | disable_mem_clk: |
fee10bd2 | 131 | clk_disable_unprepare(pl353_smc->memclk); |
edd84c42 | 132 | disable_axi_clk: |
fee10bd2 NSR |
133 | clk_disable_unprepare(pl353_smc->aclk); |
134 | ||
135 | return err; | |
136 | } | |
137 | ||
3fd269e7 | 138 | static void pl353_smc_remove(struct amba_device *adev) |
fee10bd2 NSR |
139 | { |
140 | struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev); | |
141 | ||
142 | clk_disable_unprepare(pl353_smc->memclk); | |
143 | clk_disable_unprepare(pl353_smc->aclk); | |
fee10bd2 NSR |
144 | } |
145 | ||
146 | static const struct amba_id pl353_ids[] = { | |
147 | { | |
62584c87 MR |
148 | .id = 0x00041353, |
149 | .mask = 0x000fffff, | |
fee10bd2 NSR |
150 | }, |
151 | { 0, 0 }, | |
152 | }; | |
153 | MODULE_DEVICE_TABLE(amba, pl353_ids); | |
154 | ||
155 | static struct amba_driver pl353_smc_driver = { | |
156 | .drv = { | |
fee10bd2 NSR |
157 | .name = "pl353-smc", |
158 | .pm = &pl353_smc_dev_pm_ops, | |
159 | }, | |
160 | .id_table = pl353_ids, | |
161 | .probe = pl353_smc_probe, | |
162 | .remove = pl353_smc_remove, | |
163 | }; | |
164 | ||
165 | module_amba_driver(pl353_smc_driver); | |
166 | ||
167 | MODULE_AUTHOR("Xilinx, Inc."); | |
168 | MODULE_DESCRIPTION("ARM PL353 SMC Driver"); | |
169 | MODULE_LICENSE("GPL"); |