Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ixp4xx_eth: make ptp support a platform driver

After the recent ixp4xx cleanups, the ptp driver has gained a
build failure in some configurations:

drivers/net/ethernet/xscale/ptp_ixp46x.c: In function 'ptp_ixp_init':
drivers/net/ethernet/xscale/ptp_ixp46x.c:290:51: error: 'IXP4XX_TIMESYNC_BASE_VIRT' undeclared (first use in this function)

Avoid the last bit of hardcoded constants from platform headers
by turning the ptp driver bit into a platform driver and passing
the IRQ and MMIO address as resources.

This is a bit tricky:

- The interface between the two drivers is now the new
ixp46x_ptp_find() function, replacing the global
ixp46x_phc_index variable. The call is done as late
as possible, in hwtstamp_set(), to ensure that the
ptp device is fully probed.

- As the ptp driver is now called by the network driver, the
link dependency is reversed, which in turn requires a small
Makefile hack

- The GPIO number is still left hardcoded. This is clearly not
great, but it can be addressed later. Note that commit 98ac0cc270b7
("ARM: ixp4xx: Convert to MULTI_IRQ_HANDLER") changed the
IRQ number to something meaningless. Passing the correct IRQ
in a resource fixes this.

- When the PTP driver is disabled, ethtool .get_ts_info()
now correctly lists only software timestamping regardless
of the hardware.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
[Fix a missing include]
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Arnd Bergmann and committed by
David S. Miller
9055a2f5 27c77943

+92 -34
+14
arch/arm/mach-ixp4xx/common.c
··· 268 268 .resource = ixp46x_i2c_resources 269 269 }; 270 270 271 + static struct resource ixp46x_ptp_resources[] = { 272 + DEFINE_RES_MEM(IXP4XX_TIMESYNC_BASE_PHYS, SZ_4K), 273 + DEFINE_RES_IRQ_NAMED(IRQ_IXP4XX_GPIO8, "master"), 274 + DEFINE_RES_IRQ_NAMED(IRQ_IXP4XX_GPIO7, "slave"), 275 + }; 276 + 277 + static struct platform_device ixp46x_ptp = { 278 + .name = "ptp-ixp46x", 279 + .id = -1, 280 + .resource = ixp46x_ptp_resources, 281 + .num_resources = ARRAY_SIZE(ixp46x_ptp_resources), 282 + }; 283 + 271 284 static struct platform_device *ixp46x_devices[] __initdata = { 272 285 &ixp46x_hwrandom_device, 273 286 &ixp46x_i2c_controller, 287 + &ixp46x_ptp, 274 288 }; 275 289 276 290 unsigned long ixp4xx_exp_bus_size;
+2 -2
drivers/net/ethernet/xscale/Kconfig
··· 29 29 on IXP4xx processor. 30 30 31 31 config PTP_1588_CLOCK_IXP46X 32 - tristate "Intel IXP46x as PTP clock" 32 + bool "Intel IXP46x as PTP clock" 33 33 depends on IXP4XX_ETH 34 - depends on PTP_1588_CLOCK 34 + depends on PTP_1588_CLOCK=y || PTP_1588_CLOCK=IXP4XX_ETH 35 35 default y 36 36 help 37 37 This driver adds support for using the IXP46X as a PTP
+5 -1
drivers/net/ethernet/xscale/Makefile
··· 3 3 # Makefile for the Intel XScale IXP device drivers. 4 4 # 5 5 6 + # Keep this link order to avoid deferred probing 7 + ifdef CONFIG_PTP_1588_CLOCK_IXP46X 8 + obj-$(CONFIG_IXP4XX_ETH) += ptp_ixp46x.o 9 + endif 10 + 6 11 obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o 7 - obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o
+11 -2
drivers/net/ethernet/xscale/ixp46x_ts.h
··· 62 62 #define TX_SNAPSHOT_LOCKED (1<<0) 63 63 #define RX_SNAPSHOT_LOCKED (1<<1) 64 64 65 - /* The ptp_ixp46x module will set this variable */ 66 - extern int ixp46x_phc_index; 65 + #if IS_ENABLED(CONFIG_PTP_1588_CLOCK_IXP46X) 66 + int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index); 67 + #else 68 + static inline int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index) 69 + { 70 + *regs = NULL; 71 + *phc_index = -1; 72 + 73 + return -ENODEV; 74 + } 75 + #endif 67 76 68 77 #endif
+19 -9
drivers/net/ethernet/xscale/ixp4xx_eth.c
··· 169 169 170 170 struct port { 171 171 struct eth_regs __iomem *regs; 172 + struct ixp46x_ts_regs __iomem *timesync_regs; 173 + int phc_index; 172 174 struct npe *npe; 173 175 struct net_device *netdev; 174 176 struct napi_struct napi; ··· 297 295 298 296 ch = PORT2CHANNEL(port); 299 297 300 - regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; 298 + regs = port->timesync_regs; 301 299 302 300 val = __raw_readl(&regs->channel[ch].ch_event); 303 301 ··· 342 340 343 341 ch = PORT2CHANNEL(port); 344 342 345 - regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; 343 + regs = port->timesync_regs; 346 344 347 345 /* 348 346 * This really stinks, but we have to poll for the Tx time stamp. ··· 377 375 struct hwtstamp_config cfg; 378 376 struct ixp46x_ts_regs *regs; 379 377 struct port *port = netdev_priv(netdev); 378 + int ret; 380 379 int ch; 381 380 382 381 if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) ··· 386 383 if (cfg.flags) /* reserved for future extensions */ 387 384 return -EINVAL; 388 385 386 + ret = ixp46x_ptp_find(&port->timesync_regs, &port->phc_index); 387 + if (ret) 388 + return ret; 389 + 389 390 ch = PORT2CHANNEL(port); 390 - regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; 391 + regs = port->timesync_regs; 391 392 392 393 if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON) 393 394 return -ERANGE; ··· 995 988 strlcpy(info->bus_info, "internal", sizeof(info->bus_info)); 996 989 } 997 990 998 - int ixp46x_phc_index = -1; 999 - EXPORT_SYMBOL_GPL(ixp46x_phc_index); 1000 - 1001 991 static int ixp4xx_get_ts_info(struct net_device *dev, 1002 992 struct ethtool_ts_info *info) 1003 993 { 1004 - if (!cpu_is_ixp46x()) { 994 + struct port *port = netdev_priv(dev); 995 + 996 + if (port->phc_index < 0) 997 + ixp46x_ptp_find(&port->timesync_regs, &port->phc_index); 998 + 999 + info->phc_index = port->phc_index; 1000 + 1001 + if (info->phc_index < 0) { 1005 1002 info->so_timestamping = 1006 1003 SOF_TIMESTAMPING_TX_SOFTWARE | 1007 1004 SOF_TIMESTAMPING_RX_SOFTWARE | 1008 1005 SOF_TIMESTAMPING_SOFTWARE; 1009 - info->phc_index = -1; 1010 1006 return 0; 1011 1007 } 1012 1008 info->so_timestamping = 1013 1009 SOF_TIMESTAMPING_TX_HARDWARE | 1014 1010 SOF_TIMESTAMPING_RX_HARDWARE | 1015 1011 SOF_TIMESTAMPING_RAW_HARDWARE; 1016 - info->phc_index = ixp46x_phc_index; 1017 1012 info->tx_types = 1018 1013 (1 << HWTSTAMP_TX_OFF) | 1019 1014 (1 << HWTSTAMP_TX_ON); ··· 1490 1481 port = netdev_priv(ndev); 1491 1482 port->netdev = ndev; 1492 1483 port->id = plat->npe; 1484 + port->phc_index = -1; 1493 1485 1494 1486 /* Get the port resource and remap */ 1495 1487 port->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+41 -20
drivers/net/ethernet/xscale/ptp_ixp46x.c
··· 5 5 * Copyright (C) 2010 OMICRON electronics GmbH 6 6 */ 7 7 #include <linux/device.h> 8 + #include <linux/module.h> 8 9 #include <linux/err.h> 9 10 #include <linux/gpio.h> 10 11 #include <linux/init.h> ··· 14 13 #include <linux/irq.h> 15 14 #include <linux/kernel.h> 16 15 #include <linux/ptp_clock_kernel.h> 16 + #include <linux/platform_device.h> 17 17 #include <linux/soc/ixp4xx/cpu.h> 18 18 #include <linux/module.h> 19 19 #include <mach/ixp4xx-regs.h> ··· 24 22 #define DRIVER "ptp_ixp46x" 25 23 #define N_EXT_TS 2 26 24 #define MASTER_GPIO 8 27 - #define MASTER_IRQ 25 28 25 #define SLAVE_GPIO 7 29 - #define SLAVE_IRQ 24 30 26 31 27 struct ixp_clock { 32 28 struct ixp46x_ts_regs *regs; ··· 32 32 struct ptp_clock_info caps; 33 33 int exts0_enabled; 34 34 int exts1_enabled; 35 + int slave_irq; 36 + int master_irq; 35 37 }; 36 38 37 - DEFINE_SPINLOCK(register_lock); 39 + static DEFINE_SPINLOCK(register_lock); 38 40 39 41 /* 40 42 * Register access functions ··· 277 275 return irq; 278 276 } 279 277 280 - static void __exit ptp_ixp_exit(void) 278 + int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index) 281 279 { 282 - free_irq(MASTER_IRQ, &ixp_clock); 283 - free_irq(SLAVE_IRQ, &ixp_clock); 284 - ixp46x_phc_index = -1; 280 + *regs = ixp_clock.regs; 281 + *phc_index = ptp_clock_index(ixp_clock.ptp_clock); 282 + 283 + if (!ixp_clock.ptp_clock) 284 + return -EPROBE_DEFER; 285 + 286 + return 0; 287 + } 288 + EXPORT_SYMBOL_GPL(ixp46x_ptp_find); 289 + 290 + static int ptp_ixp_remove(struct platform_device *pdev) 291 + { 292 + free_irq(ixp_clock.master_irq, &ixp_clock); 293 + free_irq(ixp_clock.slave_irq, &ixp_clock); 285 294 ptp_clock_unregister(ixp_clock.ptp_clock); 295 + ixp_clock.ptp_clock = NULL; 296 + 297 + return 0; 286 298 } 287 299 288 - static int __init ptp_ixp_init(void) 300 + static int ptp_ixp_probe(struct platform_device *pdev) 289 301 { 290 - if (!cpu_is_ixp46x()) 291 - return -ENODEV; 292 - 293 - ixp_clock.regs = 294 - (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; 302 + ixp_clock.regs = devm_platform_ioremap_resource(pdev, 0); 303 + ixp_clock.master_irq = platform_get_irq(pdev, 0); 304 + ixp_clock.slave_irq = platform_get_irq(pdev, 1); 305 + if (IS_ERR(ixp_clock.regs) || 306 + !ixp_clock.master_irq || !ixp_clock.slave_irq) 307 + return -ENXIO; 295 308 296 309 ixp_clock.caps = ptp_ixp_caps; 297 310 ··· 315 298 if (IS_ERR(ixp_clock.ptp_clock)) 316 299 return PTR_ERR(ixp_clock.ptp_clock); 317 300 318 - ixp46x_phc_index = ptp_clock_index(ixp_clock.ptp_clock); 319 - 320 301 __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend); 321 302 __raw_writel(1, &ixp_clock.regs->trgt_lo); 322 303 __raw_writel(0, &ixp_clock.regs->trgt_hi); 323 304 __raw_writel(TTIPEND, &ixp_clock.regs->event); 324 305 325 - if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) { 306 + if (ixp_clock.master_irq != setup_interrupt(MASTER_GPIO)) { 326 307 pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO); 327 308 goto no_master; 328 309 } 329 - if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) { 310 + if (ixp_clock.slave_irq != setup_interrupt(SLAVE_GPIO)) { 330 311 pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO); 331 312 goto no_slave; 332 313 } 333 314 334 315 return 0; 335 316 no_slave: 336 - free_irq(MASTER_IRQ, &ixp_clock); 317 + free_irq(ixp_clock.master_irq, &ixp_clock); 337 318 no_master: 338 319 ptp_clock_unregister(ixp_clock.ptp_clock); 320 + ixp_clock.ptp_clock = NULL; 339 321 return -ENODEV; 340 322 } 341 323 342 - module_init(ptp_ixp_init); 343 - module_exit(ptp_ixp_exit); 324 + static struct platform_driver ptp_ixp_driver = { 325 + .driver.name = "ptp-ixp46x", 326 + .driver.suppress_bind_attrs = true, 327 + .probe = ptp_ixp_probe, 328 + .remove = ptp_ixp_remove, 329 + }; 330 + module_platform_driver(ptp_ixp_driver); 344 331 345 332 MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 346 333 MODULE_DESCRIPTION("PTP clock using the IXP46X timer");