diff options
Diffstat (limited to 'drivers/net/ethernet/xscale')
-rw-r--r-- | drivers/net/ethernet/xscale/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/xscale/Makefile | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/xscale/ixp46x_ts.h | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/xscale/ixp4xx_eth.c | 247 | ||||
-rw-r--r-- | drivers/net/ethernet/xscale/ptp_ixp46x.c | 125 |
5 files changed, 237 insertions, 165 deletions
diff --git a/drivers/net/ethernet/xscale/Kconfig b/drivers/net/ethernet/xscale/Kconfig index 98aa7b8ddb06..b33f64c54b0e 100644 --- a/drivers/net/ethernet/xscale/Kconfig +++ b/drivers/net/ethernet/xscale/Kconfig @@ -8,7 +8,7 @@ config NET_VENDOR_XSCALE default y depends on NET_VENDOR_INTEL && (ARM && ARCH_IXP4XX && \ IXP4XX_NPE && IXP4XX_QMGR) - ---help--- + help If you have a network (Ethernet) card belonging to this class, say Y. Note that the answer to this question does not directly affect the @@ -20,17 +20,18 @@ if NET_VENDOR_XSCALE config IXP4XX_ETH tristate "Intel IXP4xx Ethernet support" - depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR + depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR && OF select PHYLIB + select OF_MDIO select NET_PTP_CLASSIFY - ---help--- + help Say Y here if you want to use built-in Ethernet ports on IXP4xx processor. config PTP_1588_CLOCK_IXP46X - tristate "Intel IXP46x as PTP clock" + bool "Intel IXP46x as PTP clock" depends on IXP4XX_ETH - depends on PTP_1588_CLOCK + depends on PTP_1588_CLOCK=y || PTP_1588_CLOCK=IXP4XX_ETH default y help This driver adds support for using the IXP46X as a PTP diff --git a/drivers/net/ethernet/xscale/Makefile b/drivers/net/ethernet/xscale/Makefile index 607f91b1e878..e935f2a2979f 100644 --- a/drivers/net/ethernet/xscale/Makefile +++ b/drivers/net/ethernet/xscale/Makefile @@ -3,5 +3,9 @@ # Makefile for the Intel XScale IXP device drivers. # +# Keep this link order to avoid deferred probing +ifdef CONFIG_PTP_1588_CLOCK_IXP46X +obj-$(CONFIG_IXP4XX_ETH) += ptp_ixp46x.o +endif + obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o -obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o diff --git a/drivers/net/ethernet/xscale/ixp46x_ts.h b/drivers/net/ethernet/xscale/ixp46x_ts.h index d792130e27b0..ee9b93ded20a 100644 --- a/drivers/net/ethernet/xscale/ixp46x_ts.h +++ b/drivers/net/ethernet/xscale/ixp46x_ts.h @@ -62,7 +62,16 @@ struct ixp46x_ts_regs { #define TX_SNAPSHOT_LOCKED (1<<0) #define RX_SNAPSHOT_LOCKED (1<<1) -/* The ptp_ixp46x module will set this variable */ -extern int ixp46x_phc_index; +#if IS_ENABLED(CONFIG_PTP_1588_CLOCK_IXP46X) +int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index); +#else +static inline int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index) +{ + *regs = NULL; + *phc_index = -1; + + return -ENODEV; +} +#endif #endif diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 269596c15133..3b0c5f177447 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -28,14 +28,21 @@ #include <linux/kernel.h> #include <linux/net_tstamp.h> #include <linux/of.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> #include <linux/phy.h> -#include <linux/platform_data/eth_ixp4xx.h> #include <linux/platform_device.h> #include <linux/ptp_classify.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/soc/ixp4xx/npe.h> #include <linux/soc/ixp4xx/qmgr.h> +#include <linux/soc/ixp4xx/cpu.h> +#include <linux/types.h> + +#define IXP4XX_ETH_NPEA 0x00 +#define IXP4XX_ETH_NPEB 0x10 +#define IXP4XX_ETH_NPEC 0x20 #include "ixp46x_ts.h" @@ -145,6 +152,16 @@ typedef void buffer_t; #define free_buffer_irq kfree #endif +/* Information about built-in Ethernet MAC interfaces */ +struct eth_plat_info { + u8 phy; /* MII PHY ID, 0 - 31 */ + u8 rxq; /* configurable, currently 0 - 31 only */ + u8 txreadyq; + u8 hwaddr[ETH_ALEN]; + u8 npe; /* NPE instance used by this interface */ + bool has_mdio; /* If this instance has an MDIO bus */ +}; + struct eth_regs { u32 tx_control[2], __res1[2]; /* 000 */ u32 rx_control[2], __res2[2]; /* 010 */ @@ -165,15 +182,16 @@ struct eth_regs { }; struct port { - struct resource *mem_res; struct eth_regs __iomem *regs; + struct ixp46x_ts_regs __iomem *timesync_regs; + int phc_index; struct npe *npe; struct net_device *netdev; struct napi_struct napi; struct eth_plat_info *plat; buffer_t *rx_buff_tab[RX_DESCS], *tx_buff_tab[TX_DESCS]; struct desc *desc_tab; /* coherent */ - u32 desc_tab_phys; + dma_addr_t desc_tab_phys; int id; /* logical port ID */ int speed, duplex; u8 firmware[4]; @@ -247,9 +265,10 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt) } #endif -static spinlock_t mdio_lock; +static DEFINE_SPINLOCK(mdio_lock); static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */ static struct mii_bus *mdio_bus; +static struct device_node *mdio_bus_np; static int ports_open; static struct port *npe_port_tab[MAX_NPES]; static struct dma_pool *dma_pool; @@ -292,7 +311,7 @@ static void ixp_rx_timestamp(struct port *port, struct sk_buff *skb) ch = PORT2CHANNEL(port); - regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; + regs = port->timesync_regs; val = __raw_readl(®s->channel[ch].ch_event); @@ -337,7 +356,7 @@ static void ixp_tx_timestamp(struct port *port, struct sk_buff *skb) ch = PORT2CHANNEL(port); - regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; + regs = port->timesync_regs; /* * This really stinks, but we have to poll for the Tx time stamp. @@ -372,16 +391,18 @@ static int hwtstamp_set(struct net_device *netdev, struct ifreq *ifr) struct hwtstamp_config cfg; struct ixp46x_ts_regs *regs; struct port *port = netdev_priv(netdev); + int ret; int ch; if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) return -EFAULT; - if (cfg.flags) /* reserved for future extensions */ - return -EINVAL; + ret = ixp46x_ptp_find(&port->timesync_regs, &port->phc_index); + if (ret) + return ret; ch = PORT2CHANNEL(port); - regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; + regs = port->timesync_regs; if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON) return -ERANGE; @@ -528,13 +549,13 @@ static int ixp4xx_mdio_register(struct eth_regs __iomem *regs) mdio_regs = regs; __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control); - spin_lock_init(&mdio_lock); mdio_bus->name = "IXP4xx MII Bus"; mdio_bus->read = &ixp4xx_mdio_read; mdio_bus->write = &ixp4xx_mdio_write; snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "ixp4xx-eth-0"); - if ((err = mdiobus_register(mdio_bus))) + err = of_mdiobus_register(mdio_bus, mdio_bus_np); + if (err) mdiobus_free(mdio_bus); return err; } @@ -820,7 +841,7 @@ static void eth_txdone_irq(void *unused) } } -static int eth_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t eth_xmit(struct sk_buff *skb, struct net_device *dev) { struct port *port = netdev_priv(dev); unsigned int txreadyq = port->plat->txreadyq; @@ -847,14 +868,14 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev) bytes = len; mem = skb->data; #else - offset = (int)skb->data & 3; /* keep 32-bit alignment */ + offset = (uintptr_t)skb->data & 3; /* keep 32-bit alignment */ bytes = ALIGN(offset + len, 4); if (!(mem = kmalloc(bytes, GFP_ATOMIC))) { dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; } - memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4); + memcpy_swab32(mem, (u32 *)((uintptr_t)skb->data & ~3), bytes / 4); #endif phys = dma_map_single(&dev->dev, mem, bytes, DMA_TO_DEVICE); @@ -978,32 +999,34 @@ static void ixp4xx_get_drvinfo(struct net_device *dev, { struct port *port = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); snprintf(info->fw_version, sizeof(info->fw_version), "%u:%u:%u:%u", port->firmware[0], port->firmware[1], port->firmware[2], port->firmware[3]); - strlcpy(info->bus_info, "internal", sizeof(info->bus_info)); + strscpy(info->bus_info, "internal", sizeof(info->bus_info)); } -int ixp46x_phc_index = -1; -EXPORT_SYMBOL_GPL(ixp46x_phc_index); - static int ixp4xx_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) { - if (!cpu_is_ixp46x()) { + struct port *port = netdev_priv(dev); + + if (port->phc_index < 0) + ixp46x_ptp_find(&port->timesync_regs, &port->phc_index); + + info->phc_index = port->phc_index; + + if (info->phc_index < 0) { info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE; - info->phc_index = -1; return 0; } info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; - info->phc_index = ixp46x_phc_index; info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); @@ -1086,16 +1109,15 @@ static int init_queues(struct port *port) int i; if (!ports_open) { - dma_pool = dma_pool_create(DRV_NAME, port->netdev->dev.parent, + dma_pool = dma_pool_create(DRV_NAME, &port->netdev->dev, POOL_ALLOC_SIZE, 32, 0); if (!dma_pool) return -ENOMEM; } - if (!(port->desc_tab = dma_pool_alloc(dma_pool, GFP_KERNEL, - &port->desc_tab_phys))) + port->desc_tab = dma_pool_zalloc(dma_pool, GFP_KERNEL, &port->desc_tab_phys); + if (!port->desc_tab) return -ENOMEM; - memset(port->desc_tab, 0, POOL_ALLOC_SIZE); memset(port->rx_buff_tab, 0, sizeof(port->rx_buff_tab)); /* tables */ memset(port->tx_buff_tab, 0, sizeof(port->tx_buff_tab)); @@ -1354,24 +1376,82 @@ static const struct net_device_ops ixp4xx_netdev_ops = { .ndo_stop = eth_close, .ndo_start_xmit = eth_xmit, .ndo_set_rx_mode = eth_set_mcast_list, - .ndo_do_ioctl = eth_ioctl, + .ndo_eth_ioctl = eth_ioctl, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; +static struct eth_plat_info *ixp4xx_of_get_platdata(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct of_phandle_args queue_spec; + struct of_phandle_args npe_spec; + struct device_node *mdio_np; + struct eth_plat_info *plat; + u8 mac[ETH_ALEN]; + int ret; + + plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return NULL; + + ret = of_parse_phandle_with_fixed_args(np, "intel,npe-handle", 1, 0, + &npe_spec); + if (ret) { + dev_err(dev, "no NPE engine specified\n"); + return NULL; + } + /* NPE ID 0x00, 0x10, 0x20... */ + plat->npe = (npe_spec.args[0] << 4); + + /* Check if this device has an MDIO bus */ + mdio_np = of_get_child_by_name(np, "mdio"); + if (mdio_np) { + plat->has_mdio = true; + mdio_bus_np = mdio_np; + /* DO NOT put the mdio_np, it will be used */ + } + + /* Get the rx queue as a resource from queue manager */ + ret = of_parse_phandle_with_fixed_args(np, "queue-rx", 1, 0, + &queue_spec); + if (ret) { + dev_err(dev, "no rx queue phandle\n"); + return NULL; + } + plat->rxq = queue_spec.args[0]; + + /* Get the txready queue as resource from queue manager */ + ret = of_parse_phandle_with_fixed_args(np, "queue-txready", 1, 0, + &queue_spec); + if (ret) { + dev_err(dev, "no txready queue phandle\n"); + return NULL; + } + plat->txreadyq = queue_spec.args[0]; + + ret = of_get_mac_address(np, mac); + if (!ret) { + dev_info(dev, "Setting macaddr from DT %pM\n", mac); + memcpy(plat->hwaddr, mac, ETH_ALEN); + } + + return plat; +} + static int ixp4xx_eth_probe(struct platform_device *pdev) { - char phy_id[MII_BUS_ID_SIZE + 3]; struct phy_device *phydev = NULL; struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct eth_plat_info *plat; - resource_size_t regs_phys; struct net_device *ndev; - struct resource *res; struct port *port; int err; - plat = dev_get_platdata(dev); + plat = ixp4xx_of_get_platdata(dev); + if (!plat) + return -ENODEV; if (!(ndev = devm_alloc_etherdev(dev, sizeof(struct port)))) return -ENOMEM; @@ -1379,76 +1459,46 @@ static int ixp4xx_eth_probe(struct platform_device *pdev) SET_NETDEV_DEV(ndev, dev); port = netdev_priv(ndev); port->netdev = ndev; - port->id = pdev->id; + port->id = plat->npe; + port->phc_index = -1; /* Get the port resource and remap */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - regs_phys = res->start; - port->regs = devm_ioremap_resource(dev, res); - - switch (port->id) { - case IXP4XX_ETH_NPEA: - /* If the MDIO bus is not up yet, defer probe */ - if (!mdio_bus) - return -EPROBE_DEFER; - break; - case IXP4XX_ETH_NPEB: - /* - * On all except IXP43x, NPE-B is used for the MDIO bus. - * If there is no NPE-B in the feature set, bail out, else - * register the MDIO bus. - */ - if (!cpu_is_ixp43x()) { - if (!(ixp4xx_read_feature_bits() & - IXP4XX_FEATURE_NPEB_ETH0)) - return -ENODEV; - /* Else register the MDIO bus on NPE-B */ - if ((err = ixp4xx_mdio_register(port->regs))) - return err; - } - if (!mdio_bus) - return -EPROBE_DEFER; - break; - case IXP4XX_ETH_NPEC: - /* - * IXP43x lacks NPE-B and uses NPE-C for the MDIO bus access, - * of there is no NPE-C, no bus, nothing works, so bail out. - */ - if (cpu_is_ixp43x()) { - if (!(ixp4xx_read_feature_bits() & - IXP4XX_FEATURE_NPEC_ETH)) - return -ENODEV; - /* Else register the MDIO bus on NPE-C */ - if ((err = ixp4xx_mdio_register(port->regs))) - return err; + port->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(port->regs)) + return PTR_ERR(port->regs); + + /* Register the MDIO bus if we have it */ + if (plat->has_mdio) { + err = ixp4xx_mdio_register(port->regs); + if (err) { + dev_err(dev, "failed to register MDIO bus\n"); + return err; } - if (!mdio_bus) - return -EPROBE_DEFER; - break; - default: - return -ENODEV; } + /* If the instance with the MDIO bus has not yet appeared, + * defer probing until it gets probed. + */ + if (!mdio_bus) + return -EPROBE_DEFER; ndev->netdev_ops = &ixp4xx_netdev_ops; ndev->ethtool_ops = &ixp4xx_ethtool_ops; ndev->tx_queue_len = 100; + /* Inherit the DMA masks from the platform device */ + ndev->dev.dma_mask = dev->dma_mask; + ndev->dev.coherent_dma_mask = dev->coherent_dma_mask; - netif_napi_add(ndev, &port->napi, eth_poll, NAPI_WEIGHT); + netif_napi_add_weight(ndev, &port->napi, eth_poll, NAPI_WEIGHT); if (!(port->npe = npe_request(NPE_ID(port->id)))) return -EIO; - port->mem_res = request_mem_region(regs_phys, REGS_SIZE, ndev->name); - if (!port->mem_res) { - err = -EBUSY; - goto err_npe_rel; - } - port->plat = plat; npe_port_tab[NPE_ID(port->id)] = port; - memcpy(ndev->dev_addr, plat->hwaddr, ETH_ALEN); + if (is_valid_ether_addr(plat->hwaddr)) + eth_hw_addr_set(ndev, plat->hwaddr); + else + eth_hw_addr_random(ndev); platform_set_drvdata(pdev, ndev); @@ -1458,12 +1508,10 @@ static int ixp4xx_eth_probe(struct platform_device *pdev) __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control); udelay(50); - snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, - mdio_bus->id, plat->phy); - phydev = phy_connect(ndev, phy_id, &ixp4xx_adjust_link, - PHY_INTERFACE_MODE_MII); - if (IS_ERR(phydev)) { - err = PTR_ERR(phydev); + phydev = of_phy_get_and_connect(ndev, np, ixp4xx_adjust_link); + if (!phydev) { + err = -ENODEV; + dev_err(dev, "no phydev\n"); goto err_free_mem; } @@ -1481,8 +1529,6 @@ err_phy_dis: phy_disconnect(phydev); err_free_mem: npe_port_tab[NPE_ID(port->id)] = NULL; - release_resource(port->mem_res); -err_npe_rel: npe_release(port->npe); return err; } @@ -1498,12 +1544,21 @@ static int ixp4xx_eth_remove(struct platform_device *pdev) ixp4xx_mdio_remove(); npe_port_tab[NPE_ID(port->id)] = NULL; npe_release(port->npe); - release_resource(port->mem_res); return 0; } +static const struct of_device_id ixp4xx_eth_of_match[] = { + { + .compatible = "intel,ixp4xx-ethernet", + }, + { }, +}; + static struct platform_driver ixp4xx_eth_driver = { - .driver.name = DRV_NAME, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(ixp4xx_eth_of_match), + }, .probe = ixp4xx_eth_probe, .remove = ixp4xx_eth_remove, }; diff --git a/drivers/net/ethernet/xscale/ptp_ixp46x.c b/drivers/net/ethernet/xscale/ptp_ixp46x.c index 9ecc395239e9..9abbdb71e629 100644 --- a/drivers/net/ethernet/xscale/ptp_ixp46x.c +++ b/drivers/net/ethernet/xscale/ptp_ixp46x.c @@ -5,25 +5,22 @@ * Copyright (C) 2010 OMICRON electronics GmbH */ #include <linux/device.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> -#include <linux/module.h> - #include <linux/ptp_clock_kernel.h> +#include <linux/platform_device.h> +#include <linux/soc/ixp4xx/cpu.h> #include "ixp46x_ts.h" #define DRIVER "ptp_ixp46x" #define N_EXT_TS 2 -#define MASTER_GPIO 8 -#define MASTER_IRQ 25 -#define SLAVE_GPIO 7 -#define SLAVE_IRQ 24 struct ixp_clock { struct ixp46x_ts_regs *regs; @@ -31,9 +28,11 @@ struct ixp_clock { struct ptp_clock_info caps; int exts0_enabled; int exts1_enabled; + int slave_irq; + int master_irq; }; -DEFINE_SPINLOCK(register_lock); +static DEFINE_SPINLOCK(register_lock); /* * Register access functions @@ -242,53 +241,38 @@ static const struct ptp_clock_info ptp_ixp_caps = { static struct ixp_clock ixp_clock; -static int setup_interrupt(int gpio) +int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index) { - int irq; - int err; - - err = gpio_request(gpio, "ixp4-ptp"); - if (err) - return err; - - err = gpio_direction_input(gpio); - if (err) - return err; + *regs = ixp_clock.regs; + *phc_index = ptp_clock_index(ixp_clock.ptp_clock); - irq = gpio_to_irq(gpio); - if (irq < 0) - return irq; - - err = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING); - if (err) { - pr_err("cannot set trigger type for irq %d\n", irq); - return err; - } - - err = request_irq(irq, isr, 0, DRIVER, &ixp_clock); - if (err) { - pr_err("request_irq failed for irq %d\n", irq); - return err; - } + if (!ixp_clock.ptp_clock) + return -EPROBE_DEFER; - return irq; + return 0; } +EXPORT_SYMBOL_GPL(ixp46x_ptp_find); -static void __exit ptp_ixp_exit(void) +/* Called from the registered devm action */ +static void ptp_ixp_unregister_action(void *d) { - free_irq(MASTER_IRQ, &ixp_clock); - free_irq(SLAVE_IRQ, &ixp_clock); - ixp46x_phc_index = -1; - ptp_clock_unregister(ixp_clock.ptp_clock); + struct ptp_clock *ptp_clock = d; + + ptp_clock_unregister(ptp_clock); + ixp_clock.ptp_clock = NULL; } -static int __init ptp_ixp_init(void) +static int ptp_ixp_probe(struct platform_device *pdev) { - if (!cpu_is_ixp46x()) - return -ENODEV; + struct device *dev = &pdev->dev; + int ret; - ixp_clock.regs = - (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; + ixp_clock.regs = devm_platform_ioremap_resource(pdev, 0); + ixp_clock.master_irq = platform_get_irq(pdev, 0); + ixp_clock.slave_irq = platform_get_irq(pdev, 1); + if (IS_ERR(ixp_clock.regs) || + ixp_clock.master_irq < 0 || ixp_clock.slave_irq < 0) + return -ENXIO; ixp_clock.caps = ptp_ixp_caps; @@ -297,32 +281,51 @@ static int __init ptp_ixp_init(void) if (IS_ERR(ixp_clock.ptp_clock)) return PTR_ERR(ixp_clock.ptp_clock); - ixp46x_phc_index = ptp_clock_index(ixp_clock.ptp_clock); + ret = devm_add_action_or_reset(dev, ptp_ixp_unregister_action, + ixp_clock.ptp_clock); + if (ret) { + dev_err(dev, "failed to install clock removal handler\n"); + return ret; + } __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend); __raw_writel(1, &ixp_clock.regs->trgt_lo); __raw_writel(0, &ixp_clock.regs->trgt_hi); __raw_writel(TTIPEND, &ixp_clock.regs->event); - if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) { - pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO); - goto no_master; - } - if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) { - pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO); - goto no_slave; - } + ret = devm_request_irq(dev, ixp_clock.master_irq, isr, + 0, DRIVER, &ixp_clock); + if (ret) + return dev_err_probe(dev, ret, + "request_irq failed for irq %d\n", + ixp_clock.master_irq); + + ret = devm_request_irq(dev, ixp_clock.slave_irq, isr, + 0, DRIVER, &ixp_clock); + if (ret) + return dev_err_probe(dev, ret, + "request_irq failed for irq %d\n", + ixp_clock.slave_irq); return 0; -no_slave: - free_irq(MASTER_IRQ, &ixp_clock); -no_master: - ptp_clock_unregister(ixp_clock.ptp_clock); - return -ENODEV; } -module_init(ptp_ixp_init); -module_exit(ptp_ixp_exit); +static const struct of_device_id ptp_ixp_match[] = { + { + .compatible = "intel,ixp46x-ptp-timer", + }, + { }, +}; + +static struct platform_driver ptp_ixp_driver = { + .driver = { + .name = "ptp-ixp46x", + .of_match_table = ptp_ixp_match, + .suppress_bind_attrs = true, + }, + .probe = ptp_ixp_probe, +}; +module_platform_driver(ptp_ixp_driver); MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); MODULE_DESCRIPTION("PTP clock using the IXP46X timer"); |