From: Andrea Greco <a.gr...@4sigma.it> Add support for com20022I/com20020, io mapped.
Signed-off-by: Andrea Greco <a.gr...@4sigma.it> --- drivers/net/arcnet/Kconfig | 9 +- drivers/net/arcnet/Makefile | 1 + drivers/net/arcnet/com20020-io.c | 315 +++++++++++++++++++++++++++++++++++++++ drivers/net/arcnet/com20020.c | 5 +- 4 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 drivers/net/arcnet/com20020-io.c diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig index afc5898e7a16..f72620dc63ec 100644 --- a/drivers/net/arcnet/Kconfig +++ b/drivers/net/arcnet/Kconfig @@ -3,7 +3,7 @@ # menuconfig ARCNET - depends on NETDEVICES && (ISA || PCI || PCMCIA) + depends on NETDEVICES tristate "ARCnet support" ---help--- If you have a network card of this type, say Y and check out the @@ -129,5 +129,12 @@ config ARCNET_COM20020_CS To compile this driver as a module, choose M here: the module will be called com20020_cs. If unsure, say N. +config ARCNET_COM20020_IO + tristate "Support for COM20020 (IO mapped)" + depends on ARCNET_COM20020 + help + Say Y here if your custom board mount com20020 chipset or friends. + Supported Chipset: com20020, com20022, com20022I-3v3 + If unsure, say N. endif # ARCNET diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile index 53525e8ea130..18da4341f404 100644 --- a/drivers/net/arcnet/Makefile +++ b/drivers/net/arcnet/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_ARCNET_COM20020) += com20020.o obj-$(CONFIG_ARCNET_COM20020_ISA) += com20020-isa.o obj-$(CONFIG_ARCNET_COM20020_PCI) += com20020-pci.o obj-$(CONFIG_ARCNET_COM20020_CS) += com20020_cs.o +obj-$(CONFIG_ARCNET_COM20020_IO) += com20020-io.o diff --git a/drivers/net/arcnet/com20020-io.c b/drivers/net/arcnet/com20020-io.c new file mode 100644 index 000000000000..23c24d4de5a9 --- /dev/null +++ b/drivers/net/arcnet/com20020-io.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Linux ARCnet driver for com 20020. + * + * datasheet: + * http://ww1.microchip.com/downloads/en/DeviceDoc/200223vrevc.pdf + * http://ww1.microchip.com/downloads/en/DeviceDoc/20020.pdf + * + * Supported chip version: + * - com20020 + * - com20022 + * - com20022I-3v3 + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/netdevice.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/sizes.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include "arcdevice.h" +#include "com20020.h" + +/* Reset (5 * xTalFreq), minimal com20020 xTal is 10Mhz */ +#define RESET_DELAY 500 + +static unsigned int io_arc_inb(int addr, int offset) +{ + return ioread8((void *__iomem) addr + offset); +} + +static void io_arc_outb(int value, int addr, int offset) +{ + iowrite8(value, (void *__iomem)addr + offset); +} + +static void io_arc_insb(int addr, int offset, void *buffer, int count) +{ + ioread8_rep((void *__iomem) (addr + offset), buffer, count); +} + +static void io_arc_outsb(int addr, int offset, void *buffer, int count) +{ + iowrite8_rep((void *__iomem) (addr + offset), buffer, count); +} + +enum com20020_xtal_freq { + freq_10Mhz = 10, + freq_20Mhz = 20, +}; + +enum com20020_arcnet_speed { + arc_speed_10M_bps = 10000000, + arc_speed_5M_bps = 5000000, + arc_speed_2M50_bps = 2500000, + arc_speed_1M25_bps = 1250000, + arc_speed_625K_bps = 625000, + arc_speed_312K5_bps = 312500, + arc_speed_156K25_bps = 156250, +}; + +enum com20020_timeout { + arc_timeout_328us = 328000, + arc_timeout_164us = 164000, + arc_timeout_82us = 82000, + arc_timeout_20u5s = 20500, +}; + +static int setup_clock(int *clockp, int *clockm, int xtal, int arcnet_speed) +{ + int pll_factor, req_clock_frq = 20; + + switch (arcnet_speed) { + case arc_speed_10M_bps: + req_clock_frq = 80; + *clockp = 0; + break; + case arc_speed_5M_bps: + req_clock_frq = 40; + *clockp = 0; + break; + case arc_speed_2M50_bps: + *clockp = 0; + break; + case arc_speed_1M25_bps: + *clockp = 1; + break; + case arc_speed_625K_bps: + *clockp = 2; + break; + case arc_speed_312K5_bps: + *clockp = 3; + break; + case arc_speed_156K25_bps: + *clockp = 4; + break; + default: + return -EINVAL; + } + + if (xtal != freq_10Mhz && xtal != freq_20Mhz) + return -EINVAL; + + pll_factor = (unsigned int)req_clock_frq / xtal; + + switch (pll_factor) { + case 1: + *clockm = 0; + break; + case 2: + *clockm = 1; + break; + case 4: + *clockm = 3; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int setup_timeout(int *timeout) +{ + switch (*timeout) { + case arc_timeout_328us: + *timeout = 0; + break; + case arc_timeout_164us: + *timeout = 1; + break; + case arc_timeout_82us: + *timeout = 2; + break; + case arc_timeout_20u5s: + *timeout = 3; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int com20020_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct net_device *dev; + struct arcnet_local *lp; + struct resource res, *iores; + int ret, phy_reset; + u32 timeout, xtal, arc_speed; + int clockp, clockm; + bool backplane = false; + int ioaddr; + + np = pdev->dev.of_node; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + + ret = of_property_read_u32(np, "timeout-ns", &timeout); + if (ret) { + dev_err(&pdev->dev, "timeout is required param"); + return ret; + } + + ret = of_property_read_u32(np, "smsc,xtal-mhz", &xtal); + if (ret) { + dev_err(&pdev->dev, "xtal-mhz is required param"); + return ret; + } + + ret = of_property_read_u32(np, "bus-speed-bps", &arc_speed); + if (ret) { + dev_err(&pdev->dev, "Bus speed is required param"); + return ret; + } + + if (of_property_read_bool(np, "smsc,backplane-enabled")) + backplane = true; + + phy_reset = of_get_named_gpio(np, "reset-gpios", 0); + if (!gpio_is_valid(phy_reset)) { + dev_err(&pdev->dev, "reset gpio not valid"); + return phy_reset; + } + + ret = devm_gpio_request_one(&pdev->dev, phy_reset, GPIOF_OUT_INIT_LOW, + "arcnet-reset"); + if (ret) { + dev_err(&pdev->dev, "failed to get phy reset gpio: %d\n", ret); + return ret; + } + + dev = alloc_arcdev(NULL); + dev->netdev_ops = &com20020_netdev_ops; + lp = netdev_priv(dev); + + lp->card_flags = ARC_CAN_10MBIT; + + /* Peak random address, + * if required user could set a new-one in userspace + */ + get_random_bytes(dev->dev_addr, dev->addr_len); + + if (!devm_request_mem_region(&pdev->dev, res.start, resource_size(&res), + lp->card_name)) + return -EBUSY; + + ioaddr = (int)devm_ioremap(&pdev->dev, iores->start, + resource_size(iores)); + if (!ioaddr) { + dev_err(&pdev->dev, "ioremap fallied\n"); + return -ENOMEM; + } + + gpio_set_value_cansleep(phy_reset, 0); + ndelay(RESET_DELAY); + gpio_set_value_cansleep(phy_reset, 1); + + lp->hw.arc_inb = io_arc_inb; + lp->hw.arc_outb = io_arc_outb; + lp->hw.arc_insb = io_arc_insb; + lp->hw.arc_outsb = io_arc_outsb; + + /* ARCNET controller needs this access to detect bustype */ + lp->hw.arc_outb(0x00, ioaddr, COM20020_REG_W_COMMAND); + lp->hw.arc_inb(ioaddr, COM20020_REG_R_DIAGSTAT); + + dev->base_addr = (unsigned long)ioaddr; + + dev->irq = of_get_named_gpio(np, "interrupts", 0); + if (dev->irq == -EPROBE_DEFER) { + return dev->irq; + } else if (!gpio_is_valid(dev->irq)) { + dev_err(&pdev->dev, "irq-gpios not valid !"); + return -EIO; + } + dev->irq = gpio_to_irq(dev->irq); + + ret = setup_clock(&clockp, &clockm, xtal, arc_speed); + if (ret) { + dev_err(&pdev->dev, + "Impossible use oscillator:%dMhz and arcnet bus speed:%dKbps", + xtal, arc_speed / 1000); + return ret; + } + + ret = setup_timeout(&timeout); + if (ret) { + dev_err(&pdev->dev, "Timeout:%d is not valid value", timeout); + return ret; + } + + lp->backplane = (int)backplane; + lp->timeout = timeout; + lp->clockm = clockm; + lp->clockp = clockp; + lp->hw.owner = THIS_MODULE; + + if (lp->hw.arc_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) { + ret = -EIO; + goto err_release_mem; + } + + if (com20020_check(dev)) { + ret = -EIO; + goto err_release_mem; + } + + ret = com20020_found(dev, IRQF_TRIGGER_FALLING); + if (ret) + goto err_release_mem; + + dev_dbg(&pdev->dev, "probe Done\n"); + return 0; + +err_release_mem: + devm_iounmap(&pdev->dev, (void __iomem *)ioaddr); + devm_release_mem_region(&pdev->dev, res.start, resource_size(&res)); + dev_err(&pdev->dev, "probe failed!\n"); + return ret; +} + +static const struct of_device_id of_com20020_match[] = { + { .compatible = "smsc,com20020", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_com20020_match); + +static struct platform_driver of_com20020_driver = { + .driver = { + .name = "com20020-memory-bus", + .of_match_table = of_com20020_match, + }, + .probe = com20020_probe, +}; + +static int com20020_init(void) +{ + return platform_driver_register(&of_com20020_driver); +} +late_initcall(com20020_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index cbcea7834378..8d979a66d8e9 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -43,7 +43,7 @@ #include "com20020.h" static const char * const clockrates[] = { - "XXXXXXX", "XXXXXXXX", "XXXXXX", "2.5 Mb/s", + "10 Mb/s", "XXXXXXXX", "XXXXXX", "2.5 Mb/s", "1.25Mb/s", "625 Kb/s", "312.5 Kb/s", "156.25 Kb/s", "Reserved", "Reserved", "Reserved" }; @@ -429,7 +429,8 @@ static void com20020_set_mc_list(struct net_device *dev) #if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \ defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \ - defined(CONFIG_ARCNET_COM20020_CS_MODULE) + defined(CONFIG_ARCNET_COM20020_CS_MODULE) || \ + defined(CONFIG_ARCNET_COM20020_IO) EXPORT_SYMBOL(com20020_check); EXPORT_SYMBOL(com20020_found); EXPORT_SYMBOL(com20020_netdev_ops); -- 2.14.4