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