1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2013-2016 Freescale Semiconductor Inc. |
4 | * Copyright 2016-2018 NXP |
5 | * Copyright 2020 NXP |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/fsl/mc.h> |
12 | |
13 | #include "dpaa2-ptp.h" |
14 | |
15 | static int dpaa2_ptp_enable(struct ptp_clock_info *ptp, |
16 | struct ptp_clock_request *rq, int on) |
17 | { |
18 | struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); |
19 | struct fsl_mc_device *mc_dev; |
20 | struct device *dev; |
21 | u32 mask = 0; |
22 | u32 bit; |
23 | int err; |
24 | |
25 | dev = ptp_qoriq->dev; |
26 | mc_dev = to_fsl_mc_device(dev); |
27 | |
28 | switch (rq->type) { |
29 | case PTP_CLK_REQ_EXTTS: |
30 | switch (rq->extts.index) { |
31 | case 0: |
32 | bit = DPRTC_EVENT_ETS1; |
33 | break; |
34 | case 1: |
35 | bit = DPRTC_EVENT_ETS2; |
36 | break; |
37 | default: |
38 | return -EINVAL; |
39 | } |
40 | if (on) |
41 | extts_clean_up(ptp_qoriq, index: rq->extts.index, update_event: false); |
42 | break; |
43 | case PTP_CLK_REQ_PPS: |
44 | bit = DPRTC_EVENT_PPS; |
45 | break; |
46 | default: |
47 | return -EOPNOTSUPP; |
48 | } |
49 | |
50 | err = dprtc_get_irq_mask(mc_io: mc_dev->mc_io, cmd_flags: 0, token: mc_dev->mc_handle, |
51 | DPRTC_IRQ_INDEX, mask: &mask); |
52 | if (err < 0) { |
53 | dev_err(dev, "dprtc_get_irq_mask(): %d\n" , err); |
54 | return err; |
55 | } |
56 | |
57 | if (on) |
58 | mask |= bit; |
59 | else |
60 | mask &= ~bit; |
61 | |
62 | err = dprtc_set_irq_mask(mc_io: mc_dev->mc_io, cmd_flags: 0, token: mc_dev->mc_handle, |
63 | DPRTC_IRQ_INDEX, mask); |
64 | if (err < 0) { |
65 | dev_err(dev, "dprtc_set_irq_mask(): %d\n" , err); |
66 | return err; |
67 | } |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | static const struct ptp_clock_info dpaa2_ptp_caps = { |
73 | .owner = THIS_MODULE, |
74 | .name = "DPAA2 PTP Clock" , |
75 | .max_adj = 512000, |
76 | .n_alarm = 2, |
77 | .n_ext_ts = 2, |
78 | .n_per_out = 3, |
79 | .n_pins = 0, |
80 | .pps = 1, |
81 | .adjfine = ptp_qoriq_adjfine, |
82 | .adjtime = ptp_qoriq_adjtime, |
83 | .gettime64 = ptp_qoriq_gettime, |
84 | .settime64 = ptp_qoriq_settime, |
85 | .enable = dpaa2_ptp_enable, |
86 | }; |
87 | |
88 | static irqreturn_t dpaa2_ptp_irq_handler_thread(int irq, void *priv) |
89 | { |
90 | struct ptp_qoriq *ptp_qoriq = priv; |
91 | struct ptp_clock_event event; |
92 | struct fsl_mc_device *mc_dev; |
93 | struct device *dev; |
94 | u32 status = 0; |
95 | int err; |
96 | |
97 | dev = ptp_qoriq->dev; |
98 | mc_dev = to_fsl_mc_device(dev); |
99 | |
100 | err = dprtc_get_irq_status(mc_io: mc_dev->mc_io, cmd_flags: 0, token: mc_dev->mc_handle, |
101 | DPRTC_IRQ_INDEX, status: &status); |
102 | if (unlikely(err)) { |
103 | dev_err(dev, "dprtc_get_irq_status err %d\n" , err); |
104 | return IRQ_NONE; |
105 | } |
106 | |
107 | if (status & DPRTC_EVENT_PPS) { |
108 | event.type = PTP_CLOCK_PPS; |
109 | ptp_clock_event(ptp: ptp_qoriq->clock, event: &event); |
110 | } |
111 | |
112 | if (status & DPRTC_EVENT_ETS1) |
113 | extts_clean_up(ptp_qoriq, index: 0, update_event: true); |
114 | |
115 | if (status & DPRTC_EVENT_ETS2) |
116 | extts_clean_up(ptp_qoriq, index: 1, update_event: true); |
117 | |
118 | err = dprtc_clear_irq_status(mc_io: mc_dev->mc_io, cmd_flags: 0, token: mc_dev->mc_handle, |
119 | DPRTC_IRQ_INDEX, status); |
120 | if (unlikely(err)) { |
121 | dev_err(dev, "dprtc_clear_irq_status err %d\n" , err); |
122 | return IRQ_NONE; |
123 | } |
124 | |
125 | return IRQ_HANDLED; |
126 | } |
127 | |
128 | static int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev) |
129 | { |
130 | struct device *dev = &mc_dev->dev; |
131 | struct ptp_qoriq *ptp_qoriq; |
132 | struct device_node *node; |
133 | void __iomem *base; |
134 | int err; |
135 | |
136 | ptp_qoriq = devm_kzalloc(dev, size: sizeof(*ptp_qoriq), GFP_KERNEL); |
137 | if (!ptp_qoriq) |
138 | return -ENOMEM; |
139 | |
140 | err = fsl_mc_portal_allocate(mc_dev, mc_io_flags: 0, new_mc_io: &mc_dev->mc_io); |
141 | if (err) { |
142 | if (err == -ENXIO) |
143 | err = -EPROBE_DEFER; |
144 | else |
145 | dev_err(dev, "fsl_mc_portal_allocate err %d\n" , err); |
146 | goto err_exit; |
147 | } |
148 | |
149 | err = dprtc_open(mc_io: mc_dev->mc_io, cmd_flags: 0, dprtc_id: mc_dev->obj_desc.id, |
150 | token: &mc_dev->mc_handle); |
151 | if (err) { |
152 | dev_err(dev, "dprtc_open err %d\n" , err); |
153 | goto err_free_mcp; |
154 | } |
155 | |
156 | ptp_qoriq->dev = dev; |
157 | |
158 | node = of_find_compatible_node(NULL, NULL, compat: "fsl,dpaa2-ptp" ); |
159 | if (!node) { |
160 | err = -ENODEV; |
161 | goto err_close; |
162 | } |
163 | |
164 | dev->of_node = node; |
165 | |
166 | base = of_iomap(node, index: 0); |
167 | if (!base) { |
168 | err = -ENOMEM; |
169 | goto err_put; |
170 | } |
171 | |
172 | err = fsl_mc_allocate_irqs(mc_dev); |
173 | if (err) { |
174 | dev_err(dev, "MC irqs allocation failed\n" ); |
175 | goto err_unmap; |
176 | } |
177 | |
178 | ptp_qoriq->irq = mc_dev->irqs[0]->virq; |
179 | |
180 | err = request_threaded_irq(irq: ptp_qoriq->irq, NULL, |
181 | thread_fn: dpaa2_ptp_irq_handler_thread, |
182 | IRQF_NO_SUSPEND | IRQF_ONESHOT, |
183 | name: dev_name(dev), dev: ptp_qoriq); |
184 | if (err < 0) { |
185 | dev_err(dev, "devm_request_threaded_irq(): %d\n" , err); |
186 | goto err_free_mc_irq; |
187 | } |
188 | |
189 | err = dprtc_set_irq_enable(mc_io: mc_dev->mc_io, cmd_flags: 0, token: mc_dev->mc_handle, |
190 | DPRTC_IRQ_INDEX, en: 1); |
191 | if (err < 0) { |
192 | dev_err(dev, "dprtc_set_irq_enable(): %d\n" , err); |
193 | goto err_free_threaded_irq; |
194 | } |
195 | |
196 | err = ptp_qoriq_init(ptp_qoriq, base, caps: &dpaa2_ptp_caps); |
197 | if (err) |
198 | goto err_free_threaded_irq; |
199 | |
200 | dpaa2_phc_index = ptp_qoriq->phc_index; |
201 | dpaa2_ptp = ptp_qoriq; |
202 | dev_set_drvdata(dev, data: ptp_qoriq); |
203 | |
204 | return 0; |
205 | |
206 | err_free_threaded_irq: |
207 | free_irq(ptp_qoriq->irq, ptp_qoriq); |
208 | err_free_mc_irq: |
209 | fsl_mc_free_irqs(mc_dev); |
210 | err_unmap: |
211 | iounmap(addr: base); |
212 | err_put: |
213 | of_node_put(node); |
214 | err_close: |
215 | dprtc_close(mc_io: mc_dev->mc_io, cmd_flags: 0, token: mc_dev->mc_handle); |
216 | err_free_mcp: |
217 | fsl_mc_portal_free(mc_io: mc_dev->mc_io); |
218 | err_exit: |
219 | return err; |
220 | } |
221 | |
222 | static void dpaa2_ptp_remove(struct fsl_mc_device *mc_dev) |
223 | { |
224 | struct device *dev = &mc_dev->dev; |
225 | struct ptp_qoriq *ptp_qoriq; |
226 | |
227 | ptp_qoriq = dev_get_drvdata(dev); |
228 | |
229 | dpaa2_phc_index = -1; |
230 | ptp_qoriq_free(ptp_qoriq); |
231 | |
232 | fsl_mc_free_irqs(mc_dev); |
233 | dprtc_close(mc_io: mc_dev->mc_io, cmd_flags: 0, token: mc_dev->mc_handle); |
234 | fsl_mc_portal_free(mc_io: mc_dev->mc_io); |
235 | } |
236 | |
237 | static const struct fsl_mc_device_id dpaa2_ptp_match_id_table[] = { |
238 | { |
239 | .vendor = FSL_MC_VENDOR_FREESCALE, |
240 | .obj_type = "dprtc" , |
241 | }, |
242 | {} |
243 | }; |
244 | MODULE_DEVICE_TABLE(fslmc, dpaa2_ptp_match_id_table); |
245 | |
246 | static struct fsl_mc_driver dpaa2_ptp_drv = { |
247 | .driver = { |
248 | .name = KBUILD_MODNAME, |
249 | .owner = THIS_MODULE, |
250 | }, |
251 | .probe = dpaa2_ptp_probe, |
252 | .remove = dpaa2_ptp_remove, |
253 | .match_id_table = dpaa2_ptp_match_id_table, |
254 | }; |
255 | |
256 | module_fsl_mc_driver(dpaa2_ptp_drv); |
257 | |
258 | MODULE_LICENSE("GPL v2" ); |
259 | MODULE_DESCRIPTION("DPAA2 PTP Clock Driver" ); |
260 | |