Commit | Line | Data |
---|---|---|
873e65bc | 1 | // SPDX-License-Identifier: GPL-2.0-only |
863d08ec TS |
2 | /* |
3 | * PTP 1588 clock using the EG20T PCH | |
4 | * | |
5 | * Copyright (C) 2010 OMICRON electronics GmbH | |
6 | * Copyright (C) 2011-2012 LAPIS SEMICONDUCTOR Co., LTD. | |
7 | * | |
8 | * This code was derived from the IXP46X driver. | |
863d08ec TS |
9 | */ |
10 | ||
11 | #include <linux/device.h> | |
12 | #include <linux/err.h> | |
863d08ec TS |
13 | #include <linux/interrupt.h> |
14 | #include <linux/io.h> | |
8664d49a | 15 | #include <linux/io-64-nonatomic-lo-hi.h> |
d09adf61 | 16 | #include <linux/io-64-nonatomic-hi-lo.h> |
863d08ec TS |
17 | #include <linux/irq.h> |
18 | #include <linux/kernel.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/pci.h> | |
21 | #include <linux/ptp_clock_kernel.h> | |
f90fc37f | 22 | #include <linux/ptp_pch.h> |
769b0daf | 23 | #include <linux/slab.h> |
863d08ec TS |
24 | |
25 | #define STATION_ADDR_LEN 20 | |
26 | #define PCI_DEVICE_ID_PCH_1588 0x8819 | |
27 | #define IO_MEM_BAR 1 | |
28 | ||
29 | #define DEFAULT_ADDEND 0xA0000000 | |
30 | #define TICKS_NS_SHIFT 5 | |
31 | #define N_EXT_TS 2 | |
32 | ||
33 | enum pch_status { | |
34 | PCH_SUCCESS, | |
35 | PCH_INVALIDPARAM, | |
36 | PCH_NOTIMESTAMP, | |
37 | PCH_INTERRUPTMODEINUSE, | |
38 | PCH_FAILED, | |
39 | PCH_UNSUPPORTED, | |
40 | }; | |
287f93de LJ |
41 | |
42 | /* | |
863d08ec TS |
43 | * struct pch_ts_regs - IEEE 1588 registers |
44 | */ | |
45 | struct pch_ts_regs { | |
46 | u32 control; | |
47 | u32 event; | |
48 | u32 addend; | |
49 | u32 accum; | |
50 | u32 test; | |
51 | u32 ts_compare; | |
52 | u32 rsystime_lo; | |
53 | u32 rsystime_hi; | |
54 | u32 systime_lo; | |
55 | u32 systime_hi; | |
56 | u32 trgt_lo; | |
57 | u32 trgt_hi; | |
58 | u32 asms_lo; | |
59 | u32 asms_hi; | |
60 | u32 amms_lo; | |
61 | u32 amms_hi; | |
62 | u32 ch_control; | |
63 | u32 ch_event; | |
64 | u32 tx_snap_lo; | |
65 | u32 tx_snap_hi; | |
66 | u32 rx_snap_lo; | |
67 | u32 rx_snap_hi; | |
68 | u32 src_uuid_lo; | |
69 | u32 src_uuid_hi; | |
70 | u32 can_status; | |
71 | u32 can_snap_lo; | |
72 | u32 can_snap_hi; | |
73 | u32 ts_sel; | |
74 | u32 ts_st[6]; | |
75 | u32 reserve1[14]; | |
76 | u32 stl_max_set_en; | |
77 | u32 stl_max_set; | |
78 | u32 reserve2[13]; | |
79 | u32 srst; | |
80 | }; | |
81 | ||
82 | #define PCH_TSC_RESET (1 << 0) | |
83 | #define PCH_TSC_TTM_MASK (1 << 1) | |
84 | #define PCH_TSC_ASMS_MASK (1 << 2) | |
85 | #define PCH_TSC_AMMS_MASK (1 << 3) | |
86 | #define PCH_TSC_PPSM_MASK (1 << 4) | |
87 | #define PCH_TSE_TTIPEND (1 << 1) | |
88 | #define PCH_TSE_SNS (1 << 2) | |
89 | #define PCH_TSE_SNM (1 << 3) | |
90 | #define PCH_TSE_PPS (1 << 4) | |
91 | #define PCH_CC_MM (1 << 0) | |
92 | #define PCH_CC_TA (1 << 1) | |
93 | ||
94 | #define PCH_CC_MODE_SHIFT 16 | |
95 | #define PCH_CC_MODE_MASK 0x001F0000 | |
96 | #define PCH_CC_VERSION (1 << 31) | |
97 | #define PCH_CE_TXS (1 << 0) | |
98 | #define PCH_CE_RXS (1 << 1) | |
99 | #define PCH_CE_OVR (1 << 0) | |
100 | #define PCH_CE_VAL (1 << 1) | |
101 | #define PCH_ECS_ETH (1 << 0) | |
102 | ||
103 | #define PCH_ECS_CAN (1 << 1) | |
863d08ec TS |
104 | |
105 | #define PCH_IEEE1588_ETH (1 << 0) | |
106 | #define PCH_IEEE1588_CAN (1 << 1) | |
287f93de LJ |
107 | |
108 | /* | |
863d08ec TS |
109 | * struct pch_dev - Driver private data |
110 | */ | |
111 | struct pch_dev { | |
7d3ac5c7 | 112 | struct pch_ts_regs __iomem *regs; |
863d08ec TS |
113 | struct ptp_clock *ptp_clock; |
114 | struct ptp_clock_info caps; | |
115 | int exts0_enabled; | |
116 | int exts1_enabled; | |
117 | ||
863d08ec TS |
118 | u32 irq; |
119 | struct pci_dev *pdev; | |
120 | spinlock_t register_lock; | |
121 | }; | |
122 | ||
287f93de | 123 | /* |
863d08ec TS |
124 | * struct pch_params - 1588 module parameter |
125 | */ | |
126 | struct pch_params { | |
127 | u8 station[STATION_ADDR_LEN]; | |
128 | }; | |
129 | ||
130 | /* structure to hold the module parameters */ | |
131 | static struct pch_params pch_param = { | |
132 | "00:00:00:00:00:00" | |
133 | }; | |
134 | ||
135 | /* | |
136 | * Register access functions | |
137 | */ | |
138 | static inline void pch_eth_enable_set(struct pch_dev *chip) | |
139 | { | |
140 | u32 val; | |
141 | /* SET the eth_enable bit */ | |
142 | val = ioread32(&chip->regs->ts_sel) | (PCH_ECS_ETH); | |
143 | iowrite32(val, (&chip->regs->ts_sel)); | |
144 | } | |
145 | ||
7d3ac5c7 | 146 | static u64 pch_systime_read(struct pch_ts_regs __iomem *regs) |
863d08ec TS |
147 | { |
148 | u64 ns; | |
863d08ec | 149 | |
8664d49a | 150 | ns = ioread64_lo_hi(®s->systime_lo); |
863d08ec | 151 | |
8664d49a | 152 | return ns << TICKS_NS_SHIFT; |
863d08ec TS |
153 | } |
154 | ||
7d3ac5c7 | 155 | static void pch_systime_write(struct pch_ts_regs __iomem *regs, u64 ns) |
863d08ec | 156 | { |
8664d49a | 157 | iowrite64_lo_hi(ns >> TICKS_NS_SHIFT, ®s->systime_lo); |
863d08ec TS |
158 | } |
159 | ||
160 | static inline void pch_block_reset(struct pch_dev *chip) | |
161 | { | |
162 | u32 val; | |
163 | /* Reset Hardware Assist block */ | |
164 | val = ioread32(&chip->regs->control) | PCH_TSC_RESET; | |
165 | iowrite32(val, (&chip->regs->control)); | |
166 | val = val & ~PCH_TSC_RESET; | |
167 | iowrite32(val, (&chip->regs->control)); | |
168 | } | |
169 | ||
863d08ec TS |
170 | void pch_ch_control_write(struct pci_dev *pdev, u32 val) |
171 | { | |
172 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
173 | ||
174 | iowrite32(val, (&chip->regs->ch_control)); | |
175 | } | |
176 | EXPORT_SYMBOL(pch_ch_control_write); | |
177 | ||
178 | u32 pch_ch_event_read(struct pci_dev *pdev) | |
179 | { | |
180 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
181 | u32 val; | |
182 | ||
183 | val = ioread32(&chip->regs->ch_event); | |
184 | ||
185 | return val; | |
186 | } | |
187 | EXPORT_SYMBOL(pch_ch_event_read); | |
188 | ||
189 | void pch_ch_event_write(struct pci_dev *pdev, u32 val) | |
190 | { | |
191 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
192 | ||
193 | iowrite32(val, (&chip->regs->ch_event)); | |
194 | } | |
195 | EXPORT_SYMBOL(pch_ch_event_write); | |
196 | ||
197 | u32 pch_src_uuid_lo_read(struct pci_dev *pdev) | |
198 | { | |
199 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
200 | u32 val; | |
201 | ||
202 | val = ioread32(&chip->regs->src_uuid_lo); | |
203 | ||
204 | return val; | |
205 | } | |
206 | EXPORT_SYMBOL(pch_src_uuid_lo_read); | |
207 | ||
208 | u32 pch_src_uuid_hi_read(struct pci_dev *pdev) | |
209 | { | |
210 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
211 | u32 val; | |
212 | ||
213 | val = ioread32(&chip->regs->src_uuid_hi); | |
214 | ||
215 | return val; | |
216 | } | |
217 | EXPORT_SYMBOL(pch_src_uuid_hi_read); | |
218 | ||
219 | u64 pch_rx_snap_read(struct pci_dev *pdev) | |
220 | { | |
221 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
222 | u64 ns; | |
863d08ec | 223 | |
8664d49a | 224 | ns = ioread64_lo_hi(&chip->regs->rx_snap_lo); |
863d08ec | 225 | |
8664d49a | 226 | return ns << TICKS_NS_SHIFT; |
863d08ec TS |
227 | } |
228 | EXPORT_SYMBOL(pch_rx_snap_read); | |
229 | ||
230 | u64 pch_tx_snap_read(struct pci_dev *pdev) | |
231 | { | |
232 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
233 | u64 ns; | |
863d08ec | 234 | |
8664d49a | 235 | ns = ioread64_lo_hi(&chip->regs->tx_snap_lo); |
863d08ec | 236 | |
8664d49a | 237 | return ns << TICKS_NS_SHIFT; |
863d08ec TS |
238 | } |
239 | EXPORT_SYMBOL(pch_tx_snap_read); | |
240 | ||
241 | /* This function enables all 64 bits in system time registers [high & low]. | |
242 | This is a work-around for non continuous value in the SystemTime Register*/ | |
243 | static void pch_set_system_time_count(struct pch_dev *chip) | |
244 | { | |
245 | iowrite32(0x01, &chip->regs->stl_max_set_en); | |
246 | iowrite32(0xFFFFFFFF, &chip->regs->stl_max_set); | |
247 | iowrite32(0x00, &chip->regs->stl_max_set_en); | |
248 | } | |
249 | ||
250 | static void pch_reset(struct pch_dev *chip) | |
251 | { | |
252 | /* Reset Hardware Assist */ | |
253 | pch_block_reset(chip); | |
254 | ||
255 | /* enable all 32 bits in system time registers */ | |
256 | pch_set_system_time_count(chip); | |
257 | } | |
258 | ||
259 | /** | |
260 | * pch_set_station_address() - This API sets the station address used by | |
261 | * IEEE 1588 hardware when looking at PTP | |
262 | * traffic on the ethernet interface | |
263 | * @addr: dress which contain the column separated address to be used. | |
287f93de | 264 | * @pdev: PCI device. |
863d08ec | 265 | */ |
17cdedf3 | 266 | int pch_set_station_address(u8 *addr, struct pci_dev *pdev) |
863d08ec | 267 | { |
863d08ec | 268 | struct pch_dev *chip = pci_get_drvdata(pdev); |
4e76b5c1 AS |
269 | bool valid; |
270 | u64 mac; | |
863d08ec TS |
271 | |
272 | /* Verify the parameter */ | |
7d3ac5c7 | 273 | if ((chip->regs == NULL) || addr == (u8 *)NULL) { |
863d08ec TS |
274 | dev_err(&pdev->dev, |
275 | "invalid params returning PCH_INVALIDPARAM\n"); | |
276 | return PCH_INVALIDPARAM; | |
277 | } | |
863d08ec | 278 | |
4e76b5c1 AS |
279 | valid = mac_pton(addr, (u8 *)&mac); |
280 | if (!valid) { | |
281 | dev_err(&pdev->dev, "invalid params returning PCH_INVALIDPARAM\n"); | |
282 | return PCH_INVALIDPARAM; | |
863d08ec | 283 | } |
4e76b5c1 AS |
284 | |
285 | dev_dbg(&pdev->dev, "invoking pch_station_set\n"); | |
8664d49a | 286 | iowrite64_lo_hi(mac, &chip->regs->ts_st); |
863d08ec TS |
287 | return 0; |
288 | } | |
17cdedf3 | 289 | EXPORT_SYMBOL(pch_set_station_address); |
863d08ec TS |
290 | |
291 | /* | |
292 | * Interrupt service routine | |
293 | */ | |
294 | static irqreturn_t isr(int irq, void *priv) | |
295 | { | |
296 | struct pch_dev *pch_dev = priv; | |
7d3ac5c7 | 297 | struct pch_ts_regs __iomem *regs = pch_dev->regs; |
863d08ec | 298 | struct ptp_clock_event event; |
d09adf61 | 299 | u32 ack = 0, val; |
863d08ec TS |
300 | |
301 | val = ioread32(®s->event); | |
302 | ||
303 | if (val & PCH_TSE_SNS) { | |
304 | ack |= PCH_TSE_SNS; | |
305 | if (pch_dev->exts0_enabled) { | |
863d08ec TS |
306 | event.type = PTP_CLOCK_EXTTS; |
307 | event.index = 0; | |
d09adf61 | 308 | event.timestamp = ioread64_hi_lo(®s->asms_hi); |
863d08ec TS |
309 | event.timestamp <<= TICKS_NS_SHIFT; |
310 | ptp_clock_event(pch_dev->ptp_clock, &event); | |
311 | } | |
312 | } | |
313 | ||
314 | if (val & PCH_TSE_SNM) { | |
315 | ack |= PCH_TSE_SNM; | |
316 | if (pch_dev->exts1_enabled) { | |
863d08ec TS |
317 | event.type = PTP_CLOCK_EXTTS; |
318 | event.index = 1; | |
d09adf61 | 319 | event.timestamp = ioread64_hi_lo(®s->asms_hi); |
863d08ec TS |
320 | event.timestamp <<= TICKS_NS_SHIFT; |
321 | ptp_clock_event(pch_dev->ptp_clock, &event); | |
322 | } | |
323 | } | |
324 | ||
325 | if (val & PCH_TSE_TTIPEND) | |
326 | ack |= PCH_TSE_TTIPEND; /* this bit seems to be always set */ | |
327 | ||
328 | if (ack) { | |
329 | iowrite32(ack, ®s->event); | |
330 | return IRQ_HANDLED; | |
331 | } else | |
332 | return IRQ_NONE; | |
333 | } | |
334 | ||
335 | /* | |
336 | * PTP clock operations | |
337 | */ | |
338 | ||
2e77eded | 339 | static int ptp_pch_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) |
863d08ec | 340 | { |
2e77eded | 341 | u32 addend; |
863d08ec | 342 | struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); |
7d3ac5c7 | 343 | struct pch_ts_regs __iomem *regs = pch_dev->regs; |
863d08ec | 344 | |
2e77eded | 345 | addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm); |
863d08ec TS |
346 | |
347 | iowrite32(addend, ®s->addend); | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
352 | static int ptp_pch_adjtime(struct ptp_clock_info *ptp, s64 delta) | |
353 | { | |
354 | s64 now; | |
355 | unsigned long flags; | |
356 | struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); | |
7d3ac5c7 | 357 | struct pch_ts_regs __iomem *regs = pch_dev->regs; |
863d08ec TS |
358 | |
359 | spin_lock_irqsave(&pch_dev->register_lock, flags); | |
360 | now = pch_systime_read(regs); | |
361 | now += delta; | |
362 | pch_systime_write(regs, now); | |
363 | spin_unlock_irqrestore(&pch_dev->register_lock, flags); | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
a043a729 | 368 | static int ptp_pch_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) |
863d08ec TS |
369 | { |
370 | u64 ns; | |
863d08ec TS |
371 | unsigned long flags; |
372 | struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); | |
7d3ac5c7 | 373 | struct pch_ts_regs __iomem *regs = pch_dev->regs; |
863d08ec TS |
374 | |
375 | spin_lock_irqsave(&pch_dev->register_lock, flags); | |
376 | ns = pch_systime_read(regs); | |
377 | spin_unlock_irqrestore(&pch_dev->register_lock, flags); | |
378 | ||
80e95f47 | 379 | *ts = ns_to_timespec64(ns); |
863d08ec TS |
380 | return 0; |
381 | } | |
382 | ||
383 | static int ptp_pch_settime(struct ptp_clock_info *ptp, | |
a043a729 | 384 | const struct timespec64 *ts) |
863d08ec TS |
385 | { |
386 | u64 ns; | |
387 | unsigned long flags; | |
388 | struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); | |
7d3ac5c7 | 389 | struct pch_ts_regs __iomem *regs = pch_dev->regs; |
863d08ec | 390 | |
80e95f47 | 391 | ns = timespec64_to_ns(ts); |
863d08ec TS |
392 | |
393 | spin_lock_irqsave(&pch_dev->register_lock, flags); | |
394 | pch_systime_write(regs, ns); | |
395 | spin_unlock_irqrestore(&pch_dev->register_lock, flags); | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | static int ptp_pch_enable(struct ptp_clock_info *ptp, | |
401 | struct ptp_clock_request *rq, int on) | |
402 | { | |
403 | struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); | |
404 | ||
405 | switch (rq->type) { | |
406 | case PTP_CLK_REQ_EXTTS: | |
407 | switch (rq->extts.index) { | |
408 | case 0: | |
409 | pch_dev->exts0_enabled = on ? 1 : 0; | |
410 | break; | |
411 | case 1: | |
412 | pch_dev->exts1_enabled = on ? 1 : 0; | |
413 | break; | |
414 | default: | |
415 | return -EINVAL; | |
416 | } | |
417 | return 0; | |
418 | default: | |
419 | break; | |
420 | } | |
421 | ||
422 | return -EOPNOTSUPP; | |
423 | } | |
424 | ||
7d47e9a2 | 425 | static const struct ptp_clock_info ptp_pch_caps = { |
863d08ec TS |
426 | .owner = THIS_MODULE, |
427 | .name = "PCH timer", | |
428 | .max_adj = 50000000, | |
429 | .n_ext_ts = N_EXT_TS, | |
4986b4f0 | 430 | .n_pins = 0, |
863d08ec | 431 | .pps = 0, |
2e77eded | 432 | .adjfine = ptp_pch_adjfine, |
863d08ec | 433 | .adjtime = ptp_pch_adjtime, |
a043a729 RC |
434 | .gettime64 = ptp_pch_gettime, |
435 | .settime64 = ptp_pch_settime, | |
863d08ec TS |
436 | .enable = ptp_pch_enable, |
437 | }; | |
438 | ||
b1f7c8cc | 439 | static void pch_remove(struct pci_dev *pdev) |
863d08ec TS |
440 | { |
441 | struct pch_dev *chip = pci_get_drvdata(pdev); | |
442 | ||
874f50c8 | 443 | free_irq(pdev->irq, chip); |
863d08ec | 444 | ptp_clock_unregister(chip->ptp_clock); |
863d08ec TS |
445 | } |
446 | ||
5c0a4256 | 447 | static s32 |
863d08ec TS |
448 | pch_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
449 | { | |
450 | s32 ret; | |
451 | unsigned long flags; | |
452 | struct pch_dev *chip; | |
453 | ||
874f50c8 | 454 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); |
863d08ec TS |
455 | if (chip == NULL) |
456 | return -ENOMEM; | |
457 | ||
458 | /* enable the 1588 pci device */ | |
874f50c8 | 459 | ret = pcim_enable_device(pdev); |
863d08ec TS |
460 | if (ret != 0) { |
461 | dev_err(&pdev->dev, "could not enable the pci device\n"); | |
874f50c8 | 462 | return ret; |
863d08ec TS |
463 | } |
464 | ||
874f50c8 AS |
465 | ret = pcim_iomap_regions(pdev, BIT(IO_MEM_BAR), "1588_regs"); |
466 | if (ret) { | |
863d08ec | 467 | dev_err(&pdev->dev, "could not locate IO memory address\n"); |
874f50c8 | 468 | return ret; |
863d08ec TS |
469 | } |
470 | ||
471 | /* get the virtual address to the 1588 registers */ | |
874f50c8 | 472 | chip->regs = pcim_iomap_table(pdev)[IO_MEM_BAR]; |
863d08ec | 473 | chip->caps = ptp_pch_caps; |
1ef76158 | 474 | chip->ptp_clock = ptp_clock_register(&chip->caps, &pdev->dev); |
874f50c8 AS |
475 | if (IS_ERR(chip->ptp_clock)) |
476 | return PTR_ERR(chip->ptp_clock); | |
863d08ec TS |
477 | |
478 | spin_lock_init(&chip->register_lock); | |
479 | ||
480 | ret = request_irq(pdev->irq, &isr, IRQF_SHARED, KBUILD_MODNAME, chip); | |
481 | if (ret != 0) { | |
482 | dev_err(&pdev->dev, "failed to get irq %d\n", pdev->irq); | |
483 | goto err_req_irq; | |
484 | } | |
485 | ||
486 | /* indicate success */ | |
487 | chip->irq = pdev->irq; | |
488 | chip->pdev = pdev; | |
489 | pci_set_drvdata(pdev, chip); | |
490 | ||
491 | spin_lock_irqsave(&chip->register_lock, flags); | |
492 | /* reset the ieee1588 h/w */ | |
493 | pch_reset(chip); | |
494 | ||
495 | iowrite32(DEFAULT_ADDEND, &chip->regs->addend); | |
8664d49a | 496 | iowrite64_lo_hi(1, &chip->regs->trgt_lo); |
863d08ec | 497 | iowrite32(PCH_TSE_TTIPEND, &chip->regs->event); |
863d08ec TS |
498 | |
499 | pch_eth_enable_set(chip); | |
500 | ||
501 | if (strcmp(pch_param.station, "00:00:00:00:00:00") != 0) { | |
502 | if (pch_set_station_address(pch_param.station, pdev) != 0) { | |
503 | dev_err(&pdev->dev, | |
504 | "Invalid station address parameter\n" | |
505 | "Module loaded but station address not set correctly\n" | |
506 | ); | |
507 | } | |
508 | } | |
509 | spin_unlock_irqrestore(&chip->register_lock, flags); | |
510 | return 0; | |
511 | ||
512 | err_req_irq: | |
513 | ptp_clock_unregister(chip->ptp_clock); | |
863d08ec | 514 | |
863d08ec TS |
515 | dev_err(&pdev->dev, "probe failed(ret=0x%x)\n", ret); |
516 | ||
517 | return ret; | |
518 | } | |
519 | ||
9baa3c34 | 520 | static const struct pci_device_id pch_ieee1588_pcidev_id[] = { |
863d08ec TS |
521 | { |
522 | .vendor = PCI_VENDOR_ID_INTEL, | |
523 | .device = PCI_DEVICE_ID_PCH_1588 | |
524 | }, | |
525 | {0} | |
526 | }; | |
7cd8b154 | 527 | MODULE_DEVICE_TABLE(pci, pch_ieee1588_pcidev_id); |
863d08ec | 528 | |
d8d78949 | 529 | static struct pci_driver pch_driver = { |
863d08ec TS |
530 | .name = KBUILD_MODNAME, |
531 | .id_table = pch_ieee1588_pcidev_id, | |
532 | .probe = pch_probe, | |
533 | .remove = pch_remove, | |
863d08ec | 534 | }; |
3fa66d3d | 535 | module_pci_driver(pch_driver); |
863d08ec | 536 | |
7d3ac5c7 S |
537 | module_param_string(station, |
538 | pch_param.station, sizeof(pch_param.station), 0444); | |
863d08ec | 539 | MODULE_PARM_DESC(station, |
55c31b5b | 540 | "IEEE 1588 station address to use - colon separated hex values"); |
863d08ec TS |
541 | |
542 | MODULE_AUTHOR("LAPIS SEMICONDUCTOR, <tshimizu818@gmail.com>"); | |
543 | MODULE_DESCRIPTION("PTP clock using the EG20T timer"); | |
544 | MODULE_LICENSE("GPL"); |