10ce5ebd2SThomas Bogendoerfer // SPDX-License-Identifier: GPL-2.0 20ce5ebd2SThomas Bogendoerfer /* 30ce5ebd2SThomas Bogendoerfer * SGI IOC3 multifunction device driver 40ce5ebd2SThomas Bogendoerfer * 50ce5ebd2SThomas Bogendoerfer * Copyright (C) 2018, 2019 Thomas Bogendoerfer <tbogendoerfer@suse.de> 60ce5ebd2SThomas Bogendoerfer * 70ce5ebd2SThomas Bogendoerfer * Based on work by: 80ce5ebd2SThomas Bogendoerfer * Stanislaw Skowronek <skylark@unaligned.org> 90ce5ebd2SThomas Bogendoerfer * Joshua Kinard <kumba@gentoo.org> 100ce5ebd2SThomas Bogendoerfer * Brent Casavant <bcasavan@sgi.com> - IOC4 master driver 110ce5ebd2SThomas Bogendoerfer * Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer 120ce5ebd2SThomas Bogendoerfer */ 130ce5ebd2SThomas Bogendoerfer 140ce5ebd2SThomas Bogendoerfer #include <linux/delay.h> 150ce5ebd2SThomas Bogendoerfer #include <linux/errno.h> 160ce5ebd2SThomas Bogendoerfer #include <linux/interrupt.h> 170ce5ebd2SThomas Bogendoerfer #include <linux/mfd/core.h> 180ce5ebd2SThomas Bogendoerfer #include <linux/module.h> 190ce5ebd2SThomas Bogendoerfer #include <linux/pci.h> 200ce5ebd2SThomas Bogendoerfer #include <linux/platform_device.h> 210ce5ebd2SThomas Bogendoerfer #include <linux/platform_data/sgi-w1.h> 220ce5ebd2SThomas Bogendoerfer #include <linux/rtc/ds1685.h> 230ce5ebd2SThomas Bogendoerfer 240ce5ebd2SThomas Bogendoerfer #include <asm/pci/bridge.h> 250ce5ebd2SThomas Bogendoerfer #include <asm/sn/ioc3.h> 260ce5ebd2SThomas Bogendoerfer 270ce5ebd2SThomas Bogendoerfer #define IOC3_IRQ_SERIAL_A 6 280ce5ebd2SThomas Bogendoerfer #define IOC3_IRQ_SERIAL_B 15 290ce5ebd2SThomas Bogendoerfer #define IOC3_IRQ_KBD 22 300ce5ebd2SThomas Bogendoerfer 310ce5ebd2SThomas Bogendoerfer /* Bitmask for selecting which IRQs are level triggered */ 320ce5ebd2SThomas Bogendoerfer #define IOC3_LVL_MASK (BIT(IOC3_IRQ_SERIAL_A) | BIT(IOC3_IRQ_SERIAL_B)) 330ce5ebd2SThomas Bogendoerfer 340ce5ebd2SThomas Bogendoerfer #define M48T35_REG_SIZE 32768 /* size of m48t35 registers */ 350ce5ebd2SThomas Bogendoerfer 360ce5ebd2SThomas Bogendoerfer /* 1.2 us latency timer (40 cycles at 33 MHz) */ 370ce5ebd2SThomas Bogendoerfer #define IOC3_LATENCY 40 380ce5ebd2SThomas Bogendoerfer 390ce5ebd2SThomas Bogendoerfer struct ioc3_priv_data { 400ce5ebd2SThomas Bogendoerfer struct irq_domain *domain; 410ce5ebd2SThomas Bogendoerfer struct ioc3 __iomem *regs; 420ce5ebd2SThomas Bogendoerfer struct pci_dev *pdev; 430ce5ebd2SThomas Bogendoerfer int domain_irq; 440ce5ebd2SThomas Bogendoerfer }; 450ce5ebd2SThomas Bogendoerfer 460ce5ebd2SThomas Bogendoerfer static void ioc3_irq_ack(struct irq_data *d) 470ce5ebd2SThomas Bogendoerfer { 480ce5ebd2SThomas Bogendoerfer struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d); 490ce5ebd2SThomas Bogendoerfer unsigned int hwirq = irqd_to_hwirq(d); 500ce5ebd2SThomas Bogendoerfer 510ce5ebd2SThomas Bogendoerfer writel(BIT(hwirq), &ipd->regs->sio_ir); 520ce5ebd2SThomas Bogendoerfer } 530ce5ebd2SThomas Bogendoerfer 540ce5ebd2SThomas Bogendoerfer static void ioc3_irq_mask(struct irq_data *d) 550ce5ebd2SThomas Bogendoerfer { 560ce5ebd2SThomas Bogendoerfer struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d); 570ce5ebd2SThomas Bogendoerfer unsigned int hwirq = irqd_to_hwirq(d); 580ce5ebd2SThomas Bogendoerfer 590ce5ebd2SThomas Bogendoerfer writel(BIT(hwirq), &ipd->regs->sio_iec); 600ce5ebd2SThomas Bogendoerfer } 610ce5ebd2SThomas Bogendoerfer 620ce5ebd2SThomas Bogendoerfer static void ioc3_irq_unmask(struct irq_data *d) 630ce5ebd2SThomas Bogendoerfer { 640ce5ebd2SThomas Bogendoerfer struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d); 650ce5ebd2SThomas Bogendoerfer unsigned int hwirq = irqd_to_hwirq(d); 660ce5ebd2SThomas Bogendoerfer 670ce5ebd2SThomas Bogendoerfer writel(BIT(hwirq), &ipd->regs->sio_ies); 680ce5ebd2SThomas Bogendoerfer } 690ce5ebd2SThomas Bogendoerfer 700ce5ebd2SThomas Bogendoerfer static struct irq_chip ioc3_irq_chip = { 710ce5ebd2SThomas Bogendoerfer .name = "IOC3", 720ce5ebd2SThomas Bogendoerfer .irq_ack = ioc3_irq_ack, 730ce5ebd2SThomas Bogendoerfer .irq_mask = ioc3_irq_mask, 740ce5ebd2SThomas Bogendoerfer .irq_unmask = ioc3_irq_unmask, 750ce5ebd2SThomas Bogendoerfer }; 760ce5ebd2SThomas Bogendoerfer 770ce5ebd2SThomas Bogendoerfer static int ioc3_irq_domain_map(struct irq_domain *d, unsigned int irq, 780ce5ebd2SThomas Bogendoerfer irq_hw_number_t hwirq) 790ce5ebd2SThomas Bogendoerfer { 800ce5ebd2SThomas Bogendoerfer /* Set level IRQs for every interrupt contained in IOC3_LVL_MASK */ 810ce5ebd2SThomas Bogendoerfer if (BIT(hwirq) & IOC3_LVL_MASK) 820ce5ebd2SThomas Bogendoerfer irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_level_irq); 830ce5ebd2SThomas Bogendoerfer else 840ce5ebd2SThomas Bogendoerfer irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_edge_irq); 850ce5ebd2SThomas Bogendoerfer 860ce5ebd2SThomas Bogendoerfer irq_set_chip_data(irq, d->host_data); 870ce5ebd2SThomas Bogendoerfer return 0; 880ce5ebd2SThomas Bogendoerfer } 890ce5ebd2SThomas Bogendoerfer 900ce5ebd2SThomas Bogendoerfer static void ioc3_irq_domain_unmap(struct irq_domain *d, unsigned int irq) 910ce5ebd2SThomas Bogendoerfer { 920ce5ebd2SThomas Bogendoerfer irq_set_chip_and_handler(irq, NULL, NULL); 930ce5ebd2SThomas Bogendoerfer irq_set_chip_data(irq, NULL); 940ce5ebd2SThomas Bogendoerfer } 950ce5ebd2SThomas Bogendoerfer 960ce5ebd2SThomas Bogendoerfer static const struct irq_domain_ops ioc3_irq_domain_ops = { 970ce5ebd2SThomas Bogendoerfer .map = ioc3_irq_domain_map, 980ce5ebd2SThomas Bogendoerfer .unmap = ioc3_irq_domain_unmap, 990ce5ebd2SThomas Bogendoerfer }; 1000ce5ebd2SThomas Bogendoerfer 1010ce5ebd2SThomas Bogendoerfer static void ioc3_irq_handler(struct irq_desc *desc) 1020ce5ebd2SThomas Bogendoerfer { 1030ce5ebd2SThomas Bogendoerfer struct irq_domain *domain = irq_desc_get_handler_data(desc); 1040ce5ebd2SThomas Bogendoerfer struct ioc3_priv_data *ipd = domain->host_data; 1050ce5ebd2SThomas Bogendoerfer struct ioc3 __iomem *regs = ipd->regs; 1060ce5ebd2SThomas Bogendoerfer u32 pending, mask; 1070ce5ebd2SThomas Bogendoerfer unsigned int irq; 1080ce5ebd2SThomas Bogendoerfer 1090ce5ebd2SThomas Bogendoerfer pending = readl(®s->sio_ir); 1100ce5ebd2SThomas Bogendoerfer mask = readl(®s->sio_ies); 1110ce5ebd2SThomas Bogendoerfer pending &= mask; /* Mask off not enabled interrupts */ 1120ce5ebd2SThomas Bogendoerfer 1130ce5ebd2SThomas Bogendoerfer if (pending) { 1140ce5ebd2SThomas Bogendoerfer irq = irq_find_mapping(domain, __ffs(pending)); 1150ce5ebd2SThomas Bogendoerfer if (irq) 1160ce5ebd2SThomas Bogendoerfer generic_handle_irq(irq); 1170ce5ebd2SThomas Bogendoerfer } else { 1180ce5ebd2SThomas Bogendoerfer spurious_interrupt(); 1190ce5ebd2SThomas Bogendoerfer } 1200ce5ebd2SThomas Bogendoerfer } 1210ce5ebd2SThomas Bogendoerfer 1220ce5ebd2SThomas Bogendoerfer /* 1230ce5ebd2SThomas Bogendoerfer * System boards/BaseIOs use more interrupt pins of the bridge ASIC 1240ce5ebd2SThomas Bogendoerfer * to which the IOC3 is connected. Since the IOC3 MFD driver 1250ce5ebd2SThomas Bogendoerfer * knows wiring of these extra pins, we use the map_irq function 1260ce5ebd2SThomas Bogendoerfer * to get interrupts activated 1270ce5ebd2SThomas Bogendoerfer */ 1280ce5ebd2SThomas Bogendoerfer static int ioc3_map_irq(struct pci_dev *pdev, int slot, int pin) 1290ce5ebd2SThomas Bogendoerfer { 1300ce5ebd2SThomas Bogendoerfer struct pci_host_bridge *hbrg = pci_find_host_bridge(pdev->bus); 1310ce5ebd2SThomas Bogendoerfer 1320ce5ebd2SThomas Bogendoerfer return hbrg->map_irq(pdev, slot, pin); 1330ce5ebd2SThomas Bogendoerfer } 1340ce5ebd2SThomas Bogendoerfer 1350ce5ebd2SThomas Bogendoerfer static int ioc3_irq_domain_setup(struct ioc3_priv_data *ipd, int irq) 1360ce5ebd2SThomas Bogendoerfer { 1370ce5ebd2SThomas Bogendoerfer struct irq_domain *domain; 1380ce5ebd2SThomas Bogendoerfer struct fwnode_handle *fn; 1390ce5ebd2SThomas Bogendoerfer 1400ce5ebd2SThomas Bogendoerfer fn = irq_domain_alloc_named_fwnode("IOC3"); 1410ce5ebd2SThomas Bogendoerfer if (!fn) 1420ce5ebd2SThomas Bogendoerfer goto err; 1430ce5ebd2SThomas Bogendoerfer 1440ce5ebd2SThomas Bogendoerfer domain = irq_domain_create_linear(fn, 24, &ioc3_irq_domain_ops, ipd); 1450ce5ebd2SThomas Bogendoerfer if (!domain) 1460ce5ebd2SThomas Bogendoerfer goto err; 1470ce5ebd2SThomas Bogendoerfer 1480ce5ebd2SThomas Bogendoerfer irq_domain_free_fwnode(fn); 1490ce5ebd2SThomas Bogendoerfer ipd->domain = domain; 1500ce5ebd2SThomas Bogendoerfer 1510ce5ebd2SThomas Bogendoerfer irq_set_chained_handler_and_data(irq, ioc3_irq_handler, domain); 1520ce5ebd2SThomas Bogendoerfer ipd->domain_irq = irq; 1530ce5ebd2SThomas Bogendoerfer return 0; 1540ce5ebd2SThomas Bogendoerfer 1550ce5ebd2SThomas Bogendoerfer err: 1560ce5ebd2SThomas Bogendoerfer dev_err(&ipd->pdev->dev, "irq domain setup failed\n"); 1570ce5ebd2SThomas Bogendoerfer return -ENOMEM; 1580ce5ebd2SThomas Bogendoerfer } 1590ce5ebd2SThomas Bogendoerfer 1600ce5ebd2SThomas Bogendoerfer static struct resource ioc3_uarta_resources[] = { 1610ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uarta), 1620ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, sregs.uarta)), 1630ce5ebd2SThomas Bogendoerfer DEFINE_RES_IRQ(IOC3_IRQ_SERIAL_A) 1640ce5ebd2SThomas Bogendoerfer }; 1650ce5ebd2SThomas Bogendoerfer 1660ce5ebd2SThomas Bogendoerfer static struct resource ioc3_uartb_resources[] = { 1670ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uartb), 1680ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, sregs.uartb)), 1690ce5ebd2SThomas Bogendoerfer DEFINE_RES_IRQ(IOC3_IRQ_SERIAL_B) 1700ce5ebd2SThomas Bogendoerfer }; 1710ce5ebd2SThomas Bogendoerfer 1720ce5ebd2SThomas Bogendoerfer static struct mfd_cell ioc3_serial_cells[] = { 1730ce5ebd2SThomas Bogendoerfer { 1740ce5ebd2SThomas Bogendoerfer .name = "ioc3-serial8250", 1750ce5ebd2SThomas Bogendoerfer .resources = ioc3_uarta_resources, 1760ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_uarta_resources), 1770ce5ebd2SThomas Bogendoerfer }, 1780ce5ebd2SThomas Bogendoerfer { 1790ce5ebd2SThomas Bogendoerfer .name = "ioc3-serial8250", 1800ce5ebd2SThomas Bogendoerfer .resources = ioc3_uartb_resources, 1810ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_uartb_resources), 1820ce5ebd2SThomas Bogendoerfer } 1830ce5ebd2SThomas Bogendoerfer }; 1840ce5ebd2SThomas Bogendoerfer 1850ce5ebd2SThomas Bogendoerfer static int ioc3_serial_setup(struct ioc3_priv_data *ipd) 1860ce5ebd2SThomas Bogendoerfer { 1870ce5ebd2SThomas Bogendoerfer int ret; 1880ce5ebd2SThomas Bogendoerfer 1890ce5ebd2SThomas Bogendoerfer /* Set gpio pins for RS232/RS422 mode selection */ 1900ce5ebd2SThomas Bogendoerfer writel(GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL, 1910ce5ebd2SThomas Bogendoerfer &ipd->regs->gpcr_s); 1920ce5ebd2SThomas Bogendoerfer /* Select RS232 mode for uart a */ 1930ce5ebd2SThomas Bogendoerfer writel(0, &ipd->regs->gppr[6]); 1940ce5ebd2SThomas Bogendoerfer /* Select RS232 mode for uart b */ 1950ce5ebd2SThomas Bogendoerfer writel(0, &ipd->regs->gppr[7]); 1960ce5ebd2SThomas Bogendoerfer 1970ce5ebd2SThomas Bogendoerfer /* Switch both ports to 16650 mode */ 1980ce5ebd2SThomas Bogendoerfer writel(readl(&ipd->regs->port_a.sscr) & ~SSCR_DMA_EN, 1990ce5ebd2SThomas Bogendoerfer &ipd->regs->port_a.sscr); 2000ce5ebd2SThomas Bogendoerfer writel(readl(&ipd->regs->port_b.sscr) & ~SSCR_DMA_EN, 2010ce5ebd2SThomas Bogendoerfer &ipd->regs->port_b.sscr); 2020ce5ebd2SThomas Bogendoerfer udelay(1000); /* Wait until mode switch is done */ 2030ce5ebd2SThomas Bogendoerfer 2040ce5ebd2SThomas Bogendoerfer ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 2050ce5ebd2SThomas Bogendoerfer ioc3_serial_cells, ARRAY_SIZE(ioc3_serial_cells), 2060ce5ebd2SThomas Bogendoerfer &ipd->pdev->resource[0], 0, ipd->domain); 2070ce5ebd2SThomas Bogendoerfer if (ret) { 2080ce5ebd2SThomas Bogendoerfer dev_err(&ipd->pdev->dev, "Failed to add 16550 subdevs\n"); 2090ce5ebd2SThomas Bogendoerfer return ret; 2100ce5ebd2SThomas Bogendoerfer } 2110ce5ebd2SThomas Bogendoerfer 2120ce5ebd2SThomas Bogendoerfer return 0; 2130ce5ebd2SThomas Bogendoerfer } 2140ce5ebd2SThomas Bogendoerfer 2150ce5ebd2SThomas Bogendoerfer static struct resource ioc3_kbd_resources[] = { 2160ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, serio), 2170ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, serio)), 2180ce5ebd2SThomas Bogendoerfer DEFINE_RES_IRQ(IOC3_IRQ_KBD) 2190ce5ebd2SThomas Bogendoerfer }; 2200ce5ebd2SThomas Bogendoerfer 2210ce5ebd2SThomas Bogendoerfer static struct mfd_cell ioc3_kbd_cells[] = { 2220ce5ebd2SThomas Bogendoerfer { 2230ce5ebd2SThomas Bogendoerfer .name = "ioc3-kbd", 2240ce5ebd2SThomas Bogendoerfer .resources = ioc3_kbd_resources, 2250ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_kbd_resources), 2260ce5ebd2SThomas Bogendoerfer } 2270ce5ebd2SThomas Bogendoerfer }; 2280ce5ebd2SThomas Bogendoerfer 2290ce5ebd2SThomas Bogendoerfer static int ioc3_kbd_setup(struct ioc3_priv_data *ipd) 2300ce5ebd2SThomas Bogendoerfer { 2310ce5ebd2SThomas Bogendoerfer int ret; 2320ce5ebd2SThomas Bogendoerfer 2330ce5ebd2SThomas Bogendoerfer ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 2340ce5ebd2SThomas Bogendoerfer ioc3_kbd_cells, ARRAY_SIZE(ioc3_kbd_cells), 2350ce5ebd2SThomas Bogendoerfer &ipd->pdev->resource[0], 0, ipd->domain); 2360ce5ebd2SThomas Bogendoerfer if (ret) { 2370ce5ebd2SThomas Bogendoerfer dev_err(&ipd->pdev->dev, "Failed to add 16550 subdevs\n"); 2380ce5ebd2SThomas Bogendoerfer return ret; 2390ce5ebd2SThomas Bogendoerfer } 2400ce5ebd2SThomas Bogendoerfer 2410ce5ebd2SThomas Bogendoerfer return 0; 2420ce5ebd2SThomas Bogendoerfer } 2430ce5ebd2SThomas Bogendoerfer 2440ce5ebd2SThomas Bogendoerfer static struct resource ioc3_eth_resources[] = { 2450ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, eth), 2460ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, eth)), 2470ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, ssram), 2480ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, ssram)), 2490ce5ebd2SThomas Bogendoerfer DEFINE_RES_IRQ(0) 2500ce5ebd2SThomas Bogendoerfer }; 2510ce5ebd2SThomas Bogendoerfer 2520ce5ebd2SThomas Bogendoerfer static struct resource ioc3_w1_resources[] = { 2530ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, mcr), 2540ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, mcr)), 2550ce5ebd2SThomas Bogendoerfer }; 2560ce5ebd2SThomas Bogendoerfer static struct sgi_w1_platform_data ioc3_w1_platform_data; 2570ce5ebd2SThomas Bogendoerfer 2580ce5ebd2SThomas Bogendoerfer static struct mfd_cell ioc3_eth_cells[] = { 2590ce5ebd2SThomas Bogendoerfer { 2600ce5ebd2SThomas Bogendoerfer .name = "ioc3-eth", 2610ce5ebd2SThomas Bogendoerfer .resources = ioc3_eth_resources, 2620ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_eth_resources), 2630ce5ebd2SThomas Bogendoerfer }, 2640ce5ebd2SThomas Bogendoerfer { 2650ce5ebd2SThomas Bogendoerfer .name = "sgi_w1", 2660ce5ebd2SThomas Bogendoerfer .resources = ioc3_w1_resources, 2670ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_w1_resources), 2680ce5ebd2SThomas Bogendoerfer .platform_data = &ioc3_w1_platform_data, 2690ce5ebd2SThomas Bogendoerfer .pdata_size = sizeof(ioc3_w1_platform_data), 2700ce5ebd2SThomas Bogendoerfer } 2710ce5ebd2SThomas Bogendoerfer }; 2720ce5ebd2SThomas Bogendoerfer 2730ce5ebd2SThomas Bogendoerfer static int ioc3_eth_setup(struct ioc3_priv_data *ipd) 2740ce5ebd2SThomas Bogendoerfer { 2750ce5ebd2SThomas Bogendoerfer int ret; 2760ce5ebd2SThomas Bogendoerfer 2770ce5ebd2SThomas Bogendoerfer /* Enable One-Wire bus */ 2780ce5ebd2SThomas Bogendoerfer writel(GPCR_MLAN_EN, &ipd->regs->gpcr_s); 2790ce5ebd2SThomas Bogendoerfer 2800ce5ebd2SThomas Bogendoerfer /* Generate unique identifier */ 2810ce5ebd2SThomas Bogendoerfer snprintf(ioc3_w1_platform_data.dev_id, 2820ce5ebd2SThomas Bogendoerfer sizeof(ioc3_w1_platform_data.dev_id), "ioc3-%012llx", 2830ce5ebd2SThomas Bogendoerfer ipd->pdev->resource->start); 2840ce5ebd2SThomas Bogendoerfer 2850ce5ebd2SThomas Bogendoerfer ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 2860ce5ebd2SThomas Bogendoerfer ioc3_eth_cells, ARRAY_SIZE(ioc3_eth_cells), 2870ce5ebd2SThomas Bogendoerfer &ipd->pdev->resource[0], ipd->pdev->irq, NULL); 2880ce5ebd2SThomas Bogendoerfer if (ret) { 2890ce5ebd2SThomas Bogendoerfer dev_err(&ipd->pdev->dev, "Failed to add ETH/W1 subdev\n"); 2900ce5ebd2SThomas Bogendoerfer return ret; 2910ce5ebd2SThomas Bogendoerfer } 2920ce5ebd2SThomas Bogendoerfer 2930ce5ebd2SThomas Bogendoerfer return 0; 2940ce5ebd2SThomas Bogendoerfer } 2950ce5ebd2SThomas Bogendoerfer 2960ce5ebd2SThomas Bogendoerfer static struct resource ioc3_m48t35_resources[] = { 2970ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(IOC3_BYTEBUS_DEV0, M48T35_REG_SIZE) 2980ce5ebd2SThomas Bogendoerfer }; 2990ce5ebd2SThomas Bogendoerfer 3000ce5ebd2SThomas Bogendoerfer static struct mfd_cell ioc3_m48t35_cells[] = { 3010ce5ebd2SThomas Bogendoerfer { 3020ce5ebd2SThomas Bogendoerfer .name = "rtc-m48t35", 3030ce5ebd2SThomas Bogendoerfer .resources = ioc3_m48t35_resources, 3040ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_m48t35_resources), 3050ce5ebd2SThomas Bogendoerfer } 3060ce5ebd2SThomas Bogendoerfer }; 3070ce5ebd2SThomas Bogendoerfer 3080ce5ebd2SThomas Bogendoerfer static int ioc3_m48t35_setup(struct ioc3_priv_data *ipd) 3090ce5ebd2SThomas Bogendoerfer { 3100ce5ebd2SThomas Bogendoerfer int ret; 3110ce5ebd2SThomas Bogendoerfer 3120ce5ebd2SThomas Bogendoerfer ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO, 3130ce5ebd2SThomas Bogendoerfer ioc3_m48t35_cells, ARRAY_SIZE(ioc3_m48t35_cells), 3140ce5ebd2SThomas Bogendoerfer &ipd->pdev->resource[0], 0, ipd->domain); 3150ce5ebd2SThomas Bogendoerfer if (ret) 3160ce5ebd2SThomas Bogendoerfer dev_err(&ipd->pdev->dev, "Failed to add M48T35 subdev\n"); 3170ce5ebd2SThomas Bogendoerfer 3180ce5ebd2SThomas Bogendoerfer return ret; 3190ce5ebd2SThomas Bogendoerfer } 3200ce5ebd2SThomas Bogendoerfer 3210ce5ebd2SThomas Bogendoerfer static struct ds1685_rtc_platform_data ip30_rtc_platform_data = { 3220ce5ebd2SThomas Bogendoerfer .bcd_mode = false, 3230ce5ebd2SThomas Bogendoerfer .no_irq = false, 3240ce5ebd2SThomas Bogendoerfer .uie_unsupported = true, 3250ce5ebd2SThomas Bogendoerfer .access_type = ds1685_reg_indirect, 3260ce5ebd2SThomas Bogendoerfer }; 3270ce5ebd2SThomas Bogendoerfer 3280ce5ebd2SThomas Bogendoerfer static struct resource ioc3_rtc_ds1685_resources[] = { 3290ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(IOC3_BYTEBUS_DEV1, 1), 3300ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(IOC3_BYTEBUS_DEV2, 1), 3310ce5ebd2SThomas Bogendoerfer DEFINE_RES_IRQ(0) 3320ce5ebd2SThomas Bogendoerfer }; 3330ce5ebd2SThomas Bogendoerfer 3340ce5ebd2SThomas Bogendoerfer static struct mfd_cell ioc3_ds1685_cells[] = { 3350ce5ebd2SThomas Bogendoerfer { 3360ce5ebd2SThomas Bogendoerfer .name = "rtc-ds1685", 3370ce5ebd2SThomas Bogendoerfer .resources = ioc3_rtc_ds1685_resources, 3380ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_rtc_ds1685_resources), 3390ce5ebd2SThomas Bogendoerfer .platform_data = &ip30_rtc_platform_data, 3400ce5ebd2SThomas Bogendoerfer .pdata_size = sizeof(ip30_rtc_platform_data), 3410ce5ebd2SThomas Bogendoerfer .id = PLATFORM_DEVID_NONE, 3420ce5ebd2SThomas Bogendoerfer } 3430ce5ebd2SThomas Bogendoerfer }; 3440ce5ebd2SThomas Bogendoerfer 3450ce5ebd2SThomas Bogendoerfer static int ioc3_ds1685_setup(struct ioc3_priv_data *ipd) 3460ce5ebd2SThomas Bogendoerfer { 3470ce5ebd2SThomas Bogendoerfer int ret, irq; 3480ce5ebd2SThomas Bogendoerfer 3490ce5ebd2SThomas Bogendoerfer irq = ioc3_map_irq(ipd->pdev, 6, 0); 3500ce5ebd2SThomas Bogendoerfer 3510ce5ebd2SThomas Bogendoerfer ret = mfd_add_devices(&ipd->pdev->dev, 0, ioc3_ds1685_cells, 3520ce5ebd2SThomas Bogendoerfer ARRAY_SIZE(ioc3_ds1685_cells), 3530ce5ebd2SThomas Bogendoerfer &ipd->pdev->resource[0], irq, NULL); 3540ce5ebd2SThomas Bogendoerfer if (ret) 3550ce5ebd2SThomas Bogendoerfer dev_err(&ipd->pdev->dev, "Failed to add DS1685 subdev\n"); 3560ce5ebd2SThomas Bogendoerfer 3570ce5ebd2SThomas Bogendoerfer return ret; 3580ce5ebd2SThomas Bogendoerfer }; 3590ce5ebd2SThomas Bogendoerfer 3600ce5ebd2SThomas Bogendoerfer 3610ce5ebd2SThomas Bogendoerfer static struct resource ioc3_leds_resources[] = { 3620ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, gppr[0]), 3630ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, gppr[0])), 3640ce5ebd2SThomas Bogendoerfer DEFINE_RES_MEM(offsetof(struct ioc3, gppr[1]), 3650ce5ebd2SThomas Bogendoerfer sizeof_field(struct ioc3, gppr[1])), 3660ce5ebd2SThomas Bogendoerfer }; 3670ce5ebd2SThomas Bogendoerfer 3680ce5ebd2SThomas Bogendoerfer static struct mfd_cell ioc3_led_cells[] = { 3690ce5ebd2SThomas Bogendoerfer { 3700ce5ebd2SThomas Bogendoerfer .name = "ip30-leds", 3710ce5ebd2SThomas Bogendoerfer .resources = ioc3_leds_resources, 3720ce5ebd2SThomas Bogendoerfer .num_resources = ARRAY_SIZE(ioc3_leds_resources), 3730ce5ebd2SThomas Bogendoerfer .id = PLATFORM_DEVID_NONE, 3740ce5ebd2SThomas Bogendoerfer } 3750ce5ebd2SThomas Bogendoerfer }; 3760ce5ebd2SThomas Bogendoerfer 3770ce5ebd2SThomas Bogendoerfer static int ioc3_led_setup(struct ioc3_priv_data *ipd) 3780ce5ebd2SThomas Bogendoerfer { 3790ce5ebd2SThomas Bogendoerfer int ret; 3800ce5ebd2SThomas Bogendoerfer 3810ce5ebd2SThomas Bogendoerfer ret = mfd_add_devices(&ipd->pdev->dev, 0, ioc3_led_cells, 3820ce5ebd2SThomas Bogendoerfer ARRAY_SIZE(ioc3_led_cells), 3830ce5ebd2SThomas Bogendoerfer &ipd->pdev->resource[0], 0, ipd->domain); 3840ce5ebd2SThomas Bogendoerfer if (ret) 3850ce5ebd2SThomas Bogendoerfer dev_err(&ipd->pdev->dev, "Failed to add LED subdev\n"); 3860ce5ebd2SThomas Bogendoerfer 3870ce5ebd2SThomas Bogendoerfer return ret; 3880ce5ebd2SThomas Bogendoerfer } 3890ce5ebd2SThomas Bogendoerfer 3900ce5ebd2SThomas Bogendoerfer static int ip27_baseio_setup(struct ioc3_priv_data *ipd) 3910ce5ebd2SThomas Bogendoerfer { 3920ce5ebd2SThomas Bogendoerfer int ret, io_irq; 3930ce5ebd2SThomas Bogendoerfer 3940ce5ebd2SThomas Bogendoerfer io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 3950ce5ebd2SThomas Bogendoerfer PCI_INTERRUPT_INTB); 3960ce5ebd2SThomas Bogendoerfer ret = ioc3_irq_domain_setup(ipd, io_irq); 3970ce5ebd2SThomas Bogendoerfer if (ret) 3980ce5ebd2SThomas Bogendoerfer return ret; 3990ce5ebd2SThomas Bogendoerfer 4000ce5ebd2SThomas Bogendoerfer ret = ioc3_eth_setup(ipd); 4010ce5ebd2SThomas Bogendoerfer if (ret) 4020ce5ebd2SThomas Bogendoerfer return ret; 4030ce5ebd2SThomas Bogendoerfer 4040ce5ebd2SThomas Bogendoerfer ret = ioc3_serial_setup(ipd); 4050ce5ebd2SThomas Bogendoerfer if (ret) 4060ce5ebd2SThomas Bogendoerfer return ret; 4070ce5ebd2SThomas Bogendoerfer 4080ce5ebd2SThomas Bogendoerfer return ioc3_m48t35_setup(ipd); 4090ce5ebd2SThomas Bogendoerfer } 4100ce5ebd2SThomas Bogendoerfer 4110ce5ebd2SThomas Bogendoerfer static int ip27_baseio6g_setup(struct ioc3_priv_data *ipd) 4120ce5ebd2SThomas Bogendoerfer { 4130ce5ebd2SThomas Bogendoerfer int ret, io_irq; 4140ce5ebd2SThomas Bogendoerfer 4150ce5ebd2SThomas Bogendoerfer io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 4160ce5ebd2SThomas Bogendoerfer PCI_INTERRUPT_INTB); 4170ce5ebd2SThomas Bogendoerfer ret = ioc3_irq_domain_setup(ipd, io_irq); 4180ce5ebd2SThomas Bogendoerfer if (ret) 4190ce5ebd2SThomas Bogendoerfer return ret; 4200ce5ebd2SThomas Bogendoerfer 4210ce5ebd2SThomas Bogendoerfer ret = ioc3_eth_setup(ipd); 4220ce5ebd2SThomas Bogendoerfer if (ret) 4230ce5ebd2SThomas Bogendoerfer return ret; 4240ce5ebd2SThomas Bogendoerfer 4250ce5ebd2SThomas Bogendoerfer ret = ioc3_serial_setup(ipd); 4260ce5ebd2SThomas Bogendoerfer if (ret) 4270ce5ebd2SThomas Bogendoerfer return ret; 4280ce5ebd2SThomas Bogendoerfer 4290ce5ebd2SThomas Bogendoerfer ret = ioc3_m48t35_setup(ipd); 4300ce5ebd2SThomas Bogendoerfer if (ret) 4310ce5ebd2SThomas Bogendoerfer return ret; 4320ce5ebd2SThomas Bogendoerfer 4330ce5ebd2SThomas Bogendoerfer return ioc3_kbd_setup(ipd); 4340ce5ebd2SThomas Bogendoerfer } 4350ce5ebd2SThomas Bogendoerfer 4360ce5ebd2SThomas Bogendoerfer static int ip27_mio_setup(struct ioc3_priv_data *ipd) 4370ce5ebd2SThomas Bogendoerfer { 4380ce5ebd2SThomas Bogendoerfer int ret; 4390ce5ebd2SThomas Bogendoerfer 4400ce5ebd2SThomas Bogendoerfer ret = ioc3_irq_domain_setup(ipd, ipd->pdev->irq); 4410ce5ebd2SThomas Bogendoerfer if (ret) 4420ce5ebd2SThomas Bogendoerfer return ret; 4430ce5ebd2SThomas Bogendoerfer 4440ce5ebd2SThomas Bogendoerfer ret = ioc3_serial_setup(ipd); 4450ce5ebd2SThomas Bogendoerfer if (ret) 4460ce5ebd2SThomas Bogendoerfer return ret; 4470ce5ebd2SThomas Bogendoerfer 4480ce5ebd2SThomas Bogendoerfer return ioc3_kbd_setup(ipd); 4490ce5ebd2SThomas Bogendoerfer } 4500ce5ebd2SThomas Bogendoerfer 4510ce5ebd2SThomas Bogendoerfer static int ip30_sysboard_setup(struct ioc3_priv_data *ipd) 4520ce5ebd2SThomas Bogendoerfer { 4530ce5ebd2SThomas Bogendoerfer int ret, io_irq; 4540ce5ebd2SThomas Bogendoerfer 4550ce5ebd2SThomas Bogendoerfer io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 4560ce5ebd2SThomas Bogendoerfer PCI_INTERRUPT_INTB); 4570ce5ebd2SThomas Bogendoerfer ret = ioc3_irq_domain_setup(ipd, io_irq); 4580ce5ebd2SThomas Bogendoerfer if (ret) 4590ce5ebd2SThomas Bogendoerfer return ret; 4600ce5ebd2SThomas Bogendoerfer 4610ce5ebd2SThomas Bogendoerfer ret = ioc3_eth_setup(ipd); 4620ce5ebd2SThomas Bogendoerfer if (ret) 4630ce5ebd2SThomas Bogendoerfer return ret; 4640ce5ebd2SThomas Bogendoerfer 4650ce5ebd2SThomas Bogendoerfer ret = ioc3_serial_setup(ipd); 4660ce5ebd2SThomas Bogendoerfer if (ret) 4670ce5ebd2SThomas Bogendoerfer return ret; 4680ce5ebd2SThomas Bogendoerfer 4690ce5ebd2SThomas Bogendoerfer ret = ioc3_kbd_setup(ipd); 4700ce5ebd2SThomas Bogendoerfer if (ret) 4710ce5ebd2SThomas Bogendoerfer return ret; 4720ce5ebd2SThomas Bogendoerfer 4730ce5ebd2SThomas Bogendoerfer ret = ioc3_ds1685_setup(ipd); 4740ce5ebd2SThomas Bogendoerfer if (ret) 4750ce5ebd2SThomas Bogendoerfer return ret; 4760ce5ebd2SThomas Bogendoerfer 4770ce5ebd2SThomas Bogendoerfer return ioc3_led_setup(ipd); 4780ce5ebd2SThomas Bogendoerfer } 4790ce5ebd2SThomas Bogendoerfer 4800ce5ebd2SThomas Bogendoerfer static int ioc3_menet_setup(struct ioc3_priv_data *ipd) 4810ce5ebd2SThomas Bogendoerfer { 4820ce5ebd2SThomas Bogendoerfer int ret, io_irq; 4830ce5ebd2SThomas Bogendoerfer 4840ce5ebd2SThomas Bogendoerfer io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 4850ce5ebd2SThomas Bogendoerfer PCI_INTERRUPT_INTB); 4860ce5ebd2SThomas Bogendoerfer ret = ioc3_irq_domain_setup(ipd, io_irq); 4870ce5ebd2SThomas Bogendoerfer if (ret) 4880ce5ebd2SThomas Bogendoerfer return ret; 4890ce5ebd2SThomas Bogendoerfer 4900ce5ebd2SThomas Bogendoerfer ret = ioc3_eth_setup(ipd); 4910ce5ebd2SThomas Bogendoerfer if (ret) 4920ce5ebd2SThomas Bogendoerfer return ret; 4930ce5ebd2SThomas Bogendoerfer 4940ce5ebd2SThomas Bogendoerfer return ioc3_serial_setup(ipd); 4950ce5ebd2SThomas Bogendoerfer } 4960ce5ebd2SThomas Bogendoerfer 4970ce5ebd2SThomas Bogendoerfer static int ioc3_menet4_setup(struct ioc3_priv_data *ipd) 4980ce5ebd2SThomas Bogendoerfer { 4990ce5ebd2SThomas Bogendoerfer return ioc3_eth_setup(ipd); 5000ce5ebd2SThomas Bogendoerfer } 5010ce5ebd2SThomas Bogendoerfer 5020ce5ebd2SThomas Bogendoerfer static int ioc3_cad_duo_setup(struct ioc3_priv_data *ipd) 5030ce5ebd2SThomas Bogendoerfer { 5040ce5ebd2SThomas Bogendoerfer int ret, io_irq; 5050ce5ebd2SThomas Bogendoerfer 5060ce5ebd2SThomas Bogendoerfer io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn), 5070ce5ebd2SThomas Bogendoerfer PCI_INTERRUPT_INTB); 5080ce5ebd2SThomas Bogendoerfer ret = ioc3_irq_domain_setup(ipd, io_irq); 5090ce5ebd2SThomas Bogendoerfer if (ret) 5100ce5ebd2SThomas Bogendoerfer return ret; 5110ce5ebd2SThomas Bogendoerfer 5120ce5ebd2SThomas Bogendoerfer ret = ioc3_eth_setup(ipd); 5130ce5ebd2SThomas Bogendoerfer if (ret) 5140ce5ebd2SThomas Bogendoerfer return ret; 5150ce5ebd2SThomas Bogendoerfer 5160ce5ebd2SThomas Bogendoerfer return ioc3_kbd_setup(ipd); 5170ce5ebd2SThomas Bogendoerfer } 5180ce5ebd2SThomas Bogendoerfer 5190ce5ebd2SThomas Bogendoerfer /* Helper macro for filling ioc3_info array */ 5200ce5ebd2SThomas Bogendoerfer #define IOC3_SID(_name, _sid, _setup) \ 5210ce5ebd2SThomas Bogendoerfer { \ 5220ce5ebd2SThomas Bogendoerfer .name = _name, \ 5230ce5ebd2SThomas Bogendoerfer .sid = PCI_VENDOR_ID_SGI | (IOC3_SUBSYS_ ## _sid << 16), \ 5240ce5ebd2SThomas Bogendoerfer .setup = _setup, \ 5250ce5ebd2SThomas Bogendoerfer } 5260ce5ebd2SThomas Bogendoerfer 5270ce5ebd2SThomas Bogendoerfer static struct { 5280ce5ebd2SThomas Bogendoerfer const char *name; 5290ce5ebd2SThomas Bogendoerfer u32 sid; 5300ce5ebd2SThomas Bogendoerfer int (*setup)(struct ioc3_priv_data *ipd); 5310ce5ebd2SThomas Bogendoerfer } ioc3_infos[] = { 5320ce5ebd2SThomas Bogendoerfer IOC3_SID("IP27 BaseIO6G", IP27_BASEIO6G, &ip27_baseio6g_setup), 5330ce5ebd2SThomas Bogendoerfer IOC3_SID("IP27 MIO", IP27_MIO, &ip27_mio_setup), 5340ce5ebd2SThomas Bogendoerfer IOC3_SID("IP27 BaseIO", IP27_BASEIO, &ip27_baseio_setup), 5350ce5ebd2SThomas Bogendoerfer IOC3_SID("IP29 System Board", IP29_SYSBOARD, &ip27_baseio6g_setup), 5360ce5ebd2SThomas Bogendoerfer IOC3_SID("IP30 System Board", IP30_SYSBOARD, &ip30_sysboard_setup), 5370ce5ebd2SThomas Bogendoerfer IOC3_SID("MENET", MENET, &ioc3_menet_setup), 5380ce5ebd2SThomas Bogendoerfer IOC3_SID("MENET4", MENET4, &ioc3_menet4_setup) 5390ce5ebd2SThomas Bogendoerfer }; 5400ce5ebd2SThomas Bogendoerfer #undef IOC3_SID 5410ce5ebd2SThomas Bogendoerfer 5420ce5ebd2SThomas Bogendoerfer static int ioc3_setup(struct ioc3_priv_data *ipd) 5430ce5ebd2SThomas Bogendoerfer { 5440ce5ebd2SThomas Bogendoerfer u32 sid; 5450ce5ebd2SThomas Bogendoerfer int i; 5460ce5ebd2SThomas Bogendoerfer 5470ce5ebd2SThomas Bogendoerfer /* Clear IRQs */ 5480ce5ebd2SThomas Bogendoerfer writel(~0, &ipd->regs->sio_iec); 5490ce5ebd2SThomas Bogendoerfer writel(~0, &ipd->regs->sio_ir); 5500ce5ebd2SThomas Bogendoerfer writel(0, &ipd->regs->eth.eier); 5510ce5ebd2SThomas Bogendoerfer writel(~0, &ipd->regs->eth.eisr); 5520ce5ebd2SThomas Bogendoerfer 5530ce5ebd2SThomas Bogendoerfer /* Read subsystem vendor id and subsystem id */ 5540ce5ebd2SThomas Bogendoerfer pci_read_config_dword(ipd->pdev, PCI_SUBSYSTEM_VENDOR_ID, &sid); 5550ce5ebd2SThomas Bogendoerfer 5560ce5ebd2SThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(ioc3_infos); i++) 5570ce5ebd2SThomas Bogendoerfer if (sid == ioc3_infos[i].sid) { 5580ce5ebd2SThomas Bogendoerfer pr_info("ioc3: %s\n", ioc3_infos[i].name); 5590ce5ebd2SThomas Bogendoerfer return ioc3_infos[i].setup(ipd); 5600ce5ebd2SThomas Bogendoerfer } 5610ce5ebd2SThomas Bogendoerfer 5620ce5ebd2SThomas Bogendoerfer /* Treat everything not identified by PCI subid as CAD DUO */ 5630ce5ebd2SThomas Bogendoerfer pr_info("ioc3: CAD DUO\n"); 5640ce5ebd2SThomas Bogendoerfer return ioc3_cad_duo_setup(ipd); 5650ce5ebd2SThomas Bogendoerfer } 5660ce5ebd2SThomas Bogendoerfer 5670ce5ebd2SThomas Bogendoerfer static int ioc3_mfd_probe(struct pci_dev *pdev, 5680ce5ebd2SThomas Bogendoerfer const struct pci_device_id *pci_id) 5690ce5ebd2SThomas Bogendoerfer { 5700ce5ebd2SThomas Bogendoerfer struct ioc3_priv_data *ipd; 5710ce5ebd2SThomas Bogendoerfer struct ioc3 __iomem *regs; 5720ce5ebd2SThomas Bogendoerfer int ret; 5730ce5ebd2SThomas Bogendoerfer 5740ce5ebd2SThomas Bogendoerfer ret = pci_enable_device(pdev); 5750ce5ebd2SThomas Bogendoerfer if (ret) 5760ce5ebd2SThomas Bogendoerfer return ret; 5770ce5ebd2SThomas Bogendoerfer 5780ce5ebd2SThomas Bogendoerfer pci_write_config_byte(pdev, PCI_LATENCY_TIMER, IOC3_LATENCY); 5790ce5ebd2SThomas Bogendoerfer pci_set_master(pdev); 5800ce5ebd2SThomas Bogendoerfer 5810ce5ebd2SThomas Bogendoerfer ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 5820ce5ebd2SThomas Bogendoerfer if (ret) { 5830ce5ebd2SThomas Bogendoerfer pr_err("%s: No usable DMA configuration, aborting.\n", 5840ce5ebd2SThomas Bogendoerfer pci_name(pdev)); 5850ce5ebd2SThomas Bogendoerfer goto out_disable_device; 5860ce5ebd2SThomas Bogendoerfer } 5870ce5ebd2SThomas Bogendoerfer 5880ce5ebd2SThomas Bogendoerfer /* Set up per-IOC3 data */ 5890ce5ebd2SThomas Bogendoerfer ipd = devm_kzalloc(&pdev->dev, sizeof(struct ioc3_priv_data), 5900ce5ebd2SThomas Bogendoerfer GFP_KERNEL); 5910ce5ebd2SThomas Bogendoerfer if (!ipd) { 5920ce5ebd2SThomas Bogendoerfer ret = -ENOMEM; 5930ce5ebd2SThomas Bogendoerfer goto out_disable_device; 5940ce5ebd2SThomas Bogendoerfer } 5950ce5ebd2SThomas Bogendoerfer ipd->pdev = pdev; 5960ce5ebd2SThomas Bogendoerfer 5970ce5ebd2SThomas Bogendoerfer /* 5980ce5ebd2SThomas Bogendoerfer * Map all IOC3 registers. These are shared between subdevices 5990ce5ebd2SThomas Bogendoerfer * so the main IOC3 module manages them. 6000ce5ebd2SThomas Bogendoerfer */ 6010ce5ebd2SThomas Bogendoerfer regs = pci_ioremap_bar(pdev, 0); 6020ce5ebd2SThomas Bogendoerfer if (!regs) { 6030ce5ebd2SThomas Bogendoerfer dev_warn(&pdev->dev, "ioc3: Unable to remap PCI BAR for %s.\n", 6040ce5ebd2SThomas Bogendoerfer pci_name(pdev)); 6050ce5ebd2SThomas Bogendoerfer ret = -ENOMEM; 6060ce5ebd2SThomas Bogendoerfer goto out_disable_device; 6070ce5ebd2SThomas Bogendoerfer } 6080ce5ebd2SThomas Bogendoerfer ipd->regs = regs; 6090ce5ebd2SThomas Bogendoerfer 6100ce5ebd2SThomas Bogendoerfer /* Track PCI-device specific data */ 6110ce5ebd2SThomas Bogendoerfer pci_set_drvdata(pdev, ipd); 6120ce5ebd2SThomas Bogendoerfer 6130ce5ebd2SThomas Bogendoerfer ret = ioc3_setup(ipd); 6140ce5ebd2SThomas Bogendoerfer if (ret) { 6150ce5ebd2SThomas Bogendoerfer /* Remove all already added MFD devices */ 6160ce5ebd2SThomas Bogendoerfer mfd_remove_devices(&ipd->pdev->dev); 6170ce5ebd2SThomas Bogendoerfer if (ipd->domain) { 6180ce5ebd2SThomas Bogendoerfer irq_domain_remove(ipd->domain); 6190ce5ebd2SThomas Bogendoerfer free_irq(ipd->domain_irq, (void *)ipd); 6200ce5ebd2SThomas Bogendoerfer } 6210ce5ebd2SThomas Bogendoerfer pci_iounmap(pdev, regs); 6220ce5ebd2SThomas Bogendoerfer goto out_disable_device; 6230ce5ebd2SThomas Bogendoerfer } 6240ce5ebd2SThomas Bogendoerfer 6250ce5ebd2SThomas Bogendoerfer return 0; 6260ce5ebd2SThomas Bogendoerfer 6270ce5ebd2SThomas Bogendoerfer out_disable_device: 6280ce5ebd2SThomas Bogendoerfer pci_disable_device(pdev); 6290ce5ebd2SThomas Bogendoerfer return ret; 6300ce5ebd2SThomas Bogendoerfer } 6310ce5ebd2SThomas Bogendoerfer 6320ce5ebd2SThomas Bogendoerfer static void ioc3_mfd_remove(struct pci_dev *pdev) 6330ce5ebd2SThomas Bogendoerfer { 6340ce5ebd2SThomas Bogendoerfer struct ioc3_priv_data *ipd; 6350ce5ebd2SThomas Bogendoerfer 6360ce5ebd2SThomas Bogendoerfer ipd = pci_get_drvdata(pdev); 6370ce5ebd2SThomas Bogendoerfer 6380ce5ebd2SThomas Bogendoerfer /* Clear and disable all IRQs */ 6390ce5ebd2SThomas Bogendoerfer writel(~0, &ipd->regs->sio_iec); 6400ce5ebd2SThomas Bogendoerfer writel(~0, &ipd->regs->sio_ir); 6410ce5ebd2SThomas Bogendoerfer 6420ce5ebd2SThomas Bogendoerfer /* Release resources */ 6430ce5ebd2SThomas Bogendoerfer mfd_remove_devices(&ipd->pdev->dev); 6440ce5ebd2SThomas Bogendoerfer if (ipd->domain) { 6450ce5ebd2SThomas Bogendoerfer irq_domain_remove(ipd->domain); 6460ce5ebd2SThomas Bogendoerfer free_irq(ipd->domain_irq, (void *)ipd); 6470ce5ebd2SThomas Bogendoerfer } 6480ce5ebd2SThomas Bogendoerfer pci_iounmap(pdev, ipd->regs); 6490ce5ebd2SThomas Bogendoerfer pci_disable_device(pdev); 6500ce5ebd2SThomas Bogendoerfer } 6510ce5ebd2SThomas Bogendoerfer 6520ce5ebd2SThomas Bogendoerfer static struct pci_device_id ioc3_mfd_id_table[] = { 6530ce5ebd2SThomas Bogendoerfer { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID }, 6540ce5ebd2SThomas Bogendoerfer { 0, }, 6550ce5ebd2SThomas Bogendoerfer }; 6560ce5ebd2SThomas Bogendoerfer MODULE_DEVICE_TABLE(pci, ioc3_mfd_id_table); 6570ce5ebd2SThomas Bogendoerfer 6580ce5ebd2SThomas Bogendoerfer static struct pci_driver ioc3_mfd_driver = { 6590ce5ebd2SThomas Bogendoerfer .name = "IOC3", 6600ce5ebd2SThomas Bogendoerfer .id_table = ioc3_mfd_id_table, 6610ce5ebd2SThomas Bogendoerfer .probe = ioc3_mfd_probe, 6620ce5ebd2SThomas Bogendoerfer .remove = ioc3_mfd_remove, 6630ce5ebd2SThomas Bogendoerfer }; 6640ce5ebd2SThomas Bogendoerfer 6650ce5ebd2SThomas Bogendoerfer module_pci_driver(ioc3_mfd_driver); 6660ce5ebd2SThomas Bogendoerfer 6670ce5ebd2SThomas Bogendoerfer MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfer@suse.de>"); 6680ce5ebd2SThomas Bogendoerfer MODULE_DESCRIPTION("SGI IOC3 MFD driver"); 6690ce5ebd2SThomas Bogendoerfer MODULE_LICENSE("GPL v2"); 670